feat(caption): stream live captions and polish finished lines in context

Replaces the fixed six-second batches with a streaming track per audio
source: the uncommitted audio is re-recognised every second and sent as a
partial with its agreed (stable) prefix, a short pause finalises the line,
and long unbroken speech is committed at Whisper segment boundaries. Idle
audio is trimmed so silence cannot produce invented sentences.

Finished lines are corrected by the local model against the previous lines
and replaced in place; edits that change too much are rejected. The
behaviour can be switched off in Settings.
This commit is contained in:
Yun Chan 2026-09-24 20:22:21 +09:00
parent db8d9448a3
commit 39b8e7448e
28 changed files with 827 additions and 217 deletions

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "{{app}} konnte nicht zu Ausschlüssen hinzugefügt werden.",
"popup.caption.loading": "Sprachmodell wird vorbereitet…",
"popup.caption.waiting": "Hört zu… der erste Untertitel kann einige Sekunden dauern",
"popup.caption.dragHint": "Ziehen zum Verschieben · Doppelklick setzt zurück"
"popup.caption.dragHint": "Ziehen zum Verschieben · Doppelklick setzt zurück",
"settings.captionRefine": "Untertitel im Kontext glätten",
"settings.captionRefine.desc": "Die lokale KI korrigiert Leerzeichen, Satzzeichen und falsch verstandene Wörter anhand der umgebenden Zeilen."
}

View file

@ -1903,5 +1903,7 @@
"input.feedback.excludedFailed": "Exclusions could not be saved.",
"input.feedback.recommendationFailed": "Could not add {{app}} to exclusions.",
"popup.caption.waiting": "Listening… the first caption can take a few seconds",
"popup.caption.dragHint": "Drag to move · double-click to reset"
"popup.caption.dragHint": "Drag to move · double-click to reset",
"settings.captionRefine": "Polish captions with context",
"settings.captionRefine.desc": "The local AI fixes spacing, punctuation and misheard words in finished captions using the surrounding lines."
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "No se pudo añadir {{app}} a las exclusiones.",
"popup.caption.loading": "Preparando el modelo de voz…",
"popup.caption.waiting": "Escuchando… el primer subtítulo puede tardar unos segundos",
"popup.caption.dragHint": "Arrastra para mover · doble clic para restablecer"
"popup.caption.dragHint": "Arrastra para mover · doble clic para restablecer",
"settings.captionRefine": "Pulir subtítulos con contexto",
"settings.captionRefine.desc": "La IA local corrige espacios, puntuación y palabras mal oídas usando las líneas cercanas."
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "Impossible d’ajouter {{app}} aux exclusions.",
"popup.caption.loading": "Préparation du modèle vocal…",
"popup.caption.waiting": "Écoute… le premier sous-titre peut prendre quelques secondes",
"popup.caption.dragHint": "Glisser pour déplacer · double-clic pour réinitialiser"
"popup.caption.dragHint": "Glisser pour déplacer · double-clic pour réinitialiser",
"settings.captionRefine": "Affiner les sous-titres selon le contexte",
"settings.captionRefine.desc": "L’IA locale corrige les espaces, la ponctuation et les mots mal entendus d’après les lignes voisines."
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "{{app}} を除外に追加できませんでした。",
"popup.caption.loading": "音声モデルを準備中…",
"popup.caption.waiting": "聞き取り中… 最初の字幕まで数秒かかることがあります",
"popup.caption.dragHint": "ドラッグで移動 · ダブルクリックで元の位置"
"popup.caption.dragHint": "ドラッグで移動 · ダブルクリックで元の位置",
"settings.captionRefine": "文脈で字幕を整える",
"settings.captionRefine.desc": "確定した字幕の区切り・句読点・聞き間違いを、ローカルAIが前後の文脈に合わせて直します。"
}

View file

