Merge pull request #1409 from JohnyMoSwag/master (closes #1404)
authorJaime Marquínez Ferrándiz <jaime.marquinez.ferrandiz@gmail.com>
Fri, 13 Sep 2013 17:41:15 +0000 (19:41 +0200)
committerJaime Marquínez Ferrándiz <jaime.marquinez.ferrandiz@gmail.com>
Fri, 13 Sep 2013 17:52:56 +0000 (19:52 +0200)
added kickstarter IE

26 files changed:
.gitignore
README.md
devscripts/gh-pages/add-version.py
devscripts/gh-pages/update-sites.py
devscripts/youtube_genalgo.py
test/parameters.json
test/test_all_urls.py
test/test_dailymotion_subtitles.py [new file with mode: 0644]
test/test_youtube_subtitles.py
youtube_dl/FileDownloader.py
youtube_dl/YoutubeDL.py
youtube_dl/__init__.py
youtube_dl/extractor/__init__.py
youtube_dl/extractor/canalc2.py
youtube_dl/extractor/canalplus.py
youtube_dl/extractor/dailymotion.py
youtube_dl/extractor/francetv.py [new file with mode: 0644]
youtube_dl/extractor/funnyordie.py
youtube_dl/extractor/gamespot.py
youtube_dl/extractor/howcast.py
youtube_dl/extractor/kickstarter.py
youtube_dl/extractor/slideshare.py [new file with mode: 0644]
youtube_dl/extractor/sohu.py
youtube_dl/extractor/subtitles.py [new file with mode: 0644]
youtube_dl/extractor/youtube.py
youtube_dl/version.py

index ca4e8f35321bea65b42197d82b63769a35944168..61cb6bc3c8db2abcd0126c665cc6fbc526a101cd 100644 (file)
@@ -17,4 +17,10 @@ youtube-dl.tar.gz
 .coverage
 cover/
 updates_key.pem
-*.egg-info
\ No newline at end of file
+*.egg-info
+*.srt
+*.sbv
+*.vtt
+*.flv
+*.mp4
+*.part
index 2776cb3eb09a5af767d499ab5129b93cbfb9eeff..400e6cd480fa8dbc6182fdb760c2529f17f6c853 100644 (file)
--- a/README.md
+++ b/README.md
@@ -123,10 +123,8 @@ which means you can modify it, redistribute it or use it however you like.
                                only)
 
 ## Subtitle Options:
-    --write-sub                write subtitle file (currently youtube only)
-    --write-auto-sub           write automatic subtitle file (currently youtube
-                               only)
-    --only-sub                 [deprecated] alias of --skip-download
+    --write-sub                write subtitle file
+    --write-auto-sub           write automatic subtitle file (youtube only)
     --all-subs                 downloads all the available subtitles of the
                                video
     --list-subs                lists all available subtitles for the video
index 03733c35d21a8f73662555e637a5f9d026a0a94b..35865b2f30f9526f4a05b4bad594f078f5a56443 100755 (executable)
@@ -3,7 +3,8 @@
 import json
 import sys
 import hashlib
-import urllib.request
+import os.path
+
 
 if len(sys.argv) <= 1:
     print('Specify the version number as parameter')
@@ -25,6 +26,7 @@ filenames = {
     'tar': 'youtube-dl-%s.tar.gz' % version}
 build_dir = os.path.join('..', '..', 'build', version)
 for key, filename in filenames.items():
+    url = 'https://yt-dl.org/downloads/%s/%s' % (version, filename)
     fn = os.path.join(build_dir, filename)
     with open(fn, 'rb') as f:
         data = f.read()
index fa4bb2beb1f8e56f9272a82aadaf89bb59344758..33f242480e7c967ec0ca89fe57b5392dda0a524f 100755 (executable)
@@ -14,7 +14,7 @@ def main():
         template = tmplf.read()
 
     ie_htmls = []
-    for ie in sorted(youtube_dl.gen_extractors(), key=lambda i: i.IE_NAME):
+    for ie in sorted(youtube_dl.gen_extractors(), key=lambda i: i.IE_NAME.lower()):
         ie_html = '<b>{}</b>'.format(ie.IE_NAME)
         try:
             ie_html += ': {}'.format(ie.IE_DESC)
index 3d1f83a9375e199cea0fc5c1ddad90e491043a33..b390c7e2ead61db13197020327d082dbae30f0ba 100644 (file)
@@ -23,18 +23,18 @@ tests = [
     # 86 - vfluy6kdb 2013/09/06
     ("qwertyuioplkjhgfdsazxcvbnm1234567890QWERTYUIOPLKJHGFDSAZXCVBNM!@#$%^&*()_-+={[|};?/>.<",
      "yuioplkjhgfdsazxcvbnm12345678q0QWrRTYUIOELKJHGFD-AZXCVBNM!@#$%^&*()_<+={[|};?/>.S"),
-    # 85
+    # 85 - vflkuzxcs 2013/09/11
     ("qwertyuioplkjhgfdsazxcvbnm1234567890QWERTYUIOPLKJHGFDSAZXCVBNM!@#$%^&*()_-+={[};?/>.<",
-     ".>/?;}[{=+-_)(*&^%$#@!MNBVCXZASDFGHJKLPOIUYTREWQ0q876543r1mnbvcx9asdfghjklpoiuyt2"),
+     "T>/?;}[{=+-_)(*&^%$#@!MNBVCXZASDFGHJKLPOvUY.REWQ0987654321mnbqcxzasdfghjklpoiuytr"),
     # 84 - vflg0g8PQ 2013/08/29 (sporadic)
     ("qwertyuioplkjhgfdsazxcvbnm1234567890QWERTYUIOPLKJHGFDSAZXCVBNM!@#$%^&*()_-+={[};?>.<",
      ">?;}[{=+-_)(*&^%$#@!MNBVCXZASDFGHJKLPOIUYTREWq0987654321mnbvcxzasdfghjklpoiuytr"),
     # 83
     ("qwertyuioplkjhgfdsazxcvbnm1234567890QWERTYUIOPLKJHGFDSAZXCVBNM!#$%^&*()_+={[};?/>.<",
      ".>/?;}[{=+_)(*&^%<#!MNBVCXZASPFGHJKLwOIUYTREWQ0987654321mnbvcxzasdfghjklpoiuytreq"),
