mirror of
https://github.com/alexta69/metube.git
synced 2026-09-21 13:35:01 +00:00
fix: point audio download links at the directory the server used (closes #533)
The server picks the download directory on download_type alone (ytdl.py:1530: AUDIO_DOWNLOAD_DIR if download_type == 'audio'). The UI picked the URL base on download_type *or* a .mp3 extension, so the two disagreed for any mp3 produced under a video-type download -- a postprocessor, a preset, or a record predating download_type. Those files are written to DOWNLOAD_DIR but were linked under audio_download/, giving a 404 on every instance where the two directories differ. The .mp3 clause was not an incomplete audio check to be extended with more extensions; it was a second, conflicting rule. Removing it makes the UI agree with where the file actually is. Same fix in buildChapterDownloadLink, which carried a copy. Test verified by reintroducing the bug: the video-type mp3 case fails with 'audio_download/song.mp3' as expected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -413,4 +413,64 @@ describe('App', () => {
|
||||
expect(errorSpy).toHaveBeenCalledWith('Subscription name must not be empty');
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
// Issue #533: the server picks AUDIO_DOWNLOAD_DIR on download_type alone
|
||||
// (ytdl.py), so the UI's choice of URL base has to use the same rule. It used
|
||||
// to also treat any .mp3 as audio, which pointed the link at audio_download/
|
||||
// for files the server had written to DOWNLOAD_DIR.
|
||||
describe('download links follow the server directory rule (#533)', () => {
|
||||
const makeDownload = (over: Partial<Download>): 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: 'finished',
|
||||
msg: '',
|
||||
percent: 100,
|
||||
speed: 0,
|
||||
eta: 0,
|
||||
filename: 'song.mp4',
|
||||
checked: false,
|
||||
...over,
|
||||
} as Download);
|
||||
|
||||
const appWithDirs = () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
const app = fixture.componentInstance;
|
||||
const downloads = TestBed.inject(DownloadsService) as unknown as DownloadsServiceStub;
|
||||
downloads.configuration['PUBLIC_HOST_URL'] = 'download/';
|
||||
downloads.configuration['PUBLIC_HOST_AUDIO_URL'] = 'audio_download/';
|
||||
return app;
|
||||
};
|
||||
|
||||
it('uses the audio base for an audio download', () => {
|
||||
const app = appWithDirs();
|
||||
const link = app.buildDownloadLink(makeDownload({ download_type: 'audio', filename: 'song.mp3' }));
|
||||
expect(link).toBe('audio_download/song.mp3');
|
||||
});
|
||||
|
||||
it('uses the video base for an mp3 produced by a video download', () => {
|
||||
const app = appWithDirs();
|
||||
const link = app.buildDownloadLink(makeDownload({ download_type: 'video', filename: 'song.mp3' }));
|
||||
expect(link).toBe('download/song.mp3');
|
||||
});
|
||||
|
||||
it('uses the video base for a video download', () => {
|
||||
const app = appWithDirs();
|
||||
const link = app.buildDownloadLink(makeDownload({ filename: 'clip.mp4' }));
|
||||
expect(link).toBe('download/clip.mp4');
|
||||
});
|
||||
|
||||
it('applies the same rule to chapter links', () => {
|
||||
const app = appWithDirs();
|
||||
const dl = makeDownload({ download_type: 'video' });
|
||||
expect(app.buildChapterDownloadLink(dl, 'ch1.mp3')).toBe('download/ch1.mp3');
|
||||
const audio = makeDownload({ download_type: 'audio' });
|
||||
expect(app.buildChapterDownloadLink(audio, 'ch1.mp3')).toBe('audio_download/ch1.mp3');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+8
-2
@@ -1284,7 +1284,12 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
|
||||
buildDownloadLink(download: Download) {
|
||||
let baseDir = this.downloads.configuration["PUBLIC_HOST_URL"];
|
||||
if (download.download_type === 'audio' || download.filename.endsWith('.mp3')) {
|
||||
// Must match the server's directory rule exactly: ytdl.py writes to
|
||||
// AUDIO_DOWNLOAD_DIR on download_type alone. Treating any .mp3 as audio
|
||||
// sent the link to audio_download/ for mp3s produced under a video-type
|
||||
// download (a postprocessor, a preset, or a legacy record), which the
|
||||
// server had written to DOWNLOAD_DIR -- a 404 whenever the two differ.
|
||||
if (download.download_type === 'audio') {
|
||||
baseDir = this.downloads.configuration["PUBLIC_HOST_AUDIO_URL"];
|
||||
}
|
||||
|
||||
@@ -1382,7 +1387,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
|
||||
buildChapterDownloadLink(download: Download, chapterFilename: string) {
|
||||
let baseDir = this.downloads.configuration["PUBLIC_HOST_URL"];
|
||||
if (download.download_type === 'audio' || chapterFilename.endsWith('.mp3')) {
|
||||
// Same server-side rule as buildDownloadLink above.
|
||||
if (download.download_type === 'audio') {
|
||||
baseDir = this.downloads.configuration["PUBLIC_HOST_AUDIO_URL"];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user