3 from __future__
import unicode_literals
5 # Allow direct execution
10 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
12 from yt_dlp
import YoutubeDL
13 from yt_dlp
.compat
import compat_shlex_quote
14 from yt_dlp
.postprocessor
import (
16 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 %s' % compat_shlex_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):
81 c
= {'start_time': start, 'end_time': end, '_categories': [(cat, start, end)]}
87 def _chapter(start
, end
, title
=None, remove
=False):
88 c
= {'start_time': start, 'end_time': end}
95 def _chapters(self
, ends
, titles
):
96 self
.assertEqual(len(ends
), len(titles
))
99 for e
, t
in zip(ends
, titles
):
100 chapters
.append(self
._chapter
(start
, e
, t
))
104 def _remove_marked_arrange_sponsors_test_impl(
105 self
, chapters
, expected_chapters
, expected_removed
):
106 actual_chapters
, actual_removed
= (
107 self
._pp
._remove
_marked
_arrange
_sponsors
(chapters
))
108 for c
in actual_removed
:
110 c
.pop('_categories', None)
112 'start_time': c
['start_time'],
113 'end_time': c
['end_time'],
115 } for c
in actual_chapters
]
116 self
.assertSequenceEqual(expected_chapters
, actual_chapters
)
117 self
.assertSequenceEqual(expected_removed
, actual_removed
)
119 def test_remove_marked_arrange_sponsors_CanGetThroughUnaltered(self
):
120 chapters
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4'])
121 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, chapters
, [])
123 def test_remove_marked_arrange_sponsors_ChapterWithSponsors(self
):
124 chapters
= self
._chapters
([70], ['c']) + [
125 self
._sponsor
_chapter
(10, 20, 'sponsor'),
126 self
._sponsor
_chapter
(30, 40, 'preview'),
127 self
._sponsor
_chapter
(50, 60, 'sponsor')]
128 expected
= self
._chapters
(
129 [10, 20, 30, 40, 50, 60, 70],
130 ['c', '[SponsorBlock]: Sponsor', 'c', '[SponsorBlock]: Preview/Recap',
131 'c', '[SponsorBlock]: Sponsor', 'c'])
132 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
134 def test_remove_marked_arrange_sponsors_UniqueNamesForOverlappingSponsors(self
):
135 chapters
= self
._chapters
([120], ['c']) + [
136 self
._sponsor
_chapter
(10, 45, 'sponsor'), self
._sponsor
_chapter
(20, 40, 'selfpromo'),
137 self
._sponsor
_chapter
(50, 70, 'sponsor'), self
._sponsor
_chapter
(60, 85, 'selfpromo'),
138 self
._sponsor
_chapter
(90, 120, 'selfpromo'), self
._sponsor
_chapter
(100, 110, 'sponsor')]
139 expected
= self
._chapters
(
140 [10, 20, 40, 45, 50, 60, 70, 85, 90, 100, 110, 120],
141 ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
142 '[SponsorBlock]: Sponsor',
143 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
144 '[SponsorBlock]: Unpaid/Self Promotion',
145 'c', '[SponsorBlock]: Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion, Sponsor',
146 '[SponsorBlock]: Unpaid/Self Promotion'])
147 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
149 def test_remove_marked_arrange_sponsors_ChapterWithCuts(self
):
150 cuts
= [self
._chapter
(10, 20, remove
=True),
151 self
._sponsor
_chapter
(30, 40, 'sponsor', remove
=True),
152 self
._chapter
(50, 60, remove
=True)]
153 chapters
= self
._chapters
([70], ['c']) + cuts
154 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
155 chapters
, self
._chapters
([40], ['c']), cuts
)
157 def test_remove_marked_arrange_sponsors_ChapterWithSponsorsAndCuts(self
):
158 chapters
= self
._chapters
([70], ['c']) + [
159 self
._sponsor
_chapter
(10, 20, 'sponsor'),
160 self
._sponsor
_chapter
(30, 40, 'selfpromo', remove
=True),
161 self
._sponsor
_chapter
(50, 60, 'interaction')]
162 expected
= self
._chapters
([10, 20, 40, 50, 60],
163 ['c', '[SponsorBlock]: Sponsor', 'c',
164 '[SponsorBlock]: Interaction Reminder', 'c'])
165 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
166 chapters
, expected
, [self
._chapter
(30, 40, remove
=True)])
168 def test_remove_marked_arrange_sponsors_ChapterWithSponsorCutInTheMiddle(self
):
169 cuts
= [self
._sponsor
_chapter
(20, 30, 'selfpromo', remove
=True),
170 self
._chapter
(40, 50, remove
=True)]
171 chapters
= self
._chapters
([70], ['c']) + [self
._sponsor
_chapter
(10, 60, 'sponsor')] + cuts
172 expected
= self
._chapters
(
173 [10, 40, 50], ['c', '[SponsorBlock]: Sponsor', 'c'])
174 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
176 def test_remove_marked_arrange_sponsors_ChapterWithCutHidingSponsor(self
):
177 cuts
= [self
._sponsor
_chapter
(20, 50, 'selpromo', remove
=True)]
178 chapters
= self
._chapters
([60], ['c']) + [
179 self
._sponsor
_chapter
(10, 20, 'intro'),
180 self
._sponsor
_chapter
(30, 40, 'sponsor'),
181 self
._sponsor
_chapter
(50, 60, 'outro'),
183 expected
= self
._chapters
(
184 [10, 20, 30], ['c', '[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits'])
185 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
187 def test_remove_marked_arrange_sponsors_ChapterWithAdjacentSponsors(self
):
188 chapters
= self
._chapters
([70], ['c']) + [
189 self
._sponsor
_chapter
(10, 20, 'sponsor'),
190 self
._sponsor
_chapter
(20, 30, 'selfpromo'),
191 self
._sponsor
_chapter
(30, 40, 'interaction')]
192 expected
= self
._chapters
(
193 [10, 20, 30, 40, 70],
194 ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion',
195 '[SponsorBlock]: Interaction Reminder', 'c'])
196 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
198 def test_remove_marked_arrange_sponsors_ChapterWithAdjacentCuts(self
):
199 chapters
= self
._chapters
([70], ['c']) + [
200 self
._sponsor
_chapter
(10, 20, 'sponsor'),
201 self
._sponsor
_chapter
(20, 30, 'interaction', remove
=True),
202 self
._chapter
(30, 40, remove
=True),
203 self
._sponsor
_chapter
(40, 50, 'selpromo', remove
=True),
204 self
._sponsor
_chapter
(50, 60, 'interaction')]
205 expected
= self
._chapters
([10, 20, 30, 40],
206 ['c', '[SponsorBlock]: Sponsor',
207 '[SponsorBlock]: Interaction Reminder', 'c'])
208 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
209 chapters
, expected
, [self
._chapter
(20, 50, remove
=True)])
211 def test_remove_marked_arrange_sponsors_ChapterWithOverlappingSponsors(self
):
212 chapters
= self
._chapters
([70], ['c']) + [
213 self
._sponsor
_chapter
(10, 30, 'sponsor'),
214 self
._sponsor
_chapter
(20, 50, 'selfpromo'),
215 self
._sponsor
_chapter
(40, 60, 'interaction')]
216 expected
= self
._chapters
(
217 [10, 20, 30, 40, 50, 60, 70],
218 ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
219 '[SponsorBlock]: Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion, Interaction Reminder',
220 '[SponsorBlock]: Interaction Reminder', 'c'])
221 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
223 def test_remove_marked_arrange_sponsors_ChapterWithOverlappingCuts(self
):
224 chapters
= self
._chapters
([70], ['c']) + [
225 self
._sponsor
_chapter
(10, 30, 'sponsor', remove
=True),
226 self
._sponsor
_chapter
(20, 50, 'selfpromo', remove
=True),
227 self
._sponsor
_chapter
(40, 60, 'interaction', remove
=True)]
228 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
229 chapters
, self
._chapters
([20], ['c']), [self
._chapter
(10, 60, remove
=True)])
231 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsors(self
):
232 chapters
= self
._chapters
([170], ['c']) + [
233 self
._sponsor
_chapter
(0, 30, 'intro'),
234 self
._sponsor
_chapter
(20, 50, 'sponsor'),
235 self
._sponsor
_chapter
(40, 60, 'selfpromo'),
236 self
._sponsor
_chapter
(70, 90, 'sponsor'),
237 self
._sponsor
_chapter
(80, 100, 'sponsor'),
238 self
._sponsor
_chapter
(90, 110, 'sponsor'),
239 self
._sponsor
_chapter
(120, 140, 'selfpromo'),
240 self
._sponsor
_chapter
(130, 160, 'interaction'),
241 self
._sponsor
_chapter
(150, 170, 'outro')]
242 expected
= self
._chapters
(
243 [20, 30, 40, 50, 60, 70, 110, 120, 130, 140, 150, 160, 170],
244 ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Intermission/Intro Animation, Sponsor', '[SponsorBlock]: Sponsor',
245 '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion', 'c',
246 '[SponsorBlock]: Sponsor', 'c', '[SponsorBlock]: Unpaid/Self Promotion',
247 '[SponsorBlock]: Unpaid/Self Promotion, Interaction Reminder',
248 '[SponsorBlock]: Interaction Reminder',
249 '[SponsorBlock]: Interaction Reminder, Endcards/Credits', '[SponsorBlock]: Endcards/Credits'])
250 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
252 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingCuts(self
):
253 chapters
= self
._chapters
([170], ['c']) + [
254 self
._chapter
(0, 30, remove
=True),
255 self
._sponsor
_chapter
(20, 50, 'sponsor', remove
=True),
256 self
._chapter
(40, 60, remove
=True),
257 self
._sponsor
_chapter
(70, 90, 'sponsor', remove
=True),
258 self
._chapter
(80, 100, remove
=True),
259 self
._chapter
(90, 110, remove
=True),
260 self
._sponsor
_chapter
(120, 140, 'sponsor', remove
=True),
261 self
._sponsor
_chapter
(130, 160, 'selfpromo', remove
=True),
262 self
._chapter
(150, 170, remove
=True)]
263 expected_cuts
= [self
._chapter
(0, 60, remove
=True),
264 self
._chapter
(70, 110, remove
=True),
265 self
._chapter
(120, 170, remove
=True)]
266 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
267 chapters
, self
._chapters
([20], ['c']), expected_cuts
)
269 def test_remove_marked_arrange_sponsors_OverlappingSponsorsDifferentTitlesAfterCut(self
):
270 chapters
= self
._chapters
([60], ['c']) + [
271 self
._sponsor
_chapter
(10, 60, 'sponsor'),
272 self
._sponsor
_chapter
(10, 40, 'intro'),
273 self
._sponsor
_chapter
(30, 50, 'interaction'),
274 self
._sponsor
_chapter
(30, 50, 'selfpromo', remove
=True),
275 self
._sponsor
_chapter
(40, 50, 'interaction'),
276 self
._sponsor
_chapter
(50, 60, 'outro')]
277 expected
= self
._chapters
(
278 [10, 30, 40], ['c', '[SponsorBlock]: Sponsor, Intermission/Intro Animation', '[SponsorBlock]: Sponsor, Endcards/Credits'])
279 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
280 chapters
, expected
, [self
._chapter
(30, 50, remove
=True)])
282 def test_remove_marked_arrange_sponsors_SponsorsNoLongerOverlapAfterCut(self
):
283 chapters
= self
._chapters
([70], ['c']) + [
284 self
._sponsor
_chapter
(10, 30, 'sponsor'),
285 self
._sponsor
_chapter
(20, 50, 'interaction'),
286 self
._sponsor
_chapter
(30, 50, 'selpromo', remove
=True),
287 self
._sponsor
_chapter
(40, 60, 'sponsor'),
288 self
._sponsor
_chapter
(50, 60, 'interaction')]
289 expected
= self
._chapters
(
290 [10, 20, 40, 50], ['c', '[SponsorBlock]: Sponsor',
291 '[SponsorBlock]: Sponsor, Interaction Reminder', 'c'])
292 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
293 chapters
, expected
, [self
._chapter
(30, 50, remove
=True)])
295 def test_remove_marked_arrange_sponsors_SponsorsStillOverlapAfterCut(self
):
296 chapters
= self
._chapters
([70], ['c']) + [
297 self
._sponsor
_chapter
(10, 60, 'sponsor'),
298 self
._sponsor
_chapter
(20, 60, 'interaction'),
299 self
._sponsor
_chapter
(30, 50, 'selfpromo', remove
=True)]
300 expected
= self
._chapters
(
301 [10, 20, 40, 50], ['c', '[SponsorBlock]: Sponsor',
302 '[SponsorBlock]: Sponsor, Interaction Reminder', 'c'])
303 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
304 chapters
, expected
, [self
._chapter
(30, 50, remove
=True)])
306 def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsorsAndCuts(self
):
307 chapters
= self
._chapters
([200], ['c']) + [
308 self
._sponsor
_chapter
(10, 40, 'sponsor'),
309 self
._sponsor
_chapter
(10, 30, 'intro'),
310 self
._chapter
(20, 30, remove
=True),
311 self
._sponsor
_chapter
(30, 40, 'selfpromo'),
312 self
._sponsor
_chapter
(50, 70, 'sponsor'),
313 self
._sponsor
_chapter
(60, 80, 'interaction'),
314 self
._chapter
(70, 80, remove
=True),
315 self
._sponsor
_chapter
(70, 90, 'sponsor'),
316 self
._sponsor
_chapter
(80, 100, 'interaction'),
317 self
._sponsor
_chapter
(120, 170, 'selfpromo'),
318 self
._sponsor
_chapter
(130, 180, 'outro'),
319 self
._chapter
(140, 150, remove
=True),
320 self
._chapter
(150, 160, remove
=True)]
321 expected
= self
._chapters
(
322 [10, 20, 30, 40, 50, 70, 80, 100, 110, 130, 140, 160],
323 ['c', '[SponsorBlock]: Sponsor, Intermission/Intro Animation', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion',
324 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Interaction Reminder',
325 '[SponsorBlock]: Interaction Reminder', 'c', '[SponsorBlock]: Unpaid/Self Promotion',
326 '[SponsorBlock]: Unpaid/Self Promotion, Endcards/Credits', '[SponsorBlock]: Endcards/Credits', 'c'])
327 expected_cuts
= [self
._chapter
(20, 30, remove
=True),
328 self
._chapter
(70, 80, remove
=True),
329 self
._chapter
(140, 160, remove
=True)]
330 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, expected_cuts
)
332 def test_remove_marked_arrange_sponsors_SponsorOverlapsMultipleChapters(self
):
333 chapters
= (self
._chapters
([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5'])
334 + [self
._sponsor
_chapter
(10, 90, 'sponsor')])
335 expected
= self
._chapters
([10, 90, 100], ['c1', '[SponsorBlock]: Sponsor', 'c5'])
336 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
338 def test_remove_marked_arrange_sponsors_CutOverlapsMultipleChapters(self
):
339 cuts
= [self
._chapter
(10, 90, remove
=True)]
340 chapters
= self
._chapters
([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']) + cuts
341 expected
= self
._chapters
([10, 20], ['c1', 'c5'])
342 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
344 def test_remove_marked_arrange_sponsors_SponsorsWithinSomeChaptersAndOverlappingOthers(self
):
345 chapters
= (self
._chapters
([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4'])
346 + [self
._sponsor
_chapter
(20, 30, 'sponsor'),
347 self
._sponsor
_chapter
(50, 70, 'selfpromo')])
348 expected
= self
._chapters
([10, 20, 30, 40, 50, 70, 80],
349 ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c2', 'c3',
350 '[SponsorBlock]: Unpaid/Self Promotion', 'c4'])
351 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
353 def test_remove_marked_arrange_sponsors_CutsWithinSomeChaptersAndOverlappingOthers(self
):
354 cuts
= [self
._chapter
(20, 30, remove
=True), self
._chapter
(50, 70, remove
=True)]
355 chapters
= self
._chapters
([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4']) + cuts
356 expected
= self
._chapters
([10, 30, 40, 50], ['c1', 'c2', 'c3', 'c4'])
357 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
359 def test_remove_marked_arrange_sponsors_ChaptersAfterLastSponsor(self
):
360 chapters
= (self
._chapters
([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4'])
361 + [self
._sponsor
_chapter
(10, 30, 'music_offtopic')])
362 expected
= self
._chapters
(
363 [10, 30, 40, 50, 60],
364 ['c1', '[SponsorBlock]: Non-Music Section', 'c2', 'c3', 'c4'])
365 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
367 def test_remove_marked_arrange_sponsors_ChaptersAfterLastCut(self
):
368 cuts
= [self
._chapter
(10, 30, remove
=True)]
369 chapters
= self
._chapters
([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']) + cuts
370 expected
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4'])
371 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
373 def test_remove_marked_arrange_sponsors_SponsorStartsAtChapterStart(self
):
374 chapters
= (self
._chapters
([10, 20, 40], ['c1', 'c2', 'c3'])
375 + [self
._sponsor
_chapter
(20, 30, 'sponsor')])
376 expected
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3'])
377 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
379 def test_remove_marked_arrange_sponsors_CutStartsAtChapterStart(self
):
380 cuts
= [self
._chapter
(20, 30, remove
=True)]
381 chapters
= self
._chapters
([10, 20, 40], ['c1', 'c2', 'c3']) + cuts
382 expected
= self
._chapters
([10, 20, 30], ['c1', 'c2', 'c3'])
383 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
385 def test_remove_marked_arrange_sponsors_SponsorEndsAtChapterEnd(self
):
386 chapters
= (self
._chapters
([10, 30, 40], ['c1', 'c2', 'c3'])
387 + [self
._sponsor
_chapter
(20, 30, 'sponsor')])
388 expected
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3'])
389 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
391 def test_remove_marked_arrange_sponsors_CutEndsAtChapterEnd(self
):
392 cuts
= [self
._chapter
(20, 30, remove
=True)]
393 chapters
= self
._chapters
([10, 30, 40], ['c1', 'c2', 'c3']) + cuts
394 expected
= self
._chapters
([10, 20, 30], ['c1', 'c2', 'c3'])
395 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
397 def test_remove_marked_arrange_sponsors_SponsorCoincidesWithChapters(self
):
398 chapters
= (self
._chapters
([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4'])
399 + [self
._sponsor
_chapter
(10, 30, 'sponsor')])
400 expected
= self
._chapters
([10, 30, 40], ['c1', '[SponsorBlock]: Sponsor', 'c4'])
401 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
403 def test_remove_marked_arrange_sponsors_CutCoincidesWithChapters(self
):
404 cuts
= [self
._chapter
(10, 30, remove
=True)]
405 chapters
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) + cuts
406 expected
= self
._chapters
([10, 20], ['c1', 'c4'])
407 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
409 def test_remove_marked_arrange_sponsors_SponsorsAtVideoBoundaries(self
):
410 chapters
= (self
._chapters
([20, 40, 60], ['c1', 'c2', 'c3'])
411 + [self
._sponsor
_chapter
(0, 10, 'intro'), self
._sponsor
_chapter
(50, 60, 'outro')])
412 expected
= self
._chapters
(
413 [10, 20, 40, 50, 60], ['[SponsorBlock]: Intermission/Intro Animation', 'c1', 'c2', 'c3', '[SponsorBlock]: Endcards/Credits'])
414 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
416 def test_remove_marked_arrange_sponsors_CutsAtVideoBoundaries(self
):
417 cuts
= [self
._chapter
(0, 10, remove
=True), self
._chapter
(50, 60, remove
=True)]
418 chapters
= self
._chapters
([20, 40, 60], ['c1', 'c2', 'c3']) + cuts
419 expected
= self
._chapters
([10, 30, 40], ['c1', 'c2', 'c3'])
420 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
422 def test_remove_marked_arrange_sponsors_SponsorsOverlapChaptersAtVideoBoundaries(self
):
423 chapters
= (self
._chapters
([10, 40, 50], ['c1', 'c2', 'c3'])
424 + [self
._sponsor
_chapter
(0, 20, 'intro'), self
._sponsor
_chapter
(30, 50, 'outro')])
425 expected
= self
._chapters
(
426 [20, 30, 50], ['[SponsorBlock]: Intermission/Intro Animation', 'c2', '[SponsorBlock]: Endcards/Credits'])
427 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
429 def test_remove_marked_arrange_sponsors_CutsOverlapChaptersAtVideoBoundaries(self
):
430 cuts
= [self
._chapter
(0, 20, remove
=True), self
._chapter
(30, 50, remove
=True)]
431 chapters
= self
._chapters
([10, 40, 50], ['c1', 'c2', 'c3']) + cuts
432 expected
= self
._chapters
([10], ['c2'])
433 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, cuts
)
435 def test_remove_marked_arrange_sponsors_EverythingSponsored(self
):
436 chapters
= (self
._chapters
([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4'])
437 + [self
._sponsor
_chapter
(0, 20, 'intro'), self
._sponsor
_chapter
(20, 40, 'outro')])
438 expected
= self
._chapters
([20, 40], ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits'])
439 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, expected
, [])
441 def test_remove_marked_arrange_sponsors_EverythingCut(self
):
442 cuts
= [self
._chapter
(0, 20, remove
=True), self
._chapter
(20, 40, remove
=True)]
443 chapters
= self
._chapters
([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) + cuts
444 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
445 chapters
, [], [self
._chapter
(0, 40, remove
=True)])
447 def test_remove_marked_arrange_sponsors_TinyChaptersInTheOriginalArePreserved(self
):
448 chapters
= self
._chapters
([0.1, 0.2, 0.3, 0.4], ['c1', 'c2', 'c3', 'c4'])
449 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(chapters
, chapters
, [])
451 def test_remove_marked_arrange_sponsors_TinySponsorsAreIgnored(self
):
452 chapters
= [self
._sponsor
_chapter
(0, 0.1, 'intro'), self
._chapter
(0.1, 0.2, 'c1'),
453 self
._sponsor
_chapter
(0.2, 0.3, 'sponsor'), self
._chapter
(0.3, 0.4, 'c2'),
454 self
._sponsor
_chapter
(0.4, 0.5, 'outro')]
455 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
456 chapters
, self
._chapters
([0.3, 0.5], ['c1', 'c2']), [])
458 def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromCutsAreIgnored(self
):
459 cuts
= [self
._chapter
(1.5, 2.5, remove
=True)]
460 chapters
= self
._chapters
([2, 3, 3.5], ['c1', 'c2', 'c3']) + cuts
461 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
462 chapters
, self
._chapters
([2, 2.5], ['c1', 'c3']), cuts
)
464 def test_remove_marked_arrange_sponsors_SingleTinyChapterIsPreserved(self
):
465 cuts
= [self
._chapter
(0.5, 2, remove
=True)]
466 chapters
= self
._chapters
([2], ['c']) + cuts
467 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
468 chapters
, self
._chapters
([0.5], ['c']), cuts
)
470 def test_remove_marked_arrange_sponsors_TinyChapterAtTheStartPrependedToTheNext(self
):
471 cuts
= [self
._chapter
(0.5, 2, remove
=True)]
472 chapters
= self
._chapters
([2, 4], ['c1', 'c2']) + cuts
473 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
474 chapters
, self
._chapters
([2.5], ['c2']), cuts
)
476 def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromSponsorOverlapAreIgnored(self
):
477 chapters
= self
._chapters
([1, 3, 4], ['c1', 'c2', 'c3']) + [
478 self
._sponsor
_chapter
(1.5, 2.5, 'sponsor')]
479 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
480 chapters
, self
._chapters
([1.5, 2.5, 4], ['c1', '[SponsorBlock]: Sponsor', 'c3']), [])
482 def test_remove_marked_arrange_sponsors_TinySponsorsOverlapsAreIgnored(self
):
483 chapters
= self
._chapters
([2, 3, 5], ['c1', 'c2', 'c3']) + [
484 self
._sponsor
_chapter
(1, 3, 'sponsor'),
485 self
._sponsor
_chapter
(2.5, 4, 'selfpromo')
487 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
488 chapters
, self
._chapters
([1, 3, 4, 5], [
489 'c1', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion', 'c3']), [])
491 def test_remove_marked_arrange_sponsors_TinySponsorsPrependedToTheNextSponsor(self
):
492 chapters
= self
._chapters
([4], ['c']) + [
493 self
._sponsor
_chapter
(1.5, 2, 'sponsor'),
494 self
._sponsor
_chapter
(2, 4, 'selfpromo')
496 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
497 chapters
, self
._chapters
([1.5, 4], ['c', '[SponsorBlock]: Unpaid/Self Promotion']), [])
499 def test_remove_marked_arrange_sponsors_SmallestSponsorInTheOverlapGetsNamed(self
):
500 self
._pp
._sponsorblock
_chapter
_title
= '[SponsorBlock]: %(name)s'
501 chapters
= self
._chapters
([10], ['c']) + [
502 self
._sponsor
_chapter
(2, 8, 'sponsor'),
503 self
._sponsor
_chapter
(4, 6, 'selfpromo')
505 self
._remove
_marked
_arrange
_sponsors
_test
_impl
(
506 chapters
, self
._chapters
([2, 4, 6, 8, 10], [
507 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion',
508 '[SponsorBlock]: Sponsor', 'c'
511 def test_make_concat_opts_CommonCase(self
):
512 sponsor_chapters
= [self
._chapter
(1, 2, 's1'), self
._chapter
(10, 20, 's2')]
513 expected
= '''ffconcat version 1.0
522 opts
= self
._pp
._make
_concat
_opts
(sponsor_chapters
, 30)
523 self
.assertEqual(expected
, ''.join(self
._pp
._concat
_spec
(['test'] * len(opts
), opts
)))
525 def test_make_concat_opts_NoZeroDurationChunkAtVideoStart(self
):
526 sponsor_chapters
= [self
._chapter
(0, 1, 's1'), self
._chapter
(10, 20, 's2')]
527 expected
= '''ffconcat version 1.0
534 opts
= self
._pp
._make
_concat
_opts
(sponsor_chapters
, 30)
535 self
.assertEqual(expected
, ''.join(self
._pp
._concat
_spec
(['test'] * len(opts
), opts
)))
537 def test_make_concat_opts_NoZeroDurationChunkAtVideoEnd(self
):
538 sponsor_chapters
= [self
._chapter
(1, 2, 's1'), self
._chapter
(10, 20, 's2')]
539 expected
= '''ffconcat version 1.0
546 opts
= self
._pp
._make
_concat
_opts
(sponsor_chapters
, 20)
547 self
.assertEqual(expected
, ''.join(self
._pp
._concat
_spec
(['test'] * len(opts
), opts
)))
549 def test_quote_for_concat_RunsOfQuotes(self
):
551 r
"'special '\'' '\'\''characters'\'\'\''galore'",
552 self
._pp
._quote
_for
_ffmpeg
("special ' ''characters'''galore"))
554 def test_quote_for_concat_QuotesAtStart(self
):
556 r
"\'\'\''special '\'' characters '\'' galore'",
557 self
._pp
._quote
_for
_ffmpeg
("'''special ' characters ' galore"))
559 def test_quote_for_concat_QuotesAtEnd(self
):
561 r
"'special '\'' characters '\'' galore'\'\'\'",
562 self
._pp
._quote
_for
_ffmpeg
("special ' characters ' galore'''"))