Forráskód Böngészése

批量上传视频修复

yuhongqi 15 órája
szülő
commit
d8f2969bd2
1 módosított fájl, 167 hozzáadás és 9 törlés
  1. 167 9
      src/views/course/videoResource/index.vue

+ 167 - 9
src/views/course/videoResource/index.vue

@@ -1575,6 +1575,16 @@ export default {
         return;
       }
 
+      // 验证所有视频的元信息完整性
+      const invalidVideos = this.videoList.filter(item => {
+        return !this.validateVideoMetadata(item);
+      });
+      if (invalidVideos.length > 0) {
+        const invalidNames = invalidVideos.map(v => v.fileName).join('、');
+        this.$message.error(`以下视频元信息不完整,无法提交:${invalidNames}`);
+        return;
+      }
+
       if (this.add){
         this.$message.warning("请勿重复提交")
         return
@@ -1585,6 +1595,10 @@ export default {
       const videoList = JSON.parse(JSON.stringify(this.videoList));
       videoList.forEach(item => {
         item.projectIds = item.projectIds.join(",");
+        // 确保duration是有效的数字
+        if (!item.duration || item.duration <= 0 || isNaN(item.duration)) {
+          console.error('视频时长无效,跳过:', item.fileName, item.duration);
+        }
       });
       batchAddVideoResource(videoList).then(response => {
         if (response.code === 200) {
@@ -1592,6 +1606,10 @@ export default {
           this.batchAddVisible = false;
           this.getList();
         }
+      }).catch(error => {
+        this.$message.error('批量添加失败: ' + (error.message || '未知错误'));
+      }).finally(() => {
+        this.add = false;
       })
     },
     /** 获取分类名称 */
@@ -1771,22 +1789,45 @@ export default {
           line2Status: 'pending'
         },
         file: file,
-        queuePosition: this.uploadQueue.length + 1 // Track queue position
+        queuePosition: this.uploadQueue.length + 1, // Track queue position
+        metadataLoaded: false // 标记元数据是否已加载
       };
 
       this.uploadQueue.push(tempVideo);
       this.videoList.unshift(tempVideo);
 
-      // 获取视频时长
-      const video = document.createElement('video');
-      video.preload = 'metadata';
-      video.onloadedmetadata = () => {
+      // 获取视频时长和元信息(确保完整)
+      try {
+        await this.getVideoMetadata(file, tempVideo);
+      } catch (error) {
+        console.error('获取视频元信息失败:', error);
         const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
         if (index !== -1) {
-          tempVideo.duration = Math.round(video.duration);
+          this.videoList[index].uploadStatus = 'failed';
+          this.$message.error(`视频 ${tempVideo.fileName} 元信息获取失败,请检查视频文件是否完整`);
         }
-      };
-      video.src = URL.createObjectURL(file);
+        // 从队列中移除
+        const queueIndex = this.uploadQueue.findIndex(item => item.tempId === tempVideo.tempId);
+        if (queueIndex !== -1) {
+          this.uploadQueue.splice(queueIndex, 1);
+        }
+        return;
+      }
+
+      // 验证元信息完整性
+      if (!this.validateVideoMetadata(tempVideo)) {
+        const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
+        if (index !== -1) {
+          this.videoList[index].uploadStatus = 'failed';
+          this.$message.error(`视频 ${tempVideo.fileName} 元信息不完整(时长: ${tempVideo.duration}秒),请检查视频文件`);
+        }
+        // 从队列中移除
+        const queueIndex = this.uploadQueue.findIndex(item => item.tempId === tempVideo.tempId);
+        if (queueIndex !== -1) {
+          this.uploadQueue.splice(queueIndex, 1);
+        }
+        return;
+      }
 
       // 关闭上传弹窗
       this.showUpload = false;
@@ -1800,6 +1841,96 @@ export default {
       }
     },
 
