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>
- ytdlp.js: watch for [youtube:tab] in yt-dlp output during flat-playlist step;
return [] when channel expansion is detected so cascade falls through to ytsearch
- Revert player.js / main.js / state.js PWA _changingTrack changes (not effective)
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>
e.target in dragstart is always the draggable item element, never the
handle child — so closest('.pl-drag-handle') always returned null and
every drag was cancelled. Now mousedown on handle sets fromHandle=true
and item.draggable=true; dragstart checks the bool instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
draggable=true must be set before any mouse event (not on mousedown).
Firefox requires setData() for drag to work. Removed duplicate dragend
listener that was resetting draggable=false and conflicting with the first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drag handle appears on hover, moves items in DOM, syncs order to
songs array, and saves to Navidrome via createPlaylist 800ms after
last drop (debounced to avoid rapid API calls).
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>
new Image() onload fires when bytes are downloaded but GPU decode of the
blurred layer hasn't happened yet on mobile. Double rAF defers the opacity
swap until after the browser has actually painted the new background.
will-change:opacity keeps the layers on dedicated compositor layers so
the blur doesn't need to be recomputed on every opacity transition.
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>
_syncNpDirect is called 2-3x per skip from different call sites;
the crossfade now no-ops when the cover URL hasn't changed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two-layer opacity crossfade replaces the broken background-image transition.
New image loads on the hidden layer, then both opacity values swap smoothly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- renderQueue: in-place active-class update when queue unchanged — no DOM rebuild, no image reload
- updatePlayerUI: update img.src directly instead of replacing innerHTML
- _syncNpDirect: same img.src update for now-playing cover
- navigate('home'): always reloads home data so new albums appear without restart
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>
- 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>
_iGet now tries backend proxy first (if backendUrl set), falls back to direct
Apple API URL so iTunes search/lookup/charts work regardless of backend config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- itunesSearch: use r.text() + JSON.parse so empty body (rate-limit) returns [] instead of throwing
- reduce Spotify artwork batch size 8→4 to avoid hammering iTunes and affecting global search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CSS: target #player-artist-name span:hover and #np-artist-name span:hover so only the hovered artist name highlights, not all at once
- change artist separator from / to • in song items, player bar, now playing overlay, album header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>