Render structured media details instead of raw JSON, keep search state when navigating back from detail, and surface HDHive link validation and fallback resource-page links for clearer troubleshooting. Co-authored-by: Cursor <cursoragent@cursor.com>
69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
from config import Config
|
|
from http_client import request_json
|
|
|
|
|
|
def _headers():
|
|
headers = {
|
|
"Accept": "application/json",
|
|
"X-API-Key": Config.HDHIVE_API_KEY,
|
|
}
|
|
if Config.HDHIVE_ACCESS_TOKEN:
|
|
headers["Authorization"] = f"Bearer {Config.HDHIVE_ACCESS_TOKEN}"
|
|
return headers
|
|
|
|
|
|
def search_resource(media_type, tmdb_id):
|
|
url = f"{Config.HDHIVE_BASE_URL}/api/open/resources/{media_type}/{tmdb_id}"
|
|
return request_json(
|
|
url,
|
|
headers=_headers(),
|
|
max_retry=Config.MAX_RETRY,
|
|
retry_delay_ms=Config.RETRY_DELAY_MS,
|
|
provider="hdhive",
|
|
)
|
|
|
|
|
|
def unlock_link(slug):
|
|
url = f"{Config.HDHIVE_BASE_URL}/api/open/resources/unlock"
|
|
return request_json(
|
|
url,
|
|
method="POST",
|
|
payload={"slug": slug},
|
|
headers={**_headers(), "Content-Type": "application/json"},
|
|
max_retry=Config.MAX_RETRY,
|
|
retry_delay_ms=Config.RETRY_DELAY_MS,
|
|
provider="hdhive",
|
|
)
|
|
|
|
|
|
def normalize_resource(search_data, unlock_data):
|
|
resolution = (search_data or {}).get("video_resolution")
|
|
source = (search_data or {}).get("source")
|
|
subtitle_language = (search_data or {}).get("subtitle_language")
|
|
unlock_url = (unlock_data or {}).get("full_url") or (unlock_data or {}).get("url") or ""
|
|
media_url = (search_data or {}).get("media_url") or ""
|
|
detail_url = media_url
|
|
if not detail_url and (search_data or {}).get("media_slug"):
|
|
detail_url = f"{Config.HDHIVE_BASE_URL}/movie/{(search_data or {}).get('media_slug')}"
|
|
|
|
validate_status = (search_data or {}).get("validate_status")
|
|
validate_message = (search_data or {}).get("validate_message")
|
|
|
|
return {
|
|
"resourceTitle": (search_data or {}).get("title", ""),
|
|
"quality": ", ".join(resolution) if isinstance(resolution, list) else "",
|
|
"size": (search_data or {}).get("share_size", ""),
|
|
"diskType": (search_data or {}).get("pan_type", ""),
|
|
"source": ", ".join(source) if isinstance(source, list) else "",
|
|
"subtitleLanguage": ", ".join(subtitle_language)
|
|
if isinstance(subtitle_language, list)
|
|
else "",
|
|
"slug": (search_data or {}).get("slug", ""),
|
|
"unlockUrl": unlock_url,
|
|
"detailUrl": detail_url,
|
|
"availability": "available" if unlock_url else ("index_only" if detail_url else "unknown"),
|
|
"validateStatus": validate_status,
|
|
"validateMessage": validate_message,
|
|
"raw": {"searchData": search_data, "unlockData": unlock_data},
|
|
}
|