Add Rekordbox Sync: compare SoundCloud likes and playlists to a collection - #28
Draft
cole-hackman wants to merge 2 commits into
Draft
cole-hackman wants to merge 2 commits into
cole-hackman wants to merge 2 commits into
Conversation
…ction Surfaces the gap between what a DJ saves on SoundCloud and what actually made it into rekordbox. Drop a collection XML export and get back the tracks you liked but never imported, ambiguous matches worth a look, per-playlist drift, and collection tracks nothing on SoundCloud accounted for. Every list exports to CSV. Runs entirely in the browser. The XML holds absolute paths to the user's own music files, so uploading it is neither necessary nor ours to do — and a 20k-track export is tens of MB, which the backend has no reason to carry. The only network calls are the existing likes and playlist endpoints. The two sides share no track identifier, so matching is inferred from text. rekordbox has clean ID3 artist/title fields; SoundCloud has one free-text title plus an uploader who is frequently a label or promo channel. Each SoundCloud track therefore yields several candidate readings and the best-scoring one wins. Normalization folds accents, reconciles "&" with "and", and strips promo decoration and catalogue numbers. Version text is compared as its own field so an original and a remix never match each other — reporting one as the other is worse than reporting a miss. Matching is narrowed by an inverted title-token index, since comparing 5k likes against a 20k collection outright would be 100M comparisons. Matches are graded, and the uncertain ones go to a review bucket rather than being silently decided either way. The parsing and matching logic lives in server/lib/ and is imported by the page across the directory boundary, so the Jest suite exercises the same code the browser runs instead of a mirrored copy. Adds 78 tests covering the messy title formats real libraries are full of. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3LQgRH2WGiHjEiBY3GoX5
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Two labeled corpora with explicit ground truth, so "how accurate is it" has an answer that can be re-checked rather than argued. The first covers the distortions the matcher was built for. The second is deliberately hostile: untagged rips where rekordbox falls back to a filename, multi-artist spellings, titles that differ only by a numeral, generic titles like "Intro", and a track renamed beyond recovery. Both currently pass in full, but the headline number is weak evidence and the file says so: the SoundCloud side is synthetic and written by the same hand as the matcher, so it measures the failure modes we anticipated and guards against regressing them. It cannot measure the ones we missed. The assertions are therefore weighted toward the failure that matters. Recall has a floor; false ownership has none permitted at all. A miss is visible and recoverable, a wrong "you already own this" is neither. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3LQgRH2WGiHjEiBY3GoX5
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Surfaces the gap between what a DJ saves on SoundCloud and what actually made it into rekordbox.
Drop a rekordbox collection XML export (
File → Export Collection in xml format), pick your likes and/or playlists, and get four reports:Every list exports to CSV. New page at
/rekordbox-sync, wired into the sidebar, dashboard, and recent-tools tracking.Runs entirely in the browser
No upload. The XML holds absolute paths to the user's own music files, so sending it to the server is neither necessary nor ours to do — and a 20k-track export runs to tens of MB the backend has no reason to carry. The only network calls are the existing
GET /api/likesandGET /api/playlists/:id.No new server routes, no schema changes, no new dependencies.
How matching works
The two sides share no track identifier, so every match is inferred from text — and the sides are shaped differently:
So each SoundCloud track yields several candidate readings (uploader-as-artist, artist-split-from-title, and the reverse) and the best-scoring one wins. Normalization folds accents, expands
&→and, strips promo decoration ([PREMIERE],FREE DOWNLOAD |, catalogue numbers like[WARP001]), and separates featured artists.Two decisions worth flagging for review:
Versions never collapse. Version text is extracted and compared as its own field, so an original and a remix can't match each other. A DJ who owns both has two records, and reporting either as the other is worse than reporting a miss.
Original Mixnormalizes to "no version" so it agrees with a bare title.Uncertainty is surfaced, not decided. Matches are graded
exact→strong→fuzzy→ none.exact/strongcount as owned;fuzzygoes to a review bucket. An exact title match with a disagreeing artist lands there rather than counting as a miss — otherwise a label upload sends you off to re-download a track you already have. Runtime disagreement over 20s penalizes a match, which is what separates a radio edit from an extended mix.Comparing everything against everything is O(n·m) — 5k likes against a 20k collection is 100M comparisons, too slow for a browser — so candidates are narrowed by shared title token via an inverted index that prefers rare tokens.
Where the code lives
Parsing and matching sit in
server/lib/and are imported by the page across the directory boundary. That's deliberate: the repo's existing pattern for testing frontend logic is to duplicate it into the test file (see the header oftests/export.test.js), which would be a bad trade for a matching engine whose correctness is the whole feature. This way Jest exercises the same code the browser runs. It resolves because Vercel builds from the repo root (vercel.json→npm run build), and it's verified by a realnext buildbelow.server/lib/rekordbox-xml.jsserver/lib/rekordbox-match.jsserver/lib/rekordbox-report.jsfrontend-UI/src/lib/rekordbox/index.tsTesting
78 new tests, including an end-to-end pass from raw XML text through to a finished report using the messy title formats real libraries are full of — promo prefix + artist-in-title + catalogue number,
&vsand, and accent folding (Björk – Jóga↔Bjork - Joga).npm test— 201 pass, 0 failures. One suite is marked failed:tests/soundcloud-client.test.js, which fails identically on a clean tree (verified by stashing) and is unrelated to this change.npx tsc --noEmit— cleannpx next lint— cleannpx next build— succeeds,/rekordbox-syncexports at 13.2 kBAlso covered: a 5,000-track collection matched against 300 queries to confirm the index avoids quadratic blowup.
Limitations
Artist, filename-as-title) match poorly, and a track uploaded to SoundCloud under a wholly different name won't match at all.Generated by Claude Code