Commit Graph

12 Commits

Author SHA1 Message Date
lentikr 184a67e519 feat(lyrics,mediactl): embed local lyrics and album art in MPRIS/NowPlaying
Read embedded lyrics (LRC or plain text) and cover art from local file
tags at play time. Lyrics are preferred over network fetch when present;
album art is cached by content hash under ~/.local/share/cliamp/album-art/
and published via mpris:artUrl (Linux) and MPNowPlayingInfoCenter (macOS).
2026-06-29 20:17:41 +08:00
Bjarne Øverli 5fcd5aaabe fix(ui): keep now playing stable when browsing playlists
Release / build (amd64, darwin, macos-latest) (push) Has been cancelled
Release / build (amd64, linux, ubuntu-latest) (push) Has been cancelled
Release / build (amd64, windows, windows-latest) (push) Has been cancelled
Release / build (arm64, darwin, macos-latest) (push) Has been cancelled
Release / build (arm64, linux, ubuntu-latest) (push) Has been cancelled
Release / release (push) Has been cancelled
Release / update-homebrew (push) Has been cancelled
2026-06-14 18:30:02 +02:00
Nicholas Zambetti d6cd99697c Improvements to album header visibility & control (#234)
Re-runs the cohesion heuristic as tracks are added or queued so headers adapt to playlist growth, while preserving the user's ctrl+h preference via a manual override flag. The Add path uses running counters so the cost stays O(k) per call instead of O(N) per Add.
2026-05-23 09:18:27 +02:00
Bjarne Øverli 2b0a904633 Stop recomputing album-header default on every track Add
The heuristic ran on every Add path (including each YTDL batch of 20
tracks), which scaled O(N^2) on incremental loads and silently
overrode the user's Ctrl+H toggle whenever a track was queued. Recompute
only on Replace and on first population; preserve the user's choice
otherwise.

Also dedupe the " · Album" suffix logic into a small helper, and pull
the cohesion ratio into a named constant.
2026-05-06 19:46:20 +02:00
fad647834d Sticky album headers with auto-toggle heuristic and manual toggle keybind (#208)
* Implement sticky album headers and hide redundant album suffixes

* Add blank album separator when album ends and next track lacks album info

* Add keybind 'H' to toggle album headers

* Centralize album grouping and header logic

* Change 'toggle headers' keybind from H to ctrl+h

* Use modern Go iterators for playlist rendering and album grouping

* Support ctrl+h to toggle album headers in playlist manager and provider browser

* Add cohesion heuristic to auto-toggle album headers
2026-05-06 19:38:56 +02:00
Bjarne Øverli b616440ae5 Fix playlist sort on bulk add and add x to remove from playlist (#210)
- File browser bulk-add now emits selected paths in directory listing
  order instead of map iteration order, so albums play in track order.
- Add Playlist.Remove plus `x` keybinding to drop the highlighted track
  from the casual playlist without restarting cliamp.
2026-05-06 19:32:19 +02:00
Andrew Carter 7a68eee191 Spotify: stop rapid skipping from triggering browser re-auth (#212)
Skipping tracks aggressively in a Spotify playlist would frequently pop
open a browser to re-authenticate, even though the librespot session was
still valid. Two issues caused this, plus a contention bug that made the
first easier to hit:

1. isAuthError treated any context.DeadlineExceeded as an auth failure.
   When skipping fast, the per-stream 30s context (or a wrapped
   DeadlineExceeded surfaced from librespot's chunk fetch when it was
   interrupted) was misclassified, kicking off the reconnect path.
   Reclassify: deadline/cancellation are NOT auth signals; only
   KeyProviderError is.

2. NewStreamer's reconnect path ended in ReconnectInteractive on the
   second failure, which always opens a browser. Replace the interactive
   fallback with returning playlist.ErrNeedsAuth so the UI can surface a
   sign-in prompt rather than yanking the user into a browser tab
   mid-skip. Silent reconnect from cached creds is still attempted once.

3. Session.NewStream held s.mu across the librespot network call, so
   concurrent NewStream / webApi calls serialized and were more likely
   to hit the 30s timeout under rapid skipping. Snapshot s.player under
   the lock and call NewStream lock-free.

Tests:
- New TestIsAuthError covers nil, plain errors, deadline/cancellation
  (wrapped + bare), and KeyProviderError (wrapped + bare).

Docs:
- docs/spotify.md notes the new behavior: rapid skipping never opens a
  browser; sign-in prompts surface in the UI instead.



spotify: stop rapid skipping from triggering browser re-auth

Skipping tracks aggressively in a Spotify playlist would frequently pop
open a browser to re-authenticate, even though the librespot session was
still valid. Two issues caused this, plus a contention bug that made the
first easier to hit:

1. isAuthError treated any context.DeadlineExceeded as an auth failure.
   When skipping fast, the per-stream 30s context (or a wrapped
   DeadlineExceeded surfaced from librespot's chunk fetch when it was
   interrupted) was misclassified, kicking off the reconnect path.
   Reclassify: deadline/cancellation are NOT auth signals; only
   KeyProviderError is.

2. NewStreamer's reconnect path ended in ReconnectInteractive on the
   second failure, which always opens a browser. Replace the interactive
   fallback with returning playlist.ErrNeedsAuth so the UI can surface a
   sign-in prompt rather than yanking the user into a browser tab
   mid-skip. Silent reconnect from cached creds is still attempted once.

3. Session.NewStream held s.mu across the librespot network call, so
   concurrent NewStream / webApi calls serialized and were more likely
   to hit the 30s timeout under rapid skipping. Snapshot s.player under
   the lock and call NewStream lock-free.

Tests:
- New TestIsAuthError covers nil, plain errors, deadline/cancellation
  (wrapped + bare), and KeyProviderError (wrapped + bare).

Docs:
- docs/spotify.md notes the new behavior: rapid skipping never opens a
  browser; sign-in prompts surface in the UI instead.



spotify: stop rapid skipping from triggering browser re-auth

Skipping tracks aggressively in a Spotify playlist would frequently pop
open a browser to re-authenticate, even though the librespot session was
still valid. Two issues caused this, plus a contention bug that made the
first easier to hit:

1. isAuthError treated any context.DeadlineExceeded as an auth failure.
   When skipping fast, the per-stream 30s context (or a wrapped
   DeadlineExceeded surfaced from librespot's chunk fetch when it was
   interrupted) was misclassified, kicking off the reconnect path.
   Reclassify: deadline/cancellation are NOT auth signals; only
   KeyProviderError is.

2. NewStreamer's reconnect path ended in ReconnectInteractive on the
   second failure, which always opens a browser. Replace the interactive
   fallback with returning playlist.ErrNeedsAuth so the UI can surface a
   sign-in prompt rather than yanking the user into a browser tab
   mid-skip. Silent reconnect from cached creds is still attempted once.

3. Session.NewStream held s.mu across the librespot network call, so
   concurrent NewStream / webApi calls serialized and were more likely
   to hit the 30s timeout under rapid skipping. Snapshot s.player under
   the lock and call NewStream lock-free.

Tests:
- New TestIsAuthError covers nil, plain errors, deadline/cancellation
  (wrapped + bare), and KeyProviderError (wrapped + bare).

Docs:
- docs/spotify.md notes the new behavior: rapid skipping never opens a
  browser; sign-in prompts surface in the UI instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 08:25:03 +02:00
Bjarne Øverli e14d84c419 Add play/append/queue actions to Spotify and YouTube search results
Release / build (amd64, darwin, macos-latest) (push) Has been cancelled
Release / build (amd64, linux, ubuntu-latest) (push) Has been cancelled
Release / build (amd64, windows, windows-latest) (push) Has been cancelled
Release / build (arm64, darwin, macos-latest) (push) Has been cancelled
Release / build (arm64, linux, ubuntu-latest) (push) Has been cancelled
Release / release (push) Has been cancelled
Release / update-homebrew (push) Has been cancelled
Search results overlays now support Enter (play now), a (append), and
q (queue next) on the selected track, instead of only routing to a
Spotify cloud-playlist add. The cloud-playlist add moves to p.

YouTube/SoundCloud net search gains a results picker (ytsearch10 /
scsearch10) using the same keymap, replacing the previous one-shot
ytsearch1 auto-queue. Adds shared playTrackImmediate / appendTrack /
queueTrackNext helpers and closeNetSearch / closeSpotSearch helpers
that drop cached result slices on close.

playlist.IsYTSearch generalises the ytsearch:/ytsearchN:/scsearch:/
scsearchN: prefix check used by IsURL, IsYouTubeURL, and IsYTDL.
2026-05-01 21:15:40 +02:00
Gjermund Garaba bf77ca203e fix: skip unavailable Spotify tracks during playback (#173)
* fix(playlist): skip unavailable Spotify tracks during playback

Mark Spotify tracks as unplayable when the API reports restrictions,
skip them in next/prev/activate playback flows, and surface their
state in the playlist UI.

Add coverage for playlist navigation and playback behavior around
unavailable tracks.

* fixes and whatnot
2026-04-13 18:02:54 +02:00
Gjermund Garaba b6deffe92b chore(deps): bump to charm.land v2 dependencies (#161)
Co-authored-by: bjarneo <bjarneo@users.noreply.github.com>
2026-04-02 15:49:51 +02:00
Bjarne Øverli 2029e39e52 Add feed track support for podcast RSS playlists
Release / build (amd64, darwin, macos-latest) (push) Has been cancelled
Release / build (amd64, linux, ubuntu-latest) (push) Has been cancelled
Release / build (amd64, windows, windows-latest) (push) Has been cancelled
Release / build (arm64, darwin, macos-latest) (push) Has been cancelled
Release / build (arm64, linux, ubuntu-latest) (push) Has been cancelled
Release / release (push) Has been cancelled
Release / update-homebrew (push) Has been cancelled
2026-03-30 21:33:06 +02:00
Bjarne Øverli 77cac44689 Split the model into smaller files 2026-03-30 16:52:25 +02:00