Commit Graph
8 Commits
Author SHA1 Message Date
dtourolle 0815445aa7 feat(library): exclude chosen folders from music browsing
Replaces a hardcoded filter that dropped anything named "Podcasts" from music
results — one user's library layout compiled into the shipped product, keyed on
an English literal, applied only at the six call sites someone had remembered.

Exclusion is now a user setting stored in Rust and applied at the repository
layer's convergence points, so scope is decided once and is the same on every
screen. It matches on folder id rather than name: a title is not what an item
is, which is why an album legitimately called "Podcasts" used to vanish.

Deliberately not filtered: get_item (an id asked for by name was navigated to on
purpose, and refusing it would break playback of anything inside a hidden
folder), get_downloaded_items (hiding a download would leave the user unable to
delete a file whose disk usage they can still see), and the offline cache (an
exclusion is a view preference and must be reversible without a re-crawl).

Also removes src/lib/utils/validation.ts — six exported validators with no
caller outside their own test file, which made the module read as covered
input validation while guarding nothing.

TRACES: UR-076 | DR-209 | UT-203
2026-08-20 20:09:57 +02:00
dtourolle ac3cd67164 feat(library): exclude chosen folders from music browsing
Replaces `src/lib/utils/podcastFilter.ts` — a shipped personal workaround
that dropped any item whose name, album, album artist or artist was
literally "Podcasts" — with a real user setting applied in Rust.

The old filter was wrong twice over: it hardcoded one user's folder
layout keyed on an English literal, and it put a domain rule (what a
query should return) in the presentation layer. It slipped past
`check:boundary` only because it matched on names rather than on an
item-type array.

- `repository::exclusions` owns the rule and the process-wide id set,
  the same shape as `online::STREAMING_QUALITY` so it survives a
  repository being rebuilt on re-login.
- `HybridRepository` applies it where the cache and server legs of every
  cache-first query converge (`parallel_race` / `race_with_refresh`),
  plus the bespoke `get_items` path and the server-only reads. Filtering
  before the "has content" check is what makes a cache page of nothing
  but hidden items fall through to the server.
- Exclusion is by stable item id, never by name, and matches an item's
  own id or any container link it carries (parent, album, library,
  series, season, artist).
- A direct `get_item` lookup and the Downloads surface are deliberately
  unfiltered: hiding those would break playback and file management of
  anything inside a hidden folder.
- `LibrarySettings` persists to `app_settings` and is restored in the
  setup hook, alongside the streaming-quality cap. Default is an empty
  list — nobody inherits the old "Podcasts" behaviour.
- New commands `library_get_settings`, `library_set_settings` and
  `library_get_exclusion_candidates`; the candidates read goes through
  `get_items_unfiltered` so an already-hidden folder still appears in the
  picker and the setting can be undone.
- Settings page gains a "Hidden Folders" section that renders the
  backend's candidate list and sends back ticked ids; it decides nothing.

TRACES: UR-076 | DR-209 | UT-203
2026-08-20 19:38:05 +02:00
dtourolle d54d8cc7c4 refactor(logging): route frontend console calls through the logger
TRACES: | DR-204

484 ungated `console.*` calls across 63 non-test frontend files shipped to
end users with no way to turn them off. Mechanical substitution, no control
flow, error handling or message semantics changed:

  console.log / console.debug -> log.debug
  console.info                -> log.info
  console.warn                -> log.warn
  console.error               -> log.error

Hand-written `"[Scope] …"` prefixes are dropped where the logger's scope
now carries them; scope names that already existed are preserved verbatim
(`[Auth]`, `[VideoPlayer]`, `[PiP]`, …) and inferred from the filename
where a file had none. `src/routes/player/[id]/+page.svelte` keeps its
`NextEpisode` and `AutoPlay` sub-scopes as separate loggers rather than
flattening them into the page scope.

`grep -rn 'console\.' src/` now matches nothing outside the tests and the
facade itself.
2026-08-20 19:29:59 +02:00
dtourolle 93d198ce21 domain: primaryImageTag -> imageId end-to-end (phase 4a/4b)
Rust: PlayerMediaItem and MergedMediaItem gain image_id (dual-carry),
populated from primary_image_tag at every construction/conversion site.
Regenerated bindings.

Frontend: all catalog + player + merged readers now use imageId. The
NowPlayingItem->MediaItem bridge (player.ts) properly maps the remote
session's Jellyfin fields (Type, runTimeTicks, primaryImageTag) onto the
neutral kind/durationMs/imageId. Types that are genuinely out of scope
(Person, NowPlayingItem, PlayItemRequest) keep primaryImageTag.

Rust 456, frontend 644, check clean.
2026-07-23 21:40:58 +02:00
dtourolle 75014ee00f Fix sleep bug, fix menu return
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m1s
Traceability Validation / Check Requirement Traces (push) Successful in 23s
Build & Release / Run Tests (push) Successful in 4m7s
🏗️ Build and Test JellyTau / Build Android APK (push) Successful in 19m5s
Build & Release / Build Linux (push) Successful in 16m20s
Build & Release / Build Android (push) Successful in 19m12s
Build & Release / Create Release (push) Successful in 8s
2026-07-01 23:49:51 +02:00
dtourolle 6836ce79c8 fix(Remote playback): kludge to scrub after stream move
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 3m51s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 20s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Successful in 18m18s
2026-06-25 21:31:39 +02:00
dtourolle 1836615dc0 feat(library): genre sliders, artist links, and navigation utils
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 3m49s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 19s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Successful in 18m24s
- music landing: diverse per-genre album sliders (online counts /
  offline wide-probe fallback) and home-screen library shortcuts
- add ArtistLinks component and shared navigation/genreDiversity utils
- player/playback-mode refinements across Rust and frontend
2026-06-25 19:18:06 +02:00
dtourolle 17a35573a0 feat(library): focused music/TV/movie landing screens + self-draining download queue
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 9m49s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 25s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Successful in 22m33s
Library screens:
- Add dedicated music, TV, and movie landing pages (hero banner +
  horizontal carousels) backed by new music/tv/movies stores.
- Route tvshows libraries to /library/tv; surface rediscover ("haven't
  listened to in a while") albums via a new repository method across
  online/offline/hybrid repos plus the repository_get_rediscover_albums
  command.
- Add an A-Z jump bar for long alphabetically-sorted lists, with grid
  index anchors in LibraryGrid/LibraryListView/TrackList.
- Filter the "Podcasts" folder out of music library queries.

Downloads:
- Add a backend queue pump: enqueue_download / enqueue_video_downloads
  persist the resolved stream URL + target dir on each row (migration
  017), and the pump starts up to max_concurrent and drains the rest
  automatically as slots free, instead of the frontend silently dropping
  items past the concurrency limit. Album/series/season buttons now
  enqueue rather than calling start_download directly.

Other fixes:
- Hybrid search now returns instant cache results and pushes the merged
  cache+server union via a request-id-tagged search-event, so superseded
  queries can't clobber fresher results.
- URL-encode SearchTerm / genres / item types in online repo requests.
- Android: pause on audio-becoming-noisy (headphone/BT disconnect).
2026-06-24 20:44:17 +02:00