Adds tools/screencast/ — a reusable screen-recording pipeline for skill demo videos. Wraps a pytest desktop E2E run with: - ScreenRecorder: mss-based full-screen frame capture (background thread) - SubtitleEngine: stdout keyword → timestamped SRT generation - compose_video: FFmpeg filter_complex merge (video + subtitles + BGM) - run_screencast: main orchestrator wiring all three layers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
56 lines
1.9 KiB
Python
56 lines
1.9 KiB
Python
"""字幕引擎:根据 stdout 关键词打时间戳,生成 SRT 字幕文件。"""
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import List, Tuple
|
|
|
|
|
|
@dataclass
|
|
class _Entry:
|
|
start: float # 距录制开始的秒数
|
|
text: str
|
|
duration: float = 5.0 # 每条字幕默认显示 5 秒
|
|
|
|
|
|
class SubtitleEngine:
|
|
def __init__(self, script: List[Tuple[str, str]], default_duration: float = 5.0):
|
|
"""
|
|
script: [(关键词, 字幕文案), ...]
|
|
关键词大小写不敏感,每个关键词只触发一次。
|
|
"""
|
|
self._script = script
|
|
self._default_duration = default_duration
|
|
self._entries: List[_Entry] = []
|
|
self._matched: set[str] = set()
|
|
self._t0: float = 0.0
|
|
|
|
def set_start_time(self, t: float) -> None:
|
|
self._t0 = t
|
|
|
|
def process_line(self, line: str) -> None:
|
|
lower = line.lower()
|
|
for keyword, text in self._script:
|
|
if keyword in self._matched:
|
|
continue
|
|
if keyword.lower() in lower:
|
|
elapsed = time.time() - self._t0
|
|
self._entries.append(_Entry(start=elapsed, text=text, duration=self._default_duration))
|
|
self._matched.add(keyword)
|
|
|
|
def generate_srt(self, output_path: str) -> None:
|
|
def _fmt(s: float) -> str:
|
|
h = int(s // 3600)
|
|
m = int((s % 3600) // 60)
|
|
sec = int(s % 60)
|
|
ms = int((s % 1) * 1000)
|
|
return f"{h:02d}:{m:02d}:{sec:02d},{ms:03d}"
|
|
|
|
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
for i, e in enumerate(self._entries, 1):
|
|
f.write(f"{i}\n")
|
|
f.write(f"{_fmt(e.start)} --> {_fmt(e.start + e.duration)}\n")
|
|
f.write(f"{e.text}\n\n")
|