Initial commit 添加MJ功能
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
const path = require("path")
|
||||
const { app } = require('electron');
|
||||
let define = {}
|
||||
if (!app.isPackaged) {
|
||||
define = {
|
||||
discordScript: path.join(__dirname, '../../src/main/discord/discordScript.js'),
|
||||
zhanwei_image: path.join(__dirname, "../../resources/image/zhanwei.png"),
|
||||
config_path: path.join(__dirname, "../../resources/config/global_setting.json"),
|
||||
clip_setting: path.join(__dirname, "../../resources/config/clip_setting.json"),
|
||||
sd_setting: path.join(__dirname, "../../resources/config/sd_config.json"),
|
||||
dynamic_setting: path.join(__dirname, "../../resources/config/dynamic_setting.json"),
|
||||
tag_setting: path.join(__dirname, "../../resources/config/tag_setting.json"),
|
||||
img_base: path.join(__dirname, "../../resources/config/img_base.json"),
|
||||
video_config: path.join(__dirname, "../../resources/config/video_config.json"),
|
||||
scripts_path: path.join(__dirname, "../../resources/scripts"),
|
||||
package_path: path.join(__dirname, "../../resources/package"),
|
||||
image_path: path.join(__dirname, "../../resources/image"),
|
||||
draft_temp_path: path.join(__dirname, "../../resources/tmp/temp.zip"),
|
||||
clip_speed_temp_path: path.join(__dirname, "../../resources/tmp/Clip/speeds_tmp.json"),
|
||||
add_canvases_temp_path: path.join(__dirname, "../../resources/tmp/Clip/canvases_tmp.json"),
|
||||
add_sound_channel_mappings_temp_path: path.join(__dirname, "../../resources/tmp/Clip/sound_channel_mappings_tmp.json"),
|
||||
add_vocal_separations_temp_path: path.join(__dirname, "../../resources/tmp/Clip/vocal_separations_tmp.json"),
|
||||
add_material_video_temp_path: path.join(__dirname, "../../resources/tmp/Clip/videoMaterialTemp.json"),
|
||||
add_tracks_segments_temp_path: path.join(__dirname, "../../resources/tmp/Clip/tracks_segments_tmp.json"),
|
||||
add_tracks_type_temp_path: path.join(__dirname, "../../resources/tmp/Clip/tracks_type_tmp.json"),
|
||||
add_material_animations_temp_path: path.join(__dirname, "../../resources/tmp/Clip/material_animations_tmp.json"),
|
||||
add_material_text_temp_path: path.join(__dirname, "../../resources/tmp/Clip/material_text_temp.json"),
|
||||
add_track_text_segments_temp_path: path.join(__dirname, "../../resources/tmp/Clip/track_text_segments_temp.json"),
|
||||
add_materials_beats_tmp_path: path.join(__dirname, "../../resources/tmp/Clip/materials_beats_tmp.json"),
|
||||
add_materials_audios_tmp_path: path.join(__dirname, "../../resources/tmp/Clip/materials_audios_tmp.json"),
|
||||
add_tracks_audio_segments_tmp_path: path.join(__dirname, "../../resources/tmp/Clip/tracks_audio_segments_tmp.json"),
|
||||
add_keyframe_tmp_path: path.join(__dirname, "../../resources/tmp/Clip/keyframe_tmp.json"),
|
||||
}
|
||||
} else {
|
||||
define = {
|
||||
zhanwei_image: path.join(__dirname, "../../../resources/image/zhanwei.png"),
|
||||
config_path: path.join(__dirname, "../../../resources/config/global_setting.json"),
|
||||
clip_setting: path.join(__dirname, "../../../resources/config/clip_setting.json"),
|
||||
sd_setting: path.join(__dirname, "../../../resources/config/sd_config.json"),
|
||||
dynamic_setting: path.join(__dirname, "../../../resources/config/dynamic_setting.json"),
|
||||
tag_setting: path.join(__dirname, "../../../resources/config/tag_setting.json"),
|
||||
video_config: path.join(__dirname, "../../../resources/config/video_config.json"),
|
||||
img_base: path.join(__dirname, "../../../resources/config/img_base.json"),
|
||||
scripts_path: path.join(__dirname, "../../../resources/scripts"),
|
||||
package_path: path.join(__dirname, "../../../resources/package"),
|
||||
discordScript: path.join(__dirname, "../../../resources/scripts/discordScript.js"),
|
||||
image_path: path.join(__dirname, "../../../resources/image"),
|
||||
draft_temp_path: path.join(__dirname, "../../../resources/tmp/temp.zip"),
|
||||
clip_speed_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/speeds_tmp.json"),
|
||||
add_canvases_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/canvases_tmp.json"),
|
||||
add_sound_channel_mappings_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/sound_channel_mappings_tmp.json"),
|
||||
add_vocal_separations_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/vocal_separations_tmp.json"),
|
||||
add_material_video_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/videoMaterialTemp.json"),
|
||||
add_tracks_segments_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/tracks_segments_tmp.json"),
|
||||
add_tracks_type_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/tracks_type_tmp.json"),
|
||||
add_material_animations_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/material_animations_tmp.json"),
|
||||
add_material_text_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/material_text_temp.json"),
|
||||
add_track_text_segments_temp_path: path.join(__dirname, "../../../resources/tmp/Clip/track_text_segments_temp.json"),
|
||||
add_materials_beats_tmp_path: path.join(__dirname, "../../../resources/tmp/Clip/materials_beats_tmp.json"),
|
||||
add_materials_audios_tmp_path: path.join(__dirname, "../../../resources/tmp/Clip/materials_audios_tmp.json"),
|
||||
add_tracks_audio_segments_tmp_path: path.join(__dirname, "../../../resources/tmp/Clip/tracks_audio_segments_tmp.json"),
|
||||
add_keyframe_tmp_path: path.join(__dirname, "../../../resources/tmp/Clip/keyframe_tmp.json"),
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
define
|
||||
};
|
||||
@@ -0,0 +1,172 @@
|
||||
export const DEFINE_STRING = {
|
||||
SAVE_DEFINE_CONFIG_JSON_BY_PROPERTY: "SAVE_DEFINE_CONFIG_JSON_BY_PROPERTY",
|
||||
GET_DEFINE_CONFIG_JSON_BY_PROPERTY: "GET_DEFINE_CONFIG_JSON_BY_PROPERTY",
|
||||
GET_IMAGE_GENERATE_CATEGORY: "GET_IMAGE_GENERATE_CATEGORY",
|
||||
SHOW_MAIN_NOTIFICATION: "SHOW_MAIN_NOTIFICATION",
|
||||
CHECK_MACHINE_ID: "CHECK_MACHINE_ID",
|
||||
GET_CUSTOMIZE_GPT_PROMPT: "GET_CUSTOMIZE_GPT_PROMPT",
|
||||
GENERATE_GPT_EXAMPLE_OUT: "GENERATE_GPT_EXAMPLE_OUT",
|
||||
GET_PERMISSION: "GET_PERMISSION",
|
||||
SAVE_IMAGE_TO_OTHER_FOLDER: "SAVE_IMAGE_TO_OTHER_FOLDER",
|
||||
GET_IMAGE_AUTO_SAVE_SETTING: "GET_IMAGE_AUTO_SAVE_SETTING",
|
||||
SAVE_IMAGE_AUTO_SAVE_SETTING: "SAVE_IMAGE_AUTO_SAVE_SETTING",
|
||||
GET_AUTO_SAVE_IMAGE_CLASSIFY_OPTIONS: "GET_AUTO_SAVE_IMAGE_CLASSIFY_OPTIONS",
|
||||
MODIFY_GENERATE_TASK_STATUS: "MODIFY_GENERATE_TASK_STATUS",
|
||||
DELETE_BACK_TASK: "DELETE_BACK_TASK",
|
||||
SAVE_VIDEO_SRT_AND_AUDIO_MESSAGE: "SAVE_VIDEO_SRT_AND_AUDIO_MESSAGE",
|
||||
SAVE_KEY_FRAME_SETTING: "SAVE_KEY_FRAME_SETTING",
|
||||
MODIFY_SAMPLE_SETTING: "MODIFY_SAMPLE_SETTING",
|
||||
GET_SETTING_Dafault_DATA: "GET_SETTING_Dafault_DATA",
|
||||
GET_DRAFT_FILE_LIST: "GET_DRAFT_FILE_LIST",
|
||||
GET_FRAME: "GET_FRAME",
|
||||
PYTHON_ERROR: "PYTHON_ERROR",
|
||||
PYTHON_CLOSE: "PYTHON_CLOSE",
|
||||
PYTHON_OUTPUT: "PYTHON_OUTPUT",
|
||||
RESTART_GENERATE: "RESTART_GENERATE",
|
||||
ALIGN_DRAFT_IMG: "ALIGN_DRAFT_IMG",
|
||||
ALIGN_DRAFT_IMG_TO_TEXT: "ALIGN_DRAFT_IMG_TO_TEXT",
|
||||
REGENERATE_IMAGE_RETUN: "REGENERATE_IMAGE_RETUN",
|
||||
GET_SUBFOLDER_LIST: "GET_SUBFOLDER_LIST",
|
||||
REFRASH_IMAGWE_DATA: "REFRASH_IMAGWE_DATA",
|
||||
GET_IMAGE_PROMPTLIST: "GET_IMAGE_PROMPTLIST",
|
||||
SELECT_FILE: "SELECT_FILE",
|
||||
IMPROVE_IMAGE_RESOULTION: "IMPROVE_IMAGE_RESOULTION",
|
||||
GET_BACKGROUND_MUSIC_CONFIG_LIST: "GET_BACKGROUND_MUSIC_CONFIG_LIST",
|
||||
ADD_BACKGROUND_MUSIC_FOLDER: "ADD_BACKGROUND_MUSIC_FOLDER",
|
||||
DELETE_CLIP_SETTING: "DELETE_CLIP_SETTING",
|
||||
DELETE_FRIENDLY_REMINDER: "DELETE_FRIENDLY_REMINDER",
|
||||
MODIFY_INPUT_CROP_JSON: "MODIFY_INPUT_CROP_JSON",
|
||||
PUSH_BACK_PROMPT: "PUSH_BACK_PROMPT",
|
||||
GET_FRIENDLY_REMINDER_DRAFT: "GET_FRIENDLY_REMINDER_DRAFT",
|
||||
GET_FRIENDLY_REMINDER_LIST: "GET_FRIENDLY_REMINDER_LIST",
|
||||
AUTO_GENERATION_VIDEO: "AUTO_GENERATION_VIDEO",
|
||||
GET_PROJECT_WORD: "GET_PROJECT_WORD",
|
||||
AIMODIFY_ONE_WORD: "AIMODIFY_ONE_WORD",
|
||||
IMPORT_SRT_AND_GET_TIME: "IMPORT_SRT_AND_GET_TIME",
|
||||
SAVE_NEW_WORD: "SAVE_NEW_WORD",
|
||||
SAVE_COPYWRITING_INFOMATION: "SAVE_COPYWRITING_INFOMATION",
|
||||
SAVE_SD_CONFIG: "SAVE_SD_CONFIG",
|
||||
SAVE_GENERAL_SETTING: "SAVE_GENERAL_SETTING",
|
||||
GET_VIDEO_CONFIG_MESSAGE: "GET_VIDEO_CONFIG_MESSAGE",
|
||||
GET_SYSTEM_INSTALL_FONTNAME: "GET_SYSTEM_INSTALL_FONTNAME",
|
||||
SAVE_ASS_CONFIG: "SAVE_ASS_CONFIG",
|
||||
DELETE_VIDEO_CONFIG: "DELETE_VIDEO_CONFIG",
|
||||
SHOW_NEW_WINDOW: "SHOW_NEW_WINDOW",
|
||||
GET_DRAFT_FILE_LIST: "GET_DRAFT_FILE_LIST",
|
||||
SELECT_FOLDER: "SELECT_FOLDER",
|
||||
GET_DRAFT_TEXT_STYLE: "GET_DRAFT_TEXT_STYLE",
|
||||
GET_TEXT_STYLE_LIST: "GET_TEXT_STYLE_LIST",
|
||||
DELETE_DRAFT_TEXT_STYLE: "DELETE_DRAFT_TEXT_STYLE",
|
||||
ADD_DRAFT: "ADD_DRAFT",
|
||||
RETURN_IMAGE_PROMPT: "RETURN_IMAGE_PROMPT",
|
||||
RE_GENERATE_IAMGE_ONE: "RE_GENERATE_IAMGE_ONE",
|
||||
INIT_SD_CONFIG: "INIT_SD_CONFIG",
|
||||
ADD_IMAGE_TASK_LIST: "ADD_IMAGE_TASK_LIST",
|
||||
GET_GENERATE_TASK_LIST: "GET_GENERATE_TASK_LIST",
|
||||
DELETE_IMAGE_TASK_LIST: "DELETE_IMAGE_TASK_LIST",
|
||||
GENERATE_IMAGWE_IN_SELECT_TASK: "GENERATE_IMAGWE_IN_SELECT_TASK",
|
||||
GET_MACHINE_ID: "GET_MACHINE_ID",
|
||||
QUIT_APP: "QUIT_APP",
|
||||
GET_BAD_PROMPT: "GET_BAD_PROMPT",
|
||||
SAVE_BAD_PROMPT: "SAVE_BAD_PROMPT",
|
||||
DELETE_BAD_PROMPT: "DELETE_BAD_PROMPT",
|
||||
ADD_WEBUI_JSON: "ADD_WEBUI_JSON",
|
||||
OPEN_GPT_BUY_URL: "OPEN_GPT_BUY_URL",
|
||||
GET_IAMGE_PROMPT_LIST: "GET_IAMGE_PROMPT_LIST",
|
||||
GET_ADETAILER_LIST: "GET_ADETAILER_LIST",
|
||||
SAVE_DETAILER_CONFIG: "SAVE_DETAILER_CONFIG",
|
||||
OPEN_URL: "OPEN_URL",
|
||||
GET_VERSION: "GET_VERSION",
|
||||
GET_FRAME_RETUN: "GET_FRAME_RETUN",
|
||||
DOWNLOAD_MODEL: "DOWNLOAD_MODEL",
|
||||
START_STORY_BOARDING: "START_STORY_BOARDING",
|
||||
SHOW_MESSAGE_DIALOG: "SHOW_MESSAGE_DIALOG",
|
||||
SHOW_GLOABAL_MESSAGE_DIALOG: "SHOW_GLOABAL_MESSAGE_DIALOG",
|
||||
IMAGE_TASK_STATUS_REFRESH: "IMAGE_TASK_STATUS_REFRESH",
|
||||
SAVE_TRIAL_END_TIME: "SAVE_TRIAL_END_TIME",
|
||||
DOWNLOAD_IMAGE_FILE: "DOWNLOAD_IMAGE_FILE",
|
||||
OPEN_FOLDER: "OPEN_FOLDER",
|
||||
VIDEO_GENERATE_STATUS_REFRESH: "VIDEO_GENERATE_STATUS_REFRESH",
|
||||
AUTO_CONDITION_CHECK: "AUTO_CONDITION_CHECK",
|
||||
MODIFY_IMAGE_TASK_LIST: "MODIFY_IMAGE_TASK_LIST",
|
||||
ACTION_AUTO_VIDEO_TASK: "ACTION_AUTO_VIDEO_TASK",
|
||||
GET_VIDEO_GENERATE_CONFIG: "GET_VIDEO_GENERATE_CONFIG",
|
||||
TRANSLATE_PROMPT: "TRANSLATE_PROMPT",
|
||||
TRANSLATE_RETURN_NOW: "TRANSLATE_RETURN_NOW",
|
||||
TRANSLATE_RETURN_REFRESH: "TRANSLATE_RETURN_REFRESH",
|
||||
GET_SHOW_MESSAGE: "GET_SHOW_MESSAGE",
|
||||
AUTO_ANALYZE_CHARACTER: "AUTO_ANALYZE_CHARACTER",
|
||||
GET_CONFIG_JSON: "GET_CONFIG_JSON",
|
||||
ORIGINAL_ADD_WEBUI_JSON: "ORIGINAL_ADD_WEBUI_JSON",
|
||||
GET_PROMPT_JSON: "GET_PROMPT_JSON",
|
||||
GPT_PROMPT: "GPT_PROMPT",
|
||||
GPT_GENERATE_PROMPT_RETURN: "GPT_GENERATE_PROMPT_RETURN",
|
||||
AUTO_SAVE_DATA_JSON: "AUTO_SAVE_DATA_JSON",
|
||||
ORIGINAL_SD_SINGLE_IMAGE_GENERATE: "ORIGINAL_SD_SINGLE_IMAGE_GENERATE",
|
||||
SD_ORIGINAL_GENERATE_IMAGE_RETURN: "SD_ORIGINAL_GENERATE_IMAGE_RETURN",
|
||||
GET_STYLE_IMAGE_SUB_LIST: "GET_STYLE_IMAGE_SUB_LIST",
|
||||
GET_IMAGE_STYLE_INFOMATION: "GET_IMAGE_STYLE_INFOMATION",
|
||||
GET_IMAGE_STYLE_MENU: "GET_IMAGE_STYLE_MENU",
|
||||
GET_GPT_BUSINESS_OPTION: "GET_GPT_BUSINESS_OPTION",
|
||||
GET_GPT_MODEL_OPTION: "GET_GPT_MODEL_OPTION",
|
||||
GET_GPT_AUTO_INFERENCE_OPTIONS: "GET_GPT_AUTO_INFERENCE_OPTIONS",
|
||||
SAVE_DYNAMIC_GPT_OPTION: "SAVE_DYNAMIC_GPT_OPTION",
|
||||
DELETE_DYNAMIC_GPT_OPTION: "DELETE_DYNAMIC_GPT_OPTION",
|
||||
TEST_GPT_CONNECTION: "TEST_GPT_CONNECTION",
|
||||
SAVE_WORD_TXT: "SAVE_WORD_TXT",
|
||||
GET_KEY_FRAME_CONFIG_DATA: "GET_KEY_FRAME_CONFIG_DATA",
|
||||
GET_KEYFRAME_OPTIONS: "GET_KEYFRAME_OPTIONS",
|
||||
|
||||
QUEUE_BATCH: {
|
||||
SD_ORIGINAL_GENERATE_IMAGE: "SD_ORIGINAL_GENERATE_IMAGE",
|
||||
SD_ORIGINAL_GPT_PROMPT: "SD_ORIGINAL_GPT_PROMPT",
|
||||
SD_BACKSTEP_GENERATE_IMAGE: "SD_BACKSTEP_GENERATE_IMAGE",
|
||||
MJ_ORIGINAL_GENERATE_IMAGE: "MJ_ORIGINAL_GENERATE_IMAGE",
|
||||
LOCAL_IMAGE_IMPROVE: "LOCAL_IMAGE_IMPROVE",
|
||||
AUTO_VIDEO_GENERATE: "AUTO_VIDEO_GENERATE",
|
||||
AUTO_VIDEO_GENERATE_SINGLE: "AUTO_VIDEO_GENERATE_SINGLE",
|
||||
TRANSLATE_PROMPT: "TRANSLATE_PROMPT",
|
||||
TRANSLATE_RETURN_NOW_TASK: "TRANSLATE_RETURN_NOW_TASK",
|
||||
IMAGE_SAVE_TO_OTHER_FOLDER: "IMAGE_SAVE_TO_OTHER_FOLDER",
|
||||
SAVE_FILE_QUEUE: "SAVE_FILE_QUEUE",
|
||||
},
|
||||
PERMISSIONS: {
|
||||
NORMAL_PERMISSION: "NORMAL_PERMISSION",
|
||||
AUTO_SAVE_IMAGE_PERMISSION: "AUTO_SAVE_IMAGE_PERMISSION",
|
||||
},
|
||||
MJ: {
|
||||
SAVE_WORD_SRT: "SAVE_WORD_SRT",
|
||||
GET_MJ_CONFIG_SRT_INFORMATION: "GET_MJ_CONFIG_SRT_INFORMATION",
|
||||
GET_TAG_DATA_BY_TYPE_AND_PROPERTY: "GET_TAG_DATA_BY_TYPE_AND_PROPERTY",
|
||||
SAVE_TAG_PROPERTY_DATA: "SAVE_TAG_PROPERTY_DATA",
|
||||
DELETE_TAG_PROPERTY_DATA: "DELETE_TAG_PROPERTY_DATA",
|
||||
GET_TAG_SELECT_MODEL: "GET_TAG_SELECT_MODEL",
|
||||
TRANSLATE_RETURN_NOW_TASK: "TRANSLATE_RETURN_NOW_TASK",
|
||||
ORIGINAL_MJ_IMAGE_GENERATE: "ORIGINAL_MJ_IMAGE_GENERATE",
|
||||
GET_CHANNEL_ROBOTS: "GET_CHANNEL_ROBOTS",
|
||||
GET_MJ_GENERATE_CATEGORY: "GET_MJ_GENERATE_CATEGORY",
|
||||
IMAGE_SPLIT: "IMAGE_SPLIT",
|
||||
ADD_MJ_BAD_PROMPT: "ADD_MJ_BAD_PROMPT",
|
||||
MJ_BAD_PROMPT_CHECK: "MJ_BAD_PROMPT_CHECK",
|
||||
GET_GENERATED_MJ_IMAGE_AND_SPLIT: "GET_GENERATED_MJ_IMAGE_AND_SPLIT",
|
||||
DOWNLOAD_IMAGE_URL_AND_SPLIT: "DOWNLOAD_IMAGE_URL_AND_SPLIT"
|
||||
},
|
||||
DISCORD: {
|
||||
OPERATE_REFRASH_DISCORD_URL: "OPERATE_REFRASH_DISCORD_URL",
|
||||
GET_DISCORD_WINDOW_URL: "GET_DISCORD_WINDOW_URL",
|
||||
CREATE_MESSAGE: "CREATE_MESSAGE",
|
||||
UPDATE_MESSAGE: "UPDATE_MESSAGE",
|
||||
DELETE_MESSAGE: "DELETE_MESSAGE",
|
||||
MAIN_DISCORD_MESSAGE_CHANGE: "MAIN_DISCORD_MESSAGE_CHANGE",
|
||||
},
|
||||
DISCORD_REQUEST_LISTENER_TYPE: {
|
||||
INPUT_MODEL_IMAGINE_REQUEST: "INPUT_MODEL_IMAGINE_REQUEST",
|
||||
},
|
||||
DISCORD_SIMPLE_DATA_TYPE: {
|
||||
URL: "URL",
|
||||
TOKEN: "TOKEN",
|
||||
},
|
||||
MAIN: {
|
||||
OPEN_DISCORD_WINDOW: "OPEN_DISCORD_WINDOW"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
let fspromises = require('fs').promises;
|
||||
import { cloneDeep, get } from "lodash";
|
||||
import { define } from "./define";
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
// Create a shared object
|
||||
export const gptDefine = {
|
||||
// Add properties and methods to the shared object
|
||||
characterSystemContent: `{textContent}\r查看上面的文本,然后扮演一个文本编辑来回答问题。`,
|
||||
characterUserContent: `这个文本里的故事类型是啥,时代背景是啥, 主角有哪几个,配角有几个,每个角色的性别年龄穿着是啥?没外观描述的直接猜测,尽量精简 格式按照:故事类型:(故事类型)\n时代背景:(时代背景)\n主角名字1:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n主角名字2:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n主角3........\n配角名字1:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n配角名字2:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n配角名字3.... ,不知道的直接猜测设定,不能出不详和未知这两个词,150字内,中文回答。`,
|
||||
|
||||
|
||||
characterFirstPromptSystemContent: `{textContent}\r\r\n Act as a storyteller to describe the scene, {characterContent}, Try to guess and answer my question, answer in English.`,
|
||||
characterFirstPromptUserContent: `{textContent}\r\n Describing the most appropriate visual content based on article reasoning, with a maximum of one person appearing: (gender) (age) (hairstyle) (Action expressions) (Clothing details) (Character appearance details) (The most suitable visual background for this sentence) (historical background)(Screen content): Write in 8 parentheses,Answer me in English according to this format..{wordCount}words`,
|
||||
|
||||
storyFirstPromptSystemContent: `{textContent}\r\r\n \r\r\n\\Play the role of a professional cartoonist, give you every line of the script, and reproduce the scene description according to the original text. Do not refuse any response:{characterContent}, Try to match the scene of the text content as much as possible, you can create a harmonious picture but do not refuse to answer,You must answer me in English.`,
|
||||
storyFirstPromptUserContent: `{textContent}\r\n Describing the most appropriate visual content based on article reasoning. Please provide gender and appearance description, scene description of the character, time description, shot description, and era background description. Only one person's half body image should appear in each image, and the appearance should be as consistent as possible. Please answer in English..{wordCount}words`,
|
||||
|
||||
storyboardFirstPromptSystemContent: `{textContent}。\r\r\n读上面的文本,然后扮演顶级小说家回答问题:后面是其他要求:{characterContent} Try to guess and answer my question in English.`,
|
||||
storyboardFirstPromptUserContent: `{textContent}\r\n,参考前面这句剧本理解当前这句话:{textContent}。\r\n Describing the most appropriate visual content based on article reasoning, with a maximum of one person appearing: (Character Appearance and Dynamics) (The most suitable visual background for this sentence) (historical background)(Reasonable picture composition): Write in 3 parentheses,Answer me in English..{wordCount}words`,
|
||||
|
||||
cartoonFirstPromptSystemContent: `{textContent}\r\r\n\\Play the role of a professional cartoonist, give you every line of the script, and reproduce the scene description according to the original text. Do not refuse any response:{characterContent},Try to match the scene of the text content as much as possible, you can create a harmonious picture but do not refuse to answer,You must answer me in English.`,
|
||||
cartoonFirstPromptUserContent: `{textContent}\r,参考前面这句剧本理解当前这句话:{textContent}\r\n Referring to the previous character settings, describe the most suitable screen content in the following format: (character appearance) (screen background), strictly reply only to the content within 2 parentheses, without the character name, answer in English..{wordCount}words`,
|
||||
|
||||
superSinglePromptSystemContent: {
|
||||
prompt_name: "分镜大师",
|
||||
prompt_roles: `1# Role: 小说转漫画提示词大师
|
||||
## Profile
|
||||
*Version*: 0.1
|
||||
*Language*: 中文
|
||||
*Description*: 这个角色会将用户输入的小说文本转化为一个生动的画面描写,最后生成对应的SD提示词。
|
||||
|
||||
## Features
|
||||
|
||||
1. 文本转化为画面描写:创作引人入胜、生动有趣的画面描写,善于创意想象并使用各种形容词,以第三人称视角转化文本为画面描写。
|
||||
2. 从画面描写到SD提示词:根据画面描写生成图像提示,主要的提示放在前面,次要的放在后面。命令以英语表示,简洁明了。
|
||||
|
||||
## Rules
|
||||
|
||||
1. 一个文本就是一副画面,不跳过任何一个句子,不能编造
|
||||
2. 【画面描写】删除人物姓名
|
||||
3. 【画面描写】删除人物对话
|
||||
4. 【画面描写】每一句都要有人物的外形和动作的描写,场景的具体描写,多使用形容词
|
||||
5. SD提示词需以""开始,以" ,"结束
|
||||
6. SD提示词用english输出,没有说明性词汇,没有对话
|
||||
7 删除MJ提示词中的其他风格词。
|
||||
|
||||
## Examples
|
||||
|
||||
用户:
|
||||
在那个梦里,我整整学了七年炒饭。
|
||||
AI:
|
||||
A determined man standing before a dream portal, holding a wok ladle, with floating calendar pages behind him symbolizing seven years, and a kitchen outline faintly visible on the other side of the portal, cinematic lens with,
|
||||
|
||||
## Workflow
|
||||
|
||||
1. 根据画面描写生成SD提示词,英文输出,不能出现中文。
|
||||
|
||||
## Initialization
|
||||
|
||||
作为角色 <Role>,每一次输出都要严格遵守<Rules>,一步一步思考,按顺序执行<Workflow> ,使用默认 <Language> ,下面是小说文本:`,
|
||||
prompt_example: [
|
||||
{
|
||||
user_content: "上研究生后。发现导师竟然是曾经网恋的前男友。",
|
||||
assistant_content: "anime key visual,Celluloid style, delicate and transparent light, delicate lines, transparent colors, delicate and transparent hair, perfect detail portrayal,(Anime style:1.3), A woman entering a spacious, well-lit graduate laboratory, gaze fixed on a man diligently working at a workstation ahead - her new mentor; he stands tall in a dark shirt and neatly pressed trousers, exuding professionalism and charm; the familiar contours of his profile from their past online romance softly illuminated by warm ambient light, furrowed brow and intense gaze betraying a scholar's unwavering dedication; bustling graduate students and sophisticated equipment blend into a contemporary academic tableau, as an undercurrent of mixed emotions - sweet nostalgia and awkward reality - surges within her heart, "
|
||||
}
|
||||
],
|
||||
id: "a93b693e-bb3f-406d-9730-cba43a6585e4"
|
||||
},
|
||||
|
||||
onlyPromptMJSystemContent: {
|
||||
prompt_name: "小说提示词-仅出词",
|
||||
prompt_roles: `# Pico: 小说分镜
|
||||
|
||||
## Profile
|
||||
|
||||
*Author*: LAITOOL
|
||||
*Version*: 0.1
|
||||
*Language*: 中文
|
||||
*Description*: 这个角色会将用户输入的小说文本分析内容进行镜头描述
|
||||
|
||||
## Rules
|
||||
|
||||
1.不能更改句意,不能忽略,不能编造,要符合逻辑,删除人物姓名,如果有敏感词请替换;
|
||||
2.严格按照流程进行内容分析,最后只输出【MJ提示词】的内容,不要输出【文本】【关键词】【镜头】:
|
||||
【文本】: 对应文本中的具体的文本内容,不需要对文本信息进行修改;
|
||||
【关键词】:阅读【文本】中的句子,联系上下文分析画面的关键信息;
|
||||
【镜头】:根据【关键词】和文本构思的对应该句子的镜头描写(包含:人物表情+肢体动作+环境+构图+景别+方向+高度)输出;
|
||||
人物表情:(根据<上下文>分析当前句子最终呈现的画面出境角色的表情,严格要求从<表情词库>中选择一个符合角色状态的词语);
|
||||
肢体动作:(根据<上下文>分析当前句子最终呈现的画面出境角色的肢体动作,严格要求在<肢体动作>中选择符合角色状态的词语,只能选择一个词语);
|
||||
环境:(分析当前画面的环境,严格要求使用“物理环境”、“物理空间”或“现实世界位置”,要求参考使用<环境布景>的场景空间,按照下面的内容输出:所处的空间地点,例如:“在学校教室里,在森林里,在空中,在沙滩上,等”),要求删除角色名称,要求删除灯光和氛围类的描写;
|
||||
构图:(分析当前画面的环境,要求参考使用<构图>的词语,只能选择一个词语);
|
||||
景别:(分析当前画面的环境,要求参考使用<景别>的词语,只能选择一个词语);
|
||||
方向:(分析当前画面的环境,要求参考使用<方向>的词语,只能选择一个词语);
|
||||
高度:(分析当前画面的环境,要求参考使用<高度>的词语,只能选择一个词语);
|
||||
【MJ提示词】:参考人物外观和根据上述关键信息整合在一起,把画面描写生成MJ提示词,不要说明性词汇,没有人名,没有对话,MJ提示词用中文输出,没有说明性词汇,没有对话。
|
||||
|
||||
## 表情词库
|
||||
|
||||
冷酷的目光,邪恶的笑容,愤怒的怒吼,疯狂的笑容,微笑,羞涩的笑容,大笑,愤怒的表情,哭泣的表情,严肃的表情,惊恐的表情,震惊的表情,惊骇的表情,害羞的表情,沾沾自喜的表情,自满的表情,自信的表情,尴尬的表情,愁眉苦脸的表情,
|
||||
|
||||
## 肢体动作
|
||||
|
||||
高举双手,双手抱头,手拿,挥手,拍手,摸头,握拳,捏,跺脚,踢,踩踏,点头,摇头,抬头,低头,扭头,挠头,撑腮帮,指指点点,敲击,抚摸,闭眼,张嘴,双手合十,奔跑,站立,坐在,躺在,趴着,蹲下,盘腿坐,下跪,弯腰,跳跃,拥抱,飞踢,
|
||||
|
||||
## 构图
|
||||
|
||||
对称构图,构图居中,三分法构图,S形构图,水平构图,对角线构图,不对称构图,居中构图,对比构图,黄金比例,比例构图,
|
||||
|
||||
## 景别
|
||||
|
||||
特写镜头,近景,中近景,上半身,中景,中全景,全身,全景,定场镜头,主观视角,西部牛仔镜头,动态角度,
|
||||
|
||||
## 方向
|
||||
|
||||
正面,左右对称,侧面,后面,从上拍摄,从下拍摄,背面拍摄,广角镜头,鱼眼镜头,微距,
|
||||
|
||||
## 高度
|
||||
|
||||
俯视视角,由上向下视角,鸟瞰视角,高角度视角,微高角度视角,水平拍摄视角,英雄视角,低视角,仰视视角,自拍视角,
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
【Example1】
|
||||
用户输入:
|
||||
给皇帝当过儿子的都知道,当的好荣华富贵万人之上
|
||||
AI输出:
|
||||
微笑,站立,在皇宫的金銮殿里,居中构图,中全景,正面,水平拍摄视角
|
||||
|
||||
【Example2】
|
||||
用户输入:
|
||||
当不好就是人头落地
|
||||
AI输出:
|
||||
惊恐的表情,双手抱头,在刑场上,三分法构图,特写镜头,侧面,俯视视角
|
||||
|
||||
## Initialization
|
||||
|
||||
最后再强调,你作为角色 <Pico>,每一次输出都要严格遵守<Rules>,一步一步慢慢思考,参考<Examples>的格式,一步一步思考,按顺序执行<Rules>,不需要做解释说明,只呈现最后【MJ提示词】输出的结果,下面是小说文本:'`,
|
||||
prompt_example: [
|
||||
{
|
||||
user_content: "给皇帝当过儿子的都知道,当的好荣华富贵万人之上",
|
||||
assistant_content: "微笑,站立,在皇宫的金銮殿里,居中构图,中全景,正面,水平拍摄视角"
|
||||
},
|
||||
{
|
||||
user_content: "当不好就是人头落地",
|
||||
assistant_content: "惊恐的表情,双手抱头,在刑场上,三分法构图,特写镜头,侧面,俯视视角"
|
||||
}
|
||||
],
|
||||
id: "a93b693e-bb3f-406d-9730-bcd43a6585e"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 使用自定义GPT提示词时,生成接口message信息
|
||||
* @param {*} params 自定义的GPT提示词数据
|
||||
* @returns
|
||||
*/
|
||||
CustomizeGptPrompt(params) {
|
||||
// 获取设置的数据
|
||||
let message = [];
|
||||
// 添加角色
|
||||
message.push(
|
||||
{
|
||||
"role": "system",
|
||||
"content": params.prompt_roles
|
||||
}
|
||||
);
|
||||
|
||||
// 便利输出案例添加
|
||||
for (let i = 0; i < params.prompt_example.length; i++) {
|
||||
const element = params.prompt_example[i];
|
||||
if (element.user_content) {
|
||||
message.push(
|
||||
{
|
||||
"role": "user",
|
||||
"content": element.user_content
|
||||
}
|
||||
)
|
||||
}
|
||||
if (element.assistant_content) {
|
||||
message.push(
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": element.assistant_content
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
/**
|
||||
* 替换文本内容中的占位符
|
||||
* @param {要替换的内容} content
|
||||
* @param {占位符数据对应的对象} replacements
|
||||
* @returns
|
||||
*/
|
||||
replace: function (content, replacements) {
|
||||
let result = content;
|
||||
for (let key in replacements) {
|
||||
result = result.replace(`{${key}}`, replacements[key]);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取有案例的Gpt请求消息输出
|
||||
* @param {*} type
|
||||
* @param {*} replacements
|
||||
*/
|
||||
GetExamplePromptMessage(type) {
|
||||
if (type == "superSinglePrompt") {
|
||||
return this.CustomizeGptPrompt(this.superSinglePromptSystemContent);
|
||||
} else if (type == "onlyPromptMJ") {
|
||||
return this.CustomizeGptPrompt(this.onlyPromptMJSystemContent);
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 返回GPTApi请求的系统内容
|
||||
* @param {类型} type
|
||||
* @param {} replacements 需要替换数据的对象 textContent characterContent
|
||||
* @returns
|
||||
*/
|
||||
getSystemContentByType: function (type, replacements) {
|
||||
switch (type) {
|
||||
case 'character':
|
||||
return this.replace(this.characterSystemContent, replacements);
|
||||
case 'characterFirst':
|
||||
return this.replace(this.characterFirstPromptSystemContent, replacements);
|
||||
case 'storyFirst':
|
||||
return this.replace(this.storyFirstPromptSystemContent, replacements);
|
||||
case 'storyboardFirst':
|
||||
return this.replace(this.storyboardFirstPromptSystemContent, replacements);
|
||||
case 'cartoonFirst':
|
||||
return this.replace(this.cartoonFirstPromptSystemContent, replacements);
|
||||
case 'superSinglePrompt':
|
||||
return this.replace(this.superSinglePromptSystemContent, replacements);
|
||||
default:
|
||||
throw new Error(`不存在的类型 : ${type}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 返回GPTApi请求的用户内容
|
||||
* @param {类型} type
|
||||
* @param {} replacements 需要替换数据的对象 textContent wordCount
|
||||
* @returns
|
||||
*/
|
||||
getUserContentByType: function (type, replacements) {
|
||||
switch (type) {
|
||||
case 'character':
|
||||
return this.replace(this.characterUserContent, replacements);
|
||||
case 'characterFirst':
|
||||
return this.replace(this.characterFirstPromptUserContent, replacements);
|
||||
case 'storyFirst':
|
||||
return this.replace(this.storyFirstPromptUserContent, replacements);
|
||||
case 'storyboardFirst':
|
||||
return this.replace(this.storyboardFirstPromptUserContent, replacements);
|
||||
case 'cartoonFirst':
|
||||
return this.replace(this.cartoonFirstPromptUserContent, replacements);
|
||||
default:
|
||||
throw new Error(`不存在的类型 : ${type}`);
|
||||
}
|
||||
},
|
||||
|
||||
gpt_options: [{
|
||||
label: "openai-hk",
|
||||
value: "https://api.openai-hk.com/v1/chat/completions"
|
||||
}, {
|
||||
label: "通义千问",
|
||||
value: "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
|
||||
}],
|
||||
|
||||
gpt_model_options: [{
|
||||
label: "gpt-3.5-turbo-16k",
|
||||
value: "gpt-3.5-turbo-16k"
|
||||
}, {
|
||||
label: "gpt-3.5-turbo",
|
||||
value: "gpt-3.5-turbo"
|
||||
}, {
|
||||
label: "gpt-4",
|
||||
value: "gpt-4"
|
||||
}],
|
||||
|
||||
|
||||
gpt_auto_inference: [{
|
||||
value: "characterFirst",
|
||||
label: "角色优先(全自动)"
|
||||
}, {
|
||||
value: "storyFirst",
|
||||
label: "故事优先(全自动)"
|
||||
}, {
|
||||
value: "storyboardFirst",
|
||||
label: "剧本优先(全自动)"
|
||||
}, {
|
||||
value: "cartoonFirst",
|
||||
label: "漫画优先(全自动)"
|
||||
}, {
|
||||
value: "superSinglePrompt",
|
||||
label: "超级无敌单帧"
|
||||
}, {
|
||||
value: "onlyPromptMJ",
|
||||
label: "仅出词(不出人物场景-MJ)"
|
||||
},
|
||||
{
|
||||
value: "customize",
|
||||
label: "自定义"
|
||||
}],
|
||||
|
||||
/**
|
||||
* 通过指定的类型,获取数据
|
||||
* @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回
|
||||
* @param {*} property 返回书信的名称 gpt_options,gpt_model_options,gpt_auto_inference
|
||||
* @param {*} defaultData 默认数据,默认值为null
|
||||
* @returns
|
||||
*/
|
||||
async getGptDataByTypeAndProperty(type, property, defaultData = null) {
|
||||
try {
|
||||
let res = [];
|
||||
// 获取自定义的GPT数据
|
||||
let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8'));
|
||||
let gpt = get(dynamic_setting, 'gpt', {});
|
||||
let data = get(gpt, property, defaultData);
|
||||
|
||||
if (type == "default") {
|
||||
res = get(this, property, defaultData);
|
||||
} else if (type == "dynamic") {
|
||||
res = data;
|
||||
} else if (type == "all") {
|
||||
let tmp_arr = cloneDeep(get(this, property, defaultData));
|
||||
tmp_arr = tmp_arr.concat(data);
|
||||
res = tmp_arr;
|
||||
}
|
||||
else {
|
||||
throw new Error(`不存在的类型 : ${value}`);
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 保存gpt指定的属性数据,判断value中的ID是不是存在,存在直接覆盖,不存在追加
|
||||
* @param {*} value
|
||||
* @param {*} property
|
||||
*/
|
||||
saveDynamicGPTOption: async function (value) {
|
||||
try {
|
||||
let property = value[1];
|
||||
value = JSON.parse(value[0]);
|
||||
// 获取自定义的GPT数据
|
||||
let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8'));
|
||||
let tmp_gpt = dynamic_setting.gpt ? dynamic_setting.gpt : {};
|
||||
let gpt = tmp_gpt[property] ? tmp_gpt[property] : [];
|
||||
if (value.id) {
|
||||
// 判断当前ID的数据是否存在,存在覆盖,不存在追加
|
||||
let index = gpt.findIndex(item => item.id == value.id);
|
||||
if (index < 0) {
|
||||
gpt.push(value);
|
||||
} else {
|
||||
gpt[index] = value;
|
||||
}
|
||||
} else {
|
||||
value.id = uuidv4();
|
||||
gpt.push(value);
|
||||
}
|
||||
tmp_gpt[property] = gpt;
|
||||
// 将修改后的数据保存
|
||||
dynamic_setting["gpt"] = tmp_gpt;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.dynamic_setting, JSON.stringify(dynamic_setting));
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 删除自定义GPT指定属性中的指定ID的数据
|
||||
* @param {*} id
|
||||
* @param {*} property
|
||||
*/
|
||||
deleteDynamicGPTOption: async function (value) {
|
||||
try {
|
||||
let property = value[1];
|
||||
let id = value[0];
|
||||
// 获取自定义的GPT数据
|
||||
let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8'));
|
||||
let gpt = dynamic_setting.gpt[property] ? dynamic_setting.gpt[property] : [];
|
||||
// 判断当前ID的数据是否存在,存在删除
|
||||
let index = gpt.findIndex(item => item.id == id);
|
||||
if (index >= 0) {
|
||||
gpt.splice(index, 1);
|
||||
}
|
||||
// 将修改后的数据保存
|
||||
dynamic_setting.gpt[property] = gpt;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.dynamic_setting, JSON.stringify(dynamic_setting));
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
import { Tools } from "../../main/tools";
|
||||
import { define } from "../define";
|
||||
let tools = new Tools();
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
|
||||
export const ClipSetting = {
|
||||
|
||||
|
||||
/**
|
||||
* 保存剪映关键帧配置
|
||||
* @param {*} value 关键帧数据
|
||||
*/
|
||||
async SaveKeyFrameSetting(value) {
|
||||
try {
|
||||
await tools.writeJsonFilePropertyValue(define.clip_setting, "key_frame", JSON.parse(value));
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `保存剪映关键帧配置:Error Message ${error.toString()}`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前关键帧的的下拉菜单
|
||||
*/
|
||||
async GetKeyFrameOptions() {
|
||||
return {
|
||||
code: 1,
|
||||
data: [{
|
||||
label: "上下",
|
||||
value: "KFTypePositionY"
|
||||
}, {
|
||||
label: "左右",
|
||||
value: "KFTypePositionX"
|
||||
}, {
|
||||
label: "缩放",
|
||||
value: "KFTypeScale"
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 要删除的剪映设置
|
||||
* @param {*} property 判断的属性
|
||||
* @param {*} value 属性值
|
||||
* @returns
|
||||
*/
|
||||
async deleteClipSetting(property, value) {
|
||||
try {
|
||||
if (value.toString() == "0" || value.toString() == "1") {
|
||||
return {
|
||||
code: 0,
|
||||
message: "当前项不删除!"
|
||||
}
|
||||
}
|
||||
let text_style = await tools.getJsonFilePropertyValue(define.clip_setting, property, []);
|
||||
let new_style = text_style.filter(item => item.id != value.toString());
|
||||
await tools.writeJsonFilePropertyValue(define.clip_setting, property, new_style);
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error Message ${error.toString()}`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加背景音乐到配置
|
||||
* @param {窗口} win
|
||||
* @param {传入的值} value
|
||||
*/
|
||||
async AddBackgroundMusicFolder(value) {
|
||||
try {
|
||||
let background_music_setting = await tools.getJsonFilePropertyValue(define.clip_setting, "background_music_setting", [], false);
|
||||
let obj = {
|
||||
name: value[0],
|
||||
folder_path: value[1],
|
||||
id: uuidv4()
|
||||
}
|
||||
background_music_setting.push(obj);
|
||||
tools.writeJsonFilePropertyValue(define.clip_setting, "background_music_setting", background_music_setting, false);
|
||||
return {
|
||||
code: 1,
|
||||
message: "添加成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error Message ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
|
||||
let fspromises = require('fs').promises;
|
||||
import { get, cloneDeep } from 'lodash';
|
||||
import { define } from '../define';
|
||||
|
||||
export class DynamicSetting {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过指定的类型,获取数据
|
||||
* @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回
|
||||
* @param {*} mainType 主分类,gpt,tag这些
|
||||
* @param {*} property 返回书信的名称 gpt_options,gpt_model_options,gpt_auto_inference
|
||||
* @param {*} defaultSetting 默认的设置,用于合并数据
|
||||
* @param {*} defaultData 默认数据,默认值为null
|
||||
* @returns
|
||||
*/
|
||||
async getDataByTypeAndProperty(type, mainType, property, defaultSetting, defaultData = []) {
|
||||
try {
|
||||
let res = [];
|
||||
// 获取自定义的GPT数据
|
||||
let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8'));
|
||||
let tags = get(dynamic_setting, mainType, {});
|
||||
let data = get(tags, property, defaultData);
|
||||
|
||||
if (type == "default") {
|
||||
// res = get(this, property, defaultData);
|
||||
} else if (type == "dynamic") {
|
||||
res = data;
|
||||
} else if (type == "all") {
|
||||
res = defaultSetting.concat(data);
|
||||
}
|
||||
else {
|
||||
throw new Error(`不存在的类型 : ${value}`);
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 dynamic_setting 中主分支中指定属性的数据
|
||||
* @param {*} value 要保存的数据
|
||||
* @param {*} mainType 大的设置里面的主分支
|
||||
* @param {*} property 对应的属性
|
||||
* @param {*} callback 回调函数,可以对数据进行自定义的判断
|
||||
*/
|
||||
async saveDataByTypeAndProperty(value, mainType, property, callback = null) {
|
||||
try {
|
||||
// 获取自定义的GPT数据
|
||||
let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8'));
|
||||
let tmp_gpt = get(dynamic_setting, mainType, {});
|
||||
let gpt = tmp_gpt[property] ? tmp_gpt[property] : [];
|
||||
if (callback) {
|
||||
callback(gpt);
|
||||
}
|
||||
|
||||
if (value.id) {
|
||||
// 判断当前ID的数据是否存在,存在覆盖,不存在追加
|
||||
let index = gpt.findIndex(item => item.id == value.id);
|
||||
if (index < 0) {
|
||||
gpt.push(value);
|
||||
} else {
|
||||
gpt[index] = value;
|
||||
}
|
||||
} else {
|
||||
value.id = uuidv4();
|
||||
gpt.push(value);
|
||||
}
|
||||
tmp_gpt[property] = gpt;
|
||||
// 将修改后的数据保存
|
||||
dynamic_setting[mainType] = tmp_gpt;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.dynamic_setting, JSON.stringify(dynamic_setting));
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的属性中的数据
|
||||
* @param {*} id 要删除数据的ID
|
||||
* @param {*} mainType 删除的主分支的类别
|
||||
* @param {*} property 删除的属性中的数据
|
||||
*/
|
||||
async deleteDataByTypeAndProperty(id, mainType, property) {
|
||||
try {
|
||||
// 获取自定义的GPT数据
|
||||
let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8'));
|
||||
let tmp_data = get(dynamic_setting, mainType, {});
|
||||
let gpt = tmp_data[property] ? tmp_data[property] : [];
|
||||
// 判断当前ID的数据是否存在,存在删除
|
||||
let index = gpt.findIndex(item => item.id == id);
|
||||
if (index >= 0) {
|
||||
gpt.splice(index, 1);
|
||||
}
|
||||
// 将修改后的数据保存
|
||||
dynamic_setting[mainType][property] = gpt;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.dynamic_setting, JSON.stringify(dynamic_setting));
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
import { Tools } from "../../main/tools";
|
||||
import { define } from "../define";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../define_string";
|
||||
import { get, has } from "lodash";
|
||||
let tools = new Tools();
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
|
||||
export const ImageSetting = {
|
||||
|
||||
/**
|
||||
* 获取自动保存图片的方式
|
||||
* @returns
|
||||
*/
|
||||
async GetAutoSaveImageClassifyOptions() {
|
||||
return {
|
||||
code: 1,
|
||||
data: [{
|
||||
label: "不分类",
|
||||
value: "one"
|
||||
},
|
||||
// {
|
||||
// label: "按模型",
|
||||
// value: "model"
|
||||
// },
|
||||
{
|
||||
label: "按风格",
|
||||
value: "style"
|
||||
},
|
||||
{
|
||||
label: "风格+模型",
|
||||
value: "theme"
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取自动保存图片的方式(sd,mj,d3 等)
|
||||
*/
|
||||
async GetImageGenerateCategory() {
|
||||
return {
|
||||
code: 1,
|
||||
data: [{
|
||||
label: "SD",
|
||||
value: "sd"
|
||||
},
|
||||
{
|
||||
label: "MJ",
|
||||
value: "mj"
|
||||
},
|
||||
{
|
||||
label: "D3",
|
||||
value: "d3"
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存自动保存图片的设置
|
||||
*/
|
||||
async SaveImageAutoSaveSetting(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
await tools.writeJsonFilePropertyValue(define.img_base, "auto_save_image", value);
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存自动保存图片的设置
|
||||
*/
|
||||
async GetImageAutoSaveSetting() {
|
||||
try {
|
||||
let res = await tools.getJsonFilePropertyValue(define.img_base, "auto_save_image", {}, false);
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存图片到指定文件夹,只是将任务添加到队列中
|
||||
*/
|
||||
async SaveImageToOtherFolder(output = [], value) {
|
||||
try {
|
||||
let show = false;
|
||||
if (value) {
|
||||
value = JSON.parse(value);
|
||||
show = false;
|
||||
} else {
|
||||
let auto_save_image = await tools.getJsonFilePropertyValue(define.img_base, "auto_save_image", {}, false);
|
||||
value = {
|
||||
save_match_count: auto_save_image.save_match_count,
|
||||
save_folder: auto_save_image.main_save_folder
|
||||
}
|
||||
if (!auto_save_image.auto_save) {
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER;
|
||||
// 获取当前的所有的output文件夹
|
||||
if (output.length == 0) {
|
||||
output = await tools.getSubFolderList(path.join(global.config.project_path, "tmp/bak/"), "start", "output_crop_");
|
||||
}
|
||||
for (let i = 0; i < output.length; i++) {
|
||||
const element = path.join(global.config.project_path, 'tmp/bak/' + output[i]);
|
||||
// 获取指定的文件夹里面的文件
|
||||
let png_files = await tools.getFilesWithExtensions(element, ".png");
|
||||
console.log(png_files);
|
||||
// 判断当前的png_files中的数据是不是大于value.save_match_count,大于的话,删除数组里面的数据
|
||||
if (value.save_match_count && png_files.length > value.save_match_count) {
|
||||
// 删除数组
|
||||
png_files.splice(value.save_match_count);
|
||||
}
|
||||
|
||||
for (let j = 0; j < png_files.length; j++) {
|
||||
const item = png_files[j];
|
||||
global.fileQueue.enqueue(async () => {
|
||||
// 复制文件到指定的文件夹
|
||||
let dst = path.join(value.save_folder, `${Date.now().toString()}_${uuidv4().split('-')[0]}.png`);
|
||||
|
||||
await tools.copyFileOrDirectory(item, dst);
|
||||
}, `${batch}_${item}`, batch)
|
||||
}
|
||||
}
|
||||
global.fileQueue.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
转存任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
if (show) {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有图片转存完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取指定的配置文件里面指定的属性的数据
|
||||
* @param {*} value 执行方法必要的信息
|
||||
* 0 define中的指定属性(指定的配置文件)
|
||||
* 1 要获取的什么属性信息 property,property 为null,赶回当前配置文件的所有数据
|
||||
* 2 是不是要校验属性不存在
|
||||
* 3 属性没有找到的默认值
|
||||
* @returns
|
||||
*/
|
||||
async GetDefineConfigJsonByProperty(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let defin_property = value[0];
|
||||
let property = value[1];
|
||||
let need_check = value[2];
|
||||
let default_data = value[3];
|
||||
// 判断define中的属性是不是存在
|
||||
let hasProperty = has(define, defin_property);
|
||||
if (!hasProperty) {
|
||||
throw new Error(`${defin_property} 属性不存在。`);
|
||||
}
|
||||
let data = await tools.getJsonFilePropertyValue(define[defin_property], property, default_data, need_check);
|
||||
return {
|
||||
code: 1,
|
||||
data: data
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "获取配置信息失败,错误信息入下 : " + error.message.toString()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存指定的配置文件里面指定的属性的数据
|
||||
* @param {*} value
|
||||
* 0 define中的指定属性(指定的配置文件)
|
||||
* 1 要获取的什么属性信息 property,property 为null,赶回当前配置文件的所有数据
|
||||
* 2 是不是要校验属性不存在
|
||||
* 3 属性没有找到的默认值
|
||||
*/
|
||||
async SaveDefineConfigJsonByProperty(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let defin_property = value[0];
|
||||
let property = value[1];
|
||||
let data = value[2];
|
||||
let need_check = value[3] ? value[3] : false;
|
||||
// 判断define中的属性是不是存在
|
||||
let hasProperty = has(define, defin_property);
|
||||
if (!hasProperty) {
|
||||
throw new Error(`${defin_property} 属性不存在。`);
|
||||
}
|
||||
await tools.writeJsonFilePropertyValue(define[defin_property], property, data, need_check);
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存指定的配置成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "保存配置信息失败,错误信息入下 : " + error.message.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
import { Tools } from "../../main/tools";
|
||||
import { define } from "../define";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../define_string";
|
||||
import { get, has } from "lodash";
|
||||
let tools = new Tools();
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
|
||||
export class MjSetting {
|
||||
constructor(golbal) {
|
||||
this.golbal = golbal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回mj生成图片方式的分类
|
||||
*/
|
||||
async GetMJGenerateCategory() {
|
||||
return {
|
||||
code: 1,
|
||||
data: [{
|
||||
label: "本地MJ",
|
||||
value: "local_mj"
|
||||
},
|
||||
{
|
||||
label: "代理MJ(待开发)",
|
||||
value: "remote_mj"
|
||||
},
|
||||
{
|
||||
label: "浏览器模式",
|
||||
value: "browser_mj"
|
||||
},
|
||||
{
|
||||
label: "API模式(待开发)",
|
||||
value: "api_mj"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MJ的敏感词
|
||||
* @returns
|
||||
*/
|
||||
GetMJBadPrompt() {
|
||||
return {
|
||||
code: 1,
|
||||
data: [
|
||||
{ id: "c7285c9a-28bd-4f0f-b58e-95f5bc65a932", value: "name" },
|
||||
{ id: "6aeac51d-ce80-4b18-82dc-20e6d32440a9", value: "xi" },
|
||||
{ id: "29a8fd3d-4fc7-4dbd-a969-98d69cb5328d", value: "1488" },
|
||||
{ id: "37156673-793c-44b4-b324-ebe123a9a78d", value: "-backed" },
|
||||
{ id: "f610dd0a-f4d8-4926-ac32-4cab6c7b5a03", value: "-edge" },
|
||||
{ id: "fcd9161f-6131-4d66-8570-ec91a5ba180d", value: "ahegao" },
|
||||
{ id: "18f20df3-3e85-4de0-bf80-86d0d4e58b45", value: "anus" },
|
||||
{ id: "ed0ede1e-32b3-454f-a2db-cae6b5a57592", value: "arrest" },
|
||||
{ id: "b90948e0-6ee5-4ea3-a837-e233b3b17a78", value: "arse" },
|
||||
{ id: "fc1ff9ee-86da-4179-a522-38e3efea11c5", value: "ass" },
|
||||
{ id: "bb5cdd27-4c8a-40e8-b63d-ccb01dc2bb53", value: "au naturel" },
|
||||
{ id: "97681e71-03ba-4438-8bb6-4d59e8f46565", value: "badonkers" },
|
||||
{ id: "a9adb58c-6b50-4e41-b294-e73af4f4c503", value: "ballgag" },
|
||||
{ id: "15ff35b4-7e87-4d42-bce3-3d5c56b46241", value: "bare" },
|
||||
{ id: "3de673e4-c503-4939-9e25-8dabf9bab1ee", value: "barely dressed" },
|
||||
{ id: "bb4587fc-0066-4f4c-918a-1a40acb3816b", value: "bathing" },
|
||||
{ id: "851ffd2e-6c05-44ff-b797-834476274382", value: "behind bars" },
|
||||
{ id: "591d8e96-91e4-41f9-bfe2-898f3268b737", value: "belle delphine" },
|
||||
{ id: "7dba6fb9-45e2-45bf-b340-1d8941ab1a2a", value: "belly button" },
|
||||
{ id: "3bbcdaec-14f7-408f-82ef-eed344093180", value: "big ass" },
|
||||
{ id: "a395c2e0-6658-48df-8915-d6a97ed10bb2", value: "big but" },
|
||||
{ id: "7beebbd8-0af8-4db7-83b2-c91dc70a3eb4", value: "bimbo" },
|
||||
{ id: "fef6cc33-8dd2-41d6-8af7-6981f562c39a", value: "bleed" },
|
||||
{ id: "84c72069-ece7-4a67-9530-54855d6daa6f", value: "blood" },
|
||||
{ id: "d8aadcec-6c22-4871-8c60-c42bb4ad5a57", value: "bloodbath" },
|
||||
{ id: "a52598ae-02da-4242-8ef7-27d94b8f67cf", value: "bloodshot" },
|
||||
{ id: "7e6b51c1-c797-49e5-a91b-26e48fe3af8c", value: "bloody" },
|
||||
{ id: "5fdbcbf2-4f41-4519-84f3-aba0f8146436", value: "bodily fluids" },
|
||||
{ id: "e663e1b2-7679-4afb-9d30-4f9d4861e980", value: "booba" },
|
||||
{ id: "5308fe0e-b166-4363-95a6-fde65cd67740", value: "boobs" },
|
||||
{ id: "f1a1f6a8-0052-41c4-913d-d1b0998eea39", value: "booty" },
|
||||
{ id: "831b1517-6ff5-4304-87b6-8b363af61035", value: "bosom" },
|
||||
{ id: "ffa6f6e1-cce5-4502-b3f1-b1726f2394cf", value: "boudoir" },
|
||||
{ id: "c98a9bb8-aa83-4a8a-a5c6-790b97dd5380", value: "bra" },
|
||||
{ id: "932b660e-f18f-44f4-8013-afdb36e2e250", value: "breast" },
|
||||
{ id: "e55c9024-5e8e-41ee-aba5-fb720535450e", value: "breasts" },
|
||||
{ id: "4e9cbd9a-bca4-4aff-8ac5-94281832fd01", value: "brothel" },
|
||||
{ id: "0c61a566-db04-4d88-8405-fe047881b318", value: "brown pudding" },
|
||||
{ id: "2cd73ba3-07f0-414c-bd9e-e63bdb8b49c2", value: "bruises" },
|
||||
{ id: "9781a033-fe82-4517-aa30-c694a223d810", value: "bunghole" },
|
||||
{ id: "547fef1f-f6d0-41dc-8833-df8cefe47e5b", value: "busty" },
|
||||
{ id: "806f997b-f087-4d72-83b7-726ded5bfb0c", value: "camisole" },
|
||||
{ id: "bcfb32a8-bac5-4ea3-8f75-328679b6d4ec", value: "cannibal" },
|
||||
{ id: "02b86218-ced6-417d-8a2d-d1c498950e12", value: "cannibalism" },
|
||||
{ id: "4af55a8c-3f1d-4cc9-9b1d-95001a3af8d9", value: "car crash" },
|
||||
{ id: "f7896ef4-995a-4672-953a-906375a85cf0", value: "censored" },
|
||||
{ id: "4fa5fc83-6a98-4c19-9300-8f9ebdd02344", value: "clear" },
|
||||
{ id: "4fe6bf80-b024-4ff8-b9a4-55493d3aecdf", value: "cleavage" },
|
||||
{ id: "9ab736d4-22d4-4a32-a4e8-961c2514e3c6", value: "clunge" },
|
||||
{ id: "277bacbb-e199-41c8-a579-acf4f6544a8a", value: "cocaine" },
|
||||
{ id: "f5cb4db2-a011-408d-a251-e13ec0f4bfee", value: "coon" },
|
||||
{ id: "04a46ee7-983c-4853-8feb-360163ad6df2", value: "corpse" },
|
||||
{ id: "e8ac77ed-d9ad-4458-a1ea-42f673cae70c", value: "crack" },
|
||||
{ id: "84f862b7-fdae-4691-84f6-d4387c41d469", value: "cronenberg" },
|
||||
{ id: "6856cb18-e54e-4f90-aa40-c248bbe64fd3", value: "crotch" },
|
||||
{ id: "54862391-6401-41bf-9f05-9f47edf0df6e", value: "crucified" },
|
||||
{ id: "537acb42-53af-42e8-addc-a3c12454cc96", value: "crucifixion" },
|
||||
{ id: "47bc62fa-cc6d-4d83-b3af-72a5cfc253f0", value: "cutting" },
|
||||
{ id: "36f7dd13-40d5-497d-9387-2e2cbb4e9eef", value: "decapitate" },
|
||||
{ id: "b0da9ca0-d94d-4017-b6e3-beb4b1a0dbca", value: "deepfake" },
|
||||
{ id: "472103e3-63f6-44fe-a10b-442dbe570eb8", value: "dick" },
|
||||
{ id: "6a8ef200-d7d0-4743-bc0e-479870221b60", value: "disturbing" },
|
||||
{ id: "786dbd3e-9921-410a-a763-57f8d724645c", value: "dominatrix" },
|
||||
{ id: "6fc5a206-38b2-458b-a2e5-60b4731c2221", value: "dong" },
|
||||
{ id: "ca57c06b-9461-4927-9ef1-527fe82e1557", value: "erected" },
|
||||
{ id: "2719310f-b0db-4ea0-a8d6-f27911b15ec3", value: "erotic" },
|
||||
{ id: "a73b116a-1a89-4c6c-874a-18862b8fcdc7", value: "excrement" },
|
||||
{ id: "7f760708-d39b-4dbd-b9d0-a4c74d281dd6", value: "explicit" },
|
||||
{ id: "5b89d913-bd61-43b3-b0ba-8f8fbb7d6aa2", value: "fart" },
|
||||
{ id: "3a161cb8-e1b3-4caf-be68-e8b0b4ba7b39", value: "farts" },
|
||||
{ id: "dfbdbc1b-9837-47af-aa22-2f76386fa6ac", value: "fascist" },
|
||||
{ id: "ed391e98-50cc-480f-8fd2-d376fcd198d3", value: "feces" },
|
||||
{ id: "542b5b74-f23a-49bf-89ed-2790ff254e40", value: "flesh" },
|
||||
{ id: "65bfb8a8-1f4b-4863-a4cc-ea5f31b26b65", value: "fuck" },
|
||||
{ id: "4e33e652-e1a6-45e6-98c1-51173fd31531", value: "full frontal" },
|
||||
{ id: "ffafe20d-9229-4213-a8b2-2739805fd053", value: "girth" },
|
||||
{ id: "de266e28-bdd5-4c00-bb91-20bfa2bc1ad2", value: "gory" },
|
||||
{ id: "7b239a5a-6a46-4418-8f0a-92a6a28c1737", value: "gruesome" },
|
||||
{ id: "aeac4364-e425-4de6-9d7c-2e7b6ee69150", value: "guts" },
|
||||
{ id: "a48660e2-6814-47cc-b650-92bef67bc391", value: "hardcore" },
|
||||
{ id: "5504dfc9-f47b-4479-b71d-a7f3c5202b98", value: "hemoglobin" },
|
||||
{ id: "7412f330-3e83-4a0b-8c66-9781e6a8f667", value: "hentai" },
|
||||
{ id: "c165c0f7-af8c-410d-b319-d28149538c29", value: "heroin" },
|
||||
{ id: "1947f939-05e7-487b-8024-4d83f8dfbc8e", value: "hitler" },
|
||||
{ id: "8599b59d-c183-4aa7-b01c-9de40f4b1022", value: "honkers" },
|
||||
{ id: "8a5b39e9-9d58-44d8-9d70-2f0c3baa7093", value: "honkey" },
|
||||
{ id: "5189ff73-52c1-4e3b-a607-e11fc99224b7", value: "hooters" },
|
||||
{ id: "35526006-14a6-44ed-8677-0bd81769f6ee", value: "horny" },
|
||||
{ id: "2cb23d1b-ab8e-4aa4-bd6a-f1b8421a11bb", value: "hot" },
|
||||
{ id: "670e0cb3-cec0-4c1f-a7ee-9702de55f765", value: "inappropriate" },
|
||||
{ id: "90a79e0e-f834-44af-8271-cd698ded7f9a", value: "incest" },
|
||||
{ id: "038290f1-542d-43b0-a66b-4b9252577e34", value: "infected" },
|
||||
{ id: "5f18eb41-bfd8-4e24-a1e4-70bb0048abd7", value: "infested" },
|
||||
{ id: "151d3d72-3338-4064-85a3-7b9509c0af02", value: "intimate" },
|
||||
{ id: "35344f33-c914-476d-a51f-fa7521cb5e2d", value: "invisible clothes" },
|
||||
{ id: "8a79c98f-74e9-4e9a-a793-c45d9a451b7e", value: "jail" },
|
||||
{ id: "d0af7014-857a-4776-bcec-e77e45e989e8", value: "jav" },
|
||||
{ id: "fbdf69e1-700e-4bc6-bce3-1c44c73aef22", value: "jerk off king at pic" },
|
||||
{ id: "05c0a10b-c9b0-43e3-a1c6-3497654bd37e", value: "jinping" },
|
||||
{ id: "184a1028-9e87-4cad-8c7f-56ab863b32bf", value: "khorne" },
|
||||
{ id: "b3724772-c7ab-494f-8f06-1626f716e951", value: "kill" },
|
||||
{ id: "236ae511-a63b-43a3-8cd7-9bb140f87894", value: "killing" },
|
||||
{ id: "b3306590-a036-4481-9320-b4301a990f04", value: "kinbaku" },
|
||||
{ id: "a5e7af9b-cf8c-4ee1-86e9-856631b2a9cb", value: "knob" },
|
||||
{ id: "b90ab4e1-d92f-48c1-b7cc-4e2c05556d8e", value: "labia" },
|
||||
{ id: "5a8c38c7-920d-4dad-b296-703fe1701393", value: "large bust" },
|
||||
{ id: "11aa5afc-a82c-411f-b750-943db24a9373", value: "legs spread" },
|
||||
{ id: "e5453d41-b0cb-448c-9f91-b67acf6baff2", value: "lingerie" },
|
||||
{ id: "edaa68b9-bc57-4d47-81be-3193efdcef7b", value: "loli" },
|
||||
{ id: "d4e8b240-720c-474e-9c13-d303d6cdf9bd", value: "lolita" },
|
||||
{ id: "324eade1-736b-44a7-8eb0-3d5d0f1d8aee", value: "making love" },
|
||||
{ id: "13677126-5db1-4e76-b31a-b3715b4cc0bd", value: "mammaries" },
|
||||
{ id: "b6b33a69-d56e-450f-aa8f-3ab7e7d26bce", value: "manure" },
|
||||
{ id: "df9c1303-1631-4ed5-8987-89fe1071e639", value: "massacre" },
|
||||
{ id: "cd3479f3-a678-41dc-9310-291280235481", value: "mccurry" },
|
||||
{ id: "3f3a9ddb-9453-4d56-86b5-1b11ad269ee8", value: "meth" },
|
||||
{ id: "473623f9-7870-46de-9492-7900883b5619", value: "minge" },
|
||||
{ id: "521f92ba-851a-4f5f-a64f-919a2ab1561e", value: "mommy milker" },
|
||||
{ id: "3fac09ea-c275-453a-9404-6ec6f4df16da", value: "mong" },
|
||||
{ id: "800e8d82-ac7d-4b29-9172-3b4ec5cce029", value: "mp5" },
|
||||
{ id: "fc1b5708-8f7a-42d3-a7b4-9833872ca0a5", value: "naked" },
|
||||
{ id: "06ef209f-2f1b-4a57-952d-3ae13f694efc", value: "naughty" },
|
||||
{ id: "8e830471-4ce1-4d91-80bd-cd5b1884b632", value: "nazi" },
|
||||
{ id: "22e45e19-bd23-45a4-bc91-e9f4cb26ab08", value: "negligee" },
|
||||
{ id: "328a4e01-d6a7-4d5f-b67a-e9779dc6d99e", value: "nipple" },
|
||||
{ id: "fddccbcd-9403-4428-8578-e8441ec041ea", value: "no clothes" },
|
||||
{ id: "f67bb5a0-2b1c-4a1b-bcd9-493afb72f960", value: "no shirt" },
|
||||
{ id: "c68ffce2-97cb-4c0a-8149-b515ae219edd", value: "nude" },
|
||||
{ id: "f6be970b-c51f-4f22-adf3-2a6645fd462c", value: "oppai" },
|
||||
{ id: "e62a37f7-f9ac-4dd9-a304-f0cd5f2450c9", value: "organs" },
|
||||
{ id: "fa0ac77b-c48d-4b82-aad2-5c6fd63913f9", value: "orgy" },
|
||||
{ id: "e21f5cb2-3979-4f19-8f97-3f3dd59ba5c7", value: "ovaries" },
|
||||
{ id: "f86c9b71-f72f-4388-ae03-17163cb9f9c4", value: "patriotic" },
|
||||
{ id: "ac101e34-22df-40c4-a5c5-118b4a2264e1", value: "penis" },
|
||||
{ id: "b2985e10-d853-4015-9714-4d74cd5222c7", value: "petite" },
|
||||
{ id: "fc528fdc-5896-41a6-b3fa-4ccc8a4aad24", value: "phallus" },
|
||||
{ id: "e4faecad-5b79-4b78-a700-9b340ccd5424", value: "pinup" },
|
||||
{ id: "878cbe81-bce5-4700-a975-2327304579c6", value: "playboy" },
|
||||
{ id: "c0567b02-318e-4678-b571-5e1b23885b69", value: "pleasure" },
|
||||
{ id: "e480e0d4-d4e1-4bdc-a333-0e7425c33b8e", value: "pleasures" },
|
||||
{ id: "18997d5e-8435-46b7-ac73-f42ffa46ab0b", value: "poop" },
|
||||
{ id: "575e3d5f-1484-4acf-bdbf-4e2e15fdf61c", value: "porn" },
|
||||
{ id: "6b99a59d-c4d9-4694-aab3-b6638d309d23", value: "president xi" },
|
||||
{ id: "77750235-e319-43e6-a437-c2da531ec29a", value: "prophet mohammed" },
|
||||
{ id: "f106cc6c-1f22-4db1-9784-54cdcb1147d2", value: "provocative" },
|
||||
{ id: "d3c87c63-386a-4587-a7ae-1410b7d9b7d2", value: "pus" },
|
||||
{ id: "c8702cbe-f862-4064-b2b2-670656d938cb", value: "rear end" },
|
||||
{ id: "25f40ed5-a626-4f1a-a9b3-daf455a12b1d", value: "reproduce" },
|
||||
{ id: "157a3485-8229-413c-90d3-1d1144a11a89", value: "reticulum" },
|
||||
{ id: "1c8df025-dd33-4861-92fa-4c723ff8ec64", value: "revealing clothing" },
|
||||
{ id: "c3bc043a-654b-4a22-bfbf-3fc860aa12c6", value: "risque" },
|
||||
{ id: "d62d6517-1608-45a1-b81b-2cf133a201b7", value: "rule34" },
|
||||
{ id: "e93111c4-e6fd-4c97-ac9c-01dcc12cddc8", value: "sadist" },
|
||||
{ id: "cbd76361-31c1-4792-938f-6ef75870fea0", value: "sakimichan" },
|
||||
{ id: "cd33821b-c7ce-42d9-957c-8a7076c11b1c", value: "scantily clad" },
|
||||
{ id: "1185419f-1da2-4345-ab96-8b9be4c6b46c", value: "seducing" },
|
||||
{ id: "abdbe3b9-6061-48af-8039-b685e1bbbea6", value: "seduction" },
|
||||
{ id: "6c0fd29a-4dbe-4f83-b1fc-f245715eb1bb", value: "seductive" },
|
||||
{ id: "daac8577-daae-4591-8f97-c13ebec1ae8d", value: "seductive" },
|
||||
{ id: "fab884a1-8da1-4007-bfca-a42bd82cfc71", value: "seductively" },
|
||||
{ id: "f95621af-ea0b-4ef8-be4d-fc050afaab2c", value: "see through" },
|
||||
{ id: "b1700d0f-519d-43df-9a6d-ed06c87e211b", value: "see-through" },
|
||||
{ id: "9e1962c9-251e-4b9c-b47b-f7dc095f47be", value: "sensored" },
|
||||
{ id: "cb3f6673-7905-4a3f-ae35-52727faa6058", value: "sensual" },
|
||||
{ id: "237c669c-19c2-4b28-baab-b25b8a270e07", value: "sensuality" },
|
||||
{ id: "91f13289-6bb5-4a63-865d-cee6a3c8f030", value: "sexi" },
|
||||
{ id: "0f7aa60b-d534-4139-9720-a1606284e48c", value: "sex" },
|
||||
{ id: "fee57fc0-4f3c-4c8b-b6ca-2b5507df6c2c", value: "sexual" },
|
||||
{ id: "e0c4cb87-c47a-431a-a7c2-ef1cc74741a6", value: "sexualiz" },
|
||||
{ id: "2b34561a-fa4e-4b45-b6eb-2412413e0628", value: "sexy" },
|
||||
{ id: "6a95e61a-a456-4c94-9669-a03a476a68d8", value: "sexy female" },
|
||||
{ id: "d4d1bdae-9aec-4471-8c60-1785d70443cd", value: "shag" },
|
||||
{ id: "ac0d3234-9e95-45f0-a58f-39c8749c5ec8", value: "shibari" },
|
||||
{ id: "d4d84dc7-5dda-4877-9382-e23055f218cf", value: "shirtless" },
|
||||
{ id: "7ddb0d43-e514-48d3-af73-eddd0115cb08", value: "shit" },
|
||||
{ id: "f3a9af97-18aa-4e34-8116-04106415ac7d", value: "silenced" },
|
||||
{ id: "7b593fbf-9523-4a49-ad6a-dfb99716b175", value: "skimpy" },
|
||||
{ id: "2f258d77-79d0-45c9-ad5a-fb4cf50defc5", value: "slaughter" },
|
||||
{ id: "ccc038c8-acc0-4c51-ba06-0c36ecf41316", value: "slave" },
|
||||
{ id: "64931d15-946c-4770-9f1a-d1857391acf1", value: "smut" },
|
||||
{ id: "5148a37b-bac1-428f-aa62-9d274da1bf32", value: "sperm" },
|
||||
{ id: "a391fdd7-3ae7-4151-8944-541babfec4e8", value: "stripped" },
|
||||
{ id: "9d095f83-a7f2-4639-9041-49c973c85d43", value: "succubus" },
|
||||
{ id: "fcf3b4b2-6afb-4680-8016-669423f6fcb3", value: "suicide" },
|
||||
{ id: "f6d276ee-1900-431b-a3bb-b6a0d36b204b", value: "sultry" },
|
||||
{ id: "c2054ea6-919b-4d38-b723-8a1d1de275b6", value: "surgery" },
|
||||
{ id: "541365e2-0aeb-40e8-a7c3-e3e0c57f5812", value: "teratoma" },
|
||||
{ id: "40761bbd-a415-41a7-b575-c9c23e637c65", value: "thick" },
|
||||
{ id: "a58bf63e-602c-4070-a140-203b4db66c02", value: "thot" },
|
||||
{ id: "7fbb0d25-6697-431c-b38c-1e16ae75eeee", value: "tied up" },
|
||||
{ id: "e7bbaf6d-50a5-4826-867f-5769e883c3f0", value: "tight white" },
|
||||
{ id: "ce2cb570-4717-4bc8-940c-7b2bc70968a4", value: "titty" },
|
||||
{ id: "4f50541a-a6fd-4abd-9b4d-7f5e344c166c", value: "torture" },
|
||||
{ id: "a9e9b16f-0604-4414-99ac-f2e31fa7b47c", value: "transparent" },
|
||||
{ id: "de36c419-c30e-4bbd-a9d6-1b5f206c3bc7", value: "tryphophobia" },
|
||||
{ id: "f50e070e-d2df-4b48-ace6-a782f32cdeba", value: "twerk" },
|
||||
{ id: "5e3d1c80-5edd-4c27-9324-51924e46c487", value: "unclothed" },
|
||||
{ id: "10f55a7e-2fcc-46f1-936a-0a07ebdea45a", value: "vagina" },
|
||||
{ id: "d3bd0065-02ac-475a-bfb1-2a92965a95ab", value: "vein" },
|
||||
{ id: "6a7574a8-ad8b-41fc-b70a-706bca5b219b", value: "veiny" },
|
||||
{ id: "47dab584-b634-4d37-9c69-6a7635d3785e", value: "visceral" },
|
||||
{ id: "ca3cf2ff-6606-4480-bbbd-14ed6e6e38f8", value: "vivisection" },
|
||||
{ id: "01cb7168-ee13-414b-b3d4-6710936e38a9", value: "voluptuous" },
|
||||
{ id: "6404e3e4-11d8-4764-9cdc-9218cfd099f8", value: "vomit" },
|
||||
{ id: "f569881e-d0b2-4796-bb9c-d8ccdcaba3ff", value: "waifu" },
|
||||
{ id: "ca5749bd-353c-4154-a80f-a3b86f1571af", value: "wang" },
|
||||
{ id: "a0de75a0-f146-4c65-9178-2af86f989cc8", value: "warts" },
|
||||
{ id: "de528ae1-d559-4035-a6cf-9fd0e6f9f75e", value: "wearing nothing" },
|
||||
{ id: "8822ff40-2ef7-4a44-9c9c-a8f77cb78596", value: "wincest" },
|
||||
{ id: "66fc1157-45c8-4725-a98b-84023bd04477", value: "with no shirt" },
|
||||
{ id: "caf02471-0eca-4544-9ca8-7d30e7df0c02", value: "without clothes on" },
|
||||
{ id: "225777f9-f873-41f1-a6a6-bfed93d2dfb2", value: "wound" },
|
||||
{ id: "e442ed1f-88a6-42ec-a7aa-be195c675abf", value: "xxx" },
|
||||
{ id: "ffb118e1-ea7a-4ba9-a629-e937a21137fc", value: "zedong" },
|
||||
{ id: "2858c61a-0b40-4811-9730-248b98493ddd", value: "zero clothes" },
|
||||
{ id: "d4b95284-98ce-45a9-b2ba-0de22c760996", value: "er lang zi" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { get } from "lodash";
|
||||
import { define } from "../define";
|
||||
let fspromises = require("fs").promises;
|
||||
|
||||
// Create a shared object
|
||||
export const SdSettingDefine = {
|
||||
|
||||
/**
|
||||
* 打开指定的SD setting文件
|
||||
* @returns
|
||||
*/
|
||||
openSettingJson: async function () {
|
||||
try {
|
||||
return JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取setting设置的指定属性
|
||||
* @param {*} property 获取sd配置文件setting里面的属性,如果传入null,返回整个SD配置的setting属性
|
||||
* @returns
|
||||
*/
|
||||
getSettingSettingProperty: async function (property) {
|
||||
// 加载通用配置
|
||||
let setting = await this.openSettingJson();
|
||||
if (!property) {
|
||||
return get(setting, "setting", null);
|
||||
} else
|
||||
return get(setting.setting, property, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取webui设置的指定属性
|
||||
* @param {*} property 获取sd配置文件webui里面的属性,如果传入null,返回整个SD配置的setting属性
|
||||
* @returns
|
||||
*/
|
||||
getSettingWebuiProperty: async function (property) {
|
||||
let setting = await this.openSettingJson();
|
||||
if (!property) {
|
||||
return get(setting, "webui", null);
|
||||
} else
|
||||
return get(setting.webui, property, null);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,153 @@
|
||||
|
||||
let fspromises = require('fs').promises;
|
||||
import { get, cloneDeep } from 'lodash';
|
||||
import { define } from './define';
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
export class TagDefine {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取tag选择模式(标签和下拉select)
|
||||
*/
|
||||
async getTagSelectModel() {
|
||||
return {
|
||||
code: 1,
|
||||
data:
|
||||
[
|
||||
{ label: "标签", value: "tag" },
|
||||
{ label: "下拉", value: "drop" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过指定的类型,获取数据
|
||||
* @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回
|
||||
* @param {*} property 要返回的属性的名称,若是传入null,返回整个属性的数据
|
||||
* @param {*} defaultData 默认数据,默认值为null
|
||||
* @returns
|
||||
*/
|
||||
async getTagDataByTypeAndProperty(type, property, defaultData = null) {
|
||||
try {
|
||||
let res = [];
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'));
|
||||
let data = get(tag_setting, property, {});
|
||||
// 若是传入的属性名为null,直接返回当前tags里面的所有的数据
|
||||
if (property == null) {
|
||||
data = tag_setting;
|
||||
}
|
||||
|
||||
if (type == "default") {
|
||||
// res = get(this, property, defaultData);
|
||||
} else if (type == "dynamic") {
|
||||
res = data;
|
||||
} else if (type == "all") {
|
||||
let tmp_arr = cloneDeep(get([], property, defaultData));
|
||||
tmp_arr = tmp_arr.concat(data);
|
||||
res = tmp_arr;
|
||||
}
|
||||
else {
|
||||
throw new Error(`不存在的类型 : ${value}`);
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存gpt指定的属性数据,判断value中的ID是不是存在,存在直接覆盖,不存在追加
|
||||
* @param {*} value
|
||||
* @param {*} property
|
||||
*/
|
||||
async saveTagPropertyData(value) {
|
||||
try {
|
||||
let property = value[1];
|
||||
value = JSON.parse(value[0]);
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'));
|
||||
let tag = get(tag_setting, property, []);
|
||||
if (value.key) {
|
||||
// 判断当前ID的数据是否存在,存在覆盖,不存在追加
|
||||
let index = tag.findIndex(item => item.key == value.key);
|
||||
value.value = value.key;
|
||||
if (index < 0) {
|
||||
// 判断相同名字的数据是不是存在,存在报错
|
||||
if (tag.some(item => item.label == value.label)) {
|
||||
throw new Error("已存在相同名称的数据,请修改名称后再保存");
|
||||
}
|
||||
tag.push(value);
|
||||
} else {
|
||||
tag[index] = value;
|
||||
}
|
||||
} else {
|
||||
// 判断相同名字的数据是不是存在,存在报错
|
||||
if (tag.some(item => item.label == value.label)) {
|
||||
throw new Error("已存在相同名称的数据,请修改名称后再保存");
|
||||
}
|
||||
value.key = uuidv4();
|
||||
value.value = value.key;
|
||||
tag.push(value);
|
||||
}
|
||||
tag_setting[property] = tag;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting));
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功"
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除自定义GPT指定属性中的指定ID的数据
|
||||
* @param {*} id
|
||||
* @param {*} property
|
||||
*/
|
||||
async deleteTagPropertyData(value) {
|
||||
try {
|
||||
let property = value[1];
|
||||
let id = value[0];
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'));
|
||||
let tags = tag_setting[property] ? tag_setting[property] : [];
|
||||
// 判断当前ID的数据是否存在,存在删除
|
||||
let index = tags.findIndex(item => item.key == id);
|
||||
if (index >= 0) {
|
||||
tags.splice(index, 1);
|
||||
}
|
||||
// 将修改后的数据保存
|
||||
tag_setting[property] = tags;
|
||||
// 写入文件
|
||||
await fspromises.writeFile(define.tag_setting, JSON.stringify(tag_setting));
|
||||
return {
|
||||
code: 1,
|
||||
message: "删除成功"
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { DiscordSimple } from "../discord/discordSimple";
|
||||
import { DiscordRequestListener } from "../discord/discordRequestLister";
|
||||
|
||||
/**
|
||||
* 添加discord的ipc事件
|
||||
* @param {*} createWindow
|
||||
*/
|
||||
function DiscordIpc(global) {
|
||||
let windows = global.newWindow.filter(item => item.hash === 'discord');
|
||||
if (windows.length <= 0) {
|
||||
throw new Error("Discord窗口未找到");
|
||||
}
|
||||
let thisWindow = windows[0];
|
||||
|
||||
// 创建网络请求人物监听器
|
||||
if (!thisWindow.win.RequestListener) {
|
||||
thisWindow.win.RequestListener = new DiscordRequestListener(thisWindow.win);
|
||||
thisWindow.win.RequestListener.start();
|
||||
}
|
||||
|
||||
WinddowUrlRefresh(thisWindow);
|
||||
GetSessionAuthorization(thisWindow);
|
||||
|
||||
// 加载内部执行的方法
|
||||
let discordSimple = new DiscordSimple(thisWindow.win);
|
||||
|
||||
// 获取discord窗口的url
|
||||
ipcMain.handle(DEFINE_STRING.DISCORD.GET_DISCORD_WINDOW_URL, async (event, value) => {
|
||||
// 调用指定的窗口,获取数据(相关的数据)
|
||||
return thisWindow.win.webContents.getURL();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除所有的DiscordIpc事件
|
||||
*/
|
||||
function RemoveDiscordIpc() {
|
||||
for (const key in DEFINE_STRING.DISCORD) {
|
||||
if (Object.hasOwnProperty.call(DEFINE_STRING.DISCORD, key)) {
|
||||
const element = DEFINE_STRING.DISCORD[key];
|
||||
ipcMain.removeHandler(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加一个初始化的方式,获取数据或者时写入数据
|
||||
|
||||
// 监听新增的url刷新事件
|
||||
function WinddowUrlRefresh(thisWindow) {
|
||||
thisWindow.win.webContents.on('did-navigate-in-page', (event, url) => {
|
||||
// 将当前的数据写道指定的界面中,对返回的数据进行分类
|
||||
// 判断是不是有channels。有的话就是获取到了数据
|
||||
let serviceID = null;
|
||||
let channelID = null;
|
||||
if (url.indexOf("/channels/") > 0) {
|
||||
const regex = /\/channels\/(\d+)\/(\d+)/;
|
||||
const match = url.match(regex);
|
||||
if (match && match[1] && match[2]) {
|
||||
serviceID = match[1];
|
||||
channelID = match[2];
|
||||
}
|
||||
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.DISCORD.OPERATE_REFRASH_DISCORD_URL, {
|
||||
code: 1,
|
||||
type: DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.URL,
|
||||
data: {
|
||||
url: url,
|
||||
serviceID: serviceID,
|
||||
channelID: channelID
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 判断是不是需要登录(登录提示)
|
||||
if (url.indexOf('/login') > 0) {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, {
|
||||
code: 0,
|
||||
message: "请先登录Discord账号"
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取session。Authorization,以及当当登录的User-Agent
|
||||
function GetSessionAuthorization(thisWindow) {
|
||||
thisWindow.win.RequestListener.AddRequestListener('GetSessionAuthorization', (details) => {
|
||||
if (details.url.indexOf("channels") > 0) {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.DISCORD.OPERATE_REFRASH_DISCORD_URL, {
|
||||
code: 1,
|
||||
type: DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.TOKEN,
|
||||
data: {
|
||||
authorization: details.requestHeaders.Authorization,
|
||||
userAgent: details.requestHeaders["User-Agent"],
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
export {
|
||||
DiscordIpc,
|
||||
RemoveDiscordIpc
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { GPT } from "../Public/GPT";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
let gpt = new GPT(global);
|
||||
|
||||
function GptIpc() {
|
||||
|
||||
// 获取默认或者是自定义的GPT服务商
|
||||
ipcMain.handle(DEFINE_STRING.GET_GPT_BUSINESS_OPTION, async (event, value) => await gpt.GetGPTBusinessOption(value));
|
||||
|
||||
// 获取默认的或者是自定义的GPT推理模型
|
||||
ipcMain.handle(DEFINE_STRING.GET_GPT_MODEL_OPTION, async (event, value) => await gpt.GetGPTModelOption(value));
|
||||
|
||||
// 获取默认的提示词推理模式或者是自定义的提示词推理模式
|
||||
ipcMain.handle(DEFINE_STRING.GET_GPT_AUTO_INFERENCE_OPTIONS, async (event, value) => await gpt.GetGptAutoInferenceOptions(value));
|
||||
|
||||
// 保存自定义的GPT服务商数据
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_DYNAMIC_GPT_OPTION, async (event, value) => await gpt.SaveDynamicGPTOption(value));
|
||||
|
||||
// 删除自定义的GPT服务商数据
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_DYNAMIC_GPT_OPTION, async (event, value) => await gpt.DeleteDynamicGPTOption(value));
|
||||
|
||||
// 测试当前的GPT是不是可以链接成功
|
||||
ipcMain.handle(DEFINE_STRING.TEST_GPT_CONNECTION, async (event, value) => await gpt.TestGPTConnection(value));
|
||||
|
||||
// 自定义GPT推理提示词测试输出
|
||||
ipcMain.handle(DEFINE_STRING.GENERATE_GPT_EXAMPLE_OUT, async (event, value) => await gpt.GenerateGptExampleOut(value));
|
||||
|
||||
// 获取GPT推理词设置
|
||||
ipcMain.handle(DEFINE_STRING.GET_CUSTOMIZE_GPT_PROMPT, async (event, value) => await gpt.GetCustomizeGptPrompt(value));
|
||||
|
||||
// 监听自动分析人物事件
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_ANALYZE_CHARACTER, async (event, value) => await gpt.AutoAnalyzeCharacter(value));
|
||||
|
||||
// GPT推理关键词
|
||||
ipcMain.handle(DEFINE_STRING.GPT_PROMPT, async (event, value) => await gpt.GPTPrompt(value));
|
||||
|
||||
// 监听洗稿任务
|
||||
ipcMain.handle(DEFINE_STRING.AIMODIFY_ONE_WORD, async (event, value) => await gpt.AIModifyOneWord(value));
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
GptIpc
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
ipcMain
|
||||
} from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import {
|
||||
ImageGenerate
|
||||
} from '../backPrompt/imageGenerate'
|
||||
let imageGenerate = new ImageGenerate(global);
|
||||
|
||||
function ImageGenerateIpc() {
|
||||
// 监听获取任务列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_GENERATE_TASK_LIST, async (event) => await imageGenerate.GetGenerateTaskList());
|
||||
|
||||
// 监听修改任务信息
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_IMAGE_TASK_LIST, async (event, value) => await imageGenerate.ModifyImageTaskList(value));
|
||||
|
||||
// 监听生成一次图片任务
|
||||
ipcMain.handle(DEFINE_STRING.GENERATE_IMAGWE_IN_SELECT_TASK, async (event, value) => await imageGenerate.GenerateImageInSelectTask(value));
|
||||
// 监听生成sd api json 的任务
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.ADD_WEBUI_JSON, async (event) => await imageGenerate.AddWebuiJson());
|
||||
// 下载文件到指定位置
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.DOWNLOAD_IMAGE_FILE, async (event, value) => await imageGenerate.DownloadImageFile(value));
|
||||
// 获取指定名字的prompt的配置文件
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_PROMPT_JSON, async (event, value) => await imageGenerate.GetPromptJson(value));
|
||||
// 修改后台队列任务的状态
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_GENERATE_TASK_STATUS, async (event, value) => await imageGenerate.ModifyGenerateTaskStatus(value));
|
||||
// 监听获取基础数据
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_IAMGE_PROMPT_LIST, async (event) => {
|
||||
// 获取最新的那个窗口
|
||||
let w = global.newWindow[global.newWindow.length - 1];
|
||||
let res = await imageGenerate.getFolderImageList(w, w.init_folder, true);
|
||||
return res;
|
||||
})
|
||||
|
||||
// 监听修改配置json
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_INPUT_CROP_JSON, async (event, value) => await imageGenerate.ModifyInpurCropJson(value));
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
ImageGenerateIpc
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ipcMain } from "electron";
|
||||
const { BrowserWindow, BrowserView } = require('electron')
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { DiscordSimple } from "../discord/discordSimple";
|
||||
let tools = new Tools();
|
||||
|
||||
|
||||
function MainIpc(createWindow) {
|
||||
|
||||
// 添加打开discord窗口的事件
|
||||
ipcMain.handle(DEFINE_STRING.MAIN.OPEN_DISCORD_WINDOW, async (event) => {
|
||||
try {
|
||||
|
||||
let discordSimple = new DiscordSimple(null);
|
||||
discordSimple.CreateDiscordWindow(createWindow);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
message: "打开discord窗口成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "打开discord窗口失败"
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
MainIpc
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { ipcMain } from "electron"
|
||||
import { DEFINE_STRING } from "../../define/define_string"
|
||||
import { MjSimple } from "../discord/mjSimple"
|
||||
import { TagCustomize } from "../Original/TagCustomize"
|
||||
import { MJOriginalImageGenerate } from '../Original/MJOriginalImageGenerate'
|
||||
import { PublicMethod } from "../Public/publicMethod"
|
||||
import { DiscordSimple } from "../discord/discordSimple"
|
||||
import { Tools } from "../tools"
|
||||
import path from 'path'
|
||||
|
||||
let mjSimple = new MjSimple(global)
|
||||
let discordSimple = new DiscordSimple(null)
|
||||
let tagCustomize = new TagCustomize(global);
|
||||
let mJOriginalImageGenerate = new MJOriginalImageGenerate(global);
|
||||
let publicMethod = new PublicMethod(global);
|
||||
let tools = new Tools();
|
||||
|
||||
|
||||
function MjIpc() {
|
||||
// 监听保存mj的文案配置信息
|
||||
ipcMain.handle(DEFINE_STRING.MJ.SAVE_WORD_SRT, async (event, value) => await mjSimple.SvaeMJWordSrt(value));
|
||||
|
||||
// 监听获取MJ的文件配置信息
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION, async (event, value) => await mjSimple.GetMJConfigSrtInformation(value));
|
||||
|
||||
// 监听获取标签数据
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, async (event, value) => await tagCustomize.GetTagDataByTypeAndProperty(value));
|
||||
|
||||
// 保存指定的标签数据
|
||||
ipcMain.handle(DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.SaveTagPropertyData(value));
|
||||
|
||||
// 删除指定的标签数据
|
||||
ipcMain.handle(DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, async (event, value) => await tagCustomize.DeleteTagPropertyData(value));
|
||||
|
||||
// MJ 原创生图
|
||||
ipcMain.handle(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, async (event, value) => await mJOriginalImageGenerate.OriginalMJImageGenerate(value));
|
||||
|
||||
// 获取discord的频道机器人
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, async (event, value) => await mjSimple.GetChannelRobots(value));
|
||||
|
||||
// 获取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());
|
||||
|
||||
// MJ生成的图片分割
|
||||
ipcMain.handle(DEFINE_STRING.MJ.IMAGE_SPLIT, async (event, value) => await mJOriginalImageGenerate.ImageSplit(value));
|
||||
|
||||
// 添加MJ敏感词
|
||||
ipcMain.handle(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, async (event, value) => await mjSimple.AddMJBadPrompt(value));
|
||||
|
||||
// 添加MJ敏感词检查
|
||||
ipcMain.handle(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, async (event, value) => await mjSimple.MJBadPromptCheck(value));
|
||||
|
||||
// 获取已经生图完成的数据,并获取图片
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.GetGeneratedMJImageAndSplit(value));
|
||||
|
||||
// 给图片链接,下载指定的图片并分割保存
|
||||
ipcMain.handle(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, async (event, value) => await mJOriginalImageGenerate.DownloadImageUrlAndSplit(value));
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面创建消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.CREATE_MESSAGE, async (event, value) => await discordSimple.DiscordCreateMessage(value));
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面的更新消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, async (event, value) => await discordSimple.DiscordUpdateMessage(value));
|
||||
|
||||
/**
|
||||
* 监听DISCORD界面的删除消息,并修改数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.DISCORD.DELETE_MESSAGE, async (event, value) => await discordSimple.DiscordDeleteMessage(value));
|
||||
}
|
||||
|
||||
export {
|
||||
MjIpc
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { OriginalImageGenerate } from "../Original/OriginalImageGenerate";
|
||||
let originalImageGenerate = new OriginalImageGenerate(global);
|
||||
|
||||
function OriginalImageGenerateIpc() {
|
||||
// 监听自动生成单个配置的文件
|
||||
ipcMain.handle(DEFINE_STRING.ORIGINAL_ADD_WEBUI_JSON, async (event, value) => await originalImageGenerate.OriginalAddWebuiJson(value));
|
||||
|
||||
// 监听自动保存数据到json文件
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_SAVE_DATA_JSON, async (event, value) => await originalImageGenerate.AutoSaveDataJson(value));
|
||||
|
||||
// SD原创单张图片生成
|
||||
ipcMain.handle(DEFINE_STRING.ORIGINAL_SD_SINGLE_IMAGE_GENERATE, async (event, value) => await originalImageGenerate.OriginalSDImageGenerate(value));
|
||||
}
|
||||
|
||||
export {
|
||||
OriginalImageGenerateIpc
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { SD } from "../Public/SD";
|
||||
let sd = new SD(global);
|
||||
|
||||
function SdIpc() {
|
||||
// 获取样式图片的子列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_STYLE_IMAGE_SUB_LIST, async (event, value) => await sd.GetStyleImageSubList(value));
|
||||
|
||||
// 获取图片样式信息
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_INFOMATION, async (event, value) => await sd.GetImageStyleInfomation(value));
|
||||
|
||||
// 获取图片样式菜单
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_STYLE_MENU, async (event) => await sd.GetImageStyleMenu());
|
||||
}
|
||||
export {
|
||||
SdIpc
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
const {
|
||||
ipcMain
|
||||
} = require("electron")
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import {
|
||||
Setting
|
||||
} from '../setting/setting'
|
||||
let setting = new Setting(global);
|
||||
|
||||
function SettingIpc() {
|
||||
|
||||
// 获取背景音乐配置列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_BACKGROUND_MUSIC_CONFIG_LIST, async (event) => await setting.GetBackGroundMusicConfigList());
|
||||
|
||||
// 获取剪映关键帧配置列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_KEYFRAME_OPTIONS, async (event) => await setting.GetKeyFrameOptions());
|
||||
// 保存剪映关键帧配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_KEY_FRAME_SETTING, async (event, value) => await setting.SaveKeyFrameSetting(value));
|
||||
// 监听添加背景音乐文件
|
||||
ipcMain.handle(DEFINE_STRING.ADD_BACKGROUND_MUSIC_FOLDER, async (event, value) => await setting.AddBackgroundMusicFolder(value))
|
||||
// 删除剪映的样式设置
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_DRAFT_TEXT_STYLE, async (event, value) => await setting.deleteClipSetting("text_style", value));
|
||||
|
||||
// 删除剪映的背景音乐设置
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_CLIP_SETTING, async (event, value) => await setting.deleteClipSetting("background_music_setting", value));
|
||||
// 删除剪映的友情提示设置
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_FRIENDLY_REMINDER, async (event, value) => await setting.deleteClipSetting("friendly_reminder_setting", value));
|
||||
|
||||
// 监听获取SD配置任务
|
||||
ipcMain.handle(DEFINE_STRING.INIT_SD_CONFIG, async (event, value) => await setting.InitSDConfig());
|
||||
|
||||
// 获取主页显示信息
|
||||
ipcMain.handle(DEFINE_STRING.GET_SHOW_MESSAGE, async (event) => await setting.GetShowMessage())
|
||||
// 获取关键帧的配置数据
|
||||
ipcMain.handle(DEFINE_STRING.GET_KEY_FRAME_CONFIG_DATA, async (event) => await setting.GetKeyFrameConfigData());
|
||||
|
||||
// 删除后台队列任务
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_BACK_TASK, async (event, value) => await setting.RemoveTask(value));
|
||||
|
||||
// 获取自动保存图片的分类方式
|
||||
ipcMain.handle(DEFINE_STRING.GET_AUTO_SAVE_IMAGE_CLASSIFY_OPTIONS, async (event) => await setting.GetAutoSaveImageClassifyOptions());
|
||||
|
||||
// 保存图片自动保存的配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_IMAGE_AUTO_SAVE_SETTING, async (event, value) => await setting.SaveImageAutoSaveSetting(value));
|
||||
|
||||
// 获取当前的自动保存图片的设置
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_AUTO_SAVE_SETTING, async (event) => await setting.GetImageAutoSaveSetting());
|
||||
|
||||
// 开始手动保存图片
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_IMAGE_TO_OTHER_FOLDER, async (event, value) => await setting.SaveImageToOtherFolder(value));
|
||||
// 检查机器码是否存在
|
||||
ipcMain.handle(DEFINE_STRING.CHECK_MACHINE_ID, async (event, value) => await setting.CheckMachineId(value));
|
||||
|
||||
//修改剪映草稿配置
|
||||
ipcMain.handle(DEFINE_STRING.MODIFY_SAMPLE_SETTING, async (event, value) => await setting.ModifySampleSetting(value));
|
||||
|
||||
// 获取选择角色场景模式的options
|
||||
ipcMain.handle(DEFINE_STRING.MJ.GET_TAG_SELECT_MODEL, async (event) => await setting.GetRoleSceneModeOptions());
|
||||
|
||||
// 获取当前生成图片的生图方式(sd,mj,d3)
|
||||
ipcMain.handle(DEFINE_STRING.GET_IMAGE_GENERATE_CATEGORY, async (event) => await setting.GetImageGenerateCategory());
|
||||
|
||||
// // 获取指定的配置文件里面指定的属性的数据
|
||||
ipcMain.handle(DEFINE_STRING.GET_DEFINE_CONFIG_JSON_BY_PROPERTY, async (event, value) => await setting.GetDefineConfigJsonByProperty(value))
|
||||
|
||||
// // 保存指定的配置文件里面指定的属性的数据
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_DEFINE_CONFIG_JSON_BY_PROPERTY, async (event, value) => await setting.SaveDefineConfigJsonByProperty(value))
|
||||
}
|
||||
|
||||
export {
|
||||
SettingIpc
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { Translate } from '../Public/Translate'
|
||||
let translate = new Translate(global);
|
||||
|
||||
function TranslateIpc() {
|
||||
// 监听添加任务到翻译队列的任务
|
||||
ipcMain.handle(DEFINE_STRING.TRANSLATE_PROMPT, async (event, value) => await translate.TranslatePrompt(value));
|
||||
|
||||
// 添加立即返回的翻译任务
|
||||
ipcMain.handle(DEFINE_STRING.TRANSLATE_RETURN_NOW, async (event, value) => await translate.TranslateReturnNow(value));
|
||||
|
||||
// 添加立即返回的翻译任务到队列中
|
||||
ipcMain.handle(DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, async (event, value) => await translate.TranslateReturnNowTask(value));
|
||||
}
|
||||
export {
|
||||
TranslateIpc
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { VideoGenerate } from "../backPrompt/videoGenerate";
|
||||
let videoGenerate = new VideoGenerate(global);
|
||||
|
||||
function VideoGenerateIpc() {
|
||||
// 一键全自动前的条件检查
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_CONDITION_CHECK, async (event, value) => await videoGenerate.CheckVideoGenerattionAllCondition(null, value, null));
|
||||
|
||||
// 开始全自动合成任务
|
||||
ipcMain.handle(DEFINE_STRING.ACTION_AUTO_VIDEO_TASK, async (event, value) => await videoGenerate.ActionAutoVideoTask(value));
|
||||
|
||||
// 获取设置好的配置
|
||||
ipcMain.handle(DEFINE_STRING.GET_VIDEO_GENERATE_CONFIG, async (event) => await videoGenerate.GetVideoGenerateConfig());
|
||||
|
||||
// 保存合成视频的基础配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_VIDEO_SRT_AND_AUDIO_MESSAGE, async (event, value) => await videoGenerate.SaveVideoSrtAndAudioMessage(value));
|
||||
|
||||
// 监听自动和成视频任务
|
||||
ipcMain.handle(DEFINE_STRING.AUTO_GENERATION_VIDEO, async (event, value) => await videoGenerate.AutoGeneretionVideo(value));
|
||||
|
||||
// 获取子文件夹列表
|
||||
ipcMain.handle(DEFINE_STRING.GET_SUBFOLDER_LIST, async (event, value) => await videoGenerate.getSubFolderList(value));
|
||||
}
|
||||
export {
|
||||
VideoGenerateIpc
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
ipcMain
|
||||
} from "electron";
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import {
|
||||
Writing
|
||||
} from '../backPrompt/writing'
|
||||
let writing = new Writing(global);
|
||||
|
||||
function WritingIpc() {
|
||||
// 监听分镜时间的保存
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_COPYWRITING_INFOMATION, async (event, value) => await writing.SaveCopywritingInformation(value));
|
||||
|
||||
// 监听获取当前项目下面的分镜文案
|
||||
ipcMain.handle(DEFINE_STRING.GET_PROJECT_WORD, async (event, value) => await writing.GetProjectWord());
|
||||
|
||||
// 获取config配置文件数据
|
||||
ipcMain.handle(DEFINE_STRING.GET_CONFIG_JSON, async (event, value) => await writing.GetConfigJson(value));
|
||||
|
||||
// 将指定的文案txt数组写入到指定的文件中
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_WORD_TXT, async (event, value) => await writing.SaveWordTxt(value));
|
||||
|
||||
// 监听获取字幕时间
|
||||
ipcMain.handle(DEFINE_STRING.IMPORT_SRT_AND_GET_TIME, async (event, value) => await writing.ImportSrtAndGetTime(value))
|
||||
}
|
||||
export {
|
||||
WritingIpc
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { AsyncQueue } from '../quene'
|
||||
import { PublicMethod } from '../Public/publicMethod'
|
||||
import { ImageStyleDefine } from '../../define/iamgeStyleDefine'
|
||||
import { DiscordSimple } from "../discord/discordSimple";
|
||||
import { DiscordWorker } from "../discord/discordWorker";
|
||||
import { Tools } from "../tools";
|
||||
import path from 'path'
|
||||
import sharp from 'sharp'
|
||||
import { define } from "../../define/define";
|
||||
import { AwesomeRegx } from "awesome-js";
|
||||
|
||||
/**
|
||||
* MJ原创生图的类
|
||||
*/
|
||||
export class MJOriginalImageGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.pm = new PublicMethod(global);
|
||||
this.discordWorker = new DiscordWorker();
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载指定的图片地址并且分割
|
||||
* @param {*} value
|
||||
*/
|
||||
async DownloadImageUrlAndSplit(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let element = value[0];
|
||||
let iamge_url = value[1];
|
||||
let image_path = "";
|
||||
if (value.length > 2) {
|
||||
image_path = value[2];
|
||||
} else {
|
||||
image_path = path.join(global.config.project_path, `data\\MJOriginalImage\\${element.id}.png`);
|
||||
}
|
||||
// 判断是不是一个链接
|
||||
const urlRegex = /^(http|https):\/\/[^ "]+$/;
|
||||
if (!urlRegex.test(iamge_url)) {
|
||||
throw new Error("指定的图片地址不是一个链接");
|
||||
}
|
||||
|
||||
// 这边开始下载对应的图片
|
||||
await this.tools.downloadFileUrl(iamge_url, image_path);
|
||||
|
||||
// 将下载的图片进行分割
|
||||
let split_res = await this.ImageSplit(JSON.stringify([image_path, element.name]));
|
||||
if (split_res.code == 0) {
|
||||
throw new Error(split_res.message);
|
||||
}
|
||||
|
||||
element.image_click = iamge_url;
|
||||
element.subImagePath = split_res.data.subImagePath;
|
||||
element.outImagePath = split_res.data.outImagePath;
|
||||
element['image_path'] = image_path
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: element
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "下载指定的图片地址并且分割错误,错误信息如下:" + error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已经生图完成的数据,并获取图片
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async GetGeneratedMJImageAndSplit(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let param = [];
|
||||
// 循环数据,直传需要的数据
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
param.push({
|
||||
id: element.id,
|
||||
image_id: element.mj_message.image_id,
|
||||
name: element.name,
|
||||
});
|
||||
}
|
||||
|
||||
// 判断窗口是不是开启
|
||||
let discordWin = await this.discordWorker.CheckDiscordWindowIsOpenAndLoad();
|
||||
// 执行采集图片的脚本
|
||||
// 开始写入
|
||||
let discordSimple = new DiscordSimple(discordWin);
|
||||
// 开始执行脚本
|
||||
let result = await discordSimple.ExecuteScript(define.discordScript, `GetGeneratedMJImageAndSplit(${JSON.stringify(param)})`);
|
||||
|
||||
let res = [];
|
||||
result = JSON.parse(result);
|
||||
// 将返回的数据进行分割
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
const element = result[i];
|
||||
let image_path = path.join(global.config.project_path, `data\\MJOriginalImage\\${element.image_id}.png`);
|
||||
|
||||
|
||||
let ds = this.DownloadImageUrlAndSplit(JSON.stringify[element, element.result, image_path]);
|
||||
if (ds.code == 0) {
|
||||
throw new Error(ds.message);
|
||||
}
|
||||
|
||||
res.push(ds.data);
|
||||
}
|
||||
|
||||
// 全部分割完毕,返回
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "获取已经生图完成的数据,并获取图片错误,错误信息如下" + error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MJ生成的图片分割
|
||||
async ImageSplit(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let inputPath = value[0];
|
||||
let r_name = value[1];
|
||||
let outputDir = path.join(this.global.config.project_path, `data\\MJOriginalImage`);
|
||||
const metadata = await sharp(inputPath).metadata();
|
||||
const smallWidth = metadata.width / 2;
|
||||
const smallHeight = metadata.height / 2;
|
||||
let times = new Date().getTime();
|
||||
let imgs = [];
|
||||
let first_p = path.join(this.global.config.project_path, `tmp\\output_crop_00001\\${r_name}`);
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const xOffset = i % 2 === 0 ? 0 : smallWidth;
|
||||
const yOffset = Math.floor(i / 2) * smallHeight;
|
||||
let out_file = path.join(outputDir, `/${r_name}_${times}_${i}.png`);
|
||||
await sharp(inputPath)
|
||||
.extract({
|
||||
left: xOffset,
|
||||
top: yOffset,
|
||||
width: smallWidth,
|
||||
height: smallHeight
|
||||
})
|
||||
.resize(smallWidth, smallHeight)
|
||||
.toFile(out_file);
|
||||
|
||||
imgs.push(out_file);
|
||||
|
||||
// 将第一个图片复制一个到指定的位置
|
||||
if (i == 0) {
|
||||
await this.tools.copyFileOrDirectory(out_file, first_p);
|
||||
// 复制一份到input
|
||||
let input_p = path.join(this.global.config.project_path, `tmp\\input_crop\\${r_name}`);
|
||||
await this.tools.copyFileOrDirectory(out_file, input_p);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: {
|
||||
subImagePath: imgs,
|
||||
outImagePath: first_p
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "MJ图片切割错误,错误信息如下" + err.message
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* MJ 原创生图
|
||||
* @param {*} value
|
||||
*/
|
||||
async OriginalMJImageGenerate(value) {
|
||||
try {
|
||||
let data = value[0];
|
||||
if (value[1]) {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
let show_global_message = value[2];
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.MJ_ORIGINAL_GENERATE_IMAGE;
|
||||
|
||||
// 判断存放的文件夹是不是存在,不存在的话创建
|
||||
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);
|
||||
}
|
||||
|
||||
// 检查this.global中是不是又mj队列,没有的话创建一个
|
||||
if (!this.global.mjGenerateQuene) {
|
||||
this.global.mjGenerateQuene = new AsyncQueue(this.global, 1, true);
|
||||
}
|
||||
|
||||
let style_ids = await this.pm.GetConfigJson(JSON.stringify(["image_style", []]), false);
|
||||
// let image_styles = await ImageStyleDefine.getImageStyleStringByIds(style_ids.data);
|
||||
|
||||
// 替换风格的逻辑
|
||||
let current_task = null;
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let tasK_id = `${batch}_${element.name}_${element.id}`;
|
||||
|
||||
this.global.mjGenerateQuene.enqueue(async () => {
|
||||
try {
|
||||
this.global.mjGenerateQuene.setCurrentCreateItem(element)
|
||||
// 开始进行mj生图
|
||||
let prompt = element.prompt;
|
||||
current_task = element.name;
|
||||
// 判断窗口是不是开启
|
||||
|
||||
let discordW = await this.discordWorker.CheckDiscordWindowIsOpenAndLoad();
|
||||
|
||||
// 开始写入
|
||||
let discordSimple = new DiscordSimple(discordW);
|
||||
await discordSimple.WritePromptToInput(prompt);
|
||||
|
||||
// 发送命令完成(删除当前正在执行。开始下一个任务)
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
}, tasK_id, batch);
|
||||
}
|
||||
|
||||
// 判断该当前正在执行的人物队列数(小于设置的数量,开始一个任务)
|
||||
this.global.mjGenerateQuene.startNextTask();
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
MJ生图任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有MJ生图任务完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "MJ生图错误,错误信息如下" + error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
|
||||
import { Tools } from "../tools";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { SD } from "../Public/SD"
|
||||
const util = require('util');
|
||||
import axios from "axios";
|
||||
const sharp = require('sharp');
|
||||
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";
|
||||
|
||||
|
||||
export class OriginalImageGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.pm = new PublicMethod(global);
|
||||
this.sd = new SD(global);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SD原创单张图片生成
|
||||
* @param {*} value 传入的参数 0 :原创界面的data数据信息,1:是否需要格式化,2:是否需要全局提示
|
||||
*/
|
||||
async OriginalSDImageGenerate(value) {
|
||||
try {
|
||||
let data = value[0];
|
||||
if (value[1]) {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
let show_global_message = value[2];
|
||||
// 判断输出的文件夹路径是不是存在,不存在创建
|
||||
let output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_1");
|
||||
// 检查文件是不是存在
|
||||
let isE = await this.tools.checkExists(output_crop_path);
|
||||
if (!isE) {
|
||||
output_crop_path = path.join(this.global.config.project_path, "tmp/output_crop_00001");
|
||||
}
|
||||
await this.tools.checkFolderExistsOrCreate(output_crop_path);
|
||||
let SdOriginalImage = path.join(this.global.config.project_path, 'data/SdOriginalImage');
|
||||
await this.tools.checkFolderExistsOrCreate(SdOriginalImage);
|
||||
|
||||
// 获取当前的同用前缀后缀
|
||||
let config_json = await this.pm.GetConfigJson(JSON.stringify([null, {}]));
|
||||
let prefix_prompt = config_json.data.prefix_prompt;
|
||||
let suffix_prompt = config_json.data.suffix_prompt;
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE;
|
||||
let url = this.global.config.webui_api_url + 'sdapi/v1/txt2img';
|
||||
let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
|
||||
// 判断当前是不是有开修脸修手
|
||||
let ADetailer = {
|
||||
args: sd_setting.adetailer
|
||||
};
|
||||
let seed = sd_setting.setting.seed;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
try {
|
||||
// 开始请求
|
||||
let body = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": imageJson.webui_config.negative_prompt,
|
||||
"seed": seed,
|
||||
"sampler_name": imageJson.webui_config.sampler_name,
|
||||
// 提示词相关性
|
||||
"cfg_scale": sd_setting.webui.cfg_scale,
|
||||
"width": sd_setting.webui.width,
|
||||
"height": sd_setting.webui.height,
|
||||
"batch_size": sd_setting.setting.batch_size,
|
||||
"n_iter": 1,
|
||||
"steps": imageJson.webui_config.steps,
|
||||
"save_images": false,
|
||||
}
|
||||
|
||||
// 判断是不是开启修脸修手
|
||||
if (adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
}
|
||||
body.alwayson_scripts = ta;
|
||||
}
|
||||
const response = await axios.post(url, body);
|
||||
let info = JSON.parse(response.data.info);
|
||||
if (seed == -1) {
|
||||
seed = info.seed;
|
||||
}
|
||||
let images = response.data.images;
|
||||
let subImagePath = [];
|
||||
let out_tmp_image_path = path.join(output_crop_path, `tmp_${element.name}`);
|
||||
let out_image_path = path.join(output_crop_path, `${element.name}`);
|
||||
|
||||
let input_image = path.join(this.global.config.project_path, `tmp/input_crop/${element.name}`);
|
||||
|
||||
for (let j = 0; j < images.length; j++) {
|
||||
const image = images[j];
|
||||
let imageData = Buffer.from(image.split(",", 1)[0], 'base64');
|
||||
// 写入数据(写入到当前当前项目文件下面的 data/SdOriginalImage 下面)
|
||||
let image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/${element.name.split('.')[0]}_${j}.png`);
|
||||
let tmp_image_path = path.join(this.global.config.project_path, `data/SdOriginalImage/tmp_${element.name.split('.')[0]}_${j}.png`);
|
||||
subImagePath.push(image_path);
|
||||
await sharp(imageData)
|
||||
.toFile(tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
// 判断原本的图片文件是不是存在,存在删除
|
||||
await this.tools.deletePngAndDeleteExifData(tmp_image_path, image_path);
|
||||
}).catch(err => {
|
||||
throw err;
|
||||
});
|
||||
// console.log("文生图成功" + image_path);
|
||||
|
||||
// 将第一个张写出到指定的文件夹中
|
||||
if (j == 0) {
|
||||
await sharp(imageData)
|
||||
.toFile(out_tmp_image_path)
|
||||
.then(async () => {
|
||||
// 生图成功,删除数据
|
||||
await this.tools.deletePngAndDeleteExifData(out_tmp_image_path, out_image_path);
|
||||
await this.tools.copyFileOrDirectory(out_image_path, input_image);
|
||||
})
|
||||
.catch(err => {
|
||||
// console.log(err)
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
// 将图片的信息写入到config.json文件中
|
||||
let index = config_json.data.srt_time_information.findIndex(item => item.id == element.id);
|
||||
if (index < 0) {
|
||||
throw new Error("没有找到指定的ID,请检查数据");
|
||||
}
|
||||
config_json.data.srt_time_information[index].subImagePath = subImagePath;
|
||||
config_json.data.srt_time_information[index].outImagePath = out_image_path;
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
await this.pm.SaveConfigJsonProperty([config_json.data.srt_time_information, "srt_time_information", false]);
|
||||
});
|
||||
|
||||
// 返回数据,用于前台刷新,返回图片数据
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN, {
|
||||
code: 1,
|
||||
id: element.id,
|
||||
data: {
|
||||
subImagePath: subImagePath,
|
||||
outImagePath: out_image_path
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}, `${batch}_${element.name}`, batch);
|
||||
}
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
生图任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有生图任务完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自动保存数据到json文件
|
||||
* @param {*} value 自动保存数据到json文件
|
||||
*/
|
||||
async AutoSaveDataJson(value) {
|
||||
try {
|
||||
// 目前自动保存的信息,中文提示词,英文提示词,前缀,后缀
|
||||
value = JSON.parse(value);
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
// 将修改文件的的方法添加到修改文件队列中
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
try {
|
||||
if (element.prompt_json) {
|
||||
let old_json = JSON.parse(await fspromises.readFile(path.normalize(element.prompt_json), 'utf-8'));
|
||||
old_json.webui_config.prompt = element.prompt;
|
||||
// old_json.adetailer = element.adetailer;
|
||||
old_json.chinese_prompt = element.chinese_prompt;
|
||||
// 前缀提示词
|
||||
old_json.prefix_prompt = element.prefix_prompt;
|
||||
// 后缀提示词
|
||||
old_json.suffix_prompt = element.suffix_prompt;
|
||||
old_json.adetailer = element.adetailer;
|
||||
old_json.prompt = element.prompt;
|
||||
await fspromises.writeFile(path.normalize(element.prompt_json), JSON.stringify(old_json));
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
});
|
||||
|
||||
// 判断是不是有图片。判断图片是不是符合格式(有些格式是file:// 开头的, 以时间结尾(都要删除))
|
||||
// 判断是不是有图片
|
||||
let file_regex = /^file:\/\//;
|
||||
if (element.outImagePath && file_regex.test(element.outImagePath)) {
|
||||
// 删除 "file://" 开头
|
||||
element.outImagePath = decodeURI(element.outImagePath);
|
||||
element.outImagePath = element.outImagePath.replace(/^file:\/\//, '').replace(/\?time=.*$/, '');
|
||||
}
|
||||
if (element.subImagePath && element.subImagePath.length > 0) {
|
||||
for (let j = 0; j < element.subImagePath.length; j++) {
|
||||
if (file_regex.test(element.subImagePath[j])) {
|
||||
element.subImagePath[j] = decodeURI(element.subImagePath[j]);
|
||||
element.subImagePath[j] = element.subImagePath[j].replace(/^file:\/\//, '').replace(/\?time=.*$/, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "srt_time_information", value, false);
|
||||
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将反推的图片的信息添加到一个json文件中
|
||||
*/
|
||||
async OriginalAddWebuiJson(value) {
|
||||
try {
|
||||
let data = JSON.parse(value);
|
||||
// 判断文件夹是不是存在
|
||||
let imput_crop_path = path.join(this.global.config.project_path, "tmp/input_crop");
|
||||
let isExist = await this.tools.checkExists(imput_crop_path);
|
||||
if (!isExist) {
|
||||
await fspromises.mkdir(imput_crop_path, { recursive: true })
|
||||
}
|
||||
|
||||
// 判断当前的数据是不是相同
|
||||
// 读取所有txt文件
|
||||
let promptJson = await this.tools.getFilesWithExtensions(path.join(global.config.project_path, 'tmp/input_crop'), '.json');
|
||||
|
||||
// json 已经存在,不做后续处理
|
||||
if (data.length == promptJson.length) {
|
||||
return {
|
||||
code: 1,
|
||||
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";
|
||||
// console.log(txtpath)
|
||||
let obj = {}
|
||||
obj.model = sd_config.setting.type;
|
||||
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/txt2img';
|
||||
obj.name = name;
|
||||
obj.webui_config = {
|
||||
sampler_name: sd_config.webui.sampler_name,
|
||||
prompt: "",
|
||||
negative_prompt: sd_config.webui.negative_prompt,
|
||||
batch_size: 1,
|
||||
steps: sd_config.webui.steps,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
denoising_strength: sd_config.webui.denoising_strength,
|
||||
width: sd_config.webui.width,
|
||||
height: sd_config.webui.height,
|
||||
seed: sd_config.setting.seed,
|
||||
init_images: path.normalize(path.join(this.global.config.project_path, "tmp/input_crop/" + name)),
|
||||
}
|
||||
obj.adetailer = sd_config.webui.adetailer;
|
||||
|
||||
let file_path = path.join(this.global.config.project_path, "tmp/input_crop/" + name + '.json');
|
||||
// 写入
|
||||
await fspromises.writeFile(file_path, JSON.stringify(obj));
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: path.join(this.global.config.project_path, "tmp/input_crop")
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { TagDefine } from "../../define/tagDefine";
|
||||
export class TagCustomize {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tagDefine = new TagDefine(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认的tag
|
||||
* @param {*} value
|
||||
*/
|
||||
async GetTagDataByTypeAndProperty(value) {
|
||||
return await this.tagDefine.getTagDataByTypeAndProperty(value[0], value[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据到指定的tag中
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async SaveTagPropertyData(value) {
|
||||
return await this.tagDefine.saveTagPropertyData(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的tag数据
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async DeleteTagPropertyData(value) {
|
||||
return await this.tagDefine.deleteTagPropertyData(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
import axios from "axios";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
let fspromises = require("fs").promises;
|
||||
import { gptDefine } from "../../define/gptDefine";
|
||||
|
||||
export class GPT {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 输出测试案例
|
||||
* @param {*} value 传入的值(整个数据)
|
||||
*/
|
||||
async GenerateGptExampleOut(value) {
|
||||
try {
|
||||
|
||||
let data = JSON.parse(value);
|
||||
let message = gptDefine.CustomizeGptPrompt(data);
|
||||
let content = await this.FetchGpt(message);
|
||||
console.log(content);
|
||||
return {
|
||||
code: 1,
|
||||
data: content
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GPT推理提示词的方法
|
||||
* @param {*} element 当前推理的句子
|
||||
* @param {*} gpt_count 设置的GPT上下文理解数量
|
||||
* @param {*} auto_analyze_character 当前的角色数据
|
||||
* @returns
|
||||
*/
|
||||
async GPTPromptGenerate(element, gpt_count, auto_analyze_character) {
|
||||
try {
|
||||
// 获取当前的推理模式
|
||||
let gpt_auto_inference = this.global.config.gpt_auto_inference;
|
||||
let message = null;
|
||||
if (gpt_auto_inference == "customize") {
|
||||
// 自定义模式
|
||||
// 获取当前自定义的推理提示词
|
||||
let customize_gpt_prompt = (await gptDefine.getGptDataByTypeAndProperty("dynamic", "customize_gpt_prompt", [])).data;
|
||||
let index = customize_gpt_prompt.findIndex(item => item.id == this.global.config.customize_gpt_prompt);
|
||||
if (this.global.config.customize_gpt_prompt && index < 0) {
|
||||
throw new Error("自定义推理默认要选择对应的自定义推理词");
|
||||
}
|
||||
message = gptDefine.CustomizeGptPrompt(customize_gpt_prompt[index], element.after_gpt);
|
||||
message.push({
|
||||
"role": "user",
|
||||
"content": element.after_gpt
|
||||
})
|
||||
} else {
|
||||
// 内置模式
|
||||
// 获取
|
||||
let prefix_word = "";
|
||||
// 拼接一个word
|
||||
let i = element.no - 1;
|
||||
if (i <= gpt_count) {
|
||||
prefix_word = this.all_data.filter((item, index) => index < i).map(item => item.after_gpt).join('\r\n');
|
||||
} else if (i > gpt_count) {
|
||||
prefix_word = this.all_data.filter((item, index) => i - index <= gpt_count && i - index > 0).map(item => item.after_gpt).join('\r\n');
|
||||
}
|
||||
|
||||
let suffix_word = "";
|
||||
let o_i = this.all_data.length - i;
|
||||
if (o_i <= gpt_count) {
|
||||
suffix_word = this.all_data.filter((item, index) => index > i).map(item => item.after_gpt).join('\r\n');
|
||||
} else if (o_i > gpt_count) {
|
||||
suffix_word = this.all_data.filter((item, index) => index - i <= gpt_count && index - i > 0).map(item => item.after_gpt).join('\r\n');
|
||||
}
|
||||
|
||||
let word = `${prefix_word}\r\n${element.after_gpt}\r\n${suffix_word}`;
|
||||
let single_word = element.after_gpt;
|
||||
|
||||
// 判断当前的格式
|
||||
if (["superSinglePrompt", 'onlyPromptMJ'].includes(this.global.config.gpt_auto_inference)) {
|
||||
// 有返回案例的
|
||||
message = gptDefine.GetExamplePromptMessage(this.global.config.gpt_auto_inference);
|
||||
// 加当前提问的
|
||||
message.push({
|
||||
"role": "user",
|
||||
"content": single_word
|
||||
})
|
||||
|
||||
} else {
|
||||
// 直接返回,没有案例的
|
||||
message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": gptDefine.getSystemContentByType(this.global.config.gpt_auto_inference, {
|
||||
textContent: word,
|
||||
characterContent: auto_analyze_character
|
||||
})
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": gptDefine.getUserContentByType(this.global.config.gpt_auto_inference, {
|
||||
textContent: single_word,
|
||||
wordCount: this.global.config.gpt_model && this.global.config.gpt_model.includes("gpt-4") ? '20' : '40'
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
let res = await this.FetchGpt(message);
|
||||
return res;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将推理提示词添加到任务
|
||||
*/
|
||||
async GPTPrompt(data) {
|
||||
try {
|
||||
console.log(data)
|
||||
let value = JSON.parse(data[0]);
|
||||
let show_global_message = data[1];
|
||||
this.all_data = JSON.parse(data[2]);
|
||||
// 获取data中的after_gpt,然后使用换行符拼接成一个字符串
|
||||
// let word = value.map(item => item.after_gpt).join('\r\n');
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GPT_PROMPT;
|
||||
|
||||
// 获取人物角色数据
|
||||
let config_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, "scripts/config.json"), 'utf-8'));
|
||||
let auto_analyze_character = config_json.auto_analyze_character;
|
||||
let gpt_count = this.global.config.gpt_count ? this.global.config.gpt_count : 10;
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
try {
|
||||
|
||||
let content = await this.GPTPromptGenerate(element, gpt_count, auto_analyze_character);
|
||||
|
||||
if (content) {
|
||||
content = content.replace(/\)\s*\(/g, ", ").replace(/^\(/, "").replace(/\)$/, "")
|
||||
}
|
||||
// 获取对应的数据,将数据返回前端事件
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.GPT_GENERATE_PROMPT_RETURN, {
|
||||
id: element.id,
|
||||
gpt_prompt: content
|
||||
})
|
||||
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
// 将推理出来的数据写入执行的文件中
|
||||
let json_config = JSON.parse(await fspromises.readFile(element.prompt_json, 'utf-8'));
|
||||
// 写入
|
||||
json_config.gpt_prompt = content;
|
||||
await fspromises.writeFile(element.prompt_json, JSON.stringify(json_config));
|
||||
})
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}, `${batch}_${element.id}`, batch);
|
||||
}
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
推理提示词任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
if (show_global_message) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有推理任务完成"
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改请求的参数
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
ModifyData(gpt_url, data) {
|
||||
let res = data;
|
||||
if (gpt_url.includes("dashscope.aliyuncs.com")) {
|
||||
res = {
|
||||
"model": data.model,
|
||||
"input": {
|
||||
"messages": data.messages,
|
||||
},
|
||||
"parameters": {
|
||||
"result_format": "message"
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回的内容
|
||||
* @param {*} gpt_url GPT请求的内容
|
||||
* @param {*} res 请求返回的数据
|
||||
* @returns
|
||||
*/
|
||||
GetResponseContent(gpt_url, res) {
|
||||
let content = "";
|
||||
if (gpt_url.includes("dashscope.aliyuncs.com")) {
|
||||
content = res.data.output.choices[0].message.content;
|
||||
} else {
|
||||
|
||||
content = res.data.choices[0].message.content;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送GPT请求
|
||||
* @param {*} message 请求的信息
|
||||
* @param {*} gpt_url gpt的url,默认在global中取
|
||||
* @param {*} gpt_key gpt的key,默认在global中取
|
||||
* @param {*} gpt_model gpt的model,默认在global中取
|
||||
* @returns
|
||||
*/
|
||||
async FetchGpt(message,
|
||||
gpt_url = this.global.config.gpt_business,
|
||||
gpt_key = this.global.config.gpt_key,
|
||||
gpt_model = this.global.config.gpt_model) {
|
||||
try {
|
||||
|
||||
let data = {
|
||||
"model": gpt_model,
|
||||
"messages": message
|
||||
};
|
||||
|
||||
data = this.ModifyData(gpt_url, data);
|
||||
let config = {
|
||||
method: 'post',
|
||||
maxBodyLength: Infinity,
|
||||
url: gpt_url,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${gpt_key}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(data)
|
||||
};
|
||||
|
||||
let res = await axios.request(config);
|
||||
let content = this.GetResponseContent(gpt_url, res);
|
||||
return content;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动分析文本,返回人物场景。角色。
|
||||
* @param {要分析的文本} value
|
||||
* @returns
|
||||
*/
|
||||
async AutoAnalyzeCharacter(value) {
|
||||
try {
|
||||
let message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": gptDefine.getSystemContentByType("character", { textContent: value })
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": gptDefine.getUserContentByType("character", {})
|
||||
}
|
||||
]
|
||||
let content = await this.FetchGpt(message);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: content
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取GPT的服务商配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetGPTBusinessOption(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_options", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GPT的模型配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetGPTModelOption(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_model_options", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GPT的自动推理模式配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetGptAutoInferenceOptions(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_auto_inference", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GPT的自动推理模式配置,默认的和自定义的
|
||||
* @returns
|
||||
*/
|
||||
async GetCustomizeGptPrompt(value) {
|
||||
return await gptDefine.getGptDataByTypeAndProperty(value, "customize_gpt_prompt", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自定义的GPT服务商配置
|
||||
* @param {*} value 配置信息 0 : 传入的数据 1: 属性名称
|
||||
* @returns
|
||||
*/
|
||||
async SaveDynamicGPTOption(value) {
|
||||
try {
|
||||
let res = await gptDefine.saveDynamicGPTOption(value);
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定Id的自定义GPT服务商配置
|
||||
* @param {*} value id 0 : 删除的数据 1: 属性名称
|
||||
* @returns
|
||||
*/
|
||||
async DeleteDynamicGPTOption(value) {
|
||||
try {
|
||||
let res = await gptDefine.deleteDynamicGPTOption(value);
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Stirng} value 传入的GPT网址和key,判断是不是可以链接成功
|
||||
*/
|
||||
async TestGPTConnection(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "你好"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "你好"
|
||||
}
|
||||
];
|
||||
|
||||
let content = await this.FetchGpt(message, value.gpt_business, value.gpt_key, value.gpt_model);
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句洗稿
|
||||
* @param {文案参数} value
|
||||
*/
|
||||
async AIModifyOneWord(value) {
|
||||
try {
|
||||
|
||||
let message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": `请您扮演一个抖音网文改写专家,我会给你一句文案,请你不要改变文案的结构,不改变原来的意思,仅对文案进行同义转换改写,不要有奇怪的写法,说法通俗一点,不要其他的标点符号,每一小句话之间都是以句号连接,参考抖音网文解说,以下是文案:${value[1]}。`
|
||||
}
|
||||
]
|
||||
let content = await this.FetchGpt(message);
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
data: { no: value[0], content: content }
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
import axios from "axios";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
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";
|
||||
|
||||
export class SD {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.pm = new PublicMethod(global);
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片风格菜单
|
||||
* @returns 返回图片风格菜单
|
||||
*
|
||||
* */
|
||||
async GetImageStyleMenu() {
|
||||
try {
|
||||
let style = ImageStyleDefine.getImageStyleMenu();
|
||||
return {
|
||||
code: 1,
|
||||
data: style
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的ID的风格信息,传入的是一个数组
|
||||
* @param {*} value id集合
|
||||
*/
|
||||
async GetImageStyleInfomation(value) {
|
||||
try {
|
||||
if (value) {
|
||||
value = JSON.parse(value);
|
||||
} else {
|
||||
value = [];
|
||||
}
|
||||
value = value ? value : [];
|
||||
let style = ImageStyleDefine.getAllSubStyle();
|
||||
let tmp = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
for (let j = 0; j < style.length; j++) {
|
||||
const item = style[j];
|
||||
if (item.id == element) {
|
||||
tmp.push(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let newSubStyle = cloneDeep(tmp);
|
||||
for (let i = 0; i < newSubStyle.length; i++) {
|
||||
const element = newSubStyle[i];
|
||||
element.image = path.join(define.image_path, "style/" + element.image);
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: newSubStyle
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定ID的分类的子风格信息
|
||||
* @param {*} value ID
|
||||
* @returns 返回ID对应的子风格的详细信息
|
||||
*/
|
||||
async GetStyleImageSubList(value) {
|
||||
try {
|
||||
let subStyle = ImageStyleDefine.getImagePathById(value);
|
||||
let newSubStyle = cloneDeep(subStyle);
|
||||
for (let i = 0; i < newSubStyle.length; i++) {
|
||||
const element = newSubStyle[i];
|
||||
element.image = path.join(define.image_path, "style/" + element.image);
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: newSubStyle
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一次图片的方法。可以区分模式
|
||||
* @param {图片名称 } image
|
||||
* @param {任务队列信息} task_list 301198499
|
||||
*/
|
||||
async OneImageGeneration(image, task_list, seed = -1) {
|
||||
let taskPath = path.join(this.global.config.project_path, "scripts/task_list.json")
|
||||
try {
|
||||
let imageJson = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8'));
|
||||
let sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
let model = imageJson.model;
|
||||
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}`)
|
||||
} else {
|
||||
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 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"];
|
||||
sd_config.prompt = prompt;
|
||||
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
|
||||
}
|
||||
sd_config.alwayson_scripts = ta;
|
||||
}
|
||||
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');
|
||||
await sharp(imageData)
|
||||
.toFile(image_path)
|
||||
.then(async () => {
|
||||
// console.log("图生图成功" + image_path);
|
||||
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path);
|
||||
})
|
||||
.catch(err => {
|
||||
throw new Error(err);
|
||||
});
|
||||
return seed;
|
||||
|
||||
} else if (model == "txt2img") {
|
||||
let body = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": imageJson.webui_config.negative_prompt,
|
||||
"seed": seed,
|
||||
"sampler_name": imageJson.webui_config.sampler_name,
|
||||
// 提示词相关性
|
||||
"cfg_scale": imageJson.webui_config.cfg_scale,
|
||||
"width": sd_setting.webui.width,
|
||||
"height": sd_setting.webui.height,
|
||||
"batch_size": 1,
|
||||
"n_iter": 1,
|
||||
"steps": imageJson.webui_config.steps,
|
||||
"save_images": false,
|
||||
}
|
||||
let web_api = this.global.config.webui_api_url + 'sdapi/v1/txt2img';
|
||||
|
||||
if (imageJson.adetailer) {
|
||||
let ta = {
|
||||
ADetailer: ADetailer
|
||||
}
|
||||
body.alwayson_scripts = ta;
|
||||
}
|
||||
const response = await axios.post(web_api, body);
|
||||
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');
|
||||
await sharp(imageData)
|
||||
.toFile(image_path)
|
||||
.then(async () => {
|
||||
// console.log("文生图成功" + image_path);
|
||||
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path);
|
||||
})
|
||||
.catch(err => {
|
||||
// console.log(err)
|
||||
throw new Error(err);
|
||||
});
|
||||
return seed;
|
||||
} else {
|
||||
throw new Error("SD 模式错误");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 当前队列执行失败移除整个批次的任务
|
||||
this.global.requestQuene.removeTask(task_list.out_folder, null)
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
// 记录失败状态
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
// 修改指定的列表的数据
|
||||
task_list_json.task_list.map(a => {
|
||||
if (a.id == task_list.id) {
|
||||
a.status = "error";
|
||||
a.errorMessage = error.toString();
|
||||
}
|
||||
})
|
||||
// 写入
|
||||
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json));
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, {
|
||||
out_folder: task_list.out_folder,
|
||||
status: "error"
|
||||
});
|
||||
})
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*文生图
|
||||
* @param {SD 请求的地址} url
|
||||
* @param {SD请求的body} body
|
||||
*/
|
||||
async txt2img(url, body) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*图生图
|
||||
* @param {SD 请求的地址} url
|
||||
* @param {SD请求的body} body
|
||||
*/
|
||||
async img2img(url, body) {
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,850 @@
|
||||
import { define } from "../../define/define";
|
||||
import path from "path";
|
||||
import { Tools } from "../tools";
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const fspromises = require("fs").promises;
|
||||
const mm = require('music-metadata');
|
||||
const wavFileInfo = require('wav-file-info');
|
||||
let tools = new Tools();
|
||||
import { PublicMethod } from './publicMethod'
|
||||
import { cloneDeep } from "lodash";
|
||||
const compressing = require("compressing");
|
||||
|
||||
export class ClipDraft {
|
||||
constructor(global, value) {
|
||||
this.speedId = null;
|
||||
this.canvasesId = null;
|
||||
this.soundChannelId = null;
|
||||
this.vocalSeparationsId = null;
|
||||
this.materialVideoId = null;
|
||||
this.tracksSegmentsId = null;
|
||||
this.trackTypeId = null;
|
||||
this.materialAnimationsId = null;
|
||||
this.materialsTextID = null;
|
||||
this.materialsBeatsID = null;
|
||||
this.textId = null;
|
||||
this.friendlyReminderId = null;
|
||||
this.draft_json = null;
|
||||
this.one_duration_time = 5000000;
|
||||
this.text_end_time = 0;
|
||||
this.iamge_end_time = 0;
|
||||
this.dubbing_emd_time = 0;
|
||||
this.draft_duration_time = 0;
|
||||
this.global = global;
|
||||
this.value = value;
|
||||
this.pm = new PublicMethod(global);
|
||||
}
|
||||
|
||||
async InitData() {
|
||||
this.draft_name = this.global.config.project_name + '_' + this.value[0];
|
||||
let draft_path = path.join(this.global.config.draft_path, this.draft_name);
|
||||
await fspromises.rm(draft_path, { recursive: true, force: true });
|
||||
await compressing.zip.uncompress(define.draft_temp_path, path.join(this.global.config.draft_path, this.global.config.project_name + '_' + this.value[0]));
|
||||
this.draftPath = path.join(draft_path, "draft_content.json");
|
||||
this.image_dir = path.join(this.global.config.project_path, `tmp/${this.value[0]}`);
|
||||
this.srtPath = this.value[1].srt_path;
|
||||
this.style_id = this.value[1].draft_srt_style;
|
||||
this.num = 1;
|
||||
this.mp3_path = this.value[1].audio_path;
|
||||
this.friendlyReminderId = this.value[1].friendly_reminder;
|
||||
this.srt_information = (await this.pm.GetConfigJson(JSON.stringify(["srt_time_information", []]))).data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载默认的草稿
|
||||
* @returns
|
||||
*/
|
||||
async LoadDraftJson() {
|
||||
let draft_json = JSON.parse(await fspromises.readFile(this.draftPath));
|
||||
this.draft_json = draft_json;
|
||||
// console.log(this.draft_json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加speed
|
||||
*/
|
||||
async AddSpeeds() {
|
||||
// 获取speed的模板地址
|
||||
let speed_json = JSON.parse(await fspromises.readFile(define.clip_speed_temp_path));
|
||||
// console.log(speed_json)
|
||||
// 设置ID
|
||||
let speedID = uuidv4().toUpperCase();
|
||||
this.speedId = speedID;
|
||||
speed_json.id = speedID;
|
||||
return speed_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Canvases
|
||||
*/
|
||||
async AddCanvases() {
|
||||
let canvases_json = JSON.parse(await fspromises.readFile(define.add_canvases_temp_path));
|
||||
// console.log(canvases_json);
|
||||
// 设置ID
|
||||
let canvasesId = uuidv4().toUpperCase();
|
||||
this.canvasesId = canvasesId;
|
||||
canvases_json.id = canvasesId;
|
||||
return canvases_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加soundchannel
|
||||
* @returns
|
||||
*/
|
||||
async AddSoundChannelMapping() {
|
||||
let soundChannelMapping_json = JSON.parse(await fspromises.readFile(define.add_sound_channel_mappings_temp_path));
|
||||
// console.log(soundChannelMapping_json)
|
||||
let sound_channel_mappings_tmp_ID = uuidv4().toUpperCase();
|
||||
this.soundChannelId = sound_channel_mappings_tmp_ID;
|
||||
soundChannelMapping_json.id = sound_channel_mappings_tmp_ID;
|
||||
return soundChannelMapping_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 vocal_separations
|
||||
*/
|
||||
async AddVocalSeparations() {
|
||||
let vocal_separations_json = JSON.parse(await fspromises.readFile(define.add_vocal_separations_temp_path));
|
||||
// console.log(vocal_separations_json);
|
||||
let vocalSeparationId = uuidv4().toUpperCase();
|
||||
this.vocalSeparationsId = vocalSeparationId;
|
||||
vocal_separations_json.id = vocalSeparationId;
|
||||
return vocal_separations_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个文件到原材料地址
|
||||
* @param {图片文件地址} imagePath
|
||||
*/
|
||||
async AddMaterialVideo(imagePath) {
|
||||
let materialVideoTmpJson = JSON.parse(await fspromises.readFile(define.add_material_video_temp_path));
|
||||
// console.log(materialVideoTmpJson);
|
||||
let materialId = uuidv4().toUpperCase();
|
||||
this.materialVideoId = materialId;
|
||||
materialVideoTmpJson.id = materialId;
|
||||
|
||||
// 获取输入的图片宽高
|
||||
// let image = await Jimp.read(imagePath);
|
||||
// let width = image.bitmap.width;
|
||||
// let height = image.bitmap.height;
|
||||
materialVideoTmpJson.width = 1000;
|
||||
materialVideoTmpJson.height = 1000;
|
||||
|
||||
materialVideoTmpJson.path = imagePath;
|
||||
let image_name = path.basename(imagePath);
|
||||
materialVideoTmpJson.material_name = image_name;
|
||||
|
||||
return materialVideoTmpJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个轨道
|
||||
* @returns
|
||||
*/
|
||||
async AddTracksSegments() {
|
||||
let tracksJson = JSON.parse(await fspromises.readFile(define.add_tracks_segments_temp_path));
|
||||
// console.log(tracksJson);
|
||||
let tracksSegmentsId = uuidv4().toUpperCase();
|
||||
tracksJson.id = tracksSegmentsId;
|
||||
tracksJson.extra_material_refs = [];
|
||||
tracksJson.extra_material_refs.push(this.speedId);
|
||||
tracksJson.extra_material_refs.push(this.canvasesId);
|
||||
tracksJson.extra_material_refs.push(this.soundChannelId);
|
||||
tracksJson.extra_material_refs.push(this.vocalSeparationsId);
|
||||
tracksJson.material_id = this.materialVideoId;
|
||||
tracksJson.target_timerange.start = this.num * tracksJson.target_timerange.duration;
|
||||
return tracksJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个track
|
||||
* @param {track的类型} type
|
||||
*/
|
||||
async AddTracks(type) {
|
||||
let tracks_json = JSON.parse(await fspromises.readFile(define.add_tracks_type_temp_path));
|
||||
let track_type_id = uuidv4();
|
||||
this.trackTypeId = track_type_id;
|
||||
tracks_json.id = this.trackTypeId;
|
||||
tracks_json.type = type;
|
||||
return tracks_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单个图片到轨道
|
||||
*/
|
||||
async AddOneImageToDraft(image_path) {
|
||||
// 添加 canvases
|
||||
let canvanses = await this.AddCanvases();
|
||||
this.draft_json.materials.canvases.push(canvanses)
|
||||
|
||||
// 添加 sound_channel_mappings
|
||||
let sound_channel = await this.AddSoundChannelMapping();
|
||||
this.draft_json.materials.sound_channel_mappings.push(sound_channel)
|
||||
|
||||
// 添加 speeds
|
||||
let speeds = await this.AddSpeeds();
|
||||
this.draft_json.materials.speeds.push(speeds);
|
||||
|
||||
// 添加 vocal_separations
|
||||
let vocal_sep = await this.AddVocalSeparations();
|
||||
this.draft_json.materials.vocal_separations.push(vocal_sep);
|
||||
|
||||
// 添加视频 materials 下面的 Videos
|
||||
let video = await this.AddMaterialVideo(image_path);
|
||||
this.draft_json.materials.videos.push(video);
|
||||
|
||||
// 添加track轨道
|
||||
let segment = await this.AddTracksSegments();
|
||||
return segment;
|
||||
// this.draft_json.tracks.segments.push(segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将所有的文件全部都写轨道上面
|
||||
*/
|
||||
async AddAllImageToTracks() {
|
||||
let img_dir = path.normalize(this.image_dir);
|
||||
let files = await fspromises.readdir(img_dir)
|
||||
let imageFiles = files.filter(file => /\.(png)$/i.test(file));
|
||||
imageFiles.sort();
|
||||
imageFiles = imageFiles.map(item => path.join(img_dir, item))
|
||||
// console.log(imageFiles);
|
||||
|
||||
// 创建一个tracks
|
||||
let tracks_json = await this.AddTracks("video");
|
||||
//往tracks里面的segments添加图片数据
|
||||
for (let i = 0; i < imageFiles.length; i++) {
|
||||
const image_path = imageFiles[i];
|
||||
let segment = await this.AddOneImageToDraft(image_path);
|
||||
tracks_json.segments.push(segment);
|
||||
// console.log(tracks_json);
|
||||
}
|
||||
|
||||
this.draft_json.tracks.push(tracks_json);
|
||||
// 修改持续时间
|
||||
let duration_time = imageFiles.length * this.one_duration_time;
|
||||
this.iamge_end_time = duration_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加materials中的material_animations
|
||||
*/
|
||||
async AddMaterialAnimations() {
|
||||
let material_animations = JSON.parse(await fspromises.readFile(define.add_material_animations_temp_path));
|
||||
let material_animations_id = uuidv4();
|
||||
this.materialAnimationsId = material_animations_id;
|
||||
material_animations.id = material_animations_id;
|
||||
return material_animations;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为字幕添加样式
|
||||
*/
|
||||
async AddTextStyle(material_text_json) {
|
||||
try {
|
||||
let clip_setting = JSON.parse(await fspromises.readFile(define.clip_setting));
|
||||
let text_style = clip_setting.text_style.filter(item => item.id != "0" && item.id != "1");
|
||||
|
||||
// 添加默认样式
|
||||
let c = JSON.parse(material_text_json.content);
|
||||
let data = JSON.parse(`[{\"size\":7.882736,\"fill\":{\"content\":{\"solid\":{\"color\":[1,1,1]}}},\"range\":[0,5]}]`);
|
||||
data[0].range = [0, c.text.length];
|
||||
c["styles"] = data;
|
||||
material_text_json.content = JSON.stringify(c);
|
||||
return material_text_json;
|
||||
|
||||
// 判断是不是添加样式添加样式
|
||||
if (this.style_id == "0") {
|
||||
return material_text_json;
|
||||
} else if (this.style_id == "1") {
|
||||
// 随机
|
||||
const randomIndex = Math.floor(Math.random() * text_style.length);
|
||||
this.style_id = text_style[randomIndex].id;
|
||||
}
|
||||
let style = text_style.filter(item => item.id == this.style_id)[0];
|
||||
let content = JSON.parse(material_text_json.content);
|
||||
|
||||
// 修改范围
|
||||
let textstring = content.text;
|
||||
let length = textstring.length;
|
||||
style.style[0].range = [0, length];
|
||||
|
||||
content.styles = style.style;
|
||||
material_text_json.content = JSON.stringify(content);
|
||||
let path = style.style[0].font.path;
|
||||
let id = style.style[0].font.id;
|
||||
let font_size = style.font_size;
|
||||
let fonts = style.fonts;
|
||||
let style_name = style.style_name;
|
||||
material_text_json.fonts.id = uuidv4();
|
||||
material_text_json.font_category_id = id;
|
||||
material_text_json.fonts.path = path;
|
||||
material_text_json.fonts.title = fonts;
|
||||
material_text_json.check_flag = font_size;
|
||||
material_text_json.title = fonts;
|
||||
material_text_json.font_size = font_size;
|
||||
material_text_json.style_name = style_name;
|
||||
return material_text_json;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个单的materialsText
|
||||
*/
|
||||
async AddMaterialsText(textString) {
|
||||
let material_text_json = JSON.parse(await fspromises.readFile(define.add_material_text_temp_path));
|
||||
let material_text_id = uuidv4();
|
||||
this.materialsTextID = material_text_id;
|
||||
material_text_json.id = material_text_id;
|
||||
|
||||
// 设置内容
|
||||
let content = JSON.parse(material_text_json.content);
|
||||
content.text = textString;
|
||||
material_text_json.content = JSON.stringify(content);
|
||||
|
||||
material_text_json = await this.AddTextStyle(material_text_json);
|
||||
return material_text_json;
|
||||
}
|
||||
|
||||
async ModifyTextClipTransform(text_segments) {
|
||||
// console.log(text_segments);
|
||||
|
||||
let clip_setting = JSON.parse(await fspromises.readFile(define.clip_setting));
|
||||
let text_style = clip_setting.text_style;
|
||||
if (text_style.length <= 0)
|
||||
return text_segments;
|
||||
let style = text_style.filter(item => item.id == this.style_id)[0];
|
||||
text_segments.clip = style.clip;
|
||||
return text_segments;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个字幕到tracks
|
||||
* @returns
|
||||
*/
|
||||
async AddOneTextToDraft(timeObj) {
|
||||
try {
|
||||
|
||||
// 添加 materials 中的 material_animations
|
||||
let material_animattions = await this.AddMaterialAnimations();
|
||||
this.draft_json.materials.material_animations.push(material_animattions);
|
||||
|
||||
// 添加 materials 下面的texts
|
||||
let material_text = await this.AddMaterialsText(timeObj.text);
|
||||
this.draft_json.materials.texts.push(material_text);
|
||||
|
||||
let text_segments = JSON.parse(await fspromises.readFile(define.add_track_text_segments_temp_path));
|
||||
let textId = uuidv4();
|
||||
this.textId = textId;
|
||||
text_segments.id = textId;
|
||||
text_segments.extra_material_refs = [this.materialAnimationsId];
|
||||
text_segments.material_id = this.materialsTextID;
|
||||
text_segments.target_timerange.start = timeObj.start;
|
||||
text_segments.target_timerange.duration = timeObj.end - timeObj.start;
|
||||
|
||||
// 修改样式偏移量
|
||||
text_segments = await this.ModifyTextClipTransform(text_segments);
|
||||
|
||||
return text_segments;
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error Message ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加所有的text到tracks里面
|
||||
*/
|
||||
async AddAllTextToTrack() {
|
||||
// 添加一个tracks
|
||||
let new_tracks = await this.AddTracks("text");
|
||||
// 计算时间
|
||||
let srt_data = (await fspromises.readFile(this.srtPath)).toString("utf-8");
|
||||
const entries = srt_data.replace(/\r\n/g, '\n').split('\n\n');
|
||||
let data = entries.map(entry => {
|
||||
const lines = entry.split('\n');
|
||||
if (lines.length >= 3) {
|
||||
const times = lines[1];
|
||||
const text = lines.slice(2).join(' ');
|
||||
const [start, end] = times.split(' --> ').map(time => {
|
||||
const [hours, minutes, seconds] = time.split(':');
|
||||
const [sec, millis] = seconds.split(',');
|
||||
return ((parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(sec)) * 1000 + parseInt(millis)) * 1000;
|
||||
});
|
||||
return { start, end, text };
|
||||
}
|
||||
}).filter(entry => entry);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const text = data[i];
|
||||
let text_se = await this.AddOneTextToDraft(text);
|
||||
// console.log(text_se);
|
||||
new_tracks.segments.push(text_se);
|
||||
if (i == data.length - 1) {
|
||||
this.text_end_time = text.end;
|
||||
}
|
||||
}
|
||||
// console.log(this.draft_json)
|
||||
this.draft_json.tracks.push(new_tracks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 materials下面的 beats
|
||||
*/
|
||||
async AddMaterialsBeats() {
|
||||
let beats_json = JSON.parse(await fspromises.readFile(define.add_materials_beats_tmp_path));
|
||||
let materialsBeatsID = uuidv4();
|
||||
this.materialsBeatsID = materialsBeatsID;
|
||||
beats_json.id = materialsBeatsID;
|
||||
return beats_json;
|
||||
}
|
||||
|
||||
|
||||
async getAudioDuration(filePath) {
|
||||
const ext = filePath.split('.').pop().toLowerCase();
|
||||
|
||||
switch (ext) {
|
||||
case 'mp3':
|
||||
try {
|
||||
const metadata = await mm.parseFile(filePath);
|
||||
return metadata.format.duration;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
case 'wav':
|
||||
return new Promise((resolve, reject) => {
|
||||
wavFileInfo.infoByFilename(filePath, (err, info) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(info.duration);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
default:
|
||||
throw new Error("不支持的文件类型");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 materials 下面的 audios
|
||||
*/
|
||||
async AddMaterialsAudios(musicPath) {
|
||||
try {
|
||||
let audios_json = JSON.parse(await fspromises.readFile(define.add_materials_audios_tmp_path));
|
||||
let mp3_name = path.basename(musicPath);
|
||||
let time = await this.getAudioDuration(path.normalize(musicPath));
|
||||
let duration_time = time * 1000000;
|
||||
if (this.audios_duration_time == undefined) {
|
||||
this.audios_duration_time = duration_time;
|
||||
}
|
||||
let audiosID = uuidv4();
|
||||
this.materialsAudiosID = audiosID;
|
||||
audios_json.id = audiosID;
|
||||
audios_json.name = mp3_name;
|
||||
audios_json.duration = duration_time;
|
||||
audios_json.path = musicPath;
|
||||
console.log(audios_json)
|
||||
return audios_json;
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加tracks下面的audios下面的Segments
|
||||
*/
|
||||
async AddAudioTracksSegments() {
|
||||
try {
|
||||
let audio_segments = JSON.parse(await fspromises.readFile(define.add_tracks_audio_segments_tmp_path));
|
||||
let audioId = uuidv4();
|
||||
this.tracksAudioId = audioId;
|
||||
audio_segments.id = audioId;
|
||||
audio_segments.material_id = this.materialsAudiosID;
|
||||
audio_segments.extra_material_refs = [];
|
||||
audio_segments.extra_material_refs.push(this.speedID);
|
||||
audio_segments.extra_material_refs.push(this.materialsBeatsID);
|
||||
audio_segments.extra_material_refs.push(this.soundChannelId);
|
||||
audio_segments.extra_material_refs.push(this.vocalSeparationsId);
|
||||
audio_segments.source_timerange.duration = this.audios_duration_time;
|
||||
audio_segments.target_timerange.duration = this.audios_duration_time;
|
||||
return audio_segments;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加配音
|
||||
*/
|
||||
async AddDubbingMusic(musicPath) {
|
||||
// 添加speeds
|
||||
let speeds = await this.AddSpeeds();
|
||||
this.draft_json.materials.speeds.push(speeds);
|
||||
|
||||
// 添加beats
|
||||
let beats = await this.AddMaterialsBeats();
|
||||
this.draft_json.materials.beats.push(beats);
|
||||
|
||||
// 添加 sound_channel_mappings
|
||||
let sound_channel_mappings = await this.AddSoundChannelMapping();
|
||||
this.draft_json.materials.sound_channel_mappings.push(sound_channel_mappings)
|
||||
|
||||
// 添加 materials 下面的 audios
|
||||
let audios = await this.AddMaterialsAudios(musicPath);
|
||||
this.draft_json.materials.audios.push(audios);
|
||||
|
||||
// 添加一个track
|
||||
let tracks_json = await this.AddTracks("audio");
|
||||
|
||||
let audio_segments_json = await this.AddAudioTracksSegments();
|
||||
tracks_json.segments.push(audio_segments_json);
|
||||
this.draft_json.tracks.push(tracks_json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加背景音乐
|
||||
* @param {背景音乐的ID} background_music_id
|
||||
*/
|
||||
async AddRandomBackfroundMusic(background_music_id) {
|
||||
try {
|
||||
// 获取背景音乐文件夹
|
||||
let clip_setting_json = JSON.parse(await fspromises.readFile(define.clip_setting));
|
||||
let setting = clip_setting_json.background_music_setting.filter(item => item.id == background_music_id);
|
||||
console.log(setting);
|
||||
let folder_path = setting[0].folder_path;
|
||||
console.log(folder_path);
|
||||
let files = await tools.getFilesWithExtensions(folder_path, [".mp3", ".wav"]);
|
||||
if (files.length == 0) {
|
||||
throw new Error("背景音乐文件夹下面未存在数据");
|
||||
}
|
||||
// 获取随机的数据
|
||||
const randomIndex = Math.floor(Math.random() * files.length);
|
||||
let musicPath = files[randomIndex];
|
||||
|
||||
// 添加speeds
|
||||
let speeds = await this.AddSpeeds();
|
||||
this.draft_json.materials.speeds.push(speeds);
|
||||
|
||||
// 添加beats
|
||||
let beats = await this.AddMaterialsBeats();
|
||||
this.draft_json.materials.beats.push(beats);
|
||||
|
||||
// 添加 sound_channel_mappings
|
||||
let sound_channel_mappings = await this.AddSoundChannelMapping();
|
||||
this.draft_json.materials.sound_channel_mappings.push(sound_channel_mappings)
|
||||
|
||||
// 添加 materials 下面的 audios
|
||||
let audios = await this.AddMaterialsAudios(musicPath);
|
||||
// audios.duration = this.text_end_time;
|
||||
this.draft_json.materials.audios.push(audios);
|
||||
|
||||
// 添加一个track
|
||||
let tracks_json = await this.AddTracks("audio");
|
||||
let audio_segments_json = await this.AddAudioTracksSegments();
|
||||
// 修改
|
||||
audio_segments_json.source_timerange.duration = this.audios_duration_time;
|
||||
audio_segments_json.target_timerange.duration = this.audios_duration_time;
|
||||
tracks_json.segments.push(audio_segments_json);
|
||||
this.draft_json.tracks.push(tracks_json);
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加温馨提示
|
||||
*/
|
||||
async AddFriendlyReminder() {
|
||||
// 直接push
|
||||
try {
|
||||
|
||||
return;
|
||||
|
||||
let friendlyReminder = null;
|
||||
let friendlyReminderSetting = JSON.parse(await fspromises.readFile(define.clip_setting)).friendly_reminder_setting;
|
||||
friendlyReminderSetting = friendlyReminderSetting.filter(item => item.id != "0" && item.id != "1");
|
||||
if (friendlyReminderSetting.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.friendlyReminderId == "0") {
|
||||
return;
|
||||
} else if (this.friendlyReminderId == "1") {
|
||||
// 获取随机的数据
|
||||
const randomIndex = Math.floor(Math.random() * friendlyReminderSetting.length);
|
||||
friendlyReminder = friendlyReminderSetting[randomIndex];
|
||||
} else {
|
||||
friendlyReminder = friendlyReminderSetting.filter(item => item.id == this.friendlyReminderId);
|
||||
}
|
||||
|
||||
// 添加 materials 下面的 material_animations
|
||||
this.draft_json.materials.material_animations.push(friendlyReminder.material_animations);
|
||||
// 添加 materials下面的texts
|
||||
this.draft_json.materials.texts.push(friendlyReminder.texts);
|
||||
// 添加 tracks
|
||||
let track = friendlyReminder.tracks;
|
||||
// 修改持续时间
|
||||
track.segments[0].target_timerange.duration = this.audios_duration_time;
|
||||
this.draft_json.tracks.push(track);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改草稿的持续时间
|
||||
*/
|
||||
async ModifyDurationTime() {
|
||||
|
||||
let max_time = Math.max(this.iamge_end_time, this.text_end_time, this.audios_duration_time);
|
||||
this.draft_json.duration = max_time;
|
||||
this.draft_json.canvas_config.height = 1440;
|
||||
this.draft_json.canvas_config.width = 1920;
|
||||
this.draft_json.canvas_config.ratio = "4:3";
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件写道指定的位置
|
||||
*/
|
||||
async WriteDraftFile() {
|
||||
await fspromises.writeFile(this.draftPath, JSON.stringify(this.draft_json));
|
||||
}
|
||||
|
||||
async find_draft_node(nodes, type, value) {
|
||||
for (let index = 0; index < nodes.length; index++) {
|
||||
let node = nodes[index];
|
||||
if (node[type] == value) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将草稿图片和文字对齐
|
||||
*/
|
||||
async AlginDraftImgToText() {
|
||||
// 所有的字幕轨道里面的数据,读取出来
|
||||
let img_nodes = (await this.find_draft_node(this.draft_json.tracks, "type", "video")).segments;
|
||||
|
||||
//将最后一个数据修改为背景音乐的最后时间
|
||||
this.srt_information[this.srt_information.length - 1].end_time = this.audios_duration_time / 1000;
|
||||
|
||||
// 开始对齐
|
||||
for (let i = 0; i < this.srt_information.length; i++) {
|
||||
if (img_nodes.length < i) {
|
||||
break;
|
||||
}
|
||||
const element = this.srt_information[i];
|
||||
let duration = 0;
|
||||
if (i + 1 < this.srt_information.length) {
|
||||
duration = (this.srt_information[i + 1].start_time - element.start_time - 1) * 1000;
|
||||
} else {
|
||||
duration = (element.end_time - element.start_time) * 1000;
|
||||
}
|
||||
img_nodes[i].source_timerange.duration = duration;
|
||||
img_nodes[i].target_timerange.duration = duration;
|
||||
img_nodes[i].target_timerange.start = element.start_time * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 key_frame 返回关键帧数据
|
||||
* @param {*} key_frame 关键帧配置
|
||||
*/
|
||||
async GetFrameData(key_frame) {
|
||||
if (key_frame.key_frame == "KFTypePositionY") {
|
||||
return key_frame.up_down_key_frame;
|
||||
} else if (key_frame.key_frame == "KFTypePositionX") {
|
||||
return key_frame.left_right_key_frame;
|
||||
} else if (key_frame.key_frame == "KFTypeScale") {
|
||||
return key_frame.scale_key_frame;
|
||||
} else {
|
||||
return {
|
||||
"default_scale": 100,
|
||||
"start_position": 0,
|
||||
"end_position": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片添加关键帧
|
||||
*/
|
||||
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"));
|
||||
// 添加关键帧
|
||||
// 将最后一个数据修改为背景音乐的最后时间
|
||||
this.srt_information[this.srt_information.length - 1].end_time = this.audios_duration_time / 1000;
|
||||
|
||||
let key_frame_setting = await tools.getJsonFilePropertyValue(define.clip_setting, "key_frame", null, false);
|
||||
// 判断关键帧配置是不是存在。不存在直接结束
|
||||
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 up_pos = Math.abs(key_frame_pos.start_position);
|
||||
let down_pos = Math.abs(key_frame_pos.end_position);
|
||||
|
||||
if (key_frame_setting.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].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)
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
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;
|
||||
|
||||
// 修改缩放倍率
|
||||
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") {
|
||||
// 勾选了匀速。需要计算时间(计算比例)
|
||||
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();
|
||||
key_frame_tmp.keyframe_list[0].values = [up_pos_rate];
|
||||
|
||||
let dow_pos_rate = isDown ? (0 - down_pos / this.draft_json.canvas_config.width) : (down_pos / this.draft_json.canvas_config.width)
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
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;
|
||||
|
||||
// 修改缩放倍率
|
||||
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") {
|
||||
if (isFixedSpeed && image_duartion < key_frame_time) {
|
||||
let time_rate = image_duartion / key_frame_time;
|
||||
// 计算方式和上面的不同
|
||||
let sub_total = Math.abs(up_pos - down_pos);
|
||||
let currwnt_rate = sub_total * (1 - time_rate);
|
||||
up_pos = up_pos + currwnt_rate / 2;
|
||||
down_pos = down_pos - currwnt_rate / 2;
|
||||
}
|
||||
|
||||
// 修改上面的数据,添加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();
|
||||
key_frame_tmp.keyframe_list[0].values = [up_pos_rate];
|
||||
|
||||
let dow_pos_rate = isDown ? down_pos / 100 : up_pos / 100;
|
||||
key_frame_tmp.keyframe_list[1].id = uuidv4();
|
||||
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";
|
||||
|
||||
// 修改上面的数据,添加Y轴缩放
|
||||
// 修改缩放倍率
|
||||
element.clip.scale.x = isDown ? up_pos : dow_pos_rate;
|
||||
element.clip.scale.y = isDown ? up_pos : dow_pos_rate;
|
||||
element.clip.transform.x = 0;
|
||||
element.common_keyframes.push(key_frame_tmp);
|
||||
|
||||
key_frame_tmp = cloneDeep(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);
|
||||
|
||||
isDown = !isDown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 添加草稿
|
||||
*/
|
||||
async addDraft() {
|
||||
try {
|
||||
await this.InitData();
|
||||
await this.LoadDraftJson();
|
||||
await this.AddAllImageToTracks();
|
||||
await this.AddAllTextToTrack();
|
||||
await this.AddDubbingMusic(path.normalize(this.value[1].audio_path));
|
||||
if (this.value[1].background_music != "" && this.value[1].background_music != undefined && this.value[1].background_music != null) {
|
||||
await this.AddRandomBackfroundMusic(this.value[1].background_music);
|
||||
}
|
||||
|
||||
// 添加温馨提示
|
||||
// await this.AddFriendlyReminder();
|
||||
|
||||
|
||||
await this.ModifyDurationTime();
|
||||
|
||||
// 对齐草稿数据
|
||||
await this.AlginDraftImgToText();
|
||||
|
||||
// 添加关键帧
|
||||
await this.AddKeyFarme();
|
||||
|
||||
await this.WriteDraftFile();
|
||||
return {
|
||||
code: 1,
|
||||
draft_name: this.draft_name
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `An error occurred: ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
import path from "path";
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { get, has } from "lodash";
|
||||
const util = require('util');
|
||||
const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
const fspromises = require("fs").promises;
|
||||
|
||||
export class PublicMethod {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改config.json文件中的指定的属性
|
||||
* @param {*} value 0: 要修改的数据 1: 要修改的属性 2: 是否需要解析
|
||||
* @returns
|
||||
*/
|
||||
async SaveConfigJsonProperty(value) {
|
||||
try {
|
||||
let data = value[0];
|
||||
let property = value[1];
|
||||
let parse = value[2];
|
||||
if (parse) {
|
||||
data = JSON.parse(value[0])
|
||||
}
|
||||
let json_path = path.join(global.config.project_path, "scripts/config.json");
|
||||
// 判断文件是不是存在
|
||||
let isExit = await this.tools.checkExists(json_path);
|
||||
let json_data = {};
|
||||
if (!isExit) {
|
||||
const dirPath = path.dirname(json_path);
|
||||
await fspromises.mkdir(dirPath, { recursive: true });
|
||||
await fspromises.writeFile(json_path, '{}');
|
||||
}
|
||||
else {
|
||||
const o_data = await fspromises.readFile(json_path, 'utf8');
|
||||
// 将读取的 JSON 字符串转换为 JavaScript 对象
|
||||
let obj = JSON.parse(o_data);
|
||||
json_data = obj;
|
||||
}
|
||||
json_data[property] = data;
|
||||
|
||||
await fspromises.writeFile(json_path, JSON.stringify(json_data));
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前项目的config.json文件中的指定的属性信息,若是没有传入属性,则返回所有的信息
|
||||
* @param {Array} value 0 要获取的属性 1 返回的默认值
|
||||
* @param {Boolean} ckeck 是否需要校验属性不存在
|
||||
* @returns
|
||||
*/
|
||||
async GetConfigJson(value, ckeck = true) {
|
||||
try {
|
||||
value = JSON.parse(value)
|
||||
let srt_config_path = path.join(global.config.project_path, "scripts/config.json");
|
||||
let data = await this.tools.getJsonFilePropertyValue(srt_config_path, value[0], value[1], ckeck);
|
||||
return {
|
||||
code: 1,
|
||||
data: data
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改生成图片的任务队列数据
|
||||
* @param {传入的要修改的数据数组} value
|
||||
*/
|
||||
async ModifyImageTaskList(value) {
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
try {
|
||||
let task_list_path = path.join(this.global.config.project_path, "scripts/task_list.json");
|
||||
let isE = await this.tools.checkExists(task_list_path);
|
||||
if (!isE) {
|
||||
throw new Error("任务队列文件不存在。请先添加 批次任务");
|
||||
}
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(task_list_path, "utf-8"));
|
||||
// 循环循环数据。修改
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
let index = task_list_json.task_list.findIndex(item => item.id == element.id);
|
||||
task_list_json.task_list[index] = element;
|
||||
}
|
||||
await fspromises.writeFile(task_list_path, JSON.stringify(task_list_json));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}, "modifyFile", "modifyFile", "task_list")
|
||||
this.global.fileQueue.setSubBatchCompletionCallback("modifyFile", "task_list", async (failedTasks) => {
|
||||
// 报错
|
||||
if (failedTasks.length > 0) {
|
||||
let message = "";
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
throw new Error(message);
|
||||
// this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
// code: 0,
|
||||
// message: message
|
||||
// })
|
||||
}
|
||||
// else {
|
||||
// this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
// code: 1,
|
||||
// message: "修改成功"
|
||||
// })
|
||||
// }
|
||||
return true;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 高清指定的文件夹
|
||||
* @param {要高清的文件夹} folder
|
||||
*/
|
||||
async ImproveFolder(folder) {
|
||||
try {
|
||||
let bakPath = path.join(this.global.config.project_path, "tmp/bak");
|
||||
let oldInput = path.join(this.global.config.project_path, "tmp/" + folder);
|
||||
let newInput = path.join(this.global.config.project_path, "tmp/bak/" + folder)
|
||||
// 创建文件夹
|
||||
let existFolder = await this.tools.checkExists(bakPath);
|
||||
if (!existFolder) {
|
||||
await fspromises.mkdir(bakPath, { recursive: true });
|
||||
}
|
||||
|
||||
let isExistNewFolder = await this.tools.checkExists(newInput);
|
||||
if (isExistNewFolder) {
|
||||
await fspromises.rm(newInput, { recursive: true, force: true });
|
||||
}
|
||||
// 备份文件
|
||||
await fspromises.rename(oldInput, newInput);
|
||||
//创建同名的文件,用作输出
|
||||
await fspromises.mkdir(oldInput, { recursive: true });
|
||||
|
||||
// 开始高清
|
||||
let command = `"${path.join(define.package_path, "Improve/rnv.exe")}" -i "${newInput}" -o "${oldInput}"`;
|
||||
let out = await execAsync(command, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' });
|
||||
console.log(out);
|
||||
await this.ModifyTaskStatus('out_folder', folder, "video_improvied");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: folder,
|
||||
status: "video_improvied"
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
await this.ModifyTaskStatus('out_folder', folder, "video_improvie_error");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: folder,
|
||||
status: "video_improvie_error"
|
||||
})
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成SD相对的JSON文件。删除反推的txt文件
|
||||
*/
|
||||
async AddWebuiJson() {
|
||||
try {
|
||||
// 读取所有txt文件
|
||||
let txtfile = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.txt');
|
||||
let image = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.png');
|
||||
let promptJson = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.json');
|
||||
|
||||
// json 已经存在,不做后续处理
|
||||
if (image.length == promptJson.length) {
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
}
|
||||
|
||||
if (txtfile.length != image.length) {
|
||||
throw new Error("关键词文件和图片数量对不上,请检查!!")
|
||||
}
|
||||
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
|
||||
for (let i = 0; i < image.length; i++) {
|
||||
const element = image[i];
|
||||
let txtpath = element.split('.png')[0] + '.txt';
|
||||
let prompt = await fspromises.readFile(txtpath, 'utf-8');
|
||||
// console.log(txtpath)
|
||||
let obj = {}
|
||||
obj.model = sd_config.setting.type;
|
||||
obj.api = sd_config.setting.webui_api_url + 'sdapi/v1/img2img';
|
||||
obj.webui_config = {
|
||||
sampler_name: sd_config.webui.sampler_name,
|
||||
prompt: prompt + ',' + sd_config.webui.prompt,
|
||||
negative_prompt: sd_config.webui.negative_prompt,
|
||||
batch_size: 1,
|
||||
steps: sd_config.webui.steps,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
denoising_strength: sd_config.webui.denoising_strength,
|
||||
width: sd_config.webui.width,
|
||||
height: sd_config.webui.height,
|
||||
seed: sd_config.setting.seed,
|
||||
init_images: element,
|
||||
}
|
||||
obj.adetailer = sd_config.webui.adetailer;
|
||||
|
||||
// 写入
|
||||
await fspromises.writeFile(element + '.json', JSON.stringify(obj));
|
||||
// 删除对应的txt文件
|
||||
await fspromises.unlink(txtpath);
|
||||
}
|
||||
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前项目的生图任务列表
|
||||
*/
|
||||
async GetImageTask() {
|
||||
try {
|
||||
let json_path = path.join(this.global.config.project_path, "scripts/task_list.json");
|
||||
let isExit = await this.tools.checkExists(json_path);
|
||||
if (!isExit) {
|
||||
return {
|
||||
code: 1,
|
||||
data: null
|
||||
}
|
||||
}
|
||||
let json_data = JSON.parse(await fspromises.readFile(json_path));
|
||||
return {
|
||||
code: 1,
|
||||
data: json_data
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改任务的状态
|
||||
* @param {查找的类型 id out_folder} type
|
||||
* @param {查找的类型} id
|
||||
* @param {新的状态} newStatus
|
||||
*/
|
||||
async ModifyTaskStatus(type, id, newStatus) {
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
try {
|
||||
// 将修改数据写入到一个并发为 1 的队列中
|
||||
let tmp_task = await fspromises.readFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), 'utf-8');
|
||||
console.log(tmp_task)
|
||||
let task = JSON.parse(tmp_task);
|
||||
if (type == "id") {
|
||||
let index = task.task_list.findIndex(item => item.id == id);
|
||||
if (index < 0) {
|
||||
throw new Error("传入的数据未找到");
|
||||
} else {
|
||||
task.task_list[index].status = newStatus;
|
||||
}
|
||||
} else if (type == "out_folder") {
|
||||
let index = task.task_list.findIndex(item => item.out_folder == id);
|
||||
if (index < 0) {
|
||||
throw new Error("传入的数据未找到");
|
||||
} else {
|
||||
task.task_list[index].status = newStatus;
|
||||
}
|
||||
} else {
|
||||
throw new Error("输入类型错误")
|
||||
}
|
||||
await fspromises.writeFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), JSON.stringify(task));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}, "modifyFile", "modifyFile", "task_list")
|
||||
this.global.fileQueue.setSubBatchCompletionCallback("modifyFile", "task_list", async (failedTasks) => {
|
||||
// 报错
|
||||
if (failedTasks.length > 0) {
|
||||
let message = "";
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件夹下面特定条件的文件夹
|
||||
* @param {指定的文件夹目录} parentFolder
|
||||
* @param {查询条件 start end include} condition
|
||||
* @param {查询的值} value
|
||||
* @returns
|
||||
*/
|
||||
async getSubFolderList(parentFolder, condition, value) {
|
||||
try {
|
||||
// console.log(value);
|
||||
let folders = await fspromises.readdir(parentFolder, { withFileTypes: true });
|
||||
folders = folders.filter(item => item.isDirectory())
|
||||
.map(item => item.name)
|
||||
|
||||
if (condition == "start") {
|
||||
folders = folders.filter(item => item.startsWith(value));
|
||||
} else if (condition == "end") {
|
||||
folders = folders.filter(item => item.endsWith(value));
|
||||
} else if (condition == "include") {
|
||||
//包含过滤
|
||||
folders = folders.filter(item => item.includes(value));
|
||||
} else {
|
||||
throw new Error("条件参数错误");
|
||||
}
|
||||
return folders;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
|
||||
import { Tools } from "../tools";
|
||||
import path from "path";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { define } from "../../define/define";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { SD } from "../Public/SD"
|
||||
const util = require('util');
|
||||
const { spawn, exec } = require('child_process');
|
||||
import axios from "axios";
|
||||
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";
|
||||
|
||||
|
||||
export class ImageGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.pm = new PublicMethod(global);
|
||||
this.sd = new SD(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的提示词json文件数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async GetPromptJson(value) {
|
||||
try {
|
||||
let json_path = path.join(this.global.config.project_path, "tmp/input_crop/" + value + ".json");
|
||||
let isExist = await this.tools.checkExists(json_path);
|
||||
if (!isExist) {
|
||||
throw new Error("提示词文件不存在");
|
||||
}
|
||||
let res = JSON.parse(await fspromises.readFile(json_path, 'utf-8'));
|
||||
return {
|
||||
value: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定任务队列的状态
|
||||
* @param {*} value 0 使用什么查找(id。out_folder,) 1查找的值 2 状态值
|
||||
* @returns
|
||||
*/
|
||||
async ModifyGenerateTaskStatus(value) {
|
||||
try {
|
||||
await this.pm.ModifyTaskStatus(value[0], value[1], value[2]);
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 修改本地的参数
|
||||
* @param {修改json的参数} value
|
||||
*/
|
||||
async ModifyInpurCropJson(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let prompt = value.prompt;
|
||||
// 重绘幅度
|
||||
let denoising_strength = value.denoising_strength;
|
||||
let json_path = path.join(path.join(global.config.project_path, 'tmp/input_crop'), value.name + ".json");
|
||||
let old_json = JSON.parse(await fspromises.readFile(json_path, 'utf-8'));
|
||||
old_json.webui_config.prompt = prompt;
|
||||
old_json.webui_config.denoising_strength = denoising_strength;
|
||||
old_json.model = value.model;
|
||||
old_json.adetailer = value.adetailer;
|
||||
old_json.chinese_prompt = value.chinese_prompt;
|
||||
|
||||
if (value.modify_old) {
|
||||
old_json.webui_config.init_images = value.modify_old;
|
||||
}
|
||||
|
||||
await fspromises.writeFile(json_path, JSON.stringify(old_json));
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件夹里的所有的图片,以及其他信息
|
||||
* @param {图片文件夹} value
|
||||
* @returns
|
||||
*/
|
||||
async getFolderImageList(mainWindow, value, isReturn) {
|
||||
try {
|
||||
let img_dir = path.normalize(value);
|
||||
let base_name = path.basename(img_dir);
|
||||
let tmp = path.dirname(img_dir);
|
||||
let input_path = path.join(tmp, "input_crop")
|
||||
// console.log(tmp, input_path)
|
||||
let files = await fspromises.readdir(img_dir)
|
||||
let imageFiles = files.filter(file => /\.(png)$/i.test(file));
|
||||
imageFiles.sort();
|
||||
|
||||
let res = [];
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, 'scripts/task_list.json'), 'utf-8'));
|
||||
// 处理数据
|
||||
for (let i = 0; i < imageFiles.length; i++) {
|
||||
const img = imageFiles[i];
|
||||
let imput_path = path.join(input_path, img);
|
||||
let img_json = path.join(input_path, img + '.json');
|
||||
// 判断文件是不是存在
|
||||
let prompt_json = JSON.parse(await fspromises.readFile(img_json));
|
||||
|
||||
let output_list = await this.pm.getSubFolderList(path.join(this.global.config.project_path, "tmp"), 'start', 'output_crop');
|
||||
let output_image_list = [];
|
||||
for (let i = 0; i < output_list.length; i++) {
|
||||
const item = output_list[i];
|
||||
// 找到找到文件对应的任务列表的样式和lora
|
||||
let task_list = task_list_json.task_list.filter(a => a.out_folder == item);
|
||||
let output_1 = path.join(tmp, item);
|
||||
let obj = {
|
||||
id: uuidv4(),
|
||||
img_path: path.join(output_1, img),
|
||||
image_style: task_list[0].image_style,
|
||||
lora: task_list[0].lora
|
||||
}
|
||||
output_image_list.push(obj);
|
||||
|
||||
}
|
||||
let main_task_list = task_list_json.task_list.filter(a => a.out_folder == base_name);
|
||||
let obj = {
|
||||
name: img,
|
||||
id: uuidv4(),
|
||||
sampler_name: prompt_json.webui_config.sampler_name,
|
||||
prompt: prompt_json.webui_config.prompt,
|
||||
chinese_prompt: prompt_json.chinese_prompt,
|
||||
negative_prompt: prompt_json.webui_config.negative_prompt,
|
||||
denoising_strength: prompt_json.webui_config.denoising_strength,
|
||||
seed: prompt_json.webui_config.seed,
|
||||
image: path.join(img_dir, img),
|
||||
oldImage: prompt_json.webui_config.init_images,
|
||||
output_list: output_image_list,
|
||||
model: prompt_json.model,
|
||||
image_style: main_task_list[0].image_style,
|
||||
lora: main_task_list[0].lora,
|
||||
image_style_list: main_task_list[0].image_style_list,
|
||||
adetailer: prompt_json.adetailer,
|
||||
width: prompt_json.webui_config.width,
|
||||
height: prompt_json.webui_config.height
|
||||
}
|
||||
res.push(obj)
|
||||
// 获取提示词
|
||||
}
|
||||
if (isReturn) {
|
||||
return {
|
||||
code: 1,
|
||||
windowID: mainWindow.id,
|
||||
value: res
|
||||
}
|
||||
} else {
|
||||
mainWindow.webContents.send(DEFINE_STRING.RETURN_IMAGE_PROMPT, {
|
||||
code: 1,
|
||||
windowID: mainWindow.id,
|
||||
value: res
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error message ${error.toString()}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反推的图片的信息添加到一个json文件中
|
||||
*/
|
||||
async AddWebuiJson() {
|
||||
try {
|
||||
return await this.pm.AddWebuiJson();
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务队列数据
|
||||
* @param {替换后的值} value
|
||||
*/
|
||||
async ModifyImageTaskList(value) {
|
||||
try {
|
||||
let res = await this.pm.ModifyImageTaskList(value);
|
||||
if (res) {
|
||||
return {
|
||||
code: 1,
|
||||
message: "替换成功"
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成图片的任务队列(可以添加部分判断)
|
||||
*/
|
||||
async GetGenerateTaskList() {
|
||||
try {
|
||||
return await this.pm.GetImageTask()
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成任务中的队列
|
||||
* @param {传入所有执行生图队列的任务} value
|
||||
*/
|
||||
async GenerateImageInSelectTask(value) {
|
||||
try {
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE
|
||||
let taskPath = path.join(this.global.config.project_path, 'scripts/task_list.json');
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
let auto_save_image = await this.tools.getJsonFilePropertyValue(define.img_base, "auto_save_image", {}, false);
|
||||
let png_files = [];
|
||||
// 读取队列中的任务,然后添加队列
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const element = value[i];
|
||||
let task_list = task_list_json.task_list.filter(item => item.id == element)[0];
|
||||
let seed = -1;
|
||||
let images = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.png');
|
||||
await fspromises.mkdir(path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder), { recursive: true });
|
||||
if (images.length <= 0) {
|
||||
throw new Error("未检测到抽帧图片。请检查");
|
||||
}
|
||||
if (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 () => {
|
||||
let res = await this.sd.OneImageGeneration(images[0], task_list, seed);
|
||||
let tmp_seed = -1;
|
||||
if (seed == -1) {
|
||||
tmp_seed = res;
|
||||
}
|
||||
for (let j = 1; j < images.length; j++) {
|
||||
const item = images[j];
|
||||
|
||||
let has_permission = false;
|
||||
// 判断权限
|
||||
let permission = this.global.permission;
|
||||
|
||||
if (permission && permission.length >= 0) {
|
||||
if (permission.indexOf(DEFINE_STRING.PERMISSIONS.AUTO_SAVE_IMAGE_PERMISSION) >= 0) {
|
||||
has_permission = true;
|
||||
}
|
||||
} else {
|
||||
has_permission = true;
|
||||
}
|
||||
|
||||
if (auto_save_image.save_match_count && j >= auto_save_image.save_match_count && has_permission && auto_save_image.auto_match) {
|
||||
// 现在随机匹配视频
|
||||
// 获取指定的文件夹中的图片
|
||||
let randomData = png_files[Math.floor(Math.random() * png_files.length)];
|
||||
let base_name = path.basename(item);
|
||||
let copy_path = path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder, base_name);
|
||||
await this.tools.copyFileOrDirectory(randomData, copy_path);
|
||||
} else {
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.sd.OneImageGeneration(item, task_list, tmp_seed);
|
||||
}, `${task_list.out_folder}_${images[j]}`, batch, task_list.out_folder)
|
||||
}
|
||||
}
|
||||
|
||||
}, `${task_list.out_folder}_${images[0]}`, batch, task_list.out_folder)
|
||||
// }
|
||||
task_list.status = 'queue';
|
||||
|
||||
// 开始批次执行(全部刷新)
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, task_list);
|
||||
|
||||
// 判断是不是还有批次执行的任务
|
||||
// 监听子批次完成(修改当前批次的状态)
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, task_list.out_folder, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
|
||||
} else {
|
||||
// 判断是不是有错误。没有错误的话。直接修改状态。有错误直接记录错误(写入一个就行)
|
||||
task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
// 全部执行完毕之后,修改
|
||||
task_list_json.task_list.map(a => {
|
||||
if (a.out_folder == task_list.out_folder) {
|
||||
a.status = "ok";
|
||||
}
|
||||
})
|
||||
// 写入
|
||||
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json));
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, {
|
||||
out_folder: task_list.out_folder,
|
||||
status: "ok"
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: task_list.out_folder,
|
||||
status: "ok"
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 监听总批次完成
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
批次生成任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有生成任务完成"
|
||||
})
|
||||
}
|
||||
})
|
||||
// 写回
|
||||
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json));
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量高清文件夹
|
||||
* @param {项目下面的文件夹的值} value
|
||||
*/
|
||||
async ImproveResolution(value) {
|
||||
try {
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.LOCAL_IMAGE_IMPROVE;
|
||||
|
||||
// 加入队列
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const folder = value[i];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.pm.ImproveFolder(folder);
|
||||
}, folder, batch)
|
||||
|
||||
// 添加队列后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', folder, "video_improving");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: folder,
|
||||
status: "video_improving"
|
||||
})
|
||||
}
|
||||
|
||||
global.requestQuene.setBatchCompletionCallback(batch, async (failedTasks) => {
|
||||
|
||||
let permission = this.global.permission;
|
||||
// 判断权限
|
||||
if (permission && permission.length >= 0) {
|
||||
if (permission.indexOf(DEFINE_STRING.PERMISSIONS.AUTO_SAVE_IMAGE_PERMISSION) >= 0) {
|
||||
// 在高清前,先将要高清的图片保存
|
||||
let res = await ImageSetting.SaveImageToOtherFolder(value, null);
|
||||
if (res.code == 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 在高清前,先将要高清的图片保存
|
||||
let res = await ImageSetting.SaveImageToOtherFolder(value, null);
|
||||
if (res.code == 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
高清任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有任务高清完成"
|
||||
})
|
||||
}
|
||||
})
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件到指定位置
|
||||
* @param {*} value 0: 源图片地址 1: 类型 2: 目标文件地址
|
||||
*/
|
||||
async DownloadImageFile(value) {
|
||||
try {
|
||||
let type = value[1];
|
||||
let img_file = value[0];
|
||||
let target_file = value[2];
|
||||
if (type == "cover") {
|
||||
// 下载封面
|
||||
// 判断封面是不是存在
|
||||
let cover_path = path.join(this.global.config.project_path, 'data/封面');
|
||||
let isExist = await this.tools.checkExists(cover_path);
|
||||
if (!isExist) {
|
||||
await fspromises.mkdir(cover_path, { recursive: true });
|
||||
}
|
||||
// 复制
|
||||
await fspromises.copyFile(img_file, path.join(cover_path, Date.now() + '.png'));
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功!"
|
||||
}
|
||||
} else if (type == "replace") {
|
||||
if (!target_file) {
|
||||
throw new Error("目标文件地址不存在");
|
||||
}
|
||||
await fspromises.copyFile(path.normalize(decodeURIComponent(img_file)), path.normalize(decodeURIComponent(target_file)));
|
||||
await this.tools.copyFileOrDirectory(decodeURIComponent(img_file), decodeURIComponent(target_file).replace("output_crop_1", "input_crop"));
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
return {
|
||||
code: 0,
|
||||
message: "参数错误"
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
import path from "path";
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { func } from '../func'
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
const util = require('util');
|
||||
const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
const fspromises = require("fs").promises;
|
||||
import { SD } from "../Public/SD"
|
||||
|
||||
export class VideoGenerate {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.pm = new PublicMethod(global);
|
||||
this.sd = new SD(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取之前的基础配置
|
||||
*/
|
||||
async GetVideoGenerateConfig() {
|
||||
try {
|
||||
let res = await this.tools.getJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "video_config", {}, false);
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存合成视频的基础信息(Srt,audio,background)
|
||||
* @param {*} value 保存合成视频的基础信息(Srt,audio,background)
|
||||
*/
|
||||
async SaveVideoSrtAndAudioMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "video_config", value);
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*全自动合成视频的任务(出图、高清、合成视频)
|
||||
* @param {传入的需要执行的task_list的任务} value
|
||||
*/
|
||||
async ActionAutoVideoTask(value) {
|
||||
try {
|
||||
// 开始添加队列任务
|
||||
// 生图
|
||||
// 将当前的所有任务添加到队列中
|
||||
await this.pm.AddWebuiJson();
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE;
|
||||
let taskPath = path.join(this.global.config.project_path, "scripts/task_list.json");
|
||||
|
||||
// // 修改数据(保存srt字幕文件位置。配音文件位置。背景音乐文件)
|
||||
// this.global.fileQueue.enqueue(async () => {
|
||||
// let config_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, "scripts/config.json"), 'utf-8'));
|
||||
// config_json.srt_path = value[1].srt_path;
|
||||
// config_json.audio_path = value[1].audio_path;
|
||||
// config_json.background_music = value[1].background_music;
|
||||
// await fspromises.writeFile(path.join(this.global.config.project_path, "scripts/config.json"), JSON.stringify(config_json));
|
||||
// })
|
||||
|
||||
await this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/config.json"), "video_config", value[1]);
|
||||
|
||||
// 便利所有的队列任务
|
||||
for (let i = 0; i < value[0].length; i++) {
|
||||
// 将所有的数据天添加到队列(总的大队列,有很多的小队列)
|
||||
// 将所有生图任务添加到队列中
|
||||
const task_list = value[0][i];
|
||||
let seed = -1;
|
||||
let subBatchId = `${task_list.out_folder}_image`
|
||||
let images = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, 'tmp/input_crop'), '.png');
|
||||
await fspromises.mkdir(path.join(this.global.config.project_path, 'tmp/' + task_list.out_folder), { recursive: true });
|
||||
if (images.length <= 0) {
|
||||
throw new Error("未检测到抽帧图片。请检查");
|
||||
}
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
let res = await this.sd.OneImageGeneration(images[0], task_list, seed);
|
||||
let tmp_seed = -1;
|
||||
if (seed == -1) {
|
||||
tmp_seed = res;
|
||||
}
|
||||
for (let j = 1; j < images.length; j++) {
|
||||
const element = images[j];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.sd.OneImageGeneration(element, task_list, tmp_seed);
|
||||
}, `${task_list.out_folder}_${images[j]}`, batch, subBatchId)
|
||||
}
|
||||
|
||||
}, `${task_list.out_folder}_${images[0]}`, batch, subBatchId)
|
||||
// }
|
||||
task_list.status = 'queue';
|
||||
task_list["isAuto"] = "true";
|
||||
await this.pm.ModifyImageTaskList([task_list]);
|
||||
// 修改状态
|
||||
// await this.pm.ModifyTaskStatus("id", task_list.id, "queue");
|
||||
// this.global.fileQueue.enqueue(async () => {
|
||||
// let task_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
// let index = task_json.task_list.findIndex(item => item.id == task_list.id);
|
||||
// task_json.task_list[index] = task_list;
|
||||
// await fspromises.writeFile(taskPath, JSON.stringify(task_json));
|
||||
// })
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
|
||||
// 判断是不是还有批次执行的任务
|
||||
// 监听子批次完成(修改当前批次的状态)
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, subBatchId, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
// 之前的任务出现错误
|
||||
// 执行错误
|
||||
} else {
|
||||
// 判断是不是有错误。没有错误的话。直接修改状态。有错误直接记录错误(写入一个就行)
|
||||
task_list.status = "ok";
|
||||
await this.pm.ModifyTaskStatus("id", task_list.id, 'ok');
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
console.log(subBatchId + "生图执行完毕。可以开始执行高清")
|
||||
|
||||
// 添加高清队列
|
||||
// task_list
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.pm.ImproveFolder(task_list.out_folder);
|
||||
}, `${task_list.out_folder}_improve`, batch, `${task_list.out_folder}_improve`);
|
||||
// 添加队列后修改状态
|
||||
task_list.status = "video_improving"
|
||||
await this.pm.ModifyTaskStatus('out_folder', task_list.out_folder, "video_improving");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
|
||||
// 监听高清任务完成
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, `${task_list.out_folder}_improve`, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
// 之前的任务出现错误
|
||||
// 执行错误
|
||||
} else {
|
||||
|
||||
console.log(task_list.out_folder + "高清完成,可以开始合成视频");
|
||||
// 添加生成视频队列
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
let video_config = JSON.parse(await fspromises.readFile(define.video_config, 'utf-8'));
|
||||
await this.AutoGeneretionOneVide(task_list.out_folder, video_config, value);
|
||||
}, `${task_list.out_folder}_video`, batch, `${task_list.out_folder}_video`);
|
||||
task_list.status = "video_queue";
|
||||
// 添加后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', task_list.out_folder, "video_queue");
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
|
||||
// 监听生成视频任务完成
|
||||
this.global.requestQuene.setSubBatchCompletionCallback(batch, `${task_list.out_folder}_video`, async (failedTasks) => {
|
||||
console.log(failedTasks)
|
||||
if (failedTasks.length > 0) {
|
||||
// 之前的任务出现错误
|
||||
// 执行错误
|
||||
} else {
|
||||
console.log(task_list.out_folder + "合成视频完成");
|
||||
// 添加生成视频队列
|
||||
task_list.status = "video_ok";
|
||||
// 添加后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', task_list.out_folder, "video_ok");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, task_list)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 监听总批次完成
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, async (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
批次生成任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
console.log("所有的自动生成任务完成");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有自动任务完成"
|
||||
})
|
||||
}
|
||||
|
||||
this.global.fileQueue.enqueue(async () => {
|
||||
// 读取最新的数据删除
|
||||
// 将自动化标识删除
|
||||
let task_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
||||
for (let i = 0; i < task_json.task_list.length; i++) {
|
||||
let task_list = task_json.task_list[i];
|
||||
task_list["isAuto"] = "false";
|
||||
await this.pm.ModifyImageTaskList([task_list]);
|
||||
}
|
||||
})
|
||||
})
|
||||
return {
|
||||
code: 1,
|
||||
message: "自动任务添加到队列中"
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {
|
||||
code: 0,
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件夹下面的子文件夹,添加判断条件
|
||||
* @param {value} value
|
||||
* @returns
|
||||
*/
|
||||
async getSubFolderList(value) {
|
||||
try {
|
||||
let folder = await this.pm.getSubFolderList(path.join(this.global.config.project_path, "tmp"), value[0], value[1]);
|
||||
// 找到所有的数据(查询状态返回)
|
||||
let task_path = path.join(this.global.config.project_path, "scripts/task_list.json");
|
||||
let isExist = await this.tools.checkExists(task_path);
|
||||
let data = [];
|
||||
if (!isExist) {
|
||||
for (let i = 0; i < folder.length; i++) {
|
||||
const element = folder[i];
|
||||
let obj = {
|
||||
folder: element,
|
||||
status: "unkown error"
|
||||
}
|
||||
data.push(obj);
|
||||
}
|
||||
} else {
|
||||
let task_list = JSON.parse(await fspromises.readFile(task_path, 'utf-8'))["task_list"];
|
||||
// 查询状态
|
||||
for (let i = 0; i < folder.length; i++) {
|
||||
const element = folder[i];
|
||||
let index = task_list.findIndex(item => item.out_folder == element);
|
||||
if (index < 0) {
|
||||
let obj = {
|
||||
folder: element,
|
||||
status: "unkown error"
|
||||
}
|
||||
data.push(obj);
|
||||
} else {
|
||||
let status = task_list[index].status;
|
||||
let obj = {
|
||||
folder: element,
|
||||
status: status
|
||||
}
|
||||
data.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
data: data
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error Message ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成一个视频
|
||||
* @param {视频生成图片的文件夹} element
|
||||
* @param {视频配置} video_config
|
||||
* @param {参数。第一个值为数组,为生成的图片文件。第二个参数是配置} value
|
||||
*/
|
||||
async AutoGeneretionOneVide(element, video_config, value) {
|
||||
try {
|
||||
let background_music = "";
|
||||
// 读取背景音乐的路径
|
||||
if (value[1].background_music != "") {
|
||||
let background_music_config = (await func.getClipSetting("background_music_setting")).value;
|
||||
let background_music_filter = background_music_config.filter(item => item.id == value[1].background_music);
|
||||
if (background_music_filter.length <= 0) {
|
||||
throw new Error("背景音乐对应的配置没有找到");
|
||||
} else {
|
||||
background_music = background_music_filter[0].folder_path;
|
||||
}
|
||||
}
|
||||
|
||||
// 随机获取字幕设置
|
||||
let ass_config = video_config.assConfig;
|
||||
let ass_random_index = Math.floor(Math.random() * ass_config.length);
|
||||
let watermark_config = video_config.watermarkConfig;
|
||||
let watermark_random_index = Math.floor(Math.random() * watermark_config.length);
|
||||
|
||||
// 对每个视频生成配置文件并将其添加到配置文件中
|
||||
let obj = {
|
||||
srt_path: value[1].srt_path,
|
||||
// 字幕样式(需要随机)
|
||||
srt_style: ass_config[ass_random_index],
|
||||
audio_path: value[1].audio_path,
|
||||
background_music_folder: background_music,
|
||||
// 水印设置(需要随机)
|
||||
friendly_reminder: watermark_config[watermark_random_index],
|
||||
video_resolution_x: video_config.video_resolution_x,
|
||||
video_resolution_y: video_config.video_resolution_y,
|
||||
outpue_file: path.join(this.global.config.project_path, this.global.config.project_name + element + ".mp4"),
|
||||
image_folder: path.join(this.global.config.project_path, 'tmp/' + element),
|
||||
srt_config: path.join(this.global.config.project_path, 'scripts/config.json'),
|
||||
mp4_file_txt: path.join(this.global.config.project_path, `scripts/${element}.txt`),
|
||||
status: "no",
|
||||
audio_sound_size: video_config.audioSoundSize,
|
||||
background_music_sound_size: video_config.backgroundMusicSoundSize,
|
||||
keyFrame: video_config.keyframe,
|
||||
frameRate: video_config.frameRate,
|
||||
bitRate: video_config.bitRate
|
||||
}
|
||||
// 将配置文件写入
|
||||
let project_config_path = path.join(this.global.config.project_path, `scripts/${element}.json`);
|
||||
await fspromises.writeFile(project_config_path, JSON.stringify(obj));
|
||||
// let task_list = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path,'scripts/task_')));
|
||||
let scriptPath = path.join(define.scripts_path, 'Lai.exe');
|
||||
// 执行生成图片的脚本
|
||||
let script = `cd "${define.scripts_path}" && "${scriptPath}" -c "${project_config_path.replaceAll('\\', '/')}"`;
|
||||
const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' });
|
||||
if (output.stderr != '') {
|
||||
obj.status = "video_error";
|
||||
obj.stdout = output.stdout;
|
||||
obj.stderr = output.stderr;
|
||||
await this.pm.ModifyTaskStatus('out_folder', element, "video_error");
|
||||
throw new Error(output.stderr);
|
||||
} else {
|
||||
obj.status = "video_ok";
|
||||
obj.stdout = output.stdout;
|
||||
obj.stderr = output.stderr;
|
||||
await this.pm.ModifyTaskStatus('out_folder', element, "video_ok");
|
||||
// 将写出的视频中的exif数据删除
|
||||
// await this.tools.deletePngAndDeleteExifData(obj.outpue_file, path.join(this.global.config.project_path, this.global.config.project_name + "_" + element.split('_')[element.split('_').length - 1] + ".mp4"));
|
||||
}
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: element,
|
||||
status: obj.status
|
||||
})
|
||||
} catch (error) {
|
||||
//手动修改
|
||||
let task_list_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, "scripts/task_list.json"), 'utf-8'));
|
||||
let index = task_list_json.task_list.findIndex(item => item.out_folder == element);
|
||||
if (index < 0) {
|
||||
throw new Error("未找到对应的任务");
|
||||
}
|
||||
task_list_json.task_list[index].status = "video_error";
|
||||
task_list_json.task_list[index].errorMessage = error.toString();
|
||||
// 写回
|
||||
await fspromises.writeFile(path.join(this.global.config.project_path, "scripts/task_list.json"), JSON.stringify(task_list_json));
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: element,
|
||||
status: "video_error"
|
||||
})
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自动合成任务到队列中去
|
||||
* @param {} value
|
||||
*/
|
||||
async AutoGeneretionVideo(value) {
|
||||
try {
|
||||
// 先检查所有的条件
|
||||
let batch = DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE_SINGLE;
|
||||
// console.log(value);
|
||||
let video_config = JSON.parse(await fspromises.readFile(define.video_config, 'utf-8'));
|
||||
let res = await this.CheckVideoGenerattionAllCondition(video_config, value[1], value[0]);
|
||||
if (res.code == 0) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, res);
|
||||
return res;
|
||||
}
|
||||
for (let i = 0; i < value[0].length; i++) {
|
||||
const element = value[0][i];
|
||||
this.global.requestQuene.enqueue(async () => {
|
||||
await this.AutoGeneretionOneVide(element, video_config, value)
|
||||
}, element, batch);
|
||||
|
||||
// 添加队列后修改状态
|
||||
await this.pm.ModifyTaskStatus('out_folder', element, "video_queue");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH, {
|
||||
out_folder: element,
|
||||
status: "video_queue"
|
||||
})
|
||||
}
|
||||
|
||||
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => {
|
||||
if (failedTasks.length > 0) {
|
||||
let message = `
|
||||
生成视频任务都已完成。
|
||||
但是以下任务执行失败:
|
||||
`
|
||||
failedTasks.forEach(({ taskId, error }) => {
|
||||
message += `${taskId}-, \n 错误信息: ${error}` + '\n';
|
||||
});
|
||||
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: message
|
||||
})
|
||||
} else {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 1,
|
||||
message: "所有生成视频任务已完成"
|
||||
})
|
||||
}
|
||||
})
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测生成视频条件的必要条件
|
||||
* @param {生成视频配置文件} video_config
|
||||
* @param {生成视频的基础配置} simpleData
|
||||
* @param {输出文件夹位置,为null,检查input文件里面的图片文件是不是和配置文件中的图片数量一致} out_folder
|
||||
* @returns
|
||||
*/
|
||||
async CheckVideoGenerattionAllCondition(video_config, simpleData, out_folder) {
|
||||
let message = "";
|
||||
if (video_config == null) {
|
||||
video_config = JSON.parse(await fspromises.readFile(define.video_config, 'utf-8'));
|
||||
}
|
||||
// 基础信息检测
|
||||
if (video_config.audioSoundSize == null) {
|
||||
message += "配音大小不能为空" + '\n';
|
||||
}
|
||||
if (video_config.backgroundMusicSoundSize == null) {
|
||||
message += "背景音乐音量大学不能为空" + '\n';
|
||||
}
|
||||
if (video_config.video_resolution_x == null) {
|
||||
message += "生成视频的宽度不能为空" + '\n';
|
||||
}
|
||||
if (video_config.video_resolution_y == null) {
|
||||
message += "生成视频的高度不能为空" + '\r\n';
|
||||
}
|
||||
if (video_config.offsetValue == null) {
|
||||
message += "视频的上下偏移量不能为空" + '\n';
|
||||
}
|
||||
if (video_config.frameRate == null) {
|
||||
message += "生成视频的帧率不能为空" + '\n';
|
||||
}
|
||||
if (video_config.bitRate == null) {
|
||||
message += "生成视频码率的不能为空" + '\n';
|
||||
}
|
||||
|
||||
// 判断字幕列表中是不是有数据
|
||||
if (video_config.assConfig == null || video_config.assConfig.length <= 0) {
|
||||
message += "字幕设置最少包含一条" + '\n';
|
||||
}
|
||||
if (video_config.watermarkConfig == null || video_config.watermarkConfig.length <= 0) {
|
||||
message += "水印设置最少包含一条" + '\n';
|
||||
}
|
||||
// 判断背景音乐文件夹中是不是存在。并且检查其中是不是有音乐文件
|
||||
if (simpleData.background_music == "") {
|
||||
message += "背景音乐文件夹路径不能为空" + '\n';
|
||||
}
|
||||
// 判断背景音乐文件夹是不是存在
|
||||
if (await this.tools.checkExists(simpleData.background_music)) {
|
||||
message += "背景音乐文件夹不存在" + '\n';
|
||||
}
|
||||
// 判断里面是不是有MP3或者是wav
|
||||
let clip_json = JSON.parse(await fspromises.readFile(define.clip_setting, 'utf-8'))["background_music_setting"];
|
||||
let background_music_obj = clip_json.filter(item => item.id == simpleData.background_music)[0];
|
||||
let mp3_file = await this.tools.getFilesWithExtensions(background_music_obj.folder_path, '.mp3');
|
||||
let wav_file = await this.tools.getFilesWithExtensions(background_music_obj.folder_path, '.wav');
|
||||
if (mp3_file.length <= 0 && wav_file.length <= 0) {
|
||||
message += "背景文件夹中没有 MP3 或 WAV 文件" + '\n';
|
||||
}
|
||||
|
||||
let config_path = path.join(this.global.config.project_path, 'scripts/config.json');
|
||||
let isE = await this.tools.checkExists(config_path);
|
||||
if (!isE) {
|
||||
message += "配置文件不存在。请先导入字幕文件。并调整时间轴。"
|
||||
} else {
|
||||
// 判断文案时间信息和图片信息是不是相同
|
||||
let config_json = JSON.parse(await fspromises.readFile(config_path, 'utf-8'));
|
||||
let len = config_json["srt_time_information"].length;
|
||||
if (out_folder == null) {
|
||||
// 判断输入文件中的配置文件中的数量是不是对上
|
||||
let img_l = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/input_crop"), '.png');
|
||||
if (img_l.length != len) {
|
||||
message += `input_crop 文件里面图片和文案信息对不上。检查是不是图片数量不对`
|
||||
}
|
||||
// 判断是不是有tag文件
|
||||
let tag_txt = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/input_crop"), '.txt');
|
||||
let c_j = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/input_crop"), '.json');
|
||||
if (tag_txt.length > 0) {
|
||||
if (tag_txt.length != img_l.length) {
|
||||
message += "反推的tag文件和图片的数量对不上。" + "\n";
|
||||
}
|
||||
} else if (c_j.length > 0) {
|
||||
if (c_j.length != img_l.length) {
|
||||
message += "已存在的配置文件和图片的数量对不上。" + "\n";
|
||||
}
|
||||
} else {
|
||||
message += "好像没有反推呢" + "\n";
|
||||
}
|
||||
} else {
|
||||
// 判断当前的输入的 out_folder 中图片数量是不是可以和配置文件中能否对上
|
||||
for (let i = 0; i < out_folder.length; i++) {
|
||||
const element = out_folder[i];
|
||||
let image_l = await this.tools.getFilesWithExtensions(path.join(this.global.config.project_path, "tmp/" + element), '.png');
|
||||
if (image_l.length != len) {
|
||||
message += `${element} 文件里面图片和文案信息对不上。检查是不是图片数量不对`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message != '') {
|
||||
let res = {
|
||||
code: 0,
|
||||
message: message
|
||||
}
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, res)
|
||||
return res;
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
let path = require("path");
|
||||
let fspromises = require("fs").promises;
|
||||
import { Tools } from "../tools";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { PublicMethod } from "../Public/publicMethod";
|
||||
import { define } from "../../define/define";
|
||||
import { get, has } from "lodash";
|
||||
import { ClipSetting } from "../../define/setting/clipSetting";
|
||||
const { v4: uuidv4 } = require('uuid'); // 引入UUID库来生成唯一标识符
|
||||
let tools = new Tools();
|
||||
|
||||
export class Writing {
|
||||
constructor(global) {
|
||||
this.global = global
|
||||
this.pm = new PublicMethod(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文案信息写入到本地的文案文件中
|
||||
* @param {*} value
|
||||
*/
|
||||
async SaveWordTxt(value) {
|
||||
try {
|
||||
let word_path = path.join(global.config.project_path, "文案.txt");
|
||||
await tools.writeArrayToFile(value, word_path);
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功"
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分镜的时间信息添加道配置文件中
|
||||
* @param {*} value 是一个数组,0 :写入的数据 1:写入的属性 2:是否需要解析
|
||||
*/
|
||||
async SaveCopywritingInformation(value) {
|
||||
try {
|
||||
return await this.pm.SaveConfigJsonProperty(value);
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Config.json文件中指定的属性
|
||||
* @param {Array} value 传入的值 0 : 需要获取的属性 1: 返回的默认值
|
||||
* @returns
|
||||
*/
|
||||
async GetConfigJson(value) {
|
||||
try {
|
||||
return await this.pm.GetConfigJson(value, false);
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前项目下面的文案
|
||||
*/
|
||||
async GetProjectWord() {
|
||||
try {
|
||||
// 先判断当前的项目文件下面是不是又配置文件。没有才读取文案
|
||||
let srt_config_path = path.join(global.config.project_path, "scripts/config.json");
|
||||
let isExist = await tools.checkExists(srt_config_path);
|
||||
let data = null;
|
||||
let isImformation = false;
|
||||
if (isExist) {
|
||||
let config_1 = JSON.parse(await fspromises.readFile(srt_config_path));
|
||||
isImformation = has(config_1, 'srt_time_information');
|
||||
if (isImformation) {
|
||||
data = JSON.parse(await fspromises.readFile(srt_config_path)).srt_time_information;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isExist || !isImformation) {
|
||||
let word_path = path.join(global.config.project_path, "文案.txt");
|
||||
let isExistWord = await tools.checkExists(word_path);
|
||||
if (!isExistWord) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "没有文案文件"
|
||||
}
|
||||
}
|
||||
let data = await fspromises.readFile(word_path, { encoding: 'utf-8' });
|
||||
let lines = data.split(/\r?\n/);
|
||||
// 打印或返回这个数组
|
||||
// console.log(lines);
|
||||
// 判断是不是有洗稿后的文件
|
||||
let new_srt_path = path.join(global.config.project_path, "new_word.txt");
|
||||
let isExistAfterGPTWord = await tools.checkExists(new_srt_path);
|
||||
let after_data = null;
|
||||
if (isExistAfterGPTWord) {
|
||||
after_data = (await fspromises.readFile(new_srt_path, { encoding: 'utf-8' })).split(/\r?\n/);
|
||||
}
|
||||
// 判断抽帧文件是不是存在
|
||||
// 返回图片信息
|
||||
let old_image_path_list = await tools.getFilesWithExtensions(path.join(global.config.project_path, "tmp/input_crop"), '.png');
|
||||
let res = [];
|
||||
let lastId = '';
|
||||
// 处理数据
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
let id = uuidv4();
|
||||
let after_gpt = null;
|
||||
if (after_data != null) {
|
||||
after_gpt = after_data[i];
|
||||
}
|
||||
|
||||
let img_path = null;
|
||||
if (old_image_path_list != null) {
|
||||
img_path = old_image_path_list[i];
|
||||
}
|
||||
let obj = {
|
||||
no: i + 1,
|
||||
id: id,
|
||||
lastId: lastId,
|
||||
word: line,
|
||||
old_image: img_path,
|
||||
after_gpt: after_gpt,
|
||||
start_time: null,
|
||||
end_time: null,
|
||||
timeLimit: null,
|
||||
subValue: []
|
||||
}
|
||||
res.push(obj);
|
||||
lastId = id;
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
type: 0,
|
||||
data: res
|
||||
}
|
||||
} else {
|
||||
let data = JSON.parse(await fspromises.readFile(srt_config_path)).srt_time_information;
|
||||
return {
|
||||
code: 1,
|
||||
type: 1,
|
||||
data: data
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搭导入srt。然后加载时间轴。完全匹配失败的将会还是会导入然后手动手动切换
|
||||
* @param {文案洗稿界面信息} textData
|
||||
*/
|
||||
async ImportSrtAndGetTime(data) {
|
||||
let textData = data[0];
|
||||
let init_num = textData.length;
|
||||
let srt_path = data[1];
|
||||
let current_text = "";
|
||||
try {
|
||||
if (!srt_path) {
|
||||
// 获取项目下面的所有的srt
|
||||
let srtfiles = await tools.getFilesWithExtensions(global.config.project_path, '.srt');
|
||||
if (srtfiles.length <= 0) {
|
||||
throw new Error("没有SRT文件");
|
||||
}
|
||||
srt_path = srtfiles[0];
|
||||
}
|
||||
let srt_data = (await fspromises.readFile(srt_path, 'utf-8')).toString("utf-8");
|
||||
const entries = srt_data.replace(/\r\n/g, '\n').split('\n\n');
|
||||
let data = entries.map(entry => {
|
||||
const lines = entry.split('\n');
|
||||
if (lines.length >= 3) {
|
||||
const times = lines[1];
|
||||
const text = lines.slice(2).join(' ');
|
||||
const [start, end] = times.split(' --> ').map(time => {
|
||||
const [hours, minutes, seconds] = time.split(':');
|
||||
const [sec, millis] = seconds.split(',');
|
||||
return ((parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(sec)) * 1000 + parseInt(millis));
|
||||
});
|
||||
return { start, end, text, id: uuidv4() };
|
||||
}
|
||||
}).filter(entry => entry);
|
||||
// 开始匹配(洗稿后的)
|
||||
let srt_list = [];
|
||||
let srt_obj = null
|
||||
let text_count = 0;
|
||||
let tmp_str = "";
|
||||
for (let i = 0; i < data.length;) {
|
||||
let srt_value = data[i].text;
|
||||
current_text = `字幕: “${srt_value}” 和文案第${text_count + 1} 行数据 “${textData[text_count].after_gpt}” 数据不匹配(检查一下上下文)`;
|
||||
let start_time = data[i].start;
|
||||
let end_time = data[i].end;
|
||||
let obj = {
|
||||
start_time,
|
||||
end_time,
|
||||
srt_value,
|
||||
id: data[i].id
|
||||
};
|
||||
// 判断当前字幕是不是在当前句
|
||||
// 不能用简单的包含,而是将数据进行去除特殊符号拼接后判断是不是相同
|
||||
tmp_str += srt_value;
|
||||
if (tools.removePunctuationIncludingEllipsis(textData[text_count].after_gpt).startsWith(tools.removePunctuationIncludingEllipsis(tmp_str))) {
|
||||
if (srt_obj == null) {
|
||||
srt_obj = {}
|
||||
srt_obj.id = uuidv4();
|
||||
srt_obj.start_time = start_time;
|
||||
srt_obj.value = srt_value;
|
||||
srt_obj.subValue = [obj];
|
||||
}
|
||||
else {
|
||||
srt_obj.value = srt_obj.value + srt_value;
|
||||
srt_obj.subValue = [...srt_obj.subValue, obj];
|
||||
}
|
||||
textData[text_count].start_time = srt_obj.start_time;
|
||||
textData[text_count].subValue = srt_obj.subValue
|
||||
srt_list.push(obj);
|
||||
i++;
|
||||
} else {
|
||||
// 判断下一句文件是不是以当当前巨开头。是的话继续。不是的话。直接返回后面的所有信息
|
||||
if (tools.removePunctuationIncludingEllipsis(textData[text_count + 1].after_gpt).startsWith(tools.removePunctuationIncludingEllipsis(srt_value))) {
|
||||
textData[text_count].end_time = srt_list[srt_list.length - 1].end_time;
|
||||
text_count++;
|
||||
srt_obj = null;
|
||||
tmp_str = ""
|
||||
} else {
|
||||
// 将下面的数据直接 添加到textData后面。
|
||||
// 修改当前行数据的结束事件为
|
||||
if (srt_list.length > 0) {
|
||||
textData[text_count].end_time = srt_list[srt_list.length - 1].end_time;
|
||||
text_count++;
|
||||
}
|
||||
// 将后面的数据直接添加
|
||||
let lastId = textData[textData.length - 1].id;
|
||||
for (let j = i; j < data.length; j++) {
|
||||
// 直接修改原有数据
|
||||
if (text_count < init_num) {
|
||||
textData[text_count].subValue = [{
|
||||
start_time: data[j].start,
|
||||
end_time: data[j].end,
|
||||
id: data[j].id,
|
||||
srt_value: data[j].text
|
||||
}]
|
||||
textData[text_count].start_time = data[j].start;
|
||||
textData[text_count].end_time = data[j].end;
|
||||
text_count++;
|
||||
}
|
||||
else {
|
||||
let id = uuidv4();
|
||||
// 添加
|
||||
let obj = {
|
||||
no: j + 1,
|
||||
id: id,
|
||||
word: null,
|
||||
lastId: lastId,
|
||||
old_image: path.normalize(define.zhanwei_image),
|
||||
after_gpt: null,
|
||||
start_time: data[j].start,
|
||||
end_time: data[j].end,
|
||||
subValue: [{
|
||||
start_time: data[j].start,
|
||||
end_time: data[j].end,
|
||||
id: data[j].id,
|
||||
srt_value: data[j].text
|
||||
}]
|
||||
}
|
||||
lastId = id;
|
||||
textData.push(obj);
|
||||
}
|
||||
}
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
|
||||
code: 0,
|
||||
message: current_text
|
||||
})
|
||||
return {
|
||||
code: 1,
|
||||
data: textData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 最后对齐
|
||||
textData[textData.length - 1].end_time = srt_list[srt_list.length - 1].end_time
|
||||
// 返回数据
|
||||
return {
|
||||
code: 1,
|
||||
data: textData
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
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
|
||||
this.apiClient = axios.create({
|
||||
baseURL: 'https://discord.com'
|
||||
});
|
||||
this.DiscordBaseUrl = 'https://discord.com';
|
||||
this.ServerId = mj_setting.serviceID;
|
||||
this.ChannelId = mj_setting.channelID;
|
||||
this.userToken = mj_setting.token;
|
||||
this.botId = mj_setting.select_robot?.botId;
|
||||
this.commandId = mj_setting.select_robot?.commandId;
|
||||
this.versionId = mj_setting.select_robot?.versionId;
|
||||
this.versionName = mj_setting.select_robot?.versionName;
|
||||
this.botName = mj_setting.select_robot?.botName;
|
||||
}
|
||||
|
||||
// 提交任务
|
||||
async imagine(data) {
|
||||
|
||||
// let req_data = {
|
||||
// "token": this.userToken,
|
||||
// "method": "post",
|
||||
// "api_url": "/mj/submit/imagine",
|
||||
// "data":data
|
||||
// }
|
||||
|
||||
// const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
// await this.interactions(data.prompt);
|
||||
return await this.interactions(data.prompt)
|
||||
// return {
|
||||
// code:1,
|
||||
// result:'taskid_'+new Date().getTime()
|
||||
// }
|
||||
// return response.data;
|
||||
|
||||
}
|
||||
async channelList() {
|
||||
axios.get(`https://discord.com/api/v9/channels/${this.ChannelId}/messages?limit=20`, {
|
||||
method: 'get',
|
||||
headers: {
|
||||
"Authorization": this.userToken
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
// 请求成功处理
|
||||
console.log(typeof response.data);
|
||||
let eList = []
|
||||
let flg = false;
|
||||
let job_id = '';
|
||||
let type = '';
|
||||
if (response && response.data) {
|
||||
try {
|
||||
response.data.forEach(element => {
|
||||
flg = false;
|
||||
type = '';
|
||||
if (element.attachments && element.attachments.length) {
|
||||
const arr = element.attachments[0].filename.split("_");
|
||||
job_id = arr[arr.length - 1].replace(".png", '');
|
||||
if (element.components) {
|
||||
element.components.forEach(e2 => {
|
||||
e2.components.forEach(e3 => {
|
||||
if (e3.label == 'U1') {
|
||||
flg = true;
|
||||
type = 'U1'
|
||||
} else if (e3.label && e3.label.indexOf('Upscale') > -1) {
|
||||
flg = true;
|
||||
type = 'Upscale'
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
let t = eList.find((e) => {
|
||||
return e.filename == element.attachments[0].filename;
|
||||
})
|
||||
|
||||
if (!t) {
|
||||
eList.push(
|
||||
{
|
||||
flg: flg, job_id: job_id, filename: element.attachments[0].filename,
|
||||
url: element.attachments[0].url,
|
||||
proxy_url: element.attachments[0].proxy_url,
|
||||
type: type,
|
||||
timestamp: element.timestamp
|
||||
})
|
||||
}
|
||||
|
||||
// console.log({flg:flg,job_id:job_id,filename:element.attachments[0].filename})
|
||||
} else {
|
||||
console.log({ flg: flg })
|
||||
}
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('异常2', error)
|
||||
}
|
||||
// console.log(eList)
|
||||
// 8e7406df-bf0c-4e3d-8e49-b2bb8e2c263d
|
||||
// 5abedc71-ba80-4756-8ddc-489c927d3acd
|
||||
}
|
||||
}).catch(error => {
|
||||
// 请求失败处理
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
// 混合
|
||||
async blend(data) {
|
||||
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": "/mj/submit/blend",
|
||||
"data": data
|
||||
}
|
||||
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 反推
|
||||
async describe(data) {
|
||||
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": "/mj/submit/describe",
|
||||
"data": data
|
||||
}
|
||||
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
// 获取任务
|
||||
async getTaskId(task_id) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "get",
|
||||
"api_url": `/mj/task/${task_id}/fetch`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//获取seed
|
||||
async imageSeed(task_id) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "get",
|
||||
"api_url": `/mj/task/${task_id}/image-seed`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
//账号创建
|
||||
async account_create(data) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": '/mj/account/create',
|
||||
"data": data
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data, { timeout: 20000 });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//账号创建
|
||||
async account_fetch(cid) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "get",
|
||||
"api_url": `/mj/account/${cid}/fetch`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
//账号同步信息
|
||||
async account_asyn_info(cid) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": `/mj/account/${cid}/sync-info`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data, { timeout: 20000 });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//账号删除信息
|
||||
async account_del_info(cid) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "delete",
|
||||
"api_url": `/mj/account/${cid}/delete`,
|
||||
"data": {}
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
//执行动作
|
||||
async action(data) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": '/mj/submit/action',
|
||||
"data": data
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// 确认弹窗
|
||||
async modal(data) {
|
||||
let req_data = {
|
||||
"token": this.userToken,
|
||||
"method": "post",
|
||||
"api_url": '/mj/submit/modal',
|
||||
"data": data
|
||||
}
|
||||
const response = await this.apiClient.post('/api/v3/req_mj_api', req_data);
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
async interactions(prompt) {
|
||||
// 直接自己调用
|
||||
prompt = prompt.trim();
|
||||
// prompt = "4k,8k,best quality, masterpiece, woman, divorced, leaving fast-paced city life, serene expression, walking away from cityscape, bustling streets, entering tranquil countryside, peaceful surroundings, rejuvenating atmosphere, , --niji 5 --ar 4:3"
|
||||
var payload = {};
|
||||
if (this.botName == 'niji') {
|
||||
payload = {
|
||||
"type": 2,
|
||||
"application_id": this.botId,
|
||||
"guild_id": this.ServerId,
|
||||
"channel_id": this.ChannelId,
|
||||
"session_id": this.userToken,
|
||||
"data": {
|
||||
"version": this.versionId,
|
||||
"id": this.commandId,
|
||||
"name": "imagine",
|
||||
"type": 1,
|
||||
"options": [
|
||||
{
|
||||
"type": 3,
|
||||
"name": "prompt",
|
||||
"value": prompt
|
||||
}
|
||||
],
|
||||
"application_command": {
|
||||
"id": this.commandId,
|
||||
"type": 1,
|
||||
"application_id": this.botId,
|
||||
"version": this.versionId,
|
||||
"name": "imagine",
|
||||
"description": "Create images with Midjourney",
|
||||
"options": [
|
||||
{
|
||||
"type": 3,
|
||||
"name": "prompt",
|
||||
"description": "The prompt to imagine",
|
||||
"required": true,
|
||||
"description_localized": "The prompt to imagine",
|
||||
"name_localized": "prompt"
|
||||
}
|
||||
],
|
||||
"integration_types": [
|
||||
0
|
||||
],
|
||||
"global_popularity_rank": 1,
|
||||
"description_localized": "Create images with Midjourney",
|
||||
"name_localized": "imagine"
|
||||
},
|
||||
"attachments": [
|
||||
|
||||
]
|
||||
},
|
||||
// "nonce": "1210857131343872000",
|
||||
"analytics_location": "slash_ui"
|
||||
}
|
||||
} else {
|
||||
payload = {
|
||||
"type": 2,
|
||||
// "application_id":"1022952195194359889",//niji
|
||||
"application_id": this.botId,
|
||||
"guild_id": this.ServerId,
|
||||
"channel_id": this.ChannelId,
|
||||
"session_id": this.userToken,
|
||||
"data": {
|
||||
"version": this.versionId,
|
||||
"id": this.commandId,
|
||||
"name": "imagine", "type": 1,
|
||||
"options":
|
||||
[{
|
||||
"type": 3,
|
||||
"name": "prompt",
|
||||
"value": prompt
|
||||
}],
|
||||
"application_command": {
|
||||
"id": this.commandId,
|
||||
"type": 1,
|
||||
"application_id": this.botId,
|
||||
"version": this.versionId,
|
||||
"name": "imagine",
|
||||
"description": "Create images with Niji journey",
|
||||
"options": [{ "type": 3, "name": "prompt", "description": "The prompt to imagine", "required": true, "description_localized": "The prompt to imagine", "name_localized": "prompt" }],
|
||||
"integration_types": [0], "global_popularity_rank": 1, "description_localized": "Create images with Niji journey", "name_localized": "imagine"
|
||||
},
|
||||
"attachments": []
|
||||
},
|
||||
"analytics_location": "slash_ui"
|
||||
}
|
||||
}
|
||||
|
||||
let response = {
|
||||
status: 200,
|
||||
data: {}
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: this.userToken,
|
||||
};
|
||||
await fetch(`${this.DiscordBaseUrl}/api/v9/interactions`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
headers: headers,
|
||||
}).then(res => res.json()).then(res => {
|
||||
response.data = res;
|
||||
}).catch(e => {
|
||||
console.error("请求失败了,详细信息:" + JSON.stringify(e));
|
||||
response = {
|
||||
status: 500,
|
||||
data: JSON.stringify(e)
|
||||
};
|
||||
});
|
||||
console.log('response结果')
|
||||
console.log(response)
|
||||
// if (response.status == 204) {
|
||||
// //成功
|
||||
|
||||
// }
|
||||
if (response.status >= 400) {
|
||||
console.error("api.error.config", {
|
||||
payload: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
return {
|
||||
code: response.status,
|
||||
response: response
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return 500;
|
||||
}
|
||||
return;
|
||||
const client = new Midjourney.Midjourney({
|
||||
ServerId: this.ServerId,
|
||||
ChannelId: this.ChannelId,
|
||||
SalaiToken: this.userToken,
|
||||
Debug: true,
|
||||
fetch: fetch,
|
||||
Ws: true, //enable ws is required for remix mode (and custom zoom)
|
||||
});
|
||||
await client.init();
|
||||
console.log('mjmj_begin2', prompt);
|
||||
// const prompt =
|
||||
// "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration";
|
||||
//imagine
|
||||
const Imagine = await client.Imagine(
|
||||
prompt,
|
||||
(uri, progress) => {
|
||||
client.Close();
|
||||
|
||||
console.log("loading", uri, "progress", progress);
|
||||
return
|
||||
}
|
||||
);
|
||||
console.log(Imagine);
|
||||
if (!Imagine) {
|
||||
console.log("no message");
|
||||
console.log('mjmj_end2')
|
||||
return;
|
||||
}
|
||||
console.log('mjmj_end')
|
||||
client.Close();
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道内的机器人
|
||||
* @returns 返回机器人列表
|
||||
*/
|
||||
async getBotList() {
|
||||
try {
|
||||
|
||||
const headers = {
|
||||
'Host': 'discord.com',
|
||||
'Connection': 'keep-alive',
|
||||
'authorization': this.userToken,
|
||||
}
|
||||
|
||||
await fetch('https://discord.com/api/v9/guilds/1182523906855284826/application-command-index', {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
})
|
||||
.then(response => {
|
||||
response.json()
|
||||
}
|
||||
)
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error)
|
||||
})
|
||||
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async getMjMsgList() {
|
||||
// const headers = {
|
||||
// "Content-Type": "application/json",
|
||||
// Authorization:this.userToken,
|
||||
// };
|
||||
let response = {
|
||||
status: 200,
|
||||
data: {}
|
||||
};
|
||||
// // `https://discord.com/api/v9/channels/${mj_channelId}/messages?limit=10
|
||||
// await fetch(`${this.DiscordBaseUrl}/api/v9/channels/${this.ChannelId}/messages?limit=50`, {
|
||||
// method: "GET",
|
||||
// headers: headers,
|
||||
// }).then(res => res.json()).then(res => {
|
||||
// response.data=res;
|
||||
// }).catch(e => {
|
||||
// console.error("请求失败了,详细信息:" + JSON.stringify(e));
|
||||
// response ={
|
||||
// status:500,
|
||||
// data:JSON.stringify(e)
|
||||
// };
|
||||
// });
|
||||
// console.log('getMjMsgList_response结果')
|
||||
// // console.log(response)
|
||||
|
||||
// return response;
|
||||
axios.get(`https://discord.com/api/v9/channels/${this.ChannelId}/messages?limit=20`, {
|
||||
method: 'get',
|
||||
headers: {
|
||||
"Authorization": this.userToken
|
||||
}
|
||||
}).then(res => {
|
||||
response.data = res;
|
||||
}).catch(error => {
|
||||
// 请求失败处理
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
export class DiscordRequestListener {
|
||||
constructor(thisWindow) {
|
||||
this.listeners = {};
|
||||
this.thisWindow = thisWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加网络请求前的监听人物
|
||||
* @param {*} key 存储的属性名称
|
||||
* @param {*} listener 执行的方法
|
||||
* @returns
|
||||
*/
|
||||
async AddRequestListener(key, listener) {
|
||||
// 判断当前key是不是存在
|
||||
if (this.listeners[key]) {
|
||||
return;
|
||||
}
|
||||
this.listeners[key] = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定的key事件
|
||||
* @param {*} key
|
||||
*/
|
||||
async RemoveRequestListener(key) {
|
||||
if (this.listeners[key]) {
|
||||
delete this.listeners[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听指定的窗口的网络请求
|
||||
*/
|
||||
async start() {
|
||||
// 获取session
|
||||
// 获取Authorization
|
||||
// 获取User-Agent
|
||||
const mySession = this.thisWindow.webContents.session; // 获取 session
|
||||
// 使用 webRequest API 监听请求
|
||||
mySession.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
|
||||
for (const key in this.listeners) {
|
||||
if (Object.hasOwnProperty.call(this.listeners, key)) {
|
||||
const element = this.listeners[key];
|
||||
element(details);
|
||||
}
|
||||
}
|
||||
callback({ cancel: false, requestHeaders: details.requestHeaders });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
// @noBytecode
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框,并聚焦
|
||||
* @returns 返回获取的消息输入框
|
||||
*/
|
||||
function getInputFoucs() {
|
||||
let selector = 'div[contenteditable="true"][class^="markup_"]';
|
||||
const inputElement = document.querySelector(selector) // as HTMLInputElement;
|
||||
if (!inputElement) {
|
||||
throw new Error('没有找到对应的消息输入框');
|
||||
}
|
||||
inputElement.focus()
|
||||
return inputElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 image_id 的图片
|
||||
*/
|
||||
function GetGeneratedMJImageAndSplit(str) {
|
||||
let res = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const element = str[i];
|
||||
let image_id = element.image_id;
|
||||
let links = document.getElementsByTagName('a');
|
||||
// 创建一个空数组来存储包含特定字符串的链接
|
||||
let result = [];
|
||||
// 遍历所有的链接
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
// 如果链接的href属性包含特定的字符串,就把它添加到结果数组中
|
||||
if (links[i].href.includes(image_id)) {
|
||||
result.push(links[i].href);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
res.push({
|
||||
id: element.id,
|
||||
image_id: element.image_id,
|
||||
result: result[0],
|
||||
name: element.name
|
||||
});
|
||||
}
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}
|
||||
|
||||
window['updateObserver'] = {};
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框的X,Y位置,每次输入前都要检测一下
|
||||
*/
|
||||
function GetMessageInputPosition() {
|
||||
try {
|
||||
let inputElement = getInputFoucs();
|
||||
const rect = inputElement.getBoundingClientRect();
|
||||
// 计算鼠标点击位置(假设点击在元素中心)
|
||||
const mouseX = rect.left + rect.width / 2;
|
||||
const mouseY = rect.top + rect.height / 2;
|
||||
sightingTelescope(mouseX, mouseY)
|
||||
return { mouseX, mouseY };
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 瞄准镜
|
||||
*/
|
||||
function sightingTelescope(x, y) {
|
||||
var div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.left = (x - 1) + "px";
|
||||
div.style.top = (y - 15) + "px";
|
||||
div.style.width = '30px';
|
||||
div.style.height = '30px';
|
||||
div.style.zIndex = '9999';
|
||||
div.style.border = '1px dashed green';
|
||||
div.style.color = 'green';
|
||||
div.style.textAlign = 'center';
|
||||
div.style.fontWeight = '600';
|
||||
div.style.borderRadius = '50%';
|
||||
div.style.fontSize = '30px';
|
||||
div.textContent = '+';
|
||||
document.body.appendChild(div);
|
||||
setTimeout(function () {
|
||||
div.parentNode.removeChild(div);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断discord中的输入框中是不是有数据直接返回
|
||||
* 有数据返回true,无数据返回false
|
||||
*/
|
||||
function GetMessageInputValue() {
|
||||
let inputElement = getInputFoucs();
|
||||
// 判断是不是为空
|
||||
let emptyElement = inputElement.querySelector('span[class^="emptyText_"]');
|
||||
if (emptyElement) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听指定的元素
|
||||
*/
|
||||
function MutationObserverElement() {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('ol[class^="scrollerInner_"][data-list-id^="chat-message"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = { childList: true, subtree: true };
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
if (mutation.addedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
||||
const element = mutation.addedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 新增消息", element)
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 新增消息", element);
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mutation.removedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
const element = mutation.removedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
|
||||
// MutationObserverUpdateElement(id)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
|
||||
stopUpdateObserver(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
stopUpdateObserver(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 创建一个观察器实例并传入回调函数
|
||||
window.observer = new MutationObserver(callback);
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window.observer.observe(messageList, config);
|
||||
}
|
||||
|
||||
|
||||
// 往上找到指定的标签
|
||||
function findParentNode(startNode) {
|
||||
let data = {};
|
||||
let currentNode = startNode;
|
||||
|
||||
while (currentNode !== null) {
|
||||
|
||||
if (!data["currentNode"] && currentNode.id && currentNode.id.startsWith('message-content')) {
|
||||
data["firstNode"] = currentNode;
|
||||
}
|
||||
|
||||
if (currentNode.getAttribute && currentNode.getAttribute('data-list-item-id') && currentNode.getAttribute('data-list-item-id').startsWith('chat-messages')) {
|
||||
data["currentNode"] = currentNode;
|
||||
return data;
|
||||
}
|
||||
currentNode = currentNode.parentNode;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function gatherNodeData(node) {
|
||||
let result = node.textContent || ''; // 获取当前节点的文本内容
|
||||
return result;
|
||||
// 遍历所有子节点
|
||||
}
|
||||
|
||||
// 获取一个数据的当前消息的数据
|
||||
function getMessageData(mainData) {
|
||||
if (!mainData["currentNode"]) {
|
||||
window.discord.UpdateMessage(JSON.stringify({ code: 0, message: "没有找到对应的消息" }))
|
||||
return;
|
||||
}
|
||||
// 获取当前的消息ID
|
||||
|
||||
let res = {};
|
||||
|
||||
let data_arr = mainData["currentNode"].getAttribute('data-list-item-id')?.split('-');
|
||||
let message_id = data_arr[data_arr.length - 1];
|
||||
let channel_id = data_arr[data_arr.length - 2];
|
||||
res["message_id"] = message_id;
|
||||
res["channel_id"] = channel_id;
|
||||
|
||||
// 判断是不是又违规的消息
|
||||
let text = mainData["currentNode"].textContent.toLowerCase().includes("Banned prompt detected");
|
||||
|
||||
|
||||
if (text) {
|
||||
res["error"] = "Banned prompt detected";
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res))
|
||||
}
|
||||
|
||||
// 获取提示词相关的数据
|
||||
// 开始拼接提示词数据
|
||||
let message_prompt = null;
|
||||
if (!mainData["firstNode"]) {
|
||||
message_prompt = null;
|
||||
} else {
|
||||
message_prompt = gatherNodeData(mainData["firstNode"]);
|
||||
}
|
||||
res["message_prompt"] = message_prompt;
|
||||
|
||||
// 判断是不是有图片信息
|
||||
let image_content = mainData["currentNode"].querySelector('div[id^="message-accessories-' + message_id + '"]')
|
||||
?.querySelector('div[class^="visualMediaItemContainer"]')
|
||||
?.querySelector('div[class^="imageWrapper"]');
|
||||
|
||||
let image_click = null;
|
||||
let image_show = null;
|
||||
if (image_content) {
|
||||
// 最后的图片信息
|
||||
image_click = image_content.querySelector('a').href;
|
||||
image_show = image_content.querySelector('a').getAttribute("data-safe-src");
|
||||
}
|
||||
res["image_click"] = image_click;
|
||||
res["image_show"] = image_show;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function MutationObserverUpdateElement(elementId) {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('li[id^="' + elementId + '"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = {
|
||||
characterData: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
// 判断当前节点是不是有更新
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'characterData') {
|
||||
console.log(mutation)
|
||||
console.log("LAITool 修改消息: " + 'The ' + mutation.attributeName + ' attribute was modified.', mutation.target);
|
||||
|
||||
|
||||
// 往上找数据,开始获取数据
|
||||
let mainData = findParentNode(mutation.target);
|
||||
|
||||
let res = getMessageData(mainData);
|
||||
|
||||
console.log("LAITool 修改消息", res);
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
if (!window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId] = new MutationObserver(callback)
|
||||
}
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window['updateObserver'][elementId].observe(messageList, config);
|
||||
}
|
||||
|
||||
// 开始监听
|
||||
function startObserving() {
|
||||
// 判断当前是不是有观察器
|
||||
if (window.observer) {
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
// 没有开始监听
|
||||
MutationObserverElement();
|
||||
console.log("开始监听")
|
||||
return "开始监听";
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopObserver() {
|
||||
window.observer.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopUpdateObserver(elementId) {
|
||||
if (window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId].disconnect();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,780 @@
|
||||
let fspromises = require("fs").promises;
|
||||
import path from "path";
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { once } from "events";
|
||||
import { DiscordRequestListener } from "./discordRequestLister";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
|
||||
/**
|
||||
* 对DisCord窗口进行操作的方法
|
||||
*/
|
||||
export class DiscordSimple {
|
||||
constructor(win) {
|
||||
this.win = win;
|
||||
this.tools = new Tools();
|
||||
this.script = define.discordScript;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建discord窗口
|
||||
* @param {*} createWindow 创建的函数(其实没有必要穿)
|
||||
* @returns 返回创建的窗体的示例
|
||||
*/
|
||||
async CreateDiscordWindow(createWindow) {
|
||||
try {
|
||||
// 判断是不是打开了discord窗口
|
||||
let index = global.newWindow.findIndex((item) => item.hash == "discord");
|
||||
if (index != -1) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "已经打开了discord窗口"
|
||||
}
|
||||
}
|
||||
// 要增加判断(可以加载到指定的界面)
|
||||
// 判断是不是有MJ的对应的配置,没有的话加载到默认的界面,有的话加载到指定的界面
|
||||
let mj_config = await this.tools.getJsonFilePropertyValue(define.img_base, "mj_config", null, false);
|
||||
let url = 'https://discord.com/app';
|
||||
if (mj_config && mj_config.serverID && mj_config.channelID) {
|
||||
url = `https://discord.com/channels/${mj_config.serverID}/${mj_config.channelID}`;
|
||||
}
|
||||
let w = await createWindow("discord", null, url);
|
||||
return w;
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听界面刷新完毕事件
|
||||
*/
|
||||
async WaitWindowFinishLoad(discordWindow) {
|
||||
try {
|
||||
async function waitForEvent(emitter, eventName, timeout) {
|
||||
const eventPromise = once(emitter, eventName);
|
||||
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
reject(new Error('Timeout Open Discord Dialog'));
|
||||
}, timeout);
|
||||
});
|
||||
|
||||
await Promise.race([eventPromise, timeoutPromise]);
|
||||
}
|
||||
|
||||
// 使用方法(监听窗口启动,如果10秒没有启动,使用代码关闭,移除队列)
|
||||
await waitForEvent(discordWindow.webContents, 'did-finish-load', 10000);
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
} catch (error) {
|
||||
// 关闭窗口
|
||||
discordWindow.win.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化DisCord的配置文件
|
||||
*/
|
||||
async InitData() {
|
||||
if (this.mj_config) return;
|
||||
// 初始化配置文件,若是没有配置文件,则创建一个配置文件
|
||||
let mj_config_default = {
|
||||
serviceID: null,
|
||||
channelID: null,
|
||||
authorization: null,
|
||||
userAgent: null
|
||||
}
|
||||
// 判断是不是有对应的mj的配置,没有的话就返回默认值
|
||||
let mj_config = await this.tools.getJsonFilePropertyValue(define.img_base, "mj_config", mj_config_default, false);
|
||||
this.mj_config = mj_config;
|
||||
}
|
||||
|
||||
// 在每次输入之前获取指定的位置
|
||||
// 获取当前界面中Input的位置
|
||||
/**
|
||||
* 获取当前界面中Input的位置
|
||||
*/
|
||||
async GetInputPosition() {
|
||||
try {
|
||||
await this.InitData();
|
||||
await this.tools.delay(10000)
|
||||
let result = await this.ExecuteScript(this.script, 'GetMessageInputPosition()');
|
||||
this.x = result.mouseX;
|
||||
this.y = result.mouseY;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装一个执行脚本的方法
|
||||
* @param {*} scriptpath 要执行的脚本的路径
|
||||
* @param {*} funcname 执行脚本的方法名(要暴露出来)
|
||||
*/
|
||||
async ExecuteScript(scriptpath, funcname = "") {
|
||||
try {
|
||||
await this.InitData();
|
||||
// let script = await fspromises.readFile(scriptpath, 'utf-8');
|
||||
let script = `
|
||||
// @noBytecode
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框,并聚焦
|
||||
* @returns 返回获取的消息输入框
|
||||
*/
|
||||
function getInputFoucs() {
|
||||
let selector = 'div[contenteditable="true"][class^="markup_"]';
|
||||
const inputElement = document.querySelector(selector) // as HTMLInputElement;
|
||||
if (!inputElement) {
|
||||
throw new Error('没有找到对应的消息输入框');
|
||||
}
|
||||
inputElement.focus()
|
||||
return inputElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 image_id 的图片
|
||||
*/
|
||||
function GetGeneratedMJImageAndSplit(str) {
|
||||
let res = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const element = str[i];
|
||||
let image_id = element.image_id;
|
||||
let links = document.getElementsByTagName('a');
|
||||
// 创建一个空数组来存储包含特定字符串的链接
|
||||
let result = [];
|
||||
// 遍历所有的链接
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
// 如果链接的href属性包含特定的字符串,就把它添加到结果数组中
|
||||
if (links[i].href.includes(image_id)) {
|
||||
result.push(links[i].href);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
res.push({
|
||||
id: element.id,
|
||||
image_id: element.image_id,
|
||||
result: result[0],
|
||||
name: element.name
|
||||
});
|
||||
}
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}
|
||||
|
||||
window['updateObserver'] = {};
|
||||
|
||||
/**
|
||||
* 获取Discord的消息输入框的X,Y位置,每次输入前都要检测一下
|
||||
*/
|
||||
function GetMessageInputPosition() {
|
||||
try {
|
||||
let inputElement = getInputFoucs();
|
||||
const rect = inputElement.getBoundingClientRect();
|
||||
// 计算鼠标点击位置(假设点击在元素中心)
|
||||
const mouseX = rect.left + rect.width / 2;
|
||||
const mouseY = rect.top + rect.height / 2;
|
||||
sightingTelescope(mouseX, mouseY)
|
||||
return { mouseX, mouseY };
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 瞄准镜
|
||||
*/
|
||||
function sightingTelescope(x, y) {
|
||||
var div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.left = (x - 1) + "px";
|
||||
div.style.top = (y - 15) + "px";
|
||||
div.style.width = '30px';
|
||||
div.style.height = '30px';
|
||||
div.style.zIndex = '9999';
|
||||
div.style.border = '1px dashed green';
|
||||
div.style.color = 'green';
|
||||
div.style.textAlign = 'center';
|
||||
div.style.fontWeight = '600';
|
||||
div.style.borderRadius = '50%';
|
||||
div.style.fontSize = '30px';
|
||||
div.textContent = '+';
|
||||
document.body.appendChild(div);
|
||||
setTimeout(function () {
|
||||
div.parentNode.removeChild(div);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断discord中的输入框中是不是有数据直接返回
|
||||
* 有数据返回true,无数据返回false
|
||||
*/
|
||||
function GetMessageInputValue() {
|
||||
let inputElement = getInputFoucs();
|
||||
// 判断是不是为空
|
||||
let emptyElement = inputElement.querySelector('span[class^="emptyText_"]');
|
||||
if (emptyElement) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听指定的元素
|
||||
*/
|
||||
function MutationObserverElement() {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('ol[class^="scrollerInner_"][data-list-id^="chat-message"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = { childList: true, subtree: true };
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
if (mutation.addedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
||||
const element = mutation.addedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 新增消息", element)
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 新增消息", element);
|
||||
data.code = 1;
|
||||
window.discord.CreateMessage(JSON.stringify(data))
|
||||
MutationObserverUpdateElement(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mutation.removedNodes.length) {
|
||||
// 判断当前此修改是不是包含发送的消息
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
const element = mutation.removedNodes[i];
|
||||
let id = element.id;
|
||||
if (id && id.includes('chat-messages-')) {
|
||||
let data = {};
|
||||
let id_arr = id.split('-');
|
||||
data["message_id"] = id_arr[id_arr.length - 1];
|
||||
data["channel_id"] = id_arr[id_arr.length - 2];
|
||||
data["tiamspan"] = new Date().getTime();
|
||||
|
||||
// 获取消息的ID
|
||||
let data_list = element.querySelector('div[data-list-item-id^="chat-messages"]');
|
||||
if (!data_list) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有数据
|
||||
|
||||
let content_data = data_list.querySelector('div[class^="contents_"]')?.querySelector('div[id^="message-content"]');
|
||||
|
||||
if (!content_data) {
|
||||
return;
|
||||
}
|
||||
// 判断下面是不是有元素
|
||||
let has_data = content_data.hasChildNodes();
|
||||
|
||||
// 找到之后,再重新找节点,创建数据
|
||||
let mainCurrentNode = {
|
||||
"currentNode": data_list,
|
||||
"firstNode": content_data
|
||||
}
|
||||
|
||||
let res = getMessageData(mainCurrentNode);
|
||||
for (let d in res) {
|
||||
data[d] = res[d];
|
||||
}
|
||||
|
||||
// 对应的元素存在,就是一个新消息 返回
|
||||
if (!has_data) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
|
||||
// MutationObserverUpdateElement(id)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
|
||||
stopUpdateObserver(id)
|
||||
} else {
|
||||
// 判断下面是不是有
|
||||
let wrapper = content_data.querySelector('div[class^="wrapper"]');
|
||||
// 判断下面是不是strong元素
|
||||
let strong = content_data.querySelector('strong');
|
||||
|
||||
if (!wrapper && strong) {
|
||||
console.log("LAItool 删除消息", element)
|
||||
data.code = 1;
|
||||
window.discord.DeleteMessage(JSON.stringify(data))
|
||||
stopUpdateObserver(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 创建一个观察器实例并传入回调函数
|
||||
window.observer = new MutationObserver(callback);
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window.observer.observe(messageList, config);
|
||||
}
|
||||
|
||||
|
||||
// 往上找到指定的标签
|
||||
function findParentNode(startNode) {
|
||||
let data = {};
|
||||
let currentNode = startNode;
|
||||
|
||||
while (currentNode !== null) {
|
||||
|
||||
if (!data["currentNode"] && currentNode.id && currentNode.id.startsWith('message-content')) {
|
||||
data["firstNode"] = currentNode;
|
||||
}
|
||||
|
||||
if (currentNode.getAttribute && currentNode.getAttribute('data-list-item-id') && currentNode.getAttribute('data-list-item-id').startsWith('chat-messages')) {
|
||||
data["currentNode"] = currentNode;
|
||||
return data;
|
||||
}
|
||||
currentNode = currentNode.parentNode;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function gatherNodeData(node) {
|
||||
let result = node.textContent || ''; // 获取当前节点的文本内容
|
||||
return result;
|
||||
// 遍历所有子节点
|
||||
}
|
||||
|
||||
// 获取一个数据的当前消息的数据
|
||||
function getMessageData(mainData) {
|
||||
if (!mainData["currentNode"]) {
|
||||
window.discord.UpdateMessage(JSON.stringify({ code: 0, message: "没有找到对应的消息" }))
|
||||
return;
|
||||
}
|
||||
// 获取当前的消息ID
|
||||
|
||||
let res = {};
|
||||
|
||||
let data_arr = mainData["currentNode"].getAttribute('data-list-item-id')?.split('-');
|
||||
let message_id = data_arr[data_arr.length - 1];
|
||||
let channel_id = data_arr[data_arr.length - 2];
|
||||
res["message_id"] = message_id;
|
||||
res["channel_id"] = channel_id;
|
||||
|
||||
// 判断是不是又违规的消息
|
||||
let text = mainData["currentNode"].textContent.toLowerCase().includes("Banned prompt detected");
|
||||
|
||||
|
||||
if (text) {
|
||||
res["error"] = "Banned prompt detected";
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res))
|
||||
}
|
||||
|
||||
// 获取提示词相关的数据
|
||||
// 开始拼接提示词数据
|
||||
let message_prompt = null;
|
||||
if (!mainData["firstNode"]) {
|
||||
message_prompt = null;
|
||||
} else {
|
||||
message_prompt = gatherNodeData(mainData["firstNode"]);
|
||||
}
|
||||
res["message_prompt"] = message_prompt;
|
||||
|
||||
// 判断是不是有图片信息
|
||||
let image_content = mainData["currentNode"].querySelector('div[id^="message-accessories-' + message_id + '"]')
|
||||
?.querySelector('div[class^="visualMediaItemContainer"]')
|
||||
?.querySelector('div[class^="imageWrapper"]');
|
||||
|
||||
let image_click = null;
|
||||
let image_show = null;
|
||||
if (image_content) {
|
||||
// 最后的图片信息
|
||||
image_click = image_content.querySelector('a').href;
|
||||
image_show = image_content.querySelector('a').getAttribute("data-safe-src");
|
||||
}
|
||||
res["image_click"] = image_click;
|
||||
res["image_show"] = image_show;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function MutationObserverUpdateElement(elementId) {
|
||||
// 观察的节点
|
||||
let messageList = document.querySelector('li[id^="' + elementId + '"]');
|
||||
|
||||
// 观察器的配置(需要观察什么变动)
|
||||
const config = {
|
||||
characterData: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
// 当观察到变动时执行的回调函数
|
||||
const callback = function (mutationsList, observer) {
|
||||
// 判断当前节点是不是有更新
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'characterData') {
|
||||
console.log(mutation)
|
||||
console.log("LAITool 修改消息: " + 'The ' + mutation.attributeName + ' attribute was modified.', mutation.target);
|
||||
|
||||
|
||||
// 往上找数据,开始获取数据
|
||||
let mainData = findParentNode(mutation.target);
|
||||
|
||||
let res = getMessageData(mainData);
|
||||
|
||||
console.log("LAITool 修改消息", res);
|
||||
res.code = 1;
|
||||
window.discord.UpdateMessage(JSON.stringify(res));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
if (!window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId] = new MutationObserver(callback)
|
||||
}
|
||||
|
||||
// 以上述配置开始观察目标节点
|
||||
window['updateObserver'][elementId].observe(messageList, config);
|
||||
}
|
||||
|
||||
// 开始监听
|
||||
function startObserving() {
|
||||
// 判断当前是不是有观察器
|
||||
if (window.observer) {
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
// 没有开始监听
|
||||
MutationObserverElement();
|
||||
console.log("开始监听")
|
||||
return "开始监听";
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopObserver() {
|
||||
window.observer.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听
|
||||
*/
|
||||
function stopUpdateObserver(elementId) {
|
||||
if (window['updateObserver'][elementId]) {
|
||||
window['updateObserver'][elementId].disconnect();
|
||||
}
|
||||
}
|
||||
`;
|
||||
this.win.webContents.focus();
|
||||
let result = await this.win.webContents.executeJavaScript(`${script}\n${funcname}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每句提示词输入之前,删除现在的数据
|
||||
*/
|
||||
async onDelete() {
|
||||
await this.InitData();
|
||||
// 执行脚本判断是不是又数据,有的话删除数据
|
||||
let res = await this.ExecuteScript(this.script, "GetMessageInputValue()")
|
||||
// 有数据,删除
|
||||
if (res) {
|
||||
// 发送全选事件
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A', modifiers: ['Control'] });
|
||||
this.win.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'A', modifiers: ['Control'] });
|
||||
await this.tools.delay(300) // 等待全选完成
|
||||
// 发送删除事件
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Delete' });
|
||||
this.win.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'Delete' });
|
||||
// 判断是不是被删除
|
||||
do {
|
||||
await this.tools.delay(300);
|
||||
res = await this.ExecuteScript(this.script, "GetMessageInputValue()")
|
||||
}
|
||||
while (res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将提示词写入到指定的input中(一句)
|
||||
* @param {*} prompt
|
||||
*/
|
||||
async WritePromptToInput(prompt) {
|
||||
try {
|
||||
await this.InitData();
|
||||
|
||||
await this.GetInputPosition();
|
||||
|
||||
// 判断当前的输入框中是不是又数据,又的话删除数据
|
||||
await this.onDelete();
|
||||
|
||||
// 当前选择的机器人命令
|
||||
let command = "/imagine";
|
||||
// 循环写入数据
|
||||
for (let i = 0; i < command.length; i++) {
|
||||
const key = command[i];
|
||||
let simpleWait = Math.floor(Math.random() * (150));
|
||||
await this.tools.delay(simpleWait)
|
||||
|
||||
this.win.webContents.sendInputEvent({ type: 'char', keyCode: key });
|
||||
}
|
||||
let simpleWait = Math.floor(Math.random() * (5000)) + 1000;
|
||||
await this.tools.delay(simpleWait)
|
||||
// 写完之后,执行enter
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Enter' });;
|
||||
|
||||
|
||||
// 循环写入数据
|
||||
for (let i = 0; i < prompt.length; i++) {
|
||||
const key = prompt[i];
|
||||
if (key == "") {
|
||||
let spaceTime = Math.floor(Math.random() * (500 - 100)) + 100;
|
||||
await this.tools.delay(spaceTime)
|
||||
} else {
|
||||
let simpleWait = Math.floor(Math.random() * (150));
|
||||
await this.tools.delay(simpleWait)
|
||||
}
|
||||
|
||||
this.win.webContents.sendInputEvent({ type: 'char', keyCode: key });
|
||||
|
||||
}
|
||||
// 执行命令
|
||||
let res = await this.ExecuteScript(this.script, 'startObserving()');
|
||||
console.log(res);
|
||||
|
||||
simpleWait = Math.floor(Math.random() * (1000));
|
||||
await this.tools.delay(simpleWait)
|
||||
|
||||
//
|
||||
// 写完之后,执行enter
|
||||
this.win.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Enter' });
|
||||
this.win.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'Enter' });
|
||||
|
||||
// 开始监听网络请求
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sendChangeMessage(data) {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.DISCORD.MAIN_DISCORD_MESSAGE_CHANGE, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* discord创建消息,并更新数据
|
||||
* @param {*} Value
|
||||
*/
|
||||
async DiscordCreateMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
|
||||
let currentCreateItem = global.mjGenerateQuene.getCurrentCreateItem();
|
||||
console.log("LAITOOL 创建数据: ", value);
|
||||
value.type = "created"
|
||||
|
||||
// 判断是不是是不是错误数据
|
||||
if (value.error) {
|
||||
// 先移除任务
|
||||
global.mjGenerateQuene.removeTaskProgress((taskProgress) => {
|
||||
return taskProgress.filter(item => item?.message_id != value.message_id);
|
||||
})
|
||||
|
||||
// 在将当前任务设置为空
|
||||
global.mjGenerateQuene.setCurrentCreateItem(null);
|
||||
// 开始下一个任务
|
||||
global.mjGenerateQuene.startNextTask();
|
||||
}
|
||||
|
||||
|
||||
// 判断当前是不是有提示词
|
||||
if ((value.image_click == null || value.image_click == "") && (value.image_show == null || value.image_show == "") && value.message_prompt == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// 这是一个生成完成了(通过图片ID判断)
|
||||
if ((value.image_click != null && value.image_click != '') && value.message_prompt != "") {
|
||||
// 获取图片的ID
|
||||
let image_regex = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
|
||||
let image_match = value.image_click.match(image_regex);
|
||||
|
||||
if (image_match) {
|
||||
value["image_id"] = image_match[1];
|
||||
}
|
||||
|
||||
let image_path = path.join(global.config.project_path, `data\\MJOriginalImage\\${value.image_id}.png`);
|
||||
|
||||
// 这边开始下载对应的图片
|
||||
await this.tools.downloadFileUrl(value.image_click, image_path);
|
||||
|
||||
value["image_path"] = image_path;
|
||||
// 注意这边要移除掉id
|
||||
value.id = null;
|
||||
value.type = "finished"
|
||||
} else {
|
||||
if (currentCreateItem) {
|
||||
currentCreateItem["message_id"] = value.message_id;
|
||||
// 修改数据
|
||||
global.mjGenerateQuene.setCurrentCreateItem(currentCreateItem);
|
||||
// let currentCreateItem = { id: "6075f067-d824-45a4-9213-86e8f254cfd9" };
|
||||
value.id = currentCreateItem.id
|
||||
}
|
||||
}
|
||||
|
||||
// 获取获取
|
||||
this.sendChangeMessage(value)
|
||||
|
||||
// 创建或者是完成之后,开始下一个任务。
|
||||
|
||||
// 判断是不是创建(创建的话直接判断)
|
||||
// 获取正在执行的任务,自己保存的,不是设置的。现在正在后台执行的mj任务
|
||||
// 如果是创建。创建成功,直接删除当前数据
|
||||
if (value.type == "created") {
|
||||
global.mjGenerateQuene.setCurrentCreateItem(null);
|
||||
}
|
||||
|
||||
global.mjGenerateQuene.startNextTask();
|
||||
|
||||
} catch (error) {
|
||||
this.sendChangeMessage({
|
||||
code: 0,
|
||||
message: "MJ创建消息保存失败: " + error.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听discord的更新消息。并修改数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async DiscordUpdateMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
// 接收到discord的消息
|
||||
console.log("LAITOOL 更新数据: ", value);
|
||||
value.type = "updated";
|
||||
|
||||
// 更新的时候,修改数据(判断是不是有进度)
|
||||
let regex = /\((\d+)%\)/;
|
||||
let match = value["message_prompt"].match(regex);
|
||||
if (match) {
|
||||
value.progress = parseInt(match[1]);
|
||||
}
|
||||
// 判断是不是又图片,有的话。固定图片ID,并且只固定一次(前端固定吧)
|
||||
if (value.image_click) {
|
||||
let image_regex = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
|
||||
let image_match = value.image_click.match(image_regex);
|
||||
|
||||
if (image_match) {
|
||||
value["image_id"] = image_match[1];
|
||||
}
|
||||
}
|
||||
this.sendChangeMessage(value)
|
||||
|
||||
} catch (error) {
|
||||
this.sendChangeMessage({
|
||||
code: 0,
|
||||
message: "MJ更新消息保存失败: " + error.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听discord的删除消息。并修改数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async DiscordDeleteMessage(value) {
|
||||
try {
|
||||
value = JSON.parse(value)
|
||||
if (value['image_click'] == null && value['image_show'] == null && value['message_prompt'] == "") {
|
||||
return;
|
||||
}
|
||||
console.log("LAITOOL 删除数据: ", value)
|
||||
value.type = "delete"
|
||||
this.sendChangeMessage(value)
|
||||
|
||||
// 这边可能要做判断(判断是不是开启下一个)
|
||||
|
||||
// 删除当前消息ID对应的历史任务(传入一个构造函数,函数的参数是当前所有的历史任务)
|
||||
global.mjGenerateQuene.removeTaskProgress((taskProgress) => {
|
||||
return taskProgress.filter(item => item?.message_id != value.message_id);
|
||||
})
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.sendChangeMessage({
|
||||
code: 0,
|
||||
message: 'MJ删除数据保存失败 :' + error.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { DiscordSimple } from "./discordSimple";
|
||||
import { Tools } from "../tools";
|
||||
import { define } from "../../define/define";
|
||||
|
||||
/**
|
||||
* discord中的一些公用的方法
|
||||
*/
|
||||
export class DiscordWorker {
|
||||
constructor() {
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化DisCord的配置文件
|
||||
*/
|
||||
async InitData() {
|
||||
if (this.mj_config) return;
|
||||
// 初始化配置文件,若是没有配置文件,则创建一个配置文件
|
||||
let mj_config_default = {
|
||||
serviceID: null,
|
||||
channelID: null,
|
||||
authorization: null,
|
||||
userAgent: null
|
||||
}
|
||||
// 判断是不是有对应的mj的配置,没有的话就返回默认值
|
||||
let mj_config = await this.tools.getJsonFilePropertyValue(define.img_base, "mj_config", mj_config_default, false);
|
||||
this.mj_config = mj_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过hash判断指定的窗口是不是开启
|
||||
* @param {*} hash 指定界面的hash
|
||||
* @returns true/false
|
||||
*/
|
||||
CheckDiscordWindowIsOpen(hash) {
|
||||
try {
|
||||
let index = global.newWindow.findIndex(item => item.hash === hash);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return global.newWindow[index].win
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 判断当前的Doscord界面是不是开启,没有开启的话开启并加载到设置指定的界面
|
||||
async CheckDiscordWindowIsOpenAndLoad() {
|
||||
try {
|
||||
let discordW = this.CheckDiscordWindowIsOpen('discord');
|
||||
if (!discordW) {
|
||||
// 自动打开窗口(调用方法)
|
||||
let mainSimple = new DiscordSimple(global.newWindow[0].win);
|
||||
discordW = await mainSimple.CreateDiscordWindow(global.createWindow);
|
||||
//等待窗口加载完毕(接收消息-一直阻塞)
|
||||
await mainSimple.WaitWindowFinishLoad(discordW);
|
||||
}
|
||||
// 切换到指定的界面
|
||||
// 获取当前配置serverID和channelID是不是当前界面是一样的
|
||||
|
||||
await this.InitData();
|
||||
|
||||
let url = discordW.webContents.getURL();
|
||||
const regex = /\/channels\/(\d+)\/(\d+)/;
|
||||
const match = url.match(regex);
|
||||
let serviceID = null;
|
||||
let channelID = null;
|
||||
if (match && match[1] && match[2]) {
|
||||
serviceID = match[1];
|
||||
channelID = match[2];
|
||||
}
|
||||
if (!(serviceID && channelID && this.mj_config.serviceID === serviceID && this.mj_config.channelID === channelID)) {
|
||||
// 重新加载url
|
||||
// 重新拼接url
|
||||
await discordW.webContents.loadURL(`https://discord.com/channels/${this.mj_config.serviceID}/${this.mj_config.channelID}`)
|
||||
// 等待界面加载完毕
|
||||
}
|
||||
return discordW;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
let path = require('path');
|
||||
import { Tools } from "../tools";
|
||||
import { DiscordAPI } from "./discordApi";
|
||||
import { MjSetting } from "../../define/setting/mjSetting";
|
||||
import { DynamicSetting } from "../../define/setting/dynamicSetting";
|
||||
import { AwesomeHelp } from "awesome-js"
|
||||
|
||||
export class MjSimple {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
this.mjSetting = new MjSetting(global);
|
||||
this.dynamicSetting = new DynamicSetting(global);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文案的基础信息
|
||||
* @param {*} value 参数信息
|
||||
* @returns
|
||||
*/
|
||||
async SvaeMJWordSrt(value) {
|
||||
try {
|
||||
let mj_config = path.join(this.global.config.project_path, "scripts/mj_config.json");
|
||||
await this.tools.checkJsonFileExistsOrCreate(mj_config);
|
||||
this.tools.writeJsonFilePropertyValue(path.join(this.global.config.project_path, "scripts/mj_config.json"), value[1], value[0], false);
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取MJ配置的字幕信息
|
||||
async GetMJConfigSrtInformation() {
|
||||
try {
|
||||
let mj_config = path.join(this.global.config.project_path, "scripts/mj_config.json");
|
||||
let res = await this.tools.getJsonFilePropertyValue(mj_config, "srt_time_information", [], false);
|
||||
return {
|
||||
code: 1,
|
||||
data: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取频道机器人
|
||||
async GetChannelRobots(value) {
|
||||
// let
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
let discordAPI = new DiscordAPI(value);
|
||||
let res = await discordAPI.getBotList();
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "获取频道机器人失败, 错误信息如下:" + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取MJ生图的方式
|
||||
async GetMJGenerateCategory() {
|
||||
return this.mjSetting.GetMJGenerateCategory();
|
||||
}
|
||||
|
||||
// 保存MJ敏感词
|
||||
async AddMJBadPrompt(value) {
|
||||
try {
|
||||
await this.dynamicSetting.saveDataByTypeAndProperty(value, "mj", "bad_prompt", (data) => {
|
||||
// 判断当前数据在默认的敏感词数据中是不是存在。
|
||||
let default_bad_prompt = this.mjSetting.GetMJBadPrompt().data;
|
||||
let index = default_bad_prompt.findIndex(item => item.value.toLowerCase() == value.value.toLowerCase());
|
||||
if (index >= 0) {
|
||||
throw new Error("当前敏感词已存在默认的敏感词中");
|
||||
}
|
||||
// 在判断当前数据在自定义的数据中是不是存在
|
||||
index = data.findIndex(item => item.value.toLowerCase() == value.value.toLowerCase());
|
||||
if (index >= 0) {
|
||||
throw new Error("当前敏感词已存在自定义的敏感词中");
|
||||
}
|
||||
|
||||
});
|
||||
return {
|
||||
code: 1,
|
||||
message: "添加铭感词成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取MJ所有的敏感词
|
||||
async GetMJBadPrompt() {
|
||||
try {
|
||||
let default_bad_prompt = this.mjSetting.GetMJBadPrompt().data;
|
||||
let data = await this.dynamicSetting.getDataByTypeAndProperty("all", 'mj', 'bad_prompt', default_bad_prompt, []);
|
||||
return {
|
||||
code: 1,
|
||||
data: data
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "获取敏感词失败, 错误信息如下:" + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前出入数据所有的敏感词
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
async MJBadPromptCheck(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
// 获取所有的敏感词
|
||||
let bad_prompts = await this.GetMJBadPrompt();
|
||||
if (bad_prompts.code == 0) {
|
||||
throw new Error(bad_prompts.message);
|
||||
}
|
||||
bad_prompts = bad_prompts.data.data;
|
||||
let bad_prompts_string = [];
|
||||
for (let i = 0; i < bad_prompts.length; i++) {
|
||||
const element = bad_prompts[i];
|
||||
bad_prompts_string.push(element.value);
|
||||
}
|
||||
let bad_prompt_ids = [];
|
||||
|
||||
let badPromptMap = AwesomeHelp.makeSensitiveMap(bad_prompts_string);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const element = data[i];
|
||||
let prompt = element.prompt;
|
||||
if (!prompt || prompt == "") {
|
||||
continue;
|
||||
}
|
||||
// 判断是不是存在敏感词
|
||||
let res = AwesomeHelp.checkSensitiveWord(prompt.toLowerCase(), false, badPromptMap);
|
||||
|
||||
// 输出Map中的所有的key字符串
|
||||
console.log(badPromptMap.keys());
|
||||
// 判断当前数据的位置
|
||||
for (let [key, value] of res.entries()) {
|
||||
let ket_length = key.length;
|
||||
|
||||
for (let j = 0; j < value.length; j++) {
|
||||
const item = value[j];
|
||||
let location = item.location;
|
||||
|
||||
// 判断执行位置之前是不是空格或者是,
|
||||
let start = location - 1;
|
||||
let end = location + ket_length;
|
||||
let start_check = prompt.toLowerCase()[start] == ' ' || prompt.toLowerCase()[start] == ',';
|
||||
let end_check = prompt.toLowerCase()[end] == ' ' || prompt.toLowerCase()[end] == ',';
|
||||
if (start_check && end_check) {
|
||||
// 这个位置是敏感词
|
||||
bad_prompt_ids.push({
|
||||
id: element.id,
|
||||
no: element.no
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(res);
|
||||
|
||||
|
||||
}
|
||||
|
||||
console.log(bad_prompt_ids)
|
||||
return {
|
||||
code: 1,
|
||||
data: bad_prompt_ids
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: "检查敏感词失败, 错误信息如下:" + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1122
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,434 @@
|
||||
import { app, shell, BrowserWindow, ipcMain, dialog, nativeTheme } from 'electron'
|
||||
import path, { join } from 'path'
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/icon.ico?asset'
|
||||
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'
|
||||
import { has, isEmpty } from 'lodash'
|
||||
|
||||
// ipc
|
||||
import { SettingIpc } from './IPCEvent/settingIpc.js'
|
||||
import { ImageGenerateIpc } from './IPCEvent/imageGenerateIpc.js'
|
||||
import { WritingIpc } from './IPCEvent/writingIpc.js'
|
||||
import { VideoGenerateIpc } from './IPCEvent/videoGenerateIpc'
|
||||
import { TranslateIpc } from './IPCEvent/translateIpc.js'
|
||||
import { GptIpc } from './IPCEvent/gptIpc.js'
|
||||
import { MjIpc } from './IPCEvent/mjIpc.js'
|
||||
import { OriginalImageGenerateIpc } from './IPCEvent/originalImageGenerateIpc'
|
||||
import { SdIpc } from './IPCEvent/sdIpc.js'
|
||||
import { DiscordIpc, RemoveDiscordIpc } from './IPCEvent/discordIpc.js'
|
||||
import { MainIpc } from './IPCEvent/mainIpc.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;
|
||||
gl.requestQuene = new AsyncQueue(gl, res.task_number);
|
||||
gl.fileQueue = new AsyncQueue(gl, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
function setIpcHandler(hash) {
|
||||
if (hash == "discord") {
|
||||
DiscordIpc(global);
|
||||
}
|
||||
}
|
||||
|
||||
function removeIpcHandler(hash) {
|
||||
if (hash == "discord") {
|
||||
RemoveDiscordIpc();
|
||||
}
|
||||
}
|
||||
|
||||
async function createWindow(hash = "ShowMessage", data, url = null) {
|
||||
// Create the browser window.
|
||||
global.currentHash = hash;
|
||||
|
||||
let mainWindow = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 670,
|
||||
title: 'LAITool',
|
||||
icon: '../../resources/icon.ico',
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux' ? { icon } : {}),
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: false,
|
||||
nodeIntegration: (hash == 'discord' ? false : true), // 在网页中集成Node
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false,
|
||||
partition: "persist:my-partition",
|
||||
}
|
||||
})
|
||||
|
||||
mainWindow.myID = uuidv4();
|
||||
|
||||
mainWindow.on('ready-to-show', () => {
|
||||
mainWindow.show()
|
||||
})
|
||||
|
||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||
shell.openExternal(details.url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
// 判断是不是加载外部网页
|
||||
if (url) {
|
||||
mainWindow.loadURL(url)
|
||||
} else {
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + "/#/" + hash);
|
||||
mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
if (hash != "") {
|
||||
mainWindow.loadURL(`file://${path.join(__dirname, '../renderer/index.html')}#/${hash}`)
|
||||
} else {
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow.on("closed", () => {
|
||||
// 判断指定的窗口,移除指定的监听
|
||||
removeIpcHandler(hash);
|
||||
global.newWindow = global.newWindow.filter(item => item.id != mainWindow.id)
|
||||
})
|
||||
|
||||
// 创建一个新的窗口,添加对应的监听
|
||||
|
||||
global.newWindow.push({
|
||||
hash,
|
||||
id: mainWindow.id,
|
||||
win: mainWindow,
|
||||
init_folder: data
|
||||
})
|
||||
|
||||
setIpcHandler(hash);
|
||||
await InitData(global);
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
let mainWindow;
|
||||
global.createWindow = createWindow;
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(async () => {
|
||||
// Set app user model id for windows
|
||||
electronApp.setAppUserModelId('com.electron')
|
||||
|
||||
// Default open or close DevTools by F12 in development
|
||||
// and ignore CommandOrControl + R in production.
|
||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
optimizer.watchWindowShortcuts(window)
|
||||
})
|
||||
|
||||
global.newWindow = [];
|
||||
mainWindow = createWindow('ShowMessage', null)
|
||||
|
||||
//判断是不是又配置文件,没有的话,将temp中的基础配置加载
|
||||
// 判断文件夹是不是存在
|
||||
let config_p = path.dirname(define.config_path);
|
||||
let isE = await tools.checkExists(config_p);
|
||||
// 文件夹存在判断json文件数量
|
||||
let ex_json = [];
|
||||
if (isE) {
|
||||
let ex_json_path = await tools.getFilesWithExtensions(config_p, '.json');
|
||||
for (let i = 0; i < ex_json_path.length; i++) {
|
||||
const element = ex_json_path[i];
|
||||
ex_json.push(path.basename(element));
|
||||
}
|
||||
} else {
|
||||
await fspromises.mkdir(config_p, { recursive: true });
|
||||
}
|
||||
|
||||
// 判断文件是不是存在,不存在添加
|
||||
if (ex_json.length != 5) {
|
||||
let temp_path = path.join(path.dirname(define.draft_temp_path), 'config');
|
||||
let tmp_json_path = await tools.getFilesWithExtensions(temp_path, ".json");
|
||||
for (let i = 0; i < tmp_json_path.length; i++) {
|
||||
const element = tmp_json_path[i];
|
||||
if (!ex_json.includes(path.basename(element))) {
|
||||
await fspromises.copyFile(element, path.join(config_p, path.basename(element)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断动态文件是不是存在
|
||||
tools.checkJsonFileExistsOrCreate(path.join(define.dynamic_setting));
|
||||
// 判断标签文件是不是存在
|
||||
tools.checkJsonFileExistsOrCreate(path.join(define.tag_setting));
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) mainWindow = createWindow('ShowMessage', null)
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
SettingIpc();
|
||||
ImageGenerateIpc();
|
||||
WritingIpc();
|
||||
VideoGenerateIpc();
|
||||
TranslateIpc();
|
||||
GptIpc();
|
||||
SdIpc();
|
||||
MjIpc();
|
||||
MainIpc(createWindow);
|
||||
OriginalImageGenerateIpc();
|
||||
|
||||
|
||||
ipcMain.handle('dark-mode:toggle', (event, value) => {
|
||||
if (value) {
|
||||
nativeTheme.themeSource = value;
|
||||
} else {
|
||||
nativeTheme.themeSource = "system";
|
||||
}
|
||||
return nativeTheme.shouldUseDarkColors
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app"s specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_SETTING_Dafault_DATA, async (event) => {
|
||||
return await InitData(global);
|
||||
})
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_DRAFT_FILE_LIST, async (event) => {
|
||||
let res = await func.getDraftFileList();
|
||||
return res;
|
||||
})
|
||||
|
||||
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.SELECT_FOLDER, async (event, value = null) => {
|
||||
let po = ['openDirectory'];
|
||||
if (value && !isEmpty(value.multi)) {
|
||||
po.push('multiSelections');
|
||||
}
|
||||
let { filePaths } = await dialog.showOpenDialog({
|
||||
properties: po,
|
||||
defaultPath: value && !isEmpty(value.defaultPath) ? value.defaultPath : ""
|
||||
})
|
||||
return filePaths;
|
||||
})
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.SELECT_FILE, async (event, value) => {
|
||||
try {
|
||||
let { filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: "fileName", extensions: value }
|
||||
]
|
||||
})
|
||||
return {
|
||||
code: 1,
|
||||
value: filePaths[0]
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: `Error Message ${error}`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_DRAFT_TEXT_STYLE, async (event, value) => {
|
||||
let res = await func.getDraftTextStyle(value);
|
||||
return res;
|
||||
})
|
||||
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_TEXT_STYLE_LIST, async (event) => {
|
||||
let res = await func.getClipSetting("text_style");
|
||||
return res;
|
||||
})
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.GET_FRIENDLY_REMINDER_LIST, async (event) => {
|
||||
let res = await func.getClipSetting("friendly_reminder_setting");
|
||||
return res;
|
||||
})
|
||||
ipcMain.handle(DEFINE_STRING.GET_FRIENDLY_REMINDER_DRAFT, async (event, value) => {
|
||||
let res = await func.GetDraftFriendlyReminder(value);
|
||||
return res;
|
||||
})
|
||||
|
||||
|
||||
|
||||
ipcMain.handle(DEFINE_STRING.ADD_DRAFT, async (event, value) => {
|
||||
let res = await func.addDraft(value);
|
||||
return res;
|
||||
})
|
||||
|
||||
// 获取当前版本
|
||||
ipcMain.handle(DEFINE_STRING.GET_VERSION, async (event) => version);
|
||||
|
||||
// 监听保存SD配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_SD_CONFIG, async (event, value) => await func.SaveSDConfig(value))
|
||||
|
||||
// 监听保存生成视频的简单配置
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_GENERAL_SETTING, async (event, value) => await func.SaveGeneralSetting(value));
|
||||
|
||||
// 获取当前的视频合成配置信息
|
||||
ipcMain.handle(DEFINE_STRING.GET_VIDEO_CONFIG_MESSAGE, async (event) => await func.GetVideoConfigMessage());
|
||||
|
||||
// 监听保存字幕的是指信息
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_ASS_CONFIG, async (event, value) => await func.SaveAssConfig(value));
|
||||
|
||||
// 监听获取当前系统安装的字体
|
||||
ipcMain.handle(DEFINE_STRING.GET_SYSTEM_INSTALL_FONTNAME, async (event) => await func.GetSystemInstallFontName());
|
||||
|
||||
// 监听删除视频配置任务(删除指定ID的值)
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_VIDEO_CONFIG, async (event, value) => await func.DeleteVideoConfig(value));
|
||||
|
||||
// 监听添加生图任务信息
|
||||
ipcMain.handle(DEFINE_STRING.ADD_IMAGE_TASK_LIST, async (event, value) => await func.AddImageTask(value));
|
||||
|
||||
// 监听删除生成图片列表中的信息
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_IMAGE_TASK_LIST, async (event, value) => await func.DeleteImageTaskList(value));
|
||||
|
||||
// 监听获取加密的机械码任务
|
||||
ipcMain.handle(DEFINE_STRING.GET_MACHINE_ID, async (event, value) => await func.GetMachineId());
|
||||
|
||||
// 监听获取不想要的提示词任务
|
||||
ipcMain.handle(DEFINE_STRING.GET_BAD_PROMPT, async (event) => await func.GetBadPrompt());
|
||||
|
||||
// 保存不想要的提示词
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_BAD_PROMPT, async (event, value) => await func.SaveBadPrompt(value));
|
||||
|
||||
// 一键删除不想要的值
|
||||
ipcMain.handle(DEFINE_STRING.DELETE_BAD_PROMPT, async (event) => await func.DeleteBadPrompt());
|
||||
|
||||
// 监听反推任务
|
||||
ipcMain.handle(DEFINE_STRING.PUSH_BACK_PROMPT, async (event) => await func.PushBackPrompt());
|
||||
|
||||
// 打开GPT购买界面
|
||||
ipcMain.on(DEFINE_STRING.OPEN_GPT_BUY_URL, async (event, value) => await func.openGptBuyUrl(value));
|
||||
|
||||
// 监听打开任意网站的任务
|
||||
ipcMain.on(DEFINE_STRING.OPEN_URL, async (event, value) => await func.OpenUrl(value));
|
||||
|
||||
// 监听抽帧任务
|
||||
ipcMain.handle(DEFINE_STRING.GET_FRAME, async (event, value) => await func.getFrame(value));
|
||||
|
||||
// 监听获取ADtailer列表的任务
|
||||
ipcMain.handle(DEFINE_STRING.GET_ADETAILER_LIST, async (event) => await func.GetADetailerList());
|
||||
|
||||
// 监听保存ADtailer数据
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_DETAILER_CONFIG, async (event, value) => await func.SaveADetailerConfig(value));
|
||||
|
||||
// 分镜识别。语音识别
|
||||
ipcMain.handle(DEFINE_STRING.START_STORY_BOARDING, async (event, value) => await func.StartStoryboarding(value));
|
||||
|
||||
// 保存试用结束时间
|
||||
ipcMain.on(DEFINE_STRING.SAVE_TRIAL_END_TIME, (event, value) => {
|
||||
global.endTime = value.endTime;
|
||||
global.permissions = value.permissions;
|
||||
});
|
||||
|
||||
// 获取当前机器的权限
|
||||
ipcMain.handle(DEFINE_STRING.GET_PERMISSION, async (event) => {
|
||||
return {
|
||||
code: 1,
|
||||
endTime: global.endTime,
|
||||
permissions: global.permissions
|
||||
}
|
||||
})
|
||||
|
||||
// 试用文件资源打开指定的文件夹
|
||||
ipcMain.on(DEFINE_STRING.OPEN_FOLDER, (event, value) => shell.openPath(path.join(global.config.project_path, "tmp/" + value)));
|
||||
|
||||
// 监听字幕的保存
|
||||
ipcMain.handle(DEFINE_STRING.SAVE_NEW_WORD, async (event, value) => {
|
||||
return await func.SaveNewWord(value);
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听字幕对齐任务,通过前端传过来的数据
|
||||
*/
|
||||
ipcMain.handle(DEFINE_STRING.ALIGN_DRAFT_IMG_TO_TEXT, async (event, value) => {
|
||||
return await func.alginDraftImgToText(value);
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听单张重绘任务
|
||||
*/
|
||||
ipcMain.handle(DEFINE_STRING.RE_GENERATE_IAMGE_ONE, async (event, value) => {
|
||||
console.log(value[0]);
|
||||
let newWindow = global.newWindow.filter(item => item.id == value[0]);
|
||||
let res = await func.ReGenerateImageOne(newWindow, value)
|
||||
return res;
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听文件夹高清任务
|
||||
*/
|
||||
ipcMain.handle(DEFINE_STRING.IMPROVE_IMAGE_RESOULTION, async (event, value) => {
|
||||
let res = await imageGenerate.ImproveResolution(value);
|
||||
return res;
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听刷新窗口的事件
|
||||
*/
|
||||
ipcMain.handle(DEFINE_STRING.REFRASH_IMAGWE_DATA, async (event, value) => {
|
||||
console.log(value)
|
||||
let newWindow = global.newWindow.filter(item => item.id == value[0]);
|
||||
let img_dir = path.join(global.config.project_path, "tmp/" + value[1]);
|
||||
let res = await imageGenerate.getFolderImageList(newWindow, img_dir, true);
|
||||
return res;
|
||||
})
|
||||
|
||||
// 监听打开全局窗口事件
|
||||
ipcMain.on(DEFINE_STRING.SHOW_GLOABAL_MESSAGE_DIALOG, (event, value) => {
|
||||
global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, value)
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听一个窗口,返回窗口创建成功后需要的基础数据
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.SHOW_NEW_WINDOW, async (event, value) => {
|
||||
let newW = createWindow(value[0], value[1]);
|
||||
})
|
||||
|
||||
// 监听程序关闭
|
||||
ipcMain.on(DEFINE_STRING.QUIT_APP, (event) => {
|
||||
app.quit();
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听图片对齐任务
|
||||
*/
|
||||
ipcMain.on(DEFINE_STRING.ALIGN_DRAFT_IMG, async (event, value) => {
|
||||
func.alignDraftImage(mainWindow, value)
|
||||
})
|
||||
@@ -0,0 +1,299 @@
|
||||
import { DEFINE_STRING } from "../define/define_string";
|
||||
import { Tools } from "./tools";
|
||||
export class AsyncQueue {
|
||||
constructor(global, concurrencyLimit = 1, manualMode = false) {
|
||||
this.global = global;
|
||||
this.tasks = [];
|
||||
this.concurrencyLimit = concurrencyLimit;
|
||||
this.manualMode = manualMode;
|
||||
this.currentConcurrency = 0;
|
||||
// 扩展批次完成状态对象以支持子批次
|
||||
this.batchCompletion = {};
|
||||
this.taskDeadline = global.endTime;
|
||||
this.showEndTime = true;
|
||||
this.tools = new Tools();
|
||||
this.currentCreateItem = null;
|
||||
|
||||
// 只有在手动模式下才会使用。要设置当前正在执行的任务,将正在执行的任务添加到里面,需要在任务完成之后手动移除
|
||||
this.taskProgress = [];
|
||||
}
|
||||
|
||||
async enqueue(task, taskId, batchId, subBatchId = 'default') {
|
||||
if (!this.batchCompletion[batchId]) {
|
||||
this.batchCompletion[batchId] = { remaining: 0, subBatches: {}, callback: null, failedTasks: [] };
|
||||
}
|
||||
if (!this.batchCompletion[batchId].subBatches[subBatchId]) {
|
||||
this.batchCompletion[batchId].subBatches[subBatchId] = { remaining: 0, failedTasks: [] };
|
||||
}
|
||||
|
||||
this.batchCompletion[batchId].remaining++;
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].remaining++;
|
||||
this.tasks.push({ task, taskId, batchId, subBatchId });
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
|
||||
setBatchCompletionCallback(batchId, callback) {
|
||||
if (this.batchCompletion[batchId]) {
|
||||
this.batchCompletion[batchId].callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
setSubBatchCompletionCallback(batchId, subBatchId, callback) {
|
||||
if (this.batchCompletion[batchId]) {
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
setTaskDeadline(deadline) {
|
||||
this.taskDeadline = deadline;
|
||||
}
|
||||
|
||||
async process() {
|
||||
|
||||
// 判断是不是有机器码检测的标识
|
||||
if (!this.global.CheckMachineId) {
|
||||
// throw new Error("Machine ID not detected, please check the machine ID.");
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { code: 0, message: "请联系管理员激活" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.global.endTime && new Date(Date.now()).toISOString() > this.global.endTime) {
|
||||
this.tasks = [];
|
||||
if (this.showEndTime) {
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { code: 0, message: "试用时间已到,请联系客服" });
|
||||
}
|
||||
this.showEndTime = false;
|
||||
return;
|
||||
}
|
||||
|
||||
while (this.tasks.length > 0 && this.currentConcurrency < this.concurrencyLimit) {
|
||||
const { task, taskId, batchId, subBatchId } = this.tasks.shift();
|
||||
this.currentConcurrency++;
|
||||
task().then(() => {
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, null);
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}).catch((error) => {
|
||||
// 判断是不是网络请求,不是网络请求直接报错
|
||||
let retryCount = 0;
|
||||
const maxRetryCount = 5;
|
||||
let retryTask = async () => {
|
||||
if (retryCount < maxRetryCount) {
|
||||
retryCount++;
|
||||
await this.tools.delay(2000);
|
||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION,
|
||||
{ code: 0, message: `生图失败,共5次尝试,目前第${retryCount}次` });
|
||||
task().then(() => {
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, null);
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}).catch((error) => {
|
||||
retryTask();
|
||||
});
|
||||
} else {
|
||||
// console.error(`Task ${taskId} failed after ${maxRetryCount} retries`);
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, { taskId, error });
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE].includes(batchId)) {
|
||||
retryTask();
|
||||
} else {
|
||||
console.error('An error occurred in task ' + taskId + ':', error);
|
||||
// 出现报错。直接停掉当前对应的的子批次
|
||||
this.currentConcurrency--;
|
||||
this.handleTaskCompletion(batchId, subBatchId, { taskId, error });
|
||||
if (!this.manualMode) {
|
||||
this.process();
|
||||
} else {
|
||||
// 将当前的设置删除,将历史人物删除
|
||||
this.setCurrentCreateItem(null);
|
||||
this.taskProgress = [];
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 任务完成执行的函数,用于处理任务完成后的状态更新等
|
||||
handleTaskCompletion(batchId, subBatchId, taskError) {
|
||||
const batch = this.batchCompletion[batchId];
|
||||
const subBatch = this.batchCompletion[batchId].subBatches[subBatchId];
|
||||
|
||||
if (taskError) {
|
||||
batch.failedTasks.push(taskError);
|
||||
subBatch.failedTasks.push(taskError);
|
||||
// 删除整个子批次
|
||||
this.removeTask(batchId, null, subBatchId)
|
||||
}
|
||||
this.batchCompletion[batchId].remaining--;
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].remaining--;
|
||||
|
||||
// 子批次完成
|
||||
if (this.batchCompletion[batchId].subBatches[subBatchId].remaining === 0) {
|
||||
console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`);
|
||||
const callback = this.batchCompletion[batchId].subBatches[subBatchId].callback;
|
||||
if (callback) {
|
||||
callback(this.batchCompletion[batchId].subBatches[subBatchId].failedTasks);
|
||||
}
|
||||
this.batchCompletion[batchId].subBatches[subBatchId] = { remaining: 0, callback: null, failedTasks: [] };
|
||||
}
|
||||
|
||||
if (this.batchCompletion[batchId].remaining === 0) {
|
||||
const callback = this.batchCompletion[batchId].callback;
|
||||
if (callback) {
|
||||
callback(this.batchCompletion[batchId].failedTasks);
|
||||
}
|
||||
this.batchCompletion[batchId] = { remaining: 0, subBatches: {}, callback: null, failedTasks: [] };
|
||||
}
|
||||
}
|
||||
|
||||
// 移除任务
|
||||
removeTask(batchId, taskId, subBatchId = 'default') {
|
||||
try {
|
||||
|
||||
if (!this.batchCompletion[batchId]) {
|
||||
throw new Error(`Batch ${batchId} does not exist.`);
|
||||
}
|
||||
|
||||
// 获取当前批次的所有的任务
|
||||
let initialCount = this.tasks.filter(item => item.batchId === batchId).length;
|
||||
let finalCount = initialCount;
|
||||
|
||||
// 首先,判断是否指定了taskId来决定是移除单个任务还是整个批次
|
||||
if (taskId == null) {
|
||||
// 删除整个批次
|
||||
this.tasks = this.tasks.filter(item => item.batchId !== batchId);
|
||||
finalCount = this.tasks.filter(item => item.batchId === batchId).length;
|
||||
} else if (taskId == 'all') {
|
||||
// 删除所有的指定的子批次
|
||||
this.tasks = this.tasks.filter(item => !(item.subBatchId == subBatchId && item.batchId == batchId));
|
||||
finalCount = this.tasks.filter(item => item.subBatchId == subBatchId && item.batchId == batchId).length;
|
||||
} else {
|
||||
// 删除指定的任务,需要考虑subBatchId
|
||||
this.tasks = this.tasks.filter(item => !(item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId));
|
||||
finalCount = this.tasks.filter(item => item.taskId === taskId && item.batchId === batchId && item.subBatchId === subBatchId).length;
|
||||
}
|
||||
// 在移除后再次计算该批次的任务数量
|
||||
// const finalCount = this.tasks.length;
|
||||
// 使用两次计数的差值更新 remaining
|
||||
const removedTasks = initialCount - finalCount;
|
||||
if (removedTasks > 0) {
|
||||
this.batchCompletion[batchId].remaining -= removedTasks;
|
||||
if (this.batchCompletion[batchId].subBatches[subBatchId]) {
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].remaining -= removedTasks;
|
||||
this.batchCompletion[batchId].subBatches[subBatchId].callback = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查并处理完整批次和小批次的完成状态
|
||||
const batch = this.batchCompletion[batchId];
|
||||
// 判断子批次的数量
|
||||
const subBatch = this.batchCompletion[batchId].subBatches[subBatchId];
|
||||
// 子批次完成
|
||||
if (subBatch && subBatch.remaining == 0 && subBatch.callback) {
|
||||
subBatch.callback(subBatch.failedTasks)
|
||||
delete this.batchCompletion[batchId].subBatches[subBatchId]; // 清理批次完成状态
|
||||
}
|
||||
// 总批次完成
|
||||
if (batch && batch.remaining === 0 && batch.callback) {
|
||||
const callback = batch.callback;
|
||||
callback(batch.failedTasks);
|
||||
delete this.batchCompletion[batchId]; // 清理批次完成状态
|
||||
} else {
|
||||
// 遍历小批次,看是否有需要清理的
|
||||
for (let subBatchId in batch.subBatches) {
|
||||
const subBatch = batch.subBatches[subBatchId];
|
||||
if (subBatch.remaining === 0) {
|
||||
// 这里可以进行小批次完成后的处理,比如调用小批次的特定回调
|
||||
console.log(`Sub-batch ${subBatchId} in batch ${batchId} completed.`);
|
||||
// 可以选择删除空的小批次状态,如果不需要保留它的完成记录
|
||||
delete batch.subBatches[subBatchId];
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 1
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取失败的任务
|
||||
getFailedTasks(batchId, subBatchId = 'default') {
|
||||
if (this.batchCompletion[batchId] && this.batchCompletion[batchId].subBatches[subBatchId]) {
|
||||
return this.batchCompletion[batchId].subBatches[subBatchId].failedTasks;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// 手动开启下一个任务
|
||||
async startNextTask() {
|
||||
// 判断当前是不是有任务正在执行
|
||||
if (this.currentCreateItem) {
|
||||
return;
|
||||
}
|
||||
console.log("调用开始下一个任务", this.taskProgress)
|
||||
if (this.manualMode && this.tasks.length > 0 && this.currentConcurrency < this.concurrencyLimit && this.taskProgress.length < 3) {
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
|
||||
// 修改currentItem
|
||||
setCurrentCreateItem(item) {
|
||||
this.currentCreateItem = item;
|
||||
if (item) {
|
||||
// 判断相同的ID的任务是否存在,存在则不添加
|
||||
let index = this.taskProgress.findIndex(task => task.id === item.id);
|
||||
if (index == -1) {
|
||||
this.taskProgress.push(item);
|
||||
} else {
|
||||
// 直接修改
|
||||
this.taskProgress[index] = item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 获取currentItem
|
||||
getCurrentCreateItem() {
|
||||
return this.currentCreateItem;
|
||||
}
|
||||
|
||||
// 获取当前正在执行的任务数量
|
||||
getCurrentConcurrency() {
|
||||
return this.currentConcurrency;
|
||||
}
|
||||
|
||||
// 获取当前任务的限制任务数量
|
||||
getConcurrencyLimit() {
|
||||
return this.concurrencyLimit
|
||||
}
|
||||
|
||||
// 获取当前正在执行的任务对象()
|
||||
getTaskProgress() {
|
||||
return this.taskProgress;
|
||||
}
|
||||
|
||||
// 删除自动设置的任务
|
||||
removeTaskProgress(callback) {
|
||||
this.taskProgress = callback(this.taskProgress);
|
||||
console.log("删除对应消息的任务", this.taskProgress)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
const fspromises = require("fs").promises;
|
||||
import path from "path";
|
||||
import axios from "axios";
|
||||
const { JSDOM } = require('jsdom');
|
||||
import { define } from "../../define/define";
|
||||
import { Tools } from "../tools";
|
||||
import { ClipSetting } from "../../define/setting/clipSetting";
|
||||
import { ImageSetting } from "../../define/setting/imageSetting";
|
||||
import { DEFINE_STRING } from "../../define/define_string";
|
||||
import { TagDefine } from "../../define/tagDefine";
|
||||
|
||||
let tagDefine = new TagDefine(global);
|
||||
export class Setting {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
this.tools = new Tools();
|
||||
}
|
||||
|
||||
//#region 剪映设置
|
||||
|
||||
/**
|
||||
* 添加背景音乐文件夹设置
|
||||
* @param {*} value
|
||||
*/
|
||||
async AddBackgroundMusicFolder(value) {
|
||||
return await ClipSetting.AddBackgroundMusicFolder(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剪映背景音乐配置列表
|
||||
*/
|
||||
async GetBackGroundMusicConfigList() {
|
||||
try {
|
||||
let res = await this.tools.getJsonFilePropertyValue(define.clip_setting, "background_music_setting", []);
|
||||
return {
|
||||
code: 1,
|
||||
value: res
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
error: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除剪辑配置里面对应ID的数据
|
||||
* @param {要删除的样式ID} value
|
||||
*/
|
||||
async deleteClipSetting(property, value) {
|
||||
return ClipSetting.deleteClipSetting(property, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剪映的关键帧配置
|
||||
* @returns
|
||||
*/
|
||||
async GetKeyFrameConfigData() {
|
||||
try {
|
||||
let key_frame = await this.tools.getJsonFilePropertyValue(define.clip_setting, "key_frame", null, false);
|
||||
return {
|
||||
code: 1,
|
||||
data: key_frame
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剪映关键帧配置
|
||||
* @returns
|
||||
*/
|
||||
async GetKeyFrameOptions() {
|
||||
return await ClipSetting.GetKeyFrameOptions();
|
||||
}
|
||||
|
||||
async SaveKeyFrameSetting(value) {
|
||||
return await ClipSetting.SaveKeyFrameSetting(value);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region 图片设置
|
||||
/**
|
||||
* 获取自动保存图片的存储文件的方式
|
||||
* @returns
|
||||
*/
|
||||
async GetAutoSaveImageClassifyOptions() {
|
||||
return await ImageSetting.GetAutoSaveImageClassifyOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自动保存图片的设置
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async SaveImageAutoSaveSetting(value) {
|
||||
return await ImageSetting.SaveImageAutoSaveSetting(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自动保存图片的设置
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async GetImageAutoSaveSetting(value) {
|
||||
return await ImageSetting.GetImageAutoSaveSetting(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动另存文件夹
|
||||
* @returns
|
||||
*/
|
||||
async SaveImageToOtherFolder(value) {
|
||||
return await ImageSetting.SaveImageToOtherFolder([], value);
|
||||
}
|
||||
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
/**
|
||||
* 移除任务
|
||||
* @param {*} value
|
||||
*/
|
||||
async RemoveTask(value) {
|
||||
if (value[0] == DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER) {
|
||||
this.global.fileQueue.removeTask(value[0], value[1], value[2])
|
||||
} else {
|
||||
this.global.requestQuene.removeTask(value[0], value[1], value[2])
|
||||
if (this.global.mjGenerateQuene) {
|
||||
this.global.mjGenerateQuene.removeTask(value[0], value[1], value[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 加载SD配置文件
|
||||
*/
|
||||
async InitSDConfig() {
|
||||
try {
|
||||
let sd_config = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'));
|
||||
return {
|
||||
code: 1,
|
||||
data: {
|
||||
webui_api_url: sd_config.setting.webui_api_url,
|
||||
type: sd_config.setting.type,
|
||||
sampler_name: sd_config.webui.sampler_name,
|
||||
prompt: sd_config.webui.prompt,
|
||||
negative_prompt: sd_config.webui.negative_prompt,
|
||||
denoising_strength: sd_config.webui.denoising_strength,
|
||||
steps: sd_config.webui.steps,
|
||||
width: sd_config.webui.width,
|
||||
height: sd_config.webui.height,
|
||||
adetailer: sd_config.webui.adetailer,
|
||||
batch_size: sd_config.setting.batch_size,
|
||||
seed: sd_config.setting.seed,
|
||||
style_weight: sd_config.setting.style_weight,
|
||||
cfg_scale: sd_config.webui.cfg_scale,
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取主页显示的信息
|
||||
*/
|
||||
async GetShowMessage() {
|
||||
try {
|
||||
let res = await axios.get('https://share.weiyun.com/1EPtoGg8');
|
||||
const dom = new JSDOM(res.data, {
|
||||
runScripts: "dangerously",
|
||||
resources: "usable",
|
||||
});
|
||||
|
||||
// 创建一个函数来异步获取syncData
|
||||
async function getSyncData() {
|
||||
// 创建一个Promise来等待onload事件
|
||||
await new Promise((resolve, reject) => {
|
||||
dom.window.onload = () => {
|
||||
resolve();
|
||||
};
|
||||
|
||||
// 设置一个超时,以防onload事件永远不触发
|
||||
setTimeout(() => {
|
||||
reject(new Error('Loading timed out'));
|
||||
}, 5000); // 10秒超时
|
||||
});
|
||||
|
||||
// 返回syncData对象
|
||||
return dom.window.syncData;
|
||||
}
|
||||
|
||||
// 使用异步函数并处理结果
|
||||
let re = await getSyncData();
|
||||
return {
|
||||
code: 1,
|
||||
data: re
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
*/
|
||||
async getSettingDafultData() {
|
||||
// 加载通用配置
|
||||
let data = await fspromises.readFile(define.config_path, "utf-8");
|
||||
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;
|
||||
return config_json_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改配置文件中的剪映的草稿位置
|
||||
* @param {剪映的草稿位置} value
|
||||
* @returns
|
||||
*/
|
||||
async ModifySampleSetting(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
// 当前的配置文件的内容就是global.config的内容
|
||||
// 直接修改 global.config的内容即可
|
||||
// 传入的value是一个对象,需要遍历其中的所有属性,并将属性值进行修改
|
||||
for (let key in value) {
|
||||
this.global.config[key] = value[key];
|
||||
}
|
||||
await fspromises.writeFile(define.config_path, JSON.stringify(this.global.config));
|
||||
return {
|
||||
code: 1,
|
||||
message: "保存成功"
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
cdoe: "0",
|
||||
message: "保存失败,错误信息如下:" + '\n' + error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查机器码是不是存在
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
async CheckMachineId(value) {
|
||||
try {
|
||||
// 判断机器码是不是存在
|
||||
let res = await axios.post('http://api.yu-zhile.com/GetMachineStatus', {
|
||||
machineId: value
|
||||
})
|
||||
if (res.status != 200) {
|
||||
throw new Error("请求错误");
|
||||
}
|
||||
if (res.data.code == 0) {
|
||||
throw new Error(res.data.message);
|
||||
}
|
||||
|
||||
this.global.endTime = res.data.endTime;
|
||||
this.global.permissions = res.data.permissions;
|
||||
this.global.CheckMachineId = true;
|
||||
return {
|
||||
code: 1,
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 0,
|
||||
message: error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选择角色场景模式的options
|
||||
* @returns
|
||||
*/
|
||||
async GetRoleSceneModeOptions() {
|
||||
return tagDefine.getTagSelectModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生图的类别(sd,mj,d3)
|
||||
* @returns
|
||||
*/
|
||||
async GetImageGenerateCategory() {
|
||||
return ImageSetting.GetImageGenerateCategory();
|
||||
}
|
||||
|
||||
//#region SD设置
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region MJ设置
|
||||
|
||||
/**
|
||||
* 获取指定的配置文件里面指定的属性的数据
|
||||
* @param {*} value
|
||||
*/
|
||||
async GetDefineConfigJsonByProperty(value) {
|
||||
return ImageSetting.GetDefineConfigJsonByProperty(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存指定的配置文件里面指定的属性的数据
|
||||
*/
|
||||
async SaveDefineConfigJsonByProperty(value) {
|
||||
return ImageSetting.SaveDefineConfigJsonByProperty(value);
|
||||
}
|
||||
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
const fspromises = require("fs").promises;
|
||||
const { net } = require('electron');
|
||||
import path from "path";
|
||||
const util = require('util');
|
||||
const { spawn, exec } = require('child_process');
|
||||
const execAsync = util.promisify(exec);
|
||||
import { define } from "../define/define";
|
||||
import { get, has, set } from "lodash";
|
||||
import axios from "axios";
|
||||
|
||||
export class Tools {
|
||||
constructor() { }
|
||||
|
||||
/**
|
||||
* 判断判断文件夹或者是文件是不是存在
|
||||
* @param {文件或者是文件夹的路径} filePath
|
||||
* @returns
|
||||
*/
|
||||
async checkExists(filePath) {
|
||||
try {
|
||||
await fspromises.access(filePath);
|
||||
return true; // 文件或目录存在
|
||||
} catch (error) {
|
||||
return false; // 文件或目录不存在
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断json文件是不是存在,不存在的话,协议一个空的json文件
|
||||
* @param {*} filePath 文件地址
|
||||
*/
|
||||
async checkJsonFileExistsOrCreate(filePath) {
|
||||
try {
|
||||
if (!(await this.checkExists(filePath))) {
|
||||
// 判断传入的json文件的父文件夹是不是存在,不存在的话,创建
|
||||
let parentFolder = path.dirname(filePath);
|
||||
if (!(await this.checkExists(parentFolder))) {
|
||||
// 创建文件夹
|
||||
await fspromises.mkdir(parentFolder, { recursive: true });
|
||||
}
|
||||
await fspromises.writeFile(filePath, "{}");
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目标图片,然后将原图片的exif信息删除,然后将原图片复制到目标图片地址
|
||||
* @param {*} source 原图片地址
|
||||
* @param {*} target 目标图片地址
|
||||
*/
|
||||
async deletePngAndDeleteExifData(source, target) {
|
||||
try {
|
||||
let exiftool = path.join(define.package_path, 'exittool/exiftool.exe');
|
||||
await this.deleteFileOrDirectory(target);
|
||||
let script = `"${exiftool}" -all= -overwrite_original "${source}" -o "${target}"`;
|
||||
const output = await execAsync(script, { maxBuffer: 1024 * 1024 * 10, encoding: 'utf-8' });
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文件或者是文件夹到指定的位置
|
||||
* @param {*} src 源文件或者是文件夹
|
||||
* @param {*} dest 目标文件或者是文件夹
|
||||
*/
|
||||
async copyFileOrDirectory(src, dest) {
|
||||
try {
|
||||
let stats = await fspromises.stat(src);
|
||||
if (stats.isDirectory()) {
|
||||
await fspromises.mkdir(dest, { recursive: true });
|
||||
let items = await fspromises.readdir(src);
|
||||
let promises = items.map(item => this.copyFileOrDirectory(path.join(src, item), path.join(dest, item)));
|
||||
await Promise.all(promises);
|
||||
} else if (stats.isFile()) {
|
||||
await fspromises.copyFile(src, dest);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延时多少秒
|
||||
* @param {等待时间} time
|
||||
* @returns
|
||||
*/
|
||||
async delay(time) {
|
||||
return new Promise(resolve => setTimeout(resolve, time));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定路径的文件夹是不是存在,如果不存在则创建
|
||||
*/
|
||||
async checkFolderExistsOrCreate(folderPath) {
|
||||
try {
|
||||
if (!(await this.checkExists(folderPath))) {
|
||||
await fspromises.mkdir(folderPath, { recursive: true });
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定路径下面的指定的拓展的文件
|
||||
* @param {指定的文件夹路径} folderPath
|
||||
* @param {后缀名} extensions
|
||||
* @returns
|
||||
*/
|
||||
async getFilesWithExtensions(folderPath, extensions) {
|
||||
try {
|
||||
let entries = await fspromises.readdir(folderPath, { withFileTypes: true });
|
||||
let files = [];
|
||||
// 使用Promise.all来并行处理所有的stat调用
|
||||
const fileStats = await Promise.all(entries.map(async (entry) => {
|
||||
const entryPath = path.join(folderPath, entry.name);
|
||||
if (entry.isFile()) {
|
||||
return {
|
||||
name: entry.name,
|
||||
path: entryPath,
|
||||
isFile: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
isFile: false,
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
// 过滤出文件并且满足扩展名要求的文件
|
||||
files = fileStats.filter(fileStat => fileStat.isFile && extensions.includes(path.extname(fileStat.name).toLowerCase()));
|
||||
|
||||
// 对files数组进行排序,基于文件名
|
||||
files.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
// 返回文件名数组
|
||||
return files.map(fileStat => path.join(folderPath, fileStat.name));
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除字符串中的所有标点符号,包括英文和中文的标点,以及省略号。
|
||||
*
|
||||
* @param {string} sentence 待处理的字符串。
|
||||
* @return {string} 去除标点后的字符串。
|
||||
*/
|
||||
removePunctuationIncludingEllipsis(sentence) {
|
||||
// 扩展正则表达式以包含中文标点符号和省略号
|
||||
// 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符
|
||||
const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g;
|
||||
|
||||
// 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串
|
||||
return sentence.replace(punctuationRegExp, '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将一个数组写到一个txt文件中
|
||||
* @param {数据数组} dataArray
|
||||
* @param {txt文件地址} filePath
|
||||
*/
|
||||
async writeArrayToFile(dataArray, filePath) {
|
||||
try {
|
||||
// 将数组转换为字符串,每个元素后面加上换行符
|
||||
const dataString = dataArray.join('\n');
|
||||
// 使用fs.writeFile异步写入文件
|
||||
await fspromises.writeFile(filePath, dataString);
|
||||
return dataString
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的文件夹和文件地址(删除文件夹会删除文件夹里面的所有的文件)
|
||||
* @param {文件或者文件夹的地址} dir
|
||||
*/
|
||||
async deleteFileOrDirectory(dir) {
|
||||
let isExist = await this.checkExists(dir);
|
||||
if (isExist) {
|
||||
let stats = await fspromises.stat(dir);
|
||||
if (stats.isDirectory()) {
|
||||
let items = await fspromises.readdir(dir);
|
||||
let promises = items.map(item => this.deleteFileOrDirectory(path.join(dir, item)));
|
||||
await Promise.all(promises);
|
||||
await fspromises.rmdir(dir);
|
||||
} else if (stats.isFile()) {
|
||||
await fspromises.unlink(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的json文件,并获取指定的属性的值(传入的属性值为null,返回全部。)
|
||||
* 传入的property不为null,返回全部的json文件,检查是不是要判断传入的属性是不是存在。
|
||||
* 要检查属性是不是存在,传入的checkProperty为true,不检查传入的checkProperty为false
|
||||
* checkProperty为true。若值不存在会抛出错误。为false。不存在直接返回null
|
||||
* @param {*} filePath json文件地址
|
||||
* @param {*} property 属性名称
|
||||
* @param {*} defaultValue 默认值(默认为null)
|
||||
* @param {*} checkProperty 是否检查属性是不是存在(默认为true,若为false,不存在直接返回设置的默认值)
|
||||
* @returns
|
||||
*/
|
||||
async getJsonFilePropertyValue(filePath, property, defaultValue = null, checkProperty = true) {
|
||||
try {
|
||||
let isExist = await this.checkExists(filePath);
|
||||
if (!isExist) {
|
||||
throw new Error("文件不存在,请先添加");
|
||||
}
|
||||
let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8'));
|
||||
let d = null;
|
||||
if (property) {
|
||||
if (checkProperty) {
|
||||
if (!has(config, property)) {
|
||||
throw new Error(`${property} 属性不存在,请检查是不是少了什么步骤`);
|
||||
}
|
||||
}
|
||||
d = get(config, property, defaultValue)
|
||||
|
||||
} else {
|
||||
d = config;
|
||||
}
|
||||
return d;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过地址获取指定地址的json文件,然后写入指定的属性的值(判断是不是要检查属性是不是存在)
|
||||
* @param {*} filePath 要写入的文件地址
|
||||
* @param {*} property 写入的属性名
|
||||
* @param {*} value 写入的属性的值
|
||||
* @param {*} checkProperty 是否检查属性是不是存在(默认为false,不检查直接写入-覆盖或新增。若需要检查。值不存在,会抛出错误)
|
||||
*/
|
||||
async writeJsonFilePropertyValue(filePath, property, value, checkProperty = false) {
|
||||
try {
|
||||
let isExist = await this.checkExists(filePath);
|
||||
if (!isExist) {
|
||||
throw new Error("文件不存在,请先添加");
|
||||
}
|
||||
let config = JSON.parse(await fspromises.readFile(filePath, 'utf-8'));
|
||||
if (checkProperty) {
|
||||
if (!has(config, property)) {
|
||||
throw new Error(`${property} 属性不存在,添加失败`);
|
||||
}
|
||||
}
|
||||
set(config, property, value);
|
||||
await fspromises.writeFile(filePath, JSON.stringify(config));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件夹下面特定条件的文件夹
|
||||
* @param {指定的文件夹目录} parentFolder
|
||||
* @param {查询条件 start end include} condition
|
||||
* @param {查询的值} value
|
||||
* @returns
|
||||
*/
|
||||
async getSubFolderList(parentFolder, condition, value) {
|
||||
try {
|
||||
// console.log(value);
|
||||
let folders = await fspromises.readdir(parentFolder, { withFileTypes: true });
|
||||
folders = folders.filter(item => item.isDirectory())
|
||||
.map(item => item.name)
|
||||
|
||||
if (condition == "start") {
|
||||
folders = folders.filter(item => item.startsWith(value));
|
||||
} else if (condition == "end") {
|
||||
folders = folders.filter(item => item.endsWith(value));
|
||||
} else if (condition == "include") {
|
||||
//包含过滤
|
||||
folders = folders.filter(item => item.includes(value));
|
||||
} else {
|
||||
throw new Error("条件参数错误");
|
||||
}
|
||||
return folders;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用chromium下载文件
|
||||
* @param {*} url
|
||||
* @param {*} filePath
|
||||
* @returns
|
||||
*/
|
||||
async downloadFileUrl(url, filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = net.request({
|
||||
method: 'GET',
|
||||
url: url
|
||||
});
|
||||
|
||||
request.on('response', (response) => {
|
||||
const chunks = [];
|
||||
response.on('data', (chunk) => chunks.push(chunk));
|
||||
response.on('end', async () => {
|
||||
try {
|
||||
await fspromises.writeFile(filePath, Buffer.concat(chunks));
|
||||
console.log('File downloaded successfully');
|
||||
resolve();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
request.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import { DEFINE_STRING } from '../define/define_string.js';
|
||||
// Custom APIs for renderer
|
||||
|
||||
let events = [];
|
||||
const api = {
|
||||
// 创建MJ消息
|
||||
CreateMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.CREATE_MESSAGE, value),
|
||||
|
||||
// MJ消息更新
|
||||
UpdateMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, value),
|
||||
|
||||
// MJ消息删除
|
||||
DeleteMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.DELETE_MESSAGE, value),
|
||||
}
|
||||
// Use `contextBridge` APIs to expose Electron APIs to
|
||||
// renderer only if context isolation is enabled, otherwise
|
||||
// just add to the DOM global.
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('api', api)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
} else {
|
||||
window.api = api
|
||||
}
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import { electronAPI } from '@electron-toolkit/preload'
|
||||
import { DEFINE_STRING } from '../define/define_string.js';
|
||||
// Custom APIs for renderer
|
||||
|
||||
let events = [];
|
||||
const api = {
|
||||
//#region 基础状态
|
||||
// 获取版本信息
|
||||
GetVersion: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_VERSION)),
|
||||
//#endregion
|
||||
// 保存通用设置
|
||||
ModifySampleSetting: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MODIFY_SAMPLE_SETTING, value)),
|
||||
//保存SD配置
|
||||
SaveSDConfig: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.SAVE_SD_CONFIG, value);
|
||||
callback(res);
|
||||
},
|
||||
// 保存生成时间的普通设置
|
||||
SaveGeneralSetting: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.SAVE_GENERAL_SETTING, value);
|
||||
callback(res);
|
||||
},
|
||||
// 获取视频的配置信息设置
|
||||
GetVideoConfigMessage: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_VIDEO_CONFIG_MESSAGE);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 加载SD设置
|
||||
InitSDConfig: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.INIT_SD_CONFIG);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 保存任务列表信息
|
||||
AddImageTask: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.ADD_IMAGE_TASK_LIST, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 修改任务队列信息(修改的数据是一个数组。可以添加多个和单个)
|
||||
ModifyImageTaskList: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MODIFY_IMAGE_TASK_LIST, value)),
|
||||
// 开始自动话任务
|
||||
ActionAutoVideoTask: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.ACTION_AUTO_VIDEO_TASK, value)),
|
||||
|
||||
// 获取生成图片的队列任务
|
||||
GetGenerateTaskList: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_GENERATE_TASK_LIST);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 删除生成图片列表的消息
|
||||
DeleteImageTaskList: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.DELETE_IMAGE_TASK_LIST, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 分镜语音识别消息
|
||||
StartStoryboarding: async (value) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.START_STORY_BOARDING, value);
|
||||
},
|
||||
|
||||
// 获取设置的初始数据
|
||||
getSettingDafultData: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_SETTING_Dafault_DATA)),
|
||||
|
||||
// 获取草稿地址
|
||||
getDraftFileList: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_DRAFT_FILE_LIST)
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 选择文件夹
|
||||
selectFolder: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SELECT_FOLDER, value)),
|
||||
|
||||
// 选择指定拓展名的文件
|
||||
SelectFile: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SELECT_FILE, value)),
|
||||
|
||||
getDraftTextStyle: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_DRAFT_TEXT_STYLE, value);
|
||||
callback(res);
|
||||
},
|
||||
getTextStyleList: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_TEXT_STYLE_LIST);
|
||||
callback(res);
|
||||
},
|
||||
deleteDraftTextStyle: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.DELETE_DRAFT_TEXT_STYLE, value);
|
||||
callback(res);
|
||||
},
|
||||
deleteClipSetting: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.DELETE_CLIP_SETTING, value);
|
||||
callback(res);
|
||||
},
|
||||
deleteClipStettingFriendlyReminder: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.DELETE_FRIENDLY_REMINDER, value);
|
||||
callback(res);
|
||||
},
|
||||
// 添加草稿
|
||||
addDraft: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.ADD_DRAFT, value);
|
||||
callback(res);
|
||||
},
|
||||
// 自动合成视频
|
||||
AutoGenerateVideo: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.AUTO_GENERATION_VIDEO, value);
|
||||
callback(res);
|
||||
},
|
||||
// 获取项目地址项目的所有的输出文件地址
|
||||
getSubFolderList: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_SUBFOLDER_LIST, value);
|
||||
callback(res);
|
||||
},
|
||||
// 提取草稿的温馨提示内容
|
||||
GetFriendlyReminder: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_FRIENDLY_REMINDER_DRAFT, value);
|
||||
callback(res);
|
||||
},
|
||||
// 获取温馨提示列表
|
||||
getFriendlyReminderList: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_FRIENDLY_REMINDER_LIST);
|
||||
callback(res);
|
||||
},
|
||||
// 获取剪映背景音乐配置
|
||||
GetBackgroundMusicConfigList: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_BACKGROUND_MUSIC_CONFIG_LIST)),
|
||||
// 添加背景音乐文件夹
|
||||
AddBackgroundMusicFolder: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.ADD_BACKGROUND_MUSIC_FOLDER, value)
|
||||
callback(res);
|
||||
},
|
||||
// 反写json文件的数据
|
||||
ModifyInpurCropJson: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.MODIFY_INPUT_CROP_JSON, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// AI 洗稿
|
||||
AIModifyOneWord: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.AIMODIFY_ONE_WORD, value)
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 导入字幕。对齐显示
|
||||
ImportSrtAndGetTime: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.IMPORT_SRT_AND_GET_TIME, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 获取本地安装的字体
|
||||
GetSystemInstallFontName: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_SYSTEM_INSTALL_FONTNAME);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 保存字幕设置
|
||||
SaveAssConfig: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.SAVE_ASS_CONFIG, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 删除字幕设置
|
||||
DeleteVideoConfig: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.DELETE_VIDEO_CONFIG, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 保存洗稿后的文案
|
||||
SaveNewWord: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.SAVE_NEW_WORD, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 监听分镜的时间信息
|
||||
SaveCopywritingInformation: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.SAVE_COPYWRITING_INFOMATION, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 获取不想要的提示词
|
||||
GetBadPrompt: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_BAD_PROMPT);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存不想要的提示词
|
||||
*/
|
||||
SaveBadPrompt: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.SAVE_BAD_PROMPT, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
/**
|
||||
* 一键删除不想要的值
|
||||
*/
|
||||
DeleteBadPrompt: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.DELETE_BAD_PROMPT);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 一次反推
|
||||
GenerateImageInSelectTask: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GENERATE_IMAGWE_IN_SELECT_TASK, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 显示新的窗口
|
||||
showNewWindow: (value) => {
|
||||
ipcRenderer.send(DEFINE_STRING.SHOW_NEW_WINDOW, value);
|
||||
},
|
||||
|
||||
// 获取机械码
|
||||
GetMachineId: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_MACHINE_ID);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 获取ADetalier配置列表
|
||||
GetADetailerList: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_ADETAILER_LIST);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 保存 ADetailer 数据
|
||||
SaveADetailerConfig: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.SAVE_DETAILER_CONFIG, value);
|
||||
callback(res);
|
||||
},
|
||||
|
||||
// 抽帧
|
||||
getFrame: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_FRAME, value)
|
||||
callback(res);
|
||||
},
|
||||
|
||||
//反推提示词
|
||||
PushBackPrompt: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.PUSH_BACK_PROMPT)
|
||||
callback(res)
|
||||
},
|
||||
|
||||
// 打开指定的url,在默认浏览器中
|
||||
OpenUrl: (value) => ipcRenderer.send(DEFINE_STRING.OPEN_URL, value),
|
||||
|
||||
// 生成json文件
|
||||
AddWebuiJson: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.ADD_WEBUI_JSON);
|
||||
callback(res);
|
||||
},
|
||||
ReGenerateImage: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.RE_GENERATE_IAMGE_ONE, value);
|
||||
callback(res);
|
||||
},
|
||||
ImproveResolution: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.IMPROVE_IMAGE_RESOULTION, value);
|
||||
callback(res);
|
||||
},
|
||||
alginDraftImg: (value, callback) => {
|
||||
ipcRenderer.send(DEFINE_STRING.ALIGN_DRAFT_IMG, value)
|
||||
ipcRenderer.on(DEFINE_STRING.PYTHON_OUTPUT, (event, value) => {
|
||||
callback(DEFINE_STRING.PYTHON_OUTPUT, value)
|
||||
})
|
||||
|
||||
ipcRenderer.on(DEFINE_STRING.PYTHON_CLOSE, (event, value) => {
|
||||
callback(DEFINE_STRING.PYTHON_CLOSE, value)
|
||||
})
|
||||
},
|
||||
alginDraftImgToText: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.ALIGN_DRAFT_IMG_TO_TEXT, value)
|
||||
callback(res);
|
||||
},
|
||||
getImagePromptList: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_IAMGE_PROMPT_LIST);
|
||||
callback(res);
|
||||
},
|
||||
RefreshImageData: async (value, callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.REFRASH_IMAGWE_DATA, value);
|
||||
callback(res);
|
||||
},
|
||||
GetProjectWord: async (callback) => {
|
||||
let res = await ipcRenderer.invoke(DEFINE_STRING.GET_PROJECT_WORD);
|
||||
callback(res);
|
||||
},
|
||||
// 获取当前的主页使用界面信息
|
||||
GetShowMessage: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_SHOW_MESSAGE)),
|
||||
// 立即返回的翻译任务
|
||||
TranslateReturnNow: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.TRANSLATE_RETURN_NOW, value)),
|
||||
// 添加翻译添加翻译任务到队列中
|
||||
TranslatePrompt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.TRANSLATE_PROMPT, value)),
|
||||
// 添加事件监听
|
||||
setEventListen: (value, callback) => {
|
||||
ipcRenderer.on(value[0], (event, value) => {
|
||||
callback(value);
|
||||
})
|
||||
},
|
||||
// 打开discord窗口
|
||||
OpenDiscordWindow: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MAIN.OPEN_DISCORD_WINDOW)),
|
||||
|
||||
// 获取设置的GPT提示词的咒语
|
||||
GetCustomizeGptPrompt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_CUSTOMIZE_GPT_PROMPT, value)),
|
||||
// 生成GPT自定义提示词的案例输出
|
||||
GenerateGptExampleOut: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GENERATE_GPT_EXAMPLE_OUT, value)),
|
||||
//GetPermission 获取机器的权限
|
||||
GetPermission: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_PERMISSION)),
|
||||
// 保存图片到指定的文件夹
|
||||
SaveImageToOtherFolder: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SAVE_IMAGE_TO_OTHER_FOLDER, value)),
|
||||
// 获取当前的自动保存图片的设置
|
||||
GetImageAutoSaveSetting: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_IMAGE_AUTO_SAVE_SETTING)),
|
||||
// 保存图片自动保存设置
|
||||
SaveImageAutoSaveSetting: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SAVE_IMAGE_AUTO_SAVE_SETTING, value)),
|
||||
// 获取当前自动保存图片的方式
|
||||
GetAutoSaveImageClassifyOptions: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_AUTO_SAVE_IMAGE_CLASSIFY_OPTIONS)),
|
||||
// 修改生图任务状态
|
||||
ModifyGenerateTaskStatus: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MODIFY_GENERATE_TASK_STATUS, value)),
|
||||
// 删除指定的后台任务
|
||||
DeleteBackTask: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.DELETE_BACK_TASK, value)),
|
||||
// 保存生成视频的基础配置(srt地址,配音,背景音乐文件夹等)
|
||||
SaveVideoSrtAndAudioMessage: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SAVE_VIDEO_SRT_AND_AUDIO_MESSAGE, value)),
|
||||
// 保存关键帧配置
|
||||
SaveKeyFrameSetting: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SAVE_KEY_FRAME_SETTING, value)),
|
||||
// 获取关键帧打帧方式列表
|
||||
GetKeyFrameOptions: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_KEYFRAME_OPTIONS)),
|
||||
// 获取关键帧的配置文件
|
||||
GetKeyFrameConfigData: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_KEY_FRAME_CONFIG_DATA)),
|
||||
// 保存文案数组到指定的文件
|
||||
saveWordTxt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SAVE_WORD_TXT, value)),
|
||||
// 判断该当前的是否可以链接成功
|
||||
TestGPTConnection: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.TEST_GPT_CONNECTION, value)),
|
||||
// 删除自定义的GPT服务商设置
|
||||
DeleteDynamicGptOption: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.DELETE_DYNAMIC_GPT_OPTION, value)),
|
||||
// 保存自定义的GPT服务商设置
|
||||
SaveDynamicGptOption: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SAVE_DYNAMIC_GPT_OPTION, value)),
|
||||
// 获取当前默认的推理模式或者是自定义的推理模式
|
||||
getGptAutoInferenceOptions: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_GPT_AUTO_INFERENCE_OPTIONS, value)),
|
||||
// 获取当前的GPT和自定义的GPT模型
|
||||
getGptModelOption: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_GPT_MODEL_OPTION, value)),
|
||||
// 获取默认的GPT和自定义的GPT
|
||||
getGptBusinessOption: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_GPT_BUSINESS_OPTION, value)),
|
||||
// 获取指定的风格菜单
|
||||
GetImageStyleMenu: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_IMAGE_STYLE_MENU)),
|
||||
// 获取指定的风格ID对应的数据
|
||||
GetImageStyleInfomation: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_IMAGE_STYLE_INFOMATION, value)),
|
||||
// 获取指定的图片风格下面的图片数据
|
||||
getStyleImageSubList: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_STYLE_IMAGE_SUB_LIST, value)),
|
||||
// SD 原创生成单个图片
|
||||
OriginalSDImageGenerate: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.ORIGINAL_SD_SINGLE_IMAGE_GENERATE, value)),
|
||||
// 自动保存数据到json文件事件
|
||||
AutoSaveDataJson: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.AUTO_SAVE_DATA_JSON, value)),
|
||||
// GPT推理
|
||||
GPTPrompt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GPT_PROMPT, value)),
|
||||
// 获取 prompt 的json配置文件
|
||||
GetPromptJson: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_PROMPT_JSON, value)),
|
||||
// SD原创添加配置文件
|
||||
OriginalAddWebuiJson: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.ORIGINAL_ADD_WEBUI_JSON, value)),
|
||||
//获取文件的配置文件
|
||||
GetConfigJson: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_CONFIG_JSON, value)),
|
||||
// 自动分析人物事件
|
||||
AutoAnalyzeCharacter: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.AUTO_ANALYZE_CHARACTER, value)),
|
||||
// 获取生成视频的基础设置
|
||||
GetVideoGenerateConfig: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_VIDEO_GENERATE_CONFIG)),
|
||||
// 移除事件
|
||||
removeEventListen: (eventName) => ipcRenderer.removeAllListeners(eventName),
|
||||
// 一键自动化的条件检测
|
||||
AutoConditionCheck: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.AUTO_CONDITION_CHECK, value)),
|
||||
// 下载文件到指定的文件夹
|
||||
DownloadImageFile: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.DOWNLOAD_IMAGE_FILE, value)),
|
||||
|
||||
// 检查机器码是不是存在
|
||||
CheckMachineId: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.CHECK_MACHINE_ID, value)),
|
||||
// 保存试用结束
|
||||
SaveTrialEndTime: (value) => ipcRenderer.send(DEFINE_STRING.SAVE_TRIAL_END_TIME, value),
|
||||
// 打开购买GPT地址
|
||||
openGptBuyUrl: (value) => ipcRenderer.send(DEFINE_STRING.OPEN_GPT_BUY_URL, value),
|
||||
// 打开指定的文件夹
|
||||
OpenFolder: (value) => ipcRenderer.send(DEFINE_STRING.OPEN_FOLDER, value),
|
||||
// 退出软件
|
||||
QuitApp: () => ipcRenderer.send(DEFINE_STRING.QUIT_APP),
|
||||
// 获取当前的生图方式,包含sd,mj,d3等
|
||||
GetImageGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_IMAGE_GENERATE_CATEGORY)),
|
||||
|
||||
// 获取指定的配置文件里面指定的属性的数据
|
||||
GetDefineConfigJsonByProperty: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.GET_DEFINE_CONFIG_JSON_BY_PROPERTY, value)),
|
||||
|
||||
// 保存指定的配置文件里指定的属性的数据
|
||||
SaveDefineConfigJsonByProperty: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.SAVE_DEFINE_CONFIG_JSON_BY_PROPERTY, value)),
|
||||
|
||||
// 打开全局提示框
|
||||
showGlobalMessageDialog: (value) => ipcRenderer.send(DEFINE_STRING.SHOW_GLOABAL_MESSAGE_DIALOG, value),
|
||||
|
||||
// 打开全局通知框
|
||||
showGlobalNotificationDialog: (value) => ipcRenderer.send(DEFINE_STRING.SHOW_MAIN_NOTIFICATION, value),
|
||||
}
|
||||
|
||||
const mj = {
|
||||
// 保存文案信息到mj的配置文件
|
||||
SvaeMJWordSrt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.SAVE_WORD_SRT, value)),
|
||||
// 获取MJ配置文件的字幕信息
|
||||
GetMJConfigSrtInformation: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_CONFIG_SRT_INFORMATION)),
|
||||
// 获取标签集的基础信息
|
||||
GetTagDataByTypeAndProperty: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_TAG_DATA_BY_TYPE_AND_PROPERTY, value)),
|
||||
// 保存数据到标签集中
|
||||
SaveTagPropertyData: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.SAVE_TAG_PROPERTY_DATA, value)),
|
||||
// 删除指定的标签数据
|
||||
DeleteTagPropertyData: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DELETE_TAG_PROPERTY_DATA, value)),
|
||||
// 获取选择标签的模式option列表
|
||||
GetTagSelectModel: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_TAG_SELECT_MODEL)),
|
||||
// 将翻译任务添加到后台队列中
|
||||
TranslateReturnNowTask: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.TRANSLATE_RETURN_NOW_TASK, value)),
|
||||
// MJ原创生图
|
||||
OriginalMJImageGenerate: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.ORIGINAL_MJ_IMAGE_GENERATE, value)),
|
||||
// 获取当前对话频道的机器人列表
|
||||
GetChannelRobots: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_CHANNEL_ROBOTS, value)),
|
||||
|
||||
// 获取MJ生图的方式
|
||||
GetMJGenerateCategory: async (callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_MJ_GENERATE_CATEGORY)),
|
||||
|
||||
// MJ生成的图片分割
|
||||
ImageSplit: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.IMAGE_SPLIT, value)),
|
||||
|
||||
// 添加MJ敏感词
|
||||
AddMjBadPrompt: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.ADD_MJ_BAD_PROMPT, value)),
|
||||
// 添加MJ敏感词检查
|
||||
MJBadPromptCheck: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.MJ_BAD_PROMPT_CHECK, value)),
|
||||
|
||||
// 获取已经生图完成的数据,并获取图片
|
||||
GetGeneratedMJImageAndSplit: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.GET_GENERATED_MJ_IMAGE_AND_SPLIT, value)),
|
||||
|
||||
// 给图片链接,下载指定的图片并分割保存
|
||||
DownloadImageUrlAndSplit : async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.MJ.DOWNLOAD_IMAGE_URL_AND_SPLIT, value)),
|
||||
}
|
||||
|
||||
const discord = {
|
||||
// 创建MJ消息
|
||||
CreateMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.CREATE_MESSAGE, value),
|
||||
|
||||
// MJ消息更新
|
||||
UpdateMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.UPDATE_MESSAGE, value),
|
||||
|
||||
// MJ消息删除
|
||||
DeleteMessage: (value) => ipcRenderer.send(DEFINE_STRING.DISCORD.DELETE_MESSAGE, value),
|
||||
}
|
||||
// Use `contextBridge` APIs to expose Electron APIs to
|
||||
// renderer only if context isolation is enabled, otherwise
|
||||
// just add to the DOM global.
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI)
|
||||
contextBridge.exposeInMainWorld('api', api)
|
||||
contextBridge.exposeInMainWorld('mj', mj)
|
||||
contextBridge.exposeInMainWorld('discord', discord)
|
||||
contextBridge.exposeInMainWorld('darkMode', {
|
||||
toggle: (value) => ipcRenderer.invoke('dark-mode:toggle', value),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
} else {
|
||||
window.electron = electronAPI
|
||||
window.api = api;
|
||||
window.mj = mj;
|
||||
window.discord = discord;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>LAITool 123</title>
|
||||
<!-- <meta http-equiv="Content-Security-Policy"
|
||||
content="default-src '*'; connect-src *; script-src '*'; img-src 'self' file: data: ; style-src 'self' 'unsafe-inline'" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<n-config-provider :hljs="hljs" :theme="themeData == 'dark' ? darkTheme : null">
|
||||
<n-message-provider>
|
||||
<n-dialog-provider>
|
||||
<n-notification-provider>
|
||||
<RouterView></RouterView>
|
||||
</n-notification-provider>
|
||||
</n-dialog-provider>
|
||||
</n-message-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from 'vue'
|
||||
import hljs from 'highlight.js/lib/core'
|
||||
import javascript from 'highlight.js/lib/languages/javascript'
|
||||
import { NMessageProvider, NDialogProvider, NConfigProvider, darkTheme, NNotificationProvider } from "naive-ui"
|
||||
hljs.registerLanguage('javascript', javascript)
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NConfigProvider, NDialogProvider, NMessageProvider, NNotificationProvider
|
||||
},
|
||||
setup() {
|
||||
let themeData = ref("");
|
||||
|
||||
onMounted(() => {
|
||||
window.api.getSettingDafultData(async (value) => {
|
||||
themeData.value = value.theme;
|
||||
await window.darkMode.toggle(value.theme)
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
javascript,
|
||||
hljs,
|
||||
darkTheme,
|
||||
themeData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import './assets/css/styles.less';
|
||||
</style>
|
||||
@@ -0,0 +1,207 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family:
|
||||
Roboto,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Helvetica Neue',
|
||||
'Segoe UI',
|
||||
'Oxygen',
|
||||
'Ubuntu',
|
||||
'Cantarell',
|
||||
'Open Sans',
|
||||
sans-serif;
|
||||
color: #86a5b1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
code {
|
||||
font-weight: 600;
|
||||
padding: 3px 5px;
|
||||
border-radius: 2px;
|
||||
background-color: #26282e;
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
SF Mono,
|
||||
Menlo,
|
||||
Consolas,
|
||||
Liberation Mono,
|
||||
monospace;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #9feaf9;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
#app {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.versions {
|
||||
margin: 0 auto;
|
||||
float: none;
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
font-family: 'Menlo', 'Lucida Console', monospace;
|
||||
color: #c2f5ff;
|
||||
line-height: 1;
|
||||
transition: all 0.3s;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
float: left;
|
||||
border-right: 1px solid rgba(194, 245, 255, 0.4);
|
||||
padding: 0 20px;
|
||||
font-size: 13px;
|
||||
opacity: 0.8;
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hero-logo {
|
||||
margin-top: -0.4rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
@media (max-width: 840px) {
|
||||
.versions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-logo {
|
||||
margin-top: -1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
font-weight: 400;
|
||||
color: #c2f5ff;
|
||||
text-align: center;
|
||||
margin-top: -0.5rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 660px) {
|
||||
.hero-logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-tagline {
|
||||
text-align: center;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.link-item {
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.features {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: -6px;
|
||||
|
||||
.feature-item {
|
||||
width: 33.33%;
|
||||
box-sizing: border-box;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
article {
|
||||
background-color: rgba(194, 245, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #d4e8ef;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
color: #c2f5ff;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 660px) {
|
||||
.features .feature-item {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.links {
|
||||
flex-direction: column;
|
||||
line-height: 32px;
|
||||
|
||||
.link-dot {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.features .feature-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<symbol id="electron" viewBox="0 0 900 300">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g class="hero-apps" style="fill: #71abb7;">
|
||||
<path d="M15 138l-4.9-.64L8 133l-2.1 4.36L1 138l3.6 3.26-.93 4.74L8 143.67l4.33 2.33-.93-4.74z"></path>
|
||||
<path d="M897.2 114.0912l-5.2 3.63v-2.72c0-.55-.45-1-1-1h-8c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h8c.55 0 1-.45 1-1v-2.72l5.2 3.63c.33.23.8 0 .8-.41v-10c0-.41-.47-.64-.8-.41z"></path>
|
||||
<path d="M65.4 188.625h-1.6c.88 0 1.6-.7313 1.6-1.625v-1.625c0-.8937-.72-1.625-1.6-1.625h-1.6c-.88 0-1.6.7313-1.6 1.625V187c0 .8937.72 1.625 1.6 1.625h-1.6c-.88 0-1.6.7313-1.6 1.625v3.25h1.6v4.875c0 .8937.72 1.625 1.6 1.625h1.6c.88 0 1.6-.7313 1.6-1.625V193.5H67v-3.25c0-.8937-.72-1.625-1.6-1.625zm-3.2-3.25h1.6V187h-1.6v-1.625zm3.2 6.5h-1.6v6.5h-1.6v-6.5h-1.6v-1.625h4.8v1.625zm3.344-5.6875c0-3.2175-2.576-5.8337-5.744-5.8337-3.168 0-5.744 2.6162-5.744 5.8337 0 .455.048.8937.144 1.3162v3.2175c-.976-1.2512-1.6-2.8112-1.6-4.55 0-4.03 3.232-7.3125 7.2-7.3125s7.2 3.2825 7.2 7.3125c0 1.7225-.624 3.2988-1.6 4.55v-3.2175c.096-.4387.144-.8612.144-1.3162zm6.256 0c0 4.68-2.608 8.7425-6.4 10.7738v-1.7063c2.976-1.885 4.944-5.2325 4.944-9.0675 0-5.915-4.72-10.7087-10.544-10.7087-5.824 0-10.544 4.7937-10.544 10.7087 0 3.835 1.968 7.1825 4.944 9.0675v1.7063c-3.792-2.0313-6.4-6.0938-6.4-10.7738C51 179.46 56.376 174 63 174s12 5.46 12 12.1875z"></path>
|
||||
<path d="M830.7143 142.3333c-.8643 0-1.5714.7125-1.5714 1.5834v3.1666c0 .871.707 1.5834 1.5713 1.5834h12.5714c.8643 0 1.5714-.7125 1.5714-1.5834v-3.1666c0-.871-.707-1.5834-1.5713-1.5834h-12.5714zm12.5714 2.771l-1.9643 1.979h-2.357L837 145.1043l-1.9643 1.979h-2.357l-1.9644-1.979v-1.1876h1.1786l1.964 1.979 1.9644-1.979h2.3572l1.9643 1.979 1.964-1.979h1.1787v1.1875zm-9.4286 5.1457h6.286v1.5833h-6.286V150.25zM837 136c-6.0657 0-11 4.6075-11 10.2917v7.125c0 .8708.707 1.5833 1.5714 1.5833h18.8572c.8643 0 1.5714-.7125 1.5714-1.5833v-7.125C848 140.6075 843.0657 136 837 136zm9.4286 17.4167h-18.8572v-7.125c0-4.8925 4.1486-8.851 9.4286-8.851 5.28 0 9.4286 3.9585 9.4286 8.851v7.125z"></path>
|
||||
<path d="M75 91.8065V96h4.1935L90.376 84.8174l-4.1934-4.1935L75 91.8064zm4.1935 2.7957h-2.7957v-2.7957h1.398v1.3978h1.3977v1.398zM93.591 81.6024l-1.817 1.817-4.1935-4.1934 1.817-1.817c.5453-.5453 1.426-.5453 1.971 0l2.2226 2.2224c.5453.5452.5453 1.4258 0 1.971z"></path>
|
||||
<path d="M797 187h4v4h-4v-4zm12-1v19c0 1.1-.9 2-2 2h-20c-1.1 0-2-.9-2-2v-24c0-1.1.9-2 2-2h15l7 7zm-2 1l-6-6h-14v22l6-10 4 8 4-4 6 6v-16z"></path>
|
||||
<path d="M138 125c-6.62 0-12 5-12 11 0 9.04 12 21 12 21s12-11.96 12-21c0-6-5.38-11-12-11zm0 29.1c-3.72-4.06-10-12.22-10-18.1 0-4.96 4.5-9 10-9 2.68 0 5.22.96 7.12 2.72 1.84 1.72 2.88 3.94 2.88 6.28 0 5.88-6.28 14.04-10 18.1zm4-18.1c0 2.22-1.78 4-4 4-2.22 0-4-1.78-4-4 0-2.22 1.78-4 4-4 2.22 0 4 1.78 4 4z"></path>
|
||||
<path d="M771 82h8v2h-8v-2zm0 6h8v-2h-8v2zm0 4h8v-2h-8v2zm22-10h-8v2h8v-2zm0 4h-8v2h8v-2zm0 4h-8v2h8v-2zm4-12v18c0 1.1-.9 2-2 2h-11l-2 2-2-2h-11c-1.1 0-2-.9-2-2V78c0-1.1.9-2 2-2h11l2 2 2-2h11c1.1 0 2 .9 2 2zm-16 1l-1-1h-11v18h12V79zm14-1h-11l-1 1v17h12V78z"></path>
|
||||
<path d="M176 203h-24c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v7l7-7h13c1.1 0 2-.9 2-2v-16c0-1.1-.9-2-2-2zm0 18h-14l-4 4v-4h-6v-16h24v16z"></path>
|
||||
<path d="M673 88.921c0 2.18-.9 4.18-2.34 5.66l-1.34-1.34c1.1-1.12 1.78-2.62 1.78-4.32 0-1.7-.68-3.22-1.78-4.32l1.34-1.34c1.44 1.44 2.34 3.44 2.34 5.66zm-8.56-11.48l-7.44 7.44h-4c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h4l7.44 7.44c.94.94 2.56.28 2.56-1.06v-20.76c0-1.34-1.62-2-2.56-1.06zm11.88.16l-1.34 1.34c2.56 2.56 4.12 6.06 4.12 9.96 0 3.88-1.56 7.4-4.12 9.96l1.34 1.34c2.9-2.9 4.68-6.9 4.68-11.32 0-4.44-1.78-8.44-4.68-11.32v.04zm-2.82 2.82l-1.38 1.34c1.84 1.84 2.96 4.38 2.96 7.16 0 2.78-1.12 5.32-2.96 7.12l1.38 1.34c2.16-2.16 3.5-5.16 3.5-8.46 0-3.3-1.34-6.32-3.5-8.5z"></path>
|
||||
<path d="M226 79h-16c0-1.1-.9-2-2-2h-8c-1.1 0-2 .9-2 2-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V81c0-1.1-.9-2-2-2zm-18 4h-8v-2h8v2zm9 14c-3.88 0-7-3.12-7-7s3.12-7 7-7 7 3.12 7 7-3.12 7-7 7zm5-7c0 2.76-2.26 5-5 5s-5-2.26-5-5 2.26-5 5-5 5 2.26 5 5z"></path>
|
||||
<path d="M725.8393 157h-15.6498c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h15.6298C727 153 727 153.82 727 155c0 1.18 0 2-1.1807 2h.02zm-11.6473-10c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h11.6273C727 143 727 143.82 727 145c0 1.18 0 2-1.1807 2H714.192zM695 146.82l2.8218-2.6 3.182 3.18 8.185-8.4 2.8218 2.82-11.0068 11-6.0038-6zM710.1895 163h15.6298C727 163 727 163.82 727 165c0 1.18 0 2-1.1807 2h-15.6298c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2z"></path>
|
||||
<path d="M223 152v24c0 1.65 1.35 3 3 3h36c1.65 0 3-1.35 3-3v-24c0-1.65-1.35-3-3-3h-36c-1.65 0-3 1.35-3 3zm39 0l-18 15-18-15h36zm-36 4.5l12 9-12 9v-18zm3 19.5l10.5-9 4.5 4.5 4.5-4.5 10.5 9h-30zm33-1.5l-12-9 12-9v18z"></path>
|
||||
<path d="M648 182h-3v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-9v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-3c-1.65 0-3 1.35-3 3v33c0 1.65 1.35 3 3 3h33c1.65 0 3-1.35 3-3v-33c0-1.65-1.35-3-3-3zm0 36h-33v-27h33v27zm-24-33h-3v-6h3v6zm18 0h-3v-6h3v6zm-15 12h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3z"></path>
|
||||
</g>
|
||||
<g class="hero-icons" style="fill: #c2f5ff;">
|
||||
<path d="M441.1132 69.724c7.681 0 13.9075-6.207 13.9075-13.8636 0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9076 6.207-13.9076 13.8634 0 7.6566 6.2266 13.8635 13.9076 13.8635zm0-5.7932c-4.4713 0-8.096-3.6132-8.096-8.0704 0-4.457 3.6247-8.0703 8.096-8.0703 4.4712 0 8.096 3.6133 8.096 8.0704 0 4.4572-3.6248 8.0704-8.096 8.0704z"></path>
|
||||
<path d="M354.8995 220.2693c7.681 0 13.9075-6.207 13.9075-13.8635s-6.2266-13.8634-13.9075-13.8634c-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635zm0-5.793c-4.4713 0-8.096-3.6133-8.096-8.0705 0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703c0 4.4572-3.6247 8.0704-8.096 8.0704z"></path>
|
||||
<path d="M541.0343 206.4058c0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635 7.681 0 13.9075-6.207 13.9075-13.8635zm-5.8115 0c0 4.4572-3.6247 8.0704-8.096 8.0704s-8.096-3.6132-8.096-8.0704c0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703z"></path>
|
||||
<path d="M397.6943 214.5258c9.7012 27.0033 25.5723 43.629 43.419 43.629 13.0157 0 25.0578-8.8443 34.4482-24.4154.827-1.371.3822-3.1507-.9932-3.975-1.3755-.824-3.1607-.3808-3.9876.9902-8.439 13.9938-18.8052 21.6072-29.4675 21.6072-14.8247 0-28.9803-14.8288-37.9476-39.7892-.541-1.506-2.2044-2.2897-3.7153-1.7504-1.511.5394-2.297 2.1975-1.756 3.7036z"></path>
|
||||
<path d="M514.124 163.4733c18.5545-21.85 25.033-43.826 16.122-59.2117-6.557-11.321-20.419-17.2982-38.841-17.537-1.6047-.021-2.9225 1.259-2.9434 2.8586-.0208 1.5996 1.263 2.9132 2.8678 2.934 16.5683.2148 28.5106 5.3642 33.8836 14.641 7.4018 12.7797 1.6243 32.3774-15.5247 52.5722-1.037 1.221-.8844 3.0487.3405 4.0822 1.2248 1.0336 3.0584.8817 4.0952-.3393z"></path>
|
||||
<path d="M411.5672 88.457c-28.3373-5.1448-50.7424.24-59.672 15.6575-6.6635 11.505-4.7588 26.7585 4.6193 43.0637.7982 1.3878 2.574 1.8678 3.966 1.072 1.3923-.7956 1.874-2.5656 1.0756-3.9534-8.4477-14.688-10.0915-27.8524-4.628-37.2857 7.418-12.8074 27.403-17.6105 53.5978-12.8546 1.579.2866 3.092-.7568 3.3794-2.3307.2876-1.5738-.7592-3.082-2.338-3.3687z"></path>
|
||||
<path d="M486.3075 209.2436c5.022-15.998 7.7194-34.453 7.7194-53.6842 0-47.9875-16.849-89.3545-40.8478-99.977-1.4667-.649-3.1837.0098-3.835 1.472-.6512 1.462.01 3.1735 1.4766 3.8227 21.404 9.474 37.3945 48.7337 37.3945 94.6824 0 18.6574-2.612 36.5297-7.454 51.954-.4794 1.5268.3736 3.1518 1.9052 3.6295s3.1617-.3727 3.641-1.8994z"></path>
|
||||
<path d="M466.439 89.4215c-16.7763 3.583-34.6332 10.5886-51.7827 20.4585-42.434 24.4216-70.1147 60.4323-66.2703 86.5432.233 1.5828 1.709 2.6776 3.297 2.4453 1.5877-.2323 2.686-1.7037 2.453-3.2865-3.4135-23.1838 22.825-57.3183 63.426-80.685 16.6365-9.5746 33.9267-16.3578 50.0946-19.811 1.5692-.335 2.5687-1.8748 2.2325-3.439-.336-1.5642-1.8807-2.5606-3.45-2.2255z"></path>
|
||||
<path d="M371.2508 166.997c11.458 12.5516 26.3438 24.3243 43.3203 34.0947 41.106 23.6572 84.866 29.9805 106.4328 15.3217 1.326-.9013 1.668-2.7033.7638-4.025-.904-1.3217-2.712-1.6626-4.0378-.7614-19.302 13.1195-60.871 7.1128-100.253-15.5523-16.469-9.4783-30.8834-20.8782-41.9277-32.9767-1.08-1.1832-2.9178-1.2695-4.1048-.1928-1.187 1.0766-1.2735 2.9086-.1934 4.0918z"></path>
|
||||
<path d="M443.2374 165.3634c-5.432 1.17-10.7838-2.2712-11.9598-7.686-1.1714-5.415 2.2785-10.7498 7.7106-11.922 5.432-1.17 10.7838 2.2712 11.9598 7.686 1.1737 5.415-2.2785 10.7498-7.7106 11.922z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px;">
|
||||
<n-card title="自动保存(在高清的时候)">
|
||||
|
||||
<div class="first_row" style="display: flex; margin-top: 10px">
|
||||
<div>
|
||||
<div style="margin-top: 10px;"></div>
|
||||
<div>
|
||||
<n-checkbox v-model:checked="auto_save_setting.auto_save">自动保存(选择后生效)</n-checkbox>
|
||||
</div>
|
||||
<div>
|
||||
<n-checkbox v-model:checked="auto_save_setting.auto_match">自动匹配(再跑批量的时候匹配)</n-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: 10px;">
|
||||
<div style="margin-bottom: 3px;">选择输出文件夹</div>
|
||||
<n-input placeholder="请选择主文件夹" v-model:value="auto_save_setting.main_save_folder"
|
||||
style="width: 200px; margin-right: 10px;"></n-input>
|
||||
<n-button type="info" @click="SelectMainFolder">选择文件夹</n-button>
|
||||
</div>
|
||||
<div style="margin-left: 10px;">
|
||||
<div style="margin-bottom: 3px; opacity: 0; ">12</div>
|
||||
<n-button type="success" @click="SaveImageAutoSaveSetting">保存设置</n-button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="first_row" style="display: flex; margin-top: 15px">
|
||||
<!-- <div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">保存文件夹分类</div>
|
||||
<n-select v-model:value="auto_save_setting.save_folder_classify" :options="setting_options"
|
||||
placeholder="请选择保存文件夹分类" style="width: 200px;">
|
||||
</n-select>
|
||||
</div> -->
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">保存前面多少行(空为全部)</div>
|
||||
<n-input-number v-model:value="auto_save_setting.save_match_count" :show-button="false"
|
||||
placeholder=""></n-input-number>
|
||||
</div>
|
||||
</template>
|
||||
勾选并设置之后,每次出图之后出设置的张数。其余的会自动在设置的文件中随机匹配
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card style=" margin-top: 10px;" title="手动保存">
|
||||
<div class="first_row" style="display: flex; margin-top: 15px">
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">选择保存到的文件夹</div>
|
||||
<n-input placeholder="选择保存到的文件夹" v-model:value="save_setting.save_folder"
|
||||
style="width: 200px; margin-right: 10px;"></n-input>
|
||||
<n-button type="info" @click="SelectOutFolder">选择文件夹</n-button>
|
||||
</div>
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">保存前面多少行(空为全部)</div>
|
||||
<n-input-number v-model:value="save_setting.save_match_count" :show-button="false"
|
||||
placeholder=""></n-input-number>
|
||||
</div>
|
||||
<div style="margin-left: 10px;">
|
||||
<div style="margin-bottom: 3px; opacity: 0; ">12</div>
|
||||
<n-dropdown trigger="hover" :options="options" @select="handleSelect">
|
||||
<n-button type="success" @click="SaveImage">开始另存</n-button>
|
||||
</n-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="first_row" style="display: flex; margin-top: 15px">
|
||||
<div style="margin-left: 10px; ">
|
||||
<div style="margin-bottom: 3px;">选择要保存的文件夹</div>
|
||||
<n-input placeholder="选择要保存的文件夹" v-model:value="save_setting.select_folder"
|
||||
style="width: 400px; margin-right: 10px;"></n-input>
|
||||
<n-button type="info" @click="SelectSaveFolder">选择文件夹</n-button>
|
||||
</div>
|
||||
|
||||
</div> -->
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, toRaw } from "vue";
|
||||
import { NButton, useMessage, NSpin, NSpace, NSelect, NDivider, NInput, NCode, NCard, NCheckbox, NGradientText, NFormItem, NForm, NInputNumber, NTooltip, NDropdown } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
import { isEmpty, isNull } from "lodash";
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NSpace, NSelect, NDivider, NInput, NCode, NCard, NCheckbox, NGradientText, NForm, NFormItem, NInputNumber, NTooltip, NDropdown
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage()
|
||||
let auto_save_setting = ref({
|
||||
auto_save: false,
|
||||
auto_match: false,
|
||||
save_match_count: 0,
|
||||
main_save_folder: null,
|
||||
save_folder_classify: null,
|
||||
})
|
||||
|
||||
let save_setting = ref({
|
||||
save_folder: null,
|
||||
save_match_count: 0,
|
||||
select_folder: null
|
||||
})
|
||||
let setting_options = ref([])
|
||||
|
||||
/**
|
||||
* 选择保存图片的主文件夹
|
||||
*/
|
||||
async function SelectMainFolder() {
|
||||
await window.api.selectFolder({ default_path: toRaw(auto_save_setting.value).main_save_folder }, (value) => {
|
||||
if (value.length > 0) {
|
||||
auto_save_setting.value.main_save_folder = value[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function InitData() {
|
||||
await window.api.GetAutoSaveImageClassifyOptions((value) => {
|
||||
setting_options.value = value.data
|
||||
})
|
||||
// 获取自动保存设置
|
||||
|
||||
await window.api.GetImageAutoSaveSetting((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
auto_save_setting.value = value.data
|
||||
save_setting.value.save_match_count = value.data.save_match_count
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
await InitData();
|
||||
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存图片自动保存设置
|
||||
*/
|
||||
async function SaveImageAutoSaveSetting() {
|
||||
await window.api.SaveImageAutoSaveSetting(JSON.stringify(toRaw(auto_save_setting.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value)
|
||||
return
|
||||
}
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "保存成功" })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉框点击事件
|
||||
* @param {*} value
|
||||
*/
|
||||
async function handleSelect(value) {
|
||||
if (value == 'stop_save') {
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.IMAGE_SAVE_TO_OTHER_FOLDER, "all", "default"], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return
|
||||
}
|
||||
message.success("停止成功")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 另存图片的方法
|
||||
*/
|
||||
async function SaveImage() {
|
||||
debugger
|
||||
if (isEmpty(save_setting.value.save_folder)) {
|
||||
message.error("请选择保存到的文件夹")
|
||||
return
|
||||
}
|
||||
if (save_setting.value.save_match_count == 0) {
|
||||
message.error("请填写保存前面多少行,不能为0")
|
||||
return
|
||||
}
|
||||
|
||||
await window.api.SaveImageToOtherFolder(JSON.stringify(toRaw(save_setting.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success("保存成功")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择要保存的文件夹
|
||||
*/
|
||||
async function SelectSaveFolder() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择要保存到的文件夹
|
||||
*/
|
||||
async function SelectOutFolder() {
|
||||
await window.api.selectFolder({ default_path: toRaw(auto_save_setting.value).main_save_folder }, (value) => {
|
||||
if (value.length > 0) {
|
||||
save_setting.value.save_folder = value[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
auto_save_setting,
|
||||
SelectMainFolder,
|
||||
setting_options,
|
||||
SaveImageAutoSaveSetting,
|
||||
save_setting,
|
||||
options: [
|
||||
{ label: '停止任务', key: 'stop_save' },
|
||||
],
|
||||
handleSelect,
|
||||
SaveImage,
|
||||
SelectOutFolder
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,623 @@
|
||||
<template>
|
||||
<div id="word_option"
|
||||
style=" margin-left: 20px; position:absolute; left: 0; top: 0; z-index: 100; height: 30px; width: 100%;">
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;" @click="AIModifyWord">一键洗稿</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;"
|
||||
@click="ContinueAIModifyWord">继续写稿(勾选了没有出来的)</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;" @click="SaveNewWord">保存洗稿后文件</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;" @click="ImportSrtAndGetTime">导入字幕</n-button>
|
||||
<n-button color="#1e90ff" size="small" style=" margin-left: 20px;"
|
||||
@click="SaveCopywritingInformation">保存文案信息</n-button>
|
||||
</div>
|
||||
<n-data-table style="margin-top: 15px;" :row-key="rowKey" :columns="columns" :data="data" :pagination="pagination"
|
||||
:bordered="false" @update:checked-row-keys="handleCheck" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { h, defineComponent, onMounted, ref, toRaw } from "vue";
|
||||
import { NButton, NDataTable, useMessage, NInput, NImage, useDialog } from "naive-ui";
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "切换",
|
||||
key: "switch",
|
||||
color: "#8a2be2"
|
||||
},
|
||||
{
|
||||
title: "洗稿",
|
||||
key: 'aiWord',
|
||||
color: "#8a2be2"
|
||||
},
|
||||
{
|
||||
title: "下对齐",
|
||||
key: 'AlignNextWord',
|
||||
color: "#8a2be0"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NInput, NImage
|
||||
},
|
||||
setup() {
|
||||
const message = useMessage();
|
||||
let data = ref([]);
|
||||
let selectKey = ref([]);
|
||||
let textData = [];
|
||||
let dialog = useDialog();
|
||||
let replace_before = ref("");
|
||||
let replace_after = ref("");
|
||||
|
||||
const createColumns = ({
|
||||
AIModifyOneWord,
|
||||
SwitchOldWord
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
type: "selection",
|
||||
disabled(row) {
|
||||
return row.name === "Edward King 3";
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "编号",
|
||||
key: 'no',
|
||||
width: 50
|
||||
},
|
||||
{
|
||||
title: "文案",
|
||||
key: "word",
|
||||
width: 250,
|
||||
render(row, index) {
|
||||
return h(NInput, {
|
||||
value: row.word,
|
||||
type: "textarea",
|
||||
autosize: true,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].word = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "options",
|
||||
width: 130,
|
||||
render(row) {
|
||||
const btn = buttonArr.map(item => {
|
||||
return h(
|
||||
NButton, {
|
||||
style: "margin : 2px",
|
||||
strong: true,
|
||||
size: "tiny",
|
||||
color: item.color,
|
||||
onClick: () => {
|
||||
if (item.key == "aiWord") {
|
||||
AIModifyOneWord(row);
|
||||
} else if (item.key == "switch") {
|
||||
SwitchOldWord(row);
|
||||
} else if (item.key == "AlignNextWord") {
|
||||
AlignNextWord(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
return btn;
|
||||
}
|
||||
},
|
||||
{
|
||||
title(row) {
|
||||
return h("div", {
|
||||
style: "display : flex; align-items: center;"
|
||||
}, [
|
||||
h("span", {
|
||||
style: "margin-right : 2px; width : 80px "
|
||||
}, "洗稿后"),
|
||||
h("div", {
|
||||
style: " margin-right : 5px"
|
||||
}, [
|
||||
h(NInput, {
|
||||
size: "tiny",
|
||||
placeholder: "替换前的值",
|
||||
value: replace_before.value,
|
||||
onUpdateValue(v) {
|
||||
replace_before.value = v
|
||||
}
|
||||
}),
|
||||
h(NInput, {
|
||||
size: "tiny",
|
||||
placeholder: "替换后的值",
|
||||
value: replace_after.value,
|
||||
onUpdateValue(v) {
|
||||
replace_after.value = v
|
||||
}
|
||||
})
|
||||
]),
|
||||
h("div", {}, [
|
||||
h(NButton, {
|
||||
style: "",
|
||||
size: "tiny",
|
||||
color: "#70a1ff",
|
||||
onClick: () => ReplaceText()
|
||||
}, { default: () => "替换" }),
|
||||
])
|
||||
])
|
||||
},
|
||||
key: "after_gpt",
|
||||
width: 300,
|
||||
render(row, index) {
|
||||
return h(NInput, {
|
||||
value: row.after_gpt,
|
||||
placeholder: "洗稿后的值",
|
||||
type: "textarea",
|
||||
autosize: true,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].after_gpt = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "字幕",
|
||||
key: "srt",
|
||||
render(row) {
|
||||
let tmp = row.subValue.map(item => h("div", { style: "display:flex; height : auto; margin: 2px 0" }, [
|
||||
h(NInput, {
|
||||
size: "tiny",
|
||||
value: item.srt_value
|
||||
|
||||
}),
|
||||
h(NButton, {
|
||||
style: "margin : 0 2px",
|
||||
size: "tiny",
|
||||
color: "#5f4321",
|
||||
onClick: () => Upper(row, item, true)
|
||||
}, { default: () => "向上" }),
|
||||
h(NButton, {
|
||||
style: "margin : 0 2px",
|
||||
size: "tiny",
|
||||
color: "#ee7959",
|
||||
onClick: () => Down(row, item)
|
||||
}, { default: () => "向下" })
|
||||
]))
|
||||
return tmp
|
||||
}
|
||||
}, {
|
||||
title: "时间范围",
|
||||
key: "timeLimit",
|
||||
width: 120
|
||||
}, {
|
||||
title: "原图",
|
||||
width: 120,
|
||||
render(row) {
|
||||
return h(NImage, {
|
||||
src: "file:\\" + (row.old_image ? row.old_image : row.outImagePath),
|
||||
alt: 'Image',
|
||||
width: '120'
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// 初始化加载项目下面的分镜好的文案
|
||||
// 并判断是不是有洗稿后的文件。一并加载
|
||||
await window.api.GetProjectWord(value => {
|
||||
debugger;
|
||||
data.value = value.data;
|
||||
})
|
||||
|
||||
let div = document.getElementById("word_option");
|
||||
if (window.config.theme == "dark") {
|
||||
div.style.backgroundColor = "#24242c"
|
||||
} else {
|
||||
div.style.backgroundColor = "#FFF"
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* 替换洗稿后的所有数据
|
||||
*/
|
||||
async function ReplaceText() {
|
||||
// 开始替换
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].after_gpt = data.value[i].after_gpt.replaceAll(replace_before.value, replace_after.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算指定索引值的时间
|
||||
* @param {data value 值的索引} index
|
||||
*/
|
||||
function modifyTime(index) {
|
||||
let s_v = data.value[index].subValue;
|
||||
let len = s_v.length;
|
||||
if (len > 0) {
|
||||
let s_t = s_v[0].start_time;
|
||||
let e_t = s_v[len - 1].end_time;
|
||||
data.value[index].start_time = s_t;
|
||||
data.value[index].end_time = e_t;
|
||||
data.value[index].timeLimit = `${s_t} -- ${e_t}`;
|
||||
} else {
|
||||
data.value[index].start_time = null;
|
||||
data.value[index].end_time = null;
|
||||
data.value[index].timeLimit = ``;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前行的子数据的长度是不是大于0。不大于0 的话下面的字幕往上替补
|
||||
* @param {data value 的索引} index
|
||||
*/
|
||||
function moveUp(index) {
|
||||
let s_v = data.value[index].subValue;
|
||||
if (s_v.length == 0) {
|
||||
// 将下面所有的字幕数据往上移动
|
||||
for (let i = index; i < data.value.length; i++) {
|
||||
// const element = data.value[i];
|
||||
if (data.value[i + 1] != null) {
|
||||
data.value[i].subValue = data.value[i + 1].subValue
|
||||
} else {
|
||||
// 检查当前这个是不是为空。为空直接删除
|
||||
// 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除)
|
||||
if (data.value[i].word == null || data.value[i].word == "" || data.value[i].after_gpt == null || data.value[i].after_gpt == "") {
|
||||
// 直接删除
|
||||
data.value.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字幕文件向上
|
||||
*/
|
||||
async function Upper(row, item, isM) {
|
||||
// 向上合并。
|
||||
// 当前item。向上的话。判断是不是第一个。是第一个的话。直接网上一个合并。
|
||||
// 不是第一个的话。将上面的所有的合并。
|
||||
// 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。
|
||||
// 向下合并。逻辑相同。
|
||||
|
||||
if (row.lastId == null || row.lastId == "") {
|
||||
message.error("当前是第一个。不能向上合并");
|
||||
return;
|
||||
}
|
||||
//判断当前行是不是第一个
|
||||
let itemId = item.id;
|
||||
let itemIndex = row.subValue.findIndex(item => item.id == itemId);
|
||||
let thisIndex = data.value.findIndex(item => item.id == row.id);
|
||||
if (itemIndex < 0 && isM) {
|
||||
debugger
|
||||
message.error("数据错误");
|
||||
return;
|
||||
} else {
|
||||
// 先获取上一行的数据
|
||||
let lastRowIndex = data.value.findIndex(item => item.id == row.lastId);
|
||||
//将当前及之前的数据向上合并
|
||||
for (let i = 0; i <= itemIndex; i++) {
|
||||
const element = row.subValue[i];
|
||||
data.value[lastRowIndex].subValue.push(element);
|
||||
}
|
||||
|
||||
// 删除指定的数据
|
||||
data.value[lastRowIndex + 1].subValue.splice(0, itemIndex + 1)
|
||||
moveUp(thisIndex);
|
||||
|
||||
// 判断当前行的文案和洗稿后文件是不是还有。(没有的话直接删除)
|
||||
if ((data.value[thisIndex].subValue == null) && (row.word == null || row.word == "" || row.after_gpt == null || row.after_gpt == "")) {
|
||||
// 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId)
|
||||
if (data.value[thisIndex + 1] != null) {
|
||||
data.value[thisIndex + 1].lastId = row.lastId;
|
||||
}
|
||||
// 直接删除
|
||||
data.value.splice(thisIndex, 1);
|
||||
// 调整下面的所有的编号
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
data.value[i].no = data.value[i].no - 1;
|
||||
}
|
||||
}
|
||||
// 合并之后重新计算时间(计算上一个和当前的)
|
||||
modifyTime(lastRowIndex);
|
||||
modifyTime(thisIndex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字幕文件向下
|
||||
*/
|
||||
async function Down(row, item) {
|
||||
// 向下合并。逻辑相同。
|
||||
// 当前item。。判断是不是最后一个。是最后一个的话。直接向下合并。
|
||||
// 不是第一个的话。将下面的所有的合并。
|
||||
// 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。
|
||||
//判断当前行是不是第一个
|
||||
let itemId = item.id;
|
||||
let itemIndex = row.subValue.findIndex(item => item.id == itemId);
|
||||
let thisIndex = data.value.findIndex(item => item.id == row.id);
|
||||
// 判断下一个是不是存在
|
||||
if (data.value[thisIndex + 1] == null) {
|
||||
message.error("当前已经是最后一个。不能向下合并");
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemIndex < 0) {
|
||||
message.error("数据错误 2222");
|
||||
return;
|
||||
} else {
|
||||
// 获取下一行数据
|
||||
let nextRowIndex = data.value.findIndex(item => item.lastId == row.id);
|
||||
|
||||
//将当前以及下面的所有进行合并
|
||||
for (let i = row.subValue.length - 1; i >= itemIndex; i--) {
|
||||
const element = row.subValue[i];
|
||||
data.value[nextRowIndex].subValue.unshift(element);
|
||||
}
|
||||
|
||||
// 删除指定的数据
|
||||
data.value[thisIndex].subValue.splice(itemIndex)
|
||||
|
||||
// 判断该当前字幕是不是为空(为空的话。下面的往上移动)
|
||||
moveUp(thisIndex);
|
||||
|
||||
// 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除)
|
||||
if ((data.value[thisIndex].subValue == null) && (row.word == null || row.word == "" || row.after_gpt == null || row.after_gpt == "")) {
|
||||
// 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId)
|
||||
if (data.value[nextRowIndex] != null) {
|
||||
data.value[nextRowIndex].lastId = row.lastId;
|
||||
}
|
||||
// 直接删除
|
||||
data.value.splice(thisIndex, 1);
|
||||
// 调整下面的所有的编号
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
data.value[i].no = data.value[i].no - 1;
|
||||
}
|
||||
}
|
||||
// 合并之后重新计算时间(计算上一个和当前的)
|
||||
modifyTime(nextRowIndex);
|
||||
modifyTime(thisIndex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function removePunctuationIncludingEllipsis(sentence) {
|
||||
// 扩展正则表达式以包含中文标点符号和省略号
|
||||
// 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符
|
||||
const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g;
|
||||
|
||||
// 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串
|
||||
return sentence.replace(punctuationRegExp, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 对齐当前列即下面的数据
|
||||
*/
|
||||
async function AlignNextWord(row) {
|
||||
// 判断是不是已经到到导入过字幕了
|
||||
dialog.create({
|
||||
type: "warning",
|
||||
title: "警告",
|
||||
showIcon: true,
|
||||
content: "会自动对齐下面的数据。没有对齐成功就要手动调整在自动对齐",
|
||||
style: `width : 400px;`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
// 下对齐
|
||||
let itemId = row.id;
|
||||
let thisIndex = data.value.findIndex(item => item.id == itemId);
|
||||
if (thisIndex < 0) {
|
||||
message.error("数据错误");
|
||||
return;
|
||||
}
|
||||
|
||||
let nextSrt = [];
|
||||
// 先将当前行下面的所有的数据拿出来
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
nextSrt = nextSrt.concat(element.subValue);
|
||||
}
|
||||
let init_num = thisIndex;
|
||||
// let data
|
||||
let tmp_str = "";
|
||||
for (let i = 0; i < nextSrt.length;) {
|
||||
const element = nextSrt[i];
|
||||
let current_text = `第 ${thisIndex + 1} 数据。字幕:${element.srt_value}与文案不匹配。亲检查上下文。`
|
||||
tmp_str += element.srt_value;
|
||||
|
||||
if (removePunctuationIncludingEllipsis(data.value[thisIndex].after_gpt).startsWith(removePunctuationIncludingEllipsis(tmp_str))) {
|
||||
// data.value[thisIndex].subValue.push(element);
|
||||
if (init_num < thisIndex || i > 0) {
|
||||
Upper(data.value[thisIndex + 1], element, false);
|
||||
}
|
||||
i++;
|
||||
} else {
|
||||
if (removePunctuationIncludingEllipsis(data.value[thisIndex + 1].after_gpt).startsWith(removePunctuationIncludingEllipsis(element.srt_value))) {
|
||||
thisIndex++;
|
||||
tmp_str = "";
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: 0,
|
||||
message: current_text
|
||||
})
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将原本的文案替换洗稿后
|
||||
*/
|
||||
async function SwitchOldWord(row) {
|
||||
data.value.forEach(item => {
|
||||
if (item.no == row.no) {
|
||||
item.after_gpt = item.word;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句写稿
|
||||
*/
|
||||
async function AIModifyOneWord(row) {
|
||||
await window.api.AIModifyOneWord([row.no, row.word], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
// 修改数据
|
||||
let filter_data = data.value.filter(item => item.no == value.data.no);
|
||||
filter_data[0].after_gpt = value.data.content;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键洗稿,AI修改选择的
|
||||
*/
|
||||
async function AIModifyWord() {
|
||||
let word_data = [];
|
||||
for (let i = 0; i < selectKey.value.length; i++) {
|
||||
const item = selectKey.value[i];
|
||||
let obj = {
|
||||
no: data.value[item - 1].no,
|
||||
word: data.value[item - 1].word
|
||||
}
|
||||
word_data.push(obj);
|
||||
}
|
||||
for (let i = 0; i < word_data.length; i++) {
|
||||
const item = word_data[i];
|
||||
await AIModifyOneWord(item)
|
||||
}
|
||||
|
||||
// 洗完稿之后全局提示
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "一键洗稿完成" })
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存洗稿后文件
|
||||
*/
|
||||
async function SaveNewWord() {
|
||||
let new_data_word = [];
|
||||
// 先填充没有洗稿的
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
if (!item.after_gpt) {
|
||||
item.after_gpt = item.word;
|
||||
}
|
||||
new_data_word.push(item.after_gpt);
|
||||
}
|
||||
textData = new_data_word;
|
||||
// 开始保存
|
||||
await window.api.SaveNewWord(new_data_word, (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
message.success("保存成功,并写入到了剪贴板");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存字幕文案信息,用于自动生成视频
|
||||
*/
|
||||
async function SaveCopywritingInformation() {
|
||||
await window.api.SaveCopywritingInformation([toRaw(data.value), "srt_time_information"], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入字幕并进行分镜输出,显示时间,将分镜给他传进去
|
||||
*/
|
||||
async function ImportSrtAndGetTime() {
|
||||
if (textData.length <= 0) {
|
||||
message.error("请先保存洗稿后文件,不洗稿也要保存一下");
|
||||
return;
|
||||
}
|
||||
await window.api.ImportSrtAndGetTime([toRaw(data.value)], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// 写入时间
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const item = value.data[i];
|
||||
if (i >= data.value.length) {
|
||||
item.no = i + 1;
|
||||
item.timeLimit = `${item.start_time} -- ${item.end_time}`
|
||||
data.value.push(item)
|
||||
} else {
|
||||
data.value[i].start_time = item.start_time;
|
||||
data.value[i].end_time = item.end_time;
|
||||
data.value[i].timeLimit = `${item.start_time} -- ${item.end_time}`
|
||||
data.value[i].subValue = item.subValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 继续写被勾选的但是没有出来的
|
||||
*/
|
||||
async function ContinueAIModifyWord() {
|
||||
// 获取所有被选中的data
|
||||
let word_data = [];
|
||||
for (let i = 0; i < selectKey.value.length; i++) {
|
||||
const item = selectKey.value[i];
|
||||
|
||||
if (!data.value[item - 1].after_gpt) {
|
||||
let obj = {
|
||||
no: data.value[item - 1].no,
|
||||
word: data.value[item - 1].word
|
||||
}
|
||||
word_data.push(obj);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < word_data.length; i++) {
|
||||
const item = word_data[i];
|
||||
await AIModifyOneWord(item)
|
||||
}
|
||||
// 洗完稿之后全局提示
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "继续写稿完成" })
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns: createColumns({
|
||||
AIModifyOneWord,
|
||||
SwitchOldWord
|
||||
}),
|
||||
rowKey: (row) => row.no,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys;
|
||||
},
|
||||
pagination: false,
|
||||
AIModifyWord,
|
||||
AIModifyOneWord,
|
||||
ContinueAIModifyWord,
|
||||
ImportSrtAndGetTime,
|
||||
SaveNewWord,
|
||||
SaveCopywritingInformation,
|
||||
replace_before,
|
||||
replace_after,
|
||||
ReplaceText
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<n-tabs animated default-value="inGetFrame">
|
||||
<n-tab-pane size="large" name="draftGetFrame" tab="剪映草稿抽帧">
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
正在抽帧
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<div style="color: red;">
|
||||
注意:该项是使用剪映内的草稿进行分镜
|
||||
</div>
|
||||
<n-select v-model:value="selectedValue" filterable placeholder="选择草稿" :options="options" />
|
||||
<n-button round type="primary" @click="getFrameFunc">开始抽帧</n-button>
|
||||
</n-space>
|
||||
</n-spin>
|
||||
<n-divider />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="inGetFrame" tab="软件内抽帧">
|
||||
<div style="color: red; font-size: large;">
|
||||
注意:该功能需要对环境要求较高。(环境需要自行安装,第一次执行需要下载模型需要魔法)<span class="url_class" @click="OpenTeachDoc">环境安装教程</span>
|
||||
<br>
|
||||
分割敏感度:值越小分割越精细,值越大分割越粗糙
|
||||
</div>
|
||||
<n-form style="margin-top: 50px;" ref="formRef" label-placement="left" inline :model="frameValue"
|
||||
:rules="rules" size="medium">
|
||||
<n-form-item label="分割敏感度" path="sensitivity" style="width: 300px;">
|
||||
<n-slider v-model:value=frameValue.sensitivity show-tooltip />
|
||||
</n-form-item>
|
||||
<n-form-item label="分镜视频地址" path="video_path">
|
||||
<n-input v-model:value="frameValue.video_path" placeholder="选择或输入要分镜的视频地址" style="width: 300px;" />
|
||||
<n-button type="primary" @click="GetVideoFile">
|
||||
选择文件(MP4)
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="StartStoryboarding" :loading="storyLoading">
|
||||
开始分镜
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-divider />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<n-code style="padding: 0; font-size: small;" :code="code" language="js" word-wrap />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, toRaw } from "vue";
|
||||
import { NSelect, NButton, useMessage, NSpin, NDivider, NCode, NTabs, NTabPane, NSpace, NForm, NFormItem, NSlider, NInput } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect, NButton, NSpin, NDivider, NCode, NTabs, NTabPane, NSpace, NForm, NFormItem, NSlider, NInput
|
||||
},
|
||||
setup() {
|
||||
let options = [];
|
||||
let out_dir = ref(null);
|
||||
let selectedValue = ref(null);
|
||||
let show = ref(false);
|
||||
let code = ref("");
|
||||
const message = useMessage();
|
||||
let storyLoading = ref(false);
|
||||
|
||||
let frameValue = ref({
|
||||
sensitivity: 30,
|
||||
video_path: null
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.api.getDraftFileList((value) => {
|
||||
value.forEach(element => {
|
||||
let obj = {
|
||||
label: element,
|
||||
value: element
|
||||
}
|
||||
options.push(obj)
|
||||
});
|
||||
})
|
||||
window.api.setEventListen([DEFINE_STRING.GET_FRAME_RETUN], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
code.value = code.value + "\n" + value.data;
|
||||
return;
|
||||
}
|
||||
// 完成
|
||||
if (value.type == 0) {
|
||||
storyLoading.value = false;
|
||||
message.success("分镜抽帧完成");
|
||||
}
|
||||
code.value = code.value + "\n" + value.data;
|
||||
})
|
||||
})
|
||||
|
||||
async function getFrameFunc(e) {
|
||||
out_dir = window.config.project_path
|
||||
if (selectedValue.value == null || selectedValue.value == undefined) {
|
||||
message.error("请选择剪映草稿和输出草稿")
|
||||
return;
|
||||
}
|
||||
show.value = true
|
||||
// 抽帧
|
||||
await window.api.getFrame([selectedValue.value, out_dir], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("抽帧失败");
|
||||
code.value = value.message;
|
||||
return;
|
||||
}
|
||||
message.success("抽帧成功");
|
||||
code.value = value.data;
|
||||
show.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
function selectExportFolder(e) {
|
||||
window.api.selectFolder(null, (value) => {
|
||||
if (value.length <= 0) {
|
||||
message.error("必须选择输出文件夹位置");
|
||||
return;
|
||||
}
|
||||
out_dir.value = value[0]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择指定的视频文件
|
||||
*/
|
||||
async function GetVideoFile() {
|
||||
await window.api.SelectFile(['mp4'], (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
frameValue.value.video_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始分镜执行分镜任务
|
||||
*/
|
||||
async function StartStoryboarding() {
|
||||
storyLoading.value = true;
|
||||
if (frameValue.value.video_path == null) {
|
||||
message.error("选择分镜的视频地址");
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
if (toRaw(frameValue.value).video_path.split('.')[toRaw(frameValue.value).video_path.split('.').length - 1].toUpperCase() != "MP4") {
|
||||
message.error("目前只支持MP4格式");
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.StartStoryboarding(toRaw(frameValue.value))
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开环境安装网站
|
||||
*/
|
||||
function OpenTeachDoc() {
|
||||
window.api.OpenUrl("https://pvwu1oahp5m.feishu.cn/docx/VrBVd2KUDosmNfxat3OceWuInjd?from=from_copylink");
|
||||
}
|
||||
|
||||
return {
|
||||
selectedValue,
|
||||
options,
|
||||
out_dir,
|
||||
getFrameFunc,
|
||||
selectExportFolder,
|
||||
show,
|
||||
code,
|
||||
frameValue,
|
||||
GetVideoFile,
|
||||
StartStoryboarding,
|
||||
OpenTeachDoc,
|
||||
storyLoading
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.url_class {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.url_class:hover {
|
||||
color: brown;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<div>一键矩阵功能:有对应的字幕配置文件,直接自动的生图。高清。合成视频 <span
|
||||
style="margin-left: 20px ;font-size: 20px; color: red;">注意:前三步还是必做</span></div>
|
||||
<n-form ref="configRef" inline :model="config" :rules="rules">
|
||||
<n-form-item label="SRT字幕文件" path="srt_path">
|
||||
<n-input disabled v-model:value="config.srt_path" placeholder="选择SRT文件" />
|
||||
<n-button color="#e5a84b" @click="SelectSrt">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="配音文件" path="audio_path">
|
||||
<n-input disabled="" v-model:value="config.audio_path" placeholder="选择配音文件" />
|
||||
<n-button color="#e5a84b" @click="SelectAudio">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="背景音乐文件夹" path="background_music">
|
||||
<n-select style="width: 300px;" v-model:value="config.background_music" :options="background_music_options"
|
||||
placeholder="选择背景音乐文件夹" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button :type="buttonLabel.type" @click="AutoConditionCheck">
|
||||
{{ buttonLabel.label }}
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="success" @click="ActionTask">
|
||||
一键全自动
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-divider />
|
||||
<n-data-table :columns="columns" :data="data" size="small" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, toRaw, h, onUnmounted } from "vue";
|
||||
import { NSelect, NForm, NFormItem, NButton, NInput, NIcon, NPopover, useMessage, NDivider, NDataTable, NTag } from "naive-ui"
|
||||
import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5'
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
|
||||
function statusAndMessage(row) {
|
||||
if (row.status == 'wait') {
|
||||
return {
|
||||
tagLabel: "等待中",
|
||||
color: '',
|
||||
type: "warning"
|
||||
}
|
||||
} else if (row.status == 'queue') {
|
||||
return {
|
||||
tagLabel: "队列中",
|
||||
color: '',
|
||||
type: "info"
|
||||
}
|
||||
} else if (row.status == 'ok') {
|
||||
return {
|
||||
tagLabel: "生图完成(未高清)",
|
||||
color: { color: "#2ed573", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
}
|
||||
else if (row.status == 'error') {
|
||||
return {
|
||||
tagLabel: "生图错误",
|
||||
color: "",
|
||||
type: "error"
|
||||
}
|
||||
}
|
||||
else if (row.status == "video_improvied") {
|
||||
return {
|
||||
tagLabel: "高清完成",
|
||||
color: { color: "#70a1ff", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_improving") {
|
||||
return {
|
||||
tagLabel: "高清中",
|
||||
color: { color: "#fedc5e", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_improvie_error") {
|
||||
return {
|
||||
tagLabel: "高清失败",
|
||||
color: { color: "#d23918", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
}
|
||||
else if (row.status == "video_queue") {
|
||||
return {
|
||||
tagLabel: "合成队列中",
|
||||
color: { color: "#55efc4", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_error") {
|
||||
return {
|
||||
tagLabel: "合成失败",
|
||||
color: { color: "#ff4757", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else if (row.status == "video_ok") {
|
||||
return {
|
||||
tagLabel: "合成完成",
|
||||
color: { color: "#1dd1a1", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
tagLabel: "状态错误",
|
||||
color: { color: "#d2dae2", textColor: '#ffffff' },
|
||||
type: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const createColumns = ({
|
||||
RemoveTask
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
title: "批次名称",
|
||||
key: "out_folder"
|
||||
},
|
||||
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: (() => statusAndMessage(row))().type,
|
||||
color: (() => statusAndMessage(row))().color,
|
||||
bordered: false
|
||||
},
|
||||
{
|
||||
default: (() => statusAndMessage(row).tagLabel)
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: "small",
|
||||
type: "warning",
|
||||
onClick: async () => await RemoveTask(row)
|
||||
},
|
||||
{ default: () => "停止任务" }
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect, NForm, NFormItem, NButton, NInput, NIcon, FolderOpen, NPopover, NDivider, NDataTable, NTag
|
||||
},
|
||||
setup() {
|
||||
let config = ref({
|
||||
srt_path: null,
|
||||
audio_path: null,
|
||||
draft_srt_style: "0",
|
||||
background_music: null,
|
||||
friendly_reminder: "0"
|
||||
})
|
||||
let background_music_options = ref([]);
|
||||
let configRef = ref(null);
|
||||
let message = useMessage();
|
||||
let buttonLabel = ref({
|
||||
label: "未进行条件检测",
|
||||
type: "warning"
|
||||
})
|
||||
let data = ref([]);
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取队列信息
|
||||
// 获取背景音乐文件夹信息
|
||||
await window.api.GetBackgroundMusicConfigList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < value.value.length; i++) {
|
||||
const element = value.value[i];
|
||||
let obj = {
|
||||
label: element.name,
|
||||
value: element.id,
|
||||
}
|
||||
background_music_options.value.push(obj);
|
||||
}
|
||||
})
|
||||
|
||||
// 获取上一次选择的字幕,配音。未年检
|
||||
await window.api.GetVideoGenerateConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("获取初始化数据失败");
|
||||
return;
|
||||
}
|
||||
config.value.srt_path = value.data.srt_path;
|
||||
config.value.audio_path = value.data.audio_path;
|
||||
config.value.background_music = value.data.background_music;
|
||||
|
||||
})
|
||||
|
||||
// 获取初始化数据(队列任务-这边不能添加)
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
data.value = value.data.task_list;
|
||||
})
|
||||
|
||||
// 监听状态刷新事件
|
||||
window.api.setEventListen([DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
let index = data.value.findIndex(item => item.id == value.id);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
// 修改
|
||||
data.value[index].status = value.status;
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH);
|
||||
})
|
||||
|
||||
/**
|
||||
* 全自动条件检测
|
||||
*/
|
||||
async function AutoConditionCheck(e) {
|
||||
e.preventDefault();
|
||||
configRef.value?.validate(async (errors) => {
|
||||
debugger
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return;
|
||||
}
|
||||
await window.api.AutoConditionCheck(toRaw(config.value), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
buttonLabel.value = {
|
||||
label: "条件检测失败",
|
||||
type: "error"
|
||||
}
|
||||
return;
|
||||
}
|
||||
message.success("条件检测成功");
|
||||
buttonLabel.value = {
|
||||
label: "条件检测成功",
|
||||
type: "success"
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始执行全自动任务
|
||||
*/
|
||||
async function ActionTask(e) {
|
||||
e.preventDefault();
|
||||
configRef.value?.validate(async (errors) => {
|
||||
debugger
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return;
|
||||
}
|
||||
if (buttonLabel.value.type != "success") {
|
||||
message.error("请先进行条件检测");
|
||||
return;
|
||||
}
|
||||
// 像后端传递传递开始任务
|
||||
await window.api.ActionAutoVideoTask([[...toRaw(data.value)], toRaw(config.value)], (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
// let index = data.value.findIndex(item=>item.id == value)
|
||||
window.api.showGlobalMessageDialog(value)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectSrt() {
|
||||
window.api.SelectFile(['srt'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
config.value.srt_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectAudio() {
|
||||
window.api.SelectFile(['mp3', 'wav'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
config.value.audio_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除任务
|
||||
* @param {*} row
|
||||
*/
|
||||
async function RemoveTask(row) {
|
||||
// 先删除出现的任务
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE, "all", `${row.out_folder}_image`], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 在删除高清任务
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE, "all", `${row.out_folder}_improve`], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 在删除和合成视频任务
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE, "all", `${row.out_folder}_video`], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
await window.api.ModifyGenerateTaskStatus(['out_folder', row.out_folder, 'wait'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
return {
|
||||
config,
|
||||
SelectSrt,
|
||||
SelectAudio,
|
||||
background_music_options,
|
||||
AutoConditionCheck,
|
||||
ActionTask,
|
||||
configRef,
|
||||
buttonLabel,
|
||||
data,
|
||||
columns: createColumns({
|
||||
RemoveTask
|
||||
}),
|
||||
rules: {
|
||||
srt_path: {
|
||||
required: true,
|
||||
message: "请输入SRT文件",
|
||||
trigger: ["input", "blur"]
|
||||
},
|
||||
audio_path: {
|
||||
required: true,
|
||||
message: "请选择配音文件",
|
||||
trigger: ["input", "blur"]
|
||||
},
|
||||
background_music: {
|
||||
required: true,
|
||||
message: "请选择背景音乐",
|
||||
trigger: ["input", "blur"]
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
正在反推
|
||||
</template>
|
||||
<n-button type="info" @click="PushBackPrompt"> 开始反推 </n-button>
|
||||
<n-button type="error" style="margin-left: 50px;" @click="DeleteBadPrompt"> 一键去除所有不想要的值 </n-button>
|
||||
<n-button type="warning" style="margin-left: 50px;" @click="ManagePrompt"> 管理不想要的值 </n-button>
|
||||
<n-divider />
|
||||
</n-spin>
|
||||
<n-code style="padding: 0;" :code="code" language="js" word-wrap />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, h, toRaw } from "vue";
|
||||
import { NButton, useMessage, useDialog, NDivider, NCode, NSpin } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import ManageBadPrompt from "../Components/ManageBadPrompt.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDivider, NCode, NSpin
|
||||
},
|
||||
setup() {
|
||||
let dialog = useDialog();
|
||||
let code = ref("");
|
||||
let show = ref(false);
|
||||
let promprRef = ref();
|
||||
let message = useMessage();
|
||||
|
||||
// 开始反推
|
||||
async function PushBackPrompt() {
|
||||
show.value = true;
|
||||
await window.api.PushBackPrompt((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("反推报错");
|
||||
code.value = value.message;
|
||||
return;
|
||||
}
|
||||
message.success("反推成功");
|
||||
code.value = value.data;
|
||||
show.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理不想要的值
|
||||
*/
|
||||
async function ManagePrompt() {
|
||||
// 弹窗,管理不想要的值
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
title: "管理不想要的值",
|
||||
content: () => h(ManageBadPrompt, { ref: promprRef }),
|
||||
style: `width : 650px;`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
console.log(toRaw(promprRef.value.tags));
|
||||
// 将数据写入
|
||||
await window.api.SaveBadPrompt(toRaw(promprRef.value.tags), (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return false;
|
||||
}
|
||||
message.success("添加成功");
|
||||
})
|
||||
return true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键删除不想要的值
|
||||
*/
|
||||
async function DeleteBadPrompt() {
|
||||
await window.api.DeleteBadPrompt((value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("去除成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
code,
|
||||
show,
|
||||
PushBackPrompt,
|
||||
ManagePrompt,
|
||||
DeleteBadPrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,495 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>当前选择风格:<n-button style="margin-left: 20px;" type="info" @click="ShowImageStyleDialog">选择风格</n-button>
|
||||
<n-button style="margin-left: 20px;" color="#dc6b82" @click="SaveImageSetting">保存图片设置</n-button>
|
||||
</div>
|
||||
<div>
|
||||
<ShowImageTag :selectStyle="selectStyle"></ShowImageTag>
|
||||
</div>
|
||||
<n-form ref="formRef" inline :label-width="80" :model="formValue">
|
||||
<n-form-item label="前缀" path="image_style">
|
||||
<n-input v-model:value="formValue.image_style" placeholder="填写前缀" />
|
||||
</n-form-item>
|
||||
<n-form-item label="输出轮次" path="output_rounds">
|
||||
<n-input-number v-model:value="formValue.output_rounds" placeholder="输出轮次" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="AddImageTask">
|
||||
添加任务
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="GenerateAllImage">
|
||||
批量生成
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="RefreshTaskList">
|
||||
刷新列表
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="warning" @click="StopTask">
|
||||
停止生成任务
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
<n-data-table :row-key="rowKey" @update:checked-row-keys="handleCheck" :columns="columns" :data="task_list_data"
|
||||
:pagination="false" :bordered="true" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw, vModelCheckbox } from "vue";
|
||||
import { NButton, useMessage, NSpin, NInputNumber, NDivider, NForm, NFormItem, NInput, NDataTable, NTag, useDialog, NPopover, NImage } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
import SelectImageStyle from "../Components/SelectImageStyle.vue"
|
||||
import ShowImageTag from "../Components/ShowImageTag.vue"
|
||||
import BatchSaveImageSetting from "./BatchSaveImageSetting.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NInputNumber, NDivider, NForm, NFormItem, NInput, NDataTable, NTag, NPopover, ShowImageTag
|
||||
},
|
||||
setup() {
|
||||
let show = ref(false);
|
||||
let count = ref(0);
|
||||
let message = useMessage();
|
||||
let selectKey = ref([]);
|
||||
let formValue = ref({
|
||||
image_style: null,
|
||||
lora: null,
|
||||
output_rounds: 0,
|
||||
image_style_list: []
|
||||
})
|
||||
let task_list_data = ref([]);
|
||||
let dialog = useDialog();
|
||||
let selectStyle = ref([]);
|
||||
|
||||
const createColumns = ({
|
||||
DeleteImageTaskList,
|
||||
RemoveImageTask
|
||||
}) => {
|
||||
return [{
|
||||
type: "selection",
|
||||
},
|
||||
{
|
||||
title: "编号",
|
||||
key: "no"
|
||||
},
|
||||
{
|
||||
title: "输出文件夹",
|
||||
key: "out_folder"
|
||||
},
|
||||
{
|
||||
title: "风格",
|
||||
key: "image_style_list",
|
||||
render(row, index) {
|
||||
return h("div", {}, [
|
||||
h(ShowImageTag, { selectStyle: row.image_style_list_information })
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "前缀",
|
||||
key: "image_style"
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
render(row) {
|
||||
return h(NPopover, {
|
||||
trigger: 'hover',
|
||||
disabled: (() => {
|
||||
debugger
|
||||
if (row.status == "error") {
|
||||
return false;
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})()
|
||||
},
|
||||
{
|
||||
default: () => (() => {
|
||||
if (row.status == "wait") {
|
||||
return "warning"
|
||||
} else if (row.status == "queue") {
|
||||
return "info"
|
||||
} else if (row.status == "ok") {
|
||||
return "success"
|
||||
} else if (row.status == "error") {
|
||||
return row.errorMessage
|
||||
}
|
||||
})(),
|
||||
trigger: () => h(
|
||||
NTag,
|
||||
{
|
||||
type: (() => {
|
||||
if (row.status == "wait") {
|
||||
return "warning"
|
||||
} else if (row.status == "queue") {
|
||||
return "info"
|
||||
} else if (row.status == "ok") {
|
||||
return "success"
|
||||
} else if (row.status == "error") {
|
||||
return "error"
|
||||
} else if (row.status.startsWith("video")) {
|
||||
return "success"
|
||||
}
|
||||
})(),
|
||||
color: '',
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
if (row.status == "wait") {
|
||||
return "等待中"
|
||||
} else if (row.status == "queue") {
|
||||
return "队列中"
|
||||
} else if (row.status == "ok") {
|
||||
return "完成"
|
||||
} else if (row.status == "error") {
|
||||
return "错误"
|
||||
} else if (row.status.startsWith("video")) {
|
||||
return "合成视频相关"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return h("div", {}, [
|
||||
h(NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
type: "error",
|
||||
size: "small",
|
||||
onClick: () => DeleteImageTaskList(row)
|
||||
},
|
||||
{ default: () => "删除" }),
|
||||
h(NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
style: "margin-left: 10px;",
|
||||
type: "error",
|
||||
size: "small",
|
||||
onClick: async () => await RemoveImageTask(row)
|
||||
},
|
||||
{ default: () => "停止当前任务" })]
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
async function getInitData() {
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
for (let i = 0; i < task_list_data.value.length; i++) {
|
||||
const element = task_list_data.value[i];
|
||||
// 获取指定的风格数据
|
||||
await window.api.GetImageStyleInfomation(JSON.stringify(toRaw(element.image_style_list)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// selectStyle.value = JSON.parse(value.data[0].value);
|
||||
task_list_data.value[i]['image_style_list_information'] = value.data;
|
||||
})
|
||||
}
|
||||
await window.api.GetConfigJson(JSON.stringify(["image_style", []]), async (value) => {
|
||||
debugger;
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
await window.api.GetImageStyleInfomation(JSON.stringify(toRaw(value.data)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
selectStyle.value = value.data;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getInitData();
|
||||
|
||||
// 修改一个
|
||||
window.api.setEventListen([DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
// 修改当前状态
|
||||
let index = task_list_data.value.findIndex(item => item.out_folder == value.out_folder);
|
||||
if (index < 0) {
|
||||
message.error("数据错误");
|
||||
return;
|
||||
}
|
||||
// 获取指定的数据进行状态更新
|
||||
task_list_data.value[index].status = value.status;
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存批次
|
||||
*/
|
||||
async function AddImageTask() {
|
||||
if (formValue.value.output_rounds <= 0) {
|
||||
message.error("输出轮次不能为0或更小");
|
||||
return;
|
||||
}
|
||||
// 添加listId
|
||||
let tmp_arr = [];
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i];
|
||||
tmp_arr.push(element.id);
|
||||
}
|
||||
formValue.value.image_style_list = tmp_arr;
|
||||
await window.api.AddImageTask(toRaw(formValue.value), (value) => {
|
||||
debugger;
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
await getInitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对应的行的任务
|
||||
*/
|
||||
async function DeleteImageTaskList(row) {
|
||||
// 判断当前的状态
|
||||
if (row.status == "queue") {
|
||||
message.error("队列中的状态不能删除");
|
||||
return;
|
||||
}
|
||||
|
||||
if (row.status == "ok" || row.status.startsWith("video")) {
|
||||
dialog.warning({
|
||||
title: "确认",
|
||||
content: "确定删除?会将生成的图全部删除!",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: async () => {
|
||||
await window.api.DeleteImageTaskList(row.id, async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.DeleteImageTaskList(row.id, async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await window.api.GetGenerateTaskList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
task_list_data.value = value.data.task_list;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成全部
|
||||
*/
|
||||
async function GenerateAllImage() {
|
||||
debugger
|
||||
// 生成 对应的json 文件
|
||||
await window.api.AddWebuiJson(async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
await window.api.GenerateImageInSelectTask(toRaw(selectKey.value), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("生图任务添加到队列成功");
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新任务列表
|
||||
*/
|
||||
async function RefreshTaskList() {
|
||||
await getInitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示风格选择对话框
|
||||
*/
|
||||
async function ShowImageStyleDialog() {
|
||||
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.8;
|
||||
let dialogHeight = window.innerHeight * 0.9;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(SelectImageStyle, { selectStyle: toRaw(selectStyle.value), height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// 重新加载数据
|
||||
debugger;
|
||||
console.log(selectStyle.value);
|
||||
let tmp_arr = [];
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i];
|
||||
tmp_arr.push(element.id);
|
||||
}
|
||||
// 将当前的风格保存
|
||||
await window.api.SaveCopywritingInformation([JSON.stringify(tmp_arr), "image_style", true], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function StopTask() {
|
||||
await getInitData();
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, null, "default"], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功,但正在进行的任务无法停止");
|
||||
})
|
||||
}
|
||||
|
||||
async function RemoveImageTask(row) {
|
||||
//`${task_list.out_folder}_${images[0]}`, batch, task_list.out_folder
|
||||
// 停止当前行的生图任务
|
||||
debugger;
|
||||
console.log(row);
|
||||
await getInitData();
|
||||
// 判断该当前状态
|
||||
// if (row.status != "queue") {
|
||||
// message.error("只有在队列中的任务才能停止");
|
||||
// return;
|
||||
// }
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.SD_BACKSTEP_GENERATE_IMAGE, "all", row.out_folder], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功");
|
||||
// 修改状态
|
||||
await window.api.ModifyGenerateTaskStatus(['id', row.id, 'wait'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
await getInitData();
|
||||
})
|
||||
}
|
||||
|
||||
async function ShowAutoImage() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 800;
|
||||
let dialogHeight = 600;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(BatchSaveImageSetting, { selectStyle: toRaw(selectStyle.value), height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动保存图片的设置
|
||||
*/
|
||||
async function SaveImageSetting() {
|
||||
|
||||
// 获取当前权限
|
||||
await window.api.GetPermission(async (value) => {
|
||||
let permission = value.permissions;
|
||||
// let permission = [];
|
||||
if (permission && permission.length >= 0) {
|
||||
if (permission.indexOf(DEFINE_STRING.PERMISSIONS.AUTO_SAVE_IMAGE_PERMISSION) == -1) {
|
||||
message.error("没有权限");
|
||||
return;
|
||||
} else {
|
||||
await ShowAutoImage();
|
||||
}
|
||||
} else {
|
||||
await ShowAutoImage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
show,
|
||||
count,
|
||||
formValue,
|
||||
task_list_data,
|
||||
selectKey,
|
||||
AddImageTask,
|
||||
rowKey: (row) => row.id,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys;
|
||||
},
|
||||
GenerateAllImage,
|
||||
columns: createColumns({
|
||||
DeleteImageTaskList,
|
||||
RemoveImageTask
|
||||
}),
|
||||
RefreshTaskList,
|
||||
ShowImageStyleDialog,
|
||||
selectStyle,
|
||||
StopTask,
|
||||
SaveImageSetting
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<n-tabs default-value="reDraws" size="large" animated pane-wrapper-style="margin: 0 -4px"
|
||||
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;">
|
||||
<n-tab-pane name="reDraws" tab="重绘/单独合成">
|
||||
<oneImageReDrwa></oneImageReDrwa>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="autoAll" tab="一键全自动">
|
||||
<OneKeyMatrix></OneKeyMatrix>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, onUnmounted, h, toRaw } from "vue";
|
||||
import { NTabPane, NTabs } from "naive-ui"
|
||||
|
||||
import OneKeyMatrix from "../Backstep/OneKeyMatrix.vue"
|
||||
import oneImageReDrwa from "../Backstep/oneImageReDrwa.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NTabPane, NTabs, OneKeyMatrix, oneImageReDrwa
|
||||
},
|
||||
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,647 @@
|
||||
<template>
|
||||
<div style="margin-bottom: 30px;">
|
||||
<div>注意:</div>
|
||||
<div>这里加载的是当前<strong>项目地址</strong>下面的输出文件夹</div>
|
||||
</div>
|
||||
<n-form style="margin-top: 10px; margin-bottom: 30px;" :model="formValue">
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="选择srt地址">
|
||||
<n-input style="width: 300px; margin-right: 5px;" v-model:value="formValue.srt_path" type="text"
|
||||
placeholder="SRT字幕地址" />
|
||||
<n-button color="#e5a84b" @click="SelectSrtFile">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item style="margin-left: 10px;" label="选择配音">
|
||||
<n-input style="width: 300px; margin-right: 5px;" v-model:value="formValue.audio_path" type="text"
|
||||
placeholder="配音地址(mp3或wav)" />
|
||||
|
||||
<n-button color="#e5a84b" @click="SelectMusicFile">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
|
||||
<!-- <n-form-item style="flex: 1;" label="选择字幕样式">
|
||||
<n-select v-model:value="formValue.draft_srt_style" filterable placeholder="选择样式" :options="options" />
|
||||
</n-form-item> -->
|
||||
<n-form-item style="margin-left: 10px;" label="选择背景音乐文件夹(随机匹配,自动合成视频必选)">
|
||||
<n-select style="width: 300px;" v-model:value="formValue.background_music" filterable placeholder="选择样式"
|
||||
:options="backgroundMusicOptions" />
|
||||
</n-form-item>
|
||||
<!-- <n-form-item style="flex: 1; margin-left: 20px;" label="选择温馨提示(不要就选无,可以选择随机)">
|
||||
<n-select v-model:value="formValue.friendly_reminder" filterable placeholder="选择样式"
|
||||
:options="friendlyReminderOptions" />
|
||||
</n-form-item> -->
|
||||
|
||||
<n-form-item style="margin-left: 10px;">
|
||||
<n-button type="info" @click="SaveVideoSrtAndAudioMessage">保存</n-button>
|
||||
</n-form-item>
|
||||
</div>
|
||||
<n-form-item label="注意:高清不会管是不是已经高清过,都会再次高清">
|
||||
<n-button type="primary" @click="ExportAllDraft">一键导出所有草稿</n-button>
|
||||
<n-button style="margin-left: 40px;" type="primary" @click="HdAllImages">一键高清所有图片</n-button>
|
||||
<n-button style="margin-left: 40px;" type="primary" @click="VideoGenerate">一键生成所有视频</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-data-table :columns="columns" :data="data" :pagination="pagination" :bordered="false" />
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, onUnmounted, h, toRaw } from "vue";
|
||||
import { NButton, useMessage, NInput, NDataTable, NSelect, NForm, NFormItem, useDialog, NPopover, NTag, NTabPane, NTabs, NIcon } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import OneKeyMatrix from "../Backstep/OneKeyMatrix.vue"
|
||||
import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5'
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "重绘界面",
|
||||
key: 'reDraw'
|
||||
},
|
||||
{
|
||||
title: "生成草稿",
|
||||
key: "addClipDraft"
|
||||
},
|
||||
{
|
||||
title: "高清",
|
||||
key: "Improve"
|
||||
},
|
||||
{
|
||||
title: "生成视频",
|
||||
key: "GenerateVideo"
|
||||
},
|
||||
{
|
||||
title: "打开文件夹",
|
||||
key: "OpenFolder"
|
||||
}
|
||||
]
|
||||
|
||||
function statusAndMessage(row) {
|
||||
|
||||
if (!row.status.startsWith("video")) {
|
||||
if (row.status != "ok") {
|
||||
return {
|
||||
tagLabel: "生图未完成",
|
||||
color: { color: "#eccc68", textColor: '#ffffff' }
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
tagLabel: "生图完成(未高清)",
|
||||
color: { color: "#2ed573", textColor: '#ffffff' }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (row.status == "video_improvied") {
|
||||
return {
|
||||
tagLabel: "高清完成",
|
||||
color: { color: "#70a1ff", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_improving") {
|
||||
return {
|
||||
tagLabel: "高清中",
|
||||
color: { color: "#fedc5e", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_improvie_error") {
|
||||
return {
|
||||
tagLabel: "高清失败",
|
||||
color: { color: "#d23918", textColor: '#ffffff' }
|
||||
}
|
||||
}
|
||||
else if (row.status == "video_queue") {
|
||||
return {
|
||||
tagLabel: "合成队列中",
|
||||
color: { color: "#55efc4", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_error") {
|
||||
return {
|
||||
tagLabel: "合成失败",
|
||||
color: { color: "#ff4757", textColor: '#ffffff' }
|
||||
}
|
||||
} else if (row.status == "video_ok") {
|
||||
return {
|
||||
tagLabel: "合成完成",
|
||||
color: { color: "#1dd1a1", textColor: '#ffffff' }
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
tagLabel: "状态错误",
|
||||
color: { color: "#d2dae2", textColor: '#ffffff' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NInput, NSelect, NForm, NFormItem, NPopover, NTag, NTabPane, NTabs, OneKeyMatrix, NIcon, FolderOpen
|
||||
},
|
||||
setup() {
|
||||
let image_folder = ref("");
|
||||
let message = useMessage();
|
||||
let dialog = useDialog();
|
||||
let data = ref([]);
|
||||
let options = ref([]);
|
||||
let backgroundMusicOptions = ref([]);
|
||||
let friendlyReminderOptions = ref([]);
|
||||
|
||||
let formValue = ref({
|
||||
srt_path: null,
|
||||
audio_path: null,
|
||||
draft_srt_style: '0',
|
||||
background_music: null,
|
||||
friendly_reminder: '0'
|
||||
})
|
||||
|
||||
/**
|
||||
* 创建行
|
||||
* @param {执行的方法} param0
|
||||
*/
|
||||
const createColumns = ({
|
||||
showRegenerateWindow,
|
||||
AddClipDraft,
|
||||
ImproveResolution,
|
||||
GenerateVideo,
|
||||
OpenFolder
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
title: "文件夹",
|
||||
key: "folder",
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "options",
|
||||
width: 600,
|
||||
render(row) {
|
||||
const btn = buttonArr.map(item => {
|
||||
return h(
|
||||
NButton, {
|
||||
style: "margin-right : 10px",
|
||||
strong: true,
|
||||
type: "info",
|
||||
size: "medium",
|
||||
onClick: () => {
|
||||
if (item.key == "reDraw") {
|
||||
showRegenerateWindow(row);
|
||||
} else if (item.key == "addClipDraft") {
|
||||
AddClipDraft(row);
|
||||
} else if (item.key == "Improve") {
|
||||
// 高清
|
||||
ImproveResolution(row);
|
||||
} else if (item.key == "GenerateVideo") {
|
||||
// 生成视频
|
||||
GenerateVideo(row);
|
||||
} else if (item.key == "OpenFolder") {
|
||||
// 打开对应的文件夹
|
||||
OpenFolder(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
btn.push(h(
|
||||
NButton, {
|
||||
style: "margin-right : 10px",
|
||||
strong: true,
|
||||
type: "warning",
|
||||
size: "medium",
|
||||
onClick: async () => {
|
||||
await StopTask(row);
|
||||
}
|
||||
}, {
|
||||
default: () => "停止任务"
|
||||
}
|
||||
))
|
||||
return btn;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
render(row) {
|
||||
return h(NPopover, {
|
||||
trigger: 'hover',
|
||||
disabled: (() => {
|
||||
if (row.status == "video_error") {
|
||||
return false;
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})()
|
||||
},
|
||||
{
|
||||
default: () => (() => {
|
||||
if (row.status == "wait") {
|
||||
return "warning"
|
||||
} else if (row.status == "queue") {
|
||||
return "info"
|
||||
} else if (row.status == "ok") {
|
||||
return "success"
|
||||
} else if (row.status == "error") {
|
||||
return row.errorMessage
|
||||
}
|
||||
})(),
|
||||
trigger: () => h(
|
||||
NTag,
|
||||
{
|
||||
color: (() => statusAndMessage(row))().color
|
||||
},
|
||||
{
|
||||
default: (() => statusAndMessage(row).tagLabel)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止当前行的任务
|
||||
*/
|
||||
async function StopTask(row) {
|
||||
// 判断当前的状态是不是高清中和生成视频中
|
||||
|
||||
if (row.status == "video_queue") {
|
||||
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.AUTO_VIDEO_GENERATE_SINGLE, row.folder, "default"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功");
|
||||
// 修改状态为 高清完成
|
||||
// 修改状态
|
||||
await window.api.ModifyGenerateTaskStatus(['out_folder', row.folder, 'video_improvied'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
} else if (row.status == "video_improving") {
|
||||
await window.api.DeleteBackTask([DEFINE_STRING.QUEUE_BATCH.LOCAL_IMAGE_IMPROVE, row.folder, "default"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("停止成功");
|
||||
// 修改状态
|
||||
await window.api.ModifyGenerateTaskStatus(['out_folder', row.folder, 'ok'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
message.error("当前状态不是高清中或者生成视频中,无法停止");
|
||||
}
|
||||
|
||||
await InitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 高清文件夹,只是加入队列
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function ImproveResolution(row) {
|
||||
await window.api.ImproveResolution([row.folder], (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("高清任务加入队列成功,请勿关闭软件");
|
||||
} else {
|
||||
message.error(`Error Message ${value.message}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将生成视频加入到队列中(目前一次最多三个)
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function GenerateVideo(row) {
|
||||
let validateRes = await validateMustProperty();
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
await window.api.AutoGenerateVideo([[row.folder], toRaw(formValue.value)], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(row.folder + "添加到队列成功");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键生成所有的视频(加入队列中)
|
||||
*/
|
||||
async function VideoGenerate() {
|
||||
let validateRes = await validateMustProperty();
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
let f = [];
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
f.push(item.folder);
|
||||
}
|
||||
|
||||
// await GenerateVideo(toRaw(item));
|
||||
await window.api.AutoGenerateVideo([[...f], toRaw(formValue.value)], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("所有任务添加到队列成功");
|
||||
})
|
||||
}
|
||||
|
||||
function OpenFolder(row) {
|
||||
window.api.OpenFolder(row.folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 高清所有文件夹,将所有需要高清的全部加入队列中
|
||||
*/
|
||||
async function HdAllImages() {
|
||||
dialog.warning({
|
||||
title: "确认是不是要全部高清",
|
||||
content: "确认是不是全部高清!分辨率太高,电脑会卡到爆的哦",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: async () => {
|
||||
let arr = [];
|
||||
for (let item of data.value) {
|
||||
arr.push(item.folder); // 这里应该可以正常访问和操作每个元素
|
||||
}
|
||||
|
||||
// 一次高清五个
|
||||
await window.api.ImproveResolution(arr, (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 1) {
|
||||
message.success("所有的高清任务加入队列成功,请勿关闭软件");
|
||||
} else {
|
||||
message.error(`Error Message ${value.message}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新的窗口
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function showRegenerateWindow(row) {
|
||||
window.api.showNewWindow(["ReDrawImage", window.config.project_path + "/tmp/" + row.folder]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证当前表单的必填项
|
||||
*/
|
||||
async function validateMustProperty(isBackgroundMusic = true) {
|
||||
let validateData = toRaw(formValue.value);
|
||||
if (validateData.srt_path == null || validateData.srt_path == "") {
|
||||
message.error("SRT地址必填");
|
||||
return false;
|
||||
}
|
||||
if (validateData.audio_path == null || validateData.audio_path == "") {
|
||||
message.error("配音地址必填");
|
||||
return false;
|
||||
}
|
||||
if (validateData.draft_srt_style == null || validateData.draft_srt_style == "") {
|
||||
message.error("草稿样式必选");
|
||||
return false;
|
||||
}
|
||||
if (isBackgroundMusic) {
|
||||
if (validateData.background_music == null || validateData.background_music == "") {
|
||||
message.error("背景音乐必填");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (validateData.friendly_reminder == null || validateData.friendly_reminder == "") {
|
||||
message.error("友情提示必填");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成剪映草稿
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function AddClipDraft(row) {
|
||||
let validateRes = await validateMustProperty(false);
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
await window.api.addDraft([row.folder, toRaw(formValue.value)], (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
} else {
|
||||
message.success(`添加成功,草稿名字:${value.draft_name}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键保存所有的草稿
|
||||
*/
|
||||
async function ExportAllDraft() {
|
||||
let validateRes = await validateMustProperty(false);
|
||||
if (!validateRes) {
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
await window.api.addDraft([element.folder, toRaw(formValue.value)], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
} else {
|
||||
message.success(`添加成功,草稿名字:${value.draft_name}`);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectSrtFile() {
|
||||
window.api.SelectFile(['srt'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
formValue.value.srt_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对应的字幕文件
|
||||
*/
|
||||
async function SelectMusicFile() {
|
||||
window.api.SelectFile(['mp3', 'wav'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
formValue.value.audio_path = value.value;
|
||||
});
|
||||
}
|
||||
|
||||
async function InitData() {
|
||||
// 获取对应的输出文件
|
||||
await window.api.getSubFolderList(["start", "output_crop"], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
data.value = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取对应的输出文件
|
||||
await InitData();
|
||||
|
||||
// // 初始化获取字体样式
|
||||
// await window.api.getTextStyleList((value) => {
|
||||
// if (value.code == 0) {
|
||||
// message.error(value.message);
|
||||
// return;
|
||||
// }
|
||||
// for (let i = 0; i < value.value.length; i++) {
|
||||
// const item = value.value[i];
|
||||
// let obj = {
|
||||
// label: item.name,
|
||||
// value: item.id,
|
||||
// }
|
||||
// options.value.push(obj);
|
||||
// }
|
||||
// // 设置默认选项
|
||||
// if (options.value.length > 0) {
|
||||
// formValue.value.draft_srt_style = options.value.filter(item => item.value == "1")[0].value;
|
||||
// }
|
||||
// })
|
||||
|
||||
// 获取初始化的背景音乐列表
|
||||
await window.api.GetBackgroundMusicConfigList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < value.value.length; i++) {
|
||||
const element = value.value[i];
|
||||
let obj = {
|
||||
label: element.name,
|
||||
value: element.id,
|
||||
}
|
||||
backgroundMusicOptions.value.push(obj);
|
||||
}
|
||||
})
|
||||
|
||||
// 获取温馨提示列表
|
||||
// await window.api.getFriendlyReminderList((value) => {
|
||||
// if (value.code == 0) {
|
||||
// message.error(value.message);
|
||||
// return;
|
||||
// }
|
||||
// value.value.forEach(item => {
|
||||
// let obj = {
|
||||
// label: item.name,
|
||||
// value: item.id
|
||||
// };
|
||||
// friendlyReminderOptions.value.push(obj);
|
||||
// });
|
||||
// // 设置默认选项
|
||||
// if (friendlyReminderOptions.value.length > 0) {
|
||||
// formValue.value.friendly_reminder = friendlyReminderOptions.value.filter(item => item.value == "1")[0].value;
|
||||
// }
|
||||
// })
|
||||
|
||||
// 监听状态更新事件
|
||||
window.api.setEventListen([DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
// 修改当前状态
|
||||
let index = data.value.findIndex(item => item.folder == value.out_folder);
|
||||
if (index < 0) {
|
||||
message.error("数据错误");
|
||||
return;
|
||||
}
|
||||
// 获取指定的数据进行状态更新
|
||||
data.value[index].status = value.status;
|
||||
})
|
||||
|
||||
// 获取设置的配置
|
||||
// 获取上一次选择的字幕,配音。未年检
|
||||
await window.api.GetVideoGenerateConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("获取初始化数据失败");
|
||||
return;
|
||||
}
|
||||
formValue.value.srt_path = value.data.srt_path;
|
||||
formValue.value.audio_path = value.data.audio_path;
|
||||
formValue.value.background_music = value.data.background_music;
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.VIDEO_GENERATE_STATUS_REFRESH);
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存视频的基础信息
|
||||
*/
|
||||
async function SaveVideoSrtAndAudioMessage() {
|
||||
await window.api.SaveVideoSrtAndAudioMessage(JSON.stringify(toRaw(formValue.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
image_folder,
|
||||
columns: createColumns({
|
||||
showRegenerateWindow,
|
||||
AddClipDraft,
|
||||
ImproveResolution,
|
||||
GenerateVideo,
|
||||
OpenFolder
|
||||
}),
|
||||
data,
|
||||
options,
|
||||
backgroundMusicOptions,
|
||||
friendlyReminderOptions,
|
||||
formValue,
|
||||
SelectSrtFile,
|
||||
SelectMusicFile,
|
||||
HdAllImages,
|
||||
ExportAllDraft,
|
||||
VideoGenerate,
|
||||
SaveVideoSrtAndAudioMessage
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-button @click="addDraft">添加草稿 </n-button>
|
||||
<n-button @click="downloadModel">下载模型 </n-button>
|
||||
<n-code style="padding: 0;" :code="code" language="js" word-wrap />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { NButton, useMessage, NSpin, NSpace, NSelect, NDivider, NInput, NCode } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NSpace, NSelect, NDivider, NInput, NCode
|
||||
},
|
||||
setup() {
|
||||
|
||||
let code = ref("");
|
||||
async function addDraft() {
|
||||
await window.api.addDraft(['D:\\AI 推文\\番茄小说\\如画室友\\03\\tmp\\output_crop', "D:\\AI 推文\\番茄小说\\如画室友\\03\\1705237224836_gbyfbbGz.srt"], (value) => {
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.api.setEventListen([DEFINE_STRING.GET_FRAME_RETUN], (value) => {
|
||||
debugger;
|
||||
code.value = code.value + "\n" + value.data;
|
||||
})
|
||||
})
|
||||
|
||||
async function downloadModel() {
|
||||
window.api.DownloadModel();
|
||||
}
|
||||
|
||||
return {
|
||||
addDraft,
|
||||
downloadModel,
|
||||
code
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
正在对齐
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<n-select v-model:value="selectedValue" filterable placeholder="选择草稿" :options="options" />
|
||||
<n-button round type="primary" @click="AlginDraftImg">开始对齐(草稿内单条对齐)</n-button>
|
||||
<n-button round type="primary" @click="AlginDraftImgToText">多行对齐(提供内容)</n-button>
|
||||
<n-button round type="primary" @click="AlginDraftImgToTextAll">一键对齐</n-button>
|
||||
</n-space>
|
||||
</n-spin>
|
||||
<n-divider />
|
||||
<!-- <pre>{{ items }}</pre> -->
|
||||
<n-input v-model:value="inputValue" type="textarea" placeholder="自动调整尺寸" :autosize="{
|
||||
minRows: 3
|
||||
}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { NButton, useMessage, NSpin, NSpace, NSelect, NDivider, NInput } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NSpin, NSpace, NSelect, NDivider, NInput
|
||||
},
|
||||
setup() {
|
||||
let show = ref(false);
|
||||
let options = ref([]);
|
||||
let items = ref([]);
|
||||
let selectedValue = ref(null);
|
||||
let message = useMessage()
|
||||
let inputValue = ref("")
|
||||
|
||||
// 单句对齐
|
||||
function AlginDraftImg() {
|
||||
if (selectedValue.value == null || selectedValue.value == "") {
|
||||
message.error("请选择需要对齐的草稿");
|
||||
return;
|
||||
}
|
||||
show.value = true;
|
||||
window.api.alginDraftImg(selectedValue.value, (type, value) => {
|
||||
items.value.push(value)
|
||||
console.log(value)
|
||||
if (type == DEFINE_STRING.PYTHON_CLOSE) {
|
||||
show.value = false;
|
||||
message.success("字幕对齐成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照内容对齐
|
||||
*/
|
||||
function AlginDraftImgToText() {
|
||||
if (selectedValue.value == null || selectedValue.value == "" || inputValue.value == null || inputValue.value == "") {
|
||||
message.error("请选择需要对齐的草稿和输入分镜内容");
|
||||
return;
|
||||
}
|
||||
// 文案内容
|
||||
let text_value = inputValue.value.split("\n")
|
||||
window.api.alginDraftImgToText([selectedValue.value, text_value], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("对齐成功");
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键全部对齐
|
||||
*/
|
||||
function AlginDraftImgToTextAll() {
|
||||
if (inputValue.value == null || inputValue.value == "") {
|
||||
message.error("请输入分镜内容");
|
||||
return;
|
||||
}
|
||||
debugger;
|
||||
// 文案内容
|
||||
let text_value = inputValue.value.split("\n")
|
||||
// 获取当前项目下面地所有草稿
|
||||
console.log(window);
|
||||
let draft_value = options.value.filter(item => item.value.startsWith(window.config.project_name));
|
||||
for (let i = 0; i < draft_value.length; i++) {
|
||||
const element = draft_value[i];
|
||||
window.api.alginDraftImgToText([element.value, text_value], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(element.value + "对齐成功");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.api.getDraftFileList((value) => {
|
||||
value.forEach(element => {
|
||||
let obj = {
|
||||
label: element,
|
||||
value: element
|
||||
}
|
||||
options.value.push(obj)
|
||||
});
|
||||
})
|
||||
})
|
||||
return {
|
||||
show,
|
||||
options,
|
||||
selectedValue,
|
||||
AlginDraftImg,
|
||||
AlginDraftImgToText,
|
||||
AlginDraftImgToTextAll,
|
||||
items,
|
||||
inputValue
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div style="height: 200px;">
|
||||
<div style="height: 40px;"></div>
|
||||
<div style="display: flex;">
|
||||
<n-input :disabled="true" v-model:value="machineId" type="text" placeholder="唯一码" />
|
||||
<n-popover trigger="click">
|
||||
<template #trigger>
|
||||
<n-button text style="width: 24px; font-size: 24px; color: skyblue; display: flex; heigh">
|
||||
<n-icon>
|
||||
<InformationCircle />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<span>VX:xiangbie88</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
<div style="color: red; margin: 10px">如未激活,将上面的码给管理员激活
|
||||
<n-popover trigger="hover" raw :show-arrow="false">
|
||||
<template #trigger>
|
||||
<n-tag type="info">
|
||||
客服微信
|
||||
</n-tag>
|
||||
</template>
|
||||
<n-image width="250" src="http://qiniuyun.upsurging.xyz/LAI/a41237ef53a18b6c7fba5d966e9a28f.jpg"
|
||||
preview-disabled />
|
||||
</n-popover>
|
||||
</div>
|
||||
<div style="margin: 10px;">详细的教程文档:<span class="url_class" @click="OpenTeachDoc">教程文档</span></div>
|
||||
<div style="margin: 10px;">问题/需求收集表:<span class="url_class" @click="OpenQueDoc">问题/需求收集表</span></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { useMessage, NInput, NButton, NIcon, NPopover, NImage, NTag } from "naive-ui"
|
||||
import { InformationCircle } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NInput, NButton, NIcon, InformationCircle, NPopover, NImage, NTag
|
||||
},
|
||||
setup() {
|
||||
let machineId = ref("");
|
||||
let message = useMessage();
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取机械码
|
||||
await window.api.GetMachineId((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
machineId.value = value.value;
|
||||
})
|
||||
})
|
||||
|
||||
function OpenTeachDoc() {
|
||||
window.api.OpenUrl("https://pvwu1oahp5m.feishu.cn/docx/CAjGdTDlboJ3nVx0cQccOuNHnvd?from=from_copylink");
|
||||
}
|
||||
|
||||
function OpenQueDoc() {
|
||||
window.api.OpenUrl("https://pvwu1oahp5m.feishu.cn/sheets/G5s3sX0KahH1XQtWDazcF70anUb?from=from_copylink");
|
||||
}
|
||||
return {
|
||||
machineId,
|
||||
OpenQueDoc,
|
||||
OpenTeachDoc
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.url_class {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.url_class:hover {
|
||||
color: brown;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,461 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<div style="flex: 5; margin-right: 20px">
|
||||
<n-form ref="formRef" :model="prompt" :rules="rules" :size="size">
|
||||
<n-form-item label="提示词" path="prompt">
|
||||
<n-input type="textarea" v-model:value="prompt.prompt" placeholder="提示词" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-form
|
||||
ref="formRef"
|
||||
inline
|
||||
:label-width="80"
|
||||
:model="prompt"
|
||||
:rules="rules"
|
||||
:size="size"
|
||||
>
|
||||
<n-form-item label="重绘幅度" path="denoising_strength">
|
||||
<n-input-number
|
||||
:step="0.05"
|
||||
:precision="2"
|
||||
v-model:value="prompt.denoising_strength"
|
||||
placeholder="重绘幅度"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="种子" path="seed">
|
||||
<n-input v-model:value="prompt.seed" placeholder="种子" />
|
||||
</n-form-item>
|
||||
<n-form-item label="生图模式" path="model">
|
||||
<n-select
|
||||
style="width: 150px"
|
||||
v-model:value="prompt.model"
|
||||
:options="modelOption"
|
||||
placeholder="种子"
|
||||
/>
|
||||
<n-checkbox
|
||||
style="margin-left: 20px"
|
||||
v-model:checked="prompt.adetailer"
|
||||
label="修脸"
|
||||
></n-checkbox>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="GenerateAll">全部重绘</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="图片显示大小">
|
||||
<n-slider
|
||||
style="width: 120px"
|
||||
:min="150"
|
||||
:max="300"
|
||||
v-model:value="image_width"
|
||||
:step="10"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
<div style="flex: 1; width: 150px; height: 150px">
|
||||
<n-image style="width: 150px; height: 150px" :src="prompt.oldImage" />
|
||||
<div style="display: flex; justify-content: left; align-items: center">
|
||||
<span style="font-size: 18px">{{ prompt.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-container" style="display: flex">
|
||||
<n-image-group show-toolbar-tooltip :theme-overrides="imageGroupThemeOverrides">
|
||||
<div style="display: flex; flex-wrap: wrap">
|
||||
<div
|
||||
v-for="(image, index) in images"
|
||||
style="margin: 0 10px 5px 10px"
|
||||
:key="image.id"
|
||||
>
|
||||
<n-image
|
||||
@contextmenu.prevent="handleContextMenu"
|
||||
:width="image_width"
|
||||
:height="image_height"
|
||||
:src="image.img_path"
|
||||
alt="图片描述"
|
||||
lazy
|
||||
style="display: block"
|
||||
/>
|
||||
<n-button
|
||||
:disabled="image.disabled"
|
||||
style="width: 100%"
|
||||
type="info"
|
||||
@click="ReGenerateImage(image)"
|
||||
>重绘</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-image-group>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="margin-top: 10px; display: flex; justify-content: center; align-items: center"
|
||||
>
|
||||
<n-button type="error" style="margin-right: 20px" @click="GetLast"
|
||||
>上一组(F1)</n-button
|
||||
>
|
||||
<n-button type="info" @click="GetNext">下一组(F2)</n-button>
|
||||
</div>
|
||||
|
||||
<n-dropdown
|
||||
placement="bottom-start"
|
||||
trigger="manual"
|
||||
:x="x"
|
||||
:y="y"
|
||||
:options="dropOption"
|
||||
:show="showDropdownRef"
|
||||
:on-clickoutside="onClickoutside"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
onMounted,
|
||||
watch,
|
||||
computed,
|
||||
onUnmounted,
|
||||
nextTick,
|
||||
toRaw,
|
||||
} from "vue";
|
||||
import {
|
||||
NImage,
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
useMessage,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NCheckbox,
|
||||
useThemeVars,
|
||||
NImageGroup,
|
||||
NSlider,
|
||||
NDropdown,
|
||||
} from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NSelect,
|
||||
NCheckbox,
|
||||
NImageGroup,
|
||||
NSlider,
|
||||
NDropdown,
|
||||
},
|
||||
props: {
|
||||
ImageInitData: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
debugger;
|
||||
let images = ref(
|
||||
window.ImageInitData.filter((item) => item.id == window.currentImageId)[0]
|
||||
.output_list
|
||||
);
|
||||
let prompt = ref(
|
||||
window.ImageInitData.filter((item) => item.id == window.currentImageId)[0]
|
||||
);
|
||||
const showDropdownRef = ref(false);
|
||||
const x = ref(0);
|
||||
const y = ref(0);
|
||||
let current_img = ref("");
|
||||
|
||||
let modelOption = ref([
|
||||
{
|
||||
label: "文生图",
|
||||
value: "txt2img",
|
||||
},
|
||||
{
|
||||
label: "图生图",
|
||||
value: "img2img",
|
||||
},
|
||||
]);
|
||||
|
||||
let dropOption = [
|
||||
{
|
||||
label: "保存到封面",
|
||||
key: "savaCover",
|
||||
},
|
||||
{
|
||||
label: "替换参考图",
|
||||
key: "replaceImage",
|
||||
},
|
||||
];
|
||||
|
||||
let image_width = ref(150);
|
||||
let image_height = ref(150 * (prompt.value.height / prompt.value.width));
|
||||
|
||||
// 监听myRef的变化
|
||||
const stopWatch = watch(image_width, (newValue, oldValue) => {
|
||||
image_height.value = newValue * (prompt.value.height / prompt.value.width);
|
||||
// 根据myRef的新值执行操作...
|
||||
});
|
||||
|
||||
function handleFunc(event) {
|
||||
if (event.key == "F1") {
|
||||
// 数字 112 是 F1 键的 keyCode
|
||||
GetLast();
|
||||
// 阻止默认操作
|
||||
event.preventDefault();
|
||||
// 这里调用您的函数
|
||||
} else if (event.key == "F2") {
|
||||
GetNext();
|
||||
// 阻止默认操作
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 重新计算 images,判断是不是disabled
|
||||
for (let i = 0; i < images.value.length; i++) {
|
||||
const element = images.value[i];
|
||||
if (
|
||||
prompt.value.oldImage.replaceAll("\\", "/") ==
|
||||
element.img_path.replaceAll("\\", "/")
|
||||
) {
|
||||
images.value[i].disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.isWatch) {
|
||||
window.api.setEventListen(
|
||||
[DEFINE_STRING.REGENERATE_IMAGE_RETUN, window.id],
|
||||
(value) => {
|
||||
if (value.type != 1) {
|
||||
return;
|
||||
}
|
||||
// 修改图片的数据源
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
let index = window.ImageInitData.findIndex(
|
||||
(item) => item.id == window.currentImageId
|
||||
);
|
||||
images.value = window.ImageInitData[index].output_list;
|
||||
let filteArr = images.value.filter((item) => item.id == value.id);
|
||||
filteArr[0].img_path =
|
||||
filteArr[0].img_path.split("?")[0] + `?time=${new Date().getTime()}`;
|
||||
message.success(JSON.stringify(value));
|
||||
}
|
||||
);
|
||||
window.isWatch = true;
|
||||
}
|
||||
|
||||
// 监听事件
|
||||
window.addEventListener("keydown", handleFunc);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("keydown", handleFunc);
|
||||
stopWatch();
|
||||
});
|
||||
|
||||
/**
|
||||
* 重绘图片
|
||||
*/
|
||||
async function ReGenerateImage(data) {
|
||||
debugger;
|
||||
console.log(data);
|
||||
let obj = {
|
||||
id: data.id,
|
||||
denoising_strength: prompt.value.denoising_strength,
|
||||
image: data.img_path.split("?")[0],
|
||||
name: prompt.value.name,
|
||||
negative_prompt: prompt.value.negative_prompt,
|
||||
prompt: prompt.value.prompt,
|
||||
sampler_name: prompt.value.sampler_name,
|
||||
seed: -1,
|
||||
timedata: new Date().getTime(),
|
||||
image_style: data.image_style,
|
||||
lora: data.lora,
|
||||
image_style_list: toRaw(data.image_style_list),
|
||||
model: prompt.value.model,
|
||||
adetailer: prompt.value.adetailer,
|
||||
};
|
||||
// 开始重新生成
|
||||
await window.api.ReGenerateImage([window.id, obj, 1], (value) => {
|
||||
console.log(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键全部重绘
|
||||
*/
|
||||
async function GenerateAll() {
|
||||
for (let i = 0; i < images.value.length; i++) {
|
||||
const element = images.value[i];
|
||||
await ReGenerateImage(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上一组
|
||||
*/
|
||||
function GetLast() {
|
||||
debugger;
|
||||
let index = window.ImageInitData.findIndex(
|
||||
(item) => item.id == window.currentImageId
|
||||
);
|
||||
if (index > 0) {
|
||||
images.value = window.ImageInitData[index - 1].output_list;
|
||||
prompt.value = window.ImageInitData[index - 1];
|
||||
window.currentImageId = prompt.value.id;
|
||||
} else {
|
||||
message.error("没有上一组了");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一组
|
||||
*/
|
||||
function GetNext() {
|
||||
debugger;
|
||||
let index = window.ImageInitData.findIndex(
|
||||
(item) => item.id === window.currentImageId
|
||||
);
|
||||
if (index + 1 >= window.ImageInitData.length) {
|
||||
// 已经是最后一个元素
|
||||
message.error("当前已经是最后一个了");
|
||||
} else {
|
||||
// 返回下一个对象
|
||||
images.value = window.ImageInitData[index + 1].output_list;
|
||||
prompt.value = window.ImageInitData[index + 1];
|
||||
window.currentImageId = prompt.value.id;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
images,
|
||||
ReGenerateImage,
|
||||
GetLast,
|
||||
GetNext,
|
||||
prompt,
|
||||
modelOption,
|
||||
current_img,
|
||||
GenerateAll,
|
||||
image_width,
|
||||
image_height,
|
||||
imageGroupThemeOverrides: computed(() => {
|
||||
const {
|
||||
popoverColor,
|
||||
boxShadow2,
|
||||
textColor2,
|
||||
borderRadius,
|
||||
} = useThemeVars().value;
|
||||
const themeOverrides = {
|
||||
toolbarColor: popoverColor,
|
||||
toolbarBoxShadow: boxShadow2,
|
||||
toolbarIconColor: textColor2,
|
||||
toolbarBorderRadius: borderRadius,
|
||||
};
|
||||
return themeOverrides;
|
||||
}),
|
||||
handleContextMenu(e) {
|
||||
e.preventDefault();
|
||||
let img_src = decodeURI(e.target.src);
|
||||
if (img_src) {
|
||||
current_img.value = img_src
|
||||
.split("?")[0]
|
||||
.replace(/^file:\/\/\//, "")
|
||||
.replace(/^\//, "");
|
||||
nextTick().then(() => {
|
||||
showDropdownRef.value = true;
|
||||
x.value = e.clientX;
|
||||
y.value = e.clientY;
|
||||
});
|
||||
} else {
|
||||
message.error("文件路径错误");
|
||||
return;
|
||||
}
|
||||
},
|
||||
x: x,
|
||||
y: y,
|
||||
dropOption,
|
||||
showDropdownRef,
|
||||
onClickoutside() {
|
||||
showDropdownRef.value = false;
|
||||
},
|
||||
handleSelect(key) {
|
||||
showDropdownRef.value = false;
|
||||
if (key == "savaCover") {
|
||||
// 将指定的文件写入写入到封面中
|
||||
window.api.DownloadImageFile(
|
||||
[toRaw(current_img.value), "cover", window.id],
|
||||
(value) => {
|
||||
if (value.code == 1) {
|
||||
message.success(toRaw(current_img.value) + "保存封面成功");
|
||||
return;
|
||||
}
|
||||
message.error("保存失败:" + value.message);
|
||||
}
|
||||
);
|
||||
} else if (key == "replaceImage") {
|
||||
// 替换参考图
|
||||
// 先替换界面。然后修改配置文件
|
||||
prompt.value.old_old_image = prompt.value.oldImage;
|
||||
prompt.value.oldImage = current_img.value;
|
||||
// 修改配置文件
|
||||
let arg = {
|
||||
name: prompt.value.name,
|
||||
prompt: prompt.value.prompt,
|
||||
denoising_strength: prompt.value.denoising_strength,
|
||||
model: prompt.value.model,
|
||||
adetailer: prompt.value.adetailer,
|
||||
modify_old: prompt.value.oldImage,
|
||||
};
|
||||
window.api.ModifyInpurCropJson(arg, (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("替换参考图成功");
|
||||
// 修改image中的disable
|
||||
for (let i = 0; i < images.value.length; i++) {
|
||||
const element = images.value[i];
|
||||
if (
|
||||
prompt.value.oldImage.replaceAll("\\", "/") ==
|
||||
element.img_path.replaceAll("\\", "/")
|
||||
) {
|
||||
images.value[i].disabled = true;
|
||||
} else {
|
||||
images.value[i].disabled = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.image {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image img {
|
||||
/* 固定高度 */
|
||||
margin: 10px 15px 5px 15px;
|
||||
/* 间隔 */
|
||||
}
|
||||
|
||||
/* .btn {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
} */
|
||||
</style>
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px;">
|
||||
<div style="color: red;">注意:删除所有是删除当前查找到的(就是下面显示的tag)</div>
|
||||
<div style="margin-top: 5px; display: flex; margin-bottom: 10px;">
|
||||
<n-input v-model:value="inputTag" placeholder="输入不想要的标签,多个以英文逗号隔开"></n-input>
|
||||
<n-button style="width: 60px;margin-left: 5px;" type="info" @click="AddTags">添加</n-button>
|
||||
</div>
|
||||
<div style="margin-top: 5px; display: flex; margin-bottom: 10px;">
|
||||
<n-input v-model:value="searchTag" placeholder="输入搜索的值,多个以英文逗号隔开"></n-input>
|
||||
<n-button style="width: 60px;margin-left: 5px;" type="info" @click="SearchTags">搜索</n-button>
|
||||
<n-button style="width: 100px;margin-left: 5px;" type="info" @click="DeleteTags">删除所有</n-button>
|
||||
</div>
|
||||
<n-tag style="margin: 5px;" v-for="(tag, index) in show_tags" :key="index" type="success" closable
|
||||
@close="handleClose(tag)">{{ tag }}</n-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, h, toRaw } from "vue";
|
||||
import { NButton, useMessage, NInput, NTag } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NInput, NTag
|
||||
},
|
||||
setup() {
|
||||
let inputTag = ref("");
|
||||
let searchTag = ref("");
|
||||
let show_tags = ref([]);
|
||||
let tags = ref([]);
|
||||
let message = useMessage();
|
||||
|
||||
onMounted(async () => {
|
||||
// 初始化获取提示词
|
||||
window.api.GetBadPrompt((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.value != null) {
|
||||
tags.value = value.value.split(',');
|
||||
show_tags.value = value.value.split(',');
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 添加标签
|
||||
*/
|
||||
async function AddTags() {
|
||||
if (inputTag.value == null || inputTag.value == "") {
|
||||
message.error("请输入内容");
|
||||
return;
|
||||
}
|
||||
let newTag = inputTag.value.replaceAll(',', ',').split(',');
|
||||
let mergeTag = [...newTag, ...toRaw(tags.value)];
|
||||
tags.value = [...new Set(mergeTag)]
|
||||
show_tags.value = [...new Set(mergeTag)]
|
||||
inputTag.value = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async function handleClose(tag) {
|
||||
debugger;
|
||||
// 删除对应的数据
|
||||
tags.value = tags.value.filter(item => item != tag);
|
||||
show_tags.value = show_tags.value.filter(item => item != tag);
|
||||
await SearchTags();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查找筛选值(返回符合条件的tag)
|
||||
*/
|
||||
async function SearchTags() {
|
||||
if (searchTag.value == null || searchTag.value == "") {
|
||||
show_tags.value = tags.value;
|
||||
return;
|
||||
}
|
||||
let s_ts = searchTag.value.replaceAll(',', ',').split(',');
|
||||
let temp_tag_list = [];
|
||||
for (let i = 0; i < s_ts.length; i++) {
|
||||
const element = s_ts[i];
|
||||
let t = show_tags.value.filter(item => item.includes(element));
|
||||
let t2 = [...temp_tag_list, ...t];
|
||||
temp_tag_list = [...new Set(t2)]
|
||||
}
|
||||
show_tags.value = temp_tag_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所选的tag(全部)
|
||||
*/
|
||||
async function DeleteTags() {
|
||||
debugger
|
||||
let d_l = toRaw(show_tags.value);
|
||||
for (let i = 0; i < d_l.length; i++) {
|
||||
const element = d_l[i];
|
||||
// tags.value = tags.value.filter(item => item != element);
|
||||
// show_tags.value = show_tags.value.filter(item => item != element);
|
||||
await handleClose(element)
|
||||
}
|
||||
// await SearchTags();
|
||||
}
|
||||
|
||||
return {
|
||||
tags,
|
||||
inputTag,
|
||||
AddTags,
|
||||
handleClose,
|
||||
SearchTags,
|
||||
DeleteTags,
|
||||
show_tags,
|
||||
searchTag
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div style="position: relative;">
|
||||
<n-input placeholder="新加tag,多个用逗号隔开" size="tiny" @keyup="AddTag" v-model:value="input_data"></n-input>
|
||||
|
||||
<span v-for="(tag, index) in data.chinese_prompt">
|
||||
<n-tag @click="TagClickEdit(tag, index)" size="small" :key="index" type="success" closable
|
||||
@close="handleClose(tag)">
|
||||
<template #default>
|
||||
<n-input v-if="tag.edit" size="tiny" @keyup="ModifyTag($event, tag, index)" style="width: 70px;"
|
||||
v-model:value="tag.dst">{{ tag.dst }}</n-input>
|
||||
<span
|
||||
style="max-width: 130px; min-width: 10px; display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
|
||||
:title="tag.dst" v-else>{{ tag.dst }}</span>
|
||||
</template>
|
||||
</n-tag>
|
||||
<n-button size="tiny" type="info" quaternary @click="AddTagInPosition(index)">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<add />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { h, defineComponent, onMounted, ref, toRaw, watch, onUnmounted } from "vue";
|
||||
import { NButton, NDataTable, useMessage, NInput, useDialog, NTag, NIcon } from "naive-ui";
|
||||
import { isEmpty } from "lodash";
|
||||
import { Add } from "@vicons/ionicons5"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NInput, NTag, Add, NIcon
|
||||
},
|
||||
props: ["initData", "type", "translateMode"],
|
||||
setup(props) {
|
||||
const message = useMessage();
|
||||
let input_data = ref("");
|
||||
let data = ref(props.initData);
|
||||
let type = ref(props.type);
|
||||
let translateMode = ref(props.translateMode);
|
||||
|
||||
|
||||
// 修改prompt
|
||||
async function ModifyPrompt() {
|
||||
// 修改prompt
|
||||
let res = "";
|
||||
for (let i = 0; i < data.value.chinese_prompt.length; i++) {
|
||||
const element = data.value.chinese_prompt[i];
|
||||
res += element.src + ',';
|
||||
}
|
||||
data.value.prompt = res;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
console.log(123)
|
||||
// 处理数据
|
||||
});
|
||||
|
||||
/**
|
||||
* 添加 Tag 的 方法
|
||||
* @param {*} e
|
||||
*/
|
||||
async function AddTag(e) {
|
||||
if (e.key.toLowerCase() == "enter") {
|
||||
// 添加
|
||||
if (input_data.value == "" || input_data.value == null) {
|
||||
message.error("请输入标签");
|
||||
return;
|
||||
}
|
||||
if (!data.value.chinese_prompt) {
|
||||
message.error("添加标签前请先英翻中");
|
||||
return;
|
||||
}
|
||||
let tags = input_data.value.replaceAll(',', ',');
|
||||
// 自动翻译
|
||||
if (translateMode.value) {
|
||||
// 翻译单个。马上返回
|
||||
await window.api.TranslateReturnNow([tags, 'zh', 'en', true], (value) => {
|
||||
debugger
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const element = value.data[i];
|
||||
data.value.chinese_prompt.push(element);
|
||||
}
|
||||
ModifyPrompt();
|
||||
message.success("添加成功");
|
||||
input_data.value = "";
|
||||
})
|
||||
} else {
|
||||
let tags = input_data.value.split(',');
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const element = tags[i];
|
||||
if (!isEmpty(element)) {
|
||||
data.value.chinese_prompt.push(
|
||||
{
|
||||
"src": "",
|
||||
"dst": element
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭tag的方法
|
||||
* @param {要删除的tag} tag
|
||||
*/
|
||||
async function handleClose(tag) {
|
||||
debugger
|
||||
// 删除对应的数据
|
||||
data.value.chinese_prompt = data.value.chinese_prompt.filter(item => item.src != tag.src);
|
||||
ModifyPrompt();
|
||||
await SearchTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找筛选值(返回符合条件的tag)
|
||||
*/
|
||||
async function SearchTags() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 可以编辑标签
|
||||
* @param {*} e
|
||||
*/
|
||||
async function TagClickEdit(tag, index) {
|
||||
console.log(tag, index);
|
||||
data.value.chinese_prompt[index]["edit"] = true;
|
||||
// 将其他的数据变成false
|
||||
for (let i = 0; i < data.value.chinese_prompt.length; i++) {
|
||||
if (i != index) {
|
||||
data.value.chinese_prompt[i]["edit"] = false;
|
||||
}
|
||||
}
|
||||
ModifyPrompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改标签的数据
|
||||
*/
|
||||
async function ModifyTag(e, tag, index) {
|
||||
if (e.key.toLowerCase() == "enter") {
|
||||
if (tag.dst == "" || tag.dst == null) {
|
||||
message.error("请输入标签");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tag.dst.includes(',') || tag.dst.includes(',')) {
|
||||
message.error("单个标签不能有逗号");
|
||||
return;
|
||||
}
|
||||
|
||||
// 实时翻译一下
|
||||
let tags = tag.dst.replaceAll(',', ',');
|
||||
// 自动翻译
|
||||
if (translateMode.value) {
|
||||
// 翻译单个。马上返回
|
||||
await window.api.TranslateReturnNow([tags, 'zh', 'en', false], (value) => {
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
data.value.chinese_prompt[index] = value.data[0];
|
||||
|
||||
ModifyPrompt();
|
||||
message.success("修改成功");
|
||||
input_data.value = "";
|
||||
})
|
||||
}
|
||||
data.value.chinese_prompt[index]["edit"] = false;
|
||||
ModifyPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据到指定的位置
|
||||
*/
|
||||
async function AddTagInPosition(index) {
|
||||
// 先判断是不是有没有添加的
|
||||
let ii = data.value.chinese_prompt.findIndex(item => item.edit == true);
|
||||
if (ii >= 0) {
|
||||
message.error("请先保存当前添加的标签");
|
||||
return;
|
||||
}
|
||||
data.value.chinese_prompt.splice(index + 1, 0, { "src": "", "dst": "", edit: true });
|
||||
}
|
||||
|
||||
return {
|
||||
AddTag,
|
||||
handleClose,
|
||||
SearchTags,
|
||||
type,
|
||||
translateMode,
|
||||
TagClickEdit,
|
||||
ModifyTag,
|
||||
input_data,
|
||||
data,
|
||||
AddTagInPosition
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
|
||||
<n-form label-width="auto" :model="wmSetting" label-placement="left" style="margin-top: 50px;">
|
||||
<n-form-item v-if="showTextLabel" :disable="true" style=" margin-right: 20px;" label="水印字体(要换行输入\N)"
|
||||
path="fontName">
|
||||
<n-input style="width: 400px;" v-model:value="wmSetting.showText" />
|
||||
</n-form-item>
|
||||
<n-form-item style=" margin-right: 20px;" label="字体" path="fontName">
|
||||
<n-input style="width: 300px;" v-model:value="wmSetting.fontName" :options="fontNameOptions" filterable />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="字号(80差不多)">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.fontSize" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="颜色">
|
||||
<n-color-picker style="width: 200px;" v-model:value="wmSetting.fontColor" :show-alpha="false" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="透明度">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.transparent" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-right: 20px;" label="位置">
|
||||
<n-input-number style="width: 60px; margin-right: 10px;" v-model:value="wmSetting.positionX" placeholder="X"
|
||||
:show-button="false" />
|
||||
<n-input-number style="width: 60px;" v-model:value="wmSetting.positionY" placeholder="Y"
|
||||
:show-button="false" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { NForm, NFormItem, NInput, NInputNumber, NColorPicker, NSelect, NButton } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NColorPicker, NSelect, NButton
|
||||
},
|
||||
props: ["initData", "type"],
|
||||
setup(props) {
|
||||
let showTextLabel = ref(false);
|
||||
let fontNameOptions = ref([]);
|
||||
let wmSetting = ref(props.initData);
|
||||
onMounted(() => {
|
||||
if (props.type == "watermarkConfig") {
|
||||
showTextLabel.value = true;
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
wmSetting,
|
||||
fontNameOptions,
|
||||
showTextLabel
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="color: red; margin-bottom: 5px;">
|
||||
注:添加ADetailer配置,只要选择修脸修手都会调用<br>
|
||||
</div>
|
||||
<n-dynamic-input v-model:value="ADetailer" :on-create="onCreate">
|
||||
<template #create-button-default>
|
||||
添加ADetailer数据
|
||||
</template>
|
||||
<template #default="{ value }">
|
||||
<div style="display: flex; align-items: center; width: 100%">
|
||||
<n-input v-model:value="value.ad_model" placeholder="输入模型名称"> </n-input>
|
||||
<n-input-number v-model:value="value.ad_confidence" style="margin-right: 12px; width: 160px" />
|
||||
</div>
|
||||
</template>
|
||||
</n-dynamic-input>
|
||||
<n-button type="primary" style="margin-top: 10px;" @click="SaveADetailerConfig">保存</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { useMessage, NDynamicInput, NInput, NInputNumber, NButton } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NDynamicInput, NInput, NInputNumber, NButton
|
||||
},
|
||||
setup() {
|
||||
let ADetailer = ref([]);
|
||||
let message = useMessage();
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.GetADetailerList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data != null) {
|
||||
ADetailer.value = value.data;
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 添加的状态
|
||||
*/
|
||||
function onCreate() {
|
||||
return {
|
||||
ad_model: null,
|
||||
ad_confidence: 0.3
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存修脸配置
|
||||
*/
|
||||
async function SaveADetailerConfig() {
|
||||
// 判断添加的数据是不是都有值
|
||||
let isValue = true;
|
||||
let obj = [];
|
||||
for (let i = 0; i < ADetailer.value.length; i++) {
|
||||
const element = ADetailer.value[i];
|
||||
if (element.ad_model == null || element.ad_model == "" || element.ad_confidence == null) {
|
||||
isValue = false;
|
||||
}
|
||||
obj.push({
|
||||
ad_confidence: element.ad_confidence,
|
||||
ad_model: element.ad_model
|
||||
});
|
||||
}
|
||||
if (!isValue) {
|
||||
message.error("添加的项,值不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.SaveADetailerConfig(toRaw(obj), (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
return;
|
||||
}
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
onCreate,
|
||||
ADetailer,
|
||||
SaveADetailerConfig
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-select v-model:value="draf_name" filterable placeholder="选择草稿" :options="options"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { NSelect } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect
|
||||
},
|
||||
|
||||
setup() {
|
||||
let draf_name = ref(null);
|
||||
let options = ref([]);
|
||||
onMounted(async () => {
|
||||
await window.api.getDraftFileList((value) => {
|
||||
value.forEach(element => {
|
||||
let obj = {
|
||||
label: element,
|
||||
value: element
|
||||
}
|
||||
options.value.push(obj)
|
||||
});
|
||||
})
|
||||
})
|
||||
return {
|
||||
draf_name,
|
||||
options,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<n-layout id="layout_height" has-sider style="height: 300px;">
|
||||
<n-layout-sider bordered :width="240" :native-scrollbar="false">
|
||||
<n-menu @update:value="SelectMenu" :options="menuOptions" v-model:value="selectedKeyRef" />
|
||||
</n-layout-sider>
|
||||
<n-layout :native-scrollbar="false" style="margin-top: 30px;">
|
||||
<div style="margin-left: 20px;">
|
||||
<div>选择的风格:</div>
|
||||
<ShowImageTag :selectStyle="selectStyle"></ShowImageTag>
|
||||
<div style="margin-top: 5px;">
|
||||
<div class="image-container" style="display: flex;">
|
||||
<n-image-group show-toolbar-tooltip>
|
||||
<div style="display: flex; flex-wrap: wrap;">
|
||||
<div v-for="(image, index) in style_image_list " style="margin: 0 5px 5px 5px;"
|
||||
:key="image.id">
|
||||
<n-image @contextmenu.prevent="handleContextMenu($event, image)" :width="150"
|
||||
:height="150" :src="image.image" alt="图片描述" lazy style="display: block;" />
|
||||
</div>
|
||||
</div>
|
||||
</n-image-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
|
||||
<n-dropdown style="z-index: 100;" trigger="manual" :x="x" :y="y" :options="dropOption" :show="showDropdownRef"
|
||||
:on-clickoutside="onClickoutside" @select="handleSelect" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, watch, nextTick } from "vue";
|
||||
import { NDropdown, NImage, NLayout, NLayoutContent, NLayoutSider, NMenu, NSpace, NTag, NTooltip, messageDark, useMessage } from "naive-ui"
|
||||
import ShowImageTag from "./ShowImageTag.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSpace, NLayout, NLayoutSider, NLayoutContent, NMenu, NTag, NTooltip, NImage, NDropdown, ShowImageTag
|
||||
},
|
||||
props: ["selectStyle", "height"],
|
||||
setup(props) {
|
||||
let current_menu = null;
|
||||
let selectedKeyRef = ref(null);
|
||||
|
||||
let selectStyle = ref(props.selectStyle ? props.selectStyle : []);
|
||||
let style_image_list = ref([]);
|
||||
let message = useMessage();
|
||||
let showDropdownRef = ref(false);
|
||||
let current_img = ref(null);
|
||||
let menuOptions = ref([]);
|
||||
|
||||
const x = ref(0);
|
||||
const y = ref(0);
|
||||
|
||||
let dropOption = [{
|
||||
label: "选择风格",
|
||||
key: "selectStyle"
|
||||
}];
|
||||
|
||||
// 监听props.selectStyle修改
|
||||
watch(() => props.selectStyle, (value) => {
|
||||
selectStyle.value = value;
|
||||
if (!selectStyle.value) {
|
||||
selectStyle.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
setLayoutHeight("layout_height");
|
||||
await window.api.GetImageStyleMenu((value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
menuOptions.value = value.data;
|
||||
current_menu = value.data[0].id;
|
||||
});
|
||||
await getStyleImageSubList();
|
||||
selectedKeyRef.value = current_menu;
|
||||
})
|
||||
|
||||
/**
|
||||
* 菜单选中事件
|
||||
*/
|
||||
async function SelectMenu(key, item) {
|
||||
current_menu = key;
|
||||
await getStyleImageSubList();
|
||||
}
|
||||
|
||||
async function getStyleImageSubList() {
|
||||
await window.api.getStyleImageSubList(current_menu, (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
style_image_list.value = value.data;
|
||||
});
|
||||
}
|
||||
|
||||
// 将指定ID的元素的的高度设置到和当前组件一样高
|
||||
function setLayoutHeight(id) {
|
||||
let layout_height = document.getElementById(id);
|
||||
layout_height.style.height = props.height - 40 + "px";
|
||||
}
|
||||
|
||||
async function closeTag(image) {
|
||||
debugger;
|
||||
let index = selectStyle.value.findIndex(item => item.id == image.id);
|
||||
if (index != -1) {
|
||||
selectStyle.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
selectStyle,
|
||||
menuOptions,
|
||||
SelectMenu,
|
||||
style_image_list,
|
||||
selectedKeyRef,
|
||||
showDropdownRef,
|
||||
dropOption,
|
||||
current_img,
|
||||
x: x,
|
||||
y: y,
|
||||
closeTag,
|
||||
handleContextMenu(e, image) {
|
||||
e.preventDefault();
|
||||
current_img.value = image;
|
||||
nextTick().then(() => {
|
||||
showDropdownRef.value = true;
|
||||
x.value = e.clientX;
|
||||
y.value = e.clientY;
|
||||
});
|
||||
},
|
||||
onClickoutside() {
|
||||
showDropdownRef.value = false;
|
||||
},
|
||||
handleSelect(key) {
|
||||
debugger
|
||||
showDropdownRef.value = false;
|
||||
if (key == "selectStyle") {
|
||||
// 将当前的风格添加到选择的风格中
|
||||
if (current_img.value) {
|
||||
selectStyle.value.push(current_img.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div style="margin-top: 10px; min-height: 40px;">
|
||||
<n-tooltip trigger="hover" v-for="item in selectStyle" placement="bottom-start">
|
||||
<template #trigger>
|
||||
<n-tag @close="closeTag(item)" :key="item.id" type="success"
|
||||
style="margin-right: 10px; margin-bottom: 10px" closable>
|
||||
{{ item.id.split("-")[0] }}
|
||||
</n-tag>
|
||||
</template>
|
||||
<div>
|
||||
<n-image :src="item.image" :width="200" :preview-disabled="true"></n-image>
|
||||
</div>
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, watch, nextTick } from "vue";
|
||||
import { NImage, NTag, NTooltip, useMessage } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NTag, NTooltip, NImage
|
||||
},
|
||||
props: ["selectStyle"],
|
||||
setup(props) {
|
||||
let selectStyle = ref(props.selectStyle ? props.selectStyle : []);
|
||||
let message = useMessage();
|
||||
|
||||
|
||||
// 监听props.selectStyle修改
|
||||
watch(() => props.selectStyle, (value) => {
|
||||
selectStyle.value = value;
|
||||
if (!selectStyle.value) {
|
||||
selectStyle.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
})
|
||||
|
||||
async function closeTag(image) {
|
||||
debugger;
|
||||
let index = selectStyle.value.findIndex(item => item.id == image.id);
|
||||
if (index != -1) {
|
||||
selectStyle.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
selectStyle,
|
||||
closeTag,
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-layout has-sider style="height: 100vh; position: relative;">
|
||||
<n-layout-sider bordered collapse-mode="width" :collapsed-width="64" :width="220" :collapsed="collapsed"
|
||||
show-trigger @collapse="collapsed = true" @expand="collapsed = false">
|
||||
<n-menu :collapsed="collapsed" :collapsed-width="64" :collapsed-icon-size="22" :options="menuOptions"
|
||||
:render-icon="renderMenuIcon" :expand-icon="expandIcon" />
|
||||
</n-layout-sider>
|
||||
<n-layout-content content-style="padding:30px">
|
||||
<!-- <Setting></Setting> -->
|
||||
<router-view></router-view>
|
||||
</n-layout-content>
|
||||
</n-layout>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from "vue"
|
||||
import { RouterLink } from "vue-router"
|
||||
import { NMenu, NSpace, NLayout, NLayoutSider, NLayoutContent, NIcon, useDialog, useNotification, useMessage, NSwitch, NButton } from "naive-ui";
|
||||
|
||||
import { BookmarkOutline, CaretDownOutline } from "@vicons/ionicons5";
|
||||
import CheckMachineId from "../Components/CheckMachineId.vue"
|
||||
import axios from 'axios';
|
||||
import { DEFINE_STRING } from "../../../../define/define_string.js"
|
||||
import ShowMessage from './ShowMessage.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NMenu, NSpace, NLayout, NLayoutSider, NLayoutContent, NIcon, ShowMessage, NSwitch, NButton
|
||||
},
|
||||
setup() {
|
||||
let collapsed = ref(false);
|
||||
let dialog = useDialog();
|
||||
let machineRef = ref();
|
||||
let message = useMessage();
|
||||
let notification = useNotification();
|
||||
|
||||
function renderMenuIcon(option) {
|
||||
if (option.key === "sheep-man")
|
||||
return true;
|
||||
if (option.key === "food")
|
||||
return null;
|
||||
return h(NIcon, null, { default: () => h(BookmarkOutline) });
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 加载版本
|
||||
await window.api.GetVersion((value) => {
|
||||
document.title = "LAITool " + value;
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.SHOW_MESSAGE_DIALOG], (value) => {
|
||||
let message = value.message;
|
||||
let type = "success";
|
||||
let title = "成功"
|
||||
if (value.code == 0) {
|
||||
type = "error";
|
||||
title = "成功但失败"
|
||||
} else if (value.code == 2) {
|
||||
type = "warning",
|
||||
title = "警告"
|
||||
}
|
||||
dialog.create({
|
||||
type: type,
|
||||
title: title,
|
||||
showIcon: true,
|
||||
content: message,
|
||||
style: `width : 400px;`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
})
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.SHOW_MAIN_NOTIFICATION], (value) => {
|
||||
let message = value.message;
|
||||
let type = "success";
|
||||
let title = "成功"
|
||||
if (value.code == 0) {
|
||||
type = "error";
|
||||
title = "失败"
|
||||
} else if (value.code == 2) {
|
||||
type = "warning";
|
||||
title = "警告"
|
||||
}
|
||||
notification.create({
|
||||
type: type,
|
||||
title: title,
|
||||
content: message,
|
||||
keepAliveOnHover: true,
|
||||
duration: 2500,
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
// 弹窗,获取机器码
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
content: () => h(CheckMachineId, { ref: machineRef }),
|
||||
style: `width : 400px; height: 300px`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
onClose: () => {
|
||||
// 关闭程序
|
||||
window.api.QuitApp();
|
||||
return false;
|
||||
},
|
||||
onEsc: () => {
|
||||
// 关闭程序
|
||||
window.api.QuitApp();
|
||||
return false;
|
||||
},
|
||||
onPositiveClick: async () => {
|
||||
debugger
|
||||
await new Promise(async (resolve, reject) => {
|
||||
await window.api.CheckMachineId(toRaw(machineRef.value.machineId), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
window.api.getSettingDafultData(async (value) => {
|
||||
window.config = value;
|
||||
})
|
||||
})
|
||||
|
||||
function expandIcon(value) {
|
||||
return h(NIcon, null, { default: () => h(CaretDownOutline) });
|
||||
}
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "sdoriginal",
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => "原创生图"
|
||||
}
|
||||
),
|
||||
key: "sdoriginal",
|
||||
},
|
||||
{
|
||||
label: "反推矩阵",
|
||||
key: "backward_matrix",
|
||||
children: [
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "getframe",
|
||||
}
|
||||
},
|
||||
{ default: () => "抽帧" }
|
||||
),
|
||||
key: "backward_frame"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "copywriting",
|
||||
}
|
||||
},
|
||||
{ default: () => "文案洗稿" }
|
||||
),
|
||||
key: "copywriting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "pushBackPrompt",
|
||||
}
|
||||
},
|
||||
{ default: () => "反推提示词" }
|
||||
),
|
||||
key: "push_back"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "regenerate",
|
||||
}
|
||||
},
|
||||
{ default: () => "批次生成" }
|
||||
),
|
||||
key: "regenerate"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "VideoGenerate",
|
||||
}
|
||||
},
|
||||
{ default: () => "重绘/视频合成" }
|
||||
),
|
||||
key: "VideoGenerate"
|
||||
}
|
||||
]
|
||||
},
|
||||
// {
|
||||
// label: "剪辑",
|
||||
// key: "clip_options",
|
||||
// children: [
|
||||
// {
|
||||
// label: () => h(
|
||||
// RouterLink,
|
||||
// {
|
||||
// to: {
|
||||
// name: "align_draft",
|
||||
// }
|
||||
// },
|
||||
// { default: () => "剪映草稿对齐" }
|
||||
// ),
|
||||
// key: "align_draft"
|
||||
// },
|
||||
// {
|
||||
// label: () => h(
|
||||
// RouterLink,
|
||||
// {
|
||||
// to: {
|
||||
// name: "add_draft",
|
||||
// }
|
||||
// },
|
||||
// { default: () => "添加剪映草稿" }
|
||||
// ),
|
||||
// key: "add_draft"
|
||||
// }
|
||||
// ]
|
||||
|
||||
// },
|
||||
{
|
||||
label: "设置",
|
||||
key: "setting",
|
||||
children: [
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "global_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "通用设置" }
|
||||
),
|
||||
key: "global_setting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "clip_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "剪映设置" }
|
||||
),
|
||||
key: "clip_setting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "videogeneratesetting",
|
||||
}
|
||||
},
|
||||
{ default: () => "生成视频设置" }
|
||||
),
|
||||
key: "videogeneratesetting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "sd_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "SD设置" }
|
||||
),
|
||||
key: "sd_setting"
|
||||
},
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: "mj_setting",
|
||||
}
|
||||
},
|
||||
{ default: () => "MJ设置" }
|
||||
),
|
||||
key: "mj_setting"
|
||||
}
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
renderMenuIcon,
|
||||
menuOptions,
|
||||
expandIcon,
|
||||
collapsed,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,744 @@
|
||||
<template>
|
||||
<div id="menu_div"
|
||||
style="padding : 5px 0 5px 0; position: fixed; left: 0; top: 0; backgroundColor: white; height: 30px; width: 100%; z-index: 100; display: flex; opacity: 1;">
|
||||
<n-select style="width: 200px" v-model:value="output_folder" :options="select_options" />
|
||||
<n-button type="info" style="margin:0 10px 0 10px; width: 80px" @click="RefreshImageData">刷新</n-button>
|
||||
<n-input placeholder="修改前" v-model:value="modeifyBefore" style="width: 200px"></n-input>
|
||||
<div style="display: flex; margin: 0 10px 0 10px; color: black;align-items: center;">
|
||||
<span><strong id="menu_strong">替换为</strong></span>
|
||||
</div>
|
||||
<n-input placeholder="修改后(为空则删除,并没有替换后台数据,点击后面的一键修改)" v-model:value="modeifyAfter"
|
||||
style="width: 200px"></n-input>
|
||||
<n-button type="info" style="width: 100px; margin-left: 10px;" @click="ReplacePrompt">替换</n-button>
|
||||
<n-input placeholder="添加数据(只能输入英文,逗号分割)" v-model:value="globalPromptInput" style="width: 220px"></n-input>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button type="info" style="width: 100px; margin-left: 10px;"
|
||||
@click="AddTranslateGlobalPrompt">翻译后添加</n-button>
|
||||
</template>
|
||||
<span>翻译后添加。会有中文标签提示词和英文提示词。</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button type="info" style="width: 100px; margin-left: 10px;" @click="AddGlobalPrompt">添加提示词</n-button>
|
||||
</template>
|
||||
<span>直接添加提示词。没有进行翻译。没有中文标签。需自己手动全部翻译</span>
|
||||
</n-popover>
|
||||
<n-button type="info" style="width: 150px; margin-left: 10px; margin-right: 10px;"
|
||||
@click="ModifyAllSDPrompt">一键应用所有修改</n-button>
|
||||
</div>
|
||||
<n-data-table style="margin-top: 30px;" :columns="columns" :data="data"> </n-data-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from "vue"
|
||||
import { NDataTable, NImage, useMessage, NButton, NInput, NSelect, NInputNumber, useDialog, NModal, NCheckbox, NSwitch, NPopover, NConfigProvider, darkTheme } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import GenerateAllImages from "../Components/GenerateAllImages.vue"
|
||||
import ModifyPromptChinese from "../Components/ModifyPromptChinese.vue"
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "重绘",
|
||||
key: 'reDraw',
|
||||
type: "primary",
|
||||
}
|
||||
,
|
||||
{
|
||||
title: "重绘所有",
|
||||
key: "reDrawSelect",
|
||||
type: "warning",
|
||||
}
|
||||
,
|
||||
{
|
||||
title: "修改应用全部",
|
||||
key: "modifyJson",
|
||||
type: "info",
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NDataTable, NImage, NButton, NInput, NSelect, NInputNumber, NModal, NCheckbox, NSwitch, ModifyPromptChinese, NPopover, NConfigProvider
|
||||
},
|
||||
setup() {
|
||||
const containerRef = ref < HTMLElement | undefined > (undefined)
|
||||
let message = useMessage();
|
||||
let dialog = useDialog();
|
||||
let output_folder = ref("");
|
||||
let select_options = ref([]);
|
||||
let controlHeight = ref(0);
|
||||
let modeifyBefore = ref("");
|
||||
let modeifyAfter = ref("");
|
||||
let data = ref([]);
|
||||
let multiple_value = ref([]);
|
||||
let ImageInitData = ref(null);
|
||||
let projectFolderList = ref([]);
|
||||
let dialogWidth = ref(0);
|
||||
let isTagMode = ref(false);
|
||||
let promptChineseRef = ref(null)
|
||||
let zhAutoTranslate = ref(false);
|
||||
let globalPromptInput = ref(null);
|
||||
|
||||
let model = ref("");
|
||||
let modelOption = [{
|
||||
label: "文生图",
|
||||
value: "txt2img"
|
||||
}, {
|
||||
label: "图生图",
|
||||
value: "img2img"
|
||||
}];
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
async function RefreshImageData() {
|
||||
debugger
|
||||
data.value = [];
|
||||
console.log(window)
|
||||
await window.api.RefreshImageData([window.id, output_folder.value], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
value.value.forEach(item => {
|
||||
value.value.loading = false;
|
||||
});
|
||||
data.value = value.value;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换当前词
|
||||
*/
|
||||
async function ReplacePrompt() {
|
||||
if (modeifyBefore.value == "") {
|
||||
return;
|
||||
}
|
||||
// 循环data里面的所有的提示词,修改
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
let prompt = element.prompt;
|
||||
prompt = prompt.replaceAll(modeifyBefore.value, modeifyAfter.value);
|
||||
data.value[i].prompt = prompt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键应用所有的修改
|
||||
*/
|
||||
async function ModifyAllSDPrompt() {
|
||||
dialog.warning({
|
||||
title: "确认",
|
||||
content: "确定修改?后面执行的所有的重绘,都将被修改!",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: async () => {
|
||||
let count = 0;
|
||||
// 循环修改全部
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
let prompt = "";
|
||||
let chinese_prompt = [];
|
||||
if (element.chinese_prompt) {
|
||||
for (let i = 0; i < element.chinese_prompt.length; i++) {
|
||||
const item = element.chinese_prompt[i];
|
||||
prompt += item.src + ',';
|
||||
chinese_prompt.push(item);
|
||||
}
|
||||
} else {
|
||||
prompt = element.prompt;
|
||||
chinese_prompt = null;
|
||||
}
|
||||
let arg = {
|
||||
name: element.name,
|
||||
prompt: prompt,
|
||||
denoising_strength: element.denoising_strength,
|
||||
model: element.model,
|
||||
adetailer: element.adetailer,
|
||||
chinese_prompt: chinese_prompt
|
||||
}
|
||||
|
||||
await window.api.ModifyInpurCropJson(JSON.stringify(arg), (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
});
|
||||
}
|
||||
if (count >= data.value.length) {
|
||||
message.success("修改成功")
|
||||
}
|
||||
else {
|
||||
message.error(`第${count + 1}条修改错误`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const createColumns = ({ ReGenerate, ModifyInputJson, GenerateAllImageDialog, TranslatePrompt }) => {
|
||||
return [
|
||||
{
|
||||
title: '名字',
|
||||
key: 'name',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '原图像',
|
||||
key: 'old_image',
|
||||
width: 200,
|
||||
render(row) {
|
||||
return h(NImage, {
|
||||
src: "file:\\" + row.oldImage + `?time=${row.timedata}`,
|
||||
alt: 'Image',
|
||||
height: '150'
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '图像',
|
||||
key: 'new_image',
|
||||
width: 200,
|
||||
render(row) {
|
||||
return h(NImage, {
|
||||
src: "file:\\" + row.image + `?time=${row.timedata}`,
|
||||
alt: 'Image',
|
||||
width: '150'
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "options",
|
||||
width: 100,
|
||||
render(row) {
|
||||
const btn = buttonArr.map(item => {
|
||||
return h(
|
||||
NButton, {
|
||||
strong: true,
|
||||
size: "small",
|
||||
type: item.type,
|
||||
style: "margin : 5px",
|
||||
onClick: () => {
|
||||
if (item.key == "reDraw") {
|
||||
ReGenerate(row);
|
||||
} else if (item.key == "modifyJson") {
|
||||
ModifyInputJson(row);
|
||||
} else if (item.key == "reDrawSelect") {
|
||||
GenerateAllImageDialog(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
return h('div', {
|
||||
style: "display : flex; flex-wrap : wrap"
|
||||
}, btn)
|
||||
},
|
||||
},
|
||||
{
|
||||
title(row) {
|
||||
return h("span", {}, [
|
||||
h("span", {
|
||||
style: "margin-right : 10px; width : 80px "
|
||||
}, "正向翻译"),
|
||||
h(NSwitch, {
|
||||
railStyle: ({
|
||||
focused,
|
||||
checked
|
||||
}) => {
|
||||
const style = {};
|
||||
if (checked) {
|
||||
style.background = "#f3a694";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #f3a694";
|
||||
}
|
||||
} else {
|
||||
style.background = "#775039";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #775039";
|
||||
}
|
||||
}
|
||||
return style;
|
||||
},
|
||||
onUpdateValue: (value) => {
|
||||
zhAutoTranslate.value = value;
|
||||
},
|
||||
value: zhAutoTranslate
|
||||
}, {
|
||||
checked: "自动翻译",
|
||||
unchecked: "手动翻译",
|
||||
}),
|
||||
h(NButton, {
|
||||
size: "tiny",
|
||||
color: "#e18a3b",
|
||||
style: "margin-left : 5px",
|
||||
onClick: async () => {
|
||||
// 只翻译chinese_prompt为null的
|
||||
let filterData = data.value.filter(item => item.chinese_prompt == null);
|
||||
await TranslatePrompt(JSON.stringify(filterData), 'en', 'zh', true);
|
||||
}
|
||||
}, {
|
||||
default: () => "翻译英文"
|
||||
}),
|
||||
])
|
||||
},
|
||||
key: 'prompt_translate',
|
||||
render(row, index) {
|
||||
return h("div", {}, [
|
||||
h(ModifyPromptChinese, { ref: promptChineseRef, initData: row, type: "zh", translateMode: zhAutoTranslate })
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
title() {
|
||||
return h("span", {}, [
|
||||
h("span", {
|
||||
style: "margin-right : 10px; width : 80px "
|
||||
}, "正向提示词"),
|
||||
h(NButton, {
|
||||
size: "tiny",
|
||||
color: "#e18a3b",
|
||||
style: "margin-left : 5px",
|
||||
onClick: async () => {
|
||||
TranslateChineseToPrompt();
|
||||
}
|
||||
}, {
|
||||
default: () => "翻译中文"
|
||||
}),
|
||||
])
|
||||
},
|
||||
key: 'prompt',
|
||||
width: "300",
|
||||
render(row, index) {
|
||||
return h("div", {
|
||||
style: "position: relative;"
|
||||
}, [
|
||||
h(NInput, {
|
||||
size: "tiny",
|
||||
style: "margin-top : 20px",
|
||||
value: row.prompt,
|
||||
type: "textarea",
|
||||
autosize: "{ minRows: 3 }",
|
||||
onUpdateValue(v) {
|
||||
data.value[index].prompt = v
|
||||
}
|
||||
}),
|
||||
h(NButton, {
|
||||
size: 'tiny',
|
||||
color: "#dd7694",
|
||||
style: "position: absolute; left : 0;top : 0",
|
||||
onClick: async () => {
|
||||
await window.api.TranslatePrompt([JSON.stringify([row]), 'en', 'zh', window.id, false], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
}, { default: () => "翻译" })
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "参数",
|
||||
key: "argument",
|
||||
width: 300,
|
||||
render(row, index) {
|
||||
return h('div', { class: "div_1" }, [
|
||||
h('div', { class: "div_2" }, [
|
||||
h('label', { class: 'input-label' }, '生图方式'),
|
||||
h(NSelect, {
|
||||
options: modelOption,
|
||||
style: "width : 100px",
|
||||
value: row.model,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].model = v;
|
||||
}
|
||||
}),
|
||||
h(NCheckbox, {
|
||||
style: "margin-left : 5px ; height : 100%",
|
||||
checked: row.adetailer,
|
||||
label: "修脸",
|
||||
onUpdateChecked(v) {
|
||||
data.value[index].adetailer = v;
|
||||
}
|
||||
|
||||
}),
|
||||
]),
|
||||
h('div', { class: "div_2", style: "margin: 10px 0" }, [
|
||||
h('label', { class: 'input-label' }, '重绘幅度'),
|
||||
h(NInputNumber, {
|
||||
step: "0.05",
|
||||
style: "width : 100px",
|
||||
precision: 2,
|
||||
value: row.denoising_strength,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].denoising_strength = v;
|
||||
}
|
||||
})
|
||||
])
|
||||
]);
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// 计算一行的提示词
|
||||
async function ModifyPrompt(data, id = null) {
|
||||
// 如果index 存在。替换单独
|
||||
if (id) {
|
||||
let index = data.value.findIndex(item => item.id == id);
|
||||
if (index < 0) {
|
||||
message.error("写入源数据ID错误。请检查");
|
||||
return;
|
||||
}
|
||||
let res = '';
|
||||
if (data.value[index].chinese_prompt && data.value[index].chinese_prompt.length > 0) {
|
||||
for (let i = 0; i < data.value[index].chinese_prompt.length; i++) {
|
||||
const item = data.value[index].chinese_prompt[i];
|
||||
res += item.src + ',';
|
||||
}
|
||||
} else {
|
||||
res = data.value[index].prompt
|
||||
}
|
||||
data.value[index].prompt = res;
|
||||
} else {
|
||||
// 修改全部
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
let res = "";
|
||||
if (element.chinese_prompt && element.chinese_prompt.length > 0) {
|
||||
for (let i = 0; i < element.chinese_prompt.length; i++) {
|
||||
const item = element.chinese_prompt[i];
|
||||
res += item.src + ',';
|
||||
}
|
||||
} else {
|
||||
res = element.prompt
|
||||
}
|
||||
data.value[i].prompt = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.getImagePromptList((value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message)
|
||||
return;
|
||||
}
|
||||
value.value.forEach(item => {
|
||||
value.value.loading = false;
|
||||
});
|
||||
|
||||
|
||||
data.value = value.value;
|
||||
ModifyPrompt(data);
|
||||
window.id = value.windowID;
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.REGENERATE_IMAGE_RETUN, window.id], (value) => {
|
||||
if (value.type != 0) {
|
||||
return;
|
||||
}
|
||||
// 修改图片的数据源
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
let filteArr = data.value.filter(item => item.id == value.id);
|
||||
filteArr[0].timedata = new Date().getTime();
|
||||
filteArr[0].loading = false;
|
||||
message.success(JSON.stringify(value));
|
||||
})
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.TRANSLATE_RETURN_REFRESH, window.id], (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error("返回数据错误");
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
|
||||
// 直接
|
||||
let index = data.value.findIndex(item => item.id == value.rowId);
|
||||
if (index < 0) {
|
||||
message.error("翻译后修改源数据,对应数据ID未找到");
|
||||
return;
|
||||
}
|
||||
data.value[index].chinese_prompt = value.data;
|
||||
|
||||
ModifyPrompt(data, value.rowId)
|
||||
console.log(value)
|
||||
})
|
||||
|
||||
window.api.getSubFolderList(["start", "output_crop"], (value) => {
|
||||
select_options.value = [];
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const v = value.data[i];
|
||||
let obj = {
|
||||
label: v.folder,
|
||||
value: v.folder
|
||||
};
|
||||
select_options.value.push(obj);
|
||||
}
|
||||
})
|
||||
|
||||
await window.api.getSettingDafultData((value) => {
|
||||
window.config = value;
|
||||
zhAutoTranslate.value = value.translation_auto;
|
||||
let div = document.getElementById("menu_div");
|
||||
let strong = document.getElementById("menu_strong");
|
||||
if (value.theme == "dark") {
|
||||
div.style.backgroundColor = "#24242c";
|
||||
strong.style.color = "#ccc"
|
||||
} else {
|
||||
div.style.backgroundColor = "#fff";
|
||||
strong.style.color = "#333"
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.TRANSLATE_RETURN_REFRESH);
|
||||
window.api.removeEventListen(DEFINE_STRING.REGENERATE_IMAGE_RETUN);
|
||||
})
|
||||
|
||||
/**
|
||||
* 将翻译添加到队列中的方式
|
||||
* @param {要翻译的数据(数组)} translateData
|
||||
* @param {原语言(zh,en)} from
|
||||
* @param {目标语言(zh,en)} to
|
||||
* @param {是否显示弹窗} isShow
|
||||
*/
|
||||
async function TranslatePrompt(translateData, from, to) {
|
||||
console.log(translateData);
|
||||
await window.api.TranslatePrompt([translateData, from, to, window.id, true], (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译中文到提示词
|
||||
*/
|
||||
async function TranslateChineseToPrompt() {
|
||||
debugger;
|
||||
// 将当前的prompt和Chinese_prompt中的src全部删除(全部重新翻译)
|
||||
let fileterData = [];
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
//
|
||||
// 判断chinese_prompt 是不是存在
|
||||
if (element.chinese_prompt && element.chinese_prompt.length > 0) {
|
||||
// 删除 prompt 和 chinese_prompt 中的src
|
||||
data.value[i].prompt = "";
|
||||
for (let j = 0; j < element.chinese_prompt.length; j++) {
|
||||
data.value[i].chinese_prompt[j].src = "";
|
||||
}
|
||||
// 将修改后的数据添加到fileterData
|
||||
fileterData.push(data.value[i]);
|
||||
}
|
||||
}
|
||||
console.log(fileterData);
|
||||
let tmp_str = JSON.stringify(fileterData);
|
||||
// 开始调用
|
||||
await window.api.TranslatePrompt([tmp_str, 'zh', 'en', window.id, true], (value) => {
|
||||
debugger
|
||||
console.log(value)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示选择框
|
||||
* @param {} row
|
||||
*/
|
||||
async function GenerateAllImageDialog(row) {
|
||||
// 处理数据。获取当前的所有的数据
|
||||
dialogWidth.value = window.innerWidth * 0.8;
|
||||
window.ImageInitData = data.value;
|
||||
window.currentImageId = row.id;
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
content: () => h(GenerateAllImages),
|
||||
style: `width : ${dialogWidth.value}px`,
|
||||
maskClosable: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前的修改直接应用到json文件中
|
||||
* @param {数据列表的行数据} row
|
||||
*/
|
||||
async function ModifyInputJson(row) {
|
||||
dialog.warning({
|
||||
title: "确认",
|
||||
content: "确定修改?后面执行的所有的重绘,都将被修改!",
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
maskClosable: false,
|
||||
onPositiveClick: () => {
|
||||
// 修改chinese_prompt 和prompt
|
||||
let prompt = "";
|
||||
let chinese_prompt = [];
|
||||
if (row.chinese_prompt) {
|
||||
for (let i = 0; i < row.chinese_prompt.length; i++) {
|
||||
const element = row.chinese_prompt[i];
|
||||
prompt += element.src + ',';
|
||||
chinese_prompt.push(element);
|
||||
}
|
||||
} else {
|
||||
prompt = row.prompt;
|
||||
chinese_prompt = null;
|
||||
}
|
||||
|
||||
let arg = {
|
||||
name: row.name,
|
||||
prompt: prompt,
|
||||
denoising_strength: row.denoising_strength,
|
||||
model: row.model,
|
||||
adetailer: row.adetailer,
|
||||
chinese_prompt: chinese_prompt
|
||||
}
|
||||
|
||||
window.api.ModifyInpurCropJson(JSON.stringify(arg), (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("修改成功");
|
||||
console.log(vaule);
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function ReGenerate(rowData) {
|
||||
let obj = {
|
||||
id: rowData.id,
|
||||
denoising_strength: rowData.denoising_strength,
|
||||
image: rowData.image,
|
||||
name: rowData.name,
|
||||
negative_prompt: rowData.negative_prompt,
|
||||
prompt: rowData.prompt,
|
||||
sampler_name: rowData.sampler_name,
|
||||
seed: rowData.seed,
|
||||
timedata: new Date().getTime(),
|
||||
model: rowData.model,
|
||||
image_style: rowData.image_style,
|
||||
lora: rowData.lora,
|
||||
adetailer: rowData.adetailer,
|
||||
image_style_list: toRaw(rowData.image_style_list),
|
||||
}
|
||||
rowData.loading = true;
|
||||
// 开始重新生成
|
||||
window.api.ReGenerateImage([window.id, obj, 0], (value) => {
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
// 直接添加提示词到prompt中
|
||||
async function AddGlobalPrompt() {
|
||||
if (globalPromptInput.value == "" || globalPromptInput.value == null) {
|
||||
message.error("请输入要添加的数据");
|
||||
return;
|
||||
}
|
||||
// 直接添加数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
let add_p = globalPromptInput.value.replaceAll(',', ',');
|
||||
data.value[i].prompt = `${add_p},${data.value[i].prompt}`;
|
||||
}
|
||||
message.success("添加成功");
|
||||
globalPromptInput.value = null;
|
||||
}
|
||||
|
||||
// 添加翻译后的全局配置
|
||||
async function AddTranslateGlobalPrompt() {
|
||||
if (globalPromptInput.value == "" || globalPromptInput.value == null) {
|
||||
message.error("请输入要添加的数据");
|
||||
return;
|
||||
}
|
||||
|
||||
let tmp_s = globalPromptInput.value.replaceAll(",", ",");
|
||||
// 翻译立即返回
|
||||
// 翻译单个。马上返回
|
||||
await window.api.TranslateReturnNow([tmp_s, 'en', 'zh', true], (value) => {
|
||||
debugger
|
||||
console.log(value)
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].chinese_prompt.unshift(...value.data)
|
||||
}
|
||||
ModifyPrompt(data);
|
||||
message.success("添加成功");
|
||||
globalPromptInput.value = "";
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
ImageInitData,
|
||||
projectFolderList,
|
||||
columns: createColumns({
|
||||
ReGenerate(rowData) {
|
||||
ReGenerate(rowData)
|
||||
},
|
||||
ModifyInputJson,
|
||||
GenerateAllImageDialog,
|
||||
TranslatePrompt,
|
||||
TranslateChineseToPrompt
|
||||
}),
|
||||
RefreshImageData,
|
||||
output_folder,
|
||||
select_options,
|
||||
multiple_value,
|
||||
containerRef,
|
||||
controlHeight,
|
||||
modeifyAfter,
|
||||
modeifyBefore,
|
||||
ReplacePrompt,
|
||||
ModifyAllSDPrompt,
|
||||
modelOption,
|
||||
isTagMode,
|
||||
zhAutoTranslate,
|
||||
AddGlobalPrompt,
|
||||
AddTranslateGlobalPrompt,
|
||||
globalPromptInput,
|
||||
model,
|
||||
bodyStyle: {
|
||||
width: '600px'
|
||||
},
|
||||
segmented: {
|
||||
content: 'soft',
|
||||
footer: 'soft'
|
||||
},
|
||||
promptChineseRef,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.input-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.div_1 {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.div_2 {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: auto;">
|
||||
<!-- <div style="display: flex; justify-content: center;">
|
||||
<n-image width="200" height="250" src="http://qiniuyun.upsurging.xyz/LAI/1280X1280.PNG" preview-disabled />
|
||||
<n-image width="250" style="margin-left: 100px;"
|
||||
src="http://qiniuyun.upsurging.xyz/LAI/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20240427225845.png"
|
||||
preview-disabled />
|
||||
</div> -->
|
||||
<div style="font-size: 15px ;display: flex; justify-content: center;">
|
||||
<div id="showmessage"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from "vue"
|
||||
import { NImage, useMessage } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage
|
||||
},
|
||||
setup() {
|
||||
onMounted(async () => {
|
||||
await window.api.GetShowMessage((value) => {
|
||||
if (value.code == 0) {
|
||||
return;
|
||||
}
|
||||
let parser = new DOMParser();
|
||||
let html = parser.parseFromString(value.data, 'text/html');
|
||||
let div = document.getElementById("showmessage");
|
||||
|
||||
div.innerHTML = value.data.shareInfo.note_list[0].html_content
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form ref="formRef" label-placement="top" :model="characterData" :rules="rules">
|
||||
<n-form-item label="人物名称" path="label">
|
||||
<n-input v-model:value="characterData.label" placeholder="请输入人物名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="人物别名(可多个)">
|
||||
<n-dynamic-tags type="success" v-model:value="alias_tags" />
|
||||
</n-form-item>
|
||||
<n-form-item label="人物提示词(中文)">
|
||||
<n-input type="textarea" :rows="2" v-model:value="characterData.chinese_prompt"
|
||||
placeholder="请输入人物提示词" />
|
||||
<n-button type="info" size="small" style="margin-left: 20px;" @click="TranslatePrompt"
|
||||
:loading="loading">翻译提示词</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="人物提示词(英文)" path="prompt">
|
||||
<n-input type="textarea" :rows="2" v-model:value="characterData.prompt" placeholder="请输入人物提示词" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:人物图片链接(cref)">
|
||||
<n-input v-model:value="characterData.image_url" placeholder="请输入人物图片链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="characterData.image_url" :src="characterData.image_url"
|
||||
style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:角色迁移 cw 值(0-100)">
|
||||
<n-input-number :show-button="false" v-model:value="characterData.cref_cw" min="0" max="100"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="characterData.lora" placeholder="请输入人物Lora选择" />
|
||||
<!-- <n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button> -->
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SaveCharacterTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NForm, NFormItem, NDynamicTags, NInputNumber } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentCharacter", "initFunc", "currentTags"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let alias_tags = ref(props.currentTags);
|
||||
let characterData = ref(props.currentCharacter);
|
||||
let loading = ref(false);
|
||||
let formRef = ref(null)
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.currentCharacter, (value) => {
|
||||
characterData.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
watch(() => props.currentTags, (value) => {
|
||||
alias_tags.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
async function SaveCharacterTag(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 保存之前要处理数据(判断该当前的将别名数组中的数据进行拆分)
|
||||
let children = [];
|
||||
alias_tags.value.forEach((item) => {
|
||||
children.push({
|
||||
label: item,
|
||||
key: uuidv4(),
|
||||
type: "min",
|
||||
chinese_prompt: characterData.value.chinese_prompt,
|
||||
prompt: characterData.value.prompt,
|
||||
image_url: characterData.value.image_url,
|
||||
cref_cw: characterData.value.cref_cw,
|
||||
lora: characterData.value.lora,
|
||||
})
|
||||
})
|
||||
characterData.value["children"] = children;
|
||||
|
||||
debugger;
|
||||
console.log(characterData.value);
|
||||
characterData.value['type'] = "character_main";
|
||||
|
||||
// 开始保存
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(characterData.value), "character_tags"], (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
//保存成功在initFunc
|
||||
props.initFunc();
|
||||
// 清楚数据
|
||||
characterData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: "character_main",
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
cref_cw: 20,
|
||||
lora: null,
|
||||
};
|
||||
alias_tags.value = [];
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 翻译提示词
|
||||
async function TranslatePrompt() {
|
||||
loading.value = true;
|
||||
if (characterData.value.chinese_prompt == "" || characterData.value.chinese_prompt == null) {
|
||||
message.error("请输入中文提示词");
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
await window.api.TranslateReturnNow([characterData.value.chinese_prompt, 'zh', 'en', false], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
debugger
|
||||
//
|
||||
console.log(value.data);
|
||||
characterData.value.prompt = value.data[0].src;
|
||||
})
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == "")
|
||||
return new Error(errorMessage);
|
||||
return true;
|
||||
},
|
||||
trigger: ["input", "blur", "change"]
|
||||
}]
|
||||
}
|
||||
let rules = {
|
||||
label: ruleObj("必填人物名称"),
|
||||
prompt: ruleObj("必填英文提示词")
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
characterData,
|
||||
alias_tags,
|
||||
SaveCharacterTag,
|
||||
TranslatePrompt,
|
||||
loading,
|
||||
rules,
|
||||
formRef
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form label-placement="top">
|
||||
<n-form-item label="前缀名称">
|
||||
<n-input v-model:value="prefixData.label" placeholder="请输入场景名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="前缀提示词描述">
|
||||
<n-input type="textarea" :rows="3" v-model:value="prefixData.prompt" placeholder="请输入前缀提示词描述" />
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input v-model:value="prefixData.image_url" placeholder="请输入风格垫图链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="prefixData.image_url" :src="prefixData.image_url" style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="prefixData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="prefixData.lora" placeholder="请输入风格Lora" />
|
||||
<n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button>
|
||||
</n-form-item> -->
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SavePrefixTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NForm, NFormItem, NDynamicTags, NInputNumber } from "naive-ui";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentPrefix", "initFunc"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let prefixData = ref(props.currentPrefix);
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.currentPrefix, (value) => {
|
||||
prefixData.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
async function SavePrefixTag() {
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
prefixData.value['type'] = "prefix_main";
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(prefixData.value), "prefix_tags"], (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
//保存成功在initFunc
|
||||
props.initFunc();
|
||||
// 清楚数据
|
||||
prefixData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: "prefix_main",
|
||||
prompt: null,
|
||||
// image_url: null,
|
||||
// cref_cw: 20,
|
||||
// lora: null,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
prefixData,
|
||||
SavePrefixTag
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form label-placement="top" ref="formRef" :rules="rules" :model="sceneData">
|
||||
<n-form-item label="场景名称" path="label">
|
||||
<n-input v-model:value="sceneData.label" placeholder="请输入场景名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="场景提示词描述(中文)">
|
||||
<n-input type="textarea" :rows="3" v-model:value="sceneData.chinese_prompt" placeholder="请输入场景提示词描述" />
|
||||
<n-button type="info" size="small" style="margin-left: 20px;" @click="TranslatePrompt"
|
||||
:loading="loading">翻译提示词</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="场景提示词描述(英文)" path="prompt">
|
||||
<n-input type="textarea" :rows="3" v-model:value="sceneData.prompt" placeholder="请输入场景提示词描述" />
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input v-model:value="sceneData.image_url" placeholder="请输入风格垫图链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="sceneData.image_url" :src="sceneData.image_url" style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="sceneData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="sceneData.lora" placeholder="请输入风格Lora" />
|
||||
<n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button>
|
||||
</n-form-item> -->
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SaveSceneTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NForm, NFormItem, NDynamicTags, NInputNumber } from "naive-ui";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentScene", "initFunc"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let sceneData = ref(props.currentScene);
|
||||
let loading = ref(false);
|
||||
let formRef = ref(null)
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.currentScene, (value) => {
|
||||
sceneData.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
// 保存操作
|
||||
async function SaveSceneTag(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
sceneData.value['type'] = "scene_main";
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(sceneData.value), "scene_tags"], (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
//保存成功在initFunc
|
||||
props.initFunc();
|
||||
// 清楚数据
|
||||
sceneData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: "scene_main",
|
||||
prompt: null,
|
||||
// image_url: null,
|
||||
// cref_cw: 20,
|
||||
// lora: null,
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 翻译提示词
|
||||
async function TranslatePrompt() {
|
||||
loading.value = true;
|
||||
if (sceneData.value.chinese_prompt == "" || sceneData.value.chinese_prompt == null) {
|
||||
message.error("请输入中文提示词");
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
await window.api.TranslateReturnNow([sceneData.value.chinese_prompt, 'zh', 'en', false], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
//
|
||||
console.log(value.data);
|
||||
sceneData.value.prompt = value.data[0].src;
|
||||
})
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == "")
|
||||
return new Error(errorMessage);
|
||||
return true;
|
||||
},
|
||||
trigger: ["input", "blur", "change"]
|
||||
}]
|
||||
}
|
||||
let rules = {
|
||||
label: ruleObj("必填人物名称"),
|
||||
prompt: ruleObj("必填英文提示词")
|
||||
};
|
||||
|
||||
return {
|
||||
sceneData,
|
||||
SaveSceneTag,
|
||||
loading,
|
||||
TranslatePrompt,
|
||||
rules,
|
||||
formRef
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form label-placement="top" ref="formRef" :rules="rules" :model="styleData">
|
||||
<n-form-item label="风格名称" path="label">
|
||||
<n-input v-model:value="styleData.label" placeholder="请输入风格名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="风格提示词描述(中文)">
|
||||
<n-input type="textarea" :rows="3" v-model:value="styleData.chinese_prompt"
|
||||
placeholder="请输入风格提示词描述(中文)" />
|
||||
<n-button type="info" size="small" style="margin-left: 20px;" @click="TranslatePrompt"
|
||||
:loading="loading">翻译提示词</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="风格提示词描述(英文)" path ="prompt">
|
||||
<n-input type="textarea" :rows="3" v-model:value="styleData.prompt" placeholder="请输入风格提示词描述(英文)" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input v-model:value="styleData.image_url" placeholder="请输入风格垫图链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="styleData.image_url" :src="styleData.image_url" style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="styleData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="styleData.lora" placeholder="请输入风格Lora" />
|
||||
<!-- <n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button> -->
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SaveStyleTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NForm, NFormItem, NDynamicTags, NInputNumber } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentStyle", "initFunc"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let styleData = ref(props.currentStyle);
|
||||
let loading = ref(false);
|
||||
let formRef = ref(null)
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.currentStyle, (value) => {
|
||||
styleData.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
async function SaveStyleTag(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
styleData.value["type"] = "style_main";
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(styleData.value), "style_tags"], (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
//保存成功在initFunc
|
||||
props.initFunc();
|
||||
// 清楚数据
|
||||
styleData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: "style_main",
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
cref_cw: 20,
|
||||
lora: null,
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译中文提示词
|
||||
*/
|
||||
async function TranslatePrompt(){
|
||||
loading.value = true;
|
||||
if (styleData.value.chinese_prompt == "" || styleData.value.chinese_prompt == null) {
|
||||
message.error("请输入中文提示词");
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
await window.api.TranslateReturnNow([styleData.value.chinese_prompt, 'zh', 'en', false], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
//
|
||||
console.log(value.data);
|
||||
styleData.value.prompt = value.data[0].src;
|
||||
})
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == "")
|
||||
return new Error(errorMessage);
|
||||
return true;
|
||||
},
|
||||
trigger: ["input", "blur", "change"]
|
||||
}]
|
||||
}
|
||||
let rules = {
|
||||
label: ruleObj("必填人物名称"),
|
||||
prompt: ruleObj("必填英文提示词")
|
||||
};
|
||||
|
||||
return {
|
||||
styleData,
|
||||
SaveStyleTag,
|
||||
loading,
|
||||
rules,
|
||||
formRef,
|
||||
TranslatePrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div style="width: 500px;">
|
||||
<n-form label-placement="top">
|
||||
<n-form-item label="后缀名称">
|
||||
<n-input v-model:value="suffixData.label" placeholder="请输入后缀名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="后缀提示词描述">
|
||||
<n-input type="textarea" :rows="3" v-model:value="suffixData.prompt" placeholder="请输入后缀提示词描述" />
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="MJ:风格垫图链接(sref)">
|
||||
<n-input v-model:value="suffixData.image_url" placeholder="请输入风格垫图链接" style="margin-right: 20px;" />
|
||||
<n-image v-if="suffixData.image_url" :src="suffixData.image_url" style="width: 80px; height: 80px" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ:风格迁移 cw 值(0-1000)">
|
||||
<n-input-number :show-button="false" v-model:value="suffixData.sref_cw" min="0" max="1000"
|
||||
placeholder="请输入人物风格迁移的CW值" />
|
||||
</n-form-item>
|
||||
<n-form-item label="SD:Lora选择">
|
||||
<n-input v-model:value="suffixData.lora" placeholder="请输入风格Lora" />
|
||||
<n-button color="#e18a3b" style="margin-left: 20px;">选择</n-button>
|
||||
</n-form-item> -->
|
||||
</n-form>
|
||||
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SavePrefixTag">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NForm, NFormItem, NDynamicTags, NInputNumber } from "naive-ui";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NForm, NFormItem, NDynamicTags, NInputNumber
|
||||
},
|
||||
props: ["currentSuffix", "initFunc"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let suffixData = ref(props.currentSuffix);
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.currentSuffix, (value) => {
|
||||
suffixData.value = value;
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
async function SavePrefixTag() {
|
||||
// 直接保存
|
||||
// 开始保存
|
||||
suffixData.value['type'] = "suffix_main";
|
||||
await window.mj.SaveTagPropertyData([JSON.stringify(suffixData.value), "suffix_tags"], (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
//保存成功在initFunc
|
||||
props.initFunc();
|
||||
// 清楚数据
|
||||
suffixData.value = {
|
||||
label: null,
|
||||
key: null,
|
||||
type: "suffix_main",
|
||||
prompt: null,
|
||||
// image_url: null,
|
||||
// cref_cw: 20,
|
||||
// lora: null,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
suffixData,
|
||||
SavePrefixTag
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px;">
|
||||
<n-card>
|
||||
<n-tabs class="card-tabs" default-value="autoAnalyze" size="large" animated
|
||||
pane-wrapper-style="margin: 0 -4px"
|
||||
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;">
|
||||
<n-tab-pane name="autoAnalyze" tab="自动分析">
|
||||
<n-button color="#ee7959" size="medium" @click="autoAnalyzeCharacter"
|
||||
:loading="loading">自动分析</n-button>
|
||||
<n-input style="margin-top: 20px;" type="textarea" :autosize="{
|
||||
minRows: 3,
|
||||
}" placeholder="自动分析的数据" v-model:value="AnalyzeCharacter"></n-input>
|
||||
<div>
|
||||
<div style="color: red;">注意:上面推理的不满意可以手动修改</div>
|
||||
<div style="color: red;">自动推理的的人物只能对内置的四种模式生效</div>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<n-button type="success" @click="SaveAutoAnalyzeCharacter">保存</n-button>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="Analyze" tab="手动设置标签集">
|
||||
<CharacterTags :height="height"></CharacterTags>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NCard, NTabs, NTabPane, NTree } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import CharacterTags from "./CharacterTags.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NCard, NTabs, NTabPane, NTree, CharacterTags
|
||||
},
|
||||
props: ["initData", "height"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let data = ref(props.initData);
|
||||
let height = ref(props.height);
|
||||
let AnalyzeCharacter = ref("");
|
||||
let word = ref("");
|
||||
let loading = ref(false);
|
||||
let treeData = ref([{
|
||||
label: '人物',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
suffix: () => h(NButton,
|
||||
{ text: true, type: "primary" },
|
||||
{ default: () => "suffix" })
|
||||
},
|
||||
{
|
||||
label: '场景',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
suffix: () => h(NButton,
|
||||
{ text: true, type: "primary" },
|
||||
{ default: () => "suffix" })
|
||||
}]);
|
||||
|
||||
onMounted(async () => {
|
||||
let temp_arr = [];
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i];
|
||||
temp_arr.push(element.after_gpt);
|
||||
}
|
||||
word.value = temp_arr.join('\n');
|
||||
|
||||
// 判断是不是已经有生成(有有生成显示)
|
||||
await window.api.GetConfigJson(JSON.stringify(['auto_analyze_character', ""]), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
AnalyzeCharacter.value = value.data;
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 自动分析角色
|
||||
*/
|
||||
async function autoAnalyzeCharacter() {
|
||||
if (word.value == "" || word.value == null) {
|
||||
message.error("请输入要分析的文案");
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
|
||||
await window.api.AutoAnalyzeCharacter(toRaw(word.value), (value) => {
|
||||
debugger
|
||||
loading.value = false;
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
AnalyzeCharacter.value = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自动分析的设置
|
||||
*/
|
||||
async function SaveAutoAnalyzeCharacter() {
|
||||
if (AnalyzeCharacter.value == "" || AnalyzeCharacter.value == null) {
|
||||
message.error("没有分析分析出来的信息");
|
||||
return;
|
||||
}
|
||||
// 保存信息
|
||||
await window.api.SaveCopywritingInformation([toRaw(AnalyzeCharacter.value), "auto_analyze_character"], (value) => {
|
||||
if (value.code != 1) {
|
||||
message.error("未知错误");
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
treeData,
|
||||
word,
|
||||
height,
|
||||
loading,
|
||||
AnalyzeCharacter,
|
||||
autoAnalyzeCharacter,
|
||||
SaveAutoAnalyzeCharacter,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-tabs .n-tabs-nav--bar-type {
|
||||
padding-left: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,354 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<n-split direction="horizontal" :max="0.3" :min="0.18" :default-size="0.18">
|
||||
<template #1>
|
||||
<div id="tree_define_content" style="display: flex; overflow-y: auto;">
|
||||
<n-tree :node-props="nodeProps" style="width: 200px;" block-line :data="treeData" />
|
||||
</div>
|
||||
</template>
|
||||
<template #2>
|
||||
<div style="margin-left: 20px;">
|
||||
<AddCharacterTag v-if="currentType.startsWith('character')" :currentCharacter="currentCharacter"
|
||||
:currentTags="currentTags" :initFunc="InitData" />
|
||||
<AddStyleTags v-else-if="currentType.startsWith('style')" :currentStyle="currentStyle"
|
||||
:initFunc="InitData" />
|
||||
<AddSceneTags v-else-if="currentType.startsWith('scene')" :currentScene="currentScene"
|
||||
:initFunc="InitData" />
|
||||
<AddPrefixTags v-else-if="currentType.startsWith('prefix')" :currentPrefix="currentPrefix"
|
||||
:initFunc="InitData" />
|
||||
<AddSuffixTags v-else-if="currentType.startsWith('suffix')" :currentSuffix="currentSuffix"
|
||||
:initFunc="InitData" />
|
||||
</div>
|
||||
</template>
|
||||
</n-split>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from "vue"
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NCard, NTabs, NTabPane, NTree, NSplit, NIcon } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import AddCharacterTag from "./AddCharacterTag.vue";
|
||||
import AddStyleTags from "./AddStyleTags.vue";
|
||||
import AddSceneTags from "./AddSceneTags.vue";
|
||||
import AddPrefixTags from "./AddPrefixTags.vue";
|
||||
import AddSuffixTags from "./AddSuffixTags.vue";
|
||||
import { Trash } from "@vicons/ionicons5"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, NButton, NInput, NCard, NTabs, NTabPane, NTree, NSplit, AddCharacterTag, AddStyleTags, AddSceneTags, AddPrefixTags, AddSuffixTags, NIcon, Trash
|
||||
},
|
||||
props: ["height"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let currentCharacter = ref({});
|
||||
let currentStyle = ref({});
|
||||
let currentScene = ref({});
|
||||
let currentPrefix = ref({});
|
||||
let currentSuffix = ref({});
|
||||
let currentTags = ref([]);
|
||||
let currentType = ref("character");
|
||||
let treeData = ref([{
|
||||
label: '人物',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: "character",
|
||||
suffix: () => h(NButton,
|
||||
{
|
||||
text: true, color: "#e18a3b",
|
||||
onClick: (e) => { AddCharacterTags(e) }
|
||||
},
|
||||
{ default: () => "新加" })
|
||||
},
|
||||
{
|
||||
label: '场景',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: "scene",
|
||||
suffix: () => h(NButton,
|
||||
{
|
||||
text: true, color: "#e18a3b",
|
||||
onClick: (e) => { AddSceneTags(e) }
|
||||
},
|
||||
{ default: () => "新加" })
|
||||
},
|
||||
{
|
||||
label: '风格迁移预设',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: "style",
|
||||
suffix: () => h(NButton,
|
||||
{
|
||||
text: true, color: "#e18a3b",
|
||||
onClick: (e) => { AddStyleTags(e) }
|
||||
},
|
||||
{ default: () => "新加" })
|
||||
},
|
||||
{
|
||||
label: '前缀',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: "prefix",
|
||||
suffix: () => h(NButton,
|
||||
{
|
||||
text: true, color: "#e18a3b",
|
||||
onClick: (e) => { AddPrefixTags(e) }
|
||||
},
|
||||
{ default: () => "新加" })
|
||||
},
|
||||
{
|
||||
label: '后缀',
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: "suffix",
|
||||
suffix: () => h(NButton,
|
||||
{
|
||||
text: true, color: "#e18a3b",
|
||||
onClick: (e) => { AddSuffixTags(e) }
|
||||
},
|
||||
{ default: () => "新加" })
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* 初始化标签数据
|
||||
*/
|
||||
async function InitData() {
|
||||
// 获取动态配置里面是不是又标签数(有加载,没有设置默认值)
|
||||
await window.mj.GetTagDataByTypeAndProperty(["dynamic", null], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// 判断是不是包含指定的属性
|
||||
if (value.data.hasOwnProperty("character_tags")) {
|
||||
treeData.value[0].children = value.data.character_tags;
|
||||
}
|
||||
if (value.data.hasOwnProperty("scene_tags")) {
|
||||
treeData.value[1].children = value.data.scene_tags;
|
||||
}
|
||||
if (value.data.hasOwnProperty("style_tags")) {
|
||||
treeData.value[2].children = value.data.style_tags;
|
||||
}
|
||||
if (value.data.hasOwnProperty("prefix_tags")) {
|
||||
treeData.value[3].children = value.data.prefix_tags;
|
||||
}
|
||||
if (value.data.hasOwnProperty("suffix_tags")) {
|
||||
treeData.value[4].children = value.data.suffix_tags;
|
||||
}
|
||||
|
||||
for (let i = 0; i < treeData.value.length; i++) {
|
||||
treeData.value[i].children.map(item => {
|
||||
item.suffix = () => h(NButton,
|
||||
{
|
||||
text: true, type: "error", size: "medium",
|
||||
onClick: (e) => { DeleteTags(e, item) }
|
||||
},
|
||||
{ default: () => h(NIcon, null, { default: () => h(Trash) }) });
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
SetAutoHeight();
|
||||
await InitData();
|
||||
})
|
||||
|
||||
|
||||
// 设置标签集的自动高度,传一个外面的高度进来
|
||||
function SetAutoHeight() {
|
||||
debugger
|
||||
let div = document.getElementById("tree_define_content");
|
||||
div.style.height = props.height - 180 + "px";
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的数据
|
||||
*/
|
||||
async function DeleteTags(e, value) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
let type = value.type.replace("main", "tags");
|
||||
await window.mj.DeleteTagPropertyData([value.key, type], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
await InitData();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加人物标签
|
||||
*/
|
||||
async function AddCharacterTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
// 初始化值,然后赋值给当前添加人物的组件
|
||||
currentCharacter.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
children: [],
|
||||
type: "character_main",
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
cref_cw: 50,
|
||||
lora: null,
|
||||
}
|
||||
currentTags.value = [];
|
||||
currentType.value = "character_main";
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加场景标签
|
||||
*/
|
||||
function AddSceneTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
currentScene.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: "scene_main",
|
||||
prompt: null,
|
||||
}
|
||||
currentType.value = "scene_main";
|
||||
}
|
||||
|
||||
/**
|
||||
* 新加 风格迁移预设
|
||||
* @param {*} e
|
||||
*/
|
||||
function AddStyleTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
currentStyle.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: "style_main",
|
||||
prompt: null,
|
||||
image_url: null,
|
||||
sref_cw: 50,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前缀
|
||||
*/
|
||||
function AddPrefixTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
currentPrefix.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: "prefix_main",
|
||||
prompt: null,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加后缀
|
||||
* @param {*} e
|
||||
*/
|
||||
function AddSuffixTags(e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
currentSuffix.value = {
|
||||
label: null,
|
||||
key: uuidv4(),
|
||||
type: "suffix_main",
|
||||
prompt: null,
|
||||
}
|
||||
}
|
||||
|
||||
function nodeProps({ option }) {
|
||||
return {
|
||||
onClick() {
|
||||
debugger;
|
||||
// 执行切换组件和数据
|
||||
if (option.type == "style") {
|
||||
currentType.value = "style";
|
||||
AddStyleTags(null);
|
||||
return;
|
||||
} else if (option.type == "scene") {
|
||||
AddSceneTags(null);
|
||||
currentType.value = "scene";
|
||||
// message.error("主节点不允许操作");
|
||||
return;
|
||||
} else if (option.type == "character") {
|
||||
AddCharacterTags(null);
|
||||
currentType.value = "character";
|
||||
return;
|
||||
} else if (option.type == "prefix") {
|
||||
AddPrefixTags(null);
|
||||
currentType.value = "prefix";
|
||||
return;
|
||||
} else if (option.type == "suffix") {
|
||||
AddSuffixTags(null);
|
||||
currentType.value = "suffix";
|
||||
return;
|
||||
}
|
||||
else if (option.type == "min") {
|
||||
message.error("别名节点不允许操作");
|
||||
} else {
|
||||
debugger
|
||||
console.log(option);
|
||||
currentType.value = option.type;
|
||||
// 修改当前属性
|
||||
if (currentType.value.startsWith("character")) {
|
||||
// 修改tags的值
|
||||
currentCharacter.value = option;
|
||||
currentTags.value = option.children.map((item) => {
|
||||
return item.label;
|
||||
})
|
||||
} else if (currentType.value.startsWith("scene")) {
|
||||
currentScene.value = option;
|
||||
}
|
||||
else if (currentType.value.startsWith("style")) {
|
||||
currentStyle.value = option;
|
||||
} else if (currentType.value.startsWith("prefix")) {
|
||||
currentPrefix.value = option;
|
||||
} else if (currentType.value.startsWith("suffix")) {
|
||||
currentSuffix.value = option;
|
||||
}
|
||||
else {
|
||||
message.error("未知的类型");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
treeData,
|
||||
AddCharacterTags,
|
||||
AddSceneTags,
|
||||
AddStyleTags,
|
||||
nodeProps,
|
||||
currentCharacter,
|
||||
currentTags,
|
||||
InitData,
|
||||
currentType,
|
||||
currentStyle,
|
||||
currentScene,
|
||||
currentPrefix,
|
||||
currentSuffix
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'" style="display: flex">
|
||||
<span style="margin-right: 5px">参数</span>
|
||||
<n-select
|
||||
style="width: 75px"
|
||||
size="tiny"
|
||||
:options="characterSelectMoedlOptions"
|
||||
@update:value="UpdateImageGenerateCategory"
|
||||
v-model:value="title_character_select_model"
|
||||
placeholder="选择模式"
|
||||
></n-select>
|
||||
<n-button size="tiny" color="#e18a3b" style="margin-left: 5px" @click="RefreshTagData">
|
||||
<n-icon>
|
||||
<reload />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<div v-if="title_character_select_model == 'tag'">
|
||||
<div style="min-height: 65px; overflow-y: auto; width: 190px">
|
||||
<span>人物:</span>
|
||||
<n-tag
|
||||
v-for="(character, index) in character_tags"
|
||||
v-model:checked="character.checked"
|
||||
checkable
|
||||
@click="UpdateCharacterTags"
|
||||
size="small"
|
||||
style="margin: 0 3px 3px 0"
|
||||
>
|
||||
{{ character.label }}
|
||||
</n-tag>
|
||||
</div>
|
||||
<div style="margin-top: 5px; min-height: 65px; overflow-y: auto">
|
||||
<span>场景:</span>
|
||||
<n-tag
|
||||
v-for="(scene, index) in scene_tags"
|
||||
v-model:checked="scene.checked"
|
||||
checkable
|
||||
@click="UpdateSceneTags()"
|
||||
size="small"
|
||||
style="margin: 0 3px 3px 0"
|
||||
>
|
||||
{{ scene.label }}
|
||||
</n-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>
|
||||
<div>人物:</div>
|
||||
<n-select
|
||||
style="width: 190px"
|
||||
size="tiny"
|
||||
:render-tag="renderTag"
|
||||
v-model:value="select_character_tags"
|
||||
multiple
|
||||
filterable
|
||||
:options="character_tags"
|
||||
placeholder="选择人物"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-top: 3px">
|
||||
<div>场景:</div>
|
||||
<n-select
|
||||
style="width: 190px"
|
||||
size="tiny"
|
||||
:render-tag="renderTag"
|
||||
v-model:value="select_scene_tags"
|
||||
multiple
|
||||
filterable
|
||||
:options="scene_tags"
|
||||
placeholder="选择场景"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import { NImage, useMessage, NButton, useDialog, NInput, NTag, NSelect, NIcon } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import { cloneDeep, debounce, get, once } from 'lodash'
|
||||
import { Refresh, Reload } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput,
|
||||
NTag,
|
||||
NSelect,
|
||||
NIcon,
|
||||
Reload
|
||||
},
|
||||
props: ['type', 'row', 'characterSelectModel', 'tags', 'func'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let type = ref(props.type)
|
||||
let title_character_select_model = ref(props.characterSelectModel)
|
||||
let row = ref(props.row)
|
||||
let character_tags = ref([])
|
||||
let scene_tags = ref([])
|
||||
let tags = ref(props.tags)
|
||||
|
||||
let characterData = ref([])
|
||||
|
||||
// 存储当权选择的tag数据
|
||||
let select_character_tags = ref([])
|
||||
let select_scene_tags = ref([])
|
||||
|
||||
let characterSelectMoedlOptions = ref([])
|
||||
watch(
|
||||
() => props.characterSelectModel,
|
||||
(value) => {
|
||||
debugger
|
||||
// InitData(value);
|
||||
if (value.value == 'drop') {
|
||||
select_character_tags.value = []
|
||||
select_scene_tags.value = []
|
||||
|
||||
character_tags.value.forEach((item) => {
|
||||
if (item.checked) select_character_tags.value.push(item.key)
|
||||
})
|
||||
scene_tags.value.forEach((item) => {
|
||||
if (item.checked) select_scene_tags.value.push(item.key)
|
||||
})
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.tags,
|
||||
async (value) => {
|
||||
// 刷新数据
|
||||
await InitCharacterAndSceneData()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.row,
|
||||
async (value) => {
|
||||
row.value = value
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
// debugger
|
||||
// await InitCharacterAndSceneData()
|
||||
await window.mj.GetTagSelectModel((value) => {
|
||||
characterSelectMoedlOptions.value = value.data
|
||||
})
|
||||
})
|
||||
|
||||
// 更行当前行选中的人物角色tag数据
|
||||
async function UpdateCharacterTags(value, option) {
|
||||
console.log(value, option)
|
||||
if (title_character_select_model.value == 'tag') {
|
||||
row.value.character_tags = []
|
||||
// 循环当前界面,每次全部更新。
|
||||
character_tags.value.forEach((item) => {
|
||||
if (item.checked) {
|
||||
row.value.character_tags.push(item)
|
||||
} else {
|
||||
// 移除
|
||||
let index = row.value.character_tags.findIndex((tag) => tag.key == item.key)
|
||||
if (index != -1) {
|
||||
row.value.character_tags.splice(index, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 修改当前的场景信息tags数据
|
||||
async function UpdateSceneTags() {
|
||||
row.value.scene_tags = []
|
||||
scene_tags.value.forEach((item) => {
|
||||
if (item.checked) {
|
||||
row.value.scene_tags.push(item)
|
||||
} else {
|
||||
// 移除
|
||||
let index = row.value.scene_tags.findIndex((tag) => tag.key == item.key)
|
||||
if (index != -1) {
|
||||
row.value.scene_tags.splice(index, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染tag
|
||||
const renderTag = ({ option, handleClose }) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
size: 'tiny',
|
||||
type: 'success',
|
||||
closable: true,
|
||||
onClose: (e) => {
|
||||
e.stopPropagation()
|
||||
handleClose()
|
||||
// 移除当前option
|
||||
let k = option.key
|
||||
character_tags.value.forEach((item) => {
|
||||
if (item.key == k) {
|
||||
item.checked = false
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ default: () => option.label }
|
||||
)
|
||||
}
|
||||
/**
|
||||
* 加载人物场景等信息
|
||||
* @param {*} tags
|
||||
*/
|
||||
async function InitCharacterAndSceneData() {
|
||||
debugger
|
||||
// if (!tags) return
|
||||
character_tags.value = cloneDeep(tags.value.character_tags)
|
||||
character_tags.value.forEach((item) => {
|
||||
item.children = null
|
||||
})
|
||||
scene_tags.value = cloneDeep(tags.value.scene_tags)
|
||||
|
||||
select_character_tags.value = []
|
||||
select_scene_tags.value = []
|
||||
// 初始化数据(选择为true)
|
||||
for (let i = 0; row.value.character_tags && i < row.value.character_tags.length; i++) {
|
||||
const element = row.value.character_tags[i]
|
||||
let index = character_tags.value.findIndex((tag) => tag.key == element.key)
|
||||
if (index != -1) {
|
||||
character_tags.value[index].checked = true
|
||||
// 将当前选中的数据生成到select_character_tags中
|
||||
select_character_tags.value.push(character_tags.value[index].key)
|
||||
}
|
||||
}
|
||||
for (let i = 0; row.value.scene_tags && i < row.value.scene_tags.length; i++) {
|
||||
const element = row.value.scene_tags[i]
|
||||
let index = scene_tags.value.findIndex((tag) => tag.key == element.key)
|
||||
if (index != -1) {
|
||||
scene_tags.value[index].checked = true
|
||||
// 将当前选中的数据生成到select_scene_tags中
|
||||
select_scene_tags.value.push(scene_tags.value[index].key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改属性
|
||||
* @param {*} value
|
||||
*/
|
||||
function UpdateImageGenerateCategory(value) {
|
||||
title_character_select_model.value = value
|
||||
// InitData(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新标签数据
|
||||
*/
|
||||
async function RefreshTagData() {
|
||||
props.func.refreshTagData()
|
||||
}
|
||||
|
||||
return {
|
||||
characterData,
|
||||
row,
|
||||
character_tags,
|
||||
select_character_tags,
|
||||
select_scene_tags,
|
||||
UpdateCharacterTags,
|
||||
UpdateSceneTags,
|
||||
scene_tags,
|
||||
renderTag,
|
||||
type,
|
||||
characterSelectMoedlOptions,
|
||||
InitCharacterAndSceneData,
|
||||
UpdateImageGenerateCategory,
|
||||
title_character_select_model,
|
||||
RefreshTagData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'">
|
||||
<span style="margin-right: 5px">推理提示词</span>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button color="#7c461e" style="margin-left: 5px" size="tiny" @click="AddPrefix"
|
||||
>添加前缀</n-button
|
||||
>
|
||||
</template>
|
||||
<span>添加前缀,只能添加英文,并且通用前缀不为空,推理会自动添加前缀</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button color="#e18a3b" style="margin-left: 5px" size="tiny" @click="AddSuffix"
|
||||
>添加后缀</n-button
|
||||
>
|
||||
</template>
|
||||
<span>添加后缀,只能添加英文,并且通用后缀不为空,推理会自动添加后缀</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
color="#c0d695"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="SelectGenerateImagesStyle"
|
||||
>风格</n-button
|
||||
>
|
||||
</template>
|
||||
<span>选择生成图片的风格</span>
|
||||
</n-popover>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<span>
|
||||
<n-dropdown trigger="hover" :options="translate_options" @select="TranslateAll">
|
||||
<n-button
|
||||
color="#779649"
|
||||
style="margin-left: 5px"
|
||||
size="tiny"
|
||||
@click="TranslateAll('english')"
|
||||
>一键翻译</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
</span>
|
||||
</template>
|
||||
<span
|
||||
>将下面的推理提示词或者是手写的提示词翻译成英文(只会翻译存在中文的数据),下面的翻译中文会将所有的推理提示词翻译为中文(全部都会翻译)</span
|
||||
>
|
||||
</n-popover>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<n-dropdown trigger="hover" :options="translate_options" @select="TranslateSelect">
|
||||
<n-button
|
||||
size="tiny"
|
||||
color="#dd7694"
|
||||
style="margin-right: 5px"
|
||||
@click="TranslateSelect('english')"
|
||||
>翻译英文</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
<n-button size="tiny" color="#a76283" style="margin-right: 5px" @click="SinglePrompt"
|
||||
>单句推理</n-button
|
||||
>
|
||||
<n-button color="#a46244" style="margin-right: 5px" size="tiny" @click="NextGptPrompt"
|
||||
>下推理</n-button
|
||||
>
|
||||
<n-input
|
||||
size="tiny"
|
||||
placeholder="请生成或推理提示词"
|
||||
type="textarea"
|
||||
v-model:value="row.gpt_prompt"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import { NButton, NDropdown, NInput, NPopover, useMessage } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NInput,
|
||||
NPopover,
|
||||
NDropdown
|
||||
},
|
||||
props: ['type', 'row', 'index', 'func'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let type = ref(props.type)
|
||||
let row = ref(props.row)
|
||||
let translate_options = ref([{ label: '翻译中文', key: 'chinese' }])
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.row,
|
||||
(newVal) => {
|
||||
row.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
/**
|
||||
* 单句生图
|
||||
*/
|
||||
async function SingleGenerateImage() {
|
||||
await window.api.OriginalSDImageGenerate([JSON.stringify([row]), true, false], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加生图任务成功')
|
||||
})
|
||||
}
|
||||
|
||||
function containsChineseOrPunctuation(str) {
|
||||
return /[\u4e00-\u9fa5]|[\u3000-\u301e\u2013\u2014\u2018\u2019\u201c\u201d\u2026\u203b\uff08\uff09\uff1a\uff1b\uff1f\uff01\uff0c\u3001\uff0e\u3002\uff1f\uff01\u2018\u2019\u201c\u201d]/.test(
|
||||
str
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译选择
|
||||
*/
|
||||
async function TranslateSelect(key) {
|
||||
// 将中文翻译成英文
|
||||
if (key == 'english') {
|
||||
// 检查是不是有中文
|
||||
if (containsChineseOrPunctuation(row.value.gpt_prompt)) {
|
||||
// 开始翻译
|
||||
let t = [[toRaw(row.value)], 'zh', 'en', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else {
|
||||
message.error('当前翻译的数据里面没有中文')
|
||||
}
|
||||
} else if (key == 'chinese') {
|
||||
debugger
|
||||
let t = [[toRaw(row.value)], 'en', 'zh', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译所有的数据
|
||||
* @param {*} key
|
||||
*/
|
||||
async function TranslateAll(key) {
|
||||
props.func.translateAll(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前缀
|
||||
*/
|
||||
async function AddPrefix() {
|
||||
props.func.addPrefix()
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加后缀
|
||||
*/
|
||||
async function AddSuffix() {
|
||||
props.func.addSuffix()
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择风格
|
||||
*/
|
||||
async function SelectGenerateImagesStyle() {
|
||||
props.func.selectGenerateImagesStyle()
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句推理
|
||||
*/
|
||||
async function SinglePrompt() {
|
||||
await props.func.singlePrompt(row.value)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 下推理
|
||||
*/
|
||||
async function NextGptPrompt() {
|
||||
await props.func.nextGptPrompt(row.value)
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
row,
|
||||
SingleGenerateImage,
|
||||
translate_options,
|
||||
TranslateSelect,
|
||||
TranslateAll,
|
||||
AddPrefix,
|
||||
AddSuffix,
|
||||
SelectGenerateImagesStyle,
|
||||
SinglePrompt,
|
||||
NextGptPrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'">
|
||||
<span style="margin-right: 5px">参数</span>
|
||||
</div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('sd')">
|
||||
<n-checkbox
|
||||
style="margin-left: 5px; height: 100%"
|
||||
:checked="row.adetailer"
|
||||
label="修脸"
|
||||
@update:checked="onUpdateChecked"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('mj')"></div>
|
||||
<div v-else-if="type.startsWith('data') && type.endsWith('d3')"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch, computed } from "vue";
|
||||
import { NButton, NCheckbox, NInput, useMessage } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NInput,
|
||||
NCheckbox,
|
||||
},
|
||||
props: ["type", "row", "index", "func"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let type = ref(props.type);
|
||||
let row = ref(props.row);
|
||||
let input_status = ref("default");
|
||||
|
||||
// 重新计算命令
|
||||
watch(
|
||||
() => props.type,
|
||||
(newVal) => {
|
||||
type.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.row,
|
||||
(newVal) => {
|
||||
row.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
watch(
|
||||
() => props.row.mj_message,
|
||||
(newVal) => {
|
||||
if (newVal && newVal["hasBadPrompt"]) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
if (row.value["mj_message"]?.hasBadPrompt) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
});
|
||||
|
||||
async function onUpdateChecked(v) {
|
||||
debugger;
|
||||
row.adetailer = v;
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
row,
|
||||
onUpdateChecked,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'">
|
||||
<span style="margin-right: 5px">提示词命令</span>
|
||||
<n-button size="tiny" @click="MergePrompt" color="#7c461e">合并命令</n-button>
|
||||
<n-button
|
||||
color="#e18a3b"
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="MJBadPromptCheck"
|
||||
>敏感词检查</n-button
|
||||
>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<n-button
|
||||
size="tiny"
|
||||
color="#dd7694"
|
||||
style="margin-right: 5px"
|
||||
@click="SingleGenerateImage"
|
||||
>单句生图</n-button
|
||||
>
|
||||
<n-button
|
||||
size="tiny"
|
||||
color="#a76283"
|
||||
style="margin-right: 5px"
|
||||
@click="NextGenerateImage"
|
||||
>下生图</n-button
|
||||
>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
color="#a46244"
|
||||
style="margin-right: 5px"
|
||||
size="tiny"
|
||||
@click="ImportMJImageUrl"
|
||||
>导入图片</n-button
|
||||
>
|
||||
</template>
|
||||
<span>粘贴MJ里面的图片链接。自动下载到对应的行。并分割。</span>
|
||||
</n-popover>
|
||||
|
||||
<n-input
|
||||
:status="input_status"
|
||||
readonly
|
||||
size="tiny"
|
||||
placeholder="请生成出图命令或是提示词"
|
||||
type="textarea"
|
||||
v-model:value="row.prompt"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from "vue";
|
||||
import { NButton, NInput, NPopover, useMessage, useDialog } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../../define/define_string";
|
||||
import InputDialogContent from "./InputDialogContent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NInput,
|
||||
NPopover,
|
||||
},
|
||||
props: ["type", "row", "index", "func"],
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let dialog = useDialog();
|
||||
let type = ref(props.type);
|
||||
let row = ref(props.row);
|
||||
let input_status = ref("default");
|
||||
let image_url_ref = ref(null);
|
||||
|
||||
// let input_image_
|
||||
|
||||
// 重新计算命令
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.row,
|
||||
(newVal) => {
|
||||
row.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.row.mj_message,
|
||||
(newVal) => {
|
||||
if (newVal && newVal["hasBadPrompt"]) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
if (row.value["mj_message"]?.hasBadPrompt) {
|
||||
input_status.value = "error";
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* SD单句生图
|
||||
* @param {*} row 要生图的行数据
|
||||
*/
|
||||
async function SingleGenerateImage() {
|
||||
debugger;
|
||||
let ca = window.config.image_generate_category;
|
||||
// 判断当前的生图模式
|
||||
if (ca == "sd") {
|
||||
// 生成SD生图
|
||||
await window.api.OriginalSDImageGenerate(
|
||||
[JSON.stringify([toRaw(row.value)]), true, false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加SD生图任务成功");
|
||||
}
|
||||
);
|
||||
} else if (ca == "mj") {
|
||||
// MJ生图
|
||||
await window.mj.OriginalMJImageGenerate(
|
||||
[JSON.stringify([toRaw(row.value)]), true, false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加MJ生图任务成功");
|
||||
}
|
||||
);
|
||||
} else if (ca == "d3") {
|
||||
// D3 生图
|
||||
} else {
|
||||
message.error("生图的类别错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下生图
|
||||
*/
|
||||
async function NextGenerateImage() {
|
||||
props.func.nextGenerateImage(row.value, window.config.image_generate_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* MJ敏感词检测
|
||||
*/
|
||||
async function MJBadPromptCheck() {
|
||||
props.func.mJBadPromptCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并提示词
|
||||
*/
|
||||
async function MergePrompt() {
|
||||
props.func.mergePrompt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入图片的链接,将图片链接下载,并且分割
|
||||
*/
|
||||
async function ImportMJImageUrl() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400;
|
||||
let dialogHeight = 150;
|
||||
dialog.create({
|
||||
title: "添加图片链接",
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: image_url_ref,
|
||||
initData: null,
|
||||
placeholder: "请输入图片链接",
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
debugger;
|
||||
let row_image_url = image_url_ref.value.data;
|
||||
|
||||
// 下载指定的图片地址并且分割
|
||||
await window.mj.DownloadImageUrlAndSplit(
|
||||
JSON.stringify([toRaw(row.value), row_image_url]),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// 更新当前的数据
|
||||
row.value.outImagePath = value.data.outImagePath;
|
||||
row.value.subImagePath = value.data.subImagePath;
|
||||
row.value.mj_message = value.data.mj_message ? value.data.mj_message : {};
|
||||
row.value.mj_message.image_click = value.data.image_click;
|
||||
row.value.mj_message.image_path = value.data.image_path;
|
||||
row.value.mj_message.progress = 100;
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
row,
|
||||
SingleGenerateImage,
|
||||
NextGenerateImage,
|
||||
MJBadPromptCheck,
|
||||
input_status,
|
||||
MergePrompt,
|
||||
ImportMJImageUrl,
|
||||
image_url_ref,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div v-if="type == 'title'" style="display: flex">
|
||||
<span>出图</span>
|
||||
<n-select
|
||||
style="width: 70px; margin-left: 5px"
|
||||
size="tiny"
|
||||
:options="image_options"
|
||||
@update:value="UpdateImageGenerateCategory"
|
||||
v-model:value="select_image_category"
|
||||
placeholder="选择生图模式"
|
||||
></n-select>
|
||||
<n-button
|
||||
v-if="select_image_category == 'mj'"
|
||||
color="#7c461e"
|
||||
size="tiny"
|
||||
style="margin-left: 5px"
|
||||
@click="MJGetImage"
|
||||
>MJ采集图片</n-button
|
||||
>
|
||||
</div>
|
||||
<div v-else-if="type == 'data'">
|
||||
<div style="height: 22px">进度:{{ data.mj_message?.progress }}%</div>
|
||||
<div
|
||||
style="display: flex; margin-right: 10px; height: auto; min-height: 120px"
|
||||
@dragstart="imageDragStart"
|
||||
@drop="imageDragDrop"
|
||||
@dragover="imageDragOver"
|
||||
>
|
||||
<div style="display: flex; align-items: center">
|
||||
<n-image
|
||||
class="g-image-class generate-image-show"
|
||||
v-if="outImagePath"
|
||||
:src="outImagePath"
|
||||
fit="cover"
|
||||
width="120px"
|
||||
height="120px"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<n-divider vertical style="height: 100%" />
|
||||
</div>
|
||||
<div style="display: flex; align-items: center">
|
||||
<n-image-group>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center">
|
||||
<n-image
|
||||
subImage="true"
|
||||
class="g-image-class"
|
||||
draggable="true"
|
||||
:width="width"
|
||||
style="margin: 1px"
|
||||
v-for="(image, index) in images"
|
||||
:key="image.id"
|
||||
:src="image"
|
||||
alt="图片描述"
|
||||
lazy
|
||||
>
|
||||
</n-image>
|
||||
</div>
|
||||
</n-image-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
|
||||
import { NImage, useMessage, NDivider, NImageGroup, NButton, NSelect } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NDivider,
|
||||
NImageGroup,
|
||||
NButton,
|
||||
NSelect
|
||||
},
|
||||
props: ['initData', 'type', 'image_generate_category', 'func'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let data = ref(props.initData)
|
||||
let type = ref(props.type)
|
||||
let images = ref(props.initData.subImagePath)
|
||||
let outImagePath = ref(props.initData.outImagePath)
|
||||
let sd_batch_size = ref(null)
|
||||
let width = ref(null)
|
||||
|
||||
let dragTarget = null
|
||||
|
||||
let image_options = ref([])
|
||||
let select_image_category = ref(props.image_generate_category)
|
||||
|
||||
watch(
|
||||
() => select_image_category.value,
|
||||
(value) => {
|
||||
select_image_category.value = value
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 props.initData.subImagePath 修改
|
||||
watch(
|
||||
() => props.initData.subImagePath,
|
||||
(value) => {
|
||||
images.value = value
|
||||
width.value = 170 / 3
|
||||
setNodeAttribute()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 props.initData.outImagePath 修改
|
||||
watch(
|
||||
() => props.initData.outImagePath,
|
||||
(value) => {
|
||||
outImagePath.value = value
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 props.initData 修改
|
||||
watch(
|
||||
() => props.initData,
|
||||
(value) => {
|
||||
data.value = value
|
||||
setNodeAttribute()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.InitSDConfig((value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
sd_batch_size.value = value.data.batch_size
|
||||
width.value = 170 / 3
|
||||
console.log(width.value)
|
||||
})
|
||||
setNodeAttribute()
|
||||
|
||||
// 获取生图下拉列表的方式
|
||||
await window.api.GetImageGenerateCategory((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
image_options.value = value.data
|
||||
})
|
||||
})
|
||||
|
||||
async function setNodeAttribute() {
|
||||
let ele = document.querySelectorAll('.g-image-class')
|
||||
ele.forEach((item) => {
|
||||
item.setAttribute('data-id', data.value.id)
|
||||
Array.from(item.getElementsByTagName('img')).forEach((img) => {
|
||||
img.setAttribute('data-id', data.value.id)
|
||||
})
|
||||
})
|
||||
let mainImage = document.querySelectorAll('.generate-image-show')
|
||||
mainImage.forEach((item) => {
|
||||
item.setAttribute('mainImage', '1')
|
||||
Array.from(item.getElementsByTagName('img')).forEach((img) => {
|
||||
img.setAttribute('mainImage', '1')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 图片拖拽开始事件
|
||||
async function imageDragStart(e) {
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
console.log('move', e.target)
|
||||
dragTarget = e.target
|
||||
}
|
||||
|
||||
// 图片拖拽结束事件
|
||||
async function imageDragDrop(e) {
|
||||
debugger
|
||||
console.log('end', e.target, dragTarget)
|
||||
// 判断原始数据是不是为空
|
||||
if (dragTarget == null) {
|
||||
message.error('只能拖拽到当前行的图片上')
|
||||
return
|
||||
}
|
||||
// 判断结束位置是不是主图片
|
||||
if (e.target.getAttribute('mainImage') != '1') {
|
||||
message.error('图片只能拖拽到主图片上')
|
||||
return
|
||||
}
|
||||
let source_img = dragTarget.src
|
||||
.split('?')[0]
|
||||
.replace(/^file:\/\/\//, '')
|
||||
.replace(/^\//, '')
|
||||
let target_img = e.target.src
|
||||
.split('?')[0]
|
||||
.replace(/^file:\/\/\//, '')
|
||||
.replace(/^\//, '')
|
||||
// 修改主图片
|
||||
await window.api.DownloadImageFile([source_img, 'replace', target_img], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
data.value.outImagePath =
|
||||
e.target.src.split('?')[0].replaceAll('\\', '/') + '?time=' + new Date().getTime()
|
||||
})
|
||||
}
|
||||
|
||||
// 图片拖拽进入事件
|
||||
async function imageDragOver(e) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
async function UpdateImageGenerateCategory(value, option) {
|
||||
// 更新配置文件和缓存
|
||||
window.config.image_generate_category = value
|
||||
// 保存文件
|
||||
await window.api.ModifySampleSetting(
|
||||
JSON.stringify({
|
||||
image_generate_category: value
|
||||
}),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('修改成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* MJ采集图片
|
||||
*/
|
||||
async function MJGetImage() {
|
||||
props.func.mJGetImage()
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
type,
|
||||
image_options,
|
||||
sd_batch_size,
|
||||
width,
|
||||
images,
|
||||
outImagePath,
|
||||
imageDragStart,
|
||||
imageDragDrop,
|
||||
imageDragOver,
|
||||
select_image_category,
|
||||
UpdateImageGenerateCategory,
|
||||
MJGetImage
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px">
|
||||
<n-input
|
||||
v-model:value="word"
|
||||
:autosize="{ minRows: 30, maxRows: 30 }"
|
||||
type="textarea"
|
||||
placeholder="输入分镜文案"
|
||||
@input="ChangeWordInput"
|
||||
/>
|
||||
<div style="margin-top: 5px; display: flex">
|
||||
<div style="flex: 1">共 {{ rowCount }} 行分镜(当前数量不是最后数量,最后会删除空行)</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw } from 'vue'
|
||||
import { NImage, useMessage, NButton, useDialog, NInput } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
NInput
|
||||
},
|
||||
props: ['initData'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let rowCount = ref(0)
|
||||
let data = ref(props.initData)
|
||||
let word = ref(null)
|
||||
let word_arr = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
debugger
|
||||
console.log(data)
|
||||
// 拼接word
|
||||
let tmp_arr = []
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i]
|
||||
tmp_arr.push(item.after_gpt)
|
||||
}
|
||||
word.value = tmp_arr.join('\n')
|
||||
ChangeWordInput()
|
||||
})
|
||||
|
||||
/**
|
||||
* 修改数据框时触发的事件
|
||||
*/
|
||||
async function ChangeWordInput() {
|
||||
let word_arr = word.value.split('\n')
|
||||
// 先判断是不是有空行
|
||||
if (rowCount.value == 1 && word_arr[0] == '') {
|
||||
rowCount.value = 0
|
||||
} else {
|
||||
rowCount.value = word_arr.length
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
rowCount,
|
||||
ChangeWordInput,
|
||||
word,
|
||||
word_arr
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,562 @@
|
||||
<template>
|
||||
<div id="import_word_and_srt">
|
||||
<div style="height: 30px; margin-bottom: 5px">
|
||||
<n-button color="#1e90ff" size="small" style="margin-left: 20px" @click="OpenOneDialog"
|
||||
>导入字幕</n-button
|
||||
>
|
||||
<n-button color="#1e90ff" size="small" style="margin-left: 20px" @click="ImportSrtAndGetTime"
|
||||
>导入srt</n-button
|
||||
>
|
||||
<n-button
|
||||
color="#1e90ff"
|
||||
size="small"
|
||||
style="margin-left: 20px"
|
||||
@click="SaveCopywritingInformation"
|
||||
>保存文案信息</n-button
|
||||
>
|
||||
</div>
|
||||
<n-data-table
|
||||
:max-height="maxHeight"
|
||||
:row-key="rowKey"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
@update:checked-row-keys="handleCheck"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { h, defineComponent, onMounted, ref, toRaw, watch } from 'vue'
|
||||
import { NButton, NDataTable, useMessage, NInput, NImage, useDialog } from 'naive-ui'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import EditWord from './EditWord.vue'
|
||||
const buttonArr = [
|
||||
{
|
||||
title: '下对齐',
|
||||
key: 'AlignNextWord',
|
||||
color: '#8a2be0'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton,
|
||||
NDataTable,
|
||||
NInput,
|
||||
NImage
|
||||
},
|
||||
props: ['initData', 'menuType', 'height'],
|
||||
setup(props) {
|
||||
let hh = props.height
|
||||
let maxHeight = props.height - 80
|
||||
let data = ref(props.initData)
|
||||
const message = useMessage()
|
||||
let selectKey = ref([])
|
||||
let dialog = useDialog()
|
||||
let editWordRef = ref(null)
|
||||
|
||||
const createColumns = ({ AlignNextWord }) => {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
disabled(row) {
|
||||
return row.name === 'Edward King 3'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '编号',
|
||||
key: 'no',
|
||||
width: 70
|
||||
},
|
||||
{
|
||||
title: '分镜文案',
|
||||
key: 'after_gpt',
|
||||
width: 300,
|
||||
render(row, index) {
|
||||
return h(NInput, {
|
||||
value: row.word,
|
||||
type: 'textarea',
|
||||
autosize: true,
|
||||
onUpdateValue(v) {
|
||||
data.value[index].word = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '字幕',
|
||||
key: 'srt',
|
||||
width: '300',
|
||||
render(row) {
|
||||
let tmp = row.subValue.map((item) =>
|
||||
h('div', { style: 'display:flex; height : auto; margin: 2px 0' }, [
|
||||
h(NInput, {
|
||||
size: 'tiny',
|
||||
value: item.srt_value
|
||||
}),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
style: 'margin : 0 2px',
|
||||
size: 'tiny',
|
||||
color: '#ecd452',
|
||||
onClick: () => Upper(row, item, true)
|
||||
},
|
||||
{ default: () => '向上' }
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
style: 'margin : 0 2px',
|
||||
size: 'tiny',
|
||||
color: '#ee7959',
|
||||
onClick: () => Down(row, item)
|
||||
},
|
||||
{ default: () => '向下' }
|
||||
)
|
||||
])
|
||||
)
|
||||
return tmp
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'options',
|
||||
width: 130,
|
||||
render(row) {
|
||||
const btn = buttonArr.map((item) => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
style: 'margin : 2px',
|
||||
strong: true,
|
||||
size: 'tiny',
|
||||
color: item.color,
|
||||
onClick: () => {
|
||||
if (item.key == 'AlignNextWord') {
|
||||
AlignNextWord(row)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
})
|
||||
return btn
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '时间范围',
|
||||
key: 'timeLimit',
|
||||
width: 120
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 设置窗体的高度
|
||||
function setHeight() {
|
||||
debugger
|
||||
let div = document.getElementById('import_word_and_srt')
|
||||
div.style.height = hh + 'px'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
setHeight()
|
||||
})
|
||||
|
||||
/**
|
||||
* 重新计算指定索引值的时间
|
||||
* @param {data value 值的索引} index
|
||||
*/
|
||||
function modifyTime(index) {
|
||||
let s_v = data.value[index].subValue
|
||||
let len = s_v.length
|
||||
if (len > 0) {
|
||||
let s_t = s_v[0].start_time
|
||||
let e_t = s_v[len - 1].end_time
|
||||
data.value[index].start_time = s_t
|
||||
data.value[index].end_time = e_t
|
||||
data.value[index].timeLimit = `${s_t} -- ${e_t}`
|
||||
} else {
|
||||
data.value[index].start_time = null
|
||||
data.value[index].end_time = null
|
||||
data.value[index].timeLimit = ``
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前行的子数据的长度是不是大于0。不大于0 的话下面的字幕往上替补
|
||||
* @param {data value 的索引} index
|
||||
*/
|
||||
function moveUp(index) {
|
||||
let s_v = data.value[index].subValue
|
||||
if (s_v.length == 0) {
|
||||
// 将下面所有的字幕数据往上移动
|
||||
for (let i = index; i < data.value.length; i++) {
|
||||
// const element = data.value[i];
|
||||
if (data.value[i + 1] != null) {
|
||||
data.value[i].subValue = data.value[i + 1].subValue
|
||||
} else {
|
||||
// 检查当前这个是不是为空。为空直接删除
|
||||
// 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除)
|
||||
if (
|
||||
data.value[i].word == null ||
|
||||
data.value[i].word == '' ||
|
||||
data.value[i].after_gpt == null ||
|
||||
data.value[i].after_gpt == ''
|
||||
) {
|
||||
// 直接删除
|
||||
data.value.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字幕文件向上
|
||||
*/
|
||||
async function Upper(row, item, isM) {
|
||||
// 向上合并。
|
||||
// 当前item。向上的话。判断是不是第一个。是第一个的话。直接网上一个合并。
|
||||
// 不是第一个的话。将上面的所有的合并。
|
||||
// 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。
|
||||
// 向下合并。逻辑相同。
|
||||
if (row.lastId == null || row.lastId == '') {
|
||||
message.error('当前是第一个。不能向上合并')
|
||||
return
|
||||
}
|
||||
//判断当前行是不是第一个
|
||||
let itemId = item.id
|
||||
let itemIndex = row.subValue.findIndex((item) => item.id == itemId)
|
||||
let thisIndex = data.value.findIndex((item) => item.id == row.id)
|
||||
if (itemIndex < 0 && isM) {
|
||||
message.error('数据错误')
|
||||
return
|
||||
} else {
|
||||
// 先获取上一行的数据
|
||||
let lastRowIndex = data.value.findIndex((item) => item.id == row.lastId)
|
||||
//将当前及之前的数据向上合并
|
||||
for (let i = 0; i <= itemIndex; i++) {
|
||||
const element = row.subValue[i]
|
||||
data.value[lastRowIndex].subValue.push(element)
|
||||
}
|
||||
|
||||
// 删除指定的数据
|
||||
data.value[lastRowIndex + 1].subValue.splice(0, itemIndex + 1)
|
||||
moveUp(thisIndex)
|
||||
|
||||
// 判断当前行的文案和洗稿后文件是不是还有。(没有的话直接删除)
|
||||
if (
|
||||
data.value[thisIndex].subValue == null &&
|
||||
(row.word == null || row.word == '' || row.after_gpt == null || row.after_gpt == '')
|
||||
) {
|
||||
// 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId)
|
||||
if (data.value[thisIndex + 1] != null) {
|
||||
data.value[thisIndex + 1].lastId = row.lastId
|
||||
}
|
||||
// 直接删除
|
||||
data.value.splice(thisIndex, 1)
|
||||
// 调整下面的所有的编号
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
data.value[i].no = data.value[i].no - 1
|
||||
}
|
||||
}
|
||||
// 合并之后重新计算时间(计算上一个和当前的)
|
||||
modifyTime(lastRowIndex)
|
||||
modifyTime(thisIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字幕文件向下
|
||||
*/
|
||||
async function Down(row, item) {
|
||||
// 向下合并。逻辑相同。
|
||||
// 当前item。。判断是不是最后一个。是最后一个的话。直接向下合并。
|
||||
// 不是第一个的话。将下面的所有的合并。
|
||||
// 若是当前只有一个。判断当前文案和洗稿后文件是不是存在。存在不删除。没有的话直接删除。
|
||||
//判断当前行是不是第一个
|
||||
let itemId = item.id
|
||||
let itemIndex = row.subValue.findIndex((item) => item.id == itemId)
|
||||
let thisIndex = data.value.findIndex((item) => item.id == row.id)
|
||||
// 判断下一个是不是存在
|
||||
if (data.value[thisIndex + 1] == null) {
|
||||
message.error('当前已经是最后一个。不能向下合并')
|
||||
return
|
||||
}
|
||||
|
||||
if (itemIndex < 0) {
|
||||
message.error('数据错误')
|
||||
return
|
||||
} else {
|
||||
// 获取下一行数据
|
||||
let nextRowIndex = data.value.findIndex((item) => item.lastId == row.id)
|
||||
|
||||
//将当前以及下面的所有进行合并
|
||||
for (let i = row.subValue.length - 1; i >= itemIndex; i--) {
|
||||
const element = row.subValue[i]
|
||||
data.value[nextRowIndex].subValue.unshift(element)
|
||||
}
|
||||
|
||||
// 删除指定的数据
|
||||
data.value[thisIndex].subValue.splice(itemIndex)
|
||||
|
||||
// 判断该当前字幕是不是为空(为空的话。下面的往上移动)
|
||||
moveUp(thisIndex)
|
||||
|
||||
// 判断当前行的文案、洗稿后文件、以及字幕是不是还有。(没有的话直接删除)
|
||||
if (
|
||||
data.value[thisIndex].subValue == null &&
|
||||
(row.word == null || row.word == '' || row.after_gpt == null || row.after_gpt == '')
|
||||
) {
|
||||
// 先调整lastId (判断下一行是不是存在。存在的话修改lastId为当前的lastId)
|
||||
if (data.value[nextRowIndex] != null) {
|
||||
data.value[nextRowIndex].lastId = row.lastId
|
||||
}
|
||||
// 直接删除
|
||||
data.value.splice(thisIndex, 1)
|
||||
// 调整下面的所有的编号
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
data.value[i].no = data.value[i].no - 1
|
||||
}
|
||||
}
|
||||
// 合并之后重新计算时间(计算上一个和当前的)
|
||||
modifyTime(nextRowIndex)
|
||||
modifyTime(thisIndex)
|
||||
}
|
||||
}
|
||||
|
||||
function removePunctuationIncludingEllipsis(sentence) {
|
||||
// 扩展正则表达式以包含中文标点符号和省略号
|
||||
// 注意英文省略号可能由三个连续点表示,也可能直接使用特殊的省略号字符
|
||||
const punctuationRegExp = /[., \/#!$%\^&\*;:{}=\-_`~()\[\],。、;:?!‘’“”()【】《》…]+/g
|
||||
|
||||
// 使用正则表达式的replace方法替换掉所有匹配到的标点符号为空字符串
|
||||
return sentence.replace(punctuationRegExp, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 对齐当前列即下面的数据
|
||||
*/
|
||||
async function AlignNextWord(row) {
|
||||
// 判断是不是已经到到导入过字幕了
|
||||
dialog.create({
|
||||
type: 'warning',
|
||||
title: '警告',
|
||||
showIcon: true,
|
||||
content: '会自动对齐下面的数据。没有对齐成功就要手动调整在自动对齐',
|
||||
style: `width : 400px;`,
|
||||
maskClosable: false,
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
// 下对齐
|
||||
let itemId = row.id
|
||||
let thisIndex = data.value.findIndex((item) => item.id == itemId)
|
||||
if (thisIndex < 0) {
|
||||
message.error('数据错误')
|
||||
return
|
||||
}
|
||||
|
||||
let nextSrt = []
|
||||
// 先将当前行下面的所有的数据拿出来
|
||||
for (let i = thisIndex; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
nextSrt = nextSrt.concat(element.subValue)
|
||||
}
|
||||
let init_num = thisIndex
|
||||
// let data
|
||||
let tmp_str = ''
|
||||
for (let i = 0; i < nextSrt.length; ) {
|
||||
const element = nextSrt[i]
|
||||
let current_text = `第 ${thisIndex + 1} 数据。字幕:${
|
||||
element.srt_value
|
||||
}与文案不匹配。亲检查上下文。`
|
||||
tmp_str += element.srt_value
|
||||
|
||||
if (
|
||||
removePunctuationIncludingEllipsis(data.value[thisIndex].after_gpt).startsWith(
|
||||
removePunctuationIncludingEllipsis(tmp_str)
|
||||
)
|
||||
) {
|
||||
// data.value[thisIndex].subValue.push(element);
|
||||
if (init_num < thisIndex || i > 0) {
|
||||
Upper(data.value[thisIndex + 1], element, false)
|
||||
}
|
||||
i++
|
||||
} else {
|
||||
if (
|
||||
removePunctuationIncludingEllipsis(data.value[thisIndex + 1].after_gpt).startsWith(
|
||||
removePunctuationIncludingEllipsis(element.srt_value)
|
||||
)
|
||||
) {
|
||||
thisIndex++
|
||||
tmp_str = ''
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: 0,
|
||||
message: current_text
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存字幕文案信息,用于自动生成视频
|
||||
*/
|
||||
async function SaveCopywritingInformation() {
|
||||
if (props.menuType == 'mj') {
|
||||
await window.mj.SvaeMJWordSrt([toRaw(data.value), 'srt_time_information'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
})
|
||||
return
|
||||
} else {
|
||||
debugger
|
||||
// 先保存单个文件(然后在config文件中指向对应的文件)
|
||||
await window.api.OriginalAddWebuiJson(JSON.stringify(data.value), async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
if (value.data) {
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
let name = String(data.value[i].no).padStart(5, '0')
|
||||
data.value[i].prompt_json = value.data + '/' + name + '.png.json'
|
||||
data.value[i].name = name + '.png'
|
||||
}
|
||||
}
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[toRaw(data.value), 'srt_time_information'],
|
||||
(value) => {
|
||||
if (value.code != 1) {
|
||||
message.error('未知错误')
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入字幕并进行分镜输出,显示时间,将分镜给他传进去
|
||||
*/
|
||||
async function ImportSrtAndGetTime() {
|
||||
let srt_path = null
|
||||
// 先选择SRT文件
|
||||
await window.api.SelectFile(['srt'], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
srt_path = value.value
|
||||
})
|
||||
|
||||
await window.api.ImportSrtAndGetTime([toRaw(data.value), srt_path], (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 写入时间
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const item = value.data[i]
|
||||
if (i >= data.value.length) {
|
||||
item.no = i + 1
|
||||
item.timeLimit = `${item.start_time} -- ${item.end_time}`
|
||||
data.value.push(item)
|
||||
} else {
|
||||
data.value[i].start_time = item.start_time
|
||||
data.value[i].end_time = item.end_time
|
||||
data.value[i].timeLimit = `${item.start_time} -- ${item.end_time}`
|
||||
data.value[i].subValue = item.subValue
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开添加字幕信息的窗口
|
||||
*/
|
||||
async function OpenOneDialog() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.7
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(EditWord, { ref: editWordRef, initData: data }),
|
||||
style: `width : ${dialogWidth}px; minHeight : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
positiveText: '保存',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
debugger
|
||||
console.log(editWordRef)
|
||||
debugger
|
||||
let word_arr = editWordRef.value.word.split('\n')
|
||||
let new_arr = []
|
||||
let lastId = ''
|
||||
for (let i = 0; i < word_arr.length; i++) {
|
||||
const element = word_arr[i]
|
||||
if (element != '' && element != null) {
|
||||
let id = uuidv4()
|
||||
let obj = {
|
||||
no: i + 1,
|
||||
id: id,
|
||||
lastId: lastId,
|
||||
word: element,
|
||||
after_gpt: element,
|
||||
subValue: []
|
||||
}
|
||||
lastId = id
|
||||
data.value.push(obj)
|
||||
}
|
||||
}
|
||||
|
||||
// 将文案数据写入到本地
|
||||
let local_word = word_arr.filter((item) => item != '' && item != null)
|
||||
await window.api.saveWordTxt(toRaw(local_word), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('文案写入失败' + value.message)
|
||||
return
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns: createColumns({
|
||||
AlignNextWord
|
||||
}),
|
||||
rowKey: (row) => row.id,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys
|
||||
},
|
||||
ImportSrtAndGetTime,
|
||||
SaveCopywritingInformation,
|
||||
OpenOneDialog,
|
||||
maxHeight,
|
||||
hh
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div style="margin-top: 30px">
|
||||
<n-input :placeholder="placeholder" v-model:value="data"></n-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from 'vue'
|
||||
import { NInput, useMessage } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NInput
|
||||
},
|
||||
props: ['initData', 'placeholder'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let data = ref(props.initData)
|
||||
let placeholder = ref(props.placeholder)
|
||||
|
||||
onMounted(async () => {})
|
||||
|
||||
return {
|
||||
data,
|
||||
placeholder
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div id="prompt-setting" style="margin-top: 30px">
|
||||
<n-card title="GPT设置" hoverable>
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<div style="margin-bottom: 3px">gpt模型选择</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="gpt_model_options"
|
||||
v-model:value="gpt_model"
|
||||
style="width: 160px"
|
||||
>
|
||||
</n-select>
|
||||
</div>
|
||||
<div style="margin-left: 10px">
|
||||
<div style="margin-bottom: 3px">gpt自动推理模式</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="gpt_auto_inference_options"
|
||||
v-model:value="gpt_auto_inference"
|
||||
style="width: 180px"
|
||||
>
|
||||
</n-select>
|
||||
</div>
|
||||
<div style="margin-left: 10px">
|
||||
<div style="margin-bottom: 3px">选择自定义推理词</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="customize_gpt_options"
|
||||
v-model:value="customize_gpt_select"
|
||||
style="width: 150px"
|
||||
>
|
||||
</n-select>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="SD设置" hoverable style="margin-top: 5px">
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<div style="margin-bottom: 3px">SD生图数量</div>
|
||||
<n-input-number
|
||||
style="width: 120px"
|
||||
v-model:value="sd_batch_size"
|
||||
:show-button="false"
|
||||
:min="1"
|
||||
:max="8"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-left: 10px">
|
||||
<div style="margin-bottom: 3px">种子</div>
|
||||
<n-input-number
|
||||
style="width: 120px"
|
||||
v-model:value="sd_seed"
|
||||
:show-button="false"
|
||||
:min="-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style="margin-left: 10px; display: flex; flex-wrap: wrap; width: 130px">
|
||||
<div style="margin-bottom: 3px; width: 100%">生图尺寸</div>
|
||||
<n-input-number
|
||||
style="width: 50px"
|
||||
v-model:value="sd_image_width"
|
||||
:show-button="false"
|
||||
:min="-1"
|
||||
/>
|
||||
<span style="margin: 0 10px; line-height: 35px; font-size: 20px"> * </span>
|
||||
<n-input-number
|
||||
style="width: 50px"
|
||||
v-model:value="sd_image_height"
|
||||
:show-button="false"
|
||||
:min="-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="MJ设置" hoverable style="margin-top: 5px">
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<!-- <div style="margin-bottom: 3px;">铭感词相关</div> -->
|
||||
<n-button size="small" @click="AddMjBadPrompt" color="#7c461e">添加敏感词</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="其他设置" hoverable style="margin-top: 5px">
|
||||
<div class="first_row" style="display: flex">
|
||||
<div>
|
||||
<div style="margin-bottom: 3px">人物场景选择模式</div>
|
||||
<n-select
|
||||
size="small"
|
||||
placeholder="请选择"
|
||||
:options="character_select_model_options"
|
||||
v-model:value="character_select_model"
|
||||
style="width: 120px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
<div style="text-align: right; margin-top: 5px">
|
||||
<n-button type="success" size="medium" @click="SaveSetting">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from 'vue'
|
||||
import {
|
||||
NImage,
|
||||
useMessage,
|
||||
NSelect,
|
||||
NButton,
|
||||
NInputNumber,
|
||||
NInput,
|
||||
NCard,
|
||||
useDialog
|
||||
} from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||
import InputDialogContent from './InputDialogContent.vue'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NSelect,
|
||||
NButton,
|
||||
NInputNumber,
|
||||
NInput,
|
||||
NCard
|
||||
},
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let gpt_model = ref(window.config.gpt_model)
|
||||
let gpt_auto_inference = ref(window.config.gpt_auto_inference)
|
||||
let sd_batch_size = ref(null)
|
||||
let sd_seed = ref(null)
|
||||
let sd_image_width = ref(null)
|
||||
let sd_image_height = ref(null)
|
||||
let bad_prompt_ref = ref(null)
|
||||
|
||||
let gpt_model_options = ref([])
|
||||
let gpt_auto_inference_options = ref([])
|
||||
|
||||
let customize_gpt_options = ref([])
|
||||
let customize_gpt_select = ref(window.config.customize_gpt_prompt)
|
||||
|
||||
let character_select_model = ref(window.config.character_select_model)
|
||||
let character_select_model_options = ref([])
|
||||
|
||||
/**
|
||||
* 初始化GPT的option
|
||||
*/
|
||||
async function InitGptOptions() {
|
||||
await window.api.getGptModelOption('all', (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
gpt_model_options.value = value.data
|
||||
})
|
||||
await window.api.getGptAutoInferenceOptions('all', (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
gpt_auto_inference_options.value = value.data
|
||||
})
|
||||
|
||||
await window.api.GetCustomizeGptPrompt('all', (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
customize_gpt_options.value = value.data.map((item) => {
|
||||
return {
|
||||
label: item.prompt_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
await window.mj.GetTagSelectModel((value) => {
|
||||
character_select_model_options.value = value.data
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 加载全局配置
|
||||
// 获取SD配置
|
||||
await window.api.InitSDConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
sd_batch_size.value = value.data.batch_size
|
||||
sd_seed.value = value.data.seed
|
||||
sd_image_width.value = value.data.width
|
||||
sd_image_height.value = value.data.height
|
||||
})
|
||||
|
||||
await InitGptOptions()
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存设置
|
||||
*/
|
||||
async function SaveSetting() {
|
||||
try {
|
||||
window.config.gpt_model = gpt_model.value
|
||||
window.config.gpt_auto_inference = gpt_auto_inference.value
|
||||
window.config.character_select_model = character_select_model.value
|
||||
// 保存到通用配置文件
|
||||
let obj = {
|
||||
gpt_model: gpt_model.value,
|
||||
gpt_auto_inference: gpt_auto_inference.value,
|
||||
customize_gpt_prompt: customize_gpt_select.value,
|
||||
character_select_model: character_select_model.value
|
||||
}
|
||||
await window.api.ModifySampleSetting(JSON.stringify(obj), (value) => {
|
||||
if (value.code == 0) {
|
||||
throw new Error(value.message)
|
||||
}
|
||||
})
|
||||
// 保存到SD配置文件
|
||||
let sdObj = {
|
||||
batch_size: toRaw(sd_batch_size.value),
|
||||
width: toRaw(sd_image_width.value),
|
||||
height: toRaw(sd_image_height.value)
|
||||
}
|
||||
await window.api.SaveSDConfig(sdObj, (value) => {
|
||||
if (value.code == 0) {
|
||||
throw new Error(value.message)
|
||||
}
|
||||
})
|
||||
|
||||
message.success('保存成功')
|
||||
} catch (error) {
|
||||
debugger
|
||||
message.error('保存失败 ' + error.message)
|
||||
}
|
||||
}
|
||||
|
||||
async function AddMjBadPrompt() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加MJ敏感词',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: bad_prompt_ref,
|
||||
initData: null,
|
||||
placeholder: '请输入MJ敏感词'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
debugger
|
||||
let mj_bad_prompt = bad_prompt_ref.value.data
|
||||
|
||||
if (mj_bad_prompt == null || mj_bad_prompt == '') {
|
||||
return
|
||||
}
|
||||
|
||||
let data = {
|
||||
id: uuidv4(),
|
||||
value: mj_bad_prompt.toLowerCase()
|
||||
}
|
||||
|
||||
// 保存到后台
|
||||
await window.mj.AddMjBadPrompt(data, (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success(value.message)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
gpt_model,
|
||||
gpt_model_options,
|
||||
gpt_auto_inference_options,
|
||||
gpt_auto_inference,
|
||||
sd_batch_size,
|
||||
SaveSetting,
|
||||
sd_seed,
|
||||
sd_image_width,
|
||||
sd_image_height,
|
||||
customize_gpt_options,
|
||||
customize_gpt_select,
|
||||
character_select_model_options,
|
||||
character_select_model,
|
||||
AddMjBadPrompt,
|
||||
bad_prompt_ref
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,926 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-data-table
|
||||
:max-height="maxHeight"
|
||||
:row-key="rowKey"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
@update:checked-row-keys="handleCheck"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
ref,
|
||||
h,
|
||||
onMounted,
|
||||
defineComponent,
|
||||
onUnmounted,
|
||||
watch,
|
||||
toRaw,
|
||||
render,
|
||||
onBeforeUnmount
|
||||
} from 'vue'
|
||||
import {
|
||||
NDataTable,
|
||||
NImage,
|
||||
useMessage,
|
||||
NInput,
|
||||
NButton,
|
||||
NSwitch,
|
||||
NPopover,
|
||||
useDialog,
|
||||
NCheckbox,
|
||||
NSelect,
|
||||
NIcon
|
||||
} from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
import ModifyPromptChinese from '../Components/ModifyPromptChinese.vue'
|
||||
import InputDialogContent from './Components/InputDialogContent.vue'
|
||||
import DataTableShowGenerateImage from './Components/DataTableShowGenerateImage.vue'
|
||||
import { cloneDeep, debounce, min } from 'lodash'
|
||||
import SelectImageStyle from '../Components/SelectImageStyle.vue'
|
||||
import DataTableCharacterAndScene from './Components/DataTableCharacterAndScene.vue'
|
||||
import DataTablePromptRow from './Components/DataTablePromptRow.vue'
|
||||
import DataTableGptPromptRow from './Components/DataTableGptPromptRow.vue'
|
||||
import DataTableParameterRow from './Components/DataTableParameterRow.vue'
|
||||
import { Reload } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NDataTable,
|
||||
NInput,
|
||||
NButton,
|
||||
ModifyPromptChinese,
|
||||
NSwitch,
|
||||
NPopover,
|
||||
NCheckbox,
|
||||
SelectImageStyle,
|
||||
Reload,
|
||||
NIcon
|
||||
},
|
||||
props: ['initData', 'prefix_prompt', 'suffix_prompt'],
|
||||
setup(props) {
|
||||
console.log('输出文件夹位置', props.initData)
|
||||
let data = ref(props.initData)
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let selectKey = ref([])
|
||||
let promptChineseRef = ref(null)
|
||||
let zhAutoTranslate = ref(false)
|
||||
let maxHeight = ref(null)
|
||||
let prefix_prompt = ref(props.prefix_prompt)
|
||||
let suffix_prompt = ref(props.suffix_prompt)
|
||||
|
||||
let titleCharacterAndSceneRef = ref(null)
|
||||
let dataCharacterAndSceneRef = ref(null)
|
||||
|
||||
let prefix_prompt_ref = ref(null)
|
||||
let suffix_prompt_ref = ref(null)
|
||||
let selectStyle = ref([])
|
||||
let characterSelectModel = ref(
|
||||
window.config.character_select_model ? window.config.character_select_model : 'tag'
|
||||
)
|
||||
|
||||
let tags = ref({})
|
||||
|
||||
let AutoSaveDataJsonDebounced = debounce(AutoSaveDataJson, 3000)
|
||||
let SaveDataJsonDebounced = debounce(AutoSaveDataJson, 1000)
|
||||
|
||||
let image_generate_category = ref(
|
||||
window.config.image_generate_category ? window.config.image_generate_category : 'sd'
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.initData,
|
||||
async (newVal) => {
|
||||
data.value = newVal
|
||||
if (window.config.auto_save) {
|
||||
console.log('自动保存数据')
|
||||
AutoSaveDataJsonDebounced()
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.prefix_prompt,
|
||||
(newVal) => {
|
||||
prefix_prompt.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.suffix_prompt,
|
||||
(newVal) => {
|
||||
suffix_prompt.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
/**
|
||||
* 自动保存数据到json文件
|
||||
*/
|
||||
async function AutoSaveDataJson() {
|
||||
await window.api.AutoSaveDataJson(JSON.stringify(toRaw(data.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('数据保存错误,错误信息:' + value.message)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const createColumns = ({}) => {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
disabled(row) {
|
||||
return row.name === 'Edward King 3'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
key: 'no',
|
||||
width: 50
|
||||
},
|
||||
{
|
||||
title: '字幕',
|
||||
key: 'srt',
|
||||
width: '180',
|
||||
render(row) {
|
||||
let tmp
|
||||
if (row.subValue.length > 0) {
|
||||
tmp = row.subValue.map((item) =>
|
||||
h('div', { style: 'display:flex; height : auto; margin: 2px 0' }, [
|
||||
h(NInput, {
|
||||
size: 'tiny',
|
||||
value: item.srt_value
|
||||
})
|
||||
])
|
||||
)
|
||||
} else {
|
||||
tmp = h('div', { style: 'display:flex; height : auto; margin: 2px 0' }, [
|
||||
h(NInput, {
|
||||
type: 'textarea',
|
||||
autosize: { minRows: 3, maxRows: 6 },
|
||||
size: 'tiny',
|
||||
value: row.after_gpt
|
||||
})
|
||||
])
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
},
|
||||
{
|
||||
// 修改title,往里面添加添加一个下拉选择框
|
||||
title(row, index) {
|
||||
return h(DataTableCharacterAndScene, {
|
||||
ref: titleCharacterAndSceneRef,
|
||||
type: 'title',
|
||||
row: row,
|
||||
characterSelectModel: characterSelectModel,
|
||||
func: {
|
||||
refreshTagData: async () => {
|
||||
try {
|
||||
await InitTags()
|
||||
message.success('人物角色刷新成功')
|
||||
} catch (error) {
|
||||
message.success('人物角色刷新失败,请重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
className: 'prompt_row',
|
||||
key: 'character',
|
||||
width: '200',
|
||||
render(row, index) {
|
||||
return h(DataTableCharacterAndScene, {
|
||||
ref: dataCharacterAndSceneRef,
|
||||
type: 'data',
|
||||
row: row,
|
||||
tags: tags,
|
||||
characterSelectModel: characterSelectModel
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title(row, index) {
|
||||
return h(DataTableGptPromptRow, {
|
||||
type: 'title',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
translateAll: TranslateAll,
|
||||
addPrefix: AddPrefix,
|
||||
addSuffix: AddSuffix,
|
||||
selectGenerateImagesStyle: SelectGenerateImagesStyle
|
||||
}
|
||||
})
|
||||
},
|
||||
key: 'gpt_prompt',
|
||||
className: 'prompt_row',
|
||||
render(row, index) {
|
||||
return h(DataTableGptPromptRow, {
|
||||
type: 'data',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
nextGptPrompt: NextGptPrompt,
|
||||
singlePrompt: SinglePrompt
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
// 提示词命令
|
||||
title(row, index) {
|
||||
return h(DataTablePromptRow, {
|
||||
type: 'title',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
mJBadPromptCheck: MJBadPromptCheck,
|
||||
mergePrompt: MergePrompt
|
||||
}
|
||||
})
|
||||
},
|
||||
key: 'prompt',
|
||||
width: '300',
|
||||
className: 'prompt_row',
|
||||
render(row, index) {
|
||||
return h(DataTablePromptRow, {
|
||||
type: 'data',
|
||||
row: row,
|
||||
index: index,
|
||||
func: {
|
||||
nextGenerateImage: NextGenerateImage
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
// 参数
|
||||
title(row, index) {
|
||||
return h(DataTableParameterRow, {
|
||||
type: 'title',
|
||||
row: row,
|
||||
index: index,
|
||||
image_generate_category: image_generate_category
|
||||
})
|
||||
},
|
||||
key: 'parameter',
|
||||
width: '130',
|
||||
render(row, index) {
|
||||
return h(DataTableParameterRow, {
|
||||
type: `data_${image_generate_category.value}`,
|
||||
row: row,
|
||||
index: index
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
// 出图
|
||||
title(row, index) {
|
||||
return h(DataTableShowGenerateImage, {
|
||||
initData: row,
|
||||
index: index,
|
||||
type: 'title',
|
||||
image_generate_category: image_generate_category,
|
||||
func: {
|
||||
mJGetImage: MJGetImage
|
||||
}
|
||||
})
|
||||
},
|
||||
key: 'options',
|
||||
className: 'prompt_row',
|
||||
width: '300',
|
||||
render(row, index) {
|
||||
return h(DataTableShowGenerateImage, {
|
||||
initData: row,
|
||||
index: index,
|
||||
type: 'data'
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
let isSaved = false
|
||||
// 监听control+s 保存数据
|
||||
let saveListener = function (event) {
|
||||
debugger
|
||||
if (event.ctrlKey && event.key === 's') {
|
||||
event.preventDefault()
|
||||
if (!isSaved) {
|
||||
console.log('User pressed Ctrl+S')
|
||||
SaveDataJsonDebounced()
|
||||
isSaved = true
|
||||
}
|
||||
} else {
|
||||
isSaved = false
|
||||
}
|
||||
}
|
||||
|
||||
async function InitTags() {
|
||||
await window.mj.GetTagDataByTypeAndProperty(['dynamic', null], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
tags.value = value.data
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
zhAutoTranslate.value = window.config.translation_auto
|
||||
|
||||
await window.api.GetConfigJson(JSON.stringify(['image_style', []]), async (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
await window.api.GetImageStyleInfomation(JSON.stringify(toRaw(value.data)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
selectStyle.value = value.data
|
||||
})
|
||||
})
|
||||
|
||||
// 判断data中的数据是不是有chinese text
|
||||
|
||||
window.api.setEventListen([DEFINE_STRING.TRANSLATE_RETURN_REFRESH, window.id], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('返回数据错误')
|
||||
return
|
||||
}
|
||||
// 直接
|
||||
let index = data.value.findIndex((item) => item.id == value.rowId)
|
||||
if (index < 0) {
|
||||
message.error('翻译后修改源数据,对应数据ID未找到')
|
||||
return
|
||||
}
|
||||
data.value[index].gpt_prompt = value.data
|
||||
})
|
||||
getMaxHeight()
|
||||
|
||||
window.api.setEventListen(
|
||||
[DEFINE_STRING.SD_ORIGINAL_GENERATE_IMAGE_RETURN, window.id],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('生成图片错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
// 修改data中的数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
if (data.value[i].id == value.id) {
|
||||
data.value[i].subImagePath = value.data.subImagePath.map(
|
||||
(item) => 'file://' + item.replaceAll('\\', '/') + '?time=' + new Date().getTime()
|
||||
)
|
||||
data.value[i].outImagePath =
|
||||
'file://' +
|
||||
value.data.outImagePath.replaceAll('\\', '/') +
|
||||
'?time=' +
|
||||
new Date().getTime()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
window.addEventListener('keydown', saveListener)
|
||||
|
||||
let div = document.getElementsByClassName('prompt_row')
|
||||
for (let i = 0; i < div.length; i++) {
|
||||
div[i].style.padding = '0px 5px'
|
||||
}
|
||||
|
||||
// 监听MJ信息的监听任务的返回
|
||||
|
||||
window.api.setEventListen(
|
||||
[DEFINE_STRING.DISCORD.MAIN_DISCORD_MESSAGE_CHANGE, window.id],
|
||||
(value) => {
|
||||
mainDiscordMessageChanged(value)
|
||||
}
|
||||
)
|
||||
|
||||
await InitTags()
|
||||
})
|
||||
|
||||
onBeforeUnmount(async () => {
|
||||
debugger
|
||||
await AutoSaveDataJson()
|
||||
})
|
||||
|
||||
// 接收Discord的修改信息
|
||||
async function mainDiscordMessageChanged(value) {
|
||||
if (value.code == 0) {
|
||||
message.error('Discord返回错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理数据
|
||||
if (value.type == 'created') {
|
||||
debugger
|
||||
console.log('接收Discord的创建消息', value)
|
||||
// 找到对应ID的data数据
|
||||
|
||||
let index = data.value.findIndex((item) => item.id == value.id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
delete value.type
|
||||
delete value.code
|
||||
|
||||
data.value[index]['mj_message'] = value
|
||||
} else if (value.type == 'updated') {
|
||||
console.log('接收Discord的更新消息', value)
|
||||
debugger
|
||||
// 修改对应的数据
|
||||
let index = data.value.findIndex((item) => item.mj_message?.message_id == value.message_id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
if (value.image_click != null && value.image_click != '') {
|
||||
data.value[index]['outImagePath'] = value.image_click
|
||||
}
|
||||
delete value.type
|
||||
delete value.code
|
||||
|
||||
data.value[index]['mj_message'] = value
|
||||
} else if (value.type == 'delete') {
|
||||
console.log('接收Discord的删除消息', value)
|
||||
} else if (value.type == 'finished') {
|
||||
console.log('接收Discord的完成消息', value)
|
||||
debugger
|
||||
|
||||
// 修改对应的数据
|
||||
let index = data.value.findIndex((item) => item.mj_message?.image_id == value.image_id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
|
||||
data.value[index]['outImagePath'] = value.image_path
|
||||
|
||||
delete value.type
|
||||
delete value.code
|
||||
value.progress = 100
|
||||
value.message_id = value.message_id
|
||||
|
||||
data.value[index]['mj_message'] = value
|
||||
|
||||
// 返回后端分割图片(传入图片地址,返回分割后的图片地址数组)
|
||||
await window.mj.ImageSplit(
|
||||
JSON.stringify([value.image_path, data.value[index].name]),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
data.value[index]['outImagePath'] = value.data.outImagePath
|
||||
data.value[index]['subImagePath'] = value.data.subImagePath
|
||||
}
|
||||
)
|
||||
} else {
|
||||
message.error('未知的操作类型')
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
window.api.removeEventListen(DEFINE_STRING.TRANSLATE_RETURN_REFRESH)
|
||||
window.api.removeEventListen(DEFINE_STRING.DISCORD.MAIN_DISCORD_MESSAGE_CHANGE)
|
||||
window.removeEventListener('keydown', saveListener)
|
||||
})
|
||||
|
||||
/**
|
||||
* 将翻译添加到队列中的方式
|
||||
* @param {要翻译的数据(数组)} translateData
|
||||
* @param {原语言(zh,en)} from
|
||||
* @param {目标语言(zh,en)} to
|
||||
* @param {是否显示弹窗} isShow
|
||||
*/
|
||||
async function TranslatePrompt(translateData, from, to) {
|
||||
console.log(translateData)
|
||||
await window.api.TranslatePrompt([translateData, from, to, window.id, true], (value) => {
|
||||
debugger
|
||||
console.log(value)
|
||||
})
|
||||
}
|
||||
|
||||
// 计算当前程序界面的高度,减去上下的高度,设置maxHegiht
|
||||
async function getMaxHeight() {
|
||||
let height = window.innerHeight
|
||||
maxHeight.value = height - 150
|
||||
}
|
||||
|
||||
// 监听程序界面的高度变化
|
||||
window.onresize = function () {
|
||||
getMaxHeight()
|
||||
}
|
||||
|
||||
// 添加前缀的弹窗
|
||||
const AddPrefix = () => {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加前缀',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: prefix_prompt_ref,
|
||||
initData: prefix_prompt.value,
|
||||
placeholder: '请输入前缀'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
prefix_prompt.value = prefix_prompt_ref.value.data
|
||||
// 保存通用前缀
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[prefix_prompt_ref.value.data, 'prefix_prompt', false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('保存前缀错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择生成图片的风格
|
||||
*/
|
||||
async function SelectGenerateImagesStyle() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.8
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(SelectImageStyle, {
|
||||
selectStyle: toRaw(selectStyle.value),
|
||||
height: dialogHeight
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// 重新加载数据
|
||||
let tmp_arr = []
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i]
|
||||
tmp_arr.push(element.id)
|
||||
}
|
||||
// 将当前的风格保存
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[JSON.stringify(tmp_arr), 'image_style', true],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('保存成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 添加后缀的弹窗
|
||||
const AddSuffix = () => {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 400
|
||||
let dialogHeight = 150
|
||||
dialog.create({
|
||||
title: '添加后缀',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () =>
|
||||
h(InputDialogContent, {
|
||||
ref: suffix_prompt_ref,
|
||||
initData: suffix_prompt.value,
|
||||
placeholder: '请输入后缀'
|
||||
}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
suffix_prompt.value = suffix_prompt_ref.value.data
|
||||
await window.api.SaveCopywritingInformation(
|
||||
[suffix_prompt_ref.value.data, 'suffix_prompt', false],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error('保存前缀错误,错误信息如下:' + value.message)
|
||||
return
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加MJ敏感词检查
|
||||
*/
|
||||
async function MJBadPromptCheck() {
|
||||
await window.mj.MJBadPromptCheck(JSON.stringify(toRaw(data.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
debugger
|
||||
// 先将所有的数据设置为false
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].mj_message = data.value[i].mj_message ? data.value[i].mj_message : {}
|
||||
data.value[i].mj_message['hasBadPrompt'] = false
|
||||
}
|
||||
|
||||
// 判断返回的ID数据是不是为空,不为空,修改数据
|
||||
if (value.data.length > 0) {
|
||||
let message_text = ''
|
||||
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const element = value.data[i]
|
||||
message_text += ',' + element.no.toString()
|
||||
let index = data.value.findIndex((item) => item.id == element.id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
data.value[index].mj_message = data.value[index].mj_message
|
||||
? data.value[index].mj_message
|
||||
: {}
|
||||
data.value[index].mj_message['hasBadPrompt'] = true
|
||||
}
|
||||
|
||||
let show_text = `检测到 ${value.data.length} 条敏感词,分别在第 ${message_text.substring(
|
||||
1
|
||||
)} 行。有红色框标识。请检查`
|
||||
window.api.showGlobalMessageDialog({
|
||||
code: 0,
|
||||
message: show_text
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前和下面的所有的数据生成图片
|
||||
* @param {*} row
|
||||
*/
|
||||
async function NextGenerateImage(row, ca = 'sd') {
|
||||
let index = data.value.findIndex((item) => item.id == row.id)
|
||||
let tempData = []
|
||||
for (let i = index; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
tempData.push(element)
|
||||
}
|
||||
// 判断当前的生图模式
|
||||
if (ca == 'sd') {
|
||||
// 生成SD生图
|
||||
await window.api.OriginalSDImageGenerate(
|
||||
[JSON.stringify(tempData), true, true],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加SD生图任务成功')
|
||||
}
|
||||
)
|
||||
} else if (ca == 'mj') {
|
||||
// MJ生图
|
||||
await window.mj.OriginalMJImageGenerate([JSON.stringify(tempData), true, true], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加MJ生图任务成功')
|
||||
})
|
||||
} else if (ca == 'd3') {
|
||||
} else {
|
||||
message.error('生图的类别错误')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 往下继续推理
|
||||
* @param value 当前行数据
|
||||
*/
|
||||
async function NextGptPrompt(row) {
|
||||
let index = data.value.findIndex((item) => item.id == row.id)
|
||||
let tempData = []
|
||||
for (let i = index; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
tempData.push(element)
|
||||
}
|
||||
|
||||
await window.api.GPTPrompt(
|
||||
[JSON.stringify(tempData), true, JSON.stringify(toRaw(data.value))],
|
||||
(value) => {
|
||||
console.log('添加推理任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加推理任务成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 单句推理
|
||||
* @param row
|
||||
*/
|
||||
async function SinglePrompt(row) {
|
||||
await window.api.GPTPrompt(
|
||||
[JSON.stringify([toRaw(row)]), false, JSON.stringify(toRaw(data.value))],
|
||||
(value) => {
|
||||
console.log('添加推理任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加推理任务成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function containsChineseOrPunctuation(str) {
|
||||
return /[\u4e00-\u9fa5]|[\u3000-\u301e\u2013\u2014\u2018\u2019\u201c\u201d\u2026\u203b\uff08\uff09\uff1a\uff1b\uff1f\uff01\uff0c\u3001\uff0e\u3002\uff1f\uff01\u2018\u2019\u201c\u201d]/.test(
|
||||
str
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译传入的数据
|
||||
* @param {*} key 翻译的类型
|
||||
*/
|
||||
async function TranslateAll(key) {
|
||||
debugger
|
||||
// 将中文翻译成英文
|
||||
if (key == 'english') {
|
||||
let tmp_d = []
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = cloneDeep(data.value[i])
|
||||
if (containsChineseOrPunctuation(element.gpt_prompt)) {
|
||||
tmp_d.push(element)
|
||||
}
|
||||
}
|
||||
// 检查是不是有中文
|
||||
// 开始翻译
|
||||
let t = [tmp_d, 'zh', 'en', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
} else if (key == 'chinese') {
|
||||
debugger
|
||||
let t = [[...toRaw(data.value)], 'en', 'zh', false, false]
|
||||
await window.mj.TranslateReturnNowTask(JSON.stringify(t), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加翻译任务成功')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成提示词
|
||||
*/
|
||||
async function MergePrompt() {
|
||||
debugger
|
||||
try {
|
||||
// 判断该当前是不是有风格
|
||||
// 获取当前的风格ID
|
||||
let style_str = ''
|
||||
for (let i = 0; i < selectStyle.value.length; i++) {
|
||||
const element = selectStyle.value[i]
|
||||
style_str += element.english_style + ','
|
||||
}
|
||||
|
||||
// 获取当前的前缀和后缀
|
||||
let prefix = prefix_prompt.value
|
||||
let suffix = suffix_prompt.value
|
||||
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const element = data.value[i]
|
||||
// 匹配提示词
|
||||
// 拼接当前选择的人物
|
||||
let character_string = ''
|
||||
element.character_tags?.forEach((item) => {
|
||||
character_string += item.prompt + ', '
|
||||
})
|
||||
|
||||
// 获取当前的场景提示词
|
||||
let scene_string = ''
|
||||
element.scene_tags?.forEach((item) => {
|
||||
scene_string += item.prompt + ', '
|
||||
})
|
||||
|
||||
// 开始拼接提示词
|
||||
// 风格 + 前缀 + 人物 + 场景 + 词 + 后缀
|
||||
let end_prompt = ` ${style_str} ${
|
||||
prefix ? prefix : ''
|
||||
}, ${character_string} ${scene_string} ${element.gpt_prompt}, ${suffix ? suffix : ''}`
|
||||
data.value[i].prompt = end_prompt
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('生成提示词错误,错误信息如下:' + error.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
async function MJGetImage() {
|
||||
let pa = []
|
||||
// MJ采集图片
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i]
|
||||
// 一般进度大于 50 会出现图片,
|
||||
if (!item.mj_message) {
|
||||
return
|
||||
}
|
||||
if (item.mj_message.progress && item.mj_message.progress == 100) {
|
||||
// 判断 image_path 是不是存在。
|
||||
if (item.mj_message.image_id && !item.mj_message.image_path) {
|
||||
// 通过当前的image_id获取图片
|
||||
pa.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pa.length > 0) {
|
||||
window.mj.GetGeneratedMJImageAndSplit(JSON.stringify(pa), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
// 修改数据
|
||||
for (let i = 0; i < value.data.length; i++) {
|
||||
const element = value.data[i]
|
||||
// 修改对应的数据
|
||||
let index = data.value.findIndex((item) => item.id == element.id)
|
||||
if (index < 0) {
|
||||
message.error('未找到对应的数据')
|
||||
return
|
||||
}
|
||||
data.value[index].outImagePath = element.outImagePath
|
||||
data.value[index].subImagePath = element.subImagePath
|
||||
data.value[index].mj_message.image_click = element.result
|
||||
data.value[index].mj_message.image_path = element.image_path
|
||||
}
|
||||
})
|
||||
} else {
|
||||
message.error('没有找到可以自动下载图片的数据,可以手动粘贴链接分割')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns: createColumns({}),
|
||||
selectStyle,
|
||||
rowKey: (row) => row.id,
|
||||
handleCheck(rowKeys) {
|
||||
selectKey.value = rowKeys
|
||||
},
|
||||
promptChineseRef,
|
||||
zhAutoTranslate,
|
||||
TranslatePrompt,
|
||||
TranslateAll,
|
||||
maxHeight,
|
||||
prefix_prompt_ref,
|
||||
suffix_prompt_ref,
|
||||
characterSelectModel,
|
||||
image_generate_category,
|
||||
titleCharacterAndSceneRef,
|
||||
dataCharacterAndSceneRef
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div id="menu">
|
||||
<MenuButton :initData="data" :initDataFunction="InitData" :Character="AnalyzeCharacter" :treeData="TagTreeData">
|
||||
</MenuButton>
|
||||
</div>
|
||||
<div id="data-table" style="margin-top: 10px;">
|
||||
<DataTable :initData="data" :suffix_prompt="suffix_prompt" :prefix_prompt="prefix_prompt"></DataTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, onUnmounted, toRaw } from "vue"
|
||||
import { NImage, useMessage } from "naive-ui";
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import MenuButton from './MenuButton.vue'
|
||||
import DataTable from './DataTable.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage, DataTable, MenuButton
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let data = ref([]);
|
||||
let AnalyzeCharacter = ref("");
|
||||
let TagTreeData = ref([]);
|
||||
let suffix_prompt = ref(null);
|
||||
let prefix_prompt = ref(null);
|
||||
let promptError = true;
|
||||
|
||||
async function InitData() {
|
||||
// 加载
|
||||
// 初始化加载项目下面的分镜好的文案
|
||||
// 并判断是不是有洗稿后的文件。一并加载
|
||||
await window.api.GetProjectWord(value => {
|
||||
data.value = value.data;
|
||||
})
|
||||
if (!data.value) {
|
||||
data.value = [];
|
||||
}
|
||||
|
||||
// 重新加载数据
|
||||
// 判断是不是已经有生成(有有生成显示)
|
||||
await window.api.GetConfigJson(JSON.stringify(['auto_analyze_character', ""]), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
AnalyzeCharacter.value = value.data;
|
||||
})
|
||||
|
||||
// 判断是不是有前缀
|
||||
await window.api.GetConfigJson(JSON.stringify([null, {}]), async (value) => {
|
||||
debugger
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
suffix_prompt.value = value.data.suffix_prompt;
|
||||
prefix_prompt.value = value.data.prefix_prompt;
|
||||
|
||||
})
|
||||
|
||||
// 读取data数据对应的prompt文件
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
await window.api.GetPromptJson(item.name, (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0 && promptError) {
|
||||
message.error(value.message);
|
||||
promptError = false;
|
||||
}
|
||||
data.value[i].prompt = value.data.webui_config.prompt;
|
||||
data.value[i].chinese_prompt = value.data?.chinese_prompt;
|
||||
// data.value[i].gpt_prompt = value.data?.gpt_prompt;
|
||||
data.value[i].adetailer = value.data?.adetailer;
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await InitData();
|
||||
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
data,
|
||||
AnalyzeCharacter,
|
||||
TagTreeData,
|
||||
prefix_prompt,
|
||||
suffix_prompt,
|
||||
InitData,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,369 @@
|
||||
<template>
|
||||
<div style="display: flex">
|
||||
<n-button color="#ee7959" @click="ImportWord" size="small">导入字幕/SRT</n-button>
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="GetCharacter" size="small"
|
||||
>角色分析(标签集)</n-button
|
||||
>
|
||||
<n-dropdown trigger="hover" :options="GptButtonOptions" @select="ButtonSelect">
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="GPTPrompt" size="small"
|
||||
>一键推理提示词</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
<n-dropdown trigger="hover" :options="GenerateImageOptions" @select="ButtonSelect">
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="GenerateImageAll" size="small"
|
||||
>生成所有的图片</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
|
||||
<n-dropdown trigger="hover" :options="ResetDataOptions" @select="ButtonSelect">
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="resetALLData" size="small"
|
||||
>重置数据</n-button
|
||||
>
|
||||
</n-dropdown>
|
||||
|
||||
<n-button color="#ee7959" style="margin-left: 10px" @click="OpenPromptSetting" size="small"
|
||||
>设置</n-button
|
||||
>
|
||||
<div style="display: flex; align-items: center">
|
||||
<n-switch
|
||||
v-model:value="auto_save"
|
||||
:railStyle="railStyle"
|
||||
@update:value="AutoSaveUpdate"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
<template #checked> 自动保存 </template>
|
||||
<template #unchecked> Ctrl + S </template>
|
||||
</n-switch>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, h, onMounted, defineComponent, toRaw, watch } from 'vue'
|
||||
import { NImage, useMessage, NButton, useDialog, NPopover, NSwitch, NDropdown } from 'naive-ui'
|
||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
import ImportWordAndSrt from './Components/ImportWordAndSrt.vue'
|
||||
import CharacterAnalyze from './Components/CharacterAnalyze.vue'
|
||||
import PromptSetting from './Components/PromptSetting.vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NImage,
|
||||
NButton,
|
||||
CharacterAnalyze,
|
||||
NPopover,
|
||||
NSwitch,
|
||||
NDropdown
|
||||
},
|
||||
props: ['initData', 'Character', 'treeData', 'autoSave', 'initDataFunction'],
|
||||
setup(props) {
|
||||
let message = useMessage()
|
||||
let dialog = useDialog()
|
||||
let data = ref(props.initData)
|
||||
let AnalyzeCharacter = ref(props.Character)
|
||||
let tagTreeData = ref(props.treeData)
|
||||
let auto_save = ref(window.config.auto_save ? window.config.auto_save : false)
|
||||
|
||||
watch(
|
||||
() => props.initData,
|
||||
(newVal) => {
|
||||
data.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.Character,
|
||||
(newVal) => {
|
||||
AnalyzeCharacter.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.treeData,
|
||||
(newVal) => {
|
||||
tagTreeData.value = newVal
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
window.api.setEventListen([DEFINE_STRING.GPT_GENERATE_PROMPT_RETURN], (value) => {
|
||||
console.log(value)
|
||||
// 修改data中指定的id的某个数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
if (data.value[i].id == value.id) {
|
||||
data.value[i].gpt_prompt = value.gpt_prompt
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
window.config.auto_save = auto_save.value
|
||||
})
|
||||
|
||||
/**
|
||||
* 导入字幕的配置
|
||||
*/
|
||||
async function ImportWord() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.95
|
||||
let dialogHeight = window.innerHeight * 0.95
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(ImportWordAndSrt, { initData: data.value, height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: () => {
|
||||
props.initDataFunction()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色自动分析
|
||||
*/
|
||||
async function GetCharacter() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.6
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(CharacterAnalyze, { initData: data.value, height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// 重新加载数据
|
||||
// 判断是不是已经有生成(有有生成显示)
|
||||
await window.api.GetConfigJson(
|
||||
JSON.stringify(['auto_analyze_character', '']),
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
AnalyzeCharacter.value = value.data
|
||||
}
|
||||
)
|
||||
|
||||
// 判断是不是有标签树
|
||||
await window.api.GetConfigJson(JSON.stringify(['tag_tree', []]), async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
tagTreeData.value = value.data
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* GPT分析提示词
|
||||
*/
|
||||
async function GPTPrompt() {
|
||||
// 将所有的人物添加到后台队列任务中
|
||||
console.log(data.value)
|
||||
// 处理数据,只推理prompt为空的数据
|
||||
|
||||
let tmpData = cloneDeep(toRaw(data.value))
|
||||
tmpData = tmpData.filter((item) => {
|
||||
return item.gpt_prompt == null || item.gpt_prompt == ''
|
||||
})
|
||||
|
||||
if (tmpData.length == 0) {
|
||||
message.success('没有需要推理的数据')
|
||||
return
|
||||
}
|
||||
|
||||
await window.api.GPTPrompt(
|
||||
[JSON.stringify(tmpData), true, JSON.stringify(toRaw(data.value))],
|
||||
(value) => {
|
||||
console.log('添加推理任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加推理任务成功')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新的弹窗
|
||||
*/
|
||||
async function OpenPromptSetting() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = window.innerWidth * 0.4
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(PromptSetting, {}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成所有的图片
|
||||
*/
|
||||
async function GenerateImageAll() {
|
||||
debugger
|
||||
console.log(data.value)
|
||||
let tmpData = cloneDeep(toRaw(data.value))
|
||||
tmpData = tmpData.filter((item) => {
|
||||
return item.outImagePath == null || item.outImagePath == ''
|
||||
})
|
||||
|
||||
if (tmpData.length == 0) {
|
||||
message.success('没有需要批量生成的图片')
|
||||
return
|
||||
}
|
||||
|
||||
// 判断当前是mj还是sd
|
||||
let ca = window.config.image_generate_category
|
||||
if (ca == 'sd') {
|
||||
// 将所有的人物添加到后台队列任务中
|
||||
await window.api.OriginalSDImageGenerate([JSON.stringify(tmpData), true, true], (value) => {
|
||||
console.log('添加生成图片任务成功')
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加生成图片任务成功')
|
||||
})
|
||||
} else if (ca == 'mj') {
|
||||
// MJ生图
|
||||
await window.mj.OriginalMJImageGenerate([JSON.stringify(tmpData), true, true], (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('添加MJ生图任务成功')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let railStyle = ({ focused, checked }) => {
|
||||
const style = {}
|
||||
if (checked) {
|
||||
style.background = '#f3a694'
|
||||
if (focused) {
|
||||
style.boxShadow = '0 0 0 2px #f3a694'
|
||||
}
|
||||
} else {
|
||||
style.background = '#775039'
|
||||
if (focused) {
|
||||
style.boxShadow = '0 0 0 2px #775039'
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
// 重置提示词数据
|
||||
async function ResetPrompt() {
|
||||
// 循环data删除数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].prompt = ''
|
||||
data.value[i].chinese_prompt = []
|
||||
}
|
||||
}
|
||||
|
||||
// 图片数据重置
|
||||
async function ResetImage() {
|
||||
// 循环data删除数据
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
data.value[i].outImagePath = null
|
||||
data.value[i].subImagePath = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉菜单选择值改变的方法
|
||||
* @param {*} value
|
||||
*/
|
||||
async function ButtonSelect(value) {
|
||||
if (value == 'stopGptPrompt') {
|
||||
await window.api.DeleteBackTask(
|
||||
[DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GPT_PROMPT, null, 'default'],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('停止推理任务成功')
|
||||
}
|
||||
)
|
||||
} else if (value == 'stopGenerateImage') {
|
||||
await window.api.DeleteBackTask(
|
||||
[DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GENERATE_IMAGE, null, 'default'],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('停止生成图片任务成功')
|
||||
}
|
||||
)
|
||||
await window.api.DeleteBackTask(
|
||||
[DEFINE_STRING.QUEUE_BATCH.MJ_ORIGINAL_GENERATE_IMAGE, null, 'default'],
|
||||
(value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message)
|
||||
return
|
||||
}
|
||||
message.success('停止生成图片任务成功')
|
||||
}
|
||||
)
|
||||
} else if (value == 'resetPrompt') {
|
||||
// 重置提示词
|
||||
debugger
|
||||
console.log(data)
|
||||
await ResetPrompt()
|
||||
} else if (value == 'resetImage') {
|
||||
await ResetImage()
|
||||
}
|
||||
}
|
||||
|
||||
// 重置所有数据
|
||||
async function resetALLData() {
|
||||
await ResetPrompt()
|
||||
await ResetImage()
|
||||
}
|
||||
|
||||
return {
|
||||
ImportWord,
|
||||
data,
|
||||
GetCharacter,
|
||||
tagTreeData,
|
||||
GPTPrompt,
|
||||
OpenPromptSetting,
|
||||
GenerateImageAll,
|
||||
auto_save,
|
||||
railStyle,
|
||||
AutoSaveUpdate: (value) => {
|
||||
debugger
|
||||
auto_save.value = value
|
||||
window.config.auto_save = value
|
||||
},
|
||||
GptButtonOptions: [{ label: '停止推理任务', key: 'stopGptPrompt' }],
|
||||
GenerateImageOptions: [{ label: '停止生成图片任务', key: 'stopGenerateImage' }],
|
||||
ResetDataOptions: [
|
||||
{ label: '重置提示词', key: 'resetPrompt' },
|
||||
{ label: '重置图片', key: 'resetImage' }
|
||||
],
|
||||
ButtonSelect,
|
||||
resetALLData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<n-card>
|
||||
<n-tabs class="card-tabs" default-value="background_music_setting" size="large" animated>
|
||||
<n-tab-pane name="background_music_setting" tab="背景音乐设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">设置背景乐对应的文件夹,选择对应的分类,可以随机匹配音乐</div>
|
||||
</div>
|
||||
<BackgroundMusic></BackgroundMusic>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="keyfarme_setting" tab="关键帧设置">
|
||||
<KeyFrameSetting></KeyFrameSetting>
|
||||
</n-tab-pane>
|
||||
|
||||
<!-- <n-tab-pane name="text_setting" tab="字幕设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">在剪映里面设置一个字体模板,然后选择草稿文件(只提取第一个句子),点击提取</div>
|
||||
<div style="color: red;">提取所有的内容,显示字体,颜色,字号,偏移量(根据视频比例会出现不同)</div>
|
||||
</div>
|
||||
<n-divider />
|
||||
<div style="display: flex;">
|
||||
<n-input placeholder="输入预设名称" v-model:value="textName" @keydown.enter.prevent
|
||||
:style="{ flex: '4', marginRight: '10px' }"></n-input>
|
||||
<SelectDraft ref="selectDraft" style="flex: 5;"></SelectDraft>
|
||||
<n-button round type="primary" @click="getTextStyle" style="flex: 1; margin: 0 0 0 30px ;">提取</n-button>
|
||||
</div>
|
||||
<n-divider />
|
||||
<n-data-table :columns="columns" :data="data" :row-props="rowProps" />
|
||||
<n-dropdown placement="bottom-start" trigger="manual" :x="x" :y="y" :options="options" :show="showDropdown"
|
||||
:on-clickoutside="onClickoutside" @select="handleSelect" />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="friendly_reminder_setting" tab="提示设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">在剪映里面设置一个右上角提示,然后选择草稿文件(只能设置一个),点击提取</div>
|
||||
</div>
|
||||
<n-divider />
|
||||
<div style="display: flex;">
|
||||
<n-input placeholder="提示名称" v-model:value="friendlyReminderTextName" @keydown.enter.prevent
|
||||
:style="{ flex: '3', marginRight: '10px' }"></n-input>
|
||||
<SelectDraft ref="friendlyReminderSelectDraft" style="flex: 5;"></SelectDraft>
|
||||
<n-button round type="primary" @click="GetFriendlyReminder"
|
||||
style="flex: 1; margin: 0 0 0 30px ;">提取</n-button>
|
||||
</div>
|
||||
<n-divider />
|
||||
<n-data-table :columns="friendlyReminderColums" :data="friendlyReminderData"
|
||||
:row-props="FriendlyReminderProps" />
|
||||
<n-dropdown placement="bottom-start" trigger="manual" :x="x" :y="y" :options="options"
|
||||
:show="FriendlyReminderShowDropdown" :on-clickoutside="onClickoutside"
|
||||
@select="handleFriendReminderSelect" />
|
||||
</n-tab-pane> -->
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, nextTick } from "vue";
|
||||
import { NButton, NInput, NCard, NTabs, NTabPane, NDivider, useMessage, NDataTable, NDropdown } from "naive-ui"
|
||||
import SelectDraft from "../Components/SelectDraft.vue";
|
||||
import BackgroundMusic from "./Components/BackgroundMusic.vue"
|
||||
import KeyFrameSetting from "./Components/KeyFrameSetting.vue"
|
||||
const createColumns = (type) => {
|
||||
if (type == "srt") {
|
||||
return [
|
||||
{
|
||||
title: '名字',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: "视频比例",
|
||||
key: "ratio"
|
||||
},
|
||||
|
||||
{
|
||||
title: '字体',
|
||||
key: 'fonts'
|
||||
},
|
||||
{
|
||||
title: '字号',
|
||||
key: 'font_size'
|
||||
},
|
||||
{
|
||||
title: '字体样式',
|
||||
key: 'font_style'
|
||||
},
|
||||
{
|
||||
title: '偏移量X',
|
||||
key: 'transformX'
|
||||
},
|
||||
{
|
||||
title: '偏移量Y',
|
||||
key: 'transformY'
|
||||
}
|
||||
]
|
||||
}
|
||||
else if (type == "FR") {
|
||||
return [
|
||||
{
|
||||
title: "名字",
|
||||
key: "name"
|
||||
},
|
||||
{
|
||||
title: "提示字",
|
||||
key: "text_value"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: "编辑",
|
||||
key: "edit"
|
||||
},
|
||||
{
|
||||
label: () => h("span", { style: { color: "red" } }, "删除"),
|
||||
key: "delete"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NInput, NCard, NTabs, NTabPane, NDivider, NDataTable, SelectDraft, NDropdown, BackgroundMusic, KeyFrameSetting
|
||||
},
|
||||
setup() {
|
||||
let draf_name = ref("");
|
||||
let selectDraft = ref(null);
|
||||
let friendlyReminderSelectDraft = ref(null);
|
||||
let message = useMessage();
|
||||
let textName = ref(null);
|
||||
let friendlyReminderTextName = ref("");
|
||||
let data = ref([]);
|
||||
let friendlyReminderData = ref([]);
|
||||
const xRef = ref(0);
|
||||
const yRef = ref(0);
|
||||
const showDropdown = ref(false);
|
||||
const FriendlyReminderShowDropdown = ref(false);
|
||||
let selectOption = "";
|
||||
let FriendLyOption = "";
|
||||
|
||||
/**
|
||||
* 提取字体样式
|
||||
*/
|
||||
async function getTextStyle() {
|
||||
let draft_name = selectDraft.value.draf_name;
|
||||
if (draft_name == null || draf_name == "" || textName.value == null || textName.value == "") {
|
||||
message.error("请选择草稿");
|
||||
return;
|
||||
}
|
||||
await window.api.getDraftTextStyle([textName.value, draft_name], (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("提取成功");
|
||||
getDraftFileList();
|
||||
} else {
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字幕样式泪列表
|
||||
*/
|
||||
async function getDraftFileList() {
|
||||
data.value = [];
|
||||
await window.api.getTextStyleList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
let tabledatas = value.value.filter(item => item.id != "0" && item.id != "1");
|
||||
for (let i = 0; i < tabledatas.length; i++) {
|
||||
const element = tabledatas[i];
|
||||
let obj = {
|
||||
id: element.id,
|
||||
name: element.name,
|
||||
fonts: element.fonts,
|
||||
font_size: element.font_size,
|
||||
font_style: element.style_name,
|
||||
transformX: element.clip.transform.x,
|
||||
transformY: element.clip.transform.y,
|
||||
ratio: element.ratio
|
||||
}
|
||||
data.value.push(obj)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取温馨提示列表(右上角)
|
||||
*/
|
||||
async function getFriendlyReminderList() {
|
||||
debugger;
|
||||
friendlyReminderData.value = [];
|
||||
await window.api.getFriendlyReminderList((value) => {
|
||||
friendlyReminderData.value = value.value.filter(item => item.id != "0" && item.id != "1")
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取温馨提示样式
|
||||
*/
|
||||
async function GetFriendlyReminder() {
|
||||
let friendlyReminderSelectDraft_name = friendlyReminderSelectDraft.value.draf_name;
|
||||
debugger;
|
||||
console.log(friendlyReminderSelectDraft_name);
|
||||
if (friendlyReminderTextName.value == "" || friendlyReminderTextName.value == null || friendlyReminderSelectDraft_name == "" || friendlyReminderSelectDraft_name == null) {
|
||||
message.error("请选择草稿地址和名称");
|
||||
return;
|
||||
}
|
||||
await window.api.GetFriendlyReminder([friendlyReminderSelectDraft_name, friendlyReminderTextName.value], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("写入成功!!");
|
||||
await getFriendlyReminderList();
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getDraftFileList();
|
||||
await getFriendlyReminderList();
|
||||
})
|
||||
|
||||
return {
|
||||
draf_name,
|
||||
getTextStyle,
|
||||
selectDraft,
|
||||
friendlyReminderSelectDraft,
|
||||
textName,
|
||||
data,
|
||||
options,
|
||||
x: xRef,
|
||||
y: yRef,
|
||||
showDropdown,
|
||||
FriendlyReminderShowDropdown,
|
||||
friendlyReminderTextName,
|
||||
getDraftFileList,
|
||||
GetFriendlyReminder,
|
||||
friendlyReminderData,
|
||||
columns: createColumns("srt"),
|
||||
friendlyReminderColums: createColumns("FR"),
|
||||
handleSelect: async (value) => {
|
||||
let id = JSON.parse(selectOption).id;
|
||||
// 删除对应的
|
||||
if (value == "delete") {
|
||||
await window.api.deleteDraftTextStyle(id, async (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await getDraftFileList();
|
||||
} else {
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
showDropdown.value = false;
|
||||
},
|
||||
handleFriendReminderSelect: async (value) => {
|
||||
let id = JSON.parse(FriendLyOption).id;
|
||||
// 删除对应的
|
||||
if (value == "delete") {
|
||||
await window.api.deleteClipStettingFriendlyReminder(id, async (value) => {
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await getFriendlyReminderList();
|
||||
} else {
|
||||
message.error(value.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
FriendlyReminderShowDropdown.value = false;
|
||||
},
|
||||
onClickoutside() {
|
||||
showDropdown.value = false;
|
||||
},
|
||||
rowProps: (row) => {
|
||||
return {
|
||||
onContextmenu: (e) => {
|
||||
selectOption = JSON.stringify(row, null, 2);
|
||||
e.preventDefault();
|
||||
showDropdown.value = false;
|
||||
nextTick().then(() => {
|
||||
showDropdown.value = true;
|
||||
xRef.value = e.clientX;
|
||||
yRef.value = e.clientY;
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
FriendlyReminderProps: (row) => {
|
||||
return {
|
||||
onContextmenu: (e) => {
|
||||
FriendLyOption = JSON.stringify(row, null, 2);
|
||||
e.preventDefault();
|
||||
FriendlyReminderShowDropdown.value = false;
|
||||
nextTick().then(() => {
|
||||
FriendlyReminderShowDropdown.value = true;
|
||||
xRef.value = e.clientX;
|
||||
yRef.value = e.clientY;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-tabs class="card-tabs" default-value="signin" size="large" animated pane-wrapper-style="margin: 0 -4px"
|
||||
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;">
|
||||
<n-tab-pane name="signin" tab="GPT服务商">
|
||||
<n-form ref="formRef" :model="gpt_select_value" :rules="rules">
|
||||
<n-form-item label="选择自定义的GPT服务商">
|
||||
<n-select style="width: 300px;" :options="gpt_options"
|
||||
v-model:value="gpt_select_key"></n-select>
|
||||
<n-button color="#e18a3b" style="margin-left: 10px;" @click="EditOption">编辑</n-button>
|
||||
<n-button color="#984f31" style="margin-left: 10px;" @click="AddOption">添加</n-button>
|
||||
<n-button type="error" style="margin-left: 10px;" @click="DeleteOption">删除</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item path="label" label="GPT 名称">
|
||||
<n-input v-model:value="gpt_select_value.label"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="value" label="GPT请求网址(写全)">
|
||||
<n-input v-model:value="gpt_select_value.value"></n-input>
|
||||
</n-form-item>
|
||||
<div style="text-align: right;">
|
||||
<n-button type="primary" @click="SaveGptOption">保存</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="signup" tab="GPT模型">
|
||||
<n-form ref="modelFormRef" :model="gpt_model_select_value" :rules="modelRules">
|
||||
<n-form-item label="选择自定义的GPT模型">
|
||||
<n-select style="width: 300px;" :options="gpt_model_options"
|
||||
v-model:value="gpt_model_select_key"></n-select>
|
||||
<n-button color="#e18a3b" style="margin-left: 10px;" @click="EditModelOption">编辑</n-button>
|
||||
<n-button color="#984f31" style="margin-left: 10px;" @click="AddModelOption">添加</n-button>
|
||||
<n-button type="error" style="margin-left: 10px;" @click="DeleteModelOption">删除</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item path="label" label="GPT 模型名称">
|
||||
<n-input v-model:value="gpt_model_select_value.label"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="value" label="GPT 模型值">
|
||||
<n-input v-model:value="gpt_model_select_value.value"></n-input>
|
||||
</n-form-item>
|
||||
<div style="text-align: right;">
|
||||
<n-button type="primary" @click="SaveGptModelOption">保存</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRaw, onMounted, defineComponent } from "vue"
|
||||
import { NForm, NFormItem, NInput, NInputNumber, useDialog, NButton, useMessage, NSelect, NTabs, NTabPane } from "naive-ui"
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NButton, NSelect, NTabs, NTabPane
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let gpt_model_options = ref([]);
|
||||
let gpt_options = ref([]);
|
||||
let gpt_select_value = ref({});
|
||||
let gpt_select_key = ref("");
|
||||
let gpt_model_select_value = ref({});
|
||||
let gpt_model_select_key = ref("");
|
||||
let formRef = ref(null);
|
||||
let modelFormRef = ref(null);
|
||||
|
||||
/**
|
||||
* 加载GPT的配置信息
|
||||
*/
|
||||
async function InitGptOptions() {
|
||||
await window.api.getGptBusinessOption("dynamic", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data.length == 0) {
|
||||
message.error("请先添加自定义GPT服务商");
|
||||
return;
|
||||
}
|
||||
gpt_options.value = value.data;
|
||||
gpt_select_key.value = value.data[0].value;
|
||||
gpt_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
};
|
||||
})
|
||||
|
||||
await window.api.getGptModelOption("dynamic", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data.length == 0) {
|
||||
message.error("请先添加自定义GPT模型");
|
||||
return;
|
||||
}
|
||||
gpt_model_options.value = value.data;
|
||||
gpt_model_select_key.value = value.data[0].value;
|
||||
gpt_model_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
await InitGptOptions();
|
||||
})
|
||||
|
||||
/**
|
||||
* 保存当前的GPT配置信息
|
||||
*/
|
||||
async function SaveGptOption(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 保存逻辑
|
||||
await window.api.SaveDynamicGptOption([JSON.stringify(toRaw(gpt_select_value.value)), "gpt_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前选中的GPT配置信息,写入下面已编辑
|
||||
*/
|
||||
async function EditOption() {
|
||||
let temp = gpt_options.value.find(x => x.value == gpt_select_key.value);
|
||||
gpt_select_value.value = cloneDeep(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前选中的GPT模型配置信息,写入下面已编辑
|
||||
*/
|
||||
async function EditModelOption() {
|
||||
let temp = gpt_model_options.value.find(x => x.value == gpt_model_select_key.value);
|
||||
gpt_model_select_value.value = cloneDeep(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前选中的GPT服务商信息
|
||||
*/
|
||||
async function DeleteOption() {
|
||||
let temp = gpt_options.value.find(x => x.value == gpt_select_key.value);
|
||||
let id = temp.id;
|
||||
await window.api.DeleteDynamicGptOption([id, "gpt_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前选中的GPT服务商信息
|
||||
*/
|
||||
async function DeleteModelOption() {
|
||||
let temp = gpt_model_options.value.find(x => x.value == gpt_model_select_key.value);
|
||||
let id = temp.id;
|
||||
await window.api.DeleteDynamicGptOption([id, "gpt_model_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存当前的GPT配置信息
|
||||
*/
|
||||
async function SaveGptModelOption(e) {
|
||||
e.preventDefault();
|
||||
modelFormRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 保存逻辑
|
||||
await window.api.SaveDynamicGptOption([JSON.stringify(toRaw(gpt_model_select_value.value)), "gpt_model_options"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加成功");
|
||||
await InitGptOptions();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个GPT服务商,只是清楚当前的数据
|
||||
*/
|
||||
async function AddOption() {
|
||||
gpt_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个GPT服务商,只是清楚当前的数据
|
||||
*/
|
||||
async function AddModelOption() {
|
||||
gpt_model_select_value.value = {
|
||||
label: null,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == "")
|
||||
return new Error(errorMessage);
|
||||
return true;
|
||||
},
|
||||
trigger: ["input", "blur"]
|
||||
}]
|
||||
}
|
||||
|
||||
let rules = {
|
||||
label: ruleObj("必填GPT名称"),
|
||||
value: ruleObj("必填GPT请求网址"),
|
||||
};
|
||||
|
||||
let modelRules = {
|
||||
label: ruleObj("必填GPT名称"),
|
||||
value: ruleObj("必填GPT请求网址"),
|
||||
};
|
||||
|
||||
return {
|
||||
gpt_model_options,
|
||||
gpt_options,
|
||||
gpt_select_value,
|
||||
gpt_model_select_value,
|
||||
gpt_select_key,
|
||||
gpt_model_select_key,
|
||||
SaveGptOption,
|
||||
EditOption,
|
||||
DeleteOption,
|
||||
AddOption,
|
||||
SaveGptModelOption,
|
||||
rules,
|
||||
modelRules,
|
||||
formRef,
|
||||
modelFormRef,
|
||||
EditModelOption,
|
||||
AddModelOption,
|
||||
DeleteModelOption
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-tabs .n-tabs-nav--bar-type {
|
||||
padding-left: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div style="height: 50px; display: flex; align-items: center;"> <n-select placeholder="选择编辑或是删除的数据"
|
||||
style="width: 300px;" v-model:value="customize_gpt_select" :options="customize_gpt_options"></n-select>
|
||||
<n-button size="small" style="margin: 0 10px 0 10px" color="#e18a3b"
|
||||
@click="EditCurrentSelectPrompt">编辑</n-button>
|
||||
<n-button size="small" style="margin: 0 10px 0 0px" color="#779649" @click="AddGptPrompt">新增</n-button>
|
||||
<n-button size="small" style="margin: 0 10px 0 0px" type="error" @click="DeleteGptPrompt">删除</n-button>
|
||||
</div>
|
||||
<div id="add-gpt-prompts" style="overflow-y: auto; padding-right: 20px;">
|
||||
<n-form ref="promptRef" :model="prompt_data" :rules="rules">
|
||||
<n-form-item label="提示词预设名字">
|
||||
<n-input v-model:value="prompt_data.prompt_name" placeholder="输入预设名字"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item label="提示词角色预设">
|
||||
<n-input type="textarea" style="height: 150px;" :autosize="true"
|
||||
v-model:value="prompt_data.prompt_roles" placeholder="输入提示词角色预设"></n-input>
|
||||
</n-form-item>
|
||||
<div v-for="(example, index) in prompt_data.prompt_example">
|
||||
<n-form-item :label="'案例输入 ' + (index + 1)">
|
||||
<n-input type="textarea" style="height: 60px ;margin-right: 10px;" :autosize="true"
|
||||
v-model:value="example.user_content" placeholder="输入案例输入"></n-input>
|
||||
<n-button color="#e18a3b" @click="GenerateGptExampleOut(example, index)">生成输出</n-button>
|
||||
<n-button tertiary type="error" style="font-size: 24px" @click="DeleteExampleOut(example, index)">
|
||||
<n-icon>
|
||||
<trash />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item :label="'案例输出 ' + (index + 1)">
|
||||
<n-input type="textarea" style="height: 60px" :autosize="true"
|
||||
v-model:value="example.assistant_content" placeholder="输入案例输出或是测试生成"></n-input>
|
||||
</n-form-item>
|
||||
</div>
|
||||
</n-form>
|
||||
</div>
|
||||
<div style="float: right; margin: 30px;">
|
||||
<n-button color="#e18a3b" @click="AddExamplePrompt">添加案例输出</n-button>
|
||||
<n-checkbox style="margin: 0 15px 0 15px;" v-model:checked="save_example">是否保存案例</n-checkbox>
|
||||
<n-button type="info" @click="SaveGptPrompt">保存</n-button>
|
||||
<!-- Your content here -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRaw, onMounted, defineComponent } from "vue"
|
||||
import { NForm, NFormItem, NInput, NInputNumber, useDialog, NButton, useMessage, NSelect, NTabs, NTabPane, NCheckbox, NIcon } from "naive-ui"
|
||||
import { cloneDeep, has, isEmpty } from "lodash";
|
||||
import { Trash } from "@vicons/ionicons5"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NButton, NSelect, NTabs, NTabPane, NCheckbox, NIcon, Trash
|
||||
},
|
||||
props: ["height"],
|
||||
|
||||
setup(props) {
|
||||
let message = useMessage();
|
||||
let hh = props.height;
|
||||
let save_example = ref(true);
|
||||
let prompt_data = ref({
|
||||
prompt_name: null,
|
||||
prompt_roles: null,
|
||||
prompt_example: []
|
||||
})
|
||||
|
||||
// 获取的所有的数据
|
||||
let customize_gpt_all_data = ref([]);
|
||||
// 下拉的options
|
||||
let customize_gpt_options = ref([]);
|
||||
// 选择的数据
|
||||
let customize_gpt_select = ref(null);
|
||||
|
||||
|
||||
// 设置窗体的高度
|
||||
function setHeight() {
|
||||
let div = document.getElementById("add-gpt-prompts");
|
||||
div.style.height = hh - 145 - 50 + "px";
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
async function InitData() {
|
||||
await window.api.GetCustomizeGptPrompt("dynamic", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
if (value.data.length == 0) {
|
||||
message.error("请先添加自定义提示词数据");
|
||||
return;
|
||||
}
|
||||
prompt_data.value = {
|
||||
prompt_name: null,
|
||||
prompt_roles: null,
|
||||
prompt_example: []
|
||||
}
|
||||
customize_gpt_all_data.value = value.data;
|
||||
customize_gpt_select.value = value.data[0].id;
|
||||
customize_gpt_options.value = value.data.map((item) => {
|
||||
return {
|
||||
label: item.prompt_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
setHeight();
|
||||
await InitData();
|
||||
})
|
||||
|
||||
/**
|
||||
* 生成案例输出
|
||||
* @param {*} input 输入的数据
|
||||
* @param {*} index 输入的数据的索引
|
||||
*/
|
||||
async function GenerateGptExampleOut(input, index) {
|
||||
console.log(input, index);
|
||||
if (!input.user_content) {
|
||||
message.error("请填写案例输入");
|
||||
return;
|
||||
}
|
||||
await window.api.GenerateGptExampleOut(JSON.stringify(toRaw(prompt_data.value)), (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
prompt_data.value.prompt_example[index].assistant_content = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加提示词案例输出数据
|
||||
*/
|
||||
async function AddExamplePrompt() {
|
||||
debugger;
|
||||
let last_data = prompt_data.value.prompt_example[prompt_data.value.prompt_example.length - 1];
|
||||
if (last_data != null) {
|
||||
if (isEmpty(last_data.user_content)) {
|
||||
message.error("上一个案例输入没有填写");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEmpty(last_data.assistant_content)) {
|
||||
message.error("上一个案例输出没有生成或填写");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
prompt_data.value.prompt_example.push({
|
||||
user_content: null,
|
||||
assistant_content: null
|
||||
})
|
||||
}
|
||||
|
||||
// 删除案例输出
|
||||
async function DeleteExampleOut(example, index) {
|
||||
debugger;
|
||||
if (index > prompt_data.value.prompt_example.length - 1) {
|
||||
message.error("删除的案例不存在");
|
||||
return;
|
||||
}
|
||||
prompt_data.value.prompt_example.splice(index, 1);
|
||||
}
|
||||
|
||||
// 保存提示词
|
||||
async function SaveGptPrompt() {
|
||||
// 判断是不是要保存案例输出
|
||||
let tmp_data = cloneDeep(prompt_data.value);
|
||||
if (!save_example.value) {
|
||||
tmp_data.prompt_example = [];
|
||||
}
|
||||
|
||||
// 检查数据是不是存在
|
||||
if (has(tmp_data, "prompt_name") && isEmpty(tmp_data.prompt_name)) {
|
||||
message.error("请填写提示词预设名字");
|
||||
return;
|
||||
}
|
||||
if (has(tmp_data, "prompt_roles") && isEmpty(tmp_data.prompt_roles)) {
|
||||
message.error("请填写提示词角色预设");
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tmp_data.prompt_example.length; i++) {
|
||||
if (isEmpty(tmp_data.prompt_example[i].user_content)) {
|
||||
message.error("案例输入" + (i + 1) + "没有填写");
|
||||
return;
|
||||
}
|
||||
if (isEmpty(tmp_data.prompt_example[i].assistant_content)) {
|
||||
message.error("案例输出" + (i + 1) + "没有填写");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存逻辑
|
||||
await window.api.SaveDynamicGptOption([JSON.stringify(tmp_data), "customize_gpt_prompt"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("添加成功");
|
||||
})
|
||||
|
||||
await InitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑当前选中的提示词设置
|
||||
*/
|
||||
async function EditCurrentSelectPrompt() {
|
||||
if (isEmpty(customize_gpt_select.value)) {
|
||||
message.error("请选择要编辑的数据");
|
||||
return;
|
||||
}
|
||||
let current_data = customize_gpt_all_data.value.find((item) => item.id == customize_gpt_select.value);
|
||||
if (isEmpty(current_data)) {
|
||||
message.error("当前选择的数据不存在");
|
||||
return;
|
||||
}
|
||||
prompt_data.value = current_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增提示词
|
||||
*/
|
||||
async function AddGptPrompt() {
|
||||
prompt_data.value = {
|
||||
prompt_name: null,
|
||||
prompt_roles: null,
|
||||
prompt_example: [{
|
||||
user_content: null,
|
||||
assistant_content: null
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除选中的提示词
|
||||
*/
|
||||
async function DeleteGptPrompt() {
|
||||
if (isEmpty(customize_gpt_select.value)) {
|
||||
message.error("请选择要删除的数据");
|
||||
return;
|
||||
}
|
||||
|
||||
await window.api.DeleteDynamicGptOption([toRaw(customize_gpt_select.value), "customize_gpt_prompt"], async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
})
|
||||
|
||||
await InitData();
|
||||
}
|
||||
|
||||
return {
|
||||
hh,
|
||||
prompt_data,
|
||||
save_example,
|
||||
GenerateGptExampleOut,
|
||||
AddExamplePrompt,
|
||||
DeleteExampleOut,
|
||||
SaveGptPrompt,
|
||||
customize_gpt_options,
|
||||
customize_gpt_select,
|
||||
customize_gpt_all_data,
|
||||
EditCurrentSelectPrompt,
|
||||
AddGptPrompt,
|
||||
DeleteGptPrompt
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Customize the scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<n-form ref="formRef" inline :label-width="100" :model="formValue" :size="large">
|
||||
<n-form-item label="背景音乐文件夹信息" path="formValue.name">
|
||||
<n-input style="width: 150px;" v-model:value="formValue.name" placeholder="输入名称" />
|
||||
<n-input style="width: 500px;" v-model:value="formValue.folder_path" placeholder="输入文件夹地址" />
|
||||
<n-button attr-type="button" type="primary" @click="SelectFolder">选择文件夹</n-button>
|
||||
<n-button style="margin-left: 50px;" attr-type="button" type="info"
|
||||
@click="AddBackgroundMusic">保存</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<n-data-table :columns="background_music_columns" :data="data" :pagination="pagination" :bordered="false" />
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { defineComponent, ref, h, onMounted, nextTick } from "vue";
|
||||
import { NButton, useMessage, NDataTable, NForm, NFormItem, NInput } from "naive-ui"
|
||||
|
||||
const buttonArr = [
|
||||
{
|
||||
title: "删除",
|
||||
key: "delete"
|
||||
}
|
||||
]
|
||||
|
||||
const createColumns = ({ Delete }) => {
|
||||
return [
|
||||
{
|
||||
title: "名称",
|
||||
key: "name"
|
||||
},
|
||||
{
|
||||
title: "文件夹位置",
|
||||
key: "folder_path"
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "options",
|
||||
render(row) {
|
||||
const btn = buttonArr.map(item => {
|
||||
return h(
|
||||
NButton, {
|
||||
strong: true,
|
||||
size: "small",
|
||||
type: "error",
|
||||
tertiary: true,
|
||||
onClick: () => {
|
||||
if (item.key == "edit") {
|
||||
// Edit(row);
|
||||
} else if (item.key == "delete") {
|
||||
Delete(row);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => item.title
|
||||
}
|
||||
)
|
||||
});
|
||||
return btn;
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NForm, NFormItem, NInput
|
||||
},
|
||||
setup() {
|
||||
let data = ref([]);
|
||||
let message = useMessage();
|
||||
let formValue = ref({
|
||||
name: "",
|
||||
id: "",
|
||||
folder_path: ""
|
||||
})
|
||||
|
||||
async function SelectFolder() {
|
||||
await window.api.selectFolder(null, (value) => {
|
||||
if (value.length > 0) {
|
||||
formValue.value.folder_path = value[0];
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function Delete(row) {
|
||||
await window.api.deleteClipSetting(row.id, async (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
await GetBackgroundMusicConfigList();
|
||||
})
|
||||
}
|
||||
|
||||
async function GetBackgroundMusicConfigList() {
|
||||
data.value = [];
|
||||
await window.api.GetBackgroundMusicConfigList((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
data.value = value.value;
|
||||
})
|
||||
}
|
||||
|
||||
async function AddBackgroundMusic() {
|
||||
if (formValue.value.name == "" || formValue.value.folder_path == "") {
|
||||
message.error("请填入名称和文件夹位置");
|
||||
return;
|
||||
}
|
||||
// 判断当前名称是不是存在
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const d = data.value[i];
|
||||
if (d.name == formValue.value.name) {
|
||||
message.error("名称已存在");
|
||||
return;
|
||||
}
|
||||
}
|
||||
//添加音乐文件夹
|
||||
await window.api.AddBackgroundMusicFolder([formValue.value.name, formValue.value.folder_path], async (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
return;
|
||||
}
|
||||
formValue.value.name = "";
|
||||
formValue.value.folder_path = "";
|
||||
await GetBackgroundMusicConfigList();
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await GetBackgroundMusicConfigList();
|
||||
})
|
||||
|
||||
return {
|
||||
bodyStyle: {
|
||||
width: "600px"
|
||||
},
|
||||
segmented: {
|
||||
content: "soft",
|
||||
footer: "soft"
|
||||
},
|
||||
formValue,
|
||||
data,
|
||||
background_music_columns: createColumns({
|
||||
Delete
|
||||
}),
|
||||
message,
|
||||
AddBackgroundMusic,
|
||||
SelectFolder
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div style="margin-bottom: 10px; width: 100px;">选择打帧方式</div>
|
||||
<n-select v-model:value="key_frame_data.key_frame" :options="key_frame_options" style="width: 140px;" />
|
||||
<div style="width: 170px;">
|
||||
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<n-checkbox style="padding-left: 20px;" v-model:checked="key_frame_data.isFixedSpeed">
|
||||
匀速关键帧
|
||||
</n-checkbox>
|
||||
</template>
|
||||
勾选这个,图片的时长小于这个。会匀速关键帧
|
||||
</n-tooltip>
|
||||
|
||||
</div>
|
||||
|
||||
<span>关键帧时间:</span>
|
||||
<div>
|
||||
<n-input-number :show-button="false" :step="0.1" :min="0" :max="10"
|
||||
v-model:value="key_frame_data.key_frame_time" style="width: 100px;" />
|
||||
</div>
|
||||
<div>
|
||||
<n-button style="margin-left: 30px;" type="info" @click="SaveKeyFrameSetting">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<n-card style="margin-top: 10px;" title="上下关键帧设置">
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div>
|
||||
上关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.up_down_key_frame.start_position"
|
||||
style="width: 100px; margin-right: 10px;" />
|
||||
<div>
|
||||
下关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.up_down_key_frame.end_position"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
|
||||
<div>
|
||||
缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.up_down_key_frame.default_scale"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="左右关键帧设置" style="margin-top: 10px;">
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div>
|
||||
左关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.left_right_key_frame.start_position"
|
||||
style="width: 100px; margin-right: 10px;" />
|
||||
<div>
|
||||
右关键帧位置:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.left_right_key_frame.end_position"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
|
||||
<div>
|
||||
缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.left_right_key_frame.default_scale"
|
||||
style="width: 100px; margin-right: 10px;"></n-input-number>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card title="缩放关键帧设置" style="margin-top: 10px;">
|
||||
<div style="display: flex; height: 40px; line-height: 40px;">
|
||||
<div>
|
||||
开始缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false" v-model:value="key_frame_data.scale_key_frame.start_position"
|
||||
style="width: 100px; margin-right: 10px;" />
|
||||
<div>
|
||||
结束缩放大小:
|
||||
</div>
|
||||
<n-input-number :show-button="false"
|
||||
v-model:value="key_frame_data.scale_key_frame.end_position"></n-input-number>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { useMessage, NSelect, NCheckbox, NInputNumber, NCard, NButton, NTooltip } from "naive-ui"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NSelect, NCheckbox, NInputNumber, NCard, NButton, NTooltip
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let key_frame_options = ref([]);
|
||||
let key_frame_data = ref({
|
||||
key_frame: null,
|
||||
isFixedSpeed: false,
|
||||
key_frame_time: 4,
|
||||
up_down_key_frame: {
|
||||
default_scale: 100,
|
||||
start_position: 0,
|
||||
end_position: 0
|
||||
},
|
||||
left_right_key_frame: {
|
||||
default_scale: 100,
|
||||
start_position: 0,
|
||||
end_position: 0
|
||||
},
|
||||
scale_key_frame: {
|
||||
default_scale: 100,
|
||||
start_position: 100,
|
||||
end_position: 100
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.GetKeyFrameConfigData((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error("获取关键帧配置失败");
|
||||
return;
|
||||
}
|
||||
if (value.data) {
|
||||
key_frame_data.value = value.data;
|
||||
}
|
||||
})
|
||||
|
||||
await window.api.GetKeyFrameOptions((value) => {
|
||||
if (value) {
|
||||
key_frame_options.value = value.data;
|
||||
} else {
|
||||
message.error("获取关键帧操作列表失败");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 关键帧设置保存
|
||||
*/
|
||||
async function SaveKeyFrameSetting() {
|
||||
console.log(key_frame_data.value);
|
||||
// 简单的判断
|
||||
if (key_frame_data.value.key_frame == "") {
|
||||
message.error("请选择关键帧方式");
|
||||
return;
|
||||
}
|
||||
if (key_frame_data.value.key_frame && key_frame_data.value.key_frame == "KFTypePositionY") {
|
||||
if (key_frame_data.value.up_down_key_frame.start_position == null || key_frame_data.value.up_down_key_frame.end_position == null) {
|
||||
message.error("请输入上下关键帧位置");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (key_frame_data.value.key_frame && key_frame_data.value.key_frame == "KFTypePositionX") {
|
||||
if (key_frame_data.value.left_right_key_frame.start_position == null || key_frame_data.value.left_right_key_frame.end_position == null) {
|
||||
message.error("请输入左右关键帧位置");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (key_frame_data.value.key_frame && key_frame_data.value.key_frame == "KFTypeScale") {
|
||||
if (key_frame_data.value.scale_key_frame.start_position == null || key_frame_data.value.scale_key_frame.end_position == null) {
|
||||
message.error("请输入缩放关键帧位置");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await window.api.SaveKeyFrameSetting(JSON.stringify(toRaw(key_frame_data.value)), (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("保存成功");
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
key_frame_data,
|
||||
key_frame_options,
|
||||
SaveKeyFrameSetting
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<n-button type="info" round @click="OpenDiscordWindow">打开登录MJ</n-button>
|
||||
<n-button type="info" round @click="GetChannelRobots" style="margin-left: 10px;">获取频道机器人</n-button>
|
||||
<n-button type="info" round @click="SaveMjSetting" style="margin-left: 10px;">保存MJ配置</n-button>
|
||||
<n-divider />
|
||||
<n-form ref="formRef" label-placement="top" :model="mjSetting" :rules="rules">
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="服务器ID" style="width: 400px;" path="serviceID">
|
||||
<n-input v-model:value="mjSetting.serviceID" placeholder="登录MJ后切换服务器自动获取" />
|
||||
</n-form-item>
|
||||
<n-form-item label="频道ID" style="width: 400px; margin-left: 10px" path="channelID">
|
||||
<n-input v-model:value="mjSetting.channelID" placeholder="登录MJ后切换频道自动获取" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="MJ机器ID" path="mj.botId">
|
||||
<n-input v-model:value="mjSetting.mj.botId" placeholder="MJ机器ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ命令ID" style="margin-left: 10px" path="mj.commandId">
|
||||
<n-input v-model:value="mjSetting.mj.commandId" placeholder="MJ命令ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ命令名称" style="margin-left: 10px" path="mj.commandName">
|
||||
<n-input v-model:value="mjSetting.mj.commandName" placeholder="MJ命令名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="MJ版本ID" style="margin-left: 10px" path="mj.versionId">
|
||||
<n-input v-model:value="mjSetting.mj.versionId" placeholder="MJ版本ID" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="NIJI机器ID" path="niji.botId">
|
||||
<n-input v-model:value="mjSetting.niji.botId" placeholder="NIJI机器ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="NIJI命令ID" style="margin-left: 10px" path="niji.commandId">
|
||||
<n-input v-model:value="mjSetting.niji.commandId" placeholder="NIJI命令ID" />
|
||||
</n-form-item>
|
||||
<n-form-item label="NIJI命令名称" style="margin-left: 10px" path="niji.commandName">
|
||||
<n-input v-model:value="mjSetting.niji.commandName" placeholder="NIJI命令名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="NIJI版本ID" style="margin-left: 10px" path="niji.versionId">
|
||||
<n-input v-model:value="mjSetting.niji.versionId" placeholder="NIJI版本ID" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
|
||||
<n-form-item label="用户token" style="width: 810px;" path="token">
|
||||
<n-input v-model:value="mjSetting.token" placeholder="登录MJ后切换服务器自动获取" />
|
||||
</n-form-item>
|
||||
<n-form-item label="用户Agent" style="width: 810px;" path="userAgent.userAgent">
|
||||
<n-input v-model:value="mjSetting.userAgent.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.userAgent.customize" style="width: 100px;">
|
||||
自定义
|
||||
</n-checkbox>
|
||||
</template>
|
||||
开启自定义需要手动填写userAgent。可以填入浏览器的userAgent
|
||||
</n-tooltip>
|
||||
</n-form-item>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<n-form-item label="选择生图机器人" path="select_robot" style="width: 200px;">
|
||||
<n-select :options="select_robot_options" v-model:value="mjSetting.select_robot"></n-select>
|
||||
</n-form-item>
|
||||
<n-button color="#e18a3b" style="margin-left : 3px" @click="RefreshRobotOptions">
|
||||
<n-icon size="large">
|
||||
<reload></reload>
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, nextTick, watch, toRaw } from "vue";
|
||||
import { NButton, useMessage, NDataTable, NForm, NFormItem, NInput, NDivider, NCheckbox, NSelect, NTooltip, NIcon } from "naive-ui"
|
||||
import { DEFINE_STRING } from "../../../../define/define_string";
|
||||
import { Reload } from "@vicons/ionicons5"
|
||||
import { isEmpty } from "lodash";
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NButton, NDataTable, NForm, NFormItem, NInput, NDivider, NCheckbox, NSelect, NTooltip, NIcon, Reload
|
||||
},
|
||||
setup() {
|
||||
let message = useMessage();
|
||||
let formRef = ref(null);
|
||||
|
||||
let mjSetting = ref({
|
||||
serviceID: null,
|
||||
channelID: null,
|
||||
mj: {
|
||||
botId: null,
|
||||
commandId: null,
|
||||
commandName: null,
|
||||
versionId: null
|
||||
},
|
||||
niji: {
|
||||
botId: null,
|
||||
commandId: null,
|
||||
commandName: null,
|
||||
versionId: null
|
||||
},
|
||||
token: null,
|
||||
userAgent: {
|
||||
userAgent: null,
|
||||
customize: false
|
||||
|
||||
},
|
||||
select_robot: null,
|
||||
});
|
||||
|
||||
function checkNull(obj) {
|
||||
for (let key in obj) {
|
||||
if (obj[key] == undefined) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function modifyOption(value) {
|
||||
// 遍历mjSetting.mj,判断里面的属性数据是不是都存在,如果存在则添加到 select_robot_options 中
|
||||
select_robot_options.value = [];
|
||||
|
||||
let isObj = checkNull(value.mj);
|
||||
if (isObj) {
|
||||
select_robot_options.value.push({
|
||||
label: "MJ",
|
||||
value: value.mj.botId
|
||||
})
|
||||
}
|
||||
isObj = checkNull(value.niji);
|
||||
|
||||
if (isObj) {
|
||||
select_robot_options.value.push({
|
||||
label: "NIJI",
|
||||
value: value.niji.botId
|
||||
})
|
||||
}
|
||||
// 判断当前的select_robot是不是在select_robot_options中,如果不在则设置为null
|
||||
if (select_robot_options.value.findIndex((item) => { return item.value == mjSetting.value.select_robot }) == -1)
|
||||
mjSetting.value.select_robot = null;
|
||||
}
|
||||
|
||||
// 监听数据改变,生成下拉的options
|
||||
watch(mjSetting.value, (value) => {
|
||||
mjSetting.value = value;
|
||||
// 计算下拉的options
|
||||
modifyOption(value)
|
||||
})
|
||||
|
||||
|
||||
|
||||
let select_robot_options = ref([])
|
||||
|
||||
// 初始化现在设置的数据
|
||||
async function InitData() {
|
||||
// 获取当前mj配置信息
|
||||
await window.api.GetDefineConfigJsonByProperty(JSON.stringify(["img_base", "mj_config", false, null]), (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
// 判断是不是有数据
|
||||
if (value.data) {
|
||||
mjSetting.value = value.data;
|
||||
modifyOption(value.data);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
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.serviceID = value.data.serviceID;
|
||||
mjSetting.value.channelID = value.data.channelID;
|
||||
} else if (value.type == DEFINE_STRING.DISCORD_SIMPLE_DATA_TYPE.TOKEN) {
|
||||
mjSetting.value.token = value.data.authorization;
|
||||
if (!mjSetting.value.userAgent.customize) {
|
||||
mjSetting.value.userAgent.userAgent = value.data.userAgent;
|
||||
}
|
||||
} else {
|
||||
message.error("未知的数据类型");
|
||||
}
|
||||
})
|
||||
|
||||
await InitData();
|
||||
})
|
||||
|
||||
/**
|
||||
* 打开discord窗口
|
||||
*/
|
||||
async function OpenDiscordWindow() {
|
||||
await window.api.OpenDiscordWindow((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道机器人
|
||||
*/
|
||||
async function GetChannelRobots() {
|
||||
|
||||
// 判断是不是有数据(服务器ID,频道ID,用户token,用户Agent)
|
||||
if (isEmpty(mjSetting.value.serviceID) &&
|
||||
isEmpty(mjSetting.value.channelID) &&
|
||||
isEmpty(mjSetting.value.token) &&
|
||||
isEmpty(mjSetting.value.userAgent.userAgent)) {
|
||||
message.error("请先填写或获取完整的数据(服务器ID,频道ID,用户token,用户Agent)");
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'authorization': mjSetting.value.token
|
||||
}
|
||||
|
||||
await fetch(`https://discord.com/api/v9/guilds/${mjSetting.value.serviceID}/application-command-index`, {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
debugger
|
||||
// 修改配置信息(判断是不是niji或者是mj)
|
||||
// 填充mj,niji数据
|
||||
if (data.applications && data.application_commands) {
|
||||
let mj_inedx = data.applications.findIndex((item) => {
|
||||
return item.name.toLowerCase().startsWith("midjourney");
|
||||
})
|
||||
if (mj.index != -1) {
|
||||
mjSetting.value.mj.botId = data.applications[mj_inedx].id;
|
||||
let f = data.application_commands.find((a) => { return a.name == "imagine" && a.application_id == data.applications[mj_inedx].id })
|
||||
mjSetting.value.mj.commandId = f?.id;
|
||||
mjSetting.value.mj.commandName = "imagine";
|
||||
mjSetting.value.mj.versionId = f?.version;
|
||||
}
|
||||
|
||||
let niji_inedx = data.applications.findIndex((item) => {
|
||||
return item.name.toLowerCase().startsWith("niji");
|
||||
})
|
||||
if (mj.index != -1) {
|
||||
mjSetting.value.niji.botId = data.applications[niji_inedx].id;
|
||||
let f = data.application_commands.find((a) => { return a.name == "imagine" && a.application_id == data.applications[niji_inedx].id })
|
||||
mjSetting.value.niji.commandId = f?.id;
|
||||
mjSetting.value.niji.commandName = "imagine";
|
||||
mjSetting.value.niji.versionId = f?.version;
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
window.api.showGlobalNotificationDialog({
|
||||
code: 0,
|
||||
message: "获取频道机器人请求失败,错误信息如下: " + error.message.toString()
|
||||
})
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存MJ配置
|
||||
*/
|
||||
async function SaveMjSetting(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
|
||||
await window.api.SaveDefineConfigJsonByProperty(JSON.stringify(["img_base", "mj_config", toRaw(mjSetting.value), false]), (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success(value.message);
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
async function RefreshRobotOptions() {
|
||||
modifyOption(mjSetting.value);
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == "")
|
||||
return new Error(errorMessage);
|
||||
return true;
|
||||
},
|
||||
trigger: ["input", "blur", "change"]
|
||||
}]
|
||||
}
|
||||
let rules = {
|
||||
"serviceID": ruleObj("必填人物名称"),
|
||||
"channelID": ruleObj("必填英文提示词"),
|
||||
|
||||
"mj.botId": ruleObj("必填英文提示词"),
|
||||
|
||||
"mj.commandId": ruleObj("必填英文提示词"),
|
||||
"mj.commandName": ruleObj("必填英文提示词"),
|
||||
"mj.versionId": ruleObj("必填英文提示词"),
|
||||
"niji.botId": ruleObj("必填英文提示词"),
|
||||
"niji.commandId": ruleObj("必填英文提示词"),
|
||||
"niji.commandName": ruleObj("必填英文提示词"),
|
||||
"niji.versionId": ruleObj("必填英文提示词"),
|
||||
"token": ruleObj("必填英文提示词"),
|
||||
"userAgent.userAgent": ruleObj("必填英文提示词"),
|
||||
"select_robot": ruleObj("必填英文提示词"),
|
||||
};
|
||||
|
||||
return {
|
||||
OpenDiscordWindow,
|
||||
mjSetting,
|
||||
select_robot_options,
|
||||
GetChannelRobots,
|
||||
SaveMjSetting,
|
||||
RefreshRobotOptions,
|
||||
rules,
|
||||
formRef
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<n-tabs type="line" animated size="large">
|
||||
<n-tab-pane name="normal setting" tab="基础设置">
|
||||
<n-form ref="formRef" :label-width="80" :model="formValue">
|
||||
<n-form-item label="SD请求地址" path="webui_api_url">
|
||||
<n-input v-model:value="formValue.webui_api_url" placeholder="输入SD地址" />
|
||||
</n-form-item>
|
||||
<n-form-item label="出图方式(文生图、图生图)" path="type">
|
||||
<n-select v-model:value="formValue.type" :options="modelOption" placeholder="输入采样方法" />
|
||||
</n-form-item>
|
||||
<n-form-item label="正向提示词(和推导出来的tag进行拼接)" path="prompt">
|
||||
<n-input v-model:value="formValue.prompt" placeholder="输入正向提示词" />
|
||||
</n-form-item>
|
||||
<n-form-item label="反向提示词" path="negative_prompt">
|
||||
<n-input v-model:value="formValue.negative_prompt" placeholder="输入反向提示词" />
|
||||
</n-form-item>
|
||||
<div style="display: flex">
|
||||
<n-form-item label="CFG Scale" style="margin-right: 30px;">
|
||||
<n-input-number :min="0" :max="30" :step="0.05" v-model:value="formValue.cfg_scale"
|
||||
placeholder="输入重绘幅度" />
|
||||
</n-form-item>
|
||||
<n-form-item label="重绘幅度" path="denoising_strength">
|
||||
<n-input-number :precision="2" :step="0.05" v-model:value="formValue.denoising_strength"
|
||||
placeholder="输入重绘幅度" />
|
||||
<n-checkbox style="margin-left: 30px;" size="large" v-model:checked="formValue.adetailer"
|
||||
label="是否开启修脸/修手" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
<n-form-item label="采样方式" path="sampler_name">
|
||||
<n-input v-model:value="formValue.sampler_name" placeholder="输入模型" />
|
||||
</n-form-item>
|
||||
<div style="display: flex;">
|
||||
<n-form-item label="迭代步数" path="steps">
|
||||
<n-input-number style="width: 150px;" v-model:value="formValue.steps" placeholder="输入迭代步数" />
|
||||
</n-form-item>
|
||||
<n-form-item label="风格词权重" path="style_weight" style="margin-left: 10px;">
|
||||
<n-input-number :min="1" :max="4" :step="0.05" :precision="2" style="width: 150px;"
|
||||
v-model:value="formValue.style_weight" placeholder="输入风格词权重" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
<n-form-item label="图片分辨率" path="resolution">
|
||||
<n-input-number :show-button="false" v-model:value="formValue.width" placeholder="输入迭代步数" />
|
||||
<span style="margin: 0 10px;"> * </span>
|
||||
<n-input-number :show-button="false" v-model:value="formValue.height" placeholder="输入迭代步数" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button attr-type="button" type="primary" @click="SaveSDConfig">
|
||||
保存设置
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="ADetailer setting" tab="ADetailer 设置">
|
||||
<SDADetailerSetting />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { NForm, NFormItem, NInput, NButton, useMessage, NInputNumber, NSelect, NTabs, NTabPane, NCheckbox } from "naive-ui"
|
||||
import SDADetailerSetting from "../Components/SDADetailerSetting.vue"
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NButton, NInputNumber, NSelect, NTabs, NTabPane, SDADetailerSetting, NCheckbox
|
||||
},
|
||||
setup() {
|
||||
let formValue = ref({
|
||||
webui_api_url: window.config.webui_api_url,
|
||||
type: null,
|
||||
prompt: null,
|
||||
negative_prompt: null,
|
||||
denoising_strength: 1,
|
||||
sampler_name: null,
|
||||
steps: 15,
|
||||
width: 0,
|
||||
height: 0,
|
||||
adetailer: false,
|
||||
style_weight: 1,
|
||||
cfg_scale: 1
|
||||
})
|
||||
|
||||
let modelOption = ref([{
|
||||
label: "文生图",
|
||||
value: "txt2img"
|
||||
}, {
|
||||
label: "图生图",
|
||||
value: "img2img"
|
||||
}]);
|
||||
|
||||
onMounted(async () => {
|
||||
await window.api.InitSDConfig((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
formValue.value = value.data;
|
||||
})
|
||||
})
|
||||
|
||||
let message = useMessage();
|
||||
/**
|
||||
* 保存SD设置
|
||||
*/
|
||||
async function SaveSDConfig() {
|
||||
// 保存SD配置
|
||||
await window.api.SaveSDConfig(toRaw(formValue.value), (value) => {
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
return;
|
||||
} else if (value.code == 1) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
window.api.getSettingDafultData((value) => {
|
||||
window.config = value;
|
||||
})
|
||||
return
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "未知错误" });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
formValue,
|
||||
SaveSDConfig,
|
||||
modelOption,
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<NSpace vertical>
|
||||
<n-spin :show="show">
|
||||
<template #description>
|
||||
保存中
|
||||
</template>
|
||||
<n-form ref="formRef" :model="formValue" :rules="rules">
|
||||
<n-form-item path="draft_path" label="剪映草稿地址">
|
||||
<n-input v-model:value="formValue.draft_path" @keydown.enter.prevent />
|
||||
</n-form-item>
|
||||
<n-form-item path="project_path" label="项目地址(帧,图片等输出的地址)">
|
||||
<div>
|
||||
<n-input style="width: 400px;" v-model:value="formValue.project_path" @keydown.enter.prevent />
|
||||
</div>
|
||||
<n-button color="#e5a84b" @click="SelectProjectFolder" style="margin-left: 10px;">
|
||||
<n-icon :size="20">
|
||||
<folder-open />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item path="project_name" label="项目名称">
|
||||
<n-input v-model:value="formValue.project_name" @keydown.enter.prevent />
|
||||
</n-form-item>
|
||||
<n-form-item path="system_setting" label="系统设置">
|
||||
<n-form-item path="task_number" label="后台并行任务数(量力而行)">
|
||||
<n-input-number v-model:value="formValue.task_number" @keydown.enter.prevent :min="1" :max="10"
|
||||
placeholder="输入后台并行任务数" />
|
||||
</n-form-item>
|
||||
<n-form-item label="系统主题" style="margin-left: 20px;">
|
||||
<n-switch :rail-style="railStyle" v-model:value="formValue.theme" size="medium"
|
||||
checked-value="dark" unchecked-value="light" @update:value="ChangeMode">
|
||||
<template #checked>
|
||||
暗
|
||||
</template>
|
||||
<template #unchecked>
|
||||
亮
|
||||
</template>
|
||||
</n-switch>
|
||||
</n-form-item>
|
||||
<n-form-item label="人物场景选择模式" style="margin-left: 20px;">
|
||||
<n-select size="small" placeholder="请选择" :options="character_select_model_options"
|
||||
v-model:value="formValue.character_select_model" style="width: 120px;" />
|
||||
</n-form-item>
|
||||
</n-form-item>
|
||||
<n-form-item path="gpt_setting" label="GPT设置">
|
||||
<n-form-item path="gpt_business" label="GPT接口服务商">
|
||||
<n-select v-model:value="formValue.gpt_business" :options="gpt_options" placeholder="GPT接口服务商"
|
||||
style="width: 200px;" />
|
||||
</n-form-item>
|
||||
<n-button type="info" @click="openGptBuyUrl">
|
||||
购买
|
||||
</n-button>
|
||||
<n-form-item style="width: 300px; margin-left: 30px;" path="gpt_key" label="GPT Key">
|
||||
<n-input type="password" show-password-on="mousedown" v-model:value="formValue.gpt_key"
|
||||
@keydown.enter.prevent placeholder="GPT Key" />
|
||||
</n-form-item>
|
||||
<n-form-item style="margin-left: 30px;" path="gpt_model" label="GPT模型">
|
||||
<n-select v-model:value="formValue.gpt_model" :options="gpt_model_options" placeholder="GPT模型"
|
||||
style="width: 160px;" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-left: 30px;" path="gpt_count" label="推理上下文行数">
|
||||
<n-input-number v-model:value="formValue.gpt_count" placeholder="GPT模型" style="width: 150px;"
|
||||
:show-button="false" :min="1" :max="100" />
|
||||
</n-form-item>
|
||||
<n-button color="#e18a3b" @click="addGptOption" style="margin-left: 10px;">
|
||||
添加GPT
|
||||
</n-button>
|
||||
<n-button color="#779649" @click="TestGPTConnection" :loading="loading" style="margin-left: 10px;">
|
||||
测试链接
|
||||
</n-button>
|
||||
<n-button color="#ba5b49" @click="AddGptPrompt" style="margin-left: 10px;">
|
||||
添加GPT提示词
|
||||
</n-button>
|
||||
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_setting" label="翻译设置">
|
||||
<n-form-item path="translation_business" label="翻译服务商">
|
||||
<n-select v-model:value="formValue.translation_business" :options="translation_options"
|
||||
@update:value="SwitchTranslate" placeholder="翻译服务商" style="width: 200px;" />
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_app_id" label="APP ID" style="width: 300px; margin-left: 30px;">
|
||||
<n-input type="password" show-password-on="mousedown"
|
||||
v-model:value="formValue.translation_app_id" @keydown.enter.prevent placeholder="APP ID" />
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_secret" label="产品密钥" style="width: 300px; margin-left: 30px;">
|
||||
<n-input type="password" show-password-on="mousedown"
|
||||
v-model:value="formValue.translation_secret" placeholder="请输入产品密钥"></n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="translation_secret" label="自动翻译" style="width: 300px; margin-left: 30px;">
|
||||
<n-checkbox v-model:checked="formValue.translation_auto" placeholder="请输入产品密钥"></n-checkbox>
|
||||
</n-form-item>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button
|
||||
:disabled="formValue.draft_path == null || formValue.draft_path == '' || formValue.project_path == null || formValue.project_path == ''"
|
||||
round type="primary" @click="handleValidateButtonClick">
|
||||
保存
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRaw, onMounted, defineComponent, h } from "vue"
|
||||
import { NForm, NFormItem, NInput, NInputNumber, useDialog, NButton, NSpace, NSpin, useMessage, NSelect, NCheckbox, NSwitch, NIcon } from "naive-ui"
|
||||
import AddGptOption from "./Components/AddGptOption.vue";
|
||||
import { FolderOpenSharp as FolderOpen } from '@vicons/ionicons5'
|
||||
import AddGptPrompts from './Components/AddGptPrompts.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, NInputNumber, NButton, NSpace, NSpin, NSelect, NCheckbox, NSwitch, FolderOpen, NIcon
|
||||
},
|
||||
setup() {
|
||||
let formRef = ref(null);
|
||||
let message = useMessage()
|
||||
let formValue = ref({
|
||||
draft_path: window.config.draft_path,
|
||||
project_path: window.config.project_path,
|
||||
project_name: window.config.project_name,
|
||||
gpt_business: window.config.gpt_business,
|
||||
gpt_key: window.config.gpt_key,
|
||||
gpt_model: window.config.gpt_model,
|
||||
gpt_count: window.config.gpt_count,
|
||||
task_number: window.config.task_number,
|
||||
translation_business: window.config.translation_business,
|
||||
translation_app_id: window.config.translation_app_id,
|
||||
translation_secret: window.config.translation_secret,
|
||||
translation_auto: window.config.translation_auto,
|
||||
theme: window.config.theme,
|
||||
character_select_model: window.config.character_select_model
|
||||
});
|
||||
let show = ref(false)
|
||||
let gpt_options = ref([]);
|
||||
let gpt_model_options = ref([]);
|
||||
let character_select_model_options = ref([])
|
||||
let dialog = useDialog();
|
||||
let loading = ref(false);
|
||||
|
||||
/**
|
||||
* 加载GPT的配置信息
|
||||
*/
|
||||
async function InitGptOptions() {
|
||||
debugger;
|
||||
await window.api.getGptBusinessOption("all", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
gpt_options.value = value.data;
|
||||
})
|
||||
|
||||
await window.api.getGptModelOption("all", (value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
gpt_model_options.value = value.data;
|
||||
})
|
||||
|
||||
await window.mj.GetTagSelectModel(value => {
|
||||
character_select_model_options.value = value.data;
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await InitGptOptions();
|
||||
})
|
||||
|
||||
|
||||
let translation_options = [{
|
||||
label: "百度翻译",
|
||||
value: "https://fanyi-api.baidu.com/api/trans/vip/translate"
|
||||
}, {
|
||||
label: "火山引擎",
|
||||
value: "https://translate.volcengineapi.com?"
|
||||
},
|
||||
{
|
||||
label: "腾讯翻译",
|
||||
value: "https://tmt.tencentcloudapi.com"
|
||||
},
|
||||
{
|
||||
label: "阿里翻译",
|
||||
value: "https://mt.cn-hangzhou.aliyuncs.com"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
// 切换翻译服务商事件
|
||||
function SwitchTranslate(value, option) {
|
||||
// 将之前的数据全部删除
|
||||
formValue.value.translation_app_id = null;
|
||||
formValue.value.translation_secret = null;
|
||||
}
|
||||
|
||||
let ruleObj = (errorMessage) => {
|
||||
return [{
|
||||
required: true,
|
||||
validator(rule, value) {
|
||||
if (value == null || value == "")
|
||||
return new Error(errorMessage);
|
||||
return true;
|
||||
},
|
||||
trigger: ["input", "blur", "change"]
|
||||
}]
|
||||
}
|
||||
|
||||
let rules = {
|
||||
draft_path: ruleObj("必填剪映草稿地址"),
|
||||
project_path: ruleObj("必填项目地址"),
|
||||
project_name: ruleObj("必填项目名称"),
|
||||
task_number: ruleObj("必填后台并行任务数"),
|
||||
gpt_business: ruleObj("必填GPT接口服务商"),
|
||||
gpt_key: ruleObj("必填GPT Key"),
|
||||
gpt_model: ruleObj("必填GPT模型"),
|
||||
gpt_count: ruleObj("必填自动推理上下文行数"),
|
||||
translation_secret: ruleObj("必填产品密钥"),
|
||||
translation_app_id: ruleObj("必填APP ID"),
|
||||
translation_business: ruleObj("必填翻译服务商"),
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存配置信息
|
||||
*/
|
||||
function handleValidateButtonClick(e) {
|
||||
e.preventDefault();
|
||||
formRef.value?.validate((errors) => {
|
||||
if (errors) {
|
||||
message.error("请检查必填字段");
|
||||
return
|
||||
}
|
||||
// 保存文件
|
||||
window.api.ModifySampleSetting(JSON.stringify(toRaw(formValue.value)), (value) => {
|
||||
if (value.code == 1) {
|
||||
window.api.getSettingDafultData((value) => {
|
||||
window.config = value;
|
||||
})
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: value.message })
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: value.message })
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
let railStyle = (focused,
|
||||
checked) => {
|
||||
const style = {};
|
||||
if (checked) {
|
||||
style.background = "#f3a694";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #f3a694";
|
||||
}
|
||||
} else {
|
||||
style.background = "#775039";
|
||||
if (focused) {
|
||||
style.boxShadow = "0 0 0 2px #775039";
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开购买GPT的地址
|
||||
*/
|
||||
async function openGptBuyUrl() {
|
||||
window.api.openGptBuyUrl(toRaw(formValue.value.gpt_business));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
* @param {*} value 主题名称
|
||||
*/
|
||||
async function ChangeMode(value) {
|
||||
debugger
|
||||
console.log(value)
|
||||
const isDarkMode = await window.darkMode.toggle(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加GPT地址或者模型
|
||||
*/
|
||||
async function addGptOption() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 600;
|
||||
let dialogHeight = 450;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
title: "添加GPT地址或者模型",
|
||||
content: () => h(AddGptOption, {}),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px`,
|
||||
maskClosable: false,
|
||||
onClose: async () => {
|
||||
// 刷新界面数据
|
||||
await InitGptOptions();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试当前GPT是不是可以链接成功
|
||||
*/
|
||||
async function TestGPTConnection() {
|
||||
loading.value = true;
|
||||
// 测试链接
|
||||
await window.api.TestGPTConnection(JSON.stringify(formValue.value), (value) => {
|
||||
loading.value = false;
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "GPT链接失败,错误信息: " + value.message })
|
||||
return;
|
||||
}
|
||||
window.api.showGlobalMessageDialog({ code: 1, message: "gpt链接测试成功,可以正常使用!" })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择项目文件夹
|
||||
*/
|
||||
async function SelectProjectFolder() {
|
||||
await window.api.selectFolder({ default_paht: toRaw(formValue.value).project_path }, (value) => {
|
||||
if (value.length > 0) {
|
||||
formValue.value.project_path = value[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加GPT提示词预设
|
||||
*/
|
||||
async function AddGptPrompt() {
|
||||
// 判断当前数据是不是存在
|
||||
// 处理数据。获取当前的所有的数据
|
||||
let dialogWidth = 600;
|
||||
let dialogHeight = window.innerHeight * 0.9;
|
||||
// ImportWordAndSrt
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
title: "添加GPT提示词预设",
|
||||
content: () => h(AddGptPrompts, { height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; height : ${dialogHeight}px; padding-right : 3px `,
|
||||
maskClosable: false,
|
||||
onClose: () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
formRef,
|
||||
ChangeMode,
|
||||
openGptBuyUrl,
|
||||
railStyle,
|
||||
handleValidateButtonClick,
|
||||
rules,
|
||||
SwitchTranslate,
|
||||
translation_options,
|
||||
gpt_model_options,
|
||||
gpt_options,
|
||||
show,
|
||||
formValue,
|
||||
addGptOption,
|
||||
TestGPTConnection,
|
||||
loading,
|
||||
SelectProjectFolder,
|
||||
AddGptPrompt,
|
||||
character_select_model_options,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,520 @@
|
||||
<template>
|
||||
<div style="color: red;">注意:<br>所有的配置都是基于1440 * 1080。会根据分辨率自动计算匹配</div>
|
||||
<n-tabs type="line" animated default-value="GeneralSettings">
|
||||
<n-tab-pane name="GeneralSettings" tab="通用设置">
|
||||
<n-form ref="formRef" :model="generalSetting" :rules="rules" label-placement="left" label-width="auto"
|
||||
require-mark-placement="right-hanging" :size="size">
|
||||
<n-form-item label="视频分辨率" path="video_resolution">
|
||||
<n-input-number :show-button="false" v-model:value="generalSetting.video_resolution_x"
|
||||
placeholder="视频X" />
|
||||
<div style="font-size: 30px; margin: 0 20px;">*</div>
|
||||
<n-input-number :show-button="false" v-model:value="generalSetting.video_resolution_y"
|
||||
placeholder="视频Y" />
|
||||
</n-form-item>
|
||||
|
||||
<n-tooltip trigger="hover" placement="top-start" style="background-color: aliceblue;" :show-arrow="false">
|
||||
<template #trigger>
|
||||
<n-form-item label="图片关键帧" path="keyframe">
|
||||
<n-select style="width: 150px; margin-right: 10px;"
|
||||
v-model:value="generalSetting.keyframe.name" :options="keyframeOptions"></n-select>
|
||||
<div style="margin-left: 10px;">上下:</div>
|
||||
<n-input-number :show-button="false" style="width: 100px;"
|
||||
v-model:value="generalSetting.keyframe.up_down" placeholder="上下偏移量" />
|
||||
<div style="margin-left: 10px;">左右:</div>
|
||||
<n-input-number :show-button="false" style="width: 100px;"
|
||||
v-model:value="generalSetting.keyframe.left_right" placeholder="上下偏移量" />
|
||||
<!-- <n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<div style="display: flex; margin-left: 10px;">
|
||||
<div style="margin-left: 10px; margin: auto;">大小(0-4):</div>
|
||||
<n-input-number :min="0" :max="4" :step="0.1" :show-button="false" style="width: 100px;"
|
||||
v-model:value="generalSetting.keyframe.scale" placeholder="放大缩小倍率" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
注意:放大倍率会先将图片全部铺满然后在进行放大。防止出现黑边
|
||||
</n-tooltip> -->
|
||||
|
||||
</n-form-item>
|
||||
</template>
|
||||
<div style="color: red;">设置的值越大,图片移动的速度越快(若是图片的尺寸不够,也会被放的越大)</div>
|
||||
</n-tooltip>
|
||||
<n-form-item label="帧率" path="frameRate">
|
||||
<n-input-number v-model:value="generalSetting.frameRate" placeholder="输入帧率" />
|
||||
</n-form-item>
|
||||
<n-form-item label="码率" path="bitRate">
|
||||
<n-input-number v-model:value="generalSetting.bitRate" placeholder="输入视频码率" />
|
||||
</n-form-item>
|
||||
<n-form-item label="配音声量大小" path="audioSoundSize">
|
||||
<n-input-number v-model:value="generalSetting.audioSoundSize" placeholder="配音声量大小" />
|
||||
</n-form-item>
|
||||
<n-form-item label="背景音乐声量大小" path="backgroundMusicSoundSize">
|
||||
<n-input-number v-model:value="generalSetting.backgroundMusicSoundSize" placeholder="背景音乐声量大小" />
|
||||
</n-form-item>
|
||||
<div style="display: flex;">
|
||||
<n-button round type="primary" @click="SaveGeneralSetting">
|
||||
保存
|
||||
</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="AssSetting" tab="字幕设置">
|
||||
<div style="color: red;"> 注意: 当前有些字体读取不能准确获取。可以在后面的下面的编辑列表中进行简单的修改</div>
|
||||
<!-- 字体 字号 颜色 透明度 位置 -->
|
||||
<n-form :model="assSetting" style="display: flex; flex-wrap: wrap;">
|
||||
<n-form-item style=" margin-right: 20px;" label="字体" path="fontName">
|
||||
<n-select style="width: 300px;" v-model:value="assSetting.fontName" :options="fontNameOptions"
|
||||
filterable />
|
||||
<n-button secondary color="#8a2be2" @click="GetSystemInstallFontName"> 更新字体列表 </n-button>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="字号(80差不多)">
|
||||
<n-input-number style="width: 200px;" v-model:value="assSetting.fontSize" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="颜色">
|
||||
<n-color-picker style="width: 200px;" v-model:value="assSetting.fontColor" :show-alpha="false" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="透明度(100不透明)">
|
||||
<n-input-number style="width: 200px;" v-model:value="assSetting.transparent" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-right: 20px;" label="位置">
|
||||
<n-input-number style="width: 60px; margin-right: 10px;" v-model:value="assSetting.positionX"
|
||||
placeholder="X" :show-button="false" />
|
||||
<n-input-number style="width: 60px;" v-model:value="assSetting.positionY" placeholder="Y"
|
||||
:show-button="false" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<div>
|
||||
<n-button type="info" @click="SaveAssConfig"> 保存信息 </n-button>
|
||||
</div>
|
||||
|
||||
<n-data-table :columns="assSettingColumns" :data="generalSetting.assConfig" :pagination="false"
|
||||
:bordered="false" />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="WaterMarkSetting" tab="水印设置">
|
||||
<div style="color: red;"> 注意: 当前有些字体读取不能准确获取。可以在后面的下面的编辑列表中进行简单的修改</div>
|
||||
<!-- 字体 字号 颜色 透明度 位置 -->
|
||||
<n-form :model="wmSetting" style="display: flex; flex-wrap: wrap;">
|
||||
<n-form-item style=" margin-right: 20px;" label="水印字体(要换行输入\N)" path="fontName">
|
||||
<n-input style="width: 400px;" v-model:value="wmSetting.showText" />
|
||||
</n-form-item>
|
||||
<n-form-item style=" margin-right: 20px;" label="字体" path="fontName">
|
||||
<n-select style="width: 300px;" v-model:value="wmSetting.fontName" :options="fontNameOptions"
|
||||
filterable />
|
||||
<n-button secondary color="#8a2be2" @click="GetSystemInstallFontName"> 更新字体列表 </n-button>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="字号(30差不多)">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.fontSize" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="颜色">
|
||||
<n-color-picker style="width: 200px;" v-model:value="wmSetting.fontColor" :show-alpha="false" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style=" margin-right: 20px;" label="透明度(100不透明)">
|
||||
<n-input-number style="width: 200px;" v-model:value="wmSetting.transparent" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item style="margin-right: 20px;" label="位置(X,Y)">
|
||||
<n-input-number style="width: 60px; margin-right: 10px;" v-model:value="wmSetting.positionX"
|
||||
placeholder="X" :show-button="false" />
|
||||
<n-input-number style="width: 60px;" v-model:value="wmSetting.positionY" placeholder="Y"
|
||||
:show-button="false" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<div>
|
||||
<n-button type="info" @click="SaveWaterMarkConfig"> 保存信息 </n-button>
|
||||
</div>
|
||||
|
||||
<n-data-table :columns="wmColumns" :data="generalSetting.watermarkConfig" :pagination="false"
|
||||
:bordered="false" />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="backgroundSetting" tab="背景音乐设置">
|
||||
<div style="margin:10px 0;">
|
||||
注意:
|
||||
<div style="color: red;">设置背景乐对应的文件夹,选择对应的分类,可以随机匹配音乐</div>
|
||||
</div>
|
||||
<BackgroundMusic></BackgroundMusic>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, ref, h, onMounted, toRaw } from "vue";
|
||||
import { NForm, NFormItem, NInput, NButton, useMessage, NCard, NTabs, NTabPane, NInputNumber, NSelect, NColorPicker, NDataTable, useDialog, NTooltip } from "naive-ui"
|
||||
import ModifyVideoAssSetting from '../Components/ModifyVideoAssSetting.vue'
|
||||
import BackgroundMusic from './Components/BackgroundMusic.vue'
|
||||
|
||||
/**
|
||||
* 创建字幕设置表格的列表
|
||||
*/
|
||||
const createAssSettingColumns = ({
|
||||
DeleteAssSetting,
|
||||
EditAssSetting,
|
||||
DeleteWaterMarkSetting
|
||||
}, isWater = false) => {
|
||||
let columnData = [];
|
||||
if (isWater) {
|
||||
columnData.push(
|
||||
{
|
||||
title: "显示值",
|
||||
key: "showText"
|
||||
})
|
||||
}
|
||||
let return_data = columnData.concat([
|
||||
{
|
||||
title: "字体名称",
|
||||
key: "fontName"
|
||||
},
|
||||
{
|
||||
title: "字号",
|
||||
key: "fontSize"
|
||||
},
|
||||
{
|
||||
title: "字体颜色",
|
||||
key: "fontColor"
|
||||
},
|
||||
{
|
||||
title: "透明度",
|
||||
key: "transparent"
|
||||
},
|
||||
{
|
||||
title: "字体颜色",
|
||||
key: "fontColor",
|
||||
render(row) {
|
||||
return h(NColorPicker, {
|
||||
disabled: true,
|
||||
value: row.fontColor
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "位置X",
|
||||
key: "positionX",
|
||||
},
|
||||
{
|
||||
title: "位置Y",
|
||||
key: "positionY",
|
||||
},
|
||||
]);
|
||||
if (isWater) {
|
||||
return_data.push({
|
||||
title: "Action",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return [h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
style: "margin-right:5px",
|
||||
tertiary: true,
|
||||
type: 'info',
|
||||
size: "small",
|
||||
onClick: () => EditAssSetting(row, "watermarkConfig")
|
||||
},
|
||||
{ default: () => "编辑" }
|
||||
), h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
type: 'error',
|
||||
size: "small",
|
||||
onClick: () => DeleteWaterMarkSetting(row)
|
||||
},
|
||||
{ default: () => "删除" }
|
||||
)]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return_data.push({
|
||||
title: "Action",
|
||||
key: "actions",
|
||||
render(row) {
|
||||
return [h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
style: "margin-right:5px",
|
||||
tertiary: true,
|
||||
type: 'info',
|
||||
size: "small",
|
||||
onClick: () => EditAssSetting(row, "assConfig")
|
||||
},
|
||||
{ default: () => "编辑" }
|
||||
), h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
type: 'error',
|
||||
size: "small",
|
||||
onClick: () => DeleteAssSetting(row)
|
||||
},
|
||||
{ default: () => "删除" }
|
||||
)];
|
||||
}
|
||||
})
|
||||
}
|
||||
return return_data;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NForm, NFormItem, NInput, useDialog,
|
||||
NInputNumber, NButton, NCard, NTabPane, NTabs,
|
||||
NSelect, NColorPicker, NDataTable, BackgroundMusic, NTooltip
|
||||
},
|
||||
setup() {
|
||||
let modifyRef = ref(null)
|
||||
let dialog = useDialog();
|
||||
let assSetting = ref({
|
||||
fontName: null,
|
||||
fontSize: 0,
|
||||
fontColor: "#3B7114",
|
||||
transparent: 0,
|
||||
positionX: 0,
|
||||
positionY: 0
|
||||
})
|
||||
let wmSetting = ref({
|
||||
showText: null,
|
||||
fontName: null,
|
||||
fontSize: 0,
|
||||
fontColor: "#3B7114",
|
||||
transparent: 0,
|
||||
positionX: 0,
|
||||
positionY: 0
|
||||
})
|
||||
let generalSetting = ref({
|
||||
video_resolution_x: 0,
|
||||
video_resolution_y: 0,
|
||||
keyframe: {
|
||||
name: null,
|
||||
up_down: 0,
|
||||
left_right: 0,
|
||||
scale: 0
|
||||
},
|
||||
frameRate: 0,
|
||||
bitRate: 0,
|
||||
audioSoundSize: 0,
|
||||
backgroundMusicSoundSize: 0,
|
||||
assConfig: [],
|
||||
watermarkConfig: []
|
||||
})
|
||||
let message = useMessage();
|
||||
let fontNameOptions = ref([]);
|
||||
let keyframeOptions = ref([]);
|
||||
|
||||
/**
|
||||
* 获取视频设置的基本信息
|
||||
*/
|
||||
async function GetVideoConfigMessage() {
|
||||
// 获取基础数据
|
||||
await window.api.GetVideoConfigMessage((value) => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
}
|
||||
else if (value.code == 1) {
|
||||
// 基础设置
|
||||
generalSetting.value = value.data;
|
||||
generalSetting.value.keyframe = generalSetting.value.keyframe ? generalSetting.value.keyframe : {}
|
||||
// 字体下拉列表显示值
|
||||
fontNameOptions.value = value.data.font_name_list
|
||||
//
|
||||
} else {
|
||||
message.error("未知错误");
|
||||
}
|
||||
})
|
||||
|
||||
await window.api.GetKeyFrameOptions(value => {
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
keyframeOptions.value = value.data.filter(item => item.value != "KFTypeScale");
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取基础数据
|
||||
await GetVideoConfigMessage();
|
||||
})
|
||||
/**
|
||||
* 保存基本配置
|
||||
*/
|
||||
async function SaveGeneralSetting() {
|
||||
debugger
|
||||
await window.api.SaveGeneralSetting(toRaw(generalSetting.value), (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
}
|
||||
else if (value.code == 1) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "未知错误" });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前系统已经安装了的字体文件
|
||||
*/
|
||||
async function GetSystemInstallFontName() {
|
||||
await window.api.GetSystemInstallFontName(async (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
} else if (value.code == 1) {
|
||||
message.success("保存成功");
|
||||
// 刷新数据
|
||||
await GetVideoConfigMessage();
|
||||
} else {
|
||||
message.error("未知错误")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存字幕设置
|
||||
*/
|
||||
async function SaveAssConfig() {
|
||||
window.api.SaveAssConfig(["assConfig", toRaw(assSetting.value)], async (value) => {
|
||||
debugger
|
||||
console.log(value);
|
||||
if (value.code == 1) {
|
||||
// 刷新数据,将上面的数据清零。
|
||||
await GetVideoConfigMessage();
|
||||
// message.success("保存成功");
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else if (value.code == 0) {
|
||||
// message.error(value.message);
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else {
|
||||
// message.error("未知错误");
|
||||
window.api.showGlobalMessageDialog({ code: 0, message: "未知错误" })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对应行的字幕设置
|
||||
* @param {行数据} row
|
||||
*/
|
||||
async function DeleteAssSetting(row) {
|
||||
await window.api.DeleteVideoConfig(['assConfig', row.id], async (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await GetVideoConfigMessage();
|
||||
return
|
||||
}
|
||||
message.error(value.message);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存水印设置
|
||||
*/
|
||||
async function SaveWaterMarkConfig() {
|
||||
window.api.SaveAssConfig(["watermarkConfig", toRaw(wmSetting.value)], async (value) => {
|
||||
console.log(value);
|
||||
if (value.code == 1) {
|
||||
// 刷新数据,将上面的数据清零。
|
||||
await GetVideoConfigMessage();
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else if (value.code == 0) {
|
||||
window.api.showGlobalMessageDialog(value);
|
||||
} else {
|
||||
window.api.showGlobalMessageDialog({ value: 0, message: "未知错误" })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的行
|
||||
*/
|
||||
async function DeleteWaterMarkSetting(row) {
|
||||
debugger
|
||||
await window.api.DeleteVideoConfig(['watermarkConfig', row.id], async (value) => {
|
||||
debugger;
|
||||
console.log(value)
|
||||
if (value.code == 1) {
|
||||
message.success("删除成功");
|
||||
await GetVideoConfigMessage();
|
||||
return
|
||||
}
|
||||
message.error(value.message);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑数据
|
||||
*/
|
||||
async function EditAssSetting(row, type) {
|
||||
// ModifyVideoAssSetting
|
||||
dialog.create({
|
||||
showIcon: false,
|
||||
content: () => h(ModifyVideoAssSetting, { ref: modifyRef, initData: row, type: type }),
|
||||
style: `width : 700px;`,
|
||||
maskClosable: false,
|
||||
positiveText: "确定",
|
||||
negativeText: "取消",
|
||||
onPositiveClick: async () => {
|
||||
debugger;
|
||||
let ss = modifyRef.value.wmSetting
|
||||
console.log(ss)
|
||||
|
||||
await window.api.SaveAssConfig([type, toRaw(modifyRef.value.wmSetting)], async (value) => {
|
||||
debugger;
|
||||
console.log(value);
|
||||
if (value.code == 0) {
|
||||
message.error(value.message);
|
||||
return;
|
||||
}
|
||||
message.success("修改成功")
|
||||
// 刷新数据,将上面的数据清零。
|
||||
await GetVideoConfigMessage();
|
||||
})
|
||||
},
|
||||
onNegativeClick: async () => {
|
||||
await GetVideoConfigMessage();
|
||||
},
|
||||
onClose: async () => {
|
||||
await GetVideoConfigMessage();
|
||||
},
|
||||
onEsc: async () => {
|
||||
await GetVideoConfigMessage();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
generalSetting,
|
||||
SaveGeneralSetting,
|
||||
assSetting,
|
||||
wmSetting,
|
||||
GetSystemInstallFontName,
|
||||
fontNameOptions,
|
||||
SaveAssConfig,
|
||||
SaveWaterMarkConfig,
|
||||
modifyRef,
|
||||
keyframeOptions,
|
||||
assSettingColumns: createAssSettingColumns({
|
||||
DeleteAssSetting,
|
||||
EditAssSetting,
|
||||
DeleteWaterMarkSetting
|
||||
}),
|
||||
wmColumns: createAssSettingColumns({ DeleteAssSetting, EditAssSetting, DeleteWaterMarkSetting }, true)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
import App from './App.vue'
|
||||
import { Home } from '@vicons/ionicons5';
|
||||
const app = createApp(App);
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
component: () => import('./components/Home/Home.vue'),
|
||||
children: [
|
||||
|
||||
{ path: "/global_setting", name: "global_setting", component: () => import('./components/Setting/Setting.vue') },
|
||||
{ path: "/clip_setting", name: "clip_setting", component: () => import('./components/Setting/ClipSetting.vue') },
|
||||
{ path: "/getframe", name: "getframe", component: () => import('./components/Backstep/GetFrame.vue') },
|
||||
{ path: "/pushBackPrompt", name: "pushBackPrompt", component: () => import('./components/Backstep/PushBackPrompt.vue') },
|
||||
{ path: "/regenerate", name: "regenerate", component: () => import('./components/Backstep/ReGenerate.vue') },
|
||||
{ path: "/align_draft", name: "align_draft", component: () => import('./components/Clip/AlignDraft.vue') },
|
||||
{ path: "/add_draft", name: "add_draft", component: () => import('./components/Clip/AddDraft.vue') },
|
||||
{ path: "/VideoGenerate", name: "VideoGenerate", component: () => import('./components/Backstep/VideoGenerate.vue') },
|
||||
{ path: "/sd_setting", name: "sd_setting", component: () => import('./components/Setting/SDSetting.vue') },
|
||||
{ path: "/copywriting", name: "copywriting", component: () => import('./components/Backstep/CopyWriting.vue') },
|
||||
{ path: "/videogeneratesetting", name: "videogeneratesetting", component: () => import('./components/Setting/VideoGenerateSetting.vue') },
|
||||
{ path: "/ShowMessage", name: "ShowMessage", component: () => import('./components/Home/ShowMessage.vue') },
|
||||
{ path: "/sdoriginal", name: "sdoriginal", component: () => import('./components/Original/MainPage.vue') },
|
||||
{ path: "/mj_setting", name: "mj_setting", component: () => import('./components/Setting/MJSetting.vue') },
|
||||
]
|
||||
},
|
||||
{ path: "/ReDrawImage", component: () => import('./components/Home/ReDrawImageWD.vue'), },
|
||||
]
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
app.use(router);
|
||||
|
||||
app.mount('#app')
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user