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
+26 -13
View File
@@ -60,7 +60,11 @@ impl DownloadWorker {
}
/// Attempt a single download
async fn try_download<F>(&self, task: &DownloadTask, on_progress: &F) -> Result<DownloadResult, DownloadError>
async fn try_download<F>(
&self,
task: &DownloadTask,
on_progress: &F,
) -> Result<DownloadResult, DownloadError>
where
F: Fn(u64, Option<u64>) + Send + Sync,
{
@@ -74,10 +78,7 @@ impl DownloadWorker {
// Check for partial download
let temp_path = task.target_path.with_extension("part");
let existing_bytes = if temp_path.exists() {
fs::metadata(&temp_path)
.await
.map(|m| m.len())
.unwrap_or(0)
fs::metadata(&temp_path).await.map(|m| m.len()).unwrap_or(0)
} else {
0
};
@@ -105,14 +106,17 @@ impl DownloadWorker {
.get(reqwest::header::CONTENT_LENGTH)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse::<u64>().ok())
.map(|len| if existing_bytes > 0 { len + existing_bytes } else { len });
.map(|len| {
if existing_bytes > 0 {
len + existing_bytes
} else {
len
}
});
// Open file for appending
let mut file = if existing_bytes > 0 {
fs::OpenOptions::new()
.append(true)
.open(&temp_path)
.await
fs::OpenOptions::new().append(true).open(&temp_path).await
} else {
fs::File::create(&temp_path).await
}
@@ -206,9 +210,18 @@ mod tests {
#[test]
fn test_exponential_backoff() {
assert_eq!(DownloadWorker::exponential_backoff(1), Duration::from_secs(5));
assert_eq!(DownloadWorker::exponential_backoff(2), Duration::from_secs(15));
assert_eq!(DownloadWorker::exponential_backoff(3), Duration::from_secs(45));
assert_eq!(
DownloadWorker::exponential_backoff(1),
Duration::from_secs(5)
);
assert_eq!(
DownloadWorker::exponential_backoff(2),
Duration::from_secs(15)
);
assert_eq!(
DownloadWorker::exponential_backoff(3),
Duration::from_secs(45)
);
}
#[test]