fix(downloads): treat recovered Patreon campaign-ID errors as non-fatal
When gallery-dl logs "[patreon][error] Failed to extract campaign ID" but then recovers via its /cw/<vanity> fallback and skips every file (all already archived), _categorize_error was flagging the [error] line as fatal and returning UNKNOWN_ERROR. Add "failed to extract campaign id" to the per-item error allowlist alongside "not allowed to view" and "unable to get post" so skip detection proceeds and the run is classified NO_NEW_CONTENT. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -434,12 +434,19 @@ class GalleryDLService:
|
||||
]
|
||||
has_actual_error = any(err in combined for err in actual_error_indicators)
|
||||
|
||||
# Per-item access warnings (e.g., "Not allowed to view post" on Patreon
|
||||
# /c/user/posts URLs) are normal in multi-post downloads where some posts
|
||||
# are paywalled. These shouldn't block skip detection when the overall
|
||||
# download proceeded successfully with skips.
|
||||
# Non-fatal error lines gallery-dl logs but recovers from. These shouldn't
|
||||
# block skip detection when the overall download proceeded successfully:
|
||||
# - "not allowed to view" / "unable to get post": per-item paywall warnings
|
||||
# on Patreon /c/user/posts URLs where some posts are paywalled.
|
||||
# - "failed to extract campaign id": logged when the vanity-URL HTML path
|
||||
# fails, but gallery-dl falls back to the /cw/<vanity> redirect and
|
||||
# recovers the campaign_id on its own.
|
||||
if has_actual_error and (skip_line_count > 0 or has_skip_text):
|
||||
per_item_patterns = ["not allowed to view", "unable to get post"]
|
||||
per_item_patterns = [
|
||||
"not allowed to view",
|
||||
"unable to get post",
|
||||
"failed to extract campaign id",
|
||||
]
|
||||
error_lines = [
|
||||
line for line in combined.split('\n')
|
||||
if any(ind in line for ind in actual_error_indicators)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
"""Unit tests for GalleryDLService._categorize_error.
|
||||
|
||||
Focus: making sure errors gallery-dl logs but recovers from (e.g., Patreon's
|
||||
"Failed to extract campaign ID" that falls back to /cw/<vanity>) don't get
|
||||
misclassified as real failures when the overall run was just archive skips.
|
||||
"""
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.services.gallery_dl import ErrorType, GalleryDLService
|
||||
|
||||
|
||||
def _make_service():
|
||||
"""Build a service without touching the filesystem."""
|
||||
with patch.object(GalleryDLService, "_load_base_config", return_value={}):
|
||||
return GalleryDLService(rate_limit=1.0)
|
||||
|
||||
|
||||
def test_patreon_campaign_id_error_with_skips_is_no_new_content():
|
||||
"""gallery-dl logged the extractor error but recovered + skipped all → no_new_content."""
|
||||
service = _make_service()
|
||||
stderr = (
|
||||
"[patreon][error] Failed to extract campaign ID\n"
|
||||
"[patreon][info] campaign_id: 548223\n"
|
||||
"[patreon][api] HEAD https://c10.patreonusercontent.com/4/.../img.jpg\n"
|
||||
)
|
||||
stdout = (
|
||||
"# /data/downloads/ElArteDeVero/patreon/2024-01-01_1_title/01_img.jpg\n"
|
||||
"# /data/downloads/ElArteDeVero/patreon/2024-01-02_2_title/01_img.jpg\n"
|
||||
)
|
||||
|
||||
error_type, _message = service._categorize_error(1, stdout, stderr)
|
||||
|
||||
assert error_type == ErrorType.NO_NEW_CONTENT
|
||||
|
||||
|
||||
def test_patreon_campaign_id_error_without_skips_still_fails():
|
||||
"""Same error line but no skip activity → genuine failure, not masked."""
|
||||
service = _make_service()
|
||||
stderr = "[patreon][error] Failed to extract campaign ID\n"
|
||||
stdout = ""
|
||||
|
||||
error_type, _message = service._categorize_error(1, stdout, stderr)
|
||||
|
||||
assert error_type != ErrorType.NO_NEW_CONTENT
|
||||
|
||||
|
||||
def test_patreon_campaign_id_error_alongside_real_error_still_fails():
|
||||
"""Campaign-ID error + a genuine auth error → auth error wins, not masked."""
|
||||
service = _make_service()
|
||||
stderr = (
|
||||
"[patreon][error] Failed to extract campaign ID\n"
|
||||
'[patreon][error] "GET /api/posts HTTP/1.1" 401 None\n'
|
||||
)
|
||||
stdout = "# /data/downloads/Foo/patreon/post1/img.jpg\n"
|
||||
|
||||
error_type, _message = service._categorize_error(1, stdout, stderr)
|
||||
|
||||
assert error_type == ErrorType.AUTH_ERROR
|
||||
Reference in New Issue
Block a user