A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
98
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(sync): add background syncing for new scrobbles with delay and logging

+65 -36
+38 -36
apps/api/src/nowplaying/nowplaying.service.ts
··· 14 14 15 15 export async function putArtistRecord( 16 16 track: Track, 17 - agent: Agent, 17 + agent: Agent 18 18 ): Promise<string | null> { 19 19 const rkey = TID.nextStr(); 20 20 const record: { ··· 61 61 62 62 export async function putAlbumRecord( 63 63 track: Track, 64 - agent: Agent, 64 + agent: Agent 65 65 ): Promise<string | null> { 66 66 const rkey = TID.nextStr(); 67 - let albumArt ; 67 + let albumArt; 68 68 69 69 if (track.albumArt) { 70 - let options ; 70 + let options; 71 71 if (track.albumArt.endsWith(".jpeg") || track.albumArt.endsWith(".jpg")) { 72 72 options = { encoding: "image/jpeg" }; 73 73 } ··· 121 121 122 122 export async function putSongRecord( 123 123 track: Track, 124 - agent: Agent, 124 + agent: Agent 125 125 ): Promise<string | null> { 126 126 const rkey = TID.nextStr(); 127 - let albumArt ; 127 + let albumArt; 128 128 129 129 if (track.albumArt) { 130 - let options ; 130 + let options; 131 131 if (track.albumArt.endsWith(".jpeg") || track.albumArt.endsWith(".jpg")) { 132 132 options = { encoding: "image/jpeg" }; 133 133 } ··· 188 188 189 189 async function putScrobbleRecord( 190 190 track: Track, 191 - agent: Agent, 191 + agent: Agent 192 192 ): Promise<string | null> { 193 193 const rkey = TID.nextStr(); 194 - let albumArt ; 194 + let albumArt; 195 195 196 196 if (track.albumArt) { 197 - let options ; 197 + let options; 198 198 if (track.albumArt.endsWith(".jpeg") || track.albumArt.endsWith(".jpg")) { 199 199 options = { encoding: "image/jpeg" }; 200 200 } ··· 365 365 ctx: Context, 366 366 track: Track, 367 367 agent: Agent, 368 - userDid: string, 368 + userDid: string 369 369 ): Promise<void> { 370 370 let existingTrack = await ctx.client.db.tracks 371 371 .filter( ··· 373 373 equals( 374 374 createHash("sha256") 375 375 .update( 376 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 376 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 377 377 ) 378 - .digest("hex"), 379 - ), 378 + .digest("hex") 379 + ) 380 380 ) 381 381 .getFirst(); 382 382 ··· 387 387 equals( 388 388 createHash("sha256") 389 389 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 390 - .digest("hex"), 391 - ), 390 + .digest("hex") 391 + ) 392 392 ) 393 393 .getFirst(); 394 394 if (album) { ··· 405 405 equals( 406 406 createHash("sha256") 407 407 .update(track.albumArtist.toLowerCase()) 408 - .digest("hex"), 409 - ), 408 + .digest("hex") 409 + ) 410 410 ) 411 411 .getFirst(); 412 412 if (artist) { ··· 433 433 equals( 434 434 createHash("sha256") 435 435 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 436 - .digest("hex"), 437 - ), 436 + .digest("hex") 437 + ) 438 438 ) 439 439 .getFirst(); 440 440 ··· 447 447 equals( 448 448 createHash("sha256") 449 449 .update( 450 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 450 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 451 451 ) 452 - .digest("hex"), 453 - ), 452 + .digest("hex") 453 + ) 454 454 ) 455 455 .getFirst(); 456 456 await new Promise((resolve) => setTimeout(resolve, 1000)); ··· 463 463 464 464 if (existingTrack) { 465 465 console.log( 466 - `Song found: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries`, 466 + `Song found: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries` 467 467 ); 468 468 } 469 469 ··· 513 513 equals( 514 514 createHash("sha256") 515 515 .update( 516 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 516 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 517 517 ) 518 - .digest("hex"), 519 - ), 518 + .digest("hex") 519 + ) 520 520 ) 521 521 .getFirst(); 522 522 ··· 526 526 tries < 30 527 527 ) { 528 528 console.log( 529 - `Artist uri not ready, trying again: ${chalk.magenta(tries + 1)}`, 529 + `Artist uri not ready, trying again: ${chalk.magenta(tries + 1)}` 530 530 ); 531 531 existingTrack = await ctx.client.db.tracks 532 532 .filter( ··· 534 534 equals( 535 535 createHash("sha256") 536 536 .update( 537 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 537 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 538 538 ) 539 - .digest("hex"), 540 - ), 539 + .digest("hex") 540 + ) 541 541 ) 542 542 .getFirst(); 543 543 ··· 549 549 equals( 550 550 createHash("sha256") 551 551 .update(track.albumArtist.toLowerCase()) 552 - .digest("hex"), 553 - ), 552 + .digest("hex") 553 + ) 554 554 ) 555 555 .getFirst(); 556 556 if (artist) { ··· 569 569 equals( 570 570 createHash("sha256") 571 571 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 572 - .digest("hex"), 573 - ), 572 + .digest("hex") 573 + ) 574 574 ) 575 575 .getFirst(); 576 576 if (album) { ··· 597 597 598 598 if (existingTrack?.artist_uri) { 599 599 console.log( 600 - `Artist uri ready: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries`, 600 + `Artist uri ready: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries` 601 601 ); 602 602 } 603 603 ··· 650 650 if (tries === 30 && !scrobble) { 651 651 console.log(`Scrobble not found after ${chalk.magenta("30 tries")}`); 652 652 } 653 + 654 + ctx.nc.publish("rocksky.user.scrobble.sync", Buffer.from(userDid)); 653 655 }
+27
apps/api/src/scripts/sync.ts
··· 102 102 } 103 103 } 104 104 105 + if (args.includes("--background")) { 106 + console.log("Wait for new scrobbles to sync ..."); 107 + const sub = ctx.nc.subscribe("rocksky.user.scrobble.sync"); 108 + for await (const m of sub) { 109 + const did = new TextDecoder().decode(m.data); 110 + // wait for 10 seconds to ensure the scrobble is fully created 111 + await new Promise((resolve) => setTimeout(resolve, 10000)); 112 + console.log(`Syncing scrobbles ${chalk.magenta(did)} ...`); 113 + await updateUris(did); 114 + const { records } = await ctx.client.db.scrobbles 115 + .filter({ 116 + $any: [{ "user_id.did": did }, { "user_id.handle": did }], 117 + }) 118 + .getPaginated({ 119 + pagination: { 120 + size: 5, 121 + }, 122 + sort: [{ xata_createdat: "desc" }], 123 + }); 124 + for (const scrobble of records) { 125 + console.log(`Syncing scrobble ${chalk.cyan(scrobble.xata_id)} ...`); 126 + await publishScrobble(ctx, scrobble.xata_id); 127 + } 128 + } 129 + process.exit(0); 130 + } 131 + 105 132 for (const arg of args) { 106 133 console.log(`Syncing scrobbles ${chalk.magenta(arg)} ...`); 107 134 await updateUris(arg);