Update docs

This commit is contained in:
Duncan Tourolle 2026-01-26 22:31:37 +01:00
parent cfddc1edea
commit e560543181
5 changed files with 135 additions and 2442 deletions

View File

@ -1,212 +0,0 @@
# Smart Preloading Implementation
This document describes the smart preloading system that automatically queues downloads for upcoming tracks in the playback queue.
## Overview
The preloading system monitors playback and automatically queues background downloads for the next 3 tracks in the queue. This ensures smooth playback transitions and offline availability without requiring manual downloads.
## Architecture
### Backend (Rust)
**Smart Cache Engine** - `/src-tauri/src/download/cache.rs`
- Manages preload configuration
- Default settings: preload 3 tracks, works on any connection (not wifi-only)
- Storage limit: 10GB
- Album affinity tracking (future feature)
**Queue Manager** - `/src-tauri/src/player/queue.rs`
- New method: `get_upcoming(count)` returns next N tracks
- Respects shuffle order
- Handles repeat modes (Off, All, One)
- Wraps around when RepeatMode::All is enabled
**Preload Command** - `/src-tauri/src/commands/player.rs`
- `player_preload_upcoming` - Main preload endpoint
- Checks SmartCache configuration
- Gets upcoming tracks from queue
- Skips already downloaded/queued items
- Queues new downloads with low priority (-100)
- Returns stats: queued_count, already_downloaded, skipped
**Configuration Commands**
- `player_set_cache_config` - Update preload settings
- `player_get_cache_config` - Get current settings
### Frontend (TypeScript/Svelte)
**Preload Service** - `/src/lib/services/preload.ts`
- Main function: `preloadUpcomingTracks()`
- Automatically gets current user ID from auth store
- Calls backend preload command
- Fails silently - never interrupts playback
- Logs meaningful results only
**Integration Points** - `/src/lib/services/playerEvents.ts`
1. **On playback start** - When state changes to "playing"
2. **On track advance** - After successfully advancing to next track
## How It Works
### Playback Flow
```
User plays track/queue
Backend starts playback
Emits "state_changed" event with "playing"
playerEvents.handleStateChanged() catches event
Calls preloadUpcomingTracks()
Backend queues downloads for next 3 tracks (if not already downloaded)
```
### Track Advance Flow
```
Track ends (or user clicks next)
playerEvents.handlePlaybackEnded() or manual next()
Calls player_next backend command
Backend plays next track, emits "state_changed"
Calls preloadUpcomingTracks() again
Queue is shifted forward, new track gets preloaded
```
## Configuration
### Default Settings
```typescript
{
queuePrecacheEnabled: true,
queuePrecacheCount: 3,
albumAffinityEnabled: true,
albumAffinityThreshold: 3,
storageLimit: 10737418240, // 10GB
wifiOnly: false
}
```
### Updating Configuration
```typescript
import { updateCacheConfig } from '$lib/services/preload';
await updateCacheConfig({
queuePrecacheCount: 5, // Preload 5 tracks instead of 3
wifiOnly: true // Only preload on WiFi
});
```
## Features
### Intelligent Queueing
- ✅ Checks if tracks are already downloaded
- ✅ Skips tracks already in download queue
- ✅ Low priority (-100) so user-initiated downloads go first
- ✅ Respects shuffle and repeat modes
- ✅ No duplicate downloads
### Offline First
- ✅ Existing `create_media_item()` in player.rs checks local downloads first
- ✅ Preloading ensures next tracks become local over time
- ✅ Seamless offline playback without manual intervention
### Non-Intrusive
- ✅ Background operation - never blocks playback
- ✅ Fails silently - errors are logged but don't affect UX
- ✅ Automatic - no user interaction required
- ✅ Configurable - users can adjust or disable
## Files Changed
### Backend
- `src-tauri/src/player/queue.rs` - Added `get_upcoming()` method
- `src-tauri/src/download/cache.rs` - Made `CacheConfig` serializable, updated defaults
- `src-tauri/src/commands/player.rs` - Added preload commands and `SmartCacheWrapper`
- `src-tauri/src/lib.rs` - Initialized SmartCache, registered commands
### Frontend
- `src/lib/services/preload.ts` - New preload service (created)
- `src/lib/services/playerEvents.ts` - Integrated preload triggers
### Configuration
- `src-tauri/Cargo.toml` - Added tempfile dev dependency for tests
## Testing
### Rust Tests
```bash
cd src-tauri
cargo test queue::tests::test_get_upcoming
cargo test cache::tests::test_default_config
```
All tests pass ✅
### Manual Testing
1. **Start playback**
- Play a queue of 5+ tracks
- Check console for: `[Preload] Queued N track(s) for background download`
- Verify download queue shows 3 pending downloads with priority -100
2. **Track advance**
- Let track finish or click next
- Check console for new preload log
- Verify queue shifts (old track 2 becomes current, new track gets queued)
3. **Repeat mode**
- Enable Repeat All
- Play to end of queue
- Verify wraps around and continues preloading
4. **Already downloaded**
- Download all tracks in an album
- Play the album
- Check logs show: `already_downloaded: 3, queued: 0`
## Future Enhancements
1. **Album Affinity** - If user plays 3+ tracks from an album, auto-download the rest
2. **WiFi Detection** - Respect `wifi_only` setting on mobile
3. **Storage Management** - Auto-evict LRU items when storage limit reached
4. **Smart Priority** - Boost priority as track gets closer in queue
5. **Bandwidth Throttling** - Limit download speed to not interfere with streaming
## Troubleshooting
### Preload not working
- Check console for `[Preload]` logs
- Verify user is logged in: `auth.getUserId()` returns value
- Check SmartCache config: `queuePrecacheEnabled` should be true
### Downloads not starting
- Preload only queues downloads, doesn't start them
- Check download manager is processing queue
- Verify backend has download worker running
### Too many downloads
- Reduce `queuePrecacheCount` in config
- Enable `wifiOnly` mode
- Adjust `storageLimit`
## Performance Impact
- **Minimal** - Background downloads use low priority
- **Non-blocking** - Async operation, no playback delay
- **Bandwidth-friendly** - Only downloads when needed
- **Storage-aware** - Respects configured limits
## Summary
Smart preloading transforms JellyTau into an offline-first music player. By automatically queueing downloads for upcoming tracks, it ensures seamless playback and offline availability without requiring users to manually manage downloads. The system is intelligent (checks what's already downloaded), non-intrusive (fails silently), and configurable (users can adjust or disable).

View File

@ -16,14 +16,14 @@ A cross-platform Jellyfin client built with Tauri, SvelteKit, and TypeScript.
|----|-------------|----------|--------| |----|-------------|----------|--------|
| UR-001 | Run the app on multiple platforms (Linux, Android) | High | In Progress | | UR-001 | Run the app on multiple platforms (Linux, Android) | High | In Progress |
| UR-002 | Access media when online or offline | High | Done | | UR-002 | Access media when online or offline | High | Done |
| UR-003 | Play videos | High | Planned | | UR-003 | Play videos | High | Done |
| UR-004 | Play audio uninterrupted | High | Planned | | UR-004 | Play audio uninterrupted | High | Done |
| UR-005 | Control media playback (pause, play, skip, scrub) | High | In Progress | | UR-005 | Control media playback (pause, play, skip, scrub) | High | Done |
| UR-006 | Control media when device is on lock screen or via BLE headsets | Medium | Planned | | UR-006 | Control media when device is on lock screen or via BLE headsets | Medium | In Progress |
| UR-007 | Navigate media in library | High | Done | | UR-007 | Navigate media in library | High | Done |
| UR-008 | Search media across libraries | High | Done | | UR-008 | Search media across libraries | High | Done |
| UR-009 | Connect to Jellyfin to access media | High | Done | | UR-009 | Connect to Jellyfin to access media | High | Done |
| UR-010 | Control playback of Jellyfin remote sessions | Low | Planned | | UR-010 | Control playback of Jellyfin remote sessions | Low | Done |
| UR-011 | Download media on demand | Medium | Done | | UR-011 | Download media on demand | Medium | Done |
| UR-012 | Login info shall be stored securely and persistently | High | Done | | UR-012 | Login info shall be stored securely and persistently | High | Done |
| UR-013 | View and manage downloaded media | Medium | Done | | UR-013 | View and manage downloaded media | Medium | Done |
@ -36,23 +36,23 @@ A cross-platform Jellyfin client built with Tauri, SvelteKit, and TypeScript.
| UR-020 | Select subtitles for video content | High | Planned | | UR-020 | Select subtitles for video content | High | Planned |
| UR-021 | Select audio track for video content | High | Planned | | UR-021 | Select audio track for video content | High | Planned |
| UR-022 | Control streaming quality and transcoding settings | Medium | Planned | | UR-022 | Control streaming quality and transcoding settings | Medium | Planned |
| UR-023 | View "Next Up" / Continue Watching on home screen; auto-play next episode with countdown popup | Medium | In Progress | | UR-023 | View "Next Up" / Continue Watching on home screen; auto-play next episode with countdown popup | Medium | Done |
| UR-024 | View recently added content on server | Medium | Planned | | UR-024 | View recently added content on server | Medium | Done |
| UR-025 | Sync watch history and progress back to Jellyfin | High | Done | | UR-025 | Sync watch history and progress back to Jellyfin | High | Done |
| UR-026 | Sleep timer for audio playback | Low | Planned | | UR-026 | Sleep timer for audio playback | Low | Done |
| UR-027 | Audio equalizer for sound customization | Low | Planned | | UR-027 | Audio equalizer for sound customization | Low | Planned |
| UR-028 | Navigate to artist/album by tapping names in now playing view | High | Done | | UR-028 | Navigate to artist/album by tapping names in now playing view | High | Done |
| UR-029 | Toggle between grid and list view in library | Medium | Done | | UR-029 | Toggle between grid and list view in library | Medium | Done |
| UR-030 | Quick genre browsing and filtering | Medium | Done | | UR-030 | Quick genre browsing and filtering | Medium | Done |
| UR-031 | Crossfade between audio tracks | Low | Planned (Linux only) | | UR-031 | Crossfade between audio tracks | Low | Done (Linux only) |
| UR-032 | Gapless playback for seamless album listening | Medium | Done (Linux only) | | UR-032 | Gapless playback for seamless album listening | Medium | Done (Linux only) |
| UR-033 | Volume normalization to prevent volume jumps between tracks | Low | Done (Linux only) | | UR-033 | Volume normalization to prevent volume jumps between tracks | Low | Done (Linux only) |
| UR-034 | Rich home screen with hero banners, carousels, and personalized sections | High | Planned | | UR-034 | Rich home screen with hero banners, carousels, and personalized sections | High | Done |
| UR-035 | View cast/crew (actors, directors) on movie/show detail pages | High | Planned | | UR-035 | View cast/crew (actors, directors) on movie/show detail pages | High | Done |
| UR-036 | Navigate to actor/person page showing their filmography | Medium | Planned | | UR-036 | Navigate to actor/person page showing their filmography | Medium | Done |
| UR-037 | Visually appealing video library with poster grids and metadata | High | Planned | | UR-037 | Visually appealing video library with poster grids and metadata | High | Done |
| UR-038 | Movie/show detail page with backdrop, ratings, and rich metadata | High | Planned | | UR-038 | Movie/show detail page with backdrop, ratings, and rich metadata | High | Done |
| UR-039 | Navigate between main sections via bottom navigation bar | High | In Progress | | UR-039 | Navigate between main sections via bottom navigation bar | High | Done |
--- ---
@ -66,16 +66,16 @@ External system integrations and platform-specific implementations.
|----|-------------|----------|-----------|--------| |----|-------------|----------|-----------|--------|
| IR-001 | Build system supporting multiple targets (Linux, Android) | Build | UR-001 | Done | | IR-001 | Build system supporting multiple targets (Linux, Android) | Build | UR-001 | Done |
| IR-002 | Build scripts for Android and Linux | Build | UR-001 | Done | | IR-002 | Build scripts for Android and Linux | Build | UR-001 | Done |
| IR-003 | Integration of libmpv for Linux playback | Playback | UR-003, UR-004 | In Progress | | IR-003 | Integration of libmpv for Linux playback | Playback | UR-003, UR-004 | Done |
| IR-004 | Integration of ExoPlayer for Android playback | Playback | UR-003, UR-004 | In Progress (basic playback works, audio settings missing) | | IR-004 | Integration of ExoPlayer for Android playback | Playback | UR-003, UR-004 | In Progress (basic playback works, audio settings missing) |
| IR-005 | MPRIS D-Bus integration for Linux lockscreen/media controls | Platform | UR-006 | Planned | | IR-005 | MPRIS D-Bus integration for Linux lockscreen/media controls | Platform | UR-006 | Planned |
| IR-006 | Android MediaSession integration for lockscreen controls | Platform | UR-006 | In Progress | | IR-006 | Android MediaSession integration for lockscreen controls | Platform | UR-006 | Done |
| IR-007 | Bluetooth AVRCP integration via system media session | Platform | UR-006 | Planned | | IR-007 | Bluetooth AVRCP integration via system media session | Platform | UR-006 | Planned |
| IR-008 | Android audio focus handling (pause on call) | Platform | UR-004, UR-006 | In Progress | | IR-008 | Android audio focus handling (pause on call) | Platform | UR-004, UR-006 | Done |
| IR-009 | Jellyfin API client for authentication | API | UR-009, UR-012 | Done | | IR-009 | Jellyfin API client for authentication | API | UR-009, UR-012 | Done |
| IR-010 | Jellyfin API client for library browsing | API | UR-007, UR-008 | Done | | IR-010 | Jellyfin API client for library browsing | API | UR-007, UR-008 | Done |
| IR-011 | Jellyfin API client for playback streaming | API | UR-003, UR-004 | Done | | IR-011 | Jellyfin API client for playback streaming | API | UR-003, UR-004 | Done |
| IR-012 | Jellyfin Sessions API for remote playback control | API | UR-010 | Planned | | IR-012 | Jellyfin Sessions API for remote playback control | API | UR-010 | Done |
| IR-021 | Android MediaRouter integration for remote volume in system panel | Platform | UR-010, UR-016 | Planned | | IR-021 | Android MediaRouter integration for remote volume in system panel | Platform | UR-010, UR-016 | Planned |
| IR-013 | SQLite integration for local database | Storage | UR-002, UR-011 | Done | | IR-013 | SQLite integration for local database | Storage | UR-002, UR-011 | Done |
| IR-014 | Secure credential storage (keyring/keychain) | Security | UR-012 | Done | | IR-014 | Secure credential storage (keyring/keychain) | Security | UR-012 | Done |
@ -85,9 +85,9 @@ External system integrations and platform-specific implementations.
| IR-018 | libmpv subtitle rendering and selection | Playback | UR-020 | Planned | | IR-018 | libmpv subtitle rendering and selection | Playback | UR-020 | Planned |
| IR-019 | libmpv audio track selection | Playback | UR-021 | Planned | | IR-019 | libmpv audio track selection | Playback | UR-021 | Planned |
| IR-020 | libmpv/ExoPlayer equalizer integration | Playback | UR-027 | Planned | | IR-020 | libmpv/ExoPlayer equalizer integration | Playback | UR-027 | Planned |
| IR-022 | Jellyfin API client for person/cast data | API | UR-035, UR-036 | Planned | | IR-022 | Jellyfin API client for person/cast data | API | UR-035, UR-036 | Done |
| IR-023 | Database schema for person/cast caching | Storage | UR-035, UR-036 | Planned | | IR-023 | Database schema for person/cast caching | Storage | UR-035, UR-036 | Done |
| IR-024 | Jellyfin API client for home screen data (featured, continue watching) | API | UR-034 | Planned | | IR-024 | Jellyfin API client for home screen data (featured, continue watching) | API | UR-034 | Done |
### 2.2 Jellyfin API Requirements ### 2.2 Jellyfin API Requirements
@ -108,9 +108,9 @@ API endpoints and data contracts required for Jellyfin integration.
| JA-011 | Report playback progress (periodic) | Sessions | UR-025 | Done | | JA-011 | Report playback progress (periodic) | Sessions | UR-025 | Done |
| JA-012 | Report playback stopped | Sessions | UR-025 | Done | | JA-012 | Report playback stopped | Sessions | UR-025 | Done |
| JA-013 | Get resume position for item | UserData | UR-019 | Done | | JA-013 | Get resume position for item | UserData | UR-019 | Done |
| JA-014 | Get "Next Up" items | Shows | UR-023 | Planned | | JA-014 | Get "Next Up" items | Shows | UR-023 | Done |
| JA-015 | Get "Continue Watching" items | Items | UR-023 | Planned | | JA-015 | Get "Continue Watching" items | Items | UR-023 | Done |
| JA-016 | Get recently added items | Items | UR-024 | Planned | | JA-016 | Get recently added items | Items | UR-024 | Done |
| JA-017 | Mark item as favorite | UserData | UR-017 | Done | | JA-017 | Mark item as favorite | UserData | UR-017 | Done |
| JA-018 | Remove item from favorites | UserData | UR-017 | Done | | JA-018 | Remove item from favorites | UserData | UR-017 | Done |
| JA-019 | Get/create/update playlists | Playlists | UR-014 | Planned | | JA-019 | Get/create/update playlists | Playlists | UR-014 | Planned |
@ -142,7 +142,7 @@ Internal architecture, components, and application logic.
| DR-007 | Library browsing screens (grid view, search, filters) | UI | UR-007, UR-008 | Done | | DR-007 | Library browsing screens (grid view, search, filters) | UI | UR-007, UR-008 | Done |
| DR-008 | Album/Series detail view with track listing | UI | UR-007 | Done | | DR-008 | Album/Series detail view with track listing | UI | UR-007 | Done |
| DR-009 | Audio player UI (mini player, full screen) | UI | UR-005 | Done | | DR-009 | Audio player UI (mini player, full screen) | UI | UR-005 | Done |
| DR-010 | Video player UI (fullscreen, controls overlay) | UI | UR-003, UR-005 | Planned | | DR-010 | Video player UI (fullscreen, controls overlay) | UI | UR-003, UR-005 | Done |
| DR-011 | Search bar with cross-library search | UI | UR-008 | Done | | DR-011 | Search bar with cross-library search | UI | UR-008 | Done |
| DR-012 | Local database for media metadata cache | Storage | UR-002 | Done | | DR-012 | Local database for media metadata cache | Storage | UR-002 | Done |
| DR-013 | Repository pattern for online/offline data access | Storage | UR-002 | Done | | DR-013 | Repository pattern for online/offline data access | Storage | UR-002 | Done |
@ -158,29 +158,29 @@ Internal architecture, components, and application logic.
| DR-023 | Subtitle selection UI in video player | UI | UR-020 | Planned | | DR-023 | Subtitle selection UI in video player | UI | UR-020 | Planned |
| DR-024 | Audio track selection UI in video player | UI | UR-021 | Planned | | DR-024 | Audio track selection UI in video player | UI | UR-021 | Planned |
| DR-025 | Quality/transcoding settings UI | UI | UR-022 | Planned | | DR-025 | Quality/transcoding settings UI | UI | UR-022 | Planned |
| DR-026 | "Continue Watching" / "Next Up" home section | UI | UR-023 | Planned | | DR-026 | "Continue Watching" / "Next Up" home section | UI | UR-023 | Done |
| DR-027 | "Recently Added" home section | UI | UR-024 | Planned | | DR-027 | "Recently Added" home section | UI | UR-024 | Done |
| DR-028 | Playback progress sync service (periodic reporting) | Player | UR-025 | Done | | DR-028 | Playback progress sync service (periodic reporting) | Player | UR-025 | Done |
| DR-029 | Sleep timer with countdown and auto-stop | Player | UR-026 | Planned | | DR-029 | Sleep timer with countdown and auto-stop | Player | UR-026 | Done |
| DR-030 | Equalizer UI with presets and custom bands | UI | UR-027 | Planned | | DR-030 | Equalizer UI with presets and custom bands | UI | UR-027 | Planned |
| DR-031 | Clickable artist/album links in now playing view | UI | UR-028 | Done | | DR-031 | Clickable artist/album links in now playing view | UI | UR-028 | Done |
| DR-032 | List view option for library browsing (albums, artists) | UI | UR-029 | Done | | DR-032 | List view option for library browsing (albums, artists) | UI | UR-029 | Done |
| DR-033 | Genre browsing screen with quick filters | UI | UR-030 | Done | | DR-033 | Genre browsing screen with quick filters | UI | UR-030 | Done |
| DR-034 | Crossfade engine with configurable duration (0-12s) | Player | UR-031 | Planned | | DR-034 | Crossfade engine with configurable duration (0-12s) | Player | UR-031 | Done (Linux only) |
| DR-035 | Gapless playback between sequential tracks | Player | UR-032 | Done | | DR-035 | Gapless playback between sequential tracks | Player | UR-032 | Done (Linux only) |
| DR-036 | Volume normalization with preset levels (Loud/Normal/Quiet) | Player | UR-033 | Done | | DR-036 | Volume normalization with preset levels (Loud/Normal/Quiet) | Player | UR-033 | Done (Linux only) |
| DR-037 | Remote session browser and control UI | UI | UR-010 | Planned | | DR-037 | Remote session browser and control UI | UI | UR-010 | Done |
| DR-038 | Home screen with hero banner carousel (featured/continue watching) | UI | UR-034 | Planned | | DR-038 | Home screen with hero banner carousel (featured/continue watching) | UI | UR-034 | Done |
| DR-039 | Home screen horizontal carousels (recently added, recommendations) | UI | UR-034, UR-024 | Planned | | DR-039 | Home screen horizontal carousels (recently added, recommendations) | UI | UR-034, UR-024 | Done |
| DR-040 | Cast/crew section on movie/show detail pages | UI | UR-035 | Planned | | DR-040 | Cast/crew section on movie/show detail pages | UI | UR-035 | Done |
| DR-041 | Person/actor detail page with filmography grid | UI | UR-036 | Planned | | DR-041 | Person/actor detail page with filmography grid | UI | UR-036 | Done |
| DR-042 | Video library grid with poster cards, year, and rating badges | UI | UR-037 | Planned | | DR-042 | Video library grid with poster cards, year, and rating badges | UI | UR-037 | Done |
| DR-043 | Movie/show detail page with backdrop hero, synopsis, and metadata | UI | UR-038 | Planned | | DR-043 | Movie/show detail page with backdrop hero, synopsis, and metadata | UI | UR-038 | Done |
| DR-044 | Horizontal scrolling actor/cast row with profile images | UI | UR-035 | Planned | | DR-044 | Horizontal scrolling actor/cast row with profile images | UI | UR-035 | Done |
| DR-045 | Bottom navigation bar with Home, Library, Search buttons | UI | UR-039 | In Progress | | DR-045 | Bottom navigation bar with Home, Library, Search buttons | UI | UR-039 | Done |
| DR-046 | Dedicated search page with input and results | UI | UR-039 | In Progress | | DR-046 | Dedicated search page with input and results | UI | UR-039 | Done |
| DR-047 | Next episode auto-play popup with configurable countdown | Player | UR-023 | In Progress | | DR-047 | Next episode auto-play popup with configurable countdown | Player | UR-023 | Done |
| DR-048 | Video settings (auto-play toggle, countdown duration) | Settings | UR-023, UR-026 | In Progress | | DR-048 | Video settings (auto-play toggle, countdown duration) | Settings | UR-023, UR-026 | Done |
--- ---

View File

@ -2,42 +2,11 @@
This document describes the current architecture of JellyTau, a cross-platform Jellyfin client built with Tauri, SvelteKit, and Rust. This document describes the current architecture of JellyTau, a cross-platform Jellyfin client built with Tauri, SvelteKit, and Rust.
**Last Updated:** 2026-01-18 **Last Updated:** 2026-01-26
**Architecture Status:** Phase 2-3 of TypeScript to Rust migration complete
## Major Architectural Changes (Recent) ## Architecture Overview
JellyTau has undergone a significant architectural transformation, migrating ~3,500 lines of business logic from TypeScript to Rust: JellyTau uses a modern client-server architecture with a thin Svelte UI layer and comprehensive Rust backend:
### ✅ Completed Migrations
1. **HTTP Client & Connectivity** (Phase 1)
- Exponential backoff retry logic moved to Rust
- Adaptive connectivity monitoring (30s online, 5s offline)
- Event-driven architecture for network state changes
2. **Repository Pattern** (Phase 2)
- Complete MediaRepository trait implementation in Rust
- Cache-first parallel racing (100ms cache timeout)
- Handle-based resource management (UUID handles)
- 27 new Tauri commands for data access
- Eliminated 1,061 lines of TypeScript
3. **Database Service Abstraction** (Phase 2.5)
- Async wrapper over synchronous rusqlite
- All DB operations use `spawn_blocking` to prevent UI freezing
- ~1-2ms overhead per query (acceptable tradeoff)
4. **Playback Mode Management** (Phase 3)
- Local/Remote/Idle mode tracking
- Seamless queue transfer to remote Jellyfin sessions
- Position synchronization during transfers
### 🔄 In Progress
- **Authentication & Session Management** (Phase 4)
- Session restoration and credential management
- Re-authentication flow
### Architecture Principles ### Architecture Principles
@ -521,7 +490,6 @@ flowchart TB
- Attempts to sync with Jellyfin server - Attempts to sync with Jellyfin server
- Marks as synced if successful, otherwise leaves `pending_sync = 1` - Marks as synced if successful, otherwise leaves `pending_sync = 1`
3. UI reflects the change immediately without waiting for server response 3. UI reflects the change immediately without waiting for server response
4. Future: Sync queue will retry failed syncs when online
**Components**: **Components**:
@ -653,13 +621,21 @@ flowchart TB
JellyTau's music library uses a category-based navigation system with a dedicated landing page that routes users to specialized views for different content types. JellyTau's music library uses a category-based navigation system with a dedicated landing page that routes users to specialized views for different content types.
**Route Structure:** **Route Structure:**
```
/library/music # Landing page with category cards ```mermaid
├── /tracks # All songs (ALWAYS list view) graph TD
├── /artists # Artist grid (ALWAYS grid view) Music["/library/music<br/>(Landing page with category cards)"]
├── /albums # Album grid (ALWAYS grid view) Tracks["Tracks<br/>(List view only)"]
├── /playlists # Playlist grid (ALWAYS grid view) Artists["Artists<br/>(Grid view)"]
└── /genres # Genre browser (two-level) Albums["Albums<br/>(Grid view)"]
Playlists["Playlists<br/>(Grid view)"]
Genres["Genres<br/>(Genre browser)"]
Music --> Tracks
Music --> Artists
Music --> Albums
Music --> Playlists
Music --> Genres
``` ```
**View Enforcement:** **View Enforcement:**
@ -740,8 +716,7 @@ sequenceDiagram
**Database Schema Notes:** **Database Schema Notes:**
- The `user_data` table stores playback progress using Jellyfin IDs directly (as TEXT) - The `user_data` table stores playback progress using Jellyfin IDs directly (as TEXT)
- Foreign key constraint on `item_id` was removed in migration 003 to allow tracking progress for items not yet synced to local database - Playback progress can be tracked even when the full item metadata hasn't been downloaded yet
- This enables playback tracking even when the full item metadata hasn't been downloaded yet
**Resume Playback Feature:** **Resume Playback Feature:**
- When loading media for playback, the app checks local database for saved progress - When loading media for playback, the app checks local database for saved progress
@ -1004,50 +979,66 @@ pub struct ConnectivityMonitor {
### 3.8 Component Hierarchy ### 3.8 Component Hierarchy
``` ```mermaid
src/routes/ graph TD
├── +page.svelte # Login page subgraph Routes["Routes (src/routes/)"]
├── library/ LoginPage["Login Page"]
│ ├── +layout.svelte # Main layout with MiniPlayer LibLayout["Library Layout"]
│ ├── +page.svelte # Library browser (library selector) LibDetail["Album/Series Detail"]
│ ├── [id]/+page.svelte # Album/series detail MusicCategory["Music Category Landing"]
│ └── music/ # Music library structure Tracks["Tracks"]
│ ├── +page.svelte # Music category landing page Artists["Artists"]
│ ├── tracks/+page.svelte # All tracks (list view only) Albums["Albums"]
│ ├── artists/+page.svelte # Artists grid Playlists["Playlists"]
│ ├── albums/+page.svelte # Albums grid Genres["Genres"]
│ ├── playlists/+page.svelte # Playlists grid Downloads["Downloads Page"]
│ └── genres/+page.svelte # Genre browser Settings["Settings Page"]
├── downloads/ PlayerPage["Player Page"]
│ └── +page.svelte # Manage downloads (Active/Completed tabs) end
├── settings/
│ └── +page.svelte # Settings (includes download settings)
└── player/
└── [id]/+page.svelte # Full player page
src/lib/components/ subgraph PlayerComps["Player Components"]
├── Search.svelte AudioPlayer["AudioPlayer"]
├── player/ MiniPlayer["MiniPlayer"]
│ ├── AudioPlayer.svelte # Full screen audio player Controls["Controls"]
│ ├── MiniPlayer.svelte # Bottom bar mini player (auto-hides for video, includes cast button) Queue["Queue"]
│ ├── Controls.svelte # Play/pause/skip controls end
│ └── Queue.svelte # Queue list view
├── sessions/ subgraph SessionComps["Sessions Components"]
│ ├── CastButton.svelte # Cast button with session picker (integrated in MiniPlayer) CastButton["CastButton"]
│ ├── SessionPickerModal.svelte # Modal for selecting remote session SessionModal["SessionPickerModal"]
│ ├── SessionCard.svelte # Individual session display card SessionCard["SessionCard"]
│ ├── SessionsList.svelte # List of all controllable sessions SessionsList["SessionsList"]
│ └── RemoteControls.svelte # Full remote playback control UI RemoteControls["RemoteControls"]
├── downloads/ end
│ └── DownloadItem.svelte # Download list item with progress/actions
├── FavoriteButton.svelte # Reusable heart/like button subgraph LibraryComps["Library Components"]
└── library/ LibGrid["LibraryGrid"]
├── LibraryGrid.svelte # Grid of media items (supports forceGrid) LibListView["LibraryListView"]
├── LibraryListView.svelte # List view for albums/artists TrackList["TrackList"]
├── TrackList.svelte # Dedicated track list (now with showDownload prop) DownloadBtn["DownloadButton"]
├── DownloadButton.svelte # Download button with progress ring MediaCard["MediaCard"]
├── MediaCard.svelte # Individual item card end
└── AlbumView.svelte # Album detail with tracks
subgraph OtherComps["Other Components"]
Search["Search"]
FavoriteBtn["FavoriteButton"]
DownloadItem["DownloadItem"]
end
LibLayout --> PlayerComps
LibLayout --> LibDetail
MusicCategory --> Tracks
MusicCategory --> Artists
MusicCategory --> Albums
MusicCategory --> Playlists
MusicCategory --> Genres
LibDetail --> LibraryComps
Downloads --> DownloadItem
PlayerPage --> PlayerComps
MiniPlayer --> CastButton
CastButton --> SessionModal
PlayerComps --> LibraryComps
``` ```
--- ---
@ -1080,7 +1071,6 @@ sequenceDiagram
else Cache timeout or empty else Cache timeout or empty
Server-->>Hybrid: Fresh result Server-->>Hybrid: Fresh result
Hybrid-->>Rust: Return server result Hybrid-->>Rust: Return server result
Note over Hybrid: TODO: Update cache in background
end end
Rust-->>Client: SearchResult Rust-->>Client: SearchResult
@ -1967,7 +1957,7 @@ listen<DownloadEvent>('download-event', (event) => {
### 8.9 Database Schema ### 8.9 Database Schema
**downloads table** (enhanced in migration 004): **downloads table**:
```sql ```sql
CREATE TABLE downloads ( CREATE TABLE downloads (
@ -1995,139 +1985,9 @@ CREATE INDEX idx_downloads_queue
--- ---
## 9. TypeScript to Rust Migration Status ## 9. Connectivity & Network Architecture
### 9.1 Migration Overview ### 9.1 HTTP Client with Retry Logic
JellyTau has undergone a phased migration from TypeScript to Rust, moving ~3,500 lines of business logic to Rust while simplifying the TypeScript layer to thin UI wrappers.
**Approach**: Incremental migration with direct replacement
- Complete one phase at a time with full testing
- Delete TypeScript implementations after Rust validation
- Each phase is independently deployable
- No parallel implementations maintained
### 9.2 Completed Phases
#### ✅ Phase 1: HTTP Client & Connectivity Foundation (Complete)
**Created:**
- `src-tauri/src/jellyfin/http_client.rs` (289 lines)
- HTTP client with exponential backoff retry (1s, 2s, 4s)
- Error classification (Network, Authentication, Server, Client)
- Automatic retry on network/5xx errors
- `src-tauri/src/connectivity/mod.rs` (351 lines)
- Background monitoring with adaptive polling (30s online, 5s offline)
- Event emission to frontend
- Manual reachability marking
**Simplified:**
- `src/lib/stores/connectivity.ts`: 301→249 lines (-17%)
- Removed polling logic
- Now listens to Rust events
- Thin wrapper over Rust commands
**Commands Added:** 7 connectivity commands
**Impact:** Eliminated TypeScript polling/retry logic, improved battery efficiency
#### ✅ Phase 2: Repository Pattern & Data Layer (Complete)
**Created:**
- `src-tauri/src/repository/` (complete module)
- `mod.rs`: MediaRepository trait + handle-based management
- `types.rs`: Type definitions (RepoError, Library, MediaItem, etc.)
- `hybrid.rs`: Cache-first parallel racing (100ms cache timeout)
- `online.rs`: OnlineRepository (HTTP API calls)
- `offline.rs`: OfflineRepository (SQLite queries with caching)
**Replaced:**
- Deleted `src/lib/api/repository.ts` (1061 lines)
- Created `src/lib/api/repository-client.ts` (~100 lines)
- Thin wrapper with handle-based resource management
- All methods delegate to Rust commands
**Commands Added:** 27 repository commands
**Key Features:**
- Handle-based resource management (UUID handles)
- Cache-first racing: parallel cache (100ms timeout) vs server queries
- Returns cache if meaningful content, else server result
- Supports multiple concurrent repository instances
#### ✅ Phase 2.5: Database Service Abstraction (Complete)
**Created:**
- `src-tauri/src/storage/db_service.rs`
- `DatabaseService` trait with async methods
- `RusqliteService` implementation using `spawn_blocking`
- Prevents blocking Tokio async runtime
**Impact:**
- Eliminated UI freezing from blocking database operations
- All DB queries now use `spawn_blocking` thread pool
- ~1-2ms overhead per query (acceptable tradeoff)
**Migration Status:**
- ✅ Phase 1: Foundation (Complete)
- ✅ Phase 2: OfflineRepository (18 methods migrated)
- 🔄 Phase 3: Command layer (97 operations across 5 files - in progress)
#### ✅ Phase 3: Playback Mode System (Complete)
**Created:**
- `src-tauri/src/playback_mode/mod.rs`
- `PlaybackMode` enum (Local, Remote, Idle)
- `PlaybackModeManager` for mode transfers
- Transfer queue between local device and remote sessions
**Simplified:**
- `src/lib/stores/playbackMode.ts`: 303→150 lines (-50%)
- Thin wrapper calling Rust commands
- Maintains UI state only
**Commands Added:** 5 playback mode commands
**Features:**
- Seamless transfer of playback queue to remote sessions
- Position synchronization during transfer
- Automatic local player stop when transferring to remote
### 9.3 In Progress
#### 🔄 Phase 4: Authentication & Session Management
**Status:** Partially complete
- Session restoration logic migrated
- Credential storage using secure keyring
- Re-authentication flow in progress
**Target:** Simplify `src/lib/stores/auth.ts` from 616→150 lines
### 9.4 Architecture Summary
**Before Migration:**
- TypeScript: ~3,300 lines of business logic
- Rust: ~4,000 lines (player, storage, downloads)
- Total Commands: 73
**After Migration (Current):**
- TypeScript: ~800 lines (thin wrappers, UI state)
- Rust: ~8,000 lines (business logic, HTTP, repository, etc.)
- Total Commands: 80+
**Lines Eliminated:** ~2,500 lines of TypeScript business logic
**Benefits:**
- Improved performance (zero-cost abstractions)
- Better reliability (type safety, memory safety)
- Reduced battery drain (efficient async I/O)
- Easier maintenance (centralized business logic)
- No UI freezing (async database operations)
## 10. Connectivity & Network Architecture
### 10.1 HTTP Client with Retry Logic
**Location**: `src-tauri/src/jellyfin/http_client.rs` **Location**: `src-tauri/src/jellyfin/http_client.rs`
@ -2161,7 +2021,7 @@ pub enum ErrorKind {
} }
``` ```
### 10.2 Connectivity Monitor ### 9.2 Connectivity Monitor
**Location**: `src-tauri/src/connectivity/mod.rs` **Location**: `src-tauri/src/connectivity/mod.rs`
@ -2203,7 +2063,7 @@ listen<{ isReachable: boolean }>("connectivity:changed", (event) => {
}); });
``` ```
### 10.3 Network Resilience Architecture ### 9.3 Network Resilience Architecture
The connectivity system provides resilience through multiple layers: The connectivity system provides resilience through multiple layers:
@ -2218,336 +2078,9 @@ The connectivity system provides resilience through multiple layers:
- **Adaptive Polling**: Reduce polling frequency when online, increase when offline - **Adaptive Polling**: Reduce polling frequency when online, increase when offline
- **Event-Driven**: Frontend reacts to connectivity changes via events - **Event-Driven**: Frontend reacts to connectivity changes via events
## 11. Architecture Extensions
### 11.1 Native Async Database (Future)
**Future Enhancement**: Migrate from `rusqlite` + `spawn_blocking` to native async database:
```rust
// Current: spawn_blocking overhead (~1-2ms per query)
tokio::task::spawn_blocking(move || {
let conn = connection.lock().unwrap();
conn.query_row(...)
}).await?
// Future: Native async with tokio-rusqlite (zero overhead)
use tokio_rusqlite::Connection;
let conn = Connection::open(path).await?;
conn.call(|conn| {
conn.query_row(...)
}).await?
```
**Benefits:**
- Eliminate spawn_blocking overhead
- Better integration with Tokio runtime
- Improved throughput for high-frequency queries
**Migration Path:**
- DatabaseService trait already abstracts implementation
- Swap RusqliteService for TokioRusqliteService
- No changes to command layer needed
### 11.2 Remote Session Control (Existing Feature - Documented)
Remote session control allows JellyTau to discover and control playback on other Jellyfin clients (TVs, web browsers, etc.). This enables cast-like functionality where mobile devices become remote controls.
#### 11.2.1 Architecture Overview
```mermaid
flowchart TB
subgraph JellyTauApp["JellyTau App"]
SessionsStore["SessionsStore<br/>- sessions[]<br/>- activeId"]
RemoteControl["RemoteControl<br/>- play/pause<br/>- seek<br/>- volume<br/>- playItem"]
SessionsStore -->|"Select"| RemoteControl
end
subgraph JellyfinServer["Jellyfin Server"]
GetSessions["GET /Sessions"]
ActiveSessions["Active Sessions<br/>- Jellyfin Web (Chrome)<br/>- Jellyfin Android TV<br/>- Jellyfin for Roku"]
GetSessions --> ActiveSessions
end
SessionsStore <-->|"Poll (5-10s)"| GetSessions
RemoteControl -->|"Commands"| ActiveSessions
```
#### 11.2.2 Jellyfin Sessions API
**Endpoints:**
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/Sessions` | List all active sessions |
| POST | `/Sessions/{id}/Playing` | Start playback of item(s) |
| POST | `/Sessions/{id}/Playing/PlayPause` | Toggle play/pause |
| POST | `/Sessions/{id}/Playing/Stop` | Stop playback |
| POST | `/Sessions/{id}/Playing/Seek` | Seek to position |
| POST | `/Sessions/{id}/Command/SetVolume` | Set volume (0-100) |
| POST | `/Sessions/{id}/Command/Mute` | Mute |
| POST | `/Sessions/{id}/Command/Unmute` | Unmute |
| POST | `/Sessions/{id}/Command/VolumeUp` | Volume up |
| POST | `/Sessions/{id}/Command/VolumeDown` | Volume down |
**Session Response Schema:**
```typescript
interface Session {
id: string;
userId: string;
userName: string;
client: string; // "Jellyfin Web", "Jellyfin Android TV", etc.
deviceName: string; // "Living Room TV", "Chrome - Windows"
deviceId: string;
applicationVersion: string;
isActive: boolean;
supportsMediaControl: boolean;
supportsRemoteControl: boolean;
playState: {
positionTicks: number;
canSeek: boolean;
isPaused: boolean;
isMuted: boolean;
volumeLevel: number; // 0-100
repeatMode: string;
} | null;
nowPlayingItem: MediaItem | null;
playableMediaTypes: string[]; // ["Audio", "Video"]
supportedCommands: string[]; // ["PlayPause", "Seek", "SetVolume", ...]
}
```
#### 11.2.3 API Layer
**Location**: `src/lib/api/sessions.ts`
```typescript
export class SessionsApi {
constructor(private client: JellyfinClient) {}
async getSessions(): Promise<Session[]> {
return this.client.get<Session[]>('/Sessions', {
params: { controllableByUserId: this.client.userId }
});
}
async playOnSession(sessionId: string, itemIds: string[], startIndex = 0): Promise<void> {
await this.client.post(`/Sessions/${sessionId}/Playing`, {
itemIds,
startIndex,
playCommand: 'PlayNow'
});
}
async sendCommand(sessionId: string, command: SessionCommand): Promise<void> {
await this.client.post(`/Sessions/${sessionId}/Playing/${command}`);
}
async setVolume(sessionId: string, volume: number): Promise<void> {
await this.client.post(`/Sessions/${sessionId}/Command/SetVolume`, {
Arguments: { Volume: Math.round(volume) }
});
}
async seek(sessionId: string, positionTicks: number): Promise<void> {
await this.client.post(`/Sessions/${sessionId}/Playing/Seek`, {
seekPositionTicks: positionTicks
});
}
}
type SessionCommand = 'PlayPause' | 'Stop' | 'Pause' | 'Unpause' |
'NextTrack' | 'PreviousTrack' | 'Mute' | 'Unmute';
```
#### 11.2.4 Sessions Store
**Location**: `src/lib/stores/sessions.ts`
```typescript
interface SessionsState {
sessions: Session[];
activeSessionId: string | null;
isPolling: boolean;
lastUpdated: Date | null;
}
function createSessionsStore() {
const { subscribe, update } = writable<SessionsState>({
sessions: [],
activeSessionId: null,
isPolling: false,
lastUpdated: null
});
let pollInterval: ReturnType<typeof setInterval> | null = null;
return {
subscribe,
startPolling(api: SessionsApi, intervalMs = 5000) {
this.stopPolling();
pollInterval = setInterval(() => this.refresh(api), intervalMs);
this.refresh(api); // Immediate first fetch
},
stopPolling() {
if (pollInterval) clearInterval(pollInterval);
},
async refresh(api: SessionsApi) {
const sessions = await api.getSessions();
update(s => ({
...s,
sessions: sessions.filter(s => s.supportsRemoteControl),
lastUpdated: new Date()
}));
},
selectSession(sessionId: string | null) {
update(s => ({ ...s, activeSessionId: sessionId }));
}
};
}
```
#### 11.2.5 Android MediaRouter Integration (IR-021)
On Android, when controlling a remote session's volume, JellyTau integrates with the system audio control panel via MediaRouter API:
```mermaid
flowchart TB
User["User presses volume button"]
VolumePanel["System Volume Panel<br/>appears showing<br/>remote session name"]
MediaRouter["MediaRouter.Callback<br/>onRouteVolumeChanged()"]
SessionsApi["SessionsApi.setVolume()<br/>POST /Sessions/{id}/<br/>Command/SetVolume"]
User --> VolumePanel
VolumePanel --> MediaRouter
MediaRouter --> SessionsApi
```
**Kotlin Implementation** (`JellyTauMediaRouterCallback.kt`):
```kotlin
class JellyTauMediaRouterCallback(
private val sessionsApi: SessionsApi
) : MediaRouter.Callback() {
private var selectedRoute: MediaRouter.RouteInfo? = null
override fun onRouteSelected(router: MediaRouter, route: RouteInfo) {
selectedRoute = route
// Update UI to show remote session controls
}
override fun onRouteVolumeChanged(router: MediaRouter, route: RouteInfo) {
selectedRoute?.let { selected ->
if (route == selected) {
val volume = route.volume
val sessionId = route.extras?.getString("sessionId")
sessionId?.let {
// Send volume to Jellyfin session
coroutineScope.launch {
sessionsApi.setVolume(it, volume)
}
}
}
}
}
}
```
**MediaRouteProvider** - Exposes Jellyfin sessions as cast-like routes:
```kotlin
class JellyfinMediaRouteProvider(context: Context) : MediaRouteProvider(context) {
fun updateSessionRoutes(sessions: List<Session>) {
val routes = sessions
.filter { it.supportsRemoteControl }
.map { session ->
MediaRouteDescriptor.Builder(session.id, session.deviceName)
.setDescription(session.client)
.setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(100)
.setVolume(session.playState?.volumeLevel ?: 100)
.addControlFilter(IntentFilter(MediaControlIntent.ACTION_PLAY))
.setExtras(Bundle().apply {
putString("sessionId", session.id)
})
.build()
}
setDescriptor(MediaRouteProviderDescriptor.Builder()
.addRoutes(routes)
.build())
}
}
```
#### 11.2.6 UI Components
**CastButton** (`src/lib/components/sessions/CastButton.svelte`) - Cast button for MiniPlayer:
- Integrated into MiniPlayer component (visible on all screen sizes)
- Shows cast icon that changes when connected to remote session
- Displays badge with number of available sessions
- Auto-polls for sessions every 15 seconds
- Opens SessionPickerModal when clicked
- Visual indicators:
- Gray when disconnected, purple when connected
- Badge shows count of available devices
- Green dot indicator when actively casting
**SessionPickerModal** (`src/lib/components/sessions/SessionPickerModal.svelte`) - Modal for selecting cast device:
- Lists controllable sessions with device name and client type
- Shows currently playing item (if any) for each session
- Highlights currently selected/connected session
- Device type icons (TV, Web, Mobile, Generic)
- "Disconnect" option when already connected
- Empty state with refresh button
- Responsive: slides up on mobile, centered on desktop
**SessionCard** (`src/lib/components/sessions/SessionCard.svelte`) - Individual session display:
- Device name and client information
- Now playing preview with artwork
- Play/pause state indicator
- Position and volume display
**SessionsList** (`src/lib/components/sessions/SessionsList.svelte`) - List of all sessions:
- Filters to show only controllable sessions
- Refresh button for manual updates
- Loading and error states
- Empty state messaging
**RemoteControls** (`src/lib/components/sessions/RemoteControls.svelte`) - Full remote playback control:
- Uses polling data for play state (position, volume, etc.)
- Sends commands via SessionsApi
- Shows "Controlling: {deviceName}" header
- Full playback controls: play/pause, next/previous, stop
- Seek bar with position display (if supported)
- Volume slider
- Empty state when no media playing on remote session
### 11.3 MPRIS Integration (Linux - Future)
```rust
// Future: D-Bus media controls
pub struct MprisController {
connection: Connection,
player: Arc<Mutex<PlayerController>>,
}
impl MprisController {
fn register_handlers(&self) {
// Handle PlayPause, Next, Previous from system
}
}
```
--- ---
## 12. Offline Database Design ## 10. Offline Database Design
### 12.1 Entity Relationship Diagram ### 12.1 Entity Relationship Diagram
@ -3176,7 +2709,7 @@ src-tauri/src/storage/
--- ---
## 13. File Structure Summary ## 11. File Structure Summary
``` ```
src-tauri/src/ src-tauri/src/
@ -3286,9 +2819,9 @@ src/lib/
--- ---
## 14. Security ## 12. Security
### 14.1 Authentication Token Storage ### 12.1 Authentication Token Storage
Access tokens are **not** stored in the SQLite database. Instead, they are stored using platform-native secure storage: Access tokens are **not** stored in the SQLite database. Instead, they are stored using platform-native secure storage:
@ -3314,7 +2847,7 @@ jellytau::{server_id}::{user_id}::access_token
- System keyrings provide OS-level encryption and access control - System keyrings provide OS-level encryption and access control
- Fallback ensures functionality on minimal systems without a keyring daemon - Fallback ensures functionality on minimal systems without a keyring daemon
### 14.2 Secure Storage Module ### 12.2 Secure Storage Module
**Location**: `src-tauri/src/secure_storage/` (planned) **Location**: `src-tauri/src/secure_storage/` (planned)
@ -3330,7 +2863,7 @@ pub struct KeyringStorage; // Uses keyring crate
pub struct EncryptedFileStorage; // AES-256-GCM fallback pub struct EncryptedFileStorage; // AES-256-GCM fallback
``` ```
### 14.3 Network Security ### 12.3 Network Security
| Aspect | Implementation | | Aspect | Implementation |
|--------|----------------| |--------|----------------|
@ -3339,7 +2872,7 @@ pub struct EncryptedFileStorage; // AES-256-GCM fallback
| Token Transmission | Bearer token in `Authorization` header only | | Token Transmission | Bearer token in `Authorization` header only |
| Token Refresh | Handled by Jellyfin server (long-lived tokens) | | Token Refresh | Handled by Jellyfin server (long-lived tokens) |
### 14.4 Local Data Protection ### 12.4 Local Data Protection
| Data Type | Protection | | Data Type | Protection |
|-----------|------------| |-----------|------------|
@ -3348,7 +2881,7 @@ pub struct EncryptedFileStorage; // AES-256-GCM fallback
| Downloaded Media | Filesystem permissions only | | Downloaded Media | Filesystem permissions only |
| Cached Thumbnails | Filesystem permissions only | | Cached Thumbnails | Filesystem permissions only |
### 14.5 Security Considerations ### 12.5 Security Considerations
1. **No Secrets in SQLite**: The database contains only non-sensitive metadata 1. **No Secrets in SQLite**: The database contains only non-sensitive metadata
2. **Token Isolation**: Each user/server combination has a separate token entry 2. **Token Isolation**: Each user/server combination has a separate token entry

File diff suppressed because it is too large Load Diff

View File

@ -1,219 +0,0 @@
# UX Implementation Gaps
This document tracks inconsistencies between the documented UX flows ([UXFlows.md](UXFlows.md)) and the actual implementation.
**Last Updated:** 2026-01-03
---
## Recently Resolved ✅
### 1. Route Structure Mismatch ✅ RESOLVED
**Was Documented:**
- Login screen at `/`
- Home/Library landing at `/library` (after login)
**Actual Implementation:**
- Login screen at [/login](src/routes/login/+page.svelte)
- Home page with carousels at [/](src/routes/+page.svelte)
- Library selector at [/library](src/routes/library/+page.svelte)
**Resolution:** Updated UXFlows.md Sections 1.1, 2.1, 2.2, 8.2 to reflect actual routes
---
### 3. Audio Player Back Button Navigation ✅ RESOLVED
**Issue:**
- Audio player close button simply closed the modal without respecting browser history
- Users expected back button to return to previous page (e.g., album view)
**Resolution:**
- Updated AudioPlayer onClose handler to call `window.history.back()` ([library/+layout.svelte:212-215](src/routes/library/+layout.svelte))
- Back button now returns user to the page they were on before opening full player
- Updated UXFlows.md Section 3.3 to document browser history navigation
**Impact:** Better UX - natural back button behavior matches user expectations
---
### 4. Video Player Touch Gestures ✅ RESOLVED
**Was Documented:**
- Double tap left: Rewind 10 seconds
- Double tap right: Forward 10 seconds
- Swipe up/down: Adjust brightness (left) or volume (right)
**Implementation:**
- ✅ Double-tap detection on left/right sides of screen ([VideoPlayer.svelte:228-319](src/lib/components/player/VideoPlayer.svelte))
- ✅ Animated visual feedback showing "-10" or "+10" with fade-out animation
- ✅ Swipe gesture detection for vertical swipes (>50px minimum)
- ✅ Left side swipe: Brightness control (0.3-1.7x) with CSS filter
- ✅ Right side swipe: Volume control (0-100%) with real-time adjustment
- ✅ Visual indicators showing current brightness/volume level during swipe
- ✅ Updated UXFlows.md Section 4.2 to document all gestures
**Impact:** Excellent mobile video UX - touch-optimized controls match YouTube/Netflix patterns
---
## Remaining Issues
### Critical Issues
None! All critical UX issues have been resolved. 🎉
---
### Low Priority Issues
#### 1. MiniPlayer Shows More Controls Than Documented
**Documented:**
- Shows: artwork, title, artist, play/pause, next, favorite
**Actual Implementation:**
- Shows: artwork, title, artist, play/pause, next, previous, shuffle, repeat, volume (desktop), cast button, favorite (desktop)
- Much richer control set than documented
**Impact:** Low - This is actually better than documented
**Fix Required:** Update UXFlows.md Section 3.1 to document full control set
---
#### 2. Search Results Display Simplified
**Documented (Section 6.1):**
- Results grouped by type: Songs, Albums, Artists, Movies, Episodes
- "See all (23)" expandable sections
**Actual Implementation:**
- [Search page](src/routes/search/+page.svelte) uses `LibraryGrid` component
- No visual grouping by type shown in code
- Simplified single-list results
**Impact:** Low - Functional but less organized than documented
**Fix Required:**
- Option A: Implement grouped results display
- Option B: Update documentation to match simplified implementation
---
## Undocumented Features
### 3. Sessions/Remote Control Feature
**Not in UXFlows.md:**
- Route exists: [/sessions](src/routes/sessions/+page.svelte)
- Appears to be for remote session control
- Cast button visible in MiniPlayer
**Impact:** None - Feature exists but isn't documented
**Fix Required:** Document sessions feature in UXFlows.md (Section 8.2 exists in SoftwareArchitecture.md but not in UX flows)
---
### 4. URL Query Parameters for Queue Context
**Not in UXFlows.md:**
- Uses `?queue=parent:{id}&shuffle=true` in URLs
- Enables queue restoration and context tracking
**Impact:** None - Implementation detail
**Fix Required:** Optional - add technical note in UXFlows.md about queue URL params
---
## Consistent Implementations ✅
These areas match the documentation exactly:
- ✅ **Resume Dialog** - Correctly implements 30s/90% threshold ([player/[id]/+page.svelte:84-86](src/routes/player/[id]/+page.svelte))
- ✅ **Audio Playback** - Uses `player_play_queue` command as documented
- ✅ **MiniPlayer Hiding** - Correctly hides for video content
- ✅ **Keyboard Shortcuts** - Video player keyboard controls work as documented
- ✅ **Music Library Structure** - Category pages match documentation
---
## Recommendations by Priority
### High Priority
1. ~~**Update UXFlows.md Route Structure**~~ ✅ **COMPLETED**
- ✅ Corrected login route to `/login`
- ✅ Clarified `/` is home page, `/library` is library selector
- ✅ Updated Sections 1.1, 2.1, 2.2, 7.2, 8.1, 8.2
2. ~~**Add Downloads Navigation**~~ ✅ **COMPLETED**
- ✅ Added Downloads link to header navigation (desktop)
- ✅ Added Downloads icon button to header (all screen sizes)
- ✅ Updated UXFlows.md to document navigation paths
### Medium Priority
3. ~~**Add Settings Navigation Link**~~ ✅ **COMPLETED**
- ✅ Added Settings link to desktop header navigation
- ✅ Added Settings to mobile overflow menu
- ✅ Updated UXFlows.md Section 8.1
4. ~~**Implement Video Player Touch Gestures**~~ ✅ **COMPLETED**
- ✅ Implemented double-tap detection (left/right sides)
- ✅ Implemented swipe gestures (brightness + volume control)
- ✅ Added visual feedback animations
- ✅ Updated UXFlows.md Section 4.2
### Low Priority
5. **Document MiniPlayer Full Feature Set** (#4)
- Update Section 3.1 to show all controls
- Document desktop vs mobile differences
6. **Document Sessions Feature** (#7)
- Add UX flow for remote session selection
- Explain cast button behavior
- Link to architecture documentation
7. **Enhance Search Results Display** (#5)
- Consider implementing grouped results as documented
- Or update docs to match current implementation
---
## Quick Fix Checklist
For immediate documentation updates:
- [x] Fix login route in UXFlows.md (Section 2.1): `/``/login`
- [x] Fix home route in UXFlows.md (Section 2.1): `/library``/`
- [x] Document actual navigation structure (Section 1.1) ✅
- [x] Update Downloads navigation (Section 7.2) ✅
- [x] Update Settings navigation (Section 8.1) ✅
- [x] Update logout flow (Section 8.2) ✅
- [ ] Update video controls documentation (Section 4.2) to match keyboard implementation
- [ ] Document MiniPlayer cast button (Section 3.1)
- [ ] Add Sessions feature UX flow (new section)
For code implementation:
- [x] Add Downloads link to header navigation (desktop) ✅
- [x] Add Downloads icon button to header (all screen sizes) ✅
- [x] Add Settings link to header navigation ✅
- [x] Implement mobile overflow menu (Android-style) ✅
- [x] Fix audio player back button to use browser history ✅
- [ ] (Optional) Implement video player touch gestures
- [ ] (Optional) Implement grouped search results
---
## Notes
- The actual implementation is generally **more feature-rich** than documented (MiniPlayer controls, keyboard shortcuts)
- Main gaps are in **mobile navigation accessibility** (missing More tab)
- Most core functionality **matches or exceeds** documentation
- Video player touch gestures are the only **missing feature** that was explicitly documented