修复聚合推文剪映抽帧时,导入srt没有输入 (聚合推文) SD 反推,MJ反推添加单句洗稿功能 (聚合推文)小说任务列表,添加进入出图文件夹的菜单 (聚合推文)新增一键出图 (聚合推文)新增默认出图方式设置, 5.1只对原创生效,SD反推默认是SD,MJ反推默认是MJ 5.2添加批次的默认出图方式同 a点说明,但是选择了 选择旧批次,新批次的默认出图方式会继承 后台任务,完成后台任务的可视化界面 (聚合推文)小说详情界面,添加小说和批次名称显示
347 lines
12 KiB
TypeScript
347 lines
12 KiB
TypeScript
import { define } from '../../../define/define'
|
||
import fs from "fs";
|
||
import { ImageStyle } from "../Book/imageStyle";
|
||
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
|
||
import { isEmpty } from 'lodash';
|
||
import axios from 'axios';
|
||
const fspromise = fs.promises
|
||
import path from 'path'
|
||
import { CheckFolderExistsOrCreate, CopyFileOrFolder, DeleteFileExifData } from '../../../define/Tools/file';
|
||
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';
|
||
|
||
export class FluxOpt {
|
||
gptService: GptService
|
||
bookServiceBasic: BookServiceBasic
|
||
constructor() {
|
||
this.bookServiceBasic = new BookServiceBasic()
|
||
this.gptService = new GptService()
|
||
}
|
||
|
||
|
||
// TODO 这边的设置应该改为数据库
|
||
/**
|
||
* 获取SD的设置,之后要删掉,改为数据库
|
||
*/
|
||
private async GetSDSetting() {
|
||
let sdSetting = JSON.parse(await fspromise.readFile(define.sd_setting, 'utf-8'))
|
||
return sdSetting
|
||
}
|
||
|
||
|
||
//#region flux forge 生图
|
||
/**
|
||
* 使用flux forge生图
|
||
* @param task
|
||
*/
|
||
async FluxForgeImage(task: TaskModal.Task): Promise<void> {
|
||
let sdSetting = undefined
|
||
try {
|
||
// 开始生图
|
||
sdSetting = await this.GetSDSetting()
|
||
|
||
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId);
|
||
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskDetail.bookTaskId);
|
||
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId);
|
||
|
||
let prompt = bookTaskDetail.prompt;
|
||
let url = sdSetting.setting.webui_api_url
|
||
if (url.endsWith('/')) {
|
||
url = url + "sdapi/v1/txt2img"
|
||
} else {
|
||
url = url + "/sdapi/v1/txt2img"
|
||
}
|
||
if (!isEmpty(sdSetting.webui.prompt)) {
|
||
prompt = sdSetting.webui.prompt + ', ' + prompt
|
||
}
|
||
|
||
// 判断当前是不是有开修脸修手
|
||
let ADetailer = {
|
||
args: sdSetting.adetailer
|
||
}
|
||
|
||
// 种子默认 -1,随机
|
||
let seed = -1;
|
||
let body = {
|
||
scheduler: 'Simple',
|
||
prompt: prompt,
|
||
seed: seed,
|
||
sampler_name: sdSetting.webui.sampler_name,
|
||
// 提示词相关性
|
||
cfg_scale: sdSetting.webui.cfg_scale,
|
||
distilled_cfg_scale: 3.5,
|
||
width: sdSetting.webui.width,
|
||
height: sdSetting.webui.height,
|
||
batch_size: sdSetting.setting.batch_size,
|
||
steps: sdSetting.webui.steps,
|
||
save_images: false,
|
||
tiling: false,
|
||
override_settings_restore_afterwards: true
|
||
}
|
||
|
||
if (bookTaskDetail.adetailer) {
|
||
body['alwayson_scripts'] = {
|
||
"ADetailer": ADetailer
|
||
}
|
||
}
|
||
|
||
const response = await axios.post(url, body)
|
||
// TODO 对SD图片种子的一些处理,待定
|
||
// let info = JSON.parse(res.data.info)
|
||
// if (seed == -1) {
|
||
// seed = info.seed
|
||
// }
|
||
let images = response.data.images
|
||
let SdOriginalImage = path.join(book.bookFolderPath, 'data/SdOriginalImage');
|
||
await CheckFolderExistsOrCreate(SdOriginalImage);
|
||
let outputFolder = bookTask.imageFolder;
|
||
await CheckFolderExistsOrCreate(outputFolder);
|
||
let inputFolder = path.join(book.bookFolderPath, 'tmp/input')
|
||
await CheckFolderExistsOrCreate(inputFolder);
|
||
|
||
let subImagePath = []
|
||
let outImagePath = ''
|
||
// 开始写出图片
|
||
for (let i = 0; i < images.length; i++) {
|
||
const element = images[i];
|
||
// 包含info信息的图片地址
|
||
let infoImgPath = path.join(SdOriginalImage, `info_${bookTaskDetail.name}_${new Date().getTime()}_${i}.png`)
|
||
// 不包含info信息的图片地址
|
||
let imgPath = path.join(SdOriginalImage, `${bookTaskDetail.name}_${new Date().getTime()}_${i}.png`)
|
||
await Base64ToFile(element, infoImgPath)
|
||
// 这边去图片信息
|
||
await DeleteFileExifData(path.join(define.package_path, 'exittool/exiftool.exe'), infoImgPath, imgPath);
|
||
// 写出去
|
||
if (bookTask.name == 'output_00001') {
|
||
// 复制一个到input
|
||
let inputImgPath = path.join(inputFolder, `${bookTaskDetail.name}.png`)
|
||
await CopyFileOrFolder(imgPath, inputImgPath)
|
||
}
|
||
if (i == 0) {
|
||
// 复制到对应的文件夹里面
|
||
let outPath = path.join(outputFolder, `${bookTaskDetail.name}.png`)
|
||
await CopyFileOrFolder(imgPath, outPath)
|
||
outImagePath = outPath
|
||
}
|
||
subImagePath.push(imgPath)
|
||
}
|
||
// 修改数据库
|
||
await this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetail.id, {
|
||
outImagePath: path.relative(define.project_path, outImagePath),
|
||
subImagePath: subImagePath.map((item) => path.relative(define.project_path, item))
|
||
})
|
||
await this.bookServiceBasic.UpdateTaskStatus({
|
||
id: task.id,
|
||
status: BookBackTaskStatus.DONE
|
||
});
|
||
let resp = {
|
||
mjApiUrl: url,
|
||
progress: 100,
|
||
category: MJImageType.FLUX_FORGE,
|
||
imageClick: subImagePath.join(','),
|
||
imageShow: subImagePath.join(','),
|
||
messageId: "",
|
||
action: MJAction.IMAGINE,
|
||
status: "success",
|
||
subImagePath: subImagePath,
|
||
outImagePath: outImagePath,
|
||
message: "FLUX FORGE 生成图片成功"
|
||
}
|
||
await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, resp)
|
||
global.newWindow[0].win.webContents.send(task.messageName, {
|
||
code: 1,
|
||
message: "FLUX FORGE 生成图片成功",
|
||
data: {
|
||
...resp,
|
||
id: bookTaskDetail.id
|
||
}
|
||
})
|
||
} catch (error) {
|
||
let errorMsg = "FLUX FORGE 生成图片失败,错误信息如下:" + error.toString()
|
||
|
||
await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, {
|
||
mjApiUrl: sdSetting ? sdSetting.setting.webui_api_url : "",
|
||
progress: 0,
|
||
category: MJImageType.FLUX_FORGE,
|
||
imageClick: "",
|
||
imageShow: "",
|
||
messageId: "",
|
||
action: MJAction.IMAGINE,
|
||
status: "error",
|
||
message: errorMsg
|
||
})
|
||
await this.bookServiceBasic.UpdateTaskStatus({
|
||
id: task.id,
|
||
status: BookBackTaskStatus.FAIL,
|
||
errorMessage: errorMsg
|
||
});
|
||
|
||
global.newWindow[0].win.webContents.send(task.messageName, {
|
||
code: 0,
|
||
message: errorMsg,
|
||
data: {
|
||
status: 'error',
|
||
message: errorMsg,
|
||
id: task.bookTaskDetailId
|
||
}
|
||
})
|
||
|
||
throw error
|
||
}
|
||
}
|
||
//#endregion
|
||
|
||
|
||
//#region flux api
|
||
|
||
/**
|
||
* 发送 FLUX API 请求
|
||
* @param url 请求的地址
|
||
* @param key 请求的key
|
||
* @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 }, {
|
||
headers: {
|
||
Authorization: 'Bearer ' + key
|
||
}
|
||
})
|
||
|
||
if (response.data && response.data.data && response.data.data.length > 0) {
|
||
return response.data.data[0].url
|
||
} else {
|
||
return undefined
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 调用 FLUX API 生成图片
|
||
* @param task
|
||
*/
|
||
async FluxAPIImage(task: TaskModal.Task): Promise<void> {
|
||
let sdSetting = undefined
|
||
try {
|
||
let bookTaskDetail = await this.bookServiceBasic.GetBookTaskDetailDataById(task.bookTaskDetailId);
|
||
let bookTask = await this.bookServiceBasic.GetBookTaskDataById(bookTaskDetail.bookTaskId);
|
||
let book = await this.bookServiceBasic.GetBookDataById(bookTask.bookId);
|
||
sdSetting = await this.GetSDSetting()
|
||
await this.gptService.RefreshGptSetting();
|
||
|
||
let prompt = bookTaskDetail.prompt;
|
||
let requestUrl = this.gptService.gptUrl
|
||
let uil = new URL(requestUrl);
|
||
let url = `${uil.protocol}//${uil.hostname}` + "/v1/images/generations"
|
||
|
||
if (!isEmpty(sdSetting.webui.prompt)) {
|
||
prompt = sdSetting.webui.prompt + ', ' + prompt
|
||
}
|
||
let size = `${sdSetting.webui.width}x${sdSetting.webui.height}`
|
||
let model = sdSetting.flux.model
|
||
// 一次请求生成一张 多个请求
|
||
|
||
let SdOriginalImage = path.join(book.bookFolderPath, 'data/SdOriginalImage');
|
||
await CheckFolderExistsOrCreate(SdOriginalImage);
|
||
let outputFolder = bookTask.imageFolder;
|
||
await CheckFolderExistsOrCreate(outputFolder);
|
||
let inputFolder = path.join(book.bookFolderPath, 'tmp/input')
|
||
await CheckFolderExistsOrCreate(inputFolder);
|
||
|
||
let outImagePath = ''
|
||
let subImagePath = []
|
||
|
||
let batchSize = sdSetting.setting.batch_size;
|
||
for (let i = 0; i < batchSize; i++) {
|
||
const element = batchSize;
|
||
let imageUrl = await this.FluxAPIImageRequest(url, this.gptService.gptApiKey, {
|
||
model: model,
|
||
prompt: prompt,
|
||
size: size
|
||
})
|
||
// 这边开始处理返回的数据
|
||
if (isEmpty(imageUrl)) {
|
||
throw new Error('FLUX 生图返回的图片地址为空')
|
||
}
|
||
// 下载指定的文件
|
||
let base64 = await GetImageBase64(imageUrl)
|
||
// 将base64 写出
|
||
let imgPath = path.join(SdOriginalImage, `${bookTaskDetail.name}_${new Date().getTime()}_${i}.png`)
|
||
await Base64ToFile(base64, imgPath);
|
||
|
||
// 写出去
|
||
if (bookTask.name == 'output_00001') {
|
||
// 复制一个到input
|
||
let inputImgPath = path.join(inputFolder, `${bookTaskDetail.name}.png`)
|
||
await CopyFileOrFolder(imgPath, inputImgPath)
|
||
}
|
||
if (i == 0) {
|
||
// 复制到对应的文件夹里面
|
||
let outPath = path.join(outputFolder, `${bookTaskDetail.name}.png`)
|
||
await CopyFileOrFolder(imgPath, outPath)
|
||
outImagePath = outPath
|
||
}
|
||
subImagePath.push(imgPath)
|
||
}
|
||
|
||
// 结束 开始返回
|
||
// 修改数据库
|
||
await this.bookServiceBasic.UpdateBookTaskDetail(bookTaskDetail.id, {
|
||
outImagePath: path.relative(define.project_path, outImagePath),
|
||
subImagePath: subImagePath.map((item) => path.relative(define.project_path, item))
|
||
})
|
||
await this.bookServiceBasic.UpdateTaskStatus({
|
||
id: task.id,
|
||
status: BookBackTaskStatus.DONE
|
||
});
|
||
let resp = {
|
||
mjApiUrl: url,
|
||
progress: 100,
|
||
category: MJImageType.FLUX_API,
|
||
imageClick: subImagePath.join(','),
|
||
imageShow: subImagePath.join(','),
|
||
messageId: "",
|
||
action: MJAction.IMAGINE,
|
||
status: "success",
|
||
subImagePath: subImagePath,
|
||
outImagePath: outImagePath,
|
||
message: "FLUX API 生成图片成功"
|
||
}
|
||
await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, resp)
|
||
global.newWindow[0].win.webContents.send(task.messageName, {
|
||
code: 1,
|
||
message: "FLUX API 生成图片成功",
|
||
data: {
|
||
...resp,
|
||
id: bookTaskDetail.id
|
||
}
|
||
})
|
||
|
||
} catch (error) {
|
||
let errorMsg = "FLUX API 生成图片失败,错误信息如下:" + error.toString()
|
||
await this.bookServiceBasic.UpdateBookTaskDetailMjMessage(task.bookTaskDetailId, {
|
||
mjApiUrl: "",
|
||
progress: 0,
|
||
category: MJImageType.FLUX_API,
|
||
imageClick: "",
|
||
imageShow: "",
|
||
messageId: "",
|
||
action: MJAction.IMAGINE,
|
||
status: "error",
|
||
message: errorMsg,
|
||
})
|
||
|
||
global.newWindow[0].win.webContents.send(task.messageName, {
|
||
code: 0,
|
||
message: errorMsg,
|
||
data: {
|
||
status: 'error',
|
||
message: errorMsg,
|
||
id: task.bookTaskDetailId
|
||
}
|
||
})
|
||
throw error
|
||
}
|
||
}
|
||
} |