]> jfr.im git - yt-dlp.git/blame - test/test_postprocessors.py
[test:download] Raise on network errors (#10283)
[yt-dlp.git] / test / test_postprocessors.py
CommitLineData
cc52de43 1#!/usr/bin/env python3
54007a45 2
88cf6fb3
JMF
3# Allow direct execution
4import os
5import sys
6import unittest
7a340e0d 7
88cf6fb3
JMF
8sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9
54007a45 10
752cda38 11from yt_dlp import YoutubeDL
add96eb9 12from yt_dlp.utils import shell_quote
337e0c62 13from yt_dlp.postprocessor import (
ad3dc496 14 ExecPP,
337e0c62 15 FFmpegThumbnailsConvertorPP,
16 MetadataFromFieldPP,
e9f4ccd1 17 MetadataParserPP,
f8271158 18 ModifyChaptersPP,
63c547d7 19 SponsorBlockPP,
337e0c62 20)
5bfa4862 21
22
23class TestMetadataFromField(unittest.TestCase):
88cf6fb3 24
88cf6fb3 25 def test_format_to_regex(self):
e9f4ccd1 26 self.assertEqual(
27 MetadataParserPP.format_to_regex('%(title)s - %(artist)s'),
28 r'(?P<title>.+)\ \-\ (?P<artist>.+)')
29 self.assertEqual(MetadataParserPP.format_to_regex(r'(?P<x>.+)'), r'(?P<x>.+)')
30
31 def test_field_to_template(self):
32 self.assertEqual(MetadataParserPP.field_to_template('title'), '%(title)s')
33 self.assertEqual(MetadataParserPP.field_to_template('1'), '1')
34 self.assertEqual(MetadataParserPP.field_to_template('foo bar'), 'foo bar')
35 self.assertEqual(MetadataParserPP.field_to_template(' literal'), ' literal')
36
37 def test_metadatafromfield(self):
38 self.assertEqual(
39 MetadataFromFieldPP.to_action('%(title)s \\: %(artist)s:%(title)s : %(artist)s'),
40 (MetadataParserPP.Actions.INTERPRET, '%(title)s : %(artist)s', '%(title)s : %(artist)s'))
337e0c62 41
42
43class TestConvertThumbnail(unittest.TestCase):
44 def test_escaping(self):
45 pp = FFmpegThumbnailsConvertorPP()
46 if not pp.available:
47 print('Skipping: ffmpeg not found')
48 return
49
50 file = 'test/testdata/thumbnails/foo %d bar/foo_%d.{}'
51 tests = (('webp', 'png'), ('png', 'jpg'))
52
53 for inp, out in tests:
54 out_file = file.format(out)
55 if os.path.exists(out_file):
56 os.remove(out_file)
57 pp.convert_thumbnail(file.format(inp), out)
58 assert os.path.exists(out_file)
59
60 for _, out in tests:
61 os.remove(file.format(out))
752cda38 62
63
ad3dc496 64class TestExec(unittest.TestCase):
752cda38 65 def test_parse_cmd(self):
ad3dc496 66 pp = ExecPP(YoutubeDL(), '')
752cda38 67 info = {'filepath': 'file name'}
add96eb9 68 cmd = 'echo {}'.format(shell_quote(info['filepath']))
752cda38 69
ad3dc496 70 self.assertEqual(pp.parse_cmd('echo', info), cmd)
71 self.assertEqual(pp.parse_cmd('echo {}', info), cmd)
72 self.assertEqual(pp.parse_cmd('echo %(filepath)q', info), cmd)
7a340e0d
NA
73
74
75class TestModifyChaptersPP(unittest.TestCase):
76 def setUp(self):
77 self._pp = ModifyChaptersPP(YoutubeDL())
78
79 @staticmethod
63c547d7
AR
80 def _sponsor_chapter(start, end, cat, remove=False, title=None):
81 if title is None:
82 title = SponsorBlockPP.CATEGORIES[cat]
83 return {
84 'start_time': start,
85 'end_time': end,
86 '_categories': [(cat, start, end, title)],
87 **({'remove': True} if remove else {}),
88 }
7a340e0d
NA
89
90 @staticmethod
91 def _chapter(start, end, title=None, remove=False):
92 c = {'start_time': start, 'end_time': end}
93 if title is not None:
94 c['title'] = title
95 if remove:
96 c['remove'] = True
97 return c
98
99 def _chapters(self, ends, titles):
100 self.assertEqual(len(ends), len(titles))
101 start = 0
102 chapters = []
103 for e, t in zip(ends, titles):
104 chapters.append(self._chapter(start, e, t))
105 start = e
106 return chapters
107
108 def _remove_marked_arrange_sponsors_test_impl(
109 self, chapters, expected_chapters, expected_removed):
110 actual_chapters, actual_removed = (
111 self._pp._remove_marked_arrange_sponsors(chapters))
112 for c in actual_removed:
113 c.pop('title', None)
114 c.pop('_categories', None)
115 actual_chapters = [{
116 'start_time': c['start_time'],
117 'end_time': c['end_time'],
118 'title': c['title'],
119 } for c in actual_chapters]
120 self.assertSequenceEqual(expected_chapters, actual_chapters)
121 self.assertSequenceEqual(expected_removed, actual_removed)
122
123 def test_remove_marked_arrange_sponsors_CanGetThroughUnaltered(self):
124 chapters = self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4'])
125 self._remove_marked_arrange_sponsors_test_impl(chapters, chapters, [])
126
127 def test_remove_marked_arrange_sponsors_ChapterWithSponsors(self):
add96eb9 128 chapters = [
129 *self._chapters([70], ['c']),
7a340e0d
NA
130 self._sponsor_chapter(10, 20, 'sponsor'),
131 self._sponsor_chapter(30, 40, 'preview'),
8157a09d 132 self._sponsor_chapter(50, 60, 'filler')]
7a340e0d
NA
133 expected = self._chapters(
134 [10, 20, 30, 40, 50, 60, 70],
135 ['c', '[SponsorBlock]: Sponsor', 'c', '[SponsorBlock]: Preview/Recap',
8157a09d 136 'c', '[SponsorBlock]: Filler Tangent', 'c'])
7a340e0d
NA
137 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
138
63c547d7 139 def test_remove_marked_arrange_sponsors_SponsorBlockChapters(self):
add96eb9 140 chapters = [
141 *self._chapters([70], ['c']),
63c547d7
AR
142 self._sponsor_chapter(10, 20, 'chapter', title='sb c1'),
143 self._sponsor_chapter(15, 16, 'chapter', title='sb c2'),
144 self._sponsor_chapter(30, 40, 'preview'),
145 self._sponsor_chapter(50, 60, 'filler')]
146 expected = self._chapters(
147 [10, 15, 16, 20, 30, 40, 50, 60, 70],
148 ['c', '[SponsorBlock]: sb c1', '[SponsorBlock]: sb c1, sb c2', '[SponsorBlock]: sb c1',
149 'c', '[SponsorBlock]: Preview/Recap',
150 'c', '[SponsorBlock]: Filler Tangent', 'c'])
151 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
152
7a340e0d 153 def test_remove_marked_arrange_sponsors_UniqueNamesForOverlappingSponsors(self):
add96eb9 154 chapters = [
155 *self._chapters([120], ['c']),
156 self._sponsor_chapter(10, 45, 'sponsor'),
157 self._sponsor_chapter(20, 40, 'selfpromo'),
158 self._sponsor_chapter(50, 70, 'sponsor'),
159 self._sponsor_chapter(60, 85, 'selfpromo'),
160 self._sponsor_chapter(90, 120, 'selfpromo'),
161 self._sponsor_chapter(100, 110, 'sponsor')]
7a340e0d
NA
162 expected = self._chapters(
163 [10, 20, 40, 45, 50, 60, 70, 85, 90, 100, 110, 120],
164 ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
165 '[SponsorBlock]: Sponsor',
166 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
167 '[SponsorBlock]: Unpaid/Self Promotion',
168 'c', '[SponsorBlock]: Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion, Sponsor',
169 '[SponsorBlock]: Unpaid/Self Promotion'])
170 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
171
172 def test_remove_marked_arrange_sponsors_ChapterWithCuts(self):
173 cuts = [self._chapter(10, 20, remove=True),
174 self._sponsor_chapter(30, 40, 'sponsor', remove=True),
175 self._chapter(50, 60, remove=True)]
176 chapters = self._chapters([70], ['c']) + cuts
177 self._remove_marked_arrange_sponsors_test_impl(
178 chapters, self._chapters([40], ['c']), cuts)
179
180 def test_remove_marked_arrange_sponsors_ChapterWithSponsorsAndCuts(self):
add96eb9 181 chapters = [
182 *self._chapters([70], ['c']),
7a340e0d
NA
183 self._sponsor_chapter(10, 20, 'sponsor'),
184 self._sponsor_chapter(30, 40, 'selfpromo', remove=True),
185 self._sponsor_chapter(50, 60, 'interaction')]
186 expected = self._chapters([10, 20, 40, 50, 60],
187 ['c', '[SponsorBlock]: Sponsor', 'c',
188 '[SponsorBlock]: Interaction Reminder', 'c'])
189 self._remove_marked_arrange_sponsors_test_impl(
190 chapters, expected, [self._chapter(30, 40, remove=True)])
191
192 def test_remove_marked_arrange_sponsors_ChapterWithSponsorCutInTheMiddle(self):
193 cuts = [self._sponsor_chapter(20, 30, 'selfpromo', remove=True),
194 self._chapter(40, 50, remove=True)]
add96eb9 195 chapters = [
196 *self._chapters([70], ['c']),
197 self._sponsor_chapter(10, 60, 'sponsor'),
198 *cuts]
7a340e0d
NA
199 expected = self._chapters(
200 [10, 40, 50], ['c', '[SponsorBlock]: Sponsor', 'c'])
201 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
202
203 def test_remove_marked_arrange_sponsors_ChapterWithCutHidingSponsor(self):
63c547d7 204 cuts = [self._sponsor_chapter(20, 50, 'selfpromo', remove=True)]
add96eb9 205 chapters = [
206 *self._chapters([60], ['c']),
7a340e0d
NA
207 self._sponsor_chapter(10, 20, 'intro'),
208 self._sponsor_chapter(30, 40, 'sponsor'),
209 self._sponsor_chapter(50, 60, 'outro'),
add96eb9 210 *cuts]
7a340e0d
NA
211 expected = self._chapters(
212 [10, 20, 30], ['c', '[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits'])
213 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
214
215 def test_remove_marked_arrange_sponsors_ChapterWithAdjacentSponsors(self):
add96eb9 216 chapters = [
217 *self._chapters([70], ['c']),
7a340e0d
NA
218 self._sponsor_chapter(10, 20, 'sponsor'),
219 self._sponsor_chapter(20, 30, 'selfpromo'),
220 self._sponsor_chapter(30, 40, 'interaction')]
221 expected = self._chapters(
222 [10, 20, 30, 40, 70],
223 ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion',
224 '[SponsorBlock]: Interaction Reminder', 'c'])
225 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
226
227 def test_remove_marked_arrange_sponsors_ChapterWithAdjacentCuts(self):
add96eb9 228 chapters = [
229 *self._chapters([70], ['c']),
7a340e0d
NA
230 self._sponsor_chapter(10, 20, 'sponsor'),
231 self._sponsor_chapter(20, 30, 'interaction', remove=True),
232 self._chapter(30, 40, remove=True),
63c547d7 233 self._sponsor_chapter(40, 50, 'selfpromo', remove=True),
7a340e0d
NA
234 self._sponsor_chapter(50, 60, 'interaction')]
235 expected = self._chapters([10, 20, 30, 40],
236 ['c', '[SponsorBlock]: Sponsor',
237 '[SponsorBlock]: Interaction Reminder', 'c'])
238 self._remove_marked_arrange_sponsors_test_impl(
239 chapters, expected, [self._chapter(20, 50, remove=True)])
240
241 def test_remove_marked_arrange_sponsors_ChapterWithOverlappingSponsors(self):
add96eb9 242 chapters = [
243 *self._chapters([70], ['c']),
7a340e0d
NA
244 self._sponsor_chapter(10, 30, 'sponsor'),
245 self._sponsor_chapter(20, 50, 'selfpromo'),
246 self._sponsor_chapter(40, 60, 'interaction')]
247 expected = self._chapters(
248 [10, 20, 30, 40, 50, 60, 70],
249 ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
250 '[SponsorBlock]: Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion, Interaction Reminder',
251 '[SponsorBlock]: Interaction Reminder', 'c'])
252 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
253
254 def test_remove_marked_arrange_sponsors_ChapterWithOverlappingCuts(self):
add96eb9 255 chapters = [
256 *self._chapters([70], ['c']),
7a340e0d
NA
257 self._sponsor_chapter(10, 30, 'sponsor', remove=True),
258 self._sponsor_chapter(20, 50, 'selfpromo', remove=True),
259 self._sponsor_chapter(40, 60, 'interaction', remove=True)]
260 self._remove_marked_arrange_sponsors_test_impl(
261 chapters, self._chapters([20], ['c']), [self._chapter(10, 60, remove=True)])
262
263 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsors(self):
add96eb9 264 chapters = [
265 *self._chapters([170], ['c']),
7a340e0d
NA
266 self._sponsor_chapter(0, 30, 'intro'),
267 self._sponsor_chapter(20, 50, 'sponsor'),
268 self._sponsor_chapter(40, 60, 'selfpromo'),
269 self._sponsor_chapter(70, 90, 'sponsor'),
270 self._sponsor_chapter(80, 100, 'sponsor'),
271 self._sponsor_chapter(90, 110, 'sponsor'),
272 self._sponsor_chapter(120, 140, 'selfpromo'),
273 self._sponsor_chapter(130, 160, 'interaction'),
274 self._sponsor_chapter(150, 170, 'outro')]
275 expected = self._chapters(
276 [20, 30, 40, 50, 60, 70, 110, 120, 130, 140, 150, 160, 170],
277 ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Intermission/Intro Animation, Sponsor', '[SponsorBlock]: Sponsor',
278 '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion', 'c',
279 '[SponsorBlock]: Sponsor', 'c', '[SponsorBlock]: Unpaid/Self Promotion',
280 '[SponsorBlock]: Unpaid/Self Promotion, Interaction Reminder',
281 '[SponsorBlock]: Interaction Reminder',
282 '[SponsorBlock]: Interaction Reminder, Endcards/Credits', '[SponsorBlock]: Endcards/Credits'])
283 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
284
285 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingCuts(self):
add96eb9 286 chapters = [
287 *self._chapters([170], ['c']),
7a340e0d
NA
288 self._chapter(0, 30, remove=True),
289 self._sponsor_chapter(20, 50, 'sponsor', remove=True),
290 self._chapter(40, 60, remove=True),
291 self._sponsor_chapter(70, 90, 'sponsor', remove=True),
292 self._chapter(80, 100, remove=True),
293 self._chapter(90, 110, remove=True),
294 self._sponsor_chapter(120, 140, 'sponsor', remove=True),
295 self._sponsor_chapter(130, 160, 'selfpromo', remove=True),
296 self._chapter(150, 170, remove=True)]
297 expected_cuts = [self._chapter(0, 60, remove=True),
298 self._chapter(70, 110, remove=True),
299 self._chapter(120, 170, remove=True)]
300 self._remove_marked_arrange_sponsors_test_impl(
301 chapters, self._chapters([20], ['c']), expected_cuts)
302
303 def test_remove_marked_arrange_sponsors_OverlappingSponsorsDifferentTitlesAfterCut(self):
add96eb9 304 chapters = [
305 *self._chapters([60], ['c']),
7a340e0d
NA
306 self._sponsor_chapter(10, 60, 'sponsor'),
307 self._sponsor_chapter(10, 40, 'intro'),
308 self._sponsor_chapter(30, 50, 'interaction'),
309 self._sponsor_chapter(30, 50, 'selfpromo', remove=True),
310 self._sponsor_chapter(40, 50, 'interaction'),
311 self._sponsor_chapter(50, 60, 'outro')]
312 expected = self._chapters(
313 [10, 30, 40], ['c', '[SponsorBlock]: Sponsor, Intermission/Intro Animation', '[SponsorBlock]: Sponsor, Endcards/Credits'])
314 self._remove_marked_arrange_sponsors_test_impl(
315 chapters, expected, [self._chapter(30, 50, remove=True)])
316
317 def test_remove_marked_arrange_sponsors_SponsorsNoLongerOverlapAfterCut(self):
add96eb9 318 chapters = [
319 *self._chapters([70], ['c']),
7a340e0d
NA
320 self._sponsor_chapter(10, 30, 'sponsor'),
321 self._sponsor_chapter(20, 50, 'interaction'),
63c547d7 322 self._sponsor_chapter(30, 50, 'selfpromo', remove=True),
7a340e0d
NA
323 self._sponsor_chapter(40, 60, 'sponsor'),
324 self._sponsor_chapter(50, 60, 'interaction')]
325 expected = self._chapters(
326 [10, 20, 40, 50], ['c', '[SponsorBlock]: Sponsor',
327 '[SponsorBlock]: Sponsor, Interaction Reminder', 'c'])
328 self._remove_marked_arrange_sponsors_test_impl(
329 chapters, expected, [self._chapter(30, 50, remove=True)])
330
331 def test_remove_marked_arrange_sponsors_SponsorsStillOverlapAfterCut(self):
add96eb9 332 chapters = [
333 *self._chapters([70], ['c']),
7a340e0d
NA
334 self._sponsor_chapter(10, 60, 'sponsor'),
335 self._sponsor_chapter(20, 60, 'interaction'),
336 self._sponsor_chapter(30, 50, 'selfpromo', remove=True)]
337 expected = self._chapters(
338 [10, 20, 40, 50], ['c', '[SponsorBlock]: Sponsor',
339 '[SponsorBlock]: Sponsor, Interaction Reminder', 'c'])
340 self._remove_marked_arrange_sponsors_test_impl(
341 chapters, expected, [self._chapter(30, 50, remove=True)])
342
343 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsorsAndCuts(self):
add96eb9 344 chapters = [
345 *self._chapters([200], ['c']),
7a340e0d
NA
346 self._sponsor_chapter(10, 40, 'sponsor'),
347 self._sponsor_chapter(10, 30, 'intro'),
348 self._chapter(20, 30, remove=True),
349 self._sponsor_chapter(30, 40, 'selfpromo'),
350 self._sponsor_chapter(50, 70, 'sponsor'),
351 self._sponsor_chapter(60, 80, 'interaction'),
352 self._chapter(70, 80, remove=True),
353 self._sponsor_chapter(70, 90, 'sponsor'),
354 self._sponsor_chapter(80, 100, 'interaction'),
355 self._sponsor_chapter(120, 170, 'selfpromo'),
356 self._sponsor_chapter(130, 180, 'outro'),
357 self._chapter(140, 150, remove=True),
358 self._chapter(150, 160, remove=True)]
359 expected = self._chapters(
360 [10, 20, 30, 40, 50, 70, 80, 100, 110, 130, 140, 160],
361 ['c', '[SponsorBlock]: Sponsor, Intermission/Intro Animation', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
362 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Interaction Reminder',
363 '[SponsorBlock]: Interaction Reminder', 'c', '[SponsorBlock]: Unpaid/Self Promotion',
364 '[SponsorBlock]: Unpaid/Self Promotion, Endcards/Credits', '[SponsorBlock]: Endcards/Credits', 'c'])
365 expected_cuts = [self._chapter(20, 30, remove=True),
366 self._chapter(70, 80, remove=True),
367 self._chapter(140, 160, remove=True)]
368 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, expected_cuts)
369
370 def test_remove_marked_arrange_sponsors_SponsorOverlapsMultipleChapters(self):
add96eb9 371 chapters = [
372 *self._chapters([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']),
373 self._sponsor_chapter(10, 90, 'sponsor')]
7a340e0d
NA
374 expected = self._chapters([10, 90, 100], ['c1', '[SponsorBlock]: Sponsor', 'c5'])
375 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
376
377 def test_remove_marked_arrange_sponsors_CutOverlapsMultipleChapters(self):
378 cuts = [self._chapter(10, 90, remove=True)]
379 chapters = self._chapters([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']) + cuts
380 expected = self._chapters([10, 20], ['c1', 'c5'])
381 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
382
383 def test_remove_marked_arrange_sponsors_SponsorsWithinSomeChaptersAndOverlappingOthers(self):
add96eb9 384 chapters = [
385 *self._chapters([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4']),
386 self._sponsor_chapter(20, 30, 'sponsor'),
387 self._sponsor_chapter(50, 70, 'selfpromo')]
7a340e0d
NA
388 expected = self._chapters([10, 20, 30, 40, 50, 70, 80],
389 ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c2', 'c3',
390 '[SponsorBlock]: Unpaid/Self Promotion', 'c4'])
391 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
392
393 def test_remove_marked_arrange_sponsors_CutsWithinSomeChaptersAndOverlappingOthers(self):
394 cuts = [self._chapter(20, 30, remove=True), self._chapter(50, 70, remove=True)]
395 chapters = self._chapters([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4']) + cuts
396 expected = self._chapters([10, 30, 40, 50], ['c1', 'c2', 'c3', 'c4'])
397 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
398
399 def test_remove_marked_arrange_sponsors_ChaptersAfterLastSponsor(self):
add96eb9 400 chapters = [
401 *self._chapters([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']),
402 self._sponsor_chapter(10, 30, 'music_offtopic')]
7a340e0d
NA
403 expected = self._chapters(
404 [10, 30, 40, 50, 60],
405 ['c1', '[SponsorBlock]: Non-Music Section', 'c2', 'c3', 'c4'])
406 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
407
408 def test_remove_marked_arrange_sponsors_ChaptersAfterLastCut(self):
409 cuts = [self._chapter(10, 30, remove=True)]
410 chapters = self._chapters([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']) + cuts
411 expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4'])
412 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
413
414 def test_remove_marked_arrange_sponsors_SponsorStartsAtChapterStart(self):
add96eb9 415 chapters = [
416 *self._chapters([10, 20, 40], ['c1', 'c2', 'c3']),
417 self._sponsor_chapter(20, 30, 'sponsor')]
7a340e0d
NA
418 expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3'])
419 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
420
421 def test_remove_marked_arrange_sponsors_CutStartsAtChapterStart(self):
422 cuts = [self._chapter(20, 30, remove=True)]
423 chapters = self._chapters([10, 20, 40], ['c1', 'c2', 'c3']) + cuts
424 expected = self._chapters([10, 20, 30], ['c1', 'c2', 'c3'])
425 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
426
427 def test_remove_marked_arrange_sponsors_SponsorEndsAtChapterEnd(self):
add96eb9 428 chapters = [
429 *self._chapters([10, 30, 40], ['c1', 'c2', 'c3']),
430 self._sponsor_chapter(20, 30, 'sponsor')]
7a340e0d
NA
431 expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3'])
432 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
433
434 def test_remove_marked_arrange_sponsors_CutEndsAtChapterEnd(self):
435 cuts = [self._chapter(20, 30, remove=True)]
436 chapters = self._chapters([10, 30, 40], ['c1', 'c2', 'c3']) + cuts
437 expected = self._chapters([10, 20, 30], ['c1', 'c2', 'c3'])
438 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
439
440 def test_remove_marked_arrange_sponsors_SponsorCoincidesWithChapters(self):
add96eb9 441 chapters = [
442 *self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']),
443 self._sponsor_chapter(10, 30, 'sponsor')]
7a340e0d
NA
444 expected = self._chapters([10, 30, 40], ['c1', '[SponsorBlock]: Sponsor', 'c4'])
445 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
446
447 def test_remove_marked_arrange_sponsors_CutCoincidesWithChapters(self):
448 cuts = [self._chapter(10, 30, remove=True)]
449 chapters = self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) + cuts
450 expected = self._chapters([10, 20], ['c1', 'c4'])
451 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
452
453 def test_remove_marked_arrange_sponsors_SponsorsAtVideoBoundaries(self):
add96eb9 454 chapters = [
455 *self._chapters([20, 40, 60], ['c1', 'c2', 'c3']),
456 self._sponsor_chapter(0, 10, 'intro'), self._sponsor_chapter(50, 60, 'outro')]
7a340e0d
NA
457 expected = self._chapters(
458 [10, 20, 40, 50, 60], ['[SponsorBlock]: Intermission/Intro Animation', 'c1', 'c2', 'c3', '[SponsorBlock]: Endcards/Credits'])
459 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
460
461 def test_remove_marked_arrange_sponsors_CutsAtVideoBoundaries(self):
462 cuts = [self._chapter(0, 10, remove=True), self._chapter(50, 60, remove=True)]
463 chapters = self._chapters([20, 40, 60], ['c1', 'c2', 'c3']) + cuts
464 expected = self._chapters([10, 30, 40], ['c1', 'c2', 'c3'])
465 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
466
467 def test_remove_marked_arrange_sponsors_SponsorsOverlapChaptersAtVideoBoundaries(self):
add96eb9 468 chapters = [
469 *self._chapters([10, 40, 50], ['c1', 'c2', 'c3']),
470 self._sponsor_chapter(0, 20, 'intro'),
471 self._sponsor_chapter(30, 50, 'outro')]
7a340e0d
NA
472 expected = self._chapters(
473 [20, 30, 50], ['[SponsorBlock]: Intermission/Intro Animation', 'c2', '[SponsorBlock]: Endcards/Credits'])
474 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
475
476 def test_remove_marked_arrange_sponsors_CutsOverlapChaptersAtVideoBoundaries(self):
477 cuts = [self._chapter(0, 20, remove=True), self._chapter(30, 50, remove=True)]
478 chapters = self._chapters([10, 40, 50], ['c1', 'c2', 'c3']) + cuts
479 expected = self._chapters([10], ['c2'])
480 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts)
481
482 def test_remove_marked_arrange_sponsors_EverythingSponsored(self):
add96eb9 483 chapters = [
484 *self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']),
485 self._sponsor_chapter(0, 20, 'intro'),
486 self._sponsor_chapter(20, 40, 'outro')]
7a340e0d
NA
487 expected = self._chapters([20, 40], ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits'])
488 self._remove_marked_arrange_sponsors_test_impl(chapters, expected, [])
489
490 def test_remove_marked_arrange_sponsors_EverythingCut(self):
491 cuts = [self._chapter(0, 20, remove=True), self._chapter(20, 40, remove=True)]
492 chapters = self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) + cuts
493 self._remove_marked_arrange_sponsors_test_impl(
494 chapters, [], [self._chapter(0, 40, remove=True)])
495
496 def test_remove_marked_arrange_sponsors_TinyChaptersInTheOriginalArePreserved(self):
497 chapters = self._chapters([0.1, 0.2, 0.3, 0.4], ['c1', 'c2', 'c3', 'c4'])
498 self._remove_marked_arrange_sponsors_test_impl(chapters, chapters, [])
499
500 def test_remove_marked_arrange_sponsors_TinySponsorsAreIgnored(self):
501 chapters = [self._sponsor_chapter(0, 0.1, 'intro'), self._chapter(0.1, 0.2, 'c1'),
502 self._sponsor_chapter(0.2, 0.3, 'sponsor'), self._chapter(0.3, 0.4, 'c2'),
503 self._sponsor_chapter(0.4, 0.5, 'outro')]
504 self._remove_marked_arrange_sponsors_test_impl(
505 chapters, self._chapters([0.3, 0.5], ['c1', 'c2']), [])
506
507 def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromCutsAreIgnored(self):
508 cuts = [self._chapter(1.5, 2.5, remove=True)]
509 chapters = self._chapters([2, 3, 3.5], ['c1', 'c2', 'c3']) + cuts
510 self._remove_marked_arrange_sponsors_test_impl(
511 chapters, self._chapters([2, 2.5], ['c1', 'c3']), cuts)
512
c6af2dd8
NA
513 def test_remove_marked_arrange_sponsors_SingleTinyChapterIsPreserved(self):
514 cuts = [self._chapter(0.5, 2, remove=True)]
515 chapters = self._chapters([2], ['c']) + cuts
516 self._remove_marked_arrange_sponsors_test_impl(
517 chapters, self._chapters([0.5], ['c']), cuts)
518
519 def test_remove_marked_arrange_sponsors_TinyChapterAtTheStartPrependedToTheNext(self):
520 cuts = [self._chapter(0.5, 2, remove=True)]
521 chapters = self._chapters([2, 4], ['c1', 'c2']) + cuts
522 self._remove_marked_arrange_sponsors_test_impl(
523 chapters, self._chapters([2.5], ['c2']), cuts)
524
7a340e0d 525 def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromSponsorOverlapAreIgnored(self):
add96eb9 526 chapters = [
527 *self._chapters([1, 3, 4], ['c1', 'c2', 'c3']),
7a340e0d
NA
528 self._sponsor_chapter(1.5, 2.5, 'sponsor')]
529 self._remove_marked_arrange_sponsors_test_impl(
c6af2dd8 530 chapters, self._chapters([1.5, 2.5, 4], ['c1', '[SponsorBlock]: Sponsor', 'c3']), [])
7a340e0d
NA
531
532 def test_remove_marked_arrange_sponsors_TinySponsorsOverlapsAreIgnored(self):
add96eb9 533 chapters = [
534 *self._chapters([2, 3, 5], ['c1', 'c2', 'c3']),
7a340e0d 535 self._sponsor_chapter(1, 3, 'sponsor'),
add96eb9 536 self._sponsor_chapter(2.5, 4, 'selfpromo')]
7a340e0d
NA
537 self._remove_marked_arrange_sponsors_test_impl(
538 chapters, self._chapters([1, 3, 4, 5], [
539 'c1', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion', 'c3']), [])
540
c6af2dd8 541 def test_remove_marked_arrange_sponsors_TinySponsorsPrependedToTheNextSponsor(self):
add96eb9 542 chapters = [
543 *self._chapters([4], ['c']),
c6af2dd8 544 self._sponsor_chapter(1.5, 2, 'sponsor'),
add96eb9 545 self._sponsor_chapter(2, 4, 'selfpromo')]
c6af2dd8
NA
546 self._remove_marked_arrange_sponsors_test_impl(
547 chapters, self._chapters([1.5, 4], ['c', '[SponsorBlock]: Unpaid/Self Promotion']), [])
548
549 def test_remove_marked_arrange_sponsors_SmallestSponsorInTheOverlapGetsNamed(self):
550 self._pp._sponsorblock_chapter_title = '[SponsorBlock]: %(name)s'
add96eb9 551 chapters = [
552 *self._chapters([10], ['c']),
c6af2dd8 553 self._sponsor_chapter(2, 8, 'sponsor'),
add96eb9 554 self._sponsor_chapter(4, 6, 'selfpromo')]
c6af2dd8
NA
555 self._remove_marked_arrange_sponsors_test_impl(
556 chapters, self._chapters([2, 4, 6, 8, 10], [
557 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion',
add96eb9 558 '[SponsorBlock]: Sponsor', 'c',
c6af2dd8
NA
559 ]), [])
560
7a340e0d
NA
561 def test_make_concat_opts_CommonCase(self):
562 sponsor_chapters = [self._chapter(1, 2, 's1'), self._chapter(10, 20, 's2')]
563 expected = '''ffconcat version 1.0
564file 'file:test'
565outpoint 1.000000
566file 'file:test'
567inpoint 2.000000
568outpoint 10.000000
569file 'file:test'
570inpoint 20.000000
571'''
572 opts = self._pp._make_concat_opts(sponsor_chapters, 30)
573 self.assertEqual(expected, ''.join(self._pp._concat_spec(['test'] * len(opts), opts)))
574
575 def test_make_concat_opts_NoZeroDurationChunkAtVideoStart(self):
576 sponsor_chapters = [self._chapter(0, 1, 's1'), self._chapter(10, 20, 's2')]
577 expected = '''ffconcat version 1.0
578file 'file:test'
579inpoint 1.000000
580outpoint 10.000000
581file 'file:test'
582inpoint 20.000000
583'''
584 opts = self._pp._make_concat_opts(sponsor_chapters, 30)
585 self.assertEqual(expected, ''.join(self._pp._concat_spec(['test'] * len(opts), opts)))
586
587 def test_make_concat_opts_NoZeroDurationChunkAtVideoEnd(self):
588 sponsor_chapters = [self._chapter(1, 2, 's1'), self._chapter(10, 20, 's2')]
589 expected = '''ffconcat version 1.0
590file 'file:test'
591outpoint 1.000000
592file 'file:test'
593inpoint 2.000000
594outpoint 10.000000
595'''
596 opts = self._pp._make_concat_opts(sponsor_chapters, 20)
597 self.assertEqual(expected, ''.join(self._pp._concat_spec(['test'] * len(opts), opts)))
598
599 def test_quote_for_concat_RunsOfQuotes(self):
600 self.assertEqual(
601 r"'special '\'' '\'\''characters'\'\'\''galore'",
602 self._pp._quote_for_ffmpeg("special ' ''characters'''galore"))
603
604 def test_quote_for_concat_QuotesAtStart(self):
605 self.assertEqual(
606 r"\'\'\''special '\'' characters '\'' galore'",
607 self._pp._quote_for_ffmpeg("'''special ' characters ' galore"))
608
609 def test_quote_for_concat_QuotesAtEnd(self):
610 self.assertEqual(
611 r"'special '\'' characters '\'' galore'\'\'\'",
612 self._pp._quote_for_ffmpeg("special ' characters ' galore'''"))