Browse Source

video cut

qinyan 1 year ago
parent
commit
b53939c2c8

+ 1 - 1
src/modules/editor/components/CompUI/basicUI/Page/component.tsx

@@ -27,7 +27,7 @@ export const Component = defineComponent({
           style={helper.createStyle(layout || { size: [750] })}
           class={["!h-auto", editor.store.isEditMode ? pageEditStyle : ""]}
         >
-          <div class="relative z-1000">
+          <div class="relative z-999">
             {slots.Container?.(
               children.default.map((compId) => {
                 const comp = helper.findComp(compId);

+ 4 - 2
src/modules/editor/components/CompUI/formItems/Slider.tsx

@@ -1,7 +1,7 @@
 import { css } from "@linaria/core";
 import { InputNumber, Slider } from "ant-design-vue";
 import { defineComponent, reactive, watchEffect } from "vue";
-import { bool, number } from "vue-types";
+import { bool, func, number } from "vue-types";
 
 export default defineComponent({
   props: {
@@ -11,6 +11,7 @@ export default defineComponent({
     min: number(),
     max: number(),
     step: number(),
+    formatter: func<(...args: any[]) => string>(),
   },
   emits: ["change"],
   setup(props, { emit }) {
@@ -30,7 +31,7 @@ export default defineComponent({
     });
 
     return () => {
-      const { defaultValue, disabled, min, max, step } = props;
+      const { formatter, defaultValue, disabled, min, max, step } = props;
       const attr = {
         defaultValue,
         disabled: disabled,
@@ -54,6 +55,7 @@ export default defineComponent({
           <InputNumber
             class="item_input"
             {...attr}
+            formatter={formatter}
             onPressEnter={() => changeVal()}
             // onBlur={() => {
             //   if (state.value !== props.value) changeVal();

+ 188 - 12
src/modules/editor/components/Viewport/Slider/SliderLeft/Sources/EditVideoModal.tsx

@@ -1,6 +1,12 @@
+import Empty from "@/components/Empty";
+import { TimeController } from "@/controllers/TimeController";
+import Slider from "@/modules/editor/components/CompUI/formItems/Slider";
 import { useResource } from "@/modules/resource";
 import { Button } from "ant-design-vue";
-import { defineComponent, reactive } from "vue";
+import dayjs from "dayjs";
+import { queenApi } from "queenjs";
+import Modal from "queenjs/adapter/vue/components/modal";
+import { defineComponent, reactive, ref } from "vue";
 import { object } from "vue-types";
 
 export default defineComponent({
@@ -9,29 +15,199 @@ export default defineComponent({
   },
   setup(props) {
     const resource = useResource();
+    const videoRef = ref();
+    const previewRef = ref();
+
     const state = reactive({
-      //
+      duration: 0,
+      startTime: 0,
+      endTime: 0,
+      previewObj: {} as any,
     });
 
-    const submit = () => {
-      resource.actions.editVideo({
-        start: 13,
-        end: 15,
+    const editVideo = async () => {
+      if (state.endTime - state.startTime <= 0) {
+        queenApi.messageError("截取时长错误");
+        return;
+      }
+      queenApi.showLoading("正在处理……");
+      const data = {
+        start: state.startTime,
+        end: state.endTime,
+        id: props.record._id,
+      };
+      const res = await resource.https.cutVideo(data);
+      const { url, jobId } = res.result;
+
+      const videoStatusCtrl = new TimeController({
+        delayTime: 1000,
+        durationTime: 3000,
+      });
+      videoStatusCtrl
+        .setLoop(async () => {
+          const { result } = await resource.https.queryVideoStatus(jobId);
+          if (result === "SUCCEED") {
+            videoStatusCtrl.stop();
+            queenApi.hideLoading();
+            const souceObj: createSourceType = {
+              file: { url },
+              fileType: "video",
+              from: "cut",
+            };
+            state.previewObj = souceObj;
+          } else if (result === "FAILED") {
+            videoStatusCtrl.stop();
+            queenApi.hideLoading();
+            queenApi.messageError("失败,重新提交!");
+          }
+        })
+        .start();
+    };
+
+    const createNew = async () => {
+      queenApi.showLoading("正在创建……");
+      await resource.https.createResource(state.previewObj);
+      setTimeout(async () => {
+        await resource.controls.custVideoListCtrl.loadPage(1);
+        Modal.clear();
+        queenApi.hideLoading();
+      }, 800);
+    };
+
+    const replaceVideo = async () => {
+      queenApi.showLoading("正在替换……");
+      const res = await resource.https.updateVideoUrl({
+        url: state.previewObj.file.url,
         id: props.record._id,
       });
+      setTimeout(async () => {
+        // eslint-disable-next-line vue/no-mutating-props
+        props.record.thumbnail = res.result.thumbUrl;
+        // eslint-disable-next-line vue/no-mutating-props
+        props.record.file.url = state.previewObj.file.url;
+        Modal.clear();
+        queenApi.hideLoading();
+      }, 800);
     };
 
+    function changeStart(v: number) {
+      videoRef.value.currentTime = v;
+      state.startTime = v;
+      if (state.endTime < state.startTime) {
+        state.endTime = state.startTime + 1;
+      }
+    }
+
+    function changeEnd(v: number) {
+      // videoRef.value.currentTime = v;
+      state.endTime = v;
+    }
+
+    function formateTime(time: number) {
+      // @ts-ignore
+      const h = parseInt(time / 3600);
+      // @ts-ignore
+      const minute = parseInt((time / 60) % 60);
+      const second = Math.ceil(time % 60);
+
+      const hours = h < 10 ? "0" + h : h;
+      const formatSecond = second > 59 ? 59 : second;
+      // @ts-ignore
+      return `${hours > 0 ? `${hours}:` : ""}${
+        minute < 10 ? "0" + minute : minute
+      }:${formatSecond < 10 ? "0" + formatSecond : formatSecond}`;
+    }
+
     return () => {
       const { record } = props;
-      //   console.log("record: ", record);
+      const { duration, startTime, endTime, previewObj } = state;
+      const submitDisabled = previewObj.file ? false : true;
+
       return (
         <div>
-          <div>
-            <video src={record.file?.url} class="w-full" controls />
+          <div class="flex items-center justify-center space-x-30px overflow-hidden">
+            <div>
+              <div class="mb-20px">原视频</div>
+              <div class="flex items-center justify-center w-510px h-400px bg-[#1B1B1B] rounded">
+                <video
+                  controls
+                  ref={videoRef}
+                  src={record.file?.url}
+                  class="w-full"
+                  onLoadeddata={() => {
+                    const time = videoRef.value.duration;
+                    state.duration = Math.floor(time);
+                    videoRef.value.volume = videoRef.value.volume / 2;
+                  }}
+                />
+              </div>
+            </div>
+            <div>
+              <div class="mb-20px">剪辑后预览</div>
+              <div class="flex items-center justify-center w-510px h-400px bg-[#1B1B1B] rounded">
+                {previewObj.file?.url ? (
+                  <video
+                    controls
+                    ref={previewRef}
+                    src={previewObj.file?.url}
+                    class="w-full"
+                    onLoadeddata={() => {
+                      previewRef.value.volume = previewRef.value.volume / 2;
+                    }}
+                  />
+                ) : (
+                  <Empty />
+                )}
+              </div>
+            </div>
+          </div>
+          <div class="mt-50px font-bold text-14px">剪辑</div>
+          <div class="flex items-center justify-around h-60px mt-20px rounded bg-[#1B1B1B]">
+            <div class="flex items-center justify-between space-x-20px">
+              <span class="text-12px">启始时间</span>
+              <Slider
+                min={0}
+                max={duration}
+                value={startTime}
+                class="w-300px"
+                onChange={changeStart}
+                formatter={(v) => formateTime(v)}
+              />
+            </div>
+            <div class="flex items-center justify-between space-x-20px">
+              <span class="text-12px">结束时间</span>
+              <Slider
+                min={0}
+                max={duration}
+                value={endTime}
+                class="w-300px"
+                onChange={changeEnd}
+                formatter={(v) => formateTime(v)}
+              />
+            </div>
+            <Button type="primary" class="w-100px" onClick={editVideo}>
+              生成视频
+            </Button>
           </div>
-          <div>
-            <Button type="primary" onClick={submit}>
-              提交
+          <div class="mt-60px text-center space-x-30px">
+            <Button
+              ghost
+              type="primary"
+              size="large"
+              class="w-300px"
+              disabled={submitDisabled}
+              onClick={replaceVideo}
+            >
+              替换原视频
+            </Button>
+            <Button
+              type="primary"
+              size="large"
+              class="w-300px"
+              disabled={submitDisabled}
+              onClick={createNew}
+            >
+              生成新视频
             </Button>
           </div>
         </div>

+ 2 - 1
src/modules/editor/components/Viewport/Slider/SliderLeft/Sources/SourceItem.tsx

@@ -23,7 +23,8 @@ const VideoItem = defineComponent({
     function showModal(record: any) {
       queenApi.dialog(<EditVideoModal record={record} />, {
         title: "编辑视频",
-        width: "800px",
+        width: "1100px",
+        centered: true,
       });
     }
 

+ 4 - 4
src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx

@@ -116,8 +116,8 @@ const tabs = [
 export default defineUI({
   setup() {
     const state = reactive({
-      tabIndex: 0,
-      list: [0],
+      tabIndex: 7,
+      list: [7],
     });
 
     return () => {
@@ -191,8 +191,8 @@ const PanelContent = defineComponent({
   },
   setup(props) {
     const state = reactive({
-      currCompIndex: 0,
-      list: [0],
+      currCompIndex: 1,
+      list: [1],
     });
 
     const SubTabs = () => {

+ 2 - 2
src/modules/editor/components/Viewport/Toolbar/index.tsx

@@ -50,13 +50,13 @@ export default defineUI({
               </div>
             </div>
           }
-          <div class="absolute right-25px top-80px space-x-10px z-1001">
+          <div class="absolute right-25px top-80px space-x-10px z-1000">
             <TipIcons.Screenshot
               class={bottomBtnStyles}
               onClick={() => actions.updateThumbnailByScreenshot(true)}
             />
           </div>
-          <div class="absolute bottom-20px right-20px z-1001">
+          <div class="absolute bottom-20px right-20px z-1000">
             <TipIcons.QueenService
               class={bottomBtnStyles}
               onClick={() => {

+ 0 - 33
src/modules/resource/actions/material.ts

@@ -57,37 +57,4 @@ export const materialActions = ResourceModule.action({
     const url = `${location.origin}/index.html?host=${host}#/create/${record._id}`;
     location.href = url;
   },
-  async editVideo(data) {
-    queenApi.showLoading("正在处理……");
-    const res = await this.https.cutVideo(data);
-    const { url, jobId } = res.result;
-
-    const videoStatusCtrl = new TimeController({
-      delayTime: 1000,
-      durationTime: 3000,
-    });
-    videoStatusCtrl
-      .setLoop(async () => {
-        const { result } = await this.https.queryVideoStatus(jobId);
-        if (result === "SUCCEED") {
-          videoStatusCtrl.stop();
-          const souceObj: createSourceType = {
-            file: { url },
-            fileType: "video",
-            from: "cut",
-          };
-          await this.https.createResource(souceObj);
-          setTimeout(async () => {
-            await this.controls.custVideoListCtrl.loadPage(1);
-            Modal.clear();
-            queenApi.hideLoading();
-          }, 500);
-        } else if (result === "FAILED") {
-          videoStatusCtrl.stop();
-          queenApi.hideLoading();
-          queenApi.messageError("失败,重新提交!");
-        }
-      })
-      .start();
-  },
 });

+ 6 - 1
src/modules/resource/http.ts

@@ -15,15 +15,20 @@ export const http = ResourceModule.http({
   createResource(data: createSourceType) {
     return this.request("/source/create", { method: "POST", data });
   },
+
   updateResouce(data: any) {
     return this.request("/source/update", { method: "POST", data });
   },
-  cutVideo(data: { id: string; start: string; end: string }) {
+
+  cutVideo(data: { id: string; start: number; end: number }) {
     return this.request("/video/cut", { method: "POST", data });
   },
   queryVideoStatus(id: string) {
     return this.request(`/video/cut/${id}`, { method: "GET" });
   },
+  updateVideoUrl(data: { id: string; url: string }) {
+    return this.request("source/video/update", { method: "POST", data });
+  },
 
   //   templates
   queryTplsDetail(id: string) {