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:
@@ -128,7 +128,9 @@ impl DatabaseService for RusqliteService {
|
||||
async fn execute(&self, query: Query) -> DbResult<usize> {
|
||||
let conn = Arc::clone(&self.conn);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let conn = conn.lock().map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
let conn = conn
|
||||
.lock()
|
||||
.map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
execute_query(&conn, query)
|
||||
})
|
||||
.await
|
||||
@@ -139,7 +141,9 @@ impl DatabaseService for RusqliteService {
|
||||
let conn = Arc::clone(&self.conn);
|
||||
let sql = sql.to_string();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let conn = conn.lock().map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
let conn = conn
|
||||
.lock()
|
||||
.map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
conn.execute_batch(&sql)
|
||||
.map_err(|e| format!("Execute batch failed: {}", e))
|
||||
})
|
||||
@@ -154,7 +158,9 @@ impl DatabaseService for RusqliteService {
|
||||
{
|
||||
let conn = Arc::clone(&self.conn);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let conn = conn.lock().map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
let conn = conn
|
||||
.lock()
|
||||
.map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
query_one(&conn, query, mapper)
|
||||
})
|
||||
.await
|
||||
@@ -168,7 +174,9 @@ impl DatabaseService for RusqliteService {
|
||||
{
|
||||
let conn = Arc::clone(&self.conn);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let conn = conn.lock().map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
let conn = conn
|
||||
.lock()
|
||||
.map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
query_optional(&conn, query, mapper)
|
||||
})
|
||||
.await
|
||||
@@ -182,7 +190,9 @@ impl DatabaseService for RusqliteService {
|
||||
{
|
||||
let conn = Arc::clone(&self.conn);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let conn = conn.lock().map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
let conn = conn
|
||||
.lock()
|
||||
.map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
query_many(&conn, query, mapper)
|
||||
})
|
||||
.await
|
||||
@@ -196,7 +206,9 @@ impl DatabaseService for RusqliteService {
|
||||
{
|
||||
let conn = Arc::clone(&self.conn);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let conn = conn.lock().map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
let conn = conn
|
||||
.lock()
|
||||
.map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
|
||||
conn.execute("BEGIN TRANSACTION", [])
|
||||
.map_err(|e| format!("Failed to begin transaction: {}", e))?;
|
||||
@@ -224,7 +236,9 @@ impl DatabaseService for RusqliteService {
|
||||
async fn last_insert_rowid(&self) -> DbResult<i64> {
|
||||
let conn = Arc::clone(&self.conn);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let conn = conn.lock().map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
let conn = conn
|
||||
.lock()
|
||||
.map_err(|e| format!("Failed to lock connection: {}", e))?;
|
||||
Ok(conn.last_insert_rowid())
|
||||
})
|
||||
.await
|
||||
@@ -255,7 +269,9 @@ where
|
||||
{
|
||||
match query_one(conn, query, mapper) {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(e) if e.contains("Query returned no rows") || e.contains("QueryReturnedNoRows") => Ok(None),
|
||||
Err(e) if e.contains("Query returned no rows") || e.contains("QueryReturnedNoRows") => {
|
||||
Ok(None)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
@@ -325,10 +341,7 @@ mod tests {
|
||||
let service = RusqliteService::new(Arc::new(Mutex::new(conn)));
|
||||
|
||||
let query = Query::new("SELECT name FROM test WHERE id = 1");
|
||||
let name: String = service
|
||||
.query_one(query, |row| row.get(0))
|
||||
.await
|
||||
.unwrap();
|
||||
let name: String = service.query_one(query, |row| row.get(0)).await.unwrap();
|
||||
|
||||
assert_eq!(name, "Bob");
|
||||
}
|
||||
@@ -346,10 +359,7 @@ mod tests {
|
||||
let service = RusqliteService::new(Arc::new(Mutex::new(conn)));
|
||||
|
||||
let query = Query::new("SELECT name FROM test ORDER BY id");
|
||||
let names: Vec<String> = service
|
||||
.query_many(query, |row| row.get(0))
|
||||
.await
|
||||
.unwrap();
|
||||
let names: Vec<String> = service.query_many(query, |row| row.get(0)).await.unwrap();
|
||||
|
||||
assert_eq!(names, vec!["Alice", "Bob"]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user