Commit Graph
27 Commits
Author SHA1 Message Date
HoneyandClaude Opus 4.8 b788fedec3 fix(download): dedup missed re-downloads when the title contains ' - ' (KH triple/quadruple)
findExistingTrack split the filename at the first ' - ' assuming 'Artist - Title',
so a title with internal dashes ('Kingdom Hearts 2 - Dearly Beloved - Main Theme')
or a garbage/absent artist prefix ('01-01 - …') was mis-parsed → the duplicate went
undetected and got re-downloaded (3-4x). Now match the query title against BOTH the
full stem AND the part after the first ' - ', so titles with dashes and prefix-only
filenames are recognized. Normal 'Artist - Title' files still match; no false
positives; remix-vs-base distinction preserved. Unit-tested on the exact KH case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-12 17:06:14 +02:00
HoneyandClaude Opus 4.8 eb55184118 fix(download): remix/live/acoustic/instrumental no longer skipped as duplicate of base track
findExistingTrack normalized titles by stripping ALL bracketed content, so
"Therapy (Remix)" collapsed to "Therapy" and was skipped because the base
track existed. Keep brackets that contain a version marker (remix/live/acoustic/
instrumental/vip/flip/edit/mix/…) so distinct recordings are recognized as new;
still strip noise brackets (feat. X, Official Video). Verified via Deezer that the
remix track carries the marker in its title. Unit-tested both directions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-07 17:06:00 +02:00
HoneyandClaude Opus 4.8 1e69831dd8 feat: move the whole metadata/search stack from iTunes to Deezer (1.3)
Apple's iTunes /search returns 403 from server/datacenter IPs, which had broken global
search, charts, album/artist/single downloads and the metadata-editor autofill in
production. Everything now sources metadata from Deezer (free, no key, more requests,
returns explicit versions + real artist images + durations). Web + Android share one
source, so an artist resolves to one consistent name everywhere. Deezer results are
mapped to the legacy "ItunesItem" shape, so the UI + download flow are unchanged.

Backend:
- New backend/lib/deezer.js: lookupTrackMeta, fetchAlbumTracks, searchAlbumTracks,
  fetchArtistAlbums (+ pure pickTrackMatch/mapAlbumTrack for tests).
- metadata.js drops the iTunes lookups; novasonic-server download paths (track, album,
  artist, playlist enrich, spotify artwork) + autofill now use Deezer.
- Artist download uses Deezer discography (real albums only), not the iTunes album search.
- Editor autofill endpoint /api/itunes/lookup -> /api/meta/lookup; removed the dead
  iTunes /search proxy and the Apple charts RSS proxy.
- backend/test/deezer.test.js: unit tests for numbering, mixed-artist album-artist
  stability, explicit flags, the Japanese-title match guard (npm test; RUN_LIVE=1 for live).

Web:
- api.js search/lookups already on Deezer; charts now use Deezer /chart. Artist cards
  show the real Deezer photo. Visible "iTunes"/"Apple Music" labels -> "Deezer"
  (search badge, charts badge, preview bar, editor button, artist heading). SW ns-v51.

Android:
- New DeezerModels.kt with Dz* models + mappers to ItunesItem/ItunesRssEntry.
- ItunesClient is Deezer-backed (search = tracks+albums+artists in parallel; album/artist
  lookups; /chart charts), same method signatures. Visible strings -> Deezer.

Docs: README, CLAUDE.md and CHANGELOG (1.3) updated; Platform Parity Matrix reflects the
shared Deezer source + Android non-local artist page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-31 12:48:33 +02:00
HoneyandClaude Opus 4.8 9c8b4775ea fix: use the US iTunes store everywhere for consistent artist naming
Search (web api.js) was JP-first, the download lookups were JP-first, and the Android
client was already US — so the same artist resolved inconsistently: sometimes kanji,
sometimes katakana, sometimes romaji, depending on which path and which store matched.

