This commit is contained in:
Your Name
2026-08-17 13:59:12 +08:00
parent 79601ea176
commit 49309b7c57
62 changed files with 82 additions and 47023 deletions
@@ -1,291 +1,2 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const MIN_HOLD_MS = 280;
function isBenignSttError(msg, retcode) {
const m = String(msg || "").toLowerCase();
if (/internal voice data failed/i.test(m))
return true;
if (/voice data failed/i.test(m))
return true;
if (/recordfailed|record failed|record manager/i.test(m))
return true;
if (/please stop after start/i.test(m))
return true;
if (/no recognition|not start|未开始|无识别/i.test(m))
return true;
const code = Number(retcode);
if (code === -30002 || code === -30003 || code === -30012)
return true;
return false;
}
function friendlySttError(msg) {
const m = String(msg || "").trim();
if (!m || isBenignSttError(m))
return "";
if (/not allowed|auth|permission|权限|麦克风/i.test(m))
return "请允许使用麦克风";
if (/network|网络|timeout/i.test(m))
return "网络异常,请稍后再试";
return "语音识别失败,请重试";
}
let sharedManager = null;
let sharedManagerInited = false;
let sharedRecording = false;
let activeCtl = null;
function getSharedRecordManager() {
if (sharedManagerInited)
return sharedManager;
sharedManagerInited = true;
try {
const plugin = requirePlugin("WechatSI");
sharedManager = plugin.getRecordRecognitionManager();
} catch (e) {
common_vendor.index.__f__("warn", "at tongji/composables/useSpeechToText.js:44", "WechatSI record recognition not available", e);
sharedManager = null;
return null;
}
if (!sharedManager)
return null;
sharedManager.onStart = () => {
sharedRecording = true;
if (activeCtl)
activeCtl.handleStart();
};
sharedManager.onRecognize = (res) => {
if (activeCtl)
activeCtl.handleRecognize(res);
};
sharedManager.onStop = (res) => {
sharedRecording = false;
if (activeCtl)
activeCtl.handleStop(res);
};
sharedManager.onError = (res) => {
sharedRecording = false;
if (activeCtl)
activeCtl.handleError(res);
};
return sharedManager;
}
function useSpeechToText({ onResult, onSettle, onPartial, onError, showToast } = {}) {
const sttListening = common_vendor.ref(false);
const sttHolding = common_vendor.ref(false);
const sttSupported = common_vendor.ref(false);
let wxRecordManager = null;
let holdStartTs = 0;
let holdSessionId = 0;
let startRequested = false;
let recordAuthorized = null;
let pendingStartSession = 0;
function notify(msg) {
if (!msg)
return;
if (typeof showToast === "function") {
showToast(msg);
return;
}
common_vendor.index.showToast({ title: msg, icon: "none", duration: 2e3 });
}
function emitResult(text) {
const t = String(text || "").trim();
const heldMs = Date.now() - holdStartTs;
if (typeof onSettle === "function") {
const handled = onSettle(t, { heldMs });
if (handled === true)
return;
}
if (!t) {
if (heldMs < MIN_HOLD_MS)
return;
notify("没听清,请再试一次");
return;
}
if (typeof onResult === "function") {
onResult(t);
}
}
function stopListening() {
const ownsRecording = sharedRecording && activeCtl === controller;
if (wxRecordManager && (sttListening.value || ownsRecording)) {
try {
wxRecordManager.stop();
} catch (e) {
}
}
startRequested = false;
sttHolding.value = false;
sttListening.value = false;
}
function ensureRecordAuth(onGranted) {
if (recordAuthorized === true) {
onGranted();
return;
}
common_vendor.index.authorize({
scope: "scope.record",
success() {
recordAuthorized = true;
onGranted();
},
fail() {
recordAuthorized = false;
sttHolding.value = false;
common_vendor.index.showModal({
title: "需要麦克风权限",
content: "请在设置中允许使用麦克风,以便长按说话",
confirmText: "去设置",
success(res) {
if (res.confirm) {
common_vendor.index.openSetting({});
}
}
});
}
});
}
function actuallyStartWechat(session) {
if (!wxRecordManager || session !== holdSessionId || !sttHolding.value) {
return;
}
try {
startRequested = true;
wxRecordManager.start({
duration: 6e4,
lang: "zh_CN"
});
} catch (e) {
startRequested = false;
sharedRecording = false;
sttHolding.value = false;
notify("无法开始录音");
}
}
function startWechatRecord(session) {
if (!wxRecordManager || session !== holdSessionId || !sttHolding.value) {
return;
}
if (sharedRecording || startRequested) {
pendingStartSession = session;
try {
wxRecordManager.stop();
} catch (e) {
}
return;
}
actuallyStartWechat(session);
}
function beginHoldSpeech() {
if (!sttSupported.value) {
notify("当前环境不支持语音输入");
return;
}
if (sttHolding.value)
return;
const session = ++holdSessionId;
holdStartTs = Date.now();
sttHolding.value = true;
activeCtl = controller;
ensureRecordAuth(() => startWechatRecord(session));
}
function endHoldSpeech(force = false) {
let recording = false;
recording = sharedRecording && activeCtl === controller;
if (!force && !sttHolding.value && !sttListening.value && !startRequested && !recording) {
return;
}
sttHolding.value = false;
pendingStartSession = 0;
if (sttListening.value || recording) {
stopListening();
return;
}
if (startRequested) {
startRequested = false;
holdSessionId += 1;
return;
}
holdSessionId += 1;
}
const controller = {
handleStart() {
if (!sttHolding.value) {
startRequested = false;
sttListening.value = false;
return;
}
startRequested = false;
sttListening.value = true;
},
handleRecognize(res) {
if (typeof onPartial !== "function")
return;
const t = String((res == null ? void 0 : res.result) || "").trim();
if (t)
onPartial(t);
},
handleStop(res) {
sttListening.value = false;
startRequested = false;
if (pendingStartSession && pendingStartSession === holdSessionId && sttHolding.value) {
const s = pendingStartSession;
pendingStartSession = 0;
actuallyStartWechat(s);
return;
}
pendingStartSession = 0;
emitResult(res == null ? void 0 : res.result);
},
handleError(res) {
sttListening.value = false;
startRequested = false;
const msg = res && res.msg || "";
const retcode = res && (res.retcode ?? res.errCode ?? res.code);
if (/please stop after start/i.test(msg)) {
try {
wxRecordManager.stop();
} catch (e) {
}
pendingStartSession = sttHolding.value ? holdSessionId : 0;
return;
}
pendingStartSession = 0;
sttHolding.value = false;
const heldMs = Date.now() - holdStartTs;
if (isBenignSttError(msg, retcode) || heldMs < MIN_HOLD_MS) {
if (typeof onError === "function")
onError(msg || "");
return;
}
if (typeof onError === "function") {
const handled = onError(msg || "");
if (handled === true)
return;
}
const tip = friendlySttError(msg);
if (tip)
notify(tip);
}
};
wxRecordManager = getSharedRecordManager();
sttSupported.value = !!wxRecordManager;
common_vendor.onUnmounted(() => {
sttHolding.value = false;
holdSessionId += 1;
pendingStartSession = 0;
stopListening();
if (activeCtl === controller) {
activeCtl = null;
}
});
return {
sttListening,
sttHolding,
sttSupported,
beginHoldSpeech,
endHoldSpeech,
stopSpeechToText: endHoldSpeech
};
}
exports.isBenignSttError = isBenignSttError;
exports.useSpeechToText = useSpeechToText;
"use strict";const s=require("../../common/vendor.js"),C=280;function w(n,u){const d=String(n||"").toLowerCase();if(/internal voice data failed/i.test(d)||/voice data failed/i.test(d)||/recordfailed|record failed|record manager/i.test(d)||/please stop after start/i.test(d)||/no recognition|not start|未开始|无识别/i.test(d))return!0;const h=Number(u);return h===-30002||h===-30003||h===-30012}function N(n){const u=String(n||"").trim();return!u||w(u)?"":/not allowed|auth|permission|权限|麦克风/i.test(u)?"请允许使用麦克风":/network|网络|timeout/i.test(u)?"网络异常,请稍后再试":"语音识别失败,请重试"}let c=null,D=!1,g=!1,i=null;function j(){if(D)return c;D=!0;try{c=requirePlugin("WechatSI").getRecordRecognitionManager()}catch(n){return s.index.__f__("warn","at tongji/composables/useSpeechToText.js:44","WechatSI record recognition not available",n),c=null,null}return c?(c.onStart=()=>{g=!0,i&&i.handleStart()},c.onRecognize=n=>{i&&i.handleRecognize(n)},c.onStop=n=>{g=!1,i&&i.handleStop(n)},c.onError=n=>{g=!1,i&&i.handleError(n)},c):null}function A({onResult:n,onSettle:u,onPartial:d,onError:h,showToast:M}={}){const l=s.ref(!1),r=s.ref(!1),y=s.ref(!1);let f=null,R=0,p=0,o=!1,x=null,a=0;function v(e){if(e){if(typeof M=="function"){M(e);return}s.index.showToast({title:e,icon:"none",duration:2e3})}}function H(e){const t=String(e||"").trim(),m=Date.now()-R;if(!(typeof u=="function"&&u(t,{heldMs:m})===!0)){if(!t){if(m<C)return;v("没听清,请再试一次");return}typeof n=="function"&&n(t)}}function T(){const e=g&&i===S;if(f&&(l.value||e))try{f.stop()}catch{}o=!1,r.value=!1,l.value=!1}function L(e){if(x===!0){e();return}s.index.authorize({scope:"scope.record",success(){x=!0,e()},fail(){x=!1,r.value=!1,s.index.showModal({title:"需要麦克风权限",content:"请在设置中允许使用麦克风,以便长按说话",confirmText:"去设置",success(t){t.confirm&&s.index.openSetting({})}})}})}function _(e){if(!(!f||e!==p||!r.value))try{o=!0,f.start({duration:6e4,lang:"zh_CN"})}catch{o=!1,g=!1,r.value=!1,v("无法开始录音")}}function W(e){if(!(!f||e!==p||!r.value)){if(g||o){a=e;try{f.stop()}catch{}return}_(e)}}function q(){if(!y.value){v("当前环境不支持语音输入");return}if(r.value)return;const e=++p;R=Date.now(),r.value=!0,i=S,L(()=>W(e))}function z(e=!1){let t=!1;if(t=g&&i===S,!(!e&&!r.value&&!l.value&&!o&&!t)){if(r.value=!1,a=0,l.value||t){T();return}if(o){o=!1,p+=1;return}p+=1}}const S={handleStart(){if(!r.value){o=!1,l.value=!1;return}o=!1,l.value=!0},handleRecognize(e){if(typeof d!="function")return;const t=String((e==null?void 0:e.result)||"").trim();t&&d(t)},handleStop(e){if(l.value=!1,o=!1,a&&a===p&&r.value){const t=a;a=0,_(t);return}a=0,H(e==null?void 0:e.result)},handleError(e){l.value=!1,o=!1;const t=e&&e.msg||"",m=e&&(e.retcode??e.errCode??e.code);if(/please stop after start/i.test(t)){try{f.stop()}catch{}a=r.value?p:0;return}a=0,r.value=!1;const I=Date.now()-R;if(w(t,m)||I<C){typeof h=="function"&&h(t||"");return}if(typeof h=="function"&&h(t||"")===!0)return;const b=N(t);b&&v(b)}};return f=j(),y.value=!!f,s.onUnmounted(()=>{r.value=!1,p+=1,a=0,T(),i===S&&(i=null)}),{sttListening:l,sttHolding:r,sttSupported:y,beginHoldSpeech:q,endHoldSpeech:z,stopSpeechToText:z}}exports.isBenignSttError=w;exports.useSpeechToText=A;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/composables/useSpeechToText.js.map