Commit Graph
245 Commits
Author SHA1 Message Date
HoneyandClaude Sonnet 4.6 8e13786e6b fix: remix detection — official audio for normal tracks, no suffix for remixes
Remix titles (remix/edit/flip/vip/etc.) are specific enough without suffix.
Normal tracks get 'official audio' to prefer audio over music videos.
Original official-audio issue was only for remixes — now caught by regex.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 21:08:26 +02:00
HoneyandClaude Sonnet 4.6 caa4cc1ef3 fix: remove all query suffix — rely on duration filter only
Adding 'audio' shifts results for lesser-known artists (Kanro, etc.).
Plain 'Artist - Title' matches what users find manually on YouTube.
Duration filter is the reliable quality check against wrong songs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 19:56:05 +02:00
HoneyandClaude Sonnet 4.6 dcdae19cec fix: smart query suffix — skip 'audio' for remixes/edits
Remixes already have '(Remix)' etc. in title so no suffix needed.
Normal tracks get 'audio' to filter piano covers and tutorials.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 19:20:35 +02:00
HoneyandClaude Sonnet 4.6 06c38cd427 fix: use 'audio' instead of 'official audio' in search query
'official audio' biased towards artist's main channel (wrong for remixes).
No suffix caused piano covers to rank top. 'audio' filters covers/tutorials
without over-specifying to official channels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 19:15:21 +02:00
HoneyandClaude Sonnet 4.6 11e981215c fix: remove 'official audio' from yt-dlp search query
For remixes and edits, 'official audio' biases YouTube search towards the
artist's official channel (wrong song) instead of the specific version.
Duration filter is now the quality check — the plain query is more accurate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 19:06:08 +02:00
HoneyandClaude Sonnet 4.6 7461e44980 fix: revert to ytsearch1 with match-filter, avoid --max-downloads exit code issues
--max-downloads 1 exits with code 101 which varies by yt-dlp version — caused
ffmpeg to never run, leaving untagged files. Use ytsearch1: + --match-filter instead:
filters top result by duration, exits 0 in all cases, falls back cleanly if no match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:59:39 +02:00
HoneyandClaude Sonnet 4.6 03394cac68 fix: correct yt-dlp duration filter — handle exit code 101, remove --playlist-items from filtered search
--playlist-items 1 in baseArgs caused filter to only check result #1 (filter useless).
--max-downloads 1 exits with code 101 which we treated as an error — ffmpeg never ran,
leaving untagged files (→ Unknown Artist/Album in Navidrome). Now code 101 resolves
successfully and filtered search uses commonArgs without --playlist-items.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:55:01 +02:00
HoneyandClaude Sonnet 4.6 fb02d3faf4 feat: filter yt-dlp results by song duration to avoid wrong downloads
When iTunes/Spotify provides a track duration, search 5 YouTube results
and keep only those within 60–150% of the expected length (filters remixes,
prayer videos, etc.). Falls back to ytsearch1: if no match is found.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:48:00 +02:00
HoneyandClaude Sonnet 4.6 dccf8a5dab fix: use first artist only for folder path on multi-artist tracks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:40:53 +02:00
HoneyandClaude Sonnet 4.6 8e9521f889 revert: restore original yt-dlp search — remove broken ytmsearch/ytsearch5 changes
Reverts cc7fc80, 59562fe, 595fb2b, 626a755.
The new search logic caused wrong songs, [Unknown Album] folders and duplicate downloads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:27:01 +02:00
HoneyandClaude Sonnet 4.6 626a755688 fix: suppress ytmsearch unsupported scheme error, clean up search log
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:22:56 +02:00
HoneyandClaude Sonnet 4.6 595fb2b7f3 fix: install yt-dlp via pip3 for latest version, fix multi-artist folder path
- Replace apk yt-dlp with pip3 install to get current version with ytmsearch support
- Split mainArtist on comma in downloadTrack so multi-artist tracks
  always land in the primary artist folder (e.g. "Artist1, Artist2" → Artist1/)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:18:27 +02:00
HoneyandClaude Sonnet 4.6 59562fe156 fix: use ytmsearch: instead of ytmsearch3: (unsupported by yt-dlp)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:12:50 +02:00
HoneyandClaude Sonnet 4.6 cc7fc80c71 fix: improve yt-dlp search reliability and artist folder structure
- Switch to YouTube Music search (ytmsearch3) as primary source with
  fallback to ytsearch5 if no result found
- Add duration filter (±30s/+45s) and live-stream exclusion via
  --match-filter when iTunes trackTimeMillis is available
