From 1ec12855635e505e4d47795970528b5e24237fd7 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 9 Jun 2025 14:15:19 +0800 Subject: [PATCH] fix plugins --- plugins.v2/autoclean/__init__.py | 21 ++- plugins.v2/autosignin/__init__.py | 47 +++--- plugins.v2/brushflow/__init__.py | 70 ++++----- plugins.v2/chatgpt/__init__.py | 5 +- plugins.v2/clashruleprovider/__init__.py | 31 ++-- .../clashruleprovider/clash_rule_parser.py | 6 +- plugins.v2/cleaninvalidseed/__init__.py | 98 ++++++------ plugins.v2/crossseed/__init__.py | 38 ++--- plugins.v2/doubanrank/__init__.py | 30 ++-- plugins.v2/doubansync/__init__.py | 63 ++++---- plugins.v2/downloadsitetag/__init__.py | 20 +-- plugins.v2/historytov2/__init__.py | 1 - plugins.v2/imdbsource/__init__.py | 138 +---------------- plugins.v2/iyuuautoseed/__init__.py | 31 ++-- plugins.v2/libraryscraper/__init__.py | 11 +- plugins.v2/mediaservermsg/__init__.py | 9 +- plugins.v2/mediaserverrefresh/__init__.py | 7 +- plugins.v2/personmeta/__init__.py | 20 +-- plugins.v2/playletcategory/__init__.py | 5 +- plugins.v2/qbcommand/__init__.py | 143 +++++++++--------- plugins.v2/rsssubscribe/__init__.py | 33 ++-- plugins.v2/sitestatistic/__init__.py | 18 +-- plugins.v2/speedlimiter/__init__.py | 11 +- plugins.v2/subscribeclear/__init__.py | 70 ++++----- plugins.v2/synccookiecloud/__init__.py | 4 +- plugins.v2/tobypasstrackers/__init__.py | 47 +++--- plugins.v2/tobypasstrackers/dns_helper.py | 2 - plugins.v2/torrentremover/__init__.py | 7 +- plugins.v2/torrenttransfer/__init__.py | 24 +-- 29 files changed, 394 insertions(+), 616 deletions(-) diff --git a/plugins.v2/autoclean/__init__.py b/plugins.v2/autoclean/__init__.py index 26a83dc..70a84e5 100644 --- a/plugins.v2/autoclean/__init__.py +++ b/plugins.v2/autoclean/__init__.py @@ -49,8 +49,6 @@ class AutoClean(_PluginBase): _cleantype = None _cleandate = None _cleanuser = None - _downloadhis = None - _transferhis = None # 定时器 _scheduler: Optional[BackgroundScheduler] = None @@ -70,9 +68,6 @@ class AutoClean(_PluginBase): # 加载模块 if self._enabled: - self._downloadhis = DownloadHistoryOper() - self._transferhis = TransferHistoryOper() - if self._onlyonce: # 定时服务 self._scheduler = BackgroundScheduler(timezone=settings.TZ) @@ -115,9 +110,10 @@ class AutoClean(_PluginBase): return # 查询用户清理日期之前的下载历史,不填默认清理全部用户的下载 + _downloadhis = DownloadHistoryOper() if not self._cleanuser: clean_date = self.__get_clean_date() - downloadhis_list = self._downloadhis.list_by_user_date(date=clean_date) + downloadhis_list = _downloadhis.list_by_user_date(date=clean_date) logger.info(f'获取到日期 {clean_date} 之前的下载历史 {len(downloadhis_list)} 条') self.__clean_history(date=clean_date, clean_type=self._cleantype, downloadhis_list=downloadhis_list) @@ -130,8 +126,8 @@ class AutoClean(_PluginBase): # 1.3.7版本及之前处理多位用户 if str(self._cleanuser).count(','): for username in str(self._cleanuser).split(","): - downloadhis_list = self._downloadhis.list_by_user_date(date=clean_date, - username=username) + downloadhis_list = _downloadhis.list_by_user_date(date=clean_date, + username=username) logger.info( f'获取到用户 {username} 日期 {clean_date} 之前的下载历史 {len(downloadhis_list)} 条') self.__clean_history(date=clean_date, clean_type=self._cleantype, downloadhis_list=downloadhis_list) @@ -152,8 +148,8 @@ class AutoClean(_PluginBase): # 转strftime clean_date = self.__get_clean_date(clean_date) logger.info(f'{username} 使用 {clean_type} 清理方式,清理 {clean_date} 之前的下载历史') - downloadhis_list = self._downloadhis.list_by_user_date(date=clean_date, - username=username) + downloadhis_list = _downloadhis.list_by_user_date(date=clean_date, + username=username) logger.info( f'获取到用户 {username} 日期 {clean_date} 之前的下载历史 {len(downloadhis_list)} 条') self.__clean_history(date=clean_date, clean_type=clean_type, @@ -168,6 +164,7 @@ class AutoClean(_PluginBase): return # 读取历史记录 + _transferhis = TransferHistoryOper() pulgin_history = self.get_data('history') or [] # 创建一个字典来保存分组结果 @@ -197,7 +194,7 @@ class AutoClean(_PluginBase): logger.debug(f'下载历史 {downloadhis.id} {downloadhis.title} 未获取到download_hash,跳过处理') continue # 根据hash获取转移记录 - transferhis_list = self._transferhis.list_by_hash(download_hash=downloadhis.download_hash) + transferhis_list = _transferhis.list_by_hash(download_hash=downloadhis.download_hash) if not transferhis_list: logger.warn(f"下载历史 {downloadhis.download_hash} 未查询到转移记录,跳过处理") continue @@ -208,7 +205,7 @@ class AutoClean(_PluginBase): dest_fileitem = schemas.FileItem(**history.dest_fileitem) StorageChain().delete_file(dest_fileitem) # 删除记录 - self._transferhis.delete(history.id) + _transferhis.delete(history.id) # 删除源文件 if clean_type in ["src", "all"]: src_fileitem = schemas.FileItem(**history.src_fileitem) diff --git a/plugins.v2/autosignin/__init__.py b/plugins.v2/autosignin/__init__.py index fe15ac8..0661da8 100644 --- a/plugins.v2/autosignin/__init__.py +++ b/plugins.v2/autosignin/__init__.py @@ -12,9 +12,8 @@ from apscheduler.triggers.cron import CronTrigger from ruamel.yaml import CommentedMap from app import schemas -from app.chain.site import SiteChain from app.core.config import settings -from app.core.event import EventManager, eventmanager, Event +from app.core.event import eventmanager, Event from app.db.site_oper import SiteOper from app.helper.browser import PlaywrightHelper from app.helper.cloudflare import under_challenge @@ -49,12 +48,6 @@ class AutoSignIn(_PluginBase): # 可使用的用户级别 auth_level = 2 - # 私有属性 - sites: SitesHelper = None - siteoper: SiteOper = None - sitechain: SiteChain = None - # 事件管理器 - event: EventManager = None # 定时器 _scheduler: Optional[BackgroundScheduler] = None # 加载的模块 @@ -75,10 +68,6 @@ class AutoSignIn(_PluginBase): _auto_cf: int = 0 def init_plugin(self, config: dict = None): - self.sites = SitesHelper() - self.siteoper = SiteOper() - self.event = EventManager() - self.sitechain = SiteChain() # 停止现有任务 self.stop_service() @@ -97,8 +86,8 @@ class AutoSignIn(_PluginBase): self._clean = config.get("clean") # 过滤掉已删除的站点 - all_sites = [site.id for site in self.siteoper.list_order_by_pri()] + [site.get("id") for site in - self.__custom_sites()] + all_sites = [site.id for site in SiteOper().list_order_by_pri()] + [site.get("id") for site in + self.__custom_sites()] self._sign_sites = [site_id for site_id in all_sites if site_id in self._sign_sites] self._login_sites = [site_id for site_id in all_sites if site_id in self._login_sites] # 保存配置 @@ -272,7 +261,7 @@ class AutoSignIn(_PluginBase): customSites = self.__custom_sites() site_options = ([{"title": site.name, "value": site.id} - for site in self.siteoper.list_order_by_pri()] + for site in SiteOper().list_order_by_pri()] + [{"title": site.get("name"), "value": site.get("id")} for site in customSites]) return [ @@ -565,7 +554,7 @@ class AutoSignIn(_PluginBase): sites_info = {} # 记录站点信息 # 获取站点信息 - site_indexers = self.sites.get_indexers() + site_indexers = SitesHelper().get_indexers() for site in site_indexers: if not site.get("public"): sites_info[site.get("id")] = site.get("name") @@ -734,8 +723,8 @@ class AutoSignIn(_PluginBase): # 按日期排序,最新的在前面 try: records.sort(key=lambda x: x.get("day_obj", datetime.now().date()), reverse=True) - except: - pass # 排序失败时跳过 + except Exception as e: + logger.debug(f"排序失败: {str(e)}") # 获取最新的状态作为站点概要 latest_status = records[0].get("status", "未知状态") @@ -770,8 +759,8 @@ class AutoSignIn(_PluginBase): # 按日期排序,最新的在前面 try: records.sort(key=lambda x: x.get("day_obj", datetime.now().date()), reverse=True) - except: - pass # 排序失败时跳过 + except Exception as e: + logger.debug(f"排序失败: {str(e)}") # 获取最新的状态作为站点概要 latest_status = records[0].get("status", "未知状态") @@ -1142,7 +1131,8 @@ class AutoSignIn(_PluginBase): } ] - def _create_expansion_panel(self, site_name, records, status_color, status_icon, latest_status): + @staticmethod + def _create_expansion_panel(site_name, records, status_color, status_icon, latest_status): """创建站点折叠面板""" # 生成站点图标(使用站点名的首字母) site_initial = site_name[0].upper() if site_name else "?" @@ -1322,7 +1312,7 @@ class AutoSignIn(_PluginBase): today_history = self.get_data(key=type_str + "-" + today) # 查询所有站点 - all_sites = [site for site in self.sites.get_indexers() if not site.get("public")] + self.__custom_sites() + all_sites = [site for site in SitesHelper().get_indexers() if not site.get("public")] + self.__custom_sites() # 过滤掉没有选中的站点 if do_sites: do_sites = [site for site in all_sites if site.get("id") in do_sites] @@ -1402,7 +1392,8 @@ class AutoSignIn(_PluginBase): # 失败|错误 failed_msg = [] - sites = {site.get('name'): site.get("id") for site in self.sites.get_indexers() if not site.get("public")} + sites = {site.get('name'): site.get("id") for site in SitesHelper().get_indexers() if + not site.get("public")} for s in status: site_name = s[0] site_id = None @@ -1501,7 +1492,7 @@ class AutoSignIn(_PluginBase): if apikey != settings.API_TOKEN: return schemas.Response(success=False, message="API密钥错误") domain = StringUtils.get_url_domain(url) - site_info = self.sites.get_indexer(domain) + site_info = SitesHelper().get_indexer(domain) if not site_info: return schemas.Response( success=True, @@ -1533,9 +1524,9 @@ class AutoSignIn(_PluginBase): seconds = (datetime.now() - start_time).seconds domain = StringUtils.get_url_domain(site_info.get('url')) if state: - self.siteoper.success(domain=domain, seconds=seconds) + SiteOper().success(domain=domain, seconds=seconds) else: - self.siteoper.fail(domain) + SiteOper().fail(domain) return site_info.get("name"), message @staticmethod @@ -1635,9 +1626,9 @@ class AutoSignIn(_PluginBase): seconds = (datetime.now() - start_time).seconds domain = StringUtils.get_url_domain(site_info.get('url')) if state: - self.siteoper.success(domain=domain, seconds=seconds) + SiteOper().success(domain=domain, seconds=seconds) else: - self.siteoper.fail(domain) + SiteOper().fail(domain) return site_info.get("name"), message @staticmethod diff --git a/plugins.v2/brushflow/__init__.py b/plugins.v2/brushflow/__init__.py index 69d201f..8bb0892 100644 --- a/plugins.v2/brushflow/__init__.py +++ b/plugins.v2/brushflow/__init__.py @@ -54,7 +54,7 @@ class BrushConfig: self.exclude = config.get("exclude") self.size = config.get("size") self.seeder = config.get("seeder") - self.timezone_offset = (self.__parse_number(config.get("timezone_offset", "+0")) or 0) * 60 # 转换到分钟 + self.timezone_offset = (self.__parse_number(config.get("timezone_offset", "+0")) or 0) * 60 # 转换到分钟 self.pubtime = config.get("pubtime") self.seed_time = self.__parse_number(config.get("seed_time")) self.hr_seed_time = self.__parse_number(config.get("hr_seed_time")) @@ -271,12 +271,6 @@ class BrushFlow(_PluginBase): # 可使用的用户级别 auth_level = 2 - # 私有属性 - sites_helper = None - site_oper = None - torrents_chain = None - subscribe_oper = None - downloader_helper = None # 刷流配置 _brush_config = None # Brush任务是否启动 @@ -296,11 +290,7 @@ class BrushFlow(_PluginBase): # endregion def init_plugin(self, config: dict = None): - self.sites_helper = SitesHelper() - self.site_oper = SiteOper() - self.torrents_chain = TorrentsChain() - self.subscribe_oper = SubscribeOper() - self.downloader_helper = DownloaderHelper() + self._task_brush_enable = False if not config: @@ -322,7 +312,7 @@ class BrushFlow(_PluginBase): # 这里先过滤掉已删除的站点并保存,特别注意的是,这里保留了界面选择站点时的顺序,以便后续站点随机刷流或顺序刷流 if brush_config.brushsites: - site_id_to_public_status = {site.get("id"): site.get("public") for site in self.sites_helper.get_indexers()} + site_id_to_public_status = {site.get("id"): site.get("public") for site in SitesHelper().get_indexers()} brush_config.brushsites = [ site_id for site_id in brush_config.brushsites if site_id in site_id_to_public_status and not site_id_to_public_status[site_id] @@ -394,7 +384,7 @@ class BrushFlow(_PluginBase): 服务信息 """ brush_config = self.__get_brush_config() - service = self.downloader_helper.get_service(name=brush_config.downloader) + service = DownloaderHelper().get_service(name=brush_config.downloader) if not service: self.__log_and_notify_error("站点刷流任务出错,获取下载器实例失败,请检查配置") return None @@ -819,10 +809,10 @@ class BrushFlow(_PluginBase): # 站点选项 site_options = [{"title": site.get("name"), "value": site.get("id")} - for site in self.sites_helper.get_indexers()] + for site in SitesHelper().get_indexers()] # 下载器选项 downloader_options = [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] return [ { 'component': 'VForm', @@ -1971,7 +1961,7 @@ class BrushFlow(_PluginBase): # 获取所有站点的信息,并过滤掉不存在的站点 site_infos = [] for siteid in brush_config.brushsites: - siteinfo = self.site_oper.get(siteid) + siteinfo = SiteOper().get(siteid) if siteinfo: site_infos.append(siteinfo) @@ -2006,13 +1996,13 @@ class BrushFlow(_PluginBase): """ 针对站点进行刷流 """ - siteinfo = self.site_oper.get(siteid) + siteinfo = SiteOper().get(siteid) if not siteinfo: logger.warning(f"站点不存在:{siteid}") return True logger.info(f"开始获取站点 {siteinfo.name} 的新种子 ...") - torrents = self.torrents_chain.browse(domain=siteinfo.domain) + torrents = TorrentsChain().browse(domain=siteinfo.domain) if not torrents: logger.info(f"站点 {siteinfo.name} 没有获取到种子") return True @@ -2383,7 +2373,7 @@ class BrushFlow(_PluginBase): if need_delete_hashes: # 如果是QB,则重新汇报Tracker - if self.downloader_helper.is_downloader("qbittorrent", service=self.service_info): + if DownloaderHelper().is_downloader("qbittorrent", service=self.service_info): self.__qb_torrents_reannounce(torrent_hashes=need_delete_hashes) # 删除种子 if downloader.delete_torrents(ids=need_delete_hashes, delete_file=True): @@ -2425,7 +2415,7 @@ class BrushFlow(_PluginBase): seeding_torrents_dict: Dict[str, Any]): brush_config = self.__get_brush_config() - if not self.downloader_helper.is_downloader("qbittorrent", service=self.service_info): + if not DownloaderHelper().is_downloader("qbittorrent", service=self.service_info): logger.info("同步种子刷流标签记录目前仅支持qbittorrent") return @@ -2546,7 +2536,8 @@ class BrushFlow(_PluginBase): freedate = freedate_origin.replace("T", " ").replace("Z", "") freedate = datetime.strptime(freedate, "%Y-%m-%d %H:%M:%S") delta_minutes = (((freedate - now).total_seconds() + 60) // 60) - brush_config.timezone_offset - logger.debug(f"促销截止(站点时间): {freedate_origin}, 时区偏移: {brush_config.timezone_offset}, 用户当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}, 时间差: {delta_minutes}分") + logger.debug( + f"促销截止(站点时间): {freedate_origin}, 时区偏移: {brush_config.timezone_offset}, 用户当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}, 时间差: {delta_minutes}分") if delta_minutes <= 0: return True, "促销过期" except Exception as e: @@ -2576,7 +2567,8 @@ class BrushFlow(_PluginBase): return True, reason if not hit_and_run else "H&R种子(未设置H&R条件)," + reason - def __evaluate_proxy_pre_conditions_for_delete(self, site_name: str, torrent_info: dict, torrent_task: dict) -> Tuple[bool, str]: + def __evaluate_proxy_pre_conditions_for_delete(self, site_name: str, + torrent_info: dict, torrent_task: dict) -> Tuple[bool, str]: """ 评估动态删除前置条件并返回是否应删除种子及其原因 """ @@ -2595,7 +2587,8 @@ class BrushFlow(_PluginBase): freedate = freedate_origin.replace("T", " ").replace("Z", "") freedate = datetime.strptime(freedate, "%Y-%m-%d %H:%M:%S") delta_minutes = (((freedate - now).total_seconds() + 60) // 60) - brush_config.timezone_offset - logger.debug(f"促销截止(站点时间): {freedate_origin}, 时区偏移: {brush_config.timezone_offset}, 用户当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}, 时间差: {delta_minutes}分") + logger.debug( + f"促销截止(站点时间): {freedate_origin}, 时区偏移: {brush_config.timezone_offset}, 用户当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}, 时间差: {delta_minutes}分") if delta_minutes <= 0: return True, f"促销已过期" except Exception as e: @@ -3116,7 +3109,8 @@ class BrushFlow(_PluginBase): return data return None - def __reset_download_url(self, torrent_url, site_id) -> str: + @staticmethod + def __reset_download_url(torrent_url, site_id) -> str: """ 处理下载地址 """ @@ -3125,7 +3119,7 @@ class BrushFlow(_PluginBase): if not torrent_url or torrent_url.startswith("magnet"): return torrent_url - indexers = self.sites_helper.get_indexers() + indexers = SitesHelper().get_indexers() if not indexers: return torrent_url @@ -3190,7 +3184,8 @@ class BrushFlow(_PluginBase): if not downloader: return None - if self.downloader_helper.is_downloader("qbittorrent", service=self.service_info): + downloader_helper = DownloaderHelper() + if downloader_helper.is_downloader("qbittorrent", service=self.service_info): # 限速值转为bytes up_speed = up_speed * 1024 if up_speed else None down_speed = down_speed * 1024 if down_speed else None @@ -3224,7 +3219,7 @@ class BrushFlow(_PluginBase): return torrent_hash return None - elif self.downloader_helper.is_downloader("transmission", service=self.service_info): + elif downloader_helper.is_downloader("transmission", service=self.service_info): # 如果开启代理下载以及种子地址不是磁力地址,则请求种子到内存再传入下载器 if not torrent_content.startswith("magnet"): response = RequestUtils(cookies=cookies, @@ -3272,7 +3267,7 @@ class BrushFlow(_PluginBase): 获取种子hash """ try: - return torrent.get("hash") if self.downloader_helper.is_downloader("qbittorrent", service=self.service_info) \ + return torrent.get("hash") if DownloaderHelper().is_downloader("qbittorrent", service=self.service_info) \ else torrent.hashString except Exception as e: print(str(e)) @@ -3289,8 +3284,8 @@ class BrushFlow(_PluginBase): all_hashes = [] for torrent in torrents: # 根据下载器类型获取Hash值 - hash_value = torrent.get("hash") if self.downloader_helper.is_downloader("qbittorrent", - service=self.service_info) \ + hash_value = torrent.get("hash") if DownloaderHelper().is_downloader("qbittorrent", + service=self.service_info) \ else torrent.hashString if hash_value: all_hashes.append(hash_value) @@ -3305,8 +3300,8 @@ class BrushFlow(_PluginBase): """ try: return [str(tag).strip() for tag in torrent.get("tags").split(',')] \ - if self.downloader_helper.is_downloader("qbittorrent", - service=self.service_info) else torrent.labels or [] + if DownloaderHelper().is_downloader("qbittorrent", + service=self.service_info) else torrent.labels or [] except Exception as e: print(str(e)) return [] @@ -3317,7 +3312,7 @@ class BrushFlow(_PluginBase): """ date_now = int(time.time()) # QB - if self.downloader_helper.is_downloader("qbittorrent", service=self.service_info): + if DownloaderHelper().is_downloader("qbittorrent", service=self.service_info): """ { "added_on": 1693359031, @@ -3724,7 +3719,7 @@ class BrushFlow(_PluginBase): if not self._subscribe_infos: self._subscribe_infos = {} - subscribes = self.subscribe_oper.list() + subscribes = SubscribeOper().list() if subscribes: # 遍历订阅 for subscribe in subscribes: @@ -3937,7 +3932,8 @@ class BrushFlow(_PluginBase): # 情况2: 时间段跨越午夜 return now >= start_time or now <= end_time - def __get_site_by_torrent(self, torrent: Any) -> Tuple[int, str]: + @staticmethod + def __get_site_by_torrent(torrent: Any) -> Tuple[int, str]: """ 根据tracker获取站点信息 """ @@ -3980,7 +3976,7 @@ class BrushFlow(_PluginBase): # 使用StringUtils工具类获取tracker的域名 domain = StringUtils.get_url_domain(tracker) - site_info = self.sites_helper.get_indexer(domain) + site_info = SitesHelper().get_indexer(domain) if site_info: return site_info.get("id"), site_info.get("name") diff --git a/plugins.v2/chatgpt/__init__.py b/plugins.v2/chatgpt/__init__.py index 1b2d7ac..d7181ed 100644 --- a/plugins.v2/chatgpt/__init__.py +++ b/plugins.v2/chatgpt/__init__.py @@ -337,7 +337,8 @@ class ChatGPT(_PluginBase): def get_page(self) -> List[dict]: pass - def is_api_error(self, response): + @staticmethod + def is_api_error(response): """ 判断响应是否表示API错误 :param response: API响应 @@ -486,4 +487,4 @@ class ChatGPT(_PluginBase): """ 退出插件 """ - pass \ No newline at end of file + pass diff --git a/plugins.v2/clashruleprovider/__init__.py b/plugins.v2/clashruleprovider/__init__.py index 67a2ccf..97485e1 100644 --- a/plugins.v2/clashruleprovider/__init__.py +++ b/plugins.v2/clashruleprovider/__init__.py @@ -1,25 +1,23 @@ -import requests -import re -from typing import Any, Optional, List, Dict, Tuple, Union -import time -import yaml import hashlib -from fastapi import Body, Response +import re +import time from datetime import datetime, timedelta -import pytz +from typing import Any, Optional, List, Dict, Tuple, Union +import pytz +import yaml from apscheduler.schedulers.background import BackgroundScheduler -from cachetools import cached, TTLCache from apscheduler.triggers.cron import CronTrigger +from fastapi import Body, Response from app.core.config import settings -from app.core.event import eventmanager, Event +from app.core.event import eventmanager from app.log import logger from app.plugins import _PluginBase -from app.schemas.types import EventType, NotificationType -from app.utils.http import RequestUtils -from app.plugins.clashruleprovider.clash_rule_parser import ClashRuleParser from app.plugins.clashruleprovider.clash_rule_parser import Action, RuleType, ClashRule, MatchRule, LogicRule +from app.plugins.clashruleprovider.clash_rule_parser import ClashRuleParser +from app.schemas.types import EventType +from app.utils.http import RequestUtils class ClashRuleProvider(_PluginBase): @@ -82,7 +80,7 @@ class ClashRuleProvider(_PluginBase): self._ruleset_rules = self.get_data("ruleset_rules") self._top_rules = self.get_data("top_rules") self._subscription_info = self.get_data("subscription_info") or \ - {"download": 0, "upload": 0, "total": 0, "expire": 0, "last_update": 0} + {"download": 0, "upload": 0, "total": 0, "expire": 0, "last_update": 0} self._rule_provider = self.get_data("rule_provider") or {} self._ruleset_names = self.get_data("ruleset_names") or {} if config: @@ -301,7 +299,7 @@ class ClashRuleProvider(_PluginBase): def test_connectivity(self, params: Dict[str, Any]) -> Dict[str, Any]: if not self._enabled: return {"success": False, "message": ""} - if not params.get('clash_dashboard_url') or not params.get('clash_dashboard_secret')\ + if not params.get('clash_dashboard_url') or not params.get('clash_dashboard_secret') \ or not params.get('sub_link'): return {"success": False, "message": "missing params"} clash_version_url = f"{params.get('clash_dashboard_url')}/version" @@ -363,7 +361,8 @@ class ClashRuleProvider(_PluginBase): if params.get('type') == 'ruleset': res = self.delete_rule_by_priority(params.get('priority'), self._ruleset_rule_parser) if res: - self.__add_notification_job(f"{self._ruleset_prefix}{res.action.value if isinstance(res.action, Action) else res.action}") + self.__add_notification_job( + f"{self._ruleset_prefix}{res.action.value if isinstance(res.action, Action) else res.action}") else: res = self.delete_rule_by_priority(params.get('priority'), self._clash_rule_parser) return {"success": res, "message": None} @@ -403,7 +402,7 @@ class ClashRuleProvider(_PluginBase): res = self.update_rule_by_priority(params.get('rule_data'), self._clash_rule_parser) return {"success": bool(res), "message": None} - def add_rule(self, params: Dict[str, Any]) -> Dict[str, Any]: + def add_rule(self, params: Dict[str, Any]) -> Dict[str, Any]: if not self._enabled: return {"success": False, "message": ""} if params.get('type') == 'ruleset': diff --git a/plugins.v2/clashruleprovider/clash_rule_parser.py b/plugins.v2/clashruleprovider/clash_rule_parser.py index 071d427..53d6bc2 100644 --- a/plugins.v2/clashruleprovider/clash_rule_parser.py +++ b/plugins.v2/clashruleprovider/clash_rule_parser.py @@ -282,7 +282,8 @@ class ClashRuleParser: return self.rules - def validate_rule(self, rule: ClashRule) -> bool: + @staticmethod + def validate_rule(rule: ClashRule) -> bool: """Validate a parsed rule""" try: # Basic validation based on rule type @@ -305,7 +306,8 @@ class ClashRuleParser: return True - except Exception: + except Exception as e: + print(f"Invalid rule '{rule.raw_rule}': {e}") return False def to_string(self) -> List[str]: diff --git a/plugins.v2/cleaninvalidseed/__init__.py b/plugins.v2/cleaninvalidseed/__init__.py index 8f2c820..6a054cb 100644 --- a/plugins.v2/cleaninvalidseed/__init__.py +++ b/plugins.v2/cleaninvalidseed/__init__.py @@ -1,24 +1,22 @@ -import glob -import os import shutil -import time from datetime import datetime, timedelta from pathlib import Path +from typing import Any, List, Dict, Tuple, Optional import pytz from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger -from app.utils.string import StringUtils -from app.schemas.types import EventType -from app.schemas import ServiceInfo -from app.core.event import eventmanager, Event from app.core.config import settings -from app.plugins import _PluginBase -from typing import Any, List, Dict, Tuple, Optional -from app.log import logger -from app.schemas import NotificationType +from app.core.event import eventmanager, Event from app.helper.downloader import DownloaderHelper +from app.log import logger +from app.plugins import _PluginBase +from app.schemas import NotificationType +from app.schemas import ServiceInfo +from app.schemas.types import EventType +from app.utils.string import StringUtils + class CleanInvalidSeed(_PluginBase): # 插件名称 @@ -56,6 +54,7 @@ class CleanInvalidSeed(_PluginBase): _exclude_categories = "" _exclude_labels = "" _more_logs = False + _downloaders = [] # 定时器 _scheduler: Optional[BackgroundScheduler] = None _error_msg = [ @@ -67,7 +66,7 @@ class CleanInvalidSeed(_PluginBase): _custom_error_msg = "" def init_plugin(self, config: dict = None): - self.downloader_helper = DownloaderHelper() + # 停止现有任务 self.stop_service() @@ -97,8 +96,7 @@ class CleanInvalidSeed(_PluginBase): self._scheduler.add_job( func=self.clean_invalid_seed, trigger="date", - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) - + timedelta(seconds=3), + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), name="清理无效种子", ) # 关闭一次性开关 @@ -137,7 +135,7 @@ class CleanInvalidSeed(_PluginBase): ) @property - def service_info(self) -> Optional[ServiceInfo]: + def service_info(self) -> Optional[Dict[str, ServiceInfo]]: """ 服务信息 """ @@ -145,7 +143,7 @@ class CleanInvalidSeed(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - services = self.downloader_helper.get_services(name_filters=self._downloaders) + services = DownloaderHelper().get_services(name_filters=self._downloaders) if not services: logger.warning("获取下载器实例失败,请检查配置") @@ -166,11 +164,12 @@ class CleanInvalidSeed(_PluginBase): return active_services - def check_is_qb(self, service_info) -> bool: + @staticmethod + def check_is_qb(service_info) -> bool: """ 检查下载器类型是否为 qbittorrent 或 transmission """ - if self.downloader_helper.is_downloader(service_type="qbittorrent", service=service_info): + if DownloaderHelper().is_downloader(service_type="qbittorrent", service=service_info): return True return False @@ -225,11 +224,11 @@ class CleanInvalidSeed(_PluginBase): event_data = event.event_data if event_data: if not ( - event_data.get("action") == "detect_invalid_torrents" - or event_data.get("action") == "delete_invalid_torrents" - or event_data.get("action") == "detect_invalid_files" - or event_data.get("action") == "delete_invalid_files" - or event_data.get("action") == "toggle_notify_all" + event_data.get("action") == "detect_invalid_torrents" + or event_data.get("action") == "delete_invalid_torrents" + or event_data.get("action") == "detect_invalid_files" + or event_data.get("action") == "delete_invalid_files" + or event_data.get("action") == "toggle_notify_all" ): return self.post_message( @@ -344,7 +343,7 @@ class CleanInvalidSeed(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue logger.info(f"开始清理 {downloader_name} 无效做种...") all_torrents = self.get_all_torrents(service) @@ -376,13 +375,14 @@ class CleanInvalidSeed(_PluginBase): is_tracker_working = True if not ( - (tracker.get("status") == 4) and (tracker.get("msg") in error_msgs) + (tracker.get("status") == 4) and (tracker.get("msg") in error_msgs) ): is_invalid = False working_tracker_set.add(tracker_domian) if self._more_logs: - logger.info(f"处理 [{torrent.name}] tracker [{tracker_domian}]: 分类: [{torrent.category}], 标签: [{torrent.tags}], 状态: [{tracker.get('status')}], msg: [{tracker.get('msg')}], is_invalid: [{is_invalid}], is_working: [{is_tracker_working}]") + logger.info( + f"处理 [{torrent.name}] tracker [{tracker_domian}]: 分类: [{torrent.category}], 标签: [{torrent.tags}], 状态: [{tracker.get('status')}], msg: [{tracker.get('msg')}], is_invalid: [{is_invalid}], is_working: [{is_tracker_working}]") if is_invalid: temp_invalid_torrents.append(torrent) elif not is_tracker_working: @@ -433,25 +433,30 @@ class CleanInvalidSeed(_PluginBase): if not is_excluded: if self._label_only: # 仅标记 - downloader_obj.set_torrents_tag(ids=torrent.get("hash"), tags=[self._label if self._label != "" else "无效做种"]) + downloader_obj.set_torrents_tag(ids=torrent.get("hash"), tags=[ + self._label if self._label != "" else "无效做种"]) else: # 只删除种子不删除文件,以防其它站点辅种 downloader_obj.delete_torrents(False, torrent.get("hash")) # 标记已处理种子信息 deleted_torrent_tuple_list.append( - ( - torrent.name, - torrent.category, - torrent.tags, - torrent.size, - tracker_domian, - tracker.msg, - ) + ( + torrent.name, + torrent.category, + torrent.tags, + torrent.size, + tracker_domian, + tracker.msg, ) + ) break invalid_msg = f"检测到{len(invalid_torrent_tuple_list)}个失效做种\n" tracker_not_working_msg = f"检测到{len(tracker_not_working_torrents)}个tracker未工作做种,请检查种子状态\n" + exclude_categories_msg = "" + exclude_labels_msg = "" + deleted_msg = "" + if self._label_only or self._delete_invalid_torrents: if self._label_only: deleted_msg = f"标记了{len(deleted_torrent_tuple_list)}个失效种子\n" @@ -513,34 +518,34 @@ class CleanInvalidSeed(_PluginBase): logger.info(exclude_labels_msg) # 通知 if self._notify: - invalid_msg = invalid_msg.replace("_", "\_") + invalid_msg = invalid_msg.replace("_", "\\_") self.post_message( mtype=NotificationType.SiteMessage, title=f"【清理无效做种】", text=invalid_msg, ) if self._notify_all: - tracker_not_working_msg = tracker_not_working_msg.replace("_", "\_") + tracker_not_working_msg = tracker_not_working_msg.replace("_", "\\_") self.post_message( mtype=NotificationType.SiteMessage, title=f"【清理无效做种】", text=tracker_not_working_msg, ) if self._label_only or self._delete_invalid_torrents: - deleted_msg = deleted_msg.replace("_", "\_") + deleted_msg = deleted_msg.replace("_", "\\_") self.post_message( mtype=NotificationType.SiteMessage, title=f"【清理无效做种】", text=deleted_msg, ) if self._notify_all: - exclude_categories_msg = exclude_categories_msg.replace("_", "\_") + exclude_categories_msg = exclude_categories_msg.replace("_", "\\_") self.post_message( mtype=NotificationType.SiteMessage, title=f"【清理无效做种】", text=exclude_categories_msg, ) - exclude_labels_msg = exclude_labels_msg.replace("_", "\_") + exclude_labels_msg = exclude_labels_msg.replace("_", "\\_") self.post_message( mtype=NotificationType.SiteMessage, title=f"【清理无效做种】", @@ -559,7 +564,7 @@ class CleanInvalidSeed(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue all_torrents += self.get_all_torrents(service) @@ -638,7 +643,7 @@ class CleanInvalidSeed(_PluginBase): message += f"***已删除无效源文件,释放{StringUtils.str_filesize(total_size)}空间!***\n" logger.info(message) if self._notify: - message = message.replace("_", "\_") + message = message.replace("_", "\\_") self.post_message( mtype=NotificationType.SiteMessage, title=f"【清理无效做种】", @@ -646,7 +651,8 @@ class CleanInvalidSeed(_PluginBase): ) logger.info("检测无效源文件任务结束") - def get_size(self, path: Path): + @staticmethod + def get_size(path: Path): total_size = 0 if path.is_file(): return path.stat().st_size @@ -801,7 +807,7 @@ class CleanInvalidSeed(_PluginBase): 'model': 'downloaders', 'label': '请选择下载器', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] @@ -813,7 +819,7 @@ class CleanInvalidSeed(_PluginBase): "content": [ { "component": "VCol", - "props": { "cols": 12, "md": 6 }, + "props": {"cols": 12, "md": 6}, "content": [ { "component": "VTextField", @@ -826,7 +832,7 @@ class CleanInvalidSeed(_PluginBase): }, { "component": "VCol", - "props": { "cols": 12, "md": 6 }, + "props": {"cols": 12, "md": 6}, "content": [ { "component": "VTextField", diff --git a/plugins.v2/crossseed/__init__.py b/plugins.v2/crossseed/__init__.py index 6ccc869..3301350 100644 --- a/plugins.v2/crossseed/__init__.py +++ b/plugins.v2/crossseed/__init__.py @@ -194,10 +194,6 @@ class CrossSeed(_PluginBase): # 私有属性 _scheduler = None cross_helper = None - sites = None - siteoper = None - torrent = None - downloader_helper = None # 开关 _enabled = False _cron = None @@ -233,10 +229,7 @@ class CrossSeed(_PluginBase): cached = 0 def init_plugin(self, config: dict = None): - self.sites = SitesHelper() - self.siteoper = SiteOper() - self.torrent = TorrentHelper() - self.downloader_helper = DownloaderHelper() + # 读取配置 if config: self._enabled = config.get("enabled") @@ -257,7 +250,7 @@ class CrossSeed(_PluginBase): self._success_caches = [] if self._clearcache else config.get("success_caches") or [] # 过滤掉已删除的站点 - inner_site_list = self.siteoper.list_order_by_pri() + inner_site_list = SiteOper().list_order_by_pri() all_sites = [(site.id, site.name) for site in inner_site_list] + [ (site.get("id"), site.get("name")) for site in self.__custom_sites() ] @@ -363,7 +356,7 @@ class CrossSeed(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - services = self.downloader_helper.get_services(name_filters=self._downloaders) + services = DownloaderHelper().get_services(name_filters=self._downloaders) if not services: logger.warning("获取下载器实例失败,请检查配置") return None @@ -445,7 +438,7 @@ class CrossSeed(_PluginBase): # 站点的可选项 site_options = ([{"title": site.name, "value": site.id} - for site in self.siteoper.list_order_by_pri()] + for site in SiteOper().list_order_by_pri()] + [{"title": site.get("name"), "value": site.get("id")} for site in customSites]) # 测试版本,只支持青蛙 @@ -557,7 +550,7 @@ class CrossSeed(_PluginBase): 'model': 'downloaders', 'label': '辅种下载器', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] @@ -852,7 +845,7 @@ class CrossSeed(_PluginBase): if not torrent_info.site_name: # 尝试通过域名获取站点信息 tracker_domain = StringUtils.get_url_domain(tracker) - site_info = self.sites.get_indexer(tracker_domain) + site_info = SitesHelper().get_indexer(tracker_domain) if site_info: torrent_info.site_name = site_info.get("name") @@ -983,7 +976,7 @@ class CrossSeed(_PluginBase): chunk_size = 100 for site_config in self._site_cs_infos: # 检查站点是否已经停用 - db_site = self.siteoper.get(site_config.id) + db_site = SiteOper().get(site_config.id) if db_site and not db_site.is_active: logger.info(f"站点{site_config.name}已停用,跳过辅种") continue @@ -1045,7 +1038,8 @@ class CrossSeed(_PluginBase): logger.info(f"下载器 {service.name} 辅种完成") - def __download(self, service: ServiceInfo, content: Union[bytes, str], + @staticmethod + def __download(service: ServiceInfo, content: Union[bytes, str], save_path: str) -> Optional[str]: """ 添加下载任务 @@ -1055,9 +1049,9 @@ class CrossSeed(_PluginBase): tag = StringUtils.generate_random_str(10) state = service.instance.add_torrent(content=content, - download_dir=save_path, - is_paused=True, - tag=["已整理", "辅种", tag]) + download_dir=save_path, + is_paused=True, + tag=["已整理", "辅种", tag]) if not state: return None else: @@ -1070,9 +1064,9 @@ class CrossSeed(_PluginBase): elif service.type == "transmission": # 添加任务 torrent = service.instance.add_torrent(content=content, - download_dir=save_path, - is_paused=True, - labels=["已整理", "辅种"]) + download_dir=save_path, + is_paused=True, + labels=["已整理", "辅种"]) if not torrent: return None else: @@ -1099,7 +1093,7 @@ class CrossSeed(_PluginBase): torrent_url = site_config.get_torrent_url(tor.torrent_id) # 下载种子文件 - _, content, _, _, error_msg = self.torrent.download_torrent( + _, content, _, _, error_msg = TorrentHelper().download_torrent( url=torrent_url, cookie=site_config.cookie, ua=site_config.ua or settings.USER_AGENT, diff --git a/plugins.v2/doubanrank/__init__.py b/plugins.v2/doubanrank/__init__.py index 8459ac7..94484d2 100644 --- a/plugins.v2/doubanrank/__init__.py +++ b/plugins.v2/doubanrank/__init__.py @@ -45,9 +45,6 @@ class DoubanRank(_PluginBase): # 退出事件 _event = Event() # 私有属性 - downloadchain: DownloadChain = None - subscribechain: SubscribeChain = None - mediachain: MediaChain = None _scheduler = None _douban_address = { 'movie-ustop': 'https://rsshub.app/douban/movie/ustop', @@ -70,9 +67,6 @@ class DoubanRank(_PluginBase): _proxy = False def init_plugin(self, config: dict = None): - self.downloadchain = DownloadChain() - self.subscribechain = SubscribeChain() - self.mediachain = MediaChain() if config: self._enabled = config.get("enabled") @@ -574,9 +568,10 @@ class DoubanRank(_PluginBase): if douban_id: # 识别豆瓣信息 if settings.RECOGNIZE_SOURCE == "themoviedb": - tmdbinfo = self.mediachain.get_tmdbinfo_by_doubanid(doubanid=douban_id, mtype=meta.type) + tmdbinfo = MediaChain().get_tmdbinfo_by_doubanid(doubanid=douban_id, mtype=meta.type) if not tmdbinfo: - logger.warn(f'未能通过豆瓣ID {douban_id} 获取到TMDB信息,标题:{title},豆瓣ID:{douban_id}') + logger.warn( + f'未能通过豆瓣ID {douban_id} 获取到TMDB信息,标题:{title},豆瓣ID:{douban_id}') continue mediainfo = self.chain.recognize_media(meta=meta, tmdbid=tmdbinfo.get("id")) if not mediainfo: @@ -598,22 +593,23 @@ class DoubanRank(_PluginBase): logger.info(f'{mediainfo.title_year} 评分不符合要求') continue # 查询缺失的媒体信息 - exist_flag, _ = self.downloadchain.get_no_exists_info(meta=meta, mediainfo=mediainfo) + exist_flag, _ = DownloadChain().get_no_exists_info(meta=meta, mediainfo=mediainfo) if exist_flag: logger.info(f'{mediainfo.title_year} 媒体库中已存在') continue # 判断用户是否已经添加订阅 - if self.subscribechain.exists(mediainfo=mediainfo, meta=meta): + subscribechain = SubscribeChain() + if subscribechain.exists(mediainfo=mediainfo, meta=meta): logger.info(f'{mediainfo.title_year} 订阅已存在') continue # 添加订阅 - self.subscribechain.add(title=mediainfo.title, - year=mediainfo.year, - mtype=mediainfo.type, - tmdbid=mediainfo.tmdb_id, - season=meta.begin_season, - exist_ok=True, - username="豆瓣榜单") + subscribechain.add(title=mediainfo.title, + year=mediainfo.year, + mtype=mediainfo.type, + tmdbid=mediainfo.tmdb_id, + season=meta.begin_season, + exist_ok=True, + username="豆瓣榜单") # 存储历史记录 history.append({ "title": title, diff --git a/plugins.v2/doubansync/__init__.py b/plugins.v2/doubansync/__init__.py index 25b70a7..9ce4b9f 100644 --- a/plugins.v2/doubansync/__init__.py +++ b/plugins.v2/doubansync/__init__.py @@ -9,6 +9,7 @@ from apscheduler.triggers.cron import CronTrigger from app import schemas from app.chain.media import MediaChain +from app.db.subscribe_oper import SubscribeOper from app.db.user_oper import UserOper from app.schemas.types import MediaType, EventType, SystemConfigKey @@ -50,12 +51,6 @@ class DoubanSync(_PluginBase): _interests_url: str = "https://www.douban.com/feed/people/%s/interests" _scheduler: Optional[BackgroundScheduler] = None _cache_path: Optional[Path] = None - rsshelper = None - downloadchain = None - searchchain = None - subscribechain = None - mediachain = None - useroper = None # 配置属性 _enabled: bool = False @@ -69,12 +64,6 @@ class DoubanSync(_PluginBase): _search_download = False def init_plugin(self, config: dict = None): - self.rsshelper = RssHelper() - self.downloadchain = DownloadChain() - self.searchchain = SearchChain() - self.subscribechain = SubscribeChain() - self.mediachain = MediaChain() - self.useroper = UserOper() # 停止现有任务 self.stop_service() @@ -337,7 +326,7 @@ class DoubanSync(_PluginBase): } } ] - } + } ] }, { @@ -546,12 +535,13 @@ class DoubanSync(_PluginBase): except Exception as e: logger.error("退出插件失败:%s" % str(e)) - def __get_username_by_douban(self, user_id: str) -> Optional[str]: + @staticmethod + def __get_username_by_douban(user_id: str) -> Optional[str]: """ 根据豆瓣ID获取用户名 """ try: - return self.useroper.get_name(douban_userid=user_id) + return UserOper().get_name(douban_userid=user_id) except Exception as err: logger.warn(f'{err}, 需要 MoviePilot v2.2.6+ 版本') return None @@ -579,23 +569,28 @@ class DoubanSync(_PluginBase): logger.info(f"开始同步用户 {user_id} 的豆瓣想看数据 ...") url = self._interests_url % user_id if version == "v2": - results = self.rsshelper.parse(url, headers={ + results = RssHelper().parse(url, headers={ "User-Agent": settings.USER_AGENT }) else: - results = self.rsshelper.parse(url) + results = RssHelper().parse(url) if not results: logger.warn(f"未获取到用户 {user_id} 豆瓣RSS数据:{url}") continue else: logger.info(f"获取到用户 {user_id} 豆瓣RSS数据:{len(results)}") # 解析数据 + mediachain = MediaChain() + downloadchain = DownloadChain() + subscribechain = SubscribeChain() + searchchain = SearchChain() + subscribeoper = SubscribeOper() for result in results: try: dtype = result.get("title", "")[:2] title = result.get("title", "")[2:] # 增加豆瓣昵称,数据来源自app.helper.rss.py - nickname = result.get("nickname","") + nickname = result.get("nickname", "") if nickname: nickname = f"[{nickname}]" if dtype not in ["想看"]: @@ -620,7 +615,7 @@ class DoubanSync(_PluginBase): douban_info = self.chain.douban_info(doubanid=douban_id) meta.type = MediaType.MOVIE if douban_info.get("type") == "movie" else MediaType.TV if settings.RECOGNIZE_SOURCE == "themoviedb": - tmdbinfo = self.mediachain.get_tmdbinfo_by_doubanid(doubanid=douban_id, mtype=meta.type) + tmdbinfo = mediachain.get_tmdbinfo_by_doubanid(doubanid=douban_id, mtype=meta.type) if not tmdbinfo: logger.warn(f'未能通过豆瓣ID {douban_id} 获取到TMDB信息,标题:{title},豆瓣ID:{douban_id}') continue @@ -634,7 +629,7 @@ class DoubanSync(_PluginBase): logger.warn(f'豆瓣ID {douban_id} 未识别到媒体信息') continue # 查询缺失的媒体信息 - exist_flag, no_exists = self.downloadchain.get_no_exists_info(meta=meta, mediainfo=mediainfo) + exist_flag, no_exists = downloadchain.get_no_exists_info(meta=meta, mediainfo=mediainfo) if exist_flag: logger.info(f'{mediainfo.title_year} 媒体库中已存在') action = "exist" @@ -643,9 +638,10 @@ class DoubanSync(_PluginBase): real_name = self.__get_username_by_douban(user_id) if self._search_download: # 先搜索资源 - logger.info(f'媒体库中不存在或不完整,开启搜索下载,开始搜索 {mediainfo.title_year} 的资源...') - # 按订阅优先级规则组搜索过滤,站点为设置的订阅站点 - filter_results = self.searchchain.process( + logger.info( + f'媒体库中不存在或不完整,开启搜索下载,开始搜索 {mediainfo.title_year} 的资源...') + # 按订阅优先级规则组搜索过滤,站点为设置的订阅站点 + filter_results = searchchain.process( mediainfo=mediainfo, no_exists=no_exists, sites=self.systemconfig.get(SystemConfigKey.RssSites), @@ -656,7 +652,7 @@ class DoubanSync(_PluginBase): action = "download" if mediainfo.type == MediaType.MOVIE: # 电影类型调用单次下载 - download_id = self.downloadchain.download_single( + download_id = downloadchain.download_single( context=filter_results[0], username=real_name or f"豆瓣{nickname}想看" ) @@ -666,7 +662,7 @@ class DoubanSync(_PluginBase): action = "subscribe" else: # 电视剧类型调用批量下载 - downloaded_list, no_exists = self.downloadchain.batch_download( + downloaded_list, no_exists = downloadchain.batch_download( contexts=filter_results, no_exists=no_exists, username=real_name or f"豆瓣{nickname}想看" @@ -678,13 +674,13 @@ class DoubanSync(_PluginBase): # 更新订阅信息 logger.info(f'根据缺失剧集更新订阅信息 {mediainfo.title_year} ...') - subscribe = self.subscribechain.subscribeoper.get(sub_id) + subscribe = subscribeoper.get(sub_id) if subscribe: - self.subscribechain.finish_subscribe_or_not(subscribe=subscribe, - meta=meta, - mediainfo=mediainfo, - downloads=downloaded_list, - lefts=no_exists) + subscribechain.finish_subscribe_or_not(subscribe=subscribe, + meta=meta, + mediainfo=mediainfo, + downloads=downloaded_list, + lefts=no_exists) else: logger.info(f'未找到符合条件资源,添加订阅 {mediainfo.title_year} ...') @@ -714,8 +710,9 @@ class DoubanSync(_PluginBase): # 缓存只清理一次 self._clearflag = False - def add_subscribe(self, mediainfo, meta, nickname, real_name): - return self.subscribechain.add( + @staticmethod + def add_subscribe(mediainfo, meta, nickname, real_name): + return SubscribeChain().add( title=mediainfo.title, year=mediainfo.year, mtype=mediainfo.type, diff --git a/plugins.v2/downloadsitetag/__init__.py b/plugins.v2/downloadsitetag/__init__.py index 26b8275..c9e2603 100644 --- a/plugins.v2/downloadsitetag/__init__.py +++ b/plugins.v2/downloadsitetag/__init__.py @@ -45,9 +45,6 @@ class DownloadSiteTag(_PluginBase): # 退出事件 _event = threading.Event() # 私有属性 - downloadhistory_oper = None - sites_helper = None - downloader_helper = None _scheduler = None _enabled = False _onlyonce = False @@ -64,9 +61,6 @@ class DownloadSiteTag(_PluginBase): _downloaders = None def init_plugin(self, config: dict = None): - self.downloadhistory_oper = DownloadHistoryOper() - self.downloader_helper = DownloaderHelper() - self.sites_helper = SitesHelper() # 读取配置 if config: self._enabled = config.get("enabled") @@ -113,7 +107,7 @@ class DownloadSiteTag(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - services = self.downloader_helper.get_services(name_filters=self._downloaders) + services = DownloaderHelper().get_services(name_filters=self._downloaders) if not services: logger.warning("获取下载器实例失败,请检查配置") return None @@ -205,7 +199,7 @@ class DownloadSiteTag(_PluginBase): # 记录处理的种子, 供辅种(无下载历史)使用 dispose_history = {} # 所有站点索引 - indexers = [indexer.get("name") for indexer in self.sites_helper.get_indexers()] + indexers = [indexer.get("name") for indexer in SitesHelper().get_indexers()] # JackettIndexers索引器支持多个站点, 如果不存在历史记录, 则通过tracker会再次附加其他站点名称 indexers.append("JackettIndexers") indexers = set(indexers) @@ -230,6 +224,8 @@ class DownloadSiteTag(_PluginBase): # 按添加时间进行排序, 时间靠前的按大小和名称加入处理历史, 判定为原始种子, 其他为辅种 torrents = self._torrents_sort(torrents=torrents, dl_type=service.type) logger.info(f"{self.LOG_TAG}下载器 {downloader} 分析种子信息中 ...") + downloadhis = DownloadHistoryOper() + siteshelper = SitesHelper() for torrent in torrents: try: if self._event.is_set(): @@ -246,7 +242,7 @@ class DownloadSiteTag(_PluginBase): torrent_tags = self._get_label(torrent=torrent, dl_type=service.type) torrent_cat = self._get_category(torrent=torrent, dl_type=service.type) # 提取种子hash对应的下载历史 - history: DownloadHistory = self.downloadhistory_oper.get_by_hash(_hash) + history: DownloadHistory = downloadhis.get_by_hash(_hash) if not history: # 如果找到已处理种子的历史, 表明当前种子是辅种, 否则创建一个空DownloadHistory if _key and _key in dispose_history: @@ -273,7 +269,7 @@ class DownloadSiteTag(_PluginBase): break else: domain = StringUtils.get_url_domain(tracker) - site_info = self.sites_helper.get_indexer(domain) + site_info = siteshelper.get_indexer(domain) if site_info: history.torrent_site = site_info.get("name") break @@ -652,7 +648,7 @@ class DownloadSiteTag(_PluginBase): 'model': 'downloaders', 'label': '下载器', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] @@ -856,4 +852,4 @@ class DownloadSiteTag(_PluginBase): self._event.clear() self._scheduler = None except Exception as e: - print(str(e)) \ No newline at end of file + print(str(e)) diff --git a/plugins.v2/historytov2/__init__.py b/plugins.v2/historytov2/__init__.py index e163abe..16ce8a7 100644 --- a/plugins.v2/historytov2/__init__.py +++ b/plugins.v2/historytov2/__init__.py @@ -30,7 +30,6 @@ class HistoryToV2(_PluginBase): auth_level = 1 # 私有属性 - historyoper = None _enabled = False _host = None _username = None diff --git a/plugins.v2/imdbsource/__init__.py b/plugins.v2/imdbsource/__init__.py index 770a72e..f55e148 100644 --- a/plugins.v2/imdbsource/__init__.py +++ b/plugins.v2/imdbsource/__init__.py @@ -1,18 +1,13 @@ -import re -import json -from typing import Optional, Any, List, Dict, Tuple from datetime import datetime +from typing import Optional, Any, List, Dict, Tuple +from app import schemas from app.core.config import settings from app.core.event import eventmanager, Event -from app.log import logger from app.plugins import _PluginBase +from app.plugins.imdbsource.imdb_helper import ImdbHelper from app.schemas import DiscoverSourceEventData, MediaRecognizeConvertEventData, RecommendSourceEventData from app.schemas.types import ChainEventType, MediaType -from app.core.meta import MetaBase -from app.core.context import MediaInfo -from app.plugins.imdbsource.imdb_helper import ImdbHelper -from app import schemas from app.utils.http import RequestUtils @@ -128,135 +123,8 @@ class ImdbSource(_PluginBase): "id2": self.xxx2, } """ - # return {"recognize_media": (self.recognize_media, ModuleExecutionType.Hijack)} pass - @staticmethod - # @MediaInfo.source_processor("imdb") - def process_imdb_info(mediainfo: MediaInfo, info: dict): - """处理 IMDB 信息""" - mediainfo.source_info["imdb"] = info - if isinstance(info.get('media_type'), MediaType): - mediainfo.type = info.get('media_type') - elif info.get('media_type'): - mediainfo.type = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV - mediainfo.title = info.get("title") - mediainfo.release_date = info.get('release_date') - if info.get("id"): - mediainfo.source_id["imdb"] = info.get("id") - mediainfo.imdb_id = info.get('id') - if not mediainfo.source_id: - return - mediainfo.vote_average = round(float(info.get("rating").get("aggregate_rating")), 1) if info.get("rating") else 0 - mediainfo.overview = info.get('plot') - mediainfo.genre_ids = info.get('genre') or [] - # 风格 - if not mediainfo.genres: - mediainfo.genres = [{"id": genre, "name": genre} for genre in info.get("genres") or []] - if info.get('spoken_languages', []): - mediainfo.original_language = info.get('spoken_languages', [])[0].get("name") - mediainfo.en_title = info.get('primary_title') - mediainfo.title = info.get('primary_title') - mediainfo.original_title = info.get('original_title') - # mediainfo.release_date = info.get('start_year') - mediainfo.year = info.get('start_year') - if info.get('posters', []): - mediainfo.poster_path = info.get("posters", [])[0].get("url") - directors = [] - if info.get('directors', []): - for dn in info.get('directors', []): - director = dn.get("name") - if not director: - continue - d_ = {"name": director.get("display_name"), "id": director.get("id"), "avatars": director.get("avatars")} - directors.append(d_) - if info.get('writers', []): - for wn in info.get('writers', []): - writer = wn.get("name") - d_ = {"name": writer.get("display_name"), "id": writer.get("id"), "avatars": writer.get("avatars")} - directors.append(d_) - mediainfo.directors = directors - actors = [] - if info.get('casts', []): - for cast in info.get('casts', []): - cn = cast.get("name", {}) - character_name = cast.get("characters")[0] if cast.get("characters") else '' - d_ = {"name": cn.get("display_name"), "id": cn.get("id"), - "avatars": cn.get("avatars"), "character": character_name} - actors.append(d_) - - def recognize_media(self, meta: MetaBase = None, - mtype: MediaType = None, - imdbid: Optional[str] = None, - episode_group: Optional[str] = None, - cache: Optional[bool] = True, - **kwargs) -> Optional[MediaInfo]: - logger.warn(f"IMDb Source: {MetaBase.title}") - if not self._imdb_helper: - return None - if not imdbid and not meta: - return None - if not meta: - # 未提供元数据时,直接使用imdbid查询,不使用缓存 - cache_info = {} - elif not meta.name: - logger.warn("识别媒体信息时未提供元数据名称") - return None - cache_info = {} - if not cache_info or not cache: - info = None - if imdbid: - info = self._imdb_helper.get_info(mtype=mtype, imdbid=imdbid) - if not info and meta: - info = {} - names = list(dict.fromkeys([k for k in [meta.cn_name, meta.en_name] if k])) - for name in names: - if meta.begin_season: - logger.info(f"正在识别 {name} 第{meta.begin_season}季 ...") - else: - logger.info(f"正在识别 {name} ...") - if meta.type == MediaType.UNKNOWN and not meta.year: - info = self._imdb_helper.match_multi(name) - else: - if meta.type == MediaType.TV: - # 确定是电视 - info = self._imdb_helper.match(name=name, - year=meta.year, - mtype=meta.type,season_year=meta.year, - season_number=meta.begin_season) - if not info: - # 去掉年份再查一次 - info = self._imdb_helper.match(name=name, mtype=meta.type) - else: - # 有年份先按电影查 - info = self._imdb_helper.match(name=name, year=meta.year, mtype=MediaType.MOVIE) - # 没有再按电视剧查 - if not info: - info = self._imdb_helper.match(name=name, - year=meta.year, - mtype=MediaType.TV) - if not info: - # 去掉年份和类型再查一次 - info = self._imdb_helper.match_multi(name=name) - if info: - break - else: - info = None - if info: - # mediainfo = MediaInfo(source_info={"imdb": info}) - mediainfo = MediaInfo() - if meta: - logger.info(f"{meta.name} IMDB识别结果:{mediainfo.type.value} " - f"{mediainfo.title_year} " - f"{mediainfo.imdb_id}") - else: - logger.info(f"{imdbid} IMDB识别结果:{mediainfo.type.value} " - f"{mediainfo.title_year}") - return mediainfo - - logger.info(f"{meta.name if meta else imdbid} 未匹配到IMDB媒体信息") - return None - @staticmethod def __movie_to_media(movie_info: dict) -> schemas.MediaInfo: title = "" diff --git a/plugins.v2/iyuuautoseed/__init__.py b/plugins.v2/iyuuautoseed/__init__.py index a9d9766..c64cde1 100644 --- a/plugins.v2/iyuuautoseed/__init__.py +++ b/plugins.v2/iyuuautoseed/__init__.py @@ -48,10 +48,6 @@ class IYUUAutoSeed(_PluginBase): # 私有属性 _scheduler = None iyuu_helper = None - downloader_helper = None - sites_helper = None - site_oper = None - torrent_helper = None # 开关 _enabled = False _cron = None @@ -72,6 +68,7 @@ class IYUUAutoSeed(_PluginBase): _addhosttotag = False _size = None _clearcache = False + _auto_start = False # 退出事件 _event = Event() # 种子链接xpaths @@ -99,10 +96,7 @@ class IYUUAutoSeed(_PluginBase): cached = 0 def init_plugin(self, config: dict = None): - self.sites_helper = SitesHelper() - self.site_oper = SiteOper() - self.torrent_helper = TorrentHelper() - self.downloader_helper = DownloaderHelper() + # 读取配置 if config: self._enabled = config.get("enabled") @@ -128,8 +122,8 @@ class IYUUAutoSeed(_PluginBase): self._success_caches = [] if self._clearcache else config.get("success_caches") or [] # 过滤掉已删除的站点 - all_sites = [site.id for site in self.site_oper.list_order_by_pri()] + [site.get("id") for site in - self.__custom_sites()] + all_sites = [site.id for site in SiteOper().list_order_by_pri()] + [site.get("id") for site in + self.__custom_sites()] self._sites = [site_id for site_id in all_sites if site_id in self._sites] self.__update_config() @@ -171,7 +165,7 @@ class IYUUAutoSeed(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - services = self.downloader_helper.get_services(name_filters=self._downloaders) + services = DownloaderHelper().get_services(name_filters=self._downloaders) if not services: logger.warning("获取下载器实例失败,请检查配置") return None @@ -198,7 +192,7 @@ class IYUUAutoSeed(_PluginBase): logger.debug("尚未配置主辅分离下载器,辅种不分离") return None - service = self.downloader_helper.get_service(name=self._auto_downloader) + service = DownloaderHelper().get_service(name=self._auto_downloader) if not service: logger.warning("获取主辅分离下载器实例失败,请检查配置") return None @@ -248,7 +242,7 @@ class IYUUAutoSeed(_PluginBase): # 站点的可选项 site_options = ([{"title": site.name, "value": site.id} - for site in self.site_oper.list_order_by_pri()] + for site in SiteOper().list_order_by_pri()] + [{"title": site.get("name"), "value": site.get("id")} for site in customSites]) return [ @@ -381,7 +375,7 @@ class IYUUAutoSeed(_PluginBase): 'model': 'downloaders', 'label': '下载器', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] @@ -401,7 +395,7 @@ class IYUUAutoSeed(_PluginBase): 'model': 'auto_downloader', 'label': '主辅分离', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] @@ -1048,7 +1042,8 @@ class IYUUAutoSeed(_PluginBase): # 查询站点 site_domain = StringUtils.get_url_domain(site_url) # 站点信息 - site_info = self.sites_helper.get_indexer(site_domain) + sites_helper = SitesHelper() + site_info = sites_helper.get_indexer(site_domain) if not site_info or not site_info.get('url'): logger.debug(f"没有维护种子对应的站点:{site_url}") return False @@ -1064,7 +1059,7 @@ class IYUUAutoSeed(_PluginBase): self.exist += 1 return False # 站点流控 - check, checkmsg = self.sites_helper.check(site_domain) + check, checkmsg = sites_helper.check(site_domain) if check: logger.warn(checkmsg) self.fail += 1 @@ -1086,7 +1081,7 @@ class IYUUAutoSeed(_PluginBase): else: torrent_url += "?https=1" # 下载种子文件 - _, content, _, _, error_msg = self.torrent_helper.download_torrent( + _, content, _, _, error_msg = TorrentHelper().download_torrent( url=torrent_url, cookie=site_info.get("cookie"), ua=site_info.get("ua") or settings.USER_AGENT, diff --git a/plugins.v2/libraryscraper/__init__.py b/plugins.v2/libraryscraper/__init__.py index 0ef398c..8364440 100644 --- a/plugins.v2/libraryscraper/__init__.py +++ b/plugins.v2/libraryscraper/__init__.py @@ -40,8 +40,6 @@ class LibraryScraper(_PluginBase): user_level = 1 # 私有属性 - transferhis = None - mediachain = None _scheduler = None _scraper = None # 限速开关 @@ -55,7 +53,7 @@ class LibraryScraper(_PluginBase): _event = Event() def init_plugin(self, config: dict = None): - self.mediachain = MediaChain() + # 读取配置 if config: self._enabled = config.get("enabled") @@ -70,7 +68,6 @@ class LibraryScraper(_PluginBase): # 启动定时任务 & 立即运行一次 if self._enabled or self._onlyonce: - self.transferhis = TransferHistoryOper() if self._onlyonce: logger.info(f"媒体库刮削服务,立即运行一次") @@ -401,14 +398,14 @@ class LibraryScraper(_PluginBase): # 如果未开启新增已入库媒体是否跟随TMDB信息变化则根据tmdbid查询之前的title if not settings.SCRAP_FOLLOW_TMDB: - transfer_history = self.transferhis.get_by_type_tmdbid(tmdbid=mediainfo.tmdb_id, - mtype=mediainfo.type.value) + transfer_history = TransferHistoryOper().get_by_type_tmdbid(tmdbid=mediainfo.tmdb_id, + mtype=mediainfo.type.value) if transfer_history: mediainfo.title = transfer_history.title # 获取图片 self.chain.obtain_images(mediainfo) # 刮削 - self.mediachain.scrape_metadata( + MediaChain().scrape_metadata( fileitem=schemas.FileItem( storage="local", type="dir", diff --git a/plugins.v2/mediaservermsg/__init__.py b/plugins.v2/mediaservermsg/__init__.py index 80e4105..9ee7608 100644 --- a/plugins.v2/mediaservermsg/__init__.py +++ b/plugins.v2/mediaservermsg/__init__.py @@ -31,7 +31,6 @@ class MediaServerMsg(_PluginBase): auth_level = 1 # 私有属性 - mediaserver_helper = None _enabled = False _add_play_link = False _mediaservers = None @@ -59,7 +58,7 @@ class MediaServerMsg(_PluginBase): } def init_plugin(self, config: dict = None): - self.mediaserver_helper = MediaServerHelper() + if config: self._enabled = config.get("enabled") self._types = config.get("types") or [] @@ -74,7 +73,7 @@ class MediaServerMsg(_PluginBase): logger.warning("尚未配置媒体服务器,请检查配置") return None - services = self.mediaserver_helper.get_services(type_filter=type_filter, name_filters=self._mediaservers) + services = MediaServerHelper().get_services(type_filter=type_filter, name_filters=self._mediaservers) if not services: logger.warning("获取媒体服务器实例失败,请检查配置") return None @@ -181,7 +180,7 @@ class MediaServerMsg(_PluginBase): 'model': 'mediaservers', 'label': '媒体服务器', 'items': [{"title": config.name, "value": config.name} - for config in self.mediaserver_helper.get_configs().values()] + for config in MediaServerHelper().get_configs().values()] } } ] @@ -341,7 +340,7 @@ class MediaServerMsg(_PluginBase): if service: play_link = service.instance.get_play_url(event_info.item_id) elif event_info.channel: - services = self.mediaserver_helper.get_services(type_filter=event_info.channel) + services = MediaServerHelper().get_services(type_filter=event_info.channel) for service in services.values(): play_link = service.instance.get_play_url(event_info.item_id) if play_link: diff --git a/plugins.v2/mediaserverrefresh/__init__.py b/plugins.v2/mediaserverrefresh/__init__.py index 87dc621..67f2fd3 100644 --- a/plugins.v2/mediaserverrefresh/__init__.py +++ b/plugins.v2/mediaserverrefresh/__init__.py @@ -32,13 +32,12 @@ class MediaServerRefresh(_PluginBase): auth_level = 1 # 私有属性 - mediaserver_helper = None _enabled = False _delay = 0 _mediaservers = None def init_plugin(self, config: dict = None): - self.mediaserver_helper = MediaServerHelper() + if config: self._enabled = config.get("enabled") self._delay = config.get("delay") or 0 @@ -53,7 +52,7 @@ class MediaServerRefresh(_PluginBase): logger.warning("尚未配置媒体服务器,请检查配置") return None - services = self.mediaserver_helper.get_services(name_filters=self._mediaservers) + services = MediaServerHelper().get_services(name_filters=self._mediaservers) if not services: logger.warning("获取媒体服务器实例失败,请检查配置") return None @@ -128,7 +127,7 @@ class MediaServerRefresh(_PluginBase): 'model': 'mediaservers', 'label': '媒体服务器', 'items': [{"title": config.name, "value": config.name} - for config in self.mediaserver_helper.get_configs().values()] + for config in MediaServerHelper().get_configs().values()] } } ] diff --git a/plugins.v2/personmeta/__init__.py b/plugins.v2/personmeta/__init__.py index 4be5e26..d538d28 100644 --- a/plugins.v2/personmeta/__init__.py +++ b/plugins.v2/personmeta/__init__.py @@ -55,9 +55,6 @@ class PersonMeta(_PluginBase): # 私有属性 _scheduler = None - tmdbchain = None - mschain = None - mediaserver_helper = None _enabled = False _onlyonce = False _cron = None @@ -67,9 +64,7 @@ class PersonMeta(_PluginBase): _mediaservers = [] def init_plugin(self, config: dict = None): - self.tmdbchain = TmdbChain() - self.mschain = MediaServerChain() - self.mediaserver_helper = MediaServerHelper() + if config: self._enabled = config.get("enabled") self._onlyonce = config.get("onlyonce") @@ -266,7 +261,7 @@ class PersonMeta(_PluginBase): 'model': 'mediaservers', 'label': '媒体服务器', 'items': [{"title": config.name, "value": config.name} - for config in self.mediaserver_helper.get_configs().values()] + for config in MediaServerHelper().get_configs().values()] } } ] @@ -316,7 +311,7 @@ class PersonMeta(_PluginBase): logger.warning("尚未配置媒体服务器,请检查配置") return None - services = self.mediaserver_helper.get_services(type_filter=type_filter, name_filters=self._mediaservers) + services = MediaServerHelper().get_services(type_filter=type_filter, name_filters=self._mediaservers) if not services: logger.warning("获取媒体服务器实例失败,请检查配置") return None @@ -355,7 +350,7 @@ class PersonMeta(_PluginBase): logger.warn(f"{mediainfo.title_year} 在媒体库中不存在") return # 查询条目详情 - iteminfo = self.mschain.iteminfo(server=existsinfo.server, item_id=existsinfo.itemid) + iteminfo = MediaServerChain().iteminfo(server=existsinfo.server, item_id=existsinfo.itemid) if not iteminfo: logger.warn(f"{mediainfo.title_year} 条目详情获取失败") return @@ -371,12 +366,13 @@ class PersonMeta(_PluginBase): service_infos = self.service_infos() if not service_infos: return + mediaserverchain = MediaServerChain() for server, service in service_infos.items(): # 扫描所有媒体库 logger.info(f"开始刮削服务器 {server} 的演员信息 ...") - for library in self.mschain.librarys(server): + for library in mediaserverchain.librarys(server): logger.info(f"开始刮削媒体库 {library.name} 的演员信息 ...") - for item in self.mschain.items(server, library.id): + for item in mediaserverchain.items(server, library.id): if not item: continue if not item.item_id: @@ -577,7 +573,7 @@ class PersonMeta(_PluginBase): # 从TMDB信息中更新人物信息 person_tmdbid, person_imdbid = __get_peopleid(personinfo) if person_tmdbid: - person_detail = self.tmdbchain.person_detail(int(person_tmdbid)) + person_detail = TmdbChain().person_detail(int(person_tmdbid)) if person_detail: cn_name = self.__get_chinese_name(person_detail) # 图片优先从TMDB获取 diff --git a/plugins.v2/playletcategory/__init__.py b/plugins.v2/playletcategory/__init__.py index 109d4e0..2fd85e0 100644 --- a/plugins.v2/playletcategory/__init__.py +++ b/plugins.v2/playletcategory/__init__.py @@ -1,8 +1,8 @@ import random -import time import shutil import subprocess import threading +import time from pathlib import Path from typing import Any, List, Dict, Tuple @@ -12,7 +12,6 @@ from app.core.event import eventmanager, Event from app.log import logger from app.plugins import _PluginBase from app.schemas import TransferInfo -from app.schemas.file import FileItem from app.schemas.types import EventType, MediaType, NotificationType from app.utils.system import SystemUtils @@ -320,9 +319,7 @@ class PlayletCategory(_PluginBase): try: # 相对路径 relative_path = file.relative_to(target_path) - logger.debug(f"relative_path:{to_path}") to_path = new_path / relative_path - logger.debug(f"to_path:{to_path}") shutil.move(file, to_path) except Exception as e: logger.error(f"移动文件失败:{e}") diff --git a/plugins.v2/qbcommand/__init__.py b/plugins.v2/qbcommand/__init__.py index a6c79d6..b08c649 100644 --- a/plugins.v2/qbcommand/__init__.py +++ b/plugins.v2/qbcommand/__init__.py @@ -41,8 +41,6 @@ class QbCommand(_PluginBase): auth_level = 1 # 私有属性 - _sites = None - _siteoper = None _qb = None _enabled: bool = False _notify: bool = False @@ -62,10 +60,10 @@ class QbCommand(_PluginBase): _multi_level_root_domain = ["edu.cn", "com.cn", "net.cn", "org.cn"] _scheduler = None _exclude_dirs = "" + _downloaders = [] + def init_plugin(self, config: dict = None): - self._sites = SitesHelper() - self._siteoper = SiteOper() - self.downloader_helper = DownloaderHelper() + # 停止现有任务 self.stop_service() # 读取配置 @@ -87,7 +85,7 @@ class QbCommand(_PluginBase): self._op_site_ids = config.get("op_site_ids") or [] self._downloaders = config.get("downloaders") # 查询所有站点 - all_sites = [site for site in self._sites.get_indexers() if not site.get("public")] + self.__custom_sites() + all_sites = [site for site in SitesHelper().get_indexers() if not site.get("public")] + self.__custom_sites() # 过滤掉没有选中的站点 self._op_sites = [site for site in all_sites if site.get("id") in self._op_site_ids] self._exclude_dirs = config.get("exclude_dirs") or "" @@ -101,8 +99,7 @@ class QbCommand(_PluginBase): self._scheduler.add_job( self.pause_torrent, "date", - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) - + timedelta(seconds=3), + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), ) elif self._only_resume_once: self._scheduler = BackgroundScheduler(timezone=settings.TZ) @@ -110,8 +107,7 @@ class QbCommand(_PluginBase): self._scheduler.add_job( self.resume_torrent, "date", - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) - + timedelta(seconds=3), + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), ) self._only_resume_once = False @@ -136,9 +132,9 @@ class QbCommand(_PluginBase): self._scheduler.start() if ( - self._only_pause_upload - or self._only_pause_download - or self._only_pause_checking + self._only_pause_upload + or self._only_pause_download + or self._only_pause_checking ): if self._only_pause_upload: self._scheduler = BackgroundScheduler(timezone=settings.TZ) @@ -146,8 +142,7 @@ class QbCommand(_PluginBase): self._scheduler.add_job( self.pause_torrent, "date", - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) - + timedelta(seconds=3), + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), kwargs={ 'type': self.TorrentType.UPLOADING } @@ -158,8 +153,7 @@ class QbCommand(_PluginBase): self._scheduler.add_job( self.pause_torrent, "date", - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) - + timedelta(seconds=3), + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), kwargs={ 'type': self.TorrentType.DOWNLOADING } @@ -170,8 +164,7 @@ class QbCommand(_PluginBase): self._scheduler.add_job( self.pause_torrent, "date", - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) - + timedelta(seconds=3), + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), kwargs={ 'type': self.TorrentType.CHECKING } @@ -201,7 +194,7 @@ class QbCommand(_PluginBase): self.set_limit(self._upload_limit, self._download_limit) @property - def service_info(self) -> Optional[ServiceInfo]: + def service_info(self) -> Optional[Dict[str, ServiceInfo]]: """ 服务信息 """ @@ -209,7 +202,7 @@ class QbCommand(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - services = self.downloader_helper.get_services(name_filters=self._downloaders) + services = DownloaderHelper().get_services(name_filters=self._downloaders) if not services: logger.warning("获取下载器实例失败,请检查配置") @@ -230,15 +223,17 @@ class QbCommand(_PluginBase): return active_services - def check_is_qb(self, service_info) -> bool: + @staticmethod + def check_is_qb(service_info) -> bool: """ 检查下载器类型是否为 qbittorrent 或 transmission """ - if self.downloader_helper.is_downloader(service_type="qbittorrent", service=service_info): + if DownloaderHelper().is_downloader(service_type="qbittorrent", service=service_info): return True - elif self.downloader_helper.is_downloader(service_type="transmission", service=service_info): + elif DownloaderHelper().is_downloader(service_type="transmission", service=service_info): return False return False + def get_state(self) -> bool: return self._enabled @@ -409,9 +404,9 @@ class QbCommand(_PluginBase): if torrent.state_enum.is_uploading and not torrent.state_enum.is_paused: uploading_torrents.append(torrent.get("hash")) elif ( - torrent.state_enum.is_downloading - and not torrent.state_enum.is_paused - and not torrent.state_enum.is_checking + torrent.state_enum.is_downloading + and not torrent.state_enum.is_paused + and not torrent.state_enum.is_checking ): downloading_torrents.append(torrent.get("hash")) elif torrent.state_enum.is_checking: @@ -476,7 +471,7 @@ class QbCommand(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue all_torrents = self.get_all_torrents(service) hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = ( @@ -498,12 +493,12 @@ class QbCommand(_PluginBase): mtype=NotificationType.SiteMessage, title=f"【下载器{downloader_name}暂停任务启动】", text=f"种子总数: {len(all_torrents)} \n" - f"做种数量: {len(hash_uploading)}\n" - f"下载数量: {len(hash_downloading)}\n" - f"检查数量: {len(hash_checking)}\n" - f"暂停数量: {len(hash_paused)}\n" - f"错误数量: {len(hash_error)}\n" - f"暂停操作中请稍等...\n", + f"做种数量: {len(hash_uploading)}\n" + f"下载数量: {len(hash_downloading)}\n" + f"检查数量: {len(hash_checking)}\n" + f"暂停数量: {len(hash_paused)}\n" + f"错误数量: {len(hash_error)}\n" + f"暂停操作中请稍等...\n", ) pause_torrents = self.filter_pause_torrents(all_torrents) hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = ( @@ -551,11 +546,11 @@ class QbCommand(_PluginBase): mtype=NotificationType.SiteMessage, title=f"【下载器{downloader_name}暂停任务完成】", text=f"种子总数: {len(all_torrents)} \n" - f"做种数量: {len(hash_uploading)}\n" - f"下载数量: {len(hash_downloading)}\n" - f"检查数量: {len(hash_checking)}\n" - f"暂停数量: {len(hash_paused)}\n" - f"错误数量: {len(hash_error)}\n", + f"做种数量: {len(hash_uploading)}\n" + f"下载数量: {len(hash_downloading)}\n" + f"检查数量: {len(hash_checking)}\n" + f"暂停数量: {len(hash_paused)}\n" + f"错误数量: {len(hash_error)}\n", ) def __is_excluded(self, file_path) -> bool: @@ -566,6 +561,7 @@ class QbCommand(_PluginBase): if exclude_dir and exclude_dir in str(file_path): return True return False + def filter_pause_torrents(self, all_torrents): torrents = [] for torrent in all_torrents: @@ -592,7 +588,7 @@ class QbCommand(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue all_torrents = self.get_all_torrents(service) hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = ( @@ -613,12 +609,12 @@ class QbCommand(_PluginBase): mtype=NotificationType.SiteMessage, title=f"【下载器{downloader_name}开始任务启动】", text=f"种子总数: {len(all_torrents)} \n" - f"做种数量: {len(hash_uploading)}\n" - f"下载数量: {len(hash_downloading)}\n" - f"检查数量: {len(hash_checking)}\n" - f"暂停数量: {len(hash_paused)}\n" - f"错误数量: {len(hash_error)}\n" - f"开始操作中请稍等...\n", + f"做种数量: {len(hash_uploading)}\n" + f"下载数量: {len(hash_downloading)}\n" + f"检查数量: {len(hash_checking)}\n" + f"暂停数量: {len(hash_paused)}\n" + f"错误数量: {len(hash_error)}\n" + f"开始操作中请稍等...\n", ) resume_torrents = self.filter_resume_torrents(all_torrents) @@ -655,11 +651,11 @@ class QbCommand(_PluginBase): mtype=NotificationType.SiteMessage, title=f"【下载器{downloader_name}开始任务完成】", text=f"种子总数: {len(all_torrents)} \n" - f"做种数量: {len(hash_uploading)}\n" - f"下载数量: {len(hash_downloading)}\n" - f"检查数量: {len(hash_checking)}\n" - f"暂停数量: {len(hash_paused)}\n" - f"错误数量: {len(hash_error)}\n", + f"做种数量: {len(hash_uploading)}\n" + f"下载数量: {len(hash_downloading)}\n" + f"检查数量: {len(hash_checking)}\n" + f"暂停数量: {len(hash_paused)}\n" + f"错误数量: {len(hash_error)}\n", ) def filter_resume_torrents(self, all_torrents): @@ -714,7 +710,7 @@ class QbCommand(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue all_torrents = self.get_all_torrents(service) hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = ( @@ -734,11 +730,11 @@ class QbCommand(_PluginBase): mtype=NotificationType.SiteMessage, title=f"【下载器{downloader_name}任务状态】", text=f"种子总数: {len(all_torrents)} \n" - f"做种数量: {len(hash_uploading)}\n" - f"下载数量: {len(hash_downloading)}\n" - f"检查数量: {len(hash_checking)}\n" - f"暂停数量: {len(hash_paused)}\n" - f"错误数量: {len(hash_error)}\n" + f"做种数量: {len(hash_uploading)}\n" + f"下载数量: {len(hash_downloading)}\n" + f"检查数量: {len(hash_checking)}\n" + f"暂停数量: {len(hash_paused)}\n" + f"错误数量: {len(hash_error)}\n" ) @eventmanager.register(EventType.PluginAction) @@ -766,10 +762,10 @@ class QbCommand(_PluginBase): return True if ( - not upload_limit - or not upload_limit.isdigit() - or not download_limit - or not download_limit.isdigit() + not upload_limit + or not upload_limit.isdigit() + or not download_limit + or not download_limit.isdigit() ): self.post_message( mtype=NotificationType.SiteMessage, @@ -783,7 +779,7 @@ class QbCommand(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue flag = flag and downloader_obj.set_speed_limit( download_limit=int(download_limit), upload_limit=int(upload_limit) @@ -806,7 +802,7 @@ class QbCommand(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue download_limit_current_val, _ = downloader_obj.get_speed_limit() flag = flag and downloader_obj.set_speed_limit( @@ -831,7 +827,7 @@ class QbCommand(_PluginBase): downloader_name = service.name downloader_obj = service.instance if not downloader_obj: - logger.error(f"{self.LOG_TAG} 获取下载器失败 {downloader_name}") + logger.error(f"获取下载器失败 {downloader_name}") continue _, upload_limit_current_val = downloader_obj.get_speed_limit() flag = flag and downloader_obj.set_speed_limit( @@ -856,7 +852,7 @@ class QbCommand(_PluginBase): elif flag is None and self._enabled and self._enable_upload_limit: flag = self.set_upload_limit(upload_limit) - if flag == True: + if flag is True: logger.info(f"设置QB限速成功") if self._notify: if upload_limit == 0: @@ -872,7 +868,7 @@ class QbCommand(_PluginBase): title=f"【QB远程操作】", text=text, ) - elif flag == False: + elif flag is False: logger.error(f"QB设置限速失败") if self._notify: self.post_message( @@ -881,7 +877,8 @@ class QbCommand(_PluginBase): text=f"设置QB限速失败", ) - def get_torrent_tracker(self, torrent): + @staticmethod + def get_torrent_tracker(torrent): """ qb解析 tracker :return: tracker url @@ -937,11 +934,11 @@ class QbCommand(_PluginBase): customSites = self.__custom_sites() site_options = [ - {"title": site.name, "value": site.id} - for site in self._siteoper.list_order_by_pri() - ] + [ - {"title": site.get("name"), "value": site.get("id")} for site in customSites - ] + {"title": site.name, "value": site.id} + for site in SiteOper().list_order_by_pri() + ] + [ + {"title": site.get("name"), "value": site.get("id")} for site in customSites + ] return [ { "component": "VForm", @@ -1021,7 +1018,7 @@ class QbCommand(_PluginBase): 'model': 'downloaders', 'label': '下载器', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] diff --git a/plugins.v2/rsssubscribe/__init__.py b/plugins.v2/rsssubscribe/__init__.py index 9af08e9..f2eb689 100644 --- a/plugins.v2/rsssubscribe/__init__.py +++ b/plugins.v2/rsssubscribe/__init__.py @@ -11,7 +11,6 @@ from apscheduler.triggers.cron import CronTrigger from app import schemas from app.chain.download import DownloadChain -from app.chain.search import SearchChain from app.chain.subscribe import SubscribeChain from app.core.config import settings from app.core.context import MediaInfo, TorrentInfo, Context @@ -48,10 +47,6 @@ class RssSubscribe(_PluginBase): # 私有变量 _scheduler: Optional[BackgroundScheduler] = None _cache_path: Optional[Path] = None - rsshelper = None - downloadchain = None - searchchain = None - subscribechain = None # 配置属性 _enabled: bool = False @@ -70,10 +65,6 @@ class RssSubscribe(_PluginBase): _size_range: str = "" def init_plugin(self, config: dict = None): - self.rsshelper = RssHelper() - self.downloadchain = DownloadChain() - self.searchchain = SearchChain() - self.subscribechain = SubscribeChain() # 停止现有任务 self.stop_service() @@ -618,12 +609,14 @@ class RssSubscribe(_PluginBase): history = [] else: history: List[dict] = self.get_data('history') or [] + downloadchain = DownloadChain() + subscribechain = SubscribeChain() for url in self._address.split("\n"): # 处理每一个RSS链接 if not url: continue logger.info(f"开始刷新RSS:{url} ...") - results = self.rsshelper.parse(url, proxy=self._proxy) + results = RssHelper().parse(url, proxy=self._proxy) if not results: logger.error(f"未获取到RSS数据:{url}") return @@ -704,7 +697,7 @@ class RssSubscribe(_PluginBase): # 下载或订阅 if self._action == "download": # 添加下载 - result = self.downloadchain.download_single( + result = downloadchain.download_single( context=Context( meta_info=meta, media_info=mediainfo, @@ -718,18 +711,18 @@ class RssSubscribe(_PluginBase): continue else: # 检查是否在订阅中 - subflag = self.subscribechain.exists(mediainfo=mediainfo, meta=meta) + subflag = subscribechain.exists(mediainfo=mediainfo, meta=meta) if subflag: logger.info(f'{mediainfo.title_year} {meta.season} 正在订阅中') continue # 添加订阅 - self.subscribechain.add(title=mediainfo.title, - year=mediainfo.year, - mtype=mediainfo.type, - tmdbid=mediainfo.tmdb_id, - season=meta.begin_season, - exist_ok=True, - username="RSS订阅") + subscribechain.add(title=mediainfo.title, + year=mediainfo.year, + mtype=mediainfo.type, + tmdbid=mediainfo.tmdb_id, + season=meta.begin_season, + exist_ok=True, + username="RSS订阅") # 存储历史记录 history.append({ "title": f"{mediainfo.title} {meta.season}", @@ -772,4 +765,4 @@ class RssSubscribe(_PluginBase): """ 检查字符串是否表示单个数字或数字范围(如'5', '5.5', '5-10' 或 '5.5-10.2') """ - return bool(re.match(r"^\d+(\.\d+)?(-\d+(\.\d+)?)?$", value)) \ No newline at end of file + return bool(re.match(r"^\d+(\.\d+)?(-\d+(\.\d+)?)?$", value)) diff --git a/plugins.v2/sitestatistic/__init__.py b/plugins.v2/sitestatistic/__init__.py index 5056427..ed6cad0 100644 --- a/plugins.v2/sitestatistic/__init__.py +++ b/plugins.v2/sitestatistic/__init__.py @@ -45,9 +45,6 @@ class SiteStatistic(_PluginBase): auth_level = 2 # 配置属性 - siteoper = None - siteshelper = None - sitechain = None _enabled: bool = False _onlyonce: bool = False _dashboard_type: str = "today" @@ -55,9 +52,6 @@ class SiteStatistic(_PluginBase): _scheduler = None def init_plugin(self, config: dict = None): - self.siteoper = SiteOper() - self.siteshelper = SitesHelper() - self.sitechain = SiteChain() # 停止现有任务 self.stop_service() @@ -72,7 +66,7 @@ class SiteStatistic(_PluginBase): if self._onlyonce: config["onlyonce"] = False self._scheduler = BackgroundScheduler(timezone=settings.TZ) - self._scheduler.add_job(self.sitechain.refresh_userdatas, "date", + self._scheduler.add_job(SiteChain().refresh_userdatas, "date", run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), name="站点数据统计服务") self._scheduler.print_jobs() @@ -263,13 +257,14 @@ class SiteStatistic(_PluginBase): self.post_message(mtype=NotificationType.SiteMessage, title="站点数据统计", text="\n".join(sorted_messages)) - def __get_data(self) -> Tuple[str, List[SiteUserData], List[SiteUserData]]: + @staticmethod + def __get_data() -> Tuple[str, List[SiteUserData], List[SiteUserData]]: """ 获取最近一次统计的日期、最近一次统计的站点数据、上一次的站点数据 如果上一次某个站点数据缺失,则 fallback 到该站点之前最近有数据的日期 """ # 获取所有原始数据 - raw_data_list: List[SiteUserData] = self.siteoper.get_userdata() + raw_data_list: List[SiteUserData] = SiteOper().get_userdata() if not raw_data_list: return "", [], [] @@ -1009,13 +1004,14 @@ class SiteStatistic(_PluginBase): def stop_service(self): pass - def refresh_by_domain(self, domain: str, apikey: str) -> schemas.Response: + @staticmethod + def refresh_by_domain(domain: str, apikey: str) -> schemas.Response: """ 刷新一个站点数据,可由API调用 """ if apikey != settings.API_TOKEN: return schemas.Response(success=False, message="API密钥错误") - site_info = self.siteshelper.get_indexer(domain) + site_info = SitesHelper().get_indexer(domain) if site_info: site_data = SiteChain().refresh_userdata(site=site_info) if site_data: diff --git a/plugins.v2/speedlimiter/__init__.py b/plugins.v2/speedlimiter/__init__.py index 3f01708..23e52b2 100644 --- a/plugins.v2/speedlimiter/__init__.py +++ b/plugins.v2/speedlimiter/__init__.py @@ -32,8 +32,6 @@ class SpeedLimiter(_PluginBase): auth_level = 1 # 私有属性 - downloader_helper = None - mediaserver_helper = None _scheduler = None _enabled: bool = False _notify: bool = False @@ -54,8 +52,7 @@ class SpeedLimiter(_PluginBase): _exclude_path = "" def init_plugin(self, config: dict = None): - self.downloader_helper = DownloaderHelper() - self.mediaserver_helper = MediaServerHelper() + # 读取配置 if config: self._enabled = config.get("enabled") @@ -183,7 +180,7 @@ class SpeedLimiter(_PluginBase): 'model': 'downloader', 'label': '下载器', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] @@ -402,7 +399,7 @@ class SpeedLimiter(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - services = self.downloader_helper.get_services(name_filters=self._downloader) + services = DownloaderHelper().get_services(name_filters=self._downloader) if not services: logger.warning("获取下载器实例失败,请检查配置") return None @@ -442,7 +439,7 @@ class SpeedLimiter(_PluginBase): return # 当前播放的总比特率 total_bit_rate = 0 - media_servers = self.mediaserver_helper.get_services() + media_servers = MediaServerHelper().get_services() if not media_servers: return # 查询所有媒体服务器状态 diff --git a/plugins.v2/subscribeclear/__init__.py b/plugins.v2/subscribeclear/__init__.py index 082468c..5ef0685 100644 --- a/plugins.v2/subscribeclear/__init__.py +++ b/plugins.v2/subscribeclear/__init__.py @@ -33,10 +33,9 @@ class SubscribeClear(_PluginBase): # 私有属性 _titles = [] _episodes = [] - downloader_helper = None def init_plugin(self, config: dict = None): - self.downloader_helper = DownloaderHelper() + if config: self._titles = config.get("titles") or [] self._episodes = config.get("episodes") or [] @@ -48,11 +47,10 @@ class SubscribeClear(_PluginBase): def clear_history(self, titles: List[str], episodes: List[str]): logger.info(f"清除下载历史记录:{titles} {episodes}") - data = self.get_data() - down_oper = DownloadHistoryOper() - downloader_history ={} + data = self.get_download_data() + downloader_history = {} for d in data: - if d.title in titles or d.id in episodes: + if d.title in titles or d.id in episodes: tmp = downloader_history.get(d.downloader) if not tmp: tmp = [] @@ -70,7 +68,7 @@ class SubscribeClear(_PluginBase): history_torrents = {} for t in torrents: logger.info(f"种子信息: {t}") - history_torrents[t.hash]=t + history_torrents[t.hash] = t for h in history: # 判断当前历史记录的hash是否在未找到的hash列表中 if h.download_hash not in history_torrents.keys(): @@ -79,43 +77,39 @@ class SubscribeClear(_PluginBase): else: # 从下载器删除种子 self.delete_download_history(h, history_torrents[h.download_hash]) - - - - def delete_data(self, history: DownloadHistory): + @staticmethod + def delete_data(history: DownloadHistory): """ 从订阅记录中删除该信息 """ try: down_oper = DownloadHistoryOper() down_oper.delete_history(history.id) - logger.info(f"删除下载历史记录:{history.id} {history.title} {history.seasons} {history.episodes} {history.download_hash}") + logger.info( + f"删除下载历史记录:{history.id} {history.title} {history.seasons} {history.episodes} {history.download_hash}") return True except Exception as e: logger.error(f"删除下载历史记录失败:{str(e)}") return False - - - def delete_download_history(self,history: DownloadHistory, torrent: Any): + def delete_download_history(self, history: DownloadHistory, torrent: Any): downloader_name = history.downloader downloader_obj = self.__get_downloader(downloader_name) - logger.info(f"删除种子信息:{history.id} {history.title} {history.seasons} {history.episodes} {history.download_hash}") + logger.info( + f"删除种子信息:{history.id} {history.title} {history.seasons} {history.episodes} {history.download_hash}") hashs = [history.download_hash] # 处理辅种 torrents, error = downloader_obj.get_torrents() - if error : + if error: logger.error(f"获取辅种信息失败: {error}") else: for t in torrents: if t.name == torrent.name and t.size == torrent.size: hashs.append(t.hash) - downloader_obj.delete_torrents(delete_file=True,ids=hashs) + downloader_obj.delete_torrents(delete_file=True, ids=hashs) self.delete_data(history) - - def get_state(self) -> bool: return True @@ -141,17 +135,17 @@ class SubscribeClear(_PluginBase): def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: # 获取下载历史数据 - histories = self.get_data() - + histories = self.get_download_data() + # 构造标题和剧集列表 titles = [] episode_options = [] - + for history in histories: # 标题列表 if history.title not in titles: titles.append(history.title) - + # 剧集列表 episode_str = history.title if history.seasons: @@ -160,7 +154,6 @@ class SubscribeClear(_PluginBase): episode_str += f" {history.episodes}" episode_options.append({"title": episode_str, "value": history.id}) - # 将列表转换为选择框选项格式 title_options = [{"title": t, "value": t} for t in titles] @@ -189,9 +182,9 @@ class SubscribeClear(_PluginBase): } ] } - + episode_select = { - 'component': 'VRow', + 'component': 'VRow', 'content': [ { 'component': 'VCol', @@ -220,15 +213,16 @@ class SubscribeClear(_PluginBase): 'content': [ title_select, episode_select - ] + ] } ], { "titles": [], "episodes": [] } - def get_data(self) -> List[DownloadHistory]: - down_oper = DownloadHistoryOper() + @staticmethod + def get_download_data() -> List[DownloadHistory]: + down_oper = DownloadHistoryOper() downs = [] page = 1 while True: @@ -241,7 +235,7 @@ class SubscribeClear(_PluginBase): def get_page(self) -> List[dict]: items = [] - for down in self.get_data(): + for down in self.get_download_data(): items.append({ 'component': 'tr', 'content': [ @@ -255,7 +249,7 @@ class SubscribeClear(_PluginBase): }, { 'component': 'td', - 'text':down.seasons + " " + down.episodes + 'text': down.seasons + " " + down.episodes }, { 'component': 'td', @@ -300,7 +294,7 @@ class SubscribeClear(_PluginBase): }, 'text': '名称' }, - { + { 'component': 'th', 'props': { 'class': 'text-start ps-4' @@ -330,14 +324,6 @@ class SubscribeClear(_PluginBase): } ] - - @staticmethod - def get_api(self) -> List[Dict[str, Any]]: - """ - 注册API - """ - pass - def stop_service(self): """ 退出插件 @@ -349,7 +335,7 @@ class SubscribeClear(_PluginBase): """ 服务信息 """ - services = self.downloader_helper.get_services(type_filter="qbittorrent") + services = DownloaderHelper().get_services(type_filter="qbittorrent") if not services: logger.warning("获取下载器实例失败,请检查配置") return None diff --git a/plugins.v2/synccookiecloud/__init__.py b/plugins.v2/synccookiecloud/__init__.py index 81205d6..7072b41 100644 --- a/plugins.v2/synccookiecloud/__init__.py +++ b/plugins.v2/synccookiecloud/__init__.py @@ -40,11 +40,9 @@ class SyncCookieCloud(_PluginBase): _enabled: bool = False _onlyonce: bool = False _cron: str = "" - siteoper = None _scheduler: Optional[BackgroundScheduler] = None def init_plugin(self, config: dict = None): - self.siteoper = SiteOper() # 停止现有任务 self.stop_service() @@ -92,7 +90,7 @@ class SyncCookieCloud(_PluginBase): 同步站点cookie到cookiecloud """ # 获取所有站点 - sites = self.siteoper.list_order_by_pri() + sites = SiteOper().list_order_by_pri() if not sites: return diff --git a/plugins.v2/tobypasstrackers/__init__.py b/plugins.v2/tobypasstrackers/__init__.py index 16d8bec..c87f64f 100644 --- a/plugins.v2/tobypasstrackers/__init__.py +++ b/plugins.v2/tobypasstrackers/__init__.py @@ -1,26 +1,24 @@ -from typing import Any, List, Dict, Tuple, Optional -from datetime import datetime, timedelta -import ipaddress -import socket -import base64 -import json import asyncio +import base64 +import ipaddress +import json +import socket +from datetime import datetime, timedelta +from typing import Any, List, Dict, Tuple, Optional -from apscheduler.schedulers.background import BackgroundScheduler -from fastapi import Response -from apscheduler.triggers.cron import CronTrigger import pytz +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.cron import CronTrigger +from fastapi import Response -from app.chain.site import SiteChain from app.core.config import settings -from app.core.event import EventManager, eventmanager +from app.core.event import eventmanager from app.db.site_oper import SiteOper -from app.helper.sites import SitesHelper from app.log import logger from app.plugins import _PluginBase -from app.utils.http import RequestUtils -from app.schemas.types import EventType, NotificationType from app.plugins.tobypasstrackers.dns_helper import DnsHelper +from app.schemas.types import EventType, NotificationType +from app.utils.http import RequestUtils class ToBypassTrackers(_PluginBase): @@ -43,13 +41,6 @@ class ToBypassTrackers(_PluginBase): # 可使用的用户级别 auth_level = 2 - # 私有属性 - sites: SitesHelper = None - site_chain: SiteChain = None - siteoper: SiteOper = None - - # 事件管理器 - event: EventManager = None # 定时器 _scheduler: Optional[BackgroundScheduler] = None # 开关 @@ -67,13 +58,12 @@ class ToBypassTrackers(_PluginBase): _dns_input: str = "" ipv6_txt: str = "" ipv4_txt: str = "" + trackers: Dict[str, List[str]] = {} def init_plugin(self, config: dict = None): - self.sites = SitesHelper() - # self.event = EventManager() - self.site_chain = SiteChain() + self.stop_service() - self.siteoper = SiteOper() + self.trackers = {} self.ipv6_txt = self.get_data("ipv6_txt") if self.get_data("ipv6_txt") else "" self.ipv4_txt = self.get_data("ipv4_txt") if self.get_data("ipv4_txt") else "" @@ -98,7 +88,7 @@ class ToBypassTrackers(_PluginBase): self._china_ipv6_route = config.get("china_ipv6_route") self._china_ip_route = config.get("china_ip_route") # 过滤掉已删除的站点 - all_sites = [site.id for site in self.siteoper.list_order_by_pri()] + all_sites = [site.id for site in SiteOper().list_order_by_pri()] self._bypassed_sites = [site_id for site_id in all_sites if site_id in self._bypassed_sites] self.__update_config() if self._enabled or self._onlyonce: @@ -160,8 +150,7 @@ class ToBypassTrackers(_PluginBase): def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: site_options = ([{"title": site.name, "value": site.id} - for site in self.siteoper.list_order_by_pri()] - ) + for site in SiteOper().list_order_by_pri()]) return [ { 'component': 'VForm', @@ -629,7 +618,7 @@ class ToBypassTrackers(_PluginBase): chnroute_lists = res.text[:-1].split('\n') for ipr in chnroute_lists: ip_list.append(ipr) - do_sites = {site.domain: site.name for site in self.siteoper.list_order_by_pri() if + do_sites = {site.domain: site.name for site in SiteOper().list_order_by_pri() if site.id in self._bypassed_sites} domain_name_map = {} for site in do_sites: diff --git a/plugins.v2/tobypasstrackers/dns_helper.py b/plugins.v2/tobypasstrackers/dns_helper.py index d25a3db..0a4916a 100644 --- a/plugins.v2/tobypasstrackers/dns_helper.py +++ b/plugins.v2/tobypasstrackers/dns_helper.py @@ -1,7 +1,6 @@ import re from typing import Optional, List, Callable -import aioquic import dns.asyncresolver import dns.resolver @@ -74,7 +73,6 @@ class DnsHelper: 使用 UDP 异步方式解析域名 :param domain: 域名 - :param port: DNS服务器端口(默认53) :param dns_type: 记录类型,如 A、AAAA :return: IP地址列表 或 None """ diff --git a/plugins.v2/torrentremover/__init__.py b/plugins.v2/torrentremover/__init__.py index ddae9d9..6865342 100644 --- a/plugins.v2/torrentremover/__init__.py +++ b/plugins.v2/torrentremover/__init__.py @@ -39,7 +39,6 @@ class TorrentRemover(_PluginBase): auth_level = 2 # 私有属性 - downloader_helper = None _event = threading.Event() _scheduler = None _enabled = False @@ -63,7 +62,7 @@ class TorrentRemover(_PluginBase): _torrentcategorys = None def init_plugin(self, config: dict = None): - self.downloader_helper = DownloaderHelper() + if config: self._enabled = config.get("enabled") self._onlyonce = config.get("onlyonce") @@ -257,7 +256,7 @@ class TorrentRemover(_PluginBase): 'model': 'downloaders', 'label': '下载器', 'items': [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] } } ] @@ -593,7 +592,7 @@ class TorrentRemover(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - services = self.downloader_helper.get_services(name_filters=self._downloaders) + services = DownloaderHelper().get_services(name_filters=self._downloaders) if not services: logger.warning("获取下载器实例失败,请检查配置") return None diff --git a/plugins.v2/torrenttransfer/__init__.py b/plugins.v2/torrenttransfer/__init__.py index 04681bf..430b6bf 100644 --- a/plugins.v2/torrenttransfer/__init__.py +++ b/plugins.v2/torrenttransfer/__init__.py @@ -12,7 +12,6 @@ from qbittorrentapi import TorrentDictionary from app.core.config import settings from app.helper.downloader import DownloaderHelper -from app.helper.torrent import TorrentHelper from app.log import logger from app.modules.qbittorrent import Qbittorrent from app.modules.transmission import Transmission @@ -43,8 +42,7 @@ class TorrentTransfer(_PluginBase): # 私有属性 _scheduler = None - torrent_helper = None - downloader_helper = None + # 开关 _enabled = False _cron = None @@ -76,8 +74,7 @@ class TorrentTransfer(_PluginBase): _torrent_tags = [] def init_plugin(self, config: dict = None): - self.torrent_helper = TorrentHelper() - self.downloader_helper = DownloaderHelper() + # 读取配置 if config: self._enabled = config.get("enabled") @@ -136,7 +133,8 @@ class TorrentTransfer(_PluginBase): self._scheduler.print_jobs() self._scheduler.start() - def service_info(self, name: str) -> Optional[ServiceInfo]: + @staticmethod + def service_info(name: str) -> Optional[ServiceInfo]: """ 服务信息 """ @@ -144,7 +142,7 @@ class TorrentTransfer(_PluginBase): logger.warning("尚未配置下载器,请检查配置") return None - service = self.downloader_helper.get_service(name) + service = DownloaderHelper().get_service(name) if not service or not service.instance: logger.warning(f"获取下载器 {name} 实例失败,请检查配置") return None @@ -197,7 +195,7 @@ class TorrentTransfer(_PluginBase): 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 """ downloader_options = [{"title": config.name, "value": config.name} - for config in self.downloader_helper.get_configs().values()] + for config in DownloaderHelper().get_configs().values()] return [ { 'component': 'VForm', @@ -622,7 +620,8 @@ class TorrentTransfer(_PluginBase): return downloader = service.instance from_service = self.service_info(self._fromdownloader) - if self.downloader_helper.is_downloader("qbittorrent", service=service): + downloader_helper = DownloaderHelper() + if downloader_helper.is_downloader("qbittorrent", service=service): # 生成随机Tag tag = StringUtils.generate_random_str(10) if self._remainoldtag: @@ -651,7 +650,7 @@ class TorrentTransfer(_PluginBase): logger.error(f"{downloader} 下载任务添加成功,但获取任务信息失败!") return None return torrent_hash - elif self.downloader_helper.is_downloader("transmission", service=service): + elif downloader_helper.is_downloader("transmission", service=service): # 添加任务 if self._remainoldtag: # 获取种子标签 @@ -780,6 +779,7 @@ class TorrentTransfer(_PluginBase): # 删除重复数 del_dup = 0 + downloader_helper = DownloaderHelper() for torrent_item in trans_torrents: # 检查种子文件是否存在 torrent_file = Path(self._fromtorrentpath) / f"{torrent_item.get('hash')}.torrent" @@ -814,7 +814,7 @@ class TorrentTransfer(_PluginBase): continue # 如果源下载器是QB检查是否有Tracker,没有的话额外获取 - if self.downloader_helper.is_downloader("qbittorrent", service=from_service): + if downloader_helper.is_downloader("qbittorrent", service=from_service): # 读取种子内容、解析种子文件 content = torrent_file.read_bytes() if not content: @@ -878,7 +878,7 @@ class TorrentTransfer(_PluginBase): logger.info(f"成功添加转移做种任务,种子文件:{torrent_file}") # TR会自动校验,QB需要手动校验 - if self.downloader_helper.is_downloader("qbittorrent", service=to_service): + if downloader_helper.is_downloader("qbittorrent", service=to_service): if self._skipverify: if self._autostart: logger.info(f"{download_id} 跳过校验,开启自动开始,注意观察种子的完整性")