From 5d188e3877e59394cd8da4d9f1bd100bdf91956d Mon Sep 17 00:00:00 2001 From: jxxghp Date: Tue, 3 Jun 2025 17:11:44 +0800 Subject: [PATCH] fix module close --- app/modules/bangumi/__init__.py | 2 +- app/modules/bangumi/bangumi.py | 15 ++++++++----- app/modules/filemanager/storages/alist.py | 20 ++++++++--------- app/modules/plex/__init__.py | 10 +++++++-- app/modules/plex/plex.py | 6 ++++- app/modules/trimemedia/api.py | 13 +++++++++-- app/modules/trimemedia/trimemedia.py | 6 +++-- app/utils/http.py | 27 ++++++++++++++--------- 8 files changed, 65 insertions(+), 34 deletions(-) diff --git a/app/modules/bangumi/__init__.py b/app/modules/bangumi/__init__.py index c98896c8..d51364cc 100644 --- a/app/modules/bangumi/__init__.py +++ b/app/modules/bangumi/__init__.py @@ -18,7 +18,7 @@ class BangumiModule(_ModuleBase): self.bangumiapi = BangumiApi() def stop(self): - pass + self.bangumiapi.close() def test(self) -> Tuple[bool, str]: """ diff --git a/app/modules/bangumi/bangumi.py b/app/modules/bangumi/bangumi.py index debcafbd..423e2240 100644 --- a/app/modules/bangumi/bangumi.py +++ b/app/modules/bangumi/bangumi.py @@ -25,19 +25,18 @@ class BangumiApi(object): "person_credits": "v0/persons/%s/subjects", } _base_url = "https://api.bgm.tv/" - _req = RequestUtils(session=requests.Session()) def __init__(self): - pass + self._session = requests.Session() + self._req = RequestUtils(session=self._session) - @classmethod @cached(maxsize=settings.CACHE_CONF["bangumi"], ttl=settings.CACHE_CONF["meta"]) - def __invoke(cls, url, key: Optional[str] = None, **kwargs): - req_url = cls._base_url + url + def __invoke(self, url, key: Optional[str] = None, **kwargs): + req_url = self._base_url + url params = {} if kwargs: params.update(kwargs) - resp = cls._req.get_res(url=req_url, params=params) + resp = self._req.get_res(url=req_url, params=params) try: if not resp: return None @@ -207,3 +206,7 @@ class BangumiApi(object): return self.__invoke(self._urls["discover"], key="data", _ts=datetime.strftime(datetime.now(), '%Y%m%d'), **kwargs) + + def close(self): + if self._session: + self._session.close() diff --git a/app/modules/filemanager/storages/alist.py b/app/modules/filemanager/storages/alist.py index 094d6dce..aaacdc84 100644 --- a/app/modules/filemanager/storages/alist.py +++ b/app/modules/filemanager/storages/alist.py @@ -3,6 +3,7 @@ from datetime import datetime from pathlib import Path from typing import Optional, List, Dict +import requests from requests import Response from app import schemas @@ -529,20 +530,19 @@ class Alist(StorageBase, metaclass=Singleton): if result["data"]["sign"]: download_url = download_url + "?sign=" + result["data"]["sign"] - resp = RequestUtils( - headers=self.__get_header_with_token() - ).get_res(download_url) - if not path: - new_path = settings.TEMP_PATH / fileitem.name + local_path = settings.TEMP_PATH / fileitem.name else: - new_path = path / fileitem.name + local_path = path / fileitem.name - with open(new_path, "wb") as f: - f.write(resp.content) + with requests.get(download_url, headers=self.__get_header_with_token(), stream=True) as r: + r.raise_for_status() + with open(local_path, "wb") as f: + for chunk in r.iter_content(chunk_size=8192): + f.write(chunk) - if new_path.exists(): - return new_path + if local_path.exists(): + return local_path return None def upload( diff --git a/app/modules/plex/__init__.py b/app/modules/plex/__init__.py index 1047fc2d..09193ce5 100644 --- a/app/modules/plex/__init__.py +++ b/app/modules/plex/__init__.py @@ -45,7 +45,12 @@ class PlexModule(_ModuleBase, _MediaServerBase[Plex]): return 3 def stop(self): - pass + """ + 停止模块服务 + """ + for server in self.get_instances().values(): + if server: + server.close() def test(self) -> Optional[Tuple[bool, str]]: """ @@ -273,7 +278,8 @@ class PlexModule(_ModuleBase, _MediaServerBase[Plex]): episodes=episodes ) for season, episodes in seasoninfo.items()] - def mediaserver_playing(self, server: str, count: Optional[int] = 20, **kwargs) -> List[schemas.MediaServerPlayItem]: + def mediaserver_playing(self, server: str, count: Optional[int] = 20, **kwargs) -> List[ + schemas.MediaServerPlayItem]: """ 获取媒体服务器正在播放信息 """ diff --git a/app/modules/plex/plex.py b/app/modules/plex/plex.py index 7dd666d9..8e378c5d 100644 --- a/app/modules/plex/plex.py +++ b/app/modules/plex/plex.py @@ -14,7 +14,7 @@ from app.log import logger from app.schemas import MediaType from app.utils.http import RequestUtils from app.utils.url import UrlUtils -from schemas import MediaServerItem +from app.schemas import MediaServerItem class Plex: @@ -890,3 +890,7 @@ class Plex: session = Session() session.headers = headers return session + + def close(self): + if self._session: + self._session.close() diff --git a/app/modules/trimemedia/api.py b/app/modules/trimemedia/api.py index d4c8744e..febfc9d7 100644 --- a/app/modules/trimemedia/api.py +++ b/app/modules/trimemedia/api.py @@ -111,6 +111,7 @@ class Api: "_api_path", "_request_utils", "_version", + "_session" ) @property @@ -138,7 +139,8 @@ class Api: self._apikey = apikey self._token: Optional[str] = None self._version: Optional[Version] = None - self._request_utils = RequestUtils(session=requests.Session()) + self._session = requests.Session() + self._request_utils = RequestUtils(session=self._session) def sys_version(self) -> Optional[Version]: """ @@ -352,7 +354,7 @@ class Api: def del_item(self, guid: str, delete_file: bool) -> bool: """ 删除媒体 - + :param guid: 媒体GUID :param delete_file: True删除媒体文件,False仅从媒体库移除 """ if ( @@ -491,3 +493,10 @@ class Api: if not suppress_log: logger.error(f"请求接口 {url} 异常:" + str(e)) return None + + def close(self): + """ + 关闭API会话 + """ + if self._session: + self._session.close() diff --git a/app/modules/trimemedia/trimemedia.py b/app/modules/trimemedia/trimemedia.py index b7c858e1..6b160809 100644 --- a/app/modules/trimemedia/trimemedia.py +++ b/app/modules/trimemedia/trimemedia.py @@ -55,7 +55,8 @@ class TrimeMedia: """ return self._api - def __create_api(self, host: Optional[str]) -> Optional[fnapi.Api]: + @staticmethod + def __create_api(host: Optional[str]) -> Optional[fnapi.Api]: """ 创建一个飞牛API @@ -76,7 +77,7 @@ class TrimeMedia: api = fnapi.Api(host, api_key) return api if api.sys_version() else None - def __del__(self): + def close(self): self.disconnect() def is_configured(self) -> bool: @@ -118,6 +119,7 @@ class TrimeMedia: """ if self.is_authenticated(): self._api.logout() + self._api.close() self._userinfo = None logger.debug(f"{self._username} 已断开飞牛影视") diff --git a/app/utils/http.py b/app/utils/http.py index 0bed8cce..b5ad166c 100644 --- a/app/utils/http.py +++ b/app/utils/http.py @@ -1,4 +1,5 @@ import re +from contextlib import contextmanager from typing import Any, Optional, Union import chardet @@ -143,6 +144,22 @@ class RequestUtils: raise_exception=raise_exception, **kwargs) + @contextmanager + def get_stream(self, url: str, params: dict = None, **kwargs): + """ + 获取流式响应的上下文管理器,适用于大文件下载 + :param url: 请求的URL + :param params: 请求的参数 + :param kwargs: 其他请求参数 + """ + kwargs['stream'] = True + response = self.request(method="get", url=url, params=params, **kwargs) + try: + yield response + finally: + if response: + response.close() + def post_res(self, url: str, data: Any = None, @@ -343,11 +360,6 @@ class RequestUtils: content_type = response.headers.get("Content-Type", "") if re.search(r"charset=[\"']?utf-8[\"']?", content_type, re.IGNORECASE): return "utf-8" - # 暂不支持直接提取字符集,仅提取UTF8 - # match = re.search(r"charset=[\"']?([^\"';\s]+)", content_type, re.IGNORECASE) - # if match: - # return match.group(1) - # 2. 检查响应体中的 BOM 标记(例如 UTF-8 BOM) if response.content[:3] == b"\xef\xbb\xbf": return "utf-8" @@ -355,11 +367,6 @@ class RequestUtils: # 3. 如果是 HTML 响应体,检查其中的 标签 if re.search(r"charset=[\"']?utf-8[\"']?", response.text, re.IGNORECASE): return "utf-8" - # 暂不支持直接提取字符集,仅提取UTF8 - # match = re.search(r"]+charset=[\"']?([^\"'>\s]+)", response.text, re.IGNORECASE) - # if match: - # return match.group(1) - # 4. 使用 chardet 库进一步分析内容 detection = chardet.detect(response.content) if detection.get("confidence", 0) > confidence_threshold: