Commit Graph
100 Commits
Author SHA1 Message Date
vicrodh 1ad16338fa feat: add Qobuz playlist follow/unfollow (#172)
Discovered undocumented Qobuz API endpoints from bundle analysis:
- POST /playlist/subscribe { playlist_id }
- POST /playlist/unsubscribe { playlist_id }

This is the native "follow playlist" feature that syncs across all
Qobuz clients, separate from QBZ's existing "Copy to Library" (local
copy) and "Favorite" (local flag) features.

Added:
- subscribe_playlist/unsubscribe_playlist in both qbz-qobuz and legacy client
- v2_qobuz_subscribe_playlist / v2_qobuz_unsubscribe_playlist commands
- Bookmark toggle button in PlaylistDetailView (next to Copy button)
- i18n keys in all 5 locales

cc
2026-03-19 11:36:56 -06:00
vicrodh 5488839e97 fix: increase PipeWire rate switch delay and verify after stream creation (#180)
USB hubs/docks (e.g. Razer USB4 Dock) may need longer than 300ms for
sample rate switching. When PipeWire hasn't finished reconfiguring,
96K/192K tracks play at wrong speed (half/quarter) and sound distorted.

Changes:
- Increase initial clock.force-rate delay from 300ms to 500ms
- After stream creation, query pw-metadata clock.rate to verify the
  actual graph rate matches what was requested
- If mismatch detected, retry with additional 500ms delay and re-force
- Log warnings when rate can't be verified so users can report

cc
2026-03-19 10:57:21 -06:00
vicrodh 509426ebcf fix: restore booklet viewer with mupdf 0.6 (#170)
mupdf 0.5 had struct size mismatches on Arch Linux (mupdf-sys bindings
incompatible with system MuPDF headers). Upgrading to mupdf 0.6 fixes
the bindings.

- Removed the booklet feature gate — mupdf is now a direct dependency
  again, matching the v1.1.19 configuration
- Removed stub implementation (always compile real MuPDF backend)
- sys-lib-libjpeg remains excluded (cross-distro libjpeg incompatibility)

cc
2026-03-19 09:28:07 -06:00
vicrodh c2e9a4f0ba fix: replace broken screen resolution clamping with sane fallback
xdpyinfo returns combined multi-monitor resolution (e.g. 6000x1600 for
dual monitors), causing false positives like clamping 1926x1590 to
5700x1520. The 95% multiplier made things worse — the fallback size was
larger than any single monitor.

Replace with simple corruption guard: only clamp values above 8K
(obviously corrupt DB entries), fallback to 1920x1080. The window
manager already handles fitting windows to the actual display.

cc
2026-03-19 08:44:29 -06:00
vicrodh 2439b5f4d8 fix: remove KDE KWin SSD hack entirely (#170)
The KWin window rule approach (kwinrulesrc + qdbus reconfigure) was
never actually active because GDK_BACKEND is unset on most KDE Wayland
sessions. In v1.1.18 this meant decorations(use_system_titlebar) was
used directly — GTK CSD appeared (ugly but functional, no CPU issues).

The hack itself, when triggered, caused double titlebar and a CPU busy
loop that blocked networking. Remove all KDE-specific detection and
KWin rule setup. Window now simply uses decorations(use_system_titlebar)
on all platforms, matching the effective behavior of v1.1.18.

The one-time cleanup of stale kwinrulesrc entries is preserved so users
who had the rule from v1.1.14-v1.1.19 get it cleaned up automatically.

cc
2026-03-19 08:29:49 -06:00
vicrodh 6722e6f59a fix: add offline mode button to network error screen (#173)
When BundleExtractionFailed (no network), the error box only showed
"Retry" with no way to enter offline mode. The timeout case and login
form both had the offline button, but the error state was missing it.

Added "Start Offline" button using existing offline-btn CSS and
i18n keys. Also replaced hardcoded strings with $t() calls.

cc
2026-03-19 03:32:29 -06:00
vicrodh 8a8c709c1b feat: lock volume at 100% when ALSA Direct hw: is active
With ALSA Direct hw: there is no software mixer — volume must be
controlled at the DAC/hardware level. Software volume attenuation
would reduce bit depth and defeat the purpose of bit-perfect playback.

Backend: v2_set_volume forces volume to 1.0 when backend=Alsa+plugin=Hw
Frontend: volume slider disabled (grayed out at 100%), mute/step buttons
hidden, handleVolumeChange and handleToggleMute return early.
Settings callbacks notify +page.svelte when backend/plugin changes to
update the lock state in real-time.

i18n: volumeLockedHw tooltip added to all 5 locales.

cc
2026-03-19 00:10:59 -06:00
vicrodh 9bcafe26fb fix: alsa_hardware_volume was hardcoded false + v2_reset_audio_settings sync
Two additional gaps found during audio settings sync audit:

1. convert_to_qbz_audio_settings() hardcoded alsa_hardware_volume to
   false instead of reading settings.alsa_hardware_volume. This meant
   the player never received the user's ALSA hardware volume preference.

2. v2_reset_audio_settings saved defaults to DB but never synced them
   to the CoreBridge player, leaving stale settings in memory.

cc
2026-03-18 22:20:04 -06:00
vicrodh e490010e58 fix: sync audio settings to CoreBridge player on change and session activation
Two bugs caused the exclusive mode to not work when QConnect was active:

1. Critical audio setters (backend_type, exclusive_mode, dac_passthrough,
   output_device, alsa_plugin, pw_force_bitperfect, sample_rate,
   alsa_hardware_volume) saved to the per-user store but never called
   reload_settings() on the CoreBridge player. The Player kept using
   stale settings (often defaults with backend_type=None), falling back
   to CPAL/PipeWire instead of ALSA Direct.

2. After login, per-user audio settings were loaded into AudioSettingsState
   but never pushed into the CoreBridge player, which was created at
   startup with flat-path defaults.

Fix: Add sync_audio_settings_to_player() helper and call it from all
critical audio setters. Also sync settings in activate_session() and
activate_offline_session() right after audio_settings.init_at().

cc
2026-03-18 22:03:23 -06:00
vicrodh bf91822b4d feat: add QConnect dev toggle and device name settings
Developer Mode: toggle to show/hide QConnect dev panel button in
the player bar (hidden by default, persisted in localStorage).

Integrations: editable device name input for Qobuz Connect with
default "Qbz - {hostname}". Backend commands v2_qconnect_get/set
_device_name and v2_get_hostname. Custom name is used during the
WS bootstrap handshake.

cc
2026-03-18 21:31:11 -06:00
vicrodh 2953b86062 feat: add QconnectBadge as full-height badge in song card
Place QConnect badge as direct child of badges-group with 3px gap
matching DAC/EXC spacing. Two-row vertical layout: icon on top,
Qobuz/Connect text below. Wire props from +page.svelte to both
NowPlayingBar instances. Add qconnect i18n keys to de/fr/pt locales.

cc
2026-03-18 21:03:56 -06:00
vicrodh d4fa6f4210 feat(qconnect): add debug-level diagnostics for shuffle order tracking
Logs QueueUpdated shuffle_order validity and resolve_core_shuffle_order
results at debug level for verbose troubleshooting.

cc
2026-03-16 19:16:10 -06:00
vicrodh 8330dd08ed Revert "Reapply "fix(qconnect): pass server shuffle order through to CoreBridge""
This reverts commit 2bb72455d1.
2026-03-16 11:05:12 -06:00
vicrodh 2bb72455d1 Reapply "fix(qconnect): pass server shuffle order through to CoreBridge"
This reverts commit d9f112390f.
2026-03-16 11:01:53 -06:00
vicrodh d9f112390f Revert "fix(qconnect): pass server shuffle order through to CoreBridge"
This reverts commit 065cf734d2.
2026-03-16 10:54:02 -06:00
vicrodh 065cf734d2 fix(qconnect): pass server shuffle order through to CoreBridge
resolve_core_shuffle_order was reordering the server's shuffle_order
to put the current track first. This destroyed the server's concept
of "already played" vs "upcoming" — CoreBridge showed all tracks as
upcoming while the web player correctly split them.

Now the server's raw shuffle_order is passed through unchanged.
CoreBridge's set_queue_with_order finds shuffle_position from
current_index, preserving the played/upcoming split.

cc
2026-03-16 10:52:41 -06:00
vicrodh b4ebad20c5 Revert "fix(qconnect): suppress local playback automation when connected"
This reverts commit b5ab4cd148.
2026-03-15 23:23:57 -06:00
vicrodh 9c14a76975 fix: make mupdf optional to unblock build on Arch Linux
mupdf-sys 0.5.0 bindings are incompatible with the current system
MuPDF library (struct size assertions overflow). Gate mupdf behind
a booklet feature flag (disabled by default). When disabled, booklet
commands return an error instead of crashing the build.

cc
2026-03-15 13:48:35 -06:00
vicrodh a3cde0449d feat: wire autoplay mode sync to QConnect
- setAutoplayMode now sends autoplay preference to QConnect server
- queue_load_tracks syncs local autoplay mode after loading
- QConnect panel shows autoplay_mode status
2026-03-15 10:32:59 -06:00
vicrodh 87be5fbf16 feat: add set_autoplay_mode and autoplay_load_tracks remote commands
Backend _if_remote methods for QConnect autoplay protocol:
- set_autoplay_mode_if_remote: toggles autoplay on the QConnect server
- autoplay_load_tracks_if_remote: loads track IDs into the autoplay pool
Both follow the established _if_remote pattern with diagnostics.
2026-03-15 09:46:01 -06:00
vicrodh 8e2bb021c2 fix: guard re-bootstrap with has_disconnected flag
Prevents double-bootstrap on initial connect. The Subscribed
handler now only calls bootstrap_remote_presence when the
connection was previously lost (has_disconnected=true), not
on the first connect where connect() already bootstraps.
Also sets has_disconnected=true in the Disconnected handler.
2026-03-15 09:21:41 -06:00
vicrodh 8e87b414db feat(qconnect): route volume/mute to peer renderer when remote is active
New _if_remote backend commands for set_volume and mute_volume.
handleVolumeChange and handleToggleMute in both main view and miniplayer
now check for peer renderer before setting volume locally.  Media
control set_volume also routed through the guard.  When no peer is
active, volume is set locally and reported to QConnect server.
2026-03-15 09:14:07 -06:00
vicrodh 1fc2e79391 feat(qconnect): add stop_if_remote guard for media control stop action
When a peer renderer is active, the MPRIS/media-key stop command now
sends SetPlayerState(STOPPED) to the QConnect server instead of stopping
local playback.  Falls back to local stop on error or when no peer is
active.
2026-03-15 07:27:32 -06:00
vicrodh e5155f2baa refactor: homologate seek to preview+commit in ImmersivePlayer and Miniplayer
Replace immediate seek-on-drag with the NowPlayingBar pattern:
preview the position visually during drag, commit the seek only on
mouseup.  Both PlayerControlsCompact and MiniPlayerFooter now use
dragPreviewTime/effectiveTime, matching NowPlayingBar behavior.
2026-03-14 23:22:09 -06:00
vicrodh 6bb416f854 refactor: centralize mute in ImmersivePlayer and Miniplayer via playerStore.toggleMute
Replace local isMuted/previousVolume state in PlayerControlsCompact and
MiniPlayerFooter with the global toggleMute from playerStore.  Muted
state is now derived from volume === 0, matching NowPlayingBar behavior.
Pre-mute volume is persisted in localStorage by the store.
2026-03-14 22:53:43 -06:00
vicrodh d21f2253bc feat(qconnect): add QConnect guards to shuffle, repeat, queue play, and history play handlers
When a peer renderer is active, shuffle/repeat/queue-play/history-play
commands are now routed to the QConnect server instead of being applied
locally.  Two new backend commands (toggle_shuffle_if_remote,
cycle_repeat_if_remote) follow the existing _if_remote pattern.
Guards added to both +page.svelte (main + immersive) and miniplayer.
2026-03-14 22:44:31 -06:00
vicrodh b5ab4cd148 fix(qconnect): suppress local playback automation when connected
When QBZ is the local renderer in a QConnect session and the web player
presses next, the server sends SET_STATE with the new track. The backend
correctly starts playback, but the frontend's gapless transition handler
saw the track_id change, treated it as a local gapless event, and called
nextTrack() — which advanced the CoreBridge queue every 1s polling cycle,
flooding the queue and destroying the shuffle order.

Root cause: shouldQconnectSuppressLocalPlaybackAutomation only suppressed
when a peer renderer was active (activeRendererId !== localRendererId).
When QBZ IS the renderer, remoteControlMode stayed false, so all local
automation (gapless, auto-advance, resume-from-stop) remained active and
fought the server.

Fix: suppress local playback automation whenever QConnect is connected,
regardless of which renderer is active. The server is the authority for
track transitions in all QConnect scenarios.

cc
2026-03-14 22:09:50 -06:00
vicrodh 3b3dbc7ce1 fix(qconnect): trigger resync on reorder and remove server events
SrvrCtrlQueueTracksReordered and SrvrCtrlQueueTracksRemoved were not
triggering AskForQueueState, so the local queue never reflected
reorder/remove actions performed via QConnect. Same pattern as the
existing shuffle resync.

cc
2026-03-14 21:34:03 -06:00
vicrodh 66b5288150 fix(qconnect): use authoritative queue mutations
cx
2026-03-14 19:29:11 -06:00
vicrodh 311b7269ee fix(qconnect): keep gapless enabled for local renderer
cx
2026-03-14 17:57:46 -06:00
vicrodh 52e3653dc3 fix(qconnect): complete repeat pending on loop ack
cx
2026-03-14 13:25:22 -06:00
vicrodh 8a468ed725 fix(qconnect): restore local repeat renderer flow
cx
2026-03-14 13:09:23 -06:00
vicrodh f47f691063 fix(qconnect): allow rapid repeat toggles
cx
2026-03-14 12:07:11 -06:00
vicrodh e6a20b02e8 fix(qconnect): align repeat loop mode mapping
cx
2026-03-14 11:43:31 -06:00
vicrodh 0529f355e8 fix(qconnect): reanchor repeat state before toggles
cx
2026-03-14 07:55:42 -06:00
vicrodh 7a11b6548c fix(qconnect): anchor remote shuffle projection
cx
2026-03-14 07:32:46 -06:00
vicrodh 8b950da5c9 fix(qconnect): honor remote shuffle order for next
cx
2026-03-14 00:59:51 -06:00
vicrodh f693d1c61a fix(qconnect): wait for authoritative repeat mode
cx
2026-03-14 00:12:47 -06:00
vicrodh 6805b4f61a fix(qconnect): reapply authoritative queue snapshots
cx
2026-03-13 23:22:25 -06:00
vicrodh a59daf43dc fix(qconnect): resync queue after shuffle mode changes
cx
2026-03-13 23:13:16 -06:00
vicrodh cf2447f4b2 fix(qconnect): stop deriving remote shuffle locally
cx
2026-03-13 23:07:22 -06:00
vicrodh af7652c86b fix(qconnect): wait for authoritative shuffle queue
cx
2026-03-13 22:54:27 -06:00
vicrodh 19b4182dd6 fix(qconnect): render connect queue in remote shuffle order
cx
2026-03-13 22:45:56 -06:00
vicrodh 4312bf0524 fix(qconnect): refresh queue store from backend events
cx
2026-03-13 20:51:44 -06:00
vicrodh 1fc7a0a1a3 fix(qconnect): materialize remote shuffle atomically
cx
2026-03-13 20:43:56 -06:00
vicrodh b28fd39737 fix(qconnect): trust remote shuffle order
cx
2026-03-13 20:35:56 -06:00
vicrodh 09c6d8f1f8 fix(qconnect): route shuffle and repeat through runtime
cx
2026-03-13 20:28:11 -06:00
vicrodh 2efa1b0809 fix(qconnect): route clear queue through runtime
cx
2026-03-13 20:00:57 -06:00
vicrodh f72a4db7be chore(qconnect): checkpoint stable handoff state
cx
2026-03-13 19:52:12 -06:00
vicrodh d3d0fe491a fix(qconnect): stream local handoff playback
cx
2026-03-13 18:52:30 -06:00
vicrodh f18bd10aa4 fix(qconnect): complete active-renderer pending on session ack
cx
2026-03-13 18:50:04 -06:00
vicrodh 0a9064aa24 fix(qconnect): keep local renderer queue ids in interval reports
cx
2026-03-13 17:38:52 -06:00
vicrodh de29f1e2e0 chore(qconnect): checkpoint current poc state
cx
2026-03-13 17:28:05 -06:00
vicrodh 6d20360dc3 fix(qconnect): prefer session cursor for active renderer snapshot
cx
2026-03-13 15:07:50 -06:00
vicrodh fcadb85e65 fix(qconnect): unblock rapid transport controls and stop local audio on handoff
cx
2026-03-13 13:19:53 -06:00
vicrodh 017aa9a6b1 fix(qconnect): sync remote renderer into qbz ui
cx
2026-03-13 12:51:33 -06:00
vicrodh 65a0d523bb fix(qconnect): track active remote renderer state for skip and seekbar
cx
2026-03-13 11:55:48 -06:00
vicrodh 4098c18d89 fix(qconnect): clear pending active-renderer action on ack
cx
2026-03-13 11:08:28 -06:00
vicrodh 0da98aab8c fix(qconnect): hand off queue-start playback to remote renderer
cx
2026-03-13 10:45:37 -06:00
vicrodh 145974c292 fix(qconnect): harden renderer handoff identity
cx
2026-03-13 10:20:54 -06:00
vicrodh 3c688f129e fix(qconnect): backendize remote skip handoff
cx
2026-03-13 09:59:19 -06:00
vicrodh 4629e776f1 fix(qconnect): hand off play toggle to active renderer
cx
2026-03-12 23:49:33 -06:00
vicrodh c83aedac75 fix(qconnect): align renderer state with official queue semantics
cx
2026-03-12 23:10:31 -06:00
vicrodh e748ab6616 fix(qconnect): treat omitted cloud queue ids as zero
cx
2026-03-12 20:56:56 -06:00
vicrodh 6cdacd0c29 fix(qconnect): report queue drift after inserts
cx
2026-03-12 20:33:42 -06:00
vicrodh a46c5a016f fix(qconnect): anchor play next to local current track
cx
2026-03-12 20:17:02 -06:00
vicrodh 1d28730456 fix(qconnect): report resolved queue ids on track transitions
cx
2026-03-12 20:06:20 -06:00
vicrodh a2c4017607 fix(qconnect): treat remote queue tracks as qobuz playback
cx
2026-03-12 19:48:17 -06:00
vicrodh d3c2d4c6ca fix(qconnect): align queue load payload and inbound diagnostics
cx
2026-03-12 19:31:27 -06:00
vicrodh 2d643a9bfa fix(qconnect): suppress stale renderer drift during queue loads
cx
2026-03-12 17:45:09 -06:00
vicrodh bb110f3654 fix(qconnect): unify queue replacement through shared helper
cx
2026-03-12 17:36:18 -06:00
vicrodh 674fff996b refactor(qconnect): extract runtime helpers from page\n\ncx 2026-03-12 17:21:50 -06:00
vicrodh c026fce2ca fix(qconnect): avoid stale local report drift
cx
2026-03-12 17:08:18 -06:00
vicrodh 5cb4574e37 fix(qconnect): instrument runtime and anchor play next
cx
2026-03-12 15:54:52 -06:00
vicrodh 441b74ac91 test(qconnect): lock seekbar workaround behavior
cx
2026-03-12 15:32:49 -06:00
vicrodh 02155f54fe merge(pre-release): sync qconnect-poc-v3 with latest local integration state
cx
2026-03-12 15:23:32 -06:00
vicrodh 104e38e630 fix(nav): Discover menu items now navigate to correct home tab
navigateTo() was passing the tab id to the navigation store but never
setting the local homeTab state that HomeView reads. Editor's Picks
and For You always landed on Tab-Home.
2026-03-12 15:06:57 -06:00
vicrodh ee0f8034e1 chore: add test-results to gitignore 2026-03-12 14:16:59 -06:00
vicrodh d0a39770e3 fix(linebed): rebalance spectral terrain mapping
cx
2026-03-12 14:08:05 -06:00
vicrodh 72ce0946d9 fix(spectral-ribbon): update FFT label to 4096 in stream info header 2026-03-12 13:05:53 -06:00
vicrodh 12c6e2e47b fix(spectral-ribbon): update NUM_BANDS 190 → 512 to match backend
Backend now sends 512 spectral bands (from 4096-point FFT).
SpectralRibbon was still expecting 190, causing the length check
to fail and silently discard all spectral data.
2026-03-12 13:03:15 -06:00
vicrodh 4268e6a39f feat(linebed): add FPS control to settings, default 60fps
Add linebed to the per-panel FPS settings dropdown in Settings.
Add per-panel default override system to fpsConfig so linebed
defaults to 60fps while other panels stay at 15fps.
Translations already existed in all 5 locale files.
2026-03-12 12:59:24 -06:00
vicrodh 93b7e4ab7f fix(linebed): wider trapezoid base, narrower top
Back width 48% → 32%, front width 72% → 88%. More pronounced
trapezoid shape with wider bottom area as requested.
2026-03-12 12:56:12 -06:00
vicrodh 2432c606b0 fix(linebed): tall rectangle perspective, 200 lines top to bottom
Terrain extends vertically from 12% to 88% of canvas height (76%
spread). Width contained: back 48%, front 72%. 200 lines fill
the tall rectangle with visible spacing between them.
2026-03-12 12:54:47 -06:00
vicrodh 06e868db5d fix(linebed): high viewpoint, crop treble, shorter peaks
Three issues from user feedback:

1. Right side empty: 512 log bands span 20Hz-20kHz but musicvid only
   goes to ~1752Hz. Now display first 350 bands (~3.5kHz), cutting
   the dead treble zone that wasted 60% of visual width.

2. Viewpoint too low: terrain vertical spread reduced to 33% of height
   (was 54%), centered on canvas. This simulates a much higher camera
   looking down at the table rather than crouching at table level.

3. Mountains too tall: maxAmplitude 0.35 → 0.18. Peaks should be
   modest bumps on the terrain surface, not towering spikes.

Also reduced to 150 lines for more visible spacing between them.
2026-03-12 12:50:17 -06:00
vicrodh 20c2abadcf fix(linebed): table perspective, visible back edge, distributed peaks
User feedback with annotated screenshot identified three issues:

1. Perspective (red): Was infinite conveyor belt (back 20%, front 105%).
   Now table shape: back 50%, front 82%, contained within canvas.

2. Peak distribution (green): Exponential 6/3 with per-frame normalization
   crushed everything except loudest bin. Reduced to 3.5/1.8 so moderate
   peaks stay visible across the full spectrum width.

3. Back edge (blue): Opacity faded to 0.06 at back, hiding where lines
   begin. Now 0.45 minimum — all lines clearly visible including back edge.

Also: thinner uniform lines, moderate amplitude (0.35 vs 0.55).
2026-03-12 12:42:47 -06:00
vicrodh 31311eec25 feat(linebed): 200 lines at 60fps for 3D mountain depth
The 3D mountain effect in musicvid comes from many temporally-close
lines stacking in perspective. At 15fps with 120 lines, adjacent lines
differed too much — no "body" to the mountains.

- NUM_LINES 120 → 200 (match musicvid)
- Remove frame throttle, render at full rAF rate (~60fps)
  so adjacent lines are ~17ms apart, creating solid ridges
- Perspective: backY 0.25, frontY 0.98, depth power 1.8
  packs back lines tightly (2-3px apart) for solid terrain surface
2026-03-12 12:34:16 -06:00
vicrodh d4fa3cd28f feat(linebed): 4096-point FFT, 512 bands, linear scaling
Backend:
- FFT size 1024 → 4096 (10.7 Hz bin resolution vs 43 Hz before)
- Spectral bands 190 → 512 (closer to musicvid's 1024)
- Remove pow(0.55) compression, use linear scaling (rms * 25)
  so frontend exponential transform has full dynamic range
- Reduce temporal smoothing 0.80 → 0.30 for responsive peaks

Frontend:
- Remove power-law redistribution (backend bands already log-scaled,
  was applying double perceptual weighting causing uniform plateaus)
- Restore 9-point 3-pass smoothing (now proportional: 9/512 = 1.76%
  vs musicvid's 9/1024 = 0.88%)
- NUM_BANDS matches new backend count (512)
2026-03-12 12:27:40 -06:00
vicrodh f7521e7fd1 fix(linebed): scale smoothing and power-law for 190-bin resolution
musicvid.org uses 1024 visual bins from 647 FFT bins — 5.4x our
resolution. The 9-point 3-pass smoothing spread each peak to ~25 bins
(13% of our width), creating fat mountains. And power-law 2.5 stretched
33 bass bins to 50% width, creating uniform plateaus the exponential
can't sharpen.

Fix: smoothing 5-point 2-pass (peaks spread ~9 bins = 4.7% width),
power-law 1.5 (67 bass bins to 50% width, more frequency diversity).
2026-03-12 12:21:39 -06:00
vicrodh a1a67160c8 feat(linebed): match musicvid.org spectrum pipeline and perspective
Complete rewrite of spectrum processing to match musicvid.org settings:
- Variable exponential transform (exponents 3-6 across spectrum) replaces
  flat 0.8 exponent. This crushes low values and preserves only peaks,
  creating sharp spikes instead of fat mountains.
- Normalize spectrum to [0,1] before exponential (required for correctness)
- Head margin transform (7 bins, weight 0.7, decay 1.6) replaces edge taper
- Temporal smoothing matched to 0.03 (was 0.05)
- Steeper perspective angle: backY 0.12, depth power 1.35 (was 1.7),
  maxAmplitude 0.55 (was 0.45) for more top-down viewing
2026-03-12 12:15:28 -06:00
vicrodh 2b7763c4b6 feat(linebed): add power-law frequency redistribution and edge tapering
Implement transformToVisualBins() with power 2.5 exponent from
musicvid.org to spread bass energy across full visual width instead
of clustering in the first ~30 bins. Add edge tapering (10 bands)
to eliminate staircase/square artifact at spectrum boundaries.
2026-03-12 12:03:09 -06:00
vicrodh 817c9363c4 fix(linebed): centered perspective, 120 lines, responsive peaks
Based on frame analysis of musicvid.org's actual LineBed rendering:
- Revert isometric angle to centered symmetric trapezoid perspective
  (vanishing point at center-top, NOT upper-right offset)
- Increase to 120 lines for denser terrain surface
- Lower temporal smoothing from 0.35 to 0.05 (musicvid uses 0.03)
  for much more responsive, detailed peaks
- Push snapshots every frame instead of every 2nd for fluid scroll
- Wider front lines (110% canvas width) for edge-to-edge coverage
- Higher amplitude (0.45 * height) for dramatic mountain peaks
2026-03-12 11:48:39 -06:00
vicrodh e68425c45a feat(linebed): isometric perspective matching musicvid.org camera angle
Replace centered symmetric perspective with angled isometric view:
- Vanishing point at upper-right (75%, 8%) matching camera(-80,150,-10)
- Lines converge from lower-left toward upper-right vanishing point
- Front line extends beyond left edge for full-width coverage
- Non-linear depth curve (pow 1.8) for realistic compression
- Eliminates the "square" artifact from symmetric convergence
2026-03-12 11:41:09 -06:00
vicrodh 1380ca93d8 fix(linebed): reverse data flow direction to match musicvid.org
New spectrum data now appears at the BACK (top/horizon) and scrolls
toward the FRONT (bottom/viewer), creating the "audio flowing from
far away toward you" effect. Previously data flowed bottom-to-top
which looked like the terrain was moving away from the viewer.

Also slightly increased amplitude scaling for more dramatic peaks.
2026-03-12 11:36:59 -06:00
vicrodh 8cc04c2730 improve linebed visualizer: denser terrain, full spectrum, musicvid.org processing
- Increase to 80 lines (from 35) for denser terrain appearance
- Use all 190 spectral bands instead of subsampling every 2nd
- Add 3-pass 9-point moving average smoothing (musicvid.org algorithm)
- Add exponential transform (pow 0.8) for peak emphasis
- Non-linear perspective compression (pow 1.6) for depth effect
- Widen canvas coverage (95% front, 18% back)
- Increase amplitude scaling (0.4x bed height)
- Extract buildSpectrumPath() to eliminate duplicated path code
2026-03-12 11:17:43 -06:00
vicrodh 22f49eab30 feat(immersive): add Linebed visualizer panel
3D terrain-style frequency visualization using stacked spectrum lines
with perspective. Uses viz:spectral (190 bands) for high-resolution
terrain, with quadratic curve smoothing and black occlusion fill for
depth. Track info displayed bottom-right matching other panels.
2026-03-12 10:56:50 -06:00
vicrodh 509a65572e fix(artwork): proxy track row and album header images through backend cache (#163)
TrackRow, AlbumDetailView header, and ImageLightbox were loading artwork
directly via WebKitGTK HTTPS, which fails on systems with TLS database
issues. Switch all three to use:cachedSrc action to proxy through the
Rust backend image cache, matching the pattern used by other components.
2026-03-12 10:40:48 -06:00
vicrodh 6b1dbe8d0c fix(i18n): remove (Brasil) suffix from Portuguese locale display name 2026-03-12 10:31:59 -06:00
vicrodh c94dad9f33 fix(library): album names with Disc/CD suffix misidentified as disc folders (#147)
is_disc_folder() was matching any folder name containing "Disc N" or
"CDN" anywhere in the name. This caused albums like "100 Popular
Classics, Disc 1" or "Now 75 - CD1" under Various Artists to be treated
as disc subfolders, collapsing all tracks into a single album group.

Now is_disc_folder() only returns true when the folder name consists
exclusively of disc-related tokens (disc/cd/disk, digits, and modifiers
like "bonus"). Album names with extra words are correctly left as-is.

Users must re-scan their library for the fix to take effect.
2026-03-12 10:30:58 -06:00
vicrodh 264298a949 ci: add dependabot config and PR redirect workflow
- dependabot.yml: target pre-release branch for npm and cargo updates
- redirect-pr-to-prerelease.yml: auto-redirect PRs to main → pre-release
  with a friendly comment explaining the workflow
2026-03-12 10:13:22 -06:00