Files
media_crawler/backend/http_client.py
renjue 82581d2949 Implement full media crawler workflow with Flask backend and Vue frontend.
Add TMDB search and media detail pages, HDHive resource ingestion flow, unified error handling, Docker single-container runtime, and project docs/config updates for local deployment.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-09 16:16:18 +08:00

97 lines
3.4 KiB
Python

import time
import requests
from config import Config
from error_handling import AppServiceError, classify_http_error
RETRYABLE_STATUS = {408, 425, 429, 500, 502, 503, 504}
if Config.TLS_INSECURE_SKIP_VERIFY:
requests.packages.urllib3.disable_warnings() # type: ignore[attr-defined]
def request_json(
url,
method="GET",
headers=None,
payload=None,
max_retry=3,
retry_delay_ms=500,
provider="external",
):
attempt = 0
headers = headers or {}
while attempt <= max_retry:
start = time.perf_counter()
try:
response = requests.request(
method=method,
url=url,
headers=headers,
json=payload,
timeout=20,
verify=not Config.TLS_INSECURE_SKIP_VERIFY,
)
cost_ms = round((time.perf_counter() - start) * 1000)
data = None
if response.text:
try:
data = response.json()
except ValueError:
data = response.text
if not response.ok:
retryable = response.status_code in RETRYABLE_STATUS
code = str((data or {}).get("code", response.status_code)) if isinstance(data, dict) else str(response.status_code)
message = (data or {}).get("message", f"HTTP {response.status_code}") if isinstance(data, dict) else f"HTTP {response.status_code}"
category = classify_http_error(response.status_code, code, retryable=retryable)
raise AppServiceError(
message,
category=category,
code=code,
status=response.status_code,
retryable=retryable,
provider=provider,
detail={"data": data, "costMs": cost_ms, "url": url},
)
return {"data": data, "status": response.status_code, "cost_ms": cost_ms}
except requests.Timeout as error:
retryable = True
if not retryable or attempt == max_retry:
raise AppServiceError(
"request timeout",
category="timeout",
code="REQUEST_TIMEOUT",
status=None,
retryable=True,
provider=provider,
detail={"url": url, "reason": str(error)},
) from error
delay = (2**attempt) * retry_delay_ms / 1000.0
time.sleep(delay)
attempt += 1
except requests.RequestException as error:
retryable = True
if attempt == max_retry:
raise AppServiceError(
"network request failed",
category="network",
code="NETWORK_ERROR",
status=None,
retryable=True,
provider=provider,
detail={"url": url, "reason": str(error)},
) from error
delay = (2**attempt) * retry_delay_ms / 1000.0
time.sleep(delay)
attempt += 1
except AppServiceError as error:
retryable = error.retryable
if not retryable or attempt == max_retry:
raise
delay = (2**attempt) * retry_delay_ms / 1000.0
time.sleep(delay)
attempt += 1