-    # 82 - vflZK4ZYR 2013/08/23
+    # 82 - vflGNjMhJ 2013/09/12
     ("qwertyuioplkjhgfdsazxcvbnm1234567890QWERTYUIOPLKHGFDSAZXCVBNM!@#$%^&*(-+={[};?/>.<",
-     "wertyuioplkjhgfdsaqxcvbnm1234567890QWERTYUIOPLKHGFDSAZXCVBNM!@#$%^&z(-+={[};?/>.<"),
+     ".>/?;}[<=+-(*&^%$#@!MNBVCXeASDFGHKLPOqUYTREWQ0987654321mnbvcxzasdfghjklpoiuytrIwZ"),
     # 81 - vflLC8JvQ 2013/07/25
     ("qwertyuioplkjhgfdsazxcvbnm1234567890QWERTYUIOPLKHGFDSAZXCVBNM!@#$%^&*(-+={[};?/>.",
      "C>/?;}[{=+-(*&^%$#@!MNBVYXZASDFGHKLPOIU.TREWQ0q87659321mnbvcxzasdfghjkl4oiuytrewp"),
index 96998b5c39972bb43faddbb5711d07c9ce31574e..f042880edbf0bcab661900b86809ade8ec7697af 100644 (file)
@@ -38,7 +38,6 @@
     "writedescription": false, 
     "writeinfojson": true, 
     "writesubtitles": false,
-    "onlysubtitles": false,
     "allsubtitles": false,
     "listssubtitles": false
 }
index 5d8d93e0e9db73063e097812f0403b02309527ed..99fc7bd28c46393513795de17ac7eee76cb4b9ba 100644 (file)
@@ -72,10 +72,13 @@ class TestAllURLsMatching(unittest.TestCase):
         self.assertTrue(JustinTVIE.suitable(u"http://www.twitch.tv/tsm_theoddone/c/2349361"))
 
     def test_youtube_extract(self):
-        self.assertEqual(YoutubeIE()._extract_id('http://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc')
-        self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc')
-        self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?feature=player_embedded&v=BaW_jenozKc'), 'BaW_jenozKc')
-        self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch_popup?v=BaW_jenozKc'), 'BaW_jenozKc')
+        assertExtractId = lambda url, id: self.assertEqual(YoutubeIE()._extract_id(url), id)
+        assertExtractId('http://www.youtube.com/watch?&v=BaW_jenozKc', 'BaW_jenozKc')
+        assertExtractId('https://www.youtube.com/watch?&v=BaW_jenozKc', 'BaW_jenozKc')
+        assertExtractId('https://www.youtube.com/watch?feature=player_embedded&v=BaW_jenozKc', 'BaW_jenozKc')
+        assertExtractId('https://www.youtube.com/watch_popup?v=BaW_jenozKc', 'BaW_jenozKc')
+        assertExtractId('http://www.youtube.com/watch?v=BaW_jenozKcsharePLED17F32AD9753930', 'BaW_jenozKc')
+        assertExtractId('BaW_jenozKc', 'BaW_jenozKc')
 
     def test_no_duplicates(self):
         ies = gen_extractors()
diff --git a/test/test_dailymotion_subtitles.py b/test/test_dailymotion_subtitles.py
new file mode 100644 (file)
index 0000000..bcd9f79
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+import sys
+import unittest
+import json
+import io
+import hashlib
+
+# Allow direct execution
+import os
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from youtube_dl.extractor import DailymotionIE
+from youtube_dl.utils import *
+from helper import FakeYDL
+
+md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
+
+class TestDailymotionSubtitles(unittest.TestCase):
+    def setUp(self):
+        self.DL = FakeYDL()
+        self.url = 'http://www.dailymotion.com/video/xczg00'
+    def getInfoDict(self):
+        IE = DailymotionIE(self.DL)
+        info_dict = IE.extract(self.url)
+        return info_dict
+    def getSubtitles(self):
+        info_dict = self.getInfoDict()
+        return info_dict[0]['subtitles']
+    def test_no_writesubtitles(self):
+        subtitles = self.getSubtitles()
+        self.assertEqual(subtitles, None)
+    def test_subtitles(self):
+        self.DL.params['writesubtitles'] = True
+        subtitles = self.getSubtitles()
+        self.assertEqual(md5(subtitles['en']), '976553874490cba125086bbfea3ff76f')
+    def test_subtitles_lang(self):
+        self.DL.params['writesubtitles'] = True
+        self.DL.params['subtitleslangs'] = ['fr']
+        subtitles = self.getSubtitles()
+        self.assertEqual(md5(subtitles['fr']), '594564ec7d588942e384e920e5341792')
+    def test_allsubtitles(self):
+        self.DL.params['allsubtitles'] = True
+        subtitles = self.getSubtitles()
+        self.assertEqual(len(subtitles.keys()), 5)
+    def test_list_subtitles(self):
+        self.DL.params['listsubtitles'] = True
+        info_dict = self.getInfoDict()
+        self.assertEqual(info_dict, None)
+    def test_automatic_captions(self):
+        self.DL.params['writeautomaticsub'] = True
+        self.DL.params['subtitleslang'] = ['en']
+        subtitles = self.getSubtitles()
+        self.assertTrue(len(subtitles.keys()) == 0)
+    def test_nosubtitles(self):
+        self.url = 'http://www.dailymotion.com/video/x12u166_le-zapping-tele-star-du-08-aout-2013_tv'
+        self.DL.params['allsubtitles'] = True
+        subtitles = self.getSubtitles()
+        self.assertEqual(len(subtitles), 0)
+    def test_multiple_langs(self):
+        self.DL.params['writesubtitles'] = True
+        langs = ['es', 'fr', 'de']
+        self.DL.params['subtitleslangs'] = langs
+        subtitles = self.getSubtitles()
+        for lang in langs:
+            self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang)
+
+if __name__ == '__main__':
+    unittest.main()
index 641206277bbeec22d11339bb3b143df5adff834e..5632871aca37eac4478be6efbdd2d39bbe19daba 100644 (file)
@@ -18,85 +18,63 @@ md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
 
 class TestYoutubeSubtitles(unittest.TestCase):
     def setUp(self):
-        DL = FakeYDL()
-        DL.params['allsubtitles'] = False
-        DL.params['writesubtitles'] = False
-        DL.params['subtitlesformat'] = 'srt'
-        DL.params['listsubtitles'] = False
-    def test_youtube_no_subtitles(self):
-        DL = FakeYDL()
-        DL.params['writesubtitles'] = False
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
-        subtitles = info_dict[0]['subtitles']
+        self.DL = FakeYDL()
+        self.url = 'QRS8MkLhQmM'
+    def getInfoDict(self):
+        IE = YoutubeIE(self.DL)
+        info_dict = IE.extract(self.url)
+        return info_dict
+    def getSubtitles(self):
+        info_dict = self.getInfoDict()
+        return info_dict[0]['subtitles']        
+    def test_youtube_no_writesubtitles(self):
+        self.DL.params['writesubtitles'] = False
+        subtitles = self.getSubtitles()
         self.assertEqual(subtitles, None)
     def test_youtube_subtitles(self):
