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

return uri and liked in getCurrentlyPlaying (spotify) response

+60 -11
+60 -11
rockskyapi/rocksky-auth/src/xrpc/app/rocksky/spotify/getCurrentlyPlaying.ts
··· 1 1 import { HandlerAuth } from "@atproto/xrpc-server"; 2 2 import { Context } from "context"; 3 - import { eq, or } from "drizzle-orm"; 4 - import { Effect, pipe } from "effect"; 3 + import { and, eq, or } from "drizzle-orm"; 4 + import { Effect, Match, pipe } from "effect"; 5 5 import { Server } from "lexicon"; 6 6 import { QueryParams } from "lexicon/types/app/rocksky/spotify/getCurrentlyPlaying"; 7 + import { createHash } from "node:crypto"; 7 8 import tables from "schema"; 8 9 import { SelectSpotifyAccount } from "schema/spotify-accounts"; 9 10 import { SelectUser } from "schema/users"; ··· 15 16 withUser, 16 17 Effect.flatMap(withSpotifyAccount), 17 18 Effect.flatMap(retrieve), 19 + Effect.flatMap(withUriAndLikes), 18 20 Effect.flatMap(presentation), 19 21 Effect.retry({ times: 3 }), 20 22 Effect.timeout("10 seconds"), ··· 111 113 const retrieve = ({ 112 114 spotifyAccount, 113 115 ctx, 116 + user, 114 117 }: { 115 118 spotifyAccount: SelectSpotifyAccount; 116 119 user: SelectUser; 117 120 ctx: Context; 118 121 params: QueryParams; 119 122 did?: string; 120 - }): Effect.Effect<{}, Error> => { 123 + }): Effect.Effect<any, Error> => { 121 124 return Effect.tryPromise({ 122 125 try: async () => 123 - ctx.redis.get(`${spotifyAccount.email}:current`).then((cached) => { 124 - if (cached) { 125 - return JSON.parse(cached); 126 - } 127 - return {}; 128 - }), 126 + ctx.redis 127 + .get(`${spotifyAccount.email}:current`) 128 + .then((cached) => 129 + Match.value(cached).pipe( 130 + Match.when(null, () => ({})), 131 + Match.when(undefined, () => ({})), 132 + Match.orElse(() => JSON.parse(cached)) 133 + ) 134 + ) 135 + .then((cached) => [cached, ctx, user]), 129 136 catch: (error) => 130 137 new Error(`Failed to retrieve currently playing: ${error}`), 131 138 }); 132 139 }; 133 140 134 - const presentation = (currentlyPlaying): Effect.Effect<any, never> => { 135 - return Effect.sync(() => currentlyPlaying); 141 + const withUriAndLikes = ([track, ctx, user]: [any, Context, SelectUser]) => { 142 + return Effect.tryPromise({ 143 + try: async () => { 144 + const sha256 = createHash("sha256") 145 + .update( 146 + `${track.item.name} - ${track.item.artists.map((x) => x.name).join(", ")} - ${track.item.album.name}`.toLowerCase() 147 + ) 148 + .digest("hex"); 149 + const [record] = await ctx.db 150 + .select() 151 + .from(tables.tracks) 152 + .where(eq(tables.tracks.sha256, sha256)) 153 + .execute(); 154 + return ctx.db 155 + .select() 156 + .from(tables.lovedTracks) 157 + .leftJoin( 158 + tables.tracks, 159 + eq(tables.lovedTracks.trackId, tables.tracks.id) 160 + ) 161 + .leftJoin(tables.users, eq(tables.lovedTracks.userId, tables.users.id)) 162 + .where( 163 + and(eq(tables.tracks.sha256, sha256), eq(tables.users.did, user.did)) 164 + ) 165 + .execute() 166 + .then((results) => 167 + Match.value(track).pipe( 168 + Match.when({}, () => ({})), 169 + Match.orElse(() => ({ 170 + ...track, 171 + songUri: record?.uri, 172 + artistUri: record?.artistUri, 173 + albumUri: record?.albumUri, 174 + liked: results.length > 0, 175 + })) 176 + ) 177 + ); 178 + }, 179 + catch: (error) => new Error(`Failed to retrieve URI and likes: ${error}`), 180 + }); 181 + }; 182 + 183 + const presentation = (track): Effect.Effect<any, never> => { 184 + return Effect.sync(() => track); 136 185 };