Commit Graph
50 Commits
Author SHA1 Message Date
HoneyandClaude Sonnet 4.6 62fefbb5e7 fix: import scDlSingle and scDlTrack in main.js to prevent runtime ReferenceError
Missing imports caused Object.assign(window,...) to throw before navigate
was ever assigned, breaking all onclick nav buttons.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 20:56:16 +02:00
HoneyandClaude Sonnet 4.6 0e8eb311af fix: circular import causing navigate undefined; unified importer input
- Remove top-level ui.js import from soundcloud.js (broke views.js→soundcloud.js→ui.js→views.js cycle)
- Use window.executeSpotifyDownload() instead to avoid the circular dep
- Merge Spotify + SoundCloud into one unified URL input that auto-detects
  the service and colors the badge/button accordingly
- resolveImporterLink() routes to the correct handler based on URL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 20:39:21 +02:00
HoneyandClaude Sonnet 4.6 ee65183555 feat: SoundCloud downloader + rename page to Song Importer
- New src/soundcloud.js: resolves SoundCloud URLs via yt-dlp --dump-single-json,
  renders track/playlist preview with per-track download buttons
- Backend POST /api/soundcloud/resolve: extracts metadata (title, artist, genre,
  artwork, duration) directly from SoundCloud — no iTunes lookup needed
- ytdlp.js: sourceUrl param — if set, downloads directly from URL instead of
  ytsearch cascade (used for SoundCloud tracks)
- Spotify importer page renamed to Song Importer, now contains both
  Spotify and SoundCloud sections in one view

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 20:35:48 +02:00
HoneyandClaude Sonnet 4.6 5559857c15 feat: metadata editor with MusicBrainz autofill + cover art upload
- Info modal gets an edit pencil button that switches to edit form
- Editable fields: title, artist, album, genre, year, track#, disc#, total tracks
- Cover art: upload your own image or auto-fetch from Cover Art Archive via MusicBrainz
- MusicBrainz button queries /api/musicbrainz/lookup and autofills all fields
- Save POSTs to /api/edit-metadata which rewrites tags via ffmpeg and triggers Navidrome rescan
- Backend: GET /api/musicbrainz/lookup + POST /api/edit-metadata endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 19:22:11 +02:00
HoneyandClaude Sonnet 4.6 f79817410b feat: scrobbling, genre grid redesign with detail view + play button
- Scrobble to Navidrome on auto-advance (Android) and track end (web)
  so type=recent and type=frequent are accurate for the hero card
- Genre section: pill chips → grid cards with colored left bars
- Top 15 genres shown by default (sorted by song count), expand toggle
- Genre card: click → genre detail page with full song list
- Genre card: hover play button → shuffle-play without navigating
- Genre detail page reuses album-detail layout (back btn, stats, song list)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 09:18:56 +02:00
HoneyandClaude Sonnet 4.6 4b2f277ff7 feat: persist volume level in localStorage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 21:39:56 +02:00
HoneyandClaude Sonnet 4.6 4f152028f4 feat: auto-close preview when main playback starts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 21:10:08 +02:00
HoneyandClaude Sonnet 4.6 2553534cc8 tweak: keep player bar static during swipe-up; improve title/album/artist spacing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 21:03:54 +02:00
HoneyandClaude Sonnet 4.6 e47450b20d tweak: reduce swipe-up overlay speed from 2.5x to 1.5x for smoother feel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:32:00 +02:00
HoneyandClaude Sonnet 4.6 43312be65d fix: remove overlay cap during swipe-up so NP slides smoothly without stopping
The Math.min(160) cap on 'lift' meant the overlay froze after 160px of drag
(at ~70% down the screen) while the finger kept moving — the felt 'stop'.

Now: player bar caps at its own height (once it slides off, no more movement
needed), overlay tracks the finger with no cap at 2.5× speed so ~360px of
upward drag fully reveals the now-playing screen. Release animation (from
the previous fix) then carries it smoothly to position 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:16:32 +02:00
HoneyandClaude Sonnet 4.6 b0fb1b200d fix: smooth NP swipe-open animation; live playing highlight in song lists
Swipe animation:
- _resetDrag(true) now reads the overlay's current drag position and starts
  the CSS transition from there instead of resetting to translateY(100%) first.
  That reset was causing the visible stutter (overlay jumped off-screen then
  slid back up). Now the overlay continues seamlessly from wherever the finger
  released to translateY(0).

Playing highlight:
- makeSongItem: adds data-song-id attribute to every .song-item element.
- updatePlayerUI: calls _updatePlayingHighlights() after every track change.
  This toggles .playing on all visible song items in any open list/playlist/
  favorites view so the highlighted row always matches what's actually playing,
  without requiring the user to navigate away and back.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:10:03 +02:00
