Skip to content

feat: 重新转录 — last-N recording history + replay through the live pipeline - #47

Merged
giraphant merged 9 commits into
mainfrom
feat/retranscribe
Jul 24, 2026
Merged

giraphant merged 9 commits into
mainfrom
feat/retranscribe

Conversation

@giraphant

Copy link
Copy Markdown
Owner

What

  • 每次听写结束把完整 WAV + 转录文本归档到 Caches/Dousha/history/(保留最近 N 条,设置里可调 1–50,默认 5;转录失败也归档并标红——救援场景)
  • 菜单栏新增「重新转录上次」;设置新增「历史记录」面板:每条可重新转录 / 复制文本 / 在 Finder 显示
  • 重新转录 = 把历史 WAV 通过 MultiEngineBackend 的 hub-less 回放模式(突发推送 PCM)走完整现有管线(多引擎路由 → 格式化 → 本地纠错 → 可选润色),结果只进剪贴板,不 ⌘V 注入

Key changes

  • RecordingHistoryStore:wav + sidecar JSON,自愈式列表(丢文件不列、孤儿 wav 容错)、按上限裁剪
  • MultiEngineBackend:replayWAV 参数 + forReplay 工厂;stop() 先 await startTask(含回放推送)保证音频完整送达引擎
  • RecordingController.retranscribe(id:url:):从 .transcribing 直接复用状态机;isReplay 只在注入一步分叉;pendingErrorMessage 门闩防止失败录音在归档完成前被重转毁掉
  • SonioxASR:修复 EOS-before-audio 竞态(stop 追上 openStream 时等 config 上线,镜像 DoubaoASR 的 startup grace)——回放路径 100% 命中的 bug
  • 设置面板重转按钮改响应式启用(原先在 .transcribing 渲染后卡灰)

Testing

  • 394/394 单测通过(新增 store/控制器回放/裁剪/竞态门闩等 ~25 条)
  • 真机验收通过:录音→历史→重转→剪贴板、断网失败条目→联网救援、按钮灰置恢复
  • 已知遗留:Doubao 对回放会话回 SessionFailed 50700000(现场会话正常),router 兜底 Soniox 文本,跟进见 录音鲁棒性:断网/引擎断连时音频持续录制 + 自动救援 #46

🤖 Generated with Claude Code

giraphant and others added 9 commits July 24, 2026 21:13
…o limit)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- RecordingEnvironment gains makeReplayBackend/copyToClipboard/saveHistory/
  updateHistory closures (3a).
- New isReplay + replayHistoryId state (3b); start() resets both (3c) and
  delegates its stream loop to a shared consume() (3d), reused by the new
  retranscribe()/canRetranscribe (3e).
- handleFinal now saves history on every live final (success/empty/error-tail)
  and updates the replay's history entry in place instead (3f).
- injectAndFinish copies to clipboard instead of ⌘V-injecting during replay (3g).

Also wires AppDelegate's single RecordingEnvironment call site (the only other
caller) with real implementations of the 4 new closures using the
already-merged Task 1-3 infra (MultiEngineBackend.forReplay, RecordingHistoryStore,
NSPasteboard) -- required to keep the app target compiling; not in the
original task-4 file list but mechanically forced by the struct change.
Final-review fixes for feat/retranscribe:
- gate canRetranscribe on the trailing .final that archives a failed
  live session, so a rescue retranscribe can't destroy its own audio
  or get dropped by the 3s error auto-reset (RecordingController)
- missing-file retranscribe now removes the dead history entry
  instead of surfacing a repeated error (AppDelegate)
- fix a 1-byte heap overflow in int16Buffer's memcpy on odd-length
  chunks (MultiEngineBackend)
- log (not silently swallow) a failed sharedWAV replay copy, pin the
  history id formatter to UTC, recover an orphaned sidecar on
  updateTranscript, and refresh a stale wantsWAV doc comment
Replay stops microseconds after openStream, hitting paths live recording
never exercises: Soniox's stop() flushed nothing (canSendAudio still
false) and sent end-of-audio ahead of the buffered PCM — the server
answered 'No audio received' on every replay. stop() now waits (bounded
3 s) on a per-session configSent channel, mirroring DoubaoASR's startup
grace.

The settings history pane rendered its retranscribe buttons while the
controller was still .transcribing (the history-changed notification
fires at save time) and nothing re-rendered on return to idle — buttons
stuck grey. The enabled state is now a @State mirror refreshed by a new
.doushaRecordingStatusChanged post from the applyStatusToHUD closure.

Evidence: ~/Library/Logs/Dousha/dousha.log 21:15:10Z / 21:16:51Z replay
attempts — 'config sent pcmBufferBytes=129562' → 'sent end-of-audio
marker' → 'server error: No audio received', picked=doubao len=0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@giraphant
giraphant merged commit 98dfcc2 into main Jul 24, 2026
1 check passed
@giraphant
giraphant deleted the feat/retranscribe branch July 24, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant