Explorar el Código

木易华康 视频资源上传修改

Long hace 1 día
padre
commit
89748e1518
Se han modificado 1 ficheros con 111 adiciones y 43 borrados
  1. 111 43
      src/views/course/videoResource/index.vue

+ 111 - 43
src/views/course/videoResource/index.vue

@@ -323,7 +323,7 @@
                         :show-text="false"
                         :width="30"
                         :status="currentUploadProgress.line1Status === 'success' ? 'success' :
-                                currentUploadProgress.line1Status === 'failed' ? 'exception' : ''"
+                                currentUploadProgress.line1Status === 'failed' ? 'exception' : undefined"
                       ></el-progress>
                       <span class="line-status-text">{{ Math.round(currentUploadProgress.line1) }}%</span>
                     </div>
@@ -334,7 +334,7 @@
                         :show-text="false"
                         :width="30"
                         :status="currentUploadProgress.line2Status === 'success' ? 'success' :
-                                currentUploadProgress.line2Status === 'failed' ? 'exception' : ''"
+                                currentUploadProgress.line2Status === 'failed' ? 'exception' : undefined"
                       ></el-progress>
                       <span class="line-status-text">{{ Math.round(currentUploadProgress.line2) }}%</span>
                     </div>
@@ -1374,13 +1374,35 @@ export default {
     async clipVideoFirstTwoSeconds(file) {
       return new Promise((resolve, reject) => {
         const video = document.createElement('video');
-        video.src = URL.createObjectURL(file);
+        const objectUrl = URL.createObjectURL(file);
+        video.src = objectUrl;
         video.muted = true;
         video.playsInline = true;
 
-        video.onloadedmetadata = () => {
-          video.currentTime = 0; // 定位到第一帧
-          video.onseeked = () => {
+        let timeoutId = null;
+        let seeked = false;
+
+        // 清理函数
+        const cleanup = () => {
+          if (timeoutId) {
+            clearTimeout(timeoutId);
+            timeoutId = null;
+          }
+          URL.revokeObjectURL(objectUrl);
+        };
+
+        // 超时处理
+        timeoutId = setTimeout(() => {
+          cleanup();
+          reject(new Error('视频截取超时'));
+        }, 10000); // 10秒超时
+
+        // 先设置 onseeked 监听器,防止在设置 currentTime 之前触发 seeked 事件
+        video.onseeked = () => {
+          if (seeked) return; // 防止重复触发
+          seeked = true;
+
+          try {
             const canvas = document.createElement('canvas');
             canvas.width = video.videoWidth;
             canvas.height = video.videoHeight;
@@ -1389,17 +1411,24 @@ export default {
 
             canvas.toBlob(
               (blob) => {
-                URL.revokeObjectURL(video.src);
+                cleanup();
                 resolve(blob); // 返回 JPEG Blob
               },
               'image/jpeg',
               0.8 // 质量
             );
-          };
+          } catch (error) {
+            cleanup();
+            reject(new Error('canvas操作失败: ' + error.message));
+          }
+        };
+
+        video.onloadedmetadata = () => {
+          video.currentTime = 0; // 定位到第一帧
         };
 
         video.onerror = () => {
-          URL.revokeObjectURL(video.src);
+          cleanup();
           reject(new Error('视频加载失败'));
         };
       });
@@ -1564,7 +1593,15 @@ export default {
       this.currentUploadProgress = { total: 0, line1: 0, line2: 0, line1Status: 'pending', line2Status: 'pending' };
 
       try {
-        await this.getFirstThumbnail(file, this.form);
+        // 获取封面,10秒超时,失败后继续上传
+        try {
+          await Promise.race([
+            this.getFirstThumbnail(file, this.form),
+            new Promise((_, reject) => setTimeout(() => reject(new Error('获取封面超时')), 10000))
+          ]);
+        } catch (error) {
+          console.error('获取封面失败:', error);
+        }
         const [line1Result, line2Result] = await Promise.allSettled([
           this.uploadVideoToTxPcdn(file, this.form, options.onProgress),
           //this.uploadVideoToHwObs(file, this.form, options.onProgress)
@@ -1612,6 +1649,10 @@ export default {
       video.preload = 'metadata';
       video.onloadedmetadata = () => {
         this.form.duration = Math.round(video.duration);
+        URL.revokeObjectURL(video.src);
+      };
+      video.onerror = () => {
+        URL.revokeObjectURL(video.src);
       };
       video.src = URL.createObjectURL(file);
     },
@@ -1727,8 +1768,14 @@ export default {
           this.$message.success('批量添加成功');
           this.batchAddVisible = false;
           this.getList();
+        } else {
+          this.$message.error(response.msg || '批量添加失败');
         }
-      })
+      }).catch(error => {
+        this.$message.error('批量添加失败: ' + (error.message || '网络错误'));
+      }).finally(() => {
+        this.add = false;
+      });
     },
     /** 获取分类名称 */
     getTypeName(typeId) {
@@ -1931,7 +1978,7 @@ export default {
       };
 
       this.uploadQueue.push(tempVideo);
-      this.videoList.unshift(tempVideo);
+      this.videoList.push(tempVideo);
 
        // 获取视频时长
       const video = document.createElement('video');
@@ -1941,6 +1988,10 @@ export default {
         if (index !== -1) {
           tempVideo.duration = Math.round(video.duration);
         }
+        URL.revokeObjectURL(video.src);
+      };
+      video.onerror = () => {
+        URL.revokeObjectURL(video.src);
       };
       video.src = URL.createObjectURL(file);
 
@@ -1973,18 +2024,22 @@ export default {
           const index = this.videoList.findIndex(item => item.tempId === video.tempId);
           if (index !== -1) {
             this.videoList[index].uploadStatus = 'uploading';
+            // 重置上传进度,防止下一个视频显示旧进度
+            this.videoList[index].progress = 0;
+            this.videoList[index].uploadDetails = {
+              line1: 0,
+              line2: 0,
+              line1Status: 'pending',
+              line2Status: 'pending'
+            };
           }
         });
 
         // Process current batch in parallel
         const batchPromises = currentBatch.map(video => this.uploadSingleVideo(video));
 
-        try {
-          await Promise.allSettled(batchPromises);
-          this.$message.success(`批次上传完成,已处理 ${currentBatch.length} 个视频`);
-        } catch (error) {
-          this.$message.error(`批次上传过程中发生错误: ${error.message}`);
-        }
+        await Promise.allSettled(batchPromises);
+        this.$message.success(`批次上传完成,已处理 ${currentBatch.length} 个视频`);
 
         // Update queue positions for remaining videos
         this.updateQueuePositions();
@@ -2003,17 +2058,41 @@ export default {
 
     async uploadSingleVideo(tempVideo) {
       try {
-        // 获取封面
-        await this.getFirstThumbnail(tempVideo.file, tempVideo);
-
-        // 并行上传到两个服务器
-        const [line1Result, line2Result] = await Promise.allSettled([
-          this.uploadVideoToTxPcdnBatch(tempVideo.file, tempVideo),
-          // this.uploadVideoToHwObsBatch(tempVideo.file, tempVideo)
-          this.uploadVideoToHSYBatch(tempVideo.file, tempVideo),
+        // 获取封面,10秒超时,重试3次
+        let thumbnailSuccess = false;
+        for (let i = 0; i < 3; i++) {
+          try {
+            await Promise.race([
+              this.getFirstThumbnail(tempVideo.file, tempVideo),
+              new Promise((_, reject) => setTimeout(() => reject(new Error('获取封面超时')), 10000))
+            ]);
+            thumbnailSuccess = true;
+            break;
+          } catch (error) {
+            console.error(`获取封面失败,第${i + 1}次重试:`, error);
+            if (i < 2) {
+              await new Promise(resolve => setTimeout(resolve, 1000));
+            }
+          }
+        }
+        if (!thumbnailSuccess) {
+          console.error('获取封面失败,已重试3次,跳过封面获取继续上传');
+        }
 
+        // 并行上传到两个服务器,添加超时保护
+        const uploadPromise = Promise.allSettled([
+          Promise.race([
+            this.uploadVideoToTxPcdnBatch(tempVideo.file, tempVideo),
+            new Promise((_, reject) => setTimeout(() => reject(new Error('腾讯云上传超时')), 300000))
+          ]),
+          Promise.race([
+            this.uploadVideoToHSYBatch(tempVideo.file, tempVideo),
+            new Promise((_, reject) => setTimeout(() => reject(new Error('HSY上传超时')), 300000))
+          ])
         ]);
 
+        const [line1Result, line2Result] = await uploadPromise;
+
         // 检查上传结果
         const line1Success = line1Result.status === 'fulfilled' && line1Result.value.success;
         const line2Success = line2Result.status === 'fulfilled' && line2Result.value.success;
@@ -2037,11 +2116,13 @@ export default {
 
         return { success: line1Success && line2Success, tempVideo };
       } catch (error) {
+        console.error('视频上传失败:', error);
         const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
         if (index !== -1) {
           this.videoList[index].uploadStatus = 'failed';
         }
-        throw error;
+        // 不再抛出错误,避免影响队列中的其他视频
+        return { success: false, tempVideo, error: error.message };
       }
     },
 
@@ -2360,7 +2441,7 @@ export default {
     async uploadVideoToHSYBatch(file, tempVideo) {
       try {
         const data = await uploadToHSY(file, (progress) => {
-          const progressPercent = Math.floor(progress);
+          const progressPercent = Math.floor(progress.percent);
           const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
           if (index !== -1) {
             this.videoList[index].uploadDetails.line2 = progressPercent;
@@ -2506,7 +2587,7 @@ export default {
 
         if (needRetryLine2) {
           uploadPromises.push(
-            this.uploadVideoToHwObsBatch(tempVideo.file, tempVideo)
+            this.uploadVideoToHSYBatch(tempVideo.file, tempVideo)
               .then(result => ({ line: 'line2', result }))
               .catch(error => ({ line: 'line2', result: { success: false, error: error.message } }))
           );
@@ -2567,19 +2648,6 @@ export default {
         this.$message.error(`文件 ${tempVideo.fileName} 重试过程中发生错误:${error.message || '未知错误'}`);
       }
     },
-    // 重试单个上传
-    async retryUpload(row) {
-      this.$confirm('确认要重新上传该视频吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        // 这里需要重新触发上传,但由于原始文件可能已经不存在
-        // 建议用户重新选择文件上传
-        this.$message.info('请重新选择文件进行上传');
-        this.handleUpdate(row);
-      }).catch(() => {});
-    },
     // 获取上传状态图标
     getUploadStatusIcon(status) {
       switch (status) {
@@ -2638,7 +2706,7 @@ export default {
       } else if (row.uploadStatus === 'failed') {
         return 'exception';
       }
-      return '';
+      return undefined;
     },
   }
 }