mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-10 23:11:47 +08:00
Merge pull request #3611 from InfinityPacer/feature/recommend
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
import inspect
|
import inspect
|
||||||
|
import io
|
||||||
|
import tempfile
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, Callable
|
from pathlib import Path
|
||||||
|
from typing import Any, Callable, List
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
from cachetools import TTLCache
|
from cachetools import TTLCache
|
||||||
from cachetools.keys import hashkey
|
from cachetools.keys import hashkey
|
||||||
|
|
||||||
@@ -9,9 +13,12 @@ from app.chain import ChainBase
|
|||||||
from app.chain.bangumi import BangumiChain
|
from app.chain.bangumi import BangumiChain
|
||||||
from app.chain.douban import DoubanChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.chain.tmdb import TmdbChain
|
from app.chain.tmdb import TmdbChain
|
||||||
|
from app.core.config import settings
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import MediaType
|
from app.schemas import MediaType
|
||||||
from app.utils.common import log_execution_time
|
from app.utils.common import log_execution_time
|
||||||
|
from app.utils.http import RequestUtils
|
||||||
|
from app.utils.security import SecurityUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
|
|
||||||
# 推荐相关的专用缓存
|
# 推荐相关的专用缓存
|
||||||
@@ -72,21 +79,91 @@ class RecommendChain(ChainBase, metaclass=Singleton):
|
|||||||
logger.debug("Starting to refresh Recommend data.")
|
logger.debug("Starting to refresh Recommend data.")
|
||||||
recommend_cache.clear()
|
recommend_cache.clear()
|
||||||
logger.debug("Recommend Cache has been cleared.")
|
logger.debug("Recommend Cache has been cleared.")
|
||||||
self.tmdb_movies()
|
# 缓存并刷新所有推荐数据
|
||||||
self.tmdb_tvs()
|
recommends = []
|
||||||
self.tmdb_trending()
|
recommends.extend(self.tmdb_movies())
|
||||||
self.bangumi_calendar()
|
recommends.extend(self.tmdb_tvs())
|
||||||
self.douban_movie_showing()
|
recommends.extend(self.tmdb_trending())
|
||||||
self.douban_movies()
|
recommends.extend(self.bangumi_calendar())
|
||||||
self.douban_tvs()
|
recommends.extend(self.douban_movie_showing())
|
||||||
self.douban_movie_top250()
|
recommends.extend(self.douban_movies())
|
||||||
self.douban_tv_weekly_chinese()
|
recommends.extend(self.douban_tvs())
|
||||||
self.douban_tv_weekly_global()
|
recommends.extend(self.douban_movie_top250())
|
||||||
self.douban_tv_animation()
|
recommends.extend(self.douban_tv_weekly_chinese())
|
||||||
self.douban_movie_hot()
|
recommends.extend(self.douban_tv_weekly_global())
|
||||||
self.douban_tv_hot()
|
recommends.extend(self.douban_tv_animation())
|
||||||
|
recommends.extend(self.douban_movie_hot())
|
||||||
|
recommends.extend(self.douban_tv_hot())
|
||||||
|
self.__cache_posters(recommends)
|
||||||
logger.debug("Recommend data refresh completed.")
|
logger.debug("Recommend data refresh completed.")
|
||||||
|
|
||||||
|
def __cache_posters(self, datas: List[dict]):
|
||||||
|
"""
|
||||||
|
提取 poster_path 并缓存图片
|
||||||
|
:param datas: 数据列表
|
||||||
|
"""
|
||||||
|
if not settings.GLOBAL_IMAGE_CACHE:
|
||||||
|
return
|
||||||
|
|
||||||
|
for data in datas:
|
||||||
|
poster_path = data.get("poster_path")
|
||||||
|
if poster_path:
|
||||||
|
poster_url = poster_path.replace("original", "w500")
|
||||||
|
logger.debug(f"Caching poster image: {poster_url}")
|
||||||
|
self.__fetch_and_save_image(poster_url)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __fetch_and_save_image(url: str):
|
||||||
|
"""
|
||||||
|
请求并保存图片
|
||||||
|
:param url: 图片路径
|
||||||
|
"""
|
||||||
|
if not settings.GLOBAL_IMAGE_CACHE or not url:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 生成缓存路径
|
||||||
|
sanitized_path = SecurityUtils.sanitize_url_path(url)
|
||||||
|
cache_path = settings.CACHE_PATH / "images" / sanitized_path
|
||||||
|
|
||||||
|
# 确保缓存路径和文件类型合法
|
||||||
|
if not SecurityUtils.is_safe_path(settings.CACHE_PATH, cache_path, settings.SECURITY_IMAGE_SUFFIXES):
|
||||||
|
logger.debug(f"Invalid cache path or file type for URL: {url}, sanitized path: {sanitized_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 本地存在缓存图片,则直接跳过
|
||||||
|
if cache_path.exists():
|
||||||
|
logger.debug(f"Cache hit: Image already exists at {cache_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 请求远程图片
|
||||||
|
referer = "https://movie.douban.com/" if "doubanio.com" in url else None
|
||||||
|
proxies = settings.PROXY if not referer else None
|
||||||
|
response = RequestUtils(ua=settings.USER_AGENT, proxies=proxies, referer=referer).get_res(url=url)
|
||||||
|
if not response:
|
||||||
|
logger.debug(f"Empty response for URL: {url}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证下载的内容是否为有效图片
|
||||||
|
try:
|
||||||
|
Image.open(io.BytesIO(response.content)).verify()
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Invalid image format for URL {url}: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not cache_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not cache_path.parent.exists():
|
||||||
|
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with tempfile.NamedTemporaryFile(dir=cache_path.parent, delete=False) as tmp_file:
|
||||||
|
tmp_file.write(response.content)
|
||||||
|
temp_path = Path(tmp_file.name)
|
||||||
|
temp_path.replace(cache_path)
|
||||||
|
logger.debug(f"Successfully cached image at {cache_path} for URL: {url}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Failed to write cache file {cache_path} for URL {url}: {e}")
|
||||||
|
|
||||||
@log_execution_time(logger=logger)
|
@log_execution_time(logger=logger)
|
||||||
@cached_with_empty_check
|
@cached_with_empty_check
|
||||||
def tmdb_movies(self, sort_by: str = "popularity.desc", with_genres: str = "",
|
def tmdb_movies(self, sort_by: str = "popularity.desc", with_genres: str = "",
|
||||||
|
|||||||
Reference in New Issue
Block a user