Files
rf4-spotter/apps/api/app/media_catalog.py
T

108 lines
5.0 KiB
Python

from __future__ import annotations
import json
import os
from pathlib import Path
MEDIA_ROOT = Path(os.environ.get("MEDIA_ROOT", "data/media")).resolve()
def published_assets(entity_type: str | None = None) -> list[dict]:
manifest = json.loads((MEDIA_ROOT / "manifest.json").read_text(encoding="utf-8"))
result = []
for item in manifest.get("assets", []):
if item.get("status") != "approved" or not item.get("sha256") or not item.get("local_path"):
continue
if entity_type and item.get("entity_type") != entity_type:
continue
source_page = str(item.get("source_page") or "")
source = "rf4db" if "rf4db.com" in source_page else "rf4map" if "rf4map.ru" in source_page else "rf4-official"
result.append({
"id": item["sha256"],
"entity_type": item.get("entity_type"),
"entity_key": item.get("entity_key"),
"label": item.get("label"),
"width": item.get("width"),
"height": item.get("height"),
"content_type": item.get("content_type"),
"image_url": f"/api/v1/media/assets/{item['sha256']}",
"source_system": source,
"source_url": source_page,
"variants": [
{
"role": variant.get("role"),
"format": variant.get("format"),
"width": variant.get("width"),
"height": variant.get("height"),
"url": f"/api/v1/media/assets/{variant['sha256']}",
}
for variant in item.get("derivatives", [])
if variant.get("sha256") and variant.get("local_path")
],
})
return sorted(result, key=lambda item: (str(item["entity_type"]), str(item["label"] or "").casefold(), item["id"]))
def published_file(digest: str) -> tuple[Path, str] | None:
if len(digest) != 64 or any(char not in "0123456789abcdef" for char in digest):
return None
manifest = json.loads((MEDIA_ROOT / "manifest.json").read_text(encoding="utf-8"))
item = next((row for row in manifest.get("assets", []) if row.get("status") == "approved" and row.get("sha256") == digest), None)
if not item:
return None
target = (MEDIA_ROOT / item["local_path"]).resolve()
if not target.is_relative_to(MEDIA_ROOT.resolve()) or not target.is_file():
return None
return target, str(item["content_type"])
def review_assets(entity_type: str | None = None, status: str | None = None) -> list[dict]:
manifest = json.loads((MEDIA_ROOT / "manifest.json").read_text(encoding="utf-8"))
result = []
for item in manifest.get("assets", []):
item_status = str(item.get("status") or "")
if item_status not in {"approved", "upgrade_queued", "upgrade_stored"} or (status and item_status != status):
continue
if entity_type and item.get("entity_type") != entity_type:
continue
digest = str(item.get("sha256") or "")
if len(digest) != 64 or not item.get("local_path"):
continue
source_page = str(item.get("source_page") or "")
source = "rf4db" if "rf4db.com" in source_page else "rf4map" if "rf4map.ru" in source_page else "rf4-official"
result.append({
"id": digest,
"status": item_status,
"entity_type": item.get("entity_type"),
"entity_key": item.get("entity_key"),
"label": item.get("label"),
"width": item.get("width"),
"height": item.get("height"),
"content_type": item.get("content_type"),
"image_url": f"/api/v1/admin/media/assets/{digest}",
"asset_url": item.get("asset_url", ""),
"source_system": source,
"source_url": source_page,
"duplicate_of": item.get("duplicate_of"),
"supersedes": item.get("supersedes"),
"derivatives": [{
"role": variant.get("role"), "format": variant.get("format"),
"width": variant.get("width"), "height": variant.get("height"),
} for variant in item.get("derivatives", [])],
})
return sorted(result, key=lambda item: (str(item["status"]), str(item["entity_type"]), str(item["label"] or "").casefold(), item["id"]))
def review_file(digest: str) -> tuple[Path, str] | None:
if len(digest) != 64 or any(char not in "0123456789abcdef" for char in digest):
return None
manifest = json.loads((MEDIA_ROOT / "manifest.json").read_text(encoding="utf-8"))
item = next((row for row in manifest.get("assets", []) if row.get("sha256") == digest and row.get("status") in {"approved", "upgrade_queued", "upgrade_stored"}), None)
if not item or not item.get("local_path"):
return None
target = (MEDIA_ROOT / item["local_path"]).resolve()
if not target.is_relative_to(MEDIA_ROOT.resolve()) or not target.is_file():
return None
return target, str(item.get("content_type") or "application/octet-stream")