fix(spotify): parse flat title/subtitle embed format for playlist tracks
Spotify's playlist embed uses {title, subtitle, audioPreview} per track
instead of {name, artists, album}. Fixes:
- _spTrackItem: reads title as trackName, subtitle as artistName fallback,
audioPreview.url as previewUrl, isExplicit as boolean
- Route: reads entity.authors[0].name for playlist owner, entity.subtitle
for track artist fallback, entity.title fallback for entity.name
- Playlist tracks get playlist cover art as default artwork (no per-track art)
- Removes temporary debug logging
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
363fed61a5
commit
a305ec98e2
1 file changed
+33
-17
+33
-17
@@ -447,24 +447,38 @@ function _spArtistName(artistsRaw) {
|
||||
}
|
||||
|
||||
function _spTrackItem(t, defaultAlbum, defaultArt, defaultAlbumArtist) {
|
||||
// Unwrap optional {track: ...} wrapper (playlist item format)
|
||||
// Unwrap optional {track: ...} wrapper (old playlist format)
|
||||
let raw = t?.track || t;
|
||||
// Handle GraphQL-style {data: {__typename, name, artists, ...}}
|
||||
// Handle GraphQL-style {data: {__typename, ...}}
|
||||
const d = raw?.data || raw;
|
||||
const artist = _spAllArtists(d?.artists) || defaultAlbumArtist || '';
|
||||
|
||||
// Name: new embed uses "title", old/track embed uses "name"
|
||||
const trackName = d?.name || d?.title || '';
|
||||
|
||||
// Artists: array format [{name}] or [{profile:{name}}], GraphQL {items:[...]},
|
||||
// or flat string in "subtitle" (new playlist embed: "Myrne, Aviella")
|
||||
let artist = '';
|
||||
if (d?.artists) {
|
||||
artist = _spAllArtists(d.artists);
|
||||
} else if (typeof d?.subtitle === 'string' && d.subtitle) {
|
||||
artist = d.subtitle;
|
||||
}
|
||||
artist = artist || defaultAlbumArtist || '';
|
||||
|
||||
const art = _spArtwork(d) || defaultArt || '';
|
||||
const albumObj = d?.albumOfTrack || d?.album || {};
|
||||
const album = albumObj?.name || defaultAlbum || '';
|
||||
const date = albumObj?.date?.isoString || albumObj?.release_date || '';
|
||||
|
||||
return {
|
||||
trackName: d?.name || '',
|
||||
trackName,
|
||||
artistName: artist,
|
||||
collectionName: album,
|
||||
artworkUrl100: art,
|
||||
releaseDate: date,
|
||||
trackNumber: d?.trackNumber || d?.track_number,
|
||||
trackExplicitness: d?.contentRating?.label === 'EXPLICIT' || d?.explicit ? 'explicit' : 'notExplicit',
|
||||
previewUrl: d?.previewUrl || d?.preview_url || null,
|
||||
trackExplicitness: d?.contentRating?.label === 'EXPLICIT' || d?.isExplicit || d?.explicit ? 'explicit' : 'notExplicit',
|
||||
previewUrl: d?.previewUrl || d?.preview_url || d?.audioPreview?.url || null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -480,13 +494,14 @@ app.post('/api/spotify/resolve', async (req, res) => {
|
||||
try {
|
||||
const { type, id } = parseSpotifyUrl(url);
|
||||
const entity = await fetchSpotifyEmbed(type, id);
|
||||
const artist = _spArtistName(entity.artists);
|
||||
// "subtitle" is used by the flat embed format as artist/owner string
|
||||
const artist = _spArtistName(entity.artists) || (typeof entity.subtitle === 'string' ? entity.subtitle : '');
|
||||
const artUrl = _spArtwork(entity);
|
||||
|
||||
if (type === 'track') {
|
||||
return res.json({
|
||||
type: 'track',
|
||||
name: entity.name,
|
||||
name: entity.name || entity.title || '',
|
||||
artist,
|
||||
artworkUrl: artUrl,
|
||||
tracks: [_spTrackItem(entity, entity.album?.name, artUrl, artist)],
|
||||
@@ -497,27 +512,28 @@ app.post('/api/spotify/resolve', async (req, res) => {
|
||||
const trackList = entity.trackList || entity.tracks?.items || [];
|
||||
return res.json({
|
||||
type: 'album',
|
||||
name: entity.name,
|
||||
name: entity.name || entity.title || '',
|
||||
artist,
|
||||
artworkUrl: artUrl,
|
||||
tracks: trackList.map(t => _spTrackItem(t, entity.name, artUrl, artist)),
|
||||
tracks: trackList.map(t => _spTrackItem(t, entity.name || entity.title, artUrl, artist)),
|
||||
});
|
||||
}
|
||||
|
||||
if (type === 'playlist') {
|
||||
const trackList = entity.trackList || entity.content?.items || entity.tracks?.items || [];
|
||||
const owner = entity.ownerV2?.data?.name || entity.owner?.displayName || entity.owner?.display_name || '';
|
||||
const firstRaw = trackList[0];
|
||||
console.log('[spotify/resolve] entity keys:', Object.keys(entity));
|
||||
console.log('[spotify/resolve] trackList[0]:', JSON.stringify(firstRaw, null, 2).slice(0, 800));
|
||||
const owner = entity.authors?.[0]?.name
|
||||
|| entity.ownerV2?.data?.name
|
||||
|| entity.owner?.displayName
|
||||
|| entity.owner?.display_name
|
||||
|| (typeof entity.subtitle === 'string' ? entity.subtitle : '');
|
||||
return res.json({
|
||||
type: 'playlist',
|
||||
name: entity.name,
|
||||
name: entity.name || entity.title || '',
|
||||
artist: owner,
|
||||
artworkUrl: artUrl,
|
||||
description: entity.description || '',
|
||||
tracks: trackList.map(t => _spTrackItem(t, null, null, null)),
|
||||
_debug: { entityKeys: Object.keys(entity), firstTrack: firstRaw },
|
||||
// pass artUrl as defaultArt so tracks show the playlist cover when no per-track art exists
|
||||
tracks: trackList.map(t => _spTrackItem(t, null, artUrl, null)),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user