LaiTool V3.0.4

This commit is contained in:
2024-09-04 19:49:20 +08:00
parent ccddd2412a
commit 5dc75f018a
53 changed files with 1632 additions and 312 deletions
+18 -31
View File
@@ -3,7 +3,6 @@ import sharp from 'sharp'
import { CheckFileOrDirExist } from './file'
import fs from 'fs'
import https from 'https'
import Compressor from 'compressorjs';
/**
* 将指定的图片的尺寸修改,返回修改后的图片数据(base64或buffer)
@@ -130,40 +129,28 @@ export function GetImageBase64(url: string): Promise<string> {
/**
* 压缩图片到指定的大小
* @param file 图片的Blob对象
* @param base64 图片的Blob对象
* @param maxSizeInBytes 最大文件大小,单位字节
* @returns 返回一个Promise,解析为压缩后的Blob对象
*/
export function CompressImageToSize(base64: string, maxSizeInBytes: number): Promise<Blob> {
let mimeType = this.getMimeType(base64);
let byteCharacters = atob(base64.split(',')[1]);
let byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
let byteArray = new Uint8Array(byteNumbers);
let imageBlob = new Blob([byteArray], { type: mimeType });
export async function CompressImageToSize(filePath: string, maxSizeInBytes: number): Promise<Buffer> {
let quality = 100; // 初始质量设置
let outputBuffer;
return new Promise((resolve, reject) => {
const compress = (quality: number) => {
new Compressor(imageBlob as Blob, {
quality,
success(result) {
if (result.size <= maxSizeInBytes || quality <= 0.1) {
resolve(result);
} else {
// 递归降低质量
compress(quality - 0.1);
}
},
error(err) {
reject(err);
},
});
};
// 从较高的质量开始
compress(0.9);
});
const image = sharp(filePath);
// 输出图片的大小
const metadata = await image.metadata();
// 迭代压缩过程
while (true) {
outputBuffer = await image.jpeg({ quality }).toBuffer();
if (outputBuffer.length <= maxSizeInBytes || quality === 20) {
break;
}
quality -= 5; // 每次迭代降低质量
}
return outputBuffer;
}
/**
+1 -1
View File
@@ -1,6 +1,6 @@
let apiUrl = [
{
label: 'LAI API',
label: 'LAI API - 香港',
value: 'b44c6f24-59e4-4a71-b2c7-3df0c4e35e65',
gpt_url: 'https://api.laitool.cc/v1/chat/completions',
mj_url: {
@@ -189,9 +189,6 @@ export class BookService extends BaseRealmService {
await CopyFileOrFolder(book.oldVideoPath, oldVideoPath)
}
let ffmpegOptions = new FfmpegOptions();
let res = await ffmpegOptions.FfmpegCompressVideo(oldVideoPath, 800, "2000k")
// 创建对应的文件夹
await CheckFolderExistsOrCreate(bookFolderPath)
await CheckFolderExistsOrCreate(imageFolder)
+3
View File
@@ -233,6 +233,7 @@ export const DEFINE_STRING = {
USE_BOOK_VIDEO_DATA_TO_BOOK_TASK: "USE_BOOK_VIDEO_DATA_TO_BOOK_TASK",
ADD_JIANYING_DRAFT: "ADD_JIANYING_DRAFT",
EXPORT_COPYWRITING: "EXPORT_COPYWRITING",
IMPORT_COPYWRITING: 'IMPORT_COPYWRITING',
MERGE_PROMPT: "MERGE_PROMPT",
RESET_BOOK_DATA: "RESET_BOOK_DATA",
DELETE_BOOK_DATA: "DELETE_BOOK_DATA",
@@ -240,6 +241,8 @@ export const DEFINE_STRING = {
RESET_GPT_REVERSE_DATA: "RESET_GPT_REVERSE_DATA",
REMOVE_MERGE_PROMPT_DATA: "REMOVE_MERGE_PROMPT_DATA",
REMOVE_GENERATE_IMAGE: 'REMOVE_GENERATE_IMAGE',
ADD_NEW_BOOK_TASK: "ADD_NEW_BOOK_TASK",
REPLACE_BOOK_DATA: "REPLACE_BOOK_DATA",
COMPUTE_STORYBOARD: 'COMPUTE_STORYBOARD',
+22
View File
@@ -17,6 +17,16 @@ export enum BookImageCategory {
D3 = 'd3'
}
export enum AddBookTaskCopyData {
AFTER_GPT = 'after_gpt', // 文案
OLD_IMAGE = 'old_image', // 抽帧/视频
GPT_PROMPT = 'gpt_prompt', // 反推/GPT提示词
CHARACTER = 'character', // 角色
IMAGE_STYLE = 'image_style', // 风格
PROMPT = 'prompt', // 生图提示词
IMAGE = 'image', // 生图
}
export enum MJCategroy {
@@ -234,3 +244,15 @@ export enum PromptMergeType {
// D3 合并
D3_MERGE = 'd3_merge'
}
/**
* 小说数据替换类型
*/
export enum BookRepalceDataType {
// 文案
AFTER_GPT = 'after_gpt',
// GPT提示词
GPT_PROMPT = 'gpt_prompt',
// 提示词
PROMPT = 'prompt',
}
+64 -1
View File
@@ -1,5 +1,5 @@
let fspromises = require('fs').promises
import { cloneDeep, get } from 'lodash'
import { cloneDeep, get, values } from 'lodash'
import { define } from './define'
const { v4: uuidv4 } = require('uuid')
import { apiUrl } from './api/apiUrlDefine'
@@ -69,6 +69,61 @@ export const gptDefine = {
id: 'a93b693e-bb3f-406d-9730-cba43a6585e4'
},
superSinglePromptChineseSystemContent: {
prompt_name: '超级无敌单帧-中文版',
prompt_roles: `# Role: 小说转漫画提示词大师
## Profile
*Author*: laolu
*Version*: 0.1
*Language*: 中文
*Description*: 这个角色会将用户输入的小说文本转化为一个生动的画面描写,最后生成对应的SD提示词。
## Features
1. 文本转化为画面描写:创作引人入胜、生动有趣的画面描写,善于创意想象并使用各种形容词,以第三人称视角转化文本为画面描写。
## Rules
1. 一个文本就是一副画面,不跳过任何一个句子,不能编造
2. 【画面描写】删除人物姓名
3. 【画面描写】删除人物对话
4. 【画面描写】每一句都要有人物的外形和动作的描写,场景的具体描写,多使用形容词
## Examples
用户:
在那个梦里,我整整学了七年炒饭。
AI:
一个身材高大的帅气男人站在梦境之门前,手中紧握炒饭的铲子。身后是一排代表七年的日历页,梦境之门的另一边,隐约可见一个厨房的轮廓。
## Workflow
根据文本生成对应的画面描写,直接使用中文数据,不要又过多的说明。
## Initialization
作为角色 <Role>,每一次输出都要严格遵守<Rules>,一步一步思考,按顺序执行<Workflow> ,使用默认 <Language> ,下面是小说文本:`,
prompt_example: [
{
user_content: '上研究生后。发现导师竟然是曾经网恋的前男友。',
assistant_content:
'一位女士走进一间宽敞、光线充足的研究生实验室,她的目光定格在前方工作台前一位正在辛勤工作的男士身上--他是她的新导师;他身着深色衬衫和熨烫整齐的长裤,身材高大,散发着专业精神和魅力; 温暖的环境光柔和地照亮了他们过去网恋时熟悉的轮廓,皱起的眉头和专注的目光透露出学者坚定不移的奉献精神;熙熙攘攘的研究生和精密的设备融为一体,构成了一幅现代学术画卷,她的内心涌动着甜蜜的怀旧和尴尬的现实这两种复杂的情绪'
},
{
user_content: '我欲哭无泪',
assistant_content:
'一位女士坐在昏暗的房间角落,双手紧握,眼神空洞,脸上流露出无法言说的悲伤;她的周围是散乱的家具和昏黄的灯光,营造出一种压抑和孤寂的氛围;她的身体微微前倾,仿佛承受着巨大的心理压力,却无法找到宣泄的出口;整个场景透露出一种深深的无奈和心碎,她的表情和姿态都在无声地诉说着“我欲哭无泪”的绝望。'
},
{
user_content: '被窝里他搂着我的腰亲我,再丢掉我,就把你锁起来。',
assistant_content:
'在昏暗的卧室中,一对情侣亲密地依偎在温暖的被窝里,男子温柔地搂着女子的腰,轻吻她的脸颊,气氛温馨而浪漫;然而,随着话语的转变,男子的动作突然变得愤怒,他用力地推开女子,眼神中闪过一丝威胁;女子的表情由幸福转为惊恐,她紧紧抓住被单,试图保护自己;整个场景充满了紧张和不安,昏暗的灯光和凌乱的床铺加剧了这种氛围,仿佛预示着即将发生的冲突和束缚'
}
],
id: 'a93b693e-bb3f-406d-9730-cba43a6585e7'
},
onlyPromptMJSystemContent: {
prompt_name: '小说提示词-仅出词',
prompt_roles: `# Pico: 小说分镜
@@ -208,6 +263,8 @@ export const gptDefine = {
return this.CustomizeGptPrompt(this.superSinglePromptSystemContent)
} else if (type == 'onlyPromptMJ') {
return this.CustomizeGptPrompt(this.onlyPromptMJSystemContent)
} else if (type == 'superSinglePromptChinese') {
return this.CustomizeGptPrompt(this.superSinglePromptChineseSystemContent)
} else {
return []
}
@@ -233,6 +290,8 @@ export const gptDefine = {
return this.replace(this.cartoonFirstPromptSystemContent, replacements)
case 'superSinglePrompt':
return this.replace(this.superSinglePromptSystemContent, replacements)
case 'superSinglePromptChinese':
return this.replace(this.superSinglePromptChineseSystemContent, replacements)
default:
throw new Error(`不存在的类型 : ${type}`)
}
@@ -307,6 +366,10 @@ export const gptDefine = {
value: 'superSinglePrompt',
label: '超级无敌单帧'
},
{
value: 'superSinglePromptChinese',
label: '超级无敌单帧-中文版'
},
{
value: 'onlyPromptMJ',
label: '仅出词(不出人物场景-MJ)'