mirror of
https://codeberg.org/lucida/lucida.git
synced 2026-11-13 23:27:24 -05:00
add episodes/podcasts to spotify, fix soundcloud bugs
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user