feat(gui): replace AI studio with product suite
This commit is contained in:
+99
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
@@ -41,6 +42,7 @@ class ImageStudioProject:
|
||||
target_main_count: int
|
||||
target_detail_count: int
|
||||
draft_prompt: Optional[str]
|
||||
suite_settings_json: str
|
||||
status: str
|
||||
created_at: str
|
||||
updated_at: str
|
||||
@@ -357,6 +359,32 @@ def update_project_prompt(project_id, draft_prompt, path=None, conn=None):
|
||||
return get_project(project_id, conn=database)
|
||||
|
||||
|
||||
def project_suite_settings(project) -> dict:
|
||||
raw = _get(project, "suite_settings_json", "{}") or "{}"
|
||||
try:
|
||||
value = json.loads(str(raw))
|
||||
except (TypeError, ValueError):
|
||||
return {}
|
||||
return value if isinstance(value, dict) else {}
|
||||
|
||||
|
||||
def update_project_suite_settings(project_id, settings, path=None, conn=None):
|
||||
if not isinstance(settings, dict):
|
||||
raise db.DbError("商品套图设置必须是对象")
|
||||
payload = json.dumps(settings, ensure_ascii=False, sort_keys=True)
|
||||
with _connection(conn, path) as database:
|
||||
with database:
|
||||
database.execute(
|
||||
"""
|
||||
UPDATE image_studio_projects
|
||||
SET suite_settings_json = ?, updated_at = ?
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
""",
|
||||
(payload, _now(), int(project_id)),
|
||||
)
|
||||
return get_project(project_id, conn=database)
|
||||
|
||||
|
||||
def soft_delete_project(project_id, reason="", path=None, conn=None):
|
||||
now = _now()
|
||||
with _connection(conn, path) as database:
|
||||
@@ -461,6 +489,47 @@ def list_assets(project_id, kind=None, include_missing=True, path=None, conn=Non
|
||||
return _fetch_all(database, sql, params, ImageStudioAsset)
|
||||
|
||||
|
||||
def reorder_original_assets(project_id, asset_ids, path=None, conn=None):
|
||||
ordered_ids = [int(asset_id) for asset_id in asset_ids]
|
||||
if len(ordered_ids) != len(set(ordered_ids)):
|
||||
raise db.DbError("商品原图排序不能包含重复图片")
|
||||
with _connection(conn, path) as database:
|
||||
rows = database.execute(
|
||||
"""
|
||||
SELECT id FROM image_studio_assets
|
||||
WHERE project_id = ? AND kind = ?
|
||||
ORDER BY source_order, id
|
||||
""",
|
||||
(int(project_id), ASSET_KIND_ORIGINAL),
|
||||
).fetchall()
|
||||
existing_ids = [int(row["id"]) for row in rows]
|
||||
if set(existing_ids) != set(ordered_ids):
|
||||
raise db.DbError("商品原图排序必须包含当前项目的全部原图")
|
||||
now = _now()
|
||||
with database:
|
||||
for source_order, asset_id in enumerate(ordered_ids, 1):
|
||||
database.execute(
|
||||
"""
|
||||
UPDATE image_studio_assets
|
||||
SET source_order = ?, updated_at = ?
|
||||
WHERE id = ? AND project_id = ? AND kind = ?
|
||||
""",
|
||||
(
|
||||
source_order,
|
||||
now,
|
||||
asset_id,
|
||||
int(project_id),
|
||||
ASSET_KIND_ORIGINAL,
|
||||
),
|
||||
)
|
||||
return list_assets(
|
||||
project_id,
|
||||
kind=ASSET_KIND_ORIGINAL,
|
||||
path=path,
|
||||
conn=database,
|
||||
)
|
||||
|
||||
|
||||
def asset_reference_counts(asset_id, path=None, conn=None):
|
||||
"""Return selection/job references for one asset before pool removal."""
|
||||
|
||||
@@ -506,7 +575,7 @@ def remove_asset_if_unused(asset_id, path=None, conn=None):
|
||||
return asset
|
||||
|
||||
|
||||
def sync_original_asset_urls(project_id, image_urls, path=None, conn=None):
|
||||
def sync_original_asset_urls(project_id, image_urls, path=None, conn=None, max_assets=16):
|
||||
"""Store the read-only Shopee main image URL snapshot as remote-only assets."""
|
||||
|
||||
normalized = []
|
||||
@@ -538,6 +607,13 @@ def sync_original_asset_urls(project_id, image_urls, path=None, conn=None):
|
||||
for row in existing_rows
|
||||
if row["remote_url"]
|
||||
}
|
||||
local_only_count = sum(
|
||||
1
|
||||
for row in existing_rows
|
||||
if not row["remote_url"] and row["status"] != ASSET_STATUS_MISSING
|
||||
)
|
||||
remote_limit = max(0, int(max_assets) - local_only_count)
|
||||
normalized = normalized[:remote_limit]
|
||||
active_ids = set()
|
||||
for item in normalized:
|
||||
row = by_url.get(item["src"])
|
||||
@@ -577,7 +653,11 @@ def sync_original_asset_urls(project_id, image_urls, path=None, conn=None):
|
||||
),
|
||||
)
|
||||
active_ids.add(int(cursor.lastrowid))
|
||||
missing_ids = [int(row["id"]) for row in existing_rows if int(row["id"]) not in active_ids]
|
||||
missing_ids = [
|
||||
int(row["id"])
|
||||
for row in existing_rows
|
||||
if row["remote_url"] and int(row["id"]) not in active_ids
|
||||
]
|
||||
if missing_ids:
|
||||
placeholders = ",".join("?" for _ in missing_ids)
|
||||
database.execute(
|
||||
@@ -677,6 +757,23 @@ def get_job(job_id, path=None, conn=None):
|
||||
)
|
||||
|
||||
|
||||
def list_jobs(project_id, *, statuses=None, path=None, conn=None):
|
||||
clauses = ["project_id = ?"]
|
||||
params = [int(project_id)]
|
||||
requested_statuses = [str(status) for status in (statuses or []) if str(status)]
|
||||
if requested_statuses:
|
||||
invalid = set(requested_statuses) - JOB_STATUSES
|
||||
if invalid:
|
||||
raise db.DbError("图片生成任务状态无效")
|
||||
placeholders = ",".join("?" for _ in requested_statuses)
|
||||
clauses.append(f"status IN ({placeholders})")
|
||||
params.extend(requested_statuses)
|
||||
sql = "SELECT * FROM image_studio_jobs WHERE " + " AND ".join(clauses)
|
||||
sql += " ORDER BY updated_at DESC, id DESC"
|
||||
with _connection(conn, path) as database:
|
||||
return _fetch_all(database, sql, params, ImageStudioJob)
|
||||
|
||||
|
||||
def set_job_submitted(job_id, task_id, *, call_id=None, points_cost=None, points_balance=None, path=None, conn=None):
|
||||
now = _now()
|
||||
with _connection(conn, path) as database:
|
||||
|
||||
Reference in New Issue
Block a user