File size: 5,070 Bytes
37c8161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""

任务历史持久化:按 task_id 记录 image_url、audio_url、result_video_url、status、created_at 等,媒体以 URL 保存。

"""
import json
from loguru import logger
import os
import threading
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Optional


# 默认历史文件路径:/mnt/workspace/task_history.json(创空间持久卷)。
# 本地开发等无 /mnt/workspace 的环境,请通过环境变量 TASK_HISTORY_PATH 覆盖,
# 例如 TASK_HISTORY_PATH=./task_history.json。
_DEFAULT_PATH = Path("/mnt/workspace/task_history.json")
_lock = threading.Lock()


def _path() -> Path:
    return Path(os.environ.get("TASK_HISTORY_PATH", str(_DEFAULT_PATH)))


def _load_raw() -> list[dict[str, Any]]:
    """加载原始列表(调用方需已持有 _lock)。"""
    p = _path()
    if not p.exists():
        return []
    try:
        with open(p, "r", encoding="utf-8") as f:
            data = json.load(f)
        return data.get("tasks", [])
    except (json.JSONDecodeError, OSError) as e:
        logger.warning("加载任务历史失败 path={} error={}", p, e)
        return []


def _save_raw(tasks: list[dict[str, Any]]) -> None:
    """写入列表(调用方需已持有 _lock)。"""
    p = _path()
    p.parent.mkdir(parents=True, exist_ok=True)
    with open(p, "w", encoding="utf-8") as f:
        json.dump({"tasks": tasks}, f, ensure_ascii=False, indent=2)


def load_history(owner_id: Optional[str] = None) -> list[dict[str, Any]]:
    """加载历史记录,返回按创建时间倒序的列表(最近在前)。



    :param owner_id:

        - None: 不过滤,返回全部记录(管理员视角,本地调试或脚本里使用)。

        - 非 None(含空字符串): 仅返回 owner_id 严格相等的记录。

          老记录(无 owner_id 字段,等价于 None)会被规范成空字符串后再比对,

          所以匿名访客(无 router_id,owner_id="")只会看到老记录或同样匿名的记录。

    """
    with _lock:
        tasks = _load_raw()
        tasks.sort(key=lambda t: t.get("created_at", ""), reverse=True)
        if owner_id is not None:
            tasks = [t for t in tasks if (t.get("owner_id") or "") == owner_id]
        return tasks


def add_task(

    task_id: str,

    image_url: str,

    audio_url: str,

    status: str = "PENDING",

    result_video_url: Optional[str] = None,

    message: Optional[str] = None,

    audio_duration: Optional[float] = None,

    dance_genres: Optional[str] = None,

    image_local_path: Optional[str] = None,

    audio_local_path: Optional[str] = None,

    owner_id: Optional[str] = None,

) -> None:
    """追加一条任务记录。



    :param owner_id: 用户身份标识(创空间下为 X-Modelscope-Router-Id 的值)。

        历史页按此字段过滤展示,确保用户只看到自己的任务。

    """
    created_at = datetime.now(timezone.utc).isoformat()
    entry = {
        "task_id": task_id,
        "image_url": image_url,
        "audio_url": audio_url,
        "result_video_url": result_video_url,
        "status": status,
        "created_at": created_at,
        "message": message,
        "audio_duration": audio_duration,
        "dance_genres": dance_genres,
        "image_local_path": image_local_path,
        "audio_local_path": audio_local_path,
        "video_local_path": None,
        "owner_id": owner_id or "",
    }
    with _lock:
        tasks = _load_raw()
        tasks = [t for t in tasks if t.get("task_id") != task_id]
        tasks.insert(0, entry)
        _save_raw(tasks)
    logger.debug("历史已添加 task_id={}", task_id)


def update_task(

    task_id: str,

    *,

    result_video_url: Optional[str] = None,

    status: Optional[str] = None,

    message: Optional[str] = None,

    video_local_path: Optional[str] = None,

) -> None:
    """按 task_id 更新任务记录(只更新传入的字段)。"""
    with _lock:
        tasks = _load_raw()
        for t in tasks:
            if t.get("task_id") == task_id:
                if result_video_url is not None:
                    t["result_video_url"] = result_video_url
                if status is not None:
                    t["status"] = status
                if message is not None:
                    t["message"] = message
                if video_local_path is not None:
                    t["video_local_path"] = video_local_path
                _save_raw(tasks)
                logger.debug("历史已更新 task_id={} status={}", task_id, status)
                return
    logger.warning("更新历史时未找到 task_id={}", task_id)


def get_task(task_id: str) -> Optional[dict[str, Any]]:
    """按 task_id 返回一条任务记录,不存在则返回 None。"""
    tasks = load_history()
    for t in tasks:
        if t.get("task_id") == task_id:
            return t
    return None