fix(downloads): recover local archives before remote transfer checks

This commit is contained in:
4gray committed 2026-09-08 04:00:29 +02:00
1 parent 0c13c3ad8c
commit c2868c8ac8
3 files changed
+25 -8

No files matched your search

@@ -1174,6 +1174,7 @@ it.each([
channelName: 'News',
startTimestamp: 100,
stopTimestamp: 200,
expiresAt: 201,
};
jest.doMock('./download-catchup-journal', () => ({
...jest.requireActual('./download-catchup-journal'),
@@ -1203,6 +1204,13 @@ it.each([
programmeStart: 100,
})
);
const { assertRemoteUrlAllowed } = await import('../url-safety');
jest.mocked(assertRemoteUrlAllowed).mockRejectedValue(
new Error('ENOTFOUND')
);
jest.mocked(h.authorizer.requireAuthorized).mockRejectedValue(
new Error('Folder unavailable')
);
const result =
action === 'start'
? h.startDownloadRequest(
@@ -1240,6 +1248,8 @@ it.each([
expect.objectContaining({ filePath: null })
);
expect(h.enqueueDownload).not.toHaveBeenCalled();
expect(assertRemoteUrlAllowed).not.toHaveBeenCalled();
expect(h.authorizer.requireAuthorized).not.toHaveBeenCalled();
expect(await readFile(filePath, 'utf8')).toBe('complete archive');
} finally {
jest.dontMock('./download-catchup-journal');
@@ -31,7 +31,6 @@ export async function startDownloadRequest(
data: StartDownloadRequest,
authorizer: DownloadDirectoryAuthorizer
): Promise<ElectronBridgeDownloadStartResult> {
const catchup = catchupForDownload(data);
const encodedMetadataSnapshot =
data.metadataSnapshot === undefined
? undefined
@@ -47,8 +46,6 @@ export async function startDownloadRequest(
throw new Error('Invalid download metadata snapshot');
}
console.log('[Downloads] Enqueue download:', data.title);
const directory = await authorizer.requireAuthorized(data.downloadFolder);
await assertRemoteUrlAllowed(data.url, { allowPrivateNetworks: true });
const db = await getDatabase();
if (!data.playlistId) {
@@ -67,10 +64,6 @@ export async function startDownloadRequest(
success: false,
};
}
const fileName = catchup
? sanitizeFilename(data.title) + '.ts'
: createFileName(data.title, data.url);
const headers = createHeaders(data.headers);
if (identity.kind === 'match') {
const item = identity.item;
@@ -132,7 +125,19 @@ export async function startDownloadRequest(
reason: ELECTRON_BRIDGE_DOWNLOAD_START_REASONS.AlreadyDownloaded,
};
}
}
// These checks authorize another remote transfer, not reuse of a proven file.
const catchup = catchupForDownload(data);
const directory = await authorizer.requireAuthorized(data.downloadFolder);
await assertRemoteUrlAllowed(data.url, { allowPrivateNetworks: true });
const fileName = catchup
? sanitizeFilename(data.title) + '.ts'
: createFileName(data.title, data.url);
const headers = createHeaders(data.headers);
if (identity.kind === 'match') {
const item = identity.item;
if (
['completed', 'failed', 'canceled'].includes(item.status) &&
item.filePath
+3 -1
View File
@@ -95,7 +95,9 @@ journal cleanup before row deletion. Remove/Clear preserve a final file whose
journaled identity and full size prove completed promotion, even when completion
status writes failed and its stored status is stale. Repeat submissions, Retry
and Resume restore such a journal-proven completion in place before any new
transfer or ownership reset; retained cleanup failures keep their journal. Remove, Clear completed and missing-file
transfer or ownership reset, before expiry, provider DNS and new-folder checks
that apply only to another remote transfer; retained cleanup failures keep their
journal. Remove, Clear completed and missing-file
re-download and repeated programme submissions use journal-backed private
capture for archive partial cleanup;
unknown or replaced entries are preserved. Before capture, a synchronous SQLite