Commit Graph
27 Commits
Author SHA1 Message Date
dtourolle d4e2cd120c feat(player): webview audio backend for platforms without a native one
Adds WebviewAudioBackend, used on non-Linux/non-Android targets (e.g.
Windows) where there is no libmpv/ExoPlayer. Instead of decoding, it
emits a WebviewAudioLoad event with the stream URL; a frontend <audio>
element (WebviewAudioAdapter + webviewAudio service) plays it and reports
state/position back through the existing player_report_* round-trip, so
the Rust PlayerController stays the single source of truth. Play/pause/
seek reach the element via the existing ControlCommand event.

All video already renders in the webview on every platform, so this
completes audio-only playback for Windows (video via WebView2, audio via
<audio>). Pure Rust + Tauri events, so it still cross-compiles from Linux.

Regenerates bindings.ts (adds webview_audio_load; also carries the
equalizer EQ bindings).

TRACES: UR-003, UR-004, UR-005 | DR-004
2026-07-24 23:49:23 +02:00
dtourolle 55fa26377a domain: introduce provider-neutral media model (phase 1)
Establish src-tauri/src/domain/ as the single source of truth for the
media model, with all Jellyfin translation isolated in from_jellyfin.rs.
Adds MediaKind enum and neutral duration_ms/image_id fields to MediaItem
as additive, defaulted dual-carry alongside the legacy Jellyfin-named
fields, so nothing breaks while the frontend migrates off them.

- domain/media.rs: canonical MediaKind (closed enum, replaces stringly
  item_type), Default = Other so unknown/defaulted items are inert.
- domain/from_jellyfin.rs: total, panic-free item_type -> MediaKind
  classification (all audited types + person subroles) and ticks->ms.
- MediaItem gains kind/duration_ms/image_id, populated at both mapping
  seams (online to_media_item, offline cached_item_to_media_item) and
  the synthesized-album/person sites.
- Regenerated bindings.ts: frontend now HAS the neutral model available.

Phase 1 of docs/specs/frontend-domain-model.md. No frontend behaviour
change yet; wire shape is a superset of before.

Rust 456 tests, frontend 644 tests, check + check:boundary all green.
2026-07-23 20:53:47 +02:00
dtourolle e083b53ee8 feat(downloads): WiFi-only network-type-aware download gating
Add a metered/cellular network detector so downloads honour a "WiFi
only" preference. Android reports network type via NetworkTypeMonitor;
Rust exposes it through download/network.rs and holds the queue pump when
on a metered connection, emitting a queue-wide waitingForNetwork event.
The frontend surfaces this via the networkType service and a
waitingForNetwork store flag.

TRACES: UR-053 | DR-074
2026-07-23 20:02:07 +02:00
dtourolle acf1bb200d fix resuming video playback after background audio only mode.
Traceability Validation / Check Requirement Traces (pull_request) Failing after 3h14m1s
🏗️ Build and Test JellyTau / Android Compile Check (pull_request) Has been cancelled
🏗️ Build and Test JellyTau / Run Tests (pull_request) Has been cancelled
2026-07-22 22:28:07 +02:00
dtourolle 3fbf6afdbc Background-audio handoff for video + repository/player refactor
Hand video playback off to a native audio-only stream when the app is
backgrounded or locked, with no on-device video decode (UR-040). Adds
player_enter/exit_background_audio commands, an audio-only stream URL
for video items across the repository layer, and the frontend handoff
state machine wired into VideoPlayer. Includes accompanying
repository/offline/player refactoring and regenerates the traceability
matrix.
2026-07-22 21:52:07 +02:00
dtourolle 2a1f1689b4 Layout and search fix
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 2m4s
🏗️ Build and Test JellyTau / Android Compile Check (push) Has been skipped
Traceability Validation / Check Requirement Traces (push) Successful in 23s
Build & Release / Run Tests (push) Failing after 2m45s
Build & Release / Build Linux (push) Has been skipped
Build & Release / Build Android (push) Has been skipped
Build & Release / Create Release (push) Has been skipped
2026-07-11 19:55:55 +02:00
dtourolle 36be192d44 offline mode fixes
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m39s
Traceability Validation / Check Requirement Traces (push) Successful in 24s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 5m11s
2026-07-07 16:22:12 +02:00
dtourolle acb7e5f221 fix offline mode and layout bugs
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m32s
Traceability Validation / Check Requirement Traces (push) Successful in 22s
Build & Release / Run Tests (push) Successful in 5m21s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m25s
Build & Release / Build Linux (push) Successful in 17m31s
Build & Release / Build Android (push) Successful in 22m5s
Build & Release / Create Release (push) Successful in 16s
2026-07-06 20:24:46 +02:00
dtourolle 1f6977cd01 Playback fix
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m28s
Traceability Validation / Check Requirement Traces (push) Successful in 22s
🏗️ Build and Test JellyTau / Build Android APK (push) Successful in 18m37s
Build & Release / Run Tests (push) Successful in 4m12s
Build & Release / Build Linux (push) Successful in 16m20s
Build & Release / Build Android (push) Successful in 18m57s
Build & Release / Create Release (push) Successful in 13s
2026-07-02 18:13:55 +02:00
dtourolleandClaude Opus 4.8 342f95cac1 Wire up playback reporting, fix duration flash, hide video from audio mini player
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m14s
Traceability Validation / Check Requirement Traces (push) Successful in 21s
🏗️ Build and Test JellyTau / Build Android APK (push) Successful in 19m3s
Playback reporting (position sync / resume-on-another-device):
- player_configure_jellyfin now builds a PlaybackReporter sharing the player
  controller's Arc, so Start/Progress/Stopped actually reach Jellyfin on every
  auth path (login/restore/reauth); previously they never did.