-        DL = FakeYDL()
-        DL.params['writesubtitles'] = True
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
-        sub = info_dict[0]['subtitles']['en']
-        self.assertEqual(md5(sub), '4cd9278a35ba2305f47354ee13472260')
-    def test_youtube_subtitles_it(self):
-        DL = FakeYDL()
-        DL.params['writesubtitles'] = True
-        DL.params['subtitleslangs'] = ['it']
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
-        sub = info_dict[0]['subtitles']['it']
-        self.assertEqual(md5(sub), '164a51f16f260476a05b50fe4c2f161d')
-    def test_youtube_onlysubtitles(self):
-        DL = FakeYDL()
-        DL.params['writesubtitles'] = True
-        DL.params['onlysubtitles'] = True
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
-        sub = info_dict[0]['subtitles']['en']
-        self.assertEqual(md5(sub), '4cd9278a35ba2305f47354ee13472260')
+        self.DL.params['writesubtitles'] = True
+        subtitles = self.getSubtitles()
+        self.assertEqual(md5(subtitles['en']), '4cd9278a35ba2305f47354ee13472260')
+    def test_youtube_subtitles_lang(self):
+        self.DL.params['writesubtitles'] = True
+        self.DL.params['subtitleslangs'] = ['it']
+        subtitles = self.getSubtitles()
+        self.assertEqual(md5(subtitles['it']), '164a51f16f260476a05b50fe4c2f161d')
     def test_youtube_allsubtitles(self):
-        DL = FakeYDL()
-        DL.params['allsubtitles'] = True
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
-        subtitles = info_dict[0]['subtitles']
+        self.DL.params['allsubtitles'] = True
+        subtitles = self.getSubtitles()
         self.assertEqual(len(subtitles.keys()), 13)
     def test_youtube_subtitles_sbv_format(self):
-        DL = FakeYDL()
-        DL.params['writesubtitles'] = True
-        DL.params['subtitlesformat'] = 'sbv'
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
-        sub = info_dict[0]['subtitles']['en']
-        self.assertEqual(md5(sub), '13aeaa0c245a8bed9a451cb643e3ad8b')
+        self.DL.params['writesubtitles'] = True
+        self.DL.params['subtitlesformat'] = 'sbv'
+        subtitles = self.getSubtitles()
+        self.assertEqual(md5(subtitles['en']), '13aeaa0c245a8bed9a451cb643e3ad8b')
     def test_youtube_subtitles_vtt_format(self):
-        DL = FakeYDL()
-        DL.params['writesubtitles'] = True
-        DL.params['subtitlesformat'] = 'vtt'
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
-        sub = info_dict[0]['subtitles']['en']
-        self.assertEqual(md5(sub), '356cdc577fde0c6783b9b822e7206ff7')
+        self.DL.params['writesubtitles'] = True
+        self.DL.params['subtitlesformat'] = 'vtt'
+        subtitles = self.getSubtitles()
+        self.assertEqual(md5(subtitles['en']), '356cdc577fde0c6783b9b822e7206ff7')
     def test_youtube_list_subtitles(self):
-        DL = FakeYDL()
-        DL.params['listsubtitles'] = True
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('QRS8MkLhQmM')
+        self.DL.params['listsubtitles'] = True
+        info_dict = self.getInfoDict()
         self.assertEqual(info_dict, None)
     def test_youtube_automatic_captions(self):
-        DL = FakeYDL()
-        DL.params['writeautomaticsub'] = True
-        DL.params['subtitleslangs'] = ['it']
-        IE = YoutubeIE(DL)
-        info_dict = IE.extract('8YoUxe5ncPo')
-        sub = info_dict[0]['subtitles']['it']
-        self.assertTrue(sub is not None)
+        self.url = '8YoUxe5ncPo'
+        self.DL.params['writeautomaticsub'] = True
+        self.DL.params['subtitleslangs'] = ['it']
+        subtitles = self.getSubtitles()
+        self.assertTrue(subtitles['it'] is not None)
+    def test_youtube_nosubtitles(self):
+        self.url = 'sAjKT8FhjI8'
+        self.DL.params['allsubtitles'] = True
+        subtitles = self.getSubtitles()
+        self.assertEqual(len(subtitles), 0)
     def test_youtube_multiple_langs(self):
-        DL = FakeYDL()
-        DL.params['writesubtitles'] = True
+        self.url = 'QRS8MkLhQmM'
+        self.DL.params['writesubtitles'] = True
         langs = ['it', 'fr', 'de']
-        DL.params['subtitleslangs'] = langs
-        IE = YoutubeIE(DL)
-        subtitles = IE.extract('QRS8MkLhQmM')[0]['subtitles']
+        self.DL.params['subtitleslangs'] = langs
+        subtitles = self.getSubtitles()
         for lang in langs:
             self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang)
 
index 7c5ac4bc2ecae6d3440266a98b2034ac5f56867a..0b5a5d77d409ca0a193a9ce6bf6890f88767459d 100644 (file)
@@ -66,7 +66,7 @@ class FileDownloader(object):
     @staticmethod
     def format_seconds(seconds):
         (mins, secs) = divmod(seconds, 60)
-        (hours, eta_mins) = divmod(mins, 60)
+        (hours, mins) = divmod(mins, 60)
         if hours > 99:
             return '--:--:--'
         if hours == 0:
index b289bd9e26bbc9993e6f1295a31d20b3275f5f48..c2f992b8e00f333f59da38ffd59153183d057281 100644 (file)
@@ -492,6 +492,8 @@ class YoutubeDL(object):
                 self.report_writedescription(descfn)
                 with io.open(encodeFilename(descfn), 'w', encoding='utf-8') as descfile:
                     descfile.write(info_dict['description'])
+            except (KeyError, TypeError):
+                self.report_warning(u'There\'s no description to write.')
             except (OSError, IOError):
                 self.report_error(u'Cannot write description file ' + descfn)
                 return
index 4213ec1d51fd1ea0029056227b95fa2d715674c1..696e54f49c21478c1ae70dd22ace6e73c5f79246 100644 (file)
@@ -29,6 +29,7 @@ __authors__  = (
     'Albert Kim',
     'Pierre Rudloff',
     'Huarong Huo',
+    'Ismael Mejía',
 )
 
 __license__ = 'Public Domain'
@@ -205,13 +206,10 @@ def parseOpts(overrideArguments=None):
 
     subtitles.add_option('--write-sub', '--write-srt',
             action='store_true', dest='writesubtitles',
-            help='write subtitle file (currently youtube only)', default=False)
+            help='write subtitle file', default=False)
     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
             action='store_true', dest='writeautomaticsub',
