feat: 优化聊天导出进度与媒体处理

1. 拆分读取、解析、转写、媒体处理、写入和压缩阶段,完善任务进度展示。

2. 增量导出复用语音资源与转写结果,并为缺失转写补充识别。

3. 稳定远程头像文件名并保留同源头像版本更新。

4. 支持音频附件直接播放、新窗口打开附件,并解码分享标题 XML 实体。

5. 补充导出进度、语音、头像、附件和消息解析回归测试。
This commit is contained in:
Nanin
2026-08-05 23:22:47 +08:00
parent 3c59fb64e9
commit e3615c0153
16 changed files with 495 additions and 103 deletions
+13 -4
View File
@@ -1113,9 +1113,16 @@ const renderExportScript = (name: string): string => `
return ''
}
const playableAudioPattern = /\\.(?:mp3|wav|m4a|aac|ogg|oga|opus|flac|webm)(?:$|[?#])/i
const playableAudioFile = (name, url) =>
playableAudioPattern.test(String(name || '')) || playableAudioPattern.test(String(url || ''))
const renderAudioPlayer = (source) =>
'<div class="audio-wrap"><audio class="audio" controls preload="metadata" src="' + source + '"></audio></div>'
const renderMessage = (message, archiveIndex) => {
const data = message.contentData || {}
const mediaUrl = message.exportMediaUrl ? esc(message.exportMediaUrl) : ''
const rawMediaUrl = message.exportMediaUrl ? String(message.exportMediaUrl) : ''
const mediaUrl = rawMediaUrl ? esc(rawMediaUrl) : ''
const mediaType = message.exportMediaType || data.type
let media = ''
if (mediaUrl && mediaType === 'image') {
@@ -1125,11 +1132,13 @@ const renderExportScript = (name: string): string => `
} else if (mediaUrl && mediaType === 'sticker') {
media = '<img class="media-image" data-preview src="' + mediaUrl + '" alt="表情包">'
} else if (mediaUrl && mediaType === 'file') {
const fileName = esc(message.exportMediaName || data.title || '下载文件')
media = '<a class="file-attachment" href="' + mediaUrl + '" download><span>📎</span><span>' + fileName + '</span></a>'
const rawFileName = message.exportMediaName || data.title || '打开文件'
const fileName = esc(rawFileName)
media = '<a class="file-attachment" href="' + mediaUrl + '" target="_blank" rel="noreferrer noopener"><span>📎</span><span>' + fileName + '</span></a>' +
(playableAudioFile(rawFileName, rawMediaUrl) ? renderAudioPlayer(mediaUrl) : '')
}
const audio = message.voiceDataUrl
? '<div class="audio-wrap"><audio class="audio" controls preload="metadata" src="' + esc(message.voiceDataUrl) + '"></audio></div>'
? renderAudioPlayer(esc(message.voiceDataUrl))
: ''
const voiceTranscript = message.voiceTranscript
? '<div class="voice-transcript">' + esc(message.voiceTranscript) + '</div>'