MyJAV

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

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         MyJAV
// @namespace    https://myjav.vercel.app/
// @version      0.1.6
// @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/*
// @match        https://subtitlecat.com/*
// @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      xunlei.com
// @connect      api-shoulei-ssl.xunlei.com
// @connect      subtitlecat.com
// @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/subtitles.ts
  function formatGmSubtitleError(error, fallback) {
    const message = [
      error == null ? void 0 : error.message,
      error == null ? void 0 : error.error,
      error == null ? void 0 : error.statusText,
      typeof (error == null ? void 0 : error.status) === "number" ? `status=${error.status}` : ""
    ].filter(Boolean).join(", ");
    return message || fallback;
  }
  function inferSubtitleExt(name, ext) {
    var _a;
    const rawExt = typeof ext === "string" ? ext.trim().replace(/^\./, "") : "";
    if (rawExt) return rawExt.toLowerCase();
    const nameExt = ((_a = name.match(/\.([a-z0-9]+)(?:$|\?)/i)) == null ? void 0 : _a[1]) || "";
    return nameExt.toLowerCase();
  }
  function normalizeKeyword(text) {
    return text.toLowerCase().replace(/[\s_-]+/g, "");
  }
  function filterSubtitleCatLinks(texts, keyword) {
    const normalizedKeyword = normalizeKeyword(keyword);
    if (!normalizedKeyword) return texts.map(() => true);
    return texts.map((text) => normalizeKeyword(text).includes(normalizedKeyword));
  }
  function normalizeXunleiSubtitleItems(payload) {
    const data = payload == null ? void 0 : payload.data;
    if (!Array.isArray(data)) return [];
    return data.flatMap((raw) => {
      const name = typeof raw.name === "string" ? raw.name.trim() : "";
      const url = typeof raw.url === "string" && raw.url.trim() ? raw.url.trim() : typeof raw.scid === "string" ? raw.scid.trim() : "";
      const ext = inferSubtitleExt(name, raw.ext);
      if (!url || !ext) return [];
      return [{
        name,
        ext,
        url
      }];
    });
  }
  function subtitleDownloadName(item, code) {
    const sourceName = item.name.trim();
    if (/\.[a-z0-9]{2,5}$/i.test(sourceName)) return sourceName;
    const ext = item.ext.replace(/^\./, "").toLowerCase() || "srt";
    return `${normalizeSceneCode(code)}.${ext}`;
  }
  function isPreviewableSubtitle(item) {
    const ext = item.ext.toLowerCase();
    return ext === "ass" || ext === "srt";
  }
  function buildSubtitleCatUrl(code) {
    return `https://subtitlecat.com/index.php?search=${encodeURIComponent(normalizeSceneCode(code))}`;
  }
  function fetchXunleiSubtitles(code) {
    const url = `https://api-shoulei-ssl.xunlei.com/oracle/subtitle?gcid=&cid=&name=${encodeURIComponent(normalizeSceneCode(code))}`;
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url,
        timeout: 15e3,
        onload(response) {
          try {
            if (response.status >= 400) {
              reject(new Error(`\u8FC5\u96F7\u5B57\u5E55\u8BF7\u6C42\u5931\u8D25\uFF1A${response.status}`));
              return;
            }
            const payload = JSON.parse(String(response.responseText || "{}"));
            resolve(normalizeXunleiSubtitleItems(payload));
          } catch (e) {
            reject(new Error("\u8FC5\u96F7\u5B57\u5E55\u63A5\u53E3\u8FD4\u56DE\u4E86\u65E0\u6CD5\u89E3\u6790\u7684\u5185\u5BB9"));
          }
        },
        ontimeout() {
          reject(new Error("\u8FC5\u96F7\u5B57\u5E55\u8BF7\u6C42\u8D85\u65F6"));
        },
        onerror(error) {
          reject(new Error(formatGmSubtitleError(error, "\u8FC5\u96F7\u5B57\u5E55\u8BF7\u6C42\u5931\u8D25")));
        }
      });
    });
  }
  function fetchSubtitleText(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url,
        timeout: 15e3,
        onload(response) {
          if (response.status >= 400) {
            reject(new Error(`\u5B57\u5E55\u6587\u4EF6\u8BF7\u6C42\u5931\u8D25\uFF1A${response.status}`));
            return;
          }
          resolve(String(response.responseText || response.response || ""));
        },
        ontimeout() {
          reject(new Error("\u5B57\u5E55\u6587\u4EF6\u8BF7\u6C42\u8D85\u65F6"));
        },
        onerror(error) {
          reject(new Error(formatGmSubtitleError(error, "\u5B57\u5E55\u6587\u4EF6\u8BF7\u6C42\u5931\u8D25")));
        }
      });
    });
  }
  function downloadSubtitle(content, filename) {
    const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const anchor = document.createElement("a");
    anchor.href = url;
    anchor.download = filename;
    anchor.style.display = "none";
    document.body.appendChild(anchor);
    anchor.click();
    document.body.removeChild(anchor);
    window.setTimeout(() => URL.revokeObjectURL(url), 0);
  }

  // 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 SUBTITLE_DIALOG_ID = "javstash-subtitle-dialog";
  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 = `
    .javstash-scrollable,
    #${PANEL_ID}.javstash-scrollable,
    #${PANEL_ID} .javstash-scrollable {
      scrollbar-width: thin;
      scrollbar-color: rgba(251, 191, 36, 0.35) transparent;
    }

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

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

    .javstash-scrollable::-webkit-scrollbar-thumb,
    #${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;
    }

    .javstash-scrollable::-webkit-scrollbar-thumb:hover,
    #${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 createActionButton(label) {
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = label;
    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";
    return button;
  }
  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 closeSubtitleDialog() {
    var _a;
    (_a = document.getElementById(SUBTITLE_DIALOG_ID)) == null ? void 0 : _a.remove();
  }
  function buildSubtitleDialog(titleText) {
    ensurePanelStyles();
    closeSubtitleDialog();
    const overlay = document.createElement("div");
    overlay.id = SUBTITLE_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(820px, calc(100vw - 24px))";
    dialog.style.maxHeight = "min(82vh, 760px)";
    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 = titleText;
    title.style.fontSize = "14px";
    header.appendChild(title);
    const closeButton = createActionButton("\u5173\u95ED");
    closeButton.style.border = "1px solid rgba(255, 255, 255, 0.12)";
    closeButton.style.background = "rgba(255, 255, 255, 0.04)";
    closeButton.style.color = "#f3f4f6";
    closeButton.addEventListener("click", closeSubtitleDialog);
    header.appendChild(closeButton);
    const body = document.createElement("div");
    body.className = "javstash-scrollable";
    body.style.padding = "16px";
    body.style.overflowY = "auto";
    body.style.maxHeight = "calc(min(82vh, 760px) - 56px)";
    dialog.appendChild(body);
    const status = document.createElement("div");
    status.style.minHeight = "20px";
    status.style.marginBottom = "10px";
    status.style.color = "#9ca3af";
    status.style.fontSize = "12px";
    body.appendChild(status);
    overlay.addEventListener("click", (event) => {
      if (event.target === overlay) closeSubtitleDialog();
    });
    document.addEventListener(
      "keydown",
      (event) => {
        if (event.key === "Escape") closeSubtitleDialog();
      },
      { once: true }
    );
    document.body.appendChild(overlay);
    return { body, status };
  }
  function renderSubtitlePreview(item, code) {
    const filename = subtitleDownloadName(item, code);
    const { body, status } = buildSubtitleDialog(`${item.ext.toUpperCase()} \u5B57\u5E55\u9884\u89C8 - ${filename}`);
    status.textContent = "\u6B63\u5728\u52A0\u8F7D\u5B57\u5E55\u6587\u4EF6...";
    fetchSubtitleText(item.url).then((content) => {
      status.textContent = "";
      const actionBar = document.createElement("div");
      actionBar.style.display = "flex";
      actionBar.style.justifyContent = "flex-end";
      actionBar.style.marginBottom = "10px";
      body.appendChild(actionBar);
      const downloadButton = createActionButton("\u4E0B\u8F7D");
      downloadButton.addEventListener("click", () => downloadSubtitle(content, filename));
      actionBar.appendChild(downloadButton);
      const pre = document.createElement("pre");
      pre.className = "javstash-scrollable";
      pre.style.margin = "0";
      pre.style.maxHeight = "calc(min(82vh, 760px) - 132px)";
      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 = "#1e1e1e";
      pre.style.border = "1px solid rgba(255, 255, 255, 0.08)";
      pre.style.color = "#f3f4f6";
      pre.textContent = content.split("\n").map((line, index, lines) => `${String(index + 1).padStart(String(lines.length).length, " ")}. ${line}`).join("\n");
      body.appendChild(pre);
    }).catch((error) => {
      status.style.color = "#fca5a5";
      status.textContent = error instanceof Error ? error.message : "\u5B57\u5E55\u9884\u89C8\u5931\u8D25";
    });
  }
  function renderXunleiSubtitleDialog(code) {
    const { body, status } = buildSubtitleDialog(`\u8FC5\u96F7\u5B57\u5E55 - ${normalizeSceneCode(code)}`);
    status.textContent = "\u6B63\u5728\u67E5\u8BE2\u8FC5\u96F7\u5B57\u5E55...";
    fetchXunleiSubtitles(code).then((items) => {
      if (!items.length) {
        status.style.color = "#fcd34d";
        status.textContent = "\u8FC5\u96F7\u4E2D\u627E\u4E0D\u5230\u76F8\u5173\u5B57\u5E55\u3002";
        return;
      }
      status.style.color = "#86efac";
      status.textContent = `\u627E\u5230 ${items.length} \u6761\u5B57\u5E55\u3002`;
      const list = document.createElement("div");
      list.style.display = "grid";
      list.style.gap = "8px";
      body.appendChild(list);
      items.forEach((item) => {
        const row = document.createElement("div");
        row.style.display = "grid";
        row.style.gridTemplateColumns = "minmax(0, 1fr) auto";
        row.style.gap = "10px";
        row.style.alignItems = "center";
        row.style.padding = "10px 0";
        row.style.borderTop = "1px solid rgba(255, 255, 255, 0.06)";
        list.appendChild(row);
        const main = document.createElement("div");
        main.style.minWidth = "0";
        row.appendChild(main);
        const name = document.createElement("div");
        name.textContent = item.name || subtitleDownloadName(item, code);
        name.title = name.textContent;
        name.style.overflow = "hidden";
        name.style.textOverflow = "ellipsis";
        name.style.whiteSpace = "nowrap";
        name.style.color = "#ffffff";
        name.style.fontSize = "13px";
        main.appendChild(name);
        const meta = document.createElement("div");
        meta.textContent = item.ext.toUpperCase();
        meta.style.marginTop = "2px";
        meta.style.color = "#9ca3af";
        meta.style.fontSize = "12px";
        main.appendChild(meta);
        const actions = document.createElement("div");
        actions.style.display = "flex";
        actions.style.gap = "8px";
        actions.style.alignItems = "center";
        row.appendChild(actions);
        const previewButton = createActionButton("\u9884\u89C8");
        previewButton.disabled = !isPreviewableSubtitle(item);
        previewButton.style.opacity = previewButton.disabled ? "0.5" : "1";
        previewButton.style.cursor = previewButton.disabled ? "not-allowed" : "pointer";
        previewButton.title = previewButton.disabled ? "\u4EC5\u652F\u6301\u9884\u89C8 ASS \u548C SRT \u5B57\u5E55" : "\u9884\u89C8\u5B57\u5E55";
        previewButton.addEventListener("click", () => {
          if (!previewButton.disabled) renderSubtitlePreview(item, code);
        });
        actions.appendChild(previewButton);
        const downloadButton = createActionButton("\u4E0B\u8F7D");
        downloadButton.addEventListener("click", () => {
          downloadButton.disabled = true;
          downloadButton.textContent = "\u4E0B\u8F7D\u4E2D...";
          fetchSubtitleText(item.url).then((content) => {
            downloadSubtitle(content, subtitleDownloadName(item, code));
            downloadButton.textContent = "\u5DF2\u4E0B\u8F7D";
          }).catch((error) => {
            downloadButton.textContent = "\u5931\u8D25";
            downloadButton.title = error instanceof Error ? error.message : "\u5B57\u5E55\u4E0B\u8F7D\u5931\u8D25";
          }).finally(() => {
            window.setTimeout(() => {
              downloadButton.disabled = false;
              downloadButton.textContent = "\u4E0B\u8F7D";
            }, 1200);
          });
        });
        actions.appendChild(downloadButton);
      });
    }).catch((error) => {
      status.style.color = "#fca5a5";
      status.textContent = error instanceof Error ? error.message : "\u8FC5\u96F7\u5B57\u5E55\u67E5\u8BE2\u5931\u8D25";
    });
  }
  function openSubtitleCat(code) {
    window.open(buildSubtitleCatUrl(code), "_blank", "noopener,noreferrer");
  }
  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 renderSubtitleSection(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 title = document.createElement("strong");
    title.textContent = "\u5B57\u5E55\u8D44\u6E90";
    title.style.display = "block";
    title.style.color = "#f3f4f6";
    title.style.fontSize = "13px";
    title.style.fontWeight = "600";
    title.style.marginBottom = "10px";
    section.appendChild(title);
    const actions = document.createElement("div");
    actions.style.display = "flex";
    actions.style.flexWrap = "wrap";
    actions.style.gap = "8px";
    section.appendChild(actions);
    const xunleiButton = createActionButton("\u8FC5\u96F7\u5B57\u5E55");
    xunleiButton.addEventListener("click", () => renderXunleiSubtitleDialog(code));
    actions.appendChild(xunleiButton);
    const subtitleCatButton = createActionButton("SubTitleCat");
    subtitleCatButton.addEventListener("click", () => openSubtitleCat(code));
    actions.appendChild(subtitleCatButton);
    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.border = "1px solid rgba(212, 175, 55, 0.12)";
    section.style.borderRadius = "16px";
    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.flexDirection = "column";
    section.style.alignItems = "center";
    section.style.justifyContent = "center";
    section.style.gap = "6px";
    section.style.padding = "6px";
    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 = "40px";
    button.style.height = "40px";
    button.style.borderRadius = "12px";
    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);
      });
    });
    const xunleiButton = document.createElement("button");
    xunleiButton.type = "button";
    xunleiButton.textContent = "\u8FC5\u96F7";
    xunleiButton.title = `\u4E3A ${code} \u67E5\u8BE2\u8FC5\u96F7\u5B57\u5E55`;
    xunleiButton.setAttribute("aria-label", `\u4E3A ${code} \u67E5\u8BE2\u8FC5\u96F7\u5B57\u5E55`);
    xunleiButton.style.border = "0";
    xunleiButton.style.width = "40px";
    xunleiButton.style.height = "40px";
    xunleiButton.style.borderRadius = "12px";
    xunleiButton.style.background = "rgba(96, 165, 250, 0.14)";
    xunleiButton.style.color = "#bfdbfe";
    xunleiButton.style.cursor = "pointer";
    xunleiButton.style.fontSize = "11px";
    xunleiButton.style.fontWeight = "600";
    xunleiButton.addEventListener("click", () => renderXunleiSubtitleDialog(code));
    section.appendChild(xunleiButton);
    const subtitleCatButton = document.createElement("button");
    subtitleCatButton.type = "button";
    subtitleCatButton.textContent = "Cat";
    subtitleCatButton.title = `\u4E3A ${code} \u6253\u5F00 SubTitleCat`;
    subtitleCatButton.setAttribute("aria-label", `\u4E3A ${code} \u6253\u5F00 SubTitleCat`);
    subtitleCatButton.style.border = "0";
    subtitleCatButton.style.width = "40px";
    subtitleCatButton.style.height = "40px";
    subtitleCatButton.style.borderRadius = "12px";
    subtitleCatButton.style.background = "rgba(248, 113, 113, 0.14)";
    subtitleCatButton.style.color = "#fecaca";
    subtitleCatButton.style.cursor = "pointer";
    subtitleCatButton.style.fontSize = "11px";
    subtitleCatButton.style.fontWeight = "600";
    subtitleCatButton.addEventListener("click", () => openSubtitleCat(code));
    section.appendChild(subtitleCatButton);
    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);
      renderSubtitleSection(body, state.code);
      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);
      renderSubtitleSection(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);
    renderSubtitleSection(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.6";
  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, xunlei.com, api-shoulei-ssl.xunlei.com, subtitlecat.com, *";
  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/*
// @match        https://subtitlecat.com/*
// @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      xunlei.com
// @connect      api-shoulei-ssl.xunlei.com
// @connect      subtitlecat.com
// @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 handleSubtitleCatPage() {
    var _a, _b, _c, _d, _e;
    if (!window.location.hostname.includes("subtitlecat.com")) return false;
    const keyword = (_a = new URLSearchParams(window.location.search).get("search")) == null ? void 0 : _a.trim();
    if (!keyword) return true;
    (_b = document.querySelector(".t-banner-inner")) == null ? void 0 : _b.style.setProperty("display", "none");
    (_c = document.querySelector("#navbar")) == null ? void 0 : _c.style.setProperty("display", "none");
    const links = Array.from(document.querySelectorAll(".sub-table tr td a"));
    const visibleMap = filterSubtitleCatLinks(links.map((link) => link.textContent || ""), keyword);
    let visibleCount = 0;
    links.forEach((link, index) => {
      var _a2;
      const visible = (_a2 = visibleMap[index]) != null ? _a2 : false;
      if (visible) {
        visibleCount += 1;
        return;
      }
      const row = link.closest("tr");
      if (row) row.style.display = "none";
    });
    const sectionTitle = document.querySelector(".sec-title");
    if (sectionTitle == null ? void 0 : sectionTitle.innerHTML) {
      sectionTitle.innerHTML = sectionTitle.innerHTML.replace(/^\s*\d+/, String(visibleCount));
    }
    if (visibleCount === 0) {
      const notice = document.createElement("div");
      notice.textContent = "\u8BE5\u756A\u53F7\u65E0\u5B57\u5E55";
      notice.style.margin = "16px 0";
      notice.style.padding = "10px 12px";
      notice.style.borderRadius = "8px";
      notice.style.background = "rgba(248, 113, 113, 0.12)";
      notice.style.color = "#b91c1c";
      notice.style.fontSize = "14px";
      (_e = (_d = document.querySelector(".sub-table")) == null ? void 0 : _d.parentElement) == null ? void 0 : _e.prepend(notice);
    }
    return true;
  }
  function init() {
    registerMenu();
    if (handleSubtitleCatPage()) return;
    const code = detectCode();
    if (!code) return;
    injectButton(code);
  }
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init, { once: true });
  } else {
    init();
  }
})();