feat: improve rpa video timing and narration
All checks were successful
Publish Python Package to Gitea / publish (push) Successful in 20s
All checks were successful
Publish Python Package to Gitea / publish (push) Successful in 20s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,7 +23,7 @@ __all__ = [
|
||||
"LaunchError",
|
||||
"GatewayDownError",
|
||||
]
|
||||
__version__ = "1.0.12"
|
||||
__version__ = "1.0.13"
|
||||
|
||||
|
||||
def __getattr__(name: str):
|
||||
|
||||
@@ -45,6 +45,10 @@ _VOICEOVER_MAX_CHARS = 60
|
||||
_VOICEOVER_DEDUP_SECONDS = 10.0
|
||||
_VOICEOVER_MIN_GAP_SECONDS = 2.0
|
||||
|
||||
_INTRO_BUFFER_SECONDS = 5.0
|
||||
_INTRO_STABILIZE_SECONDS = 1.0
|
||||
_OUTRO_BUFFER_SECONDS = 5.0
|
||||
|
||||
_VOICEOVER_SKIP_SUBSTRINGS = (
|
||||
"等待 1-5s",
|
||||
"跳过重复",
|
||||
@@ -54,6 +58,25 @@ _VOICEOVER_SKIP_SUBSTRINGS = (
|
||||
"失败",
|
||||
)
|
||||
|
||||
_VOICEOVER_IMPORTANT_SUBSTRINGS = (
|
||||
"开始",
|
||||
"打开",
|
||||
"启动",
|
||||
"定位",
|
||||
"输入",
|
||||
"点击",
|
||||
"搜索",
|
||||
"等待搜索结果",
|
||||
"检查登录状态",
|
||||
"处理验证码",
|
||||
"等待人工验证",
|
||||
"解析",
|
||||
"提取",
|
||||
"采集完成",
|
||||
"部分完成",
|
||||
"完成",
|
||||
)
|
||||
|
||||
# 明显技术日志:时间戳前缀、纯数字进度等
|
||||
_TECH_LOG_RE = re.compile(
|
||||
r"^\d{4}-\d{2}-\d{2}|^\[\w+\]|^DEBUG:|^INFO:|^WARN:|^ERROR:"
|
||||
@@ -173,10 +196,17 @@ def _voiceover_text_for_step(text: str) -> Optional[str]:
|
||||
return cleaned
|
||||
|
||||
|
||||
def _is_important_voiceover(text: str) -> bool:
|
||||
for important in _VOICEOVER_IMPORTANT_SUBSTRINGS:
|
||||
if important in text:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _select_voiceover_clips(
|
||||
entries: List[_StepEntry],
|
||||
) -> List[tuple[_StepEntry, str]]:
|
||||
"""按时间排序,去重并保证旁白间隔。"""
|
||||
"""按时间排序,去重并保证旁白间隔;关键动作可绕过最小间隔。"""
|
||||
result: List[tuple[_StepEntry, str]] = []
|
||||
last_text_at: Dict[str, float] = {}
|
||||
last_spoken_at = -_VOICEOVER_MIN_GAP_SECONDS
|
||||
@@ -188,7 +218,10 @@ def _select_voiceover_clips(
|
||||
prev = last_text_at.get(spoken)
|
||||
if prev is not None and entry.start - prev < _VOICEOVER_DEDUP_SECONDS:
|
||||
continue
|
||||
if entry.start - last_spoken_at < _VOICEOVER_MIN_GAP_SECONDS:
|
||||
if (
|
||||
not _is_important_voiceover(spoken)
|
||||
and entry.start - last_spoken_at < _VOICEOVER_MIN_GAP_SECONDS
|
||||
):
|
||||
continue
|
||||
last_text_at[spoken] = entry.start
|
||||
last_spoken_at = entry.start
|
||||
@@ -534,11 +567,13 @@ class RpaVideoSession:
|
||||
skill_data_dir: str,
|
||||
batch_id: str,
|
||||
title: str = "",
|
||||
closing_title: str = "",
|
||||
) -> None:
|
||||
self.skill_slug = skill_slug
|
||||
self.skill_data_dir = os.path.abspath(skill_data_dir)
|
||||
self.batch_id = batch_id
|
||||
self.title = title
|
||||
self.closing_title = closing_title
|
||||
self.enabled = _record_video_enabled()
|
||||
self.warnings: List[str] = []
|
||||
self.output_video_path: Optional[str] = None
|
||||
@@ -684,16 +719,29 @@ class RpaVideoSession:
|
||||
self._close_ffmpeg_record_log()
|
||||
time.sleep(0.3)
|
||||
|
||||
async def _safe_add_step(self, text: str, *, duration: float = 4.0) -> None:
|
||||
try:
|
||||
self.add_step(text, duration=duration)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def __aenter__(self) -> "RpaVideoSession":
|
||||
if self.enabled:
|
||||
self._t0 = time.monotonic()
|
||||
if self.title:
|
||||
self.add_step(self.title, duration=3.0)
|
||||
self._start_ffmpeg_capture()
|
||||
await asyncio.sleep(_INTRO_STABILIZE_SECONDS)
|
||||
if self.title:
|
||||
await self._safe_add_step(self.title, duration=4.0)
|
||||
intro_wait = _INTRO_BUFFER_SECONDS - _INTRO_STABILIZE_SECONDS
|
||||
if intro_wait > 0:
|
||||
await asyncio.sleep(intro_wait)
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb) -> None:
|
||||
if self.enabled:
|
||||
if self.closing_title:
|
||||
await self._safe_add_step(self.closing_title, duration=4.0)
|
||||
await asyncio.sleep(_OUTRO_BUFFER_SECONDS)
|
||||
self._stop_ffmpeg_capture()
|
||||
await self.finalize()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user