diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 7c3c9f3f..fafba887 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -115,16 +115,18 @@ class ChainBase(metaclass=ABCMeta): """ return self.run_module("recognize_media", meta=meta, mtype=mtype, tmdbid=tmdbid) - def match_doubaninfo(self, name: str, mtype: str = None, - year: str = None, season: int = None) -> Optional[dict]: + def match_doubaninfo(self, name: str, imdbid: str = None, + mtype: str = None, year: str = None, season: int = None) -> Optional[dict]: """ 搜索和匹配豆瓣信息 :param name: 标题 + :param imdbid: imdbid :param mtype: 类型 :param year: 年份 :param season: 季 """ - return self.run_module("match_doubaninfo", name=name, mtype=mtype, year=year, season=season) + return self.run_module("match_doubaninfo", name=name, imdbid=imdbid, + mtype=mtype, year=year, season=season) def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]: """ diff --git a/app/modules/douban/__init__.py b/app/modules/douban/__init__.py index eb95bddc..1411e191 100644 --- a/app/modules/douban/__init__.py +++ b/app/modules/douban/__init__.py @@ -1,4 +1,3 @@ -from datetime import datetime from pathlib import Path from typing import List, Optional, Tuple, Union @@ -406,17 +405,23 @@ class DoubanModule(_ModuleBase): return ret_medias @retry(Exception, 5, 3, 3, logger=logger) - def match_doubaninfo(self, name: str, mtype: str = None, - year: str = None, season: int = None) -> dict: + def match_doubaninfo(self, name: str, imdbid: str = None, + mtype: str = None, year: str = None, season: int = None) -> dict: """ 搜索和匹配豆瓣信息 :param name: 名称 + :param imdbid: IMDB ID :param mtype: 类型 电影/电视剧 :param year: 年份 :param season: 季号 """ - result = self.doubanapi.search(f"{name} {year or ''}".strip(), - ts=datetime.strftime(datetime.now(), '%Y%m%d%H%M%S')) + if imdbid: + # 优先使用IMDBID查询 + result = self.doubanapi.imdbid(imdbid) + if result: + return result + # 搜索 + result = self.doubanapi.search(f"{name} {year or ''}".strip()) if not result: logger.warn(f"未找到 {name} 的豆瓣信息") return {} diff --git a/app/modules/douban/apiv2.py b/app/modules/douban/apiv2.py index 920fd8b2..bebf43eb 100644 --- a/app/modules/douban/apiv2.py +++ b/app/modules/douban/apiv2.py @@ -18,28 +18,29 @@ class DoubanApi(metaclass=Singleton): _urls = { # 搜索类 # sort=U:近期热门 T:标记最多 S:评分最高 R:最新上映 - # q=search_word&start=0&count=20&sort=U + # q=search_word&start: int = 0&count: int = 20&sort=U # 聚合搜索 "search": "/search/weixin", "search_agg": "/search", + "imdbid": "/movie/imdb/%s", # 电影探索 # sort=U:综合排序 T:近期热度 S:高分优先 R:首播时间 - # tags='日本,动画,2022'&start=0&count=20&sort=U + # tags='日本,动画,2022'&start: int = 0&count: int = 20&sort=U "movie_recommend": "/movie/recommend", # 电视剧探索 "tv_recommend": "/tv/recommend", # 搜索 "movie_tag": "/movie/tag", "tv_tag": "/tv/tag", - # q=search_word&start=0&count=20 + # q=search_word&start: int = 0&count: int = 20 "movie_search": "/search/movie", "tv_search": "/search/movie", "book_search": "/search/book", "group_search": "/search/group", # 各类主题合集 - # start=0&count=20 + # start: int = 0&count: int = 20 # 正在上映 "movie_showing": "/subject_collection/movie_showing/items", # 热门电影 @@ -145,7 +146,9 @@ class DoubanApi(metaclass=Singleton): "api-client/1 com.douban.frodo/7.3.0(207) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1"] _api_secret_key = "bf7dddc7c9cfe6f7" _api_key = "0dad551ec0f84ed02907ff5c42e8ec70" + _api_key2 = "0ab215a8b1977939201640fa14c66bab" _base_url = "https://frodo.douban.com/api/v2" + _api_url = "https://api.douban.com/v2" _session = None def __init__(self): @@ -153,6 +156,9 @@ class DoubanApi(metaclass=Singleton): @classmethod def __sign(cls, url: str, ts: int, method='GET') -> str: + """ + 签名 + """ url_path = parse.urlparse(url).path raw_sign = '&'.join([method.upper(), parse.quote(url_path, safe=''), str(ts)]) return base64.b64encode( @@ -164,7 +170,10 @@ class DoubanApi(metaclass=Singleton): ).decode() @lru_cache(maxsize=settings.CACHE_CONF.get('douban')) - def __invoke(self, url, **kwargs): + def __invoke(self, url: str, **kwargs) -> dict: + """ + GET请求 + """ req_url = self._base_url + url params = {'apiKey': self._api_key} @@ -189,119 +198,223 @@ class DoubanApi(metaclass=Singleton): return resp.json() return resp.json() if resp else {} - def search(self, keyword, start=0, count=20, - ts=datetime.strftime(datetime.now(), '%Y%m%d')): + @lru_cache(maxsize=settings.CACHE_CONF.get('douban')) + def __post(self, url: str, **kwargs) -> dict: + """ + POST请求 + esponse = requests.post( + url="https://api.douban.com/v2/movie/imdb/tt29139455", + headers={ + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + "Cookie": "bid=J9zb1zA5sJc", + }, + data={ + "apikey": "0ab215a8b1977939201640fa14c66bab", + }, + ) + """ + req_url = self._api_url + url + params = {'apiKey': self._api_key2} + if kwargs: + params.update(kwargs) + if '_ts' in params: + params.pop('_ts') + resp = RequestUtils( + session=self._session + ).post_res(url=req_url, data=params) + if resp.status_code == 400 and "rate_limit" in resp.text: + return resp.json() + return resp.json() if resp else {} + + def search(self, keyword: str, start: int = 0, count: int = 20, + ts=datetime.strftime(datetime.now(), '%Y%m%d')) -> dict: + """ + 关键字搜索 + """ return self.__invoke(self._urls["search"], q=keyword, start=start, count=count, _ts=ts) - def movie_search(self, keyword, start=0, count=20, + def imdbid(self, imdbid: str, + ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + IMDBID搜索 + """ + return self.__post(self._urls["imdbid"] % imdbid, _ts=ts) + + def movie_search(self, keyword: str, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 电影搜索 + """ return self.__invoke(self._urls["movie_search"], q=keyword, start=start, count=count, _ts=ts) - def tv_search(self, keyword, start=0, count=20, + def tv_search(self, keyword: str, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 电视搜索 + """ return self.__invoke(self._urls["tv_search"], q=keyword, start=start, count=count, _ts=ts) - def book_search(self, keyword, start=0, count=20, + def book_search(self, keyword: str, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 书籍搜索 + """ return self.__invoke(self._urls["book_search"], q=keyword, start=start, count=count, _ts=ts) - def group_search(self, keyword, start=0, count=20, + def group_search(self, keyword: str, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 小组搜索 + """ return self.__invoke(self._urls["group_search"], q=keyword, start=start, count=count, _ts=ts) - def movie_showing(self, start=0, count=20, + def movie_showing(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 正在热映 + """ return self.__invoke(self._urls["movie_showing"], start=start, count=count, _ts=ts) - def movie_soon(self, start=0, count=20, + def movie_soon(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 即将上映 + """ return self.__invoke(self._urls["movie_soon"], start=start, count=count, _ts=ts) - def movie_hot_gaia(self, start=0, count=20, + def movie_hot_gaia(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 热门电影 + """ return self.__invoke(self._urls["movie_hot_gaia"], start=start, count=count, _ts=ts) - def tv_hot(self, start=0, count=20, + def tv_hot(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 热门剧集 + """ return self.__invoke(self._urls["tv_hot"], start=start, count=count, _ts=ts) - def tv_animation(self, start=0, count=20, + def tv_animation(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 动画 + """ return self.__invoke(self._urls["tv_animation"], start=start, count=count, _ts=ts) - def tv_variety_show(self, start=0, count=20, + def tv_variety_show(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 综艺 + """ return self.__invoke(self._urls["tv_variety_show"], start=start, count=count, _ts=ts) - def tv_rank_list(self, start=0, count=20, + def tv_rank_list(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 电视剧排行榜 + """ return self.__invoke(self._urls["tv_rank_list"], start=start, count=count, _ts=ts) - def show_hot(self, start=0, count=20, + def show_hot(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 综艺热门 + """ return self.__invoke(self._urls["show_hot"], start=start, count=count, _ts=ts) - def movie_detail(self, subject_id): + def movie_detail(self, subject_id: str): + """ + 电影详情 + """ return self.__invoke(self._urls["movie_detail"] + subject_id) - def movie_celebrities(self, subject_id): + def movie_celebrities(self, subject_id: str): + """ + 电影演职员 + """ return self.__invoke(self._urls["movie_celebrities"] % subject_id) - def tv_detail(self, subject_id): + def tv_detail(self, subject_id: str): + """ + 电视剧详情 + """ return self.__invoke(self._urls["tv_detail"] + subject_id) - def tv_celebrities(self, subject_id): + def tv_celebrities(self, subject_id: str): + """ + 电视剧演职员 + """ return self.__invoke(self._urls["tv_celebrities"] % subject_id) - def book_detail(self, subject_id): + def book_detail(self, subject_id: str): + """ + 书籍详情 + """ return self.__invoke(self._urls["book_detail"] + subject_id) - def movie_top250(self, start=0, count=20, + def movie_top250(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 电影TOP250 + """ return self.__invoke(self._urls["movie_top250"], start=start, count=count, _ts=ts) - def movie_recommend(self, tags='', sort='R', start=0, count=20, + def movie_recommend(self, tags='', sort='R', start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 电影探索 + """ return self.__invoke(self._urls["movie_recommend"], tags=tags, sort=sort, start=start, count=count, _ts=ts) - def tv_recommend(self, tags='', sort='R', start=0, count=20, + def tv_recommend(self, tags='', sort='R', start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 电视剧探索 + """ return self.__invoke(self._urls["tv_recommend"], tags=tags, sort=sort, start=start, count=count, _ts=ts) - def tv_chinese_best_weekly(self, start=0, count=20, + def tv_chinese_best_weekly(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 华语口碑周榜 + """ return self.__invoke(self._urls["tv_chinese_best_weekly"], start=start, count=count, _ts=ts) - def tv_global_best_weekly(self, start=0, count=20, + def tv_global_best_weekly(self, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): + """ + 全球口碑周榜 + """ return self.__invoke(self._urls["tv_global_best_weekly"], start=start, count=count, _ts=ts) - def doulist_detail(self, subject_id): + def doulist_detail(self, subject_id: str): """ 豆列详情 :param subject_id: 豆列id """ return self.__invoke(self._urls["doulist"] + subject_id) - def doulist_items(self, subject_id, start=0, count=20, + def doulist_items(self, subject_id: str, start: int = 0, count: int = 20, ts=datetime.strftime(datetime.now(), '%Y%m%d')): """ 豆列列表 diff --git a/app/plugins/personmeta/__init__.py b/app/plugins/personmeta/__init__.py index 3be06683..debff1ff 100644 --- a/app/plugins/personmeta/__init__.py +++ b/app/plugins/personmeta/__init__.py @@ -587,6 +587,7 @@ class PersonMeta(_PluginBase): time.sleep(sleep_time) # 匹配豆瓣信息 doubaninfo = self.chain.match_doubaninfo(name=mediainfo.title, + imdbid=mediainfo.imdb_id, mtype=mediainfo.type.value, year=mediainfo.year, season=season)