lq1405 51deef0c09 V3.2.0
修复聚合推文剪映抽帧时,导入srt没有输入
(聚合推文) SD 反推,MJ反推添加单句洗稿功能
(聚合推文)小说任务列表,添加进入出图文件夹的菜单
(聚合推文)新增一键出图
(聚合推文)新增默认出图方式设置,
5.1只对原创生效,SD反推默认是SD,MJ反推默认是MJ
5.2添加批次的默认出图方式同 a点说明,但是选择了 选择旧批次,新批次的默认出图方式会继承
后台任务,完成后台任务的可视化界面
(聚合推文)小说详情界面,添加小说和批次名称显示
2024-11-02 18:18:55 +08:00

347 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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