From bd7ca7fa60334422c3fe6f02bc4d9355724d04fb Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 24 Mar 2024 13:38:36 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9Am-team=20x-api-key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/site.py | 8 ++--- app/chain/download.py | 14 ++++++-- app/chain/site.py | 29 ++++++++++++++-- app/db/systemconfig_oper.py | 6 ++++ app/modules/indexer/mtorrent.py | 59 +++++++++++++++++++++++++++++++-- app/schemas/types.py | 4 +-- app/utils/string.py | 12 +++++++ 7 files changed, 119 insertions(+), 13 deletions(-) diff --git a/app/api/endpoints/site.py b/app/api/endpoints/site.py index 59360429..c4f59af6 100644 --- a/app/api/endpoints/site.py +++ b/app/api/endpoints/site.py @@ -57,8 +57,8 @@ def add_site( site_in.id = None site = Site(**site_in.dict()) site.create(db) - # 通知缓存站点图标 - EventManager().send_event(EventType.CacheSiteIcon, { + # 通知站点更新 + EventManager().send_event(EventType.SiteUpdated, { "domain": domain }) return schemas.Response(success=True) @@ -81,8 +81,8 @@ def update_site( _scheme, _netloc = StringUtils.get_url_netloc(site_in.url) site_in.url = f"{_scheme}://{_netloc}/" site.update(db, site_in.dict()) - # 通知缓存站点图标 - EventManager().send_event(EventType.CacheSiteIcon, { + # 通知站点更新 + EventManager().send_event(EventType.SiteUpdated, { "domain": site_in.domain }) return schemas.Response(success=True) diff --git a/app/chain/download.py b/app/chain/download.py index 1340ddaa..0b17bbbf 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -109,17 +109,27 @@ class DownloadChain(ChainBase): # 解码参数 req_str = base64.b64decode(base64_str.encode('utf-8')).decode('utf-8') req_params: Dict[str, dict] = json.loads(req_str) + # 是否使用cookie + if not req_params.get('cookie'): + cookie = None + # 请求头 + if req_params.get('header'): + headers = req_params.get('header') + else: + headers = None if req_params.get('method') == 'get': # GET请求 res = RequestUtils( ua=ua, - cookies=cookie + cookies=cookie, + headers=headers ).get_res(url, params=req_params.get('params')) else: # POST请求 res = RequestUtils( ua=ua, - cookies=cookie + cookies=cookie, + headers=headers ).post_res(url, params=req_params.get('params')) if not res: return None diff --git a/app/chain/site.py b/app/chain/site.py index f26b3b94..26a5bc4f 100644 --- a/app/chain/site.py +++ b/app/chain/site.py @@ -12,6 +12,7 @@ from app.core.event import eventmanager, Event, EventManager from app.db.models.site import Site from app.db.site_oper import SiteOper from app.db.siteicon_oper import SiteIconOper +from app.db.systemconfig_oper import SystemConfigOper from app.helper.browser import PlaywrightHelper from app.helper.cloudflare import under_challenge from app.helper.cookie import CookieHelper @@ -41,6 +42,7 @@ class SiteChain(ChainBase): self.cookiehelper = CookieHelper() self.message = MessageHelper() self.cookiecloud = CookieCloudHelper() + self.systemconfig = SystemConfigOper() # 特殊站点登录验证 self.special_site_test = { @@ -237,9 +239,9 @@ class SiteChain(ChainBase): public=1 if indexer.get("public") else 0) _add_count += 1 - # 通知缓存站点图标 + # 通知站点更新 if indexer: - EventManager().send_event(EventType.CacheSiteIcon, { + EventManager().send_event(EventType.SiteUpdated, { "domain": domain, }) # 处理完成 @@ -251,7 +253,7 @@ class SiteChain(ChainBase): logger.info(f"CookieCloud同步成功:{ret_msg}") return True, ret_msg - @eventmanager.register(EventType.CacheSiteIcon) + @eventmanager.register(EventType.SiteUpdated) def cache_site_icon(self, event: Event): """ 缓存站点图标 @@ -293,6 +295,27 @@ class SiteChain(ChainBase): else: logger.warn(f"缓存站点 {indexer.get('name')} 图标失败") + @eventmanager.register(EventType.SiteUpdated) + def clear_site_data(self, event: Event): + """ + 清理站点数据 + """ + if not event: + return + event_data = event.event_data or {} + # 主域名 + domain = event_data.get("domain") + if not domain: + return + # 获取主域名中间那段 + domain_host = StringUtils.get_url_host(domain) + # 查询以"site.domain_host"开头的配置项,并清除 + site_keys = self.systemconfig.all().keys() + for key in site_keys: + if key.startswith(f"site.{domain_host}"): + logger.info(f"清理站点配置:{key}") + self.systemconfig.delete(key) + def test(self, url: str) -> Tuple[bool, str]: """ 测试站点是否可用 diff --git a/app/db/systemconfig_oper.py b/app/db/systemconfig_oper.py index 88d3e149..9b73d233 100644 --- a/app/db/systemconfig_oper.py +++ b/app/db/systemconfig_oper.py @@ -56,6 +56,12 @@ class SystemConfigOper(DbOper, metaclass=Singleton): return self.__SYSTEMCONF return self.__SYSTEMCONF.get(key) + def all(self): + """ + 获取所有系统设置 + """ + return self.__SYSTEMCONF or {} + def delete(self, key: Union[str, SystemConfigKey]): """ 删除系统设置 diff --git a/app/modules/indexer/mtorrent.py b/app/modules/indexer/mtorrent.py index d75ef02d..7a935b21 100644 --- a/app/modules/indexer/mtorrent.py +++ b/app/modules/indexer/mtorrent.py @@ -6,6 +6,7 @@ from typing import Tuple, List from ruamel.yaml import CommentedMap from app.core.config import settings +from app.db.systemconfig_oper import SystemConfigOper from app.log import logger from app.schemas import MediaType from app.utils.http import RequestUtils @@ -13,6 +14,9 @@ from app.utils.string import StringUtils class MTorrentSpider: + """ + mTorrent API,需要缓存ApiKey + """ _indexerid = None _domain = None _name = "" @@ -28,6 +32,9 @@ class MTorrentSpider: _movie_category = ['401', '419', '420', '421', '439', '405', '404'] _tv_category = ['403', '402', '435', '438', '404', '405'] + # API KEY + _apikey = None + # 标签 _labels = { "0": "", @@ -41,6 +48,7 @@ class MTorrentSpider: } def __init__(self, indexer: CommentedMap): + self.systemconfig = SystemConfigOper() if indexer: self._indexerid = indexer.get('id') self._domain = indexer.get('domain') @@ -51,10 +59,50 @@ class MTorrentSpider: self._cookie = indexer.get('cookie') self._ua = indexer.get('ua') + def __get_apikey(self) -> str: + """ + 获取ApiKey + """ + domain_host = StringUtils.get_url_host(self._domain) + if not self.systemconfig.get(f"site.{domain_host}.apikey"): + try: + res = RequestUtils( + headers={ + "Content-Type": "application/json", + "User-Agent": f"{self._ua}" + }, + cookies=self._cookie, + ua=self._ua, + proxies=self._proxy, + referer=f"{self._domain}usercp?tab=laboratory", + timeout=30 + ).post_res(url=f"{self._domain}api/apikey/getKeyList") + if res and res.status_code == 200: + api_keys = res.json().get('data') + if api_keys: + logger.info(f"{self._name} 获取ApiKey成功") + # 按lastModifiedDate倒序排序 + api_keys.sort(key=lambda x: x.get('lastModifiedDate'), reverse=True) + self._apikey = api_keys[0].get('apiKey') + self.systemconfig.set(f"site.{domain_host}.apikey", self._apikey) + else: + logger.warn(f"{self._name} 获取ApiKey失败,请先在`控制台`->`实验室`建立存取令牌") + else: + logger.warn(f"{self._name} 获取ApiKey失败,请检查Cookie是否有效") + except Exception as e: + logger.error(f"{self._name} 获取ApiKey出错:{e}") + return self._apikey + def search(self, keyword: str, mtype: MediaType = None, page: int = 0) -> Tuple[bool, List[dict]]: """ 搜索 """ + # 检查ApiKey + self.__get_apikey() + + if not self._apikey: + return True, [] + if not mtype: categories = [] elif mtype == MediaType.TV: @@ -71,9 +119,9 @@ class MTorrentSpider: res = RequestUtils( headers={ "Content-Type": "application/json", - "User-Agent": f"{self._ua}" + "User-Agent": f"{self._ua}", + "x-api-key": self._apikey }, - cookies=self._cookie, proxies=self._proxy, referer=f"{self._domain}browse", timeout=30 @@ -168,9 +216,16 @@ class MTorrentSpider: url = self._downloadurl % self._domain params = { 'method': 'post', + 'cookie': False, 'params': { 'id': torrent_id }, + 'header': { + 'Content-Type': 'application/json', + 'User-Agent': f'{self._ua}', + 'Accept': 'application/json, text/plain, */*', + 'x-api-key': self._apikey + }, 'result': 'data' } # base64编码 diff --git a/app/schemas/types.py b/app/schemas/types.py index 1f991619..673ca726 100644 --- a/app/schemas/types.py +++ b/app/schemas/types.py @@ -40,8 +40,8 @@ class EventType(Enum): NameRecognize = "name.recognize" # 名称识别结果 NameRecognizeResult = "name.recognize.result" - # 缓存站点图标 - CacheSiteIcon = "cache.siteicon" + # 站点发生更新 + SiteUpdated = "site.updated" # 系统配置Key字典 diff --git a/app/utils/string.py b/app/utils/string.py index 804834a4..e1eea308 100644 --- a/app/utils/string.py +++ b/app/utils/string.py @@ -282,6 +282,18 @@ class StringUtils: return netloc[-2] return netloc[0] + @staticmethod + def get_url_host(url: str) -> str: + """ + 获取URL的一级域名 + """ + if not url: + return "" + _, netloc = StringUtils.get_url_netloc(url) + if not netloc: + return "" + return netloc.split(".")[-2] + @staticmethod def get_base_url(url: str) -> str: """