feat(record): P4 暂停/继续 —— record pause/resume 子命令 + CaptureHandle.pause/resume

SPEC_RealtimeTranscript_UX_V1 §五:
- cli.py:新增  / (状态文件 pid
  定位 + SIGUSR1 通知前台进程);前台进程注册 SIGUSR1 handler 切换
  handle.pause()/resume();状态文件记录 paused 供 status 显示
- audio_capture.py:CaptureHandle.pause()/resume() —— 采集回调丢弃帧
  + 向 Cloud WS 发 pause/resume 控制消息(云端计费暂停);is_paused 属性
- record status 显示 ⏸️/🎙️ 状态
This commit is contained in:
2026-08-09 17:54:48 +08:00
parent d760aa70b5
commit fca49f8643
2 changed files with 88 additions and 1 deletions
+50 -1
View File
@@ -56,6 +56,13 @@ def _save_state(state: dict) -> None:
json.dump(state, f, ensure_ascii=False)
def _patch_state(patch: dict) -> None:
"""合并更新状态文件(record start 进程内调用)。"""
state = _load_state() or {}
state.update(patch)
_save_state(state)
def _clear_state() -> None:
try:
_STATE_FILE.unlink()
@@ -140,6 +147,17 @@ async def _run_capture(capture_audio, ws_url, chat_id, meeting_id, source, on_te
loop.add_signal_handler(signal.SIGINT, stop_event.set)
loop.add_signal_handler(signal.SIGTERM, stop_event.set)
# P4SIGUSR1 = 暂停/继续切换(由 `mind record pause/resume` 触发)
def _toggle_pause() -> None:
if handle.is_paused:
handle.resume()
_patch_state({"paused": False})
else:
handle.pause()
_patch_state({"paused": True})
loop.add_signal_handler(signal.SIGUSR1, _toggle_pause)
try:
await stop_event.wait()
finally:
@@ -159,6 +177,36 @@ def record_stop():
click.echo(f"⏹️ 已发送停止信号 pid={state['pid']}")
@record.command(name="pause")
def record_pause():
"""暂停录音(向 record start 进程发 SIGUSR1)。"""
state = _load_state()
if not state or not _pid_alive(state.get("pid", -1)):
click.echo("⏹️ 未在录音")
return
if state.get("paused"):
click.echo("⏸️ 已处于暂停")
return
os.kill(state["pid"], signal.SIGUSR1)
_patch_state({"paused": True})
click.echo(f"⏸️ 已暂停 pid={state['pid']}")
@record.command(name="resume")
def record_resume():
"""继续录音(向 record start 进程发 SIGUSR1)。"""
state = _load_state()
if not state or not _pid_alive(state.get("pid", -1)):
click.echo("⏹️ 未在录音")
return
if not state.get("paused"):
click.echo("🎙️ 未处于暂停")
return
os.kill(state["pid"], signal.SIGUSR1)
_patch_state({"paused": False})
click.echo(f"🎙️ 已继续 pid={state['pid']}")
@record.command(name="status")
def record_status():
"""查看录音状态。"""
@@ -167,7 +215,8 @@ def record_status():
click.echo("⏹️ 未在录音")
return
duration = round(time.time() - state.get("startedAt", time.time()), 1)
click.echo(f"🎙 录音中 duration={duration}s chatId={state.get('chatId', '')} "
paused = " 已暂停" if state.get("paused") else "🎙️ 录音中"
click.echo(f"{paused} duration={duration}s chatId={state.get('chatId', '')} "
f"source={state.get('source')} meetingId={state.get('meetingId')}")