feat(media): support explicit fetch media intent

This commit is contained in:
NimBold
2026-07-07 12:44:19 +03:30
parent e30eff4d2e
commit f711c6b7a4
13 changed files with 268 additions and 47 deletions
+44
View File
@@ -30,6 +30,7 @@ describe('add download metadata workflow', () => {
'https://example.com/file.zip\nhttps://example.com/new.zip',
[existing],
undefined,
new Set(),
() => `new-${nextId++}`
);
@@ -47,12 +48,55 @@ describe('add download metadata workflow', () => {
'https://example.com/a\nhttps://example.com/a\nfile:///tmp/private\nnot-a-url',
[],
undefined,
new Set(),
() => `row-${nextId++}`
);
expect(rows.map(item => item.status)).toEqual(['loading', 'invalid', 'invalid']);
});
it('forces explicit extension media fetches through media metadata for any http page', () => {
const rows = reconcileDownloadRows(
'https://adult.example/watch/123',
[],
undefined,
new Set(['https://adult.example/watch/123'])
);
expect(rows[0]).toMatchObject({
sourceUrl: 'https://adult.example/watch/123',
isMedia: true,
status: 'loading'
});
});
it('upgrades an existing normal row when the user explicitly fetches it as media', () => {
const existing = row({
sourceUrl: 'https://adult.example/watch/123',
downloadUrl: 'https://adult.example/watch/123',
file: '123',
status: 'ready',
generation: 2,
isMedia: false
});
const rows = reconcileDownloadRows(
'https://adult.example/watch/123',
[existing],
undefined,
new Set(['https://adult.example/watch/123'])
);
expect(rows[0]).toMatchObject({
sourceUrl: 'https://adult.example/watch/123',
isMedia: true,
status: 'loading',
generation: 3,
formats: undefined,
selectedFormat: undefined
});
});
it('refreshes only failed metadata and preserves successful format selection', () => {
const ready = row({
id: 'ready',
+15 -2
View File
@@ -71,6 +71,7 @@ export const reconcileDownloadRows = (
rawText: string,
currentRows: AddDownloadDraftRow[],
pendingFilename?: string,
forceMediaUrls: ReadonlySet<string> = new Set(),
createId: () => string = () => crypto.randomUUID()
): AddDownloadDraftRow[] => {
const inputs = parseInputLines(rawText);
@@ -78,7 +79,19 @@ export const reconcileDownloadRows = (
return inputs.map(input => {
const preserved = existing.get(input.sourceUrl);
if (preserved) return preserved;
if (preserved) {
if (input.valid && forceMediaUrls.has(input.sourceUrl) && !preserved.isMedia) {
return {
...preserved,
status: 'loading',
generation: preserved.generation + 1,
isMedia: true,
formats: undefined,
selectedFormat: undefined
};
}
return preserved;
}
const fallback = canonicalizeDownloadFileName(
inputs.length === 1 && pendingFilename
@@ -93,7 +106,7 @@ export const reconcileDownloadRows = (
file: fallback,
status: input.valid ? 'loading' : 'invalid',
generation: input.valid ? 1 : 0,
isMedia: input.valid && isMediaUrl(input.sourceUrl)
isMedia: input.valid && (forceMediaUrls.has(input.sourceUrl) || isMediaUrl(input.sourceUrl))
};
});
};
+6 -1
View File
@@ -18,7 +18,12 @@ let MEDIA_DOMAINS = [
'v.redd.it',
'soundcloud.com',
'facebook.com',
'fb.watch'
'fb.watch',
'pornhub.com',
'redtube.com',
'xhamster.com',
'xnxx.com',
'xvideos.com'
];
const ACTIVE_DOWNLOAD_STATUSES: ReadonlySet<DownloadStatus> = new Set([
+2
View File
@@ -4,6 +4,7 @@ import type { MediaMetadata } from '../bindings/MediaMetadata';
type FetchMediaMetadataArgs = {
url: string;
cookieBrowser: string | null;
userAgent: string | null;
username: string | null;
password: string | null;
headers: string | null;
@@ -17,6 +18,7 @@ const metadataKey = (args: FetchMediaMetadataArgs) =>
JSON.stringify([
args.url,
args.cookieBrowser,
args.userAgent,
args.username,
args.password,
args.headers,