+    /** 获取视频元信息(Promise方式,确保完整) */
+    getVideoMetadata(file, tempVideo) {
+      return new Promise((resolve, reject) => {
+        const video = document.createElement('video');
+        video.preload = 'metadata';
+        video.muted = true; // 静音以加快加载
+
+        let resolved = false;
+        const timeout = setTimeout(() => {
+          if (!resolved) {
+            resolved = true;
+            URL.revokeObjectURL(video.src);
+            reject(new Error('获取视频元信息超时'));
+          }
+        }, 30000); // 30秒超时
+
+        video.onloadedmetadata = () => {
+          if (resolved) return;
+          resolved = true;
+          clearTimeout(timeout);
+
+          try {
+            const duration = video.duration;
+            if (isNaN(duration) || duration <= 0 || !isFinite(duration)) {
+              URL.revokeObjectURL(video.src);
+              reject(new Error('无法获取有效的视频时长'));
+              return;
+            }
+
+            const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
+            if (index !== -1) {
+              this.videoList[index].duration = Math.round(duration);
+              this.videoList[index].metadataLoaded = true;
+            }
+            tempVideo.duration = Math.round(duration);
+            tempVideo.metadataLoaded = true;
+
+            URL.revokeObjectURL(video.src);
+            resolve({
+              duration: Math.round(duration),
+              videoWidth: video.videoWidth,
+              videoHeight: video.videoHeight
+            });
+          } catch (error) {
+            URL.revokeObjectURL(video.src);
+            reject(error);
+          }
+        };
+
+        video.onerror = (error) => {
+          if (resolved) return;
+          resolved = true;
+          clearTimeout(timeout);
+          URL.revokeObjectURL(video.src);
+          reject(new Error('视频文件加载失败,请检查文件格式是否正确'));
+        };
+
+        video.src = URL.createObjectURL(file);
+      });
+    },
+
+    /** 验证视频元信息完整性 */
+    validateVideoMetadata(tempVideo) {
+      // 检查时长是否有效
+      if (!tempVideo.duration || tempVideo.duration <= 0 || isNaN(tempVideo.duration)) {
+        console.error('视频时长无效:', tempVideo.duration);
+        return false;
+      }
+
+      // 检查文件大小是否有效
+      if (!tempVideo.fileSize || tempVideo.fileSize <= 0) {
+        console.error('文件大小无效:', tempVideo.fileSize);
+        return false;
+      }
+
+      // 检查文件名是否存在
+      if (!tempVideo.fileName || tempVideo.fileName.trim() === '') {
+        console.error('文件名无效:', tempVideo.fileName);
+        return false;
+      }
+
+      // 检查元数据是否已加载
+      if (!tempVideo.metadataLoaded) {
+        console.error('元数据未加载完成');
+        return false;
+      }
+
+      return true;
+    },
+
     async processUploadQueue() {
       if (this.isProcessingBatch || this.uploadQueue.length === 0) {
         return;
@@ -1847,6 +1978,33 @@ export default {
 
     async uploadSingleVideo(tempVideo) {
       try {
+        // 再次验证元信息完整性(确保在上传前元信息完整)
+        if (!this.validateVideoMetadata(tempVideo)) {
+          const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
+          if (index !== -1) {
+            this.videoList[index].uploadStatus = 'failed';
+            this.$message.error(`视频 ${tempVideo.fileName} 元信息不完整,无法上传`);
+          }
+          throw new Error('视频元信息不完整');
+        }
+
+        // 如果元数据未加载,尝试重新加载
+        if (!tempVideo.metadataLoaded || !tempVideo.duration || tempVideo.duration <= 0) {
+          console.warn('视频元信息不完整,尝试重新加载:', tempVideo.fileName);
+          try {
+            await this.getVideoMetadata(tempVideo.file, tempVideo);
+            if (!this.validateVideoMetadata(tempVideo)) {
+              throw new Error('重新加载后元信息仍不完整');
+            }
+          } catch (error) {
+            const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
+            if (index !== -1) {
+              this.videoList[index].uploadStatus = 'failed';
+              this.$message.error(`视频 ${tempVideo.fileName} 元信息加载失败: ${error.message}`);
+            }
+            throw error;
+          }
+        }
         // 获取封面
         await this.getFirstThumbnail(tempVideo.file, tempVideo);
 
@@ -1860,7 +2018,7 @@ export default {
 
         // 检查上传结果
         const line1Success = line1Result.status === 'fulfilled' && line1Result.value.success;
-        const line2Success = line1Result.status === 'fulfilled' && line1Result.value.success;
+        const line2Success = line2Result.status === 'fulfilled' && line2Result.value.success;
 
         const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
         if (index !== -1) {