fix(player): play downloaded video offline (DR-133, DR-134)

Offline video never started: the <video> element reported NETWORK_NO_SOURCE
one millisecond after loadstart, which the UI mislabelled as "may need
transcoding" even though nothing had been fetched. Two independent causes,
both required for playback.

The path was doubled. `downloads.file_path` is stored relative to the storage
root while a download is queued, but the worker rewrites it to the absolute
path it actually wrote once the transfer completes — so a completed row is
already rooted. The player's offline branch rooted it a second time, producing
/data/user/0/app//data/user/0/app/videos/x.mp4. Audio was unaffected because it
resolves the same column through Rust's resolve_local_media_path, which does
not re-root. The join is now absolute-aware (POSIX, Windows drive letters, UNC)
so rows written before completion still resolve.

The asset protocol was never enabled. convertFileSrc rewrites a path to
http://asset.localhost/… unconditionally, but Tauri only answers that origin
when the protocol-asset cargo feature is compiled in *and*
app.security.assetProtocol.enable is set — neither was, so even a correct path
resolved to nothing. This also silently defeated the cached-thumbnail path in
imageCache, which fails soft to the server copy and so hid the breakage
whenever the server was reachable. Scoped to $APPDATA/** — the storage root
holding the database, downloads/ and the thumbnail cache — rather than an
unrestricted grant.

Diagnosed from logcat on device; UT-124 reproduces the doubled path.
This commit is contained in:
2026-08-09 15:05:16 +02:00
parent a53042fe80
commit cc7f1cece0
9 changed files with 100 additions and 17 deletions
+10
View File
@@ -1001,6 +1001,16 @@ fn set_env_if_unset(key: &str, value: &str) {
}
}
/// Downloaded media and cached thumbnails are handed to the webview as
/// `http://asset.localhost/…` URLs by `convertFileSrc`. Tauri only answers that
/// origin when the `protocol-asset` cargo feature is compiled in *and*
/// `app.security.assetProtocol.enable` is set in `tauri.conf.json`, which also
/// scopes it to `$APPDATA/**` — the storage root holding the database,
/// `downloads/` and the thumbnail cache. Both are required together: with either
/// missing the URL resolves to nothing and the webview reports
/// `NETWORK_NO_SOURCE`, which is how offline video came to fail silently.
///
/// TRACES: UR-071 | DR-134
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
// Initialize logger