From de2323d67aa9d96b19ab83945510ce804ba69757 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Wed, 19 Jun 2024 13:45:02 +0800 Subject: [PATCH 1/4] refactor RequestUtils --- app/utils/http.py | 260 +++++++++++++++++++++++++--------------------- 1 file changed, 144 insertions(+), 116 deletions(-) diff --git a/app/utils/http.py b/app/utils/http.py index 05f2cc535..270fbf195 100644 --- a/app/utils/http.py +++ b/app/utils/http.py @@ -48,128 +48,159 @@ def __init__(self, if timeout: self._timeout = timeout - def post(self, url: str, data: Any = None, json: dict = None) -> Optional[Response]: - if json is None: - json = {} + def request(self, method: str, url: str, raise_exception: bool = False, **kwargs) -> Optional[Response]: + """ + 发起HTTP请求 + :param method: HTTP方法,如 get, post, put 等 + :param url: 请求的URL + :param raise_exception: 是否在发生异常时抛出异常,否则默认拦截异常返回None + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + :return: HTTP响应对象 + :raises: requests.exceptions.RequestException 仅raise_exception为True时会抛出 + """ + if self._session is None: + req_method = requests.request + else: + req_method = self._session.request + kwargs.setdefault("headers", self._headers) + kwargs.setdefault("cookies", self._cookies) + kwargs.setdefault("proxies", self._proxies) + kwargs.setdefault("timeout", self._timeout) + kwargs.setdefault("verify", False) + kwargs.setdefault("stream", False) try: - if self._session: - return self._session.post(url, - data=data, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - json=json, - stream=False) - else: - return requests.post(url, - data=data, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - json=json, - stream=False) + return req_method(method, url, **kwargs) except requests.exceptions.RequestException: + if raise_exception: + raise return None - def get(self, url: str, params: dict = None) -> Optional[str]: - try: - if self._session: - r = self._session.get(url, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - params=params) - else: - r = requests.get(url, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - params=params) - return str(r.content, 'utf-8') - except requests.exceptions.RequestException: - return None + def get(self, url: str, params: dict = None, **kwargs) -> Optional[str]: + """ + 发送GET请求 + :param url: 请求的URL + :param params: 请求的参数 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + :return: 响应的内容,若发生RequestException则返回None + """ + response = self.request(method="get", url=url, params=params, **kwargs) + return str(response.content, "utf-8") if response else None - def get_res(self, url: str, + def post(self, url: str, data: Any = None, json: dict = None, **kwargs) -> Optional[Response]: + """ + 发送POST请求 + :param url: 请求的URL + :param data: 请求的数据,表单格式 + :param json: 请求的JSON数据 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + :return: HTTP响应对象,若发生RequestException则返回None + """ + if json is None: + json = {} + return self.request(method="post", url=url, data=data, json=json, **kwargs) + + def put(self, url: str, data: Any = None, **kwargs) -> Optional[Response]: + """ + 发送PUT请求 + :param url: 请求的URL + :param data: 请求的数据,表单格式 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + :return: HTTP响应对象,若发生RequestException则返回None + """ + return self.request(method="put", url=url, data=data, **kwargs) + + def get_res(self, + url: str, params: dict = None, data: Any = None, json: dict = None, allow_redirects: bool = True, - raise_exception: bool = False - ) -> Optional[Response]: - try: - if self._session: - return self._session.get(url, - params=params, - data=data, - json=json, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - allow_redirects=allow_redirects, - stream=False) - else: - return requests.get(url, - params=params, - data=data, - json=json, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - allow_redirects=allow_redirects, - stream=False) - except requests.exceptions.RequestException: - if raise_exception: - raise requests.exceptions.RequestException - return None + raise_exception: bool = False, + **kwargs) -> Optional[Response]: + """ + 发送GET请求并返回响应对象 + :param url: 请求的URL + :param params: 请求的参数 + :param data: 请求的数据,表单格式 + :param json: 请求的JSON数据 + :param allow_redirects: 是否允许重定向 + :param raise_exception: 是否在发生异常时抛出异常,否则默认拦截异常返回None + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + :return: HTTP响应对象,若发生RequestException则返回None + :raises: requests.exceptions.RequestException 仅raise_exception为True时会抛出 + """ + return self.request(method="get", + url=url, + params=params, + data=data, + json=json, + allow_redirects=allow_redirects, + raise_exception=raise_exception, + **kwargs) - def post_res(self, url: str, data: Any = None, params: dict = None, + def post_res(self, + url: str, + data: Any = None, + params: dict = None, allow_redirects: bool = True, files: Any = None, json: dict = None, - raise_exception: bool = False) -> Optional[Response]: - try: - if self._session: - return self._session.post(url, - data=data, - params=params, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - allow_redirects=allow_redirects, - files=files, - json=json, - stream=False) - else: - return requests.post(url, - data=data, - params=params, - verify=False, - headers=self._headers, - proxies=self._proxies, - cookies=self._cookies, - timeout=self._timeout, - allow_redirects=allow_redirects, - files=files, - json=json, - stream=False) - except requests.exceptions.RequestException: - if raise_exception: - raise requests.exceptions.RequestException - return None + raise_exception: bool = False, + **kwargs) -> Optional[Response]: + """ + 发送POST请求并返回响应对象 + :param url: 请求的URL + :param data: 请求的数据,表单格式 + :param params: 请求的参数 + :param allow_redirects: 是否允许重定向 + :param files: 请求的文件 + :param json: 请求的JSON数据 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + :param raise_exception: 是否在发生异常时抛出异常,否则默认拦截异常返回None + :return: HTTP响应对象,若发生RequestException则返回None + :raises: requests.exceptions.RequestException 仅raise_exception为True时会抛出 + """ + return self.request(method="post", + url=url, + data=data, + params=params, + allow_redirects=allow_redirects, + files=files, + json=json, + raise_exception=raise_exception, + **kwargs) + + def put_res(self, + url: str, + data: Any = None, + params: dict = None, + allow_redirects: bool = True, + files: Any = None, + json: dict = None, + raise_exception: bool = False, + **kwargs) -> Optional[Response]: + """ + 发送PUT请求并返回响应对象 + :param url: 请求的URL + :param data: 请求的数据,表单格式 + :param params: 请求的参数 + :param allow_redirects: 是否允许重定向 + :param files: 请求的文件 + :param json: 请求的JSON数据 + :param raise_exception: 是否在发生异常时抛出异常,否则默认拦截异常返回None + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + :return: HTTP响应对象,若发生RequestException则返回None + :raises: requests.exceptions.RequestException 仅raise_exception为True时会抛出 + """ + return self.request(method="put", + url=url, + data=data, + params=params, + allow_redirects=allow_redirects, + files=files, + json=json, + raise_exception=raise_exception, + **kwargs) @staticmethod def cookie_parse(cookies_str: str, array: bool = False) -> Union[list, dict]: @@ -182,15 +213,12 @@ def cookie_parse(cookies_str: str, array: bool = False) -> Union[list, dict]: if not cookies_str: return {} cookie_dict = {} - cookies = cookies_str.split(';') + cookies = cookies_str.split(";") for cookie in cookies: - cstr = cookie.split('=') + cstr = cookie.split("=") if len(cstr) > 1: cookie_dict[cstr[0].strip()] = cstr[1].strip() if array: - cookiesList = [] - for cookieName, cookieValue in cookie_dict.items(): - cookies = {'name': cookieName, 'value': cookieValue} - cookiesList.append(cookies) - return cookiesList + return [{"name": k, "value": v} for k, v in cookie_dict.items()] return cookie_dict + From cb0bb8a38e84ef096411a15eb643674eb17579aa Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:51:57 +0800 Subject: [PATCH 2/4] refactor request host --- app/modules/emby/emby.py | 10 ++------ app/modules/jellyfin/jellyfin.py | 10 ++------ app/modules/plex/plex.py | 11 +++------ app/utils/http.py | 40 ++++++++++++++++++++++++++++---- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index 6a8fd3305..18886e4e3 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -18,16 +18,10 @@ class Emby: def __init__(self): self._host = settings.EMBY_HOST if self._host: - if not self._host.endswith("/"): - self._host += "/" - if not self._host.startswith("http"): - self._host = "http://" + self._host + self._host = RequestUtils.standardize_base_url(self._host) self._playhost = settings.EMBY_PLAY_HOST if self._playhost: - if not self._playhost.endswith("/"): - self._playhost += "/" - if not self._playhost.startswith("http"): - self._playhost = "http://" + self._playhost + self._playhost = RequestUtils.standardize_base_url(self._playhost) self._apikey = settings.EMBY_API_KEY self.user = self.get_user(settings.SUPERUSER) self.folders = self.get_emby_folders() diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index deadfd86b..1a4bb2796 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -15,16 +15,10 @@ class Jellyfin: def __init__(self): self._host = settings.JELLYFIN_HOST if self._host: - if not self._host.endswith("/"): - self._host += "/" - if not self._host.startswith("http"): - self._host = "http://" + self._host + self._host = RequestUtils.standardize_base_url(self._host) self._playhost = settings.JELLYFIN_PLAY_HOST if self._playhost: - if not self._playhost.endswith("/"): - self._playhost += "/" - if not self._playhost.startswith("http"): - self._playhost = "http://" + self._playhost + self._playhost = RequestUtils.standardize_base_url(self._playhost) self._apikey = settings.JELLYFIN_API_KEY self.user = self.get_user(settings.SUPERUSER) self.serverid = self.get_server_id() diff --git a/app/modules/plex/plex.py b/app/modules/plex/plex.py index fd882cf32..edd5b22bd 100644 --- a/app/modules/plex/plex.py +++ b/app/modules/plex/plex.py @@ -11,6 +11,7 @@ from app.core.config import settings from app.log import logger from app.schemas import MediaType +from app.utils.http import RequestUtils class Plex: @@ -19,16 +20,10 @@ class Plex: def __init__(self): self._host = settings.PLEX_HOST if self._host: - if not self._host.endswith("/"): - self._host += "/" - if not self._host.startswith("http"): - self._host = "http://" + self._host + self._host = RequestUtils.standardize_base_url(self._host) self._playhost = settings.PLEX_PLAY_HOST if self._playhost: - if not self._playhost.endswith("/"): - self._playhost += "/" - if not self._playhost.startswith("http"): - self._playhost = "http://" + self._playhost + self._playhost = RequestUtils.standardize_base_url(self._playhost) self._token = settings.PLEX_TOKEN if self._host and self._token: try: diff --git a/app/utils/http.py b/app/utils/http.py index 270fbf195..fa92f337b 100644 --- a/app/utils/http.py +++ b/app/utils/http.py @@ -1,4 +1,5 @@ from typing import Union, Any, Optional +from urllib.parse import urljoin import requests import urllib3 @@ -90,7 +91,7 @@ def post(self, url: str, data: Any = None, json: dict = None, **kwargs) -> Optio """ 发送POST请求 :param url: 请求的URL - :param data: 请求的数据,表单格式 + :param data: 请求的数据 :param json: 请求的JSON数据 :param kwargs: 其他请求参数,如headers, cookies, proxies等 :return: HTTP响应对象,若发生RequestException则返回None @@ -103,7 +104,7 @@ def put(self, url: str, data: Any = None, **kwargs) -> Optional[Response]: """ 发送PUT请求 :param url: 请求的URL - :param data: 请求的数据,表单格式 + :param data: 请求的数据 :param kwargs: 其他请求参数,如headers, cookies, proxies等 :return: HTTP响应对象,若发生RequestException则返回None """ @@ -121,7 +122,7 @@ def get_res(self, 发送GET请求并返回响应对象 :param url: 请求的URL :param params: 请求的参数 - :param data: 请求的数据,表单格式 + :param data: 请求的数据 :param json: 请求的JSON数据 :param allow_redirects: 是否允许重定向 :param raise_exception: 是否在发生异常时抛出异常,否则默认拦截异常返回None @@ -150,7 +151,7 @@ def post_res(self, """ 发送POST请求并返回响应对象 :param url: 请求的URL - :param data: 请求的数据,表单格式 + :param data: 请求的数据 :param params: 请求的参数 :param allow_redirects: 是否允许重定向 :param files: 请求的文件 @@ -182,7 +183,7 @@ def put_res(self, """ 发送PUT请求并返回响应对象 :param url: 请求的URL - :param data: 请求的数据,表单格式 + :param data: 请求的数据 :param params: 请求的参数 :param allow_redirects: 是否允许重定向 :param files: 请求的文件 @@ -222,3 +223,32 @@ def cookie_parse(cookies_str: str, array: bool = False) -> Union[list, dict]: return [{"name": k, "value": v} for k, v in cookie_dict.items()] return cookie_dict + @staticmethod + def standardize_base_url(host: str) -> str: + """ + 标准化提供的主机地址,确保它以http://或https://开头,并且以斜杠(/)结尾 + :param host: 提供的主机地址字符串 + :return: 标准化后的主机地址字符串 + """ + if not host: + return host + if not host.endswith("/"): + host += "/" + if not host.startswith("http://") and not host.startswith("https://"): + host = "http://" + host + return host + + @staticmethod + def adapt_request_url(host: str, endpoint: str) -> Optional[str]: + """ + 基于传入的host,适配请求的URL,确保每个请求的URL是完整的,用于在发送请求前自动处理和修正请求的URL。 + :param host: 主机头 + :param endpoint: 端点 + :return: 完整的请求URL字符串 + """ + if not host and not endpoint: + return None + if endpoint.startswith(("http://", "https://")): + return endpoint + host = RequestUtils.standardize_base_url(host) + return urljoin(host, endpoint) if host else endpoint From da6ba3fa8bfa939e3b0b5e872b5eaaed9f3f5a3c Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:53:55 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat=EF=BC=9APlex=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E8=AF=B7=E6=B1=82=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/modules/plex/plex.py | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/app/modules/plex/plex.py b/app/modules/plex/plex.py index edd5b22bd..d7a7f7b52 100644 --- a/app/modules/plex/plex.py +++ b/app/modules/plex/plex.py @@ -6,6 +6,7 @@ from cachetools import TTLCache, cached from plexapi import media from plexapi.server import PlexServer +from requests import Response, Session from app import schemas from app.core.config import settings @@ -16,6 +17,7 @@ class Plex: _plex = None + _session = None def __init__(self): self._host = settings.PLEX_HOST @@ -32,6 +34,7 @@ def __init__(self): except Exception as e: self._plex = None logger.error(f"Plex服务器连接失败:{str(e)}") + self._session = self.__adapt_plex_session() def is_inactive(self) -> bool: """ @@ -722,3 +725,71 @@ def get_latest(self, num: int = 20) -> Optional[List[schemas.MediaServerPlayItem )) offset += num return ret_resume[:num] + + def get_data(self, endpoint: str, **kwargs) -> Optional[Response]: + """ + 自定义从媒体服务器获取数据 + :param endpoint: 端点 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + """ + return self.__request(method="get", endpoint=endpoint, **kwargs) + + def post_data(self, endpoint: str, **kwargs) -> Optional[Response]: + """ + 自定义从媒体服务器获取数据 + :param endpoint: 端点 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + """ + return self.__request(method="post", endpoint=endpoint, **kwargs) + + def put_data(self, endpoint: str, **kwargs) -> Optional[Response]: + """ + 自定义从媒体服务器获取数据 + :param endpoint: 端点 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + """ + return self.__request(method="put", endpoint=endpoint, **kwargs) + + def __request(self, method: str, endpoint: str, **kwargs) -> Optional[Response]: + """ + 自定义从媒体服务器获取数据 + :param method: HTTP方法,如 get, post, put 等 + :param endpoint: 端点 + :param kwargs: 其他请求参数,如headers, cookies, proxies等 + """ + if not self._session: + return + try: + url = RequestUtils.adapt_request_url(host=self._host, endpoint=endpoint) + kwargs.setdefault("headers", self.__get_request_headers()) + kwargs.setdefault("raise_exception", True) + request_method = getattr(RequestUtils(session=self._session), f"{method}_res", None) + if request_method: + return request_method(url=url, **kwargs) + else: + logger.error(f"方法 {method} 不存在") + return None + except Exception as e: + logger.error(f"连接Plex出错:" + str(e)) + return None + + @staticmethod + def __get_request_headers() -> dict: + """获取请求头""" + return { + "X-Plex-Token": settings.PLEX_TOKEN, + "Accept": "application/json", + "Content-Type": "application/json" + } + + @staticmethod + def __adapt_plex_session() -> Session: + """ + 创建并配置一个针对Plex服务的requests.Session实例 + 这个会话包括特定的头部信息,用于处理所有的Plex请求 + """ + # 设置请求头部,通常包括验证令牌和接受/内容类型头部 + headers = Plex.__get_request_headers() + session = Session() + session.headers = headers + return session From 6392ee627fcab6db439e6c859bd2475eccad8970 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:36:31 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix=20=E8=AF=B7=E6=B1=82=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=97=B6=E8=AE=B0=E5=BD=95debug=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/utils/http.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/utils/http.py b/app/utils/http.py index fa92f337b..6f76082f9 100644 --- a/app/utils/http.py +++ b/app/utils/http.py @@ -6,6 +6,8 @@ from requests import Session, Response from urllib3.exceptions import InsecureRequestWarning +from app.log import logger + urllib3.disable_warnings(InsecureRequestWarning) @@ -71,7 +73,8 @@ def request(self, method: str, url: str, raise_exception: bool = False, **kwargs kwargs.setdefault("stream", False) try: return req_method(method, url, **kwargs) - except requests.exceptions.RequestException: + except requests.exceptions.RequestException as e: + logger.debug(f"请求失败: {e}") if raise_exception: raise return None