2024-11-19 20:28:31 +08:00
|
|
|
|
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";
|
2025-02-24 17:25:00 +08:00
|
|
|
|
import { define } from "@/define/define"
|
|
|
|
|
|
import ForwardResponse from "@/define/response/ForwardResponse";
|
2024-11-19 20:28:31 +08:00
|
|
|
|
|
|
|
|
|
|
export class KlingService {
|
|
|
|
|
|
bookServiceBasic: BookServiceBasic
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.bookServiceBasic = new BookServiceBasic();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 可灵图转视频
|
|
|
|
|
|
* @param task 任务
|
|
|
|
|
|
* @param gptUrl GPT地址
|
|
|
|
|
|
* @param gptApiKey GPTAPIKey
|
|
|
|
|
|
*/
|
2025-02-24 17:25:00 +08:00
|
|
|
|
async KlingImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise<void> {
|
2024-11-19 20:28:31 +08:00
|
|
|
|
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";
|
2025-02-24 17:25:00 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
})
|
2024-11-19 20:28:31 +08:00
|
|
|
|
}
|
2025-02-24 17:25:00 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-11-19 20:28:31 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2025-02-24 17:25:00 +08:00
|
|
|
|
await this.FetchKlingVideoResult(bookTaskDetail, task, resData.data.task_id, baseUrl, gptApiKey, useTransfer);
|
2024-11-19 20:28:31 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
throw new Error("可灵合成视频失败,失败信息如下:" + error.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-08-09 18:46:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-02-24 17:25:00 +08:00
|
|
|
|
async FetchKlingVideoResult(bookTaskDetail: Book.SelectBookTaskDetail, task: TaskModal.Task, taskId: string, baseUrl: string, gptApiKey: string, useTransfer: boolean = false) {
|
2024-11-19 20:28:31 +08:00
|
|
|
|
while (true) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let url = baseUrl + "/kling/v1/videos/image2video/" + taskId;
|
2025-02-24 17:25:00 +08:00
|
|
|
|
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,
|
|
|
|
|
|
})
|
2024-11-19 20:28:31 +08:00
|
|
|
|
}
|
2025-02-24 17:25:00 +08:00
|
|
|
|
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") {
|
2024-11-19 20:28:31 +08:00
|
|
|
|
SendMessageToRenderer({
|
|
|
|
|
|
code: 1,
|
|
|
|
|
|
id: bookTaskDetail.id,
|
|
|
|
|
|
message: "可灵合成任务提交成功,正在合成中",
|
|
|
|
|
|
type: ResponseMessageType.KLING_VIDEO,
|
2025-02-24 17:25:00 +08:00
|
|
|
|
data: JSON.stringify(resData.data)
|
2024-11-19 20:28:31 +08:00
|
|
|
|
}, task.messageName);
|
2025-02-24 17:25:00 +08:00
|
|
|
|
} else if (resData.data.task_status == "processing") {
|
2024-11-19 20:28:31 +08:00
|
|
|
|
|
|
|
|
|
|
let videoMessage = cloneDeep(bookTaskDetail.videoMessage);
|
|
|
|
|
|
delete videoMessage.id;
|
|
|
|
|
|
videoMessage.status = VideoStatus.PROCESSING;
|
|
|
|
|
|
videoMessage.taskId = taskId;
|
2025-02-24 17:25:00 +08:00
|
|
|
|
videoMessage.messageData = JSON.stringify(resData.data);
|
2024-11-19 20:28:31 +08:00
|
|
|
|
await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(task.bookTaskDetailId, videoMessage);
|
|
|
|
|
|
|
|
|
|
|
|
SendMessageToRenderer({
|
|
|
|
|
|
code: 1,
|
|
|
|
|
|
id: bookTaskDetail.id,
|
|
|
|
|
|
message: "可灵合成任务正在合成中",
|
|
|
|
|
|
type: ResponseMessageType.KLING_VIDEO,
|
2025-02-24 17:25:00 +08:00
|
|
|
|
data: JSON.stringify(resData.data)
|
2024-11-19 20:28:31 +08:00
|
|
|
|
}, task.messageName);
|
2025-02-24 17:25:00 +08:00
|
|
|
|
} else if (resData.data.task_status == "succeed") {
|
2024-11-19 20:28:31 +08:00
|
|
|
|
// 完成
|
|
|
|
|
|
let videoMessage = cloneDeep(bookTaskDetail.videoMessage);
|
|
|
|
|
|
delete videoMessage.id;
|
|
|
|
|
|
videoMessage.status = VideoStatus.SUCCESS;
|
|
|
|
|
|
videoMessage.taskId = taskId;
|
2025-02-24 17:25:00 +08:00
|
|
|
|
videoMessage.videoUrl = resData.data.task_result.videos[0].url;
|
|
|
|
|
|
videoMessage.messageData = JSON.stringify(resData.data);
|
2024-11-19 20:28:31 +08:00
|
|
|
|
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,
|
2025-02-24 17:25:00 +08:00
|
|
|
|
data: JSON.stringify(resData.data.data)
|
2024-11-19 20:28:31 +08:00
|
|
|
|
}, task.messageName);
|
|
|
|
|
|
break;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 失败
|
|
|
|
|
|
let videoMessage = cloneDeep(bookTaskDetail.videoMessage);
|
|
|
|
|
|
delete videoMessage.id;
|
|
|
|
|
|
videoMessage.status = VideoStatus.FAIL;
|
2025-02-24 17:25:00 +08:00
|
|
|
|
videoMessage.msg = resData.message;
|
2024-11-19 20:28:31 +08:00
|
|
|
|
videoMessage.taskId = taskId;
|
2025-02-24 17:25:00 +08:00
|
|
|
|
videoMessage.messageData = JSON.stringify(resData.data);
|
2024-11-19 20:28:31 +08:00
|
|
|
|
await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(bookTaskDetail.id, videoMessage);
|
|
|
|
|
|
SendMessageToRenderer({
|
|
|
|
|
|
code: 0,
|
|
|
|
|
|
id: bookTaskDetail.id,
|
2025-02-24 17:25:00 +08:00
|
|
|
|
message: "runway合成视频失败,错误信息如下:" + resData.data.message,
|
2024-11-19 20:28:31 +08:00
|
|
|
|
type: ResponseMessageType.KLING_VIDEO,
|
2025-02-24 17:25:00 +08:00
|
|
|
|
data: JSON.stringify(resData.data)
|
2024-11-19 20:28:31 +08:00
|
|
|
|
}, task.messageName);
|
2025-02-24 17:25:00 +08:00
|
|
|
|
throw new Error("可灵合成视频失败,失败信息如下:" + resData.message);
|
2024-11-19 20:28:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 等待20秒后再次请求
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 20000));
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|