Background-audio handoff for video + repository/player refactor

Hand video playback off to a native audio-only stream when the app is
backgrounded or locked, with no on-device video decode (UR-040). Adds
player_enter/exit_background_audio commands, an audio-only stream URL
for video items across the repository layer, and the frontend handoff
state machine wired into VideoPlayer. Includes accompanying
repository/offline/player refactoring and regenerates the traceability
matrix.
This commit is contained in:
2026-07-22 21:52:07 +02:00
parent 4e6ab017d4
commit 3fbf6afdbc
72 changed files with 6728 additions and 2338 deletions
+19 -19
View File
@@ -8,16 +8,10 @@ use serde::{Deserialize, Serialize};
pub enum DownloadEvent {
/// Download has been queued
#[serde(rename_all = "camelCase")]
Queued {
download_id: i64,
item_id: String,
},
Queued { download_id: i64, item_id: String },
/// Download has started
#[serde(rename_all = "camelCase")]
Started {
download_id: i64,
item_id: String,
},
Started { download_id: i64, item_id: String },
/// Download progress update
#[serde(rename_all = "camelCase")]
Progress {
@@ -43,16 +37,10 @@ pub enum DownloadEvent {
},
/// Download paused
#[serde(rename_all = "camelCase")]
Paused {
download_id: i64,
item_id: String,
},
Paused { download_id: i64, item_id: String },
/// Download cancelled
#[serde(rename_all = "camelCase")]
Cancelled {
download_id: i64,
item_id: String,
},
Cancelled { download_id: i64, item_id: String },
}
#[cfg(test)]
@@ -98,9 +86,21 @@ mod tests {
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("\"type\":\"completed\""));
// Verify camelCase field names
assert!(json.contains("\"downloadId\":42"), "Expected downloadId (camelCase), got: {}", json);
assert!(json.contains("\"itemId\":\"song456\""), "Expected itemId (camelCase), got: {}", json);
assert!(json.contains("\"filePath\":"), "Expected filePath (camelCase), got: {}", json);
assert!(
json.contains("\"downloadId\":42"),
"Expected downloadId (camelCase), got: {}",
json
);
assert!(
json.contains("\"itemId\":\"song456\""),
"Expected itemId (camelCase), got: {}",
json
);
assert!(
json.contains("\"filePath\":"),
"Expected filePath (camelCase), got: {}",
json
);
// Verify roundtrip
let deserialized: DownloadEvent = serde_json::from_str(&json).unwrap();