基于 SDK 新模块(前两个 commit)的 screencast 工具能力升级:
- runner.py:
· 新增 activate_window: bool = True 参数,录屏前自动调
activate_and_maximize_jiangchang_window,避免开头几秒录到 PowerShell/桌面
· 新增 monitor_index: Optional[int] = None 参数,透传给 ScreenRecorder
· 自带 SDK 路径兜底 _ensure_sdk_on_path,让 record_screencast.py 直接 python
跑时(不走 pytest conftest)也能 import jiangchang_desktop_sdk.window_win32
- recorder.py:
· ScreenRecorder.__init__ 加 monitor_index 参数
· _capture_loop 用 idx 选 monitor,越界兜底到 0
· None = 旧行为(monitors[0] 所有屏合并),1 = 主屏,2+ = 第 N 屏
· 多显示器场景推荐显式传 monitor_index=1 避免录出半边空白
- subtitle.py:
· SubtitleEngine.process_line 支持二元组(兼容旧脚本)和三元组(每条独立 duration)
· 长等待场景可以让某条字幕停留更久(如 30s)覆盖中间无日志的"字幕真空"
所有改动向后兼容,全部新增参数有默认值。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
2.0 KiB
Python
64 lines
2.0 KiB
Python
"""全屏截帧引擎,后台线程持续截图存为 PNG 帧序列。"""
|
||
from __future__ import annotations
|
||
|
||
import threading
|
||
import time
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
import mss
|
||
import mss.tools
|
||
|
||
|
||
class ScreenRecorder:
|
||
def __init__(
|
||
self,
|
||
frames_dir: str,
|
||
fps: int = 10,
|
||
monitor_index: Optional[int] = None,
|
||
):
|
||
"""
|
||
Args:
|
||
monitor_index: mss 显示器索引;None=monitors[0](所有屏合并区域,
|
||
旧行为),1=主屏,2+=第 N 屏。多显示器场景推荐显式传 1。
|
||
"""
|
||
self._frames_dir = Path(frames_dir)
|
||
self._fps = fps
|
||
self._monitor_index = monitor_index
|
||
self._stop_event = threading.Event()
|
||
self._thread: threading.Thread | None = None
|
||
self._frame_count = 0
|
||
|
||
def start(self) -> None:
|
||
self._frames_dir.mkdir(parents=True, exist_ok=True)
|
||
self._stop_event.clear()
|
||
self._frame_count = 0
|
||
self._thread = threading.Thread(target=self._capture_loop, daemon=True)
|
||
self._thread.start()
|
||
|
||
def stop(self) -> None:
|
||
self._stop_event.set()
|
||
if self._thread:
|
||
self._thread.join(timeout=10)
|
||
|
||
@property
|
||
def frame_count(self) -> int:
|
||
return self._frame_count
|
||
|
||
def _capture_loop(self) -> None:
|
||
interval = 1.0 / self._fps
|
||
with mss.mss() as sct:
|
||
idx = self._monitor_index if self._monitor_index is not None else 0
|
||
if idx < 0 or idx >= len(sct.monitors):
|
||
idx = 0 # 越界兜底
|
||
monitor = sct.monitors[idx]
|
||
while not self._stop_event.is_set():
|
||
t0 = time.perf_counter()
|
||
img = sct.grab(monitor)
|
||
path = self._frames_dir / f"frame_{self._frame_count:06d}.png"
|
||
mss.tools.to_png(img.rgb, img.size, output=str(path))
|
||
self._frame_count += 1
|
||
elapsed = time.perf_counter() - t0
|
||
sleep = max(0.0, interval - elapsed)
|
||
time.sleep(sleep)
|