Compare commits

...

2 Commits

Author SHA1 Message Date
03d3ff7e9d V 3.2.4(2025.02.24)
1. 新增MJ(API,代理模式)国内转发接口(不包括反推),解决部分用户不能访问外部网络
2. 新增 FLUX-API 国内转发接口,解决部分用户不能访问外部网络(可能出现超时报错)
3. 动态设置FLUX模型,统一后台管理
4. 新增图转视频国内(Kling,Luma,Runway)转发接口,解决部分用户不能访问外部网络
5. 新增支持 Luma 慢速模型
6. 优化MJ设置及MJ请求,减少出错
7. MJ代理模式取消账号生图速度校验
注意:本次更新需要重新设置MJ设置(代理账号不用)
2025-02-24 17:25:00 +08:00
6cf1e7df5b 修复升级数据库升级失败 2025-02-17 22:11:51 +08:00
51 changed files with 1826 additions and 1125 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "laitool",
"version": "3.2.3",
"version": "3.2.4",
"lockfileVersion": 3,
"requires": true,
"packages": {

View File

@ -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,7 +92,12 @@
"resources/tmp/**",
"resources/icon.ico"
],
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"win": {
"target": "nsis",
"icon": "./resources/icon.ico"
}
}

Binary file not shown.

Binary file not shown.

View File

@ -12,7 +12,22 @@ let apiUrl = [
d3_url: {
image: 'https://api.laitool.cc/v1/images/generations'
},
buy_url: 'https://api.laitool.cc/register?aff=Zmdu'
buy_url: 'https://api.laitool.cc/login'
},
{
label: 'LAI API - 美国',
value: '2b443f53-ba12-42b3-a57c-e4df92685c73',
gpt_url: 'https://laitool.net/v1/chat/completions',
mj_url: {
imagine: 'https://laitool.net/mj/submit/imagine',
describe: 'https://laitool.net/mj/submit/describe',
update_file: 'https://laitool.net/mj/submit/upload-discord-images',
once_get_task: 'https://laitool.net/mj/task/${id}/fetch'
},
d3_url: {
image: 'https://laitool.net/v1/images/generations'
},
buy_url: 'https://laitool.net/login'
},
{
label: 'openai-hk',

View File

@ -60,7 +60,7 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
status: BookTaskStatus
errorMsg: string | null
isAuto: boolean // 是否自动
openVideoGenerate: boolean // 是否开启视频生成
openVideoGenerate: boolean | null // 是否开启视频生成
updateTime: Date
createTime: Date
imageCategory: BookImageCategory // 图片出图方式
@ -90,7 +90,7 @@ export class BookTaskModel extends Realm.Object<BookTaskModel> {
prefixPrompt: "string?",
suffixPrompt: "string?",
status: 'string',
openVideoGenerate: 'bool',
openVideoGenerate: 'bool?',
errorMsg: 'string?',
isAuto: 'bool',
updateTime: 'date',

View File

@ -291,7 +291,7 @@ export class BaseRealmService extends BaseService {
VideoMessage
],
path: this.dbpath,
schemaVersion: 37,
schemaVersion: 39,
migration: migration
}
this.realm = await Realm.open(config)

View File

@ -1,7 +1,10 @@
const path = require('path')
const { app } = require('electron')
let define = {}
if (!app.isPackaged) {
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'
if (!isBrowser) {
const path = require('path')
const { app } = require('electron')
if (!app.isPackaged) {
define = {
discordScript: path.join(__dirname, '../../src/main/discord/discordScript.js'),
zhanwei_image: path.join(__dirname, '../../resources/image/zhanwei.png'),
@ -71,7 +74,7 @@ if (!app.isPackaged) {
),
add_keyframe_tmp_path: path.join(__dirname, '../../resources/tmp/Clip/keyframe_tmp.json')
}
} else {
} else {
define = {
zhanwei_image: path.join(__dirname, '../../../resources/image/zhanwei.png'),
config_path: path.join(__dirname, '../../../resources/config/global_setting.json'),
@ -141,6 +144,7 @@ if (!app.isPackaged) {
),
add_keyframe_tmp_path: path.join(__dirname, '../../../resources/tmp/Clip/keyframe_tmp.json')
}
}
}
define['remotemj_api'] = 'https://api.laitool.net/'
@ -155,4 +159,5 @@ define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0'
define['lms'] = 'https://lms.laitool.cn'
export { define }

View File

@ -1,15 +1,4 @@
/**
* Flux API时候的
*/
export enum FLxuAPIImageType {
FLUX = "flux",
FLUX_PRO = "flux-pro",
FLUX_DEV = "flux-dev",
FLUX_SCHNELL = "flux-schnell",
FLUX_PRO_MAX = "flux-pro-max"
}
export enum PresetType {
// 角色

View File

@ -35,10 +35,10 @@ export enum MJRobotType {
export enum MJSpeed {
// 快速
FAST = 'fast',
FAST = 'FAST',
// 休闲
RELAX = 'relaxed'
RELAX = 'RELAXED'
}
export enum MJRespoonseType {

View File

@ -36,5 +36,25 @@ export enum OptionKeyName {
*/
TTS_GlobalSetting = 'TTS_GlobalSetting',
//#endregion
//#region MJ
/**
* MJ
*/
MJ_GlobalSetting = 'MJ_GlobalSetting',
//#endregion
//#region FLUX
/**
* FLUX API
*/
FLUX_APIModelList = 'FLUX_APIModelList',
//#endregion
}

View File

@ -0,0 +1,25 @@
import { ValidateJson } from "../Tools/validate";
/**
*
* @param response
* @returns
*/
function GetForwardResponseData(response: any) {
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)
}
return JSON.parse(response.data.data);
}
let ForwardResponse = {
GetForwardResponseData,
}
export default ForwardResponse;

View File

@ -60,7 +60,6 @@ function MjIpc() {
)
// 获取MJ生图的方式
// GetMJGenerateCategory: async (callback) => 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()

View File

@ -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)
)
/**

View File

@ -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'

View File

@ -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<string> {
let response = await axios.post(url, { ...body, n: 1 }, {
async FluxAPIImageRequest(url: string, key: string, body: { model: string; prompt: string; size: string; }, useTransfer: boolean): Promise<string> {
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
}
})
if (response.data && response.data.data && response.data.data.length > 0) {
return response.data.data[0].url
} else {
return undefined
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 生图返回的图片地址为空')

View File

@ -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: "",

View File

@ -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<MJSetting.MjSetting> {
/**
* MJ设置
*/
async InitMJSetting(): Promise<void> {
// 获取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的配置请先检查配置')
let apiUrlIndex = apiUrl.findIndex(item => item.value == this.mj_globalSetting.mj_apiSetting.mjApiUrl);
if (apiUrlIndex == -1) {
throw new Error('没有找到MJ API对应的请求URL请检查配置');
}
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 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, {
let resData = undefined
if (useTransfer) {
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 = res.data.progress && res.data.progress.length > 0
? parseInt(res.data.progress.slice(0, -1))
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<string> {
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<string> {
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<string> {
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 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
}

View File

@ -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: "代理MJtoken",
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

View File

@ -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)
/**
* Optionkey

View File

@ -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<GeneralResponse.ErrorItem | SuccessItem> {
public async GetOptionByKey(key: string | string[]): Promise<GeneralResponse.ErrorItem | SuccessItem> {
try {
await this.InitService()
let res = this.optionRealmService.GetOptionByKey(key)
let res: Array<OptionModel.OptionItem> | 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(

View File

@ -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<MJSetting.MjSetting> {
async GetMjSetting(): Promise<MJSettingModel.MjSimpleSettingModel> {
await this.InitService();
let mjSetting = this.mjSettingService.GetMjSetting({})
if (mjSetting.code == 1) {

View File

@ -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) {

View File

@ -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<void> {
async KlingImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise<void> {
try {
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId);
let klingOptionsString = bookTaskDetail.videoMessage.klingOptions;
@ -76,10 +77,31 @@ 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 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
@ -87,7 +109,9 @@ export class KlingService {
});
// console.log("kling合成视频结果", res);
let resData = res.data;
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 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
}
});
// console.log("kling合成视频结果", res.data);
let data = res.data.data;
if (data.task_status == "submitted") {
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));

View File

@ -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<void> {
/**
* LUMA
* @param task
* @param gptUrl
* @param gptApiKey
* @param useTransfer
*/
async LumaImageToVideo(task: TaskModal.Task, gptUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise<void> {
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";
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)
})
}
let response = await axios(transferConfig)
returnData = ForwardResponse.GetForwardResponseData(response)
} else {
let res = await axios.post(url, data, {
headers: {
"Authorization": gptApiKey
}
});
// console.log("luma合成视频结果", res);
let returnData = res.data;
// console.log("luma合成视频结果", returnData);
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<string> {
async GetVideoUri(taskId: string, baseUrl: string, gptApiKey: string, useTransfer: boolean = false): Promise<string> {
let url = baseUrl + "/luma/generations/" + taskId + "/download_video_url";
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 res = await axios.request(transferConfig);
resData = ForwardResponse.GetForwardResponseData(res)
} else {
let res = await axios.get(url, {
headers: {
Authorization: gptApiKey
}
});
let resData = res.data;
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<void> {
while (true) {
try {
let url = baseUrl + "/luma/generations/" + taskId;
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,
})
}
let res = await axios.request(transferConfig);
resData = ForwardResponse.GetForwardResponseData(res)
} else {
let res = await axios.get(url, {
headers: {
Authorization: gptApiKey
}
});
// console.log("luma合成视频结果", res.data);
let resData = res.data;
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;
}
// 保存数据

View File

@ -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, {
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)
})
}
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);
let resData = res.data;
if (resData.code != 200) {
throw new Error(res.data.msg);
resData = res.data;
}
if (resData.code != 200) {
throw new Error(resData.msg);
}
// 修改数据
let videoMessage = cloneDeep(bookTaskDetail.videoMessage);
videoMessage.taskId = resData.data.task_id;
@ -104,16 +132,40 @@ 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 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
}, {
@ -122,19 +174,20 @@ export class RunwayService {
}
});
let data = res.data;
if (data.code != 200) {
throw new Error(data.msg);
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秒后再次请求

View File

@ -128,17 +128,17 @@ export class VideoGlobal {
async ImageToVideo(task: TaskModal.Task): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
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:

View File

@ -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))