-            help='write automatic subtitle file (currently youtube only)', default=False)
-    subtitles.add_option('--only-sub',
-            action='store_true', dest='skip_download',
-            help='[deprecated] alias of --skip-download', default=False)
+            help='write automatic subtitle file (youtube only)', default=False)
     subtitles.add_option('--all-subs',
             action='store_true', dest='allsubtitles',
             help='downloads all the available subtitles of the video', default=False)
@@ -222,7 +220,7 @@ def parseOpts(overrideArguments=None):
             action='store', dest='subtitlesformat', metavar='FORMAT',
             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
-            action='callback', dest='subtitleslang', metavar='LANGS', type='str',
+            action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
             default=[], callback=_comma_separated_values_options_callback,
             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
 
@@ -593,7 +591,7 @@ def _real_main(argv=None):
         'allsubtitles': opts.allsubtitles,
         'listsubtitles': opts.listsubtitles,
         'subtitlesformat': opts.subtitlesformat,
-        'subtitleslangs': opts.subtitleslang,
+        'subtitleslangs': opts.subtitleslangs,
         'matchtitle': decodeOption(opts.matchtitle),
         'rejecttitle': decodeOption(opts.rejecttitle),
         'max_downloads': opts.max_downloads,
index 5711b6bba7c1963a60add936fbaf0e984b82a58c..d093d5c282997c7dc31d24ba43d70138c1d8fdfb 100644 (file)
@@ -29,6 +29,10 @@ from .escapist import EscapistIE
 from .exfm import ExfmIE
 from .facebook import FacebookIE
 from .flickr import FlickrIE
+from .francetv import (
+    PluzzIE,
+    FranceTvInfoIE,
+)
 from .freesound import FreesoundIE
 from .funnyordie import FunnyOrDieIE
 from .gamespot import GameSpotIE
@@ -76,6 +80,7 @@ from .roxwel import RoxwelIE
 from .rtlnow import RTLnowIE
 from .sina import SinaIE
 from .slashdot import SlashdotIE
+from .slideshare import SlideshareIE
 from .sohu import SohuIE
 from .soundcloud import SoundcloudIE, SoundcloudSetIE
 from .spiegel import SpiegelIE
index 50832217a8ad5fe52b4cfa62731171b6dfaadcd6..e7f4fa9fdc569b9eb559fd3358169e81c3cdfa3a 100644 (file)
@@ -5,7 +5,7 @@ from .common import InfoExtractor
 
 
 class Canalc2IE(InfoExtractor):
-    _IE_NAME = 'canalc2.tv'
+    IE_NAME = 'canalc2.tv'
     _VALID_URL = r'http://.*?\.canalc2\.tv/video\.asp\?idVideo=(\d+)&voir=oui'
 
     _TEST = {
index 1f02519a0149ad85aad3886bf0a01e5c986064c0..1db9b24cf204cc26d68b1a1bdaff93577c3ae903 100644 (file)
@@ -1,3 +1,4 @@
+# encoding: utf-8
 import re
 import xml.etree.ElementTree
 
@@ -5,24 +6,29 @@ from .common import InfoExtractor
 from ..utils import unified_strdate
 
 class CanalplusIE(InfoExtractor):
-    _VALID_URL = r'https?://(www\.canalplus\.fr/.*?\?vid=|player\.canalplus\.fr/#/)(?P<id>\d+)'
+    _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'
     IE_NAME = u'canalplus.fr'
 
     _TEST = {
-        u'url': u'http://www.canalplus.fr/c-divertissement/pid3351-c-le-petit-journal.html?vid=889861',
-        u'file': u'889861.flv',
-        u'md5': u'590a888158b5f0d6832f84001fbf3e99',
+        u'url': u'http://www.canalplus.fr/c-infos-documentaires/pid1830-c-zapping.html?vid=922470',
+        u'file': u'922470.flv',
         u'info_dict': {
-            u'title': u'Le Petit Journal 20/06/13 - La guerre des drone',
-            u'upload_date': u'20130620',
+            u'title': u'Zapping - 26/08/13',
+            u'description': u'Le meilleur de toutes les chaînes, tous les jours.\nEmission du 26 août 2013',
+            u'upload_date': u'20130826',
+        },
+        u'params': {
+            u'skip_download': True,
         },
-        u'skip': u'Requires rtmpdump'
     }
 
     def _real_extract(self, url):
         mobj = re.match(self._VALID_URL, url)
         video_id = mobj.group('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, 
                                            u'Downloading video info')
@@ -43,4 +49,6 @@ class CanalplusIE(InfoExtractor):
                 'ext': 'flv',
                 'upload_date': unified_strdate(infos.find('PUBLICATION/DATE').text),
                 'thumbnail': media.find('IMAGES/GRAND').text,
+                'description': infos.find('DESCRIPTION').text,
+                'view_count': int(infos.find('NB_VUES').text),
                 }
index 3c616e0896cecc33077c9bd7ae0c9b15c5d4ddbd..360113f9c46840ad4e0f38a8cc9974f36d6a25c5 100644 (file)
@@ -3,15 +3,19 @@ import json
 import itertools
 
 from .common import InfoExtractor
+from .subtitles import SubtitlesInfoExtractor
+
 from ..utils import (
     compat_urllib_request,
+    compat_str,
     get_element_by_attribute,
     get_element_by_id,
 
     ExtractorError,
 )
 
-class DailymotionIE(InfoExtractor):
+
+class DailymotionIE(SubtitlesInfoExtractor):
     """Information Extractor for Dailymotion"""
 
     _VALID_URL = r'(?i)(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/(?:embed/)?video/([^/]+)'
@@ -73,6 +77,12 @@ class DailymotionIE(InfoExtractor):
             raise ExtractorError(u'Unable to extract video URL')
         video_url = info[max_quality]
 
+        # subtitles
+        video_subtitles = self.extract_subtitles(video_id)
+        if self._downloader.params.get('listsubtitles', False):
+            self._list_available_subtitles(video_id)
+            return
+
         return [{
             'id':       video_id,
             'url':      video_url,
@@ -80,9 +90,25 @@ class DailymotionIE(InfoExtractor):
             'upload_date':  video_upload_date,
             'title':    self._og_search_title(webpage),
             'ext':      video_extension,
+            'subtitles':    video_subtitles,
             'thumbnail': info['thumbnail_url']
         }]
 
+    def _get_available_subtitles(self, video_id):
+        try:
+            sub_list = self._download_webpage(
+                'https://api.dailymotion.com/video/%s/subtitles?fields=id,language,url' % video_id,
+                video_id, note=False)
+        except ExtractorError as err:
+            self._downloader.report_warning(u'unable to download video subtitles: %s' % compat_str(err))
+            return {}
+        info = json.loads(sub_list)
+        if (info['total'] > 0):
+            sub_lang_list = dict((l['language'], l['url']) for l in info['list'])
+            return sub_lang_list
+        self._downloader.report_warning(u'video doesn\'t have subtitles')
+        return {}
+
 
 class DailymotionPlaylistIE(InfoExtractor):
     _VALID_URL = r'(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/playlist/(?P<id>.+?)/'
