tuning error recognition
This commit is contained in:
@@ -316,23 +316,52 @@ class GalleryDLService:
|
||||
return config
|
||||
|
||||
def _categorize_error(self, return_code: int, stdout: str, stderr: str) -> tuple[ErrorType, str]:
|
||||
"""Categorize the error based on output and return code."""
|
||||
"""Categorize the error based on output and return code.
|
||||
|
||||
Priority order:
|
||||
1. Check for skip messages (# prefixed lines or text patterns) → NO_NEW_CONTENT
|
||||
2. Check for actual errors (auth, rate limit, 404, network, etc.)
|
||||
3. Fall back to UNKNOWN_ERROR
|
||||
|
||||
This ordering prevents false positives where debug output matches error patterns.
|
||||
"""
|
||||
combined = f"{stdout} {stderr}".lower()
|
||||
|
||||
# Check for "no new content" FIRST for any non-zero return code
|
||||
# Gallery-dl returns 1 when all files are skipped or no new content found
|
||||
# This check must run before other error checks to avoid false positives
|
||||
skip_indicators = ["skipping", "skip", "already exists", "archive"]
|
||||
if any(indicator in combined for indicator in skip_indicators):
|
||||
# If we see skip messages and no actual error messages, it's just "no new content"
|
||||
actual_errors = ["error", "failed", "exception", "traceback"]
|
||||
if not any(err in combined for err in actual_errors):
|
||||
return ErrorType.NO_NEW_CONTENT, "No new content to download"
|
||||
# === PHASE 1: Detect skip/no-new-content scenarios ===
|
||||
# Gallery-dl indicates skipped files two ways:
|
||||
# 1. Lines starting with '#' in stdout (e.g., "# /data/downloads/Artist/file.png")
|
||||
# 2. Text patterns like "skipping", "already exists"
|
||||
|
||||
# Count lines starting with '#' (skip messages)
|
||||
skip_line_count = len([
|
||||
line for line in stdout.split('\n')
|
||||
if line.strip().startswith('#')
|
||||
])
|
||||
|
||||
# Text-based skip indicators
|
||||
skip_text_indicators = ["skipping", "already exists", "archive"]
|
||||
has_skip_text = any(ind in combined for ind in skip_text_indicators)
|
||||
|
||||
# Actual error indicators that override skip detection
|
||||
actual_error_indicators = [
|
||||
"][error]", # gallery-dl error log level: [patreon][error]
|
||||
"exception", # Python exceptions
|
||||
"traceback", # Python tracebacks
|
||||
"download failed",
|
||||
"extraction failed",
|
||||
]
|
||||
has_actual_error = any(err in combined for err in actual_error_indicators)
|
||||
|
||||
# If we have skip evidence and no real errors, it's no_new_content
|
||||
if (skip_line_count > 0 or has_skip_text) and not has_actual_error:
|
||||
return ErrorType.NO_NEW_CONTENT, "No new content to download"
|
||||
|
||||
# Empty output with low return codes often means nothing to download
|
||||
if return_code in (0, 1) and not stdout.strip():
|
||||
return ErrorType.NO_NEW_CONTENT, "No new content to download"
|
||||
|
||||
# === PHASE 2: Check for specific error types ===
|
||||
|
||||
# Auth errors - look for error context, not just HTTP codes that appear in debug logs
|
||||
# HTTP debug lines look like: '"GET /path HTTP/1.1" 401 None' - we need to detect actual errors
|
||||
auth_error_patterns = [
|
||||
@@ -370,29 +399,50 @@ class GalleryDLService:
|
||||
if any(pattern in combined for pattern in rate_limit_patterns):
|
||||
return ErrorType.RATE_LIMITED, "Rate limited by server - try increasing sleep time"
|
||||
|
||||
# 404 Not Found - check for HTTP 404 responses, not just "404" anywhere
|
||||
# 404 Not Found - check for HTTP 404 responses with error context
|
||||
# Be specific to avoid matching "not found" in debug messages like "archive entry not found"
|
||||
not_found_patterns = [
|
||||
'" 404 ', # HTTP response
|
||||
"error 404",
|
||||
"status: 404",
|
||||
"not found",
|
||||
"does not exist",
|
||||
"no longer available",
|
||||
'" 404 ', # HTTP response log line
|
||||
"error 404", # Explicit error 404
|
||||
"status: 404", # Status code indicator
|
||||
"status code: 404", # Status code indicator
|
||||
"404 not found", # Standard 404 message
|
||||
"page not found", # Common 404 page text
|
||||
"user not found", # User doesn't exist
|
||||
"creator not found",
|
||||
"artist not found",
|
||||
"content no longer available",
|
||||
"has been deleted",
|
||||
"account.*deleted",
|
||||
"profile.*not.*exist",
|
||||
]
|
||||
if any(pattern in combined for pattern in not_found_patterns):
|
||||
# Also require error-level context for generic patterns to avoid false positives
|
||||
generic_not_found = ["does not exist", "no longer available"]
|
||||
has_specific_404 = any(pattern in combined for pattern in not_found_patterns)
|
||||
has_generic_with_error = any(
|
||||
pattern in combined and "][error]" in combined
|
||||
for pattern in generic_not_found
|
||||
)
|
||||
if has_specific_404 or has_generic_with_error:
|
||||
return ErrorType.NOT_FOUND, "URL not found - artist may have changed username or deleted content"
|
||||
|
||||
# Network errors
|
||||
# Network errors - use specific patterns to avoid matching config values
|
||||
# (e.g., "timeout": 30.0 in debug output should not trigger network_error)
|
||||
network_error_patterns = [
|
||||
"timeout",
|
||||
"timed out",
|
||||
"timed out", # Actual timeout occurred
|
||||
"read timed out", # urllib3/requests timeout
|
||||
"connect timed out", # Connection timeout
|
||||
"connection timed out",
|
||||
"connection refused",
|
||||
"connection reset",
|
||||
"network unreachable",
|
||||
"name resolution",
|
||||
"dns",
|
||||
"ssl error",
|
||||
"certificate verify",
|
||||
"name resolution failed",
|
||||
"name or service not known",
|
||||
"nodename nor servname provided",
|
||||
"ssl: certificate_verify_failed",
|
||||
"ssl: wrong_version_number",
|
||||
"certificate verify failed",
|
||||
"[errno", # Socket errors like [Errno 111]
|
||||
]
|
||||
if any(pattern in combined for pattern in network_error_patterns):
|
||||
return ErrorType.NETWORK_ERROR, "Network error - check internet connection"
|
||||
@@ -418,10 +468,12 @@ class GalleryDLService:
|
||||
if "no suitable" in combined or "unsupported" in combined or "no extractor" in combined:
|
||||
return ErrorType.UNSUPPORTED_URL, "URL format not supported by gallery-dl"
|
||||
|
||||
# If we got here with return code 1 and saw some activity, it might just be no new content
|
||||
if return_code == 1:
|
||||
# If we got here with return code 1 and saw some activity but no error patterns matched,
|
||||
# it's likely just no new content (gallery-dl returns 1 when nothing to download)
|
||||
if return_code == 1 and stdout.strip() and not has_actual_error:
|
||||
return ErrorType.NO_NEW_CONTENT, "No new content to download"
|
||||
|
||||
# Final fallback - genuine unknown error
|
||||
return ErrorType.UNKNOWN_ERROR, f"Unknown error (return code: {return_code})"
|
||||
|
||||
def _count_downloaded_files(self, stdout: str) -> int:
|
||||
|
||||
Reference in New Issue
Block a user