From b2ce0b3edceef82396091f88e20bc3efd9f5e141 Mon Sep 17 00:00:00 2001 From: Tsiry Sandratraina Date: Sat, 10 Jan 2026 23:45:50 +0300 Subject: [PATCH] Add song matching with DB joins and API fallback Add RockskyClient.matchSong to query remote API as fallback. Extend track lookup with album/artist left joins and include genres, artist picture, release date and year in the match result. Log the resolved match instead of the raw DB row. Update CLI description/help styling (ASCII banner and minor command text tweaks) --- apps/cli/src/client.ts | 19 +++++++++++++++++++ apps/cli/src/index.ts | 17 +++++++++++------ apps/cli/src/lib/matchTrack.ts | 31 ++++++++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/apps/cli/src/client.ts b/apps/cli/src/client.ts index 4d53276f..f18717ff 100644 --- a/apps/cli/src/client.ts +++ b/apps/cli/src/client.ts @@ -321,4 +321,23 @@ export class RockskyClient { return response.json(); } + + async matchSong(title: string, artist: string) { + const q = new URLSearchParams({ + title, + artist, + }); + console.log(q); + const response = await fetch( + `${ROCKSKY_API_URL}/xrpc/app.rocksky.song.matchSong?${q.toString()}`, + ); + + if (!response.ok) { + throw new Error( + `Failed to match song: ${response.statusText} ${await response.text()}`, + ); + } + + return response.json(); + } } diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 89f49b3c..2b2ff304 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -22,16 +22,21 @@ const program = new Command(); program .name("rocksky") .description( - `Command-line interface for Rocksky (${chalk.underline( - "https://rocksky.app", - )}) – scrobble tracks, view stats, and manage your listening history.`, + ` + ___ __ __ _______ ____ + / _ \\___ ____/ /__ ___ / /____ __ / ___/ / / _/ + / , _/ _ \\/ __/ '_/(_- chalk.bold.cyan(str), styleCommandText: (str) => chalk.yellow(str), - styleCommandDescription: (str) => chalk.gray(str), styleDescriptionText: (str) => chalk.white(str), styleOptionText: (str) => chalk.green(str), styleArgumentText: (str) => chalk.magenta(str), @@ -134,12 +139,12 @@ program program .command("mcp") - .description("Starts an MCP server to use with Claude or other LLMs.") + .description("starts an MCP server to use with Claude or other LLMs.") .action(mcp); program .command("sync") - .description("Sync your local Rocksky data from AT Protocol.") + .description("sync your local Rocksky data from AT Protocol.") .action(sync); program.parse(process.argv); diff --git a/apps/cli/src/lib/matchTrack.ts b/apps/cli/src/lib/matchTrack.ts index 3530264d..ebd7aed7 100644 --- a/apps/cli/src/lib/matchTrack.ts +++ b/apps/cli/src/lib/matchTrack.ts @@ -1,3 +1,4 @@ +import { RockskyClient } from "client"; import { ctx } from "context"; import { eq, and, or } from "drizzle-orm"; import { logger } from "logger"; @@ -7,6 +8,19 @@ export async function matchTrack(track: string, artist: string) { const [result] = await ctx.db .select() .from(schema.tracks) + .leftJoin( + schema.albumTracks, + eq(schema.albumTracks.trackId, schema.tracks.id), + ) + .leftJoin(schema.albums, eq(schema.albumTracks.albumId, schema.albums.id)) + .leftJoin( + schema.artistAlbums, + eq(schema.artistAlbums.albumId, schema.albums.id), + ) + .leftJoin( + schema.artists, + eq(schema.artistAlbums.artistId, schema.artists.id), + ) .where( or( and(eq(schema.tracks.title, track), eq(schema.tracks.artist, artist)), @@ -17,6 +31,21 @@ export async function matchTrack(track: string, artist: string) { ), ) .execute(); + + let match = null; + + if (result) { + match = { + ...result.tracks, + genres: result.artists?.genres, + artistPicture: result.artists?.picture, + releaseDate: result.albums?.releaseDate, + year: result.albums?.year, + }; + } else { + const client = new RockskyClient(); + match = await client.matchSong(track, artist); + } logger.info`>> matchTrack ${track}, ${artist}`; - logger.info`>> matchTrack result \n ${result}`; + logger.info`${match}`; } -- 2.51.2