fix: 修复语音消息取错、时长精度与转写不刷新

- 时长解析:改取 <voicemsg voicelength>(毫秒),不再误取 length(SILK 字节数)
- 时长显示:四舍五入对齐微信口径,有原生时长时不被解码时长覆盖
- 转写:重新识别改为强制重算,跳过身份级缓存短路(音频级缓存仍生效)
- 日报:语音累计秒数显示取整,不再出现小数
This commit is contained in:
Wxw-Gu
2026-09-20 15:40:01 +08:00
parent 9b82d29037
commit 9593ca0f54
21 changed files with 435 additions and 52 deletions
+14 -8
View File
@@ -73,12 +73,14 @@ export function VoicePlayer({
const audio = new Audio()
audio.preload = 'auto'
audio.src = blobUrl
audio.onloadedmetadata = () => {
if (Number.isFinite(audio.duration)) setAudioDuration(audio.duration)
}
audio.ontimeupdate = () => {
// Silk 解码出来的时长比微信的 length 系统性偏短(实测少 20–279ms),
// 用它覆盖会把刚补回来的精度又弄丢:有原生时长时一律不覆盖,没有才回退。
const useDecodedDuration = () => {
if (duration !== undefined) return
if (Number.isFinite(audio.duration)) setAudioDuration(audio.duration)
}
audio.onloadedmetadata = useDecodedDuration
audio.ontimeupdate = useDecodedDuration
audio.onended = () => {
setIsPlaying(false)
if (globalCurrentAudio === audio) {
@@ -89,7 +91,7 @@ export function VoicePlayer({
audioRef.current = audio
objectUrlRef.current = blobUrl
return audio
}, [])
}, [duration])
const handlePlayPause = useCallback(async () => {
if (loading) return
@@ -163,7 +165,8 @@ export function VoicePlayer({
setTranscribing(true)
try {
const result = await window.api.recognizeVoice(voiceReference)
// 用户主动触发:跳过身份级缓存,重新取音频(音频级缓存仍生效)。
const result = await window.api.recognizeVoice(voiceReference, { force: true })
if (result.success) {
setTranscript(result.transcript?.trim() || '未识别出文字')
setModelStatus(null)
@@ -204,8 +207,11 @@ export function VoicePlayer({
const formatDuration = (seconds: number | undefined): string => {
if (!seconds || !isFinite(seconds)) return '0:00'
const mins = Math.floor(seconds / 60)
const secs = Math.floor(seconds % 60)
// 微信的口径是四舍五入到整秒(length=4211ms 显示 4"),所以这里也必须 round 而非 floor。
// 先整体取整再拆分钟/秒,59.6 → "1:00" 而不会冒出 "0:60"。
const rounded = Math.round(seconds)
const mins = Math.floor(rounded / 60)
const secs = rounded % 60
return `${mins}:${secs.toString().padStart(2, '0')}`
}
+10 -6
View File
@@ -138,9 +138,13 @@ export const summarySender = (
export const summaryContent = (message: Message): string => {
const data = message.contentData
if (message.type === '语音' || data?.type === 'voice') {
// 语音时长来自 <voicemsg voicelength>(毫秒),是带小数的秒(1600ms → 1.6):
// 累加时保留精度,只在显示给人的文案里取整。
const durationSec = data?.type === 'voice' ? data.duration : undefined
const durationLabel = durationSec ? ` ${Math.round(durationSec)}秒` : ''
return message.voiceTranscript?.trim()
? `[语音${data?.type === 'voice' && data.duration ? ` ${data.duration}秒` : ''}] ${message.voiceTranscript.trim()}`
: `[语音${data?.type === 'voice' && data.duration ? ` ${data.duration}秒` : ''}]`
? `[语音${durationLabel}] ${message.voiceTranscript.trim()}`
: `[语音${durationLabel}]`
}
if (!data) return message.content?.trim() || `[${message.type || '消息'}]`
@@ -567,14 +571,14 @@ const buildMediaSection = async (
voiceHighlights.push({
title: '语音输出王',
sender: voiceLeaderboard[0].sender,
note: `共发送 ${voiceLeaderboard[0].count} 条语音,累计 ${voiceLeaderboard[0].durationSec} 秒。`
note: `共发送 ${voiceLeaderboard[0].count} 条语音,累计 ${Math.round(voiceLeaderboard[0].durationSec)} 秒。`
})
}
if (bestStreak && bestStreak.count >= 2) {
voiceHighlights.push({
title: '连续发言时刻',
sender: bestStreak.sender,
note: `${bestStreak.time} 连发 ${bestStreak.count} 条语音,共 ${bestStreak.duration} 秒。`
note: `${bestStreak.time} 连发 ${bestStreak.count} 条语音,共 ${Math.round(bestStreak.duration)} 秒。`
})
}
@@ -810,7 +814,7 @@ export const buildGroupReportFacts = async (
const factsPrompt = [
`报告模式:${reportMode === 'compact' ? '精简版(30秒可读完)' : '完整版(保留更多上下文)'}`,
`消息统计:共 ${transcriptRows.length} 条,活跃成员 ${speakerCounts.size} 人,图片 ${imageCount} 张,表情 ${stickerCount} 条,语音 ${voiceCount} 条(累计 ${voiceDurationSec} 秒)。`,
`消息统计:共 ${transcriptRows.length} 条,活跃成员 ${speakerCounts.size} 人,图片 ${imageCount} 张,表情 ${stickerCount} 条,语音 ${voiceCount} 条(累计 ${Math.round(voiceDurationSec)} 秒)。`,
activeTimeline ? `活跃时段:${activeTimeline}` : '',
// AI 图片理解结果(由 ImageInsightService 提供,缓存命中或已调用 Vision)
(media.visionGallery?.length ?? 0) > 0
@@ -824,7 +828,7 @@ export const buildGroupReportFacts = async (
voiceLeaderboard.length
? `语音榜:${voiceLeaderboard
.slice(0, 3)
.map((item) => `${item.sender} ${item.count} 条 / ${item.durationSec} 秒`)
.map((item) => `${item.sender} ${item.count} 条 / ${Math.round(item.durationSec)} 秒`)
.join(';')}`
: '',
collectQuestionCandidates(messages, contact, isGroup).length