Cap Navidrome scan poll from 3 min to 60s, shrink post-scan buffer from 5s to 3s, and error fallback from 30s to 20s — playlist now appears sooner after downloads complete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add --match-filter 'webpage_url !*= /browse/' to baseArgs so yt-dlp
skips YTM artist channel pages returned as search results (previously
caused recursive channel-upload fetches that wasted time)
- Call _renderNotifList() in _addNotif so playlist-created notifications
appear immediately without needing a page reload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The normD regex used actual Unicode combining chars (U+0300-U+036F)
in the source which some build tools mangle, causing a SyntaxError at
runtime and silently killing playlist creation. Replaced with normA
that strips non-word/non-space chars via plain ASCII regex, which is
safe and achieves the same loose artist-first-word comparison.
Also fixed race condition in _pollAndCreatePlaylist — save lastStatus
before job is auto-deleted so the done check still works.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buildTitleKeywords: append '?' to keywords ending in 's' so regex
"Whispers?" matches both "Whisper" (YouTube title) and "Whispers"
(iTunes title) — fixes NoAki Whispers downloading wrong track
- _createNdPlaylist: if artist+title search returns no title match,
retry with title-only query so tracks with special chars in artist
name (e.g. "Kløn") are still found in Navidrome
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
backend: drop unreliable artist *= filter entirely — yt-dlp artist field
is empty for many label-distributed YTM tracks (NoAki, etc.), causing
correct results to be rejected in Steps 1-2 and falling through to Step 3
anyway. Search query already targets the right artist; title keyword +
duration are sufficient. Simplifies cascade from 8→8 steps but with fewer
wasted filter attempts.
frontend: add 3s delay before polling getScanStatus (gives Navidrome time
to actually start the scan), and 5s buffer after scan completes (lets DB
writes settle). Fallback to 30s fixed wait if scan API is unavailable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
backend: replace title *= (case-sensitive) with title ~= (?i) regex so
"MEMORIES" and "Echoes of Eternity" match regardless of YT title casing.
Refactor buildTitleKeyword → buildTitleKeywords returning array.
frontend: trigger Navidrome startScan after download completes and wait
for scan to finish before running _createNdPlaylist, so newly downloaded
tracks are indexed and searchable when the playlist is built.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"Kløn, Renzyx" → search with "Kløn Alone", compare "kløn" == "kløn" (not "kløn,")
songCount raised to 20 so generic titles like "Alone" have better recall.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Playlist track search: use 'Artist Title' query + songCount=10 (was title-only + 5)
fixes songs like 'Dead' by Geoxor being missed due to common-word collision
- Missing tracks now show 'Artist – Title' instead of just title
- renderJobs: update existing cards instead of full innerHTML wipe every 1.5s
eliminates slideUp animation re-triggering on every poll tick
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
closePlModal() resets S.plModalSongs=[] before ids was computed,
so no songs were ever added to the playlist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- 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>
Renders immediately with available data, then enriches with complete
metadata (genre, bitrate, path, plays, etc.) from getSong response.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collects tracks not matched in Navidrome search during playlist creation
and saves them as 'missing' in the notification — expandable list in the
bell panel labeled 'nicht in Navidrome gefunden'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bell icon in sidebar and mobile header with unread badge
- On download complete/error: saves notification to localStorage (survives reload)
- Playlist/album downloads: parses job log to list which tracks failed
- Panel toggles open with animation, click-outside closes it
- Each notification dismissable with X; "clear all" button
- Notifications capped at 100 entries in localStorage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- 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>
- Parse [offset:N] metadata from LRC files (was silently ignored, caused ~1s drift)
- Apply 200ms lookahead so active line highlights slightly before it's sung
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename "Warteschlange" -> "Queue", tighten tab padding/letter-spacing
so all 3 tabs fit on narrow screens
- _loadNpLyrics now tries Navidrome getLyricsBySongId first (works once
.lrc sidecar is rescanned), then falls back to fetching synced LRC
directly from LRCLIB and parsing [mm:ss.xx] timestamps in the frontend
- Plain text shown as last resort if no synced lyrics found
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a Lyrics tab to the NP overlay using Navidrome's getLyricsBySongId
endpoint. Synced lyrics auto-scroll and highlight the current line as
the track plays; tapping a line seeks to that position. Falls back to
plain text for unsynced lyrics.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>