Switch everything to the US store:
- api.js itunesSearch → country=us (one fetch, matches the Android client)
- metadata.js lookupItunesTrackMeta → US only (drops the JP fetch: consistent names +
  halves the download's iTunes calls, which also eases the rate limit)
- searchItunesAlbumTracks → country=us
- remove the JP-katakana guards in the album/artist download paths (redundant now, and
  they wrongly romanized genuine Japanese artists like 羽岡佳 → "Kei Haneoka")

Titles still come from the source, so a Japanese title the user picked stays Japanese;
only iTunes-derived fields (album_artist, genre, year, artwork) follow the US store.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 10:37:07 +02:00
HoneyandClaude Opus 4.8 72b612f5ff fix(backend): don't tag Western artists with the JP store's katakana album-artist
The iTunes lookup is JP-first (for correct Japanese titles/artwork), but the Japanese
store returns katakana-localized names for Western artists — so DJ Snake's single got
album_artist / folder "DJスネイク • アルーナジョージ" while the title/artist stayed English.

- lookupItunesTrackMeta: take the NAMES (album, album-artist, track, artist) from the US
  store when the search isn't Japanese; keep JP names for actual Japanese songs.
- album & artist download paths: if iTunes' collectionArtistName is Japanese but the
  request artist isn't, keep the request's English name for the tag and folder.

Already-downloaded files aren't rewritten — re-download or fix via the editor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 22:54:15 +02:00
HoneyandClaude Opus 4.8 1775ec5b9b fix(search): trim leading aesthetic punctuation in artist lookups
Artists like ".diedlonely" (leading dot, †, ✝, _ …) weren't surfaced by iTunes,
so the download's metadata lookup fell through to a titleOnly match — grabbing a
same-titled track by another artist (e.g. the "In the Bleak Midwinter" Christmas
carol: Holiday genre, 176s single) and steering the YouTube search to the wrong
(slowed) version.

- backend/metadata.js: strip leading non-alphanumeric from the search artist and
  the first-word comparison so the real artist matches (and the right album/genre/
  duration are used).
- android ExploreViewModel: strip leading symbols from the search term so the
  Explore tab finds these artists.

Keeps the original name for display/storage — only the search/compare is trimmed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:34:44 +02:00
HoneyandClaude Opus 4.8 445a7bf583 fix(backend): consistent album for no-album downloads + tolerant editor resolve
Two-part fix for "[Unknown Album]" vs "Singles" mismatch that broke the
metadata editor (File not found):

- Download: always write an album tag, defaulting to "Singles" so it matches
  the on-disk folder (album || 'Singles'). No-album tracks no longer show as
  "[Unknown Album]" while living in a "Singles" folder. (Future downloads.)
- Editor resolve: Subsonic getSong returns a *virtual* tag-built path (e.g.
  "[Unknown Album]/Title.mp3" with raw "|"), not the physical file ("Singles/
  Artist - Title_YTPMV.mp3"). Add findExistingTrack as a final fallback, and
  make its normalizer map the sanitized chars (/ \ : * ? " < > | and _) to a
  space so a title tag matches the sanitized filename. (Fixes existing tracks.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 20:33:20 +02:00
HoneyandClaude Sonnet 4.6 fa4c954ab9 feat(0.9): romaji sort-tags only, robust dupe detection, Mittelweg no-duration, autofill all fields
- Japanese tracks stored with original names in folder/filename/tags; romaji only in sort tags
- findExistingTrack: case-insensitive, searches all album folders under artist, strips release suffix
- --max-filesize 30M + 2x duration cap on all yt-dlp calls
- iTunes collectionArtistName captured as album_artist; fixes compilation album splitting
- album_artist tag uses mainArtist (not rawArtist) so all tracks in one album are consistent
- No-duration cascade: steps 5-8 (title-only/no-filter) removed — fail cleanly like with-duration
- Autofill iTunes: fills title, artist, albumArtist, discNum (was album/genre/year/track/total only)
- Autofill MusicBrainz: fills albumArtist, discNum (was missing both)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 13:26:11 +02:00
HoneyandClaude Sonnet 4.6 63ff8920bf fix: repair Japanese song download pipeline
Five separate root causes fixed:

1. buildTitleKeywords (ytdlp.js): For Japanese titles \w strips all
   kana/kanji leaving no keywords. Now uses the first whitespace-
   delimited segment as a literal regex keyword so --match-filter
   title ~= requires the YTM video to contain the Japanese text.

2. Tokenizer wait (ytdlp.js): If kuromoji hasn't finished loading when
   a Japanese download starts, romajiForStorage returns the original
   Japanese text unchanged — producing Japanese folder/filenames and
   skipping sort tags. Now waits up to 30 s for the tokenizer.

3. titleOnlyMatch guard (metadata.js): iTunes titleOnlyMatch (no artist
   check) was used as a fallback for all searches. For Japanese
   titles/artists this matched wrong US songs with identical titles
   (common in anime OSTs). Now titleOnlyMatch is skipped when the
   search title or artist contains Japanese characters.

4. squid.js rawArtist: rawArtist was not in squid's signature and
   never forwarded to embedMetadata, so Qobuz-downloaded J-Pop tracks
   stored the iTunes-resolved English name as artist instead of the
   Japanese original. Fixed by accepting and forwarding rawArtist.

5. squid.js romaji paths: Folder and filename used raw artist/title
   with no romaji conversion, creating Japanese-named paths
   inconsistent with the yt-dlp path. Now uses romajiForStorage same
   as ytdlp.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 01:40:46 +02:00
HoneyandClaude Sonnet 4.6 78024181f0 fix: always write English genre for all download paths
1. JP genre mapping table in metadata.js — when only JP iTunes has the song
   (niche Japanese artists), map localized JP genre names (アニメ, ポップ, etc.)
   to English equivalents so files never get Japanese characters in genre tag.

2. Spotify playlist import (alreadyEnriched path) — was skipping iTunes lookup
   entirely, leaving genre empty. Now does a lightweight genre-only lookup.

3. Single track download with Spotify album — was skipping iTunes when album
   already known from Spotify. Now also fetches genre from iTunes when missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 22:58:08 +02:00
HoneyandClaude Sonnet 4.6 760d8d5e1f fix: use US iTunes genre to avoid Japanese-localized genre names
JP store returns localized genres (e.g. イージーリスニング instead of Easy Listening).
Fetch JP and US in parallel, use JP match for title/album/artwork but always
take genre from US result so genre is always in English/Western characters.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 19:14:43 +02:00
HoneyandClaude Sonnet 4.6 67f4be272a fix: ie-totaltracks element id + JP-strict/US-fallback iTunes matching
Prevent false Japanese matches by never using JP title-only fallback.
Strategy: JP strict → US strict → US title-only so niche Western tracks
still resolve but same-English-title Japanese songs don't pollute results.

Also fix JS error in autofillItunes: ie-total → ie-totaltracks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 18:15:04 +02:00
Honey 09cec18b33 fix: strip punctuation in normalizeTitle so comma variants match (e.g. 'Mr. Lawrence' vs 'Mr, Lawrence') 2026-05-27 17:49:31 +02:00
Honey 9821334cc8 fix: split multi-artist string before iTunes search query in lookupItunesTrackMeta 2026-05-27 17:40:14 +02:00
Honey 3837783225 fix: iTunes JP-first with US fallback; romaji filter in playlist/starred search 2026-05-27 17:16:57 +02:00
Honey a9463917e3 fix: use iTunes country=jp globally — returns Japanese original names, identical for non-Japanese music 2026-05-27 15:21:11 +02:00
HoneyandClaude Sonnet 4.6 c7bebd5691 fix: correct artist names and prevent wrong song downloads for Japanese tracks
- spotify.js: stop overwriting trackName with iTunes English title —
  yt-dlp must search with original Japanese title to find the right song
  (was downloading English covers e.g. Ghost In A Flower instead of 幽霊花)
- metadata.js: add title-only fallback in lookupItunesTrackMeta so tracks
  are found even when kuromoji gives wrong artist romanization (e.g.
  Terui Jun Sei vs iTunes Yoshimasa Terui); also return itunesArtistName
- novasonic-server.js: use itunesArtistName from iTunes lookup as the
  stored artist when available (single-track and playlist download paths)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 00:39:26 +02:00
HoneyandClaude Sonnet 4.6 f03c6dfabd fix: remove space and / as artist separators, keep &
Separators: • · ; , & — space and / removed to avoid breaking multi-word artist names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 12:31:52 +02:00
HoneyandClaude Sonnet 4.6 e5bb2bf2d3 feat: accept space, &, / as artist separators in metadata editor
- Space as fallback separator on save when no explicit separator present
  (Artist1 Artist2 → Artist1;Artist2)
- & and / added to explicit separator set everywhere
- navidromeArtist (download path) also handles /

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 12:30:18 +02:00
HoneyandClaude Sonnet 4.6 4842c89c4e feat: artist separator normalization + separator hints in metadata editor
- Normalize •, ·, ;, , separators on save (both edit and download paths)
- Fix ndSong scope bug (was inaccessible outside try block in edit-metadata)
- Apply _normalizeArtists to artist/albumArtist before ffmpeg tags and file rename
- Storage format: Artist A;Artist B (no space, Navidrome/MusicBrainz style)
- Display format: Artist A; Artist B (space, via _fmtArtists in frontend)
- Show separator hint below label in metadata editor fields
- Remove hover effect on player bar song title

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 22:43:51 +02:00
HoneyandClaude Sonnet 4.6 7ba5b8ce9a feat: artist in filename, hide album=title subtitle, remove title hover
Naming convention (download + edit-metadata):
  Before: {01 - }Title.ext
  After:  {01 - }Artist1, Artist2 - Title.ext
- ytdlp.js: builds fileBase with artist prefix; findExistingTrack checks
  both new and old format so existing files aren't re-downloaded
- metadata.js: findExistingTrack accepts optional safeArtist param
- novasonic-server.js: file rename on title edit now includes artist
  (falls back to Navidrome's stored artist if not provided in body)

Song list subtitle: hide album name when album === title (singles where
the album is named after the track appear to duplicate the title)

Player bar: removed .player-title:hover underline/colour effect

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 22:33:35 +02:00
HoneyandClaude Sonnet 4.6 17414ca888 fix: iTunes artist match and MusicBrainz first-artist-only query
iTunes: split artist by [\s,;/] not just space so "VXLLAIN, iGRES"
yields first word "vxllain" (no trailing comma) and matches correctly.

MusicBrainz: extract first artist from the full credit string (split on
,;&•) and use `artistname` field — MB stores credits like "VXLLAIN feat.
iGRES & ENXK" which never matched the comma-joined full string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 22:00:58 +02:00
HoneyandClaude Sonnet 4.6 e05daf3824 remove: lyrics downloading (fetchLyrics, .lrc files, uslt tag embedding)
Removes LRCLIB lookup, .lrc sidecar file writing, and lyrics/uslt
metadata embedding from all downloads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 21:34:59 +02:00
HoneyandClaude Sonnet 4.6 3a8b879abf feat: use ; as artist separator in downloads, add & handling
navidromeArtist() now writes ; instead of / so Navidrome splits
correctly with the updated Tags.Artist.Split config.
Also handles & (e.g. "Artist A & Artist B" → "Artist A;Artist B").

Add fix-artist-separators.sh — one-time Debian script to update
existing library files from " / " to ";" in artist tags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 14:26:19 +02:00
Honey 757c7045ba fix: normalize title comparison in iTunes lookup to handle Spotify dash-variants vs iTunes paren-variants 2026-05-14 18:53:06 +02:00
HoneyandClaude Sonnet 4.6 0f1e6ff531 refactor: remove dead code from backend and lib
- Remove unused spotifyClientId/spotifyClientSecret from config
- Remove AUDIO_EXTS from metadata exports (internal-only)
- Remove tryYtdlp wrapper in ytdlp.js (direct spawnYtdlp calls)
- Remove _spArtistName wrapper in server (direct _spAllArtists calls)
- Fix stale comment: primary resolver is SpotipyFree, not curl_cffi

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 18:18:50 +02:00
HoneyandClaude Sonnet 4.6 ac8d26bf9d feat: split backend into lib/ modules + add squid.wtf (Qobuz) download backend
- backend/lib/config.js  — CONFIG from env (adds DOWNLOAD_BACKEND, SQUID_QUALITY)
- backend/lib/jobs.js    — jobs Map, updateJob(), log()
- backend/lib/metadata.js — iTunes, artwork, lyrics, sanitize helpers
- backend/lib/ytdlp.js   — yt-dlp download cascade + shared embedMetadata()
- backend/lib/squid.js   — ALTCHA PoW solver, Qobuz search/download via squid.wtf
- backend/lib/downloader.js — backend selector: ytdlp | squid | auto (squid-first + fallback)
- novasonic-server.js thinned to Express routes only, imports from lib/
- docker-compose: mount full backend/ dir, install deps outside read-only volume
- .env.example: document DOWNLOAD_BACKEND and SQUID_QUALITY

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 20:44:26 +02:00