From f20b1bcfe96b289c870cd874be0683c1edd1e745 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Fri, 26 Apr 2024 17:47:45 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=BA=BA=E7=89=A9=E6=90=9C?= =?UTF-8?q?=E7=B4=A2API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/media.py | 7 +- app/chain/__init__.py | 9 ++- app/chain/media.py | 67 +++++++++++-------- app/modules/douban/__init__.py | 14 ++++ app/modules/douban/apiv2.py | 11 +++ app/modules/themoviedb/__init__.py | 14 ++++ app/modules/themoviedb/tmdbapi.py | 14 ++++ .../themoviedb/tmdbv3api/objs/person.py | 13 ++++ 8 files changed, 118 insertions(+), 31 deletions(-) diff --git a/app/api/endpoints/media.py b/app/api/endpoints/media.py index d4f27777..3afb4708 100644 --- a/app/api/endpoints/media.py +++ b/app/api/endpoints/media.py @@ -63,15 +63,16 @@ def recognize_file2(path: str, 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, + type: str = "media", page: int = 1, count: int = 8, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ - 模糊搜索媒体信息列表 + 模糊搜索媒体/人物信息列表 media:媒体信息,person:人物信息 """ - _, medias = MediaChain().search(title=title) + _, medias = MediaChain().search(title=title, stype=type) if medias: return [media.to_dict() for media in medias[(page - 1) * count: page * count]] return [] diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 687acef1..81a161f3 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -19,7 +19,7 @@ from app.db.message_oper import MessageOper from app.helper.message import MessageHelper from app.log import logger 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.utils.object import ObjectUtils @@ -260,6 +260,13 @@ class ChainBase(metaclass=ABCMeta): """ 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, keywords: List[str], mtype: MediaType = None, diff --git a/app/chain/media.py b/app/chain/media.py index 71540b82..14ccf62b 100644 --- a/app/chain/media.py +++ b/app/chain/media.py @@ -2,7 +2,7 @@ import copy import time from pathlib import Path from threading import Lock -from typing import Optional, List, Tuple +from typing import Optional, List, Tuple, Union from app.chain import ChainBase 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.metainfo import MetaInfo, MetaInfoPath from app.log import logger +from app.schemas import TmdbPerson, DoubanPerson from app.schemas.types import EventType, MediaType from app.utils.singleton import Singleton from app.utils.string import StringUtils @@ -156,36 +157,48 @@ class MediaChain(ChainBase, metaclass=Singleton): # 返回上下文 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 stype: 搜索类型 media:媒体信息,person:人物信息 :return: 识别元数据,媒体信息列表 """ - # 提取要素 - mtype, key_word, season_num, episode_num, year, content = StringUtils.get_keyword(title) - # 识别 - meta = MetaInfo(content) - if not meta.name: - meta.cn_name = content - # 合并信息 - if mtype: - meta.type = mtype - if season_num: - meta.begin_season = season_num - if episode_num: - meta.begin_episode = episode_num - if year: - meta.year = year - # 开始搜索 - logger.info(f"开始搜索媒体信息:{meta.name}") - medias: Optional[List[MediaInfo]] = self.search_medias(meta=meta) - if not medias: - logger.warn(f"{meta.name} 没有找到对应的媒体信息!") - return meta, [] - logger.info(f"{content} 搜索到 {len(medias)} 条相关媒体信息") - # 识别的元数据,媒体信息列表 - return meta, medias + if stype == "media": + # 提取要素 + mtype, key_word, season_num, episode_num, year, content = StringUtils.get_keyword(title) + # 识别 + meta = MetaInfo(content) + if not meta.name: + meta.cn_name = content + # 合并信息 + if mtype: + meta.type = mtype + if season_num: + meta.begin_season = season_num + if episode_num: + meta.begin_episode = episode_num + if year: + meta.year = year + # 开始搜索 + logger.info(f"开始搜索媒体信息:{meta.name}") + medias: Optional[List[MediaInfo]] = self.search_medias(meta=meta) + if not medias: + logger.warn(f"{meta.name} 没有找到对应的媒体信息!") + return meta, [] + logger.info(f"{content} 搜索到 {len(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]: """ diff --git a/app/modules/douban/__init__.py b/app/modules/douban/__init__.py index cd8bf35b..859e8143 100644 --- a/app/modules/douban/__init__.py +++ b/app/modules/douban/__init__.py @@ -13,6 +13,7 @@ from app.modules import _ModuleBase from app.modules.douban.apiv2 import DoubanApi from app.modules.douban.douban_cache import DoubanCache from app.modules.douban.scraper import DoubanScraper +from app.schemas import DoubanPerson from app.schemas.types import MediaType from app.utils.common import retry from app.utils.http import RequestUtils @@ -563,6 +564,19 @@ class DoubanModule(_ModuleBase): media.season = meta.begin_season 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) def match_doubaninfo(self, name: str, imdbid: str = None, mtype: MediaType = None, year: str = None, season: int = None) -> dict: diff --git a/app/modules/douban/apiv2.py b/app/modules/douban/apiv2.py index b12a97a3..c3f2781d 100644 --- a/app/modules/douban/apiv2.py +++ b/app/modules/douban/apiv2.py @@ -137,6 +137,9 @@ class DoubanApi(metaclass=Singleton): # doulist "doulist": "/doulist/", "doulist_items": "/doulist/%s/items", + + # personlist + "person_search": "/person/search", } _user_agents = [ @@ -274,6 +277,14 @@ class DoubanApi(metaclass=Singleton): return self.__invoke(self._urls["group_search"], q=keyword, 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, ts=datetime.strftime(datetime.now(), '%Y%m%d')): """ diff --git a/app/modules/themoviedb/__init__.py b/app/modules/themoviedb/__init__.py index e5ae52be..a9129158 100644 --- a/app/modules/themoviedb/__init__.py +++ b/app/modules/themoviedb/__init__.py @@ -13,6 +13,7 @@ from app.modules.themoviedb.category import CategoryHelper from app.modules.themoviedb.scraper import TmdbScraper from app.modules.themoviedb.tmdb_cache import TmdbCache from app.modules.themoviedb.tmdbapi import TmdbApi +from app.schemas import TmdbPerson from app.schemas.types import MediaType, MediaImageType from app.utils.http import RequestUtils from app.utils.system import SystemUtils @@ -261,6 +262,19 @@ class TheMovieDbModule(_ModuleBase): return medias 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, metainfo: MetaBase = None, force_nfo: bool = False, force_img: bool = False) -> None: """ diff --git a/app/modules/themoviedb/tmdbapi.py b/app/modules/themoviedb/tmdbapi.py index 8045e86e..96caff5d 100644 --- a/app/modules/themoviedb/tmdbapi.py +++ b/app/modules/themoviedb/tmdbapi.py @@ -95,6 +95,20 @@ class TmdbApi: ret_infos.append(tv) 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 def __compare_names(file_name: str, tmdb_names: list) -> bool: """ diff --git a/app/modules/themoviedb/tmdbv3api/objs/person.py b/app/modules/themoviedb/tmdbv3api/objs/person.py index a976bfe6..0c23d48b 100644 --- a/app/modules/themoviedb/tmdbv3api/objs/person.py +++ b/app/modules/themoviedb/tmdbv3api/objs/person.py @@ -136,3 +136,16 @@ class Person(TMDb): params="page=%s" % page, 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" + )