- Send trackTimeMillis from frontend in download payload
- Pass durationMs through downloadTrack and downloadAlbumTracks
- Use first artist only (split on comma) for folder path on multi-artist tracks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 18:03:05 +02:00
HoneyandClaude Sonnet 4.6 2b1eff2762 fix: send track/disc numbers from iTunes to backend; backend reads them for single track downloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:28:39 +02:00
HoneyandClaude Sonnet 4.6 a2f2f4f6c3 revert: restore iTunes direct calls — remove broken backend proxy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 01:12:19 +02:00
HoneyandClaude Sonnet 4.6 ad8ba08a03 fix: itunesProxy use 2-arg https.get matching existing working pattern, smarter fallback
- itunesProxy: drop 3-arg form (url, options, cb) — use 2-arg (url, cb) like lookupItunesTrackMeta
- _iGet: if proxy returns {results:[]} trust it (iTunes said no results); only fall back to direct Apple if proxy gave no response at all

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 01:08:32 +02:00
HoneyandClaude Sonnet 4.6 5ef6a6d0ba fix: proxy all iTunes/Apple RSS calls through backend to bypass 403 browser block
Apple now blocks direct browser requests from custom origins. Added backend proxy routes:
  GET /api/itunes/search  — iTunes search
  GET /api/itunes/lookup  — iTunes lookup by ID
  GET /api/itunes/charts  — Apple RSS most-played charts (albums/songs)

All itunesSearch/Lookup/TopCharts/TopSongs functions in api.js now route through backend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:57:29 +02:00
HoneyandClaude Sonnet 4.6 8bb2b0d181 fix: spotify importer artwork — split multi-artist before iTunes, render Spotify art immediately
- track-artwork endpoint: split artist on ,/•/ before iTunes search (same as download flow)
- track-artwork endpoint: fall back to existingArt when iTunes returns nothing
- frontend: render any artwork already in Spotify data immediately, don't wait for iTunes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:41:56 +02:00
HoneyandClaude Sonnet 4.6 9da48b88d8 fix: lyrics race condition, PC scroll, artwork matching, accidental artist/album taps
- nowplaying: add cancellation token to _loadNpLyrics to prevent stale async
  calls overwriting newer results (synced→plain race condition)
- nowplaying: replace scrollIntoView with explicit container.scrollTo to prevent
  page-level scroll on desktop (topbar shifting / gap at edge)
- backend: remove results[0] fallback in lookupItunesTrackMeta — only use
  exact title+artist match to avoid embedding wrong artwork
- components/views: makeSongItem accepts noArtistLink/noAlbumLink opts;
  album-detail disables both, playlist-detail disables artist link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 23:42:29 +02:00
HoneyandClaude Sonnet 4.6 2c7a04c2bd fix(spotify): artist split + playlist cover in downloads
navidromeArtist: replace ', ' with ' / ' so Navidrome splits
multi-artist strings (e.g. 'Artist A, Artist B' → 'Artist A / Artist B')
with existing Tags.artist.Split = ["/"] config.