View File

@ -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) {

View File

@ -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
}
}

View File

@ -56,6 +56,7 @@ declare namespace BookTaskDetail {
image_url?: string; // 图片地址(参考图支持第三方图片地址、Base64)
image_end_url?: string; // 尾帧图片地址(支持第三方图片地址、Base64)
notify_hook?: string; // 回调地址
request_model?: string; // 请求的模型,快速还是慢速
}
type klingOptions = {

View File

@ -1,3 +1,4 @@
import { MJImageType, MJRobotType } from "@/define/enum/mjEnum"
import { OptionType } from "@/define/enum/option"
declare namespace OptionModel {

View File

@ -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),
/**

View File

@ -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

View File

@ -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<Array<{ label: string, value: string }>> {
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

View File

@ -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)) {

View File

@ -30,6 +30,16 @@
<n-form-item-gi :span="12">
<n-image width="100" :src="lumaOptions.image_end_url"></n-image>
</n-form-item-gi>
<n-form-item-gi :span="4" label="速度模式">
<n-select
v-model:value="lumaOptions.request_model"
:options="[
{ label: 'FAST', value: 'fast' },
{ label: 'RELAX', value: 'relax' }
]"
placeholder="请选择速度"
></n-select>
</n-form-item-gi>
</n-grid>
</n-form>
</div>
@ -37,7 +47,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { NForm, NImage, NGrid, NFormItemGi, NInput, NCheckbox } from 'naive-ui'
import { NForm, NImage, NSelect, NGrid, NFormItemGi, NInput, NCheckbox } from 'naive-ui'
let props = defineProps({
lumaOptions: undefined

View File

@ -9,7 +9,7 @@
pane-wrapper-style="margin: 0 -4px"
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;"
>
<n-tab-pane name="mj_setting" tab="MJ设置"> <MJSetting /> </n-tab-pane>
<n-tab-pane name="mj_setting" tab="MJ设置"> <MJSettingHome /> </n-tab-pane>
<n-tab-pane name="sd_setting" tab="SD设置"> <SDSetting /> </n-tab-pane>
<n-tab-pane name="export_video_setting" tab="导出视频设置">
<VideoGenerateSetting />
@ -28,7 +28,7 @@
<script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NTabPane, NTabs, NCard } from 'naive-ui'
import MJSetting from '../../Setting/MJSetting.vue'
import MJSettingHome from '../../Setting/MJSetting/MJSettingHome.vue'
import SDSetting from '../../Setting/SDSetting.vue'
import VideoGenerateSetting from '../../Setting/VideoGenerateSetting.vue'
import WatermarkSetting from '../../Setting/WatermarkSetting.vue'
@ -39,7 +39,7 @@ export default defineComponent({
NTabPane,
NTabs,
NCard,
MJSetting,
MJSettingHome,
SDSetting,
VideoGenerateSetting,
WatermarkSetting,

View File

@ -1,522 +0,0 @@
<template>
<div style="overflow: auto; height: 100%">
<n-card title="基础设置" size="medium" hoverable style="min-width: 850px">
<n-form :model="mjSetting" ref="sampleRef" :rules="sampleRules">
<div style="display: flex; align-items: center">
<n-form-item label="出图模式" style="width: 150px" path="requestModel">
<n-select
:options="request_model_options"
v-model:value="mjSetting.requestModel"
placeholder="请选择出图模式"
></n-select>
</n-form-item>
<n-form-item
label="选择生图机器人"
path="selectRobot"
style="width: 120px; margin-left: 10px"
>
<n-select
:options="select_robot_options"
v-model:value="mjSetting.selectRobot"
@update:value="UpdateSelectRobot"
></n-select>
</n-form-item>
<n-form-item label="机器人模型" style="width: 120px; margin-left: 10px" path="imageModel">
<n-select
placeholder="请选择机器人模型"
:options="image_model_options"
v-model:value="mjSetting.imageModel"
></n-select>
</n-form-item>
<n-form-item label="生图尺寸" style="width: 120px; margin-left: 10px" path="imageScale">
<n-select
placeholder="请选择生图尺寸"
:options="image_scale_options"
v-model:value="mjSetting.imageScale"
></n-select>
</n-form-item>
<n-form-item label="命令后缀" style="width: 160px; margin-left: 10px" path="imageSuffix">
<n-input v-model:value="mjSetting.imageSuffix" placeholder="请输入后缀命令"></n-input>
</n-form-item>
<n-form-item label="生图任务量" style="width: 100px; margin-left: 10px" path="taskCount">
<n-input-number
v-model:value="mjSetting.taskCount"
:show-button="false"
placeholder="MJ并发出图数量"
:min="1"
:max="20"
></n-input-number>
</n-form-item>
<n-form-item label="间隔时间(s)" style="width: 100px; margin-left: 10px" path="spaceTime">
<n-input-number
v-model:value="mjSetting.spaceTime"
:show-button="false"
placeholder="请输入间隔时间(s)"
:min="1"
:max="30"
></n-input-number>
</n-form-item>
</div>
</n-form>
</n-card>
<n-card title="API模式设置" hoverable style="margin-top: 10px; min-width: 850px" size="medium">
<n-form :model="mjSetting.apiSetting">
<div style="display: flex">
<n-form-item
label="选择出图的API"
style="width: 160px; margin-left: 10px"
path="mjApiUrl"
>
<n-select
:options="mj_api_options"
v-model:value="mjSetting.apiSetting.mjApiUrl"
placeholder="选择出图的中转API"
/>
</n-form-item>
<n-form-item style="margin-left: 10px" path="mj_api_url">
<n-button type="success" @click="openGptBuyUrl">购买</n-button>
</n-form-item>
<n-form-item
label="选择出图速度模式"
style="width: 160px; margin-left: 10px"
path="mjSpeed"
>
<n-select
:options="mj_speed_options"
v-model:value="mjSetting.apiSetting.mjSpeed"
placeholder="选择出图速度模式"
/>
</n-form-item>
<n-form-item label="输入API密钥" style="width: 160px; margin-left: 10px" path="apiKey">
<n-input
type="password"
placeholder="请输入密钥"
show-password-on="mousedown"
v-model:value="mjSetting.apiSetting.apiKey"
></n-input>
</n-form-item>
</div>
</n-form>
</n-card>
<n-card title="代理模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-button type="info" @click="AddMultiMjAccount()">账号管理</n-button>
</n-card>
<n-card title="浏览器模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-form :model="mjSetting.browserSetting">
<div style="display: flex; margin-top: 10px">
<n-form-item>
<n-button type="info" @click="OpenDiscordWindow">打开登录MJ</n-button>
</n-form-item>
<n-form-item label="服务器ID" style="width: 200px; margin-left: 5px" path="serviceId">
<n-input
v-model:value="mjSetting.browserSetting.serviceId"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
<n-form-item label="频道ID" style="width: 200px; margin-left: 10px" path="channelId">
<n-input
v-model:value="mjSetting.browserSetting.channelId"
placeholder="登录MJ后切换频道自动获取"
/>
</n-form-item>
<n-form-item label="用户token" style="width: 300px; margin-left: 10px" path="token">
<n-input
v-model:value="mjSetting.browserSetting.token"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
</div>
<n-form-item label="用户Agent" style="width: 810px" path="userAgent">
<n-input
v-model:value="mjSetting.browserSetting.userAgent"
placeholder="登录MJ后切换服务器自动获取可自定义"
style="margin-right: 10px"
/>
<n-tooltip trigger="hover" style="background-color: aliceblue; color: black">
<template #trigger>
<n-checkbox
v-model:checked="mjSetting.browserSetting.userAgentCustom"
style="width: 100px"
>
自定义
</n-checkbox>
</template>
开启自定义需要手动填写userAgent可以填入浏览器的userAgent
</n-tooltip>
</n-form-item>
</n-form>
</n-card>
<n-button type="info" round @click="SaveMjSetting" style="margin-top: 10px"
>保存MJ配置</n-button
>
</div>
</template>
<script setup>
import { defineComponent, ref, onMounted, computed, toRaw, h } from 'vue'
import {
NButton,
useMessage,
useDialog,
NDataTable,
NForm,
NFormItem,
NInput,
NDivider,
NCheckbox,
NSelect,
NTooltip,
NIcon,
NInputNumber,
NCard,
NSpin
} from 'naive-ui'
import { DEFINE_STRING } from '../../../../define/define_string'
import { Reload } from '@vicons/ionicons5'
import { isEmpty, max, min } from 'lodash'
import { MJImageType } from '../../../../define/enum/mjEnum'
import AddMultiRemoteMj from './Components/AddMultiRemoteMj.vue'
let message = useMessage()
let dialog = useDialog()
let sampleRef = ref(null)
let select_robot_options = ref([
{
label: 'MJ',
value: 'mj'
},
{
label: 'NIJI',
value: 'niji'
}
])
let mj_speed_options = ref([
{
label: 'RELAXED',
value: 'relaxed'
},
{
label: 'FAST',
value: 'fast'
}
])
let mjSetting = ref({
id: null,
imageModel: null,
imageScale: null,
imageSuffix: null,
requestModel: null,
type: null,
selectRobot: null,
spaceTime: 6,
taskCount: 3,
createTime: null,
updateTime: null,
version: null,
remoteSetting: {
accountId: null,
channelId: null,
coreSize: 3,
guildId: null,
mjBotChannelId: null,
nijiBotChannelId: null,
queueSize: 6,
remark: null, //
remixAutoSubmit: false,
timeoutMinutes: 6,
userAgent: null,
userToken: null
},
apiSetting: {
id: null,
mjApiUrl: null,
mjSpeed: null,
apiKey: null
},
browserSetting: {
id: null,
serviceId: null,
channelId: null,
mjBotId: null,
nijBotId: null,
token: null,
userAgent: null,
userAgentCustom: false
}
})
let image_scale_options = ref([])
let image_model_options = ref([])
let tmp_image_model_options = []
let request_model_options = ref([])
let mj_api_options = ref([])
let computedSuffix = () => {
let text = image_model_options.value.findIndex((item) => {
return item.value == mjSetting.value.imageModel
})
if (text == -1) {
return
}
let sc = image_scale_options.value.findIndex((item) => {
return item.value == mjSetting.value.imageScale
})
let dd = ` --${image_model_options.value[text].text} --ar ${image_scale_options.value[sc].text}`
mjSetting.value.imageSuffix = dd
return dd
}
//
async function InitData() {
//
let res = await window.setting.GetMJSettingTreeData()
if (res.code == 0) {
message.error(res.message)
return
}
if (!res.data.apiSetting) {
delete res.data.apiSetting
}
if (!res.data.remoteSetting) {
delete res.data.remoteSetting
}
if (!res.data.browserSetting) {
delete res.data.browserSetting
}
mjSetting.value = Object.assign(mjSetting.value, res.data)
console.log(res)
await window.mj.GetMJImageScale((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
image_scale_options.value = value.data
if (image_scale_options.value.length > 0 && mjSetting.value.imageScale == null) {
mjSetting.value.imageScale = image_scale_options.value[0].value
}
})
await window.mj.GetMJImageRobotModel((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
image_model_options.value = value.data
tmp_image_model_options = value.data
if (image_model_options.value.length > 0 && mjSetting.value.imageModel == null) {
mjSetting.value.imageModel = image_model_options.value[0].value
}
})
await window.mj.GetMJGenerateCategory((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
request_model_options.value = value.data.filter((item) => !item.disable)
if (request_model_options.value.length > 0 && mjSetting.value.requestModel == null) {
mjSetting.value.requestModel = request_model_options.value[0].value
}
})
await window.api.getGptBusinessOption('all', (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
mj_api_options.value = value.data.filter((item) => item.mj_url)
if (mj_api_options.value.length > 0 && mjSetting.value.apiSetting.mjApiUrl == null) {
mjSetting.value.apiSetting.mjApiUrl = mj_api_options.value[0].value
}
})
}
onMounted(async () => {
//
window.api.setEventListen([DEFINE_STRING.DISCORD.OPERATE_REFRASH_DISCORD_URL], (value) => {
if (value.code == 0) {
message.error(value.message)
return
}
//
console.log(value)
if (value.type == DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.URL) {
mjSetting.value.browserSetting.serviceId = value.data.serviceID
mjSetting.value.browserSetting.channelId = value.data.channelID
} else if (value.type == DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.TOKEN) {
mjSetting.value.browserSetting.token = value.data.authorization
if (!mjSetting.value.browserSetting.userAgentCustom) {
mjSetting.value.browserSetting.userAgent = value.data.userAgent
}
} else {
message.error('未知的数据类型')
}
})
await InitData()
if (mjSetting.value.apiSetting.mjSpeed == null) {
mjSetting.value.apiSetting.mjSpeed = mj_speed_options.value[0].value
}
})
/**
* 打开discord窗口
*/
async function OpenDiscordWindow() {
await window.api.OpenDiscordWindow((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
})
}
//
let sampleRules = {
imageModel: [{ required: true, message: '请选择机器人模型', trigger: 'blur' }],
imageScale: [{ required: true, message: '请选择生图尺寸', trigger: 'blur' }],
requestModel: [{ required: true, message: '请选择出图模式', trigger: 'blur' }],
selectRobot: [{ required: true, message: '请选择生图机器人', trigger: 'blur' }]
}
/**
* 保存MJ配置
*/
async function SaveMjSetting(e) {
//
e.preventDefault()
sampleRef.value?.validate(async (errors) => {
if (errors) {
message.error('请检查必填字段')
return
}
// mjSetting.value.imageSuffix = image_suffix.value
//
let request_model = mjSetting.value.requestModel
if (request_model == MJImageType.API_MJ) {
// API
if (
mjSetting.value.apiSetting == null ||
mjSetting.value.apiSetting.mjApiUrl == null ||
mjSetting.value.apiSetting.mjSpeed == null ||
mjSetting.value.apiSetting.apiKey == null
) {
message.error('请检查API模式设置的必填字段')
return
}
}
if (request_model == MJImageType.REMOTE_MJ) {
//
let remoteMjRes = await window.setting.GetRemoteMJSettings()
if (remoteMjRes.code == 0) {
message.error('获取代理MJ配置失败')
return
}
if (remoteMjRes.data.length <= 0) {
message.error('请先添加代理模式的配置')
return
}
}
if (request_model == MJImageType.BROWSER_MJ) {
//
if (
mjSetting.value.browserSetting == null ||
mjSetting.value.browserSetting.serviceId == null ||
mjSetting.value.browserSetting.channelId == null ||
mjSetting.value.browserSetting.token == null ||
mjSetting.value.browserSetting.userAgent == null
) {
message.error('请检查浏览器模式设置的必填字段')
return
}
}
//
let res = await window.setting.SaveMJSettingTreeData(toRaw(mjSetting.value))
if (res.code == 0) {
window.api.showGlobalMessageDialog({ code: 0, message: res.message })
return
}
mjSetting.value = res.data
//
if (mjSetting.value.requestModel == MJImageType.REMOTE_MJ) {
window.api.showGlobalMessageDialog({
code: 1,
message: `数据保存成功,当前模式为代理模式`
})
} else {
window.api.showGlobalMessageDialog({ code: 1, message: '添加成功' })
}
})
}
/**
* 更新选择的机器人时触发的方法
*/
async function UpdateSelectRobot(value) {
//
image_model_options.value = tmp_image_model_options.filter((item) => {
return item.type == value
})
//
if (
image_model_options.value.findIndex((item) => {
return item.value == mjSetting.value.imageModel
}) == -1
) {
mjSetting.value.imageModel =
image_model_options.value.length > 0 ? image_model_options.value[0].value : null
}
//
mjSetting.value.imageSuffix = computedSuffix()
}
/**
* 打开购买GPT的地址
*/
async function openGptBuyUrl() {
let tmp_gb = mj_api_options.value.filter(
(item) => item.value == mjSetting.value.apiSetting.mjApiUrl
)
if (tmp_gb.length == 0) {
message.error('当前选择的服务商没有购买地址!')
return
}
let buy_url = tmp_gb[0].buy_url
if (isEmpty(buy_url)) {
message.error('当前选择的服务商没有购买地址!')
} else {
window.api.openGptBuyUrl(buy_url)
}
}
/**
* 添加多个账号这样可以同时跑多个账号
*/
async function AddMultiMjAccount() {
let dW = window.innerWidth * 0.9
let dH = window.innerHeight * 0.9
dialog.create({
showIcon: false,
title: '管理代理模式MJ账号',
content: () => h(AddMultiRemoteMj, { height: dH }),
style: `min-width : 600px; width : ${dW}px; height : ${dH}px; padding-right : 5px;`,
maskClosable: false
})
}
</script>

View File

@ -0,0 +1,95 @@
<template>
<div class="mj-api-setting">
<n-card title="API模式设置" hoverable style="margin-top: 10px; min-width: 850px" size="medium">
<n-form :model="optionStore.MJ_GlobalSetting.mj_apiSetting">
<div style="display: flex">
<n-form-item
label="选择出图的API"
style="width: 160px; margin-left: 10px"
path="mjApiUrl"
>
<n-select
:options="mjAPIURLOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.mjApiUrl"
placeholder="选择出图的中转API"
/>
</n-form-item>
<n-form-item style="margin-left: 10px" path="mj_api_url">
<n-button type="success" @click="openGptBuyUrl">购买</n-button>
</n-form-item>
<n-form-item
label="选择出图速度模式"
style="width: 160px; margin-left: 10px"
path="mjSpeed"
>
<n-select
:options="mjImageSpeedOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.mjSpeed"
placeholder="选择出图速度模式"
/>
</n-form-item>
<n-form-item label="输入API密钥" style="width: 160px; margin-left: 10px" path="apiKey">
<n-input
type="password"
placeholder="请输入密钥"
show-password-on="mousedown"
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.apiKey"
></n-input>
</n-form-item>
<n-form-item
style="width: 120px; margin-left: 30px"
path="useTransfer"
label="是否国内转发"
>
<n-select
v-model:value="optionStore.MJ_GlobalSetting.mj_apiSetting.useTransfer"
:options="[
{ label: '是', value: true },
{ label: '否', value: false }
]"
style="width: 100px"
/>
</n-form-item>
</div>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { NCard, NForm, NFormItem, NInput, NSelect, NButton, useMessage } from 'naive-ui'
import { useOptionStore } from '@/stores/option'
import MJDefine from '@/main/Service/MJ/mjDefine'
import { isEmpty } from 'lodash'
let optionStore = useOptionStore()
let message = useMessage()
let mjAPIURLOptions = ref([])
let mjImageSpeedOptions = ref([])
onMounted(() => {
mjAPIURLOptions.value = MJDefine.GetMJAPIUrlOptions()
mjImageSpeedOptions.value = MJDefine.GetMJSpeedOptions()
})
/**
* 打开购买GPT的地址
*/
async function openGptBuyUrl() {
let selectAPIUrl = mjAPIURLOptions.value.filter(
(item) => item.value == optionStore.MJ_GlobalSetting.mj_apiSetting.mjApiUrl
)
if (selectAPIUrl.length == 0) {
message.error('当前选择的服务商没有购买地址!')
return
}
let buy_url = selectAPIUrl[0].buy_url
if (isEmpty(buy_url)) {
message.error('当前选择的服务商没有购买地址!')
} else {
window.api.OpenUrl(buy_url)
}
}
</script>

