]>
Commit | Line | Data |
---|---|---|
cc52de43 | 1 | #!/usr/bin/env python3 |
54007a45 | 2 | |
88cf6fb3 JMF |
3 | # Allow direct execution |
4 | import os | |
5 | import sys | |
6 | import unittest | |
7a340e0d | 7 | |
88cf6fb3 JMF |
8 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
9 | ||
54007a45 | 10 | |
752cda38 | 11 | from yt_dlp import YoutubeDL |
12 | from yt_dlp.compat import compat_shlex_quote | |
337e0c62 | 13 | from 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 | ||
23 | class 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 | ||
43 | class 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 | 64 | class TestExec(unittest.TestCase): |
752cda38 | 65 | def test_parse_cmd(self): |
ad3dc496 | 66 | pp = ExecPP(YoutubeDL(), '') |
752cda38 | 67 | info = {'filepath': 'file name'} |
ad3dc496 | 68 | cmd = 'echo %s' % compat_shlex_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 | ||
75 | class 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): | |
128 | chapters = self._chapters([70], ['c']) + [ | |
129 | self._sponsor_chapter(10, 20, 'sponsor'), | |
130 | self._sponsor_chapter(30, 40, 'preview'), | |
8157a09d | 131 | self._sponsor_chapter(50, 60, 'filler')] |
7a340e0d NA |
132 | expected = self._chapters( |
133 | [10, 20, 30, 40, 50, 60, 70], | |
134 | ['c', '[SponsorBlock]: Sponsor', 'c', '[SponsorBlock]: Preview/Recap', | |
8157a09d | 135 | 'c', '[SponsorBlock]: Filler Tangent', 'c']) |
7a340e0d NA |
136 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) |
137 | ||
63c547d7 AR |
138 | def test_remove_marked_arrange_sponsors_SponsorBlockChapters(self): |
139 | chapters = self._chapters([70], ['c']) + [ | |
140 | self._sponsor_chapter(10, 20, 'chapter', title='sb c1'), | |
141 | self._sponsor_chapter(15, 16, 'chapter', title='sb c2'), | |
142 | self._sponsor_chapter(30, 40, 'preview'), | |
143 | self._sponsor_chapter(50, 60, 'filler')] | |
144 | expected = self._chapters( | |
145 | [10, 15, 16, 20, 30, 40, 50, 60, 70], | |
146 | ['c', '[SponsorBlock]: sb c1', '[SponsorBlock]: sb c1, sb c2', '[SponsorBlock]: sb c1', | |
147 | 'c', '[SponsorBlock]: Preview/Recap', | |
148 | 'c', '[SponsorBlock]: Filler Tangent', 'c']) | |
149 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
150 | ||
7a340e0d NA |
151 | def test_remove_marked_arrange_sponsors_UniqueNamesForOverlappingSponsors(self): |
152 | chapters = self._chapters([120], ['c']) + [ | |
153 | self._sponsor_chapter(10, 45, 'sponsor'), self._sponsor_chapter(20, 40, 'selfpromo'), | |
154 | self._sponsor_chapter(50, 70, 'sponsor'), self._sponsor_chapter(60, 85, 'selfpromo'), | |
155 | self._sponsor_chapter(90, 120, 'selfpromo'), self._sponsor_chapter(100, 110, 'sponsor')] | |
156 | expected = self._chapters( | |
157 | [10, 20, 40, 45, 50, 60, 70, 85, 90, 100, 110, 120], | |
158 | ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', | |
159 | '[SponsorBlock]: Sponsor', | |
160 | 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', | |
161 | '[SponsorBlock]: Unpaid/Self Promotion', | |
162 | 'c', '[SponsorBlock]: Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion, Sponsor', | |
163 | '[SponsorBlock]: Unpaid/Self Promotion']) | |
164 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
165 | ||
166 | def test_remove_marked_arrange_sponsors_ChapterWithCuts(self): | |
167 | cuts = [self._chapter(10, 20, remove=True), | |
168 | self._sponsor_chapter(30, 40, 'sponsor', remove=True), | |
169 | self._chapter(50, 60, remove=True)] | |
170 | chapters = self._chapters([70], ['c']) + cuts | |
171 | self._remove_marked_arrange_sponsors_test_impl( | |
172 | chapters, self._chapters([40], ['c']), cuts) | |
173 | ||
174 | def test_remove_marked_arrange_sponsors_ChapterWithSponsorsAndCuts(self): | |
175 | chapters = self._chapters([70], ['c']) + [ | |
176 | self._sponsor_chapter(10, 20, 'sponsor'), | |
177 | self._sponsor_chapter(30, 40, 'selfpromo', remove=True), | |
178 | self._sponsor_chapter(50, 60, 'interaction')] | |
179 | expected = self._chapters([10, 20, 40, 50, 60], | |
180 | ['c', '[SponsorBlock]: Sponsor', 'c', | |
181 | '[SponsorBlock]: Interaction Reminder', 'c']) | |
182 | self._remove_marked_arrange_sponsors_test_impl( | |
183 | chapters, expected, [self._chapter(30, 40, remove=True)]) | |
184 | ||
185 | def test_remove_marked_arrange_sponsors_ChapterWithSponsorCutInTheMiddle(self): | |
186 | cuts = [self._sponsor_chapter(20, 30, 'selfpromo', remove=True), | |
187 | self._chapter(40, 50, remove=True)] | |
188 | chapters = self._chapters([70], ['c']) + [self._sponsor_chapter(10, 60, 'sponsor')] + cuts | |
189 | expected = self._chapters( | |
190 | [10, 40, 50], ['c', '[SponsorBlock]: Sponsor', 'c']) | |
191 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
192 | ||
193 | def test_remove_marked_arrange_sponsors_ChapterWithCutHidingSponsor(self): | |
63c547d7 | 194 | cuts = [self._sponsor_chapter(20, 50, 'selfpromo', remove=True)] |
7a340e0d NA |
195 | chapters = self._chapters([60], ['c']) + [ |
196 | self._sponsor_chapter(10, 20, 'intro'), | |
197 | self._sponsor_chapter(30, 40, 'sponsor'), | |
198 | self._sponsor_chapter(50, 60, 'outro'), | |
199 | ] + cuts | |
200 | expected = self._chapters( | |
201 | [10, 20, 30], ['c', '[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits']) | |
202 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
203 | ||
204 | def test_remove_marked_arrange_sponsors_ChapterWithAdjacentSponsors(self): | |
205 | chapters = self._chapters([70], ['c']) + [ | |
206 | self._sponsor_chapter(10, 20, 'sponsor'), | |
207 | self._sponsor_chapter(20, 30, 'selfpromo'), | |
208 | self._sponsor_chapter(30, 40, 'interaction')] | |
209 | expected = self._chapters( | |
210 | [10, 20, 30, 40, 70], | |
211 | ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion', | |
212 | '[SponsorBlock]: Interaction Reminder', 'c']) | |
213 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
214 | ||
215 | def test_remove_marked_arrange_sponsors_ChapterWithAdjacentCuts(self): | |
216 | chapters = self._chapters([70], ['c']) + [ | |
217 | self._sponsor_chapter(10, 20, 'sponsor'), | |
218 | self._sponsor_chapter(20, 30, 'interaction', remove=True), | |
219 | self._chapter(30, 40, remove=True), | |
63c547d7 | 220 | self._sponsor_chapter(40, 50, 'selfpromo', remove=True), |
7a340e0d NA |
221 | self._sponsor_chapter(50, 60, 'interaction')] |
222 | expected = self._chapters([10, 20, 30, 40], | |
223 | ['c', '[SponsorBlock]: Sponsor', | |
224 | '[SponsorBlock]: Interaction Reminder', 'c']) | |
225 | self._remove_marked_arrange_sponsors_test_impl( | |
226 | chapters, expected, [self._chapter(20, 50, remove=True)]) | |
227 | ||
228 | def test_remove_marked_arrange_sponsors_ChapterWithOverlappingSponsors(self): | |
229 | chapters = self._chapters([70], ['c']) + [ | |
230 | self._sponsor_chapter(10, 30, 'sponsor'), | |
231 | self._sponsor_chapter(20, 50, 'selfpromo'), | |
232 | self._sponsor_chapter(40, 60, 'interaction')] | |
233 | expected = self._chapters( | |
234 | [10, 20, 30, 40, 50, 60, 70], | |
235 | ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', | |
236 | '[SponsorBlock]: Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion, Interaction Reminder', | |
237 | '[SponsorBlock]: Interaction Reminder', 'c']) | |
238 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
239 | ||
240 | def test_remove_marked_arrange_sponsors_ChapterWithOverlappingCuts(self): | |
241 | chapters = self._chapters([70], ['c']) + [ | |
242 | self._sponsor_chapter(10, 30, 'sponsor', remove=True), | |
243 | self._sponsor_chapter(20, 50, 'selfpromo', remove=True), | |
244 | self._sponsor_chapter(40, 60, 'interaction', remove=True)] | |
245 | self._remove_marked_arrange_sponsors_test_impl( | |
246 | chapters, self._chapters([20], ['c']), [self._chapter(10, 60, remove=True)]) | |
247 | ||
248 | def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsors(self): | |
249 | chapters = self._chapters([170], ['c']) + [ | |
250 | self._sponsor_chapter(0, 30, 'intro'), | |
251 | self._sponsor_chapter(20, 50, 'sponsor'), | |
252 | self._sponsor_chapter(40, 60, 'selfpromo'), | |
253 | self._sponsor_chapter(70, 90, 'sponsor'), | |
254 | self._sponsor_chapter(80, 100, 'sponsor'), | |
255 | self._sponsor_chapter(90, 110, 'sponsor'), | |
256 | self._sponsor_chapter(120, 140, 'selfpromo'), | |
257 | self._sponsor_chapter(130, 160, 'interaction'), | |
258 | self._sponsor_chapter(150, 170, 'outro')] | |
259 | expected = self._chapters( | |
260 | [20, 30, 40, 50, 60, 70, 110, 120, 130, 140, 150, 160, 170], | |
261 | ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Intermission/Intro Animation, Sponsor', '[SponsorBlock]: Sponsor', | |
262 | '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', '[SponsorBlock]: Unpaid/Self Promotion', 'c', | |
263 | '[SponsorBlock]: Sponsor', 'c', '[SponsorBlock]: Unpaid/Self Promotion', | |
264 | '[SponsorBlock]: Unpaid/Self Promotion, Interaction Reminder', | |
265 | '[SponsorBlock]: Interaction Reminder', | |
266 | '[SponsorBlock]: Interaction Reminder, Endcards/Credits', '[SponsorBlock]: Endcards/Credits']) | |
267 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
268 | ||
269 | def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingCuts(self): | |
270 | chapters = self._chapters([170], ['c']) + [ | |
271 | self._chapter(0, 30, remove=True), | |
272 | self._sponsor_chapter(20, 50, 'sponsor', remove=True), | |
273 | self._chapter(40, 60, remove=True), | |
274 | self._sponsor_chapter(70, 90, 'sponsor', remove=True), | |
275 | self._chapter(80, 100, remove=True), | |
276 | self._chapter(90, 110, remove=True), | |
277 | self._sponsor_chapter(120, 140, 'sponsor', remove=True), | |
278 | self._sponsor_chapter(130, 160, 'selfpromo', remove=True), | |
279 | self._chapter(150, 170, remove=True)] | |
280 | expected_cuts = [self._chapter(0, 60, remove=True), | |
281 | self._chapter(70, 110, remove=True), | |
282 | self._chapter(120, 170, remove=True)] | |
283 | self._remove_marked_arrange_sponsors_test_impl( | |
284 | chapters, self._chapters([20], ['c']), expected_cuts) | |
285 | ||
286 | def test_remove_marked_arrange_sponsors_OverlappingSponsorsDifferentTitlesAfterCut(self): | |
287 | chapters = self._chapters([60], ['c']) + [ | |
288 | self._sponsor_chapter(10, 60, 'sponsor'), | |
289 | self._sponsor_chapter(10, 40, 'intro'), | |
290 | self._sponsor_chapter(30, 50, 'interaction'), | |
291 | self._sponsor_chapter(30, 50, 'selfpromo', remove=True), | |
292 | self._sponsor_chapter(40, 50, 'interaction'), | |
293 | self._sponsor_chapter(50, 60, 'outro')] | |
294 | expected = self._chapters( | |
295 | [10, 30, 40], ['c', '[SponsorBlock]: Sponsor, Intermission/Intro Animation', '[SponsorBlock]: Sponsor, Endcards/Credits']) | |
296 | self._remove_marked_arrange_sponsors_test_impl( | |
297 | chapters, expected, [self._chapter(30, 50, remove=True)]) | |
298 | ||
299 | def test_remove_marked_arrange_sponsors_SponsorsNoLongerOverlapAfterCut(self): | |
300 | chapters = self._chapters([70], ['c']) + [ | |
301 | self._sponsor_chapter(10, 30, 'sponsor'), | |
302 | self._sponsor_chapter(20, 50, 'interaction'), | |
63c547d7 | 303 | self._sponsor_chapter(30, 50, 'selfpromo', remove=True), |
7a340e0d NA |
304 | self._sponsor_chapter(40, 60, 'sponsor'), |
305 | self._sponsor_chapter(50, 60, 'interaction')] | |
306 | expected = self._chapters( | |
307 | [10, 20, 40, 50], ['c', '[SponsorBlock]: Sponsor', | |
308 | '[SponsorBlock]: Sponsor, Interaction Reminder', 'c']) | |
309 | self._remove_marked_arrange_sponsors_test_impl( | |
310 | chapters, expected, [self._chapter(30, 50, remove=True)]) | |
311 | ||
312 | def test_remove_marked_arrange_sponsors_SponsorsStillOverlapAfterCut(self): | |
313 | chapters = self._chapters([70], ['c']) + [ | |
314 | self._sponsor_chapter(10, 60, 'sponsor'), | |
315 | self._sponsor_chapter(20, 60, 'interaction'), | |
316 | self._sponsor_chapter(30, 50, 'selfpromo', remove=True)] | |
317 | expected = self._chapters( | |
318 | [10, 20, 40, 50], ['c', '[SponsorBlock]: Sponsor', | |
319 | '[SponsorBlock]: Sponsor, Interaction Reminder', 'c']) | |
320 | self._remove_marked_arrange_sponsors_test_impl( | |
321 | chapters, expected, [self._chapter(30, 50, remove=True)]) | |
322 | ||
323 | def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsorsAndCuts(self): | |
324 | chapters = self._chapters([200], ['c']) + [ | |
325 | self._sponsor_chapter(10, 40, 'sponsor'), | |
326 | self._sponsor_chapter(10, 30, 'intro'), | |
327 | self._chapter(20, 30, remove=True), | |
328 | self._sponsor_chapter(30, 40, 'selfpromo'), | |
329 | self._sponsor_chapter(50, 70, 'sponsor'), | |
330 | self._sponsor_chapter(60, 80, 'interaction'), | |
331 | self._chapter(70, 80, remove=True), | |
332 | self._sponsor_chapter(70, 90, 'sponsor'), | |
333 | self._sponsor_chapter(80, 100, 'interaction'), | |
334 | self._sponsor_chapter(120, 170, 'selfpromo'), | |
335 | self._sponsor_chapter(130, 180, 'outro'), | |
336 | self._chapter(140, 150, remove=True), | |
337 | self._chapter(150, 160, remove=True)] | |
338 | expected = self._chapters( | |
339 | [10, 20, 30, 40, 50, 70, 80, 100, 110, 130, 140, 160], | |
340 | ['c', '[SponsorBlock]: Sponsor, Intermission/Intro Animation', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', | |
341 | 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Interaction Reminder', | |
342 | '[SponsorBlock]: Interaction Reminder', 'c', '[SponsorBlock]: Unpaid/Self Promotion', | |
343 | '[SponsorBlock]: Unpaid/Self Promotion, Endcards/Credits', '[SponsorBlock]: Endcards/Credits', 'c']) | |
344 | expected_cuts = [self._chapter(20, 30, remove=True), | |
345 | self._chapter(70, 80, remove=True), | |
346 | self._chapter(140, 160, remove=True)] | |
347 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, expected_cuts) | |
348 | ||
349 | def test_remove_marked_arrange_sponsors_SponsorOverlapsMultipleChapters(self): | |
350 | chapters = (self._chapters([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']) | |
351 | + [self._sponsor_chapter(10, 90, 'sponsor')]) | |
352 | expected = self._chapters([10, 90, 100], ['c1', '[SponsorBlock]: Sponsor', 'c5']) | |
353 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
354 | ||
355 | def test_remove_marked_arrange_sponsors_CutOverlapsMultipleChapters(self): | |
356 | cuts = [self._chapter(10, 90, remove=True)] | |
357 | chapters = self._chapters([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']) + cuts | |
358 | expected = self._chapters([10, 20], ['c1', 'c5']) | |
359 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
360 | ||
361 | def test_remove_marked_arrange_sponsors_SponsorsWithinSomeChaptersAndOverlappingOthers(self): | |
362 | chapters = (self._chapters([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4']) | |
363 | + [self._sponsor_chapter(20, 30, 'sponsor'), | |
364 | self._sponsor_chapter(50, 70, 'selfpromo')]) | |
365 | expected = self._chapters([10, 20, 30, 40, 50, 70, 80], | |
366 | ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c2', 'c3', | |
367 | '[SponsorBlock]: Unpaid/Self Promotion', 'c4']) | |
368 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
369 | ||
370 | def test_remove_marked_arrange_sponsors_CutsWithinSomeChaptersAndOverlappingOthers(self): | |
371 | cuts = [self._chapter(20, 30, remove=True), self._chapter(50, 70, remove=True)] | |
372 | chapters = self._chapters([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4']) + cuts | |
373 | expected = self._chapters([10, 30, 40, 50], ['c1', 'c2', 'c3', 'c4']) | |
374 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
375 | ||
376 | def test_remove_marked_arrange_sponsors_ChaptersAfterLastSponsor(self): | |
377 | chapters = (self._chapters([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']) | |
378 | + [self._sponsor_chapter(10, 30, 'music_offtopic')]) | |
379 | expected = self._chapters( | |
380 | [10, 30, 40, 50, 60], | |
381 | ['c1', '[SponsorBlock]: Non-Music Section', 'c2', 'c3', 'c4']) | |
382 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
383 | ||
384 | def test_remove_marked_arrange_sponsors_ChaptersAfterLastCut(self): | |
385 | cuts = [self._chapter(10, 30, remove=True)] | |
386 | chapters = self._chapters([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']) + cuts | |
387 | expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) | |
388 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
389 | ||
390 | def test_remove_marked_arrange_sponsors_SponsorStartsAtChapterStart(self): | |
391 | chapters = (self._chapters([10, 20, 40], ['c1', 'c2', 'c3']) | |
392 | + [self._sponsor_chapter(20, 30, 'sponsor')]) | |
393 | expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3']) | |
394 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
395 | ||
396 | def test_remove_marked_arrange_sponsors_CutStartsAtChapterStart(self): | |
397 | cuts = [self._chapter(20, 30, remove=True)] | |
398 | chapters = self._chapters([10, 20, 40], ['c1', 'c2', 'c3']) + cuts | |
399 | expected = self._chapters([10, 20, 30], ['c1', 'c2', 'c3']) | |
400 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
401 | ||
402 | def test_remove_marked_arrange_sponsors_SponsorEndsAtChapterEnd(self): | |
403 | chapters = (self._chapters([10, 30, 40], ['c1', 'c2', 'c3']) | |
404 | + [self._sponsor_chapter(20, 30, 'sponsor')]) | |
405 | expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3']) | |
406 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
407 | ||
408 | def test_remove_marked_arrange_sponsors_CutEndsAtChapterEnd(self): | |
409 | cuts = [self._chapter(20, 30, remove=True)] | |
410 | chapters = self._chapters([10, 30, 40], ['c1', 'c2', 'c3']) + cuts | |
411 | expected = self._chapters([10, 20, 30], ['c1', 'c2', 'c3']) | |
412 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
413 | ||
414 | def test_remove_marked_arrange_sponsors_SponsorCoincidesWithChapters(self): | |
415 | chapters = (self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) | |
416 | + [self._sponsor_chapter(10, 30, 'sponsor')]) | |
417 | expected = self._chapters([10, 30, 40], ['c1', '[SponsorBlock]: Sponsor', 'c4']) | |
418 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
419 | ||
420 | def test_remove_marked_arrange_sponsors_CutCoincidesWithChapters(self): | |
421 | cuts = [self._chapter(10, 30, remove=True)] | |
422 | chapters = self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) + cuts | |
423 | expected = self._chapters([10, 20], ['c1', 'c4']) | |
424 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
425 | ||
426 | def test_remove_marked_arrange_sponsors_SponsorsAtVideoBoundaries(self): | |
427 | chapters = (self._chapters([20, 40, 60], ['c1', 'c2', 'c3']) | |
428 | + [self._sponsor_chapter(0, 10, 'intro'), self._sponsor_chapter(50, 60, 'outro')]) | |
429 | expected = self._chapters( | |
430 | [10, 20, 40, 50, 60], ['[SponsorBlock]: Intermission/Intro Animation', 'c1', 'c2', 'c3', '[SponsorBlock]: Endcards/Credits']) | |
431 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
432 | ||
433 | def test_remove_marked_arrange_sponsors_CutsAtVideoBoundaries(self): | |
434 | cuts = [self._chapter(0, 10, remove=True), self._chapter(50, 60, remove=True)] | |
435 | chapters = self._chapters([20, 40, 60], ['c1', 'c2', 'c3']) + cuts | |
436 | expected = self._chapters([10, 30, 40], ['c1', 'c2', 'c3']) | |
437 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
438 | ||
439 | def test_remove_marked_arrange_sponsors_SponsorsOverlapChaptersAtVideoBoundaries(self): | |
440 | chapters = (self._chapters([10, 40, 50], ['c1', 'c2', 'c3']) | |
441 | + [self._sponsor_chapter(0, 20, 'intro'), self._sponsor_chapter(30, 50, 'outro')]) | |
442 | expected = self._chapters( | |
443 | [20, 30, 50], ['[SponsorBlock]: Intermission/Intro Animation', 'c2', '[SponsorBlock]: Endcards/Credits']) | |
444 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
445 | ||
446 | def test_remove_marked_arrange_sponsors_CutsOverlapChaptersAtVideoBoundaries(self): | |
447 | cuts = [self._chapter(0, 20, remove=True), self._chapter(30, 50, remove=True)] | |
448 | chapters = self._chapters([10, 40, 50], ['c1', 'c2', 'c3']) + cuts | |
449 | expected = self._chapters([10], ['c2']) | |
450 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) | |
451 | ||
452 | def test_remove_marked_arrange_sponsors_EverythingSponsored(self): | |
453 | chapters = (self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) | |
454 | + [self._sponsor_chapter(0, 20, 'intro'), self._sponsor_chapter(20, 40, 'outro')]) | |
455 | expected = self._chapters([20, 40], ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits']) | |
456 | self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) | |
457 | ||
458 | def test_remove_marked_arrange_sponsors_EverythingCut(self): | |
459 | cuts = [self._chapter(0, 20, remove=True), self._chapter(20, 40, remove=True)] | |
460 | chapters = self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) + cuts | |
461 | self._remove_marked_arrange_sponsors_test_impl( | |
462 | chapters, [], [self._chapter(0, 40, remove=True)]) | |
463 | ||
464 | def test_remove_marked_arrange_sponsors_TinyChaptersInTheOriginalArePreserved(self): | |
465 | chapters = self._chapters([0.1, 0.2, 0.3, 0.4], ['c1', 'c2', 'c3', 'c4']) | |
466 | self._remove_marked_arrange_sponsors_test_impl(chapters, chapters, []) | |
467 | ||
468 | def test_remove_marked_arrange_sponsors_TinySponsorsAreIgnored(self): | |
469 | chapters = [self._sponsor_chapter(0, 0.1, 'intro'), self._chapter(0.1, 0.2, 'c1'), | |
470 | self._sponsor_chapter(0.2, 0.3, 'sponsor'), self._chapter(0.3, 0.4, 'c2'), | |
471 | self._sponsor_chapter(0.4, 0.5, 'outro')] | |
472 | self._remove_marked_arrange_sponsors_test_impl( | |
473 | chapters, self._chapters([0.3, 0.5], ['c1', 'c2']), []) | |
474 | ||
475 | def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromCutsAreIgnored(self): | |
476 | cuts = [self._chapter(1.5, 2.5, remove=True)] | |
477 | chapters = self._chapters([2, 3, 3.5], ['c1', 'c2', 'c3']) + cuts | |
478 | self._remove_marked_arrange_sponsors_test_impl( | |
479 | chapters, self._chapters([2, 2.5], ['c1', 'c3']), cuts) | |
480 | ||
c6af2dd8 NA |
481 | def test_remove_marked_arrange_sponsors_SingleTinyChapterIsPreserved(self): |
482 | cuts = [self._chapter(0.5, 2, remove=True)] | |
483 | chapters = self._chapters([2], ['c']) + cuts | |
484 | self._remove_marked_arrange_sponsors_test_impl( | |
485 | chapters, self._chapters([0.5], ['c']), cuts) | |
486 | ||
487 | def test_remove_marked_arrange_sponsors_TinyChapterAtTheStartPrependedToTheNext(self): | |
488 | cuts = [self._chapter(0.5, 2, remove=True)] | |
489 | chapters = self._chapters([2, 4], ['c1', 'c2']) + cuts | |
490 | self._remove_marked_arrange_sponsors_test_impl( | |
491 | chapters, self._chapters([2.5], ['c2']), cuts) | |
492 | ||
7a340e0d NA |
493 | def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromSponsorOverlapAreIgnored(self): |
494 | chapters = self._chapters([1, 3, 4], ['c1', 'c2', 'c3']) + [ | |
495 | self._sponsor_chapter(1.5, 2.5, 'sponsor')] | |
496 | self._remove_marked_arrange_sponsors_test_impl( | |
c6af2dd8 | 497 | chapters, self._chapters([1.5, 2.5, 4], ['c1', '[SponsorBlock]: Sponsor', 'c3']), []) |
7a340e0d NA |
498 | |
499 | def test_remove_marked_arrange_sponsors_TinySponsorsOverlapsAreIgnored(self): | |
500 | chapters = self._chapters([2, 3, 5], ['c1', 'c2', 'c3']) + [ | |
501 | self._sponsor_chapter(1, 3, 'sponsor'), | |
502 | self._sponsor_chapter(2.5, 4, 'selfpromo') | |
503 | ] | |
504 | self._remove_marked_arrange_sponsors_test_impl( | |
505 | chapters, self._chapters([1, 3, 4, 5], [ | |
506 | 'c1', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion', 'c3']), []) | |
507 | ||
c6af2dd8 NA |
508 | def test_remove_marked_arrange_sponsors_TinySponsorsPrependedToTheNextSponsor(self): |
509 | chapters = self._chapters([4], ['c']) + [ | |
510 | self._sponsor_chapter(1.5, 2, 'sponsor'), | |
511 | self._sponsor_chapter(2, 4, 'selfpromo') | |
512 | ] | |
513 | self._remove_marked_arrange_sponsors_test_impl( | |
514 | chapters, self._chapters([1.5, 4], ['c', '[SponsorBlock]: Unpaid/Self Promotion']), []) | |
515 | ||
516 | def test_remove_marked_arrange_sponsors_SmallestSponsorInTheOverlapGetsNamed(self): | |
517 | self._pp._sponsorblock_chapter_title = '[SponsorBlock]: %(name)s' | |
518 | chapters = self._chapters([10], ['c']) + [ | |
519 | self._sponsor_chapter(2, 8, 'sponsor'), | |
520 | self._sponsor_chapter(4, 6, 'selfpromo') | |
521 | ] | |
522 | self._remove_marked_arrange_sponsors_test_impl( | |
523 | chapters, self._chapters([2, 4, 6, 8, 10], [ | |
524 | 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion', | |
525 | '[SponsorBlock]: Sponsor', 'c' | |
526 | ]), []) | |
527 | ||
7a340e0d NA |
528 | def test_make_concat_opts_CommonCase(self): |
529 | sponsor_chapters = [self._chapter(1, 2, 's1'), self._chapter(10, 20, 's2')] | |
530 | expected = '''ffconcat version 1.0 | |
531 | file 'file:test' | |
532 | outpoint 1.000000 | |
533 | file 'file:test' | |
534 | inpoint 2.000000 | |
535 | outpoint 10.000000 | |
536 | file 'file:test' | |
537 | inpoint 20.000000 | |
538 | ''' | |
539 | opts = self._pp._make_concat_opts(sponsor_chapters, 30) | |
540 | self.assertEqual(expected, ''.join(self._pp._concat_spec(['test'] * len(opts), opts))) | |
541 | ||
542 | def test_make_concat_opts_NoZeroDurationChunkAtVideoStart(self): | |
543 | sponsor_chapters = [self._chapter(0, 1, 's1'), self._chapter(10, 20, 's2')] | |
544 | expected = '''ffconcat version 1.0 | |
545 | file 'file:test' | |
546 | inpoint 1.000000 | |
547 | outpoint 10.000000 | |
548 | file 'file:test' | |
549 | inpoint 20.000000 | |
550 | ''' | |
551 | opts = self._pp._make_concat_opts(sponsor_chapters, 30) | |
552 | self.assertEqual(expected, ''.join(self._pp._concat_spec(['test'] * len(opts), opts))) | |
553 | ||
554 | def test_make_concat_opts_NoZeroDurationChunkAtVideoEnd(self): | |
555 | sponsor_chapters = [self._chapter(1, 2, 's1'), self._chapter(10, 20, 's2')] | |
556 | expected = '''ffconcat version 1.0 | |
557 | file 'file:test' | |
558 | outpoint 1.000000 | |
559 | file 'file:test' | |
560 | inpoint 2.000000 | |
561 | outpoint 10.000000 | |
562 | ''' | |
563 | opts = self._pp._make_concat_opts(sponsor_chapters, 20) | |
564 | self.assertEqual(expected, ''.join(self._pp._concat_spec(['test'] * len(opts), opts))) | |
565 | ||
566 | def test_quote_for_concat_RunsOfQuotes(self): | |
567 | self.assertEqual( | |
568 | r"'special '\'' '\'\''characters'\'\'\''galore'", | |
569 | self._pp._quote_for_ffmpeg("special ' ''characters'''galore")) | |
570 | ||
571 | def test_quote_for_concat_QuotesAtStart(self): | |
572 | self.assertEqual( | |
573 | r"\'\'\''special '\'' characters '\'' galore'", | |
574 | self._pp._quote_for_ffmpeg("'''special ' characters ' galore")) | |
575 | ||
576 | def test_quote_for_concat_QuotesAtEnd(self): | |
577 | self.assertEqual( | |
578 | r"'special '\'' characters '\'' galore'\'\'\'", | |
579 | self._pp._quote_for_ffmpeg("special ' characters ' galore'''")) |