1 from __future__
import unicode_literals
5 from .common
import InfoExtractor
6 from ..compat
import compat_parse_qs
18 class GoogleDriveIE(InfoExtractor
):
22 (?:docs|drive)\.google\.com/
27 video\.google\.com/get_player\?.*?docid=
29 (?P<id>[a-zA-Z0-9_-]{28,})
32 'url': 'https://drive.google.com/file/d/0ByeS4oOUV-49Zzh4R1J6R09zazQ/edit?pli=1',
33 'md5': '5c602afbbf2c1db91831f5d82f678554',
35 'id': '0ByeS4oOUV-49Zzh4R1J6R09zazQ',
37 'title': 'Big Buck Bunny.mp4',
41 # video can't be watched anonymously due to view count limit reached,
42 # but can be downloaded (see https://github.com/ytdl-org/youtube-dl/issues/14046)
43 'url': 'https://drive.google.com/file/d/0B-vUyvmDLdWDcEt4WjBqcmI2XzQ/view',
44 'only_matching': True,
46 # video id is longer than 28 characters
47 'url': 'https://drive.google.com/file/d/1ENcQ_jeCuj7y19s66_Ou9dRP4GKGsodiDQ/edit',
48 'only_matching': True,
50 'url': 'https://drive.google.com/open?id=0B2fjwgkl1A_CX083Tkowdmt6d28',
51 'only_matching': True,
53 'url': 'https://drive.google.com/uc?id=0B2fjwgkl1A_CX083Tkowdmt6d28',
54 'only_matching': True,
74 _BASE_URL_CAPTIONS
= 'https://drive.google.com/timedtext'
75 _CAPTIONS_ENTRY_TAG
= {
77 'automatic_captions': 'target',
79 _caption_formats_ext
= []
83 def _extract_url(webpage
):
85 r
'<iframe[^>]+src="https?://(?:video\.google\.com/get_player\?.*?docid=|(?:docs|drive)\.google\.com/file/d/)(?P<id>[a-zA-Z0-9_-]{28,})',
88 return 'https://drive.google.com/file/d/%s' % mobj
.group('id')
90 def _download_subtitles_xml(self
, video_id
, subtitles_id
, hl
):
91 if self
._captions
_xml
:
93 self
._captions
_xml
= self
._download
_xml
(
94 self
._BASE
_URL
_CAPTIONS
, video_id
, query
={
103 }, note
='Downloading subtitles XML',
104 errnote
='Unable to download subtitles XML', fatal
=False)
105 if self
._captions
_xml
:
106 for f
in self
._captions
_xml
.findall('format'):
107 if f
.attrib
.get('fmt_code') and not f
.attrib
.get('default'):
108 self
._caption
_formats
_ext
.append(f
.attrib
['fmt_code'])
110 def _get_captions_by_type(self
, video_id
, subtitles_id
, caption_type
,
111 origin_lang_code
=None):
112 if not subtitles_id
or not caption_type
:
115 for caption_entry
in self
._captions
_xml
.findall(
116 self
._CAPTIONS
_ENTRY
_TAG
[caption_type
]):
117 caption_lang_code
= caption_entry
.attrib
.get('lang_code')
118 if not caption_lang_code
:
120 caption_format_data
= []
121 for caption_format
in self
._caption
_formats
_ext
:
125 'fmt': caption_format
,
126 'lang': (caption_lang_code
if origin_lang_code
is None
127 else origin_lang_code
),
132 if origin_lang_code
is not None:
133 query
.update({'tlang': caption_lang_code}
)
134 caption_format_data
.append({
135 'url': update_url_query(self
._BASE
_URL
_CAPTIONS
, query
),
136 'ext': caption_format
,
138 captions
[caption_lang_code
] = caption_format_data
141 def _get_subtitles(self
, video_id
, subtitles_id
, hl
):
142 if not subtitles_id
or not hl
:
144 self
._download
_subtitles
_xml
(video_id
, subtitles_id
, hl
)
145 if not self
._captions
_xml
:
147 return self
._get
_captions
_by
_type
(video_id
, subtitles_id
, 'subtitles')
149 def _get_automatic_captions(self
, video_id
, subtitles_id
, hl
):
150 if not subtitles_id
or not hl
:
152 self
._download
_subtitles
_xml
(video_id
, subtitles_id
, hl
)
153 if not self
._captions
_xml
:
155 track
= self
._captions
_xml
.find('track')
158 origin_lang_code
= track
.attrib
.get('lang_code')
159 if not origin_lang_code
:
161 return self
._get
_captions
_by
_type
(
162 video_id
, subtitles_id
, 'automatic_captions', origin_lang_code
)
164 def _real_extract(self
, url
):
165 video_id
= self
._match
_id
(url
)
166 video_info
= compat_parse_qs(self
._download
_webpage
(
167 'https://drive.google.com/get_video_info',
168 video_id
, query
={'docid': video_id}
))
171 return try_get(video_info
, lambda x
: x
[key
][0])
173 reason
= get_value('reason')
174 title
= get_value('title')
175 if not title
and reason
:
176 raise ExtractorError(reason
, expected
=True)
179 fmt_stream_map
= (get_value('fmt_stream_map') or '').split(',')
180 fmt_list
= (get_value('fmt_list') or '').split(',')
181 if fmt_stream_map
and fmt_list
:
185 r
'^(?P<format_id>\d+)/(?P<width>\d+)[xX](?P<height>\d+)', fmt
)
187 resolutions
[mobj
.group('format_id')] = (
188 int(mobj
.group('width')), int(mobj
.group('height')))
190 for fmt_stream
in fmt_stream_map
:
191 fmt_stream_split
= fmt_stream
.split('|')
192 if len(fmt_stream_split
) < 2:
194 format_id
, format_url
= fmt_stream_split
[:2]
196 'url': lowercase_escape(format_url
),
197 'format_id': format_id
,
198 'ext': self
._FORMATS
_EXT
[format_id
],
200 resolution
= resolutions
.get(format_id
)
203 'width': resolution
[0],
204 'height': resolution
[1],
208 source_url
= update_url_query(
209 'https://drive.google.com/uc', {
211 'export': 'download',
214 def request_source_file(source_url
, kind
):
215 return self
._request
_webpage
(
216 source_url
, video_id
, note
='Requesting %s file' % kind
,
217 errnote
='Unable to request %s file' % kind
, fatal
=False)
218 urlh
= request_source_file(source_url
, 'source')
220 def add_source_format(urlh
):
222 # Use redirect URLs as download URLs in order to calculate
223 # correct cookies in _calc_cookies.
224 # Using original URLs may result in redirect loop due to
225 # google.com's cookies mistakenly used for googleusercontent.com
226 # redirect URLs (see #23919).
227 'url': urlh
.geturl(),
228 'ext': determine_ext(title
, 'mp4').lower(),
229 'format_id': 'source',
232 if urlh
.headers
.get('Content-Disposition'):
233 add_source_format(urlh
)
235 confirmation_webpage
= self
._webpage
_read
_content
(
236 urlh
, url
, video_id
, note
='Downloading confirmation page',
237 errnote
='Unable to confirm download', fatal
=False)
238 if confirmation_webpage
:
239 confirm
= self
._search
_regex
(
240 r
'confirm=([^&"\']+)', confirmation_webpage,
241 'confirmation code
', default=None)
243 confirmed_source_url = update_url_query(source_url, {
246 urlh = request_source_file(confirmed_source_url, 'confirmed source
')
247 if urlh and urlh.headers.get('Content
-Disposition
'):
248 add_source_format(urlh)
251 get_element_by_class('uc
-error
-subcaption
', confirmation_webpage)
252 or get_element_by_class('uc
-error
-caption
', confirmation_webpage)
253 or 'unable to extract confirmation code
')
255 if not formats and reason:
256 self.raise_no_formats(reason, expected=True)
258 self._sort_formats(formats)
262 ttsurl = get_value('ttsurl
')
264 # the video Id for subtitles will be the last value in the ttsurl
266 subtitles_id = ttsurl.encode('utf
-8').decode(
267 'unicode_escape
').split('=')[-1]
269 self._downloader.cookiejar.clear(domain='.google
.com
', path='/', name='NID
')
274 'thumbnail
': 'https
://drive
.google
.com
/thumbnail?
id=' + video_id,
275 'duration
': int_or_none(get_value('length_seconds
')),
277 'subtitles
': self.extract_subtitles(video_id, subtitles_id, hl),
278 'automatic_captions
': self.extract_automatic_captions(
279 video_id, subtitles_id, hl),