View File

@ -0,0 +1,70 @@
<template>
<div class="mj-browser-setting">
<n-card title="浏览器模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-form :model="optionStore.MJ_GlobalSetting.mj_browserSetting">
<div style="display: flex; margin-top: 10px">
<n-form-item>
<n-button type="info" @click="OpenDiscordWindow">打开登录MJ</n-button>
</n-form-item>
<n-form-item label="服务器ID" style="width: 200px; margin-left: 5px" path="serviceId">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.serviceId"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
<n-form-item label="频道ID" style="width: 200px; margin-left: 10px" path="channelId">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.channelId"
placeholder="登录MJ后切换频道自动获取"
/>
</n-form-item>
<n-form-item label="用户token" style="width: 300px; margin-left: 10px" path="token">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.token"
placeholder="登录MJ后切换服务器自动获取"
/>
</n-form-item>
</div>
<n-form-item label="用户Agent" style="width: 810px" path="userAgent">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_browserSetting.userAgent"
placeholder="登录MJ后切换服务器自动获取可自定义"
style="margin-right: 10px"
/>
<n-tooltip trigger="hover" style="background-color: aliceblue; color: black">
<template #trigger>
<n-checkbox
v-model:checked="optionStore.MJ_GlobalSetting.mj_browserSetting.customUserAgent"
style="width: 100px"
>
自定义
</n-checkbox>
</template>
开启自定义需要手动填写userAgent可以填入浏览器的userAgent
</n-tooltip>
</n-form-item>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { NCard, NForm, NFormItem, NTooltip, NCheckbox, NInput, NButton } from 'naive-ui'
import { useOptionStore } from '@/stores/option'
let optionStore = useOptionStore()
/**
* 打开discord窗口
*/
async function OpenDiscordWindow() {
await window.api.OpenDiscordWindow((value) => {
if (value.code == 0) {
message.error(value.message)
return
}
})
}
</script>

