MyJAV:提供中文详情体验,并联动个人 JAV 管理与磁力资源查询
// ==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();
}
})();