LaiTool V3.0.4
This commit is contained in:
+18
-31
@@ -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,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)
|
||||
|
||||
@@ -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',
|
||||
|
||||
|
||||
@@ -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
@@ -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)'
|
||||
|
||||
Reference in New Issue
Block a user