fix: address post-audit regressions across queue, db, and ui

- Preserved extension-captured cookies through the Add modal, with a clean fallback when captured cookies break metadata fetching.
- Prevented batched extension captures from losing URLs or reusing stale cookie/header contexts.
- Fixed pause/resume and enqueue generation races, including cancellation during queue reservation and replay after task removal.
- Made startup database initialization safe under React StrictMode.
- Serialized keyring operations and corrected Linux legacy migration/deletion behavior.
- Restored `Downloading` state after yt-dlp retries.
- Replaced hardcoded media heights with dynamically detected formats, including nonstandard qualities such as 576p and 2880p.
This commit is contained in:
NimBold
2026-07-10 12:07:25 +03:30
parent 3fbd0742be
commit 4f4c655de6
11 changed files with 550 additions and 186 deletions
+21
View File
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
import {
appendRequestUrlsAfterVersion,
canSubmitMetadataRows,
mediaFormatSelectorForRow,
mediaFileNameForSelectedFormat,
@@ -93,6 +94,7 @@ describe('add download metadata workflow', () => {
status: 'ready',
generation: 4,
requestContextVersion: 1,
requestCookiesOmitted: true,
formats: [{
name: '1080p MP4',
selector: '137+140',
@@ -119,11 +121,30 @@ describe('add download metadata workflow', () => {
status: 'loading',
generation: 5,
requestContextVersion: 2,
requestCookiesOmitted: false,
formats: undefined,
selectedFormat: undefined
});
});
it('appends every unseen handoff after the observed version', () => {
const merged = appendRequestUrlsAfterVersion(
'https://existing.example/file.zip',
{
'https://first.example/file.zip': { version: 2 },
'https://second.example/file.zip': { version: 3 },
'https://existing.example/file.zip': { version: 4 }
},
1
);
expect(merged).toBe(
'https://existing.example/file.zip\n' +
'https://first.example/file.zip\n' +
'https://second.example/file.zip'
);
});
it('upgrades an existing normal row when the user explicitly fetches it as media', () => {
const existing = row({
sourceUrl: 'https://adult.example/watch/123',
+31
View File
@@ -27,6 +27,7 @@ export interface AddDownloadDraftRow {
status: MetadataStatus;
generation: number;
requestContextVersion?: number;
requestCookiesOmitted?: boolean;
isMedia: boolean;
resumable?: boolean;
formats?: AddMediaFormat[];
@@ -93,6 +94,7 @@ export const reconcileDownloadRows = (
status: 'loading',
generation: preserved.generation + 1,
requestContextVersion,
requestCookiesOmitted: false,
isMedia: preserved.isMedia || forcedMedia,
formats: preserved.isMedia || forcedMedia ? undefined : preserved.formats,
selectedFormat: preserved.isMedia || forcedMedia ? undefined : preserved.selectedFormat
@@ -120,6 +122,35 @@ export const reconcileDownloadRows = (
});
};
const comparableUrl = (rawUrl: string): string => {
try {
return new URL(rawUrl).href;
} catch {
return rawUrl.trim();
}
};
export const appendRequestUrlsAfterVersion = (
rawText: string,
requestContexts: Readonly<Record<string, { version: number }>>,
observedVersion: number
): string => {
const lines = rawText.split('\n').map(line => line.trim()).filter(Boolean);
const seen = new Set(lines.map(comparableUrl));
const additions = Object.entries(requestContexts)
.filter(([, context]) => context.version > observedVersion)
.sort(([, left], [, right]) => left.version - right.version);
for (const [url] of additions) {
const identity = comparableUrl(url);
if (seen.has(identity)) continue;
seen.add(identity);
lines.push(url);
}
return lines.join('\n');
};
export const updateRowIfCurrent = (
rows: AddDownloadDraftRow[],
id: string,