Files
ginnoir 1360a8fa65 feat(romhacks): automated hand-off — route downloads to RomM + library, render notes
handoff.py classifies completed downloads (console rom vs RPG-Maker fangame vs
patch, via bsdtar archive peek incl. RAR5), copies console roms into
Emulation/roms/<platform> for RomM, moves the full folder (rom + guides) into
the library archive, and renders note.md + handoff.json per hack for the
Obsidian publish step. Wired into the cycle; single /data mount keeps moves as
fast renames. Validated on 4 real games.
2026-06-07 20:25:30 -05:00

94 lines
4.3 KiB
YAML

# romhacks stack — automated Pokémon rom-hack ingestion from a Discord server's
# per-generation threads into the RomM library.
#
# FLOW
# romhacks (orchestrator) jdownloader (jlesage) RomM (roms stack)
# ┌───────────────────────┐ ┌──────────────────┐ ┌──────────────┐
# │ DiscordChatExporter ──▶│ JSON │ FolderWatch picks│ files │ scans │
# │ parse links + art ───▶│ crawljob│ up .crawljob, │ ───────▶ │ /storage1/ │
# │ stage metadata ───▶│ ──────▶ │ resolves ~all │ │ Emulation │
# └───────────────────────┘ │ hosts, downloads │ └──────────────┘
# └──────────────────┘
#
# WHY THIS SHAPE
# - Member-only access => no official Discord bot is possible. DiscordChatExporter
# (DCE) does a READ-ONLY export with a USER token. Self-token use violates
# Discord ToS, so this is wired for an ALT account (see stack.env) to isolate
# ban risk from the main account.
# - The download links span many file hosts (Mega/Drive/MediaFire/Pixeldrain/...),
# so JDownloader2 (jlesage image) does the actual fetching — it auto-resolves
# hundreds of hosts and handles waits/captcha via its noVNC UI.
# - DCE `--media` downloads the inline thread images (box art / screenshots); the
# orchestrator stages those + the thread blurb as metadata for a later RomM
# custom-cover hand-off (dovetails with the deck-sgdb art pipeline).
#
# INERT BY DEFAULT. Does nothing until armed: DISCORD_TOKEN/GUILD_ID are empty and
# DRY_RUN=true, so even if deployed it idles and never enqueues a download. See
# README.md for the arming checklist.
#
# BUILD (not bind) for the orchestrator: Portainer git-stack checkouts do NOT
# reliably materialize sibling repo files at relative FILE bind paths (Docker then
# auto-creates a directory — see the `share` stack note). So the orchestrator's
# scripts + channels.json are baked into a built image from ./orchestrator instead
# of bind-mounted. Editing channels.json => push => Portainer rebuild (use
# "Re-pull and redeploy" if a content-only change doesn't trigger a rebuild).
#
# Tiered binds: working state (exports/state/crawljobs + JD config) -> /config
# (SSD); bulk output + art -> /storage1/labdata/romhacks (ZFS, root-auto-created).
#
# Only jdownloader joins `edge` (Caddy proxies its noVNC UI, internal-only).
services:
romhacks:
build:
context: ./orchestrator
image: homelab/romhacks-orchestrator:latest
container_name: romhacks
restart: unless-stopped
networks: [romhacks]
env_file:
- stack.env
environment:
# Hand-off paths. /data is the single parent mount so incoming->library
# moves are instant renames (not cross-device copies); /emulation is the
# RomM Structure-A roms parent so console hacks land in roms/<platform>/.
- METADATA_DIR=/data/metadata
- INCOMING_DIR=/data/incoming
- LIBRARY_DIR=/data/library
- EMULATION_DIR=/emulation
- HANDOFF_STATE=/state/handled.json
volumes:
- /config/romhacks/exports:/exports
- /config/romhacks/state:/state
- /config/romhacks/crawljobs:/crawljobs
- /storage1/labdata/romhacks:/data
- /storage1/Emulation/roms:/emulation
jdownloader:
image: jlesage/jdownloader-2:latest
container_name: jdownloader
restart: unless-stopped
networks: [romhacks, edge]
env_file:
- stack.env
environment:
# root so it can write to the ZFS-tier /output bind (dirs auto-created as root)
- USER_ID=0
- GROUP_ID=0
volumes:
- /config/romhacks/jd:/config
# shared with the orchestrator: it drops .crawljob files here; the JD
# FolderWatch extension must be enabled + pointed at /watch (one-time, README).
- /config/romhacks/crawljobs:/watch
- /storage1/labdata/romhacks/incoming:/output
ports:
- "8998:5800"
networks:
romhacks:
name: romhacks
driver: bridge
edge:
name: edge
external: true