View File

@ -0,0 +1,48 @@
<template>
<div class="mj-remote-setting">
<n-card title="代理模式设置" hoverable style="margin-top: 10px; min-width: 850px">
<n-form inline>
<n-form-item style="width: 120px" path="useTransfer" label="是否国内转发">
<n-select
v-model:value="optionStore.MJ_GlobalSetting.mj_remoteSimpleSetting.useTransfer"
:options="[
{ label: '是', value: true },
{ label: '否', value: false }
]"
style="width: 100px"
/>
</n-form-item>
<n-form-item style="width: 120px; margin-left: 30px" path="useTransfer">
<n-button type="info" @click="AddMultiMjAccount">账号管理</n-button>
</n-form-item>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { ref, h } from 'vue'
import { NCard, NButton, useDialog, NForm, NFormItem, NSelect } from 'naive-ui'
import AddMultiRemoteMj from '../Components/AddMultiRemoteMj.vue'
import { useOptionStore } from '@/stores/option'
let optionStore = useOptionStore()
let dialog = useDialog()
/**
* 添加多个账号这样可以同时跑多个账号
*/
async function AddMultiMjAccount() {
let dW = window.innerWidth * 0.9
let dH = window.innerHeight * 0.9
dialog.create({
showIcon: false,
title: '管理代理模式MJ账号',
content: () => h(AddMultiRemoteMj, { height: dH }),
style: `min-width : 600px; width : ${dW}px; height : ${dH}px; padding-right : 5px;`,
maskClosable: false
})
}
</script>

