Everything between local assets and fal endpoints, under FAL/Utils: - Dataset: Images → Training ZIP URL (LoRA caption layout), Folder → ZIP URL, Video → Frame Dataset ZIP URL, Batch Caption Images (parallel VLM, wires straight into the trainers). trainer_node's zip helper refactored onto shared ArchiveUtils. - Load: Load Image from URL (multi-URL batching), Load Audio from URL, Load Image Folder, Upload Folder as ZIP URL. - Video (PyAV/cv2, verified against real encoded fixtures): Extract Frames (efficient last-frame seek for image-to-video chaining), Trim (keyframe remux, no re-encode), Concat (normalizes resolution/fps), Mux Audio + Video, Video → Audio. - Image: Image Grid with Labels, Resize to fal Preset (cover/contain/stretch), Image ↔ Base64. - Data: JSON Extract (dot/bracket paths over result_json outputs), Prompt Lines cycler, Text Template. av added to dependencies (lazy imports keep the pack loading without it). Review fixes: folder zips log file count/bytes and enforce configurable [archive] caps before uploading to the CDN; batch captioning re-raises ComfyUI cancellation instead of recording empty captions; malformed JSON paths return the default instead of raising; string "false" parses as boolean False; cold-start balance checks collapse to a single API call. 9 new tests (100 total).
62 lines
1.6 KiB
Python
62 lines
1.6 KiB
Python
import importlib
|
|
import importlib.util
|
|
|
|
node_list = [
|
|
"image_node",
|
|
"video_node",
|
|
"llm_node",
|
|
"vlm_node",
|
|
"trainer_node",
|
|
"upscaler_node",
|
|
"platform_node",
|
|
"billing_node",
|
|
"inbox_node",
|
|
"util_dataset_node",
|
|
"util_media_in_node",
|
|
"util_video_node",
|
|
"util_image_node",
|
|
"util_data_node",
|
|
]
|
|
|
|
NODE_CLASS_MAPPINGS = {}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
|
|
|
for module_name in node_list:
|
|
imported_module = importlib.import_module(f".nodes.{module_name}", __name__)
|
|
|
|
NODE_CLASS_MAPPINGS = {**NODE_CLASS_MAPPINGS, **imported_module.NODE_CLASS_MAPPINGS}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
**NODE_DISPLAY_NAME_MAPPINGS,
|
|
**imported_module.NODE_DISPLAY_NAME_MAPPINGS,
|
|
}
|
|
|
|
try:
|
|
from .nodes.dynamic import get_dynamic_mappings
|
|
|
|
dyn_classes, dyn_display = get_dynamic_mappings()
|
|
# static nodes win on any key collision
|
|
for k, v in dyn_classes.items():
|
|
NODE_CLASS_MAPPINGS.setdefault(k, v)
|
|
for k, v in dyn_display.items():
|
|
NODE_DISPLAY_NAME_MAPPINGS.setdefault(k, v)
|
|
except Exception as _dynamic_error: # never break static nodes
|
|
import logging
|
|
|
|
logging.getLogger(__name__).error(
|
|
"Failed to load dynamic fal nodes: %s", _dynamic_error
|
|
)
|
|
|
|
|
|
WEB_DIRECTORY = "./web"
|
|
|
|
try:
|
|
from .nodes import server_routes as _server_routes # noqa: F401 registers /fal_api routes
|
|
except Exception as _routes_error: # never break node loading over HTTP extras
|
|
import logging
|
|
|
|
logging.getLogger(__name__).warning(
|
|
"fal API server routes not registered: %s", _routes_error
|
|
)
|
|
|
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
|