142 lines
4.5 KiB
Python
Executable File
142 lines
4.5 KiB
Python
Executable File
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import time
|
|
from typing import Any
|
|
|
|
import requests
|
|
|
|
|
|
EVENT_META_INFO_URL = "https://d.minieye.tech/ess-api/dashoard/event/meta_info"
|
|
CLIP_ID_KEYS = ("clip_id", "clip_uuid", "clip_ukey", "ukey", "id")
|
|
|
|
|
|
def fetch_event_meta(
|
|
event_id: str,
|
|
timeout: float = 60.0,
|
|
max_retries: int = 3,
|
|
retry_backoff_sec: float = 2.0,
|
|
) -> dict[str, Any]:
|
|
event_id = str(event_id).strip()
|
|
if not event_id:
|
|
raise ValueError("event_id is required")
|
|
|
|
last_error: Exception | None = None
|
|
total_attempts = max(1, int(max_retries))
|
|
for attempt in range(1, total_attempts + 1):
|
|
try:
|
|
response = requests.post(
|
|
url=EVENT_META_INFO_URL,
|
|
json={"event_id": event_id},
|
|
timeout=timeout,
|
|
)
|
|
response.raise_for_status()
|
|
payload = response.json()
|
|
if not isinstance(payload, dict):
|
|
raise ValueError(f"Unexpected event meta response type: {type(payload).__name__}")
|
|
return payload
|
|
except (requests.exceptions.Timeout, requests.exceptions.RequestException, ValueError) as exc:
|
|
last_error = exc
|
|
if attempt >= total_attempts:
|
|
break
|
|
sleep_sec = max(0.0, float(retry_backoff_sec)) * (2 ** (attempt - 1))
|
|
print(
|
|
f"[warn] event_id={event_id} request attempt {attempt}/{total_attempts} failed: "
|
|
f"{type(exc).__name__}: {exc}. retry in {sleep_sec:.1f}s"
|
|
)
|
|
if sleep_sec > 0:
|
|
time.sleep(sleep_sec)
|
|
|
|
assert last_error is not None
|
|
raise last_error
|
|
|
|
|
|
def _append_clip_id(clip_ids: list[str], seen: set[str], value: Any) -> None:
|
|
clip_id = str(value).strip()
|
|
if clip_id and clip_id not in seen:
|
|
seen.add(clip_id)
|
|
clip_ids.append(clip_id)
|
|
|
|
|
|
def _extract_clip_ids_from_item(item: Any, clip_ids: list[str], seen: set[str]) -> None:
|
|
if item is None:
|
|
return
|
|
if isinstance(item, str):
|
|
_append_clip_id(clip_ids, seen, item)
|
|
return
|
|
if isinstance(item, (list, tuple, set)):
|
|
for sub_item in item:
|
|
_extract_clip_ids_from_item(sub_item, clip_ids, seen)
|
|
return
|
|
if isinstance(item, dict):
|
|
matched_key = False
|
|
for key in CLIP_ID_KEYS:
|
|
value = item.get(key)
|
|
if value is not None:
|
|
matched_key = True
|
|
_extract_clip_ids_from_item(value, clip_ids, seen)
|
|
if matched_key:
|
|
return
|
|
for value in item.values():
|
|
_extract_clip_ids_from_item(value, clip_ids, seen)
|
|
return
|
|
|
|
_append_clip_id(clip_ids, seen, item)
|
|
|
|
|
|
def extract_associated_clip_ids(payload: dict[str, Any]) -> list[str]:
|
|
clip_items = payload.get("associated_clip_list", [])
|
|
if clip_items is None:
|
|
return []
|
|
clip_ids: list[str] = []
|
|
seen: set[str] = set()
|
|
_extract_clip_ids_from_item(clip_items, clip_ids, seen)
|
|
return clip_ids
|
|
|
|
|
|
def get_associated_clip_ids(
|
|
event_id: str,
|
|
timeout: float = 60.0,
|
|
max_retries: int = 3,
|
|
retry_backoff_sec: float = 2.0,
|
|
) -> list[str]:
|
|
payload = fetch_event_meta(
|
|
event_id,
|
|
timeout=timeout,
|
|
max_retries=max_retries,
|
|
retry_backoff_sec=retry_backoff_sec,
|
|
)
|
|
return extract_associated_clip_ids(payload)
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Resolve PDCL clip ids from one or more event ids.")
|
|
parser.add_argument("event_ids", nargs="+", help="One or more event ids to resolve.")
|
|
parser.add_argument("--timeout", type=float, default=60.0, help="HTTP request timeout in seconds.")
|
|
parser.add_argument("--retries", type=int, default=3, help="Max request attempts per event id.")
|
|
parser.add_argument("--retry-backoff-sec", type=float, default=2.0, help="Base retry backoff in seconds.")
|
|
parser.add_argument("--pretty", action="store_true", help="Pretty-print the JSON result.")
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
result = {
|
|
event_id: get_associated_clip_ids(
|
|
event_id,
|
|
timeout=args.timeout,
|
|
max_retries=args.retries,
|
|
retry_backoff_sec=args.retry_backoff_sec,
|
|
)
|
|
for event_id in args.event_ids
|
|
}
|
|
if args.pretty:
|
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
else:
|
|
print(json.dumps(result, ensure_ascii=False))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|