Procházet zdrojové kódy

火山云服务端上传集成

15376779826 před 4 dny
rodič
revize
7f46604ee8

+ 4 - 0
.env.development

@@ -26,6 +26,10 @@ VUE_APP_COS_REGION = ap-chongqing
 VUE_APP_VIDEO_LINE_1 = https://xfktcpv.ylrzcloud.com
 # 线路二地址
 VUE_APP_VIDEO_LINE_2 = https://xfkobs.ylrztop.com
+#火山云视频地址域名
+VUE_APP_VIDEO_URL = https://myhkvolcengine.ylrztop.com
+#火山云视频点播空间名
+VUE_APP_HSY_SPACE = myhk-2114522511
 
 
 VUE_APP_LIVE_WS_URL = ws://127.0.0.1:7114/ws

+ 1 - 0
package.json

@@ -103,6 +103,7 @@
     "quill": "1.3.7",
     "screenfull": "5.0.2",
     "sortablejs": "1.10.2",
+    "tt-uploader": "^1.5.5",
     "vod-js-sdk-v6": "^1.7.0",
     "vue": "2.6.12",
     "vue-baidu-map": "^0.21.22",

+ 34 - 0
src/api/course/userVideo.js

@@ -104,3 +104,37 @@ export function getThumbnail(file) {
     }
   });
 }
+
+
+//火山云视频上传
+export function uploadUserVideo(file,uploadId) {
+  const formData = new FormData();
+  formData.append('file', file);
+  formData.append('uploadId', uploadId)
+  return request({
+    url: '/course/userVideo/uploadUserVideo',
+    method: 'post',
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  });
+}
+
+// 查询火山云视频上传进度
+export function getUploadProgress(uploadId) {
+  return request({
+    url: '/course/userVideo/uploadProgress',
+    method: 'get',
+    params: { uploadId }
+  });
+}
+
+export function HsyAssumeRoleService() {
+  return request({
+    url: '/course/userVideo/HsyAssumeRoleService',
+    method: 'get',
+  });
+}
+
+

+ 92 - 0
src/utils/hsy.js

@@ -0,0 +1,92 @@
+import TTUploader from 'tt-uploader'
+import { HsyAssumeRoleService } from '@/api/course/userVideo'
+const  spaceName = process.env.VUE_APP_HSY_SPACE
+export const uploadToHSY = async (file, onProgress, type, callBackUp) => {
+  try {
+    const res = await HsyAssumeRoleService()
+    //console.log('火山云 STS 凭证:', res)
+    const credentials = res.data.result.credentials
+    //console.log('火山云 credentials 凭证:', credentials)
+
+    if (!credentials) {
+      throw new Error('未获取到火山云 STS 凭证')
+    }
+
+    const uploader = new TTUploader({
+      appId: '917052',
+      userId: '68185444',
+      videoConfig: {
+        spaceName: spaceName,
+      }
+    })
+
+    const date = new Date();
+    const yyyy = date.getFullYear();
+    const MM = String(date.getMonth() + 1).padStart(2, '0');
+    const dd = String(date.getDate()).padStart(2, '0');
+    const datePath = `${yyyy}${MM}${dd}`;
+
+
+    const fileName = `${Date.now()}.mp4`;
+
+    const remoteFileName = `course/${datePath}/${fileName}`;
+
+    const fileKey = uploader.addFile({
+      file,
+      type: 'video',
+      fileName:remoteFileName,
+      stsToken: {
+        CurrentTime: credentials.currentTime,
+        ExpiredTime: credentials.expiredTime,
+        SessionToken: credentials.sessionToken,
+        AccessKeyID: credentials.accessKeyId,
+        SecretAccessKey: credentials.secretAccessKey,
+      },
+    })
+
+    return new Promise((resolve, reject) => {
+
+      // 上传进度
+      uploader.on('progress', (info) => {
+        onProgress && onProgress({
+          percent: info.percent,
+        })
+      })
+
+      // 上传完成
+      uploader.on('complete', (info) => {
+        resolve({
+          ...info.uploadResult,
+          fileKey,
+        })
+      })
+
+      // 上传失败
+      uploader.on('error', (info) => {
+        reject(info)
+      })
+
+      // 取消上传支持
+      if (callBackUp) {
+        callBackUp({
+          uploader,
+          fileKey,
+          cancel: () => {
+            try {
+              uploader.removeFile(fileKey)
+              reject(new Error('Upload cancelled by user'))
+            } catch (e) {
+              reject(e)
+            }
+          }
+        })
+      }
+
+      uploader.start(fileKey)
+    })
+
+  } catch (error) {
+    console.error('火山云上传失败:', error)
+    throw error
+  }
+}

+ 121 - 28
src/views/course/videoResource/index.vue

@@ -774,7 +774,9 @@ import {getByIds, listCourseQuestionBank} from '@/api/course/courseQuestionBank'
 import {getThumbnail} from "@/api/course/userVideo";
 import {uploadObject} from "@/utils/cos.js";
 import {uploadToOBS} from "@/utils/obs.js";
