playlist data caching

This commit is contained in:
2025-03-12 09:36:29 -07:00
parent 1520dd830c
commit e6bff5a472
3 changed files with 30 additions and 6 deletions

View File

@@ -1,7 +1,7 @@
import { createClient } from "redis"; import { createClient } from "redis";
import curriedLogger from "../utils/logger.ts";
import { sleep } from "../utils/flake.ts"; import { sleep } from "../utils/flake.ts";
import curriedLogger from "../utils/logger.ts";
const logger = curriedLogger(import.meta.filename); const logger = curriedLogger(import.meta.filename);
if (!process.env["REDIS_URI"]) if (!process.env["REDIS_URI"])

View File

@@ -24,6 +24,8 @@ import myGraph from "../utils/graph.ts";
import { parseSpotifyLink } from "../utils/spotifyUriTransformer.ts"; import { parseSpotifyLink } from "../utils/spotifyUriTransformer.ts";
// import { randomBool, sleep } from "../utils/flake.ts"; // import { randomBool, sleep } from "../utils/flake.ts";
import { redisClient } from "../config/redis.ts";
// load db models // load db models
import Playlists from "../models/playlists.ts"; import Playlists from "../models/playlists.ts";
import Links from "../models/links.ts"; import Links from "../models/links.ts";
@@ -411,12 +413,12 @@ interface _GetPlaylistTracks {
is_local: boolean; is_local: boolean;
uri: string; uri: string;
}[]; }[];
snapshot_id: string; snapshotID: string;
} }
const _getPlaylistTracks: ( const _getPlaylistTracks: (
opts: _GetPlaylistTracksArgs opts: _GetPlaylistTracksArgs
) => Promise<_GetPlaylistTracks | null> = async ({ req, res, playlistID }) => { ) => Promise<_GetPlaylistTracks | null> = async ({ req, res, playlistID }) => {
let initialFields = ["tracks(next,items(is_local,track(uri)))"]; let initialFields = ["snapshot_id,tracks(next,items(is_local,track(uri)))"];
let mainFields = ["next", "items(is_local,track(uri))"]; let mainFields = ["next", "items(is_local,track(uri))"];
const respData = await getPlaylistDetailsFirstPage({ const respData = await getPlaylistDetailsFirstPage({
@@ -427,13 +429,23 @@ const _getPlaylistTracks: (
}); });
if (!respData) return null; if (!respData) return null;
// check cache
const cachedSnapshotID = await redisClient.get(
"playlist_snapshot:" + playlistID
);
if (cachedSnapshotID === respData.snapshot_id) {
const cachedTracksData = (await redisClient.json.get(
"playlist_tracks:" + playlistID
)) as _GetPlaylistTracks["tracks"];
return { tracks: cachedTracksData, snapshotID: cachedSnapshotID };
}
const pl: _GetPlaylistTracks = { const pl: _GetPlaylistTracks = {
tracks: [], tracks: [],
snapshot_id: respData.snapshot_id, snapshotID: respData.snapshot_id,
}; };
let nextURL; let nextURL;
// varying fields again smh
if (respData.tracks.next) { if (respData.tracks.next) {
nextURL = new URL(respData.tracks.next); nextURL = new URL(respData.tracks.next);
nextURL.searchParams.set("fields", mainFields.join()); nextURL.searchParams.set("fields", mainFields.join());
@@ -467,6 +479,13 @@ const _getPlaylistTracks: (
nextURL = nextData.next; nextURL = nextData.next;
} }
// cache new data
await redisClient.set(
"playlist_snapshot:" + playlistID,
respData.snapshot_id
);
await redisClient.json.set("playlist_tracks:" + playlistID, "$", pl.tracks);
return pl; return pl;
}; };
@@ -662,7 +681,7 @@ const _pruneSingleLinkCore: (
const toDelNum = indexes.length; const toDelNum = indexes.length;
// remove in batches of 100 (from reverse, to preserve positions while modifying) // remove in batches of 100 (from reverse, to preserve positions while modifying)
let currentSnapshot = toPlaylist.snapshot_id; let currentSnapshot = toPlaylist.snapshotID;
while (indexes.length > 0) { while (indexes.length > 0) {
const nextBatch = indexes.splice(Math.max(indexes.length - 100, 0), 100); const nextBatch = indexes.splice(Math.max(indexes.length - 100, 0), 100);
const delResponse = await removePlaylistItems({ const delResponse = await removePlaylistItems({

View File

@@ -25,6 +25,11 @@ import type {
UserObject, UserObject,
} from "./objects.types.ts"; } from "./objects.types.ts";
// TODO: the data that actually gets retrieved from Spotify
// depends on the fields I pass as parameters to the API
// so... technically all fields are optional? but that's so
// horrible...
// GET method // GET method
// Albums // Albums
export type GetAlbum = AlbumObject; export type GetAlbum = AlbumObject;