forked from 77media/video-flow
完成接口
This commit is contained in:
parent
7960573a12
commit
e983a10037
@ -2,8 +2,8 @@ import { post } from './request';
|
||||
import { ProjectTypeEnum } from '@/app/model/enums';
|
||||
import { ApiResponse } from '@/api/common';
|
||||
import { BASE_URL } from './constants'
|
||||
import { AITextEntity, RoleEntity, SceneEntity, ShotEntity, TagEntity, ContentItem } from '@/app/service/domain/Entities';
|
||||
import { ScriptSlice } from "@/app/service/domain/valueObject";
|
||||
import { AITextEntity, RoleEntity, SceneEntity, ShotEntity, TagEntity } from '@/app/service/domain/Entities';
|
||||
import { ContentItem, LensType, ScriptSlice } from "@/app/service/domain/valueObject";
|
||||
|
||||
// API 响应类型
|
||||
interface BaseApiResponse<T> {
|
||||
@ -471,7 +471,7 @@ export const regenerateShot = async (request: {
|
||||
/** 分镜ID */
|
||||
shotId?: string;
|
||||
/** 镜头描述 */
|
||||
shotPrompt?: string;
|
||||
shotPrompt?: LensType[];
|
||||
/** 对话内容 */
|
||||
dialogueContent?: ContentItem[];
|
||||
/** 角色ID替换参数,格式为{oldId:string,newId:string}[] */
|
||||
@ -575,7 +575,7 @@ export const getShotVideoScript = async (request: {
|
||||
*/
|
||||
export const generateScriptStream = async (request: {
|
||||
/** 剧本提示词 */
|
||||
prompt: string;
|
||||
text: string;
|
||||
}) => {
|
||||
return post<ApiResponse<any>>('/text_to_script/generate_script_stream', request,{
|
||||
responseType: 'stream',
|
||||
@ -588,8 +588,10 @@ export const generateScriptStream = async (request: {
|
||||
* @returns Promise<ApiResponse<应用结果>>
|
||||
*/
|
||||
export const applyScriptToShot = async (request: {
|
||||
/** 项目ID */
|
||||
projectId: string;
|
||||
/** 剧本*/
|
||||
script: string;
|
||||
scriptText: string;
|
||||
}): Promise<ApiResponse<any>> => {
|
||||
return post<ApiResponse<any>>('/movie/apply_script_to_shot', request);
|
||||
};
|
||||
@ -612,3 +614,36 @@ export const getProjectScript = async (request: {
|
||||
scriptText: string;
|
||||
}>>('/movie/get_project_script', request);
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存剧本
|
||||
* @param request 保存剧本请求参数
|
||||
* @returns Promise<ApiResponse<保存结果>>
|
||||
*/
|
||||
export const saveScript = async (request: {
|
||||
/** 项目ID */
|
||||
projectId: string;
|
||||
/** 剧本文本 */
|
||||
scriptText: string;
|
||||
}): Promise<ApiResponse<any>> => {
|
||||
return post<ApiResponse<any>>('/movie/save_script', request);
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建项目
|
||||
* @param request 创建项目请求参数
|
||||
* @returns Promise<ApiResponse<{ projectId: string }>>
|
||||
*/
|
||||
export const createProject = async (request: {
|
||||
/** 用户提示词 */
|
||||
userPrompt: string;
|
||||
/** 剧本内容 */
|
||||
scriptContent: string;
|
||||
}): Promise<ApiResponse<{
|
||||
/** 项目ID */
|
||||
projectId: string;
|
||||
}>> => {
|
||||
return post<ApiResponse<{
|
||||
projectId: string;
|
||||
}>>('/movie/create_project', request);
|
||||
};
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { useState, useCallback, useMemo } from "react";
|
||||
import { ScriptSlice } from "../domain/valueObject";
|
||||
import { ScriptEditUseCase } from "../usecase/ScriptEditUseCase";
|
||||
import { getProjectScript } from "../../../api/video_flow";
|
||||
import { getProjectScript, saveScript as saveScriptAPI, createProject as createProjectAPI } from "../../../api/video_flow";
|
||||
import { throttle } from "@/utils/tools";
|
||||
|
||||
/**
|
||||
* 剧本服务Hook接口
|
||||
@ -13,113 +14,83 @@ export interface UseScriptService {
|
||||
scriptText: string;
|
||||
/** 剧本片段列表 */
|
||||
scriptSlices: ScriptSlice[];
|
||||
/** 当前聚焦的剧本片段ID */
|
||||
focusedSliceId: string;
|
||||
/** 当前聚焦的剧本片段 */
|
||||
focusedSlice: ScriptSlice | null;
|
||||
/** 当前聚焦的剧本片段文本 */
|
||||
scriptSliceText: string;
|
||||
/** 用户提示词(可编辑) */
|
||||
userPrompt: string;
|
||||
/** 加载状态 */
|
||||
loading: boolean;
|
||||
/** 错误信息 */
|
||||
error: string | null;
|
||||
/** 项目ID */
|
||||
projectId: string;
|
||||
|
||||
// 操作方法
|
||||
/** 获取剧本数据(用户提示词) */
|
||||
/** 获取根据用户想法调用接口AI生成剧本(用户提示词) */
|
||||
fetchScriptData: (prompt: string) => Promise<void>;
|
||||
/** 根据项目ID获取已存在的剧本数据 */
|
||||
fetchProjectScript: (projectId: string) => Promise<void>;
|
||||
/** 设置当前聚焦的剧本片段 */
|
||||
setFocusedSlice: (sliceId: string) => void;
|
||||
/** 清除聚焦状态 */
|
||||
clearFocusedSlice: () => void;
|
||||
/** 快速更新当前聚焦的剧本片段文本(无防抖) */
|
||||
updateScriptSliceText: (text: string, metaData?: any) => void;
|
||||
/** 更新用户提示词 */
|
||||
updateUserPrompt: (prompt: string) => void;
|
||||
/** 重置剧本内容到初始状态 */
|
||||
resetScript: () => void;
|
||||
/** AI生成剧本 */
|
||||
generateScript: (prompt: string) => Promise<void>;
|
||||
/** 应用剧本 */
|
||||
applyScript: () => Promise<void>;
|
||||
/** 更新聚焦剧本片段 */
|
||||
UpdateFocusedSlice: (text: string) => void;
|
||||
/** 中断剧本生成 */
|
||||
abortGenerateScript: () => void;
|
||||
/** 保存剧本 */
|
||||
saveScript: () => Promise<void>;
|
||||
/** 创建项目 */
|
||||
createProject: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 剧本服务Hook
|
||||
* 提供剧本相关的所有状态管理和操作方法
|
||||
* 包括剧本数据获取、片段管理、聚焦状态、防抖更新等功能
|
||||
* 包括剧本数据获取、片段管理等功能
|
||||
*/
|
||||
export const useScriptService = (): UseScriptService => {
|
||||
// 响应式状态
|
||||
const [scriptText, setScriptText] = useState<string>("");
|
||||
const [scriptSlices, setScriptSlices] = useState<ScriptSlice[]>([]);
|
||||
const [focusedSliceId, setFocusedSliceId] = useState<string>("");
|
||||
const [scriptSliceText, setScriptSliceText] = useState<string>("");
|
||||
const [userPrompt, setUserPrompt] = useState<string>("");
|
||||
const [initialScriptText, setInitialScriptText] = useState<string>("");
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [projectId, setProjectId] = useState<string>("");
|
||||
// UseCase实例
|
||||
const [scriptEditUseCase, setScriptEditUseCase] = useState<ScriptEditUseCase | null>(null);
|
||||
|
||||
// 防抖定时器
|
||||
const [debounceTimer, setDebounceTimer] = useState<NodeJS.Timeout | null>(null);
|
||||
const DEBOUNCE_DELAY = 300;
|
||||
|
||||
/**
|
||||
* 当前聚焦的剧本片段
|
||||
*/
|
||||
const focusedSlice = useMemo(() => {
|
||||
return scriptSlices.find(slice => slice.id === focusedSliceId) || null;
|
||||
}, [scriptSlices, focusedSliceId]);
|
||||
|
||||
/**
|
||||
* 获取剧本数据(用户提示词)
|
||||
* 初始化,ai生成剧本(用户提示词)
|
||||
* @param prompt 用户提示词
|
||||
*/
|
||||
const fetchScriptData = useCallback(async (prompt: string): Promise<void> => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// 清空当前状态
|
||||
setScriptText("");
|
||||
setScriptSlices([]);
|
||||
setFocusedSliceId("");
|
||||
setScriptSliceText("");
|
||||
|
||||
// 更新用户提示词状态
|
||||
setUserPrompt(prompt);
|
||||
|
||||
// 保存初始提示词(只在第一次获取时保存)
|
||||
if (!initialScriptText) {
|
||||
setInitialScriptText(prompt);
|
||||
}
|
||||
|
||||
// 创建新的剧本编辑用例
|
||||
const newScriptEditUseCase = new ScriptEditUseCase('');
|
||||
setScriptEditUseCase(newScriptEditUseCase);
|
||||
|
||||
// 调用AI生成剧本
|
||||
await newScriptEditUseCase.generateScript(prompt);
|
||||
|
||||
await newScriptEditUseCase.generateScript(prompt,throttle((newContent)=>{
|
||||
// 获取生成的剧本文本
|
||||
const generatedScriptText = newScriptEditUseCase.toString();
|
||||
setScriptText(generatedScriptText);
|
||||
|
||||
// 获取剧本片段列表
|
||||
const slices = newScriptEditUseCase.getScriptSlices();
|
||||
setScriptSlices(slices);
|
||||
|
||||
// 保存初始剧本文本(只在第一次获取时保存)
|
||||
if (!initialScriptText) {
|
||||
setInitialScriptText(generatedScriptText);
|
||||
}
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取剧本数据失败:', error);
|
||||
setError(error instanceof Error ? error.message : '获取剧本数据失败');
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@ -133,13 +104,12 @@ export const useScriptService = (): UseScriptService => {
|
||||
const fetchProjectScript = useCallback(async (projectId: string): Promise<void> => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// 清空当前状态
|
||||
setScriptText("");
|
||||
setScriptSlices([]);
|
||||
setFocusedSliceId("");
|
||||
setScriptSliceText("");
|
||||
|
||||
// 设置项目ID
|
||||
setProjectId(projectId);
|
||||
|
||||
// 调用API获取项目剧本数据
|
||||
const response = await getProjectScript({ projectId });
|
||||
@ -153,9 +123,9 @@ export const useScriptService = (): UseScriptService => {
|
||||
// 更新用户提示词状态
|
||||
setUserPrompt(prompt);
|
||||
|
||||
// 保存初始提示词(只在第一次获取时保存)
|
||||
// 保存初始剧本文本(只在第一次获取时保存)
|
||||
if (!initialScriptText) {
|
||||
setInitialScriptText(prompt);
|
||||
setInitialScriptText(scriptText);
|
||||
}
|
||||
|
||||
// 创建新的剧本编辑用例并初始化数据
|
||||
@ -171,84 +141,12 @@ export const useScriptService = (): UseScriptService => {
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取项目剧本数据失败:', error);
|
||||
setError(error instanceof Error ? error.message : '获取项目剧本数据失败');
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [initialScriptText]);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 设置当前聚焦的剧本片段
|
||||
* @param sliceId 剧本片段ID
|
||||
*/
|
||||
const setFocusedSlice = useCallback((sliceId: string): void => {
|
||||
setFocusedSliceId(sliceId);
|
||||
|
||||
// 同步输入框文本为当前聚焦片段的文本
|
||||
const focusedSlice = scriptSlices.find(slice => slice.id === sliceId);
|
||||
if (focusedSlice) {
|
||||
setScriptSliceText(focusedSlice.text);
|
||||
} else {
|
||||
setScriptSliceText("");
|
||||
}
|
||||
}, [scriptSlices]);
|
||||
|
||||
/**
|
||||
* 清除聚焦状态
|
||||
*/
|
||||
const clearFocusedSlice = useCallback((): void => {
|
||||
setFocusedSliceId("");
|
||||
setScriptSliceText("");
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 快速更新输入框文本(无防抖)
|
||||
* @param text 新的文本内容
|
||||
* @param metaData 新的元数据
|
||||
*/
|
||||
const updateScriptSliceText = useCallback((text: string, metaData?: any): void => {
|
||||
setScriptSliceText(text);
|
||||
|
||||
// 自动触发防抖更新值对象
|
||||
// 清除之前的定时器
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer);
|
||||
}
|
||||
|
||||
// 设置新的防抖定时器
|
||||
const timer = setTimeout(() => {
|
||||
UpdateFocusedSlice(text, metaData);
|
||||
}, DEBOUNCE_DELAY);
|
||||
|
||||
setDebounceTimer(timer);
|
||||
}, [debounceTimer]);
|
||||
|
||||
/**
|
||||
* 执行更新聚焦剧本片段
|
||||
* @param text 新的文本内容
|
||||
* @param metaData 新的元数据
|
||||
*/
|
||||
const UpdateFocusedSlice = useCallback((text: string, metaData?: any): void => {
|
||||
if (!focusedSliceId || !scriptEditUseCase) {
|
||||
return;
|
||||
}
|
||||
|
||||
const success = scriptEditUseCase.updateScriptSlice(
|
||||
focusedSliceId,
|
||||
text,
|
||||
metaData
|
||||
);
|
||||
|
||||
if (success) {
|
||||
// 更新本地片段列表
|
||||
const slices = scriptEditUseCase.getScriptSlices();
|
||||
setScriptSlices(slices);
|
||||
}
|
||||
}, [focusedSliceId, scriptEditUseCase]);
|
||||
|
||||
/**
|
||||
* 更新用户提示词
|
||||
* @param prompt 新的用户提示词
|
||||
@ -261,41 +159,16 @@ export const useScriptService = (): UseScriptService => {
|
||||
* 重置剧本内容到初始状态
|
||||
*/
|
||||
const resetScript = useCallback((): void => {
|
||||
if (initialScriptText) {
|
||||
// 重新调用AI生成剧本(fetchScriptData会自动清空状态)
|
||||
fetchScriptData(initialScriptText);
|
||||
if (initialScriptText && scriptEditUseCase) {
|
||||
// 重置剧本文本到初始状态
|
||||
setScriptText(initialScriptText);
|
||||
// 更新现有剧本编辑用例的数据
|
||||
scriptEditUseCase.updateScript(initialScriptText);
|
||||
// 从UseCase获取解析后的剧本片段
|
||||
const scriptSlices = scriptEditUseCase.getScriptSlices();
|
||||
setScriptSlices(scriptSlices);
|
||||
}
|
||||
}, [initialScriptText, fetchScriptData]);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* AI生成剧本
|
||||
* @param prompt 剧本提示词
|
||||
*/
|
||||
const generateScript = useCallback(async (prompt: string): Promise<void> => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
if (!scriptEditUseCase) {
|
||||
throw new Error("剧本编辑用例未初始化");
|
||||
}
|
||||
|
||||
await scriptEditUseCase.generateScript(prompt);
|
||||
|
||||
// 更新片段列表(这里需要根据实际的流式数据处理逻辑来调整)
|
||||
const slices = scriptEditUseCase.getScriptSlices();
|
||||
setScriptSlices(slices);
|
||||
|
||||
} catch (error) {
|
||||
console.error("AI生成剧本失败:", error);
|
||||
setError(error instanceof Error ? error.message : "AI生成剧本失败");
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [scriptEditUseCase]);
|
||||
}, [initialScriptText, scriptEditUseCase]);
|
||||
|
||||
/**
|
||||
* 应用剧本
|
||||
@ -303,44 +176,114 @@ export const useScriptService = (): UseScriptService => {
|
||||
const applyScript = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
if (!scriptEditUseCase) {
|
||||
throw new Error("剧本编辑用例未初始化");
|
||||
}
|
||||
|
||||
await scriptEditUseCase.applyScript();
|
||||
await scriptEditUseCase.applyScript(projectId);
|
||||
|
||||
} catch (error) {
|
||||
console.error("应用剧本失败:", error);
|
||||
setError(error instanceof Error ? error.message : "应用剧本失败");
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [scriptEditUseCase, projectId]);
|
||||
|
||||
/**
|
||||
* 中断剧本生成
|
||||
*/
|
||||
const abortGenerateScript = useCallback((): void => {
|
||||
if (scriptEditUseCase) {
|
||||
scriptEditUseCase.abortGenerateScript();
|
||||
setLoading(false);
|
||||
}
|
||||
}, [scriptEditUseCase]);
|
||||
|
||||
/**
|
||||
* 保存剧本
|
||||
*/
|
||||
const saveScript = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (!projectId) {
|
||||
throw new Error("项目ID未设置");
|
||||
}
|
||||
|
||||
if (!scriptEditUseCase) {
|
||||
throw new Error("剧本编辑用例未初始化");
|
||||
}
|
||||
|
||||
// 调用保存剧本接口
|
||||
const scriptText = scriptEditUseCase.toString();
|
||||
const response = await saveScriptAPI({ projectId, scriptText });
|
||||
|
||||
if (!response.successful) {
|
||||
throw new Error(response.message || '保存剧本失败');
|
||||
}
|
||||
|
||||
console.log("剧本保存成功");
|
||||
|
||||
} catch (error) {
|
||||
console.error("保存剧本失败:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [projectId, scriptEditUseCase]);
|
||||
|
||||
/**
|
||||
* 创建项目
|
||||
* @throws {Error} - 创建项目失败时抛出异常
|
||||
*/
|
||||
const createProject = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// 直接使用当前state中的userPrompt和scriptText
|
||||
const currentUserPrompt = userPrompt;
|
||||
const currentScriptContent = scriptText;
|
||||
|
||||
const response = await createProjectAPI({
|
||||
userPrompt: currentUserPrompt,
|
||||
scriptContent: currentScriptContent
|
||||
});
|
||||
|
||||
if (!response.successful) {
|
||||
throw new Error(response.message || '创建项目失败');
|
||||
}
|
||||
|
||||
const { projectId: newProjectId } = response.data;
|
||||
setProjectId(newProjectId);
|
||||
|
||||
console.log("项目创建成功");
|
||||
|
||||
} catch (error) {
|
||||
console.error("创建项目失败:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [userPrompt, scriptText]);
|
||||
|
||||
return {
|
||||
// 响应式状态
|
||||
scriptText,
|
||||
scriptSlices,
|
||||
focusedSliceId,
|
||||
focusedSlice,
|
||||
scriptSliceText,
|
||||
userPrompt,
|
||||
loading,
|
||||
error,
|
||||
projectId,
|
||||
|
||||
// 操作方法
|
||||
fetchScriptData,
|
||||
fetchProjectScript,
|
||||
setFocusedSlice,
|
||||
clearFocusedSlice,
|
||||
updateScriptSliceText,
|
||||
updateUserPrompt,
|
||||
resetScript,
|
||||
generateScript,
|
||||
applyScript,
|
||||
UpdateFocusedSlice
|
||||
abortGenerateScript,
|
||||
saveScript,
|
||||
createProject,
|
||||
};
|
||||
};
|
||||
|
||||
@ -3,9 +3,8 @@ import {
|
||||
ShotEntity,
|
||||
RoleEntity,
|
||||
SceneEntity,
|
||||
ContentItem,
|
||||
} from "../domain/Entities";
|
||||
import { ScriptSlice, ScriptValueObject } from "../domain/valueObject";
|
||||
import { ContentItem, ScriptSlice, ScriptValueObject } from "../domain/valueObject";
|
||||
import { ShotItem } from "../domain/Item";
|
||||
import { ShotEditUseCase } from "../usecase/ShotEditUsecase";
|
||||
import {
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
* 所有实体都应该实现这些基础接口
|
||||
*/
|
||||
|
||||
import { ContentItem, LensType } from "./valueObject";
|
||||
|
||||
/**
|
||||
* 基础实体接口
|
||||
*/
|
||||
@ -66,13 +68,7 @@ export interface SceneEntity extends BaseEntity {
|
||||
generateTextId: string;
|
||||
}
|
||||
|
||||
/**对话内容项 */
|
||||
export interface ContentItem {
|
||||
/** 角色ID */
|
||||
roleId: string;
|
||||
/** 对话内容 */
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**分镜进度 */
|
||||
export enum ShotStatus {
|
||||
/** 草稿加载中 */
|
||||
@ -82,6 +78,8 @@ export enum ShotStatus {
|
||||
/** 完成 */
|
||||
finished = 2,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分镜实体接口
|
||||
*/
|
||||
@ -101,7 +99,7 @@ export interface ShotEntity extends BaseEntity {
|
||||
/**对话内容 */
|
||||
content: ContentItem[];
|
||||
/**镜头项 */
|
||||
shot: string[];
|
||||
lens: LensType[];
|
||||
/**分镜剧本Id */
|
||||
scriptId: string;
|
||||
}
|
||||
|
||||
@ -21,7 +21,22 @@ export interface ScriptSlice {
|
||||
/** 元数据 */
|
||||
metaData: any;
|
||||
}
|
||||
|
||||
/**对话内容项 */
|
||||
export interface ContentItem {
|
||||
/** 角色ID */
|
||||
roleId: string;
|
||||
/** 对话内容 */
|
||||
content: string;
|
||||
}
|
||||
/**镜头值对象 */
|
||||
export interface LensType {
|
||||
/** 镜头名称 */
|
||||
name: string;
|
||||
/** 镜头描述 */
|
||||
content: string;
|
||||
/**运镜描述 */
|
||||
movement: string;
|
||||
}
|
||||
/**
|
||||
* @description: 剧本 值对象,将剧本文本转换为剧本对象
|
||||
* @return {*}
|
||||
|
||||
@ -7,7 +7,7 @@ describe('ScriptService 业务逻辑测试', () => {
|
||||
* 测试 generateScriptStream 流式接口,持续监听数据直到流结束
|
||||
*/
|
||||
const stream = await generateScriptStream({
|
||||
prompt: '一个年轻人在咖啡店里等待他的约会对象,心情紧张地摆弄着手机。'
|
||||
text: '一个年轻人在咖啡店里等待他的约会对象,心情紧张地摆弄着手机。'
|
||||
});
|
||||
|
||||
let allData = '';
|
||||
|
||||
@ -4,6 +4,7 @@ import { generateScriptStream, applyScriptToShot } from "@/api/video_flow";
|
||||
export class ScriptEditUseCase {
|
||||
loading: boolean = false;
|
||||
private scriptValueObject: ScriptValueObject;
|
||||
private abortController: AbortController | null = null;
|
||||
|
||||
constructor(script: string) {
|
||||
this.scriptValueObject = new ScriptValueObject(script);
|
||||
@ -12,15 +13,19 @@ export class ScriptEditUseCase {
|
||||
/**
|
||||
* @description: AI生成剧本方法
|
||||
* @param prompt 剧本提示词
|
||||
* @param stream_callback 流式数据回调函数
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async generateScript(prompt: string): Promise<void> {
|
||||
async generateScript(prompt: string, stream_callback?: (data: any) => void): Promise<void> {
|
||||
try {
|
||||
this.loading = true;
|
||||
|
||||
// 创建新的中断控制器
|
||||
this.abortController = new AbortController();
|
||||
|
||||
// 使用API接口生成剧本
|
||||
const response = await generateScriptStream({
|
||||
prompt,
|
||||
text: prompt,
|
||||
});
|
||||
|
||||
if (!response.successful) {
|
||||
@ -29,15 +34,37 @@ export class ScriptEditUseCase {
|
||||
|
||||
// 使用for await处理流式数据
|
||||
for await (const data of response.data) {
|
||||
// 检查是否被中断
|
||||
if (this.abortController.signal.aborted) {
|
||||
console.log("剧本生成被中断");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: 根据流式数据更新剧本片段
|
||||
// 这里需要根据实际的流式数据格式来处理
|
||||
// 可能需要将流式数据转换为ScriptSlice并添加到scriptValueObject中
|
||||
stream_callback?.(data);
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.abortController?.signal.aborted) {
|
||||
console.log("剧本生成被中断");
|
||||
return;
|
||||
}
|
||||
console.error("AI生成剧本出错:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.abortController = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 中断剧本生成
|
||||
*/
|
||||
abortGenerateScript(): void {
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,13 +72,14 @@ export class ScriptEditUseCase {
|
||||
* @description: 应用剧本方法
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async applyScript(): Promise<void> {
|
||||
async applyScript(projectId: string): Promise<void> {
|
||||
try {
|
||||
this.loading = true;
|
||||
|
||||
// 调用应用剧本接口
|
||||
const response = await applyScriptToShot({
|
||||
script: this.scriptValueObject.toString(),
|
||||
projectId,
|
||||
scriptText: this.scriptValueObject.toString(),
|
||||
});
|
||||
|
||||
if (!response.successful) {
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"maxNodeModuleJsDepth":0
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@ -2,10 +2,37 @@ import { ScriptSlice, ScriptSliceType } from "@/app/service/domain/valueObject";
|
||||
|
||||
export function parseScriptEntity(text: string):ScriptSlice {
|
||||
const scriptSlice:ScriptSlice={
|
||||
type:ScriptSliceType.text,
|
||||
text:text,
|
||||
metaData:{}
|
||||
// 生成唯一ID,单次使用即可
|
||||
id: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`,
|
||||
type: ScriptSliceType.text,
|
||||
text: text,
|
||||
metaData: {}
|
||||
|
||||
}
|
||||
return scriptSlice;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 节流函数,限制函数在指定时间间隔内只执行一次
|
||||
* @param {Function} func - 需要被节流的函数
|
||||
* @param {number} delay - 节流时间间隔(毫秒)
|
||||
* @returns {Function} - 节流后的新函数
|
||||
* @throws {Error} - 如果参数类型不正确
|
||||
* @example
|
||||
* const throttledFn = throttle(() => { console.log('触发'); }, 1000);
|
||||
* window.addEventListener('resize', throttledFn);
|
||||
*/
|
||||
export function throttle<T extends (...args: any[]) => any>(func: T, delay: number=100): (...args: Parameters<T>) => void {
|
||||
if (typeof delay !== 'number' || delay < 0) {
|
||||
throw new Error('throttle: 第二个参数必须是非负数');
|
||||
}
|
||||
let lastCall = 0;
|
||||
return (...args: Parameters<T>) => {
|
||||
const now = Date.now();
|
||||
if (now - lastCall >= delay) {
|
||||
lastCall = now;
|
||||
func(...args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user