From 03d3ff7e9d1a4900653d19e46494819b4e0a4603 Mon Sep 17 00:00:00 2001 From: lq1405 <2769838458@qq.com> Date: Mon, 24 Feb 2025 17:25:00 +0800 Subject: [PATCH] =?UTF-8?q?V=203.2.4=EF=BC=882025.02.24=EF=BC=89=201.=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9EMJ=EF=BC=88API=EF=BC=8C=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=EF=BC=89=E5=9B=BD=E5=86=85=E8=BD=AC=E5=8F=91?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=88=E4=B8=8D=E5=8C=85=E6=8B=AC=E5=8F=8D?= =?UTF-8?q?=E6=8E=A8=EF=BC=89=EF=BC=8C=E8=A7=A3=E5=86=B3=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=B8=8D=E8=83=BD=E8=AE=BF=E9=97=AE=E5=A4=96?= =?UTF-8?q?=E9=83=A8=E7=BD=91=E7=BB=9C=202.=20=E6=96=B0=E5=A2=9E=20FLUX-AP?= =?UTF-8?q?I=20=E5=9B=BD=E5=86=85=E8=BD=AC=E5=8F=91=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E8=A7=A3=E5=86=B3=E9=83=A8=E5=88=86=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E8=AE=BF=E9=97=AE=E5=A4=96=E9=83=A8=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=EF=BC=88=E5=8F=AF=E8=83=BD=E5=87=BA=E7=8E=B0=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E6=8A=A5=E9=94=99=EF=BC=89=203.=20=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E8=AE=BE=E7=BD=AEFLUX=E6=A8=A1=E5=9E=8B=EF=BC=8C=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E5=90=8E=E5=8F=B0=E7=AE=A1=E7=90=86=204.=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=9B=BE=E8=BD=AC=E8=A7=86=E9=A2=91=E5=9B=BD=E5=86=85?= =?UTF-8?q?=EF=BC=88Kling,Luma,Runway=EF=BC=89=E8=BD=AC=E5=8F=91=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E8=A7=A3=E5=86=B3=E9=83=A8=E5=88=86=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=B8=8D=E8=83=BD=E8=AE=BF=E9=97=AE=E5=A4=96=E9=83=A8?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=205.=20=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20Luma=20=E6=85=A2=E9=80=9F=E6=A8=A1=E5=9E=8B=206.=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96MJ=E8=AE=BE=E7=BD=AE=E5=8F=8AMJ=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=EF=BC=8C=E5=87=8F=E5=B0=91=E5=87=BA=E9=94=99=207.=20MJ?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E6=A8=A1=E5=BC=8F=E5=8F=96=E6=B6=88=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=94=9F=E5=9B=BE=E9=80=9F=E5=BA=A6=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=20=E6=B3=A8=E6=84=8F=EF=BC=9A=E6=9C=AC=E6=AC=A1=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=9C=80=E8=A6=81=E9=87=8D=E6=96=B0=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?MJ=E8=AE=BE=E7=BD=AE=EF=BC=88=E4=BB=A3=E7=90=86=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E4=B8=8D=E7=94=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 2 +- package.json | 9 +- resources/scripts/db/book.realm.lock | Bin 1416 -> 1416 bytes resources/scripts/db/software.realm | Bin 458752 -> 917504 bytes resources/scripts/db/software.realm.lock | Bin 1416 -> 1416 bytes .../api/{apiUrlDefine.js => apiUrlDefine.ts} | 17 +- src/define/define.js | 287 +++++----- src/define/enum/image.ts | 11 - src/define/enum/mjEnum.ts | 4 +- src/define/enum/option.ts | 20 + src/define/response/ForwardResponse.ts | 25 + src/main/IPCEvent/mjIpc.js | 1 - src/main/IPCEvent/optionsIpc.ts | 2 +- src/main/Public/GPT.js | 1 - src/main/Service/Flux/flux.ts | 65 ++- src/main/Service/MJ/mj.ts | 29 +- src/main/Service/MJ/mjApi.ts | 220 ++++++-- src/main/Service/MJ/mjDefine.ts | 169 ++++++ src/main/Service/Options/index.ts | 2 +- src/main/Service/Options/optionServices.ts | 21 +- .../ServiceBasic/softwareServiceBasic.ts | 4 +- src/main/Service/task/taskManage.ts | 10 +- src/main/Service/video/kling.ts | 110 ++-- src/main/Service/video/luma.ts | 132 ++++- src/main/Service/video/runway.ts | 119 ++-- src/main/Service/video/videoGlobal.ts | 10 +- src/main/func.js | 7 +- src/main/setting/setting.js | 5 +- src/model/Setting/mjSetting.d.ts | 96 ++-- src/model/book/bookTaskDetail.d.ts | 1 + src/model/option/option.d.ts | 1 + src/preload/options.ts | 2 +- src/renderer/src/common/SDFluxCommon.ts | 30 + src/renderer/src/common/initCommon.ts | 49 +- .../Components/Video/GenerateVideoDialog.vue | 2 + .../Book/Components/Video/LumaOption.vue | 12 +- .../src/components/Book/MJReverse/Setting.vue | 6 +- .../src/components/Setting/MJSetting.vue | 522 ------------------ .../Setting/MJSetting/MJAPISetting.vue | 95 ++++ .../Setting/MJSetting/MJBrowserSetting.vue | 70 +++ .../Setting/MJSetting/MJRemoteSetting.vue | 48 ++ .../Setting/MJSetting/MJSettingHome.vue | 147 +++++ .../Setting/MJSetting/MJSimpleSetting.vue | 141 +++++ .../src/components/Setting/SDSetting.vue | 385 +++++++------ src/renderer/src/components/TTS/EdgeTTS.vue | 2 +- src/renderer/src/components/TTS/TTSHome.vue | 2 +- src/renderer/src/main.js | 2 +- src/stores/option.ts | 42 +- src/stores/setting.ts | 8 +- 49 files changed, 1823 insertions(+), 1122 deletions(-) rename src/define/api/{apiUrlDefine.js => apiUrlDefine.ts} (75%) create mode 100644 src/define/response/ForwardResponse.ts create mode 100644 src/main/Service/MJ/mjDefine.ts create mode 100644 src/renderer/src/common/SDFluxCommon.ts delete mode 100644 src/renderer/src/components/Setting/MJSetting.vue create mode 100644 src/renderer/src/components/Setting/MJSetting/MJAPISetting.vue create mode 100644 src/renderer/src/components/Setting/MJSetting/MJBrowserSetting.vue create mode 100644 src/renderer/src/components/Setting/MJSetting/MJRemoteSetting.vue create mode 100644 src/renderer/src/components/Setting/MJSetting/MJSettingHome.vue create mode 100644 src/renderer/src/components/Setting/MJSetting/MJSimpleSetting.vue diff --git a/package-lock.json b/package-lock.json index 52d311f..c6a2ea1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "laitool", - "version": "3.2.3", + "version": "3.2.4", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index ce4ddcd..02c8e6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "laitool", - "version": "3.2.3", + "version": "3.2.4", "description": "An AI tool for image processing, video processing, and other functions.", "main": "./out/main/index.js", "author": "laitool.cn", @@ -92,8 +92,13 @@ "resources/tmp/**", "resources/icon.ico" ], + "nsis": { + "oneClick": false, + "allowToChangeInstallationDirectory": true + }, "win": { + "target": "nsis", "icon": "./resources/icon.ico" } } -} +} \ No newline at end of file diff --git a/resources/scripts/db/book.realm.lock b/resources/scripts/db/book.realm.lock index 6b069831563c40cfe7b05e6fd2aff514fb6a0a0d..8d081895be7b2ea677beab57f50eac7a99865c4f 100644 GIT binary patch literal 1416 zcmZQ%(^b diff --git a/resources/scripts/db/software.realm b/resources/scripts/db/software.realm index 38e7fb124b12399973ea1672a7baea1a78bc2350..205f13d596215789069b5dad9a43a622c38114d4 100644 GIT binary patch delta 66523 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 delta 65760 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 diff --git a/resources/scripts/db/software.realm.lock b/resources/scripts/db/software.realm.lock index 837939065b014ce2081ccdb777aa1f9a581296d0..109d2d934d9b5e8e19ab0bb0a83150dce0b2a178 100644 GIT binary patch literal 1416 zcmZQ% callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)), ipcMain.handle( DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY, async (event) => await mjSimple.GetMJGenerateCategory() diff --git a/src/main/IPCEvent/optionsIpc.ts b/src/main/IPCEvent/optionsIpc.ts index 944fca7..83ae84e 100644 --- a/src/main/IPCEvent/optionsIpc.ts +++ b/src/main/IPCEvent/optionsIpc.ts @@ -10,7 +10,7 @@ function OptionsIpc() { */ ipcMain.handle( DEFINE_STRING.OPTIONS.GET_OPTION_BY_KEY, - async (_, key: string) => await OptionHandle.GetOptionByKey(key) + async (_, key: string | string[]) => await OptionHandle.GetOptionByKey(key) ) /** diff --git a/src/main/Public/GPT.js b/src/main/Public/GPT.js index 812c2ed..7d9733f 100644 --- a/src/main/Public/GPT.js +++ b/src/main/Public/GPT.js @@ -4,7 +4,6 @@ import { DEFINE_STRING } from '../../define/define_string' import { define } from '../../define/define' let fspromises = require('fs').promises import { gptDefine } from '../../define/gptDefine' -import { apiUrl } from '../../define/api/apiUrlDefine' import { successMessage } from '../Public/generalTools' import { RetryWithBackoff } from '../../define/Tools/common' diff --git a/src/main/Service/Flux/flux.ts b/src/main/Service/Flux/flux.ts index 744fd66..84f759e 100644 --- a/src/main/Service/Flux/flux.ts +++ b/src/main/Service/Flux/flux.ts @@ -11,6 +11,8 @@ import { Base64ToFile, GetImageBase64 } from '../../../define/Tools/image'; import { BookBackTaskStatus } from '../../../define/enum/bookEnum'; import { MJAction, MJImageType } from '../../../define/enum/mjEnum'; import { GptService } from '../GPT/gpt'; +import { TaskModal } from '@/model/task'; +import { ValidateJson } from '@/define/Tools/validate'; export class FluxOpt { gptService: GptService @@ -203,18 +205,55 @@ export class FluxOpt { * @param body 请求的请求体 * @returns */ - async FluxAPIImageRequest(url: string, key: string, body: { model: string; prompt: string; size: string; }): Promise { - let response = await axios.post(url, { ...body, n: 1 }, { - headers: { - Authorization: 'Bearer ' + key - } - }) + async FluxAPIImageRequest(url: string, key: string, body: { model: string; prompt: string; size: string; }, useTransfer: boolean): Promise { - if (response.data && response.data.data && response.data.data.length > 0) { - return response.data.data[0].url - } else { - return undefined + let data = { + ...body, + n: 1 } + + let resData: any = []; + + 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: key, + dataString: JSON.stringify(data) + }) + } + let response = await axios(transferConfig) + if (response.status != 200) { + throw new Error("转发请求失败") + } + if (response.data.code != 1) { + throw new Error(response.data.message) + } + if (!ValidateJson(response.data.data)) { + throw new Error(response.data.data) + } + let re = JSON.parse(response.data.data); + resData = re.data + } else { + let response = await axios.post(url, data, { + headers: { + Authorization: 'Bearer ' + key + } + }) + resData = response.data.data + } + if (!Array.isArray(resData) || resData.length == 0) { + throw new Error("FLUX API 返回数据异常") + } + return resData[0].url } /** @@ -239,7 +278,11 @@ export class FluxOpt { prompt = sdSetting.webui.prompt + ', ' + prompt } let size = `${sdSetting.webui.width}x${sdSetting.webui.height}` + if (!sdSetting.flux.model) { + throw new Error('FLUX API 模型为空,请先设置!') + } let model = sdSetting.flux.model + let useTransfer = sdSetting.flux.useTransfer // 一次请求生成一张 多个请求 let SdOriginalImage = path.join(book.bookFolderPath, 'data/SdOriginalImage'); @@ -259,7 +302,7 @@ export class FluxOpt { model: model, prompt: prompt, size: size - }) + }, useTransfer) // 这边开始处理返回的数据 if (isEmpty(imageUrl)) { throw new Error('FLUX 生图返回的图片地址为空') diff --git a/src/main/Service/MJ/mj.ts b/src/main/Service/MJ/mj.ts index bdcde2e..15e501d 100644 --- a/src/main/Service/MJ/mj.ts +++ b/src/main/Service/MJ/mj.ts @@ -9,7 +9,7 @@ import { BookBackTaskStatus, BookBackTaskType, BookTaskStatus, BookType, DialogT import { DEFINE_STRING } from "../../../define/define_string"; import { MJ } from "../../../model/mj"; import { MJRespoonseType } from "../../../define/enum/mjEnum"; -import { MJSetting } from "../../../model/Setting/mjSetting"; +import { MJSettingModel } from "../../../model/Setting/mjSetting"; import { GeneralResponse } from "../../../model/generalResponse" import { LoggerStatus, ResponseMessageType } from "../../../define/enum/softwareEnum"; import { ImageStyle } from "../Book/imageStyle"; @@ -27,7 +27,7 @@ const fspromise = fs.promises export class MJOpt { mjApi: MJApi; - mjSetting: MJSetting.MjSetting + mjSimpleSetting: MJSettingModel.MjSimpleSettingModel; imageStyle: ImageStyle; logScheduler: LogScheduler; tools: Tools; @@ -48,8 +48,8 @@ export class MJOpt { * 获取MJ设置 */ async GetMJSetting() { - if (!this.mjSetting) { - this.mjSetting = await this.softWareServiceBasic.GetMjSetting() + if (!this.mjSimpleSetting) { + this.mjSimpleSetting = await this.softWareServiceBasic.GetMjSetting() } } @@ -231,7 +231,7 @@ export class MJOpt { code: 1, type: MJRespoonseType.UPDATED, mjType: MJAction.DESCRIBE, - category: this.mjSetting.type, + category: this.mjSimpleSetting.type, messageId: reqRes, id: task.bookTaskDetailId, progress: 0, @@ -258,7 +258,7 @@ export class MJOpt { code: 0, type: MJRespoonseType.UPDATED, mjType: MJAction.DESCRIBE, - category: this.mjSetting.type, + category: this.mjSimpleSetting.type, messageId: undefined, id: task.bookTaskDetailId, progress: 0, @@ -560,7 +560,7 @@ export class MJOpt { await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { mjApiUrl: this.mjApi.imagineUrl, progress: 100, - category: this.mjApi.mjSetting.type, + category: this.mjApi.mjSimpleSetting.type, imageClick: task_res.imageClick, imageShow: task_res.imageShow, messageId: task_res.messageId, @@ -622,7 +622,7 @@ export class MJOpt { await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { mjApiUrl: this.mjApi.imagineUrl, progress: 100, - category: this.mjApi.mjSetting.type, + category: this.mjApi.mjSimpleSetting.type, imageClick: task_res.imageClick, imageShow: task_res.imageShow, messageId: task_res.messageId, @@ -644,7 +644,7 @@ export class MJOpt { await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { mjApiUrl: this.mjApi.imagineUrl, progress: task_res.progress, - category: this.mjApi.mjSetting.type, + category: this.mjApi.mjSimpleSetting.type, imageClick: task_res.imageClick, imageShow: task_res.imageShow, messageId: task_res.messageId, @@ -697,7 +697,6 @@ export class MJOpt { // 这个就是任务ID let reqRes = await this.mjApi.SubmitMJImagine(task.id, prompt) if (reqRes == '23') { - console.log(task.id, "33333") // 任务队列过多,重新提交排队 await this.bookServiceBasic.UpdateTaskStatus({ id: task.id, @@ -711,7 +710,7 @@ export class MJOpt { code: 1, type: MJRespoonseType.UPDATED, mjType: MJAction.IMAGINE, - category: this.mjSetting.type, + category: this.mjSimpleSetting.type, message_id: '', id: task.bookTaskDetailId, progress: 0, @@ -721,7 +720,7 @@ export class MJOpt { await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { mjApiUrl: this.mjApi.imagineUrl, progress: 0, - category: this.mjApi.mjSetting.type, + category: this.mjApi.mjSimpleSetting.type, imageClick: "", imageShow: "", messageId: "", @@ -747,7 +746,7 @@ export class MJOpt { code: 1, type: MJRespoonseType.UPDATED, mjType: MJAction.IMAGINE, - category: this.mjSetting.type, + category: this.mjSimpleSetting.type, message_id: reqRes, id: task.bookTaskDetailId, progress: 0, @@ -773,7 +772,7 @@ export class MJOpt { code: 0, type: MJRespoonseType.UPDATED, mjType: MJAction.IMAGINE, - category: this.mjSetting.type, + category: this.mjSimpleSetting.type, messageId: undefined, id: task.bookTaskDetailId, progress: 0, @@ -784,7 +783,7 @@ export class MJOpt { await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, { mjApiUrl: this.mjApi.imagineUrl, progress: 0, - category: this.mjApi.mjSetting.type, + category: this.mjApi.mjSimpleSetting.type, imageClick: "", imageShow: "", messageId: "", diff --git a/src/main/Service/MJ/mjApi.ts b/src/main/Service/MJ/mjApi.ts index 81da1c9..0874030 100644 --- a/src/main/Service/MJ/mjApi.ts +++ b/src/main/Service/MJ/mjApi.ts @@ -1,65 +1,83 @@ import axios from "axios" import { define } from "../../../define/define" -import { GetImageBase64 } from "../../../define/Tools/image" -import { MJImageType, MJRespoonseType, MJSpeed } from "../../../define/enum/mjEnum" +import { MJImageType, MJRespoonseType, MJRobotType, MJSpeed } from "../../../define/enum/mjEnum" import { MJSettingService } from "../../../define/db/service/SoftWare/mjSettingService" import { BookBackTaskListService } from "../../../define/db/service/Book/bookBackTaskListService" import { BookBackTaskStatus } from "../../../define/enum/bookEnum" -import { MJSetting } from "../../../model/Setting/mjSetting" -import { GPT } from "../../Public/GPT" +import { MJSettingModel } from "../../../model/Setting/mjSetting" import { MJ } from "../../../model/mj" -import { LaiAPIType } from "../../../define/enum/softwareEnum" import { isEmpty } from "lodash" +import { OptionServices } from "../Options/optionServices" +import { OptionKeyName } from "@/define/enum/option" +import { ValidateJson } from "@/define/Tools/validate" +import { apiUrl } from "@/define/api/apiUrlDefine" /** * 调用MJ的API类 */ class MJApi { - mjSetting: MJSetting.MjSetting + mjSimpleSetting: MJSettingModel.MjSimpleSettingModel bootType: string imagineUrl: string fetchTaskUrl: string describeUrl: string + optionServices: OptionServices + + mj_globalSetting: MJSettingModel.MJ_GlobalSettingModel + constructor() { this.bootType = "MID_JOURNEY" + this.optionServices = new OptionServices() } - async InitMJSetting(): Promise { + /** + * 初始化MJ设置 + */ + async InitMJSetting(): Promise { // 获取MJ配置,从数据库中 let _mjSettingService = await MJSettingService.getInstance() let mjSettings = _mjSettingService.GetMJSettingTreeData() if (mjSettings.code == 0) { throw new Error(mjSettings.message) } - this.mjSetting = mjSettings.data - this.bootType = this.mjSetting.selectRobot == "niji" ? "NIJI_JOURNEY" : "MID_JOURNEY" - if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + + + let mjSettingData = await this.optionServices.GetOptionByKey(OptionKeyName.MJ_GlobalSetting); + if (mjSettingData.code == 0) { + throw new Error("加载MJ设置失败,失败原因如下:" + mjSettingData.message) + } + if (mjSettingData.data == null) { + throw new Error("加载MJ设置失败,失败原因如下:没有找到对应的MJ设置,请先去MJ设置中设置") + } + if (!ValidateJson(mjSettingData.data.value)) { + throw new Error("MJ设置的数据格式不正确,请检查数据格式") + } + + this.mj_globalSetting = JSON.parse(mjSettingData.data.value) as MJSettingModel.MJ_GlobalSettingModel + this.mjSimpleSetting = this.mj_globalSetting.mj_simpleSetting + + this.bootType = this.mjSimpleSetting.selectRobot == MJRobotType.NIJI ? "NIJI_JOURNEY" : "MID_JOURNEY" + if (this.mjSimpleSetting.type == MJImageType.REMOTE_MJ) { this.imagineUrl = define.remotemj_api + 'mj/submit/imagine' this.describeUrl = define.remotemj_api + 'mj/submit/describe' this.fetchTaskUrl = define.remotemj_api + 'mj/task/${id}/fetch' } else { - if (global.config.laiApiSelect == LaiAPIType.HK_PROXY) { - this.imagineUrl = define.hkServerUrl + 'mj/submit/imagine' - this.describeUrl = define.hkServerUrl + 'mj/submit/describe' - this.fetchTaskUrl = define.hkServerUrl + 'mj/task/${id}/fetch' - } else if (global.config.laiApiSelect == LaiAPIType.BAK_MAIN) { - this.imagineUrl = define.bakServerUrl + 'mj/submit/imagine' - this.describeUrl = define.bakServerUrl + 'mj/submit/describe' - this.fetchTaskUrl = define.bakServerUrl + 'mj/task/${id}/fetch' - } else { - let gpt = new GPT() - let mj_api = (await gpt.GetGPTBusinessOption('all', (value) => value.mj_url)).data - let mj_api_url_index = mj_api.findIndex((item) => item.value == this.mjSetting.apiSetting.mjApiUrl) - if (mj_api_url_index == -1) { - throw new Error('没有找到对应的MJ API的配置,请先检查配置') - } - this.imagineUrl = mj_api[mj_api_url_index].mj_url.imagine; - this.fetchTaskUrl = mj_api[mj_api_url_index].mj_url.once_get_task; - this.describeUrl = mj_api[mj_api_url_index].mj_url.imagine.replace("imagine", "describe"); + + let apiUrlIndex = apiUrl.findIndex(item => item.value == this.mj_globalSetting.mj_apiSetting.mjApiUrl); + if (apiUrlIndex == -1) { + throw new Error('没有找到MJ API对应的请求URL,请检查配置'); } + + let apiUrlItem = apiUrl[apiUrlIndex]; + if (apiUrlItem.mj_url == null) { + throw new Error('没有找到MJ API对应的请求URL,请检查配置'); + } + + this.imagineUrl = apiUrlItem.mj_url.imagine + this.describeUrl = apiUrlItem.mj_url.describe + this.fetchTaskUrl = apiUrlItem.mj_url.once_get_task } - return mjSettings.data } //#region 获取对应的任务,通过ID @@ -71,27 +89,66 @@ class MJApi { async GetMJAPITaskById(taskId: string, backTaskId: string) { try { await this.InitMJSetting(); - let url = this.fetchTaskUrl.replace("${id}", taskId) + let APIDescribeUrl = this.fetchTaskUrl.replace("${id}", taskId) let headers = undefined - if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + let useTransfer = false + + if (this.mjSimpleSetting.type == MJImageType.REMOTE_MJ) { headers = { 'mj-api-secret': define.API } + useTransfer = this.mj_globalSetting.mj_remoteSimpleSetting.useTransfer } else { headers = { - Authorization: this.mjSetting.apiSetting.apiKey + Authorization: this.mj_globalSetting.mj_apiSetting.apiKey } + useTransfer = this.mj_globalSetting.mj_apiSetting.useTransfer } - let res = await axios.get(url, { - headers: headers - }) + let resData = undefined + if (useTransfer) { - let progress = res.data.progress && res.data.progress.length > 0 - ? parseInt(res.data.progress.slice(0, -1)) + let url = define.lms + "/lms/Forward/GetTransfer"; + let config = { + method: 'post', + url: url, + maxBodyLength: Infinity, + headers: { + 'Content-Type': 'application/json' + }, + data: JSON.stringify({ + url: APIDescribeUrl, + apiKey: this.mjSimpleSetting.type == MJImageType.REMOTE_MJ ? + define.API : + headers.Authorization, + }) + } + + let res = await axios.request(config); + if (res.status != 200) { + throw new Error("转发请求失败") + } + if (res.data.code != 1) { + throw new Error(res.data.message) + } + if (!ValidateJson(res.data.data)) { + throw new Error(res.data.data) + } + resData = JSON.parse(res.data.data); + + } else { + let res = await axios.get(APIDescribeUrl, { + headers: headers + }) + resData = res.data + } + + + let progress = resData.progress && resData.progress.length > 0 + ? parseInt(resData.progress.slice(0, -1)) : 0 - let status = res.data.status.toLowerCase() + let status = resData.status.toLowerCase() let code = status == 'failure' || status == 'cancel' ? 0 : 1 let _bookBackTaskListService = await BookBackTaskListService.getInstance() @@ -101,22 +158,22 @@ class MJApi { _bookBackTaskListService.UpdateTaskStatus({ id: backTaskId, status: BookBackTaskStatus.FAIL, - errorMessage: res.data.message + errorMessage: resData.message }) } } let resObj = { type: MJRespoonseType.UPDATED, progress: isNaN(progress) ? 0 : progress, - category: this.mjSetting.type, - imageClick: res.data.imageUrl, - imageShow: res.data.imageUrl, - imagePath: res.data.imageUrl, + category: this.mjSimpleSetting.type, + imageClick: resData.imageUrl, + imageShow: resData.imageUrl, + imagePath: resData.imageUrl, messageId: taskId, status: status, code: code, - prompt: res.data.prompt == "" ? res.data.promptEn : res.data.prompt, - message: res.data.failReason, + prompt: resData.prompt == "" ? resData.promptEn : resData.prompt, + message: resData.failReason, mjApiUrl: this.fetchTaskUrl, } as MJ.MJResponseToFront return resObj @@ -135,7 +192,7 @@ class MJApi { async SubmitMJDescribe(param: MJ.APIDescribeParams): Promise { await this.InitMJSetting() let res = undefined - switch (this.mjSetting.type) { + switch (this.mjSimpleSetting.type) { case MJImageType.REMOTE_MJ: case MJImageType.API_MJ: res = await this.SubmitMJDescribeAPI(param) @@ -157,7 +214,7 @@ class MJApi { botType: this.bootType, base64: param.image, accountFilter: { - modes: [this.mjSetting.apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"], + modes: [this.mj_globalSetting.mj_apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"], remark: global.machineId }, @@ -168,11 +225,12 @@ class MJApi { } } - if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + if (this.mjSimpleSetting.type == MJImageType.REMOTE_MJ) { config.headers["mj-api-secret"] = define.API; + delete data.accountFilter.modes; } else { delete data.accountFilter.remark - config.headers["Authorization"] = this.mjSetting.apiSetting.apiKey; + config.headers["Authorization"] = this.mj_globalSetting.mj_apiSetting.apiKey; } // 开始请求 @@ -215,7 +273,7 @@ class MJApi { async SubmitMJImagine(taskId: string, prompt: string): Promise { await this.InitMJSetting() let res = undefined - switch (this.mjSetting.type) { + switch (this.mjSimpleSetting.type) { case MJImageType.REMOTE_MJ: case MJImageType.API_MJ: res = await this.SubmitMJImagineAPI(taskId, prompt) @@ -233,12 +291,13 @@ class MJApi { async SubmitMJImagineAPI(taskId: string, prompt: string): Promise { let _bookBackTaskListService = await BookBackTaskListService.getInstance() + // 提交API的出图任务 let data = { botType: this.bootType, prompt: prompt, accountFilter: { - modes: [this.mjSetting.apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"], + modes: [this.mj_globalSetting.mj_apiSetting.mjSpeed == MJSpeed.FAST ? "FAST" : "RELAX"], remark: global.machineId }, @@ -249,18 +308,59 @@ class MJApi { } } - if (this.mjSetting.type == MJImageType.REMOTE_MJ) { + let useTransfer = false; + + if (this.mjSimpleSetting.type == MJImageType.REMOTE_MJ) { config.headers["mj-api-secret"] = define.API; + delete data.accountFilter.modes; + useTransfer = this.mj_globalSetting.mj_remoteSimpleSetting.useTransfer } else { delete data.accountFilter.remark - config.headers["Authorization"] = this.mjSetting.apiSetting.apiKey; + config.headers["Authorization"] = this.mj_globalSetting.mj_apiSetting.apiKey; + useTransfer = this.mj_globalSetting.mj_apiSetting.useTransfer } - // 开始请求 - let res = await axios.post(this.imagineUrl, data, config) + let resData: any = undefined; + if (useTransfer) { + let url = define.lms + "/lms/Forward/SimpleTransfer" + let transferConfig = { + method: 'post', + url: url, + maxBodyLength: Infinity, + headers: { + 'Content-Type': 'application/json' + }, + data: JSON.stringify({ + url: this.imagineUrl, + apiKey: this.mjSimpleSetting.type == MJImageType.REMOTE_MJ ? define.API : this.mj_globalSetting.mj_apiSetting.apiKey, + dataString: JSON.stringify(data) + }) + } + let res = await axios.request(transferConfig); + if (res.status != 200) { + throw new Error("转发请求失败") + } + if (res.data.code != 1) { + throw new Error(res.data.message) + } + if (!ValidateJson(res.data.data)) { + throw new Error(res.data.data) + } + let re = JSON.parse(res.data.data); + resData = re + } else { + + // 开始请求 + let res = await axios.post(this.imagineUrl, data, config) + resData = res.data + } + + if (resData == null) { + throw new Error("返回的数据为空") + } // 某些API的返回的code为23,表示队列已满,需要重新请求 - if (res.data.code == 23) { + if (resData.code == 23) { _bookBackTaskListService.UpdateTaskStatus({ id: taskId, status: BookBackTaskStatus.RECONNECT @@ -268,13 +368,13 @@ class MJApi { return '23' } - if (res.data.code != 1 && res.data.code != 22) { + if (resData.code != 1 && resData.code != 22) { _bookBackTaskListService.UpdateTaskStatus({ id: taskId, status: BookBackTaskStatus.FAIL, - errorMessage: res.data.description + errorMessage: resData.description }) - throw new Error(res.data.description) + throw new Error(resData.description) } _bookBackTaskListService.UpdateTaskStatus({ @@ -282,7 +382,7 @@ class MJApi { status: BookBackTaskStatus.RUNNING }) - return res.data.result as string + return resData.result as string } diff --git a/src/main/Service/MJ/mjDefine.ts b/src/main/Service/MJ/mjDefine.ts new file mode 100644 index 0000000..a78588b --- /dev/null +++ b/src/main/Service/MJ/mjDefine.ts @@ -0,0 +1,169 @@ +import { apiUrl } from "@/define/api/apiUrlDefine" +import { MJRobotType, MJSpeed } from "@/define/enum/mjEnum" + +/** + * 获取MJ的请求模式列表 + * @returns + */ +function GetMJRequestModelOptions() { + let mjRequstModel = [{ + label: "本地MJ(待开发)", + value: "local_mj", + disable: true + }, + { + label: "代理MJ(token)", + value: "remote_mj", + disable: false + }, + { + label: "浏览器模式", + value: "browser_mj", + disable: false + }, + { + label: "API模式", + value: "api_mj", + disable: false + }] + return mjRequstModel.filter(item => !item.disable) +} + +/** + * 获取MJ的机器人列表 + * @returns + */ +function GetMJRobotOptions() { + return [{ + label: 'MJ', + value: MJRobotType.MJ + }, + { + label: 'NIJI', + value: MJRobotType.NIJI + }] +} + +/** + * 获取机器人对应的出图模型 + */ +function GetMJRobotModelOptions(mjRobot?: MJRobotType) { + let allRobotModel = [ + { + label: "MJ V6.0", + text: "v 6", + type: MJRobotType.MJ, + value: "3e6473ab-9a64-4574-9a38-f5c75af552b6" + }, + { + label: "MJ V5.2", + text: "v 5.2", + type: MJRobotType.MJ, + value: "27a0d30e-f46c-4684-96c8-d91334deb94f" + }, + { + label: "MJ V5.1", + text: "v 5.1", + type: MJRobotType.MJ, + value: "e1226715-e969-44c4-b18b-f2ad5dae5d2f" + }, { + label: "MJ V5.0", + text: "v 5", + type: MJRobotType.MJ, + value: "afb7bea1-4eda-46ea-8165-34701b4566bf" + }, { + label: "MJ V4.0", + text: "v 4", + type: MJRobotType.MJ, + value: "d05b8497-7f4a-4890-8fac-89f1803984d2" + }, { + label: "NIJI V6", + text: "niji 6", + type: MJRobotType.NIJI, + value: "99377cad-c103-4cee-a958-86a104879328" + }, { + label: "NIJI V5", + text: "niji 5", + type: MJRobotType.NIJI, + value: "53cec077-9885-4635-ab18-e021066b2c4c" + }, { + label: "NIJI V4", + text: "niji 4", + type: MJRobotType.NIJI, + value: "6a7199fe-6e0d-40a9-9772-b5eb3d2e2e66" + }, + ] + + switch (mjRobot) { + case MJRobotType.MJ: + return allRobotModel.filter(item => item.type == MJRobotType.MJ) + case MJRobotType.NIJI: + return allRobotModel.filter(item => item.type == MJRobotType.NIJI) + default: + return allRobotModel + } +} + +/** + * 获取MJ出图的比例Options + * @returns + */ +function GetMJImageScaleOptions() { + return [{ + label: "1:1", + text: "1:1", + value: "3e2772f2-041c-49c6-ba13-d0ed120310b8" + }, { + label: "4:3", + text: "4:3", + value: "fcef555c-1958-4082-88fe-434782aa8151" + }, { + label: "3:4", + text: "3:4", + value: "13f71d53-73a3-4c9b-9c1e-6e7e939aee73" + }, { + label: "16:9", + text: "16:9", + value: "bf33ce1a-15cd-4901-b38e-89543cf14a1f" + }, { + label: "9:16", + text: "9:16", + value: "fd4641e2-97f4-4a86-8616-4965e05f3348" + }] +} + +/** + * 获取MJ API 可用的URL Options + * @returns + */ +function GetMJAPIUrlOptions() { + return apiUrl.filter((item) => item.mj_url) +} + +/** + * 获取MJ的速度Options + * @returns + */ +function GetMJSpeedOptions() { + return [{ + label: "FAST", + value: MJSpeed.FAST + }, { + label: "RELAXED", + value: MJSpeed.RELAX + }] +} + +/** + * MJ的一些数据的定义 + */ +let MJDefine = { + GetMJRequestModelOptions, + GetMJRobotOptions, + GetMJRobotModelOptions, + GetMJImageScaleOptions, + GetMJAPIUrlOptions, + GetMJSpeedOptions +} + +export default MJDefine \ No newline at end of file diff --git a/src/main/Service/Options/index.ts b/src/main/Service/Options/index.ts index aae795d..f2eb151 100644 --- a/src/main/Service/Options/index.ts +++ b/src/main/Service/Options/index.ts @@ -13,7 +13,7 @@ class OptionHandle { * @param key 指定的Key的值 * @returns */ - GetOptionByKey = async (key: string) => await this.optionServices.GetOptionByKey(key) + GetOptionByKey = async (key: string | string[]) => await this.optionServices.GetOptionByKey(key) /** * 修改指定的Option,通过key,不存在则创建 diff --git a/src/main/Service/Options/optionServices.ts b/src/main/Service/Options/optionServices.ts index cea6a63..5574836 100644 --- a/src/main/Service/Options/optionServices.ts +++ b/src/main/Service/Options/optionServices.ts @@ -3,6 +3,7 @@ import { OptionKeyName, OptionType } from '@/define/enum/option' import { ValidateJson } from '@/define/Tools/validate' import { errorMessage, successMessage } from '@/main/Public/generalTools' import { ErrorItem, GeneralResponse, SuccessItem } from '@/model/generalResponse' +import { OptionModel } from '@/model/option/option' export class OptionServices { optionRealmService!: OptionRealmService constructor() { } @@ -19,10 +20,26 @@ export class OptionServices { * @param key * @returns */ - public async GetOptionByKey(key: string): Promise { + public async GetOptionByKey(key: string | string[]): Promise { try { await this.InitService() - let res = this.optionRealmService.GetOptionByKey(key) + let res: Array | OptionModel.OptionItem; + if (Array.isArray(key)) { + if (key.length <= 0) { + throw new Error('Key不能为空') + } + let temp = [] + for (let i = 0; i < key.length; i++) { + const element = key[i]; + let resItem = this.optionRealmService.GetOptionByKey(element) + if (resItem != null) { + temp.push(resItem) + } + } + res = temp; + } else { + res = this.optionRealmService.GetOptionByKey(key) + } return successMessage(res, '获取成功 OptionKey: ' + key, 'OptionOptions.GetOptionByKey') } catch (error: any) { return errorMessage( diff --git a/src/main/Service/ServiceBasic/softwareServiceBasic.ts b/src/main/Service/ServiceBasic/softwareServiceBasic.ts index 5bd4a1d..500f17b 100644 --- a/src/main/Service/ServiceBasic/softwareServiceBasic.ts +++ b/src/main/Service/ServiceBasic/softwareServiceBasic.ts @@ -1,6 +1,6 @@ import { SoftwareService } from '../../../define/db/service/SoftWare/softwareService'; import { MJSettingService } from '../../../define/db/service/SoftWare/mjSettingService'; -import { MJSetting } from '../../../model/Setting/mjSetting'; +import { MJSettingModel } from '../../../model/Setting/mjSetting'; export class SoftWareServiceBasic { @@ -85,7 +85,7 @@ export class SoftWareServiceBasic { * 获取MJ的设置信息 * @returns */ - async GetMjSetting(): Promise { + async GetMjSetting(): Promise { await this.InitService(); let mjSetting = this.mjSettingService.GetMjSetting({}) if (mjSetting.code == 1) { diff --git a/src/main/Service/task/taskManage.ts b/src/main/Service/task/taskManage.ts index f21018a..0bf0be2 100644 --- a/src/main/Service/task/taskManage.ts +++ b/src/main/Service/task/taskManage.ts @@ -12,7 +12,7 @@ import { D3Opt } from '../d3' import { FluxOpt } from '../Flux/flux' import { AsyncQueue } from '../../quene' import { SoftWareServiceBasic } from '../ServiceBasic/softwareServiceBasic' -import { MJSetting } from '../../../model/Setting/mjSetting' +import { MJSettingModel } from '../../../model/Setting/mjSetting' import { BookVideo } from '../Book/bookVideo' import { BookServiceBasic } from '../ServiceBasic/bookServiceBasic' import { TaskModal } from '@/model/task' @@ -32,7 +32,7 @@ export class TaskManager { bookServiceBasic: BookServiceBasic videoGlobal: VideoGlobal - mjSetting: MJSetting.MjSetting + mjSimpleSetting: MJSettingModel.MjSimpleSettingModel spaceTime: number = 5000; count = 0; isListening = false; @@ -65,7 +65,7 @@ export class TaskManager { } if (getMJsetting) { // 初始化MJ设置 - this.mjSetting = await this.softWareServiceBasic.GetMjSetting() + this.mjSimpleSetting = await this.softWareServiceBasic.GetMjSetting() } } @@ -141,8 +141,8 @@ export class TaskManager { if (element.type == BookBackTaskType.MJ_IMAGE || element.type == BookBackTaskType.MJ_REVERSE) { // 判断任务数量是不是又修改 let taskNumber = global.mjQueue.getConcurrencyLimit(); - if (taskNumber != this.mjSetting.taskCount) { - global.mjQueue.concurrencyLimit = this.mjSetting.taskCount // 重置并发执行的数量 + if (taskNumber != this.mjSimpleSetting.taskCount) { + global.mjQueue.concurrencyLimit = this.mjSimpleSetting.taskCount // 重置并发执行的数量 } if (global.mjQueue.getWaitingQueue() > 10) { diff --git a/src/main/Service/video/kling.ts b/src/main/Service/video/kling.ts index f42107e..2a62c88 100644 --- a/src/main/Service/video/kling.ts +++ b/src/main/Service/video/kling.ts @@ -7,11 +7,12 @@ import { cloneDeep, isEmpty } from "lodash"; import { GetBaseUrl } from "@/define/Tools/common"; import axios from "axios"; import { Book } from "@/model/book/book"; -import { c } from "naive-ui"; 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 @@ -25,7 +26,7 @@ export class KlingService { * @param gptUrl GPT地址 * @param gptApiKey GPTAPIKey */ - async KlingImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string): Promise { + 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; @@ -76,18 +77,41 @@ export class KlingService { if (klingOptions.hasOwnProperty("callback_url")) { data.callback_url = klingOptions.callback_url; } - // 开始请求 let baseUrl = GetBaseUrl(gptUrl); let url = baseUrl + "/kling/v1/videos/image2video"; - let res = await axios.post(url, data, { - headers: { - "Authorization": gptApiKey - } - }); - // console.log("kling合成视频结果", res); - let resData = res.data; + 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; // 修改数据 @@ -107,38 +131,60 @@ export class KlingService { data: JSON.stringify(resData) }, task.messageName); - await this.FetchKlingVideoResult(bookTaskDetail, task, resData.data.task_id, baseUrl, gptApiKey); + 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) { + 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 res = await axios.get(url, { - headers: { - Authorization: gptApiKey + 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, + }) } - }); - // console.log("kling合成视频结果", res.data); - let data = res.data.data; - if (data.task_status == "submitted") { + 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(data) + data: JSON.stringify(resData.data) }, task.messageName); - } else if (data.task_status == "processing") { + } 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(data); + videoMessage.messageData = JSON.stringify(resData.data); await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(task.bookTaskDetailId, videoMessage); SendMessageToRenderer({ @@ -146,16 +192,16 @@ export class KlingService { id: bookTaskDetail.id, message: "可灵合成任务正在合成中", type: ResponseMessageType.KLING_VIDEO, - data: JSON.stringify(data) + data: JSON.stringify(resData.data) }, task.messageName); - } else if (data.task_status == "succeed") { + } else if (resData.data.task_status == "succeed") { // 完成 let videoMessage = cloneDeep(bookTaskDetail.videoMessage); delete videoMessage.id; videoMessage.status = VideoStatus.SUCCESS; videoMessage.taskId = taskId; - videoMessage.videoUrl = data.task_result.videos[0].url; - videoMessage.messageData = JSON.stringify(data); + 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, @@ -169,7 +215,7 @@ export class KlingService { id: bookTaskDetail.id, message: "可灵合成视频完成", type: ResponseMessageType.KLING_VIDEO, - data: JSON.stringify(data.data) + data: JSON.stringify(resData.data.data) }, task.messageName); break; } else { @@ -177,18 +223,18 @@ export class KlingService { let videoMessage = cloneDeep(bookTaskDetail.videoMessage); delete videoMessage.id; videoMessage.status = VideoStatus.FAIL; - videoMessage.msg = res.data.message; + videoMessage.msg = resData.message; videoMessage.taskId = taskId; - videoMessage.messageData = JSON.stringify(data); + videoMessage.messageData = JSON.stringify(resData.data); await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(bookTaskDetail.id, videoMessage); SendMessageToRenderer({ code: 0, id: bookTaskDetail.id, - message: "runway合成视频失败,错误信息如下:" + res.data.message, + message: "runway合成视频失败,错误信息如下:" + resData.data.message, type: ResponseMessageType.KLING_VIDEO, - data: JSON.stringify(data) + data: JSON.stringify(resData.data) }, task.messageName); - throw new Error("可灵合成视频失败,失败信息如下:" + res.data.message); + throw new Error("可灵合成视频失败,失败信息如下:" + resData.message); } // 等待20秒后再次请求 await new Promise(resolve => setTimeout(resolve, 20000)); diff --git a/src/main/Service/video/luma.ts b/src/main/Service/video/luma.ts index fc1742f..d8c872d 100644 --- a/src/main/Service/video/luma.ts +++ b/src/main/Service/video/luma.ts @@ -11,14 +11,25 @@ import { SendMessageToRenderer } from "../globalService"; import { ResponseMessageType } from "@/define/enum/softwareEnum"; import { Book } from "@/model/book/book"; import { BookBackTaskStatus, BookTaskStatus } from "@/define/enum/bookEnum"; +import { define } from "@/define/define" +import ForwardResponse from "@/define/response/ForwardResponse"; export class LumaService { bookServiceBasic: BookServiceBasic + requestModel: string constructor() { this.bookServiceBasic = new BookServiceBasic(); + this.requestModel = "fast" } - async LumaImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string): Promise { + /** + * LUMA 合成视频 + * @param task + * @param gptUrl + * @param gptApiKey + * @param useTransfer + */ + async LumaImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise { try { let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId); let lumaOptionsString = bookTaskDetail.videoMessage.lumaOptions; @@ -27,6 +38,7 @@ export class LumaService { } let lumaOptions: BookTaskDetail.lumaOptions = JSON.parse(lumaOptionsString); // console.log("lumaOptions", lumaOptions, "gptUrl", gptUrl, "gptApiKey", gptApiKey); + this.requestModel = lumaOptions.request_model == null ? "fast" : lumaOptions.request_model; let data: BookTaskDetail.lumaOptions = { user_prompt: lumaOptions.user_prompt, @@ -59,16 +71,39 @@ export class LumaService { // 开始请求 let baseUrl = GetBaseUrl(gptUrl); let url = baseUrl + "/luma/generations"; - let res = await axios.post(url, data, { - headers: { - "Authorization": gptApiKey + if (this.requestModel == 'relax') { + url = baseUrl + "/luma-relax/luma/generations"; + } + + let returnData: 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) + }) } - }); - - // console.log("luma合成视频结果", res); - - let returnData = res.data; - // console.log("luma合成视频结果", returnData); + let response = await axios(transferConfig) + returnData = ForwardResponse.GetForwardResponseData(response) + } else { + let res = await axios.post(url, data, { + headers: { + "Authorization": gptApiKey + } + }); + returnData = res.data; + } let id = returnData.id; // 修改数据 @@ -88,7 +123,7 @@ export class LumaService { data: JSON.stringify(returnData) }, task.messageName); - await this.FetchLumaVideoResult(bookTaskDetail, task, id, baseUrl, gptApiKey); + await this.FetchLumaVideoResult(bookTaskDetail, task, id, baseUrl, gptApiKey, useTransfer); } catch (error) { throw new Error("Luma合成视频失败,错误信息如下:" + error.toString()); } @@ -101,14 +136,38 @@ export class LumaService { * @param gptApiKey Key * @returns */ - async GetVideoUri(taskId: string, baseUrl: string, gptApiKey: string): Promise { + async GetVideoUri(taskId: string, baseUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise { let url = baseUrl + "/luma/generations/" + taskId + "/download_video_url"; - let res = await axios.get(url, { - headers: { - Authorization: gptApiKey + + if (this.requestModel == "relax") { + url = baseUrl + "/luma-relax/luma/generations/" + taskId + "/download_video_url"; + } + + 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 resData = res.data; + let res = await axios.request(transferConfig); + resData = ForwardResponse.GetForwardResponseData(res) + } else { + let res = await axios.get(url, { + headers: { + Authorization: gptApiKey + } + }); + resData = res.data; + } // console.log("luma获取视频地址", resData); return resData.url; } @@ -121,24 +180,45 @@ export class LumaService { * @param baseUrl 请求的网址 * @param gptApiKey APIKey */ - async FetchLumaVideoResult(bookTaskDetail: Book.SelectBookTaskDetail, task: TaskModal.Task, taskId: string, baseUrl: string, gptApiKey: string) { + async FetchLumaVideoResult(bookTaskDetail: Book.SelectBookTaskDetail, task: TaskModal.Task, taskId: string, baseUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise { while (true) { try { let url = baseUrl + "/luma/generations/" + taskId; - let res = await axios.get(url, { - headers: { - Authorization: gptApiKey + if (this.requestModel == 'relax') { + url = baseUrl + "/luma-relax/luma/generations/" + 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, + }) } - }); - // console.log("luma合成视频结果", res.data); - let resData = res.data; + 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.state == "completed") { let video_url = resData.video.download_url; if (isEmpty(video_url)) { // 完成 - let vr = await this.GetVideoUri(taskId, baseUrl, gptApiKey); - // console.log("luma合成视频结果", vr); + let vr = await this.GetVideoUri(taskId, baseUrl, gptApiKey, useTransfer); video_url = vr; } // 保存数据 diff --git a/src/main/Service/video/runway.ts b/src/main/Service/video/runway.ts index b3cf562..b5ec31a 100644 --- a/src/main/Service/video/runway.ts +++ b/src/main/Service/video/runway.ts @@ -11,6 +11,8 @@ import { SendMessageToRenderer } from "../globalService"; import { ResponseMessageType } from "@/define/enum/softwareEnum"; import { Book } from "@/model/book/book"; import { BookBackTaskStatus, BookTaskStatus } from "@/define/enum/bookEnum"; +import { define } from "@/define/define" +import ForwardResponse from "@/define/response/ForwardResponse"; export class RunwayService { bookServiceBasic: BookServiceBasic @@ -24,7 +26,7 @@ export class RunwayService { * @param gptUrl 调用地址 * @param gptApiKey Key */ - async ImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string) { + async ImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string, useTransfer: boolean = false) { try { let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId); @@ -77,16 +79,42 @@ export class RunwayService { } // 开始请求 let baseUrl = GetBaseUrl(gptUrl); - let res = await axios.post(baseUrl + "/runway/pro/generate", data, { - headers: { - Authorization: gptApiKey + let url = baseUrl + "/runway/pro/generate" + + 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) + }) } - }); - // console.log("runway合成视频结果", res.data); - let resData = res.data; - if (resData.code != 200) { - throw new Error(res.data.msg); + let response = await axios(transferConfig) + resData = ForwardResponse.GetForwardResponseData(response) + } else { + let res = await axios.post(url, data, { + headers: { + Authorization: gptApiKey + } + }); + // console.log("runway合成视频结果", res.data); + resData = res.data; } + if (resData.code != 200) { + throw new Error(resData.msg); + } + // 修改数据 let videoMessage = cloneDeep(bookTaskDetail.videoMessage); videoMessage.taskId = resData.data.task_id; @@ -104,37 +132,62 @@ export class RunwayService { type: ResponseMessageType.RUNWAY_VIDEO, data: JSON.stringify(resData.data) }, task.messageName); - await this.FetchRunwayVideoResult(bookTaskDetail, task, resData.data.task_id, baseUrl, gptApiKey); + await this.FetchRunwayVideoResult(bookTaskDetail, task, resData.data.task_id, baseUrl, gptApiKey, useTransfer); } catch (error) { throw new Error("runway合成视频失败,错误信息如下:" + error.toString()); } } - async FetchRunwayVideoResult(bookTaskDetail: Book.SelectBookTaskDetail, task: TaskModal.Task, taskId: string, baseUrl: string, gptApiKey: string) { + async FetchRunwayVideoResult(bookTaskDetail: Book.SelectBookTaskDetail, task: TaskModal.Task, taskId: string, baseUrl: string, gptApiKey: string, useTransfer: boolean = false) { while (true) { try { let url = baseUrl + "/runway/feed"; - let res = await axios.post(url, { - "task_id": taskId - }, { - headers: { - Authorization: gptApiKey - } - }); - let data = res.data; - if (data.code != 200) { - throw new Error(data.msg); + 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({ + "task_id": taskId + }) + }) + } + let response = await axios(transferConfig) + resData = ForwardResponse.GetForwardResponseData(response) + } else { + let res = await axios.post(url, { + "task_id": taskId + }, { + headers: { + Authorization: gptApiKey + } + }); + + resData = res.data; + } + if (resData.code != 200) { + throw new Error(resData.msg); } - if (data.data.status == '3') { + if (resData.data.status == '3') { // 完成 let videoMessage = cloneDeep(bookTaskDetail.videoMessage); delete videoMessage.id; videoMessage.status = VideoStatus.SUCCESS; videoMessage.taskId = taskId; - videoMessage.videoUrl = data.data.video_url; - videoMessage.messageData = JSON.stringify(data.data); + videoMessage.videoUrl = resData.data.video_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, @@ -149,37 +202,37 @@ export class RunwayService { id: bookTaskDetail.id, message: "runway合成视频完成", type: ResponseMessageType.RUNWAY_VIDEO, - data: JSON.stringify(data.data) + data: JSON.stringify(resData.data) }, task.messageName); break; - } else if (data.data.status == '1') { + } else if (resData.data.status == '1') { // 处理中 SendMessageToRenderer({ code: 1, id: bookTaskDetail.id, message: "runway合成视频处理中", type: ResponseMessageType.RUNWAY_VIDEO, - data: JSON.stringify(data.data) + data: JSON.stringify(resData.data) }, task.messageName); } else { // 没有完成 - if (data.data.status == '2') { + if (resData.data.status == '2') { // 有报错信息直接返回错误 let videoMessage = cloneDeep(bookTaskDetail.videoMessage); delete videoMessage.id; videoMessage.status = VideoStatus.FAIL; - videoMessage.msg = data.data.msg; + videoMessage.msg = resData.data.msg; videoMessage.taskId = taskId; - videoMessage.messageData = JSON.stringify(data.data); + videoMessage.messageData = JSON.stringify(resData.data); await this.bookServiceBasic.UpdateBookTaskDetailVideoMessage(bookTaskDetail.id, videoMessage); SendMessageToRenderer({ code: 0, id: bookTaskDetail.id, - message: "runway合成视频失败,错误信息如下:" + data.data.msg, + message: "runway合成视频失败,错误信息如下:" + resData.data.msg, type: ResponseMessageType.RUNWAY_VIDEO, - data: JSON.stringify(data.data) + data: JSON.stringify(resData.data) }, task.messageName); - throw new Error(data.data.msg); + throw new Error(resData.data.msg); } } // 等待20秒后再次请求 diff --git a/src/main/Service/video/videoGlobal.ts b/src/main/Service/video/videoGlobal.ts index c45c228..e3ff7c3 100644 --- a/src/main/Service/video/videoGlobal.ts +++ b/src/main/Service/video/videoGlobal.ts @@ -128,24 +128,24 @@ export class VideoGlobal { async ImageToVideo(task: TaskModal.Task): Promise { try { - let { gptUrl, gptApiKey } = await this.gptService.RefreshGptSetting(); + let { gptUrl, gptApiKey, useTransfer } = await this.gptService.RefreshGptSetting(); switch (task.type) { case BookBackTaskType.RUNWAY_VIDEO: - await this.runwayService.ImageToVideo(task, gptUrl, gptApiKey); + await this.runwayService.ImageToVideo(task, gptUrl, gptApiKey, useTransfer); break; case BookBackTaskType.LUMA_VIDEO: - await this.lumaService.LumaImageToVideo(task, gptUrl, gptApiKey); + await this.lumaService.LumaImageToVideo(task, gptUrl, gptApiKey, useTransfer); break; case BookBackTaskType.KLING_VIDEO: - await this.klingService.KlingImageToVideo(task, gptUrl, gptApiKey); + await this.klingService.KlingImageToVideo(task, gptUrl, gptApiKey, useTransfer); break; default: throw new Error("暂不支持的视频类型"); } - + // 执行完毕,开始下载视频 let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId); let book = await this.bookServiceBasic.GetBookDataById(task.bookId); diff --git a/src/main/func.js b/src/main/func.js index 98eaca2..90dbe93 100644 --- a/src/main/func.js +++ b/src/main/func.js @@ -16,7 +16,6 @@ import { PublicMethod } from './Public/publicMethod' import { ImageStyleDefine } from '../define/iamgeStyleDefine' let tools = new Tools() let pm = new PublicMethod(global) -import { FLxuAPIImageType } from '../define/enum/image' import JianyingService from './Service/jianying/jianyingService' import VideoHandle from './Service/videoService/videoHandle' import { successMessage } from './Public/generalTools' @@ -691,11 +690,13 @@ async function SaveSDConfig(value) { if (!sd_config.flux) { let model = { - model: value.flux_model ? value.flux_model : FLxuAPIImageType.FLUX + model: value.flux_model ? value.flux_model : 'flux', + useTransfer: value.useTransfer == null ? false : value.useTransfer } sd_config.flux = model } else { - sd_config.flux.model = value.flux_model ? value.flux_model : FLxuAPIImageType.FLUX + sd_config.flux.model = value.flux_model ? value.flux_model : sd_config.flux.model + sd_config.flux.useTransfer = value.useTransfer == null ? false : value.useTransfer } await fspromises.writeFile(define.sd_setting, JSON.stringify(sd_config)) diff --git a/src/main/setting/setting.js b/src/main/setting/setting.js index 188ef7b..5b0de18 100644 --- a/src/main/setting/setting.js +++ b/src/main/setting/setting.js @@ -11,7 +11,6 @@ import { TagDefine } from '../../define/tagDefine' import { errorMessage } from '../Public/generalTools' import { TaskManager } from '../Service/task/taskManage' import { SoftWareServiceBasic } from '../Service/ServiceBasic/softwareServiceBasic' -import { FLxuAPIImageType } from '../../define/enum/image' let tagDefine = new TagDefine(global) export class Setting { @@ -176,7 +175,9 @@ export class Setting { sd_model: sd_config.sd_model, lora: sd_config.lora, sampler: sd_config.sampler, - flux_model: sd_config.flux?.model ? sd_config.flux.model : FLxuAPIImageType.FLUX + flux_model: sd_config.flux?.model, + useTransfer: + sd_config.flux.useTransfer == undefined ? false : sd_config.flux.useTransfer } } } catch (error) { diff --git a/src/model/Setting/mjSetting.d.ts b/src/model/Setting/mjSetting.d.ts index 630bfaf..a43857a 100644 --- a/src/model/Setting/mjSetting.d.ts +++ b/src/model/Setting/mjSetting.d.ts @@ -1,7 +1,10 @@ -import { MJImageType, MJRobotType } from "../../define/enum/mjEnum" +import { MJImageType, MJRobotType, MJSpeed } from "../../define/enum/mjEnum" -declare namespace MJSetting { - type ActionRemoteMJSetting = { +declare namespace MJSettingModel { + /** + * 代理模式配置数据模型 + */ + type ActionRemoteMJSettingModel = { id?: string accountId?: string channelId?: string @@ -19,22 +22,10 @@ declare namespace MJSetting { updateTime?: Date } - - type BrowserMJ = { - id: string - serviceId: string - channelId: string - mjBotId: string - nijBotId: string - token: string - userAgent: string - userAgentCustom: boolean - createTime: Date - updateTime: Date - version: string - } - - type RemoteMJ = { + /** + * 代理模式配置数据模型 + */ + type RemoteMJSettingModel = { id: string accountId: string | null channelId: string @@ -49,23 +40,47 @@ declare namespace MJSetting { timeoutMinutes: number userAgent: string userToken: string - createTime: Date - updateTime: Date - version: string + createTime?: Date + updateTime?: Date + version?: string } - type APIMj = { + /** + * 浏览器模式配置数据模型 + */ + type BrowserMJSettingModel = { + id: string + serviceId: string + channelId: string + mjBotId: string + nijBotId: string + token: string + userAgent: string + userAgentCustom: boolean + createTime?: Date + updateTime?: Date + version?: string + } + + + /** + * MJ API 设置 + */ + type MJAPISettingModel = { id: string mjApiUrl: string - mjSpeed: string // MJ出图的速度模式 + mjSpeed: MJSpeed // MJ出图的速度模式 apiKey: string - createTime: Date - updateTime: Date - version: string + useTransfer: boolean // 是否国内转发 + createTime?: Date + updateTime?: Date + version?: string } - type MjSetting = { - id: string + /** + * MJ 基础设置 + */ + type MjSimpleSettingModel = { type: MJImageType requestModel: MJImageType selectRobot: MJRobotType @@ -74,11 +89,22 @@ declare namespace MJSetting { imageSuffix: string taskCount: number spaceTime: number - createTime: Date - updateTime: Date - version: string - apiSetting: APIMj - remoteSetting: RemoteMJ - browserSetting: BrowserMJ + } + + /** + * MJ 代理设置模型 + */ + type MJRemoteSimpleSettingModel = { + useTransfer: boolean // 是否国内转发 + } + + /** + * MJ 全局设置 + */ + type MJ_GlobalSettingModel = { + mj_simpleSetting: MjSimpleSettingModel + mj_apiSetting: MJAPISettingModel + mj_browserSetting: BrowserMJSettingModel + mj_remoteSimpleSetting: MJRemoteSimpleSettingModel } } diff --git a/src/model/book/bookTaskDetail.d.ts b/src/model/book/bookTaskDetail.d.ts index 30d1f0e..3bd3ef3 100644 --- a/src/model/book/bookTaskDetail.d.ts +++ b/src/model/book/bookTaskDetail.d.ts @@ -56,6 +56,7 @@ declare namespace BookTaskDetail { image_url?: string; // 图片地址(参考图,支持第三方图片地址、Base64) image_end_url?: string; // 尾帧图片地址(支持第三方图片地址、Base64) notify_hook?: string; // 回调地址 + request_model?: string; // 请求的模型,快速还是慢速 } type klingOptions = { diff --git a/src/model/option/option.d.ts b/src/model/option/option.d.ts index 9bf418d..d0ba7ef 100644 --- a/src/model/option/option.d.ts +++ b/src/model/option/option.d.ts @@ -1,3 +1,4 @@ +import { MJImageType, MJRobotType } from "@/define/enum/mjEnum" import { OptionType } from "@/define/enum/option" declare namespace OptionModel { diff --git a/src/preload/options.ts b/src/preload/options.ts index bc8f4af..7d06f25 100644 --- a/src/preload/options.ts +++ b/src/preload/options.ts @@ -4,7 +4,7 @@ import { OptionType } from '@/define/enum/option' const options = { /** 通过Key获取指定的option */ - GetOptionByKey: async (key: string) => + GetOptionByKey: async (key: string | string[]) => await ipcRenderer.invoke(DEFINE_STRING.OPTIONS.GET_OPTION_BY_KEY, key), /** diff --git a/src/renderer/src/common/SDFluxCommon.ts b/src/renderer/src/common/SDFluxCommon.ts new file mode 100644 index 0000000..f5603cb --- /dev/null +++ b/src/renderer/src/common/SDFluxCommon.ts @@ -0,0 +1,30 @@ +import { define } from "@/define/define" +import { OptionKeyName } from "@/define/enum/option"; +import { ValidateJson } from "@/define/Tools/validate"; +import axios from "axios"; +import { isEmpty } from "lodash"; +/** + * 远程的FLUX模型列表 + * @returns + */ +async function GetRemoteFluxModelList() { + debugger + let res = await axios.get(define.lms + `/lms/LaitoolOptions/GetSimpleOptions/LaitoolFluxApiModelList`); + if (res.data.code != 1) { + throw new Error(res.data.message) + } + let data = res.data.data.filter(item => item.key == "LaitoolFluxApiModelList") + if (data.length != 1) { + throw new Error("获取远程FLUX API模型列表失败") + } + if (isEmpty(data[0].value) || !ValidateJson(data[0].value)) { + throw new Error("FLUX API模型列表数据异常,请联系管理员!") + } + return JSON.parse(data[0].value) +} + +let SDFluxCommon = { + GetRemoteFluxModelList +} + +export default SDFluxCommon \ No newline at end of file diff --git a/src/renderer/src/common/initCommon.ts b/src/renderer/src/common/initCommon.ts index b27f34d..5bc6448 100644 --- a/src/renderer/src/common/initCommon.ts +++ b/src/renderer/src/common/initCommon.ts @@ -1,6 +1,7 @@ // @ts-nocheck import { OptionKeyName, OptionType } from '@/define/enum/option' import { useOptionStore } from '@/stores/option' +import SDFluxCommon from './SDFluxCommon' /** * 初始化特殊字符数据,用于文案的相关处理 @@ -90,8 +91,54 @@ async function InitTTSGlobalSetting() { } } +/** + * 初始化模型列表 + * @returns + */ +async function InitFluxModelList(isMust: boolean = false): Promise> { + debugger + let initData = [] + if (!isMust) { + let FLUX_APIModelListData = await window.options.GetOptionByKey(OptionKeyName.FLUX_APIModelList); + if (FLUX_APIModelListData.code == 0) { + window.api.showGlobalMessageDialog(FLUX_APIModelListData); + return initData; + } + if (FLUX_APIModelListData.data == null) { + initData = await SDFluxCommon.GetRemoteFluxModelList() + let saveRes = await window.options.ModifyOptionByKey( + OptionKeyName.FLUX_APIModelList, + JSON.stringify(initData), + OptionType.JOSN + ) + if (saveRes.code == 0) { + window.api.showGlobalMessageDialog(saveRes) + return initData + } else { + return initData + } + } else { + return JSON.parse(FLUX_APIModelListData.data.value) + } + } else { + initData = await SDFluxCommon.GetRemoteFluxModelList() + let saveRes = await window.options.ModifyOptionByKey( + OptionKeyName.FLUX_APIModelList, + JSON.stringify(initData), + OptionType.JOSN + ) + if (saveRes.code == 0) { + window.api.showGlobalMessageDialog(saveRes) + return initData + } else { + return initData + } + } +} + let InitCommon = { InitSpecialCharacters, - InitTTSGlobalSetting + InitTTSGlobalSetting, + InitFluxModelList } export default InitCommon diff --git a/src/renderer/src/components/Book/Components/Video/GenerateVideoDialog.vue b/src/renderer/src/components/Book/Components/Video/GenerateVideoDialog.vue index cae464e..c050afe 100644 --- a/src/renderer/src/components/Book/Components/Video/GenerateVideoDialog.vue +++ b/src/renderer/src/components/Book/Components/Video/GenerateVideoDialog.vue @@ -95,6 +95,8 @@ async function GetBookTaskDetailOption() { } if (ValidateJson(videoMessage.value.lumaOptions)) { lumaOptions.value = JSON.parse(videoMessage.value.lumaOptions) + lumaOptions.value.request_model = + lumaOptions.value.request_model == null ? 'fast' : lumaOptions.value.request_model lumaOptions.value.image_url = videoMessage.value.imageUrl } if (ValidateJson(videoMessage.value.klingOptions)) { diff --git a/src/renderer/src/components/Book/Components/Video/LumaOption.vue b/src/renderer/src/components/Book/Components/Video/LumaOption.vue index 6f9b42a..7b40127 100644 --- a/src/renderer/src/components/Book/Components/Video/LumaOption.vue +++ b/src/renderer/src/components/Book/Components/Video/LumaOption.vue @@ -30,6 +30,16 @@ + + + @@ -37,7 +47,7 @@ diff --git a/src/renderer/src/components/Setting/MJSetting/MJAPISetting.vue b/src/renderer/src/components/Setting/MJSetting/MJAPISetting.vue new file mode 100644 index 0000000..c7d1d70 --- /dev/null +++ b/src/renderer/src/components/Setting/MJSetting/MJAPISetting.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/renderer/src/components/Setting/MJSetting/MJBrowserSetting.vue b/src/renderer/src/components/Setting/MJSetting/MJBrowserSetting.vue new file mode 100644 index 0000000..9f0d13e --- /dev/null +++ b/src/renderer/src/components/Setting/MJSetting/MJBrowserSetting.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/renderer/src/components/Setting/MJSetting/MJRemoteSetting.vue b/src/renderer/src/components/Setting/MJSetting/MJRemoteSetting.vue new file mode 100644 index 0000000..40747ac --- /dev/null +++ b/src/renderer/src/components/Setting/MJSetting/MJRemoteSetting.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/renderer/src/components/Setting/MJSetting/MJSettingHome.vue b/src/renderer/src/components/Setting/MJSetting/MJSettingHome.vue new file mode 100644 index 0000000..cb2ac67 --- /dev/null +++ b/src/renderer/src/components/Setting/MJSetting/MJSettingHome.vue @@ -0,0 +1,147 @@ + + + diff --git a/src/renderer/src/components/Setting/MJSetting/MJSimpleSetting.vue b/src/renderer/src/components/Setting/MJSetting/MJSimpleSetting.vue new file mode 100644 index 0000000..f141934 --- /dev/null +++ b/src/renderer/src/components/Setting/MJSetting/MJSimpleSetting.vue @@ -0,0 +1,141 @@ + + + diff --git a/src/renderer/src/components/Setting/SDSetting.vue b/src/renderer/src/components/Setting/SDSetting.vue index 491b873..a4b64d3 100644 --- a/src/renderer/src/components/Setting/SDSetting.vue +++ b/src/renderer/src/components/Setting/SDSetting.vue @@ -111,18 +111,41 @@ placeholder="输入迭代步数" /> - - + - - + + + 加载数据 + + + + + + + +
注意:使用国内转发可能会出现服务器超时错误,错误码504
+
+ 保存设置 @@ -134,7 +157,7 @@ - diff --git a/src/renderer/src/components/TTS/EdgeTTS.vue b/src/renderer/src/components/TTS/EdgeTTS.vue index a7491ba..cdb7606 100644 --- a/src/renderer/src/components/TTS/EdgeTTS.vue +++ b/src/renderer/src/components/TTS/EdgeTTS.vue @@ -52,6 +52,7 @@ import { useOptionStore } from '@/stores/option' let message = useMessage() let optionStore = useOptionStore() +let roleOptions = ref([]) async function SwitchTTSOptions(key) { console.log('SwitchTTSOptions', key) @@ -65,7 +66,6 @@ async function SwitchTTSOptions(key) { console.log('SwitchTTSOptions', roleOptions.value) } -let roleOptions = ref([]) onMounted(async () => { // 获取配音角色列表 await SwitchTTSOptions('edge-tts') diff --git a/src/renderer/src/components/TTS/TTSHome.vue b/src/renderer/src/components/TTS/TTSHome.vue index 0c108c5..8df6cb8 100644 --- a/src/renderer/src/components/TTS/TTSHome.vue +++ b/src/renderer/src/components/TTS/TTSHome.vue @@ -14,7 +14,7 @@ diff --git a/src/renderer/src/main.js b/src/renderer/src/main.js index 2477472..f11e49f 100644 --- a/src/renderer/src/main.js +++ b/src/renderer/src/main.js @@ -86,7 +86,7 @@ const routes = [ { path: '/mj_setting', name: 'mj_setting', - component: () => import('./components/Setting/MJSetting.vue') + component: () => import('./components/Setting/MJSetting/MJSettingHome.vue') }, { path: '/video_setting', diff --git a/src/stores/option.ts b/src/stores/option.ts index 372f2c1..055dd01 100644 --- a/src/stores/option.ts +++ b/src/stores/option.ts @@ -1,9 +1,12 @@ +import { MJImageType, MJRobotType, MJSpeed } from "@/define/enum/mjEnum"; import { OptionKeyName } from "@/define/enum/option"; import { OptionModel } from "@/model/option/option"; +import { MJSettingModel } from "@/model/Setting/mjSetting"; import { defineStore } from "pinia"; export type OptionStoreModel = { - //#region + + //#region 文案处理 /** 文案处理 AI设置 */ [OptionKeyName.CW_AISetting]: { @@ -23,6 +26,11 @@ export type OptionStoreModel = { [OptionKeyName.TTS_GlobalSetting]: OptionModel.TTS_GlobalSettingModel | undefined; //#endregion + + //#region MJ + /** MJ 基础设置 */ + [OptionKeyName.MJ_GlobalSetting]: MJSettingModel.MJ_GlobalSettingModel; + //#endregion } export const useOptionStore = defineStore('option', { @@ -64,6 +72,38 @@ export const useOptionStore = defineStore('option', { ttsText: "你好,我是你的智能语音助手!", /** 保存的音频文件路径 */ saveAudioPath: undefined, + }, + [OptionKeyName.MJ_GlobalSetting]: { + mj_simpleSetting: { + type: MJImageType.API_MJ, + requestModel: MJImageType.API_MJ, + selectRobot: MJRobotType.MJ, + imageScale: "3e2772f2-041c-49c6-ba13-d0ed120310b8", + imageModel: "3e6473ab-9a64-4574-9a38-f5c75af552b6", + imageSuffix: " --niji 6 --ar 1:1", + taskCount: 3, + spaceTime: 10 + }, + mj_apiSetting: { + id: "6db0d484-2a41-4545-8b26-ed32812965ad", + mjApiUrl: "b44c6f24-59e4-4a71-b2c7-3df0c4e35e65", + mjSpeed: MJSpeed.RELAX, + apiKey: "LAI API的令牌", + useTransfer: false + }, + mj_browserSetting: { + id: "4d5d57f2-1d7a-4e0f-b347-a7283e75d64a", + serviceId: "自己的服务器ID", + channelId: "自己的频道ID", + mjBotId: "自己的MJ机器人ID", + nijBotId: "自己的NIJI机器人ID", + token: "自己的令牌", + userAgent: "自己的UserAgent", + userAgentCustom: false, + }, + mj_remoteSimpleSetting: { + useTransfer: false + } } } as unknown as OptionStoreModel), diff --git a/src/stores/setting.ts b/src/stores/setting.ts index f8e228f..70cdb9e 100644 --- a/src/stores/setting.ts +++ b/src/stores/setting.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia' -import { MJSetting } from '../model/Setting/mjSetting' +import { MJSettingModel } from '../model/Setting/mjSetting' // 系统相关设置 export const useSettingStore = defineStore('setting', { @@ -21,7 +21,7 @@ export const useSettingStore = defineStore('setting', { userToken: undefined, createTime: undefined, updateTime: undefined - } as MJSetting.ActionRemoteMJSetting, + } as MJSettingModel.ActionRemoteMJSettingModel, // tts 配置 ttsSetting: { selectModel: 'edge-tts', @@ -37,7 +37,7 @@ export const useSettingStore = defineStore('setting', { }, } as TTSSettingModel.TTSSetting , - ttsRoles: [] + ttsRoles: [] }), getters: {}, actions: { @@ -62,7 +62,7 @@ export const useSettingStore = defineStore('setting', { userToken: undefined, createTime: undefined, updateTime: undefined - } as MJSetting.ActionRemoteMJSetting + } as MJSettingModel.ActionRemoteMJSettingModel } } })