From 6d7ace6a94991f13f2a8ed4bdde52dfcc37451cc Mon Sep 17 00:00:00 2001 From: fanxb Date: Thu, 16 Feb 2023 21:08:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=AE=8C=E5=96=84=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openRenamerBackend/api/AutoPlanApi.ts | 41 +-- .../service/ApplicationRuleService.ts | 6 + openRenamerBackend/service/AutoPlanService.ts | 102 +++++++- openRenamerBackend/util/MediaUtil.ts | 23 ++ .../src/components/rules/RuleBlock.vue | 36 +-- .../src/views/auto/components/editForm.vue | 245 +++++++++--------- 6 files changed, 266 insertions(+), 187 deletions(-) create mode 100644 openRenamerBackend/util/MediaUtil.ts diff --git a/openRenamerBackend/api/AutoPlanApi.ts b/openRenamerBackend/api/AutoPlanApi.ts index 355eece..9af5459 100644 --- a/openRenamerBackend/api/AutoPlanApi.ts +++ b/openRenamerBackend/api/AutoPlanApi.ts @@ -1,49 +1,14 @@ import { Context } from "koa"; -import FileService from "../service/FileService"; -import config from "../config"; +import AutoPlanService from "../service/AutoPlanService"; const router = {}; /** * 获取目录下的文件列表 */ -router["GET /file/query"] = async function (ctx: Context) { - ctx.body = await FileService.readPath(ctx.query.path as string, ctx.query.showHidden === '1'); +router["POST /autoPlan/save"] = async function (ctx: Context) { + ctx.body = await AutoPlanService.saveAutoConfig(ctx.request.body); }; -/** - *是否windows - */ -router['GET /file/isWindows'] = async function (ctx: Context) { - ctx.body = config.isWindows; -} - -/** - * 检查路径是否存在 - */ -router["GET /file/path/exist"] = async function (ctx: Context) { - ctx.body = await FileService.checkExist(ctx.query.path as string); -}; - -/** - * 收藏路径 - */ -router["POST /file/path/save"] = async function (ctx: Context) { - ctx.body = await FileService.savePath(ctx.request.body); -}; - -/** - * 获取收藏路径 - */ -router["GET /file/path"] = async function (ctx: Context) { - ctx.body = await FileService.getSaveList(); -}; - -/** - * 获取收藏路径 - */ -router["DELETE /file/path/delete"] = async function (ctx: Context) { - ctx.body = await FileService.deleteOne(ctx.query.id); -}; export default router; diff --git a/openRenamerBackend/service/ApplicationRuleService.ts b/openRenamerBackend/service/ApplicationRuleService.ts index e1f46c9..26f3a4d 100644 --- a/openRenamerBackend/service/ApplicationRuleService.ts +++ b/openRenamerBackend/service/ApplicationRuleService.ts @@ -4,6 +4,7 @@ import GlobalConfigDao from '../dao/GlobalConfigDao'; import { DEFAULT_TEMPLETE_ID } from '../entity/constants/GlobalConfigCodeConstant'; import GlobalConfig from '../entity/po/GlobalConfig'; +import ErrorHelper from '../util/ErrorHelper'; class ApplicationRuleService { @@ -25,6 +26,11 @@ class ApplicationRuleService { } static async deleteById(id: number): Promise { + //禁止删除默认模板 + let idStr = await GlobalConfigDao.getByCode(DEFAULT_TEMPLETE_ID); + if (id.toString() === idStr) { + throw ErrorHelper.Error400("禁止删除默认模板"); + } await ApplicationRuleDao.delete(id); } diff --git a/openRenamerBackend/service/AutoPlanService.ts b/openRenamerBackend/service/AutoPlanService.ts index f998dd3..a4ef5ec 100644 --- a/openRenamerBackend/service/AutoPlanService.ts +++ b/openRenamerBackend/service/AutoPlanService.ts @@ -2,16 +2,15 @@ import config from '../config'; import * as path from 'path'; import * as fs from 'fs-extra'; -import ProcessHelper from '../util/ProcesHelper'; -import FileObj from '../entity/vo/FileObj'; -import SavePathDao from '../dao/SavePathDao'; -import SavePath from '../entity/po/SavePath'; import AutoPlanConfigDto from '../entity/dto/AutoPlanConfigDto'; import GlobalConfig from 'entity/po/GlobalConfig'; import GlobalConfigService from './GlobalConfigService'; -import ErrorHelper from 'util/ErrorHelper'; - +import ErrorHelper from '../util/ErrorHelper'; +import TimeUtil from '../util/TimeUtil'; +import { isSub, isVideo } from '../util/MediaUtil'; +import log from '../util/LogUtil'; const autoConfigCode = "autoConfig"; +let isReadDir = false; /** * 需要处理的文件 */ @@ -34,12 +33,25 @@ class AutoPlanService { } else { autoConfig = JSON.parse(str); } + setTimeout(async () => { + while (true) { + try { + await TimeUtil.sleep(1000); + await work(); + } catch (err) { + console.log(err); + } + } + }, 1000); } /** * 保存配置 */ static async saveAutoConfig(body: AutoPlanConfigDto): Promise { + if (isReadDir) { + throw ErrorHelper.Error400("正在处理中,请稍后再试"); + } if (body.start) { if (body.paths.length == 0) { throw ErrorHelper.Error400("视频路径为空"); @@ -56,28 +68,48 @@ class AutoPlanService { await GlobalConfigService.insertOrReplace(configBody); autoConfig = body; if (body.start && !body.ignoreExist) { - await readDir(body.paths); + setTimeout(async () => { + isReadDir = true; + try { + await readDir(body.paths); + } finally { + isReadDir = false; + } + }, 1); } } - - - } +/** + * 读取目录,获取文件列表 + * @param dirList 要读取的目录 + */ async function readDir(dirList: Array): Promise { if (!dirList) { return; } for (let i in dirList) { let pathStr = dirList[i]; - if (checkIgnore(pathStr)) { + if (checkIgnore(path.basename(pathStr))) { continue; } if (!(await fs.stat(pathStr)).isDirectory()) { - needDeal.push(pathStr); + let fileName = path.basename(pathStr); + let strs = fileName.split('.').reverse(); + if (strs.length > 0 && (isSub(strs[0]) || isVideo(strs[1]))) { + needDeal.push(pathStr); + } continue; } - await readDir((await fs.readdir(pathStr)).map(item => path.join(pathStr, item))); + let childs = null; + try { + childs = await fs.readdir(pathStr); + } catch (error) { + console.warn("读取报错:{}", error); + } + if (childs != null) { + await readDir(childs.map(item => path.join(pathStr, item))); + } } } @@ -93,4 +125,48 @@ function checkIgnore(str: string): boolean { return false; } +/** + * 开始处理 + */ +async function work() { + if (autoConfig == null || !autoConfig.start) { + return; + } + while (needDeal.length > 0) { + let file = needDeal.pop(); + try { + await dealOnePath(file); + } catch (error) { + log.error("处理文件报错:{}", file); + console.error(error); + } + } +} + +/** + * 处理一个文件路径 + * @param filePath 路径 + * @returns + */ +async function dealOnePath(filePath: string) { + let exist = await fs.pathExists(filePath); + if (!exist) { + return; + } + let basePath = null; + for (let i in autoConfig.paths) { + if (filePath.startsWith(autoConfig.paths[i])) { + basePath = autoConfig.paths[i]; + break; + } + } + if (basePath == null) { + log.warn("无法识别的文件:{}", filePath); + return; + } + let relativePath = filePath.replace(basePath, ""); + let pathArrs = relativePath.split(path.sep).filter(item => item.length > 0); + +} + export default AutoPlanService; diff --git a/openRenamerBackend/util/MediaUtil.ts b/openRenamerBackend/util/MediaUtil.ts new file mode 100644 index 0000000..4303aca --- /dev/null +++ b/openRenamerBackend/util/MediaUtil.ts @@ -0,0 +1,23 @@ +const videoSet = new Set(["flv", 'avi', 'wmv', 'dat', 'vob', 'mpg', 'mpeg', 'mp4', '3gp', '3g2', 'mkv', 'rm', 'rmvb', 'mov', 'qt', 'ogg', 'ogv', 'oga', 'mod']); +/** + * 判断文件后缀是否为视频类型 + * @param str 文件后缀 + */ +export function isVideo(str: string) { + if (!str) { + return false; + } + return videoSet.has(str.toLowerCase()); +} + +const subSet = new Set(['sub', 'sst', 'son', 'srt', 'ssa', 'ass', 'smi', 'psb', 'pjs', 'stl', 'tts', 'vsf', 'zeg']); +/** + * 判断文件是否为字幕文件 + * @param str 文件后缀 + */ +export function isSub(str: string) { + if (!str) { + return false; + } + return subSet.has(str.toLowerCase()); +} \ No newline at end of file diff --git a/openRenamerFront/src/components/rules/RuleBlock.vue b/openRenamerFront/src/components/rules/RuleBlock.vue index eed73de..4dadcd8 100644 --- a/openRenamerFront/src/components/rules/RuleBlock.vue +++ b/openRenamerFront/src/components/rules/RuleBlock.vue @@ -23,9 +23,9 @@ - - - + @@ -62,7 +62,7 @@ export default { Top, Bottom, }, - data () { + data() { return { addRuleDialogShow: false, //是否显示新增规则弹窗 ruleTemplateShow: false, //是否显示选择规则模板弹窗 @@ -73,11 +73,11 @@ export default { }, computed: { //选中的规则 - checkedRules () { + checkedRules() { return this.ruleList.filter((item) => item.checked); }, }, - async created () { + async created() { //如果外部传入了规则 if (this.rules != undefined) { this.ruleList = JSON.parse(JSON.stringify(this.rules)); @@ -87,26 +87,32 @@ export default { await this.ruleUpdate(); } }, + watch: { + rules: function (newVal, oldVal) { + console.log("rules变化", newVal); + this.ruleList = JSON.parse(JSON.stringify(newVal)); + }, + }, methods: { //规则更新 - ruleUpdate () { + ruleUpdate() { let temp = this.ruleList.filter((item) => !item.blocked); this.$emit("ruleUpdate", temp); }, //模板内容提交 - async templateSubmit () { + async templateSubmit() { this.chosedTemplate.content = JSON.stringify(this.ruleList); await HttpUtil.post("/applicationRule", null, this.chosedTemplate); this.$message.success("操作成功"); }, //切换模板 - async templateUpdate (newVal) { + async templateUpdate(newVal) { this.ruleList = JSON.parse(newVal.content); this.ruleUpdate(); this.ruleTemplateShow = false; }, //新增规则 - async ruleAdd (data) { + async ruleAdd(data) { if (this.editRule != null) { let index = this.ruleList.indexOf(this.editRule); this.ruleList.splice(index, 1, data); @@ -119,7 +125,7 @@ export default { this.addRuleDialogShow = false; }, //禁用/启用 - async block () { + async block() { this.ruleList .filter((item) => item.checked) .forEach((item) => { @@ -129,17 +135,17 @@ export default { await this.ruleUpdate(); }, //删除规则 - async deleteRule () { + async deleteRule() { this.ruleList = this.ruleList.filter((item) => !item.checked); this.ruleUpdate(); }, //编辑规则 - editClick (rule) { + editClick(rule) { this.editRule = rule && rule.data ? rule : this.checkedRules[0]; this.addRuleDialogShow = true; }, //移动规则 - async move (type) { + async move(type) { let index = this.ruleList.indexOf(this.checkedRules[0]); let newIndex; if (type == "top") { @@ -160,7 +166,7 @@ export default { await this.ruleUpdate(); }, //规则弹窗关闭 - ruleDialogClose () { + ruleDialogClose() { this.editRule = null; this.addRuleDialogShow = false; }, diff --git a/openRenamerFront/src/views/auto/components/editForm.vue b/openRenamerFront/src/views/auto/components/editForm.vue index 007889e..dd005ee 100644 --- a/openRenamerFront/src/views/auto/components/editForm.vue +++ b/openRenamerFront/src/views/auto/components/editForm.vue @@ -1,161 +1,164 @@