mirror of
https://github.com/alexta69/metube.git
synced 2026-09-21 13:35:01 +00:00
fix: show the post-download processing phase in the UI (closes #424)
yt-dlp reports a download 'finished' as soon as the media bytes have
landed, but the ffmpeg work that follows -- merging, re-encoding, chapter
splitting, sponsor removal -- routinely takes longer than the download
itself. The postprocessor hook only ever looked at MoveFiles and
SplitChapters finishing, so that whole phase said nothing: the row sat in
the Downloading table on a full, frozen progress bar, and the item counted
as neither active nor queued in the header stats.
Report a 'postprocessing' status when a postprocessor starts, and reuse
the indeterminate bar the UI already runs for 'preparing', labelled so a
long re-encode is distinguishable from a stall.
Measured on a real download re-encoded with the reporter's FFmpegCopyStream
config, the UI now receives:
[ 0.27s] downloading moving bar
[ 0.28s] finished <- yt-dlp, bytes are down
[ 0.28s] postprocessing animated "Post-processing"
[13.18s] finished done
i.e. 12.9s that used to render as a static 100% bar.
The hook is latched off once MoveFiles reports finished, since that branch
announces the finished file: a 'postprocessing' arriving afterwards would
flip the row back out of its completed state for no reason. It cannot fail
the download -- _download puts an unconditional 'finished' once download()
returns, so the terminal status is settled either way -- but the flicker
and the extra broadcast are both pointless. yt-dlp does run an 'after_move'
stage after MoveFiles, though every postprocessor MeTube configures is
'after_filter' or 'post_process', both of which precede it.
update_status drops a repeated 'postprocessing': yt-dlp's metaclass wraps
run() once per class in a postprocessor's MRO, so one whose subclass
overrides run reports started twice -- FFmpegCopyStream did exactly that in
the live run, three queued statuses collapsing to a single broadcast.
Extracted to _make_postprocessor_hook, mirroring _make_progress_hook, so
the ordering above is testable; every new regression test was confirmed to
fail with the fix backed out.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+6
-2
@@ -732,8 +732,12 @@
|
||||
}
|
||||
</span>
|
||||
} @else {
|
||||
<ngb-progressbar height="1.5rem" [showValue]="download.value.status !== 'preparing'" [striped]="download.value.status === 'preparing'" [animated]="download.value.status === 'preparing'" type="success"
|
||||
[value]="download.value.status === 'preparing' ? 100 : download.value.percent" class="download-progressbar" />
|
||||
<ngb-progressbar height="1.5rem" [showValue]="!isIndeterminate(download.value)" [striped]="isIndeterminate(download.value)" [animated]="isIndeterminate(download.value)" type="success"
|
||||
[value]="isIndeterminate(download.value) ? 100 : download.value.percent" class="download-progressbar">
|
||||
@if (download.value.status === 'postprocessing') {
|
||||
<span>Post-processing</span>
|
||||
}
|
||||
</ngb-progressbar>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -473,4 +473,50 @@ describe('App', () => {
|
||||
expect(app.buildChapterDownloadLink(audio, 'ch1.mp3')).toBe('audio_download/ch1.mp3');
|
||||
});
|
||||
});
|
||||
|
||||
// Issue #424: ffmpeg work after the bytes land (merge, re-encode, split) used
|
||||
// to leave the row on a full, frozen bar with the item counted as neither
|
||||
// active nor queued.
|
||||
describe('post-processing is visible (#424)', () => {
|
||||
const queueEntry = (status: string): Download => ({
|
||||
id: 'vid1',
|
||||
title: 'Test',
|
||||
url: 'https://example.com/v',
|
||||
download_type: 'video',
|
||||
quality: 'best',
|
||||
format: 'any',
|
||||
folder: '',
|
||||
custom_name_prefix: '',
|
||||
playlist_item_limit: 0,
|
||||
status,
|
||||
msg: '',
|
||||
percent: 100,
|
||||
speed: 0,
|
||||
eta: 0,
|
||||
filename: '',
|
||||
checked: false,
|
||||
} as Download);
|
||||
|
||||
it('runs the bar indeterminate while preparing or post-processing', () => {
|
||||
const app = TestBed.createComponent(App).componentInstance;
|
||||
expect(app.isIndeterminate(queueEntry('preparing'))).toBe(true);
|
||||
expect(app.isIndeterminate(queueEntry('postprocessing'))).toBe(true);
|
||||
expect(app.isIndeterminate(queueEntry('downloading'))).toBe(false);
|
||||
expect(app.isIndeterminate(queueEntry('pending'))).toBe(false);
|
||||
});
|
||||
|
||||
it('labels the bar and counts the item as active', () => {
|
||||
// The component subscribes to queueChanged on construction, so the entry
|
||||
// has to be announced after it exists or updateMetrics never runs.
|
||||
const fixture = TestBed.createComponent(App);
|
||||
downloads.queue.set('https://example.com/v', queueEntry('postprocessing'));
|
||||
downloads.queueChanged.next();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect((fixture.nativeElement as HTMLElement).textContent).toContain('Post-processing');
|
||||
expect(fixture.componentInstance.activeDownloads).toBe(1);
|
||||
expect(fixture.componentInstance.queuedDownloads).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
+9
-1
@@ -1184,6 +1184,14 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
this.downloads.startById([id]).subscribe((res) => this.handleActionResult(res, 'Start download failed'));
|
||||
}
|
||||
|
||||
// 'preparing' (yt-dlp starting up) and 'postprocessing' (ffmpeg merging,
|
||||
// re-encoding or splitting once the bytes have landed) both have real work in
|
||||
// flight with no percentage to report, so the bar runs animated at full width
|
||||
// instead of showing a number that cannot move.
|
||||
isIndeterminate(download: Download): boolean {
|
||||
return download.status === 'preparing' || download.status === 'postprocessing';
|
||||
}
|
||||
|
||||
liveCountdownSeconds(download: Download): number | null {
|
||||
const ts = download.live_release_timestamp;
|
||||
if (ts == null || download.status !== 'scheduled') {
|
||||
@@ -1697,7 +1705,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
if (download.status === 'downloading') {
|
||||
active++;
|
||||
speed += download.speed || 0;
|
||||
} else if (download.status === 'preparing') {
|
||||
} else if (download.status === 'preparing' || download.status === 'postprocessing') {
|
||||
active++;
|
||||
} else if (download.status === 'pending' || download.status === 'scheduled') {
|
||||
queued++;
|
||||
|
||||
Reference in New Issue
Block a user