Files
biliup-next/src/biliup_next/app/task_runner.py
2026-04-01 00:44:58 +08:00

75 lines
3.2 KiB
Python

from __future__ import annotations
from biliup_next.app.bootstrap import ensure_initialized
from biliup_next.app.task_audit import record_task_action
from biliup_next.app.task_engine import (
execute_step,
next_runnable_step,
)
from biliup_next.app.task_policies import apply_disabled_step_fallbacks
from biliup_next.app.task_policies import resolve_failure
from biliup_next.core.errors import ModuleError
from biliup_next.core.models import utc_now_iso
def process_task(task_id: str, *, reset_step: str | None = None, include_stage_scan: bool = False) -> dict[str, object]:
state = ensure_initialized()
repo = state["repo"]
task = repo.get_task(task_id)
if task is None:
return {"processed": [], "error": {"code": "TASK_NOT_FOUND", "message": f"task not found: {task_id}"}}
processed: list[dict[str, object]] = []
if include_stage_scan:
ingest_settings = dict(state["settings"]["ingest"])
ingest_settings.update(state["settings"]["paths"])
stage_scan = state["ingest_service"].scan_stage(ingest_settings)
processed.append({"stage_scan": stage_scan})
record_task_action(repo, task_id, "stage_scan", "ok", "stage scan completed", stage_scan)
if reset_step:
step_names = {step.step_name for step in repo.list_steps(task_id)}
if reset_step not in step_names:
return {"processed": processed, "error": {"code": "STEP_NOT_FOUND", "message": f"step not found: {reset_step}"}}
repo.update_step_status(
task_id,
reset_step,
"pending",
error_code=None,
error_message=None,
started_at=None,
finished_at=None,
)
repo.update_task_status(task_id, task.status, utc_now_iso())
processed.append({"task_id": task_id, "step": reset_step, "reset": True})
record_task_action(repo, task_id, "retry_step", "ok", f"step reset to pending: {reset_step}", {"step_name": reset_step})
try:
while True:
current_task = repo.get_task(task.id) or task
current_steps = {step.step_name: step for step in repo.list_steps(task.id)}
if apply_disabled_step_fallbacks(state, current_task, repo):
continue
step_name, waiting_payload = next_runnable_step(current_task, current_steps, state)
if waiting_payload is not None:
processed.append(waiting_payload)
return {"processed": processed}
if step_name is None:
break
payload = execute_step(state, task.id, step_name)
if current_task.status == "failed_retryable":
payload["retry"] = True
record_task_action(repo, task_id, step_name, "ok", f"{step_name} retry succeeded", payload)
else:
record_task_action(repo, task_id, step_name, "ok", f"{step_name} succeeded", payload)
processed.append(payload)
except ModuleError as exc:
failure = resolve_failure(task, repo, state, exc)
processed.append(failure["payload"])
record_task_action(repo, task_id, failure["step_name"], "error", failure["summary"], failure["payload"])
return {"processed": processed}