Artist name from Spotify/source is stored directly in folder, filename,
and tags without being replaced by the iTunes artistName. iTunes is used
only for supplemental metadata (genre, year, cover, duration, trackNum,
albumArtist/collectionArtistName). Sort tags still receive romaji via
romajiForStorage() in ytdlp.js.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consistent with point 7: original (kanji) names in folder, filename,
and main tags — romaji only in ARTISTSORT/ALBUMARTISTSORT.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
When a track's duration is known but nothing matches the duration window, the
cascade no longer falls back to blind title-only/no-filter grabs (which pulled
same-titled videos by other uploaders, e.g. a "recognize" fan clip). Instead it
relaxes only the duration constraint while still REQUIRING the artist channel +
title. If nothing matches, the download produces no file and the job now fails
cleanly with "Nicht eindeutig gefunden — bitte Direktlink im Importer nutzen"
(previously it falsely reported success with no file).
Single, album and playlist paths all surface the not-found state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After the embedded-title change (recognize vs "recognize - Single"),
Navidrome's title-derived stem can drop the "- Single"/"- EP" suffix the
on-disk filename still carries, so _findFileByStem matched neither exact nor
suffix and every fallback failed -> "File not found on disk" on edit/delete.
Now also match on the structured prefix in both directions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The delete-song endpoint returned a bare "File not found on disk" with no
context, making path-resolution mismatches impossible to diagnose. Now
reports the Navidrome path, music root, and searched filename — matching the
detail the edit-metadata endpoint already provides.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Track path: always fetch iTunes when durationMs/trackNum missing (not just genre)
- Both paths: strip release suffixes from title before iTunes lookup
- Log "Album:" instead of "Album aus Spotify:" (also fires for iTunes global search)
- Log duration when fetched from iTunes so it's visible in job log
Ensures all importers have consistent yt-dlp search filters (duration filter
now always runs when iTunes finds the track).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- downloads.js: use durationMs (Spotify) before trackTimeMillis (iTunes)
- server: read durationMs from top-level body, not only from tracks[0]
- server: also grab durationMs + trackNum from iTunes in the Spotify path
Without durationMs the duration filter never ran, allowing a 58s fan video
to pass all text filters and be downloaded instead of the actual song.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deletes backend/lib/squid.js; strips all references from downloader.js,
config.js, novasonic-server.js, docker-compose.yml, .env.example, README,
and CHANGELOG. Default DOWNLOAD_BACKEND is now ytdlp.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Colons in startDateTime were being percent-encoded which Ticketmaster
may reject. Also log errors/status so backend logs reveal the cause
when no events appear.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend: swap /api/events/:artist to use Ticketmaster Discovery API
(startDateTime=now, classificationName=music, size=20, sort by date).
Maps TM response to the same {datetime, venue, offers} shape the modal
expects. Returns [] if TICKETMASTER_API_KEY is not set.
Frontend: remove all debug console.logs from events.js; drop redundant
client-side date filter (TM already filters by startDateTime server-side).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- edit-metadata: use albumArtistSort (when provided) for artist folder naming
instead of auto-converted romajiAlbumArtist — ensures manual overrides sent
by the download pipeline also place files in the correct folder
- Add POST /api/fix-artist-sort endpoint: searches Navidrome for all songs
where artist or albumArtist matches the kanji, re-writes ARTISTSORT +
ALBUMARTISTSORT via ffmpeg (-map_metadata 0 preserves all other existing
tags), moves each file to the correct artist folder, cleans up empty old
dirs, triggers Navidrome rescan — returns a job ID for progress tracking
- addRomajiOverrideUI: after saving a new override, calls fix-artist-sort and
shows a job card with live progress
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When Navidrome has a Japanese path in its DB but the file was saved with
a romaji filename by the download pipeline, the existing fallback chain
compared the Japanese title stem against romaji filenames and always
failed. Added _findFileByStemOrRomaji which converts the stem via
romajiForStorage before the final recursive search, covering all three
endpoints: edit-metadata, delete-song, song-sorttags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /api/delete-song endpoint: resolves path via same fallback chain as
edit-metadata, validates file is inside music root + is an audio file,
deletes it, then prunes the album folder if empty and the artist folder
if that too becomes empty — no recursive rm, depth strictly limited to
two levels above the file so no accidental mass-deletes
- Safety: path.resolve() guard ensures no path-traversal outside music root;
rmdirSync (non-recursive) fails if dir is non-empty as extra safety net
- Frontend: delete button (red outline, trash icon) at bottom of edit mode
with confirmation dialog showing title + folder-cleanup warning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Handles "File not found" for songs with non-standard folder/artist names
(e.g. artist separator • or year-only album folders) by doing a deep
recursive title-stem search as a last resort after rebase and dir scan.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/api/itunes/lookup was returning 400 for any request without artist+title,
including id-based lookups (?id=...&entity=album) used by the artist page.
Use next() to fall through to the /:endpoint proxy for those requests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a file is renamed or moved to a new artist/album folder during
metadata edit, Navidrome assigns a new song ID, breaking all playlist
entries that referenced the old ID.
Before the edit: collect which playlists contain the song.
After rename + rescan: poll Navidrome until the new song appears,
then update each affected playlist (add new ID, remove old index).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When Navidrome has a stale index (e.g. file was renamed from
'01 - Idea 10.mp3' to '01 - Gibran Alcocer - Idea 10.mp3'),
_findFileInDir now falls back to the original file's directory
and matches by track-number prefix ('01 - ') so the renamed file
is found even without a Navidrome rescan.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Spotify enrichment only copies album/trackNum/duration from iTunes.
Now also fetch totalTracks, discNum, discTotal from iTunes for:
- alreadyEnriched playlist tracks
- single track downloads where album came from Spotify
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
When Navidrome and the backend run in separate containers, Navidrome returns
absolute paths rooted at its own mount (e.g. /opt/navidrome/music) which
don't exist in the backend container (mounted at /music).
Add three-stage fallback:
1. _rebasePath: strip leading path components and try under CONFIG.musicPath
2. _findFileRecursive: exact filename search under CONFIG.musicPath
3. _findFileInDir: fuzzy search in album dir by track-number prefix or title stem
(handles files renamed to include artist in the filename)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Artist grid and detail header now show romaji names when toggle is on.
Builds a Map<artistId, artistRomaji> from the existing song romaji index
so no extra API call is needed.
- Apple Music autofill button added to edit modal next to MusicBrainz.
Reuses the existing lookupItunesTrackMeta backend function via a new
GET /api/itunes/lookup endpoint — fills album, year, genre, track#,
and cover art.
- Romaji index is invalidated after every successful download so newly
added songs appear with correct sort tags without requiring a page reload.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- backend: new GET /api/song-sorttags reads TITLESORT/ARTISTSORT/ALBUMARTISTSORT/
ALBUMSORT directly from the file via ffprobe
- ui.js: pre-fill sort fields from romaji index, then overwrite with real file
tags from the backend (shows actual values even if manually set)
- index.html: remove redundant hint texts in settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- views.js: skip ASCII word-match when artist name is non-ASCII so Japanese
artists (e.g. ヨルシカ) resolve correctly to an iTunes artist ID
- index.html + styles.css: collapsible Sort-Tags section in edit modal
- ui.js: pre-fill sort fields from romaji index; include in save body
- novasonic-server.js: accept manual titleSort/artistSort/albumArtistSort/albumSort
overrides; fall back to auto-computed romaji when not provided
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Downloads now store original Japanese in TITLE/ARTIST/ALBUM tags
so Apple Music, iTunes, YTM etc. all see the authentic original text
- Sort tags (TITLESORT/ARTISTSORT/ALBUMSORT) written with romaji for
apps that use them for sorting/display
- iTunes-resolved English artist name used for ARTISTSORT and folder names
when available (more accurate than kuromoji for known artists)
- rawArtist parameter added to downloadTrack so YTM search query uses
the original Japanese channel name (fixes wrong song downloads)
- spotify.js: artistName preserved as original; itunesArtistName stored
separately so the Spotify importer shows Japanese titles
- romaji index now includes titleRomaji/artistRomaji/albumRomaji fields
- Frontend: showRomaji setting (Settings > Bibliothek) — when on, player
bar, now-playing, queue, and song lists all show romaji via index lookup
- refreshDisplayMode() updates all live UI when the toggle is saved
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Clean Japanese punctuation (、。・) in romajiForStorage so it doesn't pass through
- Add POST /api/romaji-convert endpoint for batch title conversion
- Add romajiConvert() to api.js
- Add third fallback in _createNdPlaylist: convert Japanese title to romaji and
search Navidrome with it (for songs already downloaded with romaji tags)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- ytdlp.js + novasonic-server.js: apply romajiForStorage to album tag
and folder/rename paths (was only applied to artist/title before)
- ui.js: _createNdPlaylist falls back to romaji index for Japanese titles
since Navidrome search3 doesn't index Japanese text properly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Search iTunes with original Japanese names (accurate) rather than
pre-converting with kuromoji (inaccurate for uncommon names/readings).
iTunes returns the correct English artist name and title directly.
Also run iTunes enrichment for single-track Spotify imports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add romajiForStorage() to romaji.js — smart word spacing + Title Case
- ytdlp.js: apply to folder names, filenames, and embedded tags
- novasonic-server.js: apply in edit-metadata endpoint and Spotify resolve
- spotify.js: update track artist/name from iTunes canonical English names
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
_fmtArtists and _normalizeArtists now split on & and / in addition to
• · ; , — so "Arriez & King Dif" typed in the field normalizes correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"Arriez & King Dif" was used as-is for the folder; now splits on , ; &
so only the first artist is used: "Arriez".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
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>
After saving metadata with ffmpeg:
1. Title change → rename the mp3 in place to "{01 - }?{sanitized title}.ext"
(same convention as the downloader uses)
2. Album-artist or album change → compute new {artistFirstPart}/{album}
directory, move the file there (create dirs as needed)
3. Old album dir is deleted if empty after the move; old artist dir
is deleted too if it also becomes empty
When only the album name changes (no artist change) the old in-place
folder-rename behaviour is preserved.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- Info modal view now shows Album-Künstler row from Navidrome albumArtist
- Song Importer detects youtube.com / youtu.be URLs (red badge)
- New /api/youtube/resolve endpoint: yt-dlp --dump-json for single
videos and playlists (up to 50 entries), enriched with iTunes lookup
for proper album/year/genre/cover on single tracks
- New src/youtube.js renders preview card and delegates download to
executeSpotifyDownload (which already handles sourceUrl → direct yt-dlp)
- Importer hint updated to list YouTube as a supported source
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>