feat:人物搜索API
This commit is contained in:
parent
2f71e401be
commit
f20b1bcfe9
@ -63,15 +63,16 @@ def recognize_file2(path: str,
|
|||||||
return recognize_file(path)
|
return recognize_file(path)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/search", summary="搜索媒体信息", response_model=List[schemas.MediaInfo])
|
@router.get("/search", summary="搜索媒体/人物信息", response_model=List[schemas.MediaInfo])
|
||||||
def search_by_title(title: str,
|
def search_by_title(title: str,
|
||||||
|
type: str = "media",
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
count: int = 8,
|
count: int = 8,
|
||||||
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
模糊搜索媒体信息列表
|
模糊搜索媒体/人物信息列表 media:媒体信息,person:人物信息
|
||||||
"""
|
"""
|
||||||
_, medias = MediaChain().search(title=title)
|
_, medias = MediaChain().search(title=title, stype=type)
|
||||||
if medias:
|
if medias:
|
||||||
return [media.to_dict() for media in medias[(page - 1) * count: page * count]]
|
return [media.to_dict() for media in medias[(page - 1) * count: page * count]]
|
||||||
return []
|
return []
|
||||||
|
@ -19,7 +19,7 @@ from app.db.message_oper import MessageOper
|
|||||||
from app.helper.message import MessageHelper
|
from app.helper.message import MessageHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \
|
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \
|
||||||
WebhookEventInfo, TmdbEpisode
|
WebhookEventInfo, TmdbEpisode, TmdbPerson, DoubanPerson
|
||||||
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType
|
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType
|
||||||
from app.utils.object import ObjectUtils
|
from app.utils.object import ObjectUtils
|
||||||
|
|
||||||
@ -260,6 +260,13 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
return self.run_module("search_medias", meta=meta)
|
return self.run_module("search_medias", meta=meta)
|
||||||
|
|
||||||
|
def search_persons(self, name: str) -> Optional[List[TmdbPerson, DoubanPerson]]:
|
||||||
|
"""
|
||||||
|
搜索人物信息
|
||||||
|
:param name: 人物名称
|
||||||
|
"""
|
||||||
|
return self.run_module("search_persons", name=name)
|
||||||
|
|
||||||
def search_torrents(self, site: CommentedMap,
|
def search_torrents(self, site: CommentedMap,
|
||||||
keywords: List[str],
|
keywords: List[str],
|
||||||
mtype: MediaType = None,
|
mtype: MediaType = None,
|
||||||
|
@ -2,7 +2,7 @@ import copy
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Optional, List, Tuple
|
from typing import Optional, List, Tuple, Union
|
||||||
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.core.context import Context, MediaInfo
|
from app.core.context import Context, MediaInfo
|
||||||
@ -10,6 +10,7 @@ from app.core.event import eventmanager, Event
|
|||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.core.metainfo import MetaInfo, MetaInfoPath
|
from app.core.metainfo import MetaInfo, MetaInfoPath
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
|
from app.schemas import TmdbPerson, DoubanPerson
|
||||||
from app.schemas.types import EventType, MediaType
|
from app.schemas.types import EventType, MediaType
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
@ -156,12 +157,15 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
# 返回上下文
|
# 返回上下文
|
||||||
return Context(meta_info=file_meta, media_info=mediainfo)
|
return Context(meta_info=file_meta, media_info=mediainfo)
|
||||||
|
|
||||||
def search(self, title: str) -> Tuple[MetaBase, List[MediaInfo]]:
|
def search(self, title: str,
|
||||||
|
stype: str = "media") -> Tuple[Optional[MetaBase], List[Union[MediaInfo, TmdbPerson, DoubanPerson]]]:
|
||||||
"""
|
"""
|
||||||
搜索媒体信息
|
搜索媒体/人物信息
|
||||||
:param title: 搜索内容
|
:param title: 搜索内容
|
||||||
|
:param stype: 搜索类型 media:媒体信息,person:人物信息
|
||||||
:return: 识别元数据,媒体信息列表
|
:return: 识别元数据,媒体信息列表
|
||||||
"""
|
"""
|
||||||
|
if stype == "media":
|
||||||
# 提取要素
|
# 提取要素
|
||||||
mtype, key_word, season_num, episode_num, year, content = StringUtils.get_keyword(title)
|
mtype, key_word, season_num, episode_num, year, content = StringUtils.get_keyword(title)
|
||||||
# 识别
|
# 识别
|
||||||
@ -186,6 +190,15 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
logger.info(f"{content} 搜索到 {len(medias)} 条相关媒体信息")
|
logger.info(f"{content} 搜索到 {len(medias)} 条相关媒体信息")
|
||||||
# 识别的元数据,媒体信息列表
|
# 识别的元数据,媒体信息列表
|
||||||
return meta, medias
|
return meta, medias
|
||||||
|
else:
|
||||||
|
# 搜索人物信息
|
||||||
|
logger.info(f"开始搜索人物信息:{title}")
|
||||||
|
persons: Optional[List[Union[TmdbPerson, DoubanPerson]]] = self.search_persons(name=title)
|
||||||
|
if not persons:
|
||||||
|
logger.warn(f"{title} 没有找到对应的人物信息!")
|
||||||
|
return None, []
|
||||||
|
logger.info(f"{title} 搜索到 {len(persons)} 条相关人物信息")
|
||||||
|
return None, persons
|
||||||
|
|
||||||
def get_tmdbinfo_by_doubanid(self, doubanid: str, mtype: MediaType = None) -> Optional[dict]:
|
def get_tmdbinfo_by_doubanid(self, doubanid: str, mtype: MediaType = None) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
|
@ -13,6 +13,7 @@ from app.modules import _ModuleBase
|
|||||||
from app.modules.douban.apiv2 import DoubanApi
|
from app.modules.douban.apiv2 import DoubanApi
|
||||||
from app.modules.douban.douban_cache import DoubanCache
|
from app.modules.douban.douban_cache import DoubanCache
|
||||||
from app.modules.douban.scraper import DoubanScraper
|
from app.modules.douban.scraper import DoubanScraper
|
||||||
|
from app.schemas import DoubanPerson
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
from app.utils.common import retry
|
from app.utils.common import retry
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
@ -563,6 +564,19 @@ class DoubanModule(_ModuleBase):
|
|||||||
media.season = meta.begin_season
|
media.season = meta.begin_season
|
||||||
return ret_medias
|
return ret_medias
|
||||||
|
|
||||||
|
def search_persons(self, name: str) -> Optional[List[DoubanPerson]]:
|
||||||
|
"""
|
||||||
|
搜索人物信息
|
||||||
|
"""
|
||||||
|
if settings.RECOGNIZE_SOURCE != "douban":
|
||||||
|
return None
|
||||||
|
if not name:
|
||||||
|
return []
|
||||||
|
result = self.doubanapi.person_search(name)
|
||||||
|
if not result:
|
||||||
|
return []
|
||||||
|
return [DoubanPerson(**item) for item in result.get("items")]
|
||||||
|
|
||||||
@retry(Exception, 5, 3, 3, logger=logger)
|
@retry(Exception, 5, 3, 3, logger=logger)
|
||||||
def match_doubaninfo(self, name: str, imdbid: str = None,
|
def match_doubaninfo(self, name: str, imdbid: str = None,
|
||||||
mtype: MediaType = None, year: str = None, season: int = None) -> dict:
|
mtype: MediaType = None, year: str = None, season: int = None) -> dict:
|
||||||
|
@ -137,6 +137,9 @@ class DoubanApi(metaclass=Singleton):
|
|||||||
# doulist
|
# doulist
|
||||||
"doulist": "/doulist/",
|
"doulist": "/doulist/",
|
||||||
"doulist_items": "/doulist/%s/items",
|
"doulist_items": "/doulist/%s/items",
|
||||||
|
|
||||||
|
# personlist
|
||||||
|
"person_search": "/person/search",
|
||||||
}
|
}
|
||||||
|
|
||||||
_user_agents = [
|
_user_agents = [
|
||||||
@ -274,6 +277,14 @@ class DoubanApi(metaclass=Singleton):
|
|||||||
return self.__invoke(self._urls["group_search"], q=keyword,
|
return self.__invoke(self._urls["group_search"], q=keyword,
|
||||||
start=start, count=count, _ts=ts)
|
start=start, count=count, _ts=ts)
|
||||||
|
|
||||||
|
def person_search(self, keyword: str, start: int = 0, count: int = 20,
|
||||||
|
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
|
||||||
|
"""
|
||||||
|
人物搜索
|
||||||
|
"""
|
||||||
|
return self.__invoke(self._urls["person_search"], q=keyword,
|
||||||
|
start=start, count=count, _ts=ts)
|
||||||
|
|
||||||
def movie_showing(self, start: int = 0, count: int = 20,
|
def movie_showing(self, start: int = 0, count: int = 20,
|
||||||
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
|
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
|
||||||
"""
|
"""
|
||||||
|
@ -13,6 +13,7 @@ from app.modules.themoviedb.category import CategoryHelper
|
|||||||
from app.modules.themoviedb.scraper import TmdbScraper
|
from app.modules.themoviedb.scraper import TmdbScraper
|
||||||
from app.modules.themoviedb.tmdb_cache import TmdbCache
|
from app.modules.themoviedb.tmdb_cache import TmdbCache
|
||||||
from app.modules.themoviedb.tmdbapi import TmdbApi
|
from app.modules.themoviedb.tmdbapi import TmdbApi
|
||||||
|
from app.schemas import TmdbPerson
|
||||||
from app.schemas.types import MediaType, MediaImageType
|
from app.schemas.types import MediaType, MediaImageType
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
@ -261,6 +262,19 @@ class TheMovieDbModule(_ModuleBase):
|
|||||||
return medias
|
return medias
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def search_persons(self, name: str) -> Optional[List[TmdbPerson]]:
|
||||||
|
"""
|
||||||
|
搜索人物信息
|
||||||
|
"""
|
||||||
|
if settings.RECOGNIZE_SOURCE != "themoviedb":
|
||||||
|
return None
|
||||||
|
if not name:
|
||||||
|
return []
|
||||||
|
results = self.tmdb.search_persons(name)
|
||||||
|
if results:
|
||||||
|
return [TmdbPerson(**person) for person in results]
|
||||||
|
return []
|
||||||
|
|
||||||
def scrape_metadata(self, path: Path, mediainfo: MediaInfo, transfer_type: str,
|
def scrape_metadata(self, path: Path, mediainfo: MediaInfo, transfer_type: str,
|
||||||
metainfo: MetaBase = None, force_nfo: bool = False, force_img: bool = False) -> None:
|
metainfo: MetaBase = None, force_nfo: bool = False, force_img: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -95,6 +95,20 @@ class TmdbApi:
|
|||||||
ret_infos.append(tv)
|
ret_infos.append(tv)
|
||||||
return ret_infos
|
return ret_infos
|
||||||
|
|
||||||
|
def search_persons(self, name: str) -> List[dict]:
|
||||||
|
"""
|
||||||
|
查询模糊匹配的所有人物TMDB信息
|
||||||
|
"""
|
||||||
|
if not name:
|
||||||
|
return []
|
||||||
|
ret_infos = []
|
||||||
|
persons = self.person.search(query=name) or []
|
||||||
|
for person in persons:
|
||||||
|
if name in person.get("name"):
|
||||||
|
person['media_type'] = MediaType.PERSON
|
||||||
|
ret_infos.append(person)
|
||||||
|
return ret_infos
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __compare_names(file_name: str, tmdb_names: list) -> bool:
|
def __compare_names(file_name: str, tmdb_names: list) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -136,3 +136,16 @@ class Person(TMDb):
|
|||||||
params="page=%s" % page,
|
params="page=%s" % page,
|
||||||
key="results"
|
key="results"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def search(self, query, page=1):
|
||||||
|
"""
|
||||||
|
Search for people.
|
||||||
|
:param query: str
|
||||||
|
:param page: int
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return self._request_obj(
|
||||||
|
self._urls["search_people"],
|
||||||
|
params="query=%s&page=%s" % (query, page),
|
||||||
|
key="results"
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user