V 3.0.1 LaiTool V3.0.1-preview.3
This commit is contained in:
parent
f3a25e9474
commit
36178fbe5d
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "laitool",
|
"name": "laitool",
|
||||||
"version": "3.0.1-preview.2",
|
"version": "3.0.1-preview.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "laitool",
|
"name": "laitool",
|
||||||
"version": "3.0.1-preview.2",
|
"version": "3.0.1-preview.3",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/alimt20181012": "^1.2.0",
|
"@alicloud/alimt20181012": "^1.2.0",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "laitool",
|
"name": "laitool",
|
||||||
"version": "3.0.1-preview.2",
|
"version": "3.0.1-preview.3",
|
||||||
"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",
|
||||||
|
|||||||
BIN
resources/image/c_s/7f403066-7722-4423-853e-17bac07c5565.png
Normal file
BIN
resources/image/c_s/7f403066-7722-4423-853e-17bac07c5565.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 623 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 638 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -171,13 +171,13 @@ export function CompressImageToSize(base64: string, maxSizeInBytes: number): Pro
|
|||||||
* 将选中的区域涂白,其他区域涂黑(这个颜色可以变)
|
* 将选中的区域涂白,其他区域涂黑(这个颜色可以变)
|
||||||
* @param inputPath 输入的文件路径
|
* @param inputPath 输入的文件路径
|
||||||
* @param outputPath 输出的文件路径
|
* @param outputPath 输出的文件路径
|
||||||
* @param region 范围对象,包含x, y, width, height属性
|
* @param regions 范围对象,包含x, y, width, height属性
|
||||||
* @param markColor 标记颜色,默认为黑色 { r: 255, g: 255, b: 255 }
|
* @param markColor 标记颜色,默认为白色 { r: 255, g: 255, b: 255 }
|
||||||
* @param backColor 背景颜色,默认为白色 { r: 0, g: 0, b: 0 }
|
* @param backColor 背景颜色,默认为黑色 { r: 0, g: 0, b: 0 }
|
||||||
*/
|
*/
|
||||||
export async function ProcessImage(inputPath: string,
|
export async function ProcessImage(inputPath: string,
|
||||||
outputPath: string,
|
outputPath: string,
|
||||||
region: { width: any; height: any; x: any; y: any },
|
regions: { width: any; height: any; x: any; y: any, imageWidth?: any, imageHeight?: any }[],
|
||||||
markColor: { r: number; g: number; b: number } = { r: 255, g: 255, b: 255 },
|
markColor: { r: number; g: number; b: number } = { r: 255, g: 255, b: 255 },
|
||||||
backColor: { r: number; g: number; b: number } = { r: 0, g: 0, b: 0 },
|
backColor: { r: number; g: number; b: number } = { r: 0, g: 0, b: 0 },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -188,7 +188,7 @@ export async function ProcessImage(inputPath: string,
|
|||||||
// 获取图片的元数据
|
// 获取图片的元数据
|
||||||
const { width, height } = await image.metadata();
|
const { width, height } = await image.metadata();
|
||||||
|
|
||||||
// 创建一个新的空白图片,背景为白色
|
// 创建一个新的黑色图片,背景为白色
|
||||||
const whiteBackground = await sharp({
|
const whiteBackground = await sharp({
|
||||||
create: {
|
create: {
|
||||||
width,
|
width,
|
||||||
@ -198,27 +198,42 @@ export async function ProcessImage(inputPath: string,
|
|||||||
}
|
}
|
||||||
}).png().toBuffer();
|
}).png().toBuffer();
|
||||||
|
|
||||||
// 创建一个黑色的矩形
|
// 创建多个白色的矩形,并进行合成
|
||||||
const blackRegion = await sharp({
|
const composites = await Promise.all(regions.map(async (region) => {
|
||||||
|
|
||||||
|
let rateW = undefined;
|
||||||
|
let rateY = undefined;
|
||||||
|
let rate = undefined;
|
||||||
|
if (region.imageWidth != null && region.imageHeight != null) {
|
||||||
|
rateY = height / region.imageHeight;
|
||||||
|
rateW = width / region.imageWidth;
|
||||||
|
rate = rateY;
|
||||||
|
}
|
||||||
|
if (rate == null) {
|
||||||
|
rate = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const regionBuffer = await sharp({
|
||||||
create: {
|
create: {
|
||||||
width: region.width,
|
width: Math.ceil(region.width * rate),
|
||||||
height: region.height,
|
height: Math.ceil(region.height * rate),
|
||||||
channels: 3, // RGB channels
|
channels: 3, // RGB channels
|
||||||
background: markColor // Black color
|
background: markColor // 标记颜色
|
||||||
}
|
}
|
||||||
}).png().toBuffer();
|
}).png().toBuffer();
|
||||||
|
|
||||||
// 在白色背景上叠加黑色矩形
|
return {
|
||||||
await sharp(whiteBackground)
|
input: regionBuffer,
|
||||||
.composite([
|
left: Math.ceil(region.x * rate),
|
||||||
{
|
top: Math.ceil(region.y * rate),
|
||||||
input: blackRegion,
|
};
|
||||||
left: region.x,
|
|
||||||
top: Math.floor(region.y)
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.toFile(outputPath);
|
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 在背景上叠加所有的矩形区域
|
||||||
|
await sharp(whiteBackground)
|
||||||
|
.composite(composites)
|
||||||
|
.toFile(outputPath);
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
@ -2,17 +2,17 @@ let apiUrl = [
|
|||||||
{
|
{
|
||||||
label: 'LAI API',
|
label: 'LAI API',
|
||||||
value: 'b44c6f24-59e4-4a71-b2c7-3df0c4e35e65',
|
value: 'b44c6f24-59e4-4a71-b2c7-3df0c4e35e65',
|
||||||
gpt_url: 'https://laitool.net/v1/chat/completions',
|
gpt_url: 'https://api.laitool.cc/v1/chat/completions',
|
||||||
mj_url: {
|
mj_url: {
|
||||||
imagine: 'https://laitool.net/mj/submit/imagine',
|
imagine: 'https://api.laitool.cc/mj/submit/imagine',
|
||||||
describe: 'https://laitool.net/mj/submit/describe',
|
describe: 'https://api.laitool.cc/mj/submit/describe',
|
||||||
update_file: 'https://laitool.net/mj/submit/upload-discord-images',
|
update_file: 'https://api.laitool.cc/mj/submit/upload-discord-images',
|
||||||
once_get_task: 'https://laitool.net/mj/task/${id}/fetch'
|
once_get_task: 'https://api.laitool.cc/mj/task/${id}/fetch'
|
||||||
},
|
},
|
||||||
d3_url: {
|
d3_url: {
|
||||||
image: 'https://laitool.net/v1/images/generations'
|
image: 'https://api.laitool.cc/v1/images/generations'
|
||||||
},
|
},
|
||||||
buy_url: 'https://laitool.net/register?aff=Zmdu'
|
buy_url: 'https://api.laitool.cc/register?aff=Zmdu'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'openai-hk',
|
label: 'openai-hk',
|
||||||
|
|||||||
@ -24,7 +24,8 @@ export class BookModel extends Realm.Object<BookModel> {
|
|||||||
videoConfig: string | null // 合成视频设置
|
videoConfig: string | null // 合成视频设置
|
||||||
prefixPrompt: string | null // 前缀
|
prefixPrompt: string | null // 前缀
|
||||||
suffixPrompt: string | null // 后缀
|
suffixPrompt: string | null // 后缀
|
||||||
subtitlePosition: string | null
|
subtitlePosition: string | null // 字幕位置
|
||||||
|
watermarkPosition: string | null // 水印位置,一个json数组字符串
|
||||||
|
|
||||||
static schema: Realm.ObjectSchema = {
|
static schema: Realm.ObjectSchema = {
|
||||||
name: 'Book',
|
name: 'Book',
|
||||||
@ -50,7 +51,8 @@ export class BookModel extends Realm.Object<BookModel> {
|
|||||||
videoConfig: "string?",
|
videoConfig: "string?",
|
||||||
prefixPrompt: "string?",
|
prefixPrompt: "string?",
|
||||||
suffixPrompt: "string?",
|
suffixPrompt: "string?",
|
||||||
subtitlePosition: 'string?'
|
subtitlePosition: 'string?',
|
||||||
|
watermarkPosition: "string?"
|
||||||
},
|
},
|
||||||
// 主键为_id
|
// 主键为_id
|
||||||
primaryKey: 'id'
|
primaryKey: 'id'
|
||||||
|
|||||||
@ -155,6 +155,13 @@ const migration = (oldRealm: Realm, newRealm: Realm) => {
|
|||||||
newBookTask[i].subValue = '[]'
|
newBookTask[i].subValue = '[]'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (oldRealm.schemaVersion < 22) {
|
||||||
|
const oldBookTask = oldRealm.objects('Book')
|
||||||
|
const newBookTask = newRealm.objects('Book')
|
||||||
|
for (let i = 0; i < oldBookTask.length; i++) {
|
||||||
|
newBookTask[i].watermarkPosition = '[]'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BaseRealmService extends BaseService {
|
export class BaseRealmService extends BaseService {
|
||||||
@ -196,7 +203,7 @@ export class BaseRealmService extends BaseService {
|
|||||||
BookTaskDetailModel
|
BookTaskDetailModel
|
||||||
],
|
],
|
||||||
path: this.dbpath,
|
path: this.dbpath,
|
||||||
schemaVersion: 21,
|
schemaVersion: 22,
|
||||||
migration: migration
|
migration: migration
|
||||||
}
|
}
|
||||||
this.realm = await Realm.open(config)
|
this.realm = await Realm.open(config)
|
||||||
|
|||||||
@ -277,7 +277,7 @@ export class BookService extends BaseRealmService {
|
|||||||
* @param bookId 小说的ID
|
* @param bookId 小说的ID
|
||||||
* @param bookData 要修改的小说数据
|
* @param bookData 要修改的小说数据
|
||||||
*/
|
*/
|
||||||
async UpdateBookData(bookId: string, bookData) {
|
async UpdateBookData(bookId: string, bookData: Book.SelectBook) {
|
||||||
try {
|
try {
|
||||||
if (bookId == null) {
|
if (bookId == null) {
|
||||||
throw new Error('修改小说数据失败,缺少小说ID')
|
throw new Error('修改小说数据失败,缺少小说ID')
|
||||||
|
|||||||
@ -202,7 +202,8 @@ export const DEFINE_STRING = {
|
|||||||
BASE64_TO_FILE: 'BASE64_TO_FILE',
|
BASE64_TO_FILE: 'BASE64_TO_FILE',
|
||||||
PROCESS_IMAGE: 'PROCESS_IMAGE',
|
PROCESS_IMAGE: 'PROCESS_IMAGE',
|
||||||
BATCH_PROCESS_IMAGE: 'BATCH_PROCESS_IMAGE',
|
BATCH_PROCESS_IMAGE: 'BATCH_PROCESS_IMAGE',
|
||||||
BATCH_PROCESS_IMAGE_RESULT: 'BATCH_PROCESS_IMAGE_RESULT'
|
BATCH_PROCESS_IMAGE_RESULT: 'BATCH_PROCESS_IMAGE_RESULT',
|
||||||
|
PROCESS_IMAGE_WATERMASK_CHECK: "PROCESS_IMAGE_WATERMASK_CHECK"
|
||||||
},
|
},
|
||||||
BOOK: {
|
BOOK: {
|
||||||
MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回
|
MAIN_DATA_RETURN: 'MAIN_DATA_RETURN', // 监听任务返回
|
||||||
@ -291,6 +292,7 @@ export const DEFINE_STRING = {
|
|||||||
},
|
},
|
||||||
DB: {
|
DB: {
|
||||||
UPDATE_BOOK_TASK_DATA: "UPDATE_BOOK_TASK_DATA",
|
UPDATE_BOOK_TASK_DATA: "UPDATE_BOOK_TASK_DATA",
|
||||||
UPDATE_BOOK_TASK_DETAIL_DATA: "UPDATE_BOOK_TASK_DETAIL_DATA"
|
UPDATE_BOOK_TASK_DETAIL_DATA: "UPDATE_BOOK_TASK_DETAIL_DATA",
|
||||||
|
UPDATE_BOOK_DATA: "UPDATE_BOOK_DATA"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,21 +64,30 @@ export class TagDefine {
|
|||||||
if (res.hasOwnProperty('character_tags')) {
|
if (res.hasOwnProperty('character_tags')) {
|
||||||
res.character_tags.forEach((item) => {
|
res.character_tags.forEach((item) => {
|
||||||
if (item.show_image && item.show_image != '') {
|
if (item.show_image && item.show_image != '') {
|
||||||
item.show_image = path.join(define.image_path, item.show_image)
|
item.show_image = path.join(
|
||||||
|
define.image_path,
|
||||||
|
item.show_image + '?t=' + new Date().getTime()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (res.hasOwnProperty('scene_tags')) {
|
if (res.hasOwnProperty('scene_tags')) {
|
||||||
res.scene_tags.forEach((item) => {
|
res.scene_tags.forEach((item) => {
|
||||||
if (item.show_image && item.show_image != '') {
|
if (item.show_image && item.show_image != '') {
|
||||||
item.show_image = path.join(define.image_path, item.show_image)
|
item.show_image = path.join(
|
||||||
|
define.image_path,
|
||||||
|
item.show_image + '?t=' + new Date().getTime()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (res.hasOwnProperty('style_tags')) {
|
if (res.hasOwnProperty('style_tags')) {
|
||||||
res.style_tags.forEach((item) => {
|
res.style_tags.forEach((item) => {
|
||||||
if (item.show_image && item.show_image != '') {
|
if (item.show_image && item.show_image != '') {
|
||||||
item.show_image = path.join(define.image_path, item.show_image)
|
item.show_image = path.join(
|
||||||
|
define.image_path,
|
||||||
|
item.show_image + '?t=' + new Date().getTime()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { BookImage } from '../Service/Book/bookImage'
|
|||||||
import { ImageStyle } from '../Service/Book/imageStyle'
|
import { ImageStyle } from '../Service/Book/imageStyle'
|
||||||
import { BookTask } from '../Service/Book/bookTask'
|
import { BookTask } from '../Service/Book/bookTask'
|
||||||
import { BookVideo } from '../Service/Book/bookVideo'
|
import { BookVideo } from '../Service/Book/bookVideo'
|
||||||
|
import { Watermark } from '../Service/watermark'
|
||||||
let reverseBook = new ReverseBook()
|
let reverseBook = new ReverseBook()
|
||||||
let basicReverse = new BasicReverse()
|
let basicReverse = new BasicReverse()
|
||||||
let subtitle = new Subtitle()
|
let subtitle = new Subtitle()
|
||||||
@ -18,6 +19,7 @@ let bookImage = new BookImage()
|
|||||||
let imageStyle = new ImageStyle()
|
let imageStyle = new ImageStyle()
|
||||||
let bookTask = new BookTask()
|
let bookTask = new BookTask()
|
||||||
let bookVideo = new BookVideo()
|
let bookVideo = new BookVideo()
|
||||||
|
let watermark = new Watermark()
|
||||||
|
|
||||||
export function BookIpc() {
|
export function BookIpc() {
|
||||||
// 获取样式图片的子列表
|
// 获取样式图片的子列表
|
||||||
@ -98,13 +100,13 @@ export function BookIpc() {
|
|||||||
// 开始执行分镜任务
|
// 开始执行分镜任务
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
DEFINE_STRING.BOOK.GET_COPYWRITING,
|
DEFINE_STRING.BOOK.GET_COPYWRITING,
|
||||||
async (event, bookId) => await reverseBook.GetCopywriting(bookId)
|
async (event, bookId, bookTaskId) => await reverseBook.GetCopywriting(bookId, bookTaskId)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 执行去除水印
|
// 执行去除水印
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
DEFINE_STRING.BOOK.REMOVE_WATERMARK,
|
DEFINE_STRING.BOOK.REMOVE_WATERMARK,
|
||||||
async (event, bookId) => await reverseBook.RemoveWatermark(bookId)
|
async (event, id,operateBookType) => await watermark.RemoveWatermark(id,operateBookType)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 添加反推任务到任务列表
|
// 添加反推任务到任务列表
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import { errorMessage, successMessage } from '../Public/generalTools'
|
|||||||
import { Book } from '../../model/book'
|
import { Book } from '../../model/book'
|
||||||
import { BookTaskService } from '../../define/db/service/Book/bookTaskService'
|
import { BookTaskService } from '../../define/db/service/Book/bookTaskService'
|
||||||
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'
|
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService'
|
||||||
|
import { BookService } from '../../define/db/service/Book/bookService'
|
||||||
|
|
||||||
|
|
||||||
async function DBIpc() {
|
async function DBIpc() {
|
||||||
let bookTaskService = await BookTaskService.getInstance()
|
let bookTaskService = await BookTaskService.getInstance()
|
||||||
let bookTaskDetailService = await BookTaskDetailService.getInstance()
|
let bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||||
|
let bookService = await BookService.getInstance()
|
||||||
//#region 小说相关的修改
|
//#region 小说相关的修改
|
||||||
|
|
||||||
// 修改小说任务的数据
|
// 修改小说任务的数据
|
||||||
@ -31,6 +33,18 @@ async function DBIpc() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改小说的指定属性的数据
|
||||||
|
*/
|
||||||
|
ipcMain.handle(DEFINE_STRING.DB.UPDATE_BOOK_DATA, async (event, bookId: string, data: Book.SelectBook) => {
|
||||||
|
try {
|
||||||
|
bookService.UpdateBookData(bookId, data)
|
||||||
|
return successMessage(null, "修改小说数据成功", "DBIpc_UpdateBookData")
|
||||||
|
} catch (error) {
|
||||||
|
return errorMessage("修改小说数据失败", "DBIpc_UpdateBookData")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,9 @@ import { DEFINE_STRING } from '../../define/define_string'
|
|||||||
import { Image } from '../Public/Image'
|
import { Image } from '../Public/Image'
|
||||||
import { LOGGER_DEFINE } from '../../define/logger_define'
|
import { LOGGER_DEFINE } from '../../define/logger_define'
|
||||||
import { errorMessage } from '../Public/generalTools'
|
import { errorMessage } from '../Public/generalTools'
|
||||||
|
import { Watermark } from '../Service/watermark'
|
||||||
let image = new Image(global)
|
let image = new Image(global)
|
||||||
|
let watermark = new Watermark()
|
||||||
|
|
||||||
function ImageIpc() {
|
function ImageIpc() {
|
||||||
// 一拆四
|
// 一拆四
|
||||||
@ -32,5 +34,11 @@ function ImageIpc() {
|
|||||||
DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE,
|
DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE,
|
||||||
async (event, value) => await image.BatchProcessImage(value)
|
async (event, value) => await image.BatchProcessImage(value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
DEFINE_STRING.IMG.PROCESS_IMAGE_WATERMASK_CHECK,
|
||||||
|
async (event, imageBase64, maskPosition, bookId) =>
|
||||||
|
await watermark.ProcessImageCheck(imageBase64, maskPosition, bookId)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
export { ImageIpc }
|
export { ImageIpc }
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
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'
|
||||||
import { ImageStyleDefine } from "../../define/iamgeStyleDefine";
|
import { ImageStyleDefine } from '../../define/iamgeStyleDefine'
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash'
|
||||||
let fspromises = require("fs").promises;
|
let fspromises = require('fs').promises
|
||||||
const sharp = require('sharp');
|
const sharp = require('sharp')
|
||||||
import { SdSettingDefine } from "../../define/setting/sdSettingDefine";
|
import { SdSettingDefine } from '../../define/setting/sdSettingDefine'
|
||||||
import { PublicMethod } from "./publicMethod";
|
import { PublicMethod } from './publicMethod'
|
||||||
import { Tools } from "../tools";
|
import { Tools } from '../tools'
|
||||||
import { errorMessage, successMessage } from "../Public/generalTools";
|
import { errorMessage, successMessage } from '../Public/generalTools'
|
||||||
import { SdApi } from "../../api/sdApi";
|
import { SdApi } from '../../api/sdApi'
|
||||||
const { v4: uuidv4 } = require('uuid');
|
const { v4: uuidv4 } = require('uuid')
|
||||||
|
|
||||||
export class SD {
|
export class SD {
|
||||||
constructor(global) {
|
constructor(global) {
|
||||||
this.global = global;
|
this.global = global
|
||||||
this.pm = new PublicMethod(global);
|
this.pm = new PublicMethod(global)
|
||||||
this.tools = new Tools();
|
this.tools = new Tools()
|
||||||
this.sdApi = new SdApi();
|
this.sdApi = new SdApi()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,10 +26,10 @@ export class SD {
|
|||||||
*/
|
*/
|
||||||
async GetAllLoras(baseURL = null) {
|
async GetAllLoras(baseURL = null) {
|
||||||
try {
|
try {
|
||||||
let data = await this.sdApi.getAllLoras(baseURL);
|
let data = await this.sdApi.getAllLoras(baseURL)
|
||||||
return successMessage(data);
|
return successMessage(data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorMessage(error.toString());
|
return errorMessage(error.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,10 +40,10 @@ export class SD {
|
|||||||
*/
|
*/
|
||||||
async GetAllSDModel(baseURL = null) {
|
async GetAllSDModel(baseURL = null) {
|
||||||
try {
|
try {
|
||||||
let data = await this.sdApi.getAllSDModel(baseURL);
|
let data = await this.sdApi.getAllSDModel(baseURL)
|
||||||
return successMessage(data);
|
return successMessage(data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorMessage(error.toString());
|
return errorMessage(error.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,10 +54,10 @@ export class SD {
|
|||||||
*/
|
*/
|
||||||
async GetAllSamplers(baseURL = null) {
|
async GetAllSamplers(baseURL = null) {
|
||||||
try {
|
try {
|
||||||
let data = await this.sdApi.getAllSamplers(baseURL);
|
let data = await this.sdApi.getAllSamplers(baseURL)
|
||||||
return successMessage(data);
|
return successMessage(data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorMessage(error.toString());
|
return errorMessage(error.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,33 +69,33 @@ export class SD {
|
|||||||
async LoadSDServiceData(baseURL = null) {
|
async LoadSDServiceData(baseURL = null) {
|
||||||
try {
|
try {
|
||||||
// 加载大模型
|
// 加载大模型
|
||||||
let sd_model = await this.GetAllSDModel(baseURL);
|
let sd_model = await this.GetAllSDModel(baseURL)
|
||||||
// 往sd_model中添加一个默认的选项
|
// 往sd_model中添加一个默认的选项
|
||||||
sd_model.data.data.unshift({
|
sd_model.data.data.unshift({
|
||||||
title: "无",
|
title: '无',
|
||||||
name: "无",
|
name: '无',
|
||||||
description: "无",
|
description: '无'
|
||||||
})
|
})
|
||||||
// 加载Lora
|
// 加载Lora
|
||||||
let lora = await this.GetAllLoras(baseURL);
|
let lora = await this.GetAllLoras(baseURL)
|
||||||
lora.data.data.unshift({
|
lora.data.data.unshift({
|
||||||
Key: "无",
|
Key: '无',
|
||||||
name: "无",
|
name: '无',
|
||||||
description: "无",
|
description: '无'
|
||||||
})
|
})
|
||||||
// 加载采样器
|
// 加载采样器
|
||||||
let sampler = await this.GetAllSamplers(baseURL);
|
let sampler = await this.GetAllSamplers(baseURL)
|
||||||
sampler.data.data.unshift({
|
sampler.data.data.unshift({
|
||||||
name: "无",
|
name: '无',
|
||||||
description: "无",
|
description: '无'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!(sd_model.code & lora.code & sampler.code)) {
|
if (!(sd_model.code & lora.code & sampler.code)) {
|
||||||
throw new Error("获取SD数据错误,请检查SD WEBUI链接!");
|
throw new Error('获取SD数据错误,请检查SD WEBUI链接!')
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < lora.data.data.length; i++) {
|
for (let i = 0; i < lora.data.data.length; i++) {
|
||||||
delete lora.data.data[i].metadata;
|
delete lora.data.data[i].metadata
|
||||||
}
|
}
|
||||||
let data = {
|
let data = {
|
||||||
sd_model: sd_model.data.data,
|
sd_model: sd_model.data.data,
|
||||||
@ -103,14 +103,13 @@ export class SD {
|
|||||||
sampler: sampler.data.data
|
sampler: sampler.data.data
|
||||||
}
|
}
|
||||||
// 处理当前获取的数据,保存到配置文件中
|
// 处理当前获取的数据,保存到配置文件中
|
||||||
await SdSettingDefine.SavePropertyValue("sd_model", data.sd_model);
|
await SdSettingDefine.SavePropertyValue('sd_model', data.sd_model)
|
||||||
await SdSettingDefine.SavePropertyValue("lora", data.lora);
|
await SdSettingDefine.SavePropertyValue('lora', data.lora)
|
||||||
await SdSettingDefine.SavePropertyValue("sampler", data.sampler);
|
await SdSettingDefine.SavePropertyValue('sampler', data.sampler)
|
||||||
|
|
||||||
return successMessage(data);
|
|
||||||
|
|
||||||
|
return successMessage(data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorMessage("加载数据失败,错误信息如下:" + error.toString());
|
return errorMessage('加载数据失败,错误信息如下:' + error.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +120,7 @@ export class SD {
|
|||||||
* */
|
* */
|
||||||
async GetImageStyleMenu() {
|
async GetImageStyleMenu() {
|
||||||
try {
|
try {
|
||||||
let style = ImageStyleDefine.getImageStyleMenu();
|
let style = ImageStyleDefine.getImageStyleMenu()
|
||||||
return {
|
return {
|
||||||
code: 1,
|
code: 1,
|
||||||
data: style
|
data: style
|
||||||
@ -129,7 +128,7 @@ export class SD {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
message: "不可能出现错误"
|
message: '不可能出现错误'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,33 +140,32 @@ export class SD {
|
|||||||
async GetImageStyleInfomation(value) {
|
async GetImageStyleInfomation(value) {
|
||||||
try {
|
try {
|
||||||
if (value) {
|
if (value) {
|
||||||
value = JSON.parse(value);
|
value = JSON.parse(value)
|
||||||
} else {
|
} else {
|
||||||
value = [];
|
value = []
|
||||||
}
|
}
|
||||||
value = value ? value : [];
|
value = value ? value : []
|
||||||
let style = ImageStyleDefine.getAllSubStyle();
|
let style = ImageStyleDefine.getAllSubStyle()
|
||||||
let tmp = [];
|
let tmp = []
|
||||||
for (let i = 0; i < value.length; i++) {
|
for (let i = 0; i < value.length; i++) {
|
||||||
const element = value[i];
|
const element = value[i]
|
||||||
for (let j = 0; j < style.length; j++) {
|
for (let j = 0; j < style.length; j++) {
|
||||||
const item = style[j];
|
const item = style[j]
|
||||||
if (item.id == element) {
|
if (item.id == element) {
|
||||||
tmp.push(item);
|
tmp.push(item)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let newSubStyle = cloneDeep(tmp);
|
let newSubStyle = cloneDeep(tmp)
|
||||||
for (let i = 0; i < newSubStyle.length; i++) {
|
for (let i = 0; i < newSubStyle.length; i++) {
|
||||||
const element = newSubStyle[i];
|
const element = newSubStyle[i]
|
||||||
element.image = path.join(define.image_path, "style/" + element.image);
|
element.image = path.join(define.image_path, 'style/' + element.image)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
code: 1,
|
code: 1,
|
||||||
data: newSubStyle
|
data: newSubStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
@ -183,17 +181,19 @@ export class SD {
|
|||||||
*/
|
*/
|
||||||
async GetStyleImageSubList(value) {
|
async GetStyleImageSubList(value) {
|
||||||
try {
|
try {
|
||||||
let subStyle = ImageStyleDefine.getImagePathById(value);
|
let subStyle = ImageStyleDefine.getImagePathById(value)
|
||||||
let newSubStyle = cloneDeep(subStyle);
|
let newSubStyle = cloneDeep(subStyle)
|
||||||
for (let i = 0; i < newSubStyle.length; i++) {
|
for (let i = 0; i < newSubStyle.length; i++) {
|
||||||
const element = newSubStyle[i];
|
const element = newSubStyle[i]
|
||||||
element.image = path.join(define.image_path, "style/" + element.image);
|
element.image = path.join(
|
||||||
|
define.image_path,
|
||||||
|
'style/' + element.image + '?t=' + new Date().getTime()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
code: 1,
|
code: 1,
|
||||||
data: newSubStyle
|
data: newSubStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
@ -209,27 +209,27 @@ export class SD {
|
|||||||
*/
|
*/
|
||||||
async txt2img(value) {
|
async txt2img(value) {
|
||||||
try {
|
try {
|
||||||
value = JSON.parse(value);
|
value = JSON.parse(value)
|
||||||
let data = value[0];
|
let data = value[0]
|
||||||
let res = await this.sdApi.txt2img(data);
|
let res = await this.sdApi.txt2img(data)
|
||||||
// 将base· 64的图片转换为图片
|
// 将base· 64的图片转换为图片
|
||||||
// 将当前的图片保存到指定的文件夹中,然后返回文件路径,并且可以复制到指定的文件,删除exif信息
|
// 将当前的图片保存到指定的文件夹中,然后返回文件路径,并且可以复制到指定的文件,删除exif信息
|
||||||
let image_paths = [];
|
let image_paths = []
|
||||||
for (let i = 0; res.data.images && i < res.data.images.length; i++) {
|
for (let i = 0; res.data.images && i < res.data.images.length; i++) {
|
||||||
const element = res.data.images[i];
|
const element = res.data.images[i]
|
||||||
let image_data = {
|
let image_data = {
|
||||||
base64: element
|
base64: element
|
||||||
}
|
}
|
||||||
// 将保存图片添加到队列中
|
// 将保存图片添加到队列中
|
||||||
let image_name = `sd_${Date.now()}_${uuidv4()}.png`;
|
let image_name = `sd_${Date.now()}_${uuidv4()}.png`
|
||||||
let image_path = path.join(define.temp_sd_image, image_name);
|
let image_path = path.join(define.temp_sd_image, image_name)
|
||||||
image_path = await this.tools.saveBase64ToImage(element, image_path);
|
image_path = await this.tools.saveBase64ToImage(element, image_path)
|
||||||
image_data["image_path"] = image_path;
|
image_data['image_path'] = image_path
|
||||||
image_paths.push(image_data);
|
image_paths.push(image_data)
|
||||||
}
|
}
|
||||||
return successMessage(image_paths);
|
return successMessage(image_paths)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorMessage("生图失败,错误信息如下:" + error.toString());
|
return errorMessage('生图失败,错误信息如下:' + error.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,136 +239,143 @@ export class SD {
|
|||||||
* @param {任务队列信息} task_list 301198499
|
* @param {任务队列信息} task_list 301198499
|
||||||
*/
|
*/
|
||||||
async OneImageGeneration(image, task_list, seed = -1) {
|
async OneImageGeneration(image, task_list, seed = -1) {
|
||||||
let taskPath = path.join(this.global.config.project_path, "scripts/task_list.json")
|
let taskPath = path.join(this.global.config.project_path, 'scripts/task_list.json')
|
||||||
try {
|
try {
|
||||||
let imageJson = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8'));
|
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 sd_setting = JSON.parse(await fspromises.readFile(define.sd_setting, 'utf-8'))
|
||||||
let model = imageJson.model;
|
let model = imageJson.model
|
||||||
let image_json = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8'));
|
let image_json = JSON.parse(await fspromises.readFile(image + '.json', 'utf-8'))
|
||||||
let image_path = "";
|
let image_path = ''
|
||||||
let target_image_path = "";
|
let target_image_path = ''
|
||||||
if (image_json.name) {
|
if (image_json.name) {
|
||||||
image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/tmp_${image_json.name}`)
|
image_path = path.join(
|
||||||
target_image_path = path.join(this.global.config.project_path, `tmp/${task_list.out_folder}/${image_json.name}`)
|
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 {
|
} else {
|
||||||
image_path = image.replaceAll("input_crop", task_list.out_folder).split(".png")[0] + "_tmp.png";
|
image_path =
|
||||||
target_image_path = image.replaceAll("input_crop", task_list.out_folder);
|
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 image_styles = await ImageStyleDefine.getImageStyleStringByIds(task_list.image_style_list ? task_list.image_style_list : []);
|
let image_styles = await ImageStyleDefine.getImageStyleStringByIds(
|
||||||
let prompt = sd_setting.webui.prompt + image_styles;
|
task_list.image_style_list ? task_list.image_style_list : []
|
||||||
|
)
|
||||||
|
let prompt = sd_setting.webui.prompt + image_styles
|
||||||
// 拼接提示词
|
// 拼接提示词
|
||||||
if (task_list.image_style != null) {
|
if (task_list.image_style != null) {
|
||||||
prompt += `((${task_list.image_style})), `;
|
prompt += `((${task_list.image_style})), `
|
||||||
}
|
}
|
||||||
if (task_list.lora != null) {
|
if (task_list.lora != null) {
|
||||||
prompt += `${task_list.lora}, `;
|
prompt += `${task_list.lora}, `
|
||||||
}
|
}
|
||||||
prompt += imageJson.webui_config.prompt;
|
prompt += imageJson.webui_config.prompt
|
||||||
// 判断当前是不是有开修脸修手
|
// 判断当前是不是有开修脸修手
|
||||||
let ADetailer = {
|
let ADetailer = {
|
||||||
args: sd_setting.adetailer
|
args: sd_setting.adetailer
|
||||||
};
|
}
|
||||||
if (model == "img2img") {
|
if (model == 'img2img') {
|
||||||
let web_api = this.global.config.webui_api_url + 'sdapi/v1/img2img'
|
let web_api = this.global.config.webui_api_url + 'sdapi/v1/img2img'
|
||||||
let sd_config = imageJson["webui_config"];
|
let sd_config = imageJson['webui_config']
|
||||||
sd_config.prompt = prompt;
|
sd_config.prompt = prompt
|
||||||
sd_config.seed = seed;
|
sd_config.seed = seed
|
||||||
let im = await fspromises.readFile(image, 'binary');
|
let im = await fspromises.readFile(image, 'binary')
|
||||||
sd_config.init_images = [new Buffer.from(im, 'binary').toString('base64')];
|
sd_config.init_images = [new Buffer.from(im, 'binary').toString('base64')]
|
||||||
if (imageJson.adetailer) {
|
if (imageJson.adetailer) {
|
||||||
let ta = {
|
let ta = {
|
||||||
ADetailer: ADetailer
|
ADetailer: ADetailer
|
||||||
}
|
}
|
||||||
sd_config.alwayson_scripts = ta;
|
sd_config.alwayson_scripts = ta
|
||||||
}
|
}
|
||||||
sd_config.height = sd_setting.webui.height;
|
sd_config.height = sd_setting.webui.height
|
||||||
sd_config.width = sd_setting.webui.width;
|
sd_config.width = sd_setting.webui.width
|
||||||
const response = await axios.post(web_api, sd_config);
|
const response = await axios.post(web_api, sd_config)
|
||||||
let info = JSON.parse(response.data.info);
|
let info = JSON.parse(response.data.info)
|
||||||
if (seed == -1) {
|
if (seed == -1) {
|
||||||
seed = info.seed;
|
seed = info.seed
|
||||||
}
|
}
|
||||||
// 目前是单图出图
|
// 目前是单图出图
|
||||||
let images = response.data.images;
|
let images = response.data.images
|
||||||
let imageData = Buffer.from(images[0].split(",", 1)[0], 'base64');
|
let imageData = Buffer.from(images[0].split(',', 1)[0], 'base64')
|
||||||
await sharp(imageData)
|
await sharp(imageData)
|
||||||
.toFile(image_path)
|
.toFile(image_path)
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
// console.log("图生图成功" + image_path);
|
// console.log("图生图成功" + image_path);
|
||||||
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path);
|
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path)
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
throw new Error(err);
|
throw new Error(err)
|
||||||
});
|
})
|
||||||
return seed;
|
return seed
|
||||||
|
} else if (model == 'txt2img') {
|
||||||
} else if (model == "txt2img") {
|
|
||||||
let body = {
|
let body = {
|
||||||
"prompt": prompt,
|
prompt: prompt,
|
||||||
"negative_prompt": imageJson.webui_config.negative_prompt,
|
negative_prompt: imageJson.webui_config.negative_prompt,
|
||||||
"seed": seed,
|
seed: seed,
|
||||||
"sampler_name": imageJson.webui_config.sampler_name,
|
sampler_name: imageJson.webui_config.sampler_name,
|
||||||
// 提示词相关性
|
// 提示词相关性
|
||||||
"cfg_scale": imageJson.webui_config.cfg_scale,
|
cfg_scale: imageJson.webui_config.cfg_scale,
|
||||||
"width": sd_setting.webui.width,
|
width: sd_setting.webui.width,
|
||||||
"height": sd_setting.webui.height,
|
height: sd_setting.webui.height,
|
||||||
"batch_size": 1,
|
batch_size: 1,
|
||||||
"n_iter": 1,
|
n_iter: 1,
|
||||||
"steps": imageJson.webui_config.steps,
|
steps: imageJson.webui_config.steps,
|
||||||
"save_images": false,
|
save_images: false
|
||||||
}
|
}
|
||||||
let web_api = this.global.config.webui_api_url + 'sdapi/v1/txt2img';
|
let web_api = this.global.config.webui_api_url + 'sdapi/v1/txt2img'
|
||||||
|
|
||||||
if (imageJson.adetailer) {
|
if (imageJson.adetailer) {
|
||||||
let ta = {
|
let ta = {
|
||||||
ADetailer: ADetailer
|
ADetailer: ADetailer
|
||||||
}
|
}
|
||||||
body.alwayson_scripts = ta;
|
body.alwayson_scripts = ta
|
||||||
}
|
}
|
||||||
const response = await axios.post(web_api, body);
|
const response = await axios.post(web_api, body)
|
||||||
let info = JSON.parse(response.data.info);
|
let info = JSON.parse(response.data.info)
|
||||||
if (seed == -1) {
|
if (seed == -1) {
|
||||||
seed = info.seed;
|
seed = info.seed
|
||||||
}
|
}
|
||||||
// 目前是单图出图
|
// 目前是单图出图
|
||||||
let images = response.data.images;
|
let images = response.data.images
|
||||||
let imageData = Buffer.from(images[0].split(",", 1)[0], 'base64');
|
let imageData = Buffer.from(images[0].split(',', 1)[0], 'base64')
|
||||||
await sharp(imageData)
|
await sharp(imageData)
|
||||||
.toFile(image_path)
|
.toFile(image_path)
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
// console.log("文生图成功" + image_path);
|
// console.log("文生图成功" + image_path);
|
||||||
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path);
|
await this.tools.deletePngAndDeleteExifData(image_path, target_image_path)
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
// console.log(err)
|
// console.log(err)
|
||||||
throw new Error(err);
|
throw new Error(err)
|
||||||
});
|
})
|
||||||
return seed;
|
return seed
|
||||||
} else {
|
} else {
|
||||||
throw new Error("SD 模式错误");
|
throw new Error('SD 模式错误')
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 当前队列执行失败移除整个批次的任务
|
// 当前队列执行失败移除整个批次的任务
|
||||||
this.global.requestQuene.removeTask(task_list.out_folder, null)
|
this.global.requestQuene.removeTask(task_list.out_folder, null)
|
||||||
this.global.fileQueue.enqueue(async () => {
|
this.global.fileQueue.enqueue(async () => {
|
||||||
// 记录失败状态
|
// 记录失败状态
|
||||||
let task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'));
|
let task_list_json = JSON.parse(await fspromises.readFile(taskPath, 'utf-8'))
|
||||||
// 修改指定的列表的数据
|
// 修改指定的列表的数据
|
||||||
task_list_json.task_list.map(a => {
|
task_list_json.task_list.map((a) => {
|
||||||
if (a.id == task_list.id) {
|
if (a.id == task_list.id) {
|
||||||
a.status = "error";
|
a.status = 'error'
|
||||||
a.errorMessage = error.toString();
|
a.errorMessage = error.toString()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// 写入
|
// 写入
|
||||||
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json));
|
await fspromises.writeFile(taskPath, JSON.stringify(task_list_json))
|
||||||
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, {
|
this.global.newWindow[0].win.webContents.send(DEFINE_STRING.IMAGE_TASK_STATUS_REFRESH, {
|
||||||
out_folder: task_list.out_folder,
|
out_folder: task_list.out_folder,
|
||||||
status: "error"
|
status: 'error'
|
||||||
});
|
|
||||||
})
|
})
|
||||||
throw error;
|
})
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,16 +229,27 @@ export class ReverseBook extends BookBasic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过小说ID和小说批次任务ID获取小说和小说批次任务数据
|
||||||
|
* @param bookId 小说ID
|
||||||
|
* @param bookTaskName 小说批次的名字,或者是小说批次任务的ID
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async GetBookAndTask(bookId: string, bookTaskName: string) {
|
async GetBookAndTask(bookId: string, bookTaskName: string) {
|
||||||
let book = this.bookService.GetBookDataById(bookId)
|
let book = this.bookService.GetBookDataById(bookId)
|
||||||
if (book == null) {
|
if (book == null) {
|
||||||
throw new Error("查找小说数据失败");
|
throw new Error("查找小说数据失败");
|
||||||
}
|
}
|
||||||
// 获取小说对应的批次任务数据,默认初始化为第一个
|
// 获取小说对应的批次任务数据,默认初始化为第一个
|
||||||
let bookTaskRes = await this.bookTaskService.GetBookTaskData({
|
let condition = {
|
||||||
bookId: bookId,
|
bookId: bookId
|
||||||
name: bookTaskName
|
} as Book.QueryBookBackTaskCondition
|
||||||
})
|
if (bookTaskName == "output_00001") {
|
||||||
|
condition["name"] = bookTaskName
|
||||||
|
} else {
|
||||||
|
condition["id"] = bookTaskName
|
||||||
|
}
|
||||||
|
let bookTaskRes = await this.bookTaskService.GetBookTaskData(condition)
|
||||||
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
|
if (bookTaskRes.data.bookTasks.length <= 0 || bookTaskRes.data.total <= 0) {
|
||||||
let msg = "没有找到对应的小说批次任务数据"
|
let msg = "没有找到对应的小说批次任务数据"
|
||||||
this.taskScheduler.AddLogToDB(bookId, book.type, msg, OtherData.DEFAULT, LoggerStatus.FAIL)
|
this.taskScheduler.AddLogToDB(bookId, book.type, msg, OtherData.DEFAULT, LoggerStatus.FAIL)
|
||||||
@ -322,12 +333,12 @@ export class ReverseBook extends BookBasic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始执行分镜任务
|
* 提起文案
|
||||||
*/
|
*/
|
||||||
async GetCopywriting(bookId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
async GetCopywriting(bookId: string, bookTaskId: string = null): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||||
try {
|
try {
|
||||||
await this.InitService()
|
await this.InitService()
|
||||||
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
|
let { book, bookTask } = await this.GetBookAndTask(bookId, bookTaskId ? bookTaskId : 'output_00001')
|
||||||
if (isEmpty(book.subtitlePosition)) {
|
if (isEmpty(book.subtitlePosition)) {
|
||||||
throw new Error("请先设置小说的字幕位置")
|
throw new Error("请先设置小说的字幕位置")
|
||||||
}
|
}
|
||||||
@ -390,95 +401,6 @@ export class ReverseBook extends BookBasic {
|
|||||||
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting')
|
return errorMessage("获取分镜数据失败,失败信息如下:" + error.message, 'ReverseBook_GetCopywriting')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行去除水印操作
|
|
||||||
* @param bookId 小说ID
|
|
||||||
*/
|
|
||||||
async RemoveWatermark(bookId) {
|
|
||||||
try {
|
|
||||||
await this.InitService()
|
|
||||||
let { book, bookTask } = await this.GetBookAndTask(bookId, 'output_00001')
|
|
||||||
let subtitlePosition = book.subtitlePosition;
|
|
||||||
if (isEmpty(subtitlePosition)) {
|
|
||||||
throw new Error("当前没有文案位置的内容,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置")
|
|
||||||
}
|
|
||||||
if (!ValidateJson(subtitlePosition)) {
|
|
||||||
throw new Error("文案位置格式有误,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置")
|
|
||||||
}
|
|
||||||
let subtitlePositionParse = JSON.parse(subtitlePosition)
|
|
||||||
if (subtitlePositionParse.length <= 0) {
|
|
||||||
throw new Error("文案位置格式有误,请先进行 ‘开始提取文案 -> 提取文案设置 -> 框选大概位置 -> 保存位置’ 操作保存文案的位置")
|
|
||||||
}
|
|
||||||
let inputImageFolder = path.resolve(define.project_path, `${book.id}/tmp/input`)
|
|
||||||
if (!(await CheckFileOrDirExist(inputImageFolder))) {
|
|
||||||
throw new Error("输出文件夹不存在,请先进行抽帧等操作")
|
|
||||||
}
|
|
||||||
let iamgePaths = await GetFilesWithExtensions(inputImageFolder, ['.png'])
|
|
||||||
if (iamgePaths.length <= 0) {
|
|
||||||
throw new Error("没有检查到抽帧图片,请先进行抽帧等操作")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理位置生成蒙板
|
|
||||||
let inputImage = iamgePaths[0]
|
|
||||||
let outImagePath = path.resolve(define.project_path, `${book.id}/data/mask/mask_temp_${new Date().getTime()}.png`)
|
|
||||||
await CheckFolderExistsOrCreate(path.dirname(outImagePath));
|
|
||||||
await ProcessImage(inputImage, outImagePath, {
|
|
||||||
x: subtitlePositionParse[0].startX,
|
|
||||||
y: subtitlePositionParse[0].startY,
|
|
||||||
width: subtitlePositionParse[0].width,
|
|
||||||
height: subtitlePositionParse[0].height
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!(await CheckFileOrDirExist(outImagePath))) {
|
|
||||||
throw new Error("生成蒙板失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始去除水印
|
|
||||||
let bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({
|
|
||||||
bookId: bookId,
|
|
||||||
bookTaskId: bookTask.id
|
|
||||||
}).data as Book.SelectBookTaskDetail[]
|
|
||||||
|
|
||||||
for (let i = 0; i < bookTaskDetails.length; i++) {
|
|
||||||
const element = bookTaskDetails[i];
|
|
||||||
let bookInputImage = element.oldImage;
|
|
||||||
let name = path.basename(bookInputImage)
|
|
||||||
let bak = path.resolve(define.project_path, `${book.id}/tmp/input/bak/${name}`);
|
|
||||||
// 做个备份吧
|
|
||||||
await CopyFileOrFolder(bookInputImage, bak)
|
|
||||||
await fs.promises.unlink(bookInputImage) // 删除原来的图片
|
|
||||||
// 开始调用去除水印的方法
|
|
||||||
let imageBase64 = await GetImageBase64(bak)
|
|
||||||
let maskBase64 = await GetImageBase64(outImagePath)
|
|
||||||
|
|
||||||
let res = await this.watermark.ProcessImage({
|
|
||||||
imageBase64: imageBase64,
|
|
||||||
maskBase64: maskBase64,
|
|
||||||
type: 'file',
|
|
||||||
inputFilePath: bak,
|
|
||||||
maskPath: outImagePath,
|
|
||||||
outFilePath: bookInputImage
|
|
||||||
})
|
|
||||||
|
|
||||||
// 去水印执行完毕
|
|
||||||
this.sendReturnMessage({
|
|
||||||
code: 1,
|
|
||||||
id: element.id,
|
|
||||||
type: ResponseMessageType.REMOVE_WATERMARK,
|
|
||||||
data: res
|
|
||||||
}, DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN)
|
|
||||||
|
|
||||||
this.taskScheduler.AddLogToDB(bookId, book.type, `${element.name} 去除水印完成`, element.bookTaskId, LoggerStatus.SUCCESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全部完毕
|
|
||||||
return successMessage(null, "全部图片去除水印完成", "ReverseBook_RemoveWatermark")
|
|
||||||
} catch (error) {
|
|
||||||
return errorMessage("去除水印失败,错误信息如下:" + error.message, "ReverseBook_RemoveWatermark")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region 反推相关任务
|
//#region 反推相关任务
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import fs from 'fs'
|
|||||||
import util from 'util'
|
import util from 'util'
|
||||||
import { exec } from 'child_process'
|
import { exec } from 'child_process'
|
||||||
const execAsync = util.promisify(exec);
|
const execAsync = util.promisify(exec);
|
||||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from '../../define/Tools/file'
|
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, CopyFileOrFolder, GetFilesWithExtensions } from '../../define/Tools/file'
|
||||||
import { errorMessage, successMessage } from '../Public/generalTools'
|
import { errorMessage, successMessage } from '../Public/generalTools'
|
||||||
import { SoftwareService } from '../../define/db/service/SoftWare/softwareService'
|
import { SoftwareService } from '../../define/db/service/SoftWare/softwareService'
|
||||||
import { isEmpty } from 'lodash'
|
import { isEmpty } from 'lodash'
|
||||||
@ -12,15 +12,28 @@ import { ValidateJson } from '../../define/Tools/validate'
|
|||||||
import { define } from '../../define/define'
|
import { define } from '../../define/define'
|
||||||
import { LOGGER_DEFINE } from '../../define/logger_define'
|
import { LOGGER_DEFINE } from '../../define/logger_define'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { Base64ToFile } from '../../define/Tools/image'
|
import { Base64ToFile, GetImageBase64 } from '../../define/Tools/image'
|
||||||
import { TaskScheduler } from './taskScheduler';
|
import { TaskScheduler } from './taskScheduler';
|
||||||
import { LoggerStatus, OtherData } from '../../define/enum/softwareEnum';
|
import { LoggerStatus, OtherData, ResponseMessageType } from '../../define/enum/softwareEnum';
|
||||||
import { basicApi } from '../../api/apiBasic';
|
import { basicApi } from '../../api/apiBasic';
|
||||||
|
import { FfmpegOptions } from './ffmpegOptions';
|
||||||
|
import { ProcessImage } from '../../define/Tools/image';
|
||||||
|
import { BookService } from '../../define/db/service/Book/bookService';
|
||||||
|
import { OperateBookType } from '../../define/enum/bookEnum';
|
||||||
|
import { GeneralResponse } from '../../model/generalResponse';
|
||||||
|
import { BookTaskDetailService } from '../../define/db/service/Book/bookTaskDetailService';
|
||||||
|
import { Book } from '../../model/book';
|
||||||
|
import { DEFINE_STRING } from '../../define/define_string';
|
||||||
|
import { BookTaskService } from '../../define/db/service/Book/bookTaskService';
|
||||||
|
|
||||||
export class Watermark {
|
export class Watermark {
|
||||||
softwareService: SoftwareService
|
softwareService: SoftwareService
|
||||||
taskScheduler: TaskScheduler;
|
taskScheduler: TaskScheduler;
|
||||||
constructor() { }
|
bookService: BookService
|
||||||
|
bookTaskDetailService: BookTaskDetailService
|
||||||
|
bookTaskService: BookTaskService
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
async InitService() {
|
async InitService() {
|
||||||
if (!this.softwareService) {
|
if (!this.softwareService) {
|
||||||
@ -29,6 +42,20 @@ export class Watermark {
|
|||||||
if (!this.taskScheduler) {
|
if (!this.taskScheduler) {
|
||||||
this.taskScheduler = new TaskScheduler()
|
this.taskScheduler = new TaskScheduler()
|
||||||
}
|
}
|
||||||
|
if (!this.bookService) {
|
||||||
|
this.bookService = await BookService.getInstance()
|
||||||
|
}
|
||||||
|
if (!this.bookTaskDetailService) {
|
||||||
|
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||||
|
}
|
||||||
|
if (!this.bookTaskService) {
|
||||||
|
this.bookTaskService = await BookTaskService.getInstance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主动返回前端的消息
|
||||||
|
sendReturnMessage(data: GeneralResponse.MessageResponse, message_name = DEFINE_STRING.BOOK.GET_COPYWRITING_RETURN) {
|
||||||
|
global.newWindow[0].win.webContents.send(message_name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region 设置
|
//#region 设置
|
||||||
@ -114,7 +141,7 @@ export class Watermark {
|
|||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region 去除水印
|
//#region 去除水印相关操作
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 去除水印,试用本地lama
|
* 去除水印,试用本地lama
|
||||||
@ -234,7 +261,7 @@ export class Watermark {
|
|||||||
* 去除图片水印,当前只支持本地,后续会支持 iopaint
|
* 去除图片水印,当前只支持本地,后续会支持 iopaint
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
async ProcessImage(value: ImageModel.ProcessImageParams) {
|
async ProcessImage(value: ImageModel.ProcessImageParams): Promise<string | Buffer> {
|
||||||
let outDir = path.dirname(value.outFilePath)
|
let outDir = path.dirname(value.outFilePath)
|
||||||
await CheckFolderExistsOrCreate(outDir);
|
await CheckFolderExistsOrCreate(outDir);
|
||||||
|
|
||||||
@ -253,6 +280,182 @@ export class Watermark {
|
|||||||
throw new Error('未知的去除水印模式')
|
throw new Error('未知的去除水印模式')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region 开始去除水印
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看去除水印设置 去除图片指定位置的水印,这个是要单独做蒙板处理的
|
||||||
|
* @param imageBase64 基础图片的base64
|
||||||
|
* @param maskPosition 前端勾选的水印的位置
|
||||||
|
*/
|
||||||
|
async ProcessImageCheck(imageBase64: string, maskPosition: [], bookId: string) {
|
||||||
|
try {
|
||||||
|
await this.InitService()
|
||||||
|
// 开始做处理
|
||||||
|
console.log(imageBase64, maskPosition);
|
||||||
|
let book = this.bookService.GetBookDataById(bookId)
|
||||||
|
if (book == null) {
|
||||||
|
throw new Error('指定的小说数据不存在')
|
||||||
|
}
|
||||||
|
let inputFilePath = path.resolve(book.bookFolderPath, `data/mask/temp/${new Date().getTime()}.png`)
|
||||||
|
await CheckFolderExistsOrCreate(path.dirname(inputFilePath))
|
||||||
|
const base64Image = imageBase64.split(';base64,').pop();
|
||||||
|
await fs.promises.writeFile(inputFilePath, base64Image, { encoding: 'base64' })
|
||||||
|
let outputPath = path.resolve(book.bookFolderPath, `data/mask/mask_temp_${new Date().getTime()}.png`)
|
||||||
|
let outImagePath = path.resolve(book.bookFolderPath, `data/mask/temp/out_${new Date().getTime()}.png`)
|
||||||
|
|
||||||
|
await ProcessImage(inputFilePath, outputPath, maskPosition.map((item: any) => { return { x: item.startX, y: item.startY, width: item.width, height: item.height } }))
|
||||||
|
let markBase64 = await GetImageBase64(outputPath)
|
||||||
|
|
||||||
|
// 开始去谁赢
|
||||||
|
let res = await this.ProcessImage({
|
||||||
|
imageBase64: imageBase64,
|
||||||
|
maskBase64: markBase64, // 处理蒙板的base64
|
||||||
|
type: 'arrayBuffer', // 返回数据的类型(是直接写道输出文件地址,还是返回Buffer数组)
|
||||||
|
inputFilePath: inputFilePath, // 输入文件的地址(待处理的)
|
||||||
|
maskPath: outputPath, // 蒙板文件的地址
|
||||||
|
outFilePath: outImagePath // 输出文件的地址
|
||||||
|
})
|
||||||
|
|
||||||
|
// 将得到的buffer返回前端进行渲染
|
||||||
|
return successMessage(res, "去除水印成功", "Image_ProcessImageCheck")
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return errorMessage('去除图片指定位置的水印失败,失败信息如下:' + error.toString(), 'Image_ProcessImageCheck')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行去除水印操作
|
||||||
|
* @param bookId 小说ID
|
||||||
|
*/
|
||||||
|
async RemoveWatermark(id: string, operateBookType: OperateBookType): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||||
|
try {
|
||||||
|
await this.InitService()
|
||||||
|
let book = undefined as Book.SelectBook
|
||||||
|
let bookTask = undefined as Book.SelectBookTask
|
||||||
|
let bookTaskDetails = undefined as Book.SelectBookTaskDetail[]
|
||||||
|
if (operateBookType == OperateBookType.BOOKTASK) {
|
||||||
|
bookTask = this.bookTaskService.GetBookTaskDataById(id);
|
||||||
|
if (bookTask == null) {
|
||||||
|
throw new Error('指定的小说任务数据不存在')
|
||||||
|
}
|
||||||
|
book = this.bookService.GetBookDataById(bookTask.bookId)
|
||||||
|
if (book == null) {
|
||||||
|
throw new Error("小说数据不存在,请检查")
|
||||||
|
}
|
||||||
|
bookTaskDetails = this.bookTaskDetailService.GetBookTaskData({
|
||||||
|
bookTaskId: bookTask.id,
|
||||||
|
bookId: bookTask.bookId
|
||||||
|
}).data as Book.SelectBookTaskDetail[]
|
||||||
|
} else if (operateBookType == OperateBookType.BOOKTASKDETAIL) {
|
||||||
|
bookTask = this.bookTaskService.GetBookTaskDataById(id);
|
||||||
|
if (bookTask == null) {
|
||||||
|
throw new Error('指定的小说任务数据不存在')
|
||||||
|
}
|
||||||
|
book = this.bookService.GetBookDataById(bookTask.bookId)
|
||||||
|
if (book == null) {
|
||||||
|
throw new Error("小说数据不存在,请检查")
|
||||||
|
}
|
||||||
|
let bookTaskDetail = this.bookTaskDetailService.GetBookTaskDetailDataById(id)
|
||||||
|
if (bookTaskDetail == null) {
|
||||||
|
throw new Error("指定的小说任务分镜信息不存在,请检查")
|
||||||
|
}
|
||||||
|
bookTaskDetails = [bookTaskDetail];
|
||||||
|
} else {
|
||||||
|
throw new Error("未知的操作类型")
|
||||||
|
}
|
||||||
|
|
||||||
|
let watermarkPosition = book.watermarkPosition;
|
||||||
|
if (isEmpty(watermarkPosition)) {
|
||||||
|
throw new Error("当前没有文案位置的内容,请先进行 ‘开始去除水印 -> 选择水印位置 -> 框选大概位置 -> 保存蒙板位置’ 操作保存蒙板的位置")
|
||||||
|
}
|
||||||
|
if (!ValidateJson(watermarkPosition)) {
|
||||||
|
throw new Error("文案位置格式有误,请先进行 ‘开始去除水印 -> 选择水印位置 -> 框选大概位置 -> 保存蒙板位置’ 操作保存蒙板的位置")
|
||||||
|
}
|
||||||
|
let watermarkPositionParse = JSON.parse(watermarkPosition) as any[]
|
||||||
|
if (watermarkPositionParse.length <= 0) {
|
||||||
|
throw new Error("没有检测到去除水印的蒙板位置,请先进行 ‘开始去除水印 -> 选择水印位置 -> 框选大概位置 -> 保存蒙板位置’ 操作保存蒙板的位置")
|
||||||
|
}
|
||||||
|
let inputImageFolder = path.resolve(define.project_path, `${book.id}/tmp/input`)
|
||||||
|
if (!(await CheckFileOrDirExist(inputImageFolder))) {
|
||||||
|
throw new Error("输出文件夹不存在,请先进行抽帧等操作")
|
||||||
|
}
|
||||||
|
let iamgePaths = await GetFilesWithExtensions(inputImageFolder, ['.png'])
|
||||||
|
if (iamgePaths.length <= 0) {
|
||||||
|
throw new Error("没有检查到抽帧图片,请先进行抽帧等操作")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理位置生成蒙板
|
||||||
|
let inputImage = iamgePaths[0]
|
||||||
|
let outImagePath = path.resolve(define.project_path, `${book.id}/data/mask/mask_temp_${new Date().getTime()}.png`)
|
||||||
|
await CheckFolderExistsOrCreate(path.dirname(outImagePath));
|
||||||
|
// 这边要计算倍率
|
||||||
|
let watermarkPositionArray = watermarkPositionParse.map(item => {
|
||||||
|
return {
|
||||||
|
x: item.startX,
|
||||||
|
y: item.startY,
|
||||||
|
width: item.width,
|
||||||
|
height: item.height,
|
||||||
|
imageWidth: item.imageWidth,
|
||||||
|
imageHeight: item.imageHeight
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await ProcessImage(inputImage, outImagePath, watermarkPositionArray)
|
||||||
|
|
||||||
|
if (!(await CheckFileOrDirExist(outImagePath))) {
|
||||||
|
throw new Error("生成蒙板失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始去除水印
|
||||||
|
for (let i = 0; i < bookTaskDetails.length; i++) {
|
||||||
|
const element = bookTaskDetails[i];
|
||||||
|
let bookInputImage = element.oldImage;
|
||||||
|
let name = path.basename(bookInputImage)
|
||||||
|
let bak = path.resolve(define.project_path, `${book.id}/tmp/input/bak/${name}`);
|
||||||
|
// 做个备份吧
|
||||||
|
await CopyFileOrFolder(bookInputImage, bak)
|
||||||
|
// 新的输出地址
|
||||||
|
let tempOutImagePath = path.join(path.dirname(bookInputImage), "temp_" + path.basename(outImagePath))
|
||||||
|
// await fs.promises.unlink(bookInputImage) // 删除原来的图片
|
||||||
|
// 开始调用去除水印的方法
|
||||||
|
let imageBase64 = await GetImageBase64(bak)
|
||||||
|
let maskBase64 = await GetImageBase64(outImagePath)
|
||||||
|
|
||||||
|
let res = await this.ProcessImage({
|
||||||
|
imageBase64: imageBase64,
|
||||||
|
maskBase64: maskBase64,
|
||||||
|
type: 'file',
|
||||||
|
inputFilePath: bak,
|
||||||
|
maskPath: outImagePath,
|
||||||
|
outFilePath: tempOutImagePath
|
||||||
|
})
|
||||||
|
// 执行完成,将缓存的图片删除,复制新的图片到原来的位置
|
||||||
|
await fs.promises.unlink(bookInputImage)
|
||||||
|
await CopyFileOrFolder(tempOutImagePath, bookInputImage)
|
||||||
|
// 删除临时文件
|
||||||
|
await fs.promises.unlink(tempOutImagePath)
|
||||||
|
|
||||||
|
// 去水印执行完毕
|
||||||
|
this.sendReturnMessage({
|
||||||
|
code: 1,
|
||||||
|
id: element.id,
|
||||||
|
type: ResponseMessageType.REMOVE_WATERMARK,
|
||||||
|
data: bookInputImage
|
||||||
|
}, DEFINE_STRING.BOOK.REMOVE_WATERMARK_RETURN)
|
||||||
|
|
||||||
|
|
||||||
|
this.taskScheduler.AddLogToDB(book.id, book.type, `${element.name} 去除水印完成`, element.bookTaskId, LoggerStatus.SUCCESS)
|
||||||
|
}
|
||||||
|
// 全部完毕
|
||||||
|
return successMessage(null, "全部图片去除水印完成", "ReverseBook_RemoveWatermark")
|
||||||
|
} catch (error) {
|
||||||
|
return errorMessage("去除水印失败,错误信息如下:" + error.message, "ReverseBook_RemoveWatermark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
import { version } from '../../package.json'
|
import { version } from '../../package.json'
|
||||||
import { graphics } from 'systeminformation'
|
import { graphics } from 'systeminformation'
|
||||||
|
|
||||||
import { app, shell, BrowserWindow, ipcMain, dialog, nativeTheme } from 'electron'
|
import { app, shell, BrowserWindow, ipcMain, dialog, nativeTheme, session } from 'electron'
|
||||||
import path, { join } from 'path'
|
import path, { join } from 'path'
|
||||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||||
import icon from '../../resources/icon.ico?asset'
|
import icon from '../../resources/icon.ico?asset'
|
||||||
@ -52,7 +52,7 @@ async function createWindow(hash = 'ShowMessage', data, url = null) {
|
|||||||
// 判断当前是不是有设置的宽高,用的话记忆
|
// 判断当前是不是有设置的宽高,用的话记忆
|
||||||
let isRe =
|
let isRe =
|
||||||
global.config.window_wh_bm_remember && hash == 'ShowMessage' && global.config.window_wh_bm
|
global.config.window_wh_bm_remember && hash == 'ShowMessage' && global.config.window_wh_bm
|
||||||
|
const ses = session.fromPartition('persist:my-session')
|
||||||
let mainWindow = new BrowserWindow({
|
let mainWindow = new BrowserWindow({
|
||||||
width: isRe ? global.config.window_wh_bm.width : 900,
|
width: isRe ? global.config.window_wh_bm.width : 900,
|
||||||
height: isRe ? global.config.window_wh_bm.height : 675,
|
height: isRe ? global.config.window_wh_bm.height : 675,
|
||||||
@ -69,7 +69,9 @@ async function createWindow(hash = 'ShowMessage', data, url = null) {
|
|||||||
nodeIntegration: hash == 'discord' ? false : true, // 在网页中集成Node
|
nodeIntegration: hash == 'discord' ? false : true, // 在网页中集成Node
|
||||||
nodeIntegrationInWorker: true,
|
nodeIntegrationInWorker: true,
|
||||||
webSecurity: false,
|
webSecurity: false,
|
||||||
partition: 'persist:my-partition'
|
partition: 'persist:my-partition',
|
||||||
|
session: ses,
|
||||||
|
webviewTag : true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
1
src/model/book.d.ts
vendored
1
src/model/book.d.ts
vendored
@ -18,6 +18,7 @@ declare namespace Book {
|
|||||||
backgroundMusic?: string | null // 背景音乐ID
|
backgroundMusic?: string | null // 背景音乐ID
|
||||||
friendlyReminder?: string | null // 友情提示
|
friendlyReminder?: string | null // 友情提示
|
||||||
subtitlePosition?: string,
|
subtitlePosition?: string,
|
||||||
|
watermarkPosition?: string
|
||||||
updateTime?: Date,
|
updateTime?: Date,
|
||||||
createTime?: Date,
|
createTime?: Date,
|
||||||
version?: string,
|
version?: string,
|
||||||
|
|||||||
@ -61,12 +61,12 @@ const book = {
|
|||||||
Framing: async (bookId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.FRAMING, bookId),
|
Framing: async (bookId) => await ipcRenderer.invoke(DEFINE_STRING.BOOK.FRAMING, bookId),
|
||||||
|
|
||||||
// 获取文案信息
|
// 获取文案信息
|
||||||
GetCopywriting: async (bookId) =>
|
GetCopywriting: async (bookId, bookTaskId) =>
|
||||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_COPYWRITING, bookId),
|
await ipcRenderer.invoke(DEFINE_STRING.BOOK.GET_COPYWRITING, bookId, bookTaskId),
|
||||||
|
|
||||||
// 去除所有水印
|
// 去除所有水印
|
||||||
RemoveWatermark: async (bookId) =>
|
RemoveWatermark: async (id,operateBookType) =>
|
||||||
await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_WATERMARK, bookId),
|
await ipcRenderer.invoke(DEFINE_STRING.BOOK.REMOVE_WATERMARK, id,operateBookType),
|
||||||
|
|
||||||
// 添加单句推理的
|
// 添加单句推理的
|
||||||
AddReversePrompt: async (bookTaskDetailIds, type) =>
|
AddReversePrompt: async (bookTaskDetailIds, type) =>
|
||||||
|
|||||||
@ -5,7 +5,12 @@ import { Book } from '../model/book'
|
|||||||
const db = {
|
const db = {
|
||||||
//#region 小说相关的修改
|
//#region 小说相关的修改
|
||||||
|
|
||||||
// 修改小说人物的数据
|
// 修改小说数据
|
||||||
|
UpdateBookData: async (bookId: string, data: Book.SelectBook) => {
|
||||||
|
return await ipcRenderer.invoke(DEFINE_STRING.DB.UPDATE_BOOK_DATA, bookId, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改小说任务的数据
|
||||||
UpdateBookTaskData: async (bookTaskId: string, data: Book.SelectBookTask) => {
|
UpdateBookTaskData: async (bookTaskId: string, data: Book.SelectBookTask) => {
|
||||||
return await ipcRenderer.invoke(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DATA, bookTaskId, data)
|
return await ipcRenderer.invoke(DEFINE_STRING.DB.UPDATE_BOOK_TASK_DATA, bookTaskId, data)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,20 +1,30 @@
|
|||||||
import { ipcRenderer } from "electron"
|
import { ipcRenderer } from 'electron'
|
||||||
import { DEFINE_STRING } from "../define/define_string"
|
import { DEFINE_STRING } from '../define/define_string'
|
||||||
|
|
||||||
|
|
||||||
const img = {
|
const img = {
|
||||||
// 加载当前链接的SD服务数据
|
// 加载当前链接的SD服务数据
|
||||||
OneSplitFour: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.ONE_SPLIT_FOUR, value)),
|
OneSplitFour: async (value, callback) =>
|
||||||
|
callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.ONE_SPLIT_FOUR, value)),
|
||||||
|
|
||||||
// 将base64的图片转换为文件
|
// 将base64的图片转换为文件
|
||||||
Base64ToFile: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.BASE64_TO_FILE, value)),
|
Base64ToFile: async (value, callback) =>
|
||||||
|
callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.BASE64_TO_FILE, value)),
|
||||||
|
|
||||||
// 请求图片处理,去除水印
|
// 请求图片处理,去除水印
|
||||||
ProcessImage: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.PROCESS_IMAGE, value)),
|
ProcessImage: async (value, callback) =>
|
||||||
|
callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.PROCESS_IMAGE, value)),
|
||||||
|
|
||||||
//批量处理图片,去除水印
|
//批量处理图片,去除水印
|
||||||
BatchProcessImage: async (value, callback) => callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE, value)),
|
BatchProcessImage: async (value, callback) =>
|
||||||
}
|
callback(await ipcRenderer.invoke(DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE, value)),
|
||||||
export {
|
|
||||||
img
|
// 检查水印处理
|
||||||
|
ProcessImageCheck: async (imageBase64, maskPosition, bookId) =>
|
||||||
|
await ipcRenderer.invoke(
|
||||||
|
DEFINE_STRING.IMG.PROCESS_IMAGE_WATERMASK_CHECK,
|
||||||
|
imageBase64,
|
||||||
|
maskPosition,
|
||||||
|
bookId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
export { img }
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<n-message-provider>
|
<n-message-provider>
|
||||||
<n-dialog-provider>
|
<n-dialog-provider>
|
||||||
<n-notification-provider>
|
<n-notification-provider>
|
||||||
<n-spin style="z-index: 100;" :show="softwareStore.spin.spinning">
|
<n-spin style="z-index: 1000;" :show="softwareStore.spin.spinning">
|
||||||
<RouterView></RouterView>
|
<RouterView></RouterView>
|
||||||
<template #description> {{ softwareStore.spin.tip }} </template>
|
<template #description> {{ softwareStore.spin.tip }} </template>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
|
|||||||
32
src/renderer/src/components/APIService/APIIcon.vue
Normal file
32
src/renderer/src/components/APIService/APIIcon.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8 9H4a2 2 0 0 0-2 2v12h2v-5h4v5h2V11a2 2 0 0 0-2-2zm-4 7v-5h4v5z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<path d="M22 11h3v10h-3v2h8v-2h-3V11h3V9h-8v2z" fill="currentColor"></path>
|
||||||
|
<path
|
||||||
|
d="M14 23h-2V9h6a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-4zm0-7h4v-5h-4z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, onMounted, defineComponent, onUnmounted, toRaw, watch } from 'vue'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
onMounted(async () => {})
|
||||||
|
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
16
src/renderer/src/components/APIService/ApiService.vue
Normal file
16
src/renderer/src/components/APIService/ApiService.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div style="width: 100%; height: 100%">
|
||||||
|
<webview style="width: 100%; height: 100%" src="https://api.laitool.cc" partition="persist:your-session-name"></webview>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
export default defineComponent({
|
||||||
|
components: {},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -74,7 +74,12 @@
|
|||||||
>
|
>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="背景音乐" path="srtPath">
|
<n-form-item label="背景音乐" path="srtPath">
|
||||||
<n-select v-model:value="reverseManageStore.selectBook.backgroundMusic" :options="backgroundMusicOptions" placeholder="请选择背景音乐"> </n-select>
|
<n-select
|
||||||
|
v-model:value="reverseManageStore.selectBook.backgroundMusic"
|
||||||
|
:options="backgroundMusicOptions"
|
||||||
|
placeholder="请选择背景音乐"
|
||||||
|
>
|
||||||
|
</n-select>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="小说项目文件夹" path="bookFolderPath">
|
<n-form-item label="小说项目文件夹" path="bookFolderPath">
|
||||||
<n-input
|
<n-input
|
||||||
@ -99,7 +104,7 @@
|
|||||||
>
|
>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<div style="margin-left: 10px; display: flex; justify-content: flex-end">
|
<div style="margin-left: 10px; display: flex; justify-content: flex-end">
|
||||||
<n-button type="success" @click="AddOrModifyBook">{{
|
<n-button :loading="loading" type="success" @click="AddOrModifyBook">{{
|
||||||
type == 'add' ? '添加' : '保存'
|
type == 'add' ? '添加' : '保存'
|
||||||
}}</n-button>
|
}}</n-button>
|
||||||
</div>
|
</div>
|
||||||
@ -113,6 +118,7 @@ import { useMessage, NButton, NForm, NFormItem, NInput, NSelect, NIcon } from 'n
|
|||||||
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'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -128,7 +134,9 @@ export default defineComponent({
|
|||||||
setup(props) {
|
setup(props) {
|
||||||
let message = useMessage()
|
let message = useMessage()
|
||||||
let reverseManageStore = useReverseManageStore()
|
let reverseManageStore = useReverseManageStore()
|
||||||
|
let softwareStore = useSoftwareStore()
|
||||||
let bookRef = ref(null)
|
let bookRef = ref(null)
|
||||||
|
let loading = ref(false)
|
||||||
let backgroundMusicOptions = ref([])
|
let backgroundMusicOptions = ref([])
|
||||||
// let data = ref(reverseManageStore.selectBook)
|
// let data = ref(reverseManageStore.selectBook)
|
||||||
let type = ref(props.type ? props.type : 'add')
|
let type = ref(props.type ? props.type : 'add')
|
||||||
@ -220,7 +228,9 @@ export default defineComponent({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
debugger
|
debugger
|
||||||
|
loading.value = true
|
||||||
let res = await reverseManageStore.SaveSelectBook(cloneDeep(reverseManageStore.selectBook))
|
let res = await reverseManageStore.SaveSelectBook(cloneDeep(reverseManageStore.selectBook))
|
||||||
|
loading.value = false
|
||||||
// 判断当前的数据是不是有ID
|
// 判断当前的数据是不是有ID
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
message.error(res.message)
|
message.error(res.message)
|
||||||
@ -279,8 +289,9 @@ export default defineComponent({
|
|||||||
SelectMP4File,
|
SelectMP4File,
|
||||||
AddOrModifyBook,
|
AddOrModifyBook,
|
||||||
bookRef,
|
bookRef,
|
||||||
|
softwareStore,
|
||||||
OpenFolder,
|
OpenFolder,
|
||||||
backgroundMusicOptions
|
backgroundMusicOptions,loading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
<n-divider vertical style="height: 30px" />
|
<n-divider vertical style="height: 30px" />
|
||||||
<n-dropdown trigger="hover" :options="frameOptions" @select="handleSelect">
|
<n-dropdown trigger="hover" :options="frameOptions" @select="handleSelect">
|
||||||
<n-button :color="softwareStore.SoftColor.ORANGE" @click=""> 开始分镜 </n-button>
|
<n-button :color="softwareStore.SoftColor.ORANGE" @click="ActionFrame"> 开始分镜 </n-button>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
<n-dropdown trigger="hover" :options="copywritingOptions" @select="handleSelect">
|
<n-dropdown trigger="hover" :options="copywritingOptions" @select="handleSelect">
|
||||||
<n-button
|
<n-button
|
||||||
@ -102,18 +102,20 @@ import VideoCanvas from '../../VideoSubtitle/VideoCanvas.vue'
|
|||||||
import { SubtitleSavePositionType } from '../../../../../define/enum/waterMarkAndSubtitle'
|
import { SubtitleSavePositionType } from '../../../../../define/enum/waterMarkAndSubtitle'
|
||||||
import { DEFINE_STRING } from '../../../../../define/define_string'
|
import { DEFINE_STRING } from '../../../../../define/define_string'
|
||||||
import Setting from './Setting.vue'
|
import Setting from './Setting.vue'
|
||||||
import { BookType } from '../../../../../define/enum/bookEnum'
|
import { BookType, OperateBookType } from '../../../../../define/enum/bookEnum'
|
||||||
import TranslateSetting from '../../Setting/TranslateSetting.vue'
|
import TranslateSetting from '../../Setting/TranslateSetting.vue'
|
||||||
import { TranslateType } from '../../../../../define/enum/translate'
|
import { TranslateType } from '../../../../../define/enum/translate'
|
||||||
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common'
|
import { ContainsChineseOrPunctuation } from '../../../../../define/Tools/common'
|
||||||
import ImportWordAndSrt from '../../Original/Components/ImportWordAndSrt.vue'
|
import ImportWordAndSrt from '../../Original/Components/ImportWordAndSrt.vue'
|
||||||
|
import GetWaterMaskRectangle from '../../Watermark/GetWaterMaskRectangle.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
NButton,
|
NButton,
|
||||||
NCheckbox,
|
NCheckbox,
|
||||||
NDropdown,
|
NDropdown,
|
||||||
NDivider
|
NDivider,
|
||||||
|
GetWaterMaskRectangle
|
||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
@ -268,7 +270,10 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let res_frame = await window.book.RemoveWatermark(reverseManageStore.selectBook.id)
|
let res_frame = await window.book.RemoveWatermark(
|
||||||
|
reverseManageStore.selectBookTask.id,
|
||||||
|
OperateBookType.BOOKTASK
|
||||||
|
)
|
||||||
softwareStore.spin.spinning = false
|
softwareStore.spin.spinning = false
|
||||||
window.api.showGlobalMessageDialog(res_frame)
|
window.api.showGlobalMessageDialog(res_frame)
|
||||||
}
|
}
|
||||||
@ -292,21 +297,45 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取水印位置
|
||||||
|
*/
|
||||||
|
async function GetWatermarkPosition() {
|
||||||
|
// 开始获取水印位置
|
||||||
|
// 判断当前数据是不是存在
|
||||||
|
// 处理数据。获取当前的所有的数据
|
||||||
|
let dialogWidth = window.innerWidth * 0.7
|
||||||
|
let dialogHeight = window.innerHeight * 0.9
|
||||||
|
// ImportWordAndSrt
|
||||||
|
dialog.create({
|
||||||
|
showIcon: false,
|
||||||
|
closeOnEsc: false,
|
||||||
|
content: () => h(GetWaterMaskRectangle, { width: dialogWidth, height: dialogHeight }),
|
||||||
|
style: `width : ${dialogWidth}px; height : ${dialogHeight}px`,
|
||||||
|
maskClosable: false,
|
||||||
|
onClose: () => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一些选择方式
|
||||||
async function handleSelect(key) {
|
async function handleSelect(key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'compute_frame':
|
case 'compute_frame': // 计算分镜
|
||||||
await ComputeStoryboard()
|
await ComputeStoryboard()
|
||||||
break
|
break
|
||||||
case 'framing':
|
case 'framing': // 开始分镜
|
||||||
await Framing()
|
await Framing()
|
||||||
break
|
break
|
||||||
case 'recognizing_setting':
|
case 'recognizing_setting': // 文案位置设置
|
||||||
await GetCopywritingSetting()
|
await GetCopywritingSetting()
|
||||||
break
|
break
|
||||||
case 'mj_reverse':
|
case 'watermark_position': // 水印位置设置
|
||||||
|
await GetWatermarkPosition()
|
||||||
|
break
|
||||||
|
case 'mj_reverse': // MJ反推
|
||||||
await ImageReversePrompt(BookType.MJ_REVERSE)
|
await ImageReversePrompt(BookType.MJ_REVERSE)
|
||||||
break
|
break
|
||||||
case 'sd_reverse':
|
case 'sd_reverse': // SD反推
|
||||||
await ImageReversePrompt(BookType.SD_REVERSE)
|
await ImageReversePrompt(BookType.SD_REVERSE)
|
||||||
break
|
break
|
||||||
case 'remove_reverse':
|
case 'remove_reverse':
|
||||||
@ -570,12 +599,17 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ActionFrame() {
|
||||||
|
message.info('请使用下面菜单中的按钮进行对应的操作')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
RetuenMain,
|
RetuenMain,
|
||||||
AutoAction,
|
AutoAction,
|
||||||
ImportWordAndSrtFunc,
|
ImportWordAndSrtFunc,
|
||||||
reverseManageStore,
|
reverseManageStore,
|
||||||
softwareStore,
|
softwareStore,
|
||||||
|
ActionFrame,
|
||||||
switchLogger,
|
switchLogger,
|
||||||
ComputeStoryboard,
|
ComputeStoryboard,
|
||||||
OpenSettingDialog,
|
OpenSettingDialog,
|
||||||
@ -606,7 +640,7 @@ export default defineComponent({
|
|||||||
],
|
],
|
||||||
copywritingOptions: [
|
copywritingOptions: [
|
||||||
{
|
{
|
||||||
label: '提取文案设置',
|
label: '提取文案位置',
|
||||||
key: 'recognizing_setting'
|
key: 'recognizing_setting'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -616,8 +650,8 @@ export default defineComponent({
|
|||||||
],
|
],
|
||||||
watermarkOptions: [
|
watermarkOptions: [
|
||||||
{
|
{
|
||||||
label: '去除水印设置',
|
label: '选择水印位置',
|
||||||
key: 'watermark_setting'
|
key: 'watermark_position'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '停止去除',
|
label: '停止去除',
|
||||||
|
|||||||
@ -52,14 +52,14 @@ import {
|
|||||||
DuplicateOutline,
|
DuplicateOutline,
|
||||||
GridOutline,
|
GridOutline,
|
||||||
RadioOutline,
|
RadioOutline,
|
||||||
BookOutline
|
BookOutline,
|
||||||
} from '@vicons/ionicons5'
|
} from '@vicons/ionicons5'
|
||||||
import CheckMachineId from '../Components/CheckMachineId.vue'
|
import CheckMachineId from '../Components/CheckMachineId.vue'
|
||||||
import axios from 'axios'
|
|
||||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||||
import ShowMessage from './ShowMessage.vue'
|
import ShowMessage from './ShowMessage.vue'
|
||||||
import { MD5 } from 'crypto-js'
|
import { MD5 } from 'crypto-js'
|
||||||
import InputDialogContent from '../Original/Components/InputDialogContent.vue'
|
import InputDialogContent from '../Original/Components/InputDialogContent.vue'
|
||||||
|
import APIIcon from '../APIService/APIIcon.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -71,7 +71,8 @@ export default defineComponent({
|
|||||||
NIcon,
|
NIcon,
|
||||||
ShowMessage,
|
ShowMessage,
|
||||||
NSwitch,
|
NSwitch,
|
||||||
NButton
|
NButton,
|
||||||
|
APIIcon
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
let collapsed = ref(false)
|
let collapsed = ref(false)
|
||||||
@ -89,6 +90,7 @@ export default defineComponent({
|
|||||||
if (option.key == 'setting') return h(NIcon, null, { default: () => h(SettingsOutline) })
|
if (option.key == 'setting') return h(NIcon, null, { default: () => h(SettingsOutline) })
|
||||||
if (option.key == 'gptCopywriting') return h(NIcon, null, { default: () => h(BookOutline) })
|
if (option.key == 'gptCopywriting') return h(NIcon, null, { default: () => h(BookOutline) })
|
||||||
if (option.key == 'book_management') return h(NIcon, null, { default: () => h(GridOutline) })
|
if (option.key == 'book_management') return h(NIcon, null, { default: () => h(GridOutline) })
|
||||||
|
if (option.key == 'lai_api') return h(NIcon, null, { default: () => h(APIIcon) })
|
||||||
if (option.key == 'backward_matrix')
|
if (option.key == 'backward_matrix')
|
||||||
return h(NIcon, null, { default: () => h(DuplicateOutline) })
|
return h(NIcon, null, { default: () => h(DuplicateOutline) })
|
||||||
if (option.key == 'TTS_Services') return h(NIcon, null, { default: () => h(RadioOutline) })
|
if (option.key == 'TTS_Services') return h(NIcon, null, { default: () => h(RadioOutline) })
|
||||||
@ -452,21 +454,21 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// label: () =>
|
label: () =>
|
||||||
// h(
|
h(
|
||||||
// RouterLink,
|
RouterLink,
|
||||||
// {
|
{
|
||||||
// to: {
|
to: {
|
||||||
// name: 'test_options'
|
name: 'lai_api'
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// default: () => '测试操作'
|
default: () => 'API服务'
|
||||||
// }
|
}
|
||||||
// ),
|
),
|
||||||
// key: 'test_options'
|
key: 'lai_api'
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
label: () =>
|
label: () =>
|
||||||
h(
|
h(
|
||||||
|
|||||||
@ -3,16 +3,26 @@
|
|||||||
<div style="width: 500px; position: relative">
|
<div style="width: 500px; position: relative">
|
||||||
<n-form ref="formRef" label-placement="top" :model="characterData" :rules="rules">
|
<n-form ref="formRef" label-placement="top" :model="characterData" :rules="rules">
|
||||||
<n-form-item label="人物名称" path="label">
|
<n-form-item label="人物名称" path="label">
|
||||||
<n-input style="width: 400px;" v-model:value="characterData.label" placeholder="请输入人物名称" />
|
<n-input
|
||||||
|
style="width: 400px"
|
||||||
|
v-model:value="characterData.label"
|
||||||
|
placeholder="请输入人物名称"
|
||||||
|
/>
|
||||||
<n-checkbox style="margin-left: 20px" v-model:checked="characterData.isShow"
|
<n-checkbox style="margin-left: 20px" v-model:checked="characterData.isShow"
|
||||||
>显示</n-checkbox
|
>显示</n-checkbox
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="position: absolute; left: 520px; top: 0; display: flex; flex-direction: column"
|
||||||
>
|
>
|
||||||
<n-image
|
<n-image
|
||||||
style="position: absolute; left: 520px; top: 0"
|
|
||||||
width="120"
|
width="120"
|
||||||
height="120"
|
height="120"
|
||||||
:src="png_base64 ? png_base64 : characterData.show_image"
|
:src="png_base64 ? png_base64 : characterData.show_image"
|
||||||
/>
|
/>
|
||||||
|
<n-button color="#e18a3b" style="margin-top: 5px" size="tiny" @click="SelecImage"
|
||||||
|
>本地上传图片</n-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="人物别名(可多个)">
|
<n-form-item label="人物别名(可多个)">
|
||||||
<n-dynamic-tags type="success" v-model:value="alias_tags" />
|
<n-dynamic-tags type="success" v-model:value="alias_tags" />
|
||||||
@ -36,7 +46,7 @@
|
|||||||
style="margin-top: 10px"
|
style="margin-top: 10px"
|
||||||
@click="GenerateCharacterImage"
|
@click="GenerateCharacterImage"
|
||||||
:loading="imageLoading"
|
:loading="imageLoading"
|
||||||
>生成图片</n-button
|
>SD生成图片</n-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
@ -188,7 +198,6 @@ export default defineComponent({
|
|||||||
await window.mj.SaveTagPropertyData(
|
await window.mj.SaveTagPropertyData(
|
||||||
[JSON.stringify(characterData.value), 'character_tags'],
|
[JSON.stringify(characterData.value), 'character_tags'],
|
||||||
(value) => {
|
(value) => {
|
||||||
|
|
||||||
console.log(value)
|
console.log(value)
|
||||||
if (value.code == 0) {
|
if (value.code == 0) {
|
||||||
message.error(value.message)
|
message.error(value.message)
|
||||||
@ -280,7 +289,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
imageLoading.value = true
|
imageLoading.value = true
|
||||||
await window.sd.txt2img(JSON.stringify([d]), async (value) => {
|
await window.sd.txt2img(JSON.stringify([d]), async (value) => {
|
||||||
|
|
||||||
if (value.code == 0) {
|
if (value.code == 0) {
|
||||||
message.error(value.message)
|
message.error(value.message)
|
||||||
imageLoading.value = false
|
imageLoading.value = false
|
||||||
@ -298,8 +306,40 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择本地图片
|
||||||
|
*/
|
||||||
|
async function SelecImage() {
|
||||||
|
await window.api.SelectFile(
|
||||||
|
[
|
||||||
|
'jpeg',
|
||||||
|
'jpg',
|
||||||
|
'png',
|
||||||
|
'gif',
|
||||||
|
'bmp',
|
||||||
|
'tiff',
|
||||||
|
'tif',
|
||||||
|
'webp',
|
||||||
|
'svg',
|
||||||
|
'raw',
|
||||||
|
'cr2',
|
||||||
|
'nef',
|
||||||
|
'arw'
|
||||||
|
],
|
||||||
|
(value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
png_base64.value = null
|
||||||
|
characterData.value.show_image = value.value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
characterData,
|
characterData,
|
||||||
|
SelecImage,
|
||||||
alias_tags,
|
alias_tags,
|
||||||
SaveCharacterTag,
|
SaveCharacterTag,
|
||||||
TranslatePrompt,
|
TranslatePrompt,
|
||||||
|
|||||||
@ -3,12 +3,12 @@
|
|||||||
<n-form label-placement="top" ref="formRef" :rules="rules" :model="styleData">
|
<n-form label-placement="top" ref="formRef" :rules="rules" :model="styleData">
|
||||||
<n-form-item label="风格名称" path="label">
|
<n-form-item label="风格名称" path="label">
|
||||||
<n-input v-model:value="styleData.label" placeholder="请输入风格名称" />
|
<n-input v-model:value="styleData.label" placeholder="请输入风格名称" />
|
||||||
<n-image
|
<div style="position: absolute; left: 520px; top: 0; display: flex; flex-direction: column">
|
||||||
style="position: absolute; left: 520px; top: 0"
|
<n-image width="120" height="120" :src="png_base64 ? png_base64 : styleData.show_image" />
|
||||||
width="120"
|
<n-button color="#e18a3b" style="margin-top: 5px" size="tiny" @click="SelecImage"
|
||||||
height="120"
|
>本地上传图片</n-button
|
||||||
:src="png_base64 ? png_base64 : styleData.show_image"
|
>
|
||||||
/>
|
</div>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="风格提示词描述(中文)">
|
<n-form-item label="风格提示词描述(中文)">
|
||||||
<n-input
|
<n-input
|
||||||
@ -29,7 +29,7 @@
|
|||||||
style="margin-top: 10px"
|
style="margin-top: 10px"
|
||||||
@click="GenerateStyleImage"
|
@click="GenerateStyleImage"
|
||||||
:loading="imageLoading"
|
:loading="imageLoading"
|
||||||
>生成图片</n-button
|
>SD生成图片</n-button
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
使用SD出图模式生成风格图片,提示词为1gril加上风格提示词
|
使用SD出图模式生成风格图片,提示词为1gril加上风格提示词
|
||||||
@ -149,6 +149,9 @@ export default defineComponent({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
styleData.value['type'] = 'style_main'
|
styleData.value['type'] = 'style_main'
|
||||||
|
styleData.value['show_image'] = styleData.value.show_image
|
||||||
|
? styleData.value.show_image.split('?t')[0]
|
||||||
|
: null
|
||||||
// 直接保存
|
// 直接保存
|
||||||
// 开始保存
|
// 开始保存
|
||||||
await window.mj.SaveTagPropertyData(
|
await window.mj.SaveTagPropertyData(
|
||||||
@ -244,6 +247,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
imageLoading.value = true
|
imageLoading.value = true
|
||||||
await window.sd.txt2img(JSON.stringify([d]), async (value) => {
|
await window.sd.txt2img(JSON.stringify([d]), async (value) => {
|
||||||
|
debugger
|
||||||
if (value.code == 0) {
|
if (value.code == 0) {
|
||||||
message.error(value.message)
|
message.error(value.message)
|
||||||
imageLoading.value = false
|
imageLoading.value = false
|
||||||
@ -251,7 +255,8 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
if (value.data && value.data.length > 0) {
|
if (value.data && value.data.length > 0) {
|
||||||
let d = value.data[0]
|
let d = value.data[0]
|
||||||
styleData.value.show_image = d.image_path.replaceAll('\\', '/')
|
styleData.value.show_image =
|
||||||
|
d.image_path.replaceAll('\\', '/') + '?t=' + new Date().getTime()
|
||||||
png_base64.value = 'data:image/png;base64,' + d.base64
|
png_base64.value = 'data:image/png;base64,' + d.base64
|
||||||
console.log(styleData.value.show_image)
|
console.log(styleData.value.show_image)
|
||||||
imageLoading.value = false
|
imageLoading.value = false
|
||||||
@ -261,8 +266,52 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择本地的图片应用到风格图片
|
||||||
|
*/
|
||||||
|
async function SelecImage() {
|
||||||
|
await window.api.SelectFile(
|
||||||
|
[
|
||||||
|
'jpeg',
|
||||||
|
'jpg',
|
||||||
|
'png',
|
||||||
|
'gif',
|
||||||
|
'bmp',
|
||||||
|
'tiff',
|
||||||
|
'tif',
|
||||||
|
'webp',
|
||||||
|
'svg',
|
||||||
|
'raw',
|
||||||
|
'cr2',
|
||||||
|
'nef',
|
||||||
|
'arw'
|
||||||
|
],
|
||||||
|
(value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 读取文件并转换为Base64
|
||||||
|
fetch(value.value)
|
||||||
|
.then((response) => response.blob())
|
||||||
|
.then((blob) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onloadend = () => {
|
||||||
|
png_base64.value = reader.result
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(blob)
|
||||||
|
})
|
||||||
|
// 更新显示的图片
|
||||||
|
|
||||||
|
png_base64.value = null
|
||||||
|
styleData.value.show_image = value.value + '?t=' + new Date().getTime()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
styleData,
|
styleData,
|
||||||
|
SelecImage,
|
||||||
SaveStyleTag,
|
SaveStyleTag,
|
||||||
loading,
|
loading,
|
||||||
imageLoading,
|
imageLoading,
|
||||||
|
|||||||
@ -83,6 +83,7 @@
|
|||||||
fit="cover"
|
fit="cover"
|
||||||
width="120"
|
width="120"
|
||||||
height="120"
|
height="120"
|
||||||
|
lazy
|
||||||
/>
|
/>
|
||||||
<n-button
|
<n-button
|
||||||
text
|
text
|
||||||
|
|||||||
@ -760,7 +760,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 筛选下面被锁定的数据
|
// 筛选下面被锁定的数据
|
||||||
tempData = tempData.filter((item) => item.imageLock == false)
|
tempData = tempData.filter((item) => item.imageLock == null || item.imageLock == false)
|
||||||
|
|
||||||
if (tempData.length <= 0) {
|
if (tempData.length <= 0) {
|
||||||
message.error('下面的数据都被锁定,无法生图,请检查')
|
message.error('下面的数据都被锁定,无法生图,请检查')
|
||||||
|
|||||||
@ -220,10 +220,10 @@ export default defineComponent({
|
|||||||
* 生成所有的图片
|
* 生成所有的图片
|
||||||
*/
|
*/
|
||||||
async function GenerateImageAll() {
|
async function GenerateImageAll() {
|
||||||
|
debugger
|
||||||
let tmpData = cloneDeep(toRaw(data.value))
|
let tmpData = cloneDeep(toRaw(data.value))
|
||||||
tmpData = tmpData.filter((item) => {
|
tmpData = tmpData.filter((item) => {
|
||||||
// return item.outImagePath == null || item.outImagePath == ''
|
return item.imageLock == null || item.imageLock == false
|
||||||
return !item.imageLoak
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (tmpData.length == 0) {
|
if (tmpData.length == 0) {
|
||||||
@ -280,12 +280,24 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置GPT提示词
|
||||||
|
async function ResetGPTPrompt() {
|
||||||
|
for (let i = 0; i < data.value.length; i++) {
|
||||||
|
data.value[i].gpt_prompt = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 图片数据重置
|
// 图片数据重置
|
||||||
async function ResetImage() {
|
async function ResetImage() {
|
||||||
// 循环data删除数据
|
// 循环data删除数据
|
||||||
for (let i = 0; i < data.value.length; i++) {
|
for (let i = 0; i < data.value.length; i++) {
|
||||||
data.value[i].outImagePath = null
|
data.value[i].outImagePath = null
|
||||||
data.value[i].subImagePath = []
|
data.value[i].subImagePath = []
|
||||||
|
data.value[i].imageLock = false
|
||||||
|
// 判断是不是有mj_message,有的话删除
|
||||||
|
if (data.value[i].mj_message) {
|
||||||
|
data.value[i].mj_message = undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,6 +345,9 @@ export default defineComponent({
|
|||||||
await ResetPrompt()
|
await ResetPrompt()
|
||||||
} else if (value == 'resetImage') {
|
} else if (value == 'resetImage') {
|
||||||
await ResetImage()
|
await ResetImage()
|
||||||
|
} else if (value == 'resetGPTPrompt') {
|
||||||
|
// 重置GPT提示词
|
||||||
|
await ResetGPTPrompt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,6 +355,7 @@ export default defineComponent({
|
|||||||
async function resetALLData() {
|
async function resetALLData() {
|
||||||
await ResetPrompt()
|
await ResetPrompt()
|
||||||
await ResetImage()
|
await ResetImage()
|
||||||
|
await ResetGPTPrompt()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导入提示词
|
// 导入提示词
|
||||||
@ -408,6 +424,7 @@ export default defineComponent({
|
|||||||
GptButtonOptions: [{ label: '停止推理任务', key: 'stopGptPrompt' }],
|
GptButtonOptions: [{ label: '停止推理任务', key: 'stopGptPrompt' }],
|
||||||
GenerateImageOptions: [{ label: '停止生成图片任务', key: 'stopGenerateImage' }],
|
GenerateImageOptions: [{ label: '停止生成图片任务', key: 'stopGenerateImage' }],
|
||||||
ResetDataOptions: [
|
ResetDataOptions: [
|
||||||
|
{ label: '重置GPT提示词', key: 'resetGPTPrompt' },
|
||||||
{ label: '重置提示词', key: 'resetPrompt' },
|
{ label: '重置提示词', key: 'resetPrompt' },
|
||||||
{ label: '重置图片', key: 'resetImage' }
|
{ label: '重置图片', key: 'resetImage' }
|
||||||
],
|
],
|
||||||
|
|||||||
531
src/renderer/src/components/Watermark/GetWaterMaskRectangle.vue
Normal file
531
src/renderer/src/components/Watermark/GetWaterMaskRectangle.vue
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
<template>
|
||||||
|
<div style="margin-top: 30px">
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
"
|
||||||
|
id="cnvas_ss"
|
||||||
|
>
|
||||||
|
<canvas ref="imageCanvas" />
|
||||||
|
<canvas
|
||||||
|
ref="tempImageCanvas"
|
||||||
|
@mousemove="draw"
|
||||||
|
@mouseup="stopDrawing"
|
||||||
|
@mousedown="startDrawing"
|
||||||
|
style="position: absolute; top: 1; left: 0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 10px; display: flex; justify-content: center; align-items: center">
|
||||||
|
<n-button style="margin-left: 10px" @click="undo" type="success">回撤</n-button>
|
||||||
|
<n-button style="margin-left: 10px" @click="Check" type="success">查看去水印效果</n-button>
|
||||||
|
<n-button style="margin-left: 10px" @click="SaveMask" type="success">保存蒙板位置</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, ref, onMounted, h, watch, toRaw } from 'vue'
|
||||||
|
import {
|
||||||
|
NButton,
|
||||||
|
useMessage,
|
||||||
|
NInput,
|
||||||
|
NTag,
|
||||||
|
NImage,
|
||||||
|
NCheckbox,
|
||||||
|
NSlider,
|
||||||
|
NPopover,
|
||||||
|
NProgress,
|
||||||
|
useDialog
|
||||||
|
} from 'naive-ui'
|
||||||
|
import ImageFileSelect from './ImageFileSelect.vue'
|
||||||
|
import ProgressDialog from '../Components/ProgressDialog.vue'
|
||||||
|
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||||
|
import { cloneDeep, isEmpty } from 'lodash'
|
||||||
|
import { useReverseManageStore } from '../../../../stores/reverseManage'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
NButton,
|
||||||
|
NInput,
|
||||||
|
NTag,
|
||||||
|
ImageFileSelect,
|
||||||
|
NImage,
|
||||||
|
NCheckbox,
|
||||||
|
NSlider,
|
||||||
|
NPopover,
|
||||||
|
NProgress
|
||||||
|
},
|
||||||
|
props: ['width', 'height'],
|
||||||
|
setup(props) {
|
||||||
|
let message = useMessage()
|
||||||
|
let dialog = useDialog()
|
||||||
|
const imageCanvas = ref(null)
|
||||||
|
let tempImageCanvas = ref(null)
|
||||||
|
let imageContext = ref(null)
|
||||||
|
let reverseManageStore = useReverseManageStore()
|
||||||
|
|
||||||
|
let height = ref(props.height)
|
||||||
|
let width = ref(props.width)
|
||||||
|
let rectPos = ref(null)
|
||||||
|
let drawing = ref(false)
|
||||||
|
let moving = ref(false)
|
||||||
|
let startPos = ref(null)
|
||||||
|
let offset = ref({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
let isDrawing = false
|
||||||
|
let radius = ref(20) // 圆的半径
|
||||||
|
let canvasHistory = [] // 用于保存画布的历史记录(存放位置信息)
|
||||||
|
let step = 0
|
||||||
|
let mask_setting = ref({
|
||||||
|
isRemote: true,
|
||||||
|
localUrl: null,
|
||||||
|
mask_path: null
|
||||||
|
})
|
||||||
|
let progressDialogRef = ref(null)
|
||||||
|
let total = ref(0)
|
||||||
|
let current = ref(0)
|
||||||
|
let dp
|
||||||
|
let image = ref(null)
|
||||||
|
let initImage = null
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (imageCanvas.value) {
|
||||||
|
const ctx = imageCanvas.value.getContext('2d', { willReadFrequently: true })
|
||||||
|
if (ctx) {
|
||||||
|
imageContext.value = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let div = document.getElementById('cnvas_ss')
|
||||||
|
div.style.height = height.value - 120 + 'px'
|
||||||
|
|
||||||
|
await window.api.GetDefineConfigJsonByProperty(
|
||||||
|
JSON.stringify(['img_base', 'mask_setting', false, null]),
|
||||||
|
(value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 判断是不是有数据
|
||||||
|
if (value.data) {
|
||||||
|
mask_setting.value = Object.assign(mask_setting.value, value.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
window.api.setEventListen([DEFINE_STRING.IMG.BATCH_PROCESS_IMAGE_RESULT], (value) => {
|
||||||
|
if (value.code == 0) {
|
||||||
|
message.error(value.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total.value = value.data.total
|
||||||
|
current.value = value.data.current
|
||||||
|
if (progressDialogRef.value == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
progressDialogRef.value?.modifyTotal(total.value)
|
||||||
|
progressDialogRef.value?.modifyCurrent(current.value)
|
||||||
|
|
||||||
|
if (value.data.total == value.data.current) {
|
||||||
|
// 销毁进度组件
|
||||||
|
setTimeout(() => {
|
||||||
|
if (dp) {
|
||||||
|
dp.destroy()
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载图片
|
||||||
|
await loadFile(reverseManageStore.selectBookTaskDetail[0].oldImage)
|
||||||
|
})
|
||||||
|
|
||||||
|
let hasImage = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图片的高度并且计算适应的宽高
|
||||||
|
* @param image
|
||||||
|
*/
|
||||||
|
function getCurrentWidthHeight(image) {
|
||||||
|
let image_width = 512
|
||||||
|
let image_height = 512
|
||||||
|
// 设置图片的大小
|
||||||
|
if (image) {
|
||||||
|
image_width = image.width
|
||||||
|
image_height = image.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断图片大小,获取当前的传入的window,判断当前的宽度和高度是不是超出了
|
||||||
|
// let min_size = Math.min(width.value,height.value);
|
||||||
|
let image_height_scale = (height.value - 120) / image_height
|
||||||
|
let image_width_scale = (width.value - 120) / image_width
|
||||||
|
|
||||||
|
let scale = Math.min(image_height_scale, image_width_scale)
|
||||||
|
return [image_width * scale, image_height * scale]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImage(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = () => {
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => resolve(img)
|
||||||
|
img.onerror = reject
|
||||||
|
img.src = reader.result
|
||||||
|
}
|
||||||
|
reader.onerror = (error) => reject(error)
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadFile(fileUrl) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(fileUrl)
|
||||||
|
const blob = await response.blob()
|
||||||
|
const file = new File([blob], 'file.jpg', { type: blob.type })
|
||||||
|
const img = await getImage(file)
|
||||||
|
image.value = img
|
||||||
|
modifyImage(img)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading file:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择图片初始化
|
||||||
|
* @param image_d
|
||||||
|
*/
|
||||||
|
function modifyImage(image_d) {
|
||||||
|
let [image_width, image_height] = getCurrentWidthHeight(image_d)
|
||||||
|
imageContext.value.canvas.width = image_width
|
||||||
|
imageContext.value.canvas.height = image_height
|
||||||
|
imageContext.value.drawImage(image_d, 0, 0, image_width, image_height)
|
||||||
|
// canvasHistory.push(imageCanvas.value.toDataURL('image/png'))
|
||||||
|
hasImage = true
|
||||||
|
let w = (width.value - image_width - 56) / 2
|
||||||
|
tempImageCanvas.value.style.left = w + 'px'
|
||||||
|
tempImageCanvas.value.width = imageCanvas.value.width
|
||||||
|
tempImageCanvas.value.height = imageCanvas.value.height
|
||||||
|
tempImageCanvas.value.style.zIndex = 100
|
||||||
|
if (!initImage) {
|
||||||
|
initImage = imageCanvas.value.toDataURL('image/png')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canvas绘制矩形
|
||||||
|
* @param rect
|
||||||
|
*/
|
||||||
|
const drawRectangle = (canvas, rect, clear = true) => {
|
||||||
|
const ctx = canvas.value.getContext('2d')
|
||||||
|
if (clear) {
|
||||||
|
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height) // 清除之前的绘制
|
||||||
|
}
|
||||||
|
ctx.strokeStyle = 'red' // 设置矩形颜色
|
||||||
|
ctx.lineWidth = 2 // 设置线宽
|
||||||
|
ctx.strokeRect(rect.startX, rect.startY, rect.width, rect.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearCanvas = (canvas) => {
|
||||||
|
const c = canvas.value
|
||||||
|
const context = c.getContext('2d')
|
||||||
|
context.clearRect(0, 0, c.width, c.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前鼠标在canvas中的相对位置
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
function getCanvasRelativePosition(e) {
|
||||||
|
const rect = tempImageCanvas.value.getBoundingClientRect()
|
||||||
|
return {
|
||||||
|
x: e.clientX - rect.left,
|
||||||
|
y: e.clientY - rect.top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止画
|
||||||
|
*/
|
||||||
|
function stopDrawing(e) {
|
||||||
|
// 获取矩形
|
||||||
|
if (drawing.value) {
|
||||||
|
const pos = getCanvasRelativePosition(e)
|
||||||
|
rectPos.value = {
|
||||||
|
startX: Math.min(startPos.value.x, pos.x),
|
||||||
|
startY: Math.min(startPos.value.y, pos.y),
|
||||||
|
width: Math.abs(startPos.value.x - pos.x),
|
||||||
|
height: Math.abs(startPos.value.y - pos.y),
|
||||||
|
imageWidth: tempImageCanvas.value.width,
|
||||||
|
imageHeight: tempImageCanvas.value.height
|
||||||
|
}
|
||||||
|
drawRectangle(tempImageCanvas, rectPos.value)
|
||||||
|
|
||||||
|
// 将当前的矩形画到imageCanvas上
|
||||||
|
drawRectangle(imageCanvas, rectPos.value, false)
|
||||||
|
isDrawing = false
|
||||||
|
// canvasHistory.push(imageCanvas.value.toDataURL('image/png'))
|
||||||
|
canvasHistory.push(rectPos.value)
|
||||||
|
console.log(canvasHistory)
|
||||||
|
step++
|
||||||
|
startPos.value = null
|
||||||
|
drawing.value = false
|
||||||
|
}
|
||||||
|
if (moving.value) {
|
||||||
|
moving.value = false
|
||||||
|
// 将当前的矩形画到imageCanvas上
|
||||||
|
drawRectangle(imageCanvas, rectPos.value, false)
|
||||||
|
// canvasHistory.push(imageCanvas.value.toDataURL('image/png'))
|
||||||
|
canvasHistory.push(rectPos.value)
|
||||||
|
}
|
||||||
|
clearCanvas(tempImageCanvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充颜色
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
function draw(e) {
|
||||||
|
if (drawing.value && startPos.value) {
|
||||||
|
const currentPos = getCanvasRelativePosition(e)
|
||||||
|
const rect = {
|
||||||
|
startX: Math.min(startPos.value.x, currentPos.x),
|
||||||
|
startY: Math.min(startPos.value.y, currentPos.y),
|
||||||
|
width: Math.abs(startPos.value.x - currentPos.x),
|
||||||
|
height: Math.abs(startPos.value.y - currentPos.y)
|
||||||
|
}
|
||||||
|
drawRectangle(tempImageCanvas, rect)
|
||||||
|
} else if (moving.value && rectPos.value) {
|
||||||
|
const currentPos = getCanvasRelativePosition(e)
|
||||||
|
const newRectPos = {
|
||||||
|
startX: currentPos.x - offset.value.x,
|
||||||
|
startY: currentPos.y - offset.value.y,
|
||||||
|
width: rectPos.value.width,
|
||||||
|
height: rectPos.value.height,
|
||||||
|
imageWidth: tempImageCanvas.value.width,
|
||||||
|
imageHeight: tempImageCanvas.value.height
|
||||||
|
}
|
||||||
|
rectPos.value = newRectPos
|
||||||
|
drawRectangle(tempImageCanvas, newRectPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始话
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
function startDrawing(e) {
|
||||||
|
// 判断是移动还是绘制
|
||||||
|
const pos = getCanvasRelativePosition(e)
|
||||||
|
if (
|
||||||
|
rectPos.value &&
|
||||||
|
pos.x >= rectPos.value.startX &&
|
||||||
|
pos.x <= rectPos.value.startX + rectPos.value.width &&
|
||||||
|
pos.y >= rectPos.value.startY &&
|
||||||
|
pos.y <= rectPos.value.startY + rectPos.value.height
|
||||||
|
) {
|
||||||
|
moving.value = true
|
||||||
|
offset.value = { x: pos.x - rectPos.value.startX, y: pos.y - rectPos.value.startY }
|
||||||
|
} else {
|
||||||
|
startPos.value = pos
|
||||||
|
drawing.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回撤
|
||||||
|
*/
|
||||||
|
async function undo() {
|
||||||
|
debugger
|
||||||
|
if (step <= 0) {
|
||||||
|
// 如果没有可以撤销的步骤,就清空画布
|
||||||
|
imageContext.value.clearRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
imageContext.value.canvas.width,
|
||||||
|
imageContext.value.canvas.height
|
||||||
|
)
|
||||||
|
canvasHistory = []
|
||||||
|
} else {
|
||||||
|
step--
|
||||||
|
clearCanvas(imageCanvas)
|
||||||
|
modifyImage(image.value)
|
||||||
|
canvasHistory.pop()
|
||||||
|
// 重新绘制矩形
|
||||||
|
for (let i = 0; i < canvasHistory.length; i++) {
|
||||||
|
const element = canvasHistory[i]
|
||||||
|
drawRectangle(imageCanvas, element, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前的蒙板
|
||||||
|
*/
|
||||||
|
function getCanvasMaskBase64() {
|
||||||
|
// 处理最后的图片
|
||||||
|
const imageData = imageContext.value.getImageData(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
imageContext.value.canvas.width,
|
||||||
|
imageContext.value.canvas.height
|
||||||
|
)
|
||||||
|
const newImageData = imageContext.value.createImageData(imageData.width, imageData.height)
|
||||||
|
|
||||||
|
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||||
|
if (
|
||||||
|
imageData.data[i] === 255 &&
|
||||||
|
imageData.data[i + 1] === 255 &&
|
||||||
|
imageData.data[i + 2] === 0
|
||||||
|
) {
|
||||||
|
// 如果这个像素点的颜色是 rgba(255, 255, 0, 255),就将它设置为白色
|
||||||
|
newImageData.data[i] = newImageData.data[i + 1] = newImageData.data[i + 2] = 255
|
||||||
|
newImageData.data[i + 3] = 255
|
||||||
|
} else {
|
||||||
|
// 否则,就将它设置为黑色
|
||||||
|
newImageData.data[i] = newImageData.data[i + 1] = newImageData.data[i + 2] = 0
|
||||||
|
newImageData.data[i + 3] = 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 创建一个新的 Canvas 对象
|
||||||
|
const newCanvas = document.createElement('canvas')
|
||||||
|
newCanvas.width = imageData.width
|
||||||
|
newCanvas.height = imageData.height
|
||||||
|
const newContext = newCanvas.getContext('2d')
|
||||||
|
|
||||||
|
// 将新的 ImageData 对象绘制到新的 Canvas 对象上
|
||||||
|
newContext.putImageData(newImageData, 0, 0)
|
||||||
|
|
||||||
|
// 将新的 Canvas 对象转换为一个 base64 编码的图片 URL
|
||||||
|
const base64 = newCanvas.toDataURL('image/png')
|
||||||
|
return base64
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看效果
|
||||||
|
*/
|
||||||
|
async function Check() {
|
||||||
|
debugger
|
||||||
|
// 判断当前是不是有蒙板
|
||||||
|
if (canvasHistory.length == 0) {
|
||||||
|
message.error('请先选择去水印的区域')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = await window.img.ProcessImageCheck(
|
||||||
|
initImage,
|
||||||
|
cloneDeep(canvasHistory),
|
||||||
|
reverseManageStore.selectBook.id
|
||||||
|
)
|
||||||
|
const arrayBuffer = res.data
|
||||||
|
const blob = new Blob([arrayBuffer], { type: 'image/png' })
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onloadend = function () {
|
||||||
|
let img = new Image()
|
||||||
|
img.src = reader.result
|
||||||
|
img.onload = function () {
|
||||||
|
imageContext.value.drawImage(img, 0, 0)
|
||||||
|
canvasHistory.push({
|
||||||
|
startX: 0,
|
||||||
|
startY: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
imageWidth: tempImageCanvas.value.width,
|
||||||
|
imageHeight: tempImageCanvas.value.height
|
||||||
|
})
|
||||||
|
step++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(blob)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function SaveMask() {
|
||||||
|
debugger
|
||||||
|
// 判断当前是不是又蒙板
|
||||||
|
if (canvasHistory.length <= 1) {
|
||||||
|
message.error('请先选择去水印的区域')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let maskPos = canvasHistory.filter((item) => item.width > 0 && item.height > 0)
|
||||||
|
if (maskPos.length == 0) {
|
||||||
|
message.error('请先选择去水印的区域,或者选择的水印区域无效')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 开始保存
|
||||||
|
let res = await window.db.UpdateBookData(reverseManageStore.selectBook.id, {
|
||||||
|
watermarkPosition: JSON.stringify(maskPos)
|
||||||
|
})
|
||||||
|
if (res.code == 0) {
|
||||||
|
message.error(res.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.success('水印位置保存成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*查看蒙板
|
||||||
|
*/
|
||||||
|
async function ViewMaskImage() {
|
||||||
|
window.system.OpenFile(mask_setting.value.mask_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpenTeach(type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'lama':
|
||||||
|
window.api.OpenUrl(
|
||||||
|
'https://rvgyir5wk1c.feishu.cn/docx/RZYCdG7ZpoKsIzxBEzccNEIFn8f#QOIRdJQvEouJB7xv0Xqcx3zInyb'
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case 'iopaint':
|
||||||
|
window.api.OpenUrl(
|
||||||
|
'https://rvgyir5wk1c.feishu.cn/docx/RZYCdG7ZpoKsIzxBEzccNEIFn8f#MLoUdn5cfo6NJTxL0xTcUGyLn43'
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifyImage,
|
||||||
|
imageCanvas,
|
||||||
|
tempImageCanvas,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
stopDrawing,
|
||||||
|
startDrawing,
|
||||||
|
startPos,
|
||||||
|
draw,
|
||||||
|
undo,
|
||||||
|
Check,
|
||||||
|
offset,
|
||||||
|
SaveMask,
|
||||||
|
mask_setting,
|
||||||
|
ViewMaskImage,
|
||||||
|
radius,
|
||||||
|
progressDialogRef,
|
||||||
|
total,
|
||||||
|
current,
|
||||||
|
OpenTeach,
|
||||||
|
reverseManageStore,
|
||||||
|
image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.url_class {
|
||||||
|
color: #e18a3b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url_class:hover {
|
||||||
|
color: brown;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -43,6 +43,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function selectImage(value) {
|
async function selectImage(value) {
|
||||||
|
debugger
|
||||||
let img = await getBase64(value.file.file)
|
let img = await getBase64(value.file.file)
|
||||||
props.modifyImage(img)
|
props.modifyImage(img)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,9 @@ const app = createApp(App)
|
|||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
|
// 注册 webview 为自定义元素
|
||||||
|
app.config.compilerOptions.isCustomElement = (tag) => tag === 'webview'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
@ -101,6 +104,11 @@ const routes = [
|
|||||||
name: 'test_options',
|
name: 'test_options',
|
||||||
component: () => import('./components/VideoSubtitle/VideoCanvas.vue')
|
component: () => import('./components/VideoSubtitle/VideoCanvas.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/lai_api',
|
||||||
|
name: 'lai_api',
|
||||||
|
component: () => import('./components/APIService/ApiService.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/TTS_Services',
|
path: '/TTS_Services',
|
||||||
name: 'TTS_Services',
|
name: 'TTS_Services',
|
||||||
|
|||||||
13
vue.config.js
Normal file
13
vue.config.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export default {
|
||||||
|
chainWebpack: (config) => {
|
||||||
|
config.module
|
||||||
|
.rule('vue')
|
||||||
|
.use('vue-loader')
|
||||||
|
.tap((options) => {
|
||||||
|
options.compilerOptions = {
|
||||||
|
isCustomElement: (tag) => tag === 'webview'
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user