fix(downloads): re-resolve options on retry instead of replaying stale ones

A queued item freezes its post-processing options at enqueue time, so
retrying a failed download replayed whatever was in effect when it was
first added -- a settings change made afterward (e.g. turning
SponsorBlock-remove off, which forces a slow, failure-prone software
re-encode) never took effect on retry.

retry/retryAll now re-resolve a source-tied item's options from the
current source profile / global settings (the same precedence enqueue
uses) before requeuing it. Manual pastes with no source keep their
explicit per-download options untouched, and this is deliberately
distinct from the history re-download path, which keeps frozen options
on purpose to reproduce an identical file.
This commit is contained in:
2026-07-05 14:39:45 -04:00
parent 5de4b40c1d
commit 6cb9c5ca47
3 changed files with 152 additions and 24 deletions
+63
View File
@@ -0,0 +1,63 @@
import { describe, it, expect } from 'vitest'
import { pickRetryOptions } from '../src/renderer/src/store/retryOptions'
import { DEFAULT_DOWNLOAD_OPTIONS, type Source, type MediaProfile } from '../src/shared/ipc'
import type { ProfileDefaults } from '../src/shared/profileResolve'
const source = (over: Partial<Source> = {}): Source => ({
id: 'ac8e6608',
url: 'https://youtube.com/@NetworkChuck',
kind: 'channel',
title: 'NetworkChuck',
channel: 'NetworkChuck',
addedAt: 0,
itemCount: 0,
...over
})
// Global defaults with SponsorBlock OFF — the state after the user toggled it.
const defaults: ProfileDefaults = {
kind: 'video',
videoQuality: '1080p',
audioQuality: 'Best',
options: { ...DEFAULT_DOWNLOAD_OPTIONS, sponsorBlock: false },
outputTemplate: ''
}
describe('pickRetryOptions', () => {
it('re-resolves a source-tied item to the current global options (SponsorBlock now off)', () => {
const group = pickRetryOptions('ac8e6608:qkUTB65OfAc', [source()], [], defaults)
expect(group?.options?.sponsorBlock).toBe(false)
expect(group?.extraArgs).toBeUndefined()
expect(group?.collectionOutputTemplate).toBeUndefined()
})
it('returns null for a manual paste (no mediaItemId) so explicit options are kept', () => {
expect(pickRetryOptions(undefined, [source()], [], defaults)).toBeNull()
})
it('returns null when the source has since been deleted', () => {
expect(pickRetryOptions('deleted99:vid', [source()], [], defaults)).toBeNull()
})
it("prefers the source's Media Profile options over the globals", () => {
const profile: MediaProfile = {
id: 'p1',
name: 'Archive',
options: { ...DEFAULT_DOWNLOAD_OPTIONS, sponsorBlock: true, sponsorBlockMode: 'remove' },
extraArgs: '--no-mtime'
}
const group = pickRetryOptions(
'ac8e6608:vid',
[source({ profileId: 'p1' })],
[profile],
defaults
)
expect(group?.options?.sponsorBlock).toBe(true)
expect(group?.extraArgs).toBe('--no-mtime')
})
it('falls back to the globals when the source points at a dangling profile id', () => {
const group = pickRetryOptions('ac8e6608:vid', [source({ profileId: 'gone' })], [], defaults)
expect(group?.options?.sponsorBlock).toBe(false)
})
})