improve downloader retry behavior

- only retry download on 5xx and 429 status codes
- immediately fail on 4xx status codes
This commit is contained in:
Mike Fährmann
2017-11-10 21:35:53 +01:00
parent 5ee8ca0319
commit 79bcaa8726
6 changed files with 37 additions and 17 deletions

View File

@@ -1,5 +1,8 @@
# Changelog # Changelog
## Unreleased
- Improved retry behavior for failed HTTP downloads
## 1.0.1 - 2017-11-10 ## 1.0.1 - 2017-11-10
- Added support for: - Added support for:
- `xvideos` - https://www.xvideos.com/ ([#45](https://github.com/mikf/gallery-dl/issues/45)) - `xvideos` - https://www.xvideos.com/ ([#45](https://github.com/mikf/gallery-dl/issues/45))

View File

@@ -11,7 +11,7 @@
import os import os
import time import time
import logging import logging
from .. import config, util from .. import config, util, exception
from requests.exceptions import RequestException from requests.exceptions import RequestException
@@ -65,7 +65,7 @@ class DownloaderBase():
self.out.error(pathfmt.path, msg, tries, self.retries) self.out.error(pathfmt.path, msg, tries, self.retries)
if tries >= self.retries: if tries >= self.retries:
return False return False
time.sleep(1) time.sleep(tries)
tries += 1 tries += 1
# check for .part file # check for .part file
@@ -74,6 +74,11 @@ class DownloaderBase():
# connect to (remote) source # connect to (remote) source
try: try:
offset, size = self.connect(url, filesize) offset, size = self.connect(url, filesize)
except exception.DownloadError as exc:
self.out.error(pathfmt.path, exc, 0, 0)
return False
except exception.DownloadComplete:
break
except Exception as exc: except Exception as exc:
msg = exc msg = exc
continue continue
@@ -83,8 +88,6 @@ class DownloaderBase():
mode = "wb" mode = "wb"
if filesize: if filesize:
self.log.info("Unable to resume partial download") self.log.info("Unable to resume partial download")
elif offset == -1:
break # early finish
else: else:
mode = "ab" mode = "ab"
self.log.info("Resuming download at byte %d", offset) self.log.info("Resuming download at byte %d", offset)
@@ -124,8 +127,7 @@ class DownloaderBase():
Returns a 2-tuple containing the actual offset and expected filesize. Returns a 2-tuple containing the actual offset and expected filesize.
If the returned offset-value is greater than zero, all received data If the returned offset-value is greater than zero, all received data
will be appended to the existing .part file. If it is '-1', the will be appended to the existing .part file.
download will finish early and be considered successfull.
Return '0' as second tuple-field to indicate an unknown filesize. Return '0' as second tuple-field to indicate an unknown filesize.
""" """

View File

@@ -10,7 +10,7 @@
import mimetypes import mimetypes
from .common import DownloaderBase from .common import DownloaderBase
from .. import util from .. import util, exception
class Downloader(DownloaderBase): class Downloader(DownloaderBase):
@@ -33,14 +33,17 @@ class Downloader(DownloaderBase):
timeout=self.timeout, verify=self.verify) timeout=self.timeout, verify=self.verify)
code = self.response.status_code code = self.response.status_code
if code == 200: if code == 200: # OK
offset = 0 offset = 0
size = self.response.headers.get("Content-Length") size = self.response.headers.get("Content-Length")
elif code == 206: elif code == 206: # Partial Content
size = self.response.headers["Content-Range"].rpartition("/")[2] size = self.response.headers["Content-Range"].rpartition("/")[2]
elif code == 416: elif code == 416: # Requested Range Not Satisfiable
# file is already complete raise exception.DownloadComplete()
return -1, 0 elif 400 <= code < 500 and code != 429: # Client Error
raise exception.DownloadError(
"{} Client Error: {} for url: {}".format(
code, self.response.reason, url))
else: else:
self.response.raise_for_status() self.response.raise_for_status()

View File

@@ -17,6 +17,8 @@ Exception
| +-- AuthorizationError | +-- AuthorizationError
| +-- NotFoundError | +-- NotFoundError
| +-- HttpError | +-- HttpError
+-- DownloadError
+-- DownloadComplete
+-- NoExtractorError +-- NoExtractorError
+-- FormatError +-- FormatError
+-- FilterError +-- FilterError
@@ -48,6 +50,14 @@ class HttpError(ExtractionError):
"""HTTP request during extraction failed""" """HTTP request during extraction failed"""
class DownloadError(GalleryDLException):
"""Error during file download"""
class DownloadComplete(GalleryDLException):
"""Output file of attempted download is already complete"""
class NoExtractorError(GalleryDLException): class NoExtractorError(GalleryDLException):
"""No extractor can handle the given URL""" """No extractor can handle the given URL"""

View File

@@ -89,9 +89,10 @@ class TerminalOutput(NullOutput):
if tries <= 1 and path: if tries <= 1 and path:
print("\r", end="") print("\r", end="")
safeprint(self.shorten(CHAR_ERROR + path)) safeprint(self.shorten(CHAR_ERROR + path))
if max_tries > 1:
error = "{} ({}/{})".format(error, tries, max_tries)
print("\r[Error] ", end="") print("\r[Error] ", end="")
safeprint(error, end="") safeprint(error)
print(" (", tries, "/", max_tries, ")", sep="")
def shorten(self, txt): def shorten(self, txt):
"""Reduce the length of 'txt' to the width of the terminal""" """Reduce the length of 'txt' to the width of the terminal"""
@@ -119,8 +120,9 @@ class ColorOutput(TerminalOutput):
def error(self, path, error, tries, max_tries): def error(self, path, error, tries, max_tries):
if tries <= 1 and path: if tries <= 1 and path:
print("\r\033[1;31m", self.shorten(path), sep="") print("\r\033[1;31m", self.shorten(path), sep="")
print("\r\033[0;31m[Error]\033[0m ", error, if max_tries > 1:
" (", tries, "/", max_tries, ")", sep="") error = "{} ({}/{})".format(error, tries, max_tries)
print("\r\033[0;31m[Error]\033[0m", error)
if os.name == "nt": if os.name == "nt":

View File

@@ -6,4 +6,4 @@
# it under the terms of the GNU General Public License version 2 as # it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation. # published by the Free Software Foundation.
__version__ = "1.0.1" __version__ = "1.0.2-dev"