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>
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>
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>
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>
- _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>
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>
- 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>
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>
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>
- 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>
- '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>
- 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>
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>
- 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>
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>
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>
- 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>
- 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>
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>
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>
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>
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>
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>
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>
- 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>
- 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>
- 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>
- 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>
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>
- 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>
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>
- 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>
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>
- 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>
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>