Merge branch 'master' of https://github.com/rg3/youtube-dl
This commit is contained in:
commit
a00c9e7242
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@ README.txt
|
|||||||
youtube-dl.1
|
youtube-dl.1
|
||||||
youtube-dl.bash-completion
|
youtube-dl.bash-completion
|
||||||
youtube-dl.fish
|
youtube-dl.fish
|
||||||
|
youtube_dl/extractor/lazy_extractors.py
|
||||||
youtube-dl
|
youtube-dl
|
||||||
youtube-dl.exe
|
youtube-dl.exe
|
||||||
youtube-dl.tar.gz
|
youtube-dl.tar.gz
|
||||||
|
8
Makefile
8
Makefile
@ -1,7 +1,7 @@
|
|||||||
all: youtube-dl README.md CONTRIBUTING.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish supportedsites
|
all: youtube-dl README.md CONTRIBUTING.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish supportedsites
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf youtube-dl.1.temp.md youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz youtube-dl.zsh youtube-dl.fish *.dump *.part *.info.json *.mp4 *.flv *.mp3 *.avi CONTRIBUTING.md.tmp ISSUE_TEMPLATE.md.tmp youtube-dl youtube-dl.exe
|
rm -rf youtube-dl.1.temp.md youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz youtube-dl.zsh youtube-dl.fish youtube_dl/extractor/lazy_extractors.py *.dump *.part *.info.json *.mp4 *.flv *.mp3 *.avi CONTRIBUTING.md.tmp ISSUE_TEMPLATE.md.tmp youtube-dl youtube-dl.exe
|
||||||
find . -name "*.pyc" -delete
|
find . -name "*.pyc" -delete
|
||||||
find . -name "*.class" -delete
|
find . -name "*.class" -delete
|
||||||
|
|
||||||
@ -88,6 +88,12 @@ youtube-dl.fish: youtube_dl/*.py youtube_dl/*/*.py devscripts/fish-completion.in
|
|||||||
|
|
||||||
fish-completion: youtube-dl.fish
|
fish-completion: youtube-dl.fish
|
||||||
|
|
||||||
|
lazy-extractors: youtube_dl/extractor/lazy_extractors.py
|
||||||
|
|
||||||
|
_EXTRACTOR_FILES != find youtube_dl/extractor -iname '*.py' -and -not -iname 'lazy_extractors.py'
|
||||||
|
youtube_dl/extractor/lazy_extractors.py: devscripts/make_lazy_extractors.py devscripts/lazy_load_template.py $(_EXTRACTOR_FILES)
|
||||||
|
$(PYTHON) devscripts/make_lazy_extractors.py $@
|
||||||
|
|
||||||
youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish
|
youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish
|
||||||
@tar -czf youtube-dl.tar.gz --transform "s|^|youtube-dl/|" --owner 0 --group 0 \
|
@tar -czf youtube-dl.tar.gz --transform "s|^|youtube-dl/|" --owner 0 --group 0 \
|
||||||
--exclude '*.DS_Store' \
|
--exclude '*.DS_Store' \
|
||||||
|
@ -899,14 +899,14 @@ After you have ensured this site is distributing it's content legally, you can f
|
|||||||
# TODO more properties (see youtube_dl/extractor/common.py)
|
# TODO more properties (see youtube_dl/extractor/common.py)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
5. Add an import in [`youtube_dl/extractor/__init__.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/__init__.py).
|
5. Add an import in [`youtube_dl/extractor/extractors.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/extractors.py).
|
||||||
6. Run `python test/test_download.py TestDownload.test_YourExtractor`. This *should fail* at first, but you can continually re-run it until you're done. If you decide to add more than one test, then rename ``_TEST`` to ``_TESTS`` and make it into a list of dictionaries. The tests will then be named `TestDownload.test_YourExtractor`, `TestDownload.test_YourExtractor_1`, `TestDownload.test_YourExtractor_2`, etc.
|
6. Run `python test/test_download.py TestDownload.test_YourExtractor`. This *should fail* at first, but you can continually re-run it until you're done. If you decide to add more than one test, then rename ``_TEST`` to ``_TESTS`` and make it into a list of dictionaries. The tests will then be named `TestDownload.test_YourExtractor`, `TestDownload.test_YourExtractor_1`, `TestDownload.test_YourExtractor_2`, etc.
|
||||||
7. Have a look at [`youtube_dl/extractor/common.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should and may return](https://github.com/rg3/youtube-dl/blob/58525c94d547be1c8167d16c298bdd75506db328/youtube_dl/extractor/common.py#L68-L226). Add tests and code for as many as you want.
|
7. Have a look at [`youtube_dl/extractor/common.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should and may return](https://github.com/rg3/youtube-dl/blob/58525c94d547be1c8167d16c298bdd75506db328/youtube_dl/extractor/common.py#L68-L226). Add tests and code for as many as you want.
|
||||||
8. Keep in mind that the only mandatory fields in info dict for successful extraction process are `id`, `title` and either `url` or `formats`, i.e. these are the critical data the extraction does not make any sense without. This means that [any field](https://github.com/rg3/youtube-dl/blob/58525c94d547be1c8167d16c298bdd75506db328/youtube_dl/extractor/common.py#L138-L226) apart from aforementioned mandatory ones should be treated **as optional** and extraction should be **tolerate** to situations when sources for these fields can potentially be unavailable (even if they always available at the moment) and **future-proof** in order not to break the extraction of general purpose mandatory fields. For example, if you have some intermediate dict `meta` that is a source of metadata and it has a key `summary` that you want to extract and put into resulting info dict as `description`, you should be ready that this key may be missing from the `meta` dict, i.e. you should extract it as `meta.get('summary')` and not `meta['summary']`. Similarly, you should pass `fatal=False` when extracting data from a webpage with `_search_regex/_html_search_regex`.
|
8. Keep in mind that the only mandatory fields in info dict for successful extraction process are `id`, `title` and either `url` or `formats`, i.e. these are the critical data the extraction does not make any sense without. This means that [any field](https://github.com/rg3/youtube-dl/blob/58525c94d547be1c8167d16c298bdd75506db328/youtube_dl/extractor/common.py#L138-L226) apart from aforementioned mandatory ones should be treated **as optional** and extraction should be **tolerate** to situations when sources for these fields can potentially be unavailable (even if they always available at the moment) and **future-proof** in order not to break the extraction of general purpose mandatory fields. For example, if you have some intermediate dict `meta` that is a source of metadata and it has a key `summary` that you want to extract and put into resulting info dict as `description`, you should be ready that this key may be missing from the `meta` dict, i.e. you should extract it as `meta.get('summary')` and not `meta['summary']`. Similarly, you should pass `fatal=False` when extracting data from a webpage with `_search_regex/_html_search_regex`.
|
||||||
9. Check the code with [flake8](https://pypi.python.org/pypi/flake8).
|
9. Check the code with [flake8](https://pypi.python.org/pypi/flake8).
|
||||||
10. When the tests pass, [add](http://git-scm.com/docs/git-add) the new files and [commit](http://git-scm.com/docs/git-commit) them and [push](http://git-scm.com/docs/git-push) the result, like this:
|
10. When the tests pass, [add](http://git-scm.com/docs/git-add) the new files and [commit](http://git-scm.com/docs/git-commit) them and [push](http://git-scm.com/docs/git-push) the result, like this:
|
||||||
|
|
||||||
$ git add youtube_dl/extractor/__init__.py
|
$ git add youtube_dl/extractor/extractors.py
|
||||||
$ git add youtube_dl/extractor/yourextractor.py
|
$ git add youtube_dl/extractor/yourextractor.py
|
||||||
$ git commit -m '[yourextractor] Add new extractor'
|
$ git commit -m '[yourextractor] Add new extractor'
|
||||||
$ git push origin yourextractor
|
$ git push origin yourextractor
|
||||||
|
19
devscripts/lazy_load_template.py
Normal file
19
devscripts/lazy_load_template.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class LazyLoadExtractor(object):
|
||||||
|
_module = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ie_key(cls):
|
||||||
|
return cls.__name__[:-2]
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
mod = __import__(cls._module, fromlist=(cls.__name__,))
|
||||||
|
real_cls = getattr(mod, cls.__name__)
|
||||||
|
instance = real_cls.__new__(real_cls)
|
||||||
|
instance.__init__(*args, **kwargs)
|
||||||
|
return instance
|
63
devscripts/make_lazy_extractors.py
Normal file
63
devscripts/make_lazy_extractors.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
from __future__ import unicode_literals, print_function
|
||||||
|
|
||||||
|
from inspect import getsource
|
||||||
|
import os
|
||||||
|
from os.path import dirname as dirn
|
||||||
|
import sys
|
||||||
|
|
||||||
|
print('WARNING: Lazy loading extractors is an experimental feature that may not always work', file=sys.stderr)
|
||||||
|
|
||||||
|
sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
|
||||||
|
|
||||||
|
lazy_extractors_filename = sys.argv[1]
|
||||||
|
if os.path.exists(lazy_extractors_filename):
|
||||||
|
os.remove(lazy_extractors_filename)
|
||||||
|
|
||||||
|
from youtube_dl.extractor import _ALL_CLASSES
|
||||||
|
from youtube_dl.extractor.common import InfoExtractor
|
||||||
|
|
||||||
|
with open('devscripts/lazy_load_template.py', 'rt') as f:
|
||||||
|
module_template = f.read()
|
||||||
|
|
||||||
|
module_contents = [module_template + '\n' + getsource(InfoExtractor.suitable)]
|
||||||
|
|
||||||
|
ie_template = '''
|
||||||
|
class {name}(LazyLoadExtractor):
|
||||||
|
_VALID_URL = {valid_url!r}
|
||||||
|
_module = '{module}'
|
||||||
|
'''
|
||||||
|
|
||||||
|
make_valid_template = '''
|
||||||
|
@classmethod
|
||||||
|
def _make_valid_url(cls):
|
||||||
|
return {valid_url!r}
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def build_lazy_ie(ie, name):
|
||||||
|
valid_url = getattr(ie, '_VALID_URL', None)
|
||||||
|
s = ie_template.format(
|
||||||
|
name=name,
|
||||||
|
valid_url=valid_url,
|
||||||
|
module=ie.__module__)
|
||||||
|
if ie.suitable.__func__ is not InfoExtractor.suitable.__func__:
|
||||||
|
s += '\n' + getsource(ie.suitable)
|
||||||
|
if hasattr(ie, '_make_valid_url'):
|
||||||
|
# search extractors
|
||||||
|
s += make_valid_template.format(valid_url=ie._make_valid_url())
|
||||||
|
return s
|
||||||
|
|
||||||
|
names = []
|
||||||
|
for ie in list(sorted(_ALL_CLASSES[:-1], key=lambda cls: cls.ie_key())) + _ALL_CLASSES[-1:]:
|
||||||
|
name = ie.ie_key() + 'IE'
|
||||||
|
src = build_lazy_ie(ie, name)
|
||||||
|
module_contents.append(src)
|
||||||
|
names.append(name)
|
||||||
|
|
||||||
|
module_contents.append(
|
||||||
|
'_ALL_CLASSES = [{0}]'.format(', '.join(names)))
|
||||||
|
|
||||||
|
module_src = '\n'.join(module_contents) + '\n'
|
||||||
|
|
||||||
|
with open(lazy_extractors_filename, 'wt') as f:
|
||||||
|
f.write(module_src)
|
@ -2,5 +2,5 @@
|
|||||||
universal = True
|
universal = True
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
exclude = youtube_dl/extractor/__init__.py,devscripts/buildserver.py,devscripts/make_issue_template.py,setup.py,build,.git
|
exclude = youtube_dl/extractor/__init__.py,devscripts/buildserver.py,devscripts/lazy_load_template.py,devscripts/make_issue_template.py,setup.py,build,.git
|
||||||
ignore = E402,E501,E731
|
ignore = E402,E501,E731
|
||||||
|
22
setup.py
22
setup.py
@ -8,11 +8,12 @@ import warnings
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from setuptools import setup
|
from setuptools import setup, Command
|
||||||
setuptools_available = True
|
setuptools_available = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from distutils.core import setup
|
from distutils.core import setup, Command
|
||||||
setuptools_available = False
|
setuptools_available = False
|
||||||
|
from distutils.spawn import spawn
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# This will create an exe that needs Microsoft Visual C++ 2008
|
# This will create an exe that needs Microsoft Visual C++ 2008
|
||||||
@ -70,6 +71,22 @@ else:
|
|||||||
else:
|
else:
|
||||||
params['scripts'] = ['bin/youtube-dl']
|
params['scripts'] = ['bin/youtube-dl']
|
||||||
|
|
||||||
|
class build_lazy_extractors(Command):
|
||||||
|
description = "Build the extractor lazy loading module"
|
||||||
|
user_options = []
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
spawn(
|
||||||
|
[sys.executable, 'devscripts/make_lazy_extractors.py', 'youtube_dl/extractor/lazy_extractors.py'],
|
||||||
|
dry_run=self.dry_run,
|
||||||
|
)
|
||||||
|
|
||||||
# Get the version from youtube_dl/version.py without importing the package
|
# Get the version from youtube_dl/version.py without importing the package
|
||||||
exec(compile(open('youtube_dl/version.py').read(),
|
exec(compile(open('youtube_dl/version.py').read(),
|
||||||
'youtube_dl/version.py', 'exec'))
|
'youtube_dl/version.py', 'exec'))
|
||||||
@ -107,5 +124,6 @@ setup(
|
|||||||
"Programming Language :: Python :: 3.4",
|
"Programming Language :: Python :: 3.4",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
cmdclass={'build_lazy_extractors': build_lazy_extractors},
|
||||||
**params
|
**params
|
||||||
)
|
)
|
||||||
|
@ -143,6 +143,9 @@ def expect_value(self, got, expected, field):
|
|||||||
expect_value(self, item_got, item_expected, field)
|
expect_value(self, item_got, item_expected, field)
|
||||||
else:
|
else:
|
||||||
if isinstance(expected, compat_str) and expected.startswith('md5:'):
|
if isinstance(expected, compat_str) and expected.startswith('md5:'):
|
||||||
|
self.assertTrue(
|
||||||
|
isinstance(got, compat_str),
|
||||||
|
'Expected field %s to be a unicode object, but got value %r of type %r' % (field, got, type(got)))
|
||||||
got = 'md5:' + md5(got)
|
got = 'md5:' + md5(got)
|
||||||
elif isinstance(expected, compat_str) and expected.startswith('mincount:'):
|
elif isinstance(expected, compat_str) and expected.startswith('mincount:'):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
|
@ -11,6 +11,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|||||||
from test.helper import FakeYDL
|
from test.helper import FakeYDL
|
||||||
from youtube_dl.extractor.common import InfoExtractor
|
from youtube_dl.extractor.common import InfoExtractor
|
||||||
from youtube_dl.extractor import YoutubeIE, get_info_extractor
|
from youtube_dl.extractor import YoutubeIE, get_info_extractor
|
||||||
|
from youtube_dl.utils import encode_data_uri, strip_jsonp, ExtractorError
|
||||||
|
|
||||||
|
|
||||||
class TestIE(InfoExtractor):
|
class TestIE(InfoExtractor):
|
||||||
@ -66,5 +67,14 @@ class TestInfoExtractor(unittest.TestCase):
|
|||||||
self.assertEqual(ie._html_search_meta('e', html), '5')
|
self.assertEqual(ie._html_search_meta('e', html), '5')
|
||||||
self.assertEqual(ie._html_search_meta('f', html), '6')
|
self.assertEqual(ie._html_search_meta('f', html), '6')
|
||||||
|
|
||||||
|
def test_download_json(self):
|
||||||
|
uri = encode_data_uri(b'{"foo": "blah"}', 'application/json')
|
||||||
|
self.assertEqual(self.ie._download_json(uri, None), {'foo': 'blah'})
|
||||||
|
uri = encode_data_uri(b'callback({"foo": "blah"})', 'application/javascript')
|
||||||
|
self.assertEqual(self.ie._download_json(uri, None, transform_source=strip_jsonp), {'foo': 'blah'})
|
||||||
|
uri = encode_data_uri(b'{"foo": invalid}', 'application/json')
|
||||||
|
self.assertRaises(ExtractorError, self.ie._download_json, uri, None)
|
||||||
|
self.assertEqual(self.ie._download_json(uri, None, fatal=False), None)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -20,6 +20,7 @@ from youtube_dl.utils import (
|
|||||||
args_to_str,
|
args_to_str,
|
||||||
encode_base_n,
|
encode_base_n,
|
||||||
clean_html,
|
clean_html,
|
||||||
|
date_from_str,
|
||||||
DateRange,
|
DateRange,
|
||||||
detect_exe_version,
|
detect_exe_version,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
@ -234,6 +235,13 @@ class TestUtil(unittest.TestCase):
|
|||||||
self.assertEqual(unescapeHTML('é'), 'é')
|
self.assertEqual(unescapeHTML('é'), 'é')
|
||||||
self.assertEqual(unescapeHTML('�'), '�')
|
self.assertEqual(unescapeHTML('�'), '�')
|
||||||
|
|
||||||
|
def test_date_from_str(self):
|
||||||
|
self.assertEqual(date_from_str('yesterday'), date_from_str('now-1day'))
|
||||||
|
self.assertEqual(date_from_str('now+7day'), date_from_str('now+1week'))
|
||||||
|
self.assertEqual(date_from_str('now+14day'), date_from_str('now+2week'))
|
||||||
|
self.assertEqual(date_from_str('now+365day'), date_from_str('now+1year'))
|
||||||
|
self.assertEqual(date_from_str('now+30day'), date_from_str('now+1month'))
|
||||||
|
|
||||||
def test_daterange(self):
|
def test_daterange(self):
|
||||||
_20century = DateRange("19000101", "20000101")
|
_20century = DateRange("19000101", "20000101")
|
||||||
self.assertFalse("17890714" in _20century)
|
self.assertFalse("17890714" in _20century)
|
||||||
|
@ -82,7 +82,7 @@ from .utils import (
|
|||||||
YoutubeDLHandler,
|
YoutubeDLHandler,
|
||||||
)
|
)
|
||||||
from .cache import Cache
|
from .cache import Cache
|
||||||
from .extractor import get_info_extractor, gen_extractors
|
from .extractor import get_info_extractor, gen_extractor_classes, _LAZY_LOADER
|
||||||
from .downloader import get_suitable_downloader
|
from .downloader import get_suitable_downloader
|
||||||
from .downloader.rtmp import rtmpdump_version
|
from .downloader.rtmp import rtmpdump_version
|
||||||
from .postprocessor import (
|
from .postprocessor import (
|
||||||
@ -378,8 +378,9 @@ class YoutubeDL(object):
|
|||||||
def add_info_extractor(self, ie):
|
def add_info_extractor(self, ie):
|
||||||
"""Add an InfoExtractor object to the end of the list."""
|
"""Add an InfoExtractor object to the end of the list."""
|
||||||
self._ies.append(ie)
|
self._ies.append(ie)
|
||||||
self._ies_instances[ie.ie_key()] = ie
|
if not isinstance(ie, type):
|
||||||
ie.set_downloader(self)
|
self._ies_instances[ie.ie_key()] = ie
|
||||||
|
ie.set_downloader(self)
|
||||||
|
|
||||||
def get_info_extractor(self, ie_key):
|
def get_info_extractor(self, ie_key):
|
||||||
"""
|
"""
|
||||||
@ -397,7 +398,7 @@ class YoutubeDL(object):
|
|||||||
"""
|
"""
|
||||||
Add the InfoExtractors returned by gen_extractors to the end of the list
|
Add the InfoExtractors returned by gen_extractors to the end of the list
|
||||||
"""
|
"""
|
||||||
for ie in gen_extractors():
|
for ie in gen_extractor_classes():
|
||||||
self.add_info_extractor(ie)
|
self.add_info_extractor(ie)
|
||||||
|
|
||||||
def add_post_processor(self, pp):
|
def add_post_processor(self, pp):
|
||||||
@ -661,6 +662,7 @@ class YoutubeDL(object):
|
|||||||
if not ie.suitable(url):
|
if not ie.suitable(url):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
ie = self.get_info_extractor(ie.ie_key())
|
||||||
if not ie.working():
|
if not ie.working():
|
||||||
self.report_warning('The program functionality for this site has been marked as broken, '
|
self.report_warning('The program functionality for this site has been marked as broken, '
|
||||||
'and will probably not work.')
|
'and will probably not work.')
|
||||||
@ -1957,6 +1959,8 @@ class YoutubeDL(object):
|
|||||||
write_string(encoding_str, encoding=None)
|
write_string(encoding_str, encoding=None)
|
||||||
|
|
||||||
self._write_string('[debug] youtube-dl version ' + __version__ + '\n')
|
self._write_string('[debug] youtube-dl version ' + __version__ + '\n')
|
||||||
|
if _LAZY_LOADER:
|
||||||
|
self._write_string('[debug] Lazy loading extractors enabled' + '\n')
|
||||||
try:
|
try:
|
||||||
sp = subprocess.Popen(
|
sp = subprocess.Popen(
|
||||||
['git', 'rev-parse', '--short', 'HEAD'],
|
['git', 'rev-parse', '--short', 'HEAD'],
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,18 @@
|
|||||||
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
ExtractorError,
|
||||||
|
int_or_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AolIE(InfoExtractor):
|
class AolIE(InfoExtractor):
|
||||||
IE_NAME = 'on.aol.com'
|
IE_NAME = 'on.aol.com'
|
||||||
_VALID_URL = r'(?:aol-video:|https?://on\.aol\.com/video/.*-)(?P<id>[0-9]+)(?:$|\?)'
|
_VALID_URL = r'(?:aol-video:|https?://on\.aol\.com/video/.*-)(?P<id>[^/?-]+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://on.aol.com/video/u-s--official-warns-of-largest-ever-irs-phone-scam-518167793?icid=OnHomepageC2Wide_MustSee_Img',
|
'url': 'http://on.aol.com/video/u-s--official-warns-of-largest-ever-irs-phone-scam-518167793?icid=OnHomepageC2Wide_MustSee_Img',
|
||||||
@ -14,13 +21,79 @@ class AolIE(InfoExtractor):
|
|||||||
'id': '518167793',
|
'id': '518167793',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'U.S. Official Warns Of \'Largest Ever\' IRS Phone Scam',
|
'title': 'U.S. Official Warns Of \'Largest Ever\' IRS Phone Scam',
|
||||||
|
'description': 'A major phone scam has cost thousands of taxpayers more than $1 million, with less than a month until income tax returns are due to the IRS.',
|
||||||
|
'timestamp': 1395405060,
|
||||||
|
'upload_date': '20140321',
|
||||||
|
'uploader': 'Newsy Studio',
|
||||||
},
|
},
|
||||||
'add_ie': ['FiveMin'],
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'http://on.aol.com/video/netflix-is-raising-rates-5707d6b8e4b090497b04f706?context=PC:homepage:PL1944:1460189336183',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '5707d6b8e4b090497b04f706',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Netflix is Raising Rates',
|
||||||
|
'description': 'Netflix is rewarding millions of it’s long-standing members with an increase in cost. Veuer’s Carly Figueroa has more.',
|
||||||
|
'upload_date': '20160408',
|
||||||
|
'timestamp': 1460123280,
|
||||||
|
'uploader': 'Veuer',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
return self.url_result('5min:%s' % video_id)
|
|
||||||
|
response = self._download_json(
|
||||||
|
'https://feedapi.b2c.on.aol.com/v1.0/app/videos/aolon/%s/details' % video_id,
|
||||||
|
video_id)['response']
|
||||||
|
if response['statusText'] != 'Ok':
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, response['statusText']), expected=True)
|
||||||
|
|
||||||
|
video_data = response['data']
|
||||||
|
formats = []
|
||||||
|
m3u8_url = video_data.get('videoMasterPlaylist')
|
||||||
|
if m3u8_url:
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
m3u8_url, video_id, 'mp4', m3u8_id='hls', fatal=False))
|
||||||
|
for rendition in video_data.get('renditions', []):
|
||||||
|
video_url = rendition.get('url')
|
||||||
|
if not video_url:
|
||||||
|
continue
|
||||||
|
ext = rendition.get('format')
|
||||||
|
if ext == 'm3u8':
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
video_url, video_id, 'mp4', m3u8_id='hls', fatal=False))
|
||||||
|
else:
|
||||||
|
f = {
|
||||||
|
'url': video_url,
|
||||||
|
'format_id': rendition.get('quality'),
|
||||||
|
}
|
||||||
|
mobj = re.search(r'(\d+)x(\d+)', video_url)
|
||||||
|
if mobj:
|
||||||
|
f.update({
|
||||||
|
'width': int(mobj.group(1)),
|
||||||
|
'height': int(mobj.group(2)),
|
||||||
|
})
|
||||||
|
formats.append(f)
|
||||||
|
self._sort_formats(formats, ('width', 'height', 'tbr', 'format_id'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': video_data['title'],
|
||||||
|
'duration': int_or_none(video_data.get('duration')),
|
||||||
|
'timestamp': int_or_none(video_data.get('publishDate')),
|
||||||
|
'view_count': int_or_none(video_data.get('views')),
|
||||||
|
'description': video_data.get('description'),
|
||||||
|
'uploader': video_data.get('videoOwner'),
|
||||||
|
'formats': formats,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class AolFeaturesIE(InfoExtractor):
|
class AolFeaturesIE(InfoExtractor):
|
||||||
|
90
youtube_dl/extractor/cliprs.py
Normal file
90
youtube_dl/extractor/cliprs.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
ExtractorError,
|
||||||
|
float_or_none,
|
||||||
|
int_or_none,
|
||||||
|
parse_iso8601,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ClipRsIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?clip\.rs/(?P<id>[^/]+)/\d+'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.clip.rs/premijera-frajle-predstavljaju-novi-spot-za-pesmu-moli-me-moli/3732',
|
||||||
|
'md5': 'c412d57815ba07b56f9edc7b5d6a14e5',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1488842.1399140381',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'PREMIJERA Frajle predstavljaju novi spot za pesmu Moli me, moli',
|
||||||
|
'description': 'md5:56ce2c3b4ab31c5a2e0b17cb9a453026',
|
||||||
|
'duration': 229,
|
||||||
|
'timestamp': 1459850243,
|
||||||
|
'upload_date': '20160405',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
|
video_id = self._search_regex(
|
||||||
|
r'id=(["\'])mvp:(?P<id>.+?)\1', webpage, 'mvp id', group='id')
|
||||||
|
|
||||||
|
response = self._download_json(
|
||||||
|
'http://qi.ckm.onetapi.pl/', video_id,
|
||||||
|
query={
|
||||||
|
'body[id]': video_id,
|
||||||
|
'body[jsonrpc]': '2.0',
|
||||||
|
'body[method]': 'get_asset_detail',
|
||||||
|
'body[params][ID_Publikacji]': video_id,
|
||||||
|
'body[params][Service]': 'www.onet.pl',
|
||||||
|
'content-type': 'application/jsonp',
|
||||||
|
'x-onet-app': 'player.front.onetapi.pl',
|
||||||
|
})
|
||||||
|
|
||||||
|
error = response.get('error')
|
||||||
|
if error:
|
||||||
|
raise ExtractorError(
|
||||||
|
'%s said: %s' % (self.IE_NAME, error['message']), expected=True)
|
||||||
|
|
||||||
|
video = response['result'].get('0')
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for _, formats_dict in video['formats'].items():
|
||||||
|
if not isinstance(formats_dict, dict):
|
||||||
|
continue
|
||||||
|
for format_id, format_list in formats_dict.items():
|
||||||
|
if not isinstance(format_list, list):
|
||||||
|
continue
|
||||||
|
for f in format_list:
|
||||||
|
if not f.get('url'):
|
||||||
|
continue
|
||||||
|
formats.append({
|
||||||
|
'url': f['url'],
|
||||||
|
'format_id': format_id,
|
||||||
|
'height': int_or_none(f.get('vertical_resolution')),
|
||||||
|
'width': int_or_none(f.get('horizontal_resolution')),
|
||||||
|
'abr': float_or_none(f.get('audio_bitrate')),
|
||||||
|
'vbr': float_or_none(f.get('video_bitrate')),
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
meta = video.get('meta', {})
|
||||||
|
|
||||||
|
title = self._og_search_title(webpage, default=None) or meta['title']
|
||||||
|
description = self._og_search_description(webpage, default=None) or meta.get('description')
|
||||||
|
duration = meta.get('length') or meta.get('lenght')
|
||||||
|
timestamp = parse_iso8601(meta.get('addDate'), ' ')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'duration': duration,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'formats': formats,
|
||||||
|
}
|
@ -376,7 +376,6 @@ class InfoExtractor(object):
|
|||||||
self.to_screen('%s' % (note,))
|
self.to_screen('%s' % (note,))
|
||||||
else:
|
else:
|
||||||
self.to_screen('%s: %s' % (video_id, note))
|
self.to_screen('%s: %s' % (video_id, note))
|
||||||
# data, headers and query params will be ignored for `Request` objects
|
|
||||||
if isinstance(url_or_request, compat_urllib_request.Request):
|
if isinstance(url_or_request, compat_urllib_request.Request):
|
||||||
url_or_request = update_Request(
|
url_or_request = update_Request(
|
||||||
url_or_request, data=data, headers=headers, query=query)
|
url_or_request, data=data, headers=headers, query=query)
|
||||||
@ -843,7 +842,7 @@ class InfoExtractor(object):
|
|||||||
for input in re.findall(r'(?i)<input([^>]+)>', html):
|
for input in re.findall(r'(?i)<input([^>]+)>', html):
|
||||||
if not re.search(r'type=(["\'])(?:hidden|submit)\1', input):
|
if not re.search(r'type=(["\'])(?:hidden|submit)\1', input):
|
||||||
continue
|
continue
|
||||||
name = re.search(r'name=(["\'])(?P<value>.+?)\1', input)
|
name = re.search(r'(?:name|id)=(["\'])(?P<value>.+?)\1', input)
|
||||||
if not name:
|
if not name:
|
||||||
continue
|
continue
|
||||||
value = re.search(r'value=(["\'])(?P<value>.*?)\1', input)
|
value = re.search(r'value=(["\'])(?P<value>.*?)\1', input)
|
||||||
@ -1534,7 +1533,7 @@ class InfoExtractor(object):
|
|||||||
media_template = representation_ms_info['media_template']
|
media_template = representation_ms_info['media_template']
|
||||||
media_template = media_template.replace('$RepresentationID$', representation_id)
|
media_template = media_template.replace('$RepresentationID$', representation_id)
|
||||||
media_template = re.sub(r'\$(Number|Bandwidth)\$', r'%(\1)d', media_template)
|
media_template = re.sub(r'\$(Number|Bandwidth)\$', r'%(\1)d', media_template)
|
||||||
media_template = re.sub(r'\$(Number|Bandwidth)%(\d+)\$', r'%(\1)\2d', media_template)
|
media_template = re.sub(r'\$(Number|Bandwidth)%([^$]+)\$', r'%(\1)\2', media_template)
|
||||||
media_template.replace('$$', '$')
|
media_template.replace('$$', '$')
|
||||||
representation_ms_info['segment_urls'] = [
|
representation_ms_info['segment_urls'] = [
|
||||||
media_template % {
|
media_template % {
|
||||||
|
@ -4,10 +4,10 @@ from .common import InfoExtractor
|
|||||||
|
|
||||||
|
|
||||||
class EbaumsWorldIE(InfoExtractor):
|
class EbaumsWorldIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://www\.ebaumsworld\.com/video/watch/(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?ebaumsworld\.com/videos/[^/]+/(?P<id>\d+)'
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.ebaumsworld.com/video/watch/83367677/',
|
'url': 'http://www.ebaumsworld.com/videos/a-giant-python-opens-the-door/83367677/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '83367677',
|
'id': '83367677',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
|
992
youtube_dl/extractor/extractors.py
Normal file
992
youtube_dl/extractor/extractors.py
Normal file
@ -0,0 +1,992 @@
|
|||||||
|
# flake8: noqa
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .abc import ABCIE
|
||||||
|
from .abc7news import Abc7NewsIE
|
||||||
|
from .academicearth import AcademicEarthCourseIE
|
||||||
|
from .acast import (
|
||||||
|
ACastIE,
|
||||||
|
ACastChannelIE,
|
||||||
|
)
|
||||||
|
from .addanime import AddAnimeIE
|
||||||
|
from .adobetv import (
|
||||||
|
AdobeTVIE,
|
||||||
|
AdobeTVShowIE,
|
||||||
|
AdobeTVChannelIE,
|
||||||
|
AdobeTVVideoIE,
|
||||||
|
)
|
||||||
|
from .adultswim import AdultSwimIE
|
||||||
|
from .aenetworks import AENetworksIE
|
||||||
|
from .aftonbladet import AftonbladetIE
|
||||||
|
from .airmozilla import AirMozillaIE
|
||||||
|
from .aljazeera import AlJazeeraIE
|
||||||
|
from .alphaporno import AlphaPornoIE
|
||||||
|
from .animeondemand import AnimeOnDemandIE
|
||||||
|
from .anitube import AnitubeIE
|
||||||
|
from .anysex import AnySexIE
|
||||||
|
from .aol import (
|
||||||
|
AolIE,
|
||||||
|
AolFeaturesIE,
|
||||||
|
)
|
||||||
|
from .allocine import AllocineIE
|
||||||
|
from .aparat import AparatIE
|
||||||
|
from .appleconnect import AppleConnectIE
|
||||||
|
from .appletrailers import (
|
||||||
|
AppleTrailersIE,
|
||||||
|
AppleTrailersSectionIE,
|
||||||
|
)
|
||||||
|
from .archiveorg import ArchiveOrgIE
|
||||||
|
from .ard import (
|
||||||
|
ARDIE,
|
||||||
|
ARDMediathekIE,
|
||||||
|
SportschauIE,
|
||||||
|
)
|
||||||
|
from .arte import (
|
||||||
|
ArteTvIE,
|
||||||
|
ArteTVPlus7IE,
|
||||||
|
ArteTVCreativeIE,
|
||||||
|
ArteTVConcertIE,
|
||||||
|
ArteTVFutureIE,
|
||||||
|
ArteTVCinemaIE,
|
||||||
|
ArteTVDDCIE,
|
||||||
|
ArteTVMagazineIE,
|
||||||
|
ArteTVEmbedIE,
|
||||||
|
)
|
||||||
|
from .atresplayer import AtresPlayerIE
|
||||||
|
from .atttechchannel import ATTTechChannelIE
|
||||||
|
from .audimedia import AudiMediaIE
|
||||||
|
from .audioboom import AudioBoomIE
|
||||||
|
from .audiomack import AudiomackIE, AudiomackAlbumIE
|
||||||
|
from .azubu import AzubuIE, AzubuLiveIE
|
||||||
|
from .baidu import BaiduVideoIE
|
||||||
|
from .bambuser import BambuserIE, BambuserChannelIE
|
||||||
|
from .bandcamp import BandcampIE, BandcampAlbumIE
|
||||||
|
from .bbc import (
|
||||||
|
BBCCoUkIE,
|
||||||
|
BBCCoUkArticleIE,
|
||||||
|
BBCIE,
|
||||||
|
)
|
||||||
|
from .beeg import BeegIE
|
||||||
|
from .behindkink import BehindKinkIE
|
||||||
|
from .beatportpro import BeatportProIE
|
||||||
|
from .bet import BetIE
|
||||||
|
from .bigflix import BigflixIE
|
||||||
|
from .bild import BildIE
|
||||||
|
from .bilibili import BiliBiliIE
|
||||||
|
from .biobiochiletv import BioBioChileTVIE
|
||||||
|
from .bleacherreport import (
|
||||||
|
BleacherReportIE,
|
||||||
|
BleacherReportCMSIE,
|
||||||
|
)
|
||||||
|
from .blinkx import BlinkxIE
|
||||||
|
from .bloomberg import BloombergIE
|
||||||
|
from .bokecc import BokeCCIE
|
||||||
|
from .bpb import BpbIE
|
||||||
|
from .br import BRIE
|
||||||
|
from .bravotv import BravoTVIE
|
||||||
|
from .breakcom import BreakIE
|
||||||
|
from .brightcove import (
|
||||||
|
BrightcoveLegacyIE,
|
||||||
|
BrightcoveNewIE,
|
||||||
|
)
|
||||||
|
from .buzzfeed import BuzzFeedIE
|
||||||
|
from .byutv import BYUtvIE
|
||||||
|
from .c56 import C56IE
|
||||||
|
from .camdemy import (
|
||||||
|
CamdemyIE,
|
||||||
|
CamdemyFolderIE
|
||||||
|
)
|
||||||
|
from .camwithher import CamWithHerIE
|
||||||
|
from .canalplus import CanalplusIE
|
||||||
|
from .canalc2 import Canalc2IE
|
||||||
|
from .canvas import CanvasIE
|
||||||
|
from .cbc import (
|
||||||
|
CBCIE,
|
||||||
|
CBCPlayerIE,
|
||||||
|
)
|
||||||
|
from .cbs import CBSIE
|
||||||
|
from .cbsinteractive import CBSInteractiveIE
|
||||||
|
from .cbsnews import (
|
||||||
|
CBSNewsIE,
|
||||||
|
CBSNewsLiveVideoIE,
|
||||||
|
)
|
||||||
|
from .cbssports import CBSSportsIE
|
||||||
|
from .ccc import CCCIE
|
||||||
|
from .cda import CDAIE
|
||||||
|
from .ceskatelevize import CeskaTelevizeIE
|
||||||
|
from .channel9 import Channel9IE
|
||||||
|
from .chaturbate import ChaturbateIE
|
||||||
|
from .chilloutzone import ChilloutzoneIE
|
||||||
|
from .chirbit import (
|
||||||
|
ChirbitIE,
|
||||||
|
ChirbitProfileIE,
|
||||||
|
)
|
||||||
|
from .cinchcast import CinchcastIE
|
||||||
|
from .cinemassacre import CinemassacreIE
|
||||||
|
from .cliprs import ClipRsIE
|
||||||
|
from .clipfish import ClipfishIE
|
||||||
|
from .cliphunter import CliphunterIE
|
||||||
|
from .clipsyndicate import ClipsyndicateIE
|
||||||
|
from .cloudy import CloudyIE
|
||||||
|
from .clubic import ClubicIE
|
||||||
|
from .clyp import ClypIE
|
||||||
|
from .cmt import CMTIE
|
||||||
|
from .cnbc import CNBCIE
|
||||||
|
from .cnn import (
|
||||||
|
CNNIE,
|
||||||
|
CNNBlogsIE,
|
||||||
|
CNNArticleIE,
|
||||||
|
)
|
||||||
|
from .collegehumor import CollegeHumorIE
|
||||||
|
from .collegerama import CollegeRamaIE
|
||||||
|
from .comedycentral import ComedyCentralIE, ComedyCentralShowsIE
|
||||||
|
from .comcarcoff import ComCarCoffIE
|
||||||
|
from .commonmistakes import CommonMistakesIE, UnicodeBOMIE
|
||||||
|
from .commonprotocols import RtmpIE
|
||||||
|
from .condenast import CondeNastIE
|
||||||
|
from .cracked import CrackedIE
|
||||||
|
from .crackle import CrackleIE
|
||||||
|
from .criterion import CriterionIE
|
||||||
|
from .crooksandliars import CrooksAndLiarsIE
|
||||||
|
from .crunchyroll import (
|
||||||
|
CrunchyrollIE,
|
||||||
|
CrunchyrollShowPlaylistIE
|
||||||
|
)
|
||||||
|
from .cspan import CSpanIE
|
||||||
|
from .ctsnews import CtsNewsIE
|
||||||
|
from .cultureunplugged import CultureUnpluggedIE
|
||||||
|
from .cwtv import CWTVIE
|
||||||
|
from .dailymotion import (
|
||||||
|
DailymotionIE,
|
||||||
|
DailymotionPlaylistIE,
|
||||||
|
DailymotionUserIE,
|
||||||
|
DailymotionCloudIE,
|
||||||
|
)
|
||||||
|
from .daum import (
|
||||||
|
DaumIE,
|
||||||
|
DaumClipIE,
|
||||||
|
DaumPlaylistIE,
|
||||||
|
DaumUserIE,
|
||||||
|
)
|
||||||
|
from .dbtv import DBTVIE
|
||||||
|
from .dcn import (
|
||||||
|
DCNIE,
|
||||||
|
DCNVideoIE,
|
||||||
|
DCNLiveIE,
|
||||||
|
DCNSeasonIE,
|
||||||
|
)
|
||||||
|
from .dctp import DctpTvIE
|
||||||
|
from .deezer import DeezerPlaylistIE
|
||||||
|
from .democracynow import DemocracynowIE
|
||||||
|
from .dfb import DFBIE
|
||||||
|
from .dhm import DHMIE
|
||||||
|
from .dotsub import DotsubIE
|
||||||
|
from .douyutv import DouyuTVIE
|
||||||
|
from .dplay import DPlayIE
|
||||||
|
from .dramafever import (
|
||||||
|
DramaFeverIE,
|
||||||
|
DramaFeverSeriesIE,
|
||||||
|
)
|
||||||
|
from .dreisat import DreiSatIE
|
||||||
|
from .drbonanza import DRBonanzaIE
|
||||||
|
from .drtuber import DrTuberIE
|
||||||
|
from .drtv import DRTVIE
|
||||||
|
from .dvtv import DVTVIE
|
||||||
|
from .dump import DumpIE
|
||||||
|
from .dumpert import DumpertIE
|
||||||
|
from .defense import DefenseGouvFrIE
|
||||||
|
from .discovery import DiscoveryIE
|
||||||
|
from .dropbox import DropboxIE
|
||||||
|
from .dw import (
|
||||||
|
DWIE,
|
||||||
|
DWArticleIE,
|
||||||
|
)
|
||||||
|
from .eagleplatform import EaglePlatformIE
|
||||||
|
from .ebaumsworld import EbaumsWorldIE
|
||||||
|
from .echomsk import EchoMskIE
|
||||||
|
from .ehow import EHowIE
|
||||||
|
from .eighttracks import EightTracksIE
|
||||||
|
from .einthusan import EinthusanIE
|
||||||
|
from .eitb import EitbIE
|
||||||
|
from .ellentv import (
|
||||||
|
EllenTVIE,
|
||||||
|
EllenTVClipsIE,
|
||||||
|
)
|
||||||
|
from .elpais import ElPaisIE
|
||||||
|
from .embedly import EmbedlyIE
|
||||||
|
from .engadget import EngadgetIE
|
||||||
|
from .eporner import EpornerIE
|
||||||
|
from .eroprofile import EroProfileIE
|
||||||
|
from .escapist import EscapistIE
|
||||||
|
from .espn import ESPNIE
|
||||||
|
from .esri import EsriVideoIE
|
||||||
|
from .europa import EuropaIE
|
||||||
|
from .everyonesmixtape import EveryonesMixtapeIE
|
||||||
|
from .exfm import ExfmIE
|
||||||
|
from .expotv import ExpoTVIE
|
||||||
|
from .extremetube import ExtremeTubeIE
|
||||||
|
from .facebook import FacebookIE
|
||||||
|
from .faz import FazIE
|
||||||
|
from .fc2 import FC2IE
|
||||||
|
from .fczenit import FczenitIE
|
||||||
|
from .firstpost import FirstpostIE
|
||||||
|
from .firsttv import FirstTVIE
|
||||||
|
from .fivemin import FiveMinIE
|
||||||
|
from .fivetv import FiveTVIE
|
||||||
|
from .fktv import FKTVIE
|
||||||
|
from .flickr import FlickrIE
|
||||||
|
from .folketinget import FolketingetIE
|
||||||
|
from .footyroom import FootyRoomIE
|
||||||
|
from .fourtube import FourTubeIE
|
||||||
|
from .fox import FOXIE
|
||||||
|
from .foxgay import FoxgayIE
|
||||||
|
from .foxnews import FoxNewsIE
|
||||||
|
from .foxsports import FoxSportsIE
|
||||||
|
from .franceculture import (
|
||||||
|
FranceCultureIE,
|
||||||
|
FranceCultureEmissionIE,
|
||||||
|
)
|
||||||
|
from .franceinter import FranceInterIE
|
||||||
|
from .francetv import (
|
||||||
|
PluzzIE,
|
||||||
|
FranceTvInfoIE,
|
||||||
|
FranceTVIE,
|
||||||
|
GenerationQuoiIE,
|
||||||
|
CultureboxIE,
|
||||||
|
)
|
||||||
|
from .freesound import FreesoundIE
|
||||||
|
from .freespeech import FreespeechIE
|
||||||
|
from .freevideo import FreeVideoIE
|
||||||
|
from .funimation import FunimationIE
|
||||||
|
from .funnyordie import FunnyOrDieIE
|
||||||
|
from .gameinformer import GameInformerIE
|
||||||
|
from .gamekings import GamekingsIE
|
||||||
|
from .gameone import (
|
||||||
|
GameOneIE,
|
||||||
|
GameOnePlaylistIE,
|
||||||
|
)
|
||||||
|
from .gamersyde import GamersydeIE
|
||||||
|
from .gamespot import GameSpotIE
|
||||||
|
from .gamestar import GameStarIE
|
||||||
|
from .gametrailers import GametrailersIE
|
||||||
|
from .gazeta import GazetaIE
|
||||||
|
from .gdcvault import GDCVaultIE
|
||||||
|
from .generic import GenericIE
|
||||||
|
from .gfycat import GfycatIE
|
||||||
|
from .giantbomb import GiantBombIE
|
||||||
|
from .giga import GigaIE
|
||||||
|
from .glide import GlideIE
|
||||||
|
from .globo import (
|
||||||
|
GloboIE,
|
||||||
|
GloboArticleIE,
|
||||||
|
)
|
||||||
|
from .godtube import GodTubeIE
|
||||||
|
from .goldenmoustache import GoldenMoustacheIE
|
||||||
|
from .golem import GolemIE
|
||||||
|
from .googledrive import GoogleDriveIE
|
||||||
|
from .googleplus import GooglePlusIE
|
||||||
|
from .googlesearch import GoogleSearchIE
|
||||||
|
from .goshgay import GoshgayIE
|
||||||
|
from .gputechconf import GPUTechConfIE
|
||||||
|
from .groupon import GrouponIE
|
||||||
|
from .hark import HarkIE
|
||||||
|
from .hbo import HBOIE
|
||||||
|
from .hearthisat import HearThisAtIE
|
||||||
|
from .heise import HeiseIE
|
||||||
|
from .hellporno import HellPornoIE
|
||||||
|
from .helsinki import HelsinkiIE
|
||||||
|
from .hentaistigma import HentaiStigmaIE
|
||||||
|
from .historicfilms import HistoricFilmsIE
|
||||||
|
from .hitbox import HitboxIE, HitboxLiveIE
|
||||||
|
from .hornbunny import HornBunnyIE
|
||||||
|
from .hotnewhiphop import HotNewHipHopIE
|
||||||
|
from .hotstar import HotStarIE
|
||||||
|
from .howcast import HowcastIE
|
||||||
|
from .howstuffworks import HowStuffWorksIE
|
||||||
|
from .huffpost import HuffPostIE
|
||||||
|
from .hypem import HypemIE
|
||||||
|
from .iconosquare import IconosquareIE
|
||||||
|
from .ign import (
|
||||||
|
IGNIE,
|
||||||
|
OneUPIE,
|
||||||
|
PCMagIE,
|
||||||
|
)
|
||||||
|
from .imdb import (
|
||||||
|
ImdbIE,
|
||||||
|
ImdbListIE
|
||||||
|
)
|
||||||
|
from .imgur import (
|
||||||
|
ImgurIE,
|
||||||
|
ImgurAlbumIE,
|
||||||
|
)
|
||||||
|
from .ina import InaIE
|
||||||
|
from .indavideo import (
|
||||||
|
IndavideoIE,
|
||||||
|
IndavideoEmbedIE,
|
||||||
|
)
|
||||||
|
from .infoq import InfoQIE
|
||||||
|
from .instagram import InstagramIE, InstagramUserIE
|
||||||
|
from .internetvideoarchive import InternetVideoArchiveIE
|
||||||
|
from .iprima import IPrimaIE
|
||||||
|
from .iqiyi import IqiyiIE
|
||||||
|
from .ir90tv import Ir90TvIE
|
||||||
|
from .ivi import (
|
||||||
|
IviIE,
|
||||||
|
IviCompilationIE
|
||||||
|
)
|
||||||
|
from .ivideon import IvideonIE
|
||||||
|
from .izlesene import IzleseneIE
|
||||||
|
from .jadorecettepub import JadoreCettePubIE
|
||||||
|
from .jeuxvideo import JeuxVideoIE
|
||||||
|
from .jove import JoveIE
|
||||||
|
from .jwplatform import JWPlatformIE
|
||||||
|
from .jpopsukitv import JpopsukiIE
|
||||||
|
from .kaltura import KalturaIE
|
||||||
|
from .kanalplay import KanalPlayIE
|
||||||
|
from .kankan import KankanIE
|
||||||
|
from .karaoketv import KaraoketvIE
|
||||||
|
from .karrierevideos import KarriereVideosIE
|
||||||
|
from .keezmovies import KeezMoviesIE
|
||||||
|
from .khanacademy import KhanAcademyIE
|
||||||
|
from .kickstarter import KickStarterIE
|
||||||
|
from .keek import KeekIE
|
||||||
|
from .konserthusetplay import KonserthusetPlayIE
|
||||||
|
from .kontrtube import KontrTubeIE
|
||||||
|
from .krasview import KrasViewIE
|
||||||
|
from .ku6 import Ku6IE
|
||||||
|
from .kusi import KUSIIE
|
||||||
|
from .kuwo import (
|
||||||
|
KuwoIE,
|
||||||
|
KuwoAlbumIE,
|
||||||
|
KuwoChartIE,
|
||||||
|
KuwoSingerIE,
|
||||||
|
KuwoCategoryIE,
|
||||||
|
KuwoMvIE,
|
||||||
|
)
|
||||||
|
from .la7 import LA7IE
|
||||||
|
from .laola1tv import Laola1TvIE
|
||||||
|
from .lecture2go import Lecture2GoIE
|
||||||
|
from .lemonde import LemondeIE
|
||||||
|
from .leeco import (
|
||||||
|
LeIE,
|
||||||
|
LePlaylistIE,
|
||||||
|
LetvCloudIE,
|
||||||
|
)
|
||||||
|
from .libsyn import LibsynIE
|
||||||
|
from .lifenews import (
|
||||||
|
LifeNewsIE,
|
||||||
|
LifeEmbedIE,
|
||||||
|
)
|
||||||
|
from .limelight import (
|
||||||
|
LimelightMediaIE,
|
||||||
|
LimelightChannelIE,
|
||||||
|
LimelightChannelListIE,
|
||||||
|
)
|
||||||
|
from .liveleak import LiveLeakIE
|
||||||
|
from .livestream import (
|
||||||
|
LivestreamIE,
|
||||||
|
LivestreamOriginalIE,
|
||||||
|
LivestreamShortenerIE,
|
||||||
|
)
|
||||||
|
from .lnkgo import LnkGoIE
|
||||||
|
from .lovehomeporn import LoveHomePornIE
|
||||||
|
from .lrt import LRTIE
|
||||||
|
from .lynda import (
|
||||||
|
LyndaIE,
|
||||||
|
LyndaCourseIE
|
||||||
|
)
|
||||||
|
from .m6 import M6IE
|
||||||
|
from .macgamestore import MacGameStoreIE
|
||||||
|
from .mailru import MailRuIE
|
||||||
|
from .makerschannel import MakersChannelIE
|
||||||
|
from .makertv import MakerTVIE
|
||||||
|
from .malemotion import MalemotionIE
|
||||||
|
from .matchtv import MatchTVIE
|
||||||
|
from .mdr import MDRIE
|
||||||
|
from .metacafe import MetacafeIE
|
||||||
|
from .metacritic import MetacriticIE
|
||||||
|
from .mgoon import MgoonIE
|
||||||
|
from .minhateca import MinhatecaIE
|
||||||
|
from .ministrygrid import MinistryGridIE
|
||||||
|
from .minoto import MinotoIE
|
||||||
|
from .miomio import MioMioIE
|
||||||
|
from .mit import TechTVMITIE, MITIE, OCWMITIE
|
||||||
|
from .mitele import MiTeleIE
|
||||||
|
from .mixcloud import MixcloudIE
|
||||||
|
from .mlb import MLBIE
|
||||||
|
from .mnet import MnetIE
|
||||||
|
from .mpora import MporaIE
|
||||||
|
from .moevideo import MoeVideoIE
|
||||||
|
from .mofosex import MofosexIE
|
||||||
|
from .mojvideo import MojvideoIE
|
||||||
|
from .moniker import MonikerIE
|
||||||
|
from .mooshare import MooshareIE
|
||||||
|
from .morningstar import MorningstarIE
|
||||||
|
from .motherless import MotherlessIE
|
||||||
|
from .motorsport import MotorsportIE
|
||||||
|
from .movieclips import MovieClipsIE
|
||||||
|
from .moviezine import MoviezineIE
|
||||||
|
from .mtv import (
|
||||||
|
MTVIE,
|
||||||
|
MTVServicesEmbeddedIE,
|
||||||
|
MTVIggyIE,
|
||||||
|
MTVDEIE,
|
||||||
|
)
|
||||||
|
from .muenchentv import MuenchenTVIE
|
||||||
|
from .musicplayon import MusicPlayOnIE
|
||||||
|
from .muzu import MuzuTVIE
|
||||||
|
from .mwave import MwaveIE
|
||||||
|
from .myspace import MySpaceIE, MySpaceAlbumIE
|
||||||
|
from .myspass import MySpassIE
|
||||||
|
from .myvi import MyviIE
|
||||||
|
from .myvideo import MyVideoIE
|
||||||
|
from .myvidster import MyVidsterIE
|
||||||
|
from .nationalgeographic import (
|
||||||
|
NationalGeographicIE,
|
||||||
|
NationalGeographicChannelIE,
|
||||||
|
)
|
||||||
|
from .naver import NaverIE
|
||||||
|
from .nba import NBAIE
|
||||||
|
from .nbc import (
|
||||||
|
CSNNEIE,
|
||||||
|
NBCIE,
|
||||||
|
NBCNewsIE,
|
||||||
|
NBCSportsIE,
|
||||||
|
NBCSportsVPlayerIE,
|
||||||
|
MSNBCIE,
|
||||||
|
)
|
||||||
|
from .ndr import (
|
||||||
|
NDRIE,
|
||||||
|
NJoyIE,
|
||||||
|
NDREmbedBaseIE,
|
||||||
|
NDREmbedIE,
|
||||||
|
NJoyEmbedIE,
|
||||||
|
)
|
||||||
|
from .ndtv import NDTVIE
|
||||||
|
from .netzkino import NetzkinoIE
|
||||||
|
from .nerdcubed import NerdCubedFeedIE
|
||||||
|
from .nerdist import NerdistIE
|
||||||
|
from .neteasemusic import (
|
||||||
|
NetEaseMusicIE,
|
||||||
|
NetEaseMusicAlbumIE,
|
||||||
|
NetEaseMusicSingerIE,
|
||||||
|
NetEaseMusicListIE,
|
||||||
|
NetEaseMusicMvIE,
|
||||||
|
NetEaseMusicProgramIE,
|
||||||
|
NetEaseMusicDjRadioIE,
|
||||||
|
)
|
||||||
|
from .newgrounds import NewgroundsIE
|
||||||
|
from .newstube import NewstubeIE
|
||||||
|
from .nextmedia import (
|
||||||
|
NextMediaIE,
|
||||||
|
NextMediaActionNewsIE,
|
||||||
|
AppleDailyIE,
|
||||||
|
)
|
||||||
|
from .nextmovie import NextMovieIE
|
||||||
|
from .nfb import NFBIE
|
||||||
|
from .nfl import NFLIE
|
||||||
|
from .nhl import (
|
||||||
|
NHLIE,
|
||||||
|
NHLNewsIE,
|
||||||
|
NHLVideocenterIE,
|
||||||
|
)
|
||||||
|
from .nick import NickIE
|
||||||
|
from .niconico import NiconicoIE, NiconicoPlaylistIE
|
||||||
|
from .ninegag import NineGagIE
|
||||||
|
from .noco import NocoIE
|
||||||
|
from .normalboots import NormalbootsIE
|
||||||
|
from .nosvideo import NosVideoIE
|
||||||
|
from .nova import NovaIE
|
||||||
|
from .novamov import (
|
||||||
|
AuroraVidIE,
|
||||||
|
CloudTimeIE,
|
||||||
|
NowVideoIE,
|
||||||
|
VideoWeedIE,
|
||||||
|
WholeCloudIE,
|
||||||
|
)
|
||||||
|
from .nowness import (
|
||||||
|
NownessIE,
|
||||||
|
NownessPlaylistIE,
|
||||||
|
NownessSeriesIE,
|
||||||
|
)
|
||||||
|
from .nowtv import (
|
||||||
|
NowTVIE,
|
||||||
|
NowTVListIE,
|
||||||
|
)
|
||||||
|
from .noz import NozIE
|
||||||
|
from .npo import (
|
||||||
|
NPOIE,
|
||||||
|
NPOLiveIE,
|
||||||
|
NPORadioIE,
|
||||||
|
NPORadioFragmentIE,
|
||||||
|
SchoolTVIE,
|
||||||
|
VPROIE,
|
||||||
|
WNLIE
|
||||||
|
)
|
||||||
|
from .npr import NprIE
|
||||||
|
from .nrk import (
|
||||||
|
NRKIE,
|
||||||
|
NRKPlaylistIE,
|
||||||
|
NRKSkoleIE,
|
||||||
|
NRKTVIE,
|
||||||
|
)
|
||||||
|
from .ntvde import NTVDeIE
|
||||||
|
from .ntvru import NTVRuIE
|
||||||
|
from .nytimes import (
|
||||||
|
NYTimesIE,
|
||||||
|
NYTimesArticleIE,
|
||||||
|
)
|
||||||
|
from .nuvid import NuvidIE
|
||||||
|
from .odnoklassniki import OdnoklassnikiIE
|
||||||
|
from .oktoberfesttv import OktoberfestTVIE
|
||||||
|
from .onionstudios import OnionStudiosIE
|
||||||
|
from .ooyala import (
|
||||||
|
OoyalaIE,
|
||||||
|
OoyalaExternalIE,
|
||||||
|
)
|
||||||
|
from .openload import OpenloadIE
|
||||||
|
from .ora import OraTVIE
|
||||||
|
from .orf import (
|
||||||
|
ORFTVthekIE,
|
||||||
|
ORFOE1IE,
|
||||||
|
ORFFM4IE,
|
||||||
|
ORFIPTVIE,
|
||||||
|
)
|
||||||
|
from .pandoratv import PandoraTVIE
|
||||||
|
from .parliamentliveuk import ParliamentLiveUKIE
|
||||||
|
from .patreon import PatreonIE
|
||||||
|
from .pbs import PBSIE
|
||||||
|
from .periscope import PeriscopeIE
|
||||||
|
from .philharmoniedeparis import PhilharmonieDeParisIE
|
||||||
|
from .phoenix import PhoenixIE
|
||||||
|
from .photobucket import PhotobucketIE
|
||||||
|
from .pinkbike import PinkbikeIE
|
||||||
|
from .planetaplay import PlanetaPlayIE
|
||||||
|
from .pladform import PladformIE
|
||||||
|
from .played import PlayedIE
|
||||||
|
from .playfm import PlayFMIE
|
||||||
|
from .plays import PlaysTVIE
|
||||||
|
from .playtvak import PlaytvakIE
|
||||||
|
from .playvid import PlayvidIE
|
||||||
|
from .playwire import PlaywireIE
|
||||||
|
from .pluralsight import (
|
||||||
|
PluralsightIE,
|
||||||
|
PluralsightCourseIE,
|
||||||
|
)
|
||||||
|
from .podomatic import PodomaticIE
|
||||||
|
from .porn91 import Porn91IE
|
||||||
|
from .pornhd import PornHdIE
|
||||||
|
from .pornhub import (
|
||||||
|
PornHubIE,
|
||||||
|
PornHubPlaylistIE,
|
||||||
|
PornHubUserVideosIE,
|
||||||
|
)
|
||||||
|
from .pornotube import PornotubeIE
|
||||||
|
from .pornovoisines import PornoVoisinesIE
|
||||||
|
from .pornoxo import PornoXOIE
|
||||||
|
from .primesharetv import PrimeShareTVIE
|
||||||
|
from .promptfile import PromptFileIE
|
||||||
|
from .prosiebensat1 import ProSiebenSat1IE
|
||||||
|
from .puls4 import Puls4IE
|
||||||
|
from .pyvideo import PyvideoIE
|
||||||
|
from .qqmusic import (
|
||||||
|
QQMusicIE,
|
||||||
|
QQMusicSingerIE,
|
||||||
|
QQMusicAlbumIE,
|
||||||
|
QQMusicToplistIE,
|
||||||
|
QQMusicPlaylistIE,
|
||||||
|
)
|
||||||
|
from .quickvid import QuickVidIE
|
||||||
|
from .r7 import R7IE
|
||||||
|
from .radiode import RadioDeIE
|
||||||
|
from .radiojavan import RadioJavanIE
|
||||||
|
from .radiobremen import RadioBremenIE
|
||||||
|
from .radiofrance import RadioFranceIE
|
||||||
|
from .rai import (
|
||||||
|
RaiTVIE,
|
||||||
|
RaiIE,
|
||||||
|
)
|
||||||
|
from .rbmaradio import RBMARadioIE
|
||||||
|
from .rds import RDSIE
|
||||||
|
from .redtube import RedTubeIE
|
||||||
|
from .regiotv import RegioTVIE
|
||||||
|
from .restudy import RestudyIE
|
||||||
|
from .reverbnation import ReverbNationIE
|
||||||
|
from .revision3 import Revision3IE
|
||||||
|
from .rice import RICEIE
|
||||||
|
from .ringtv import RingTVIE
|
||||||
|
from .ro220 import Ro220IE
|
||||||
|
from .rottentomatoes import RottenTomatoesIE
|
||||||
|
from .roxwel import RoxwelIE
|
||||||
|
from .rtbf import RTBFIE
|
||||||
|
from .rte import RteIE, RteRadioIE
|
||||||
|
from .rtlnl import RtlNlIE
|
||||||
|
from .rtl2 import RTL2IE
|
||||||
|
from .rtp import RTPIE
|
||||||
|
from .rts import RTSIE
|
||||||
|
from .rtve import RTVEALaCartaIE, RTVELiveIE, RTVEInfantilIE
|
||||||
|
from .rtvnh import RTVNHIE
|
||||||
|
from .ruhd import RUHDIE
|
||||||
|
from .ruleporn import RulePornIE
|
||||||
|
from .rutube import (
|
||||||
|
RutubeIE,
|
||||||
|
RutubeChannelIE,
|
||||||
|
RutubeEmbedIE,
|
||||||
|
RutubeMovieIE,
|
||||||
|
RutubePersonIE,
|
||||||
|
)
|
||||||
|
from .rutv import RUTVIE
|
||||||
|
from .ruutu import RuutuIE
|
||||||
|
from .sandia import SandiaIE
|
||||||
|
from .safari import (
|
||||||
|
SafariIE,
|
||||||
|
SafariApiIE,
|
||||||
|
SafariCourseIE,
|
||||||
|
)
|
||||||
|
from .sapo import SapoIE
|
||||||
|
from .savefrom import SaveFromIE
|
||||||
|
from .sbs import SBSIE
|
||||||
|
from .scivee import SciVeeIE
|
||||||
|
from .screencast import ScreencastIE
|
||||||
|
from .screencastomatic import ScreencastOMaticIE
|
||||||
|
from .screenjunkies import ScreenJunkiesIE
|
||||||
|
from .screenwavemedia import ScreenwaveMediaIE, TeamFourIE
|
||||||
|
from .senateisvp import SenateISVPIE
|
||||||
|
from .servingsys import ServingSysIE
|
||||||
|
from .sexu import SexuIE
|
||||||
|
from .sexykarma import SexyKarmaIE
|
||||||
|
from .shahid import ShahidIE
|
||||||
|
from .shared import SharedIE
|
||||||
|
from .sharesix import ShareSixIE
|
||||||
|
from .sina import SinaIE
|
||||||
|
from .skynewsarabia import (
|
||||||
|
SkyNewsArabiaIE,
|
||||||
|
SkyNewsArabiaArticleIE,
|
||||||
|
)
|
||||||
|
from .slideshare import SlideshareIE
|
||||||
|
from .slutload import SlutloadIE
|
||||||
|
from .smotri import (
|
||||||
|
SmotriIE,
|
||||||
|
SmotriCommunityIE,
|
||||||
|
SmotriUserIE,
|
||||||
|
SmotriBroadcastIE,
|
||||||
|
)
|
||||||
|
from .snagfilms import (
|
||||||
|
SnagFilmsIE,
|
||||||
|
SnagFilmsEmbedIE,
|
||||||
|
)
|
||||||
|
from .snotr import SnotrIE
|
||||||
|
from .sohu import SohuIE
|
||||||
|
from .soundcloud import (
|
||||||
|
SoundcloudIE,
|
||||||
|
SoundcloudSetIE,
|
||||||
|
SoundcloudUserIE,
|
||||||
|
SoundcloudPlaylistIE,
|
||||||
|
SoundcloudSearchIE
|
||||||
|
)
|
||||||
|
from .soundgasm import (
|
||||||
|
SoundgasmIE,
|
||||||
|
SoundgasmProfileIE
|
||||||
|
)
|
||||||
|
from .southpark import (
|
||||||
|
SouthParkIE,
|
||||||
|
SouthParkDeIE,
|
||||||
|
SouthParkDkIE,
|
||||||
|
SouthParkEsIE,
|
||||||
|
SouthParkNlIE
|
||||||
|
)
|
||||||
|
from .spankbang import SpankBangIE
|
||||||
|
from .spankwire import SpankwireIE
|
||||||
|
from .spiegel import SpiegelIE, SpiegelArticleIE
|
||||||
|
from .spiegeltv import SpiegeltvIE
|
||||||
|
from .spike import SpikeIE
|
||||||
|
from .stitcher import StitcherIE
|
||||||
|
from .sport5 import Sport5IE
|
||||||
|
from .sportbox import (
|
||||||
|
SportBoxIE,
|
||||||
|
SportBoxEmbedIE,
|
||||||
|
)
|
||||||
|
from .sportdeutschland import SportDeutschlandIE
|
||||||
|
from .srgssr import (
|
||||||
|
SRGSSRIE,
|
||||||
|
SRGSSRPlayIE,
|
||||||
|
)
|
||||||
|
from .srmediathek import SRMediathekIE
|
||||||
|
from .ssa import SSAIE
|
||||||
|
from .stanfordoc import StanfordOpenClassroomIE
|
||||||
|
from .steam import SteamIE
|
||||||
|
from .streamcloud import StreamcloudIE
|
||||||
|
from .streamcz import StreamCZIE
|
||||||
|
from .streetvoice import StreetVoiceIE
|
||||||
|
from .sunporno import SunPornoIE
|
||||||
|
from .svt import (
|
||||||
|
SVTIE,
|
||||||
|
SVTPlayIE,
|
||||||
|
)
|
||||||
|
from .swrmediathek import SWRMediathekIE
|
||||||
|
from .syfy import SyfyIE
|
||||||
|
from .sztvhu import SztvHuIE
|
||||||
|
from .tagesschau import TagesschauIE
|
||||||
|
from .tapely import TapelyIE
|
||||||
|
from .tass import TassIE
|
||||||
|
from .teachertube import (
|
||||||
|
TeacherTubeIE,
|
||||||
|
TeacherTubeUserIE,
|
||||||
|
)
|
||||||
|
from .teachingchannel import TeachingChannelIE
|
||||||
|
from .teamcoco import TeamcocoIE
|
||||||
|
from .techtalks import TechTalksIE
|
||||||
|
from .ted import TEDIE
|
||||||
|
from .tele13 import Tele13IE
|
||||||
|
from .telebruxelles import TeleBruxellesIE
|
||||||
|
from .telecinco import TelecincoIE
|
||||||
|
from .telegraaf import TelegraafIE
|
||||||
|
from .telemb import TeleMBIE
|
||||||
|
from .teletask import TeleTaskIE
|
||||||
|
from .testurl import TestURLIE
|
||||||
|
from .tf1 import TF1IE
|
||||||
|
from .theintercept import TheInterceptIE
|
||||||
|
from .theonion import TheOnionIE
|
||||||
|
from .theplatform import (
|
||||||
|
ThePlatformIE,
|
||||||
|
ThePlatformFeedIE,
|
||||||
|
)
|
||||||
|
from .thescene import TheSceneIE
|
||||||
|
from .thesixtyone import TheSixtyOneIE
|
||||||
|
from .thestar import TheStarIE
|
||||||
|
from .thisamericanlife import ThisAmericanLifeIE
|
||||||
|
from .thisav import ThisAVIE
|
||||||
|
from .tinypic import TinyPicIE
|
||||||
|
from .tlc import TlcDeIE
|
||||||
|
from .tmz import (
|
||||||
|
TMZIE,
|
||||||
|
TMZArticleIE,
|
||||||
|
)
|
||||||
|
from .tnaflix import (
|
||||||
|
TNAFlixNetworkEmbedIE,
|
||||||
|
TNAFlixIE,
|
||||||
|
EMPFlixIE,
|
||||||
|
MovieFapIE,
|
||||||
|
)
|
||||||
|
from .toggle import ToggleIE
|
||||||
|
from .thvideo import (
|
||||||
|
THVideoIE,
|
||||||
|
THVideoPlaylistIE
|
||||||
|
)
|
||||||
|
from .toutv import TouTvIE
|
||||||
|
from .toypics import ToypicsUserIE, ToypicsIE
|
||||||
|
from .traileraddict import TrailerAddictIE
|
||||||
|
from .trilulilu import TriluliluIE
|
||||||
|
from .trollvids import TrollvidsIE
|
||||||
|
from .trutube import TruTubeIE
|
||||||
|
from .tube8 import Tube8IE
|
||||||
|
from .tubitv import TubiTvIE
|
||||||
|
from .tudou import (
|
||||||
|
TudouIE,
|
||||||
|
TudouPlaylistIE,
|
||||||
|
TudouAlbumIE,
|
||||||
|
)
|
||||||
|
from .tumblr import TumblrIE
|
||||||
|
from .tunein import (
|
||||||
|
TuneInClipIE,
|
||||||
|
TuneInStationIE,
|
||||||
|
TuneInProgramIE,
|
||||||
|
TuneInTopicIE,
|
||||||
|
TuneInShortenerIE,
|
||||||
|
)
|
||||||
|
from .turbo import TurboIE
|
||||||
|
from .tutv import TutvIE
|
||||||
|
from .tv2 import (
|
||||||
|
TV2IE,
|
||||||
|
TV2ArticleIE,
|
||||||
|
)
|
||||||
|
from .tv3 import TV3IE
|
||||||
|
from .tv4 import TV4IE
|
||||||
|
from .tvc import (
|
||||||
|
TVCIE,
|
||||||
|
TVCArticleIE,
|
||||||
|
)
|
||||||
|
from .tvigle import TvigleIE
|
||||||
|
from .tvland import TVLandIE
|
||||||
|
from .tvp import TvpIE, TvpSeriesIE
|
||||||
|
from .tvplay import TVPlayIE
|
||||||
|
from .tweakers import TweakersIE
|
||||||
|
from .twentyfourvideo import TwentyFourVideoIE
|
||||||
|
from .twentymin import TwentyMinutenIE
|
||||||
|
from .twentytwotracks import (
|
||||||
|
TwentyTwoTracksIE,
|
||||||
|
TwentyTwoTracksGenreIE
|
||||||
|
)
|
||||||
|
from .twitch import (
|
||||||
|
TwitchVideoIE,
|
||||||
|
TwitchChapterIE,
|
||||||
|
TwitchVodIE,
|
||||||
|
TwitchProfileIE,
|
||||||
|
TwitchPastBroadcastsIE,
|
||||||
|
TwitchBookmarksIE,
|
||||||
|
TwitchStreamIE,
|
||||||
|
)
|
||||||
|
from .twitter import (
|
||||||
|
TwitterCardIE,
|
||||||
|
TwitterIE,
|
||||||
|
TwitterAmplifyIE,
|
||||||
|
)
|
||||||
|
from .ubu import UbuIE
|
||||||
|
from .udemy import (
|
||||||
|
UdemyIE,
|
||||||
|
UdemyCourseIE
|
||||||
|
)
|
||||||
|
from .udn import UDNEmbedIE
|
||||||
|
from .digiteka import DigitekaIE
|
||||||
|
from .unistra import UnistraIE
|
||||||
|
from .urort import UrortIE
|
||||||
|
from .usatoday import USATodayIE
|
||||||
|
from .ustream import UstreamIE, UstreamChannelIE
|
||||||
|
from .ustudio import UstudioIE
|
||||||
|
from .varzesh3 import Varzesh3IE
|
||||||
|
from .vbox7 import Vbox7IE
|
||||||
|
from .veehd import VeeHDIE
|
||||||
|
from .veoh import VeohIE
|
||||||
|
from .vessel import VesselIE
|
||||||
|
from .vesti import VestiIE
|
||||||
|
from .vevo import VevoIE
|
||||||
|
from .vgtv import (
|
||||||
|
BTArticleIE,
|
||||||
|
BTVestlendingenIE,
|
||||||
|
VGTVIE,
|
||||||
|
)
|
||||||
|
from .vh1 import VH1IE
|
||||||
|
from .vice import (
|
||||||
|
ViceIE,
|
||||||
|
ViceShowIE,
|
||||||
|
)
|
||||||
|
from .viddler import ViddlerIE
|
||||||
|
from .videodetective import VideoDetectiveIE
|
||||||
|
from .videofyme import VideofyMeIE
|
||||||
|
from .videomega import VideoMegaIE
|
||||||
|
from .videomore import (
|
||||||
|
VideomoreIE,
|
||||||
|
VideomoreVideoIE,
|
||||||
|
VideomoreSeasonIE,
|
||||||
|
)
|
||||||
|
from .videopremium import VideoPremiumIE
|
||||||
|
from .videott import VideoTtIE
|
||||||
|
from .vidme import (
|
||||||
|
VidmeIE,
|
||||||
|
VidmeUserIE,
|
||||||
|
VidmeUserLikesIE,
|
||||||
|
)
|
||||||
|
from .vidzi import VidziIE
|
||||||
|
from .vier import VierIE, VierVideosIE
|
||||||
|
from .viewster import ViewsterIE
|
||||||
|
from .viidea import ViideaIE
|
||||||
|
from .vimeo import (
|
||||||
|
VimeoIE,
|
||||||
|
VimeoAlbumIE,
|
||||||
|
VimeoChannelIE,
|
||||||
|
VimeoGroupsIE,
|
||||||
|
VimeoLikesIE,
|
||||||
|
VimeoOndemandIE,
|
||||||
|
VimeoReviewIE,
|
||||||
|
VimeoUserIE,
|
||||||
|
VimeoWatchLaterIE,
|
||||||
|
)
|
||||||
|
from .vimple import VimpleIE
|
||||||
|
from .vine import (
|
||||||
|
VineIE,
|
||||||
|
VineUserIE,
|
||||||
|
)
|
||||||
|
from .viki import (
|
||||||
|
VikiIE,
|
||||||
|
VikiChannelIE,
|
||||||
|
)
|
||||||
|
from .vk import (
|
||||||
|
VKIE,
|
||||||
|
VKUserVideosIE,
|
||||||
|
)
|
||||||
|
from .vlive import VLiveIE
|
||||||
|
from .vodlocker import VodlockerIE
|
||||||
|
from .voicerepublic import VoiceRepublicIE
|
||||||
|
from .voxmedia import VoxMediaIE
|
||||||
|
from .vporn import VpornIE
|
||||||
|
from .vrt import VRTIE
|
||||||
|
from .vube import VubeIE
|
||||||
|
from .vuclip import VuClipIE
|
||||||
|
from .vulture import VultureIE
|
||||||
|
from .walla import WallaIE
|
||||||
|
from .washingtonpost import WashingtonPostIE
|
||||||
|
from .wat import WatIE
|
||||||
|
from .wayofthemaster import WayOfTheMasterIE
|
||||||
|
from .wdr import (
|
||||||
|
WDRIE,
|
||||||
|
WDRMobileIE,
|
||||||
|
WDRMausIE,
|
||||||
|
)
|
||||||
|
from .webofstories import (
|
||||||
|
WebOfStoriesIE,
|
||||||
|
WebOfStoriesPlaylistIE,
|
||||||
|
)
|
||||||
|
from .weibo import WeiboIE
|
||||||
|
from .weiqitv import WeiqiTVIE
|
||||||
|
from .wimp import WimpIE
|
||||||
|
from .wistia import WistiaIE
|
||||||
|
from .worldstarhiphop import WorldStarHipHopIE
|
||||||
|
from .wrzuta import WrzutaIE
|
||||||
|
from .wsj import WSJIE
|
||||||
|
from .xbef import XBefIE
|
||||||
|
from .xboxclips import XboxClipsIE
|
||||||
|
from .xfileshare import XFileShareIE
|
||||||
|
from .xhamster import (
|
||||||
|
XHamsterIE,
|
||||||
|
XHamsterEmbedIE,
|
||||||
|
)
|
||||||
|
from .xminus import XMinusIE
|
||||||
|
from .xnxx import XNXXIE
|
||||||
|
from .xstream import XstreamIE
|
||||||
|
from .xtube import XTubeUserIE, XTubeIE
|
||||||
|
from .xuite import XuiteIE
|
||||||
|
from .xvideos import XVideosIE
|
||||||
|
from .xxxymovies import XXXYMoviesIE
|
||||||
|
from .yahoo import (
|
||||||
|
YahooIE,
|
||||||
|
YahooSearchIE,
|
||||||
|
)
|
||||||
|
from .yam import YamIE
|
||||||
|
from .yandexmusic import (
|
||||||
|
YandexMusicTrackIE,
|
||||||
|
YandexMusicAlbumIE,
|
||||||
|
YandexMusicPlaylistIE,
|
||||||
|
)
|
||||||
|
from .yesjapan import YesJapanIE
|
||||||
|
from .yinyuetai import YinYueTaiIE
|
||||||
|
from .ynet import YnetIE
|
||||||
|
from .youjizz import YouJizzIE
|
||||||
|
from .youku import YoukuIE
|
||||||
|
from .youporn import YouPornIE
|
||||||
|
from .yourupload import YourUploadIE
|
||||||
|
from .youtube import (
|
||||||
|
YoutubeIE,
|
||||||
|
YoutubeChannelIE,
|
||||||
|
YoutubeFavouritesIE,
|
||||||
|
YoutubeHistoryIE,
|
||||||
|
YoutubeLiveIE,
|
||||||
|
YoutubePlaylistIE,
|
||||||
|
YoutubePlaylistsIE,
|
||||||
|
YoutubeRecommendedIE,
|
||||||
|
YoutubeSearchDateIE,
|
||||||
|
YoutubeSearchIE,
|
||||||
|
YoutubeSearchURLIE,
|
||||||
|
YoutubeShowIE,
|
||||||
|
YoutubeSubscriptionsIE,
|
||||||
|
YoutubeTruncatedIDIE,
|
||||||
|
YoutubeTruncatedURLIE,
|
||||||
|
YoutubeUserIE,
|
||||||
|
YoutubeWatchLaterIE,
|
||||||
|
)
|
||||||
|
from .zapiks import ZapiksIE
|
||||||
|
from .zdf import ZDFIE, ZDFChannelIE
|
||||||
|
from .zingmp3 import (
|
||||||
|
ZingMp3SongIE,
|
||||||
|
ZingMp3AlbumIE,
|
||||||
|
)
|
||||||
|
from .zippcast import ZippCastIE
|
@ -2,78 +2,133 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import int_or_none
|
from ..compat import compat_xpath
|
||||||
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
qualities,
|
||||||
|
unified_strdate,
|
||||||
|
xpath_attr,
|
||||||
|
xpath_element,
|
||||||
|
xpath_text,
|
||||||
|
xpath_with_ns,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FirstTVIE(InfoExtractor):
|
class FirstTVIE(InfoExtractor):
|
||||||
IE_NAME = '1tv'
|
IE_NAME = '1tv'
|
||||||
IE_DESC = 'Первый канал'
|
IE_DESC = 'Первый канал'
|
||||||
_VALID_URL = r'https?://(?:www\.)?1tv\.ru/(?:[^/]+/)+(?P<id>.+)'
|
_VALID_URL = r'https?://(?:www\.)?1tv\.ru/(?:[^/]+/)+p?(?P<id>\d+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.1tv.ru/videoarchive/73390',
|
# single format via video_materials.json API
|
||||||
'md5': '777f525feeec4806130f4f764bc18a4f',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '73390',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Олимпийские канатные дороги',
|
|
||||||
'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
|
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|JPG)$',
|
|
||||||
'duration': 149,
|
|
||||||
'like_count': int,
|
|
||||||
'dislike_count': int,
|
|
||||||
},
|
|
||||||
'skip': 'Only works from Russia',
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.1tv.ru/prj/inprivate/vypusk/35930',
|
'url': 'http://www.1tv.ru/prj/inprivate/vypusk/35930',
|
||||||
'md5': 'a1b6b60d530ebcf8daacf4565762bbaf',
|
'md5': '82a2777648acae812d58b3f5bd42882b',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '35930',
|
'id': '35930',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Наедине со всеми. Людмила Сенчина',
|
'title': 'Гость Людмила Сенчина. Наедине со всеми. Выпуск от 12.02.2015',
|
||||||
'description': 'md5:89553aed1d641416001fe8d450f06cb9',
|
'description': 'md5:357933adeede13b202c7c21f91b871b2',
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|JPG)$',
|
'thumbnail': 're:^https?://.*\.(?:jpg|JPG)$',
|
||||||
|
'upload_date': '20150212',
|
||||||
'duration': 2694,
|
'duration': 2694,
|
||||||
},
|
},
|
||||||
'skip': 'Only works from Russia',
|
}, {
|
||||||
|
# multiple formats via video_materials.json API
|
||||||
|
'url': 'http://www.1tv.ru/video_archive/projects/dobroeutro/p113641',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '113641',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Весенняя аллергия. Доброе утро. Фрагмент выпуска от 07.04.2016',
|
||||||
|
'description': 'md5:8dcebb3dded0ff20fade39087fd1fee2',
|
||||||
|
'thumbnail': 're:^https?://.*\.(?:jpg|JPG)$',
|
||||||
|
'upload_date': '20160407',
|
||||||
|
'duration': 179,
|
||||||
|
'formats': 'mincount:3',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# single format only available via ONE_ONLINE_VIDEOS.archive_single_xml API
|
||||||
|
'url': 'http://www.1tv.ru/video_archive/series/f7552/p47038',
|
||||||
|
'md5': '519d306c5b5669761fd8906c39dbee23',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '47038',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '"Побег". Второй сезон. 3 серия',
|
||||||
|
'description': 'md5:3abf8f6b9bce88201c33e9a3d794a00b',
|
||||||
|
'thumbnail': 're:^https?://.*\.(?:jpg|JPG)$',
|
||||||
|
'upload_date': '20120516',
|
||||||
|
'duration': 3080,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.1tv.ru/videoarchive/9967',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id, 'Downloading page')
|
# Videos with multiple formats only available via this API
|
||||||
|
video = self._download_json(
|
||||||
|
'http://www.1tv.ru/video_materials.json?legacy_id=%s' % video_id,
|
||||||
|
video_id, fatal=False)
|
||||||
|
|
||||||
video_url = self._html_search_regex(
|
description, thumbnail, upload_date, duration = [None] * 4
|
||||||
r'''(?s)(?:jwplayer\('flashvideoportal_1'\)\.setup\({|var\s+playlistObj\s*=).*?'file'\s*:\s*'([^']+)'.*?}\);''',
|
|
||||||
webpage, 'video URL')
|
|
||||||
|
|
||||||
title = self._html_search_regex(
|
if video:
|
||||||
[r'<div class="tv_translation">\s*<h1><a href="[^"]+">([^<]*)</a>',
|
item = video[0]
|
||||||
r"'title'\s*:\s*'([^']+)'"], webpage, 'title')
|
title = item['title']
|
||||||
description = self._html_search_regex(
|
quality = qualities(('ld', 'sd', 'hd', ))
|
||||||
r'<div class="descr">\s*<div> </div>\s*<p>([^<]*)</p></div>',
|
formats = [{
|
||||||
webpage, 'description', default=None) or self._html_search_meta(
|
'url': f['src'],
|
||||||
|
'format_id': f.get('name'),
|
||||||
|
'quality': quality(f.get('name')),
|
||||||
|
} for f in item['mbr'] if f.get('src')]
|
||||||
|
thumbnail = item.get('poster')
|
||||||
|
else:
|
||||||
|
# Some videos are not available via video_materials.json
|
||||||
|
video = self._download_xml(
|
||||||
|
'http://www.1tv.ru/owa/win/ONE_ONLINE_VIDEOS.archive_single_xml?pid=%s' % video_id,
|
||||||
|
video_id)
|
||||||
|
|
||||||
|
NS_MAP = {
|
||||||
|
'media': 'http://search.yahoo.com/mrss/',
|
||||||
|
}
|
||||||
|
|
||||||
|
item = xpath_element(video, './channel/item', fatal=True)
|
||||||
|
title = xpath_text(item, './title', fatal=True)
|
||||||
|
formats = [{
|
||||||
|
'url': content.attrib['url'],
|
||||||
|
} for content in item.findall(
|
||||||
|
compat_xpath(xpath_with_ns('./media:content', NS_MAP))) if content.attrib.get('url')]
|
||||||
|
thumbnail = xpath_attr(
|
||||||
|
item, xpath_with_ns('./media:thumbnail', NS_MAP), 'url')
|
||||||
|
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, video_id, 'Downloading page', fatal=False)
|
||||||
|
if webpage:
|
||||||
|
title = self._html_search_regex(
|
||||||
|
(r'<div class="tv_translation">\s*<h1><a href="[^"]+">([^<]*)</a>',
|
||||||
|
r"'title'\s*:\s*'([^']+)'"),
|
||||||
|
webpage, 'title', default=None) or title
|
||||||
|
description = self._html_search_regex(
|
||||||
|
r'<div class="descr">\s*<div> </div>\s*<p>([^<]*)</p></div>',
|
||||||
|
webpage, 'description', default=None) or self._html_search_meta(
|
||||||
'description', webpage, 'description')
|
'description', webpage, 'description')
|
||||||
|
thumbnail = thumbnail or self._og_search_thumbnail(webpage)
|
||||||
thumbnail = self._og_search_thumbnail(webpage)
|
duration = int_or_none(self._html_search_meta(
|
||||||
duration = self._og_search_property(
|
'video:duration', webpage, 'video duration', fatal=False))
|
||||||
'video:duration', webpage,
|
upload_date = unified_strdate(self._html_search_meta(
|
||||||
'video duration', fatal=False)
|
'ya:ovs:upload_date', webpage, 'upload date', fatal=False))
|
||||||
|
|
||||||
like_count = self._html_search_regex(
|
|
||||||
r'title="Понравилось".*?/></label> \[(\d+)\]',
|
|
||||||
webpage, 'like count', default=None)
|
|
||||||
dislike_count = self._html_search_regex(
|
|
||||||
r'title="Не понравилось".*?/></label> \[(\d+)\]',
|
|
||||||
webpage, 'dislike count', default=None)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'url': video_url,
|
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': description,
|
'description': description,
|
||||||
|
'upload_date': upload_date,
|
||||||
'duration': int_or_none(duration),
|
'duration': int_or_none(duration),
|
||||||
'like_count': int_or_none(like_count),
|
'formats': formats
|
||||||
'dislike_count': int_or_none(dislike_count),
|
|
||||||
}
|
}
|
||||||
|
@ -46,8 +46,8 @@ class FunnyOrDieIE(InfoExtractor):
|
|||||||
links.sort(key=lambda link: 1 if link[1] == 'mp4' else 0)
|
links.sort(key=lambda link: 1 if link[1] == 'mp4' else 0)
|
||||||
|
|
||||||
m3u8_url = self._search_regex(
|
m3u8_url = self._search_regex(
|
||||||
r'<source[^>]+src=(["\'])(?P<url>.+?/master\.m3u8)\1',
|
r'<source[^>]+src=(["\'])(?P<url>.+?/master\.m3u8[^"\']*)\1',
|
||||||
webpage, 'm3u8 url', default=None, group='url')
|
webpage, 'm3u8 url', group='url')
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
|
|
||||||
|
@ -159,9 +159,10 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
'title': title,
|
'title': title,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLAYER_REGEX = r'<iframe src="(?P<xml_root>.+?)/player.*?\.html.*?".*?</iframe>'
|
||||||
|
|
||||||
xml_root = self._html_search_regex(
|
xml_root = self._html_search_regex(
|
||||||
r'<iframe src="(?P<xml_root>.*?)player.html.*?".*?</iframe>',
|
PLAYER_REGEX, start_page, 'xml root', default=None)
|
||||||
start_page, 'xml root', default=None)
|
|
||||||
if xml_root is None:
|
if xml_root is None:
|
||||||
# Probably need to authenticate
|
# Probably need to authenticate
|
||||||
login_res = self._login(webpage_url, display_id)
|
login_res = self._login(webpage_url, display_id)
|
||||||
@ -171,18 +172,19 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
start_page = login_res
|
start_page = login_res
|
||||||
# Grab the url from the authenticated page
|
# Grab the url from the authenticated page
|
||||||
xml_root = self._html_search_regex(
|
xml_root = self._html_search_regex(
|
||||||
r'<iframe src="(.*?)player.html.*?".*?</iframe>',
|
PLAYER_REGEX, start_page, 'xml root')
|
||||||
start_page, 'xml root')
|
|
||||||
|
|
||||||
xml_name = self._html_search_regex(
|
xml_name = self._html_search_regex(
|
||||||
r'<iframe src=".*?\?xml=(.+?\.xml).*?".*?</iframe>',
|
r'<iframe src=".*?\?xml=(.+?\.xml).*?".*?</iframe>',
|
||||||
start_page, 'xml filename', default=None)
|
start_page, 'xml filename', default=None)
|
||||||
if xml_name is None:
|
if xml_name is None:
|
||||||
# Fallback to the older format
|
# Fallback to the older format
|
||||||
xml_name = self._html_search_regex(r'<iframe src=".*?\?xmlURL=xml/(?P<xml_file>.+?\.xml).*?".*?</iframe>', start_page, 'xml filename')
|
xml_name = self._html_search_regex(
|
||||||
|
r'<iframe src=".*?\?xmlURL=xml/(?P<xml_file>.+?\.xml).*?".*?</iframe>',
|
||||||
|
start_page, 'xml filename')
|
||||||
|
|
||||||
xml_description_url = xml_root + 'xml/' + xml_name
|
xml_description = self._download_xml(
|
||||||
xml_description = self._download_xml(xml_description_url, display_id)
|
'%s/xml/%s' % (xml_root, xml_name), display_id)
|
||||||
|
|
||||||
video_title = xml_description.find('./metadata/title').text
|
video_title = xml_description.find('./metadata/title').text
|
||||||
video_formats = self._parse_mp4(xml_description)
|
video_formats = self._parse_mp4(xml_description)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..utils import unified_strdate
|
||||||
|
|
||||||
|
|
||||||
class GlideIE(InfoExtractor):
|
class GlideIE(InfoExtractor):
|
||||||
@ -15,26 +16,38 @@ class GlideIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Damon Timm\'s Glide message',
|
'title': 'Damon Timm\'s Glide message',
|
||||||
'thumbnail': 're:^https?://.*?\.cloudfront\.net/.*\.jpg$',
|
'thumbnail': 're:^https?://.*?\.cloudfront\.net/.*\.jpg$',
|
||||||
|
'uploader': 'Damon Timm',
|
||||||
|
'upload_date': '20140919',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
title = self._html_search_regex(
|
title = self._html_search_regex(
|
||||||
r'<title>(.*?)</title>', webpage, 'title')
|
r'<title>(.+?)</title>', webpage, 'title')
|
||||||
video_url = self.http_scheme() + self._search_regex(
|
video_url = self._proto_relative_url(self._search_regex(
|
||||||
r'<source src="(.*?)" type="video/mp4">', webpage, 'video URL')
|
r'<source[^>]+src=(["\'])(?P<url>.+?)\1',
|
||||||
thumbnail_url = self._search_regex(
|
webpage, 'video URL', default=None,
|
||||||
r'<img id="video-thumbnail" src="(.*?)"',
|
group='url')) or self._og_search_video_url(webpage)
|
||||||
webpage, 'thumbnail url', fatal=False)
|
thumbnail = self._proto_relative_url(self._search_regex(
|
||||||
thumbnail = (
|
r'<img[^>]+id=["\']video-thumbnail["\'][^>]+src=(["\'])(?P<url>.+?)\1',
|
||||||
thumbnail_url if thumbnail_url is None
|
webpage, 'thumbnail url', default=None,
|
||||||
else self.http_scheme() + thumbnail_url)
|
group='url')) or self._og_search_thumbnail(webpage)
|
||||||
|
uploader = self._search_regex(
|
||||||
|
r'<div[^>]+class=["\']info-name["\'][^>]*>([^<]+)',
|
||||||
|
webpage, 'uploader', fatal=False)
|
||||||
|
upload_date = unified_strdate(self._search_regex(
|
||||||
|
r'<div[^>]+class="info-date"[^>]*>([^<]+)',
|
||||||
|
webpage, 'upload date', fatal=False))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'url': video_url,
|
'url': video_url,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
|
'uploader': uploader,
|
||||||
|
'upload_date': upload_date,
|
||||||
}
|
}
|
||||||
|
@ -1,93 +1,91 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
|
compat_parse_qs,
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
compat_urllib_parse_urlencode,
|
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
xpath_with_ns,
|
determine_ext,
|
||||||
|
int_or_none,
|
||||||
|
xpath_text,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class InternetVideoArchiveIE(InfoExtractor):
|
class InternetVideoArchiveIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://video\.internetvideoarchive\.net/flash/players/.*?\?.*?publishedid.*?'
|
_VALID_URL = r'https?://video\.internetvideoarchive\.net/(?:player|flash/players)/.*?\?.*?publishedid.*?'
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://video.internetvideoarchive.net/flash/players/flashconfiguration.aspx?customerid=69249&publishedid=452693&playerid=247',
|
'url': 'http://video.internetvideoarchive.net/player/6/configuration.ashx?customerid=69249&publishedid=194487&reporttag=vdbetatitle&playerid=641&autolist=0&domain=www.videodetective.com&maxrate=high&minrate=low&socialplayer=false',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '452693',
|
'id': '194487',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'SKYFALL',
|
'title': 'KICK-ASS 2',
|
||||||
'description': 'In SKYFALL, Bond\'s loyalty to M is tested as her past comes back to haunt her. As MI6 comes under attack, 007 must track down and destroy the threat, no matter how personal the cost.',
|
'description': 'md5:c189d5b7280400630a1d3dd17eaa8d8a',
|
||||||
'duration': 152,
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _build_url(query):
|
def _build_json_url(query):
|
||||||
return 'http://video.internetvideoarchive.net/flash/players/flashconfiguration.aspx?' + query
|
return 'http://video.internetvideoarchive.net/player/6/configuration.ashx?' + query
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _clean_query(query):
|
def _build_xml_url(query):
|
||||||
NEEDED_ARGS = ['publishedid', 'customerid']
|
return 'http://video.internetvideoarchive.net/flash/players/flashconfiguration.aspx?' + query
|
||||||
query_dic = compat_urlparse.parse_qs(query)
|
|
||||||
cleaned_dic = dict((k, v[0]) for (k, v) in query_dic.items() if k in NEEDED_ARGS)
|
|
||||||
# Other player ids return m3u8 urls
|
|
||||||
cleaned_dic['playerid'] = '247'
|
|
||||||
cleaned_dic['videokbrate'] = '100000'
|
|
||||||
return compat_urllib_parse_urlencode(cleaned_dic)
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
query = compat_urlparse.urlparse(url).query
|
query = compat_urlparse.urlparse(url).query
|
||||||
query_dic = compat_urlparse.parse_qs(query)
|
query_dic = compat_parse_qs(query)
|
||||||
video_id = query_dic['publishedid'][0]
|
video_id = query_dic['publishedid'][0]
|
||||||
url = self._build_url(query)
|
|
||||||
|
|
||||||
flashconfiguration = self._download_xml(url, video_id,
|
if '/player/' in url:
|
||||||
'Downloading flash configuration')
|
configuration = self._download_json(url, video_id)
|
||||||
file_url = flashconfiguration.find('file').text
|
|
||||||
file_url = file_url.replace('/playlist.aspx', '/mrssplaylist.aspx')
|
|
||||||
# Replace some of the parameters in the query to get the best quality
|
|
||||||
# and http links (no m3u8 manifests)
|
|
||||||
file_url = re.sub(r'(?<=\?)(.+)$',
|
|
||||||
lambda m: self._clean_query(m.group()),
|
|
||||||
file_url)
|
|
||||||
info = self._download_xml(file_url, video_id,
|
|
||||||
'Downloading video info')
|
|
||||||
item = info.find('channel/item')
|
|
||||||
|
|
||||||
def _bp(p):
|
# There are multiple videos in the playlist whlie only the first one
|
||||||
return xpath_with_ns(
|
# matches the video played in browsers
|
||||||
p,
|
video_info = configuration['playlist'][0]
|
||||||
{
|
|
||||||
'media': 'http://search.yahoo.com/mrss/',
|
|
||||||
'jwplayer': 'http://developer.longtailvideo.com/trac/wiki/FlashFormats',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
formats = []
|
|
||||||
for content in item.findall(_bp('media:group/media:content')):
|
|
||||||
attr = content.attrib
|
|
||||||
f_url = attr['url']
|
|
||||||
width = int(attr['width'])
|
|
||||||
bitrate = int(attr['bitrate'])
|
|
||||||
format_id = '%d-%dk' % (width, bitrate)
|
|
||||||
formats.append({
|
|
||||||
'format_id': format_id,
|
|
||||||
'url': f_url,
|
|
||||||
'width': width,
|
|
||||||
'tbr': bitrate,
|
|
||||||
})
|
|
||||||
|
|
||||||
self._sort_formats(formats)
|
formats = []
|
||||||
|
for source in video_info['sources']:
|
||||||
|
file_url = source['file']
|
||||||
|
if determine_ext(file_url) == 'm3u8':
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
file_url, video_id, ext='mp4', m3u8_id='hls'))
|
||||||
|
else:
|
||||||
|
a_format = {
|
||||||
|
'url': file_url,
|
||||||
|
}
|
||||||
|
|
||||||
|
if source.get('label') and source['label'][-4:] == ' kbs':
|
||||||
|
tbr = int_or_none(source['label'][:-4])
|
||||||
|
a_format.update({
|
||||||
|
'tbr': tbr,
|
||||||
|
'format_id': 'http-%d' % tbr,
|
||||||
|
})
|
||||||
|
formats.append(a_format)
|
||||||
|
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
title = video_info['title']
|
||||||
|
description = video_info.get('description')
|
||||||
|
thumbnail = video_info.get('image')
|
||||||
|
else:
|
||||||
|
configuration = self._download_xml(url, video_id)
|
||||||
|
formats = [{
|
||||||
|
'url': xpath_text(configuration, './file', 'file URL', fatal=True),
|
||||||
|
}]
|
||||||
|
thumbnail = xpath_text(configuration, './image', 'thumbnail')
|
||||||
|
title = 'InternetVideoArchive video %s' % video_id
|
||||||
|
description = None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': item.find('title').text,
|
'title': title,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'thumbnail': item.find(_bp('media:thumbnail')).attrib['url'],
|
'thumbnail': thumbnail,
|
||||||
'description': item.find('description').text,
|
'description': description,
|
||||||
'duration': int(attr['duration']),
|
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,15 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import int_or_none
|
from ..utils import (
|
||||||
|
float_or_none,
|
||||||
|
int_or_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class JWPlatformBaseIE(InfoExtractor):
|
class JWPlatformBaseIE(InfoExtractor):
|
||||||
def _parse_jwplayer_data(self, jwplayer_data, video_id, require_title=True):
|
def _parse_jwplayer_data(self, jwplayer_data, video_id, require_title=True):
|
||||||
video_data = jwplayer_data['playlist'][0]
|
video_data = jwplayer_data['playlist'][0]
|
||||||
subtitles = {}
|
|
||||||
for track in video_data['tracks']:
|
|
||||||
if track['kind'] == 'captions':
|
|
||||||
subtitles[track['label']] = [{'url': self._proto_relative_url(track['file'])}]
|
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for source in video_data['sources']:
|
for source in video_data['sources']:
|
||||||
@ -35,12 +34,22 @@ class JWPlatformBaseIE(InfoExtractor):
|
|||||||
})
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
subtitles = {}
|
||||||
|
tracks = video_data.get('tracks')
|
||||||
|
if tracks and isinstance(tracks, list):
|
||||||
|
for track in tracks:
|
||||||
|
if track.get('file') and track.get('kind') == 'captions':
|
||||||
|
subtitles.setdefault(track.get('label') or 'en', []).append({
|
||||||
|
'url': self._proto_relative_url(track['file'])
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': video_data['title'] if require_title else video_data.get('title'),
|
'title': video_data['title'] if require_title else video_data.get('title'),
|
||||||
'description': video_data.get('description'),
|
'description': video_data.get('description'),
|
||||||
'thumbnail': self._proto_relative_url(video_data.get('image')),
|
'thumbnail': self._proto_relative_url(video_data.get('image')),
|
||||||
'timestamp': int_or_none(video_data.get('pubdate')),
|
'timestamp': int_or_none(video_data.get('pubdate')),
|
||||||
|
'duration': float_or_none(jwplayer_data.get('duration')),
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .videodetective import VideoDetectiveIE
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_urlparse
|
||||||
|
from .internetvideoarchive import InternetVideoArchiveIE
|
||||||
|
|
||||||
|
|
||||||
# It just uses the same method as videodetective.com,
|
class RottenTomatoesIE(InfoExtractor):
|
||||||
# the internetvideoarchive.com is extracted from the og:video property
|
|
||||||
class RottenTomatoesIE(VideoDetectiveIE):
|
|
||||||
_VALID_URL = r'https?://www\.rottentomatoes\.com/m/[^/]+/trailers/(?P<id>\d+)'
|
_VALID_URL = r'https?://www\.rottentomatoes\.com/m/[^/]+/trailers/(?P<id>\d+)'
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
@ -13,7 +13,19 @@ class RottenTomatoesIE(VideoDetectiveIE):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '613340',
|
'id': '613340',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'TOY STORY 3',
|
'title': 'Toy Story 3',
|
||||||
'description': 'From the creators of the beloved TOY STORY films, comes a story that will reunite the gang in a whole new way.',
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
og_video = self._og_search_video_url(webpage)
|
||||||
|
query = compat_urlparse.urlparse(og_video).query
|
||||||
|
|
||||||
|
return {
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'url': InternetVideoArchiveIE._build_xml_url(query),
|
||||||
|
'ie_key': InternetVideoArchiveIE.ie_key(),
|
||||||
|
'title': self._og_search_title(webpage),
|
||||||
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .jwplatform import JWPlatformBaseIE
|
||||||
from ..compat import compat_urlparse
|
from ..utils import js_to_json
|
||||||
from ..utils import (
|
|
||||||
ExtractorError,
|
|
||||||
js_to_json,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ScreencastOMaticIE(InfoExtractor):
|
class ScreencastOMaticIE(JWPlatformBaseIE):
|
||||||
_VALID_URL = r'https?://screencast-o-matic\.com/watch/(?P<id>[0-9a-zA-Z]+)'
|
_VALID_URL = r'https?://screencast-o-matic\.com/watch/(?P<id>[0-9a-zA-Z]+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://screencast-o-matic.com/watch/c2lD3BeOPl',
|
'url': 'http://screencast-o-matic.com/watch/c2lD3BeOPl',
|
||||||
@ -20,6 +16,7 @@ class ScreencastOMaticIE(InfoExtractor):
|
|||||||
'title': 'Welcome to 3-4 Philosophy @ DECV!',
|
'title': 'Welcome to 3-4 Philosophy @ DECV!',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': 're:^https?://.*\.jpg$',
|
||||||
'description': 'as the title says! also: some general info re 1) VCE philosophy and 2) distance learning.',
|
'description': 'as the title says! also: some general info re 1) VCE philosophy and 2) distance learning.',
|
||||||
|
'duration': 369.163,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,23 +24,14 @@ class ScreencastOMaticIE(InfoExtractor):
|
|||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
setup_js = self._search_regex(
|
jwplayer_data = self._parse_json(
|
||||||
r"(?s)jwplayer\('mp4Player'\).setup\((\{.*?\})\);",
|
self._search_regex(
|
||||||
webpage, 'setup code')
|
r"(?s)jwplayer\('mp4Player'\).setup\((\{.*?\})\);", webpage, 'setup code'),
|
||||||
data = self._parse_json(setup_js, video_id, transform_source=js_to_json)
|
video_id, transform_source=js_to_json)
|
||||||
try:
|
|
||||||
video_data = next(
|
|
||||||
m for m in data['modes'] if m.get('type') == 'html5')
|
|
||||||
except StopIteration:
|
|
||||||
raise ExtractorError('Could not find any video entries!')
|
|
||||||
video_url = compat_urlparse.urljoin(url, video_data['config']['file'])
|
|
||||||
thumbnail = data.get('image')
|
|
||||||
|
|
||||||
return {
|
info_dict = self._parse_jwplayer_data(jwplayer_data, video_id, require_title=False)
|
||||||
'id': video_id,
|
info_dict.update({
|
||||||
'title': self._og_search_title(webpage),
|
'title': self._og_search_title(webpage),
|
||||||
'description': self._og_search_description(webpage),
|
'description': self._og_search_description(webpage),
|
||||||
'url': video_url,
|
})
|
||||||
'ext': 'mp4',
|
return info_dict
|
||||||
'thumbnail': thumbnail,
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
class TeleBruxellesIE(InfoExtractor):
|
class TeleBruxellesIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?telebruxelles\.be/(news|sport|dernier-jt)/?(?P<id>[^/#?]+)'
|
_VALID_URL = r'https?://(?:www\.)?(?:telebruxelles|bx1)\.be/(news|sport|dernier-jt)/?(?P<id>[^/#?]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.telebruxelles.be/news/auditions-devant-parlement-francken-galant-tres-attendus/',
|
'url': 'http://www.telebruxelles.be/news/auditions-devant-parlement-francken-galant-tres-attendus/',
|
||||||
'md5': '59439e568c9ee42fb77588b2096b214f',
|
'md5': '59439e568c9ee42fb77588b2096b214f',
|
||||||
@ -39,18 +41,18 @@ class TeleBruxellesIE(InfoExtractor):
|
|||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
article_id = self._html_search_regex(
|
article_id = self._html_search_regex(
|
||||||
r"<article id=\"post-(\d+)\"", webpage, 'article ID')
|
r"<article id=\"post-(\d+)\"", webpage, 'article ID', default=None)
|
||||||
title = self._html_search_regex(
|
title = self._html_search_regex(
|
||||||
r'<h1 class=\"entry-title\">(.*?)</h1>', webpage, 'title')
|
r'<h1 class=\"entry-title\">(.*?)</h1>', webpage, 'title')
|
||||||
description = self._og_search_description(webpage)
|
description = self._og_search_description(webpage, default=None)
|
||||||
|
|
||||||
rtmp_url = self._html_search_regex(
|
rtmp_url = self._html_search_regex(
|
||||||
r"file: \"(rtmp://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}/vod/mp4:\" \+ \"\w+\" \+ \".mp4)\"",
|
r'file\s*:\s*"(rtmp://[^/]+/vod/mp4:"\s*\+\s*"[^"]+"\s*\+\s*".mp4)"',
|
||||||
webpage, 'RTMP url')
|
webpage, 'RTMP url')
|
||||||
rtmp_url = rtmp_url.replace("\" + \"", "")
|
rtmp_url = re.sub(r'"\s*\+\s*"', '', rtmp_url)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': article_id,
|
'id': article_id or display_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': description,
|
'description': description,
|
||||||
|
@ -76,7 +76,11 @@ class TNAFlixNetworkBaseIE(InfoExtractor):
|
|||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
cfg_url = self._proto_relative_url(self._html_search_regex(
|
cfg_url = self._proto_relative_url(self._html_search_regex(
|
||||||
self._CONFIG_REGEX, webpage, 'flashvars.config'), 'http:')
|
self._CONFIG_REGEX, webpage, 'flashvars.config', default=None), 'http:')
|
||||||
|
|
||||||
|
if not cfg_url:
|
||||||
|
inputs = self._hidden_inputs(webpage)
|
||||||
|
cfg_url = 'https://cdn-fck.tnaflix.com/tnaflix/%s.fid?key=%s' % (inputs['vkey'], inputs['nkey'])
|
||||||
|
|
||||||
cfg_xml = self._download_xml(
|
cfg_xml = self._download_xml(
|
||||||
cfg_url, display_id, 'Downloading metadata',
|
cfg_url, display_id, 'Downloading metadata',
|
||||||
@ -132,7 +136,7 @@ class TNAFlixNetworkBaseIE(InfoExtractor):
|
|||||||
average_rating = float_or_none(extract_field(self._AVERAGE_RATING_REGEX, 'average rating'))
|
average_rating = float_or_none(extract_field(self._AVERAGE_RATING_REGEX, 'average rating'))
|
||||||
|
|
||||||
categories_str = extract_field(self._CATEGORIES_REGEX, 'categories')
|
categories_str = extract_field(self._CATEGORIES_REGEX, 'categories')
|
||||||
categories = categories_str.split(', ') if categories_str is not None else []
|
categories = [c.strip() for c in categories_str.split(',')] if categories_str is not None else []
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
@ -186,13 +190,14 @@ class TNAFlixIE(TNAFlixNetworkBaseIE):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?tnaflix\.com/[^/]+/(?P<display_id>[^/]+)/video(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?tnaflix\.com/[^/]+/(?P<display_id>[^/]+)/video(?P<id>\d+)'
|
||||||
|
|
||||||
_TITLE_REGEX = r'<title>(.+?) - TNAFlix Porn Videos</title>'
|
_TITLE_REGEX = r'<title>(.+?) - TNAFlix Porn Videos</title>'
|
||||||
_DESCRIPTION_REGEX = r'<h3 itemprop="description">([^<]+)</h3>'
|
_DESCRIPTION_REGEX = r'<meta[^>]+name="description"[^>]+content="([^"]+)"'
|
||||||
_UPLOADER_REGEX = r'(?s)<span[^>]+class="infoTitle"[^>]*>Uploaded By:</span>(.+?)<div'
|
_UPLOADER_REGEX = r'<i>\s*Verified Member\s*</i>\s*<h1>(.+?)</h1>'
|
||||||
|
_CATEGORIES_REGEX = r'(?s)<span[^>]*>Categories:</span>(.+?)</div>'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
# anonymous uploader, no categories
|
# anonymous uploader, no categories
|
||||||
'url': 'http://www.tnaflix.com/porn-stars/Carmella-Decesare-striptease/video553878',
|
'url': 'http://www.tnaflix.com/porn-stars/Carmella-Decesare-striptease/video553878',
|
||||||
'md5': 'ecf3498417d09216374fc5907f9c6ec0',
|
'md5': '7e569419fe6d69543d01e6be22f5f7c4',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '553878',
|
'id': '553878',
|
||||||
'display_id': 'Carmella-Decesare-striptease',
|
'display_id': 'Carmella-Decesare-striptease',
|
||||||
@ -201,17 +206,16 @@ class TNAFlixIE(TNAFlixNetworkBaseIE):
|
|||||||
'thumbnail': 're:https?://.*\.jpg$',
|
'thumbnail': 're:https?://.*\.jpg$',
|
||||||
'duration': 91,
|
'duration': 91,
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
'uploader': 'Anonymous',
|
'categories': ['Porn Stars'],
|
||||||
'categories': [],
|
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
# non-anonymous uploader, categories
|
# non-anonymous uploader, categories
|
||||||
'url': 'https://www.tnaflix.com/teen-porn/Educational-xxx-video/video6538',
|
'url': 'https://www.tnaflix.com/teen-porn/Educational-xxx-video/video6538',
|
||||||
'md5': '0f5d4d490dbfd117b8607054248a07c0',
|
'md5': 'fcba2636572895aba116171a899a5658',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '6538',
|
'id': '6538',
|
||||||
'display_id': 'Educational-xxx-video',
|
'display_id': 'Educational-xxx-video',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'Educational xxx video',
|
'title': 'Educational xxx video',
|
||||||
'description': 'md5:b4fab8f88a8621c8fabd361a173fe5b8',
|
'description': 'md5:b4fab8f88a8621c8fabd361a173fe5b8',
|
||||||
'thumbnail': 're:https?://.*\.jpg$',
|
'thumbnail': 're:https?://.*\.jpg$',
|
||||||
|
@ -14,8 +14,11 @@ class VideoDetectiveIE(InfoExtractor):
|
|||||||
'id': '194487',
|
'id': '194487',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'KICK-ASS 2',
|
'title': 'KICK-ASS 2',
|
||||||
'description': 'md5:65ba37ad619165afac7d432eaded6013',
|
'description': 'md5:c189d5b7280400630a1d3dd17eaa8d8a',
|
||||||
'duration': 138,
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,4 +27,4 @@ class VideoDetectiveIE(InfoExtractor):
|
|||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
og_video = self._og_search_video_url(webpage)
|
og_video = self._og_search_video_url(webpage)
|
||||||
query = compat_urlparse.urlparse(og_video).query
|
query = compat_urlparse.urlparse(og_video).query
|
||||||
return self.url_result(InternetVideoArchiveIE._build_url(query), ie=InternetVideoArchiveIE.ie_key())
|
return self.url_result(InternetVideoArchiveIE._build_json_url(query), ie=InternetVideoArchiveIE.ie_key())
|
||||||
|
@ -1792,6 +1792,8 @@ def urlencode_postdata(*args, **kargs):
|
|||||||
|
|
||||||
|
|
||||||
def update_url_query(url, query):
|
def update_url_query(url, query):
|
||||||
|
if not query:
|
||||||
|
return url
|
||||||
parsed_url = compat_urlparse.urlparse(url)
|
parsed_url = compat_urlparse.urlparse(url)
|
||||||
qs = compat_parse_qs(parsed_url.query)
|
qs = compat_parse_qs(parsed_url.query)
|
||||||
qs.update(query)
|
qs.update(query)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user