Browse Source

增加导出剪映草稿功能

黎海 2 months ago
parent
commit
1680c9325a

+ 1 - 1
src/components/CozeApiSettings.js

@@ -134,7 +134,7 @@ const CozeApiSettings = ({ onSave, isInModal = true }) => {
134 134
   // 生成工作流ID配置表单
135 135
   const renderWorkflowIdInputs = () => {
136 136
     // 添加所有工作流
137
-    const mainWorkflows = ['textToDescription', 'getStyleList', 'generateImage'];
137
+    const mainWorkflows = ['textToDescription', 'getStyleList', 'generateImage', 'exportJianyingDraft'];
138 138
     
139 139
     return mainWorkflows.map((key) => (
140 140
       <Form.Group className="mb-3" key={key}>

+ 115 - 0
src/pages/projectDetail/projectDetail.js

@@ -66,6 +66,8 @@ const ProjectDetail = () => {
66 66
   const [lipSyncProgress, setLipSyncProgress] = useState(0);
67 67
   const [lipSyncTaskId, setLipSyncTaskId] = useState(null);
68 68
   const [lipSyncResultUrl, setLipSyncResultUrl] = useState(null);
69
+  // 添加导出剪映草稿相关状态
70
+  const [exportingDraft, setExportingDraft] = useState(false);
69 71
 
70 72
   const fileInputRef = useRef(null);
71 73
 
@@ -2004,6 +2006,97 @@ const ProjectDetail = () => {
2004 2006
     }
2005 2007
   };
2006 2008
 
2009
+  /**
2010
+   * 处理导出剪映草稿
2011
+   */
2012
+  const handleExportDraft = async () => {
2013
+    try {
2014
+      // 检查是否有音频和视频
2015
+      if (!project.audio_url || !project.lip_sync_video_path) {
2016
+        toast.error('请先完成对口型视频生成');
2017
+        return;
2018
+      }
2019
+
2020
+      // 检查是否有分镜数据
2021
+      if (!segments || segments.length === 0) {
2022
+        toast.error('没有可导出的分镜数据');
2023
+        return;
2024
+      }
2025
+
2026
+      // 检查所有分镜是否都有必要的字段
2027
+      const invalidSegments = segments.filter(segment => 
2028
+        !segment.start_time || 
2029
+        !segment.end_time || 
2030
+        !segment.text || 
2031
+        !segment.image_path
2032
+      );
2033
+
2034
+      if (invalidSegments.length > 0) {
2035
+        toast.error(`有${invalidSegments.length}个分镜缺少必要信息,请完善后再导出`);
2036
+        return;
2037
+      }
2038
+
2039
+      setExportingDraft(true);
2040
+
2041
+      // 转换时间格式为微秒
2042
+      const textList = segments.map(segment => {
2043
+        const startTime = convertTimeToMicroseconds(segment.start_time);
2044
+        const endTime = convertTimeToMicroseconds(segment.end_time);
2045
+        const duration = endTime - startTime;
2046
+
2047
+        return {
2048
+          start_time: startTime,
2049
+          end_time: endTime,
2050
+          duration: duration,
2051
+          text: segment.text,
2052
+          image_path: segment.image_path
2053
+        };
2054
+      });
2055
+
2056
+      // 调用工作流API
2057
+      const cozeInstance = initCozeService();
2058
+      const response = await cozeInstance.runWorkflow(WORKFLOW_IDS.exportJianyingDraft, {
2059
+        audio_url: project.audio_url,
2060
+        video_url: project.lip_sync_video_path,
2061
+        text_list: textList
2062
+      });
2063
+
2064
+      if (response && response.data) {
2065
+        try {
2066
+          const result = JSON.parse(response.data);
2067
+          if (result.output) {
2068
+            toast.success('剪映草稿导出成功');
2069
+            // 这里可以添加下载草稿文件的逻辑
2070
+          } else {
2071
+            throw new Error('导出结果格式不正确');
2072
+          }
2073
+        } catch (e) {
2074
+          throw new Error('解析导出结果失败');
2075
+        }
2076
+      } else {
2077
+        throw new Error('导出失败,未收到有效响应');
2078
+      }
2079
+    } catch (error) {
2080
+      console.error('导出剪映草稿失败:', error);
2081
+      toast.error(`导出失败: ${error.message}`);
2082
+    } finally {
2083
+      setExportingDraft(false);
2084
+    }
2085
+  };
2086
+
2087
+  // 辅助函数:将时间格式转换为微秒
2088
+  const convertTimeToMicroseconds = (timeStr) => {
2089
+    if (!timeStr) return 0;
2090
+    
2091
+    // 处理时间格式 "HH:MM:SS,SSS"
2092
+    const [time, milliseconds] = timeStr.split(',');
2093
+    const [hours, minutes, seconds] = time.split(':').map(Number);
2094
+    const ms = milliseconds ? parseInt(milliseconds) : 0;
2095
+    
2096
+    // 转换为微秒
2097
+    return (hours * 3600 + minutes * 60 + seconds) * 1000000 + ms * 1000;
2098
+  };
2099
+
2007 2100
   if (loading && !silentLoading) {
2008 2101
     return <div className="text-center p-5">加载中...</div>;
2009 2102
   }
@@ -2281,6 +2374,28 @@ const ProjectDetail = () => {
2281 2374
                   >
2282 2375
                     清除选择
2283 2376
                   </Button>
2377
+                  <Button
2378
+                    variant="outline-success"
2379
+                    size="sm"
2380
+                    onClick={handleExportDraft}
2381
+                    disabled={exportingDraft || !project.audio_url || !project.lip_sync_video_path || !segments || segments.length === 0}
2382
+                  >
2383
+                    {exportingDraft ? (
2384
+                      <>
2385
+                        <Spinner
2386
+                          as="span"
2387
+                          animation="border"
2388
+                          size="sm"
2389
+                          role="status"
2390
+                          aria-hidden="true"
2391
+                          className="me-1"
2392
+                        />
2393
+                        导出中...
2394
+                      </>
2395
+                    ) : (
2396
+                      '导出剪映草稿'
2397
+                    )}
2398
+                  </Button>
2284 2399
                 </div>
2285 2400
               </Card.Header>
2286 2401
               <Card.Body>

+ 3 - 0
src/utils/cozeConfig.js

@@ -28,6 +28,8 @@ export const WORKFLOW_IDS = {
28 28
   // 添加画风列表和画图工作流
29 29
   getStyleList: storedWorkflowIds.getStyleList || process.env.REACT_APP_COZE_WORKFLOW_GET_STYLE_LIST || 'workflow_id_for_get_style_list',
30 30
   generateImage: storedWorkflowIds.generateImage || process.env.REACT_APP_COZE_WORKFLOW_GENERATE_IMAGE || 'workflow_id_for_generate_image',
31
+  // 添加导出剪映草稿工作流
32
+  exportJianyingDraft: storedWorkflowIds.exportJianyingDraft || process.env.REACT_APP_COZE_WORKFLOW_EXPORT_JIANYING_DRAFT || '7485203690664837120',
31 33
 };
32 34
 
33 35
 // 工作流名称到显示名称的映射
@@ -35,6 +37,7 @@ export const WORKFLOW_DISPLAY_NAMES = {
35 37
   textToDescription: '文本转描述词',
36 38
   getStyleList: '获取画风列表',
37 39
   generateImage: '生成图像',
40
+  exportJianyingDraft: '导出剪映草稿',
38 41
 };
39 42
 
40 43
 // 获取所有工作流ID和显示名称的映射