init biliup-next
This commit is contained in:
64
docs/adr/0001-modular-monolith.md
Normal file
64
docs/adr/0001-modular-monolith.md
Normal file
@ -0,0 +1,64 @@
|
||||
# ADR 0001: Use A Modular Monolith As The Target Architecture
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
当前项目由多个 Python 脚本、目录扫描逻辑、flag 文件和外部命令拼接而成。
|
||||
|
||||
主要问题:
|
||||
|
||||
- 状态不统一
|
||||
- 配置不统一
|
||||
- 重复逻辑多
|
||||
- 扩展新功能需要继续增加脚本
|
||||
- 运维和业务边界不清晰
|
||||
|
||||
重构目标要求:
|
||||
|
||||
- 可扩展
|
||||
- 可配置
|
||||
- 可观测
|
||||
- 易部署
|
||||
- 易文档化
|
||||
|
||||
## Decision
|
||||
|
||||
新系统采用模块化单体架构,而不是:
|
||||
|
||||
- 继续维护脚本集合
|
||||
- 直接拆成微服务
|
||||
|
||||
## Rationale
|
||||
|
||||
选择模块化单体的原因:
|
||||
|
||||
- 当前系统规模和团队协作模式不需要微服务
|
||||
- 单机部署和本地运维是核心需求
|
||||
- 统一数据库、配置和日志对当前问题最直接有效
|
||||
- 模块化单体足以提供清晰边界和未来插件扩展能力
|
||||
|
||||
## Consequences
|
||||
|
||||
正面影响:
|
||||
|
||||
- 部署简单
|
||||
- 重构成本可控
|
||||
- 便于引入统一状态机和管理 API
|
||||
- 后续可以逐步插件化
|
||||
|
||||
负面影响:
|
||||
|
||||
- 需要严格维持模块边界,避免重新长成“大脚本”
|
||||
- 单进程内错误隔离不如微服务天然
|
||||
|
||||
## Follow-Up Decisions
|
||||
|
||||
后续还需要补充的 ADR:
|
||||
|
||||
- 是否使用 SQLite 作为主状态存储
|
||||
- 是否引入事件总线
|
||||
- 插件机制如何注册
|
||||
- 管理台采用什么技术栈
|
||||
245
docs/api/openapi.yaml
Normal file
245
docs/api/openapi.yaml
Normal file
@ -0,0 +1,245 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: biliup-next Control API
|
||||
version: 0.1.0
|
||||
summary: 本地 worker、任务和控制台 API
|
||||
servers:
|
||||
- url: http://127.0.0.1:8787
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
summary: 控制台首页
|
||||
responses:
|
||||
"200":
|
||||
description: HTML dashboard
|
||||
/health:
|
||||
get:
|
||||
summary: 健康检查
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
/settings:
|
||||
get:
|
||||
summary: 获取当前设置
|
||||
responses:
|
||||
"200":
|
||||
description: 当前配置,敏感字段已掩码
|
||||
put:
|
||||
summary: 更新设置
|
||||
responses:
|
||||
"200":
|
||||
description: 保存成功
|
||||
/settings/schema:
|
||||
get:
|
||||
summary: 获取 settings schema
|
||||
responses:
|
||||
"200":
|
||||
description: schema-first UI 元数据
|
||||
/doctor:
|
||||
get:
|
||||
summary: 运行时依赖检查
|
||||
responses:
|
||||
"200":
|
||||
description: doctor result
|
||||
/modules:
|
||||
get:
|
||||
summary: 查询已注册模块与 manifest
|
||||
responses:
|
||||
"200":
|
||||
description: module list
|
||||
/runtime/services:
|
||||
get:
|
||||
summary: 查询 systemd 服务状态
|
||||
responses:
|
||||
"200":
|
||||
description: service list
|
||||
/runtime/services/{serviceId}/{action}:
|
||||
post:
|
||||
summary: 执行 service start/stop/restart
|
||||
parameters:
|
||||
- in: path
|
||||
name: serviceId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: action
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [start, stop, restart]
|
||||
responses:
|
||||
"202":
|
||||
description: action accepted
|
||||
/logs:
|
||||
get:
|
||||
summary: 查询日志列表或日志内容
|
||||
parameters:
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: lines
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: contains
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: log list or log content
|
||||
/history:
|
||||
get:
|
||||
summary: 查询全局动作流
|
||||
parameters:
|
||||
- in: query
|
||||
name: task_id
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: action_name
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: status
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: action records
|
||||
/tasks:
|
||||
get:
|
||||
summary: 查询任务列表
|
||||
parameters:
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: task list
|
||||
post:
|
||||
summary: 从 source_path 手动创建任务
|
||||
responses:
|
||||
"201":
|
||||
description: task created
|
||||
/tasks/{taskId}:
|
||||
get:
|
||||
summary: 查询任务详情
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: task detail
|
||||
/tasks/{taskId}/steps:
|
||||
get:
|
||||
summary: 查询任务步骤
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: task steps
|
||||
/tasks/{taskId}/artifacts:
|
||||
get:
|
||||
summary: 查询任务产物
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: task artifacts
|
||||
/tasks/{taskId}/history:
|
||||
get:
|
||||
summary: 查询单任务动作历史
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: task action history
|
||||
/tasks/{taskId}/timeline:
|
||||
get:
|
||||
summary: 查询单任务时间线
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: task timeline
|
||||
/tasks/{taskId}/actions/run:
|
||||
post:
|
||||
summary: 推进单个任务
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"202":
|
||||
description: accepted
|
||||
/tasks/{taskId}/actions/retry-step:
|
||||
post:
|
||||
summary: 从指定 step 重试
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"202":
|
||||
description: accepted
|
||||
/tasks/{taskId}/actions/reset-to-step:
|
||||
post:
|
||||
summary: 重置到指定 step 并重跑
|
||||
parameters:
|
||||
- in: path
|
||||
name: taskId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"202":
|
||||
description: accepted
|
||||
/worker/run-once:
|
||||
post:
|
||||
summary: 执行一轮 worker
|
||||
responses:
|
||||
"202":
|
||||
description: accepted
|
||||
/stage/import:
|
||||
post:
|
||||
summary: 从本机已有绝对路径复制到隔离 stage
|
||||
responses:
|
||||
"201":
|
||||
description: imported
|
||||
/stage/upload:
|
||||
post:
|
||||
summary: 上传文件到隔离 stage
|
||||
responses:
|
||||
"201":
|
||||
description: uploaded
|
||||
198
docs/architecture.md
Normal file
198
docs/architecture.md
Normal file
@ -0,0 +1,198 @@
|
||||
# Architecture
|
||||
|
||||
## Architecture Style
|
||||
|
||||
采用模块化单体架构。
|
||||
|
||||
原因:
|
||||
|
||||
- 当前规模不需要微服务
|
||||
- 需要统一配置、状态和任务模型
|
||||
- 需要较低部署复杂度
|
||||
- 需要明确模块边界和未来插件扩展能力
|
||||
|
||||
## High-Level Layers
|
||||
|
||||
### 1. Core
|
||||
|
||||
核心领域层,不依赖具体外部服务。
|
||||
|
||||
- 领域模型
|
||||
- 状态机
|
||||
- 任务编排接口
|
||||
- 事件定义
|
||||
- 配置模型
|
||||
|
||||
### 2. Modules
|
||||
|
||||
业务模块层,每个模块只关心自己的一步能力。
|
||||
|
||||
- `ingest`
|
||||
- `transcribe`
|
||||
- `song_detect`
|
||||
- `split`
|
||||
- `publish`
|
||||
- `comment`
|
||||
- `collection`
|
||||
|
||||
### 3. Infra
|
||||
|
||||
基础设施层,对外部依赖做适配。
|
||||
|
||||
- 文件系统
|
||||
- SQLite 存储
|
||||
- Groq adapter
|
||||
- Codex adapter
|
||||
- FFmpeg adapter
|
||||
- Bili API adapter
|
||||
- biliup adapter
|
||||
- 日志与审计
|
||||
|
||||
### 4. App
|
||||
|
||||
应用层,对外暴露统一运行入口。
|
||||
|
||||
- API Server
|
||||
- Worker
|
||||
- Scheduler
|
||||
- CLI
|
||||
- Admin Web
|
||||
|
||||
## Proposed Directory Layout
|
||||
|
||||
```text
|
||||
biliup-next/
|
||||
src/
|
||||
app/
|
||||
api/
|
||||
worker/
|
||||
scheduler/
|
||||
cli/
|
||||
core/
|
||||
models/
|
||||
services/
|
||||
events/
|
||||
state_machine/
|
||||
config/
|
||||
modules/
|
||||
ingest/
|
||||
transcribe/
|
||||
song_detect/
|
||||
split/
|
||||
publish/
|
||||
comment/
|
||||
collection/
|
||||
infra/
|
||||
db/
|
||||
fs/
|
||||
adapters/
|
||||
groq/
|
||||
codex/
|
||||
ffmpeg/
|
||||
bili/
|
||||
biliup/
|
||||
logging/
|
||||
plugins/
|
||||
docs/
|
||||
tests/
|
||||
```
|
||||
|
||||
## Runtime Components
|
||||
|
||||
### API Server
|
||||
|
||||
负责:
|
||||
|
||||
- 配置管理
|
||||
- 任务查询
|
||||
- 手动操作
|
||||
- 日志聚合查询
|
||||
- 模块与插件可见性展示
|
||||
|
||||
### Worker
|
||||
|
||||
负责:
|
||||
|
||||
- 消费任务
|
||||
- 推进状态机
|
||||
- 执行模块步骤
|
||||
|
||||
### Scheduler
|
||||
|
||||
负责:
|
||||
|
||||
- 定时扫描待补偿任务
|
||||
- 定时同步外部状态
|
||||
- 触发重试
|
||||
|
||||
## Control Plane
|
||||
|
||||
新系统应明确区分控制面和数据面。
|
||||
|
||||
### Control Plane
|
||||
|
||||
负责:
|
||||
|
||||
- 配置管理
|
||||
- 模块/插件注册
|
||||
- 任务可视化
|
||||
- 手动操作入口
|
||||
- 日志与诊断
|
||||
|
||||
### Data Plane
|
||||
|
||||
负责:
|
||||
|
||||
- 实际执行转录
|
||||
- 实际执行识歌
|
||||
- 实际执行切歌
|
||||
- 实际执行上传
|
||||
- 实际执行评论和合集归档
|
||||
|
||||
## Registry
|
||||
|
||||
系统内部建立统一 registry,用于注册和查找模块能力。
|
||||
|
||||
例如:
|
||||
|
||||
- 当前转录 provider
|
||||
- 当前识歌 provider
|
||||
- 当前上传 provider
|
||||
- 当前合集策略
|
||||
|
||||
核心模块只依赖抽象接口和 registry,不直接依赖具体实现。
|
||||
|
||||
## Task Lifecycle
|
||||
|
||||
```text
|
||||
created
|
||||
-> ingested
|
||||
-> transcribed
|
||||
-> songs_detected
|
||||
-> split_done
|
||||
-> published
|
||||
-> commented
|
||||
-> collection_synced
|
||||
-> completed
|
||||
```
|
||||
|
||||
失败状态不结束任务,而是转入:
|
||||
|
||||
- `failed_retryable`
|
||||
- `failed_manual`
|
||||
|
||||
## Data Ownership
|
||||
|
||||
- SQLite:任务、步骤、产物索引、配置、审计记录
|
||||
- 文件系统:视频、字幕、切片、AI 输出、日志
|
||||
- 外部平台:B 站稿件、评论、合集
|
||||
|
||||
## Key Design Rules
|
||||
|
||||
- 所有状态变更必须落库
|
||||
- 模块间只通过领域对象和事件通信
|
||||
- 外部依赖不可直接在业务模块中调用 shell 或 HTTP
|
||||
- 配置统一由 `core.config` 读取
|
||||
- 管理端展示的数据优先来自数据库,不直接从日志推断
|
||||
- 配置系统必须 schema-first
|
||||
- 插件系统必须 manifest-first
|
||||
193
docs/config-system.md
Normal file
193
docs/config-system.md
Normal file
@ -0,0 +1,193 @@
|
||||
# Config System
|
||||
|
||||
## Design Goal
|
||||
|
||||
配置系统不是辅助功能,而是新系统的控制面基础设施。
|
||||
|
||||
目标:
|
||||
|
||||
- 所有运行参数有统一来源
|
||||
- 配置可被严格校验
|
||||
- 配置可被 UI、CLI、文件三种方式修改
|
||||
- 配置变更可审计
|
||||
- 无效配置不得直接污染运行态
|
||||
|
||||
## Design Principles
|
||||
|
||||
借鉴 OpenClaw 的做法,配置系统采用 `schema-first` 设计。
|
||||
|
||||
核心原则:
|
||||
|
||||
- 所有配置项必须先声明在 schema 中
|
||||
- 所有模块只能通过配置服务读取配置
|
||||
- UI 表单由 schema 驱动渲染
|
||||
- 保存前必须校验
|
||||
- 校验失败不允许生效
|
||||
- 配置有版本和变更记录
|
||||
|
||||
## Config Sources
|
||||
|
||||
### 1. Default Config
|
||||
|
||||
系统默认值,由代码内置或默认配置文件提供。
|
||||
|
||||
### 2. Active Config
|
||||
|
||||
当前生效的正式配置。
|
||||
|
||||
建议位置:
|
||||
|
||||
- `biliup-next/config/settings.json`
|
||||
|
||||
### 3. Staged Config
|
||||
|
||||
用户在 UI 或 CLI 中提交、但尚未生效的待校验配置。
|
||||
|
||||
建议位置:
|
||||
|
||||
- `biliup-next/config/settings.staged.json`
|
||||
|
||||
### 4. Schema
|
||||
|
||||
定义配置结构、类型、默认值、枚举范围、校验规则和 UI 元信息。
|
||||
|
||||
建议位置:
|
||||
|
||||
- `biliup-next/config/settings.schema.json`
|
||||
|
||||
## Config Flow
|
||||
|
||||
```text
|
||||
User edits config
|
||||
-> Write staged config
|
||||
-> Validate against schema
|
||||
-> Run dependency checks
|
||||
-> If valid: promote to active
|
||||
-> If invalid: keep current active config
|
||||
```
|
||||
|
||||
## Validation Layers
|
||||
|
||||
### 1. Schema Validation
|
||||
|
||||
检查:
|
||||
|
||||
- 类型
|
||||
- 必填字段
|
||||
- 枚举值
|
||||
- 数值范围
|
||||
- 字段格式
|
||||
|
||||
### 2. Semantic Validation
|
||||
|
||||
检查:
|
||||
|
||||
- 路径是否存在
|
||||
- 可执行文件是否可用
|
||||
- season id 是否合法
|
||||
- 依赖组合是否冲突
|
||||
|
||||
### 3. Runtime Validation
|
||||
|
||||
检查:
|
||||
|
||||
- provider 是否可初始化
|
||||
- 凭证是否存在
|
||||
- 外部连接是否可用
|
||||
|
||||
## Suggested Config Groups
|
||||
|
||||
### runtime
|
||||
|
||||
- `workspace_dir`
|
||||
- `database_path`
|
||||
- `log_level`
|
||||
- `scan_interval_seconds`
|
||||
|
||||
### paths
|
||||
|
||||
- `stage_dir`
|
||||
- `backup_dir`
|
||||
- `session_dir`
|
||||
- `cookies_file`
|
||||
- `upload_config_file`
|
||||
|
||||
### ingest
|
||||
|
||||
- `min_duration_seconds`
|
||||
- `allowed_extensions`
|
||||
|
||||
### transcribe
|
||||
|
||||
- `provider`
|
||||
- `groq_api_key`
|
||||
- `max_file_size_mb`
|
||||
- `ffmpeg_bin`
|
||||
|
||||
### song_detect
|
||||
|
||||
- `provider`
|
||||
- `codex_cmd`
|
||||
- `poll_interval_seconds`
|
||||
|
||||
### split
|
||||
|
||||
- `ffmpeg_bin`
|
||||
- `poll_interval_seconds`
|
||||
|
||||
### publish
|
||||
|
||||
- `provider`
|
||||
- `biliup_path`
|
||||
- `cookie_file`
|
||||
- `retry_count`
|
||||
- `retry_schedule_minutes`
|
||||
- `retry_backoff_seconds`
|
||||
|
||||
### comment
|
||||
|
||||
- `enabled`
|
||||
- `max_retries`
|
||||
- `base_delay_seconds`
|
||||
- `poll_interval_seconds`
|
||||
|
||||
### collection
|
||||
|
||||
- `enabled`
|
||||
- `season_id_a`
|
||||
- `season_id_b`
|
||||
- `allow_fuzzy_full_video_match`
|
||||
- `append_collection_a_new_to_end`
|
||||
- `append_collection_b_new_to_end`
|
||||
|
||||
## UI Strategy
|
||||
|
||||
管理台不手写业务表单,而是由 schema 驱动生成配置界面。
|
||||
|
||||
每个配置项除了类型信息,还应有:
|
||||
|
||||
- `title`
|
||||
- `description`
|
||||
- `group`
|
||||
- `ui:widget`
|
||||
- `ui:secret`
|
||||
- `ui:order`
|
||||
|
||||
这样可以让配置页面和底层配置保持同源。
|
||||
|
||||
## Audit Trail
|
||||
|
||||
每次配置变更都应记录:
|
||||
|
||||
- 修改人
|
||||
- 修改时间
|
||||
- 修改前值
|
||||
- 修改后值
|
||||
- 校验结果
|
||||
- 是否已生效
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 不追求兼容任意格式的配置文件
|
||||
- 不允许模块私自定义一份独立配置入口
|
||||
- 不允许 UI 和代码维护两套不同的字段定义
|
||||
476
docs/control-plane-guide.md
Normal file
476
docs/control-plane-guide.md
Normal file
@ -0,0 +1,476 @@
|
||||
# Control Plane Guide
|
||||
|
||||
本文档面向 `biliup-next` 控制台的日常使用。
|
||||
|
||||
默认地址:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8787/
|
||||
```
|
||||
|
||||
如果当前机器已经开放公网访问,也可以使用服务器 IP + `8787` 端口访问。
|
||||
|
||||
## 页面分区
|
||||
|
||||
控制台主要分成两列:
|
||||
|
||||
- 左侧:全局状态、服务、动作流、导入入口、任务列表、Settings
|
||||
- 右侧:当前任务详情、步骤、产物、历史、时间线、模块、日志
|
||||
|
||||
建议的使用顺序是:
|
||||
|
||||
1. 先看 `Runtime`
|
||||
2. 再看 `Tasks`
|
||||
3. 选中一个任务后,看右侧详情
|
||||
4. 如果任务异常,再看 `Logs` 和 `History`
|
||||
|
||||
## Runtime
|
||||
|
||||
这里可以看 3 个汇总指标:
|
||||
|
||||
- `Health`
|
||||
- `Doctor`
|
||||
- `Tasks`
|
||||
|
||||
含义:
|
||||
|
||||
- `Health = OK`
|
||||
- API 服务本身还活着
|
||||
- `Doctor = OK`
|
||||
- 关键路径、二进制和依赖文件都存在
|
||||
- `Tasks`
|
||||
- 当前数据库里的任务数
|
||||
|
||||
如果 `Doctor` 不是 `OK`,优先不要继续点任务操作,先修运行环境。
|
||||
|
||||
## Services
|
||||
|
||||
这里可以查看并控制:
|
||||
|
||||
- `biliup-next-worker.service`
|
||||
- `biliup-next-api.service`
|
||||
- 如果还保留旧服务,也可能看到 `biliup-python.service`
|
||||
|
||||
可执行操作:
|
||||
|
||||
- `start`
|
||||
- `restart`
|
||||
- `stop`
|
||||
|
||||
建议:
|
||||
|
||||
- 页面打不开时,先看 `biliup-next-api.service`
|
||||
- 任务不推进时,先看 `biliup-next-worker.service`
|
||||
- 不要随便再启动旧 `biliup-python.service`,除非你明确知道自己要同时跑旧链路
|
||||
|
||||
## Recent Actions
|
||||
|
||||
这里显示最近的控制面动作流,例如:
|
||||
|
||||
- `worker_run_once`
|
||||
- `task_run`
|
||||
- `retry_step`
|
||||
- `reset_to_step`
|
||||
- `stage_import`
|
||||
- `stage_upload`
|
||||
- `service_action`
|
||||
|
||||
可过滤:
|
||||
|
||||
- 仅当前任务
|
||||
- `status`
|
||||
- `action_name`
|
||||
|
||||
用途:
|
||||
|
||||
- 判断最近有没有人工操作过任务
|
||||
- 判断任务是不是被重试过
|
||||
- 判断服务是不是被重启过
|
||||
|
||||
## Import To Stage
|
||||
|
||||
这里有两种入口。
|
||||
|
||||
### 1. 复制本机已有文件到隔离 stage
|
||||
|
||||
输入服务器上的绝对路径,例如:
|
||||
|
||||
```text
|
||||
/home/theshy/video/test.mp4
|
||||
```
|
||||
|
||||
点击:
|
||||
|
||||
```text
|
||||
复制到隔离 Stage
|
||||
```
|
||||
|
||||
适合:
|
||||
|
||||
- 服务器本地已有文件
|
||||
- 想快速把已有文件丢进新系统测试
|
||||
|
||||
### 2. 浏览器直接上传文件
|
||||
|
||||
选择本地文件后点击:
|
||||
|
||||
```text
|
||||
上传到隔离 Stage
|
||||
```
|
||||
|
||||
适合:
|
||||
|
||||
- 本地电脑上的测试视频
|
||||
- 不想先手动传到服务器
|
||||
|
||||
上传成功后,`worker` 会自动扫描 `stage` 并开始建任务。
|
||||
|
||||
## Tasks
|
||||
|
||||
这里列出任务列表。
|
||||
|
||||
每个任务会显示:
|
||||
|
||||
- 标题
|
||||
- 当前状态
|
||||
- 任务 ID
|
||||
|
||||
常见状态:
|
||||
|
||||
- `created`
|
||||
- `transcribed`
|
||||
- `songs_detected`
|
||||
- `split_done`
|
||||
- `published`
|
||||
- `commented`
|
||||
- `collection_synced`
|
||||
- `failed_retryable`
|
||||
- `failed_manual`
|
||||
|
||||
建议:
|
||||
|
||||
- 先选中你关心的任务
|
||||
- 再看右侧 `Task Detail / Steps / Logs`
|
||||
|
||||
## Task Detail
|
||||
|
||||
显示当前选中任务的核心信息:
|
||||
|
||||
- `Task ID`
|
||||
- `Status`
|
||||
- `Title`
|
||||
- `Source`
|
||||
- `Created`
|
||||
- `Updated`
|
||||
|
||||
上方有 3 个操作按钮:
|
||||
|
||||
- `执行当前任务`
|
||||
- `重试选中 Step`
|
||||
- `重置到选中 Step`
|
||||
|
||||
### 执行当前任务
|
||||
|
||||
作用:
|
||||
|
||||
- 手动推进当前任务一次
|
||||
|
||||
适合:
|
||||
|
||||
- 你刚改完配置
|
||||
- 你不想等下一轮 worker 轮询
|
||||
|
||||
### 重试选中 Step
|
||||
|
||||
作用:
|
||||
|
||||
- 从当前选中的 step 重新尝试
|
||||
|
||||
适合:
|
||||
|
||||
- 某一步是临时失败
|
||||
- 不需要删除后续产物
|
||||
|
||||
### 重置到选中 Step
|
||||
|
||||
作用:
|
||||
|
||||
- 清理该 step 之后的产物和标记
|
||||
- 把任务回拨到该 step
|
||||
- 然后重新执行
|
||||
|
||||
适合:
|
||||
|
||||
- 后续结果已经不可信
|
||||
- 需要从某一步重新跑整段链路
|
||||
|
||||
注意:
|
||||
|
||||
- 这是破坏性动作
|
||||
- 页面会要求确认
|
||||
|
||||
## Steps
|
||||
|
||||
这里显示任务步骤列表,例如:
|
||||
|
||||
- `ingest`
|
||||
- `transcribe`
|
||||
- `song_detect`
|
||||
- `split`
|
||||
- `publish`
|
||||
- `comment`
|
||||
- `collection_a`
|
||||
- `collection_b`
|
||||
|
||||
点击某个 step 之后:
|
||||
|
||||
- 这个 step 会成为“当前选中 step”
|
||||
- 然后你就可以点:
|
||||
- `重试选中 Step`
|
||||
- `重置到选中 Step`
|
||||
|
||||
排查原则:
|
||||
|
||||
- `transcribe` 失败:先看 `Groq API Key`、`ffmpeg`
|
||||
- `song_detect` 失败:先看 `codex_cmd`
|
||||
- `publish` 失败:先看 `cookies.json`、`biliup`
|
||||
- `collection_*` 失败:再看任务历史和日志
|
||||
|
||||
评论规则补充:
|
||||
|
||||
- `comment`
|
||||
- 纯享版视频下默认发“编号歌单”,不带时间轴
|
||||
- 完整版主视频下默认才发“带时间轴评论”
|
||||
- 如果当前任务找不到 `full_video_bvid.txt`,也没能从最近发布列表解析出完整版 BV,主视频评论会跳过
|
||||
|
||||
## Artifacts
|
||||
|
||||
这里显示任务当前已经产出的文件,例如:
|
||||
|
||||
- `source_video`
|
||||
- `subtitle_srt`
|
||||
- `songs_json`
|
||||
- `songs_txt`
|
||||
- `clip_video`
|
||||
- `publish_bvid`
|
||||
|
||||
用途:
|
||||
|
||||
- 判断任务跑到了哪一步
|
||||
- 判断关键输出是否已经落盘
|
||||
|
||||
如果开启了 cleanup 配置,任务在 `collection_synced` 后:
|
||||
|
||||
- `source_video` 对应的原始视频可能会被删除
|
||||
- `clip_video` 对应的 `split_video/` 目录也可能被清理
|
||||
|
||||
这是正常收尾行为,不代表任务失败。
|
||||
|
||||
## History
|
||||
|
||||
这里是单任务动作历史。
|
||||
|
||||
和 `Recent Actions` 的区别:
|
||||
|
||||
- `Recent Actions` 看全局
|
||||
- `History` 只看当前任务
|
||||
|
||||
可以看到:
|
||||
|
||||
- 动作名
|
||||
- 状态
|
||||
- 摘要
|
||||
- 时间
|
||||
- 结构化 `details_json`
|
||||
|
||||
用途:
|
||||
|
||||
- 判断某次 `retry` 或 `reset` 的结果
|
||||
- 判断 worker 最近对这个任务做了什么
|
||||
|
||||
## Timeline
|
||||
|
||||
这里把任务事件串成一条时间线:
|
||||
|
||||
- `Task Created`
|
||||
- step started / finished
|
||||
- artifact created
|
||||
- action records
|
||||
|
||||
适合:
|
||||
|
||||
- 回放任务完整过程
|
||||
- 查清楚任务到底卡在什么时候
|
||||
|
||||
## Modules
|
||||
|
||||
这里显示当前注册的模块 / provider。
|
||||
|
||||
用途:
|
||||
|
||||
- 确认当前用的是哪套 provider
|
||||
- 确认模块有没有注册成功
|
||||
|
||||
如果将来切 provider,这里会很有用。
|
||||
|
||||
## Doctor Checks
|
||||
|
||||
这里比顶部的 `Doctor = OK/FAIL` 更详细。
|
||||
|
||||
会列出:
|
||||
|
||||
- workspace 目录
|
||||
- `cookies_file`
|
||||
- `upload_config_file`
|
||||
- `ffprobe`
|
||||
- `ffmpeg`
|
||||
- `codex_cmd`
|
||||
- `biliup_path`
|
||||
|
||||
如果某个依赖显示 `(external)`,表示它还在用系统或父项目路径,不是 `biliup-next` 自己目录内的副本。
|
||||
|
||||
## Logs
|
||||
|
||||
这里可以看日志文件。
|
||||
|
||||
支持:
|
||||
|
||||
- 切换日志文件
|
||||
- 刷新日志
|
||||
- 按当前任务标题过滤
|
||||
|
||||
使用建议:
|
||||
|
||||
- 任务异常时,先选中任务
|
||||
- 再勾选“按当前任务标题过滤”
|
||||
- 然后查看相关日志
|
||||
|
||||
这样比直接翻整份日志快很多。
|
||||
|
||||
## Settings
|
||||
|
||||
Settings 分成两层:
|
||||
|
||||
- 上半部分:schema 驱动表单
|
||||
- 下半部分:`Advanced JSON Editor`
|
||||
|
||||
### 表单区
|
||||
|
||||
这里适合日常参数调整,例如:
|
||||
|
||||
- `min_duration_seconds`
|
||||
- `groq_api_key`
|
||||
- `codex_cmd`
|
||||
- `retry_count`
|
||||
- `season_id_a`
|
||||
- `season_id_b`
|
||||
- `post_split_comment`
|
||||
- `post_full_video_timeline_comment`
|
||||
- `delete_source_video_after_collection_synced`
|
||||
- `delete_split_videos_after_collection_synced`
|
||||
|
||||
支持:
|
||||
|
||||
- 搜索配置项
|
||||
- 高频参数优先展示
|
||||
- 低频参数收纳到 `Advanced Settings`
|
||||
|
||||
### Advanced JSON Editor
|
||||
|
||||
适合:
|
||||
|
||||
- 批量调整
|
||||
- 一次改多个字段
|
||||
- 需要直接编辑原始 JSON
|
||||
|
||||
页面上有两个同步按钮:
|
||||
|
||||
- `表单同步到 JSON`
|
||||
- `JSON 重绘表单`
|
||||
|
||||
### 敏感字段规则
|
||||
|
||||
敏感字段会显示为:
|
||||
|
||||
```text
|
||||
__BILIUP_NEXT_SECRET__
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 保留占位符:不改原值
|
||||
- 改成空字符串:清空原值
|
||||
- 改成新的字符串:更新为新值
|
||||
|
||||
## 推荐操作流
|
||||
|
||||
### 新上传一个测试视频
|
||||
|
||||
1. 打开控制台
|
||||
2. 在 `Import To Stage` 上传视频
|
||||
3. 看 `Tasks` 是否出现新任务
|
||||
4. 选中该任务
|
||||
5. 观察 `Steps / Artifacts / Timeline`
|
||||
6. 如需加速,点击 `执行当前任务`
|
||||
|
||||
### 某个任务失败
|
||||
|
||||
1. 选中任务
|
||||
2. 看 `Task Detail` 当前状态
|
||||
3. 看 `Steps` 哪一步失败
|
||||
4. 看 `Logs`
|
||||
5. 若只是临时失败:
|
||||
- 选中 step
|
||||
- 点 `重试选中 Step`
|
||||
6. 若后续产物也需要重建:
|
||||
- 选中 step
|
||||
- 点 `重置到选中 Step`
|
||||
|
||||
### 修改合集或上传参数
|
||||
|
||||
1. 打开 `Settings`
|
||||
2. 搜索目标参数,例如 `season` / `retry`
|
||||
3. 修改表单
|
||||
4. 点击 `保存 Settings`
|
||||
5. 对目标任务执行:
|
||||
- `执行当前任务`
|
||||
- 或 `重置到相关 step`
|
||||
|
||||
### 控制评论与清理行为
|
||||
|
||||
1. 打开 `Settings`
|
||||
2. 搜索:
|
||||
- `post_split_comment`
|
||||
- `post_full_video_timeline_comment`
|
||||
- `delete_source_video_after_collection_synced`
|
||||
- `delete_split_videos_after_collection_synced`
|
||||
3. 修改后保存
|
||||
4. 新任务会按新规则执行
|
||||
|
||||
建议:
|
||||
|
||||
- 如果你希望纯享版评论更适合分P浏览,保持 `post_split_comment = true`
|
||||
- 如果你不希望尝试给完整版主视频发时间轴评论,可以关闭 `post_full_video_timeline_comment`
|
||||
- 如果磁盘紧张,再开启 cleanup;默认建议先关闭,等确认流程稳定后再开
|
||||
|
||||
### 服务异常
|
||||
|
||||
1. 看 `Services`
|
||||
2. 优先 `restart biliup-next-worker.service`
|
||||
3. 如果页面自身异常,再 `restart biliup-next-api.service`
|
||||
4. 重启后看:
|
||||
- `Health`
|
||||
- `Doctor`
|
||||
- `Recent Actions`
|
||||
|
||||
## 安全建议
|
||||
|
||||
当前控制台已经对公网开放时,建议立刻设置:
|
||||
|
||||
- `runtime.control_token`
|
||||
|
||||
设置后:
|
||||
|
||||
- 除 `/` 和 `/health` 外,其余 API 都要求 `X-Biliup-Token`
|
||||
|
||||
如果你通过公网访问控制台,不建议长期保持空 token。
|
||||
238
docs/design-principles.md
Normal file
238
docs/design-principles.md
Normal file
@ -0,0 +1,238 @@
|
||||
# Design Principles
|
||||
|
||||
## Positioning
|
||||
|
||||
`biliup-next` 以 OpenClaw 的设计哲学为指引,但不复制它的产品形态。
|
||||
|
||||
本项目的核心目标不是做聊天代理系统,而是构建一个面向本地视频流水线的控制面驱动系统。
|
||||
|
||||
因此我们借鉴的是方法论:
|
||||
|
||||
- 单体优先
|
||||
- 控制面优先
|
||||
- 配置与扩展元数据优先
|
||||
- 严格校验
|
||||
- 本地优先
|
||||
- 可读、可审计、可替换
|
||||
|
||||
## Principle 1: Modular Monolith First
|
||||
|
||||
系统优先采用模块化单体架构,而不是微服务。
|
||||
|
||||
原因:
|
||||
|
||||
- 当前问题主要来自边界混乱,而不是部署扩展性不足
|
||||
- 单机部署和 systemd 管理仍然是核心场景
|
||||
- 统一配置、任务状态和日志比进程拆分更重要
|
||||
|
||||
约束:
|
||||
|
||||
- 所有模块运行在同一系统边界内
|
||||
- 模块之间通过抽象接口和统一模型交互
|
||||
- 不得通过随意脚本调用形成隐式耦合
|
||||
|
||||
## Principle 2: Control Plane First
|
||||
|
||||
功能模块不是系统中心,控制面才是系统中心。
|
||||
|
||||
控制面负责:
|
||||
|
||||
- 配置管理
|
||||
- 任务状态管理
|
||||
- 模块与插件注册
|
||||
- 手动操作入口
|
||||
- 日志与诊断
|
||||
|
||||
数据面负责:
|
||||
|
||||
- 执行转录
|
||||
- 执行识歌
|
||||
- 执行切歌
|
||||
- 执行上传
|
||||
- 执行评论和合集归档
|
||||
|
||||
任何新增功能,都必须先回答:
|
||||
|
||||
- 它如何进入控制面
|
||||
- 它的状态如何呈现
|
||||
- 它的配置如何管理
|
||||
- 它失败后如何恢复
|
||||
|
||||
## Principle 3: Schema-First Configuration
|
||||
|
||||
配置必须先有 schema,再有实现和 UI。
|
||||
|
||||
要求:
|
||||
|
||||
- 所有配置项先定义在 schema
|
||||
- 所有配置项有默认值、校验规则和说明
|
||||
- UI 基于 schema 生成
|
||||
- CLI 和 API 使用同一套字段定义
|
||||
|
||||
禁止:
|
||||
|
||||
- 在模块里私自增加隐藏配置常量
|
||||
- UI 和代码维护不同字段名
|
||||
- 配置错误仍然带病启动
|
||||
|
||||
## Principle 4: Manifest-First Extensibility
|
||||
|
||||
扩展能力先注册元数据,再执行运行时代码。
|
||||
|
||||
manifest 负责描述:
|
||||
|
||||
- 插件是谁
|
||||
- 提供什么能力
|
||||
- 需要什么配置
|
||||
- 入口在哪
|
||||
- 是否可启用
|
||||
|
||||
这样控制面可以在不执行插件代码时完成:
|
||||
|
||||
- 能力发现
|
||||
- 配置渲染
|
||||
- 可用性检查
|
||||
- 兼容性检查
|
||||
|
||||
## Principle 5: Registry Over Direct Coupling
|
||||
|
||||
模块和插件必须通过统一 registry 接入。
|
||||
|
||||
例如:
|
||||
|
||||
- transcriber registry
|
||||
- song detector registry
|
||||
- publisher registry
|
||||
- collection strategy registry
|
||||
|
||||
核心模块只依赖接口,不依赖具体 provider。
|
||||
|
||||
这意味着:
|
||||
|
||||
- 更换 Groq 为其他转录器不影响任务引擎
|
||||
- 更换 Codex 为其他识歌器不影响控制面
|
||||
- 更换 biliup 为其他上传方式不影响领域模型
|
||||
|
||||
## Principle 6: Local-First And Human-Readable
|
||||
|
||||
系统优先本地运行,本地保存,本地可读。
|
||||
|
||||
要求:
|
||||
|
||||
- 主状态存储可本地访问
|
||||
- 日志保存在本地
|
||||
- 配置保存在本地
|
||||
- 关键元数据可被开发者直接理解
|
||||
|
||||
这不意味着只靠文件系统。
|
||||
|
||||
建议做法:
|
||||
|
||||
- SQLite 保存结构化状态
|
||||
- 文件系统保存产物
|
||||
- JSON / YAML 保存配置
|
||||
- 文本日志保存审计和错误
|
||||
|
||||
## Principle 7: Strict Validation
|
||||
|
||||
系统不能接受“差不多能跑”的配置和模块状态。
|
||||
|
||||
启动或配置变更前,应验证:
|
||||
|
||||
- schema 是否合法
|
||||
- 可执行依赖是否存在
|
||||
- 必要凭证是否存在
|
||||
- provider 是否可初始化
|
||||
- 插件 manifest 是否正确
|
||||
|
||||
失败时:
|
||||
|
||||
- 保留旧运行态
|
||||
- 返回明确错误
|
||||
- 不进入半失效状态
|
||||
|
||||
## Principle 8: Single Source Of Truth
|
||||
|
||||
任务状态必须有统一来源。
|
||||
|
||||
不得同时依赖:
|
||||
|
||||
- flag 文件推断状态
|
||||
- 日志推断状态
|
||||
- 目录结构推断状态
|
||||
|
||||
正确做法:
|
||||
|
||||
- 数据库记录任务状态
|
||||
- 文件系统存放任务产物
|
||||
- 日志记录过程和诊断
|
||||
|
||||
三者职责分离,不互相替代。
|
||||
|
||||
## Principle 9: Replaceability With Stable Core
|
||||
|
||||
可替换的是 provider,不可随意漂移的是核心模型。
|
||||
|
||||
稳定核心包括:
|
||||
|
||||
- Task
|
||||
- TaskStep
|
||||
- Artifact
|
||||
- PublishRecord
|
||||
- CollectionBinding
|
||||
- Settings
|
||||
|
||||
这些模型一旦定义,应保持长期稳定,避免每新增一个模块就改核心语义。
|
||||
|
||||
## Principle 10: Observability Is A First-Class Feature
|
||||
|
||||
可观测性不是补丁,而是正式能力。
|
||||
|
||||
管理台必须能够回答:
|
||||
|
||||
- 当前有哪些任务
|
||||
- 每个任务在哪一步
|
||||
- 最近一次失败是什么
|
||||
- 当前启用的 provider 是谁
|
||||
- 当前配置是否有效
|
||||
- 哪个模块健康异常
|
||||
|
||||
如果系统不能快速回答这些问题,说明设计不完整。
|
||||
|
||||
## Principle 11: Backward-Compatible Migration
|
||||
|
||||
重构必须与旧系统并行推进。
|
||||
|
||||
要求:
|
||||
|
||||
- 原项目继续运行
|
||||
- 新项目只在 `./biliup-next` 演进
|
||||
- 先搭文档和骨架
|
||||
- 再逐步迁移模块
|
||||
- 最后切换生产入口
|
||||
|
||||
禁止:
|
||||
|
||||
- 直接在旧系统里边修边重构
|
||||
- 在未定义状态模型前大规模搬代码
|
||||
|
||||
## Principle 12: Documentation Before Expansion
|
||||
|
||||
任何新的模块、插件、控制面能力,在动手实现前都要先回答:
|
||||
|
||||
- 它属于哪个层
|
||||
- 它的输入输出是什么
|
||||
- 它如何配置
|
||||
- 它如何注册
|
||||
- 它如何被 UI 展示
|
||||
- 它如何失败和恢复
|
||||
|
||||
如果这些问题没有写清楚,就不应进入实现阶段。
|
||||
|
||||
## Summary
|
||||
|
||||
`biliup-next` 的核心方向不是“把旧脚本改漂亮”,而是:
|
||||
|
||||
- 建立一个本地优先、控制面驱动、模块边界清晰的系统
|
||||
- 让配置、模块、状态、操作和诊断都回到统一模型之下
|
||||
- 让未来扩展建立在 schema、manifest、registry 和稳定领域模型之上
|
||||
135
docs/domain-model.md
Normal file
135
docs/domain-model.md
Normal file
@ -0,0 +1,135 @@
|
||||
# Domain Model
|
||||
|
||||
## Task
|
||||
|
||||
一个任务代表一条完整的视频处理链路。
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "task_01",
|
||||
"source_type": "local_file",
|
||||
"source_path": "stage/example.mp4",
|
||||
"title": "王海颖唱歌录播 03月29日 22时02分",
|
||||
"status": "published",
|
||||
"created_at": "2026-03-30T07:50:42+08:00",
|
||||
"updated_at": "2026-03-30T07:56:13+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
- `id`: 内部唯一 ID
|
||||
- `source_type`: 输入来源,例如 `local_file`
|
||||
- `source_path`: 原始文件路径
|
||||
- `title`: 任务显示名称
|
||||
- `status`: 当前状态
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
## TaskStep
|
||||
|
||||
一个任务中的单个处理步骤。
|
||||
|
||||
### Step Names
|
||||
|
||||
- `ingest`
|
||||
- `transcribe`
|
||||
- `song_detect`
|
||||
- `split`
|
||||
- `publish`
|
||||
- `comment`
|
||||
- `collection_a`
|
||||
- `collection_b`
|
||||
|
||||
### Step Status
|
||||
|
||||
- `pending`
|
||||
- `running`
|
||||
- `succeeded`
|
||||
- `failed_retryable`
|
||||
- `failed_manual`
|
||||
- `skipped`
|
||||
|
||||
## Artifact
|
||||
|
||||
任务产物。
|
||||
|
||||
### Artifact Types
|
||||
|
||||
- `source_video`
|
||||
- `subtitle_srt`
|
||||
- `songs_json`
|
||||
- `songs_txt`
|
||||
- `clip_video`
|
||||
- `publish_bvid`
|
||||
- `comment_record`
|
||||
- `collection_record`
|
||||
|
||||
## PublishRecord
|
||||
|
||||
记录上传结果。
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "task_01",
|
||||
"platform": "bilibili",
|
||||
"aid": 123456,
|
||||
"bvid": "BV1xxxx",
|
||||
"title": "【王海颖 (歌曲纯享版)】_03月29日 22时02分 共18首歌",
|
||||
"published_at": "2026-03-30T07:56:13+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
## CollectionBinding
|
||||
|
||||
记录视频与合集之间的绑定关系。
|
||||
|
||||
### Fields
|
||||
|
||||
- `task_id`
|
||||
- `target`
|
||||
- `season_id`
|
||||
- `section_id`
|
||||
- `bvid`
|
||||
- `status`
|
||||
- `last_error`
|
||||
|
||||
### Target Values
|
||||
|
||||
- `full_video_collection`
|
||||
- `song_collection`
|
||||
|
||||
## Settings
|
||||
|
||||
统一配置项,按逻辑分组。
|
||||
|
||||
### Example Groups
|
||||
|
||||
- `runtime`
|
||||
- `paths`
|
||||
- `transcribe`
|
||||
- `song_detect`
|
||||
- `publish`
|
||||
- `comment`
|
||||
- `collection`
|
||||
|
||||
## Domain Events
|
||||
|
||||
### Core Events
|
||||
|
||||
- `TaskCreated`
|
||||
- `TaskStepStarted`
|
||||
- `TaskStepSucceeded`
|
||||
- `TaskStepFailed`
|
||||
- `ArtifactCreated`
|
||||
- `PublishCompleted`
|
||||
- `CommentCompleted`
|
||||
- `CollectionSynced`
|
||||
|
||||
## State Machine Rules
|
||||
|
||||
- 同一时刻,一个步骤只能有一个 `running`
|
||||
- 失败必须记录 `error_code` 和 `error_message`
|
||||
- `published` 之前不能进入评论和合集步骤
|
||||
- `songs_detected` 之前不能进入切歌步骤
|
||||
- `transcribed` 之前不能进入识歌步骤
|
||||
192
docs/migration-plan.md
Normal file
192
docs/migration-plan.md
Normal file
@ -0,0 +1,192 @@
|
||||
# Migration Plan
|
||||
|
||||
## Goal
|
||||
|
||||
在不破坏原项目运行的前提下,逐步将能力迁移到 `biliup-next`。
|
||||
|
||||
## Migration Principles
|
||||
|
||||
- 原项目继续作为生产系统运行
|
||||
- 新项目只在 `./biliup-next` 中演进
|
||||
- 先文档、后骨架、再迁移功能
|
||||
- 先控制面,后数据面
|
||||
- 先兼容旧目录结构,再逐步替换旧入口
|
||||
|
||||
## Phase 0: Documentation Baseline
|
||||
|
||||
目标:
|
||||
|
||||
- 明确设计原则
|
||||
- 明确架构分层
|
||||
- 明确领域模型
|
||||
- 明确配置系统和插件系统
|
||||
|
||||
产物:
|
||||
|
||||
- `vision.md`
|
||||
- `architecture.md`
|
||||
- `domain-model.md`
|
||||
- `design-principles.md`
|
||||
- `config-system.md`
|
||||
- `plugin-system.md`
|
||||
- `state-machine.md`
|
||||
- `module-contracts.md`
|
||||
- `migration-plan.md`
|
||||
|
||||
## Phase 1: Project Skeleton
|
||||
|
||||
目标:
|
||||
|
||||
- 建立 `biliup-next/src` 目录结构
|
||||
- 建立基础 Python 包
|
||||
- 建立最小配置系统
|
||||
- 建立 SQLite 存储层
|
||||
- 建立任务模型和状态模型
|
||||
|
||||
不做:
|
||||
|
||||
- 不迁移业务逻辑
|
||||
- 不接管生产入口
|
||||
|
||||
## Phase 2: Control Plane MVP
|
||||
|
||||
目标:
|
||||
|
||||
- 提供最小 API
|
||||
- 提供任务列表和配置读取能力
|
||||
- 提供最小 CLI
|
||||
- 提供 health / logs / settings / tasks 查询能力
|
||||
|
||||
产物:
|
||||
|
||||
- API server
|
||||
- config service
|
||||
- task repository
|
||||
- runtime doctor
|
||||
|
||||
## Phase 3: Data Plane Adapters
|
||||
|
||||
目标:
|
||||
|
||||
- 把旧系统依赖的外部能力封装成 adapter
|
||||
|
||||
优先顺序:
|
||||
|
||||
1. `ffmpeg` adapter
|
||||
2. `Groq` adapter
|
||||
3. `Codex` adapter
|
||||
4. `biliup` adapter
|
||||
5. `Bili API` adapter
|
||||
|
||||
理由:
|
||||
|
||||
- 先封装外部依赖,后迁移业务模块,能减少后续反复返工
|
||||
|
||||
## Phase 4: Module Migration
|
||||
|
||||
按顺序迁移业务模块。
|
||||
|
||||
### 4.1 Ingest
|
||||
|
||||
- 替代 `monitor.py` 的任务创建部分
|
||||
|
||||
### 4.2 Transcribe
|
||||
|
||||
- 替代 `video2srt.py`
|
||||
|
||||
### 4.3 Song Detect
|
||||
|
||||
- 替代 `monitorSrt.py`
|
||||
|
||||
### 4.4 Split
|
||||
|
||||
- 替代 `monitorSongs.py`
|
||||
|
||||
### 4.5 Publish
|
||||
|
||||
- 替代 `upload.py`
|
||||
|
||||
### 4.6 Comment
|
||||
|
||||
- 替代 `session_top_comment.py`
|
||||
|
||||
### 4.7 Collection
|
||||
|
||||
- 替代 `add_to_collection.py`
|
||||
|
||||
## Phase 5: Parallel Verification
|
||||
|
||||
目标:
|
||||
|
||||
- 新旧系统并行验证
|
||||
- 新系统只处理测试任务
|
||||
- 对比产物、日志、状态和 B 站结果
|
||||
|
||||
重点检查:
|
||||
|
||||
- 字幕结果
|
||||
- 歌曲识别结果
|
||||
- 切片结果
|
||||
- 上传结果
|
||||
- 评论和合集结果
|
||||
|
||||
## Phase 6: Admin UI
|
||||
|
||||
目标:
|
||||
|
||||
- 构建本地控制台
|
||||
|
||||
第一版包含:
|
||||
|
||||
- 配置页
|
||||
- 任务页
|
||||
- 模块页
|
||||
- 日志页
|
||||
- 手动操作页
|
||||
|
||||
## Phase 7: Cutover
|
||||
|
||||
目标:
|
||||
|
||||
- 新系统逐步接管生产入口
|
||||
|
||||
顺序建议:
|
||||
|
||||
1. 只接管任务可视化
|
||||
2. 接管配置管理
|
||||
3. 接管测试任务处理
|
||||
4. 接管单模块生产流量
|
||||
5. 最终接管全部生产流量
|
||||
|
||||
## Risks
|
||||
|
||||
### Risk 1: 旧系统与新系统语义不一致
|
||||
|
||||
缓解:
|
||||
|
||||
- 先定义领域模型和状态机
|
||||
- 迁移前写适配层,不直接照抄旧脚本行为
|
||||
|
||||
### Risk 2: 边迁移边污染旧项目
|
||||
|
||||
缓解:
|
||||
|
||||
- 所有新内容只放在 `./biliup-next`
|
||||
- 不改原项目运行入口
|
||||
|
||||
### Risk 3: UI 先行导致底层不稳
|
||||
|
||||
缓解:
|
||||
|
||||
- 先做控制面模型和 API
|
||||
- 最后做 UI
|
||||
|
||||
## Definition Of Done
|
||||
|
||||
迁移完成的标准不是“代码搬完”,而是:
|
||||
|
||||
- 新系统可以独立运行
|
||||
- 配置统一管理
|
||||
- 状态统一落库
|
||||
- UI 可以完整观察任务
|
||||
- 旧脚本不再是主入口
|
||||
229
docs/module-contracts.md
Normal file
229
docs/module-contracts.md
Normal file
@ -0,0 +1,229 @@
|
||||
# Module Contracts
|
||||
|
||||
## Goal
|
||||
|
||||
定义各模块的职责边界、输入输出和契约,避免旧系统中“脚本互相读目录、互相猜状态”的耦合方式。
|
||||
|
||||
## Contract Principles
|
||||
|
||||
- 每个模块只处理一类能力
|
||||
- 模块只接收明确输入,不扫描全世界
|
||||
- 模块输出必须结构化
|
||||
- 模块不直接操控其他模块的内部实现
|
||||
- 模块不直接依赖具体 provider
|
||||
|
||||
## Shared Concepts
|
||||
|
||||
所有模块统一围绕这些对象协作:
|
||||
|
||||
- `Task`
|
||||
- `TaskStep`
|
||||
- `Artifact`
|
||||
- `Settings`
|
||||
- `ProviderRef`
|
||||
|
||||
## Ingest Module
|
||||
|
||||
### Responsibility
|
||||
|
||||
- 接收文件输入
|
||||
- 校验最小处理条件
|
||||
- 创建任务
|
||||
|
||||
### Input
|
||||
|
||||
- 本地文件路径
|
||||
- 当前配置
|
||||
|
||||
### Output
|
||||
|
||||
- `Task`
|
||||
- `source_video` artifact
|
||||
|
||||
### Must Not Do
|
||||
|
||||
- 不直接转录
|
||||
- 不写上传状态
|
||||
|
||||
## Transcribe Module
|
||||
|
||||
### Responsibility
|
||||
|
||||
- 调用转录 provider
|
||||
- 生成字幕产物
|
||||
|
||||
### Input
|
||||
|
||||
- `Task`
|
||||
- `source_video` artifact
|
||||
- `transcribe` settings
|
||||
|
||||
### Output
|
||||
|
||||
- `subtitle_srt` artifact
|
||||
|
||||
### Must Not Do
|
||||
|
||||
- 不识别歌曲
|
||||
- 不决定切歌策略
|
||||
|
||||
## Song Detect Module
|
||||
|
||||
### Responsibility
|
||||
|
||||
- 根据字幕识别歌曲
|
||||
- 生成歌曲结构化结果
|
||||
|
||||
### Input
|
||||
|
||||
- `Task`
|
||||
- `subtitle_srt` artifact
|
||||
- `song_detect` settings
|
||||
|
||||
### Output
|
||||
|
||||
- `songs_json` artifact
|
||||
- `songs_txt` artifact
|
||||
|
||||
### Must Not Do
|
||||
|
||||
- 不切歌
|
||||
- 不上传
|
||||
|
||||
## Split Module
|
||||
|
||||
### Responsibility
|
||||
|
||||
- 根据歌曲列表切割纯享版片段
|
||||
|
||||
### Input
|
||||
|
||||
- `Task`
|
||||
- `songs_json` artifact
|
||||
- `source_video` artifact
|
||||
- `split` settings
|
||||
|
||||
### Output
|
||||
|
||||
- 多个 `clip_video` artifact
|
||||
|
||||
## Publish Module
|
||||
|
||||
### Responsibility
|
||||
|
||||
- 上传纯享版视频
|
||||
- 记录发布结果
|
||||
|
||||
### Input
|
||||
|
||||
- `Task`
|
||||
- `clip_video[]`
|
||||
- `publish` settings
|
||||
|
||||
### Output
|
||||
|
||||
- `PublishRecord`
|
||||
- `publish_bvid` artifact
|
||||
|
||||
### Must Not Do
|
||||
|
||||
- 不负责评论文案生成
|
||||
- 不负责合集匹配策略
|
||||
|
||||
## Comment Module
|
||||
|
||||
### Responsibility
|
||||
|
||||
- 发布并置顶评论
|
||||
|
||||
### Input
|
||||
|
||||
- `Task`
|
||||
- `PublishRecord`
|
||||
- `songs_txt` artifact
|
||||
- `comment` settings
|
||||
|
||||
### Output
|
||||
|
||||
- `comment_record` artifact
|
||||
|
||||
## Collection Module
|
||||
|
||||
### Responsibility
|
||||
|
||||
- 根据策略同步合集 A / B
|
||||
|
||||
### Input
|
||||
|
||||
- `Task`
|
||||
- `PublishRecord` 或外部 `full_video_bvid`
|
||||
- `collection` settings
|
||||
- `collection strategy`
|
||||
|
||||
### Output
|
||||
|
||||
- `CollectionBinding`
|
||||
|
||||
### Internal Sub-Strategies
|
||||
|
||||
- `full_video_collection_strategy`
|
||||
- `song_collection_strategy`
|
||||
|
||||
## Provider Contracts
|
||||
|
||||
### TranscribeProvider
|
||||
|
||||
```text
|
||||
transcribe(task, source_video, settings) -> subtitle_srt
|
||||
```
|
||||
|
||||
### SongDetector
|
||||
|
||||
```text
|
||||
detect(task, subtitle_srt, settings) -> songs_json, songs_txt
|
||||
```
|
||||
|
||||
### PublishProvider
|
||||
|
||||
```text
|
||||
publish(task, clip_videos, settings) -> PublishRecord
|
||||
```
|
||||
|
||||
### CommentStrategy
|
||||
|
||||
```text
|
||||
sync_comment(task, publish_record, songs_txt, settings) -> comment_record
|
||||
```
|
||||
|
||||
### CollectionStrategy
|
||||
|
||||
```text
|
||||
sync_collection(task, context, settings) -> CollectionBinding[]
|
||||
```
|
||||
|
||||
## Orchestration Rules
|
||||
|
||||
模块本身不负责全局编排。
|
||||
|
||||
全局编排由任务引擎或 worker 负责:
|
||||
|
||||
- 判断下一步该跑什么
|
||||
- 决定是否重试
|
||||
- 写入状态
|
||||
- 调度具体模块
|
||||
|
||||
## Error Contract
|
||||
|
||||
所有模块失败时应返回统一错误结构:
|
||||
|
||||
- `code`
|
||||
- `message`
|
||||
- `retryable`
|
||||
- `details`
|
||||
|
||||
不得只返回原始字符串日志作为唯一错误结果。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 模块之间不共享私有目录扫描逻辑
|
||||
- 模块契约不直接暴露 shell 命令细节
|
||||
156
docs/plugin-system.md
Normal file
156
docs/plugin-system.md
Normal file
@ -0,0 +1,156 @@
|
||||
# Plugin System
|
||||
|
||||
## Goal
|
||||
|
||||
插件系统的目标不是“让任何东西都能热插拔”,而是为未来的能力替换和扩展提供稳定边界。
|
||||
|
||||
优先支持:
|
||||
|
||||
- 转录提供者替换
|
||||
- 歌曲识别提供者替换
|
||||
- 上传器替换
|
||||
- 评论策略替换
|
||||
- 合集策略替换
|
||||
- 输入源扩展
|
||||
|
||||
## Design Principles
|
||||
|
||||
借鉴 OpenClaw 的思路,采用 `manifest-first` + `registry` 设计。
|
||||
|
||||
原则:
|
||||
|
||||
- 插件先注册元信息,再执行运行时代码
|
||||
- 控制面优先读取 manifest 和 schema
|
||||
- 核心系统只依赖抽象接口和 registry
|
||||
- 插件配置必须可校验
|
||||
|
||||
## Plugin Composition
|
||||
|
||||
每个插件由两部分组成:
|
||||
|
||||
### 1. Manifest
|
||||
|
||||
描述插件的元信息和配置能力。
|
||||
|
||||
例如:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "codex-song-detector",
|
||||
"name": "Codex Song Detector",
|
||||
"version": "0.1.0",
|
||||
"type": "song_detector",
|
||||
"entrypoint": "plugins.codex_song_detector.runtime:register",
|
||||
"configSchema": "plugins/codex_song_detector/config.schema.json",
|
||||
"capabilities": ["song_detect"],
|
||||
"enabledByDefault": true
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Runtime
|
||||
|
||||
真正实现业务逻辑的代码。
|
||||
|
||||
## Registry
|
||||
|
||||
系统启动时统一构建 registry。
|
||||
|
||||
registry 负责:
|
||||
|
||||
- 注册插件能力
|
||||
- 按类型查找实现
|
||||
- 根据配置激活当前 provider
|
||||
|
||||
### Registry Types
|
||||
|
||||
- `ingest_provider`
|
||||
- `transcribe_provider`
|
||||
- `song_detector`
|
||||
- `split_provider`
|
||||
- `publish_provider`
|
||||
- `comment_strategy`
|
||||
- `collection_strategy`
|
||||
|
||||
## Plugin Loading Flow
|
||||
|
||||
```text
|
||||
Discover manifests
|
||||
-> Validate manifests
|
||||
-> Register capabilities in registry
|
||||
-> Load plugin config schema
|
||||
-> Validate plugin config
|
||||
-> Activate runtime implementation
|
||||
```
|
||||
|
||||
## Why Manifest-First
|
||||
|
||||
这样设计有 4 个直接好处:
|
||||
|
||||
- 管理台可以在不执行插件代码时展示插件信息
|
||||
- UI 可以根据 schema 渲染配置表单
|
||||
- 系统可以提前发现缺失字段或不兼容版本
|
||||
- 插件运行失败不会影响元数据层的可见性
|
||||
|
||||
## Suggested Plugin Boundaries
|
||||
|
||||
### Transcribe Provider
|
||||
|
||||
示例:
|
||||
|
||||
- `groq`
|
||||
- `openai`
|
||||
- `local_whisper`
|
||||
|
||||
### Song Detector
|
||||
|
||||
示例:
|
||||
|
||||
- `codex`
|
||||
- `rule_engine`
|
||||
- `custom_llm`
|
||||
|
||||
### Publish Provider
|
||||
|
||||
示例:
|
||||
|
||||
- `biliup_cli`
|
||||
- `bilibili_api`
|
||||
|
||||
### Collection Strategy
|
||||
|
||||
示例:
|
||||
|
||||
- `default_song_collection`
|
||||
- `title_match_full_video`
|
||||
- `manual_binding`
|
||||
|
||||
## Control Plane Integration
|
||||
|
||||
插件系统必须服务于控制面。
|
||||
|
||||
因此管理台至少需要知道:
|
||||
|
||||
- 当前有哪些插件
|
||||
- 每个插件类型是什么
|
||||
- 当前启用的是哪一个
|
||||
- 配置是否有效
|
||||
- 最近一次健康检查结果
|
||||
|
||||
## Restrictions
|
||||
|
||||
为了避免再次走向“任意脚本散落”,插件系统需要约束:
|
||||
|
||||
- 插件不得直接修改核心数据库结构
|
||||
- 插件不得绕过统一配置系统
|
||||
- 插件不得私自写独立日志目录作为唯一状态来源
|
||||
- 插件不得直接互相调用具体实现
|
||||
|
||||
## Initial Strategy
|
||||
|
||||
第一阶段不追求真正的第三方插件生态。
|
||||
|
||||
先实现“内置插件化”:
|
||||
|
||||
- 核心仓库内提供多个 provider
|
||||
- 统一用 manifest + registry 管理
|
||||
- 等边界稳定后,再考虑开放外部插件目录
|
||||
212
docs/state-machine.md
Normal file
212
docs/state-machine.md
Normal file
@ -0,0 +1,212 @@
|
||||
# State Machine
|
||||
|
||||
## Goal
|
||||
|
||||
定义 `biliup-next` 的任务状态机,取代旧系统依赖 flag 文件、日志和目录结构推断状态的方式。
|
||||
|
||||
状态机目标:
|
||||
|
||||
- 让每个任务始终有明确状态
|
||||
- 支持失败重试和人工介入
|
||||
- 让 UI 和 API 可以直接消费状态
|
||||
- 保证步骤顺序和依赖关系清晰
|
||||
|
||||
## State Model
|
||||
|
||||
任务状态分为两层:
|
||||
|
||||
- `task status`:任务整体状态
|
||||
- `step status`:任务中每一步的执行状态
|
||||
|
||||
## Task Status
|
||||
|
||||
### Core Statuses
|
||||
|
||||
- `created`
|
||||
- `ingested`
|
||||
- `transcribed`
|
||||
- `songs_detected`
|
||||
- `split_done`
|
||||
- `published`
|
||||
- `commented`
|
||||
- `collection_synced`
|
||||
- `completed`
|
||||
|
||||
### Failure Statuses
|
||||
|
||||
- `failed_retryable`
|
||||
- `failed_manual`
|
||||
|
||||
### Terminal Statuses
|
||||
|
||||
- `completed`
|
||||
- `cancelled`
|
||||
- `failed_manual`
|
||||
|
||||
## Step Status
|
||||
|
||||
每个步骤都独立维护自己的状态。
|
||||
|
||||
- `pending`
|
||||
- `running`
|
||||
- `succeeded`
|
||||
- `failed_retryable`
|
||||
- `failed_manual`
|
||||
- `skipped`
|
||||
|
||||
## Step Definitions
|
||||
|
||||
### ingest
|
||||
|
||||
负责:
|
||||
|
||||
- 接收输入视频
|
||||
- 基础校验
|
||||
- 创建任务记录
|
||||
|
||||
### transcribe
|
||||
|
||||
负责:
|
||||
|
||||
- 生成字幕
|
||||
- 记录字幕产物
|
||||
|
||||
### song_detect
|
||||
|
||||
负责:
|
||||
|
||||
- 识别歌曲列表
|
||||
- 生成 `songs.json` 和 `songs.txt`
|
||||
|
||||
### split
|
||||
|
||||
负责:
|
||||
|
||||
- 根据歌单切割视频
|
||||
- 生成切片产物
|
||||
|
||||
### publish
|
||||
|
||||
负责:
|
||||
|
||||
- 上传纯享版视频
|
||||
- 记录 `aid/bvid`
|
||||
|
||||
### comment
|
||||
|
||||
负责:
|
||||
|
||||
- 发布评论
|
||||
- 置顶评论
|
||||
|
||||
### collection_a
|
||||
|
||||
负责:
|
||||
|
||||
- 将完整版视频加入合集 A
|
||||
|
||||
### collection_b
|
||||
|
||||
负责:
|
||||
|
||||
- 将纯享版视频加入合集 B
|
||||
|
||||
## State Transition Rules
|
||||
|
||||
### Task-Level
|
||||
|
||||
```text
|
||||
created
|
||||
-> ingested
|
||||
-> transcribed
|
||||
-> songs_detected
|
||||
-> split_done
|
||||
-> published
|
||||
-> commented
|
||||
-> collection_synced
|
||||
-> completed
|
||||
```
|
||||
|
||||
### Failure Transition
|
||||
|
||||
任何步骤失败后:
|
||||
|
||||
- 若允许自动重试:任务进入 `failed_retryable`
|
||||
- 若必须人工介入:任务进入 `failed_manual`
|
||||
|
||||
重试成功后:
|
||||
|
||||
- 任务回到该步骤成功后的下一个合法状态
|
||||
|
||||
## Dependency Rules
|
||||
|
||||
- `transcribe` 必须依赖 `ingest`
|
||||
- `song_detect` 必须依赖 `transcribe`
|
||||
- `split` 必须依赖 `song_detect`
|
||||
- `publish` 必须依赖 `split`
|
||||
- `comment` 必须依赖 `publish`
|
||||
- `collection_b` 必须依赖 `publish`
|
||||
- `collection_a` 通常依赖外部完整版 BV,可独立于 `publish`
|
||||
|
||||
## Special Case: Collection A
|
||||
|
||||
合集 A 的数据来源与主上传链路不同。
|
||||
|
||||
因此:
|
||||
|
||||
- `collection_a` 不应阻塞主任务完成
|
||||
- `collection_a` 可作为独立步骤存在
|
||||
- 任务整体完成不必强依赖 `collection_a` 成功
|
||||
|
||||
建议:
|
||||
|
||||
- `completed` 表示主链路完成
|
||||
- `collection_synced` 表示所有合集同步完成
|
||||
|
||||
## Retry Strategy
|
||||
|
||||
### Retryable Errors
|
||||
|
||||
适合自动重试:
|
||||
|
||||
- 网络错误
|
||||
- 外部 API 临时失败
|
||||
- 上传频控
|
||||
- 外部命令短时异常
|
||||
|
||||
### Manual Errors
|
||||
|
||||
需要人工介入:
|
||||
|
||||
- 配置缺失
|
||||
- 凭证失效
|
||||
- 文件损坏
|
||||
- provider 不可用
|
||||
- 标题无法匹配完整版 BV
|
||||
|
||||
## Persistence Requirements
|
||||
|
||||
每次状态变更都必须落库:
|
||||
|
||||
- 任务状态
|
||||
- 步骤状态
|
||||
- 开始时间
|
||||
- 结束时间
|
||||
- 错误码
|
||||
- 错误信息
|
||||
- 重试次数
|
||||
|
||||
## UI Expectations
|
||||
|
||||
UI 至少需要直接展示:
|
||||
|
||||
- 当前任务状态
|
||||
- 当前正在运行的步骤
|
||||
- 最近失败步骤
|
||||
- 重试次数
|
||||
- 是否需要人工介入
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 不追求一个任务多个步骤完全并发执行
|
||||
- 不允许继续依赖 flag 文件作为权威状态来源
|
||||
54
docs/vision.md
Normal file
54
docs/vision.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Vision
|
||||
|
||||
## Goal
|
||||
|
||||
将当前基于目录监听和脚本拼接的流水线,重构为一个模块化、可扩展、可观测、可运维的单体系统。
|
||||
|
||||
系统负责:
|
||||
|
||||
- 接收本地视频任务
|
||||
- 执行转录、识歌、切歌、上传、评论、合集归档
|
||||
- 记录任务状态、产物、错误和外部结果
|
||||
- 提供统一配置和管理入口
|
||||
|
||||
系统不负责:
|
||||
|
||||
- 直播录制
|
||||
- 完整版视频的外部发布流程
|
||||
- 多账号复杂运营后台
|
||||
- 分布式调度
|
||||
|
||||
## Users
|
||||
|
||||
- 运维者:部署、启动、排查、重试任务
|
||||
- 内容生产者:投放视频、观察任务状态
|
||||
- 开发者:新增模块、替换外部依赖、扩展功能
|
||||
|
||||
## Problems In Current Project
|
||||
|
||||
- 状态分散在目录名、flag 文件、日志中,缺少单一事实来源
|
||||
- 业务逻辑和运维逻辑耦合严重
|
||||
- 配置项散落在多个脚本和常量中
|
||||
- 同类逻辑重复实现,例如 B 站列表解析、合集处理、任务扫描
|
||||
- 可观测性不足,失败后需要人工翻日志定位
|
||||
- 扩展新能力时只能继续加脚本,结构会越来越乱
|
||||
|
||||
## Target Characteristics
|
||||
|
||||
- 模块化单体,而不是脚本集合
|
||||
- 显式任务状态机
|
||||
- 统一配置系统
|
||||
- 外部依赖适配器化
|
||||
- 结构化任务存储
|
||||
- 插件式扩展点
|
||||
- Web 管理台
|
||||
- 文档优先
|
||||
|
||||
## Milestones
|
||||
|
||||
1. 定义架构、领域模型、模块接口和 API。
|
||||
2. 建立新系统骨架,不影响旧系统运行。
|
||||
3. 落地统一配置、任务状态存储和最小管理 API。
|
||||
4. 按模块迁移旧能力:转录、识歌、切歌、上传、评论、合集。
|
||||
5. 接入 Web 管理台。
|
||||
6. 逐步切换生产流量,最终替换旧脚本体系。
|
||||
Reference in New Issue
Block a user