1 from __future__ 
import unicode_literals
 
  13 import xml
.etree
.ElementTree
 
  15 from ..compat 
import ( 
  19     compat_urllib_parse_urlparse
, 
  34 _NO_DEFAULT 
= object() 
  37 class InfoExtractor(object): 
  38     """Information Extractor class. 
  40     Information extractors are the classes that, given a URL, extract 
  41     information about the video (or videos) the URL refers to. This 
  42     information includes the real video URL, the video title, author and 
  43     others. The information is stored in a dictionary which is then 
  44     passed to the YoutubeDL. The YoutubeDL processes this 
  45     information possibly downloading the video to the file system, among 
  46     other possible outcomes. 
  48     The type field determines the the type of the result. 
  49     By far the most common value (and the default if _type is missing) is 
  50     "video", which indicates a single video. 
  52     For a video, the dictionaries must include the following fields: 
  55     title:          Video title, unescaped. 
  57     Additionally, it must contain either a formats entry or a url one: 
  59     formats:        A list of dictionaries for each format available, ordered 
  60                     from worst to best quality. 
  63                     * url        Mandatory. The URL of the video file 
  64                     * ext        Will be calculated from url if missing 
  65                     * format     A human-readable description of the format 
  66                                  ("mp4 container with h264/opus"). 
  67                                  Calculated from the format_id, width, height. 
  68                                  and format_note fields if missing. 
  69                     * format_id  A short description of the format 
  70                                  ("mp4_h264_opus" or "19"). 
  71                                 Technically optional, but strongly recommended. 
  72                     * format_note Additional info about the format 
  73                                  ("3D" or "DASH video") 
  74                     * width      Width of the video, if known 
  75                     * height     Height of the video, if known 
  76                     * resolution Textual description of width and height 
  77                     * tbr        Average bitrate of audio and video in KBit/s 
  78                     * abr        Average audio bitrate in KBit/s 
  79                     * acodec     Name of the audio codec in use 
  80                     * asr        Audio sampling rate in Hertz 
  81                     * vbr        Average video bitrate in KBit/s 
  83                     * vcodec     Name of the video codec in use 
  84                     * container  Name of the container format 
  85                     * filesize   The number of bytes, if known in advance 
  86                     * filesize_approx  An estimate for the number of bytes 
  87                     * player_url SWF Player URL (used for rtmpdump). 
  88                     * protocol   The protocol that will be used for the actual 
  90                                  "http", "https", "rtsp", "rtmp", "m3u8" or so. 
  91                     * preference Order number of this format. If this field is 
  92                                  present and not None, the formats get sorted 
  93                                  by this field, regardless of all other values. 
  94                                  -1 for default (order by other properties), 
  95                                  -2 or smaller for less than default. 
  96                                  < -1000 to hide the format (if there is 
  97                                     another one which is strictly better) 
  98                     * language_preference  Is this in the correct requested 
 100                                  10 if it's what the URL is about, 
 101                                  -1 for default (don't know), 
 102                                  -10 otherwise, other values reserved for now. 
 103                     * quality    Order number of the video quality of this 
 104                                  format, irrespective of the file format. 
 105                                  -1 for default (order by other properties), 
 106                                  -2 or smaller for less than default. 
 107                     * source_preference  Order number for this video source 
 108                                   (quality takes higher priority) 
 109                                  -1 for default (order by other properties), 
 110                                  -2 or smaller for less than default. 
 111                     * http_referer  HTTP Referer header value to set. 
 112                     * http_method  HTTP method to use for the download. 
 113                     * http_headers  A dictionary of additional HTTP headers 
 114                                  to add to the request. 
 115                     * http_post_data  Additional data to send with a POST 
 117                     * stretched_ratio  If given and not 1, indicates that the 
 118                                        video's pixels are not square. 
 119                                        width : height ratio as float. 
 120     url:            Final video URL. 
 121     ext:            Video filename extension. 
 122     format:         The video format, defaults to ext (used for --get-format) 
 123     player_url:     SWF Player URL (used for rtmpdump). 
 125     The following fields are optional: 
 127     alt_title:      A secondary title of the video. 
 128     display_id      An alternative identifier for the video, not necessarily 
 129                     unique, but available before title. Typically, id is 
 130                     something like "4234987", title "Dancing naked mole rats", 
 131                     and display_id "dancing-naked-mole-rats" 
 132     thumbnails:     A list of dictionaries, with the following entries: 
 134                         * "width" (optional, int) 
 135                         * "height" (optional, int) 
 136                         * "resolution" (optional, string "{width}x{height"}, 
 138     thumbnail:      Full URL to a video thumbnail image. 
 139     description:    Full video description. 
 140     uploader:       Full name of the video uploader. 
 141     timestamp:      UNIX timestamp of the moment the video became available. 
 142     upload_date:    Video upload date (YYYYMMDD). 
 143                     If not explicitly set, calculated from timestamp. 
 144     uploader_id:    Nickname or id of the video uploader. 
 145     location:       Physical location where the video was filmed. 
 146     subtitles:      The subtitle file contents as a dictionary in the format 
 147                     {language: subtitles}. 
 148     duration:       Length of the video in seconds, as an integer. 
 149     view_count:     How many users have watched the video on the platform. 
 150     like_count:     Number of positive ratings of the video 
 151     dislike_count:  Number of negative ratings of the video 
 152     comment_count:  Number of comments on the video 
 153     comments:       A list of comments, each with one or more of the following 
 154                     properties (all but one of text or html optional): 
 155                         * "author" - human-readable name of the comment author 
 156                         * "author_id" - user ID of the comment author 
 158                         * "html" - Comment as HTML 
 159                         * "text" - Plain text of the comment 
 160                         * "timestamp" - UNIX timestamp of comment 
 161                         * "parent" - ID of the comment this one is replying to. 
 162                                      Set to "root" to indicate that this is a 
 163                                      comment to the original video. 
 164     age_limit:      Age restriction for the video, as an integer (years) 
 165     webpage_url:    The url to the video webpage, if given to youtube-dl it 
 166                     should allow to get the same result again. (It will be set 
 167                     by YoutubeDL if it's missing) 
 168     categories:     A list of categories that the video falls in, for example 
 170     is_live:        True, False, or None (=unknown). Whether this video is a 
 171                     live stream that goes on instead of a fixed-length video. 
 173     Unless mentioned otherwise, the fields should be Unicode strings. 
 175     Unless mentioned otherwise, None is equivalent to absence of information. 
 178     _type "playlist" indicates multiple videos. 
 179     There must be a key "entries", which is a list, an iterable, or a PagedList 
 180     object, each element of which is a valid dictionary by this specification. 
 182     Additionally, playlists can have "title" and "id" attributes with the same 
 183     semantics as videos (see above). 
 186     _type "multi_video" indicates that there are multiple videos that 
 187     form a single show, for examples multiple acts of an opera or TV episode. 
 188     It must have an entries key like a playlist and contain all the keys 
 189     required for a video at the same time. 
 192     _type "url" indicates that the video must be extracted from another 
 193     location, possibly by a different extractor. Its only required key is: 
 194     "url" - the next URL to extract. 
 195     The key "ie_key" can be set to the class name (minus the trailing "IE", 
 196     e.g. "Youtube") if the extractor class is known in advance. 
 197     Additionally, the dictionary may have any properties of the resolved entity 
 198     known in advance, for example "title" if the title of the referred video is 
 202     _type "url_transparent" entities have the same specification as "url", but 
 203     indicate that the given additional information is more precise than the one 
 204     associated with the resolved URL. 
 205     This is useful when a site employs a video service that hosts the video and 
 206     its technical metadata, but that video service does not embed a useful 
 207     title, description etc. 
 210     Subclasses of this one should re-define the _real_initialize() and 
 211     _real_extract() methods and define a _VALID_URL regexp. 
 212     Probably, they should also be added to the list of extractors. 
 214     Finally, the _WORKING attribute should be set to False for broken IEs 
 215     in order to warn the users and skip the tests. 
 222     def __init__(self
, downloader
=None): 
 223         """Constructor. Receives an optional downloader.""" 
 225         self
.set_downloader(downloader
) 
 228     def suitable(cls
, url
): 
 229         """Receives a URL and returns True if suitable for this IE.""" 
 231         # This does not use has/getattr intentionally - we want to know whether 
 232         # we have cached the regexp for *this* class, whereas getattr would also 
 233         # match the superclass 
 234         if '_VALID_URL_RE' not in cls
.__dict
__: 
 235             cls
._VALID
_URL
_RE 
= re
.compile(cls
._VALID
_URL
) 
 236         return cls
._VALID
_URL
_RE
.match(url
) is not None 
 239     def _match_id(cls
, url
): 
 240         if '_VALID_URL_RE' not in cls
.__dict
__: 
 241             cls
._VALID
_URL
_RE 
= re
.compile(cls
._VALID
_URL
) 
 242         m 
= cls
._VALID
_URL
_RE
.match(url
) 
 248         """Getter method for _WORKING.""" 
 251     def initialize(self
): 
 252         """Initializes an instance (authentication, etc).""" 
 254             self
._real
_initialize
() 
 257     def extract(self
, url
): 
 258         """Extracts URL information and returns it in list of dicts.""" 
 260         return self
._real
_extract
(url
) 
 262     def set_downloader(self
, downloader
): 
 263         """Sets the downloader for this IE.""" 
 264         self
._downloader 
= downloader
 
 266     def _real_initialize(self
): 
 267         """Real initialization process. Redefine in subclasses.""" 
 270     def _real_extract(self
, url
): 
 271         """Real extraction process. Redefine in subclasses.""" 
 276         """A string for getting the InfoExtractor with get_info_extractor""" 
 277         return cls
.__name
__[:-2] 
 281         return type(self
).__name
__[:-2] 
 283     def _request_webpage(self
, url_or_request
, video_id
, note
=None, errnote
=None, fatal
=True): 
 284         """ Returns the response handle """ 
 286             self
.report_download_webpage(video_id
) 
 287         elif note 
is not False: 
 289                 self
.to_screen('%s' % (note
,)) 
 291                 self
.to_screen('%s: %s' % (video_id
, note
)) 
 293             return self
._downloader
.urlopen(url_or_request
) 
 294         except (compat_urllib_error
.URLError
, compat_http_client
.HTTPException
, socket
.error
) as err
: 
 298                 errnote 
= 'Unable to download webpage' 
 299             errmsg 
= '%s: %s' % (errnote
, compat_str(err
)) 
 301                 raise ExtractorError(errmsg
, sys
.exc_info()[2], cause
=err
) 
 303                 self
._downloader
.report_warning(errmsg
) 
 306     def _download_webpage_handle(self
, url_or_request
, video_id
, note
=None, errnote
=None, fatal
=True): 
 307         """ Returns a tuple (page content as string, URL handle) """ 
 308         # Strip hashes from the URL (#1038) 
 309         if isinstance(url_or_request
, (compat_str
, str)): 
 310             url_or_request 
= url_or_request
.partition('#')[0] 
 312         urlh 
= self
._request
_webpage
(url_or_request
, video_id
, note
, errnote
, fatal
) 
 316         content 
= self
._webpage
_read
_content
(urlh
, url_or_request
, video_id
, note
, errnote
, fatal
) 
 317         return (content
, urlh
) 
 319     def _webpage_read_content(self
, urlh
, url_or_request
, video_id
, note
=None, errnote
=None, fatal
=True, prefix
=None): 
 320         content_type 
= urlh
.headers
.get('Content-Type', '') 
 321         webpage_bytes 
= urlh
.read() 
 322         if prefix 
is not None: 
 323             webpage_bytes 
= prefix 
+ webpage_bytes
 
 324         m 
= re
.match(r
'[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+\s*;\s*charset=(.+)', content_type
) 
 326             encoding 
= m
.group(1) 
 328             m 
= re
.search(br
'<meta[^>]+charset=[\'"]?([^\'")]+)[ /\'">]', 
 329                           webpage_bytes[:1024]) 
 331                 encoding = m.group(1).decode('ascii') 
 332             elif webpage_bytes.startswith(b'\xff\xfe'): 
 336         if self._downloader.params.get('dump_intermediate_pages', False): 
 338                 url = url_or_request.get_full_url() 
 339             except AttributeError: 
 341             self.to_screen('Dumping request to ' + url) 
 342             dump = base64.b64encode(webpage_bytes).decode('ascii') 
 343             self._downloader.to_screen(dump) 
 344         if self._downloader.params.get('write_pages', False): 
 346                 url = url_or_request.get_full_url() 
 347             except AttributeError: 
 349             basen = '%s_%s' % (video_id, url) 
 351                 h = '___' + hashlib.md5(basen.encode('utf-8')).hexdigest() 
 352                 basen = basen[:240 - len(h)] + h 
 353             raw_filename = basen + '.dump' 
 354             filename = sanitize_filename(raw_filename, restricted=True) 
 355             self.to_screen('Saving request to ' + filename) 
 356             # Working around MAX_PATH limitation on Windows (see 
 357             # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx) 
 359                 absfilepath = os.path.abspath(filename) 
 360                 if len(absfilepath) > 259: 
 361                     filename = '\\\\?\\' + absfilepath 
 362             with open(filename, 'wb') as outf: 
 363                 outf.write(webpage_bytes) 
 366             content = webpage_bytes.decode(encoding, 'replace') 
 368             content = webpage_bytes.decode('utf-8', 'replace') 
 370         if ('<title>Access to this site is blocked</title>' in content and 
 371                 'Websense' in content[:512]): 
 372             msg = 'Access to this webpage has been blocked by Websense filtering software in your network.' 
 373             blocked_iframe = self._html_search_regex( 
 374                 r'<iframe src="([^
"]+)"', content, 
 375                 'Websense information URL
', default=None) 
 377                 msg += ' Visit 
%s for more details
' % blocked_iframe 
 378             raise ExtractorError(msg, expected=True) 
 382     def _download_webpage(self, url_or_request, video_id, note=None, errnote=None, fatal=True, tries=1, timeout=5): 
 383         """ Returns the data of the page as a string """ 
 386         while success is False: 
 388                 res = self._download_webpage_handle(url_or_request, video_id, note, errnote, fatal) 
 390             except compat_http_client.IncompleteRead as e: 
 392                 if try_count >= tries: 
 394                 self._sleep(timeout, video_id) 
 401     def _download_xml(self, url_or_request, video_id, 
 402                       note='Downloading XML
', errnote='Unable to download XML
', 
 403                       transform_source=None, fatal=True): 
 404         """Return the xml as an xml.etree.ElementTree.Element""" 
 405         xml_string = self._download_webpage( 
 406             url_or_request, video_id, note, errnote, fatal=fatal) 
 407         if xml_string is False: 
 410             xml_string = transform_source(xml_string) 
 411         return xml.etree.ElementTree.fromstring(xml_string.encode('utf
-8')) 
 413     def _download_json(self, url_or_request, video_id, 
 414                        note='Downloading JSON metadata
', 
 415                        errnote='Unable to download JSON metadata
', 
 416                        transform_source=None, 
 418         json_string = self._download_webpage( 
 419             url_or_request, video_id, note, errnote, fatal=fatal) 
 420         if (not fatal) and json_string is False: 
 422         return self._parse_json( 
 423             json_string, video_id, transform_source=transform_source, fatal=fatal) 
 425     def _parse_json(self, json_string, video_id, transform_source=None, fatal=True): 
 427             json_string = transform_source(json_string) 
 429             return json.loads(json_string) 
 430         except ValueError as ve: 
 431             errmsg = '%s: Failed to parse JSON 
' % video_id 
 433                 raise ExtractorError(errmsg, cause=ve) 
 435                 self.report_warning(errmsg + str(ve)) 
 437     def report_warning(self, msg, video_id=None): 
 438         idstr = '' if video_id is None else '%s: ' % video_id 
 439         self._downloader.report_warning( 
 440             '[%s] %s%s' % (self.IE_NAME, idstr, msg)) 
 442     def to_screen(self, msg): 
 443         """Print msg to screen, prefixing it with '[ie_name
]'""" 
 444         self._downloader.to_screen('[%s] %s' % (self.IE_NAME, msg)) 
 446     def report_extraction(self, id_or_name): 
 447         """Report information extraction.""" 
 448         self.to_screen('%s: Extracting information
' % id_or_name) 
 450     def report_download_webpage(self, video_id): 
 451         """Report webpage download.""" 
 452         self.to_screen('%s: Downloading webpage
' % video_id) 
 454     def report_age_confirmation(self): 
 455         """Report attempt to confirm age.""" 
 456         self.to_screen('Confirming age
') 
 458     def report_login(self): 
 459         """Report attempt to log in.""" 
 460         self.to_screen('Logging 
in') 
 462     # Methods for following #608 
 464     def url_result(url, ie=None, video_id=None): 
 465         """Returns a url that points to a page that should be processed""" 
 466         # TODO: ie should be the class used for getting the info 
 467         video_info = {'_type
': 'url
', 
 470         if video_id is not None: 
 471             video_info['id'] = video_id 
 475     def playlist_result(entries, playlist_id=None, playlist_title=None, playlist_description=None): 
 476         """Returns a playlist""" 
 477         video_info = {'_type
': 'playlist
', 
 480             video_info['id'] = playlist_id 
 482             video_info['title
'] = playlist_title 
 483         if playlist_description: 
 484             video_info['description
'] = playlist_description 
 487     def _search_regex(self, pattern, string, name, default=_NO_DEFAULT, fatal=True, flags=0, group=None): 
 489         Perform a regex search on the given string, using a single or a list of 
 490         patterns returning the first matching group. 
 491         In case of failure return a default value or raise a WARNING or a 
 492         RegexNotFoundError, depending on fatal, specifying the field name. 
 494         if isinstance(pattern, (str, compat_str, compiled_regex_type)): 
 495             mobj = re.search(pattern, string, flags) 
 498                 mobj = re.search(p, string, flags) 
 502         if os.name != 'nt
' and sys.stderr.isatty(): 
 503             _name = '\033[0;34m
%s\033[0m
' % name 
 509                 # return the first matching group 
 510                 return next(g for g in mobj.groups() if g is not None) 
 512                 return mobj.group(group) 
 513         elif default is not _NO_DEFAULT: 
 516             raise RegexNotFoundError('Unable to extract 
%s' % _name) 
 518             self._downloader.report_warning('unable to extract 
%s; ' 
 519                                             'please report this issue on http
://yt
-dl
.org
/bug
' % _name) 
 522     def _html_search_regex(self, pattern, string, name, default=_NO_DEFAULT, fatal=True, flags=0, group=None): 
 524         Like _search_regex, but strips HTML tags and unescapes entities. 
 526         res = self._search_regex(pattern, string, name, default, fatal, flags, group) 
 528             return clean_html(res).strip() 
 532     def _get_login_info(self): 
 534         Get the the login info as (username, password) 
 535         It will look in the netrc file using the _NETRC_MACHINE value 
 536         If there's no info available
, return (None, None) 
 538         if self._downloader is None: 
 543         downloader_params = self._downloader.params 
 545         # Attempt to use provided username and password or .netrc data 
 546         if downloader_params.get('username', None) is not None: 
 547             username = downloader_params['username'] 
 548             password = downloader_params['password'] 
 549         elif downloader_params.get('usenetrc', False): 
 551                 info = netrc.netrc().authenticators(self._NETRC_MACHINE) 
 556                     raise netrc.NetrcParseError('No authenticators for %s' % self._NETRC_MACHINE) 
 557             except (IOError, netrc.NetrcParseError) as err: 
 558                 self._downloader.report_warning('parsing .netrc: %s' % compat_str(err)) 
 560         return (username, password) 
 562     def _get_tfa_info(self): 
 564         Get the two
-factor authentication info
 
 565         TODO 
- asking the user will be required 
for sms
/phone verify
 
 566         currently just uses the command line option
 
 567         If there
's no info available, return None 
 569         if self._downloader is None: 
 571         downloader_params = self._downloader.params 
 573         if downloader_params.get('twofactor
', None) is not None: 
 574             return downloader_params['twofactor
'] 
 578     # Helper functions for extracting OpenGraph info 
 580     def _og_regexes(prop): 
 581         content_re = r'content
=(?
:"([^>]+?)"|
\'([^
>]+?
)\')' 
 582         property_re = r'(?
:name|
property)=[\'"]og:%s[\'"]' % re.escape(prop) 
 583         template = r'<meta
[^
>]+?
%s[^
>]+?
%s' 
 585             template % (property_re, content_re), 
 586             template % (content_re, property_re), 
 589     def _og_search_property(self, prop, html, name=None, **kargs): 
 591             name = 'OpenGraph 
%s' % prop 
 592         escaped = self._search_regex(self._og_regexes(prop), html, name, flags=re.DOTALL, **kargs) 
 595         return unescapeHTML(escaped) 
 597     def _og_search_thumbnail(self, html, **kargs): 
 598         return self._og_search_property('image
', html, 'thumbnail url
', fatal=False, **kargs) 
 600     def _og_search_description(self, html, **kargs): 
 601         return self._og_search_property('description
', html, fatal=False, **kargs) 
 603     def _og_search_title(self, html, **kargs): 
 604         return self._og_search_property('title
', html, **kargs) 
 606     def _og_search_video_url(self, html, name='video url
', secure=True, **kargs): 
 607         regexes = self._og_regexes('video
') + self._og_regexes('video
:url
') 
 609             regexes = self._og_regexes('video
:secure_url
') + regexes 
 610         return self._html_search_regex(regexes, html, name, **kargs) 
 612     def _og_search_url(self, html, **kargs): 
 613         return self._og_search_property('url
', html, **kargs) 
 615     def _html_search_meta(self, name, html, display_name=None, fatal=False, **kwargs): 
 616         if display_name is None: 
 618         return self._html_search_regex( 
 620                     (?=[^>]+(?:itemprop|name|property)=(["\']?)%s\1) 
 621                     [^>]+?content=(["\'])(?P<content>.*?)\2''' % re.escape(name), 
 622             html, display_name, fatal=fatal, group='content
', **kwargs) 
 624     def _dc_search_uploader(self, html): 
 625         return self._html_search_meta('dc
.creator
', html, 'uploader
') 
 627     def _rta_search(self, html): 
 628         # See http://www.rtalabel.org/index.php?content=howtofaq#single 
 629         if re.search(r'(?ix
)<meta\s
+name
="rating"\s
+' 
 630                      r'     content
="RTA-5042-1996-1400-1577-RTA"', 
 635     def _media_rating_search(self, html): 
 636         # See http://www.tjg-designs.com/WP/metadata-code-examples-adding-metadata-to-your-web-pages/ 
 637         rating = self._html_search_meta('rating
', html) 
 649         return RATING_TABLE.get(rating.lower(), None) 
 651     def _twitter_search_player(self, html): 
 652         return self._html_search_meta('twitter
:player
', html, 
 653                                       'twitter card player
') 
 655     def _sort_formats(self, formats): 
 657             raise ExtractorError('No video formats found
') 
 660             # TODO remove the following workaround 
 661             from ..utils import determine_ext 
 662             if not f.get('ext
') and 'url
' in f: 
 663                 f['ext
'] = determine_ext(f['url
']) 
 665             preference = f.get('preference
') 
 666             if preference is None: 
 667                 proto = f.get('protocol
') 
 669                     proto = compat_urllib_parse_urlparse(f.get('url
', '')).scheme 
 671                 preference = 0 if proto in ['http
', 'https
'] else -0.1 
 672                 if f.get('ext
') in ['f4f
', 'f4m
']:  # Not yet supported 
 675             if f.get('vcodec
') == 'none
':  # audio only 
 676                 if self._downloader.params.get('prefer_free_formats
'): 
 677                     ORDER = ['aac
', 'mp3
', 'm4a
', 'webm
', 'ogg
', 'opus
'] 
 679                     ORDER = ['webm
', 'opus
', 'ogg
', 'mp3
', 'aac
', 'm4a
'] 
 682                     audio_ext_preference = ORDER.index(f['ext
']) 
 684                     audio_ext_preference = -1 
 686                 if self._downloader.params.get('prefer_free_formats
'): 
 687                     ORDER = ['flv
', 'mp4
', 'webm
'] 
 689                     ORDER = ['webm
', 'flv
', 'mp4
'] 
 691                     ext_preference = ORDER.index(f['ext
']) 
 694                 audio_ext_preference = 0 
 698                 f.get('language_preference
') if f.get('language_preference
') is not None else -1, 
 699                 f.get('quality
') if f.get('quality
') is not None else -1, 
 700                 f.get('height
') if f.get('height
') is not None else -1, 
 701                 f.get('width
') if f.get('width
') is not None else -1, 
 703                 f.get('tbr
') if f.get('tbr
') is not None else -1, 
 704                 f.get('vbr
') if f.get('vbr
') is not None else -1, 
 705                 f.get('abr
') if f.get('abr
') is not None else -1, 
 706                 audio_ext_preference, 
 707                 f.get('fps
') if f.get('fps
') is not None else -1, 
 708                 f.get('filesize
') if f.get('filesize
') is not None else -1, 
 709                 f.get('filesize_approx
') if f.get('filesize_approx
') is not None else -1, 
 710                 f.get('source_preference
') if f.get('source_preference
') is not None else -1, 
 713         formats.sort(key=_formats_key) 
 715     def http_scheme(self): 
 716         """ Either "http:" or "https:", depending on the user's preferences 
""" 
 719             if self._downloader.params.get('prefer_insecure', False) 
 722     def _proto_relative_url(self, url, scheme=None): 
 725         if url.startswith('//'): 
 727                 scheme = self.http_scheme() 
 732     def _sleep(self, timeout, video_id, msg_template=None): 
 733         if msg_template is None: 
 734             msg_template = '%(video_id)s: Waiting for %(timeout)s seconds' 
 735         msg = msg_template % {'video_id': video_id, 'timeout': timeout} 
 739     def _extract_f4m_formats(self, manifest_url, video_id): 
 740         manifest = self._download_xml( 
 741             manifest_url, video_id, 'Downloading f4m manifest', 
 742             'Unable to download f4m manifest') 
 745         manifest_version = '1.0' 
 746         media_nodes = manifest.findall('{http://ns.adobe.com/f4m/1.0}media') 
 748             manifest_version = '2.0' 
 749             media_nodes = manifest.findall('{http://ns.adobe.com/f4m/2.0}media') 
 750         for i, media_el in enumerate(media_nodes): 
 751             if manifest_version == '2.0': 
 752                 manifest_url = '/'.join(manifest_url.split('/')[:-1]) + '/' + media_el.attrib.get('href') 
 753             tbr = int_or_none(media_el.attrib.get('bitrate')) 
 754             format_id = 'f4m-%d' % (i if tbr is None else tbr) 
 756                 'format_id': format_id, 
 760                 'width': int_or_none(media_el.attrib.get('width')), 
 761                 'height': int_or_none(media_el.attrib.get('height')), 
 763         self._sort_formats(formats) 
 767     def _extract_m3u8_formats(self, m3u8_url, video_id, ext=None, 
 768                               entry_protocol='m3u8', preference=None): 
 771             'format_id': 'm3u8-meta', 
 776             'resolution': 'multiple', 
 777             'format_note': 'Quality selection URL', 
 780         format_url = lambda u: ( 
 782             if re.match(r'^https?://', u) 
 783             else compat_urlparse.urljoin(m3u8_url, u)) 
 785         m3u8_doc = self._download_webpage( 
 787             note='Downloading m3u8 information', 
 788             errnote='Failed to download m3u8 information') 
 791             r'(?P<key>[a-zA-Z_-]+)=(?P<val>"[^"]+"|[^",]+)(?:,|$)') 
 792         for line in m3u8_doc.splitlines(): 
 793             if line.startswith('#EXT-X-STREAM-INF:'): 
 795                 for m in kv_rex.finditer(line): 
 797                     if v.startswith('"'): 
 799                     last_info[m.group('key')] = v 
 800             elif line.startswith('#') or not line.strip(): 
 803                 if last_info is None: 
 804                     formats.append({'url': format_url(line)}) 
 806                 tbr = int_or_none(last_info.get('BANDWIDTH'), scale=1000) 
 809                     'format_id': 'm3u8-%d' % (tbr if tbr else len(formats)), 
 810                     'url': format_url(line.strip()), 
 813                     'protocol': entry_protocol, 
 814                     'preference': preference, 
 816                 codecs = last_info.get('CODECS') 
 818                     # TODO: looks like video codec is not always necessarily goes first 
 819                     va_codecs = codecs.split(',') 
 821                         f['vcodec'] = va_codecs[0].partition('.')[0] 
 822                     if len(va_codecs) > 1 and va_codecs[1]: 
 823                         f['acodec'] = va_codecs[1].partition('.')[0] 
 824                 resolution = last_info.get('RESOLUTION') 
 826                     width_str, height_str = resolution.split('x') 
 827                     f['width'] = int(width_str) 
 828                     f['height'] = int(height_str) 
 831         self._sort_formats(formats) 
 834     # TODO: improve extraction 
 835     def _extract_smil_formats(self, smil_url, video_id): 
 836         smil = self._download_xml( 
 837             smil_url, video_id, 'Downloading SMIL file', 
 838             'Unable to download SMIL file') 
 840         base = smil.find('./head/meta').get('base') 
 844         for video in smil.findall('./body/switch/video'): 
 845             src = video.get('src') 
 848             bitrate = int_or_none(video.get('system-bitrate') or video.get('systemBitrate'), 1000) 
 849             width = int_or_none(video.get('width')) 
 850             height = int_or_none(video.get('height')) 
 851             proto = video.get('proto') 
 854                     if base.startswith('rtmp'): 
 856                     elif base.startswith('http'): 
 858             ext = video.get('ext') 
 860                 formats.extend(self._extract_m3u8_formats(src, video_id, ext)) 
 861             elif proto == 'rtmp': 
 863                 streamer = video.get('streamer') or base 
 868                     'format_id': 'rtmp-%d' % (rtmp_count if bitrate is None else bitrate), 
 873         self._sort_formats(formats) 
 877     def _live_title(self, name): 
 878         """ Generate the title 
for a live video 
""" 
 879         now = datetime.datetime.now() 
 880         now_str = now.strftime("%Y-%m-%d %H:%M") 
 881         return name + ' ' + now_str 
 883     def _int(self, v, name, fatal=False, **kwargs): 
 884         res = int_or_none(v, **kwargs) 
 885         if 'get_attr' in kwargs: 
 886             print(getattr(v, kwargs['get_attr'])) 
 888             msg = 'Failed to extract %s: Could not parse value %r' % (name, v) 
 890                 raise ExtractorError(msg) 
 892                 self._downloader.report_warning(msg) 
 895     def _float(self, v, name, fatal=False, **kwargs): 
 896         res = float_or_none(v, **kwargs) 
 898             msg = 'Failed to extract %s: Could not parse value %r' % (name, v) 
 900                 raise ExtractorError(msg) 
 902                 self._downloader.report_warning(msg) 
 905     def _set_cookie(self, domain, name, value, expire_time=None): 
 906         cookie = compat_cookiejar.Cookie( 
 907             0, name, value, None, None, domain, None, 
 908             None, '/', True, False, expire_time, '', None, None, None) 
 909         self._downloader.cookiejar.set_cookie(cookie) 
 911     def get_testcases(self, include_onlymatching=False): 
 912         t = getattr(self, '_TEST', None) 
 914             assert not hasattr(self, '_TESTS'), \ 
 915                 '%s has _TEST and _TESTS' % type(self).__name__ 
 918             tests = getattr(self, '_TESTS', []) 
 920             if not include_onlymatching and t.get('only_matching', False): 
 922             t['name'] = type(self).__name__[:-len('IE')] 
 925     def is_suitable(self, age_limit): 
 926         """ Test whether the extractor 
is generally suitable 
for the given
 
 927         age 
limit (i
.e
. pornographic sites are 
not, all others usually are
) """ 
 929         any_restricted = False 
 930         for tc in self.get_testcases(include_onlymatching=False): 
 932                 tc = tc['playlist'][0] 
 933             is_restricted = age_restricted( 
 934                 tc.get('info_dict', {}).get('age_limit'), age_limit) 
 935             if not is_restricted: 
 937             any_restricted = any_restricted or is_restricted 
 938         return not any_restricted 
 941 class SearchInfoExtractor(InfoExtractor): 
 943     Base 
class for paged search queries extractors
. 
 944     They accept urls 
in the format 
_SEARCH_KEY(|all|
[0-9]):{query}
 
 945     Instances should define _SEARCH_KEY 
and _MAX_RESULTS
. 
 949     def _make_valid_url(cls): 
 950         return r'%s(?P<prefix>|[1-9][0-9]*|all):(?P<query>[\s\S]+)' % cls._SEARCH_KEY 
 953     def suitable(cls, url): 
 954         return re.match(cls._make_valid_url(), url) is not None 
 956     def _real_extract(self, query): 
 957         mobj = re.match(self._make_valid_url(), query) 
 959             raise ExtractorError('Invalid search query "%s"' % query) 
 961         prefix = mobj.group('prefix') 
 962         query = mobj.group('query') 
 964             return self._get_n_results(query, 1) 
 965         elif prefix == 'all': 
 966             return self._get_n_results(query, self._MAX_RESULTS) 
 970                 raise ExtractorError('invalid download number %s for query "%s"' % (n, query)) 
 971             elif n > self._MAX_RESULTS: 
 972                 self._downloader.report_warning('%s returns max %i results (you requested %i)' % (self._SEARCH_KEY, self._MAX_RESULTS, n)) 
 973                 n = self._MAX_RESULTS 
 974             return self._get_n_results(query, n) 
 976     def _get_n_results(self, query, n): 
 977         """Get a specified number of results 
for a query
""" 
 978         raise NotImplementedError("This method must be implemented by subclasses") 
 981     def SEARCH_KEY(self): 
 982         return self._SEARCH_KEY