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 } }