Fix #45 implement crude checking if a song already exists in tidal
This commit is contained in:
@ -46,6 +46,7 @@ type SongRow = {
|
||||
thumbnailUrl?: string;
|
||||
thumbnailWidth?: number;
|
||||
thumbnailHeight?: number;
|
||||
tidalId: string;
|
||||
};
|
||||
|
||||
type AccountAvatarRow = {
|
||||
@ -333,6 +334,12 @@ function getMigrations(): Migration[] {
|
||||
statement: `
|
||||
ALTER TABLE songs ADD COLUMN spotifyUrl TEXT NULL;
|
||||
ALTER TABLE songs ADD COLUMN spotifyUri TEXT NULL;`
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'song tidal id',
|
||||
statement: `
|
||||
ALTER TABLE songs ADD COLUMN tidalId TEXT NULL;`
|
||||
}
|
||||
];
|
||||
}
|
||||
@ -464,8 +471,9 @@ function saveSongInfoData(postUrl: string, songs: SongInfo[]): Promise<void> {
|
||||
for (const song of songs) {
|
||||
db.run(
|
||||
`
|
||||
INSERT INTO songs (postedUrl, overviewUrl, type, youtubeUrl, spotifyUrl, spotifyUri, title, artistName, thumbnailUrl, post_url, thumbnailWidth, thumbnailHeight)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO songs (postedUrl, overviewUrl, type, youtubeUrl, spotifyUrl, spotifyUri, tidalId,
|
||||
title, artistName, thumbnailUrl, post_url, thumbnailWidth, thumbnailHeight)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
[
|
||||
song.postedUrl,
|
||||
@ -474,6 +482,7 @@ function saveSongInfoData(postUrl: string, songs: SongInfo[]): Promise<void> {
|
||||
song.youtubeUrl,
|
||||
song.spotifyUrl,
|
||||
song.spotifyUri,
|
||||
song.tidalUri,
|
||||
song.title,
|
||||
song.artistName,
|
||||
song.thumbnailUrl,
|
||||
@ -574,7 +583,7 @@ function getSongData(postIdsParams: string, postIds: string[]): Promise<Map<stri
|
||||
return new Promise((resolve, reject) => {
|
||||
db.all(
|
||||
`SELECT post_url, songs.postedUrl, songs.overviewUrl, songs.type, songs.youtubeUrl, songs.spotifyUri, songs.spotifyUri,
|
||||
songs.title, songs.artistName, songs.thumbnailUrl, songs.post_url, songs.thumbnailWidth, songs.thumbnailHeight
|
||||
songs.tidalId, songs.title, songs.artistName, songs.thumbnailUrl, songs.post_url, songs.thumbnailWidth, songs.thumbnailHeight
|
||||
FROM songs
|
||||
WHERE post_url IN (${postIdsParams});`,
|
||||
postIds,
|
||||
@ -591,6 +600,7 @@ function getSongData(postIdsParams: string, postIds: string[]): Promise<Map<stri
|
||||
youtubeUrl: item.youtubeUrl,
|
||||
spotifyUrl: item.spotifyUrl,
|
||||
spotifyUri: item.spotifyUri,
|
||||
tidalUri: item.tidalId,
|
||||
type: item.type,
|
||||
title: item.title,
|
||||
artistName: item.artistName,
|
||||
@ -683,6 +693,38 @@ function getSongThumbnailData(
|
||||
});
|
||||
}
|
||||
|
||||
export async function doesTidalSongExist(song: SongInfo): Promise<boolean> {
|
||||
if (!databaseReady) {
|
||||
await waitReady();
|
||||
}
|
||||
if (!song.tidalUri) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sql = `SELECT songs.title, songs.artistName, songs.tidalId
|
||||
FROM songs
|
||||
WHERE songs.tidalId = $tidalId
|
||||
LIMIT $limit`;
|
||||
|
||||
// If only one exists: This is the one that has just been added
|
||||
// If more exits: It has been added before
|
||||
const params = {
|
||||
$tidalId: song.tidalUri,
|
||||
$limit: 2
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
db.all(sql, params, (err, rows) => {
|
||||
if (err != null) {
|
||||
logger.error('Error loading songs', err);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
logger.debug('doesTidalSongExist', song.tidalUri, rows, rows.length > 1);
|
||||
resolve(rows.length > 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPosts(
|
||||
since: string | null,
|
||||
before: string | null,
|
||||
|
@ -6,6 +6,7 @@ import { createHash } from 'crypto';
|
||||
import { OauthPlaylistAdder } from './oauthPlaylistAdder';
|
||||
import type { PlaylistAdder } from './playlistAdder';
|
||||
import type { TidalAddToPlaylistResponse } from './tidalResponse';
|
||||
import { doesTidalSongExist } from '$lib/server/db';
|
||||
|
||||
export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAdder {
|
||||
private static code_verifier?: string;
|
||||
@ -117,6 +118,16 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
|
||||
return;
|
||||
}
|
||||
|
||||
const alreadyExists = await doesTidalSongExist(song);
|
||||
try {
|
||||
if (alreadyExists) {
|
||||
this.logger.info('Skip adding song to playlist, has already been added', song);
|
||||
return;
|
||||
}
|
||||
} catch (dbe) {
|
||||
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 = {
|
||||
method: 'POST',
|
||||
@ -140,41 +151,6 @@ export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAd
|
||||
const apiUrl = new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}/relationships/items`);
|
||||
const request = new Request(apiUrl, options);
|
||||
|
||||
// This would be API v1 (or api v2, but *not* the OpenAPI v2),
|
||||
// but that requires r_usr and w_usr permission scopes which are impossible to request
|
||||
/*
|
||||
const options: RequestInit = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `${token.token_type} ${token.access_token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
onArtifactNotFound: 'SKIP',
|
||||
trackIds: song.tidalUri,
|
||||
//toIndex: -1
|
||||
onDupes: 'SKIP'
|
||||
})
|
||||
};
|
||||
const apiUrl = new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}/items`);
|
||||
try {
|
||||
const r = await fetch(new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}`), {
|
||||
headers: {
|
||||
Authorization: `${token.token_type} ${token.access_token}`
|
||||
}
|
||||
});
|
||||
const txt = await r.text();
|
||||
this.logger.debug('playlist', r.status, txt);
|
||||
const rj = JSON.parse(txt);
|
||||
this.logger.debug('playlist', rj);
|
||||
} catch (e) {
|
||||
this.logger.error('playlist fetch failed', e);
|
||||
}
|
||||
|
||||
const request = new Request(apiUrl, options);
|
||||
this.logger.debug('Adding to playlist request', request);
|
||||
*/
|
||||
|
||||
let resp: Response | null = null;
|
||||
let respTxt: string | null = null;
|
||||
try {
|
||||
|
@ -200,10 +200,14 @@ export class TimelineReader {
|
||||
if (e instanceof Error && e.cause === 429) {
|
||||
this.logger.warn('song.link rate limit reached. Trying again in 10 seconds');
|
||||
await sleep(10_000);
|
||||
return await this.getSongInfo(url, remainingTries - 1);
|
||||
} else {
|
||||
this.logger.error(
|
||||
`Failed to load ${url} info from song.link. Trying again in 3 seconds`,
|
||||
e
|
||||
);
|
||||
await sleep(3_000);
|
||||
}
|
||||
this.logger.error(`Failed to load ${url} info from song.link`, e);
|
||||
return null;
|
||||
return await this.getSongInfo(url, remainingTries - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user