# -*- coding: utf-8 -*-
"""SOTVLA 本地搜索聚合代理 - 端口 8001
聚合多个 MacCMS 采集站 API，带 CORS 头，供 onlinetv.html 调用。
用法: python sotvla_proxy.py  (后台运行)
"""
import json
import re
import os
import urllib.parse
import urllib.request
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler

# 直接集成洛雪音乐后端模块（不单独开端口）
LX_DIR = r'D:\doubao\lxmusic'
_lx = None
if os.path.isdir(LX_DIR):
    try:
        import importlib.util
        _spec = importlib.util.spec_from_file_location('lxmusic_server', os.path.join(LX_DIR, 'server.py'))
        _lx = importlib.util.module_from_spec(_spec)
        _spec.loader.exec_module(_lx)
    except Exception as e:
        print('[洛雪音乐] 模块加载失败: %s' % e, flush=True)
        _lx = None

# 采集站列表（从原站提取）
SOURCES = [
    {"id": 15, "name": "暴风资源", "url": "https://bfzyapi.com/api.php/provide/vod/?ac=videolist&wd=", "m3u8": True},
    {"id": 3,  "name": "量子资源", "url": "https://cj.lziapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": False},
    {"id": 10, "name": "极速资源", "url": "https://jszyapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": False},
    {"id": 2,  "name": "非凡资源", "url": "https://api.ffzyapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": False},
    {"id": 20, "name": "西瓜资源", "url": "https://backup.xigua-api.com/api.php/provide/vod/?ac=videolist&wd=", "m3u8": False},
    {"id": 9,  "name": "爱奇艺资源", "url": "https://iqiyizyapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": False},
    {"id": 21, "name": "光速资源", "url": "https://api.guangsuapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 22, "name": "红牛资源", "url": "https://www.hongniuzy2.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 23, "name": "豪华资源", "url": "https://hhzyapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 24, "name": "魔都资源", "url": "https://caiji.moduapi.cc/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 25, "name": "百度资源", "url": "https://api.apibdzy.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 26, "name": "无尽资源", "url": "https://api.wujinapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 27, "name": "电影天堂", "url": "http://caiji.dyttzyapi.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 28, "name": "360资源", "url": "https://360zy.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 29, "name": "速播资源", "url": "https://subocaiji.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
    {"id": 30, "name": "金鹰资源", "url": "https://www.jinyingzy.com/api.php/provide/vod/?ac=detail&wd=", "m3u8": True},
]

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
}


def fetch_json(url, timeout=15):
    req = urllib.request.Request(url, headers=HEADERS)
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        raw = resp.read().decode("utf-8", errors="replace")
    # 修复 MacCMS 返回的 Infinity/NaN 问题
    raw = re.sub(r"\bInfinity\b", "null", raw)
    raw = re.sub(r"\bNaN\b", "null", raw)
    return json.loads(raw)


def search_source(src, keyword):
    url = src["url"] + urllib.parse.quote(keyword)
    try:
        data = fetch_json(url, timeout=5)
        items = data.get("list") or []
        results = []
        for it in items:
            play_url = it.get("vod_play_url") or ""
            episodes = []
            if play_url:
                # 按$$$分割多播放线路，优先选m3u8结尾的线路
                lines = play_url.split("$$$")
                best_line = None
                for line in lines:
                    if ".m3u8" in line.lower():
                        best_line = line
                        break
                if best_line is None:
                    best_line = lines[0]
                # 解析 "第01集$url#第02集$url" 格式
                for ep in best_line.split("#"):
                    parts = ep.split("$")
                    if len(parts) >= 2:
                        episodes.append({"name": parts[0], "url": parts[1]})
            results.append({
                "source_id": src["id"],
                "source_name": src["name"],
                "vod_id": it.get("vod_id", ""),
                "name": it.get("vod_name", ""),
                "pic": it.get("vod_pic", ""),
                "type": it.get("vod_class", ""),
                "actor": it.get("vod_actor", ""),
                "director": it.get("vod_director", ""),
                "area": it.get("vod_area", ""),
                "year": it.get("vod_year", ""),
                "blurb": it.get("vod_blurb", ""),
                "serial": it.get("vod_serial", ""),
                "total": it.get("vod_total", ""),
                "episodes": episodes,
                "is_m3u8": src["m3u8"] or bool(episodes and ".m3u8" in episodes[0].url.lower()),
            })
        return results
    except Exception as e:
        return []


