V2.2.4 版本
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
ipcMain
|
||||
} from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { Tools } from "../tools";
|
||||
import path from "path";
|
||||
import { errorMessage, successMessage } from "../generalTools";
|
||||
let tools = new Tools();
|
||||
|
||||
function GlobalIpc() {
|
||||
|
||||
/**
|
||||
* 将传入的文件地址修改为base64
|
||||
*/
|
||||
ipcMain.handle(DEFINE_STRING.GET_FILE_BASE64, async (event, value) => {
|
||||
try {
|
||||
value = path.normalize(value)
|
||||
//检查文件或者时文件夹是不是存在
|
||||
let isExists = await tools.checkExists(value);
|
||||
console.log("isExists", value, isExists);
|
||||
// 获取文件,将其转换为base64
|
||||
if (!isExists) {
|
||||
throw new Error("文件不存在");
|
||||
}
|
||||
return successMessage(await tools.readFileBase64(value));
|
||||
} catch (error) {
|
||||
return errorMessage("获取文件失败" + error)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
GlobalIpc
|
||||
}
|
||||
@@ -12,6 +12,12 @@ function SdIpc() {
|
||||
|
||||
// 获取图片样式菜单
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_MENU, async (event) => await sd.GetImageStyleMenu());
|
||||
|
||||
// 加载当前链接的SD服务数据
|
||||
ipcMain.handle(DEFINE_STRING.SD.LOAD_SD_SERVICE_DATA, async (event, value) => await sd.LoadSDServiceData(value));
|
||||
|
||||
// 文生图,单张
|
||||
ipcMain.handle(DEFINE_STRING.SD.TXT2IMG, async (event, value) => await sd.txt2img(value));
|
||||
}
|
||||
export {
|
||||
SdIpc
|
||||
|
||||
@@ -9,6 +9,7 @@ import path from 'path'
|
||||
import sharp from 'sharp'
|
||||
import { define } from "../../define/define";
|
||||
import { AwesomeRegx } from "awesome-js";
|
||||
import { checkStringValueAddSuffix } from "../generalTools";
|
||||
|
||||
/**
|
||||
* MJ原创生图的类
|
||||
@@ -196,13 +197,15 @@ export class MJOriginalImageGenerate {
|
||||
|
||||
// 判断存放的文件夹是不是存在,不存在的话创建
|
||||
let outputDir = path.join(this.global.config.project_path, `data\\MJOriginalImage`);
|
||||
|
||||
await this.tools.checkFolderExistsOrCreate(outputDir);
|
||||
|
||||
let fileExist = await this.tools.checkExists(outputDir);
|
||||
if (!fileExist) {
|
||||
await this.tools.createDirectory(outputDir);
|
||||
}
|
||||
// 判断该当前tmp\output_crop_00001文件夹是不是存在,不存在创建
|
||||
let output_crop_00001 = path.join(this.global.config.project_path, `tmp\\output_crop_00001`);
|
||||
await this.tools.checkFolderExistsOrCreate(output_crop_00001);
|
||||
|
||||
|
||||
// 检查this.global中是不是又mj队列,没有的话创建一个
|
||||
if (!this.global.mjGenerateQuene) {
|
||||
@@ -210,7 +213,7 @@ export class MJOriginalImageGenerate {
|
||||
}
|
||||
|
||||
let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false);
|
||||
// let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data);
|
||||
let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data);
|
||||
|
||||
// 替换风格的逻辑
|
||||
let current_task = null;
|
||||
@@ -219,11 +222,17 @@ export class MJOriginalImageGenerate {
|
||||
const element = data[i];
|
||||
let tasK_id = `${batch}_${element.name}_${element.id}`;
|
||||
|
||||
let old_prompt = element.prompt;
|
||||
// 拼接提示词
|
||||
// 图生图的链接
|
||||
// 获取风格词
|
||||
let prompt = " " + image_styles + old_prompt;
|
||||
|
||||
|
||||
this.global.mjGenerateQuene.enqueue(async () => {
|
||||
try {
|
||||
this.global.mjGenerateQuene.setCurrentCreateItem(element)
|
||||
// 开始进行mj生图
|
||||
let prompt = element.prompt;
|
||||
current_task = element.name;
|
||||
// 判断窗口是不是开启
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let fspromises = require("fs").promises;
|
||||
import { MD5 } from "crypto-js";
|
||||
import { ImageStyleDefine } from "../../define/iamgeStyleDefine";
|
||||
|
||||
|
||||
@@ -64,26 +63,21 @@ export class OriginalImageGenerate {
|
||||
|
||||
let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false);
|
||||
let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data);
|
||||
//
|
||||
console.log(image_styles);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let adetailer = element.adetailer;
|
||||
|
||||
let imageJson = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8'));
|
||||
// let prompt = image_styles + sd_setting.webui.prompt + ',' + element.prompt;
|
||||
// // 添加前缀
|
||||
// if (prefix_prompt) {
|
||||
// prompt = prefix_prompt + ',' + prompt;
|
||||
// }
|
||||
// // 添加后缀
|
||||
// if (suffix_prompt) {
|
||||
// prompt = prompt + ',' + suffix_prompt;
|
||||
// }
|
||||
let prompt = imageJson.webui_config.prompt;
|
||||
|
||||
|
||||
let prompt = sd_setting.webui.prompt + image_styles + ',' + element.prompt;
|
||||
// 添加前缀
|
||||
if (prefix_prompt) {
|
||||
prompt = prefix_prompt + ',' + prompt;
|
||||
}
|
||||
// 添加后缀
|
||||
if (suffix_prompt) {
|
||||
prompt = prompt + ',' + suffix_prompt;
|
||||
}
|
||||
// let prompt = imageJson.webui_config.prompt;
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
try {
|
||||
// 开始请求
|
||||
@@ -91,7 +85,7 @@ export class OriginalImageGenerate {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": imageJson.webui_config.negative_prompt,
|
||||
"seed": seed,
|
||||
"sampler_name": imageJson.webui_config.sampler_name,
|
||||
"sampler_name": sd_setting.webui.sampler_name,
|
||||
// 提示词相关性
|
||||
"cfg_scale": sd_setting.webui.cfg_scale,
|
||||
"width": sd_setting.webui.width,
|
||||
@@ -258,7 +252,15 @@ export class OriginalImageGenerate {
|
||||
if (element.outImagePath && file_regex.test(element.outImagePath)) {
|
||||
// 删除 "file://" 开头
|
||||
element.outImagePath = decodeURI(element.outImagePath);
|
||||
element.outImagePath = element.outImagePath.replace(/^file:\/\//, '').replace(/\?time=.*$/, '');
|
||||
// 判断element.outImagePath是不是不是以file://开头的,是的话,删除
|
||||
if (element.outImagePath.startsWith("file://")) {
|
||||
element.outImagePath = element.outImagePath.substring(7);
|
||||
}
|
||||
element.outImagePath = element.outImagePath.replace(/\?time=.*$/, '');
|
||||
// 判断element.outImagePath是不是以/开头的,是的话,删除
|
||||
if (element.outImagePath.startsWith("/")) {
|
||||
element.outImagePath = element.outImagePath.substring(1);
|
||||
}
|
||||
}
|
||||
if (element.subImagePath && element.subImagePath.length > 0) {
|
||||
for (let j = 0; j < element.subImagePath.length; j++) {
|
||||
@@ -308,9 +310,7 @@ export class OriginalImageGenerate {
|
||||
data: path.join(this.global.config.project_path, "tmp/input_crop")
|
||||
}
|
||||
}
|
||||
|
||||
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let name = String(element.no).padStart(5, '0') + ".png";
|
||||
|
||||
+142
-42
@@ -6,19 +6,112 @@ import { ImageStyleDefine } from "../../define/iamgeStyleDefine";
|
||||
import { cloneDeep } from 'lodash';
|
||||
let fspromises = require("fs").promises;
|
||||
const sharp = require('sharp');
|
||||
// const {
|
||||
// createCanvas,
|
||||
// loadImage
|
||||
// } = require('canvas');
|
||||
import { SdSettingDefine } from "../../define/setting/sdSettingDefine";
|
||||
import { PublicMethod } from "./publicMethod";
|
||||
import { Tools } from "../tools";
|
||||
import { errorMessage, successMessage } from "../generalTools";
|
||||
import { SdApi } from "../../api/sdApi";
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
export class SD {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.pm = new PublicMethod(global);
|
||||
this.tools = new Tools();
|
||||
this.sdApi = new SdApi();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前SD服务器所有的lora信息
|
||||
*/
|
||||
async GetAllLoras(baseURL = null) {
|
||||
try {
|
||||
let data = await this.sdApi.getAllLoras(baseURL);
|
||||
return successMessage(data);
|
||||
} catch (error) {
|
||||
return errorMessage(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的checkpoint模型
|
||||
* @param {*} baseURL
|
||||
* @returns
|
||||
*/
|
||||
async GetAllSDModel(baseURL = null) {
|
||||
try {
|
||||
let data = await this.sdApi.getAllSDModel(baseURL);
|
||||
return successMessage(data);
|
||||
} catch (error) {
|
||||
return errorMessage(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的采样器
|
||||
* @param {*} baseURL
|
||||
* @returns
|
||||
*/
|
||||
async GetAllSamplers(baseURL = null) {
|
||||
try {
|
||||
let data = await this.sdApi.getAllSamplers(baseURL);
|
||||
return successMessage(data);
|
||||
} catch (error) {
|
||||
return errorMessage(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载所有的SD数据
|
||||
* @param {*} baseURL
|
||||
* @returns
|
||||
*/
|
||||
async LoadSDServiceData(baseURL = null) {
|
||||
try {
|
||||
// 加载大模型
|
||||
let sd_model = await this.GetAllSDModel(baseURL);
|
||||
// 往sd_model中添加一个默认的选项
|
||||
sd_model.data.data.unshift({
|
||||
title: "无",
|
||||
name: "无",
|
||||
description: "无",
|
||||
})
|
||||
// 加载Lora
|
||||
let lora = await this.GetAllLoras(baseURL);
|
||||
lora.data.data.unshift({
|
||||
Key: "无",
|
||||
name: "无",
|
||||
description: "无",
|
||||
})
|
||||
// 加载采样器
|
||||
let sampler = await this.GetAllSamplers(baseURL);
|
||||
sampler.data.data.unshift({
|
||||
name: "无",
|
||||
description: "无",
|
||||
})
|
||||
|
||||
if (!(sd_model.code & lora.code & sampler.code)) {
|
||||
throw new Error("获取SD数据错误,请检查SD WEBUI链接!");
|
||||
}
|
||||
|
||||
for (let i = 0; i < lora.data.data.length; i++) {
|
||||
delete lora.data.data[i].metadata;
|
||||
}
|
||||
let data = {
|
||||
sd_model: sd_model.data.data,
|
||||
lora: lora.data.data,
|
||||
sampler: sampler.data.data
|
||||
}
|
||||
// 处理当前获取的数据,保存到配置文件中
|
||||
await SdSettingDefine.SavePropertyValue("sd_model", data.sd_model);
|
||||
await SdSettingDefine.SavePropertyValue("lora", data.lora);
|
||||
await SdSettingDefine.SavePropertyValue("sampler", data.sampler);
|
||||
|
||||
return successMessage(data);
|
||||
|
||||
} catch (error) {
|
||||
return errorMessage(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +127,10 @@ export class SD {
|
||||
data: style
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
message: "不可能出现错误"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +202,37 @@ export class SD {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单张生图
|
||||
* @param {*} value 0 生图的参数,1 图片的表示,用于保存 ,2 baseUrl
|
||||
* @returns
|
||||
*/
|
||||
async txt2img(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let data = value[0];
|
||||
let res = await this.sdApi.txt2img(data);
|
||||
// 将base· 64的图片转换为图片
|
||||
// 将当前的图片保存到指定的文件夹中,然后返回文件路径,并且可以复制到指定的文件,删除exif信息
|
||||
let image_paths = [];
|
||||
for (let i = 0; res.data.images && i < res.data.images.length; i++) {
|
||||
const element = res.data.images[i];
|
||||
let image_data = {
|
||||
base64: element
|
||||
}
|
||||
// 将保存图片添加到队列中
|
||||
let image_name = `sd_${Date.now()}_${uuidv4()}.png`;
|
||||
let image_path = path.join(define.temp_sd_image, image_name);
|
||||
image_path = await this.tools.saveBase64ToImage(element, image_path);
|
||||
image_data["image_path"] = image_path;
|
||||
image_paths.push(image_data);
|
||||
}
|
||||
return successMessage(image_paths);
|
||||
} catch (error) {
|
||||
return errorMessage("生图失败,错误信息如下:" + error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一次图片的方法。可以区分模式
|
||||
* @param {图片名称 } image
|
||||
@@ -120,7 +247,6 @@ export class SD {
|
||||
let image_json = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8'));
|
||||
let image_path = "";
|
||||
let target_image_path = "";
|
||||
|
||||
if (image_json.name) {
|
||||
image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/tmp_${image_json.name}`)
|
||||
target_image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/${image_json.name}`)
|
||||
@@ -128,25 +254,20 @@ export class SD {
|
||||
image_path = image.replaceAll("input_crop", task_list.out_folder).split(".png")[0] + "_tmp.png";
|
||||
target_image_path = image.replaceAll("input_crop", task_list.out_folder);
|
||||
}
|
||||
|
||||
// let prompt = "";
|
||||
// // 拼接提示词
|
||||
// if (task_list.image_style != null) {
|
||||
// prompt += `((${task_list.image_style})),`;
|
||||
// }
|
||||
// if (task_list.lora != null) {
|
||||
// prompt += `${task_list.lora},`;
|
||||
// }
|
||||
// let image_styles = await ImageStyleDefine.getImageStyleStringByIds(task_list.image_style_list ? task_list.image_style_list : []);
|
||||
|
||||
// prompt = `${prompt}, ${image_styles}, ${imageJson.webui_config.prompt}`;
|
||||
let prompt = imageJson.webui_config.prompt;
|
||||
|
||||
let image_styles = await ImageStyleDefine.getImageStyleStringByIds(task_list.image_style_list ? task_list.image_style_list : []);
|
||||
let prompt = sd_setting.webui.prompt + image_styles;
|
||||
// 拼接提示词
|
||||
if (task_list.image_style != null) {
|
||||
prompt += `((${task_list.image_style})), `;
|
||||
}
|
||||
if (task_list.lora != null) {
|
||||
prompt += `${task_list.lora}, `;
|
||||
}
|
||||
prompt += imageJson.webui_config.prompt;
|
||||
// 判断当前是不是有开修脸修手
|
||||
let ADetailer = {
|
||||
args: sd_setting.adetailer
|
||||
};
|
||||
|
||||
if (model == "img2img") {
|
||||
let web_api = this.global.config.webui_api_url + 'sdapi/v1/img2img'
|
||||
let sd_config = imageJson["webui_config"];
|
||||
@@ -154,7 +275,6 @@ export class SD {
|
||||
sd_config.seed = seed;
|
||||
let im = await fspromises.readFile(image, 'binary');
|
||||
sd_config.init_images = [new Buffer.from(im, 'binary').toString('base64')];
|
||||
|
||||
if (imageJson.adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
@@ -163,13 +283,11 @@ export class SD {
|
||||
}
|
||||
sd_config.height = sd_setting.webui.height;
|
||||
sd_config.width = sd_setting.webui.width;
|
||||
|
||||
const response = await axios.post(web_api, sd_config);
|
||||
let info = JSON.parse(response.data.info);
|
||||
if (seed == -1) {
|
||||
seed = info.seed;
|
||||
}
|
||||
|
||||
// 目前是单图出图
|
||||
let images = response.data.images;
|
||||
let imageData = Buffer.from(images[0].split(",", 1)[0], 'base64');
|
||||
@@ -253,22 +371,4 @@ export class SD {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*文生图
|
||||
* @param {SD 请求的地址} url
|
||||
* @param {SD请求的body} body
|
||||
*/
|
||||
async txt2img(url, body) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*图生图
|
||||
* @param {SD 请求的地址} url
|
||||
* @param {SD请求的body} body
|
||||
*/
|
||||
async img2img(url, body) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -662,12 +662,12 @@ export class ClipDraft {
|
||||
* 通过 key_frame 返回关键帧数据
|
||||
* @param {*} key_frame 关键帧配置
|
||||
*/
|
||||
async GetFrameData(key_frame) {
|
||||
if (key_frame.key_frame == "KFTypePositionY") {
|
||||
async GetFrameData(key_frame_key, key_frame) {
|
||||
if (key_frame_key == "KFTypePositionY") {
|
||||
return key_frame.up_down_key_frame;
|
||||
} else if (key_frame.key_frame == "KFTypePositionX") {
|
||||
} else if (key_frame_key == "KFTypePositionX") {
|
||||
return key_frame.left_right_key_frame;
|
||||
} else if (key_frame.key_frame == "KFTypeScale") {
|
||||
} else if (key_frame_key == "KFTypeScale") {
|
||||
return key_frame.scale_key_frame;
|
||||
} else {
|
||||
return {
|
||||
@@ -683,7 +683,8 @@ export class ClipDraft {
|
||||
*/
|
||||
async AddKeyFarme() {
|
||||
let img_nodes = (await this.find_draft_node(this.draft_json.tracks, "type", "video")).segments;
|
||||
let key_frame_tmp_data = JSON.parse(await fspromises.readFile(define.add_keyframe_tmp_path, "utf-8"));
|
||||
let key_frame_tmp_data = await fspromises.readFile(define.add_keyframe_tmp_path, "utf-8");
|
||||
|
||||
// 添加关键帧
|
||||
// 将最后一个数据修改为背景音乐的最后时间
|
||||
this.srt_information[this.srt_information.length - 1].end_time = this.audios_duration_time / 1000;
|
||||
@@ -693,34 +694,42 @@ export class ClipDraft {
|
||||
if (key_frame_setting == null) {
|
||||
return;
|
||||
}
|
||||
let key_frame_pos = await this.GetFrameData(key_frame_setting);
|
||||
let isFixedSpeed = key_frame_setting.isFixedSpeed;
|
||||
let key_frame_time = key_frame_setting.key_frame_time * 1000000;
|
||||
let isDown = true;
|
||||
let scale_rate = key_frame_pos.default_scale / 100;
|
||||
|
||||
// 获取通用的关键帧配置,然后添加到每一个图片上面(可以设置时间。当前图片的持续实现小于设置的时间。会计算不要过快)
|
||||
for (let i = 0; i < img_nodes.length; i++) {
|
||||
let element = img_nodes[i];
|
||||
let image_duartion = cloneDeep(element.source_timerange.duration);
|
||||
let key_frame = key_frame_setting.key_frame;
|
||||
// 做随机处理
|
||||
let real_key_frame = key_frame;
|
||||
let key_frame_list = ["KFTypePositionY", "KFTypePositionX", "KFTypeScale"];
|
||||
if (key_frame == "KFTypeRandom") {
|
||||
// 随机获取 key_frame_list 里面的一个数据
|
||||
const randomIndex = Math.floor(Math.random() * key_frame_list.length);
|
||||
real_key_frame = key_frame_list[randomIndex];
|
||||
}
|
||||
|
||||
let key_frame_pos = await this.GetFrameData(real_key_frame, key_frame_setting);
|
||||
|
||||
let key_frame_time = key_frame_setting.key_frame_time * 1000000;
|
||||
let scale_rate = key_frame_pos.default_scale / 100;
|
||||
|
||||
let up_pos = Math.abs(key_frame_pos.start_position);
|
||||
let down_pos = Math.abs(key_frame_pos.end_position);
|
||||
let key_frame_tmp = JSON.parse(key_frame_tmp_data)
|
||||
|
||||
if (key_frame_setting.key_frame == "KFTypePositionY") {
|
||||
|
||||
|
||||
if (real_key_frame == "KFTypePositionY") {
|
||||
// 勾选了匀速。需要计算时间(计算比例)
|
||||
|
||||
if (isFixedSpeed && image_duartion < key_frame_time) {
|
||||
let time_rate = image_duartion / key_frame_time;
|
||||
up_pos = up_pos * time_rate;
|
||||
down_pos = down_pos * time_rate;
|
||||
}
|
||||
let key_frame_tmp = cloneDeep(key_frame_tmp_data)
|
||||
let up_pos_rate = isDown ? (up_pos / this.draft_json.canvas_config.height) : (0 - up_pos / this.draft_json.canvas_config.height)
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id = uuidv4(); // Corrected assignment
|
||||
key_frame_tmp.keyframe_list[0].values = [up_pos_rate];
|
||||
|
||||
let dow_pos_rate = isDown ? (0 - down_pos / this.draft_json.canvas_config.height) : (down_pos / this.draft_json.canvas_config.height)
|
||||
@@ -728,22 +737,20 @@ export class ClipDraft {
|
||||
key_frame_tmp.keyframe_list[1].time_offset = image_duartion;
|
||||
key_frame_tmp.keyframe_list[1].values = [dow_pos_rate];
|
||||
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame;
|
||||
key_frame_tmp.property_type = real_key_frame;
|
||||
|
||||
// 修改缩放倍率
|
||||
element.clip.scale.x = scale_rate;
|
||||
element.clip.scale.y = scale_rate;
|
||||
element.clip.transform.y = dow_pos_rate;
|
||||
isDown = !isDown;
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
} else if (key_frame_setting.key_frame == "KFTypePositionX") {
|
||||
} else if (real_key_frame == "KFTypePositionX") {
|
||||
// 勾选了匀速。需要计算时间(计算比例)
|
||||
if (isFixedSpeed && image_duartion < key_frame_time) {
|
||||
let time_rate = image_duartion / key_frame_time;
|
||||
up_pos = up_pos * time_rate;
|
||||
down_pos = down_pos * time_rate;
|
||||
}
|
||||
let key_frame_tmp = cloneDeep(key_frame_tmp_data)
|
||||
let up_pos_rate = isDown ? (up_pos / this.draft_json.canvas_config.width) : (0 - up_pos / this.draft_json.canvas_config.width)
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
@@ -754,16 +761,15 @@ export class ClipDraft {
|
||||
key_frame_tmp.keyframe_list[1].time_offset = image_duartion;
|
||||
key_frame_tmp.keyframe_list[1].values = [dow_pos_rate];
|
||||
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame;
|
||||
key_frame_tmp.property_type = real_key_frame;
|
||||
|
||||
// 修改缩放倍率
|
||||
element.clip.scale.x = scale_rate;
|
||||
element.clip.scale.y = scale_rate;
|
||||
element.clip.transform.x = dow_pos_rate;
|
||||
isDown = !isDown;
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
}
|
||||
else if (key_frame_setting.key_frame == "KFTypeScale") {
|
||||
else if (real_key_frame == "KFTypeScale") {
|
||||
if (isFixedSpeed && image_duartion < key_frame_time) {
|
||||
let time_rate = image_duartion / key_frame_time;
|
||||
// 计算方式和上面的不同
|
||||
@@ -774,7 +780,6 @@ export class ClipDraft {
|
||||
}
|
||||
|
||||
// 修改上面的数据,添加Y轴缩放
|
||||
let key_frame_tmp = cloneDeep(key_frame_tmp_data)
|
||||
let up_pos_rate = isDown ? up_pos / 100 : down_pos / 100;
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
@@ -785,7 +790,7 @@ export class ClipDraft {
|
||||
key_frame_tmp.keyframe_list[1].time_offset = image_duartion;
|
||||
key_frame_tmp.keyframe_list[1].values = [dow_pos_rate];
|
||||
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame + "X";
|
||||
key_frame_tmp.property_type = real_key_frame + "X";
|
||||
|
||||
// 修改上面的数据,添加Y轴缩放
|
||||
// 修改缩放倍率
|
||||
@@ -794,15 +799,15 @@ export class ClipDraft {
|
||||
element.clip.transform.x = 0;
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
|
||||
key_frame_tmp = cloneDeep(key_frame_tmp)
|
||||
|
||||
key_frame_tmp = JSON.parse(JSON.stringify(key_frame_tmp))
|
||||
key_frame_tmp.id = uuidv4();
|
||||
key_frame_tmp.keyframe_list[0].id == uuidv4();
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
key_frame_tmp.property_type = key_frame_setting.key_frame + "Y";
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
|
||||
key_frame_tmp.property_type = real_key_frame + "Y";
|
||||
isDown = !isDown;
|
||||
}
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ const sharp = require('sharp');
|
||||
const execAsync = util.promisify(exec);
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let fspromises = require("fs").promises;
|
||||
import { MD5 } from "crypto-js";
|
||||
import { ImageSetting } from "../../define/setting/imageSetting";
|
||||
|
||||
|
||||
@@ -257,7 +256,7 @@ export class ImageGenerate {
|
||||
if (images.length <= 0) {
|
||||
throw new Error("未检测到抽帧图片。请检查");
|
||||
}
|
||||
if (images.length > auto_save_image.save_match_count) {
|
||||
if (auto_save_image.save_match_count && auto_save_image.auto_match && images.length > auto_save_image.save_match_count) {
|
||||
png_files = await this.tools.getFilesWithExtensions(auto_save_image.main_save_folder, '.png');
|
||||
}
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
const axios = require('axios');
|
||||
// import { Midjourney as mApi} from "midjourney";
|
||||
// const MJapi2 = require('midjourney');
|
||||
// const { midjourney } = require('midjourney')
|
||||
// const Midjourney = require('midjourney');
|
||||
// ES5 的模块引入方式
|
||||
const fetch = require("node-fetch");
|
||||
|
||||
// ES6 的模块引入方式
|
||||
// import fetch from "node-fetch";
|
||||
|
||||
export class DiscordAPI {
|
||||
constructor(mj_setting) {
|
||||
// https://discord.com/api/v9/channels/1208362852482809939/messages?limit=20
|
||||
|
||||
+7
-3
@@ -134,7 +134,7 @@ async function ReGenerateImageOne(window, value) {
|
||||
|
||||
let image_styles = await ImageStyleDefine.getImageStyleStringByIds(value[1].image_style_list ? value[1].image_style_list : []);
|
||||
|
||||
let prompt = image_styles;
|
||||
let prompt = sd_setting.webui.prompt + image_styles;
|
||||
// 拼接提示词
|
||||
if (value[1].image_style != null) {
|
||||
prompt += `((${value[1].image_style})),`;
|
||||
@@ -143,6 +143,8 @@ async function ReGenerateImageOne(window, value) {
|
||||
prompt += `${value[1].lora},`;
|
||||
}
|
||||
prompt += value[1].prompt;
|
||||
|
||||
|
||||
let model = value[1].model;
|
||||
|
||||
// 判断当前是不是有开修脸修手
|
||||
@@ -624,8 +626,12 @@ async function SaveSDConfig(value) {
|
||||
try {
|
||||
let sd_config = JSON.parse((await fspromises.readFile(define.sd_setting, "utf-8")).toString());
|
||||
global.config.webui_api_url = value.webui_api_url || value.webui_api_url == '' ? value.webui_api_url : global.config.webui_api_url;
|
||||
|
||||
sd_config.setting.webui_api_url = value.webui_api_url || value.webui_api_url == "" ? value.webui_api_url : sd_config.setting.webui_api_url;
|
||||
sd_config.setting.type = value.type ? value.type : sd_config.setting.type;
|
||||
sd_config.setting.batch_size = value.batch_size ? value.batch_size : sd_config.setting.batch_size;
|
||||
sd_config.setting.style_weight = value.style_weight ? value.style_weight : sd_config.setting.style_weight;
|
||||
|
||||
sd_config.webui.prompt = value.prompt || value.prompt == "" ? value.prompt : sd_config.webui.prompt;
|
||||
sd_config.webui.negative_prompt = value.negative_prompt || value.negative_prompt == "" ? value.negative_prompt : sd_config.webui.negative_prompt;
|
||||
sd_config.webui.denoising_strength = value.denoising_strength || value.denoising_strength == "" ? value.denoising_strength : sd_config.webui.denoising_strength;
|
||||
@@ -635,8 +641,6 @@ async function SaveSDConfig(value) {
|
||||
sd_config.webui.height = value.height ? value.height : sd_config.webui.height;
|
||||
sd_config.webui.cfg_scale = value.cfg_scale ? value.cfg_scale : sd_config.webui.cfg_scale;
|
||||
sd_config.webui.adetailer = value.adetailer ? value.adetailer : sd_config.webui.adetailer;
|
||||
sd_config.setting.batch_size = value.batch_size ? value.batch_size : sd_config.setting.batch_size;
|
||||
sd_config.setting.style_weight = value.style_weight ? value.style_weight : sd_config.setting.style_weight;
|
||||
|
||||
await fspromises.writeFile(define.sd_setting, JSON.stringify(sd_config));
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的后缀
|
||||
* @param {*} value 要检查的字符串
|
||||
* @param {*} suffix 要添加的后缀
|
||||
* @returns
|
||||
*/
|
||||
function checkStringValueAddSuffix(value, suffix) {
|
||||
if (value && value !== null && value !== undefined && value !== '') {
|
||||
return value + suffix;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串的值是不是存在,不是null,不是undefined,不是空字符串,存在的话添加指定的前缀
|
||||
* @param {*} value 要检查的值
|
||||
* @param {*} prefix 要添加的前缀
|
||||
* @returns
|
||||
*/
|
||||
|
||||
function checkStringValueAddPrefix(value, prefix) {
|
||||
if (value && value !== null && value !== undefined && value !== '') {
|
||||
return prefix + value;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个函数,判断传入的字符串的值是不是存在,存在的话删除后面指定数量的字符
|
||||
* @param {*} value 要删除的字符串
|
||||
* @param {*} suffix 删除的后缀
|
||||
* @returns
|
||||
*/
|
||||
function checkStringValueDeleteSuffix(value, suffix) {
|
||||
// 增加一个判断,当前删除的数量是不是大于字符串的长度
|
||||
if (value && value !== null && value !== undefined && value !== '') {
|
||||
if (suffix.length > value.length) {
|
||||
return '';
|
||||
} else {
|
||||
return value.slice(0, value.length - suffix.length);
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个函数,判断传入的字符串的值是不是存在,存在的话删除前面指定数量的字符
|
||||
* @param {*} value 操作的字符串
|
||||
* @param {*} prefix 要删除的字符串
|
||||
* @returns
|
||||
*/
|
||||
function checkStringValueDeletePrefix(value, prefix) {
|
||||
// 增加一个判断,当前删除的数量是不是大于字符串的长度
|
||||
if (value && value !== null && value !== undefined && value !== '') {
|
||||
if (prefix.length > value.length) {
|
||||
return '';
|
||||
} else {
|
||||
return value.slice(prefix.length);
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 返回成功的消息,包含code,data,message
|
||||
* @param {*} code
|
||||
* @param {*} data
|
||||
* @param {*} message
|
||||
* @returns
|
||||
*/
|
||||
function successMessage(data, message = null) {
|
||||
return {
|
||||
code: 1,
|
||||
data: data,
|
||||
message: message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回失败的消息,包含code,message
|
||||
* @param {*} code
|
||||
* @param {*} message
|
||||
* @returns
|
||||
*/
|
||||
function errorMessage(message) {
|
||||
return {
|
||||
code: 0,
|
||||
message: message
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
checkStringValueAddSuffix,
|
||||
checkStringValueAddPrefix,
|
||||
checkStringValueDeletePrefix,
|
||||
checkStringValueDeleteSuffix,
|
||||
successMessage,
|
||||
errorMessage
|
||||
}
|
||||
+30
-10
@@ -1,3 +1,8 @@
|
||||
import fspromises from "fs/promises";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { version } from '../../package.json'
|
||||
|
||||
|
||||
import { app, shell, BrowserWindow, ipcMain, dialog, nativeTheme } from 'electron'
|
||||
import path, { join } from 'path'
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||
@@ -6,10 +11,6 @@ import { define } from '../define/define.js'
|
||||
import { func } from './func.js'
|
||||
import { AsyncQueue } from "./quene.js"
|
||||
import { DEFINE_STRING } from '../define/define_string.js'
|
||||
const fspromises = require("fs").promises;
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { version } = require('../../package.json')
|
||||
|
||||
import { Tools } from './tools.js'
|
||||
import { ImageGenerate } from './backPrompt/imageGenerate.js'
|
||||
import { Setting } from './setting/setting.js'
|
||||
@@ -27,12 +28,14 @@ import { OriginalImageGenerateIpc } from './IPCEvent/originalImageGenerateIpc'
|
||||
import { SdIpc } from './IPCEvent/sdIpc.js'
|
||||
import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js'
|
||||
import { MainIpc } from './IPCEvent/mainIpc.js'
|
||||
import { GlobalIpc } from "./IPCEvent/globalIpc.js";
|
||||
|
||||
let tools = new Tools();
|
||||
let imageGenerate = new ImageGenerate(global);
|
||||
let setting = new Setting(global);
|
||||
|
||||
|
||||
|
||||
async function InitData(gl) {
|
||||
let res = await setting.getSettingDafultData();
|
||||
gl.config = res;
|
||||
@@ -55,11 +58,17 @@ function removeIpcHandler(hash) {
|
||||
|
||||
async function createWindow(hash = "ShowMessage", data, url = null) {
|
||||
// Create the browser window.
|
||||
await InitData(global);
|
||||
global.currentHash = hash;
|
||||
// 判断当前是不是有设置的宽高,用的话记忆
|
||||
let isRe = global.config.window_wh_bm_remember && hash == "ShowMessage" && global.config.window_wh_bm;
|
||||
|
||||
|
||||
let mainWindow = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 670,
|
||||
width: isRe ? global.config.window_wh_bm.width : 900,
|
||||
height: isRe ? global.config.window_wh_bm.height : 675,
|
||||
x: isRe ? global.config.window_wh_bm.x : 100,
|
||||
y: isRe ? global.config.window_wh_bm.y : 100,
|
||||
title: 'LAITool',
|
||||
icon: '../../resources/icon.ico',
|
||||
show: false,
|
||||
@@ -104,10 +113,16 @@ async function createWindow(hash = "ShowMessage", data, url = null) {
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow.on("closed", () => {
|
||||
mainWindow.on("close", async () => {
|
||||
// 判断指定的窗口,移除指定的监听
|
||||
removeIpcHandler(hash);
|
||||
global.newWindow = global.newWindow.filter(item => item.id != mainWindow.id)
|
||||
// 判断当前的是不是开启了记录功能
|
||||
if (global.config.window_wh_bm_remember && hash == "ShowMessage") {
|
||||
let window_wh_bm = mainWindow.getBounds();
|
||||
// 记录到文件中
|
||||
await setting.ModifySampleSetting(JSON.stringify({ window_wh_bm: window_wh_bm }))
|
||||
}
|
||||
})
|
||||
|
||||
// 创建一个新的窗口,添加对应的监听
|
||||
@@ -120,7 +135,6 @@ async function createWindow(hash = "ShowMessage", data, url = null) {
|
||||
})
|
||||
|
||||
setIpcHandler(hash);
|
||||
await InitData(global);
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
@@ -141,6 +155,8 @@ app.whenReady().then(async () => {
|
||||
optimizer.watchWindowShortcuts(window)
|
||||
})
|
||||
|
||||
//
|
||||
|
||||
global.newWindow = [];
|
||||
mainWindow = createWindow('ShowMessage', null)
|
||||
|
||||
@@ -173,9 +189,12 @@ app.whenReady().then(async () => {
|
||||
}
|
||||
|
||||
// 判断动态文件是不是存在
|
||||
tools.checkJsonFileExistsOrCreate(path.join(define.dynamic_setting));
|
||||
tools.checkJsonFileExistsOrCreate(path.normalize(define.dynamic_setting));
|
||||
// 判断标签文件是不是存在
|
||||
tools.checkJsonFileExistsOrCreate(path.join(define.tag_setting));
|
||||
tools.checkJsonFileExistsOrCreate(path.normalize(define.tag_setting));
|
||||
// 判断SD图片缓存文件是不是存在(不存在创建)
|
||||
tools.checkFolderExistsOrCreate(path.normalize(define.temp_sd_image));
|
||||
tools.checkFolderExistsOrCreate(path.normalize(path.join(define.image_path, "c_s")));
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
@@ -204,6 +223,7 @@ SdIpc();
|
||||
MjIpc();
|
||||
MainIpc(createWindow);
|
||||
OriginalImageGenerateIpc();
|
||||
GlobalIpc();
|
||||
|
||||
|
||||
ipcMain.handle('dark-mode:toggle', (event, value) => {
|
||||
|
||||
@@ -162,6 +162,9 @@ export class Setting {
|
||||
seed: sd_config.setting.seed,
|
||||
style_weight: sd_config.setting.style_weight,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
sd_model: sd_config.sd_model,
|
||||
lora: sd_config.lora,
|
||||
sampler: sd_config.sampler,
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -226,6 +229,7 @@ export class Setting {
|
||||
let sd_data = await fspromises.readFile(define.sd_setting, 'utf-8');
|
||||
let config_json_date = JSON.parse(data);
|
||||
config_json_date.webui_api_url = JSON.parse(sd_data).setting.webui_api_url;
|
||||
config_json_date["space_image"] = define.zhanwei_image;
|
||||
return config_json_date;
|
||||
}
|
||||
|
||||
|
||||
+40
-1
@@ -25,6 +25,21 @@ export class Tools {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的文件,然后将其转换为base64
|
||||
* @param {*} filePath 文件地址
|
||||
* @returns
|
||||
*/
|
||||
async readFileBase64(filePath) {
|
||||
try {
|
||||
let data = await fspromises.readFile(filePath);
|
||||
return data.toString('base64');
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断json文件是不是存在,不存在的话,协议一个空的json文件
|
||||
@@ -301,7 +316,6 @@ export class Tools {
|
||||
method: 'GET',
|
||||
url: url
|
||||
});
|
||||
|
||||
request.on('response', (response) => {
|
||||
const chunks = [];
|
||||
response.on('data', (chunk) => chunks.push(chunk));
|
||||
@@ -323,4 +337,29 @@ export class Tools {
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将base64保存为图片,然后删除图片的exif信息
|
||||
* @param {*} base64 图片的base64数据
|
||||
* @param {*} filePath 保存文件地址
|
||||
* @param {*} isDeleteExif 是不是要删除exif信息(默认为true)
|
||||
*/
|
||||
async saveBase64ToImage(base64, filePath, isDeleteExif = true) {
|
||||
try {
|
||||
let base64Data = base64.replace(/^data:image\/\w+;base64,/, "");
|
||||
let dataBuffer = Buffer.from(base64Data, 'base64');
|
||||
if (isDeleteExif) {
|
||||
// 先将图片写道一个缓存位置,清除数据的时候,将原图片的exif信息删除,然后将原图片复制到目标图片地址
|
||||
let dir = path.dirname(filePath);
|
||||
let tempFilePath = path.join(dir, `temp_${Date.now()}.png`);
|
||||
await fspromises.writeFile(tempFilePath, dataBuffer);
|
||||
this.deletePngAndDeleteExifData(tempFilePath, filePath);
|
||||
} else {
|
||||
await fspromises.writeFile(filePath, dataBuffer);
|
||||
}
|
||||
return filePath;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user