fix(downloads): harden add modal and retry state

Route explicit no-limit speed overrides without inheriting the global cap.

Preserve dotted media titles when switching formats and drain retry gid completions through remember_gid.
This commit is contained in:
NimBold
2026-07-08 21:57:12 +03:30
parent f84726a403
commit 161b0028f9
9 changed files with 205 additions and 71 deletions
+31
View File
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
import {
canSubmitMetadataRows,
mediaFormatSelectorForRow,
mediaFileNameForSelectedFormat,
metadataSummaryMessage,
reconcileDownloadRows,
refreshFailedMetadataRows,
@@ -154,6 +155,36 @@ describe('add download metadata workflow', () => {
expect(mediaFormatSelectorForRow(failedMedia)).toBeUndefined();
});
it('replaces only known media container suffixes when selecting formats', () => {
const mediaRow = row({
isMedia: true,
formats: [
{
name: '1080p MP4',
selector: 'mp4',
ext: 'mp4',
formatLabel: '1080p',
detail: '10 MB',
type: 'Video',
bytes: 10
},
{
name: '1080p MKV',
selector: 'mkv',
ext: 'mkv',
formatLabel: '1080p',
detail: '11 MB',
type: 'Video',
bytes: 11
}
],
selectedFormat: 1
});
expect(mediaFileNameForSelectedFormat('Version 1.5', mediaRow)).toBe('Version 1.5.mkv');
expect(mediaFileNameForSelectedFormat('Version 1.5.mp4', mediaRow)).toBe('Version 1.5.mkv');
});
it('reports fallback and invalid states accurately', () => {
expect(metadataSummaryMessage([
row(),
+32
View File
@@ -148,6 +148,38 @@ export const mediaFormatSelectorForRow = (
return row.formats?.[row.selectedFormat]?.selector;
};
export const mediaFileNameForSelectedFormat = (
fileName: string,
row: Pick<AddDownloadDraftRow, 'formats' | 'selectedFormat'>
): string => {
const selectedFormat = row.selectedFormat === undefined
? undefined
: row.formats?.[row.selectedFormat];
if (!selectedFormat) return canonicalizeDownloadFileName(fileName);
const cleanFileName = canonicalizeDownloadFileName(fileName);
const selectedExt = selectedFormat.ext.replace(/^\.+/, '');
if (!selectedExt) return cleanFileName;
const lowerFileName = cleanFileName.toLowerCase();
if (lowerFileName.endsWith(`.${selectedExt.toLowerCase()}`)) {
return cleanFileName;
}
const lastDot = cleanFileName.lastIndexOf('.');
const currentExt = lastDot > 0 ? cleanFileName.slice(lastDot + 1).toLowerCase() : '';
const knownFormatExts = new Set(
(row.formats || [])
.map(format => format.ext.replace(/^\.+/, '').toLowerCase())
.filter(Boolean)
);
const baseName = currentExt && knownFormatExts.has(currentExt)
? cleanFileName.slice(0, lastDot)
: cleanFileName;
return canonicalizeDownloadFileName(`${baseName}.${selectedExt}`);
};
export const metadataSummaryMessage = (rows: AddDownloadDraftRow[]): string => {
if (rows.length === 0) return 'Paste one or more links.';