4 from __future__
import unicode_literals
6 # Allow direct execution
10 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
13 # Various small unit tests
17 import xml
.etree
.ElementTree
19 from yt_dlp
.utils
import (
42 get_element_by_attribute
,
43 get_elements_by_class
,
44 get_elements_by_attribute
,
105 parse_dfxp_time_expr
,
108 cli_valueless_option
,
114 from yt_dlp
.compat
import (
116 compat_etree_fromstring
,
125 class TestUtil(unittest
.TestCase
):
126 def test_timeconvert(self
):
127 self
.assertTrue(timeconvert('') is None)
128 self
.assertTrue(timeconvert('bougrg') is None)
130 def test_sanitize_filename(self
):
131 self
.assertEqual(sanitize_filename(''), '')
132 self
.assertEqual(sanitize_filename('abc'), 'abc')
133 self
.assertEqual(sanitize_filename('abc_d-e'), 'abc_d-e')
135 self
.assertEqual(sanitize_filename('123'), '123')
137 self
.assertEqual('abc_de', sanitize_filename('abc/de'))
138 self
.assertFalse('/' in sanitize_filename('abc/de///'))
140 self
.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de'))
141 self
.assertEqual('xxx', sanitize_filename('xxx/<>\\*|'))
142 self
.assertEqual('yes no', sanitize_filename('yes? no'))
143 self
.assertEqual('this - that', sanitize_filename('this: that'))
145 self
.assertEqual(sanitize_filename('AT&T'), 'AT&T')
147 self
.assertEqual(sanitize_filename(aumlaut
), aumlaut
)
148 tests
= '\u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0430'
149 self
.assertEqual(sanitize_filename(tests
), tests
)
152 sanitize_filename('New World record at 0:12:34'),
153 'New World record at 0_12_34')
155 self
.assertEqual(sanitize_filename('--gasdgf'), '_-gasdgf')
156 self
.assertEqual(sanitize_filename('--gasdgf', is_id
=True), '--gasdgf')
157 self
.assertEqual(sanitize_filename('.gasdgf'), 'gasdgf')
158 self
.assertEqual(sanitize_filename('.gasdgf', is_id
=True), '.gasdgf')
162 for fbc
in forbidden
:
163 self
.assertTrue(fbc
not in sanitize_filename(fc
))
165 def test_sanitize_filename_restricted(self
):
166 self
.assertEqual(sanitize_filename('abc', restricted
=True), 'abc')
167 self
.assertEqual(sanitize_filename('abc_d-e', restricted
=True), 'abc_d-e')
169 self
.assertEqual(sanitize_filename('123', restricted
=True), '123')
171 self
.assertEqual('abc_de', sanitize_filename('abc/de', restricted
=True))
172 self
.assertFalse('/' in sanitize_filename('abc/de///', restricted
=True))
174 self
.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de', restricted
=True))
175 self
.assertEqual('xxx', sanitize_filename('xxx/<>\\*|', restricted
=True))
176 self
.assertEqual('yes_no', sanitize_filename('yes? no', restricted
=True))
177 self
.assertEqual('this_-_that', sanitize_filename('this: that', restricted
=True))
179 tests
= 'aäb\u4e2d\u56fd\u7684c'
180 self
.assertEqual(sanitize_filename(tests
, restricted
=True), 'aab_c')
181 self
.assertTrue(sanitize_filename('\xf6', restricted
=True) != '') # No empty filename
183 forbidden
= '"\0\\/&!: \'\t\n()[]{}$;`^,#'
185 for fbc
in forbidden
:
186 self
.assertTrue(fbc
not in sanitize_filename(fc
, restricted
=True))
188 # Handle a common case more neatly
189 self
.assertEqual(sanitize_filename('\u5927\u58f0\u5e26 - Song', restricted
=True), 'Song')
190 self
.assertEqual(sanitize_filename('\u603b\u7edf: Speech', restricted
=True), 'Speech')
191 # .. but make sure the file name is never empty
192 self
.assertTrue(sanitize_filename('-', restricted
=True) != '')
193 self
.assertTrue(sanitize_filename(':', restricted
=True) != '')
195 self
.assertEqual(sanitize_filename(
196 'ÂÃÄÀÁÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖŐØŒÙÚÛÜŰÝÞßàáâãäåæçèéêëìíîïðñòóôõöőøœùúûüűýþÿ', restricted
=True),
197 'AAAAAAAECEEEEIIIIDNOOOOOOOOEUUUUUYTHssaaaaaaaeceeeeiiiionooooooooeuuuuuythy')
199 def test_sanitize_ids(self
):
200 self
.assertEqual(sanitize_filename('_n_cd26wFpw', is_id
=True), '_n_cd26wFpw')
201 self
.assertEqual(sanitize_filename('_BD_eEpuzXw', is_id
=True), '_BD_eEpuzXw')
202 self
.assertEqual(sanitize_filename('N0Y__7-UOdI', is_id
=True), 'N0Y__7-UOdI')
204 def test_sanitize_path(self
):
205 if sys
.platform
!= 'win32':
208 self
.assertEqual(sanitize_path('abc'), 'abc')
209 self
.assertEqual(sanitize_path('abc/def'), 'abc\\def')
210 self
.assertEqual(sanitize_path('abc\\def'), 'abc\\def')
211 self
.assertEqual(sanitize_path('abc|def'), 'abc#def')
212 self
.assertEqual(sanitize_path('<>:"|?*'), '#######')
213 self
.assertEqual(sanitize_path('C:/abc/def'), 'C:\\abc\\def')
214 self
.assertEqual(sanitize_path('C?:/abc/def'), 'C##\\abc\\def')
216 self
.assertEqual(sanitize_path('\\\\?\\UNC\\ComputerName\\abc'), '\\\\?\\UNC\\ComputerName\\abc')
217 self
.assertEqual(sanitize_path('\\\\?\\UNC/ComputerName/abc'), '\\\\?\\UNC\\ComputerName\\abc')
219 self
.assertEqual(sanitize_path('\\\\?\\C:\\abc'), '\\\\?\\C:\\abc')
220 self
.assertEqual(sanitize_path('\\\\?\\C:/abc'), '\\\\?\\C:\\abc')
221 self
.assertEqual(sanitize_path('\\\\?\\C:\\ab?c\\de:f'), '\\\\?\\C:\\ab#c\\de#f')
222 self
.assertEqual(sanitize_path('\\\\?\\C:\\abc'), '\\\\?\\C:\\abc')
225 sanitize_path('youtube/%(uploader)s/%(autonumber)s-%(title)s-%(upload_date)s.%(ext)s'),
226 'youtube\\%(uploader)s\\%(autonumber)s-%(title)s-%(upload_date)s.%(ext)s')
229 sanitize_path('youtube/TheWreckingYard ./00001-Not bad, Especially for Free! (1987 Yamaha 700)-20141116.mp4.part'),
230 'youtube\\TheWreckingYard #\\00001-Not bad, Especially for Free! (1987 Yamaha 700)-20141116.mp4.part')
231 self
.assertEqual(sanitize_path('abc/def...'), 'abc\\def..#')
232 self
.assertEqual(sanitize_path('abc.../def'), 'abc..#\\def')
233 self
.assertEqual(sanitize_path('abc.../def...'), 'abc..#\\def..#')
235 self
.assertEqual(sanitize_path('../abc'), '..\\abc')
236 self
.assertEqual(sanitize_path('../../abc'), '..\\..\\abc')
237 self
.assertEqual(sanitize_path('./abc'), 'abc')
238 self
.assertEqual(sanitize_path('./../abc'), '..\\abc')
240 def test_sanitize_url(self
):
241 self
.assertEqual(sanitize_url('//foo.bar'), 'http://foo.bar')
242 self
.assertEqual(sanitize_url('httpss://foo.bar'), 'https://foo.bar')
243 self
.assertEqual(sanitize_url('rmtps://foo.bar'), 'rtmps://foo.bar')
244 self
.assertEqual(sanitize_url('https://foo.bar'), 'https://foo.bar')
245 self
.assertEqual(sanitize_url('foo bar'), 'foo bar')
247 def test_extract_basic_auth(self
):
248 auth_header
= lambda url
: sanitized_Request(url
).get_header('Authorization')
249 self
.assertFalse(auth_header('http://foo.bar'))
250 self
.assertFalse(auth_header('http://:foo.bar'))
251 self
.assertEqual(auth_header('http://@foo.bar'), 'Basic Og==')
252 self
.assertEqual(auth_header('http://:pass@foo.bar'), 'Basic OnBhc3M=')
253 self
.assertEqual(auth_header('http://user:@foo.bar'), 'Basic dXNlcjo=')
254 self
.assertEqual(auth_header('http://user:pass@foo.bar'), 'Basic dXNlcjpwYXNz')
256 def test_expand_path(self
):
258 return '%{0}%'.format(var
) if sys
.platform
== 'win32' else '${0}'.format(var
)
260 compat_setenv('yt_dlp_EXPATH_PATH', 'expanded')
261 self
.assertEqual(expand_path(env('yt_dlp_EXPATH_PATH')), 'expanded')
262 self
.assertEqual(expand_path(env('HOME')), compat_getenv('HOME'))
263 self
.assertEqual(expand_path('~'), compat_getenv('HOME'))
265 expand_path('~/%s' % env('yt_dlp_EXPATH_PATH')),
266 '%s/expanded' % compat_getenv('HOME'))
268 def test_prepend_extension(self
):
269 self
.assertEqual(prepend_extension('abc.ext', 'temp'), 'abc.temp.ext')
270 self
.assertEqual(prepend_extension('abc.ext', 'temp', 'ext'), 'abc.temp.ext')
271 self
.assertEqual(prepend_extension('abc.unexpected_ext', 'temp', 'ext'), 'abc.unexpected_ext.temp')
272 self
.assertEqual(prepend_extension('abc', 'temp'), 'abc.temp')
273 self
.assertEqual(prepend_extension('.abc', 'temp'), '.abc.temp')
274 self
.assertEqual(prepend_extension('.abc.ext', 'temp'), '.abc.temp.ext')
276 def test_replace_extension(self
):
277 self
.assertEqual(replace_extension('abc.ext', 'temp'), 'abc.temp')
278 self
.assertEqual(replace_extension('abc.ext', 'temp', 'ext'), 'abc.temp')
279 self
.assertEqual(replace_extension('abc.unexpected_ext', 'temp', 'ext'), 'abc.unexpected_ext.temp')
280 self
.assertEqual(replace_extension('abc', 'temp'), 'abc.temp')
281 self
.assertEqual(replace_extension('.abc', 'temp'), '.abc.temp')
282 self
.assertEqual(replace_extension('.abc.ext', 'temp'), '.abc.temp')
284 def test_subtitles_filename(self
):
285 self
.assertEqual(subtitles_filename('abc.ext', 'en', 'vtt'), 'abc.en.vtt')
286 self
.assertEqual(subtitles_filename('abc.ext', 'en', 'vtt', 'ext'), 'abc.en.vtt')
287 self
.assertEqual(subtitles_filename('abc.unexpected_ext', 'en', 'vtt', 'ext'), 'abc.unexpected_ext.en.vtt')
289 def test_remove_start(self
):
290 self
.assertEqual(remove_start(None, 'A - '), None)
291 self
.assertEqual(remove_start('A - B', 'A - '), 'B')
292 self
.assertEqual(remove_start('B - A', 'A - '), 'B - A')
294 def test_remove_end(self
):
295 self
.assertEqual(remove_end(None, ' - B'), None)
296 self
.assertEqual(remove_end('A - B', ' - B'), 'A')
297 self
.assertEqual(remove_end('B - A', ' - B'), 'B - A')
299 def test_remove_quotes(self
):
300 self
.assertEqual(remove_quotes(None), None)
301 self
.assertEqual(remove_quotes('"'), '"')
302 self
.assertEqual(remove_quotes("'"), "'")
303 self
.assertEqual(remove_quotes(';'), ';')
304 self
.assertEqual(remove_quotes('";'), '";')
305 self
.assertEqual(remove_quotes('""'), '')
306 self
.assertEqual(remove_quotes('";"'), ';')
308 def test_ordered_set(self
):
309 self
.assertEqual(orderedSet([1, 1, 2, 3, 4, 4, 5, 6, 7, 3, 5]), [1, 2, 3, 4, 5, 6, 7])
310 self
.assertEqual(orderedSet([]), [])
311 self
.assertEqual(orderedSet([1]), [1])
312 # keep the list ordered
313 self
.assertEqual(orderedSet([135, 1, 1, 1]), [135, 1])
315 def test_unescape_html(self
):
316 self
.assertEqual(unescapeHTML('%20;'), '%20;')
317 self
.assertEqual(unescapeHTML('/'), '/')
318 self
.assertEqual(unescapeHTML('/'), '/')
319 self
.assertEqual(unescapeHTML('é'), 'é')
320 self
.assertEqual(unescapeHTML('�'), '�')
321 self
.assertEqual(unescapeHTML('&a"'), '&a"')
323 self
.assertEqual(unescapeHTML('.''), '.\'')
325 def test_date_from_str(self
):
326 self
.assertEqual(date_from_str('yesterday'), date_from_str('now-1day'))
327 self
.assertEqual(date_from_str('now+7day'), date_from_str('now+1week'))
328 self
.assertEqual(date_from_str('now+14day'), date_from_str('now+2week'))
329 self
.assertEqual(date_from_str('20200229+365day'), date_from_str('20200229+1year'))
330 self
.assertEqual(date_from_str('20210131+28day'), date_from_str('20210131+1month'))
332 def test_datetime_from_str(self
):
333 self
.assertEqual(datetime_from_str('yesterday', precision
='day'), datetime_from_str('now-1day', precision
='auto'))
334 self
.assertEqual(datetime_from_str('now+7day', precision
='day'), datetime_from_str('now+1week', precision
='auto'))
335 self
.assertEqual(datetime_from_str('now+14day', precision
='day'), datetime_from_str('now+2week', precision
='auto'))
336 self
.assertEqual(datetime_from_str('20200229+365day', precision
='day'), datetime_from_str('20200229+1year', precision
='auto'))
337 self
.assertEqual(datetime_from_str('20210131+28day', precision
='day'), datetime_from_str('20210131+1month', precision
='auto'))
338 self
.assertEqual(datetime_from_str('20210131+59day', precision
='day'), datetime_from_str('20210131+2month', precision
='auto'))
339 self
.assertEqual(datetime_from_str('now+1day', precision
='hour'), datetime_from_str('now+24hours', precision
='auto'))
340 self
.assertEqual(datetime_from_str('now+23hours', precision
='hour'), datetime_from_str('now+23hours', precision
='auto'))
342 def test_daterange(self
):
343 _20century
= DateRange("19000101", "20000101")
344 self
.assertFalse("17890714" in _20century
)
345 _ac
= DateRange("00010101")
346 self
.assertTrue("19690721" in _ac
)
347 _firstmilenium
= DateRange(end
="10000101")
348 self
.assertTrue("07110427" in _firstmilenium
)
350 def test_unified_dates(self
):
351 self
.assertEqual(unified_strdate('December 21, 2010'), '20101221')
352 self
.assertEqual(unified_strdate('8/7/2009'), '20090708')
353 self
.assertEqual(unified_strdate('Dec 14, 2012'), '20121214')
354 self
.assertEqual(unified_strdate('2012/10/11 01:56:38 +0000'), '20121011')
355 self
.assertEqual(unified_strdate('1968 12 10'), '19681210')
356 self
.assertEqual(unified_strdate('1968-12-10'), '19681210')
357 self
.assertEqual(unified_strdate('28/01/2014 21:00:00 +0100'), '20140128')
359 unified_strdate('11/26/2014 11:30:00 AM PST', day_first
=False),
362 unified_strdate('2/2/2015 6:47:40 PM', day_first
=False),
364 self
.assertEqual(unified_strdate('Feb 14th 2016 5:45PM'), '20160214')
365 self
.assertEqual(unified_strdate('25-09-2014'), '20140925')
366 self
.assertEqual(unified_strdate('27.02.2016 17:30'), '20160227')
367 self
.assertEqual(unified_strdate('UNKNOWN DATE FORMAT'), None)
368 self
.assertEqual(unified_strdate('Feb 7, 2016 at 6:35 pm'), '20160207')
369 self
.assertEqual(unified_strdate('July 15th, 2013'), '20130715')
370 self
.assertEqual(unified_strdate('September 1st, 2013'), '20130901')
371 self
.assertEqual(unified_strdate('Sep 2nd, 2013'), '20130902')
372 self
.assertEqual(unified_strdate('November 3rd, 2019'), '20191103')
373 self
.assertEqual(unified_strdate('October 23rd, 2005'), '20051023')
375 def test_unified_timestamps(self
):
376 self
.assertEqual(unified_timestamp('December 21, 2010'), 1292889600)
377 self
.assertEqual(unified_timestamp('8/7/2009'), 1247011200)
378 self
.assertEqual(unified_timestamp('Dec 14, 2012'), 1355443200)
379 self
.assertEqual(unified_timestamp('2012/10/11 01:56:38 +0000'), 1349920598)
380 self
.assertEqual(unified_timestamp('1968 12 10'), -33436800)
381 self
.assertEqual(unified_timestamp('1968-12-10'), -33436800)
382 self
.assertEqual(unified_timestamp('28/01/2014 21:00:00 +0100'), 1390939200)
384 unified_timestamp('11/26/2014 11:30:00 AM PST', day_first
=False),
387 unified_timestamp('2/2/2015 6:47:40 PM', day_first
=False),
389 self
.assertEqual(unified_timestamp('Feb 14th 2016 5:45PM'), 1455471900)
390 self
.assertEqual(unified_timestamp('25-09-2014'), 1411603200)
391 self
.assertEqual(unified_timestamp('27.02.2016 17:30'), 1456594200)
392 self
.assertEqual(unified_timestamp('UNKNOWN DATE FORMAT'), None)
393 self
.assertEqual(unified_timestamp('May 16, 2016 11:15 PM'), 1463440500)
394 self
.assertEqual(unified_timestamp('Feb 7, 2016 at 6:35 pm'), 1454870100)
395 self
.assertEqual(unified_timestamp('2017-03-30T17:52:41Q'), 1490896361)
396 self
.assertEqual(unified_timestamp('Sep 11, 2013 | 5:49 AM'), 1378878540)
397 self
.assertEqual(unified_timestamp('December 15, 2017 at 7:49 am'), 1513324140)
398 self
.assertEqual(unified_timestamp('2018-03-14T08:32:43.1493874+00:00'), 1521016363)
400 def test_determine_ext(self
):
401 self
.assertEqual(determine_ext('http://example.com/foo/bar.mp4/?download'), 'mp4')
402 self
.assertEqual(determine_ext('http://example.com/foo/bar/?download', None), None)
403 self
.assertEqual(determine_ext('http://example.com/foo/bar.nonext/?download', None), None)
404 self
.assertEqual(determine_ext('http://example.com/foo/bar/mp4?download', None), None)
405 self
.assertEqual(determine_ext('http://example.com/foo/bar.m3u8//?download'), 'm3u8')
406 self
.assertEqual(determine_ext('foobar', None), None)
408 def test_find_xpath_attr(self
):
416 doc
= compat_etree_fromstring(testxml
)
418 self
.assertEqual(find_xpath_attr(doc
, './/fourohfour', 'n'), None)
419 self
.assertEqual(find_xpath_attr(doc
, './/fourohfour', 'n', 'v'), None)
420 self
.assertEqual(find_xpath_attr(doc
, './/node', 'n'), None)
421 self
.assertEqual(find_xpath_attr(doc
, './/node', 'n', 'v'), None)
422 self
.assertEqual(find_xpath_attr(doc
, './/node', 'x'), doc
[1])
423 self
.assertEqual(find_xpath_attr(doc
, './/node', 'x', 'a'), doc
[1])
424 self
.assertEqual(find_xpath_attr(doc
, './/node', 'x', 'b'), doc
[3])
425 self
.assertEqual(find_xpath_attr(doc
, './/node', 'y'), doc
[2])
426 self
.assertEqual(find_xpath_attr(doc
, './/node', 'y', 'c'), doc
[2])
427 self
.assertEqual(find_xpath_attr(doc
, './/node', 'y', 'd'), doc
[3])
428 self
.assertEqual(find_xpath_attr(doc
, './/node', 'x', ''), doc
[4])
430 def test_xpath_with_ns(self
):
431 testxml
= '''<root xmlns:media="http://example.com/">
433 <media:author>The Author</media:author>
434 <url>http://server.com/download.mp3</url>
437 doc
= compat_etree_fromstring(testxml
)
438 find
= lambda p
: doc
.find(xpath_with_ns(p
, {'media': 'http://example.com/'}
))
439 self
.assertTrue(find('media:song') is not None)
440 self
.assertEqual(find('media:song/media:author').text
, 'The Author')
441 self
.assertEqual(find('media:song/url').text
, 'http://server.com/download.mp3')
443 def test_xpath_element(self
):
444 doc
= xml
.etree
.ElementTree
.Element('root')
445 div
= xml
.etree
.ElementTree
.SubElement(doc
, 'div')
446 p
= xml
.etree
.ElementTree
.SubElement(div
, 'p')
448 self
.assertEqual(xpath_element(doc
, 'div/p'), p
)
449 self
.assertEqual(xpath_element(doc
, ['div/p']), p
)
450 self
.assertEqual(xpath_element(doc
, ['div/bar', 'div/p']), p
)
451 self
.assertEqual(xpath_element(doc
, 'div/bar', default
='default'), 'default')
452 self
.assertEqual(xpath_element(doc
, ['div/bar'], default
='default'), 'default')
453 self
.assertTrue(xpath_element(doc
, 'div/bar') is None)
454 self
.assertTrue(xpath_element(doc
, ['div/bar']) is None)
455 self
.assertTrue(xpath_element(doc
, ['div/bar'], 'div/baz') is None)
456 self
.assertRaises(ExtractorError
, xpath_element
, doc
, 'div/bar', fatal
=True)
457 self
.assertRaises(ExtractorError
, xpath_element
, doc
, ['div/bar'], fatal
=True)
458 self
.assertRaises(ExtractorError
, xpath_element
, doc
, ['div/bar', 'div/baz'], fatal
=True)
460 def test_xpath_text(self
):
466 doc
= compat_etree_fromstring(testxml
)
467 self
.assertEqual(xpath_text(doc
, 'div/p'), 'Foo')
468 self
.assertEqual(xpath_text(doc
, 'div/bar', default
='default'), 'default')
469 self
.assertTrue(xpath_text(doc
, 'div/bar') is None)
470 self
.assertRaises(ExtractorError
, xpath_text
, doc
, 'div/bar', fatal
=True)
472 def test_xpath_attr(self
):
478 doc
= compat_etree_fromstring(testxml
)
479 self
.assertEqual(xpath_attr(doc
, 'div/p', 'x'), 'a')
480 self
.assertEqual(xpath_attr(doc
, 'div/bar', 'x'), None)
481 self
.assertEqual(xpath_attr(doc
, 'div/p', 'y'), None)
482 self
.assertEqual(xpath_attr(doc
, 'div/bar', 'x', default
='default'), 'default')
483 self
.assertEqual(xpath_attr(doc
, 'div/p', 'y', default
='default'), 'default')
484 self
.assertRaises(ExtractorError
, xpath_attr
, doc
, 'div/bar', 'x', fatal
=True)
485 self
.assertRaises(ExtractorError
, xpath_attr
, doc
, 'div/p', 'y', fatal
=True)
487 def test_smuggle_url(self
):
488 data
= {"ö": "ö", "abc": [3]}
489 url
= 'https://foo.bar/baz?x=y#a'
490 smug_url
= smuggle_url(url
, data
)
491 unsmug_url
, unsmug_data
= unsmuggle_url(smug_url
)
492 self
.assertEqual(url
, unsmug_url
)
493 self
.assertEqual(data
, unsmug_data
)
495 res_url
, res_data
= unsmuggle_url(url
)
496 self
.assertEqual(res_url
, url
)
497 self
.assertEqual(res_data
, None)
499 smug_url
= smuggle_url(url
, {'a': 'b'}
)
500 smug_smug_url
= smuggle_url(smug_url
, {'c': 'd'}
)
501 res_url
, res_data
= unsmuggle_url(smug_smug_url
)
502 self
.assertEqual(res_url
, url
)
503 self
.assertEqual(res_data
, {'a': 'b', 'c': 'd'}
)
505 def test_shell_quote(self
):
506 args
= ['ffmpeg', '-i', encodeFilename('ñ€ß\'.mp4')]
509 """ffmpeg -i 'ñ€ß'"'"'.mp4'""" if compat_os_name
!= 'nt' else '''ffmpeg -i "ñ€ß'.mp4"''')
511 def test_float_or_none(self
):
512 self
.assertEqual(float_or_none('42.42'), 42.42)
513 self
.assertEqual(float_or_none('42'), 42.0)
514 self
.assertEqual(float_or_none(''), None)
515 self
.assertEqual(float_or_none(None), None)
516 self
.assertEqual(float_or_none([]), None)
517 self
.assertEqual(float_or_none(set()), None)
519 def test_int_or_none(self
):
520 self
.assertEqual(int_or_none('42'), 42)
521 self
.assertEqual(int_or_none(''), None)
522 self
.assertEqual(int_or_none(None), None)
523 self
.assertEqual(int_or_none([]), None)
524 self
.assertEqual(int_or_none(set()), None)
526 def test_str_to_int(self
):
527 self
.assertEqual(str_to_int('123,456'), 123456)
528 self
.assertEqual(str_to_int('123.456'), 123456)
529 self
.assertEqual(str_to_int(523), 523)
530 # Python 3 has no long
531 if sys
.version_info
< (3, 0):
532 eval('self.assertEqual(str_to_int(123456L), 123456)')
533 self
.assertEqual(str_to_int('noninteger'), None)
534 self
.assertEqual(str_to_int([]), None)
536 def test_url_basename(self
):
537 self
.assertEqual(url_basename('http://foo.de/'), '')
538 self
.assertEqual(url_basename('http://foo.de/bar/baz'), 'baz')
539 self
.assertEqual(url_basename('http://foo.de/bar/baz?x=y'), 'baz')
540 self
.assertEqual(url_basename('http://foo.de/bar/baz#x=y'), 'baz')
541 self
.assertEqual(url_basename('http://foo.de/bar/baz/'), 'baz')
543 url_basename('http://media.w3.org/2010/05/sintel/trailer.mp4'),
546 def test_base_url(self
):
547 self
.assertEqual(base_url('http://foo.de/'), 'http://foo.de/')
548 self
.assertEqual(base_url('http://foo.de/bar'), 'http://foo.de/')
549 self
.assertEqual(base_url('http://foo.de/bar/'), 'http://foo.de/bar/')
550 self
.assertEqual(base_url('http://foo.de/bar/baz'), 'http://foo.de/bar/')
551 self
.assertEqual(base_url('http://foo.de/bar/baz?x=z/x/c'), 'http://foo.de/bar/')
553 def test_urljoin(self
):
554 self
.assertEqual(urljoin('http://foo.de/', '/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
555 self
.assertEqual(urljoin(b
'http://foo.de/', '/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
556 self
.assertEqual(urljoin('http://foo.de/', b
'/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
557 self
.assertEqual(urljoin(b
'http://foo.de/', b
'/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
558 self
.assertEqual(urljoin('//foo.de/', '/a/b/c.txt'), '//foo.de/a/b/c.txt')
559 self
.assertEqual(urljoin('http://foo.de/', 'a/b/c.txt'), 'http://foo.de/a/b/c.txt')
560 self
.assertEqual(urljoin('http://foo.de', '/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
561 self
.assertEqual(urljoin('http://foo.de', 'a/b/c.txt'), 'http://foo.de/a/b/c.txt')
562 self
.assertEqual(urljoin('http://foo.de/', 'http://foo.de/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
563 self
.assertEqual(urljoin('http://foo.de/', '//foo.de/a/b/c.txt'), '//foo.de/a/b/c.txt')
564 self
.assertEqual(urljoin(None, 'http://foo.de/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
565 self
.assertEqual(urljoin(None, '//foo.de/a/b/c.txt'), '//foo.de/a/b/c.txt')
566 self
.assertEqual(urljoin('', 'http://foo.de/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
567 self
.assertEqual(urljoin(['foobar'], 'http://foo.de/a/b/c.txt'), 'http://foo.de/a/b/c.txt')
568 self
.assertEqual(urljoin('http://foo.de/', None), None)
569 self
.assertEqual(urljoin('http://foo.de/', ''), None)
570 self
.assertEqual(urljoin('http://foo.de/', ['foobar']), None)
571 self
.assertEqual(urljoin('http://foo.de/a/b/c.txt', '.././../d.txt'), 'http://foo.de/d.txt')
572 self
.assertEqual(urljoin('http://foo.de/a/b/c.txt', 'rtmp://foo.de'), 'rtmp://foo.de')
573 self
.assertEqual(urljoin(None, 'rtmp://foo.de'), 'rtmp://foo.de')
575 def test_url_or_none(self
):
576 self
.assertEqual(url_or_none(None), None)
577 self
.assertEqual(url_or_none(''), None)
578 self
.assertEqual(url_or_none('foo'), None)
579 self
.assertEqual(url_or_none('http://foo.de'), 'http://foo.de')
580 self
.assertEqual(url_or_none('https://foo.de'), 'https://foo.de')
581 self
.assertEqual(url_or_none('http$://foo.de'), None)
582 self
.assertEqual(url_or_none('http://foo.de'), 'http://foo.de')
583 self
.assertEqual(url_or_none('//foo.de'), '//foo.de')
584 self
.assertEqual(url_or_none('s3://foo.de'), None)
585 self
.assertEqual(url_or_none('rtmpte://foo.de'), 'rtmpte://foo.de')
586 self
.assertEqual(url_or_none('mms://foo.de'), 'mms://foo.de')
587 self
.assertEqual(url_or_none('rtspu://foo.de'), 'rtspu://foo.de')
588 self
.assertEqual(url_or_none('ftps://foo.de'), 'ftps://foo.de')
590 def test_parse_age_limit(self
):
591 self
.assertEqual(parse_age_limit(None), None)
592 self
.assertEqual(parse_age_limit(False), None)
593 self
.assertEqual(parse_age_limit('invalid'), None)
594 self
.assertEqual(parse_age_limit(0), 0)
595 self
.assertEqual(parse_age_limit(18), 18)
596 self
.assertEqual(parse_age_limit(21), 21)
597 self
.assertEqual(parse_age_limit(22), None)
598 self
.assertEqual(parse_age_limit('18'), 18)
599 self
.assertEqual(parse_age_limit('18+'), 18)
600 self
.assertEqual(parse_age_limit('PG-13'), 13)
601 self
.assertEqual(parse_age_limit('TV-14'), 14)
602 self
.assertEqual(parse_age_limit('TV-MA'), 17)
603 self
.assertEqual(parse_age_limit('TV14'), 14)
604 self
.assertEqual(parse_age_limit('TV_G'), 0)
606 def test_parse_duration(self
):
607 self
.assertEqual(parse_duration(None), None)
608 self
.assertEqual(parse_duration(False), None)
609 self
.assertEqual(parse_duration('invalid'), None)
610 self
.assertEqual(parse_duration('1'), 1)
611 self
.assertEqual(parse_duration('1337:12'), 80232)
612 self
.assertEqual(parse_duration('9:12:43'), 33163)
613 self
.assertEqual(parse_duration('12:00'), 720)
614 self
.assertEqual(parse_duration('00:01:01'), 61)
615 self
.assertEqual(parse_duration('x:y'), None)
616 self
.assertEqual(parse_duration('3h11m53s'), 11513)
617 self
.assertEqual(parse_duration('3h 11m 53s'), 11513)
618 self
.assertEqual(parse_duration('3 hours 11 minutes 53 seconds'), 11513)
619 self
.assertEqual(parse_duration('3 hours 11 mins 53 secs'), 11513)
620 self
.assertEqual(parse_duration('62m45s'), 3765)
621 self
.assertEqual(parse_duration('6m59s'), 419)
622 self
.assertEqual(parse_duration('49s'), 49)
623 self
.assertEqual(parse_duration('0h0m0s'), 0)
624 self
.assertEqual(parse_duration('0m0s'), 0)
625 self
.assertEqual(parse_duration('0s'), 0)
626 self
.assertEqual(parse_duration('01:02:03.05'), 3723.05)
627 self
.assertEqual(parse_duration('T30M38S'), 1838)
628 self
.assertEqual(parse_duration('5 s'), 5)
629 self
.assertEqual(parse_duration('3 min'), 180)
630 self
.assertEqual(parse_duration('2.5 hours'), 9000)
631 self
.assertEqual(parse_duration('02:03:04'), 7384)
632 self
.assertEqual(parse_duration('01:02:03:04'), 93784)
633 self
.assertEqual(parse_duration('1 hour 3 minutes'), 3780)
634 self
.assertEqual(parse_duration('87 Min.'), 5220)
635 self
.assertEqual(parse_duration('PT1H0.040S'), 3600.04)
636 self
.assertEqual(parse_duration('PT00H03M30SZ'), 210)
637 self
.assertEqual(parse_duration('P0Y0M0DT0H4M20.880S'), 260.88)
639 def test_fix_xml_ampersands(self
):
641 fix_xml_ampersands('"&x=y&z=a'), '"&x=y&z=a')
643 fix_xml_ampersands('"&x=y&wrong;&z=a'),
644 '"&x=y&wrong;&z=a')
646 fix_xml_ampersands('&'><"'),
647 '&'><"')
649 fix_xml_ampersands('Ӓ᪼'), 'Ӓ᪼')
650 self
.assertEqual(fix_xml_ampersands('&#&#'), '&#&#')
652 def test_paged_list(self
):
653 def testPL(size
, pagesize
, sliceargs
, expected
):
654 def get_page(pagenum
):
655 firstid
= pagenum
* pagesize
656 upto
= min(size
, pagenum
* pagesize
+ pagesize
)
657 for i
in range(firstid
, upto
):
660 pl
= OnDemandPagedList(get_page
, pagesize
)
661 got
= pl
.getslice(*sliceargs
)
662 self
.assertEqual(got
, expected
)
664 iapl
= InAdvancePagedList(get_page
, size
// pagesize
+ 1, pagesize
)
665 got
= iapl
.getslice(*sliceargs
)
666 self
.assertEqual(got
, expected
)
668 testPL(5, 2, (), [0, 1, 2, 3, 4])
669 testPL(5, 2, (1,), [1, 2, 3, 4])
670 testPL(5, 2, (2,), [2, 3, 4])
671 testPL(5, 2, (4,), [4])
672 testPL(5, 2, (0, 3), [0, 1, 2])
673 testPL(5, 2, (1, 4), [1, 2, 3])
674 testPL(5, 2, (2, 99), [2, 3, 4])
675 testPL(5, 2, (20, 99), [])
677 def test_read_batch_urls(self
):
678 f
= io
.StringIO('''\xef\xbb\xbf foo
681 # More after this line\r
684 self
.assertEqual(read_batch_urls(f
), ['foo', 'bar', 'baz', 'bam'])
686 def test_urlencode_postdata(self
):
687 data
= urlencode_postdata({'username': 'foo@bar.com', 'password': '1234'}
)
688 self
.assertTrue(isinstance(data
, bytes))
690 def test_update_url_query(self
):
692 return compat_parse_qs(compat_urlparse
.urlparse(url
).query
)
693 self
.assertEqual(query_dict(update_url_query(
694 'http://example.com/path', {'quality': ['HD'], 'format': ['mp4']}
)),
695 query_dict('http://example.com/path?quality=HD&format=mp4'))
696 self
.assertEqual(query_dict(update_url_query(
697 'http://example.com/path', {'system': ['LINUX', 'WINDOWS']}
)),
698 query_dict('http://example.com/path?system=LINUX&system=WINDOWS'))
699 self
.assertEqual(query_dict(update_url_query(
700 'http://example.com/path', {'fields': 'id,formats,subtitles'}
)),
701 query_dict('http://example.com/path?fields=id,formats,subtitles'))
702 self
.assertEqual(query_dict(update_url_query(
703 'http://example.com/path', {'fields': ('id,formats,subtitles', 'thumbnails')}
)),
704 query_dict('http://example.com/path?fields=id,formats,subtitles&fields=thumbnails'))
705 self
.assertEqual(query_dict(update_url_query(
706 'http://example.com/path?manifest=f4m', {'manifest': []}
)),
707 query_dict('http://example.com/path'))
708 self
.assertEqual(query_dict(update_url_query(
709 'http://example.com/path?system=LINUX&system=WINDOWS', {'system': 'LINUX'}
)),
710 query_dict('http://example.com/path?system=LINUX'))
711 self
.assertEqual(query_dict(update_url_query(
712 'http://example.com/path', {'fields': b'id,formats,subtitles'}
)),
713 query_dict('http://example.com/path?fields=id,formats,subtitles'))
714 self
.assertEqual(query_dict(update_url_query(
715 'http://example.com/path', {'width': 1080, 'height': 720}
)),
716 query_dict('http://example.com/path?width=1080&height=720'))
717 self
.assertEqual(query_dict(update_url_query(
718 'http://example.com/path', {'bitrate': 5020.43}
)),
719 query_dict('http://example.com/path?bitrate=5020.43'))
720 self
.assertEqual(query_dict(update_url_query(
721 'http://example.com/path', {'test': '第二行тест'}
)),
722 query_dict('http://example.com/path?test=%E7%AC%AC%E4%BA%8C%E8%A1%8C%D1%82%D0%B5%D1%81%D1%82'))
724 def test_multipart_encode(self
):
726 multipart_encode({b'field': b'value'}
, boundary
='AAAAAA')[0],
727 b
'--AAAAAA\r\nContent-Disposition: form-data; name="field"\r\n\r\nvalue\r\n--AAAAAA--\r\n')
729 multipart_encode({'欄位'.encode('utf-8'): '值'.encode('utf-8')}
, boundary
='AAAAAA')[0],
730 b
'--AAAAAA\r\nContent-Disposition: form-data; name="\xe6\xac\x84\xe4\xbd\x8d"\r\n\r\n\xe5\x80\xbc\r\n--AAAAAA--\r\n')
732 ValueError, multipart_encode
, {b'field': b'value'}
, boundary
='value')
734 def test_dict_get(self
):
742 d
= FALSE_VALUES
.copy()
744 self
.assertEqual(dict_get(d
, 'a'), 42)
745 self
.assertEqual(dict_get(d
, 'b'), None)
746 self
.assertEqual(dict_get(d
, 'b', 42), 42)
747 self
.assertEqual(dict_get(d
, ('a', )), 42)
748 self
.assertEqual(dict_get(d
, ('b', 'a', )), 42)
749 self
.assertEqual(dict_get(d
, ('b', 'c', 'a', 'd', )), 42)
750 self
.assertEqual(dict_get(d
, ('b', 'c', )), None)
751 self
.assertEqual(dict_get(d
, ('b', 'c', ), 42), 42)
752 for key
, false_value
in FALSE_VALUES
.items():
753 self
.assertEqual(dict_get(d
, ('b', 'c', key
, )), None)
754 self
.assertEqual(dict_get(d
, ('b', 'c', key
, ), skip_false_values
=False), false_value
)
756 def test_merge_dicts(self
):
757 self
.assertEqual(merge_dicts({'a': 1}
, {'b': 2}
), {'a': 1, 'b': 2}
)
758 self
.assertEqual(merge_dicts({'a': 1}
, {'a': 2}
), {'a': 1}
)
759 self
.assertEqual(merge_dicts({'a': 1}
, {'a': None}
), {'a': 1}
)
760 self
.assertEqual(merge_dicts({'a': 1}
, {'a': ''}
), {'a': 1}
)
761 self
.assertEqual(merge_dicts({'a': 1}
, {}), {'a': 1}
)
762 self
.assertEqual(merge_dicts({'a': None}
, {'a': 1}
), {'a': 1}
)
763 self
.assertEqual(merge_dicts({'a': ''}
, {'a': 1}
), {'a': ''}
)
764 self
.assertEqual(merge_dicts({'a': ''}
, {'a': 'abc'}
), {'a': 'abc'}
)
765 self
.assertEqual(merge_dicts({'a': None}
, {'a': ''}
, {'a': 'abc'}
), {'a': 'abc'}
)
767 def test_encode_compat_str(self
):
768 self
.assertEqual(encode_compat_str(b
'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82', 'utf-8'), 'тест')
769 self
.assertEqual(encode_compat_str('тест', 'utf-8'), 'тест')
771 def test_parse_iso8601(self
):
772 self
.assertEqual(parse_iso8601('2014-03-23T23:04:26+0100'), 1395612266)
773 self
.assertEqual(parse_iso8601('2014-03-23T22:04:26+0000'), 1395612266)
774 self
.assertEqual(parse_iso8601('2014-03-23T22:04:26Z'), 1395612266)
775 self
.assertEqual(parse_iso8601('2014-03-23T22:04:26.1234Z'), 1395612266)
776 self
.assertEqual(parse_iso8601('2015-09-29T08:27:31.727'), 1443515251)
777 self
.assertEqual(parse_iso8601('2015-09-29T08-27-31.727'), None)
779 def test_strip_jsonp(self
):
780 stripped
= strip_jsonp('cb ([ {"id":"532cb",\n\n\n"x":\n3}\n]\n);')
781 d
= json
.loads(stripped
)
782 self
.assertEqual(d
, [{"id": "532cb", "x": 3}
])
784 stripped
= strip_jsonp('parseMetadata({"STATUS":"OK"})\n\n\n//epc')
785 d
= json
.loads(stripped
)
786 self
.assertEqual(d
, {'STATUS': 'OK'}
)
788 stripped
= strip_jsonp('ps.embedHandler({"status": "success"});')
789 d
= json
.loads(stripped
)
790 self
.assertEqual(d
, {'status': 'success'}
)
792 stripped
= strip_jsonp('window.cb && window.cb({"status": "success"});')
793 d
= json
.loads(stripped
)
794 self
.assertEqual(d
, {'status': 'success'}
)
796 stripped
= strip_jsonp('window.cb && cb({"status": "success"});')
797 d
= json
.loads(stripped
)
798 self
.assertEqual(d
, {'status': 'success'}
)
800 stripped
= strip_jsonp('({"status": "success"});')
801 d
= json
.loads(stripped
)
802 self
.assertEqual(d
, {'status': 'success'}
)
804 def test_strip_or_none(self
):
805 self
.assertEqual(strip_or_none(' abc'), 'abc')
806 self
.assertEqual(strip_or_none('abc '), 'abc')
807 self
.assertEqual(strip_or_none(' abc '), 'abc')
808 self
.assertEqual(strip_or_none('\tabc\t'), 'abc')
809 self
.assertEqual(strip_or_none('\n\tabc\n\t'), 'abc')
810 self
.assertEqual(strip_or_none('abc'), 'abc')
811 self
.assertEqual(strip_or_none(''), '')
812 self
.assertEqual(strip_or_none(None), None)
813 self
.assertEqual(strip_or_none(42), None)
814 self
.assertEqual(strip_or_none([]), None)
816 def test_uppercase_escape(self
):
817 self
.assertEqual(uppercase_escape('aä'), 'aä')
818 self
.assertEqual(uppercase_escape('\\U0001d550'), '𝕐')
820 def test_lowercase_escape(self
):
821 self
.assertEqual(lowercase_escape('aä'), 'aä')
822 self
.assertEqual(lowercase_escape('\\u0026'), '&')
824 def test_limit_length(self
):
825 self
.assertEqual(limit_length(None, 12), None)
826 self
.assertEqual(limit_length('foo', 12), 'foo')
828 limit_length('foo bar baz asd', 12).startswith('foo bar'))
829 self
.assertTrue('...' in limit_length('foo bar baz asd', 12))
831 def test_mimetype2ext(self
):
832 self
.assertEqual(mimetype2ext(None), None)
833 self
.assertEqual(mimetype2ext('video/x-flv'), 'flv')
834 self
.assertEqual(mimetype2ext('application/x-mpegURL'), 'm3u8')
835 self
.assertEqual(mimetype2ext('text/vtt'), 'vtt')
836 self
.assertEqual(mimetype2ext('text/vtt;charset=utf-8'), 'vtt')
837 self
.assertEqual(mimetype2ext('text/html; charset=utf-8'), 'html')
838 self
.assertEqual(mimetype2ext('audio/x-wav'), 'wav')
839 self
.assertEqual(mimetype2ext('audio/x-wav;codec=pcm'), 'wav')
841 def test_month_by_name(self
):
842 self
.assertEqual(month_by_name(None), None)
843 self
.assertEqual(month_by_name('December', 'en'), 12)
844 self
.assertEqual(month_by_name('décembre', 'fr'), 12)
845 self
.assertEqual(month_by_name('December'), 12)
846 self
.assertEqual(month_by_name('décembre'), None)
847 self
.assertEqual(month_by_name('Unknown', 'unknown'), None)
849 def test_parse_codecs(self
):
850 self
.assertEqual(parse_codecs(''), {})
851 self
.assertEqual(parse_codecs('avc1.77.30, mp4a.40.2'), {
852 'vcodec': 'avc1.77.30',
853 'acodec': 'mp4a.40.2',
855 self
.assertEqual(parse_codecs('mp4a.40.2'), {
857 'acodec': 'mp4a.40.2',
859 self
.assertEqual(parse_codecs('mp4a.40.5,avc1.42001e'), {
860 'vcodec': 'avc1.42001e',
861 'acodec': 'mp4a.40.5',
863 self
.assertEqual(parse_codecs('avc3.640028'), {
864 'vcodec': 'avc3.640028',
867 self
.assertEqual(parse_codecs(', h264,,newcodec,aac'), {
871 self
.assertEqual(parse_codecs('av01.0.05M.08'), {
872 'vcodec': 'av01.0.05M.08',
875 self
.assertEqual(parse_codecs('theora, vorbis'), {
879 self
.assertEqual(parse_codecs('unknownvcodec, unknownacodec'), {
880 'vcodec': 'unknownvcodec',
881 'acodec': 'unknownacodec',
883 self
.assertEqual(parse_codecs('unknown'), {})
885 def test_escape_rfc3986(self
):
886 reserved
= "!*'();:@&=+$,/?#[]"
887 unreserved
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~'
888 self
.assertEqual(escape_rfc3986(reserved
), reserved
)
889 self
.assertEqual(escape_rfc3986(unreserved
), unreserved
)
890 self
.assertEqual(escape_rfc3986('тест'), '%D1%82%D0%B5%D1%81%D1%82')
891 self
.assertEqual(escape_rfc3986('%D1%82%D0%B5%D1%81%D1%82'), '%D1%82%D0%B5%D1%81%D1%82')
892 self
.assertEqual(escape_rfc3986('foo bar'), 'foo%20bar')
893 self
.assertEqual(escape_rfc3986('foo%20bar'), 'foo%20bar')
895 def test_escape_url(self
):
897 escape_url('http://wowza.imust.org/srv/vod/telemb/new/UPLOAD/UPLOAD/20224_IncendieHavré_FD.mp4'),
898 'http://wowza.imust.org/srv/vod/telemb/new/UPLOAD/UPLOAD/20224_IncendieHavre%CC%81_FD.mp4'
901 escape_url('http://www.ardmediathek.de/tv/Sturm-der-Liebe/Folge-2036-Zu-Mann-und-Frau-erklärt/Das-Erste/Video?documentId=22673108&bcastId=5290'),
902 'http://www.ardmediathek.de/tv/Sturm-der-Liebe/Folge-2036-Zu-Mann-und-Frau-erkl%C3%A4rt/Das-Erste/Video?documentId=22673108&bcastId=5290'
905 escape_url('http://тест.рф/фрагмент'),
906 'http://xn--e1aybc.xn--p1ai/%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82'
909 escape_url('http://тест.рф/абв?абв=абв#абв'),
910 'http://xn--e1aybc.xn--p1ai/%D0%B0%D0%B1%D0%B2?%D0%B0%D0%B1%D0%B2=%D0%B0%D0%B1%D0%B2#%D0%B0%D0%B1%D0%B2'
912 self
.assertEqual(escape_url('http://vimeo.com/56015672#at=0'), 'http://vimeo.com/56015672#at=0')
914 def test_js_to_json_realworld(self
):
916 'clip':{'provider':'pseudo'}
918 self
.assertEqual(js_to_json(inp
), '''{
919 "clip":{"provider":"pseudo"}
921 json
.loads(js_to_json(inp
))
924 'playlist':[{'controls':{'all':null}}]
926 self
.assertEqual(js_to_json(inp
), '''{
927 "playlist":[{"controls":{"all":null}}]
930 inp
= '''"The CW\\'s \\'Crazy Ex-Girlfriend\\'"'''
931 self
.assertEqual(js_to_json(inp
), '''"The CW's 'Crazy Ex-Girlfriend'"''')
933 inp
= '"SAND Number: SAND 2013-7800P\\nPresenter: Tom Russo\\nHabanero Software Training - Xyce Software\\nXyce, Sandia\\u0027s"'
934 json_code
= js_to_json(inp
)
935 self
.assertEqual(json
.loads(json_code
), json
.loads(inp
))
938 0:{src:'skipped', type: 'application/dash+xml'},
939 1:{src:'skipped', type: 'application/vnd.apple.mpegURL'},
941 self
.assertEqual(js_to_json(inp
), '''{
942 "0":{"src":"skipped", "type": "application/dash+xml"},
943 "1":{"src":"skipped", "type": "application/vnd.apple.mpegURL"}
946 inp
= '''{"foo":101}'''
947 self
.assertEqual(js_to_json(inp
), '''{"foo":101}''')
949 inp
= '''{"duration": "00:01:07"}'''
950 self
.assertEqual(js_to_json(inp
), '''{"duration": "00:01:07"}''')
952 inp
= '''{segments: [{"offset":-3.885780586188048e-16,"duration":39.75000000000001}]}'''
953 self
.assertEqual(js_to_json(inp
), '''{"segments": [{"offset":-3.885780586188048e-16,"duration":39.75000000000001}]}''')
955 def test_js_to_json_edgecases(self
):
956 on
= js_to_json("{abc_def:'1\\'\\\\2\\\\\\'3\"4'}")
957 self
.assertEqual(json
.loads(on
), {"abc_def": "1'\\2\\'3\"4"}
)
959 on
= js_to_json('{"abc": true}')
960 self
.assertEqual(json
.loads(on
), {'abc': True}
)
962 # Ignore JavaScript code as well
969 self
.assertEqual(d
['x'], 1)
970 self
.assertEqual(d
['y'], 'a')
972 # Just drop ! prefix for now though this results in a wrong value
983 self
.assertEqual(json
.loads(on
), {
994 on
= js_to_json('["abc", "def",]')
995 self
.assertEqual(json
.loads(on
), ['abc', 'def'])
997 on
= js_to_json('[/*comment\n*/"abc"/*comment\n*/,/*comment\n*/"def",/*comment\n*/]')
998 self
.assertEqual(json
.loads(on
), ['abc', 'def'])
1000 on
= js_to_json('[//comment\n"abc" //comment\n,//comment\n"def",//comment\n]')
1001 self
.assertEqual(json
.loads(on
), ['abc', 'def'])
1003 on
= js_to_json('{"abc": "def",}')
1004 self
.assertEqual(json
.loads(on
), {'abc': 'def'}
)
1006 on
= js_to_json('{/*comment\n*/"abc"/*comment\n*/:/*comment\n*/"def"/*comment\n*/,/*comment\n*/}')
1007 self
.assertEqual(json
.loads(on
), {'abc': 'def'}
)
1009 on
= js_to_json('{ 0: /* " \n */ ",]" , }')
1010 self
.assertEqual(json
.loads(on
), {'0': ',]'}
)
1012 on
= js_to_json('{ /*comment\n*/0/*comment\n*/: /* " \n */ ",]" , }')
1013 self
.assertEqual(json
.loads(on
), {'0': ',]'}
)
1015 on
= js_to_json('{ 0: // comment\n1 }')
1016 self
.assertEqual(json
.loads(on
), {'0': 1}
)
1018 on
= js_to_json(r
'["<p>x<\/p>"]')
1019 self
.assertEqual(json
.loads(on
), ['<p>x</p>'])
1021 on
= js_to_json(r
'["\xaa"]')
1022 self
.assertEqual(json
.loads(on
), ['\u00aa'])
1024 on
= js_to_json("['a\\\nb']")
1025 self
.assertEqual(json
.loads(on
), ['ab'])
1027 on
= js_to_json("/*comment\n*/[/*comment\n*/'a\\\nb'/*comment\n*/]/*comment\n*/")
1028 self
.assertEqual(json
.loads(on
), ['ab'])
1030 on
= js_to_json('{0xff:0xff}')
1031 self
.assertEqual(json
.loads(on
), {'255': 255}
)
1033 on
= js_to_json('{/*comment\n*/0xff/*comment\n*/:/*comment\n*/0xff/*comment\n*/}')
1034 self
.assertEqual(json
.loads(on
), {'255': 255}
)
1036 on
= js_to_json('{077:077}')
1037 self
.assertEqual(json
.loads(on
), {'63': 63}
)
1039 on
= js_to_json('{/*comment\n*/077/*comment\n*/:/*comment\n*/077/*comment\n*/}')
1040 self
.assertEqual(json
.loads(on
), {'63': 63}
)
1042 on
= js_to_json('{42:42}')
1043 self
.assertEqual(json
.loads(on
), {'42': 42}
)
1045 on
= js_to_json('{/*comment\n*/42/*comment\n*/:/*comment\n*/42/*comment\n*/}')
1046 self
.assertEqual(json
.loads(on
), {'42': 42}
)
1048 on
= js_to_json('{42:4.2e1}')
1049 self
.assertEqual(json
.loads(on
), {'42': 42.0}
)
1051 on
= js_to_json('{ "0x40": "0x40" }')
1052 self
.assertEqual(json
.loads(on
), {'0x40': '0x40'}
)
1054 on
= js_to_json('{ "040": "040" }')
1055 self
.assertEqual(json
.loads(on
), {'040': '040'}
)
1057 on
= js_to_json('[1,//{},\n2]')
1058 self
.assertEqual(json
.loads(on
), [1, 2])
1060 def test_js_to_json_malformed(self
):
1061 self
.assertEqual(js_to_json('42a1'), '42"a1"')
1062 self
.assertEqual(js_to_json('42a-1'), '42"a"-1')
1064 def test_extract_attributes(self
):
1065 self
.assertEqual(extract_attributes('<e x="y">'), {'x': 'y'}
)
1066 self
.assertEqual(extract_attributes("<e x='y'>"), {'x': 'y'}
)
1067 self
.assertEqual(extract_attributes('<e x=y>'), {'x': 'y'}
)
1068 self
.assertEqual(extract_attributes('<e x="a \'b\' c">'), {'x': "a 'b' c"}
)
1069 self
.assertEqual(extract_attributes('<e x=\'a "b" c\'>'), {'x': 'a "b" c'}
)
1070 self
.assertEqual(extract_attributes('<e x="y">'), {'x': 'y'}
)
1071 self
.assertEqual(extract_attributes('<e x="y">'), {'x': 'y'}
)
1072 self
.assertEqual(extract_attributes('<e x="&">'), {'x': '&'}
) # XML
1073 self
.assertEqual(extract_attributes('<e x=""">'), {'x': '"'}
)
1074 self
.assertEqual(extract_attributes('<e x="£">'), {'x': '£'}
) # HTML 3.2
1075 self
.assertEqual(extract_attributes('<e x="λ">'), {'x': 'λ'}
) # HTML 4.0
1076 self
.assertEqual(extract_attributes('<e x="&foo">'), {'x': '&foo'}
)
1077 self
.assertEqual(extract_attributes('<e x="\'">'), {'x': "'"}
)
1078 self
.assertEqual(extract_attributes('<e x=\'"\'>'), {'x': '"'}
)
1079 self
.assertEqual(extract_attributes('<e x >'), {'x': None}
)
1080 self
.assertEqual(extract_attributes('<e x=y a>'), {'x': 'y', 'a': None}
)
1081 self
.assertEqual(extract_attributes('<e x= y>'), {'x': 'y'}
)
1082 self
.assertEqual(extract_attributes('<e x=1 y=2 x=3>'), {'y': '2', 'x': '3'}
)
1083 self
.assertEqual(extract_attributes('<e \nx=\ny\n>'), {'x': 'y'}
)
1084 self
.assertEqual(extract_attributes('<e \nx=\n"y"\n>'), {'x': 'y'}
)
1085 self
.assertEqual(extract_attributes("<e \nx=\n'y'\n>"), {'x': 'y'}
)
1086 self
.assertEqual(extract_attributes('<e \nx="\ny\n">'), {'x': '\ny\n'}
)
1087 self
.assertEqual(extract_attributes('<e CAPS=x>'), {'caps': 'x'}
) # Names lowercased
1088 self
.assertEqual(extract_attributes('<e x=1 X=2>'), {'x': '2'}
)
1089 self
.assertEqual(extract_attributes('<e X=1 x=2>'), {'x': '2'}
)
1090 self
.assertEqual(extract_attributes('<e _:funny-name1=1>'), {'_:funny-name1': '1'}
)
1091 self
.assertEqual(extract_attributes('<e x="Fáilte 世界 \U0001f600">'), {'x': 'Fáilte 世界 \U0001f600'}
)
1092 self
.assertEqual(extract_attributes('<e x="décomposé">'), {'x': 'décompose\u0301'}
)
1093 # "Narrow" Python builds don't support unicode code points outside BMP.
1096 supports_outside_bmp
= True
1098 supports_outside_bmp
= False
1099 if supports_outside_bmp
:
1100 self
.assertEqual(extract_attributes('<e x="Smile 😀!">'), {'x': 'Smile \U0001f600!'}
)
1101 # Malformed HTML should not break attributes extraction on older Python
1102 self
.assertEqual(extract_attributes('<mal"formed/>'), {})
1104 def test_clean_html(self
):
1105 self
.assertEqual(clean_html('a:\nb'), 'a: b')
1106 self
.assertEqual(clean_html('a:\n "b"'), 'a: "b"')
1107 self
.assertEqual(clean_html('a<br>\xa0b'), 'a\nb')
1109 def test_intlist_to_bytes(self
):
1111 intlist_to_bytes([0, 1, 127, 128, 255]),
1112 b
'\x00\x01\x7f\x80\xff')
1114 def test_args_to_str(self
):
1116 args_to_str(['foo', 'ba/r', '-baz', '2 be', '']),
1117 'foo ba/r -baz \'2 be\' \'\'' if compat_os_name
!= 'nt' else 'foo ba/r -baz "2 be" ""'
1120 def test_parse_filesize(self
):
1121 self
.assertEqual(parse_filesize(None), None)
1122 self
.assertEqual(parse_filesize(''), None)
1123 self
.assertEqual(parse_filesize('91 B'), 91)
1124 self
.assertEqual(parse_filesize('foobar'), None)
1125 self
.assertEqual(parse_filesize('2 MiB'), 2097152)
1126 self
.assertEqual(parse_filesize('5 GB'), 5000000000)
1127 self
.assertEqual(parse_filesize('1.2Tb'), 1200000000000)
1128 self
.assertEqual(parse_filesize('1.2tb'), 1200000000000)
1129 self
.assertEqual(parse_filesize('1,24 KB'), 1240)
1130 self
.assertEqual(parse_filesize('1,24 kb'), 1240)
1131 self
.assertEqual(parse_filesize('8.5 megabytes'), 8500000)
1133 def test_parse_count(self
):
1134 self
.assertEqual(parse_count(None), None)
1135 self
.assertEqual(parse_count(''), None)
1136 self
.assertEqual(parse_count('0'), 0)
1137 self
.assertEqual(parse_count('1000'), 1000)
1138 self
.assertEqual(parse_count('1.000'), 1000)
1139 self
.assertEqual(parse_count('1.1k'), 1100)
1140 self
.assertEqual(parse_count('1.1kk'), 1100000)
1141 self
.assertEqual(parse_count('1.1kk '), 1100000)
1142 self
.assertEqual(parse_count('1.1kk views'), 1100000)
1144 def test_parse_resolution(self
):
1145 self
.assertEqual(parse_resolution(None), {})
1146 self
.assertEqual(parse_resolution(''), {})
1147 self
.assertEqual(parse_resolution('1920x1080'), {'width': 1920, 'height': 1080}
)
1148 self
.assertEqual(parse_resolution('1920×1080'), {'width': 1920, 'height': 1080}
)
1149 self
.assertEqual(parse_resolution('1920 x 1080'), {'width': 1920, 'height': 1080}
)
1150 self
.assertEqual(parse_resolution('720p'), {'height': 720}
)
1151 self
.assertEqual(parse_resolution('4k'), {'height': 2160}
)
1152 self
.assertEqual(parse_resolution('8K'), {'height': 4320}
)
1154 def test_parse_bitrate(self
):
1155 self
.assertEqual(parse_bitrate(None), None)
1156 self
.assertEqual(parse_bitrate(''), None)
1157 self
.assertEqual(parse_bitrate('300kbps'), 300)
1158 self
.assertEqual(parse_bitrate('1500kbps'), 1500)
1159 self
.assertEqual(parse_bitrate('300 kbps'), 300)
1161 def test_version_tuple(self
):
1162 self
.assertEqual(version_tuple('1'), (1,))
1163 self
.assertEqual(version_tuple('10.23.344'), (10, 23, 344))
1164 self
.assertEqual(version_tuple('10.1-6'), (10, 1, 6)) # avconv style
1166 def test_detect_exe_version(self
):
1167 self
.assertEqual(detect_exe_version('''ffmpeg version 1.2.1
1168 built on May 27 2013 08:37:26 with gcc 4.7 (Debian 4.7.3-4)
1169 configuration: --prefix=/usr --extra-'''), '1.2.1')
1170 self
.assertEqual(detect_exe_version('''ffmpeg version N-63176-g1fb4685
1171 built on May 15 2014 22:09:06 with gcc 4.8.2 (GCC)'''), 'N-63176-g1fb4685')
1172 self
.assertEqual(detect_exe_version('''X server found. dri2 connection failed!
1173 Trying to open render node...
1174 Success at /dev/dri/renderD128.
1175 ffmpeg version 2.4.4 Copyright (c) 2000-2014 the FFmpeg ...'''), '2.4.4')
1177 def test_age_restricted(self
):
1178 self
.assertFalse(age_restricted(None, 10)) # unrestricted content
1179 self
.assertFalse(age_restricted(1, None)) # unrestricted policy
1180 self
.assertFalse(age_restricted(8, 10))
1181 self
.assertTrue(age_restricted(18, 14))
1182 self
.assertFalse(age_restricted(18, 18))
1184 def test_is_html(self
):
1185 self
.assertFalse(is_html(b
'\x49\x44\x43<html'))
1186 self
.assertTrue(is_html(b
'<!DOCTYPE foo>\xaaa'))
1187 self
.assertTrue(is_html( # UTF-8 with BOM
1188 b
'\xef\xbb\xbf<!DOCTYPE foo>\xaaa'))
1189 self
.assertTrue(is_html( # UTF-16-LE
1190 b
'\xff\xfe<\x00h\x00t\x00m\x00l\x00>\x00\xe4\x00'
1192 self
.assertTrue(is_html( # UTF-16-BE
1193 b
'\xfe\xff\x00<\x00h\x00t\x00m\x00l\x00>\x00\xe4'
1195 self
.assertTrue(is_html( # UTF-32-BE
1196 b
'\x00\x00\xFE\xFF\x00\x00\x00<\x00\x00\x00h\x00\x00\x00t\x00\x00\x00m\x00\x00\x00l\x00\x00\x00>\x00\x00\x00\xe4'))
1197 self
.assertTrue(is_html( # UTF-32-LE
1198 b
'\xFF\xFE\x00\x00<\x00\x00\x00h\x00\x00\x00t\x00\x00\x00m\x00\x00\x00l\x00\x00\x00>\x00\x00\x00\xe4\x00\x00\x00'))
1200 def test_render_table(self
):
1204 [[123, 4], [9999, 51]]),
1209 def test_match_str(self
):
1211 self
.assertFalse(match_str('xy', {'x': 1200}
))
1212 self
.assertTrue(match_str('!xy', {'x': 1200}
))
1213 self
.assertTrue(match_str('x', {'x': 1200}
))
1214 self
.assertFalse(match_str('!x', {'x': 1200}
))
1215 self
.assertTrue(match_str('x', {'x': 0}
))
1216 self
.assertTrue(match_str('is_live', {'is_live': True}
))
1217 self
.assertFalse(match_str('is_live', {'is_live': False}
))
1218 self
.assertFalse(match_str('is_live', {'is_live': None}
))
1219 self
.assertFalse(match_str('is_live', {}))
1220 self
.assertFalse(match_str('!is_live', {'is_live': True}
))
1221 self
.assertTrue(match_str('!is_live', {'is_live': False}
))
1222 self
.assertTrue(match_str('!is_live', {'is_live': None}
))
1223 self
.assertTrue(match_str('!is_live', {}))
1224 self
.assertTrue(match_str('title', {'title': 'abc'}
))
1225 self
.assertTrue(match_str('title', {'title': ''}
))
1226 self
.assertFalse(match_str('!title', {'title': 'abc'}
))
1227 self
.assertFalse(match_str('!title', {'title': ''}
))
1230 self
.assertFalse(match_str('x>0', {'x': 0}
))
1231 self
.assertFalse(match_str('x>0', {}))
1232 self
.assertTrue(match_str('x>?0', {}))
1233 self
.assertTrue(match_str('x>1K', {'x': 1200}
))
1234 self
.assertFalse(match_str('x>2K', {'x': 1200}
))
1235 self
.assertTrue(match_str('x>=1200 & x < 1300', {'x': 1200}
))
1236 self
.assertFalse(match_str('x>=1100 & x < 1200', {'x': 1200}
))
1239 self
.assertFalse(match_str('y=a212', {'y': 'foobar42'}
))
1240 self
.assertTrue(match_str('y=foobar42', {'y': 'foobar42'}
))
1241 self
.assertFalse(match_str('y!=foobar42', {'y': 'foobar42'}
))
1242 self
.assertTrue(match_str('y!=foobar2', {'y': 'foobar42'}
))
1243 self
.assertTrue(match_str('y^=foo', {'y': 'foobar42'}
))
1244 self
.assertFalse(match_str('y!^=foo', {'y': 'foobar42'}
))
1245 self
.assertFalse(match_str('y^=bar', {'y': 'foobar42'}
))
1246 self
.assertTrue(match_str('y!^=bar', {'y': 'foobar42'}
))
1247 self
.assertRaises(ValueError, match_str
, 'x^=42', {'x': 42}
)
1248 self
.assertTrue(match_str('y*=bar', {'y': 'foobar42'}
))
1249 self
.assertFalse(match_str('y!*=bar', {'y': 'foobar42'}
))
1250 self
.assertFalse(match_str('y*=baz', {'y': 'foobar42'}
))
1251 self
.assertTrue(match_str('y!*=baz', {'y': 'foobar42'}
))
1252 self
.assertTrue(match_str('y$=42', {'y': 'foobar42'}
))
1253 self
.assertFalse(match_str('y$=43', {'y': 'foobar42'}
))
1256 self
.assertFalse(match_str(
1257 'like_count > 100 & dislike_count <? 50 & description',
1258 {'like_count': 90, 'description': 'foo'}
))
1259 self
.assertTrue(match_str(
1260 'like_count > 100 & dislike_count <? 50 & description',
1261 {'like_count': 190, 'description': 'foo'}
))
1262 self
.assertFalse(match_str(
1263 'like_count > 100 & dislike_count <? 50 & description',
1264 {'like_count': 190, 'dislike_count': 60, 'description': 'foo'}
))
1265 self
.assertFalse(match_str(
1266 'like_count > 100 & dislike_count <? 50 & description',
1267 {'like_count': 190, 'dislike_count': 10}
))
1270 self
.assertTrue(match_str(r
'x~=\bbar', {'x': 'foo bar'}
))
1271 self
.assertFalse(match_str(r
'x~=\bbar.+', {'x': 'foo bar'}
))
1272 self
.assertFalse(match_str(r
'x~=^FOO', {'x': 'foo bar'}
))
1273 self
.assertTrue(match_str(r
'x~=(?i)^FOO', {'x': 'foo bar'}
))
1276 self
.assertTrue(match_str(r
'x^="foo"', {'x': 'foo "bar"'}
))
1277 self
.assertFalse(match_str(r
'x^="foo "', {'x': 'foo "bar"'}
))
1278 self
.assertFalse(match_str(r
'x$="bar"', {'x': 'foo "bar"'}
))
1279 self
.assertTrue(match_str(r
'x$=" \"bar\""', {'x': 'foo "bar"'}
))
1282 self
.assertFalse(match_str(r
'x=foo & bar', {'x': 'foo & bar'}
))
1283 self
.assertTrue(match_str(r
'x=foo \& bar', {'x': 'foo & bar'}
))
1284 self
.assertTrue(match_str(r
'x=foo \& bar & x^=foo', {'x': 'foo & bar'}
))
1285 self
.assertTrue(match_str(r
'x="foo \& bar" & x^=foo', {'x': 'foo & bar'}
))
1289 r
'!is_live & like_count>?100 & description~=\'(?i
)\bcats \
& dogs
\b\'',
1290 {'description': 'Raining Cats & Dogs'})
1292 def test_parse_dfxp_time_expr(self):
1293 self.assertEqual(parse_dfxp_time_expr(None), None)
1294 self.assertEqual(parse_dfxp_time_expr(''), None)
1295 self.assertEqual(parse_dfxp_time_expr('0.1'), 0.1)
1296 self.assertEqual(parse_dfxp_time_expr('0.1s
'), 0.1)
1297 self.assertEqual(parse_dfxp_time_expr('00:00:01'), 1.0)
1298 self.assertEqual(parse_dfxp_time_expr('00:00:01.100'), 1.1)
1299 self.assertEqual(parse_dfxp_time_expr('00:00:01:100'), 1.1)
1301 def test_dfxp2srt(self):
1302 dfxp_data = '''<?xml version="1.0" encoding="UTF-8"?>
1303 <tt xmlns="http://www.w3.org/ns/ttml" xml:lang="en" xmlns:tts="http://www.w3.org/ns/ttml#parameter">
1306 <p begin="0" end="1">The following line contains Chinese characters and special symbols</p>
1307 <p begin="1" end="2">第二行<br/>♪♪</p>
1308 <p begin="2" dur="1"><span>Third<br/>Line</span></p>
1309 <p begin="3" end="-1">Lines with invalid timestamps are ignored</p>
1310 <p begin="-1" end="-1">Ignore, two</p>
1311 <p begin="3" dur="-1">Ignored, three</p>
1314 </tt>'''.encode('utf
-8')
1316 00:00:00,000 --> 00:00:01,000
1317 The following line contains Chinese characters and special symbols
1320 00:00:01,000 --> 00:00:02,000
1325 00:00:02,000 --> 00:00:03,000
1330 self.assertEqual(dfxp2srt(dfxp_data), srt_data)
1332 dfxp_data_no_default_namespace = '''<?xml version="1.0" encoding="UTF-8"?>
1333 <tt xml:lang="en" xmlns:tts="http://www.w3.org/ns/ttml#parameter">
1336 <p begin="0" end="1">The first line</p>
1339 </tt>'''.encode('utf
-8')
1341 00:00:00,000 --> 00:00:01,000
1345 self.assertEqual(dfxp2srt(dfxp_data_no_default_namespace), srt_data)
1347 dfxp_data_with_style = '''<?xml version="1.0" encoding="utf-8"?>
1348 <tt xmlns="http://www.w3.org/2006/10/ttaf1" xmlns:ttp="http://www.w3.org/2006/10/ttaf1#parameter" ttp:timeBase="media" xmlns:tts="http://www.w3.org/2006/10/ttaf1#style" xml:lang="en" xmlns:ttm="http://www.w3.org/2006/10/ttaf1#metadata">
1351 <style id="s2" style="s0" tts:color="cyan" tts:fontWeight="bold" />
1352 <style id="s1" style="s0" tts:color="yellow" tts:fontStyle="italic" />
1353 <style id="s3" style="s0" tts:color="lime" tts:textDecoration="underline" />
1354 <style id="s0" tts:backgroundColor="black" tts:fontStyle="normal" tts:fontSize="16" tts:fontFamily="sansSerif" tts:color="white" />
1357 <body tts:textAlign="center" style="s0">
1359 <p begin="00:00:02.08" id="p0" end="00:00:05.84">default style<span tts:color="red">custom style</span></p>
1360 <p style="s2" begin="00:00:02.08" id="p0" end="00:00:05.84"><span tts:color="lime">part 1<br /></span><span tts:color="cyan">part 2</span></p>
1361 <p style="s3" begin="00:00:05.84" id="p1" end="00:00:09.56">line 3<br />part 3</p>
1362 <p style="s1" tts:textDecoration="underline" begin="00:00:09.56" id="p2" end="00:00:12.36"><span style="s2" tts:color="lime">inner<br /> </span>style</p>
1365 </tt>'''.encode('utf
-8')
1367 00:00:02,080 --> 00:00:05,839
1368 <font color="white" face="sansSerif" size="16">default style<font color="red">custom style</font></font>
1371 00:00:02,080 --> 00:00:05,839
1372 <b><font color="cyan" face="sansSerif" size="16"><font color="lime">part 1
1373 </font>part 2</font></b>
1376 00:00:05,839 --> 00:00:09,560
1377 <u><font color="lime">line 3
1381 00:00:09,560 --> 00:00:12,359
1382 <i><u><font color="yellow"><font color="lime">inner
1383 </font>style</font></u></i>
1386 self.assertEqual(dfxp2srt(dfxp_data_with_style), srt_data)
1388 dfxp_data_non_utf8 = '''<?xml version="1.0" encoding="UTF-16"?>
1389 <tt xmlns="http://www.w3.org/ns/ttml" xml:lang="en" xmlns:tts="http://www.w3.org/ns/ttml#parameter">
1392 <p begin="0" end="1">Line 1</p>
1393 <p begin="1" end="2">第二行</p>
1396 </tt>'''.encode('utf
-16')
1398 00:00:00,000 --> 00:00:01,000
1402 00:00:01,000 --> 00:00:02,000
1406 self.assertEqual(dfxp2srt(dfxp_data_non_utf8), srt_data)
1408 def test_cli_option(self):
1409 self.assertEqual(cli_option({'proxy': '127.0.0.1:3128'}, '--proxy
', 'proxy
'), ['--proxy
', '127.0.0.1:3128'])
1410 self.assertEqual(cli_option({'proxy': None}, '--proxy
', 'proxy
'), [])
1411 self.assertEqual(cli_option({}, '--proxy
', 'proxy
'), [])
1412 self.assertEqual(cli_option({'retries': 10}, '--retries
', 'retries
'), ['--retries
', '10'])
1414 def test_cli_valueless_option(self):
1415 self.assertEqual(cli_valueless_option(
1416 {'downloader': 'external'}, '--external
-downloader
', 'downloader
', 'external
'), ['--external
-downloader
'])
1417 self.assertEqual(cli_valueless_option(
1418 {'downloader': 'internal'}, '--external
-downloader
', 'downloader
', 'external
'), [])
1419 self.assertEqual(cli_valueless_option(
1420 {'nocheckcertificate': True}, '--no
-check
-certificate
', 'nocheckcertificate
'), ['--no
-check
-certificate
'])
1421 self.assertEqual(cli_valueless_option(
1422 {'nocheckcertificate': False}, '--no
-check
-certificate
', 'nocheckcertificate
'), [])
1423 self.assertEqual(cli_valueless_option(
1424 {'checkcertificate': True}, '--no
-check
-certificate
', 'checkcertificate
', False), [])
1425 self.assertEqual(cli_valueless_option(
1426 {'checkcertificate': False}, '--no
-check
-certificate
', 'checkcertificate
', False), ['--no
-check
-certificate
'])
1428 def test_cli_bool_option(self):
1431 {'nocheckcertificate': True}, '--no
-check
-certificate
', 'nocheckcertificate
'),
1432 ['--no
-check
-certificate
', 'true
'])
1435 {'nocheckcertificate': True}, '--no
-check
-certificate
', 'nocheckcertificate
', separator='='),
1436 ['--no
-check
-certificate
=true
'])
1439 {'nocheckcertificate': True}, '--check
-certificate
', 'nocheckcertificate
', 'false
', 'true
'),
1440 ['--check
-certificate
', 'false
'])
1443 {'nocheckcertificate': True}, '--check
-certificate
', 'nocheckcertificate
', 'false
', 'true
', '='),
1444 ['--check
-certificate
=false
'])
1447 {'nocheckcertificate': False}, '--check
-certificate
', 'nocheckcertificate
', 'false
', 'true
'),
1448 ['--check
-certificate
', 'true
'])
1451 {'nocheckcertificate': False}, '--check
-certificate
', 'nocheckcertificate
', 'false
', 'true
', '='),
1452 ['--check
-certificate
=true
'])
1455 {}, '--check
-certificate
', 'nocheckcertificate
', 'false
', 'true
', '='),
1458 def test_ohdave_rsa_encrypt(self):
1459 N = 0xab86b6371b5318aaa1d3c9e612a9f1264f372323c8c0f19875b5fc3b3fd3afcc1e5bec527aa94bfa85bffc157e4245aebda05389a5357b75115ac94f074aefcd
1463 ohdave_rsa_encrypt(b'aa111222
', e, N),
1464 '726664bd9a23fd0c70f9f1b84aab5e3905ce1e45a584e9cbcf9bcc7510338fc1986d6c599ff990d923aa43c51c0d9013cd572e13bc58f4ae48f2ed8c0b0ba881
')
1466 def test_pkcs1pad(self):
1468 padded_data = pkcs1pad(data, 32)
1469 self.assertEqual(padded_data[:2], [0, 2])
1470 self.assertEqual(padded_data[28:], [0, 1, 2, 3])
1472 self.assertRaises(ValueError, pkcs1pad, data, 8)
1474 def test_encode_base_n(self):
1475 self.assertEqual(encode_base_n(0, 30), '0')
1476 self.assertEqual(encode_base_n(80, 30), '2k
')
1478 custom_table = '9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA
'
1479 self.assertEqual(encode_base_n(0, 30, custom_table), '9')
1480 self.assertEqual(encode_base_n(80, 30, custom_table), '7P
')
1482 self.assertRaises(ValueError, encode_base_n, 0, 70)
1483 self.assertRaises(ValueError, encode_base_n, 0, 60, custom_table)
1485 def test_caesar(self):
1486 self.assertEqual(caesar('ace
', 'abcdef
', 2), 'cea
')
1487 self.assertEqual(caesar('cea
', 'abcdef
', -2), 'ace
')
1488 self.assertEqual(caesar('ace
', 'abcdef
', -2), 'eac
')
1489 self.assertEqual(caesar('eac
', 'abcdef
', 2), 'ace
')
1490 self.assertEqual(caesar('ace
', 'abcdef
', 0), 'ace
')
1491 self.assertEqual(caesar('xyz
', 'abcdef
', 2), 'xyz
')
1492 self.assertEqual(caesar('abc
', 'acegik
', 2), 'ebg
')
1493 self.assertEqual(caesar('ebg
', 'acegik
', -2), 'abc
')
1495 def test_rot47(self):
1496 self.assertEqual(rot47('yt
-dlp
'), r'JE\
5=A
')
1497 self.assertEqual(rot47('YT
-DLP
'), r'*%\s
{!')
1499 def test_urshift(self):
1500 self.assertEqual(urshift(3, 1), 1)
1501 self.assertEqual(urshift(-3, 1), 2147483646)
1503 def test_get_element_by_class(self):
1505 <span class="foo bar">nice</span>
1508 self.assertEqual(get_element_by_class('foo
', html), 'nice
')
1509 self.assertEqual(get_element_by_class('no
-such
-class', html), None)
1511 def test_get_element_by_attribute(self):
1513 <span class="foo bar">nice</span>
1516 self.assertEqual(get_element_by_attribute('class', 'foo bar
', html), 'nice
')
1517 self.assertEqual(get_element_by_attribute('class', 'foo
', html), None)
1518 self.assertEqual(get_element_by_attribute('class', 'no
-such
-foo
', html), None)
1521 <div itemprop="author" itemscope>foo</div>
1524 self.assertEqual(get_element_by_attribute('itemprop
', 'author
', html), 'foo
')
1526 def test_get_elements_by_class(self):
1528 <span class="foo bar">nice</span><span class="foo bar">also nice</span>
1531 self.assertEqual(get_elements_by_class('foo
', html), ['nice
', 'also nice
'])
1532 self.assertEqual(get_elements_by_class('no
-such
-class', html), [])
1534 def test_get_elements_by_attribute(self):
1536 <span class="foo bar">nice</span><span class="foo bar">also nice</span>
1539 self.assertEqual(get_elements_by_attribute('class', 'foo bar
', html), ['nice
', 'also nice
'])
1540 self.assertEqual(get_elements_by_attribute('class', 'foo
', html), [])
1541 self.assertEqual(get_elements_by_attribute('class', 'no
-such
-foo
', html), [])
1543 def test_iri_to_uri(self):
1545 iri_to_uri('https
://www
.google
.com
/search?q
=foo
&ie
=utf
-8&oe
=utf
-8&client
=firefox
-b
'),
1546 'https
://www
.google
.com
/search?q
=foo
&ie
=utf
-8&oe
=utf
-8&client
=firefox
-b
') # Same
1548 iri_to_uri('https
://www
.google
.com
/search?q
=Käsesoßenrührlöffel
'), # German for cheese sauce stirring spoon
1549 'https
://www
.google
.com
/search?q
=K
%C3
%A4seso
%C3
%9Fenr
%C3
%BChrl
%C3
%B6ffel
')
1551 iri_to_uri('https
://www
.google
.com
/search?q
=lt
<+gt
>+eq
%3D
+amp
%26+percent
%25+hash%23+colon
%3A
+tilde~
#trash=?&garbage=#'),
1552 'https://www.google.com/search?q=lt%3C+gt%3E+eq%3D+amp%26+percent%25+hash%23+colon%3A+tilde~#trash=?&garbage=#')
1554 iri_to_uri('http://правозащита38.рф/category/news/'),
1555 'http://xn--38-6kcaak9aj5chl4a3g.xn--p1ai/category/news/')
1557 iri_to_uri('http://www.правозащита38.рф/category/news/'),
1558 'http://www.xn--38-6kcaak9aj5chl4a3g.xn--p1ai/category/news/')
1560 iri_to_uri('https://i❤.ws/emojidomain/👍👏🤝💪'),
1561 'https://xn--i-7iq.ws/emojidomain/%F0%9F%91%8D%F0%9F%91%8F%F0%9F%A4%9D%F0%9F%92%AA')
1563 iri_to_uri('http://日本語.jp/'),
1564 'http://xn--wgv71a119e.jp/')
1566 iri_to_uri('http://导航.中国/'),
1567 'http://xn--fet810g.xn--fiqs8s/')
1569 def test_clean_podcast_url(self
):
1570 self
.assertEqual(clean_podcast_url('https://www.podtrac.com/pts/redirect.mp3/chtbl.com/track/5899E/traffic.megaphone.fm/HSW7835899191.mp3'), 'https://traffic.megaphone.fm/HSW7835899191.mp3')
1571 self
.assertEqual(clean_podcast_url('https://play.podtrac.com/npr-344098539/edge1.pod.npr.org/anon.npr-podcasts/podcast/npr/waitwait/2020/10/20201003_waitwait_wwdtmpodcast201003-015621a5-f035-4eca-a9a1-7c118d90bc3c.mp3'), 'https://edge1.pod.npr.org/anon.npr-podcasts/podcast/npr/waitwait/2020/10/20201003_waitwait_wwdtmpodcast201003-015621a5-f035-4eca-a9a1-7c118d90bc3c.mp3')
1573 def test_LazyList(self
):
1574 it
= list(range(10))
1576 self
.assertEqual(list(LazyList(it
)), it
)
1577 self
.assertEqual(LazyList(it
).exhaust(), it
)
1578 self
.assertEqual(LazyList(it
)[5], it
[5])
1580 self
.assertEqual(LazyList(it
)[5:], it
[5:])
1581 self
.assertEqual(LazyList(it
)[:5], it
[:5])
1582 self
.assertEqual(LazyList(it
)[::2], it
[::2])
1583 self
.assertEqual(LazyList(it
)[1::2], it
[1::2])
1584 self
.assertEqual(LazyList(it
)[5::-1], it
[5::-1])
1585 self
.assertEqual(LazyList(it
)[6:2:-2], it
[6:2:-2])
1586 self
.assertEqual(LazyList(it
)[::-1], it
[::-1])
1588 self
.assertTrue(LazyList(it
))
1589 self
.assertFalse(LazyList(range(0)))
1590 self
.assertEqual(len(LazyList(it
)), len(it
))
1591 self
.assertEqual(repr(LazyList(it
)), repr(it
))
1592 self
.assertEqual(str(LazyList(it
)), str(it
))
1594 self
.assertEqual(list(LazyList(it
).reverse()), it
[::-1])
1595 self
.assertEqual(list(LazyList(it
).reverse()[1:3:7]), it
[::-1][1:3:7])
1596 self
.assertEqual(list(LazyList(it
).reverse()[::-1]), it
)
1598 def test_LazyList_laziness(self
):
1600 def test(ll
, idx
, val
, cache
):
1601 self
.assertEqual(ll
[idx
], val
)
1602 self
.assertEqual(getattr(ll
, '_LazyList__cache'), list(cache
))
1604 ll
= LazyList(range(10))
1605 test(ll
, 0, 0, range(1))
1606 test(ll
, 5, 5, range(6))
1607 test(ll
, -3, 7, range(10))
1609 ll
= LazyList(range(10)).reverse()
1610 test(ll
, -1, 0, range(1))
1611 test(ll
, 3, 6, range(10))
1613 ll
= LazyList(itertools
.count())
1614 test(ll
, 10, 10, range(11))
1616 test(ll
, -15, 14, range(15))
1619 if __name__
== '__main__':