mirror of
https://gitlab.com/dstftw/youtube-dl.git
synced 2020-11-16 09:42:26 +00:00
Merge remote-tracking branch 'jtwaleson/master'
This commit is contained in:
commit
784b6d3a9b
|
@ -9,6 +9,7 @@ import youtube_dl
|
||||||
BASH_COMPLETION_FILE = "youtube-dl.bash-completion"
|
BASH_COMPLETION_FILE = "youtube-dl.bash-completion"
|
||||||
BASH_COMPLETION_TEMPLATE = "devscripts/bash-completion.in"
|
BASH_COMPLETION_TEMPLATE = "devscripts/bash-completion.in"
|
||||||
|
|
||||||
|
|
||||||
def build_completion(opt_parser):
|
def build_completion(opt_parser):
|
||||||
opts_flag = []
|
opts_flag = []
|
||||||
for group in opt_parser.option_groups:
|
for group in opt_parser.option_groups:
|
||||||
|
|
|
@ -233,6 +233,7 @@ def rmtree(path):
|
||||||
|
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
|
|
||||||
|
|
||||||
class BuildError(Exception):
|
class BuildError(Exception):
|
||||||
def __init__(self, output, code=500):
|
def __init__(self, output, code=500):
|
||||||
self.output = output
|
self.output = output
|
||||||
|
|
|
@ -23,6 +23,7 @@ EXTRA_ARGS = {
|
||||||
'batch-file': ['--require-parameter'],
|
'batch-file': ['--require-parameter'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def build_completion(opt_parser):
|
def build_completion(opt_parser):
|
||||||
commands = []
|
commands = []
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import hashlib
|
import hashlib
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import tempfile
|
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
|
@ -73,4 +73,3 @@ atom_template = atom_template.replace('@ENTRIES@', entries_str)
|
||||||
|
|
||||||
with io.open('update/releases.atom', 'w', encoding='utf-8') as atom_file:
|
with io.open('update/releases.atom', 'w', encoding='utf-8') as atom_file:
|
||||||
atom_file.write(atom_template)
|
atom_file.write(atom_template)
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(
|
||||||
|
|
||||||
import youtube_dl
|
import youtube_dl
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
with open('supportedsites.html.in', 'r', encoding='utf-8') as tmplf:
|
with open('supportedsites.html.in', 'r', encoding='utf-8') as tmplf:
|
||||||
template = tmplf.read()
|
template = tmplf.read()
|
||||||
|
|
|
@ -4,13 +4,17 @@ import sys, os
|
||||||
import urllib2
|
import urllib2
|
||||||
import json, hashlib
|
import json, hashlib
|
||||||
|
|
||||||
|
|
||||||
def rsa_verify(message, signature, key):
|
def rsa_verify(message, signature, key):
|
||||||
from struct import pack
|
from struct import pack
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from sys import version_info
|
from sys import version_info
|
||||||
|
|
||||||
def b(x):
|
def b(x):
|
||||||
if version_info[0] == 2: return x
|
if version_info[0] == 2:
|
||||||
else: return x.encode('latin1')
|
return x
|
||||||
|
else:
|
||||||
|
return x.encode('latin1')
|
||||||
assert(type(message) == type(b('')))
|
assert(type(message) == type(b('')))
|
||||||
block_size = 0
|
block_size = 0
|
||||||
n = key[0]
|
n = key[0]
|
||||||
|
@ -23,13 +27,17 @@ def rsa_verify(message, signature, key):
|
||||||
raw_bytes.insert(0, pack("B", signature & 0xFF))
|
raw_bytes.insert(0, pack("B", signature & 0xFF))
|
||||||
signature >>= 8
|
signature >>= 8
|
||||||
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
|
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
|
||||||
if signature[0:2] != b('\x00\x01'): return False
|
if signature[0:2] != b('\x00\x01'):
|
||||||
|
return False
|
||||||
signature = signature[2:]
|
signature = signature[2:]
|
||||||
if not b('\x00') in signature: return False
|
if not b('\x00') in signature:
|
||||||
|
return False
|
||||||
signature = signature[signature.index(b('\x00')) + 1:]
|
signature = signature[signature.index(b('\x00')) + 1:]
|
||||||
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False
|
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')):
|
||||||
|
return False
|
||||||
signature = signature[19:]
|
signature = signature[19:]
|
||||||
if signature != sha256(message).digest(): return False
|
if signature != sha256(message).digest():
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n')
|
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n')
|
||||||
|
|
|
@ -72,8 +72,10 @@ class FakeYDL(YoutubeDL):
|
||||||
def expect_warning(self, regex):
|
def expect_warning(self, regex):
|
||||||
# Silence an expected warning matching a regex
|
# Silence an expected warning matching a regex
|
||||||
old_report_warning = self.report_warning
|
old_report_warning = self.report_warning
|
||||||
|
|
||||||
def report_warning(self, message):
|
def report_warning(self, message):
|
||||||
if re.match(regex, message): return
|
if re.match(regex, message):
|
||||||
|
return
|
||||||
old_report_warning(message)
|
old_report_warning(message)
|
||||||
self.report_warning = types.MethodType(report_warning, self)
|
self.report_warning = types.MethodType(report_warning, self)
|
||||||
|
|
||||||
|
|
|
@ -266,6 +266,7 @@ class TestFormatSelection(unittest.TestCase):
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'width': None,
|
'width': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
def fname(templ):
|
def fname(templ):
|
||||||
ydl = YoutubeDL({'outtmpl': templ})
|
ydl = YoutubeDL({'outtmpl': templ})
|
||||||
return ydl.prepare_filename(info)
|
return ydl.prepare_filename(info)
|
||||||
|
|
|
@ -40,18 +40,22 @@ from youtube_dl.extractor import get_info_extractor
|
||||||
|
|
||||||
RETRIES = 3
|
RETRIES = 3
|
||||||
|
|
||||||
|
|
||||||
class YoutubeDL(youtube_dl.YoutubeDL):
|
class YoutubeDL(youtube_dl.YoutubeDL):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.to_stderr = self.to_screen
|
self.to_stderr = self.to_screen
|
||||||
self.processed_info_dicts = []
|
self.processed_info_dicts = []
|
||||||
super(YoutubeDL, self).__init__(*args, **kwargs)
|
super(YoutubeDL, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def report_warning(self, message):
|
def report_warning(self, message):
|
||||||
# Don't accept warnings during tests
|
# Don't accept warnings during tests
|
||||||
raise ExtractorError(message)
|
raise ExtractorError(message)
|
||||||
|
|
||||||
def process_info(self, info_dict):
|
def process_info(self, info_dict):
|
||||||
self.processed_info_dicts.append(info_dict)
|
self.processed_info_dicts.append(info_dict)
|
||||||
return super(YoutubeDL, self).process_info(info_dict)
|
return super(YoutubeDL, self).process_info(info_dict)
|
||||||
|
|
||||||
|
|
||||||
def _file_md5(fn):
|
def _file_md5(fn):
|
||||||
with open(fn, 'rb') as f:
|
with open(fn, 'rb') as f:
|
||||||
return hashlib.md5(f.read()).hexdigest()
|
return hashlib.md5(f.read()).hexdigest()
|
||||||
|
@ -61,10 +65,13 @@ defs = gettestcases()
|
||||||
|
|
||||||
class TestDownload(unittest.TestCase):
|
class TestDownload(unittest.TestCase):
|
||||||
maxDiff = None
|
maxDiff = None
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.defs = defs
|
self.defs = defs
|
||||||
|
|
||||||
### Dynamically generate tests
|
# Dynamically generate tests
|
||||||
|
|
||||||
|
|
||||||
def generator(test_case):
|
def generator(test_case):
|
||||||
|
|
||||||
def test_template(self):
|
def test_template(self):
|
||||||
|
@ -101,6 +108,7 @@ def generator(test_case):
|
||||||
ydl = YoutubeDL(params, auto_init=False)
|
ydl = YoutubeDL(params, auto_init=False)
|
||||||
ydl.add_default_info_extractors()
|
ydl.add_default_info_extractors()
|
||||||
finished_hook_called = set()
|
finished_hook_called = set()
|
||||||
|
|
||||||
def _hook(status):
|
def _hook(status):
|
||||||
if status['status'] == 'finished':
|
if status['status'] == 'finished':
|
||||||
finished_hook_called.add(status['filename'])
|
finished_hook_called.add(status['filename'])
|
||||||
|
@ -111,6 +119,7 @@ def generator(test_case):
|
||||||
return tc.get('file') or ydl.prepare_filename(tc.get('info_dict', {}))
|
return tc.get('file') or ydl.prepare_filename(tc.get('info_dict', {}))
|
||||||
|
|
||||||
res_dict = None
|
res_dict = None
|
||||||
|
|
||||||
def try_rm_tcs_files(tcs=None):
|
def try_rm_tcs_files(tcs=None):
|
||||||
if tcs is None:
|
if tcs is None:
|
||||||
tcs = test_cases
|
tcs = test_cases
|
||||||
|
@ -206,7 +215,7 @@ def generator(test_case):
|
||||||
|
|
||||||
return test_template
|
return test_template
|
||||||
|
|
||||||
### And add them to TestDownload
|
# And add them to TestDownload
|
||||||
for n, test_case in enumerate(defs):
|
for n, test_case in enumerate(defs):
|
||||||
test_method = generator(test_case)
|
test_method = generator(test_case)
|
||||||
tname = 'test_' + str(test_case['name'])
|
tname = 'test_' + str(test_case['name'])
|
||||||
|
|
|
@ -23,6 +23,7 @@ from youtube_dl.extractor import (
|
||||||
class BaseTestSubtitles(unittest.TestCase):
|
class BaseTestSubtitles(unittest.TestCase):
|
||||||
url = None
|
url = None
|
||||||
IE = None
|
IE = None
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.DL = FakeYDL()
|
self.DL = FakeYDL()
|
||||||
self.ie = self.IE(self.DL)
|
self.ie = self.IE(self.DL)
|
||||||
|
|
|
@ -45,7 +45,6 @@ from youtube_dl.utils import (
|
||||||
escape_rfc3986,
|
escape_rfc3986,
|
||||||
escape_url,
|
escape_url,
|
||||||
js_to_json,
|
js_to_json,
|
||||||
get_filesystem_encoding,
|
|
||||||
intlist_to_bytes,
|
intlist_to_bytes,
|
||||||
args_to_str,
|
args_to_str,
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,17 +31,16 @@ params = get_params({
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TEST_ID = 'gr51aVj-mLg'
|
TEST_ID = 'gr51aVj-mLg'
|
||||||
ANNOTATIONS_FILE = TEST_ID + '.flv.annotations.xml'
|
ANNOTATIONS_FILE = TEST_ID + '.flv.annotations.xml'
|
||||||
EXPECTED_ANNOTATIONS = ['Speech bubble', 'Note', 'Title', 'Spotlight', 'Label']
|
EXPECTED_ANNOTATIONS = ['Speech bubble', 'Note', 'Title', 'Spotlight', 'Label']
|
||||||
|
|
||||||
|
|
||||||
class TestAnnotations(unittest.TestCase):
|
class TestAnnotations(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Clear old files
|
# Clear old files
|
||||||
self.tearDown()
|
self.tearDown()
|
||||||
|
|
||||||
|
|
||||||
def test_info_json(self):
|
def test_info_json(self):
|
||||||
expected = list(EXPECTED_ANNOTATIONS) # Two annotations could have the same text.
|
expected = list(EXPECTED_ANNOTATIONS) # Two annotations could have the same text.
|
||||||
ie = youtube_dl.extractor.YoutubeIE()
|
ie = youtube_dl.extractor.YoutubeIE()
|
||||||
|
@ -71,7 +70,6 @@ class TestAnnotations(unittest.TestCase):
|
||||||
# We should have seen (and removed) all the expected annotation texts.
|
# We should have seen (and removed) all the expected annotation texts.
|
||||||
self.assertEqual(len(expected), 0, 'Not all expected annotations were found.')
|
self.assertEqual(len(expected), 0, 'Not all expected annotations were found.')
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
try_rm(ANNOTATIONS_FILE)
|
try_rm(ANNOTATIONS_FILE)
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,6 @@ from test.helper import FakeYDL
|
||||||
from youtube_dl.extractor import (
|
from youtube_dl.extractor import (
|
||||||
YoutubePlaylistIE,
|
YoutubePlaylistIE,
|
||||||
YoutubeIE,
|
YoutubeIE,
|
||||||
YoutubeChannelIE,
|
|
||||||
YoutubeShowIE,
|
|
||||||
YoutubeTopListIE,
|
|
||||||
YoutubeSearchURLIE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ from .compat import (
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_urllib_error,
|
compat_urllib_error,
|
||||||
compat_urllib_request,
|
compat_urllib_request,
|
||||||
shlex_quote,
|
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
escape_url,
|
escape_url,
|
||||||
|
@ -700,6 +699,7 @@ class YoutubeDL(object):
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
'Extractor %s returned a compat_list result. '
|
'Extractor %s returned a compat_list result. '
|
||||||
'It needs to be updated.' % ie_result.get('extractor'))
|
'It needs to be updated.' % ie_result.get('extractor'))
|
||||||
|
|
||||||
def _fixup(r):
|
def _fixup(r):
|
||||||
self.add_extra_info(r,
|
self.add_extra_info(r,
|
||||||
{
|
{
|
||||||
|
@ -1428,4 +1428,3 @@ class YoutubeDL(object):
|
||||||
if encoding is None:
|
if encoding is None:
|
||||||
encoding = preferredencoding()
|
encoding = preferredencoding()
|
||||||
return encoding
|
return encoding
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,6 @@ def _real_main(argv=None):
|
||||||
compat_print(desc)
|
compat_print(desc)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
# Conflicting, missing and erroneous options
|
# Conflicting, missing and erroneous options
|
||||||
if opts.usenetrc and (opts.username is not None or opts.password is not None):
|
if opts.usenetrc and (opts.username is not None or opts.password is not None):
|
||||||
parser.error('using .netrc conflicts with giving username/password')
|
parser.error('using .netrc conflicts with giving username/password')
|
||||||
|
@ -317,7 +316,6 @@ def _real_main(argv=None):
|
||||||
ydl.add_post_processor(FFmpegAudioFixPP())
|
ydl.add_post_processor(FFmpegAudioFixPP())
|
||||||
ydl.add_post_processor(AtomicParsleyPP())
|
ydl.add_post_processor(AtomicParsleyPP())
|
||||||
|
|
||||||
|
|
||||||
# Please keep ExecAfterDownload towards the bottom as it allows the user to modify the final file in any way.
|
# Please keep ExecAfterDownload towards the bottom as it allows the user to modify the final file in any way.
|
||||||
# So if the user is able to remove the file before your postprocessor runs it might cause a few problems.
|
# So if the user is able to remove the file before your postprocessor runs it might cause a few problems.
|
||||||
if opts.exec_cmd:
|
if opts.exec_cmd:
|
||||||
|
|
|
@ -7,6 +7,7 @@ from .utils import bytes_to_intlist, intlist_to_bytes
|
||||||
|
|
||||||
BLOCK_SIZE_BYTES = 16
|
BLOCK_SIZE_BYTES = 16
|
||||||
|
|
||||||
|
|
||||||
def aes_ctr_decrypt(data, key, counter):
|
def aes_ctr_decrypt(data, key, counter):
|
||||||
"""
|
"""
|
||||||
Decrypt with aes in counter mode
|
Decrypt with aes in counter mode
|
||||||
|
@ -32,6 +33,7 @@ def aes_ctr_decrypt(data, key, counter):
|
||||||
|
|
||||||
return decrypted_data
|
return decrypted_data
|
||||||
|
|
||||||
|
|
||||||
def aes_cbc_decrypt(data, key, iv):
|
def aes_cbc_decrypt(data, key, iv):
|
||||||
"""
|
"""
|
||||||
Decrypt with aes in CBC mode
|
Decrypt with aes in CBC mode
|
||||||
|
@ -57,6 +59,7 @@ def aes_cbc_decrypt(data, key, iv):
|
||||||
|
|
||||||
return decrypted_data
|
return decrypted_data
|
||||||
|
|
||||||
|
|
||||||
def key_expansion(data):
|
def key_expansion(data):
|
||||||
"""
|
"""
|
||||||
Generate key schedule
|
Generate key schedule
|
||||||
|
@ -91,6 +94,7 @@ def key_expansion(data):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def aes_encrypt(data, expanded_key):
|
def aes_encrypt(data, expanded_key):
|
||||||
"""
|
"""
|
||||||
Encrypt one block with aes
|
Encrypt one block with aes
|
||||||
|
@ -111,6 +115,7 @@ def aes_encrypt(data, expanded_key):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def aes_decrypt(data, expanded_key):
|
def aes_decrypt(data, expanded_key):
|
||||||
"""
|
"""
|
||||||
Decrypt one block with aes
|
Decrypt one block with aes
|
||||||
|
@ -131,6 +136,7 @@ def aes_decrypt(data, expanded_key):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def aes_decrypt_text(data, password, key_size_bytes):
|
def aes_decrypt_text(data, password, key_size_bytes):
|
||||||
"""
|
"""
|
||||||
Decrypt text
|
Decrypt text
|
||||||
|
@ -157,6 +163,7 @@ def aes_decrypt_text(data, password, key_size_bytes):
|
||||||
|
|
||||||
class Counter:
|
class Counter:
|
||||||
__value = nonce + [0] * (BLOCK_SIZE_BYTES - NONCE_LENGTH_BYTES)
|
__value = nonce + [0] * (BLOCK_SIZE_BYTES - NONCE_LENGTH_BYTES)
|
||||||
|
|
||||||
def next_value(self):
|
def next_value(self):
|
||||||
temp = self.__value
|
temp = self.__value
|
||||||
self.__value = inc(self.__value)
|
self.__value = inc(self.__value)
|
||||||
|
@ -241,15 +248,19 @@ RIJNDAEL_LOG_TABLE = (0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1a, 0xc6, 0x4b, 0xc7
|
||||||
0x44, 0x11, 0x92, 0xd9, 0x23, 0x20, 0x2e, 0x89, 0xb4, 0x7c, 0xb8, 0x26, 0x77, 0x99, 0xe3, 0xa5,
|
0x44, 0x11, 0x92, 0xd9, 0x23, 0x20, 0x2e, 0x89, 0xb4, 0x7c, 0xb8, 0x26, 0x77, 0x99, 0xe3, 0xa5,
|
||||||
0x67, 0x4a, 0xed, 0xde, 0xc5, 0x31, 0xfe, 0x18, 0x0d, 0x63, 0x8c, 0x80, 0xc0, 0xf7, 0x70, 0x07)
|
0x67, 0x4a, 0xed, 0xde, 0xc5, 0x31, 0xfe, 0x18, 0x0d, 0x63, 0x8c, 0x80, 0xc0, 0xf7, 0x70, 0x07)
|
||||||
|
|
||||||
|
|
||||||
def sub_bytes(data):
|
def sub_bytes(data):
|
||||||
return [SBOX[x] for x in data]
|
return [SBOX[x] for x in data]
|
||||||
|
|
||||||
|
|
||||||
def sub_bytes_inv(data):
|
def sub_bytes_inv(data):
|
||||||
return [SBOX_INV[x] for x in data]
|
return [SBOX_INV[x] for x in data]
|
||||||
|
|
||||||
|
|
||||||
def rotate(data):
|
def rotate(data):
|
||||||
return data[1:] + [data[0]]
|
return data[1:] + [data[0]]
|
||||||
|
|
||||||
|
|
||||||
def key_schedule_core(data, rcon_iteration):
|
def key_schedule_core(data, rcon_iteration):
|
||||||
data = rotate(data)
|
data = rotate(data)
|
||||||
data = sub_bytes(data)
|
data = sub_bytes(data)
|
||||||
|
@ -257,14 +268,17 @@ def key_schedule_core(data, rcon_iteration):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def xor(data1, data2):
|
def xor(data1, data2):
|
||||||
return [x ^ y for x, y in zip(data1, data2)]
|
return [x ^ y for x, y in zip(data1, data2)]
|
||||||
|
|
||||||
|
|
||||||
def rijndael_mul(a, b):
|
def rijndael_mul(a, b):
|
||||||
if(a == 0 or b == 0):
|
if(a == 0 or b == 0):
|
||||||
return 0
|
return 0
|
||||||
return RIJNDAEL_EXP_TABLE[(RIJNDAEL_LOG_TABLE[a] + RIJNDAEL_LOG_TABLE[b]) % 0xFF]
|
return RIJNDAEL_EXP_TABLE[(RIJNDAEL_LOG_TABLE[a] + RIJNDAEL_LOG_TABLE[b]) % 0xFF]
|
||||||
|
|
||||||
|
|
||||||
def mix_column(data, matrix):
|
def mix_column(data, matrix):
|
||||||
data_mixed = []
|
data_mixed = []
|
||||||
for row in range(4):
|
for row in range(4):
|
||||||
|
@ -275,6 +289,7 @@ def mix_column(data, matrix):
|
||||||
data_mixed.append(mixed)
|
data_mixed.append(mixed)
|
||||||
return data_mixed
|
return data_mixed
|
||||||
|
|
||||||
|
|
||||||
def mix_columns(data, matrix=MIX_COLUMN_MATRIX):
|
def mix_columns(data, matrix=MIX_COLUMN_MATRIX):
|
||||||
data_mixed = []
|
data_mixed = []
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
|
@ -282,9 +297,11 @@ def mix_columns(data, matrix=MIX_COLUMN_MATRIX):
|
||||||
data_mixed += mix_column(column, matrix)
|
data_mixed += mix_column(column, matrix)
|
||||||
return data_mixed
|
return data_mixed
|
||||||
|
|
||||||
|
|
||||||
def mix_columns_inv(data):
|
def mix_columns_inv(data):
|
||||||
return mix_columns(data, MIX_COLUMN_MATRIX_INV)
|
return mix_columns(data, MIX_COLUMN_MATRIX_INV)
|
||||||
|
|
||||||
|
|
||||||
def shift_rows(data):
|
def shift_rows(data):
|
||||||
data_shifted = []
|
data_shifted = []
|
||||||
for column in range(4):
|
for column in range(4):
|
||||||
|
@ -292,6 +309,7 @@ def shift_rows(data):
|
||||||
data_shifted.append(data[((column + row) & 0b11) * 4 + row])
|
data_shifted.append(data[((column + row) & 0b11) * 4 + row])
|
||||||
return data_shifted
|
return data_shifted
|
||||||
|
|
||||||
|
|
||||||
def shift_rows_inv(data):
|
def shift_rows_inv(data):
|
||||||
data_shifted = []
|
data_shifted = []
|
||||||
for column in range(4):
|
for column in range(4):
|
||||||
|
@ -299,6 +317,7 @@ def shift_rows_inv(data):
|
||||||
data_shifted.append(data[((column - row) & 0b11) * 4 + row])
|
data_shifted.append(data[((column - row) & 0b11) * 4 + row])
|
||||||
return data_shifted
|
return data_shifted
|
||||||
|
|
||||||
|
|
||||||
def inc(data):
|
def inc(data):
|
||||||
data = data[:] # copy
|
data = data[:] # copy
|
||||||
for i in range(len(data) - 1, -1, -1):
|
for i in range(len(data) - 1, -1, -1):
|
||||||
|
|
|
@ -182,8 +182,10 @@ except ImportError: # Python < 3.3
|
||||||
|
|
||||||
|
|
||||||
def compat_ord(c):
|
def compat_ord(c):
|
||||||
if type(c) is int: return c
|
if type(c) is int:
|
||||||
else: return ord(c)
|
return c
|
||||||
|
else:
|
||||||
|
return ord(c)
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 0):
|
if sys.version_info >= (3, 0):
|
||||||
|
|
|
@ -101,4 +101,3 @@ class NativeHlsFD(FileDownloader):
|
||||||
})
|
})
|
||||||
self.try_rename(tmpfilename, filename)
|
self.try_rename(tmpfilename, filename)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
class AdultSwimIE(InfoExtractor):
|
class AdultSwimIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://video\.adultswim\.com/(?P<path>.+?)(?:\.html)?(?:\?.*)?(?:#.*)?$'
|
_VALID_URL = r'https?://video\.adultswim\.com/(?P<path>.+?)(?:\.html)?(?:\?.*)?(?:#.*)?$'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
|
|
|
@ -70,11 +70,13 @@ class AppleTrailersIE(InfoExtractor):
|
||||||
uploader_id = mobj.group('company')
|
uploader_id = mobj.group('company')
|
||||||
|
|
||||||
playlist_url = compat_urlparse.urljoin(url, 'includes/playlists/itunes.inc')
|
playlist_url = compat_urlparse.urljoin(url, 'includes/playlists/itunes.inc')
|
||||||
|
|
||||||
def fix_html(s):
|
def fix_html(s):
|
||||||
s = re.sub(r'(?s)<script[^<]*?>.*?</script>', '', s)
|
s = re.sub(r'(?s)<script[^<]*?>.*?</script>', '', s)
|
||||||
s = re.sub(r'<img ([^<]*?)>', r'<img \1/>', s)
|
s = re.sub(r'<img ([^<]*?)>', r'<img \1/>', s)
|
||||||
# The ' in the onClick attributes are not escaped, it couldn't be parsed
|
# The ' in the onClick attributes are not escaped, it couldn't be parsed
|
||||||
# like: http://trailers.apple.com/trailers/wb/gravity/
|
# like: http://trailers.apple.com/trailers/wb/gravity/
|
||||||
|
|
||||||
def _clean_json(m):
|
def _clean_json(m):
|
||||||
return 'iTunes.playURL(%s);' % m.group(1).replace('\'', ''')
|
return 'iTunes.playURL(%s);' % m.group(1).replace('\'', ''')
|
||||||
s = re.sub(self._JSON_RE, _clean_json, s)
|
s = re.sub(self._JSON_RE, _clean_json, s)
|
||||||
|
|
|
@ -192,4 +192,3 @@ class ARDIE(InfoExtractor):
|
||||||
'upload_date': upload_date,
|
'upload_date': upload_date,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import re
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import ExtractorError
|
from ..utils import ExtractorError
|
||||||
|
|
||||||
|
|
||||||
class Channel9IE(InfoExtractor):
|
class Channel9IE(InfoExtractor):
|
||||||
'''
|
'''
|
||||||
Common extractor for channel9.msdn.com.
|
Common extractor for channel9.msdn.com.
|
||||||
|
|
|
@ -39,6 +39,7 @@ class ClipsyndicateIE(InfoExtractor):
|
||||||
transform_source=fix_xml_ampersands)
|
transform_source=fix_xml_ampersands)
|
||||||
|
|
||||||
track_doc = pdoc.find('trackList/track')
|
track_doc = pdoc.find('trackList/track')
|
||||||
|
|
||||||
def find_param(name):
|
def find_param(name):
|
||||||
node = find_xpath_attr(track_doc, './/param', 'name', name)
|
node = find_xpath_attr(track_doc, './/param', 'name', name)
|
||||||
if node is not None:
|
if node is not None:
|
||||||
|
|
|
@ -434,6 +434,7 @@ class InfoExtractor(object):
|
||||||
if video_id is not None:
|
if video_id is not None:
|
||||||
video_info['id'] = video_id
|
video_info['id'] = video_id
|
||||||
return video_info
|
return video_info
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def playlist_result(entries, playlist_id=None, playlist_title=None):
|
def playlist_result(entries, playlist_id=None, playlist_title=None):
|
||||||
"""Returns a playlist"""
|
"""Returns a playlist"""
|
||||||
|
|
|
@ -69,11 +69,9 @@ class CrunchyrollIE(SubtitlesInfoExtractor):
|
||||||
login_request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
login_request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
self._download_webpage(login_request, None, False, 'Wrong login info')
|
self._download_webpage(login_request, None, False, 'Wrong login info')
|
||||||
|
|
||||||
|
|
||||||
def _real_initialize(self):
|
def _real_initialize(self):
|
||||||
self._login()
|
self._login()
|
||||||
|
|
||||||
|
|
||||||
def _decrypt_subtitles(self, data, iv, id):
|
def _decrypt_subtitles(self, data, iv, id):
|
||||||
data = bytes_to_intlist(data)
|
data = bytes_to_intlist(data)
|
||||||
iv = bytes_to_intlist(iv)
|
iv = bytes_to_intlist(iv)
|
||||||
|
@ -99,8 +97,10 @@ class CrunchyrollIE(SubtitlesInfoExtractor):
|
||||||
return shaHash + [0] * 12
|
return shaHash + [0] * 12
|
||||||
|
|
||||||
key = obfuscate_key(id)
|
key = obfuscate_key(id)
|
||||||
|
|
||||||
class Counter:
|
class Counter:
|
||||||
__value = iv
|
__value = iv
|
||||||
|
|
||||||
def next_value(self):
|
def next_value(self):
|
||||||
temp = self.__value
|
temp = self.__value
|
||||||
self.__value = inc(self.__value)
|
self.__value = inc(self.__value)
|
||||||
|
|
|
@ -18,6 +18,7 @@ from ..utils import (
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DailymotionBaseInfoExtractor(InfoExtractor):
|
class DailymotionBaseInfoExtractor(InfoExtractor):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _build_request(url):
|
def _build_request(url):
|
||||||
|
@ -27,6 +28,7 @@ class DailymotionBaseInfoExtractor(InfoExtractor):
|
||||||
request.add_header('Cookie', 'ff=off')
|
request.add_header('Cookie', 'ff=off')
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
|
||||||
class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor):
|
class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor):
|
||||||
"""Information Extractor for Dailymotion"""
|
"""Information Extractor for Dailymotion"""
|
||||||
|
|
||||||
|
|
|
@ -1025,4 +1025,3 @@ class GenericIE(InfoExtractor):
|
||||||
'_type': 'playlist',
|
'_type': 'playlist',
|
||||||
'entries': entries,
|
'entries': entries,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,4 +45,3 @@ class JadoreCettePubIE(InfoExtractor):
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': description,
|
'description': description,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,4 +30,3 @@ class Ku6IE(InfoExtractor):
|
||||||
'title': title,
|
'title': title,
|
||||||
'url': downloadUrl
|
'url': downloadUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,4 +75,3 @@ class Laola1TvIE(InfoExtractor):
|
||||||
'categories': categories,
|
'categories': categories,
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,7 @@ class LiveLeakIE(InfoExtractor):
|
||||||
'uploader': 'ljfriel2',
|
'uploader': 'ljfriel2',
|
||||||
'title': 'Most unlucky car accident'
|
'title': 'Most unlucky car accident'
|
||||||
}
|
}
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
'url': 'http://www.liveleak.com/view?i=f93_1390833151',
|
'url': 'http://www.liveleak.com/view?i=f93_1390833151',
|
||||||
'md5': 'd3f1367d14cc3c15bf24fbfbe04b9abf',
|
'md5': 'd3f1367d14cc3c15bf24fbfbe04b9abf',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
@ -30,8 +29,7 @@ class LiveLeakIE(InfoExtractor):
|
||||||
'uploader': 'ARD_Stinkt',
|
'uploader': 'ARD_Stinkt',
|
||||||
'title': 'German Television does first Edward Snowden Interview (ENGLISH)',
|
'title': 'German Television does first Edward Snowden Interview (ENGLISH)',
|
||||||
}
|
}
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
'url': 'http://www.liveleak.com/view?i=4f7_1392687779',
|
'url': 'http://www.liveleak.com/view?i=4f7_1392687779',
|
||||||
'md5': '42c6d97d54f1db107958760788c5f48f',
|
'md5': '42c6d97d54f1db107958760788c5f48f',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
|
|
@ -7,6 +7,7 @@ from ..utils import (
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MalemotionIE(InfoExtractor):
|
class MalemotionIE(InfoExtractor):
|
||||||
_VALID_URL = r'^(?:https?://)?malemotion\.com/video/(.+?)\.(?P<id>.+?)(#|$)'
|
_VALID_URL = r'^(?:https?://)?malemotion\.com/video/(.+?)\.(?P<id>.+?)(#|$)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
|
|
|
@ -245,7 +245,7 @@ class MTVIE(MTVServicesInfoExtractor):
|
||||||
m_vevo = re.search(r'isVevoVideo = true;.*?vevoVideoId = "(.*?)";',
|
m_vevo = re.search(r'isVevoVideo = true;.*?vevoVideoId = "(.*?)";',
|
||||||
webpage, re.DOTALL)
|
webpage, re.DOTALL)
|
||||||
if m_vevo:
|
if m_vevo:
|
||||||
vevo_id = m_vevo.group(1);
|
vevo_id = m_vevo.group(1)
|
||||||
self.to_screen('Vevo video detected: %s' % vevo_id)
|
self.to_screen('Vevo video detected: %s' % vevo_id)
|
||||||
return self.url_result('vevo:%s' % vevo_id, ie='Vevo')
|
return self.url_result('vevo:%s' % vevo_id, ie='Vevo')
|
||||||
|
|
||||||
|
|
|
@ -73,4 +73,3 @@ class MuenchenTVIE(InfoExtractor):
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,4 +173,3 @@ class MyVideoIE(InfoExtractor):
|
||||||
'play_path': video_playpath,
|
'play_path': video_playpath,
|
||||||
'player_url': video_swfobj,
|
'player_url': video_swfobj,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ class NBAIE(InfoExtractor):
|
||||||
duration = parse_duration(
|
duration = parse_duration(
|
||||||
self._html_search_meta('duration', webpage, 'duration', fatal=False))
|
self._html_search_meta('duration', webpage, 'duration', fatal=False))
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': shortened_video_id,
|
'id': shortened_video_id,
|
||||||
'url': video_url,
|
'url': video_url,
|
||||||
|
|
|
@ -97,4 +97,3 @@ class OoyalaIE(InfoExtractor):
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return self._extract_result(videos_info[0], videos_more_info)
|
return self._extract_result(videos_info[0], videos_more_info)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import re
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import int_or_none
|
from ..utils import int_or_none
|
||||||
|
|
||||||
|
|
||||||
class PodomaticIE(InfoExtractor):
|
class PodomaticIE(InfoExtractor):
|
||||||
IE_NAME = 'podomatic'
|
IE_NAME = 'podomatic'
|
||||||
_VALID_URL = r'^(?P<proto>https?)://(?P<channel>[^.]+)\.podomatic\.com/entry/(?P<id>[^?]+)'
|
_VALID_URL = r'^(?P<proto>https?)://(?P<channel>[^.]+)\.podomatic\.com/entry/(?P<id>[^?]+)'
|
||||||
|
|
|
@ -41,4 +41,3 @@ class RingTVIE(InfoExtractor):
|
||||||
'thumbnail': thumbnail_url,
|
'thumbnail': thumbnail_url,
|
||||||
'description': description,
|
'description': description,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class RtlXlIE(InfoExtractor):
|
||||||
|
|
||||||
formats = self._extract_m3u8_formats(m3u8_url, uuid, ext='mp4')
|
formats = self._extract_m3u8_formats(m3u8_url, uuid, ext='mp4')
|
||||||
|
|
||||||
video_urlpart = videopath.split('/flash/')[1][:-4]
|
video_urlpart = videopath.split('/flash/')[1][:-5]
|
||||||
PG_URL_TEMPLATE = 'http://pg.us.rtl.nl/rtlxl/network/%s/progressive/%s.mp4'
|
PG_URL_TEMPLATE = 'http://pg.us.rtl.nl/rtlxl/network/%s/progressive/%s.mp4'
|
||||||
|
|
||||||
formats.extend([
|
formats.extend([
|
||||||
|
|
|
@ -54,7 +54,6 @@ def _decrypt_url(png):
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RTVEALaCartaIE(InfoExtractor):
|
class RTVEALaCartaIE(InfoExtractor):
|
||||||
IE_NAME = 'rtve.es:alacarta'
|
IE_NAME = 'rtve.es:alacarta'
|
||||||
IE_DESC = 'RTVE a la carta'
|
IE_DESC = 'RTVE a la carta'
|
||||||
|
|
|
@ -67,5 +67,3 @@ class ServingSysIE(InfoExtractor):
|
||||||
'title': title,
|
'title': title,
|
||||||
'entries': entries,
|
'entries': entries,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os.path
|
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
|
@ -12,15 +11,15 @@ from ..utils import (
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
compat_urllib_request,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
url_basename,
|
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
unified_strdate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SmotriIE(InfoExtractor):
|
class SmotriIE(InfoExtractor):
|
||||||
IE_DESC = 'Smotri.com'
|
IE_DESC = 'Smotri.com'
|
||||||
IE_NAME = 'smotri'
|
IE_NAME = 'smotri'
|
||||||
_VALID_URL = r'^https?://(?:www\.)?(?:smotri\.com/video/view/\?id=|pics\.smotri\.com/(?:player|scrubber_custom8)\.swf\?file=)(?P<videoid>v(?P<realvideoid>[0-9]+)[a-z0-9]{4})'
|
_VALID_URL = r'^https?://(?:www\.)?(?:smotri\.com/video/view/\?id=|pics\.smotri\.com/(?:player|scrubber_custom8)\.swf\?file=)(?P<id>v(?P<realvideoid>[0-9]+)[a-z0-9]{4})'
|
||||||
_NETRC_MACHINE = 'smotri'
|
_NETRC_MACHINE = 'smotri'
|
||||||
|
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
|
@ -35,7 +34,6 @@ class SmotriIE(InfoExtractor):
|
||||||
'uploader': 'rbc2008',
|
'uploader': 'rbc2008',
|
||||||
'uploader_id': 'rbc08',
|
'uploader_id': 'rbc08',
|
||||||
'upload_date': '20131118',
|
'upload_date': '20131118',
|
||||||
'description': 'катастрофа с камер видеонаблюдения, видео катастрофа с камер видеонаблюдения',
|
|
||||||
'thumbnail': 'http://frame6.loadup.ru/8b/a9/2610366.3.3.jpg',
|
'thumbnail': 'http://frame6.loadup.ru/8b/a9/2610366.3.3.jpg',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -50,7 +48,6 @@ class SmotriIE(InfoExtractor):
|
||||||
'uploader': 'Support Photofile@photofile',
|
'uploader': 'Support Photofile@photofile',
|
||||||
'uploader_id': 'support-photofile',
|
'uploader_id': 'support-photofile',
|
||||||
'upload_date': '20070704',
|
'upload_date': '20070704',
|
||||||
'description': 'test, видео test',
|
|
||||||
'thumbnail': 'http://frame4.loadup.ru/03/ed/57591.2.3.jpg',
|
'thumbnail': 'http://frame4.loadup.ru/03/ed/57591.2.3.jpg',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -66,7 +63,6 @@ class SmotriIE(InfoExtractor):
|
||||||
'uploader_id': 'timoxa40',
|
'uploader_id': 'timoxa40',
|
||||||
'upload_date': '20100404',
|
'upload_date': '20100404',
|
||||||
'thumbnail': 'http://frame7.loadup.ru/af/3f/1390466.3.3.jpg',
|
'thumbnail': 'http://frame7.loadup.ru/af/3f/1390466.3.3.jpg',
|
||||||
'description': 'TOCCA_A_NOI_-_LE_COSE_NON_VANNO_CAMBIAMOLE_ORA-1, видео TOCCA_A_NOI_-_LE_COSE_NON_VANNO_CAMBIAMOLE_ORA-1',
|
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'videopassword': 'qwerty',
|
'videopassword': 'qwerty',
|
||||||
|
@ -85,7 +81,6 @@ class SmotriIE(InfoExtractor):
|
||||||
'upload_date': '20101001',
|
'upload_date': '20101001',
|
||||||
'thumbnail': 'http://frame3.loadup.ru/75/75/1540889.1.3.jpg',
|
'thumbnail': 'http://frame3.loadup.ru/75/75/1540889.1.3.jpg',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
'description': 'этот ролик не покажут по ТВ, видео этот ролик не покажут по ТВ',
|
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'videopassword': '333'
|
'videopassword': '333'
|
||||||
|
@ -102,17 +97,11 @@ class SmotriIE(InfoExtractor):
|
||||||
'uploader': 'HannahL',
|
'uploader': 'HannahL',
|
||||||
'uploader_id': 'lisaha95',
|
'uploader_id': 'lisaha95',
|
||||||
'upload_date': '20090331',
|
'upload_date': '20090331',
|
||||||
'description': 'Shakira - Don\'t Bother, видео Shakira - Don\'t Bother',
|
|
||||||
'thumbnail': 'http://frame8.loadup.ru/44/0b/918809.7.3.jpg',
|
'thumbnail': 'http://frame8.loadup.ru/44/0b/918809.7.3.jpg',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
_SUCCESS = 0
|
|
||||||
_PASSWORD_NOT_VERIFIED = 1
|
|
||||||
_PASSWORD_DETECTED = 2
|
|
||||||
_VIDEO_NOT_FOUND = 3
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _extract_url(cls, webpage):
|
def _extract_url(cls, webpage):
|
||||||
mobj = re.search(
|
mobj = re.search(
|
||||||
|
@ -137,44 +126,44 @@ class SmotriIE(InfoExtractor):
|
||||||
return self._html_search_meta(name, html, display_name)
|
return self._html_search_meta(name, html, display_name)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
video_id = self._match_id(url)
|
||||||
video_id = mobj.group('videoid')
|
|
||||||
real_video_id = mobj.group('realvideoid')
|
|
||||||
|
|
||||||
# Download video JSON data
|
video_form = {
|
||||||
video_json_url = 'http://smotri.com/vt.php?id=%s' % real_video_id
|
'ticket': video_id,
|
||||||
video_json_page = self._download_webpage(video_json_url, video_id, 'Downloading video JSON')
|
'video_url': '1',
|
||||||
video_json = json.loads(video_json_page)
|
'frame_url': '1',
|
||||||
|
'devid': 'LoadupFlashPlayer',
|
||||||
|
'getvideoinfo': '1',
|
||||||
|
}
|
||||||
|
|
||||||
status = video_json['status']
|
request = compat_urllib_request.Request(
|
||||||
if status == self._VIDEO_NOT_FOUND:
|
'http://smotri.com/video/view/url/bot/', compat_urllib_parse.urlencode(video_form))
|
||||||
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
|
||||||
|
video = self._download_json(request, video_id, 'Downloading video JSON')
|
||||||
|
|
||||||
|
if video.get('_moderate_no') or not video.get('moderated'):
|
||||||
|
raise ExtractorError('Video %s has not been approved by moderator' % video_id, expected=True)
|
||||||
|
|
||||||
|
if video.get('error'):
|
||||||
raise ExtractorError('Video %s does not exist' % video_id, expected=True)
|
raise ExtractorError('Video %s does not exist' % video_id, expected=True)
|
||||||
elif status == self._PASSWORD_DETECTED: # The video is protected by a password, retry with
|
|
||||||
# video-password set
|
|
||||||
video_password = self._downloader.params.get('videopassword', None)
|
|
||||||
if not video_password:
|
|
||||||
raise ExtractorError('This video is protected by a password, use the --video-password option', expected=True)
|
|
||||||
video_json_url += '&md5pass=%s' % hashlib.md5(video_password.encode('utf-8')).hexdigest()
|
|
||||||
video_json_page = self._download_webpage(video_json_url, video_id, 'Downloading video JSON (video-password set)')
|
|
||||||
video_json = json.loads(video_json_page)
|
|
||||||
status = video_json['status']
|
|
||||||
if status == self._PASSWORD_NOT_VERIFIED:
|
|
||||||
raise ExtractorError('Video password is invalid', expected=True)
|
|
||||||
|
|
||||||
if status != self._SUCCESS:
|
video_url = video.get('_vidURL') or video.get('_vidURL_mp4')
|
||||||
raise ExtractorError('Unexpected status value %s' % status)
|
title = video['title']
|
||||||
|
thumbnail = video['_imgURL']
|
||||||
# Extract the URL of the video
|
upload_date = unified_strdate(video['added'])
|
||||||
video_url = video_json['file_data']
|
uploader = video['userNick']
|
||||||
|
uploader_id = video['userLogin']
|
||||||
|
duration = int_or_none(video['duration'])
|
||||||
|
|
||||||
# Video JSON does not provide enough meta data
|
# Video JSON does not provide enough meta data
|
||||||
# We will extract some from the video web page instead
|
# We will extract some from the video web page instead
|
||||||
video_page_url = 'http://smotri.com/video/view/?id=%s' % video_id
|
webpage_url = 'http://smotri.com/video/view/?id=%s' % video_id
|
||||||
video_page = self._download_webpage(video_page_url, video_id, 'Downloading video page')
|
webpage = self._download_webpage(webpage_url, video_id, 'Downloading video page')
|
||||||
|
|
||||||
# Warning if video is unavailable
|
# Warning if video is unavailable
|
||||||
warning = self._html_search_regex(
|
warning = self._html_search_regex(
|
||||||
r'<div class="videoUnModer">(.*?)</div>', video_page,
|
r'<div class="videoUnModer">(.*?)</div>', webpage,
|
||||||
'warning message', default=None)
|
'warning message', default=None)
|
||||||
if warning is not None:
|
if warning is not None:
|
||||||
self._downloader.report_warning(
|
self._downloader.report_warning(
|
||||||
|
@ -182,84 +171,32 @@ class SmotriIE(InfoExtractor):
|
||||||
(video_id, warning))
|
(video_id, warning))
|
||||||
|
|
||||||
# Adult content
|
# Adult content
|
||||||
if re.search('EroConfirmText">', video_page) is not None:
|
if re.search('EroConfirmText">', webpage) is not None:
|
||||||
self.report_age_confirmation()
|
self.report_age_confirmation()
|
||||||
confirm_string = self._html_search_regex(
|
confirm_string = self._html_search_regex(
|
||||||
r'<a href="/video/view/\?id=%s&confirm=([^"]+)" title="[^"]+">' % video_id,
|
r'<a href="/video/view/\?id=%s&confirm=([^"]+)" title="[^"]+">' % video_id,
|
||||||
video_page, 'confirm string')
|
webpage, 'confirm string')
|
||||||
confirm_url = video_page_url + '&confirm=%s' % confirm_string
|
confirm_url = webpage_url + '&confirm=%s' % confirm_string
|
||||||
video_page = self._download_webpage(confirm_url, video_id, 'Downloading video page (age confirmed)')
|
webpage = self._download_webpage(confirm_url, video_id, 'Downloading video page (age confirmed)')
|
||||||
adult_content = True
|
adult_content = True
|
||||||
else:
|
else:
|
||||||
adult_content = False
|
adult_content = False
|
||||||
|
|
||||||
# Extract the rest of meta data
|
view_count = self._html_search_regex(
|
||||||
video_title = self._search_meta('name', video_page, 'title')
|
|
||||||
if not video_title:
|
|
||||||
video_title = os.path.splitext(url_basename(video_url))[0]
|
|
||||||
|
|
||||||
video_description = self._search_meta('description', video_page)
|
|
||||||
END_TEXT = ' на сайте Smotri.com'
|
|
||||||
if video_description and video_description.endswith(END_TEXT):
|
|
||||||
video_description = video_description[:-len(END_TEXT)]
|
|
||||||
START_TEXT = 'Смотреть онлайн ролик '
|
|
||||||
if video_description and video_description.startswith(START_TEXT):
|
|
||||||
video_description = video_description[len(START_TEXT):]
|
|
||||||
video_thumbnail = self._search_meta('thumbnail', video_page)
|
|
||||||
|
|
||||||
upload_date_str = self._search_meta('uploadDate', video_page, 'upload date')
|
|
||||||
if upload_date_str:
|
|
||||||
upload_date_m = re.search(r'(?P<year>\d{4})\.(?P<month>\d{2})\.(?P<day>\d{2})T', upload_date_str)
|
|
||||||
video_upload_date = (
|
|
||||||
(
|
|
||||||
upload_date_m.group('year') +
|
|
||||||
upload_date_m.group('month') +
|
|
||||||
upload_date_m.group('day')
|
|
||||||
)
|
|
||||||
if upload_date_m else None
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
video_upload_date = None
|
|
||||||
|
|
||||||
duration_str = self._search_meta('duration', video_page)
|
|
||||||
if duration_str:
|
|
||||||
duration_m = re.search(r'T(?P<hours>[0-9]{2})H(?P<minutes>[0-9]{2})M(?P<seconds>[0-9]{2})S', duration_str)
|
|
||||||
video_duration = (
|
|
||||||
(
|
|
||||||
(int(duration_m.group('hours')) * 60 * 60) +
|
|
||||||
(int(duration_m.group('minutes')) * 60) +
|
|
||||||
int(duration_m.group('seconds'))
|
|
||||||
)
|
|
||||||
if duration_m else None
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
video_duration = None
|
|
||||||
|
|
||||||
video_uploader = self._html_search_regex(
|
|
||||||
'<div class="DescrUser"><div>Автор.*?onmouseover="popup_user_info[^"]+">(.*?)</a>',
|
|
||||||
video_page, 'uploader', fatal=False, flags=re.MULTILINE|re.DOTALL)
|
|
||||||
|
|
||||||
video_uploader_id = self._html_search_regex(
|
|
||||||
'<div class="DescrUser"><div>Автор.*?onmouseover="popup_user_info\\(.*?\'([^\']+)\'\\);">',
|
|
||||||
video_page, 'uploader id', fatal=False, flags=re.MULTILINE|re.DOTALL)
|
|
||||||
|
|
||||||
video_view_count = self._html_search_regex(
|
|
||||||
'Общее количество просмотров.*?<span class="Number">(\\d+)</span>',
|
'Общее количество просмотров.*?<span class="Number">(\\d+)</span>',
|
||||||
video_page, 'view count', fatal=False, flags=re.MULTILINE|re.DOTALL)
|
webpage, 'view count', fatal=False, flags=re.MULTILINE | re.DOTALL)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'url': video_url,
|
'url': video_url,
|
||||||
'title': video_title,
|
'title': title,
|
||||||
'thumbnail': video_thumbnail,
|
'thumbnail': thumbnail,
|
||||||
'description': video_description,
|
'uploader': uploader,
|
||||||
'uploader': video_uploader,
|
'upload_date': upload_date,
|
||||||
'upload_date': video_upload_date,
|
'uploader_id': uploader_id,
|
||||||
'uploader_id': video_uploader_id,
|
'duration': duration,
|
||||||
'duration': video_duration,
|
'view_count': int_or_none(view_count),
|
||||||
'view_count': int_or_none(video_view_count),
|
|
||||||
'age_limit': 18 if adult_content else 0,
|
'age_limit': 18 if adult_content else 0,
|
||||||
'video_page_url': video_page_url
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -93,4 +93,3 @@ class SportDeutschlandIE(InfoExtractor):
|
||||||
'rtmp_live': asset.get('live'),
|
'rtmp_live': asset.get('live'),
|
||||||
'timestamp': parse_iso8601(asset.get('date')),
|
'timestamp': parse_iso8601(asset.get('date')),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ class SubtitlesInfoExtractor(InfoExtractor):
|
||||||
|
|
||||||
sub_lang_list = {}
|
sub_lang_list = {}
|
||||||
for sub_lang in requested_langs:
|
for sub_lang in requested_langs:
|
||||||
if not sub_lang in available_subs_list:
|
if sub_lang not in available_subs_list:
|
||||||
self._downloader.report_warning(u'no closed captions found in the specified language "%s"' % sub_lang)
|
self._downloader.report_warning(u'no closed captions found in the specified language "%s"' % sub_lang)
|
||||||
continue
|
continue
|
||||||
sub_lang_list[sub_lang] = available_subs_list[sub_lang]
|
sub_lang_list[sub_lang] = available_subs_list[sub_lang]
|
||||||
|
|
|
@ -35,6 +35,7 @@ class ThePlatformIE(InfoExtractor):
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
|
@ -48,7 +49,6 @@ class ThePlatformIE(InfoExtractor):
|
||||||
smil_url = ('http://link.theplatform.com/s/dJ5BDC/{0}/meta.smil?'
|
smil_url = ('http://link.theplatform.com/s/dJ5BDC/{0}/meta.smil?'
|
||||||
'format=smil&mbr=true'.format(video_id))
|
'format=smil&mbr=true'.format(video_id))
|
||||||
|
|
||||||
|
|
||||||
meta = self._download_xml(smil_url, video_id)
|
meta = self._download_xml(smil_url, video_id)
|
||||||
try:
|
try:
|
||||||
error_msg = next(
|
error_msg = next(
|
||||||
|
|
|
@ -63,4 +63,3 @@ class TriluliluIE(InfoExtractor):
|
||||||
'description': description,
|
'description': description,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,4 +35,3 @@ class ViceIE(InfoExtractor):
|
||||||
except ExtractorError:
|
except ExtractorError:
|
||||||
raise ExtractorError('The page doesn\'t contain a video', expected=True)
|
raise ExtractorError('The page doesn\'t contain a video', expected=True)
|
||||||
return self.url_result(ooyala_url, ie='Ooyala')
|
return self.url_result(ooyala_url, ie='Ooyala')
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VideofyMeIE(InfoExtractor):
|
class VideofyMeIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(www\.videofy\.me/.+?|p\.videofy\.me/v)/(?P<id>\d+)(&|#|$)'
|
_VALID_URL = r'https?://(www\.videofy\.me/.+?|p\.videofy\.me/v)/(?P<id>\d+)(&|#|$)'
|
||||||
IE_NAME = u'videofy.me'
|
IE_NAME = u'videofy.me'
|
||||||
|
|
|
@ -30,4 +30,3 @@ class VidziIE(InfoExtractor):
|
||||||
'title': title,
|
'title': title,
|
||||||
'url': video_url,
|
'url': video_url,
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,4 +51,3 @@ class WorldStarHipHopIE(InfoExtractor):
|
||||||
'title': video_title,
|
'title': video_title,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,4 +47,3 @@ class XBefIE(InfoExtractor):
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ from ..utils import (
|
||||||
uppercase_escape,
|
uppercase_escape,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class YoutubeBaseInfoExtractor(InfoExtractor):
|
class YoutubeBaseInfoExtractor(InfoExtractor):
|
||||||
"""Provide base functions for Youtube extractors"""
|
"""Provide base functions for Youtube extractors"""
|
||||||
_LOGIN_URL = 'https://accounts.google.com/ServiceLogin'
|
_LOGIN_URL = 'https://accounts.google.com/ServiceLogin'
|
||||||
|
@ -180,8 +181,10 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
|
||||||
'next_url': '/',
|
'next_url': '/',
|
||||||
'action_confirm': 'Confirm',
|
'action_confirm': 'Confirm',
|
||||||
}
|
}
|
||||||
req = compat_urllib_request.Request(self._AGE_URL,
|
req = compat_urllib_request.Request(
|
||||||
compat_urllib_parse.urlencode(age_form).encode('ascii'))
|
self._AGE_URL,
|
||||||
|
compat_urllib_parse.urlencode(age_form).encode('ascii')
|
||||||
|
)
|
||||||
|
|
||||||
self._download_webpage(
|
self._download_webpage(
|
||||||
req, None,
|
req, None,
|
||||||
|
@ -651,6 +654,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):
|
||||||
|
|
||||||
def _extract_from_m3u8(self, manifest_url, video_id):
|
def _extract_from_m3u8(self, manifest_url, video_id):
|
||||||
url_map = {}
|
url_map = {}
|
||||||
|
|
||||||
def _get_urls(_manifest):
|
def _get_urls(_manifest):
|
||||||
lines = _manifest.split('\n')
|
lines = _manifest.split('\n')
|
||||||
urls = filter(lambda l: l and not l.startswith('#'),
|
urls = filter(lambda l: l and not l.startswith('#'),
|
||||||
|
@ -974,6 +978,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):
|
||||||
dash_manifest_url = video_info.get('dashmpd')[0]
|
dash_manifest_url = video_info.get('dashmpd')[0]
|
||||||
else:
|
else:
|
||||||
dash_manifest_url = ytplayer_config['args']['dashmpd']
|
dash_manifest_url = ytplayer_config['args']['dashmpd']
|
||||||
|
|
||||||
def decrypt_sig(mobj):
|
def decrypt_sig(mobj):
|
||||||
s = mobj.group(1)
|
s = mobj.group(1)
|
||||||
dec_s = self._decrypt_signature(s, video_id, player_url, age_gate)
|
dec_s = self._decrypt_signature(s, video_id, player_url, age_gate)
|
||||||
|
@ -1033,6 +1038,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistIE(YoutubeBaseInfoExtractor):
|
class YoutubePlaylistIE(YoutubeBaseInfoExtractor):
|
||||||
IE_DESC = 'YouTube.com playlists'
|
IE_DESC = 'YouTube.com playlists'
|
||||||
_VALID_URL = r"""(?x)(?:
|
_VALID_URL = r"""(?x)(?:
|
||||||
|
@ -1333,8 +1339,10 @@ class YoutubeUserIE(InfoExtractor):
|
||||||
# Don't return True if the url can be extracted with other youtube
|
# Don't return True if the url can be extracted with other youtube
|
||||||
# extractor, the regex would is too permissive and it would match.
|
# extractor, the regex would is too permissive and it would match.
|
||||||
other_ies = iter(klass for (name, klass) in globals().items() if name.endswith('IE') and klass is not cls)
|
other_ies = iter(klass for (name, klass) in globals().items() if name.endswith('IE') and klass is not cls)
|
||||||
if any(ie.suitable(url) for ie in other_ies): return False
|
if any(ie.suitable(url) for ie in other_ies):
|
||||||
else: return super(YoutubeUserIE, cls).suitable(url)
|
return False
|
||||||
|
else:
|
||||||
|
return super(YoutubeUserIE, cls).suitable(url)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
# Extract username
|
# Extract username
|
||||||
|
@ -1557,12 +1565,14 @@ class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor):
|
||||||
paging = mobj.group('paging')
|
paging = mobj.group('paging')
|
||||||
return self.playlist_result(feed_entries, playlist_title=self._PLAYLIST_TITLE)
|
return self.playlist_result(feed_entries, playlist_title=self._PLAYLIST_TITLE)
|
||||||
|
|
||||||
|
|
||||||
class YoutubeRecommendedIE(YoutubeFeedsInfoExtractor):
|
class YoutubeRecommendedIE(YoutubeFeedsInfoExtractor):
|
||||||
IE_DESC = 'YouTube.com recommended videos, ":ytrec" for short (requires authentication)'
|
IE_DESC = 'YouTube.com recommended videos, ":ytrec" for short (requires authentication)'
|
||||||
_VALID_URL = r'https?://www\.youtube\.com/feed/recommended|:ytrec(?:ommended)?'
|
_VALID_URL = r'https?://www\.youtube\.com/feed/recommended|:ytrec(?:ommended)?'
|
||||||
_FEED_NAME = 'recommended'
|
_FEED_NAME = 'recommended'
|
||||||
_PLAYLIST_TITLE = 'Youtube Recommended videos'
|
_PLAYLIST_TITLE = 'Youtube Recommended videos'
|
||||||
|
|
||||||
|
|
||||||
class YoutubeWatchLaterIE(YoutubeFeedsInfoExtractor):
|
class YoutubeWatchLaterIE(YoutubeFeedsInfoExtractor):
|
||||||
IE_DESC = 'Youtube watch later list, ":ytwatchlater" for short (requires authentication)'
|
IE_DESC = 'Youtube watch later list, ":ytwatchlater" for short (requires authentication)'
|
||||||
_VALID_URL = r'https?://www\.youtube\.com/feed/watch_later|:ytwatchlater'
|
_VALID_URL = r'https?://www\.youtube\.com/feed/watch_later|:ytwatchlater'
|
||||||
|
@ -1570,6 +1580,7 @@ class YoutubeWatchLaterIE(YoutubeFeedsInfoExtractor):
|
||||||
_PLAYLIST_TITLE = 'Youtube Watch Later'
|
_PLAYLIST_TITLE = 'Youtube Watch Later'
|
||||||
_PERSONAL_FEED = True
|
_PERSONAL_FEED = True
|
||||||
|
|
||||||
|
|
||||||
class YoutubeHistoryIE(YoutubeFeedsInfoExtractor):
|
class YoutubeHistoryIE(YoutubeFeedsInfoExtractor):
|
||||||
IE_DESC = 'Youtube watch history, ":ythistory" for short (requires authentication)'
|
IE_DESC = 'Youtube watch history, ":ythistory" for short (requires authentication)'
|
||||||
_VALID_URL = 'https?://www\.youtube\.com/feed/history|:ythistory'
|
_VALID_URL = 'https?://www\.youtube\.com/feed/history|:ythistory'
|
||||||
|
@ -1577,6 +1588,7 @@ class YoutubeHistoryIE(YoutubeFeedsInfoExtractor):
|
||||||
_PERSONAL_FEED = True
|
_PERSONAL_FEED = True
|
||||||
_PLAYLIST_TITLE = 'Youtube Watch History'
|
_PLAYLIST_TITLE = 'Youtube Watch History'
|
||||||
|
|
||||||
|
|
||||||
class YoutubeFavouritesIE(YoutubeBaseInfoExtractor):
|
class YoutubeFavouritesIE(YoutubeBaseInfoExtractor):
|
||||||
IE_NAME = 'youtube:favorites'
|
IE_NAME = 'youtube:favorites'
|
||||||
IE_DESC = 'YouTube.com favourite videos, ":ytfav" for short (requires authentication)'
|
IE_DESC = 'YouTube.com favourite videos, ":ytfav" for short (requires authentication)'
|
||||||
|
|
|
@ -26,4 +26,3 @@ class ExecAfterDownloadPP(PostProcessor):
|
||||||
'Command returned error code %d' % retCode)
|
'Command returned error code %d' % retCode)
|
||||||
|
|
||||||
return None, information # by default, keep file and do nothing
|
return None, information # by default, keep file and do nothing
|
||||||
|
|
||||||
|
|
|
@ -108,4 +108,3 @@ class XAttrMetadataPP(PostProcessor):
|
||||||
except (subprocess.CalledProcessError, OSError):
|
except (subprocess.CalledProcessError, OSError):
|
||||||
self._downloader.report_error("This filesystem doesn't support extended attributes. (You may have to enable them in your /etc/fstab)")
|
self._downloader.report_error("This filesystem doesn't support extended attributes. (You may have to enable them in your /etc/fstab)")
|
||||||
return False, info
|
return False, info
|
||||||
|
|
||||||
|
|
|
@ -827,4 +827,3 @@ class SWFInterpreter(object):
|
||||||
|
|
||||||
avm_class.method_pyfunctions[func_name] = resfunc
|
avm_class.method_pyfunctions[func_name] = resfunc
|
||||||
return resfunc
|
return resfunc
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,17 @@ from .utils import (
|
||||||
)
|
)
|
||||||
from .version import __version__
|
from .version import __version__
|
||||||
|
|
||||||
|
|
||||||
def rsa_verify(message, signature, key):
|
def rsa_verify(message, signature, key):
|
||||||
from struct import pack
|
from struct import pack
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from sys import version_info
|
from sys import version_info
|
||||||
|
|
||||||
def b(x):
|
def b(x):
|
||||||
if version_info[0] == 2: return x
|
if version_info[0] == 2:
|
||||||
else: return x.encode('latin1')
|
return x
|
||||||
|
else:
|
||||||
|
return x.encode('latin1')
|
||||||
assert(type(message) == type(b('')))
|
assert(type(message) == type(b('')))
|
||||||
block_size = 0
|
block_size = 0
|
||||||
n = key[0]
|
n = key[0]
|
||||||
|
@ -32,13 +36,17 @@ def rsa_verify(message, signature, key):
|
||||||
raw_bytes.insert(0, pack("B", signature & 0xFF))
|
raw_bytes.insert(0, pack("B", signature & 0xFF))
|
||||||
signature >>= 8
|
signature >>= 8
|
||||||
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
|
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
|
||||||
if signature[0:2] != b('\x00\x01'): return False
|
if signature[0:2] != b('\x00\x01'):
|
||||||
|
return False
|
||||||
signature = signature[2:]
|
signature = signature[2:]
|
||||||
if not b('\x00') in signature: return False
|
if not b('\x00') in signature:
|
||||||
|
return False
|
||||||
signature = signature[signature.index(b('\x00')) + 1:]
|
signature = signature[signature.index(b('\x00')) + 1:]
|
||||||
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False
|
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')):
|
||||||
|
return False
|
||||||
signature = signature[19:]
|
signature = signature[19:]
|
||||||
if signature != sha256(message).digest(): return False
|
if signature != sha256(message).digest():
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,7 +66,8 @@ def update_self(to_screen, verbose):
|
||||||
try:
|
try:
|
||||||
newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip()
|
newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip()
|
||||||
except:
|
except:
|
||||||
if verbose: to_screen(compat_str(traceback.format_exc()))
|
if verbose:
|
||||||
|
to_screen(compat_str(traceback.format_exc()))
|
||||||
to_screen(u'ERROR: can\'t find the current version. Please try again later.')
|
to_screen(u'ERROR: can\'t find the current version. Please try again later.')
|
||||||
return
|
return
|
||||||
if newversion == __version__:
|
if newversion == __version__:
|
||||||
|
@ -70,7 +79,8 @@ def update_self(to_screen, verbose):
|
||||||
versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8')
|
versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8')
|
||||||
versions_info = json.loads(versions_info)
|
versions_info = json.loads(versions_info)
|
||||||
except:
|
except:
|
||||||
if verbose: to_screen(compat_str(traceback.format_exc()))
|
if verbose:
|
||||||
|
to_screen(compat_str(traceback.format_exc()))
|
||||||
to_screen(u'ERROR: can\'t obtain versions info. Please try again later.')
|
to_screen(u'ERROR: can\'t obtain versions info. Please try again later.')
|
||||||
return
|
return
|
||||||
if not 'signature' in versions_info:
|
if not 'signature' in versions_info:
|
||||||
|
@ -118,7 +128,8 @@ def update_self(to_screen, verbose):
|
||||||
newcontent = urlh.read()
|
newcontent = urlh.read()
|
||||||
urlh.close()
|
urlh.close()
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
if verbose: to_screen(compat_str(traceback.format_exc()))
|
if verbose:
|
||||||
|
to_screen(compat_str(traceback.format_exc()))
|
||||||
to_screen(u'ERROR: unable to download latest version')
|
to_screen(u'ERROR: unable to download latest version')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -131,7 +142,8 @@ def update_self(to_screen, verbose):
|
||||||
with open(exe + '.new', 'wb') as outf:
|
with open(exe + '.new', 'wb') as outf:
|
||||||
outf.write(newcontent)
|
outf.write(newcontent)
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
if verbose: to_screen(compat_str(traceback.format_exc()))
|
if verbose:
|
||||||
|
to_screen(compat_str(traceback.format_exc()))
|
||||||
to_screen(u'ERROR: unable to write the new version')
|
to_screen(u'ERROR: unable to write the new version')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -150,7 +162,8 @@ start /b "" cmd /c del "%%~f0"&exit /b"
|
||||||
subprocess.Popen([bat]) # Continues to run in the background
|
subprocess.Popen([bat]) # Continues to run in the background
|
||||||
return # Do not show premature success messages
|
return # Do not show premature success messages
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
if verbose: to_screen(compat_str(traceback.format_exc()))
|
if verbose:
|
||||||
|
to_screen(compat_str(traceback.format_exc()))
|
||||||
to_screen(u'ERROR: unable to overwrite current version')
|
to_screen(u'ERROR: unable to overwrite current version')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -161,7 +174,8 @@ start /b "" cmd /c del "%%~f0"&exit /b"
|
||||||
newcontent = urlh.read()
|
newcontent = urlh.read()
|
||||||
urlh.close()
|
urlh.close()
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
if verbose: to_screen(compat_str(traceback.format_exc()))
|
if verbose:
|
||||||
|
to_screen(compat_str(traceback.format_exc()))
|
||||||
to_screen(u'ERROR: unable to download latest version')
|
to_screen(u'ERROR: unable to download latest version')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -174,12 +188,14 @@ start /b "" cmd /c del "%%~f0"&exit /b"
|
||||||
with open(filename, 'wb') as outf:
|
with open(filename, 'wb') as outf:
|
||||||
outf.write(newcontent)
|
outf.write(newcontent)
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
if verbose: to_screen(compat_str(traceback.format_exc()))
|
if verbose:
|
||||||
|
to_screen(compat_str(traceback.format_exc()))
|
||||||
to_screen(u'ERROR: unable to overwrite current version')
|
to_screen(u'ERROR: unable to overwrite current version')
|
||||||
return
|
return
|
||||||
|
|
||||||
to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')
|
to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')
|
||||||
|
|
||||||
|
|
||||||
def get_notes(versions, fromVersion):
|
def get_notes(versions, fromVersion):
|
||||||
notes = []
|
notes = []
|
||||||
for v, vdata in sorted(versions.items()):
|
for v, vdata in sorted(versions.items()):
|
||||||
|
@ -187,6 +203,7 @@ def get_notes(versions, fromVersion):
|
||||||
notes.extend(vdata.get('notes', []))
|
notes.extend(vdata.get('notes', []))
|
||||||
return notes
|
return notes
|
||||||
|
|
||||||
|
|
||||||
def print_notes(to_screen, versions, fromVersion=__version__):
|
def print_notes(to_screen, versions, fromVersion=__version__):
|
||||||
notes = get_notes(versions, fromVersion)
|
notes = get_notes(versions, fromVersion)
|
||||||
if notes:
|
if notes:
|
||||||
|
|
|
@ -56,6 +56,7 @@ std_headers = {
|
||||||
'Accept-Language': 'en-us,en;q=0.5',
|
'Accept-Language': 'en-us,en;q=0.5',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def preferredencoding():
|
def preferredencoding():
|
||||||
"""Get preferred encoding.
|
"""Get preferred encoding.
|
||||||
|
|
||||||
|
@ -146,6 +147,8 @@ else:
|
||||||
|
|
||||||
# On python2.6 the xml.etree.ElementTree.Element methods don't support
|
# On python2.6 the xml.etree.ElementTree.Element methods don't support
|
||||||
# the namespace parameter
|
# the namespace parameter
|
||||||
|
|
||||||
|
|
||||||
def xpath_with_ns(path, ns_map):
|
def xpath_with_ns(path, ns_map):
|
||||||
components = [c.split(':') for c in path.split('/')]
|
components = [c.split(':') for c in path.split('/')]
|
||||||
replaced = []
|
replaced = []
|
||||||
|
@ -256,6 +259,7 @@ def timeconvert(timestr):
|
||||||
timestamp = email.utils.mktime_tz(timetuple)
|
timestamp = email.utils.mktime_tz(timetuple)
|
||||||
return timestamp
|
return timestamp
|
||||||
|
|
||||||
|
|
||||||
def sanitize_filename(s, restricted=False, is_id=False):
|
def sanitize_filename(s, restricted=False, is_id=False):
|
||||||
"""Sanitizes a string so it could be used as part of a filename.
|
"""Sanitizes a string so it could be used as part of a filename.
|
||||||
If restricted is set, use a stricter subset of allowed characters.
|
If restricted is set, use a stricter subset of allowed characters.
|
||||||
|
@ -288,6 +292,7 @@ def sanitize_filename(s, restricted=False, is_id=False):
|
||||||
result = '_'
|
result = '_'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def orderedSet(iterable):
|
def orderedSet(iterable):
|
||||||
""" Remove all duplicates from the input iterable """
|
""" Remove all duplicates from the input iterable """
|
||||||
res = []
|
res = []
|
||||||
|
@ -372,6 +377,7 @@ def decodeOption(optval):
|
||||||
assert isinstance(optval, compat_str)
|
assert isinstance(optval, compat_str)
|
||||||
return optval
|
return optval
|
||||||
|
|
||||||
|
|
||||||
def formatSeconds(secs):
|
def formatSeconds(secs):
|
||||||
if secs > 3600:
|
if secs > 3600:
|
||||||
return '%d:%02d:%02d' % (secs // 3600, (secs % 3600) // 60, secs % 60)
|
return '%d:%02d:%02d' % (secs // 3600, (secs % 3600) // 60, secs % 60)
|
||||||
|
@ -424,6 +430,7 @@ def make_HTTPS_handler(opts_no_check_certificate, **kwargs):
|
||||||
|
|
||||||
class ExtractorError(Exception):
|
class ExtractorError(Exception):
|
||||||
"""Error during info extraction."""
|
"""Error during info extraction."""
|
||||||
|
|
||||||
def __init__(self, msg, tb=None, expected=False, cause=None, video_id=None):
|
def __init__(self, msg, tb=None, expected=False, cause=None, video_id=None):
|
||||||
""" tb, if given, is the original traceback (so that it can be printed out).
|
""" tb, if given, is the original traceback (so that it can be printed out).
|
||||||
If expected is set, this is a normal error message and most likely not a bug in youtube-dl.
|
If expected is set, this is a normal error message and most likely not a bug in youtube-dl.
|
||||||
|
@ -468,6 +475,7 @@ class DownloadError(Exception):
|
||||||
configured to continue on errors. They will contain the appropriate
|
configured to continue on errors. They will contain the appropriate
|
||||||
error message.
|
error message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, msg, exc_info=None):
|
def __init__(self, msg, exc_info=None):
|
||||||
""" exc_info, if given, is the original exception that caused the trouble (as returned by sys.exc_info()). """
|
""" exc_info, if given, is the original exception that caused the trouble (as returned by sys.exc_info()). """
|
||||||
super(DownloadError, self).__init__(msg)
|
super(DownloadError, self).__init__(msg)
|
||||||
|
@ -489,9 +497,11 @@ class PostProcessingError(Exception):
|
||||||
This exception may be raised by PostProcessor's .run() method to
|
This exception may be raised by PostProcessor's .run() method to
|
||||||
indicate an error in the postprocessing task.
|
indicate an error in the postprocessing task.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
|
|
||||||
class MaxDownloadsReached(Exception):
|
class MaxDownloadsReached(Exception):
|
||||||
""" --max-downloads limit has been reached. """
|
""" --max-downloads limit has been reached. """
|
||||||
pass
|
pass
|
||||||
|
@ -521,6 +531,7 @@ class ContentTooShortError(Exception):
|
||||||
self.downloaded = downloaded
|
self.downloaded = downloaded
|
||||||
self.expected = expected
|
self.expected = expected
|
||||||
|
|
||||||
|
|
||||||
class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
|
class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
|
||||||
"""Handler for HTTP requests and responses.
|
"""Handler for HTTP requests and responses.
|
||||||
|
|
||||||
|
@ -681,6 +692,7 @@ def unified_strdate(date_str):
|
||||||
upload_date = datetime.datetime(*timetuple[:6]).strftime('%Y%m%d')
|
upload_date = datetime.datetime(*timetuple[:6]).strftime('%Y%m%d')
|
||||||
return upload_date
|
return upload_date
|
||||||
|
|
||||||
|
|
||||||
def determine_ext(url, default_ext='unknown_video'):
|
def determine_ext(url, default_ext='unknown_video'):
|
||||||
if url is None:
|
if url is None:
|
||||||
return default_ext
|
return default_ext
|
||||||
|
@ -690,9 +702,11 @@ def determine_ext(url, default_ext='unknown_video'):
|
||||||
else:
|
else:
|
||||||
return default_ext
|
return default_ext
|
||||||
|
|
||||||
|
|
||||||
def subtitles_filename(filename, sub_lang, sub_format):
|
def subtitles_filename(filename, sub_lang, sub_format):
|
||||||
return filename.rsplit('.', 1)[0] + '.' + sub_lang + '.' + sub_format
|
return filename.rsplit('.', 1)[0] + '.' + sub_lang + '.' + sub_format
|
||||||
|
|
||||||
|
|
||||||
def date_from_str(date_str):
|
def date_from_str(date_str):
|
||||||
"""
|
"""
|
||||||
Return a datetime object from a string in the format YYYYMMDD or
|
Return a datetime object from a string in the format YYYYMMDD or
|
||||||
|
@ -719,6 +733,7 @@ def date_from_str(date_str):
|
||||||
return today + delta
|
return today + delta
|
||||||
return datetime.datetime.strptime(date_str, "%Y%m%d").date()
|
return datetime.datetime.strptime(date_str, "%Y%m%d").date()
|
||||||
|
|
||||||
|
|
||||||
def hyphenate_date(date_str):
|
def hyphenate_date(date_str):
|
||||||
"""
|
"""
|
||||||
Convert a date in 'YYYYMMDD' format to 'YYYY-MM-DD' format"""
|
Convert a date in 'YYYYMMDD' format to 'YYYY-MM-DD' format"""
|
||||||
|
@ -728,8 +743,10 @@ def hyphenate_date(date_str):
|
||||||
else:
|
else:
|
||||||
return date_str
|
return date_str
|
||||||
|
|
||||||
|
|
||||||
class DateRange(object):
|
class DateRange(object):
|
||||||
"""Represents a time interval between two dates"""
|
"""Represents a time interval between two dates"""
|
||||||
|
|
||||||
def __init__(self, start=None, end=None):
|
def __init__(self, start=None, end=None):
|
||||||
"""start and end must be strings in the format accepted by date"""
|
"""start and end must be strings in the format accepted by date"""
|
||||||
if start is not None:
|
if start is not None:
|
||||||
|
@ -742,15 +759,18 @@ class DateRange(object):
|
||||||
self.end = datetime.datetime.max.date()
|
self.end = datetime.datetime.max.date()
|
||||||
if self.start > self.end:
|
if self.start > self.end:
|
||||||
raise ValueError('Date range: "%s" , the start date must be before the end date' % self)
|
raise ValueError('Date range: "%s" , the start date must be before the end date' % self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def day(cls, day):
|
def day(cls, day):
|
||||||
"""Returns a range that only contains the given day"""
|
"""Returns a range that only contains the given day"""
|
||||||
return cls(day, day)
|
return cls(day, day)
|
||||||
|
|
||||||
def __contains__(self, date):
|
def __contains__(self, date):
|
||||||
"""Check if the date is in the range"""
|
"""Check if the date is in the range"""
|
||||||
if not isinstance(date, datetime.date):
|
if not isinstance(date, datetime.date):
|
||||||
date = date_from_str(date)
|
date = date_from_str(date)
|
||||||
return self.start <= date <= self.end
|
return self.start <= date <= self.end
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s - %s' % (self.start.isoformat(), self.end.isoformat())
|
return '%s - %s' % (self.start.isoformat(), self.end.isoformat())
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue