add episodes/podcasts to spotify, fix soundcloud bugs

This commit is contained in:
aria
2024-07-17 04:12:16 -04:00
parent 25f7babb3e
commit 12a04b2a7b
8 changed files with 217 additions and 32 deletions

View File

@@ -1,6 +1,6 @@
import Librespot, { LibrespotOptions } from 'librespot'
import { parseArtist, parseAlbum, parseTrack } from './parse.js'
import { GetByUrlResponse, SearchResults, StreamerWithLogin } from '../../types.js'
import { parseArtist, parseAlbum, parseTrack, parseEpisode, parsePodcast } from './parse.js'
import { GetByUrlResponse, ItemType, SearchResults, StreamerAccount, StreamerWithLogin } from '../../types.js'
class Spotify implements StreamerWithLogin {
client: Librespot
@@ -11,18 +11,22 @@ class Spotify implements StreamerWithLogin {
login(username: string, password: string) {
return this.client.login(username, password)
}
#getUrlParts(url: string): ['artist' | 'album' | 'track', string] {
#getUrlParts(url: string): ['artist' | 'album' | 'track' | 'episode' | 'show', string] {
const urlObj = new URL(url)
const parts = urlObj.pathname.slice(1).split('/')
if (parts.length > 2) throw new Error('Unknown Spotify URL')
if (parts[0] != 'artist' && parts[0] != 'track' && parts[0] != 'album') {
if (parts[0] != 'artist' && parts[0] != 'track' && parts[0] != 'album' && parts[0] != 'show' && parts[0] != 'episode') {
throw new Error(`Spotify type "${parts[0]}" unsupported`)
}
if (!parts[1]) throw new Error('Unknown Spotify URL')
return [parts[0], parts[1]]
}
async getTypeFromUrl(url: string) {
return this.#getUrlParts(url)[0]
let type: ItemType | 'show' = this.#getUrlParts(url)[0]
if (type == 'show') type = 'podcast'
return type
}
async getByUrl(url: string, limit = 0): Promise<GetByUrlResponse> {
const [type, id] = this.#getUrlParts(url)
@@ -59,7 +63,7 @@ class Spotify implements StreamerWithLogin {
if (tracks) {
return {
type,
metadata: parseAlbum(metadata),
metadata: {...parseAlbum(metadata), trackCount: tracks.length},
tracks: tracks?.map((e) => parseTrack(e)) ?? []
}
}
@@ -69,6 +73,29 @@ class Spotify implements StreamerWithLogin {
tracks: []
}
}
case 'episode': {
const metadata = await this.client.get.episodeMetadata(id)
return {
type,
getStream: async () => {
const streamData = await this.client.get.episodeStream(id)
return {
mimeType: 'audio/ogg',
sizeBytes: streamData.sizeBytes,
stream: streamData.stream
}
},
metadata: parseEpisode(metadata)
}
}
case 'show': {
const metadata = await this.client.get.podcastMetadata(id)
return {
type: 'podcast',
metadata: parsePodcast(metadata),
episodes: (metadata.episodes?.map((e) => parseEpisode(e))) ?? []
}
}
}
}
async search(query: string): Promise<SearchResults> {
@@ -80,6 +107,12 @@ class Spotify implements StreamerWithLogin {
tracks: results.tracks?.map((e) => parseTrack(e)) ?? []
}
}
async getAccountInfo(): Promise<StreamerAccount> {
return {
valid: true,
premium: (await this.client.isPremium())
}
}
disconnect() {
return this.client.disconnect()
}

View File

@@ -1,5 +1,5 @@
import type { SpotifyAlbum, SpotifyArtist, SpotifyThumbnail, SpotifyTrack } from 'librespot/types'
import { Album, Artist, Track } from '../../types.js'
import type { SpotifyAlbum, SpotifyArtist, SpotifyThumbnail, SpotifyTrack, SpotifyEpisode, SpotifyPodcast } from 'librespot/types'
import { Album, Artist, Episode, Podcast, Track } from '../../types.js'
function parseThumbnails(raw: SpotifyThumbnail[]) {
return raw
@@ -47,6 +47,34 @@ export function parseAlbum(raw: SpotifyAlbum): Album {
trackCount: raw.totalTracks,
releaseDate: raw.releaseDate,
coverArtwork: parseThumbnails(raw.coverArtwork),
artists: raw.artists.map((e) => parseArtist(e))
artists: raw.artists.map((e) => parseArtist(e)),
}
}
export function parseEpisode(raw: SpotifyEpisode) {
const episode: Episode = {
title: raw.name,
id: raw.id,
url: raw.externalUrl,
explicit: raw.explicit,
description: raw.description,
coverArtwork: parseThumbnails(raw.coverArtwork),
releaseDate: raw.releaseDate,
durationMs: raw.durationMs
}
if (raw.podcast) episode.podcast = parsePodcast(raw.podcast)
return episode
}
export function parsePodcast(raw: SpotifyPodcast) {
const podcast: Podcast = {
title: raw.name,
id: raw.id,
url: raw.externalUrl,
description: raw.description,
coverArtwork: parseThumbnails(raw.coverArtwork),
}
if (typeof raw.explicit == 'boolean') podcast.explicit = raw.explicit
return podcast
}