"""视频:业务规则(文件落盘 + 路径写入 videos 表)。""" from __future__ import annotations import json import os import sys from typing import Any, Dict, Optional from content_manager.config import get_skill_data_dir, resolve_stored_path from content_manager.db import videos_repository as vr from content_manager.db.connection import get_conn, init_db from content_manager.services import file_store from content_manager.util.timeutil import now_unix, unix_to_iso def _row_to_public_dict(row: tuple) -> Dict[str, Any]: rid, file_path, title, duration_ms, status, source, account_id, error_msg, extra_json, cat, uat = row abs_path = resolve_stored_path(str(file_path)) d: Dict[str, Any] = { "id": int(rid), "kind": "video", "file_path": file_path, "absolute_path": abs_path, "title": title, "duration_ms": duration_ms, "status": status or "draft", "source": source or "manual", "account_id": account_id, "error_msg": error_msg, "created_at": unix_to_iso(cat), "updated_at": unix_to_iso(uat), } if extra_json: try: ex = json.loads(extra_json) if isinstance(ex, dict): d["extra"] = ex except json.JSONDecodeError: pass return d def cmd_add( src_file: str, title: Optional[str] = None, duration_ms: Optional[int] = None, source: str = "manual", ) -> None: init_db() src_file = os.path.abspath(src_file.strip()) if not os.path.isfile(src_file): print(f"❌ 找不到文件:{src_file}") sys.exit(1) skill_data = get_skill_data_dir() ts = now_unix() conn = get_conn() try: new_id = vr.insert_row( conn, file_path="", title=(title or "").strip() or None, duration_ms=duration_ms, status="draft", source=source, account_id=None, error_msg=None, extra_json=None, created_at=ts, updated_at=ts, ) rel, _abs = file_store.copy_into_skill_data(skill_data, "videos", new_id, src_file) vr.update_file_path(conn, new_id, rel, now_unix()) conn.commit() except Exception: conn.rollback() raise finally: conn.close() print(f"✅ 已新增视频 id={new_id} | 路径:{rel}") def cmd_get(video_id: str) -> None: init_db() if not str(video_id).strip().isdigit(): print("❌ 视频 id 必须是纯数字。请先 video list 查看。") sys.exit(1) vid = int(video_id) conn = get_conn() try: row = vr.fetch_by_id(conn, vid) finally: conn.close() if not row: print("❌ 没有这条视频记录。") sys.exit(1) print(json.dumps(_row_to_public_dict(row), ensure_ascii=False)) def cmd_list(limit: int = 20, max_chars: int = 80) -> None: init_db() conn = get_conn() try: rows = vr.list_recent(conn, limit) finally: conn.close() if not rows: print("暂无视频") return def trunc(s: str) -> str: if not s: return "" return s if len(s) <= max_chars else s[:max_chars] + "..." sep = "_" * 39 for idx, r in enumerate(rows): rid, file_path, title, duration_ms, status, source, account_id, error_msg, extra_json, cat, uat = r print(f"id:{rid}") print(f"file_path:{trunc(str(file_path or ''))}") print(f"title:{title or ''}") print(f"duration_ms:{duration_ms if duration_ms is not None else ''}") print(f"status:{status or ''}") print(f"source:{source or ''}") print(f"account_id:{account_id or ''}") print(f"error_msg:{error_msg or ''}") print(f"created_at:{unix_to_iso(cat) or ''}") print(f"updated_at:{unix_to_iso(uat) or ''}") if idx != len(rows) - 1: print(sep) print() def cmd_delete(video_id: str) -> None: init_db() if not str(video_id).strip().isdigit(): print("❌ 视频 id 必须是纯数字。") sys.exit(1) vid = int(video_id) skill_data = get_skill_data_dir() conn = get_conn() try: row = vr.fetch_by_id(conn, vid) if not row: print("❌ 没有 id 为 {} 的视频记录。".format(vid)) sys.exit(1) rel = row[1] n = vr.delete_by_id(conn, vid) if n == 0: sys.exit(1) conn.commit() finally: conn.close() file_store.remove_files_for_relative_path(skill_data, str(rel)) print(f"✅ 已删除视频 id={vid}") def cmd_feedback( video_id: str, status: str, account_id: Optional[str] = None, error_msg: Optional[str] = None, ) -> None: init_db() if not str(video_id).strip().isdigit(): print("❌ 视频 id 必须是纯数字。") sys.exit(1) vid = int(video_id) ts = now_unix() conn = get_conn() try: if vr.fetch_by_id(conn, vid) is None: print("❌ 没有 id 为 {} 的视频记录。".format(vid)) sys.exit(1) vr.update_feedback(conn, vid, status, account_id, error_msg, ts) conn.commit() finally: conn.close() print("✅ 状态已更新")