A card on the canvas (right-click > Job sheet) takes many prompts, a line each, the runs of each, a rest between pictures and the same seeds if wanted; with no prompt it runs the canvas as it is. The page runs it from copies of the workflow kept at the start, as saves run, so the canvas stays as it is; pause, go on and stop from any page, a failed or interrupted run pauses it, a reload goes on and another page takes over a sheet whose page is gone. run_workflow(sheet=...) lets an AI hand over prompts or settings by name; the sheet is that call's run step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
174 lines
12 KiB
Python
174 lines
12 KiB
Python
from . import (
|
|
activity_log, audio_catalog, folder_types, gallery_routes, materials, media_routes, model_catalog,
|
|
model_folder, model_media, model_metadata, model_resolution, model_type_listing, kept_images, quick_index, node_material, recipe_packages, recipes, scan_choice, scan_report, scan_summary,
|
|
local_guard, lora_info, mcp_server, model_download, model_import, model_placement, translation_routes, version_manager, tts_version, backup,
|
|
network_check, model_heads, model_usage, usage_ledger, sidecar_layout, ai_tasks_routes, snapshot_routes, workflow_saves, example_workflows,
|
|
model_guide, job_sheets,
|
|
)
|
|
from .scanner import *
|
|
from .config import *
|
|
from .notebooks import *
|
|
from .parameters import *
|
|
|
|
import os
|
|
|
|
from aiohttp import web
|
|
|
|
# ComfyUI serves the web folder under the plugin folder's own name, whatever it is called.
|
|
EXTENSION_STATIC_PREFIX = f'/extensions/{os.path.basename(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))}/'
|
|
|
|
|
|
@web.middleware
|
|
async def no_cache_extension_middleware(request, handler):
|
|
"""Make browsers revalidate this plugin's JS/CSS so a normal refresh picks up updates.
|
|
|
|
`no-cache` still allows 304 responses via ETag/Last-Modified. This replaces
|
|
per-import `?v=` query strings, which load a second module instance whenever
|
|
two files import the same module with different URLs.
|
|
"""
|
|
response = await handler(request)
|
|
if request.path.startswith(EXTENSION_STATIC_PREFIX):
|
|
response.headers['Cache-Control'] = 'no-cache'
|
|
return response
|
|
|
|
def setup_routes(app):
|
|
model_heads.keep_on_disk()
|
|
if hasattr(app, 'middlewares') and no_cache_extension_middleware not in app.middlewares:
|
|
app.middlewares.append(no_cache_extension_middleware)
|
|
# Other devices on the network may look; only this computer may change things (local_guard.py).
|
|
if hasattr(app, 'middlewares') and local_guard.local_only_middleware not in app.middlewares:
|
|
app.middlewares.append(local_guard.local_only_middleware)
|
|
app.router.add_get('/anomalous/access', local_guard.api_access)
|
|
app.router.add_post('/anomalous/snapshot_check', snapshot_routes.api_snapshot_check)
|
|
app.router.add_get('/anomalous/saves', workflow_saves.api_list)
|
|
app.router.add_get('/anomalous/saves/group', workflow_saves.api_group)
|
|
app.router.add_get('/anomalous/saves/workflow', workflow_saves.api_workflow)
|
|
app.router.add_post('/anomalous/saves/add', workflow_saves.api_add)
|
|
app.router.add_post('/anomalous/saves/update', workflow_saves.api_update)
|
|
app.router.add_post('/anomalous/saves/ran', workflow_saves.api_ran)
|
|
app.router.add_post('/anomalous/saves/remove', workflow_saves.api_remove)
|
|
app.router.add_post('/anomalous/saves/remove_group', workflow_saves.api_remove_group)
|
|
app.router.add_get('/anomalous/job_sheet', job_sheets.api_get)
|
|
app.router.add_get('/anomalous/job_sheet/workflow', job_sheets.api_workflow)
|
|
app.router.add_post('/anomalous/job_sheet/start', job_sheets.api_start)
|
|
app.router.add_post('/anomalous/job_sheet/report', job_sheets.api_report)
|
|
app.router.add_post('/anomalous/job_sheet/ask', job_sheets.api_ask)
|
|
app.router.add_post('/anomalous/job_sheet/clear', job_sheets.api_clear)
|
|
try:
|
|
folder_types.upgrade_folder_defaults()
|
|
except Exception as error:
|
|
print(f"[Anomalous Browser] Could not update the shown model folders: {error}")
|
|
app.router.add_get('/anomalous/folders', model_catalog.api_get_folders)
|
|
app.router.add_get('/anomalous/all_folder_types', folder_types.api_get_all_folder_types)
|
|
app.router.add_get('/anomalous/models', model_catalog.api_get_models)
|
|
app.router.add_get('/anomalous/type_models', model_type_listing.api_get_type_models)
|
|
app.router.add_get('/anomalous/all_scan_models', model_catalog.api_get_all_scan_models)
|
|
app.router.add_get('/anomalous/image', media_routes.api_serve_image)
|
|
app.router.add_get('/anomalous/card_cache', media_routes.api_card_cache)
|
|
app.router.add_post('/anomalous/card_cache/clear', media_routes.api_clear_card_cache)
|
|
app.router.add_post('/anomalous/scan', api_scan_folder)
|
|
app.router.add_get('/anomalous/scan_status', api_scan_status)
|
|
app.router.add_get('/anomalous/find_model', model_catalog.api_find_model)
|
|
app.router.add_get('/anomalous/config', api_get_config)
|
|
app.router.add_post('/anomalous/save_config', api_save_config)
|
|
app.router.add_post('/anomalous/delete_model', model_metadata.api_delete_model)
|
|
app.router.add_post('/anomalous/open_model_folder', model_folder.api_open_model_folder)
|
|
app.router.add_get('/anomalous/compatible_models', model_catalog.api_compatible_models)
|
|
app.router.add_get('/anomalous/model_guide', model_guide.api_model_guide)
|
|
app.router.add_get('/anomalous/notebooks', api_get_notebooks)
|
|
app.router.add_get('/anomalous/notebooks/by_node_type', api_get_notebooks_by_node_type)
|
|
app.router.add_post('/anomalous/save_notebook', api_save_notebook)
|
|
app.router.add_post('/anomalous/delete_notebook', api_delete_notebook)
|
|
|
|
# Parameter Routes
|
|
app.router.add_get('/anomalous/parameters', api_get_parameters)
|
|
app.router.add_get('/anomalous/parameters/by_node_type', api_get_parameters_by_type)
|
|
app.router.add_get('/anomalous/parameters/signatures', api_get_parameter_set_signatures)
|
|
app.router.add_post('/anomalous/save_parameter', api_save_parameter)
|
|
app.router.add_post('/anomalous/rename_parameter', api_rename_parameter)
|
|
app.router.add_post('/anomalous/delete_parameter', api_delete_parameter)
|
|
app.router.add_get('/anomalous/parameter_gallery', api_get_parameter_gallery)
|
|
app.router.add_get('/anomalous/parameter_pictures', api_get_parameter_pictures)
|
|
|
|
# Recipe Routes
|
|
app.router.add_get('/anomalous/recipes', recipes.api_get_recipes)
|
|
app.router.add_get('/anomalous/example_workflows', example_workflows.api_examples)
|
|
app.router.add_get('/anomalous/recipe_full', recipes.api_get_recipe_full)
|
|
app.router.add_get('/anomalous/recipe_asset', recipes.api_get_recipe_asset)
|
|
app.router.add_get('/anomalous/recipe_gallery', recipes.api_get_recipe_gallery)
|
|
app.router.add_get('/anomalous/recipe_parameter_gallery', recipes.api_get_recipe_parameter_gallery)
|
|
app.router.add_get('/anomalous/recipe_gallery_compare', recipes.api_get_recipe_gallery_compare)
|
|
app.router.add_post('/anomalous/save_recipe', recipes.api_save_recipe)
|
|
app.router.add_post('/anomalous/update_recipe', recipes.api_update_recipe)
|
|
app.router.add_post('/anomalous/recipe_focus', recipes.api_set_recipe_focus)
|
|
app.router.add_post('/anomalous/set_recipe_gallery_cover', recipes.api_set_recipe_gallery_cover)
|
|
app.router.add_post('/anomalous/delete_recipe', recipes.api_delete_recipe)
|
|
app.router.add_get('/anomalous/recipe_history', recipes.api_get_recipe_history)
|
|
app.router.add_get('/anomalous/recipe_version', recipes.api_get_recipe_version)
|
|
app.router.add_post('/anomalous/restore_recipe_version', recipes.api_restore_recipe_version)
|
|
app.router.add_post('/anomalous/refresh_recipe_identity', recipes.api_refresh_recipe_identity)
|
|
app.router.add_post('/anomalous/export_recipe_package', recipe_packages.api_export_recipe_package)
|
|
app.router.add_post('/anomalous/import_recipe_package_inspect', recipe_packages.api_import_recipe_package_inspect)
|
|
app.router.add_post('/anomalous/import_recipe_package_commit', recipe_packages.api_import_recipe_package_commit)
|
|
|
|
# Curated Material Library Routes
|
|
app.router.add_get('/anomalous/materials', materials.api_get_materials)
|
|
app.router.add_get('/anomalous/material_full', materials.api_get_material_full)
|
|
app.router.add_get('/anomalous/material_asset', materials.api_get_material_asset)
|
|
app.router.add_get('/anomalous/materials/by_node_type', materials.api_get_materials_by_node_type)
|
|
app.router.add_post('/anomalous/inspect_image_material', materials.api_inspect_image_material)
|
|
app.router.add_post('/anomalous/save_image_material', materials.api_save_image_material)
|
|
app.router.add_post('/anomalous/save_prompt_plan', materials.api_save_prompt_plan)
|
|
app.router.add_post('/anomalous/save_node_material', node_material.api_save_node_material)
|
|
app.router.add_post('/anomalous/delete_material', materials.api_delete_material)
|
|
app.router.add_post('/anomalous/update_material', materials.api_update_material)
|
|
app.router.add_post('/anomalous/mark_material_moved', materials.api_mark_material_moved)
|
|
|
|
app.router.add_post('/anomalous/translate', translation_routes.api_translate)
|
|
app.router.add_get('/anomalous/base_models', model_catalog.api_base_models)
|
|
app.router.add_get('/anomalous/gallery_images', gallery_routes.api_get_gallery_images)
|
|
app.router.add_post('/anomalous/delete_gallery_image', gallery_routes.api_delete_gallery_image)
|
|
app.router.add_get('/anomalous/output_thumbnail', gallery_routes.api_output_thumbnail)
|
|
app.router.add_get('/anomalous/output_preview', gallery_routes.api_output_preview)
|
|
app.router.add_get('/anomalous/kept_images', kept_images.api_kept_images)
|
|
app.router.add_get('/anomalous/quick_index', quick_index.api_quick_index)
|
|
app.router.add_get('/anomalous/resolve_hash', model_resolution.api_resolve_hash)
|
|
app.router.add_post('/anomalous/resolve_hash_batch', model_resolution.api_resolve_hash_batch)
|
|
app.router.add_get('/anomalous/all_hashes', model_resolution.api_get_all_hashes)
|
|
app.router.add_post('/anomalous/scan_all', api_scan_all)
|
|
app.router.add_get('/anomalous/global_scan_status', api_global_scan_status)
|
|
app.router.add_post('/anomalous/clear_cache', media_routes.api_clear_cache)
|
|
app.router.add_post('/anomalous/update_metadata', model_metadata.api_update_metadata)
|
|
app.router.add_post('/anomalous/set_custom_cover', model_media.api_set_custom_cover)
|
|
app.router.add_post('/anomalous/upload_custom_cover', model_media.api_upload_custom_cover)
|
|
app.router.add_get('/anomalous/model_covers', model_media.api_model_covers)
|
|
app.router.add_get('/anomalous/model_images', media_routes.api_get_model_images)
|
|
app.router.add_post('/anomalous/resolve_paths_to_previews', model_catalog.api_resolve_paths_to_previews)
|
|
app.router.add_get('/anomalous/scan_summary', scan_summary.api_scan_summary)
|
|
app.router.add_get('/anomalous/scan_matched', scan_summary.api_scan_matched)
|
|
app.router.add_get('/anomalous/scan_pickable', scan_summary.api_scan_pickable)
|
|
app.router.add_get('/anomalous/last_scan', scan_report.api_last_scan)
|
|
app.router.add_get('/anomalous/scan_model', scan_report.api_scan_model)
|
|
app.router.add_post('/anomalous/scan_choice', scan_choice.api_set_scan_choice)
|
|
app.router.add_get('/anomalous/lora_info', lora_info.api_lora_info)
|
|
|
|
# Audio & Voice Studio Routes
|
|
app.router.add_get('/anomalous/audio_stream', audio_catalog.api_serve_audio)
|
|
app.router.add_get('/anomalous/audio_gallery', audio_catalog.api_get_audio_gallery)
|
|
app.router.add_post('/anomalous/delete_audio_gallery', audio_catalog.api_delete_audio_gallery)
|
|
app.router.add_get('/anomalous/audio_previews', audio_catalog.api_get_audio_previews)
|
|
app.router.add_post('/anomalous/save_audio_preview', audio_catalog.api_save_audio_preview)
|
|
version_manager.register_routes(app)
|
|
tts_version.register_routes(app)
|
|
activity_log.register_routes(app)
|
|
mcp_server.register_routes(app)
|
|
ai_tasks_routes.register_routes(app)
|
|
model_download.register_routes(app)
|
|
network_check.register_routes(app)
|
|
model_usage.register_routes(app)
|
|
usage_ledger.register_routes(app)
|
|
sidecar_layout.register_routes(app)
|
|
model_import.register_routes(app)
|
|
model_placement.register_routes(app)
|
|
backup.register_routes(app)
|