- The PlaybackReporterWrapper now shares the same Arc the controller and MPV
  progress loop report through, instead of a dead parallel Option.
- Android position callbacks now emit throttled progress reports (30s/item),
  mirroring the MPV backend.

Duration flash on pause:
- resolveDuration() prefers the live store duration for the already-loaded
  track over the runTimeTicks estimate, so pausing no longer clobbers the
  slider's max to 0 when runTimeTicks is missing.

Video leaking into audio mini player:
- isVideoItem() also checks the backend PlayerMediaItem mediaType
  discriminator, so a video started via player_play_item (no Jellyfin `type`,
  mediaType "video") no longer surfaces in the audio mini player.

Middle-truncation of long media names:
- New truncateMiddle util applied to track/episode/card/mini-player titles so
  distinguishing tails (episode numbers, suffixes) stay visible.

Adds regression tests for the duration and mini-player fixes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 21:52:27 +02:00
dtourolle 0eae81ec59 Add JRay support
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 17m59s
Traceability Validation / Check Requirement Traces (push) Successful in 1m48s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been cancelled
2026-06-28 20:38:58 +02:00
dtourolle 8eae4ae253 layout improvements
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 9m2s
Traceability Validation / Check Requirement Traces (push) Successful in 2m30s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been cancelled
2026-06-28 20:14:17 +02:00
dtourolleandClaude Opus 4.8 385d2270c9 fix(android): keep lockscreen/media controls in sync with playback
The lockscreen controls drifted out of sync, especially while casting, and
couldn't control remote playback. Two media sessions were competing (a Media3
MediaSession driving transport vs a MediaSessionCompat driving the notification),
position was only pushed on play/pause so the scrubber froze mid-track, and
remote mode showed stale local metadata with dead buttons.

- Make MediaSessionCompat the single source of truth; route all transport
  commands (both the Compat callback and the Media3 wrappedPlayer) through Rust
  via nativeOnMediaCommand instead of touching ExoPlayer directly.
- Push position on every 250ms tick via a lightweight updatePlaybackPosition,
  and report 0.0 playback speed when paused so Android stops extrapolating.
- Mirror the remote session's now-playing onto the lockscreen from the native
  session poller (works while the screen is locked, unlike WebView timers) via
  a new player::update_lockscreen_metadata JNI bridge.
- Make MediaSessionHandler mode-aware: in remote mode forward play/pause/next/
  prev/seek to the remote Jellyfin session; Stop while casting emits
  RemoteDisconnectRequested, which the frontend handles by transferring to local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 23:55:26 +02:00
dtourolle 7d7f27aa10 feat(library and playback): Support for serverside channel plugins and hls streaming 2026-06-27 17:25:57 +02:00
dtourolle f1d25c4f4d Add support for fusing/unfusing JellyLMS zones into synchronized
multi-room groups, addressed by MAC (derived from the `lms-{mac}` device id).
2026-06-26 19:27:37 +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
dtourolle d01c2aab9f Migrate all IPC call sites to typed tauri-specta commands.*
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 4m39s
Traceability Validation / Check Requirement Traces (pull_request) Failing after 36s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Failing after 1m57s
Replace the remaining ~155 untyped invoke() calls across stores, services,
components, and routes with the generated commands.* wrappers from
$lib/api/bindings, so every IPC call is compile-time-checked against the
command signatures.

- Register repository_get_subtitle_url and repository_get_video_download_url
  in specta_builder() and the invoke_handler; regenerate bindings.ts.
- Source duplicated wire types (AutoplaySettings, CacheConfig, Session,
  ConnectivityStatus, audio/video settings, etc.) from bindings.
- Fix two bugs surfaced by the typed wrappers:
  - VideoDownloadButton passed an un-awaited Promise as the stream URL.
  - setAutoplaySettings omitted the required userId argument.
- Update unit tests asserting the old invoke(name, args) shape.
- Remove the five param-naming guard tests; the compiler and codegen now
  enforce what they checked.

svelte-check: 0 errors. vitest: green. cargo test --lib: green.
2026-06-21 08:47:04 +02:00
dtourolle ea342d76e3 Fix download + casting breakages from the specta migration
Casting: restore camelCase serialization on SessionInfo/NowPlayingItem/PlayState
(container rename_all = "camelCase" + per-field PascalCase serde aliases so Jellyfin
PascalCase still deserializes). Bindings and the existing frontend are camelCase
again — casting works with no frontend session changes.

Downloads: migrate DownloadButton.svelte and downloads.ts to the typed
commands.downloadItemAndStart / downloadItem / downloadVideo wrappers (request
objects), matching the bundled backend signatures.

