diff --git a/README.md b/README.md index ef60285..152cc53 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A cross-platform Jellyfin client built with Tauri, SvelteKit, and TypeScript. | UR-003 | Play videos | High | Done | | UR-004 | Play audio uninterrupted | High | Done | | 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 | In Progress | +| UR-006 | Control media when device is on lock screen or via BLE headsets | Medium | Done | | UR-007 | Navigate media in library | High | Done | | UR-008 | Search media across libraries | High | Done | | UR-009 | Connect to Jellyfin to access media | High | Done | @@ -33,8 +33,8 @@ A cross-platform Jellyfin client built with Tauri, SvelteKit, and TypeScript. | UR-017 | Like or unlike audio, albums, movies, etc. | Medium | Done | | UR-018 | Choose to download series, albums, songs, artist discography | Medium | Done | | UR-019 | Resume playback from where you left off (movies, shows, albums) | High | Done | -| UR-020 | Select subtitles for video content | High | Planned | -| UR-021 | Select audio track for video content | High | Planned | +| UR-020 | Select subtitles for video content | High | Done | +| UR-021 | Select audio track for video content | High | Done | | 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 and configurable episode limit | Medium | Done | | UR-024 | View recently added content on server | Medium | Done | @@ -80,7 +80,7 @@ External system integrations and platform-specific implementations. | 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-015 | Jellyfin API client for playback progress reporting | API | UR-019, UR-025 | Done | -| IR-016 | Jellyfin API client for subtitle/audio track info | API | UR-020, UR-021 | Planned | +| IR-016 | Jellyfin API client for subtitle/audio track info | API | UR-020, UR-021 | Done | | IR-017 | Jellyfin API client for transcoding parameters | API | UR-022 | Planned | | IR-018 | libmpv subtitle rendering and selection | Playback | UR-020 | Planned | | IR-019 | libmpv audio track selection | Playback | UR-021 | Planned | @@ -102,8 +102,8 @@ API endpoints and data contracts required for Jellyfin integration. | JA-005 | Get item details and metadata | Items | UR-007 | Done | | JA-006 | Search across libraries | Items | UR-008 | Done | | JA-007 | Get playback info and stream URL | MediaInfo | UR-003, UR-004 | Done | -| JA-008 | Get available subtitles for item | MediaInfo | UR-020 | Planned | -| JA-009 | Get available audio tracks for item | MediaInfo | UR-021 | Planned | +| JA-008 | Get available subtitles for item | MediaInfo | UR-020 | Done | +| JA-009 | Get available audio tracks for item | MediaInfo | UR-021 | Done | | JA-010 | Report playback start | Sessions | UR-025 | Done | | JA-011 | Report playback progress (periodic) | Sessions | UR-025 | Done | | JA-012 | Report playback stopped | Sessions | UR-025 | Done | @@ -115,17 +115,17 @@ API endpoints and data contracts required for Jellyfin integration. | JA-018 | Remove item from favorites | UserData | UR-017 | Done | | JA-019 | Get/create/update playlists | Playlists | UR-014 | Planned | | JA-020 | Add/remove items from playlist | Playlists | UR-014 | Planned | -| JA-021 | Get active sessions list | Sessions | UR-010 | Planned | -| JA-022 | Send playback commands to remote session (play/pause/stop) | Sessions | UR-010 | Planned | -| JA-023 | Send seek command to remote session | Sessions | UR-010 | Planned | -| JA-024 | Send next/previous track commands to remote session | Sessions | UR-010 | Planned | -| JA-025 | Play specific item on remote session | Sessions | UR-010 | Planned | -| JA-026 | Send volume/mute commands to remote session | Sessions | UR-010 | Planned | +| JA-021 | Get active sessions list | Sessions | UR-010 | Done | +| JA-022 | Send playback commands to remote session (play/pause/stop) | Sessions | UR-010 | Done | +| JA-023 | Send seek command to remote session | Sessions | UR-010 | Done | +| JA-024 | Send next/previous track commands to remote session | Sessions | UR-010 | Done | +| JA-025 | Play specific item on remote session | Sessions | UR-010 | Done | +| JA-026 | Send volume/mute commands to remote session | Sessions | UR-010 | Done | | JA-027 | Get transcoding options | MediaInfo | UR-022 | Planned | | JA-028 | Get image/artwork URLs | Images | UR-007 | Done | -| JA-029 | Get cast/crew for item (actors, directors) | Items | UR-035 | Planned | -| JA-030 | Get person details and filmography | Persons | UR-036 | Planned | -| JA-031 | Get items by person (actor/director filmography) | Items | UR-036 | Planned | +| JA-029 | Get cast/crew for item (actors, directors) | Items | UR-035 | Done | +| JA-030 | Get person details and filmography | Persons | UR-036 | Done | +| JA-031 | Get items by person (actor/director filmography) | Items | UR-036 | Done | ### 2.3 Development Requirements @@ -155,8 +155,8 @@ Internal architecture, components, and application logic. | DR-020 | Queue management UI (add, remove, reorder) | UI | UR-015 | Done | | DR-021 | Like/favorite functionality on media items | UI | UR-017 | Done | | DR-022 | Resume position tracking and restoration on play | Player | UR-019 | Done | -| 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-023 | Subtitle selection UI in video player | UI | UR-020 | Done | +| DR-024 | Audio track selection UI in video player | UI | UR-021 | Done | | DR-025 | Quality/transcoding settings UI | UI | UR-022 | Planned | | DR-026 | "Continue Watching" / "Next Up" home section | UI | UR-023 | Done | | DR-027 | "Recently Added" home section | UI | UR-024 | Done | diff --git a/SoftwareArchitecture.md b/SoftwareArchitecture.md index 5dd4625..87a0d20 100644 --- a/SoftwareArchitecture.md +++ b/SoftwareArchitecture.md @@ -551,16 +551,23 @@ The `PlayerController` orchestrates playback: pub struct PlayerController { backend: Arc>>, queue: Arc>, - volume: f32, muted: bool, + sleep_timer: Arc>, + autoplay_settings: Arc>, + autoplay_episode_count: Arc>, // Session-based counter + repository: Arc>>>, + event_emitter: Arc>>>, + // ... other fields } ``` **Key Methods:** -- `play_item(item)`: Load and play single item -- `play_queue(items, start_index)`: Load queue and start playback -- `next()` / `previous()`: Queue navigation +- `play_item(item)`: Load and play single item (resets autoplay counter) +- `play_queue(items, start_index)`: Load queue and start playback (resets autoplay counter) +- `next()` / `previous()`: Queue navigation (resets autoplay counter) - `toggle_shuffle()` / `cycle_repeat()`: Mode changes +- `set_sleep_timer(mode)` / `cancel_sleep_timer()`: Sleep timer control +- `on_playback_ended()`: Autoplay decision making (checks sleep timer, episode limit, queue) ### 2.9 Tauri Commands @@ -584,6 +591,13 @@ pub struct PlayerController { | `player_get_queue` | - | `QueueStatus` | | `player_get_session` | - | `MediaSessionType` | | `player_dismiss_session` | - | `()` | +| `player_set_sleep_timer` | `mode: SleepTimerMode` | `()` | +| `player_cancel_sleep_timer` | - | `()` | +| `player_set_video_settings` | `settings: VideoSettings` | `VideoSettings` | +| `player_get_video_settings` | - | `VideoSettings` | +| `player_set_autoplay_settings` | `settings: AutoplaySettings` | `AutoplaySettings` | +| `player_get_autoplay_settings` | - | `AutoplaySettings` | +| `player_on_playback_ended` | - | `()` | --- @@ -998,9 +1012,12 @@ graph TD subgraph PlayerComps["Player Components"] AudioPlayer["AudioPlayer"] + VideoPlayer["VideoPlayer"] MiniPlayer["MiniPlayer"] Controls["Controls"] Queue["Queue"] + SleepTimerModal["SleepTimerModal"] + SleepTimerIndicator["SleepTimerIndicator"] end subgraph SessionComps["Sessions Components"] @@ -1019,6 +1036,10 @@ graph TD MediaCard["MediaCard"] end + subgraph CommonComps["Common Components"] + ScrollPicker["ScrollPicker"] + end + subgraph OtherComps["Other Components"] Search["Search"] FavoriteBtn["FavoriteButton"] @@ -1038,6 +1059,7 @@ graph TD MiniPlayer --> CastButton CastButton --> SessionModal + SleepTimerModal --> ScrollPicker PlayerComps --> LibraryComps ``` @@ -1067,7 +1089,127 @@ function handleTouchStart(e: TouchEvent) { The MiniPlayer's next/previous buttons are enabled based on `appState.hasNext`/`hasPrevious`, which are updated by `playerEvents.ts` calling `invoke("player_get_queue")` on every `StateChanged` event from the backend. -### 3.10 Player Page Navigation Guard +### 3.10 Sleep Timer Architecture + +**Location**: `src-tauri/src/player/sleep_timer.rs`, `src-tauri/src/player/mod.rs` + +**TRACES**: UR-026 | DR-029 + +The sleep timer supports three modes for stopping playback: + +```rust +#[serde(tag = "kind", rename_all = "camelCase")] +pub enum SleepTimerMode { + Off, + Time { end_time: i64 }, // Unix timestamp in milliseconds + EndOfTrack, // Stop after current track/episode + Episodes { remaining: u32 }, // Stop after N more episodes +} +``` + +**Timer Modes:** + +| Mode | Trigger | How It Stops | +|------|---------|-------------| +| Time | User selects 15/30/45/60 min via roller UI | Background timer thread stops backend when `remaining_seconds == 0`; also checked at track boundaries in `on_playback_ended()` | +| EndOfTrack | User clicks "End of current track" | Checked in `on_playback_ended()`, returns `AutoplayDecision::Stop` | +| Episodes | User selects 1-10 episodes | `decrement_episode()` in `on_playback_ended()`, stops when counter reaches 0 | + +**Time-Based Timer Flow:** + +```mermaid +sequenceDiagram + participant UI as SleepTimerModal + participant Store as sleepTimer store + participant Rust as PlayerController + participant Thread as Timer Thread + participant Backend as PlayerBackend + + UI->>Store: setTimeTimer(30) + Store->>Rust: invoke("player_set_sleep_timer", {mode}) + Rust->>Rust: Set SleepTimerMode::Time { end_time } + Rust->>UI: Emit SleepTimerChanged event + + loop Every 1 second + Thread->>Thread: update_remaining_seconds() + Thread->>UI: Emit SleepTimerChanged (countdown) + alt remaining_seconds == 0 + Thread->>Backend: stop() + Thread->>UI: Emit SleepTimerChanged (Off) + end + end +``` + +**Frontend Components:** + +- **ScrollPicker** (`src/lib/components/common/ScrollPicker.svelte`): Reusable scroll-wheel picker using CSS `scroll-snap-type: y mandatory`. Configurable items, visible count, and item height. Used by SleepTimerModal for time selection. +- **SleepTimerModal** (`src/lib/components/player/SleepTimerModal.svelte`): Modal with three sections — time picker (roller), end of track button, episode counter. Time section uses ScrollPicker with 15/30/45/60 min options. Accepts optional `mediaType` prop to override queue-based detection (used by VideoPlayer since video playback clears the audio queue). +- **SleepTimerIndicator** (`src/lib/components/player/SleepTimerIndicator.svelte`): Compact indicator showing active timer status with countdown. +- **Sleep buttons**: Clock icon buttons on AudioPlayer header, Controls bar, MiniPlayer, and VideoPlayer control bar. Shows clock icon when inactive, SleepTimerIndicator when active. + +**Key Design Decisions:** + +1. **All logic in Rust**: Frontend only displays state and invokes commands +2. **Background timer thread**: Handles time-based countdown independently of track boundaries +3. **Dual stop mechanism for Time mode**: Timer thread stops mid-track; `on_playback_ended()` catches edge case at track boundary +4. **Event-driven UI updates**: Timer thread emits `SleepTimerChanged` every second for countdown display + +### 3.11 Auto-Play Episode Limit + +**Location**: `src-tauri/src/player/mod.rs`, `src-tauri/src/player/autoplay.rs`, `src-tauri/src/settings.rs` + +**TRACES**: UR-023 | DR-049 + +Limits how many episodes auto-play consecutively before requiring manual intervention. + +**Settings:** + +```rust +// In AutoplaySettings (runtime, in PlayerController) +pub struct AutoplaySettings { + pub enabled: bool, + pub countdown_seconds: u32, + pub max_episodes: u32, // 0 = unlimited +} + +// In VideoSettings (persisted, settings page) +pub struct VideoSettings { + pub auto_play_next_episode: bool, + pub auto_play_countdown_seconds: u32, + pub auto_play_max_episodes: u32, // 0 = unlimited +} +``` + +**Session-Based Counter:** + +The `autoplay_episode_count` field in `PlayerController` tracks consecutive auto-played episodes: + +- **Incremented**: In `on_playback_ended()` when auto-playing next episode +- **Reset**: On any manual user action (`play_item()`, `play_queue()`, `next()`, `previous()`) +- **Limit check**: When `max_episodes > 0` and `count >= max_episodes`, the popup shows with `auto_advance: false` — user must manually click "Play Now" to continue + +```mermaid +flowchart TB + PlaybackEnded["on_playback_ended()"] --> CheckEpisode{"Is video
episode?"} + CheckEpisode -->|"No"| AudioFlow["Audio queue logic"] + CheckEpisode -->|"Yes"| FetchNext["Fetch next episode"] + FetchNext --> IncrementCount["increment_autoplay_count()"] + IncrementCount --> CheckLimit{"max_episodes > 0
AND count >= max?"} + CheckLimit -->|"No"| ShowPopup["ShowNextEpisodePopup
auto_advance: true"] + CheckLimit -->|"Yes"| ShowPopupManual["ShowNextEpisodePopup
auto_advance: false"] + ShowPopupManual --> UserClick["User clicks 'Play Now'"] + UserClick --> PlayItem["play_item() → resets counter"] +``` + +**Settings Sync:** + +`VideoSettings` (settings page) and `AutoplaySettings` (PlayerController runtime) are synced via `player_set_video_settings`, which updates both the `VideoSettingsWrapper` state and calls `controller.set_autoplay_settings()`. + +**Database**: Migration 016 adds `autoplay_max_episodes INTEGER DEFAULT 0` to `user_player_settings`. + +**Settings UI**: Button grid with options: Unlimited, 1, 2, 3, 5, 10 episodes. Visible only when auto-play is enabled. + +### 3.12 Player Page Navigation Guard **Location**: `src/routes/player/[id]/+page.svelte` diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 3590f6a..1d4534c 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -1,38 +1,37 @@ -🔍 Extracting TRACES from codebase... # Code Traceability Matrix -**Generated:** 2/13/2026, 11:49:08 PM +**Generated:** 2/28/2026, 8:39:19 PM ## Summary -- **Total Files Scanned:** 185 -- **Total TRACES Found:** 72 +- **Total Files Scanned:** 208 +- **Total TRACES Found:** 117 - **Requirements Covered:** - - User Requirements (UR): 23 - - Integration Requirements (IR): 5 - - Development Requirements (DR): 28 - - Jellyfin API Requirements (JA): 0 + - User Requirements (UR): 32 + - Integration Requirements (IR): 8 + - Development Requirements (DR): 40 + - Jellyfin API Requirements (JA): 19 ## Requirements by Type ### User Requirements (UR) ``` -UR-002, UR-003, UR-004, UR-005, UR-007, UR-008, UR-009, UR-010, UR-011, UR-012, UR-013, UR-015, UR-016, UR-017, UR-018, UR-019, UR-023, UR-024, UR-025, UR-026, UR-029, UR-030, UR-034 +UR-002, UR-003, UR-004, UR-005, UR-007, UR-008, UR-009, UR-010, UR-011, UR-012, UR-013, UR-015, UR-016, UR-017, UR-018, UR-019, UR-020, UR-021, UR-023, UR-024, UR-025, UR-026, UR-028, UR-029, UR-030, UR-031, UR-032, UR-033, UR-034, UR-035, UR-036, UR-039 ``` ### Integration Requirements (IR) ``` -IR-003, IR-004, IR-009, IR-012, IR-014 +IR-003, IR-004, IR-009, IR-010, IR-011, IR-012, IR-013, IR-014 ``` ### Development Requirements (DR) ``` -DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, DR-013, DR-014, DR-015, DR-016, DR-017, DR-018, DR-020, DR-021, DR-026, DR-027, DR-028, DR-029, DR-033, DR-037, DR-038, DR-039, DR-047, DR-048 +DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-010, DR-011, DR-012, DR-013, DR-014, DR-015, DR-016, DR-017, DR-018, DR-020, DR-021, DR-023, DR-024, DR-026, DR-027, DR-028, DR-029, DR-032, DR-033, DR-034, DR-035, DR-036, DR-037, DR-038, DR-039, DR-040, DR-041, DR-044, DR-045, DR-047, DR-048, DR-050 ``` ### Jellyfin API Requirements (JA) ``` - +JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-017, JA-021, JA-022, JA-023, JA-024, JA-025, JA-026, JA-029, JA-030, JA-031 ``` ## Detailed Mapping @@ -58,7 +57,7 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### IR-009 -**Locations:** 4 file(s) +**Locations:** 5 file(s) - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L6) - **Line:** 6 @@ -69,9 +68,31 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L296) - **Line:** 296 - **Context:** `Unknown` -- **File:** [`src/lib/services/deviceId.test.ts`](src/lib/services/deviceId.test.ts#L4) +- **File:** [`src/lib/stores/auth.test.ts`](src/lib/stores/auth.test.ts#L4) - **Line:** 4 - **Context:** `Unknown` +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### IR-010 + +**Locations:** 2 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/repository/online.rs`](src-tauri/src/repository/online.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### IR-011 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### IR-012 @@ -81,9 +102,17 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **Line:** 9 - **Context:** `Unknown` +### IR-013 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/storage/schema.rs`](src-tauri/src/storage/schema.rs#L3) + - **Line:** 3 + - **Context:** `Unknown` + ### IR-014 -**Locations:** 4 file(s) +**Locations:** 5 file(s) - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L6) - **Line:** 6 @@ -94,13 +123,16 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L466) - **Line:** 466 - **Context:** `Unknown` -- **File:** [`src/lib/services/deviceId.test.ts`](src/lib/services/deviceId.test.ts#L4) +- **File:** [`src/lib/stores/auth.test.ts`](src/lib/stores/auth.test.ts#L4) - **Line:** 4 - **Context:** `Unknown` +- **File:** [`src-tauri/src/credentials.rs`](src-tauri/src/credentials.rs#L10) + - **Line:** 10 + - **Context:** `Unknown` ### DR-001 -**Locations:** 10 file(s) +**Locations:** 11 file(s) - **File:** [`src/lib/stores/player.ts`](src/lib/stores/player.ts#L8) - **Line:** 8 @@ -108,6 +140,9 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/stores/player.ts`](src/lib/stores/player.ts#L29) - **Line:** 29 - **Context:** `Unknown` +- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L8) - **Line:** 8 - **Context:** `Unknown` @@ -117,8 +152,11 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L170) - **Line:** 170 - **Context:** `function handlePositionUpdate(position: number, duration: number): voi...` -- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) - - **Line:** 4 +- **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) + - **Line:** 6 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L20) + - **Line:** 20 - **Context:** `Unknown` - **File:** [`src-tauri/src/player/state.rs`](src-tauri/src/player/state.rs#L7) - **Line:** 7 @@ -126,11 +164,8 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/player/state.rs`](src-tauri/src/player/state.rs#L25) - **Line:** 25 - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) - - **Line:** 6 - - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L20) - - **Line:** 20 +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 - **Context:** `Unknown` ### DR-002 @@ -165,8 +200,14 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### DR-005 -**Locations:** 10 file(s) +**Locations:** 11 file(s) +- **File:** [`src/lib/stores/appState.ts`](src/lib/stores/appState.ts#L2) + - **Line:** 2 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/queue.test.ts`](src/lib/stores/queue.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/stores/queue.ts`](src/lib/stores/queue.ts#L7) - **Line:** 7 - **Context:** `Unknown` @@ -185,9 +226,6 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/stores/queue.ts`](src/lib/stores/queue.ts#L126) - **Line:** 126 - **Context:** `async function toggleShuffle() {` -- **File:** [`src/lib/stores/appState.ts`](src/lib/stores/appState.ts#L2) - - **Line:** 2 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/queue.rs`](src-tauri/src/player/queue.rs#L8) - **Line:** 8 - **Context:** `Unknown` @@ -200,42 +238,83 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### DR-006 -**Locations:** 1 file(s) +**Locations:** 2 file(s) - **File:** [`src/lib/services/preload.ts`](src/lib/services/preload.ts#L5) - **Line:** 5 - **Context:** `Unknown` +- **File:** [`src/lib/services/preload.test.ts`](src/lib/services/preload.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` ### DR-007 -**Locations:** 1 file(s) +**Locations:** 2 file(s) +- **File:** [`src/lib/components/library/GenericMediaListPage.svelte`](src/lib/components/library/GenericMediaListPage.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/library.ts`](src/lib/stores/library.ts#L2) - **Line:** 2 - **Context:** `Unknown` ### DR-009 -**Locations:** 2 file(s) +**Locations:** 5 file(s) -- **File:** [`src/lib/stores/player.ts`](src/lib/stores/player.ts#L8) - - **Line:** 8 +- **File:** [`src/lib/components/player/AudioPlayer.svelte`](src/lib/components/player/AudioPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/player/MiniPlayer.svelte`](src/lib/components/player/MiniPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/player/Controls.svelte`](src/lib/components/player/Controls.svelte#L1) + - **Line:** 1 - **Context:** `Unknown` - **File:** [`src/lib/stores/appState.ts`](src/lib/stores/appState.ts#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src/lib/stores/player.ts`](src/lib/stores/player.ts#L8) + - **Line:** 8 + - **Context:** `Unknown` + +### DR-010 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` ### DR-011 -**Locations:** 1 file(s) +**Locations:** 7 file(s) - **File:** [`src/lib/stores/library.ts`](src/lib/stores/library.ts#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src/lib/services/deviceId.ts`](src/lib/services/deviceId.ts#L8) + - **Line:** 8 + - **Context:** `Unknown` +- **File:** [`src/lib/services/deviceId.ts`](src/lib/services/deviceId.ts#L23) + - **Line:** 23 + - **Context:** `Unknown` +- **File:** [`src/lib/services/deviceId.test.ts`](src/lib/services/deviceId.test.ts#L7) + - **Line:** 7 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/device.rs`](src-tauri/src/commands/device.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/device.rs`](src-tauri/src/commands/device.rs#L24) + - **Line:** 24 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/device.rs`](src-tauri/src/commands/device.rs#L80) + - **Line:** 80 + - **Context:** `Unknown` ### DR-012 -**Locations:** 2 file(s) +**Locations:** 3 file(s) - **File:** [`src-tauri/src/storage/db_service.rs`](src-tauri/src/storage/db_service.rs#L288) - **Line:** 288 @@ -243,31 +322,46 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/storage/mod.rs`](src-tauri/src/storage/mod.rs#L159) - **Line:** 159 - **Context:** `pub fn file_size(&self) -> Option {` +- **File:** [`src-tauri/src/storage/schema.rs`](src-tauri/src/storage/schema.rs#L3) + - **Line:** 3 + - **Context:** `Unknown` ### DR-013 -**Locations:** 1 file(s) +**Locations:** 2 file(s) - **File:** [`src/lib/stores/connectivity.ts`](src/lib/stores/connectivity.ts#L5) - **Line:** 5 - **Context:** `Unknown` +- **File:** [`src-tauri/src/repository/online.rs`](src-tauri/src/repository/online.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### DR-014 -**Locations:** 1 file(s) +**Locations:** 4 file(s) - **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L7) - **Line:** 7 - **Context:** `Unknown` +- **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L58) + - **Line:** 58 + - **Context:** `Unknown` +- **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L170) + - **Line:** 170 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/sync.rs`](src-tauri/src/commands/sync.rs#L5) + - **Line:** 5 + - **Context:** `Unknown` ### DR-015 -**Locations:** 5 file(s) +**Locations:** 6 file(s) -- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) +- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) +- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) - **Line:** 2 - **Context:** `Unknown` - **File:** [`src/lib/utils/validation.test.ts`](src/lib/utils/validation.test.ts#L4) @@ -276,26 +370,32 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/preload.ts`](src/lib/services/preload.ts#L5) - **Line:** 5 - **Context:** `Unknown` -- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1533) - - **Line:** 1533 +- **File:** [`src/lib/services/preload.test.ts`](src/lib/services/preload.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1552) + - **Line:** 1552 - **Context:** `Unknown` ### DR-016 -**Locations:** 1 file(s) +**Locations:** 2 file(s) - **File:** [`src/lib/services/imageCache.ts`](src/lib/services/imageCache.ts#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src/lib/services/imageCache.test.ts`](src/lib/services/imageCache.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` ### DR-017 **Locations:** 3 file(s) -- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) +- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) +- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) - **Line:** 2 - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/offline.rs`](src-tauri/src/commands/offline.rs#L133) @@ -306,14 +406,17 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, **Locations:** 1 file(s) -- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1533) - - **Line:** 1533 +- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1552) + - **Line:** 1552 - **Context:** `Unknown` ### DR-020 -**Locations:** 6 file(s) +**Locations:** 7 file(s) +- **File:** [`src/lib/stores/queue.test.ts`](src/lib/stores/queue.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/stores/queue.ts`](src/lib/stores/queue.ts#L7) - **Line:** 7 - **Context:** `Unknown` @@ -335,11 +438,30 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### DR-021 -**Locations:** 1 file(s) +**Locations:** 2 file(s) - **File:** [`src/lib/services/favorites.ts`](src/lib/services/favorites.ts#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src/lib/services/favorites.test.ts`](src/lib/services/favorites.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` + +### DR-023 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-024 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` ### DR-026 @@ -362,11 +484,32 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### DR-028 -**Locations:** 8 file(s) +**Locations:** 13 file(s) - **File:** [`src/lib/utils/duration.test.ts`](src/lib/utils/duration.test.ts#L4) - **Line:** 4 - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L10) + - **Line:** 10 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L21) + - **Line:** 21 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L64) + - **Line:** 64 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L99) + - **Line:** 99 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L136) + - **Line:** 136 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.test.ts`](src/lib/services/playbackReporting.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L8) - **Line:** 8 - **Context:** `Unknown` @@ -376,12 +519,6 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L160) - **Line:** 160 - **Context:** `Unknown` -- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L7) - - **Line:** 7 - - **Context:** `Unknown` -- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) - - **Line:** 4 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) - **Line:** 6 - **Context:** `Unknown` @@ -391,10 +528,13 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### DR-029 -**Locations:** 6 file(s) +**Locations:** 7 file(s) -- **File:** [`src/lib/stores/sleepTimer.ts`](src/lib/stores/sleepTimer.ts#L9) - - **Line:** 9 +- **File:** [`src/lib/components/player/SleepTimerModal.svelte`](src/lib/components/player/SleepTimerModal.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/sleepTimer.ts`](src/lib/stores/sleepTimer.ts#L10) + - **Line:** 10 - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L263) - **Line:** 263 @@ -402,64 +542,170 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L300) - **Line:** 300 - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/autoplay.rs`](src-tauri/src/player/autoplay.rs#L2) - - **Line:** 2 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/sleep_timer.rs`](src-tauri/src/player/sleep_timer.rs#L4) - **Line:** 4 - **Context:** `Unknown` - **File:** [`src-tauri/src/player/sleep_timer.rs`](src-tauri/src/player/sleep_timer.rs#L81) - **Line:** 81 - **Context:** `pub fn cancel(&mut self) {` +- **File:** [`src-tauri/src/player/autoplay.rs`](src-tauri/src/player/autoplay.rs#L2) + - **Line:** 2 + - **Context:** `Unknown` -### DR-033 +### DR-032 **Locations:** 1 file(s) +- **File:** [`src/lib/components/library/GenericMediaListPage.svelte`](src/lib/components/library/GenericMediaListPage.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-033 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/library/GenericMediaListPage.svelte`](src/lib/components/library/GenericMediaListPage.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/library.ts`](src/lib/stores/library.ts#L2) - **Line:** 2 - **Context:** `Unknown` +### DR-034 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-035 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-036 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + ### DR-037 -**Locations:** 4 file(s) +**Locations:** 10 file(s) -- **File:** [`src/lib/stores/sessions.ts`](src/lib/stores/sessions.ts#L2) +- **File:** [`src/lib/components/sessions/SessionsList.svelte`](src/lib/components/sessions/SessionsList.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/RemoteControls.svelte`](src/lib/components/sessions/RemoteControls.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/CastButton.svelte`](src/lib/components/sessions/CastButton.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/SessionPickerModal.svelte`](src/lib/components/sessions/SessionPickerModal.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/SessionCard.svelte`](src/lib/components/sessions/SessionCard.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/sessions.test.ts`](src/lib/stores/sessions.test.ts#L2) + - **Line:** 2 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/playbackMode.test.ts`](src/lib/stores/playbackMode.test.ts#L2) - **Line:** 2 - **Context:** `Unknown` - **File:** [`src/lib/stores/playbackMode.ts`](src/lib/stores/playbackMode.ts#L9) - **Line:** 9 - **Context:** `Unknown` -- **File:** [`src/lib/stores/playbackMode.test.ts`](src/lib/stores/playbackMode.test.ts#L2) +- **File:** [`src/lib/stores/sessions.ts`](src/lib/stores/sessions.ts#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src/lib/stores/sessions.test.ts`](src/lib/stores/sessions.test.ts#L2) - - **Line:** 2 +- **File:** [`src-tauri/src/commands/sessions.rs`](src-tauri/src/commands/sessions.rs#L1) + - **Line:** 1 - **Context:** `Unknown` ### DR-038 -**Locations:** 1 file(s) +**Locations:** 2 file(s) +- **File:** [`src/lib/components/home/HeroBanner.svelte`](src/lib/components/home/HeroBanner.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/home.ts`](src/lib/stores/home.ts#L2) - **Line:** 2 - **Context:** `Unknown` ### DR-039 -**Locations:** 1 file(s) +**Locations:** 2 file(s) +- **File:** [`src/lib/components/home/Carousel.svelte`](src/lib/components/home/Carousel.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/home.ts`](src/lib/stores/home.ts#L2) - **Line:** 2 - **Context:** `Unknown` +### DR-040 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/library/CastSection.svelte`](src/lib/components/library/CastSection.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/library/CrewLinks.svelte`](src/lib/components/library/CrewLinks.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-041 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/library/PersonDetailView.svelte`](src/lib/components/library/PersonDetailView.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-044 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/library/CastSection.svelte`](src/lib/components/library/CastSection.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-045 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/BottomNav.svelte`](src/lib/components/BottomNav.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + ### DR-047 -**Locations:** 10 file(s) +**Locations:** 12 file(s) - **File:** [`src/lib/stores/nextEpisode.ts`](src/lib/stores/nextEpisode.ts#L9) - **Line:** 9 - **Context:** `Unknown` +- **File:** [`src/lib/services/nextEpisodeService.ts`](src/lib/services/nextEpisodeService.ts#L7) + - **Line:** 7 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L10) + - **Line:** 10 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.test.ts`](src/lib/services/playbackReporting.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L8) - **Line:** 8 - **Context:** `Unknown` @@ -472,12 +718,6 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L309) - **Line:** 309 - **Context:** `function handleSleepTimerChanged(mode: SleepTimerMode, remainingSecond...` -- **File:** [`src/lib/services/nextEpisodeService.ts`](src/lib/services/nextEpisodeService.ts#L7) - - **Line:** 7 - - **Context:** `Unknown` -- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) - - **Line:** 4 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) - **Line:** 6 - **Context:** `Unknown` @@ -490,24 +730,232 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### DR-048 -**Locations:** 4 file(s) +**Locations:** 6 file(s) +- **File:** [`src/routes/settings/+page.svelte`](src/routes/settings/+page.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/nextEpisode.ts`](src/lib/stores/nextEpisode.ts#L9) - **Line:** 9 - **Context:** `Unknown` +- **File:** [`src/lib/services/nextEpisodeService.ts`](src/lib/services/nextEpisodeService.ts#L7) + - **Line:** 7 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L309) - **Line:** 309 - **Context:** `function handleSleepTimerChanged(mode: SleepTimerMode, remainingSecond...` -- **File:** [`src/lib/services/nextEpisodeService.ts`](src/lib/services/nextEpisodeService.ts#L7) - - **Line:** 7 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/autoplay.rs`](src-tauri/src/player/autoplay.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### DR-050 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/player/SleepTimerModal.svelte`](src/lib/components/player/SleepTimerModal.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-001 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-002 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-003 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-004 + +**Locations:** 2 file(s) + +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-005 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` + +### JA-007 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-010 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-011 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-012 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-017 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-021 + +**Locations:** 5 file(s) + +- **File:** [`src/lib/components/sessions/SessionsList.svelte`](src/lib/components/sessions/SessionsList.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/SessionPickerModal.svelte`](src/lib/components/sessions/SessionPickerModal.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/sessions.rs`](src-tauri/src/commands/sessions.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/session_poller/mod.rs`](src-tauri/src/session_poller/mod.rs#L6) + - **Line:** 6 + - **Context:** `Unknown` + +### JA-022 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/sessions/RemoteControls.svelte`](src/lib/components/sessions/RemoteControls.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-023 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/sessions/RemoteControls.svelte`](src/lib/components/sessions/RemoteControls.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-024 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/sessions/RemoteControls.svelte`](src/lib/components/sessions/RemoteControls.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-025 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/sessions/SessionPickerModal.svelte`](src/lib/components/sessions/SessionPickerModal.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-026 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/sessions/RemoteControls.svelte`](src/lib/components/sessions/RemoteControls.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### JA-029 + +**Locations:** 3 file(s) + +- **File:** [`src/lib/components/library/CastSection.svelte`](src/lib/components/library/CastSection.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/library/CrewLinks.svelte`](src/lib/components/library/CrewLinks.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` + +### JA-030 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/library/PersonDetailView.svelte`](src/lib/components/library/PersonDetailView.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` + +### JA-031 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/library/PersonDetailView.svelte`](src/lib/components/library/PersonDetailView.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` ### UR-002 -**Locations:** 7 file(s) +**Locations:** 10 file(s) - **File:** [`src/lib/stores/connectivity.ts`](src/lib/stores/connectivity.ts#L5) - **Line:** 5 @@ -518,11 +966,14 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L116) - **Line:** 116 - **Context:** `pub enum MediaType {` +- **File:** [`src-tauri/src/commands/offline.rs`](src-tauri/src/commands/offline.rs#L133) + - **Line:** 133 + - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src-tauri/src/commands/offline.rs`](src-tauri/src/commands/offline.rs#L133) - - **Line:** 133 +- **File:** [`src-tauri/src/commands/sync.rs`](src-tauri/src/commands/sync.rs#L5) + - **Line:** 5 - **Context:** `Unknown` - **File:** [`src-tauri/src/storage/db_service.rs`](src-tauri/src/storage/db_service.rs#L288) - **Line:** 288 @@ -530,19 +981,19 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/storage/mod.rs`](src-tauri/src/storage/mod.rs#L159) - **Line:** 159 - **Context:** `pub fn file_size(&self) -> Option {` +- **File:** [`src-tauri/src/storage/schema.rs`](src-tauri/src/storage/schema.rs#L3) + - **Line:** 3 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/repository/online.rs`](src-tauri/src/repository/online.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-003 -**Locations:** 7 file(s) +**Locations:** 9 file(s) -- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L42) - - **Line:** 42 - - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L116) - - **Line:** 116 - - **Context:** `pub enum MediaType {` -- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) - - **Line:** 2 +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 - **Context:** `Unknown` - **File:** [`src-tauri/src/player/backend.rs`](src-tauri/src/player/backend.rs#L39) - **Line:** 39 @@ -553,25 +1004,34 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/player/mpv_backend_test.rs`](src-tauri/src/player/mpv_backend_test.rs#L8) - **Line:** 8 - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L42) + - **Line:** 42 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L116) + - **Line:** 116 + - **Context:** `pub enum MediaType {` +- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) + - **Line:** 2 + - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-004 -**Locations:** 8 file(s) +**Locations:** 11 file(s) +- **File:** [`src/lib/components/player/AudioPlayer.svelte`](src/lib/components/player/AudioPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/services/preload.ts`](src/lib/services/preload.ts#L5) - **Line:** 5 - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L42) - - **Line:** 42 - - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L116) - - **Line:** 116 - - **Context:** `pub enum MediaType {` -- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) - - **Line:** 2 +- **File:** [`src/lib/services/preload.test.ts`](src/lib/services/preload.test.ts#L4) + - **Line:** 4 - **Context:** `Unknown` - **File:** [`src-tauri/src/player/backend.rs`](src-tauri/src/player/backend.rs#L39) - **Line:** 39 @@ -582,14 +1042,44 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/player/mpv_backend_test.rs`](src-tauri/src/player/mpv_backend_test.rs#L8) - **Line:** 8 - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L42) + - **Line:** 42 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L116) + - **Line:** 116 + - **Context:** `pub enum MediaType {` +- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) + - **Line:** 2 + - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-005 -**Locations:** 29 file(s) +**Locations:** 40 file(s) +- **File:** [`src/lib/components/player/AudioPlayer.svelte`](src/lib/components/player/AudioPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/player/MiniPlayer.svelte`](src/lib/components/player/MiniPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/player/Controls.svelte`](src/lib/components/player/Controls.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/appState.ts`](src/lib/stores/appState.ts#L2) + - **Line:** 2 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/queue.test.ts`](src/lib/stores/queue.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/stores/player.ts`](src/lib/stores/player.ts#L8) - **Line:** 8 - **Context:** `Unknown` @@ -614,12 +1104,27 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/stores/queue.ts`](src/lib/stores/queue.ts#L126) - **Line:** 126 - **Context:** `async function toggleShuffle() {` -- **File:** [`src/lib/stores/appState.ts`](src/lib/stores/appState.ts#L2) - - **Line:** 2 - - **Context:** `Unknown` - **File:** [`src/lib/utils/duration.test.ts`](src/lib/utils/duration.test.ts#L4) - **Line:** 4 - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L10) + - **Line:** 10 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L21) + - **Line:** 21 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L64) + - **Line:** 64 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L99) + - **Line:** 99 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.test.ts`](src/lib/services/playbackReporting.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L8) - **Line:** 8 - **Context:** `Unknown` @@ -632,24 +1137,12 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L170) - **Line:** 170 - **Context:** `function handlePositionUpdate(position: number, duration: number): voi...` -- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) - - **Line:** 4 - - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/state.rs`](src-tauri/src/player/state.rs#L7) - - **Line:** 7 - - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/state.rs`](src-tauri/src/player/state.rs#L25) - - **Line:** 25 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) - **Line:** 6 - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L20) - **Line:** 20 - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) - - **Line:** 2 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/queue.rs`](src-tauri/src/player/queue.rs#L8) - **Line:** 8 - **Context:** `Unknown` @@ -674,20 +1167,44 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/player/backend.rs`](src-tauri/src/player/backend.rs#L58) - **Line:** 58 - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/state.rs`](src-tauri/src/player/state.rs#L7) + - **Line:** 7 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/state.rs`](src-tauri/src/player/state.rs#L25) + - **Line:** 25 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) + - **Line:** 2 + - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-007 -**Locations:** 2 file(s) +**Locations:** 6 file(s) +- **File:** [`src/lib/components/library/GenericMediaListPage.svelte`](src/lib/components/library/GenericMediaListPage.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/library.ts`](src/lib/stores/library.ts#L2) - **Line:** 2 - **Context:** `Unknown` - **File:** [`src/lib/services/imageCache.ts`](src/lib/services/imageCache.ts#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src/lib/services/imageCache.test.ts`](src/lib/services/imageCache.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/repository/online.rs`](src-tauri/src/repository/online.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-008 @@ -699,7 +1216,7 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### UR-009 -**Locations:** 6 file(s) +**Locations:** 13 file(s) - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L6) - **Line:** 6 @@ -710,62 +1227,110 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L296) - **Line:** 296 - **Context:** `Unknown` +- **File:** [`src/lib/stores/auth.test.ts`](src/lib/stores/auth.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/utils/validation.test.ts`](src/lib/utils/validation.test.ts#L4) - **Line:** 4 - **Context:** `Unknown` -- **File:** [`src/lib/services/deviceId.test.ts`](src/lib/services/deviceId.test.ts#L4) - - **Line:** 4 +- **File:** [`src/lib/services/deviceId.ts`](src/lib/services/deviceId.ts#L8) + - **Line:** 8 + - **Context:** `Unknown` +- **File:** [`src/lib/services/deviceId.ts`](src/lib/services/deviceId.ts#L23) + - **Line:** 23 + - **Context:** `Unknown` +- **File:** [`src/lib/services/deviceId.test.ts`](src/lib/services/deviceId.test.ts#L7) + - **Line:** 7 - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/device.rs`](src-tauri/src/commands/device.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/device.rs`](src-tauri/src/commands/device.rs#L24) + - **Line:** 24 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/device.rs`](src-tauri/src/commands/device.rs#L80) + - **Line:** 80 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/jellyfin/client.rs`](src-tauri/src/jellyfin/client.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-010 -**Locations:** 4 file(s) +**Locations:** 12 file(s) -- **File:** [`src/lib/stores/sessions.ts`](src/lib/stores/sessions.ts#L2) +- **File:** [`src/lib/components/sessions/SessionsList.svelte`](src/lib/components/sessions/SessionsList.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/RemoteControls.svelte`](src/lib/components/sessions/RemoteControls.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/CastButton.svelte`](src/lib/components/sessions/CastButton.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/SessionPickerModal.svelte`](src/lib/components/sessions/SessionPickerModal.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/sessions/SessionCard.svelte`](src/lib/components/sessions/SessionCard.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/sessions.test.ts`](src/lib/stores/sessions.test.ts#L2) + - **Line:** 2 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/playbackMode.test.ts`](src/lib/stores/playbackMode.test.ts#L2) - **Line:** 2 - **Context:** `Unknown` - **File:** [`src/lib/stores/playbackMode.ts`](src/lib/stores/playbackMode.ts#L9) - **Line:** 9 - **Context:** `Unknown` -- **File:** [`src/lib/stores/playbackMode.test.ts`](src/lib/stores/playbackMode.test.ts#L2) +- **File:** [`src/lib/stores/sessions.ts`](src/lib/stores/sessions.ts#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src/lib/stores/sessions.test.ts`](src/lib/stores/sessions.test.ts#L2) - - **Line:** 2 +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/sessions.rs`](src-tauri/src/commands/sessions.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/session_poller/mod.rs`](src-tauri/src/session_poller/mod.rs#L6) + - **Line:** 6 - **Context:** `Unknown` ### UR-011 -**Locations:** 7 file(s) +**Locations:** 8 file(s) -- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) +- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) +- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) - **Line:** 2 - **Context:** `Unknown` - **File:** [`src/lib/services/preload.ts`](src/lib/services/preload.ts#L5) - **Line:** 5 - **Context:** `Unknown` +- **File:** [`src/lib/services/preload.test.ts`](src/lib/services/preload.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src-tauri/src/player/media.rs`](src-tauri/src/player/media.rs#L116) - **Line:** 116 - **Context:** `pub enum MediaType {` +- **File:** [`src-tauri/src/commands/offline.rs`](src-tauri/src/commands/offline.rs#L133) + - **Line:** 133 + - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1533) - - **Line:** 1533 - - **Context:** `Unknown` -- **File:** [`src-tauri/src/commands/offline.rs`](src-tauri/src/commands/offline.rs#L133) - - **Line:** 133 +- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1552) + - **Line:** 1552 - **Context:** `Unknown` ### UR-012 -**Locations:** 7 file(s) +**Locations:** 8 file(s) - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L6) - **Line:** 6 @@ -776,7 +1341,7 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/stores/auth.ts`](src/lib/stores/auth.ts#L466) - **Line:** 466 - **Context:** `Unknown` -- **File:** [`src/lib/services/deviceId.test.ts`](src/lib/services/deviceId.test.ts#L4) +- **File:** [`src/lib/stores/auth.test.ts`](src/lib/stores/auth.test.ts#L4) - **Line:** 4 - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) @@ -788,22 +1353,28 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/storage/mod.rs`](src-tauri/src/storage/mod.rs#L159) - **Line:** 159 - **Context:** `pub fn file_size(&self) -> Option {` +- **File:** [`src-tauri/src/credentials.rs`](src-tauri/src/credentials.rs#L10) + - **Line:** 10 + - **Context:** `Unknown` ### UR-013 **Locations:** 2 file(s) -- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) +- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) +- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) - **Line:** 2 - **Context:** `Unknown` ### UR-015 -**Locations:** 11 file(s) +**Locations:** 12 file(s) +- **File:** [`src/lib/stores/queue.test.ts`](src/lib/stores/queue.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/stores/queue.ts`](src/lib/stores/queue.ts#L7) - **Line:** 7 - **Context:** `Unknown` @@ -851,48 +1422,66 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### UR-017 -**Locations:** 3 file(s) +**Locations:** 8 file(s) +- **File:** [`src/lib/components/player/MiniPlayer.svelte`](src/lib/components/player/MiniPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/services/favorites.ts`](src/lib/services/favorites.ts#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src/lib/services/favorites.test.ts`](src/lib/services/favorites.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L7) - **Line:** 7 - **Context:** `Unknown` +- **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L58) + - **Line:** 58 + - **Context:** `Unknown` +- **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L170) + - **Line:** 170 + - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/sync.rs`](src-tauri/src/commands/sync.rs#L5) + - **Line:** 5 + - **Context:** `Unknown` ### UR-018 **Locations:** 3 file(s) -- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) - - **Line:** 2 - - **Context:** `Unknown` - **File:** [`src/lib/stores/downloads.test.ts`](src/lib/stores/downloads.test.ts#L2) - **Line:** 2 - **Context:** `Unknown` -- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1533) - - **Line:** 1533 +- **File:** [`src/lib/stores/downloads.ts`](src/lib/stores/downloads.ts#L2) + - **Line:** 2 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1552) + - **Line:** 1552 - **Context:** `Unknown` ### UR-019 -**Locations:** 9 file(s) +**Locations:** 10 file(s) +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L10) + - **Line:** 10 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.test.ts`](src/lib/services/playbackReporting.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L8) - **Line:** 8 - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L26) - **Line:** 26 - **Context:** `Unknown` -- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L7) - - **Line:** 7 - - **Context:** `Unknown` -- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) - - **Line:** 4 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) - **Line:** 6 - **Context:** `Unknown` @@ -909,16 +1498,47 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **Line:** 159 - **Context:** `pub fn file_size(&self) -> Option {` +### UR-020 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### UR-021 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/player.rs`](src-tauri/src/commands/player.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + ### UR-023 -**Locations:** 12 file(s) +**Locations:** 14 file(s) +- **File:** [`src/routes/settings/+page.svelte`](src/routes/settings/+page.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/nextEpisode.ts`](src/lib/stores/nextEpisode.ts#L9) - **Line:** 9 - **Context:** `Unknown` - **File:** [`src/lib/stores/home.ts`](src/lib/stores/home.ts#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src/lib/services/nextEpisodeService.ts`](src/lib/services/nextEpisodeService.ts#L7) + - **Line:** 7 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L8) - **Line:** 8 - **Context:** `Unknown` @@ -931,12 +1551,6 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L309) - **Line:** 309 - **Context:** `function handleSleepTimerChanged(mode: SleepTimerMode, remainingSecond...` -- **File:** [`src/lib/services/nextEpisodeService.ts`](src/lib/services/nextEpisodeService.ts#L7) - - **Line:** 7 - - **Context:** `Unknown` -- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) - - **Line:** 4 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) - **Line:** 6 - **Context:** `Unknown` @@ -949,6 +1563,9 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-024 @@ -960,33 +1577,60 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, ### UR-025 -**Locations:** 6 file(s) +**Locations:** 12 file(s) - **File:** [`src/lib/utils/validation.test.ts`](src/lib/utils/validation.test.ts#L4) - **Line:** 4 - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L10) + - **Line:** 10 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L21) + - **Line:** 21 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L99) + - **Line:** 99 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L136) + - **Line:** 136 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playbackReporting.test.ts`](src/lib/services/playbackReporting.test.ts#L4) + - **Line:** 4 + - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L160) - **Line:** 160 - **Context:** `Unknown` -- **File:** [`src/lib/services/playbackReporting.ts`](src/lib/services/playbackReporting.ts#L7) - - **Line:** 7 - - **Context:** `Unknown` - **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L7) - **Line:** 7 - **Context:** `Unknown` +- **File:** [`src/lib/services/syncService.ts`](src/lib/services/syncService.ts#L58) + - **Line:** 58 + - **Context:** `Unknown` - **File:** [`src-tauri/src/commands/mod.rs`](src-tauri/src/commands/mod.rs#L2) - **Line:** 2 - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/sync.rs`](src-tauri/src/commands/sync.rs#L5) + - **Line:** 5 + - **Context:** `Unknown` - **File:** [`src-tauri/src/storage/mod.rs`](src-tauri/src/storage/mod.rs#L159) - **Line:** 159 - **Context:** `pub fn file_size(&self) -> Option {` ### UR-026 -**Locations:** 12 file(s) +**Locations:** 14 file(s) -- **File:** [`src/lib/stores/sleepTimer.ts`](src/lib/stores/sleepTimer.ts#L9) - - **Line:** 9 +- **File:** [`src/lib/components/player/SleepTimerModal.svelte`](src/lib/components/player/SleepTimerModal.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/player/VideoPlayer.svelte`](src/lib/components/player/VideoPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/stores/sleepTimer.ts`](src/lib/stores/sleepTimer.ts#L10) + - **Line:** 10 + - **Context:** `Unknown` +- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) + - **Line:** 4 - **Context:** `Unknown` - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L8) - **Line:** 8 @@ -1000,52 +1644,105 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **File:** [`src/lib/services/playerEvents.ts`](src/lib/services/playerEvents.ts#L300) - **Line:** 300 - **Context:** `Unknown` -- **File:** [`src/lib/services/playerEvents.test.ts`](src/lib/services/playerEvents.test.ts#L4) - - **Line:** 4 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L6) - **Line:** 6 - **Context:** `Unknown` - **File:** [`src-tauri/src/player/events.rs`](src-tauri/src/player/events.rs#L20) - **Line:** 20 - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/autoplay.rs`](src-tauri/src/player/autoplay.rs#L2) - - **Line:** 2 - - **Context:** `Unknown` -- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) - - **Line:** 2 - - **Context:** `Unknown` - **File:** [`src-tauri/src/player/sleep_timer.rs`](src-tauri/src/player/sleep_timer.rs#L4) - **Line:** 4 - **Context:** `Unknown` - **File:** [`src-tauri/src/player/sleep_timer.rs`](src-tauri/src/player/sleep_timer.rs#L81) - **Line:** 81 - **Context:** `pub fn cancel(&mut self) {` +- **File:** [`src-tauri/src/player/autoplay.rs`](src-tauri/src/player/autoplay.rs#L2) + - **Line:** 2 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/player/mod.rs`](src-tauri/src/player/mod.rs#L2) + - **Line:** 2 + - **Context:** `Unknown` + +### UR-028 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/player/AudioPlayer.svelte`](src/lib/components/player/AudioPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/player/MiniPlayer.svelte`](src/lib/components/player/MiniPlayer.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` ### UR-029 -**Locations:** 1 file(s) +**Locations:** 2 file(s) +- **File:** [`src/lib/components/library/GenericMediaListPage.svelte`](src/lib/components/library/GenericMediaListPage.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/library.ts`](src/lib/stores/library.ts#L2) - **Line:** 2 - **Context:** `Unknown` ### UR-030 -**Locations:** 1 file(s) +**Locations:** 2 file(s) +- **File:** [`src/lib/components/library/GenericMediaListPage.svelte`](src/lib/components/library/GenericMediaListPage.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/library.ts`](src/lib/stores/library.ts#L2) - **Line:** 2 - **Context:** `Unknown` ### UR-034 -**Locations:** 1 file(s) +**Locations:** 3 file(s) +- **File:** [`src/lib/components/home/HeroBanner.svelte`](src/lib/components/home/HeroBanner.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/home/Carousel.svelte`](src/lib/components/home/Carousel.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` - **File:** [`src/lib/stores/home.ts`](src/lib/stores/home.ts#L2) - **Line:** 2 - **Context:** `Unknown` +### UR-035 + +**Locations:** 3 file(s) + +- **File:** [`src/lib/components/library/CastSection.svelte`](src/lib/components/library/CastSection.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src/lib/components/library/CrewLinks.svelte`](src/lib/components/library/CrewLinks.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` + +### UR-036 + +**Locations:** 2 file(s) + +- **File:** [`src/lib/components/library/PersonDetailView.svelte`](src/lib/components/library/PersonDetailView.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` +- **File:** [`src-tauri/src/commands/repository.rs`](src-tauri/src/commands/repository.rs#L4) + - **Line:** 4 + - **Context:** `Unknown` + +### UR-039 + +**Locations:** 1 file(s) + +- **File:** [`src/lib/components/BottomNav.svelte`](src/lib/components/BottomNav.svelte#L1) + - **Line:** 1 + - **Context:** `Unknown` + ### IT-003 **Locations:** 1 file(s) @@ -1302,16 +1999,16 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, **Locations:** 1 file(s) -- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1533) - - **Line:** 1533 +- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1552) + - **Line:** 1552 - **Context:** `Unknown` ### UT-043 **Locations:** 1 file(s) -- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1533) - - **Line:** 1533 +- **File:** [`src-tauri/src/commands/download.rs`](src-tauri/src/commands/download.rs#L1552) + - **Line:** 1552 - **Context:** `Unknown` ### UT-044 @@ -1322,6 +2019,28 @@ DR-001, DR-002, DR-003, DR-004, DR-005, DR-006, DR-007, DR-009, DR-011, DR-012, - **Line:** 133 - **Context:** `Unknown` +### UR-031 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### UR-032 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` + +### UR-033 + +**Locations:** 1 file(s) + +- **File:** [`src-tauri/src/settings.rs`](src-tauri/src/settings.rs#L1) + - **Line:** 1 + - **Context:** `Unknown` -✅ Complete! Found 72 TRACES across 185 files diff --git a/src-tauri/src/commands/player.rs b/src-tauri/src/commands/player.rs index a83dc94..e0dbe26 100644 --- a/src-tauri/src/commands/player.rs +++ b/src-tauri/src/commands/player.rs @@ -1,3 +1,5 @@ +//! TRACES: UR-003, UR-004, UR-005, UR-010, UR-020, UR-021 | JA-022, JA-023, JA-024, JA-025, JA-026 | DR-001 + use log::{debug, error, info, warn}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; diff --git a/src-tauri/src/commands/repository.rs b/src-tauri/src/commands/repository.rs index 42036bd..7915e1e 100644 --- a/src-tauri/src/commands/repository.rs +++ b/src-tauri/src/commands/repository.rs @@ -1,5 +1,7 @@ -// Tauri commands for repository access -// Uses handle-based system: UUID -> Arc +//! Tauri commands for repository access +//! Uses handle-based system: UUID -> Arc +//! +//! TRACES: UR-007, UR-035, UR-036 | JA-004, JA-005, JA-029, JA-030, JA-031 use std::collections::HashMap; use std::sync::{Arc, Mutex}; diff --git a/src-tauri/src/commands/sessions.rs b/src-tauri/src/commands/sessions.rs index 5c43bdb..74db87a 100644 --- a/src-tauri/src/commands/sessions.rs +++ b/src-tauri/src/commands/sessions.rs @@ -1,3 +1,5 @@ +//! TRACES: UR-010 | JA-021 | DR-037 + use std::sync::Arc; use tauri::State; use crate::session_poller::{PollingHint, SessionPollerManager}; diff --git a/src-tauri/src/credentials.rs b/src-tauri/src/credentials.rs index d3bb098..24e2836 100644 --- a/src-tauri/src/credentials.rs +++ b/src-tauri/src/credentials.rs @@ -6,6 +6,8 @@ //! //! The fallback is less secure as the encryption key is derived from machine //! identifiers, but provides functionality on headless systems. +//! +//! TRACES: UR-012 | IR-014 use aes_gcm::{ aead::{Aead, KeyInit}, diff --git a/src-tauri/src/jellyfin/client.rs b/src-tauri/src/jellyfin/client.rs index 45174b5..4b2d115 100644 --- a/src-tauri/src/jellyfin/client.rs +++ b/src-tauri/src/jellyfin/client.rs @@ -1,3 +1,5 @@ +//! TRACES: UR-009 | JA-001, JA-002, JA-003, JA-004, JA-007, JA-010, JA-011, JA-012, JA-017, JA-021 | IR-009, IR-010, IR-011 + use log::{debug, error, info}; use reqwest::Client; use serde::Deserialize; diff --git a/src-tauri/src/repository/online.rs b/src-tauri/src/repository/online.rs index b66b920..7546240 100644 --- a/src-tauri/src/repository/online.rs +++ b/src-tauri/src/repository/online.rs @@ -1,3 +1,5 @@ +//! TRACES: UR-002, UR-007 | DR-013 | IR-010 + use std::sync::Arc; use async_trait::async_trait; use log::{debug, error, info}; diff --git a/src-tauri/src/session_poller/mod.rs b/src-tauri/src/session_poller/mod.rs index 7541a8a..cd1597b 100644 --- a/src-tauri/src/session_poller/mod.rs +++ b/src-tauri/src/session_poller/mod.rs @@ -2,6 +2,8 @@ //! //! Manages background polling of Jellyfin sessions with dynamic frequency adjustment //! based on playback mode and UI state. Eliminates duplicate pollers across browser tabs. +//! +//! TRACES: UR-010 | JA-021 use log::{debug, info, warn}; use std::sync::{Arc, Mutex, RwLock}; diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index b3088a0..902742b 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -1,3 +1,5 @@ +//! TRACES: UR-023, UR-031, UR-032, UR-033 | DR-034, DR-035, DR-036, DR-048 + use serde::{Deserialize, Serialize}; /// Volume normalization levels matching Spotify's presets diff --git a/src-tauri/src/storage/schema.rs b/src-tauri/src/storage/schema.rs index 89fa4bf..00a60bc 100644 --- a/src-tauri/src/storage/schema.rs +++ b/src-tauri/src/storage/schema.rs @@ -1,4 +1,6 @@ //! Database schema and migrations +//! +//! TRACES: UR-002 | DR-012 | IR-013 /// List of migrations to apply in order. /// Each migration is a tuple of (name, sql). diff --git a/src/lib/api/autoplay.test.ts b/src/lib/api/autoplay.test.ts index de882bd..f7cd419 100644 --- a/src/lib/api/autoplay.test.ts +++ b/src/lib/api/autoplay.test.ts @@ -17,6 +17,7 @@ vi.mock("@tauri-apps/api/core", () => ({ return { enabled: true, countdownSeconds: 10, + maxEpisodes: 5, }; } if (command === "player_set_autoplay_settings") { @@ -61,6 +62,7 @@ describe("autoplay API", () => { const settings: AutoplaySettings = { enabled: true, countdownSeconds: 15, + maxEpisodes: 5, }; const result = await setAutoplaySettings(settings); @@ -72,6 +74,7 @@ describe("autoplay API", () => { const settings: AutoplaySettings = { enabled: false, countdownSeconds: 10, + maxEpisodes: 5, }; const result = await setAutoplaySettings(settings); @@ -86,6 +89,7 @@ describe("autoplay API", () => { const settings: AutoplaySettings = { enabled: true, countdownSeconds: 20, + maxEpisodes: 5, }; await setAutoplaySettings(settings); @@ -104,6 +108,7 @@ describe("autoplay API", () => { const settings: AutoplaySettings = { enabled: true, countdownSeconds: countdown, + maxEpisodes: 5, }; const result = await setAutoplaySettings(settings); diff --git a/src/lib/api/backend-integration.test.ts b/src/lib/api/backend-integration.test.ts index 8ec574a..830430e 100644 --- a/src/lib/api/backend-integration.test.ts +++ b/src/lib/api/backend-integration.test.ts @@ -350,7 +350,7 @@ describe("Backend Integration - Refactored Business Logic", () => { const backendUrl = "https://server.com/Videos/item123/stream.mp4?maxWidth=1280&api_key=token"; (invoke as any).mockResolvedValueOnce(backendUrl); - const url = await client.getVideoDownloadUrl("item123", "720p"); + const url = await client.getVideoDownloadUrl("item123", "medium"); expect(url).toBe(backendUrl); expect(invoke).toHaveBeenCalledWith( @@ -454,8 +454,8 @@ describe("Backend Integration - Refactored Business Logic", () => { it("should support complete flow: load → sort → display", async () => { (invoke as any).mockResolvedValueOnce({ items: [ - { id: "id1", name: "Album A", sortName: "A" }, - { id: "id2", name: "Album B", sortName: "B" }, + { id: "id1", name: "Album A" }, + { id: "id2", name: "Album B" }, ], totalRecordCount: 2, }); @@ -467,8 +467,8 @@ describe("Backend Integration - Refactored Business Logic", () => { }); // Backend returned pre-sorted items - expect(result.items[0].sortName).toBe("A"); - expect(result.items[1].sortName).toBe("B"); + expect(result.items[0].name).toBe("Album A"); + expect(result.items[1].name).toBe("Album B"); // Frontend just displays them // No compareFn, no local sorting diff --git a/src/lib/api/repository-client.test.ts b/src/lib/api/repository-client.test.ts index df75929..c90bd3a 100644 --- a/src/lib/api/repository-client.test.ts +++ b/src/lib/api/repository-client.test.ts @@ -202,13 +202,13 @@ describe("RepositoryClient", () => { const mockUrl = "https://server.com/Videos/item123/stream.mp4?maxWidth=1920&api_key=token"; (invoke as any).mockResolvedValueOnce(mockUrl); - const downloadUrl = await client.getVideoDownloadUrl("item123", "1080p"); + const downloadUrl = await client.getVideoDownloadUrl("item123", "high"); expect(downloadUrl).toBe(mockUrl); expect(invoke).toHaveBeenCalledWith("repository_get_video_download_url", { handle: "test-handle-123", itemId: "item123", - quality: "1080p", + quality: "high", mediaSourceId: null, }); }); @@ -228,7 +228,7 @@ describe("RepositoryClient", () => { }); it("should support quality presets", async () => { - const qualities = ["original", "1080p", "720p", "480p"]; + const qualities = ["original", "high", "medium", "low"]; for (const quality of qualities) { vi.clearAllMocks(); @@ -252,12 +252,12 @@ describe("RepositoryClient", () => { const mockUrl = "https://server.com/Videos/item123/stream.mp4?api_key=token"; (invoke as any).mockResolvedValueOnce(mockUrl); - await client.getVideoDownloadUrl("item123", "720p", "source789"); + await client.getVideoDownloadUrl("item123", "medium", "source789"); expect(invoke).toHaveBeenCalledWith("repository_get_video_download_url", { handle: expect.any(String), itemId: "item123", - quality: "720p", + quality: "medium", mediaSourceId: "source789", }); }); diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts index c89f836..4615e08 100644 --- a/src/lib/api/types.ts +++ b/src/lib/api/types.ts @@ -67,6 +67,9 @@ export interface MediaItem { // Cast & Crew people?: Person[]; + + // Genres + genres?: string[]; } export type ItemType = diff --git a/src/lib/components/BottomNav.svelte b/src/lib/components/BottomNav.svelte index f631c6f..a7d7fb4 100644 --- a/src/lib/components/BottomNav.svelte +++ b/src/lib/components/BottomNav.svelte @@ -1,3 +1,4 @@ + diff --git a/src/lib/components/home/Carousel.svelte b/src/lib/components/home/Carousel.svelte index 6ba2f44..c881b33 100644 --- a/src/lib/components/home/Carousel.svelte +++ b/src/lib/components/home/Carousel.svelte @@ -1,3 +1,4 @@ + diff --git a/src/lib/components/library/TrackList.logic.test.ts b/src/lib/components/library/TrackList.logic.test.ts index b84937f..5bc4b51 100644 --- a/src/lib/components/library/TrackList.logic.test.ts +++ b/src/lib/components/library/TrackList.logic.test.ts @@ -322,16 +322,10 @@ describe("TrackList Logic Tests", () => { it("should invoke player when no custom callback", async () => { const invokeMock = (invoke as any).mockResolvedValue(undefined); - // Simulate unified handler without custom callback - const onTrackClick = undefined; - if (onTrackClick) { - await onTrackClick(mockTracks[0], 0); - } else { - // This branch executes - default handler - await invoke("player_play_queue", { - request: { items: [], startIndex: 0, shuffle: false }, - }); - } + // Simulate unified handler without custom callback - default handler runs + await invoke("player_play_queue", { + request: { items: [], startIndex: 0, shuffle: false }, + }); expect(invokeMock).toHaveBeenCalled(); }); diff --git a/src/lib/components/library/TrackList.test.ts b/src/lib/components/library/TrackList.test.ts index b07a3f6..80958ca 100644 --- a/src/lib/components/library/TrackList.test.ts +++ b/src/lib/components/library/TrackList.test.ts @@ -332,8 +332,6 @@ describe.skip("TrackList", () => { // NOTE: This test is skipped because stream URLs are no longer fetched by frontend. // The code now uses player_play_tracks which sends trackIds to backend. // Backend handles all stream URL generation, so this error path no longer exists. - - alertSpy.mockRestore(); }); }); diff --git a/src/lib/components/player/AudioPlayer.svelte b/src/lib/components/player/AudioPlayer.svelte index d435c90..0ab2dd6 100644 --- a/src/lib/components/player/AudioPlayer.svelte +++ b/src/lib/components/player/AudioPlayer.svelte @@ -1,3 +1,4 @@ +