class Handler(BaseHTTPRequestHandler):
    def _cors(self):
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")

    def do_OPTIONS(self):
        self.send_response(204)
        self._cors()
        self.end_headers()

    WEB_ROOTS = [r"D:\doubao\tmp\酱腌菜电商看板", r"D:\doubao"]

    def _serve_static(self, path):
        import os, urllib.parse
        path = urllib.parse.unquote(path)
        if path == "/" or path == "":
            path = "/onlinetv.html"
        # 别名：/lxmusic.html → /lxmusic/lxmusic.html
        if path == "/lxmusic.html":
            path = "/lxmusic/lxmusic.html"
        # 在多个根目录中查找
        full = None
        for root in self.WEB_ROOTS:
            candidate = os.path.normpath(os.path.join(root, path.lstrip("/")))
            if candidate.startswith(root) and os.path.isfile(candidate):
                full = candidate
                break
        if not full:
            self.send_error(404)
            return
        ext = os.path.splitext(full)[1].lower()
        ctypes = {".html":"text/html; charset=utf-8", ".css":"text/css", ".js":"application/javascript",
                  ".png":"image/png", ".jpg":"image/jpeg", ".webp":"image/webp", ".ico":"image/x-icon"}
        ctype = ctypes.get(ext, "application/octet-stream")
        with open(full, "rb") as f:
            body = f.read()
        self.send_response(200)
        self.send_header("Content-Type", ctype)
        self.send_header("Content-Length", str(len(body)))
        self._cors()
        self.end_headers()
        self.wfile.write(body)

    def _proxy_to(self, url):
        import urllib.request
        try:
            req = urllib.request.Request(url)
            resp = urllib.request.urlopen(req, timeout=15)
            body = resp.read()
            ct = resp.headers.get("Content-Type", "application/octet-stream")
            self.send_response(resp.status)
            self.send_header("Content-Type", ct)
            self.send_header("Content-Length", str(len(body)))
            self._cors()
            self.end_headers()
            self.wfile.write(body)
        except Exception as e:
            self.send_error(502, str(e))

    def do_GET(self):
        parsed = urllib.parse.urlparse(self.path)
        # lxmusic 接口：直接调用集成模块（不单独开端口）
        if parsed.path.startswith("/lxmusic/api/"):
            if _lx is None:
                self.send_error(503, "lxmusic not loaded")
                return
            api_name = parsed.path[len("/lxmusic/api/"):]
            query = dict(urllib.parse.parse_qs(parsed.query))
            # 借用 lxmusic Handler 的所有方法
            old_json = self._json
            self._json = lambda status, payload: _lx.Handler._json(self, status, payload)
            self._text = lambda t, ct="text/plain; charset=utf-8": _lx.Handler._text(self, t, ct)
            self._stream_audio = lambda *a, **kw: _lx.Handler._stream_audio(self, *a, **kw)
            self._stream_pic = lambda *a, **kw: _lx.Handler._stream_pic(self, *a, **kw)
            self._send_bytes = lambda *a, **kw: _lx.Handler._send_bytes(self, *a, **kw)
            try:
                _lx.Handler._api(self, api_name, query)
            finally:
                self._json = old_json
            return
        # lxmusic 静态资源
        if parsed.path in ("/lxmusic-manifest.json", "/lxmusic-sw.js",
                          "/lxmusic-icon-16.png", "/lxmusic-icon-32.png",
                          "/lxmusic-icon-180.png", "/lxmusic-icon-192.png",
                          "/lxmusic-icon-512.png"):
            import os
            fpath = os.path.join(r"D:\doubao\lxmusic", parsed.path.lstrip("/"))
            if os.path.isfile(fpath):
                with open(fpath, "rb") as f:
                    body = f.read()
                ext = os.path.splitext(fpath)[1].lower()
                ct = {".json":"application/json", ".js":"application/javascript", ".png":"image/png"}.get(ext, "application/octet-stream")
                self.send_response(200)
                self.send_header("Content-Type", ct)
                self.send_header("Content-Length", str(len(body)))
                self.end_headers()
                self.wfile.write(body)
                return
        # 静态文件服务（非/api路径）
        if not parsed.path.startswith("/api/"):
            self._serve_static(parsed.path)
            return
        if parsed.path == "/api/search":
            # 前端并行请求每个源，这里只是兼容旧调用
            qs = urllib.parse.parse_qs(parsed.query)
            kw = (qs.get("q", [""])[0] or "").strip()
            if not kw:
                self._json({"error": "missing q"})
                return
            all_results = []
            for src in SOURCES:
                try:
                    items = search_source(src, kw)
                    all_results.extend(items)
                except Exception:
                    pass
            self._json({"keyword": kw, "total": len(all_results), "results": all_results})
        elif parsed.path == "/api/search-src":
            # 单源搜索：前端并行调用
            qs = urllib.parse.parse_qs(parsed.query)
            kw = (qs.get("q", [""])[0] or "").strip()
            sid = qs.get("src", ["0"])[0]
            if not kw:
                self._json({"error": "missing q"})
                return
            src = next((s for s in SOURCES if str(s["id"]) == str(sid)), None)
            if not src:
                self._json({"error": "bad src"})
                return
            try:
                items = search_source(src, kw)
                self._json({"results": items})
            except Exception as e:
                self._json({"results": [], "error": str(e)})
        elif parsed.path == "/api/sources":
            self._json({"sources": [{"id": s["id"], "name": s["name"]} for s in SOURCES]})
        elif parsed.path == "/api/vod-list":
            # 按分类拉取最新影片列表（从暴风资源，最稳定）
            qs = urllib.parse.parse_qs(parsed.query)
            tid = qs.get("t", ["1"])[0]
            pg = qs.get("pg", ["1"])[0]
            year = qs.get("year", [""])[0]
            area = qs.get("area", [""])[0]
            by = qs.get("by", [""])[0]
            try:
                url = "https://cj.lziapi.com/api.php/provide/vod/?ac=videolist&t=" + str(tid) + "&pg=" + str(pg)
                if by: url += "&by=" + by
                data = fetch_json(url, timeout=8)
                list_data = data.get("list", [])[:21]
                # 筛选
                if year:
                    list_data = [x for x in list_data if year in str(x.get("vod_year", ""))]
                if area:
                    list_data = [x for x in list_data if area in str(x.get("vod_area", ""))]
                results = []
                for item in list_data:
                    play_url = ""
                    play_from = item.get("vod_play_from", "")
                    play_urls = item.get("vod_play_url", "")
                    if play_urls:
                        first_ep = play_urls.split("$$$")[0] if "$$$" in play_urls else play_urls.split("#")[0]
                        if "#" in first_ep:
                            play_url = first_ep.split("#")[-1]
                    results.append({
                        "name": item.get("vod_name", ""),
                        "pic": item.get("vod_pic", ""),
                        "remarks": item.get("vod_remarks", ""),
                        "year": item.get("vod_year", ""),
                        "area": item.get("vod_area", ""),
                        "type": item.get("vod_class", ""),
                        "play_url": play_url,
                    })
                self._json({"list": results, "total": data.get("total", 0), "page": int(pg)})
            except Exception as e:
                self._json({"error": str(e), "list": [], "total": 0})
        elif parsed.path == "/api/live-channels":
            qs = urllib.parse.parse_qs(parsed.query)
            src_id = qs.get("src", ["1"])[0]
            # 多套直播源，前端可手动切换
            LIVE_SOURCES = {
                "1": {"name": "高速源(et6jt3)", "url": "https://et6jt3.github.io/iptv/tv/iptv4.m3u"},
                "2": {"name": "稳定源(vbskycn)", "url": "https://gh-proxy.com/raw.githubusercontent.com/vbskycn/iptv/refs/heads/master/tv/iptv4.m3u"},
                "3": {"name": "央视专属", "url": "https://gh-proxy.com/raw.githubusercontent.com/peterhchina/iptv/main/CCTV-V4.m3u"},
            }
            src = LIVE_SOURCES.get(src_id, LIVE_SOURCES["1"])
            channels = []
            try:
                req = urllib.request.Request(src["url"], headers={"User-Agent": HEADERS["User-Agent"]})
                with urllib.request.urlopen(req, timeout=15) as resp:
                    raw = resp.read()
                # peterhchina 的源是gzip压缩的
                if raw[:2] == b'\x1f\x8b':
                    import gzip
                    text = gzip.decompress(raw).decode("utf-8", errors="replace")
                else:
                    text = raw.decode("utf-8", errors="replace")
                lines = text.splitlines()
                cur_name = ""; cur_logo = ""; cur_group = ""
                for line in lines:
                    line = line.strip()
                    if line.startswith("#EXTINF"):
                        logo_m = re.search(r'tvg-logo="([^"]*)"', line)
                        grp_m = re.search(r'group-title="([^"]*)"', line)
                        if logo_m: cur_logo = logo_m.group(1)
                        if grp_m: cur_group = grp_m.group(1)
                        parts = line.split(",", 1)
                        if len(parts) > 1: cur_name = parts[1].strip()
                    elif line and not line.startswith("#"):
                        channels.append({
                            "name": cur_name or "未知频道",
                            "url": line,
                            "logo": cur_logo,
                            "group": cur_group or "其他",
                            "source": "tv",
                        })
                        cur_name = ""; cur_logo = ""
            except Exception as e:
                print(f"[live] fetch {src['url']} failed: {e}")
            self._json({"channels": channels, "src": src_id, "srcName": src["name"]})
        elif parsed.path == "/api/proxy-m3u8":
            # 代理 m3u8 播放地址（解决混合内容/CORS + 重写分片URL）
            qs = urllib.parse.parse_qs(parsed.query)
            target = qs.get("url", [""])[0]
            if not target:
                self._json({"error": "missing url"})
                return
            try:
                # 夸克网盘的m3u8需要特殊的请求头
                headers = {
                    "User-Agent": HEADERS["User-Agent"],
                    "Referer": target.rsplit("/", 3)[0] + "/",
                }
                if "quark.cn" in target:
                    # 加载夸克cookies
                    try:
                        with open(r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json", "r", encoding="utf-8") as f:
                            pan_cookies = json.load(f)
                            quark_cookies = pan_cookies.get("quark", "")
                            if quark_cookies:
                                headers["Cookie"] = quark_cookies
                    except:
                        pass
                    headers["Referer"] = "https://pan.quark.cn/"
                req = urllib.request.Request(target, headers=headers)
                with urllib.request.urlopen(req, timeout=10) as resp:
                    raw = resp.read().decode("utf-8", errors="replace")

                # m3u8 的 base URL 是 target 去掉最后一段
                base_url = target.rsplit("/", 1)[0] + "/"

                new_lines = []
                for line in raw.splitlines():
                    line = line.strip()
                    if not line:
                        new_lines.append(line)
                        continue
                    # 重写 EXT-X-KEY 里的相对 URI（AES加密key）
                    if line.startswith("#EXT-X-KEY:"):
                        line = re.sub(r'URI="([^"]+)"', lambda m: 'URI="' + urllib.parse.urljoin(base_url, m.group(1)) + '"', line)
                        new_lines.append(line)
                        continue
                    if line.startswith("#"):
                        new_lines.append(line)
                        continue
                    # 分片地址
                    if "quark.cn" in target:
                        # 夸克网盘的ts分片需要通过proxy-ts代理
                        if line.startswith("http://") or line.startswith("https://"):
                            abs_url = line
                        elif line.startswith("/"):
                            pu = urllib.parse.urlparse(target)
                            abs_url = pu.scheme + "://" + pu.netloc + line
                        else:
                            abs_url = base_url + line
                        # 改成通过proxy-ts代理
                        new_lines.append("/api/proxy-ts?url=" + urllib.parse.quote(abs_url))
                    else:
                        # 其他站点：保持原始绝对URL，让浏览器直接从CDN加载
                        if line.startswith("http://") or line.startswith("https://"):
                            new_lines.append(line)
                        elif line.startswith("/"):
                            pu = urllib.parse.urlparse(target)
                            abs_url = pu.scheme + "://" + pu.netloc + line
                            new_lines.append(abs_url)
                        else:
                            new_lines.append(base_url + line)

                body = "\n".join(new_lines).encode("utf-8")
                self.send_response(200)
                self.send_header("Content-Type", "application/vnd.apple.mpegurl")
                self.send_header("Access-Control-Allow-Origin", "*")
                self.send_header("Content-Length", str(len(body)))
                self.end_headers()
                self.wfile.write(body)
            except Exception as e:
                self._json({"error": str(e)}, code=502)
        elif parsed.path == "/api/proxy-ts":
            # 代理 .ts 分片
            qs = urllib.parse.parse_qs(parsed.query)
            target = qs.get("url", [""])[0]
            if not target:
                self._json({"error": "missing url"})
                return
            try:
                # 夸克网盘的ts分片需要特殊的请求头
                headers = {
                    "User-Agent": HEADERS["User-Agent"],
                    "Referer": target.rsplit("/", 3)[0] + "/",
                }
                if "quark.cn" in target:
                    # 加载夸克cookies
                    try:
                        with open(r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json", "r", encoding="utf-8") as f:
                            pan_cookies = json.load(f)
                            quark_cookies = pan_cookies.get("quark", "")
                            if quark_cookies:
                                headers["Cookie"] = quark_cookies
                    except:
                        pass
                    headers["Referer"] = "https://pan.quark.cn/"
                req = urllib.request.Request(target, headers=headers)
                with urllib.request.urlopen(req, timeout=15) as resp:
                    body = resp.read()
                self.send_response(200)
                self.send_header("Content-Type", "video/mp2t")
                self.send_header("Access-Control-Allow-Origin", "*")
                self.send_header("Content-Length", str(len(body)))
                self.end_headers()
                self.wfile.write(body)
            except Exception as e:
                self._json({"error": str(e)}, code=502)
        elif parsed.path == "/api/proxy-img":
            qs = urllib.parse.parse_qs(parsed.query)
            target = qs.get("url", [""])[0]
            if not target:
                self._json({"error": "missing url"}, code=400)
                return
            try:
                req = urllib.request.Request(target, headers={
                    "User-Agent": HEADERS["User-Agent"],
                    "Referer": "https://www.wogg.net/",
                })
                with urllib.request.urlopen(req, timeout=10) as resp:
                    body = resp.read()
                    ctype = resp.headers.get("Content-Type", "image/jpeg")
                self.send_response(200)
                self.send_header("Content-Type", ctype)
                self.send_header("Access-Control-Allow-Origin", "*")
                self.send_header("Cache-Control", "public, max-age=86400")
                self.send_header("Content-Length", str(len(body)))
                self.end_headers()
                self.wfile.write(body)
            except Exception as e:
                self._json({"error": str(e)}, code=502)
        elif parsed.path == "/api/wogg-search":
            qs = urllib.parse.parse_qs(parsed.query)
            q = qs.get("q", [""])[0]
            if not q:
                self._json({"error": "missing q"})
                return
            try:
                url = "https://www.wogg.net/vodsearch/" + urllib.parse.quote(q) + "----------1---.html"
                req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_2 like Mac OS X)"})
                with urllib.request.urlopen(req, timeout=10) as resp:
                    html = resp.read().decode("utf-8", errors="replace")
                results = []
                # 解析搜索结果卡片
                items = re.findall(r'class="module-item[\s\S]*?data-src="([^"]*)"[\s\S]*?href="(/voddetail/[^"]+)"[^>]*title="([^"]*)"[^>]*>([^<]*)</a>', html)
                for item in items:
                    pic = item[0].replace('&amp;', '&')
                    results.append({
                        "pic": pic,
                        "id": item[1],
                        "name": item[2],
                        "remarks": item[3].strip(),
                        "url": "https://www.wogg.net" + item[1],
                    })
                self._json({"list": results})
            except Exception as e:
                self._json({"error": str(e), "list": []})
        elif parsed.path == "/api/wogg-category":
            qs = urllib.parse.parse_qs(parsed.query)
            cid = qs.get("cid", ["1"])[0]
            page = qs.get("page", ["1"])[0]
            try:
                url = f"https://www.wogg.net/vodtype/{cid}-{page}.html"
                req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_2 like Mac OS X)"})
                with urllib.request.urlopen(req, timeout=10) as resp:
                    html = resp.read().decode("utf-8", errors="replace")
                results = []
                items = re.findall(r'class="module-item[\s\S]*?data-src="([^"]*)"[\s\S]*?href="(/voddetail/[^"]+)"[^>]*title="([^"]*)"[^>]*>([^<]*)</a>', html)
                for item in items:
                    pic = item[0].replace('&amp;', '&')
                    results.append({
                        "pic": pic,
                        "id": item[1],
                        "name": item[2],
                        "remarks": item[3].strip(),
                        "url": "https://www.wogg.net" + item[1],
                    })
                self._json({"list": results, "cid": cid, "page": page})
            except Exception as e:
                self._json({"error": str(e), "list": []})
        elif parsed.path == "/api/wogg-detail":
            qs = urllib.parse.parse_qs(parsed.query)
            url = qs.get("url", [""])[0]
            if not url:
                self._json({"error": "missing url"})
                return
            try:
                req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_2 like Mac OS X)"})
                with urllib.request.urlopen(req, timeout=10) as resp:
                    html = resp.read().decode("utf-8", errors="replace")
                # 提取网盘分享链接
                tracks = []
                # module-row-title 结构
                blocks = re.findall(r'class="module-row-title"[\s\S]*?<h4>([^<]+)</h4>[\s\S]*?<p>([^<]+)</p>', html)
                for b in blocks:
                    name = b[0].replace(" - 玩偶哥哥", "").strip()
                    pan_url = b[1].strip()
                    if pan_url:
                        tracks.append({"name": name, "pan": pan_url})
                self._json({"tracks": tracks})
            except Exception as e:
                self._json({"error": str(e), "tracks": []})
        elif parsed.path == "/api/quark-play":
            # 夸克网盘完整播放流程：解析分享链接 → 转存 → 获取m3u8地址
            qs = urllib.parse.parse_qs(parsed.query)
            share_url = qs.get("share_url", [""])[0]
            fid = qs.get("fid", [""])[0]
            if not share_url or not fid:
                self._json({"error": "missing share_url or fid"})
                return
            try:
                # 从分享链接提取pwd_id
                m = re.search(r'/s/([a-zA-Z0-9]+)', share_url)
                if not m:
                    self._json({"error": "invalid share url"})
                    return
                pwd_id = m.group(1)
                
                # 读取cookies
                cookies_file = r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json"
                try:
                    with open(cookies_file, "r", encoding="utf-8") as f:
                        cookies = json.load(f)
                except:
                    cookies = {}
                quark_cookie = cookies.get("quark", "")
                if not quark_cookie:
                    self._json({"error": "quark cookies not set"})
                    return
                
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                    "Content-Type": "application/json",
                    "Referer": "https://pan.quark.cn/",
                    "Cookie": quark_cookie,
                }
                
                # 第一步：获取stoken
                token_url = "https://drive-h.quark.cn/1/clouddrive/share/sharepage/token?pr=ucpro&fr=pc"
                token_body = {"pwd_id": pwd_id, "passcode": ""}
                req = urllib.request.Request(token_url, data=json.dumps(token_body).encode("utf-8"), headers=headers, method="POST")
                with urllib.request.urlopen(req, timeout=10) as resp:
                    token_result = json.loads(resp.read().decode("utf-8"))
                stoken = token_result.get("data", {}).get("stoken", "")
                if not stoken:
                    self._json({"error": "failed to get stoken"})
                    return
                
                # 第二步：暂时跳过转存，直接用传入的fid
                # （假设fid已经是用户网盘里的fid）
                saved_fid = fid
                
                # 第三步：调用播放API获取m3u8地址
                play_url = "https://drive-pc.quark.cn/1/clouddrive/file/v2/play?pr=ucpro&fr=pc&uc_param_str="
                play_body = {"fid": saved_fid}
                req = urllib.request.Request(play_url, data=json.dumps(play_body).encode("utf-8"), headers=headers, method="POST")
                with urllib.request.urlopen(req, timeout=10) as resp:
                    play_result = json.loads(resp.read().decode("utf-8"))
                
                # 提取m3u8地址
                m3u8_url = ""
                video_list = play_result.get("data", {}).get("video_list", [])
                for v in video_list:
                    if v.get("accessable") and v.get("video_info", {}).get("url"):
                        m3u8_url = v.get("video_info", {}).get("url", "")
                        break
                
                if not m3u8_url:
                    self._json({"error": "failed to get m3u8 url", "detail": play_result})
                    return
                
                self._json({
                    "m3u8_url": m3u8_url,
                    "file_name": play_result.get("data", {}).get("file_name", ""),
                    "size": play_result.get("data", {}).get("size", 0),
                })
            except Exception as e:
                self._json({"error": str(e)})
        elif parsed.path == "/api/aliyun-qrcode":
            # 生成阿里云盘扫码登录二维码
            try:
                import http.cookiejar
                cj = http.cookiejar.CookieJar()
                opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                }
                # 第一步：访问authorize获取SESSIONID
                req1 = urllib.request.Request(
                    "https://auth.aliyundrive.com/v2/oauth/authorize?login_type=custom&response_type=code&redirect_uri=https%3A%2F%2Fwww.aliyundrive.com%2Fsign%2Fcallback&client_id=25dzX3vbYqktVxyX&state=%7B%22origin%22%3A%22file%3A%2F%2F%22%7D",
                    headers=headers
                )
                opener.open(req1, timeout=10).close()
                # 第二步：生成二维码
                req2 = urllib.request.Request(
                    "https://passport.aliyundrive.com/newlogin/qrcode/generate.do?appName=aliyun_drive",
                    headers=headers
                )
                resp2 = opener.open(req2, timeout=10)
                data2 = json.loads(resp2.read().decode("utf-8"))
                qr_data = data2.get("content", {}).get("data", {})
                # 保存cookie和qr_data到session（全局存储）
                import threading
                import uuid
                if not hasattr(Handler, '_qr_sessions'):
                    Handler._qr_sessions = {}
                    Handler._qr_lock = threading.Lock()
                with Handler._qr_lock:
                    sid = "aliyun_" + str(uuid.uuid4())
                    Handler._qr_sessions[sid] = {
                        "cookies": "; ".join([f"{c.name}={c.value}" for c in cj]),
                        "qr_data": qr_data,
                    }
                self._json({
                    "qr_content": qr_data.get("codeContent", ""),
                    "sid": sid,
                })
            except Exception as e:
                self._json({"error": str(e)})
        elif parsed.path == "/api/aliyun-qrcode-status":
            # 查询扫码状态
            qs = urllib.parse.parse_qs(parsed.query)
            sid = qs.get("sid", [""])[0]
            if not sid:
                self._json({"error": "missing sid"})
                return
            try:
                if not hasattr(Handler, '_qr_sessions') or sid not in Handler._qr_sessions:
                    self._json({"error": "session expired"})
                    return
                session = Handler._qr_sessions[sid]
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                    "Content-Type": "application/x-www-form-urlencoded",
                    "Cookie": session["cookies"],
                }
                # POST查询状态
                post_data = urllib.parse.urlencode(session["qr_data"]).encode("utf-8")
                req = urllib.request.Request(
                    "https://passport.aliyundrive.com/newlogin/qrcode/query.do?appName=aliyun_drive",
                    data=post_data,
                    headers=headers,
                    method="POST"
                )
                resp = urllib.request.urlopen(req, timeout=10)
                result = json.loads(resp.read().decode("utf-8"))
                login_data = result.get("content", {}).get("data", {})
                status = login_data.get("qrCodeStatus", "")
                if status == "CONFIRMED":
                    # 扫码成功，提取refresh_token
                    biz_ext = login_data.get("bizExt", "")
                    if biz_ext:
                        try:
                            biz = json.loads(biz_ext)
                            refresh_token = biz.get("pds_login_result", {}).get("refreshToken", "")
                            if refresh_token:
                                # 保存到cookies文件
                                cookies_file = r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json"
                                try:
                                    with open(cookies_file, "r", encoding="utf-8") as f:
                                        cookies = json.load(f)
                                except:
                                    cookies = {}
                                cookies["aliyun_refresh_token"] = refresh_token
                                with open(cookies_file, "w", encoding="utf-8") as f:
                                    json.dump(cookies, f, ensure_ascii=False, indent=2)
                                self._json({"status": "confirmed", "refresh_token": refresh_token})
                                return
                        except:
                            pass
                    self._json({"status": "confirmed"})
                elif status == "SCANED":
                    self._json({"status": "scanned"})
                else:
                    self._json({"status": "waiting"})
            except Exception as e:
                self._json({"error": str(e)})
        elif parsed.path == "/api/quark-qrcode":
            # 生成夸克网盘扫码登录二维码
            try:
                import uuid
                # 从文件里读取我们从夸克页面提取的实时token和request_id
                session_file = r"D:\doubao\tmp\酱腌菜电商看板\quark_session.json"
                try:
                    with open(session_file, "r", encoding="utf-8") as f:
                        session_data = json.load(f)
                    token = session_data.get("token", "")
                    request_id = session_data.get("request_id", "")
                    timestamp = session_data.get("timestamp", 0)
                except:
                    token = ""
                    request_id = ""
                    timestamp = 0

                if not token:
                    # 如果没有session文件，就自己调用API生成
                    request_id = str(uuid.uuid4())
                    headers = {
                        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                        "Referer": "https://pan.quark.cn/",
                    }
                    req = urllib.request.Request(
                        f"https://uop.quark.cn/cas/ajax/getTokenForQrcodeLogin?client_id=532&v=1.2&request_id={request_id}",
                        headers=headers
                    )
                    resp = urllib.request.urlopen(req, timeout=10)
                    data = json.loads(resp.read().decode("utf-8"))
                    token = data.get("data", {}).get("members", {}).get("token", "")
                    timestamp = time.time()
                    if not token:
                        self._json({"error": "获取token失败: " + str(data)})
                        return

                # 保存session
                import threading
                if not hasattr(self, '_qr_sessions'):
                    Handler._qr_sessions = {}
                    Handler._qr_lock = threading.Lock()
                with Handler._qr_lock:
                    sid = "quark_" + str(uuid.uuid4())
                    Handler._qr_sessions[sid] = {
                        "type": "quark",
                        "token": token,
                        "request_id": request_id,
                        "timestamp": timestamp,
                    }
                # 直接返回二维码图片的URL和sid
                self._json({
                    "qr_image": "/real_quark_qr.png?v=1",
                    "sid": sid,
                })
            except Exception as e:
                self._json({"error": str(e)})
        elif parsed.path == "/api/quark-qrcode-status":
            # 查询夸克扫码状态
            qs = urllib.parse.parse_qs(parsed.query)
            sid = qs.get("sid", [""])[0]
            if not sid:
                self._json({"error": "missing sid"})
                return
            try:
                if not hasattr(Handler, '_qr_sessions') or sid not in Handler._qr_sessions:
                    self._json({"error": "session expired"})
                    return
                session = Handler._qr_sessions[sid]
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                    "Referer": "https://pan.quark.cn/",
                }
                req = urllib.request.Request(
                    f"https://uop.quark.cn/cas/ajax/getServiceTicketByQrcodeToken?client_id=532&v=1.2&request_id={session['request_id']}&token={session['token']}",
                    headers=headers
                )
                resp = urllib.request.urlopen(req, timeout=10)
                result = json.loads(resp.read().decode("utf-8"))
                # 解析状态
                data = result.get("data", {})
                members = data.get("members", {})
                status = members.get("status", 0)
                ticket = members.get("ticket", "")
                if ticket:
                    # 扫码成功
                    cookies_file = r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json"
                    try:
                        with open(cookies_file, "r", encoding="utf-8") as f:
                            cookies = json.load(f)
                    except:
                        cookies = {}
                    cookies["quark_ticket"] = ticket
                    with open(cookies_file, "w", encoding="utf-8") as f:
                        json.dump(cookies, f, ensure_ascii=False, indent=2)
                    self._json({"status": "confirmed", "ticket": ticket})
                elif status == 1:
                    self._json({"status": "scanned"})
                else:
                    self._json({"status": "waiting"})
            except Exception as e:
                self._json({"error": str(e)})
        elif parsed.path == "/api/baidu-qrcode":
            # 生成百度网盘扫码登录二维码
            try:
                import time as _time
                t = int(_time.time() * 1000)
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                    "Referer": "https://pan.baidu.com/",
                }
                req = urllib.request.Request(
                    f"https://passport.baidu.com/v2/api/getqrcode?lp=pc&client=0&login_version=2&u=https%3A%2F%2Fpan.baidu.com%2F&t={t}",
                    headers=headers
                )
                resp = urllib.request.urlopen(req, timeout=10)
                data = json.loads(resp.read().decode("utf-8"))
                imgurl = data.get("imgurl", "")
                sign = data.get("sign", "")
                if not sign:
                    self._json({"error": "获取sign失败: " + str(data)})
                    return
                # 保存session
                import threading
                import uuid
                if not hasattr(Handler, '_qr_sessions'):
                    Handler._qr_sessions = {}
                    Handler._qr_lock = threading.Lock()
                with Handler._qr_lock:
                    sid = "baidu_" + str(uuid.uuid4())
                    Handler._qr_sessions[sid] = {
                        "type": "baidu",
                        "sign": sign,
                        "imgurl": "https://" + imgurl,
                    }
                self._json({
                    "qr_img": "https://" + imgurl,
                    "sid": sid,
                })
            except Exception as e:
                self._json({"error": str(e)})
        elif parsed.path == "/api/baidu-qrcode-status":
            # 查询百度扫码状态
            qs = urllib.parse.parse_qs(parsed.query)
            sid = qs.get("sid", [""])[0]
            if not sid:
                self._json({"error": "missing sid"})
                return
            try:
                if not hasattr(Handler, '_qr_sessions') or sid not in Handler._qr_sessions:
                    self._json({"error": "session expired"})
                    return
                session = Handler._qr_sessions[sid]
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                    "Referer": "https://pan.baidu.com/",
                }
                req = urllib.request.Request(
                    f"https://passport.baidu.com/channel/unicast?channel_id={session['sign']}&callback=this_is_callback&t={int(__import__('time').time()*1000)}",
                    headers=headers
                )
                try:
                    resp = urllib.request.urlopen(req, timeout=5)
                    result_text = resp.read().decode("utf-8")
                    # 解析JSONP
                    m = re.search(r'this_is_callback\((.*)\)', result_text)
                    if m:
                        result = json.loads(m.group(1))
                        channel_v = result.get("channel_v", "{}")
                        try:
                            cv = json.loads(channel_v)
                            status = cv.get("status", 0)
                            if status == 0:
                                self._json({"status": "waiting"})
                            elif status == 1:
                                self._json({"status": "scanned"})
                            else:
                                # 扫码成功
                                bduss = cv.get("v", "")
                                cookies_file = r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json"
                                try:
                                    with open(cookies_file, "r", encoding="utf-8") as f:
                                        cookies = json.load(f)
                                except:
                                    cookies = {}
                                cookies["baidu_bduss"] = bduss
                                with open(cookies_file, "w", encoding="utf-8") as f:
                                    json.dump(cookies, f, ensure_ascii=False, indent=2)
                                self._json({"status": "confirmed", "bduss": bduss})
                        except:
                            self._json({"status": "waiting"})
                    else:
                        self._json({"status": "waiting"})
                except Exception as te:
                    # 超时是正常的，说明还在等待扫码
                    self._json({"status": "waiting"})
            except Exception as e:
                self._json({"error": str(e)})
        elif parsed.path == "/api/pan-cookies":
            # 获取或设置网盘cookies
            cookies_file = r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json"
            if self.command == "GET":
                try:
                    with open(cookies_file, "r", encoding="utf-8") as f:
                        self._json(json.load(f))
                except:
                    self._json({"aliyun": "", "quark": "", "uc": "", "baidu": ""})
            else:
                try:
                    length = int(self.headers.get("Content-Length", 0))
                    body = self.rfile.read(length)
                    data = json.loads(body.decode("utf-8"))
                    with open(cookies_file, "w", encoding="utf-8") as f:
                        json.dump(data, f, ensure_ascii=False, indent=2)
                    self._json({"ok": True})
                except Exception as e:
                    self._json({"error": str(e)}, code=500)
        elif parsed.path == "/api/pan-parse":
            # 解析网盘分享链接
            qs = urllib.parse.parse_qs(parsed.query)
            pan_url = qs.get("url", [""])[0]
            if not pan_url:
                self._json({"error": "missing url"}, code=400)
                return
            # 读取cookies
            cookies_file = r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json"
            try:
                with open(cookies_file, "r", encoding="utf-8") as f:
                    cookies = json.load(f)
            except:
                cookies = {}
            try:
                if "alipan.com" in pan_url or "aliyundrive.com" in pan_url:
                    result = self._parse_aliyun(pan_url, cookies.get("aliyun", ""))
                elif "quark.cn" in pan_url or "quark.com" in pan_url:
                    result = self._parse_quark(pan_url, cookies.get("quark", ""))
                elif "uc.cn" in pan_url:
                    result = self._parse_uc(pan_url, cookies.get("uc", ""))
                elif "pan.baidu.com" in pan_url or "baidu.com" in pan_url:
                    result = self._parse_baidu(pan_url, cookies.get("baidu", ""))
                else:
                    result = {"error": "不支持的网盘类型"}
                self._json(result)
            except Exception as e:
                self._json({"error": str(e), "files": []})
        else:
            self._json({"error": "not found"}, code=404)

    def do_POST(self):
        parsed = urllib.parse.urlparse(self.path)
        if parsed.path.startswith("/lxmusic/"):
            if _lx is None:
                self.send_error(503, "lxmusic not loaded")
                return
            self._json = lambda status, payload: _lx.Handler._json(self, status, payload)
            self._text = lambda t, ct="text/plain; charset=utf-8": _lx.Handler._text(self, t, ct)
            _lx.Handler.do_POST(self)
            return
        elif parsed.path == "/api/pan-cookies":
            # 设置网盘cookies
            cookies_file = r"D:\doubao\tmp\酱腌菜电商看板\pan_cookies.json"
            try:
                length = int(self.headers.get("Content-Length", 0))
                body = self.rfile.read(length)
                data = json.loads(body.decode("utf-8"))
                with open(cookies_file, "w", encoding="utf-8") as f:
                    json.dump(data, f, ensure_ascii=False, indent=2)
                self._json({"ok": True})
            except Exception as e:
                self._json({"error": str(e)}, code=500)
            return
        self._json({"error": "not found"}, code=404)

    def _json(self, obj, code=200):
        body = json.dumps(obj, ensure_ascii=False).encode("utf-8")
        self.send_response(code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, *args):
        pass  # 静默

    def _parse_aliyun(self, pan_url, cookie):
        """解析阿里云盘分享链接"""
        # 提取share_id
        m = re.search(r'/s/([A-Za-z0-9]+)', pan_url)
        if not m:
            return {"error": "无法解析分享ID", "files": []}
        share_id = m.group(1)
        # 获取share_token
        headers = {
            "User-Agent": HEADERS["User-Agent"],
            "Content-Type": "application/json",
            "Origin": "https://www.alipan.com",
            "Referer": "https://www.alipan.com/",
        }
        if cookie:
            headers["Cookie"] = cookie
        req = urllib.request.Request(
            "https://api.aliyundrive.com/v2/share_link/get_share_token",
            data=json.dumps({"share_id": share_id, "share_pwd": ""}).encode("utf-8"),
            headers=headers,
            method="POST"
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            token_data = json.loads(resp.read().decode("utf-8"))
        share_token = token_data.get("share_token", "")
        if not share_token:
            return {"error": "获取分享token失败: " + str(token_data), "files": []}
        # 列出文件
        headers["x-share-token"] = share_token
        req2 = urllib.request.Request(
            "https://api.aliyundrive.com/adrive/v1.0/share_link/list",
            data=json.dumps({
                "share_id": share_id,
                "parent_file_id": token_data.get("share_file_id", ""),
                "limit": 100,
                "order_by": "name",
                "order_direction": "ASC"
            }).encode("utf-8"),
            headers=headers,
            method="POST"
        )
        with urllib.request.urlopen(req2, timeout=10) as resp:
            list_data = json.loads(resp.read().decode("utf-8"))
        files = []
        for f in list_data.get("items", []):
            files.append({
                "name": f.get("name", ""),
                "file_id": f.get("file_id", ""),
                "type": f.get("type", ""),
                "size": f.get("size", 0),
                "category": f.get("category", ""),
            })
        return {"files": files, "share_id": share_id, "share_token": share_token}

    def _parse_quark(self, pan_url, cookie):
        """解析夸克网盘分享链接"""
        import time
        m = re.search(r'/s/([A-Za-z0-9]+)', pan_url)
        if not m:
            return {"error": "无法解析分享ID", "files": []}
        pwd_id = m.group(1)
        headers = {
            "User-Agent": HEADERS["User-Agent"],
            "Content-Type": "application/json",
            "Origin": "https://pan.quark.cn",
            "Referer": f"https://pan.quark.cn/s/{pwd_id}",
        }
        if cookie:
            headers["Cookie"] = cookie
        # 获取stoken
        ts = int(time.time() * 1000)
        params1 = f"?pr=ucpro&fr=pc&__dt={ts%1000}&__t={ts}"
        req = urllib.request.Request(
            f"https://drive-h.quark.cn/1/clouddrive/share/sharepage/token{params1}",
            data=json.dumps({"pwd_id": pwd_id, "passcode": ""}).encode("utf-8"),
            headers=headers,
            method="POST"
        )
        try:
            with urllib.request.urlopen(req, timeout=10) as resp:
                token_data = json.loads(resp.read().decode("utf-8"))
        except Exception as e:
            return {"error": f"获取stoken失败: {e}", "files": []}
        stoken = token_data.get("data", {}).get("stoken", "")
        if not stoken:
            return {"error": "获取stoken失败: " + str(token_data), "files": []}
        # 列出文件
        ts2 = int(time.time() * 1000)
        params2 = f"?pr=ucpro&fr=pc&pwd_id={pwd_id}&pdir_fid=0&force=0&_page=1&_size=50&_fetch_banner=1&_fetch_share=1&_fetch_total=1&_sort=file_type:asc,file_name:asc&stoken={urllib.parse.quote(stoken)}&__dt={ts2%1000}&__t={ts2}"
        headers2 = {
            "User-Agent": HEADERS["User-Agent"],
            "Referer": f"https://pan.quark.cn/s/{pwd_id}",
        }
        if cookie:
            headers2["Cookie"] = cookie
        req2 = urllib.request.Request(
            f"https://drive-h.quark.cn/1/clouddrive/share/sharepage/detail{params2}",
            headers=headers2,
            method="GET"
        )
        try:
            with urllib.request.urlopen(req2, timeout=10) as resp:
                list_data = json.loads(resp.read().decode("utf-8"))
        except Exception as e:
            return {"error": f"获取文件列表失败: {e}", "files": []}
        files = []
        for f in list_data.get("data", {}).get("list", []):
            files.append({
                "name": f.get("file_name", ""),
                "file_id": f.get("fid", ""),
                "type": "folder" if f.get("dir") else "file",
                "size": f.get("size", 0),
                "category": f.get("file_category", ""),
            })
        return {"files": files, "pwd_id": pwd_id}

    def _parse_uc(self, pan_url, cookie):
        """解析UC网盘分享链接"""
        m = re.search(r'/s/([A-Za-z0-9]+)', pan_url)
        if not m:
            return {"error": "无法解析分享ID", "files": []}
        share_id = m.group(1)
        return {"error": "UC网盘解析暂未实现", "files": [], "share_id": share_id}

    def _parse_baidu(self, pan_url, cookie):
        """解析百度网盘分享链接"""
        m = re.search(r'/s/([A-Za-z0-9_-]+)', pan_url)
        if not m:
            return {"error": "无法解析分享ID", "files": []}
        share_id = m.group(1)
        
        # 提取提取码
        pwd = ""
        pwd_match = re.search(r'pwd=([A-Za-z0-9]+)', pan_url)
        if pwd_match:
            pwd = pwd_match.group(1)
        
        headers = {
            "User-Agent": HEADERS["User-Agent"],
            "Content-Type": "application/x-www-form-urlencoded",
            "Referer": "https://pan.baidu.com/",
        }
        if cookie:
            headers["Cookie"] = cookie
        
        # 验证分享链接（带提取码）
        post_data = f"pwd={pwd}".encode("utf-8")
        req = urllib.request.Request(
            f"https://pan.baidu.com/share/verify?surl={share_id}&t=0&channel=chunlei&web=1&app_id=250528&clienttype=0",
            data=post_data,
            headers=headers,
            method="POST"
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            verify_data = json.loads(resp.read().decode("utf-8"))
        
        sign = verify_data.get("data", {}).get("sign", "")
        timestamp = verify_data.get("data", {}).get("timestamp", "")
        if not sign:
            return {"error": f"百度网盘验证失败（errno: {verify_data.get('errno')}）", "files": []}
        
        # 列出文件
        req2 = urllib.request.Request(
            f"https://pan.baidu.com/share/list?uk=&shareid=&surl={share_id}&sign={sign}&timestamp={timestamp}&page=1&num=100&dir=/&order=name&desc=0&clienttype=0&app_id=250528&web=1",
            headers=headers,
            method="GET"
        )
        with urllib.request.urlopen(req2, timeout=10) as resp:
            list_data = json.loads(resp.read().decode("utf-8"))
        
        files = []
        for f in list_data.get("list", []):
            files.append({
                "name": f.get("server_filename", ""),
                "file_id": f.get("fs_id", ""),
                "type": "folder" if f.get("isdir") else "file",
                "size": f.get("size", 0),
                "category": f.get("category", ""),
                "dlink": f.get("dlink", ""),
            })
        return {"files": files, "share_id": share_id, "pwd": pwd}


if __name__ == "__main__":
    server = ThreadingHTTPServer(("0.0.0.0", 8000), Handler)
    print("SOTVLA proxy on http://0.0.0.0:8000")
    server.serve_forever()
