feat: first-class SponsorBlock toggle

A "Remove sponsor segments" switch next to "Split by chapters" queues
the download with the same postprocessor pair the CLI's
--sponsorblock-remove sponsor builds (SponsorBlock + ModifyChapters).
The flag persists as a cookie like the other form options, survives in
the queue records, and is carried into retries.

The pair is registered above the chapter-splitting block: yt-dlp runs
same-stage postprocessors in list order, so ModifyChapters has to
rewrite the chapter list before FFmpegSplitChapters cuts the file up,
matching what the CLI builds for --sponsorblock-remove sponsor
--split-chapters. With both toggles on the other way around the chapter
files keep the sponsor segments and the removal desyncs the remaining
chapter timings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
tjelite1986
2026-07-16 22:19:41 +02:00
parent ac46fff6d9
commit 8c2990e68a
9 changed files with 147 additions and 1 deletions
+63
View File
@@ -678,6 +678,69 @@ class DownloadResultTests(unittest.TestCase):
)
def _capture_ytdl_params(download: Download) -> dict:
"""Run ``_download`` far enough to capture the params it builds."""
fake_ydl = MagicMock()
fake_ydl.download.return_value = 0
download.status_queue = types.SimpleNamespace(put=lambda _: None)
with patch('ytdl.install_socket_guard'), \
patch.object(Download, '_make_youtube_dl', return_value=fake_ydl) as make:
download._download()
params, = make.call_args.args
return params
class SponsorBlockPostprocessorTests(unittest.TestCase):
def test_no_sponsorblock_postprocessors_when_disabled(self):
download = _make_test_download()
params = _capture_ytdl_params(download)
keys = [pp['key'] for pp in params.get('postprocessors', [])]
self.assertNotIn('SponsorBlock', keys)
self.assertNotIn('ModifyChapters', keys)
def test_sponsorblock_pair_matches_the_cli(self):
download = _make_test_download()
download.info.sponsorblock = True
params = _capture_ytdl_params(download)
self.assertEqual(
params['postprocessors'],
[
{
'key': 'SponsorBlock',
'categories': ['sponsor'],
'when': 'after_filter',
},
{
'key': 'ModifyChapters',
'remove_sponsor_segments': ['sponsor'],
'force_keyframes': False,
},
],
)
def test_segment_removal_runs_before_the_chapter_split(self):
# yt-dlp runs same-stage postprocessors in list order, so ModifyChapters
# has to rewrite the chapter list before FFmpegSplitChapters cuts the
# file up -- the order the CLI builds for
# --sponsorblock-remove sponsor --split-chapters.
download = _make_test_download()
download.info.sponsorblock = True
download.info.split_by_chapters = True
download.info.chapter_template = '%(section_number)s.%(ext)s'
params = _capture_ytdl_params(download)
keys = [pp['key'] for pp in params['postprocessors']]
self.assertEqual(keys, ['SponsorBlock', 'ModifyChapters', 'FFmpegSplitChapters'])
self.assertEqual(params['outtmpl']['chapter'], '%(section_number)s.%(ext)s')
class ProgressThrottleTests(unittest.TestCase):
def test_downloading_ticks_are_throttled(self):
dl = _make_test_download()