Merge remote-tracking branch 'jaimeMF/load-info'
authorPhilipp Hagemeister <phihag@phihag.de>
Mon, 9 Dec 2013 03:55:02 +0000 (04:55 +0100)
committerPhilipp Hagemeister <phihag@phihag.de>
Mon, 9 Dec 2013 03:55:02 +0000 (04:55 +0100)
1  2 
youtube_dl/YoutubeDL.py
youtube_dl/__init__.py

diff --combined youtube_dl/YoutubeDL.py
index 17b3827f26a6ad966a70d41896b7a9b782889c1b,77339dddfd591f2a02bdba3e91414142ec9c1d7c..79d5c7e5eb76087c77dbe523450c0da653868ea8
@@@ -22,6 -22,7 +22,6 @@@ if os.name == 'nt'
  from .utils import (
      compat_cookiejar,
      compat_http_client,
 -    compat_print,
      compat_str,
      compat_urllib_error,
      compat_urllib_request,
@@@ -132,8 -133,6 +132,8 @@@ class YoutubeDL(object)
      nocheckcertificate:Do not verify SSL certificates
      proxy:             URL of the proxy server to use
      socket_timeout:    Time to wait for unresponsive hosts, in seconds
 +    bidi_workaround:   Work around buggy terminals without bidirectional text
 +                       support, using fridibi
  
      The following parameters are not used by YoutubeDL itself, they are used by
      the FileDownloader:
          self._download_retcode = 0
          self._num_downloads = 0
          self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)]
 +        self._err_file = sys.stderr
          self.params = {} if params is None else params
  
 +        # Pipe messsages through fribidi
 +        if params.get('bidi_workaround', False):
 +            # fribidi does not support ungetting, so force newlines
 +            params['progress_with_newline'] = True
 +
 +            for fid in ['_screen_file', '_err_file']:
 +                class FribidiOut(object):
 +                    def __init__(self, outfile, errfile):
 +                        self.outfile = outfile
 +                        self.process = subprocess.Popen(
 +                            ['fribidi'],
 +                            stdin=subprocess.PIPE,
 +                            stdout=outfile,
 +                            stderr=errfile)
 +
 +                    def write(self, s):
 +                        res = self.process.stdin.write(s)
 +                        self.flush()
 +                        return res
 +
 +                    def flush(self):
 +                        return self.process.stdin.flush()
 +
 +                    def isatty(self):
 +                        return self.outfile.isatty()
 +
 +                try:
 +                    vout = FribidiOut(getattr(self, fid), self._err_file)
 +                    setattr(self, fid, vout)
 +                except OSError as ose:
 +                    if ose.errno == 2:
 +                        self.report_warning(u'Could not find fribidi executable, ignoring --bidi-workaround . Make sure that  fribidi  is an executable file in one of the directories in your $PATH.')
 +                        break
 +                    else:
 +                        raise
 +
          if (sys.version_info >= (3,) and sys.platform != 'win32' and
                  sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968']
                  and not params['restrictfilenames']):
          pp.set_downloader(self)
  
      def to_screen(self, message, skip_eol=False):
 +        """Print message to stdout if not in quiet mode."""
 +        return self.to_stdout(message, skip_eol, check_quiet=True)
 +
 +    def to_stdout(self, message, skip_eol=False, check_quiet=False):
          """Print message to stdout if not in quiet mode."""
          if self.params.get('logger'):
              self.params['logger'].debug(message)
 -        elif not self.params.get('quiet', False):
 +        elif not check_quiet or not self.params.get('quiet', False):
              terminator = [u'\n', u''][skip_eol]
              output = message + terminator
              write_string(output, self._screen_file)
              self.params['logger'].error(message)
          else:
              output = message + u'\n'
 -            if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr
 -                output = output.encode(preferredencoding())
 -            sys.stderr.write(output)
 +            write_string(output, self._err_file)
  
      def to_console_title(self, message):
          if not self.params.get('consoletitle', False):
          Print the message to stderr, it will be prefixed with 'WARNING:'
          If stderr is a tty file the 'WARNING:' will be colored
          '''
 -        if sys.stderr.isatty() and os.name != 'nt':
 +        if self._err_file.isatty() and os.name != 'nt':
              _msg_header = u'\033[0;33mWARNING:\033[0m'
          else:
              _msg_header = u'WARNING:'
          Do the same as trouble, but prefixes the message with 'ERROR:', colored
          in red if stderr is a tty file.
          '''
 -        if sys.stderr.isatty() and os.name != 'nt':
 +        if self._err_file.isatty() and os.name != 'nt':
              _msg_header = u'\033[0;31mERROR:\033[0m'
          else:
              _msg_header = u'ERROR:'
          for key, value in extra_info.items():
              info_dict.setdefault(key, value)
  
 -    def extract_info(self, url, download=True, ie_key=None, extra_info={}):
 +    def extract_info(self, url, download=True, ie_key=None, extra_info={},
 +                     process=True):
          '''
          Returns a list with a dictionary for each video we find.
          If 'download', also downloads the videos.
                          'webpage_url': url,
                          'extractor_key': ie.ie_key(),
                      })
 -                return self.process_ie_result(ie_result, download, extra_info)
 +                if process:
 +                    return self.process_ie_result(ie_result, download, extra_info)
 +                else:
 +                    return ie_result
              except ExtractorError as de: # An error we somewhat expected
                  self.report_error(compat_str(de), de.format_traceback())
                  break
                                       download,
                                       ie_key=ie_result.get('ie_key'),
                                       extra_info=extra_info)
 +        elif result_type == 'url_transparent':
 +            # Use the information from the embedding page
 +            info = self.extract_info(
 +                ie_result['url'], ie_key=ie_result.get('ie_key'),
 +                extra_info=extra_info, download=False, process=False)
 +
 +            def make_result(embedded_info):
 +                new_result = ie_result.copy()
 +                for f in ('_type', 'url', 'ext', 'player_url', 'formats',
 +                          'entries', 'urlhandle', 'ie_key', 'duration',
 +                          'subtitles', 'annotations', 'format',
 +                          'thumbnail', 'thumbnails'):
 +                    if f in new_result:
 +                        del new_result[f]
 +                    if f in embedded_info:
 +                        new_result[f] = embedded_info[f]
 +                return new_result
 +            new_result = make_result(info)
 +
 +            assert new_result.get('_type') != 'url_transparent'
 +            if new_result.get('_type') == 'compat_list':
 +                new_result['entries'] = [
 +                    make_result(e) for e in new_result['entries']]
 +
 +            return self.process_ie_result(
 +                new_result, download=download, extra_info=extra_info)
          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)
  
          # Forced printings
          if self.params.get('forcetitle', False):
 -            compat_print(info_dict['fulltitle'])
 +            self.to_stdout(info_dict['fulltitle'])
          if self.params.get('forceid', False):
 -            compat_print(info_dict['id'])
 +            self.to_stdout(info_dict['id'])
          if self.params.get('forceurl', False):
              # For RTMP URLs, also include the playpath
 -            compat_print(info_dict['url'] + info_dict.get('play_path', u''))
 +            self.to_stdout(info_dict['url'] + info_dict.get('play_path', u''))
          if self.params.get('forcethumbnail', False) and info_dict.get('thumbnail') is not None:
 -            compat_print(info_dict['thumbnail'])
 +            self.to_stdout(info_dict['thumbnail'])
          if self.params.get('forcedescription', False) and info_dict.get('description') is not None:
 -            compat_print(info_dict['description'])
 +            self.to_stdout(info_dict['description'])
          if self.params.get('forcefilename', False) and filename is not None:
 -            compat_print(filename)
 +            self.to_stdout(filename)
          if self.params.get('forceformat', False):
 -            compat_print(info_dict['format'])
 +            self.to_stdout(info_dict['format'])
          if self.params.get('forcejson', False):
 -            compat_print(json.dumps(info_dict))
 +            info_dict['_filename'] = filename
 +            self.to_stdout(json.dumps(info_dict))
  
          # Do nothing else if in simulate mode
          if self.params.get('simulate', False):
  
          return self._download_retcode
  
+     def download_with_info_file(self, info_filename):
+         with open(info_filename, 'r') as f:
+             # TODO: Check for errors
+             info = json.load(f)
+         try:
+             self.process_ie_result(info, download=True)
+         except DownloadError:
+             webpage_url = info.get('webpage_url')
+             if webpage_url is not None:
+                 self.report_warning(u'The info failed to download, trying with "%s"' % webpage_url)
+                 return self.download([webpage_url])
+             else:
+                 raise
+         return self._download_retcode
      def post_process(self, filename, ie_info):
          """Run all the postprocessors on the given file."""
          info = dict(ie_info)
diff --combined youtube_dl/__init__.py
index 2e3f969193eb9a777cd3e847eb550a80ad79e18a,b0d9a67630be106da71e3d34898167b43874c8aa..6e9dd68c48806f85b87a8db21e804cf070c760bc
@@@ -204,9 -204,6 +204,9 @@@ def parseOpts(overrideArguments=None)
      general.add_option(
          '--socket-timeout', dest='socket_timeout',
          type=float, default=None, help=optparse.SUPPRESS_HELP)
 +    general.add_option(
 +        '--bidi-workaround', dest='bidi_workaround', action='store_true',
 +        help=u'Work around terminals that lack bidirectional text support. Requires fribidi executable in PATH')
  
  
      selection.add_option('--playlist-start',
              help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
      filesystem.add_option('-a', '--batch-file',
              dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
+     filesystem.add_option('--load-info',
+             dest='load_info_filename', metavar='FILE',
+             help='json file containing the video information (created with the "--write-json" option')
      filesystem.add_option('-w', '--no-overwrites',
              action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
      filesystem.add_option('-c', '--continue',
@@@ -687,7 -687,6 +690,7 @@@ def _real_main(argv=None)
          'nocheckcertificate': opts.no_check_certificate,
          'proxy': opts.proxy,
          'socket_timeout': opts.socket_timeout,
 +        'bidi_workaround': opts.bidi_workaround,
      }
  
      with YoutubeDL(ydl_opts) as ydl:
              update_self(ydl.to_screen, opts.verbose)
  
          # Maybe do nothing
-         if len(all_urls) < 1:
+         if (len(all_urls) < 1) and (opts.load_info_filename is None):
              if not opts.update_self:
                  parser.error(u'you must provide at least one URL')
              else:
                  sys.exit()
  
          try:
-             retcode = ydl.download(all_urls)
+             if opts.load_info_filename is not None:
+                 retcode = ydl.download_with_info_file(opts.load_info_filename)
+             else:
+                 retcode = ydl.download(all_urls)
          except MaxDownloadsReached:
              ydl.to_screen(u'--max-download limit reached, aborting.')
              retcode = 101