From 917177e821983632854e714d90b491dd2311e316 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:53:26 +0300 Subject: [PATCH] move assets related stuff to "app/assets" folder (#10184) --- app/__init__.py | 5 --- app/alembic_db/env.py | 3 +- app/assets/__init__.py | 4 ++ .../_helpers.py} | 0 app/{ => assets}/api/__init__.py | 0 .../assets_routes.py => assets/api/routes.py} | 37 ++++++++++--------- app/{ => assets}/api/schemas_in.py | 0 app/{ => assets}/api/schemas_out.py | 0 app/{ => assets}/database/__init__.py | 0 app/{ => assets}/database/helpers/__init__.py | 0 app/{ => assets}/database/helpers/bulk_ops.py | 0 .../database/helpers/escape_like.py | 0 .../database/helpers/fast_check.py | 0 app/{ => assets}/database/helpers/filters.py | 2 +- .../database/helpers/ownership.py | 0 .../database/helpers/projection.py | 0 app/{ => assets}/database/helpers/tags.py | 2 +- app/{ => assets}/database/models.py | 0 .../database/services/__init__.py | 0 app/{ => assets}/database/services/content.py | 2 +- app/{ => assets}/database/services/info.py | 2 +- app/{ => assets}/database/services/queries.py | 0 app/{ => assets}/database/timeutil.py | 0 app/{assets_manager.py => assets/manager.py} | 4 +- app/{assets_scanner.py => assets/scanner.py} | 4 +- app/{ => assets}/storage/__init__.py | 0 app/{ => assets}/storage/hashing.py | 0 app/{database => }/db.py | 4 +- main.py | 3 +- server.py | 2 +- 30 files changed, 37 insertions(+), 37 deletions(-) delete mode 100644 app/__init__.py create mode 100644 app/assets/__init__.py rename app/{_assets_helpers.py => assets/_helpers.py} (100%) rename app/{ => assets}/api/__init__.py (100%) rename app/{api/assets_routes.py => assets/api/routes.py} (94%) rename app/{ => assets}/api/schemas_in.py (100%) rename app/{ => assets}/api/schemas_out.py (100%) rename app/{ => assets}/database/__init__.py (100%) rename app/{ => assets}/database/helpers/__init__.py (100%) rename app/{ => assets}/database/helpers/bulk_ops.py (100%) rename app/{ => assets}/database/helpers/escape_like.py (100%) rename app/{ => assets}/database/helpers/fast_check.py (100%) rename app/{ => assets}/database/helpers/filters.py (98%) rename app/{ => assets}/database/helpers/ownership.py (100%) rename app/{ => assets}/database/helpers/projection.py (100%) rename app/{ => assets}/database/helpers/tags.py (98%) rename app/{ => assets}/database/models.py (100%) rename app/{ => assets}/database/services/__init__.py (100%) rename app/{ => assets}/database/services/content.py (99%) rename app/{ => assets}/database/services/info.py (99%) rename app/{ => assets}/database/services/queries.py (100%) rename app/{ => assets}/database/timeutil.py (100%) rename app/{assets_manager.py => assets/manager.py} (99%) rename app/{assets_scanner.py => assets/scanner.py} (99%) rename app/{ => assets}/storage/__init__.py (100%) rename app/{ => assets}/storage/hashing.py (100%) rename app/{database => }/db.py (98%) diff --git a/app/__init__.py b/app/__init__.py deleted file mode 100644 index f73951107..000000000 --- a/app/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .api.assets_routes import register_assets_system -from .assets_scanner import sync_seed_assets -from .database.db import init_db_engine - -__all__ = ["init_db_engine", "sync_seed_assets", "register_assets_system"] diff --git a/app/alembic_db/env.py b/app/alembic_db/env.py index 4d7770679..44f4e1a0c 100644 --- a/app/alembic_db/env.py +++ b/app/alembic_db/env.py @@ -2,13 +2,12 @@ from sqlalchemy import engine_from_config from sqlalchemy import pool from alembic import context +from app.assets.database.models import Base # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config - -from app.database.models import Base target_metadata = Base.metadata # other values from the config, defined by the needs of env.py, diff --git a/app/assets/__init__.py b/app/assets/__init__.py new file mode 100644 index 000000000..28020a293 --- /dev/null +++ b/app/assets/__init__.py @@ -0,0 +1,4 @@ +from .api.routes import register_assets_system +from .scanner import sync_seed_assets + +__all__ = ["sync_seed_assets", "register_assets_system"] diff --git a/app/_assets_helpers.py b/app/assets/_helpers.py similarity index 100% rename from app/_assets_helpers.py rename to app/assets/_helpers.py diff --git a/app/api/__init__.py b/app/assets/api/__init__.py similarity index 100% rename from app/api/__init__.py rename to app/assets/api/__init__.py diff --git a/app/api/assets_routes.py b/app/assets/api/routes.py similarity index 94% rename from app/api/assets_routes.py rename to app/assets/api/routes.py index 6bb0ed77e..4ca746775 100644 --- a/app/api/assets_routes.py +++ b/app/assets/api/routes.py @@ -10,7 +10,8 @@ from pydantic import ValidationError import folder_paths -from .. import assets_manager, assets_scanner, user_manager +from ... import user_manager +from .. import manager, scanner from . import schemas_in, schemas_out ROUTES = web.RouteTableDef() @@ -29,7 +30,7 @@ async def head_asset_by_hash(request: web.Request) -> web.Response: algo, digest = hash_str.split(":", 1) if algo != "blake3" or not digest or any(c for c in digest if c not in "0123456789abcdef"): return _error_response(400, "INVALID_HASH", "hash must be like 'blake3:'") - exists = await assets_manager.asset_exists(asset_hash=hash_str) + exists = await manager.asset_exists(asset_hash=hash_str) return web.Response(status=200 if exists else 404) @@ -51,7 +52,7 @@ async def list_assets(request: web.Request) -> web.Response: except ValidationError as ve: return _validation_error_response("INVALID_QUERY", ve) - payload = await assets_manager.list_assets( + payload = await manager.list_assets( include_tags=q.include_tags, exclude_tags=q.exclude_tags, name_contains=q.name_contains, @@ -72,7 +73,7 @@ async def download_asset_content(request: web.Request) -> web.Response: disposition = "attachment" try: - abs_path, content_type, filename = await assets_manager.resolve_asset_content_for_download( + abs_path, content_type, filename = await manager.resolve_asset_content_for_download( asset_info_id=str(uuid.UUID(request.match_info["id"])), owner_id=USER_MANAGER.get_request_user_id(request), ) @@ -102,7 +103,7 @@ async def create_asset_from_hash(request: web.Request) -> web.Response: except Exception: return _error_response(400, "INVALID_JSON", "Request body must be valid JSON.") - result = await assets_manager.create_asset_from_hash( + result = await manager.create_asset_from_hash( hash_str=body.hash, name=body.name, tags=body.tags, @@ -154,7 +155,7 @@ async def upload_asset(request: web.Request) -> web.Response: return _error_response(400, "INVALID_HASH", "hash must be like 'blake3:'") provided_hash = f"{algo}:{digest}" try: - provided_hash_exists = await assets_manager.asset_exists(asset_hash=provided_hash) + provided_hash_exists = await manager.asset_exists(asset_hash=provided_hash) except Exception: provided_hash_exists = None # do not fail the whole request here @@ -241,7 +242,7 @@ async def upload_asset(request: web.Request) -> web.Response: # Fast path: if a valid provided hash exists, create AssetInfo without writing anything if spec.hash and provided_hash_exists is True: try: - result = await assets_manager.create_asset_from_hash( + result = await manager.create_asset_from_hash( hash_str=spec.hash, name=spec.name or (spec.hash.split(":", 1)[1]), tags=spec.tags, @@ -269,7 +270,7 @@ async def upload_asset(request: web.Request) -> web.Response: return _error_response(404, "ASSET_NOT_FOUND", "Provided hash not found and no file uploaded.") try: - created = await assets_manager.upload_asset_from_temp_path( + created = await manager.upload_asset_from_temp_path( spec, temp_path=tmp_path, client_filename=file_client_name, @@ -300,7 +301,7 @@ async def upload_asset(request: web.Request) -> web.Response: async def get_asset(request: web.Request) -> web.Response: asset_info_id = str(uuid.UUID(request.match_info["id"])) try: - result = await assets_manager.get_asset( + result = await manager.get_asset( asset_info_id=asset_info_id, owner_id=USER_MANAGER.get_request_user_id(request), ) @@ -327,7 +328,7 @@ async def update_asset(request: web.Request) -> web.Response: return _error_response(400, "INVALID_JSON", "Request body must be valid JSON.") try: - result = await assets_manager.update_asset( + result = await manager.update_asset( asset_info_id=asset_info_id, name=body.name, tags=body.tags, @@ -357,7 +358,7 @@ async def set_asset_preview(request: web.Request) -> web.Response: return _error_response(400, "INVALID_JSON", "Request body must be valid JSON.") try: - result = await assets_manager.set_asset_preview( + result = await manager.set_asset_preview( asset_info_id=asset_info_id, preview_asset_id=body.preview_id, owner_id=USER_MANAGER.get_request_user_id(request), @@ -381,7 +382,7 @@ async def delete_asset(request: web.Request) -> web.Response: delete_content = True if delete_content is None else delete_content.lower() not in {"0", "false", "no"} try: - deleted = await assets_manager.delete_asset_reference( + deleted = await manager.delete_asset_reference( asset_info_id=asset_info_id, owner_id=USER_MANAGER.get_request_user_id(request), delete_content_if_orphan=delete_content, @@ -411,7 +412,7 @@ async def get_tags(request: web.Request) -> web.Response: status=400, ) - result = await assets_manager.list_tags( + result = await manager.list_tags( prefix=query.prefix, limit=query.limit, offset=query.offset, @@ -434,7 +435,7 @@ async def add_asset_tags(request: web.Request) -> web.Response: return _error_response(400, "INVALID_JSON", "Request body must be valid JSON.") try: - result = await assets_manager.add_tags_to_asset( + result = await manager.add_tags_to_asset( asset_info_id=asset_info_id, tags=data.tags, origin="manual", @@ -465,7 +466,7 @@ async def delete_asset_tags(request: web.Request) -> web.Response: return _error_response(400, "INVALID_JSON", "Request body must be valid JSON.") try: - result = await assets_manager.remove_tags_from_asset( + result = await manager.remove_tags_from_asset( asset_info_id=asset_info_id, tags=data.tags, owner_id=USER_MANAGER.get_request_user_id(request), @@ -496,7 +497,7 @@ async def seed_assets(request: web.Request) -> web.Response: return _validation_error_response("INVALID_BODY", ve) try: - await assets_scanner.sync_seed_assets(body.roots) + await scanner.sync_seed_assets(body.roots) except Exception: LOGGER.exception("sync_seed_assets failed for roots=%s", body.roots) return _error_response(500, "INTERNAL", "Unexpected server error.") @@ -515,14 +516,14 @@ async def schedule_asset_scan(request: web.Request) -> web.Response: except ValidationError as ve: return _validation_error_response("INVALID_BODY", ve) - states = await assets_scanner.schedule_scans(body.roots) + states = await scanner.schedule_scans(body.roots) return web.json_response(states.model_dump(mode="json"), status=202) @ROUTES.get("/api/assets/scan") async def get_asset_scan_status(request: web.Request) -> web.Response: root = request.query.get("root", "").strip().lower() - states = assets_scanner.current_statuses() + states = scanner.current_statuses() if root in {"models", "input", "output"}: states = [s for s in states.scans if s.root == root] # type: ignore states = schemas_out.AssetScanStatusResponse(scans=states) diff --git a/app/api/schemas_in.py b/app/assets/api/schemas_in.py similarity index 100% rename from app/api/schemas_in.py rename to app/assets/api/schemas_in.py diff --git a/app/api/schemas_out.py b/app/assets/api/schemas_out.py similarity index 100% rename from app/api/schemas_out.py rename to app/assets/api/schemas_out.py diff --git a/app/database/__init__.py b/app/assets/database/__init__.py similarity index 100% rename from app/database/__init__.py rename to app/assets/database/__init__.py diff --git a/app/database/helpers/__init__.py b/app/assets/database/helpers/__init__.py similarity index 100% rename from app/database/helpers/__init__.py rename to app/assets/database/helpers/__init__.py diff --git a/app/database/helpers/bulk_ops.py b/app/assets/database/helpers/bulk_ops.py similarity index 100% rename from app/database/helpers/bulk_ops.py rename to app/assets/database/helpers/bulk_ops.py diff --git a/app/database/helpers/escape_like.py b/app/assets/database/helpers/escape_like.py similarity index 100% rename from app/database/helpers/escape_like.py rename to app/assets/database/helpers/escape_like.py diff --git a/app/database/helpers/fast_check.py b/app/assets/database/helpers/fast_check.py similarity index 100% rename from app/database/helpers/fast_check.py rename to app/assets/database/helpers/fast_check.py diff --git a/app/database/helpers/filters.py b/app/assets/database/helpers/filters.py similarity index 98% rename from app/database/helpers/filters.py rename to app/assets/database/helpers/filters.py index 0b6d85b8d..0edc0c66d 100644 --- a/app/database/helpers/filters.py +++ b/app/assets/database/helpers/filters.py @@ -3,7 +3,7 @@ from typing import Optional, Sequence import sqlalchemy as sa from sqlalchemy import exists -from ..._assets_helpers import normalize_tags +from ..._helpers import normalize_tags from ..models import AssetInfo, AssetInfoMeta, AssetInfoTag diff --git a/app/database/helpers/ownership.py b/app/assets/database/helpers/ownership.py similarity index 100% rename from app/database/helpers/ownership.py rename to app/assets/database/helpers/ownership.py diff --git a/app/database/helpers/projection.py b/app/assets/database/helpers/projection.py similarity index 100% rename from app/database/helpers/projection.py rename to app/assets/database/helpers/projection.py diff --git a/app/database/helpers/tags.py b/app/assets/database/helpers/tags.py similarity index 98% rename from app/database/helpers/tags.py rename to app/assets/database/helpers/tags.py index 058869eca..402dc346d 100644 --- a/app/database/helpers/tags.py +++ b/app/assets/database/helpers/tags.py @@ -5,7 +5,7 @@ from sqlalchemy.dialects import postgresql as d_pg from sqlalchemy.dialects import sqlite as d_sqlite from sqlalchemy.ext.asyncio import AsyncSession -from ..._assets_helpers import normalize_tags +from ..._helpers import normalize_tags from ..models import AssetInfo, AssetInfoTag, Tag from ..timeutil import utcnow diff --git a/app/database/models.py b/app/assets/database/models.py similarity index 100% rename from app/database/models.py rename to app/assets/database/models.py diff --git a/app/database/services/__init__.py b/app/assets/database/services/__init__.py similarity index 100% rename from app/database/services/__init__.py rename to app/assets/database/services/__init__.py diff --git a/app/database/services/content.py b/app/assets/database/services/content.py similarity index 99% rename from app/database/services/content.py rename to app/assets/database/services/content.py index 05450f823..864c19044 100644 --- a/app/database/services/content.py +++ b/app/assets/database/services/content.py @@ -12,7 +12,7 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import noload -from ..._assets_helpers import compute_relative_filename +from ..._helpers import compute_relative_filename from ...storage import hashing as hashing_mod from ..helpers import ( ensure_tags_exist, diff --git a/app/database/services/info.py b/app/assets/database/services/info.py similarity index 99% rename from app/database/services/info.py rename to app/assets/database/services/info.py index 8a28bcf4c..b49955741 100644 --- a/app/database/services/info.py +++ b/app/assets/database/services/info.py @@ -8,7 +8,7 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import contains_eager, noload -from ..._assets_helpers import compute_relative_filename, normalize_tags +from ..._helpers import compute_relative_filename, normalize_tags from ..helpers import ( apply_metadata_filter, apply_tag_filters, diff --git a/app/database/services/queries.py b/app/assets/database/services/queries.py similarity index 100% rename from app/database/services/queries.py rename to app/assets/database/services/queries.py diff --git a/app/database/timeutil.py b/app/assets/database/timeutil.py similarity index 100% rename from app/database/timeutil.py rename to app/assets/database/timeutil.py diff --git a/app/assets_manager.py b/app/assets/manager.py similarity index 99% rename from app/assets_manager.py rename to app/assets/manager.py index 4aae6e8ad..50cf146d2 100644 --- a/app/assets_manager.py +++ b/app/assets/manager.py @@ -6,13 +6,13 @@ from typing import Optional, Sequence from comfy_api.internal import async_to_sync -from ._assets_helpers import ( +from ..db import create_session +from ._helpers import ( ensure_within_base, get_name_and_tags_from_asset_path, resolve_destination_from_tags, ) from .api import schemas_in, schemas_out -from .database.db import create_session from .database.models import Asset from .database.services import ( add_tags_to_asset_info, diff --git a/app/assets_scanner.py b/app/assets/scanner.py similarity index 99% rename from app/assets_scanner.py rename to app/assets/scanner.py index 7ef64a052..aa8123a49 100644 --- a/app/assets_scanner.py +++ b/app/assets/scanner.py @@ -10,7 +10,8 @@ import sqlalchemy as sa import folder_paths -from ._assets_helpers import ( +from ..db import create_session +from ._helpers import ( collect_models_files, compute_relative_filename, get_comfy_models_folders, @@ -21,7 +22,6 @@ from ._assets_helpers import ( ts_to_iso, ) from .api import schemas_in, schemas_out -from .database.db import create_session from .database.helpers import ( add_missing_tag_for_asset_id, ensure_tags_exist, diff --git a/app/storage/__init__.py b/app/assets/storage/__init__.py similarity index 100% rename from app/storage/__init__.py rename to app/assets/storage/__init__.py diff --git a/app/storage/hashing.py b/app/assets/storage/hashing.py similarity index 100% rename from app/storage/hashing.py rename to app/assets/storage/hashing.py diff --git a/app/database/db.py b/app/db.py similarity index 98% rename from app/database/db.py rename to app/db.py index 54f9000cc..f125706f0 100644 --- a/app/database/db.py +++ b/app/db.py @@ -27,8 +27,8 @@ SESSION: Optional[async_sessionmaker] = None def _root_paths(): """Resolve alembic.ini and migrations script folder.""" root_path = os.path.abspath(os.path.dirname(__file__)) - config_path = os.path.abspath(os.path.join(root_path, "../../alembic.ini")) - scripts_path = os.path.abspath(os.path.join(root_path, "../alembic_db")) + config_path = os.path.abspath(os.path.join(root_path, "../alembic.ini")) + scripts_path = os.path.abspath(os.path.join(root_path, "alembic_db")) return config_path, scripts_path diff --git a/main.py b/main.py index 62279268d..9bc9ac9ed 100644 --- a/main.py +++ b/main.py @@ -279,7 +279,8 @@ def cleanup_temp(): shutil.rmtree(temp_dir, ignore_errors=True) async def setup_database(): - from app import init_db_engine, sync_seed_assets + from app.assets import sync_seed_assets + from app.db import init_db_engine await init_db_engine() if not args.disable_assets_autoscan: diff --git a/server.py b/server.py index d8c1c02c3..424ca9b59 100644 --- a/server.py +++ b/server.py @@ -37,7 +37,7 @@ from app.model_manager import ModelFileManager from app.custom_node_manager import CustomNodeManager from typing import Optional, Union from api_server.routes.internal.internal_routes import InternalRoutes -from app import sync_seed_assets, register_assets_system +from app.assets import sync_seed_assets, register_assets_system from protocol import BinaryEventTypes # Import cache control middleware