diff --git a/youtube_dl/extractor/francetv.py b/youtube_dl/extractor/francetv.py
new file mode 100644 (file)
index 0000000..f2b12c8
--- /dev/null
@@ -0,0 +1,77 @@
+# encoding: utf-8
+import re
+import xml.etree.ElementTree
+
+from .common import InfoExtractor
+from ..utils import (
+    compat_urlparse,
+)
+
+
+class FranceTVBaseInfoExtractor(InfoExtractor):
+    def _extract_video(self, video_id):
+        xml_desc = self._download_webpage(
+            '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')
+        video_url = video_url.replace('/z/', '/i/')
+        thumbnail_path = info.find('image').text
+
+        return {'id': video_id,
+                'ext': 'mp4',
+                'url': video_url,
+                'title': info.find('titre').text,
+                'thumbnail': compat_urlparse.urljoin('http://pluzz.francetv.fr', thumbnail_path),
+                'description': info.find('synopsis').text,
+                }
+
+
+class PluzzIE(FranceTVBaseInfoExtractor):
+    IE_NAME = u'pluzz.francetv.fr'
+    _VALID_URL = r'https?://pluzz\.francetv\.fr/videos/(.*?)\.html'
+
+    _TEST = {
+        u'url': u'http://pluzz.francetv.fr/videos/allo_rufo_saison5_,88439064.html',
+        u'file': u'88439064.mp4',
+        u'info_dict': {
+            u'title': u'Allô Rufo',
+            u'description': u'md5:d909f1ebdf963814b65772aea250400e',
+        },
+        u'params': {
+            u'skip_download': True,
+        },
+    }
+
+    def _real_extract(self, url):
+        title = re.match(self._VALID_URL, url).group(1)
+        webpage = self._download_webpage(url, title)
+        video_id = self._search_regex(
+            r'data-diffusion="(\d+)"', webpage, 'ID')
+        return self._extract_video(video_id)
+
+
+class FranceTvInfoIE(FranceTVBaseInfoExtractor):
+    IE_NAME = u'francetvinfo.fr'
+    _VALID_URL = r'https?://www\.francetvinfo\.fr/replay.*/(?P<title>.+).html'
+
+    _TEST = {
+        u'url': u'http://www.francetvinfo.fr/replay-jt/france-3/soir-3/jt-grand-soir-3-lundi-26-aout-2013_393427.html',
+        u'file': u'84981923.mp4',
+        u'info_dict': {
+            u'title': u'Soir 3',
+        },
+        u'params': {
+            u'skip_download': True,
+        },
+    }
+
+    def _real_extract(self, url):
+        mobj = re.match(self._VALID_URL, url)
+        page_title = mobj.group('title')
+        webpage = self._download_webpage(url, page_title)
+        video_id = self._search_regex(r'id-video=(\d+?)"', webpage, u'video id')
+        return self._extract_video(video_id)
index 4508f0dfac29a85d86533c2f781414b9b17d10cb..f3d86a711c749734e78b210682f034d37e4536cb 100644 (file)
@@ -21,7 +21,7 @@ class FunnyOrDieIE(InfoExtractor):
         video_id = mobj.group('id')
         webpage = self._download_webpage(url, video_id)
 
