lsc 1 ay önce
ebeveyn
işleme
0767fc24b3

+ 1 - 1
dist/index.html

@@ -32,7 +32,7 @@
       width: 100%;
       background: #e6eaf0;
       font-family: '黑体';
-    }</style><link href=./static/css/app.baa34a6632b591fc97a22d80fecf864e.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.4dce3a5441c732d84c44.js></script><script type=text/javascript src=./static/js/app.db35cab79907c4382a9d.js></script></body></html><script>function stopSafari() {
+    }</style><link href=./static/css/app.9f54f422b0e5340515a2a7b88240a883.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.f3ed8b06bd4bf0a5a411.js></script><script type=text/javascript src=./static/js/vendor.9fbc3b8ae50969dbcaf1.js></script><script type=text/javascript src=./static/js/app.5b9940dc8e9f53f88d96.js></script></body></html><script>function stopSafari() {
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/css/app.9f54f422b0e5340515a2a7b88240a883.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/css/app.9f54f422b0e5340515a2a7b88240a883.css.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/0.14d2881b7f04cb07b724.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/0.14d2881b7f04cb07b724.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/app.5b9940dc8e9f53f88d96.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/app.5b9940dc8e9f53f88d96.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/app.db35cab79907c4382a9d.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/app.db35cab79907c4382a9d.js.map


+ 0 - 2
dist/static/js/manifest.3ad1d5771e9b13dbdad2.js

@@ -1,2 +0,0 @@
-!function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];a<e.length;a++)i=e[a],o[i]&&l.push(o[i][0]),o[i]=0;for(f in u)Object.prototype.hasOwnProperty.call(u,f)&&(r[f]=u[f]);for(n&&n(e,u,c);l.length;)l.shift()();if(c)for(a=0;a<c.length;a++)p=t(t.s=c[a]);return p};var e={},o={2:0};function t(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return r[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=r,t.c=e,t.d=function(r,n,e){t.o(r,n)||Object.defineProperty(r,n,{configurable:!1,enumerable:!0,get:e})},t.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return t.d(n,"a",n),n},t.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},t.p="./",t.oe=function(r){throw console.error(r),r}}([]);
-//# sourceMappingURL=manifest.3ad1d5771e9b13dbdad2.js.map

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/manifest.3ad1d5771e9b13dbdad2.js.map


+ 2 - 0
dist/static/js/manifest.f3ed8b06bd4bf0a5a411.js

