import Realm, { UpdateMode } from 'realm' import { BookModel } from '../../model/Book/book.js' import path from 'path' import { define } from '../../../define' import { BookImageCategory, BookTaskStatus, BookType } from '../../../enum/bookEnum.js' import { successMessage } from '../../../../main/Public/generalTools' import { CheckFolderExistsOrCreate, CopyFileOrFolder } from '../../../Tools/file' const { v4: uuidv4 } = require('uuid') import { BaseRealmService } from './bookBasic.js' import { isEmpty } from 'lodash' import { FfmpegOptions } from '../../../../main/Service/ffmpegOptions.js' import { version } from '../../../../../package.json' import { Book } from '../../../../model/book/book.js' import { GeneralResponse } from '../../../../model/generalResponse.js' import { ImageToVideoModels } from '@/define/enum/video.js' export class BookService extends BaseRealmService { static instance: BookService | null = null realm: Realm private constructor() { super() } /** * 获取当前实例对象,为空则创建一个新的 * @returns */ public static async getInstance() { if (BookService.instance === null) { BookService.instance = new BookService() await super.getInstance() } await BookService.instance.open() return BookService.instance } /** * 获取小说信息,没有找到返回null * @param bookId */ GetBookDataById(bookId: string): Book.SelectBook | null { try { if (isEmpty(bookId)) { throw new Error('获取小说信息失败,缺少小说ID') } let books = this.realm.objects('Book').filtered('id = $0', bookId) if (books.length <= 0) { return null } else { // 对返回的数据进行处理 let resBooks = Array.from(books).map((book) => { // 这里可以直接操作普通对象 let bookObj = { ...book, bookFolderPath: path.resolve( define.project_path, book.bookFolderPath.replace(/\\/g, '/') ), oldVideoPath: book.oldVideoPath ? path.resolve(define.project_path, book.oldVideoPath.replace(/\\/g, '/')) : '', imageFolder: book.imageFolder ? path.resolve(define.project_path, book.imageFolder.replace(/\\/g, '/')) : '' } return bookObj }) return resBooks[0] } } catch (error) { throw error } } /** * 获取小说信息,通过参数查询 * @returns */ GetBookData(bookQuery) { try { // 获取所有的小说数据,并进行时间降序排序 let books = this.realm.objects('Book') let book_length = books.length // 开始开始筛选 if (bookQuery.bookId) { // 查询对应的小说ID的数据 books = books.filtered('id = $0', bookQuery.bookId) } books = books.sorted('updateTime', true) // 判断是不是有page和pageSize,有的话对查询返回的信息做分页 if (bookQuery.page && bookQuery.pageSize) { books = books.slice( (bookQuery.page - 1) * bookQuery.pageSize, bookQuery.page * bookQuery.pageSize ) as unknown as Realm.Results } if (books.length <= 0) { return successMessage( { res_book: [], book_length: 0 }, '没有数据', 'ReverseBook_GetBookData' ) } // 将realm对象数组转换为普通对象数组 let res_book = Array.from(books).map((book) => { // 这里可以直接操作普通对象 let bookObj = { ...book, bookFolderPath: path.resolve( define.project_path, book.bookFolderPath.replace(/\\/g, '/') ), oldVideoPath: book.oldVideoPath ? path.resolve(define.project_path, book.oldVideoPath.replace(/\\/g, '/')) : '', imageFolder: book.imageFolder ? path.resolve(define.project_path, book.imageFolder.replace(/\\/g, '/')) : '', imageStyle: book.imageStyle ? Array.from(book.imageStyle) : [], customizeImageStyle: book.customizeImageStyle ? Array.from(book.imageStyle) : [], } as Book.SelectBook return bookObj }) return successMessage( { res_book, book_length }, '获取成功', 'ReverseBook_GetBookData' ) } catch (error) { throw error } } /** * 新增或者是修小说数据 * @param {*} book 小说信息 * @returns */ async AddOrModifyBook(book) { try { if (book == null) { throw new Error('小说数据为空,无法修改') } // 当小说的类型是反推的时候,必须传入视频 if (book.type == BookType.MJ_REVERSE || book.type == BookType.SD_REVERSE) { // 判断视频是否存在 if (book.oldVideoPath == null || book.oldVideoPath == '') { throw new Error('反推必须传入视频') } } if (book.id == null) { // 新增 // 判断指定的名字在数据库中是否存在 let books = this.realm.objects('Book').filtered('name = $0', book.name) if (books.length > 0) { throw new Error(`小说名字 ${book.name} 已经存在,请更换小说名字`) } console.log(this) // 新增数据 book.id = uuidv4() book.createTime = new Date() book.updateTime = new Date() // 检查传入的视频文件是不是存在 // 获取当前最大的no let maxNo = this.realm.objects('Book').max('no') book.no = maxNo == null ? 1 : Number(maxNo) + 1 // 拼接项目文件夹 book.bookFolderPath = book.id book.imageFolder = `${book.id}/tmp` let bookFolderPath = path.resolve(define.project_path, book.id) let imageFolder = path.resolve(define.project_path, `${book.id}/tmp`) let oldVideoPath = path.resolve(define.project_path, `${book.id}/data/${book.id}.mp4`) let bookTaskImageFolder = path.resolve(imageFolder, 'output_00001') // 将视频拷贝一个到项目文件下面 if (book.oldVideoPath) { await CopyFileOrFolder(book.oldVideoPath, oldVideoPath) } // 创建对应的文件夹 await CheckFolderExistsOrCreate(bookFolderPath) await CheckFolderExistsOrCreate(imageFolder) await CheckFolderExistsOrCreate(bookTaskImageFolder) // 创建默认的任务文件夹 // 修改数据 book.oldVideoPath = path.relative(define.project_path, oldVideoPath) if (book.type == BookType.ORIGINAL) { book.oldVideoPath = null } let imageCategory = BookImageCategory.MJ if (book.type == BookType.SD_REVERSE) { imageCategory = BookImageCategory.SD } else if (book.type == BookType.MJ_REVERSE) { imageCategory = BookImageCategory.MJ } else if (book.type == BookType.ORIGINAL) { imageCategory = global.config.defaultImageMode ?? BookImageCategory.MJ } else { throw new Error('未知的小说类型') } let videoCategory = ImageToVideoModels.MJ_VIDEO; this.realm.write(() => { book.version = version this.realm.create('Book', book) // 添加一个任务 let bookTask = { id: uuidv4(), bookId: book.id, no: 1, name: 'output_00001', generateVideoPath: null, srtPath: null, audioPath: null, imageFolder: path.relative(define.project_path, bookTaskImageFolder), // 获取文件输出对于项目的相对路径 styleList: [], prefix: null, status: BookTaskStatus.WAIT, errorMsg: null, isAuto: false, updateTime: new Date(), createTime: new Date(), version: version, imageCategory: imageCategory, videoCategory: videoCategory, openVideoGenerate: false } // 添加任务 this.realm.create('BookTask', bookTask) }) // 保存成功,返回数据,但是要做处理 book.bookFolderPath = bookFolderPath book.imageFolder = imageFolder book.oldVideoPath = oldVideoPath return successMessage(book, '新增成功', 'BookBasic_AddOrModifyBook') } else { // 修改 // 判断指定的名字在数据库中是否存在(不算自己) let books = this.realm .objects('Book') .filtered('name = $0 AND id != $1', book.name, book.id) if (books.length > 0) { throw new Error(`小说名字 ${book.name} 已经存在,请更换小说名字`) } // 两个文件夹地址不能改,删除两个属性 delete book.bookFolderPath delete book.imageFolder // 修改数据 book.updateTime = new Date() this.transaction(() => { this.realm.create('Book', book, UpdateMode.Modified) }) // 保存成功,返回数据,但是要做处理 return successMessage(null, '修改成功', 'BookBasic_AddOrModifyBook') } } catch (error) { throw error } } /** * 修改小说数据 * @param bookId 小说的ID * @param bookData 要修改的小说数据 */ UpdateBookData(bookId: string, bookData: Book.SelectBook): Book.SelectBook { try { if (bookId == null) { throw new Error('修改小说数据失败,缺少小说ID') } if (bookData == null) { throw new Error('修改小说数据失败,缺少小说数据') } // 检查小说ID对应的数据是不是存在 let bookRes = this.GetBookDataById(bookId) if (bookRes == null) { throw new Error('修改小说数据失败,小说ID对应的数据不存在') } if (bookData && bookData.id) { delete bookData.id } // 开始修改 this.transaction(() => { this.realm.create('Book', { id: bookId, ...bookData }, UpdateMode.Modified) }) bookRes = this.GetBookDataById(bookId) if (bookRes == null) { throw new Error('获取修改后的小说数据失败,小说ID对应的数据不存在') } // return successMessage(bookRes, '修改小说数据成功', 'ReverseBook_UpdateBookData') return bookRes; } catch (error) { throw error } } /** * 删除指定的小说任务 * @param bookId 需要删除的小说的ID */ DeleteBookData(bookId: string): void { try { this.transaction(() => { let book = this.realm.objectForPrimaryKey('Book', bookId); if (book == null) { throw new Error('未找到对应的小说') } // 删除对应的小说 this.realm.delete(book) }) } catch (error) { throw error } } }