为Bangumi和Douban模块添加异步API支持

This commit is contained in:
jxxghp
2025-07-30 22:18:11 +08:00
parent 71f7bc7b1b
commit 232759829e
4 changed files with 755 additions and 2 deletions
+116
View File
@@ -82,6 +82,29 @@ class BangumiModule(_ModuleBase):
return None return None
async def async_recognize_media(self, bangumiid: int = None,
**kwargs) -> Optional[MediaInfo]:
"""
识别媒体信息(异步版本)
:param bangumiid: 识别的Bangumi ID
:return: 识别的媒体信息,包括剧集信息
"""
if not bangumiid:
return None
# 直接查询详情
info = await self.async_bangumi_info(bangumiid=bangumiid)
if info:
# 赋值TMDB信息并返回
mediainfo = MediaInfo(bangumi_info=info)
logger.info(f"{bangumiid} Bangumi识别结果:{mediainfo.type.value} "
f"{mediainfo.title_year}")
return mediainfo
else:
logger.info(f"{bangumiid} 未匹配到Bangumi媒体信息")
return None
def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]: def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]:
""" """
搜索媒体信息 搜索媒体信息
@@ -99,6 +122,23 @@ class BangumiModule(_ModuleBase):
or meta.name.lower() in str(info.get("name_cn")).lower()] or meta.name.lower() in str(info.get("name_cn")).lower()]
return [] return []
async def async_search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息(异步版本)
:param meta: 识别的元数据
:reutrn: 媒体信息
"""
if settings.SEARCH_SOURCE and "bangumi" not in settings.SEARCH_SOURCE:
return None
if not meta.name:
return []
infos = await self.bangumiapi.async_search(meta.name)
if infos:
return [MediaInfo(bangumi_info=info) for info in infos
if meta.name.lower() in str(info.get("name")).lower()
or meta.name.lower() in str(info.get("name_cn")).lower()]
return []
def bangumi_info(self, bangumiid: int) -> Optional[dict]: def bangumi_info(self, bangumiid: int) -> Optional[dict]:
""" """
获取Bangumi信息 获取Bangumi信息
@@ -110,6 +150,17 @@ class BangumiModule(_ModuleBase):
logger.info(f"开始获取Bangumi信息:{bangumiid} ...") logger.info(f"开始获取Bangumi信息:{bangumiid} ...")
return self.bangumiapi.detail(bangumiid) return self.bangumiapi.detail(bangumiid)
async def async_bangumi_info(self, bangumiid: int) -> Optional[dict]:
"""
获取Bangumi信息(异步版本)
:param bangumiid: BangumiID
:return: Bangumi信息
"""
if not bangumiid:
return None
logger.info(f"开始获取Bangumi信息:{bangumiid} ...")
return await self.bangumiapi.async_detail(bangumiid)
def bangumi_calendar(self) -> Optional[List[MediaInfo]]: def bangumi_calendar(self) -> Optional[List[MediaInfo]]:
""" """
获取Bangumi每日放送 获取Bangumi每日放送
@@ -119,6 +170,15 @@ class BangumiModule(_ModuleBase):
return [MediaInfo(bangumi_info=info) for info in infos] return [MediaInfo(bangumi_info=info) for info in infos]
return [] return []
async def async_bangumi_calendar(self) -> Optional[List[MediaInfo]]:
"""
获取Bangumi每日放送(异步版本)
"""
infos = await self.bangumiapi.async_calendar()
if infos:
return [MediaInfo(bangumi_info=info) for info in infos]
return []
def bangumi_credits(self, bangumiid: int) -> List[schemas.MediaPerson]: def bangumi_credits(self, bangumiid: int) -> List[schemas.MediaPerson]:
""" """
根据TMDBID查询电影演职员表 根据TMDBID查询电影演职员表
@@ -129,6 +189,16 @@ class BangumiModule(_ModuleBase):
return [schemas.MediaPerson(source='bangumi', **person) for person in persons] return [schemas.MediaPerson(source='bangumi', **person) for person in persons]
return [] return []
async def async_bangumi_credits(self, bangumiid: int) -> List[schemas.MediaPerson]:
"""
根据TMDBID查询电影演职员表(异步版本)
:param bangumiid: BangumiID
"""
persons = await self.bangumiapi.async_credits(bangumiid)
if persons:
return [schemas.MediaPerson(source='bangumi', **person) for person in persons]
return []
def bangumi_recommend(self, bangumiid: int) -> List[MediaInfo]: def bangumi_recommend(self, bangumiid: int) -> List[MediaInfo]:
""" """
根据BangumiID查询推荐电影 根据BangumiID查询推荐电影
@@ -139,6 +209,16 @@ class BangumiModule(_ModuleBase):
return [MediaInfo(bangumi_info=subject) for subject in subjects] return [MediaInfo(bangumi_info=subject) for subject in subjects]
return [] return []
async def async_bangumi_recommend(self, bangumiid: int) -> List[MediaInfo]:
"""
根据BangumiID查询推荐电影(异步版本)
:param bangumiid: BangumiID
"""
subjects = await self.bangumiapi.async_subjects(bangumiid)
if subjects:
return [MediaInfo(bangumi_info=subject) for subject in subjects]
return []
def bangumi_person_detail(self, person_id: int) -> Optional[schemas.MediaPerson]: def bangumi_person_detail(self, person_id: int) -> Optional[schemas.MediaPerson]:
""" """
获取人物详细信息 获取人物详细信息
@@ -156,6 +236,23 @@ class BangumiModule(_ModuleBase):
}) })
return None return None
async def async_bangumi_person_detail(self, person_id: int) -> Optional[schemas.MediaPerson]:
"""
获取人物详细信息(异步版本)
:param person_id: 豆瓣人物ID
"""
personinfo = await self.bangumiapi.async_person_detail(person_id)
if personinfo:
return schemas.MediaPerson(source='bangumi', **{
"id": personinfo.get("id"),
"name": personinfo.get("name"),
"images": personinfo.get("images"),
"biography": personinfo.get("summary"),
"birthday": personinfo.get("birth_day"),
"gender": personinfo.get("gender")
})
return None
def bangumi_person_credits(self, person_id: int) -> List[MediaInfo]: def bangumi_person_credits(self, person_id: int) -> List[MediaInfo]:
""" """
根据TMDBID查询人物参演作品 根据TMDBID查询人物参演作品
@@ -166,6 +263,16 @@ class BangumiModule(_ModuleBase):
return [MediaInfo(bangumi_info=credit) for credit in credits_info] return [MediaInfo(bangumi_info=credit) for credit in credits_info]
return [] return []
async def async_bangumi_person_credits(self, person_id: int) -> List[MediaInfo]:
"""
根据TMDBID查询人物参演作品(异步版本)
:param person_id: 人物ID
"""
credits_info = await self.bangumiapi.async_person_credits(person_id=person_id)
if credits_info:
return [MediaInfo(bangumi_info=credit) for credit in credits_info]
return []
def bangumi_discover(self, **kwargs) -> Optional[List[MediaInfo]]: def bangumi_discover(self, **kwargs) -> Optional[List[MediaInfo]]:
""" """
发现Bangumi番剧 发现Bangumi番剧
@@ -174,3 +281,12 @@ class BangumiModule(_ModuleBase):
if infos: if infos:
return [MediaInfo(bangumi_info=info) for info in infos] return [MediaInfo(bangumi_info=info) for info in infos]
return [] return []
async def async_bangumi_discover(self, **kwargs) -> Optional[List[MediaInfo]]:
"""
发现Bangumi番剧(异步版本)
"""
infos = await self.bangumiapi.async_discover(**kwargs)
if infos:
return [MediaInfo(bangumi_info=info) for info in infos]
return []
+94 -1
View File
@@ -5,7 +5,7 @@ import requests
from app.core.cache import cached from app.core.cache import cached
from app.core.config import settings from app.core.config import settings
from app.utils.http import RequestUtils from app.utils.http import RequestUtils, AsyncRequestUtils
class BangumiApi(object): class BangumiApi(object):
@@ -29,6 +29,7 @@ class BangumiApi(object):
def __init__(self): def __init__(self):
self._session = requests.Session() self._session = requests.Session()
self._req = RequestUtils(session=self._session) self._req = RequestUtils(session=self._session)
self._async_req = AsyncRequestUtils()
@cached(maxsize=settings.CONF.bangumi, ttl=settings.CONF.meta) @cached(maxsize=settings.CONF.bangumi, ttl=settings.CONF.meta)
def __invoke(self, url, key: Optional[str] = None, **kwargs): def __invoke(self, url, key: Optional[str] = None, **kwargs):
@@ -46,6 +47,22 @@ class BangumiApi(object):
print(e) print(e)
return None return None
@cached(maxsize=settings.CONF.bangumi, ttl=settings.CONF.meta)
async def __async_invoke(self, url, key: Optional[str] = None, **kwargs):
req_url = self._base_url + url
params = {}
if kwargs:
params.update(kwargs)
resp = await self._async_req.get_res(url=req_url, params=params)
try:
if not resp:
return None
result = resp.json()
return result.get(key) if key else result
except Exception as e:
print(e)
return None
def search(self, name): def search(self, name):
""" """
搜索媒体信息 搜索媒体信息
@@ -55,6 +72,15 @@ class BangumiApi(object):
return result.get("list") return result.get("list")
return [] return []
async def async_search(self, name):
"""
搜索媒体信息(异步版本)
"""
result = await self.__async_invoke("search/subject/%s" % name)
if result:
return result.get("list")
return []
def calendar(self): def calendar(self):
""" """
获取每日放送,返回items 获取每日放送,返回items
@@ -153,12 +179,29 @@ class BangumiApi(object):
ret_list.extend(item.get("items") or []) ret_list.extend(item.get("items") or [])
return ret_list return ret_list
async def async_calendar(self):
"""
获取每日放送,返回items(异步版本)
"""
ret_list = []
result = await self.__async_invoke(self._urls["calendar"], _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
if result:
for item in result:
ret_list.extend(item.get("items") or [])
return ret_list
def detail(self, bid: int): def detail(self, bid: int):
""" """
获取番剧详情 获取番剧详情
""" """
return self.__invoke(self._urls["detail"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d')) return self.__invoke(self._urls["detail"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
async def async_detail(self, bid: int):
"""
获取番剧详情(异步版本)
"""
return await self.__async_invoke(self._urls["detail"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
def credits(self, bid: int): def credits(self, bid: int):
""" """
获取番剧人物 获取番剧人物
@@ -175,18 +218,48 @@ class BangumiApi(object):
ret_list.append(actor_info) ret_list.append(actor_info)
return ret_list return ret_list
async def async_credits(self, bid: int):
"""
获取番剧人物(异步版本)
"""
ret_list = []
result = await self.__async_invoke(self._urls["characters"] % bid,
_ts=datetime.strftime(datetime.now(), '%Y%m%d'))
if result:
for item in result:
character_id = item.get("id")
actors = item.get("actors")
if character_id and actors and actors[0]:
actor_info = actors[0]
actor_info.update({'career': [item.get('name')]})
ret_list.append(actor_info)
return ret_list
def subjects(self, bid: int): def subjects(self, bid: int):
""" """
获取关联条目信息 获取关联条目信息
""" """
return self.__invoke(self._urls["subjects"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d')) return self.__invoke(self._urls["subjects"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
async def async_subjects(self, bid: int):
"""
获取关联条目信息(异步版本)
"""
return await self.__async_invoke(self._urls["subjects"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
def person_detail(self, person_id: int): def person_detail(self, person_id: int):
""" """
获取人物详细信息 获取人物详细信息
""" """
return self.__invoke(self._urls["person_detail"] % person_id, _ts=datetime.strftime(datetime.now(), '%Y%m%d')) return self.__invoke(self._urls["person_detail"] % person_id, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
async def async_person_detail(self, person_id: int):
"""
获取人物详细信息(异步版本)
"""
return await self.__async_invoke(self._urls["person_detail"] % person_id,
_ts=datetime.strftime(datetime.now(), '%Y%m%d'))
def person_credits(self, person_id: int): def person_credits(self, person_id: int):
""" """
获取人物参演作品 获取人物参演作品
@@ -199,6 +272,18 @@ class BangumiApi(object):
ret_list.append(item) ret_list.append(item)
return ret_list return ret_list
async def async_person_credits(self, person_id: int):
"""
获取人物参演作品(异步版本)
"""
ret_list = []
result = await self.__async_invoke(self._urls["person_credits"] % person_id,
_ts=datetime.strftime(datetime.now(), '%Y%m%d'))
if result:
for item in result:
ret_list.append(item)
return ret_list
def discover(self, **kwargs): def discover(self, **kwargs):
""" """
发现 发现
@@ -207,6 +292,14 @@ class BangumiApi(object):
key="data", key="data",
_ts=datetime.strftime(datetime.now(), '%Y%m%d'), **kwargs) _ts=datetime.strftime(datetime.now(), '%Y%m%d'), **kwargs)
async def async_discover(self, **kwargs):
"""
发现(异步版本)
"""
return await self.__async_invoke(self._urls["discover"],
key="data",
_ts=datetime.strftime(datetime.now(), '%Y%m%d'), **kwargs)
def close(self): def close(self):
if self._session: if self._session:
self._session.close() self._session.close()
+205
View File
@@ -485,6 +485,62 @@ class DoubanModule(_ModuleBase):
else: else:
return __douban_movie() or __douban_tv() return __douban_movie() or __douban_tv()
@rate_limit_exponential(source="douban_info")
async def async_douban_info(self, doubanid: str, mtype: MediaType = None, raise_exception: bool = True) -> Optional[
dict]:
"""
获取豆瓣信息(异步版本)
:param doubanid: 豆瓣ID
:param mtype: 媒体类型
:param raise_exception: 触发速率限制时是否抛出异常
:return: 豆瓣信息
"""
async def __async_douban_tv():
"""
获取豆瓣剧集信息(异步版本)
"""
info = await self.doubanapi.async_tv_detail(doubanid)
if info:
if "subject_ip_rate_limit" in info.get("msg", ""):
msg = f"触发豆瓣IP速率限制,错误信息:{info} ..."
logger.warn(msg)
raise APIRateLimitException(msg)
celebrities = await self.doubanapi.async_tv_celebrities(doubanid)
if celebrities:
info["directors"] = celebrities.get("directors")
info["actors"] = celebrities.get("actors")
return info
async def __async_douban_movie():
"""
获取豆瓣电影信息(异步版本)
"""
info = await self.doubanapi.async_movie_detail(doubanid)
if info:
if "subject_ip_rate_limit" in info.get("msg", ""):
msg = f"触发豆瓣IP速率限制,错误信息:{info} ..."
logger.warn(msg)
raise APIRateLimitException(msg)
celebrities = await self.doubanapi.async_movie_celebrities(doubanid)
if celebrities:
info["directors"] = celebrities.get("directors")
info["actors"] = celebrities.get("actors")
return info
if not doubanid:
return None
logger.info(f"开始获取豆瓣信息:{doubanid} ...")
if mtype == MediaType.TV:
return await __async_douban_tv()
elif mtype == MediaType.MOVIE:
return await __async_douban_movie()
else:
movie_result = await __async_douban_movie()
if movie_result:
return movie_result
return await __async_douban_tv()
def douban_discover(self, mtype: MediaType, sort: str, tags: str, def douban_discover(self, mtype: MediaType, sort: str, tags: str,
page: int = 1, count: int = 30) -> Optional[List[MediaInfo]]: page: int = 1, count: int = 30) -> Optional[List[MediaInfo]]:
""" """
@@ -513,6 +569,34 @@ class DoubanModule(_ModuleBase):
and "tv_large.jpg" not in media.poster_path] and "tv_large.jpg" not in media.poster_path]
return [] return []
async def async_douban_discover(self, mtype: MediaType, sort: str, tags: str,
page: int = 1, count: int = 30) -> Optional[List[MediaInfo]]:
"""
发现豆瓣电影、剧集(异步版本)
:param mtype: 媒体类型
:param sort: 排序方式
:param tags: 标签
:param page: 页码
:param count: 数量
:return: 媒体信息列表
"""
logger.info(f"开始发现豆瓣 {mtype.value} ...")
if mtype == MediaType.MOVIE:
infos = await self.doubanapi.async_movie_recommend(start=(page - 1) * count, count=count,
sort=sort, tags=tags)
else:
infos = await self.doubanapi.async_tv_recommend(start=(page - 1) * count, count=count,
sort=sort, tags=tags)
if infos and infos.get("items"):
medias = [MediaInfo(douban_info=info) for info in infos.get("items")]
return [media for media in medias if media.poster_path
and "movie_large.jpg" not in media.poster_path
and "tv_normal.png" not in media.poster_path
and "movie_large.jpg" not in media.poster_path
and "tv_normal.jpg" not in media.poster_path
and "tv_large.jpg" not in media.poster_path]
return []
def movie_showing(self, page: int = 1, count: int = 30) -> List[MediaInfo]: def movie_showing(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
""" """
获取正在上映的电影 获取正在上映的电影
@@ -523,6 +607,16 @@ class DoubanModule(_ModuleBase):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")] return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return [] return []
async def async_movie_showing(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
"""
获取正在上映的电影(异步版本)
"""
infos = await self.doubanapi.async_movie_showing(start=(page - 1) * count,
count=count)
if infos and infos.get("subject_collection_items"):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return []
def tv_weekly_chinese(self, page: int = 1, count: int = 30) -> List[MediaInfo]: def tv_weekly_chinese(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
""" """
获取豆瓣本周口碑国产剧 获取豆瓣本周口碑国产剧
@@ -533,6 +627,16 @@ class DoubanModule(_ModuleBase):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")] return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return [] return []
async def async_tv_weekly_chinese(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
"""
获取豆瓣本周口碑国产剧(异步版本)
"""
infos = await self.doubanapi.async_tv_chinese_best_weekly(start=(page - 1) * count,
count=count)
if infos:
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return []
def tv_weekly_global(self, page: int = 1, count: int = 30) -> List[MediaInfo]: def tv_weekly_global(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
""" """
获取豆瓣本周口碑外国剧 获取豆瓣本周口碑外国剧
@@ -543,6 +647,16 @@ class DoubanModule(_ModuleBase):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")] return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return [] return []
async def async_tv_weekly_global(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
"""
获取豆瓣本周口碑外国剧(异步版本)
"""
infos = await self.doubanapi.async_tv_global_best_weekly(start=(page - 1) * count,
count=count)
if infos and infos.get("subject_collection_items"):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return []
def tv_animation(self, page: int = 1, count: int = 30) -> List[MediaInfo]: def tv_animation(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
""" """
获取豆瓣动画剧 获取豆瓣动画剧
@@ -553,6 +667,16 @@ class DoubanModule(_ModuleBase):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")] return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return [] return []
async def async_tv_animation(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
"""
获取豆瓣动画剧(异步版本)
"""
infos = await self.doubanapi.async_tv_animation(start=(page - 1) * count,
count=count)
if infos and infos.get("subject_collection_items"):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return []
def movie_hot(self, page: int = 1, count: int = 30) -> List[MediaInfo]: def movie_hot(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
""" """
获取豆瓣热门电影 获取豆瓣热门电影
@@ -563,6 +687,16 @@ class DoubanModule(_ModuleBase):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")] return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return [] return []
async def async_movie_hot(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
"""
获取豆瓣热门电影(异步版本)
"""
infos = await self.doubanapi.async_movie_hot_gaia(start=(page - 1) * count,
count=count)
if infos and infos.get("subject_collection_items"):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return []
def tv_hot(self, page: int = 1, count: int = 30) -> List[MediaInfo]: def tv_hot(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
""" """
获取豆瓣热门剧集 获取豆瓣热门剧集
@@ -573,6 +707,16 @@ class DoubanModule(_ModuleBase):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")] return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return [] return []
async def async_tv_hot(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
"""
获取豆瓣热门剧集(异步版本)
"""
infos = await self.doubanapi.async_tv_hot(start=(page - 1) * count,
count=count)
if infos and infos.get("subject_collection_items"):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return []
def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]: def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]:
""" """
搜索媒体信息 搜索媒体信息
@@ -606,6 +750,39 @@ class DoubanModule(_ModuleBase):
media.season = meta.begin_season media.season = meta.begin_season
return ret_medias return ret_medias
async def async_search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]:
"""
搜索媒体信息(异步版本)
:param meta: 识别的元数据
:reutrn: 媒体信息
"""
if settings.SEARCH_SOURCE and "douban" not in settings.SEARCH_SOURCE:
return None
if not meta.name:
return []
result = await self.doubanapi.async_search(meta.name)
if not result or not result.get("items"):
return []
# 返回数据
ret_medias = []
for item_obj in result.get("items"):
if meta.type and meta.type != MediaType.UNKNOWN and meta.type.value != item_obj.get("type_name"):
continue
if item_obj.get("type_name") not in (MediaType.TV.value, MediaType.MOVIE.value):
continue
if meta.name not in item_obj.get("target", {}).get("title"):
continue
ret_medias.append(MediaInfo(douban_info=item_obj.get("target")))
# 将搜索词中的季写入标题中
if ret_medias and meta.begin_season:
# 小写数据转大写
season_str = cn2an.an2cn(meta.begin_season, "low")
for media in ret_medias:
if media.type == MediaType.TV:
media.title = f"{media.title}{season_str}"
media.season = meta.begin_season
return ret_medias
def search_persons(self, name: str) -> Optional[List[MediaPerson]]: def search_persons(self, name: str) -> Optional[List[MediaPerson]]:
""" """
搜索人物信息 搜索人物信息
@@ -624,6 +801,24 @@ class DoubanModule(_ModuleBase):
}) for item in result.get('items') if name in item.get('target', {}).get('title')] }) for item in result.get('items') if name in item.get('target', {}).get('title')]
return [] return []
async def async_search_persons(self, name: str) -> Optional[List[MediaPerson]]:
"""
搜索人物信息(异步版本)
"""
if not name:
return []
result = await self.doubanapi.async_person_search(keyword=name)
if result and result.get('items'):
return [MediaPerson(source='douban', **{
'id': item.get('target_id'),
'name': item.get('target', {}).get('title'),
'url': item.get('target', {}).get('url'),
'images': item.get('target', {}).get('cover', {}),
'avatar': (item.get('target', {}).get('cover_img', {}).get('url')
or '').replace("/l/public/", "/s/public/"),
}) for item in result.get('items') if name in item.get('target', {}).get('title')]
return []
@retry(Exception, 5, 3, 3, logger=logger) @retry(Exception, 5, 3, 3, logger=logger)
@rate_limit_exponential(source="match_doubaninfo") @rate_limit_exponential(source="match_doubaninfo")
def match_doubaninfo(self, name: str, imdbid: str = None, def match_doubaninfo(self, name: str, imdbid: str = None,
@@ -696,6 +891,16 @@ class DoubanModule(_ModuleBase):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")] return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return [] return []
async def async_movie_top250(self, page: int = 1, count: int = 30) -> List[MediaInfo]:
"""
获取豆瓣电影TOP250(异步版本)
"""
infos = await self.doubanapi.async_movie_top250(start=(page - 1) * count,
count=count)
if infos and infos.get("subject_collection_items"):
return [MediaInfo(douban_info=info) for info in infos.get("subject_collection_items")]
return []
def metadata_nfo(self, mediainfo: MediaInfo, season: int = None, **kwargs) -> Optional[str]: def metadata_nfo(self, mediainfo: MediaInfo, season: int = None, **kwargs) -> Optional[str]:
""" """
获取NFO文件内容文本 获取NFO文件内容文本
+340 -1
View File
@@ -11,7 +11,7 @@ import requests
from app.core.cache import cached from app.core.cache import cached
from app.core.config import settings from app.core.config import settings
from app.utils.http import RequestUtils from app.utils.http import RequestUtils, AsyncRequestUtils
from app.utils.singleton import WeakSingleton from app.utils.singleton import WeakSingleton
@@ -154,6 +154,7 @@ class DoubanApi(metaclass=WeakSingleton):
def __init__(self): def __init__(self):
self._session = requests.Session() self._session = requests.Session()
self._async_req = AsyncRequestUtils()
@classmethod @classmethod
def __sign(cls, url: str, ts: str, method='GET') -> str: def __sign(cls, url: str, ts: str, method='GET') -> str:
@@ -177,6 +178,13 @@ class DoubanApi(metaclass=WeakSingleton):
""" """
return self.__invoke(url, **kwargs) return self.__invoke(url, **kwargs)
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta)
async def __async_invoke_recommend(self, url: str, **kwargs) -> dict:
"""
推荐/发现类API(异步版本)
"""
return await self.__async_invoke(url, **kwargs)
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta) @cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta)
def __invoke_search(self, url: str, **kwargs) -> dict: def __invoke_search(self, url: str, **kwargs) -> dict:
""" """
@@ -184,6 +192,13 @@ class DoubanApi(metaclass=WeakSingleton):
""" """
return self.__invoke(url, **kwargs) return self.__invoke(url, **kwargs)
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta)
async def __async_invoke_search(self, url: str, **kwargs) -> dict:
"""
搜索类API(异步版本)
"""
return await self.__async_invoke(url, **kwargs)
def __invoke(self, url: str, **kwargs) -> dict: def __invoke(self, url: str, **kwargs) -> dict:
""" """
GET请求 GET请求
@@ -212,6 +227,32 @@ class DoubanApi(metaclass=WeakSingleton):
return resp.json() return resp.json()
return resp.json() if resp else {} return resp.json() if resp else {}
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta)
async def __async_invoke(self, url: str, **kwargs) -> dict:
"""
GET请求(异步版本)
"""
req_url = self._base_url + url
params: dict = {'apiKey': self._api_key}
if kwargs:
params.update(kwargs)
ts = params.pop(
'_ts',
datetime.strftime(datetime.now(), '%Y%m%d')
)
params.update({
'os_rom': 'android',
'apiKey': self._api_key,
'_ts': ts,
'_sig': self.__sign(url=req_url, ts=ts)
})
resp = await self._async_req.get_res(url=req_url, params=params)
if resp is not None and resp.status_code == 400 and "rate_limit" in resp.text:
return resp.json()
return resp.json() if resp else {}
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta) @cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta)
def __post(self, url: str, **kwargs) -> dict: def __post(self, url: str, **kwargs) -> dict:
""" """
@@ -241,6 +282,22 @@ class DoubanApi(metaclass=WeakSingleton):
return resp.json() return resp.json()
return resp.json() if resp else {} return resp.json() if resp else {}
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta)
async def __async_post(self, url: str, **kwargs) -> dict:
"""
POST请求(异步版本)
"""
req_url = self._api_url + url
params = {'apikey': self._api_key2}
if kwargs:
params.update(kwargs)
if '_ts' in params:
params.pop('_ts')
resp = await self._async_req.post_res(url=req_url, data=params)
if resp is not None and resp.status_code == 400 and "rate_limit" in resp.text:
return resp.json()
return resp.json() if resp else {}
def imdbid(self, imdbid: str, def imdbid(self, imdbid: str,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -248,6 +305,13 @@ class DoubanApi(metaclass=WeakSingleton):
""" """
return self.__post(self._urls["imdbid"] % imdbid, _ts=ts) return self.__post(self._urls["imdbid"] % imdbid, _ts=ts)
async def async_imdbid(self, imdbid: str,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
IMDBID搜索(异步版本)
"""
return await self.__async_post(self._urls["imdbid"] % imdbid, _ts=ts)
def search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20, def search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')) -> dict: ts=datetime.strftime(datetime.now(), '%Y%m%d')) -> dict:
""" """
@@ -256,6 +320,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["search"], q=keyword, return self.__invoke_search(self._urls["search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')) -> dict:
"""
关键字搜索(异步版本)
"""
return await self.__async_invoke_search(self._urls["search"], q=keyword,
start=start, count=count, _ts=ts)
def movie_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20, def movie_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -264,6 +336,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["movie_search"], q=keyword, return self.__invoke_search(self._urls["movie_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影搜索(异步版本)
"""
return await self.__async_invoke_search(self._urls["movie_search"], q=keyword,
start=start, count=count, _ts=ts)
def tv_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20, def tv_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -272,6 +352,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["tv_search"], q=keyword, return self.__invoke_search(self._urls["tv_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视搜索(异步版本)
"""
return await self.__async_invoke_search(self._urls["tv_search"], q=keyword,
start=start, count=count, _ts=ts)
def book_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20, def book_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -280,6 +368,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["book_search"], q=keyword, return self.__invoke_search(self._urls["book_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_book_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
书籍搜索(异步版本)
"""
return await self.__async_invoke_search(self._urls["book_search"], q=keyword,
start=start, count=count, _ts=ts)
def group_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20, def group_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -288,6 +384,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["group_search"], q=keyword, return self.__invoke_search(self._urls["group_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_group_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
小组搜索(异步版本)
"""
return await self.__async_invoke_search(self._urls["group_search"], q=keyword,
start=start, count=count, _ts=ts)
def person_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20, def person_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -296,6 +400,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["search_subject"], type="person", q=keyword, return self.__invoke_search(self._urls["search_subject"], type="person", q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_person_search(self, keyword: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
人物搜索(异步版本)
"""
return await self.__async_invoke_search(self._urls["search_subject"], type="person", q=keyword,
start=start, count=count, _ts=ts)
def movie_showing(self, start: Optional[int] = 0, count: Optional[int] = 20, def movie_showing(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -304,6 +416,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["movie_showing"], return self.__invoke_recommend(self._urls["movie_showing"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_showing(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
正在热映(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["movie_showing"],
start=start, count=count, _ts=ts)
def movie_soon(self, start: Optional[int] = 0, count: Optional[int] = 20, def movie_soon(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -312,6 +432,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["movie_soon"], return self.__invoke_recommend(self._urls["movie_soon"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_soon(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
即将上映(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["movie_soon"],
start=start, count=count, _ts=ts)
def movie_hot_gaia(self, start: Optional[int] = 0, count: Optional[int] = 20, def movie_hot_gaia(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -320,6 +448,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["movie_hot_gaia"], return self.__invoke_recommend(self._urls["movie_hot_gaia"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_hot_gaia(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
热门电影(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["movie_hot_gaia"],
start=start, count=count, _ts=ts)
def tv_hot(self, start: Optional[int] = 0, count: Optional[int] = 20, def tv_hot(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -328,6 +464,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_hot"], return self.__invoke_recommend(self._urls["tv_hot"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_hot(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
热门剧集(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["tv_hot"],
start=start, count=count, _ts=ts)
def tv_animation(self, start: Optional[int] = 0, count: Optional[int] = 20, def tv_animation(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -336,6 +480,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_animation"], return self.__invoke_recommend(self._urls["tv_animation"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_animation(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
动画(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["tv_animation"],
start=start, count=count, _ts=ts)
def tv_variety_show(self, start: Optional[int] = 0, count: Optional[int] = 20, def tv_variety_show(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -344,6 +496,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_variety_show"], return self.__invoke_recommend(self._urls["tv_variety_show"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_variety_show(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
综艺(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["tv_variety_show"],
start=start, count=count, _ts=ts)
def tv_rank_list(self, start: Optional[int] = 0, count: Optional[int] = 20, def tv_rank_list(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -352,6 +512,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_rank_list"], return self.__invoke_recommend(self._urls["tv_rank_list"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_rank_list(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视剧排行榜(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["tv_rank_list"],
start=start, count=count, _ts=ts)
def show_hot(self, start: Optional[int] = 0, count: Optional[int] = 20, def show_hot(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -360,36 +528,74 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["show_hot"], return self.__invoke_recommend(self._urls["show_hot"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_show_hot(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
综艺热门(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["show_hot"],
start=start, count=count, _ts=ts)
def movie_detail(self, subject_id: str): def movie_detail(self, subject_id: str):
""" """
电影详情 电影详情
""" """
return self.__invoke_search(self._urls["movie_detail"] + subject_id) return self.__invoke_search(self._urls["movie_detail"] + subject_id)
async def async_movie_detail(self, subject_id: str):
"""
电影详情(异步版本)
"""
return await self.__async_invoke_search(self._urls["movie_detail"] + subject_id)
def movie_celebrities(self, subject_id: str): def movie_celebrities(self, subject_id: str):
""" """
电影演职员 电影演职员
""" """
return self.__invoke_search(self._urls["movie_celebrities"] % subject_id) return self.__invoke_search(self._urls["movie_celebrities"] % subject_id)
async def async_movie_celebrities(self, subject_id: str):
"""
电影演职员(异步版本)
"""
return await self.__async_invoke_search(self._urls["movie_celebrities"] % subject_id)
def tv_detail(self, subject_id: str): def tv_detail(self, subject_id: str):
""" """
电视剧详情 电视剧详情
""" """
return self.__invoke_search(self._urls["tv_detail"] + subject_id) return self.__invoke_search(self._urls["tv_detail"] + subject_id)
async def async_tv_detail(self, subject_id: str):
"""
电视剧详情(异步版本)
"""
return await self.__async_invoke_search(self._urls["tv_detail"] + subject_id)
def tv_celebrities(self, subject_id: str): def tv_celebrities(self, subject_id: str):
""" """
电视剧演职员 电视剧演职员
""" """
return self.__invoke_search(self._urls["tv_celebrities"] % subject_id) return self.__invoke_search(self._urls["tv_celebrities"] % subject_id)
async def async_tv_celebrities(self, subject_id: str):
"""
电视剧演职员(异步版本)
"""
return await self.__async_invoke_search(self._urls["tv_celebrities"] % subject_id)
def book_detail(self, subject_id: str): def book_detail(self, subject_id: str):
""" """
书籍详情 书籍详情
""" """
return self.__invoke_search(self._urls["book_detail"] + subject_id) return self.__invoke_search(self._urls["book_detail"] + subject_id)
async def async_book_detail(self, subject_id: str):
"""
书籍详情(异步版本)
"""
return await self.__async_invoke_search(self._urls["book_detail"] + subject_id)
def movie_top250(self, start: Optional[int] = 0, count: Optional[int] = 20, def movie_top250(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -398,6 +604,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["movie_top250"], return self.__invoke_recommend(self._urls["movie_top250"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_top250(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影TOP250(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["movie_top250"],
start=start, count=count, _ts=ts)
def movie_recommend(self, tags='', sort='R', start: Optional[int] = 0, count: Optional[int] = 20, def movie_recommend(self, tags='', sort='R', start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -406,6 +620,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["movie_recommend"], tags=tags, sort=sort, return self.__invoke_recommend(self._urls["movie_recommend"], tags=tags, sort=sort,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_recommend(self, tags='', sort='R', start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影探索(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["movie_recommend"], tags=tags, sort=sort,
start=start, count=count, _ts=ts)
def tv_recommend(self, tags='', sort='R', start: Optional[int] = 0, count: Optional[int] = 20, def tv_recommend(self, tags='', sort='R', start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -414,6 +636,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_recommend"], tags=tags, sort=sort, return self.__invoke_recommend(self._urls["tv_recommend"], tags=tags, sort=sort,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_recommend(self, tags='', sort='R', start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视剧探索(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["tv_recommend"], tags=tags, sort=sort,
start=start, count=count, _ts=ts)
def tv_chinese_best_weekly(self, start: Optional[int] = 0, count: Optional[int] = 20, def tv_chinese_best_weekly(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -422,6 +652,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_chinese_best_weekly"], return self.__invoke_recommend(self._urls["tv_chinese_best_weekly"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_chinese_best_weekly(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
华语口碑周榜(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["tv_chinese_best_weekly"],
start=start, count=count, _ts=ts)
def tv_global_best_weekly(self, start: Optional[int] = 0, count: Optional[int] = 20, def tv_global_best_weekly(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -430,6 +668,14 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_global_best_weekly"], return self.__invoke_recommend(self._urls["tv_global_best_weekly"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_global_best_weekly(self, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
全球口碑周榜(异步版本)
"""
return await self.__async_invoke_recommend(self._urls["tv_global_best_weekly"],
start=start, count=count, _ts=ts)
def doulist_detail(self, subject_id: str): def doulist_detail(self, subject_id: str):
""" """
豆列详情 豆列详情
@@ -437,6 +683,13 @@ class DoubanApi(metaclass=WeakSingleton):
""" """
return self.__invoke_search(self._urls["doulist"] + subject_id) return self.__invoke_search(self._urls["doulist"] + subject_id)
async def async_doulist_detail(self, subject_id: str):
"""
豆列详情(异步版本)
:param subject_id: 豆列id
"""
return await self.__async_invoke_search(self._urls["doulist"] + subject_id)
def doulist_items(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20, def doulist_items(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -449,6 +702,18 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["doulist_items"] % subject_id, return self.__invoke_search(self._urls["doulist_items"] % subject_id,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_doulist_items(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
豆列列表(异步版本)
:param subject_id: 豆列id
:param start: 开始
:param count: 数量
:param ts: 时间戳
"""
return await self.__async_invoke_search(self._urls["doulist_items"] % subject_id,
start=start, count=count, _ts=ts)
def movie_recommendations(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20, def movie_recommendations(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -461,6 +726,18 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["movie_recommendations"] % subject_id, return self.__invoke_recommend(self._urls["movie_recommendations"] % subject_id,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_recommendations(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影推荐(异步版本)
:param subject_id: 电影id
:param start: 开始
:param count: 数量
:param ts: 时间戳
"""
return await self.__async_invoke_recommend(self._urls["movie_recommendations"] % subject_id,
start=start, count=count, _ts=ts)
def tv_recommendations(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20, def tv_recommendations(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -473,6 +750,18 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_recommend(self._urls["tv_recommendations"] % subject_id, return self.__invoke_recommend(self._urls["tv_recommendations"] % subject_id,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_recommendations(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视剧推荐(异步版本)
:param subject_id: 电视剧id
:param start: 开始
:param count: 数量
:param ts: 时间戳
"""
return await self.__async_invoke_recommend(self._urls["tv_recommendations"] % subject_id,
start=start, count=count, _ts=ts)
def movie_photos(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20, def movie_photos(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -485,6 +774,18 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["movie_photos"] % subject_id, return self.__invoke_search(self._urls["movie_photos"] % subject_id,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_movie_photos(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影剧照(异步版本)
:param subject_id: 电影id
:param start: 开始
:param count: 数量
:param ts: 时间戳
"""
return await self.__async_invoke_search(self._urls["movie_photos"] % subject_id,
start=start, count=count, _ts=ts)
def tv_photos(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20, def tv_photos(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
@@ -497,6 +798,18 @@ class DoubanApi(metaclass=WeakSingleton):
return self.__invoke_search(self._urls["tv_photos"] % subject_id, return self.__invoke_search(self._urls["tv_photos"] % subject_id,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_tv_photos(self, subject_id: str, start: Optional[int] = 0, count: Optional[int] = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视剧剧照(异步版本)
:param subject_id: 电视剧id
:param start: 开始
:param count: 数量
:param ts: 时间戳
"""
return await self.__async_invoke_search(self._urls["tv_photos"] % subject_id,
start=start, count=count, _ts=ts)
def person_detail(self, subject_id: int): def person_detail(self, subject_id: int):
""" """
用户详情 用户详情
@@ -505,6 +818,14 @@ class DoubanApi(metaclass=WeakSingleton):
""" """
return self.__invoke_search(self._urls["person_detail"] + str(subject_id)) return self.__invoke_search(self._urls["person_detail"] + str(subject_id))
async def async_person_detail(self, subject_id: int):
"""
用户详情(异步版本)
:param subject_id: 人物 id
:return:
"""
return await self.__async_invoke_search(self._urls["person_detail"] + str(subject_id))
def person_work(self, subject_id: int, start: Optional[int] = 0, count: Optional[int] = 20, def person_work(self, subject_id: int, start: Optional[int] = 0, count: Optional[int] = 20,
sort_by: Optional[str] = "time", sort_by: Optional[str] = "time",
collection_title: Optional[str] = "影视", collection_title: Optional[str] = "影视",
@@ -523,6 +844,24 @@ class DoubanApi(metaclass=WeakSingleton):
collection_title=collection_title, collection_title=collection_title,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
async def async_person_work(self, subject_id: int, start: Optional[int] = 0, count: Optional[int] = 20,
sort_by: Optional[str] = "time",
collection_title: Optional[str] = "影视",
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
用户作品集(异步版本)
:param subject_id: work_collection id
:param start: 开始页
:param count: 数量
:param sort_by: collection or time or vote
:param collection_title: 影视 or 图书 or 音乐
:param ts: 时间戳
:return:
"""
return await self.__async_invoke_search(self._urls["person_work"] % subject_id, sortby=sort_by,
collection_title=collection_title,
start=start, count=count, _ts=ts)
def clear_cache(self): def clear_cache(self):
""" """
清空LRU缓存 清空LRU缓存