Skip to content

[DRAFT] Beatport/Beatsource + ID3 + deps modernization (credit: @rosgr100, upstream PR #526)#4

Draft
TX-RX wants to merge 37 commits into
mainfrom
pr-526-beatport-modernization
Draft

[DRAFT] Beatport/Beatsource + ID3 + deps modernization (credit: @rosgr100, upstream PR #526)#4
TX-RX wants to merge 37 commits into
mainfrom
pr-526-beatport-modernization

Conversation

@TX-RX

@TX-RX TX-RX commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Pulls in @rosgr100's upstream PR Marekkon5#526 unchanged so the original commits (and attribution to @Simon-Zwa, @rosgr100, and @benjaminlewandowski / @jiji-lewandowski) are preserved.

Opened as draft because this is a superset of PR #1 (Simon-Zwa's clean v4-search fix) plus a very large amount of additional work that needs review before merge. Do not merge until we've decided which pieces we want.

What's included

Grouped by author:

@Simon-Zwa (3 commits, rebased): Same content as PR #1 — Beatport search moved to v4 catalog API.

@rosgr100 / @spirosg (~25 commits):

  • Beatsource: matching v4 catalog API switch and OAuth flow parity with Beatport.
  • ID3 hygiene: fixes duplicate TXXX frame accumulation ( + 'UNIQUEFILEID' + , + 'WWWAUDIOFILE' + ) on overwrite; standardizes YEAR to 4 digits and puts full YYYY-MM-DD into + 'RELEASETIME' + / + 'PUBLISHTIME' + .
  • Cover art cleanup on overwrite.
  • Beatport matching: mix-name taxonomy ( + 'MixType' + enum), Jaccard artist comparison, remix stopword filtering, deterministic confidence ceiling — all fenced behind a < 0.80 fallback so the primary matcher stays untouched.
  • Beatport search sanitization: strips + 'feat' + / + 'ft' + / + 'featuring' + in + 'clear_search_query' + (the v4 API returns 0 results when features are in the query).
  • OAuth token deadlock fix ( + 'drop(token)' + before recursive refresh).
  • Bounded retry loop in + 'update_token' + .
  • Beatsource: matching improvements + parenthesis stripping.
  • Dependency modernization: rodio 0.22 upgrade migrated the whole audio pipeline from i16 → f32 (aiff, alac, flac, mp3, mp4, ogg, wav), with a down-sample block in + 'shazam.rs' + to feed the SongRec fingerprinter its expected i16 stream.
  • Wry 0.55 upgrade — main window/navigation refactor to the new closure signatures.
  • + 'cargo-bundle' + -style cover-art-file guard (respects + 'config.album_art_file' + before writing loose + 'cover.jpg' + s).
  • Universal macOS binary in CI.
  • Node 22, upgraded GitHub Actions versions.

@benjaminlewandowski / @jiji-lewandowski (2 commits, merged in via SpirosG's PR #2):

  • Switch client to pnpm workspaces.
  • Removes JunoDownload entirely — source, asset, module registration, and UI mentions. Documented as intentional; JunoDownload's site had rendering changes that broke the scraper, and the reasoning is that no one on that fork was going to maintain it.

Concerns for review

  • The audio-pipeline i16 → f32 migration is a big refactor. It compiles, but it needs actual audio-playback smoke tests on all three platforms before we ship this to users.
  • JunoDownload removal is a permanent decision. It's a superset of platforms we care about, so this fork is fine losing it — but it's worth calling out.
  • The mix-taxonomy fallback matcher is heuristic. It looked reasonable on paper but hasn't been validated on our music library.
  • We already have PR Beatport: switch search to v4 catalog API (credit: @Simon-Zwa, upstream PR #523) #1 doing the clean v4-search fix. If we merge that first, most of the + 'beatport.rs' + changes here will merge cleanly on top; the audio-pipeline changes are unrelated.

Recommendation

Cherry-pick, don't wholesale merge. Suggested slices:

  1. The Beatport matching improvements ( + 'feat' + stripping, mix taxonomy, artist Jaccard).
  2. The ID3 duplicate-TXXX fix + date standardization.
  3. The OAuth deadlock fix + bounded retry.
  4. The cover-art-file guard.
  5. Beatsource parity (only if we care about Beatsource).
  6. Hold audio-pipeline modernization for a separate PR after smoke tests.

Simon-Zwa and others added 30 commits May 18, 2026 21:25
Beatport no longer exposes __NEXT_DATA__ on search pages, causing search failures.

Updated Beatport search to use the v4 catalog API endpoint instead of scraping the website.

Adjusted deserialization to support the new API response format (tracks array).
Added a note about a temporary Beatport fix for May 2026.
Updated user agent string and fixed a typo in the search query parameter. Changed the token fetching method to use OAuth API and updated the token expiration logic.
Updated GitHub Actions to use the latest versions of actions for checkout, cache, setup-node, and pnpm.
… and update platform statics

Summary of Dependency Modernization (Non-API Changes)

    Audio Engine Upgrades (rodio 0.22): Migrated the entire playback module (onetagger-player) from i16 integers to f32 floats. Updated all local decoders (aiff, alac, flac, mp3, mp4, ogg, wav) to yield floating-point data streams, and wrapped sample rates/channels in strict NonZeroU32/NonZeroU16 structural safety types.

    Shazam Fingerprinting Fixes (onetagger-autotag): Adapted shazam.rs to map floating-point streams from UniformSourceIterator. Implemented a down-sampling translation block to scale and convert the raw float sample buffer back to standard i16 PCM vectors to satisfy the underlying SongRec signature generator requirements.

    Window & UI Layer Adjustments (wry 0.55): Refactored the window lifecycle application loop in main.rs. Split the legacy webview navigation handler into separate modern asynchronous functions, updated the closures to support dual-argument signatures (handling url and NewWindowFeatures), and migrated target window states to use the modern NewWindowResponse::Allow and Deny enums.

    Crate Configuration Updates (Cargo.toml): Swapped deprecated reqwest flags from "rustls-tls" to "rustls". Restored explicit "query" and "form" dependency compilation flags inside the platforms module to handle isolated network request actions. Replaced the legacy audio "cpal" identifier with the modernized "playback" flag to maintain access to local host speakers.

Wrapped the cover art file-writing loop in an explicit `config.album_art_file` check to prevent loose 'cover.jpg' files from being saved when the feature is disabled in settings.
id3.rs: Resolved the duplicate TXXX frame bug (e.g., UNIQUEFILEID, WWWAUDIOFILE) by explicitly clearing existing extended text frames before writing new ones.

lib.rs (tag) & lib.rs (autotagger) : Fixed ID3 date mapping to ensure the standard YEAR frame strictly outputs a 4-digit year (YYYY).

lib.rs (tag) & lib.rs (autotagger): Added logic to automatically inject the full YYYY-MM-DD date strings into custom RELEASETIME and PUBLISHTIME tags.
beatsource.rs: Added clear_search_query helper to strip parentheses from search strings before hitting the v4/catalog/search endpoint.

This mirrors the recent Beatport fix and prevents 400/403 API crashes when scraping tracks with bracketed metadata (e.g., mix names or featured artists) in the title
Removed installation of nodejs and pnpm from dependencies. Added separate steps for installing NodeJS and pnpm.
Updated macOS build job to create a universal binary for both Intel and Apple Silicon architectures. Adjusted steps for caching, installation, and artifact uploads.
**The Problem:**
Perfectly matched tracks were receiving ~56% accuracy scores because OneTagger's standard fuzzy matching compared long local titles like `Title (Extended Mix)` against Beatport's shorter base `Title` field.

**The Solution:**
* **Smart Fallback:** Added a secondary matching pass that only triggers if the initial score is < 80%.
* **Regex Extraction:** Safely splits the local title and mix name for independent grading.
* **Weighted Scoring:** Calculates a new accuracy score heavily weighted toward the base title (70%) but rewarding accurate mix names (30%).
* **False Positive Prevention:** Added a strict boolean check to immediately reject API tracks if their mix name directly contradicts the local file's mix name.
* **Deps:** Added `strsim` to `onetagger-platforms`.
**The Problem:**
Perfectly matched tracks were receiving ~56% accuracy scores because OneTagger's standard fuzzy matching compared long local titles like `Title (Extended Mix)` against Beatport's shorter base `Title` field.

**The Solution:**
* **Smart Fallback:** Added a secondary matching pass that only triggers if the initial score is < 80%.
* **Regex Extraction:** Safely splits the local title and mix name for independent grading.
* **Weighted Scoring:** Calculates a new accuracy score heavily weighted toward the base title (70%) but rewarding accurate mix names (30%).
* **False Positive Prevention:** Added a strict boolean check to immediately reject API tracks if their mix name directly contradicts the local file's mix name.
* **Deps:** Added `strsim` to `onetagger-platforms`.
…y and remix matching

This PR introduces a highly optimized, secondary fallback matching engine exclusively within beatport.rs. It is designed to resolve widespread false negatives caused by Beatport's inconsistent metadata formatting (e.g., artist reordering, alias variations, and arbitrary (Extended Mix) suffixes) without altering the primary MatchingUtils core logic.  The fallback only triggers if the primary matcher returns a confidence score below 0.80, acting as a localized rescue mission for difficult DJ metadata.

Key Features & Improvements

Categorical Version Taxonomy (MixType): Replaces brittle string equality with a strict enum matrix. This guarantees that functionally different DJ mixes (e.g., Club Mix vs Extended Mix) are strictly isolated and cannot falsely match, while safely bridging tracks missing explicit tags (Unknown ↔ Original).  Jaccard (Token-Based) Similarity: Transitions from Levenshtein distance to Jaccard set intersection for Artist arrays and Remix titles. This completely resolves the mathematical penalties previously caused by word reordering (e.g., "Guetta Remix" vs "Remix Guetta") and punctuation differences ("&" vs "and").  Remix Stopword Filtering: Violently strips noise words (remix, rmx, mix, edit, vip, dub, etc.) prior to Jaccard comparison. This prevents false positives where two completely different remixers achieve a high similarity score simply because both strings contain the word "remix".  Deterministic Confidence Ceiling: Enforces a strict scoring hierarchy where fuzzy Jaccard artist matches are mathematically capped (0.9) to ensure they can never outrank a verified exact match (1.0).

Performance Optimizations

O(N²) Prevention: Eliminates nested vector iteration by utilizing an internal HashMap lookup during fallback score updates.  Regex Caching: Migrates the primary Mix Regex to a static OnceLock so it compiles exactly once per application lifetime.  Closure Hoisting: Moves allocation-heavy normalizer closures (normalize_punctuation, normalize_artists) outside the main iteration loop.

Architectural Safety

Zero Core Impact: These changes are 100% fenced behind the < 0.80 fallback gate and contained entirely inside beatport.rs. The primary engine remains completely untouched, ensuring stability across other platform matchers.
…ag pagination

The Beatport module struggled to match tracks containing featured artists in the local title (e.g., "Forever Ft. Sabrina Johnston").

 Beatport's search API frequently chokes and returns 0 results when featured artists are included in the search string.

Even when found, OneTagger's strict accuracy thresholds would fail the match because Beatport's base title didn't contain the featured artist.

Furthermore, Beatport's Auto Tag fall back search was failing on valid tracks because the engine exited pagination prematurely (only checking Page 1) and didn't sort the fallback array by accuracy.

The Solution
This PR completely overhauls the Beatport matching logic and search sanitization to be bulletproof out-of-the-box.

Key Changes:

Hardcoded Search Sanitization: Baked a feature-stripping Regex ((?i)\s+(?:ft|feat|featuring)\.?\s+[^()]+) directly into clear_search_query. The Beatport API now always receives a clean base title, preventing 0-result API crashes.

 Universal Math Fallback: Added a smart fallback engine that dynamically strips features from both the local title and the Beatport API title during the Levenshtein calculation. This guarantees a 1.0 accuracy score for valid tracks even if the user has the OneTagger title cleanup regex empty.

Autotag Pagination Fix: Modified the search loop to respect the user's max_pages config during Auto Tag runs, rather than prematurely returning after Page 1 if a match wasn't instantly found.

Array Sorting: Forced matched_tracks to sort by accuracy descending so Auto Tag's automated selection reliably grabs the 100% match instead of a scrambled lower-tier match.
…ashes

Two edge-case bugs were causing the Beatport module to freeze or aggressively fail out of track matching:

Token Deadlock: If the Beatport API token expired mid-session, the update_token function attempted a recursive call while still holding the Mutex guard, causing the application thread to permanently freeze.

ISRC Hard Crash: The ISRC matching path used the ? operator on the track detail API fetch. If the API rate-limited or dropped the connection on that specific call, it hard-failed the entire matching process instead of safely falling back to the text-search engine.

The Solution

Mutex Deadlock Fix: Explicitly added drop(token); to release the Mutex guard before the recursive update_token call, ensuring thread safety upon token expiry.

Graceful ISRC Fallback: Replaced the ? operator in the ISRC search block with a match statement. If a track detail fetch fails, it now logs a warning and gracefully falls through to the standard text search, rather than crashing the tagger.
Implemented a bounded retry loop in the update_token method to handle expired tokens more effectively by preventing infinite recursion. Added error handling for cases where the Beatport API continuously provides expired tokens.
jiji lewandowski and others added 7 commits June 16, 2026 22:33
Add pnpm-workspace.yaml and remove the npm lockfile while updating client configuration and source files. Updates include package.json and tsconfig.json changes plus code adjustments in src/main.ts and src/views/TagEditor.vue to align with the workspace/dependency setup and recent refactors.
Remove the JunoDownload platform (source, asset and module) and related references across the codebase. Update onetagger-autotag to stop registering JunoDownload and remove the junodownload module from onetagger-platforms. UI changes in AutotaggerPlatforms.vue: switch to a localPlatforms reactive array, add sorting, a watch to keep UI/config in sync, protect against re-sorts during drag operations, update draggable props and ensure settings are saved when order or enabled state changes. Minor UX/content tweaks: remove Juno mentions from README and HelpButton tooltip and from AutotaggerStatus display mapping. Misc: adjust rodio import in onetagger-player (remove unused Player) and make webview drag-drop handling ignore empty drops (fixes internal drag operations such as vuedraggable).
updates for client + some smaller rust fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants