From 48907b60816c42aa1e239dd92859f64c4e9cb839 Mon Sep 17 00:00:00 2001 From: thsrite Date: Fri, 29 Mar 2024 09:57:17 +0800 Subject: [PATCH] =?UTF-8?q?fix=20=E8=AE=A2=E9=98=85=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E7=BB=9F=E8=AE=A11.5=E5=A2=9E=E5=8A=A0=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/SubscribeStatistic.md | 1 + package.json | 2 +- plugins/subscribestatistic/__init__.py | 408 +++++++++++++++++++------ 4 files changed, 315 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index b083fea..7f068d2 100644 --- a/README.md +++ b/README.md @@ -28,5 +28,5 @@ MoviePilot三方插件市场:https://github.com/thsrite/MoviePilot-Plugins/ - [云盘实时链接 1.5](docs%2FCloudLinkMonitor.md) - [源文件恢复 1.2](docs%2FLinkToSrc.md) - [微信消息转发 1.3](docs%2FWeChatForward.md) -- [订阅下载统计 1.4](docs%2FSubscribeStatistic.md) +- [订阅下载统计 1.5](docs%2FSubscribeStatistic.md) diff --git a/docs/SubscribeStatistic.md b/docs/SubscribeStatistic.md index f8996a8..3cd2e0f 100644 --- a/docs/SubscribeStatistic.md +++ b/docs/SubscribeStatistic.md @@ -2,6 +2,7 @@ ### 更新记录 +- 1.5 增加消息推送 - 1.4 无订阅站点也统计数量 - 1.3 fix bug - 1.2 fix bug diff --git a/package.json b/package.json index 32db836..5b9321e 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,7 @@ "SubscribeStatistic": { "name": "订阅下载统计", "description": "统计指定时间内各站点订阅及下载情况。", - "version": "1.4", + "version": "1.5", "icon": "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/subscribestatistic.png", "author": "thsrite", "level": 1 diff --git a/plugins/subscribestatistic/__init__.py b/plugins/subscribestatistic/__init__.py index 17925fb..8da998c 100644 --- a/plugins/subscribestatistic/__init__.py +++ b/plugins/subscribestatistic/__init__.py @@ -1,4 +1,5 @@ import json +from datetime import datetime, timedelta from app.db.downloadhistory_oper import DownloadHistoryOper from app.db.site_oper import SiteOper @@ -6,6 +7,13 @@ from app.plugins import _PluginBase from app.db.subscribe_oper import SubscribeOper from typing import Any, List, Dict, Tuple +import pytz +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.cron import CronTrigger + +from app.log import logger +from app.core.config import settings +from app.schemas import NotificationType from app.schemas.types import SystemConfigKey @@ -17,7 +25,7 @@ class SubscribeStatistic(_PluginBase): # 插件图标 plugin_icon = "https://raw.githubusercontent.com/thsrite/MoviePilot-Plugins/main/icons/subscribestatistic.png" # 插件版本 - plugin_version = "1.4" + plugin_version = "1.5" # 插件作者 plugin_author = "thsrite" # 作者主页 @@ -31,13 +39,18 @@ class SubscribeStatistic(_PluginBase): # 任务执行间隔 _enabled = False + _notify = False + _onlyonce = False _movie_subscribe_days = None _tv_subscribe_days = None _movie_download_days = None _tv_download_days = None + _notify_type = None + _msgtype = None subscribe = None downloadhis = None siteoper = None + _cron: str = "" def init_plugin(self, config: dict = None): self.subscribe = SubscribeOper() @@ -45,10 +58,98 @@ class SubscribeStatistic(_PluginBase): self.siteoper = SiteOper() if config: self._enabled = config.get("enabled") + self._notify = config.get("notify") + self._onlyonce = config.get("onlyonce") + self._cron = config.get("cron") self._movie_subscribe_days = config.get("movie_subscribe_days") self._tv_subscribe_days = config.get("tv_subscribe_days") self._movie_download_days = config.get("movie_download_days") self._tv_download_days = config.get("tv_download_days") + self._notify_type = config.get("notify_type") + self._msgtype = config.get("msgtype") + + if self._enabled and ( + self._cron or self._onlyonce) and self._notify and self._msgtype and self._notify_type: + # 定时服务 + self._scheduler = BackgroundScheduler(timezone=settings.TZ) + + # 立即运行一次 + if self._onlyonce: + logger.info(f"订阅下载统计服务启动,立即运行一次") + self._scheduler.add_job(self.notify, 'date', + run_date=datetime.now( + tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), + name="订阅下载统计") + # 关闭一次性开关 + self._onlyonce = False + + # 保存配置 + self.__update_config() + + # 周期运行 + if self._cron: + try: + self._scheduler.add_job(func=self.notify, + trigger=CronTrigger.from_crontab(self._cron), + name="订阅下载统计") + except Exception as err: + logger.error(f"定时任务配置错误:{err}") + # 推送实时消息 + self.systemmessage.put(f"执行周期配置错误:{err}") + + # 启动任务 + if self._scheduler.get_jobs(): + self._scheduler.print_jobs() + self._scheduler.start() + + def notify(self): + """ + 发送统计消息 + """ + text = "" + if 'movie_subscribes' in self._notify_type: + text += "【电影订阅统计】\n" + _, movie_subscribe_sites, movie_subscribe_datas = self.__get_movie_subscribes() + movie_subscribe_dict = dict(zip(movie_subscribe_sites, movie_subscribe_datas)) + movie_subscribe_dict = dict(sorted(movie_subscribe_dict.items(), key=lambda x: x[1], reverse=True)) + for movie_subscribe_site in movie_subscribe_dict.keys(): + text += f"{movie_subscribe_site}: {movie_subscribe_dict[movie_subscribe_site]}\n" + text += "\n" + + if 'tv_subscribes' in self._notify_type: + text += "【电视剧订阅统计】\n" + _, tv_subscribe_sites, tv_subscribe_datas = self.__get_tv_subscribes() + tv_subscribe_dict = dict(zip(tv_subscribe_sites, tv_subscribe_datas)) + tv_subscribe_dict = dict(sorted(tv_subscribe_dict.items(), key=lambda x: x[1], reverse=True)) + for tv_subscribe_site in tv_subscribe_dict.keys(): + text += f"{tv_subscribe_site}: {tv_subscribe_dict[tv_subscribe_site]}\n" + text += "\n" + + if 'movie_downloads' in self._notify_type: + text += "【电影下载统计】\n" + _, movie_download_sites, movie_download_datas = self.__get_movie_downloads() + movie_download_dict = dict(zip(movie_download_sites, movie_download_datas)) + movie_download_dict = dict(sorted(movie_download_dict.items(), key=lambda x: x[1], reverse=True)) + for movie_download_site in movie_download_dict.keys(): + text += f"{movie_download_site}: {movie_download_dict[movie_download_site]}\n" + text += "\n" + + if 'tv_downloads' in self._notify_type: + text += "【电视剧下载统计】\n" + _, tv_download_sites, tv_download_datas = self.__get_tv_downloads() + tv_download_dict = dict(zip(tv_download_sites, tv_download_datas)) + tv_download_dict = dict(sorted(tv_download_dict.items(), key=lambda x: x[1], reverse=True)) + for tv_download_site in tv_download_dict.keys(): + text += f"{tv_download_site}: {tv_download_dict[tv_download_site]}\n" + + # 发送通知 + mtype = NotificationType.Manual + if self._msgtype: + mtype = NotificationType.__getitem__(str(self._msgtype)) or NotificationType.Manual + + self.post_message(title="订阅下载统计", + mtype=mtype, + text=text) def get_state(self) -> bool: return self._enabled @@ -60,10 +161,108 @@ class SubscribeStatistic(_PluginBase): def get_api(self) -> List[Dict[str, Any]]: pass + def __get_movie_subscribes(self): + """ + 获取电影订阅统计数据 + """ + # 电影订阅 + movie_subscribes = self.subscribe.list_by_type(mtype='电影', days=self._movie_subscribe_days) + movie_subscribe_sites = [] + movie_subscribe_datas = [] + if movie_subscribes: + movie_subscribe_site_ids = [] + for movie_subscribe in movie_subscribes: + if movie_subscribe.sites: + movie_subscribe_site_ids += [site for site in json.loads(movie_subscribe.sites)] + else: + movie_subscribe_site_ids += self.systemconfig.get(SystemConfigKey.RssSites) or [] + + for movie_subscribe_site_id in movie_subscribe_site_ids: + site = self.siteoper.get(movie_subscribe_site_id) + if site: + if not movie_subscribe_sites.__contains__(site.name): + movie_subscribe_sites.append(site.name) + movie_subscribe_datas.append(movie_subscribe_site_ids.count(movie_subscribe_site_id)) + + return movie_subscribes, movie_subscribe_sites, movie_subscribe_datas + + def __get_tv_subscribes(self): + """ + 获取电视剧订阅统计数据 + """ + tv_subscribes = self.subscribe.list_by_type(mtype='电视剧', days=self._tv_subscribe_days) + tv_subscribe_sites = [] + tv_subscribe_datas = [] + if tv_subscribes: + tv_subscribe_site_ids = [] + for tv_subscribe in tv_subscribes: + if tv_subscribe.sites: + tv_subscribe_site_ids += [site for site in json.loads(tv_subscribe.sites)] + else: + tv_subscribe_site_ids += self.systemconfig.get(SystemConfigKey.RssSites) or [] + + for tv_subscribe_site_id in tv_subscribe_site_ids: + site = self.siteoper.get(tv_subscribe_site_id) + if site: + if not tv_subscribe_sites.__contains__(site.name): + tv_subscribe_sites.append(site.name) + tv_subscribe_datas.append(tv_subscribe_site_ids.count(tv_subscribe_site_id)) + + return tv_subscribes, tv_subscribe_sites, tv_subscribe_datas + + def __get_movie_downloads(self): + """ + 获取电影下载统计数据 + """ + movie_downloads = self.downloadhis.list_by_type(mtype="电影", days=self._movie_download_days) + movie_download_sites = [] + movie_download_datas = [] + if movie_downloads: + movie_download_sites2 = [] + for movie_download in movie_downloads: + if movie_download.torrent_site: + movie_download_sites2.append(movie_download.torrent_site) + + for movie_download_site in movie_download_sites2: + if not movie_download_sites.__contains__(movie_download_site): + movie_download_sites.append(movie_download_site) + if not movie_download_datas.__contains__(movie_download_site): + movie_download_datas.append(movie_download_sites2.count(movie_download_site)) + + return movie_downloads, movie_download_sites, movie_download_datas + + def __get_tv_downloads(self): + """ + 获取电视剧下载统计数据 + """ + tv_downloads = self.downloadhis.list_by_type(mtype="电视剧", days=self._tv_download_days) + tv_download_sites = [] + tv_download_datas = [] + if tv_downloads: + tv_download_sites2 = [] + for tv_download in tv_downloads: + if tv_download.torrent_site: + tv_download_sites2.append(tv_download.torrent_site) + + for tv_download_site in tv_download_sites2: + if not tv_download_sites.__contains__(tv_download_site): + tv_download_sites.append(tv_download_site) + if not tv_download_datas.__contains__(tv_download_site): + tv_download_datas.append(tv_download_sites2.count(tv_download_site)) + + return tv_downloads, tv_download_sites, tv_download_datas + def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: """ 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 """ + # 编历 NotificationType 枚举,生成消息类型选项 + MsgTypeOptions = [] + for item in NotificationType: + MsgTypeOptions.append({ + "title": item.value, + "value": item.name + }) return [ { 'component': 'VForm', @@ -87,38 +286,38 @@ class SubscribeStatistic(_PluginBase): } ] }, - # { - # 'component': 'VCol', - # 'props': { - # 'cols': 12, - # 'md': 4 - # }, - # 'content': [ - # { - # 'component': 'VSwitch', - # 'props': { - # 'model': 'notify', - # 'label': '发送通知', - # } - # } - # ] - # }, - # { - # 'component': 'VCol', - # 'props': { - # 'cols': 12, - # 'md': 4 - # }, - # 'content': [ - # { - # 'component': 'VSwitch', - # 'props': { - # 'model': 'onlyonce', - # 'label': '立即运行一次', - # } - # } - # ] - # } + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'notify', + 'label': '发送通知', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'onlyonce', + 'label': '立即运行一次', + } + } + ] + } ] }, { @@ -194,6 +393,76 @@ class SubscribeStatistic(_PluginBase): }, ] }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'cron', + 'label': '执行周期', + 'placeholder': '5位cron表达式,留空自动' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'multiple': True, + 'chips': True, + 'model': 'notify_type', + 'label': '推送类型', + 'items': [ + {'title': '电影订阅', 'value': 'movie_subscribes'}, + {'title': '电视剧订阅', 'value': 'tv_subscribes'}, + {'title': '电影下载', 'value': 'movie_downloads'}, + {'title': '电视剧下载', 'value': 'tv_downloads'}, + ] + } + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'multiple': False, + 'chips': True, + 'model': 'msgtype', + 'label': '消息类型', + 'items': MsgTypeOptions + } + } + ] + } + ] + }, + ] + }, { 'component': 'VRow', 'content': [ @@ -240,10 +509,15 @@ class SubscribeStatistic(_PluginBase): } ], { "enabled": False, + "notify": False, + "onlyonce": False, + "cron": "5 1 * * *", "movie_subscribe_days": 30, "tv_subscribe_days": 30, "movie_download_days": 7, - "tv_download_days": 7 + "tv_download_days": 7, + "notify_type": "", + "msgtype": "" } def get_page(self) -> List[dict]: @@ -259,74 +533,16 @@ class SubscribeStatistic(_PluginBase): ] # 电影订阅 - movie_subscribes = self.subscribe.list_by_type(mtype='电影', days=self._movie_subscribe_days) - movie_subscribe_sites = [] - movie_subscribe_datas = [] - if movie_subscribes: - movie_subscribe_site_ids = [] - for movie_subscribe in movie_subscribes: - if movie_subscribe.sites: - movie_subscribe_site_ids += [site for site in json.loads(movie_subscribe.sites)] - else: - movie_subscribe_site_ids += self.systemconfig.get(SystemConfigKey.RssSites) or [] - - for movie_subscribe_site_id in movie_subscribe_site_ids: - site = self.siteoper.get(movie_subscribe_site_id) - if site: - if not movie_subscribe_sites.__contains__(site.name): - movie_subscribe_sites.append(site.name) - movie_subscribe_datas.append(movie_subscribe_site_ids.count(movie_subscribe_site_id)) + movie_subscribes, movie_subscribe_sites, movie_subscribe_datas = self.__get_movie_subscribes() # 电视剧订阅 - tv_subscribes = self.subscribe.list_by_type(mtype='电视剧', days=self._tv_subscribe_days) - tv_subscribe_sites = [] - tv_subscribe_datas = [] - if tv_subscribes: - tv_subscribe_site_ids = [] - for tv_subscribe in tv_subscribes: - if tv_subscribe.sites: - tv_subscribe_site_ids += [site for site in json.loads(tv_subscribe.sites)] - else: - tv_subscribe_site_ids += self.systemconfig.get(SystemConfigKey.RssSites) or [] - - for tv_subscribe_site_id in tv_subscribe_site_ids: - site = self.siteoper.get(tv_subscribe_site_id) - if site: - if not tv_subscribe_sites.__contains__(site.name): - tv_subscribe_sites.append(site.name) - tv_subscribe_datas.append(tv_subscribe_site_ids.count(tv_subscribe_site_id)) + tv_subscribes, tv_subscribe_sites, tv_subscribe_datas = self.__get_tv_subscribes() # 电影下载 - movie_downloads = self.downloadhis.list_by_type(mtype="电影", days=self._movie_download_days) - movie_download_sites = [] - movie_download_datas = [] - if movie_downloads: - movie_download_sites2 = [] - for movie_download in movie_downloads: - if movie_download.torrent_site: - movie_download_sites2.append(movie_download.torrent_site) - - for movie_download_site in movie_download_sites2: - if not movie_download_sites.__contains__(movie_download_site): - movie_download_sites.append(movie_download_site) - if not movie_download_datas.__contains__(movie_download_site): - movie_download_datas.append(movie_download_sites2.count(movie_download_site)) + movie_downloads, movie_download_sites, movie_download_datas = self.__get_movie_downloads() # 电视剧下载 - tv_downloads = self.downloadhis.list_by_type(mtype="电视剧", days=self._tv_download_days) - tv_download_sites = [] - tv_download_datas = [] - if tv_downloads: - tv_download_sites2 = [] - for tv_download in tv_downloads: - if tv_download.torrent_site: - tv_download_sites2.append(tv_download.torrent_site) - - for tv_download_site in tv_download_sites2: - if not tv_download_sites.__contains__(tv_download_site): - tv_download_sites.append(tv_download_site) - if not tv_download_datas.__contains__(tv_download_site): - tv_download_datas.append(tv_download_sites2.count(tv_download_site)) + tv_downloads, tv_download_sites, tv_download_datas = self.__get_tv_downloads() # 拼装页面 return [