LaiTool 3.0.1

This commit is contained in:
lq1405 2024-08-20 10:37:38 +08:00
parent 223678f761
commit 07e8a5a657
28 changed files with 794 additions and 548 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "laitool", "name": "laitool",
"version": "3.0.1-preview.7", "version": "3.0.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "laitool", "name": "laitool",
"version": "3.0.1-preview.7", "version": "3.0.2",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@alicloud/alimt20181012": "^1.2.0", "@alicloud/alimt20181012": "^1.2.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "laitool", "name": "laitool",
"version": "3.0.1-preview.7", "version": "3.0.2",
"description": "An AI tool for image processing, video processing, and other functions.", "description": "An AI tool for image processing, video processing, and other functions.",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "laitool.cn", "author": "laitool.cn",

Binary file not shown.

View File

@ -9,3 +9,31 @@ export function ContainsChineseOrPunctuation(str: string): boolean {
str str
) )
} }
/**
*
* @param fn
* @param retries
* @param delay
* @returns
*/
export async function RetryWithBackoff<T>(fn: () => Promise<T>, retries: number = 5, delay: number = 2000): Promise<T> {
let attempts = 0;
while (attempts < retries) {
try {
return await fn();
} catch (error) {
attempts++;
// 这边记下日志吧
global.logger.error(
fn.name + '_RetryWithBackoff',
`${attempts} 请求失败,开始下一次重试,失败信息如下:` + error.toString()
)
if (attempts >= retries) {
throw new Error(`失败次数超过 ${retries} 错误信息如下: ${error.message}`);
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('所有重试失败'); // 理论上不会到达这里
}

View File

@ -120,7 +120,7 @@ export function BookIpc() {
// 开始执行获取小说文案的方法 // 开始执行获取小说文案的方法
ipcMain.handle( ipcMain.handle(
DEFINE_STRING.BOOK.GET_COPYWRITING, DEFINE_STRING.BOOK.GET_COPYWRITING,
async (event, bookId, bookTaskId, operateBookType) => await subtitleService.GetCopywriting(bookId, bookTaskId, operateBookType) async (event, bookId, bookTaskId, operateBookType, coverData) => await subtitleService.GetCopywriting(bookId, bookTaskId, operateBookType, coverData)
) )
// 获取小说的文案数据,然后保存到对应的文件中 // 获取小说的文案数据,然后保存到对应的文件中

View File

@ -161,13 +161,13 @@ export class MJOriginalImageGenerate {
* 初始化MJ设置 * 初始化MJ设置
*/ */
async InitMjSetting() { async InitMjSetting() {
let mjSetting_res = await ImageSetting.GetDefineConfigJsonByProperty( // 获取MJ配置从数据库中
JSON.stringify(['img_base', 'mj_config', false, null]) let _mjSettingService = await MJSettingService.getInstance()
) let mjSettings = _mjSettingService.GetMJSettingTreeData()
if (mjSetting_res.code == 0 || !mjSetting_res.data) { if (mjSettings.code == 0) {
throw new Error('请先添加MJ配置') throw new Error(mjSettings.message)
} }
let mjSetting = mjSetting_res.data let mjSetting = mjSettings.data
return mjSetting return mjSetting
} }
@ -247,6 +247,7 @@ export class MJOriginalImageGenerate {
let result = [] let result = []
// 浏览器生图模式 // 浏览器生图模式
if (request_model == 'browser_mj') { if (request_model == 'browser_mj') {
throw new Error('该模式不支持获取已经生图完成的数据,并获取图片')
let param = [] let param = []
// 循环数据,直传需要的数据 // 循环数据,直传需要的数据
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
@ -285,18 +286,18 @@ export class MJOriginalImageGenerate {
// 请求 // 请求
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
const element = value[i] const element = value[i]
if (element.mj_message.progress == 100) { // if (element.mj_message.progress == 100) {
continue // continue
} // }
if (element.mj_message.progress.status == 'success') { // if (element.mj_message.progress.status == 'success') {
continue // continue
} // }
let task_res = await this.discordAPI.GetMJAPITaskByID( let task_res = await this.discordAPI.GetMJAPITaskByID(
element.mj_message.message_id, element.mj_message.message_id,
once_get_task, once_get_task,
mjSetting.apiSetting.apiKey mjSetting.apiSetting.apiKey
) ) // 这边价格判断,是不是真的结束了
if (task_res.code == 0) { if (task_res.code == 0) {
task_res['id'] = element.id task_res['id'] = element.id
task_res['mj_api_url'] = mjSetting.apiSetting.mjApiUrl task_res['mj_api_url'] = mjSetting.apiSetting.mjApiUrl
@ -306,7 +307,6 @@ export class MJOriginalImageGenerate {
if (task_res.progress != 100) { if (task_res.progress != 100) {
continue continue
} }
result.push({ result.push({
id: element.id, id: element.id,
image_id: null, image_id: null,
@ -314,6 +314,8 @@ export class MJOriginalImageGenerate {
name: element.name name: element.name
}) })
} }
} else {
throw new Error('未知的生图模式,请检查配置')
} }
let res = [] let res = []

View File

@ -1,470 +1,526 @@
import axios from "axios"; import axios from 'axios'
import path from "path"; import path from 'path'
import { DEFINE_STRING } from "../../define/define_string"; import { DEFINE_STRING } from '../../define/define_string'
import { define } from "../../define/define"; import { define } from '../../define/define'
let fspromises = require("fs").promises; let fspromises = require('fs').promises
import { gptDefine } from "../../define/gptDefine"; import { gptDefine } from '../../define/gptDefine'
import { apiUrl } from "../../define/api/apiUrlDefine"; import { apiUrl } from '../../define/api/apiUrlDefine'
import { successMessage } from "../Public/generalTools"; import { successMessage } from '../Public/generalTools'
import { RetryWithBackoff } from '../../define/Tools/common'
export class GPT { export class GPT {
constructor(global) { constructor(global) {
this.global = global; this.global = global
}
/**
* 输出测试案例
* @param {*} value 传入的值整个数据
*/
async GenerateGptExampleOut(value) {
try {
let data = JSON.parse(value)
let message = gptDefine.CustomizeGptPrompt(data)
let content = await RetryWithBackoff(
async () => {
return await this.FetchGpt(message)
},
5,
2000
)
console.log(content)
return {
code: 1,
data: content
}
} catch (error) {
return {
code: 0,
message: error.toString()
}
} }
}
/**
/** * GPT推理提示词的方法
* 输出测试案例 * @param {*} element 当前推理的句子
* @param {*} value 传入的值整个数据 * @param {*} gpt_count 设置的GPT上下文理解数量
*/ * @param {*} auto_analyze_character 当前的角色数据
async GenerateGptExampleOut(value) { * @returns
try { */
async GPTPromptGenerate(element, gpt_count, auto_analyze_character) {
let data = JSON.parse(value); try {
let message = gptDefine.CustomizeGptPrompt(data); // 获取当前的推理模式
let content = await this.FetchGpt(message); let gpt_auto_inference = this.global.config.gpt_auto_inference
console.log(content); let message = null
return { if (gpt_auto_inference == 'customize') {
code: 1, // 自定义模式
data: content // 获取当前自定义的推理提示词
} let customize_gpt_prompt = (
await gptDefine.getGptDataByTypeAndProperty('dynamic', 'customize_gpt_prompt', [])
} catch (error) { ).data
return { let index = customize_gpt_prompt.findIndex(
code: 0, (item) => item.id == this.global.config.customize_gpt_prompt
message: error.toString() )
} 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',
* GPT推理提示词的方法 content: element.after_gpt
* @param {*} element 当前推理的句子 })
* @param {*} gpt_count 设置的GPT上下文理解数量 } else {
* @param {*} auto_analyze_character 当前的角色数据 // 内置模式
* @returns // 获取
*/ let prefix_word = ''
async GPTPromptGenerate(element, gpt_count, auto_analyze_character) { // 拼接一个word
try { let i = element.no - 1
// 获取当前的推理模式 if (i <= gpt_count) {
let gpt_auto_inference = this.global.config.gpt_auto_inference; prefix_word = this.all_data
let message = null; .filter((item, index) => index < i)
if (gpt_auto_inference == "customize") { .map((item) => item.after_gpt)
// 自定义模式 .join('\r\n')
// 获取当前自定义的推理提示词 } else if (i > gpt_count) {
let customize_gpt_prompt = (await gptDefine.getGptDataByTypeAndProperty("dynamic", "customize_gpt_prompt", [])).data; prefix_word = this.all_data
let index = customize_gpt_prompt.findIndex(item => item.id == this.global.config.customize_gpt_prompt); .filter((item, index) => i - index <= gpt_count && i - index > 0)
if (this.global.config.customize_gpt_prompt && index < 0) { .map((item) => item.after_gpt)
throw new Error("自定义推理默认要选择对应的自定义推理词"); .join('\r\n')
}
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;
} }
}
/** let suffix_word = ''
* 将推理提示词添加到任务 let o_i = this.all_data.length - i
*/ if (o_i <= gpt_count) {
async GPTPrompt(data) { suffix_word = this.all_data
try { .filter((item, index) => index > i)
console.log(data) .map((item) => item.after_gpt)
let value = JSON.parse(data[0]); .join('\r\n')
let show_global_message = data[1]; } else if (o_i > gpt_count) {
this.all_data = JSON.parse(data[2]); suffix_word = this.all_data
// 获取data中的after_gpt然后使用换行符拼接成一个字符串 .filter((item, index) => index - i <= gpt_count && index - i > 0)
// let word = value.map(item => item.after_gpt).join('\r\n'); .map((item) => item.after_gpt)
let batch = DEFINE_STRING.QUEUE_BATCH.SD_ORIGINAL_GPT_PROMPT; .join('\r\n')
}
// 获取人物角色数据 let word = `${prefix_word}\r\n${element.after_gpt}\r\n${suffix_word}`
let config_json = JSON.parse(await fspromises.readFile(path.join(this.global.config.project_path, "scripts/config.json"), 'utf-8')); let single_word = element.after_gpt
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 (['superSinglePrompt', 'onlyPromptMJ'].includes(this.global.config.gpt_auto_inference)) {
if (content) { // 有返回案例的
content = content.replace(/\)\s*\(/g, ", ").replace(/^\(/, "").replace(/\)$/, "") message = gptDefine.GetExamplePromptMessage(this.global.config.gpt_auto_inference)
} // 加当前提问的
// 获取对应的数据,将数据返回前端事件 message.push({
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.GPT_GENERATE_PROMPT_RETURN, { role: 'user',
id: element.id, content: single_word
gpt_prompt: content })
}) } else {
// 直接返回,没有案例的
this.global.fileQueue.enqueue(async () => { message = [
// 将推理出来的数据写入执行的文件中 {
let json_config = JSON.parse(await fspromises.readFile(element.prompt_json, 'utf-8')); role: 'system',
// 写入 content: gptDefine.getSystemContentByType(this.global.config.gpt_auto_inference, {
json_config.gpt_prompt = content; textContent: word,
await fspromises.writeFile(element.prompt_json, JSON.stringify(json_config)); characterContent: auto_analyze_character
}) })
} catch (error) { },
throw error; {
} role: 'user',
}, `${batch}_${element.id}`, batch); 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 RetryWithBackoff(
async () => {
return await this.FetchGpt(message)
},
5,
2000
)
return res
} catch (error) {
throw error
}
}
this.global.requestQuene.setBatchCompletionCallback(batch, (failedTasks) => { /**
if (failedTasks.length > 0) { * 将推理提示词添加到任务
let message = ` */
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 }) => { failedTasks.forEach(({ taskId, error }) => {
message += `${taskId}-, \n 错误信息: ${error}` + '\n'; message += `${taskId}-, \n 错误信息: ${error}` + '\n'
}); })
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, { this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
code: 0, code: 0,
message: message 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 { } else {
if (show_global_message) {
content = res.data.choices[0].message.content; this.global.newWindow[0].win.webContents.send(DEFINE_STRING.SHOW_MESSAGE_DIALOG, {
code: 1,
message: '所有推理任务完成'
})
}
} }
return content; })
return {
code: 1
}
} catch (error) {
return {
code: 0,
message: error.toString()
}
} }
}
/** /**
* 发送GPT请求 * 修改请求的参数
* @param {*} message 请求的信息 * @param {*} data
* @param {*} gpt_url gpt的url默认在global中取 * @returns
* @param {*} gpt_key gpt的key默认在global中取 */
* @param {*} gpt_model gpt的model默认在global中取 ModifyData(gpt_url, data) {
* @returns let res = data
*/ if (gpt_url.includes('dashscope.aliyuncs.com')) {
async FetchGpt(message, res = {
gpt_url = this.global.config.gpt_business, model: data.model,
gpt_key = this.global.config.gpt_key, input: {
gpt_model = this.global.config.gpt_model) { messages: data.messages
try { },
// 还有自定义的 parameters: {
let all_options = (await this.GetGPTBusinessOption("all", (value) => value.gpt_url)).data; result_format: 'message'
// 判断gpt_business 是不是一个http开头的
if (!gpt_url.includes("http")) {
// 获取对应Id的gpt_url
let index = all_options.findIndex(item => item.value == gpt_url && item.gpt_url);
if (index < 0) {
throw new Error("获取GPT的服务商配置失败");
}
gpt_url = all_options[index].gpt_url;
}
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;
} }
}
} }
return res
}
/** /**
* 自动分析文本返回人物场景角色 * 获取返回的内容
* @param {要分析的文本} value * @param {*} gpt_url GPT请求的内容
* @returns * @param {*} res 请求返回的数据
*/ * @returns
async AutoAnalyzeCharacter(value) { */
try { GetResponseContent(gpt_url, res) {
let message = [ let content = ''
{ if (gpt_url.includes('dashscope.aliyuncs.com')) {
"role": "system", content = res.data.output.choices[0].message.content
"content": gptDefine.getSystemContentByType("character", { textContent: value }) } else {
}, content = res.data.choices[0].message.content
{ }
"role": "user", return content
"content": gptDefine.getUserContentByType("character", {}) }
}
]
let content = await this.FetchGpt(message);
return { /**
code: 1, * 发送GPT请求
data: content * @param {*} message 请求的信息
} * @param {*} gpt_url gpt的url默认在global中取
} catch (error) { * @param {*} gpt_key gpt的key默认在global中取
return { * @param {*} gpt_model gpt的model默认在global中取
code: 0, * @returns
message: error.toString() */
} 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 all_options = (await this.GetGPTBusinessOption('all', (value) => value.gpt_url)).data
// 判断gpt_business 是不是一个http开头的
if (!gpt_url.includes('http')) {
// 获取对应Id的gpt_url
let index = all_options.findIndex((item) => item.value == gpt_url && item.gpt_url)
if (index < 0) {
throw new Error('获取GPT的服务商配置失败')
} }
gpt_url = all_options[index].gpt_url
}
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
} }
}
/**
/** * 自动分析文本返回人物场景角色
* 获取GPT的服务商配置默认的和自定义的 * @param {要分析的文本} value
* @returns * @returns
*/ */
async GetGPTBusinessOption(value, callback = null) { async AutoAnalyzeCharacter(value) {
let res = await gptDefine.getGptDataByTypeAndProperty(value, "gpt_options", []); try {
if (res.code == 0) { let message = [
return res; {
} else { role: 'system',
if (callback) { content: gptDefine.getSystemContentByType('character', { textContent: value })
callback(res.data) },
} {
return successMessage(res.data) role: 'user',
content: gptDefine.getUserContentByType('character', {})
} }
} ]
let content = await RetryWithBackoff(
async () => {
return await this.FetchGpt(message)
},
5,
2000
)
/** return {
* 获取GPT的模型配置默认的和自定义的 code: 1,
* @returns data: content
*/ }
async GetGPTModelOption(value) { } catch (error) {
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_model_options", []); return {
code: 0,
message: error.toString()
}
} }
}
/** /**
* 获取GPT的自动推理模式配置默认的和自定义的 * 获取GPT的服务商配置默认的和自定义的
* @returns * @returns
*/ */
async GetGptAutoInferenceOptions(value) { async GetGPTBusinessOption(value, callback = null) {
return await gptDefine.getGptDataByTypeAndProperty(value, "gpt_auto_inference", []); let res = await gptDefine.getGptDataByTypeAndProperty(value, 'gpt_options', [])
if (res.code == 0) {
return res
} else {
if (callback) {
callback(res.data)
}
return successMessage(res.data)
} }
}
/** /**
* 获取GPT的自动推理模式配置默认的和自定义的 * 获取GPT的模型配置默认的和自定义的
* @returns * @returns
*/ */
async GetCustomizeGptPrompt(value) { async GetGPTModelOption(value) {
return await gptDefine.getGptDataByTypeAndProperty(value, "customize_gpt_prompt", []); 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()
}
} }
}
/** /**
* 保存自定义的GPT服务商配置 * 删除指定Id的自定义GPT服务商配置
* @param {*} value 配置信息 0 : 传入的数据 1: 属性名称 * @param {*} value id 0 : 删除的数据 1: 属性名称
* @returns * @returns
*/ */
async SaveDynamicGPTOption(value) { async DeleteDynamicGPTOption(value) {
try { try {
let res = await gptDefine.saveDynamicGPTOption(value); let res = await gptDefine.deleteDynamicGPTOption(value)
return { return {
code: 1, code: 1,
} data: res
} catch (error) { }
return { } catch (error) {
code: 0, return {
message: error.toString() 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 RetryWithBackoff(
async () => {
return await this.FetchGpt(message, value.gpt_business, value.gpt_key, value.gpt_model)
},
5,
2000
)
return {
code: 1
}
} catch (error) {
return {
code: 0,
message: error.toString()
}
} }
}
/** /**
* 删除指定Id的自定义GPT服务商配置 * 单句洗稿
* @param {*} value id 0 : 删除的数据 1: 属性名称 * @param {文案参数} value
* @returns */
*/ async AIModifyOneWord(value) {
async DeleteDynamicGPTOption(value) { try {
try { let message = [
let res = await gptDefine.deleteDynamicGPTOption(value); {
return { role: 'system',
code: 1, content:
data: res 'You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.'
} },
} catch (error) { {
return { role: 'user',
code: 0, content: `请您扮演一个抖音网文改写专家,我会给你一句文案,请你不要改变文案的结构,不改变原来的意思,仅对文案进行同义转换改写,不要有奇怪的写法,说法通俗一点,不要其他的标点符号,每一小句话之间都是以句号连接,参考抖音网文解说,以下是文案:${value[1]}`
message: error.toString()
}
} }
]
let content = await RetryWithBackoff(
async () => {
return await this.FetchGpt(message)
},
5,
2000
)
return {
code: 1,
data: { no: value[0], content: content }
}
} 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()
}
}
}
}

View File

@ -14,6 +14,7 @@ import { isEmpty } from 'lodash'
import { ValidateJson } from '../../define/Tools/validate' import { ValidateJson } from '../../define/Tools/validate'
import { GetOpenAISuccessResponse } from '../../define/response/openAIResponse' import { GetOpenAISuccessResponse } from '../../define/response/openAIResponse'
import { successMessage } from './generalTools' import { successMessage } from './generalTools'
import { RetryWithBackoff } from '../../define/Tools/common'
let { Signer } = require('@volcengine/openapi') let { Signer } = require('@volcengine/openapi')
@ -267,69 +268,83 @@ export class Translate {
messages: [] messages: []
} }
// 开始调用GPT进行翻译 // 开始调用GPT进行翻译
if (from == 'en' && to == "zh") { if (from == 'en' && to == 'zh') {
data.messages.push({ data.messages.push({
"role": "system", role: 'system',
"content": '我想让你充当英译中专家用中文100%还原描述,不要加其他的联想,只翻译字面意思,请检查所有信息是否准确,并在回答时保持简活,不需要任何其他反馈。' content:
'我想让你充当英译中专家用中文100%还原描述,不要加其他的联想,只翻译字面意思,请检查所有信息是否准确,并在回答时保持简活,不需要任何其他反馈。'
}) })
// 添加示例 // 添加示例
data.messages.push({ data.messages.push({
"role": "user", role: 'user',
"content": 'A woman in her twenties with messy hair and a look of shock and despair. She is wearing a simple white shirt and jeans. Her face shows mixed emotions. The background is a dim and quiet room. The historical background is modern. The screen content is a close-up of the womans face with tear marks.' content:
'A woman in her twenties with messy hair and a look of shock and despair. She is wearing a simple white shirt and jeans. Her face shows mixed emotions. The background is a dim and quiet room. The historical background is modern. The screen content is a close-up of the womans face with tear marks.'
}) })
data.messages.push({ data.messages.push({
"role": "assistant", role: 'assistant',
"content": '一位二十多岁的女人,头发凌乱,表情震惊和绝望。她穿着一件简单的白色衬衫和牛仔裤。她的脸上显示出复杂的情感。背景是一个昏暗安静的房间。历史背景是现代的。屏幕内容是女人脸部的特写,带有泪痕。' content:
'一位二十多岁的女人,头发凌乱,表情震惊和绝望。她穿着一件简单的白色衬衫和牛仔裤。她的脸上显示出复杂的情感。背景是一个昏暗安静的房间。历史背景是现代的。屏幕内容是女人脸部的特写,带有泪痕。'
}) })
data.messages.push({ data.messages.push({
"role": "user", role: 'user',
"content": 'A twenty-something woman with short curly hair, smiling, wearing a casual white shirt and jeans, sitting on a comfortable sofa with a cup of coffee in her hand. She is in a small and cozy living room with a few books on the bookshelf, modern interior design, and natural light pouring into the room in the late afternoon. Screen content: Part of the sofa cushions.' content:
'A twenty-something woman with short curly hair, smiling, wearing a casual white shirt and jeans, sitting on a comfortable sofa with a cup of coffee in her hand. She is in a small and cozy living room with a few books on the bookshelf, modern interior design, and natural light pouring into the room in the late afternoon. Screen content: Part of the sofa cushions.'
}) })
data.messages.push({ data.messages.push({
"role": "assistant", role: 'assistant',
"content": '一位二十多岁的女性,留着短卷发,面带微笑,穿着休闲的白色衬衫和牛仔裤,手拿一杯咖啡,坐在一个舒适的沙发上。她所在的是一个小而温馨的客厅,书架上有几本书,现代室内设计,下午晚些时候,自然光线洒进房间。屏幕内容:沙发垫的一部分。' content:
'一位二十多岁的女性,留着短卷发,面带微笑,穿着休闲的白色衬衫和牛仔裤,手拿一杯咖啡,坐在一个舒适的沙发上。她所在的是一个小而温馨的客厅,书架上有几本书,现代室内设计,下午晚些时候,自然光线洒进房间。屏幕内容:沙发垫的一部分。'
}) })
data.messages.push({ data.messages.push({
"role": "user", role: 'user',
"content": 'In a modern city, a streamlined car is parked on the street. A man in his thirties, with short brown hair combed back, a calm, confident look, tall and thin in a clean white shirt and black pants, sits in the car. The interior of the car is clean and modern, and the background is blurred to highlight the man\'s calm demeanor. The man\'s cell phone is ringing. The scene is set in the present.' content:
"In a modern city, a streamlined car is parked on the street. A man in his thirties, with short brown hair combed back, a calm, confident look, tall and thin in a clean white shirt and black pants, sits in the car. The interior of the car is clean and modern, and the background is blurred to highlight the man's calm demeanor. The man's cell phone is ringing. The scene is set in the present."
}) })
data.messages.push({ data.messages.push({
"role": "assistant", role: 'assistant',
"content": '在现代城市,一辆流线型轿车停在街上。一个三十多岁的男人,短短的棕色头发梳向后,神情冷静,自信,穿着干净的白衬衫和黑裤子,身材高挑瘦长,坐在车里。车内干净而现代,背景模糊,以突出男人平静的神态。男人的手机正在响。场景设定在现在。' content:
'在现代城市,一辆流线型轿车停在街上。一个三十多岁的男人,短短的棕色头发梳向后,神情冷静,自信,穿着干净的白衬衫和黑裤子,身材高挑瘦长,坐在车里。车内干净而现代,背景模糊,以突出男人平静的神态。男人的手机正在响。场景设定在现在。'
}) })
} else if (from == 'zh' && to == "en") { } else if (from == 'zh' && to == 'en') {
data.messages.push({ data.messages.push({
"role": "system", role: 'system',
"content": '我想让你充当中译英专家用中文100%还原描述,不要加其他的联想,只翻译字面意思,请检查所有信息是否准确,并在回答时保持简活,不需要任何其他反馈。' content:
'我想让你充当中译英专家用中文100%还原描述,不要加其他的联想,只翻译字面意思,请检查所有信息是否准确,并在回答时保持简活,不需要任何其他反馈。'
}) })
// 添加示例 // 添加示例
data.messages.push({ data.messages.push({
"role": "user", role: 'user',
"content": '一位二十多岁的女人,头发凌乱,表情震惊和绝望。她穿着一件简单的白色衬衫和牛仔裤。她的脸上显示出复杂的情感。背景是一个昏暗安静的房间。历史背景是现代的。屏幕内容是女人脸部的特写,带有泪痕。' content:
'一位二十多岁的女人,头发凌乱,表情震惊和绝望。她穿着一件简单的白色衬衫和牛仔裤。她的脸上显示出复杂的情感。背景是一个昏暗安静的房间。历史背景是现代的。屏幕内容是女人脸部的特写,带有泪痕。'
}) })
data.messages.push({ data.messages.push({
"role": "assistant", role: 'assistant',
"content": 'A woman in her twenties with messy hair and a look of shock and despair. She is wearing a simple white shirt and jeans. Her face shows mixed emotions. The background is a dim and quiet room. The historical background is modern. The screen content is a close-up of the womans face with tear marks.' content:
'A woman in her twenties with messy hair and a look of shock and despair. She is wearing a simple white shirt and jeans. Her face shows mixed emotions. The background is a dim and quiet room. The historical background is modern. The screen content is a close-up of the womans face with tear marks.'
}) })
data.messages.push({ data.messages.push({
"role": "user", role: 'user',
"content": '一位二十多岁的女性,留着短卷发,面带微笑,穿着休闲的白色衬衫和牛仔裤,手拿一杯咖啡,坐在一个舒适的沙发上。她所在的是一个小而温馨的客厅,书架上有几本书,现代室内设计,下午晚些时候,自然光线洒进房间。屏幕内容:沙发垫的一部分。' content:
'一位二十多岁的女性,留着短卷发,面带微笑,穿着休闲的白色衬衫和牛仔裤,手拿一杯咖啡,坐在一个舒适的沙发上。她所在的是一个小而温馨的客厅,书架上有几本书,现代室内设计,下午晚些时候,自然光线洒进房间。屏幕内容:沙发垫的一部分。'
}) })
data.messages.push({ data.messages.push({
"role": "assistant", role: 'assistant',
"content": 'A twenty-something woman with short curly hair, smiling, wearing a casual white shirt and jeans, sitting on a comfortable sofa with a cup of coffee in her hand. She is in a small and cozy living room with a few books on the bookshelf, modern interior design, and natural light pouring into the room in the late afternoon. Screen content: Part of the sofa cushions.' content:
'A twenty-something woman with short curly hair, smiling, wearing a casual white shirt and jeans, sitting on a comfortable sofa with a cup of coffee in her hand. She is in a small and cozy living room with a few books on the bookshelf, modern interior design, and natural light pouring into the room in the late afternoon. Screen content: Part of the sofa cushions.'
}) })
data.messages.push({ data.messages.push({
"role": "user", role: 'user',
"content": '在现代城市,一辆流线型轿车停在街上。一个三十多岁的男人,短短的棕色头发梳向后,神情冷静,自信,穿着干净的白衬衫和黑裤子,身材高挑瘦长,坐在车里。车内干净而现代,背景模糊,以突出男人平静的神态。男人的手机正在响。场景设定在现在。' content:
'在现代城市,一辆流线型轿车停在街上。一个三十多岁的男人,短短的棕色头发梳向后,神情冷静,自信,穿着干净的白衬衫和黑裤子,身材高挑瘦长,坐在车里。车内干净而现代,背景模糊,以突出男人平静的神态。男人的手机正在响。场景设定在现在。'
}) })
data.messages.push({ data.messages.push({
"role": "assistant", role: 'assistant',
"content": 'In a modern city, a streamlined car is parked on the street. A man in his thirties, with short brown hair combed back, a calm, confident look, tall and thin in a clean white shirt and black pants, sits in the car. The interior of the car is clean and modern, and the background is blurred to highlight the man\'s calm demeanor. The man\'s cell phone is ringing. The scene is set in the present.' content:
"In a modern city, a streamlined car is parked on the street. A man in his thirties, with short brown hair combed back, a calm, confident look, tall and thin in a clean white shirt and black pants, sits in the car. The interior of the car is clean and modern, and the background is blurred to highlight the man's calm demeanor. The man's cell phone is ringing. The scene is set in the present."
}) })
} else { } else {
throw new Error("GPT翻译只支持中英互译") throw new Error('GPT翻译只支持中英互译')
} }
data.messages.push({ data.messages.push({
role: 'user', role: 'user',
@ -346,7 +361,13 @@ export class Translate {
}, },
data: JSON.stringify(data) data: JSON.stringify(data)
} }
let res = await axios.request(config) let res = await RetryWithBackoff(
async () => {
return await axios.request(config)
},
5,
2000
)
// 将返回的数据进行拼接数据处理 // 将返回的数据进行拼接数据处理
let res_data = [] let res_data = []
@ -416,7 +437,6 @@ export class Translate {
if (arr_data.length <= 0) { if (arr_data.length <= 0) {
return return
} }
let req_data = {} let req_data = {}
for (let j = 0; j < arr_data.length; j++) { for (let j = 0; j < arr_data.length; j++) {
const element = arr_data[j] const element = arr_data[j]

View File

@ -243,7 +243,7 @@ export class BookTask {
startTime: element.startTime, startTime: element.startTime,
endTime: element.endTime, endTime: element.endTime,
timeLimit: element.timeLimit, timeLimit: element.timeLimit,
subValue: element.subValue, subValue: element.subValue && element.subValue.length > 0 ? JSON.stringify(element.subValue) : undefined,
characterTags: element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [], characterTags: element.characterTags && element.characterTags.length > 0 ? cloneDeep(element.characterTags) : [],
gptPrompt: element.gptPrompt, gptPrompt: element.gptPrompt,
outImagePath: path.relative(define.project_path, outImagePath), outImagePath: path.relative(define.project_path, outImagePath),

View File

@ -1,6 +1,7 @@
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { gptDefine } from "../../../define/gptDefine"; import { gptDefine } from "../../../define/gptDefine";
import axios from "axios"; import axios from "axios";
import { RetryWithBackoff } from "../../../define/Tools/common";
/** /**
* GPT相关的服务都在这边 * GPT相关的服务都在这边
@ -168,7 +169,10 @@ export class GptService {
let baseSubUrl = baseUrl ? (baseUrl.endsWith('/') ? baseUrl + 'v1/chat/completions' : baseUrl + '/v1/chat/completions') : null; let baseSubUrl = baseUrl ? (baseUrl.endsWith('/') ? baseUrl + 'v1/chat/completions' : baseUrl + '/v1/chat/completions') : null;
let url = baseSubUrl ? baseSubUrl : "https://api.laitool.cc/v1/chat/completions" let url = baseSubUrl ? baseSubUrl : "https://api.laitool.cc/v1/chat/completions"
// 开始请求这个默认是使用的是LAI API的gpt-4o-mini // 开始请求这个默认是使用的是LAI API的gpt-4o-mini
let content = await this.FetchGpt(message, 'gpt-4o-mini', apiKey, url) let content = await RetryWithBackoff<string>(async () => {
return await this.FetchGpt(message, 'gpt-4o-mini', apiKey, url);
}, 5, 2000)
return content return content
} catch (error) { } catch (error) {
throw error throw error

View File

@ -22,6 +22,7 @@ import { BookTaskStatus, OperateBookType } from '../../../define/enum/bookEnum'
import axios from 'axios' import axios from 'axios'
import { GptService } from '../GPT/gpt' import { GptService } from '../GPT/gpt'
import FormData from 'form-data' import FormData from 'form-data'
import { RetryWithBackoff } from '../../../define/Tools/common'
const util = require('util') const util = require('util')
const { exec } = require('child_process') const { exec } = require('child_process')
const execAsync = util.promisify(exec) const execAsync = util.promisify(exec)
@ -596,17 +597,22 @@ export class Subtitle {
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': subtitleSetting.laiWhisper.apiKey, 'Authorization': subtitleSetting.laiWhisper.apiKey,
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data',
...formdata.getHeaders() // 在Node.js环境中需要添加这一行 ...formdata.getHeaders() // 在Node.js环境中需要添加这一行
}, },
data: formdata data: formdata
}; };
let res = await axios(config) // laiwhisper 要做重试机制
let res = await RetryWithBackoff(async () => {
return await axios(config)
}, 5, 2000)
let text = res.data.text; let text = res.data.text;
// 但是这边是繁体,需要转化为简体 // 但是这边是繁体,需要转化为简体
let simpleText = await this.gptService.ChineseTraditionalToSimplified(text, subtitleSetting.laiWhisper.apiKey, url); // 请求也要做重试
let simpleText = await RetryWithBackoff(async () => {
return await this.gptService.ChineseTraditionalToSimplified(text, subtitleSetting.laiWhisper.apiKey, url);
}, 5, 2000);
console.log(res.data) console.log(res.data)
return simpleText; return simpleText;

View File

@ -11,9 +11,7 @@ import fs from 'fs'
import { CheckFileOrDirExist } from "../../../define/Tools/file"; import { CheckFileOrDirExist } from "../../../define/Tools/file";
import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic"; import { BookServiceBasic } from "../ServiceBasic/bookServiceBasic";
import { Subtitle } from "./subtitle"; import { Subtitle } from "./subtitle";
import { LoggerStatus, ResponseMessageType } from "../../../define/enum/softwareEnum";
import { TaskScheduler } from "../taskScheduler"; import { TaskScheduler } from "../taskScheduler";
import { OperationType } from "realm/dist/public-types/internal";
import { OperateBookType } from "../../../define/enum/bookEnum"; import { OperateBookType } from "../../../define/enum/bookEnum";
import { Book } from "../../../model/book"; import { Book } from "../../../model/book";
@ -126,9 +124,14 @@ export class SubtitleService {
//#region 语音转文案或者是字幕识别 //#region 语音转文案或者是字幕识别
/** /**
* *
* @param bookId ID
* @param bookTaskId ID
* @param operateBookType
* @param coverData
* @returns
*/ */
async GetCopywriting(bookId: string, bookTaskId: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> { async GetCopywriting(bookId: string, bookTaskId: string, operateBookType: OperateBookType, coverData: boolean): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
try { try {
let subtitleSettingRes = await this.GetSubtitleSetting(); let subtitleSettingRes = await this.GetSubtitleSetting();
if (subtitleSettingRes.code == 0) { if (subtitleSettingRes.code == 0) {
@ -152,8 +155,13 @@ export class SubtitleService {
} else { } else {
throw new Error("未知的操作类型") throw new Error("未知的操作类型")
} }
if (!coverData) { // 不覆盖数据,将已经有的数据过滤掉
bookTaskDetails = bookTaskDetails.filter(item => isEmpty(item.afterGpt) && isEmpty(item.word))
}
if (bookTaskDetails.length <= 0) { if (bookTaskDetails.length <= 0) {
throw new Error("分镜信息不存在"); throw new Error("分镜信息不存在 / 已经有文案,无需提取");
} }
let { book, bookTask } = await this.bookServiceBasic.GetBookAndTask(bookId, tempBookTaskId) let { book, bookTask } = await this.bookServiceBasic.GetBookAndTask(bookId, tempBookTaskId)

View File

@ -16,6 +16,7 @@ import { SoftwareService } from "../../../define/db/service/SoftWare/softwareSer
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { ValidateJson } from "../../../define/Tools/validate"; import { ValidateJson } from "../../../define/Tools/validate";
import { GetOpenAISuccessResponse } from "../../../define/response/openAIResponse"; import { GetOpenAISuccessResponse } from "../../../define/response/openAIResponse";
import { RetryWithBackoff } from "../../../define/Tools/common";
let { let {
Signer Signer
@ -202,7 +203,10 @@ export class Translate {
}, },
data: JSON.stringify(data) data: JSON.stringify(data)
}; };
let res = await axios.request(config); let res = await RetryWithBackoff(async () => {
return await axios.request(config);
}, 5, 2000)
// let res = await axios.request(config);
// 将返回的数据进行拼接数据处理 // 将返回的数据进行拼接数据处理
let res_data = []; let res_data = [];

View File

@ -72,12 +72,13 @@ const book = {
//#region 文案相关信息 //#region 文案相关信息
// 获取文案信息 // 获取文案信息
GetCopywriting: async (bookId, bookTaskId, operateBookType) => GetCopywriting: async (bookId, bookTaskId, operateBookType, coverData) =>
await ipcRenderer.invoke( await ipcRenderer.invoke(
DEFINE_STRING.BOOK.GET_COPYWRITING, DEFINE_STRING.BOOK.GET_COPYWRITING,
bookId, bookId,
bookTaskId, bookTaskId,
operateBookType operateBookType,
coverData
), ),
// 将文案信息导出,方便修改 // 将文案信息导出,方便修改

View File

@ -115,10 +115,10 @@
<script> <script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue' import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NButton, NForm, NFormItem, NInput, NSelect, NIcon } from 'naive-ui' import { useMessage, NButton, NForm, NFormItem, NInput, NSelect, NIcon } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts' import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts'
import { CloseSharp } from '@vicons/ionicons5' import { CloseSharp } from '@vicons/ionicons5'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../../stores/software.ts'
export default defineComponent({ export default defineComponent({
components: { components: {

View File

@ -30,9 +30,9 @@
<script setup> <script setup>
import { ref, h, toRaw } from 'vue' import { ref, h, toRaw } from 'vue'
import { useMessage, useDialog, NButton } from 'naive-ui' import { useMessage, useDialog, NButton } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts' import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../../stores/software.ts'
import AddBook from '../Components/AddBook.vue' import AddBook from './AddBook.vue'
let props = defineProps({ let props = defineProps({
book: undefined book: undefined
}) })

View File

@ -36,9 +36,9 @@
<script> <script>
import { ref, onMounted, defineComponent, h } from 'vue' import { ref, onMounted, defineComponent, h } from 'vue'
import { useMessage, NButton, useDialog } from 'naive-ui' import { useMessage, NButton, useDialog } from 'naive-ui'
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts' import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../../stores/software.ts'
import { BookType, OperateBookType } from '../../../../../define/enum/bookEnum' import { BookType, OperateBookType } from '../../../../../../define/enum/bookEnum.ts'
import ManageBookTaskGenerateInformation from './ManageBookTaskGenerateInformation.vue' import ManageBookTaskGenerateInformation from './ManageBookTaskGenerateInformation.vue'
export default defineComponent({ export default defineComponent({

View File

@ -41,10 +41,10 @@
import { ref, onMounted, defineComponent, onUnmounted, h, toRaw, computed } from 'vue' import { ref, onMounted, defineComponent, onUnmounted, h, toRaw, computed } from 'vue'
import { useMessage, NButton, NIcon, useDialog, NPopover, NSwitch, NDropdown } from 'naive-ui' import { useMessage, NButton, NIcon, useDialog, NPopover, NSwitch, NDropdown } from 'naive-ui'
import { AddSharp, ReloadSharp, ResizeSharp, SwapHorizontalSharp } from '@vicons/ionicons5' import { AddSharp, ReloadSharp, ResizeSharp, SwapHorizontalSharp } from '@vicons/ionicons5'
import AddBook from '../Components/AddBook.vue' import AddBook from './AddBook.vue'
import { useSoftwareStore } from '../../../../../stores/software' import { useSoftwareStore } from '../../../../../../stores/software'
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts' import { useReverseManageStore } from '../../../../../../stores/reverseManage'
import { BookType } from '../../../../../define/enum/bookEnum' import { BookType } from '../../../../../../define/enum/bookEnum'
export default defineComponent({ export default defineComponent({
components: { components: {

View File

@ -14,9 +14,9 @@
<script> <script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, nextTick, watchEffect } from 'vue' import { ref, onMounted, defineComponent, onUnmounted, toRaw, nextTick, watchEffect } from 'vue'
import { useMessage, NLog } from 'naive-ui' import { useMessage, NLog } from 'naive-ui'
import Artplayer from '../../Components/Artplayer.vue' import Artplayer from '../../../Components/Artplayer.vue'
import { useReverseManageStore } from '../../../../../stores/reverseManage.ts' import { useReverseManageStore } from '../../../../../../stores/reverseManage.ts'
import { DEFINE_STRING } from '../../../../../define/define_string' import { DEFINE_STRING } from '../../../../../../define/define_string.ts'
export default defineComponent({ export default defineComponent({
components: { Artplayer, NLog }, components: { Artplayer, NLog },

View File

@ -66,7 +66,7 @@
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue' import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NForm, NFormItem, NButton, NIcon, NInput, NSelect } from 'naive-ui' import { useMessage, NForm, NFormItem, NButton, NIcon, NInput, NSelect } from 'naive-ui'
import { FolderOpen } from '@vicons/ionicons5' import { FolderOpen } from '@vicons/ionicons5'
import { OperateBookType } from '../../../../../define/enum/bookEnum' import { OperateBookType } from '../../../../../../define/enum/bookEnum'
export default defineComponent({ export default defineComponent({
components: { NForm, NFormItem, NButton, NIcon, NInput, NSelect, FolderOpen }, components: { NForm, NFormItem, NButton, NIcon, NInput, NSelect, FolderOpen },

View File

@ -15,9 +15,9 @@
<n-button <n-button
:color="softwareStore.SoftColor.ORANGE" :color="softwareStore.SoftColor.ORANGE"
style="margin-left: 5px" style="margin-left: 5px"
@click="GetCopywriting" @click="GetCopywriting('all')"
> >
开始提取文案 开始提取全部文案
</n-button> </n-button>
</n-dropdown> </n-dropdown>
@ -244,7 +244,7 @@ export default defineComponent({
} }
// //
async function GetCopywriting() { async function GetCopywriting(type) {
if (isEmpty(reverseManageStore.selectBookTask.id)) { if (isEmpty(reverseManageStore.selectBookTask.id)) {
window.api.showGlobalMessageDialog({ window.api.showGlobalMessageDialog({
code: 0, code: 0,
@ -261,7 +261,9 @@ export default defineComponent({
let da = dialog.warning({ let da = dialog.warning({
title: '开始提取文案提示', title: '开始提取文案提示',
content: `即将进行文案提取,当前的文案提取模式为 ${subtitleSettingRes.data.selectModel} ,是否继续?`, content: `即将进行 ${type == 'blank' ? '空白' : '全部'} 文案提取${
type == 'blank' ? '' : ',会重新提取并覆盖旧的文案数据'
}当前的文案提取模式为 ${subtitleSettingRes.data.selectModel} 是否继续`,
positiveText: '继续', positiveText: '继续',
negativeText: '取消', negativeText: '取消',
onPositiveClick: async () => { onPositiveClick: async () => {
@ -271,7 +273,8 @@ export default defineComponent({
let copywriting_res = await window.book.GetCopywriting( let copywriting_res = await window.book.GetCopywriting(
reverseManageStore.selectBook.id, reverseManageStore.selectBook.id,
reverseManageStore.selectBookTask.id, reverseManageStore.selectBookTask.id,
OperateBookType.BOOKTASK OperateBookType.BOOKTASK,
type == 'blank' ? false : true
) )
softwareStore.spin.spinning = false softwareStore.spin.spinning = false
if (copywriting_res.code == 0) { if (copywriting_res.code == 0) {
@ -382,6 +385,9 @@ export default defineComponent({
case 'framing': // case 'framing': //
await Framing() await Framing()
break break
case 'recognizing_blank': //
await GetCopywriting('blank')
break
case 'recognizing_setting': // case 'recognizing_setting': //
await GetCopywritingSetting() await GetCopywritingSetting()
break break
@ -561,30 +567,41 @@ export default defineComponent({
* @param to 目标语言 * @param to 目标语言
*/ */
async function TranslateService(from, to) { async function TranslateService(from, to) {
debugger
let translateData = [] let translateData = []
for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) { for (let i = 0; i < reverseManageStore.selectBookTaskDetail.length; i++) {
const element = reverseManageStore.selectBookTaskDetail[i] const element = reverseManageStore.selectBookTaskDetail[i]
let reversePrompt = element.reversePrompt if (!isEmpty(element.gptPrompt)) {
if (reversePrompt) { translateData.push({
reversePrompt.forEach((item) => { text: element.gptPrompt,
if (from == 'zh' && to == 'en') { from: from,
// to: to,
if (!ContainsChineseOrPunctuation(item.promptCN ? item.promptCN : item.prompt)) { type: TranslateType.GPT_PROMPT_TRANSLATE,
return isSplit: false, // true
} bookTaskDetailId: element.id
}
let temp_obj = {
text: item.prompt,
from: from,
to: to,
type: TranslateType.REVERSE_PROMPT_TRANSLATE,
isSplit: false, // true
bookTaskDetailId: element.id,
reversePromptId: item.id
}
translateData.push(temp_obj)
}) })
} else {
let reversePrompt = element.reversePrompt
if (reversePrompt) {
reversePrompt.forEach((item) => {
if (from == 'zh' && to == 'en') {
//
if (!ContainsChineseOrPunctuation(item.promptCN ? item.promptCN : item.prompt)) {
return
}
}
let temp_obj = {
text: item.promptCN ? item.promptCN : item.prompt,
from: from,
to: to,
type: TranslateType.REVERSE_PROMPT_TRANSLATE,
isSplit: false, // true
bookTaskDetailId: element.id,
reversePromptId: item.id
}
translateData.push(temp_obj)
})
}
} }
} }
if (translateData.length == 0) { if (translateData.length == 0) {
@ -704,6 +721,10 @@ export default defineComponent({
{ label: '分镜', key: 'framing' } { label: '分镜', key: 'framing' }
], ],
copywritingOptions: [ copywritingOptions: [
{
label: '提取空白文案',
key: 'recognizing_blank'
},
{ {
label: '提取文案位置', label: '提取文案位置',
key: 'recognizing_setting' key: 'recognizing_setting'

View File

@ -71,7 +71,8 @@ async function GetVideoSubtitle() {
let res = await window.book.GetCopywriting( let res = await window.book.GetCopywriting(
reverseManageStore.selectBook.id, reverseManageStore.selectBook.id,
data.value.id, data.value.id,
OperateBookType.BOOKTASKDETAIL OperateBookType.BOOKTASKDETAIL,
true
) )
softwareStore.spin.spinning = false softwareStore.spin.spinning = false
if (res.code == 0) { if (res.code == 0) {

View File

@ -21,11 +21,11 @@
<script> <script>
import { ref, onMounted, defineComponent, onUnmounted, toRaw, reactive, computed, h } from 'vue' import { ref, onMounted, defineComponent, onUnmounted, toRaw, reactive, computed, h } from 'vue'
import { useMessage, NButton, NDataTable, NTag, NEllipsis } from 'naive-ui' import { useMessage, NButton, NDataTable, NTag, NEllipsis } from 'naive-ui'
import ManageBookButton from './Components/ManageBookButton.vue' import ManageBookButton from './Components/ManageBook/ManageBookButton.vue'
import { useSoftwareStore } from '../../../../stores/software' import { useSoftwareStore } from '../../../../stores/software'
import { useReverseManageStore } from '../../../../stores/reverseManage.ts' import { useReverseManageStore } from '../../../../stores/reverseManage.ts'
import { BookType } from '../../../../define/enum/bookEnum' import { BookType } from '../../../../define/enum/bookEnum'
import BookListAction from './Components/BookListAction.vue' import BookListAction from './Components/ManageBook/BookListAction.vue'
export default defineComponent({ export default defineComponent({
components: { components: {

View File

@ -18,7 +18,7 @@ import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
import { useMessage, NDivider } from 'naive-ui' import { useMessage, NDivider } from 'naive-ui'
import ManageBookDetailButton from './Components/ManageBookDetailButton.vue' import ManageBookDetailButton from './Components/ManageBookDetailButton.vue'
import ManageBookReverseTable from './MJReverse/ManageBookReverseTable.vue' import ManageBookReverseTable from './MJReverse/ManageBookReverseTable.vue'
import ManageBookShowLogger from './Components/ManageBookShowLogger.vue' import ManageBookShowLogger from './Components/ManageBook/ManageBookShowLogger.vue'
import { useSoftwareStore } from '../../../../stores/software' import { useSoftwareStore } from '../../../../stores/software'
import { DEFINE_STRING } from '../../../../define/define_string' import { DEFINE_STRING } from '../../../../define/define_string'
import { DialogType } from '../../../../define/enum/bookEnum' import { DialogType } from '../../../../define/enum/bookEnum'

View File

@ -36,7 +36,7 @@ import { useMessage, useDialog, NButton, NDataTable, NIcon, NWatermark } from 'n
import { useReverseManageStore } from '../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../stores/software' import { useSoftwareStore } from '../../../../stores/software'
import { AddSharp } from '@vicons/ionicons5' import { AddSharp } from '@vicons/ionicons5'
import BookTaskListAction from './Components/BookTaskListAction.vue' import BookTaskListAction from './Components/ManageBook/BookTaskListAction.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { OperateBookType } from '../../../../define/enum/bookEnum' import { OperateBookType } from '../../../../define/enum/bookEnum'
import { DEFINE_STRING } from '../../../../define/define_string' import { DEFINE_STRING } from '../../../../define/define_string'

View File

@ -88,7 +88,7 @@
<n-button <n-button
text text
:type="data.imageLock ? 'error' : 'primary'" :type="data.imageLock ? 'error' : 'primary'"
style="font-size: 24px; position: absolute; left: -12px; top: -0" style="font-size: 24px; position: absolute; left: -12px; top: -5px"
@click="ModifyLock" @click="ModifyLock"
> >
<n-icon> <n-icon>
@ -96,6 +96,16 @@
<LockOpen v-else /> <LockOpen v-else />
</n-icon> </n-icon>
</n-button> </n-button>
<n-button
text
type="info"
style="font-size: 24px; position: absolute; left: 8px; top: -5px"
@click="DownloadImage"
>
<n-icon>
<Download />
</n-icon>
</n-button>
</div> </div>
<div> <div>
<n-divider vertical style="height: 100%" /> <n-divider vertical style="height: 100%" />
@ -128,6 +138,7 @@ import { ref, h, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'v
import { import {
NImage, NImage,
useMessage, useMessage,
useDialog,
NDivider, NDivider,
NImageGroup, NImageGroup,
NButton, NButton,
@ -138,8 +149,9 @@ import {
} from 'naive-ui' } from 'naive-ui'
import { usePromptStore } from '../../../../../stores/prompt' import { usePromptStore } from '../../../../../stores/prompt'
import { useImageStore } from '../../../../../stores/image' import { useImageStore } from '../../../../../stores/image'
import { LockClosed, LockOpen } from '@vicons/ionicons5' import { LockClosed, LockOpen, Download } from '@vicons/ionicons5'
import { useReverseManageStore } from '../../../../../stores/reverseManage' import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { isEmpty } from 'lodash'
export default defineComponent({ export default defineComponent({
components: { components: {
@ -152,13 +164,15 @@ export default defineComponent({
NTag, NTag,
NIcon, NIcon,
LockClosed, LockClosed,
LockOpen LockOpen,
Download
}, },
props: ['initData', 'type', 'image_generate_category', 'func', 'index'], props: ['initData', 'type', 'image_generate_category', 'func', 'index'],
setup(props) { setup(props) {
let imageStore = useImageStore() let imageStore = useImageStore()
let promptStore = usePromptStore() let promptStore = usePromptStore()
let message = useMessage() let message = useMessage()
let dialog = useDialog()
let data = ref(props.initData) let data = ref(props.initData)
let type = ref(props.type) let type = ref(props.type)
let images = ref(props.initData.subImagePath) let images = ref(props.initData.subImagePath)
@ -313,7 +327,16 @@ export default defineComponent({
* MJ采集图片 * MJ采集图片
*/ */
async function MJGetImage() { async function MJGetImage() {
props.func.mJGetImage() dialog.warning({
title: 'MJ采集图片警告',
content:
'只有API出图才能采集其他模式不支持且API出图图片链接一般只有二十四个小时有效过期失败是否继续',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
await props.func.mJGetImage()
}
})
} }
/** /**
@ -351,12 +374,70 @@ export default defineComponent({
} }
} }
/**
* 下载对应的分镜图片
*/
async function DownloadImage() {
// mj_message
dialog.warning({
title: '下载图片警告',
content: '单个图片的下载,只支持 MJ API 生图,当前有图片的话会被覆盖掉,是否继续?',
positiveText: '继续',
negativeText: '取消',
onPositiveClick: async () => {
if (data.value.mj_message) {
debugger
//
// success error
if (data.value.mj_message.status == 'error') {
message.error('失败状态不能采集图片')
return
}
if (isEmpty(data.value.mj_message.message_id)) {
message.error('没有消息ID不能采集图片')
return
}
window.mj.GetGeneratedMJImageAndSplit(JSON.stringify([toRaw(data.value)]), (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.outImagePath =
'file://' +
element.outImagePath.replaceAll('\\', '/') +
'?time=' +
new Date().getTime()
data.value.subImagePath = element.subImagePath.map(
(item) => 'file://' + item.replaceAll('\\', '/') + '?time=' + new Date().getTime()
)
data.value.mj_message.image_click = element.result
data.value.mj_message.image_path = element.image_path
data.value.mj_message.progress = 100
data.value.mj_message.status = 'success'
}
})
} else if (data.value.mjMessage) {
message.error('暂时不支持')
return
} else {
message.error('没有生图数据')
}
}
})
}
return { return {
data, data,
type, type,
image_options, image_options,
sd_batch_size, sd_batch_size,
ModifyLock, ModifyLock,
DownloadImage,
width, width,
images, images,
space_image, space_image,

View File

@ -933,12 +933,26 @@ export default defineComponent({
if (!item.mj_message) { if (!item.mj_message) {
continue continue
} }
if (item.mj_message.status && item.mj_message.status == 'error') { // success error
if (item.mj_message.status == 'error' || item.mj_message.status == 'success') {
continue continue
} }
// message_id
if (item.mj_message.message_id && !isEmpty(item.mj_message.message_id)) { if (item.mj_message.message_id && !isEmpty(item.mj_message.message_id)) {
pa.push(item) pa.push(item)
} }
// //
// if (
// !(isEmpty(item.outImagePath) || (!item.subImagePath && item.subImagePath.length <= 0))
// ) {
// continue
// }
// if (item.mj_message.message_id && !isEmpty(item.mj_message.message_id)) {
// pa.push(item)
// }
} }
if (pa.length > 0) { if (pa.length > 0) {