diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index a078d193..25de81c9 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -16,6 +16,7 @@ from fastapi import APIRouter, Body, Depends, HTTPException, Header, Request, Re from fastapi.responses import StreamingResponse from app import schemas +from app.chain.mediaserver import MediaServerChain from app.chain.search import SearchChain from app.chain.system import SystemChain from app.core.cache import AsyncFileCache @@ -52,6 +53,7 @@ async def fetch_image( proxy: bool = False, use_cache: bool = False, if_none_match: Optional[str] = None, + cookies: Optional[str | dict] = None, allowed_domains: Optional[set[str]] = None) -> Optional[Response]: """ 处理图片缓存逻辑,支持HTTP缓存和磁盘缓存 @@ -96,8 +98,13 @@ async def fetch_image( # 请求远程图片 referer = "https://movie.douban.com/" if "doubanio.com" in url else None proxies = settings.PROXY if proxy else None - response = await AsyncRequestUtils(ua=settings.NORMAL_USER_AGENT, proxies=proxies, referer=referer, - accept_type="image/avif,image/webp,image/apng,*/*").get_res(url=url) + response = await AsyncRequestUtils( + ua=settings.NORMAL_USER_AGENT, + proxies=proxies, + referer=referer, + cookies=cookies, + accept_type="image/avif,image/webp,image/apng,*/*", + ).get_res(url=url) if not response: logger.warn(f"Failed to fetch image from URL: {url}") return None @@ -140,6 +147,7 @@ async def proxy_img( imgurl: str, proxy: bool = False, cache: bool = False, + use_cookies: bool = False, if_none_match: Annotated[str | None, Header()] = None, _: schemas.TokenPayload = Depends(verify_resource_token) ) -> Response: @@ -150,7 +158,12 @@ async def proxy_img( hosts = [config.config.get("host") for config in MediaServerHelper().get_configs().values() if config and config.config and config.config.get("host")] allowed_domains = set(settings.SECURITY_IMAGE_DOMAINS) | set(hosts) - return await fetch_image(url=imgurl, proxy=proxy, use_cache=cache, + cookies = ( + MediaServerChain().get_image_cookies(server=None, image_url=imgurl) + if use_cookies + else None + ) + return await fetch_image(url=imgurl, proxy=proxy, use_cache=cache, cookies=cookies, if_none_match=if_none_match, allowed_domains=allowed_domains) diff --git a/app/chain/mediaserver.py b/app/chain/mediaserver.py index 399cfee4..e6ae098e 100644 --- a/app/chain/mediaserver.py +++ b/app/chain/mediaserver.py @@ -113,6 +113,16 @@ class MediaServerChain(ChainBase): """ return self.run_module("mediaserver_play_url", server=server, item_id=item_id) + def get_image_cookies( + self, server: Optional[str], image_url: str + ) -> Optional[str | dict]: + """ + 获取图片的Cookies + """ + return self.run_module( + "mediaserver_image_cookies", server=server, image_url=image_url + ) + def sync(self): """ 同步媒体库所有数据到本地数据库 diff --git a/app/modules/trimemedia/__init__.py b/app/modules/trimemedia/__init__.py index 73e5d0a6..ce6758cf 100644 --- a/app/modules/trimemedia/__init__.py +++ b/app/modules/trimemedia/__init__.py @@ -382,3 +382,27 @@ class TrimeMediaModule(_ModuleBase, _MediaServerBase[TrimeMedia]): if not server_obj: return [] return server_obj.get_latest_backdrops(num=count, remote=remote) or [] + + def mediaserver_image_cookies( + self, + server: Optional[str] = None, + image_url: Optional[str] = None, + **kwargs, + ) -> Optional[str | dict]: + """ + 获取飞牛影视服务器的图片Cookies + + :param server: 媒体服务器名称 + :param image_url: 图片网址 + """ + if not image_url: + return None + if server: + server_obj = self.get_instance(server) + if not server_obj: + return None + return server_obj.get_image_cookies(image_url) + else: + for server_obj in self.get_instances().values(): + if cookies := server_obj.get_image_cookies(image_url): + return cookies diff --git a/app/modules/trimemedia/api.py b/app/modules/trimemedia/api.py index e2533043..61e7fb84 100644 --- a/app/modules/trimemedia/api.py +++ b/app/modules/trimemedia/api.py @@ -190,7 +190,7 @@ class Api: """ 用户列表(仅管理员有权访问) """ - if (res := self.request("/memory/user/list")) and res.success: + if (res := self.request("/manager/user/list")) and res.success: if not res.data: return [] return [ diff --git a/app/modules/trimemedia/trimemedia.py b/app/modules/trimemedia/trimemedia.py index 93a884ee..fce6b666 100644 --- a/app/modules/trimemedia/trimemedia.py +++ b/app/modules/trimemedia/trimemedia.py @@ -5,6 +5,7 @@ import app.modules.trimemedia.api as fnapi from app import schemas from app.log import logger from app.schemas import MediaType +from app.utils.security import SecurityUtils from app.utils.url import UrlUtils @@ -20,6 +21,7 @@ class TrimeMedia: _sync_libraries: List[str] = [] _api: Optional[fnapi.Api] = None + _version: Optional[fnapi.Version] = None def __init__( self, @@ -55,6 +57,13 @@ class TrimeMedia: """ return self._api + @property + def version(self) -> Optional[fnapi.Version]: + """ + 获得飞牛API的版本 + """ + return self._version + class _ApiCreateResult: api: fnapi.Api version: fnapi.Version @@ -123,7 +132,9 @@ class TrimeMedia: self.disconnect() if result := self.__create_api(self._host): self._api = result.api + self._version = result.version # 版本号:0.8.53, 服务版本:0.8.23 + # 版本号:0.8.56, 服务版本:0.8.23 接口/memory/user/list改为/manager/user/list logger.debug( f"版本号:{result.version.frontend}, 服务版本:{result.version.backend}" ) @@ -189,6 +200,7 @@ class TrimeMedia: ], link=f"{self._playhost or self._api.host}/library/{library.guid}", server_type="trimemedia", + use_cookies=True, ) ) return libraries @@ -487,6 +499,7 @@ class TrimeMedia: else 0 ), server_type="trimemedia", + use_cookies=True, ) def get_items( @@ -603,6 +616,7 @@ class TrimeMedia: if (item_details := self._api.item(item.guid)) is None: continue if remote: + # FIXME 新版飞牛的壁纸无法直接在浏览器中访问 img_host = self._playhost or self._api.host else: img_host = self._api.host @@ -631,3 +645,15 @@ class TrimeMedia: ) else False ) + + def get_image_cookies(self, image_url: str): + """ + 获得指定图片的Cookies + """ + if not self.is_authenticated(): + return None + if not image_url or not SecurityUtils.is_safe_url( + image_url, [self._api.host], strict=True + ): + return None + return {"Trim-MC-token": self._api.token} diff --git a/app/schemas/mediaserver.py b/app/schemas/mediaserver.py index a5427228..10d15710 100644 --- a/app/schemas/mediaserver.py +++ b/app/schemas/mediaserver.py @@ -74,6 +74,8 @@ class MediaServerLibrary(BaseModel): link: Optional[str] = None # 服务器类型 server_type: Optional[str] = None + # 飞牛的图片需要Cookies + use_cookies: Optional[bool] = None class MediaServerItemUserState(BaseModel): @@ -177,3 +179,5 @@ class MediaServerPlayItem(BaseModel): percent: Optional[float] = None BackdropImageTags: Optional[list] = Field(default_factory=list) server_type: Optional[str] = None + # 飞牛的图片需要Cookies + use_cookies: Optional[bool] = None