mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
fix(downloads): clean settled archives before deleting stale rows
This commit is contained in:
1 parent
0a780ecd10
commit
916f216655
3 files changed
+41
-2
No files matched your search
@@ -56,7 +56,13 @@ export async function removeDownloadRequest(downloadId: number) {
|
||||
success: false,
|
||||
error: 'Download is completing; try again shortly',
|
||||
};
|
||||
if (row?.filePath && removablePartialStatuses.has(row.status)) {
|
||||
// A settled archive can still say downloading when its cancellation
|
||||
// status write failed. Its journal must be drained before row deletion.
|
||||
if (
|
||||
row?.filePath &&
|
||||
(row.contentType === 'catchup' ||
|
||||
removablePartialStatuses.has(row.status))
|
||||
) {
|
||||
try {
|
||||
if (row.contentType === 'catchup')
|
||||
removeJournaledCatchupPartial(
|
||||
|
||||
@@ -78,6 +78,37 @@ describe('downloads events: partial-file cleanup', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.each([false, true])(
|
||||
'retries settled archive cleanup despite a failed cancellation status write (locked=%s)',
|
||||
async (locked) => {
|
||||
const { deleteWhere } = mockDownloadRow({
|
||||
...createDownloadRow('downloading'),
|
||||
contentType: 'catchup',
|
||||
});
|
||||
const proof = {
|
||||
version: 1,
|
||||
phase: 'transfer',
|
||||
partialCleanupPath: '/downloads/.iptvnator-cleanup-test/entry',
|
||||
};
|
||||
mockArchiveProofs.mockResolvedValue(new Map([[42, proof]]));
|
||||
if (locked)
|
||||
mockRemoveJournaledPartial.mockImplementation(() => {
|
||||
throw new Error('EACCES');
|
||||
});
|
||||
await expect(
|
||||
getHandler('DOWNLOADS_REMOVE')(null, 42)
|
||||
).resolves.toMatchObject({ success: !locked });
|
||||
expect(mockRemoveJournaledPartial).toHaveBeenCalledWith(
|
||||
'/downloads/resume.mp4',
|
||||
proof,
|
||||
expect.any(Function),
|
||||
true
|
||||
);
|
||||
if (locked) expect(deleteWhere).not.toHaveBeenCalled();
|
||||
else expect(deleteWhere).toHaveBeenCalled();
|
||||
}
|
||||
);
|
||||
|
||||
it('keeps a committing archive row and journal intact on Remove', async () => {
|
||||
const { deleteWhere } = mockDownloadRow(
|
||||
createDownloadRow('downloading')
|
||||
|
||||
@@ -89,7 +89,9 @@ overwritten by completion. Remove rejects this committing row before partial
|
||||
cleanup or deletion, and Clear completed skips it, preserving the cascading
|
||||
journal until completion finishes. Remove waits for an accepted active archive
|
||||
cancellation to settle before reading/deleting its row; queued archives are
|
||||
canceled first, and a concurrent new runtime attempt blocks removal. Remove, Clear completed and missing-file
|
||||
canceled first, and a concurrent new runtime attempt blocks removal. A settled
|
||||
archive still marked downloading after a failed status write also retries
|
||||
journal cleanup before row deletion. Remove, Clear completed and missing-file
|
||||
re-download use journal-backed private capture for archive partial cleanup;
|
||||
unknown or replaced entries are preserved. Before capture, a synchronous SQLite
|
||||
write records `partialCleanupPath` (or `finalCleanupPath` for failed promotion)
|
||||
|
||||
Reference in new issue
Block a user