commits
Create idx_title_artist_album_albumartist on tracks (title, artist,
album, album_artist) and update the Drizzle schema, migration snapshot,
and journal
Replace paginated listRecords calls with getRepo and parse CAR blocks
using @ipld/car and @ipld/dag-cbor. Add related dependencies (and
multiformats) and update bun.lock (apps/cli version 0.2.0 → 0.3.0).
Call useFollowsQuery for neighbour DIDs (excluding the current DID) and
merge returned follow DIDs into the local follows set using useEffect
Remove tracks array from playlist schema and update types, PKL, and
lexicon files accordingly
Implement /1/submit-listens with Token auth, validate body via zod, and
asynchronously match tracks then publish scrobbles for 'single' listens.
Add /1/validate-token and several stub ListenBrainz-compatible routes.
Add Listenbrainz zod schemas and TypeScript types. Use handle from
getDidAndHandle for token validation.
Capitalize "DID" in the handle error and remove URL underline styling
Exit with a clear error when neither ROCKSKY_HANDLE/ROCKSKY_IDENTIFIER
nor ROCKSKY_PASSWORD is set, showing guidance and a link to create app
passwords. Use default empty strings for these env vars so envalid won't
throw. Also adjust CLI help banner/link formatting and add the hono
dependency
After putScrobbleRecord, poll the database for the scrobble URI. Retry
up to 40 times with a 600ms delay between attempts and log an error if
the record is not detected.
Publish the newest scrobble ID after saving so subscribers can react.
Refactor JS subscriber to call updateUris(ctx, did) and
refreshScrobbles, use the tables namespace for DB queries, add
hashing/logging, and limit syncs to 5 recent scrobbles.
Rename query data to rawNowPlayings and deduplicate by a composite key
(trackId-did-createdAt). Remove the structuralSharing override and use
the composite value as the React key for StoryContainer to ensure
stable, unique list items.
Use _.uniqBy to filter duplicate entries, add lodash import, and update
bun.lock accordingly
When ROCKSKY_IDENTIFIER and ROCKSKY_PASSWORD are present, resolve DID
and handle via the agent, create the user in the local DB if missing,
and print the profile URL. Otherwise fall back to the existing token
file flow.
Return null from matchTrack if matched object lacks title or artist and
log an error. Have scrobble command exit with status 1 when no match is
found.
Add shouldSync(agent) to check remote scrobble records and the local DB;
call sync() if the latest remote CID is not present locally before
publishing a scrobble. Import sync from cmd/sync. Also update the
matchTrack logger to include the matched title and artist.
Make subscribeToJetstream return Promise<void> and await it where used
(sync and scrobble). Move client event handlers inside the Promise and
resolve on "open". Return early Promise.resolve() if lock file exists.
Call client.connect() from within the Promise.
Remove the JetStream lock file after scrobble dry-run and after
successful publish. Also explicitly exit with code 0 in scrobble
command.
Add initializeDatabase() to run Drizzle migrations at startup. Use
better-sqlite3 Database and run migrate() against detected migrations
folder (prod ./drizzle or dev ../../drizzle). Update drizzle.config to
output to ./drizzle and make build copy it into dist. Add db:generate,
db:migrate and db:studio npm scripts.
Pass dryRun through the scrobble action into publishScrobble and short-
circuit publishing to log-only when set
Add DB existence checks for tracks, artists, and albums before creating
records (match on title/artist/albumArtist as appropriate). Implement
putScrobbleRecord to construct, validate, and publish a scrobble record
(handles timestamps, optional fields, rkey). Replace direct console logs
with structured logger calls and improve error messages. Also import
'or' from drizzle-orm for compound queries.
Add modules to create and clean up per-user temp lock files on exit
(cleanUpSyncLockOnExit, cleanUpJetstreamLockOnExit).
subscribeToJetstream now checks/creates a JetStream lock file and sync
writes a sync lock and registers cleanup handlers. Export createUser and
subscribeToJetstream for reuse.
Wire createUser/subscribeToJetstream into scrobble flow and add helper
functions to put song, artist and album records (with validation) and a
stub for scrobble records. Update matchTrack types: releaseDate is now a
string, add mbArtists and MusicBrainzArtist type. Remove some unused
imports.
Return true/false from publishScrobble instead of exiting inside the
function. Have the CLI command exit when publishScrobble fails. Add
countScrobbles to warn when a user has no local scrobbles and tweak the
matchTrack log message.
Check for a temporary lock file in os.tmpdir() named rocksky-<did>.lock.
If present, log a colored error and exit(1) to avoid concurrent
publishing while the CLI is syncing.
Move getDidAndHandle into lib/getDidAndHandle and update sync to import
it. Add apps/cli/src/scrobble.ts with publishScrobble and
getRecentScrobble. Scrobble command now calls publishScrobble and skips
duplicates by checking recent scrobbles in the database.
Store sqlite KV under OS-specific data path using env-paths
(rocksky/rocksky-kv.sqlite) and ensure directory exists. Export kv from
context so the CLI uses persistent storage.
Replace the previous DB query in matchTrack with a kv-backed cache. On
cache hit return cached result and trigger an async background refresh;
on miss fetch from RockskyClient and save to kv. Remove now-unused
imports and schema usage.
Fetch MBIDs and artist data in matchSong via a new searchOnMusicBrainz,
add MusicBrainzArtist type, and attach mbId / mbArtists to the song
view. Also make CLI matchTrack return the matched track
(MatchTrackResult) so scrobble can consume the result.
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)
Implement /1/submit-listens with Token auth, validate body via zod, and
asynchronously match tracks then publish scrobbles for 'single' listens.
Add /1/validate-token and several stub ListenBrainz-compatible routes.
Add Listenbrainz zod schemas and TypeScript types. Use handle from
getDidAndHandle for token validation.
Make subscribeToJetstream return Promise<void> and await it where used
(sync and scrobble). Move client event handlers inside the Promise and
resolve on "open". Return early Promise.resolve() if lock file exists.
Call client.connect() from within the Promise.
Remove the JetStream lock file after scrobble dry-run and after
successful publish. Also explicitly exit with code 0 in scrobble
command.
Add initializeDatabase() to run Drizzle migrations at startup. Use
better-sqlite3 Database and run migrate() against detected migrations
folder (prod ./drizzle or dev ../../drizzle). Update drizzle.config to
output to ./drizzle and make build copy it into dist. Add db:generate,
db:migrate and db:studio npm scripts.
Add DB existence checks for tracks, artists, and albums before creating
records (match on title/artist/albumArtist as appropriate). Implement
putScrobbleRecord to construct, validate, and publish a scrobble record
(handles timestamps, optional fields, rkey). Replace direct console logs
with structured logger calls and improve error messages. Also import
'or' from drizzle-orm for compound queries.
Add modules to create and clean up per-user temp lock files on exit
(cleanUpSyncLockOnExit, cleanUpJetstreamLockOnExit).
subscribeToJetstream now checks/creates a JetStream lock file and sync
writes a sync lock and registers cleanup handlers. Export createUser and
subscribeToJetstream for reuse.
Wire createUser/subscribeToJetstream into scrobble flow and add helper
functions to put song, artist and album records (with validation) and a
stub for scrobble records. Update matchTrack types: releaseDate is now a
string, add mbArtists and MusicBrainzArtist type. Remove some unused
imports.
Store sqlite KV under OS-specific data path using env-paths
(rocksky/rocksky-kv.sqlite) and ensure directory exists. Export kv from
context so the CLI uses persistent storage.
Replace the previous DB query in matchTrack with a kv-backed cache. On
cache hit return cached result and trigger an async background refresh;
on miss fetch from RockskyClient and save to kv. Remove now-unused
imports and schema usage.
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)