UNPKG

mstf-kit

Version:

一个现代化的 JavaScript/TypeScript 工具库,提供了丰富的常用工具函数

2 lines (1 loc) 41.6 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class e{constructor(e){this._enabled=!1,this._prefix="[AudioDebug]",this._level="all",e&&(void 0!==e.enabled&&(this._enabled=e.enabled),e.prefix&&(this._prefix=e.prefix),e.level&&(this._level=e.level))}enable(){this._enabled=!0}disable(){this._enabled=!1}setLevel(e){this._level=e}setPrefix(e){this._prefix=e}log(e,...t){!this._enabled||"info"!==this._level&&"all"!==this._level||this._prefix}warn(e,...t){!this._enabled||"warn"!==this._level&&"all"!==this._level||console.warn(`${this._prefix} ${e}`,...t)}error(e,...t){!this._enabled||"error"!==this._level&&"all"!==this._level||console.error(`${this._prefix} ${e}`,...t)}get enabled(){return this._enabled}get level(){return this._level}}function t(t){return new e(t)}const i=t({enabled:!1,prefix:"[AudioStream]"});class o{constructor(e={},o={}){this._isProcessing=!0,this._isPlaying=!1,this._currentAudio=null,this._buffer="",this._audioChunks=[],this._audioQueue=[],this._isPlayingQueue=!1,this._shouldContinueQueue=!0,this._decoder=new TextDecoder,this._isFirstChunk=!0,this._pendingFirstChunk=null,this._accumulatedChunks=[],this._playbackDelayCount=0,this._isProcessingQueue=!1,this._playLock=!1,this._processedAudioHashes=new Set,this._lastResponseSize=0,this._debugger=e.debugger||i,!0!==e.debug||e.debugger||(this._debugger=t({enabled:!0,prefix:"[StreamAudioProcessor]"})),this._options={mimeType:e.mimeType||"audio/mpeg",autoPlay:!1!==e.autoPlay,audioDataField:e.audioDataField||"",textDataField:e.textDataField||"",processRawData:e.processRawData||!1,directAudioResponse:e.directAudioResponse||!1,playbackDelayChunks:e.playbackDelayChunks||0,playFirstChunkImmediately:e.playFirstChunkImmediately||!1,playInSequentialOrder:!1!==e.playInSequentialOrder},this._callbacks=o||{},this._debugger.log("音频处理器已初始化",{options:this._options,hasCallbacks:!!o,callbackTypes:o?Object.keys(o):[]}),this._isPlaying=this._options.autoPlay,this._isFirstChunk=!0,this._shouldContinueQueue=!0,this.onDownloadProgress=this.onDownloadProgress.bind(this),this.addAudioToQueue=this.addAudioToQueue.bind(this),this.playAudioBlob=this.playAudioBlob.bind(this)}onDownloadProgress(e){var t,i,o;this._debugger.log("处理下载进度事件",{eventType:typeof e,hasEvent:!!e.event,hasTarget:!!(null==(t=e.event)?void 0:t.target),responseType:(null==(o=null==(i=e.event)?void 0:i.target)?void 0:o.response)?typeof e.event.target.response:"undefined"});try{if(!this._isProcessing)return void this._debugger.log("下载处理已暂停,跳过此事件");if(this._options.directAudioResponse)return this._debugger.log("使用直接音频响应模式处理"),void this._processDirectAudioResponse(e);if(e.event&&e.event.target&&e.event.target.response){this._debugger.log("从进度事件获取响应数据");const t=e.event.target.response;if(this._debugger.log("响应数据类型: "+typeof t),t instanceof ArrayBuffer||t instanceof Blob)return this._debugger.log("响应是二进制数据,直接处理"),void this._processRawAudio(t);if("string"==typeof t){this._debugger.log(`接收到字符串数据,长度: ${t.length}`);let e="";this._buffer.length>0?t.startsWith(this._buffer)?(this._debugger.log("响应包含现有缓冲区,提取新内容"),e=t.slice(this._buffer.length)):(this._debugger.log("响应与缓冲区不匹配,使用整个响应"),e=t):(this._debugger.log("首次接收数据"),e=t),this._buffer=t,e.length>0?(this._debugger.log(`处理新增内容,长度: ${e.length}`),this._callbacks.onRawChunk&&this._callbacks.onRawChunk(e),this._options.processRawData?(this._debugger.log("使用原始数据处理模式"),this._processRawAudio(e)):(this._debugger.log("使用标准数据处理模式"),this._processNewData())):this._debugger.log("没有新增内容,跳过处理")}else this._debugger.log("无法处理未知类型的响应: "+typeof t)}else this._debugger.log("进度事件中无响应数据")}catch(s){this._debugger.error("处理下载进度事件失败:",s),this._callbacks.onError&&this._callbacks.onError(s instanceof Error?s:new Error(String(s)))}}async _handleStream(e){try{const t=e.getReader();for(;;){const{done:e,value:i}=await t.read();if(e)break;const o=this._decoder.decode(i,{stream:!0});this._buffer+=o,this._processNewData()}}catch(t){this._callbacks.onError&&this._callbacks.onError(t instanceof Error?t:new Error(String(t)))}}_processDirectAudioResponse(e){var t,i,o;this._debugger.log("处理直接音频响应",{eventType:typeof e,hasEvent:!!e.event,hasTarget:!!(null==(t=e.event)?void 0:t.target),responseType:(null==(o=null==(i=e.event)?void 0:i.target)?void 0:o.response)?typeof e.event.target.response:"undefined"});try{if(!e.event||!e.event.target)return void this._debugger.log("直接音频响应事件中没有 target");const t=e.event.target.response;if(!t)return void this._debugger.log("直接音频响应中没有实际数据");this._debugger.log("直接音频响应数据类型: "+typeof t);const i=t instanceof Blob?t.size:t instanceof ArrayBuffer?t.byteLength:"string"==typeof t?t.length:0;if(this._debugger.log(`响应大小: ${i}字节, 上次大小: ${this._lastResponseSize}字节`),i>0&&i===this._lastResponseSize)return void this._debugger.log(`跳过可能重复的响应数据: ${i}字节`);if(this._lastResponseSize=i,t instanceof Blob)this._debugger.log(`处理Blob响应: ${t.size}字节, 类型: ${t.type}`),this._processRawAudio(t);else if(t instanceof ArrayBuffer)this._debugger.log(`处理ArrayBuffer响应: ${t.byteLength}字节`),this._processRawAudio(t);else if("string"==typeof t){this._debugger.log(`处理字符串响应: ${t.length}字节`);let e="";this._buffer.length>0?t.startsWith(this._buffer)?(e=t.slice(this._buffer.length),this._debugger.log(`从响应中提取新内容: ${e.length}字节`)):(e=t,this._debugger.log(`使用整个响应作为新内容: ${e.length}字节`)):(e=t,this._debugger.log(`首次接收数据: ${e.length}字节`)),this._buffer=t,e.length>0?(this._callbacks.onRawChunk&&this._callbacks.onRawChunk(e),this._processRawAudio(e)):this._debugger.log("没有新增内容,跳过处理")}else this._debugger.log("尝试处理未知类型的响应"),this._processRawAudio(t)}catch(s){this._debugger.error("处理直接音频响应失败:",s),this._callbacks.onError&&this._callbacks.onError(s instanceof Error?s:new Error(String(s)))}}_processNewData(){this._debugger.log(`处理新数据,当前缓冲区大小: ${this._buffer.length}字节`);const e=this._buffer.split("\n"),t=e.pop()||"";this._buffer=t;let i=0;for(const s of e)s.trim()&&(this._processLine(s.trim()),i++);if(this._debugger.log(`已处理${i}行数据,剩余缓冲区大小: ${this._buffer.length}字节`),this._buffer.trim())try{((()=>{try{return JSON.parse(this._buffer),!0}catch(e){return!1}})()||this._buffer.includes('"audio"')&&this._buffer.includes('"}')||this._buffer.includes("'audio'")&&this._buffer.includes("'}"))&&(this._processLine(this._buffer.trim()),this._buffer="",this._debugger.log("已处理最后一行完整数据"))}catch(o){this._debugger.log("最后一行数据可能不完整,保留在缓冲区")}}_processLine(e){try{this._debugger.log(`处理数据行: 长度=${e.length}字节,前30个字符: ${e.substring(0,30).replace(/\n/g,"\\n")}...`);const i=e.startsWith("data:")?"data:":"",o=i?e.substring(i.length).trim():e.trim();if(!o)return;this._callbacks.onRawChunk&&this._callbacks.onRawChunk(o);let s=this._extractAudioDataFromLine(o);if(s)this._debugger.log(`提取到音频数据,大小=${s.length}`),this._processAudioData(s);else try{const e=JSON.parse(o),t=(e,i="")=>{if(!e||"object"!=typeof e)return null;for(const o in e){const s=i?`${i}.${o}`:o;if("audio"===o&&"string"==typeof e[o])return e[o];if("object"==typeof e[o]&&null!==e[o]){const i=t(e[o],s);if(i)return i}}return null},i=t(e);i&&(this._debugger.log(`通过递归搜索找到音频数据: ${i.substring(0,30)}...`),this._processAudioData(i))}catch(t){this._debugger.log("JSON解析失败,尝试其他方法提取数据")}if(this._options.textDataField){const e=this._extractTextDataFromLine(o);e&&this._processTextData(e)}}catch(i){this._debugger.error("处理数据行失败:",i),this._callbacks.onError&&this._callbacks.onError(i instanceof Error?i:new Error(String(i)))}}_extractAudioDataFromLine(e){try{try{const t=JSON.parse(e),i=this._extractAudioData(t);if(i)return i}catch(t){}const i=this._options.audioDataField.split("."),o=i[i.length-1],s=[new RegExp(`["']${o}["']\\s*:\\s*(["']([^"']*)["'])`,"i"),new RegExp(`["']${o}["']\\s*:\\s*b["']([^"']*)["']`,"i"),new RegExp(`${o}\\s*:\\s*(["']([^"']*)["'])`,"i"),new RegExp(`${o}\\s*:\\s*b["']([^"']*)["']`,"i"),new RegExp("([A-Za-z0-9+/]{100,}={0,2})","i"),new RegExp("data:audio/[^;]+;base64,([A-Za-z0-9+/]+={0,2})","i"),new RegExp("audio\\s*:\\s*{\\s*data\\s*:\\s*[\"'](.*?)[\"']\\s*,?\\s*}","i"),new RegExp("audio\\s*:\\s*{\\s*[^}]*\\s*data\\s*:\\s*[\"'](.*?)[\"']\\s*[^}]*\\s*}","i")];for(const t of s){const i=e.match(t);if(i&&(i[1]||i[2])){const e=i[2]||i[1];if(/^[A-Za-z0-9+/=]+$/.test(e))return this._debugger.log(`通过正则表达式匹配找到可能的音频数据: ${t.toString()}`),e}}if(e.length>100){const t=e.match(/["']([A-Za-z0-9+/]{100,}={0,2})["']/g);if(t&&t.length>0)for(const e of t){const t=e.replace(/^["']|["']$/g,"");if(/^[A-Za-z0-9+/=]+$/.test(t))return this._debugger.log(`找到可能的Base64音频数据,长度: ${t.length}`),t}}return null}catch(i){return this._debugger.error("提取音频数据出错:",i),null}}_extractAudioData(e){if(!e)return null;const t=this._options.audioDataField.split(".");let i=e;for(const s of t){if(!i||"object"!=typeof i||!(s in i))return this._recursiveExtractAudioData(e);i=i[s]}if(i&&"string"!=typeof i){if("object"==typeof i&&i.data&&"string"==typeof i.data)return i.data;if(Array.isArray(i))try{const e=new Uint8Array(i);return btoa(String.fromCharCode.apply(null,Array.from(e)))}catch(o){this._debugger.error("无法将数组转换为Base64字符串:",o)}return this._recursiveExtractAudioData(i)}return"string"==typeof i?i:null}_recursiveExtractAudioData(e){if(!e||"object"!=typeof e)return null;const t=["audio","data","content","value","base64","sound","voice"];for(const i of t)if(i in e&&"string"==typeof e[i]){const t=e[i];if(t.length>50&&/^[A-Za-z0-9+/=]+$/.test(t))return this._debugger.log(`在对象中找到可能的音频字段: ${i}`),t}for(const i in e)if("object"==typeof e[i]&&null!==e[i]){if("audio"===i||"sound"===i||"voice"===i){const t=e[i];if("object"==typeof t)for(const e in t)if("string"==typeof t[e]){const o=t[e];if(o.length>50&&/^[A-Za-z0-9+/=]+$/.test(o))return this._debugger.log(`在嵌套对象${i}.${e}中找到可能的音频数据`),o}}const t=this._recursiveExtractAudioData(e[i]);if(t)return t}return null}_extractTextDataFromLine(e){try{const t=JSON.parse(e);return this._extractTextData(t)}catch(s){}const t=this._options.textDataField.split("."),i=t[t.length-1],o=[new RegExp(`["']${i}["']\\s*:\\s*["']([^"']*)["']`,"i"),new RegExp(`${i}\\s*:\\s*["']([^"']*)["']`,"i")];for(const r of o){const t=e.match(r);if(t&&t[1])return t[1]}return null}_extractTextData(e){if(!e||!this._options.textDataField)return null;const t=this._options.textDataField.split(".");let i=e;for(const o of t){if(!i||"object"!=typeof i||!(o in i))return null;i=i[o]}return"string"==typeof i?i:null}_processTextData(e){try{this._callbacks.onTextData&&this._callbacks.onTextData(e)}catch(t){this._callbacks.onError&&this._callbacks.onError(t instanceof Error?t:new Error(String(t)))}}_processAudioData(e){try{this._debugger.log(`处理音频数据: 长度=${e.length}`);const t=atob(e.replace(/^data:[^;]+;base64,/,"")),i=new Uint8Array(t.length);for(let e=0;e<t.length;e++)i[e]=t.charCodeAt(e);const o=new Blob([i],{type:this._options.mimeType});if(this._debugger.log(`创建音频Blob: ${o.size}字节`),this._audioChunks.push(o),this._options.playbackDelayChunks>0){if(this._accumulatedChunks.push(o),this._playbackDelayCount++,this._debugger.log(`音频块已缓存: ${this._playbackDelayCount}/${this._options.playbackDelayChunks}`),this._playbackDelayCount>=this._options.playbackDelayChunks&&(this._debugger.log("已达到延迟播放阈值,准备播放"),this._options.autoPlay)){const e=new Blob(this._accumulatedChunks,{type:this._options.mimeType});this._debugger.log(`播放累积的音频块: ${e.size}字节`),this._playAudio(e),this._accumulatedChunks=[],this._playbackDelayCount=0}}else this._options.autoPlay&&(this._isFirstChunk&&this._options.playFirstChunkImmediately?(this._debugger.log("立即播放第一个音频块"),this._pendingFirstChunk=o,this._playAudio(o),this._isFirstChunk=!1):(this._debugger.log(`添加音频块到播放队列: ${o.size}字节`),this._addToPlayQueue(o)));this._callbacks.onAudioData?(this._debugger.log(`触发 onAudioData 回调: ${o.size}字节`),this._callbacks.onAudioData(o)):this._debugger.warn("警告: 未提供 onAudioData 回调")}catch(t){this._debugger.error("处理音频数据时出错:",t),this._callbacks.onError&&this._callbacks.onError(t instanceof Error?t:new Error(String(t)))}}_ensureQueueIsProcessing(){this._audioQueue.length>0&&!this._isPlayingQueue&&!this._isProcessingQueue&&this._shouldContinueQueue&&this._isPlaying&&(this._debugger.log("确保音频队列开始处理"),setTimeout((()=>this._processAudioQueue()),10))}_processAudioQueue(){if(this._debugger.log(`处理队列状态: 队列长度=${this._audioQueue.length}, 是否处理中=${this._isProcessingQueue}, 是否正在播放=${this._isPlayingQueue}, 是否应继续=${this._shouldContinueQueue}, 是否允许播放=${this._isPlaying}`),!this._isProcessingQueue&&0!==this._audioQueue.length&&this._isPlaying&&this._shouldContinueQueue)if(this._cleanupAudioQueue(),0!==this._audioQueue.length){if(this._isProcessingQueue=!0,this._currentAudio&&!this._currentAudio.paused&&!this._currentAudio.ended)return this._debugger.log("有音频正在播放,等待完成后再处理队列"),this._isProcessingQueue=!1,void(this._isPlayingQueue||setTimeout((()=>{this._audioQueue.length>0&&!this._isPlayingQueue&&!this._isProcessingQueue&&this._shouldContinueQueue&&this._isPlaying&&(this._debugger.log("播放完成回调没有触发,重新开始处理队列"),this._processAudioQueue())}),500));this._debugger.log("开始播放队列中的第一个音频"),this._playLock&&(this._debugger.warn("检测到播放锁仍然活跃,强制重置"),this._playLock=!1),this._playNextInQueue()}else this._debugger.log("队列清理后为空,退出处理");else this._debugger.log("不满足处理队列条件,退出处理")}_cleanupAudioQueue(){if(this._audioQueue.length<=1)return;this._debugger.log(`清理音频队列前: ${this._audioQueue.length}个项目`);const e=[],t=[];for(const i of this._audioQueue)e.some((e=>Math.abs(e-i.size)<10))?this._debugger.log(`从队列中移除相似大小的Blob: ${i.size}字节`):(t.push(i),e.push(i.size));this._audioQueue=t,this._debugger.log(`清理音频队列后: ${this._audioQueue.length}个项目`)}_playNextInQueue(){if(!this._isPlaying||0===this._audioQueue.length||!this._shouldContinueQueue)return this._isPlayingQueue=!1,void(this._isProcessingQueue=!1);if(this._playLock)return this._isProcessingQueue=!1,void setTimeout((()=>this._processAudioQueue()),100);let e;this._isPlayingQueue=!0,this._options.playInSequentialOrder,e=this._audioQueue.shift(),this._debugger.log(`播放队列: 正在播放队列中的音频,剩余队列长度: ${this._audioQueue.length}`),this._playAudioFromQueue(e)}_playAudioFromQueue(e){if(!this._shouldContinueQueue||!this._isPlaying)return this._isPlayingQueue=!1,void(this._isProcessingQueue=!1);this._playLock=!0;const t=URL.createObjectURL(e),i=new Audio(t);this._currentAudio=i;let o=0;const s=()=>{i.removeEventListener("canplaythrough",s),this._shouldContinueQueue&&this._isPlaying?(this._debugger.log("开始播放音频"),i.play().catch((e=>{this._debugger.error("播放音频失败:",e),o<2?(o++,this._debugger.log(`尝试重新播放音频, 第${o}次重试`),setTimeout((()=>i.play().catch((()=>n()))),300)):n()}))):n()},r=e=>{this._debugger.error("音频加载或播放错误:",e),n()},n=()=>{this._debugger.log("音频播放完成"),this._currentAudio===i&&(this._currentAudio=null),URL.revokeObjectURL(t),i.removeEventListener("canplaythrough",s),i.removeEventListener("error",r),i.removeEventListener("ended",a),this._playLock=!1,this._isProcessingQueue=!1,this._audioQueue.length>0&&this._shouldContinueQueue&&this._isPlaying?(this._debugger.log(`准备播放队列中的下一个音频,剩余队列长度: ${this._audioQueue.length}`),setTimeout((()=>{this._playNextInQueue()}),100)):(this._isPlayingQueue=!1,setTimeout((()=>{this._audioQueue.length>0&&!this._isPlayingQueue&&!this._isProcessingQueue&&this._shouldContinueQueue&&this._isPlaying&&(this._debugger.log("检测到队列中有未播放的音频,重新开始播放"),this._processAudioQueue())}),200))},a=()=>{this._debugger.log("音频播放到末尾"),n()},u=setTimeout((()=>{this._debugger.warn("音频加载超时,强制进入下一个"),this._currentAudio===i&&n()}),5e3);i.addEventListener("canplaythrough",(()=>{clearTimeout(u),s()})),i.addEventListener("error",r),i.addEventListener("ended",a),i.load()}_addToPlayQueue(e){if(this._audioQueue.some((t=>Math.abs(t.size-e.size)<10)))this._debugger.log(`跳过相似大小的音频块添加: ${e.size}字节`);else if(this._options.playInSequentialOrder?this._audioQueue.push(e):this._audioQueue.unshift(e),this._debugger.log(`音频队列: 添加新音频到队列,当前队列长度: ${this._audioQueue.length}`),this._isProcessingQueue||this._isPlayingQueue)this._debugger.log(`队列处理状态: isProcessingQueue=${this._isProcessingQueue}, isPlayingQueue=${this._isPlayingQueue}`);else{if(this._currentAudio&&!this._currentAudio.paused&&!this._currentAudio.ended){this._debugger.log("有音频正在播放,等待它完成后再处理队列");const e=this._currentAudio,t=e.onended;return void(e.onended=i=>{t&&"function"==typeof t&&t.call(e,i),setTimeout((()=>{this._audioQueue.length>0&&!this._isPlayingQueue&&!this._isProcessingQueue&&(this._debugger.log("音频结束后处理队列"),this._processAudioQueue())}),10)})}this._debugger.log("开始处理队列"),setTimeout((()=>this._processAudioQueue()),10)}}play(){this._isPlaying=!0,this._pendingFirstChunk&&(this._audioQueue.push(this._pendingFirstChunk),this._pendingFirstChunk=null,this._isPlayingQueue||this._playNextInQueue())}stop(){this.stopPlayback(),this.stopProcessing()}startProcessing(){this._isProcessing=!0}stopProcessing(){this._isProcessing=!1}get isPlaying(){return this._isPlaying}get isProcessing(){return this._isProcessing}get audioChunks(){return[...this._audioChunks]}get completeAudio(){return 0===this._audioChunks.length?new Blob([],{type:this._options.mimeType}):new Blob(this._audioChunks,{type:this._options.mimeType})}clearAudio(){this._audioChunks=[]}_playAudio(e){this._addToPlayQueue(e)}_processRawAudio(e){var t;this._debugger.log("处理原始音频数据: 类型="+typeof e);try{if(e instanceof ArrayBuffer||ArrayBuffer.isView(e)){this._debugger.log(`处理二进制数据: 大小=${e.byteLength||(null==(t=e.buffer)?void 0:t.byteLength)||"unknown"}字节`);const i=new Blob([e],{type:this._options.mimeType});return this._debugger.log(`从二进制数据创建音频Blob: ${i.size}字节`),this._audioChunks.push(i),this._options.autoPlay&&(this._addToPlayQueue(i),this._ensureQueueIsProcessing()),void(this._callbacks.onAudioData&&(this._debugger.log(`触发原始二进制数据 onAudioData 回调: ${i.size}字节`),this._callbacks.onAudioData(i)))}if(e instanceof Blob)return this._debugger.log(`处理Blob数据: 大小=${e.size}字节`),this._audioChunks.push(e),this._options.autoPlay&&(this._addToPlayQueue(e),this._ensureQueueIsProcessing()),void(this._callbacks.onAudioData&&(this._debugger.log(`触发Blob onAudioData 回调: ${e.size}字节`),this._callbacks.onAudioData(e)));if(Array.isArray(e)||e instanceof Uint8Array){this._debugger.log(`处理数组数据: 长度=${e.length}`);const t=new Blob(Array.isArray(e)?[new Uint8Array(e)]:[e],{type:this._options.mimeType});return this._debugger.log(`从数组创建音频Blob: ${t.size}字节`),this._audioChunks.push(t),this._options.autoPlay&&(this._addToPlayQueue(t),this._ensureQueueIsProcessing()),void(this._callbacks.onAudioData&&(this._debugger.log(`触发数组 onAudioData 回调: ${t.size}字节`),this._callbacks.onAudioData(t)))}if("string"==typeof e){if(this._debugger.log(`处理字符串数据: 长度=${e.length}`),/^[A-Za-z0-9+/=]+$/.test(e.trim()))return this._debugger.log("检测到可能是Base64字符串,尝试解码"),void this._processAudioData(e);{this._debugger.log("将字符串数据作为原始文本处理");const t=new Blob([e],{type:"text/plain"});return this._audioChunks.push(t),void(this._callbacks.onTextData?(this._debugger.log("触发 onTextData 回调"),this._callbacks.onTextData(e)):this._callbacks.onAudioData&&(this._debugger.log(`触发文本数据的 onAudioData 回调: ${t.size}字节`),this._callbacks.onAudioData(t)))}}if("object"==typeof e&&null!==e){this._debugger.log("处理对象数据:",Object.keys(e));let t=!1;const i=["audio","sound","voice","data","content","audioData","soundData"];for(const o of i)if(o in e&&"string"==typeof e[o]){const i=e[o];if(/^[A-Za-z0-9+/=]+$/.test(i)){this._debugger.log(`从对象中提取到可能的音频数据,字段: ${o}`),this._processAudioData(i),t=!0;break}}else if(o in e&&(e[o]instanceof ArrayBuffer||ArrayBuffer.isView(e[o])||e[o]instanceof Blob)){this._debugger.log(`从对象中提取到二进制音频数据,字段: ${o}`),this._processRawAudio(e[o]),t=!0;break}if(!t){this._debugger.log("未找到可识别的音频数据,将对象转为JSON");const t=JSON.stringify(e);this._callbacks.onTextData&&(this._debugger.log("触发JSON对象的 onTextData 回调"),this._callbacks.onTextData(t));const i=new Blob([t],{type:"application/json"});this._callbacks.onAudioData&&(this._debugger.log(`触发JSON对象的 onAudioData 回调: ${i.size}字节`),this._callbacks.onAudioData(i))}return}this._debugger.log("无法处理未知类型的数据: "+typeof e)}catch(i){this._debugger.error("处理原始音频数据时出错:",i),this._callbacks.onError&&this._callbacks.onError(i instanceof Error?i:new Error(String(i)))}}stopPlayback(){if(this._isPlaying=!1,this._shouldContinueQueue=!1,this._currentAudio){try{this._currentAudio.pause(),this._currentAudio.currentTime=0}catch(e){}this._currentAudio=null}this._audioQueue=[],this._pendingFirstChunk=null,this._isPlayingQueue=!1,this._isProcessingQueue=!1,this._playLock=!1}resumePlayback(){this._isPlaying=!0,this._shouldContinueQueue=!0,this._cleanupAudioQueue(),this._pendingFirstChunk&&(this._audioQueue.some((e=>Math.abs(e.size-this._pendingFirstChunk.size)<10))?this._debugger.log("跳过添加相似大小的首个待处理音频"):this._audioQueue.push(this._pendingFirstChunk),this._pendingFirstChunk=null),this._audioQueue.length>0&&!this._isProcessingQueue&&!this._isPlayingQueue?(this._debugger.log("恢复播放: 开始处理音频队列"),this._processAudioQueue()):this._debugger.log(`恢复播放: 队列状态 - 长度=${this._audioQueue.length}, 处理中=${this._isProcessingQueue}, 播放中=${this._isPlayingQueue}`)}addAudioToQueue(e){this._isPlaying&&this._addToPlayQueue(e)}playAudioBlob(e){if(!e)return;const t=URL.createObjectURL(e),i=new Audio(t);this._currentAudio&&this._currentAudio.pause(),this._currentAudio=i,i.onended=()=>{URL.revokeObjectURL(t),this._currentAudio===i&&(this._currentAudio=null)},i.onerror=()=>{URL.revokeObjectURL(t),this._currentAudio===i&&(this._currentAudio=null)},i.load(),i.play().catch((e=>{this._debugger.error("播放音频失败:",e),URL.revokeObjectURL(t),this._currentAudio===i&&(this._currentAudio=null)}))}createAudioElement(e){if(!e)throw new Error("无效的音频数据");const t=URL.createObjectURL(e),i=new Audio(t);return i.onended=()=>{URL.revokeObjectURL(t)},i.onerror=()=>{URL.revokeObjectURL(t)},i.load(),i}get bufferedChunksCount(){return this._audioQueue.length}get firstAudioChunk(){return this._pendingFirstChunk}finalizeProcessing(){if(this._isProcessing){this._debugger.log("完成音频处理,处理剩余数据",{"缓冲区长度":this._buffer.length,"音频块数量":this._audioChunks.length,"队列长度":this._audioQueue.length,"播放状态":this._isPlaying,"自动播放":this._options.autoPlay,"有待处理首个音频":!!this._pendingFirstChunk});try{if(this._buffer.trim()&&(this._processLine(this._buffer.trim()),this._buffer=""),this._pendingFirstChunk&&this._isPlaying&&this._options.autoPlay&&(this._debugger.log("处理待播放的首个音频片段"),this._options.playFirstChunkImmediately?0!==this._audioQueue.length||this._currentAudio&&!this._currentAudio.paused&&!this._currentAudio.ended?this._addToPlayQueue(this._pendingFirstChunk):(this._debugger.log("立即播放首个待播放音频区块"),this.playAudioBlob(this._pendingFirstChunk)):this._addToPlayQueue(this._pendingFirstChunk),this._pendingFirstChunk=null,this._isProcessingQueue||this._isPlayingQueue||setTimeout((()=>this._processAudioQueue()),10)),this._options.playInSequentialOrder&&0===this._audioQueue.length&&this._audioChunks.length>0&&this._isPlaying&&this._options.autoPlay&&(this._debugger.log("没有队列中的音频,但有处理过的音频块,尝试播放最后一个"),!this._currentAudio||this._currentAudio.paused||this._currentAudio.ended)){const e=this._audioChunks[this._audioChunks.length-1];this.playAudioBlob(e)}this._audioQueue.length>0&&!this._isPlayingQueue&&!this._isProcessingQueue&&this._shouldContinueQueue&&this._isPlaying&&(this._debugger.log("检测到队列中可能有未处理的音频,尝试重新启动队列处理"),setTimeout((()=>this._processAudioQueue()),100))}catch(e){this._debugger.error("完成处理时出错:",e),this._audioQueue.length>0&&!this._isPlayingQueue&&this._isPlaying&&this._shouldContinueQueue&&(this._debugger.log("尽管有错误,仍尝试处理音频队列"),setTimeout((()=>{this._isProcessingQueue=!1,this._processAudioQueue()}),200))}}}_hashString(e){if(e.length>1e3){const t=[e.substring(0,100),e.substring(Math.floor(e.length/2)-50,Math.floor(e.length/2)+50),e.substring(e.length-100)];e=t.join("|")+e.length}let t=0;for(let i=0;i<e.length;i++)t=(t<<5)-t+e.charCodeAt(i),t|=0;return(t>>>0).toString(16)}}function s(e){const t=new AbortController,i=t.signal,{url:o,method:s="GET",headers:r={},body:n,timeout:a=3e4,directAudioResponse:u=!1,retries:l=0,retryDelay:h=1e3,processor:d,onProgress:g,onSuccess:c,onError:_,onComplete:p}=e,f=setTimeout((()=>{t.abort()}),a),b={method:s,headers:{Accept:u?"audio/*":"application/json, text/plain, */*",...r},signal:i,credentials:"same-origin"};if(n)if("object"!=typeof n||n instanceof FormData||n instanceof Blob)b.body=n;else{b.body=JSON.stringify(n);const e=b.headers;e["Content-Type"]||(e["Content-Type"]="application/json")}let y=0,A=0;const m=async()=>{var e;try{const t=await fetch(o,b);if(!t.ok)throw new Error(`HTTP error! Status: ${t.status}`);if(u){const e=await t.blob();if(d){const t={event:{target:{response:e}}};d.onDownloadProgress(t),d.finalizeProcessing()}return g&&g({loaded:e.size,total:e.size,event:{target:{response:e}}}),void(c&&c(e))}const i=null==(e=t.body)?void 0:e.getReader();if(!i){const e=await t.text();if(d){const t={event:{target:{response:e}}};d.onDownloadProgress(t),d.finalizeProcessing()}return g&&g({loaded:e.length,total:e.length}),void(c&&c(e))}for(;;){const{done:e,value:t}=await i.read();if(e){d&&d.finalizeProcessing();break}if(A+=t.length,g||d){const e={loaded:A,total:void 0,event:{target:{response:t}}};g&&g(e),d&&d.onDownloadProgress(e)}}c&&c(t)}catch(t){if("AbortError"===t.name)return void(_&&_(new Error("请求被取消")));if(y<l)return y++,await new Promise((e=>setTimeout(e,h))),m();_&&_(t instanceof Error?t:new Error(String(t)))}finally{clearTimeout(f),p&&p()}};return m(),{abort:()=>{t.abort()}}}function r(e,t){return new o(e||{},t)}function n(e={}){const t=async function(e){const i={...t.defaults,...e},{url:o,method:n,headers:a,body:u,data:l,params:h,responseType:d,timeout:g,directAudioResponse:c,retries:_,retryDelay:p,processor:f,processorOptions:b,processorCallbacks:y,onDownloadProgress:A,onSuccess:m,onError:w,onComplete:P}=i;if(!o)throw new Error("URL不能为空");let D=o;if(h){const e=Object.entries(h).map((([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`)).join("&");D+=(o.includes("?")?"&":"?")+e}const k=l||u;let Q=f;Q||!b&&!y||(Q=r(b||{},y||{}));const v=n;return new Promise(((t,o)=>{const r=s({url:D,method:v,headers:a,body:k,timeout:g,directAudioResponse:c||"blob"===d||"arraybuffer"===d,retries:_,retryDelay:p,processor:Q,onProgress:A,onSuccess:e=>{m&&m(e),t({data:e,status:200,statusText:"OK",headers:{},config:i,processor:Q})},onError:e=>{w&&w(e),o(e)},onComplete:P});e.cancelController=r}))};return t.defaults={method:"GET",headers:{},timeout:3e4,...e},t.get=function(e,i={}){return t({...i,url:e,method:"GET"})},t.post=function(e,i,o={}){return t({...o,url:e,method:"POST",data:i})},t.createProcessor=function(e,t){return r(e,t)},t}const a=n({headers:{"Content-Type":"application/json"}});async function u(e,t,i={}){const o=new AbortController,s=o.signal,{directAudioResponse:r=!1,mimeType:n,onProgress:a,onComplete:u,onError:l}=i;n&&(t._options.mimeType=n);try{if(r){let i;if(!("blob"in e)||"function"!=typeof e.blob)throw new Error("无法从响应中获取Blob数据");i=await e.blob();const s={event:{target:{response:i}}};return t.onDownloadProgress(s),a&&a(i.size),u&&u(),{abort:()=>o.abort()}}const i="body"in e?e.body:null;if(!i){if("text"in e&&"function"==typeof e.text){const i=await e.text();if(i){const e={loaded:i.length,total:i.length,event:{target:{response:i}}};return t.onDownloadProgress(e),t.finalizeProcessing(),a&&a(i.length),u&&u(),{abort:()=>o.abort()}}}throw new Error("响应对象不包含可读流或文本数据")}const n=i.getReader();let h=0;return(async()=>{try{for(;!s.aborted;){const{done:e,value:i}=await n.read();if(e){t.finalizeProcessing(),u&&u();break}h+=i.length;const o={loaded:h,total:void 0,event:{target:{response:i}}};t.onDownloadProgress(o),a&&a(h)}}catch(e){!s.aborted&&l&&l(e instanceof Error?e:new Error(String(e)))}})(),{abort:()=>{o.abort(),n.cancel().catch((()=>{}))}}}catch(h){return l&&l(h instanceof Error?h:new Error(String(h))),{abort:()=>o.abort()}}}async function l(e,o={},s={}){var n;const a=o.debugger||(o.debug?t({enabled:!0,prefix:"[processAxiosResponse]"}):i);a.log("开始处理 Axios 响应:",{hasData:!!e.data,dataType:e.data?typeof e.data:"undefined",isArrayBuffer:e.data instanceof ArrayBuffer,responseType:(null==(n=e.config)?void 0:n.responseType)||"未知",hasAudioDataField:!!o.audioDataField,audioDataField:o.audioDataField||""});const u=r({directAudioResponse:!0,...o},s);a.log("音频处理器配置:",{mimeType:u._options.mimeType,autoPlay:u._options.autoPlay,directAudioResponse:u._options.directAudioResponse,audioDataField:u._options.audioDataField});const l=new AbortController;try{if(!e)return a.error("警告: 传入的 axiosResponse 为空!"),{processor:u,controller:{abort:()=>l.abort()}};if(e.data)if(!o.audioDataField||"object"!=typeof e.data||e.data instanceof ArrayBuffer||e.data instanceof Blob)if(e.data instanceof ArrayBuffer||e.config&&"arraybuffer"===e.config.responseType){a.log("处理ArrayBuffer响应数据");const t=new Blob([e.data],{type:o.mimeType||"audio/mpeg"});u.addAudioToQueue(t),u._options.autoPlay&&u.play(),s.onAudioData&&s.onAudioData(t)}else if(e.data instanceof Blob)a.log("处理Blob响应数据"),u.addAudioToQueue(e.data),u._options.autoPlay&&u.play(),s.onAudioData&&s.onAudioData(e.data);else if("string"==typeof e.data){a.log("处理字符串响应数据");const t={event:{target:{response:e.data}}};u.onDownloadProgress(t)}else{a.log("处理其他类型响应数据");const t={event:{target:{response:e.data}}};u.onDownloadProgress(t)}else{a.log(`尝试从响应中提取 ${o.audioDataField} 字段`);const t=o.audioDataField.split(".");let i=e.data;for(const e of t){if(!i||"object"!=typeof i||!(e in i)){i=null;break}i=i[e]}if(i)if(a.log("成功提取到音频数据: 类型="+typeof i),i instanceof ArrayBuffer||i instanceof Blob){const e=i instanceof ArrayBuffer?new Blob([i],{type:o.mimeType||"audio/mpeg"}):i;u.addAudioToQueue(e),u._options.autoPlay&&u.play(),s.onAudioData&&s.onAudioData(e)}else if("string"==typeof i)if(/^[A-Za-z0-9+/=]+$/.test(i.trim()))try{const e=atob(i.trim()),t=new Uint8Array(e.length);for(let i=0;i<e.length;i++)t[i]=e.charCodeAt(i);const r=new Blob([t],{type:o.mimeType||"audio/mpeg"});u.addAudioToQueue(r),u._options.autoPlay&&u.play(),s.onAudioData&&s.onAudioData(r)}catch(h){a.error("Base64解码失败:",h),s.onTextData&&s.onTextData(i)}else s.onTextData&&s.onTextData(i);else{const e=JSON.stringify(i);s.onTextData&&s.onTextData(e)}else a.warn(`在响应中未找到指定的音频数据字段: ${o.audioDataField}`)}else a.warn("响应数据为空");return u.finalizeProcessing(),{processor:u,controller:{abort:()=>l.abort()}}}catch(d){return a.error("处理Axios响应时出错:",d),s.onError&&s.onError(d instanceof Error?d:new Error(String(d))),{processor:u,controller:{abort:()=>l.abort()}}}finally{a.log("Axios响应处理完成")}}async function h(e,o={},s={}){var n;const a=o.debugger||(o.debug?t({enabled:!0,prefix:"[processCompleteAudio]"}):i);return a.log("处理完整音频响应",{hasData:!!e.data,status:e.status,headers:e.headers,dataType:e.data?e.data instanceof ArrayBuffer?"ArrayBuffer":typeof e.data:"undefined",dataSize:e.data instanceof ArrayBuffer?e.data.byteLength:e.data?"unknown":0,responseType:(null==(n=e.config)?void 0:n.responseType)||"未知",hasAudioDataField:!!o.audioDataField}),new Promise((async(t,i)=>{try{if(!e||!e.data){const e=new Error("响应为空或不包含数据");return s.onError&&s.onError(e),void i(e)}if(e.data instanceof ArrayBuffer&&e.data.byteLength<100)try{const t=(new TextDecoder).decode(e.data);if(t.trim().startsWith("{")||t.trim().startsWith("["))try{const e=JSON.parse(t);a.warn("检测到错误响应:",e);const o=new Error(`服务器返回错误: ${JSON.stringify(e)}`);return s.onError&&s.onError(o),void i(o)}catch(n){}}catch(n){}const u=r({mimeType:o.mimeType||"audio/mpeg",autoPlay:void 0===o.autoPlay||o.autoPlay,...o},s);let l;if(!o.audioDataField||"object"!=typeof e.data||e.data instanceof ArrayBuffer||e.data instanceof Blob)if(e.data instanceof ArrayBuffer||e.config&&"arraybuffer"===e.config.responseType)a.log("直接使用ArrayBuffer数据创建音频Blob"),l=new Blob([e.data],{type:o.mimeType||"audio/mpeg"});else if(e.data instanceof Blob)a.log("直接使用Blob数据"),l=e.data;else if("string"==typeof e.data)if(a.log("处理字符串响应数据"),/^[A-Za-z0-9+/=]+$/.test(e.data.trim()))try{const t=atob(e.data.trim()),i=new Uint8Array(t.length);for(let e=0;e<t.length;e++)i[e]=t.charCodeAt(e);l=new Blob([i],{type:o.mimeType||"audio/mpeg"})}catch(n){a.error("Base64解码失败:",n),l=new Blob([e.data],{type:"text/plain"})}else l=new Blob([e.data],{type:"text/plain"});else{a.warn("未知类型响应数据,尝试转换为JSON");try{const t=JSON.stringify(e.data);l=new Blob([t],{type:"application/json"})}catch(n){a.error("转换响应数据失败:",n),l=new Blob([],{type:"application/octet-stream"})}}else{a.log(`尝试从响应中提取 ${o.audioDataField} 字段`);const t=o.audioDataField.split(".");let i=e.data;for(const e of t){if(!i||"object"!=typeof i||!(e in i)){i=null;break}i=i[e]}if(i)if(a.log("成功提取到音频数据: 类型="+typeof i),i instanceof ArrayBuffer)l=new Blob([i],{type:o.mimeType||"audio/mpeg"});else if(i instanceof Blob)l=i;else if("string"==typeof i&&/^[A-Za-z0-9+/=]+$/.test(i.trim()))try{const e=atob(i.trim()),t=new Uint8Array(e.length);for(let i=0;i<e.length;i++)t[i]=e.charCodeAt(i);l=new Blob([t],{type:o.mimeType||"audio/mpeg"})}catch(n){a.error("Base64解码失败:",n),l=new Blob([i],{type:"text/plain"})}else{const e="string"==typeof i?i:JSON.stringify(i);l=new Blob([e],{type:"text/plain"})}else a.warn(`在响应中未找到指定的音频数据字段: ${o.audioDataField}`),l=new Blob([],{type:o.mimeType||"audio/mpeg"})}a.log(`创建的音频Blob: ${l.size}字节, 类型: ${l.type}`),!1!==o.autoPlay&&u.playAudioBlob(l),s.onAudioData&&s.onAudioData(l),t({processor:u,audioBlob:l})}catch(u){a.error("处理音频响应失败:",u),s.onError&&s.onError(u instanceof Error?u:new Error(String(u))),i(u)}}))}async function d(e,o={}){const s=o.debug?t({enabled:!0,prefix:"[getAudioFromResponse]"}):i;s.log("从响应中提取音频数据",{hasResponse:!!e,hasData:e&&!!e.data,hasAudioDataField:!!o.audioDataField,dataType:e&&e.data?e.data instanceof ArrayBuffer?"ArrayBuffer":typeof e.data:"未知"});try{if(!e)throw new Error("响应为空");const t=o.mimeType||"audio/mpeg";if(o.audioDataField&&e.data&&"object"==typeof e.data&&!(e.data instanceof ArrayBuffer)&&!(e.data instanceof Blob)){s.log(`尝试从响应中提取 ${o.audioDataField} 字段`);const i=o.audioDataField.split(".");let n=e.data;for(const e of i){if(!n||"object"!=typeof n||!(e in n)){n=null;break}n=n[e]}if(!n)throw s.warn(`在响应中未找到指定的音频数据字段: ${o.audioDataField}`),new Error(`在响应中未找到指定的音频数据字段: ${o.audioDataField}`);if(s.log("成功提取到指定字段数据: 类型="+typeof n),n instanceof ArrayBuffer)return new Blob([n],{type:t});if(n instanceof Blob)return n;if("string"!=typeof n||!/^[A-Za-z0-9+/=]+$/.test(n.trim()))throw s.warn("提取的数据不是有效的音频格式: "+typeof n),new Error("提取的数据字段不是有效的音频格式: "+typeof n);try{const e=atob(n.trim()),i=new Uint8Array(e.length);for(let t=0;t<e.length;t++)i[t]=e.charCodeAt(t);return new Blob([i],{type:t})}catch(r){throw s.error("Base64解码失败:",r),new Error("无法解析Base64音频数据")}}if(e.data instanceof ArrayBuffer)return s.log("将ArrayBuffer转换为Blob"),new Blob([e.data],{type:t});if(e.data instanceof Blob)return s.log("直接使用响应中的Blob"),e.data;if("string"!=typeof e.data)throw"object"==typeof e.data&&null!==e.data?(s.warn("响应为对象但不是可用的音频格式"),new Error("无法解析为音频数据: 响应为JSON或普通对象")):(s.error("未知的响应数据类型"),new Error("无法解析为音频数据: 未知类型 "+typeof e.data));if(!/^[A-Za-z0-9+/=]+$/.test(e.data.trim()))throw s.warn("响应数据是字符串但不是Base64格式"),new Error("无法解析为音频数据: 非Base64字符串");s.log("检测到Base64字符串,尝试解码");try{const i=atob(e.data.trim()),o=new Uint8Array(i.length);for(let e=0;e<i.length;e++)o[e]=i.charCodeAt(e);return new Blob([o],{type:t})}catch(r){throw s.error("Base64解码失败:",r),new Error("无法解析为音频数据: Base64解码失败")}}catch(n){return s.error("从响应中提取音频数据失败:",n),new Blob([],{type:o.mimeType||"audio/mpeg"})}}const g={async streamingExample(){const e=r({mimeType:"audio/mp3",autoPlay:!0},{onAudioData:e=>{}});return await fetch("/audio/stream",{method:"POST"}),e.finalizeProcessing(),{processor:e}},async completeResponseExample(){const e=await fetch("/audio/synthesize",{method:"POST"}).then((e=>e.arrayBuffer())).then((e=>({data:e,status:200}))),{processor:t,audioBlob:i}=await h(e,{mimeType:"audio/mp3",autoPlay:!0,debug:!0},{onAudioData:e=>{}});return{processor:t,audioBlob:i}},async simpleExample(){const e=await fetch("/audio/synthesize",{method:"POST"}).then((e=>e.arrayBuffer())).then((e=>({data:e,status:200}))),t=await d(e,{mimeType:"audio/mp3",debug:!0}),i=URL.createObjectURL(t);return new Audio(i).play(),{audioBlob:t}}};exports.AudioDebugger=e,exports.AudioResponseExamples=g,exports.StreamAudioProcessor=o,exports.createAudioDebugger=t,exports.createAudioInstance=function(e={}){return n(e)},exports.createAudioStream=function(e,t={},i={}){const o=r(t,i);return{processor:o,controller:s({...e,processor:o})}},exports.createStreamAudioProcessor=r,exports.fetchAudioStream=s,exports.getAudioFromResponse=d,exports.globalAudioDebugger=i,exports.handleAudioResponse=async function(e,o){const s=(null==o?void 0:o.debugger)||((null==o?void 0:o.debug)?t({enabled:!0,prefix:"[handleAudioResponse]"}):i);return s.log("处理音频响应示例"),new Promise(((t,i)=>{var r,n,a,u;const h=[],d={onAudioData:e=>{s.log(`收到音频数据: ${e.size}字节`),h.push(e)},onTextData:e=>{s.log(`收到文本数据: ${e.length}字节`)},onError:e=>{s.error("处理音频时出错:",e),i(e)},onRawChunk:e=>{s.log(`收到原始数据块: ${e.length}字节`)}},g={mimeType:(null==o?void 0:o.mimeType)||"audio/mpeg",autoPlay:null==(r=null==o?void 0:o.autoPlay)||r,directAudioResponse:null==(n=null==o?void 0:o.directAudioResponse)||n,playbackDelayChunks:null!=(a=null==o?void 0:o.playbackDelayChunks)?a:0,playFirstChunkImmediately:null==(u=null==o?void 0:o.playFirstChunkImmediately)||u,debugger:s};l(e,g,d).then((({processor:e})=>{s.log(`音频处理完成,共收集到 ${h.length} 个音频块`),setTimeout((()=>{t(h)}),100)})).catch(i)}))},exports.handleStreamResponse=u,exports.msAudio=a,exports.processAxiosResponse=l,exports.processCompleteAudioResponse=h,exports.processExternalResponse=async function(e,t={},i={}){const o=r(t,i);return{processor:o,controller:await u(e,o,{directAudioResponse:t.directAudioResponse,mimeType:t.mimeType,onError:i.onError})}};