HoneyandClaude Sonnet 4.6 9d8a5a22bb fix: button focus ring, playlist reorder mode, edit modal mobile, backdrop-filter
- button:focus{outline:none} — removes the blue tap-highlight box in WebView
- Playlist reorder: replaced always-on drag handles with explicit 'Neu anordnen'
  mode. Handles only appear after tapping the button; a sticky Speichern/Abbrechen
  bar appears above the list. Touch drag is now fully supported. Context-menu
  (long-press) is blocked while reorder mode is active to prevent conflicts.
- styles.css: .pl-drag-handle always visible when pl-draggable (no hover needed)
- Edit modal footer: wrap buttons on small screens so Save doesn't overflow
- Edit modal overlay: remove backdrop-filter:blur — causes WebView compositor
  glitch where 3/4 of the modal disappears after returning from background

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:40:11 +02:00
Honey e78df7bdb9 feat: add search filter bar to playlist detail and favorites views 2026-05-13 13:58:08 +02:00
Honey 47f1605965 feat: add sort filter chips to albums and favorites views 2026-05-13 12:53:34 +02:00
Honey 5ee6614ac9 fix: lower default audio volume from 50% to 30% 2026-05-09 16:21:09 +02:00
Honey 2feb00be62 fix: don't auto-resume audio on tab/app focus if user explicitly paused 2026-05-09 16:17:50 +02:00
HoneyandClaude Sonnet 4.6 76ecfe2268 feat: wire NSBridge fully + HD artwork + disable watchdog in wrapper mode
player.js
- Replace `_native()` function with `const HAS_BRIDGE` (evaluated once at
  module load so bridge detection is rock-solid)
- Add `buildTrackList()` — maps S.queue to NSBridge Track array with 1024px
  artworkUrl so lock-screen cover is crisp on high-DPR displays
- Add `_pushQueueIfBridge()` — called by `fillRadioQueue()` so radio top-ups
  are pushed to ExoPlayer in real time (fixes autoplay gap after radio refill)
- `playQueue`: route through `NSBridge.setQueue` when bridge present; old
  `S.audio` path retained for browser/PWA fallback
- `playCurrent`: `NSBridge.seekToIndex + play` in bridge mode
- `nextSong / prevSong / togglePlay / onEnded`: delegate to bridge, skip local
  S.qIdx manipulation (native sends onTrackChanged callback instead)
- `seekTo / _initBarScrub`: use `_lastDurationMs` for ms-accurate seeks
- `onTrackChanged`: guard with `p.index !== S.qIdx` to avoid redundant redraws
- `updateMediaSession`: artwork sizes 96/300/512 → 256/512/1024
- Player-bar cover: 100 → 256px; NP cover/bg: 400 → 1024px;
  queue items: 60 → 128px; preload hint sizes: 96/300/512 → 256/512/1024

main.js
- Wrap watchdog setInterval in `typeof window.NSBridge === 'undefined'` guard;
  the foreground service keeps ExoPlayer alive — JS watchdog not needed there

views.js
- Album detail / playlist detail covers: 300 → 512px
- Playlist grid cards: 200 → 512px
- Search result song cards: 200 → 256px
- Artist top-songs / all-songs thumbnails: 60 → 128px
- Partial album art in _renderArtistAlbums: 300 → 512px

components.js
- makeAlbumCard: 200 → 512px
- makeSongItem: 60 → 128px

nowplaying.js
- NP queue items: 60 → 128px
- NP action sheet thumb: 80 → 256px

sw.js
- Bump CACHE to ns-v41 so stale small-cover entries are evicted on next load

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 10:33:43 +02:00
HoneyandClaude Sonnet 4.6 0e17d2e5db feat: native Android audio via Media3 ExoPlayer + full NSBridge JS adapter
- Replace legacy MediaBrowserServiceCompat/MediaSessionCompat with
  Media3 MediaSessionService (MusicService.kt) + ExoPlayer set to
  WAKE_MODE_NETWORK — holds WakeLock + WifiLock through Doze so audio
  plays uninterrupted on locked screen
- Media3 manages the notification; no manual NotificationCompat.Builder
  → no disappear/reappear flicker when skipping tracks
- Full bidirectional bridge:
    JS → native: window.NSBridge.* (WebBridge.kt, 13 methods)
    Native → JS: window.NSPlayer.* callbacks (onReady, onTrackChanged,
      onPositionUpdate, onPlaybackStateChanged, onQueueEnded, onError)
