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

fix: add missing commas in type definitions and function parameters for consistency

+73 -73
+3 -3
apps/api/src/index.ts
··· 46 rateLimiter({ 47 limit: 1000, 48 window: 30, // 👈 30 seconds 49 - }) 50 ); 51 52 app.use("*", async (c, next) => { ··· 161 ctx.redis.get(`nowplaying:${user.did}:status`), 162 ]); 163 return c.json( 164 - nowPlaying ? { ...JSON.parse(nowPlaying), is_playing: status === "1" } : {} 165 ); 166 }); 167 ··· 313 listeners: 1, 314 sha256: item.track.sha256, 315 id: item.scrobble.id, 316 - })) 317 ); 318 }); 319
··· 46 rateLimiter({ 47 limit: 1000, 48 window: 30, // 👈 30 seconds 49 + }), 50 ); 51 52 app.use("*", async (c, next) => { ··· 161 ctx.redis.get(`nowplaying:${user.did}:status`), 162 ]); 163 return c.json( 164 + nowPlaying ? { ...JSON.parse(nowPlaying), is_playing: status === "1" } : {}, 165 ); 166 }); 167 ··· 313 listeners: 1, 314 sha256: item.track.sha256, 315 id: item.scrobble.id, 316 + })), 317 ); 318 }); 319
+1 -1
apps/api/src/lexicon/types/app/rocksky/apikey/createApikey.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyApikeyDefs from "./defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The name of the API key. */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyApikeyDefs from "./defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The name of the API key. */
+1 -1
apps/api/src/lexicon/types/app/rocksky/apikey/updateApikey.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyApikeyDefs from "./defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The ID of the API key to update. */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyApikeyDefs from "./defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The ID of the API key to update. */
+1 -1
apps/api/src/lexicon/types/app/rocksky/like/dislikeShout.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "../shout/defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to dislike */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "../shout/defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to dislike */
+1 -1
apps/api/src/lexicon/types/app/rocksky/like/dislikeSong.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskySongDefs from "../song/defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The unique identifier of the song to dislike */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskySongDefs from "../song/defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The unique identifier of the song to dislike */
+1 -1
apps/api/src/lexicon/types/app/rocksky/like/likeShout.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "../shout/defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to like */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "../shout/defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to like */
+1 -1
apps/api/src/lexicon/types/app/rocksky/like/likeSong.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskySongDefs from "../song/defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The unique identifier of the song to like */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskySongDefs from "../song/defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The unique identifier of the song to like */
+1 -1
apps/api/src/lexicon/types/app/rocksky/scrobble/createScrobble.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyScrobbleDefs from "./defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The title of the track being scrobbled */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyScrobbleDefs from "./defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The title of the track being scrobbled */
+1 -1
apps/api/src/lexicon/types/app/rocksky/shout/createShout.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "./defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The content of the shout */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "./defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The content of the shout */
+1 -1
apps/api/src/lexicon/types/app/rocksky/shout/replyShout.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "./defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to reply to */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "./defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to reply to */
+1 -1
apps/api/src/lexicon/types/app/rocksky/shout/reportShout.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "./defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to report */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskyShoutDefs from "./defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The unique identifier of the shout to report */
+1 -1
apps/api/src/lexicon/types/app/rocksky/song/createSong.ts
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskySongDefs from "./defs"; 11 12 - export type QueryParams = {} 13 14 export interface InputSchema { 15 /** The title of the song */
··· 9 import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 import type * as AppRockskySongDefs from "./defs"; 11 12 + export type QueryParams = {}; 13 14 export interface InputSchema { 15 /** The title of the song */
+1 -1
apps/api/src/lexicon/types/app/rocksky/spotify/next.ts
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 - export type QueryParams = {} 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 + export type QueryParams = {}; 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
+1 -1
apps/api/src/lexicon/types/app/rocksky/spotify/pause.ts
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 - export type QueryParams = {} 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 + export type QueryParams = {}; 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
+1 -1
apps/api/src/lexicon/types/app/rocksky/spotify/play.ts
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 - export type QueryParams = {} 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 + export type QueryParams = {}; 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
+1 -1
apps/api/src/lexicon/types/app/rocksky/spotify/previous.ts
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 - export type QueryParams = {} 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
··· 8 import { CID } from "multiformats/cid"; 9 import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 11 + export type QueryParams = {}; 12 13 export type InputSchema = undefined; 14 export type HandlerInput = undefined;
+46 -46
apps/api/src/nowplaying/nowplaying.service.ts
··· 26 27 export async function putArtistRecord( 28 track: Track, 29 - agent: Agent 30 ): Promise<string | null> { 31 const rkey = TID.nextStr(); 32 const record: Artist.Record = { ··· 62 63 export async function putAlbumRecord( 64 track: Track, 65 - agent: Agent 66 ): Promise<string | null> { 67 const rkey = TID.nextStr(); 68 ··· 103 104 export async function putSongRecord( 105 track: Track, 106 - agent: Agent 107 ): Promise<string | null> { 108 const rkey = TID.nextStr(); 109 ··· 158 159 async function putScrobbleRecord( 160 track: Track, 161 - agent: Agent 162 ): Promise<string | null> { 163 const rkey = TID.nextStr(); 164 ··· 276 .where( 277 and( 278 eq(artistAlbums.albumId, scrobble.album.id), 279 - eq(artistAlbums.artistId, scrobble.artist.id) 280 - ) 281 ) 282 .limit(1) 283 .then((rows) => rows[0]), ··· 440 }, 441 }), 442 null, 443 - 2 444 ); 445 446 ctx.nc.publish( 447 "rocksky.scrobble", 448 - Buffer.from(message.replaceAll("sha_256", "sha256")) 449 ); 450 451 const trackMessage = JSON.stringify( ··· 492 xata_createdat: artist_album.createdAt.toISOString(), 493 xata_updatedat: artist_album.updatedAt.toISOString(), 494 }, 495 - }) 496 ); 497 498 ctx.nc.publish( 499 "rocksky.track", 500 - Buffer.from(trackMessage.replaceAll("sha_256", "sha256")) 501 ); 502 } 503 ··· 505 ctx: Context, 506 track: Track, 507 agent: Agent, 508 - userDid: string 509 ): Promise<void> { 510 // check if scrobble already exists (user did + timestamp) 511 const scrobbleTime = dayjs.unix(track.timestamp || dayjs().unix()); ··· 524 eq(tracks.title, track.title), 525 eq(tracks.artist, track.artist), 526 gte(scrobbles.timestamp, scrobbleTime.subtract(60, "seconds").toDate()), 527 - lte(scrobbles.timestamp, scrobbleTime.add(60, "seconds").toDate()) 528 - ) 529 ) 530 .limit(1) 531 .then((rows) => rows[0]); ··· 533 if (existingScrobble) { 534 console.log( 535 `Scrobble already exists for ${chalk.cyan(track.title)} at ${chalk.cyan( 536 - scrobbleTime.format("YYYY-MM-DD HH:mm:ss") 537 - )}` 538 ); 539 return; 540 } ··· 547 tracks.sha256, 548 createHash("sha256") 549 .update( 550 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 551 ) 552 - .digest("hex") 553 - ) 554 ) 555 .limit(1) 556 .then((rows) => rows[0]); ··· 564 albums.sha256, 565 createHash("sha256") 566 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 567 - .digest("hex") 568 - ) 569 ) 570 .limit(1) 571 .then((rows) => rows[0]); ··· 586 artists.sha256, 587 createHash("sha256") 588 .update(track.albumArtist.toLowerCase()) 589 - .digest("hex") 590 - ) 591 ) 592 .limit(1) 593 .then((rows) => rows[0]); ··· 618 artist: track.artist.split(",").map((a) => ({ name: a.trim() })), 619 name: track.title, 620 album: track.album, 621 - } 622 ); 623 624 if (!mbTrack?.trackMBID) { ··· 647 albums.sha256, 648 createHash("sha256") 649 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 650 - .digest("hex") 651 - ) 652 ) 653 .limit(1) 654 .then((rows) => rows[0]); ··· 664 tracks.sha256, 665 createHash("sha256") 666 .update( 667 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 668 ) 669 - .digest("hex") 670 - ) 671 ) 672 .limit(1) 673 .then((rows) => rows[0]); ··· 681 682 if (existingTrack) { 683 console.log( 684 - `Song found: ${chalk.cyan(existingTrack.id)} - ${track.title}, after ${chalk.magenta(tries)} tries` 685 ); 686 } 687 ··· 694 artists.sha256, 695 createHash("sha256") 696 .update(track.albumArtist.toLowerCase()) 697 - .digest("hex") 698 ), 699 eq( 700 artists.sha256, 701 - createHash("sha256").update(track.artist.toLowerCase()).digest("hex") 702 - ) 703 - ) 704 ) 705 .limit(1) 706 .then((rows) => rows[0]); ··· 715 .innerJoin(artists, eq(userArtists.artistId, artists.id)) 716 .innerJoin(users, eq(userArtists.userId, users.id)) 717 .where( 718 - and(eq(artists.id, existingArtist?.id || ""), eq(users.did, userDid)) 719 ) 720 .limit(1) 721 .then((rows) => rows[0]); ··· 750 tracks.sha256, 751 createHash("sha256") 752 .update( 753 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 754 ) 755 - .digest("hex") 756 - ) 757 ) 758 .limit(1) 759 .then((rows) => rows[0]); 760 761 while (!existingTrack?.artistUri && !existingTrack?.albumUri && tries < 30) { 762 console.log( 763 - `Artist uri not ready, trying again: ${chalk.magenta(tries + 1)}` 764 ); 765 existingTrack = await ctx.db 766 .select() ··· 770 tracks.sha256, 771 createHash("sha256") 772 .update( 773 - `${track.title} - ${track.artist} - ${track.album}`.toLowerCase() 774 ) 775 - .digest("hex") 776 - ) 777 ) 778 .limit(1) 779 .then((rows) => rows[0]); ··· 788 artists.sha256, 789 createHash("sha256") 790 .update(track.albumArtist.toLowerCase()) 791 - .digest("hex") 792 - ) 793 ) 794 .limit(1) 795 .then((rows) => rows[0]); ··· 812 albums.sha256, 813 createHash("sha256") 814 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 815 - .digest("hex") 816 - ) 817 ) 818 .limit(1) 819 .then((rows) => rows[0]); ··· 843 844 if (existingTrack?.artistUri) { 845 console.log( 846 - `Artist uri ready: ${chalk.cyan(existingTrack.id)} - ${track.title}, after ${chalk.magenta(tries)} tries` 847 ); 848 } 849
··· 26 27 export async function putArtistRecord( 28 track: Track, 29 + agent: Agent, 30 ): Promise<string | null> { 31 const rkey = TID.nextStr(); 32 const record: Artist.Record = { ··· 62 63 export async function putAlbumRecord( 64 track: Track, 65 + agent: Agent, 66 ): Promise<string | null> { 67 const rkey = TID.nextStr(); 68 ··· 103 104 export async function putSongRecord( 105 track: Track, 106 + agent: Agent, 107 ): Promise<string | null> { 108 const rkey = TID.nextStr(); 109 ··· 158 159 async function putScrobbleRecord( 160 track: Track, 161 + agent: Agent, 162 ): Promise<string | null> { 163 const rkey = TID.nextStr(); 164 ··· 276 .where( 277 and( 278 eq(artistAlbums.albumId, scrobble.album.id), 279 + eq(artistAlbums.artistId, scrobble.artist.id), 280 + ), 281 ) 282 .limit(1) 283 .then((rows) => rows[0]), ··· 440 }, 441 }), 442 null, 443 + 2, 444 ); 445 446 ctx.nc.publish( 447 "rocksky.scrobble", 448 + Buffer.from(message.replaceAll("sha_256", "sha256")), 449 ); 450 451 const trackMessage = JSON.stringify( ··· 492 xata_createdat: artist_album.createdAt.toISOString(), 493 xata_updatedat: artist_album.updatedAt.toISOString(), 494 }, 495 + }), 496 ); 497 498 ctx.nc.publish( 499 "rocksky.track", 500 + Buffer.from(trackMessage.replaceAll("sha_256", "sha256")), 501 ); 502 } 503 ··· 505 ctx: Context, 506 track: Track, 507 agent: Agent, 508 + userDid: string, 509 ): Promise<void> { 510 // check if scrobble already exists (user did + timestamp) 511 const scrobbleTime = dayjs.unix(track.timestamp || dayjs().unix()); ··· 524 eq(tracks.title, track.title), 525 eq(tracks.artist, track.artist), 526 gte(scrobbles.timestamp, scrobbleTime.subtract(60, "seconds").toDate()), 527 + lte(scrobbles.timestamp, scrobbleTime.add(60, "seconds").toDate()), 528 + ), 529 ) 530 .limit(1) 531 .then((rows) => rows[0]); ··· 533 if (existingScrobble) { 534 console.log( 535 `Scrobble already exists for ${chalk.cyan(track.title)} at ${chalk.cyan( 536 + scrobbleTime.format("YYYY-MM-DD HH:mm:ss"), 537 + )}`, 538 ); 539 return; 540 } ··· 547 tracks.sha256, 548 createHash("sha256") 549 .update( 550 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 551 ) 552 + .digest("hex"), 553 + ), 554 ) 555 .limit(1) 556 .then((rows) => rows[0]); ··· 564 albums.sha256, 565 createHash("sha256") 566 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 567 + .digest("hex"), 568 + ), 569 ) 570 .limit(1) 571 .then((rows) => rows[0]); ··· 586 artists.sha256, 587 createHash("sha256") 588 .update(track.albumArtist.toLowerCase()) 589 + .digest("hex"), 590 + ), 591 ) 592 .limit(1) 593 .then((rows) => rows[0]); ··· 618 artist: track.artist.split(",").map((a) => ({ name: a.trim() })), 619 name: track.title, 620 album: track.album, 621 + }, 622 ); 623 624 if (!mbTrack?.trackMBID) { ··· 647 albums.sha256, 648 createHash("sha256") 649 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 650 + .digest("hex"), 651 + ), 652 ) 653 .limit(1) 654 .then((rows) => rows[0]); ··· 664 tracks.sha256, 665 createHash("sha256") 666 .update( 667 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 668 ) 669 + .digest("hex"), 670 + ), 671 ) 672 .limit(1) 673 .then((rows) => rows[0]); ··· 681 682 if (existingTrack) { 683 console.log( 684 + `Song found: ${chalk.cyan(existingTrack.id)} - ${track.title}, after ${chalk.magenta(tries)} tries`, 685 ); 686 } 687 ··· 694 artists.sha256, 695 createHash("sha256") 696 .update(track.albumArtist.toLowerCase()) 697 + .digest("hex"), 698 ), 699 eq( 700 artists.sha256, 701 + createHash("sha256").update(track.artist.toLowerCase()).digest("hex"), 702 + ), 703 + ), 704 ) 705 .limit(1) 706 .then((rows) => rows[0]); ··· 715 .innerJoin(artists, eq(userArtists.artistId, artists.id)) 716 .innerJoin(users, eq(userArtists.userId, users.id)) 717 .where( 718 + and(eq(artists.id, existingArtist?.id || ""), eq(users.did, userDid)), 719 ) 720 .limit(1) 721 .then((rows) => rows[0]); ··· 750 tracks.sha256, 751 createHash("sha256") 752 .update( 753 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 754 ) 755 + .digest("hex"), 756 + ), 757 ) 758 .limit(1) 759 .then((rows) => rows[0]); 760 761 while (!existingTrack?.artistUri && !existingTrack?.albumUri && tries < 30) { 762 console.log( 763 + `Artist uri not ready, trying again: ${chalk.magenta(tries + 1)}`, 764 ); 765 existingTrack = await ctx.db 766 .select() ··· 770 tracks.sha256, 771 createHash("sha256") 772 .update( 773 + `${track.title} - ${track.artist} - ${track.album}`.toLowerCase(), 774 ) 775 + .digest("hex"), 776 + ), 777 ) 778 .limit(1) 779 .then((rows) => rows[0]); ··· 788 artists.sha256, 789 createHash("sha256") 790 .update(track.albumArtist.toLowerCase()) 791 + .digest("hex"), 792 + ), 793 ) 794 .limit(1) 795 .then((rows) => rows[0]); ··· 812 albums.sha256, 813 createHash("sha256") 814 .update(`${track.album} - ${track.albumArtist}`.toLowerCase()) 815 + .digest("hex"), 816 + ), 817 ) 818 .limit(1) 819 .then((rows) => rows[0]); ··· 843 844 if (existingTrack?.artistUri) { 845 console.log( 846 + `Artist uri ready: ${chalk.cyan(existingTrack.id)} - ${track.title}, after ${chalk.magenta(tries)} tries`, 847 ); 848 } 849
+4 -4
apps/api/src/sqliteKv.ts
··· 101 oc.column("id").doUpdateSet({ 102 value, 103 updated_at: now, 104 - }) 105 ) 106 .execute(); 107 }, ··· 126 oc.column("id").doUpdateSet({ 127 value, 128 updated_at: now, 129 - }) 130 ) 131 .execute(); 132 - }) 133 ); 134 }); 135 }, ··· 170 await getDb().destroy(); 171 }, 172 }; 173 - } 174 );
··· 101 oc.column("id").doUpdateSet({ 102 value, 103 updated_at: now, 104 + }), 105 ) 106 .execute(); 107 }, ··· 126 oc.column("id").doUpdateSet({ 127 value, 128 updated_at: now, 129 + }), 130 ) 131 .execute(); 132 + }), 133 ); 134 }); 135 }, ··· 170 await getDb().destroy(); 171 }, 172 }; 173 + }, 174 );
+5 -5
apps/api/src/tealfm/index.ts
··· 20 async function publishPlayingNow( 21 agent: Agent, 22 track: MusicbrainzTrack, 23 - duration: number 24 ) { 25 try { 26 // wait 60 seconds to ensure the track is actually being played ··· 34 // diff in seconds less than 60 35 Math.abs( 36 new Date(record.playedTime).getTime() - 37 - new Date(track.timestamp).getTime() 38 ) < 60000 39 ); 40 }); 41 if (alreadyPlayed) { 42 console.log( 43 `Track ${chalk.cyan(track.name)} by ${chalk.cyan( 44 - track.artist.map((a) => a.name).join(", ") 45 - )} already played recently. Skipping...` 46 ); 47 return; 48 } ··· 88 async function publishStatus( 89 agent: Agent, 90 track: MusicbrainzTrack, 91 - duration: number 92 ) { 93 const item: PlayView = { 94 trackName: track.name,
··· 20 async function publishPlayingNow( 21 agent: Agent, 22 track: MusicbrainzTrack, 23 + duration: number, 24 ) { 25 try { 26 // wait 60 seconds to ensure the track is actually being played ··· 34 // diff in seconds less than 60 35 Math.abs( 36 new Date(record.playedTime).getTime() - 37 + new Date(track.timestamp).getTime(), 38 ) < 60000 39 ); 40 }); 41 if (alreadyPlayed) { 42 console.log( 43 `Track ${chalk.cyan(track.name)} by ${chalk.cyan( 44 + track.artist.map((a) => a.name).join(", "), 45 + )} already played recently. Skipping...`, 46 ); 47 return; 48 } ··· 88 async function publishStatus( 89 agent: Agent, 90 track: MusicbrainzTrack, 91 + duration: number, 92 ) { 93 const item: PlayView = { 94 trackName: track.name,