Tooling:
- Enable tauri-specta ErrorHandlingMode::Throw so commands.* return Promise<T>
  and throw (drop-in for invoke()).
- Disambiguate specta name collisions: player MediaItem/MediaSource ->
  PlayerMediaItem/PlayerMediaSource, auth ServerInfo -> AuthServerInfo.
- Regenerate bindings.ts; svelte-check passes (0 errors).
2026-06-20 19:27:29 +02:00
dtourolle dc1f516da7 Fix Android insta-crash: don't export TS bindings at runtime
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 2m18s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped
Traceability Validation / Check Requirement Traces (push) Failing after 18s
The tauri-specta `.export(...)` call ran inside run() on every debug app start
and tried to write `../src/lib/api/bindings.ts`, a path that doesn't exist on a
device — `.expect()` panicked at startup, crashing the app instantly on Android.
Bindings are generated by the `export_typescript_bindings` test instead.
2026-06-20 18:36:57 +02:00
dtourolle 731966246f Workstream E: generate bindings.ts via reusable specta_builder + export test
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 3m41s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped
Traceability Validation / Check Requirement Traces (push) Failing after 48s
- Extract specta_builder() so run() and a #[test] share one command list.
- Add export_typescript_bindings test that writes src/lib/api/bindings.ts.
- Configure the TS exporter bigint behavior to Number (matches existing frontend).
- Commit the generated bindings.ts (typed commands.* wrappers + all DTO types).
2026-06-20 18:25:54 +02:00
dtourolle ada3ed64ab Workstream E (backend): wire tauri-specta — annotate commands, derive Type, generate-ready Builder
- Add #[specta::specta] to all 201 #[tauri::command] functions.
- Derive specta::Type on all IPC DTOs (repository/types, settings, player/storage/
  download command DTOs, player enums, jellyfin SessionInfo/NowPlayingItem/PlayState,
  ThumbnailCacheStats, DownloadInfo, CacheConfig, etc.).
- Replace tauri::generate_handler! with a tauri_specta::Builder + collect_commands!
  in lib.rs (exports bindings.ts in debug builds).

Two contract changes required by specta constraints (frontend migration follows):
- specta caps command arity at 10 args: download_item_and_start / download_item /
  download_video now take a single request struct (params bundled, body unchanged
  via destructuring).
- specta can't parse split serde rename_all: SessionInfo/NowPlayingItem/PlayState
  switched to rename_all = "PascalCase" (Jellyfin deserialization preserved; these
  now serialize PascalCase to the frontend).

cargo check --lib is clean (0 errors). Frontend migration to bindings.ts is the next step.
2026-06-20 18:20:25 +02:00
dtourolle 642ec17069 Workstream C: convert commands/download to a folder module, extract pinning and smart-cache commands
🏗️ Build and Test JellyTau / Build Android APK (push) Has been cancelled
🏗️ Build and Test JellyTau / Run Tests (push) Has been cancelled
Traceability Validation / Check Requirement Traces (push) Has been cancelled
- Move commands/download.rs to commands/download/mod.rs.
- Extract pin/unpin/is_pinned into download/pinning.rs.
- Extract smart-cache stats/config + album recommendation commands into
  download/smart_cache.rs.
- Re-exported via pub use so command names stay at commands::download::*;
  invoke_handler unchanged, all tests pass.
2026-06-20 16:56:22 +02:00
dtourolle 6866f03c55 Architecture remediation A/B/F: poison-tolerant locks, graceful backend init, doc fixes
Workstream A — poison-tolerant locking:
- Add utils/lock.rs with MutexSafe/RwLockSafe extension traits that recover a
  poisoned std::sync lock instead of panicking, plus unit tests.
- Replace all 153 .lock().unwrap() and 4 .read()/.write().unwrap() production
  sites with _safe variants across 14 files, eliminating the player
  crash-cascade class. Tokio async mutexes are unchanged.

Workstream B — graceful backend init:
- create_player_backend no longer panics when MPV/ExoPlayer fail to initialize;
  it falls back to NullBackend and emits a backend-init-failed event so the UI
  can show "playback unavailable" instead of the app crashing. Fatal DB-setup
  panics are kept.

Workstream F — doc reconciliation:
- Rewrite software-architecture.md's inaccurate "thin UI / ~800 lines" claims to
  reflect reality (~20.5k non-test frontend) and document the events+polling
  hybrid plus the new locking/backend-init behavior.
2026-06-20 16:03:54 +02:00
dtourolle 09780103a7 Split software arch desc for easier manintenance. Many fixes related to next video playing and remote playback
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 12s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped
Traceability Validation / Check Requirement Traces (push) Failing after 1s
2026-03-01 19:47:46 +01:00
dtourolle e3797f32ca many changes
Traceability Validation / Check Requirement Traces (push) Failing after 1m18s
🏗️ Build and Test JellyTau / Build APK and Run Tests (push) Has been cancelled
2026-02-14 00:09:47 +01:00
dtourolle cfddc1edea First working POC 2026-01-26 22:21:54 +01:00