A decentralized music tracking and discovery platform built on AT Protocol 🎵

Sync scrobbles before publishing when needed

Add shouldSync(agent) to check remote scrobble records and the local DB;
call sync() if the latest remote CID is not present locally before
publishing a scrobble. Import sync from cmd/sync. Also update the
matchTrack logger to include the matched title and artist.

+39 -3
+1 -2
apps/cli/src/lib/matchTrack.ts
··· 36 36 await ctx.kv.setItem(`${track} - ${artist}`, match); 37 37 } 38 38 39 - logger.info`Matched track ${track}, ${artist}`; 40 - logger.info`${match}`; 39 + logger.info`Matched track ${match.title} by${match.artist}`; 41 40 42 41 return match; 43 42 }
+38 -1
apps/cli/src/scrobble.ts
··· 16 16 import * as Song from "lexicon/types/app/rocksky/song"; 17 17 import { TID } from "@atproto/common"; 18 18 import { Agent } from "@atproto/api"; 19 - import { createUser, subscribeToJetstream } from "cmd/sync"; 19 + import { createUser, subscribeToJetstream, sync } from "cmd/sync"; 20 20 21 21 export async function publishScrobble( 22 22 track: MatchTrackResult, ··· 49 49 } 50 50 51 51 logger.info`${handle} Publishing scrobble for ${track.title} by ${track.artist} at ${timestamp ? dayjs.unix(timestamp).format("YYYY-MM-DD HH:mm:ss") : dayjs().format("YYYY-MM-DD HH:mm:ss")}`; 52 + 53 + if (await shouldSync(agent)) { 54 + logger.info`${handle} Syncing scrobbles before publishing`; 55 + await sync(); 56 + } else { 57 + logger.info`${handle} No need to sync scrobbles before publishing`; 58 + } 52 59 53 60 if (dryRun) { 54 61 logger.info`${handle} Dry run: Skipping publishing scrobble for ${track.title} by ${track.artist} at ${timestamp ? dayjs.unix(timestamp).format("YYYY-MM-DD HH:mm:ss") : dayjs().format("YYYY-MM-DD HH:mm:ss")}`; ··· 344 351 return null; 345 352 } 346 353 } 354 + 355 + async function shouldSync(agent: Agent): Promise<boolean> { 356 + const res = await agent.com.atproto.repo.listRecords({ 357 + repo: agent.assertDid, 358 + collection: "app.rocksky.scrobble", 359 + limit: 1, 360 + }); 361 + 362 + const records = res.data.records as Array<{ 363 + uri: string; 364 + cid: string; 365 + value: Scrobble.Record; 366 + }>; 367 + 368 + if (!records.length) { 369 + logger.info`No scrobble records found`; 370 + return true; 371 + } 372 + 373 + const { count } = await ctx.db 374 + .select({ 375 + count: sql<number>`count(*)`, 376 + }) 377 + .from(schema.scrobbles) 378 + .where(eq(schema.scrobbles.cid, records[0].cid)) 379 + .execute() 380 + .then((result) => result[0]); 381 + 382 + return count === 0; 383 + }