Unify menu labels, centralize hoster URL normalization, and add auto-update toggle
This commit is contained in:
@@ -833,20 +833,55 @@ class AniworldPlugin(BasisPlugin):
|
||||
merged_poster = (poster or old_poster or "").strip()
|
||||
self._title_meta[title] = (merged_plot, merged_poster)
|
||||
|
||||
def _extract_series_metadata(self, soup: BeautifulSoupT) -> tuple[str, str]:
|
||||
@staticmethod
|
||||
def _is_series_image_url(url: str) -> bool:
|
||||
value = (url or "").strip().casefold()
|
||||
if not value:
|
||||
return False
|
||||
blocked = (
|
||||
"/public/img/facebook",
|
||||
"/public/img/logo",
|
||||
"aniworld-logo",
|
||||
"favicon",
|
||||
"/public/img/german.svg",
|
||||
"/public/img/japanese-",
|
||||
)
|
||||
return not any(marker in value for marker in blocked)
|
||||
|
||||
@staticmethod
|
||||
def _extract_style_url(style_value: str) -> str:
|
||||
style_value = (style_value or "").strip()
|
||||
if not style_value:
|
||||
return ""
|
||||
match = re.search(r"url\((['\"]?)(.*?)\1\)", style_value, flags=re.IGNORECASE)
|
||||
if not match:
|
||||
return ""
|
||||
return (match.group(2) or "").strip()
|
||||
|
||||
def _extract_series_metadata(self, soup: BeautifulSoupT) -> tuple[str, str, str]:
|
||||
if not soup:
|
||||
return "", ""
|
||||
return "", "", ""
|
||||
plot = ""
|
||||
poster = ""
|
||||
fanart = ""
|
||||
|
||||
for selector in ("meta[property='og:description']", "meta[name='description']"):
|
||||
node = soup.select_one(selector)
|
||||
if node is None:
|
||||
continue
|
||||
content = (node.get("content") or "").strip()
|
||||
if content:
|
||||
plot = content
|
||||
break
|
||||
root = soup.select_one("#series") or soup
|
||||
|
||||
description_node = root.select_one("p.seri_des")
|
||||
if description_node is not None:
|
||||
full_text = (description_node.get("data-full-description") or "").strip()
|
||||
short_text = (description_node.get_text(" ", strip=True) or "").strip()
|
||||
plot = full_text or short_text
|
||||
|
||||
if not plot:
|
||||
for selector in ("meta[property='og:description']", "meta[name='description']"):
|
||||
node = soup.select_one(selector)
|
||||
if node is None:
|
||||
continue
|
||||
content = (node.get("content") or "").strip()
|
||||
if content:
|
||||
plot = content
|
||||
break
|
||||
if not plot:
|
||||
for selector in (".series-description", ".seri_des", ".description", "article p"):
|
||||
node = soup.select_one(selector)
|
||||
@@ -857,25 +892,61 @@ class AniworldPlugin(BasisPlugin):
|
||||
plot = text
|
||||
break
|
||||
|
||||
for selector in ("meta[property='og:image']", "meta[name='twitter:image']"):
|
||||
node = soup.select_one(selector)
|
||||
if node is None:
|
||||
continue
|
||||
content = (node.get("content") or "").strip()
|
||||
if content:
|
||||
poster = _absolute_url(content)
|
||||
break
|
||||
cover = root.select_one("div.seriesCoverBox img[itemprop='image'], div.seriesCoverBox img")
|
||||
if cover is not None:
|
||||
for attr in ("data-src", "src"):
|
||||
value = (cover.get(attr) or "").strip()
|
||||
if value:
|
||||
candidate = _absolute_url(value)
|
||||
if self._is_series_image_url(candidate):
|
||||
poster = candidate
|
||||
break
|
||||
|
||||
if not poster:
|
||||
for selector in ("img.seriesCoverBox", ".seriesCoverBox img", "img[alt][src]"):
|
||||
for selector in ("meta[property='og:image']", "meta[name='twitter:image']"):
|
||||
node = soup.select_one(selector)
|
||||
if node is None:
|
||||
continue
|
||||
content = (node.get("content") or "").strip()
|
||||
if content:
|
||||
candidate = _absolute_url(content)
|
||||
if self._is_series_image_url(candidate):
|
||||
poster = candidate
|
||||
break
|
||||
if not poster:
|
||||
for selector in ("img.seriesCoverBox", ".seriesCoverBox img"):
|
||||
image = soup.select_one(selector)
|
||||
if image is None:
|
||||
continue
|
||||
value = (image.get("data-src") or image.get("src") or "").strip()
|
||||
if value:
|
||||
poster = _absolute_url(value)
|
||||
break
|
||||
candidate = _absolute_url(value)
|
||||
if self._is_series_image_url(candidate):
|
||||
poster = candidate
|
||||
break
|
||||
|
||||
return plot, poster
|
||||
backdrop_node = root.select_one("section.title .backdrop, .SeriesSection .backdrop, .backdrop")
|
||||
if backdrop_node is not None:
|
||||
raw_style = (backdrop_node.get("style") or "").strip()
|
||||
style_url = self._extract_style_url(raw_style)
|
||||
if style_url:
|
||||
candidate = _absolute_url(style_url)
|
||||
if self._is_series_image_url(candidate):
|
||||
fanart = candidate
|
||||
|
||||
if not fanart:
|
||||
for selector in ("meta[property='og:image']",):
|
||||
node = soup.select_one(selector)
|
||||
if node is None:
|
||||
continue
|
||||
content = (node.get("content") or "").strip()
|
||||
if content:
|
||||
candidate = _absolute_url(content)
|
||||
if self._is_series_image_url(candidate):
|
||||
fanart = candidate
|
||||
break
|
||||
|
||||
return plot, poster, fanart
|
||||
|
||||
@staticmethod
|
||||
def _season_links_cache_name(series_url: str) -> str:
|
||||
@@ -1031,14 +1102,17 @@ class AniworldPlugin(BasisPlugin):
|
||||
|
||||
try:
|
||||
soup = _get_soup(series.url, session=get_requests_session("aniworld", headers=HEADERS))
|
||||
plot, poster = self._extract_series_metadata(soup)
|
||||
plot, poster, fanart = self._extract_series_metadata(soup)
|
||||
except Exception:
|
||||
plot, poster = "", ""
|
||||
plot, poster, fanart = "", "", ""
|
||||
|
||||
if plot:
|
||||
info["plot"] = plot
|
||||
if poster:
|
||||
art = {"thumb": poster, "poster": poster}
|
||||
if fanart:
|
||||
art["fanart"] = fanart
|
||||
art["landscape"] = fanart
|
||||
self._store_title_meta(title, plot=info.get("plot", ""), poster=poster)
|
||||
return info, art, None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user