Commit Graph
95 Commits
Author SHA1 Message Date
HoneyandClaude Sonnet 4.6 e75d90e165 fix: eliminate flash on skip, refresh home on nav
- 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>
2026-04-28 09:21:12 +02:00
HoneyandClaude Sonnet 4.6 f210d7350e feat: replace native confirm() with custom styled dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:15:37 +02:00
HoneyandClaude Sonnet 4.6 8c83d4d598 feat: add delete playlist button to edit modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:13:04 +02:00
HoneyandClaude Sonnet 4.6 b575e38726 feat: add cover art to queue panel, remove redundant sidebar queue nav item
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:07:07 +02:00
HoneyandClaude Sonnet 4.6 4ab54e12e1 feat: track missing tracks in Navidrome playlist creation notification
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>
2026-04-28 08:59:17 +02:00
HoneyandClaude Sonnet 4.6 d5a26b764d feat: add notification bell with persistent download log
- 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>
2026-04-28 08:54:20 +02:00
HoneyandClaude Sonnet 4.6 fef8578a27 feat: add greeting header and recently played row to home screen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:39:54 +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 55be924805 fix: iTunes functions fall back to direct Apple API when backend not configured
_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>
2026-04-28 01:01:03 +02:00
HoneyandClaude Sonnet 4.6 063bf64996 fix: remove Discover APIs tab from login page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:58:58 +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 2b6870583b fix: handle iTunes rate-limit empty responses, reduce Spotify artwork concurrency
- 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>
2026-04-28 00:54:53 +02:00
HoneyandClaude Sonnet 4.6 a61436669e fix: per-span hover highlight, use • as artist separator everywhere
- 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>
2026-04-28 00:51:38 +02:00
HoneyandClaude Sonnet 4.6 90c76fe9a7 fix: spotify importer uses same iTunes lookup as global search for artwork + metadata
- _loadSpotifyTrackArtwork: rewritten to call itunesSearch() directly from browser (same as global search) instead of backend endpoint — same quality, works for all regions
- enriches track objects with collectionName, releaseDate, trackNumber, trackExplicitness so downloads get full metadata too
- 8-concurrent batch to avoid hammering iTunes
- adds onerror fallback to makeGlobalTrackItem img so broken URLs show 🎵 placeholder

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:49:55 +02:00
HoneyandClaude Sonnet 4.6 a31953d17e fix: split multi-artists in player bar and now playing overlay, fix broken onclick
- player bar: fix onclick broken by JSON.stringify producing double-quoted strings inside double-quoted HTML attribute; use single-quoted JS args instead
- now playing overlay: np-artist-name now splits artists and each is individually clickable (closes NP then navigates to artist)
- remove wrapper onclick from both np-artist-name and player-artist-name in HTML

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:45:10 +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 e029a8f900 fix: split multi-artists in player bar, each name navigates to own artist profile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:39:23 +02:00
HoneyandClaude Sonnet 4.6 60c9cb6cbb fix: no artist/album links in playlist, add comma to artist split
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:35:05 +02:00
HoneyandClaude Sonnet 4.6 e5a80242e9 fix: split multi-artists by • and /, clickable artist/album in playlists and album detail
- components.js: expand artist split regex to handle both ' / ' and '•' separators
- views.js: enable artist+album links in playlist song items (were noArtistLink:true)
- views.js: enable artist links in album detail song items
- views.js: album header artist is now split and renders as clickable links

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:31:48 +02:00
HoneyandClaude Sonnet 4.6 51babd9e41 fix: bump SW cache to v39, split multi-artists by ' / ', name-search fallback for artist nav
- sw.js: ns-v38 → ns-v39 forces PWA cache clear so previous fixes take effect
- components: split s.artist by ' / ' in makeSongItem — each part becomes its
  own clickable link; noArtistLink turns all parts into plain text
- views: s_goArtist searches Navidrome by name when no artistId is available
  (needed for secondary artists that only have the primary artistId in the song)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:13:13 +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 14e8226d1d fix(lyrics): hide scrollbars + fade edges + reduce active scale
- overflow-x:hidden prevents horizontal scroll indicator from
  scale(1.2) transform on active line