@ -1910,5 +1910,7 @@
"input.feedback.excludedFailed": "제외 목록을 저장하지 못했습니다.",
"input.feedback.recommendationFailed": "{{app}}을(를) 제외 목록에 추가하지 못했습니다.",
"popup.caption.waiting": "듣는 중… 첫 자막까지 몇 초 걸릴 수 있어요",
"popup.caption.dragHint": "끌어서 이동 · 더블클릭하면 원위치"
"popup.caption.dragHint": "끌어서 이동 · 더블클릭하면 원위치",
"settings.captionRefine": "자막 문맥 다듬기",
"settings.captionRefine.desc": "확정된 자막을 로컬 AI가 앞뒤 문맥에 맞게 띄어쓰기·문장부호·잘못 들은 단어를 고칩니다."
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "Não foi possível adicionar {{app}} às exclusões.",
"popup.caption.loading": "Preparando o modelo de voz…",
"popup.caption.waiting": "Ouvindo… a primeira legenda pode levar alguns segundos",
"popup.caption.dragHint": "Arraste para mover · clique duplo para restaurar"
"popup.caption.dragHint": "Arraste para mover · clique duplo para restaurar",
"settings.captionRefine": "Refinar legendas pelo contexto",
"settings.captionRefine.desc": "A IA local corrige espaços, pontuação e palavras mal ouvidas usando as linhas próximas."
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "Не удалось добавить {{app}} в исключения.",
"popup.caption.loading": "Подготовка речевой модели…",
"popup.caption.waiting": "Слушаю… первый субтитр может появиться через несколько секунд",
"popup.caption.dragHint": "Перетащите, чтобы переместить · двойной щелчок — сброс"
"popup.caption.dragHint": "Перетащите, чтобы переместить · двойной щелчок — сброс",
"settings.captionRefine": "Уточнять субтитры по контексту",
"settings.captionRefine.desc": "Локальный ИИ исправляет пробелы, пунктуацию и ослышки в готовых субтитрах по соседним строкам."
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "ไม่สามารถเพิ่ม {{app}} ในรายการยกเว้นได้",
"popup.caption.loading": "กำลังเตรียมโมเดลเสียง…",
"popup.caption.waiting": "กำลังฟัง… คำบรรยายแรกอาจใช้เวลาสักครู่",
"popup.caption.dragHint": "ลากเพื่อย้าย · ดับเบิลคลิกเพื่อรีเซ็ต"
"popup.caption.dragHint": "ลากเพื่อย้าย · ดับเบิลคลิกเพื่อรีเซ็ต",
"settings.captionRefine": "ขัดเกลาคำบรรยายตามบริบท",
"settings.captionRefine.desc": "AI ในเครื่องจะแก้เว้นวรรค เครื่องหมายวรรคตอน และคำที่ได้ยินผิด โดยดูจากบรรทัดรอบข้าง"
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "Không thể thêm {{app}} vào danh sách loại trừ.",
"popup.caption.loading": "Đang chuẩn bị mô hình giọng nói…",
"popup.caption.waiting": "Đang nghe… phụ đề đầu tiên có thể mất vài giây",
"popup.caption.dragHint": "Kéo để di chuyển · nhấp đúp để đặt lại"
"popup.caption.dragHint": "Kéo để di chuyển · nhấp đúp để đặt lại",
"settings.captionRefine": "Chỉnh phụ đề theo ngữ cảnh",
"settings.captionRefine.desc": "AI cục bộ sửa khoảng trắng, dấu câu và từ nghe nhầm dựa trên các dòng xung quanh."
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "無法將 {{app}} 加入排除清單。",
"popup.caption.loading": "正在準備語音模型…",
"popup.caption.waiting": "聆聽中… 第一則字幕可能需要幾秒鐘",
"popup.caption.dragHint": "拖曳以移動 · 按兩下還原"
"popup.caption.dragHint": "拖曳以移動 · 按兩下還原",
"settings.captionRefine": "依上下文潤飾字幕",
"settings.captionRefine.desc": "本機 AI 會依上下文修正已確定字幕的空格、標點與聽錯的詞。"
}

View file

@ -522,5 +522,7 @@
"input.feedback.recommendationFailed": "无法将 {{app}} 添加到排除列表。",
"popup.caption.loading": "正在准备语音模型…",
"popup.caption.waiting": "正在聆听… 第一条字幕可能需要几秒钟",
"popup.caption.dragHint": "拖动以移动 · 双击复位"
"popup.caption.dragHint": "拖动以移动 · 双击复位",
"settings.captionRefine": "按上下文润色字幕",
"settings.captionRefine.desc": "本地 AI 会根据上下文修正已确定字幕的空格、标点和听错的词。"
}