3 # Allow direct execution
8 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
11 from yt_dlp
import YoutubeDL
12 from yt_dlp
.utils
import shell_quote
13 from yt_dlp
.postprocessor
import (
15 FFmpegThumbnailsConvertorPP
,
23 class TestMetadataFromField(unittest
.TestCase
):
25 def test_format_to_regex(self
):
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>.+)')
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')
37 def test_metadatafromfield(self
):
39 MetadataFromFieldPP
.to_action('%(title)s \\: %(artist)s:%(title)s : %(artist)s'),
40 (MetadataParserPP
.Actions
.INTERPRET
, '%(title)s : %(artist)s', '%(title)s : %(artist)s'))
43 class TestConvertThumbnail(unittest
.TestCase
):
44 def test_escaping(self
):
45 pp
= FFmpegThumbnailsConvertorPP()
47 print('Skipping: ffmpeg not found')
50 file = 'test/testdata/thumbnails/foo %d bar/foo_%d.{}'
51 tests
= (('webp', 'png'), ('png', 'jpg'))
53 for inp
, out
in tests
:
54 out_file
= file.format(out
)
55 if os
.path
.exists(out_file
):
57 pp
.convert_thumbnail(file.format(inp
), out
)
58 assert os
.path
.exists(out_file
)
61 os
.remove(file.format(out
))
64 class TestExec(unittest
.TestCase
):
65 def test_parse_cmd(self
):
66 pp
= ExecPP(YoutubeDL(), '')
67 info
= {'filepath': 'file name'}
68 cmd
= 'echo {}'.format(shell_quote(info
['filepath']))
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
)
75 class TestModifyChaptersPP(unittest
.TestCase
):
77 self
._pp
= ModifyChaptersPP(YoutubeDL())
80 def _sponsor_chapter(start
, end
, cat
, remove
=False, title
=None):
82 title
= SponsorBlockPP
.CATEGORIES
[cat
]
86 '_categories': [(cat
, start
, end
, title
)],
87 **({'remove': True} if remove
else {}),
91 def _chapter(start
, end
, title
=None, remove
=False):
92 c
= {'start_time': start
, 'end_time': end
}
99 def _chapters(self
, ends
, titles
):
100 self
.assertEqual(len(ends
), len(titles
))
103 for e
, t
in zip(ends
, titles
):
104 chapters
.append(self
._chapter
(start
, e
, t
))
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
:
114 c
.pop('_categories', None)
116 'start_time': c
['start_time'],
117 'end_time': c
['end_time'],
119 } for c
in actual_chapters
]
120 self
.assertSequenceEqual(expected_chapters
, actual_chapters
)
121 self
.assertSequenceEqual(expected_removed
, actual_removed
)
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
, [])
127 def test_remove_marked_arrange_sponsors_ChapterWithSponsors(self
):
129 *self
._chapters
([70], ['c']),
130 self
._sponsor
_chapter
(10, 20, 'sponsor'),
131 self
._sponsor
_chapter
(30, 40, 'preview'),
132 self
._sponsor
_chapter
(50, 60, 'filler')]
133 expected
= self
._chapters
(
134 [10, 20, 30, 40, 50, 60, 70],
135 ['c', '[SponsorBlock]: Sponsor', 'c', '[SponsorBlock]: Preview/Recap',
136 'c', '[SponsorBlock]: Filler Tangent', 'c'])
137 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
139 def test_remove_marked_arrange_sponsors_SponsorBlockChapters(self
):
141 *self
._chapters
([70], ['c']),
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
, [])
153 def test_remove_marked_arrange_sponsors_UniqueNamesForOverlappingSponsors(self
):
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')]
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
, [])
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
)
180 def test_remove_marked_arrange_sponsors_ChapterWithSponsorsAndCuts(self
):
182 *self
._chapters
([70], ['c']),
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)])
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)]
196 *self
._chapters
([70], ['c']),
197 self
._sponsor
_chapter
(10, 60, 'sponsor'),
199 expected
= self
._chapters
(
200 [10, 40, 50], ['c', '[SponsorBlock]: Sponsor', 'c'])
201 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
203 def test_remove_marked_arrange_sponsors_ChapterWithCutHidingSponsor(self
):
204 cuts
= [self
._sponsor
_chapter
(20, 50, 'selfpromo', remove
=True)]
206 *self
._chapters
([60], ['c']),
207 self
._sponsor
_chapter
(10, 20, 'intro'),
208 self
._sponsor
_chapter
(30, 40, 'sponsor'),
209 self
._sponsor
_chapter
(50, 60, 'outro'),
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
)
215 def test_remove_marked_arrange_sponsors_ChapterWithAdjacentSponsors(self
):
217 *self
._chapters
([70], ['c']),
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
, [])
227 def test_remove_marked_arrange_sponsors_ChapterWithAdjacentCuts(self
):
229 *self
._chapters
([70], ['c']),
230 self
._sponsor
_chapter
(10, 20, 'sponsor'),
231 self
._sponsor
_chapter
(20, 30, 'interaction', remove
=True),
232 self
._chapter
(30, 40, remove
=True),
233 self
._sponsor
_chapter
(40, 50, 'selfpromo', remove
=True),
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)])
241 def test_remove_marked_arrange_sponsors_ChapterWithOverlappingSponsors(self
):
243 *self
._chapters
([70], ['c']),
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
, [])
254 def test_remove_marked_arrange_sponsors_ChapterWithOverlappingCuts(self
):
256 *self
._chapters
([70], ['c']),
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)])
263 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsors(self
):
265 *self
._chapters
([170], ['c']),
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
, [])
285 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingCuts(self
):
287 *self
._chapters
([170], ['c']),
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
)
303 def test_remove_marked_arrange_sponsors_OverlappingSponsorsDifferentTitlesAfterCut(self
):
305 *self
._chapters
([60], ['c']),
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)])
317 def test_remove_marked_arrange_sponsors_SponsorsNoLongerOverlapAfterCut(self
):
319 *self
._chapters
([70], ['c']),
320 self
._sponsor
_chapter
(10, 30, 'sponsor'),
321 self
._sponsor
_chapter
(20, 50, 'interaction'),
322 self
._sponsor
_chapter
(30, 50, 'selfpromo', remove
=True),
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)])
331 def test_remove_marked_arrange_sponsors_SponsorsStillOverlapAfterCut(self
):
333 *self
._chapters
([70], ['c']),
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)])
343 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsorsAndCuts(self
):
345 *self
._chapters
([200], ['c']),
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
)
370 def test_remove_marked_arrange_sponsors_SponsorOverlapsMultipleChapters(self
):
372 *self
._chapters
([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']),
373 self
._sponsor
_chapter
(10, 90, 'sponsor')]
374 expected
= self
._chapters
([10, 90, 100], ['c1', '[SponsorBlock]: Sponsor', 'c5'])
375 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
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
)
383 def test_remove_marked_arrange_sponsors_SponsorsWithinSomeChaptersAndOverlappingOthers(self
):
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')]
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
, [])
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
)
399 def test_remove_marked_arrange_sponsors_ChaptersAfterLastSponsor(self
):
401 *self
._chapters
([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']),
402 self
._sponsor
_chapter
(10, 30, 'music_offtopic')]
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
, [])
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
)
414 def test_remove_marked_arrange_sponsors_SponsorStartsAtChapterStart(self
):
416 *self
._chapters
([10, 20, 40], ['c1', 'c2', 'c3']),
417 self
._sponsor
_chapter
(20, 30, 'sponsor')]
418 expected
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3'])
419 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
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
)
427 def test_remove_marked_arrange_sponsors_SponsorEndsAtChapterEnd(self
):
429 *self
._chapters
([10, 30, 40], ['c1', 'c2', 'c3']),
430 self
._sponsor
_chapter
(20, 30, 'sponsor')]
431 expected
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3'])
432 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
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
)
440 def test_remove_marked_arrange_sponsors_SponsorCoincidesWithChapters(self
):
442 *self
._chapters
([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']),
443 self
._sponsor
_chapter
(10, 30, 'sponsor')]
444 expected
= self
._chapters
([10, 30, 40], ['c1', '[SponsorBlock]: Sponsor', 'c4'])
445 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
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
)
453 def test_remove_marked_arrange_sponsors_SponsorsAtVideoBoundaries(self
):
455 *self
._chapters
([20, 40, 60], ['c1', 'c2', 'c3']),
456 self
._sponsor
_chapter
(0, 10, 'intro'), self
._sponsor
_chapter
(50, 60, 'outro')]
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
, [])
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
)
467 def test_remove_marked_arrange_sponsors_SponsorsOverlapChaptersAtVideoBoundaries(self
):
469 *self
._chapters
([10, 40, 50], ['c1', 'c2', 'c3']),
470 self
._sponsor
_chapter
(0, 20, 'intro'),
471 self
._sponsor
_chapter
(30, 50, 'outro')]
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
, [])
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
)
482 def test_remove_marked_arrange_sponsors_EverythingSponsored(self
):
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')]
487 expected
= self
._chapters
([20, 40], ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits'])
488 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
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)])
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
, [])
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']), [])
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
)
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
)
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
)
525 def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromSponsorOverlapAreIgnored(self
):
527 *self
._chapters
([1, 3, 4], ['c1', 'c2', 'c3']),
528 self
._sponsor
_chapter
(1.5, 2.5, 'sponsor')]
529 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
530 chapters
, self
._chapters
([1.5, 2.5, 4], ['c1', '[SponsorBlock]: Sponsor', 'c3']), [])
532 def test_remove_marked_arrange_sponsors_TinySponsorsOverlapsAreIgnored(self
):
534 *self
._chapters
([2, 3, 5], ['c1', 'c2', 'c3']),
535 self
._sponsor
_chapter
(1, 3, 'sponsor'),
536 self
._sponsor
_chapter
(2.5, 4, 'selfpromo')]
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']), [])
541 def test_remove_marked_arrange_sponsors_TinySponsorsPrependedToTheNextSponsor(self
):
543 *self
._chapters
([4], ['c']),
544 self
._sponsor
_chapter
(1.5, 2, 'sponsor'),
545 self
._sponsor
_chapter
(2, 4, 'selfpromo')]
546 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
547 chapters
, self
._chapters
([1.5, 4], ['c', '[SponsorBlock]: Unpaid/Self Promotion']), [])
549 def test_remove_marked_arrange_sponsors_SmallestSponsorInTheOverlapGetsNamed(self
):
550 self
._pp
._sponsorblock
_chapter
_title
= '[SponsorBlock]: %(name)s'
552 *self
._chapters
([10], ['c']),
553 self
._sponsor
_chapter
(2, 8, 'sponsor'),
554 self
._sponsor
_chapter
(4, 6, 'selfpromo')]
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',
558 '[SponsorBlock]: Sponsor', 'c',
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
572 opts
= self
._pp
._make
_concat
_opts
(sponsor_chapters
, 30)
573 self
.assertEqual(expected
, ''.join(self
._pp
._concat
_spec
(['test'] * len(opts
), opts
)))
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
584 opts
= self
._pp
._make
_concat
_opts
(sponsor_chapters
, 30)
585 self
.assertEqual(expected
, ''.join(self
._pp
._concat
_spec
(['test'] * len(opts
), opts
)))
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
596 opts
= self
._pp
._make
_concat
_opts
(sponsor_chapters
, 20)
597 self
.assertEqual(expected
, ''.join(self
._pp
._concat
_spec
(['test'] * len(opts
), opts
)))
599 def test_quote_for_concat_RunsOfQuotes(self
):
601 r
"'special '\'' '\'\''characters'\'\'\''galore'",
602 self
._pp
._quote
_for
_ffmpeg
("special ' ''characters'''galore"))
604 def test_quote_for_concat_QuotesAtStart(self
):
606 r
"\'\'\''special '\'' characters '\'' galore'",
607 self
._pp
._quote
_for
_ffmpeg
("'''special ' characters ' galore"))
609 def test_quote_for_concat_QuotesAtEnd(self
):
611 r
"'special '\'' characters '\'' galore'\'\'\'",
612 self
._pp
._quote
_for
_ffmpeg
("special ' characters ' galore'''"))