Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions DEVELOPMENT_LOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Development Log

## 2026-08-23 长直播分层高光选片(PR 2)

- `long_live_talk` 不再进入通用选片,改用固定约 300 秒、重叠 60 秒的语言高光窗口。
- 新增窗口级 SQLite checkpoint:状态、累计尝试次数、结果校验和、错误和下次重试时间均独立保存;同一转写指纹下成功窗口跨进程复用。
- 远程窗口单轮最多尝试 3 次并指数退避;失败窗口不会抹掉其他窗口结果,再次分析只请求缺失窗口。
- 高光按金句观点、故事经历、情绪峰值、冲突反转、实用知识、互动幽默六类召回,跨窗口按时间与文本语义合并。
- 最终结果先执行每小时密度上限,再轮询各小时执行总量上限,避免前半场提前占满全部名额。
- 时间轴覆盖不足 90% 时保存为“分析不完整”,自动流水线和手动切片均会在生成文件、同步发送中心之前停止。
- 新增六小时结构化时间轴、跨窗口去重、三次重试、断点复用、覆盖率门禁和幂等数据库测试。

## 2026-08-23 长直播基础设施(PR 1)

- 新建任务移除综艺隐藏默认值,页面、multipart 上传与 JSON API 都要求显式选择三种模式;历史任务保留原模式。
Expand Down
4 changes: 2 additions & 2 deletions NEXT_STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
## 2026-08-23 长直播四阶段进度

- [x] PR 1:模式必选、已有文件入口、媒体/磁盘预检、持久化重型 Job、转写断点与词级时间戳。
- [ ] PR 2:`long_live_talk` 5 分钟重叠窗口、每小时覆盖、全局配额、去重、90% 覆盖门禁和窗口级恢复。
- [x] PR 2:`long_live_talk` 5 分钟重叠窗口、每小时覆盖、全局配额、去重、90% 覆盖门禁和窗口级恢复。
- [ ] PR 3:统一字幕 track/revision/cue、pysubs2 导入导出、wavesurfer 波形与专业编辑器。
- [ ] PR 4:字幕审核暂停、AI 建议 revision、异步批量烧录、NVENC 回退和发送中心门禁。

当前验证重点:完成 PR 1 专项与全量回归;不要在 PR 1 宣称长直播选片或新字幕系统已经可用
当前验证重点:PR 2 完成专项与全量回归后进入字幕数据层;在 PR 3/PR 4 完成前,不宣称新字幕审核和批量烧录已经可用

## 2026-08-23 v2.1.0 主线同步后检查

Expand Down
50 changes: 50 additions & 0 deletions app/db/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,31 @@ def init_db() -> None:
FOREIGN KEY(task_id) REFERENCES tasks(id)
);

CREATE TABLE IF NOT EXISTS ai_analysis_windows (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
transcript_fingerprint TEXT NOT NULL,
provider TEXT NOT NULL,
model TEXT NOT NULL DEFAULT '',
window_index INTEGER NOT NULL,
start_seconds INTEGER NOT NULL,
end_seconds INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'queued',
attempt_count INTEGER NOT NULL DEFAULT 0,
result_json TEXT,
result_checksum TEXT,
error_message TEXT,
next_retry_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
completed_at TEXT,
UNIQUE(
task_id, transcript_fingerprint, provider, model,
window_index, start_seconds, end_seconds
),
FOREIGN KEY(task_id) REFERENCES tasks(id)
);

CREATE TABLE IF NOT EXISTS cut_runs (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
Expand Down Expand Up @@ -413,6 +438,7 @@ def init_db() -> None:
_restore_legacy_user_cancelled_publish_jobs(connection)
_migrate_workflow_jobs_table(connection)
_migrate_transcription_tables(connection)
_migrate_ai_analysis_windows_table(connection)
_migrate_cut_runs_table(connection)
_seed_ai_prompt_presets(connection)
_seed_subtitle_style_preset(connection)
Expand All @@ -438,6 +464,7 @@ def _requires_long_live_schema_migration(database_path) -> bool:
or "lease_owner" not in job_columns
or "transcription_runs" not in table_names
or "transcription_chunks" not in table_names
or "ai_analysis_windows" not in table_names
)


Expand Down Expand Up @@ -476,6 +503,7 @@ def _create_indexes(connection: sqlite3.Connection) -> None:
"CREATE INDEX IF NOT EXISTS idx_workflow_jobs_task_type_status ON workflow_jobs(task_id, job_type, status)",
"CREATE INDEX IF NOT EXISTS idx_transcription_runs_task_active ON transcription_runs(task_id, is_active, updated_at)",
"CREATE INDEX IF NOT EXISTS idx_transcription_chunks_run_status ON transcription_chunks(run_id, status, chunk_index)",
"CREATE INDEX IF NOT EXISTS idx_ai_analysis_windows_resume ON ai_analysis_windows(task_id, transcript_fingerprint, provider, model, status, window_index)",
"""CREATE UNIQUE INDEX IF NOT EXISTS uq_publish_jobs_active_clip_platform_mode
ON publish_jobs(output_clip_id, platform, publish_mode)
WHERE status IN ('DRAFT', 'WAITING', 'SCHEDULED', 'PUBLISHING', 'NEED_REVIEW')
Expand Down Expand Up @@ -1263,6 +1291,28 @@ def _migrate_transcription_tables(connection: sqlite3.Connection) -> None:
)


def _migrate_ai_analysis_windows_table(connection: sqlite3.Connection) -> None:
"""创建长直播 AI 窗口 checkpoint 表;成功窗口可跨进程复用。"""
connection.executescript(
"""
CREATE TABLE IF NOT EXISTS ai_analysis_windows (
id TEXT PRIMARY KEY, task_id TEXT NOT NULL,
transcript_fingerprint TEXT NOT NULL, provider TEXT NOT NULL,
model TEXT NOT NULL DEFAULT '', window_index INTEGER NOT NULL,
start_seconds INTEGER NOT NULL, end_seconds INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'queued', attempt_count INTEGER NOT NULL DEFAULT 0,
result_json TEXT, result_checksum TEXT, error_message TEXT, next_retry_at TEXT,
created_at TEXT NOT NULL, updated_at TEXT NOT NULL, completed_at TEXT,
UNIQUE(
task_id, transcript_fingerprint, provider, model,
window_index, start_seconds, end_seconds
),
FOREIGN KEY(task_id) REFERENCES tasks(id)
);
"""
)


def _migrate_cut_runs_table(connection: sqlite3.Connection) -> None:
"""cut_runs 表的列级迁移,兼容未来新增字段"""
columns = _get_table_columns(connection, "cut_runs")
Expand Down
Loading