mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 16:07:01 +08:00
fix: improve media image scraping
This commit is contained in:
@@ -969,7 +969,24 @@ class TheMovieDbModule(_ModuleBase):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _process_tmdb_images(mediainfo: MediaInfo, images: dict) -> MediaInfo:
|
||||
def _pick_best_tmdb_image(images: list) -> Optional[str]:
|
||||
"""
|
||||
从 TMDB 图片候选中选出评分最高的文件路径。
|
||||
"""
|
||||
if not images:
|
||||
return None
|
||||
images = sorted(
|
||||
images,
|
||||
key=lambda x: (
|
||||
x.get("vote_average") or 0,
|
||||
x.get("vote_count") or 0,
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
return images[0].get("file_path")
|
||||
|
||||
@classmethod
|
||||
def _process_tmdb_images(cls, mediainfo: MediaInfo, images: dict) -> MediaInfo:
|
||||
"""
|
||||
处理 TMDB 图片数据
|
||||
:param mediainfo: 媒体信息
|
||||
@@ -980,22 +997,16 @@ class TheMovieDbModule(_ModuleBase):
|
||||
images = images[0]
|
||||
# 背景图
|
||||
if not mediainfo.backdrop_path:
|
||||
backdrops = images.get("backdrops")
|
||||
if backdrops:
|
||||
backdrops = sorted(backdrops, key=lambda x: x.get("vote_average"), reverse=True)
|
||||
mediainfo.backdrop_path = settings.TMDB_IMAGE_URL(backdrops[0].get("file_path"))
|
||||
if image_path := cls._pick_best_tmdb_image(images.get("backdrops")):
|
||||
mediainfo.backdrop_path = settings.TMDB_IMAGE_URL(image_path)
|
||||
# 标志
|
||||
if not mediainfo.logo_path:
|
||||
logos = images.get("logos")
|
||||
if logos:
|
||||
logos = sorted(logos, key=lambda x: x.get("vote_average"), reverse=True)
|
||||
mediainfo.logo_path = settings.TMDB_IMAGE_URL(logos[0].get("file_path"))
|
||||
if image_path := cls._pick_best_tmdb_image(images.get("logos")):
|
||||
mediainfo.logo_path = settings.TMDB_IMAGE_URL(image_path)
|
||||
# 海报
|
||||
if not mediainfo.poster_path:
|
||||
posters = images.get("posters")
|
||||
if posters:
|
||||
posters = sorted(posters, key=lambda x: x.get("vote_average"), reverse=True)
|
||||
mediainfo.poster_path = settings.TMDB_IMAGE_URL(posters[0].get("file_path"))
|
||||
if image_path := cls._pick_best_tmdb_image(images.get("posters")):
|
||||
mediainfo.poster_path = settings.TMDB_IMAGE_URL(image_path)
|
||||
return mediainfo
|
||||
|
||||
def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]:
|
||||
@@ -1011,9 +1022,15 @@ class TheMovieDbModule(_ModuleBase):
|
||||
|
||||
# 调用TMDB图片接口
|
||||
if mediainfo.type == MediaType.MOVIE:
|
||||
images = self.tmdb.get_movie_images(mediainfo.tmdb_id)
|
||||
images = self.tmdb.get_movie_images(
|
||||
mediainfo.tmdb_id,
|
||||
original_language=mediainfo.original_language,
|
||||
)
|
||||
else:
|
||||
images = self.tmdb.get_tv_images(mediainfo.tmdb_id)
|
||||
images = self.tmdb.get_tv_images(
|
||||
mediainfo.tmdb_id,
|
||||
original_language=mediainfo.original_language,
|
||||
)
|
||||
if not images:
|
||||
return mediainfo
|
||||
|
||||
@@ -1033,9 +1050,15 @@ class TheMovieDbModule(_ModuleBase):
|
||||
|
||||
# 调用TMDB图片接口
|
||||
if mediainfo.type == MediaType.MOVIE:
|
||||
images = await self.tmdb.async_get_movie_images(mediainfo.tmdb_id)
|
||||
images = await self.tmdb.async_get_movie_images(
|
||||
mediainfo.tmdb_id,
|
||||
original_language=mediainfo.original_language,
|
||||
)
|
||||
else:
|
||||
images = await self.tmdb.async_get_tv_images(mediainfo.tmdb_id)
|
||||
images = await self.tmdb.async_get_tv_images(
|
||||
mediainfo.tmdb_id,
|
||||
original_language=mediainfo.original_language,
|
||||
)
|
||||
if not images:
|
||||
return mediainfo
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ class TmdbScraper:
|
||||
if poster_name and poster_url:
|
||||
images[poster_name] = poster_url
|
||||
else:
|
||||
self.__ensure_main_images(mediainfo)
|
||||
# 获取媒体信息中原有图片
|
||||
for attr_name, attr_value in vars(mediainfo).items():
|
||||
if (
|
||||
@@ -155,6 +156,52 @@ class TmdbScraper:
|
||||
images[image_name] = image_url
|
||||
return images
|
||||
|
||||
def __ensure_main_images(self, mediainfo: MediaInfo) -> None:
|
||||
"""
|
||||
主媒体图片缺失时从 TMDB images 接口回填,避免当前语言没有图时只生成 NFO。
|
||||
"""
|
||||
if not mediainfo or not mediainfo.tmdb_id:
|
||||
return
|
||||
if mediainfo.poster_path and mediainfo.backdrop_path:
|
||||
return
|
||||
if mediainfo.type == MediaType.MOVIE:
|
||||
image_info = self.default_tmdb.get_movie_images(
|
||||
mediainfo.tmdb_id,
|
||||
original_language=mediainfo.original_language,
|
||||
)
|
||||
else:
|
||||
image_info = self.default_tmdb.get_tv_images(
|
||||
mediainfo.tmdb_id,
|
||||
original_language=mediainfo.original_language,
|
||||
)
|
||||
if not image_info:
|
||||
return
|
||||
if not mediainfo.poster_path:
|
||||
poster_path = self.__pick_best_image_path(image_info.get("posters"))
|
||||
if poster_path:
|
||||
mediainfo.poster_path = settings.TMDB_IMAGE_URL(poster_path)
|
||||
if not mediainfo.backdrop_path:
|
||||
backdrop_path = self.__pick_best_image_path(image_info.get("backdrops"))
|
||||
if backdrop_path:
|
||||
mediainfo.backdrop_path = settings.TMDB_IMAGE_URL(backdrop_path)
|
||||
|
||||
@staticmethod
|
||||
def __pick_best_image_path(images: list) -> Optional[str]:
|
||||
"""
|
||||
从 TMDB 图片列表中选择评分和投票数最高的一张。
|
||||
"""
|
||||
if not images:
|
||||
return None
|
||||
images = sorted(
|
||||
images,
|
||||
key=lambda item: (
|
||||
item.get("vote_average") or 0,
|
||||
item.get("vote_count") or 0,
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
return images[0].get("file_path")
|
||||
|
||||
@staticmethod
|
||||
def get_season_poster(seasoninfo: dict, season: int) -> Tuple[str, str]:
|
||||
"""
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Optional, List
|
||||
|
||||
import zhconv
|
||||
|
||||
from app.core.config import settings
|
||||
from app.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
from app.utils.string import StringUtils
|
||||
@@ -1232,7 +1233,24 @@ class TmdbApi:
|
||||
logger.error(str(e))
|
||||
return []
|
||||
|
||||
def get_movie_images(self, tmdbid: int) -> dict:
|
||||
@staticmethod
|
||||
def _build_include_image_language(original_language: Optional[str] = None) -> str:
|
||||
"""
|
||||
构造图片接口语言回退列表,避免当前语言没有图片时返回空列表。
|
||||
"""
|
||||
languages = []
|
||||
for language in (
|
||||
getattr(settings, "TMDB_LOCALE", None),
|
||||
"en",
|
||||
None,
|
||||
original_language,
|
||||
):
|
||||
language = "null" if language is None else str(language).strip()
|
||||
if language and language not in languages:
|
||||
languages.append(language)
|
||||
return ",".join(languages)
|
||||
|
||||
def get_movie_images(self, tmdbid: int, original_language: Optional[str] = None) -> dict:
|
||||
"""
|
||||
获取电影的图片
|
||||
"""
|
||||
@@ -1240,12 +1258,17 @@ class TmdbApi:
|
||||
return {}
|
||||
try:
|
||||
logger.debug(f"正在获取电影图片:{tmdbid}...")
|
||||
return self.movie.images(movie_id=tmdbid) or {}
|
||||
return self.movie.images(
|
||||
movie_id=tmdbid,
|
||||
include_image_language=self._build_include_image_language(
|
||||
original_language
|
||||
),
|
||||
) or {}
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
return {}
|
||||
|
||||
def get_tv_images(self, tmdbid: int) -> dict:
|
||||
def get_tv_images(self, tmdbid: int, original_language: Optional[str] = None) -> dict:
|
||||
"""
|
||||
获取电视剧的图片
|
||||
"""
|
||||
@@ -1253,7 +1276,12 @@ class TmdbApi:
|
||||
return {}
|
||||
try:
|
||||
logger.debug(f"正在获取电视剧图片:{tmdbid}...")
|
||||
return self.tv.images(tv_id=tmdbid) or {}
|
||||
return self.tv.images(
|
||||
tv_id=tmdbid,
|
||||
include_image_language=self._build_include_image_language(
|
||||
original_language
|
||||
),
|
||||
) or {}
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
return {}
|
||||
@@ -1965,7 +1993,9 @@ class TmdbApi:
|
||||
logger.error(str(e))
|
||||
return []
|
||||
|
||||
async def async_get_movie_images(self, tmdbid: int) -> dict:
|
||||
async def async_get_movie_images(
|
||||
self, tmdbid: int, original_language: Optional[str] = None
|
||||
) -> dict:
|
||||
"""
|
||||
获取电影的图片(异步版本)
|
||||
"""
|
||||
@@ -1973,12 +2003,19 @@ class TmdbApi:
|
||||
return {}
|
||||
try:
|
||||
logger.debug(f"正在获取电影图片:{tmdbid}...")
|
||||
return await self.movie.async_images(movie_id=tmdbid) or {}
|
||||
return await self.movie.async_images(
|
||||
movie_id=tmdbid,
|
||||
include_image_language=self._build_include_image_language(
|
||||
original_language
|
||||
),
|
||||
) or {}
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
return {}
|
||||
|
||||
async def async_get_tv_images(self, tmdbid: int) -> dict:
|
||||
async def async_get_tv_images(
|
||||
self, tmdbid: int, original_language: Optional[str] = None
|
||||
) -> dict:
|
||||
"""
|
||||
获取电视剧的图片(异步版本)
|
||||
"""
|
||||
@@ -1986,7 +2023,12 @@ class TmdbApi:
|
||||
return {}
|
||||
try:
|
||||
logger.debug(f"正在获取电视剧图片:{tmdbid}...")
|
||||
return await self.tv.async_images(tv_id=tmdbid) or {}
|
||||
return await self.tv.async_images(
|
||||
tv_id=tmdbid,
|
||||
include_image_language=self._build_include_image_language(
|
||||
original_language
|
||||
),
|
||||
) or {}
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
return {}
|
||||
|
||||
Reference in New Issue
Block a user