MyJAV

MyJAV:提供中文详情体验,并联动个人 JAV 管理与磁力资源查询

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         MyJAV
// @namespace    https://myjav.vercel.app/
// @version      0.1.5
// @description  MyJAV:提供中文详情体验,并联动个人 JAV 管理与磁力资源查询
// @match        https://www.javbus.com/*
// @match        https://javdb.com/*
// @match        https://www.javdb.com/*
// @match        https://myjav.vercel.app/v/*
// @match        http://localhost:3456/v/*
// @run-at       document-idle
// @require      https://update.greasyfork.org/scripts/515994/1478507/gh_2215_make_GM_xhr_more_parallel_again.js
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @connect      localhost
// @connect      127.0.0.1
// @connect      myjav.vercel.app
// @connect      btdig.com
// @connect      www.btdig.com
// @connect      sukebei.nyaa.si
// @connect      *
// ==/UserScript==

// Compiled from tampermonkey/src/main.ts. Do not edit this file directly.
"use strict";
var MyJAVUserscript = (() => {
  // lib/public-code-token.ts
  var TOKEN_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  var SOURCE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
  var DEFAULT_OBFUSCATION_KEY = "javstash-public-view";
  function readEnv(name) {
    var _a, _b;
    const processLike = globalThis;
    return (_b = (_a = processLike.process) == null ? void 0 : _a.env) == null ? void 0 : _b[name];
  }
  function getObfuscationKey() {
    return readEnv("NEXT_PUBLIC_PUBLIC_CODE_OBFUSCATION_KEY") || readEnv("PUBLIC_CODE_OBFUSCATION_KEY") || DEFAULT_OBFUSCATION_KEY;
  }
  function createSeed(input) {
    let hash = 2166136261;
    for (let index = 0; index < input.length; index += 1) {
      hash ^= input.charCodeAt(index);
      hash = Math.imul(hash, 16777619);
    }
    return hash >>> 0;
  }
  function createPrng(seed) {
    let value = seed || 1831565813;
    return () => {
      value = Math.imul(value ^ value >>> 15, value | 1);
      value ^= value + Math.imul(value ^ value >>> 7, value | 61);
      return ((value ^ value >>> 14) >>> 0) / 4294967296;
    };
  }
  function buildTokenPairs() {
    const pairs = [];
    for (const first of TOKEN_ALPHABET) {
      for (const second of TOKEN_ALPHABET) {
        pairs.push(`${first}${second}`);
      }
    }
    const prng = createPrng(createSeed(`${getObfuscationKey()}:pairs`));
    for (let index = pairs.length - 1; index > 0; index -= 1) {
      const swapIndex = Math.floor(prng() * (index + 1));
      [pairs[index], pairs[swapIndex]] = [pairs[swapIndex], pairs[index]];
    }
    return pairs;
  }
  var tokenPairs = buildTokenPairs();
  var encodeMap = new Map(
    SOURCE_ALPHABET.split("").map((char, index) => [char, tokenPairs[index]])
  );
  var decodeMap = new Map(
    SOURCE_ALPHABET.split("").map((char, index) => [tokenPairs[index], char])
  );
  function encodePublicCodeToken(code) {
    return code.split("").map((char) => {
      var _a;
      return (_a = encodeMap.get(char)) != null ? _a : char;
    }).join("");
  }
  function decodePublicCodeToken(token) {
    if (!token.trim() || token.length % 2 !== 0 || /[^A-Za-z]/.test(token)) {
      return null;
    }
    let decoded = "";
    for (let index = 0; index < token.length; index += 2) {
      const chunk = token.slice(index, index + 2);
      const sourceChar = decodeMap.get(chunk);
      if (!sourceChar) {
        return null;
      }
      decoded += sourceChar;
    }
    return decoded;
  }

  // src/browser/userscript-contract.ts
  var DEFAULT_PROXY_ORIGIN = "https://myjav.vercel.app";
  var LOCAL_DEV_PROXY_ORIGIN = "http://localhost:3456";
  var LOOKUP_CACHE_KEY_PREFIX = "javstash_lookup_cache_v1:";
  var LOOKUP_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
  var LOOKUP_PATH = "/api/browser/lookup";
  var MAGNETS_PATH = "/api/browser/magnets";
  var ITEM_TAGS_PATH = "/api/admin/item-tags";
  var SCENE_CODE_TEXT_PATTERN = /\b([A-Za-z0-9]{2,10}-\d{2,6})\b/;
  var OWN_PUBLIC_DETAIL_URL_PATTERN = /^https?:\/\/(?:myjav\.vercel\.app|localhost:3456)\/v\/[A-Za-z0-9]+(?:[/?#].*)?$/i;
  function normalizeSceneCode(code) {
    return String(code || "").trim().toUpperCase();
  }
  function normalizeOrigin(origin) {
    return String(origin || "").trim().replace(/\/+$/, "");
  }
  function getProxyOriginCandidates(savedOrigin, currentOrigin = DEFAULT_PROXY_ORIGIN) {
    const configured = normalizeOrigin(savedOrigin);
    if (configured) return [configured];
    const current = normalizeOrigin(currentOrigin);
    if (current === LOCAL_DEV_PROXY_ORIGIN || current === DEFAULT_PROXY_ORIGIN) {
      return [current];
    }
    return [LOCAL_DEV_PROXY_ORIGIN, DEFAULT_PROXY_ORIGIN];
  }
  function extractJavbusCode(url) {
    const match = String(url).match(/^https?:\/\/www\.javbus\.com\/([A-Za-z0-9]{2,10}-\d{2,6})\/?$/i);
    return match ? normalizeSceneCode(match[1]) : null;
  }
  function isJavdbDetailUrl(url) {
    return /^https?:\/\/(?:www\.)?javdb\.com\/v\/[A-Za-z0-9]+(?:[/?#].*)?$/i.test(String(url));
  }
  function isOwnPublicDetailUrl(url) {
    return OWN_PUBLIC_DETAIL_URL_PATTERN.test(String(url));
  }
  function extractOwnPublicCode(url) {
    if (!isOwnPublicDetailUrl(url)) return null;
    const tokenMatch = String(url).match(/\/v\/([A-Za-z0-9]+)(?:[/?#].*)?$/i);
    const decoded = (tokenMatch == null ? void 0 : tokenMatch[1]) ? decodePublicCodeToken(tokenMatch[1]) : null;
    return decoded ? normalizeSceneCode(decoded) : null;
  }
  function shouldInjectInlineMagnetPanel(url) {
    return isOwnPublicDetailUrl(url);
  }
  function extractCodeFromText(text) {
    const match = String(text || "").match(SCENE_CODE_TEXT_PATTERN);
    return match ? normalizeSceneCode(match[1]) : null;
  }
  function buildLookupUrl(proxyOrigin, code) {
    const url = new URL(LOOKUP_PATH, proxyOrigin);
    url.searchParams.set("code", normalizeSceneCode(code));
    return url.toString();
  }
  function buildMagnetsUrl(proxyOrigin, code) {
    const url = new URL(MAGNETS_PATH, proxyOrigin);
    if (code) {
      url.searchParams.set("code", normalizeSceneCode(code));
    }
    return url.toString();
  }
  function buildItemTagsUrl(proxyOrigin, code) {
    const url = new URL(ITEM_TAGS_PATH, proxyOrigin);
    if (code) {
      url.searchParams.set("code", normalizeSceneCode(code));
    }
    return url.toString();
  }
  function buildLookupCacheKey(code) {
    return LOOKUP_CACHE_KEY_PREFIX + normalizeSceneCode(code);
  }
  function createLookupCacheEntry(payload, now = Date.now()) {
    return {
      code: normalizeSceneCode(payload.code),
      title: payload.title,
      description: payload.description,
      cachedAt: now,
      expiresAt: now + LOOKUP_CACHE_TTL_MS
    };
  }
  function readValidLookupCacheEntry(value, now = Date.now()) {
    if (!value || typeof value !== "object") {
      return null;
    }
    const candidate = value;
    if (typeof candidate.code !== "string" || typeof candidate.title !== "string" || typeof candidate.description !== "string" || typeof candidate.cachedAt !== "number" || typeof candidate.expiresAt !== "number" || candidate.expiresAt <= now) {
      return null;
    }
    return {
      code: normalizeSceneCode(candidate.code),
      title: candidate.title,
      description: candidate.description,
      cachedAt: candidate.cachedAt,
      expiresAt: candidate.expiresAt
    };
  }

  // tampermonkey/src/pages/detect.ts
  function findJavdbCodeValueElement() {
    const blocks = Array.from(document.querySelectorAll(".video-detail .movie-panel-info .panel-block"));
    for (const block of blocks) {
      const label = block.querySelector("strong");
      const labelText = String((label == null ? void 0 : label.textContent) || "").trim();
      if (labelText !== "\u756A\u865F:" && labelText !== "\u756A\u53F7:" && labelText !== "\u756A\u53F7") {
        continue;
      }
      const value = block.querySelector(".value");
      if (value) return value;
    }
    return null;
  }
  function extractJavdbCodeFromPage() {
    const value = findJavdbCodeValueElement();
    return value ? extractCodeFromText(value.textContent || "") : null;
  }
  function detectCode() {
    if (isJavdbDetailUrl(window.location.href)) {
      return extractJavdbCodeFromPage() || extractCodeFromText(document.body.innerText || "");
    }
    if (isOwnPublicDetailUrl(window.location.href)) {
      return extractOwnPublicCode(window.location.href) || extractCodeFromText(document.body.innerText || "");
    }
    return extractJavbusCode(window.location.href) || extractCodeFromText(document.body.innerText || "");
  }
  function findInjectionTarget(code) {
    if (isJavdbDetailUrl(window.location.href)) {
      const value = findJavdbCodeValueElement();
      if (value && normalizeSceneCode(value.textContent || "").includes(code)) {
        return value;
      }
    }
    if (isOwnPublicDetailUrl(window.location.href)) {
      const copyableCodeButtons = Array.from(document.querySelectorAll('button[title="\u70B9\u51FB\u590D\u5236\u756A\u53F7"]'));
      const codeButton = copyableCodeButtons.find((button) => normalizeSceneCode(button.textContent || "") === code);
      if (codeButton) {
        return codeButton;
      }
    }
    const infoParagraphs = Array.from(document.querySelectorAll(".info p"));
    for (const paragraph of infoParagraphs) {
      const label = paragraph.querySelector("span.header");
      const labelText = String((label == null ? void 0 : label.textContent) || "").trim();
      if (labelText !== "\u8B58\u5225\u78BC:" && labelText !== "\u8BC6\u522B\u7801:" && labelText !== "\u8B58\u5225\u78BC") {
        continue;
      }
      const spans = Array.from(paragraph.querySelectorAll("span"));
      const codeSpan = spans.find((span) => normalizeSceneCode(span.textContent || "") === code);
      if (codeSpan) return codeSpan;
    }
    const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
      acceptNode(node) {
        const text = String(node.textContent || "");
        if (!text || !text.includes(code)) return NodeFilter.FILTER_SKIP;
        const parent = node.parentElement;
        if (!parent) return NodeFilter.FILTER_SKIP;
        const tagName = parent.tagName;
        if (tagName === "SCRIPT" || tagName === "STYLE" || tagName === "NOSCRIPT") {
          return NodeFilter.FILTER_SKIP;
        }
        return NodeFilter.FILTER_ACCEPT;
      }
    });
    const textNode = walker.nextNode();
    return textNode ? textNode.parentElement : null;
  }

  // tampermonkey/src/config.ts
  var PERSONAL_API_KEY_STORAGE_KEY = "javstash_personal_api_key";
  var BUTTON_CLASS = "javstash-lookup-button";
  var PANEL_ID = "javstash-lookup-panel";
  var ZH_REGEX = /中文|字幕|中字|(-|_)c(?!d)/i;
  var FC2_REGEX = /^FC2-/i;

  // tampermonkey/src/gm/http.ts
  function formatGmError(error, url) {
    const parts = [
      error == null ? void 0 : error.message,
      error == null ? void 0 : error.error,
      error == null ? void 0 : error.details,
      typeof (error == null ? void 0 : error.status) === "number" ? `status=${error.status}` : "",
      (error == null ? void 0 : error.statusText) ? `statusText=${error.statusText}` : "",
      (error == null ? void 0 : error.finalUrl) ? `finalUrl=${error.finalUrl}` : "",
      typeof (error == null ? void 0 : error.readyState) === "number" ? `readyState=${error.readyState}` : ""
    ].filter(Boolean);
    return parts.length > 0 ? `\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25: ${parts.join(", ")}` : `\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25: ${url}`;
  }
  function parseJsonResponse(response, url) {
    const text = response.responseText || "{}";
    if (text.trim().startsWith("<")) {
      throw new Error(`MyJAV \u63A5\u53E3\u8FD4\u56DE\u4E86 HTML \u9875\u9762\uFF1A${url}\u3002\u8BF7\u68C0\u67E5\u670D\u52A1\u5730\u5740\u662F\u5426\u6B63\u786E\u6216\u65B0\u63A5\u53E3\u662F\u5426\u5DF2\u90E8\u7F72\u3002`);
    }
    try {
      return JSON.parse(text);
    } catch (error) {
      throw new Error(`MyJAV \u63A5\u53E3\u8FD4\u56DE\u4E86\u975E JSON \u5185\u5BB9\uFF1A${url}`);
    }
  }
  function normalizeUserFacingError(message) {
    if (/Missing JavStash ApiKey/i.test(message)) {
      return "\u5F53\u524D\u8FDE\u63A5\u7684 MyJAV \u670D\u52A1\u4ECD\u5728\u4F7F\u7528\u65E7\u7248\u9274\u6743\u903B\u8F91\uFF0C\u8BF7\u68C0\u67E5\u6CB9\u7334\u4E2D\u7684\u201CMyJAV \u670D\u52A1\u5730\u5740\u201D\u662F\u5426\u6307\u5411\u6700\u65B0\u670D\u52A1\u3002";
    }
    return message;
  }
  function requestJson({
    method,
    url,
    headers,
    data,
    timeout = 15e3
  }) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method,
        url,
        headers,
        data,
        timeout,
        onload(response) {
          try {
            const payload = parseJsonResponse(response, url);
            if (response.status >= 400 || payload.ok === false) {
              reject(new Error(normalizeUserFacingError(payload.message || payload.error || payload.code || "\u8BF7\u6C42\u5931\u8D25")));
              return;
            }
            resolve(payload);
          } catch (error) {
            reject(error instanceof Error ? error : new Error("\u89E3\u6790\u54CD\u5E94\u5931\u8D25"));
          }
        },
        ontimeout() {
          reject(new Error("\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"));
        },
        onerror(error) {
          reject(new Error(formatGmError(error, url)));
        }
      });
    });
  }
  function requestDocument(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url,
        timeout: 3e4,
        onload(response) {
          if (response.status >= 400) {
            reject(new Error("\u8BF7\u6C42\u5931\u8D25\uFF1A" + response.status));
            return;
          }
          const html = String(response.responseText || response.response || "").trim();
          if (!html) {
            reject(new Error("\u8FD4\u56DE\u4E86\u7A7A\u9875\u9762"));
            return;
          }
          resolve(new DOMParser().parseFromString(html, "text/html"));
        },
        ontimeout() {
          reject(new Error("\u8BF7\u6C42\u8D85\u65F6"));
        },
        onerror(error) {
          reject(new Error(formatGmError(error, url)));
        }
      });
    });
  }

  // tampermonkey/src/storage.ts
  function getPersonalApiKey() {
    return String(GM_getValue(PERSONAL_API_KEY_STORAGE_KEY, "") || "").trim();
  }
  function getProxyOrigin() {
    return DEFAULT_PROXY_ORIGIN;
  }
  function getProxyOriginCandidatesFromStorage() {
    return getProxyOriginCandidates(DEFAULT_PROXY_ORIGIN, DEFAULT_PROXY_ORIGIN);
  }
  function readLookupCache(code) {
    const cacheKey = buildLookupCacheKey(code);
    const cached = GM_getValue(cacheKey, null);
    const entry = readValidLookupCacheEntry(cached);
    if (!entry) {
      if (cached !== null) {
        GM_setValue(cacheKey, null);
      }
      return null;
    }
    return entry;
  }
  function writeLookupCache(payload) {
    const entry = createLookupCacheEntry(payload);
    GM_setValue(buildLookupCacheKey(entry.code), entry);
    return entry;
  }

  // tampermonkey/src/api/javstash.ts
  function requestLookup(code) {
    return requestWithProxyCandidates(
      (proxyOrigin) => requestJson({
        method: "GET",
        url: buildLookupUrl(proxyOrigin, code)
      })
    );
  }
  function requestMagnetWrite(code, sources) {
    return requestWithProxyCandidates(
      (proxyOrigin) => requestJson({
        method: "POST",
        url: buildMagnetsUrl(proxyOrigin),
        headers: {
          "Content-Type": "application/json"
        },
        data: JSON.stringify({
          code: normalizeSceneCode(code),
          sources
        })
      })
    );
  }
  function requestUserItemTags(code, personalApiKey) {
    return requestWithProxyCandidates(
      (proxyOrigin) => requestJson({
        method: "GET",
        url: buildItemTagsUrl(proxyOrigin, code),
        headers: {
          Authorization: `Bearer ${personalApiKey}`
        }
      })
    );
  }
  function requestUpsertUserItemTag(code, tag, personalApiKey) {
    return requestWithProxyCandidates(
      (proxyOrigin) => requestJson({
        method: "PUT",
        url: buildItemTagsUrl(proxyOrigin),
        headers: {
          Authorization: `Bearer ${personalApiKey}`,
          "Content-Type": "application/json"
        },
        data: JSON.stringify({
          code: normalizeSceneCode(code),
          tag
        })
      })
    );
  }
  function requestDeleteUserItemTag(code, tag, personalApiKey) {
    return requestWithProxyCandidates(
      (proxyOrigin) => requestJson({
        method: "DELETE",
        url: buildItemTagsUrl(proxyOrigin),
        headers: {
          Authorization: `Bearer ${personalApiKey}`,
          "Content-Type": "application/json"
        },
        data: JSON.stringify({
          code: normalizeSceneCode(code),
          tag
        })
      })
    );
  }
  async function requestWithProxyCandidates(request) {
    const candidates = getProxyOriginCandidatesFromStorage();
    let lastError;
    const failures = [];
    for (const proxyOrigin of candidates) {
      try {
        return await request(proxyOrigin);
      } catch (error) {
        lastError = error;
        failures.push(`${proxyOrigin} => ${error instanceof Error ? error.message : "MyJAV \u8BF7\u6C42\u5931\u8D25"}`);
      }
    }
    if (lastError instanceof Error) {
      throw new Error(`${lastError.message}${failures.length > 0 ? ` | \u5C1D\u8BD5\u94FE\u8DEF: ${failures.join(" ; ")}` : ""}`);
    }
    throw new Error(failures.length > 0 ? `MyJAV \u8BF7\u6C42\u5931\u8D25 | \u5C1D\u8BD5\u94FE\u8DEF: ${failures.join(" ; ")}` : "MyJAV \u8BF7\u6C42\u5931\u8D25");
  }

  // tampermonkey/src/magnets/normalize.ts
  function codePatternForScene(code) {
    const fc2Joiner = FC2_REGEX.test(code) ? "|_" : "";
    const parts = code.trim().toUpperCase().split("-").map((item, index) => {
      return index ? item.replace(/^0/, "") : item;
    });
    return new RegExp("(?<![a-z])" + parts.join("\\s?(0|-" + fc2Joiner + "){0,4}\\s?") + "(?!\\d)", "i");
  }
  function parseSizeToBytes(sizeStr = "") {
    const match = String(sizeStr).match(/\d+\.?\d*/);
    const size = match ? Number(match[0]) : 0;
    if (!Number.isFinite(size) || size <= 0) return 0;
    if (/gib/i.test(sizeStr)) return size * 1024 ** 3;
    if (/mib/i.test(sizeStr)) return size * 1024 ** 2;
    if (/kib/i.test(sizeStr)) return size * 1024;
    if (/gb/i.test(sizeStr)) return size * 1e3 ** 3;
    if (/mb/i.test(sizeStr)) return size * 1e3 ** 2;
    if (/kb/i.test(sizeStr)) return size * 1e3;
    if (/byte/i.test(sizeStr)) return size;
    return 0;
  }

  // tampermonkey/src/magnets/sources.ts
  function queryRows(doc, selectors) {
    const list = Array.isArray(selectors) ? selectors : [selectors];
    for (const selector of list) {
      const rows = Array.from(doc.querySelectorAll(selector));
      if (rows.length > 0) return rows;
    }
    return [];
  }
  function extractMagnetRows(code, source, host, search, selectors, filter) {
    const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
    return requestDocument(host + search).then((doc) => {
      const items = parseMagnetRows(doc, code, host, selectors, filter);
      return {
        source,
        status: items.length ? "success" : "empty",
        items,
        checkedAt
      };
    }).catch((error) => ({
      source,
      status: "error",
      items: [],
      checkedAt,
      error: error instanceof Error ? error.message : "\u67E5\u8BE2\u5931\u8D25"
    }));
  }
  function parseMagnetRows(doc, code, host, selectors, filter) {
    const regex = codePatternForScene(code);
    return queryRows(doc, selectors).reduce((acc, row) => {
      const name = String(filter.name(row) || "").replace(/[\u200B-\u200D\uFEFF]/g, "").trim();
      if (!name || !regex.test(name)) return acc;
      const link = String(filter.link(row) || "").split("&")[0];
      if (!link.toLowerCase().startsWith("magnet:?xt=")) return acc;
      const size = String(filter.size(row) || "").trim();
      let href = String(filter.href(row) || "").trim();
      if (href && !href.includes("//")) {
        href = host + href.replace(/^\//, "");
      }
      acc.push({
        name,
        link,
        size,
        bytes: parseSizeToBytes(size),
        date: String(filter.date(row) || "").trim(),
        href: href || void 0,
        zh: ZH_REGEX.test(name)
      });
      return acc;
    }, []);
  }
  function fetchSukebeiMagnets(code) {
    return extractMagnetRows(
      code,
      "sukebei",
      "https://sukebei.nyaa.si/",
      "?f=0&c=0_0&q=" + encodeURIComponent(code),
      [".torrent-list tbody tr", ".table-responsive table tbody tr"],
      {
        name: (row) => {
          var _a, _b;
          return ((_a = row.querySelector("td:nth-child(2) a[title]")) == null ? void 0 : _a.getAttribute("title")) || ((_b = row.querySelector("td:nth-child(2) a")) == null ? void 0 : _b.textContent);
        },
        link: (row) => {
          var _a, _b;
          return ((_a = row.querySelector("td:nth-child(3) a[href^='magnet:']")) == null ? void 0 : _a.href) || ((_b = row.querySelector("td:nth-child(3) a:last-child")) == null ? void 0 : _b.href);
        },
        size: (row) => {
          var _a;
          return (_a = row.querySelector("td:nth-child(4)")) == null ? void 0 : _a.textContent;
        },
        date: (row) => {
          var _a;
          return String(((_a = row.querySelector("td:nth-child(5)")) == null ? void 0 : _a.textContent) || "").split(" ")[0];
        },
        href: (row) => {
          var _a, _b;
          return ((_a = row.querySelector("td:nth-child(2) a[title]")) == null ? void 0 : _a.getAttribute("href")) || ((_b = row.querySelector("td:nth-child(2) a")) == null ? void 0 : _b.getAttribute("href"));
        }
      }
    );
  }
  function fetchBTDiggMagnets(code, useWww = false) {
    const host = "https://" + (useWww ? "www." : "") + "btdig.com/";
    return extractMagnetRows(
      code,
      "btdig",
      host,
      "search?order=0&q=" + encodeURIComponent(code),
      ".one_result",
      {
        name: (row) => {
          var _a;
          return (_a = row.querySelector(".torrent_name")) == null ? void 0 : _a.textContent;
        },
        link: (row) => {
          var _a;
          return (_a = row.querySelector(".torrent_magnet a")) == null ? void 0 : _a.href;
        },
        size: (row) => {
          var _a;
          return (_a = row.querySelector(".torrent_size")) == null ? void 0 : _a.textContent;
        },
        date: (row) => {
          var _a;
          return (_a = row.querySelector(".torrent_age")) == null ? void 0 : _a.textContent;
        },
        href: (row) => {
          var _a;
          return (_a = row.querySelector(".torrent_name a")) == null ? void 0 : _a.href;
        }
      }
    ).then((result) => {
      if (!useWww && (result.status === "empty" || result.status === "error")) {
        return fetchBTDiggMagnets(code, true);
      }
      return result;
    });
  }

  // tampermonkey/src/ui/panel.ts
  var USER_TAGS = ["watch_later", "favorite", "dislike"];
  var USER_TAG_LABELS = {
    watch_later: "\u7A0D\u540E\u770B",
    favorite: "\u6536\u85CF",
    dislike: "dislike"
  };
  var PANEL_STYLE_ID = "javstash-lookup-panel-style";
  var INLINE_MAGNET_ACTIONS_ID = "javstash-inline-magnet-actions";
  var inlineMagnetObserver = null;
  var inlineMagnetRetryTimer = null;
  function buildPublicDetailUrl(code) {
    if (!code) return null;
    return `${DEFAULT_PROXY_ORIGIN}/v/${encodePublicCodeToken(normalizeSceneCode(code))}`;
  }
  function getTagsForCode(payload, code) {
    var _a, _b;
    return (_b = (_a = payload.groupedTags) == null ? void 0 : _a[normalizeSceneCode(code)]) != null ? _b : [];
  }
  function describeMagnetSources(sources = []) {
    return sources.map((source) => {
      const name = source.label || source.source;
      if (source.status === "success") return `${name} \u6210\u529F`;
      if (source.status === "empty") return `${name} \u65E0\u7ED3\u679C`;
      return `${name} \u5931\u8D25${source.error ? `: ${source.error}` : ""}`;
    }).join("\uFF1B");
  }
  function ensurePanelStyles() {
    if (document.getElementById(PANEL_STYLE_ID)) return;
    const style = document.createElement("style");
    style.id = PANEL_STYLE_ID;
    style.textContent = `
    #${PANEL_ID}.javstash-scrollable,
    #${PANEL_ID} .javstash-scrollable {
      scrollbar-width: thin;
      scrollbar-color: rgba(251, 191, 36, 0.35) transparent;
    }

    #${PANEL_ID}.javstash-scrollable::-webkit-scrollbar,
    #${PANEL_ID} .javstash-scrollable::-webkit-scrollbar {
      width: 8px;
      height: 8px;
    }

    #${PANEL_ID}.javstash-scrollable::-webkit-scrollbar-track,
    #${PANEL_ID} .javstash-scrollable::-webkit-scrollbar-track {
      background: transparent;
    }

    #${PANEL_ID}.javstash-scrollable::-webkit-scrollbar-thumb,
    #${PANEL_ID} .javstash-scrollable::-webkit-scrollbar-thumb {
      background: linear-gradient(180deg, rgba(251, 191, 36, 0.38), rgba(245, 158, 11, 0.26));
      border-radius: 999px;
      border: 2px solid transparent;
      background-clip: padding-box;
    }

    #${PANEL_ID}.javstash-scrollable::-webkit-scrollbar-thumb:hover,
    #${PANEL_ID} .javstash-scrollable::-webkit-scrollbar-thumb:hover {
      background: linear-gradient(180deg, rgba(251, 191, 36, 0.52), rgba(245, 158, 11, 0.4));
      border-radius: 999px;
      border: 2px solid transparent;
      background-clip: padding-box;
    }
  `;
    document.head.appendChild(style);
  }
  function ensurePanel() {
    ensurePanelStyles();
    let panel = document.getElementById(PANEL_ID);
    if (panel) return panel;
    panel = document.createElement("aside");
    panel.id = PANEL_ID;
    panel.className = "javstash-scrollable";
    panel.style.position = "fixed";
    panel.style.right = "24px";
    panel.style.bottom = "24px";
    panel.style.width = "360px";
    panel.style.maxWidth = "calc(100vw - 32px)";
    panel.style.maxHeight = "70vh";
    panel.style.overflowX = "hidden";
    panel.style.overflowY = "auto";
    panel.style.scrollbarGutter = "stable";
    panel.style.zIndex = "2147483647";
    panel.style.background = "linear-gradient(180deg, rgba(251, 191, 36, 0.08), rgba(251, 191, 36, 0) 72px), #111418";
    panel.style.color = "#f3f4f6";
    panel.style.border = "1px solid rgba(255, 255, 255, 0.08)";
    panel.style.borderRadius = "12px";
    panel.style.boxShadow = "0 20px 48px rgba(0, 0, 0, 0.45)";
    panel.style.padding = "14px 14px 12px";
    panel.style.fontSize = "14px";
    panel.style.lineHeight = "1.7";
    panel.style.display = "none";
    panel.style.backdropFilter = "blur(10px)";
    document.body.appendChild(panel);
    return panel;
  }
  function isPanelVisible() {
    const panel = document.getElementById(PANEL_ID);
    return !!(panel && panel.style.display !== "none");
  }
  function closePanel() {
    const panel = document.getElementById(PANEL_ID);
    if (panel) panel.style.display = "none";
  }
  function getPanelCode() {
    const panel = document.getElementById(PANEL_ID);
    return (panel == null ? void 0 : panel.dataset.code) ? normalizeSceneCode(panel.dataset.code) : null;
  }
  function renderMagnetSection(body, code) {
    if (!code) return;
    const section = document.createElement("div");
    section.style.marginTop = "14px";
    section.style.paddingTop = "12px";
    section.style.borderTop = "1px solid rgba(255, 255, 255, 0.08)";
    const header = document.createElement("div");
    header.style.display = "flex";
    header.style.alignItems = "center";
    header.style.justifyContent = "space-between";
    header.style.gap = "10px";
    header.style.marginBottom = "10px";
    section.appendChild(header);
    const title = document.createElement("strong");
    title.textContent = "\u78C1\u529B\u8D44\u6E90";
    title.style.color = "#f3f4f6";
    title.style.fontSize = "13px";
    title.style.fontWeight = "600";
    header.appendChild(title);
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = "\u67E5\u8BE2\u78C1\u529B\u8D44\u6E90";
    button.style.border = "1px solid rgba(251, 191, 36, 0.28)";
    button.style.borderRadius = "8px";
    button.style.background = "rgba(251, 191, 36, 0.1)";
    button.style.color = "#fde68a";
    button.style.cursor = "pointer";
    button.style.fontSize = "12px";
    button.style.padding = "5px 9px";
    header.appendChild(button);
    const status = document.createElement("div");
    status.style.color = "#9ca3af";
    status.style.fontSize = "12px";
    status.textContent = "\u67E5\u8BE2\u540E\u4F1A\u5199\u5165 MyJAV \u78C1\u529B\u8D44\u6E90\u7F13\u5B58\u3002";
    section.appendChild(status);
    const list = document.createElement("div");
    list.className = "javstash-scrollable javstash-magnet-list";
    list.style.display = "none";
    list.style.marginTop = "10px";
    list.style.maxHeight = "240px";
    list.style.overflowX = "hidden";
    list.style.overflowY = "auto";
    list.style.paddingRight = "2px";
    section.appendChild(list);
    button.addEventListener("click", () => {
      button.disabled = true;
      button.textContent = "\u67E5\u8BE2\u4E2D...";
      status.style.color = "#9ca3af";
      status.textContent = "\u6B63\u5728\u7531\u672C\u673A\u67E5\u8BE2 BTDigg \u548C Sukebei...";
      list.style.display = "none";
      list.innerHTML = "";
      Promise.all([fetchBTDiggMagnets(code), fetchSukebeiMagnets(code)]).then((sources) => requestMagnetWrite(code, sources)).then((payload) => {
        const items = Array.isArray(payload.items) ? payload.items : [];
        const sourceSummary = Array.isArray(payload.sources) ? payload.sources : [];
        const hasError = sourceSummary.some((source) => source.status === "error");
        const hasSuccess = sourceSummary.some((source) => source.status === "success");
        status.style.color = hasError && !hasSuccess ? "#fca5a5" : hasError ? "#fcd34d" : "#86efac";
        status.textContent = items.length > 0 ? `\u5DF2\u5199\u5165\u7F13\u5B58\uFF0C\u5171 ${items.length} \u6761\u3002${sourceSummary.length ? " " + describeMagnetSources(sourceSummary) : ""}` : sourceSummary.length ? describeMagnetSources(sourceSummary) : "\u5DF2\u5199\u5165\u7F13\u5B58\uFF0C\u5171 0 \u6761\u3002";
        renderMagnetList(list, items);
        window.dispatchEvent(new CustomEvent("javstash:magnets-updated", {
          detail: { code: normalizeSceneCode(code) }
        }));
      }).catch((error) => {
        status.style.color = "#fca5a5";
        status.textContent = error instanceof Error ? error.message : "\u78C1\u529B\u8D44\u6E90\u67E5\u8BE2\u5931\u8D25";
      }).finally(() => {
        button.disabled = false;
        button.textContent = "\u67E5\u8BE2\u78C1\u529B\u8D44\u6E90";
      });
    });
    body.appendChild(section);
  }
  function findOwnMagnetSection() {
    var _a, _b, _c, _d;
    const exactHeading = Array.from(document.querySelectorAll("h3")).find((element) => String(element.textContent || "").trim() === "\u78C1\u529B\u8D44\u6E90");
    if (exactHeading) {
      return (_b = (_a = exactHeading.closest("section")) != null ? _a : exactHeading.parentElement) != null ? _b : null;
    }
    const sectionByText = Array.from(document.querySelectorAll("section")).find((element) => String(element.textContent || "").includes("\u78C1\u529B\u8D44\u6E90"));
    if (sectionByText) return sectionByText;
    const fallbackHeading = Array.from(document.querySelectorAll("h1, h2, h4, h5, h6, strong, span, div")).find((element) => String(element.textContent || "").trim() === "\u78C1\u529B\u8D44\u6E90");
    return (_d = (_c = fallbackHeading == null ? void 0 : fallbackHeading.closest("section")) != null ? _c : fallbackHeading == null ? void 0 : fallbackHeading.parentElement) != null ? _d : null;
  }
  function disconnectInlineMagnetObserver() {
    inlineMagnetObserver == null ? void 0 : inlineMagnetObserver.disconnect();
    inlineMagnetObserver = null;
  }
  function clearInlineMagnetRetryTimer() {
    if (inlineMagnetRetryTimer !== null) {
      window.clearTimeout(inlineMagnetRetryTimer);
      inlineMagnetRetryTimer = null;
    }
  }
  function renderInlineMagnetActions(code) {
    if (document.getElementById(INLINE_MAGNET_ACTIONS_ID)) return true;
    const host = findOwnMagnetSection();
    if (!host) return false;
    const section = document.createElement("div");
    section.id = INLINE_MAGNET_ACTIONS_ID;
    section.style.position = "fixed";
    section.style.right = "24px";
    section.style.bottom = "24px";
    section.style.width = "52px";
    section.style.height = "52px";
    section.style.border = "1px solid rgba(212, 175, 55, 0.12)";
    section.style.borderRadius = "999px";
    section.style.background = "rgba(17, 20, 24, 0.9)";
    section.style.backdropFilter = "blur(10px)";
    section.style.boxShadow = "0 14px 34px rgba(0, 0, 0, 0.24)";
    section.style.zIndex = "2147483646";
    section.style.display = "flex";
    section.style.alignItems = "center";
    section.style.justifyContent = "center";
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = "\u78C1\u529B";
    button.title = `\u4E3A ${code} \u67E5\u8BE2\u78C1\u529B\u8D44\u6E90`;
    button.setAttribute("aria-label", `\u4E3A ${code} \u67E5\u8BE2\u78C1\u529B\u8D44\u6E90`);
    button.style.border = "0";
    button.style.width = "100%";
    button.style.height = "100%";
    button.style.borderRadius = "999px";
    button.style.background = "rgba(251, 191, 36, 0.14)";
    button.style.color = "#fde68a";
    button.style.cursor = "pointer";
    button.style.fontSize = "11px";
    button.style.fontWeight = "600";
    button.style.letterSpacing = "0.08em";
    section.appendChild(button);
    button.addEventListener("click", () => {
      button.disabled = true;
      button.textContent = "\u67E5\u8BE2\u4E2D";
      Promise.all([fetchBTDiggMagnets(code), fetchSukebeiMagnets(code)]).then((sources) => requestMagnetWrite(code, sources)).then((payload) => {
        const items = Array.isArray(payload.items) ? payload.items : [];
        const sourceSummary = Array.isArray(payload.sources) ? payload.sources : [];
        const hasError = sourceSummary.some((source) => source.status === "error");
        const hasSuccess = sourceSummary.some((source) => source.status === "success");
        button.textContent = hasError && !hasSuccess ? "\u5931\u8D25" : items.length > 0 ? "\u5DF2\u66F4\u65B0" : "\u65E0\u7ED3\u679C";
        button.style.color = hasError && !hasSuccess ? "#fca5a5" : hasError ? "#fcd34d" : "#86efac";
        button.title = items.length > 0 ? `\u5DF2\u5199\u5165\u7F13\u5B58\uFF0C\u5171 ${items.length} \u6761\u3002${sourceSummary.length ? " " + describeMagnetSources(sourceSummary) : ""}` : sourceSummary.length ? describeMagnetSources(sourceSummary) : "\u5DF2\u5199\u5165\u7F13\u5B58\uFF0C\u5171 0 \u6761\u3002";
        window.dispatchEvent(new CustomEvent("javstash:magnets-updated", {
          detail: { code: normalizeSceneCode(code) }
        }));
      }).catch((error) => {
        button.textContent = "\u5931\u8D25";
        button.style.color = "#fca5a5";
        button.title = error instanceof Error ? error.message : "\u78C1\u529B\u8D44\u6E90\u67E5\u8BE2\u5931\u8D25";
      }).finally(() => {
        window.setTimeout(() => {
          button.disabled = false;
          button.textContent = "\u78C1\u529B";
          button.style.color = "#fde68a";
          button.title = `\u4E3A ${code} \u67E5\u8BE2\u78C1\u529B\u8D44\u6E90`;
        }, 1600);
      });
    });
    document.body.appendChild(section);
    disconnectInlineMagnetObserver();
    clearInlineMagnetRetryTimer();
    return true;
  }
  function scheduleInlineMagnetActions(code) {
    if (renderInlineMagnetActions(code)) return;
    if (inlineMagnetObserver || !document.body) return;
    inlineMagnetObserver = new MutationObserver(() => {
      renderInlineMagnetActions(code);
    });
    inlineMagnetObserver.observe(document.body, {
      childList: true,
      subtree: true
    });
    let attempts = 0;
    const retry = () => {
      if (renderInlineMagnetActions(code)) return;
      attempts += 1;
      if (attempts >= 16) {
        clearInlineMagnetRetryTimer();
        disconnectInlineMagnetObserver();
        return;
      }
      inlineMagnetRetryTimer = window.setTimeout(retry, 500);
    };
    inlineMagnetRetryTimer = window.setTimeout(retry, 500);
    window.setTimeout(() => {
      clearInlineMagnetRetryTimer();
      disconnectInlineMagnetObserver();
    }, 8e3);
  }
  function renderMagnetList(container, items) {
    if (!items.length) {
      container.style.display = "none";
      return;
    }
    container.style.display = "block";
    container.innerHTML = "";
    const copyAll = document.createElement("button");
    copyAll.type = "button";
    copyAll.textContent = "\u590D\u5236\u5168\u90E8";
    copyAll.style.marginBottom = "8px";
    copyAll.style.border = "1px solid rgba(255, 255, 255, 0.08)";
    copyAll.style.borderRadius = "8px";
    copyAll.style.background = "rgba(255, 255, 255, 0.04)";
    copyAll.style.color = "#f3f4f6";
    copyAll.style.cursor = "pointer";
    copyAll.style.fontSize = "12px";
    copyAll.style.padding = "5px 9px";
    copyAll.addEventListener("click", () => {
      navigator.clipboard.writeText(items.map((item) => item.link).join("\n"));
      copyAll.textContent = "\u5DF2\u590D\u5236";
      window.setTimeout(() => {
        copyAll.textContent = "\u590D\u5236\u5168\u90E8";
      }, 1200);
    });
    container.appendChild(copyAll);
    items.slice(0, 30).forEach((item) => {
      const row = document.createElement("div");
      row.style.display = "grid";
      row.style.gridTemplateColumns = "minmax(0, 1fr) auto";
      row.style.gap = "8px";
      row.style.padding = "8px 0";
      row.style.borderTop = "1px solid rgba(255, 255, 255, 0.06)";
      const main = document.createElement("div");
      main.style.minWidth = "0";
      const name = document.createElement("a");
      name.href = item.link;
      name.textContent = item.name || item.link;
      name.title = item.name || item.link;
      name.style.display = "block";
      name.style.overflow = "hidden";
      name.style.textOverflow = "ellipsis";
      name.style.whiteSpace = "nowrap";
      name.style.color = "#ffffff";
      name.style.textDecoration = "none";
      name.style.fontSize = "13px";
      main.appendChild(name);
      const meta = document.createElement("div");
      meta.style.marginTop = "2px";
      meta.style.color = "#9ca3af";
      meta.style.fontSize = "12px";
      meta.textContent = [
        item.size || "-",
        item.zh ? "\u4E2D\u5B57" : "",
        Array.isArray(item.sources) ? item.sources.join(", ") : item.source || ""
      ].filter(Boolean).join(" \xB7 ");
      main.appendChild(meta);
      const copy = document.createElement("button");
      copy.type = "button";
      copy.textContent = "\u590D\u5236";
      copy.style.alignSelf = "center";
      copy.style.border = "1px solid rgba(255, 255, 255, 0.08)";
      copy.style.borderRadius = "7px";
      copy.style.background = "rgba(255, 255, 255, 0.04)";
      copy.style.color = "#d1d5db";
      copy.style.cursor = "pointer";
      copy.style.fontSize = "12px";
      copy.style.padding = "4px 8px";
      copy.addEventListener("click", () => {
        navigator.clipboard.writeText(item.link);
        copy.textContent = "\u5DF2\u590D\u5236";
        window.setTimeout(() => {
          copy.textContent = "\u590D\u5236";
        }, 1200);
      });
      row.appendChild(main);
      row.appendChild(copy);
      container.appendChild(row);
    });
  }
  function renderUserTagSection(body, code) {
    if (!code) return;
    const section = document.createElement("div");
    section.style.marginTop = "14px";
    section.style.paddingTop = "12px";
    section.style.borderTop = "1px solid rgba(255, 255, 255, 0.08)";
    const header = document.createElement("div");
    header.style.display = "flex";
    header.style.alignItems = "center";
    header.style.justifyContent = "space-between";
    header.style.gap = "10px";
    header.style.marginBottom = "10px";
    section.appendChild(header);
    const title = document.createElement("strong");
    title.textContent = "\u4E2A\u4EBA\u72B6\u6001";
    title.style.color = "#f3f4f6";
    title.style.fontSize = "13px";
    title.style.fontWeight = "600";
    header.appendChild(title);
    const status = document.createElement("div");
    status.style.color = "#9ca3af";
    status.style.fontSize = "12px";
    status.style.flex = "1 1 auto";
    status.style.textAlign = "right";
    status.textContent = "\u6B63\u5728\u540C\u6B65\u4E2A\u4EBA\u72B6\u6001...";
    header.appendChild(status);
    const actions = document.createElement("div");
    actions.style.display = "flex";
    actions.style.flexWrap = "wrap";
    actions.style.gap = "8px";
    actions.style.marginTop = "10px";
    section.appendChild(actions);
    const personalApiKey = getPersonalApiKey();
    let activeTags = [];
    let pending = false;
    const renderButtons = () => {
      actions.innerHTML = "";
      USER_TAGS.forEach((tag) => {
        const active = activeTags.includes(tag);
        const button = document.createElement("button");
        button.type = "button";
        button.textContent = USER_TAG_LABELS[tag];
        button.disabled = pending || !personalApiKey;
        button.style.border = active ? "1px solid rgba(251, 191, 36, 0.55)" : "1px solid rgba(255, 255, 255, 0.08)";
        button.style.borderRadius = "999px";
        button.style.background = active ? "rgba(251, 191, 36, 0.16)" : "rgba(255, 255, 255, 0.04)";
        button.style.color = active ? "#fde68a" : "#d1d5db";
        button.style.cursor = button.disabled ? "not-allowed" : "pointer";
        button.style.fontSize = "12px";
        button.style.padding = "6px 10px";
        button.style.opacity = button.disabled ? "0.55" : "1";
        button.addEventListener("click", () => {
          if (button.disabled || pending) return;
          const hasTag = activeTags.includes(tag);
          pending = true;
          status.style.color = "#9ca3af";
          status.textContent = hasTag ? `\u6B63\u5728\u53D6\u6D88${USER_TAG_LABELS[tag]}...` : `\u6B63\u5728\u66F4\u65B0${USER_TAG_LABELS[tag]}...`;
          renderButtons();
          const request = hasTag ? requestDeleteUserItemTag(code, tag, personalApiKey) : requestUpsertUserItemTag(code, tag, personalApiKey);
          request.then((payload) => {
            activeTags = getTagsForCode(payload, code);
            status.style.color = "#86efac";
            status.textContent = hasTag ? `\u5DF2\u53D6\u6D88${USER_TAG_LABELS[tag]}` : `\u5DF2\u540C\u6B65${USER_TAG_LABELS[tag]}`;
          }).catch((error) => {
            status.style.color = "#fca5a5";
            status.textContent = error instanceof Error ? error.message : "\u4E2A\u4EBA\u72B6\u6001\u540C\u6B65\u5931\u8D25";
          }).finally(() => {
            pending = false;
            renderButtons();
          });
        });
        actions.appendChild(button);
      });
    };
    renderButtons();
    if (!personalApiKey) {
      status.textContent = "\u672A\u914D\u7F6E\u4E2A\u4EBA\u8BBF\u95EE\u5BC6\u94A5\uFF0C\u8BF7\u4ECE\u6CB9\u7334\u83DC\u5355\u4E2D\u8BBE\u7F6E\u540E\u540C\u6B65\u7A0D\u540E\u770B / \u6536\u85CF / dislike\u3002";
      body.appendChild(section);
      return;
    }
    requestUserItemTags(code, personalApiKey).then((payload) => {
      activeTags = getTagsForCode(payload, code);
      status.style.color = "#9ca3af";
      status.textContent = activeTags.length > 0 ? `\u5DF2\u540C\u6B65 ${activeTags.map((tag) => USER_TAG_LABELS[tag]).join(" / ")}` : "\u5F53\u524D\u5F71\u7247\u6682\u65E0\u4E2A\u4EBA\u72B6\u6001\u3002";
      renderButtons();
    }).catch((error) => {
      status.style.color = "#fca5a5";
      status.textContent = error instanceof Error ? error.message : "\u4E2A\u4EBA\u72B6\u6001\u8BFB\u53D6\u5931\u8D25";
    });
    body.appendChild(section);
  }
  function renderPanel(state) {
    const panel = ensurePanel();
    panel.innerHTML = "";
    panel.style.display = "block";
    panel.dataset.code = state.code ? normalizeSceneCode(state.code) : "";
    const header = document.createElement("div");
    header.style.display = "flex";
    header.style.justifyContent = "space-between";
    header.style.alignItems = "flex-start";
    header.style.marginBottom = "12px";
    header.style.gap = "12px";
    header.style.paddingBottom = "10px";
    header.style.borderBottom = "1px solid rgba(255, 255, 255, 0.06)";
    header.style.cursor = "pointer";
    header.onclick = () => closePanel();
    const detailUrl = buildPublicDetailUrl(state.code);
    const title = detailUrl ? document.createElement("a") : document.createElement("strong");
    title.textContent = state.code ? "\u756A\u53F7 " + state.code + " \u7684\u8BE6\u60C5\u9875" : "\u4E2D\u6587\u5185\u5BB9\u67E5\u8BE2";
    title.style.fontWeight = "500";
    title.style.fontSize = "13px";
    title.style.lineHeight = "1.5";
    title.style.color = "#9ca3af";
    if (title instanceof HTMLAnchorElement && detailUrl) {
      title.href = detailUrl;
      title.target = "_blank";
      title.rel = "noreferrer noopener";
      title.style.textDecoration = "none";
    }
    header.appendChild(title);
    const closeButton = document.createElement("button");
    closeButton.type = "button";
    closeButton.textContent = "\xD7";
    closeButton.setAttribute("aria-label", "\u5173\u95ED\u4E2D\u6587\u8BE6\u60C5");
    closeButton.style.border = "0";
    closeButton.style.background = "transparent";
    closeButton.style.color = "#9ca3af";
    closeButton.style.cursor = "pointer";
    closeButton.style.fontSize = "18px";
    closeButton.style.lineHeight = "1";
    closeButton.style.padding = "0";
    closeButton.style.flex = "0 0 auto";
    closeButton.onclick = () => closePanel();
    header.appendChild(closeButton);
    panel.appendChild(header);
    const body = document.createElement("div");
    body.style.marginTop = "12px";
    panel.appendChild(body);
    if (state.state === "loading") {
      const loadingText = document.createElement("div");
      loadingText.style.color = "#d1d5db";
      loadingText.textContent = "\u6B63\u5728\u67E5\u8BE2\u4E2D\u6587\u5185\u5BB9...";
      body.appendChild(loadingText);
      renderMagnetSection(body, state.code);
      return;
    }
    if (state.state === "error") {
      body.textContent = state.message || "\u67E5\u8BE2\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
      renderUserTagSection(body, state.code);
      renderMagnetSection(body, state.code);
      return;
    }
    const titleBlock = document.createElement("div");
    titleBlock.style.marginBottom = "14px";
    const titleText = document.createElement("div");
    titleText.style.fontSize = "16px";
    titleText.style.fontWeight = "600";
    titleText.style.color = "#ffffff";
    titleText.style.letterSpacing = "0.01em";
    titleText.style.lineHeight = "1.55";
    titleText.textContent = state.title || "\u6682\u65E0\u6807\u9898";
    titleBlock.appendChild(titleText);
    body.appendChild(titleBlock);
    const descBlock = document.createElement("div");
    descBlock.style.padding = "10px 12px";
    descBlock.style.background = "rgba(255, 255, 255, 0.03)";
    descBlock.style.borderLeft = "2px solid rgba(251, 191, 36, 0.45)";
    descBlock.style.borderRadius = "8px";
    const descText = document.createElement("div");
    descText.style.color = "#d1d5db";
    descText.style.whiteSpace = "pre-wrap";
    descText.textContent = state.description || "\u6682\u65E0\u7B80\u4ECB";
    descBlock.appendChild(descText);
    body.appendChild(descBlock);
    renderUserTagSection(body, state.code);
    renderMagnetSection(body, state.code);
  }
  function injectButton(code) {
    if (!code) return;
    if (shouldInjectInlineMagnetPanel(window.location.href)) {
      scheduleInlineMagnetActions(code);
      return;
    }
    if (document.querySelector("." + BUTTON_CLASS)) return;
    const button = document.createElement("button");
    button.type = "button";
    button.className = BUTTON_CLASS;
    button.textContent = "\u4E2D\u6587\u8BE6\u60C5";
    button.style.marginLeft = "8px";
    button.style.padding = "5px 10px";
    button.style.border = "1px solid rgba(251, 191, 36, 0.32)";
    button.style.borderRadius = "999px";
    button.style.background = "#171b21";
    button.style.color = "#f3f4f6";
    button.style.cursor = "pointer";
    button.style.fontSize = "12px";
    button.style.boxShadow = "0 6px 20px rgba(0, 0, 0, 0.18)";
    button.addEventListener("click", () => {
      if (isPanelVisible() && getPanelCode() === normalizeSceneCode(code)) {
        closePanel();
        return;
      }
      const cached = readLookupCache(code);
      if (cached) {
        renderPanel({
          state: "success",
          code: cached.code,
          title: cached.title,
          description: cached.description,
          source: "cache"
        });
        return;
      }
      renderPanel({ state: "loading", code });
      requestLookup(code).then((payload) => {
        const cachedPayload = writeLookupCache({
          code: payload.code || code,
          title: payload.title || "",
          description: payload.description || ""
        });
        renderPanel({
          state: "success",
          code: cachedPayload.code,
          title: cachedPayload.title,
          description: cachedPayload.description,
          source: "network"
        });
      }).catch((error) => {
        renderPanel({
          state: "error",
          code,
          message: error instanceof Error ? error.message : "\u67E5\u8BE2\u5931\u8D25"
        });
      });
    });
    const target = findInjectionTarget(code);
    if (target) {
      if (target.parentNode) {
        target.parentNode.insertBefore(button, target.nextSibling);
      } else {
        target.appendChild(button);
      }
      return;
    }
    button.style.position = "fixed";
    button.style.top = "24px";
    button.style.right = "24px";
    button.style.zIndex = "2147483646";
    document.body.appendChild(button);
  }

  // tampermonkey/src/metadata.ts
  var USERSCRIPT_VERSION = "0.1.5";
  var USERSCRIPT_REQUIRE_GM_XHR_PATCH = true;
  var USERSCRIPT_CONNECT_SUMMARY = "localhost, 127.0.0.1, myjav.vercel.app, btdig.com, www.btdig.com, sukebei.nyaa.si, *";
  var USERSCRIPT_METADATA = `// ==UserScript==
// @name         MyJAV
// @namespace    https://myjav.vercel.app/
// @version      ${USERSCRIPT_VERSION}
// @description  MyJAV\uFF1A\u63D0\u4F9B\u4E2D\u6587\u8BE6\u60C5\u4F53\u9A8C\uFF0C\u5E76\u8054\u52A8\u4E2A\u4EBA JAV \u7BA1\u7406\u4E0E\u78C1\u529B\u8D44\u6E90\u67E5\u8BE2
// @match        https://www.javbus.com/*
// @match        https://javdb.com/*
// @match        https://www.javdb.com/*
// @match        https://myjav.vercel.app/v/*
// @match        http://localhost:3456/v/*
// @run-at       document-idle
// @require      https://update.greasyfork.org/scripts/515994/1478507/gh_2215_make_GM_xhr_more_parallel_again.js
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @connect      localhost
// @connect      127.0.0.1
// @connect      myjav.vercel.app
// @connect      btdig.com
// @connect      www.btdig.com
// @connect      sukebei.nyaa.si
// @connect      *
// ==/UserScript==`;

  // tampermonkey/src/main.ts
  var DIAGNOSTICS_DIALOG_ID = "javstash-diagnostics-dialog";
  function buildDiagnosticsSummary() {
    var _a, _b, _c, _d;
    const code = detectCode();
    const runtimeConnects = Array.isArray((_a = GM_info == null ? void 0 : GM_info.script) == null ? void 0 : _a.connects) ? GM_info.script.connects : [];
    const runtimeMatches = Array.isArray((_b = GM_info == null ? void 0 : GM_info.script) == null ? void 0 : _b.matches) ? GM_info.script.matches : [];
    const runtimeGrants = Array.isArray((_c = GM_info == null ? void 0 : GM_info.script) == null ? void 0 : _c.grants) ? GM_info.script.grants : [];
    const injectionTarget = code ? findInjectionTarget(code) : null;
    return [
      `\u6E90\u7801\u7248\u672C: ${USERSCRIPT_VERSION}`,
      `\u8FD0\u884C\u65F6\u7248\u672C: ${((_d = GM_info == null ? void 0 : GM_info.script) == null ? void 0 : _d.version) || "\u672A\u77E5"}`,
      `GM_xhr\u8865\u4E01: ${USERSCRIPT_REQUIRE_GM_XHR_PATCH ? "\u5DF2\u5185\u7F6E" : "\u672A\u5185\u7F6E"}`,
      `\u5F53\u524D\u9875\u9762: ${window.location.href}`,
      `\u5F53\u524D\u4EE3\u7406\u5730\u5740: ${getProxyOrigin()}`,
      `\u6E90\u7801@connect: ${USERSCRIPT_CONNECT_SUMMARY}`,
      `\u8FD0\u884C\u65F6@connect: ${runtimeConnects.join(", ") || "\u7A7A"}`,
      `\u8FD0\u884C\u65F6@match: ${runtimeMatches.join(", ") || "\u7A7A"}`,
      `\u8FD0\u884C\u65F6@grant: ${runtimeGrants.join(", ") || "\u7A7A"}`,
      `\u8BC6\u522B\u756A\u53F7: ${code || "\u672A\u8BC6\u522B"}`,
      `\u6CE8\u5165\u76EE\u6807: ${injectionTarget ? `${injectionTarget.tagName}${injectionTarget.textContent ? ` (${String(injectionTarget.textContent).trim().slice(0, 40)})` : ""}` : "\u672A\u627E\u5230"}`
    ].join("\n");
  }
  async function copyText(text) {
    var _a;
    if ((_a = navigator.clipboard) == null ? void 0 : _a.writeText) {
      await navigator.clipboard.writeText(text);
      return;
    }
    const textarea = document.createElement("textarea");
    textarea.value = text;
    textarea.setAttribute("readonly", "true");
    textarea.style.position = "fixed";
    textarea.style.opacity = "0";
    textarea.style.pointerEvents = "none";
    document.body.appendChild(textarea);
    textarea.select();
    const copied = document.execCommand("copy");
    document.body.removeChild(textarea);
    if (!copied) {
      throw new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u590D\u5236\u5230\u526A\u8D34\u677F");
    }
  }
  function closeDiagnosticsDialog() {
    var _a;
    (_a = document.getElementById(DIAGNOSTICS_DIALOG_ID)) == null ? void 0 : _a.remove();
  }
  function showDiagnosticsDialog(summary) {
    closeDiagnosticsDialog();
    const overlay = document.createElement("div");
    overlay.id = DIAGNOSTICS_DIALOG_ID;
    overlay.style.position = "fixed";
    overlay.style.inset = "0";
    overlay.style.zIndex = "2147483647";
    overlay.style.display = "flex";
    overlay.style.alignItems = "center";
    overlay.style.justifyContent = "center";
    overlay.style.padding = "20px";
    overlay.style.background = "rgba(0, 0, 0, 0.55)";
    const dialog = document.createElement("div");
    dialog.style.width = "min(720px, calc(100vw - 24px))";
    dialog.style.maxHeight = "min(80vh, 720px)";
    dialog.style.overflow = "hidden";
    dialog.style.borderRadius = "14px";
    dialog.style.border = "1px solid rgba(255, 255, 255, 0.1)";
    dialog.style.background = "#111418";
    dialog.style.boxShadow = "0 24px 64px rgba(0, 0, 0, 0.45)";
    dialog.style.color = "#f3f4f6";
    dialog.style.display = "flex";
    dialog.style.flexDirection = "column";
    overlay.appendChild(dialog);
    const header = document.createElement("div");
    header.style.display = "flex";
    header.style.alignItems = "center";
    header.style.justifyContent = "space-between";
    header.style.gap = "12px";
    header.style.padding = "14px 16px";
    header.style.borderBottom = "1px solid rgba(255, 255, 255, 0.08)";
    dialog.appendChild(header);
    const title = document.createElement("strong");
    title.textContent = "\u811A\u672C\u8BCA\u65AD\u4FE1\u606F";
    title.style.fontSize = "14px";
    header.appendChild(title);
    const actionBar = document.createElement("div");
    actionBar.style.display = "flex";
    actionBar.style.alignItems = "center";
    actionBar.style.gap = "8px";
    header.appendChild(actionBar);
    const copyButton = document.createElement("button");
    copyButton.type = "button";
    copyButton.textContent = "\u590D\u5236\u8BCA\u65AD\u4FE1\u606F";
    copyButton.style.border = "1px solid rgba(251, 191, 36, 0.28)";
    copyButton.style.borderRadius = "8px";
    copyButton.style.background = "rgba(251, 191, 36, 0.1)";
    copyButton.style.color = "#fde68a";
    copyButton.style.cursor = "pointer";
    copyButton.style.fontSize = "12px";
    copyButton.style.padding = "5px 9px";
    actionBar.appendChild(copyButton);
    const closeButton = document.createElement("button");
    closeButton.type = "button";
    closeButton.textContent = "\u5173\u95ED";
    closeButton.style.border = "1px solid rgba(255, 255, 255, 0.12)";
    closeButton.style.borderRadius = "8px";
    closeButton.style.background = "rgba(255, 255, 255, 0.04)";
    closeButton.style.color = "#f3f4f6";
    closeButton.style.cursor = "pointer";
    closeButton.style.fontSize = "12px";
    closeButton.style.padding = "5px 9px";
    actionBar.appendChild(closeButton);
    const body = document.createElement("div");
    body.style.padding = "16px";
    dialog.appendChild(body);
    const status = document.createElement("div");
    status.style.minHeight = "20px";
    status.style.marginBottom = "8px";
    status.style.color = "#9ca3af";
    status.style.fontSize = "12px";
    body.appendChild(status);
    const pre = document.createElement("pre");
    pre.textContent = summary;
    pre.style.margin = "0";
    pre.style.maxHeight = "calc(80vh - 140px)";
    pre.style.overflow = "auto";
    pre.style.whiteSpace = "pre-wrap";
    pre.style.wordBreak = "break-word";
    pre.style.fontSize = "12px";
    pre.style.lineHeight = "1.65";
    pre.style.padding = "12px";
    pre.style.borderRadius = "10px";
    pre.style.background = "rgba(255, 255, 255, 0.04)";
    pre.style.border = "1px solid rgba(255, 255, 255, 0.08)";
    body.appendChild(pre);
    copyButton.addEventListener("click", () => {
      copyButton.disabled = true;
      copyText(summary).then(() => {
        status.style.color = "#86efac";
        status.textContent = "\u8BCA\u65AD\u4FE1\u606F\u5DF2\u590D\u5236";
      }).catch((error) => {
        status.style.color = "#fca5a5";
        status.textContent = error instanceof Error ? error.message : "\u590D\u5236\u8BCA\u65AD\u4FE1\u606F\u5931\u8D25";
      }).finally(() => {
        window.setTimeout(() => {
          copyButton.disabled = false;
        }, 200);
      });
    });
    closeButton.addEventListener("click", closeDiagnosticsDialog);
    overlay.addEventListener("click", (event) => {
      if (event.target === overlay) closeDiagnosticsDialog();
    });
    document.addEventListener(
      "keydown",
      (event) => {
        if (event.key === "Escape") closeDiagnosticsDialog();
      },
      { once: true }
    );
    document.body.appendChild(overlay);
  }
  function registerMenu() {
    GM_registerMenuCommand("\u8BBE\u7F6E\u4E2A\u4EBA\u8BBF\u95EE\u5BC6\u94A5", () => {
      const current = getPersonalApiKey();
      const next = window.prompt(
        "\u8BF7\u8F93\u5165\u8D26\u6237\u9875\u751F\u6210\u7684\u4E2A\u4EBA\u8BBF\u95EE\u5BC6\u94A5\uFF0C\u7528\u4E8E\u540C\u6B65\u7A0D\u540E\u770B / \u6536\u85CF / dislike",
        current
      );
      if (typeof next === "string") {
        GM_setValue(PERSONAL_API_KEY_STORAGE_KEY, next.trim());
      }
    });
    GM_registerMenuCommand("\u67E5\u770B\u811A\u672C\u8BCA\u65AD\u4FE1\u606F", () => {
      showDiagnosticsDialog(buildDiagnosticsSummary());
    });
  }
  function init() {
    registerMenu();
    const code = detectCode();
    if (!code) return;
    injectButton(code);
  }
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init, { once: true });
  } else {
    init();
  }
})();