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.

Move delay before broadcasting to clients

Replace per-socket 5s wait with a single 4s pause before iterating
clients to avoid per-client staggering and reduce total delay Move delay
before broadcasting to clients

Wait 4s before fetching broadcast payload and reuse the results for all
matching clients instead of delaying per-socket. This avoids redundant
data retrieval and reduces overall broadcast latency.

Wrap Effect pipelines in getScrobbles, getScrobblesChart and
getActorScrobbles with Effect.runPromise so they execute and return
promises.

+57 -50
+13 -12
apps/ws/src/main.ts
··· 68 68 69 69 logger.info` Cursor: ${event.time_us}`; 70 70 71 - for (const [socket, channels] of clients) { 72 - if (channels.has(collection) && socket.readyState === WebSocket.OPEN) { 73 - try { 74 - await new Promise((resolve) => setTimeout(resolve, 5000)); 75 - const nowPlayings = await getNowPlayings(ctx); 76 - const scrobbles = await getScrobbles(ctx); 77 - const scrobblesChart = await getScrobblesChart(ctx); 78 - const actorScrobbles = await getActorScrobbles(ctx, event.did); 79 - const actorAlbums = await getActorAlbums(ctx, event.did); 80 - const actorArtists = await getActorArtists(ctx, event.did); 71 + await new Promise((resolve) => setTimeout(resolve, 4000)); 72 + 73 + try { 74 + const nowPlayings = await getNowPlayings(ctx); 75 + const scrobbles = await getScrobbles(ctx); 76 + const scrobblesChart = await getScrobblesChart(ctx); 77 + const actorScrobbles = await getActorScrobbles(ctx, event.did); 78 + const actorAlbums = await getActorAlbums(ctx, event.did); 79 + const actorArtists = await getActorArtists(ctx, event.did); 81 80 81 + for (const [socket, channels] of clients) { 82 + if (channels.has(collection) && socket.readyState === WebSocket.OPEN) { 82 83 socket.send( 83 84 JSON.stringify({ 84 85 nowPlayings, ··· 91 92 did: event.did, 92 93 }), 93 94 ); 94 - } catch (error) { 95 - logger.error`Failed to send data to client: ${error}`; 96 95 } 97 96 } 97 + } catch (error) { 98 + logger.error`Failed to send data to client: ${error}`; 98 99 } 99 100 } 100 101 });
+16 -14
apps/ws/src/services/getActorScrobbles.ts
··· 3 3 import { deepCamelCaseKeys } from "../lib/deepCamelKeys.ts"; 4 4 5 5 export default function (ctx: Context, did: string) { 6 - return pipe( 7 - retrieve({ 8 - ctx, 9 - params: { 10 - did, 11 - offset: 0, 12 - limit: 10, 13 - }, 14 - }), 15 - Effect.flatMap(presentation), 16 - Effect.retry({ times: 3 }), 17 - Effect.timeout("10 seconds"), 18 - Effect.catchAll((error) => 19 - Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 6 + return Effect.runPromise( 7 + pipe( 8 + retrieve({ 9 + ctx, 10 + params: { 11 + did, 12 + offset: 0, 13 + limit: 10, 14 + }, 15 + }), 16 + Effect.flatMap(presentation), 17 + Effect.retry({ times: 3 }), 18 + Effect.timeout("10 seconds"), 19 + Effect.catchAll((error) => 20 + Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 21 + ), 20 22 ), 21 23 ); 22 24 }
+14 -12
apps/ws/src/services/getScrobbles.ts
··· 4 4 import { eq, desc } from "drizzle-orm"; 5 5 6 6 export default function (ctx: Context, offset?: number, limit?: number) { 7 - return pipe( 8 - retrieve({ 9 - ctx, 10 - params: { 11 - offset: offset || 0, 12 - limit: limit || 20, 13 - }, 14 - }), 15 - Effect.retry({ times: 3 }), 16 - Effect.timeout("10 seconds"), 17 - Effect.catchAll((error) => 18 - Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 7 + return Effect.runPromise( 8 + pipe( 9 + retrieve({ 10 + ctx, 11 + params: { 12 + offset: offset || 0, 13 + limit: limit || 20, 14 + }, 15 + }), 16 + Effect.retry({ times: 3 }), 17 + Effect.timeout("10 seconds"), 18 + Effect.catchAll((error) => 19 + Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 20 + ), 19 21 ), 20 22 ); 21 23 }
+14 -12
apps/ws/src/services/getScrobblesChart.ts
··· 4 4 import tables from "../schema/mod.ts"; 5 5 6 6 export default function (ctx: Context, did?: string) { 7 - return pipe( 8 - retrieve({ 9 - ctx, 10 - params: { 11 - did, 12 - }, 13 - }), 14 - Effect.flatMap(presentation), 15 - Effect.retry({ times: 3 }), 16 - Effect.timeout("10 seconds"), 17 - Effect.catchAll((error) => 18 - Effect.fail(new Error(`Failed to retrieve scrobbles chart: ${error}`)), 7 + return Effect.runPromise( 8 + pipe( 9 + retrieve({ 10 + ctx, 11 + params: { 12 + did, 13 + }, 14 + }), 15 + Effect.flatMap(presentation), 16 + Effect.retry({ times: 3 }), 17 + Effect.timeout("10 seconds"), 18 + Effect.catchAll((error) => 19 + Effect.fail(new Error(`Failed to retrieve scrobbles chart: ${error}`)), 20 + ), 19 21 ), 20 22 ); 21 23 }