From 90cd207d52d77e075e62cc256cc39dceee05bbf3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rog=C3=A9rio=20Brito?= Date: Mon, 11 Nov 2013 22:27:50 -0200 Subject: [PATCH] Imported Upstream version 2013.11.11 --- README.md | 29 ++-- README.txt | 29 ++-- devscripts/check-porn.py | 39 +++++ setup.py | 7 +- test/helper.py | 17 +++ test/test_YoutubeDL.py | 20 ++- test/test_dailymotion_subtitles.py | 70 --------- test/test_download.py | 75 +++++++--- test/test_playlists.py | 18 +++ test/test_subtitles.py | 211 +++++++++++++++++++++++++++ test/test_youtube_subtitles.py | 95 ------------ youtube-dl | Bin 244153 -> 261436 bytes youtube-dl.1 | 29 ++-- youtube-dl.bash-completion | 2 +- youtube_dl/FileDownloader.py | 58 +++++--- youtube_dl/YoutubeDL.py | 88 ++++++----- youtube_dl/__init__.py | 13 +- youtube_dl/extractor/__init__.py | 13 ++ youtube_dl/extractor/addanime.py | 25 +++- youtube_dl/extractor/arte.py | 48 ++++-- youtube_dl/extractor/bambuser.py | 80 ++++++++++ youtube_dl/extractor/brightcove.py | 61 ++++++-- youtube_dl/extractor/canalc2.py | 6 +- youtube_dl/extractor/cinemassacre.py | 27 ++-- youtube_dl/extractor/cnn.py | 2 +- youtube_dl/extractor/common.py | 34 ++++- youtube_dl/extractor/dailymotion.py | 70 ++++++--- youtube_dl/extractor/depositfiles.py | 2 +- youtube_dl/extractor/eighttracks.py | 4 +- youtube_dl/extractor/eitb.py | 37 +++++ youtube_dl/extractor/exfm.py | 16 +- youtube_dl/extractor/extremetube.py | 50 +++++++ youtube_dl/extractor/facebook.py | 71 +++++---- youtube_dl/extractor/faz.py | 6 +- youtube_dl/extractor/generic.py | 48 +++++- youtube_dl/extractor/hypem.py | 4 +- youtube_dl/extractor/instagram.py | 2 +- youtube_dl/extractor/kankan.py | 7 +- youtube_dl/extractor/keezmovies.py | 61 ++++++++ youtube_dl/extractor/livestream.py | 10 +- youtube_dl/extractor/metacafe.py | 53 ++++++- youtube_dl/extractor/mofosex.py | 49 +++++++ youtube_dl/extractor/mtv.py | 3 + youtube_dl/extractor/myspace.py | 48 ++++++ youtube_dl/extractor/nowvideo.py | 5 +- youtube_dl/extractor/pornhub.py | 69 +++++++++ youtube_dl/extractor/pornotube.py | 3 +- youtube_dl/extractor/rtlnow.py | 11 +- youtube_dl/extractor/slashdot.py | 1 + youtube_dl/extractor/soundcloud.py | 130 +++++++---------- youtube_dl/extractor/space.py | 35 +++++ youtube_dl/extractor/spankwire.py | 74 ++++++++++ youtube_dl/extractor/subtitles.py | 12 +- youtube_dl/extractor/teamcoco.py | 41 +++++- youtube_dl/extractor/ted.py | 36 ++++- youtube_dl/extractor/tube8.py | 65 +++++++++ youtube_dl/extractor/vevo.py | 94 +++++++++--- youtube_dl/extractor/viddler.py | 2 +- youtube_dl/extractor/vimeo.py | 56 +++++-- youtube_dl/extractor/vk.py | 45 ++++++ youtube_dl/extractor/weibo.py | 1 + youtube_dl/extractor/xhamster.py | 52 +++++-- youtube_dl/extractor/xnxx.py | 2 +- youtube_dl/extractor/xtube.py | 55 +++++++ youtube_dl/extractor/yahoo.py | 2 +- youtube_dl/extractor/youjizz.py | 8 +- youtube_dl/extractor/youku.py | 6 +- youtube_dl/extractor/youporn.py | 89 ++++------- youtube_dl/extractor/youtube.py | 107 ++++++-------- youtube_dl/utils.py | 5 + youtube_dl/version.py | 2 +- 71 files changed, 1919 insertions(+), 726 deletions(-) create mode 100644 devscripts/check-porn.py delete mode 100644 test/test_dailymotion_subtitles.py create mode 100644 test/test_subtitles.py delete mode 100644 test/test_youtube_subtitles.py create mode 100644 youtube_dl/extractor/bambuser.py create mode 100644 youtube_dl/extractor/eitb.py create mode 100644 youtube_dl/extractor/extremetube.py create mode 100644 youtube_dl/extractor/keezmovies.py create mode 100644 youtube_dl/extractor/mofosex.py create mode 100644 youtube_dl/extractor/myspace.py create mode 100644 youtube_dl/extractor/pornhub.py create mode 100644 youtube_dl/extractor/space.py create mode 100644 youtube_dl/extractor/spankwire.py create mode 100644 youtube_dl/extractor/tube8.py create mode 100644 youtube_dl/extractor/vk.py create mode 100644 youtube_dl/extractor/xtube.py diff --git a/README.md b/README.md index 2b8db0c..6632e58 100644 --- a/README.md +++ b/README.md @@ -79,24 +79,27 @@ which means you can modify it, redistribute it or use it however you like. different, %(autonumber)s to get an automatically incremented number, %(ext)s for the filename extension, %(format)s for the format description - (like "22 - 1280x720" or "HD")%(upload_date)s for - the upload date (YYYYMMDD), %(extractor)s for the - provider (youtube, metacafe, etc), %(id)s for the - video id , %(playlist)s for the playlist the - video is in, %(playlist_index)s for the position - in the playlist and %% for a literal percent. Use - - to output to stdout. Can also be used to - download to a different directory, for example - with -o '/my/downloads/%(uploader)s/%(title)s-%(i - d)s.%(ext)s' . + (like "22 - 1280x720" or "HD"),%(format_id)s for + the unique id of the format (like Youtube's + itags: "137"),%(upload_date)s for the upload date + (YYYYMMDD), %(extractor)s for the provider + (youtube, metacafe, etc), %(id)s for the video id + , %(playlist)s for the playlist the video is in, + %(playlist_index)s for the position in the + playlist and %% for a literal percent. Use - to + output to stdout. Can also be used to download to + a different directory, for example with -o '/my/d + ownloads/%(uploader)s/%(title)s-%(id)s.%(ext)s' . --autonumber-size NUMBER Specifies the number of digits in %(autonumber)s when it is present in output filename template or - --autonumber option is given + --auto-number option is given --restrict-filenames Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames -a, --batch-file FILE file containing URLs to download ('-' for stdin) -w, --no-overwrites do not overwrite files - -c, --continue resume partially downloaded files + -c, --continue force resume of partially downloaded files. By + default, youtube-dl will resume downloads if + possible. --no-continue do not resume partially downloaded files (restart from beginning) --cookies FILE file to read cookies from and dump cookie jar in @@ -126,6 +129,8 @@ which means you can modify it, redistribute it or use it however you like. -v, --verbose print various debugging information --dump-intermediate-pages print downloaded pages to debug problems(very verbose) + --write-pages Write downloaded pages to files in the current + directory ## Video Format Options: -f, --format FORMAT video format code, specifiy the order of diff --git a/README.txt b/README.txt index b773576..4b400fd 100644 --- a/README.txt +++ b/README.txt @@ -92,24 +92,27 @@ Filesystem Options: different, %(autonumber)s to get an automatically incremented number, %(ext)s for the filename extension, %(format)s for the format description - (like "22 - 1280x720" or "HD")%(upload_date)s for - the upload date (YYYYMMDD), %(extractor)s for the - provider (youtube, metacafe, etc), %(id)s for the - video id , %(playlist)s for the playlist the - video is in, %(playlist_index)s for the position - in the playlist and %% for a literal percent. Use - - to output to stdout. Can also be used to - download to a different directory, for example - with -o '/my/downloads/%(uploader)s/%(title)s-%(i - d)s.%(ext)s' . + (like "22 - 1280x720" or "HD"),%(format_id)s for + the unique id of the format (like Youtube's + itags: "137"),%(upload_date)s for the upload date + (YYYYMMDD), %(extractor)s for the provider + (youtube, metacafe, etc), %(id)s for the video id + , %(playlist)s for the playlist the video is in, + %(playlist_index)s for the position in the + playlist and %% for a literal percent. Use - to + output to stdout. Can also be used to download to + a different directory, for example with -o '/my/d + ownloads/%(uploader)s/%(title)s-%(id)s.%(ext)s' . --autonumber-size NUMBER Specifies the number of digits in %(autonumber)s when it is present in output filename template or - --autonumber option is given + --auto-number option is given --restrict-filenames Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames -a, --batch-file FILE file containing URLs to download ('-' for stdin) -w, --no-overwrites do not overwrite files - -c, --continue resume partially downloaded files + -c, --continue force resume of partially downloaded files. By + default, youtube-dl will resume downloads if + possible. --no-continue do not resume partially downloaded files (restart from beginning) --cookies FILE file to read cookies from and dump cookie jar in @@ -141,6 +144,8 @@ Verbosity / Simulation Options: -v, --verbose print various debugging information --dump-intermediate-pages print downloaded pages to debug problems(very verbose) + --write-pages Write downloaded pages to files in the current + directory Video Format Options: --------------------- diff --git a/devscripts/check-porn.py b/devscripts/check-porn.py new file mode 100644 index 0000000..63401fe --- /dev/null +++ b/devscripts/check-porn.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +This script employs a VERY basic heuristic ('porn' in webpage.lower()) to check +if we are not 'age_limit' tagging some porn site +""" + +# Allow direct execution +import os +import sys +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from test.helper import get_testcases +from youtube_dl.utils import compat_urllib_request + +for test in get_testcases(): + try: + webpage = compat_urllib_request.urlopen(test['url'], timeout=10).read() + except: + print('\nFail: {0}'.format(test['name'])) + continue + + webpage = webpage.decode('utf8', 'replace') + + if 'porn' in webpage.lower() and ('info_dict' not in test + or 'age_limit' not in test['info_dict'] + or test['info_dict']['age_limit'] != 18): + print('\nPotential missing age_limit check: {0}'.format(test['name'])) + + elif 'porn' not in webpage.lower() and ('info_dict' in test and + 'age_limit' in test['info_dict'] and + test['info_dict']['age_limit'] == 18): + print('\nPotential false negative: {0}'.format(test['name'])) + + else: + sys.stdout.write('.') + sys.stdout.flush() + +print() diff --git a/setup.py b/setup.py index 2391c52..aa7cfca 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,10 @@ import sys try: from setuptools import setup + setuptools_available = True except ImportError: from distutils.core import setup + setuptools_available = False try: # This will create an exe that needs Microsoft Visual C++ 2008 @@ -43,13 +45,16 @@ if len(sys.argv) >= 2 and sys.argv[1] == 'py2exe': params = py2exe_params else: params = { - 'scripts': ['bin/youtube-dl'], 'data_files': [ # Installing system-wide would require sudo... ('etc/bash_completion.d', ['youtube-dl.bash-completion']), ('share/doc/youtube_dl', ['README.txt']), ('share/man/man1/', ['youtube-dl.1']) ] } + if setuptools_available: + params['entry_points'] = {'console_scripts': ['youtube-dl = youtube_dl:main']} + else: + params['scripts'] = ['bin/youtube-dl'] # Get the version from youtube_dl/version.py without importing the package exec(compile(open('youtube_dl/version.py').read(), diff --git a/test/helper.py b/test/helper.py index 777119e..d7bf7a8 100644 --- a/test/helper.py +++ b/test/helper.py @@ -5,9 +5,11 @@ import json import os.path import re import types +import sys import youtube_dl.extractor from youtube_dl import YoutubeDL +from youtube_dl.utils import preferredencoding def global_setup(): @@ -33,6 +35,21 @@ def try_rm(filename): raise +def report_warning(message): + ''' + Print the message to stderr, it will be prefixed with 'WARNING:' + If stderr is a tty file the 'WARNING:' will be colored + ''' + if sys.stderr.isatty() and os.name != 'nt': + _msg_header = u'\033[0;33mWARNING:\033[0m' + else: + _msg_header = u'WARNING:' + output = u'%s %s\n' % (_msg_header, message) + if 'b' in getattr(sys.stderr, 'mode', '') or sys.version_info[0] < 3: + output = output.encode(preferredencoding()) + sys.stderr.write(output) + + class FakeYDL(YoutubeDL): def __init__(self, override=None): # Different instances of the downloader can't share the same dictionary diff --git a/test/test_YoutubeDL.py b/test/test_YoutubeDL.py index f8cd1bd..58cf9c3 100644 --- a/test/test_YoutubeDL.py +++ b/test/test_YoutubeDL.py @@ -62,10 +62,10 @@ class TestFormatSelection(unittest.TestCase): def test_format_limit(self): formats = [ - {u'format_id': u'meh'}, - {u'format_id': u'good'}, - {u'format_id': u'great'}, - {u'format_id': u'excellent'}, + {u'format_id': u'meh', u'url': u'http://example.com/meh'}, + {u'format_id': u'good', u'url': u'http://example.com/good'}, + {u'format_id': u'great', u'url': u'http://example.com/great'}, + {u'format_id': u'excellent', u'url': u'http://example.com/exc'}, ] info_dict = { u'formats': formats, u'extractor': u'test', 'id': 'testvid'} @@ -128,6 +128,18 @@ class TestFormatSelection(unittest.TestCase): downloaded = ydl.downloaded_info_dicts[0] self.assertEqual(downloaded['format_id'], u'35') + def test_add_extra_info(self): + test_dict = { + 'extractor': 'Foo', + } + extra_info = { + 'extractor': 'Bar', + 'playlist': 'funny videos', + } + YDL.add_extra_info(test_dict, extra_info) + self.assertEqual(test_dict['extractor'], 'Foo') + self.assertEqual(test_dict['playlist'], 'funny videos') + if __name__ == '__main__': unittest.main() diff --git a/test/test_dailymotion_subtitles.py b/test/test_dailymotion_subtitles.py deleted file mode 100644 index c596415..0000000 --- a/test/test_dailymotion_subtitles.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python - -# Allow direct execution -import os -import sys -import unittest -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from test.helper import FakeYDL, global_setup, md5 -global_setup() - - -from youtube_dl.extractor import DailymotionIE - -class TestDailymotionSubtitles(unittest.TestCase): - def setUp(self): - self.DL = FakeYDL() - self.url = 'http://www.dailymotion.com/video/xczg00' - def getInfoDict(self): - IE = DailymotionIE(self.DL) - info_dict = IE.extract(self.url) - return info_dict - def getSubtitles(self): - info_dict = self.getInfoDict() - return info_dict[0]['subtitles'] - def test_no_writesubtitles(self): - subtitles = self.getSubtitles() - self.assertEqual(subtitles, None) - def test_subtitles(self): - self.DL.params['writesubtitles'] = True - subtitles = self.getSubtitles() - self.assertEqual(md5(subtitles['en']), '976553874490cba125086bbfea3ff76f') - def test_subtitles_lang(self): - self.DL.params['writesubtitles'] = True - self.DL.params['subtitleslangs'] = ['fr'] - subtitles = self.getSubtitles() - self.assertEqual(md5(subtitles['fr']), '594564ec7d588942e384e920e5341792') - def test_allsubtitles(self): - self.DL.params['writesubtitles'] = True - self.DL.params['allsubtitles'] = True - subtitles = self.getSubtitles() - self.assertEqual(len(subtitles.keys()), 5) - def test_list_subtitles(self): - self.DL.expect_warning(u'Automatic Captions not supported by this server') - self.DL.params['listsubtitles'] = True - info_dict = self.getInfoDict() - self.assertEqual(info_dict, None) - def test_automatic_captions(self): - self.DL.expect_warning(u'Automatic Captions not supported by this server') - self.DL.params['writeautomaticsub'] = True - self.DL.params['subtitleslang'] = ['en'] - subtitles = self.getSubtitles() - self.assertTrue(len(subtitles.keys()) == 0) - def test_nosubtitles(self): - self.DL.expect_warning(u'video doesn\'t have subtitles') - self.url = 'http://www.dailymotion.com/video/x12u166_le-zapping-tele-star-du-08-aout-2013_tv' - self.DL.params['writesubtitles'] = True - self.DL.params['allsubtitles'] = True - subtitles = self.getSubtitles() - self.assertEqual(len(subtitles), 0) - def test_multiple_langs(self): - self.DL.params['writesubtitles'] = True - langs = ['es', 'fr', 'de'] - self.DL.params['subtitleslangs'] = langs - subtitles = self.getSubtitles() - for lang in langs: - self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang) - -if __name__ == '__main__': - unittest.main() diff --git a/test/test_download.py b/test/test_download.py index b9a9be1..16f2008 100644 --- a/test/test_download.py +++ b/test/test_download.py @@ -6,7 +6,14 @@ import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from test.helper import get_params, get_testcases, global_setup, try_rm, md5 +from test.helper import ( + get_params, + get_testcases, + global_setup, + try_rm, + md5, + report_warning +) global_setup() @@ -19,10 +26,12 @@ import youtube_dl.YoutubeDL from youtube_dl.utils import ( compat_str, compat_urllib_error, + compat_HTTPError, DownloadError, ExtractorError, UnavailableVideoError, ) +from youtube_dl.extractor import get_info_extractor RETRIES = 3 @@ -55,17 +64,25 @@ def generator(test_case): def test_template(self): ie = youtube_dl.extractor.get_info_extractor(test_case['name']) + other_ies = [get_info_extractor(ie_key) for ie_key in test_case.get('add_ie', [])] def print_skipping(reason): print('Skipping %s: %s' % (test_case['name'], reason)) - if not ie._WORKING: + if not ie.working(): print_skipping('IE marked as not _WORKING') return - if 'playlist' not in test_case and not test_case['file']: - print_skipping('No output file specified') - return + if 'playlist' not in test_case: + info_dict = test_case.get('info_dict', {}) + if not test_case.get('file') and not (info_dict.get('id') and info_dict.get('ext')): + print_skipping('The output file cannot be know, the "file" ' + 'key is missing or the info_dict is incomplete') + return if 'skip' in test_case: print_skipping(test_case['skip']) return + for other_ie in other_ies: + if not other_ie.working(): + print_skipping(u'test depends on %sIE, marked as not WORKING' % other_ie.ie_key()) + return params = get_params(test_case.get('params', {})) @@ -77,35 +94,47 @@ def generator(test_case): finished_hook_called.add(status['filename']) ydl.fd.add_progress_hook(_hook) + def get_tc_filename(tc): + return tc.get('file') or ydl.prepare_filename(tc.get('info_dict', {})) + test_cases = test_case.get('playlist', [test_case]) - for tc in test_cases: - try_rm(tc['file']) - try_rm(tc['file'] + '.part') - try_rm(tc['file'] + '.info.json') + def try_rm_tcs_files(): + for tc in test_cases: + tc_filename = get_tc_filename(tc) + try_rm(tc_filename) + try_rm(tc_filename + '.part') + try_rm(tc_filename + '.info.json') + try_rm_tcs_files() try: - for retry in range(1, RETRIES + 1): + try_num = 1 + while True: try: ydl.download([test_case['url']]) except (DownloadError, ExtractorError) as err: - if retry == RETRIES: raise - # Check if the exception is not a network related one - if not err.exc_info[0] in (compat_urllib_error.URLError, socket.timeout, UnavailableVideoError): + if not err.exc_info[0] in (compat_urllib_error.URLError, socket.timeout, UnavailableVideoError) or (err.exc_info[0] == compat_HTTPError and err.exc_info[1].code == 503): raise - print('Retrying: {0} failed tries\n\n##########\n\n'.format(retry)) + if try_num == RETRIES: + report_warning(u'Failed due to network errors, skipping...') + return + + print('Retrying: {0} failed tries\n\n##########\n\n'.format(try_num)) + + try_num += 1 else: break for tc in test_cases: + tc_filename = get_tc_filename(tc) if not test_case.get('params', {}).get('skip_download', False): - self.assertTrue(os.path.exists(tc['file']), msg='Missing file ' + tc['file']) - self.assertTrue(tc['file'] in finished_hook_called) - self.assertTrue(os.path.exists(tc['file'] + '.info.json')) + self.assertTrue(os.path.exists(tc_filename), msg='Missing file ' + tc_filename) + self.assertTrue(tc_filename in finished_hook_called) + self.assertTrue(os.path.exists(tc_filename + '.info.json')) if 'md5' in tc: - md5_for_file = _file_md5(tc['file']) + md5_for_file = _file_md5(tc_filename) self.assertEqual(md5_for_file, tc['md5']) - with io.open(tc['file'] + '.info.json', encoding='utf-8') as infof: + with io.open(tc_filename + '.info.json', encoding='utf-8') as infof: info_dict = json.load(infof) for (info_field, expected) in tc.get('info_dict', {}).items(): if isinstance(expected, compat_str) and expected.startswith('md5:'): @@ -125,11 +154,11 @@ def generator(test_case): # Check for the presence of mandatory fields for key in ('id', 'url', 'title', 'ext'): self.assertTrue(key in info_dict.keys() and info_dict[key]) + # Check for mandatory fields that are automatically set by YoutubeDL + for key in ['webpage_url', 'extractor', 'extractor_key']: + self.assertTrue(info_dict.get(key), u'Missing field: %s' % key) finally: - for tc in test_cases: - try_rm(tc['file']) - try_rm(tc['file'] + '.part') - try_rm(tc['file'] + '.info.json') + try_rm_tcs_files() return test_template diff --git a/test/test_playlists.py b/test/test_playlists.py index d6a8d56..706b6bd 100644 --- a/test/test_playlists.py +++ b/test/test_playlists.py @@ -17,9 +17,11 @@ from youtube_dl.extractor import ( DailymotionUserIE, VimeoChannelIE, UstreamChannelIE, + SoundcloudSetIE, SoundcloudUserIE, LivestreamIE, NHLVideocenterIE, + BambuserChannelIE, ) @@ -60,6 +62,14 @@ class TestPlaylists(unittest.TestCase): self.assertEqual(result['id'], u'5124905') self.assertTrue(len(result['entries']) >= 11) + def test_soundcloud_set(self): + dl = FakeYDL() + ie = SoundcloudSetIE(dl) + result = ie.extract('https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep') + self.assertIsPlaylist(result) + self.assertEqual(result['title'], u'The Royal Concept EP') + self.assertTrue(len(result['entries']) >= 6) + def test_soundcloud_user(self): dl = FakeYDL() ie = SoundcloudUserIE(dl) @@ -85,5 +95,13 @@ class TestPlaylists(unittest.TestCase): self.assertEqual(result['title'], u'Highlights') self.assertEqual(len(result['entries']), 12) + def test_bambuser_channel(self): + dl = FakeYDL() + ie = BambuserChannelIE(dl) + result = ie.extract('http://bambuser.com/channel/pixelversity') + self.assertIsPlaylist(result) + self.assertEqual(result['title'], u'pixelversity') + self.assertTrue(len(result['entries']) >= 66) + if __name__ == '__main__': unittest.main() diff --git a/test/test_subtitles.py b/test/test_subtitles.py new file mode 100644 index 0000000..06a3048 --- /dev/null +++ b/test/test_subtitles.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python + +# Allow direct execution +import os +import sys +import unittest +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from test.helper import FakeYDL, global_setup, md5 +global_setup() + + +from youtube_dl.extractor import ( + YoutubeIE, + DailymotionIE, + TEDIE, +) + + +class BaseTestSubtitles(unittest.TestCase): + url = None + IE = None + def setUp(self): + self.DL = FakeYDL() + self.ie = self.IE(self.DL) + + def getInfoDict(self): + info_dict = self.ie.extract(self.url) + return info_dict + + def getSubtitles(self): + info_dict = self.getInfoDict() + return info_dict['subtitles'] + + +class TestYoutubeSubtitles(BaseTestSubtitles): + url = 'QRS8MkLhQmM' + IE = YoutubeIE + + def getSubtitles(self): + info_dict = self.getInfoDict() + return info_dict[0]['subtitles'] + + def test_youtube_no_writesubtitles(self): + self.DL.params['writesubtitles'] = False + subtitles = self.getSubtitles() + self.assertEqual(subtitles, None) + + def test_youtube_subtitles(self): + self.DL.params['writesubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['en']), '4cd9278a35ba2305f47354ee13472260') + + def test_youtube_subtitles_lang(self): + self.DL.params['writesubtitles'] = True + self.DL.params['subtitleslangs'] = ['it'] + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['it']), '164a51f16f260476a05b50fe4c2f161d') + + def test_youtube_allsubtitles(self): + self.DL.params['writesubtitles'] = True + self.DL.params['allsubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(len(subtitles.keys()), 13) + + def test_youtube_subtitles_sbv_format(self): + self.DL.params['writesubtitles'] = True + self.DL.params['subtitlesformat'] = 'sbv' + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['en']), '13aeaa0c245a8bed9a451cb643e3ad8b') + + def test_youtube_subtitles_vtt_format(self): + self.DL.params['writesubtitles'] = True + self.DL.params['subtitlesformat'] = 'vtt' + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['en']), '356cdc577fde0c6783b9b822e7206ff7') + + def test_youtube_list_subtitles(self): + self.DL.expect_warning(u'Video doesn\'t have automatic captions') + self.DL.params['listsubtitles'] = True + info_dict = self.getInfoDict() + self.assertEqual(info_dict, None) + + def test_youtube_automatic_captions(self): + self.url = '8YoUxe5ncPo' + self.DL.params['writeautomaticsub'] = True + self.DL.params['subtitleslangs'] = ['it'] + subtitles = self.getSubtitles() + self.assertTrue(subtitles['it'] is not None) + + def test_youtube_nosubtitles(self): + self.DL.expect_warning(u'video doesn\'t have subtitles') + self.url = 'sAjKT8FhjI8' + self.DL.params['writesubtitles'] = True + self.DL.params['allsubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(len(subtitles), 0) + + def test_youtube_multiple_langs(self): + self.url = 'QRS8MkLhQmM' + self.DL.params['writesubtitles'] = True + langs = ['it', 'fr', 'de'] + self.DL.params['subtitleslangs'] = langs + subtitles = self.getSubtitles() + for lang in langs: + self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang) + + +class TestDailymotionSubtitles(BaseTestSubtitles): + url = 'http://www.dailymotion.com/video/xczg00' + IE = DailymotionIE + + def test_no_writesubtitles(self): + subtitles = self.getSubtitles() + self.assertEqual(subtitles, None) + + def test_subtitles(self): + self.DL.params['writesubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['en']), '976553874490cba125086bbfea3ff76f') + + def test_subtitles_lang(self): + self.DL.params['writesubtitles'] = True + self.DL.params['subtitleslangs'] = ['fr'] + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['fr']), '594564ec7d588942e384e920e5341792') + + def test_allsubtitles(self): + self.DL.params['writesubtitles'] = True + self.DL.params['allsubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(len(subtitles.keys()), 5) + + def test_list_subtitles(self): + self.DL.expect_warning(u'Automatic Captions not supported by this server') + self.DL.params['listsubtitles'] = True + info_dict = self.getInfoDict() + self.assertEqual(info_dict, None) + + def test_automatic_captions(self): + self.DL.expect_warning(u'Automatic Captions not supported by this server') + self.DL.params['writeautomaticsub'] = True + self.DL.params['subtitleslang'] = ['en'] + subtitles = self.getSubtitles() + self.assertTrue(len(subtitles.keys()) == 0) + + def test_nosubtitles(self): + self.DL.expect_warning(u'video doesn\'t have subtitles') + self.url = 'http://www.dailymotion.com/video/x12u166_le-zapping-tele-star-du-08-aout-2013_tv' + self.DL.params['writesubtitles'] = True + self.DL.params['allsubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(len(subtitles), 0) + + def test_multiple_langs(self): + self.DL.params['writesubtitles'] = True + langs = ['es', 'fr', 'de'] + self.DL.params['subtitleslangs'] = langs + subtitles = self.getSubtitles() + for lang in langs: + self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang) + + +class TestTedSubtitles(BaseTestSubtitles): + url = 'http://www.ted.com/talks/dan_dennett_on_our_consciousness.html' + IE = TEDIE + + def test_no_writesubtitles(self): + subtitles = self.getSubtitles() + self.assertEqual(subtitles, None) + + def test_subtitles(self): + self.DL.params['writesubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['en']), '2154f31ff9b9f89a0aa671537559c21d') + + def test_subtitles_lang(self): + self.DL.params['writesubtitles'] = True + self.DL.params['subtitleslangs'] = ['fr'] + subtitles = self.getSubtitles() + self.assertEqual(md5(subtitles['fr']), '7616cbc6df20ec2c1204083c83871cf6') + + def test_allsubtitles(self): + self.DL.params['writesubtitles'] = True + self.DL.params['allsubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(len(subtitles.keys()), 28) + + def test_list_subtitles(self): + self.DL.expect_warning(u'Automatic Captions not supported by this server') + self.DL.params['listsubtitles'] = True + info_dict = self.getInfoDict() + self.assertEqual(info_dict, None) + + def test_automatic_captions(self): + self.DL.expect_warning(u'Automatic Captions not supported by this server') + self.DL.params['writeautomaticsub'] = True + self.DL.params['subtitleslang'] = ['en'] + subtitles = self.getSubtitles() + self.assertTrue(len(subtitles.keys()) == 0) + + def test_multiple_langs(self): + self.DL.params['writesubtitles'] = True + langs = ['es', 'fr', 'de'] + self.DL.params['subtitleslangs'] = langs + subtitles = self.getSubtitles() + for lang in langs: + self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang) + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_youtube_subtitles.py b/test/test_youtube_subtitles.py deleted file mode 100644 index 00430a3..0000000 --- a/test/test_youtube_subtitles.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python - -# Allow direct execution -import os -import sys -import unittest -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from test.helper import FakeYDL, global_setup, md5 -global_setup() - - -from youtube_dl.extractor import YoutubeIE - - -class TestYoutubeSubtitles(unittest.TestCase): - def setUp(self): - self.DL = FakeYDL() - self.url = 'QRS8MkLhQmM' - - def getInfoDict(self): - IE = YoutubeIE(self.DL) - info_dict = IE.extract(self.url) - return info_dict - - def getSubtitles(self): - info_dict = self.getInfoDict() - return info_dict[0]['subtitles'] - - def test_youtube_no_writesubtitles(self): - self.DL.params['writesubtitles'] = False - subtitles = self.getSubtitles() - self.assertEqual(subtitles, None) - - def test_youtube_subtitles(self): - self.DL.params['writesubtitles'] = True - subtitles = self.getSubtitles() - self.assertEqual(md5(subtitles['en']), '4cd9278a35ba2305f47354ee13472260') - - def test_youtube_subtitles_lang(self): - self.DL.params['writesubtitles'] = True - self.DL.params['subtitleslangs'] = ['it'] - subtitles = self.getSubtitles() - self.assertEqual(md5(subtitles['it']), '164a51f16f260476a05b50fe4c2f161d') - - def test_youtube_allsubtitles(self): - self.DL.params['writesubtitles'] = True - self.DL.params['allsubtitles'] = True - subtitles = self.getSubtitles() - self.assertEqual(len(subtitles.keys()), 13) - - def test_youtube_subtitles_sbv_format(self): - self.DL.params['writesubtitles'] = True - self.DL.params['subtitlesformat'] = 'sbv' - subtitles = self.getSubtitles() - self.assertEqual(md5(subtitles['en']), '13aeaa0c245a8bed9a451cb643e3ad8b') - - def test_youtube_subtitles_vtt_format(self): - self.DL.params['writesubtitles'] = True - self.DL.params['subtitlesformat'] = 'vtt' - subtitles = self.getSubtitles() - self.assertEqual(md5(subtitles['en']), '356cdc577fde0c6783b9b822e7206ff7') - - def test_youtube_list_subtitles(self): - self.DL.expect_warning(u'Video doesn\'t have automatic captions') - self.DL.params['listsubtitles'] = True - info_dict = self.getInfoDict() - self.assertEqual(info_dict, None) - - def test_youtube_automatic_captions(self): - self.url = '8YoUxe5ncPo' - self.DL.params['writeautomaticsub'] = True - self.DL.params['subtitleslangs'] = ['it'] - subtitles = self.getSubtitles() - self.assertTrue(subtitles['it'] is not None) - - def test_youtube_nosubtitles(self): - self.DL.expect_warning(u'video doesn\'t have subtitles') - self.url = 'sAjKT8FhjI8' - self.DL.params['writesubtitles'] = True - self.DL.params['allsubtitles'] = True - subtitles = self.getSubtitles() - self.assertEqual(len(subtitles), 0) - - def test_youtube_multiple_langs(self): - self.url = 'QRS8MkLhQmM' - self.DL.params['writesubtitles'] = True - langs = ['it', 'fr', 'de'] - self.DL.params['subtitleslangs'] = langs - subtitles = self.getSubtitles() - for lang in langs: - self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang) - -if __name__ == '__main__': - unittest.main() diff --git a/youtube-dl b/youtube-dl index dc47dc0dba5b5a5945259c2dffeac63ca46b9ef0..0924a5d8a7fa976e2878e3607a5410a96559acf0 100755 GIT binary patch delta 133071 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 z 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 W)UZGWV6a^9J4Y5AeP08{!u}6)Lzy@L delta 116714 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 z 1: @@ -571,9 +585,9 @@ class YoutubeDL(object): if self.params.get('forceurl', False): # For RTMP URLs, also include the playpath compat_print(info_dict['url'] + info_dict.get('play_path', u'')) - if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict: + if self.params.get('forcethumbnail', False) and info_dict.get('thumbnail') is not None: compat_print(info_dict['thumbnail']) - if self.params.get('forcedescription', False) and 'description' in info_dict: + if self.params.get('forcedescription', False) and info_dict.get('description') is not None: compat_print(info_dict['description']) if self.params.get('forcefilename', False) and filename is not None: compat_print(filename) @@ -754,30 +768,36 @@ class YoutubeDL(object): archive_file.write(vid_id + u'\n') @staticmethod - def format_resolution(format): + def format_resolution(format, default='unknown'): + if format.get('_resolution') is not None: + return format['_resolution'] if format.get('height') is not None: if format.get('width') is not None: res = u'%sx%s' % (format['width'], format['height']) else: res = u'%sp' % format['height'] else: - res = '???' + res = default return res def list_formats(self, info_dict): - formats_s = [] - for format in info_dict.get('formats', [info_dict]): - formats_s.append(u'%-15s: %-5s %-15s[%s]' % ( + def line(format): + return (u'%-20s%-10s%-12s%s' % ( format['format_id'], format['ext'], - format.get('format_note') or '-', self.format_resolution(format), + format.get('format_note', ''), ) ) - if len(formats_s) != 1: - formats_s[0] += ' (worst)' - formats_s[-1] += ' (best)' - formats_s = "\n".join(formats_s) - self.to_screen(u'[info] Available formats for %s:\n' - u'format code extension note resolution\n%s' % ( - info_dict['id'], formats_s)) + + formats = info_dict.get('formats', [info_dict]) + formats_s = list(map(line, formats)) + if len(formats) > 1: + formats_s[0] += (' ' if formats[0].get('format_note') else '') + '(worst)' + formats_s[-1] += (' ' if formats[-1].get('format_note') else '') + '(best)' + + header_line = line({ + 'format_id': u'format code', 'ext': u'extension', + '_resolution': u'resolution', 'format_note': u'note'}) + self.to_screen(u'[info] Available formats for %s:\n%s\n%s' % + (info_dict['id'], header_line, u"\n".join(formats_s))) diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index fce1adf..1f1db9f 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -133,7 +133,7 @@ def parseOpts(overrideArguments=None): def _hide_login_info(opts): opts = list(opts) - for private_opt in ['-p', '--password', '-u', '--username']: + for private_opt in ['-p', '--password', '-u', '--username', '--video-password']: try: i = opts.index(private_opt) opts[i+1] = '' @@ -316,6 +316,9 @@ def parseOpts(overrideArguments=None): verbosity.add_option('--dump-intermediate-pages', action='store_true', dest='dump_intermediate_pages', default=False, help='print downloaded pages to debug problems(very verbose)') + verbosity.add_option('--write-pages', + action='store_true', dest='write_pages', default=False, + help='Write downloaded pages to files in the current directory') verbosity.add_option('--youtube-print-sig-code', action='store_true', dest='youtube_print_sig_code', default=False, help=optparse.SUPPRESS_HELP) @@ -336,7 +339,8 @@ def parseOpts(overrideArguments=None): '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, ' '%(autonumber)s to get an automatically incremented number, ' '%(ext)s for the filename extension, ' - '%(format)s for the format description (like "22 - 1280x720" or "HD")' + '%(format)s for the format description (like "22 - 1280x720" or "HD"),' + '%(format_id)s for the unique id of the format (like Youtube\'s itags: "137"),' '%(upload_date)s for the upload date (YYYYMMDD), ' '%(extractor)s for the provider (youtube, metacafe, etc), ' '%(id)s for the video id , %(playlist)s for the playlist the video is in, ' @@ -345,7 +349,7 @@ def parseOpts(overrideArguments=None): 'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .')) filesystem.add_option('--autonumber-size', dest='autonumber_size', metavar='NUMBER', - help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --autonumber option is given') + help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given') filesystem.add_option('--restrict-filenames', action='store_true', dest='restrictfilenames', help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False) @@ -354,7 +358,7 @@ def parseOpts(overrideArguments=None): filesystem.add_option('-w', '--no-overwrites', action='store_true', dest='nooverwrites', help='do not overwrite files', default=False) filesystem.add_option('-c', '--continue', - action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True) + action='store_true', dest='continue_dl', help='force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.', default=True) filesystem.add_option('--no-continue', action='store_false', dest='continue_dl', help='do not resume partially downloaded files (restart from beginning)') @@ -651,6 +655,7 @@ def _real_main(argv=None): 'prefer_free_formats': opts.prefer_free_formats, 'verbose': opts.verbose, 'dump_intermediate_pages': opts.dump_intermediate_pages, + 'write_pages': opts.write_pages, 'test': opts.test, 'keepvideo': opts.keepvideo, 'min_filesize': opts.min_filesize, diff --git a/youtube_dl/extractor/__init__.py b/youtube_dl/extractor/__init__.py index db69af3..f9caca4 100644 --- a/youtube_dl/extractor/__init__.py +++ b/youtube_dl/extractor/__init__.py @@ -9,6 +9,7 @@ from .arte import ( ArteTVFutureIE, ) from .auengine import AUEngineIE +from .bambuser import BambuserIE, BambuserChannelIE from .bandcamp import BandcampIE from .bliptv import BlipTVIE, BlipTVUserIE from .bloomberg import BloombergIE @@ -37,8 +38,10 @@ from .defense import DefenseGouvFrIE from .ebaumsworld import EbaumsWorldIE from .ehow import EHowIE from .eighttracks import EightTracksIE +from .eitb import EitbIE from .escapist import EscapistIE from .exfm import ExfmIE +from .extremetube import ExtremeTubeIE from .facebook import FacebookIE from .faz import FazIE from .fktv import ( @@ -72,6 +75,7 @@ from .jeuxvideo import JeuxVideoIE from .jukebox import JukeboxIE from .justintv import JustinTVIE from .kankan import KankanIE +from .keezmovies import KeezMoviesIE from .kickstarter import KickStarterIE from .keek import KeekIE from .liveleak import LiveLeakIE @@ -80,8 +84,10 @@ from .metacafe import MetacafeIE from .metacritic import MetacriticIE from .mit import TechTVMITIE, MITIE from .mixcloud import MixcloudIE +from .mofosex import MofosexIE from .mtv import MTVIE from .muzu import MuzuTVIE +from .myspace import MySpaceIE from .myspass import MySpassIE from .myvideo import MyVideoIE from .naver import NaverIE @@ -94,6 +100,7 @@ from .ooyala import OoyalaIE from .orf import ORFIE from .pbs import PBSIE from .photobucket import PhotobucketIE +from .pornhub import PornHubIE from .pornotube import PornotubeIE from .rbmaradio import RBMARadioIE from .redtube import RedTubeIE @@ -109,6 +116,8 @@ from .slideshare import SlideshareIE from .sohu import SohuIE from .soundcloud import SoundcloudIE, SoundcloudSetIE, SoundcloudUserIE from .southparkstudios import SouthParkStudiosIE +from .space import SpaceIE +from .spankwire import SpankwireIE from .spiegel import SpiegelIE from .stanfordoc import StanfordOpenClassroomIE from .statigram import StatigramIE @@ -121,6 +130,7 @@ from .tf1 import TF1IE from .thisav import ThisAVIE from .traileraddict import TrailerAddictIE from .trilulilu import TriluliluIE +from .tube8 import Tube8IE from .tudou import TudouIE from .tumblr import TumblrIE from .tutv import TutvIE @@ -137,6 +147,7 @@ from .videofyme import VideofyMeIE from .videopremium import VideoPremiumIE from .vimeo import VimeoIE, VimeoChannelIE from .vine import VineIE +from .vk import VKIE from .wat import WatIE from .websurg import WeBSurgIE from .weibo import WeiboIE @@ -145,6 +156,7 @@ from .worldstarhiphop import WorldStarHipHopIE from .xhamster import XHamsterIE from .xnxx import XNXXIE from .xvideos import XVideosIE +from .xtube import XTubeIE from .yahoo import YahooIE, YahooSearchIE from .youjizz import YouJizzIE from .youku import YoukuIE @@ -153,6 +165,7 @@ from .youtube import ( YoutubeIE, YoutubePlaylistIE, YoutubeSearchIE, + YoutubeSearchDateIE, YoutubeUserIE, YoutubeChannelIE, YoutubeShowIE, diff --git a/youtube_dl/extractor/addanime.py b/youtube_dl/extractor/addanime.py index 82a785a..b99d4b9 100644 --- a/youtube_dl/extractor/addanime.py +++ b/youtube_dl/extractor/addanime.py @@ -17,8 +17,8 @@ class AddAnimeIE(InfoExtractor): IE_NAME = u'AddAnime' _TEST = { u'url': u'http://www.add-anime.net/watch_video.php?v=24MR3YO5SAS9', - u'file': u'24MR3YO5SAS9.flv', - u'md5': u'1036a0e0cd307b95bd8a8c3a5c8cfaf1', + u'file': u'24MR3YO5SAS9.mp4', + u'md5': u'72954ea10bc979ab5e2eb288b21425a0', u'info_dict': { u"description": u"One Piece 606", u"title": u"One Piece 606" @@ -31,7 +31,8 @@ class AddAnimeIE(InfoExtractor): video_id = mobj.group('video_id') webpage = self._download_webpage(url, video_id) except ExtractorError as ee: - if not isinstance(ee.cause, compat_HTTPError): + if not isinstance(ee.cause, compat_HTTPError) or \ + ee.cause.code != 503: raise redir_webpage = ee.cause.read().decode('utf-8') @@ -60,16 +61,26 @@ class AddAnimeIE(InfoExtractor): note=u'Confirming after redirect') webpage = self._download_webpage(url, video_id) - video_url = self._search_regex(r"var normal_video_file = '(.*?)';", - webpage, u'video file URL') + formats = [] + for format_id in ('normal', 'hq'): + rex = r"var %s_video_file = '(.*?)';" % re.escape(format_id) + video_url = self._search_regex(rex, webpage, u'video file URLx', + fatal=False) + if not video_url: + continue + formats.append({ + 'format_id': format_id, + 'url': video_url, + }) + if not formats: + raise ExtractorError(u'Cannot find any video format!') video_title = self._og_search_title(webpage) video_description = self._og_search_description(webpage) return { '_type': 'video', 'id': video_id, - 'url': video_url, - 'ext': 'flv', + 'formats': formats, 'title': video_title, 'description': video_description } diff --git a/youtube_dl/extractor/arte.py b/youtube_dl/extractor/arte.py index d39b489..b35a679 100644 --- a/youtube_dl/extractor/arte.py +++ b/youtube_dl/extractor/arte.py @@ -10,6 +10,7 @@ from ..utils import ( unified_strdate, determine_ext, get_element_by_id, + compat_str, ) # There are different sources of video in arte.tv, the extraction process @@ -158,7 +159,9 @@ class ArteTVPlus7IE(InfoExtractor): 'thumbnail': player_info.get('programImage') or player_info.get('VTU', {}).get('IUR'), } - formats = player_info['VSR'].values() + all_formats = player_info['VSR'].values() + # Some formats use the m3u8 protocol + all_formats = list(filter(lambda f: f.get('videoFormat') != 'M3U8', all_formats)) def _match_lang(f): if f.get('versionCode') is None: return True @@ -170,24 +173,39 @@ class ArteTVPlus7IE(InfoExtractor): regexes = [r'VO?%s' % l, r'VO?.-ST%s' % l] return any(re.match(r, f['versionCode']) for r in regexes) # Some formats may not be in the same language as the url - formats = filter(_match_lang, formats) - # Some formats use the m3u8 protocol - formats = filter(lambda f: f.get('videoFormat') != 'M3U8', formats) - # We order the formats by quality + formats = filter(_match_lang, all_formats) formats = list(formats) # in python3 filter returns an iterator + if not formats: + # Some videos are only available in the 'Originalversion' + # they aren't tagged as being in French or German + if all(f['versionCode'] == 'VO' for f in all_formats): + formats = all_formats + else: + raise ExtractorError(u'The formats list is empty') + if re.match(r'[A-Z]Q', formats[0]['quality']) is not None: - sort_key = lambda f: ['HQ', 'MQ', 'EQ', 'SQ'].index(f['quality']) + def sort_key(f): + return ['HQ', 'MQ', 'EQ', 'SQ'].index(f['quality']) else: - sort_key = lambda f: int(f.get('height',-1)) + def sort_key(f): + return ( + # Sort first by quality + int(f.get('height',-1)), + int(f.get('bitrate',-1)), + # The original version with subtitles has lower relevance + re.match(r'VO-ST(F|A)', f.get('versionCode', '')) is None, + # The version with sourds/mal subtitles has also lower relevance + re.match(r'VO?(F|A)-STM\1', f.get('versionCode', '')) is None, + ) formats = sorted(formats, key=sort_key) - # Prefer videos without subtitles in the same language - formats = sorted(formats, key=lambda f: re.match(r'VO(F|A)-STM\1', f.get('versionCode', '')) is None) - # Pick the best quality def _format(format_info): - quality = format_info['quality'] - m_quality = re.match(r'\w*? - (\d*)p', quality) - if m_quality is not None: - quality = m_quality.group(1) + quality = '' + height = format_info.get('height') + if height is not None: + quality = compat_str(height) + bitrate = format_info.get('bitrate') + if bitrate is not None: + quality += '-%d' % bitrate if format_info.get('versionCode') is not None: format_id = u'%s-%s' % (quality, format_info['versionCode']) else: @@ -196,7 +214,7 @@ class ArteTVPlus7IE(InfoExtractor): 'format_id': format_id, 'format_note': format_info.get('versionLibelle'), 'width': format_info.get('width'), - 'height': format_info.get('height'), + 'height': height, } if format_info['mediaType'] == u'rtmp': info['url'] = format_info['streamer'] diff --git a/youtube_dl/extractor/bambuser.py b/youtube_dl/extractor/bambuser.py new file mode 100644 index 0000000..f3b36f4 --- /dev/null +++ b/youtube_dl/extractor/bambuser.py @@ -0,0 +1,80 @@ +import re +import json +import itertools + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_request, +) + + +class BambuserIE(InfoExtractor): + IE_NAME = u'bambuser' + _VALID_URL = r'https?://bambuser\.com/v/(?P\d+)' + _API_KEY = '005f64509e19a868399060af746a00aa' + + _TEST = { + u'url': u'http://bambuser.com/v/4050584', + u'md5': u'fba8f7693e48fd4e8641b3fd5539a641', + u'info_dict': { + u'id': u'4050584', + u'ext': u'flv', + u'title': u'Education engineering days - lightning talks', + u'duration': 3741, + u'uploader': u'pixelversity', + u'uploader_id': u'344706', + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + info_url = ('http://player-c.api.bambuser.com/getVideo.json?' + '&api_key=%s&vid=%s' % (self._API_KEY, video_id)) + info_json = self._download_webpage(info_url, video_id) + info = json.loads(info_json)['result'] + + return { + 'id': video_id, + 'title': info['title'], + 'url': info['url'], + 'thumbnail': info.get('preview'), + 'duration': int(info['length']), + 'view_count': int(info['views_total']), + 'uploader': info['username'], + 'uploader_id': info['uid'], + } + + +class BambuserChannelIE(InfoExtractor): + IE_NAME = u'bambuser:channel' + _VALID_URL = r'http://bambuser.com/channel/(?P.*?)(?:/|#|\?|$)' + # The maximum number we can get with each request + _STEP = 50 + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + user = mobj.group('user') + urls = [] + last_id = '' + for i in itertools.count(1): + req_url = ('http://bambuser.com/xhr-api/index.php?username={user}' + '&sort=created&access_mode=0%2C1%2C2&limit={count}' + '&method=broadcast&format=json&vid_older_than={last}' + ).format(user=user, count=self._STEP, last=last_id) + req = compat_urllib_request.Request(req_url) + # Without setting this header, we wouldn't get any result + req.add_header('Referer', 'http://bambuser.com/channel/%s' % user) + info_json = self._download_webpage(req, user, + u'Downloading page %d' % i) + results = json.loads(info_json)['result'] + if len(results) == 0: + break + last_id = results[-1]['vid'] + urls.extend(self.url_result(v['page'], 'Bambuser') for v in results) + + return { + '_type': 'playlist', + 'title': user, + 'entries': urls, + } diff --git a/youtube_dl/extractor/brightcove.py b/youtube_dl/extractor/brightcove.py index 1392f38..d8c3546 100644 --- a/youtube_dl/extractor/brightcove.py +++ b/youtube_dl/extractor/brightcove.py @@ -9,10 +9,13 @@ from ..utils import ( compat_urllib_parse, find_xpath_attr, compat_urlparse, + compat_str, + compat_urllib_request, ExtractorError, ) + class BrightcoveIE(InfoExtractor): _VALID_URL = r'https?://.*brightcove\.com/(services|viewer).*\?(?P.*)' _FEDERATED_URL_TEMPLATE = 'http://c.brightcove.com/services/viewer/htmlFederated?%s' @@ -23,7 +26,7 @@ class BrightcoveIE(InfoExtractor): # From http://www.8tv.cat/8aldia/videos/xavier-sala-i-martin-aquesta-tarda-a-8-al-dia/ u'url': u'http://c.brightcove.com/services/viewer/htmlFederated?playerID=1654948606001&flashID=myExperience&%40videoPlayer=2371591881001', u'file': u'2371591881001.mp4', - u'md5': u'9e80619e0a94663f0bdc849b4566af19', + u'md5': u'8eccab865181d29ec2958f32a6a754f5', u'note': u'Test Brightcove downloads and detection in GenericIE', u'info_dict': { u'title': u'Xavier Sala i Martín: “Un banc que no presta és un banc zombi que no serveix per a res”', @@ -41,6 +44,17 @@ class BrightcoveIE(InfoExtractor): u'uploader': u'Oracle', }, }, + { + # From http://mashable.com/2013/10/26/thermoelectric-bracelet-lets-you-control-your-body-temperature/ + u'url': u'http://c.brightcove.com/services/viewer/federated_f9?&playerID=1265504713001&publisherID=AQ%7E%7E%2CAAABBzUwv1E%7E%2CxP-xFHVUstiMFlNYfvF4G9yFnNaqCw_9&videoID=2750934548001', + u'info_dict': { + u'id': u'2750934548001', + u'ext': u'mp4', + u'title': u'This Bracelet Acts as a Personal Thermostat', + u'description': u'md5:547b78c64f4112766ccf4e151c20b6a0', + u'uploader': u'Mashable', + }, + }, ] @classmethod @@ -68,24 +82,48 @@ class BrightcoveIE(InfoExtractor): videoPlayer = find_xpath_attr(object_doc, './param', 'name', '@videoPlayer') if videoPlayer is not None: params['@videoPlayer'] = videoPlayer.attrib['value'] + linkBase = find_xpath_attr(object_doc, './param', 'name', 'linkBaseURL') + if linkBase is not None: + params['linkBaseURL'] = linkBase.attrib['value'] data = compat_urllib_parse.urlencode(params) return cls._FEDERATED_URL_TEMPLATE % data + @classmethod + def _extract_brightcove_url(cls, webpage): + """Try to extract the brightcove url from the wepbage, returns None + if it can't be found + """ + m_brightcove = re.search( + r']+?class=([\'"])[^>]*?BrightcoveExperience.*?\1.+?', + webpage, re.DOTALL) + if m_brightcove is not None: + return cls._build_brighcove_url(m_brightcove.group()) + else: + return None + def _real_extract(self, url): + # Change the 'videoId' and others field to '@videoPlayer' + url = re.sub(r'(?<=[?&])(videoI(d|D)|bctid)', '%40videoPlayer', url) + # Change bckey (used by bcove.me urls) to playerKey + url = re.sub(r'(?<=[?&])bckey', 'playerKey', url) mobj = re.match(self._VALID_URL, url) query_str = mobj.group('query') query = compat_urlparse.parse_qs(query_str) videoPlayer = query.get('@videoPlayer') if videoPlayer: - return self._get_video_info(videoPlayer[0], query_str) + return self._get_video_info(videoPlayer[0], query_str, query) else: player_key = query['playerKey'] return self._get_playlist_info(player_key[0]) - def _get_video_info(self, video_id, query): - request_url = self._FEDERATED_URL_TEMPLATE % query - webpage = self._download_webpage(request_url, video_id) + def _get_video_info(self, video_id, query_str, query): + request_url = self._FEDERATED_URL_TEMPLATE % query_str + req = compat_urllib_request.Request(request_url) + linkBase = query.get('linkBaseURL') + if linkBase is not None: + req.add_header('Referer', linkBase[0]) + webpage = self._download_webpage(req, video_id) self.report_extraction(video_id) info = self._search_regex(r'var experienceJSON = ({.*?});', webpage, 'json') @@ -109,7 +147,7 @@ class BrightcoveIE(InfoExtractor): def _extract_video_info(self, video_info): info = { - 'id': video_info['id'], + 'id': compat_str(video_info['id']), 'title': video_info['displayName'], 'description': video_info.get('shortDescription'), 'thumbnail': video_info.get('videoStillURL') or video_info.get('thumbnailURL'), @@ -119,15 +157,14 @@ class BrightcoveIE(InfoExtractor): renditions = video_info.get('renditions') if renditions: renditions = sorted(renditions, key=lambda r: r['size']) - best_format = renditions[-1] - info.update({ - 'url': best_format['defaultURL'], - 'ext': 'mp4', - }) + info['formats'] = [{ + 'url': rend['defaultURL'], + 'height': rend.get('frameHeight'), + 'width': rend.get('frameWidth'), + } for rend in renditions] elif video_info.get('FLVFullLengthURL') is not None: info.update({ 'url': video_info['FLVFullLengthURL'], - 'ext': 'flv', }) else: raise ExtractorError(u'Unable to extract video url for %s' % info['id']) diff --git a/youtube_dl/extractor/canalc2.py b/youtube_dl/extractor/canalc2.py index e7f4fa9..3d8d7f9 100644 --- a/youtube_dl/extractor/canalc2.py +++ b/youtube_dl/extractor/canalc2.py @@ -6,7 +6,7 @@ from .common import InfoExtractor class Canalc2IE(InfoExtractor): IE_NAME = 'canalc2.tv' - _VALID_URL = r'http://.*?\.canalc2\.tv/video\.asp\?idVideo=(\d+)&voir=oui' + _VALID_URL = r'http://.*?\.canalc2\.tv/video\.asp\?.*?idVideo=(?P\d+)' _TEST = { u'url': u'http://www.canalc2.tv/video.asp?idVideo=12163&voir=oui', @@ -18,7 +18,9 @@ class Canalc2IE(InfoExtractor): } def _real_extract(self, url): - video_id = re.match(self._VALID_URL, url).group(1) + video_id = re.match(self._VALID_URL, url).group('id') + # We need to set the voir field for getting the file name + url = 'http://www.canalc2.tv/video.asp?idVideo=%s&voir=oui' % video_id webpage = self._download_webpage(url, video_id) file_name = self._search_regex( r"so\.addVariable\('file','(.*?)'\);", diff --git a/youtube_dl/extractor/cinemassacre.py b/youtube_dl/extractor/cinemassacre.py index 6925b96..f0d08ce 100644 --- a/youtube_dl/extractor/cinemassacre.py +++ b/youtube_dl/extractor/cinemassacre.py @@ -41,7 +41,7 @@ class CinemassacreIE(InfoExtractor): webpage_url = u'http://' + mobj.group('url') webpage = self._download_webpage(webpage_url, None) # Don't know video id yet video_date = mobj.group('date_Y') + mobj.group('date_m') + mobj.group('date_d') - mobj = re.search(r'src="(?Phttp://player\.screenwavemedia\.com/play/(?:embed|player)\.php\?id=(?:Cinemassacre-)?(?P.+?))"', webpage) + mobj = re.search(r'src="(?Phttp://player\.screenwavemedia\.com/play/[a-zA-Z]+\.php\?id=(?:Cinemassacre-)?(?P.+?))"', webpage) if not mobj: raise ExtractorError(u'Can\'t extract embed url and video id') playerdata_url = mobj.group(u'embed_url') @@ -55,30 +55,32 @@ class CinemassacreIE(InfoExtractor): video_description = None playerdata = self._download_webpage(playerdata_url, video_id) - base_url = self._html_search_regex(r'\'streamer\': \'(?Prtmp://.*?)/(?:vod|Cinemassacre)\'', - playerdata, u'base_url') - base_url += '/Cinemassacre/' - # Important: The file names in playerdata are not used by the player and even wrong for some videos - sd_file = 'Cinemassacre-%s_high.mp4' % video_id - hd_file = 'Cinemassacre-%s.mp4' % video_id - video_thumbnail = 'http://image.screenwavemedia.com/Cinemassacre/Cinemassacre-%s_thumb_640x360.jpg' % video_id + url = self._html_search_regex(r'\'streamer\': \'(?P[^\']+)\'', playerdata, u'url') + + sd_file = self._html_search_regex(r'\'file\': \'(?P[^\']+)\'', playerdata, u'sd_file') + hd_file = self._html_search_regex(r'\'?file\'?: "(?P[^"]+)"', playerdata, u'hd_file') + video_thumbnail = self._html_search_regex(r'\'image\': \'(?P[^\']+)\'', playerdata, u'thumbnail', fatal=False) formats = [ { - 'url': base_url + sd_file, + 'url': url, + 'play_path': 'mp4:' + sd_file, + 'rtmp_live': True, # workaround 'ext': 'flv', 'format': 'sd', 'format_id': 'sd', }, { - 'url': base_url + hd_file, + 'url': url, + 'play_path': 'mp4:' + hd_file, + 'rtmp_live': True, # workaround 'ext': 'flv', 'format': 'hd', 'format_id': 'hd', }, ] - info = { + return { 'id': video_id, 'title': video_title, 'formats': formats, @@ -86,6 +88,3 @@ class CinemassacreIE(InfoExtractor): 'upload_date': video_date, 'thumbnail': video_thumbnail, } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info diff --git a/youtube_dl/extractor/cnn.py b/youtube_dl/extractor/cnn.py index a79f881..34adf6d 100644 --- a/youtube_dl/extractor/cnn.py +++ b/youtube_dl/extractor/cnn.py @@ -6,7 +6,7 @@ from ..utils import determine_ext class CNNIE(InfoExtractor): - _VALID_URL = r'''(?x)https?://(edition\.)?cnn\.com/video/(data/.+?|\?)/ + _VALID_URL = r'''(?x)https?://((edition|www)\.)?cnn\.com/video/(data/.+?|\?)/ (?P.+?/(?P[^/]+?)(?:\.cnn|(?=&)))''' _TESTS = [{ diff --git a/youtube_dl/extractor/common.py b/youtube_dl/extractor/common.py index 7d7ce5d..fb2d50a 100644 --- a/youtube_dl/extractor/common.py +++ b/youtube_dl/extractor/common.py @@ -14,6 +14,8 @@ from ..utils import ( clean_html, compiled_regex_type, ExtractorError, + RegexNotFoundError, + sanitize_filename, unescapeHTML, ) @@ -61,7 +63,7 @@ class InfoExtractor(object): * ext Will be calculated from url if missing * format A human-readable description of the format ("mp4 container with h264/opus"). - Calculated from the format_id, width, height + Calculated from the format_id, width, height. and format_note fields if missing. * format_id A short description of the format ("mp4_h264_opus" or "19") @@ -69,6 +71,9 @@ class InfoExtractor(object): ("3D" or "DASH video") * width Width of the video, if known * height Height of the video, if known + webpage_url: The url to the video webpage, if given to youtube-dl it + should allow to get the same result again. (It will be set + by YoutubeDL if it's missing) Unless mentioned otherwise, the fields should be Unicode strings. @@ -181,6 +186,17 @@ class InfoExtractor(object): self.to_screen(u'Dumping request to ' + url) dump = base64.b64encode(webpage_bytes).decode('ascii') self._downloader.to_screen(dump) + if self._downloader.params.get('write_pages', False): + try: + url = url_or_request.get_full_url() + except AttributeError: + url = url_or_request + raw_filename = ('%s_%s.dump' % (video_id, url)) + filename = sanitize_filename(raw_filename, restricted=True) + self.to_screen(u'Saving request to ' + filename) + with open(filename, 'wb') as outf: + outf.write(webpage_bytes) + content = webpage_bytes.decode(encoding, 'replace') return (content, urlh) @@ -231,7 +247,7 @@ class InfoExtractor(object): Perform a regex search on the given string, using a single or a list of patterns returning the first matching group. In case of failure return a default value or raise a WARNING or a - ExtractorError, depending on fatal, specifying the field name. + RegexNotFoundError, depending on fatal, specifying the field name. """ if isinstance(pattern, (str, compat_str, compiled_regex_type)): mobj = re.search(pattern, string, flags) @@ -251,7 +267,7 @@ class InfoExtractor(object): elif default is not None: return default elif fatal: - raise ExtractorError(u'Unable to extract %s' % _name) + raise RegexNotFoundError(u'Unable to extract %s' % _name) else: self._downloader.report_warning(u'unable to extract %s; ' u'please report this issue on http://yt-dl.org/bug' % _name) @@ -306,7 +322,9 @@ class InfoExtractor(object): if name is None: name = 'OpenGraph %s' % prop escaped = self._search_regex(self._og_regex(prop), html, name, flags=re.DOTALL, **kargs) - return unescapeHTML(escaped) + if not escaped is None: + return unescapeHTML(escaped) + return None def _og_search_thumbnail(self, html, **kargs): return self._og_search_property('image', html, u'thumbnail url', fatal=False, **kargs) @@ -317,10 +335,10 @@ class InfoExtractor(object): def _og_search_title(self, html, **kargs): return self._og_search_property('title', html, **kargs) - def _og_search_video_url(self, html, name='video url', **kargs): - return self._html_search_regex([self._og_regex('video:secure_url'), - self._og_regex('video')], - html, name, **kargs) + def _og_search_video_url(self, html, name='video url', secure=True, **kargs): + regexes = [self._og_regex('video')] + if secure: regexes.insert(0, self._og_regex('video:secure_url')) + return self._html_search_regex(regexes, html, name, **kargs) def _rta_search(self, html): # See http://www.rtalabel.org/index.php?content=howtofaq#single diff --git a/youtube_dl/extractor/dailymotion.py b/youtube_dl/extractor/dailymotion.py index 7d83539..e87690f 100644 --- a/youtube_dl/extractor/dailymotion.py +++ b/youtube_dl/extractor/dailymotion.py @@ -21,6 +21,7 @@ class DailymotionBaseInfoExtractor(InfoExtractor): """Build a request with the family filter disabled""" request = compat_urllib_request.Request(url) request.add_header('Cookie', 'family_filter=off') + request.add_header('Cookie', 'ff=off') return request class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor): @@ -28,6 +29,15 @@ class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor): _VALID_URL = r'(?i)(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/(?:embed/)?video/([^/]+)' IE_NAME = u'dailymotion' + + _FORMATS = [ + (u'stream_h264_ld_url', u'ld'), + (u'stream_h264_url', u'standard'), + (u'stream_h264_hq_url', u'hq'), + (u'stream_h264_hd_url', u'hd'), + (u'stream_h264_hd1080_url', u'hd180'), + ] + _TESTS = [ { u'url': u'http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech', @@ -52,6 +62,18 @@ class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor): }, u'skip': u'VEVO is only available in some countries', }, + # age-restricted video + { + u'url': u'http://www.dailymotion.com/video/xyh2zz_leanna-decker-cyber-girl-of-the-year-desires-nude-playboy-plus_redband', + u'file': u'xyh2zz.mp4', + u'md5': u'0d667a7b9cebecc3c89ee93099c4159d', + u'info_dict': { + u'title': 'Leanna Decker - Cyber Girl Of The Year Desires Nude [Playboy Plus]', + u'uploader': 'HotWaves1012', + u'age_limit': 18, + } + + } ] def _real_extract(self, url): @@ -60,7 +82,6 @@ class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor): video_id = mobj.group(1).split('_')[0].split('?')[0] - video_extension = 'mp4' url = 'http://www.dailymotion.com/video/%s' % video_id # Retrieve video webpage to extract further information @@ -82,7 +103,8 @@ class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor): video_uploader = self._search_regex([r'(?im)<span class="owner[^\"]+?">[^<]+?<a [^>]+?>([^<]+?)</a>', # Looking for official user r'<(?:span|a) .*?rel="author".*?>([^<]+?)</'], - webpage, 'video uploader') + webpage, 'video uploader', fatal=False) + age_limit = self._rta_search(webpage) video_upload_date = None mobj = re.search(r'<div class="[^"]*uploaded_cont[^"]*" title="[^"]*">([0-9]{2})-([0-9]{2})-([0-9]{4})</div>', webpage) @@ -99,37 +121,43 @@ class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor): msg = 'Couldn\'t get video, Dailymotion says: %s' % info['error']['title'] raise ExtractorError(msg, expected=True) - # TODO: support choosing qualities - - for key in ['stream_h264_hd1080_url','stream_h264_hd_url', - 'stream_h264_hq_url','stream_h264_url', - 'stream_h264_ld_url']: - if info.get(key):#key in info and info[key]: - max_quality = key - self.to_screen(u'Using %s' % key) - break - else: + formats = [] + for (key, format_id) in self._FORMATS: + video_url = info.get(key) + if video_url is not None: + m_size = re.search(r'H264-(\d+)x(\d+)', video_url) + if m_size is not None: + width, height = m_size.group(1), m_size.group(2) + else: + width, height = None, None + formats.append({ + 'url': video_url, + 'ext': 'mp4', + 'format_id': format_id, + 'width': width, + 'height': height, + }) + if not formats: raise ExtractorError(u'Unable to extract video URL') - video_url = info[max_quality] # subtitles - video_subtitles = self.extract_subtitles(video_id) + video_subtitles = self.extract_subtitles(video_id, webpage) if self._downloader.params.get('listsubtitles', False): - self._list_available_subtitles(video_id) + self._list_available_subtitles(video_id, webpage) return - return [{ + return { 'id': video_id, - 'url': video_url, + 'formats': formats, 'uploader': video_uploader, 'upload_date': video_upload_date, 'title': self._og_search_title(webpage), - 'ext': video_extension, 'subtitles': video_subtitles, - 'thumbnail': info['thumbnail_url'] - }] + 'thumbnail': info['thumbnail_url'], + 'age_limit': age_limit, + } - def _get_available_subtitles(self, video_id): + def _get_available_subtitles(self, video_id, webpage): try: sub_list = self._download_webpage( 'https://api.dailymotion.com/video/%s/subtitles?fields=id,language,url' % video_id, diff --git a/youtube_dl/extractor/depositfiles.py b/youtube_dl/extractor/depositfiles.py index d433489..2c9fb5f 100644 --- a/youtube_dl/extractor/depositfiles.py +++ b/youtube_dl/extractor/depositfiles.py @@ -25,7 +25,7 @@ class DepositFilesIE(InfoExtractor): url = 'http://depositfiles.com/en/files/' + file_id # Retrieve file webpage with 'Free download' button pressed - free_download_indication = { 'gateway_result' : '1' } + free_download_indication = {'gateway_result' : '1'} request = compat_urllib_request.Request(url, compat_urllib_parse.urlencode(free_download_indication)) try: self.report_download_webpage(file_id) diff --git a/youtube_dl/extractor/eighttracks.py b/youtube_dl/extractor/eighttracks.py index cced068..2cfbcd3 100644 --- a/youtube_dl/extractor/eighttracks.py +++ b/youtube_dl/extractor/eighttracks.py @@ -101,7 +101,7 @@ class EightTracksIE(InfoExtractor): first_url = 'http://8tracks.com/sets/%s/play?player=sm&mix_id=%s&format=jsonh' % (session, mix_id) next_url = first_url res = [] - for i in itertools.count(): + for i in range(track_count): api_json = self._download_webpage(next_url, playlist_id, note=u'Downloading song information %s/%s' % (str(i+1), track_count), errnote=u'Failed to download song information') @@ -116,7 +116,5 @@ class EightTracksIE(InfoExtractor): 'ext': 'm4a', } res.append(info) - if api_data['set']['at_last_track']: - break next_url = 'http://8tracks.com/sets/%s/next?player=sm&mix_id=%s&format=jsonh&track_id=%s' % (session, mix_id, track_data['id']) return res diff --git a/youtube_dl/extractor/eitb.py b/youtube_dl/extractor/eitb.py new file mode 100644 index 0000000..4ba3231 --- /dev/null +++ b/youtube_dl/extractor/eitb.py @@ -0,0 +1,37 @@ +# encoding: utf-8 +import re + +from .common import InfoExtractor +from .brightcove import BrightcoveIE +from ..utils import ExtractorError + + +class EitbIE(InfoExtractor): + IE_NAME = u'eitb.tv' + _VALID_URL = r'https?://www\.eitb\.tv/(eu/bideoa|es/video)/[^/]+/(?P<playlist_id>\d+)/(?P<chapter_id>\d+)' + + _TEST = { + u'add_ie': ['Brightcove'], + u'url': u'http://www.eitb.tv/es/video/60-minutos-60-minutos-2013-2014/2677100210001/2743577154001/lasa-y-zabala-30-anos/', + u'md5': u'edf4436247185adee3ea18ce64c47998', + u'info_dict': { + u'id': u'2743577154001', + u'ext': u'mp4', + u'title': u'60 minutos (Lasa y Zabala, 30 años)', + # All videos from eitb has this description in the brightcove info + u'description': u'.', + u'uploader': u'Euskal Telebista', + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + chapter_id = mobj.group('chapter_id') + webpage = self._download_webpage(url, chapter_id) + bc_url = BrightcoveIE._extract_brightcove_url(webpage) + if bc_url is None: + raise ExtractorError(u'Could not extract the Brightcove url') + # The BrightcoveExperience object doesn't contain the video id, we set + # it manually + bc_url += '&%40videoPlayer={0}'.format(chapter_id) + return self.url_result(bc_url, BrightcoveIE.ie_key()) diff --git a/youtube_dl/extractor/exfm.py b/youtube_dl/extractor/exfm.py index 3443f19..a51d79b 100644 --- a/youtube_dl/extractor/exfm.py +++ b/youtube_dl/extractor/exfm.py @@ -11,16 +11,17 @@ class ExfmIE(InfoExtractor): _SOUNDCLOUD_URL = r'(?:http://)?(?:www\.)?api\.soundcloud.com/tracks/([^/]+)/stream' _TESTS = [ { - u'url': u'http://ex.fm/song/1bgtzg', - u'file': u'95223130.mp3', - u'md5': u'8a7967a3fef10e59a1d6f86240fd41cf', + u'url': u'http://ex.fm/song/eh359', + u'file': u'44216187.mp3', + u'md5': u'e45513df5631e6d760970b14cc0c11e7', u'info_dict': { - u"title": u"We Can't Stop - Miley Cyrus", - u"uploader": u"Miley Cyrus", - u'upload_date': u'20130603', - u'description': u'Download "We Can\'t Stop" \r\niTunes: http://smarturl.it/WeCantStop?IQid=SC\r\nAmazon: http://smarturl.it/WeCantStopAMZ?IQid=SC', + u"title": u"Test House \"Love Is Not Enough\" (Extended Mix) DeadJournalist Exclusive", + u"uploader": u"deadjournalist", + u'upload_date': u'20120424', + u'description': u'Test House \"Love Is Not Enough\" (Extended Mix) DeadJournalist Exclusive', }, u'note': u'Soundcloud song', + u'skip': u'The site is down too often', }, { u'url': u'http://ex.fm/song/wddt8', @@ -30,6 +31,7 @@ class ExfmIE(InfoExtractor): u'title': u'Safe and Sound', u'uploader': u'Capital Cities', }, + u'skip': u'The site is down too often', }, ] diff --git a/youtube_dl/extractor/extremetube.py b/youtube_dl/extractor/extremetube.py new file mode 100644 index 0000000..1c20e43 --- /dev/null +++ b/youtube_dl/extractor/extremetube.py @@ -0,0 +1,50 @@ +import os +import re + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse_urlparse, + compat_urllib_request, + compat_urllib_parse, +) + +class ExtremeTubeIE(InfoExtractor): + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>extremetube\.com/video/.+?(?P<videoid>[0-9]+))(?:[/?&]|$)' + _TEST = { + u'url': u'http://www.extremetube.com/video/music-video-14-british-euro-brit-european-cumshots-swallow-652431', + u'file': u'652431.mp4', + u'md5': u'1fb9228f5e3332ec8c057d6ac36f33e0', + u'info_dict': { + u"title": u"Music Video 14 british euro brit european cumshots swallow", + u"uploader": u"unknown", + u"age_limit": 18, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') + + req = compat_urllib_request.Request(url) + req.add_header('Cookie', 'age_verified=1') + webpage = self._download_webpage(req, video_id) + + video_title = self._html_search_regex(r'<h1 [^>]*?title="([^"]+)"[^>]*>\1<', webpage, u'title') + uploader = self._html_search_regex(r'>Posted by:(?=<)(?:\s|<[^>]*>)*(.+?)\|', webpage, u'uploader', fatal=False) + video_url = compat_urllib_parse.unquote(self._html_search_regex(r'video_url=(.+?)&', webpage, u'video_url')) + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] + format = path.split('/')[5].split('_')[:2] + format = "-".join(format) + + return { + 'id': video_id, + 'title': video_title, + 'uploader': uploader, + 'url': video_url, + 'ext': extension, + 'format': format, + 'format_id': format, + 'age_limit': 18, + } diff --git a/youtube_dl/extractor/facebook.py b/youtube_dl/extractor/facebook.py index 9d1bc07..f8bdfc2 100644 --- a/youtube_dl/extractor/facebook.py +++ b/youtube_dl/extractor/facebook.py @@ -19,7 +19,8 @@ class FacebookIE(InfoExtractor): """Information Extractor for Facebook""" _VALID_URL = r'^(?:https?://)?(?:\w+\.)?facebook\.com/(?:video/video|photo)\.php\?(?:.*?)v=(?P<ID>\d+)(?:.*)' - _LOGIN_URL = 'https://login.facebook.com/login.php?m&next=http%3A%2F%2Fm.facebook.com%2Fhome.php&' + _LOGIN_URL = 'https://www.facebook.com/login.php?next=http%3A%2F%2Ffacebook.com%2Fhome.php&login_attempt=1' + _CHECKPOINT_URL = 'https://www.facebook.com/checkpoint/?next=http%3A%2F%2Ffacebook.com%2Fhome.php&_fb_noscript=1' _NETRC_MACHINE = 'facebook' IE_NAME = u'facebook' _TEST = { @@ -36,50 +37,56 @@ class FacebookIE(InfoExtractor): """Report attempt to log in.""" self.to_screen(u'Logging in') - def _real_initialize(self): - if self._downloader is None: - return - - useremail = None - password = None - downloader_params = self._downloader.params - - # Attempt to use provided username and password or .netrc data - if downloader_params.get('username', None) is not None: - useremail = downloader_params['username'] - password = downloader_params['password'] - elif downloader_params.get('usenetrc', False): - try: - info = netrc.netrc().authenticators(self._NETRC_MACHINE) - if info is not None: - useremail = info[0] - password = info[2] - else: - raise netrc.NetrcParseError('No authenticators for %s' % self._NETRC_MACHINE) - except (IOError, netrc.NetrcParseError) as err: - self._downloader.report_warning(u'parsing .netrc: %s' % compat_str(err)) - return - + def _login(self): + (useremail, password) = self._get_login_info() if useremail is None: return - # Log in + login_page_req = compat_urllib_request.Request(self._LOGIN_URL) + login_page_req.add_header('Cookie', 'locale=en_US') + self.report_login() + login_page = self._download_webpage(login_page_req, None, note=False, + errnote=u'Unable to download login page') + lsd = self._search_regex(r'"lsd":"(\w*?)"', login_page, u'lsd') + lgnrnd = self._search_regex(r'name="lgnrnd" value="([^"]*?)"', login_page, u'lgnrnd') + login_form = { 'email': useremail, 'pass': password, - 'login': 'Log+In' + 'lsd': lsd, + 'lgnrnd': lgnrnd, + 'next': 'http://facebook.com/home.php', + 'default_persistent': '0', + 'legacy_return': '1', + 'timezone': '-60', + 'trynum': '1', } request = compat_urllib_request.Request(self._LOGIN_URL, compat_urllib_parse.urlencode(login_form)) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') try: - self.report_login() login_results = compat_urllib_request.urlopen(request).read() if re.search(r'<form(.*)name="login"(.*)</form>', login_results) is not None: self._downloader.report_warning(u'unable to log in: bad username/password, or exceded login rate limit (~3/min). Check credentials or wait.') return + + check_form = { + 'fb_dtsg': self._search_regex(r'"fb_dtsg":"(.*?)"', login_results, u'fb_dtsg'), + 'nh': self._search_regex(r'name="nh" value="(\w*?)"', login_results, u'nh'), + 'name_action_selected': 'dont_save', + 'submit[Continue]': self._search_regex(r'<input value="(.*?)" name="submit\[Continue\]"', login_results, u'continue'), + } + check_req = compat_urllib_request.Request(self._CHECKPOINT_URL, compat_urllib_parse.urlencode(check_form)) + check_req.add_header('Content-Type', 'application/x-www-form-urlencoded') + check_response = compat_urllib_request.urlopen(check_req).read() + if re.search(r'id="checkpointSubmitButton"', check_response) is not None: + self._downloader.report_warning(u'Unable to confirm login, you have to login in your brower and authorize the login.') except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: self._downloader.report_warning(u'unable to log in: %s' % compat_str(err)) return + def _real_initialize(self): + self._login() + def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) if mobj is None: @@ -93,7 +100,13 @@ class FacebookIE(InfoExtractor): AFTER = '.forEach(function(variable) {swf.addVariable(variable[0], variable[1]);});' m = re.search(re.escape(BEFORE) + '(.*?)' + re.escape(AFTER), webpage) if not m: - raise ExtractorError(u'Cannot parse data') + m_msg = re.search(r'class="[^"]*uiInterstitialContent[^"]*"><div>(.*?)</div>', webpage) + if m_msg is not None: + raise ExtractorError( + u'The video is not available, Facebook said: "%s"' % m_msg.group(1), + expected=True) + else: + raise ExtractorError(u'Cannot parse data') data = dict(json.loads(m.group(1))) params_raw = compat_urllib_parse.unquote(data['params']) params = json.loads(params_raw) diff --git a/youtube_dl/extractor/faz.py b/youtube_dl/extractor/faz.py index deaa4ed..89ed08d 100644 --- a/youtube_dl/extractor/faz.py +++ b/youtube_dl/extractor/faz.py @@ -5,8 +5,6 @@ import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( determine_ext, - clean_html, - get_element_by_attribute, ) @@ -47,12 +45,12 @@ class FazIE(InfoExtractor): 'format_id': code.lower(), }) - descr_html = get_element_by_attribute('class', 'Content Copy', webpage) + descr = self._html_search_regex(r'<p class="Content Copy">(.*?)</p>', webpage, u'description') info = { 'id': video_id, 'title': self._og_search_title(webpage), 'formats': formats, - 'description': clean_html(descr_html), + 'description': descr, 'thumbnail': config.find('STILL/STILL_BIG').text, } # TODO: Remove when #980 has been merged diff --git a/youtube_dl/extractor/generic.py b/youtube_dl/extractor/generic.py index 69e0a7b..c7552fd 100644 --- a/youtube_dl/extractor/generic.py +++ b/youtube_dl/extractor/generic.py @@ -25,7 +25,7 @@ class GenericIE(InfoExtractor): { u'url': u'http://www.hodiho.fr/2013/02/regis-plante-sa-jeep.html', u'file': u'13601338388002.mp4', - u'md5': u'85b90ccc9d73b4acd9138d3af4c27f89', + u'md5': u'6e15c93721d7ec9e9ca3fdbf07982cfd', u'info_dict': { u"uploader": u"www.hodiho.fr", u"title": u"R\u00e9gis plante sa Jeep" @@ -33,6 +33,7 @@ class GenericIE(InfoExtractor): }, # embedded vimeo video { + u'add_ie': ['Vimeo'], u'url': u'http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding-and-rvalue-references', u'file': u'22444065.mp4', u'md5': u'2903896e23df39722c33f015af0666e2', @@ -41,7 +42,35 @@ class GenericIE(InfoExtractor): u"uploader_id": u"skillsmatter", u"uploader": u"Skills Matter", } - } + }, + # bandcamp page with custom domain + { + u'add_ie': ['Bandcamp'], + u'url': u'http://bronyrock.com/track/the-pony-mash', + u'file': u'3235767654.mp3', + u'info_dict': { + u'title': u'The Pony Mash', + u'uploader': u'M_Pallante', + }, + u'skip': u'There is a limit of 200 free downloads / month for the test song', + }, + # embedded brightcove video + # it also tests brightcove videos that need to set the 'Referer' in the + # http requests + { + u'add_ie': ['Brightcove'], + u'url': u'http://www.bfmtv.com/video/bfmbusiness/cours-bourse/cours-bourse-l-analyse-technique-154522/', + u'info_dict': { + u'id': u'2765128793001', + u'ext': u'mp4', + u'title': u'Le cours de bourse : l’analyse technique', + u'description': u'md5:7e9ad046e968cb2d1114004aba466fd9', + u'uploader': u'BFM BUSINESS', + }, + u'params': { + u'skip_download': True, + }, + }, ] def report_download_webpage(self, video_id): @@ -134,10 +163,9 @@ class GenericIE(InfoExtractor): self.report_extraction(video_id) # Look for BrightCove: - m_brightcove = re.search(r'<object[^>]+?class=([\'"])[^>]*?BrightcoveExperience.*?\1.+?</object>', webpage, re.DOTALL) - if m_brightcove is not None: + bc_url = BrightcoveIE._extract_brightcove_url(webpage) + if bc_url is not None: self.to_screen(u'Brightcove video detected.') - bc_url = BrightcoveIE._build_brighcove_url(m_brightcove.group()) return self.url_result(bc_url, 'Brightcove') # Look for embedded Vimeo player @@ -150,11 +178,17 @@ class GenericIE(InfoExtractor): # Look for embedded YouTube player mobj = re.search( - r'<iframe[^>]+?src="(https?://(?:www\.)?youtube.com/embed/.+?)"', webpage) + r'<iframe[^>]+?src=(["\'])(?P<url>https?://(?:www\.)?youtube.com/embed/.+?)\1', webpage) if mobj: - surl = unescapeHTML(mobj.group(1)) + surl = unescapeHTML(mobj.group(u'url')) return self.url_result(surl, 'Youtube') + # Look for Bandcamp pages with custom domain + mobj = re.search(r'<meta property="og:url"[^>]*?content="(.*?bandcamp\.com.*?)"', webpage) + if mobj is not None: + burl = unescapeHTML(mobj.group(1)) + return self.url_result(burl, 'Bandcamp') + # Start with something easy: JW Player in SWFObject mobj = re.search(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage) if mobj is None: diff --git a/youtube_dl/extractor/hypem.py b/youtube_dl/extractor/hypem.py index ab2b591..9bd06e7 100644 --- a/youtube_dl/extractor/hypem.py +++ b/youtube_dl/extractor/hypem.py @@ -30,7 +30,7 @@ class HypemIE(InfoExtractor): raise ExtractorError(u'Invalid URL: %s' % url) track_id = mobj.group(1) - data = { 'ax': 1, 'ts': time.time() } + data = {'ax': 1, 'ts': time.time()} data_encoded = compat_urllib_parse.urlencode(data) complete_url = url + "?" + data_encoded request = compat_urllib_request.Request(complete_url) @@ -68,4 +68,4 @@ class HypemIE(InfoExtractor): 'ext': "mp3", 'title': title, 'artist': artist, - }] \ No newline at end of file + }] diff --git a/youtube_dl/extractor/instagram.py b/youtube_dl/extractor/instagram.py index ddc4288..213aac4 100644 --- a/youtube_dl/extractor/instagram.py +++ b/youtube_dl/extractor/instagram.py @@ -26,7 +26,7 @@ class InstagramIE(InfoExtractor): return [{ 'id': video_id, - 'url': self._og_search_video_url(webpage), + 'url': self._og_search_video_url(webpage, secure=False), 'ext': 'mp4', 'title': u'Video by %s' % uploader_id, 'thumbnail': self._og_search_thumbnail(webpage), diff --git a/youtube_dl/extractor/kankan.py b/youtube_dl/extractor/kankan.py index 445d465..50916f4 100644 --- a/youtube_dl/extractor/kankan.py +++ b/youtube_dl/extractor/kankan.py @@ -1,8 +1,10 @@ import re +import hashlib from .common import InfoExtractor from ..utils import determine_ext +_md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest() class KankanIE(InfoExtractor): _VALID_URL = r'https?://(?:.*?\.)?kankan\.com/.+?/(?P<id>\d+)\.shtml' @@ -30,7 +32,10 @@ class KankanIE(InfoExtractor): video_id, u'Downloading video url info') ip = self._search_regex(r'ip:"(.+?)"', video_info_page, u'video url ip') path = self._search_regex(r'path:"(.+?)"', video_info_page, u'video url path') - video_url = 'http://%s%s' % (ip, path) + param1 = self._search_regex(r'param1:(\d+)', video_info_page, u'param1') + param2 = self._search_regex(r'param2:(\d+)', video_info_page, u'param2') + key = _md5('xl_mp43651' + param1 + param2) + video_url = 'http://%s%s?key=%s&key1=%s' % (ip, path, key, param2) return {'id': video_id, 'title': title, diff --git a/youtube_dl/extractor/keezmovies.py b/youtube_dl/extractor/keezmovies.py new file mode 100644 index 0000000..29658a7 --- /dev/null +++ b/youtube_dl/extractor/keezmovies.py @@ -0,0 +1,61 @@ +import os +import re + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse_urlparse, + compat_urllib_request, + compat_urllib_parse, +) +from ..aes import ( + aes_decrypt_text +) + +class KeezMoviesIE(InfoExtractor): + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>keezmovies\.com/video/.+?(?P<videoid>[0-9]+))(?:[/?&]|$)' + _TEST = { + u'url': u'http://www.keezmovies.com/video/petite-asian-lady-mai-playing-in-bathtub-1214711', + u'file': u'1214711.mp4', + u'md5': u'6e297b7e789329923fcf83abb67c9289', + u'info_dict': { + u"title": u"Petite Asian Lady Mai Playing In Bathtub", + u"age_limit": 18, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') + + req = compat_urllib_request.Request(url) + req.add_header('Cookie', 'age_verified=1') + webpage = self._download_webpage(req, video_id) + + # embedded video + mobj = re.search(r'href="([^"]+)"></iframe>', webpage) + if mobj: + embedded_url = mobj.group(1) + return self.url_result(embedded_url) + + video_title = self._html_search_regex(r'<h1 [^>]*>([^<]+)', webpage, u'title') + video_url = compat_urllib_parse.unquote(self._html_search_regex(r'video_url=(.+?)&', webpage, u'video_url')) + if webpage.find('encrypted=true')!=-1: + password = self._html_search_regex(r'video_title=(.+?)&', webpage, u'password') + video_url = aes_decrypt_text(video_url, password, 32).decode('utf-8') + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] + format = path.split('/')[4].split('_')[:2] + format = "-".join(format) + + age_limit = self._rta_search(webpage) + + return { + 'id': video_id, + 'title': video_title, + 'url': video_url, + 'ext': extension, + 'format': format, + 'format_id': format, + 'age_limit': age_limit, + } diff --git a/youtube_dl/extractor/livestream.py b/youtube_dl/extractor/livestream.py index d04da98..4531fd6 100644 --- a/youtube_dl/extractor/livestream.py +++ b/youtube_dl/extractor/livestream.py @@ -40,13 +40,9 @@ class LivestreamIE(InfoExtractor): if video_id is None: # This is an event page: - player = get_meta_content('twitter:player', webpage) - if player is None: - raise ExtractorError('Couldn\'t extract event api url') - api_url = player.replace('/player', '') - api_url = re.sub(r'^(https?://)(new\.)', r'\1api.\2', api_url) - info = json.loads(self._download_webpage(api_url, event_name, - u'Downloading event info')) + config_json = self._search_regex(r'window.config = ({.*?});', + webpage, u'window config') + info = json.loads(config_json)['event'] videos = [self._extract_video_info(video_data['data']) for video_data in info['feed']['data'] if video_data['type'] == u'video'] return self.playlist_result(videos, info['id'], info['full_name']) diff --git a/youtube_dl/extractor/metacafe.py b/youtube_dl/extractor/metacafe.py index e537648..91480ba 100644 --- a/youtube_dl/extractor/metacafe.py +++ b/youtube_dl/extractor/metacafe.py @@ -20,10 +20,12 @@ class MetacafeIE(InfoExtractor): _DISCLAIMER = 'http://www.metacafe.com/family_filter/' _FILTER_POST = 'http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user' IE_NAME = u'metacafe' - _TESTS = [{ + _TESTS = [ + # Youtube video + { u"add_ie": ["Youtube"], u"url": u"http://metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/", - u"file": u"_aUehQsCQtM.flv", + u"file": u"_aUehQsCQtM.mp4", u"info_dict": { u"upload_date": u"20090102", u"title": u"The Electric Company | \"Short I\" | PBS KIDS GO!", @@ -32,15 +34,42 @@ class MetacafeIE(InfoExtractor): u"uploader_id": u"PBS" } }, + # Normal metacafe video + { + u'url': u'http://www.metacafe.com/watch/11121940/news_stuff_you_wont_do_with_your_playstation_4/', + u'md5': u'6e0bca200eaad2552e6915ed6fd4d9ad', + u'info_dict': { + u'id': u'11121940', + u'ext': u'mp4', + u'title': u'News: Stuff You Won\'t Do with Your PlayStation 4', + u'uploader': u'ign', + u'description': u'Sony released a massive FAQ on the PlayStation Blog detailing the PS4\'s capabilities and limitations.', + }, + }, + # AnyClip video { u"url": u"http://www.metacafe.com/watch/an-dVVXnuY7Jh77J/the_andromeda_strain_1971_stop_the_bomb_part_3/", u"file": u"an-dVVXnuY7Jh77J.mp4", u"info_dict": { u"title": u"The Andromeda Strain (1971): Stop the Bomb Part 3", u"uploader": u"anyclip", - u"description": u"md5:38c711dd98f5bb87acf973d573442e67" - } - }] + u"description": u"md5:38c711dd98f5bb87acf973d573442e67", + }, + }, + # age-restricted video + { + u'url': u'http://www.metacafe.com/watch/5186653/bbc_internal_christmas_tape_79_uncensored_outtakes_etc/', + u'md5': u'98dde7c1a35d02178e8ab7560fe8bd09', + u'info_dict': { + u'id': u'5186653', + u'ext': u'mp4', + u'title': u'BBC INTERNAL Christmas Tape \'79 - UNCENSORED Outtakes, Etc.', + u'uploader': u'Dwayne Pipe', + u'description': u'md5:950bf4c581e2c059911fa3ffbe377e4b', + u'age_limit': 18, + }, + }, + ] def report_disclaimer(self): @@ -62,6 +91,7 @@ class MetacafeIE(InfoExtractor): 'submit': "Continue - I'm over 18", } request = compat_urllib_request.Request(self._FILTER_POST, compat_urllib_parse.urlencode(disclaimer_form)) + request.add_header('Content-Type', 'application/x-www-form-urlencoded') try: self.report_age_confirmation() compat_urllib_request.urlopen(request).read() @@ -83,7 +113,12 @@ class MetacafeIE(InfoExtractor): # Retrieve video webpage to extract further information req = compat_urllib_request.Request('http://www.metacafe.com/watch/%s/' % video_id) - req.headers['Cookie'] = 'flashVersion=0;' + + # AnyClip videos require the flashversion cookie so that we get the link + # to the mp4 file + mobj_an = re.match(r'^an-(.*?)$', video_id) + if mobj_an: + req.headers['Cookie'] = 'flashVersion=0;' webpage = self._download_webpage(req, video_id) # Extract URL, uploader and title from webpage @@ -125,6 +160,11 @@ class MetacafeIE(InfoExtractor): r'submitter=(.*?);|googletag\.pubads\(\)\.setTargeting\("(?:channel|submiter)","([^"]+)"\);', webpage, u'uploader nickname', fatal=False) + if re.search(r'"contentRating":"restricted"', webpage) is not None: + age_limit = 18 + else: + age_limit = 0 + return { '_type': 'video', 'id': video_id, @@ -134,4 +174,5 @@ class MetacafeIE(InfoExtractor): 'upload_date': None, 'title': video_title, 'ext': video_ext, + 'age_limit': age_limit, } diff --git a/youtube_dl/extractor/mofosex.py b/youtube_dl/extractor/mofosex.py new file mode 100644 index 0000000..b9430b0 --- /dev/null +++ b/youtube_dl/extractor/mofosex.py @@ -0,0 +1,49 @@ +import os +import re + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse_urlparse, + compat_urllib_request, + compat_urllib_parse, +) + +class MofosexIE(InfoExtractor): + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>mofosex\.com/videos/(?P<videoid>[0-9]+)/.*?\.html)' + _TEST = { + u'url': u'http://www.mofosex.com/videos/5018/japanese-teen-music-video.html', + u'file': u'5018.mp4', + u'md5': u'1b2eb47ac33cc75d4a80e3026b613c5a', + u'info_dict': { + u"title": u"Japanese Teen Music Video", + u"age_limit": 18, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') + + req = compat_urllib_request.Request(url) + req.add_header('Cookie', 'age_verified=1') + webpage = self._download_webpage(req, video_id) + + video_title = self._html_search_regex(r'<h1>(.+?)<', webpage, u'title') + video_url = compat_urllib_parse.unquote(self._html_search_regex(r'flashvars.video_url = \'([^\']+)', webpage, u'video_url')) + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] + format = path.split('/')[5].split('_')[:2] + format = "-".join(format) + + age_limit = self._rta_search(webpage) + + return { + 'id': video_id, + 'title': video_title, + 'url': video_url, + 'ext': extension, + 'format': format, + 'format_id': format, + 'age_limit': age_limit, + } diff --git a/youtube_dl/extractor/mtv.py b/youtube_dl/extractor/mtv.py index e520e2b..24a79ae 100644 --- a/youtube_dl/extractor/mtv.py +++ b/youtube_dl/extractor/mtv.py @@ -26,6 +26,7 @@ class MTVIE(InfoExtractor): }, }, { + u'add_ie': ['Vevo'], u'url': u'http://www.mtv.com/videos/taylor-swift/916187/everything-has-changed-ft-ed-sheeran.jhtml', u'file': u'USCJY1331283.mp4', u'md5': u'73b4e7fcadd88929292fe52c3ced8caf', @@ -80,6 +81,8 @@ class MTVIE(InfoExtractor): video_id = self._id_from_uri(uri) self.report_extraction(video_id) mediagen_url = itemdoc.find('%s/%s' % (_media_xml_tag('group'), _media_xml_tag('content'))).attrib['url'] + # Remove the templates, like &device={device} + mediagen_url = re.sub(r'&[^=]*?={.*?}(?=(&|$))', u'', mediagen_url) if 'acceptMethods' not in mediagen_url: mediagen_url += '&acceptMethods=fms' mediagen_page = self._download_webpage(mediagen_url, video_id, diff --git a/youtube_dl/extractor/myspace.py b/youtube_dl/extractor/myspace.py new file mode 100644 index 0000000..050f54a --- /dev/null +++ b/youtube_dl/extractor/myspace.py @@ -0,0 +1,48 @@ +import re +import json + +from .common import InfoExtractor +from ..utils import ( + compat_str, +) + + +class MySpaceIE(InfoExtractor): + _VALID_URL = r'https?://myspace\.com/([^/]+)/video/[^/]+/(?P<id>\d+)' + + _TEST = { + u'url': u'https://myspace.com/coldplay/video/viva-la-vida/100008689', + u'info_dict': { + u'id': u'100008689', + u'ext': u'flv', + u'title': u'Viva La Vida', + u'description': u'The official Viva La Vida video, directed by Hype Williams', + u'uploader': u'Coldplay', + u'uploader_id': u'coldplay', + }, + u'params': { + # rtmp download + u'skip_download': True, + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + webpage = self._download_webpage(url, video_id) + context = json.loads(self._search_regex(r'context = ({.*?});', webpage, + u'context')) + video = context['video'] + rtmp_url, play_path = video['streamUrl'].split(';', 1) + + return { + 'id': compat_str(video['mediaId']), + 'title': video['title'], + 'url': rtmp_url, + 'play_path': play_path, + 'ext': 'flv', + 'description': video['description'], + 'thumbnail': video['imageUrl'], + 'uploader': video['artistName'], + 'uploader_id': video['artistUsername'], + } diff --git a/youtube_dl/extractor/nowvideo.py b/youtube_dl/extractor/nowvideo.py index ab52ad4..241cc16 100644 --- a/youtube_dl/extractor/nowvideo.py +++ b/youtube_dl/extractor/nowvideo.py @@ -20,7 +20,10 @@ class NowVideoIE(InfoExtractor): video_id = mobj.group('id') webpage_url = 'http://www.nowvideo.ch/video/' + video_id + embed_url = 'http://embed.nowvideo.ch/embed.php?v=' + video_id webpage = self._download_webpage(webpage_url, video_id) + embed_page = self._download_webpage(embed_url, video_id, + u'Downloading embed page') self.report_extraction(video_id) @@ -28,7 +31,7 @@ class NowVideoIE(InfoExtractor): webpage, u'video title') video_key = self._search_regex(r'var fkzd="(.*)";', - webpage, u'video key') + embed_page, u'video key') api_call = "http://www.nowvideo.ch/api/player.api.php?file={0}&numOfErrors=0&cid=1&key={1}".format(video_id, video_key) api_response = self._download_webpage(api_call, video_id, diff --git a/youtube_dl/extractor/pornhub.py b/youtube_dl/extractor/pornhub.py new file mode 100644 index 0000000..75cf4bb --- /dev/null +++ b/youtube_dl/extractor/pornhub.py @@ -0,0 +1,69 @@ +import os +import re + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse_urlparse, + compat_urllib_request, + compat_urllib_parse, + unescapeHTML, +) +from ..aes import ( + aes_decrypt_text +) + +class PornHubIE(InfoExtractor): + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>pornhub\.com/view_video\.php\?viewkey=(?P<videoid>[0-9]+))' + _TEST = { + u'url': u'http://www.pornhub.com/view_video.php?viewkey=648719015', + u'file': u'648719015.mp4', + u'md5': u'882f488fa1f0026f023f33576004a2ed', + u'info_dict': { + u"uploader": u"BABES-COM", + u"title": u"Seductive Indian beauty strips down and fingers her pink pussy", + u"age_limit": 18 + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') + + req = compat_urllib_request.Request(url) + req.add_header('Cookie', 'age_verified=1') + webpage = self._download_webpage(req, video_id) + + video_title = self._html_search_regex(r'<h1 [^>]+>([^<]+)', webpage, u'title') + video_uploader = self._html_search_regex(r'<b>From: </b>(?:\s|<[^>]*>)*(.+?)<', webpage, u'uploader', fatal=False) + thumbnail = self._html_search_regex(r'"image_url":"([^"]+)', webpage, u'thumbnail', fatal=False) + if thumbnail: + thumbnail = compat_urllib_parse.unquote(thumbnail) + + video_urls = list(map(compat_urllib_parse.unquote , re.findall(r'"quality_[0-9]{3}p":"([^"]+)', webpage))) + if webpage.find('"encrypted":true') != -1: + password = self._html_search_regex(r'"video_title":"([^"]+)', webpage, u'password').replace('+', ' ') + video_urls = list(map(lambda s: aes_decrypt_text(s, password, 32).decode('utf-8'), video_urls)) + + formats = [] + for video_url in video_urls: + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] + format = path.split('/')[5].split('_')[:2] + format = "-".join(format) + formats.append({ + 'url': video_url, + 'ext': extension, + 'format': format, + 'format_id': format, + }) + formats.sort(key=lambda format: list(map(lambda s: s.zfill(6), format['format'].split('-')))) + + return { + 'id': video_id, + 'uploader': video_uploader, + 'title': video_title, + 'thumbnail': thumbnail, + 'formats': formats, + 'age_limit': 18, + } diff --git a/youtube_dl/extractor/pornotube.py b/youtube_dl/extractor/pornotube.py index 5d770ec..35dc5a9 100644 --- a/youtube_dl/extractor/pornotube.py +++ b/youtube_dl/extractor/pornotube.py @@ -16,7 +16,8 @@ class PornotubeIE(InfoExtractor): u'md5': u'374dd6dcedd24234453b295209aa69b6', u'info_dict': { u"upload_date": u"20090708", - u"title": u"Marilyn-Monroe-Bathing" + u"title": u"Marilyn-Monroe-Bathing", + u"age_limit": 18 } } diff --git a/youtube_dl/extractor/rtlnow.py b/youtube_dl/extractor/rtlnow.py index d1b08c9..9ac7c3b 100644 --- a/youtube_dl/extractor/rtlnow.py +++ b/youtube_dl/extractor/rtlnow.py @@ -63,13 +63,12 @@ class RTLnowIE(InfoExtractor): }, }, { - u'url': u'http://www.rtlnitronow.de/recht-ordnung/lebensmittelkontrolle-erlangenordnungsamt-berlin.php?film_id=127367&player=1&season=1', - u'file': u'127367.flv', + u'url': u'http://www.rtlnitronow.de/recht-ordnung/stadtpolizei-frankfurt-gerichtsvollzieher-leipzig.php?film_id=129679&player=1&season=1', + u'file': u'129679.flv', u'info_dict': { - u'upload_date': u'20130926', - u'title': u'Recht & Ordnung - Lebensmittelkontrolle Erlangen/Ordnungsamt...', - u'description': u'Lebensmittelkontrolle Erlangen/Ordnungsamt Berlin', - u'thumbnail': u'http://autoimg.static-fra.de/nitronow/344787/1500x1500/image2.jpg', + u'upload_date': u'20131016', + u'title': u'Recht & Ordnung - Stadtpolizei Frankfurt/ Gerichtsvollzieher...', + u'description': u'Stadtpolizei Frankfurt/ Gerichtsvollzieher Leipzig', }, u'params': { u'skip_download': True, diff --git a/youtube_dl/extractor/slashdot.py b/youtube_dl/extractor/slashdot.py index 2cba530..f5003c7 100644 --- a/youtube_dl/extractor/slashdot.py +++ b/youtube_dl/extractor/slashdot.py @@ -7,6 +7,7 @@ class SlashdotIE(InfoExtractor): _VALID_URL = r'https?://tv.slashdot.org/video/\?embed=(?P<id>.*?)(&|$)' _TEST = { + u'add_ie': ['Ooyala'], u'url': u'http://tv.slashdot.org/video/?embed=JscHMzZDplD0p-yNLOzTfzC3Q3xzJaUz', u'file': u'JscHMzZDplD0p-yNLOzTfzC3Q3xzJaUz.mp4', u'md5': u'd2222e7a4a4c1541b3e0cf732fb26735', diff --git a/youtube_dl/extractor/soundcloud.py b/youtube_dl/extractor/soundcloud.py index 29cd561..4717fbb 100644 --- a/youtube_dl/extractor/soundcloud.py +++ b/youtube_dl/extractor/soundcloud.py @@ -29,17 +29,34 @@ class SoundcloudIE(InfoExtractor): ) ''' IE_NAME = u'soundcloud' - _TEST = { - u'url': u'http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy', - u'file': u'62986583.mp3', - u'md5': u'ebef0a451b909710ed1d7787dddbf0d7', - u'info_dict': { - u"upload_date": u"20121011", - u"description": u"No Downloads untill we record the finished version this weekend, i was too pumped n i had to post it , earl is prolly gonna b hella p.o'd", - u"uploader": u"E.T. ExTerrestrial Music", - u"title": u"Lostin Powers - She so Heavy (SneakPreview) Adrian Ackers Blueprint 1" - } - } + _TESTS = [ + { + u'url': u'http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy', + u'file': u'62986583.mp3', + u'md5': u'ebef0a451b909710ed1d7787dddbf0d7', + u'info_dict': { + u"upload_date": u"20121011", + u"description": u"No Downloads untill we record the finished version this weekend, i was too pumped n i had to post it , earl is prolly gonna b hella p.o'd", + u"uploader": u"E.T. ExTerrestrial Music", + u"title": u"Lostin Powers - She so Heavy (SneakPreview) Adrian Ackers Blueprint 1" + } + }, + # not streamable song + { + u'url': u'https://soundcloud.com/the-concept-band/goldrushed-mastered?in=the-concept-band/sets/the-royal-concept-ep', + u'info_dict': { + u'id': u'47127627', + u'ext': u'mp3', + u'title': u'Goldrushed', + u'uploader': u'The Royal Concept', + u'upload_date': u'20120521', + }, + u'params': { + # rtmp + u'skip_download': True, + }, + }, + ] _CLIENT_ID = 'b45b1aa10f1ac2941910a7f0d10f8e28' @@ -56,16 +73,16 @@ class SoundcloudIE(InfoExtractor): return 'http://api.soundcloud.com/resolve.json?url=' + url + '&client_id=' + cls._CLIENT_ID def _extract_info_dict(self, info, full_title=None, quiet=False): - video_id = info['id'] - name = full_title or video_id + track_id = compat_str(info['id']) + name = full_title or track_id if quiet == False: self.report_extraction(name) thumbnail = info['artwork_url'] if thumbnail is not None: thumbnail = thumbnail.replace('-large', '-t500x500') - return { - 'id': info['id'], + result = { + 'id': track_id, 'url': info['stream_url'] + '?client_id=' + self._CLIENT_ID, 'uploader': info['user']['username'], 'upload_date': unified_strdate(info['created_at']), @@ -74,6 +91,21 @@ class SoundcloudIE(InfoExtractor): 'description': info['description'], 'thumbnail': thumbnail, } + if info.get('downloadable', False): + result['url'] = 'https://api.soundcloud.com/tracks/{0}/download?client_id={1}'.format(track_id, self._CLIENT_ID) + if not info.get('streamable', False): + # We have to get the rtmp url + stream_json = self._download_webpage( + 'http://api.soundcloud.com/i1/tracks/{0}/streams?client_id={1}'.format(track_id, self._CLIENT_ID), + track_id, u'Downloading track url') + rtmp_url = json.loads(stream_json)['rtmp_mp3_128_url'] + # The url doesn't have an rtmp app, we have to extract the playpath + url, path = rtmp_url.split('mp3:', 1) + result.update({ + 'url': url, + 'play_path': 'mp3:' + path, + }) + return result def _real_extract(self, url): mobj = re.match(self._VALID_URL, url, flags=re.VERBOSE) @@ -106,70 +138,8 @@ class SoundcloudIE(InfoExtractor): class SoundcloudSetIE(SoundcloudIE): _VALID_URL = r'^(?:https?://)?(?:www\.)?soundcloud\.com/([\w\d-]+)/sets/([\w\d-]+)(?:[?].*)?$' IE_NAME = u'soundcloud:set' - _TEST = { - u"url":"https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep", - u"playlist": [ - { - u"file":"30510138.mp3", - u"md5":"f9136bf103901728f29e419d2c70f55d", - u"info_dict": { - u"upload_date": u"20111213", - u"description": u"The Royal Concept from Stockholm\r\nFilip / Povel / David / Magnus\r\nwww.royalconceptband.com", - u"uploader": u"The Royal Concept", - u"title": u"D-D-Dance" - } - }, - { - u"file":"47127625.mp3", - u"md5":"09b6758a018470570f8fd423c9453dd8", - u"info_dict": { - u"upload_date": u"20120521", - u"description": u"The Royal Concept from Stockholm\r\nFilip / Povel / David / Magnus\r\nwww.royalconceptband.com", - u"uploader": u"The Royal Concept", - u"title": u"The Royal Concept - Gimme Twice" - } - }, - { - u"file":"47127627.mp3", - u"md5":"154abd4e418cea19c3b901f1e1306d9c", - u"info_dict": { - u"upload_date": u"20120521", - u"uploader": u"The Royal Concept", - u"title": u"Goldrushed" - } - }, - { - u"file":"47127629.mp3", - u"md5":"2f5471edc79ad3f33a683153e96a79c1", - u"info_dict": { - u"upload_date": u"20120521", - u"description": u"The Royal Concept from Stockholm\r\nFilip / Povel / David / Magnus\r\nwww.royalconceptband.com", - u"uploader": u"The Royal Concept", - u"title": u"In the End" - } - }, - { - u"file":"47127631.mp3", - u"md5":"f9ba87aa940af7213f98949254f1c6e2", - u"info_dict": { - u"upload_date": u"20120521", - u"description": u"The Royal Concept from Stockholm\r\nFilip / David / Povel / Magnus\r\nwww.theroyalconceptband.com", - u"uploader": u"The Royal Concept", - u"title": u"Knocked Up" - } - }, - { - u"file":"75206121.mp3", - u"md5":"f9d1fe9406717e302980c30de4af9353", - u"info_dict": { - u"upload_date": u"20130116", - u"description": u"The unreleased track World on Fire premiered on the CW's hit show Arrow (8pm/7pm central). \r\nAs a gift to our fans we would like to offer you a free download of the track! ", - u"uploader": u"The Royal Concept", - u"title": u"World On Fire" - } - } - ] - } + # it's in tests/test_playlists.py + _TESTS = [] def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) @@ -208,7 +178,7 @@ class SoundcloudUserIE(SoundcloudIE): IE_NAME = u'soundcloud:user' # it's in tests/test_playlists.py - _TEST = None + _TESTS = [] def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) diff --git a/youtube_dl/extractor/space.py b/youtube_dl/extractor/space.py new file mode 100644 index 0000000..0d32a06 --- /dev/null +++ b/youtube_dl/extractor/space.py @@ -0,0 +1,35 @@ +import re + +from .common import InfoExtractor +from .brightcove import BrightcoveIE +from ..utils import RegexNotFoundError, ExtractorError + + +class SpaceIE(InfoExtractor): + _VALID_URL = r'https?://www\.space\.com/\d+-(?P<title>[^/\.\?]*?)-video.html' + _TEST = { + u'add_ie': ['Brightcove'], + u'url': u'http://www.space.com/23373-huge-martian-landforms-detail-revealed-by-european-probe-video.html', + u'info_dict': { + u'id': u'2780937028001', + u'ext': u'mp4', + u'title': u'Huge Martian Landforms\' Detail Revealed By European Probe | Video', + u'description': u'md5:db81cf7f3122f95ed234b631a6ea1e61', + u'uploader': u'TechMedia Networks', + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + title = mobj.group('title') + webpage = self._download_webpage(url, title) + try: + # Some videos require the playerKey field, which isn't define in + # the BrightcoveExperience object + brightcove_url = self._og_search_video_url(webpage) + except RegexNotFoundError: + # Other videos works fine with the info from the object + brightcove_url = BrightcoveIE._extract_brightcove_url(webpage) + if brightcove_url is None: + raise ExtractorError(u'The webpage does not contain a video', expected=True) + return self.url_result(brightcove_url, BrightcoveIE.ie_key()) diff --git a/youtube_dl/extractor/spankwire.py b/youtube_dl/extractor/spankwire.py new file mode 100644 index 0000000..97f9c26 --- /dev/null +++ b/youtube_dl/extractor/spankwire.py @@ -0,0 +1,74 @@ +import os +import re + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse_urlparse, + compat_urllib_request, + compat_urllib_parse, + unescapeHTML, +) +from ..aes import ( + aes_decrypt_text +) + +class SpankwireIE(InfoExtractor): + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>spankwire\.com/[^/]*/video(?P<videoid>[0-9]+)/?)' + _TEST = { + u'url': u'http://www.spankwire.com/Buckcherry-s-X-Rated-Music-Video-Crazy-Bitch/video103545/', + u'file': u'103545.mp4', + u'md5': u'1b3f55e345500552dbc252a3e9c1af43', + u'info_dict': { + u"uploader": u"oreusz", + u"title": u"Buckcherry`s X Rated Music Video Crazy Bitch", + u"description": u"Crazy Bitch X rated music video.", + u"age_limit": 18, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') + + req = compat_urllib_request.Request(url) + req.add_header('Cookie', 'age_verified=1') + webpage = self._download_webpage(req, video_id) + + video_title = self._html_search_regex(r'<h1>([^<]+)', webpage, u'title') + video_uploader = self._html_search_regex(r'by:\s*<a [^>]*>(.+?)</a>', webpage, u'uploader', fatal=False) + thumbnail = self._html_search_regex(r'flashvars\.image_url = "([^"]+)', webpage, u'thumbnail', fatal=False) + description = self._html_search_regex(r'>\s*Description:</div>\s*<[^>]*>([^<]+)', webpage, u'description', fatal=False) + if len(description) == 0: + description = None + + video_urls = list(map(compat_urllib_parse.unquote , re.findall(r'flashvars\.quality_[0-9]{3}p = "([^"]+)', webpage))) + if webpage.find('flashvars\.encrypted = "true"') != -1: + password = self._html_search_regex(r'flashvars\.video_title = "([^"]+)', webpage, u'password').replace('+', ' ') + video_urls = list(map(lambda s: aes_decrypt_text(s, password, 32).decode('utf-8'), video_urls)) + + formats = [] + for video_url in video_urls: + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] + format = path.split('/')[4].split('_')[:2] + format = "-".join(format) + formats.append({ + 'url': video_url, + 'ext': extension, + 'format': format, + 'format_id': format, + }) + formats.sort(key=lambda format: list(map(lambda s: s.zfill(6), format['format'].split('-')))) + + age_limit = self._rta_search(webpage) + + return { + 'id': video_id, + 'uploader': video_uploader, + 'title': video_title, + 'thumbnail': thumbnail, + 'description': description, + 'formats': formats, + 'age_limit': age_limit, + } diff --git a/youtube_dl/extractor/subtitles.py b/youtube_dl/extractor/subtitles.py index 90de7de..4b4c523 100644 --- a/youtube_dl/extractor/subtitles.py +++ b/youtube_dl/extractor/subtitles.py @@ -12,9 +12,9 @@ class SubtitlesInfoExtractor(InfoExtractor): return any([self._downloader.params.get('writesubtitles', False), self._downloader.params.get('writeautomaticsub')]) - def _list_available_subtitles(self, video_id, webpage=None): + def _list_available_subtitles(self, video_id, webpage): """ outputs the available subtitles for the video """ - sub_lang_list = self._get_available_subtitles(video_id) + sub_lang_list = self._get_available_subtitles(video_id, webpage) auto_captions_list = self._get_available_automatic_caption(video_id, webpage) sub_lang = ",".join(list(sub_lang_list.keys())) self.to_screen(u'%s: Available subtitles for video: %s' % @@ -23,7 +23,7 @@ class SubtitlesInfoExtractor(InfoExtractor): self.to_screen(u'%s: Available automatic captions for video: %s' % (video_id, auto_lang)) - def extract_subtitles(self, video_id, video_webpage=None): + def extract_subtitles(self, video_id, webpage): """ returns {sub_lang: sub} ,{} if subtitles not found or None if the subtitles aren't requested. @@ -32,9 +32,9 @@ class SubtitlesInfoExtractor(InfoExtractor): return None available_subs_list = {} if self._downloader.params.get('writeautomaticsub', False): - available_subs_list.update(self._get_available_automatic_caption(video_id, video_webpage)) + available_subs_list.update(self._get_available_automatic_caption(video_id, webpage)) if self._downloader.params.get('writesubtitles', False): - available_subs_list.update(self._get_available_subtitles(video_id)) + available_subs_list.update(self._get_available_subtitles(video_id, webpage)) if not available_subs_list: # error, it didn't get the available subtitles return {} @@ -74,7 +74,7 @@ class SubtitlesInfoExtractor(InfoExtractor): return return sub - def _get_available_subtitles(self, video_id): + def _get_available_subtitles(self, video_id, webpage): """ returns {sub_lang: url} or {} if not available Must be redefined by the subclasses diff --git a/youtube_dl/extractor/teamcoco.py b/youtube_dl/extractor/teamcoco.py index c910110..bc48620 100644 --- a/youtube_dl/extractor/teamcoco.py +++ b/youtube_dl/extractor/teamcoco.py @@ -1,4 +1,5 @@ import re +import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -11,7 +12,7 @@ class TeamcocoIE(InfoExtractor): _TEST = { u'url': u'http://teamcoco.com/video/louis-ck-interview-george-w-bush', u'file': u'19705.mp4', - u'md5': u'27b6f7527da5acf534b15f21b032656e', + u'md5': u'cde9ba0fa3506f5f017ce11ead928f9a', u'info_dict': { u"description": u"Louis C.K. got starstruck by George W. Bush, so what? Part one.", u"title": u"Louis C.K. Interview Pt. 1 11/3/11" @@ -31,16 +32,40 @@ class TeamcocoIE(InfoExtractor): self.report_extraction(video_id) data_url = 'http://teamcoco.com/cvp/2.0/%s.xml' % video_id - data = self._download_webpage(data_url, video_id, 'Downloading data webpage') + data_xml = self._download_webpage(data_url, video_id, 'Downloading data webpage') + data = xml.etree.ElementTree.fromstring(data_xml.encode('utf-8')) - video_url = self._html_search_regex(r'<file [^>]*type="high".*?>(.*?)</file>', - data, u'video URL') - return [{ + qualities = ['500k', '480p', '1000k', '720p', '1080p'] + formats = [] + for file in data.findall('files/file'): + if file.attrib.get('playmode') == 'all': + # it just duplicates one of the entries + break + file_url = file.text + m_format = re.search(r'(\d+(k|p))\.mp4', file_url) + if m_format is not None: + format_id = m_format.group(1) + else: + format_id = file.attrib['bitrate'] + formats.append({ + 'url': file_url, + 'ext': 'mp4', + 'format_id': format_id, + }) + def sort_key(f): + try: + return qualities.index(f['format_id']) + except ValueError: + return -1 + formats.sort(key=sort_key) + if not formats: + raise RegexNotFoundError(u'Unable to extract video URL') + + return { 'id': video_id, - 'url': video_url, - 'ext': 'mp4', + 'formats': formats, 'title': self._og_search_title(webpage), 'thumbnail': self._og_search_thumbnail(webpage), 'description': self._og_search_description(webpage), - }] + } diff --git a/youtube_dl/extractor/ted.py b/youtube_dl/extractor/ted.py index dfa1176..76cfdfb 100644 --- a/youtube_dl/extractor/ted.py +++ b/youtube_dl/extractor/ted.py @@ -1,10 +1,14 @@ import json import re -from .common import InfoExtractor +from .subtitles import SubtitlesInfoExtractor +from ..utils import ( + compat_str, + RegexNotFoundError, +) -class TEDIE(InfoExtractor): +class TEDIE(SubtitlesInfoExtractor): _VALID_URL=r'''http://www\.ted\.com/ ( ((?P<type_playlist>playlists)/(?P<playlist_id>\d+)) # We have a playlist @@ -32,7 +36,7 @@ class TEDIE(InfoExtractor): def _real_extract(self, url): m=re.match(self._VALID_URL, url, re.VERBOSE) if m.group('type_talk'): - return [self._talk_info(url)] + return self._talk_info(url) else : playlist_id=m.group('playlist_id') name=m.group('name') @@ -82,11 +86,21 @@ class TEDIE(InfoExtractor): 'url': stream['file'], 'format': stream['id'] } for stream in info['htmlStreams']] + + video_id = info['id'] + + # subtitles + video_subtitles = self.extract_subtitles(video_id, webpage) + if self._downloader.params.get('listsubtitles', False): + self._list_available_subtitles(video_id, webpage) + return + info = { - 'id': info['id'], + 'id': video_id, 'title': title, 'thumbnail': thumbnail, 'description': desc, + 'subtitles': video_subtitles, 'formats': formats, } @@ -94,3 +108,17 @@ class TEDIE(InfoExtractor): info.update(info['formats'][-1]) return info + + def _get_available_subtitles(self, video_id, webpage): + try: + options = self._search_regex(r'(?:<select name="subtitles_language_select" id="subtitles_language_select">)(.*?)(?:</select>)', webpage, 'subtitles_language_select', flags=re.DOTALL) + languages = re.findall(r'(?:<option value=")(\S+)"', options) + if languages: + sub_lang_list = {} + for l in languages: + url = 'http://www.ted.com/talks/subtitles/id/%s/lang/%s/format/srt' % (video_id, l) + sub_lang_list[l] = url + return sub_lang_list + except RegexNotFoundError as err: + self._downloader.report_warning(u'video doesn\'t have subtitles') + return {} diff --git a/youtube_dl/extractor/tube8.py b/youtube_dl/extractor/tube8.py new file mode 100644 index 0000000..d4b7603 --- /dev/null +++ b/youtube_dl/extractor/tube8.py @@ -0,0 +1,65 @@ +import os +import re + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse_urlparse, + compat_urllib_request, + compat_urllib_parse, + unescapeHTML, +) +from ..aes import ( + aes_decrypt_text +) + +class Tube8IE(InfoExtractor): + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>tube8\.com/[^/]+/[^/]+/(?P<videoid>[0-9]+)/?)' + _TEST = { + u'url': u'http://www.tube8.com/teen/kasia-music-video/229795/', + u'file': u'229795.mp4', + u'md5': u'e9e0b0c86734e5e3766e653509475db0', + u'info_dict': { + u"description": u"hot teen Kasia grinding", + u"uploader": u"unknown", + u"title": u"Kasia music video", + u"age_limit": 18, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') + + req = compat_urllib_request.Request(url) + req.add_header('Cookie', 'age_verified=1') + webpage = self._download_webpage(req, video_id) + + video_title = self._html_search_regex(r'videotitle ="([^"]+)', webpage, u'title') + video_description = self._html_search_regex(r'>Description:</strong>(.+?)<', webpage, u'description', fatal=False) + video_uploader = self._html_search_regex(r'>Submitted by:</strong>(?:\s|<[^>]*>)*(.+?)<', webpage, u'uploader', fatal=False) + thumbnail = self._html_search_regex(r'"image_url":"([^"]+)', webpage, u'thumbnail', fatal=False) + if thumbnail: + thumbnail = thumbnail.replace('\\/', '/') + + video_url = self._html_search_regex(r'"video_url":"([^"]+)', webpage, u'video_url') + if webpage.find('"encrypted":true')!=-1: + password = self._html_search_regex(r'"video_title":"([^"]+)', webpage, u'password') + video_url = aes_decrypt_text(video_url, password, 32).decode('utf-8') + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] + format = path.split('/')[4].split('_')[:2] + format = "-".join(format) + + return { + 'id': video_id, + 'uploader': video_uploader, + 'title': video_title, + 'thumbnail': thumbnail, + 'description': video_description, + 'url': video_url, + 'ext': extension, + 'format': format, + 'format_id': format, + 'age_limit': 18, + } diff --git a/youtube_dl/extractor/vevo.py b/youtube_dl/extractor/vevo.py index 1c1cc41..3f6020f 100644 --- a/youtube_dl/extractor/vevo.py +++ b/youtube_dl/extractor/vevo.py @@ -5,7 +5,7 @@ import datetime from .common import InfoExtractor from ..utils import ( - determine_ext, + compat_HTTPError, ExtractorError, ) @@ -16,26 +16,22 @@ class VevoIE(InfoExtractor): (currently used by MTVIE) """ _VALID_URL = r'((http://www.vevo.com/watch/.*?/.*?/)|(vevo:))(?P<id>.*?)(\?|$)' - _TEST = { + _TESTS = [{ u'url': u'http://www.vevo.com/watch/hurts/somebody-to-die-for/GB1101300280', u'file': u'GB1101300280.mp4', + u"md5": u"06bea460acb744eab74a9d7dcb4bfd61", u'info_dict': { u"upload_date": u"20130624", u"uploader": u"Hurts", u"title": u"Somebody to Die For", - u'duration': 230, + u"duration": 230, + u"width": 1920, + u"height": 1080, } - } + }] + _SMIL_BASE_URL = 'http://smil.lvl3.vevo.com/' - def _real_extract(self, url): - mobj = re.match(self._VALID_URL, url) - video_id = mobj.group('id') - - json_url = 'http://videoplayer.vevo.com/VideoService/AuthenticateVideo?isrc=%s' % video_id - info_json = self._download_webpage(json_url, video_id, u'Downloading json info') - - self.report_extraction(video_id) - video_info = json.loads(info_json)['video'] + def _formats_from_json(self, video_info): last_version = {'version': -1} for version in video_info['videoVersions']: # These are the HTTP downloads, other types are for different manifests @@ -50,17 +46,74 @@ class VevoIE(InfoExtractor): # Already sorted from worst to best quality for rend in renditions.findall('rendition'): attr = rend.attrib - f_url = attr['url'] + format_note = '%(videoCodec)s@%(videoBitrate)4sk, %(audioCodec)s@%(audioBitrate)3sk' % attr formats.append({ - 'url': f_url, - 'ext': determine_ext(f_url), + 'url': attr['url'], + 'format_id': attr['name'], + 'format_note': format_note, 'height': int(attr['frameheight']), 'width': int(attr['frameWidth']), }) + return formats + + def _formats_from_smil(self, smil_xml): + formats = [] + smil_doc = xml.etree.ElementTree.fromstring(smil_xml.encode('utf-8')) + els = smil_doc.findall('.//{http://www.w3.org/2001/SMIL20/Language}video') + for el in els: + src = el.attrib['src'] + m = re.match(r'''(?xi) + (?P<ext>[a-z0-9]+): + (?P<path> + [/a-z0-9]+ # The directory and main part of the URL + _(?P<cbr>[0-9]+)k + _(?P<width>[0-9]+)x(?P<height>[0-9]+) + _(?P<vcodec>[a-z0-9]+) + _(?P<vbr>[0-9]+) + _(?P<acodec>[a-z0-9]+) + _(?P<abr>[0-9]+) + \.[a-z0-9]+ # File extension + )''', src) + if not m: + continue - date_epoch = int(self._search_regex( - r'/Date\((\d+)\)/', video_info['launchDate'], u'launch date'))/1000 - upload_date = datetime.datetime.fromtimestamp(date_epoch) + format_url = self._SMIL_BASE_URL + m.group('path') + format_note = ('%(vcodec)s@%(vbr)4sk, %(acodec)s@%(abr)3sk' % + m.groupdict()) + formats.append({ + 'url': format_url, + 'format_id': u'SMIL_' + m.group('cbr'), + 'format_note': format_note, + 'ext': m.group('ext'), + 'width': int(m.group('width')), + 'height': int(m.group('height')), + }) + return formats + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + + json_url = 'http://videoplayer.vevo.com/VideoService/AuthenticateVideo?isrc=%s' % video_id + info_json = self._download_webpage(json_url, video_id, u'Downloading json info') + video_info = json.loads(info_json)['video'] + + formats = self._formats_from_json(video_info) + try: + smil_url = '%s/Video/V2/VFILE/%s/%sr.smil' % ( + self._SMIL_BASE_URL, video_id, video_id.lower()) + smil_xml = self._download_webpage(smil_url, video_id, + u'Downloading SMIL info') + formats.extend(self._formats_from_smil(smil_xml)) + except ExtractorError as ee: + if not isinstance(ee.cause, compat_HTTPError): + raise + self._downloader.report_warning( + u'Cannot download SMIL information, falling back to JSON ..') + + timestamp_ms = int(self._search_regex( + r'/Date\((\d+)\)/', video_info['launchDate'], u'launch date')) + upload_date = datetime.datetime.fromtimestamp(timestamp_ms // 1000) info = { 'id': video_id, 'title': video_info['title'], @@ -71,7 +124,4 @@ class VevoIE(InfoExtractor): 'duration': video_info['duration'], } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info diff --git a/youtube_dl/extractor/viddler.py b/youtube_dl/extractor/viddler.py index 12c84a9..826804a 100644 --- a/youtube_dl/extractor/viddler.py +++ b/youtube_dl/extractor/viddler.py @@ -8,7 +8,7 @@ from ..utils import ( class ViddlerIE(InfoExtractor): - _VALID_URL = r'(?P<domain>https?://(?:www\.)?viddler.com)/(?:v|embed|player)/(?P<id>[0-9]+)' + _VALID_URL = r'(?P<domain>https?://(?:www\.)?viddler.com)/(?:v|embed|player)/(?P<id>[a-z0-9]+)' _TEST = { u"url": u"http://www.viddler.com/v/43903784", u'file': u'43903784.mp4', diff --git a/youtube_dl/extractor/vimeo.py b/youtube_dl/extractor/vimeo.py index 1125513..d465bf2 100644 --- a/youtube_dl/extractor/vimeo.py +++ b/youtube_dl/extractor/vimeo.py @@ -1,3 +1,4 @@ +# encoding: utf-8 import json import re import itertools @@ -10,6 +11,7 @@ from ..utils import ( clean_html, get_element_by_attribute, ExtractorError, + RegexNotFoundError, std_headers, unsmuggle_url, ) @@ -18,12 +20,12 @@ class VimeoIE(InfoExtractor): """Information extractor for vimeo.com.""" # _VALID_URL matches Vimeo URLs - _VALID_URL = r'(?P<proto>https?://)?(?:(?:www|player)\.)?vimeo(?P<pro>pro)?\.com/(?:(?:(?:groups|album)/[^/]+)|(?:.*?)/)?(?P<direct_link>play_redirect_hls\?clip_id=)?(?:videos?/)?(?P<id>[0-9]+)/?(?:[?].*)?$' + _VALID_URL = r'(?P<proto>https?://)?(?:(?:www|(?P<player>player))\.)?vimeo(?P<pro>pro)?\.com/(?:(?:(?:groups|album)/[^/]+)|(?:.*?)/)?(?P<direct_link>play_redirect_hls\?clip_id=)?(?:videos?/)?(?P<id>[0-9]+)/?(?:[?].*)?(?:#.*)?$' _NETRC_MACHINE = 'vimeo' IE_NAME = u'vimeo' _TESTS = [ { - u'url': u'http://vimeo.com/56015672', + u'url': u'http://vimeo.com/56015672#at=0', u'file': u'56015672.mp4', u'md5': u'8879b6cc097e987f02484baf890129e5', u'info_dict': { @@ -54,7 +56,22 @@ class VimeoIE(InfoExtractor): u'title': u'Kathy Sierra: Building the minimum Badass User, Business of Software', u'uploader': u'The BLN & Business of Software', }, - } + }, + { + u'url': u'http://vimeo.com/68375962', + u'file': u'68375962.mp4', + u'md5': u'aaf896bdb7ddd6476df50007a0ac0ae7', + u'note': u'Video protected with password', + u'info_dict': { + u'title': u'youtube-dl password protected test video', + u'upload_date': u'20130614', + u'uploader_id': u'user18948128', + u'uploader': u'Jaime Marquínez Ferrándiz', + }, + u'params': { + u'videopassword': u'youtube-dl', + }, + }, ] def _login(self): @@ -111,11 +128,9 @@ class VimeoIE(InfoExtractor): raise ExtractorError(u'Invalid URL: %s' % url) video_id = mobj.group('id') - if not mobj.group('proto'): - url = 'https://' + url - elif mobj.group('pro'): + if mobj.group('pro') or mobj.group('player'): url = 'http://player.vimeo.com/video/' + video_id - elif mobj.group('direct_link'): + else: url = 'https://vimeo.com/' + video_id # Retrieve video webpage to extract further information @@ -129,18 +144,26 @@ class VimeoIE(InfoExtractor): # Extract the config JSON try: - config = self._search_regex([r' = {config:({.+?}),assets:', r'c=({.+?);'], - webpage, u'info section', flags=re.DOTALL) - config = json.loads(config) - except: + try: + config_url = self._html_search_regex( + r' data-config-url="(.+?)"', webpage, u'config URL') + config_json = self._download_webpage(config_url, video_id) + config = json.loads(config_json) + except RegexNotFoundError: + # For pro videos or player.vimeo.com urls + config = self._search_regex([r' = {config:({.+?}),assets:', r'c=({.+?);'], + webpage, u'info section', flags=re.DOTALL) + config = json.loads(config) + except Exception as e: if re.search('The creator of this video has not given you permission to embed it on this domain.', webpage): raise ExtractorError(u'The author has restricted the access to this video, try with the "--referer" option') - if re.search('If so please provide the correct password.', webpage): + if re.search('<form[^>]+?id="pw_form"', webpage) is not None: self._verify_video_password(url, video_id, webpage) return self._real_extract(url) else: - raise ExtractorError(u'Unable to extract info section') + raise ExtractorError(u'Unable to extract info section', + cause=e) # Extract title video_title = config["video"]["title"] @@ -180,7 +203,7 @@ class VimeoIE(InfoExtractor): # Vimeo specific: extract video codec and quality information # First consider quality, then codecs, then take everything codecs = [('vp6', 'flv'), ('vp8', 'flv'), ('h264', 'mp4')] - files = { 'hd': [], 'sd': [], 'other': []} + files = {'hd': [], 'sd': [], 'other': []} config_files = config["video"].get("files") or config["request"].get("files") for codec_name, codec_extension in codecs: for quality in config_files.get(codec_name, []): @@ -209,7 +232,7 @@ class VimeoIE(InfoExtractor): if len(formats) == 0: raise ExtractorError(u'No known codec found') - return [{ + return { 'id': video_id, 'uploader': video_uploader, 'uploader_id': video_uploader_id, @@ -218,7 +241,8 @@ class VimeoIE(InfoExtractor): 'thumbnail': video_thumbnail, 'description': video_description, 'formats': formats, - }] + 'webpage_url': url, + } class VimeoChannelIE(InfoExtractor): diff --git a/youtube_dl/extractor/vk.py b/youtube_dl/extractor/vk.py new file mode 100644 index 0000000..90d8a6d --- /dev/null +++ b/youtube_dl/extractor/vk.py @@ -0,0 +1,45 @@ +# encoding: utf-8 +import re +import json + +from .common import InfoExtractor +from ..utils import ( + compat_str, + unescapeHTML, +) + + +class VKIE(InfoExtractor): + IE_NAME = u'vk.com' + _VALID_URL = r'https?://vk\.com/(?:videos.*?\?.*?z=)?video(?P<id>.*?)(?:\?|%2F|$)' + + _TEST = { + u'url': u'http://vk.com/videos-77521?z=video-77521_162222515%2Fclub77521', + u'md5': u'0deae91935c54e00003c2a00646315f0', + u'info_dict': { + u'id': u'162222515', + u'ext': u'flv', + u'title': u'ProtivoGunz - Хуёвая песня', + u'uploader': u'Noize MC', + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + info_url = 'http://vk.com/al_video.php?act=show&al=1&video=%s' % video_id + info_page = self._download_webpage(info_url, video_id) + m_yt = re.search(r'src="(http://www.youtube.com/.*?)"', info_page) + if m_yt is not None: + self.to_screen(u'Youtube video detected') + return self.url_result(m_yt.group(1), 'Youtube') + vars_json = self._search_regex(r'var vars = ({.*?});', info_page, u'vars') + vars = json.loads(vars_json) + + return { + 'id': compat_str(vars['vid']), + 'url': vars['url240'], + 'title': unescapeHTML(vars['md_title']), + 'thumbnail': vars['jpg'], + 'uploader': vars['md_author'], + } diff --git a/youtube_dl/extractor/weibo.py b/youtube_dl/extractor/weibo.py index 0757495..fa784ab 100644 --- a/youtube_dl/extractor/weibo.py +++ b/youtube_dl/extractor/weibo.py @@ -13,6 +13,7 @@ class WeiboIE(InfoExtractor): _VALID_URL = r'https?://video\.weibo\.com/v/weishipin/t_(?P<id>.+?)\.htm' _TEST = { + u'add_ie': ['Sina'], u'url': u'http://video.weibo.com/v/weishipin/t_zjUw2kZ.htm', u'file': u'98322879.flv', u'info_dict': { diff --git a/youtube_dl/extractor/xhamster.py b/youtube_dl/extractor/xhamster.py index 81c4be3..7444d33 100644 --- a/youtube_dl/extractor/xhamster.py +++ b/youtube_dl/extractor/xhamster.py @@ -36,21 +36,25 @@ class XHamsterIE(InfoExtractor): }] def _real_extract(self,url): + def extract_video_url(webpage): + mobj = re.search(r'\'srv\': \'(?P<server>[^\']*)\',\s*\'file\': \'(?P<file>[^\']+)\',', webpage) + if mobj is None: + raise ExtractorError(u'Unable to extract media URL') + if len(mobj.group('server')) == 0: + return compat_urllib_parse.unquote(mobj.group('file')) + else: + return mobj.group('server')+'/key='+mobj.group('file') + + def is_hd(webpage): + return webpage.find('<div class=\'icon iconHD\'>') != -1 + mobj = re.match(self._VALID_URL, url) video_id = mobj.group('id') seo = mobj.group('seo') - mrss_url = 'http://xhamster.com/movies/%s/%s.html?hd' % (video_id, seo) + mrss_url = 'http://xhamster.com/movies/%s/%s.html' % (video_id, seo) webpage = self._download_webpage(mrss_url, video_id) - mobj = re.search(r'\'srv\': \'(?P<server>[^\']*)\',\s*\'file\': \'(?P<file>[^\']+)\',', webpage) - if mobj is None: - raise ExtractorError(u'Unable to extract media URL') - if len(mobj.group('server')) == 0: - video_url = compat_urllib_parse.unquote(mobj.group('file')) - else: - video_url = mobj.group('server')+'/key='+mobj.group('file') - video_title = self._html_search_regex(r'<title>(?P<title>.+?) - xHamster\.com', webpage, u'title') @@ -76,14 +80,32 @@ class XHamsterIE(InfoExtractor): age_limit = self._rta_search(webpage) - return [{ - 'id': video_id, - 'url': video_url, - 'ext': determine_ext(video_url), - 'title': video_title, + video_url = extract_video_url(webpage) + hd = is_hd(webpage) + formats = [{ + 'url': video_url, + 'ext': determine_ext(video_url), + 'format': 'hd' if hd else 'sd', + 'format_id': 'hd' if hd else 'sd', + }] + if not hd: + webpage = self._download_webpage(mrss_url+'?hd', video_id) + if is_hd(webpage): + video_url = extract_video_url(webpage) + formats.append({ + 'url': video_url, + 'ext': determine_ext(video_url), + 'format': 'hd', + 'format_id': 'hd', + }) + + return { + 'id': video_id, + 'title': video_title, + 'formats': formats, 'description': video_description, 'upload_date': video_upload_date, 'uploader_id': video_uploader_id, 'thumbnail': video_thumbnail, 'age_limit': age_limit, - }] + } diff --git a/youtube_dl/extractor/xnxx.py b/youtube_dl/extractor/xnxx.py index 8a0eb1a..1177a4b 100644 --- a/youtube_dl/extractor/xnxx.py +++ b/youtube_dl/extractor/xnxx.py @@ -9,7 +9,7 @@ from ..utils import ( class XNXXIE(InfoExtractor): - _VALID_URL = r'^(?:https?://)?video\.xnxx\.com/video([0-9]+)/(.*)' + _VALID_URL = r'^(?:https?://)?(?:video|www)\.xnxx\.com/video([0-9]+)/(.*)' VIDEO_URL_RE = r'flv_url=(.*?)&' VIDEO_TITLE_RE = r'(.*?)\s+-\s+XNXX.COM' VIDEO_THUMB_RE = r'url_bigthumb=(.*?)&' diff --git a/youtube_dl/extractor/xtube.py b/youtube_dl/extractor/xtube.py new file mode 100644 index 0000000..03ad88b --- /dev/null +++ b/youtube_dl/extractor/xtube.py @@ -0,0 +1,55 @@ +import os +import re + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse_urlparse, + compat_urllib_request, + compat_urllib_parse, +) + +class XTubeIE(InfoExtractor): + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>xtube\.com/watch\.php\?v=(?P<videoid>[^/?&]+))' + _TEST = { + u'url': u'http://www.xtube.com/watch.php?v=kVTUy_G222_', + u'file': u'kVTUy_G222_.mp4', + u'md5': u'092fbdd3cbe292c920ef6fc6a8a9cdab', + u'info_dict': { + u"title": u"strange erotica", + u"description": u"surreal gay themed erotica...almost an ET kind of thing", + u"uploader": u"greenshowers", + u"age_limit": 18, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') + + req = compat_urllib_request.Request(url) + req.add_header('Cookie', 'age_verified=1') + webpage = self._download_webpage(req, video_id) + + video_title = self._html_search_regex(r'<div class="p_5px[^>]*>([^<]+)', webpage, u'title') + video_uploader = self._html_search_regex(r'so_s\.addVariable\("owner_u", "([^"]+)', webpage, u'uploader', fatal=False) + video_description = self._html_search_regex(r'<p class="video_description">([^<]+)', webpage, u'description', default=None) + video_url= self._html_search_regex(r'var videoMp4 = "([^"]+)', webpage, u'video_url').replace('\\/', '/') + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] + format = path.split('/')[5].split('_')[:2] + format[0] += 'p' + format[1] += 'k' + format = "-".join(format) + + return { + 'id': video_id, + 'title': video_title, + 'uploader': video_uploader, + 'description': video_description, + 'url': video_url, + 'ext': extension, + 'format': format, + 'format_id': format, + 'age_limit': 18, + } diff --git a/youtube_dl/extractor/yahoo.py b/youtube_dl/extractor/yahoo.py index 464b498..34e6afb 100644 --- a/youtube_dl/extractor/yahoo.py +++ b/youtube_dl/extractor/yahoo.py @@ -132,7 +132,7 @@ class YahooSearchIE(SearchInfoExtractor): mobj = re.search(r'(?P<url>screen\.yahoo\.com/.*?-\d*?\.html)"', r) e = self.url_result('http://' + mobj.group('url'), 'Yahoo') res['entries'].append(e) - if (pagenum * 30 +i >= n) or (m[u'last'] >= (m[u'total'] -1 )): + if (pagenum * 30 +i >= n) or (m[u'last'] >= (m[u'total'] -1)): break return res diff --git a/youtube_dl/extractor/youjizz.py b/youtube_dl/extractor/youjizz.py index 1265639..1fcc518 100644 --- a/youtube_dl/extractor/youjizz.py +++ b/youtube_dl/extractor/youjizz.py @@ -13,7 +13,8 @@ class YouJizzIE(InfoExtractor): u'file': u'2189178.flv', u'md5': u'07e15fa469ba384c7693fd246905547c', u'info_dict': { - u"title": u"Zeichentrick 1" + u"title": u"Zeichentrick 1", + u"age_limit": 18, } } @@ -25,6 +26,8 @@ class YouJizzIE(InfoExtractor): # Get webpage content webpage = self._download_webpage(url, video_id) + age_limit = self._rta_search(webpage) + # Get the video title video_title = self._html_search_regex(r'<title>(?P<title>.*)', webpage, u'title').strip() @@ -60,6 +63,7 @@ class YouJizzIE(InfoExtractor): 'title': video_title, 'ext': 'flv', 'format': 'flv', - 'player_url': embed_page_url} + 'player_url': embed_page_url, + 'age_limit': age_limit} return [info] diff --git a/youtube_dl/extractor/youku.py b/youtube_dl/extractor/youku.py index 9d88c17..a8fd40c 100644 --- a/youtube_dl/extractor/youku.py +++ b/youtube_dl/extractor/youku.py @@ -18,7 +18,7 @@ class YoukuIE(InfoExtractor): u"url": u"http://v.youku.com/v_show/id_XNDgyMDQ2NTQw.html", u"file": u"XNDgyMDQ2NTQw_part00.flv", u"md5": u"ffe3f2e435663dc2d1eea34faeff5b5b", - u"params": { u"test": False }, + u"params": {u"test": False}, u"info_dict": { u"title": u"youtube-dl test video \"'/\\ä↭𝕐" } @@ -37,8 +37,8 @@ class YoukuIE(InfoExtractor): source = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\:._-1234567890") seed = float(seed) for i in range(len(source)): - seed = (seed * 211 + 30031 ) % 65536 - index = math.floor(seed / 65536 * len(source) ) + seed = (seed * 211 + 30031) % 65536 + index = math.floor(seed / 65536 * len(source)) mixed.append(source[int(index)]) source.remove(source[int(index)]) #return ''.join(mixed) diff --git a/youtube_dl/extractor/youporn.py b/youtube_dl/extractor/youporn.py index e3b56ce..bd0f2ca 100644 --- a/youtube_dl/extractor/youporn.py +++ b/youtube_dl/extractor/youporn.py @@ -17,7 +17,7 @@ from ..aes import ( ) class YouPornIE(InfoExtractor): - _VALID_URL = r'^(?:https?://)?(?:\w+\.)?youporn\.com/watch/(?P[0-9]+)/(?P[^/]+)' + _VALID_URL = r'^(?:https?://)?(?:www\.)?(?P<url>youporn\.com/watch/(?P<videoid>[0-9]+)/(?P<title>[^/]+))' _TEST = { u'url': u'http://www.youporn.com/watch/505835/sex-ed-is-it-safe-to-masturbate-daily/', u'file': u'505835.mp4', @@ -31,23 +31,10 @@ class YouPornIE(InfoExtractor): } } - def _print_formats(self, formats): - """Print all available formats""" - print(u'Available formats:') - print(u'ext\t\tformat') - print(u'---------------------------------') - for format in formats: - print(u'%s\t\t%s' % (format['ext'], format['format'])) - - def _specific(self, req_format, formats): - for x in formats: - if x["format"] == req_format: - return x - return None - def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) video_id = mobj.group('videoid') + url = 'http://www.' + mobj.group('url') req = compat_urllib_request.Request(url) req.add_header('Cookie', 'age_verified=1') @@ -71,27 +58,22 @@ class YouPornIE(InfoExtractor): except KeyError: raise ExtractorError('Missing JSON parameter: ' + sys.exc_info()[1]) - # Get all of the formats available + # Get all of the links from the page DOWNLOAD_LIST_RE = r'(?s)<ul class="downloadList">(?P<download_list>.*?)</ul>' download_list_html = self._search_regex(DOWNLOAD_LIST_RE, webpage, u'download list').strip() - - # Get all of the links from the page - LINK_RE = r'(?s)<a href="(?P<url>[^"]+)">' + LINK_RE = r'<a href="([^"]+)">' links = re.findall(LINK_RE, download_list_html) - - # Get link of hd video if available - mobj = re.search(r'var encryptedQuality720URL = \'(?P<encrypted_video_url>[a-zA-Z0-9+/]+={0,2})\';', webpage) - if mobj != None: - encrypted_video_url = mobj.group(u'encrypted_video_url') - video_url = aes_decrypt_text(encrypted_video_url, video_title, 32).decode('utf-8') - links = [video_url] + links + + # Get all encrypted links + encrypted_links = re.findall(r'var encryptedQuality[0-9]{3}URL = \'([a-zA-Z0-9+/]+={0,2})\';', webpage) + for encrypted_link in encrypted_links: + link = aes_decrypt_text(encrypted_link, video_title, 32).decode('utf-8') + links.append(link) if not links: raise ExtractorError(u'ERROR: no known formats available for video') - self.to_screen(u'Links found: %d' % len(links)) - formats = [] for link in links: @@ -99,43 +81,36 @@ class YouPornIE(InfoExtractor): # http://cdn1.download.youporn.phncdn.com/201210/31/8004515/480p_370k_8004515/YouPorn%20-%20Nubile%20Films%20The%20Pillow%20Fight.mp4?nvb=20121113051249&nva=20121114051249&ir=1200&sr=1200&hash=014b882080310e95fb6a0 # A path looks like this: # /201210/31/8004515/480p_370k_8004515/YouPorn%20-%20Nubile%20Films%20The%20Pillow%20Fight.mp4 - video_url = unescapeHTML( link ) - path = compat_urllib_parse_urlparse( video_url ).path - extension = os.path.splitext( path )[1][1:] + video_url = unescapeHTML(link) + path = compat_urllib_parse_urlparse(video_url).path + extension = os.path.splitext(path)[1][1:] format = path.split('/')[4].split('_')[:2] + # size = format[0] # bitrate = format[1] - format = "-".join( format ) + format = "-".join(format) # title = u'%s-%s-%s' % (video_title, size, bitrate) formats.append({ - 'id': video_id, 'url': video_url, - 'uploader': video_uploader, - 'upload_date': upload_date, - 'title': video_title, 'ext': extension, 'format': format, - 'thumbnail': thumbnail, - 'description': video_description, - 'age_limit': age_limit, + 'format_id': format, }) - if self._downloader.params.get('listformats', None): - self._print_formats(formats) - return - - req_format = self._downloader.params.get('format', 'best') - self.to_screen(u'Format: %s' % req_format) - - if req_format is None or req_format == 'best': - return [formats[0]] - elif req_format == 'worst': - return [formats[-1]] - elif req_format in ('-1', 'all'): - return formats - else: - format = self._specific( req_format, formats ) - if format is None: - raise ExtractorError(u'Requested format not available') - return [format] + # Sort and remove doubles + formats.sort(key=lambda format: list(map(lambda s: s.zfill(6), format['format'].split('-')))) + for i in range(len(formats)-1,0,-1): + if formats[i]['format_id'] == formats[i-1]['format_id']: + del formats[i] + + return { + 'id': video_id, + 'uploader': video_uploader, + 'upload_date': upload_date, + 'title': video_title, + 'thumbnail': thumbnail, + 'description': video_description, + 'age_limit': age_limit, + 'formats': formats, + } diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index a88cba2..c992cba 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -74,14 +74,8 @@ class YoutubeBaseInfoExtractor(InfoExtractor): self._downloader.report_warning(u'unable to fetch login page: %s' % compat_str(err)) return False - galx = None - dsh = None - match = re.search(re.compile(r'<input.+?name="GALX".+?value="(.+?)"', re.DOTALL), login_page) - if match: - galx = match.group(1) - match = re.search(re.compile(r'<input.+?name="dsh".+?value="(.+?)"', re.DOTALL), login_page) - if match: - dsh = match.group(1) + galx = self._search_regex(r'(?s)<input.+?name="GALX".+?value="(.+?)"', + login_page, u'Login GALX parameter') # Log in login_form_strs = { @@ -95,7 +89,6 @@ class YoutubeBaseInfoExtractor(InfoExtractor): u'checkConnection': u'', u'checkedDomains': u'youtube', u'dnConn': u'', - u'dsh': dsh, u'pstMsg': u'0', u'rmShown': u'1', u'secTok': u'', @@ -236,11 +229,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): '136': 'mp4', '137': 'mp4', '138': 'mp4', - '139': 'mp4', - '140': 'mp4', - '141': 'mp4', '160': 'mp4', + # Dash mp4 audio + '139': 'm4a', + '140': 'm4a', + '141': 'm4a', + # Dash webm '171': 'webm', '172': 'webm', @@ -344,18 +339,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): u"description": u"test chars: \"'/\\ä↭𝕐\n\nThis is a test video for youtube-dl.\n\nFor more information, contact phihag@phihag.de ." } }, - { - u"url": u"http://www.youtube.com/watch?v=1ltcDfZMA3U", - u"file": u"1ltcDfZMA3U.flv", - u"note": u"Test VEVO video (#897)", - u"info_dict": { - u"upload_date": u"20070518", - u"title": u"Maps - It Will Find You", - u"description": u"Music video by Maps performing It Will Find You.", - u"uploader": u"MuteUSA", - u"uploader_id": u"MuteUSA" - } - }, { u"url": u"http://www.youtube.com/watch?v=UxxajLWwzqY", u"file": u"UxxajLWwzqY.mp4", @@ -1099,7 +1082,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): else: raise ExtractorError(u'Unable to decrypt signature, key length %d not supported; retrying might work' % (len(s))) - def _get_available_subtitles(self, video_id): + def _get_available_subtitles(self, video_id, webpage): try: sub_list = self._download_webpage( 'http://video.google.com/timedtext?hl=en&type=list&v=%s' % video_id, @@ -1116,7 +1099,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): 'lang': lang, 'v': video_id, 'fmt': self._downloader.params.get('subtitlesformat'), - 'name': l[0], + 'name': l[0].encode('utf-8'), }) url = u'http://www.youtube.com/api/timedtext?' + params sub_lang_list[lang] = url @@ -1403,32 +1386,29 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): # this signatures are encrypted if 'url_encoded_fmt_stream_map' not in args: raise ValueError(u'No stream_map present') # caught below - m_s = re.search(r'[&,]s=', args['url_encoded_fmt_stream_map']) + re_signature = re.compile(r'[&,]s=') + m_s = re_signature.search(args['url_encoded_fmt_stream_map']) if m_s is not None: self.to_screen(u'%s: Encrypted signatures detected.' % video_id) video_info['url_encoded_fmt_stream_map'] = [args['url_encoded_fmt_stream_map']] - m_s = re.search(r'[&,]s=', args.get('adaptive_fmts', u'')) + m_s = re_signature.search(args.get('adaptive_fmts', u'')) if m_s is not None: - if 'url_encoded_fmt_stream_map' in video_info: - video_info['url_encoded_fmt_stream_map'][0] += ',' + args['adaptive_fmts'] - else: - video_info['url_encoded_fmt_stream_map'] = [args['adaptive_fmts']] - elif 'adaptive_fmts' in video_info: - if 'url_encoded_fmt_stream_map' in video_info: - video_info['url_encoded_fmt_stream_map'][0] += ',' + video_info['adaptive_fmts'][0] + if 'adaptive_fmts' in video_info: + video_info['adaptive_fmts'][0] += ',' + args['adaptive_fmts'] else: - video_info['url_encoded_fmt_stream_map'] = video_info['adaptive_fmts'] + video_info['adaptive_fmts'] = [args['adaptive_fmts']] except ValueError: pass if 'conn' in video_info and video_info['conn'][0].startswith('rtmp'): self.report_rtmp_download() video_url_list = [(None, video_info['conn'][0])] - elif 'url_encoded_fmt_stream_map' in video_info and len(video_info['url_encoded_fmt_stream_map']) >= 1: - if 'rtmpe%3Dyes' in video_info['url_encoded_fmt_stream_map'][0]: + elif len(video_info.get('url_encoded_fmt_stream_map', [])) >= 1 or len(video_info.get('adaptive_fmts', [])) >= 1: + encoded_url_map = video_info.get('url_encoded_fmt_stream_map', [''])[0] + ',' + video_info.get('adaptive_fmts',[''])[0] + if 'rtmpe%3Dyes' in encoded_url_map: raise ExtractorError('rtmpe downloads are not supported, see https://github.com/rg3/youtube-dl/issues/343 for more information.', expected=True) url_map = {} - for url_data_str in video_info['url_encoded_fmt_stream_map'][0].split(','): + for url_data_str in encoded_url_map.split(','): url_data = compat_parse_qs(url_data_str) if 'itag' in url_data and 'url' in url_data: url = url_data['url'][0] @@ -1481,13 +1461,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): raise ExtractorError(u'no conn, hlsvp or url_encoded_fmt_stream_map information found in video info') results = [] - for format_param, video_real_url in video_url_list: + for itag, video_real_url in video_url_list: # Extension - video_extension = self._video_extensions.get(format_param, 'flv') + video_extension = self._video_extensions.get(itag, 'flv') - video_format = '{0} - {1}{2}'.format(format_param if format_param else video_extension, - self._video_dimensions.get(format_param, '???'), - ' ('+self._special_itags[format_param]+')' if format_param in self._special_itags else '') + video_format = '{0} - {1}{2}'.format(itag if itag else video_extension, + self._video_dimensions.get(itag, '???'), + ' ('+self._special_itags[itag]+')' if itag in self._special_itags else '') results.append({ 'id': video_id, @@ -1498,13 +1478,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): 'title': video_title, 'ext': video_extension, 'format': video_format, + 'format_id': itag, 'thumbnail': video_thumbnail, 'description': video_description, 'player_url': player_url, 'subtitles': video_subtitles, 'duration': video_duration, 'age_limit': 18 if age_gate else 0, - 'annotations': video_annotations + 'annotations': video_annotations, + 'webpage_url': 'https://www.youtube.com/watch?v=%s' % video_id, }) return results @@ -1590,7 +1572,6 @@ class YoutubePlaylistIE(InfoExtractor): class YoutubeChannelIE(InfoExtractor): IE_DESC = u'YouTube.com channels' _VALID_URL = r"^(?:https?://)?(?:youtu\.be|(?:\w+\.)?youtube(?:-nocookie)?\.com)/channel/([0-9A-Za-z_-]+)" - _TEMPLATE_URL = 'http://www.youtube.com/channel/%s/videos?sort=da&flow=list&view=0&page=%s&gl=US&hl=en' _MORE_PAGES_INDICATOR = 'yt-uix-load-more' _MORE_PAGES_URL = 'http://www.youtube.com/c4_browse_ajax?action_load_more_videos=1&flow=list&paging=%s&view=0&sort=da&channel_id=%s' IE_NAME = u'youtube:channel' @@ -1611,30 +1592,20 @@ class YoutubeChannelIE(InfoExtractor): # Download channel page channel_id = mobj.group(1) video_ids = [] - pagenum = 1 - url = self._TEMPLATE_URL % (channel_id, pagenum) - page = self._download_webpage(url, channel_id, - u'Downloading page #%s' % pagenum) + # Download all channel pages using the json-based channel_ajax query + for pagenum in itertools.count(1): + url = self._MORE_PAGES_URL % (pagenum, channel_id) + page = self._download_webpage(url, channel_id, + u'Downloading page #%s' % pagenum) - # Extract video identifiers - ids_in_page = self.extract_videos_from_page(page) - video_ids.extend(ids_in_page) + page = json.loads(page) - # Download any subsequent channel pages using the json-based channel_ajax query - if self._MORE_PAGES_INDICATOR in page: - for pagenum in itertools.count(1): - url = self._MORE_PAGES_URL % (pagenum, channel_id) - page = self._download_webpage(url, channel_id, - u'Downloading page #%s' % pagenum) - - page = json.loads(page) - - ids_in_page = self.extract_videos_from_page(page['content_html']) - video_ids.extend(ids_in_page) + ids_in_page = self.extract_videos_from_page(page['content_html']) + video_ids.extend(ids_in_page) - if self._MORE_PAGES_INDICATOR not in page['load_more_widget_html']: - break + if self._MORE_PAGES_INDICATOR not in page['load_more_widget_html']: + break self._downloader.to_screen(u'[youtube] Channel %s: Found %i videos' % (channel_id, len(video_ids))) @@ -1750,6 +1721,10 @@ class YoutubeSearchIE(SearchInfoExtractor): videos = [self.url_result('http://www.youtube.com/watch?v=%s' % id, 'Youtube') for id in video_ids] return self.playlist_result(videos, query) +class YoutubeSearchDateIE(YoutubeSearchIE): + _API_URL = 'https://gdata.youtube.com/feeds/api/videos?q=%s&start-index=%i&max-results=50&v=2&alt=jsonc&orderby=published' + _SEARCH_KEY = 'ytsearchdate' + IE_DESC = u'YouTube.com searches, newest videos first' class YoutubeShowIE(InfoExtractor): IE_DESC = u'YouTube.com (multi-season) shows' diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index bfb8f6b..1d97853 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -572,6 +572,11 @@ class ExtractorError(Exception): return u''.join(traceback.format_tb(self.traceback)) +class RegexNotFoundError(ExtractorError): + """Error when a regex didn't match""" + pass + + class DownloadError(Exception): """Download Error exception. diff --git a/youtube_dl/version.py b/youtube_dl/version.py index a5b56d8..338e7ba 100644 --- a/youtube_dl/version.py +++ b/youtube_dl/version.py @@ -1,2 +1,2 @@ -__version__ = '2013.10.23' +__version__ = '2013.11.11' -- 2.41.1