feat(downloads): estimate a transcode's size so the progress bar moves
A transcode is produced as it is sent — chunked, with no Content-Length — and the worker reported progress 0.0 for its whole duration: an empty bar reading "0%" while the byte count climbed for an hour. That is the case every film whose audio must be re-encoded lands in. The backend already fetches the item to decide the audio policy, and that item carries what a prediction needs: the source's size (an `original` download copies the picture, so the output is the source give or take the audio track) and its runtime (a preset re-encodes at fixed rates, so the size is rate × runtime — from a preset table the URL builder now shares, so the two cannot drift). The prediction is made where the URL is resolved and persisted as the row's file_size. The worker uses it only when the response has no length; the server's figure always wins; an estimated bar is capped at 99% so a low prediction never shows a finished download still running; and the Completed event now carries the bytes actually written so the frontend stops persisting the row's file_size as the final size. The row renders three honest states: exact "42%", estimated "~42%" with "X / ~Y", or — with no total at all — an indeterminate band and the bytes so far, never "0%". The single-video button joins the series/season buttons on the enqueue path so all three resolve, and predict, in one place. DR-290, UT-252, UT-253, UT-254. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,10 @@ pub enum DownloadEvent {
|
||||
bytes_downloaded: i64,
|
||||
total_bytes: Option<i64>,
|
||||
progress: f64, // 0.0 to 1.0
|
||||
/// `total_bytes` is a prediction rather than the server's
|
||||
/// `Content-Length`, so `progress` stops short of 1.0 until the
|
||||
/// download completes. TRACES: UR-071 | DR-290
|
||||
estimated: bool,
|
||||
},
|
||||
/// Download completed successfully
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -27,6 +31,10 @@ pub enum DownloadEvent {
|
||||
download_id: i64,
|
||||
item_id: String,
|
||||
file_path: String,
|
||||
/// Bytes actually written. The frontend persists completion too, and
|
||||
/// without this it fell back to the row's `file_size` — which is a
|
||||
/// prediction for a transcode (DR-290), not the real size.
|
||||
bytes_downloaded: i64,
|
||||
},
|
||||
/// Download failed with error
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -60,6 +68,7 @@ mod tests {
|
||||
bytes_downloaded: 1024,
|
||||
total_bytes: Some(2048),
|
||||
progress: 0.5,
|
||||
estimated: false,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&event).unwrap();
|
||||
@@ -86,6 +95,7 @@ mod tests {
|
||||
download_id: 42,
|
||||
item_id: "song456".to_string(),
|
||||
file_path: "/path/to/file.mp3".to_string(),
|
||||
bytes_downloaded: 4096,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&event).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user