]>
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, |
337e0c62 | 19 | ) |
5bfa4862 | 20 | |
21 | ||
22 | class 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 | ||
42 | class 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 | 63 | class 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 | ||
74 | class 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 | |
513 | file 'file:test' | |
514 | outpoint 1.000000 | |
515 | file 'file:test' | |
516 | inpoint 2.000000 | |
517 | outpoint 10.000000 | |
518 | file 'file:test' | |
519 | inpoint 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 | |
527 | file 'file:test' | |
528 | inpoint 1.000000 | |
529 | outpoint 10.000000 | |
530 | file 'file:test' | |
531 | inpoint 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 | |
539 | file 'file:test' | |
540 | outpoint 1.000000 | |
541 | file 'file:test' | |
542 | inpoint 2.000000 | |
543 | outpoint 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'''")) |