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