- scrollbar-width:none + ::-webkit-scrollbar hide vertical scrollbar
- mask-image gradient fades lyrics at top/bottom edges so text
  doesn't clip abruptly against the container border
- Active line scale reduced from 1.2 to 1.08 (less overflow risk)
- padding-top removed from lyrics-view (mask handles visual spacing)
- lyrics-plain gets padding-top:24px for better visual breathing room

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:32:28 +02:00
HoneyandClaude Sonnet 4.6 ec681b9057 fix(lyrics): improve LRCLIB lookup reliability
- Retry LRCLIB without album_name when exact match fails (404) —
  album name mismatch was the most common cause of falling back to plain
- Capture plainLyrics from LRCLIB and prefer it over Navidrome plain
  (LRCLIB plain text is usually higher quality)
- Use only first artist for LRCLIB lookup (multi-artist strings like
  'Artist A / Artist B' cause 404)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:27:19 +02:00
HoneyandClaude Sonnet 4.6 16b7faee2b refactor: split views.js and ui.js into focused modules
Extract 3 new files from oversized source files:
- src/components.js (~86 lines): makeCard, makeAlbumCard, makeSongItem,
  makeGlobalTrackItem, mkSection — reusable DOM builders
- src/spotify.js (~187 lines): loadSpotifyImporter, resolveSpotifyLink,
  clearSpotifyPreview, spotifyDlAll, and private render/artwork helpers
- src/nowplaying.js (~255 lines): openNowPlaying, closeNowPlaying,
  syncNowPlaying, switchNpTab, full lyrics engine, renderNpQueue,
  npGoArtist/Album, openNpMenu/closeNpMenu

views.js: 1121 → 864 lines
ui.js:     706 → 462 lines

Update imports in main.js, player.js, views.js accordingly.
Build verified: 15 modules, no errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:20:00 +02:00
HoneyandClaude Sonnet 4.6 4bdf3c7cbc fix(views): merge duplicate api.js imports into one line
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:11:21 +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 e31ddec843 fix(mobile): restore playback state + MediaSession stability
- _restoreLastPlayed: set audio.src + call updateMediaSession so play
  button works immediately and notification shows artwork on app open
- visibilitychange: re-register all MediaSession handlers after screen
  unlock, fold/flip, or background return (fixes vanishing notification)
- Stall watchdog (3s interval): detects frozen audio in background and
  calls play() to recover; also catches missed 'ended' events so
  auto-advance works reliably when screen is off
- stalled/error listeners: attempt audio reload on network hiccup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:53:25 +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 e75f2ddf97 fix: Spotify Importer button layout (btn-primary had width:100%)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 01:07:53 +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 9f68deea94 fix: lyrics sync offset tag + 200ms lookahead
- 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>
2026-04-26 22:38:20 +02:00
HoneyandClaude Sonnet 4.6 af03061f75 fix: lyrics active line uses transform scale instead of font-size
Prevents other lines from shifting when active line grows.
Adds subtle glow effect on active line for karaoke feel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 22:35:29 +02:00
HoneyandClaude Sonnet 4.6 9bf6d189df fix: artist hero banner taller on desktop, fix right-side overlay bleed
- Hero height 220px mobile → 320px desktop (min-width: 769px)
- Larger artist name (46px) and more padding on desktop
- Overlay gradient now also darkens the right edge instead of fading to transparent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 22:12:38 +02:00
HoneyandClaude Sonnet 4.6 976e80f86d feat: all-songs view as separate page, iTunes album detail page
- 'Alle X Songs' button now navigates to a dedicated page instead of expanding inline
- Partial/not-downloaded albums now open an iTunes album detail page with track list,
  preview buttons and download option
- Added page-artist-all-songs and page-itunes-album-detail to index.html
- Replaced _expandArtistAllSongs with loadArtistAllSongs + loadItunesAlbumDetail

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 19:23:16 +02:00
HoneyandClaude Sonnet 4.6 72f6087043 feat: unified artist page with full iTunes/Navidrome album categorization
- loadArtistDetail now works for both Navidrome artists and iTunes-only
  artists (id=null + itunesArtistId). loadItunesArtistResults from global
  search now navigates to the same artist-detail page.
