Files
MoviePilot/app/modules/bangumi/bangumi.py
景大侠 4c3d47f1f0 feat(cache): 同步/异步函数可共享缓存
- 缓存键支持自定义命名,使异步与同步函数可共享缓存结果
- 内存缓存改为类变量,实现多个cache装饰器共享同一缓存空间
- 重构AsyncMemoryBackend,减少重复代码
- 补齐部分模块的缓存清理功能
2026-02-10 18:46:49 +08:00

312 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime
from typing import Optional
import requests
from app.core.cache import cached
from app.core.config import settings
from app.utils.http import RequestUtils, AsyncRequestUtils
class BangumiApi(object):
"""
https://bangumi.github.io/api/
"""
_urls = {
"discover": "v0/subjects",
"search": "search/subjects/%s?type=2",
"calendar": "calendar",
"detail": "v0/subjects/%s",
"credits": "v0/subjects/%s/persons",
"subjects": "v0/subjects/%s/subjects",
"characters": "v0/subjects/%s/characters",
"person_detail": "v0/persons/%s",
"person_credits": "v0/persons/%s/subjects",
}
_base_url = "https://api.bgm.tv/"
def __init__(self):
self._session = requests.Session()
self._req = RequestUtils(ua=settings.NORMAL_USER_AGENT, session=self._session)
self._async_req = AsyncRequestUtils(ua=settings.NORMAL_USER_AGENT)
@cached(maxsize=settings.CONF.bangumi, ttl=settings.CONF.meta, shared_key="get")
def __invoke(self, url, key: Optional[str] = None, **kwargs):
req_url = self._base_url + url
params = {}
if kwargs:
params.update(kwargs)
resp = self._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
@cached(maxsize=settings.CONF.bangumi, ttl=settings.CONF.meta, shared_key="get")
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):
"""
搜索媒体信息
"""
result = self.__invoke("search/subject/%s" % name)
if result:
return result.get("list")
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):
"""
获取每日放送返回items
"""
"""
[
{
"weekday": {
"en": "Mon",
"cn": "星期一",
"ja": "月耀日",
"id": 1
},
"items": [
{
"id": 350235,
"url": "http://bgm.tv/subject/350235",
"type": 2,
"name": "月が導く異世界道中 第二幕",
"name_cn": "月光下的异世界之旅 第二幕",
"summary": "",
"air_date": "2024-01-08",
"air_weekday": 1,
"rating": {
"total": 257,
"count": {
"1": 1,
"2": 1,
"3": 4,
"4": 15,
"5": 51,
"6": 111,
"7": 49,
"8": 13,
"9": 5,
"10": 7
},
"score": 6.1
},
"rank": 6125,
"images": {
"large": "http://lain.bgm.tv/pic/cover/l/3c/a5/350235_A0USf.jpg",
"common": "http://lain.bgm.tv/pic/cover/c/3c/a5/350235_A0USf.jpg",
"medium": "http://lain.bgm.tv/pic/cover/m/3c/a5/350235_A0USf.jpg",
"small": "http://lain.bgm.tv/pic/cover/s/3c/a5/350235_A0USf.jpg",
"grid": "http://lain.bgm.tv/pic/cover/g/3c/a5/350235_A0USf.jpg"
},
"collection": {
"doing": 920
}
},
{
"id": 358561,
"url": "http://bgm.tv/subject/358561",
"type": 2,
"name": "大宇宙时代",
"name_cn": "大宇宙时代",
"summary": "",
"air_date": "2024-01-22",
"air_weekday": 1,
"rating": {
"total": 2,
"count": {
"1": 0,
"2": 0,
"3": 0,
"4": 0,
"5": 1,
"6": 1,
"7": 0,
"8": 0,
"9": 0,
"10": 0
},
"score": 5.5
},
"images": {
"large": "http://lain.bgm.tv/pic/cover/l/71/66/358561_UzsLu.jpg",
"common": "http://lain.bgm.tv/pic/cover/c/71/66/358561_UzsLu.jpg",
"medium": "http://lain.bgm.tv/pic/cover/m/71/66/358561_UzsLu.jpg",
"small": "http://lain.bgm.tv/pic/cover/s/71/66/358561_UzsLu.jpg",
"grid": "http://lain.bgm.tv/pic/cover/g/71/66/358561_UzsLu.jpg"
},
"collection": {
"doing": 9
}
}
]
}
]
"""
ret_list = []
result = self.__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
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):
"""
获取番剧详情
"""
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):
"""
获取番剧人物
"""
ret_list = []
result = self.__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
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):
"""
获取关联条目信息
"""
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):
"""
获取人物详细信息
"""
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):
"""
获取人物参演作品
"""
ret_list = []
result = self.__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
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):
"""
发现
"""
return self.__invoke(self._urls["discover"],
key="data",
_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 clear_cache(self):
"""
清除缓存
"""
self.__invoke.cache_clear()
def close(self):
if self._session:
self._session.close()