From a90bf50558532722fc60fcbe274f6627e0c3810b Mon Sep 17 00:00:00 2001 From: dusk Date: Thu, 6 Feb 2025 23:57:57 +0300 Subject: [PATCH] refactor: don't fetch steam / lastfm / bsky posts on user request, instead peridocially do it in the background every minute --- bun.lockb | Bin 185888 -> 188120 bytes package.json | 2 ++ src/hooks.server.ts | 17 +++++++++++++++++ src/lib/bluesky.ts | 11 ++++++++++- src/lib/lastfm.ts | 20 +++++++------------- src/lib/steam.ts | 26 ++++++++++---------------- src/routes/+page.server.ts | 13 +++++++------ src/routes/+page.svelte | 2 ++ src/routes/log/+page.server.ts | 4 ++-- 9 files changed, 57 insertions(+), 38 deletions(-) create mode 100644 src/hooks.server.ts diff --git a/bun.lockb b/bun.lockb index 4da935d00a35e1e463ccc93c5be7873209f0a366..ce7f9263422e51b58b20f1c564f8ed1756eebbc7 100755 GIT binary patch delta 34195 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

&rXT;7;+p#h((yg7KD!Eh&YTor9;Cw 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 diff --git a/package.json b/package.json index f15566d..3e6809c 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,10 @@ "@neodrag/svelte": "^2.3.0", "@skyware/bot": "^0.3.8", "@std/toml": "npm:@jsr/std__toml", + "@types/node-schedule": "^2.1.7", "base64url": "^3.0.1", "nanoid": "^5.0.9", + "node-schedule": "^2.1.1", "rehype-autolink-headings": "^7.1.0", "rehype-slug": "^6.0.0", "robots-parser": "^3.0.1", diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..918c690 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,17 @@ +import { updateLastPosts } from '$lib/bluesky'; +import { lastFmUpdateNowPlaying } from '$lib/lastfm'; +import { steamUpdateNowPlaying } from '$lib/steam' +import { cancelJob, scheduleJob, scheduledJobs } from 'node-schedule' + +const UPDATE_LAST_JOB_NAME = "update steam game, lastfm track, bsky posts" + +if (UPDATE_LAST_JOB_NAME in scheduledJobs) { + console.log(`${UPDATE_LAST_JOB_NAME} is already running, cancelling so we can start a new one`) + cancelJob(UPDATE_LAST_JOB_NAME) +} + +console.log(`starting ${UPDATE_LAST_JOB_NAME} job...`); +scheduleJob(UPDATE_LAST_JOB_NAME, "*/1 * * * *", async () => { + console.log(`running ${UPDATE_LAST_JOB_NAME} job...`) + await Promise.all([steamUpdateNowPlaying(), lastFmUpdateNowPlaying(), updateLastPosts()]) +}).invoke() // invoke once immediately \ No newline at end of file diff --git a/src/lib/bluesky.ts b/src/lib/bluesky.ts index 0b791d0..29f5fe7 100644 --- a/src/lib/bluesky.ts +++ b/src/lib/bluesky.ts @@ -32,4 +32,13 @@ export const getUserPosts = async (did: string, includeReposts: boolean = false, feedCursor = feedData.cursor } return posts -} \ No newline at end of file +} + +const lastPosts = writable([]) + +export const updateLastPosts = async () => { + const posts = await getUserPosts("did:plc:dfl62fgb7wtjj3fcbb72naae", false, 13) + lastPosts.set(posts) +} + +export const getLastPosts = () => { return get(lastPosts) } \ No newline at end of file diff --git a/src/lib/lastfm.ts b/src/lib/lastfm.ts index 25425e5..8e34248 100644 --- a/src/lib/lastfm.ts +++ b/src/lib/lastfm.ts @@ -1,17 +1,11 @@ import { get, writable } from "svelte/store" const GET_RECENT_TRACKS_ENDPOINT = "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=yusdacra&api_key=da1911d405b5b37383e200b8f36ee9ec&format=json&limit=1" -const CACHE_EXPIRY_SECONDS = 10 type LastTrack = {name: string, artist: string, image: string | null, link: string} -type CachedLastTrack = {track: LastTrack | null, since: number} -const cachedLastTrack = writable({track: null, since: 0}) +const lastTrack = writable(null) -export const lastFmGetNowPlaying: () => Promise = async () => { - var cached = get(cachedLastTrack) - if (Date.now() - cached.since < CACHE_EXPIRY_SECONDS * 1000) { - return cached.track - } +export const lastFmUpdateNowPlaying = async () => { try { var resp = await (await fetch(GET_RECENT_TRACKS_ENDPOINT)).json() var track = resp.recenttracks.track[0] ?? null @@ -24,11 +18,11 @@ export const lastFmGetNowPlaying: () => Promise = async () => image: track.image[2]['#text'] ?? null, link: track.url, } - cachedLastTrack.set({track: data, since: Date.now()}) - return data + lastTrack.set(data) } catch(why) { console.log("could not fetch last fm: ", why) - cachedLastTrack.set({track: null, since: Date.now()}) - return null + lastTrack.set(null) } -} \ No newline at end of file +} + +export const getNowPlaying = () => { return get(lastTrack) } \ No newline at end of file diff --git a/src/lib/steam.ts b/src/lib/steam.ts index cefcc96..f82041e 100644 --- a/src/lib/steam.ts +++ b/src/lib/steam.ts @@ -4,42 +4,36 @@ import { get, writable } from "svelte/store"; const STEAM_ID = "76561198106829949" const GET_PLAYER_SUMMARY_ENDPOINT = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${env.STEAM_API_KEY}&steamids=${STEAM_ID}&format=json` -const CACHE_EXPIRY_SECONDS = 10 type LastGame = {name: string, link: string, icon: string, pfp: string} -type CachedLastGame = {game: LastGame | null, since: number} -const steamgriddbClient = writable(null); -const cachedLastGame = writable({game: null, since: 0}) +const steamgriddbClient = writable(null) +const lastGame = writable(null) -export const steamGetNowPlaying: () => Promise = async () => { +export const steamUpdateNowPlaying = async () => { var griddbClient = get(steamgriddbClient) if (griddbClient === null) { griddbClient = new SGDB(env.STEAMGRIDDB_API_KEY) steamgriddbClient.set(griddbClient) } - var cached = get(cachedLastGame) - if (Date.now() - cached.since < CACHE_EXPIRY_SECONDS * 1000) { - return cached.game - } try { var profile = (await (await fetch(GET_PLAYER_SUMMARY_ENDPOINT)).json()).response.players[0] if (!profile.gameid) { throw "no game is being played" } var icons = await griddbClient.getIconsBySteamAppId(profile.gameid, ['official']) - console.log(icons) - var game = { + //console.log(icons) + var game: LastGame = { name: profile.gameextrainfo, link: `https://store.steampowered.com/app/${profile.gameid}`, icon: icons[0].thumb.toString(), pfp: profile.avatarmedium, } - cachedLastGame.set({game, since: Date.now()}) - return game + lastGame.set(game) } catch(why) { console.log("could not fetch steam: ", why) - cachedLastGame.set({game: null, since: Date.now()}) - return null + lastGame.set(null) } -} \ No newline at end of file +} + +export const getLastGame = () => { return get(lastGame) } \ No newline at end of file diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index df4eeac..4333997 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,12 +1,13 @@ -import { getUserPosts } from "$lib/bluesky.js" -import { lastFmGetNowPlaying } from "$lib/lastfm" -import { steamGetNowPlaying } from "$lib/steam" +import { getLastPosts } from "$lib/bluesky.js" +import { getNowPlaying } from "$lib/lastfm" +import { getLastGame } from "$lib/steam" import { noteFromBskyPost } from "../components/note.svelte" export const load = async ({}) => { - const lastTrack = await lastFmGetNowPlaying() - const lastGame = await steamGetNowPlaying() - const lastNote = noteFromBskyPost((await getUserPosts("did:plc:dfl62fgb7wtjj3fcbb72naae", false, 1))[0]) + const lastTrack = getNowPlaying() + const lastGame = getLastGame() + const lastPosts = getLastPosts() + const lastNote = lastPosts.length > 0 ? noteFromBskyPost(lastPosts[0]) : null let banners: number[] = [] while (banners.length < 3) { const no = getBannerNo(banners) diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 7e5ec96..42a9362 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -146,6 +146,7 @@ + {#if data.lastNote}

+ {/if} {#if data.lastTrack}
diff --git a/src/routes/log/+page.server.ts b/src/routes/log/+page.server.ts index 021775a..49b2ef3 100644 --- a/src/routes/log/+page.server.ts +++ b/src/routes/log/+page.server.ts @@ -1,4 +1,4 @@ -import { getUserPosts } from '$lib/bluesky.js'; +import { getLastPosts, getUserPosts } from '$lib/bluesky.js'; import { noteFromBskyPost } from '../../components/note.svelte'; export const load = async ({ }) => { @@ -7,6 +7,6 @@ export const load = async ({ }) => { export const _load = async () => { return { - feedPosts: (await getUserPosts("did:plc:dfl62fgb7wtjj3fcbb72naae", false, 13)).map(noteFromBskyPost), + feedPosts: getLastPosts().map(noteFromBskyPost), } } \ No newline at end of file