Began cleaning up the ytmusic parsers

This commit is contained in:
Eclypsed
2024-03-31 23:41:55 -04:00
parent dc18005a60
commit 78b1f7e140

View File

@@ -142,10 +142,9 @@ export class YouTubeMusic implements Connection {
const headerTitle = section.musicCarouselShelfRenderer.header.musicCarouselShelfBasicHeaderRenderer.title.runs[0].text const headerTitle = section.musicCarouselShelfRenderer.header.musicCarouselShelfBasicHeaderRenderer.title.runs[0].text
const rawContents = section.musicCarouselShelfRenderer.contents const rawContents = section.musicCarouselShelfRenderer.contents
const parsedContent: (Song | Album | Playlist)[] = const parsedContent = rawContents.map((content) =>
'musicTwoRowItemRenderer' in rawContents[0] 'musicTwoRowItemRenderer' in content ? this.parseTwoRowItemRenderer(content.musicTwoRowItemRenderer) : this.parseResponsiveListItemRenderer(content.musicResponsiveListItemRenderer),
? this.parseTwoRowItemRenderer((rawContents as { musicTwoRowItemRenderer: InnerTube.musicTwoRowItemRenderer }[]).map((item) => item.musicTwoRowItemRenderer)) )
: this.parseResponsiveListItemRenderer((rawContents as { musicResponsiveListItemRenderer: InnerTube.musicResponsiveListItemRenderer }[]).map((item) => item.musicResponsiveListItemRenderer))
if (headerTitle === 'Listen again') { if (headerTitle === 'Listen again') {
homeItems.listenAgain = parsedContent homeItems.listenAgain = parsedContent
@@ -159,158 +158,109 @@ export class YouTubeMusic implements Connection {
return homeItems return homeItems
} }
private parseTwoRowItemRenderer = (rowContent: InnerTube.musicTwoRowItemRenderer[]): (Song | Album | Playlist)[] => { private parseTwoRowItemRenderer = (rowContent: InnerTube.musicTwoRowItemRenderer): Song | Album | Artist | Playlist => {
const parsedContent: (Song | Album | Playlist)[] = [] const connection = { id: this.id, type: 'youtube-music' } satisfies (Song | Album | Artist | Playlist)['connection']
for (const data of rowContent) { const name = rowContent.title.runs[0].text
const title = data.title.runs[0].text
const subtitles = data.subtitle.runs
const artists: Song['artists'] | Album['artists'] = []
for (const subtitle of subtitles) {
if (subtitle.navigationEndpoint && 'browseEndpoint' in subtitle.navigationEndpoint) {
artists.push({ id: subtitle.navigationEndpoint.browseEndpoint.browseId, name: subtitle.text })
}
}
if ('browseEndpoint' in data.navigationEndpoint && data.navigationEndpoint.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType === 'MUSIC_PAGE_TYPE_ALBUM') { function getArtists() {
const album: Album = { const artists: (Song | Album)['artists'] = []
connection: { for (const run of rowContent.subtitle.runs) {
id: this.id, if (run.navigationEndpoint && 'browseEndpoint' in run.navigationEndpoint) {
type: 'youtube-music',
},
type: 'album',
id: data.navigationEndpoint.browseEndpoint.browseId,
name: title,
thumbnail: refineThumbnailUrl(data.thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails[0].url),
}
if (artists.length > 0) album.artists = artists
parsedContent.push(album)
} else if ('watchEndpoint' in data.navigationEndpoint) {
const song: Song = {
connection: {
id: this.id,
type: 'youtube-music',
},
type: 'song',
id: data.navigationEndpoint.watchEndpoint.videoId,
name: title,
thumbnail: refineThumbnailUrl(data.thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails[0].url),
}
if (artists.length > 0) song.artists = artists
parsedContent.push(song)
}
}
return parsedContent
}
private parseResponsiveListItemRenderer = (listContent: InnerTube.musicResponsiveListItemRenderer[]): Song[] => {
const parsedContent: Song[] = []
for (const data of listContent) {
const title = data.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text,
id = (data.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint! as { watchEndpoint: InnerTube.watchEndpoint }).watchEndpoint.videoId
const artists: Song['artists'] = []
for (const run of data.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs) {
if ('navigationEndpoint' in run && 'browseEndpoint' in run.navigationEndpoint!) {
artists.push({ id: run.navigationEndpoint.browseEndpoint.browseId, name: run.text }) artists.push({ id: run.navigationEndpoint.browseEndpoint.browseId, name: run.text })
} }
} }
if (artists.length > 0) return artists
const thumbnail = refineThumbnailUrl(data.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[0].url) return undefined
const song: Song = {
connection: {
id: this.id,
type: 'youtube-music',
},
type: 'song',
id,
name: title,
thumbnail,
} }
if (artists.length > 0) song.artists = artists const thumbnail = refineThumbnailUrl(rowContent.thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails[0].url)
if ('watchEndpoint' in rowContent.navigationEndpoint) {
const id = rowContent.navigationEndpoint.watchEndpoint.videoId
return { connection, id, name, type: 'song', artists: getArtists(), thumbnail } satisfies Song
}
if ('watchPlaylistEndpoint' in rowContent.navigationEndpoint) {
const id = rowContent.navigationEndpoint.watchPlaylistEndpoint.playlistId
return { connection, id, name, type: 'playlist', thumbnail } satisfies Playlist
}
const pageType = rowContent.navigationEndpoint.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType
const id = rowContent.navigationEndpoint.browseEndpoint.browseId
switch (pageType) {
case 'MUSIC_PAGE_TYPE_ALBUM':
return { connection, id, name, type: 'album', artists: getArtists(), thumbnail } satisfies Album
case 'MUSIC_PAGE_TYPE_ARTIST':
return { connection, id, name, type: 'artist', thumbnail } satisfies Artist
case 'MUSIC_PAGE_TYPE_PLAYLIST':
return { connection, id, name, type: 'playlist', thumbnail } satisfies Playlist
}
}
private parseResponsiveListItemRenderer = (listContent: InnerTube.musicResponsiveListItemRenderer): Song => {
const connection = { id: this.id, type: 'youtube-music' } satisfies Song['connection']
const name = listContent.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text
const id = (listContent.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint! as { watchEndpoint: InnerTube.watchEndpoint }).watchEndpoint.videoId
const mappedArtists: Song['artists'] = []
for (const run of listContent.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs) {
if ('navigationEndpoint' in run && 'browseEndpoint' in run.navigationEndpoint!) {
mappedArtists.push({ id: run.navigationEndpoint.browseEndpoint.browseId, name: run.text })
}
}
const artists = mappedArtists.length > 0 ? mappedArtists : undefined
function getAlbum() {
// This is like the ONE situation where `text` might not have a run // This is like the ONE situation where `text` might not have a run
if (data.flexColumns[2].musicResponsiveListItemFlexColumnRenderer.text?.runs) { if (listContent.flexColumns[2].musicResponsiveListItemFlexColumnRenderer.text?.runs) {
song.album = { return {
id: (data.flexColumns[2].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint! as { browseEndpoint: InnerTube.browseEndpoint }).browseEndpoint.browseId, id: (listContent.flexColumns[2].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint! as { browseEndpoint: InnerTube.browseEndpoint }).browseEndpoint.browseId,
name: data.flexColumns[2].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text, name: listContent.flexColumns[2].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text,
} satisfies Song['album']
} }
return undefined
} }
parsedContent.push(song) const thumbnail = refineThumbnailUrl(listContent.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[0].url)
}
return parsedContent return { connection, id, name, type: 'song', artists, album: getAlbum(), thumbnail } satisfies Song
} }
private parseMusicCardShelfRenderer = (cardContent: InnerTube.musicCardShelfRenderer): Song | Album | Artist | Playlist => { private parseMusicCardShelfRenderer = (cardContent: InnerTube.musicCardShelfRenderer): Song | Album | Artist | Playlist => {
const navigationEndpoint = cardContent.title.runs[0].navigationEndpoint const connection = { id: this.id, type: 'youtube-music' } satisfies (Song | Album | Artist | Playlist)['connection']
const artists: Song['artists'] | Album['artists'] = [] const name = cardContent.title.runs[0].text
cardContent.subtitle.runs.forEach((run) => {
if (run.navigationEndpoint?.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType === 'MUSIC_PAGE_TYPE_ARTIST') { const artistsSubtitleRuns = cardContent.subtitle.runs.filter(
artists.push({ id: run.navigationEndpoint.browseEndpoint.browseId, name: run.text }) (run) => run.navigationEndpoint?.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType === 'MUSIC_PAGE_TYPE_ARTIST',
} )
const artists: (Song | Album)['artists'] =
artistsSubtitleRuns.length > 0
? artistsSubtitleRuns.map((run) => {
return { id: run.navigationEndpoint!.browseEndpoint.browseId, name: run.text }
}) })
: undefined
const thumbnail = refineThumbnailUrl(cardContent.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[0].url) const thumbnail = refineThumbnailUrl(cardContent.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[0].url)
const navigationEndpoint = cardContent.title.runs[0].navigationEndpoint
if ('watchEndpoint' in navigationEndpoint) { if ('watchEndpoint' in navigationEndpoint) {
const id = navigationEndpoint.watchEndpoint.videoId
const albumSubtitleRun = cardContent.subtitle.runs.find( const albumSubtitleRun = cardContent.subtitle.runs.find(
(run) => run.navigationEndpoint?.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType === 'MUSIC_PAGE_TYPE_ALBUM', (run) => run.navigationEndpoint?.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType === 'MUSIC_PAGE_TYPE_ALBUM',
) )
const song: Song = { const album = albumSubtitleRun ? { id: albumSubtitleRun.navigationEndpoint!.browseEndpoint.browseId, name: albumSubtitleRun.text } : undefined
connection: { return { connection, id, name, type: 'song', artists, album, thumbnail } satisfies Song
id: this.id,
type: 'youtube-music',
},
id: navigationEndpoint.watchEndpoint.videoId,
name: cardContent.title.runs[0].text,
type: 'song',
thumbnail,
} }
if (artists.length > 0) song.artists = artists
if (albumSubtitleRun) song.album = { id: albumSubtitleRun.navigationEndpoint!.browseEndpoint.browseId, name: albumSubtitleRun.text }
return song
} else {
const pageType = navigationEndpoint.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType const pageType = navigationEndpoint.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType
if (pageType === 'MUSIC_PAGE_TYPE_ALBUM') { const id = navigationEndpoint.browseEndpoint.browseId
const album: Album = { switch (pageType) {
connection: { case 'MUSIC_PAGE_TYPE_ALBUM':
id: this.id, return { connection, id, name, type: 'album', artists, thumbnail } satisfies Album
type: 'youtube-music', case 'MUSIC_PAGE_TYPE_ARTIST':
}, return { connection, id, name, type: 'artist', thumbnail } satisfies Artist
id: navigationEndpoint.browseEndpoint.browseId, case 'MUSIC_PAGE_TYPE_PLAYLIST':
name: cardContent.title.runs[0].text, return { connection, id, name, type: 'playlist', thumbnail } satisfies Playlist
type: 'album',
thumbnail,
}
if (artists.length > 0) album.artists = artists
return album
} else if (pageType === 'MUSIC_PAGE_TYPE_ARTIST') {
const artist: Artist = {
connection: {
id: this.id,
type: 'youtube-music',
},
id: navigationEndpoint.browseEndpoint.browseId,
name: cardContent.title.runs[0].text,
type: 'artist',
thumbnail,
}
return artist
} else {
// NEED TO GET A SAMPLE FOR THIS
const playlist: Playlist = {
connection: {
id: this.id,
type: 'youtube-music',
},
id: navigationEndpoint.browseEndpoint.browseId,
name: cardContent.title.runs[0].text,
type: 'playlist',
}
return playlist
}
} }
} }
} }
@@ -457,20 +407,6 @@ declare namespace InnerTube {
title: { title: {
runs: [runs] runs: [runs]
} }
strapline: [runs]
accessibilityData: accessibilityData
headerStyle: string
moreContentButton?: {
buttonRenderer: {
style: string
text: {
runs: [runs]
}
navigationEndpoint: navigationEndpoint
trackingParams: string
accessibilityData: accessibilityData
}
}
thumbnail?: musicThumbnailRenderer thumbnail?: musicThumbnailRenderer
trackingParams: string trackingParams: string
} }
@@ -486,14 +422,14 @@ declare namespace InnerTube {
itemSize: string itemSize: string
} }
type musicDescriptionShelfRenderer = { // type musicDescriptionShelfRenderer = {
header: { // header: {
runs: [runs] // runs: [runs]
} // }
description: { // description: {
runs: [runs] // runs: [runs]
} // }
} // }
type musicTwoRowItemRenderer = { type musicTwoRowItemRenderer = {
thumbnailRenderer: { thumbnailRenderer: {