feat: expand RF4 media inventory

This commit is contained in:
ik
2026-09-13 08:06:48 +07:00
parent 4042c80f00
commit 674bc5627f
9 changed files with 5054 additions and 19 deletions
+4 -2
View File
@@ -37,9 +37,9 @@ DEFAULT_STATE_FILE = Path(".cache/community-fetch-state.json")
ALLOWED_HOSTS = frozenset({
"download.rf4db.com", "rf4db.com",
"rf4-stat.ru",
"rf4map.ru",
"rf4map.ru", "gw.rf4map.ru", "hb.ru-msk.vkcloud-storage.ru",
"rf4-posts.com",
"rf4game.de",
"rf4game.de", "rf4game.ru",
})
MAX_RESPONSE_BYTES = 5 * 1024 * 1024 # 5 MB
@@ -105,6 +105,8 @@ def fetch_site_key(url: str) -> str:
hostname = hostname[4:]
if hostname.startswith("cdn."):
hostname = hostname[4:]
if hostname == "gw.rf4map.ru":
hostname = "rf4map.ru"
if not hostname:
raise ValueError("source URL must include a hostname")
return hostname
+1 -1
View File
@@ -41,7 +41,7 @@ def _kind(url: str, label: str | None, context: str) -> str:
value = f"{label or ''} {context}".casefold()
if "/flags/" in path or "/themes/" in path or path.endswith(("/logo.png", "/logo.svg")):
return "reference"
if re.search(r"/(?:fish|fishes|fische|species)/", path) or "/media/fische/" in value or any(word in value for word in ("рыба ", "fish: ", "fisch: ")):
if re.search(r"/(?:fish|fishes|fische|species)/", path) or re.search(r"/(?:fish|species)[_-]", path) or "/media/fische/" in value or any(word in value for word in ("рыба ", "fish: ", "fisch: ")):
return "fish"
if re.search(r"/(?:maps?|levels?|lakes?|waterbodies)/", path) or "/media/levels/" in value or any(word in value for word in ("карта ", "map of ", "gewässerkarte")):
return "waterbody"
+5 -4
View File
@@ -15,12 +15,12 @@ DEFAULT_ROOT = Path("data/media")
MAX_ASSET_BYTES = 15 * 1024 * 1024
def _download_one(root: Path, state_file: Path) -> str:
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"))
queued = next((item for item in manifest["assets"] if item.get("status") == "queued"), None)
queued = next((item for item in manifest["assets"] if item.get("status") == "queued" and (asset_url is None or item["asset_url"] == asset_url)), None)
if queued is None:
return "queue is empty"
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)
@@ -51,6 +51,7 @@ def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Index authorized RF4 media without hotlinking")
parser.add_argument("url", nargs="?")
parser.add_argument("--download-one", action="store_true", help="Store one queued asset while respecting site cooldown")
parser.add_argument("--asset-url", help="Download this exact queued asset instead of the first queue item")
parser.add_argument("--reclassify", action="store_true", help="Reapply current conservative classifier without network access")
parser.add_argument("--review-url", help="Review an asset already present in the manifest")
parser.add_argument("--decision", choices=("approved", "rejected"))
@@ -77,7 +78,7 @@ def main(argv: list[str] | None = None) -> int:
print(f"reclassified {len(manifest['assets'])} candidates")
return 0
if args.download_one:
print(_download_one(args.root, args.state_file))
print(_download_one(args.root, args.state_file, args.asset_url))
return 0
if not args.url:
parser.error("url is required unless --download-one is used")