mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
fix: 清理导出服务日志并简化whisper接口参数
- 移除exportService中的冗余console日志输出 - 简化whisper API接口,移除downloadModel和getModelStatus的payload参数 - 清理图片、表情、语音导出过程中的调试日志 - 移除数据库查询和媒体处理中的详细日志记录 - 优化代码可读性,减少控制台输出噪音
This commit is contained in:
@@ -184,10 +184,10 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
whisper: {
|
whisper: {
|
||||||
downloadModel: (payload: { modelName: string; downloadDir?: string; source?: string }) =>
|
downloadModel: () =>
|
||||||
ipcRenderer.invoke('whisper:downloadModel', payload),
|
ipcRenderer.invoke('whisper:downloadModel'),
|
||||||
getModelStatus: (payload: { modelName: string; downloadDir?: string }) =>
|
getModelStatus: () =>
|
||||||
ipcRenderer.invoke('whisper:getModelStatus', payload),
|
ipcRenderer.invoke('whisper:getModelStatus'),
|
||||||
onDownloadProgress: (callback: (payload: { modelName: string; downloadedBytes: number; totalBytes?: number; percent?: number }) => void) => {
|
onDownloadProgress: (callback: (payload: { modelName: string; downloadedBytes: number; totalBytes?: number; percent?: number }) => void) => {
|
||||||
ipcRenderer.on('whisper:downloadProgress', (_, payload) => callback(payload))
|
ipcRenderer.on('whisper:downloadProgress', (_, payload) => callback(payload))
|
||||||
return () => ipcRenderer.removeAllListeners('whisper:downloadProgress')
|
return () => ipcRenderer.removeAllListeners('whisper:downloadProgress')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as http from 'http'
|
import * as http from 'http'
|
||||||
import * as https from 'https'
|
import * as https from 'https'
|
||||||
@@ -364,7 +364,6 @@ class ExportService {
|
|||||||
|
|
||||||
return `[${callType}]`
|
return `[${callType}]`
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[ExportService] Failed to parse VOIP message:', e)
|
|
||||||
return '[通话]'
|
return '[通话]'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,7 +416,6 @@ class ExportService {
|
|||||||
if (localType === 3 && options.exportImages) {
|
if (localType === 3 && options.exportImages) {
|
||||||
const result = await this.exportImage(msg, sessionId, mediaDir)
|
const result = await this.exportImage(msg, sessionId, mediaDir)
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log('[ExportService] 图片导出成功:', result.relativePath)
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -438,7 +436,6 @@ class ExportService {
|
|||||||
if (localType === 47 && options.exportEmojis) {
|
if (localType === 47 && options.exportEmojis) {
|
||||||
const result = await this.exportEmoji(msg, sessionId, mediaDir)
|
const result = await this.exportEmoji(msg, sessionId, mediaDir)
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log('[ExportService] 表情导出成功:', result.relativePath)
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -461,12 +458,9 @@ class ExportService {
|
|||||||
const imageDatName = msg.imageDatName
|
const imageDatName = msg.imageDatName
|
||||||
|
|
||||||
if (!imageMd5 && !imageDatName) {
|
if (!imageMd5 && !imageDatName) {
|
||||||
console.log('[ExportService] 图片消息缺少 md5 和 datName:', msg.localId)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[ExportService] 导出图片:', { localId: msg.localId, imageMd5, imageDatName, sessionId })
|
|
||||||
|
|
||||||
const result = await imageDecryptService.decryptImage({
|
const result = await imageDecryptService.decryptImage({
|
||||||
sessionId,
|
sessionId,
|
||||||
imageMd5,
|
imageMd5,
|
||||||
@@ -524,7 +518,6 @@ class ExportService {
|
|||||||
|
|
||||||
return null
|
return null
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[ExportService] 导出图片失败:', e)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,19 +538,15 @@ class ExportService {
|
|||||||
|
|
||||||
// 如果已存在则跳过
|
// 如果已存在则跳过
|
||||||
if (fs.existsSync(destPath)) {
|
if (fs.existsSync(destPath)) {
|
||||||
console.log('[ExportService] 语音已存在:', destPath)
|
|
||||||
return {
|
return {
|
||||||
relativePath: `media/voices/${fileName}`,
|
relativePath: `media/voices/${fileName}`,
|
||||||
kind: 'voice'
|
kind: 'voice'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[ExportService] 导出语音:', { localId: msg.localId, sessionId })
|
|
||||||
|
|
||||||
// 调用 chatService 获取语音数据
|
// 调用 chatService 获取语音数据
|
||||||
const voiceResult = await chatService.getVoiceData(sessionId, msgId)
|
const voiceResult = await chatService.getVoiceData(sessionId, msgId)
|
||||||
if (!voiceResult.success || !voiceResult.data) {
|
if (!voiceResult.success || !voiceResult.data) {
|
||||||
console.log('[ExportService] 获取语音数据失败:', voiceResult.error)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,13 +554,11 @@ class ExportService {
|
|||||||
const wavBuffer = Buffer.from(voiceResult.data, 'base64')
|
const wavBuffer = Buffer.from(voiceResult.data, 'base64')
|
||||||
fs.writeFileSync(destPath, wavBuffer)
|
fs.writeFileSync(destPath, wavBuffer)
|
||||||
|
|
||||||
console.log('[ExportService] 语音导出成功:', destPath)
|
|
||||||
return {
|
return {
|
||||||
relativePath: `media/voices/${fileName}`,
|
relativePath: `media/voices/${fileName}`,
|
||||||
kind: 'voice'
|
kind: 'voice'
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[ExportService] 导出语音失败:', e)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -587,7 +574,6 @@ class ExportService {
|
|||||||
}
|
}
|
||||||
return '[语音消息 - 转文字失败]'
|
return '[语音消息 - 转文字失败]'
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[ExportService] 语音转文字失败:', e)
|
|
||||||
return '[语音消息 - 转文字失败]'
|
return '[语音消息 - 转文字失败]'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -625,7 +611,6 @@ class ExportService {
|
|||||||
|
|
||||||
// 如果已存在则跳过
|
// 如果已存在则跳过
|
||||||
if (fs.existsSync(destPath)) {
|
if (fs.existsSync(destPath)) {
|
||||||
console.log('[ExportService] 表情已存在:', destPath)
|
|
||||||
return {
|
return {
|
||||||
relativePath: `media/emojis/${fileName}`,
|
relativePath: `media/emojis/${fileName}`,
|
||||||
kind: 'emoji'
|
kind: 'emoji'
|
||||||
@@ -634,22 +619,18 @@ class ExportService {
|
|||||||
|
|
||||||
// 下载表情
|
// 下载表情
|
||||||
if (emojiUrl) {
|
if (emojiUrl) {
|
||||||
console.log('[ExportService] 开始下载表情:', emojiUrl)
|
|
||||||
const downloaded = await this.downloadFile(emojiUrl, destPath)
|
const downloaded = await this.downloadFile(emojiUrl, destPath)
|
||||||
if (downloaded) {
|
if (downloaded) {
|
||||||
console.log('[ExportService] 表情下载成功:', destPath)
|
|
||||||
return {
|
return {
|
||||||
relativePath: `media/emojis/${fileName}`,
|
relativePath: `media/emojis/${fileName}`,
|
||||||
kind: 'emoji'
|
kind: 'emoji'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('[ExportService] 表情下载失败:', emojiUrl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[ExportService] 导出表情失败:', e)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -823,12 +804,10 @@ class ExportService {
|
|||||||
// 图片消息
|
// 图片消息
|
||||||
imageMd5 = this.extractImageMd5(content)
|
imageMd5 = this.extractImageMd5(content)
|
||||||
imageDatName = this.extractImageDatName(content)
|
imageDatName = this.extractImageDatName(content)
|
||||||
console.log('[ExportService] 提取图片字段:', { localId, imageMd5, imageDatName })
|
|
||||||
} else if (localType === 47 && content) {
|
} else if (localType === 47 && content) {
|
||||||
// 动画表情
|
// 动画表情
|
||||||
emojiCdnUrl = this.extractEmojiUrl(content)
|
emojiCdnUrl = this.extractEmojiUrl(content)
|
||||||
emojiMd5 = this.extractEmojiMd5(content)
|
emojiMd5 = this.extractEmojiMd5(content)
|
||||||
console.log('[ExportService] 提取表情字段:', { localId, emojiMd5, emojiCdnUrl: emojiCdnUrl?.substring(0, 100) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.push({
|
rows.push({
|
||||||
@@ -1233,7 +1212,6 @@ class ExportService {
|
|||||||
|
|
||||||
return { success: true }
|
return { success: true }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('ExportService: 导出失败:', e)
|
|
||||||
return { success: false, error: String(e) }
|
return { success: false, error: String(e) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1348,7 +1326,6 @@ class ExportService {
|
|||||||
|
|
||||||
return { success: true }
|
return { success: true }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('ExportService: 导出失败:', e)
|
|
||||||
return { success: false, error: String(e) }
|
return { success: false, error: String(e) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1570,13 +1547,6 @@ class ExportService {
|
|||||||
|
|
||||||
// 调试日志
|
// 调试日志
|
||||||
if (msg.localType === 3 || msg.localType === 47) {
|
if (msg.localType === 3 || msg.localType === 47) {
|
||||||
console.log('[ExportService] 媒体消息填充表格:', {
|
|
||||||
localId: msg.localId,
|
|
||||||
localType: msg.localType,
|
|
||||||
hasMediaItem: !!mediaItem,
|
|
||||||
mediaRelativePath: mediaItem?.relativePath,
|
|
||||||
contentValue: contentValue?.substring(0, 100)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
worksheet.getCell(currentRow, 1).value = i + 1
|
worksheet.getCell(currentRow, 1).value = i + 1
|
||||||
@@ -1628,8 +1598,6 @@ class ExportService {
|
|||||||
|
|
||||||
return { success: true }
|
return { success: true }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('ExportService: 导出 Excel 失败:', e)
|
|
||||||
|
|
||||||
// 处理文件被占用的错误
|
// 处理文件被占用的错误
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
if (e.message.includes('EBUSY') || e.message.includes('resource busy') || e.message.includes('locked')) {
|
if (e.message.includes('EBUSY') || e.message.includes('resource busy') || e.message.includes('locked')) {
|
||||||
@@ -1721,3 +1689,4 @@ class ExportService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const exportService = new ExportService()
|
export const exportService = new ExportService()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
import { existsSync, mkdirSync, statSync, unlinkSync, createWriteStream } from 'fs'
|
import { existsSync, mkdirSync, statSync, unlinkSync, createWriteStream } from 'fs'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import * as https from 'https'
|
import * as https from 'https'
|
||||||
@@ -98,7 +98,6 @@ export class VoiceTranscribeService {
|
|||||||
sizeBytes: totalSize
|
sizeBytes: totalSize
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[VoiceTranscribe] getModelStatus error:', error)
|
|
||||||
return { success: false, error: String(error) }
|
return { success: false, error: String(error) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +124,6 @@ export class VoiceTranscribeService {
|
|||||||
const vadPath = this.resolveModelPath((SENSEVOICE_MODEL.files as any).vad)
|
const vadPath = this.resolveModelPath((SENSEVOICE_MODEL.files as any).vad)
|
||||||
|
|
||||||
// 下载模型文件 (40%)
|
// 下载模型文件 (40%)
|
||||||
console.info('[VoiceTranscribe] 开始下载模型文件...')
|
|
||||||
await this.downloadToFile(
|
await this.downloadToFile(
|
||||||
MODEL_DOWNLOAD_URLS.model,
|
MODEL_DOWNLOAD_URLS.model,
|
||||||
modelPath,
|
modelPath,
|
||||||
@@ -142,7 +140,6 @@ export class VoiceTranscribeService {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 下载 tokens 文件 (30%)
|
// 下载 tokens 文件 (30%)
|
||||||
console.info('[VoiceTranscribe] 开始下载 tokens 文件...')
|
|
||||||
await this.downloadToFile(
|
await this.downloadToFile(
|
||||||
MODEL_DOWNLOAD_URLS.tokens,
|
MODEL_DOWNLOAD_URLS.tokens,
|
||||||
tokensPath,
|
tokensPath,
|
||||||
@@ -160,7 +157,6 @@ export class VoiceTranscribeService {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 下载 vad 文件 (30%)
|
// 下载 vad 文件 (30%)
|
||||||
console.info('[VoiceTranscribe] 开始下载 VAD 文件...')
|
|
||||||
await this.downloadToFile(
|
await this.downloadToFile(
|
||||||
(MODEL_DOWNLOAD_URLS as any).vad,
|
(MODEL_DOWNLOAD_URLS as any).vad,
|
||||||
vadPath,
|
vadPath,
|
||||||
@@ -178,10 +174,8 @@ export class VoiceTranscribeService {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
console.info('[VoiceTranscribe] 模型下载完成')
|
|
||||||
return { success: true, modelPath, tokensPath }
|
return { success: true, modelPath, tokensPath }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[VoiceTranscribe] 下载失败:', error)
|
|
||||||
const modelPath = this.resolveModelPath(SENSEVOICE_MODEL.files.model)
|
const modelPath = this.resolveModelPath(SENSEVOICE_MODEL.files.model)
|
||||||
const tokensPath = this.resolveModelPath(SENSEVOICE_MODEL.files.tokens)
|
const tokensPath = this.resolveModelPath(SENSEVOICE_MODEL.files.tokens)
|
||||||
const vadPath = this.resolveModelPath((SENSEVOICE_MODEL.files as any).vad)
|
const vadPath = this.resolveModelPath((SENSEVOICE_MODEL.files as any).vad)
|
||||||
@@ -221,8 +215,6 @@ export class VoiceTranscribeService {
|
|||||||
// main.js 和 transcribeWorker.js 同在 dist-electron 目录下
|
// main.js 和 transcribeWorker.js 同在 dist-electron 目录下
|
||||||
const workerPath = join(__dirname, 'transcribeWorker.js')
|
const workerPath = join(__dirname, 'transcribeWorker.js')
|
||||||
|
|
||||||
console.info('[VoiceTranscribe] 启动后台 Worker 转写...', { workerPath })
|
|
||||||
|
|
||||||
const worker = new Worker(workerPath, {
|
const worker = new Worker(workerPath, {
|
||||||
workerData: {
|
workerData: {
|
||||||
modelPath,
|
modelPath,
|
||||||
@@ -248,7 +240,6 @@ export class VoiceTranscribeService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
worker.on('error', (err: Error) => {
|
worker.on('error', (err: Error) => {
|
||||||
console.error('[VoiceTranscribe] Worker error:', err)
|
|
||||||
resolve({ success: false, error: String(err) })
|
resolve({ success: false, error: String(err) })
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -260,7 +251,6 @@ export class VoiceTranscribeService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[VoiceTranscribe] 启动 Worker 失败:', error)
|
|
||||||
resolve({ success: false, error: String(error) })
|
resolve({ success: false, error: String(error) })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -350,10 +340,10 @@ export class VoiceTranscribeService {
|
|||||||
// sherpa-onnx 的 recognizer 可能需要手动释放
|
// sherpa-onnx 的 recognizer 可能需要手动释放
|
||||||
this.recognizer = null
|
this.recognizer = null
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[VoiceTranscribe] 释放识别器失败:', error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const voiceTranscribeService = new VoiceTranscribeService()
|
export const voiceTranscribeService = new VoiceTranscribeService()
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,13 @@ export class WcdbService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.worker.on('error', (err) => {
|
this.worker.on('error', (err) => {
|
||||||
console.error('WCDB Worker 错误:', err)
|
// Worker error
|
||||||
})
|
})
|
||||||
|
|
||||||
this.worker.on('exit', (code) => {
|
this.worker.on('exit', (code) => {
|
||||||
if (code !== 0) console.error(`WCDB Worker 异常退出,退出码: ${code}`)
|
if (code !== 0) {
|
||||||
|
// Worker exited with error
|
||||||
|
}
|
||||||
this.worker = null
|
this.worker = null
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ export class WcdbService {
|
|||||||
this.setLogEnabled(this.logEnabled)
|
this.setLogEnabled(this.logEnabled)
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('创建 WCDB Worker 失败:', e)
|
// Failed to create worker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +99,7 @@ export class WcdbService {
|
|||||||
setPaths(resourcesPath: string, userDataPath: string): void {
|
setPaths(resourcesPath: string, userDataPath: string): void {
|
||||||
this.resourcesPath = resourcesPath
|
this.resourcesPath = resourcesPath
|
||||||
this.userDataPath = userDataPath
|
this.userDataPath = userDataPath
|
||||||
this.callWorker('setPaths', { resourcesPath, userDataPath }).catch(console.error)
|
this.callWorker('setPaths', { resourcesPath, userDataPath }).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,7 +107,7 @@ export class WcdbService {
|
|||||||
*/
|
*/
|
||||||
setLogEnabled(enabled: boolean): void {
|
setLogEnabled(enabled: boolean): void {
|
||||||
this.logEnabled = enabled
|
this.logEnabled = enabled
|
||||||
this.callWorker('setLogEnabled', { enabled }).catch(console.error)
|
this.callWorker('setLogEnabled', { enabled }).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { parentPort, workerData } from 'worker_threads'
|
import { parentPort, workerData } from 'worker_threads'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
|
||||||
interface WorkerParams {
|
interface WorkerParams {
|
||||||
@@ -9,37 +9,23 @@ interface WorkerParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
console.info('[TranscribeWorker] Worker process starting...');
|
|
||||||
|
|
||||||
if (!parentPort) {
|
if (!parentPort) {
|
||||||
console.error('[TranscribeWorker] Critical Error: parentPort is null');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.info('[TranscribeWorker] Loading sherpa-onnx-node...');
|
|
||||||
// 动态加载以捕获可能的加载错误(如 C++ 运行库缺失等)
|
// 动态加载以捕获可能的加载错误(如 C++ 运行库缺失等)
|
||||||
let sherpa: any;
|
let sherpa: any;
|
||||||
try {
|
try {
|
||||||
sherpa = require('sherpa-onnx-node');
|
sherpa = require('sherpa-onnx-node');
|
||||||
console.info('[TranscribeWorker] sherpa-onnx-node loaded successfully.');
|
|
||||||
} catch (requireError) {
|
} catch (requireError) {
|
||||||
console.error('[TranscribeWorker] Failed to load sherpa-onnx-node:', requireError);
|
|
||||||
parentPort.postMessage({ type: 'error', error: 'Failed to load speech engine: ' + String(requireError) });
|
parentPort.postMessage({ type: 'error', error: 'Failed to load speech engine: ' + String(requireError) });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { modelPath, tokensPath, wavData: rawWavData, sampleRate } = workerData as WorkerParams
|
const { modelPath, tokensPath, wavData: rawWavData, sampleRate } = workerData as WorkerParams
|
||||||
const wavData = Buffer.from(rawWavData);
|
const wavData = Buffer.from(rawWavData);
|
||||||
console.info('[TranscribeWorker] Params received:', {
|
|
||||||
modelPath,
|
|
||||||
tokensPath,
|
|
||||||
sampleRate,
|
|
||||||
wavDataLength: wavData?.length
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1. 初始化识别器 (SenseVoiceSmall)
|
// 1. 初始化识别器 (SenseVoiceSmall)
|
||||||
console.info('[TranscribeWorker] Initializing OfflineRecognizer...');
|
|
||||||
const recognizerConfig = {
|
const recognizerConfig = {
|
||||||
modelConfig: {
|
modelConfig: {
|
||||||
senseVoice: {
|
senseVoice: {
|
||||||
@@ -52,12 +38,8 @@ async function run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const recognizer = new sherpa.OfflineRecognizer(recognizerConfig)
|
const recognizer = new sherpa.OfflineRecognizer(recognizerConfig)
|
||||||
console.info('[TranscribeWorker] OfflineRecognizer initialized.');
|
|
||||||
|
|
||||||
// 2. 初始化 VAD (用于流式输出效果)
|
// 2. 初始化 VAD (用于流式输出效果)
|
||||||
const vadPath = modelPath.replace('model.int8.onnx', 'silero_vad.onnx');
|
const vadPath = modelPath.replace('model.int8.onnx', 'silero_vad.onnx');
|
||||||
console.info('[TranscribeWorker] VAD Path:', vadPath);
|
|
||||||
|
|
||||||
const vadConfig = {
|
const vadConfig = {
|
||||||
sileroVad: {
|
sileroVad: {
|
||||||
model: vadPath,
|
model: vadPath,
|
||||||
@@ -73,8 +55,6 @@ async function run() {
|
|||||||
|
|
||||||
// 检查 VAD 模型是否存在,如果不存在则退回到全量识别
|
// 检查 VAD 模型是否存在,如果不存在则退回到全量识别
|
||||||
if (!fs.existsSync(vadPath)) {
|
if (!fs.existsSync(vadPath)) {
|
||||||
console.warn('[TranscribeWorker] VAD model not found, falling back to full transcription.');
|
|
||||||
|
|
||||||
const pcmData = wavData.slice(44)
|
const pcmData = wavData.slice(44)
|
||||||
const samples = new Float32Array(pcmData.length / 2)
|
const samples = new Float32Array(pcmData.length / 2)
|
||||||
for (let i = 0; i < samples.length; i++) {
|
for (let i = 0; i < samples.length; i++) {
|
||||||
@@ -86,15 +66,11 @@ async function run() {
|
|||||||
recognizer.decode(stream)
|
recognizer.decode(stream)
|
||||||
const result = recognizer.getResult(stream)
|
const result = recognizer.getResult(stream)
|
||||||
|
|
||||||
console.info('[TranscribeWorker] Full transcription result:', result.text);
|
|
||||||
parentPort.postMessage({ type: 'final', text: result.text })
|
parentPort.postMessage({ type: 'final', text: result.text })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info('[TranscribeWorker] Initializing Vad...');
|
|
||||||
const vad = new sherpa.Vad(vadConfig, 60) // 60s max
|
const vad = new sherpa.Vad(vadConfig, 60) // 60s max
|
||||||
console.info('[TranscribeWorker] VAD initialized.');
|
|
||||||
|
|
||||||
// 3. 处理音频数据
|
// 3. 处理音频数据
|
||||||
const pcmData = wavData.slice(44)
|
const pcmData = wavData.slice(44)
|
||||||
const samples = new Float32Array(pcmData.length / 2)
|
const samples = new Float32Array(pcmData.length / 2)
|
||||||
@@ -107,7 +83,6 @@ async function run() {
|
|||||||
let offset = 0
|
let offset = 0
|
||||||
let accumulatedText = ''
|
let accumulatedText = ''
|
||||||
|
|
||||||
console.info('[TranscribeWorker] Starting processing loop...');
|
|
||||||
let segmentCount = 0;
|
let segmentCount = 0;
|
||||||
|
|
||||||
while (offset < samples.length) {
|
while (offset < samples.length) {
|
||||||
@@ -120,9 +95,6 @@ async function run() {
|
|||||||
while (!vad.isEmpty()) {
|
while (!vad.isEmpty()) {
|
||||||
const segment = vad.front(false)
|
const segment = vad.front(false)
|
||||||
|
|
||||||
// Log segment detection
|
|
||||||
console.info(`[TranscribeWorker] VAD Segment detected. Duration: ${segment.samples.length / sampleRate}s`);
|
|
||||||
|
|
||||||
const stream = recognizer.createStream()
|
const stream = recognizer.createStream()
|
||||||
stream.acceptWaveform({ sampleRate, samples: segment.samples })
|
stream.acceptWaveform({ sampleRate, samples: segment.samples })
|
||||||
recognizer.decode(stream)
|
recognizer.decode(stream)
|
||||||
@@ -133,7 +105,6 @@ async function run() {
|
|||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
accumulatedText += (accumulatedText ? ' ' : '') + text
|
accumulatedText += (accumulatedText ? ' ' : '') + text
|
||||||
segmentCount++;
|
segmentCount++;
|
||||||
console.info(`[TranscribeWorker] Partial update #${segmentCount}: "${text}" -> Total: "${accumulatedText.substring(0, 50)}..."`);
|
|
||||||
parentPort.postMessage({ type: 'partial', text: accumulatedText })
|
parentPort.postMessage({ type: 'partial', text: accumulatedText })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,26 +120,23 @@ async function run() {
|
|||||||
vad.flush();
|
vad.flush();
|
||||||
while (!vad.isEmpty()) {
|
while (!vad.isEmpty()) {
|
||||||
const segment = vad.front(false);
|
const segment = vad.front(false);
|
||||||
console.info(`[TranscribeWorker] Final VAD Segment detected. Duration: ${segment.samples.length / sampleRate}s`);
|
|
||||||
const stream = recognizer.createStream()
|
const stream = recognizer.createStream()
|
||||||
stream.acceptWaveform({ sampleRate, samples: segment.samples })
|
stream.acceptWaveform({ sampleRate, samples: segment.samples })
|
||||||
recognizer.decode(stream)
|
recognizer.decode(stream)
|
||||||
const result = recognizer.getResult(stream)
|
const result = recognizer.getResult(stream)
|
||||||
if (result.text) {
|
if (result.text) {
|
||||||
accumulatedText += (accumulatedText ? ' ' : '') + result.text.trim()
|
accumulatedText += (accumulatedText ? ' ' : '') + result.text.trim()
|
||||||
console.info(`[TranscribeWorker] Final partial update: "${result.text.trim()}"`);
|
|
||||||
parentPort.postMessage({ type: 'partial', text: accumulatedText })
|
parentPort.postMessage({ type: 'partial', text: accumulatedText })
|
||||||
}
|
}
|
||||||
vad.pop();
|
vad.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info('[TranscribeWorker] Loop finished. Final text length:', accumulatedText.length);
|
|
||||||
parentPort.postMessage({ type: 'final', text: accumulatedText })
|
parentPort.postMessage({ type: 'final', text: accumulatedText })
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[TranscribeWorker] Fatal error:', error);
|
|
||||||
parentPort.postMessage({ type: 'error', error: String(error) })
|
parentPort.postMessage({ type: 'error', error: String(error) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "weflow",
|
"name": "weflow",
|
||||||
"version": "1.1.2",
|
"version": "1.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "weflow",
|
"name": "weflow",
|
||||||
"version": "1.1.2",
|
"version": "1.2.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "^12.5.0",
|
"better-sqlite3": "^12.5.0",
|
||||||
|
|||||||
@@ -1412,7 +1412,7 @@
|
|||||||
|
|
||||||
.voice-transcript.sent {
|
.voice-transcript.sent {
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: rgba(255, 255, 255, 0.9);
|
||||||
color: var(--text-primary);
|
color: #333333;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ function ExportPage() {
|
|||||||
exportImages: options.exportMedia && options.exportImages,
|
exportImages: options.exportMedia && options.exportImages,
|
||||||
exportVoices: options.exportMedia && options.exportVoices,
|
exportVoices: options.exportMedia && options.exportVoices,
|
||||||
exportEmojis: options.exportMedia && options.exportEmojis,
|
exportEmojis: options.exportMedia && options.exportEmojis,
|
||||||
exportVoiceAsText: options.exportMedia && options.exportVoiceAsText,
|
exportVoiceAsText: options.exportVoiceAsText, // 独立于 exportMedia
|
||||||
dateRange: options.useAllTime ? null : options.dateRange ? {
|
dateRange: options.useAllTime ? null : options.dateRange ? {
|
||||||
start: Math.floor(options.dateRange.start.getTime() / 1000),
|
start: Math.floor(options.dateRange.start.getTime() / 1000),
|
||||||
// 将结束日期设置为当天的 23:59:59,以包含当天的所有消息
|
// 将结束日期设置为当天的 23:59:59,以包含当天的所有消息
|
||||||
@@ -444,15 +444,14 @@ function ExportPage() {
|
|||||||
|
|
||||||
<div className="media-option-divider"></div>
|
<div className="media-option-divider"></div>
|
||||||
|
|
||||||
<label className={`media-checkbox-row ${!options.exportMedia ? 'disabled' : ''}`}>
|
<label className="media-checkbox-row">
|
||||||
<div className="media-checkbox-info">
|
<div className="media-checkbox-info">
|
||||||
<span className="media-checkbox-title">语音转文字</span>
|
<span className="media-checkbox-title">语音转文字</span>
|
||||||
<span className="media-checkbox-desc">将语音消息转换为文字导出</span>
|
<span className="media-checkbox-desc">将语音消息转换为文字导出(不导出语音文件)</span>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={options.exportVoiceAsText}
|
checked={options.exportVoiceAsText}
|
||||||
disabled={!options.exportMedia}
|
|
||||||
onChange={e => setOptions({ ...options, exportVoiceAsText: e.target.checked })}
|
onChange={e => setOptions({ ...options, exportVoiceAsText: e.target.checked })}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export default defineConfig({
|
|||||||
'koffi',
|
'koffi',
|
||||||
'fsevents',
|
'fsevents',
|
||||||
'whisper-node',
|
'whisper-node',
|
||||||
'shelljs'
|
'shelljs',
|
||||||
|
'exceljs'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user