Playlist tracks: remove playlist cover URL as defaultArt in _spTrackItem
so that songs where iTunes lookup fails get no embedded artwork instead
of the wrong playlist cover art.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:58:27 +02:00
HoneyandClaude Sonnet 4.6 74743a81e6 fix(spotify): use ', ' as artist separator (iTunes/Spotify standard)
User will add ', ' to Navidrome's Tags.artist.Split config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:43:30 +02:00
HoneyandClaude Sonnet 4.6 a5e38e64cf fix(spotify): use ' / ' as artist separator for Navidrome multi-artist splitting
Navidrome splits artist tags on '/' (Tags.artist.Split = ["/"]).
Using ' / ' between multiple artists (both from array format and from
Spotify's comma-separated subtitle) lets Navidrome register them as
separate artists without any config change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:39:36 +02:00
HoneyandClaude Sonnet 4.6 9f0cb899ed fix(spotify): use first artist for iTunes search in single-track downloads
Multi-artist strings like "Kløn, Renzyx" from Spotify's subtitle field
confuse the iTunes search API. Use only the first artist (split on comma)
as the search term, same as the playlist path already does.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:30:56 +02:00
HoneyandClaude Sonnet 4.6 16cf49638f fix(spotify): iTunes lookup per track in playlist downloads
Spotify's flat embed format provides no album, year or per-track artwork.
Now calls lookupItunesTrackMeta for every playlist track before downloading:
- Uses first artist of "Artist1, Artist2" subtitle for iTunes search query
  (better match), keeps full string as the MP3 tag
- iTunes album/year/trackNum/artwork override the empty Spotify fields
- Falls back to Spotify data if iTunes returns nothing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:28:24 +02:00
HoneyandClaude Sonnet 4.6 5446d83ab3 feat(spotify): lazy-load per-track iTunes artwork for playlists/albums
Adds POST /api/spotify/track-artwork endpoint that takes an array of
{idx, trackName, artistName} and returns iTunes artwork URLs in batches
of 8 concurrent requests.

Frontend calls this after rendering the track list, then patches each
.gtrack-art element with the correct album cover as results arrive.
Also updates _spotifyResolved.tracks so downloads use the right artwork.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:19:08 +02:00
HoneyandClaude Sonnet 4.6 a305ec98e2 fix(spotify): parse flat title/subtitle embed format for playlist tracks
Spotify's playlist embed uses {title, subtitle, audioPreview} per track
instead of {name, artists, album}. Fixes:
- _spTrackItem: reads title as trackName, subtitle as artistName fallback,
  audioPreview.url as previewUrl, isExplicit as boolean
- Route: reads entity.authors[0].name for playlist owner, entity.subtitle
  for track artist fallback, entity.title fallback for entity.name
- Playlist tracks get playlist cover art as default artwork (no per-track art)
- Removes temporary debug logging

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:14:02 +02:00
HoneyandClaude Sonnet 4.6 363fed61a5 debug: log Spotify playlist entity structure to diagnose empty tracks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 02:03:00 +02:00
HoneyandClaude Sonnet 4.6 393b88c823 fix(spotify): handle GraphQL embed structure, all artists, correct metadata
- _spTrackItem now unwraps GraphQL-style {data:{__typename,...}} wrappers
- _spAllArtists joins all featured artists (e.g. "Kløn, Renzyx")
- _spArtwork adds albumOfTrack path used by newer Spotify embed
- fetchSpotifyEmbed: added entity.content.items / entity.data fallback paths
- Track download now uses Spotify metadata directly (album, art, year,
  trackNum) when provided — avoids iTunes returning wrong album for
  multi-artist tracks
- Playlist trackList also tries entity.content.items and entity.tracks.items
- Frontend: redesigned preview card with green top accent, bigger art,
  playlist checkbox styled as pill with Spotify color on check, album
  button shows actual album name

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 01:59:12 +02:00
HoneyandClaude Sonnet 4.6 487dbf12b4 feat(backend): switch Spotify resolve to embed-page scraping
Removes the Spotify Web API token approach (blocked on non-Premium
accounts and from datacenter IPs). Now fetches open.spotify.com/embed
pages and parses __NEXT_DATA__ JSON — works without any auth from any IP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 01:47:06 +02:00
HoneyandClaude Sonnet 4.6 6f419d24ff fix: Spotify locale URL support + improved token headers
- parseSpotifyUrl now handles intl-de/, intl-en/ and other locale prefixes
- getSpotifyToken uses full browser headers + sp_t cookie for the public endpoint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 01:37:57 +02:00
HoneyandClaude Sonnet 4.6 cfa8a8cd49 fix: use Spotify public web-player token — no Premium/API account needed
Replace Client Credentials flow with Spotify's public web-player token endpoint
(open.spotify.com/get_access_token) which works without any account or Premium.
Remove Spotify credential fields from UI entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 01:32:56 +02:00
HoneyandClaude Sonnet 4.6 dc1bec74a1 fix: log Spotify resolve errors to console
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 01:30:25 +02:00
HoneyandClaude Sonnet 4.6 f7a5247f17 feat: Spotify Importer — link → yt-dlp download + optional Navidrome playlist
- New 'Spotify Importer' page under Entdecken (sidebar + mobile nav)
- Backend: POST /api/spotify/resolve — resolves track/album/playlist URLs via Spotify API
- Backend: new 'playlist' download type accepting pre-resolved track list (no iTunes re-fetch)
- Frontend: paste Spotify link → preview with cover, type badge, track list
  - Track: single download or full album via iTunes fallback
  - Album: download all tracks with Spotify metadata
  - Playlist: download all tracks + optional 'Als Navidrome-Playlist speichern' checkbox
- Navidrome playlist creation: searches library by title/artist after download, creates playlist
- Spotify Client ID + Secret stored in app settings (setup screen + settings modal)
- iTunes metadata tags kept for all downloaded files (existing pipeline unchanged)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 00:56:11 +02:00
HoneyandClaude Sonnet 4.6 5987e1f303 fix: auto-lookup album metadata from iTunes when missing on track download
Songs from new releases (Apple RSS feed) have no collectionName, causing
the album tag to be omitted from embedded metadata -> Navidrome shows
[Unknown Album]. Backend now calls iTunes search to fill in album, year,
trackNum, totalTracks, discNum, genre before downloading.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 18:57:25 +02:00
HoneyandClaude Sonnet 4.6 93b859bf0e feat: explicit metadata, lyrics embedding, E badge in global search
Backend:
- fetchLyrics() queries LRCLIB for plain + synced lyrics
- Synced lyrics saved as .lrc file (Navidrome reads these)
- Plain lyrics embedded via ffmpeg -metadata lyrics=
- ITUNESADVISORY=1 written for explicit tracks
- explicit flag passed through album/track download chain
- fetchItunesTracks + searchItunesAlbumTracks use country=us&explicit=Yes

Frontend:
- explicit:true sent in download payload from iTunes track data
- "E" badge shown on explicit tracks in global search track list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 18:12:53 +02:00
HoneyandClaude Sonnet 4.6 499179fd40 Show toast when download is skipped due to duplicate
Backend: set skipped:true on job when track already exists on disk.
Album downloads report "X geladen, Y bereits vorhanden" in the message.
Frontend: pollJob shows "⏭ bereits auf dem Server" instead of "✅ gespeichert"
when the job has skipped:true.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 14:06:45 +02:00
HoneyandClaude Sonnet 4.6 f08303753c Add duplicate check before downloading tracks
Before starting yt-dlp, check if Artist/Album/Title.{ext} already
exists on disk. If found, log a skip message and return early instead
of re-downloading and overwriting the existing file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 13:53:03 +02:00
HoneyandClaude Sonnet 4.6 4e19178e24 feat: add disc number and compilation metadata tags
- disc tag written as discNum/discTotal (e.g. 2/2) for multi-disc albums
- compilation=1 set when iTunes collectionType is Compilation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 00:44:37 +02:00
HoneyandClaude Sonnet 4.6 5f8141d6ba feat: write genre metadata from iTunes primaryGenreName
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 00:42:07 +02:00
HoneyandClaude Sonnet 4.6 15959b45eb feat: convert feat./ft./featuring to / in artist tag for Navidrome split
Navidrome splits artist tags on "/" so "Drake / Rihanna" becomes two
separate artist entries. navidromeArtist() converts iTunes featuring
patterns before writing the ID3 artist tag. album_artist stays clean
(main artist only) for correct album grouping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 00:23:46 +02:00
HoneyandClaude Sonnet 4.6 934e4f3b47 fix: hi-res cover art embedded, consistent album_artist for Navidrome grouping
- downloadArtwork(): fetches 1000x1000 iTunes artwork (was 100x100/600x600)
  and embeds it into the MP3 via ffmpeg — no more blurry YouTube thumbnails
- ffmpeg now uses -map_metadata -1 to strip all existing tags (removes the
  blurry YT thumbnail that was being copied over) then writes clean ID3 tags
- albumArtist param added to downloadTrack(); downloadAlbumTracks() always
  passes the main artist as album_artist regardless of track's feat. artists
  so Navidrome groups all tracks under one artist consistently
- Removed embedThumbnailFallback (superseded by new flow)
- Removed old downloadCoverArt (replaced by async downloadArtwork)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 00:21:38 +02:00
HoneyandClaude Sonnet 4.6 c470ff9d12 fix: album/artist download now saves individual tracks with full metadata
- Remove broken 'full album' fallback that downloaded everything as one MP3
- Add searchItunesAlbumTracks() so albums without a collectionId can still
  be resolved to individual tracks via iTunes search
- Prefix filenames with zero-padded track number (01 - Title.mp3)
- Write track tag as 'N/total' format (e.g. 3/12) plus totaltracks tag
  so Navidrome gets correct track order and total count

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:34:41 +02:00
HoneyandClaude Sonnet 4.6 f298661d72 feat: swipe-up player bar opens Now Playing; fix backend CORS for reverse proxy
- Add touchstart/touchend on .player bar: swipe up ≥40px or fast flick opens np-overlay
- Replace cors() package with explicit middleware that sets Access-Control-Allow-Origin/Methods/Headers and Access-Control-Allow-Private-Network (needed for Chrome when backend is on private network or behind proxy)
- Remove cors npm dependency from backend
- Bump cache to ns-v20

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:54:05 +02:00
HoneyandClaude Sonnet 4.6 b6edfcf14b Add NAVIDROME_URL autofill, remove Navidrome scan trigger
- Frontend reads window.NS_SERVER_URL from config.js (injected at container start via docker-entrypoint.sh) and pre-fills the setup screen
- Removed triggerNaviScan from frontend and backend (Navidrome auto-scans on file change)
- Removed NAVIDROME_USER/PASS from backend and compose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 16:44:38 +02:00