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.
128 lines
4.4 KiB
Rust
128 lines
4.4 KiB
Rust
//! Sync queue processor with retry logic and exponential backoff
|
|
//!
|
|
//! This is a placeholder implementation. Full implementation will be added
|
|
//! when the reporter is integrated. Dead code warnings are suppressed.
|
|
|
|
#![allow(dead_code)]
|
|
#![allow(unused_imports)]
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use tokio::sync::Mutex as TokioMutex;
|
|
|
|
use crate::jellyfin::client::JellyfinClient;
|
|
use crate::repository::MediaRepository;
|
|
use crate::storage::db_service::RusqliteService;
|
|
|
|
/// Configuration for sync processor
|
|
pub struct SyncConfig {
|
|
pub max_retries: u32, // 5
|
|
pub base_retry_delay_ms: u64, // 1000ms
|
|
pub batch_size: usize, // 10 items
|
|
}
|
|
|
|
impl Default for SyncConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
max_retries: 5,
|
|
base_retry_delay_ms: 1000,
|
|
batch_size: 10,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Sync queue processor that handles retry logic with exponential backoff
|
|
///
|
|
/// This is a placeholder implementation. Full implementation will be added
|
|
/// in a subsequent task following the plan.
|
|
pub struct SyncProcessor {
|
|
_db_service: Arc<RusqliteService>,
|
|
_jellyfin_client: Arc<TokioMutex<Option<JellyfinClient>>>,
|
|
_repository: Arc<dyn MediaRepository>,
|
|
_processing: Arc<TokioMutex<bool>>,
|
|
_cancelled: Arc<AtomicBool>,
|
|
_config: SyncConfig,
|
|
}
|
|
|
|
impl SyncProcessor {
|
|
/// Creates a new SyncProcessor
|
|
pub fn new(
|
|
db_service: Arc<RusqliteService>,
|
|
jellyfin_client: Arc<TokioMutex<Option<JellyfinClient>>>,
|
|
repository: Arc<dyn MediaRepository>,
|
|
) -> Self {
|
|
Self {
|
|
_db_service: db_service,
|
|
_jellyfin_client: jellyfin_client,
|
|
_repository: repository,
|
|
_processing: Arc::new(TokioMutex::new(false)),
|
|
_cancelled: Arc::new(AtomicBool::new(false)),
|
|
_config: SyncConfig::default(),
|
|
}
|
|
}
|
|
|
|
/// Starts the sync processor
|
|
pub async fn start(&self) -> Result<(), String> {
|
|
log::info!("[SyncProcessor] Started (placeholder implementation)");
|
|
// TODO: Implement full processor logic
|
|
Ok(())
|
|
}
|
|
|
|
/// Stops the sync processor
|
|
pub async fn stop(&self) -> Result<(), String> {
|
|
log::info!("[SyncProcessor] Stopped (placeholder implementation)");
|
|
// TODO: Implement stop logic
|
|
Ok(())
|
|
}
|
|
|
|
/// Processes the sync queue once
|
|
pub async fn process_queue(&self) -> Result<(), String> {
|
|
log::debug!("[SyncProcessor] Processing queue (placeholder)");
|
|
// TODO: Implement queue processing
|
|
Ok(())
|
|
}
|
|
|
|
/// Calculates exponential backoff delay
|
|
fn _calculate_backoff(&self, retry_count: u32) -> Duration {
|
|
let delay_ms = self._config.base_retry_delay_ms * 2_u64.pow(retry_count);
|
|
let max_delay_ms = 10_000; // 10 seconds max
|
|
Duration::from_millis(delay_ms.min(max_delay_ms))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_sync_config_default() {
|
|
let config = SyncConfig::default();
|
|
assert_eq!(config.max_retries, 5);
|
|
assert_eq!(config.base_retry_delay_ms, 1000);
|
|
assert_eq!(config.batch_size, 10);
|
|
}
|
|
|
|
// TODO: Re-enable when SyncProcessor is fully implemented
|
|
// #[test]
|
|
// fn test_calculate_backoff() {
|
|
// let config = SyncConfig::default();
|
|
// let processor = SyncProcessor {
|
|
// _db_service: Arc::new(unsafe { std::mem::zeroed() }), // Placeholder for test
|
|
// _jellyfin_client: Arc::new(TokioMutex::new(None)),
|
|
// _repository: Arc::new(unsafe { std::mem::zeroed() }), // Placeholder for test
|
|
// _processing: Arc::new(TokioMutex::new(false)),
|
|
// _cancelled: Arc::new(AtomicBool::new(false)),
|
|
// _config: config,
|
|
// };
|
|
//
|
|
// // Test exponential backoff: 1s, 2s, 4s, 8s, 10s (capped)
|
|
// assert_eq!(processor._calculate_backoff(0), Duration::from_millis(1000));
|
|
// assert_eq!(processor._calculate_backoff(1), Duration::from_millis(2000));
|
|
// assert_eq!(processor._calculate_backoff(2), Duration::from_millis(4000));
|
|
// assert_eq!(processor._calculate_backoff(3), Duration::from_millis(8000));
|
|
// assert_eq!(processor._calculate_backoff(4), Duration::from_millis(10000)); // capped
|
|
// assert_eq!(processor._calculate_backoff(5), Duration::from_millis(10000)); // capped
|
|
// }
|
|
}
|