fix: release cooldown on local media failures
This commit is contained in:
@@ -7,7 +7,7 @@ from pathlib import Path
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
from .community_cli import MIN_FETCH_INTERVAL_SECONDS, USER_AGENT, _StrictRedirectHandler, _read_state, _validate_url_before_io, check_and_reserve, fetch_html, fetch_site_key
|
||||
from .community_cli import MIN_FETCH_INTERVAL_SECONDS, USER_AGENT, _StrictRedirectHandler, _read_state, _validate_url_before_io, check_and_reserve, fetch_html, fetch_site_key, release_reservation
|
||||
from .media_assets import approve_stored_assets, audit_media_catalog, compare_quality_upgrades, extract_media_candidates, generate_media_derivatives, generate_quality_contact_sheets, media_coverage, media_quality_report, merge_manifest, publish_quality_upgrades, queue_quality_upgrades, reconcile_queued_duplicates, reclassify_manifest, review_asset, rollback_quality_upgrade, store_asset
|
||||
|
||||
|
||||
@@ -104,6 +104,11 @@ def _attempt_asset(root: Path, manifest_path: Path, manifest: dict, queued: dict
|
||||
return f"stored {url} as {relative}", None
|
||||
|
||||
|
||||
def _response_was_received(error: Exception) -> bool:
|
||||
"""Only an HTTP response or validated body is allowed to consume cooldown."""
|
||||
return isinstance(error, (urllib.error.HTTPError, ValueError))
|
||||
|
||||
|
||||
def _download_one(root: Path, state_file: Path, asset_url: str | None = None) -> str:
|
||||
manifest_path = root / "manifest.json"
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
@@ -112,9 +117,11 @@ def _download_one(root: Path, state_file: Path, asset_url: str | None = None) ->
|
||||
return "queue is empty" if asset_url is None else "asset is not queued"
|
||||
url = queued["asset_url"]
|
||||
_validate_url_before_io(url)
|
||||
check_and_reserve(fetch_site_key(url), state_file=state_file)
|
||||
reserved_at = check_and_reserve(fetch_site_key(url), state_file=state_file)
|
||||
message, error = _attempt_asset(root, manifest_path, manifest, queued)
|
||||
if error is not None:
|
||||
if not _response_was_received(error):
|
||||
release_reservation(fetch_site_key(url), state_file=state_file, reserved_at=reserved_at)
|
||||
raise error
|
||||
return message
|
||||
|
||||
@@ -142,6 +149,7 @@ def _download_batch(root: Path, state_file: Path, *, limit: int = MAX_BATCH_ASSE
|
||||
domains[site] = {"attempted": 0, "stored": 0, "failed": 0, "skipped": str(exc)}
|
||||
continue
|
||||
stored = failed = consecutive_failures = 0
|
||||
response_received = False
|
||||
messages: list[str] = []
|
||||
stopped_reason: str | None = None
|
||||
for item in items[:limit]:
|
||||
@@ -149,17 +157,24 @@ def _download_batch(root: Path, state_file: Path, *, limit: int = MAX_BATCH_ASSE
|
||||
messages.append(message)
|
||||
if error is None:
|
||||
stored += 1
|
||||
response_received = True
|
||||
consecutive_failures = 0
|
||||
continue
|
||||
failed += 1
|
||||
consecutive_failures += 1
|
||||
status = item.get("status")
|
||||
if _response_was_received(error):
|
||||
response_received = True
|
||||
if status in {"blocked", "queued"}:
|
||||
stopped_reason = f"domain stopped after {status} response"
|
||||
break
|
||||
if consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
|
||||
stopped_reason = f"domain stopped after {MAX_CONSECUTIVE_FAILURES} consecutive failures"
|
||||
break
|
||||
if not response_received and failed and stored == 0:
|
||||
# No HTTP response was observed in this reservation window. It is
|
||||
# safe to release this process's reservation for a later retry.
|
||||
release_reservation(site, state_file=state_file, reserved_at=reserved_at)
|
||||
domains[site] = {
|
||||
"attempted": stored + failed, "stored": stored, "failed": failed,
|
||||
"stopped_reason": stopped_reason, "messages": messages,
|
||||
|
||||
Reference in New Issue
Block a user