View File

@ -0,0 +1,147 @@
<template>
<div class="mj-setting-home">
<MJSimpleSetting />
<MJAPISetting />
<MJRemoteSetting />
<MJBrowserSetting />
<n-button type="info" @click="SaveMjSetting" style="margin-top: 10px">保存MJ配置</n-button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useMessage, NButton } from 'naive-ui'
import { useSoftwareStore } from '@/stores/software'
import { useOptionStore } from '@/stores/option'
import { TimeDelay } from '@/define/Tools/time'
import { OptionKeyName, OptionType } from '@/define/enum/option'
import MJSimpleSetting from './MJSimpleSetting.vue'
import MJAPISetting from './MJAPISetting.vue'
import MJRemoteSetting from './MJRemoteSetting.vue'
import MJBrowserSetting from './MJBrowserSetting.vue'
import { MJImageType } from '@/define/enum/mjEnum'
import { isEmpty } from 'lodash'
let softwareStore = useSoftwareStore()
let optionStore = useOptionStore()
let message = useMessage()
onMounted(async () => {
await InitMJSettingData()
})
/**
* 初始化MJSetting数据
*/
async function InitMJSettingData() {
try {
softwareStore.spin.spinning = true
softwareStore.spin.tip = '正在加载数据,请稍等...'
let mjRes = await window.options.GetOptionByKey(OptionKeyName.MJ_GlobalSetting)
if (mjRes.code == 0) {
window.api.showGlobalMessageDialog({
code: 0,
message: '加载或者是初始化MJSetting信息失败失败原因' + mjRes.message
})
return
}
if (mjRes.data == null) {
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.MJ_GlobalSetting,
JSON.stringify(optionStore.MJ_GlobalSetting),
OptionType.JSON
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog({
code: 0,
message: '加载或者是初始化MJSetting信息失败失败原因' + saveRes.message
})
return
} else {
message.success('初始化MJSetting信息成功')
return
}
} else {
optionStore.MJ_GlobalSetting = JSON.parse(mjRes.data.value)
message.success('加载MJSetting信息成功')
}
await TimeDelay(1000)
} catch (error) {
window.api.showGlobalMessageDialog({
code: 0,
message: '加载或者是初始化MJSetting信息失败失败原因' + error.toString()
})
} finally {
softwareStore.spin.spinning = false
}
}
/**
* 保存MJ配置
*/
async function SaveMjSetting() {
//
debugger
//
let request_model = optionStore.MJ_GlobalSetting.mj_simpleSetting.requestModel
if (request_model == MJImageType.API_MJ) {
// API
if (
optionStore.MJ_GlobalSetting.mj_simpleSetting == null ||
isEmpty(optionStore.MJ_GlobalSetting.mj_apiSetting.mjApiUrl) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_apiSetting.mjSpeed) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_apiSetting.apiKey)
) {
message.error('请检查API模式设置的必填字段')
return
}
}
if (request_model == MJImageType.REMOTE_MJ) {
//
let remoteMjRes = await window.setting.GetRemoteMJSettings()
if (remoteMjRes.code == 0) {
message.error('获取代理MJ配置失败')
return
}
if (remoteMjRes.data.length <= 0) {
message.error('请先添加代理模式的配置')
return
}
}
if (request_model == MJImageType.BROWSER_MJ) {
//
if (
optionStore.MJ_GlobalSetting.mj_browserSetting == null ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.serviceId) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.channelId) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.token) ||
isEmpty(optionStore.MJ_GlobalSetting.mj_browserSetting.userAgent)
) {
message.error('请检查浏览器模式设置的必填字段')
return
}
}
optionStore.MJ_GlobalSetting.mj_simpleSetting.type =
optionStore.MJ_GlobalSetting.mj_simpleSetting.requestModel
let saveRes = await window.options.ModifyOptionByKey(
OptionKeyName.MJ_GlobalSetting,
JSON.stringify(optionStore.MJ_GlobalSetting),
OptionType.JSON
)
if (saveRes.code == 0) {
window.api.showGlobalMessageDialog(saveRes)
} else {
window.api.showGlobalMessageDialog({
code: 1,
message: '保存数据成功'
})
}
}
</script>