-        video_url = self._search_regex(r'type: "video/mp4", src: "(.*?)"',
+        video_url = self._search_regex(r'type="video/mp4" src="(.*?)"',
             webpage, u'video URL', flags=re.DOTALL)
 
         info = {
index 7585b70618d1e4f92e8297fbf4d1397359a5224b..cd3bbe65f5dd9891f5cf4a68fb1adcd8a45c4196 100644 (file)
@@ -14,7 +14,7 @@ class GameSpotIE(InfoExtractor):
         u"file": u"6410818.mp4",
         u"md5": u"b2a30deaa8654fcccd43713a6b6a4825",
         u"info_dict": {
-            u"title": u"Arma III - Community Guide: SITREP I",
+            u"title": u"Arma 3 - Community Guide: SITREP I",
             u"upload_date": u"20130627", 
         }
     }
index 6104c4b5e910c5bcd61574b26b1bf6a57ba5bbce..46954337f25e1cbd7bae89e7da76d4e93ecc8c9e 100644 (file)
@@ -19,8 +19,7 @@ class HowcastIE(InfoExtractor):
         mobj = re.match(self._VALID_URL, url)
 
         video_id = mobj.group('id')
-        webpage_url = 'http://www.howcast.com/videos/' + video_id
-        webpage = self._download_webpage(webpage_url, video_id)
+        webpage = self._download_webpage(url, video_id)
 
         self.report_extraction(video_id)
 
index 7f6f2b064ef99321860fc642b11cfd55639050bb..50bc883ef4c2a0aafae84f8e31c995f833d7d7ee 100644 (file)
@@ -4,40 +4,34 @@ from .common import InfoExtractor
 
 
 class KickStarterIE(InfoExtractor):
-    _VALID_URL = r'https?://www\.kickstarter\.com/projects/(?P<id>.*)/.*\?'
+    _VALID_URL = r'https?://www\.kickstarter\.com/projects/(?P<id>\d*)/.*'
     _TEST = {
-        "url": "https://www.kickstarter.com/projects/1404461844/intersection-the-story-of-josh-grant?ref=home_location",
-        "file": "1404461844.mp4",
-        "md5": "c81addca81327ffa66c642b5d8b08cab",
-        "info_dict": {
-            "title": u"Intersection: The Story of Josh Grant by Kyle Cowling \u2014 Kickstarter"
-        }
+        u"url": u"https://www.kickstarter.com/projects/1404461844/intersection-the-story-of-josh-grant?ref=home_location",
+        u"file": u"1404461844.mp4",
+        u"md5": u"c81addca81327ffa66c642b5d8b08cab",
+        u"info_dict": {
+            u"title": u"Intersection: The Story of Josh Grant by Kyle Cowling",
+        },
     }
 
-
     def _real_extract(self, url):
         m = re.match(self._VALID_URL, url)
         video_id = m.group('id')
-
         webpage_src = self._download_webpage(url, video_id)
 
         video_url = self._search_regex(r'data-video="(.*?)">',
             webpage_src, u'video URL')
-
         if 'mp4' in video_url:
             ext = 'mp4'
         else:
             ext = 'flv'
-
-        video_title = self._html_search_regex(r"<title>(.*)</title>?",
-            webpage_src, u'title')
-
+        video_title = self._html_search_regex(r"<title>(.*?)</title>",
+            webpage_src, u'title').rpartition(u'\u2014 Kickstarter')[0].strip()
 
         results = [{
                     'id': video_id,
-                    'url' : video_url,
-                    'title' : video_title,
-                    'ext' : ext,
+                    'url': video_url,
+                    'title': video_title,
+                    'ext': ext,
                     }]
-
-        return results
\ No newline at end of file
+        return results
diff --git a/youtube_dl/extractor/slideshare.py b/youtube_dl/extractor/slideshare.py
new file mode 100644 (file)
index 0000000..afc3001
--- /dev/null
@@ -0,0 +1,47 @@
+import re
+import json
+
+from .common import InfoExtractor
+from ..utils import (
+    compat_urlparse,
+    ExtractorError,
+)
+
+
+class SlideshareIE(InfoExtractor):
+    _VALID_URL = r'https?://www\.slideshare\.net/[^/]+?/(?P<title>.+?)($|\?)'
+
+    _TEST = {
+        u'url': u'http://www.slideshare.net/Dataversity/keynote-presentation-managing-scale-and-complexity',
+        u'file': u'25665706.mp4',
+        u'info_dict': {
+            u'title': u'Managing Scale and Complexity',
+            u'description': u'This was a keynote presentation at the NoSQL Now! 2013 Conference & Expo (http://www.nosqlnow.com). This presentation was given by Adrian Cockcroft from Netflix',
+        },
+    }
+
+    def _real_extract(self, url):
+        mobj = re.match(self._VALID_URL, url)
+        page_title = mobj.group('title')
+        webpage = self._download_webpage(url, page_title)
+        slideshare_obj = self._search_regex(
+            r'var slideshare_object =  ({.*?}); var user_info =',
+            webpage, u'slideshare object')
+        info = json.loads(slideshare_obj)
+        if info['slideshow']['type'] != u'video':
+            raise ExtractorError(u'Webpage type is "%s": only video extraction is supported for Slideshare' % info['slideshow']['type'], expected=True)
+
+        doc = info['doc']
+        bucket = info['jsplayer']['video_bucket']
+        ext = info['jsplayer']['video_extension']
+        video_url = compat_urlparse.urljoin(bucket, doc + '-SD.' + ext)
+
+        return {
+            '_type': 'video',
+            'id': info['slideshow']['id'],
+            'title': info['slideshow']['title'],
+            'ext': ext,
+            'url': video_url,
+            'thumbnail': info['slideshow']['pin_image_url'],
+            'description': self._og_search_description(webpage),
+        }
index 77bb0a8dc3b5555e28eda831ca11c2d5eff96f79..2b9bf0cb7c5fd32f002ff26922488385401a909e 100644 (file)
@@ -8,7 +8,7 @@ from ..utils import ExtractorError
 
 
 class SohuIE(InfoExtractor):
-    _VALID_URL = r'https?://tv\.sohu\.com/\d+?/n(?P<id>\d+)\.shtml.*?'
+    _VALID_URL = r'https?://(?P<mytv>my\.)?tv\.sohu\.com/.+?/(?(mytv)|n)(?P<id>\d+)\.shtml.*?'
 
     _TEST = {
         u'url': u'http://tv.sohu.com/20130724/n382479172.shtml#super',
@@ -21,8 +21,11 @@ class SohuIE(InfoExtractor):
 
     def _real_extract(self, url):
 
-        def _fetch_data(vid_id):
-            base_data_url = u'http://hot.vrs.sohu.com/vrs_flash.action?vid='
+        def _fetch_data(vid_id, mytv=False):
+            if mytv:
+                base_data_url = 'http://my.tv.sohu.com/play/videonew.do?vid='
+            else:
+                base_data_url = u'http://hot.vrs.sohu.com/vrs_flash.action?vid='
             data_url = base_data_url + str(vid_id)
             data_json = self._download_webpage(
                 data_url, video_id,
@@ -31,15 +34,16 @@ class SohuIE(InfoExtractor):
 
         mobj = re.match(self._VALID_URL, url)
         video_id = mobj.group('id')
+        mytv = mobj.group('mytv') is not None
 
         webpage = self._download_webpage(url, video_id)
         raw_title = self._html_search_regex(r'(?s)<title>(.+?)</title>',
                                             webpage, u'video title')
         title = raw_title.partition('-')[0].strip()
 
-        vid = self._html_search_regex(r'var vid="(\d+)"', webpage,
+        vid = self._html_search_regex(r'var vid ?= ?["\'](\d+)["\']', webpage,
                                       u'video path')
-        data = _fetch_data(vid)
+        data = _fetch_data(vid, mytv)
 
         QUALITIES = ('ori', 'super', 'high', 'nor')
         vid_ids = [data['data'][q + 'Vid']
@@ -51,7 +55,7 @@ class SohuIE(InfoExtractor):
         # For now, we just pick the highest available quality
         vid_id = vid_ids[-1]
 
-        format_data = data if vid == vid_id else _fetch_data(vid_id)
+        format_data = data if vid == vid_id else _fetch_data(vid_id, mytv)
         part_count = format_data['data']['totalBlocks']
         allot = format_data['allot']
         prot = format_data['prot']
diff --git a/youtube_dl/extractor/subtitles.py b/youtube_dl/extractor/subtitles.py
new file mode 100644 (file)
index 0000000..97215f2
--- /dev/null
@@ -0,0 +1,92 @@
+from .common import InfoExtractor
+
+from ..utils import (
+    compat_str,
+    ExtractorError,
+)
+
+
+class SubtitlesInfoExtractor(InfoExtractor):
+    @property
+    def _have_to_download_any_subtitles(self):
+        return any([self._downloader.params.get('writesubtitles', False),
+                    self._downloader.params.get('writeautomaticsub'),
+                    self._downloader.params.get('allsubtitles', False)])
+
+    def _list_available_subtitles(self, video_id, webpage=None):
+        """ outputs the available subtitles for the video """
+        sub_lang_list = self._get_available_subtitles(video_id)
+        auto_captions_list = self._get_available_automatic_caption(video_id, webpage)
+        sub_lang = ",".join(list(sub_lang_list.keys()))
+        self.to_screen(u'%s: Available subtitles for video: %s' %
+                       (video_id, sub_lang))
+        auto_lang = ",".join(auto_captions_list.keys())
+        self.to_screen(u'%s: Available automatic captions for video: %s' %
+                       (video_id, auto_lang))
+
+    def extract_subtitles(self, video_id, video_webpage=None):
+        """
+        returns {sub_lang: sub} ,{} if subtitles not found or None if the
+        subtitles aren't requested.
+        """
+        if not self._have_to_download_any_subtitles:
+            return None
+        available_subs_list = {}
+        if self._downloader.params.get('writeautomaticsub', False):
+            available_subs_list.update(self._get_available_automatic_caption(video_id, video_webpage))
+        if self._downloader.params.get('writesubtitles', False) or self._downloader.params.get('allsubtitles', False):
+            available_subs_list.update(self._get_available_subtitles(video_id))
+
+        if not available_subs_list:  # error, it didn't get the available subtitles
+            return {}
+        if self._downloader.params.get('allsubtitles', False):
+            sub_lang_list = available_subs_list
+        else:
+            if self._downloader.params.get('subtitleslangs', False):
+                requested_langs = self._downloader.params.get('subtitleslangs')
+            elif 'en' in available_subs_list:
+                requested_langs = ['en']
+            else:
+                requested_langs = [list(available_subs_list.keys())[0]]
+
+            sub_lang_list = {}
+            for sub_lang in requested_langs:
+                if not sub_lang in available_subs_list:
+                    self._downloader.report_warning(u'no closed captions found in the specified language "%s"' % sub_lang)
+                    continue
+                sub_lang_list[sub_lang] = available_subs_list[sub_lang]
+
+        subtitles = {}
+        for sub_lang, url in sub_lang_list.items():
+            subtitle = self._request_subtitle_url(sub_lang, url)
+            if subtitle:
+                subtitles[sub_lang] = subtitle
+        return subtitles
+
+    def _request_subtitle_url(self, sub_lang, url):
+        """ makes the http request for the subtitle """
+        try:
+            sub = self._download_webpage(url, None, note=False)
+        except ExtractorError as err:
+            self._downloader.report_warning(u'unable to download video subtitles for %s: %s' % (sub_lang, compat_str(err)))
+            return
+        if not sub:
+            self._downloader.report_warning(u'Did not fetch video subtitles')
+            return
+        return sub
+
+    def _get_available_subtitles(self, video_id):
+        """
+        returns {sub_lang: url} or {} if not available
+        Must be redefined by the subclasses
+        """
+        pass
+
+    def _get_available_automatic_caption(self, video_id, webpage):
+        """
+        returns {sub_lang: url} or {} if not available
+        Must be redefined by the subclasses that support automatic captions,
+        otherwise it will return {}
+        """
+        self._downloader.report_warning(u'Automatic Captions not supported by this server')
+        return {}
index 423a5e973ce258e1248c62e7ed444ef943fc797e..f49665925fdeadeb9f3f64f9eafb7c7a12080fee 100644 (file)
@@ -5,8 +5,10 @@ import netrc
 import re
 import socket
 import itertools
+import xml.etree.ElementTree
 
 from .common import InfoExtractor, SearchInfoExtractor
+from .subtitles import SubtitlesInfoExtractor
 from ..utils import (
     compat_http_client,
     compat_parse_qs,
@@ -130,7 +132,8 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
             return
         self._confirm_age()
 
-class YoutubeIE(YoutubeBaseInfoExtractor):
+
+class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):
     IE_DESC = u'YouTube.com'
     _VALID_URL = r"""^
                      (
@@ -150,7 +153,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                          |youtu\.be/                                          # just youtu.be/xxxx
                          )
                      )?                                                       # all until now is optional -> you can pass the naked ID
-                     ([0-9A-Za-z_-]+)                                         # here is it! the YouTube video ID
+                     ([0-9A-Za-z_-]{11})                                      # here is it! the YouTube video ID
                      (?(1).+)?                                                # if we found the ID, everything can follow
                      $"""
     _NEXT_URL_RE = r'[\?&]next_url=([^&]+)'
@@ -397,19 +400,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
         """Report attempt to download video info webpage."""
         self.to_screen(u'%s: Downloading video info webpage' % video_id)
 
-    def report_video_subtitles_download(self, video_id):
-        """Report attempt to download video info webpage."""
-        self.to_screen(u'%s: Checking available subtitles' % video_id)
-
-    def report_video_subtitles_request(self, video_id, sub_lang, format):
-        """Report attempt to download video info webpage."""
-        self.to_screen(u'%s: Downloading video subtitles for %s.%s' % (video_id, sub_lang, format))
-
-    def report_video_subtitles_available(self, video_id, sub_lang_list):
-        """Report available subtitles."""
-        sub_lang = ",".join(list(sub_lang_list.keys()))
-        self.to_screen(u'%s: Available subtitles for video: %s' % (video_id, sub_lang))
-
     def report_information_extraction(self, video_id):
         """Report attempt to extract video information."""
         self.to_screen(u'%s: Extracting video information' % video_id)
@@ -438,13 +428,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
         elif len(s) == 86:
             return s[5:34] + s[0] + s[35:38] + s[3] + s[39:45] + s[38] + s[46:53] + s[73] + s[54:73] + s[85] + s[74:85] + s[53]
         elif len(s) == 85:
-            return s[83:34:-1] + s[0] + s[33:27:-1] + s[3] + s[26:19:-1] + s[34] + s[18:3:-1] + s[27]
+            return s[40] + s[82:43:-1] + s[22] + s[42:40:-1] + s[83] + s[39:22:-1] + s[0] + s[21:2:-1]
         elif len(s) == 84:
             return s[81:36:-1] + s[0] + s[35:2:-1]
         elif len(s) == 83:
             return s[81:64:-1] + s[82] + s[63:52:-1] + s[45] + s[51:45:-1] + s[1] + s[44:1:-1] + s[0]
         elif len(s) == 82:
-            return s[1:19] + s[0] + s[20:68] + s[19] + s[69:82]
+            return s[80:73:-1] + s[81] + s[72:54:-1] + s[2] + s[53:43:-1] + s[0] + s[42:2:-1] + s[43] + s[1] + s[54]
         elif len(s) == 81:
             return s[56] + s[79:56:-1] + s[41] + s[55:41:-1] + s[80] + s[40:34:-1] + s[0] + s[33:29:-1] + s[34] + s[28:9:-1] + s[29] + s[8:0:-1] + s[9]
         elif len(s) == 80:
@@ -464,56 +454,38 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
             # Fallback to the other algortihms
             return self._decrypt_signature(s)
 
-
     def _get_available_subtitles(self, video_id):
-        self.report_video_subtitles_download(video_id)
-        request = compat_urllib_request.Request('http://video.google.com/timedtext?hl=en&type=list&v=%s' % video_id)
         try:
-            sub_list = compat_urllib_request.urlopen(request).read().decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
+            sub_list = self._download_webpage(
+                'http://video.google.com/timedtext?hl=en&type=list&v=%s' % video_id,
+                video_id, note=False)
+        except ExtractorError as err:
             self._downloader.report_warning(u'unable to download video subtitles: %s' % compat_str(err))
             return {}
-        sub_lang_list = re.findall(r'name="([^"]*)"[^>]+lang_code="([\w\-]+)"', sub_list)
-        sub_lang_list = dict((l[1], l[0]) for l in sub_lang_list)
+        lang_list = re.findall(r'name="([^"]*)"[^>]+lang_code="([\w\-]+)"', sub_list)
+
+        sub_lang_list = {}
+        for l in lang_list:
+            lang = l[1]
+            params = compat_urllib_parse.urlencode({
+                'lang': lang,
+                'v': video_id,
+                'fmt': self._downloader.params.get('subtitlesformat'),
+            })
+            url = u'http://www.youtube.com/api/timedtext?' + params
+            sub_lang_list[lang] = url
         if not sub_lang_list:
             self._downloader.report_warning(u'video doesn\'t have subtitles')
             return {}
         return sub_lang_list
 
-    def _list_available_subtitles(self, video_id):
-        sub_lang_list = self._get_available_subtitles(video_id)
-        self.report_video_subtitles_available(video_id, sub_lang_list)
-
-    def _request_subtitle(self, sub_lang, sub_name, video_id, format):
-        """
-        Return the subtitle as a string or None if they are not found
-        """
-        self.report_video_subtitles_request(video_id, sub_lang, format)
-        params = compat_urllib_parse.urlencode({
-            'lang': sub_lang,
-            'name': sub_name,
-            'v': video_id,
-            'fmt': format,
-        })
-        url = 'http://www.youtube.com/api/timedtext?' + params
-        try:
-            sub = compat_urllib_request.urlopen(url).read().decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_warning(u'unable to download video subtitles for %s: %s' % (sub_lang, compat_str(err)))
-            return
-        if not sub:
-            self._downloader.report_warning(u'Did not fetch video subtitles')
-            return
-        return sub
-
-    def _request_automatic_caption(self, video_id, webpage):
+    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_lang = (self._downloader.params.get('subtitleslangs') or ['en'])[0]
         sub_format = self._downloader.params.get('subtitlesformat')
         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"' % sub_lang
+        err_msg = u'Couldn\'t find automatic captions for %s' % video_id
         if mobj is None:
             self._downloader.report_warning(err_msg)
             return {}
@@ -522,53 +494,38 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
             args = player_config[u'args']
             caption_url = args[u'ttsurl']
             timestamp = args[u'timestamp']
-            params = compat_urllib_parse.urlencode({
-                'lang': 'en',
-                'tlang': sub_lang,
-                'fmt': sub_format,
-                'ts': timestamp,
-                'kind': 'asr',
+            # We get the available subtitles
+            list_params = compat_urllib_parse.urlencode({
+                'type': 'list',
+                'tlangs': 1,
+                'asrs': 1,
             })
-            subtitles_url = caption_url + '&' + params
-            sub = self._download_webpage(subtitles_url, video_id, u'Downloading automatic captions')
-            return {sub_lang: sub}
+            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'))
+            original_lang_node = caption_list.find('track')
+            if original_lang_node.attrib.get('kind') != 'asr' :
+                self._downloader.report_warning(u'Video doesn\'t have automatic captions')
+                return {}
+            original_lang = original_lang_node.attrib['lang_code']
+
+            sub_lang_list = {}
+            for lang_node in caption_list.findall('target'):
+                sub_lang = lang_node.attrib['lang_code']
+                params = compat_urllib_parse.urlencode({
+                    'lang': original_lang,
+                    'tlang': sub_lang,
+                    'fmt': sub_format,
+                    'ts': timestamp,
+                    'kind': 'asr',
+                })
+                sub_lang_list[sub_lang] = caption_url + '&' + params
+            return sub_lang_list
         # An extractor error can be raise by the download process if there are
         # no automatic captions but there are subtitles
         except (KeyError, ExtractorError):
             self._downloader.report_warning(err_msg)
             return {}
-    
-    def _extract_subtitles(self, video_id):
-        """
-        Return a dictionary: {language: subtitles} or {} if the subtitles
-        couldn't be found
-        """
-        available_subs_list = self._get_available_subtitles(video_id)
-        sub_format = self._downloader.params.get('subtitlesformat')
-        if  not available_subs_list: #There was some error, it didn't get the available subtitles
-            return {}
-        if self._downloader.params.get('allsubtitles', False):
-            sub_lang_list = available_subs_list
-        else:
-            if self._downloader.params.get('subtitleslangs', False):
-                reqested_langs = self._downloader.params.get('subtitleslangs')
-            elif 'en' in available_subs_list:
-                reqested_langs = ['en']
-            else:
-                reqested_langs = [list(available_subs_list.keys())[0]]
-
-            sub_lang_list = {}
-            for sub_lang in reqested_langs:
-                if not sub_lang in available_subs_list:
-                    self._downloader.report_warning(u'no closed captions found in the specified language "%s"' % sub_lang)
-                    continue
-                sub_lang_list[sub_lang] = available_subs_list[sub_lang]
-        subtitles = {}
-        for sub_lang in sub_lang_list:
-            subtitle = self._request_subtitle(sub_lang, sub_lang_list[sub_lang].encode('utf-8'), video_id, sub_format)
-            if subtitle:
-                subtitles[sub_lang] = subtitle
-        return subtitles
 
     def _print_formats(self, formats):
         print('Available formats:')
@@ -643,7 +600,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
         manifest = self._download_webpage(manifest_url, video_id, u'Downloading formats manifest')
         formats_urls = _get_urls(manifest)
         for format_url in formats_urls:
-            itag = self._search_regex(r'itag%3D(\d+?)/', format_url, 'itag')
+            itag = self._search_regex(r'itag/(\d+?)/', format_url, 'itag')
             url_map[itag] = format_url
         return url_map
 
@@ -768,15 +725,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 video_description = u''
 
         # subtitles
-        video_subtitles = None
-
-        if self._downloader.params.get('writesubtitles', False) or self._downloader.params.get('allsubtitles', False):
-            video_subtitles = self._extract_subtitles(video_id)
-        elif self._downloader.params.get('writeautomaticsub', False):
-            video_subtitles = self._request_automatic_caption(video_id, video_webpage)
+        video_subtitles = self.extract_subtitles(video_id, video_webpage)
 
         if self._downloader.params.get('listsubtitles', False):
-            self._list_available_subtitles(video_id)
+            self._list_available_subtitles(video_id, video_webpage)
             return
 
         if 'length_seconds' not in video_info:
index 2f31cdd875e9151ad66d9a66e4658e91acf1fde7..3b2505c77f9026cfc92b50f6bc86b875fc4b70ba 100644 (file)
@@ -1,2 +1,2 @@
 
-__version__ = '2013.09.06.1'
+__version__ = '2013.09.12'