- Album sections: "In Library" (fully downloaded), "Unvollständig" (partial
  — e.g. 2/12 tracks), "Noch nicht heruntergeladen" (iTunes-only). Partial
  albums show X/Y badge and a direct download button on hover.
- iTunes albums now fetched via lookup endpoint (up to 200 albums) instead
  of search (max 20), fixing the missing-albums problem.
- "Alle X Songs in Library" expand button fetches all songs across albums
  and renders them grouped by album with play/context-menu support.
- Back button uses _navGoBack() for context-aware navigation.
- CSS: .section-count, .album-partial-badge, .card-dl-btn (hover reveal)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 19:10:52 +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 f5e0af6e90 fix: lyrics sync via LRCLIB fallback + tab overflow fix
- 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>
2026-04-26 18:49:56 +02:00
HoneyandClaude Sonnet 4.6 fbabdf8269 feat: synced lyrics view in Now Playing overlay
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>
2026-04-26 18:42:45 +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 3c5048f017 fix: use iTunes lookup endpoint for explicit artist tracks
iTunes search endpoint always returns cleaned versions.
Added itunesLookupArtistSongs() using /lookup?explicit=Yes which returns
the actual explicit tracks. Used in artist profile (global search) and
artist detail page (library). Album track lookup also gets explicit=Yes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 17:58:32 +02:00
HoneyandClaude Sonnet 4.6 fc951d103b fix: use iTunes US catalog + explicit=Yes to show all tracks including explicit
country=de silently filtered explicit content from iTunes results.
Switched to country=us&explicit=Yes so all songs appear regardless of explicit flag.
Removed wrong explicit-filtering code from views.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 17:48:35 +02:00
HoneyandClaude Sonnet 4.6 31e5940ab6 fix: remove library results from global search, filter explicit content
- Global search now shows only iTunes results (no Navidrome library section)
- All iTunes results (tracks, albums, artist pages, album detail, recommendations)
  filtered to exclude explicit content (trackExplicitness/collectionExplicitness === 'explicit')

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 17:43:31 +02:00
HoneyandClaude Sonnet 4.6 fa7e980f1a feat: player drag feedback, global search fixes, iTunes artist discovery
- Player bar: drag handle pill + live lift/peek animation when swiping up to NP
- Global search: now includes Navidrome library results alongside iTunes; fix songs not showing (filter wrapperType=track)
- iTunes artist page: hero banner with album art, top songs section
- Artist detail: iTunes global top tracks + extra albums not on server

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 17:26:50 +02:00
HoneyandClaude Sonnet 4.6 ca21b98a19 Update README with full project overview
Covers features, module structure, Docker setup, env vars,
dev workflow and first-launch instructions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 14:08:10 +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 023d3710ab Fix missing window globals after Vite migration
switchNavMode, stopPreview, npToggleStar, loadRandom were imported
but not exposed to window — causing onclick ReferenceErrors in HTML.
Added npSeek stub (was empty function in original app.js).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 13:15:53 +02:00
HoneyandClaude Sonnet 4.6 655cbe6893 Remove deprecated root-level files after Vite migration
app.js replaced by src/ modules; sw.js, manifest.json, icon.svg
moved to public/ — root copies were duplicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 13:04:00 +02:00
HoneyandClaude Sonnet 4.6 9aded56713 Migrate to Vite + ES module architecture
Split monolithic app.js (~1850 lines) into maintainable ES modules:
- src/state.js  — shared mutable state (S object)
- src/utils.js  — md5, esc, formatDur, toast
- src/api.js    — Subsonic API, Navidrome REST, iTunes API
- src/player.js — playback, transport, queue, media session, preview
- src/views.js  — navigation, page loaders, card makers, search
- src/ui.js     — all modals, overlays, sidebar, settings
- src/pwa.js    — service worker registration + update banner
- src/main.js   — app init, connect, window globals