+import {uploadToHSY} from "@/utils/hsy.js";
 import MinimizableDialog from "@/components/MinimizableDialog"
+import log from "@/views/monitor/job/log.vue";
 
 export default {
   name: 'VideoResource',
@@ -827,6 +829,8 @@ export default {
         typeSubId: null,
         projectIds: [],
         sort: null,
+        hsyVid:null,//火山云上传视频返回vid
+        hsyVodUrl:null,//火山云url
         // 新增上传状态字段
         uploadStatus: 'pending', // pending, uploading, success, failed
         uploadProgress: {
@@ -1107,6 +1111,7 @@ export default {
           this.add = true
 
           const params = Object.assign({}, this.form);
+          console.log("提交素材表单参数",this.form)
           params.projectIds = this.form.projectIds.join(',');
           if (this.form.id != null) {
             updateVideoResource(params).then(response => {
@@ -1301,37 +1306,95 @@ export default {
       }
     },
     //上传华为云Obs
-    async uploadVideoToHwObs(file, form, onProgress) {
+    // async uploadVideoToHwObs(file, form, onProgress) {
+    //   try {
+    //     // 更新线路2状态为上传中
+    //     this.updateUploadProgress('line2Status', 'uploading');
+    //
+    //     const data = await uploadToOBS(file, (progress) => {
+    //       const progressPercent = Math.floor(progress);
+    //       this.updateUploadProgress('line2', progressPercent);
+    //       const progressEvent = { percent: progressPercent, loaded: progress, total: progress, lengthComputable: true };
+    //       onProgress(progressEvent);
+    //     }, 1, (uploadInfo) => {
+    //       if (form.tempId) {
+    //         const tokens = this.uploadCancellationTokens.get(form.tempId) || {};
+    //         tokens.obs = uploadInfo.cancel;
+    //         this.uploadCancellationTokens.set(form.tempId, tokens);
+    //       }
+    //     });
+    //
+    //     form.line2 = `${process.env.VUE_APP_VIDEO_LINE_2}/${data.urlPath}`;
+    //
+    //     // 更新线路2状态为成功
+    //     this.updateUploadProgress('line2Status', 'success');
+    //     this.updateUploadProgress('line2', 100);
+    //
+    //     this.$message.success("线路二上传成功");
+    //     return { success: true, url: form.line2 };
+    //   } catch (error) {
+    //     // 更新线路2状态为失败
+    //     this.updateUploadProgress('line2Status', 'failed');
+    //     this.$message.error("线路二上传失败");
+    //     return { success: false, error: error.message };
+    //   }
+    // },
+    //上传火山云
+    async uploadVideoToHsy(file, form, onProgress) {
       try {
-        // 更新线路2状态为上传中
         this.updateUploadProgress('line2Status', 'uploading');
 
-        const data = await uploadToOBS(file, (progress) => {
-          const progressPercent = Math.floor(progress);
-          this.updateUploadProgress('line2', progressPercent);
-          const progressEvent = { percent: progressPercent, loaded: progress, total: progress, lengthComputable: true };
-          onProgress(progressEvent);
-        }, 1, (uploadInfo) => {
-          if (form.tempId) {
-            const tokens = this.uploadCancellationTokens.get(form.tempId) || {};
-            tokens.obs = uploadInfo.cancel;
-            this.uploadCancellationTokens.set(form.tempId, tokens);
-          }
-        });
+        const data = await uploadToHSY(
+          file,
+          (progress) => {
+            // 火山云的进度是小数0-1
+            if (typeof progress.percent === 'number') {
+              const percent = Math.floor(progress.percent * 100);
+
+              // 更新线路2进度
+              this.updateUploadProgress('line2', percent);
+
+              // 对外统一 progress 事件(模拟 xhr)
+              onProgress?.({
+                percent,
+                loaded: percent,
+                total: 100,
+                lengthComputable: true
+              });
+            }
 
-        form.line2 = `${process.env.VUE_APP_VIDEO_LINE_2}/${data.urlPath}`;
+            // 状态同步(成功 / 失败)
+            if (progress.status === 'success') {
+              this.updateUploadProgress('line2Status', 'success');
+              this.updateUploadProgress('line2', 100);
+            }
 
-        // 更新线路2状态为成功
-        this.updateUploadProgress('line2Status', 'success');
-        this.updateUploadProgress('line2', 100);
+            if (progress.status === 'failed') {
+              this.updateUploadProgress('line2Status', 'failed');
+            }
+          },
+          1,
+          (uploadInfo) => {
+            if (form.tempId) {
+              const tokens = this.uploadCancellationTokens.get(form.tempId) || {};
+              tokens.hsy = uploadInfo.cancel;
+              this.uploadCancellationTokens.set(form.tempId, tokens);
+            }
+          }
+        );
+        console.log("上传火山云返回参数",data)
 
-        this.$message.success("线路二上传成功");
+        form.line2 = `${process.env.VUE_APP_VIDEO_URL}/${data.SourceInfo.FileName}`;
+        this.form.hsyVid = data.Vid
+        this.form.hsyVodUrl = process.env.VUE_APP_VIDEO_URL+"/"+data.SourceInfo.FileName
+        console.log("this.form",this.form)
+        this.$message.success('线路二上传成功');
         return { success: true, url: form.line2 };
+
       } catch (error) {
-        // 更新线路2状态为失败
         this.updateUploadProgress('line2Status', 'failed');
-        this.$message.error("线路二上传失败");
-        return { success: false, error: error.message };
+        this.$message.error('线路二上传失败');
+        return { success: false, error: error?.message || 'upload failed' };
       }
     },
     // 更新上传进度的辅助方法
@@ -1368,7 +1431,8 @@ export default {
         await this.getFirstThumbnail(file, this.form);
         const [line1Result, line2Result] = await Promise.allSettled([
           this.uploadVideoToTxPcdn(file, this.form, options.onProgress),
-          this.uploadVideoToHwObs(file, this.form, options.onProgress)
+          //this.uploadVideoToHwObs(file, this.form, options.onProgress)
+          this.uploadVideoToHsy(file, this.form, options.onProgress)
         ]);
 
         const line1Success = line1Result.status === 'fulfilled' && line1Result.value.success;
@@ -1470,6 +1534,7 @@ export default {
     },
     /** 批量修改 */
     submitBatchUpdate() {
+      console.log("批量上传表单提交参数",this.form)
       this.$refs["form"].validate(valid => {
         if (valid) {
           if (this.batchUpdateForm.ids.length === 0) {
@@ -1503,6 +1568,7 @@ export default {
       }
 
       // 检查是否所有选中的视频都已上传完成
+      console.log("videoList",this.videoList)
       const incompleteVideos = this.videoList.filter(item => (item.progress || 0) < 100);
       if (incompleteVideos.length > 0) {
         this.$message.warning('有未完成上传的视频,请先完成上传');
@@ -1776,6 +1842,7 @@ export default {
       this.isProcessingBatch = false;
       this.isUploading = false;
       this.$message.success('所有视频上传队列处理完成!');
+      console.log("批量上传form",this.form)
     },
 
     async uploadSingleVideo(tempVideo) {
@@ -1786,7 +1853,9 @@ export default {
         // 并行上传到两个服务器
         const [line1Result, line2Result] = await Promise.allSettled([
           this.uploadVideoToTxPcdnBatch(tempVideo.file, tempVideo),
-          this.uploadVideoToHwObsBatch(tempVideo.file, tempVideo)
+          // this.uploadVideoToHwObsBatch(tempVideo.file, tempVideo)
+          this.uploadVideoToHSYBatch(tempVideo.file, tempVideo),
+
         ]);
 
         // 检查上传结果
@@ -2110,9 +2179,31 @@ export default {
       }
     },
     // 批量上传 - 华为云
-    async uploadVideoToHwObsBatch(file, tempVideo) {
+    // async uploadVideoToHwObsBatch(file, tempVideo) {
+    //   try {
+    //     const data = await uploadToOBS(file, (progress) => {
+    //       const progressPercent = Math.floor(progress);
+    //       const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
+    //       if (index !== -1) {
+    //         this.videoList[index].uploadDetails.line2 = progressPercent;
+    //         this.videoList[index].uploadDetails.line2Status = 'uploading';
+    //         this.updateBatchProgress(index);
+    //       }
+    //     }, 1, (uploadInfo) => {
+    //       const tokens = this.uploadCancellationTokens.get(tempVideo.tempId) || {};
+    //       tokens.obs = uploadInfo.cancel;
+    //       this.uploadCancellationTokens.set(tempVideo.tempId, tokens);
+    //     });
+    //
+    //     tempVideo.line2 = `${process.env.VUE_APP_VIDEO_LINE_2}/${data.urlPath}`;
+    //     return { success: true, url: tempVideo.line2 };
+    //   } catch (error) {
+    //     return { success: false, error: error.message };
+    //   }
+    // },
+    async uploadVideoToHSYBatch(file, tempVideo) {
       try {
-        const data = await uploadToOBS(file, (progress) => {
+        const data = await uploadToHSY(file, (progress) => {
           const progressPercent = Math.floor(progress);
           const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
           if (index !== -1) {
@@ -2125,9 +2216,11 @@ export default {
           tokens.obs = uploadInfo.cancel;
           this.uploadCancellationTokens.set(tempVideo.tempId, tokens);
         });
+        console.log("批量上传返回参数",data)
+        tempVideo.line2 = `${process.env.VUE_APP_VIDEO_URL}/${data.SourceInfo.FileName}`;
+        tempVideo.hsyVid = data.Vid;
 
-        tempVideo.line2 = `${process.env.VUE_APP_VIDEO_LINE_2}/${data.urlPath}`;
-        return { success: true, url: tempVideo.line2 };
+        return { success: true, url: tempVideo.line2};
       } catch (error) {
         return { success: false, error: error.message };
       }