// ==UserScript==
// @name JAV-JHS
// @namespace https://sleazyfork.org/zh-CN/scripts/533695
// @version 1.1.0
// @author fuajofkewmrw
// @description Jav-鉴黄师 收藏,屏蔽,标记已下载,在线预览,fc2ppv
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=javdb.com
// @match https://javdb.com/*
// @match https://javtrailers.com/*
// @match https://subtitlecat.com/*
// @match https://jable.tv/videos/*
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/layer.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/js/md5.min.js
// @connect *
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant window.close
// ==/UserScript==
(t => {
if (typeof GM_addStyle == "function") {
GM_addStyle(t);
return
}
const i = document.createElement("style");
i.textContent = t, document.head.append(i)
})(" .container{min-width:85%}.navbar{z-index:12345679!important}.movie-list.h{grid-template-columns:repeat(5,minmax(0,1fr))!important}.sub-header,#footer,.search-recent-keywords,.app-desktop-banner,body>section>div>div.video-detail>div:nth-child(5),body>section>div>div.video-detail>div:nth-child(6),h3.main-title,div.video-meta-panel>div>div:nth-child(2)>nav>div.review-buttons>div:nth-child(2),div.video-detail>div:nth-child(4)>div>div.tabs.no-bottom>ul>li:nth-child(3),div.video-detail>div:nth-child(4)>div>div.tabs.no-bottom>ul>li:nth-child(2),div.video-detail>div:nth-child(4)>div>div.tabs.no-bottom>ul>li:nth-child(1),.top-meta,.float-buttons{display:none!important}div.tabs.no-bottom,.tabs ul{border-bottom:none!important} ");
(function (n, layuiLayer, a) {
'use strict';
var e = Object.defineProperty, t = (t, n, a) => ((t, n, a) => n in t ? e(t, n, {
enumerable: true,
configurable: true,
writable: true,
value: a
}) : t[n] = a)(t, "symbol" != typeof n ? n + "" : n, a);
class Utils {
constructor() {
return t(this, "insertStyle", (e => {
e && (-1 === e.indexOf("<style>") && (e = "<style>" + e + "</style>"), $("head").append(e));
})), t(this, "msg", {
success(e, t = {}) {
const n = Array.isArray(e) ? e : [e];
this.list(n, [], [], t);
},
error(e, t = {}) {
const n = Array.isArray(e) ? e : [e];
this.list([], n, [], t);
},
info(e, t = {}) {
const n = Array.isArray(e) ? e : [e];
this.list([], [], n, t);
},
list(e = [], t = [], n = [], a = {}) {
let i = "";
const r = (e, t) => {
e && 0 !== e.length && (i += `<div style="color:${t.color};margin-bottom:10px;">`,
e.forEach((e => i += `${t.prefix} ${e}<br/>`)), i += "</div>");
};
r(e, {
prefix: "✓",
color: "#76d25e"
}), r(t, {
prefix: "✗",
color: "#dc4b64"
}), r(n, {
prefix: "ℹ",
color: "#d7c88b"
});
let s = 0;
t.length > 0 && 0 === e.length && 0 === n.length ? s = 2 : e.length > 0 && 0 === t.length && (s = 1),
layer.msg(i, {
icon: s,
time: 2e3
});
}
}), t(this, "http", {
alertFun: e => {
layer.msg(e, {
icon: 2
});
},
get(e, t = {}, n = {}, a) {
return this.jqueryRequest("GET", e, null, t, n, a);
},
post(e, t = {}, n = {}, a) {
return this.jqueryRequest("POST", e, t, null, n, a);
},
put(e, t = {}, n = {}, a) {
return this.jqueryRequest("PUT", e, t, null, n, a);
},
del(e, t = {}, n = {}, a) {
return this.jqueryRequest("DELETE", e, null, t, n, a);
},
jqueryRequest(e, t, n = {}, a = {}, i = {}, r = true) {
return "POST" === e && (i = {
"Content-Type": "application/json",
...i
}), new Promise(((s, o) => {
$.ajax({
method: e,
url: t,
async: r,
data: "GET" === e || "DELETE" === e ? a : JSON.stringify(n),
headers: i,
success: e => this.handleResponse(e, s, o),
error: e => o(e)
});
}));
},
handleResponse(e, t, n) {
const a = e;
if (200 === a.code) t(a); else if (401 === a.code) window.location.reload(); else {
const e = a.msg || "请求失败";
this.alertFun ? this.alertFun(e) : console.error(e), n(new Error(e));
}
}
}), Utils.instance || (Utils.instance = this, this.intervalContainer = {}), Utils.instance;
}
importResource(e) {
let t;
e.indexOf("css") >= 0 ? (t = document.createElement("link"), t.setAttribute("rel", "stylesheet"),
t.href = e) : (t = document.createElement("script"), t.setAttribute("type", "text/javascript"),
t.src = e), document.documentElement.appendChild(t);
}
loopDetector(e, t, n = 20, a = 1e4, i = true) {
let r = false;
const s = Math.random(), o = (new Date).getTime();
this.intervalContainer[s] = setInterval((() => {
(new Date).getTime() - o > a && (console.warn("loopDetector timeout!", e, t), r = i),
(e() || r) && (clearInterval(this.intervalContainer[s]), t && t(), delete this.intervalContainer[s]);
}), n);
}
rightClick(e, t) {
e.jquery && (e = e[0]), e ? e.addEventListener("contextmenu", (e => {
e.preventDefault(), t(e);
})) : console.error("rightClick(), 找不到元素");
}
q(e, t, n, a) {
let i, r;
e ? (i = e.clientX - 120, r = e.clientY - 120) : (i = window.innerWidth / 2 - 120,
r = window.innerHeight / 2 - 120), layer.confirm(t, {
offset: [r, i],
btn: ["屏蔽", "取消"],
zIndex: 99999999999
}, (function () {
n(), layer.closeAll();
}), (function () {
a && a();
}));
}
getNowStr(e = "-", t = ":") {
const n = new Date, a = n.getFullYear(), i = String(n.getMonth() + 1).padStart(2, "0"), r = String(n.getDate()).padStart(2, "0"), s = String(n.getHours()).padStart(2, "0"), o = String(n.getMinutes()).padStart(2, "0"), l = String(n.getSeconds()).padStart(2, "0");
return `${[a, i, r].join(e)} ${[s, o, l].join(t)}`;
}
}
const i = new Utils;
class LocalStorageManager {
constructor() {
return LocalStorageManager.instance || (LocalStorageManager.instance = this), LocalStorageManager.instance;
}
findData(e, t) {
return t.find((t => t.carNum === e));
}
async getData() {
const e = {
dataList: [],
filterKeywordList: [],
filterActorList: []
}, t = localStorage.appData;
t || (localStorage.appData = JSON.stringify(e));
const n = t ? JSON.parse(t) : e, a = [], i = [], r = [];
for (const s of n.dataList) s.filter ? a.push(s) : s.hasDown ? r.push(s) : s.favorite && i.push(s);
return {
filterList: a,
favoriteList: i,
hasDownList: r,
filterKeywordList: n.filterKeywordList,
filterActorList: n.filterActorList,
data: n
};
}
async saveKeyData(e, t) {
}
async changeData(e, t, n, a) {
t.includes("http") || (t = window.location.origin + t);
const r = localStorage.appData, s = JSON.parse(r);
let o = s.dataList, l = this.findData(e, o);
if (l || (l = {
carNum: e,
url: t,
actress: n,
createDate: i.getNowStr(),
filter: false,
favorite: false,
hasDown: false
}, o.push(l)), "filter" === a) {
if (l.filter) throw new Error(e + " 已在屏蔽列表中");
l.filter = true, l.favorite = false, l.hasDown = false;
} else if ("favorite" === a) {
if (l.favorite) throw new Error(e + " 已在收藏列表中");
l.filter = false, l.favorite = true, l.hasDown = false;
} else {
if ("hasDown" !== a) throw new Error("actionType错误");
l.filter = true, l.favorite = true, l.hasDown = true;
}
localStorage.setItem("appData", JSON.stringify(s));
}
async saveFilterActor(e) {
const t = localStorage.appData, n = JSON.parse(t);
if (n.filterActorList.includes(e)) throw new Error(e + " 已存在");
n.filterActorList.push(e), localStorage.setItem("appData", JSON.stringify(n));
}
async saveFilterKeyword(e) {
const t = localStorage.appData, n = JSON.parse(t);
if (n.filterKeywordList.includes(e)) throw new Error(e + " 已存在");
n.filterKeywordList.push(e), localStorage.setItem("appData", JSON.stringify(n));
}
async removeData(e) {
const t = localStorage.appData, n = JSON.parse(t);
let a = n.dataList;
if (!this.findData(e, a)) throw new Error("未找到该番号信息:" + e);
a = a.filter((t => t.carNum !== e)), n.dataList = a, localStorage.setItem("appData", JSON.stringify(n));
}
}
const r = new LocalStorageManager;
class PluginManager {
constructor() {
this.plugins = new Map, this.isInitialized = false;
}
register(e) {
if ("function" != typeof e) throw new Error("插件必须是一个类");
const t = e.name;
if (!t) throw new Error("类必须要有名称");
const n = t.toLowerCase();
if (this.plugins.has(n)) throw new Error(`插件"${t}"已注册`);
const a = new e;
a.pluginManager = this, this.plugins.set(n, a);
}
getBean(e) {
return this.plugins.get(e.toLowerCase());
}
_initialize() {
if (this.isInitialized) return;
const e = new Map;
for (const [t, n] of this.plugins) "function" == typeof n.injectBean && e.set(t, {
instance: n,
deps: this._getDependencies(n.injectBean)
});
for (const [t, {instance: n, deps: a}] of e) {
const e = a.map((e => {
const n = e.toLowerCase();
if (!this.plugins.has(n)) throw new Error(`插件"${t}"依赖的插件"${e}"未注册`);
return this.plugins.get(n);
}));
n.injectBean(...e);
}
this.isInitialized = true;
}
_getDependencies(e) {
const t = e.toString();
return t.slice(t.indexOf("(") + 1, t.indexOf(")")).split(",").map((e => e.trim())).filter((e => e));
}
process() {
this.isInitialized || this._initialize();
for (const [t, n] of this.plugins) try {
"function" == typeof n.handle && (i.insertStyle(n.initCss()), n.handle());
} catch (e) {
console.error("执行插件发生错误", e);
}
}
}
const s = {
filterList: [],
favoriteList: [],
filterKeywordList: [],
filterActorList: [],
hasHandleList: [],
answerCount: 1,
reviewKeyword: ["像", "是你"]
};
class BasePlugin {
constructor() {
this.pluginManager = null, this.utils = i, this.isDetailPage = window.location.href.includes("javdb") && window.location.href.includes("/v"),
this.isListPage = window.location.href.includes("javdb") && !window.location.href.includes("/v"),
this.storageManager = r, Object.keys(s).forEach((e => {
Object.defineProperty(this, e, {
get: () => s[e],
set(t) {
s[e] = t;
},
enumerable: true,
configurable: true
});
}));
}
injectBean() {
}
initCss() {
}
handle() {
}
openPage(e, t, n, a) {
n || (n = true), a && a.ctrlKey ? window.open(e) : layer.open({
type: 2,
title: t,
content: e,
shadeClose: n,
area: ["80%", "90%"],
isOutAnim: false,
anim: -1
});
}
closePage() {
layer.msg("操作成功", {
icon: 1
}), layer.closeAll();
[".layui-layer-shade", ".layui-layer-move", ".layui-layer"].forEach((function (e) {
parent.document.querySelectorAll(e).forEach((function (e) {
e.parentNode.removeChild(e);
}));
})), window.close();
}
getPageInfo() {
return {
carNum: $('a[title="複製番號"]').attr("data-clipboard-text"),
url: window.location.href.split("#")[0],
actress: $(".female").prev().map(((e, t) => $(t).text())).get().join(" "),
actors: $(".male").prev().map(((e, t) => $(t).text())).get().join(" ")
};
}
refresh() {
localStorage.refresh = Date.now().toString(), this.isListPage && this.pluginManager.getBean("ListPagePlugin").refresh();
}
async changeData(e, t, n, a) {
if (console.log(e), !e) throw layer.error("番号为空!"), new Error("番号为空!");
if (!t) throw layer.error("url为空!"), new Error("url为空!");
await this.storageManager.changeData(e, t, n, a), this.refresh();
}
}
class ListPagePlugin extends BasePlugin {
injectBean(autoPagePlugin, fc2Plugin) {
this.autoPagePlugin = autoPagePlugin, this.fc2Plugin = fc2Plugin;
}
handle() {
$('.navbar-item:contains("FC2")').attr("href", "/advanced_search?type=3&score_min=4&d=1"),
$('.tabs a:contains("FC2")').attr("href", "/advanced_search?type=3&score_min=4&d=1"),
this.refresh(), window.addEventListener("storage", (e => {
"refresh" === e.key && this.refresh();
}));
}
async refresh() {
if (!this.isListPage) return;
const e = await this.storageManager.getData();
this.filterList = e.filterList, this.favoriteList = e.favoriteList, this.filterKeywordList = e.filterKeywordList,
this.filterActorList = e.filterActorList, this.filterMovieList(), this.autoPagePlugin.handlePaging();
}
filterMovieList() {
if (window.location.href.includes("search?q")) return;
let e = $(".movie-list .item").toArray();
const t = this.favoriteList.map((e => e.carNum));
e.forEach((e => {
let n = $(e), a = n.find("a"), i = a.attr("href"), r = a.attr("title"), s = n.find(".video-title").find("strong").text();
const o = `${s}-hide`, l = `${s}-tag`, c = `${s}-click`;
if ((this.filterList.some((e => e.carNum === s)) || this.filterKeywordList.some((e => r.includes(e) || s.includes(e)))) && !this.hasHandleList.includes(o)) return n.hide(),
void this.hasHandleList.push(o);
t.includes(s) && !this.hasHandleList.includes(l) && (n.find(".tags").append('<span class="tag is-success" style="margin-right: 5px">待下载</span>'),
this.hasHandleList.push(l)), this.hasHandleList.includes(c) || (n.on("click", (e => {
if (e.preventDefault(), s.includes("FC2-")) {
let e = i.split("/").filter(Boolean).pop();
this.fc2Plugin.openFc2Page(e, s, i);
} else this.openPage(i, s, false, e);
})), this.utils.rightClick(n.find("img"), (e => {
this.utils.q(e, `是否屏蔽番号${s}?`, (() => {
this.changeData(s, i, "", "filter").then((e => layer.msg("操作成功", {
icon: 1
})));
}));
})), this.hasHandleList.push(c));
})), $("#wait-down-btn span").text(`打开待下载(${this.favoriteList.length})`);
}
}
class SearchPlugin extends BasePlugin {
initCss() {
return "\n .search-bar-container {\n margin-bottom: 0 !important;\n }\n \n .search-bar-container .column {\n padding: 10px 12px !important;\n }\n \n .search-bar-wrap {\n background-color: inherit;\n padding: 0;\n }\n ";
}
handle() {
this.isListPage && ($(".search-input").html('<input id="search-keyword" class="input is-medium" data-type="all" type="text" value="" placeholder="輸入影片番號,演員名等關鍵字進行檢索">'),
$("#search-bar-container").prependTo("#tags"), $(".search-submit").html('<button type="button" id="search-btn" class="button is-medium is-info">檢索</button>'),
$("#search-btn").on("click", (e => {
let t = $("#search-keyword").val(), n = $("#search-type option:selected").val();
"" !== t && this.openPage("/search?q=" + t + "&f=" + n, "搜索", false, e);
})), $("#search-keyword").on("paste", (e => {
setTimeout((() => {
$("#search-btn").click();
}), 0);
})).on("keypress", (e => {
"Enter" === e.key && setTimeout((() => {
$("#search-btn").click();
}), 0);
})));
}
}
class AutoPagePlugin extends BasePlugin {
constructor() {
super(), this.paging = false;
}
handle() {
if (!this.isListPage) return;
let e = "yes" === localStorage.autoPage ? "关闭自动翻页" : "开启自动翻页";
$(".pagination").prepend(`<a class='pagination-previous' id='auto-page'>${e}</a>`),
$("#auto-page").on("click", (e => {
e.preventDefault(), "yes" === localStorage.autoPage ? (localStorage.autoPage = "no",
$("#auto-page").html("开启自动翻页")) : (localStorage.autoPage = "yes", $("#auto-page").html("关闭自动翻页"),
this.handlePaging());
}));
}
handlePaging() {
if (!this.isListPage) return;
if (this.paging) return;
let e = true;
if ($(".movie-list .item:visible").each(((t, n) => {
0 === $(n).find("span:contains('待下载')").length && (e = false);
})), !e) return;
if ("yes" !== localStorage.autoPage) return;
let t = $(".pagination-next");
0 !== t.length && (this.paging = true, layer.msg("下一页....", {
time: 500,
end: () => {
t[0].click();
}
}));
}
}
class DetailPagePlugin extends BasePlugin {
injectBean(detailPageMenuPlugin) {
this.detailPageMenuPlugin = detailPageMenuPlugin;
}
initCss() {
return this.isDetailPage ? "\n .main-nav,#search-bar-container {\n display: none !important;\n }\n \n html {\n padding-top:0px!important;\n }\n " : "";
}
async handle() {
if (!this.isDetailPage) return;
const e = await this.storageManager.getData();
this.filterList = e.filterList, this.favoriteList = e.favoriteList, this.filterKeywordList = e.filterKeywordList,
this.filterActorList = e.filterActorList, this.checkFilterActor();
}
checkFilterActor() {
if (!this.isDetailPage) return;
let e = this.getPageInfo().actors;
this.filterActorList.forEach((t => {
e.indexOf(t) > -1 && (this.answerCount++, this.utils.q(null, "存在xxx演员, 是否屏蔽?", (() => {
this.detailPageMenuPlugin.filterOne(null, true);
})));
}));
}
}
function o() {
const e = Math.floor(Date.now() / 1e3);
if (e - (localStorage.review_ts || 0) <= 20) return localStorage.review_sign;
const t = `${e}.lpw6vgqzsp.${a(`${e}71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa`)}`;
return localStorage.review_ts = e, localStorage.review_sign = t, t;
}
const l = "https://api.ffaoa.com/api", c = (e, t = 20) => new Promise(((n, a) => {
$.ajax({
method: "GET",
url: `${l}/v1/movies/${e}/reviews`,
data: {
page: 1,
sort_by: "hotly",
limit: t
},
headers: {
jdSignature: o()
},
success: e => {
const t = e.data.reviews;
n(t);
},
error: e => {
console.error(e), msg.error("发生错误" + e), a(e);
}
});
}));
class ReviewPlugin extends BasePlugin {
async handle() {
if (!this.isDetailPage) return;
const e = window.location.href.split("/"), t = e[e.length - 1].split("#")[0];
let n = $("#magnets-content");
n.append('<div id="reviewsLoading" style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">获取评论中...</div>');
const a = await c(t, 20);
$("#reviewsLoading").remove(), 0 === a.length && n.append('<div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">无评论</div>'),
n.append('<hr style="border: 0; height: 2px; background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));"/>'),
a.forEach((e => {
let t = false;
for (let n = 0; n < this.reviewKeyword.length; n++) if (e.content.indexOf(this.reviewKeyword[n]) > -1) {
t = true;
break;
}
if (t) return;
let a = "";
for (let n = 0; n < e.score; n++) a += '<i class="icon-star"></i>';
let i = `\n <div class="item columns is-desktop" style="display:block;margin-top:6px;background-color:#ffffff;padding:10px;margin-left: -10px;word-break: break-word;">\n ${e.username} <span class="score-stars">${a}</span> <span class="time">${e.created_at.replace("T", " ").replace(".000Z", "")}</span> 点赞:${e.likes_count}\n <p style="margin-top: 5px;">${e.content}</p>\n </div>\n `;
n.append(i);
}));
}
}
const d = class _HotkeyManager {
constructor() {
if (new.target === _HotkeyManager) throw new Error("HotkeyManager cannot be instantiated.");
}
static registerHotkey(e, t, n = null) {
if (Array.isArray(e)) {
let a = [];
return e.forEach((e => {
if (!this.isHotkeyFormat(e)) throw new Error("快捷键格式错误");
let i = this.recordHotkey(e, t, n);
a.push(i);
})), a;
}
if (!this.isHotkeyFormat(e)) throw new Error("快捷键格式错误");
return this.recordHotkey(e, t, n);
}
static recordHotkey(e, t, n) {
let a = Math.random().toString(36).substr(2);
return this.registerHotKeyMap.set(a, {
hotkeyString: e,
callback: t,
keyupCallback: n
}), a;
}
static unregisterHotkey(e) {
this.registerHotKeyMap.has(e) && this.registerHotKeyMap.delete(e);
}
static isHotkeyFormat(e) {
return e.toLowerCase().split("+").map((e => e.trim())).every((e => ["ctrl", "shift", "alt"].includes(e) || 1 === e.length));
}
static judgeHotkey(e, t) {
const n = e.toLowerCase().split("+").map((e => e.trim())), a = n.includes("ctrl"), i = n.includes("shift"), r = n.includes("alt"), s = n.find((e => "ctrl" !== e && "shift" !== e && "alt" !== e));
return (this.isMac ? t.metaKey : t.ctrlKey) === a && t.shiftKey === i && t.altKey === r && t.key.toLowerCase() === s;
}
};
t(d, "isMac", 0 === navigator.platform.indexOf("Mac")), t(d, "registerHotKeyMap", new Map),
t(d, "handleKeydown", (e => {
for (const [t, n] of d.registerHotKeyMap) {
let t = n.hotkeyString, a = n.callback;
d.judgeHotkey(t, e) && (e.preventDefault(), a(e));
}
})), t(d, "handleKeyup", (e => {
for (const [t, n] of d.registerHotKeyMap) {
let t = n.hotkeyString, a = n.keyupCallback;
a && (d.judgeHotkey(t, e) && (e.preventDefault(), a(e)));
}
}));
let h = d;
document.addEventListener("keydown", (e => {
h.handleKeydown(e);
})), document.addEventListener("keyup", (e => {
h.handleKeyup(e);
}));
class DetailPageMenuPlugin extends BasePlugin {
constructor() {
super(), this.allowRepeatDown = false;
}
injectBean() {
}
initCss() {
return "\n .fr-btn {\n float: right;\n margin-left: 4px !important;\n }\n ";
}
handle() {
this.bindHotkey(), this.isDetailPage && this.createMenuBtn();
}
hotkey() {
if (this.isDetailPage) return [{
hotkey: ["a"],
callback: () => {
this.answerCount >= 2 ? this.filterOne(null, true) : this.filterOne(null), this.answerCount++;
}
}, {
hotkey: ["s"],
callback: () => this.favoriteOne(null)
}, {
hotkey: ["z"],
callback: () => this.speedVideo()
}];
}
createMenuBtn() {
const e = this.getPageInfo(), t = e.carNum, n = [{
id: "favoriteBtn",
sort: 1,
html: function () {
return `<a id="${this.id}" class="menu-btn" style="background-color:#25b1dc"><span>收藏(s)</span></a>`;
},
action: e => this.favoriteOne()
}, {
id: "filterBtn",
sort: 2,
html: function () {
return `<a id="${this.id}" class="menu-btn" style="background-color:#de3333"><span>屏蔽(a)</span></a>`;
},
action: e => this.filterOne(e)
}, {
id: "hasDownBtn",
sort: 3,
html: function () {
return `<a id="${this.id}" class="menu-btn" style="background-color:#7bc73b"><span>加入已下载</span></a>`;
},
action: t => this.changeData(e.carNum, e.url, e.actress, "hasDown").then((e => this.closePage()))
}, {
id: "enable-magnets-filter",
sort: 5,
html: function () {
return `<a id="${this.id}" class="menu-btn" style="background-color:#c2bd4c"><span>关闭磁力过滤</span></a>`;
},
action: e => {
$("#magnets-content .item").toArray().forEach((e => $(e).show()));
}
}, {
id: "jable-video-btn",
sort: 6,
html: function () {
return `<a id="${this.id}" class="menu-btn fr-btn" style="background:linear-gradient(to right, rgb(255,161,0), rgb(0,119,172))"><span>Jable</span></a>`;
},
action: e => this.openPage(`https://jable.tv/videos/${t}/`, t, false, e)
}, {
id: "missav-video-btn",
sort: 7,
html: function () {
return `<a id="${this.id}" class="menu-btn fr-btn" style="background:linear-gradient(to right, #d29494, rgb(254,98,142))"><span>MissAv</span></a>`;
},
action: e => window.open(`https://missav.ws/search/${t}`, "_blank")
}, {
id: "preview-video-btn",
sort: 8,
html: function () {
return `<a id="${this.id}" class="menu-btn fr-btn" style="background:linear-gradient(to right, #d7ab91, rgb(255,76,76))"><span>预览视频(z)</span></a>`;
},
action: e => this.openPage(`https://javtrailers.com/video/${t.toLowerCase().replace("-", "00")}`, t, false, e)
}, {
id: "search-subtitle-btn",
sort: 9,
html: function () {
return `<a id="${this.id}" class="menu-btn fr-btn" style="background-color: #2196F3"><span>搜索字幕</span></a>`;
},
action: e => this.openPage(`https://subtitlecat.com/index.php?search=${t}`, t, false, e)
}];
n.sort(((e, t) => e.sort - t.sort));
const a = `\n <div style="transform: translateY(-50%);">\n ${n.map((e => e.html())).join("\n")}\n </div>\n `;
$(".tabs").after(a), n.forEach((({id: e, action: t}) => {
$(`#${e}`).on("click", t);
}));
}
favoriteOne() {
let e = this.getPageInfo();
this.changeData(e.carNum, e.url, e.actress, "favorite").then((e => this.closePage()));
}
filterOne(e, t) {
e && e.preventDefault();
let n = this.getPageInfo();
t ? this.changeData(n.carNum, n.url, n.actress, "filter").then((e => this.closePage())) : this.utils.q(e, `是否屏蔽${n.carNum}?`, (() => {
this.changeData(n.carNum, n.url, n.actress, "filter").then((e => this.closePage()));
}));
}
checkHasDown() {
let e = this.getPageInfo().carNum;
$("#magnets-content a, #magnets-content button").on("click", (t => {
this.allowRepeatDown || this.storageManager.checkHasDown(e).then((e => {
"yes" === e.data && (t.preventDefault(), t.stopPropagation(), layer.msg(e.msg, {
icon: 2
}));
}));
}));
}
speedVideo() {
const e = $('iframe[id^="layui-layer-iframe"]');
0 === e.length ? $("#preview-video-btn").click() : e[0].contentWindow.postMessage("speedVideo", "*");
}
bindHotkey() {
const e = {
a: () => {
this.answerCount >= 2 ? this.filterOne(null, true) : this.filterOne(null), this.answerCount++;
},
s: () => this.favoriteOne(null),
z: () => this.speedVideo()
}, t = (e, t) => {
h.registerHotkey(e, (() => {
this.isDetailPage ? t() : (e => {
const t = $(".layui-layer-content iframe");
0 !== t.length && t[0].contentWindow.postMessage(e, "*");
})(e);
}));
};
this.isDetailPage && window.addEventListener("message", (t => {
e[t.data] && e[t.data]();
})), Object.entries(e).forEach((([e, n]) => {
t(e, n);
}));
}
}
class ListPageMenuPlugin extends BasePlugin {
constructor() {
super(), this.buttons = [];
}
handle() {
this.isListPage && this.createMenuBtn();
}
initCss() {
return "\n .menu-box {\n position: fixed;\n right: 10px;\n top: 50%;\n transform: translateY(-50%);\n display: flex;\n flex-direction: column;\n z-index: 1000;\n gap: 6px;\n }\n \n .menu-btn {\n display: inline-block;\n min-width: 80px;\n padding: 7px 12px;\n border-radius: 4px;\n color: white;\n text-decoration: none;\n font-weight: bold;\n font-size: 12px;\n text-align: center;\n cursor: pointer;\n transition: all 0.3s ease;\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);\n border: none;\n line-height: 1.3;\n margin: 0;\n }\n \n .menu-btn:hover {\n transform: translateY(-1px);\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);\n opacity: 0.9;\n }\n \n .menu-btn:active {\n transform: translateY(0);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\n }\n \n \n /*检查字幕表格*/\n .data-table {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-family: 'Helvetica Neue', Arial, sans-serif;\n background: #fff;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);\n margin: 0;\n }\n \n .data-table thead tr {\n background: #f8fafc;\n }\n \n .data-table th {\n padding: 16px 20px;\n text-align: left;\n color: #64748b;\n font-weight: 500;\n font-size: 14px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid #e2e8f0;\n }\n \n .data-table td {\n padding: 14px 20px;\n color: #334155;\n font-size: 15px;\n border-bottom: 1px solid #f1f5f9;\n }\n \n .data-table tbody tr:last-child td {\n border-bottom: none;\n }\n \n .data-table tbody tr {\n transition: all 0.2s ease;\n }\n \n .data-table tbody tr:hover {\n background: #f8fafc;\n }\n \n .data-table a {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n margin-right: 10px;\n border-radius: 6px;\n text-decoration: none;\n font-size: 13px;\n font-weight: 500;\n transition: all 0.2s ease;\n }\n \n .data-table a:first-child {\n background: #f0fdf4;\n color: #16a34a;\n border: 1px solid #dcfce7;\n }\n \n .data-table a:last-child {\n background: #f0f9ff;\n color: #0284c7;\n border: 1px solid #e0f2fe;\n }\n \n .data-table a:hover {\n transform: translateY(-1px);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);\n }\n \n .data-table a:first-child:hover {\n background: #dcfce7;\n }\n \n .data-table a:last-child:hover {\n background: #e0f2fe;\n }\n\n ";
}
createMenuBtn() {
const e = [{
id: "wait-check-btn",
style: "background-color:#dcc45b",
text: "打开待鉴定",
sort: 1,
action: e => this.openWaitCheck(e)
}, {
id: "wait-down-btn",
style: "background-color:#7cb7e8",
text: "打开待下载",
sort: 2,
action: e => this.openFavorite(e)
}, {
id: "auto-play-btn",
style: "background-color:" + ("yes" === localStorage.autoPlay ? "#dc4c5e" : "#65ced2"),
text: "yes" === localStorage.autoPlay ? "关闭自动播放" : "开启自动播放",
sort: 4,
action: e => this.changeAutoPlay(e)
}, {
id: "historyBtn",
style: "background-color:#aade66",
text: "历史列表",
sort: 6,
action: e => this.openHistory(e)
}, {
id: "importBtn",
style: "background-color:#d2668d",
text: "导入数据",
sort: 98,
action: e => this.importData(e)
}, {
id: "exportBtn",
style: "background-color:#85d0a3",
text: "导出数据",
sort: 99,
action: e => this.exportData(e)
}];
e.sort(((e, t) => e.sort - t.sort));
const t = `\n <div class="menu-box">\n ${e.map((e => e.disable ? "" : `\n <a id="${e.id}" class="menu-btn" style="${e.style}">\n <span>${e.text}</span>\n </a>\n `)).join("")}\n </div>\n `;
$("body").after(t), e.forEach((e => {
e.action && $(`#${e.id}`).on("click", (t => {
e.action(t);
}));
}));
}
openWaitCheck() {
this.changeAutoPlay("yes");
let e = 0;
$(".movie-list .item:visible").each(((t, n) => {
if (e >= 8) return false;
if (0 === $(n).find("span:contains('待下载')").length) {
const t = $(n).find("a").attr("href");
t && (window.open(t), e++);
}
}));
}
openFavorite() {
this.changeAutoPlay("no");
for (let e = 0; e < 10; e++) {
if (e >= this.favoriteList.length) return;
window.open(this.favoriteList[e].url);
}
}
changeAutoPlay(e) {
let t = localStorage.autoPlay;
t !== e && (localStorage.autoPlay = "yes" === t ? "no" : "yes", $("#auto-play-btn").css("background-color", "yes" === localStorage.autoPlay ? "#dc4c5e" : "#65ced2"),
$("#auto-play-btn span").text("yes" === localStorage.autoPlay ? "关闭自动播放" : "开启自动播放"));
}
openHistory(e) {
this.storageManager.getData().then((e => {
console.log(e);
const t = e.data.dataList || [];
t.reverse();
let n = [...t];
const a = {
filtered: {
text: "已屏蔽",
color: "#ec4949",
condition: e => !e.hasDown && !e.favorite
},
favorite: {
text: "已收藏",
color: "#50adb9",
condition: e => e.favorite && !e.hasDown
},
hasDown: {
text: "已下载",
color: "#8ebd6e",
condition: e => e.hasDown
}
}, i = e => {
let t = a.filtered;
return e.hasDown ? t = a.hasDown : e.favorite && (t = a.favorite), `\n <tr>\n <td>${e.carNum}</td>\n <td>${e.actress ? e.actress : ""}</td>\n <td>${e.createDate ? e.createDate : ""}</td>\n <td style="color:${t.color}">${t.text}</td>\n <td>\n <a class="action-remove" data-car-num="${e.carNum}" data-url="${e.url}">移除</a>\n <a class="action-detail" data-car-num="${e.carNum}" data-url="${e.url}">详情页</a>\n </td>\n </tr>\n `;
}, r = e => `\n <table class="data-table">\n <thead>\n <tr>\n <th>番号</th>\n <th width="300px">演员</th>\n <th>创建日期</th>\n <th>状态</th>\n <th>操作</th>\n </tr>\n </thead>\n <tbody>\n ${e.map(i).join("")}\n </tbody>\n </table>\n `, s = (e, i) => {
n = (e => "all" === e ? [...t] : t.filter(a[e].condition))(i), $(e).find(".data-table").replaceWith(r(n)),
$(e).find(".history-btn").removeClass("active").filter(`[data-action="${i}"]`).addClass("active");
}, o = (e, t, n) => {
"详情" === e && this.openPage(t.url), "移除" === e && this.utils.q(n, `是否移除${t.carNum}?`, (() => {
this.storageManager.removeData(t.carNum).then((e => {
let n = $(".movie-list .item").toArray();
for (let a = 0; a < n.length; a++) {
let e = $(n[a]), i = e.find(".video-title").find("strong").text();
if (i === t.carNum) {
e.show();
const t = `${i}-hide`;
this.hasHandleList = this.hasHandleList.filter((e => e !== t));
break;
}
}
layer.close(l), this.openHistory();
}));
}));
};
let l = layer.open({
type: 1,
title: "历史列表",
content: `\n <div style="margin: 10px">\n ${[{
action: "filtered",
text: "已屏蔽",
color: "#ec4949"
}, {
action: "favorite",
text: "已收藏",
color: "#50adb9"
}, {
action: "hasDown",
text: "已下载",
color: "#8ebd6e"
}, {
action: "all",
text: "所有",
color: "#d3c8a5"
}].map((e => `\n <a class="menu-btn history-btn" data-action="${e.action}" style="background-color:${e.color} !important;">\n ${e.text}\n </a>\n `)).join("")}\n </div>\n ${r(n)}\n `,
area: ["60%", "80%"],
success: (e, t) => {
$(e).on("click", ".history-btn", (function () {
s(e, $(this).data("action"));
})).on("click", ".action-remove", (function (e) {
e.stopPropagation(), o("移除", $(this).data(), e);
})).on("click", ".action-detail", (function (e) {
e.stopPropagation(), o("详情", $(this).data(), e);
}));
},
end: () => {
this.refresh();
}
});
}));
}
importData() {
try {
const e = document.createElement("input");
e.type = "file", e.accept = ".json", e.onchange = e => {
const t = e.target.files[0];
if (!t) return;
const n = new FileReader;
n.onload = e => {
try {
const t = e.target.result;
JSON.parse(t);
localStorage.appData ? layer.confirm("当前已有数据,确定要覆盖吗?", {
icon: 3,
title: "确认覆盖",
btn: ["确定", "取消"]
}, (function (e) {
localStorage.setItem("appData", t), layer.msg("数据导入成功", {
icon: 1
}), layer.close(e);
}), (function (e) {
layer.msg("已取消导入", {
icon: 2
}), layer.close(e);
})) : (localStorage.setItem("appData", t), layer.msg("数据导入成功", {
icon: 1
}));
} catch (t) {
layer.msg("导入失败:文件内容不是有效的JSON格式", {
icon: 2
}), console.error("导入JSON解析错误:", t);
}
}, n.onerror = () => {
layer.msg("读取文件时出错", {
icon: 2
});
}, n.readAsText(t);
}, document.body.appendChild(e), e.click(), setTimeout((() => document.body.removeChild(e)), 1e3);
} catch (e) {
console.error("导入数据时出错:", e), layer.msg("导入数据时出错: " + e.message, {
icon: 2
});
}
}
exportData(e) {
try {
const e = localStorage.appData;
if (!e) return void layer.msg("没有找到可导出的appData数据", {
icon: 2
});
const t = `${this.utils.getNowStr("_", "_")}_appData.json`, n = new Blob([e], {
type: "application/json"
}), a = URL.createObjectURL(n), i = document.createElement("a");
i.href = a, i.download = t, document.body.appendChild(i), i.click(), setTimeout((() => {
document.body.removeChild(i), URL.revokeObjectURL(a);
}), 100), layer.msg("数据导出成功", {
icon: 1
}), console.log("数据导出成功:", t);
} catch (t) {
console.error("导出数据时出错:", t), layer.msg("导出数据时出错: " + t.message, {
icon: 2
});
}
}
archiveFile() {
this.storageManager.archiveFile().then((e => {
let t = e.successMsgList, n = e.errorMsgList;
msg.list(t, n), t.length || n.length || layer.msg("没有可归档文件");
}));
}
checkSubTitle() {
this.storageManager.checkSubTitle().then((e => {
let t = e.data;
if (0 === t.length) return void layer.msg("视频字幕完整");
let n = '<table class="data-table">';
n += "<thead><tr>", n += "<th>番号</th>", n += "<th>文件路径</th>", n += "<th>操作</th>",
n += "</tr></thead>", n += "<tbody>", $.each(t, (function (e, t) {
n += "<tr>", n += "<td>" + t.carNum + "</td>", n += "<td>" + t.filePath + "</td>",
n += `<td>\n <a href="${"https://subtitlecat.com/index.php?search=" + t.carNum}" target="_blank">搜索字幕</a>\n <a href="${t.url}" target="_blank">详情页</a>\n </td>`,
n += "</tr>";
})), n += "</tbody>", n += "</table>", layer.open({
type: 1,
title: "检查字幕",
content: n,
area: ["1000px", "400px"]
});
}));
}
}
class HighlightMagnetPlugin extends BasePlugin {
handle() {
let e = $("#magnets-content .name").toArray(), t = false;
e.forEach((e => {
let n = $(e), a = n.text().toLowerCase();
a.indexOf("4k") > -1 && n.css("color", "#f40"), (a.indexOf("-c") > -1 || a.indexOf("-uc") > -1 || a.indexOf("4k") > -1) && (t = true);
})), t && e.forEach((e => {
let t = $(e), n = t.text().toLowerCase();
n.indexOf("-c") > -1 || n.indexOf("-uc") > -1 || n.indexOf("4k") > -1 || t.parent().parent().parent().hide();
}));
}
}
class PreviewVideoPlugin extends BasePlugin {
handle() {
$(".preview-video-container").on("click", (e => {
e.preventDefault(), $("#preview-video-btn").click();
})), "yes" === localStorage.getItem("autoPlay") && $("#preview-video-btn").click();
}
}
class SelectTextFilterPlugin extends BasePlugin {
injectBean(detailPageMenuPlugin) {
this.detailPageMenuPlugin = detailPageMenuPlugin;
}
handle() {
this.isDetailPage && (this.utils.rightClick($("h2"), (e => {
const t = window.getSelection().toString();
if (t) {
let n = {
clientX: e.clientX,
clientY: e.clientY + 120
};
this.utils.q(n, `是否屏蔽关键词${t}?`, (() => {
this.saveFilterKeyword(t).then((e => {
this.closePage();
}));
}));
}
})), $(".male").prev().toArray().forEach((e => {
this.utils.rightClick($(e), (t => {
let n = $(e).text().trim();
this.utils.q(t, `是否屏蔽演员${n}?`, (() => {
this.saveFilterActor(n).then((e => {
this.detailPageMenuPlugin.filterOne(null, true);
}));
}));
}));
})), this.utils.rightClick($(".preview-images"), (e => {
let t = this.getPageInfo();
this.utils.q(e, `是否屏蔽${t.carNum}?`, (() => {
this.changeData(t.carNum, t.url, "", "filter").then((e => {
this.closePage();
}));
}));
})), this.utils.rightClick($(".column-video-cover"), (e => {
let t = this.getPageInfo();
this.utils.q(e, `是否屏蔽${t.carNum}?`, (() => {
this.changeData(t.carNum, t.url, "", "filter").then((e => {
this.closePage();
}));
}));
})));
}
async saveFilterKeyword(e) {
await this.storageManager.saveFilterKeyword(e), this.refresh();
}
async saveFilterActor(e) {
await this.storageManager.saveFilterActor(e), this.refresh();
}
}
class JavTrailersPlugin extends BasePlugin {
constructor() {
super(), this.hasBand = false;
}
handlePlayJavTrailers() {
this.hasBand || this.utils.loopDetector((() => 0 !== $("#vjs_video_3_html5_api").length), (() => {
setTimeout((() => {
this.hasBand = true;
let e = document.getElementById("vjs_video_3_html5_api");
e.play(), e.currentTime = 5, e.addEventListener("timeupdate", (function () {
e.currentTime >= 14 && e.currentTime < 16 && (e.currentTime += 2);
})), $("#vjs_video_3_html5_api").css({
position: "fixed",
width: "100vw",
height: "100vh",
objectFit: "cover",
zIndex: "999999999"
}), $(".vjs-control-bar").css({
position: "fixed",
bottom: "20px",
zIndex: "999999999"
});
}), 0);
}));
}
handle() {
if (!window.location.hostname.includes("javtrailers")) return;
if ($("h1:contains('Page not found')").length > 0) {
let e = window.location.href.split("video/")[1].toLowerCase().replace("00", "-");
return void (window.location.href = "https://javtrailers.com/search/" + e);
}
let e = $(".videos-list .video-link").toArray();
if (e.length) {
const t = window.location.href.split("search/")[1].toLowerCase(), n = e.find((e => $(e).find(".vid-title").text().toLowerCase().includes(t)));
if (n) return void (window.location.href = $(n).attr("href"));
}
this.handlePlayJavTrailers(), $("#videoPlayerContainer").on("click", (() => {
this.handlePlayJavTrailers();
})), window.addEventListener("message", (e => {
let t = document.getElementById("vjs_video_3_html5_api");
t && (t.currentTime += 5);
})), h.registerHotkey("z", (() => {
const e = document.getElementById("vjs_video_3_html5_api");
e && (e.currentTime += 5);
})), h.registerHotkey("a", (() => window.parent.postMessage("a", "*"))), h.registerHotkey("s", (() => window.parent.postMessage("s", "*")));
}
}
class SubTitleCatPlugin extends BasePlugin {
handle() {
if (!window.location.hostname.includes("subtitlecat")) return;
$(".t-banner-inner").hide(), $("#navbar").hide();
let e = window.location.href.split("=")[1].toLowerCase();
$(".sub-table tr td a").toArray().forEach((t => {
let n = $(t);
n.text().toLowerCase().includes(e) || n.parent().parent().hide();
}));
}
}
class JablePlugin extends BasePlugin {
handle() {
window.location.hostname.includes("jable") && ($("#player")[0].play(), $('button[data-plyr="fullscreen"]').click(),
h.registerHotkey("a", (() => window.parent.postMessage("a", "*"))), h.registerHotkey("s", (() => window.parent.postMessage("s", "*"))));
}
}
var g = (() => "undefined" != typeof GM_xmlhttpRequest ? GM_xmlhttpRequest : void 0)();
class Fc2Plugin extends BasePlugin {
injectBean(detailPageMenuPlugin) {
this.detailPageMenuPlugin = detailPageMenuPlugin;
}
handle() {
}
initCss() {
return "\n /* 弹层样式 */\n .movie-detail-layer .layui-layer-title {\n font-size: 18px;\n color: #333;\n background: #f8f8f8;\n }\n \n \n /* 容器样式 */\n .movie-detail-container {\n display: flex;\n height: 100%;\n background: #fff;\n }\n \n .movie-poster-container {\n flex: 0 0 60%;\n padding: 15px;\n }\n \n .right-box {\n flex: 1;\n padding: 20px;\n overflow-y: auto;\n }\n \n /* 预告片iframe */\n .movie-trailer {\n width: 100%;\n height: 100%;\n min-height: 400px;\n background: #000;\n border-radius: 4px;\n }\n \n /* 电影信息样式 */\n .movie-title {\n font-size: 24px;\n margin-bottom: 15px;\n color: #333;\n }\n \n .movie-meta {\n margin-bottom: 20px;\n color: #666;\n }\n \n .movie-meta span {\n margin-right: 15px;\n }\n \n /* 演员列表 */\n .actor-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n margin-top: 10px;\n }\n \n .actor-tag {\n padding: 4px 12px;\n background: #f0f0f0;\n border-radius: 15px;\n font-size: 12px;\n color: #555;\n }\n \n /* 图片列表 */\n .image-list {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n margin-top: 10px;\n }\n \n .movie-image-thumb {\n width: 120px;\n height: 80px;\n object-fit: cover;\n border-radius: 4px;\n cursor: pointer;\n transition: transform 0.3s;\n }\n \n .movie-image-thumb:hover {\n transform: scale(1.05);\n }\n \n /* 加载中和错误状态 */\n .search-loading, .movie-error {\n padding: 40px;\n text-align: center;\n color: #999;\n }\n \n .movie-error {\n color: #f56c6c;\n }\n \n .fancybox-container{\n z-index:99999999\n }\n \n \n /* 错误提示样式 */\n .movie-not-found, .movie-error {\n text-align: center;\n padding: 30px;\n color: #666;\n }\n \n .movie-not-found h3, .movie-error h3 {\n color: #f56c6c;\n margin: 15px 0;\n }\n \n .icon-warning, .icon-error {\n font-size: 50px;\n color: #e6a23c;\n }\n \n .icon-error {\n color: #f56c6c;\n }\n\n ";
}
openFc2Page(e, t, n) {
(e => new Promise(((t, n) => {
let a = `https://hohoj.tv/search?text=${e}`;
console.log("请求页面", a), g({
method: "GET",
url: a,
onload: function (n) {
let a = n.responseText, i = null;
if (a.includes("找不到任何影片")) return void t({
pageUrl: i
});
const r = (new DOMParser).parseFromString(a, "text/html");
$(r).find(".video-item a").toArray().forEach((t => {
if ($(t).find(".video-item-title").text().includes(e)) {
let e = $(t).attr("href").split("id=")[1];
i = "https://hohoj.tv/embed?id=" + e;
}
})), console.log("解析成功:", i), t({
pageUrl: i
});
},
onerror: function (e) {
console.error("Request failed:", e);
}
});
})))(t.replace("FC2-", "")).then((e => {
let t = e.pageUrl;
const n = document.querySelector(".movie-poster-container"), a = document.querySelector(".movie-trailer");
document.querySelector(".movie-info-container"), t ? $(a).attr("src", t) : (n.innerHTML = '\n <div class="movie-not-found">\n <i class="icon-warning"></i>\n <h3>未找到相关内容</h3>\n <p>hohoj.tv 中没有找到与当前番号相关的影片信息</p>\n </div>\n ',
a.style.display = "none");
}));
let a = "";
(e => new Promise(((t, n) => {
$.ajax({
method: "GET",
url: `${l}/v4/movies/${e}`,
headers: {
jdSignature: o()
},
success: e => {
e.data || (i.msg.error("发生错误" + e.message), n(e.message));
const a = e.data.movie, r = a.id, s = a.actors, o = a.origin_title, l = a.number, c = a.score, d = a.release_date, h = a.preview_images, g = [];
h.forEach((e => {
g.push(e.large_url.replace("https://tp-iu.cmastd.com/rhe951l4q", "https://c0.jdbstatic.com"));
})), console.log(s), t({
movieId: r,
actors: s,
title: o,
carNum: l,
score: c,
releaseDate: d,
imgList: g
});
},
error: e => {
i.msg.error("发生错误" + e.responseJSON.message), n(e);
}
});
})))(e).then((e => {
const t = e.actors || [], n = e.imgList || [];
let i = "";
if (t.length > 0) for (let s = 0; s < t.length; s++) {
let e = t[s];
i += `<span class="actor-tag"><a href="/actors/${e.id}" target="_blank">${e.name}</a></span>`,
0 === e.gender && (a += e.name);
} else i = '<span class="no-data">暂无演员信息</span>';
let r = "";
r = Array.isArray(n) && n.length > 0 ? n.map(((e, t) => `\n <a href="${e}" data-fancybox="movie-gallery" data-caption="剧照 ${t + 1}">\n <img src="${e}" class="movie-image-thumb" alt=""/>\n </a>\n `)).join("") : '<div class="no-data">暂无剧照</div>',
$(".movie-info-container").html(`\n <h3 class="movie-title">${e.title || "无标题"}</h3>\n <div class="movie-meta">\n <span>番号: ${e.carNum || "未知"}</span>\n <span>年份: ${e.releaseDate || "未知"}</span>\n <span>评分: ${e.score || "无"}</span>\n </div>\n <div class="movie-actors">\n <div class="actor-list">主演: ${i}</div>\n </div>\n <div class="movie-gallery" style="margin-top:10px">\n <h4>剧照: </h4>\n <div class="image-list">${r}</div>\n </div>\n `);
})).catch((e => {
console.error(e), $(".movie-info-container").html(`\n <div class="movie-error">加载失败: ${e.message}</div>\n `);
})), (e => new Promise(((t, n) => {
$.ajax({
method: "GET",
url: `${l}/v1/movies/${e}/magnets`,
headers: {
jdSignature: o()
},
success: e => {
let n = e.data.magnets;
t({
magnetList: n
});
},
error: e => {
i.msg.error("发生错误" + e), n(e);
}
});
})))(e).then((e => {
let t = e.magnetList, n = "";
if (t.length > 0) for (let a = 0; a < t.length; a++) {
let e = t[a], i = "";
a % 2 == 0 && (i = "odd"), n += `\n <div class="item columns is-desktop ${i}">\n <div class="magnet-name column is-four-fifths">\n <a href="magnet:?xt=urn:btih:${e.hash}" title="右鍵點擊並選擇「複製鏈接地址」">\n <span class="name">${e.name}.torrent</span>\n <br>\n <span class="meta">\n ${e.hash}, ${e.files_count}個文件\n </span>\n <br>\n <div class="tags">\n ${e.hd ? '<span class="tag is-primary is-small is-light">高清</span>' : ""}\n </div>\n </a>\n </div>\n <div class="buttons column">\n <button class="button is-info is-small copy-to-clipboard" data-clipboard-text="magnet:?xt=urn:btih:${e.hash}" type="button"> 複製 </button>\n </div>\n <div class="date column"><span class="time">${e.created_at}</span></div>\n </div>\n `;
} else n = '<span class="no-data">暂无磁力信息</span>';
$("#magnets-content").html(n);
})).catch((e => {
console.error(e), $("#magnets-content").html(`\n <div class="movie-error">加载失败: ${e.message}</div>\n `);
})), c(e, 20).then((e => {
let t = $("#reviews-content");
if (0 === e.length) return void t.html('<div class="movie-error">无评论</div> ');
let n = '<hr style="border: 0; height: 2px; background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));"/>';
e.forEach((e => {
let t = false;
for (let n = 0; n < this.reviewKeyword.length; n++) if (e.content.indexOf(this.reviewKeyword[n]) > -1) {
t = true;
break;
}
if (t) return;
let a = "";
for (let n = 0; n < e.score; n++) a += '<i class="icon-star"></i>';
n += `\n <div class="item columns is-desktop" style="display:block;margin-top:6px;background-color:#ffffff;padding:10px;margin-left: -10px;word-break: break-word;">\n ${e.username} <span class="score-stars">${a}</span> <span class="time">${e.created_at.replace("T", " ").replace(".000Z", "")}</span> 点赞:${e.likes_count}\n <p style="margin-top: 5px;">${e.content}</p>\n </div>\n `;
})), t.html(n);
})).catch((e => {
console.error(e), $("#reviews-content").html(`\n <div class="movie-error">加载失败: ${e.message}</div>\n `);
})), layer.open({
type: 1,
title: "影片详情",
content: '\n <div class="movie-detail-container">\n <div class="movie-poster-container">\n <iframe class="movie-trailer" frameborder="0" allowfullscreen scrolling="no"></iframe>\n </div>\n <div class="right-box">\n <div class="movie-info-container">\n <div class="search-loading">加载中...</div>\n </div>\n <div id="btn-box">\n <a id="favoriteBtn" class="menu-btn" style="background-color:#25b1dc"><span>收藏</span></a>\n <a id="filterBtn" class="menu-btn" style="background-color:#de3333"><span>屏蔽</span></a>\n <a id="hasDownBtn" class="menu-btn" style="background-color:#7bc73b"><span>加入已下载</span></a>\n </div>\n <div class="message video-panel" style="margin-top:20px">\n <div id="magnets-content" class="magnet-links">\n <div class="search-loading">加载中...</div>\n </div>\n </div>\n <div id="reviews-content">\n <div class="search-loading">加载中...</div>\n </div>\n </div>\n </div>\n ',
area: ["80%", "90%"],
skin: "movie-detail-layer",
scrollbar: false,
success: (e, i) => {
$("#favoriteBtn").on("click", (e => {
this.changeData(t, n, a, "favorite").then((e => layer.closeAll()));
})), $("#filterBtn").on("click", (e => {
this.utils.q(e, `是否屏蔽${t}?`, (() => {
this.changeData(t, n, a, "filter").then((e => layer.closeAll()));
}));
})), $("#hasDownBtn").on("click", (e => {
this.changeData(t, n, a, "hasDown").then((e => layer.closeAll()));
}));
}
});
}
}
class FoldCategoryPlugin extends BasePlugin {
handle() {
if (!this.isListPage) return;
let e = $(".tabs ul"), t = $("h2.section-title"), n = "y" === localStorage.foldCategory;
const [a, i] = n ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"];
let r;
e.length > 0 && (e.append(`\n <li class="is-active" id="foldCategoryBtn">\n <a class="menu-btn" style="background-color:#7bc73b !important;margin-left: 20px;border-bottom:none !important;border-radius:3px;">\n <span>${a}</span>\n <i style="margin-left: 10px" class="${i}"></i>\n </a>\n </li>\n `),
r = $("#tags")), t.length > 0 && (t.append(`\n <div id="foldCategoryBtn">\n <a class="menu-btn" style="background-color:#7bc73b !important;margin-left: 20px;border-bottom:none !important;border-radius:3px;">\n <span>${a}</span>\n <i style="margin-left: 10px" class="${i}"></i>\n </a>\n </div>\n `),
r = $("section > div > div.box")), r[n ? "hide" : "show"](), $("#foldCategoryBtn").on("click", (e => {
e.preventDefault(), n = !n, localStorage.foldCategory = n ? "y" : "n";
const [t, a] = n ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"];
$("#foldCategoryBtn").find("span").text(t).end().find("i").attr("class", a), r[n ? "hide" : "show"]();
}));
let s = $("#tags dl div.tag.is-info").map((function () {
return $(this).text().replaceAll("\n", "").replaceAll(" ", "");
})).get().join(" ");
e.append(`<li style="margin-left: 50px;float: right"><span>${s}</span></li>`);
}
}
window.$ = n, window.jQuery = n, window.layer = layer, layer.error = e => {
layer.msg(e, {
icon: 2
});
}, i.importResource("https://cdn.jsdelivr.net/npm/[email protected]/layer.min.css"),
function () {
const e = new PluginManager;
e.register(ListPagePlugin), e.register(AutoPagePlugin), e.register(Fc2Plugin), e.register(FoldCategoryPlugin),
e.register(ListPageMenuPlugin), e.register(SearchPlugin), e.register(DetailPagePlugin),
e.register(ReviewPlugin), e.register(DetailPageMenuPlugin), e.register(HighlightMagnetPlugin),
e.register(PreviewVideoPlugin), e.register(SelectTextFilterPlugin), e.register(JavTrailersPlugin),
e.register(SubTitleCatPlugin), e.register(JablePlugin), e.process();
}();
})($, layer, md5);