Sync all projects

This commit is contained in:
github-actions[bot]
2026-07-07 13:23:44 +00:00
parent 4ff404cee2
commit 198ae4b459
5 changed files with 4449 additions and 4283 deletions
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import json, requests
from urllib.parse import quote
try:
from base.spider import Spider as BaseSpider
except Exception:
BaseSpider = object
class Spider(BaseSpider):
def getName(self): return "全球追更"
def init(self, extend=""):
self.key = "2894d9a1baf7812b451de03c801b0281"
self.ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
self.headers = {"User-Agent": self.ua}
self.img = "https://images.tmdb.org/t/p/w500"
self.apis = ["https://api.tmdb.org/3"]
self.platforms = [{"id":"domestic","name":"国内聚合","network":"2007|1419|1330|1605|1631"},{"id":"netflix","name":"Netflix","network":"213"},{"id":"hbo","name":"HBO Max","network":"49"},{"id":"disney","name":"Disney+","network":"2739"},{"id":"appletv","name":"Apple TV+","network":"2552"},{"id":"amazon","name":"Amazon Prime","network":"1024"},{"id":"hulu","name":"Hulu","network":"453"},{"id":"paramount","name":"Paramount+","network":"4330"}]
self.cache = {}
def _get(self, endpoint, params=None):
params = params or {}
params["api_key"] = self.key
for base in self.apis:
try:
r = requests.get(base + endpoint, params=params, headers=self.headers, timeout=15)
if r.status_code == 200: return r.json()
except Exception:
continue
return {}
def _today(self):
import datetime
return datetime.datetime.now().strftime("%Y-%m-%d")
def _filters(self):
return [{"key":"sort","name":"🔥 动态追踪","value":[{"n":"📅 追更模式","v":"next_episode"},{"n":"📆 今日播出","v":"daily_airing"},{"n":"🆕 最新上线","v":"first_air_date.desc"},{"n":"⭐ 综合热度","v":"popularity.desc"}]},{"key":"type","name":"📺 内容类型","value":[{"n":"🎥 电视剧集","v":"tv"},{"n":"🎬 电影作品","v":"movie"},{"n":"🌸 动漫动画","v":"anime"},{"n":"🎤 综艺节目","v":"variety"}]}]
def _pic(self, p): return self.img + p if p else ""
def _vod(self, item, typ, sort="popularity.desc"):
mid = str(item.get("id",""))
name = item.get("name") or item.get("title") or ""
remark = "" + str(round(float(item.get("vote_average") or 0), 1))
date = item.get("first_air_date") or item.get("release_date") or "1900-01-01"
ck = "info_" + typ + "_" + mid + "_" + sort
if ck in self.cache: return self.cache[ck]
if not any("\u4e00" <= x <= "\u9fff" for x in name):
d = self._get(("/movie/" if typ == "movie" else "/tv/") + mid, {"language":"zh-CN","append_to_response":"alternative_titles,external_ids"})
alt = ((d.get("alternative_titles") or {}).get("titles") or (d.get("alternative_titles") or {}).get("results") or [])
for t in alt:
if t.get("iso_3166_1") == "CN" and t.get("title"):
name = t.get("title")
break
if typ != "movie" and sort in ["next_episode","daily_airing","first_air_date.desc"]:
d = self._get("/tv/" + mid, {"language":"zh-CN"})
ep = d.get("next_episode_to_air") or d.get("last_episode_to_air")
if ep:
date = ep.get("air_date") or date
remark = ("🕒" if d.get("next_episode_to_air") else "") + date[5:] + " S" + str(ep.get("season_number") or 0).zfill(2) + "E" + str(ep.get("episode_number") or 0).zfill(2)
vod = {"vod_id":typ + ":" + mid, "vod_name":name, "vod_pic":self._pic(item.get("poster_path")), "vod_remarks":remark, "_date":date}
self.cache[ck] = vod
return vod
def homeContent(self, filter):
fs = self._filters()
return {"class":[{"type_id":p["id"],"type_name":p["name"]} for p in self.platforms], "filters":{p["id"]:fs for p in self.platforms}, "list":[]}
def categoryContent(self, tid, pg, filter, extend):
page = int(pg or 1)
p = next((x for x in self.platforms if x["id"] == tid), None)
if not p: return {"page":page,"pagecount":1,"limit":20,"total":0,"list":[]}
sort = (extend or {}).get("sort") or "popularity.desc"
typ = (extend or {}).get("type") or "tv"
media = "movie" if typ == "movie" else "tv"
endpoint = "/discover/movie" if media == "movie" else "/discover/tv"
base = {"language":"zh-CN","page":page,"sort_by":"popularity.desc" if sort in ["daily_airing","next_episode"] else sort}
if typ == "anime": base["with_genres"] = "16"
if typ == "variety": base["with_genres"] = "10764|10767"
if sort == "daily_airing":
base["air_date.gte"] = self._today()
base["air_date.lte"] = self._today()
items, seen = [], set()
for net in p["network"].split("|"):
q = dict(base)
q["with_networks"] = net
data = self._get(endpoint, q)
for i in data.get("results", []):
mid = str(i.get("id",""))
if mid and mid not in seen:
seen.add(mid)
items.append(i)
if sort in ["next_episode","daily_airing","first_air_date.desc"]: items.sort(key=lambda x:x.get("first_air_date") or x.get("release_date") or "", reverse=True)
else: items.sort(key=lambda x:float(x.get("popularity") or 0), reverse=True)
return {"page":page,"pagecount":100,"limit":20,"total":2000,"list":[self._vod(i, media, sort) for i in items[:20]]}
def detailContent(self, ids):
raw = ids[0] if isinstance(ids, list) else ids
arr = str(raw).split(":", 1)
typ, mid = (arr[0], arr[1]) if len(arr) == 2 else ("tv", str(raw))
data = self._get(("/movie/" if typ == "movie" else "/tv/") + mid, {"language":"zh-CN","append_to_response":"credits"})
if not data and typ != "movie":
typ = "movie"
data = self._get("/movie/" + mid, {"language":"zh-CN","append_to_response":"credits"})
if not data: return {"list":[]}
name = data.get("name") or data.get("title") or ""
year = (data.get("release_date") or data.get("first_air_date") or "")[:4]
area = ", ".join([c.get("name","") for c in data.get("production_countries", [])])
actor = ", ".join([c.get("name","") for c in (data.get("credits") or {}).get("cast", [])[:8]])
director = ", ".join([c.get("name","") for c in (data.get("credits") or {}).get("crew", []) if c.get("job") == "Director"][:3])
return {"list":[{"vod_id":typ + ":" + mid,"vod_name":name,"vod_pic":self._pic(data.get("poster_path")),"type_name":"电影" if typ == "movie" else "电视剧","vod_year":year,"vod_area":area,"vod_remarks":"电影" if typ == "movie" else ("已完结" if data.get("status") == "Ended" else "连载中"),"vod_actor":actor,"vod_director":director,"vod_content":data.get("overview") or "暂无剧情简介","vod_play_from":"其他源","vod_play_url":"播放$" + quote(name)}]}
def searchContent(self, key, quick, pg="1"):
page = int(pg or 1)
data = self._get("/search/multi", {"query":key,"page":page,"language":"zh-CN"})
return {"list":[{"vod_id":i.get("media_type","tv") + ":" + str(i.get("id","")),"vod_name":i.get("title") or i.get("name") or key,"vod_pic":self._pic(i.get("poster_path")),"vod_remarks":"电影" if i.get("media_type") == "movie" else "剧集","goSearch":True} for i in data.get("results", []) if i.get("media_type") in ["movie","tv"]],"page":page,"pagecount":data.get("total_pages",1),"total":data.get("total_results",0)}
def playerContent(self, flag, id, vipFlags):
return {"parse":1,"url":id}