@easyv/biz-components 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,15 @@
1
- var m = Object.defineProperty;
2
- var p = (i, t, e) => t in i ? m(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
3
- var o = (i, t, e) => p(i, typeof t != "symbol" ? t + "" : t, e);
1
+ var S = Object.defineProperty;
2
+ var m = (i, t, e) => t in i ? S(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
3
+ var o = (i, t, e) => m(i, typeof t != "symbol" ? t + "" : t, e);
4
4
  import d from "../../node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/index.es.js";
5
- import { toBase64 as S } from "./utils.es.js";
6
- import { RecorderManager as T } from "./RecorderManager/RecorderManager.es.js";
7
- let l = 0;
8
- const w = "93b73e33", y = "ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0", x = "fe43de7a071e3a32ec03c6f09fe7bedf";
9
- function I() {
10
- const i = "wss://iat-api.xfyun.cn/v2/iat", t = "iat-api.xfyun.cn", e = x, s = y, r = (/* @__PURE__ */ new Date()).toUTCString(), n = "hmac-sha256", a = "host date request-line", h = `host: ${t}
5
+ import { toBase64 as p } from "./utils.es.js";
6
+ import { RecorderManager as w } from "./RecorderManager/RecorderManager.es.js";
7
+ let u = 0;
8
+ const T = "93b73e33", y = "ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0", I = "fe43de7a071e3a32ec03c6f09fe7bedf";
9
+ function R() {
10
+ const i = "wss://iat-api.xfyun.cn/v2/iat", t = "iat-api.xfyun.cn", e = I, s = y, r = (/* @__PURE__ */ new Date()).toUTCString(), n = "hmac-sha256", a = "host date request-line", h = `host: ${t}
11
11
  date: ${r}
12
- GET /v2/iat HTTP/1.1`, c = d.HmacSHA256(h, s), u = d.enc.Base64.stringify(c), g = `api_key="${e}", algorithm="${n}", headers="${a}", signature="${u}"`, f = window.btoa(g);
12
+ GET /v2/iat HTTP/1.1`, c = d.HmacSHA256(h, s), l = d.enc.Base64.stringify(c), g = `api_key="${e}", algorithm="${n}", headers="${a}", signature="${l}"`, f = window.btoa(g);
13
13
  return `${i}?authorization=${f}&date=${r}&host=${t}`;
14
14
  }
15
15
  class C {
@@ -35,11 +35,7 @@ class C {
35
35
  const h = n.ws;
36
36
  for (let c = 0; c < h.length; c++)
37
37
  a = a + h[c].cw[0].w;
38
- n.pgs ? (n.pgs === "apd" && (this.resultText = this.resultTextTemp), this.resultTextTemp = this.resultText + a) : this.resultText = this.resultText + a, console.log("%c 🐶 dog ==== renderResult:", "color: green; font-size: 16px;", {
39
- str: a,
40
- resultTextTemp: this.resultTextTemp,
41
- resultText: this.resultText
42
- });
38
+ n.pgs ? (n.pgs === "apd" && (this.resultText = this.resultTextTemp), this.resultTextTemp = this.resultText + a) : this.resultText = this.resultText + a;
43
39
  }
44
40
  if (e.code === 0 && e.data.status === 2) {
45
41
  this.config.onMessage({
@@ -75,7 +71,7 @@ class C {
75
71
  }));
76
72
  const e = {
77
73
  common: {
78
- app_id: w
74
+ app_id: T
79
75
  },
80
76
  business: {
81
77
  language: "zh_cn",
@@ -100,10 +96,10 @@ class C {
100
96
  console.log(
101
97
  "%c ❤️ love ==== start new socket ",
102
98
  "color: red; font-size: 16px;",
103
- l,
99
+ u,
104
100
  (/* @__PURE__ */ new Date()).toUTCString()
105
- ), l++;
106
- const t = ((s = (e = this.config).getWebSocketUrl) == null ? void 0 : s.call(e)) || I();
101
+ ), u++;
102
+ const t = ((s = (e = this.config).getWebSocketUrl) == null ? void 0 : s.call(e)) || R();
107
103
  if ("WebSocket" in window)
108
104
  this.wsInstance = new WebSocket(t);
109
105
  else {
@@ -128,13 +124,13 @@ class C {
128
124
  status: t ? 2 : 1,
129
125
  format: "audio/L16;rate=16000",
130
126
  encoding: "raw",
131
- audio: S(e)
127
+ audio: p(e)
132
128
  }
133
129
  })
134
130
  ), t && (this.readyToSend = !1, this.changeBtnStatus("CLOSING")));
135
131
  }
136
132
  initializeRecorder() {
137
- this.recorder = new T(), this.recorder && (this.recorder.onFrameRecorded = (t) => {
133
+ this.recorder = new w(), this.recorder && (this.recorder.onFrameRecorded = (t) => {
138
134
  this.recordProcess(t);
139
135
  }, this.recorder.onStop = (t) => {
140
136
  }, this.recorder.onStart = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"XunFeiStreamVoiceManager.es.js","sources":["../../../src/utils/xunFeiVoice/XunFeiStreamVoiceManager.ts"],"sourcesContent":["import CryptoJS from 'crypto-js';\nimport { RecorderManager } from './RecorderManager';\nimport type { OnMessageInfo, RecordFrameInfo } from './types';\nimport { toBase64 } from './utils';\n\ntype errorType = 'socketConnect' | 'message' | 'record';\nlet startCount = 0;\n\nexport interface XunFeiStreamVoiceManagerConfig {\n onError: (errInfo: { type: errorType; message: string }) => void;\n /** ws 收到消息的回调\n * @param info.message 消息内容\n * @param info.isLatest 是否是最后一条消息。 isLatest 为 true 时, message 为完整的语音识别结果。\n */\n onMessage: (info: OnMessageInfo) => void;\n /** webSocket 关闭的回调。\n * 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket\n */\n onWsClose: () => void;\n /**\n * 获取链接讯飞语音识别地址的 url\n * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n */\n getWebSocketUrl?: () => string;\n}\ntype SocketStatus = 'UNDEFINED' | 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED';\n\n/** 现在前端mock,后续移到接口下发 */\nconst APPID = '93b73e33';\nconst API_SECRET = 'ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0';\nconst API_KEY = 'fe43de7a071e3a32ec03c6f09fe7bedf';\n/**\n * 获取websocket url\n * 该接口需要后端提供,这里为了方便前端处理\n */\nfunction getWebSocketUrl() {\n // 请求地址根据语种不同变化\n const url = 'wss://iat-api.xfyun.cn/v2/iat';\n const host = 'iat-api.xfyun.cn';\n const apiKey = API_KEY;\n const apiSecret = API_SECRET;\n const date = new Date().toUTCString();\n const algorithm = 'hmac-sha256';\n const headers = 'host date request-line';\n const signatureOrigin = `host: ${host}\\ndate: ${date}\\nGET /v2/iat HTTP/1.1`;\n const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);\n const signature = CryptoJS.enc.Base64.stringify(signatureSha);\n const authorizationOrigin = `api_key=\"${apiKey}\", algorithm=\"${algorithm}\", headers=\"${headers}\", signature=\"${signature}\"`;\n const authorization = window.btoa(authorizationOrigin);\n return `${url}?authorization=${authorization}&date=${date}&host=${host}`;\n}\n\n/**\n * 讯飞流式语音识别\n */\nexport class XunFeiStreamVoiceManager {\n wsInstance: WebSocket | null = null;\n status: SocketStatus = 'UNDEFINED';\n recorder: RecorderManager | null = null;\n isDestroy = false;\n resultText = '';\n resultTextTemp = '';\n continueRecord = false;\n startTimeStamp = 0;\n recordStatus: 'start' | 'stop' = 'stop';\n readyToSend = false;\n\n constructor(public config: XunFeiStreamVoiceManagerConfig) {\n this.initializeRecorder();\n }\n renderResult(resultData: string) {\n // 识别结束\n const jsonData = JSON.parse(resultData);\n if (jsonData.data && jsonData.data.result) {\n const data = jsonData.data.result;\n let str = '';\n const ws = data.ws;\n for (let i = 0; i < ws.length; i++) {\n str = str + ws[i].cw[0].w;\n }\n // 开启 wpgs 会有此字段(前提:在控制台开通动态修正功能)\n // 取值为 \"apd\"时表示该片结果是追加到前面的最终结果;取值为\"rpl\" 时表示替换前面的部分结果,替换范围为rg字段\n if (data.pgs) {\n if (data.pgs === 'apd') {\n // 将resultTextTemp同步给resultText\n this.resultText = this.resultTextTemp;\n }\n // 将结果存储在resultTextTemp中\n this.resultTextTemp = this.resultText + str;\n } else {\n this.resultText = this.resultText + str;\n }\n console.log('%c 🐶 dog ==== renderResult:', 'color: green; font-size: 16px;', {\n str,\n resultTextTemp: this.resultTextTemp,\n resultText: this.resultText,\n });\n }\n /**\n * 识别结果是否结束标识:\n * 0:识别的第一块结果。\n * 1:识别中间结果。\n * 2:识别最后一块结果\n */\n if (jsonData.code === 0 && jsonData.data.status === 2) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: true,\n tempMessage: this.resultTextTemp,\n });\n this.wsInstance?.close();\n return;\n }\n if (jsonData.code === 0 && jsonData.data.status === 1) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: false,\n tempMessage: this.resultTextTemp,\n });\n }\n // code 不为0时表示出错\n if (jsonData.code !== 0) {\n this.wsInstance?.close();\n this.config.onError({ type: 'message', message: resultData });\n }\n }\n\n setConfig(newConfig: XunFeiStreamVoiceManagerConfig) {\n this.config = {\n ...this.config,\n ...newConfig,\n };\n this.initializeRecorder();\n }\n sendMessage(message: string) {\n if (this.wsInstance?.readyState === WebSocket.OPEN && this.readyToSend) {\n this.wsInstance.send(message);\n }\n }\n\n async recordConfigSend() {\n if (this.isDestroy) {\n return;\n }\n try {\n // 开始录音\n await this.recorder?.start({\n sampleRate: 16000,\n frameSize: 1280,\n });\n const params = {\n common: {\n app_id: APPID,\n },\n business: {\n language: 'zh_cn',\n domain: 'iat',\n accent: 'mandarin',\n vad_eos: 5000,\n dwa: 'wpgs',\n },\n data: {\n status: 0,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n },\n };\n this.readyToSend = true;\n this.sendMessage(JSON.stringify(params));\n } catch (error) {\n console.error('record error:', error);\n this.config.onError({ type: 'record', message: '录音失败, 请检查权限!' });\n }\n }\n\n startNewSocket() {\n console.log(\n '%c ❤️ love ==== start new socket ',\n 'color: red; font-size: 16px;',\n startCount,\n new Date().toUTCString(),\n );\n startCount++;\n const websocketUrl = this.config.getWebSocketUrl?.() || getWebSocketUrl();\n if ('WebSocket' in window) {\n this.wsInstance = new WebSocket(websocketUrl);\n } else {\n this.config.onError({ type: 'socketConnect', message: '浏览器不支持 WebSocket' });\n return;\n }\n this.wsInstance.onmessage = (e) => {\n // 处理返回数据\n this.renderResult(e.data);\n };\n this.wsInstance.onerror = (e) => {\n console.error('socket error:', e);\n this.changeBtnStatus('CLOSED');\n };\n this.wsInstance.onclose = async () => {\n this.changeBtnStatus('CLOSED');\n this.config.onWsClose();\n this.recorder?.stop();\n if (this.continueRecord) {\n this.readyToSend = false;\n await this.recorder?.close();\n this.startRecord();\n }\n };\n this.wsInstance.onopen = () => {\n this.changeBtnStatus('OPEN');\n this.recordConfigSend();\n };\n }\n\n recordProcess({ isLastFrame, frameBuffer }: RecordFrameInfo) {\n if (this.isDestroy) {\n return;\n }\n if (!this.wsInstance) {\n return;\n }\n if (this.wsInstance.readyState === this.wsInstance.OPEN) {\n this.sendMessage(\n JSON.stringify({\n data: {\n status: isLastFrame ? 2 : 1,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n audio: toBase64(frameBuffer),\n },\n }),\n );\n }\n if (isLastFrame) {\n this.readyToSend = false;\n this.changeBtnStatus('CLOSING');\n }\n }\n\n initializeRecorder() {\n this.recorder = new RecorderManager();\n if (!this.recorder) {\n return;\n }\n this.recorder.onFrameRecorded = (data: RecordFrameInfo) => {\n this.recordProcess(data);\n };\n this.recorder.onStop = (_data: ArrayBuffer[]) => {\n // do nothing\n };\n this.recorder.onStart = () => {\n console.log('%c ❤️ love ==== recorder onStart:', 'color: red; font-size: 16px;');\n this.changeBtnStatus('OPEN');\n };\n }\n\n changeBtnStatus(status: SocketStatus) {\n this.status = status;\n }\n\n startRecord() {\n if (this.isDestroy) {\n return;\n }\n this.startTimeStamp = Date.now();\n this.resultText = '';\n this.resultTextTemp = '';\n this.startNewSocket();\n }\n\n async stopRecord() {\n console.log('stop record ====', Date.now() - this.startTimeStamp);\n // 结束录音,为了防止刚开始就关闭\n if (Date.now() - this.startTimeStamp < 500) {\n setTimeout(() => {\n this.recorder?.stop();\n }, 100);\n } else {\n this.recorder?.stop();\n }\n // 这里为了防止ws没有返回结束的数据,导致 ws 一直没关,所以加个定时器,5s 后手动关闭\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n }, 5000);\n }\n\n /** 开启持续监听 */\n startContinueRecord() {\n this.continueRecord = true;\n this.startRecord();\n }\n\n stopContinueRecord() {\n this.continueRecord = false;\n this.stopRecord();\n }\n\n destroy() {\n this.isDestroy = true;\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n this.recorder?.stop();\n }, 1000);\n }\n}\n"],"names":["startCount","APPID","API_SECRET","API_KEY","getWebSocketUrl","url","host","apiKey","apiSecret","date","algorithm","headers","signatureOrigin","signatureSha","CryptoJS","signature","authorizationOrigin","authorization","XunFeiStreamVoiceManager","config","__publicField","resultData","jsonData","data","str","ws","i","_a","_b","newConfig","message","params","error","websocketUrl","e","isLastFrame","frameBuffer","toBase64","RecorderManager","_data","status","_c"],"mappings":";;;;;;AAMA,IAAIA,IAAa;AAsBjB,MAAMC,IAAQ,YACRC,IAAa,oCACbC,IAAU;AAKhB,SAASC,IAAkB;AAEzB,QAAMC,IAAM,iCACNC,IAAO,oBACPC,IAASJ,GACTK,IAAYN,GACZO,KAAO,oBAAI,KAAK,GAAE,YAAY,GAC9BC,IAAY,eACZC,IAAU,0BACVC,IAAkB,SAASN,CAAI;AAAA,QAAWG,CAAI;AAAA,uBAC9CI,IAAeC,EAAS,WAAWF,GAAiBJ,CAAS,GAC7DO,IAAYD,EAAS,IAAI,OAAO,UAAUD,CAAY,GACtDG,IAAsB,YAAYT,CAAM,iBAAiBG,CAAS,eAAeC,CAAO,iBAAiBI,CAAS,KAClHE,IAAgB,OAAO,KAAKD,CAAmB;AACrD,SAAO,GAAGX,CAAG,kBAAkBY,CAAa,SAASR,CAAI,SAASH,CAAI;AACxE;AAKO,MAAMY,EAAyB;AAAA,EAYpC,YAAmBC,GAAwC;AAX3D,IAAAC,EAAA,oBAA+B;AAC/B,IAAAA,EAAA,gBAAuB;AACvB,IAAAA,EAAA,kBAAmC;AACnC,IAAAA,EAAA,mBAAY;AACZ,IAAAA,EAAA,oBAAa;AACb,IAAAA,EAAA,wBAAiB;AACjB,IAAAA,EAAA,wBAAiB;AACjB,IAAAA,EAAA,wBAAiB;AACjB,IAAAA,EAAA,sBAAiC;AACjC,IAAAA,EAAA,qBAAc;AAEK,SAAA,SAAAD,GACjB,KAAK,mBAAmB;AAAA,EAAA;AAAA,EAE1B,aAAaE,GAAoB;;AAEzB,UAAAC,IAAW,KAAK,MAAMD,CAAU;AACtC,QAAIC,EAAS,QAAQA,EAAS,KAAK,QAAQ;AACnC,YAAAC,IAAOD,EAAS,KAAK;AAC3B,UAAIE,IAAM;AACV,YAAMC,IAAKF,EAAK;AAChB,eAASG,IAAI,GAAGA,IAAID,EAAG,QAAQC;AAC7B,QAAAF,IAAMA,IAAMC,EAAGC,CAAC,EAAE,GAAG,CAAC,EAAE;AAI1B,MAAIH,EAAK,OACHA,EAAK,QAAQ,UAEf,KAAK,aAAa,KAAK,iBAGpB,KAAA,iBAAiB,KAAK,aAAaC,KAEnC,KAAA,aAAa,KAAK,aAAaA,GAE9B,QAAA,IAAI,kCAAkC,kCAAkC;AAAA,QAC9E,KAAAA;AAAA,QACA,gBAAgB,KAAK;AAAA,QACrB,YAAY,KAAK;AAAA,MAAA,CAClB;AAAA,IAAA;AAQH,QAAIF,EAAS,SAAS,KAAKA,EAAS,KAAK,WAAW,GAAG;AACrD,WAAK,OAAO,UAAU;AAAA,QACpB,SAAS,KAAK;AAAA,QACd,UAAU;AAAA,QACV,aAAa,KAAK;AAAA,MAAA,CACnB,IACDK,IAAA,KAAK,eAAL,QAAAA,EAAiB;AACjB;AAAA,IAAA;AAEF,IAAIL,EAAS,SAAS,KAAKA,EAAS,KAAK,WAAW,KAClD,KAAK,OAAO,UAAU;AAAA,MACpB,SAAS,KAAK;AAAA,MACd,UAAU;AAAA,MACV,aAAa,KAAK;AAAA,IAAA,CACnB,GAGCA,EAAS,SAAS,OACpBM,IAAA,KAAK,eAAL,QAAAA,EAAiB,SACjB,KAAK,OAAO,QAAQ,EAAE,MAAM,WAAW,SAASP,GAAY;AAAA,EAC9D;AAAA,EAGF,UAAUQ,GAA2C;AACnD,SAAK,SAAS;AAAA,MACZ,GAAG,KAAK;AAAA,MACR,GAAGA;AAAA,IACL,GACA,KAAK,mBAAmB;AAAA,EAAA;AAAA,EAE1B,YAAYC,GAAiB;;AAC3B,MAAIH,IAAA,KAAK,eAAL,gBAAAA,EAAiB,gBAAe,UAAU,QAAQ,KAAK,eACpD,KAAA,WAAW,KAAKG,CAAO;AAAA,EAC9B;AAAA,EAGF,MAAM,mBAAmB;;AACvB,QAAI,MAAK;AAGL,UAAA;AAEI,gBAAAH,IAAA,KAAK,aAAL,gBAAAA,EAAe,MAAM;AAAA,UACzB,YAAY;AAAA,UACZ,WAAW;AAAA,QAAA;AAEb,cAAMI,IAAS;AAAA,UACb,QAAQ;AAAA,YACN,QAAQ9B;AAAA,UACV;AAAA,UACA,UAAU;AAAA,YACR,UAAU;AAAA,YACV,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,KAAK;AAAA,UACP;AAAA,UACA,MAAM;AAAA,YACJ,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,UAAU;AAAA,UAAA;AAAA,QAEd;AACA,aAAK,cAAc,IACnB,KAAK,YAAY,KAAK,UAAU8B,CAAM,CAAC;AAAA,eAChCC,GAAO;AACN,gBAAA,MAAM,iBAAiBA,CAAK,GACpC,KAAK,OAAO,QAAQ,EAAE,MAAM,UAAU,SAAS,gBAAgB;AAAA,MAAA;AAAA,EACjE;AAAA,EAGF,iBAAiB;;AACP,YAAA;AAAA,MACN;AAAA,MACA;AAAA,MACAhC;AAAA,OACA,oBAAI,KAAK,GAAE,YAAY;AAAA,IACzB,GACAA;AACA,UAAMiC,MAAeL,KAAAD,IAAA,KAAK,QAAO,oBAAZ,gBAAAC,EAAA,KAAAD,OAAmCvB,EAAgB;AACxE,QAAI,eAAe;AACZ,WAAA,aAAa,IAAI,UAAU6B,CAAY;AAAA,SACvC;AACL,WAAK,OAAO,QAAQ,EAAE,MAAM,iBAAiB,SAAS,oBAAoB;AAC1E;AAAA,IAAA;AAEG,SAAA,WAAW,YAAY,CAACC,MAAM;AAE5B,WAAA,aAAaA,EAAE,IAAI;AAAA,IAC1B,GACK,KAAA,WAAW,UAAU,CAACA,MAAM;AACvB,cAAA,MAAM,iBAAiBA,CAAC,GAChC,KAAK,gBAAgB,QAAQ;AAAA,IAC/B,GACK,KAAA,WAAW,UAAU,YAAY;;AACpC,WAAK,gBAAgB,QAAQ,GAC7B,KAAK,OAAO,UAAU,IACtBP,IAAA,KAAK,aAAL,QAAAA,EAAe,QACX,KAAK,mBACP,KAAK,cAAc,IACb,QAAAC,IAAA,KAAK,aAAL,gBAAAA,EAAe,UACrB,KAAK,YAAY;AAAA,IAErB,GACK,KAAA,WAAW,SAAS,MAAM;AAC7B,WAAK,gBAAgB,MAAM,GAC3B,KAAK,iBAAiB;AAAA,IACxB;AAAA,EAAA;AAAA,EAGF,cAAc,EAAE,aAAAO,GAAa,aAAAC,KAAgC;AAC3D,IAAI,KAAK,aAGJ,KAAK,eAGN,KAAK,WAAW,eAAe,KAAK,WAAW,QAC5C,KAAA;AAAA,MACH,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,UACJ,QAAQD,IAAc,IAAI;AAAA,UAC1B,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,OAAOE,EAASD,CAAW;AAAA,QAAA;AAAA,MAE9B,CAAA;AAAA,IACH,GAEED,MACF,KAAK,cAAc,IACnB,KAAK,gBAAgB,SAAS;AAAA,EAChC;AAAA,EAGF,qBAAqB;AAEf,IADC,KAAA,WAAW,IAAIG,EAAgB,GAC/B,KAAK,aAGL,KAAA,SAAS,kBAAkB,CAACf,MAA0B;AACzD,WAAK,cAAcA,CAAI;AAAA,IACzB,GACK,KAAA,SAAS,SAAS,CAACgB,MAAyB;AAAA,IAEjD,GACK,KAAA,SAAS,UAAU,MAAM;AACpB,cAAA,IAAI,sCAAsC,8BAA8B,GAChF,KAAK,gBAAgB,MAAM;AAAA,IAC7B;AAAA,EAAA;AAAA,EAGF,gBAAgBC,GAAsB;AACpC,SAAK,SAASA;AAAA,EAAA;AAAA,EAGhB,cAAc;AACZ,IAAI,KAAK,cAGJ,KAAA,iBAAiB,KAAK,IAAI,GAC/B,KAAK,aAAa,IAClB,KAAK,iBAAiB,IACtB,KAAK,eAAe;AAAA,EAAA;AAAA,EAGtB,MAAM,aAAa;;AACjB,YAAQ,IAAI,qBAAqB,KAAK,IAAI,IAAI,KAAK,cAAc,GAE7D,KAAK,IAAA,IAAQ,KAAK,iBAAiB,MACrC,WAAW,MAAM;;AACf,OAAAb,IAAA,KAAK,aAAL,QAAAA,EAAe;AAAA,OACd,GAAG,KAENA,IAAA,KAAK,aAAL,QAAAA,EAAe,QAGjB,WAAW,MAAM;;AACf,QAAIA,IAAA,KAAK,eAAL,gBAAAA,EAAiB,gBAAe,UAAU,UAC5CC,IAAA,KAAK,eAAL,QAAAA,EAAiB;AAAA,OAElB,GAAI;AAAA,EAAA;AAAA;AAAA,EAIT,sBAAsB;AACpB,SAAK,iBAAiB,IACtB,KAAK,YAAY;AAAA,EAAA;AAAA,EAGnB,qBAAqB;AACnB,SAAK,iBAAiB,IACtB,KAAK,WAAW;AAAA,EAAA;AAAA,EAGlB,UAAU;AACR,SAAK,YAAY,IACjB,WAAW,MAAM;;AACf,QAAID,IAAA,KAAK,eAAL,gBAAAA,EAAiB,gBAAe,UAAU,UAC5CC,IAAA,KAAK,eAAL,QAAAA,EAAiB,WAEnBa,IAAA,KAAK,aAAL,QAAAA,EAAe;AAAA,OACd,GAAI;AAAA,EAAA;AAEX;"}
1
+ {"version":3,"file":"XunFeiStreamVoiceManager.es.js","sources":["../../../src/utils/xunFeiVoice/XunFeiStreamVoiceManager.ts"],"sourcesContent":["import CryptoJS from 'crypto-js';\nimport { RecorderManager } from './RecorderManager';\nimport type { OnMessageInfo, RecordFrameInfo } from './types';\nimport { toBase64 } from './utils';\n\ntype errorType = 'socketConnect' | 'message' | 'record';\nlet startCount = 0;\n\nexport interface XunFeiStreamVoiceManagerConfig {\n onError: (errInfo: { type: errorType; message: string }) => void;\n /** ws 收到消息的回调\n * @param info.message 消息内容\n * @param info.isLatest 是否是最后一条消息。 isLatest 为 true 时, message 为完整的语音识别结果。\n */\n onMessage: (info: OnMessageInfo) => void;\n /** webSocket 关闭的回调。\n * 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket\n */\n onWsClose: () => void;\n /**\n * 获取链接讯飞语音识别地址的 url\n * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n */\n getWebSocketUrl?: () => string;\n}\ntype SocketStatus = 'UNDEFINED' | 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED';\n\n/** 现在前端mock,后续移到接口下发 */\nconst APPID = '93b73e33';\nconst API_SECRET = 'ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0';\nconst API_KEY = 'fe43de7a071e3a32ec03c6f09fe7bedf';\n/**\n * 获取websocket url\n * 该接口需要后端提供,这里为了方便前端处理\n */\nfunction getWebSocketUrl() {\n // 请求地址根据语种不同变化\n const url = 'wss://iat-api.xfyun.cn/v2/iat';\n const host = 'iat-api.xfyun.cn';\n const apiKey = API_KEY;\n const apiSecret = API_SECRET;\n const date = new Date().toUTCString();\n const algorithm = 'hmac-sha256';\n const headers = 'host date request-line';\n const signatureOrigin = `host: ${host}\\ndate: ${date}\\nGET /v2/iat HTTP/1.1`;\n const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);\n const signature = CryptoJS.enc.Base64.stringify(signatureSha);\n const authorizationOrigin = `api_key=\"${apiKey}\", algorithm=\"${algorithm}\", headers=\"${headers}\", signature=\"${signature}\"`;\n const authorization = window.btoa(authorizationOrigin);\n return `${url}?authorization=${authorization}&date=${date}&host=${host}`;\n}\n\n/**\n * 讯飞流式语音识别\n */\nexport class XunFeiStreamVoiceManager {\n wsInstance: WebSocket | null = null;\n status: SocketStatus = 'UNDEFINED';\n recorder: RecorderManager | null = null;\n isDestroy = false;\n resultText = '';\n resultTextTemp = '';\n continueRecord = false;\n startTimeStamp = 0;\n recordStatus: 'start' | 'stop' = 'stop';\n readyToSend = false;\n\n constructor(public config: XunFeiStreamVoiceManagerConfig) {\n this.initializeRecorder();\n }\n renderResult(resultData: string) {\n // 识别结束\n const jsonData = JSON.parse(resultData);\n if (jsonData.data && jsonData.data.result) {\n const data = jsonData.data.result;\n let str = '';\n const ws = data.ws;\n for (let i = 0; i < ws.length; i++) {\n str = str + ws[i].cw[0].w;\n }\n // 开启 wpgs 会有此字段(前提:在控制台开通动态修正功能)\n // 取值为 \"apd\"时表示该片结果是追加到前面的最终结果;取值为\"rpl\" 时表示替换前面的部分结果,替换范围为rg字段\n if (data.pgs) {\n if (data.pgs === 'apd') {\n // 将resultTextTemp同步给resultText\n this.resultText = this.resultTextTemp;\n }\n // 将结果存储在resultTextTemp中\n this.resultTextTemp = this.resultText + str;\n } else {\n this.resultText = this.resultText + str;\n }\n }\n /**\n * 识别结果是否结束标识:\n * 0:识别的第一块结果。\n * 1:识别中间结果。\n * 2:识别最后一块结果\n */\n if (jsonData.code === 0 && jsonData.data.status === 2) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: true,\n tempMessage: this.resultTextTemp,\n });\n this.wsInstance?.close();\n return;\n }\n if (jsonData.code === 0 && jsonData.data.status === 1) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: false,\n tempMessage: this.resultTextTemp,\n });\n }\n // code 不为0时表示出错\n if (jsonData.code !== 0) {\n this.wsInstance?.close();\n this.config.onError({ type: 'message', message: resultData });\n }\n }\n\n setConfig(newConfig: XunFeiStreamVoiceManagerConfig) {\n this.config = {\n ...this.config,\n ...newConfig,\n };\n this.initializeRecorder();\n }\n sendMessage(message: string) {\n if (this.wsInstance?.readyState === WebSocket.OPEN && this.readyToSend) {\n this.wsInstance.send(message);\n }\n }\n\n async recordConfigSend() {\n if (this.isDestroy) {\n return;\n }\n try {\n // 开始录音\n await this.recorder?.start({\n sampleRate: 16000,\n frameSize: 1280,\n });\n const params = {\n common: {\n app_id: APPID,\n },\n business: {\n language: 'zh_cn',\n domain: 'iat',\n accent: 'mandarin',\n vad_eos: 5000,\n dwa: 'wpgs',\n },\n data: {\n status: 0,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n },\n };\n this.readyToSend = true;\n this.sendMessage(JSON.stringify(params));\n } catch (error) {\n console.error('record error:', error);\n this.config.onError({ type: 'record', message: '录音失败, 请检查权限!' });\n }\n }\n\n startNewSocket() {\n console.log(\n '%c ❤️ love ==== start new socket ',\n 'color: red; font-size: 16px;',\n startCount,\n new Date().toUTCString(),\n );\n startCount++;\n const websocketUrl = this.config.getWebSocketUrl?.() || getWebSocketUrl();\n if ('WebSocket' in window) {\n this.wsInstance = new WebSocket(websocketUrl);\n } else {\n this.config.onError({ type: 'socketConnect', message: '浏览器不支持 WebSocket' });\n return;\n }\n this.wsInstance.onmessage = (e) => {\n // 处理返回数据\n this.renderResult(e.data);\n };\n this.wsInstance.onerror = (e) => {\n console.error('socket error:', e);\n this.changeBtnStatus('CLOSED');\n };\n this.wsInstance.onclose = async () => {\n this.changeBtnStatus('CLOSED');\n this.config.onWsClose();\n this.recorder?.stop();\n if (this.continueRecord) {\n this.readyToSend = false;\n await this.recorder?.close();\n this.startRecord();\n }\n };\n this.wsInstance.onopen = () => {\n this.changeBtnStatus('OPEN');\n this.recordConfigSend();\n };\n }\n\n recordProcess({ isLastFrame, frameBuffer }: RecordFrameInfo) {\n if (this.isDestroy) {\n return;\n }\n if (!this.wsInstance) {\n return;\n }\n if (this.wsInstance.readyState === this.wsInstance.OPEN) {\n this.sendMessage(\n JSON.stringify({\n data: {\n status: isLastFrame ? 2 : 1,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n audio: toBase64(frameBuffer),\n },\n }),\n );\n }\n if (isLastFrame) {\n this.readyToSend = false;\n this.changeBtnStatus('CLOSING');\n }\n }\n\n initializeRecorder() {\n this.recorder = new RecorderManager();\n if (!this.recorder) {\n return;\n }\n this.recorder.onFrameRecorded = (data: RecordFrameInfo) => {\n this.recordProcess(data);\n };\n this.recorder.onStop = (_data: ArrayBuffer[]) => {\n // do nothing\n };\n this.recorder.onStart = () => {\n console.log('%c ❤️ love ==== recorder onStart:', 'color: red; font-size: 16px;');\n this.changeBtnStatus('OPEN');\n };\n }\n\n changeBtnStatus(status: SocketStatus) {\n this.status = status;\n }\n\n startRecord() {\n if (this.isDestroy) {\n return;\n }\n this.startTimeStamp = Date.now();\n this.resultText = '';\n this.resultTextTemp = '';\n this.startNewSocket();\n }\n\n async stopRecord() {\n console.log('stop record ====', Date.now() - this.startTimeStamp);\n // 结束录音,为了防止刚开始就关闭\n if (Date.now() - this.startTimeStamp < 500) {\n setTimeout(() => {\n this.recorder?.stop();\n }, 100);\n } else {\n this.recorder?.stop();\n }\n // 这里为了防止ws没有返回结束的数据,导致 ws 一直没关,所以加个定时器,5s 后手动关闭\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n }, 5000);\n }\n\n /** 开启持续监听 */\n startContinueRecord() {\n this.continueRecord = true;\n this.startRecord();\n }\n\n stopContinueRecord() {\n this.continueRecord = false;\n this.stopRecord();\n }\n\n destroy() {\n this.isDestroy = true;\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n this.recorder?.stop();\n }, 1000);\n }\n}\n"],"names":["startCount","APPID","API_SECRET","API_KEY","getWebSocketUrl","url","host","apiKey","apiSecret","date","algorithm","headers","signatureOrigin","signatureSha","CryptoJS","signature","authorizationOrigin","authorization","XunFeiStreamVoiceManager","config","__publicField","resultData","jsonData","data","str","ws","i","_a","_b","newConfig","message","params","error","websocketUrl","e","isLastFrame","frameBuffer","toBase64","RecorderManager","_data","status","_c"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easyv/biz-components",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "type": "module",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",