Merge pull request #5096 from cddjr/fix_trimemedia_cookies

This commit is contained in:
jxxghp
2025-11-05 23:14:18 +08:00
committed by GitHub
6 changed files with 81 additions and 4 deletions

View File

@@ -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)

View File

@@ -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):
"""
同步媒体库所有数据到本地数据库

View File

@@ -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

View File

@@ -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 [

View File

@@ -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}

View File

@@ -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