Spaces:
Sleeping
Sleeping
const plugin = requirePlugin("WechatSI"); | |
const pluginManager = plugin.getRecordRecognitionManager(); | |
const nativeRecorderManager = wx.getRecorderManager(); | |
// Helper function to show detailed errors | |
function showDetailedError(title, content) { | |
wx.showModal({ | |
title: title, | |
content: typeof content === 'object' ? JSON.stringify(content) : String(content), | |
showCancel: false | |
}); | |
} | |
Page({ | |
data: { | |
// Static language configuration | |
languages: { | |
'zh': { name: '中文', flag: 'cn', code: 'zh_CN', usePlugin: true }, | |
'en': { name: 'English', flag: 'us', code: 'en_US', usePlugin: true }, | |
'ja': { name: '日本語', flag: 'jp', code: 'ja_JP', usePlugin: false }, | |
'ko': { name: '한국어', flag: 'kr', code: 'ko_KR', usePlugin: false } | |
}, | |
// Dynamic data | |
sourceLang: 'zh', | |
targetLang: 'en', | |
transcript: '', | |
outputText: '', | |
isRecording: false, | |
hfSpaceUrl: 'https://dazaozi-wechat-translator-app.hf.space', | |
currentManagerType: null // 'plugin' or 'native' | |
}, | |
onLoad: function () { | |
// Set the full language arrays for the UI to use | |
this.setData({ | |
sourceLanguages: Object.values(this.data.languages).map(lang => ({...lang, langCode: this.getLangCode(lang.code)})), | |
targetLanguages: Object.values(this.data.languages).map(lang => ({...lang, langCode: this.getLangCode(lang.code)})) | |
}); | |
this.initPluginManager(); | |
this.initNativeRecorderManager(); | |
}, | |
getLangCode: function(fullCode) { | |
return fullCode.split('_')[0]; | |
}, | |
// --- Language Selection Logic (Simplified and Synchronous) --- | |
selectSourceLanguage: function (e) { | |
this.setData({ sourceLang: e.currentTarget.dataset.langCode }); | |
}, | |
selectTargetLanguage: function (e) { | |
this.setData({ targetLang: e.currentTarget.dataset.langCode }); | |
}, | |
swapLanguages: function () { | |
const { sourceLang, targetLang } = this.data; | |
this.setData({ | |
sourceLang: targetLang, | |
targetLang: sourceLang, | |
transcript: this.data.outputText, | |
outputText: this.data.transcript | |
}); | |
}, | |
// --- Manager Initializations --- | |
initPluginManager: function () { | |
pluginManager.onStart = () => this.setData({ transcript: '正在聆听 (插件)...', outputText: '' }); | |
pluginManager.onRecognize = (res) => this.setData({ transcript: res.result }); | |
pluginManager.onStop = (res) => { | |
this.setData({ isRecording: false }); | |
if (!res.result) { return this.setData({ transcript: '识别结果为空' }); } | |
this.setData({ transcript: res.result }); | |
const { sourceLang, targetLang, languages } = this.data; | |
const isPluginTarget = languages[targetLang].usePlugin; | |
if (sourceLang === targetLang) { | |
this.setData({ outputText: res.result }); | |
} else if (isPluginTarget) { | |
this.translateViaPlugin(res.result); | |
} else { | |
this.translateViaHfBridge(res.result, sourceLang, targetLang); | |
} | |
}; | |
pluginManager.onError = (res) => { | |
this.setData({ isRecording: false }); | |
showDetailedError('插件录音失败', res); | |
}; | |
}, | |
initNativeRecorderManager: function () { | |
nativeRecorderManager.onStart = () => this.setData({ transcript: '正在聆听 (原生)...', outputText: '' }); | |
nativeRecorderManager.onStop = (res) => { | |
this.setData({ isRecording: false }); | |
if (res.tempFilePath) { this.uploadAudioForASR(res.tempFilePath); } | |
else { this.setData({ transcript: '原生录音文件获取失败' }); } | |
}; | |
nativeRecorderManager.onError = (res) => { | |
this.setData({ isRecording: false }); | |
showDetailedError('原生录音失败', res); | |
}; | |
}, | |
// --- Main Record Button Handler --- | |
handleRecordToggle: function() { | |
if (this.data.isRecording) { | |
this.stopRecording(); | |
return; | |
} | |
wx.getSetting({ | |
success: (res) => { | |
if (!res.authSetting['scope.record']) { | |
wx.authorize({ | |
scope: 'scope.record', | |
success: this.startRecording, | |
fail: (err) => showDetailedError('授权失败', err) | |
}); | |
} else { | |
this.startRecording(); | |
} | |
}, | |
fail: (err) => showDetailedError('无法获取权限设置', err) | |
}); | |
}, | |
startRecording: function () { | |
console.log(`[DEBUG] Starting recording. Source: ${this.data.sourceLang}, Target: ${this.data.targetLang}`); | |
const { sourceLang, languages } = this.data; | |
const shouldUsePlugin = languages[sourceLang].usePlugin; | |
const managerType = shouldUsePlugin ? 'plugin' : 'native'; | |
this.setData({ isRecording: true, currentManagerType: managerType }); | |
if (shouldUsePlugin) { | |
pluginManager.start({ lang: languages[sourceLang].code, translate: false }); | |
} else { | |
nativeRecorderManager.start({ format: 'mp3', sampleRate: 16000, numberOfChannels: 1 }); | |
} | |
}, | |
stopRecording: function () { | |
if (this.data.currentManagerType === 'native') { | |
nativeRecorderManager.stop(); | |
} else { | |
pluginManager.stop(); | |
} | |
}, | |
// --- Translation Logic --- | |
translateViaPlugin: function(text) { | |
const { sourceLang, targetLang, languages } = this.data; | |
this.setData({ outputText: '翻译中 (插件)...' }); | |
plugin.translate({ | |
lfrom: languages[sourceLang].code, | |
lto: languages[targetLang].code, | |
content: text, | |
success: (res) => { | |
if (res.retcode === 0) { this.setData({ outputText: res.result }); } | |
else { showDetailedError('插件翻译失败', res); } | |
}, | |
fail: (err) => showDetailedError('插件翻译接口调用失败', err) | |
}); | |
}, | |
uploadAudioForASR: function (filePath) { | |
this.setData({ transcript: '正在识别 (HF)...' }); | |
wx.getFileSystemManager().readFile({ filePath, encoding: 'base64', success: (res) => { | |
wx.request({ | |
url: `${this.data.hfSpaceUrl}/api/asr`, | |
method: 'POST', | |
data: { "audio_data_uri": `data:audio/mp3;base64,${res.data}` }, | |
timeout: 60000, | |
success: (asrRes) => { | |
if (asrRes.statusCode === 200 && asrRes.data.transcript) { | |
const transcript = asrRes.data.transcript; | |
this.setData({ transcript }); | |
this.translateViaHfBridge(transcript, this.data.sourceLang, this.data.targetLang); | |
} else { showDetailedError('HF识别失败', asrRes.data); } | |
}, | |
fail: (err) => showDetailedError('HF识别请求失败', err) | |
}); | |
}}); | |
}, | |
translateViaHfBridge: function(text, source, target) { | |
if (source === target) { return this.setData({ outputText: text }); } | |
this.setData({ outputText: '翻译中 (HF)...' }); | |
this.translateViaHF(text, source, target, (result) => { | |
if (result) { this.setData({ outputText: result }); } | |
}); | |
}, | |
translateViaHF: function(text, sourceLang, targetLang, callback) { | |
wx.request({ | |
url: `${this.data.hfSpaceUrl}/api/translate`, | |
method: 'POST', | |
data: { "text": text, "source_lang": sourceLang, "target_lang": targetLang }, | |
timeout: 45000, | |
success: (res) => { | |
if (res.statusCode === 200 && res.data.translated_text) { callback(res.data.translated_text); } | |
else { showDetailedError('HF翻译失败', res.data); callback(null); } | |
}, | |
fail: (err) => { showDetailedError('HF翻译请求失败', err); callback(null); } | |
}); | |
} | |
}); | |