From b0c41fdb1dd5a82227efda5ebe5b75cd3355666f Mon Sep 17 00:00:00 2001 From: thsrite Date: Wed, 3 Jan 2024 19:28:17 +0800 Subject: [PATCH] =?UTF-8?q?fix=20=E7=9F=AD=E5=89=A7=E5=88=AE=E5=89=8A?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8E=92=E9=99=A4=E5=85=B3=E9=94=AE=E8=AF=8D?= =?UTF-8?q?=EF=BC=8C=E7=AB=8B=E5=8D=B3=E6=89=A7=E8=A1=8C=E4=B8=80=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/ShortPlayMonitor.md | 15 +- package.json | 2 +- plugins/shortplaymonitor/__init__.py | 286 ++++++++++++++++----------- 4 files changed, 185 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 7b3de3b..65c4584 100644 --- a/README.md +++ b/README.md @@ -24,5 +24,5 @@ MoviePilot三方插件市场:https://github.com/thsrite/MoviePilot-Plugins/ - [订阅提醒 1.1](docs%2FSubscribeReminder.md) - [Emby观影报告 1.4](docs%2FEmbyReporter.md) - [豆瓣明星热映订阅 1.2](docs%2FActorSubscribe.md) -- [短剧刮削 1.0](docs%2FShortPlayMonitor.md) +- [短剧刮削 1.1](docs%2FShortPlayMonitor.md) diff --git a/docs/ShortPlayMonitor.md b/docs/ShortPlayMonitor.md index f777f69..9e7aef4 100644 --- a/docs/ShortPlayMonitor.md +++ b/docs/ShortPlayMonitor.md @@ -2,4 +2,17 @@ ### 更新记录 -- 1.0 监控视频短剧创建,刮削 \ No newline at end of file +- 1.1 增加排除关键词,立即执行一次 +- 1.0 监控视频短剧创建,刮削 + +监控方式: + +- fast:性能模式,内部处理系统操作类型选择最优解 +- compatibility:兼容模式,目录同步性能降低且NAS不能休眠,但可以兼容挂载的远程共享目录如SMB (建议使用) + +是否重命名 +- true 自定义识别词 +- false + +封面比例: +2:3 \ No newline at end of file diff --git a/package.json b/package.json index 5eff358..e2e118d 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "ShortPlayMonitor": { "name": "短剧刮削", "description": "监控视频短剧创建,刮削。", - "version": "1.0", + "version": "1.1", "icon": "Amule_B.png", "author": "thsrite", "level": 1 diff --git a/plugins/shortplaymonitor/__init__.py b/plugins/shortplaymonitor/__init__.py index a1a5431..1858ed5 100644 --- a/plugins/shortplaymonitor/__init__.py +++ b/plugins/shortplaymonitor/__init__.py @@ -1,9 +1,12 @@ import os +import re import threading +import datetime from pathlib import Path from typing import Any, List, Dict, Tuple, Optional +import pytz from apscheduler.schedulers.background import BackgroundScheduler from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer @@ -28,9 +31,6 @@ class FileMonitorHandler(FileSystemEventHandler): self._watch_path = watching_path self.file_change = file_change - # def on_any_event(self, event): - # logger.info(f"目录监控event_type {event.event_type} 路径 {event.src_path}") - def on_created(self, event): self.file_change.event_handler(event=event, source_dir=self._watch_path, event_path=event.src_path) @@ -46,7 +46,7 @@ class ShortPlayMonitor(_PluginBase): # 插件图标 plugin_icon = "Amule_B.png" # 插件版本 - plugin_version = "1.0" + plugin_version = "1.1" # 插件作者 plugin_author = "thsrite" # 作者主页 @@ -62,8 +62,8 @@ class ShortPlayMonitor(_PluginBase): _enabled = False _monitor_confs = None _onlyonce = False + _exclude_keywords = "" _observer = [] - _video_formats = ('.mp4', '.avi', '.rmvb', '.wmv', '.mov', '.mkv', '.flv', '.ts', '.webm', '.iso', '.mpg', '.m2ts') _timeline = "00:03:01" _dirconf = {} _renameconf = {} @@ -80,8 +80,9 @@ class ShortPlayMonitor(_PluginBase): if config: self._enabled = config.get("enabled") - # self._onlyonce = config.get("onlyonce") + self._onlyonce = config.get("onlyonce") self._monitor_confs = config.get("monitor_confs") + self._exclude_keywords = config.get("exclude_keywords") or "" # 停止现有任务 self.stop_service() @@ -150,22 +151,37 @@ class ShortPlayMonitor(_PluginBase): logger.error(f"{source_dir} 启动目录监控失败:{err_msg}") self.systemmessage.put(f"{source_dir} 启动目录监控失败:{err_msg}") - # # 运行一次定时服务 - # if self._onlyonce: - # logger.info("云盘监控服务启动,立即运行一次") - # self._scheduler.add_job(func=self.sync_all, trigger='date', - # run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), - # name="云盘监控全量执行") - # # 关闭一次性开关 - # self._onlyonce = False - # # 保存配置 - # self.__update_config() + # 运行一次定时服务 + if self._onlyonce: + logger.info("短剧监控服务启动,立即运行一次") + self._scheduler.add_job(func=self.sync_all, trigger='date', + run_date=datetime.datetime.now( + tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3), + name="短剧监控全量执行") + # 关闭一次性开关 + self._onlyonce = False + # 保存配置 + self.__update_config() # 启动任务 if self._scheduler.get_jobs(): self._scheduler.print_jobs() self._scheduler.start() + def sync_all(self): + """ + 立即运行一次,全量同步目录中所有文件 + """ + logger.info("开始全量同步短剧监控目录 ...") + # 遍历所有监控目录 + for mon_path in self._dirconf.keys(): + # 遍历目录下所有文件 + for file_path in SystemUtils.list_files(Path(mon_path), settings.RMT_MEDIAEXT): + self.__handle_file(is_directory=Path(file_path).is_dir(), + event_path=str(file_path), + source_dir=mon_path) + logger.info("全量同步短剧监控目录完成!") + def event_handler(self, event, source_dir: str, event_path: str): """ 处理文件变化 @@ -181,13 +197,23 @@ class ShortPlayMonitor(_PluginBase): logger.info(f"{event_path} 是回收站或隐藏的文件,跳过处理") return - # 文件发生变化 - logger.info(f"变动类型 {event.event_type} 变动路径 {event_path}") - self.__handle_file(event=event, event_path=event_path, source_dir=source_dir) + # 命中过滤关键字不处理 + if self._exclude_keywords: + for keyword in self._exclude_keywords.split("\n"): + if keyword and re.findall(keyword, event_path): + logger.info(f"{event_path} 命中过滤关键字 {keyword},不处理") + return - def __handle_file(self, event, event_path: str, source_dir: str): + # 文件发生变化 + logger.debug(f"变动类型 {event.event_type} 变动路径 {event_path}") + self.__handle_file(is_directory=event.is_directory, + event_path=event_path, + source_dir=source_dir) + + def __handle_file(self, is_directory: bool, event_path: str, source_dir: str): """ 同步一个文件 + :event.is_directory :param event_path: 事件文件路径 :param source_dir: 监控目录 """ @@ -211,7 +237,7 @@ class ShortPlayMonitor(_PluginBase): target_path = Path(target_path).parent / title # 文件夹同步创建 - if event.is_directory: + if is_directory: # 目标文件夹不存在则创建 if not Path(target_path).exists(): logger.info(f"创建目标文件夹 {target_path}") @@ -233,7 +259,8 @@ class ShortPlayMonitor(_PluginBase): logger.info(f"文件 {source_dir} 硬链接完成") # 生成缩略图 - thumb_path = self.gen_file_thumb(target_path) + thumb_path = self.gen_file_thumb(file_path=target_path, + cover_conf=cover_conf) if not (target_path.parent / "poster.jpg").exists(): SystemUtils.copy(thumb_path, target_path.parent / "poster.jpg") else: @@ -243,7 +270,7 @@ class ShortPlayMonitor(_PluginBase): logger.error(f"event_handler_created error: {e}") print(str(e)) - def gen_file_thumb(self, file_path: Path): + def gen_file_thumb(self, file_path: Path, cover_conf: str): """ 处理一个文件 """ @@ -255,7 +282,9 @@ class ShortPlayMonitor(_PluginBase): logger.info(f"缩略图已存在:{thumb_path}") return if self.get_thumb(video_path=str(file_path), - image_path=str(thumb_path), frames=self._timeline): + image_path=str(thumb_path), + cover_conf=str(cover_conf), + frames=self._timeline): logger.info(f"{file_path} 缩略图已生成:{thumb_path}") return thumb_path except Exception as err: @@ -292,7 +321,8 @@ class ShortPlayMonitor(_PluginBase): """ self.update_config({ "enabled": self._enabled, - # "onlyonce": self._onlyonce, + "exclude_keywords": self._exclude_keywords, + "onlyonce": self._onlyonce, "monitor_confs": self._monitor_confs }) @@ -311,98 +341,120 @@ class ShortPlayMonitor(_PluginBase): 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 """ return [ - { - 'component': 'VForm', - 'content': [ - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 6 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'enabled', - 'label': '启用插件', - } - } - ] - }, - # { - # 'component': 'VCol', - # 'props': { - # 'cols': 12, - # 'md': 6 - # }, - # 'content': [ - # { - # 'component': 'VSwitch', - # 'props': { - # 'model': 'onlyonce', - # 'label': '立即运行一次', - # } - # } - # ] - # } - ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12 - }, - 'content': [ - { - 'component': 'VTextarea', - 'props': { - 'model': 'monitor_confs', - 'label': '监控目录', - 'rows': 5, - 'placeholder': '监控目录#目的目录#是否重命名#封面比例' - } - } - ] - } - ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - }, - 'content': [ - { - 'component': 'VAlert', - 'props': { - 'type': 'info', - 'variant': 'tonal', - 'text': '配置说明:' - 'https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/docs/CloudStrm.md' - } - } - ] - } - ] - } - ] - } - ], { - "enabled": False, - # "relay": 3, - # "onlyonce": False, - "monitor_confs": "" - } + { + 'component': 'VForm', + 'content': [ + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'enabled', + 'label': '启用插件', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'onlyonce', + 'label': '立即运行一次', + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12 + }, + 'content': [ + { + 'component': 'VTextarea', + 'props': { + 'model': 'monitor_confs', + 'label': '监控目录', + 'rows': 5, + 'placeholder': '监控方式#监控目录#目的目录#是否重命名#封面比例' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VTextarea', + 'props': { + 'model': 'exclude_keywords', + 'label': '排除关键词', + 'rows': 2, + 'placeholder': '每一行一个关键词' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VAlert', + 'props': { + 'type': 'info', + 'variant': 'tonal', + 'text': '配置说明:' + 'https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/docs/ShortPlayMonitor.md' + } + } + ] + } + ] + } + ] + } + ], { + "enabled": False, + "onlyonce": False, + "monitor_confs": "", + "exclude_keywords": "" + } def get_page(self) -> List[dict]: pass