#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
遮天法 · 极道帝兵 · 骚火电影 Spider（最终稳定版）
修复排行榜/最近更新无数据：增加 text_list 解析
"""
import re
import json
import base64
import html
from urllib.parse import quote, unquote, urljoin

try:
    import requests
    from lxml import etree
except ImportError:
    requests = None
    etree = None

try:
    from base.spider import Spider as BaseSpider
except ImportError:
    class BaseSpider:
        def init(self, extend=""): pass
        def homeContent(self, filter): pass
        def homeVideoContent(self): pass
        def categoryContent(self, tid, pg, filter, extend): pass
        def detailContent(self, ids): pass
        def playerContent(self, flag, id, vipFlags=None): pass
        def searchContent(self, key, quick, pg='1'): pass
        def isVideoFormat(self, url): pass
        def manualVideoCheck(self): pass
        def localProxy(self, param): pass


class Spider(BaseSpider):
    def __init__(self):
        super().__init__()
        self.host = "https://shdy2.com"
        self.name = "骚火电影_最终版"
        self.session = requests.Session() if requests else None
        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",
            "Referer": self.host + "/",
            "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120"',
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": '"Windows"',
            "Upgrade-Insecure-Requests": "1",
        }
        if self.session:
            self.session.headers.update(self.headers)
            self.session.verify = False

    def init(self, extend=""):
        if extend and extend.startswith("http"):
            self.host = extend.rstrip("/")
            self.headers["Referer"] = self.host + "/"
            if self.session:
                self.session.headers.update({"Referer": self.host + "/"})

    def getName(self):
        return self.name

    def isVideoFormat(self, url):
        return any(x in url for x in [".m3u8", ".mp4", ".flv", ".ts"])

    def manualVideoCheck(self):
        return False

    # ---------- 辅助工具 ----------
    def _fix_url(self, url):
        if not url:
            return ""
        url = url.strip()
        if url.startswith("//"):
            return "https:" + url
        if url.startswith("/"):
            return urljoin(self.host, url)
        if url.startswith("http"):
            return url
        return urljoin(self.host, "/" + url)

    def _clean_text(self, text):
        if not text:
            return ""
        return html.unescape(re.sub(r"<[^>]+>", "", str(text))).strip()

    def _fetch(self, url, referer=None):
        if not self.session:
            return ""
        headers = dict(self.headers)
        if referer:
            headers["Referer"] = referer
        try:
            resp = self.session.get(url, headers=headers, timeout=15)
            resp.encoding = "utf-8"
            return resp.text
        except Exception as e:
            print(f"[{self.name}] 请求失败: {url} -> {e}")
            return ""

    # ==================== 极致深度提取（含 Meta Refresh） ====================
    def _extract_play(self, html, base_url, depth=0):
        if depth > 5:
            return ""

        # 1. 直连视频文件
        for pat in [r'(https?://[^\s"\']+\.m3u8[^\s"\']*)',
                    r'(https?://[^\s"\']+\.mp4[^\s"\']*)',
                    r'(https?://[^\s"\']+\.flv[^\s"\']*)']:
            m = re.search(pat, html, re.I)
            if m:
                return m.group(1)

        # 2. Meta Refresh 重定向
        refresh = re.search(r'<meta[^>]+http-equiv="refresh"[^>]+content="\d+;url=([^"]+)"', html, re.I)
        if refresh:
            redirect_url = self._fix_url(refresh.group(1))
            if redirect_url:
                redirected = self._fetch(redirect_url, referer=base_url)
                if redirected:
                    return self._extract_play(redirected, redirect_url, depth+1)

        # 3. 常见播放器变量
        patterns = [
            r'var\s*now\s*=\s*["\']([^"\']+)["\']',
            r'player_data\s*=\s*(\{.*?\})',
            r'var\s*playurl\s*=\s*["\']([^"\']+)["\']',
            r'var\s*player_aaaa\s*=\s*(\{.*?\})',
            r'url\s*:\s*["\']([^"\']+\.m3u8)["\']',
            r'videoSources\s*:\s*(\[.*?\])',
            r'wvPlayer\.play\s*\(\s*["\']([^"\']+)["\']',
            r'location\.href\s*=\s*["\']([^"\']+)["\']',
            r'playerConfig\s*=\s*(\{.*?\})',
            r'playInfo\s*=\s*(\{.*?\})',
            r'<video[^>]+src="([^"]+)"',
            r'<source[^>]+src="([^"]+)"',
            r'file\s*:\s*["\']([^"\']+\.m3u8)["\']',
        ]
        for pat in patterns:
            m = re.search(pat, html, re.DOTALL)
            if m:
                group = m.group(1)
                if pat.endswith(r'\}') or pat.endswith(r'\]'):
                    try:
                        data = json.loads(group)
                        if isinstance(data, list):
                            return data[0].get("file") or data[0].get("url") or ""
                        return data.get("url") or data.get("playUrl") or data.get("file") or ""
                    except:
                        pass
                else:
                    if group.startswith("http"):
                        return group
                    if group.startswith("/"):
                        return urljoin(base_url, group)

        # 4. iframe递归
        iframe_matches = re.findall(r'<iframe[^>]+src="([^"]+)"', html, re.I)
        for iframe_url in iframe_matches:
            full_iframe = self._fix_url(iframe_url)
            if full_iframe:
                inner_html = self._fetch(full_iframe, referer=base_url)
                if inner_html:
                    result = self._extract_play(inner_html, full_iframe, depth+1)
                    if result:
                        return result

        # 5. eval/Base64解密
        eval_match = re.search(r'eval\s*\((["\'])(.+?)\1', html, re.DOTALL)
        if eval_match:
            try:
                decoded = base64.b64decode(eval_match.group(2)).decode()
                return self._extract_play(decoded, base_url, depth+1)
            except:
                pass

        # 6. 从 script 中提取 .m3u8 字符串
        script_matches = re.findall(r'<script[^>]*>(.*?)</script>', html, re.S)
        for script in script_matches:
            m = re.search(r'(https?://[^\s"\']+\.m3u8[^\s"\']*)', script, re.I)
            if m:
                return m.group(1)

        return ""

    # ==================== 解析视频列表（同时支持 v_list 和 text_list） ====================
    def _parse_video_list(self, html):
        videos = []
        doc = etree.HTML(html) if etree else None
        if doc:
            # 优先 v_list（带海报），其次 text_list（纯文字）
            items = doc.xpath('//ul[contains(@class,"v_list")]/li')
            if not items:
                items = doc.xpath('//ul[contains(@class,"text_list")]/li')
            if not items:
                items = doc.xpath('//div[contains(@class,"grid_box")]//ul/li')
            for li in items:
                try:
                    # 尝试 text_list 格式（a + span）
                    title = li.xpath('.//a/text()')
                    href = li.xpath('.//a/@href')
                    note = li.xpath('.//span[contains(@class,"v_note")]/text()')
                    if title and href:
                        title = self._clean_text(title[0])
                        href = href[0]
                        note = self._clean_text(note[0]) if note else ""
                        pic = ""  # text_list 无图
                    else:
                        # 尝试 v_list 格式（带图片）
                        title = li.xpath('.//p[contains(@class,"v_title")]/a/text()') or \
                                li.xpath('.//div[contains(@class,"v_img")]/a/@title') or \
                                li.xpath('.//a[2]/text()')
                        title = self._clean_text(title[0]) if title else ""
                        href = li.xpath('.//p[contains(@class,"v_title")]/a/@href') or \
                               li.xpath('.//div[contains(@class,"v_img")]/a/@href')
                        href = href[0] if href else ""
                        pic = li.xpath('.//img/@data-original') or li.xpath('.//img/@src')
                        pic = self._fix_url(pic[0]) if pic else ""
                        note = li.xpath('.//div[contains(@class,"v_note")]/text()')
                        note = self._clean_text(note[0]) if note else ""

                    if not title:
                        continue
                    vid = re.search(r'/(\d+)\.html', href)
                    vid = vid.group(1) if vid else href
                    videos.append({
                        "vod_id": vid,
                        "vod_name": title,
                        "vod_pic": pic,
                        "vod_remarks": note
                    })
                except Exception as e:
                    print(f"解析列表项失败: {e}")
                    continue
        else:
            # ---------- 正则回退 ----------
            # 1. 尝试匹配 v_list
            pat_v = r'<li>.*?<a href="(/movie/(\d+)\.html)"[^>]*>.*?<img[^>]+data-original="([^"]+)"[^>]*>.*?<div class="v_note">(.*?)</div>.*?<p class="v_title"><a[^>]*>(.*?)</a>'
            for href, vid, pic, note, title in re.findall(pat_v, html, re.S):
                videos.append({
                    "vod_id": vid,
                    "vod_name": self._clean_text(title),
                    "vod_pic": self._fix_url(pic),
                    "vod_remarks": self._clean_text(note)
                })
            # 2. 如果没有匹配到，尝试 text_list
            if not videos:
                pat_t = r'<li>.*?<a href="([^"]+)"[^>]*>([^<]+)</a>.*?<span[^>]*>([^<]+)</span>'
                for href, title, note in re.findall(pat_t, html, re.S):
                    vid = re.search(r'/(\d+)\.html', href)
                    vid = vid.group(1) if vid else href
                    videos.append({
                        "vod_id": vid,
                        "vod_name": self._clean_text(title),
                        "vod_pic": "",
                        "vod_remarks": self._clean_text(note)
                    })
        return videos

    # ---------- 首页 ----------
    def homeContent(self, filter=False):
        classes = [
            {"type_id": "1", "type_name": "电影"},
            {"type_id": "2", "type_name": "电视剧"},
            {"type_id": "top", "type_name": "排行榜"},
            {"type_id": "new", "type_name": "最近更新"},
        ]
        return {"class": classes, "filters": {}}

    def homeVideoContent(self):
        html = self._fetch(self.host + "/", referer=self.host + "/")
        if not html:
            return []
        doc = etree.HTML(html) if etree else None
        if doc:
            first_list = doc.xpath('//section[contains(@class,"grid_box")]//ul[contains(@class,"v_list")]')
            if first_list:
                ul = first_list[0]
                videos = []
                for li in ul.xpath('./li'):
                    try:
                        title = li.xpath('.//p[contains(@class,"v_title")]/a/text()')
                        title = self._clean_text(title[0]) if title else ""
                        href = li.xpath('.//p[contains(@class,"v_title")]/a/@href') or li.xpath('.//div[contains(@class,"v_img")]/a/@href')
                        href = href[0] if href else ""
                        vid = re.search(r'/(\d+)\.html', href)
                        vid = vid.group(1) if vid else href
                        pic = li.xpath('.//img/@data-original') or li.xpath('.//img/@src')
                        pic = self._fix_url(pic[0]) if pic else ""
                        note = li.xpath('.//div[contains(@class,"v_note")]/text()')
                        note = self._clean_text(note[0]) if note else ""
                        videos.append({
                            "vod_id": vid,
                            "vod_name": title,
                            "vod_pic": pic,
                            "vod_remarks": note
                        })
                    except:
                        continue
                return videos
        all_videos = self._parse_video_list(html)
        return all_videos[:20]

    # ---------- 分类（修复排行榜和最近更新） ----------
    def categoryContent(self, tid, pg, filter=False, extend=None):
        result = {"list": [], "page": int(pg), "pagecount": 1, "limit": 24, "total": 0}
        try:
            if tid == "top":
                url = f"{self.host}/top.html"
                # 排行榜无分页
                result["pagecount"] = 1
            elif tid == "new":
                url = f"{self.host}/new.html"
                result["pagecount"] = 1
            else:
                url = f"{self.host}/list/{tid}.html?page={pg}"

            html_text = self._fetch(url, referer=self.host + "/")
            if not html_text:
                return result

            videos = self._parse_video_list(html_text)
            result["list"] = videos

            # 对于非 top/new，尝试提取分页
            if tid not in ("top", "new"):
                doc = etree.HTML(html_text) if etree else None
                if doc:
                    page_links = doc.xpath('//a[contains(@href,"page=")]')
                    nums = []
                    for a in page_links:
                        txt = a.text
                        if txt and txt.isdigit():
                            nums.append(int(txt))
                    if nums:
                        result["pagecount"] = max(nums)
                    else:
                        result["pagecount"] = int(pg) + 1
                else:
                    result["pagecount"] = int(pg) + 1

            return result
        except Exception as e:
            print(f"[{self.name}] categoryContent 异常: {e}")
            return result

    # ---------- 详情页（线路名修复） ----------
    def detailContent(self, ids):
        result = {"list": []}
        try:
            vid = ids[0] if isinstance(ids, list) else ids
            url = f"{self.host}/movie/{vid}.html"
            html_text = self._fetch(url, referer=self.host + "/")
            if not html_text:
                return result

            doc = etree.HTML(html_text) if etree else None

            # ---- 提取片名 ----
            title = ""
            if doc:
                title_el = doc.xpath('//h1[contains(@class,"v_title")]/a/text()')
                if not title_el:
                    title_el = doc.xpath('//h1/text()')
                title = self._clean_text(title_el[0]) if title_el else vid
            else:
                title_match = re.search(r'<h1[^>]+class="v_title"[^>]*><a[^>]*>([^<]+)</a>', html_text)
                title = self._clean_text(title_match.group(1)) if title_match else vid

            # ---- 提取海报 ----
            pic = ""
            if doc:
                pic_el = doc.xpath('//img[contains(@class,"poster") or contains(@class,"cover") or contains(@class,"v_img")]/@src') or \
                         doc.xpath('//img[contains(@class,"poster")]/@data-original') or \
                         doc.xpath('//meta[@property="og:image"]/@content')
                pic = self._fix_url(pic_el[0]) if pic_el else ""
            else:
                pic_match = re.search(r'<img[^>]+data-original="([^"]+)"', html_text)
                pic = self._fix_url(pic_match.group(1)) if pic_match else ""

            # ---- 提取播放源：线路名 + 剧集 ----
            sources = []
            play_urls = []

            if doc:
                from_lis = doc.xpath('//div[contains(@class,"play_from")]//ul[contains(@class,"from_list")]/li')
                if from_lis:
                    for li in from_lis:
                        name = self._clean_text(li.xpath('./text()'))
                        if name:
                            sources.append(name)
                play_lis = doc.xpath('//ul[contains(@class,"play_list")]/li')
                if not play_lis:
                    play_lis = doc.xpath('//div[contains(@class,"play_list")]//li')

                if sources and len(sources) == len(play_lis):
                    for idx, li in enumerate(play_lis):
                        eps = []
                        for a in li.xpath('.//a[contains(@href,"/play/")]'):
                            ep_title = a.text or "播放"
                            ep_href = a.get("href")
                            if ep_href:
                                eps.append(f"{self._clean_text(ep_title)}${self._fix_url(ep_href)}")
                        if eps:
                            play_urls.append("#".join(eps))
                else:
                    for idx, li in enumerate(play_lis):
                        eps = []
                        for a in li.xpath('.//a[contains(@href,"/play/")]'):
                            ep_title = a.text or "播放"
                            ep_href = a.get("href")
                            if ep_href:
                                eps.append(f"{self._clean_text(ep_title)}${self._fix_url(ep_href)}")
                        if eps:
                            if not sources or idx >= len(sources):
                                sources.append(f"线路{len(sources)+1}")
                            play_urls.append("#".join(eps))
            else:
                # 正则回退
                from_match = re.search(r'<ul[^>]+class="from_list"[^>]*>(.*?)</ul>', html_text, re.S)
                if from_match:
                    from_html = from_match.group(1)
                    names = re.findall(r'<li[^>]*>([^<]+)</li>', from_html)
                    sources = [self._clean_text(n) for n in names if n.strip()]
                play_blocks = re.findall(r'<ul[^>]+class="play_list"[^>]*>(.*?)</ul>', html_text, re.S)
                for idx, block in enumerate(play_blocks):
                    eps = re.findall(r'<a[^>]+href="(/play/[^"]+)"[^>]*>([^<]+)</a>', block)
                    if eps:
                        ep_str = "#".join([f"{self._clean_text(name)}${self._fix_url(href)}" for href, name in eps])
                        if sources and idx < len(sources):
                            play_urls.append(ep_str)
                        else:
                            sources.append(f"线路{len(sources)+1}")
                            play_urls.append(ep_str)

            if play_urls and not sources:
                sources = [f"线路{i+1}" for i in range(len(play_urls))]

            if not play_urls:
                play_url = self._extract_play(html_text, url)
                if play_url:
                    sources = ["直链"]
                    play_urls = [f"正片${play_url}"]

            result["list"].append({
                "vod_id": vid,
                "vod_name": title,
                "vod_pic": pic,
                "vod_play_from": "$$$".join(sources) if sources else "默认",
                "vod_play_url": "$$$".join(play_urls) if play_urls else ""
            })
            return result
        except Exception as e:
            print(f"[{self.name}] detailContent 异常: {e}")
            return result

    # ---------- 播放（嗅探兜底） ----------
    def playerContent(self, flag, id, vipFlags=None):
        result = {"parse": 0, "playUrl": "", "url": "", "header": ""}
        try:
            if self.isVideoFormat(id):
                result["url"] = id
                result["header"] = json.dumps({"Referer": self.host + "/", "User-Agent": self.headers["User-Agent"]})
                return result

            if id.startswith("http") or id.startswith("/"):
                full_url = self._fix_url(id)
                html_text = self._fetch(full_url, referer=self.host + "/")
                if html_text:
                    play_url = self._extract_play(html_text, full_url)
                    if play_url:
                        result["url"] = play_url
                        result["header"] = json.dumps({"Referer": self.host + "/", "User-Agent": self.headers["User-Agent"]})
                        return result
                    # 提取失败则让 TVBox 嗅探
                    result["parse"] = 1
                    result["url"] = full_url
                    return result

            result["parse"] = 1
            result["url"] = id
            return result
        except Exception as e:
            print(f"[{self.name}] playerContent 异常: {e}")
            result["parse"] = 1
            result["url"] = id
            return result

    # ---------- 搜索 ----------
    def searchContent(self, key, quick, pg="1"):
        result = {"list": [], "page": int(pg), "pagecount": 1, "limit": 24, "total": 0}
        try:
            url = f"{self.host}/s----------.html?wd={quote(key)}"
            html_text = self._fetch(url, referer=self.host + "/")
            if not html_text:
                return result
            videos = self._parse_video_list(html_text)
            result["list"] = videos
            return result
        except Exception as e:
            print(f"[{self.name}] searchContent 异常: {e}")
            return result

    # ---------- 本地代理 ----------
    def localProxy(self, param):
        return [200, "text/plain", b"", {}]