Updated Dockerfile to multi-stage (Node build → nginx serve).
Static assets (sw.js, manifest.json, icon.svg) moved to public/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 13:01:34 +02:00
HoneyandClaude Sonnet 4.6 ce53c48835 fix: playlist cover auto-refresh after upload
Re-generate coverUrl (includes fresh auth params) + append _cb=timestamp
to bypass browser cache. Update both detail view and edit modal preview.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 02:06:41 +02:00
HoneyandClaude Sonnet 4.6 1d318d052c fix: playlist cover upload — POST /api/playlist/{id}/image FormData with JWT
Confirmed endpoint pattern from Navidrome API contract (artist/:id/image).
FormData field 'image' + X-Nd-Authorization Bearer token. Bump SW ns-v36.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 01:44:23 +02:00
HoneyandClaude Sonnet 4.6 cb337fd463 fix: playlist cover upload via PUT /api/playlist/{id} with base64 imageData
Navidrome (react-admin) expects image as base64 data URL in JSON body,
not as a raw binary POST to /image endpoint. Bump SW to ns-v35.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 01:42:59 +02:00
HoneyandClaude Sonnet 4.6 16f4f03474 fix: playlist cover upload — send raw image body not multipart
Navidrome expects Content-Type: image/* with raw binary body.
Bump SW to ns-v34.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 01:37:08 +02:00
HoneyandClaude Sonnet 4.6 1c59b4eccd feat: show playlist description in detail view
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 01:29:16 +02:00
HoneyandClaude Sonnet 4.6 729e9d260c fix: Navidrome REST auth for playlist cover — use JWT Bearer token
POST /auth/login to get JWT token, then X-Nd-Authorization: Bearer <token>
for cover upload/delete. Resets cached token on auth failure.
Bump SW to ns-v32.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 01:28:21 +02:00
HoneyandClaude Sonnet 4.6 23d7c62045 feat: Edit Playlist modal — name, description, public toggle, cover upload
- 'Bearbeiten' button in playlist-detail header opens modal
- Subsonic updatePlaylist API: name, comment, public synced
- Cover upload via POST /api/playlist/{id}/image (Navidrome REST, Basic Auth)
- Cover delete via DELETE /api/playlist/{id}/image
- Graceful toast if Navidrome version doesn't support cover upload
- Mobile-responsive layout (stacks cover + fields on small screens)
- Bump SW cache to ns-v31

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 01:24:11 +02:00
HoneyandClaude Sonnet 4.6 dad2b0b730 fix: Android lock screen notification + radio mode + SW auto-update reliability
- Set mediaSession.playbackState='playing'/'paused' so Android keeps the
  notification alive when the screen sleeps
- Provide 96x96/300x300/512x512 artwork sizes for Android notification views
- playQueue() now calls fillRadioQueue() immediately when radio mode is on,
  so the infinite queue works without toggling radio off/on
- SW: add GET_VERSION/VERSION message handler for version-query fallback
- SW registration: updateViaCache:'none' prevents browser HTTP-caching sw.js
- Bump cache to ns-v30

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 01:02:56 +02:00
HoneyandClaude Sonnet 4.6 0b9493c147 fix: NP overlay — more top padding, centered content, roomier topbar
- np-inner padding-top increased to max(safe-area, 36px)
- np-topbar margin-bottom 28→32px + gap:12px between buttons and tabs
- np-tabs flex:1 + flex:1 on each tab so they share space evenly, no overflow
- np-tab font-size 11→10px, padding tighter to fit small screens
- #np-player-view gains justify-content:center so art/controls float mid-screen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 00:48:13 +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 e462b54adf feat: SW update banner — notify instead of force-reload
- SW activate now posts SW_UPDATED message to all clients instead of
  calling client.navigate() (which was disruptive and unreliable)
- injectPWA() calls reg.update() on every app open to eagerly check
  for a new SW rather than waiting for the 24h browser interval
- showUpdateBanner() renders a persistent accent-bordered toast with
  version tag, a "Neu laden" button and a dismiss X
- Banner sits above player bar, repositions on mobile above bottom-nav

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 00:30:35 +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 c1f71c1d06 fix: player-track overflow:visible so vinyl circle is not clipped
overflow:hidden on .player-track was cutting off the border-radius and
box-shadow ring of the circular cover art on all sides except one.
Text truncation is handled by the individual text elements themselves.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:52:25 +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 09ce590abd feat: mobile search bar in header — tap icon to search Navidrome library
Search icon added to mobile header. Tapping opens an inline search bar
below the header. Typing searches artists/albums/songs via Navidrome
search3 API with 300ms debounce. X button closes and returns to Home.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:53:56 +02:00
HoneyandClaude Sonnet 4.6 4b34bbc043 feat: smooth progress bar scrubbing with touch drag support
- initProgressScrub() adds touchstart/move/end + mousedown drag to both
  the player bar and NP overlay progress bars
- CSS transition disabled during scrub so fill follows finger instantly
- updateProgress() skips DOM updates while _scrubbing is active so
  timeupdate events don't fight the drag position
- Works on both bars with a shared _initBarScrub helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:45:23 +02:00
HoneyandClaude Sonnet 4.6 08d2f69720 fix: SW must not intercept cross-origin requests (backend API calls)
Added same-origin check so the SW only caches its own static assets.
Cross-origin requests (Navidrome API, NovaSonic backend) pass through
directly — SW interference was causing 'failed to fetch' in the PWA.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:17:47 +02:00
HoneyandClaude Sonnet 4.6 d9256e6294 fix: auto-reload all PWA windows when new SW activates
Chain activate: clear old caches → claim clients → navigate all windows.
client.navigate(url) forces a full reload so users always get the new
version without manually closing/reopening the PWA.
Also removed sw.js from PRECACHE (browser fetches SW directly, no need to cache it).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:10:08 +02:00
HoneyandClaude Sonnet 4.6 21822b4ddf fix: bottom-nav-inner must be column not row — tabs stacked above buttons
flex-direction:row put the tab strip and icon buttons side-by-side,
both fighting for 100% width, making them overflow and appear huge.
Column stacks the Bibliothek/Entdecken tabs on top, icon row below.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:07:14 +02:00
HoneyandClaude Sonnet 4.6 1b981591d4 fix: switch SW to network-first strategy so PWA always gets fresh updates
Cache-first meant users were stuck on old versions until manual cache clear.
Network-first fetches live, updates cache, falls back to cache when offline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:02:05 +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 052846e458 Round vinyl cover in player bar + larger mobile player
- player-art: border-radius 50% (circle), vinyl ring shadow, centre dot appears while spinning
- Mobile player height: 62px -> 74px, cover: 36px -> 46px

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:43:20 +02:00
HoneyandClaude Sonnet 4.6 6e00b78219 Fix MUSIC_PATH direct mount + backend URL prefill
- MUSIC_PATH is now used 1:1 inside the container (no /music remapping)
- config.js 404-safe with inline fallback defaults
- Prefill uses .trim() so empty strings don't overwrite fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:37:30 +02:00
HoneyandClaude Sonnet 4.6 56c5662a11 Fix PWA install: static manifest.json + proper SVG icon
- Replace blob: manifest (ignored by Chrome) with static manifest.json
- Replace data: URL icon with real icon.svg file
- Link manifest and apple-touch-icon directly in <head>
- Remove blob/data URL generation from app.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:20:55 +02:00
HoneyandClaude Sonnet 4.6 042ce0b095 Fix Android media notification: add setPositionState + seek handlers
- setPositionState synced every second via updateProgress so the notification shows progress bar and timestamps
- seekto handler enables dragging the notification progress bar
- seekbackward/seekforward handlers enable 10s skip buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:16:29 +02:00
HoneyandClaude Sonnet 4.6 87636ea774 Fix bottom nav: flex:1 on items so they stretch edge to edge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:14:20 +02:00
HoneyandClaude Sonnet 4.6 89c08ed2ab Bottom nav: flex space-around for proper edge-to-edge distribution
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:11:13 +02:00
HoneyandClaude Sonnet 4.6 a5461ff92c Fix bottom nav full-width layout + add backend URL autofill
- Remove max-width/gap/padding from bottom nav so buttons fill full screen width
- Add NS_BACKEND_URL to config.js entrypoint and autofill backend-url field on setup screen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:03:34 +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
HoneyandClaude Sonnet 4.6 c789672793 Remove Traefik labels, use external reverse proxy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:03:54 +02:00
HoneyandClaude Sonnet 4.6 df69990625 Add Docker setup: nginx frontend + backend compose
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 14:55:28 +02:00
HoneyandClaude Sonnet 4.6 a3ac8bef30 Initial commit: split single-file PWA into index.html, styles.css, app.js, sw.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 14:49:12 +02:00