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

fix: avoid duplicate scrobbles

+52 -54
+52 -54
apps/api/src/nowplaying/nowplaying.service.ts
··· 13 13 14 14 export async function putArtistRecord( 15 15 track: Track, 16 - agent: Agent, 16 + agent: Agent 17 17 ): Promise<string | null> { 18 18 const rkey = TID.nextStr(); 19 19 const record: { ··· 54 54 55 55 export async function putAlbumRecord( 56 56 track: Track, 57 - agent: Agent, 57 + agent: Agent 58 58 ): Promise<string | null> { 59 59 const rkey = TID.nextStr(); 60 60 ··· 94 94 95 95 export async function putSongRecord( 96 96 track: Track, 97 - agent: Agent, 97 + agent: Agent 98 98 ): Promise<string | null> { 99 99 const rkey = TID.nextStr(); 100 100 ··· 146 146 147 147 async function putScrobbleRecord( 148 148 track: Track, 149 - agent: Agent, 149 + agent: Agent 150 150 ): Promise<string | null> { 151 151 const rkey = TID.nextStr(); 152 152 ··· 308 308 ctx: Context, 309 309 track: Track, 310 310 agent: Agent, 311 - userDid: string, 311 + userDid: string 312 312 ): Promise<void> { 313 313 // check if scrobble already exists (user did + timestamp) 314 - const scrobbleTime = dayjs.unix(track.timestamp); 315 - if (track.timestamp) { 316 - const existingScrobble = await ctx.client.db.scrobbles 317 - .filter("user_id.did", equals(userDid)) 318 - .filter("track_id.title", equals(track.title)) 319 - .filter("track_id.artist", equals(track.artist)) 320 - .filter({ 321 - $any: [ 322 - { 323 - timestamp: { 324 - $ge: scrobbleTime.subtract(5, "seconds").toISOString(), 325 - }, 314 + const scrobbleTime = dayjs.unix(track.timestamp || dayjs().unix()); 315 + const existingScrobble = await ctx.client.db.scrobbles 316 + .filter("user_id.did", equals(userDid)) 317 + .filter("track_id.title", equals(track.title)) 318 + .filter("track_id.artist", equals(track.artist)) 319 + .filter({ 320 + $any: [ 321 + { 322 + timestamp: { 323 + $ge: scrobbleTime.subtract(10, "seconds").toISOString(), 326 324 }, 327 - { timestamp: { $le: scrobbleTime.add(5, "seconds").toISOString() } }, 328 - ], 329 - }) 330 - .getFirst(); 325 + }, 326 + { timestamp: { $le: scrobbleTime.add(10, "seconds").toISOString() } }, 327 + ], 328 + }) 329 + .getFirst(); 331 330 332 - if (existingScrobble) { 333 - console.log( 334 - `Scrobble already exists for ${chalk.cyan(track.title)} at ${chalk.cyan( 335 - dayjs.unix(track.timestamp).format("YYYY-MM-DD HH:mm:ss"), 336 - )}`, 337 - ); 338 - return; 339 - } 331 + if (existingScrobble) { 332 + console.log( 333 + `Scrobble already exists for ${chalk.cyan(track.title)} at ${chalk.cyan( 334 + dayjs.unix(track.timestamp).format("YYYY-MM-DD HH:mm:ss") 335 + )}` 336 + ); 337 + return; 340 338 } 341 339 342 340 let existingTrack = await ctx.client.db.tracks ··· 345 343 equals( 346 344 createHash("sha256") 347 345 .update( 348 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 346 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 349 347 ) 350 - .digest("hex"), 351 - ), 348 + .digest("hex") 349 + ) 352 350 ) 353 351 .getFirst(); 354 352 ··· 359 357 equals( 360 358 createHash("sha256") 361 359 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 362 - .digest("hex"), 363 - ), 360 + .digest("hex") 361 + ) 364 362 ) 365 363 .getFirst(); 366 364 if (album) { ··· 377 375 equals( 378 376 createHash("sha256") 379 377 .update(track.albumArtist.toLowerCase()) 380 - .digest("hex"), 381 - ), 378 + .digest("hex") 379 + ) 382 380 ) 383 381 .getFirst(); 384 382 if (artist) { ··· 405 403 equals( 406 404 createHash("sha256") 407 405 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 408 - .digest("hex"), 409 - ), 406 + .digest("hex") 407 + ) 410 408 ) 411 409 .getFirst(); 412 410 ··· 419 417 equals( 420 418 createHash("sha256") 421 419 .update( 422 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 420 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 423 421 ) 424 - .digest("hex"), 425 - ), 422 + .digest("hex") 423 + ) 426 424 ) 427 425 .getFirst(); 428 426 await new Promise((resolve) => setTimeout(resolve, 1000)); ··· 435 433 436 434 if (existingTrack) { 437 435 console.log( 438 - `Song found: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries`, 436 + `Song found: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries` 439 437 ); 440 438 } 441 439 ··· 485 483 equals( 486 484 createHash("sha256") 487 485 .update( 488 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 486 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 489 487 ) 490 - .digest("hex"), 491 - ), 488 + .digest("hex") 489 + ) 492 490 ) 493 491 .getFirst(); 494 492 ··· 498 496 tries < 30 499 497 ) { 500 498 console.log( 501 - `Artist uri not ready, trying again: ${chalk.magenta(tries + 1)}`, 499 + `Artist uri not ready, trying again: ${chalk.magenta(tries + 1)}` 502 500 ); 503 501 existingTrack = await ctx.client.db.tracks 504 502 .filter( ··· 506 504 equals( 507 505 createHash("sha256") 508 506 .update( 509 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 507 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 510 508 ) 511 - .digest("hex"), 512 - ), 509 + .digest("hex") 510 + ) 513 511 ) 514 512 .getFirst(); 515 513 ··· 521 519 equals( 522 520 createHash("sha256") 523 521 .update(track.albumArtist.toLowerCase()) 524 - .digest("hex"), 525 - ), 522 + .digest("hex") 523 + ) 526 524 ) 527 525 .getFirst(); 528 526 if (artist) { ··· 541 539 equals( 542 540 createHash("sha256") 543 541 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 544 - .digest("hex"), 545 - ), 542 + .digest("hex") 543 + ) 546 544 ) 547 545 .getFirst(); 548 546 if (album) { ··· 569 567 570 568 if (existingTrack?.artist_uri) { 571 569 console.log( 572 - `Artist uri ready: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries`, 570 + `Artist uri ready: ${chalk.cyan(existingTrack.xata_id)} - ${track.title}, after ${chalk.magenta(tries)} tries` 573 571 ); 574 572 } 575 573