@@ -0,0 +1,2 @@
+!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,i){for(var u,a,f,s=0,l=[];s<r.length;s++)a=r[s],t[a]&&l.push(t[a][0]),t[a]=0;for(u in c)Object.prototype.hasOwnProperty.call(c,u)&&(e[u]=c[u]);for(n&&n(r,c,i);l.length;)l.shift()();if(i)for(s=0;s<i.length;s++)f=o(o.s=i[s]);return f};var r={},t={3:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,o.nc&&i.setAttribute("nonce",o.nc),i.src=o.p+"static/js/"+e+"."+{0:"14d2881b7f04cb07b724"}[e]+".js";var u=setTimeout(a,12e4);function a(){i.onerror=i.onload=null,clearTimeout(u);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return i.onerror=i.onload=a,c.appendChild(i),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="./",o.oe=function(e){throw console.error(e),e}}([]);
+//# sourceMappingURL=manifest.f3ed8b06bd4bf0a5a411.js.map

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/manifest.f3ed8b06bd4bf0a5a411.js.map


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/vendor.9fbc3b8ae50969dbcaf1.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/static/js/vendor.9fbc3b8ae50969dbcaf1.js.map


+ 6 - 0
package-lock.json

@@ -9,6 +9,7 @@
       "version": "1.0.0",
       "dependencies": {
         "@antv/g6": "^4.8.25",
+        "@breezystack/lamejs": "^1.2.7",
         "@ffmpeg/ffmpeg": "^0.9.8",
         "@microsoft/fetch-event-source": "^2.0.1",
         "@vue-office/docx": "^1.6.2",
@@ -559,6 +560,11 @@
       "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
       "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
     },
+    "node_modules/@breezystack/lamejs": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/@breezystack/lamejs/-/lamejs-1.2.7.tgz",
+      "integrity": "sha512-6wc7ck65ctA75Hq7FYHTtTvGnYs6msgdxiSUICQ+A01nVOWg6rqouZB8IdyteRlfpYYiFovkf67dIeOgWIUzTA=="
+    },
     "node_modules/@ffmpeg/core": {
       "version": "0.10.0",
       "resolved": "https://registry.npmmirror.com/@ffmpeg/core/-/core-0.10.0.tgz",

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
   },
   "dependencies": {
     "@antv/g6": "^4.8.25",
+    "@breezystack/lamejs": "^1.2.7",
     "@ffmpeg/ffmpeg": "^0.9.8",
     "@microsoft/fetch-event-source": "^2.0.1",
     "@vue-office/docx": "^1.6.2",

+ 254 - 2
src/components/pages/knowledge/fileBox.vue

@@ -309,6 +309,232 @@ import wOffice from "../test/file/wOffice.vue";
 import { v4 as uuidv4 } from "uuid";
 import checkDialog from "./components/checkDialog";
 
+
+// 音频格式转换:将非MP3音频转换为真正的MP3格式(使用@breezystack/lamejs库)
+const convertAudioToMp3 = async (file) => {
+  let lamejs;
+  let Mp3Encoder;
+  let audioContext = null;
+  
+  try {
+    // 确保传入的是 File 对象
+    let fileObj = file;
+    
+    // 如果不是 File 对象,尝试从可能的属性中获取
+    if (!(fileObj instanceof File) && !(fileObj instanceof Blob)) {
+      // 尝试从 raw 属性获取
+      if (fileObj.raw && (fileObj.raw instanceof File || fileObj.raw instanceof Blob)) {
+        fileObj = fileObj.raw;
+      } else {
+        throw new Error('传入的不是有效的 File 或 Blob 对象');
+      }
+    }
+    
+    // 检查文件大小(限制为50MB)
+    if (fileObj.size > 50 * 1024 * 1024) {
+      throw new Error('音频文件过大(超过50MB),无法转换');
+    }
+    
+    // 导入修复版本的lamejs
+    console.log('开始加载lamejs库...');
+    const lamejsModule = await import('@breezystack/lamejs');
+    
+    // 获取Mp3Encoder
+    if (lamejsModule.default && lamejsModule.default.Mp3Encoder) {
+      Mp3Encoder = lamejsModule.default.Mp3Encoder;
+      lamejs = lamejsModule.default;
+    } else if (lamejsModule.Mp3Encoder) {
+      Mp3Encoder = lamejsModule.Mp3Encoder;
+      lamejs = lamejsModule;
+    } else if (lamejsModule.default) {
+      // 检查是否是命名空间导出
+      lamejs = lamejsModule.default;
+      if (lamejs.Mp3Encoder) {
+        Mp3Encoder = lamejs.Mp3Encoder;
+      } else {
+        throw new Error('lamejs库结构异常:无法找到Mp3Encoder');
+      }
+    } else {
+      throw new Error('lamejs库结构异常:无法找到Mp3Encoder');
+    }
+    
+    if (!Mp3Encoder || typeof Mp3Encoder !== 'function') {
+      throw new Error('lamejs库加载失败:Mp3Encoder未找到或不是构造函数');
+    }
+    
+    console.log('lamejs加载成功,Mp3Encoder类型:', typeof Mp3Encoder);
+    
+    // 创建AudioContext
+    const AudioContextClass = window.AudioContext || window.webkitAudioContext;
+    if (!AudioContextClass) {
+      throw new Error('浏览器不支持Web Audio API');
+    }
+    
+    audioContext = new AudioContextClass();
+    
+    // 读取文件为ArrayBuffer
+    console.log('开始读取音频文件,大小:', fileObj.size, 'bytes');
+    
+    // 读取文件为ArrayBuffer
+    let arrayBuffer;
+    if (typeof fileObj.arrayBuffer === 'function') {
+      // 使用 File/Blob 的 arrayBuffer 方法
+      arrayBuffer = await fileObj.arrayBuffer();
+    } else {
+      // 如果没有 arrayBuffer 方法,使用 FileReader
+      arrayBuffer = await new Promise((resolve, reject) => {
+        const reader = new FileReader();
+        reader.onload = (e) => resolve(e.target.result);
+        reader.onerror = reject;
+        reader.readAsArrayBuffer(fileObj);
+      });
+    }
+    console.log('文件读取完成,ArrayBuffer大小:', arrayBuffer.byteLength);
+    
+    // 解码音频文件
+    console.log('开始解码音频数据...');
+    const audioBuffer = await audioContext.decodeAudioData(arrayBuffer.slice(0));
+    console.log('音频解码完成,采样率:', audioBuffer.sampleRate, '声道数:', audioBuffer.numberOfChannels, '时长:', audioBuffer.duration, '秒');
+    
+    // 获取音频数据
+    const sampleRate = audioBuffer.sampleRate;
+    const numberOfChannels = audioBuffer.numberOfChannels;
+    const samples = audioBuffer.getChannelData(0); // 使用第一个声道
+    
+    // 处理音频声道:如果是立体声,保持立体声;如果是多声道,混合为单声道
+    let audioData;
+    let channels = numberOfChannels;
+    
+    if (numberOfChannels === 1) {
+      // 单声道,直接使用
+      audioData = samples;
+      channels = 1;
+    } else if (numberOfChannels === 2) {
+      // 立体声,保持立体声(交错存储)
+      console.log('检测到立体声音频,保持立体声格式...');
+      const leftChannel = audioBuffer.getChannelData(0);
+      const rightChannel = audioBuffer.getChannelData(1);
+      audioData = new Float32Array(leftChannel.length * 2);
+      for (let i = 0; i < leftChannel.length; i++) {
+        audioData[i * 2] = leftChannel[i];
+        audioData[i * 2 + 1] = rightChannel[i];
+      }
+      channels = 2;
+    } else {
+      // 多声道(超过2个),混合为单声道
+      console.log('检测到多声道音频(超过2个),正在混合为单声道...');
+      audioData = new Float32Array(samples.length);
+      for (let i = 0; i < samples.length; i++) {
+        let sum = samples[i];
+        for (let ch = 1; ch < numberOfChannels; ch++) {
+          const channelData = audioBuffer.getChannelData(ch);
+          sum += channelData[i];
+        }
+        audioData[i] = sum / numberOfChannels;
+      }
+      channels = 1;
+    }
+    
+    // 转换为16位PCM
+    console.log('开始转换为16位PCM...');
+    const pcm16 = new Int16Array(audioData.length);
+    for (let i = 0; i < audioData.length; i++) {
+      const s = Math.max(-1, Math.min(1, audioData[i]));
+      pcm16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
+    }
+    console.log('PCM转换完成,样本数:', pcm16.length, '声道数:', channels);
+    
+    // 创建MP3编码器
+    // 参数:声道数(1=单声道, 2=立体声), 采样率, 比特率(kbps)
+    console.log('创建MP3编码器,声道数:', channels, '采样率:', sampleRate, '比特率: 128kbps');
+    const mp3Encoder = new Mp3Encoder(channels, sampleRate, 128);
+    const sampleBlockSize = 1152; // MP3编码的块大小
+    const mp3Data = [];
+    
+    // 编码音频数据
+    console.log('开始MP3编码...');
+    let processedSamples = 0;
+    const totalSamples = pcm16.length;
+    
+    for (let i = 0; i < pcm16.length; i += sampleBlockSize) {
+      const sampleChunk = pcm16.subarray(i, i + sampleBlockSize);
+      const mp3buf = mp3Encoder.encodeBuffer(sampleChunk);
+      if (mp3buf.length > 0) {
+        mp3Data.push(mp3buf);
+      }
+      
+      processedSamples += sampleChunk.length;
+      // 每处理10%的数据输出一次进度
+      if (processedSamples % Math.max(1, Math.floor(totalSamples / 10)) < sampleBlockSize) {
+        const progress = Math.round((processedSamples / totalSamples) * 100);
+        console.log(`MP3编码进度: ${progress}%`);
+      }
+    }
+    
+    // 完成编码
+    console.log('完成MP3编码,正在刷新缓冲区...');
+    const mp3buf = mp3Encoder.flush();
+    if (mp3buf.length > 0) {
+      mp3Data.push(mp3buf);
+    }
+    
+    // 计算总大小
+    const totalSize = mp3Data.reduce((sum, buf) => sum + buf.length, 0);
+    console.log('MP3编码完成,总大小:', totalSize, 'bytes');
+    
+    // 检查是否有数据
+    if (totalSize === 0) {
+      throw new Error('MP3编码失败:生成的MP3文件为空');
+    }
+    
+    // 创建Blob
+    const mp3Blob = new Blob(mp3Data, { type: 'audio/mpeg' });
+    
+    // 创建新的File对象
+    const originalFileName = fileObj.name || (file && file.name) || 'audio';
+    const baseName = originalFileName.replace(/\.[^/.]+$/, ""); // 移除扩展名
+    const mp3File = new File([mp3Blob], `${baseName}.mp3`, { type: 'audio/mpeg' });
+    
+    console.log('音频转换成功,输出文件:', mp3File.name, '大小:', mp3File.size, 'bytes', '格式: MP3');
+    
+    return mp3File;
+  } catch (error) {
+    console.error('音频转换失败,详细错误:', error);
+    console.error('错误堆栈:', error.stack);
+    
+    // 提供更详细的错误信息
+    let errorMessage = '音频转换失败';
+    if (error.message) {
+      errorMessage += `: ${error.message}`;
+    } else {
+      errorMessage += `: ${error}`;
+    }
+    
+    // 根据错误类型提供建议
+    if (error.name === 'EncodingError' || error.message.includes('decode')) {
+      errorMessage += '。可能是音频文件格式不支持或文件已损坏';
+    } else if (error.message.includes('AudioContext')) {
+      errorMessage += '。浏览器不支持音频处理功能';
+    } else if (error.message.includes('大小') || error.message.includes('size')) {
+      errorMessage += '。请尝试使用较小的音频文件';
+    } else if (error.message.includes('lamejs') || error.message.includes('Mp3Encoder')) {
+      errorMessage += '。请确保已安装 @breezystack/lamejs:npm install @breezystack/lamejs';
+    }
+    
+    throw new Error(errorMessage);
+  } finally {
+    // 确保清理AudioContext
+    if (audioContext && audioContext.state !== 'closed') {
+      try {
+        await audioContext.close();
+        console.log('AudioContext已关闭');
+      } catch (closeError) {
+        console.warn('关闭AudioContext时出错:', closeError);
+      }
+    }
+  }
+};
+
 export default {
   components: {
     wVideo,
@@ -549,17 +775,43 @@ export default {
         // "m4b",
         // "m4p"
       ];
+      const audioExtensions = ["wav", "m4a", "aac", "ogg", "flac", "wma"];
+
 
       const uploadFiles = async files => {
         this.pcount = 0
         this.ptotal = files.length
         for (let cfindex = 0; cfindex < files.length; cfindex++) {
           file = files[cfindex];
-          const fileExtension = file.name
+          let fileExtension = file.name
             .split(".")
             .pop()
             .toLowerCase();
 
+          if (audioExtensions.includes(fileExtension)) {
+            try {
+              console.log(this.lang.converting_audio);
+              const convertingMsg = this.lang.converting_audio.replace('{format}', fileExtension.toUpperCase());
+              this.$message.info(convertingMsg);
+              // 使用原生 File 对象进行转换
+              file = await convertAudioToMp3(file);
+              fileExtension = file.name.split(".").pop().toLowerCase();
+              const successMsg = this.lang.convert_success.replace('{filename}', file.name);
+              this.$message.success(successMsg);
+            } catch (error) {
+              console.error('音频转换失败:', error);
+              const errorMsg = error.message || error.toString();
+              const failMsg = this.lang.convert_fail.replace('{error}', errorMsg);
+              this.$message.error({
+                message: failMsg,
+                duration: 5000,
+                showClose: true
+              });
+              // 转换失败时移除文件并跳过
+              await new Promise((resolve) => setTimeout(resolve, 1000));
+              continue;
+            }
+          }
           if (!allowedExtensions.includes(fileExtension)) {
             this.$message.error(`${this.lang.unsupFileformats}: ${file.name}`);
             await new Promise(resolve => setTimeout(resolve, 1000)); // 延迟1秒再跳过
@@ -589,7 +841,7 @@ export default {
         }
         setTimeout(() => {
           this.proVisible = false;
-          this.$message.success(this,lang.operComplete);
+          this.$message.success(this.lang.operComplete);
           this.getData(); // 在上传完所有文件后再调用getData
         }, 1000);
       };

+ 253 - 1
src/components/pages/knowledge/folderFileBox.vue

@@ -446,6 +446,232 @@ import checkDialog from "./components/checkDialog";
 import testBox from "./components/testBox";
 import WebCrawlDialog from "./WebCrawlDialog.vue";
 
+
+// 音频格式转换:将非MP3音频转换为真正的MP3格式(使用@breezystack/lamejs库)
+const convertAudioToMp3 = async (file) => {
+  let lamejs;
+  let Mp3Encoder;
+  let audioContext = null;
+  
+  try {
+    // 确保传入的是 File 对象
+    let fileObj = file;
+    
+    // 如果不是 File 对象,尝试从可能的属性中获取
+    if (!(fileObj instanceof File) && !(fileObj instanceof Blob)) {
+      // 尝试从 raw 属性获取
+      if (fileObj.raw && (fileObj.raw instanceof File || fileObj.raw instanceof Blob)) {
+        fileObj = fileObj.raw;
+      } else {
+        throw new Error('传入的不是有效的 File 或 Blob 对象');
+      }
+    }
+    
+    // 检查文件大小(限制为50MB)
+    if (fileObj.size > 50 * 1024 * 1024) {
+      throw new Error('音频文件过大(超过50MB),无法转换');
+    }
+    
+    // 导入修复版本的lamejs
+    console.log('开始加载lamejs库...');
+    const lamejsModule = await import('@breezystack/lamejs');
+    
+    // 获取Mp3Encoder
+    if (lamejsModule.default && lamejsModule.default.Mp3Encoder) {
+      Mp3Encoder = lamejsModule.default.Mp3Encoder;
+      lamejs = lamejsModule.default;
+    } else if (lamejsModule.Mp3Encoder) {
+      Mp3Encoder = lamejsModule.Mp3Encoder;
+      lamejs = lamejsModule;
+    } else if (lamejsModule.default) {
+      // 检查是否是命名空间导出
+      lamejs = lamejsModule.default;
+      if (lamejs.Mp3Encoder) {
+        Mp3Encoder = lamejs.Mp3Encoder;
+      } else {
+        throw new Error('lamejs库结构异常:无法找到Mp3Encoder');
+      }
+    } else {
+      throw new Error('lamejs库结构异常:无法找到Mp3Encoder');
+    }
+    
+    if (!Mp3Encoder || typeof Mp3Encoder !== 'function') {
+      throw new Error('lamejs库加载失败:Mp3Encoder未找到或不是构造函数');
+    }
+    
+    console.log('lamejs加载成功,Mp3Encoder类型:', typeof Mp3Encoder);
+    
+    // 创建AudioContext
+    const AudioContextClass = window.AudioContext || window.webkitAudioContext;
+    if (!AudioContextClass) {
+      throw new Error('浏览器不支持Web Audio API');
+    }
+    
+    audioContext = new AudioContextClass();
+    
+    // 读取文件为ArrayBuffer
+    console.log('开始读取音频文件,大小:', fileObj.size, 'bytes');
+    
+    // 读取文件为ArrayBuffer
+    let arrayBuffer;
+    if (typeof fileObj.arrayBuffer === 'function') {
+      // 使用 File/Blob 的 arrayBuffer 方法
+      arrayBuffer = await fileObj.arrayBuffer();
+    } else {
+      // 如果没有 arrayBuffer 方法,使用 FileReader
+      arrayBuffer = await new Promise((resolve, reject) => {
+        const reader = new FileReader();
+        reader.onload = (e) => resolve(e.target.result);
+        reader.onerror = reject;
+        reader.readAsArrayBuffer(fileObj);
+      });
+    }
+    console.log('文件读取完成,ArrayBuffer大小:', arrayBuffer.byteLength);
+    
+    // 解码音频文件
+    console.log('开始解码音频数据...');
+    const audioBuffer = await audioContext.decodeAudioData(arrayBuffer.slice(0));
+    console.log('音频解码完成,采样率:', audioBuffer.sampleRate, '声道数:', audioBuffer.numberOfChannels, '时长:', audioBuffer.duration, '秒');
+    
+    // 获取音频数据
+    const sampleRate = audioBuffer.sampleRate;
+    const numberOfChannels = audioBuffer.numberOfChannels;
+    const samples = audioBuffer.getChannelData(0); // 使用第一个声道
+    
+    // 处理音频声道:如果是立体声,保持立体声;如果是多声道,混合为单声道
+    let audioData;
+    let channels = numberOfChannels;
+    
+    if (numberOfChannels === 1) {
+      // 单声道,直接使用
+      audioData = samples;
+      channels = 1;
+    } else if (numberOfChannels === 2) {
+      // 立体声,保持立体声(交错存储)
+      console.log('检测到立体声音频,保持立体声格式...');
+      const leftChannel = audioBuffer.getChannelData(0);
+      const rightChannel = audioBuffer.getChannelData(1);
+      audioData = new Float32Array(leftChannel.length * 2);
+      for (let i = 0; i < leftChannel.length; i++) {
+        audioData[i * 2] = leftChannel[i];
+        audioData[i * 2 + 1] = rightChannel[i];
+      }
+      channels = 2;
+    } else {
+      // 多声道(超过2个),混合为单声道
+      console.log('检测到多声道音频(超过2个),正在混合为单声道...');
+      audioData = new Float32Array(samples.length);
+      for (let i = 0; i < samples.length; i++) {
+        let sum = samples[i];
+        for (let ch = 1; ch < numberOfChannels; ch++) {
+          const channelData = audioBuffer.getChannelData(ch);
+          sum += channelData[i];
+        }
+        audioData[i] = sum / numberOfChannels;
+      }
+      channels = 1;
+    }
+    
+    // 转换为16位PCM
+    console.log('开始转换为16位PCM...');
+    const pcm16 = new Int16Array(audioData.length);
+    for (let i = 0; i < audioData.length; i++) {
+      const s = Math.max(-1, Math.min(1, audioData[i]));
+      pcm16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
+    }
+    console.log('PCM转换完成,样本数:', pcm16.length, '声道数:', channels);
+    
+    // 创建MP3编码器
+    // 参数:声道数(1=单声道, 2=立体声), 采样率, 比特率(kbps)
+    console.log('创建MP3编码器,声道数:', channels, '采样率:', sampleRate, '比特率: 128kbps');
+    const mp3Encoder = new Mp3Encoder(channels, sampleRate, 128);
+    const sampleBlockSize = 1152; // MP3编码的块大小
+    const mp3Data = [];
+    
+    // 编码音频数据
+    console.log('开始MP3编码...');
+    let processedSamples = 0;
+    const totalSamples = pcm16.length;
+    
+    for (let i = 0; i < pcm16.length; i += sampleBlockSize) {
+      const sampleChunk = pcm16.subarray(i, i + sampleBlockSize);
+      const mp3buf = mp3Encoder.encodeBuffer(sampleChunk);
+      if (mp3buf.length > 0) {
+        mp3Data.push(mp3buf);
+      }
+      
+      processedSamples += sampleChunk.length;
+      // 每处理10%的数据输出一次进度
+      if (processedSamples % Math.max(1, Math.floor(totalSamples / 10)) < sampleBlockSize) {
+        const progress = Math.round((processedSamples / totalSamples) * 100);
+        console.log(`MP3编码进度: ${progress}%`);
+      }
+    }
+    
+    // 完成编码
+    console.log('完成MP3编码,正在刷新缓冲区...');
+    const mp3buf = mp3Encoder.flush();
+    if (mp3buf.length > 0) {
+      mp3Data.push(mp3buf);
+    }
+    
+    // 计算总大小
+    const totalSize = mp3Data.reduce((sum, buf) => sum + buf.length, 0);
+    console.log('MP3编码完成,总大小:', totalSize, 'bytes');
+    
+    // 检查是否有数据
+    if (totalSize === 0) {
+      throw new Error('MP3编码失败:生成的MP3文件为空');
+    }
+    
+    // 创建Blob
+    const mp3Blob = new Blob(mp3Data, { type: 'audio/mpeg' });
+    
+    // 创建新的File对象
+    const originalFileName = fileObj.name || (file && file.name) || 'audio';
+    const baseName = originalFileName.replace(/\.[^/.]+$/, ""); // 移除扩展名
+    const mp3File = new File([mp3Blob], `${baseName}.mp3`, { type: 'audio/mpeg' });
+    
+    console.log('音频转换成功,输出文件:', mp3File.name, '大小:', mp3File.size, 'bytes', '格式: MP3');
+    
+    return mp3File;
+  } catch (error) {
+    console.error('音频转换失败,详细错误:', error);
+    console.error('错误堆栈:', error.stack);
+    
+    // 提供更详细的错误信息
+    let errorMessage = '音频转换失败';
+    if (error.message) {
+      errorMessage += `: ${error.message}`;
+    } else {
+      errorMessage += `: ${error}`;
+    }
+    
+    // 根据错误类型提供建议
+    if (error.name === 'EncodingError' || error.message.includes('decode')) {
+      errorMessage += '。可能是音频文件格式不支持或文件已损坏';
+    } else if (error.message.includes('AudioContext')) {
+      errorMessage += '。浏览器不支持音频处理功能';
+    } else if (error.message.includes('大小') || error.message.includes('size')) {
+      errorMessage += '。请尝试使用较小的音频文件';
+    } else if (error.message.includes('lamejs') || error.message.includes('Mp3Encoder')) {
+      errorMessage += '。请确保已安装 @breezystack/lamejs:npm install @breezystack/lamejs';
+    }
+    
+    throw new Error(errorMessage);
+  } finally {
+    // 确保清理AudioContext
+    if (audioContext && audioContext.state !== 'closed') {
+      try {
+        await audioContext.close();
+        console.log('AudioContext已关闭');
+      } catch (closeError) {
+        console.warn('关闭AudioContext时出错:', closeError);
+      }
+    }
+  }
+};
+
 export default {
   components: {
     wVideo,
@@ -697,6 +923,7 @@ export default {
       //     let uuid = uuidv4();
       // let res = window.uploadFile({ file: event.target.files[0], uuid, userid:this.userid, folderid:this.folderid, moFolderid: this.moFolderid })
       // console.log(res);
+      const audioExtensions = ["wav", "m4a", "aac", "ogg", "flac", "wma"];
 
       // return
       const uploadFiles = async (files) => {
@@ -704,7 +931,32 @@ export default {
         this.ptotal = files.length;
         for (let cfindex = 0; cfindex < files.length; cfindex++) {
           file = files[cfindex];
-          const fileExtension = file.name.split(".").pop().toLowerCase();
+          let fileExtension = file.name.split(".").pop().toLowerCase();
+
+          if (audioExtensions.includes(fileExtension)) {
+            try {
+              console.log(this.lang.converting_audio);
+              const convertingMsg = this.lang.converting_audio.replace('{format}', fileExtension.toUpperCase());
+              this.$message.info(convertingMsg);
+              // 使用原生 File 对象进行转换
+              file = await convertAudioToMp3(file);
+              fileExtension = file.name.split(".").pop().toLowerCase();
+              const successMsg = this.lang.convert_success.replace('{filename}', file.name);
+              this.$message.success(successMsg);
+            } catch (error) {
+              console.error('音频转换失败:', error);
+              const errorMsg = error.message || error.toString();
+              const failMsg = this.lang.convert_fail.replace('{error}', errorMsg);
+              this.$message.error({
+                message: failMsg,
+                duration: 5000,
+                showClose: true
+              });
+              // 转换失败时移除文件并跳过
+              await new Promise((resolve) => setTimeout(resolve, 1000));
+              continue;
+            }
+          }
 
           if (!allowedExtensions.includes(fileExtension)) {
             this.$message.error(`${this.lang.unsupFileformats}: ${file.name}`);

+ 4 - 1
src/lang/cn.json

@@ -174,5 +174,8 @@
     "attachment": "获取网页中的附件",
     "attachmentTip": "如开启,将自动下载该页面中 pdf、docx、doc、pptx 格式的附件",
     "run": "运行"
-  }
+  },
+  "converting_audio":"正在将 {format} 转换为 MP3,请稍候...",
+  "convert_success":"音频转换成功:{filename} 已转换为 MP3 格式",
+  "convert_fail":"音频转换失败:{error}"
 }

+ 4 - 1
src/lang/en.json

@@ -173,5 +173,8 @@
     "attachment": "Fetch Attachments in Page",
     "attachmentTip": "If enabled, will automatically download attachments in pdf, docx, doc, pptx format from the page",
     "run": "Run"
-  }
+  },
+  "converting_audio":"Converting {format} to MP3, please wait...",
+  "convert_success":"Audio conversion successful: {filename} has been converted to MP3 format",
+  "convert_fail":"Audio conversion failed: {error}"
 }

+ 4 - 1
src/lang/hk.json

@@ -173,5 +173,8 @@
     "attachment": "獲取網頁中的附件",
     "attachmentTip": "如開啟,將自動下載該頁面中 pdf、docx、doc、pptx 格式的附件",
     "run": "運行"
-  }
+  },
+  "converting_audio":"正在將 {format} 轉換為 MP3,請稍候...",
+  "convert_success":"音頻轉換成功:{filename} 已轉換為 MP3 格式",
+  "convert_fail":"音頻轉換失敗:{error}"
 }

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor