From b238854ce845f3796daac74edab2e8a373e8ba1a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rog=C3=A9rio=20Brito?= Date: Thu, 5 Dec 2013 01:41:08 -0200 Subject: [PATCH] Imported Upstream version 2013.12.04 --- README.md | 17 +- README.txt | 19 +- devscripts/bash-completion.in | 15 +- setup.py | 2 +- test/helper.py | 4 - test/parameters.json | 3 +- test/test_age_restriction.py | 5 +- test/test_all_urls.py | 13 +- test/test_download.py | 9 +- test/test_playlists.py | 43 ++- test/test_subtitles.py | 5 +- test/test_utils.py | 6 + test/test_write_annotations.py | 3 +- test/test_write_info_json.py | 5 +- test/test_youtube_lists.py | 32 +- test/test_youtube_signature.py | 3 - youtube-dl | Bin 261436 -> 283113 bytes youtube-dl.1 | 18 +- youtube-dl.bash-completion | 17 +- youtube_dl/FileDownloader.py | 127 +++++--- youtube_dl/PostProcessor.py | 2 +- youtube_dl/YoutubeDL.py | 298 ++++++++++++++++--- youtube_dl/__init__.py | 222 ++++++-------- youtube_dl/extractor/__init__.py | 41 ++- youtube_dl/extractor/anitube.py | 53 ++++ youtube_dl/extractor/appletrailers.py | 9 +- youtube_dl/extractor/archiveorg.py | 11 +- youtube_dl/extractor/arte.py | 11 +- youtube_dl/extractor/auengine.py | 31 +- youtube_dl/extractor/bambuser.py | 8 +- youtube_dl/extractor/bandcamp.py | 70 ++++- youtube_dl/extractor/brightcove.py | 18 +- youtube_dl/extractor/canalplus.py | 7 +- youtube_dl/extractor/clipfish.py | 58 ++++ youtube_dl/extractor/clipsyndicate.py | 52 ++++ youtube_dl/extractor/cnn.py | 4 +- youtube_dl/extractor/collegehumor.py | 11 +- youtube_dl/extractor/comedycentral.py | 43 ++- youtube_dl/extractor/common.py | 71 ++++- youtube_dl/extractor/d8.py | 22 ++ youtube_dl/extractor/dailymotion.py | 2 +- youtube_dl/extractor/daum.py | 18 +- youtube_dl/extractor/dreisat.py | 11 +- youtube_dl/extractor/ebaumsworld.py | 4 +- youtube_dl/extractor/eighttracks.py | 1 - youtube_dl/extractor/escapist.py | 66 ++-- youtube_dl/extractor/facebook.py | 1 - youtube_dl/extractor/faz.py | 9 +- youtube_dl/extractor/fktv.py | 1 - youtube_dl/extractor/francetv.py | 4 +- youtube_dl/extractor/gamekings.py | 38 +++ youtube_dl/extractor/gamespot.py | 7 +- youtube_dl/extractor/gametrailers.py | 16 +- youtube_dl/extractor/generic.py | 51 ++-- youtube_dl/extractor/howcast.py | 2 +- youtube_dl/extractor/imdb.py | 59 ++++ youtube_dl/extractor/internetvideoarchive.py | 7 +- youtube_dl/extractor/jeuxvideo.py | 10 +- youtube_dl/extractor/justintv.py | 4 +- youtube_dl/extractor/livestream.py | 44 ++- youtube_dl/extractor/metacritic.py | 5 +- youtube_dl/extractor/mixcloud.py | 2 +- youtube_dl/extractor/mtv.py | 82 ++--- youtube_dl/extractor/myspass.py | 4 +- youtube_dl/extractor/naver.py | 12 +- youtube_dl/extractor/nbc.py | 5 +- youtube_dl/extractor/nhl.py | 6 +- youtube_dl/extractor/niconico.py | 127 ++++++++ youtube_dl/extractor/podomatic.py | 49 +++ youtube_dl/extractor/pornhub.py | 1 - youtube_dl/extractor/redtube.py | 6 +- youtube_dl/extractor/rtlnow.py | 12 - youtube_dl/extractor/sina.py | 4 +- youtube_dl/extractor/smotri.py | 252 ++++++++++++++++ youtube_dl/extractor/soundcloud.py | 77 +++-- youtube_dl/extractor/southparkstudios.py | 36 ++- youtube_dl/extractor/spankwire.py | 12 +- youtube_dl/extractor/spiegel.py | 52 ++-- youtube_dl/extractor/streamcloud.py | 66 ++++ youtube_dl/extractor/sztvhu.py | 3 +- youtube_dl/extractor/teamcoco.py | 6 +- youtube_dl/extractor/ted.py | 41 ++- youtube_dl/extractor/toutv.py | 71 +++++ youtube_dl/extractor/trilulilu.py | 11 +- youtube_dl/extractor/tube8.py | 2 - youtube_dl/extractor/tvp.py | 42 +++ youtube_dl/extractor/vevo.py | 7 +- youtube_dl/extractor/viddler.py | 8 +- youtube_dl/extractor/videofyme.py | 4 +- youtube_dl/extractor/videopremium.py | 16 +- youtube_dl/extractor/viki.py | 101 +++++++ youtube_dl/extractor/vimeo.py | 43 ++- youtube_dl/extractor/vine.py | 2 +- youtube_dl/extractor/xhamster.py | 2 +- youtube_dl/extractor/xtube.py | 1 - youtube_dl/extractor/yahoo.py | 52 +++- youtube_dl/extractor/youtube.py | 224 +++++++++----- youtube_dl/extractor/zdf.py | 140 ++++++--- youtube_dl/update.py | 24 +- youtube_dl/utils.py | 61 +++- youtube_dl/version.py | 2 +- 101 files changed, 2551 insertions(+), 865 deletions(-) create mode 100644 youtube_dl/extractor/anitube.py create mode 100644 youtube_dl/extractor/clipfish.py create mode 100644 youtube_dl/extractor/clipsyndicate.py create mode 100644 youtube_dl/extractor/d8.py create mode 100644 youtube_dl/extractor/gamekings.py create mode 100644 youtube_dl/extractor/imdb.py create mode 100644 youtube_dl/extractor/niconico.py create mode 100644 youtube_dl/extractor/podomatic.py create mode 100644 youtube_dl/extractor/smotri.py create mode 100644 youtube_dl/extractor/streamcloud.py create mode 100644 youtube_dl/extractor/toutv.py create mode 100644 youtube_dl/extractor/tvp.py create mode 100644 youtube_dl/extractor/viki.py diff --git a/README.md b/README.md index 6632e58..029c418 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ which means you can modify it, redistribute it or use it however you like. --list-extractors List all supported extractors and the URLs they would handle --extractor-descriptions Output descriptions of all supported extractors - --proxy URL Use the specified HTTP/HTTPS proxy + --proxy URL Use the specified HTTP/HTTPS proxy. Pass in an + empty string (--proxy "") for direct connection --no-check-certificate Suppress HTTPS certificate validation. --cache-dir DIR Location in the filesystem where youtube-dl can store downloaded information permanently. By @@ -55,8 +56,9 @@ which means you can modify it, redistribute it or use it however you like. --dateafter DATE download only videos uploaded after this date --no-playlist download only the currently playing video --age-limit YEARS download only videos suitable for the given age - --download-archive FILE Download only videos not present in the archive - file. Record all downloaded videos in it. + --download-archive FILE Download only videos not listed in the archive + file. Record the IDs of all downloaded videos in + it. ## Download Options: -r, --rate-limit LIMIT maximum download rate in bytes per second (e.g. @@ -123,17 +125,18 @@ which means you can modify it, redistribute it or use it however you like. --get-description simulate, quiet but print video description --get-filename simulate, quiet but print output filename --get-format simulate, quiet but print output format + -j, --dump-json simulate, quiet but print JSON information --newline output progress bar as new lines --no-progress do not print progress bar --console-title display progress in console titlebar -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 + --write-pages Write downloaded intermediary pages to files in + the current directory to debug problems ## Video Format Options: - -f, --format FORMAT video format code, specifiy the order of + -f, --format FORMAT video format code, specify the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported --all-formats download all available video formats @@ -181,7 +184,7 @@ which means you can modify it, redistribute it or use it however you like. # CONFIGURATION -You can configure youtube-dl by placing default arguments (such as `--extract-audio --no-mtime` to always extract the audio and not copy the mtime) into `/etc/youtube-dl.conf` and/or `~/.config/youtube-dl.conf`. +You can configure youtube-dl by placing default arguments (such as `--extract-audio --no-mtime` to always extract the audio and not copy the mtime) into `/etc/youtube-dl.conf` and/or `~/.config/youtube-dl.conf`. On Windows, the configuration file locations are `%APPDATA%\youtube-dl\config.txt` and `C:\Users\\youtube-dl.conf`. # OUTPUT TEMPLATE diff --git a/README.txt b/README.txt index 4b400fd..fc9f17e 100644 --- a/README.txt +++ b/README.txt @@ -37,7 +37,8 @@ OPTIONS --list-extractors List all supported extractors and the URLs they would handle --extractor-descriptions Output descriptions of all supported extractors - --proxy URL Use the specified HTTP/HTTPS proxy + --proxy URL Use the specified HTTP/HTTPS proxy. Pass in an + empty string (--proxy "") for direct connection --no-check-certificate Suppress HTTPS certificate validation. --cache-dir DIR Location in the filesystem where youtube-dl can store downloaded information permanently. By @@ -64,8 +65,9 @@ Video Selection: --dateafter DATE download only videos uploaded after this date --no-playlist download only the currently playing video --age-limit YEARS download only videos suitable for the given age - --download-archive FILE Download only videos not present in the archive - file. Record all downloaded videos in it. + --download-archive FILE Download only videos not listed in the archive + file. Record the IDs of all downloaded videos in + it. Download Options: ----------------- @@ -138,19 +140,20 @@ Verbosity / Simulation Options: --get-description simulate, quiet but print video description --get-filename simulate, quiet but print output filename --get-format simulate, quiet but print output format + -j, --dump-json simulate, quiet but print JSON information --newline output progress bar as new lines --no-progress do not print progress bar --console-title display progress in console titlebar -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 + --write-pages Write downloaded intermediary pages to files in + the current directory to debug problems Video Format Options: --------------------- - -f, --format FORMAT video format code, specifiy the order of + -f, --format FORMAT video format code, specify the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported --all-formats download all available video formats @@ -207,7 +210,9 @@ CONFIGURATION You can configure youtube-dl by placing default arguments (such as --extract-audio --no-mtime to always extract the audio and not copy the -mtime) into /etc/youtube-dl.conf and/or ~/.config/youtube-dl.conf. +mtime) into /etc/youtube-dl.conf and/or ~/.config/youtube-dl.conf. On +Windows, the configuration file locations are +%APPDATA%\youtube-dl\config.txt and C:\Users\\youtube-dl.conf. OUTPUT TEMPLATE =============== diff --git a/devscripts/bash-completion.in b/devscripts/bash-completion.in index ce893fc..3af87a3 100644 --- a/devscripts/bash-completion.in +++ b/devscripts/bash-completion.in @@ -1,10 +1,21 @@ __youtube_dl() { - local cur prev opts + local cur prev opts fileopts diropts keywords COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" opts="{{flags}}" - keywords=":ytfavorites :ytrecommended :ytsubscriptions :ytwatchlater" + keywords=":ytfavorites :ytrecommended :ytsubscriptions :ytwatchlater :ythistory" + fileopts="-a|--batch-file|--download-archive|--cookies" + diropts="--cache-dir" + + if [[ ${prev} =~ ${fileopts} ]]; then + COMPREPLY=( $(compgen -f -- ${cur}) ) + return 0 + elif [[ ${prev} =~ ${diropts} ]]; then + COMPREPLY=( $(compgen -d -- ${cur}) ) + return 0 + fi if [[ ${cur} =~ : ]]; then COMPREPLY=( $(compgen -W "${keywords}" -- ${cur}) ) diff --git a/setup.py b/setup.py index aa7cfca..8e24fe6 100644 --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ else: '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']) + ('share/man/man1', ['youtube-dl.1']) ] } if setuptools_available: diff --git a/test/helper.py b/test/helper.py index d7bf7a8..b1f421a 100644 --- a/test/helper.py +++ b/test/helper.py @@ -12,10 +12,6 @@ from youtube_dl import YoutubeDL from youtube_dl.utils import preferredencoding -def global_setup(): - youtube_dl._setup_opener(timeout=10) - - def get_params(override=None): PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json") diff --git a/test/parameters.json b/test/parameters.json index f042880..487a46d 100644 --- a/test/parameters.json +++ b/test/parameters.json @@ -39,5 +39,6 @@ "writeinfojson": true, "writesubtitles": false, "allsubtitles": false, - "listssubtitles": false + "listssubtitles": false, + "socket_timeout": 20 } diff --git a/test/test_age_restriction.py b/test/test_age_restriction.py index d500c6e..c9cdb96 100644 --- a/test/test_age_restriction.py +++ b/test/test_age_restriction.py @@ -6,8 +6,7 @@ import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from test.helper import global_setup, try_rm -global_setup() +from test.helper import try_rm from youtube_dl import YoutubeDL @@ -24,7 +23,7 @@ def _download_restricted(url, filename, age): } ydl = YoutubeDL(params) ydl.add_default_info_extractors() - json_filename = filename + '.info.json' + json_filename = os.path.splitext(filename)[0] + '.info.json' try_rm(json_filename) ydl.download([url]) res = os.path.exists(json_filename) diff --git a/test/test_all_urls.py b/test/test_all_urls.py index 56e5f80..6b9764c 100644 --- a/test/test_all_urls.py +++ b/test/test_all_urls.py @@ -100,10 +100,15 @@ class TestAllURLsMatching(unittest.TestCase): def test_keywords(self): self.assertMatch(':ytsubs', ['youtube:subscriptions']) self.assertMatch(':ytsubscriptions', ['youtube:subscriptions']) - self.assertMatch(':thedailyshow', ['ComedyCentral']) - self.assertMatch(':tds', ['ComedyCentral']) - self.assertMatch(':colbertreport', ['ComedyCentral']) - self.assertMatch(':cr', ['ComedyCentral']) + self.assertMatch(':ythistory', ['youtube:history']) + self.assertMatch(':thedailyshow', ['ComedyCentralShows']) + self.assertMatch(':tds', ['ComedyCentralShows']) + self.assertMatch(':colbertreport', ['ComedyCentralShows']) + self.assertMatch(':cr', ['ComedyCentralShows']) + + def test_vimeo_matching(self): + self.assertMatch('http://vimeo.com/channels/tributes', ['vimeo:channel']) + self.assertMatch('http://vimeo.com/user7108434', ['vimeo:user']) if __name__ == '__main__': diff --git a/test/test_download.py b/test/test_download.py index 16f2008..dd5818d 100644 --- a/test/test_download.py +++ b/test/test_download.py @@ -9,12 +9,10 @@ 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, report_warning ) -global_setup() import hashlib @@ -103,7 +101,7 @@ def generator(test_case): tc_filename = get_tc_filename(tc) try_rm(tc_filename) try_rm(tc_filename + '.part') - try_rm(tc_filename + '.info.json') + try_rm(os.path.splitext(tc_filename)[0] + '.info.json') try_rm_tcs_files() try: try_num = 1 @@ -130,11 +128,12 @@ def generator(test_case): if not test_case.get('params', {}).get('skip_download', False): 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')) + info_json_fn = os.path.splitext(tc_filename)[0] + '.info.json' + self.assertTrue(os.path.exists(info_json_fn)) if 'md5' in tc: md5_for_file = _file_md5(tc_filename) self.assertEqual(md5_for_file, tc['md5']) - with io.open(tc_filename + '.info.json', encoding='utf-8') as infof: + with io.open(info_json_fn, 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:'): diff --git a/test/test_playlists.py b/test/test_playlists.py index 706b6bd..00c9501 100644 --- a/test/test_playlists.py +++ b/test/test_playlists.py @@ -8,20 +8,23 @@ 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 -global_setup() +from test.helper import FakeYDL from youtube_dl.extractor import ( DailymotionPlaylistIE, DailymotionUserIE, VimeoChannelIE, + VimeoUserIE, UstreamChannelIE, SoundcloudSetIE, SoundcloudUserIE, LivestreamIE, NHLVideocenterIE, BambuserChannelIE, + BandcampAlbumIE, + SmotriCommunityIE, + SmotriUserIE ) @@ -54,6 +57,14 @@ class TestPlaylists(unittest.TestCase): self.assertEqual(result['title'], u'Vimeo Tributes') self.assertTrue(len(result['entries']) > 24) + def test_vimeo_user(self): + dl = FakeYDL() + ie = VimeoUserIE(dl) + result = ie.extract('http://vimeo.com/nkistudio/videos') + self.assertIsPlaylist(result) + self.assertEqual(result['title'], u'Nki') + self.assertTrue(len(result['entries']) > 65) + def test_ustream_channel(self): dl = FakeYDL() ie = UstreamChannelIE(dl) @@ -101,7 +112,33 @@ class TestPlaylists(unittest.TestCase): result = ie.extract('http://bambuser.com/channel/pixelversity') self.assertIsPlaylist(result) self.assertEqual(result['title'], u'pixelversity') - self.assertTrue(len(result['entries']) >= 66) + self.assertTrue(len(result['entries']) >= 60) + + def test_bandcamp_album(self): + dl = FakeYDL() + ie = BandcampAlbumIE(dl) + result = ie.extract('http://mpallante.bandcamp.com/album/nightmare-night-ep') + self.assertIsPlaylist(result) + self.assertEqual(result['title'], u'Nightmare Night EP') + self.assertTrue(len(result['entries']) >= 4) + + def test_smotri_community(self): + dl = FakeYDL() + ie = SmotriCommunityIE(dl) + result = ie.extract('http://smotri.com/community/video/kommuna') + self.assertIsPlaylist(result) + self.assertEqual(result['id'], u'kommuna') + self.assertEqual(result['title'], u'КПРФ') + self.assertTrue(len(result['entries']) >= 4) + + def test_smotri_user(self): + dl = FakeYDL() + ie = SmotriUserIE(dl) + result = ie.extract('http://smotri.com/user/inspector') + self.assertIsPlaylist(result) + self.assertEqual(result['id'], u'inspector') + self.assertEqual(result['title'], u'Inspector') + self.assertTrue(len(result['entries']) >= 9) if __name__ == '__main__': unittest.main() diff --git a/test/test_subtitles.py b/test/test_subtitles.py index 06a3048..23a6531 100644 --- a/test/test_subtitles.py +++ b/test/test_subtitles.py @@ -6,8 +6,7 @@ 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 test.helper import FakeYDL, md5 from youtube_dl.extractor import ( @@ -73,7 +72,7 @@ class TestYoutubeSubtitles(BaseTestSubtitles): self.DL.params['writesubtitles'] = True self.DL.params['subtitlesformat'] = 'vtt' subtitles = self.getSubtitles() - self.assertEqual(md5(subtitles['en']), '356cdc577fde0c6783b9b822e7206ff7') + self.assertEqual(md5(subtitles['en']), '3cb210999d3e021bd6c7f0ea751eab06') def test_youtube_list_subtitles(self): self.DL.expect_warning(u'Video doesn\'t have automatic captions') diff --git a/test/test_utils.py b/test/test_utils.py index f3fbff0..e9e590e 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -24,6 +24,8 @@ from youtube_dl.utils import ( xpath_with_ns, smuggle_url, unsmuggle_url, + shell_quote, + encodeFilename, ) if sys.version_info < (3, 0): @@ -170,6 +172,10 @@ class TestUtil(unittest.TestCase): self.assertEqual(res_url, url) self.assertEqual(res_data, None) + def test_shell_quote(self): + args = ['ffmpeg', '-i', encodeFilename(u'ñ€ß\'.mp4')] + self.assertEqual(shell_quote(args), u"""ffmpeg -i 'ñ€ß'"'"'.mp4'""") + if __name__ == '__main__': unittest.main() diff --git a/test/test_write_annotations.py b/test/test_write_annotations.py index 35defb8..eac53b2 100644 --- a/test/test_write_annotations.py +++ b/test/test_write_annotations.py @@ -7,8 +7,7 @@ import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from test.helper import get_params, global_setup, try_rm -global_setup() +from test.helper import get_params, try_rm import io diff --git a/test/test_write_info_json.py b/test/test_write_info_json.py index a5b6f69..d717761 100644 --- a/test/test_write_info_json.py +++ b/test/test_write_info_json.py @@ -7,8 +7,7 @@ import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from test.helper import get_params, global_setup -global_setup() +from test.helper import get_params import io @@ -31,7 +30,7 @@ params = get_params({ TEST_ID = 'BaW_jenozKc' -INFO_JSON_FILE = TEST_ID + '.mp4.info.json' +INFO_JSON_FILE = TEST_ID + '.info.json' DESCRIPTION_FILE = TEST_ID + '.mp4.description' EXPECTED_DESCRIPTION = u'''test chars: "'/\ä↭𝕐 diff --git a/test/test_youtube_lists.py b/test/test_youtube_lists.py index 4b7a784..95f07d1 100644 --- a/test/test_youtube_lists.py +++ b/test/test_youtube_lists.py @@ -6,8 +6,7 @@ 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 -global_setup() +from test.helper import FakeYDL from youtube_dl.extractor import ( @@ -27,7 +26,7 @@ class TestYoutubeLists(unittest.TestCase): def test_youtube_playlist(self): dl = FakeYDL() ie = YoutubePlaylistIE(dl) - result = ie.extract('https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re')[0] + result = ie.extract('https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re') self.assertIsPlaylist(result) self.assertEqual(result['title'], 'ytdl test PL') ytie_results = [YoutubeIE()._extract_id(url['url']) for url in result['entries']] @@ -44,13 +43,13 @@ class TestYoutubeLists(unittest.TestCase): def test_issue_673(self): dl = FakeYDL() ie = YoutubePlaylistIE(dl) - result = ie.extract('PLBB231211A4F62143')[0] + result = ie.extract('PLBB231211A4F62143') self.assertTrue(len(result['entries']) > 25) def test_youtube_playlist_long(self): dl = FakeYDL() ie = YoutubePlaylistIE(dl) - result = ie.extract('https://www.youtube.com/playlist?list=UUBABnxM4Ar9ten8Mdjj1j0Q')[0] + result = ie.extract('https://www.youtube.com/playlist?list=UUBABnxM4Ar9ten8Mdjj1j0Q') self.assertIsPlaylist(result) self.assertTrue(len(result['entries']) >= 799) @@ -58,7 +57,7 @@ class TestYoutubeLists(unittest.TestCase): #651 dl = FakeYDL() ie = YoutubePlaylistIE(dl) - result = ie.extract('https://www.youtube.com/playlist?list=PLwP_SiAcdui0KVebT0mU9Apz359a4ubsC')[0] + result = ie.extract('https://www.youtube.com/playlist?list=PLwP_SiAcdui0KVebT0mU9Apz359a4ubsC') ytie_results = [YoutubeIE()._extract_id(url['url']) for url in result['entries']] self.assertFalse('pElCt5oNDuI' in ytie_results) self.assertFalse('KdPEApIVdWM' in ytie_results) @@ -66,7 +65,7 @@ class TestYoutubeLists(unittest.TestCase): def test_youtube_playlist_empty(self): dl = FakeYDL() ie = YoutubePlaylistIE(dl) - result = ie.extract('https://www.youtube.com/playlist?list=PLtPgu7CB4gbZDA7i_euNxn75ISqxwZPYx')[0] + result = ie.extract('https://www.youtube.com/playlist?list=PLtPgu7CB4gbZDA7i_euNxn75ISqxwZPYx') self.assertIsPlaylist(result) self.assertEqual(len(result['entries']), 0) @@ -74,7 +73,7 @@ class TestYoutubeLists(unittest.TestCase): dl = FakeYDL() ie = YoutubePlaylistIE(dl) # TODO find a > 100 (paginating?) videos course - result = ie.extract('https://www.youtube.com/course?list=ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8')[0] + result = ie.extract('https://www.youtube.com/course?list=ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8') entries = result['entries'] self.assertEqual(YoutubeIE()._extract_id(entries[0]['url']), 'j9WZyLZCBzs') self.assertEqual(len(entries), 25) @@ -84,22 +83,22 @@ class TestYoutubeLists(unittest.TestCase): dl = FakeYDL() ie = YoutubeChannelIE(dl) #test paginated channel - result = ie.extract('https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w')[0] + result = ie.extract('https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w') self.assertTrue(len(result['entries']) > 90) #test autogenerated channel - result = ie.extract('https://www.youtube.com/channel/HCtnHdj3df7iM/videos')[0] + result = ie.extract('https://www.youtube.com/channel/HCtnHdj3df7iM/videos') self.assertTrue(len(result['entries']) >= 18) def test_youtube_user(self): dl = FakeYDL() ie = YoutubeUserIE(dl) - result = ie.extract('https://www.youtube.com/user/TheLinuxFoundation')[0] + result = ie.extract('https://www.youtube.com/user/TheLinuxFoundation') self.assertTrue(len(result['entries']) >= 320) def test_youtube_safe_search(self): dl = FakeYDL() ie = YoutubePlaylistIE(dl) - result = ie.extract('PLtPgu7CB4gbY9oDN3drwC3cMbJggS7dKl')[0] + result = ie.extract('PLtPgu7CB4gbY9oDN3drwC3cMbJggS7dKl') self.assertEqual(len(result['entries']), 2) def test_youtube_show(self): @@ -108,5 +107,14 @@ class TestYoutubeLists(unittest.TestCase): result = ie.extract('http://www.youtube.com/show/airdisasters') self.assertTrue(len(result) >= 3) + def test_youtube_mix(self): + dl = FakeYDL() + ie = YoutubePlaylistIE(dl) + result = ie.extract('http://www.youtube.com/watch?v=lLJf9qJHR3E&list=RDrjFaenf1T-Y') + entries = result['entries'] + self.assertTrue(len(entries) >= 20) + original_video = entries[0] + self.assertEqual(original_video['id'], 'rjFaenf1T-Y') + if __name__ == '__main__': unittest.main() diff --git a/test/test_youtube_signature.py b/test/test_youtube_signature.py index 5e1ff5e..0567006 100644 --- a/test/test_youtube_signature.py +++ b/test/test_youtube_signature.py @@ -6,9 +6,6 @@ import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from test.helper import global_setup -global_setup() - import io import re diff --git a/youtube-dl b/youtube-dl index 0924a5d8a7fa976e2878e3607a5410a96559acf0..899ca28bd80ce245f7afd7cbadc2eae53755a5ff 100755 GIT binary patch delta 180923 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 WbSXjn;3x?GHE4p{$P7U4(Ek9Ypa_}( delta 159862 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

