83 lines
4.3 KiB
JavaScript
83 lines
4.3 KiB
JavaScript
import axios from 'axios';
|
|
import { db } from './db.js';
|
|
import { ensureValidAccessToken } from './spotify.js';
|
|
export async function syncUserData(uid) {
|
|
const accessToken = await ensureValidAccessToken(uid);
|
|
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
const [recentResp, topTracksShort, topTracksMed, topArtistsShort, topArtistsMed] = await Promise.all([
|
|
axios.get('https://api.spotify.com/v1/me/player/recently-played?limit=20', { headers }),
|
|
axios.get('https://api.spotify.com/v1/me/top/tracks?time_range=short_term&limit=20', { headers }),
|
|
axios.get('https://api.spotify.com/v1/me/top/tracks?time_range=medium_term&limit=20', { headers }),
|
|
axios.get('https://api.spotify.com/v1/me/top/artists?time_range=short_term&limit=20', { headers }),
|
|
axios.get('https://api.spotify.com/v1/me/top/artists?time_range=medium_term&limit=20', { headers })
|
|
]);
|
|
const trx = db.db.transaction(() => {
|
|
const rpStmt = db.db.prepare('INSERT OR REPLACE INTO recently_played (id, user_id, played_at, track_json) VALUES (?, ?, ?, ?)');
|
|
for (const item of recentResp.data.items) {
|
|
rpStmt.run(item.played_at + ':' + item.track.id, uid, new Date(item.played_at).getTime(), JSON.stringify(item.track));
|
|
}
|
|
const ttDel = db.db.prepare('DELETE FROM top_tracks WHERE user_id=? AND time_range=?');
|
|
const ttIns = db.db.prepare('INSERT INTO top_tracks (user_id, time_range, rank, track_json) VALUES (?, ?, ?, ?)');
|
|
for (const [range, payload] of [
|
|
['short_term', topTracksShort.data],
|
|
['medium_term', topTracksMed.data],
|
|
]) {
|
|
ttDel.run(uid, range);
|
|
payload.items.forEach((t, idx) => ttIns.run(uid, range, idx + 1, JSON.stringify(t)));
|
|
}
|
|
const taDel = db.db.prepare('DELETE FROM top_artists WHERE user_id=? AND time_range=?');
|
|
const taIns = db.db.prepare('INSERT INTO top_artists (user_id, time_range, rank, artist_json) VALUES (?, ?, ?, ?)');
|
|
for (const [range, payload] of [
|
|
['short_term', topArtistsShort.data],
|
|
['medium_term', topArtistsMed.data],
|
|
]) {
|
|
taDel.run(uid, range);
|
|
payload.items.forEach((a, idx) => taIns.run(uid, range, idx + 1, JSON.stringify(a)));
|
|
}
|
|
});
|
|
trx();
|
|
db.db.prepare('UPDATE users SET last_synced_at=?, updated_at=? WHERE id=?').run(Date.now(), Date.now(), uid);
|
|
}
|
|
// Capture the user's current playback and persist as a recent play if changed
|
|
export async function captureNowPlaying(uid) {
|
|
try {
|
|
const accessToken = await ensureValidAccessToken(uid);
|
|
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
const resp = await axios.get('https://api.spotify.com/v1/me/player/currently-playing?additional_types=track', {
|
|
headers,
|
|
validateStatus: () => true,
|
|
});
|
|
if (resp.status !== 200)
|
|
return; // nothing to record
|
|
const data = resp.data;
|
|
if (!data?.is_playing || !data?.item)
|
|
return;
|
|
const track = data.item;
|
|
const trackId = track?.id;
|
|
if (!trackId)
|
|
return;
|
|
// Deduplicate: update existing row if same track found recently
|
|
const recentRows = db.db.prepare('SELECT id, track_json, played_at FROM recently_played WHERE user_id=? ORDER BY played_at DESC LIMIT 50').all(uid);
|
|
for (const row of recentRows) {
|
|
try {
|
|
const rowTrack = JSON.parse(row.track_json);
|
|
if (rowTrack?.id === trackId) {
|
|
// If the last record for this track is within 15 minutes, treat as the same session; update timestamp
|
|
if (Date.now() - row.played_at < 15 * 60 * 1000) {
|
|
db.db.prepare('UPDATE recently_played SET played_at = ?, track_json = ? WHERE id = ?').run(Date.now(), JSON.stringify(track), row.id);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
db.db
|
|
.prepare('INSERT INTO recently_played (user_id, played_at, track_json) VALUES (?, ?, ?)')
|
|
.run(uid, Date.now(), JSON.stringify(track));
|
|
}
|
|
catch {
|
|
// ignore errors while capturing now playing to keep background loop robust
|
|
}
|
|
}
|