From 5f5d8fc7a3dac662c90505f39c4ecf7e69eed73c Mon Sep 17 00:00:00 2001 From: thsrite Date: Fri, 26 Apr 2024 14:58:26 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E5=AA=92=E4=BD=93=E5=BA=93=E5=85=83?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=88=B7=E6=96=B0=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + icons/media.png | Bin 0 -> 5461 bytes package.json | 11 ++ plugins/metarefresh/__init__.py | 289 ++++++++++++++++++++++++++++++++ 4 files changed, 301 insertions(+) create mode 100644 icons/media.png create mode 100644 plugins/metarefresh/__init__.py diff --git a/README.md b/README.md index 8fe8629..96fc722 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,4 @@ MoviePilot三方插件市场:https://github.com/thsrite/MoviePilot-Plugins/ - 插件彻底卸载 1.0 - 实时软连接 1.3 - 订阅规则自动填充 2.5 +- 媒体库元数据刷新 1.0 diff --git a/icons/media.png b/icons/media.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8b5ebfc9d4e9984b36792b755b23e648d0685e GIT binary patch literal 5461 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 Z%Mpn6l-iPt>oiVEXP{$>t=DoQ{SWid bool: + return self._enabled + + def __update_config(self): + self.update_config( + { + "onlyonce": self._onlyonce, + "cron": self._cron, + "enabled": self._enabled, + "days": self._days, + "servers": self._servers, + } + ) + + def refresh(self): + """ + 刷新媒体库元数据 + """ + if not self._servers: + logger.error("没有选择刷新媒体服务器") + return + + # 获取days内入库的媒体 + current_date = datetime.now() + # 计算几天前的日期 + target_date = current_date - timedelta(days=int(self._days)) + transferhistorys = TransferHistoryOper().list_by_date(target_date) + if not transferhistorys: + logger.error(f"{self._days}天内没有媒体库入库记录") + return + + logger.info(f"开始刷新媒体库元数据,最近{self._days}天内入库媒体:{len(transferhistorys)}个") + # 刷新媒体库 + items = [ + RefreshMediaItem( + title=transferinfo.title, + year=transferinfo.year, + type=transferinfo.type, + category=transferinfo.category, + target_path=transferinfo.dest + ) + for transferinfo in transferhistorys + ] + + if "emby" in self._servers and "emby" in settings.MEDIASERVER: + Emby().refresh_library_by_items(items) + if "jellyfin" in self._servers and "jellyfin" in settings.MEDIASERVER: + # FIXME Jellyfin未找到刷新单个项目的API + Jellyfin().refresh_root_library() + if "plex" in self._servers and "plex" in settings.MEDIASERVER: + Plex().refresh_library_by_items(items) + + @staticmethod + def get_command() -> List[Dict[str, Any]]: + pass + + def get_api(self) -> List[Dict[str, Any]]: + pass + + def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: + """ + 拼装插件配置页面,需要返回两块数据: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, + 'md': 4 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'cron', + 'label': '执行周期', + 'placeholder': '5位cron表达式,留空自动' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'days', + 'label': '最新入库天数' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'multiple': True, + 'chips': True, + 'model': 'servers', + 'label': '媒体服务器', + 'items': [ + { + "title": "emby", + "vale": "emby" + }, + { + "title": "jellyfin", + "vale": "jellyfin" + }, + { + "title": "plex", + "vale": "plex" + } + ] + } + } + ] + }, + ], + }, + ], + } + ], { + "enabled": False, + "onlyonce": False, + "cron": "5 1 * * *", + "days": 5, + "servers": [] + } + + def get_page(self) -> List[dict]: + pass + + def stop_service(self): + """ + 退出插件 + """ + try: + if self._scheduler: + self._scheduler.remove_all_jobs() + if self._scheduler.running: + self._scheduler.shutdown() + self._scheduler = None + except Exception as e: + logger.error("退出插件失败:%s" % str(e))