tidal: chunk playlist adding, fix next-paging handling

This commit is contained in:
2025-07-15 14:22:45 +02:00
parent f309cd87d1
commit a9178b340a

View File

@ -121,8 +121,13 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
try {
let albumUrl: URL;
if (nextLink) {
this.logger.debug('getAlbumItems nextPage', nextLink);
albumUrl = new URL(nextLink);
} else {
this.logger.debug(
'getAlbumItems albumUrl',
`${this.apiBase}/albums/${albumId}/relationships/items`
);
albumUrl = new URL(`${this.apiBase}/albums/${albumId}/relationships/items`);
albumUrl.searchParams.append('countryCode', 'DE');
}
@ -144,7 +149,12 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
return { id: x.id, type: x.type };
});
if (respData.links?.next) {
const nextPage = await this.getAlbumItems(albumId, remaning, respData.links.next);
this.logger.debug('getAlbumItems requesting next page', respData.links.next);
const nextPage = await this.getAlbumItems(
albumId,
remaning,
this.apiBase + respData.links.next
);
tracks = tracks.concat(nextPage);
}
return tracks;
@ -243,8 +253,23 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
this.logger.error('Could not check for tidal dupes', dbe);
}
// This would be API v2, but that's still in beta and only allows adding an item *before* another one
const options: RequestInit = {
// Tidal can only handle max. 20 items to be added at once
// This isn't documented, but the API helpfully provides a useful error message
let chunkSize = 20;
const chunkedTracks: { id: string; type: string }[][] = [];
let chunkIndex = 0;
while (chunkIndex < tracks.length) {
chunkedTracks.push(tracks.slice(chunkIndex, chunkIndex + chunkSize));
chunkIndex += chunkSize;
}
const apiUrl = new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}/relationships/items`);
let options: RequestInit;
let request: Request;
let resp: Response | null = null;
let respTxt: string | null = null;
for (let chunk of chunkedTracks) {
options = {
method: 'POST',
headers: {
Authorization: `${token.token_type} ${token.access_token}`,
@ -252,17 +277,14 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
Accept: 'application/vnd.api+json'
},
body: JSON.stringify({
data: tracks,
data: chunk,
meta: {
positionBefore: 'ffb6286e-237a-4dfc-bbf1-2fb0eb004ed5' // Hardcoded last element of list
}
})
};
const apiUrl = new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}/relationships/items`);
const request = new Request(apiUrl, options);
let resp: Response | null = null;
let respTxt: string | null = null;
request = new Request(apiUrl, options);
try {
resp = await fetch(request);
this.processTidalHeaders(resp.headers);
@ -281,7 +303,7 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
if (token == null) {
return;
}
this.addToPlaylistRetry(song, remaning--);
await this.addToPlaylistRetry(song, remaning--);
}
} else if (respObj === null) {
switch (resp.status) {
@ -314,9 +336,8 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
'sec'
);
// Try again secondsToWait sec later, just to be safe one additional second
setTimeout(() => {
this.addToPlaylistRetry(song, remaning--);
}, secondsToWait * 1000);
await sleep(secondsToWait * 1000);
await this.addToPlaylistRetry(song, remaning--);
}
break;
default:
@ -335,6 +356,7 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
this.logger.error('Add to playlist request failed', resp?.status, e);
}
}
}
public async addToPlaylist(song: SongInfo) {
await this.addToPlaylistRetry(song);
}