View File

@ -0,0 +1,141 @@
<template>
<div class="mj-simple-setting">
<n-card title="基础设置" size="medium" hoverable style="min-width: 850px">
<n-form
:model="optionStore.MJ_GlobalSetting.mj_simpleSetting"
ref="sampleRef"
:rules="sampleRules"
>
<div style="display: flex; align-items: center">
<n-form-item label="出图模式" style="width: 150px" path="requestModel">
<n-select
:options="mjRequestModelOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.requestModel"
placeholder="请选择出图模式"
></n-select>
</n-form-item>
<n-form-item
label="选择生图机器人"
path="selectRobot"
style="width: 120px; margin-left: 10px"
>
<n-select
:options="mjRobotOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.selectRobot"
@update:value="UpdateSelectRobot"
></n-select>
</n-form-item>
<n-form-item label="机器人模型" style="width: 120px; margin-left: 10px" path="imageModel">
<n-select
placeholder="请选择机器人模型"
:options="mjRobotModelOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.imageModel"
@update:value="UpdateRobotModel"
></n-select>
</n-form-item>
<n-form-item label="生图尺寸" style="width: 120px; margin-left: 10px" path="imageScale">
<n-select
placeholder="请选择生图尺寸"
:options="mjImageScaleOptions"
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.imageScale"
@update:value="UpdateImageScale"
></n-select>
</n-form-item>
<n-form-item label="命令后缀" style="width: 160px; margin-left: 10px" path="imageSuffix">
<n-input
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.imageSuffix"
placeholder="请输入后缀命令"
></n-input>
</n-form-item>
<n-form-item label="生图任务量" style="width: 100px; margin-left: 10px" path="taskCount">
<n-input-number
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.taskCount"
:show-button="false"
placeholder="MJ并发出图数量"
:min="1"
:max="20"
></n-input-number>
</n-form-item>
<n-form-item label="间隔时间(s)" style="width: 100px; margin-left: 10px" path="spaceTime">
<n-input-number
v-model:value="optionStore.MJ_GlobalSetting.mj_simpleSetting.spaceTime"
:show-button="false"
placeholder="请输入间隔时间(s)"
:min="1"
:max="30"
></n-input-number>
</n-form-item>
</div>
</n-form>
</n-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { NCard, NForm, NFormItem, NInputNumber, NSelect, NInput } from 'naive-ui'
import { useOptionStore } from '@/stores/option'
import MJDefine from '@/main/Service/MJ/mjDefine'
let optionStore = useOptionStore()
let mjRequestModelOptions = ref([])
let mjRobotOptions = ref([])
let mjRobotModelOptions = ref([])
let mjImageScaleOptions = ref([])
//
let sampleRules = {
imageModel: [{ required: true, message: '请选择机器人模型', trigger: 'blur' }],
imageScale: [{ required: true, message: '请选择生图尺寸', trigger: 'blur' }],
requestModel: [{ required: true, message: '请选择出图模式', trigger: 'blur' }],
selectRobot: [{ required: true, message: '请选择生图机器人', trigger: 'blur' }]
}
onMounted(() => {
mjRequestModelOptions.value = MJDefine.GetMJRequestModelOptions()
mjRobotOptions.value = MJDefine.GetMJRobotOptions()
mjRobotModelOptions.value = MJDefine.GetMJRobotModelOptions(
optionStore.MJ_GlobalSetting.mj_simpleSetting.selectRobot
)
mjImageScaleOptions.value = MJDefine.GetMJImageScaleOptions()
})
let computedSuffix = () => {
let text = mjRobotModelOptions.value.findIndex((item) => {
return item.value == optionStore.MJ_GlobalSetting.mj_simpleSetting.imageModel
})
if (text == -1) {
return
}
let sc = mjImageScaleOptions.value.findIndex((item) => {
return item.value == optionStore.MJ_GlobalSetting.mj_simpleSetting.imageScale
})
let dd = ` --${mjRobotModelOptions.value[text].text} --ar ${mjImageScaleOptions.value[sc].text}`
optionStore.MJ_GlobalSetting.mj_simpleSetting.imageSuffix = dd
return dd
}
/**
* 更新选择的机器人时触发的方法
*/
function UpdateSelectRobot() {
mjRobotModelOptions.value = MJDefine.GetMJRobotModelOptions(
optionStore.MJ_GlobalSetting.mj_simpleSetting.selectRobot
)
optionStore.MJ_GlobalSetting.mj_simpleSetting.imageModel =
mjRobotModelOptions.value.length > 0 ? mjRobotModelOptions.value[0].value : undefined
//
computedSuffix()
}
function UpdateImageScale() {
computedSuffix()
}
function UpdateRobotModel() {
computedSuffix()
}
</script>

