diff options
Diffstat (limited to 'youtube_dl/YoutubeDL.py')
| -rw-r--r-- | youtube_dl/YoutubeDL.py | 146 | 
1 files changed, 81 insertions, 65 deletions
| diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 296c0f992..e2332f9b8 100644 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -91,7 +91,7 @@ class YoutubeDL(object):      downloadarchive:   File name of a file where all downloads are recorded.                         Videos already present in the file are not downloaded                         again. -     +      The following parameters are not used by YoutubeDL itself, they are used by      the FileDownloader:      nopart, updatetime, buffersize, ratelimit, min_filesize, max_filesize, test, @@ -216,10 +216,10 @@ class YoutubeDL(object):          If stderr is a tty file the 'WARNING:' will be colored          '''          if sys.stderr.isatty() and os.name != 'nt': -            _msg_header=u'\033[0;33mWARNING:\033[0m' +            _msg_header = u'\033[0;33mWARNING:\033[0m'          else: -            _msg_header=u'WARNING:' -        warning_message=u'%s %s' % (_msg_header,message) +            _msg_header = u'WARNING:' +        warning_message = u'%s %s' % (_msg_header, message)          self.to_stderr(warning_message)      def report_error(self, message, tb=None): @@ -234,19 +234,6 @@ class YoutubeDL(object):          error_message = u'%s %s' % (_msg_header, message)          self.trouble(error_message, tb) -    def slow_down(self, start_time, byte_counter): -        """Sleep if the download speed is over the rate limit.""" -        rate_limit = self.params.get('ratelimit', None) -        if rate_limit is None or byte_counter == 0: -            return -        now = time.time() -        elapsed = now - start_time -        if elapsed <= 0.0: -            return -        speed = float(byte_counter) / elapsed -        if speed > rate_limit: -            time.sleep((byte_counter - rate_limit * (now - start_time)) / rate_limit) -      def report_writedescription(self, descfn):          """ Report that the description file is being written """          self.to_screen(u'[info] Writing video description to: ' + descfn) @@ -288,13 +275,15 @@ class YoutubeDL(object):              if template_dict['playlist_index'] is not None:                  template_dict['playlist_index'] = u'%05d' % template_dict['playlist_index'] -            sanitize = lambda k,v: sanitize_filename( +            sanitize = lambda k, v: sanitize_filename(                  u'NA' if v is None else compat_str(v),                  restricted=self.params.get('restrictfilenames'), -                is_id=(k==u'id')) -            template_dict = dict((k, sanitize(k, v)) for k,v in template_dict.items()) +                is_id=(k == u'id')) +            template_dict = dict((k, sanitize(k, v)) +                                 for k, v in template_dict.items()) -            filename = self.params['outtmpl'] % template_dict +            tmpl = os.path.expanduser(self.params['outtmpl']) +            filename = tmpl % template_dict              return filename          except KeyError as err:              self.report_error(u'Erroneous output template') @@ -328,14 +317,14 @@ class YoutubeDL(object):              return (u'%(title)s has already been recorded in archive'                      % info_dict)          return None -         +      def extract_info(self, url, download=True, ie_key=None, extra_info={}):          '''          Returns a list with a dictionary for each video we find.          If 'download', also downloads the videos.          extra_info is a dict containing the extra values to add to each result           ''' -         +          if ie_key:              ies = [self.get_info_extractor(ie_key)]          else: @@ -377,7 +366,7 @@ class YoutubeDL(object):                      raise          else:              self.report_error(u'no suitable InfoExtractor: %s' % url) -         +      def process_ie_result(self, ie_result, download=True, extra_info={}):          """          Take the result of the ie(may be modified) and resolve all unresolved @@ -401,7 +390,7 @@ class YoutubeDL(object):          elif result_type == 'playlist':              # We process each entry in the playlist              playlist = ie_result.get('title', None) or ie_result.get('id', None) -            self.to_screen(u'[download] Downloading playlist: %s'  % playlist) +            self.to_screen(u'[download] Downloading playlist: %s' % playlist)              playlist_results = [] @@ -419,12 +408,12 @@ class YoutubeDL(object):              self.to_screen(u"[%s] playlist '%s': Collected %d video ids (downloading %d of them)" %                  (ie_result['extractor'], playlist, n_all_entries, n_entries)) -            for i,entry in enumerate(entries,1): -                self.to_screen(u'[download] Downloading video #%s of %s' %(i, n_entries)) +            for i, entry in enumerate(entries, 1): +                self.to_screen(u'[download] Downloading video #%s of %s' % (i, n_entries))                  extra = { -                         'playlist': playlist,  -                         'playlist_index': i + playliststart, -                         } +                    'playlist': playlist, +                    'playlist_index': i + playliststart, +                }                  if not 'extractor' in entry:                      # We set the extractor, if it's an url it will be set then to                      # the new extractor, but if it's already a video we must make @@ -448,6 +437,22 @@ class YoutubeDL(object):          else:              raise Exception('Invalid result type: %s' % result_type) +    def select_format(self, format_spec, available_formats): +        if format_spec == 'best' or format_spec is None: +            return available_formats[-1] +        elif format_spec == 'worst': +            return available_formats[0] +        else: +            extensions = [u'mp4', u'flv', u'webm', u'3gp'] +            if format_spec in extensions: +                filter_f = lambda f: f['ext'] == format_spec +            else: +                filter_f = lambda f: f['format_id'] == format_spec +            matches = list(filter(filter_f, available_formats)) +            if matches: +                return matches[-1] +        return None +      def process_video_result(self, info_dict, download=True):          assert info_dict.get('_type', 'video') == 'video' @@ -458,7 +463,8 @@ class YoutubeDL(object):          # This extractors handle format selection themselves          if info_dict['extractor'] in [u'youtube', u'Youku', u'YouPorn', u'mixcloud']: -            self.process_info(info_dict) +            if download: +                self.process_info(info_dict)              return info_dict          # We now pick which formats have to be downloaded @@ -470,17 +476,14 @@ class YoutubeDL(object):          # We check that all the formats have the format and format_id fields          for (i, format) in enumerate(formats): -            if format.get('format') is None: -                if format.get('height') is not None: -                    if format.get('width') is not None: -                        format_desc = u'%sx%s' % (format['width'], format['height']) -                    else: -                        format_desc = u'%sp' % format['height'] -                else: -                    format_desc = '???' -                format['format'] = format_desc              if format.get('format_id') is None:                  format['format_id'] = compat_str(i) +            if format.get('format') is None: +                format['format'] = u'{id} - {res}{note}'.format( +                    id=format['format_id'], +                    res=self.format_resolution(format), +                    note=u' ({})'.format(format['format_note']) if format.get('format_note') is not None else '', +                )          if self.params.get('listformats', None):              self.list_formats(info_dict) @@ -502,22 +505,20 @@ class YoutubeDL(object):              formats = sorted(formats, key=_free_formats_key)          req_format = self.params.get('format', 'best') +        if req_format is None: +            req_format = 'best'          formats_to_download = [] -        if req_format == 'best' or req_format is None: -            formats_to_download = [formats[-1]] -        elif req_format == 'worst': -            formats_to_download = [formats[0]]          # The -1 is for supporting YoutubeIE -        elif req_format in ('-1', 'all'): +        if req_format in ('-1', 'all'):              formats_to_download = formats          else: -            # We can accept formats requestd in the format: 34/10/5, we pick +            # We can accept formats requestd in the format: 34/5/best, we pick              # the first that is available, starting from left              req_formats = req_format.split('/')              for rf in req_formats: -                matches = filter(lambda f:f['format_id'] == rf ,formats) -                if matches: -                    formats_to_download = [matches[0]] +                selected_format = self.select_format(rf, formats) +                if selected_format is not None: +                    formats_to_download = [selected_format]                      break          if not formats_to_download:              raise ExtractorError(u'requested format not available') @@ -608,20 +609,20 @@ class YoutubeDL(object):          if self.params.get('writeannotations', False):              try: -               annofn = filename + u'.annotations.xml' -               self.report_writeannotations(annofn) -               with io.open(encodeFilename(annofn), 'w', encoding='utf-8') as annofile: -                   annofile.write(info_dict['annotations']) +                annofn = filename + u'.annotations.xml' +                self.report_writeannotations(annofn) +                with io.open(encodeFilename(annofn), 'w', encoding='utf-8') as annofile: +                    annofile.write(info_dict['annotations'])              except (KeyError, TypeError):                  self.report_warning(u'There are no annotations to write.')              except (OSError, IOError): -                 self.report_error(u'Cannot write annotations file: ' + annofn) -                 return +                self.report_error(u'Cannot write annotations file: ' + annofn) +                return          subtitles_are_requested = any([self.params.get('writesubtitles', False),                                         self.params.get('writeautomaticsub')]) -        if  subtitles_are_requested and 'subtitles' in info_dict and info_dict['subtitles']: +        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              subtitles = info_dict['subtitles'] @@ -643,7 +644,7 @@ class YoutubeDL(object):              infofn = filename + u'.info.json'              self.report_writeinfojson(infofn)              try: -                json_info_dict = dict((k, v) for k,v in info_dict.items() if not k in ['urlhandle']) +                json_info_dict = dict((k, v) for k, v in info_dict.items() if not k in ['urlhandle'])                  write_json_file(json_info_dict, encodeFilename(infofn))              except (OSError, IOError):                  self.report_error(u'Cannot write metadata to JSON file ' + infofn) @@ -713,7 +714,7 @@ class YoutubeDL(object):          keep_video = None          for pp in self._pps:              try: -                keep_video_wish,new_info = pp.run(info) +                keep_video_wish, new_info = pp.run(info)                  if keep_video_wish is not None:                      if keep_video_wish:                          keep_video = keep_video_wish @@ -752,16 +753,31 @@ class YoutubeDL(object):          with locked_file(fn, 'a', encoding='utf-8') as archive_file:              archive_file.write(vid_id + u'\n') +    @staticmethod +    def format_resolution(format): +        if format.get('height') is not None: +            if format.get('width') is not None: +                res = u'%sx%s' % (format['width'], format['height']) +            else: +                res = u'%sp' % format['height'] +        else: +            res = '???' +        return res +      def list_formats(self, info_dict):          formats_s = []          for format in info_dict.get('formats', [info_dict]): -            formats_s.append("%s\t:\t%s\t[%s]" % (format['format_id'], -                                                format['ext'], -                                                format.get('format', '???'), -                                                ) -                            ) +            formats_s.append(u'%-15s: %-5s     %-15s[%s]' % ( +                format['format_id'], +                format['ext'], +                format.get('format_note') or '-', +                self.format_resolution(format), +                ) +            )          if len(formats_s) != 1: -            formats_s[0]  += ' (worst)' +            formats_s[0] += ' (worst)'              formats_s[-1] += ' (best)'          formats_s = "\n".join(formats_s) -        self.to_screen(u"[info] Available formats for %s:\nformat code\textension\n%s" % (info_dict['id'], formats_s))  +        self.to_screen(u'[info] Available formats for %s:\n' +            u'format code    extension   note           resolution\n%s' % ( +                info_dict['id'], formats_s)) | 