- player.js: progressive enhancement — all playback routes through
  NSBridge when running inside the wrapper, falls back to S.audio on
  plain PWA/browser; removes all window.Android.* calls
- main.js: guard _onForeground mediaSession playbackState setter behind
  !window.NSBridge so native Media3 session is never stomped
- ProGuard rules updated to keep WebBridge @JavascriptInterface methods
  and Media3 classes
- bridge-test.html: manual integration test page for every bridge method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 01:14:28 +02:00
HoneyandClaude Sonnet 4.6 d355129134 fix: lock screen audio + notification — audioSession once, deferred metadata, SW artwork cache
- Set navigator.audioSession.type='playback' only once (guarded flag) — repeated
  reassignment was resetting the OS audio session and triggering Doze network cuts
- Defer updateMediaSession() until after play() resolves, not before audio.src swap —
  new MediaMetadata during HAVE_NOTHING state caused Samsung to close/reopen notification
- Fix silent bug: updatePlayerUI(s, true) now actually skips MediaSession (added param)
- 10s safety guards now attempt play() instead of just resetting _trackChanging flag
- SW v40: cache-first for getCoverArt with token-stripped keys — artwork served from
  cache even when Doze blocks network, survives session restarts with new auth tokens
- Pre-warm next track artwork 2s after song starts (earlier than 30s-remaining trigger)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 18:37:23 +02:00
HoneyandClaude Sonnet 4.6 688348545d fix: clean rewrite of audio recovery matching Kima Hub design
playCurrent():
- Remove canplay listener from before play() — it caused a race where
  both the listener and the initial play() fired simultaneously
- canplay is now only registered in the catch (failure path only),
  eliminating the double-play race entirely
- AbortError: reload src+load() with _trackChanging=true (OS interruption
  recovery); canplay fires once buffered and retries play()
- Other errors: reset _trackChanging, let canplay retry (not throttled
  by Android background timers unlike setTimeout)
