LaiTool V3.0.1-preview.5
This commit is contained in:
parent
f64c7ad677
commit
e85b0a986d
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ node_modules
|
||||
dist
|
||||
out
|
||||
project
|
||||
tts/*
|
||||
resources/scripts/build*
|
||||
resources/scripts/dist
|
||||
resources/scripts/model
|
||||
|
||||
30
package-lock.json
generated
30
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "laitool",
|
||||
"version": "3.0.1-preview.4",
|
||||
"version": "3.0.1-preview.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "laitool",
|
||||
"version": "3.0.1-preview.4",
|
||||
"version": "3.0.1-preview.5",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@alicloud/alimt20181012": "^1.2.0",
|
||||
@ -1927,13 +1927,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.14.11",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.11.tgz",
|
||||
"integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
|
||||
"version": "22.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-22.1.0.tgz",
|
||||
"integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
"undici-types": "~6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node/node_modules/undici-types": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.13.0.tgz",
|
||||
"integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg=="
|
||||
},
|
||||
"node_modules/@types/responselike": {
|
||||
"version": "1.0.3",
|
||||
"license": "MIT",
|
||||
@ -5732,6 +5737,14 @@
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/httpx/node_modules/@types/node": {
|
||||
"version": "20.14.14",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.14.tgz",
|
||||
"integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"license": "MIT",
|
||||
@ -11679,9 +11692,10 @@
|
||||
"integrity": "sha512-Ka0DBegjuV9IPYFT1h0Qqk5U4pccebNIJCGl8C5uU7xtOs+jpJvKGAY4fHGK25hTmXZOEUl9Cnsg5cS6K/b5DA=="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.3",
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "laitool",
|
||||
"version": "3.0.1-preview.4",
|
||||
"version": "3.0.1-preview.5",
|
||||
"description": "An AI tool for image processing, video processing, and other functions.",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "laitool.cn",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 623 KiB After Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
BIN
resources/scripts/db/tts.realm
Normal file
BIN
resources/scripts/db/tts.realm
Normal file
Binary file not shown.
BIN
resources/scripts/db/tts.realm.lock
Normal file
BIN
resources/scripts/db/tts.realm.lock
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
"draft_path": "你的剪映草稿地址",
|
||||
"project_path": "你的项目文件地址(存放图片视频等数据的文件夹)",
|
||||
"project_name": "你的项目名字",
|
||||
"gpt_business": "b8866543-8c27-4888-869c-00aa1eb31272",
|
||||
"gpt_business": "b44c6f24-59e4-4a71-b2c7-3df0c4e35e65",
|
||||
"gpt_model": "gpt-3.5-turbo",
|
||||
"task_number": 1,
|
||||
"translation_business": "https://fanyi-api.baidu.com/api/trans/vip/translate",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,13 +1,11 @@
|
||||
import Realm, { UpdateMode } from 'realm'
|
||||
import { BookModel } from '../../model/Book/book.js'
|
||||
import path from 'path'
|
||||
import { BaseService } from '../baseService.js'
|
||||
import { define } from '../../../define.js'
|
||||
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 { BookTaskService } from './bookTaskService'
|
||||
import { BaseRealmService } from './bookBasic.js'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { FfmpegOptions } from '../../../../main/Service/ffmpegOptions.js'
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
import Realm from 'realm'
|
||||
import path from 'path'
|
||||
import { BaseService } from '../baseService.js'
|
||||
import { define } from '../../../define.js'
|
||||
import { BookTaskModel } from '../../model/Book/bookTask.js'
|
||||
import { BookTaskStatus } from '../../../enum/bookEnum.js'
|
||||
import { successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseRealmService } from './bookBasic'
|
||||
import { cloneDeep, endsWith, isEmpty } from 'lodash'
|
||||
import { book } from '../../../../preload/book.js'
|
||||
import { DefaultObject } from 'realm/dist/public-types/schema.js'
|
||||
import { cloneDeep, isEmpty } from 'lodash'
|
||||
import { JoinPath } from '../../../Tools/file'
|
||||
import { BookTaskDetailModel, ReversePrompt } from '../../model/Book/bookTaskDetail.js'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
import Realm, { UpdateMode } from 'realm'
|
||||
import path from 'path'
|
||||
import { BaseService } from '../baseService.js'
|
||||
import { define } from '../../../define.js'
|
||||
import { SoftwareModel } from '../../model/SoftWare/software'
|
||||
import { ComponentSize, SoftwareThemeType } from '../../../enum/softwareEnum.js'
|
||||
import { errorMessage, successMessage } from '../../../../main/Public/generalTools'
|
||||
import { BaseSoftWareService } from './softwareBasic.js'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
@ -1,13 +1,36 @@
|
||||
import SoftwareService from './SoftWare/softwareService'
|
||||
import { BookService } from './Book/bookService'
|
||||
import { BookTaskService } from './Book/bookTaskService'
|
||||
import { BookTaskDetailService } from './Book/bookTaskDetailService'
|
||||
import { BookBackTaskListService } from './Book/bookBackTaskListService'
|
||||
import { MJSettingService } from './SoftWare/mjSettingService'
|
||||
|
||||
export class ServiceBase {
|
||||
softService: SoftwareService
|
||||
softService: SoftwareService | undefined
|
||||
bookService: BookService | undefined
|
||||
bookTaskService: BookTaskService | undefined
|
||||
bookTaskDetailService: BookTaskDetailService | undefined
|
||||
bookBackTaskListService: BookBackTaskListService | undefined
|
||||
mjSettingService: MJSettingService | undefined
|
||||
|
||||
constructor() {}
|
||||
|
||||
constructor() { }
|
||||
|
||||
async InitService() {
|
||||
if (!this.softService) {
|
||||
this.softService = await SoftwareService.getInstance()
|
||||
}
|
||||
if (!this.bookService) {
|
||||
this.bookService = await BookService.getInstance()
|
||||
}
|
||||
if (!this.bookTaskDetailService) {
|
||||
this.bookTaskDetailService = await BookTaskDetailService.getInstance()
|
||||
}
|
||||
if (!this.bookBackTaskListService) {
|
||||
this.bookBackTaskListService = await BookBackTaskListService.getInstance()
|
||||
}
|
||||
if (this.mjSettingService) {
|
||||
this.mjSettingService = await MJSettingService.getInstance()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/define/db/tts/tts.ts
Normal file
36
src/define/db/tts/tts.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// @ts-ignore
|
||||
import Realm from 'realm'
|
||||
import { TTSSelectModel } from '../../enum/tts'
|
||||
|
||||
export class TTSModel extends Realm.Object<TTSModel> {
|
||||
id: string
|
||||
no: number
|
||||
name: string // 一把就是配音的前几个字
|
||||
textPath: string // 保存生成文本的地址
|
||||
ttsPath: string // 生成的配置文件地址
|
||||
srtPath: string | null // 生成的SRT地址
|
||||
selectModel: TTSSelectModel // 选择模式
|
||||
hasSrt: boolean
|
||||
srtJsonPath: string | null
|
||||
createTime: Date
|
||||
updateTime: Date
|
||||
|
||||
static schema: Realm.ObjectSchema = {
|
||||
name: 'TTSModel',
|
||||
properties: {
|
||||
id: 'string',
|
||||
no: 'int',
|
||||
name: 'string',
|
||||
textPath: 'string',
|
||||
ttsPath: 'string',
|
||||
srtPath: 'string?',
|
||||
selectModel: 'string',
|
||||
hasSrt: 'bool',
|
||||
srtJsonPath: 'string?',
|
||||
createTime: 'date',
|
||||
updateTime: 'date'
|
||||
},
|
||||
// 主键为_id
|
||||
primaryKey: 'id'
|
||||
}
|
||||
}
|
||||
60
src/define/db/tts/ttsBase.ts
Normal file
60
src/define/db/tts/ttsBase.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import Realm from 'realm'
|
||||
import { BaseService } from '../service/baseService'
|
||||
import { define } from '../../define'
|
||||
import path from 'path'
|
||||
import { TTSModel } from './tts'
|
||||
|
||||
|
||||
let dbPath = path.resolve(define.db_path, 'tts.realm')
|
||||
|
||||
// 版本迁移
|
||||
const migration = (oldRealm: Realm, newRealm: Realm) => {
|
||||
if (oldRealm.schemaVersion < 2) {
|
||||
const oldObjects = oldRealm.objects<TTSModel>('TTSModel')
|
||||
const newObjects = newRealm.objects<TTSModel>('TTSModel')
|
||||
for (let i = 0; i < oldObjects.length; i++) {
|
||||
newObjects[i].textPath = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseTTSService extends BaseService {
|
||||
static instance: BaseTTSService | null = null
|
||||
protected realm: Realm | null = null
|
||||
dbpath: string
|
||||
|
||||
protected constructor() {
|
||||
super()
|
||||
this.dbpath = dbPath
|
||||
}
|
||||
|
||||
public static async getInstance() {
|
||||
if (BaseTTSService.instance === null) {
|
||||
BaseTTSService.instance = new BaseTTSService()
|
||||
await BaseTTSService.instance.open()
|
||||
}
|
||||
return BaseTTSService.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据库连接,如果已经存在则直接返回
|
||||
* @returns
|
||||
*/
|
||||
async open() {
|
||||
try {
|
||||
if (this.realm != null) return
|
||||
// 判断当前全局是不是又当前这个
|
||||
const config = {
|
||||
schema: [
|
||||
TTSModel
|
||||
],
|
||||
path: this.dbpath,
|
||||
schemaVersion: 2,
|
||||
migration: migration
|
||||
}
|
||||
this.realm = await Realm.open(config)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
164
src/define/db/tts/ttsService.ts
Normal file
164
src/define/db/tts/ttsService.ts
Normal file
@ -0,0 +1,164 @@
|
||||
import Realm from 'realm'
|
||||
import path from 'path'
|
||||
import { BaseTTSService } from './ttsBase'
|
||||
import { define } from '../../define'
|
||||
import { TTSModel } from './tts'
|
||||
import { tts } from '../../../model/tts'
|
||||
import { isEmpty } from 'lodash'
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
|
||||
export class TTSService extends BaseTTSService {
|
||||
static instance: TTSService | null = null
|
||||
realm: Realm
|
||||
private constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前实例对象,为空则创建一个新的
|
||||
* @returns
|
||||
*/
|
||||
public static async getInstance() {
|
||||
if (TTSService.instance === null) {
|
||||
TTSService.instance = new TTSService()
|
||||
await super.getInstance()
|
||||
}
|
||||
await TTSService.instance.open()
|
||||
return TTSService.instance
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取配音历史信息,可以分页和筛选
|
||||
* @param condition 查询的条件参数
|
||||
* @returns 返回当前的查到的所有数据和没有被分割过的数据的数量
|
||||
*/
|
||||
GetTTSHistory(condition: tts.TTSHistoryQueryParams): tts.TTSModelRes {
|
||||
try {
|
||||
let TTSHistory = this.realm.objects<TTSModel>('TTSModel')
|
||||
|
||||
if (condition.name) {
|
||||
TTSHistory = TTSHistory.filtered('name CONTAINS $0', condition.name)
|
||||
}
|
||||
if (condition.id) {
|
||||
TTSHistory = TTSHistory.filtered('id == $0', condition.id)
|
||||
}
|
||||
TTSHistory = TTSHistory.sorted('updateTime', true)
|
||||
|
||||
let TTSCount = TTSHistory.length
|
||||
|
||||
if (condition.page && condition.pageSize) {
|
||||
TTSHistory = TTSHistory.slice(
|
||||
(condition.page - 1) * condition.pageSize,
|
||||
condition.page * condition.pageSize
|
||||
) as unknown as Realm.Results<TTSModel>
|
||||
}
|
||||
|
||||
// 将数据进行处理
|
||||
// 将realm对象数组转换为普通对象数组
|
||||
let TTSHistoryRes = Array.from(TTSHistory).map((item) => {
|
||||
// 这里可以直接操作普通对象
|
||||
let bookObj = {
|
||||
...item,
|
||||
ttsPath: item.ttsPath ? path.resolve(define.tts_path, item.ttsPath.replace(/\\/g, '/')) : '',
|
||||
srtPath: item.srtPath ? path.resolve(define.tts_path, item.srtPath.replace(/\\/g, '/')) : '',
|
||||
srtJsonPath: item.srtJsonPath ? path.resolve(define.tts_path, item.srtJsonPath.replace(/\\/g, '/')) : '',
|
||||
textPath: item.textPath ? path.resolve(define.tts_path, item.textPath.replace(/\\/g, '/')) : ''
|
||||
} as tts.TTSModel
|
||||
return bookObj
|
||||
}) as tts.TTSModel[]
|
||||
return {
|
||||
ttsList: TTSHistoryRes,
|
||||
ttsCount: TTSCount // 返回筛选的总数
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过指定ID获取对应的配音历史信息
|
||||
* @param id 配音历史ID
|
||||
*/
|
||||
GetTTSHistoryById(id: string): tts.TTSModel {
|
||||
try {
|
||||
let res = this.GetTTSHistory({ id: id });
|
||||
if (res.ttsList.length < 0) {
|
||||
throw new Error("没有找到指定ID的配音历史数据,请检查");
|
||||
}
|
||||
// 返回第一个数据
|
||||
return res.ttsList[0];
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增一条TTS生成历史记录
|
||||
* @param data 要新增的数据
|
||||
*/
|
||||
AddTTSHistory(data: tts.TTSModel): tts.TTSModel {
|
||||
try {
|
||||
if (isEmpty(data.id)) {
|
||||
throw new Error('新增TTS历史记录失败,缺少ID')
|
||||
}
|
||||
// 获取最大的no
|
||||
let maxNo = this.realm.objects<TTSModel>('TTSModel').max('no')
|
||||
|
||||
data.no = maxNo == undefined ? 1 : Number(maxNo) + 1
|
||||
data.createTime = new Date()
|
||||
data.updateTime = new Date()
|
||||
|
||||
this.transaction(() => {
|
||||
this.realm.create('TTSModel', data)
|
||||
})
|
||||
return data
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新TTS历史记录数据,修改之后,返回修改后的数据
|
||||
* @param ttsId 要更新的TTS历史记录ID
|
||||
* @param data 要更新的数据
|
||||
*/
|
||||
UpdetateTTSHistory(ttsId: string, data: tts.TTSModel): tts.TTSModel {
|
||||
try {
|
||||
this.transaction(() => {
|
||||
let tts = this.realm.objectForPrimaryKey<TTSModel>('TTSModel', ttsId)
|
||||
if (tts == null) {
|
||||
throw new Error('没有找到指定ID的TTS历史记录,请检查')
|
||||
}
|
||||
for (let key in data) {
|
||||
tts[key] = data[key]
|
||||
}
|
||||
tts.updateTime = new Date()
|
||||
})
|
||||
|
||||
// 获取修改后的数据返回
|
||||
let res = this.GetTTSHistoryById(ttsId)
|
||||
return res
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除TTS历史记录
|
||||
* @param ttsId tts历史记录ID
|
||||
*/
|
||||
DeleteTTSHistory(ttsId: string): void {
|
||||
try {
|
||||
this.transaction(() => {
|
||||
let tts = this.realm.objectForPrimaryKey<TTSModel>('TTSModel', ttsId)
|
||||
if (tts == null) {
|
||||
throw new Error('没有找到指定ID的TTS历史记录,请检查')
|
||||
}
|
||||
this.realm.delete(tts)
|
||||
})
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,7 +144,7 @@ if (!app.isPackaged) {
|
||||
|
||||
define['remotemj_api'] = 'https://api.laitool.net/'
|
||||
define['serverUrl'] = 'http://lapi.laitool.cn'
|
||||
define['hkServerUrl'] = 'https://api.laitool.cc/'
|
||||
define['bakServerUrl'] = 'https://bakapi.laitool.cc/'
|
||||
define['hkServerUrl'] = 'https://laitool.net/'
|
||||
define['bakServerUrl'] = 'https://laitool.net/'
|
||||
define['API'] = 'f85d39ed5a40fd09966f13f12b6cf0f0'
|
||||
export { define }
|
||||
|
||||
@ -283,7 +283,10 @@ export const DEFINE_STRING = {
|
||||
TTS: {
|
||||
GET_TTS_CONFIG: 'GET_TTS_CONFIG',
|
||||
GENERATE_AUDIO: 'GENERATE_AUDIO',
|
||||
SAVE_TTS_CONFIG: 'SAVE_TTS_CONFIG'
|
||||
SAVE_TTS_CONFIG: 'SAVE_TTS_CONFIG',
|
||||
GENERATE_SRT: "GENERATE_SRT",
|
||||
GET_TTS_HISTORY_DATA: 'GET_TTS_HISTORY_DATA',
|
||||
DELETE_TTS_HISTORY: 'DELETE_TTS_HISTORY',
|
||||
},
|
||||
WRITE: {
|
||||
GET_WRITE_CONFIG: 'GET_WRITE_CONFIG',
|
||||
|
||||
5
src/define/enum/tts.ts
Normal file
5
src/define/enum/tts.ts
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
export enum TTSSelectModel {
|
||||
// edge-tts
|
||||
edgeTTS = 'edge-tts',
|
||||
}
|
||||
@ -275,6 +275,14 @@ export const gptDefine = {
|
||||
{
|
||||
label: 'gpt-4',
|
||||
value: 'gpt-4'
|
||||
},
|
||||
{
|
||||
label: 'deepseek-chat',
|
||||
value: 'deepseek-chat'
|
||||
},
|
||||
{
|
||||
label: 'deepseek-coder',
|
||||
value: 'deepseek-coder'
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
@ -115,10 +115,9 @@ export class TagDefine {
|
||||
let property = value[1]
|
||||
value = JSON.parse(value[0])
|
||||
let tmp_key = uuidv4()
|
||||
|
||||
// 特殊操作。为角色和场景的时候,需要copy图片
|
||||
if (property == 'character_tags' || property == 'scene_tags' || property == 'style_tags') {
|
||||
let show_image = value.show_image
|
||||
let show_image = value.show_image ? value.show_image.split('?t')[0] : null
|
||||
if (show_image && show_image != '') {
|
||||
let file_name = `c_s/${value.key ? value.key : tmp_key}.png`
|
||||
let new_image_path = path.join(define.image_path, file_name)
|
||||
@ -129,7 +128,6 @@ export class TagDefine {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取自定义的GPT数据
|
||||
let tag_setting = JSON.parse(await fspromises.readFile(define.tag_setting, 'utf-8'))
|
||||
let tag = get(tag_setting, property, [])
|
||||
|
||||
@ -1,40 +1,39 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { ipcMain } from 'electron'
|
||||
import { DEFINE_STRING } from '../../define/define_string'
|
||||
import { CheckFileOrDirExist } from "../../define/Tools/file";
|
||||
import { errorMessage, successMessage } from "../Public/generalTools";
|
||||
import { CheckFileOrDirExist } from '../../define/Tools/file'
|
||||
import { errorMessage, successMessage } from '../Public/generalTools'
|
||||
import path from 'path'
|
||||
const { shell } = require('electron')
|
||||
|
||||
function SystemIpc() {
|
||||
// 打开指定的文件
|
||||
ipcMain.on(DEFINE_STRING.SYSTEM.OPEN_FILE, async (event, value) => {
|
||||
await shell.openPath(value)
|
||||
})
|
||||
|
||||
// 打开指定的文件
|
||||
ipcMain.on(DEFINE_STRING.SYSTEM.OPEN_FILE, async (event, value) => {
|
||||
await shell.openPath(value);
|
||||
});
|
||||
|
||||
// 试用文件资源打开指定的文件夹
|
||||
ipcMain.handle(DEFINE_STRING.OPEN_FOLDER, async (event, value) => {
|
||||
try {
|
||||
let openFolder = null
|
||||
if (value.baseProject) {
|
||||
openFolder = path.join(global.config.project_path, value.folderPath)
|
||||
}
|
||||
else {
|
||||
openFolder = value.folderPath
|
||||
}
|
||||
// 判断文件夹是不是存在
|
||||
let isExist = await CheckFileOrDirExist(openFolder)
|
||||
if (!isExist) {
|
||||
throw new Error("文件夹不存在,请检查")
|
||||
}
|
||||
shell.openPath(openFolder)
|
||||
return successMessage(null, '打开成功');
|
||||
} catch (error) {
|
||||
return errorMessage("打开文件夹错误,错误信息如下:" + error.message, "SystemIpc_OPEN_FOLDER")
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
export {
|
||||
SystemIpc
|
||||
// 试用文件资源打开指定的文件夹
|
||||
ipcMain.handle(DEFINE_STRING.OPEN_FOLDER, async (event, value) => {
|
||||
try {
|
||||
let openFolder = null
|
||||
if (value.baseProject) {
|
||||
openFolder = path.join(global.config.project_path, value.folderPath)
|
||||
}
|
||||
if (value.dirFloder) {
|
||||
openFolder = path.dirname(value.folderPath)
|
||||
}
|
||||
if (!openFolder) {
|
||||
openFolder = value.folderPath
|
||||
}
|
||||
// 判断文件夹是不是存在
|
||||
let isExist = await CheckFileOrDirExist(openFolder)
|
||||
if (!isExist) {
|
||||
throw new Error('文件夹不存在,请检查')
|
||||
}
|
||||
shell.openPath(openFolder)
|
||||
return successMessage(null, '打开成功')
|
||||
} catch (error) {
|
||||
return errorMessage('打开文件夹错误,错误信息如下:' + error.message, 'SystemIpc_OPEN_FOLDER')
|
||||
}
|
||||
})
|
||||
}
|
||||
export { SystemIpc }
|
||||
|
||||
@ -6,11 +6,35 @@ const tts = new TTS()
|
||||
|
||||
export function TTSIpc() {
|
||||
// 获取当前的TTS配置数据
|
||||
ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => tts.GetTTSCOnfig())
|
||||
ipcMain.handle(DEFINE_STRING.TTS.GET_TTS_CONFIG, async () => await tts.GetTTSCOnfig())
|
||||
|
||||
// 保存TTS配置
|
||||
ipcMain.handle(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, async (event, data) => tts.SaveTTSConfig(data))
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.TTS.SAVE_TTS_CONFIG,
|
||||
async (event, data) => await tts.SaveTTSConfig(data)
|
||||
)
|
||||
|
||||
// 生成音频
|
||||
ipcMain.handle(DEFINE_STRING.TTS.GENERATE_AUDIO, async (event, text) => tts.GenerateAudio(text))
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.TTS.GENERATE_AUDIO,
|
||||
async (event, text) => await tts.GenerateAudio(text)
|
||||
)
|
||||
|
||||
// 生成SRT字幕文件
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.TTS.GENERATE_SRT,
|
||||
async (event, ttsId) => await tts.GenerateSRT(ttsId)
|
||||
)
|
||||
|
||||
// 删除配音历史记录
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.TTS.DELETE_TTS_HISTORY,
|
||||
async (event, ttsId) => await tts.DeleteTTSHistory(ttsId)
|
||||
)
|
||||
|
||||
// 获取配音的历史记录
|
||||
ipcMain.handle(
|
||||
DEFINE_STRING.TTS.GET_TTS_HISTORY_DATA,
|
||||
async (event, queryCondition) => await tts.GetTTSHistoryData(queryCondition)
|
||||
)
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export class ClipDraft {
|
||||
this.one_duration_time = 5000000;
|
||||
this.text_end_time = 0;
|
||||
this.iamge_end_time = 0;
|
||||
this.dubbing_emd_time = 0;
|
||||
this.TTS_emd_time = 0;
|
||||
this.draft_duration_time = 0;
|
||||
this.global = global;
|
||||
this.value = value;
|
||||
@ -484,7 +484,7 @@ export class ClipDraft {
|
||||
/**
|
||||
* 添加配音
|
||||
*/
|
||||
async AddDubbingMusic(musicPath) {
|
||||
async AddTTSMusic(musicPath) {
|
||||
// 添加speeds
|
||||
let speeds = await this.AddSpeeds();
|
||||
this.draft_json.materials.speeds.push(speeds);
|
||||
@ -820,7 +820,7 @@ export class ClipDraft {
|
||||
await this.LoadDraftJson();
|
||||
await this.AddAllImageToTracks();
|
||||
await this.AddAllTextToTrack();
|
||||
await this.AddDubbingMusic(path.normalize(this.value[1].audio_path));
|
||||
await this.AddTTSMusic(path.normalize(this.value[1].audio_path));
|
||||
if (this.value[1].background_music != "" && this.value[1].background_music != undefined && this.value[1].background_music != null) {
|
||||
await this.AddRandomBackfroundMusic(this.value[1].background_music);
|
||||
}
|
||||
|
||||
@ -34,14 +34,16 @@ export class TaskManager {
|
||||
this.mjOpt = new MJOpt();
|
||||
}
|
||||
|
||||
async InitService() {
|
||||
async InitService(getMJsetting = false) {
|
||||
if (!this.softwareService) {
|
||||
this.softwareService = await SoftwareService.getInstance();
|
||||
}
|
||||
if (!this.bookBackTaskListService) {
|
||||
this.bookBackTaskListService = await BookBackTaskListService.getInstance();
|
||||
}
|
||||
await this.mjOpt.InitService();
|
||||
if (getMJsetting) {
|
||||
await this.mjOpt.InitService();
|
||||
}
|
||||
}
|
||||
|
||||
async GetGlobalConfig() {
|
||||
@ -117,6 +119,7 @@ export class TaskManager {
|
||||
}
|
||||
|
||||
this.spaceTime = 5000;
|
||||
await this.InitService(true);
|
||||
//循环添加任务
|
||||
for (let index = 0; index < tasks.data.length; index++) {
|
||||
const element = tasks.data[index];
|
||||
|
||||
@ -1,13 +1,21 @@
|
||||
import { errorMessage, successMessage } from '../Public/generalTools'
|
||||
import { SoftwareService } from '../../define/db/service/SoftWare/softwareService'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { define } from '../../define/define'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { ValidateJson } from '../../define/Tools/validate'
|
||||
import { CheckFileOrDirExist, CheckFolderExistsOrCreate, DeleteFolderAllFile } from '../../define/Tools/file'
|
||||
import { TTSSelectModel } from '../../define/enum/tts'
|
||||
const { EdgeTTS } = require('node-edge-tts')
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
import { TTSService } from '../../define/db/tts/ttsService'
|
||||
import { tts } from '../../model/tts'
|
||||
import { GeneralResponse } from '../../model/generalResponse'
|
||||
|
||||
export class TTS {
|
||||
softService: SoftwareService
|
||||
ttsService: TTSService
|
||||
|
||||
constructor() { }
|
||||
|
||||
@ -18,8 +26,14 @@ export class TTS {
|
||||
if (!this.softService) {
|
||||
this.softService = await SoftwareService.getInstance()
|
||||
}
|
||||
if (!this.ttsService) {
|
||||
this.ttsService = await TTSService.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//#region 设置相关
|
||||
|
||||
/**
|
||||
* 初始化TTS设置
|
||||
*/
|
||||
@ -80,6 +94,9 @@ export class TTS {
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 合成音频相关
|
||||
/**
|
||||
* 生成音频
|
||||
* @param text 要生成的文本
|
||||
@ -91,18 +108,43 @@ export class TTS {
|
||||
if (ttsSetting.code === 0) {
|
||||
return ttsSetting
|
||||
}
|
||||
let res
|
||||
let res = undefined
|
||||
|
||||
let audioPath = path.join(define.project_path, 'audio.mp3')
|
||||
let selectModel = ttsSetting.data.selectModel
|
||||
// 生成对应的ID
|
||||
let thisId = uuidv4()
|
||||
|
||||
// 讲text写道本地
|
||||
let textPath = path.join(define.tts_path, `${thisId}/${thisId}.txt`)
|
||||
await CheckFolderExistsOrCreate(path.dirname(textPath))
|
||||
await fs.promises.writeFile(textPath, text, 'utf-8')
|
||||
|
||||
let audioPath = path.join(define.tts_path, `${thisId}/${thisId}.mp3`)
|
||||
let selectModel = ttsSetting.data.selectModel as TTSSelectModel
|
||||
|
||||
let hasSrt = true
|
||||
switch (selectModel) {
|
||||
case 'edge-tts':
|
||||
res = await this.GenerateAudioByEdgeTTS(text, ttsSetting.data.edgeTTS)
|
||||
case TTSSelectModel.edgeTTS:
|
||||
hasSrt = ttsSetting.data.edgeTTS.saveSubtitles
|
||||
res = await this.GenerateAudioByEdgeTTS(text, ttsSetting.data.edgeTTS, audioPath)
|
||||
break
|
||||
|
||||
default:
|
||||
throw new Error('未知的TTS模式')
|
||||
}
|
||||
|
||||
if (res == undefined) {
|
||||
throw new Error('生成音频失败,未知错误')
|
||||
}
|
||||
|
||||
// 这边返回成功,保存配音历史
|
||||
this.ttsService.AddTTSHistory({
|
||||
name: text.substring(0, 10),
|
||||
ttsPath: res.mp3Path ? path.relative(define.tts_path, res.mp3Path) : null,
|
||||
hasSrt: hasSrt,
|
||||
selectModel: selectModel,
|
||||
srtJsonPath: res.srtJsonPath ? path.relative(define.tts_path, res.srtJsonPath) : null,
|
||||
id: thisId,
|
||||
textPath: textPath ? path.relative(define.tts_path, textPath) : null
|
||||
})
|
||||
return res
|
||||
} catch (error) {
|
||||
return errorMessage('生成音频失败,错误信息如下:' + error.toString(), 'TTS_GenerateAudio')
|
||||
@ -110,35 +152,175 @@ export class TTS {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用EdgeTTS生成音频的方法
|
||||
* 使用EdgeTTS生成音频的方法,生成完成,返回生成的音频路径
|
||||
* @param text 要生成的文本
|
||||
* @param edgeTTS edgetts的设置
|
||||
* @returns
|
||||
*/
|
||||
// @ts-ignore
|
||||
async GenerateAudioByEdgeTTS(text: string, edgeTTS: TTSSettingModel.EdgeTTSSetting) {
|
||||
async GenerateAudioByEdgeTTS(text: string, edgeTTS: TTSSettingModel.EdgeTTSSetting, mp3Path: string) {
|
||||
try {
|
||||
const tts = new EdgeTTS({
|
||||
voice: edgeTTS.value,
|
||||
lang: edgeTTS.lang,
|
||||
outputFormat: 'audio-24khz-96kbitrate-mono-mp3',
|
||||
saveSubtitles: edgeTTS.saveSubtitles,
|
||||
saveSubtitles: true,
|
||||
pitch: `${edgeTTS.pitch}%`,
|
||||
rate: `${edgeTTS.rate}%`,
|
||||
volumn: `${edgeTTS.volumn}%`
|
||||
})
|
||||
let ttsRes = await tts.ttsPromise(text, 'C:\\Users\\27698\\Desktop\\audio.mp3')
|
||||
let ttsRes = await tts.ttsPromise(text, mp3Path)
|
||||
console.log(ttsRes)
|
||||
return successMessage(
|
||||
'C:\\Users\\27698\\Desktop\\audio.mp3',
|
||||
'生成音频成功',
|
||||
'TTS_GenerateAudioByEdgeTTS'
|
||||
)
|
||||
return {
|
||||
mp3Path: mp3Path,
|
||||
srtJsonPath: mp3Path + '.json'
|
||||
};
|
||||
} catch (error) {
|
||||
return errorMessage(
|
||||
'生成音频失败,错误信息如下:' + error.toString(),
|
||||
'TTS_GenerateAudioByEdgeTTS'
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 合成字幕
|
||||
|
||||
/**
|
||||
* 通过配音历史ID生成字幕
|
||||
* @param ttsId 配音历史ID
|
||||
*/
|
||||
async GenerateSRT(ttsId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
// 获取配音历史
|
||||
let ttsHistory = this.ttsService.GetTTSHistoryById(ttsId)
|
||||
let selectModel = ttsHistory.selectModel as TTSSelectModel
|
||||
let res = undefined
|
||||
switch (selectModel) {
|
||||
case TTSSelectModel.edgeTTS:
|
||||
res = await this.GenerateSRTByEdgeTTS(ttsHistory)
|
||||
break
|
||||
default:
|
||||
throw new Error('未知的TTS模式')
|
||||
}
|
||||
// 这边重新请求,返回一个完整的
|
||||
let ttsHistoryData = this.ttsService.GetTTSHistoryById(ttsId)
|
||||
return successMessage(ttsHistoryData, '生成字幕成功', 'TTS_GenerateSRT')
|
||||
} catch (error) {
|
||||
return errorMessage('生成字幕失败,错误信息如下:' + error.toString(), 'TTS_GenerateSRT')
|
||||
}
|
||||
}
|
||||
|
||||
async GenerateSRTByEdgeTTS(ttsHistory: tts.TTSModel) {
|
||||
try {
|
||||
// 一系列的检查文件是不是存在
|
||||
if (isEmpty(ttsHistory.textPath)) {
|
||||
throw new Error('生成字幕失败,文本文件不存在')
|
||||
}
|
||||
if (isEmpty(ttsHistory.srtJsonPath)) {
|
||||
throw new Error('生成字幕失败,srtJson文件不存在')
|
||||
}
|
||||
let checkFileExist = await CheckFileOrDirExist(ttsHistory.textPath)
|
||||
if (!checkFileExist) {
|
||||
throw new Error('生成字幕失败,文本文件不存在')
|
||||
}
|
||||
checkFileExist = await CheckFileOrDirExist(ttsHistory.srtJsonPath)
|
||||
if (!checkFileExist) {
|
||||
throw new Error('生成字幕失败,srtJson文件不存在')
|
||||
}
|
||||
let text = await fs.promises.readFile(ttsHistory.textPath, 'utf-8');
|
||||
let srtJson = JSON.parse(await fs.promises.readFile(ttsHistory.srtJsonPath, 'utf-8'));
|
||||
|
||||
// 根据标点符号和换行符分割文案
|
||||
// 更新后的正则表达式,匹配所有中文和英文的标点符号以及换行符
|
||||
const parts = text.match(
|
||||
/[^,。!?;:“”()《》,.!?;:"()<>$$$$\n]+[,。!?;:“”()《》,.!?;:"()<>$$$$\n]*/g
|
||||
);
|
||||
// 初始化 SRT 内容
|
||||
let srtContent = "";
|
||||
let index = 1;
|
||||
|
||||
// 函数用于去掉文本末尾的标点符号
|
||||
const removeTrailingPunctuation = (text: string) => {
|
||||
return text.replace(/[\s,。!?;:“”()《》,.!?;:"()<>$$$$]+$/, "");
|
||||
};
|
||||
|
||||
// 配对时间轴和文案分段
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
let startTime =
|
||||
srtJson[i * 2]?.start || srtJson[srtJson.length - 1].start;
|
||||
let endTime =
|
||||
srtJson[i * 2 + 1]?.end || srtJson[srtJson.length - 1].end;
|
||||
|
||||
// 去掉文案末尾的标点符号
|
||||
const cleanedPart = removeTrailingPunctuation(part.trim());
|
||||
|
||||
// 将时间格式化为 SRT 格式
|
||||
const formatTime = (ms) => {
|
||||
const date = new Date(ms);
|
||||
const hours = String(date.getUTCHours()).padStart(2, "0");
|
||||
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
||||
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
|
||||
const milliseconds = String(date.getUTCMilliseconds()).padStart(3, "0");
|
||||
return `${hours}:${minutes}:${seconds},${milliseconds}`;
|
||||
};
|
||||
|
||||
// 生成 SRT 片段
|
||||
srtContent += `${index}\n${formatTime(startTime)} --> ${formatTime(
|
||||
endTime
|
||||
)}\n${cleanedPart}\n\n`;
|
||||
index++;
|
||||
}
|
||||
|
||||
console.log(srtContent);
|
||||
// 将数据写入srt文件
|
||||
let srtPath = path.join(define.tts_path, `${ttsHistory.id}/${ttsHistory.id}.srt`)
|
||||
await fs.promises.writeFile(srtPath, srtContent, 'utf-8')
|
||||
// 更新配音历史
|
||||
this.ttsService.UpdetateTTSHistory(ttsHistory.id, { srtPath: path.relative(define.tts_path, srtPath) })
|
||||
// 返回成功
|
||||
return srtPath;
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 配音历史记录相关
|
||||
|
||||
/**
|
||||
* 获取配音的历史记录
|
||||
* @param queryCondition 查询的条件
|
||||
* @returns
|
||||
*/
|
||||
async GetTTSHistoryData(queryCondition: tts.TTSHistoryQueryParams): Promise<GeneralResponse.SuccessItem | GeneralResponse.ErrorItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
let res = this.ttsService.GetTTSHistory(queryCondition);
|
||||
return successMessage(res, "获取配音历史任务成功", 'TTS_GetTTSHistoryData')
|
||||
} catch (error) {
|
||||
return errorMessage('查询配音历史失败,错误信息:' + error.message, 'TTS_GetTTSHistoryData')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配音历史
|
||||
* @param ttsId 要删除的ID
|
||||
* @returns
|
||||
*/
|
||||
async DeleteTTSHistory(ttsId: string): Promise<GeneralResponse.ErrorItem | GeneralResponse.SuccessItem> {
|
||||
try {
|
||||
await this.InitService()
|
||||
// 先删除数据库中数据,然后删除文件
|
||||
let ttsHistory = this.ttsService.GetTTSHistoryById(ttsId)
|
||||
|
||||
this.ttsService.DeleteTTSHistory(ttsId);
|
||||
// 删除文件
|
||||
let ttsDir = path.join(define.tts_path, ttsId)
|
||||
await DeleteFolderAllFile(ttsDir, true);
|
||||
return successMessage(ttsId, '删除配音历史成功', 'TTS_DeleteTTSHistory')
|
||||
} catch (error) {
|
||||
return errorMessage('删除配音历史失败,错误信息:' + error.message, 'TTS_DeleteTTSHistory')
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
30
src/model/tts.d.ts
vendored
Normal file
30
src/model/tts.d.ts
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
import { TTSSelectModel } from "../define/enum/tts"
|
||||
|
||||
declare namespace tts {
|
||||
type TTSHistoryQueryParams = {
|
||||
page?: number // 当前页数
|
||||
pageSize?: number // 每页显示的数量
|
||||
name?: string // 配音名称
|
||||
id?: string // 配音ID
|
||||
}
|
||||
|
||||
type TTSModel = {
|
||||
id?: string
|
||||
no?: number
|
||||
textPath?: string // 保存生成文本的地址
|
||||
name?: string // 一把就是配音的前几个字
|
||||
ttsPath?: string // 生成的配置文件地址
|
||||
srtPath?: string // 生成的SRT地址
|
||||
selectModel?: TTSSelectModel // 选择模式
|
||||
hasSrt?: boolean
|
||||
srtJsonPath?: string
|
||||
createTime?: Date
|
||||
updateTime?: Date
|
||||
}
|
||||
|
||||
type TTSModelRes = {
|
||||
ttsList: TTSModel[]
|
||||
ttsCount: number
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,6 +9,17 @@ const tts = {
|
||||
SaveTTSConfig: async (data) => await ipcRenderer.invoke(DEFINE_STRING.TTS.SAVE_TTS_CONFIG, data),
|
||||
|
||||
// 生成音频
|
||||
GenerateAudio: async (text) => await ipcRenderer.invoke(DEFINE_STRING.TTS.GENERATE_AUDIO, text)
|
||||
GenerateAudio: async (text) => await ipcRenderer.invoke(DEFINE_STRING.TTS.GENERATE_AUDIO, text),
|
||||
|
||||
// 生成SRT字幕
|
||||
GenerateSrt: async (text) => await ipcRenderer.invoke(DEFINE_STRING.TTS.GENERATE_SRT, text),
|
||||
|
||||
// 删除配音历史记录
|
||||
DeleteTTSHistory: async (ttsId) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.TTS.DELETE_TTS_HISTORY, ttsId),
|
||||
|
||||
// 获取生成音频的历史记录
|
||||
GetTTSHistoryData: async (queryCondition) =>
|
||||
await ipcRenderer.invoke(DEFINE_STRING.TTS.GET_TTS_HISTORY_DATA, queryCondition)
|
||||
}
|
||||
export { tts }
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
show-trigger
|
||||
@collapse="collapsed = true"
|
||||
@expand="collapsed = false"
|
||||
style="position: relative"
|
||||
>
|
||||
<n-menu
|
||||
:collapsed="collapsed"
|
||||
@ -18,7 +19,8 @@
|
||||
:options="menuOptions"
|
||||
:render-icon="renderMenuIcon"
|
||||
:expand-icon="expandIcon"
|
||||
/>
|
||||
>
|
||||
</n-menu>
|
||||
</n-layout-sider>
|
||||
<n-layout-content content-style="padding: 20px 5px 5px 20px; height:100%">
|
||||
<!-- <Setting></Setting> -->
|
||||
@ -52,7 +54,7 @@ import {
|
||||
DuplicateOutline,
|
||||
GridOutline,
|
||||
RadioOutline,
|
||||
BookOutline,
|
||||
BookOutline
|
||||
} from '@vicons/ionicons5'
|
||||
import CheckMachineId from '../Components/CheckMachineId.vue'
|
||||
import { DEFINE_STRING } from '../../../../define/define_string'
|
||||
@ -242,7 +244,8 @@ export default defineComponent({
|
||||
{
|
||||
to: {
|
||||
name: 'gptCopywriting'
|
||||
}
|
||||
},
|
||||
class: 'router-link-a'
|
||||
},
|
||||
{
|
||||
default: () => '文案处理'
|
||||
@ -382,6 +385,36 @@ export default defineComponent({
|
||||
// ]
|
||||
|
||||
// },
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'lai_api'
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => 'API服务'
|
||||
}
|
||||
),
|
||||
key: 'lai_api'
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'TTS_Services'
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => '语音服务'
|
||||
}
|
||||
),
|
||||
key: 'TTS_Services'
|
||||
},
|
||||
{
|
||||
label: '设置',
|
||||
key: 'setting',
|
||||
@ -453,39 +486,13 @@ export default defineComponent({
|
||||
key: 'mj_setting'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'lai_api'
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => 'API服务'
|
||||
}
|
||||
),
|
||||
key: 'lai_api'
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'TTS_Services'
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => '语音服务'
|
||||
}
|
||||
),
|
||||
key: 'TTS_Services'
|
||||
}
|
||||
]
|
||||
|
||||
function renderIcon(icon) {
|
||||
return () => h(NIcon, null, { default: () => h(icon) })
|
||||
}
|
||||
|
||||
return {
|
||||
renderMenuIcon,
|
||||
menuOptions,
|
||||
|
||||
@ -185,7 +185,9 @@ export default defineComponent({
|
||||
chinese_prompt: characterData.value.chinese_prompt,
|
||||
prompt: characterData.value.prompt,
|
||||
image_url: characterData.value.image_url,
|
||||
show_image: characterData.value.show_image,
|
||||
show_image: characterData.value.show_image
|
||||
? characterData.value.show_image.split('?t')[0]
|
||||
: null,
|
||||
cref_cw: characterData.value.cref_cw,
|
||||
lora: characterData.value.lora,
|
||||
lora_weight: characterData.value.lora_weight
|
||||
@ -194,6 +196,9 @@ export default defineComponent({
|
||||
characterData.value['children'] = children
|
||||
console.log(characterData.value)
|
||||
characterData.value['type'] = 'character_main'
|
||||
characterData.value['show_image'] = characterData.value.show_image
|
||||
? characterData.value.show_image.split('?t')[0]
|
||||
: null
|
||||
// 开始保存
|
||||
await window.mj.SaveTagPropertyData(
|
||||
[JSON.stringify(characterData.value), 'character_tags'],
|
||||
|
||||
@ -26,11 +26,6 @@
|
||||
<n-form-item label="语调">
|
||||
<n-input-number v-model:value="settingStore.ttsSetting.edgeTTS.pitch" :min="0" :max="100" />
|
||||
</n-form-item>
|
||||
<n-form-item label="生成SRT">
|
||||
<n-checkbox v-model:checked="settingStore.ttsSetting.edgeTTS.saveSubtitles">
|
||||
复选框
|
||||
</n-checkbox>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
156
src/renderer/src/components/TTS/TTSHistory.vue
Normal file
156
src/renderer/src/components/TTS/TTSHistory.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div id="tts-history">
|
||||
<n-data-table
|
||||
remote
|
||||
ref="table"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:row-key="rowKey"
|
||||
@update:page="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, defineComponent, onUnmounted, toRaw, reactive, h } from 'vue'
|
||||
import { useMessage, NDataTable } from 'naive-ui'
|
||||
import TTSHistoryAction from './TTSHistoryAction.vue'
|
||||
import TTSHistoryShowPath from './TTSHistoryShowPath.vue'
|
||||
|
||||
let props = defineProps({
|
||||
height: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
let height = ref(props.height)
|
||||
let data = ref([])
|
||||
let message = useMessage()
|
||||
let loading = ref(false)
|
||||
let rowKey = (rowData) => {
|
||||
return rowData.id
|
||||
}
|
||||
const pagination = reactive({
|
||||
page: 0,
|
||||
pageCount: 0,
|
||||
pageSize: 20,
|
||||
prefix({ itemCount }) {
|
||||
return `Total is ${itemCount}.`
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// 加载的时候设置高度和滚动条
|
||||
let div = document.getElementById('tts-history')
|
||||
div.style.height = height.value - 70 + 'px'
|
||||
div.style.overflow = 'auto'
|
||||
await GetTTSHistoryData(pagination.page)
|
||||
})
|
||||
|
||||
async function GetTTSHistoryData(currentPage) {
|
||||
// 这边加载数据
|
||||
if (!loading.value) {
|
||||
let res = await window.tts.GetTTSHistoryData({
|
||||
page: currentPage,
|
||||
pageSize: pagination.pageSize
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
}
|
||||
data.value = res.data.ttsList
|
||||
pagination.page = currentPage
|
||||
pagination.pageCount = Math.ceil(res.data.ttsCount / pagination.pageSize)
|
||||
pagination.itemCount = res.data.ttsCount
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePageChange(currentPage) {
|
||||
await GetTTSHistoryData(currentPage)
|
||||
}
|
||||
|
||||
async function handleUpdateRow(row, index) {
|
||||
if (row == null) {
|
||||
debugger
|
||||
// data.value.splice(index, 1)
|
||||
// 直接重新加载吧
|
||||
await GetTTSHistoryData(pagination.page)
|
||||
} else {
|
||||
data.value[index] = row
|
||||
}
|
||||
}
|
||||
|
||||
let columns = ref([
|
||||
{
|
||||
title: 'No.',
|
||||
key: 'no',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '名字',
|
||||
key: 'name',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '配音地址',
|
||||
key: 'ttsPath',
|
||||
render(row, index) {
|
||||
return h(TTSHistoryShowPath, {
|
||||
text: row.ttsPath
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '字幕地址',
|
||||
key: 'srtPath',
|
||||
render(row, index) {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
className: 'tts-path',
|
||||
onClick: () => {
|
||||
window.system.OpenFile(row.srtPath)
|
||||
}
|
||||
},
|
||||
row.srtPath
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createTime',
|
||||
width: 200,
|
||||
render(row, index) {
|
||||
return h('span', row.createTime.toLocaleString())
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
key: 'action',
|
||||
width: 280,
|
||||
render(row, index) {
|
||||
return h(TTSHistoryAction, {
|
||||
row: row,
|
||||
index: index,
|
||||
onUpdateRow: (data, dataIndex) => handleUpdateRow(data, dataIndex)
|
||||
})
|
||||
}
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tts-path {
|
||||
overflow: hidden;
|
||||
max-width: 300px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tts-path:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
74
src/renderer/src/components/TTS/TTSHistoryAction.vue
Normal file
74
src/renderer/src/components/TTS/TTSHistoryAction.vue
Normal file
@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
size="small"
|
||||
style="margin-right: 5px"
|
||||
@click="GenerateSrt"
|
||||
>生成字幕</n-button
|
||||
>
|
||||
<n-button
|
||||
:color="softwareStore.SoftColor.BROWN_YELLOW"
|
||||
size="small"
|
||||
style="margin-right: 5px"
|
||||
@click="OpenFolder"
|
||||
>打开文件夹</n-button
|
||||
>
|
||||
<n-button type="error" size="small" style="margin-right: 5px" @click="DeleteTTSHistory"
|
||||
>删除</n-button
|
||||
>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useMessage, NButton } from 'naive-ui'
|
||||
import { useSoftwareStore } from '../../../../stores/software'
|
||||
let message = useMessage()
|
||||
let softwareStore = useSoftwareStore()
|
||||
let props = defineProps({
|
||||
row: undefined,
|
||||
index: undefined
|
||||
})
|
||||
const emit = defineEmits(['update-row'])
|
||||
|
||||
// 调用生成SRT的方法
|
||||
async function GenerateSrt() {
|
||||
let res = await window.tts.GenerateSrt(props.row.id)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
return
|
||||
} else {
|
||||
// 这边要处理返回的数据
|
||||
emit('update-row', res.data, props.index)
|
||||
message.success(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开对应的文件夹
|
||||
*/
|
||||
async function OpenFolder() {
|
||||
let res = await window.system.OpenFolder({
|
||||
folderPath: props.row.ttsPath,
|
||||
baseProject: false,
|
||||
dirFloder: true
|
||||
})
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用删除数据的方法
|
||||
async function DeleteTTSHistory() {
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '删除中...'
|
||||
let res = await window.tts.DeleteTTSHistory(props.row.id)
|
||||
if (res.code == 0) {
|
||||
message.error(res.message)
|
||||
} else {
|
||||
// 这边要处理返回的数据
|
||||
emit('update-row', null, props.index)
|
||||
message.success(res.message)
|
||||
}
|
||||
softwareStore.spin.spinning = false
|
||||
}
|
||||
</script>
|
||||
35
src/renderer/src/components/TTS/TTSHistoryShowPath.vue
Normal file
35
src/renderer/src/components/TTS/TTSHistoryShowPath.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<div @click="Click" class="show-text-ellipsis">{{ text }}</div>
|
||||
</template>
|
||||
<span>{{ text }}</span>
|
||||
</n-popover>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { NPopover } from 'naive-ui'
|
||||
let props = defineProps({
|
||||
text: undefined
|
||||
})
|
||||
let text = ref(props.text)
|
||||
|
||||
function Click() {
|
||||
// window.open(text.value)
|
||||
window.system.OpenFile(text.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.show-text-ellipsis {
|
||||
overflow: hidden;
|
||||
max-width: 300px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
.show-text-ellipsis:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@ -103,6 +103,7 @@ import { useSoftwareStore } from '../../../../stores/software'
|
||||
import { AddCircleOutline } from '@vicons/ionicons5'
|
||||
import InputDialogContent from '../Original/Components/InputDialogContent.vue'
|
||||
import { useSettingStore } from '../../../../stores/setting'
|
||||
import TTSHistory from './TTSHistory.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -116,7 +117,8 @@ export default defineComponent({
|
||||
NPopover,
|
||||
AddCircleOutline,
|
||||
NIcon,
|
||||
InputDialogContent
|
||||
InputDialogContent,
|
||||
TTSHistory
|
||||
},
|
||||
|
||||
setup() {
|
||||
@ -259,6 +261,7 @@ export default defineComponent({
|
||||
softwareStore.spin.spinning = true
|
||||
softwareStore.spin.tip = '正在合成音频,请稍等...'
|
||||
|
||||
// 保存配置信息
|
||||
let saveRes = await window.tts.SaveTTSConfig(toRaw(settingStore.ttsSetting))
|
||||
if (saveRes.code == 0) {
|
||||
softwareStore.spin.spinning = false
|
||||
@ -266,16 +269,32 @@ export default defineComponent({
|
||||
return
|
||||
}
|
||||
|
||||
// 开始真正的合成音频
|
||||
let generateRes = await window.tts.GenerateAudio(text.value)
|
||||
if (generateRes.code == 0) {
|
||||
softwareStore.spin.spinning = false
|
||||
message.error(generateRes.message)
|
||||
return
|
||||
}
|
||||
audioUrl.value = generateRes.data
|
||||
debugger
|
||||
audioUrl.value = generateRes.mp3Path
|
||||
softwareStore.spin.spinning = false
|
||||
}
|
||||
|
||||
// 显示配音的历史记录
|
||||
function ShowHistory() {
|
||||
let dialogWidth = window.innerWidth * 0.8
|
||||
let dialogHeight = window.innerHeight * 0.9
|
||||
dialog.create({
|
||||
title: '配音历史记录',
|
||||
showIcon: false,
|
||||
closeOnEsc: false,
|
||||
content: () => h(TTSHistory, { height: dialogHeight }),
|
||||
style: `width : ${dialogWidth}px; min-height : ${dialogHeight}px;matgin-right: 0px`,
|
||||
maskClosable: false
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
text,
|
||||
azurettsRef,
|
||||
@ -284,6 +303,7 @@ export default defineComponent({
|
||||
writeSetting,
|
||||
softwareStore,
|
||||
ModifySplitChar,
|
||||
ShowHistory,
|
||||
SaveTTSConfig,
|
||||
FormatWord,
|
||||
ClearText,
|
||||
|
||||
@ -43,6 +43,7 @@ export const useSoftwareStore = defineStore('software', {
|
||||
async GetComponentSize() {
|
||||
debugger
|
||||
if (this.componentSize.length == 0) {
|
||||
//@ts-ignore
|
||||
let res = await window.setting.GetComponentSize()
|
||||
this.componentSize = res.data
|
||||
}
|
||||
@ -54,6 +55,7 @@ export const useSoftwareStore = defineStore('software', {
|
||||
// 将当前的software数据保存到数据库中
|
||||
async SaveSoftware() {
|
||||
// 保存数据
|
||||
// @ts-ignore
|
||||
return await window.setting.SaveSoftWareSetting(JSON.parse(JSON.stringify(this.softWare)))
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user