import { TaskModal } from "@/model/task"; import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; import { ValidateJson } from "@/define/Tools/validate"; import { BookTaskDetail } from "@/model/book/bookTaskDetail"; import { GetImageBase64 } from "@/define/Tools/image"; import { cloneDeep, isEmpty } from "lodash"; import { GetBaseUrl } from "@/define/Tools/common"; import axios from "axios"; import { Book } from "@/model/book/book"; import { VideoStatus } from "@/define/enum/video"; import { SendMessageToRenderer } from "../globalService"; import { ResponseMessageType } from "@/define/enum/softwareEnum"; import { BookBackTaskStatus, BookTaskStatus } from "@/define/enum/bookEnum"; import { define } from "@/define/define" import ForwardResponse from "@/define/response/ForwardResponse"; export class KlingService { bookServiceBasic: BookServiceBasic constructor() { this.bookServiceBasic = new BookServiceBasic(); } /** * 可灵图转视频 * @param task 任务 * @param gptUrl GPT地址 * @param gptApiKey GPTAPIKey */ async KlingImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise { try { let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId); let klingOptionsString = bookTaskDetail.videoMessage.klingOptions; if (!ValidateJson(klingOptionsString)) { throw new Error("klingOptions参数错误,请检查"); } let klingOptions: BookTaskDetail.klingOptions = JSON.parse(klingOptionsString); // console.log("klingOptions", klingOptions, "gptUrl", gptUrl, "gptApiKey", gptApiKey); let data: BookTaskDetail.klingOptions = { image: klingOptions.image, model: "kling-v1" } if (!data.image.startsWith("http")) { data.image = await GetImageBase64(data.image); } // 尾帧控制 if (klingOptions.hasOwnProperty("style") && !isEmpty(klingOptions.image_tail)) { if (klingOptions.image_tail.startsWith("http")) { data.image_tail = klingOptions.image_tail; } else { data.image_tail = await GetImageBase64(klingOptions.image_tail); } } if (klingOptions.hasOwnProperty("prompt") && !isEmpty(klingOptions.prompt)) { data.prompt = klingOptions.prompt; } if (klingOptions.hasOwnProperty("negative_prompt") && !isEmpty(klingOptions.negative_prompt)) { data.negative_prompt = klingOptions.negative_prompt; } if (klingOptions.hasOwnProperty("cfg_scale")) { data.cfg_scale = klingOptions.cfg_scale; } if (klingOptions.hasOwnProperty("mode")) { data.mode = klingOptions.mode; } if (klingOptions.hasOwnProperty("duration")) { data.duration = klingOptions.duration; } if (klingOptions.hasOwnProperty("callback_url")) { data.callback_url = klingOptions.callback_url; } // 开始请求 let baseUrl = GetBaseUrl(gptUrl); let url = baseUrl + "/kling/v1/videos/image2video"; let resData: any = undefined; if (useTransfer) { let transferUrl = define.lms + "/lms/Forward/SimpleTransfer"; let transferConfig = { method: 'post', url: transferUrl, maxBodyLength: Infinity, timeout: 600000, // 600 seconds timeout headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ url: url, apiKey: gptApiKey, dataString: JSON.stringify(data) }) } let response = await axios(transferConfig) resData = ForwardResponse.GetForwardResponseData(response) } else { let res = await axios.post(url, data, { headers: { "Authorization": gptApiKey } }); // console.log("kling合成视频结果", res); resData = res.data; } let id = resData.data.task_id; // 修改数据 let videoMessage = cloneDeep(bookTaskDetail.videoMessage); videoMessage.taskId = id; videoMessage.status = VideoStatus.WAIT; videoMessage.messageData = JSON.stringify(resData); delete videoMessage.id; await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(task.bookTaskDetailId, videoMessage); // 返回前端数据 SendMessageToRenderer({ code: 1, id: task.bookTaskDetailId, message: "可灵合成任务提交成功", type: ResponseMessageType.KLING_VIDEO, data: JSON.stringify(resData) }, task.messageName); await this.FetchKlingVideoResult(bookTaskDetail, task, resData.data.task_id, baseUrl, gptApiKey, useTransfer); } catch (error) { throw new Error("可灵合成视频失败,失败信息如下:" + error.toString()); } } async FetchKlingVideoResult(bookTaskDetail: Book.SelectBookTaskDetail, task: TaskModal.Task, taskId: string, baseUrl: string, gptApiKey: string, useTransfer: boolean = false) { while (true) { try { let url = baseUrl + "/kling/v1/videos/image2video/" + taskId; let resData: any = undefined; if (useTransfer) { let transferUrl = define.lms + "/lms/Forward/GetTransfer"; let transferConfig = { method: 'post', url: transferUrl, maxBodyLength: Infinity, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ url: url, apiKey: gptApiKey, }) } let res = await axios.request(transferConfig); resData = ForwardResponse.GetForwardResponseData(res) } else { let res = await axios.get(url, { headers: { Authorization: gptApiKey } }); resData = res.data; } if (!resData) { throw new Error("获取可灵合成视频结果失败") } if (resData.data.task_status == "submitted") { SendMessageToRenderer({ code: 1, id: bookTaskDetail.id, message: "可灵合成任务提交成功,正在合成中", type: ResponseMessageType.KLING_VIDEO, data: JSON.stringify(resData.data) }, task.messageName); } else if (resData.data.task_status == "processing") { let videoMessage = cloneDeep(bookTaskDetail.videoMessage); delete videoMessage.id; videoMessage.status = VideoStatus.PROCESSING; videoMessage.taskId = taskId; videoMessage.messageData = JSON.stringify(resData.data); await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(task.bookTaskDetailId, videoMessage); SendMessageToRenderer({ code: 1, id: bookTaskDetail.id, message: "可灵合成任务正在合成中", type: ResponseMessageType.KLING_VIDEO, data: JSON.stringify(resData.data) }, task.messageName); } else if (resData.data.task_status == "succeed") { // 完成 let videoMessage = cloneDeep(bookTaskDetail.videoMessage); delete videoMessage.id; videoMessage.status = VideoStatus.SUCCESS; videoMessage.taskId = taskId; videoMessage.videoUrl = resData.data.task_result.videos[0].url; videoMessage.messageData = JSON.stringify(resData.data); await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(task.bookTaskDetailId, videoMessage); await this.bookServiceBasic.UpdetedBookTaskData(task.bookTaskId, { status: BookTaskStatus.IMAGE_TO_VIDEO_SUCCESS, }) await this.bookServiceBasic.UpdateTaskStatus({ id: task.id, status: BookBackTaskStatus.DONE, }) SendMessageToRenderer({ code: 1, id: bookTaskDetail.id, message: "可灵合成视频完成", type: ResponseMessageType.KLING_VIDEO, data: JSON.stringify(resData.data.data) }, task.messageName); break; } else { // 失败 let videoMessage = cloneDeep(bookTaskDetail.videoMessage); delete videoMessage.id; videoMessage.status = VideoStatus.FAIL; videoMessage.msg = resData.message; videoMessage.taskId = taskId; videoMessage.messageData = JSON.stringify(resData.data); await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(bookTaskDetail.id, videoMessage); SendMessageToRenderer({ code: 0, id: bookTaskDetail.id, message: "runway合成视频失败,错误信息如下:" + resData.data.message, type: ResponseMessageType.KLING_VIDEO, data: JSON.stringify(resData.data) }, task.messageName); throw new Error("可灵合成视频失败,失败信息如下:" + resData.message); } // 等待20秒后再次请求 await new Promise(resolve => setTimeout(resolve, 20000)); } catch (error) { throw error; } } } }