- Add 10s safety net to reset _trackChanging if Doze kills everything
  (matches Kima Hub's reload failsafe)

main.js error handler:
- Skip when _trackChanging=true (playCurrent already recovering) to
  prevent conflicting dual recovery attempts
- _onForeground: reset _trackChanging on play() failure so the flag
  can't get stuck when foreground recovery also fails

index.html:
- Add crossorigin=anonymous: Navidrome and NovaSonic backend both send
  Access-Control-Allow-Origin:* so this is safe, and it enables proper
  cross-context HTTP caching for cover art and audio responses

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 23:34:28 +02:00
HoneyandClaude Sonnet 4.6 a0c6d09c41 fix: stable notification artwork + canplay retry for background audio
- Cache cover art URLs per song so the same auth token is reused across
  metadata updates; Samsung/Chrome can cache the artwork image and the
  notification no longer visually reloads on every skip
- Add canplay listener as background-safe play() retry: the audio
  element fires canplay from its internal state machine (not throttled
  like setTimeout by Android Doze), so play() is retried as soon as
  the browser has buffered enough data
- Set navigator.audioSession.type='playback' on each _tryPlay attempt
  (not just on play event) so the OS audio session claim happens before
  Chrome requests the play grant
- Guard _onForeground playbackState assignment with !_trackChanging to
  prevent visibilitychange/pageshow from stomping 'playing' to 'paused'
  during an in-progress track transition

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 22:49:35 +02:00
HoneyandClaude Sonnet 4.6 3f5a5a4c43 fix: Samsung notification stays alive; retry play() on background rejection
- Set playbackState='playing' explicitly before MediaMetadata swap so
  Samsung One UI never sees a gap where state is ambiguous during skip
- Add retry loop (500ms→1s→2s→4s→8s) in playCurrent() catch — handles
  Android Doze rejecting play() in background; stops retrying if the
  song changes mid-flight (AbortError)
- Fix error handler: set _trackChanging=true before audio.load() so
  the load()'s pause event can't slip past the Samsung notification guard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 22:29:30 +02:00
HoneyandClaude Sonnet 4.6 33dc07ed5d fix: audioSession playback type, exponential backoff, pageshow recovery
- Set navigator.audioSession.type = 'playback' on first play — tells Android
  OS this is a music app, keeping background network alive through Doze mode
- Replace flat 2s error retry with exponential backoff (1s/2s/4s/8s)
- Add pageshow event alongside visibilitychange for Samsung/iOS PWA reliability
- Attempt audio resume on foreground if OS killed playback while backgrounded
- Switch preload from hidden audio element to link rel=prefetch (lighter,
  avoids triggering two-audio-stream detection on Android)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 21:34:23 +02:00
HoneyandClaude Sonnet 4.6 89e169b70a fix: prevent notification close on skip and background audio stall
- playCurrent: set _trackChanging flag and update media session metadata
  before changing src, so the pause event fired by src-swap does not
  set playbackState='paused' and dismiss the Samsung cover screen notification
- Remove stalled->load() handler that silently paused audio with no recovery
- Error handler now retries regardless of paused state (load() causes paused)
- Add .catch() to audio.play() in playCurrent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 20:23:51 +02:00
Honey 7744ae85db fix: Scrub lag fix and Volume control for Audiobooks 2026-05-03 17:20:02 +02:00
Honey 4b07203082 fix: Player switching 2026-05-03 17:10:03 +02:00
Honey 0d8b8b6f5d fix: Bug fixes Scrub lag Chapter track last chapter Mini bar stays over music Resume ABS doesn't stop music 2026-05-03 16:47:17 +02:00
Honey eb4d6fcbf9 fix: CROS fix, Mini Bar fix switches Audiobook and Music Player, Chapter Track mode added, Delted Playlist mode from Audiobooks 2026-05-03 15:14:19 +02:00
Honey 8f126562e7 update: Playlist mode toggle und Mini player bar für Audiobücher
fix: Music starts → ABS pauses: ABS starts → Music pauses
2026-05-03 15:04:30 +02:00
HoneyandClaude Sonnet 4.6 7c6343e28e fix: ABS API key auth + mobile bottom-nav Hörbücher button
- Replace username/password login with API key (Bearer token) auth
  - New absVerifyApiKey() verifies via GET /api/me instead of POST /login
  - Settings shows single API Key field with hint (Profil → API Token in ABS)
  - API key never pre-filled in settings for security
- Add "Bücher" button to mobile bottom-nav (bnav-library), hidden until connected
- _showAbsNavItem() helper keeps sidebar + bnav in sync on connect/disconnect

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 14:41:18 +02:00
HoneyandClaude Sonnet 4.6 c17b7ea11a feat: Audiobookshelf integration — browse, stream, chapters, sleep timer
- src/abs.js: full ABS API client + player engine
  - Login/token auth, library browsing, search
  - Playback session API (multi-file book support via startOffset)
  - Progress sync to ABS every 30s and on pause
  - Chapter navigation (prev/next/seek-to), ±10s / ±30s skip
  - Speed control (0.75–2×), sleep timer with countdown
- index.html: sidebar nav, audiobooks/detail pages, full-screen player overlay,
  ABS settings section (URL + credentials + library picker)
- state.js: abs + absPlayer state objects
- views.js: navigate() handles 'audiobooks' and 'abs-book-detail'
- styles.css: book grid, detail page, player overlay, pickers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 14:35:00 +02:00
HoneyandClaude Sonnet 4.6 719d4d9afd fix: standard ALTCHA PoW + lower default volume to 65%
Replace custom PoW scheme with standard ALTCHA: SHA-256(salt+number)
solver, base64-encoded {algorithm,challenge,number,salt,signature} verify
payload. Add verbose logging to diagnose future failures.
Lower default audio volume from 0.8 to 0.65.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 13:57:47 +02:00
HoneyandClaude Sonnet 4.6 598c108214 fix: detect YTM artist channel expansion in getYtmVideoUrls; revert PWA MediaSession changes
- 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>
2026-05-03 01:11:36 +02:00
Honey a5959b19b8 fix: pwa changes 2026-05-02 21:27:57 +02:00
HoneyandClaude Sonnet 4.6 fdb5f16332 fix: remove dead Spotify credentials; backend URL falls back to NS_BACKEND_URL env on login and auto-login
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:31:23 +02:00
HoneyandClaude Sonnet 4.6 abe048c4ed fix: remove bell+settings from sidebar; preserve backend/api keys on re-login
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:28:37 +02:00
HoneyandClaude Sonnet 4.6 85290a54f6 fix: null-safe doConnect for removed setup fields; move logout to settings with confirm; clean sidebar buttons
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:24:27 +02:00
HoneyandClaude Sonnet 4.6 c046aea776 feat: theme system with persistent selection and Dark Ocean starter theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 10:10:44 +02:00
HoneyandClaude Sonnet 4.6 1bb8494fdb feat: add navigation button to notifications for direct song/album/playlist access
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 10:01:41 +02:00
HoneyandClaude Sonnet 4.6 d9b9151f50 feat: persist shuffle and repeat state across reloads/updates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:55:32 +02:00
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 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 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 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 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 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 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 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