Files
T
digital-garbage 9791cb8ccd Add FunPack Easy Gen (simplified single-scene UI) and a /funpack Hub
Easy Gen is a new Fooocus-style frontend at /funpack/easy — big preview,
one prompt box, Upload, Generate, and an Advanced settings panel — built
entirely on the Movie Editor's existing project format and API (no new
backend endpoints), so projects stay interchangeable between the two UIs.

The Hub at /funpack links to both UIs and surfaces version/branch/commit
plus an Update/Restart action reusing the Editor's git_update.py flow.

Startup now prints full host:port URLs for all three (Hub, Editor, Easy
Gen) via a new config.comfy_display_url() helper.

Also: settings_window.js's About section now labels itself via
window.FunPackAppName instead of hardcoding "Cutting Room", and Easy
Gen's boot-error banner accumulates distinct errors instead of
overwriting, so a cascading failure (e.g. dom.js not loading) surfaces
its root cause instead of just the last script in load order.
2026-07-23 16:37:26 +03:00

75 lines
2.5 KiB
Python

"""FunPack Easy Gen — simplified single-scene generation UI.
UI: GET /funpack/easy/ (static assets under /funpack/easy/<file>)
No new backend API on purpose: this frontend talks to the exact same
/funpack/movie/api/* endpoints the Movie Editor uses (same Project files,
same on-disk storage), so a project created here opens unchanged in the
full Editor and vice versa. The only job of this module is serving Easy
Gen's own frontend files, falling back to the Editor's frontend directory
for shared scripts/styles (api.js excluded — see easy_gen/frontend/api.js)
so those stay a single source of truth instead of being copy-pasted.
"""
from __future__ import annotations
import mimetypes
from pathlib import Path
from typing import Optional
try:
from aiohttp import web
from server import PromptServer
except Exception: # pragma: no cover - only available inside ComfyUI
web = None
PromptServer = None
from ..movie_editor.backend import config as movie_config
UI_PREFIX = "/funpack/easy"
FRONTEND_DIR = Path(__file__).resolve().parent / "frontend"
def _resolve(tail: str) -> Optional[Path]:
rel = (tail or "index.html").lstrip("/")
for root in (FRONTEND_DIR, movie_config.FRONTEND_DIR):
root = root.resolve()
target = (root / rel).resolve()
if root not in target.parents and target != root:
continue # path traversal guard
if target.is_dir():
target = target / "index.html"
if target.is_file():
return target
return None
def _serve_static(tail: str) -> "web.Response":
target = _resolve(tail)
if target is None:
raise web.HTTPNotFound()
ctype = mimetypes.guess_type(str(target))[0] or "application/octet-stream"
if target.suffix == ".js":
ctype = "text/javascript" # be explicit; some platforms guess text/plain
return web.Response(
body=target.read_bytes(), content_type=ctype,
headers={"Cache-Control": "no-store, max-age=0"},
)
if web is not None and PromptServer is not None:
routes = PromptServer.instance.routes
@routes.get(UI_PREFIX)
async def _root_redirect(_req):
raise web.HTTPFound(UI_PREFIX + "/")
@routes.get(UI_PREFIX + "/")
async def _index(_req):
return _serve_static("index.html")
@routes.get(UI_PREFIX + "/{tail:.*}")
async def _static(req):
return _serve_static(req.match_info["tail"])
print(f"[FunPack] Easy Gen available at {movie_config.comfy_display_url()}{UI_PREFIX}/")