feat(downloads): clear multi-phase download progress + transient-error retries
The progress bar looked broken (95% -> 5% -> recount) because yt-dlp downloads the video and audio streams separately (each 0->100%) then merges — with no indication of which step is running. Now: - worker labels the phase from the stream codecs (video / audio) and via a postprocessor hook (merging / processing), so the user sees what's happening - DownloadCenter shows the phase as the status and renders an indeterminate pulse (no bogus %) during merge/processing; phase i18n en/hu/de - yt-dlp retries (3) + fragment_retries (5) so transient network blips self-heal instead of failing the job (seen once as a DNS 'No address associated with hostname' error) Verified: phase transitions queued -> video -> processing -> done.
This commit is contained in:
parent
c6ceaea899
commit
b200f61642
6 changed files with 73 additions and 9 deletions
|
|
@ -36,8 +36,15 @@ const STATUS_CLS: Record<string, string> = {
|
|||
canceled: "text-muted",
|
||||
};
|
||||
|
||||
const PHASE_KEYS = ["video", "audio", "merging", "processing"];
|
||||
|
||||
function StatusPill({ job }: { job: DownloadJob }) {
|
||||
const { t } = useTranslation();
|
||||
// While downloading, show the concrete phase (video / audio / merging) so the user isn't
|
||||
// confused by the bar resetting between the separate video and audio streams.
|
||||
if (job.status === "running" && job.phase && PHASE_KEYS.includes(job.phase)) {
|
||||
return <span className="text-xs font-medium text-accent">{t(`downloads.phase.${job.phase}`)}</span>;
|
||||
}
|
||||
const key = job.expired ? "expired" : job.status;
|
||||
return (
|
||||
<span className={clsx("text-xs font-medium", STATUS_CLS[job.status] ?? "text-muted")}>
|
||||
|
|
@ -109,14 +116,23 @@ function DownloadRow({
|
|||
</div>
|
||||
{running && (
|
||||
<div className="mt-1.5">
|
||||
<div className="h-1.5 rounded-full bg-surface overflow-hidden">
|
||||
<div className="h-full bg-accent transition-all" style={{ width: `${job.progress}%` }} />
|
||||
</div>
|
||||
<div className="text-[11px] text-muted mt-0.5 flex gap-2">
|
||||
<span>{job.progress}%</span>
|
||||
{job.speed_bps ? <span>{formatSpeed(job.speed_bps)}</span> : null}
|
||||
{job.eta_s ? <span>{formatEta(job.eta_s)}</span> : null}
|
||||
</div>
|
||||
{job.phase === "merging" || job.phase === "processing" ? (
|
||||
// No meaningful percentage during ffmpeg — show an indeterminate pulse.
|
||||
<div className="h-1.5 rounded-full bg-surface overflow-hidden">
|
||||
<div className="h-full w-full bg-accent/60 animate-pulse" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="h-1.5 rounded-full bg-surface overflow-hidden">
|
||||
<div className="h-full bg-accent transition-all" style={{ width: `${job.progress}%` }} />
|
||||
</div>
|
||||
<div className="text-[11px] text-muted mt-0.5 flex gap-2">
|
||||
<span>{job.progress}%</span>
|
||||
{job.speed_bps ? <span>{formatSpeed(job.speed_bps)}</span> : null}
|
||||
{job.eta_s ? <span>{formatEta(job.eta_s)}</span> : null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue