diff --git a/wmf.txt b/wmf.txt index 8a233d4..be07d75 100644 --- a/wmf.txt +++ b/wmf.txt @@ -22,7 +22,401 @@ "key": "SA视频", "name": "七味网", "type": 3, - "py":"https://raw.giteeusercontent.com/zzzlllmmm1/tv/raw/master/py/SA%E8%A7%86%E9%A2%91.py?metadata=eyJyIjoibWFzdGVyIiwiZnAiOiJweS9TQeinhumikS5weSIsInVpZCI6NzY0Mzc4MiwicGlkIjoxNzk2ODU0NCwic3RvIjoiZ2l0LXNoYXJkaW5nLXN0by00MnQtMDEwIiwicnAiOiJyZXBvcy8xNC8wYi8xNDBiNTA2ZWFkODNiMDQ4OWI0ZWMzMTBlZWQ3ZjY2NGM3N2MwZDBiYmU3Mjc4YmNhYjc2MWY2YjUxODVmOTA0LmdpdCIsImlzcCI6dHJ1ZSwiZXhwaXJlX2F0IjoxNzg3NTQ4ODAwfQ&signature=mWJSz7-aoiHUKFMYu1WGhYqf2-NQ_YDPyCAMaVWgTjQ", + "py":"# -*- coding: utf-8 -*- +# - SA 影视 (https://www.lsjys11.com/) +# 优化版:图片直链 + 视频代理 + 单线路 + +import re +import json +import sys +import requests +from urllib.parse import quote, unquote, urljoin, urlparse +from html import unescape + +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def getName(self): + return "SA影视" + + def init(self, extend=""): + self.host = "https://www.lsjys11.com" + self.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'Referer': self.host, + } + self.session = requests.Session() + adapter = requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=30, max_retries=0) + self.session.mount('http://', adapter) + self.session.mount('https://', adapter) + self.timeout = 8 + self.UA = self.headers['User-Agent'] + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + if hasattr(self, 'session'): + self.session.close() + + CATEGORIES = { + "movie": {"name": "电影", "cat_id": 13}, + "tv": {"name": "连续剧", "cat_id": 12}, + "variety": {"name": "综艺", "cat_id": 11}, + "anime": {"name": "动漫", "cat_id": 14}, + "short": {"name": "短剧", "cat_id": 16}, + "documentary": {"name": "纪录片", "cat_id": 15}, + } + + def _proxy_url(self, url, typ="m3u8"): + url = str(url or "").strip() + if not url: + return "" + try: + return self.getProxyUrl() + "&type=" + typ + "&url=" + quote(url, safe="") + except Exception: + return url + + def _play_headers(self, url=""): + host = "" + try: + u = urlparse(url or self.host) + host = u.scheme + "://" + u.netloc + "/" if u.scheme and u.netloc else self.host + except Exception: + host = self.host + return { + "User-Agent": self.UA, + "Accept": "*/*", + "Connection": "keep-alive", + "Referer": self.host, + "Origin": self.host.rstrip("/"), + } + + def _parse_nuxt(self, html): + m = re.search(r'', html, re.DOTALL) + if not m: + return None + try: + return json.loads(m.group(1)) + except: + return None + + def _extract_videos(self, data): + if not data: + return [] + videos = [] + for item in data: + if not isinstance(item, dict): + continue + if 'id' not in item or 'name' not in item or 'score' not in item: + continue + vid = item['id'] + name = item['name'] + if isinstance(vid, int) and vid < len(data): + vid = data[vid] + if isinstance(name, int) and name < len(data): + name = data[name] + if not isinstance(vid, str) or not isinstance(name, str): + continue + if len(vid) < 5: + continue + cover = "" + img_idx = item.get('img') + if isinstance(img_idx, int) and img_idx < len(data): + cover = data[img_idx] + if isinstance(cover, str) and cover.startswith("//"): + cover = "https:" + cover + score = "" + score_idx = item.get('score') + if isinstance(score_idx, int) and score_idx < len(data): + score = str(data[score_idx]) + episodes = "" + if item.get('number'): + n = item['number'] + if isinstance(n, int) and n < len(data): + episodes = str(data[n]) + videos.append({ + "vod_id": f"/movie/detail/{vid}", + "vod_name": name, + "vod_pic": cover if isinstance(cover, str) else "", + "vod_remarks": f"{score}分 {episodes}集".strip() if score else "", + }) + return videos + + def homeContent(self, filter): + classes = [] + filters = {} + for cid, info in self.CATEGORIES.items(): + classes.append({"type_id": cid, "type_name": info["name"]}) + filters[cid] = [] + result = {"class": classes, "filters": filters} + try: + rsp = self.fetch(self.host, headers=self.headers) + html = rsp.text + data = self._parse_nuxt(html) + videos = self._extract_videos(data) + result["list"] = videos[:50] + except Exception as e: + self.log(f"首页获取出错: {str(e)}") + result["list"] = [] + return result + + def homeVideoContent(self): + try: + rsp = self.fetch(self.host, headers=self.headers) + html = rsp.text + data = self._parse_nuxt(html) + videos = self._extract_videos(data) + return {"list": videos[:50]} + except Exception as e: + self.log(f"首页视频获取出错: {str(e)}") + return {"list": []} + + def categoryContent(self, tid, pg, filter, extend): + result = {"list": [], "page": int(pg), "pagecount": 999, "limit": 24, "total": 9999} + info = self.CATEGORIES.get(tid) + if not info: + return result + try: + page_num = int(pg) if pg and int(pg) > 0 else 1 + if page_num > 1: + url = f"{self.host}/movie/list/{page_num}?cat_id={info['cat_id']}&position=movie&page={page_num}" + else: + url = f"{self.host}/movie/list?cat_id={info['cat_id']}&position=movie" + rsp = self.fetch(url, headers=self.headers) + html = rsp.text + data = self._parse_nuxt(html) + result["list"] = self._extract_videos(data) + except Exception as e: + self.log(f"分类获取出错: {str(e)}") + return result + + def _get_str(self, data, val): + if isinstance(val, int) and val < len(data): + return str(data[val]) + return str(val) + + def _extract_episodes(self, data, item): + episodes = [] + links_idx = item.get('links') + if not isinstance(links_idx, int) or links_idx >= len(data): + return episodes + links = data[links_idx] + if not isinstance(links, list): + return episodes + for link_idx in links: + if not isinstance(link_idx, int) or link_idx >= len(data): + continue + link_data = data[link_idx] + if not isinstance(link_data, dict): + continue + items_idx = link_data.get('items') + if not isinstance(items_idx, int) or items_idx >= len(data): + continue + items = data[items_idx] + if not isinstance(items, list): + continue + for item_idx in items: + if not isinstance(item_idx, int) or item_idx >= len(data): + continue + ep = data[item_idx] + if not isinstance(ep, dict): + continue + ep_id = self._get_str(data, ep.get('id', '')) + ep_name = self._get_str(data, ep.get('name', '')) + if ep_id and ep_name: + episodes.append({'name': ep_name, 'id': ep_id}) + return episodes + + def detailContent(self, ids): + if not ids or not ids[0]: + return {"list": []} + vid = ids[0] + url = f"{self.host}{vid}" + try: + rsp = self.fetch(url, headers=self.headers) + html = rsp.text + + title = "" + desc = "" + cover = "" + category = "" + director = "" + actors = "" + genre = "" + + ld_m = re.search(r'', html, re.DOTALL) + if ld_m: + try: + ld = json.loads(ld_m.group(1)) + if ld.get("@type") in ["Movie", "TVSeries", "Episode"]: + title = ld.get("name", "") + desc = ld.get("description", "") + cover = ld.get("image", "") + d = ld.get("director", []) + if d and isinstance(d, list): + director = ", ".join([x.get("name", "") for x in d if isinstance(x, dict)]) + a = ld.get("actor", []) + if a and isinstance(a, list): + actors = ", ".join([x.get("name", "") for x in a if isinstance(x, dict)]) + g = ld.get("genre", []) + if g and isinstance(g, list): + genre = ", ".join(g) + category = genre + except: + pass + + if not title: + hm = re.search(r']+property="og:title"[^>]+content="([^"]*)"', html) + if hm: + title = unescape(hm.group(1)) + title = re.sub(r'\s+在线观看.*', '', title) + + if not desc: + dm = re.search(r']+name="description"[^>]+content="([^"]*)"', html) + if dm: + desc = unescape(dm.group(1)) + + if not cover: + cm = re.search(r']+property="og:image"[^>]+content="([^"]*)"', html) + if cm: + cover = unescape(cm.group(1)) + + h1m = re.search(r'