]> jfr.im git - yt-dlp.git/blobdiff - yt_dlp/downloader/common.py
[cleanup] Misc
[yt-dlp.git] / yt_dlp / downloader / common.py
index e24d951b1624d49dbd81c1fbabeebe68a4b04f14..ab557a47aca2298a6687692fed646a7dd30ff8d6 100644 (file)
@@ -1,5 +1,6 @@
 import contextlib
 import errno
+import functools
 import os
 import random
 import re
     QuietMultilinePrinter,
 )
 from ..utils import (
+    IDENTITY,
+    NO_DEFAULT,
     NUMBER_RE,
     LockingUnsupportedError,
     Namespace,
+    RetryManager,
     classproperty,
     decodeArgument,
     encodeFilename,
-    error_to_compat_str,
-    float_or_none,
     format_bytes,
     join_nonempty,
     sanitize_open,
@@ -90,6 +92,7 @@ def _set_ydl(self, ydl):
 
         for func in (
             'deprecation_warning',
+            'deprecated_feature',
             'report_error',
             'report_file_already_downloaded',
             'report_warning',
@@ -118,7 +121,7 @@ def format_seconds(seconds):
         if time.hours > 99:
             return '--:--:--'
         if not time.hours:
-            return '%02d:%02d' % time[1:-1]
+            return '   %02d:%02d' % time[1:-1]
         return '%02d:%02d:%02d' % time[:-1]
 
     format_eta = format_seconds
@@ -215,27 +218,24 @@ def ytdl_filename(self, filename):
         return filename + '.ytdl'
 
     def wrap_file_access(action, *, fatal=False):
-        def outer(func):
-            def inner(self, *args, **kwargs):
-                file_access_retries = self.params.get('file_access_retries', 0)
-                retry = 0
-                while True:
-                    try:
-                        return func(self, *args, **kwargs)
-                    except OSError as err:
-                        retry = retry + 1
-                        if retry > file_access_retries or err.errno not in (errno.EACCES, errno.EINVAL):
-                            if not fatal:
-                                self.report_error(f'unable to {action} file: {err}')
-                                return
-                            raise
-                        self.to_screen(
-                            f'[download] Unable to {action} file due to file access error. '
-                            f'Retrying (attempt {retry} of {self.format_retries(file_access_retries)}) ...')
-                        if not self.sleep_retry('file_access', retry):
-                            time.sleep(0.01)
-            return inner
-        return outer
+        def error_callback(err, count, retries, *, fd):
+            return RetryManager.report_retry(
+                err, count, retries, info=fd.__to_screen,
+                warn=lambda e: (time.sleep(0.01), fd.to_screen(f'[download] Unable to {action} file: {e}')),
+                error=None if fatal else lambda e: fd.report_error(f'Unable to {action} file: {e}'),
+                sleep_func=fd.params.get('retry_sleep_functions', {}).get('file_access'))
+
+        def wrapper(self, func, *args, **kwargs):
+            for retry in RetryManager(self.params.get('file_access_retries'), error_callback, fd=self):
+                try:
+                    return func(self, *args, **kwargs)
+                except OSError as err:
+                    if err.errno in (errno.EACCES, errno.EINVAL):
+                        retry.error = err
+                        continue
+                    retry.error_callback(err, 1, 0)
+
+        return functools.partial(functools.partialmethod, wrapper)
 
     @wrap_file_access('open', fatal=True)
     def sanitize_open(self, filename, open_mode):
@@ -382,25 +382,20 @@ def report_resuming_byte(self, resume_len):
         """Report attempt to resume at given byte."""
         self.to_screen('[download] Resuming download at byte %s' % resume_len)
 
-    def report_retry(self, err, count, retries):
-        """Report retry in case of HTTP error 5xx"""
-        self.__to_screen(
-            '[download] Got server HTTP error: %s. Retrying (attempt %d of %s) ...'
-            % (error_to_compat_str(err), count, self.format_retries(retries)))
-        self.sleep_retry('http', count)
+    def report_retry(self, err, count, retries, frag_index=NO_DEFAULT, fatal=True):
+        """Report retry"""
+        is_frag = False if frag_index is NO_DEFAULT else 'fragment'
+        RetryManager.report_retry(
+            err, count, retries, info=self.__to_screen,
+            warn=lambda msg: self.__to_screen(f'[download] Got error: {msg}'),
+            error=IDENTITY if not fatal else lambda e: self.report_error(f'\r[download] Got error: {e}'),
+            sleep_func=self.params.get('retry_sleep_functions', {}).get(is_frag or 'http'),
+            suffix=f'fragment{"s" if frag_index is None else f" {frag_index}"}' if is_frag else None)
 
     def report_unable_to_resume(self):
         """Report it was impossible to resume download."""
         self.to_screen('[download] Unable to resume')
 
-    def sleep_retry(self, retry_type, count):
-        sleep_func = self.params.get('retry_sleep_functions', {}).get(retry_type)
-        delay = float_or_none(sleep_func(n=count - 1)) if sleep_func else None
-        if delay:
-            self.__to_screen(f'Sleeping {delay:.2f} seconds ...')
-            time.sleep(delay)
-        return sleep_func is not None
-
     @staticmethod
     def supports_manifest(manifest):
         """ Whether the downloader can download the fragments from the manifest.