!3-J3nN)<5_g(|$ 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

3AGxNE*dU!cFJn&%? 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

Vp{%(H2ajP)h>@ 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 diff --git a/youtube-dl.1 b/youtube-dl.1 index bdd5a2d..a172bf8 100644 --- a/youtube-dl.1 +++ b/youtube-dl.1 @@ -34,7 +34,8 @@ redistribute it or use it however you like. \-\-list\-extractors\ \ \ \ \ \ \ \ \ \ List\ all\ supported\ extractors\ and\ the\ URLs\ they \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ would\ handle \-\-extractor\-descriptions\ \ \ Output\ descriptions\ of\ all\ supported\ extractors -\-\-proxy\ URL\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Use\ the\ specified\ HTTP/HTTPS\ proxy +\-\-proxy\ URL\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Use\ the\ specified\ HTTP/HTTPS\ proxy.\ Pass\ in\ an +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ empty\ string\ (\-\-proxy\ "")\ for\ direct\ connection \-\-no\-check\-certificate\ \ \ \ \ Suppress\ HTTPS\ certificate\ validation. \-\-cache\-dir\ DIR\ \ \ \ \ \ \ \ \ \ \ \ Location\ in\ the\ filesystem\ where\ youtube\-dl\ can \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ store\ downloaded\ information\ permanently.\ By @@ -63,8 +64,9 @@ redistribute it or use it however you like. \-\-dateafter\ DATE\ \ \ \ \ \ \ \ \ \ \ download\ only\ videos\ uploaded\ after\ this\ date \-\-no\-playlist\ \ \ \ \ \ \ \ \ \ \ \ \ \ download\ only\ the\ currently\ playing\ video \-\-age\-limit\ YEARS\ \ \ \ \ \ \ \ \ \ download\ only\ videos\ suitable\ for\ the\ given\ age -\-\-download\-archive\ FILE\ \ \ \ Download\ only\ videos\ not\ present\ in\ the\ archive -\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ file.\ Record\ all\ downloaded\ videos\ in\ it. +\-\-download\-archive\ FILE\ \ \ \ Download\ only\ videos\ not\ listed\ in\ the\ archive +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ file.\ Record\ the\ IDs\ of\ all\ downloaded\ videos\ in +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ it. \f[] .fi .SS Download Options: @@ -143,21 +145,22 @@ redistribute it or use it however you like. \-\-get\-description\ \ \ \ \ \ \ \ \ \ simulate,\ quiet\ but\ print\ video\ description \-\-get\-filename\ \ \ \ \ \ \ \ \ \ \ \ \ simulate,\ quiet\ but\ print\ output\ filename \-\-get\-format\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ simulate,\ quiet\ but\ print\ output\ format +\-j,\ \-\-dump\-json\ \ \ \ \ \ \ \ \ \ \ \ simulate,\ quiet\ but\ print\ JSON\ information \-\-newline\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ output\ progress\ bar\ as\ new\ lines \-\-no\-progress\ \ \ \ \ \ \ \ \ \ \ \ \ \ do\ not\ print\ progress\ bar \-\-console\-title\ \ \ \ \ \ \ \ \ \ \ \ display\ progress\ in\ console\ titlebar \-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 +\-\-write\-pages\ \ \ \ \ \ \ \ \ \ \ \ \ \ Write\ downloaded\ intermediary\ pages\ to\ files\ in +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ the\ current\ directory\ to\ debug\ problems \f[] .fi .SS Video Format Options: .IP .nf \f[C] -\-f,\ \-\-format\ FORMAT\ \ \ \ \ \ \ \ video\ format\ code,\ specifiy\ the\ order\ of +\-f,\ \-\-format\ FORMAT\ \ \ \ \ \ \ \ video\ format\ code,\ specify\ the\ order\ of \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ preference\ using\ slashes:\ "\-f\ 22/17/18".\ "\-f\ mp4" \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ and\ "\-f\ flv"\ are\ also\ supported \-\-all\-formats\ \ \ \ \ \ \ \ \ \ \ \ \ \ download\ all\ available\ video\ formats @@ -222,6 +225,9 @@ You can configure youtube\-dl by placing default arguments (such as \f[C]\-\-extract\-audio\ \-\-no\-mtime\f[] to always extract the audio and not copy the mtime) into \f[C]/etc/youtube\-dl.conf\f[] and/or \f[C]~/.config/youtube\-dl.conf\f[]. +On Windows, the configuration file locations are +\f[C]%APPDATA%\\youtube\-dl\\config.txt\f[] and +\f[C]C:\\Users\\\\youtube\-dl.conf\f[]. .SH OUTPUT TEMPLATE .PP The \f[C]\-o\f[] option allows users to indicate a template for the diff --git a/youtube-dl.bash-completion b/youtube-dl.bash-completion index 0123996..9c30e7b 100644 --- a/youtube-dl.bash-completion +++ b/youtube-dl.bash-completion @@ -1,10 +1,21 @@ __youtube_dl() { - local cur prev opts + local cur prev opts fileopts diropts keywords COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - opts="--help --version --update --ignore-errors --abort-on-error --dump-user-agent --user-agent --referer --list-extractors --extractor-descriptions --proxy --no-check-certificate --cache-dir --no-cache-dir --playlist-start --playlist-end --match-title --reject-title --max-downloads --min-filesize --max-filesize --date --datebefore --dateafter --no-playlist --age-limit --download-archive --rate-limit --retries --buffer-size --no-resize-buffer --test --title --id --literal --auto-number --output --autonumber-size --restrict-filenames --batch-file --no-overwrites --continue --no-continue --cookies --no-part --no-mtime --write-description --write-info-json --write-annotations --write-thumbnail --quiet --simulate --skip-download --get-url --get-title --get-id --get-thumbnail --get-description --get-filename --get-format --newline --no-progress --console-title --verbose --dump-intermediate-pages --write-pages --youtube-print-sig-code --format --all-formats --prefer-free-formats --max-quality --list-formats --write-sub --write-auto-sub --all-subs --list-subs --sub-format --sub-lang --username --password --netrc --video-password --extract-audio --audio-format --audio-quality --recode-video --keep-video --no-post-overwrites --embed-subs --add-metadata" - keywords=":ytfavorites :ytrecommended :ytsubscriptions :ytwatchlater" + prev="${COMP_WORDS[COMP_CWORD-1]}" + opts="--help --version --update --ignore-errors --abort-on-error --dump-user-agent --user-agent --referer --list-extractors --extractor-descriptions --proxy --no-check-certificate --cache-dir --no-cache-dir --socket-timeout --playlist-start --playlist-end --match-title --reject-title --max-downloads --min-filesize --max-filesize --date --datebefore --dateafter --no-playlist --age-limit --download-archive --rate-limit --retries --buffer-size --no-resize-buffer --test --title --id --literal --auto-number --output --autonumber-size --restrict-filenames --batch-file --no-overwrites --continue --no-continue --cookies --no-part --no-mtime --write-description --write-info-json --write-annotations --write-thumbnail --quiet --simulate --skip-download --get-url --get-title --get-id --get-thumbnail --get-description --get-filename --get-format --dump-json --newline --no-progress --console-title --verbose --dump-intermediate-pages --write-pages --youtube-print-sig-code --format --all-formats --prefer-free-formats --max-quality --list-formats --write-sub --write-auto-sub --all-subs --list-subs --sub-format --sub-lang --username --password --netrc --video-password --extract-audio --audio-format --audio-quality --recode-video --keep-video --no-post-overwrites --embed-subs --add-metadata" + keywords=":ytfavorites :ytrecommended :ytsubscriptions :ytwatchlater :ythistory" + fileopts="-a|--batch-file|--download-archive|--cookies" + diropts="--cache-dir" + + if [[ ${prev} =~ ${fileopts} ]]; then + COMPREPLY=( $(compgen -f -- ${cur}) ) + return 0 + elif [[ ${prev} =~ ${diropts} ]]; then + COMPREPLY=( $(compgen -d -- ${cur}) ) + return 0 + fi if [[ ${cur} =~ : ]]; then COMPREPLY=( $(compgen -W "${keywords}" -- ${cur}) ) diff --git a/youtube_dl/FileDownloader.py b/youtube_dl/FileDownloader.py index 088f595..3ff9716 100644 --- a/youtube_dl/FileDownloader.py +++ b/youtube_dl/FileDownloader.py @@ -1,19 +1,16 @@ -import math import os import re import subprocess import sys import time -if os.name == 'nt': - import ctypes - from .utils import ( compat_urllib_error, compat_urllib_request, ContentTooShortError, determine_ext, encodeFilename, + format_bytes, sanitize_open, timeconvert, ) @@ -56,20 +53,6 @@ class FileDownloader(object): self._progress_hooks = [] self.params = params - @staticmethod - def format_bytes(bytes): - if bytes is None: - return 'N/A' - if type(bytes) is str: - bytes = float(bytes) - if bytes == 0.0: - exponent = 0 - else: - exponent = int(math.log(bytes, 1024.0)) - suffix = ['B','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'][exponent] - converted = float(bytes) / float(1024 ** exponent) - return '%.2f%s' % (converted, suffix) - @staticmethod def format_seconds(seconds): (mins, secs) = divmod(seconds, 60) @@ -120,7 +103,7 @@ class FileDownloader(object): def format_speed(speed): if speed is None: return '%10s' % '---b/s' - return '%10s' % ('%s/s' % FileDownloader.format_bytes(speed)) + return '%10s' % ('%s/s' % format_bytes(speed)) @staticmethod def best_block_size(elapsed_time, bytes): @@ -151,16 +134,8 @@ class FileDownloader(object): def to_stderr(self, message): self.ydl.to_screen(message) - def to_cons_title(self, message): - """Set console/terminal window title to message.""" - if not self.params.get('consoletitle', False): - return - if os.name == 'nt' and ctypes.windll.kernel32.GetConsoleWindow(): - # c_wchar_p() might not be necessary if `message` is - # already of type unicode() - ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message)) - elif 'TERM' in os.environ: - self.to_screen('\033]0;%s\007' % message, skip_eol=True) + def to_console_title(self, message): + self.ydl.to_console_title(message) def trouble(self, *args, **kargs): self.ydl.trouble(*args, **kargs) @@ -249,7 +224,7 @@ class FileDownloader(object): else: self.to_screen(u'\r%s[download] %s of %s at %s ETA %s' % (clear_line, percent_str, data_len_str, speed_str, eta_str), skip_eol=True) - self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' % + self.to_console_title(u'youtube-dl - %s of %s at %s ETA %s' % (percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip())) def report_resuming_byte(self, resume_len): @@ -281,6 +256,61 @@ class FileDownloader(object): (clear_line, data_len_str, self.format_seconds(tot_time))) def _download_with_rtmpdump(self, filename, url, player_url, page_url, play_path, tc_url, live): + def run_rtmpdump(args): + start = time.time() + resume_percent = None + resume_downloaded_data_len = None + proc = subprocess.Popen(args, stderr=subprocess.PIPE) + cursor_in_new_line = True + proc_stderr_closed = False + while not proc_stderr_closed: + # read line from stderr + line = u'' + while True: + char = proc.stderr.read(1) + if not char: + proc_stderr_closed = True + break + if char in [b'\r', b'\n']: + break + line += char.decode('ascii', 'replace') + if not line: + # proc_stderr_closed is True + continue + mobj = re.search(r'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec \(([0-9]{1,2}\.[0-9])%\)', line) + if mobj: + downloaded_data_len = int(float(mobj.group(1))*1024) + percent = float(mobj.group(2)) + if not resume_percent: + resume_percent = percent + resume_downloaded_data_len = downloaded_data_len + eta = self.calc_eta(start, time.time(), 100-resume_percent, percent-resume_percent) + speed = self.calc_speed(start, time.time(), downloaded_data_len-resume_downloaded_data_len) + data_len = None + if percent > 0: + data_len = int(downloaded_data_len * 100 / percent) + data_len_str = u'~' + format_bytes(data_len) + self.report_progress(percent, data_len_str, speed, eta) + cursor_in_new_line = False + self._hook_progress({ + 'downloaded_bytes': downloaded_data_len, + 'total_bytes': data_len, + 'tmpfilename': tmpfilename, + 'filename': filename, + 'status': 'downloading', + 'eta': eta, + 'speed': speed, + }) + elif self.params.get('verbose', False): + if not cursor_in_new_line: + self.to_screen(u'') + cursor_in_new_line = True + self.to_screen(u'[rtmpdump] '+line) + proc.wait() + if not cursor_in_new_line: + self.to_screen(u'') + return proc.returncode + self.report_destination(filename) tmpfilename = self.temp_name(filename) test = self.params.get('test', False) @@ -291,12 +321,11 @@ class FileDownloader(object): except (OSError, IOError): self.report_error(u'RTMP download detected but "rtmpdump" could not be run') return False - verbosity_option = '--verbose' if self.params.get('verbose', False) else '--quiet' # Download using rtmpdump. rtmpdump returns exit code 2 when # the connection was interrumpted and resuming appears to be # possible. This is part of rtmpdump's normal usage, AFAIK. - basic_args = ['rtmpdump', verbosity_option, '-r', url, '-o', tmpfilename] + basic_args = ['rtmpdump', '--verbose', '-r', url, '-o', tmpfilename] if player_url is not None: basic_args += ['--swfVfy', player_url] if page_url is not None: @@ -310,30 +339,48 @@ class FileDownloader(object): if live: basic_args += ['--live'] args = basic_args + [[], ['--resume', '--skip', '1']][self.params.get('continuedl', False)] + + if sys.platform == 'win32' and sys.version_info < (3, 0): + # Windows subprocess module does not actually support Unicode + # on Python 2.x + # See http://stackoverflow.com/a/9951851/35070 + subprocess_encoding = sys.getfilesystemencoding() + args = [a.encode(subprocess_encoding, 'ignore') for a in args] + else: + subprocess_encoding = None + if self.params.get('verbose', False): + if subprocess_encoding: + str_args = [ + a.decode(subprocess_encoding) if isinstance(a, bytes) else a + for a in args] + else: + str_args = args try: import pipes - shell_quote = lambda args: ' '.join(map(pipes.quote, args)) + shell_quote = lambda args: ' '.join(map(pipes.quote, str_args)) except ImportError: shell_quote = repr - self.to_screen(u'[debug] rtmpdump command line: ' + shell_quote(args)) - retval = subprocess.call(args) + self.to_screen(u'[debug] rtmpdump command line: ' + shell_quote(str_args)) + + retval = run_rtmpdump(args) + while (retval == 2 or retval == 1) and not test: prevsize = os.path.getsize(encodeFilename(tmpfilename)) - self.to_screen(u'\r[rtmpdump] %s bytes' % prevsize, skip_eol=True) + self.to_screen(u'[rtmpdump] %s bytes' % prevsize) time.sleep(5.0) # This seems to be needed - retval = subprocess.call(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1]) + retval = run_rtmpdump(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1]) cursize = os.path.getsize(encodeFilename(tmpfilename)) if prevsize == cursize and retval == 1: break # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those if prevsize == cursize and retval == 2 and cursize > 1024: - self.to_screen(u'\r[rtmpdump] Could not download the whole video. This can happen for some advertisements.') + self.to_screen(u'[rtmpdump] Could not download the whole video. This can happen for some advertisements.') retval = 0 break if retval == 0 or (test and retval == 2): fsize = os.path.getsize(encodeFilename(tmpfilename)) - self.to_screen(u'\r[rtmpdump] %s bytes' % fsize) + self.to_screen(u'[rtmpdump] %s bytes' % fsize) self.try_rename(tmpfilename, filename) self._hook_progress({ 'downloaded_bytes': fsize, @@ -536,7 +583,7 @@ class FileDownloader(object): self.to_screen(u'\r[download] File is larger than max-filesize (%s bytes > %s bytes). Aborting.' % (data_len, max_data_len)) return False - data_len_str = self.format_bytes(data_len) + data_len_str = format_bytes(data_len) byte_counter = 0 + resume_len block_size = self.params.get('buffersize', 1024) start = time.time() diff --git a/youtube_dl/PostProcessor.py b/youtube_dl/PostProcessor.py index 13b56ed..69aedf8 100644 --- a/youtube_dl/PostProcessor.py +++ b/youtube_dl/PostProcessor.py @@ -501,7 +501,7 @@ class FFmpegMetadataPP(FFmpegPostProcessor): options = ['-c', 'copy'] for (name, value) in metadata.items(): - options.extend(['-metadata', '%s="%s"' % (name, value)]) + options.extend(['-metadata', '%s=%s' % (name, value)]) options.extend(['-f', ext]) self._downloader.to_screen(u'[ffmpeg] Adding metadata to \'%s\'' % filename) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 5253c39..b68b110 100644 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -5,17 +5,53 @@ from __future__ import absolute_import import errno import io +import json import os +import platform import re import shutil +import subprocess import socket import sys import time import traceback -from .utils import * +if os.name == 'nt': + import ctypes + +from .utils import ( + compat_cookiejar, + compat_http_client, + compat_print, + compat_str, + compat_urllib_error, + compat_urllib_request, + ContentTooShortError, + date_from_str, + DateRange, + determine_ext, + DownloadError, + encodeFilename, + ExtractorError, + format_bytes, + locked_file, + make_HTTPS_handler, + MaxDownloadsReached, + PostProcessingError, + platform_name, + preferredencoding, + SameFileError, + sanitize_filename, + subtitles_filename, + takewhile_inclusive, + UnavailableVideoError, + write_json_file, + write_string, + YoutubeDLHandler, +) from .extractor import get_info_extractor, gen_extractors from .FileDownloader import FileDownloader +from .version import __version__ class YoutubeDL(object): @@ -57,6 +93,7 @@ class YoutubeDL(object): forcethumbnail: Force printing thumbnail URL. forcedescription: Force printing description. forcefilename: Force printing final filename. + forcejson: Force printing info_dict as JSON. simulate: Do not download the video files. format: Video format code. format_limit: Highest quality format to try. @@ -68,6 +105,7 @@ class YoutubeDL(object): playlistend: Playlist item to end at. matchtitle: Download only matching titles. rejecttitle: Reject downloads for matching titles. + logger: Log messages to a logging.Logger instance. logtostderr: Log messages to stderr instead of stdout. writedescription: Write the video description to a .description file writeinfojson: Write the video description to a .info.json file @@ -88,9 +126,13 @@ class YoutubeDL(object): noplaylist: Download single video instead of a playlist if in doubt. age_limit: An integer representing the user's age in years. Unsuitable videos for the given age are skipped. - downloadarchive: File name of a file where all downloads are recorded. + download_archive: File name of a file where all downloads are recorded. Videos already present in the file are not downloaded again. + cookiefile: File name where cookies should be read from and dumped to. + nocheckcertificate:Do not verify SSL certificates + proxy: URL of the proxy server to use + socket_timeout: Time to wait for unresponsive hosts, in seconds The following parameters are not used by YoutubeDL itself, they are used by the FileDownloader: @@ -105,7 +147,7 @@ class YoutubeDL(object): _num_downloads = None _screen_file = None - def __init__(self, params): + def __init__(self, params=None): """Create a FileDownloader object with the given options.""" self._ies = [] self._ies_instances = {} @@ -114,6 +156,7 @@ class YoutubeDL(object): self._download_retcode = 0 self._num_downloads = 0 self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)] + self.params = {} if params is None else params if (sys.version_info >= (3,) and sys.platform != 'win32' and sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968'] @@ -123,14 +166,15 @@ class YoutubeDL(object): u'Assuming --restrict-filenames since file system encoding ' u'cannot encode all charactes. ' u'Set the LC_ALL environment variable to fix this.') - params['restrictfilenames'] = True + self.params['restrictfilenames'] = True - self.params = params self.fd = FileDownloader(self, self.params) - if '%(stitle)s' in self.params['outtmpl']: + if '%(stitle)s' in self.params.get('outtmpl', ''): self.report_warning(u'%(stitle)s is deprecated. Use the %(title)s and the --restrict-filenames flag(which also secures %(uploader)s et al) instead.') + self._setup_opener() + def add_info_extractor(self, ie): """Add an InfoExtractor object to the end of the list.""" self._ies.append(ie) @@ -163,7 +207,9 @@ class YoutubeDL(object): def to_screen(self, message, skip_eol=False): """Print message to stdout if not in quiet mode.""" - if not self.params.get('quiet', False): + if self.params.get('logger'): + self.params['logger'].debug(message) + elif not self.params.get('quiet', False): terminator = [u'\n', u''][skip_eol] output = message + terminator write_string(output, self._screen_file) @@ -171,14 +217,47 @@ class YoutubeDL(object): def to_stderr(self, message): """Print message to stderr.""" assert type(message) == type(u'') - output = message + u'\n' - if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr - output = output.encode(preferredencoding()) - sys.stderr.write(output) + if self.params.get('logger'): + self.params['logger'].error(message) + else: + output = message + u'\n' + if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr + output = output.encode(preferredencoding()) + sys.stderr.write(output) + + def to_console_title(self, message): + if not self.params.get('consoletitle', False): + return + if os.name == 'nt' and ctypes.windll.kernel32.GetConsoleWindow(): + # c_wchar_p() might not be necessary if `message` is + # already of type unicode() + ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message)) + elif 'TERM' in os.environ: + write_string(u'\033]0;%s\007' % message, self._screen_file) + + def save_console_title(self): + if not self.params.get('consoletitle', False): + return + if 'TERM' in os.environ: + # Save the title on stack + write_string(u'\033[22;0t', self._screen_file) + + def restore_console_title(self): + if not self.params.get('consoletitle', False): + return + if 'TERM' in os.environ: + # Restore the title from stack + write_string(u'\033[23;0t', self._screen_file) - def fixed_template(self): - """Checks if the output template is fixed.""" - return (re.search(u'(?u)%\\(.+?\\)s', self.params['outtmpl']) is None) + def __enter__(self): + self.save_console_title() + return self + + def __exit__(self, *args): + self.restore_console_title() + + if self.params.get('cookiefile') is not None: + self.cookiejar.save() def trouble(self, message=None, tb=None): """Determine action to take when a download problem appears. @@ -254,7 +333,7 @@ class YoutubeDL(object): """Report file has already been fully downloaded.""" try: self.to_screen(u'[download] %s has already been downloaded' % file_name) - except (UnicodeEncodeError) as err: + except UnicodeEncodeError: self.to_screen(u'[download] The file has already been downloaded') def increment_downloads(self): @@ -295,15 +374,17 @@ class YoutubeDL(object): def _match_entry(self, info_dict): """ Returns None iff the file should be downloaded """ - title = info_dict['title'] - matchtitle = self.params.get('matchtitle', False) - if matchtitle: - if not re.search(matchtitle, title, re.IGNORECASE): - return u'[download] "' + title + '" title did not match pattern "' + matchtitle + '"' - rejecttitle = self.params.get('rejecttitle', False) - if rejecttitle: - if re.search(rejecttitle, title, re.IGNORECASE): - return u'"' + title + '" title matched reject pattern "' + rejecttitle + '"' + if 'title' in info_dict: + # This can happen when we're just evaluating the playlist + title = info_dict['title'] + matchtitle = self.params.get('matchtitle', False) + if matchtitle: + if not re.search(matchtitle, title, re.IGNORECASE): + return u'[download] "' + title + '" title did not match pattern "' + matchtitle + '"' + rejecttitle = self.params.get('rejecttitle', False) + if rejecttitle: + if re.search(rejecttitle, title, re.IGNORECASE): + return u'"' + title + '" title matched reject pattern "' + rejecttitle + '"' date = info_dict.get('upload_date', None) if date is not None: dateRange = self.params.get('daterange', DateRange()) @@ -314,8 +395,8 @@ class YoutubeDL(object): if age_limit < info_dict.get('age_limit', 0): return u'Skipping "' + title + '" because it is age restricted' if self.in_download_archive(info_dict): - return (u'%(title)s has already been recorded in archive' - % info_dict) + return (u'%s has already been recorded in archive' + % info_dict.get('title', info_dict.get('id', u'video'))) return None @staticmethod @@ -385,7 +466,7 @@ class YoutubeDL(object): result_type = ie_result.get('_type', 'video') # If not given we suppose it's a video, support the default old system if result_type == 'video': self.add_extra_info(ie_result, extra_info) - return self.process_video_result(ie_result) + return self.process_video_result(ie_result, download=download) elif result_type == 'url': # We have to add extra_info to the results because it may be # contained in a playlist @@ -394,7 +475,7 @@ class YoutubeDL(object): ie_key=ie_result.get('ie_key'), extra_info=extra_info) elif result_type == 'playlist': - self.add_extra_info(ie_result, extra_info) + # We process each entry in the playlist playlist = ie_result.get('title', None) or ie_result.get('id', None) self.to_screen(u'[download] Downloading playlist: %s' % playlist) @@ -424,6 +505,12 @@ class YoutubeDL(object): 'webpage_url': ie_result['webpage_url'], 'extractor_key': ie_result['extractor_key'], } + + reason = self._match_entry(entry) + if reason is not None: + self.to_screen(u'[download] ' + reason) + continue + entry_result = self.process_ie_result(entry, download=download, extra_info=extra) @@ -579,7 +666,7 @@ class YoutubeDL(object): # Forced printings if self.params.get('forcetitle', False): - compat_print(info_dict['title']) + compat_print(info_dict['fulltitle']) if self.params.get('forceid', False): compat_print(info_dict['id']) if self.params.get('forceurl', False): @@ -593,6 +680,8 @@ class YoutubeDL(object): compat_print(filename) if self.params.get('forceformat', False): compat_print(info_dict['format']) + if self.params.get('forcejson', False): + compat_print(json.dumps(info_dict)) # Do nothing else if in simulate mode if self.params.get('simulate', False): @@ -640,7 +729,7 @@ class YoutubeDL(object): # subtitles download errors are already managed as troubles in relevant IE # that way it will silently go on when used with unsupporting IE subtitles = info_dict['subtitles'] - sub_format = self.params.get('subtitlesformat') + sub_format = self.params.get('subtitlesformat', 'srt') for sub_lang in subtitles.keys(): sub = subtitles[sub_lang] if sub is None: @@ -655,7 +744,7 @@ class YoutubeDL(object): return if self.params.get('writeinfojson', False): - infofn = filename + u'.info.json' + infofn = os.path.splitext(filename)[0] + u'.info.json' self.report_writeinfojson(infofn) try: json_info_dict = dict((k, v) for k, v in info_dict.items() if not k in ['urlhandle']) @@ -706,13 +795,15 @@ class YoutubeDL(object): def download(self, url_list): """Download a given list of URLs.""" - if len(url_list) > 1 and self.fixed_template(): + if (len(url_list) > 1 and + '%' not in self.params['outtmpl'] + and self.params.get('max_downloads') != 1): raise SameFileError(self.params['outtmpl']) for url in url_list: try: #It also downloads the videos - videos = self.extract_info(url) + self.extract_info(url) except UnavailableVideoError: self.report_error(u'unable to download video') except MaxDownloadsReached: @@ -744,11 +835,26 @@ class YoutubeDL(object): except (IOError, OSError): self.report_warning(u'Unable to remove downloaded video file') + def _make_archive_id(self, info_dict): + # Future-proof against any change in case + # and backwards compatibility with prior versions + extractor = info_dict.get('extractor_key') + if extractor is None: + if 'id' in info_dict: + extractor = info_dict.get('ie_key') # key in a playlist + if extractor is None: + return None # Incomplete video information + return extractor.lower() + u' ' + info_dict['id'] + def in_download_archive(self, info_dict): fn = self.params.get('download_archive') if fn is None: return False - vid_id = info_dict['extractor'] + u' ' + info_dict['id'] + + vid_id = self._make_archive_id(info_dict) + if vid_id is None: + return False # Incomplete video information + try: with locked_file(fn, 'r', encoding='utf-8') as archive_file: for line in archive_file: @@ -763,12 +869,15 @@ class YoutubeDL(object): fn = self.params.get('download_archive') if fn is None: return - vid_id = info_dict['extractor'] + u' ' + info_dict['id'] + vid_id = self._make_archive_id(info_dict) + assert vid_id with locked_file(fn, 'a', encoding='utf-8') as archive_file: archive_file.write(vid_id + u'\n') @staticmethod def format_resolution(format, default='unknown'): + if format.get('vcodec') == 'none': + return 'audio only' if format.get('_resolution') is not None: return format['_resolution'] if format.get('height') is not None: @@ -781,23 +890,124 @@ class YoutubeDL(object): return res def list_formats(self, info_dict): - def line(format): - return (u'%-20s%-10s%-12s%s' % ( + def format_note(fdict): + res = u'' + if fdict.get('format_note') is not None: + res += fdict['format_note'] + u' ' + if (fdict.get('vcodec') is not None and + fdict.get('vcodec') != 'none'): + res += u'%-5s' % fdict['vcodec'] + elif fdict.get('vbr') is not None: + res += u'video' + if fdict.get('vbr') is not None: + res += u'@%4dk' % fdict['vbr'] + if fdict.get('acodec') is not None: + if res: + res += u', ' + res += u'%-5s' % fdict['acodec'] + elif fdict.get('abr') is not None: + if res: + res += u', ' + res += 'audio' + if fdict.get('abr') is not None: + res += u'@%3dk' % fdict['abr'] + if fdict.get('filesize') is not None: + if res: + res += u', ' + res += format_bytes(fdict['filesize']) + return res + + def line(format, idlen=20): + return ((u'%-' + compat_str(idlen + 1) + u's%-10s%-12s%s') % ( format['format_id'], format['ext'], self.format_resolution(format), - format.get('format_note', ''), - ) - ) + format_note(format), + )) formats = info_dict.get('formats', [info_dict]) - formats_s = list(map(line, formats)) + idlen = max(len(u'format code'), + max(len(f['format_id']) for f in formats)) + formats_s = [line(f, idlen) for f in 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)' + formats_s[0] += (' ' if format_note(formats[0]) else '') + '(worst)' + formats_s[-1] += (' ' if format_note(formats[-1]) else '') + '(best)' header_line = line({ 'format_id': u'format code', 'ext': u'extension', - '_resolution': u'resolution', 'format_note': u'note'}) + '_resolution': u'resolution', 'format_note': u'note'}, idlen=idlen) self.to_screen(u'[info] Available formats for %s:\n%s\n%s' % (info_dict['id'], header_line, u"\n".join(formats_s))) + + def urlopen(self, req): + """ Start an HTTP download """ + return self._opener.open(req) + + def print_debug_header(self): + if not self.params.get('verbose'): + return + write_string(u'[debug] youtube-dl version ' + __version__ + u'\n') + try: + sp = subprocess.Popen( + ['git', 'rev-parse', '--short', 'HEAD'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + cwd=os.path.dirname(os.path.abspath(__file__))) + out, err = sp.communicate() + out = out.decode().strip() + if re.match('[0-9a-f]+', out): + write_string(u'[debug] Git HEAD: ' + out + u'\n') + except: + try: + sys.exc_clear() + except: + pass + write_string(u'[debug] Python version %s - %s' % + (platform.python_version(), platform_name()) + u'\n') + + proxy_map = {} + for handler in self._opener.handlers: + if hasattr(handler, 'proxies'): + proxy_map.update(handler.proxies) + write_string(u'[debug] Proxy map: ' + compat_str(proxy_map) + u'\n') + + def _setup_opener(self): + timeout_val = self.params.get('socket_timeout') + timeout = 600 if timeout_val is None else float(timeout_val) + + opts_cookiefile = self.params.get('cookiefile') + opts_proxy = self.params.get('proxy') + + if opts_cookiefile is None: + self.cookiejar = compat_cookiejar.CookieJar() + else: + self.cookiejar = compat_cookiejar.MozillaCookieJar( + opts_cookiefile) + if os.access(opts_cookiefile, os.R_OK): + self.cookiejar.load() + + cookie_processor = compat_urllib_request.HTTPCookieProcessor( + self.cookiejar) + if opts_proxy is not None: + if opts_proxy == '': + proxies = {} + else: + proxies = {'http': opts_proxy, 'https': opts_proxy} + else: + proxies = compat_urllib_request.getproxies() + # Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805) + if 'http' in proxies and 'https' not in proxies: + proxies['https'] = proxies['http'] + proxy_handler = compat_urllib_request.ProxyHandler(proxies) + https_handler = make_HTTPS_handler( + self.params.get('nocheckcertificate', False)) + opener = compat_urllib_request.build_opener( + https_handler, proxy_handler, cookie_processor, YoutubeDLHandler()) + # Delete the default user-agent header, which would otherwise apply in + # cases where our custom HTTP handler doesn't come into play + # (See https://github.com/rg3/youtube-dl/issues/1309 for details) + opener.addheaders = [] + self._opener = opener + + # TODO remove this global modification + compat_urllib_request.install_opener(opener) + socket.setdefaulttimeout(timeout) diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 1f1db9f..d2446b6 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -32,50 +32,45 @@ __authors__ = ( 'Ismael Mejía', 'Steffan \'Ruirize\' James', 'Andras Elso', + 'Jelle van der Waa', + 'Marcin Cieślak', + 'Anton Larionov', + 'Takuya Tsuchida', + 'Sergey M.', ) __license__ = 'Public Domain' import codecs -import collections import getpass import optparse import os import random import re import shlex -import socket import subprocess import sys -import traceback -import platform from .utils import ( - compat_cookiejar, compat_print, - compat_str, - compat_urllib_request, DateRange, decodeOption, determine_ext, DownloadError, get_cachedir, - make_HTTPS_handler, MaxDownloadsReached, - platform_name, preferredencoding, SameFileError, std_headers, write_string, - YoutubeDLHandler, ) from .update import update_self -from .version import __version__ from .FileDownloader import ( FileDownloader, ) from .extractor import gen_extractors +from .version import __version__ from .YoutubeDL import YoutubeDL from .PostProcessor import ( FFmpegMetadataPP, @@ -86,11 +81,11 @@ from .PostProcessor import ( def parseOpts(overrideArguments=None): - def _readOptions(filename_bytes): + def _readOptions(filename_bytes, default=[]): try: optionf = open(filename_bytes) except IOError: - return [] # silently skip if file is not present + return default # silently skip if file is not present try: res = [] for l in optionf: @@ -196,7 +191,9 @@ def parseOpts(overrideArguments=None): general.add_option('--extractor-descriptions', action='store_true', dest='list_extractor_descriptions', help='Output descriptions of all supported extractors', default=False) - general.add_option('--proxy', dest='proxy', default=None, help='Use the specified HTTP/HTTPS proxy', metavar='URL') + general.add_option( + '--proxy', dest='proxy', default=None, metavar='URL', + help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection') general.add_option('--no-check-certificate', action='store_true', dest='no_check_certificate', default=False, help='Suppress HTTPS certificate validation.') general.add_option( '--cache-dir', dest='cachedir', default=get_cachedir(), metavar='DIR', @@ -204,6 +201,9 @@ def parseOpts(overrideArguments=None): general.add_option( '--no-cache-dir', action='store_const', const=None, dest='cachedir', help='Disable filesystem caching') + general.add_option( + '--socket-timeout', dest='socket_timeout', + type=float, default=None, help=optparse.SUPPRESS_HELP) selection.add_option('--playlist-start', @@ -212,7 +212,9 @@ def parseOpts(overrideArguments=None): dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1) selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)') selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)') - selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None) + selection.add_option('--max-downloads', metavar='NUMBER', + dest='max_downloads', type=int, default=None, + help='Abort after downloading NUMBER files') selection.add_option('--min-filesize', metavar='SIZE', dest='min_filesize', help="Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)", default=None) selection.add_option('--max-filesize', metavar='SIZE', dest='max_filesize', help="Do not download any videos larger than SIZE (e.g. 50k or 44.6m)", default=None) selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None) @@ -224,7 +226,7 @@ def parseOpts(overrideArguments=None): default=None, type=int) selection.add_option('--download-archive', metavar='FILE', dest='download_archive', - help='Download only videos not present in the archive file. Record all downloaded videos in it.') + help='Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it.') authentication.add_option('-u', '--username', @@ -239,7 +241,7 @@ def parseOpts(overrideArguments=None): video_format.add_option('-f', '--format', action='store', dest='format', metavar='FORMAT', default='best', - help='video format code, specifiy the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported') + help='video format code, specify the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported') video_format.add_option('--all-formats', action='store_const', dest='format', help='download all available video formats', const='all') video_format.add_option('--prefer-free-formats', @@ -304,6 +306,9 @@ def parseOpts(overrideArguments=None): verbosity.add_option('--get-format', action='store_true', dest='getformat', help='simulate, quiet but print output format', default=False) + verbosity.add_option('-j', '--dump-json', + action='store_true', dest='dumpjson', + help='simulate, quiet but print JSON information', default=False) verbosity.add_option('--newline', action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False) verbosity.add_option('--no-progress', @@ -318,7 +323,7 @@ def parseOpts(overrideArguments=None): 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') + help='Write downloaded intermediary pages to files in the current directory to debug problems') verbosity.add_option('--youtube-print-sig-code', action='store_true', dest='youtube_print_sig_code', default=False, help=optparse.SUPPRESS_HELP) @@ -416,6 +421,8 @@ def parseOpts(overrideArguments=None): if opts.verbose: write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n') else: + systemConf = _readOptions('/etc/youtube-dl.conf') + xdg_config_home = os.environ.get('XDG_CONFIG_HOME') if xdg_config_home: userConfFile = os.path.join(xdg_config_home, 'youtube-dl', 'config') @@ -425,8 +432,31 @@ def parseOpts(overrideArguments=None): userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl', 'config') if not os.path.isfile(userConfFile): userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf') - systemConf = _readOptions('/etc/youtube-dl.conf') - userConf = _readOptions(userConfFile) + userConf = _readOptions(userConfFile, None) + + if userConf is None: + appdata_dir = os.environ.get('appdata') + if appdata_dir: + userConf = _readOptions( + os.path.join(appdata_dir, 'youtube-dl', 'config'), + default=None) + if userConf is None: + userConf = _readOptions( + os.path.join(appdata_dir, 'youtube-dl', 'config.txt'), + default=None) + + if userConf is None: + userConf = _readOptions( + os.path.join(os.path.expanduser('~'), 'youtube-dl.conf'), + default=None) + if userConf is None: + userConf = _readOptions( + os.path.join(os.path.expanduser('~'), 'youtube-dl.conf.txt'), + default=None) + + if userConf is None: + userConf = [] + commandLineConf = sys.argv[1:] argv = systemConf + userConf + commandLineConf opts, args = parser.parse_args(argv) @@ -445,19 +475,6 @@ def _real_main(argv=None): parser, opts, args = parseOpts(argv) - # Open appropriate CookieJar - if opts.cookiefile is None: - jar = compat_cookiejar.CookieJar() - else: - try: - jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile) - if os.access(opts.cookiefile, os.R_OK): - jar.load() - except (IOError, OSError) as err: - if opts.verbose: - traceback.print_exc() - write_string(u'ERROR: unable to open cookie file\n') - sys.exit(101) # Set user agent if opts.user_agent is not None: std_headers['User-Agent'] = opts.user_agent @@ -489,8 +506,6 @@ def _real_main(argv=None): all_urls = batchurls + args all_urls = [url.strip() for url in all_urls] - opener = _setup_opener(jar=jar, opts=opts) - extractors = gen_extractors() if opts.list_extractors: @@ -545,7 +560,7 @@ def _real_main(argv=None): if opts.retries is not None: try: opts.retries = int(opts.retries) - except (TypeError, ValueError) as err: + except (TypeError, ValueError): parser.error(u'invalid retry count specified') if opts.buffersize is not None: numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize) @@ -556,13 +571,13 @@ def _real_main(argv=None): opts.playliststart = int(opts.playliststart) if opts.playliststart <= 0: raise ValueError(u'Playlist start must be positive') - except (TypeError, ValueError) as err: + except (TypeError, ValueError): parser.error(u'invalid playlist start number specified') try: opts.playlistend = int(opts.playlistend) if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart): raise ValueError(u'Playlist end must be greater than playlist start') - except (TypeError, ValueError) as err: + except (TypeError, ValueError): parser.error(u'invalid playlist end number specified') if opts.extractaudio: if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']: @@ -601,13 +616,12 @@ def _real_main(argv=None): u' file! Use "%%(ext)s" instead of %r' % determine_ext(outtmpl, u'')) - # YoutubeDL - ydl = YoutubeDL({ + ydl_opts = { 'usenetrc': opts.usenetrc, 'username': opts.username, 'password': opts.password, 'videopassword': opts.videopassword, - 'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat), + 'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat or opts.dumpjson), 'forceurl': opts.geturl, 'forcetitle': opts.gettitle, 'forceid': opts.getid, @@ -615,8 +629,9 @@ def _real_main(argv=None): 'forcedescription': opts.getdescription, 'forcefilename': opts.getfilename, 'forceformat': opts.getformat, + 'forcejson': opts.dumpjson, 'simulate': opts.simulate, - 'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat), + 'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat or opts.dumpjson), 'format': opts.format, 'format_limit': opts.format_limit, 'listformats': opts.listformats, @@ -665,102 +680,47 @@ def _real_main(argv=None): 'youtube_print_sig_code': opts.youtube_print_sig_code, 'age_limit': opts.age_limit, 'download_archive': opts.download_archive, - }) - - if opts.verbose: - write_string(u'[debug] youtube-dl version ' + __version__ + u'\n') - try: - sp = subprocess.Popen( - ['git', 'rev-parse', '--short', 'HEAD'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd=os.path.dirname(os.path.abspath(__file__))) - out, err = sp.communicate() - out = out.decode().strip() - if re.match('[0-9a-f]+', out): - write_string(u'[debug] Git HEAD: ' + out + u'\n') - except: - try: - sys.exc_clear() - except: - pass - write_string(u'[debug] Python version %s - %s' %(platform.python_version(), platform_name()) + u'\n') - - proxy_map = {} - for handler in opener.handlers: - if hasattr(handler, 'proxies'): - proxy_map.update(handler.proxies) - write_string(u'[debug] Proxy map: ' + compat_str(proxy_map) + u'\n') - - ydl.add_default_info_extractors() - - # PostProcessors - # Add the metadata pp first, the other pps will copy it - if opts.addmetadata: - ydl.add_post_processor(FFmpegMetadataPP()) - if opts.extractaudio: - ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites)) - if opts.recodevideo: - ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo)) - if opts.embedsubtitles: - ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat)) - - # Update version - if opts.update_self: - update_self(ydl.to_screen, opts.verbose) - - # Maybe do nothing - if len(all_urls) < 1: - if not opts.update_self: - parser.error(u'you must provide at least one URL') - else: - sys.exit() + 'cookiefile': opts.cookiefile, + 'nocheckcertificate': opts.no_check_certificate, + 'proxy': opts.proxy, + 'socket_timeout': opts.socket_timeout, + } - try: - retcode = ydl.download(all_urls) - except MaxDownloadsReached: - ydl.to_screen(u'--max-download limit reached, aborting.') - retcode = 101 + with YoutubeDL(ydl_opts) as ydl: + ydl.print_debug_header() + ydl.add_default_info_extractors() + + # PostProcessors + # Add the metadata pp first, the other pps will copy it + if opts.addmetadata: + ydl.add_post_processor(FFmpegMetadataPP()) + if opts.extractaudio: + ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites)) + if opts.recodevideo: + ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo)) + if opts.embedsubtitles: + ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat)) + + # Update version + if opts.update_self: + update_self(ydl.to_screen, opts.verbose) + + # Maybe do nothing + if len(all_urls) < 1: + if not opts.update_self: + parser.error(u'you must provide at least one URL') + else: + sys.exit() - # Dump cookie jar if requested - if opts.cookiefile is not None: try: - jar.save() - except (IOError, OSError): - sys.exit(u'ERROR: unable to save cookie jar') + retcode = ydl.download(all_urls) + except MaxDownloadsReached: + ydl.to_screen(u'--max-download limit reached, aborting.') + retcode = 101 sys.exit(retcode) -def _setup_opener(jar=None, opts=None, timeout=300): - if opts is None: - FakeOptions = collections.namedtuple( - 'FakeOptions', ['proxy', 'no_check_certificate']) - opts = FakeOptions(proxy=None, no_check_certificate=False) - - cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar) - if opts.proxy is not None: - if opts.proxy == '': - proxies = {} - else: - proxies = {'http': opts.proxy, 'https': opts.proxy} - else: - proxies = compat_urllib_request.getproxies() - # Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805) - if 'http' in proxies and 'https' not in proxies: - proxies['https'] = proxies['http'] - proxy_handler = compat_urllib_request.ProxyHandler(proxies) - https_handler = make_HTTPS_handler(opts) - opener = compat_urllib_request.build_opener( - https_handler, proxy_handler, cookie_processor, YoutubeDLHandler()) - # Delete the default user-agent header, which would otherwise apply in - # cases where our custom HTTP handler doesn't come into play - # (See https://github.com/rg3/youtube-dl/issues/1309 for details) - opener.addheaders = [] - compat_urllib_request.install_opener(opener) - socket.setdefaulttimeout(timeout) - return opener - - def main(argv=None): try: _real_main(argv) diff --git a/youtube_dl/extractor/__init__.py b/youtube_dl/extractor/__init__.py index f9caca4..bd99648 100644 --- a/youtube_dl/extractor/__init__.py +++ b/youtube_dl/extractor/__init__.py @@ -1,5 +1,6 @@ from .appletrailers import AppleTrailersIE from .addanime import AddAnimeIE +from .anitube import AnitubeIE from .archiveorg import ArchiveOrgIE from .ard import ARDIE from .arte import ( @@ -10,7 +11,7 @@ from .arte import ( ) from .auengine import AUEngineIE from .bambuser import BambuserIE, BambuserChannelIE -from .bandcamp import BandcampIE +from .bandcamp import BandcampIE, BandcampAlbumIE from .bliptv import BlipTVIE, BlipTVUserIE from .bloomberg import BloombergIE from .breakcom import BreakIE @@ -19,12 +20,15 @@ from .c56 import C56IE from .canalplus import CanalplusIE from .canalc2 import Canalc2IE from .cinemassacre import CinemassacreIE +from .clipfish import ClipfishIE +from .clipsyndicate import ClipsyndicateIE from .cnn import CNNIE from .collegehumor import CollegeHumorIE -from .comedycentral import ComedyCentralIE +from .comedycentral import ComedyCentralIE, ComedyCentralShowsIE from .condenast import CondeNastIE from .criterion import CriterionIE from .cspan import CSpanIE +from .d8 import D8IE from .dailymotion import ( DailymotionIE, DailymotionPlaylistIE, @@ -57,6 +61,7 @@ from .francetv import ( ) from .freesound import FreesoundIE from .funnyordie import FunnyOrDieIE +from .gamekings import GamekingsIE from .gamespot import GameSpotIE from .gametrailers import GametrailersIE from .generic import GenericIE @@ -67,6 +72,7 @@ from .hotnewhiphop import HotNewHipHopIE from .howcast import HowcastIE from .hypem import HypemIE from .ign import IGNIE, OneUPIE +from .imdb import ImdbIE from .ina import InaIE from .infoq import InfoQIE from .instagram import InstagramIE @@ -79,7 +85,7 @@ from .keezmovies import KeezMoviesIE from .kickstarter import KickStarterIE from .keek import KeekIE from .liveleak import LiveLeakIE -from .livestream import LivestreamIE +from .livestream import LivestreamIE, LivestreamOriginalIE from .metacafe import MetacafeIE from .metacritic import MetacriticIE from .mit import TechTVMITIE, MITIE @@ -95,11 +101,13 @@ from .nba import NBAIE from .nbc import NBCNewsIE from .newgrounds import NewgroundsIE from .nhl import NHLIE, NHLVideocenterIE +from .niconico import NiconicoIE from .nowvideo import NowVideoIE from .ooyala import OoyalaIE from .orf import ORFIE from .pbs import PBSIE from .photobucket import PhotobucketIE +from .podomatic import PodomaticIE from .pornhub import PornHubIE from .pornotube import PornotubeIE from .rbmaradio import RBMARadioIE @@ -113,27 +121,38 @@ from .rutube import RutubeIE from .sina import SinaIE from .slashdot import SlashdotIE from .slideshare import SlideshareIE +from .smotri import ( + SmotriIE, + SmotriCommunityIE, + SmotriUserIE, +) from .sohu import SohuIE from .soundcloud import SoundcloudIE, SoundcloudSetIE, SoundcloudUserIE -from .southparkstudios import SouthParkStudiosIE +from .southparkstudios import ( + SouthParkStudiosIE, + SouthparkDeIE, +) from .space import SpaceIE from .spankwire import SpankwireIE from .spiegel import SpiegelIE from .stanfordoc import StanfordOpenClassroomIE from .statigram import StatigramIE from .steam import SteamIE +from .streamcloud import StreamcloudIE from .sztvhu import SztvHuIE from .teamcoco import TeamcocoIE from .techtalks import TechTalksIE from .ted import TEDIE from .tf1 import TF1IE from .thisav import ThisAVIE +from .toutv import TouTvIE from .traileraddict import TrailerAddictIE from .trilulilu import TriluliluIE from .tube8 import Tube8IE from .tudou import TudouIE from .tumblr import TumblrIE from .tutv import TutvIE +from .tvp import TvpIE from .unistra import UnistraIE from .ustream import UstreamIE, UstreamChannelIE from .vbox7 import Vbox7IE @@ -145,8 +164,13 @@ from .viddler import ViddlerIE from .videodetective import VideoDetectiveIE from .videofyme import VideofyMeIE from .videopremium import VideoPremiumIE -from .vimeo import VimeoIE, VimeoChannelIE +from .vimeo import ( + VimeoIE, + VimeoChannelIE, + VimeoUserIE, +) from .vine import VineIE +from .viki import VikiIE from .vk import VKIE from .wat import WatIE from .websurg import WeBSurgIE @@ -157,7 +181,11 @@ from .xhamster import XHamsterIE from .xnxx import XNXXIE from .xvideos import XVideosIE from .xtube import XTubeIE -from .yahoo import YahooIE, YahooSearchIE +from .yahoo import ( + YahooIE, + YahooNewsIE, + YahooSearchIE, +) from .youjizz import YouJizzIE from .youku import YoukuIE from .youporn import YouPornIE @@ -174,6 +202,7 @@ from .youtube import ( YoutubeTruncatedURLIE, YoutubeWatchLaterIE, YoutubeFavouritesIE, + YoutubeHistoryIE, ) from .zdf import ZDFIE diff --git a/youtube_dl/extractor/anitube.py b/youtube_dl/extractor/anitube.py new file mode 100644 index 0000000..2b019da --- /dev/null +++ b/youtube_dl/extractor/anitube.py @@ -0,0 +1,53 @@ +import re + +from .common import InfoExtractor + + +class AnitubeIE(InfoExtractor): + IE_NAME = u'anitube.se' + _VALID_URL = r'https?://(?:www\.)?anitube\.se/video/(?P\d+)' + + _TEST = { + u'url': u'http://www.anitube.se/video/36621', + u'md5': u'59d0eeae28ea0bc8c05e7af429998d43', + u'file': u'36621.mp4', + u'info_dict': { + u'id': u'36621', + u'ext': u'mp4', + u'title': u'Recorder to Randoseru 01', + }, + u'skip': u'Blocked in the US', + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + + webpage = self._download_webpage(url, video_id) + key = self._html_search_regex(r'http://www\.anitube\.se/embed/([A-Za-z0-9_-]*)', + webpage, u'key') + + config_xml = self._download_xml('http://www.anitube.se/nuevo/econfig.php?key=%s' % key, + key) + + video_title = config_xml.find('title').text + + formats = [] + video_url = config_xml.find('file') + if video_url is not None: + formats.append({ + 'format_id': 'sd', + 'url': video_url.text, + }) + video_url = config_xml.find('filehd') + if video_url is not None: + formats.append({ + 'format_id': 'hd', + 'url': video_url.text, + }) + + return { + 'id': video_id, + 'title': video_title, + 'formats': formats + } diff --git a/youtube_dl/extractor/appletrailers.py b/youtube_dl/extractor/appletrailers.py index 6d6237f..4befff3 100644 --- a/youtube_dl/extractor/appletrailers.py +++ b/youtube_dl/extractor/appletrailers.py @@ -113,7 +113,7 @@ class AppleTrailersIE(InfoExtractor): }) formats = sorted(formats, key=lambda f: (f['height'], f['width'])) - info = { + playlist.append({ '_type': 'video', 'id': video_id, 'title': title, @@ -124,12 +124,7 @@ class AppleTrailersIE(InfoExtractor): 'upload_date': upload_date, 'uploader_id': uploader_id, 'user_agent': 'QuickTime compatible (youtube-dl)', - } - # TODO: Remove when #980 has been merged - info['url'] = formats[-1]['url'] - info['ext'] = formats[-1]['ext'] - - playlist.append(info) + }) return { '_type': 'playlist', diff --git a/youtube_dl/extractor/archiveorg.py b/youtube_dl/extractor/archiveorg.py index 61ce446..3ae0aeb 100644 --- a/youtube_dl/extractor/archiveorg.py +++ b/youtube_dl/extractor/archiveorg.py @@ -49,7 +49,7 @@ class ArchiveOrgIE(InfoExtractor): for f in formats: f['ext'] = determine_ext(f['url']) - info = { + return { '_type': 'video', 'id': video_id, 'title': title, @@ -57,12 +57,5 @@ class ArchiveOrgIE(InfoExtractor): 'description': description, 'uploader': uploader, 'upload_date': upload_date, + 'thumbnail': data.get('misc', {}).get('image'), } - thumbnail = data.get('misc', {}).get('image') - if thumbnail: - info['thumbnail'] = thumbnail - - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - - return info diff --git a/youtube_dl/extractor/arte.py b/youtube_dl/extractor/arte.py index b35a679..8b62ee7 100644 --- a/youtube_dl/extractor/arte.py +++ b/youtube_dl/extractor/arte.py @@ -1,7 +1,6 @@ # encoding: utf-8 import re import json -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -69,7 +68,7 @@ class ArteTvIE(InfoExtractor): lang = mobj.group('lang') return self._extract_liveweb(url, name, lang) - if re.search(self._LIVE_URL, video_id) is not None: + if re.search(self._LIVE_URL, url) is not None: raise ExtractorError(u'Arte live streams are not yet supported, sorry') # self.extractLiveStream(url) # return @@ -78,8 +77,7 @@ class ArteTvIE(InfoExtractor): """Extract from videos.arte.tv""" ref_xml_url = url.replace('/videos/', '/do_delegate/videos/') ref_xml_url = ref_xml_url.replace('.html', ',view,asPlayerXml.xml') - ref_xml = self._download_webpage(ref_xml_url, video_id, note=u'Downloading metadata') - ref_xml_doc = xml.etree.ElementTree.fromstring(ref_xml) + ref_xml_doc = self._download_xml(ref_xml_url, video_id, note=u'Downloading metadata') config_node = find_xpath_attr(ref_xml_doc, './/video', 'lang', lang) config_xml_url = config_node.attrib['ref'] config_xml = self._download_webpage(config_xml_url, video_id, note=u'Downloading configuration') @@ -109,13 +107,12 @@ class ArteTvIE(InfoExtractor): """Extract form http://liveweb.arte.tv/""" webpage = self._download_webpage(url, name) video_id = self._search_regex(r'eventId=(\d+?)("|&)', webpage, u'event id') - config_xml = self._download_webpage('http://download.liveweb.arte.tv/o21/liveweb/events/event-%s.xml' % video_id, + config_doc = self._download_xml('http://download.liveweb.arte.tv/o21/liveweb/events/event-%s.xml' % video_id, video_id, u'Downloading information') - config_doc = xml.etree.ElementTree.fromstring(config_xml.encode('utf-8')) event_doc = config_doc.find('event') url_node = event_doc.find('video').find('urlHd') if url_node is None: - url_node = video_doc.find('urlSd') + url_node = event_doc.find('urlSd') return {'id': video_id, 'title': event_doc.find('name%s' % lang.capitalize()).text, diff --git a/youtube_dl/extractor/auengine.py b/youtube_dl/extractor/auengine.py index 0febbff..95c0380 100644 --- a/youtube_dl/extractor/auengine.py +++ b/youtube_dl/extractor/auengine.py @@ -1,10 +1,10 @@ -import os.path import re from .common import InfoExtractor from ..utils import ( compat_urllib_parse, - compat_urllib_parse_urlparse, + determine_ext, + ExtractorError, ) class AUEngineIE(InfoExtractor): @@ -25,22 +25,25 @@ class AUEngineIE(InfoExtractor): title = self._html_search_regex(r'(?P<title>.+?)', webpage, u'title') title = title.strip() - links = re.findall(r'[^A-Za-z0-9]?(?:file|url):\s*["\'](http[^\'"&]*)', webpage) - links = [compat_urllib_parse.unquote(l) for l in links] + links = re.findall(r'\s(?:file|url):\s*["\']([^\'"]+)["\']', webpage) + links = map(compat_urllib_parse.unquote, links) + + thumbnail = None + video_url = None for link in links: - root, pathext = os.path.splitext(compat_urllib_parse_urlparse(link).path) - if pathext == '.png': + if link.endswith('.png'): thumbnail = link - elif pathext == '.mp4': - url = link - ext = pathext + elif '/videos/' in link: + video_url = link + if not video_url: + raise ExtractorError(u'Could not find video URL') + ext = u'.' + determine_ext(video_url) if ext == title[-len(ext):]: title = title[:-len(ext)] - ext = ext[1:] - return [{ + + return { 'id': video_id, - 'url': url, - 'ext': ext, + 'url': video_url, 'title': title, 'thumbnail': thumbnail, - }] + } diff --git a/youtube_dl/extractor/bambuser.py b/youtube_dl/extractor/bambuser.py index f3b36f4..b80508e 100644 --- a/youtube_dl/extractor/bambuser.py +++ b/youtube_dl/extractor/bambuser.py @@ -15,7 +15,8 @@ class BambuserIE(InfoExtractor): _TEST = { u'url': u'http://bambuser.com/v/4050584', - u'md5': u'fba8f7693e48fd4e8641b3fd5539a641', + # MD5 seems to be flaky, see https://travis-ci.org/rg3/youtube-dl/jobs/14051016#L388 + #u'md5': u'fba8f7693e48fd4e8641b3fd5539a641', u'info_dict': { u'id': u'4050584', u'ext': u'flv', @@ -24,6 +25,11 @@ class BambuserIE(InfoExtractor): u'uploader': u'pixelversity', u'uploader_id': u'344706', }, + u'params': { + # It doesn't respect the 'Range' header, it would download the whole video + # caused the travis builds to fail: https://travis-ci.org/rg3/youtube-dl/jobs/14493845#L59 + u'skip_download': True, + }, } def _real_extract(self, url): diff --git a/youtube_dl/extractor/bandcamp.py b/youtube_dl/extractor/bandcamp.py index 129a20f..3a32c14 100644 --- a/youtube_dl/extractor/bandcamp.py +++ b/youtube_dl/extractor/bandcamp.py @@ -3,13 +3,16 @@ import re from .common import InfoExtractor from ..utils import ( + compat_str, + compat_urlparse, ExtractorError, ) class BandcampIE(InfoExtractor): + IE_NAME = u'Bandcamp' _VALID_URL = r'http://.*?\.bandcamp\.com/track/(?P.*)' - _TEST = { + _TESTS = [{ u'url': u'http://youtube-dl.bandcamp.com/track/youtube-dl-test-song', u'file': u'1812978515.mp3', u'md5': u'cdeb30cdae1921719a3cbcab696ef53c', @@ -17,7 +20,7 @@ class BandcampIE(InfoExtractor): u"title": u"youtube-dl test song \"'/\\\u00e4\u21ad" }, u'skip': u'There is a limit of 200 free downloads / month for the test song' - } + }] def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) @@ -26,6 +29,23 @@ class BandcampIE(InfoExtractor): # We get the link to the free download page m_download = re.search(r'freeDownloadPage: "(.*?)"', webpage) if m_download is None: + m_trackinfo = re.search(r'trackinfo: (.+),\s*?\n', webpage) + if m_trackinfo: + json_code = m_trackinfo.group(1) + data = json.loads(json_code) + + for d in data: + formats = [{ + 'format_id': 'format_id', + 'url': format_url, + 'ext': format_id.partition('-')[0] + } for format_id, format_url in sorted(d['file'].items())] + return { + 'id': compat_str(d['id']), + 'title': d['title'], + 'formats': formats, + } + else: raise ExtractorError(u'No free songs found') download_link = m_download.group(1) @@ -61,3 +81,49 @@ class BandcampIE(InfoExtractor): } return [track_info] + + +class BandcampAlbumIE(InfoExtractor): + IE_NAME = u'Bandcamp:album' + _VALID_URL = r'http://.*?\.bandcamp\.com/album/(?P<title>.*)' + + _TEST = { + u'url': u'http://blazo.bandcamp.com/album/jazz-format-mixtape-vol-1', + u'playlist': [ + { + u'file': u'1353101989.mp3', + u'md5': u'39bc1eded3476e927c724321ddf116cf', + u'info_dict': { + u'title': u'Intro', + } + }, + { + u'file': u'38097443.mp3', + u'md5': u'1a2c32e2691474643e912cc6cd4bffaa', + u'info_dict': { + u'title': u'Kero One - Keep It Alive (Blazo remix)', + } + }, + ], + u'params': { + u'playlistend': 2 + }, + u'skip': u'Bancamp imposes download limits. See test_playlists:test_bandcamp_album for the playlist test' + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + title = mobj.group('title') + webpage = self._download_webpage(url, title) + tracks_paths = re.findall(r'<a href="(.*?)" itemprop="url">', webpage) + if not tracks_paths: + raise ExtractorError(u'The page doesn\'t contain any track') + entries = [ + self.url_result(compat_urlparse.urljoin(url, t_path), ie=BandcampIE.ie_key()) + for t_path in tracks_paths] + title = self._search_regex(r'album_title : "(.*?)"', webpage, u'title') + return { + '_type': 'playlist', + 'title': title, + 'entries': entries, + } diff --git a/youtube_dl/extractor/brightcove.py b/youtube_dl/extractor/brightcove.py index d8c3546..66fe0ac 100644 --- a/youtube_dl/extractor/brightcove.py +++ b/youtube_dl/extractor/brightcove.py @@ -75,16 +75,22 @@ class BrightcoveIE(InfoExtractor): params = {'flashID': object_doc.attrib['id'], 'playerID': find_xpath_attr(object_doc, './param', 'name', 'playerID').attrib['value'], } - playerKey = find_xpath_attr(object_doc, './param', 'name', 'playerKey') + def find_param(name): + node = find_xpath_attr(object_doc, './param', 'name', name) + if node is not None: + return node.attrib['value'] + return None + playerKey = find_param('playerKey') # Not all pages define this value if playerKey is not None: - params['playerKey'] = playerKey.attrib['value'] - videoPlayer = find_xpath_attr(object_doc, './param', 'name', '@videoPlayer') + params['playerKey'] = playerKey + # The three fields hold the id of the video + videoPlayer = find_param('@videoPlayer') or find_param('videoId') or find_param('videoID') if videoPlayer is not None: - params['@videoPlayer'] = videoPlayer.attrib['value'] - linkBase = find_xpath_attr(object_doc, './param', 'name', 'linkBaseURL') + params['@videoPlayer'] = videoPlayer + linkBase = find_param('linkBaseURL') if linkBase is not None: - params['linkBaseURL'] = linkBase.attrib['value'] + params['linkBaseURL'] = linkBase data = compat_urllib_parse.urlencode(params) return cls._FEDERATED_URL_TEMPLATE % data diff --git a/youtube_dl/extractor/canalplus.py b/youtube_dl/extractor/canalplus.py index 1db9b24..7cdcd83 100644 --- a/youtube_dl/extractor/canalplus.py +++ b/youtube_dl/extractor/canalplus.py @@ -1,10 +1,10 @@ # encoding: utf-8 import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import unified_strdate + class CanalplusIE(InfoExtractor): _VALID_URL = r'https?://(www\.canalplus\.fr/.*?/(?P<path>.*)|player\.canalplus\.fr/#/(?P<id>\d+))' _VIDEO_INFO_TEMPLATE = 'http://service.canal-plus.com/video/rest/getVideosLiees/cplus/%s' @@ -25,16 +25,15 @@ class CanalplusIE(InfoExtractor): def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) - video_id = mobj.group('id') + video_id = mobj.groupdict().get('id') if video_id is None: webpage = self._download_webpage(url, mobj.group('path')) video_id = self._search_regex(r'videoId = "(\d+)";', webpage, u'video id') info_url = self._VIDEO_INFO_TEMPLATE % video_id - info_page = self._download_webpage(info_url,video_id, + doc = self._download_xml(info_url,video_id, u'Downloading video info') self.report_extraction(video_id) - doc = xml.etree.ElementTree.fromstring(info_page.encode('utf-8')) video_info = [video for video in doc if video.find('ID').text == video_id][0] infos = video_info.find('INFOS') media = video_info.find('MEDIA') diff --git a/youtube_dl/extractor/clipfish.py b/youtube_dl/extractor/clipfish.py new file mode 100644 index 0000000..43efb08 --- /dev/null +++ b/youtube_dl/extractor/clipfish.py @@ -0,0 +1,58 @@ +import re +import time +import xml.etree.ElementTree + +from .common import InfoExtractor +from ..utils import ExtractorError + + +class ClipfishIE(InfoExtractor): + IE_NAME = u'clipfish' + + _VALID_URL = r'^https?://(?:www\.)?clipfish\.de/.*?/video/(?P<id>[0-9]+)/' + _TEST = { + u'url': u'http://www.clipfish.de/special/game-trailer/video/3966754/fifa-14-e3-2013-trailer/', + u'file': u'3966754.mp4', + u'md5': u'2521cd644e862936cf2e698206e47385', + u'info_dict': { + u'title': u'FIFA 14 - E3 2013 Trailer', + u'duration': 82, + }, + u'skip': 'Blocked in the US' + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group(1) + + info_url = ('http://www.clipfish.de/devxml/videoinfo/%s?ts=%d' % + (video_id, int(time.time()))) + doc = self._download_xml( + info_url, video_id, note=u'Downloading info page') + title = doc.find('title').text + video_url = doc.find('filename').text + if video_url is None: + xml_bytes = xml.etree.ElementTree.tostring(doc) + raise ExtractorError(u'Cannot find video URL in document %r' % + xml_bytes) + thumbnail = doc.find('imageurl').text + duration_str = doc.find('duration').text + m = re.match( + r'^(?P<hours>[0-9]+):(?P<minutes>[0-9]{2}):(?P<seconds>[0-9]{2}):(?P<ms>[0-9]*)$', + duration_str) + if m: + duration = ( + (int(m.group('hours')) * 60 * 60) + + (int(m.group('minutes')) * 60) + + (int(m.group('seconds'))) + ) + else: + duration = None + + return { + 'id': video_id, + 'title': title, + 'url': video_url, + 'thumbnail': thumbnail, + 'duration': duration, + } diff --git a/youtube_dl/extractor/clipsyndicate.py b/youtube_dl/extractor/clipsyndicate.py new file mode 100644 index 0000000..d4fc869 --- /dev/null +++ b/youtube_dl/extractor/clipsyndicate.py @@ -0,0 +1,52 @@ +import re +import xml.etree.ElementTree + +from .common import InfoExtractor +from ..utils import ( + find_xpath_attr, +) + + +class ClipsyndicateIE(InfoExtractor): + _VALID_URL = r'http://www\.clipsyndicate\.com/video/play(list/\d+)?/(?P<id>\d+)' + + _TEST = { + u'url': u'http://www.clipsyndicate.com/video/play/4629301/brick_briscoe', + u'md5': u'4d7d549451bad625e0ff3d7bd56d776c', + u'info_dict': { + u'id': u'4629301', + u'ext': u'mp4', + u'title': u'Brick Briscoe', + u'duration': 612, + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + js_player = self._download_webpage( + 'http://eplayer.clipsyndicate.com/embed/player.js?va_id=%s' % video_id, + video_id, u'Downlaoding player') + # it includes a required token + flvars = self._search_regex(r'flvars: "(.*?)"', js_player, u'flvars') + + playlist_page = self._download_webpage( + 'http://eplayer.clipsyndicate.com/osmf/playlist?%s' % flvars, + video_id, u'Downloading video info') + # Fix broken xml + playlist_page = re.sub('&', '&', playlist_page) + pdoc = xml.etree.ElementTree.fromstring(playlist_page.encode('utf-8')) + + track_doc = pdoc.find('trackList/track') + def find_param(name): + node = find_xpath_attr(track_doc, './/param', 'name', name) + if node is not None: + return node.attrib['value'] + + return { + 'id': video_id, + 'title': find_param('title'), + 'url': track_doc.find('location').text, + 'thumbnail': find_param('thumbnail'), + 'duration': int(find_param('duration')), + } diff --git a/youtube_dl/extractor/cnn.py b/youtube_dl/extractor/cnn.py index 34adf6d..a034bb2 100644 --- a/youtube_dl/extractor/cnn.py +++ b/youtube_dl/extractor/cnn.py @@ -1,5 +1,4 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import determine_ext @@ -33,8 +32,7 @@ class CNNIE(InfoExtractor): path = mobj.group('path') page_title = mobj.group('title') info_url = u'http://cnn.com/video/data/3.0/%s/index.xml' % path - info_xml = self._download_webpage(info_url, page_title) - info = xml.etree.ElementTree.fromstring(info_xml.encode('utf-8')) + info = self._download_xml(info_url, page_title) formats = [] for f in info.findall('files/file'): diff --git a/youtube_dl/extractor/collegehumor.py b/youtube_dl/extractor/collegehumor.py index 8d4c93d..b27c1df 100644 --- a/youtube_dl/extractor/collegehumor.py +++ b/youtube_dl/extractor/collegehumor.py @@ -1,5 +1,4 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -46,11 +45,10 @@ class CollegeHumorIE(InfoExtractor): self.report_extraction(video_id) xmlUrl = 'http://www.collegehumor.com/moogaloop/video/' + video_id - metaXml = self._download_webpage(xmlUrl, video_id, + mdoc = self._download_xml(xmlUrl, video_id, u'Downloading info XML', u'Unable to download video info XML') - mdoc = xml.etree.ElementTree.fromstring(metaXml) try: videoNode = mdoc.findall('./video')[0] youtubeIdNode = videoNode.find('./youtubeID') @@ -65,16 +63,13 @@ class CollegeHumorIE(InfoExtractor): if next_url.endswith(u'manifest.f4m'): manifest_url = next_url + '?hdcore=2.10.3' - manifestXml = self._download_webpage(manifest_url, video_id, + adoc = self._download_xml(manifest_url, video_id, u'Downloading XML manifest', u'Unable to download video info XML') - adoc = xml.etree.ElementTree.fromstring(manifestXml) try: - media_node = adoc.findall('./{http://ns.adobe.com/f4m/1.0}media')[0] - node_id = media_node.attrib['url'] video_id = adoc.findall('./{http://ns.adobe.com/f4m/1.0}id')[0].text - except IndexError as err: + except IndexError: raise ExtractorError(u'Invalid manifest file') url_pr = compat_urllib_parse_urlparse(info['thumbnail']) info['url'] = url_pr.scheme + '://' + url_pr.netloc + video_id[:-2].replace('.csmil','').replace(',','') diff --git a/youtube_dl/extractor/comedycentral.py b/youtube_dl/extractor/comedycentral.py index 69b2bee..53579aa 100644 --- a/youtube_dl/extractor/comedycentral.py +++ b/youtube_dl/extractor/comedycentral.py @@ -1,7 +1,7 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor +from .mtv import MTVServicesInfoExtractor from ..utils import ( compat_str, compat_urllib_parse, @@ -11,7 +11,31 @@ from ..utils import ( ) -class ComedyCentralIE(InfoExtractor): +class ComedyCentralIE(MTVServicesInfoExtractor): + _VALID_URL = r'http://www.comedycentral.com/(video-clips|episodes|cc-studios)/(?P<title>.*)' + _FEED_URL = u'http://comedycentral.com/feeds/mrss/' + + _TEST = { + u'url': u'http://www.comedycentral.com/video-clips/kllhuv/stand-up-greg-fitzsimmons--uncensored---too-good-of-a-mother', + u'md5': u'4167875aae411f903b751a21f357f1ee', + u'info_dict': { + u'id': u'cef0cbb3-e776-4bc9-b62e-8016deccb354', + u'ext': u'mp4', + u'title': u'Uncensored - Greg Fitzsimmons - Too Good of a Mother', + u'description': u'After a certain point, breastfeeding becomes c**kblocking.', + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + title = mobj.group('title') + webpage = self._download_webpage(url, title) + mgid = self._search_regex(r'data-mgid="(?P<mgid>mgid:.*?)"', + webpage, u'mgid') + return self._get_videos_info(mgid) + + +class ComedyCentralShowsIE(InfoExtractor): IE_DESC = u'The Daily Show / Colbert Report' # urls can be abbreviations like :thedailyshow or :colbert # urls for episodes like: @@ -127,13 +151,12 @@ class ComedyCentralIE(InfoExtractor): uri = mMovieParams[0][1] indexUrl = 'http://shadow.comedycentral.com/feeds/video_player/mrss/?' + compat_urllib_parse.urlencode({'uri': uri}) - indexXml = self._download_webpage(indexUrl, epTitle, + idoc = self._download_xml(indexUrl, epTitle, u'Downloading show index', u'unable to download episode index') results = [] - idoc = xml.etree.ElementTree.fromstring(indexXml) itemEls = idoc.findall('.//item') for partNum,itemEl in enumerate(itemEls): mediaId = itemEl.findall('./guid')[0].text @@ -144,10 +167,9 @@ class ComedyCentralIE(InfoExtractor): configUrl = ('http://www.comedycentral.com/global/feeds/entertainment/media/mediaGenEntertainment.jhtml?' + compat_urllib_parse.urlencode({'uri': mediaId})) - configXml = self._download_webpage(configUrl, epTitle, + cdoc = self._download_xml(configUrl, epTitle, u'Downloading configuration for %s' % shortMediaId) - cdoc = xml.etree.ElementTree.fromstring(configXml) turls = [] for rendition in cdoc.findall('.//rendition'): finfo = (rendition.attrib['bitrate'], rendition.findall('./src')[0].text) @@ -169,7 +191,7 @@ class ComedyCentralIE(InfoExtractor): }) effTitle = showId + u'-' + epTitle + u' part ' + compat_str(partNum+1) - info = { + results.append({ 'id': shortMediaId, 'formats': formats, 'uploader': showId, @@ -177,11 +199,6 @@ class ComedyCentralIE(InfoExtractor): 'title': effTitle, 'thumbnail': None, 'description': compat_str(officialTitle), - } - - # TODO: Remove when #980 has been merged - info.update(info['formats'][-1]) - - results.append(info) + }) return results diff --git a/youtube_dl/extractor/common.py b/youtube_dl/extractor/common.py index fb2d50a..1b04908 100644 --- a/youtube_dl/extractor/common.py +++ b/youtube_dl/extractor/common.py @@ -4,11 +4,11 @@ import re import socket import sys import netrc +import xml.etree.ElementTree from ..utils import ( compat_http_client, compat_urllib_error, - compat_urllib_request, compat_str, clean_html, @@ -19,6 +19,7 @@ from ..utils import ( unescapeHTML, ) + class InfoExtractor(object): """Information Extractor class. @@ -71,6 +72,11 @@ class InfoExtractor(object): ("3D" or "DASH video") * width Width of the video, if known * height Height of the video, if known + * abr Average audio bitrate in KBit/s + * acodec Name of the audio codec in use + * vbr Average video bitrate in KBit/s + * vcodec Name of the video codec in use + * filesize The number of bytes, if known in advance 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) @@ -152,7 +158,7 @@ class InfoExtractor(object): elif note is not False: self.to_screen(u'%s: %s' % (video_id, note)) try: - return compat_urllib_request.urlopen(url_or_request) + return self._downloader.urlopen(url_or_request) except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: if errnote is None: errnote = u'Unable to download webpage' @@ -204,6 +210,12 @@ class InfoExtractor(object): """ Returns the data of the page as a string """ return self._download_webpage_handle(url_or_request, video_id, note, errnote)[0] + def _download_xml(self, url_or_request, video_id, + note=u'Downloading XML', errnote=u'Unable to download XML'): + """Return the xml as an xml.etree.ElementTree.Element""" + xml_string = self._download_webpage(url_or_request, video_id, note, errnote) + return xml.etree.ElementTree.fromstring(xml_string.encode('utf-8')) + def to_screen(self, msg): """Print msg to screen, prefixing it with '[ie_name]'""" self._downloader.to_screen(u'[%s] %s' % (self.IE_NAME, msg)) @@ -225,12 +237,14 @@ class InfoExtractor(object): self.to_screen(u'Logging in') #Methods for following #608 - def url_result(self, url, ie=None): + def url_result(self, url, ie=None, video_id=None): """Returns a url that points to a page that should be processed""" #TODO: ie should be the class used for getting the info video_info = {'_type': 'url', 'url': url, 'ie_key': ie} + if video_id is not None: + video_info['id'] = video_id return video_info def playlist_result(self, entries, playlist_id=None, playlist_title=None): """Returns a playlist""" @@ -315,16 +329,22 @@ class InfoExtractor(object): # Helper functions for extracting OpenGraph info @staticmethod - def _og_regex(prop): - return r'<meta.+?property=[\'"]og:%s[\'"].+?content=(?:"(.+?)"|\'(.+?)\')' % re.escape(prop) + def _og_regexes(prop): + content_re = r'content=(?:"([^>]+?)"|\'(.+?)\')' + property_re = r'property=[\'"]og:%s[\'"]' % re.escape(prop) + template = r'<meta[^>]+?%s[^>]+?%s' + return [ + template % (property_re, content_re), + template % (content_re, property_re), + ] def _og_search_property(self, prop, html, name=None, **kargs): if name is None: name = 'OpenGraph %s' % prop - escaped = self._search_regex(self._og_regex(prop), html, name, flags=re.DOTALL, **kargs) - if not escaped is None: - return unescapeHTML(escaped) - return None + escaped = self._search_regex(self._og_regexes(prop), html, name, flags=re.DOTALL, **kargs) + if escaped is None: + return None + return unescapeHTML(escaped) def _og_search_thumbnail(self, html, **kargs): return self._og_search_property('image', html, u'thumbnail url', fatal=False, **kargs) @@ -336,10 +356,22 @@ class InfoExtractor(object): return self._og_search_property('title', html, **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')) + regexes = self._og_regexes('video') + if secure: regexes = self._og_regexes('video:secure_url') + regexes return self._html_search_regex(regexes, html, name, **kargs) + def _html_search_meta(self, name, html, display_name=None): + if display_name is None: + display_name = name + return self._html_search_regex( + r'''(?ix)<meta + (?=[^>]+(?:itemprop|name|property)=["\']%s["\']) + [^>]+content=["\']([^"\']+)["\']''' % re.escape(name), + html, display_name, fatal=False) + + def _dc_search_uploader(self, html): + return self._html_search_meta('dc.creator', html, 'uploader') + def _rta_search(self, html): # See http://www.rtalabel.org/index.php?content=howtofaq#single if re.search(r'(?ix)<meta\s+name="rating"\s+' @@ -348,6 +380,23 @@ class InfoExtractor(object): return 18 return 0 + def _media_rating_search(self, html): + # See http://www.tjg-designs.com/WP/metadata-code-examples-adding-metadata-to-your-web-pages/ + rating = self._html_search_meta('rating', html) + + if not rating: + return None + + RATING_TABLE = { + 'safe for kids': 0, + 'general': 8, + '14 years': 14, + 'mature': 17, + 'restricted': 19, + } + return RATING_TABLE.get(rating.lower(), None) + + class SearchInfoExtractor(InfoExtractor): """ diff --git a/youtube_dl/extractor/d8.py b/youtube_dl/extractor/d8.py new file mode 100644 index 0000000..a56842b --- /dev/null +++ b/youtube_dl/extractor/d8.py @@ -0,0 +1,22 @@ +# encoding: utf-8 +from .canalplus import CanalplusIE + + +class D8IE(CanalplusIE): + _VALID_URL = r'https?://www\.d8\.tv/.*?/(?P<path>.*)' + _VIDEO_INFO_TEMPLATE = 'http://service.canal-plus.com/video/rest/getVideosLiees/d8/%s' + IE_NAME = u'd8.tv' + + _TEST = { + u'url': u'http://www.d8.tv/d8-docs-mags/pid6589-d8-campagne-intime.html', + u'file': u'966289.flv', + u'info_dict': { + u'title': u'Campagne intime - Documentaire exceptionnel', + u'description': u'md5:d2643b799fb190846ae09c61e59a859f', + u'upload_date': u'20131108', + }, + u'params': { + # rtmp + u'skip_download': True, + }, + } diff --git a/youtube_dl/extractor/dailymotion.py b/youtube_dl/extractor/dailymotion.py index e87690f..71f5e03 100644 --- a/youtube_dl/extractor/dailymotion.py +++ b/youtube_dl/extractor/dailymotion.py @@ -186,7 +186,7 @@ class DailymotionPlaylistIE(DailymotionBaseInfoExtractor): webpage = self._download_webpage(request, id, u'Downloading page %s' % pagenum) - playlist_el = get_element_by_attribute(u'class', u'video_list', webpage) + playlist_el = get_element_by_attribute(u'class', u'row video_list', webpage) video_ids.extend(re.findall(r'data-id="(.+?)"', playlist_el)) if re.search(self._MORE_PAGES_INDICATOR, webpage, re.DOTALL) is None: diff --git a/youtube_dl/extractor/daum.py b/youtube_dl/extractor/daum.py index a804e83..d418ce4 100644 --- a/youtube_dl/extractor/daum.py +++ b/youtube_dl/extractor/daum.py @@ -1,6 +1,5 @@ # encoding: utf-8 import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -29,17 +28,16 @@ class DaumIE(InfoExtractor): video_id = mobj.group(1) canonical_url = 'http://tvpot.daum.net/v/%s' % video_id webpage = self._download_webpage(canonical_url, video_id) - full_id = self._search_regex(r'<link rel="video_src" href=".+?vid=(.+?)"', + full_id = self._search_regex( + r'<iframe src="http://videofarm.daum.net/controller/video/viewer/Video.html\?.*?vid=(.+?)[&"]', webpage, u'full id') query = compat_urllib_parse.urlencode({'vid': full_id}) - info_xml = self._download_webpage( + info = self._download_xml( 'http://tvpot.daum.net/clip/ClipInfoXml.do?' + query, video_id, u'Downloading video info') - urls_xml = self._download_webpage( + urls = self._download_xml( 'http://videofarm.daum.net/controller/api/open/v1_2/MovieData.apixml?' + query, video_id, u'Downloading video formats info') - info = xml.etree.ElementTree.fromstring(info_xml.encode('utf-8')) - urls = xml.etree.ElementTree.fromstring(urls_xml.encode('utf-8')) self.to_screen(u'%s: Getting video urls' % video_id) formats = [] @@ -49,10 +47,9 @@ class DaumIE(InfoExtractor): 'vid': full_id, 'profile': profile, }) - url_xml = self._download_webpage( + url_doc = self._download_xml( 'http://videofarm.daum.net/controller/api/open/v1_2/MovieLocation.apixml?' + format_query, video_id, note=False) - url_doc = xml.etree.ElementTree.fromstring(url_xml.encode('utf-8')) format_url = url_doc.find('result/url').text formats.append({ 'url': format_url, @@ -60,7 +57,7 @@ class DaumIE(InfoExtractor): 'format_id': profile, }) - info = { + return { 'id': video_id, 'title': info.find('TITLE').text, 'formats': formats, @@ -69,6 +66,3 @@ class DaumIE(InfoExtractor): 'duration': int(info.find('DURATION').text), 'upload_date': info.find('REGDTTM').text[:8], } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info diff --git a/youtube_dl/extractor/dreisat.py b/youtube_dl/extractor/dreisat.py index 765cb1f..24ce794 100644 --- a/youtube_dl/extractor/dreisat.py +++ b/youtube_dl/extractor/dreisat.py @@ -1,7 +1,6 @@ # coding: utf-8 import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -30,8 +29,7 @@ class DreiSatIE(InfoExtractor): mobj = re.match(self._VALID_URL, url) video_id = mobj.group('id') details_url = 'http://www.3sat.de/mediathek/xmlservice/web/beitragsDetails?ak=web&id=%s' % video_id - details_xml = self._download_webpage(details_url, video_id, note=u'Downloading video details') - details_doc = xml.etree.ElementTree.fromstring(details_xml.encode('utf-8')) + details_doc = self._download_xml(details_url, video_id, note=u'Downloading video details') thumbnail_els = details_doc.findall('.//teaserimage') thumbnails = [{ @@ -67,7 +65,7 @@ class DreiSatIE(InfoExtractor): return (qidx, prefer_http, format['video_bitrate']) formats.sort(key=_sortkey) - info = { + return { '_type': 'video', 'id': video_id, 'title': video_title, @@ -78,8 +76,3 @@ class DreiSatIE(InfoExtractor): 'uploader': video_uploader, 'upload_date': upload_date, } - - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - - return info diff --git a/youtube_dl/extractor/ebaumsworld.py b/youtube_dl/extractor/ebaumsworld.py index f02c699..877113d 100644 --- a/youtube_dl/extractor/ebaumsworld.py +++ b/youtube_dl/extractor/ebaumsworld.py @@ -1,5 +1,4 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import determine_ext @@ -21,9 +20,8 @@ class EbaumsWorldIE(InfoExtractor): def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) video_id = mobj.group('id') - config_xml = self._download_webpage( + config = self._download_xml( 'http://www.ebaumsworld.com/video/player/%s' % video_id, video_id) - config = xml.etree.ElementTree.fromstring(config_xml.encode('utf-8')) video_url = config.find('file').text return { diff --git a/youtube_dl/extractor/eighttracks.py b/youtube_dl/extractor/eighttracks.py index 2cfbcd3..f21ef88 100644 --- a/youtube_dl/extractor/eighttracks.py +++ b/youtube_dl/extractor/eighttracks.py @@ -1,4 +1,3 @@ -import itertools import json import random import re diff --git a/youtube_dl/extractor/escapist.py b/youtube_dl/extractor/escapist.py index 3aa2da5..b1242f6 100644 --- a/youtube_dl/extractor/escapist.py +++ b/youtube_dl/extractor/escapist.py @@ -11,11 +11,11 @@ from ..utils import ( class EscapistIE(InfoExtractor): - _VALID_URL = r'^(https?://)?(www\.)?escapistmagazine\.com/videos/view/(?P<showname>[^/]+)/(?P<episode>[^/?]+)[/?]?.*$' + _VALID_URL = r'^https?://?(www\.)?escapistmagazine\.com/videos/view/(?P<showname>[^/]+)/(?P<episode>[^/?]+)[/?]?.*$' _TEST = { u'url': u'http://www.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate', u'file': u'6618-Breaking-Down-Baldurs-Gate.mp4', - u'md5': u'c6793dbda81388f4264c1ba18684a74d', + u'md5': u'ab3a706c681efca53f0a35f1415cf0d1', u'info_dict': { u"description": u"Baldur's Gate: Original, Modded or Enhanced Edition? I'll break down what you can expect from the new Baldur's Gate: Enhanced Edition.", u"uploader": u"the-escapist-presents", @@ -25,50 +25,60 @@ class EscapistIE(InfoExtractor): def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) - if mobj is None: - raise ExtractorError(u'Invalid URL: %s' % url) showName = mobj.group('showname') videoId = mobj.group('episode') self.report_extraction(videoId) webpage = self._download_webpage(url, videoId) - videoDesc = self._html_search_regex('<meta name="description" content="([^"]*)"', + videoDesc = self._html_search_regex( + r'<meta name="description" content="([^"]*)"', webpage, u'description', fatal=False) - playerUrl = self._og_search_video_url(webpage, name='player url') + playerUrl = self._og_search_video_url(webpage, name=u'player URL') - title = self._html_search_regex('<meta name="title" content="([^"]*)"', - webpage, u'player url').split(' : ')[-1] + title = self._html_search_regex( + r'<meta name="title" content="([^"]*)"', + webpage, u'title').split(' : ')[-1] - configUrl = self._search_regex('config=(.*)$', playerUrl, u'config url') + configUrl = self._search_regex('config=(.*)$', playerUrl, u'config URL') configUrl = compat_urllib_parse.unquote(configUrl) - configJSON = self._download_webpage(configUrl, videoId, - u'Downloading configuration', - u'unable to download configuration') - - # Technically, it's JavaScript, not JSON - configJSON = configJSON.replace("'", '"') - + formats = [] + + def _add_format(name, cfgurl): + configJSON = self._download_webpage( + cfgurl, videoId, + u'Downloading ' + name + ' configuration', + u'Unable to download ' + name + ' configuration') + + # Technically, it's JavaScript, not JSON + configJSON = configJSON.replace("'", '"') + + try: + config = json.loads(configJSON) + except (ValueError,) as err: + raise ExtractorError(u'Invalid JSON in configuration file: ' + compat_str(err)) + playlist = config['playlist'] + formats.append({ + 'url': playlist[1]['url'], + 'format_id': name, + }) + + _add_format(u'normal', configUrl) + hq_url = (configUrl + + ('&hq=1' if '?' in configUrl else configUrl + '?hq=1')) try: - config = json.loads(configJSON) - except (ValueError,) as err: - raise ExtractorError(u'Invalid JSON in configuration file: ' + compat_str(err)) + _add_format(u'hq', hq_url) + except ExtractorError: + pass # That's fine, we'll just use normal quality - playlist = config['playlist'] - videoUrl = playlist[1]['url'] - - info = { + return { 'id': videoId, - 'url': videoUrl, + 'formats': formats, 'uploader': showName, - 'upload_date': None, 'title': title, - 'ext': 'mp4', 'thumbnail': self._og_search_thumbnail(webpage), 'description': videoDesc, 'player_url': playerUrl, } - - return [info] diff --git a/youtube_dl/extractor/facebook.py b/youtube_dl/extractor/facebook.py index f8bdfc2..3b21071 100644 --- a/youtube_dl/extractor/facebook.py +++ b/youtube_dl/extractor/facebook.py @@ -1,5 +1,4 @@ import json -import netrc import re import socket diff --git a/youtube_dl/extractor/faz.py b/youtube_dl/extractor/faz.py index 89ed08d..d0dfde6 100644 --- a/youtube_dl/extractor/faz.py +++ b/youtube_dl/extractor/faz.py @@ -1,6 +1,5 @@ # encoding: utf-8 import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -28,9 +27,8 @@ class FazIE(InfoExtractor): webpage = self._download_webpage(url, video_id) config_xml_url = self._search_regex(r'writeFLV\(\'(.+?)\',', webpage, u'config xml url') - config_xml = self._download_webpage(config_xml_url, video_id, + config = self._download_xml(config_xml_url, video_id, u'Downloading config xml') - config = xml.etree.ElementTree.fromstring(config_xml.encode('utf-8')) encodings = config.find('ENCODINGS') formats = [] @@ -46,13 +44,10 @@ class FazIE(InfoExtractor): }) descr = self._html_search_regex(r'<p class="Content Copy">(.*?)</p>', webpage, u'description') - info = { + return { 'id': video_id, 'title': self._og_search_title(webpage), 'formats': formats, 'description': descr, 'thumbnail': config.find('STILL/STILL_BIG').text, } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info diff --git a/youtube_dl/extractor/fktv.py b/youtube_dl/extractor/fktv.py index 9c89362..dba1a8d 100644 --- a/youtube_dl/extractor/fktv.py +++ b/youtube_dl/extractor/fktv.py @@ -39,7 +39,6 @@ class FKTVIE(InfoExtractor): for i, _ in enumerate(files, 1): video_id = '%04d%d' % (episode, i) video_url = 'http://dl%d.fernsehkritik.tv/fernsehkritik%d%s.flv' % (server, episode, '' if i == 1 else '-%d' % i) - video_title = 'Fernsehkritik %d.%d' % (episode, i) videos.append({ 'id': video_id, 'url': video_url, diff --git a/youtube_dl/extractor/francetv.py b/youtube_dl/extractor/francetv.py index 086cafc..6e19710 100644 --- a/youtube_dl/extractor/francetv.py +++ b/youtube_dl/extractor/francetv.py @@ -1,6 +1,5 @@ # encoding: utf-8 import re -import xml.etree.ElementTree import json from .common import InfoExtractor @@ -11,11 +10,10 @@ from ..utils import ( class FranceTVBaseInfoExtractor(InfoExtractor): def _extract_video(self, video_id): - xml_desc = self._download_webpage( + info = self._download_xml( 'http://www.francetvinfo.fr/appftv/webservices/video/' 'getInfosOeuvre.php?id-diffusion=' + video_id, video_id, 'Downloading XML config') - info = xml.etree.ElementTree.fromstring(xml_desc.encode('utf-8')) manifest_url = info.find('videos/video/url').text video_url = manifest_url.replace('manifest.f4m', 'index_2_av.m3u8') diff --git a/youtube_dl/extractor/gamekings.py b/youtube_dl/extractor/gamekings.py new file mode 100644 index 0000000..c91669b --- /dev/null +++ b/youtube_dl/extractor/gamekings.py @@ -0,0 +1,38 @@ +import re + +from .common import InfoExtractor + + +class GamekingsIE(InfoExtractor): + _VALID_URL = r'http?://www\.gamekings\.tv/videos/(?P<name>[0-9a-z\-]+)' + _TEST = { + u"url": u"http://www.gamekings.tv/videos/phoenix-wright-ace-attorney-dual-destinies-review/", + u'file': u'20130811.mp4', + # MD5 is flaky, seems to change regularly + #u'md5': u'2f32b1f7b80fdc5cb616efb4f387f8a3', + u'info_dict': { + u"title": u"Phoenix Wright: Ace Attorney \u2013 Dual Destinies Review", + u"description": u"Melle en Steven hebben voor de review een week in de rechtbank doorbracht met Phoenix Wright: Ace Attorney - Dual Destinies.", + } + } + + def _real_extract(self, url): + + mobj = re.match(self._VALID_URL, url) + name = mobj.group('name') + webpage = self._download_webpage(url, name) + video_url = self._og_search_video_url(webpage) + + video = re.search(r'[0-9]+', video_url) + video_id = video.group(0) + + # Todo: add medium format + video_url = video_url.replace(video_id, 'large/' + video_id) + + return { + 'id': video_id, + 'ext': 'mp4', + 'url': video_url, + 'title': self._og_search_title(webpage), + 'description': self._og_search_description(webpage), + } diff --git a/youtube_dl/extractor/gamespot.py b/youtube_dl/extractor/gamespot.py index 0987683..26b7d2a 100644 --- a/youtube_dl/extractor/gamespot.py +++ b/youtube_dl/extractor/gamespot.py @@ -24,7 +24,7 @@ class GameSpotIE(InfoExtractor): def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) - page_id = video_id = mobj.group('page_id') + page_id = mobj.group('page_id') webpage = self._download_webpage(url, page_id) data_video_json = self._search_regex(r'data-video=\'(.*?)\'', webpage, u'data video') data_video = json.loads(unescapeHTML(data_video_json)) @@ -47,13 +47,10 @@ class GameSpotIE(InfoExtractor): 'format_id': q, }) - info = { + return { 'id': data_video['guid'], 'title': compat_urllib_parse.unquote(data_video['title']), 'formats': formats, 'description': get_meta_content('description', webpage), 'thumbnail': self._og_search_thumbnail(webpage), } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info diff --git a/youtube_dl/extractor/gametrailers.py b/youtube_dl/extractor/gametrailers.py index 3cc02d9..3a8bef2 100644 --- a/youtube_dl/extractor/gametrailers.py +++ b/youtube_dl/extractor/gametrailers.py @@ -1,13 +1,11 @@ import re -from .mtv import MTVIE, _media_xml_tag +from .mtv import MTVServicesInfoExtractor -class GametrailersIE(MTVIE): - """ - Gametrailers use the same videos system as MTVIE, it just changes the feed - url, where the uri is and the method to get the thumbnails. - """ + +class GametrailersIE(MTVServicesInfoExtractor): _VALID_URL = r'http://www.gametrailers.com/(?P<type>videos|reviews|full-episodes)/(?P<id>.*?)/(?P<title>.*)' + _TEST = { u'url': u'http://www.gametrailers.com/videos/zbvr8i/mirror-s-edge-2-e3-2013--debut-trailer', u'file': u'70e9a5d7-cf25-4a10-9104-6f3e7342ae0d.mp4', @@ -17,15 +15,9 @@ class GametrailersIE(MTVIE): u'description': u'Faith is back! Check out the World Premiere trailer for Mirror\'s Edge 2 straight from the EA Press Conference at E3 2013!', }, } - # Overwrite MTVIE properties we don't want - _TESTS = [] _FEED_URL = 'http://www.gametrailers.com/feeds/mrss' - def _get_thumbnail_url(self, uri, itemdoc): - search_path = '%s/%s' % (_media_xml_tag('group'), _media_xml_tag('thumbnail')) - return itemdoc.find(search_path).attrib['url'] - def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) video_id = mobj.group('id') diff --git a/youtube_dl/extractor/generic.py b/youtube_dl/extractor/generic.py index c7552fd..10ae062 100644 --- a/youtube_dl/extractor/generic.py +++ b/youtube_dl/extractor/generic.py @@ -162,6 +162,16 @@ class GenericIE(InfoExtractor): raise ExtractorError(u'Failed to download URL: %s' % url) self.report_extraction(video_id) + + # it's tempting to parse this further, but you would + # have to take into account all the variations like + # Video Title - Site Name + # Site Name | Video Title + # Video Title - Tagline | Site Name + # and so on and so forth; it's just not practical + video_title = self._html_search_regex(r'<title>(.*)', + webpage, u'video title', default=u'video', flags=re.DOTALL) + # Look for BrightCove: bc_url = BrightcoveIE._extract_brightcove_url(webpage) if bc_url is not None: @@ -177,17 +187,29 @@ class GenericIE(InfoExtractor): return self.url_result(surl, 'Vimeo') # Look for embedded YouTube player - mobj = re.search( - r']+?src=(["\'])(?Phttps?://(?:www\.)?youtube.com/embed/.+?)\1', webpage) - if mobj: - surl = unescapeHTML(mobj.group(u'url')) - return self.url_result(surl, 'Youtube') + matches = re.findall( + r']+?src=(["\'])(?P(?:https?:)?//(?:www\.)?youtube.com/embed/.+?)\1', webpage) + if matches: + urlrs = [self.url_result(unescapeHTML(tuppl[1]), 'Youtube') + for tuppl in matches] + return self.playlist_result( + urlrs, playlist_id=video_id, playlist_title=video_title) + + # Look for embedded Dailymotion player + matches = re.findall( + r']+?src=(["\'])(?P(?:https?:)?//(?:www\.)?dailymotion.com/embed/video/.+?)\1', webpage) + if matches: + urlrs = [self.url_result(unescapeHTML(tuppl[1]), 'Dailymotion') + for tuppl in matches] + return self.playlist_result( + urlrs, playlist_id=video_id, playlist_title=video_title) # Look for Bandcamp pages with custom domain mobj = re.search(r']*?content="(.*?bandcamp\.com.*?)"', webpage) if mobj is not None: burl = unescapeHTML(mobj.group(1)) - return self.url_result(burl, 'Bandcamp') + # Don't set the extractor because it can be a track url or an album + return self.url_result(burl) # Start with something easy: JW Player in SWFObject mobj = re.search(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage) @@ -196,7 +218,7 @@ class GenericIE(InfoExtractor): mobj = re.search(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage) if mobj is None: # Broaden the search a little bit: JWPlayer JS loader - mobj = re.search(r'[^A-Za-z0-9]?file["\']?:\s*["\'](http[^\'"&]*)', webpage) + mobj = re.search(r'[^A-Za-z0-9]?file["\']?:\s*["\'](http[^\'"]*)', webpage) if mobj is None: # Try to find twitter cards info mobj = re.search(r'(.*)', - webpage, u'video title', default=u'video', flags=re.DOTALL) - # video uploader is domain name video_uploader = self._search_regex(r'(?:https?://)?([^/]*)/.*', url, u'video uploader') - return [{ + return { 'id': video_id, 'url': video_url, 'uploader': video_uploader, 'upload_date': None, 'title': video_title, - 'ext': video_extension, - }] + } diff --git a/youtube_dl/extractor/howcast.py b/youtube_dl/extractor/howcast.py index 4695433..bafc582 100644 --- a/youtube_dl/extractor/howcast.py +++ b/youtube_dl/extractor/howcast.py @@ -8,7 +8,7 @@ class HowcastIE(InfoExtractor): _TEST = { u'url': u'http://www.howcast.com/videos/390161-How-to-Tie-a-Square-Knot-Properly', u'file': u'390161.mp4', - u'md5': u'1d7ba54e2c9d7dc6935ef39e00529138', + u'md5': u'8b743df908c42f60cf6496586c7f12c3', u'info_dict': { u"description": u"The square knot, also known as the reef knot, is one of the oldest, most basic knots to tie, and can be used in many different ways. Here's the proper way to tie a square knot.", u"title": u"How to Tie a Square Knot Properly" diff --git a/youtube_dl/extractor/imdb.py b/youtube_dl/extractor/imdb.py new file mode 100644 index 0000000..d8e9712 --- /dev/null +++ b/youtube_dl/extractor/imdb.py @@ -0,0 +1,59 @@ +import re +import json + +from .common import InfoExtractor +from ..utils import ( + compat_urlparse, + get_element_by_attribute, +) + + +class ImdbIE(InfoExtractor): + IE_NAME = u'imdb' + IE_DESC = u'Internet Movie Database trailers' + _VALID_URL = r'http://www\.imdb\.com/video/imdb/vi(?P\d+)' + + _TEST = { + u'url': u'http://www.imdb.com/video/imdb/vi2524815897', + u'md5': u'9f34fa777ade3a6e57a054fdbcb3a068', + u'info_dict': { + u'id': u'2524815897', + u'ext': u'mp4', + u'title': u'Ice Age: Continental Drift Trailer (No. 2) - IMDb', + u'description': u'md5:9061c2219254e5d14e03c25c98e96a81', + u'duration': 151, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + webpage = self._download_webpage(url,video_id) + descr = get_element_by_attribute('itemprop', 'description', webpage) + available_formats = re.findall( + r'case \'(?P.*?)\' :$\s+url = \'(?P.*?)\'', webpage, + flags=re.MULTILINE) + formats = [] + for f_id, f_path in available_formats: + format_page = self._download_webpage( + compat_urlparse.urljoin(url, f_path), + u'Downloading info for %s format' % f_id) + json_data = self._search_regex( + r']+class="imdb-player-data"[^>]*?>(.*?)', + format_page, u'json data', flags=re.DOTALL) + info = json.loads(json_data) + format_info = info['videoPlayerObject']['video'] + formats.append({ + 'format_id': f_id, + 'url': format_info['url'], + 'height': int(info['titleObject']['encoding']['selected'][:-1]), + }) + + return { + 'id': video_id, + 'title': self._og_search_title(webpage), + 'formats': formats, + 'description': descr, + 'thumbnail': format_info['slate'], + 'duration': int(info['titleObject']['title']['duration_seconds']), + } diff --git a/youtube_dl/extractor/internetvideoarchive.py b/youtube_dl/extractor/internetvideoarchive.py index be8e05f..16a6f73 100644 --- a/youtube_dl/extractor/internetvideoarchive.py +++ b/youtube_dl/extractor/internetvideoarchive.py @@ -1,5 +1,4 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -43,9 +42,8 @@ class InternetVideoArchiveIE(InfoExtractor): video_id = query_dic['publishedid'][0] url = self._build_url(query) - flashconfiguration_xml = self._download_webpage(url, video_id, + flashconfiguration = self._download_xml(url, video_id, u'Downloading flash configuration') - flashconfiguration = xml.etree.ElementTree.fromstring(flashconfiguration_xml.encode('utf-8')) file_url = flashconfiguration.find('file').text file_url = file_url.replace('/playlist.aspx', '/mrssplaylist.aspx') # Replace some of the parameters in the query to get the best quality @@ -53,9 +51,8 @@ class InternetVideoArchiveIE(InfoExtractor): file_url = re.sub(r'(?<=\?)(.+)$', lambda m: self._clean_query(m.group()), file_url) - info_xml = self._download_webpage(file_url, video_id, + info = self._download_xml(file_url, video_id, u'Downloading video info') - info = xml.etree.ElementTree.fromstring(info_xml.encode('utf-8')) item = info.find('channel/item') def _bp(p): diff --git a/youtube_dl/extractor/jeuxvideo.py b/youtube_dl/extractor/jeuxvideo.py index 6bb54b9..caf9d8c 100644 --- a/youtube_dl/extractor/jeuxvideo.py +++ b/youtube_dl/extractor/jeuxvideo.py @@ -2,7 +2,6 @@ import json import re -import xml.etree.ElementTree from .common import InfoExtractor @@ -22,7 +21,7 @@ class JeuxVideoIE(InfoExtractor): def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) - title = re.match(self._VALID_URL, url).group(1) + title = mobj.group(1) webpage = self._download_webpage(url, title) xml_link = self._html_search_regex( r'', @@ -32,12 +31,9 @@ class JeuxVideoIE(InfoExtractor): r'http://www\.jeuxvideo\.com/config/\w+/\d+/(.*?)/\d+_player\.xml', xml_link, u'video ID') - xml_config = self._download_webpage( + config = self._download_xml( xml_link, title, u'Downloading XML config') - config = xml.etree.ElementTree.fromstring(xml_config.encode('utf-8')) - info_json = self._search_regex( - r'(?sm)(.*?)', - xml_config, u'JSON information') + info_json = config.find('format.json').text info = json.loads(info_json)['versions'][0] video_url = 'http://video720.jeuxvideo.com/' + info['file'] diff --git a/youtube_dl/extractor/justintv.py b/youtube_dl/extractor/justintv.py index f600179..e9bde0c 100644 --- a/youtube_dl/extractor/justintv.py +++ b/youtube_dl/extractor/justintv.py @@ -1,7 +1,6 @@ import json import os import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -94,10 +93,9 @@ class JustinTVIE(InfoExtractor): archive_id = m.group(1) api = api_base + '/broadcast/by_chapter/%s.xml' % chapter_id - chapter_info_xml = self._download_webpage(api, chapter_id, + doc = self._download_xml(api, chapter_id, note=u'Downloading chapter information', errnote=u'Chapter information download failed') - doc = xml.etree.ElementTree.fromstring(chapter_info_xml) for a in doc.findall('.//archive'): if archive_id == a.find('./id').text: break diff --git a/youtube_dl/extractor/livestream.py b/youtube_dl/extractor/livestream.py index 4531fd6..9bc35b1 100644 --- a/youtube_dl/extractor/livestream.py +++ b/youtube_dl/extractor/livestream.py @@ -5,12 +5,12 @@ from .common import InfoExtractor from ..utils import ( compat_urllib_parse_urlparse, compat_urlparse, - get_meta_content, - ExtractorError, + xpath_with_ns, ) class LivestreamIE(InfoExtractor): + IE_NAME = u'livestream' _VALID_URL = r'http://new.livestream.com/.*?/(?P.*?)(/videos/(?P\d+))?/?$' _TEST = { u'url': u'http://new.livestream.com/CoheedandCambria/WebsterHall/videos/4719370', @@ -54,3 +54,43 @@ class LivestreamIE(InfoExtractor): info = json.loads(self._download_webpage(api_url, video_id, u'Downloading video info')) return self._extract_video_info(info) + + +# The original version of Livestream uses a different system +class LivestreamOriginalIE(InfoExtractor): + IE_NAME = u'livestream:original' + _VALID_URL = r'https?://www\.livestream\.com/(?P[^/]+)/video\?.*?clipId=(?P.*?)(&|$)' + _TEST = { + u'url': u'http://www.livestream.com/dealbook/video?clipId=pla_8aa4a3f1-ba15-46a4-893b-902210e138fb', + u'info_dict': { + u'id': u'pla_8aa4a3f1-ba15-46a4-893b-902210e138fb', + u'ext': u'flv', + u'title': u'Spark 1 (BitCoin) with Cameron Winklevoss & Tyler Winklevoss of Winklevoss Capital', + }, + u'params': { + # rtmp + u'skip_download': True, + }, + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + user = mobj.group('user') + api_url = 'http://x{0}x.api.channel.livestream.com/2.0/clipdetails?extendedInfo=true&id={1}'.format(user, video_id) + + info = self._download_xml(api_url, video_id) + item = info.find('channel').find('item') + ns = {'media': 'http://search.yahoo.com/mrss'} + thumbnail_url = item.find(xpath_with_ns('media:thumbnail', ns)).attrib['url'] + # Remove the extension and number from the path (like 1.jpg) + path = self._search_regex(r'(user-files/.+)_.*?\.jpg$', thumbnail_url, u'path') + + return { + 'id': video_id, + 'title': item.find('title').text, + 'url': 'rtmp://extondemand.livestream.com/ondemand', + 'play_path': 'mp4:trans/dv15/mogulus-{0}.mp4'.format(path), + 'ext': 'flv', + 'thumbnail': thumbnail_url, + } diff --git a/youtube_dl/extractor/metacritic.py b/youtube_dl/extractor/metacritic.py index 449138b..6b95b49 100644 --- a/youtube_dl/extractor/metacritic.py +++ b/youtube_dl/extractor/metacritic.py @@ -43,13 +43,10 @@ class MetacriticIE(InfoExtractor): description = self._html_search_regex(r'Description:(.*?)

', webpage, u'description', flags=re.DOTALL) - info = { + return { 'id': video_id, 'title': clip.find('title').text, 'formats': formats, 'description': description, 'duration': int(clip.find('duration').text), } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info diff --git a/youtube_dl/extractor/mixcloud.py b/youtube_dl/extractor/mixcloud.py index a200dcd..e2baf44 100644 --- a/youtube_dl/extractor/mixcloud.py +++ b/youtube_dl/extractor/mixcloud.py @@ -60,7 +60,7 @@ class MixcloudIE(InfoExtractor): 'title': info['name'], 'url': final_song_url, 'ext': 'mp3', - 'description': info['description'], + 'description': info.get('description'), 'thumbnail': info['pictures'].get('extra_large'), 'uploader': info['user']['name'], 'uploader_id': info['user']['username'], diff --git a/youtube_dl/extractor/mtv.py b/youtube_dl/extractor/mtv.py index 24a79ae..6b3feb5 100644 --- a/youtube_dl/extractor/mtv.py +++ b/youtube_dl/extractor/mtv.py @@ -10,35 +10,8 @@ from ..utils import ( def _media_xml_tag(tag): return '{http://search.yahoo.com/mrss/}%s' % tag -class MTVIE(InfoExtractor): - _VALID_URL = r'^https?://(?:www\.)?mtv\.com/videos/.+?/(?P[0-9]+)/[^/]+$' - - _FEED_URL = 'http://www.mtv.com/player/embed/AS3/rss/' - - _TESTS = [ - { - u'url': u'http://www.mtv.com/videos/misc/853555/ours-vh1-storytellers.jhtml', - u'file': u'853555.mp4', - u'md5': u'850f3f143316b1e71fa56a4edfd6e0f8', - u'info_dict': { - u'title': u'Taylor Swift - "Ours (VH1 Storytellers)"', - u'description': u'Album: Taylor Swift performs "Ours" for VH1 Storytellers at Harvey Mudd College.', - }, - }, - { - 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', - u'info_dict': { - u'title': u'Everything Has Changed', - u'upload_date': u'20130606', - u'uploader': u'Taylor Swift', - }, - u'skip': u'VEVO is only available in some countries', - }, - ] +class MTVServicesInfoExtractor(InfoExtractor): @staticmethod def _id_from_uri(uri): return uri.split(':')[-1] @@ -48,18 +21,22 @@ class MTVIE(InfoExtractor): def _transform_rtmp_url(rtmp_video_url): m = re.match(r'^rtmpe?://.*?/(?Pgsp\..+?/.*)$', rtmp_video_url) if not m: - raise ExtractorError(u'Cannot transform RTMP url') + return rtmp_video_url base = 'http://mtvnmobile.vo.llnwd.net/kip0/_pxn=1+_pxI0=Ripod-h264+_pxL0=undefined+_pxM0=+_pxK=18639+_pxE=mp4/44620/mtvnorigin/' return base + m.group('finalid') def _get_thumbnail_url(self, uri, itemdoc): - return 'http://mtv.mtvnimages.com/uri/' + uri + search_path = '%s/%s' % (_media_xml_tag('group'), _media_xml_tag('thumbnail')) + thumb_node = itemdoc.find(search_path) + if thumb_node is None: + return None + else: + return thumb_node.attrib['url'] def _extract_video_formats(self, metadataXml): if '/error_country_block.swf' in metadataXml: raise ExtractorError(u'This video is not available from your country.', expected=True) mdoc = xml.etree.ElementTree.fromstring(metadataXml.encode('utf-8')) - renditions = mdoc.findall('.//rendition') formats = [] for rendition in mdoc.findall('.//rendition'): @@ -94,7 +71,7 @@ class MTVIE(InfoExtractor): else: description = None - info = { + return { 'title': itemdoc.find('title').text, 'formats': self._extract_video_formats(mediagen_page), 'id': video_id, @@ -102,19 +79,46 @@ class MTVIE(InfoExtractor): 'description': description, } - # TODO: Remove when #980 has been merged - info.update(info['formats'][-1]) - - return info - def _get_videos_info(self, uri): video_id = self._id_from_uri(uri) data = compat_urllib_parse.urlencode({'uri': uri}) - infoXml = self._download_webpage(self._FEED_URL +'?' + data, video_id, + idoc = self._download_xml(self._FEED_URL +'?' + data, video_id, u'Downloading info') - idoc = xml.etree.ElementTree.fromstring(infoXml.encode('utf-8')) return [self._get_video_info(item) for item in idoc.findall('.//item')] + +class MTVIE(MTVServicesInfoExtractor): + _VALID_URL = r'^https?://(?:www\.)?mtv\.com/videos/.+?/(?P[0-9]+)/[^/]+$' + + _FEED_URL = 'http://www.mtv.com/player/embed/AS3/rss/' + + _TESTS = [ + { + u'url': u'http://www.mtv.com/videos/misc/853555/ours-vh1-storytellers.jhtml', + u'file': u'853555.mp4', + u'md5': u'850f3f143316b1e71fa56a4edfd6e0f8', + u'info_dict': { + u'title': u'Taylor Swift - "Ours (VH1 Storytellers)"', + u'description': u'Album: Taylor Swift performs "Ours" for VH1 Storytellers at Harvey Mudd College.', + }, + }, + { + 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', + u'info_dict': { + u'title': u'Everything Has Changed', + u'upload_date': u'20130606', + u'uploader': u'Taylor Swift', + }, + u'skip': u'VEVO is only available in some countries', + }, + ] + + def _get_thumbnail_url(self, uri, itemdoc): + return 'http://mtv.mtvnimages.com/uri/' + uri + def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) video_id = mobj.group('videoid') diff --git a/youtube_dl/extractor/myspass.py b/youtube_dl/extractor/myspass.py index 107665d..0067bf1 100644 --- a/youtube_dl/extractor/myspass.py +++ b/youtube_dl/extractor/myspass.py @@ -1,5 +1,4 @@ import os.path -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -33,8 +32,7 @@ class MySpassIE(InfoExtractor): # get metadata metadata_url = META_DATA_URL_TEMPLATE % video_id - metadata_text = self._download_webpage(metadata_url, video_id) - metadata = xml.etree.ElementTree.fromstring(metadata_text.encode('utf-8')) + metadata = self._download_xml(metadata_url, video_id) # extract values from metadata url_flv_el = metadata.find('url_flv') diff --git a/youtube_dl/extractor/naver.py b/youtube_dl/extractor/naver.py index 9df236d..c012ec0 100644 --- a/youtube_dl/extractor/naver.py +++ b/youtube_dl/extractor/naver.py @@ -1,6 +1,5 @@ # encoding: utf-8 import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -38,14 +37,12 @@ class NaverIE(InfoExtractor): 'protocol': 'p2p', 'inKey': key, }) - info_xml = self._download_webpage( + info = self._download_xml( 'http://serviceapi.rmcnmv.naver.com/flash/videoInfo.nhn?' + query, video_id, u'Downloading video info') - urls_xml = self._download_webpage( + urls = self._download_xml( 'http://serviceapi.rmcnmv.naver.com/flash/playableEncodingOption.nhn?' + query_urls, video_id, u'Downloading video formats info') - info = xml.etree.ElementTree.fromstring(info_xml.encode('utf-8')) - urls = xml.etree.ElementTree.fromstring(urls_xml.encode('utf-8')) formats = [] for format_el in urls.findall('EncodingOptions/EncodingOption'): @@ -59,7 +56,7 @@ class NaverIE(InfoExtractor): 'height': int(format_el.find('height').text), }) - info = { + return { 'id': video_id, 'title': info.find('Subject').text, 'formats': formats, @@ -68,6 +65,3 @@ class NaverIE(InfoExtractor): 'upload_date': info.find('WriteDate').text.replace('.', ''), 'view_count': int(info.find('PlayCount').text), } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info diff --git a/youtube_dl/extractor/nbc.py b/youtube_dl/extractor/nbc.py index 3bc9dae..e8bbfff 100644 --- a/youtube_dl/extractor/nbc.py +++ b/youtube_dl/extractor/nbc.py @@ -1,5 +1,4 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import find_xpath_attr, compat_str @@ -21,8 +20,8 @@ class NBCNewsIE(InfoExtractor): def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) video_id = mobj.group('id') - info_xml = self._download_webpage('http://www.nbcnews.com/id/%s/displaymode/1219' % video_id, video_id) - info = xml.etree.ElementTree.fromstring(info_xml.encode('utf-8')).find('video') + all_info = self._download_xml('http://www.nbcnews.com/id/%s/displaymode/1219' % video_id, video_id) + info = all_info.find('video') return {'id': video_id, 'title': info.find('headline').text, diff --git a/youtube_dl/extractor/nhl.py b/youtube_dl/extractor/nhl.py index 224f56a..2edd806 100644 --- a/youtube_dl/extractor/nhl.py +++ b/youtube_dl/extractor/nhl.py @@ -1,6 +1,5 @@ import re import json -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -26,9 +25,8 @@ class NHLBaseInfoExtractor(InfoExtractor): 'path': initial_video_url.replace('.mp4', '_sd.mp4'), }) path_url = 'http://video.nhl.com/videocenter/servlets/encryptvideopath?' + data - path_response = self._download_webpage(path_url, video_id, + path_doc = self._download_xml(path_url, video_id, u'Downloading final video url') - path_doc = xml.etree.ElementTree.fromstring(path_response) video_url = path_doc.find('path').text join = compat_urlparse.urljoin @@ -72,7 +70,7 @@ class NHLIE(NHLBaseInfoExtractor): class NHLVideocenterIE(NHLBaseInfoExtractor): IE_NAME = u'nhl.com:videocenter' - IE_DESC = u'Download the first 12 videos from a videocenter category' + IE_DESC = u'NHL videocenter category' _VALID_URL = r'https?://video\.(?P[^.]*)\.nhl\.com/videocenter/(console\?.*?catid=(?P[^&]+))?' @classmethod diff --git a/youtube_dl/extractor/niconico.py b/youtube_dl/extractor/niconico.py new file mode 100644 index 0000000..4677431 --- /dev/null +++ b/youtube_dl/extractor/niconico.py @@ -0,0 +1,127 @@ +# encoding: utf-8 + +import re +import socket + +from .common import InfoExtractor +from ..utils import ( + compat_http_client, + compat_urllib_error, + compat_urllib_parse, + compat_urllib_request, + compat_urlparse, + compat_str, + + ExtractorError, + unified_strdate, +) + + +class NiconicoIE(InfoExtractor): + IE_NAME = u'niconico' + IE_DESC = u'ニコニコ動画' + + _TEST = { + u'url': u'http://www.nicovideo.jp/watch/sm22312215', + u'file': u'sm22312215.mp4', + u'md5': u'd1a75c0823e2f629128c43e1212760f9', + u'info_dict': { + u'title': u'Big Buck Bunny', + u'uploader': u'takuya0301', + u'uploader_id': u'2698420', + u'upload_date': u'20131123', + u'description': u'(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org', + }, + u'params': { + u'username': u'ydl.niconico@gmail.com', + u'password': u'youtube-dl', + }, + } + + _VALID_URL = r'^https?://(?:www\.|secure\.)?nicovideo\.jp/watch/([a-z][a-z][0-9]+)(?:.*)$' + _NETRC_MACHINE = 'niconico' + # If True it will raise an error if no login info is provided + _LOGIN_REQUIRED = True + + def _real_initialize(self): + self._login() + + def _login(self): + (username, password) = self._get_login_info() + # No authentication to be performed + if username is None: + if self._LOGIN_REQUIRED: + raise ExtractorError(u'No login info available, needed for using %s.' % self.IE_NAME, expected=True) + return False + + # Log in + login_form_strs = { + u'mail': username, + u'password': password, + } + # Convert to UTF-8 *before* urlencode because Python 2.x's urlencode + # chokes on unicode + login_form = dict((k.encode('utf-8'), v.encode('utf-8')) for k,v in login_form_strs.items()) + login_data = compat_urllib_parse.urlencode(login_form).encode('utf-8') + request = compat_urllib_request.Request( + u'https://secure.nicovideo.jp/secure/login', login_data) + login_results = self._download_webpage( + request, u'', note=u'Logging in', errnote=u'Unable to log in') + if re.search(r'(?i)

Log in error

', login_results) is not None: + self._downloader.report_warning(u'unable to log in: bad username or password') + return False + return True + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group(1) + + # Get video webpage. We are not actually interested in it, but need + # the cookies in order to be able to download the info webpage + self._download_webpage('http://www.nicovideo.jp/watch/' + video_id, video_id) + + video_info = self._download_xml( + 'http://ext.nicovideo.jp/api/getthumbinfo/' + video_id, video_id, + note=u'Downloading video info page') + + # Get flv info + flv_info_webpage = self._download_webpage( + u'http://flapi.nicovideo.jp/api/getflv?v=' + video_id, + video_id, u'Downloading flv info') + video_real_url = compat_urlparse.parse_qs(flv_info_webpage)['url'][0] + + # Start extracting information + video_title = video_info.find('.//title').text + video_extension = video_info.find('.//movie_type').text + video_format = video_extension.upper() + video_thumbnail = video_info.find('.//thumbnail_url').text + video_description = video_info.find('.//description').text + video_uploader_id = video_info.find('.//user_id').text + video_upload_date = unified_strdate(video_info.find('.//first_retrieve').text.split('+')[0]) + video_view_count = video_info.find('.//view_counter').text + video_webpage_url = video_info.find('.//watch_url').text + + # uploader + video_uploader = video_uploader_id + url = 'http://seiga.nicovideo.jp/api/user/info?id=' + video_uploader_id + try: + user_info = self._download_xml( + url, video_id, note=u'Downloading user information') + video_uploader = user_info.find('.//nickname').text + except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: + self._downloader.report_warning(u'Unable to download user info webpage: %s' % compat_str(err)) + + return { + 'id': video_id, + 'url': video_real_url, + 'title': video_title, + 'ext': video_extension, + 'format': video_format, + 'thumbnail': video_thumbnail, + 'description': video_description, + 'uploader': video_uploader, + 'upload_date': video_upload_date, + 'uploader_id': video_uploader_id, + 'view_count': video_view_count, + 'webpage_url': video_webpage_url, + } diff --git a/youtube_dl/extractor/podomatic.py b/youtube_dl/extractor/podomatic.py new file mode 100644 index 0000000..5820097 --- /dev/null +++ b/youtube_dl/extractor/podomatic.py @@ -0,0 +1,49 @@ +import json +import re + +from .common import InfoExtractor + + +class PodomaticIE(InfoExtractor): + IE_NAME = 'podomatic' + _VALID_URL = r'^(?Phttps?)://(?P[^.]+)\.podomatic\.com/entry/(?P[^?]+)' + + _TEST = { + u"url": u"http://scienceteachingtips.podomatic.com/entry/2009-01-02T16_03_35-08_00", + u"file": u"2009-01-02T16_03_35-08_00.mp3", + u"md5": u"84bb855fcf3429e6bf72460e1eed782d", + u"info_dict": { + u"uploader": u"Science Teaching Tips", + u"uploader_id": u"scienceteachingtips", + u"title": u"64. When the Moon Hits Your Eye", + u"duration": 446, + } + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + channel = mobj.group('channel') + + json_url = (('%s://%s.podomatic.com/entry/embed_params/%s' + + '?permalink=true&rtmp=0') % + (mobj.group('proto'), channel, video_id)) + data_json = self._download_webpage( + json_url, video_id, note=u'Downloading video info') + data = json.loads(data_json) + + video_url = data['downloadLink'] + uploader = data['podcast'] + title = data['title'] + thumbnail = data['imageLocation'] + duration = int(data['length'] / 1000.0) + + return { + 'id': video_id, + 'url': video_url, + 'title': title, + 'uploader': uploader, + 'uploader_id': channel, + 'thumbnail': thumbnail, + 'duration': duration, + } diff --git a/youtube_dl/extractor/pornhub.py b/youtube_dl/extractor/pornhub.py index 75cf4bb..8b34719 100644 --- a/youtube_dl/extractor/pornhub.py +++ b/youtube_dl/extractor/pornhub.py @@ -6,7 +6,6 @@ from ..utils import ( compat_urllib_parse_urlparse, compat_urllib_request, compat_urllib_parse, - unescapeHTML, ) from ..aes import ( aes_decrypt_text diff --git a/youtube_dl/extractor/redtube.py b/youtube_dl/extractor/redtube.py index 994778e..c2254ae 100644 --- a/youtube_dl/extractor/redtube.py +++ b/youtube_dl/extractor/redtube.py @@ -8,7 +8,9 @@ class RedTubeIE(InfoExtractor): _TEST = { u'url': u'http://www.redtube.com/66418', u'file': u'66418.mp4', - u'md5': u'7b8c22b5e7098a3e1c09709df1126d2d', + # md5 varies from time to time, as in + # https://travis-ci.org/rg3/youtube-dl/jobs/14052463#L295 + #u'md5': u'7b8c22b5e7098a3e1c09709df1126d2d', u'info_dict': { u"title": u"Sucked on a toilet", u"age_limit": 18, @@ -28,7 +30,7 @@ class RedTubeIE(InfoExtractor): r'', webpage, u'video URL') video_title = self._html_search_regex( - r'

(.+?)

', + r'

(.+?)

', webpage, u'title') # No self-labeling, but they describe themselves as diff --git a/youtube_dl/extractor/rtlnow.py b/youtube_dl/extractor/rtlnow.py index 9ac7c3b..2f238de 100644 --- a/youtube_dl/extractor/rtlnow.py +++ b/youtube_dl/extractor/rtlnow.py @@ -62,18 +62,6 @@ class RTLnowIE(InfoExtractor): u'skip_download': True, }, }, - { - 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'20131016', - u'title': u'Recht & Ordnung - Stadtpolizei Frankfurt/ Gerichtsvollzieher...', - u'description': u'Stadtpolizei Frankfurt/ Gerichtsvollzieher Leipzig', - }, - u'params': { - u'skip_download': True, - }, - }, { u'url': u'http://www.n-tvnow.de/top-gear/episode-1-2013-01-01-00-00-00.php?film_id=124903&player=1&season=10', u'file': u'124903.flv', diff --git a/youtube_dl/extractor/sina.py b/youtube_dl/extractor/sina.py index 14b1c65..74a87fe 100644 --- a/youtube_dl/extractor/sina.py +++ b/youtube_dl/extractor/sina.py @@ -1,7 +1,6 @@ # coding: utf-8 import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -35,12 +34,11 @@ class SinaIE(InfoExtractor): def _extract_video(self, video_id): data = compat_urllib_parse.urlencode({'vid': video_id}) - url_page = self._download_webpage('http://v.iask.com/v_play.php?%s' % data, + url_doc = self._download_xml('http://v.iask.com/v_play.php?%s' % data, video_id, u'Downloading video url') image_page = self._download_webpage( 'http://interface.video.sina.com.cn/interface/common/getVideoImage.php?%s' % data, video_id, u'Downloading thumbnail info') - url_doc = xml.etree.ElementTree.fromstring(url_page.encode('utf-8')) return {'id': video_id, 'url': url_doc.find('./durl/url').text, diff --git a/youtube_dl/extractor/smotri.py b/youtube_dl/extractor/smotri.py new file mode 100644 index 0000000..f035a32 --- /dev/null +++ b/youtube_dl/extractor/smotri.py @@ -0,0 +1,252 @@ +# encoding: utf-8 + +import re +import json +import hashlib + +from .common import InfoExtractor +from ..utils import ( + determine_ext, + ExtractorError +) + + +class SmotriIE(InfoExtractor): + IE_DESC = u'Smotri.com' + IE_NAME = u'smotri' + _VALID_URL = r'^https?://(?:www\.)?(?Psmotri\.com/video/view/\?id=(?Pv(?P[0-9]+)[a-z0-9]{4}))' + + _TESTS = [ + # real video id 2610366 + { + u'url': u'http://smotri.com/video/view/?id=v261036632ab', + u'file': u'v261036632ab.mp4', + u'md5': u'2a7b08249e6f5636557579c368040eb9', + u'info_dict': { + u'title': u'катастрофа с камер видеонаблюдения', + u'uploader': u'rbc2008', + u'uploader_id': u'rbc08', + u'upload_date': u'20131118', + u'description': u'катастрофа с камер видеонаблюдения, видео катастрофа с камер видеонаблюдения', + u'thumbnail': u'http://frame6.loadup.ru/8b/a9/2610366.3.3.jpg', + }, + }, + # real video id 57591 + { + u'url': u'http://smotri.com/video/view/?id=v57591cb20', + u'file': u'v57591cb20.flv', + u'md5': u'830266dfc21f077eac5afd1883091bcd', + u'info_dict': { + u'title': u'test', + u'uploader': u'Support Photofile@photofile', + u'uploader_id': u'support-photofile', + u'upload_date': u'20070704', + u'description': u'test, видео test', + u'thumbnail': u'http://frame4.loadup.ru/03/ed/57591.2.3.jpg', + }, + }, + # video-password + { + u'url': u'http://smotri.com/video/view/?id=v1390466a13c', + u'file': u'v1390466a13c.mp4', + u'md5': u'f6331cef33cad65a0815ee482a54440b', + u'info_dict': { + u'title': u'TOCCA_A_NOI_-_LE_COSE_NON_VANNO_CAMBIAMOLE_ORA-1', + u'uploader': u'timoxa40', + u'uploader_id': u'timoxa40', + u'upload_date': u'20100404', + u'thumbnail': u'http://frame7.loadup.ru/af/3f/1390466.3.3.jpg', + u'description': u'TOCCA_A_NOI_-_LE_COSE_NON_VANNO_CAMBIAMOLE_ORA-1, видео TOCCA_A_NOI_-_LE_COSE_NON_VANNO_CAMBIAMOLE_ORA-1', + }, + u'params': { + u'videopassword': u'qwerty', + }, + }, + # age limit + video-password + { + u'url': u'http://smotri.com/video/view/?id=v15408898bcf', + u'file': u'v15408898bcf.flv', + u'md5': u'91e909c9f0521adf5ee86fbe073aad70', + u'info_dict': { + u'title': u'этот ролик не покажут по ТВ', + u'uploader': u'zzxxx', + u'uploader_id': u'ueggb', + u'upload_date': u'20101001', + u'thumbnail': u'http://frame3.loadup.ru/75/75/1540889.1.3.jpg', + u'age_limit': 18, + u'description': u'этот ролик не покажут по ТВ, видео этот ролик не покажут по ТВ', + }, + u'params': { + u'videopassword': u'333' + } + } + ] + + _SUCCESS = 0 + _PASSWORD_NOT_VERIFIED = 1 + _PASSWORD_DETECTED = 2 + _VIDEO_NOT_FOUND = 3 + + def _search_meta(self, name, html, display_name=None): + if display_name is None: + display_name = name + return self._html_search_regex( + r'' % re.escape(name), + html, display_name, fatal=False) + return self._html_search_meta(name, html, display_name) + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('videoid') + real_video_id = mobj.group('realvideoid') + + # Download video JSON data + video_json_url = 'http://smotri.com/vt.php?id=%s' % real_video_id + video_json_page = self._download_webpage(video_json_url, video_id, u'Downloading video JSON') + video_json = json.loads(video_json_page) + + status = video_json['status'] + if status == self._VIDEO_NOT_FOUND: + raise ExtractorError(u'Video %s does not exist' % video_id, expected=True) + elif status == self._PASSWORD_DETECTED: # The video is protected by a password, retry with + # video-password set + video_password = self._downloader.params.get('videopassword', None) + if not video_password: + raise ExtractorError(u'This video is protected by a password, use the --video-password option', expected=True) + video_json_url += '&md5pass=%s' % hashlib.md5(video_password.encode('utf-8')).hexdigest() + video_json_page = self._download_webpage(video_json_url, video_id, u'Downloading video JSON (video-password set)') + video_json = json.loads(video_json_page) + status = video_json['status'] + if status == self._PASSWORD_NOT_VERIFIED: + raise ExtractorError(u'Video password is invalid', expected=True) + + if status != self._SUCCESS: + raise ExtractorError(u'Unexpected status value %s' % status) + + # Extract the URL of the video + video_url = video_json['file_data'] + + # Video JSON does not provide enough meta data + # We will extract some from the video web page instead + video_page_url = 'http://' + mobj.group('url') + video_page = self._download_webpage(video_page_url, video_id, u'Downloading video page') + + # Adult content + if re.search(u'EroConfirmText">', video_page) is not None: + self.report_age_confirmation() + confirm_string = self._html_search_regex( + r'
' % video_id, + video_page, u'confirm string') + confirm_url = video_page_url + '&confirm=%s' % confirm_string + video_page = self._download_webpage(confirm_url, video_id, u'Downloading video page (age confirmed)') + adult_content = True + else: + adult_content = False + + # Extract the rest of meta data + video_title = self._search_meta(u'name', video_page, u'title') + if not video_title: + video_title = video_url.rsplit('/', 1)[-1] + + video_description = self._search_meta(u'description', video_page) + END_TEXT = u' на сайте Smotri.com' + if video_description.endswith(END_TEXT): + video_description = video_description[:-len(END_TEXT)] + START_TEXT = u'Смотреть онлайн ролик ' + if video_description.startswith(START_TEXT): + video_description = video_description[len(START_TEXT):] + video_thumbnail = self._search_meta(u'thumbnail', video_page) + + upload_date_str = self._search_meta(u'uploadDate', video_page, u'upload date') + upload_date_m = re.search(r'(?P\d{4})\.(?P\d{2})\.(?P\d{2})T', upload_date_str) + video_upload_date = ( + ( + upload_date_m.group('year') + + upload_date_m.group('month') + + upload_date_m.group('day') + ) + if upload_date_m else None + ) + + duration_str = self._search_meta(u'duration', video_page) + duration_m = re.search(r'T(?P[0-9]{2})H(?P[0-9]{2})M(?P[0-9]{2})S', duration_str) + video_duration = ( + ( + (int(duration_m.group('hours')) * 60 * 60) + + (int(duration_m.group('minutes')) * 60) + + int(duration_m.group('seconds')) + ) + if duration_m else None + ) + + video_uploader = self._html_search_regex( + u'
Автор.*?onmouseover="popup_user_info[^"]+">(.*?)', + video_page, u'uploader', fatal=False, flags=re.MULTILINE|re.DOTALL) + + video_uploader_id = self._html_search_regex( + u'
Автор.*?onmouseover="popup_user_info\\(.*?\'([^\']+)\'\\);">', + video_page, u'uploader id', fatal=False, flags=re.MULTILINE|re.DOTALL) + + video_view_count = self._html_search_regex( + u'Общее количество просмотров.*?(\\d+)', + video_page, u'view count', fatal=False, flags=re.MULTILINE|re.DOTALL) + + return { + 'id': video_id, + 'url': video_url, + 'title': video_title, + 'thumbnail': video_thumbnail, + 'description': video_description, + 'uploader': video_uploader, + 'upload_date': video_upload_date, + 'uploader_id': video_uploader_id, + 'video_duration': video_duration, + 'view_count': video_view_count, + 'age_limit': 18 if adult_content else 0, + 'video_page_url': video_page_url + } + + +class SmotriCommunityIE(InfoExtractor): + IE_DESC = u'Smotri.com community videos' + IE_NAME = u'smotri:community' + _VALID_URL = r'^https?://(?:www\.)?smotri\.com/community/video/(?P[0-9A-Za-z_\'-]+)' + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + community_id = mobj.group('communityid') + + url = 'http://smotri.com/export/rss/video/by/community/-/%s/video.xml' % community_id + rss = self._download_xml(url, community_id, u'Downloading community RSS') + + entries = [self.url_result(video_url.text, 'Smotri') + for video_url in rss.findall('./channel/item/link')] + + description_text = rss.find('./channel/description').text + community_title = self._html_search_regex( + u'^Видео сообщества "([^"]+)"$', description_text, u'community title') + + return self.playlist_result(entries, community_id, community_title) + + +class SmotriUserIE(InfoExtractor): + IE_DESC = u'Smotri.com user videos' + IE_NAME = u'smotri:user' + _VALID_URL = r'^https?://(?:www\.)?smotri\.com/user/(?P[0-9A-Za-z_\'-]+)' + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + user_id = mobj.group('userid') + + url = 'http://smotri.com/export/rss/user/video/-/%s/video.xml' % user_id + rss = self._download_xml(url, user_id, u'Downloading user RSS') + + entries = [self.url_result(video_url.text, 'Smotri') + for video_url in rss.findall('./channel/item/link')] + + description_text = rss.find('./channel/description').text + user_nickname = self._html_search_regex( + u'^Видео режиссера (.*)$', description_text, + u'user nickname') + + return self.playlist_result(entries, user_id, user_nickname) diff --git a/youtube_dl/extractor/soundcloud.py b/youtube_dl/extractor/soundcloud.py index 4717fbb..3a19ab1 100644 --- a/youtube_dl/extractor/soundcloud.py +++ b/youtube_dl/extractor/soundcloud.py @@ -59,6 +59,7 @@ class SoundcloudIE(InfoExtractor): ] _CLIENT_ID = 'b45b1aa10f1ac2941910a7f0d10f8e28' + _IPHONE_CLIENT_ID = '376f225bf427445fc4bfb6b99b72e0bf' @classmethod def suitable(cls, url): @@ -75,36 +76,79 @@ class SoundcloudIE(InfoExtractor): def _extract_info_dict(self, info, full_title=None, quiet=False): track_id = compat_str(info['id']) name = full_title or track_id - if quiet == False: + if quiet: self.report_extraction(name) thumbnail = info['artwork_url'] if thumbnail is not None: thumbnail = thumbnail.replace('-large', '-t500x500') + ext = info.get('original_format', u'mp3') result = { - 'id': track_id, - 'url': info['stream_url'] + '?client_id=' + self._CLIENT_ID, + 'id': track_id, 'uploader': info['user']['username'], 'upload_date': unified_strdate(info['created_at']), - 'title': info['title'], - 'ext': u'mp3', + 'title': info['title'], '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 + # We can build a direct link to the song + format_url = ( + u'https://api.soundcloud.com/tracks/{0}/download?client_id={1}'.format( + track_id, self._CLIENT_ID)) + result['formats'] = [{ + 'format_id': 'download', + 'ext': ext, + 'url': format_url, + 'vcodec': 'none', + }] + else: + # We have to retrieve the url stream_json = self._download_webpage( - 'http://api.soundcloud.com/i1/tracks/{0}/streams?client_id={1}'.format(track_id, self._CLIENT_ID), + 'http://api.soundcloud.com/i1/tracks/{0}/streams?client_id={1}'.format(track_id, self._IPHONE_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, - }) + + formats = [] + format_dict = json.loads(stream_json) + for key, stream_url in format_dict.items(): + if key.startswith(u'http'): + formats.append({ + 'format_id': key, + 'ext': ext, + 'url': stream_url, + 'vcodec': 'none', + }) + elif key.startswith(u'rtmp'): + # The url doesn't have an rtmp app, we have to extract the playpath + url, path = stream_url.split('mp3:', 1) + formats.append({ + 'format_id': key, + 'url': url, + 'play_path': 'mp3:' + path, + 'ext': ext, + 'vcodec': 'none', + }) + + if not formats: + # We fallback to the stream_url in the original info, this + # cannot be always used, sometimes it can give an HTTP 404 error + formats.append({ + 'format_id': u'fallback', + 'url': info['stream_url'] + '?client_id=' + self._CLIENT_ID, + 'ext': ext, + 'vcodec': 'none', + }) + + def format_pref(f): + if f['format_id'].startswith('http'): + return 2 + if f['format_id'].startswith('rtmp'): + return 1 + return 0 + + formats.sort(key=format_pref) + result['formats'] = formats + return result def _real_extract(self, url): @@ -158,7 +202,6 @@ class SoundcloudSetIE(SoundcloudIE): resolv_url = self._resolv_url(url) info_json = self._download_webpage(resolv_url, full_title) - videos = [] info = json.loads(info_json) if 'errors' in info: for err in info['errors']: diff --git a/youtube_dl/extractor/southparkstudios.py b/youtube_dl/extractor/southparkstudios.py index b1e96b6..fd90cc5 100644 --- a/youtube_dl/extractor/southparkstudios.py +++ b/youtube_dl/extractor/southparkstudios.py @@ -1,38 +1,42 @@ import re -from .mtv import MTVIE, _media_xml_tag +from .mtv import MTVServicesInfoExtractor -class SouthParkStudiosIE(MTVIE): +class SouthParkStudiosIE(MTVServicesInfoExtractor): IE_NAME = u'southparkstudios.com' - _VALID_URL = r'https?://www\.southparkstudios\.com/(clips|full-episodes)/(?P.+?)(\?|#|$)' + _VALID_URL = r'(https?://)?(www\.)?(?Psouthparkstudios\.com/(clips|full-episodes)/(?P.+?)(\?|#|$))' _FEED_URL = 'http://www.southparkstudios.com/feeds/video-player/mrss' - _TEST = { + _TESTS = [{ u'url': u'http://www.southparkstudios.com/clips/104437/bat-daded#tab=featured', u'file': u'a7bff6c2-ed00-11e0-aca6-0026b9414f30.mp4', u'info_dict': { u'title': u'Bat Daded', u'description': u'Randy disqualifies South Park by getting into a fight with Bat Dad.', }, - } - - # Overwrite MTVIE properties we don't want - _TESTS = [] - - def _get_thumbnail_url(self, uri, itemdoc): - search_path = '%s/%s' % (_media_xml_tag('group'), _media_xml_tag('thumbnail')) - thumb_node = itemdoc.find(search_path) - if thumb_node is None: - return None - else: - return thumb_node.attrib['url'] + }] def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) + url = u'http://www.' + mobj.group(u'url') video_id = mobj.group('id') webpage = self._download_webpage(url, video_id) mgid = self._search_regex(r'swfobject.embedSWF\(".*?(mgid:.*?)"', webpage, u'mgid') return self._get_videos_info(mgid) + +class SouthparkDeIE(SouthParkStudiosIE): + IE_NAME = u'southpark.de' + _VALID_URL = r'(https?://)?(www\.)?(?Psouthpark\.de/(clips|alle-episoden)/(?P.+?)(\?|#|$))' + _FEED_URL = 'http://www.southpark.de/feeds/video-player/mrss/' + + _TESTS = [{ + u'url': u'http://www.southpark.de/clips/uygssh/the-government-wont-respect-my-privacy#tab=featured', + u'file': u'85487c96-b3b9-4e39-9127-ad88583d9bf2.mp4', + u'info_dict': { + u'title': u'The Government Won\'t Respect My Privacy', + u'description': u'Cartman explains the benefits of "Shitter" to Stan, Kyle and Craig.', + }, + }] diff --git a/youtube_dl/extractor/spankwire.py b/youtube_dl/extractor/spankwire.py index 97f9c26..9e2ad0d 100644 --- a/youtube_dl/extractor/spankwire.py +++ b/youtube_dl/extractor/spankwire.py @@ -6,7 +6,6 @@ from ..utils import ( compat_urllib_parse_urlparse, compat_urllib_request, compat_urllib_parse, - unescapeHTML, ) from ..aes import ( aes_decrypt_text @@ -36,11 +35,12 @@ class SpankwireIE(InfoExtractor): webpage = self._download_webpage(req, video_id) video_title = self._html_search_regex(r'

([^<]+)', webpage, u'title') - video_uploader = self._html_search_regex(r'by:\s*]*>(.+?)', 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:

\s*<[^>]*>([^<]+)', webpage, u'description', fatal=False) - if len(description) == 0: - description = None + video_uploader = self._html_search_regex( + r'by:\s*]*>(.+?)', 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'([^<]+)<', webpage, u'description', fatal=False) 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: diff --git a/youtube_dl/extractor/spiegel.py b/youtube_dl/extractor/spiegel.py index 13c8640..6955205 100644 --- a/youtube_dl/extractor/spiegel.py +++ b/youtube_dl/extractor/spiegel.py @@ -1,19 +1,26 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor class SpiegelIE(InfoExtractor): _VALID_URL = r'https?://(?:www\.)?spiegel\.de/video/[^/]*-(?P[0-9]+)(?:\.html)?(?:#.*)?$' - _TEST = { + _TESTS = [{ u'url': u'http://www.spiegel.de/video/vulkan-tungurahua-in-ecuador-ist-wieder-aktiv-video-1259285.html', u'file': u'1259285.mp4', u'md5': u'2c2754212136f35fb4b19767d242f66e', u'info_dict': { u"title": u"Vulkanausbruch in Ecuador: Der \"Feuerschlund\" ist wieder aktiv" } - } + }, + { + u'url': u'http://www.spiegel.de/video/schach-wm-videoanalyse-des-fuenften-spiels-video-1309159.html', + u'file': u'1309159.mp4', + u'md5': u'f2cdf638d7aa47654e251e1aee360af1', + u'info_dict': { + u'title': u'Schach-WM in der Videoanalyse: Carlsen nutzt die Fehlgriffe des Titelverteidigers' + } + }] def _real_extract(self, url): m = re.match(self._VALID_URL, url) @@ -21,25 +28,36 @@ class SpiegelIE(InfoExtractor): webpage = self._download_webpage(url, video_id) - video_title = self._html_search_regex(r'
(.*?)
', - webpage, u'title') + video_title = self._html_search_regex( + r'
(.*?)
', webpage, u'title') xml_url = u'http://video2.spiegel.de/flash/' + video_id + u'.xml' - xml_code = self._download_webpage(xml_url, video_id, - note=u'Downloading XML', errnote=u'Failed to download XML') - - idoc = xml.etree.ElementTree.fromstring(xml_code) - last_type = idoc[-1] - filename = last_type.findall('./filename')[0].text - duration = float(last_type.findall('./duration')[0].text) + idoc = self._download_xml( + xml_url, video_id, + note=u'Downloading XML', errnote=u'Failed to download XML') + + formats = [ + { + 'format_id': n.tag.rpartition('type')[2], + 'url': u'http://video2.spiegel.de/flash/' + n.find('./filename').text, + 'width': int(n.find('./width').text), + 'height': int(n.find('./height').text), + 'abr': int(n.find('./audiobitrate').text), + 'vbr': int(n.find('./videobitrate').text), + 'vcodec': n.find('./codec').text, + 'acodec': 'MP4A', + } + for n in list(idoc) + # Blacklist type 6, it's extremely LQ and not available on the same server + if n.tag.startswith('type') and n.tag != 'type6' + ] + formats.sort(key=lambda f: f['vbr']) + duration = float(idoc[0].findall('./duration')[0].text) - video_url = 'http://video2.spiegel.de/flash/' + filename - video_ext = filename.rpartition('.')[2] info = { 'id': video_id, - 'url': video_url, - 'ext': video_ext, 'title': video_title, 'duration': duration, + 'formats': formats, } - return [info] + return info diff --git a/youtube_dl/extractor/streamcloud.py b/youtube_dl/extractor/streamcloud.py new file mode 100644 index 0000000..9faf3a5 --- /dev/null +++ b/youtube_dl/extractor/streamcloud.py @@ -0,0 +1,66 @@ +# coding: utf-8 +import re +import time + +from .common import InfoExtractor +from ..utils import ( + compat_urllib_parse, + compat_urllib_request, +) + + +class StreamcloudIE(InfoExtractor): + IE_NAME = u'streamcloud.eu' + _VALID_URL = r'https?://streamcloud\.eu/(?P[a-zA-Z0-9_-]+)/(?P[^#?]*)\.html' + + _TEST = { + u'url': u'http://streamcloud.eu/skp9j99s4bpz/youtube-dl_test_video_____________-BaW_jenozKc.mp4.html', + u'file': u'skp9j99s4bpz.mp4', + u'md5': u'6bea4c7fa5daaacc2a946b7146286686', + u'info_dict': { + u'title': u'youtube-dl test video \'/\\ ä ↭', + u'duration': 9, + }, + u'skip': u'Only available from the EU' + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + + orig_webpage = self._download_webpage(url, video_id) + + fields = re.findall(r'''(?x)]*>([^<]+)<', webpage, u'title') + video_url = self._search_regex( + r'file:\s*"([^"]+)"', webpage, u'video URL') + duration_str = self._search_regex( + r'duration:\s*"?([0-9]+)"?', webpage, u'duration', fatal=False) + duration = None if duration_str is None else int(duration_str) + thumbnail = self._search_regex( + r'image:\s*"([^"]+)"', webpage, u'thumbnail URL', fatal=False) + + return { + 'id': video_id, + 'title': title, + 'url': video_url, + 'duration': duration, + 'thumbnail': thumbnail, + } diff --git a/youtube_dl/extractor/sztvhu.py b/youtube_dl/extractor/sztvhu.py index 81fa35c..c9359fa 100644 --- a/youtube_dl/extractor/sztvhu.py +++ b/youtube_dl/extractor/sztvhu.py @@ -15,7 +15,8 @@ class SztvHuIE(InfoExtractor): u'info_dict': { u"title": u"Cserkészek népszerűsítették a környezettudatos életmódot a Savaria téren", u"description": u'A zöld nap játékos ismeretterjesztő programjait a Magyar Cserkész Szövetség szervezte, akik az ország nyolc városában adják át tudásukat az érdeklődőknek. A PET...', - } + }, + u'skip': u'Service temporarily disabled as of 2013-11-20' } def _real_extract(self, url): diff --git a/youtube_dl/extractor/teamcoco.py b/youtube_dl/extractor/teamcoco.py index bc48620..2bf26d0 100644 --- a/youtube_dl/extractor/teamcoco.py +++ b/youtube_dl/extractor/teamcoco.py @@ -1,5 +1,4 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -32,8 +31,7 @@ class TeamcocoIE(InfoExtractor): self.report_extraction(video_id) data_url = 'http://teamcoco.com/cvp/2.0/%s.xml' % video_id - data_xml = self._download_webpage(data_url, video_id, 'Downloading data webpage') - data = xml.etree.ElementTree.fromstring(data_xml.encode('utf-8')) + data = self._download_xml(data_url, video_id, 'Downloading data webpage') qualities = ['500k', '480p', '1000k', '720p', '1080p'] @@ -60,7 +58,7 @@ class TeamcocoIE(InfoExtractor): return -1 formats.sort(key=sort_key) if not formats: - raise RegexNotFoundError(u'Unable to extract video URL') + raise ExtractorError(u'Unable to extract video URL') return { 'id': video_id, diff --git a/youtube_dl/extractor/ted.py b/youtube_dl/extractor/ted.py index 76cfdfb..4bca62b 100644 --- a/youtube_dl/extractor/ted.py +++ b/youtube_dl/extractor/ted.py @@ -4,7 +4,6 @@ import re from .subtitles import SubtitlesInfoExtractor from ..utils import ( - compat_str, RegexNotFoundError, ) @@ -43,26 +42,25 @@ class TEDIE(SubtitlesInfoExtractor): self.to_screen(u'Getting info of playlist %s: "%s"' % (playlist_id,name)) return [self._playlist_videos_info(url,name,playlist_id)] - def _playlist_videos_info(self,url,name,playlist_id=0): + + def _playlist_videos_info(self, url, name, playlist_id): '''Returns the videos of the playlist''' - video_RE=r''' - (?P.+?)

' - webpage=self._download_webpage(url, playlist_id, 'Downloading playlist webpage') - m_videos=re.finditer(video_RE,webpage,re.VERBOSE) - m_names=re.finditer(video_name_RE,webpage) + + webpage = self._download_webpage( + url, playlist_id, u'Downloading playlist webpage') + matches = re.finditer( + r'/talks/[^"]+\.html)">[^<]*

', + webpage) playlist_title = self._html_search_regex(r'div class="headline">\s*?

\s*?(.*?)', webpage, 'playlist title') - playlist_entries = [] - for m_video, m_name in zip(m_videos,m_names): - talk_url='http://www.ted.com%s' % m_name.group('talk_url') - playlist_entries.append(self.url_result(talk_url, 'TED')) - return self.playlist_result(playlist_entries, playlist_id = playlist_id, playlist_title = playlist_title) + playlist_entries = [ + self.url_result(u'http://www.ted.com' + m.group('talk_url'), 'TED') + for m in matches + ] + return self.playlist_result( + playlist_entries, playlist_id=playlist_id, playlist_title=playlist_title) def _talk_info(self, url, video_id=0): """Return the video for the talk in the url""" @@ -85,7 +83,7 @@ class TEDIE(SubtitlesInfoExtractor): 'ext': 'mp4', 'url': stream['file'], 'format': stream['id'] - } for stream in info['htmlStreams']] + } for stream in info['htmlStreams']] video_id = info['id'] @@ -95,7 +93,7 @@ class TEDIE(SubtitlesInfoExtractor): self._list_available_subtitles(video_id, webpage) return - info = { + return { 'id': video_id, 'title': title, 'thumbnail': thumbnail, @@ -104,11 +102,6 @@ class TEDIE(SubtitlesInfoExtractor): 'formats': formats, } - # TODO: Remove when #980 has been merged - info.update(info['formats'][-1]) - - return info - def _get_available_subtitles(self, video_id, webpage): try: options = self._search_regex(r'(?:)', webpage, 'subtitles_language_select', flags=re.DOTALL) @@ -119,6 +112,6 @@ class TEDIE(SubtitlesInfoExtractor): 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: + except RegexNotFoundError: self._downloader.report_warning(u'video doesn\'t have subtitles') return {} diff --git a/youtube_dl/extractor/toutv.py b/youtube_dl/extractor/toutv.py new file mode 100644 index 0000000..1e9598e --- /dev/null +++ b/youtube_dl/extractor/toutv.py @@ -0,0 +1,71 @@ +# coding: utf-8 +import re + +from .common import InfoExtractor +from ..utils import ( + ExtractorError, + unified_strdate, +) + + +class TouTvIE(InfoExtractor): + IE_NAME = u'tou.tv' + _VALID_URL = r'https?://www\.tou\.tv/(?P[a-zA-Z0-9_-]+(?:/(?PS[0-9]+E[0-9]+)))' + + _TEST = { + u'url': u'http://www.tou.tv/30-vies/S04E41', + u'file': u'30-vies_S04E41.mp4', + u'info_dict': { + u'title': u'30 vies Saison 4 / Épisode 41', + u'description': u'md5:da363002db82ccbe4dafeb9cab039b09', + u'age_limit': 8, + u'uploader': u'Groupe des Nouveaux Médias', + u'duration': 1296, + u'upload_date': u'20131118', + u'thumbnail': u'http://static.tou.tv/medias/images/2013-11-18_19_00_00_30VIES_0341_01_L.jpeg', + }, + u'params': { + u'skip_download': True, # Requires rtmpdump + }, + u'skip': 'Only available in Canada' + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + webpage = self._download_webpage(url, video_id) + + mediaId = self._search_regex( + r'"idMedia":\s*"([^"]+)"', webpage, u'media ID') + + streams_url = u'http://release.theplatform.com/content.select?pid=' + mediaId + streams_doc = self._download_xml( + streams_url, video_id, note=u'Downloading stream list') + + video_url = next(n.text + for n in streams_doc.findall('.//choice/url') + if u'//ad.doubleclick' not in n.text) + if video_url.endswith('/Unavailable.flv'): + raise ExtractorError( + u'Access to this video is blocked from outside of Canada', + expected=True) + + duration_str = self._html_search_meta( + 'video:duration', webpage, u'duration') + duration = int(duration_str) if duration_str else None + upload_date_str = self._html_search_meta( + 'video:release_date', webpage, u'upload date') + upload_date = unified_strdate(upload_date_str) if upload_date_str else None + + return { + 'id': video_id, + 'title': self._og_search_title(webpage), + 'url': video_url, + 'description': self._og_search_description(webpage), + 'uploader': self._dc_search_uploader(webpage), + 'thumbnail': self._og_search_thumbnail(webpage), + 'age_limit': self._media_rating_search(webpage), + 'duration': duration, + 'upload_date': upload_date, + 'ext': 'mp4', + } diff --git a/youtube_dl/extractor/trilulilu.py b/youtube_dl/extractor/trilulilu.py index 0bf028f..d64aaa4 100644 --- a/youtube_dl/extractor/trilulilu.py +++ b/youtube_dl/extractor/trilulilu.py @@ -1,6 +1,5 @@ import json import re -import xml.etree.ElementTree from .common import InfoExtractor @@ -36,12 +35,10 @@ class TriluliluIE(InfoExtractor): format_url = (u'http://fs%(server)s.trilulilu.ro/%(hash)s/' u'video-formats2' % log) - format_str = self._download_webpage( + format_doc = self._download_xml( format_url, video_id, note=u'Downloading formats', errnote=u'Error while downloading formats') - - format_doc = xml.etree.ElementTree.fromstring(format_str) video_url_template = ( u'http://fs%(server)s.trilulilu.ro/stream.php?type=video' @@ -58,7 +55,7 @@ class TriluliluIE(InfoExtractor): for fnode in format_doc.findall('./formats/format') ] - info = { + return { '_type': 'video', 'id': video_id, 'formats': formats, @@ -67,7 +64,3 @@ class TriluliluIE(InfoExtractor): 'thumbnail': thumbnail, } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - - return info diff --git a/youtube_dl/extractor/tube8.py b/youtube_dl/extractor/tube8.py index d4b7603..4d9d41d 100644 --- a/youtube_dl/extractor/tube8.py +++ b/youtube_dl/extractor/tube8.py @@ -5,8 +5,6 @@ from .common import InfoExtractor from ..utils import ( compat_urllib_parse_urlparse, compat_urllib_request, - compat_urllib_parse, - unescapeHTML, ) from ..aes import ( aes_decrypt_text diff --git a/youtube_dl/extractor/tvp.py b/youtube_dl/extractor/tvp.py new file mode 100644 index 0000000..bfed9dd --- /dev/null +++ b/youtube_dl/extractor/tvp.py @@ -0,0 +1,42 @@ +import json +import re + +from .common import InfoExtractor + + +class TvpIE(InfoExtractor): + IE_NAME = u'tvp.pl' + _VALID_URL = r'https?://www\.tvp\.pl/.*?wideo/(?P\d+)/(?P\d+)' + + _TEST = { + u'url': u'http://www.tvp.pl/warszawa/magazyny/campusnews/wideo/31102013/12878238', + u'md5': u'148408967a6a468953c0a75cbdaf0d7a', + u'file': u'12878238.wmv', + u'info_dict': { + u'title': u'31.10.2013 - Odcinek 2', + u'description': u'31.10.2013 - Odcinek 2', + }, + u'skip': u'Download has to use same server IP as extraction. Therefore, a good (load-balancing) DNS resolver will make the download fail.' + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + webpage = self._download_webpage(url, video_id) + json_url = 'http://www.tvp.pl/pub/stat/videofileinfo?video_id=%s' % video_id + json_params = self._download_webpage( + json_url, video_id, u"Downloading video metadata") + + params = json.loads(json_params) + self.report_extraction(video_id) + video_url = params['video_url'] + + title = self._og_search_title(webpage, fatal=True) + return { + 'id': video_id, + 'title': title, + 'ext': 'wmv', + 'url': video_url, + 'description': self._og_search_description(webpage), + 'thumbnail': self._og_search_thumbnail(webpage), + } diff --git a/youtube_dl/extractor/vevo.py b/youtube_dl/extractor/vevo.py index 3f6020f..4378b17 100644 --- a/youtube_dl/extractor/vevo.py +++ b/youtube_dl/extractor/vevo.py @@ -78,12 +78,13 @@ class VevoIE(InfoExtractor): continue 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, + 'vcodec': m.group('vcodec'), + 'acodec': m.group('acodec'), + 'vbr': int(m.group('vbr')), + 'abr': int(m.group('abr')), 'ext': m.group('ext'), 'width': int(m.group('width')), 'height': int(m.group('height')), diff --git a/youtube_dl/extractor/viddler.py b/youtube_dl/extractor/viddler.py index 826804a..75335df 100644 --- a/youtube_dl/extractor/viddler.py +++ b/youtube_dl/extractor/viddler.py @@ -47,7 +47,7 @@ class ViddlerIE(InfoExtractor): r"thumbnail\s*:\s*'([^']*)'", webpage, u'thumbnail', fatal=False) - info = { + return { '_type': 'video', 'id': video_id, 'title': title, @@ -56,9 +56,3 @@ class ViddlerIE(InfoExtractor): 'duration': duration, 'formats': formats, } - - # TODO: Remove when #980 has been merged - info['formats'][-1]['ext'] = determine_ext(info['formats'][-1]['url']) - info.update(info['formats'][-1]) - - return info diff --git a/youtube_dl/extractor/videofyme.py b/youtube_dl/extractor/videofyme.py index 94f64ff..912802d 100644 --- a/youtube_dl/extractor/videofyme.py +++ b/youtube_dl/extractor/videofyme.py @@ -1,5 +1,4 @@ import re -import xml.etree.ElementTree from .common import InfoExtractor from ..utils import ( @@ -27,9 +26,8 @@ class VideofyMeIE(InfoExtractor): def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) video_id = mobj.group('id') - config_xml = self._download_webpage('http://sunshine.videofy.me/?videoId=%s' % video_id, + config = self._download_xml('http://sunshine.videofy.me/?videoId=%s' % video_id, video_id) - config = xml.etree.ElementTree.fromstring(config_xml.encode('utf-8')) video = config.find('video') sources = video.find('sources') url_node = next(node for node in [find_xpath_attr(sources, 'source', 'id', 'HQ %s' % key) diff --git a/youtube_dl/extractor/videopremium.py b/youtube_dl/extractor/videopremium.py index 65f39b9..acae814 100644 --- a/youtube_dl/extractor/videopremium.py +++ b/youtube_dl/extractor/videopremium.py @@ -5,7 +5,7 @@ from .common import InfoExtractor class VideoPremiumIE(InfoExtractor): - _VALID_URL = r'(?:https?://)?(?:www\.)?videopremium\.tv/(?P\w+)(?:/.*)?' + _VALID_URL = r'(?:https?://)?(?:www\.)?videopremium\.(?:tv|me)/(?P\w+)(?:/.*)?' _TEST = { u'url': u'http://videopremium.tv/4w7oadjsf156', u'file': u'4w7oadjsf156.f4v', @@ -24,12 +24,16 @@ class VideoPremiumIE(InfoExtractor): webpage_url = 'http://videopremium.tv/' + video_id webpage = self._download_webpage(webpage_url, video_id) - self.report_extraction(video_id) + if re.match(r"^]*>window.location\s*=", webpage): + # Download again, we need a cookie + webpage = self._download_webpage( + webpage_url, video_id, + note=u'Downloading webpage again (with cookie)') - video_title = self._html_search_regex(r'\s*(.+?)\s*<', - webpage, u'video title') + video_title = self._html_search_regex( + r'\s*(.+?)\s*<', webpage, u'video title') - return [{ + return { 'id': video_id, 'url': "rtmp://e%d.md.iplay.md/play" % random.randint(1, 16), 'play_path': "mp4:%s.f4v" % video_id, @@ -37,4 +41,4 @@ class VideoPremiumIE(InfoExtractor): 'player_url': "http://videopremium.tv/uplayer/uppod.swf", 'ext': 'f4v', 'title': video_title, - }] + } diff --git a/youtube_dl/extractor/viki.py b/youtube_dl/extractor/viki.py new file mode 100644 index 0000000..2206a06 --- /dev/null +++ b/youtube_dl/extractor/viki.py @@ -0,0 +1,101 @@ +import re + +from ..utils import ( + ExtractorError, + unescapeHTML, + unified_strdate, +) +from .subtitles import SubtitlesInfoExtractor + + +class VikiIE(SubtitlesInfoExtractor): + IE_NAME = u'viki' + + _VALID_URL = r'^https?://(?:www\.)?viki\.com/videos/(?P[0-9]+v)' + _TEST = { + u'url': u'http://www.viki.com/videos/1023585v-heirs-episode-14', + u'file': u'1023585v.mp4', + u'md5': u'a21454021c2646f5433514177e2caa5f', + u'info_dict': { + u'title': u'Heirs Episode 14', + u'uploader': u'SBS', + u'description': u'md5:c4b17b9626dd4b143dcc4d855ba3474e', + u'upload_date': u'20131121', + u'age_limit': 13, + }, + u'skip': u'Blocked in the US', + } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group(1) + + webpage = self._download_webpage(url, video_id) + title = self._og_search_title(webpage) + description = self._og_search_description(webpage) + thumbnail = self._og_search_thumbnail(webpage) + + uploader_m = re.search( + r'Broadcast Network: \s*([^<]*)<', webpage) + if uploader_m is None: + uploader = None + else: + uploader = uploader_m.group(1).strip() + + rating_str = self._html_search_regex( + r'Rating: \s*([^<]*)<', webpage, + u'rating information', default='').strip() + RATINGS = { + 'G': 0, + 'PG': 10, + 'PG-13': 13, + 'R': 16, + 'NC': 18, + } + age_limit = RATINGS.get(rating_str) + + info_url = 'http://www.viki.com/player5_fragment/%s?action=show&controller=videos' % video_id + info_webpage = self._download_webpage( + info_url, video_id, note=u'Downloading info page') + if re.match(r'\s*]+src="([^"]+)"', info_webpage, u'video URL') + + upload_date_str = self._html_search_regex( + r'"created_at":"([^"]+)"', info_webpage, u'upload date') + upload_date = ( + unified_strdate(upload_date_str) + if upload_date_str is not None + else None + ) + + # subtitles + video_subtitles = self.extract_subtitles(video_id, info_webpage) + if self._downloader.params.get('listsubtitles', False): + self._list_available_subtitles(video_id, info_webpage) + return + + return { + 'id': video_id, + 'title': title, + 'url': video_url, + 'description': description, + 'thumbnail': thumbnail, + 'age_limit': age_limit, + 'uploader': uploader, + 'subtitles': video_subtitles, + 'upload_date': upload_date, + } + + def _get_available_subtitles(self, video_id, info_webpage): + res = {} + for sturl_html in re.findall(r'', info_webpage): + sturl = unescapeHTML(sturl_html) + m = re.search(r'/(?P[a-z]+)\.vtt', sturl) + if not m: + continue + res[m.group('lang')] = sturl + return res diff --git a/youtube_dl/extractor/vimeo.py b/youtube_dl/extractor/vimeo.py index d465bf2..f27763a 100644 --- a/youtube_dl/extractor/vimeo.py +++ b/youtube_dl/extractor/vimeo.py @@ -151,7 +151,7 @@ class VimeoIE(InfoExtractor): config = json.loads(config_json) except RegexNotFoundError: # For pro videos or player.vimeo.com urls - config = self._search_regex([r' = {config:({.+?}),assets:', r'c=({.+?);'], + config = self._search_regex([r' = {config:({.+?}),assets:', r'(?:c|b)=({.+?});'], webpage, u'info section', flags=re.DOTALL) config = json.loads(config) except Exception as e: @@ -249,25 +249,46 @@ class VimeoChannelIE(InfoExtractor): IE_NAME = u'vimeo:channel' _VALID_URL = r'(?:https?://)?vimeo.\com/channels/(?P[^/]+)' _MORE_PAGES_INDICATOR = r']+?title="(.*?)"' - def _real_extract(self, url): - mobj = re.match(self._VALID_URL, url) - channel_id = mobj.group('id') + def _extract_videos(self, list_id, base_url): video_ids = [] - for pagenum in itertools.count(1): - webpage = self._download_webpage('http://vimeo.com/channels/%s/videos/page:%d' % (channel_id, pagenum), - channel_id, u'Downloading page %s' % pagenum) + webpage = self._download_webpage( + '%s/videos/page:%d/' % (base_url, pagenum),list_id, + u'Downloading page %s' % pagenum) video_ids.extend(re.findall(r'id="clip_(\d+?)"', webpage)) if re.search(self._MORE_PAGES_INDICATOR, webpage, re.DOTALL) is None: break entries = [self.url_result('http://vimeo.com/%s' % video_id, 'Vimeo') for video_id in video_ids] - channel_title = self._html_search_regex(r'(.*?)' % channel_id, - webpage, u'channel title') + list_title = self._html_search_regex(self._TITLE_RE, webpage, + u'list title') return {'_type': 'playlist', - 'id': channel_id, - 'title': channel_title, + 'id': list_id, + 'title': list_title, 'entries': entries, } + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + channel_id = mobj.group('id') + return self._extract_videos(channel_id, 'http://vimeo.com/channels/%s' % channel_id) + + +class VimeoUserIE(VimeoChannelIE): + IE_NAME = u'vimeo:user' + _VALID_URL = r'(?:https?://)?vimeo.\com/(?P[^/]+)' + _TITLE_RE = r']+?class="user">([^<>]+?)' + + @classmethod + def suitable(cls, url): + if VimeoChannelIE.suitable(url) or VimeoIE.suitable(url): + return False + return super(VimeoUserIE, cls).suitable(url) + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + name = mobj.group('name') + return self._extract_videos(name, 'http://vimeo.com/%s' % name) diff --git a/youtube_dl/extractor/vine.py b/youtube_dl/extractor/vine.py index c4ec1f0..651ba31 100644 --- a/youtube_dl/extractor/vine.py +++ b/youtube_dl/extractor/vine.py @@ -27,7 +27,7 @@ class VineIE(InfoExtractor): video_url = self._html_search_regex(r'.*?

(.+?)

', + uploader = self._html_search_regex(r'

(.*?)

', webpage, u'uploader', fatal=False, flags=re.DOTALL) return [{ diff --git a/youtube_dl/extractor/xhamster.py b/youtube_dl/extractor/xhamster.py index 7444d33..279f75e 100644 --- a/youtube_dl/extractor/xhamster.py +++ b/youtube_dl/extractor/xhamster.py @@ -26,7 +26,7 @@ class XHamsterIE(InfoExtractor): { u'url': u'http://xhamster.com/movies/2221348/britney_spears_sexy_booty.html?hd', u'file': u'2221348.flv', - u'md5': u'e767b9475de189320f691f49c679c4c7', + u'md5': u'970a94178ca4118c5aa3aaea21211b81', u'info_dict': { u"upload_date": u"20130914", u"uploader_id": u"jojo747400", diff --git a/youtube_dl/extractor/xtube.py b/youtube_dl/extractor/xtube.py index 03ad88b..e3458d2 100644 --- a/youtube_dl/extractor/xtube.py +++ b/youtube_dl/extractor/xtube.py @@ -5,7 +5,6 @@ from .common import InfoExtractor from ..utils import ( compat_urllib_parse_urlparse, compat_urllib_request, - compat_urllib_parse, ) class XTubeIE(InfoExtractor): diff --git a/youtube_dl/extractor/yahoo.py b/youtube_dl/extractor/yahoo.py index 34e6afb..e457c47 100644 --- a/youtube_dl/extractor/yahoo.py +++ b/youtube_dl/extractor/yahoo.py @@ -17,27 +17,21 @@ class YahooIE(InfoExtractor): _TESTS = [ { u'url': u'http://screen.yahoo.com/julian-smith-travis-legg-watch-214727115.html', - u'file': u'214727115.flv', + u'file': u'214727115.mp4', + u'md5': u'4962b075c08be8690a922ee026d05e69', u'info_dict': { u'title': u'Julian Smith & Travis Legg Watch Julian Smith', u'description': u'Julian and Travis watch Julian Smith', }, - u'params': { - # Requires rtmpdump - u'skip_download': True, - }, }, { u'url': u'http://screen.yahoo.com/wired/codefellas-s1-ep12-cougar-lies-103000935.html', - u'file': u'103000935.flv', + u'file': u'103000935.mp4', + u'md5': u'd6e6fc6e1313c608f316ddad7b82b306', u'info_dict': { u'title': u'Codefellas - The Cougar Lies with Spanish Moss', u'description': u'Agent Topple\'s mustache does its dirty work, and Nicole brokers a deal for peace. But why is the NSA collecting millions of Instagram brunch photos? And if your waffles have nothing to hide, what are they so worried about?', }, - u'params': { - # Requires rtmpdump - u'skip_download': True, - }, }, ] @@ -46,15 +40,19 @@ class YahooIE(InfoExtractor): video_id = mobj.group('id') webpage = self._download_webpage(url, video_id) - items_json = self._search_regex(r'YVIDEO_INIT_ITEMS = ({.*?});$', + items_json = self._search_regex(r'mediaItems: ({.*?})$', webpage, u'items', flags=re.MULTILINE) items = json.loads(items_json) info = items['mediaItems']['query']['results']['mediaObj'][0] # The 'meta' field is not always in the video webpage, we request it # from another page long_id = info['id'] + return self._get_info(info['id'], video_id) + + def _get_info(self, long_id, video_id): query = ('SELECT * FROM yahoo.media.video.streams WHERE id="%s"' - ' AND plrs="86Gj0vCaSzV_Iuf6hNylf2"' % long_id) + ' AND plrs="86Gj0vCaSzV_Iuf6hNylf2" AND region="US"' + ' AND protocol="http"' % long_id) data = compat_urllib_parse.urlencode({ 'q': query, 'env': 'prod', @@ -91,17 +89,39 @@ class YahooIE(InfoExtractor): formats.append(format_info) formats = sorted(formats, key=lambda f:(f['height'], f['width'])) - info = { + return { 'id': video_id, 'title': meta['title'], 'formats': formats, 'description': clean_html(meta['description']), 'thumbnail': meta['thumbnail'], } - # TODO: Remove when #980 has been merged - info.update(formats[-1]) - return info + +class YahooNewsIE(YahooIE): + IE_NAME = 'yahoo:news' + _VALID_URL = r'http://news\.yahoo\.com/video/.*?-(?P\d*?)\.html' + + _TEST = { + u'url': u'http://news.yahoo.com/video/china-moses-crazy-blues-104538833.html', + u'md5': u'67010fdf3a08d290e060a4dd96baa07b', + u'info_dict': { + u'id': u'104538833', + u'ext': u'mp4', + u'title': u'China Moses Is Crazy About the Blues', + u'description': u'md5:9900ab8cd5808175c7b3fe55b979bed0', + }, + } + + # Overwrite YahooIE properties we don't want + _TESTS = [] + + def _real_extract(self, url): + mobj = re.match(self._VALID_URL, url) + video_id = mobj.group('id') + webpage = self._download_webpage(url, video_id) + long_id = self._search_regex(r'contentId: \'(.+?)\',', webpage, u'long id') + return self._get_info(long_id, video_id) class YahooSearchIE(SearchInfoExtractor): diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index c992cba..7fff761 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -11,7 +11,6 @@ import socket import string import struct import traceback -import xml.etree.ElementTree import zlib from .common import InfoExtractor, SearchInfoExtractor @@ -29,6 +28,7 @@ from ..utils import ( clean_html, get_cachedir, get_element_by_id, + get_element_by_attribute, ExtractorError, unescapeHTML, unified_strdate, @@ -139,10 +139,10 @@ class YoutubeBaseInfoExtractor(InfoExtractor): class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): IE_DESC = u'YouTube.com' - _VALID_URL = r"""^ + _VALID_URL = r"""(?x)^ ( - (?:https?://)? # http(s):// (optional) - (?:(?:(?:(?:\w+\.)?youtube(?:-nocookie)?\.com/| + (?:https?://|//)? # http(s):// or protocol-independent URL (optional) + (?:(?:(?:(?:\w+\.)?[yY][oO][uU][tT][uU][bB][eE](?:-nocookie)?\.com/| tube\.majestyc\.net/| youtube\.googleapis\.com/) # the various hostnames, with wildcard subdomains (?:.*?\#/)? # handle anchor (#/) redirect urls @@ -248,21 +248,21 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): '248': 'webm', } _video_dimensions = { - '5': '240x400', + '5': '400x240', '6': '???', '13': '???', - '17': '144x176', - '18': '360x640', - '22': '720x1280', - '34': '360x640', - '35': '480x854', - '36': '240x320', - '37': '1080x1920', - '38': '3072x4096', - '43': '360x640', - '44': '480x854', - '45': '720x1280', - '46': '1080x1920', + '17': '176x144', + '18': '640x360', + '22': '1280x720', + '34': '640x360', + '35': '854x480', + '36': '320x240', + '37': '1920x1080', + '38': '4096x3072', + '43': '640x360', + '44': '854x480', + '45': '1280x720', + '46': '1920x1080', '82': '360p', '83': '480p', '84': '720p', @@ -336,7 +336,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): u"uploader": u"Philipp Hagemeister", u"uploader_id": u"phihag", u"upload_date": u"20121002", - u"description": u"test chars: \"'/\\ä↭𝕐\n\nThis is a test video for youtube-dl.\n\nFor more information, contact phihag@phihag.de ." + u"description": u"test chars: \"'/\\ä↭𝕐\ntest URL: https://github.com/rg3/youtube-dl/issues/1892\n\nThis is a test video for youtube-dl.\n\nFor more information, contact phihag@phihag.de ." } }, { @@ -363,6 +363,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): u"uploader_id": u"justintimberlakeVEVO" } }, + { + u"url": u"//www.YouTube.com/watch?v=yZIXLfi8CZQ", + u"file": u"yZIXLfi8CZQ.mp4", + u"note": u"Embed-only video (#1746)", + u"info_dict": { + u"upload_date": u"20120608", + u"title": u"Principal Sexually Assaults A Teacher - Episode 117 - 8th June 2012", + u"description": u"md5:09b78bd971f1e3e289601dfba15ca4f7", + u"uploader": u"SET India", + u"uploader_id": u"setindia" + } + }, ] @@ -370,7 +382,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): def suitable(cls, url): """Receives a URL and returns True if suitable for this IE.""" if YoutubePlaylistIE.suitable(url): return False - return re.match(cls._VALID_URL, url, re.VERBOSE) is not None + return re.match(cls._VALID_URL, url) is not None def __init__(self, *args, **kwargs): super(YoutubeIE, self).__init__(*args, **kwargs) @@ -1019,6 +1031,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): """Turn the encrypted s field into a working signature""" if player_url is not None: + if player_url.startswith(u'//'): + player_url = u'https:' + player_url try: player_id = (player_url, len(s)) if player_id not in self._player_cache: @@ -1098,7 +1112,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): params = compat_urllib_parse.urlencode({ 'lang': lang, 'v': video_id, - 'fmt': self._downloader.params.get('subtitlesformat'), + 'fmt': self._downloader.params.get('subtitlesformat', 'srt'), 'name': l[0].encode('utf-8'), }) url = u'http://www.youtube.com/api/timedtext?' + params @@ -1111,7 +1125,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): def _get_available_automatic_caption(self, video_id, webpage): """We need the webpage for getting the captions url, pass it as an argument to speed up the process.""" - sub_format = self._downloader.params.get('subtitlesformat') + sub_format = self._downloader.params.get('subtitlesformat', 'srt') self.to_screen(u'%s: Looking for automatic captions' % video_id) mobj = re.search(r';ytplayer.config = ({.*?});', webpage) err_msg = u'Couldn\'t find automatic captions for %s' % video_id @@ -1130,8 +1144,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): 'asrs': 1, }) list_url = caption_url + '&' + list_params - list_page = self._download_webpage(list_url, video_id) - caption_list = xml.etree.ElementTree.fromstring(list_page.encode('utf-8')) + caption_list = self._download_xml(list_url, video_id) original_lang_node = caption_list.find('track') if original_lang_node is None or original_lang_node.attrib.get('kind') != 'asr' : self._downloader.report_warning(u'Video doesn\'t have automatic captions') @@ -1270,7 +1283,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): # We simulate the access to the video from www.youtube.com/v/{video_id} # this can be viewed without login into Youtube data = compat_urllib_parse.urlencode({'video_id': video_id, - 'el': 'embedded', + 'el': 'player_embedded', 'gl': 'US', 'hl': 'en', 'eurl': 'https://youtube.googleapis.com/v/' + video_id, @@ -1299,6 +1312,11 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): else: raise ExtractorError(u'"token" parameter not in video info for unknown reason') + if 'view_count' in video_info: + view_count = int(video_info['view_count'][0]) + else: + view_count = None + # Check for "rental" videos if 'ypc_video_rental_bar_text' in video_info and 'author' not in video_info: raise ExtractorError(u'"rental" videos not supported') @@ -1348,6 +1366,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): # description video_description = get_element_by_id("eow-description", video_webpage) if video_description: + video_description = re.sub(r'''(?x) + + [^<]+ + + ''', r'\1', video_description) video_description = clean_html(video_description) else: fd_mobj = re.search(r'[0-9A-Za-z_-]{11})&[^"]*?index=(?P\d+)' IE_NAME = u'youtube:playlist' @classmethod @@ -1515,6 +1544,27 @@ class YoutubePlaylistIE(InfoExtractor): """Receives a URL and returns True if suitable for this IE.""" return re.match(cls._VALID_URL, url, re.VERBOSE) is not None + def _real_initialize(self): + self._login() + + def _ids_to_results(self, ids): + return [self.url_result(vid_id, 'Youtube', video_id=vid_id) + for vid_id in ids] + + def _extract_mix(self, playlist_id): + # The mixes are generated from a a single video + # the id of the playlist is just 'RD' + video_id + url = 'https://youtube.com/watch?v=%s&list=%s' % (playlist_id[2:], playlist_id) + webpage = self._download_webpage(url, playlist_id, u'Downloading Youtube mix') + title_span = (get_element_by_attribute('class', 'title long-title', webpage) or + get_element_by_attribute('class', 'title ', webpage)) + title = clean_html(title_span) + video_re = r'data-index="\d+".*?href="/watch\?v=([0-9A-Za-z_-]{11})&[^"]*?list=%s' % re.escape(playlist_id) + ids = orderedSet(re.findall(video_re, webpage)) + url_results = self._ids_to_results(ids) + + return self.playlist_result(url_results, playlist_id, title) + def _real_extract(self, url): # Extract playlist id mobj = re.match(self._VALID_URL, url, re.VERBOSE) @@ -1528,45 +1578,33 @@ class YoutubePlaylistIE(InfoExtractor): video_id = query_dict['v'][0] if self._downloader.params.get('noplaylist'): self.to_screen(u'Downloading just video %s because of --no-playlist' % video_id) - return self.url_result('https://www.youtube.com/watch?v=' + video_id, 'Youtube') + return self.url_result(video_id, 'Youtube', video_id=video_id) else: self.to_screen(u'Downloading playlist PL%s - add --no-playlist to just download video %s' % (playlist_id, video_id)) - # Download playlist videos from API - videos = [] + if len(playlist_id) == 13: # 'RD' + 11 characters for the video id + # Mixes require a custom extraction process + return self._extract_mix(playlist_id) + + # Extract the video ids from the playlist pages + ids = [] for page_num in itertools.count(1): - start_index = self._MAX_RESULTS * (page_num - 1) + 1 - if start_index >= 1000: - self._downloader.report_warning(u'Max number of results reached') - break - url = self._TEMPLATE_URL % (playlist_id, self._MAX_RESULTS, start_index) + url = self._TEMPLATE_URL % (playlist_id, page_num) page = self._download_webpage(url, playlist_id, u'Downloading page #%s' % page_num) + matches = re.finditer(self._VIDEO_RE, page) + # We remove the duplicates and the link with index 0 + # (it's not the first video of the playlist) + new_ids = orderedSet(m.group('id') for m in matches if m.group('index') != '0') + ids.extend(new_ids) - try: - response = json.loads(page) - except ValueError as err: - raise ExtractorError(u'Invalid JSON in API response: ' + compat_str(err)) - - if 'feed' not in response: - raise ExtractorError(u'Got a malformed response from YouTube API') - playlist_title = response['feed']['title']['$t'] - if 'entry' not in response['feed']: - # Number of videos is a multiple of self._MAX_RESULTS + if re.search(self._MORE_PAGES_INDICATOR, page) is None: break - for entry in response['feed']['entry']: - index = entry['yt$position']['$t'] - if 'media$group' in entry and 'yt$videoid' in entry['media$group']: - videos.append(( - index, - 'https://www.youtube.com/watch?v=' + entry['media$group']['yt$videoid']['$t'] - )) + playlist_title = self._og_search_title(page) - videos = [v[1] for v in sorted(videos)] - - url_results = [self.url_result(vurl, 'Youtube') for vurl in videos] - return [self.playlist_result(url_results, playlist_id, playlist_title)] + url_results = self._ids_to_results(ids) + return self.playlist_result(url_results, playlist_id, playlist_title) class YoutubeChannelIE(InfoExtractor): @@ -1592,26 +1630,37 @@ class YoutubeChannelIE(InfoExtractor): # Download channel page channel_id = mobj.group(1) video_ids = [] + url = 'https://www.youtube.com/channel/%s/videos' % channel_id + channel_page = self._download_webpage(url, channel_id) + if re.search(r'channel-header-autogenerated-label', channel_page) is not None: + autogenerated = True + else: + autogenerated = False - # 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) - - page = json.loads(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 autogenerated: + # The videos are contained in a single page + # the ajax pages can't be used, they are empty + video_ids = self.extract_videos_from_page(channel_page) + else: + # 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) + + page = json.loads(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 self._downloader.to_screen(u'[youtube] Channel %s: Found %i videos' % (channel_id, len(video_ids))) - urls = ['http://www.youtube.com/watch?v=%s' % id for id in video_ids] - url_entries = [self.url_result(eurl, 'Youtube') for eurl in urls] - return [self.playlist_result(url_entries, channel_id)] + url_entries = [self.url_result(video_id, 'Youtube', video_id=video_id) + for video_id in video_ids] + return self.playlist_result(url_entries, channel_id) class YoutubeUserIE(InfoExtractor): @@ -1675,9 +1724,11 @@ class YoutubeUserIE(InfoExtractor): if len(ids_in_page) < self._GDATA_PAGE_SIZE: break - urls = ['http://www.youtube.com/watch?v=%s' % video_id for video_id in video_ids] - url_results = [self.url_result(rurl, 'Youtube') for rurl in urls] - return [self.playlist_result(url_results, playlist_title = username)] + url_results = [ + self.url_result(video_id, 'Youtube', video_id=video_id) + for video_id in video_ids] + return self.playlist_result(url_results, playlist_title=username) + class YoutubeSearchIE(SearchInfoExtractor): IE_DESC = u'YouTube.com searches' @@ -1718,10 +1769,12 @@ class YoutubeSearchIE(SearchInfoExtractor): if len(video_ids) > n: video_ids = video_ids[:n] - videos = [self.url_result('http://www.youtube.com/watch?v=%s' % id, 'Youtube') for id in video_ids] + videos = [self.url_result(video_id, 'Youtube', video_id=video_id) + for video_id in video_ids] return self.playlist_result(videos, query) class YoutubeSearchDateIE(YoutubeSearchIE): + IE_NAME = YoutubeSearchIE.IE_NAME + ':date' _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' @@ -1748,7 +1801,6 @@ class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor): Subclasses must define the _FEED_NAME and _PLAYLIST_TITLE properties. """ _LOGIN_REQUIRED = True - _PAGING_STEP = 30 # use action_load_personal_feed instead of action_load_system_feed _PERSONAL_FEED = False @@ -1768,9 +1820,8 @@ class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor): def _real_extract(self, url): feed_entries = [] - # The step argument is available only in 2.7 or higher - for i in itertools.count(0): - paging = i*self._PAGING_STEP + paging = 0 + for i in itertools.count(1): info = self._download_webpage(self._FEED_TEMPLATE % paging, u'%s feed' % self._FEED_NAME, u'Downloading page %s' % i) @@ -1778,9 +1829,12 @@ class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor): feed_html = info['feed_html'] m_ids = re.finditer(r'"/watch\?v=(.*?)["&]', feed_html) ids = orderedSet(m.group(1) for m in m_ids) - feed_entries.extend(self.url_result(id, 'Youtube') for id in ids) + feed_entries.extend( + self.url_result(video_id, 'Youtube', video_id=video_id) + for video_id in ids) if info['paging'] is None: break + paging = info['paging'] return self.playlist_result(feed_entries, playlist_title=self._PLAYLIST_TITLE) class YoutubeSubscriptionsIE(YoutubeFeedsInfoExtractor): @@ -1800,9 +1854,15 @@ class YoutubeWatchLaterIE(YoutubeFeedsInfoExtractor): _VALID_URL = r'https?://www\.youtube\.com/feed/watch_later|:ytwatchlater' _FEED_NAME = 'watch_later' _PLAYLIST_TITLE = u'Youtube Watch Later' - _PAGING_STEP = 100 _PERSONAL_FEED = True +class YoutubeHistoryIE(YoutubeFeedsInfoExtractor): + IE_DESC = u'Youtube watch history, "ythistory" keyword (requires authentication)' + _VALID_URL = u'https?://www\.youtube\.com/feed/history|:ythistory' + _FEED_NAME = 'history' + _PERSONAL_FEED = True + _PLAYLIST_TITLE = u'Youtube Watch History' + class YoutubeFavouritesIE(YoutubeBaseInfoExtractor): IE_NAME = u'youtube:favorites' IE_DESC = u'YouTube.com favourite videos, "ytfav" keyword (requires authentication)' diff --git a/youtube_dl/extractor/zdf.py b/youtube_dl/extractor/zdf.py index faed7ff..689f197 100644 --- a/youtube_dl/extractor/zdf.py +++ b/youtube_dl/extractor/zdf.py @@ -1,75 +1,125 @@ +# coding: utf-8 + +import operator import re from .common import InfoExtractor from ..utils import ( - determine_ext, - ExtractorError, + unified_strdate, ) class ZDFIE(InfoExtractor): - _VALID_URL = r'^http://www\.zdf\.de\/ZDFmediathek(?P#)?\/(.*beitrag\/video\/)(?P[^/\?]+)(?:\?.*)?' - _MEDIA_STREAM = r'' + _VALID_URL = r'^https?://www\.zdf\.de/ZDFmediathek(?P#)?/(.*beitrag/(?:video/)?)(?P[0-9]+)(?:/[^/?]+)?(?:\?.*)?' + + _TEST = { + u"url": u"http://www.zdf.de/ZDFmediathek/beitrag/video/2037704/ZDFspezial---Ende-des-Machtpokers--?bc=sts;stt", + u"file": u"2037704.webm", + u"info_dict": { + u"upload_date": u"20131127", + u"description": u"Union und SPD haben sich auf einen Koalitionsvertrag geeinigt. Aber was bedeutet das für die Bürger? Sehen Sie hierzu das ZDFspezial \"Ende des Machtpokers - Große Koalition für Deutschland\".", + u"uploader": u"spezial", + u"title": u"ZDFspezial - Ende des Machtpokers" + }, + u"skip": u"Videos on ZDF.de are depublicised in short order", + } def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) - if mobj is None: - raise ExtractorError(u'Invalid URL: %s' % url) video_id = mobj.group('video_id') - if mobj.group('hash'): - url = url.replace(u'#', u'', 1) + xml_url = u'http://www.zdf.de/ZDFmediathek/xmlservice/web/beitragsDetails?ak=web&id=%s' % video_id + doc = self._download_xml( + xml_url, video_id, + note=u'Downloading video info', + errnote=u'Failed to download video info') + + title = doc.find('.//information/title').text + description = doc.find('.//information/detail').text + uploader_node = doc.find('.//details/originChannelTitle') + uploader = None if uploader_node is None else uploader_node.text + duration_str = doc.find('.//details/length').text + duration_m = re.match(r'''(?x)^ + (?P[0-9]{2}) + :(?P[0-9]{2}) + :(?P[0-9]{2}) + (?:\.(?P[0-9]+)?) + ''', duration_str) + duration = ( + ( + (int(duration_m.group('hours')) * 60 * 60) + + (int(duration_m.group('minutes')) * 60) + + int(duration_m.group('seconds')) + ) + if duration_m + else None + ) + upload_date = unified_strdate(doc.find('.//details/airtime').text) + + def xml_to_format(fnode): + video_url = fnode.find('url').text + is_available = u'http://www.metafilegenerator' not in video_url - html = self._download_webpage(url, video_id) - streams = [m.groupdict() for m in re.finditer(self._MEDIA_STREAM, html)] - if streams is None: - raise ExtractorError(u'No media url found.') + format_id = fnode.attrib['basetype'] + format_m = re.match(r'''(?x) + (?P[^_]+)_(?P[^_]+)_(?P[^_]+)_ + (?P[^_]+)_(?P[^_]+)_(?P[^_]+) + ''', format_id) - # s['media_type'] == 'wstreaming' -> use 'Windows Media Player' and mms url - # s['media_type'] == 'hstreaming' -> use 'Quicktime' and rtsp url - # choose first/default media type and highest quality for now - def stream_pref(s): - TYPE_ORDER = ['ostreaming', 'hstreaming', 'wstreaming'] + ext = format_m.group('container') + is_supported = ext != 'f4f' + + PROTO_ORDER = ['http', 'rtmp', 'rtsp'] try: - type_pref = TYPE_ORDER.index(s['media_type']) + proto_pref = -PROTO_ORDER.index(format_m.group('proto')) except ValueError: - type_pref = 999 + proto_pref = 999 - QUALITY_ORDER = ['veryhigh', '300'] + quality = fnode.find('./quality').text + QUALITY_ORDER = ['veryhigh', '300', 'high', 'med', 'low'] try: - quality_pref = QUALITY_ORDER.index(s['quality']) + quality_pref = -QUALITY_ORDER.index(quality) except ValueError: quality_pref = 999 - return (type_pref, quality_pref) - - sorted_streams = sorted(streams, key=stream_pref) - if not sorted_streams: - raise ExtractorError(u'No stream found.') - stream = sorted_streams[0] - - media_link = self._download_webpage( - stream['video_url'], - video_id, - u'Get stream URL') + abr = int(fnode.find('./audioBitrate').text) // 1000 + vbr = int(fnode.find('./videoBitrate').text) // 1000 + pref = (is_available, is_supported, + proto_pref, quality_pref, vbr, abr) - MMS_STREAM = r'href="(?Pmms://[^"]*)"' - RTSP_STREAM = r'(?Prtsp://[^"]*.mp4)' + format_note = u'' + if not is_supported: + format_note += u'(unsupported)' + if not format_note: + format_note = None - mobj = re.search(self._MEDIA_STREAM, media_link) - if mobj is None: - mobj = re.search(RTSP_STREAM, media_link) - if mobj is None: - raise ExtractorError(u'Cannot extract mms:// or rtsp:// URL') - video_url = mobj.group('video_url') + return { + 'format_id': format_id + u'-' + quality, + 'url': video_url, + 'ext': ext, + 'acodec': format_m.group('acodec'), + 'vcodec': format_m.group('vcodec'), + 'abr': abr, + 'vbr': vbr, + 'width': int(fnode.find('./width').text), + 'height': int(fnode.find('./height').text), + 'filesize': int(fnode.find('./filesize').text), + 'format_note': format_note, + '_pref': pref, + '_available': is_available, + } - title = self._html_search_regex( - r'(.*?)

', - html, u'title') + format_nodes = doc.findall('.//formitaeten/formitaet') + formats = sorted(filter(lambda f: f['_available'], + map(xml_to_format, format_nodes)), + key=operator.itemgetter('_pref')) return { 'id': video_id, - 'url': video_url, 'title': title, - 'ext': determine_ext(video_url) + 'formats': formats, + 'description': description, + 'uploader': uploader, + 'duration': duration, + 'upload_date': upload_date, } diff --git a/youtube_dl/update.py b/youtube_dl/update.py index 0689a48..cd96701 100644 --- a/youtube_dl/update.py +++ b/youtube_dl/update.py @@ -2,11 +2,15 @@ import io import json import traceback import hashlib +import os import subprocess import sys from zipimport import zipimporter -from .utils import * +from .utils import ( + compat_str, + compat_urllib_request, +) from .version import __version__ def rsa_verify(message, signature, key): @@ -37,6 +41,7 @@ def rsa_verify(message, signature, key): if signature != sha256(message).digest(): return False return True + def update_self(to_screen, verbose): """Update the program file with the latest version from the repository""" @@ -78,6 +83,13 @@ def update_self(to_screen, verbose): return version_id = versions_info['latest'] + + def version_tuple(version_str): + return tuple(map(int, version_str.split('.'))) + if version_tuple(__version__) >= version_tuple(version_id): + to_screen(u'youtube-dl is up to date (%s)' % __version__) + return + to_screen(u'Updating to version ' + version_id + '...') version = versions_info['versions'][version_id] @@ -105,7 +117,7 @@ def update_self(to_screen, verbose): urlh = compat_urllib_request.urlopen(version['exe'][0]) newcontent = urlh.read() urlh.close() - except (IOError, OSError) as err: + except (IOError, OSError): if verbose: to_screen(compat_str(traceback.format_exc())) to_screen(u'ERROR: unable to download latest version') return @@ -118,7 +130,7 @@ def update_self(to_screen, verbose): try: with open(exe + '.new', 'wb') as outf: outf.write(newcontent) - except (IOError, OSError) as err: + except (IOError, OSError): if verbose: to_screen(compat_str(traceback.format_exc())) to_screen(u'ERROR: unable to write the new version') return @@ -137,7 +149,7 @@ start /b "" cmd /c del "%%~f0"&exit /b" subprocess.Popen([bat]) # Continues to run in the background return # Do not show premature success messages - except (IOError, OSError) as err: + except (IOError, OSError): if verbose: to_screen(compat_str(traceback.format_exc())) to_screen(u'ERROR: unable to overwrite current version') return @@ -148,7 +160,7 @@ start /b "" cmd /c del "%%~f0"&exit /b" urlh = compat_urllib_request.urlopen(version['bin'][0]) newcontent = urlh.read() urlh.close() - except (IOError, OSError) as err: + except (IOError, OSError): if verbose: to_screen(compat_str(traceback.format_exc())) to_screen(u'ERROR: unable to download latest version') return @@ -161,7 +173,7 @@ start /b "" cmd /c del "%%~f0"&exit /b" try: with open(filename, 'wb') as outf: outf.write(newcontent) - except (IOError, OSError) as err: + except (IOError, OSError): if verbose: to_screen(compat_str(traceback.format_exc())) to_screen(u'ERROR: unable to overwrite current version') return diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index 1d97853..c486ef8 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -8,13 +8,16 @@ import gzip import io import json import locale +import math import os import pipes import platform import re +import ssl import socket import sys import traceback +import xml.etree.ElementTree import zlib try: @@ -535,17 +538,34 @@ def formatSeconds(secs): else: return '%d' % secs -def make_HTTPS_handler(opts): - if sys.version_info < (3,2): - # Python's 2.x handler is very simplistic - return compat_urllib_request.HTTPSHandler() +def make_HTTPS_handler(opts_no_check_certificate): + if sys.version_info < (3, 2): + import httplib + + class HTTPSConnectionV3(httplib.HTTPSConnection): + def __init__(self, *args, **kwargs): + httplib.HTTPSConnection.__init__(self, *args, **kwargs) + + def connect(self): + sock = socket.create_connection((self.host, self.port), self.timeout) + if self._tunnel_host: + self.sock = sock + self._tunnel() + try: + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv3) + except ssl.SSLError: + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv23) + + class HTTPSHandlerV3(compat_urllib_request.HTTPSHandler): + def https_open(self, req): + return self.do_open(HTTPSConnectionV3, req) + return HTTPSHandlerV3() else: - import ssl - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context = ssl.SSLContext(ssl.PROTOCOL_SSLv3) context.set_default_verify_paths() context.verify_mode = (ssl.CERT_NONE - if opts.no_check_certificate + if opts_no_check_certificate else ssl.CERT_REQUIRED) return compat_urllib_request.HTTPSHandler(context=context) @@ -734,6 +754,8 @@ def unified_strdate(date_str): '%Y/%m/%d %H:%M:%S', '%d.%m.%Y %H:%M', '%Y-%m-%dT%H:%M:%SZ', + '%Y-%m-%dT%H:%M:%S.%fZ', + '%Y-%m-%dT%H:%M:%S.%f0Z', '%Y-%m-%dT%H:%M:%S', ] for expression in format_expressions: @@ -949,7 +971,16 @@ class locked_file(object): def shell_quote(args): - return ' '.join(map(pipes.quote, args)) + quoted_args = [] + encoding = sys.getfilesystemencoding() + if encoding is None: + encoding = 'utf-8' + for a in args: + if isinstance(a, bytes): + # We may get a filename encoded with 'encodeFilename' + a = a.decode(encoding) + quoted_args.append(pipes.quote(a)) + return u' '.join(quoted_args) def takewhile_inclusive(pred, seq): @@ -976,3 +1007,17 @@ def unsmuggle_url(smug_url): jsond = compat_parse_qs(sdata)[u'__youtubedl_smuggle'][0] data = json.loads(jsond) return url, data + + +def format_bytes(bytes): + if bytes is None: + return u'N/A' + if type(bytes) is str: + bytes = float(bytes) + if bytes == 0.0: + exponent = 0 + else: + exponent = int(math.log(bytes, 1024.0)) + suffix = [u'B', u'KiB', u'MiB', u'GiB', u'TiB', u'PiB', u'EiB', u'ZiB', u'YiB'][exponent] + converted = float(bytes) / float(1024 ** exponent) + return u'%.2f%s' % (converted, suffix) diff --git a/youtube_dl/version.py b/youtube_dl/version.py index 338e7ba..68b30bf 100644 --- a/youtube_dl/version.py +++ b/youtube_dl/version.py @@ -1,2 +1,2 @@ -__version__ = '2013.11.11' +__version__ = '2013.12.04' -- 2.41.1