DR-235 phase 3. Every video renderer is native now: mpv on Linux and Windows, ExoPlayer on Android, all drawing behind the transparent webview. The HTML5 <video> path is gone, not bypassed: - Frontend: hls.js, Html5PlayerAdapter and its compatibility shim, the createAdapter factory, streamTransport, hlsRecovery, timeTracking, videoFit, the <video>/<track> markup and every element handler in VideoPlayer (3277 -> 2144 lines), the experimentalNativeVideo store and its Settings toggle, webviewVideoFallback/supportsNativeVideo, and the setHtml5VideoState PiP bridge call. NativePlayerAdapter is the one video adapter; webview audio gets its own adapter kind. - Rust: use_html5 dropped from player_seek_video, player_switch_audio_track and player_set_stream_quality with the Html5* strategies and ReloadStream responses; use_html5_element and VideoBackend dropped from PlayerStatus; player_play_item always loads the backend (set_current_item removed); Capabilities::webview removed; the WebKitGTK GStreamer/VAAPI setup (and its gst-inspect spawn) removed. - Android: the HTML5 video state in PictureInPictureManager and ScreenWakeManager, and the bridge method feeding it. - CSP: connect-src loses http:/https: and worker-src loses blob: - both existed for hls.js; with it gone they were only an exfiltration channel and a blob worker for injected script. A test now keeps them out. mpv takes over what the <video> element did (mpv_tracks, UT-275): subtitles are the WebVTT list the play request carries, queued on sub-files and selected by position in that list, starting off; audio tracks are selected by position in the file; sid/aid are reset before each load. Without this, Linux video had no subtitle selection and a direct-play audio switch failed since mpv became its renderer. Verified: Rust 948 passing, and the same 948 cross-compiled for Windows under wine against the shipped DLL (track tests included); frontend 1111 passing; aarch64 debug APK builds. Lint warnings 158 -> 146, CI ratchet tightened to match. Not yet seen on Windows hardware.
222 lines
14 KiB
Markdown
222 lines
14 KiB
Markdown
# JellyTau Software Architecture
|
|
|
|
This document describes the current architecture of JellyTau, a cross-platform Jellyfin client built with Tauri, SvelteKit, and Rust.
|
|
|
|
**Last Updated:** 2026-06-20
|
|
|
|
## Architecture Overview
|
|
|
|
JellyTau uses a client-server architecture: business logic lives in a comprehensive Rust backend, while a UI-rich Svelte frontend handles presentation and interaction.
|
|
|
|
### Architecture Principles
|
|
|
|
- **Business Logic in Rust**: Core logic — playback, repository, sync, downloads, connectivity — lives in Rust for performance, reliability, and type safety.
|
|
- **Presentation in Svelte**: The frontend (~20.5k non-test lines) owns UI, layout, navigation, and interaction state and invokes Rust commands. It is intentionally UI-heavy, **not** a thin wrapper. Largest pieces: components + routes (~14.6k lines), stores (~3.4k), api/services/utils (~2.4k); `VideoPlayer.svelte` alone is ~1.6k lines.
|
|
- **Events + Polling hybrid**: Rust emits events the frontend listens to, and the UI also polls status on short intervals in a few hot spots (e.g. queue status in `library/+layout.svelte`, playback progress in `VideoPlayer.svelte`).
|
|
- **Unified player boundary**: UI components control playback only through the frontend facade `src/lib/player/index.ts` (`playerController`), never by calling `commands.player*` directly. Webview-rendered HTML5 video reports its state back into Rust via `src/lib/player/html5Adapter.ts` and the `player_report_*` commands, so the `PlayerController` stays the single source of truth in both native (MPV/ExoPlayer) and HTML5 modes (see [05-platform-backends.md](05-platform-backends.md)).
|
|
- **Handle-Based Resources**: UUID handles for stateful Rust objects.
|
|
- **Cache-First**: Parallel queries with intelligent fallback.
|
|
- **Single source of truth for reachability**: Server reachability is derived from the outcome of *real repository traffic*, not a side-channel poller. The `OnlineRepository` reports each server result to the `ConnectivityMonitor` (classified via `RepoError`), which applies a time-window debounce before declaring the server offline and recovers instantly on the first success. The standalone `/System/Info/Public` probe runs *only while offline*, as a recovery detector for idle sessions.
|
|
- **Poison-tolerant locking**: Shared `std::sync` state is accessed via the `MutexSafe`/`RwLockSafe` helpers in `utils/lock.rs`, which recover a poisoned lock instead of cascading a panic across the player.
|
|
- **Graceful backend init**: If a native player backend (MPV/ExoPlayer) fails to initialize, the app falls back to a no-op backend and emits a `backend-init-failed` event rather than crashing.
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
subgraph Frontend["Svelte Frontend"]
|
|
subgraph Stores["Stores (Thin Wrappers)"]
|
|
auth["auth"]
|
|
player["player"]
|
|
queue["queue"]
|
|
library["library"]
|
|
connectivity["connectivity"]
|
|
playbackMode["playbackMode"]
|
|
end
|
|
subgraph Components
|
|
playerComp["player/"]
|
|
libraryComp["library/"]
|
|
Search["Search"]
|
|
end
|
|
subgraph Routes
|
|
routeLibrary["/library"]
|
|
routePlayer["/player"]
|
|
routeRoot["/"]
|
|
end
|
|
subgraph API["API Layer (Thin Client)"]
|
|
RepositoryClient["RepositoryClient<br/>(Handle-based)"]
|
|
JellyfinClient["JellyfinClient<br/>(Helper)"]
|
|
end
|
|
end
|
|
|
|
Frontend -->|"Tauri IPC (invoke)"| Backend
|
|
|
|
subgraph Backend["Rust Backend (Business Logic)"]
|
|
subgraph Commands["Tauri Commands (90+)"]
|
|
PlayerCmds["player.rs"]
|
|
RepoCmds["repository.rs (27)"]
|
|
PlaybackModeCmds["playback_mode.rs (5)"]
|
|
StorageCmds["storage.rs"]
|
|
ConnectivityCmds["connectivity.rs (7)"]
|
|
end
|
|
|
|
subgraph Core["Core Modules"]
|
|
MediaSessionManager["MediaSessionManager<br/>(Audio/Movie/TvShow/Idle)"]
|
|
|
|
PlayerController["PlayerController<br/>+ PlayerBackend<br/>+ QueueManager"]
|
|
|
|
Repository["Repository Layer<br/>HybridRepository (cache-first)<br/>OnlineRepository (HTTP)<br/>OfflineRepository (SQLite)"]
|
|
|
|
PlaybackModeManager["PlaybackModeManager<br/>(Local/Remote/Idle)"]
|
|
|
|
ConnectivityMonitor["ConnectivityMonitor<br/>(Adaptive polling)"]
|
|
|
|
HttpClient["HttpClient<br/>(Exponential backoff retry)"]
|
|
end
|
|
|
|
subgraph Storage["Storage Layer"]
|
|
DatabaseService["DatabaseService<br/>(Async trait)"]
|
|
SQLite["SQLite Database<br/>(13 tables)"]
|
|
end
|
|
|
|
Commands --> Core
|
|
Core --> Storage
|
|
Repository --> HttpClient
|
|
Repository --> DatabaseService
|
|
Repository -->|"reports server outcome<br/>(success / RepoError)"| ConnectivityMonitor
|
|
end
|
|
```
|
|
|
|
> The `Repository --> ConnectivityMonitor` edge is the source of truth for the offline/online banner: every server request the user actually makes updates reachability. The monitor's own polling is now an offline-only recovery probe (see [07-connectivity.md](07-connectivity.md)).
|
|
|
|
---
|
|
|
|
## Detailed Documentation
|
|
|
|
Each major subsystem is documented in its own file in this directory:
|
|
|
|
| Document | Contents |
|
|
|----------|----------|
|
|
| [01 - Rust Backend](01-rust-backend.md) | Media session state machine, player state machine, playback mode, media items, queue manager, favorites (marking + browsing), player backend trait, player controller, playlist system, **domain vocabulary owned by Rust** (search scope, library exclusions, streaming quality ladder), **background workers** (catalog indexer, drains), Tauri commands |
|
|
| [02 - Svelte Frontend](02-svelte-frontend.md) | Store structure, music library navigation, playback reporting, repository architecture, playback mode system, database service abstraction, component hierarchy, MiniPlayer, sleep timer, auto-play, navigation guard, playlist management UI, library mosaic, series/episode navigation, downloaded browse, safe-area insets, native-video store, logging |
|
|
| [03 - Data Flow](03-data-flow.md) | Repository query flow (cache-first), locally-indexed search, playback initiation, playback mode transfer, queue navigation, volume control |
|
|
| [04 - Type Sync & Threading](04-type-sync-and-threading.md) | Rust/TypeScript type synchronization, Tauri v2 IPC parameter naming convention, thread safety patterns |
|
|
| [05 - Platform Backends](05-platform-backends.md) | Player events system, MpvBackend (Linux), ExoPlayerBackend (Android) incl. audio settings parity, **native video compositing**, MediaSession & remote volume, album art caching, backend initialization |
|
|
| [06 - Downloads & Offline](06-downloads-and-offline.md) | Download manager, download worker, smart caching engine, **one storage model (cache entries are downloads)**, offline catalog visibility, download/offline commands, player integration, frontend store, UI components |
|
|
| [07 - Connectivity](07-connectivity.md) | HTTP client with retry logic, connectivity monitor, network resilience architecture |
|
|
| [08 - Database Design](08-database-design.md) | Entity relationships, all table definitions (servers, users, libraries, items, user_data, downloads, media_streams, sync_queue, thumbnails, playlists), key queries, data flow diagrams, storage estimates |
|
|
| [09 - Security](09-security.md) | Authentication token storage, secure storage module, network security, webview CSP + asset-protocol scope, **path confinement and input binding**, local data protection |
|
|
|
|
---
|
|
|
|
## File Structure Summary
|
|
|
|
```
|
|
src-tauri/src/
|
|
├── lib.rs # Tauri app setup, state initialization
|
|
├── commands/ # Tauri command handlers (~245 #[tauri::command] fns)
|
|
│ ├── mod.rs # Command exports
|
|
│ ├── player/ # Player commands: queue, remote, session, settings, timers
|
|
│ ├── repository.rs # Repository commands (items, search, favourites, disk usage)
|
|
│ ├── catalog.rs # Catalog sync + the background index pass
|
|
│ ├── favorites.rs # Offline favourite drain
|
|
│ ├── library.rs # Library listing + folder exclusions
|
|
│ ├── playlist.rs # Playlist commands
|
|
│ ├── playback_mode.rs # Local/remote transfer
|
|
│ ├── playback_reporting.rs
|
|
│ ├── connectivity.rs # Connectivity commands
|
|
│ ├── storage/ # Storage & database commands: people, series_prefs, thumbnails
|
|
│ ├── download/ # Download commands: mod, pinning, smart_cache
|
|
│ ├── offline.rs # Offline commands
|
|
│ ├── device.rs # Device id / capabilities
|
|
│ ├── sessions.rs # Remote sessions
|
|
│ ├── sync.rs # Sync queue commands
|
|
│ └── sync_drain.rs # Background sync-queue drain
|
|
├── repository/ # Repository pattern implementation
|
|
│ ├── mod.rs # MediaRepository trait, handle management
|
|
│ ├── types.rs # RepoError, Library, MediaItem, etc.
|
|
│ ├── hybrid.rs # HybridRepository with cache-first racing
|
|
│ ├── online.rs # OnlineRepository (HTTP API)
|
|
│ └── offline.rs # OfflineRepository (SQLite queries)
|
|
├── playback_mode/ # Playback mode manager
|
|
│ └── mod.rs # PlaybackMode enum, transfer logic
|
|
├── connectivity/ # Connectivity monitoring
|
|
│ └── mod.rs # ConnectivityMonitor, adaptive polling
|
|
├── jellyfin/ # Jellyfin API client
|
|
│ ├── mod.rs # Module exports
|
|
│ ├── http_client.rs # HTTP client with retry logic
|
|
│ └── client.rs # JellyfinClient for API calls
|
|
├── storage/ # Database layer
|
|
│ ├── mod.rs # Database struct, migrations
|
|
│ ├── db_service.rs # DatabaseService trait (async wrapper)
|
|
│ ├── schema.rs # Table definitions
|
|
│ └── queries/ # Query modules
|
|
├── download/ # Download manager module
|
|
│ ├── mod.rs # DownloadManager, DownloadInfo, DownloadTask
|
|
│ ├── worker.rs # DownloadWorker, HTTP streaming, retry logic
|
|
│ ├── events.rs # DownloadEvent enum
|
|
│ └── cache.rs # SmartCache, CacheConfig, LRU eviction
|
|
└── player/ # Player subsystem
|
|
├── mod.rs # PlayerController
|
|
├── session.rs # MediaSessionManager, MediaSessionType
|
|
├── state.rs # PlayerState, PlayerEvent
|
|
├── media.rs # MediaItem, MediaSource, MediaType
|
|
├── queue.rs # QueueManager, RepeatMode
|
|
├── backend.rs # PlayerBackend trait, NullBackend
|
|
├── events.rs # PlayerStatusEvent, TauriEventEmitter
|
|
├── mpv/ # Linux MPV backend
|
|
│ ├── mod.rs # MpvBackend implementation
|
|
│ └── event_loop.rs # Dedicated thread for MPV operations
|
|
└── android/ # Android ExoPlayer backend
|
|
└── mod.rs # ExoPlayerBackend + JNI bindings
|
|
|
|
src/lib/
|
|
├── api/ # Thin API layer (~200 lines total)
|
|
│ ├── types.ts # TypeScript type definitions
|
|
│ ├── repository-client.ts # RepositoryClient wrapper (~100 lines)
|
|
│ ├── client.ts # JellyfinClient (helper for streaming)
|
|
│ └── sessions.ts # SessionsApi (remote session control)
|
|
├── player/ # Unified player boundary (frontend)
|
|
│ ├── index.ts # playerController facade — the only write-side entry point for playback
|
|
├── services/
|
|
│ ├── playerEvents.ts # Tauri event listener for player events
|
|
│ └── playbackReporting.ts # Thin wrapper (~50 lines)
|
|
├── stores/ # Thin reactive wrappers over Rust commands
|
|
│ ├── index.ts # Re-exports
|
|
│ ├── auth.ts # Auth store (calls Rust commands)
|
|
│ ├── player.ts # Player store
|
|
│ ├── queue.ts # Queue store
|
|
│ ├── library.ts # Library store
|
|
│ ├── playbackMode.ts # Playback mode store (~150 lines)
|
|
│ ├── connectivity.ts # Connectivity store (~250 lines)
|
|
│ └── downloads.ts # Downloads store with event listeners
|
|
└── components/
|
|
├── Search.svelte
|
|
├── player/ # Player UI components
|
|
├── playlist/ # Playlist modals (Create, AddTo)
|
|
├── sessions/ # Remote session control UI
|
|
├── downloads/ # Download UI components
|
|
└── library/ # Library UI components + PlaylistDetailView
|
|
```
|
|
|
|
## Key Architecture Changes
|
|
|
|
**What moved to Rust (~3,500 lines of business logic):**
|
|
1. **HTTP Client** (338 lines) - Retry logic with exponential backoff
|
|
2. **Connectivity Monitor** (301 lines) - Reachability derived from real repository traffic, time-window debounce, offline-only recovery probe, event emission
|
|
3. **Repository Pattern** (1061 lines) - Cache-first hybrid with parallel racing
|
|
4. **Database Service** - Async wrapper preventing UI freezing
|
|
5. **Playback Mode** (303 lines) - Local/remote transfer coordination
|
|
|
|
**Svelte/TypeScript frontend (~20.5k non-test lines, plus ~9.6k test lines):**
|
|
- Components + routes (~14.6k lines) — UI and presentation
|
|
- Stores (~3.4k lines) — reactive state that invokes Rust commands and listens for events
|
|
- api / services / utils (~2.4k lines) — typed clients, event listeners, conversion helpers
|
|
|
|
The frontend is genuinely UI-heavy; business decisions live in Rust, but the UI owns layout, navigation, and interaction state.
|
|
|
|
**Total Commands:** ~245 `#[tauri::command]` functions across 17 command modules
|
|
(~58k lines of Rust, ~37k non-test lines of TypeScript/Svelte).
|
|
|
|
> Counts and line totals in this file are periodic snapshots, not gates — the
|
|
> authority is the tree. Regenerate with
|
|
> `grep -rc '#\[tauri::command\]' src-tauri/src` and `wc -l`.
|