aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--test/parameters.json1
-rw-r--r--test/test_dailymotion_subtitles.py62
-rw-r--r--test/test_youtube_subtitles.py108
-rw-r--r--youtube_dl/YoutubeDL.py52
-rw-r--r--youtube_dl/__init__.py7
-rw-r--r--youtube_dl/extractor/common.py3
-rw-r--r--youtube_dl/extractor/dailymotion.py46
-rw-r--r--youtube_dl/extractor/subtitles.py75
-rw-r--r--youtube_dl/extractor/youtube.py196
10 files changed, 314 insertions, 244 deletions
diff --git a/.gitignore b/.gitignore
index ca4e8f353..61cb6bc3c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/test/parameters.json b/test/parameters.json
index 96998b5c3..f042880ed 100644
--- a/test/parameters.json
+++ b/test/parameters.json
@@ -38,7 +38,6 @@
"writedescription": false,
"writeinfojson": true,
"writesubtitles": false,
- "onlysubtitles": false,
"allsubtitles": false,
"listssubtitles": false
}
diff --git a/test/test_dailymotion_subtitles.py b/test/test_dailymotion_subtitles.py
new file mode 100644
index 000000000..efc4e574f
--- /dev/null
+++ b/test/test_dailymotion_subtitles.py
@@ -0,0 +1,62 @@
+#!/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['subtitleslang'] = '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): #ojo
+ 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)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_youtube_subtitles.py b/test/test_youtube_subtitles.py
index 86e09c9b1..e40243077 100644
--- a/test/test_youtube_subtitles.py
+++ b/test/test_youtube_subtitles.py
@@ -18,78 +18,58 @@ 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'][0]
- self.assertEqual(md5(sub[2]), '4cd9278a35ba2305f47354ee13472260')
- def test_youtube_subtitles_it(self):
- DL = FakeYDL()
- DL.params['writesubtitles'] = True
- DL.params['subtitleslang'] = 'it'
- IE = YoutubeIE(DL)
- info_dict = IE.extract('QRS8MkLhQmM')
- sub = info_dict[0]['subtitles'][0]
- self.assertEqual(md5(sub[2]), '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'][0]
- self.assertEqual(md5(sub[2]), '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['subtitleslang'] = '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.assertEqual(len(subtitles), 13)
+ 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'][0]
- self.assertEqual(md5(sub[2]), '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'][0]
- self.assertEqual(md5(sub[2]), '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['subtitleslang'] = 'it'
- IE = YoutubeIE(DL)
- info_dict = IE.extract('8YoUxe5ncPo')
- sub = info_dict[0]['subtitles'][0]
- self.assertTrue(sub[2] is not None)
+ self.url = '8YoUxe5ncPo'
+ self.DL.params['writeautomaticsub'] = True
+ self.DL.params['subtitleslang'] = '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)
+
if __name__ == '__main__':
unittest.main()
diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py
index d1618da79..fa7bb1387 100644
--- a/youtube_dl/YoutubeDL.py
+++ b/youtube_dl/YoutubeDL.py
@@ -221,19 +221,16 @@ class YoutubeDL(object):
def report_writesubtitles(self, sub_filename):
""" Report that the subtitles file is being written """
- self.to_screen(u'[info] Writing video subtitles to: ' + sub_filename)
+ self.to_screen(u'[info] Writing subtitle: ' + sub_filename)
+
+ def report_existingsubtitles(self, sub_filename):
+ """ Report that the subtitles file has been already written """
+ self.to_screen(u'[info] Skipping existing subtitle: ' + sub_filename)
def report_writeinfojson(self, infofn):
""" Report that the metadata file has been written """
self.to_screen(u'[info] Video description metadata as JSON to: ' + infofn)
- def report_file_already_downloaded(self, file_name):
- """Report file has already been fully downloaded."""
- try:
- self.to_screen(u'[download] %s has already been downloaded' % file_name)
- except (UnicodeEncodeError) as err:
- self.to_screen(u'[download] The file has already been downloaded')
-
def increment_downloads(self):
"""Increment the ordinal that assigns a number to each file."""
self._num_downloads += 1
@@ -483,41 +480,32 @@ class YoutubeDL(object):
self.report_error(u'Cannot write description file ' + descfn)
return
- if (self.params.get('writesubtitles', False) or self.params.get('writeautomaticsub')) and 'subtitles' in info_dict and info_dict['subtitles']:
+ subtitles_are_requested = any([self.params.get('writesubtitles', False),
+ self.params.get('writeautomaticsub'),
+ self.params.get('allsubtitles', False)])
+
+ if subtitles_are_requested and 'subtitles' in info_dict and info_dict['subtitles']:
# subtitles download errors are already managed as troubles in relevant IE
# that way it will silently go on when used with unsupporting IE
- subtitle = info_dict['subtitles'][0]
- (sub_error, sub_lang, sub) = subtitle
+ subtitles = info_dict['subtitles']
sub_format = self.params.get('subtitlesformat')
- if sub_error:
- self.report_warning("Some error while getting the subtitles")
- else:
+
+ for sub_lang in subtitles.keys():
+ sub = subtitles[sub_lang]
+ if sub is None:
+ continue
try:
sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
+ if os.path.isfile(encodeFilename(sub_filename)):
+ self.report_existingsubtitles(sub_filename)
+ continue
self.report_writesubtitles(sub_filename)
with io.open(encodeFilename(sub_filename), 'w', encoding='utf-8') as subfile:
- subfile.write(sub)
+ subfile.write(sub)
except (OSError, IOError):
self.report_error(u'Cannot write subtitles file ' + descfn)
return
- if self.params.get('allsubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
- subtitles = info_dict['subtitles']
- sub_format = self.params.get('subtitlesformat')
- for subtitle in subtitles:
- (sub_error, sub_lang, sub) = subtitle
- if sub_error:
- self.report_warning("Some error while getting the subtitles")
- else:
- try:
- sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
- self.report_writesubtitles(sub_filename)
- with io.open(encodeFilename(sub_filename), 'w', encoding='utf-8') as subfile:
- subfile.write(sub)
- except (OSError, IOError):
- self.report_error(u'Cannot write subtitles file ' + descfn)
- return
-
if self.params.get('writeinfojson', False):
infofn = filename + u'.info.json'
self.report_writeinfojson(infofn)
diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py
index f4890f1a6..c21bf6d4a 100644
--- a/youtube_dl/__init__.py
+++ b/youtube_dl/__init__.py
@@ -190,13 +190,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)
diff --git a/youtube_dl/extractor/common.py b/youtube_dl/extractor/common.py
index 8009c2d85..52c4483c9 100644
--- a/youtube_dl/extractor/common.py
+++ b/youtube_dl/extractor/common.py
@@ -47,7 +47,8 @@ class InfoExtractor(object):
uploader_id: Nickname or id of the video uploader.
location: Physical location of the video.
player_url: SWF Player URL (used for rtmpdump).
- subtitles: The subtitle file contents.
+ subtitles: The subtitle file contents as a dictionary in the format
+ {language: subtitles}.
view_count: How many users have watched the video on the platform.
urlhandle: [internal] The urlHandle to be used to download the file,
like returned by urllib.request.urlopen
diff --git a/youtube_dl/extractor/dailymotion.py b/youtube_dl/extractor/dailymotion.py
index fa8c630d0..f54ecc569 100644
--- a/youtube_dl/extractor/dailymotion.py
+++ b/youtube_dl/extractor/dailymotion.py
@@ -1,17 +1,39 @@
import re
import json
-import itertools
+import socket
from .common import InfoExtractor
+from .subtitles import NoAutoSubtitlesIE
+
from ..utils import (
+ compat_http_client,
+ compat_urllib_error,
compat_urllib_request,
+ compat_str,
get_element_by_attribute,
get_element_by_id,
ExtractorError,
)
-class DailymotionIE(InfoExtractor):
+
+class DailyMotionSubtitlesIE(NoAutoSubtitlesIE):
+
+ def _get_available_subtitles(self, video_id):
+ request = compat_urllib_request.Request('https://api.dailymotion.com/video/%s/subtitles?fields=id,language,url' % 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:
+ 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 DailymotionIE(DailyMotionSubtitlesIE):
"""Information Extractor for Dailymotion"""
_VALID_URL = r'(?i)(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/video/([^/]+)'
@@ -21,7 +43,7 @@ class DailymotionIE(InfoExtractor):
u'file': u'x33vw9.mp4',
u'md5': u'392c4b85a60a90dc4792da41ce3144eb',
u'info_dict': {
- u"uploader": u"Alex and Van .",
+ u"uploader": u"Alex and Van .",
u"title": u"Tutoriel de Youtubeur\"DL DES VIDEO DE YOUTUBE\""
}
}
@@ -63,14 +85,27 @@ class DailymotionIE(InfoExtractor):
for key in ['stream_h264_hd1080_url','stream_h264_hd_url',
'stream_h264_hq_url','stream_h264_url',
'stream_h264_ld_url']:
- if info.get(key):#key in info and info[key]:
+ if info.get(key): # key in info and info[key]:
max_quality = key
- self.to_screen(u'Using %s' % key)
+ self.to_screen(u'%s: Using %s' % (video_id, key))
break
else:
raise ExtractorError(u'Unable to extract video URL')
video_url = info[max_quality]
+ # subtitles
+ video_subtitles = None
+ video_webpage = 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)
+
+ if self._downloader.params.get('listsubtitles', False):
+ self._list_available_subtitles(video_id)
+ return
+
return [{
'id': video_id,
'url': video_url,
@@ -78,6 +113,7 @@ class DailymotionIE(InfoExtractor):
'upload_date': video_upload_date,
'title': self._og_search_title(webpage),
'ext': video_extension,
+ 'subtitles': video_subtitles,
'thumbnail': info['thumbnail_url']
}]
diff --git a/youtube_dl/extractor/subtitles.py b/youtube_dl/extractor/subtitles.py
new file mode 100644
index 000000000..caacea5fe
--- /dev/null
+++ b/youtube_dl/extractor/subtitles.py
@@ -0,0 +1,75 @@
+import socket
+
+from .common import InfoExtractor
+
+from ..utils import (
+ compat_http_client,
+ compat_urllib_error,
+ compat_urllib_request,
+ compat_str,
+)
+
+
+class SubtitlesIE(InfoExtractor):
+
+ def _list_available_subtitles(self, video_id):
+ """ outputs the available subtitles for the video """
+ sub_lang_list = self._get_available_subtitles(video_id)
+ sub_lang = ",".join(list(sub_lang_list.keys()))
+ self.to_screen(u'%s: Available subtitles for video: %s' %
+ (video_id, sub_lang))
+
+ def _extract_subtitles(self, video_id):
+ """ returns {sub_lang: sub} or {} if subtitles not found """
+ sub_lang_list = self._get_available_subtitles(video_id)
+ if not sub_lang_list: # error, it didn't get the available subtitles
+ return {}
+
+ if self._downloader.params.get('writesubtitles', False):
+ if self._downloader.params.get('subtitleslang', False):
+ sub_lang = self._downloader.params.get('subtitleslang')
+ elif 'en' in sub_lang_list:
+ sub_lang = 'en'
+ else:
+ sub_lang = list(sub_lang_list.keys())[0]
+ if not sub_lang in sub_lang_list:
+ self._downloader.report_warning(u'no closed captions found in the specified language "%s"' % sub_lang)
+ return {}
+ sub_lang_list = {sub_lang: sub_lang_list[sub_lang]}
+
+ subtitles = {}
+ for sub_lang, url in sub_lang_list.iteritems():
+ 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 = 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 _get_available_subtitles(self, video_id):
+ """ returns {sub_lang: url} or {} if not available """
+ """ Must be redefined by the subclasses """
+ pass
+
+ def _request_automatic_caption(self, video_id, webpage):
+ """ returns {sub_lang: sub} or {} if not available """
+ """ Must be redefined by the subclasses """
+ pass
+
+
+class NoAutoSubtitlesIE(SubtitlesIE):
+ """ A subtitle class for the servers that don't support auto-captions"""
+
+ def _request_automatic_caption(self, video_id, webpage):
+ self._downloader.report_warning(u'Automatic Captions not supported by this server')
+ return {}
diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py
index c539b6891..f6ffb86c3 100644
--- a/youtube_dl/extractor/youtube.py
+++ b/youtube_dl/extractor/youtube.py
@@ -7,6 +7,7 @@ import socket
import itertools
from .common import InfoExtractor, SearchInfoExtractor
+from .subtitles import SubtitlesIE
from ..utils import (
compat_http_client,
compat_parse_qs,
@@ -23,114 +24,67 @@ from ..utils import (
orderedSet,
)
-class YoutubeBaseInfoExtractor(InfoExtractor):
- """Provide base functions for Youtube extractors"""
- _LOGIN_URL = 'https://accounts.google.com/ServiceLogin'
- _LANG_URL = r'https://www.youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1'
- _AGE_URL = 'http://www.youtube.com/verify_age?next_url=/&gl=US&hl=en'
- _NETRC_MACHINE = 'youtube'
- # If True it will raise an error if no login info is provided
- _LOGIN_REQUIRED = False
-
- def report_lang(self):
- """Report attempt to set language."""
- self.to_screen(u'Setting language')
-
- def _set_language(self):
- request = compat_urllib_request.Request(self._LANG_URL)
- try:
- self.report_lang()
- compat_urllib_request.urlopen(request).read()
- except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
- self._downloader.report_warning(u'unable to set language: %s' % compat_str(err))
- return False
- return True
-
- 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
-
- request = compat_urllib_request.Request(self._LOGIN_URL)
- try:
- login_page = compat_urllib_request.urlopen(request).read().decode('utf-8')
- except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
- self._downloader.report_warning(u'unable to fetch login page: %s' % compat_str(err))
- return False
-
- galx = None
- dsh = None
- match = re.search(re.compile(r'<input.+?name="GALX".+?value="(.+?)"', re.DOTALL), login_page)
- if match:
- galx = match.group(1)
- match = re.search(re.compile(r'<input.+?name="dsh".+?value="(.+?)"', re.DOTALL), login_page)
- if match:
- dsh = match.group(1)
-
- # Log in
- login_form_strs = {
- u'continue': u'https://www.youtube.com/signin?action_handle_signin=true&feature=sign_in_button&hl=en_US&nomobiletemp=1',
- u'Email': username,
- u'GALX': galx,
- u'Passwd': password,
- u'PersistentCookie': u'yes',
- u'_utf8': u'霱',
- u'bgresponse': u'js_disabled',
- u'checkConnection': u'',
- u'checkedDomains': u'youtube',
- u'dnConn': u'',
- u'dsh': dsh,
- u'pstMsg': u'0',
- u'rmShown': u'1',
- u'secTok': u'',
- u'signIn': u'Sign in',
- u'timeStmp': u'',
- u'service': u'youtube',
- u'uilel': u'3',
- u'hl': u'en_US',
- }
- # 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('ascii')
- request = compat_urllib_request.Request(self._LOGIN_URL, login_data)
+
+class YoutubeSubtitlesIE(SubtitlesIE):
+
+ def _get_available_subtitles(self, video_id):
+ request = compat_urllib_request.Request('http://video.google.com/timedtext?hl=en&type=list&v=%s' % video_id)
try:
- self.report_login()
- login_results = compat_urllib_request.urlopen(request).read().decode('utf-8')
- if re.search(r'(?i)<form[^>]* id="gaia_loginform"', login_results) is not None:
- self._downloader.report_warning(u'unable to log in: bad username or password')
- return False
+ sub_list = compat_urllib_request.urlopen(request).read().decode('utf-8')
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
- self._downloader.report_warning(u'unable to log in: %s' % compat_str(err))
- return False
- return True
-
- def _confirm_age(self):
- age_form = {
- 'next_url': '/',
- 'action_confirm': 'Confirm',
- }
- request = compat_urllib_request.Request(self._AGE_URL, compat_urllib_parse.urlencode(age_form))
+ self._downloader.report_warning(u'unable to download video subtitles: %s' % compat_str(err))
+ return {}
+ 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 _request_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('subtitleslang') or 'en'
+ 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
+ if mobj is None:
+ self._downloader.report_warning(err_msg)
+ return {}
+ player_config = json.loads(mobj.group(1))
try:
- self.report_age_confirmation()
- compat_urllib_request.urlopen(request).read().decode('utf-8')
- except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
- raise ExtractorError(u'Unable to confirm age: %s' % compat_str(err))
- return True
+ 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',
+ })
+ subtitles_url = caption_url + '&' + params
+ sub = self._download_webpage(subtitles_url, video_id, u'Downloading automatic captions')
+ return {sub_lang: sub}
+ # 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 _real_initialize(self):
- if self._downloader is None:
- return
- if not self._set_language():
- return
- if not self._login():
- return
- self._confirm_age()
-class YoutubeIE(YoutubeBaseInfoExtractor):
+class YoutubeIE(YoutubeSubtitlesIE):
IE_DESC = u'YouTube.com'
_VALID_URL = r"""^
(
@@ -141,7 +95,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
(?: # the various things that can precede the ID:
(?:(?:v|embed|e)/) # v/ or embed/ or e/
|(?: # or the v= param in all its forms
- (?:(?:watch|movie)(?:_popup)?(?:\.php)?)? # preceding watch(_popup|.php) or nothing (like /?v=xxxx)
+ (?:watch|movie(?:_popup)?(?:\.php)?)? # preceding watch(_popup|.php) or nothing (like /?v=xxxx)
(?:\?|\#!?) # the params delimiter ? or # or #!
(?:.*?&)? # any other preceding param (like /?s=tuff&v=xxxx)
v=
@@ -384,19 +338,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)
@@ -736,25 +677,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
# subtitles
video_subtitles = None
- if self._downloader.params.get('writesubtitles', False):
- video_subtitles = self._extract_subtitle(video_id)
- if video_subtitles:
- (sub_error, sub_lang, sub) = video_subtitles[0]
- if sub_error:
- self._downloader.report_warning(sub_error)
-
- if self._downloader.params.get('writeautomaticsub', False):
+ 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)
- (sub_error, sub_lang, sub) = video_subtitles[0]
- if sub_error:
- self._downloader.report_warning(sub_error)
-
- if self._downloader.params.get('allsubtitles', False):
- video_subtitles = self._extract_all_subtitles(video_id)
- for video_subtitle in video_subtitles:
- (sub_error, sub_lang, sub) = video_subtitle
- if sub_error:
- self._downloader.report_warning(sub_error)
if self._downloader.params.get('listsubtitles', False):
self._list_available_subtitles(video_id)