feat(downloads): granular post-processing phase labels

ffmpeg post-steps have no byte-progress, so instead of a silent 'Processing' the row now names
the current step (Merging / Extracting audio / Embedding thumbnail / Removing sponsors / Writing
metadata) with an indeterminate pulse. Byte-progress phases (video/audio) keep the % bar.
i18n en/hu/de.
This commit is contained in:
npeter83 2026-07-03 04:01:53 +02:00
parent 4b15d55092
commit fc69656d12
5 changed files with 39 additions and 8 deletions

View file

@ -246,15 +246,30 @@ def _make_progress_hook(job_id: int, asset_id: int):
return hook return hook
def _pp_phase(pp: str) -> str:
"""Map a yt-dlp postprocessor class name to a user-facing phase label. ffmpeg post-steps
aren't byte-progress operations (no %), so the UI shows the step name + an indeterminate
pulse this makes the long merge/finalize legible instead of a silent 'Processing'."""
if "Merg" in pp:
return "merging"
if "ExtractAudio" in pp:
return "audio_extract"
if "Thumbnail" in pp:
return "thumbnail"
if "SponsorBlock" in pp or "ModifyChapters" in pp:
return "sponsorblock"
if "Metadata" in pp:
return "metadata"
return "processing"
def _make_pp_hook(job_id: int): def _make_pp_hook(job_id: int):
"""Postprocessor hook: surface the ffmpeg merge/convert/extract step so the bar shows a """Postprocessor hook: surface the current ffmpeg step so the bar shows a concrete label
'Merging/Processing' label instead of freezing at 100% after the streams finish.""" (Merging / Embedding thumbnail / ) instead of freezing at 100% after the streams finish."""
def hook(d: dict) -> None: def hook(d: dict) -> None:
if d.get("status") in ("started", "processing"): if d.get("status") in ("started", "processing"):
pp = d.get("postprocessor") or "" _set_job(job_id, phase=_pp_phase(d.get("postprocessor") or ""), speed_bps=None, eta_s=None)
phase = "merging" if "Merg" in pp else "processing"
_set_job(job_id, phase=phase, speed_bps=None, eta_s=None)
return hook return hook

View file

@ -36,7 +36,11 @@ const STATUS_CLS: Record<string, string> = {
canceled: "text-muted", canceled: "text-muted",
}; };
const PHASE_KEYS = ["video", "audio", "merging", "processing"]; // Byte-progress phases (show a % bar) vs ffmpeg post-steps (no %, indeterminate pulse).
const DOWNLOAD_PHASES = ["video", "audio"];
const PHASE_KEYS = [
"video", "audio", "merging", "audio_extract", "thumbnail", "sponsorblock", "metadata", "processing",
];
function StatusPill({ job }: { job: DownloadJob }) { function StatusPill({ job }: { job: DownloadJob }) {
const { t } = useTranslation(); const { t } = useTranslation();
@ -116,8 +120,8 @@ function DownloadRow({
</div> </div>
{running && ( {running && (
<div className="mt-1.5"> <div className="mt-1.5">
{job.phase === "merging" || job.phase === "processing" ? ( {job.phase && !DOWNLOAD_PHASES.includes(job.phase) ? (
// No meaningful percentage during ffmpeg — show an indeterminate pulse. // ffmpeg post-steps aren't byte-progress — show an indeterminate pulse, no %.
<div className="h-1.5 rounded-full bg-surface overflow-hidden"> <div className="h-1.5 rounded-full bg-surface overflow-hidden">
<div className="h-full w-full bg-accent/60 animate-pulse" /> <div className="h-full w-full bg-accent/60 animate-pulse" />
</div> </div>

View file

@ -41,6 +41,10 @@
"video": "Video wird geladen", "video": "Video wird geladen",
"audio": "Audio wird geladen", "audio": "Audio wird geladen",
"merging": "Zusammenführen", "merging": "Zusammenführen",
"audio_extract": "Audio wird extrahiert",
"thumbnail": "Vorschaubild einbetten",
"sponsorblock": "Sponsoren entfernen",
"metadata": "Metadaten schreiben",
"processing": "Verarbeitung" "processing": "Verarbeitung"
}, },
"actions": { "actions": {

View file

@ -41,6 +41,10 @@
"video": "Downloading video", "video": "Downloading video",
"audio": "Downloading audio", "audio": "Downloading audio",
"merging": "Merging", "merging": "Merging",
"audio_extract": "Extracting audio",
"thumbnail": "Embedding thumbnail",
"sponsorblock": "Removing sponsors",
"metadata": "Writing metadata",
"processing": "Processing" "processing": "Processing"
}, },
"actions": { "actions": {

View file

@ -41,6 +41,10 @@
"video": "Videósáv letöltése", "video": "Videósáv letöltése",
"audio": "Hangsáv letöltése", "audio": "Hangsáv letöltése",
"merging": "Összefűzés", "merging": "Összefűzés",
"audio_extract": "Hang kinyerése",
"thumbnail": "Bélyegkép beágyazása",
"sponsorblock": "Szponzorok eltávolítása",
"metadata": "Metaadat írása",
"processing": "Feldolgozás" "processing": "Feldolgozás"
}, },
"actions": { "actions": {