View File

@ -111,6 +111,7 @@
placeholder="输入迭代步数"
/>
</n-form-item>
<div style="display: flex">
<n-form-item
style="width: 300px"
label="FLXU API模型和GPT公用一个服务商"
@ -122,7 +123,29 @@
:options="flux_model_options"
>
</n-select>
<n-button type="primary" style="margin-left: 10px" @click="GetFluxModelList"
>加载数据
</n-button>
</n-form-item>
<n-form-item
style="width: 150px; margin-left: 30px"
label="是否国内转发"
path="useTransfer"
>
<n-select
placeholder="请选择是否转换"
v-model:value="formValue.useTransfer"
:options="[
{ label: '是', value: true },
{ label: '否', value: false }
]"
>
</n-select>
</n-form-item>
<n-form-item style="margin-left: 30px">
<div style="color: red">注意使用国内转发可能会出现服务器超时错误错误码504</div>
</n-form-item>
</div>
<n-form-item>
<n-button attr-type="button" type="primary" @click="SaveSDConfig"> 保存设置 </n-button>
</n-form-item>
@ -134,7 +157,7 @@
</n-tabs>
</template>
<script>
<script setup>
import { defineComponent, ref, h, onMounted, toRaw } from 'vue'
import {
NForm,
@ -149,23 +172,11 @@ import {
NCheckbox
} from 'naive-ui'
import SDADetailerSetting from '../Components/SDADetailerSetting.vue'
import { FLxuAPIImageType } from '../../../../define/enum/image'
import InitCommon from '../../common/initCommon'
export default defineComponent({
components: {
NForm,
NFormItem,
NInput,
NButton,
NInputNumber,
NSelect,
NTabs,
NTabPane,
SDADetailerSetting,
NCheckbox
},
setup() {
let formValue = ref({
let flux_model_options = ref([])
let formValue = ref({
webui_api_url: window.config.webui_api_url,
type: null,
prompt: null,
@ -180,14 +191,15 @@ export default defineComponent({
cfg_scale: 1,
sd_models: null,
lora: null,
flux_model: FLxuAPIImageType.FLUX
})
flux_model: null,
useTransfer: false
})
let samplers_options = ref([])
let lora_options = ref([])
let sd_models_options = ref([])
let samplers_options = ref([])
let lora_options = ref([])
let sd_models_options = ref([])
let modelOption = ref([
let modelOption = ref([
{
label: '文生图',
value: 'txt2img'
@ -196,9 +208,9 @@ export default defineComponent({
label: '图生图',
value: 'img2img'
}
])
])
onMounted(async () => {
onMounted(async () => {
await window.api.InitSDConfig((value) => {
if (value.code == 0) {
message.error(value.message)
@ -234,13 +246,34 @@ export default defineComponent({
})
}
})
})
await InitFluxModelList()
})
let message = useMessage()
/**
/**
* 初始化FLUX模型列表
*/
async function InitFluxModelList(isMust = false) {
try {
flux_model_options.value = await InitCommon.InitFluxModelList(isMust)
if (!formValue.value.flux_model) {
formValue.value.flux_model =
flux_model_options.value.length > 0 ? flux_model_options.value[0].value : undefined
}
} catch (error) {
message.error('加载FLUX模型列表失败')
}
}
async function GetFluxModelList() {
await InitFluxModelList(true)
message.success('加载远程 FLUX API 模型列表成功')
}
let message = useMessage()
/**
* 保存SD设置
*/
async function SaveSDConfig() {
async function SaveSDConfig() {
// SD
await window.api.SaveSDConfig(toRaw(formValue.value), (value) => {
if (value.code == 0) {
@ -256,12 +289,12 @@ export default defineComponent({
window.api.showGlobalMessageDialog({ code: 0, message: value.message })
}
})
}
}
/**
/**
* 加载SD数据
*/
async function LoadSDServiceData() {
async function LoadSDServiceData() {
await window.sd.LoadSDServiceData(toRaw(formValue.value).webui_api_url, (value) => {
if (value.code == 0) {
message.error(value.message)
@ -298,39 +331,5 @@ export default defineComponent({
}
message.success('加载成功')
})
}
return {
formValue,
SaveSDConfig,
modelOption,
LoadSDServiceData,
samplers_options,
lora_options,
sd_models_options,
flux_model_options: [
{
label: 'FLUX',
value: FLxuAPIImageType.FLUX
},
{
label: 'FLUX DEV',
value: FLxuAPIImageType.FLUX_DEV
},
{
label: 'FLUX PRO',
value: FLxuAPIImageType.FLUX_PRO
},
{
label: 'FLUX PRO MAX',
value: FLxuAPIImageType.FLUX_PRO_MAX
},
{
label: 'FLUX SCHNELL',
value: FLxuAPIImageType.FLUX_SCHNELL
}
]
}
}
})
}
</script>

View File

@ -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')

View File

@ -14,7 +14,7 @@
</n-form>
<EdgeTTS v-if="optionStore.TTS_GlobalSetting.selectModel == 'edge-tts'" />
<!-- <AzureTTS
:azureTTS="settingStore.ttsSetting.azureTTS"
:azureTTS=""
v-else-if="optionStore.TTS_GlobalSetting.selectModel == 'azure-tts'"
/> -->
</div>

View File

@ -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',

View File

@ -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),

View File

@ -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',
@ -62,7 +62,7 @@ export const useSettingStore = defineStore('setting', {
userToken: undefined,
createTime: undefined,
updateTime: undefined
} as MJSetting.ActionRemoteMJSetting
} as MJSettingModel.ActionRemoteMJSettingModel
}
}
})