11import SpotifyInfo from "spotify-url-info" ;
22import SpotifyWebApi from "spotify-web-api-node" ;
33import { fetch } from "undici" ;
4+ import { DisTubeError , isTruthy } from "distube" ;
45import { parse as parseSpotifyUri } from "spotify-uri" ;
5- import { DisTubeError } from "distube" ;
66
77const SUPPORTED_TYPES = [ "album" , "playlist" , "track" , "artist" ] as const ;
88
@@ -42,7 +42,7 @@ type Data = Track | TrackList;
4242let firstWarning1 = true ;
4343let firstWarning2 = true ;
4444
45- const apiError = ( e : any ) =>
45+ export const apiError = ( e : any ) =>
4646 new DisTubeError (
4747 "SPOTIFY_API_ERROR" ,
4848 `The URL is private or unavailable.${ e ?. body ?. error ?. message ? `\nDetails: ${ e . body . error . message } ` : "" } ${
@@ -125,26 +125,27 @@ export class API {
125125 getData ( url : `${string } /artist/${string } `) : Promise < Artist > ;
126126 getData ( url : string ) : Promise < Data > ;
127127 async getData ( url : string ) : Promise < Data > {
128- const parsedUrl = this . parseUrl ( url ) ;
129- const id = ( < any > parsedUrl ) . id ;
128+ const { type , id } = this . parseUrl ( url ) ;
129+
130130 if ( ! id ) throw new DisTubeError ( "SPOTIFY_API_INVALID_URL" , "Invalid URL" ) ;
131- if ( ! this . isSupportedTypes ( parsedUrl . type ) ) {
132- throw new DisTubeError ( "SPOTIFY_API_UNSUPPORTED_TYPE" , "Unsupported URL type" ) ;
133- }
131+ if ( ! this . isSupportedTypes ( type ) ) throw new DisTubeError ( "SPOTIFY_API_UNSUPPORTED_TYPE" , "Unsupported URL type" ) ;
132+
134133 await this . refreshToken ( ) ;
135- if ( parsedUrl . type === "track" ) {
136- if ( ! this . _tokenAvailable ) return info . getData ( url ) ;
137- return api
138- . getTrack ( id )
139- . then ( ( { body } ) => body )
140- . catch ( e => {
141- throw apiError ( e ) ;
142- } ) ;
134+ if ( type === "track" ) {
135+ if ( ! this . _tokenAvailable ) {
136+ return info . getData ( url ) ;
137+ }
138+ try {
139+ const { body } = await api . getTrack ( id ) ;
140+ return body ;
141+ } catch ( e ) {
142+ throw apiError ( e ) ;
143+ }
143144 }
144145 if ( ! this . _tokenAvailable ) {
145146 const data = ( await info . getData ( url ) ) as EmbedList ;
146147 return {
147- type : parsedUrl . type ,
148+ type,
148149 name : data . title ,
149150 thumbnail : data . coverArt ?. sources ?. [ 0 ] ?. url ,
150151 url,
@@ -155,51 +156,39 @@ export class API {
155156 } ) ) ,
156157 } ;
157158 }
158- let name : string , thumbnail : string , tracks : Track [ ] ;
159159 try {
160- switch ( parsedUrl . type ) {
161- case "album" : {
162- const { body } = await api . getAlbum ( id ) ;
163- name = body . name ;
164- thumbnail = body . images ?. [ 0 ] ?. url ;
165- url = body . external_urls ?. spotify ;
166- tracks = await this . #getFullItems( body ) ;
167- break ;
168- }
169- case "playlist" : {
170- const { body } = await api . getPlaylist ( id ) ;
171- name = body . name ;
172- thumbnail = body . images ?. [ 0 ] ?. url ;
173- url = body . external_urls ?. spotify ;
174- tracks = ( await this . #getFullItems( body ) ) . map ( i => i . track ) ;
175- break ;
176- }
177- case "artist" : {
178- const { body } = await api . getArtist ( id ) ;
179- name = body . name ;
180- thumbnail = body . images ?. [ 0 ] ?. url ;
181- url = body . external_urls ?. spotify ;
182- tracks = ( await api . getArtistTopTracks ( id , this . topTracksCountry ) ) . body . tracks ;
183- break ;
184- }
185- default :
186- throw new DisTubeError ( "SPOTIFY_API_UNSUPPORTED_TYPE" , "Unsupported URL type" ) ;
187- }
188- } catch ( e : any ) {
160+ const { body } = await api [ type === "album" ? "getAlbum" : type === "playlist" ? "getPlaylist" : "getArtist" ] ( id ) ;
161+ return {
162+ type,
163+ name : body . name ,
164+ thumbnail : body . images ?. [ 0 ] ?. url ,
165+ url : body . external_urls ?. spotify ,
166+ tracks : ( await this . #getTracks( body ) ) . filter ( t => t ?. type === "track" ) ,
167+ } ;
168+ } catch ( e ) {
189169 throw apiError ( e ) ;
190170 }
191- return {
192- type : parsedUrl . type ,
193- name,
194- thumbnail,
195- url,
196- tracks : tracks . filter ( t => t ?. type === "track" ) ,
197- } ;
198171 }
199172
200- #getFullItems( data : SpotifyApi . SingleAlbumResponse ) : Promise < SpotifyApi . TrackObjectSimplified [ ] > ;
201- #getFullItems( data : SpotifyApi . SinglePlaylistResponse ) : Promise < SpotifyApi . PlaylistTrackObject [ ] > ;
202- async #getFullItems( data : SpotifyApi . SingleAlbumResponse | SpotifyApi . SinglePlaylistResponse ) {
173+ async #getTracks(
174+ data : SpotifyApi . SingleAlbumResponse | SpotifyApi . SinglePlaylistResponse | SpotifyApi . SingleArtistResponse ,
175+ ) : Promise < Track [ ] > {
176+ switch ( data . type ) {
177+ case "artist" : {
178+ return ( await api . getArtistTopTracks ( data . id , this . topTracksCountry ) ) . body . tracks ;
179+ }
180+ case "album" : {
181+ return await this . #getPaginatedItems( data ) ;
182+ }
183+ case "playlist" : {
184+ return ( await this . #getPaginatedItems( data ) ) . map ( i => i . track ) . filter ( isTruthy ) ;
185+ }
186+ }
187+ }
188+
189+ #getPaginatedItems( data : SpotifyApi . SingleAlbumResponse ) : Promise < SpotifyApi . TrackObjectSimplified [ ] > ;
190+ #getPaginatedItems( data : SpotifyApi . SinglePlaylistResponse ) : Promise < SpotifyApi . PlaylistTrackObject [ ] > ;
191+ async #getPaginatedItems( data : SpotifyApi . SingleAlbumResponse | SpotifyApi . SinglePlaylistResponse ) {
203192 const items : ( SpotifyApi . TrackObjectSimplified | SpotifyApi . PlaylistTrackObject ) [ ] = data . tracks . items ;
204193 const isPlaylist = data . type === "playlist" ;
205194 const limit = isPlaylist ? 100 : 50 ;
0 commit comments