@@ -220,6 +220,7 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
220220
221221 this . updateCacheTime ( timestamp ) ;
222222
223+ // The main list of search results
223224 const tab = content
224225 . contents
225226 . tabbedSearchResultsRenderer
@@ -231,9 +232,14 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
231232 ?. content
232233 . sectionListRenderer
233234 . contents
235+ // Search results are grouped into music shelfs.
236+ // Since we filtered the search by albums,
237+ // we only care about the first (and only) music shelf
234238 . find ( ( renderer ) => 'musicShelfRenderer' in renderer )
235239 ?. musicShelfRenderer
236240 . contents
241+ // The first (and usually only) item in the music in the shelf
242+ // is the album we are looking for
237243 . at ( 0 )
238244 ?. musicResponsiveListItemRenderer
239245 . navigationEndpoint
@@ -253,6 +259,7 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
253259 } ;
254260 }
255261
262+ /** Fetch a YouTube Music album by its browse ID */
256263 async fetchAlbum ( albumId : string ) {
257264 const { content, timestamp } = await this . provider . browse < Album > ( albumId )
258265 . catch ( ( reason ) => {
@@ -267,6 +274,7 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
267274 return content ;
268275 }
269276
277+ /** Fetch a YouTube Music playlist by its playlist ID */
270278 async fetchPlaylist ( playlistId : string ) {
271279 const { content, timestamp } = await this . provider . browse < Playlist > ( `VL${ playlistId } ` )
272280 . catch ( ( reason ) => {
@@ -281,6 +289,7 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
281289 return content ;
282290 }
283291
292+ /** Extract and fetch the corresponding playlist of an album */
284293 async playlistFromAlbum ( album : Album ) {
285294 const { id } = this . provider . extractEntityFromUrl (
286295 new URL ( album . microformat . microformatDataRenderer . urlCanonical ) ,
@@ -289,6 +298,10 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
289298 return await this . fetchPlaylist ( id ) ;
290299 }
291300
301+ /**
302+ * Extract and fetch the corresponding album of a playlist, if it exists.
303+ * Throws a ProviderError otherwise.
304+ */
292305 async albumFromPlaylist ( playlistId : string ) {
293306 // cbrd=1 seems to skip the privacy consent screen
294307 const { content, timestamp } = await this . provider . extractEmbeddedJson (
@@ -314,12 +327,19 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
314327 }
315328
316329 protected override convertRawRelease ( { album, playlist } : YouTubeMusicRelease ) {
330+ const url = album . microformat . microformatDataRenderer . urlCanonical ;
331+
317332 if ( ! this . entity ) {
318333 this . entity = this . provider . extractEntityFromUrl (
319- new URL ( album . microformat . microformatDataRenderer . urlCanonical ) ,
334+ new URL ( url ) ,
320335 ) ;
321336 }
322337
338+ /**
339+ * The header contains the main metadata about the album.
340+ * It's mostly what's shown on the left half of an album screen
341+ * on the YouTube Music web UI.
342+ */
323343 const header = album
324344 . contents
325345 . twoColumnBrowseResultsRenderer
@@ -393,7 +413,7 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
393413 externalLinks : [
394414 {
395415 // Album URL is always of type https://music.youtube.com/playlist?list=:playlist_id
396- url : album . microformat . microformatDataRenderer . urlCanonical ,
416+ url,
397417 // Set free streaming if every track is (seemingly) streamable
398418 types : tracklist . every ( ( track ) => track . recording ?. externalIds ?. length ) ? [ 'free streaming' ] : undefined ,
399419 } ,
@@ -413,6 +433,7 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
413433 ?. musicThumbnailRenderer
414434 . thumbnail
415435 . thumbnails
436+ // Sort by highest pixel count
416437 . sort ( ( a , b ) => a . width * a . height - b . width * b . height )
417438 . at ( - 1 ) ;
418439 if ( image ) {
@@ -503,12 +524,16 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
503524 }
504525
505526 extractTrackData ( item : Renderer < 'MusicResponsiveListItem' > ) {
506- const columns = item . musicResponsiveListItemRenderer . flexColumns . map (
527+ // Extract the text run of each flex column.
528+ // The flex columns usually are:
529+ // - Title (usually with endpoint to video)
530+ // - Artist name (usually with endpoint to artist)
531+ // - Album name (usually with endpoint to album)
532+ const columnTextRuns = item . musicResponsiveListItemRenderer . flexColumns . map (
507533 ( column ) => column . musicResponsiveListItemFlexColumnRenderer . text . runs ,
508534 ) ;
509535
510- item . musicResponsiveListItemRenderer ;
511- const titleRun = columns [ 0 ] [ 0 ] ;
536+ const titleRun = columnTextRuns [ 0 ] [ 0 ] ;
512537 const title = titleRun . text ;
513538
514539 let videoId : string | undefined ;
@@ -518,10 +543,10 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
518543 musicVideoType = titleRun . navigationEndpoint . watchEndpoint ?. watchEndpointMusicSupportedConfigs ?. musicVideoType ;
519544 }
520545
521- const artistRuns = columns [ 1 ] ;
546+ const artistRuns = columnTextRuns [ 1 ] ;
522547 const artists = artistRuns ? this . extractArtistCredit ( artistRuns ) : undefined ;
523548
524- const albumRun = columns . at ( 2 ) ?. at ( 0 ) ;
549+ const albumRun = columnTextRuns . at ( 2 ) ?. at ( 0 ) ;
525550 let albumName : string | undefined ;
526551 let albumId : string | undefined ;
527552 if ( albumRun ) {
@@ -533,11 +558,11 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
533558
534559 const index = item . musicResponsiveListItemRenderer . index ?. runs . at ( 0 ) ?. text ;
535560
561+ // The duration is given in (HH:)MM:SS, which is then parsed manually using the code below
536562 const durationString = item . musicResponsiveListItemRenderer . fixedColumns ?. at ( 0 )
537563 ?. musicResponsiveListItemFixedColumnRenderer . text . runs
538564 . map ( ( r ) => r . text )
539565 . join ( '' ) ;
540-
541566 const duration = durationString ?. split ( ':' ) . reduce ( ( acc , curr ) => {
542567 try {
543568 return acc * 60 + Number . parseInt ( curr ) ;
@@ -560,7 +585,6 @@ export class YouTubeMusicReleaseLookup extends ReleaseLookup<YouTubeMusicProvide
560585 . filter ( ( endpoint ) => 'browseEndpoint' in endpoint )
561586 . find ( ( endpoint ) => endpoint . browseEndpoint . browseId . startsWith ( 'MPTC' ) )
562587 ?. browseEndpoint ;
563-
564588 if ( creditsEndpoint ) {
565589 // await this.provider.browse(creditsEndpoint.browseId);
566590 }
0 commit comments