T-564 async cmhub image tasks

This commit is contained in:
chengma
2026-07-09 00:07:09 +08:00
parent 487408ba0f
commit c435456977
10 changed files with 761 additions and 38 deletions
+92 -1
View File
@@ -46,6 +46,7 @@ FAILURE_STEP_LABELS = {
"cover_submit": "提交封面任务",
"cover_build_request": "构建封面请求",
"cover_request": "请求生成封面",
"cover_poll": "等待封面结果",
"cover_download": "下载新封面",
"cover_save": "保存新封面",
"cover_parse_response": "解析封面响应",
@@ -138,6 +139,8 @@ class Task:
old_cover_path: Optional[str]
new_title: Optional[str]
new_cover_path: Optional[str]
image_task_id: Optional[str]
image_task_key: Optional[str]
committed: int
stage: str
status: str
@@ -231,6 +234,8 @@ CREATE TABLE IF NOT EXISTS tasks (
old_cover_path TEXT,
new_title TEXT,
new_cover_path TEXT,
image_task_id TEXT,
image_task_key TEXT,
committed INTEGER NOT NULL DEFAULT 0,
stage TEXT NOT NULL DEFAULT 'imported',
status TEXT NOT NULL DEFAULT 'pending',
@@ -355,6 +360,7 @@ def init_db(path=None, conn=None) -> None:
with database:
database.executescript(SCHEMA_SQL)
_ensure_batch_delete_columns(database)
_ensure_task_image_task_columns(database)
def _ensure_batch_delete_columns(database):
@@ -364,6 +370,14 @@ def _ensure_batch_delete_columns(database):
if "deleted_reason" not in columns:
database.execute("ALTER TABLE batches ADD COLUMN deleted_reason TEXT")
def _ensure_task_image_task_columns(database):
columns = {row["name"] for row in database.execute("PRAGMA table_info(tasks)").fetchall()}
if "image_task_id" not in columns:
database.execute("ALTER TABLE tasks ADD COLUMN image_task_id TEXT")
if "image_task_key" not in columns:
database.execute("ALTER TABLE tasks ADD COLUMN image_task_key TEXT")
def create_batch(file_paths: Iterable[str], note=None, path=None, conn=None) -> str:
batch_id = datetime.now().strftime("%Y%m%d_%H%M%S_") + uuid.uuid4().hex[:8]
files = [os.path.abspath(file_path) for file_path in file_paths]
@@ -688,6 +702,79 @@ def mark_skipped(task_id, reason, path=None, conn=None) -> None:
)
def ensure_image_task_key(task_id, path=None, conn=None) -> str:
"""Return a stable cmhub image idempotency key for the current cover attempt."""
task_id = int(task_id)
with _connection(conn, path) as database:
with database:
row = database.execute(
"SELECT image_task_key FROM tasks WHERE id = ?",
(task_id,),
).fetchone()
if row is None:
raise DbError(f"任务不存在: {task_id}")
key = str(row["image_task_key"] or "").strip()
if not key:
key = f"cmshopee-task-{task_id}-{uuid.uuid4().hex}"
database.execute(
"""
UPDATE tasks
SET image_task_key = ?, updated_at = ?
WHERE id = ?
""",
(key, _now(), task_id),
)
return key
def set_image_task_submitted(task_id, image_task_id, image_task_key=None, path=None, conn=None) -> None:
"""Persist the cmhub image task id immediately after submit succeeds."""
task_id = int(task_id)
image_task_id = str(image_task_id or "").strip()
if not image_task_id:
raise DbError("缺少 cmhub 生图任务ID")
with _connection(conn, path) as database:
with database:
row = database.execute(
"SELECT image_task_key FROM tasks WHERE id = ?",
(task_id,),
).fetchone()
if row is None:
raise DbError(f"任务不存在: {task_id}")
key = str(image_task_key or row["image_task_key"] or "").strip()
if not key:
key = f"cmshopee-task-{task_id}-{uuid.uuid4().hex}"
database.execute(
"""
UPDATE tasks
SET image_task_id = ?,
image_task_key = ?,
updated_at = ?
WHERE id = ?
""",
(image_task_id, key, _now(), task_id),
)
def clear_image_task(task_id, path=None, conn=None) -> None:
"""Clear persisted cmhub image task state so the next cover run can submit anew."""
with _connection(conn, path) as database:
with database:
database.execute(
"""
UPDATE tasks
SET image_task_id = NULL,
image_task_key = NULL,
updated_at = ?
WHERE id = ?
""",
(_now(), int(task_id)),
)
def set_collected(task_id, old_title, old_cover_path, path=None, conn=None) -> None:
now = _now()
with _connection(conn, path) as database:
@@ -821,19 +908,23 @@ def reset_generated(
now = _now()
new_title = None if reset_title else before.new_title
new_cover = None if reset_cover else before.new_cover_path
image_task_id = None if reset_cover else before.image_task_id
image_task_key = None if reset_cover else before.image_task_key
with database:
database.execute(
"""
UPDATE tasks
SET new_title = ?,
new_cover_path = ?,
image_task_id = ?,
image_task_key = ?,
stage = 'generated',
status = 'success',
last_error = NULL,
updated_at = ?
WHERE id = ?
""",
(new_title, new_cover, now, int(task_id)),
(new_title, new_cover, image_task_id, image_task_key, now, int(task_id)),
)
after = get_task(task_id, conn=database)
return {