mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-05-12 19:16:47 +08:00
feat: fix search, poster serving, and add hover overlay UI for cards
- Fix search store exports to match component expectations (inputValue, bangumiList, onSearch) and transform data to SearchResult format - Fix poster endpoint path check that incorrectly blocked all requests - Add resolvePosterUrl utility to handle both external URLs and local paths - Move tags into hover overlay on homepage cards and calendar cards - Show title and tags on poster hover with dark semi-transparent styling - Add downloader API, store, and page - Update backend to async patterns and uv migration changes - Remove .claude/settings.local.json from tracking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,13 +7,13 @@ logger = logging.getLogger(__name__)
|
||||
BGM_CALENDAR_URL = "https://api.bgm.tv/calendar"
|
||||
|
||||
|
||||
def fetch_bgm_calendar() -> list[dict]:
|
||||
async def fetch_bgm_calendar() -> list[dict]:
|
||||
"""Fetch the current season's broadcast calendar from Bangumi.tv API.
|
||||
|
||||
Returns a flat list of anime items with their air_weekday (0=Mon, ..., 6=Sun).
|
||||
"""
|
||||
with RequestContent() as req:
|
||||
data = req.get_json(BGM_CALENDAR_URL)
|
||||
async with RequestContent() as req:
|
||||
data = await req.get_json(BGM_CALENDAR_URL)
|
||||
|
||||
if not data:
|
||||
logger.warning("[BGM Calendar] Failed to fetch calendar data.")
|
||||
|
||||
@@ -5,10 +5,10 @@ def search_url(e):
|
||||
return f"https://api.bgm.tv/search/subject/{e}?responseGroup=large"
|
||||
|
||||
|
||||
def bgm_parser(title):
|
||||
async def bgm_parser(title):
|
||||
url = search_url(title)
|
||||
with RequestContent() as req:
|
||||
contents = req.get_json(url)
|
||||
async with RequestContent() as req:
|
||||
contents = await req.get_json(url)
|
||||
if contents:
|
||||
return contents[0]
|
||||
else:
|
||||
|
||||
@@ -7,10 +7,10 @@ from module.network import RequestContent
|
||||
from module.utils import save_image
|
||||
|
||||
|
||||
def mikan_parser(homepage: str):
|
||||
async def mikan_parser(homepage: str):
|
||||
root_path = parse_url(homepage).host
|
||||
with RequestContent() as req:
|
||||
content = req.get_html(homepage)
|
||||
async with RequestContent() as req:
|
||||
content = await req.get_html(homepage)
|
||||
soup = BeautifulSoup(content, "html.parser")
|
||||
poster_div = soup.find("div", {"class": "bangumi-poster"}).get("style")
|
||||
official_title = soup.select_one(
|
||||
@@ -20,7 +20,7 @@ def mikan_parser(homepage: str):
|
||||
if poster_div:
|
||||
poster_path = poster_div.split("url('")[1].split("')")[0]
|
||||
poster_path = poster_path.split("?")[0]
|
||||
img = req.get_content(f"https://{root_path}{poster_path}")
|
||||
img = await req.get_content(f"https://{root_path}{poster_path}")
|
||||
suffix = poster_path.split(".")[-1]
|
||||
poster_link = save_image(img, suffix)
|
||||
return poster_link, official_title
|
||||
@@ -28,5 +28,6 @@ def mikan_parser(homepage: str):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import asyncio
|
||||
homepage = "https://mikanani.me/Home/Episode/c89b3c6f0c1c0567a618f5288b853823c87a9862"
|
||||
print(mikan_parser(homepage))
|
||||
print(asyncio.run(mikan_parser(homepage)))
|
||||
|
||||
@@ -31,11 +31,11 @@ def info_url(e, key):
|
||||
return f"{TMDB_URL}/3/tv/{e}?api_key={TMDB_API}&language={LANGUAGE[key]}"
|
||||
|
||||
|
||||
def is_animation(tv_id, language) -> bool:
|
||||
async def is_animation(tv_id, language, req: RequestContent) -> bool:
|
||||
url_info = info_url(tv_id, language)
|
||||
with RequestContent() as req:
|
||||
type_id = req.get_json(url_info)["genres"]
|
||||
for type in type_id:
|
||||
type_id = await req.get_json(url_info)
|
||||
if type_id:
|
||||
for type in type_id.get("genres", []):
|
||||
if type.get("id") == 16:
|
||||
return True
|
||||
return False
|
||||
@@ -56,21 +56,27 @@ def get_season(seasons: list) -> tuple[int, str]:
|
||||
return len(ss), ss[-1].get("poster_path")
|
||||
|
||||
|
||||
def tmdb_parser(title, language, test: bool = False) -> TMDBInfo | None:
|
||||
with RequestContent() as req:
|
||||
async def tmdb_parser(title, language, test: bool = False) -> TMDBInfo | None:
|
||||
async with RequestContent() as req:
|
||||
url = search_url(title)
|
||||
contents = req.get_json(url).get("results")
|
||||
contents = await req.get_json(url)
|
||||
if not contents:
|
||||
return None
|
||||
contents = contents.get("results")
|
||||
if contents.__len__() == 0:
|
||||
url = search_url(title.replace(" ", ""))
|
||||
contents = req.get_json(url).get("results")
|
||||
contents_resp = await req.get_json(url)
|
||||
if not contents_resp:
|
||||
return None
|
||||
contents = contents_resp.get("results")
|
||||
# 判断动画
|
||||
if contents:
|
||||
for content in contents:
|
||||
id = content["id"]
|
||||
if is_animation(id, language):
|
||||
if await is_animation(id, language, req):
|
||||
break
|
||||
url_info = info_url(id, language)
|
||||
info_content = req.get_json(url_info)
|
||||
info_content = await req.get_json(url_info)
|
||||
season = [
|
||||
{
|
||||
"season": s.get("name"),
|
||||
@@ -87,7 +93,7 @@ def tmdb_parser(title, language, test: bool = False) -> TMDBInfo | None:
|
||||
year_number = info_content.get("first_air_date").split("-")[0]
|
||||
if poster_path:
|
||||
if not test:
|
||||
img = req.get_content(f"https://image.tmdb.org/t/p/w780{poster_path}")
|
||||
img = await req.get_content(f"https://image.tmdb.org/t/p/w780{poster_path}")
|
||||
poster_link = save_image(img, "jpg")
|
||||
else:
|
||||
poster_link = "https://image.tmdb.org/t/p/w780" + poster_path
|
||||
@@ -107,4 +113,5 @@ def tmdb_parser(title, language, test: bool = False) -> TMDBInfo | None:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(tmdb_parser("魔法禁书目录", "zh"))
|
||||
import asyncio
|
||||
print(asyncio.run(tmdb_parser("魔法禁书目录", "zh")))
|
||||
|
||||
Reference in New Issue
Block a user