- // ==UserScript==
- // @name JavScript
- // @namespace JavScript@blc
- // @version 3.1.0
- // @author blc
- // @description 一站式体验,JavBus 兼容
- // @icon https://s1.ax1x.com/2022/04/01/q5lzYn.png
- // @include *
- // @require https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js
- // @require https://unpkg.com/infinite-scroll@4/dist/infinite-scroll.pkgd.min.js
- // @resource play https://s4.ax1x.com/2022/01/12/7nYuKe.png
- // @resource success https://s1.ax1x.com/2022/04/01/q5l2LD.png
- // @resource info https://s1.ax1x.com/2022/04/01/q5lyz6.png
- // @resource warn https://s1.ax1x.com/2022/04/01/q5lgsO.png
- // @resource error https://s1.ax1x.com/2022/04/01/q5lcQK.png
- // @connect *
- // @run-at document-start
- // @grant GM_registerMenuCommand
- // @grant GM_getResourceURL
- // @grant GM_xmlhttpRequest
- // @grant GM_setClipboard
- // @grant GM_notification
- // @grant GM_addElement
- // @grant GM_deleteValue
- // @grant GM_listValues
- // @grant GM_openInTab
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_info
- // @license GPL-3.0-only
- // @compatible chrome
- // @compatible edge
- // ==/UserScript==
-
- /**
- * TODO:
- * ⏳ 脚本 - JavDB 兼容
- * ⏳ 详情 - 发送磁链至 aria2 下载?
- * ⏳ 列表 - 自定义数据聚合页
- * ⏳ 脚本 - icon, style, bootstrap 精简 & 调整统一
- */
-
- (function () {
- // match domain
- const MatchDomains = [
- { domain: "JavBus", regex: /(jav|bus|dmm|see|cdn|fan){2}\./g },
- { domain: "JavDB", regex: /javdb\d*\.com/g },
- { domain: "Drive115", regex: /captchaapi\.115\.com/g },
- ];
- const Matched = MatchDomains.find(({ regex }) => regex.test(location.host));
- if (!Matched?.domain) return;
-
- // document
- const DOC = document;
- DOC.create = (tag, attr = {}, child) => {
- const element = DOC.createElement(tag);
- Object.keys(attr).forEach(name => element.setAttribute(name, attr[name]));
- typeof child === "string" && element.appendChild(DOC.createTextNode(child));
- typeof child === "object" && element.appendChild(child);
- return element;
- };
-
- // request
- const request = (url, data = {}, method = "GET", params = {}) => {
- if (!url) return;
-
- method = method ? method.toUpperCase().trim() : "GET";
- if (!["GET", "POST"].includes(method)) return;
-
- if (Object.prototype.toString.call(data) === "[object Object]") {
- data = Object.keys(data).reduce((pre, cur) => {
- return `${pre ? `${pre}&` : pre}${cur}=${encodeURIComponent(data[cur])}`;
- }, "");
- }
-
- if (method === "GET") {
- params.responseType = params.responseType ?? "document";
- if (data) {
- if (url.includes("?")) {
- url = `${url}${url.charAt(url.length - 1) === "&" ? "" : "&"}${data}`;
- } else {
- url = `${url}?${data}`;
- }
- }
- }
- if (method === "POST") {
- params.responseType = params.responseType ?? "json";
- const headers = params.headers ?? {};
- params.headers = { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", ...headers };
- }
-
- return new Promise(resolve => {
- GM_xmlhttpRequest({
- url,
- data,
- method,
- timeout: 20000,
- onload: ({ status, response }) => {
- if (response?.errcode === 911) verify();
- if (status === 404) response = false;
- if (response && ["", "text"].includes(params.responseType)) {
- const htmlRegex = /<\/?[a-z][\s\S]*>/i;
- const jsonRegex = /^{.*}$/;
- if (htmlRegex.test(response)) {
- response = new DOMParser().parseFromString(response, "text/html");
- } else if (jsonRegex.test(response)) {
- response = JSON.parse(response);
- }
- }
- resolve(response);
- },
- ...params,
- });
- });
- };
-
- // utils
- const getDate = timestamp => {
- const date = timestamp ? new Date(timestamp) : new Date();
- const Y = date.getFullYear();
- const M = `${date.getMonth() + 1}`.padStart(2, "0");
- const D = `${date.getDate()}`.padStart(2, "0");
- return `${Y}-${M}-${D}`;
- };
- const addCopyTarget = (selectors, attr = {}) => {
- const node = DOC.querySelector(selectors);
- const _attr = { "data-copy": node.textContent.trim(), class: "x-ml", href: "javascript:void(0);" };
- const target = DOC.create("a", { ..._attr, ...attr }, "复制");
- target.addEventListener("click", handleCopyTxt);
- node.appendChild(target);
- };
- const handleCopyTxt = (e, text) => {
- if (!e?.target?.dataset?.copy?.trim()) return;
-
- e.preventDefault();
- e.stopPropagation();
-
- const { target } = e;
- GM_setClipboard(target.dataset.copy.trim());
- const originText = target.textContent ?? "";
- target.textContent = text ?? "成功";
-
- const timer = setTimeout(() => {
- target.textContent = originText;
- clearTimeout(timer);
- }, 400);
-
- return 1;
- };
- const transToBytes = sizeStr => {
- const sizer = [
- { unit: /byte/gi, transform: size => size },
- { unit: /kb/gi, transform: size => size * Math.pow(1000, 1) },
- { unit: /mb/gi, transform: size => size * Math.pow(1000, 2) },
- { unit: /gb/gi, transform: size => size * Math.pow(1000, 3) },
- { unit: /kib/gi, transform: size => size * Math.pow(1024, 1) },
- { unit: /mib/gi, transform: size => size * Math.pow(1024, 2) },
- { unit: /gib/gi, transform: size => size * Math.pow(1024, 3) },
- ];
-
- const size = sizeStr.replace(/[a-zA-Z\s]/g, "");
- if (size <= 0) return 0;
-
- return (
- sizer
- .find(({ unit }) => unit.test(sizeStr))
- ?.transform(size)
- ?.toFixed(2) ?? 0
- );
- };
- const unique = (arr, key) => {
- if (!key) return Array.from(new Set(arr));
-
- arr = arr.map(item => {
- item[key] = item[key]?.toLowerCase();
- return item;
- });
- return Array.from(new Set(arr.map(e => e[key]))).map(e => arr.find(x => x[key] === e));
- };
- const notify = msg => {
- GM_notification({
- highlight: true,
- silent: true,
- timeout: 2000,
- ...msg,
- text: msg?.text ?? GM_info.script.name,
- image: GM_getResourceURL(msg?.image ?? "info"),
- onclick: msg?.clickUrl ? () => GM_openInTab(msg.clickUrl, { setParent: true, active: true }) : () => {},
- });
- };
- const verify = () => {
- const h = 667;
- const w = 375;
- const t = (window.screen.availHeight - h) / 2;
- const l = (window.screen.availWidth - w) / 2;
-
- window.open(
- `https://captchaapi.115.com/?ac=security_code&type=web&cb=Close911_${new Date().getTime()}`,
- "验证账号",
- `height=${h},width=${w},top=${t},left=${l},toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,status=no`
- );
- };
- const delay = n => new Promise(r => setTimeout(r, n * 1000));
- const codeParse = code => {
- const codes = code
- .split(/-|_/)
- .filter(Boolean)
- .map(item => (item.startsWith("0") ? item.slice(1) : item));
-
- return {
- prefix: codes[0],
- regex: new RegExp(codes.join(".{0,4}"), "i"),
- };
- };
-
- // store
- class Store {
- static init() {
- const date = getDate();
- const cdKey = "CD";
- if (GM_getValue(cdKey, "") === date) return;
-
- GM_setValue(cdKey, date);
- GM_setValue("DETAILS", {});
- GM_setValue("RESOURCE", []);
- }
- static getDetail(key) {
- const details = GM_getValue("DETAILS", {});
- return details[key] ?? {};
- }
- static upDetail(key, val) {
- const details = GM_getValue("DETAILS", {});
- details[key] = { ...this.getDetail(key), ...val };
- GM_setValue("DETAILS", details);
- }
- }
-
- // apis
- class Apis {
- // movie
- static async movieImg(code) {
- code = code.toUpperCase();
-
- const [blogJav, javStore] = await Promise.all([
- request(`https://www.google.com/search?q=${code} site:blogjav.net`),
- request(`https://javstore.net/search/${code}.html`),
- ]);
-
- const [bjRes, jsRes] = await Promise.all([
- request(
- `http://webcache.googleusercontent.com/search?q=cache:${
- blogJav?.querySelector("#rso .g .yuRUbf a")?.href
- }`
- ),
- request(javStore?.querySelector("#content_news li a")?.href),
- ]);
-
- let bjImg = "";
- if (bjRes) {
- bjImg = bjRes
- ?.querySelector("#page .entry-content a img")
- ?.getAttribute("data-lazy-src")
- .replace("//t", "//img")
- .replace("thumbs", "images");
- }
-
- return bjImg || jsRes?.querySelector(".news a img[alt*='.th']").src.replace(".th", "") || "";
- }
- static async movieVideo(code, studio) {
- code = code.toLowerCase();
-
- if (studio) {
- const matchList = [
- {
- name: "東京熱",
- match: "https://my.cdn.tokyo-hot.com/media/samples/%s.mp4",
- },
- {
- name: "カリビアンコム",
- match: "https://smovie.caribbeancom.com/sample/movies/%s/1080p.mp4",
- },
- {
- name: "一本道",
- match: "http://smovie.1pondo.tv/sample/movies/%s/1080p.mp4",
- },
- {
- name: "HEYZO",
- trans: code => code.replace(/HEYZO\-/gi, ""),
- match: "https://www.heyzo.com/contents/3000/%s/heyzo_hd_%s_sample.mp4",
- },
- ];
- const matched = matchList.find(({ name }) => name === studio);
- if (matched) return matched.match.replace(/%s/g, matched.trans ? matched.trans(code) : code);
- }
-
- let [r18, xrmoo] = await Promise.all([
- request(`https://www.r18.com/common/search/order=match/searchword=${code}/`),
- request(`http://dmm.xrmoo.com/sindex.php?searchstr=${code}`),
- ]);
-
- r18 = r18?.querySelector("a.js-view-sample");
-
- return (
- r18?.getAttribute("data-video-high") ||
- r18?.getAttribute("data-video-med") ||
- r18?.getAttribute("data-video-low") ||
- xrmoo
- ?.querySelector(".card .card-footer a.viewVideo")
- ?.getAttribute("data-link")
- .replace("_sm_w", "_dmb_w") ||
- ""
- );
- }
- static async moviePlayer(code) {
- code = code.toUpperCase();
- const { regex } = codeParse(code);
-
- const matchList = [
- {
- site: "Netflav",
- host: "https://netflav.com/",
- search: "search?type=title&keyword=%s",
- selectors: ".grid_root .grid_cell",
- filter: {
- name: e => e?.querySelector(".grid_title").textContent,
- },
- },
- {
- site: "Avgle",
- host: "https://avgle.com/",
- search: "search/videos?search_query=%s&search_type=videos",
- selectors: ".row .well.well-sm",
- filter: {
- name: e => e?.querySelector(".video-title")?.textContent,
- },
- },
- ];
-
- const matched = await Promise.all(
- matchList.map(({ host, search }) => request(`${host}${search.replace(/%s/g, code)}`))
- );
-
- const players = [];
- for (let index = 0; index < matchList.length; index++) {
- let node = matched[index];
- if (!node) continue;
-
- const { selectors, site, filter, host } = matchList[index];
- node = node?.querySelectorAll(selectors);
- if (!node?.length) continue;
-
- for (const item of node) {
- const player = { from: site };
- Object.keys(filter).forEach(key => {
- player[key] = filter[key](item) ?? "";
- });
-
- const { name } = player;
- let link = item?.querySelector("a")?.getAttribute("href");
- if (!name || !regex.test(name) || !link) continue;
-
- player.link = !link.includes("//") ? `${host}${link.replace(/^\//, "")}` : link;
- if (!("zh" in player)) player.zh = /中文/g.test(name);
-
- if (player.zh) {
- players.unshift(player);
- break;
- }
- players.push(player);
- }
-
- if (players.find(item => item.zh)) break;
- }
-
- return players.length ? players[0].link : "";
- }
- static async movieTitle(sentence) {
- const st = encodeURIComponent(sentence.trim());
- const data = {
- async: `translate,sl:auto,tl:zh-CN,st:${st},id:1650701080679,qc:true,ac:false,_id:tw-async-translate,_pms:s,_fmt:pc`,
- };
-
- const res = await request(
- "https://www.google.com/async/translate?vet=12ahUKEwixq63V3Kn3AhUCJUQIHdMJDpkQqDh6BAgCECw..i&ei=CbNjYvGCPYLKkPIP05O4yAk&yv=3",
- data,
- "POST",
- { responseType: "" }
- );
-
- return res?.querySelector("#tw-answ-target-text")?.textContent ?? "";
- }
- static async movieStar(code) {
- code = code.toUpperCase();
- const site = "https://javdb.com";
-
- let res = await request(`${site}/search?q=${code}&sb=0`);
- const href = res?.querySelector("#videos .grid-item a").getAttribute("href");
- if (!href) return;
-
- res = await request(`${site}${href}`);
- res = res?.querySelectorAll(".movie-panel-info > .panel-block");
- if (!res?.length) return;
-
- res = res[res.length - 2]?.querySelector(".value").textContent.trim();
- return res
- .split(/\n/)
- .filter(item => item.indexOf("♀") !== -1)
- .map(item => item.replace("♀", "").trim());
- }
- static async movieMagnet(code) {
- code = code.toUpperCase();
-
- const matchList = [
- {
- site: "Sukebei",
- host: "https://sukebei.nyaa.si/",
- search: "?f=0&c=0_0&q=%s",
- selectors: ".table-responsive table tbody tr",
- filter: {
- name: e => e?.querySelector("td:nth-child(2) a").textContent,
- link: e => e?.querySelector("td:nth-child(3) a:last-child").href,
- size: e => e?.querySelector("td:nth-child(4)").textContent,
- date: e => e?.querySelector("td:nth-child(5)").textContent.split(" ")[0],
- href: e => e?.querySelector("td:nth-child(2) a").getAttribute("href"),
- },
- },
- {
- site: "BTSOW",
- host: "https://btsow.rest/",
- search: "search/%s",
- selectors: ".data-list .row:not(.hidden-xs)",
- filter: {
- name: e => e?.querySelector(".file").textContent,
- link: e => `magnet:?xt=urn:btih:${e?.querySelector("a").href.split("/").pop()}`,
- size: e => e?.querySelector(".size").textContent,
- date: e => e?.querySelector(".date").textContent,
- href: e => e?.querySelector("a").getAttribute("href"),
- },
- },
- ];
-
- const matched = await Promise.all(
- matchList.map(({ host, search }) => request(`${host}${search.replace(/%s/g, code)}`))
- );
-
- const magnets = [];
- for (let index = 0; index < matchList.length; index++) {
- let node = matched[index];
- if (!node) continue;
-
- const { selectors, site, filter, host } = matchList[index];
- node = node?.querySelectorAll(selectors);
- if (!node?.length) continue;
-
- for (const item of node) {
- const magnet = { from: site };
- Object.keys(filter).forEach(key => {
- magnet[key] = filter[key](item)?.trim();
- });
-
- magnet.bytes = transToBytes(magnet.size);
- magnet.zh = /中文/g.test(magnet.name);
- magnet.link = magnet.link.split("&")[0];
- const { href } = magnet;
- if (href && !href.includes("//")) magnet.href = `${host}${href.replace(/^\//, "")}`;
-
- magnets.push(magnet);
- }
- }
-
- return magnets;
- }
- // drive
- static async searchVideo(params = { search_value: "" } | "") {
- if (typeof params === "string") params = { search_value: params };
- if (!params.search_value.trim()) return [];
-
- const res = await request(
- "https://webapi.115.com/files/search",
- {
- offset: 0,
- limit: 10000,
- date: "",
- aid: 1,
- cid: 0,
- pick_code: "",
- type: 4,
- source: "",
- format: "json",
- o: "user_ptime",
- asc: 0,
- star: "",
- suffix: "",
- ...params,
- },
- "GET",
- { responseType: "json" }
- );
-
- return (res.data ?? []).map(({ cid, fid, n, pc, t }) => {
- return { cid, fid, n, pc, t };
- });
- }
- static async getFile(params = {}) {
- const res = await request(
- "https://webapi.115.com/files",
- {
- aid: 1,
- cid: 0,
- o: "user_ptime",
- asc: 0,
- offset: 0,
- show_dir: 1,
- limit: 115,
- code: "",
- scid: "",
- snap: 0,
- natsort: 1,
- record_open_time: 1,
- source: "",
- format: "json",
- ...params,
- },
- "GET",
- { responseType: "json" }
- );
-
- return res?.data ?? [];
- }
- static async getSign() {
- const { state, sign, time } = await request(
- "http://115.com/",
- { ct: "offline", ac: "space", _: new Date().getTime() },
- "GET",
- { responseType: "json" }
- );
-
- if (state) return { sign, time };
-
- notify({
- title: "请求失败,115未登录",
- text: "点击跳转登录",
- image: "error",
- clickUrl: "http://115.com/?mode=login",
- timeout: 3000,
- });
- }
- static async addTaskUrl({ url, wp_path_id = "", sign, time }) {
- const _sign = sign && time ? { sign, time } : await this.getSign();
- if (!_sign) return;
-
- return await request(
- "https://115.com/web/lixian/?ct=lixian&ac=add_task_url",
- { url, wp_path_id, ..._sign },
- "POST"
- );
- }
- static getVideo(cid = "") {
- return this.getFile({ cid, custom_order: 0, star: "", suffix: "", type: 4 });
- }
- static driveRename(res) {
- const data = {};
- for (const { fid, file_name } of res) data[`files_new_name[${fid}]`] = file_name;
-
- return request("https://webapi.115.com/files/batch_rename", data, "POST");
- }
- }
-
- // common
- class Common {
- menus = {
- tabs: [
- { title: "全站", key: "global", prefix: "G" },
- { title: "列表页", key: "list", prefix: "L" },
- { title: "详情页", key: "movie", prefix: "M" },
- { title: "115 相关", key: "drive", prefix: "D" },
- ],
- commands: [
- "G_DARK",
- "G_SEARCH",
- "G_CLICK",
- "L_MIT",
- "L_MTH",
- "L_MTL",
- "L_SCROLL",
- "M_IMG",
- "M_VIDEO",
- "M_PLAYER",
- "M_TITLE",
- "M_STAR",
- "M_SUB",
- "M_SORT",
- "M_MAGNET",
- "D_MATCH",
- "D_CID",
- "D_VERIFY",
- "D_RENAME",
- ],
- details: [
- {
- name: "暗黑模式",
- key: "G_DARK",
- type: "switch",
- info: "常用页面暗黑模式",
- defaultVal: true,
- hotkey: "d",
- },
- {
- name: "快捷搜索",
- key: "G_SEARCH",
- type: "switch",
- info: "<kbd>/</kbd> 获取焦点,<kbd>ctrl</kbd> + <kbd>/</kbd> 快速搜索粘贴板第一项",
- defaultVal: true,
- hotkey: "k",
- },
- {
- name: "点击事件",
- key: "G_CLICK",
- type: "switch",
- info: "影片/演员卡片以新窗口打开,左击前台,右击后台",
- defaultVal: true,
- hotkey: "c",
- },
- {
- name: "预览图替换",
- key: "L_MIT",
- type: "switch",
- info: "替换为封面大图",
- defaultVal: true,
- },
- {
- name: "标题等高",
- key: "L_MTH",
- type: "switch",
- info: "影片标题强制等高",
- defaultVal: false,
- },
- {
- name: "标题最大行",
- key: "L_MTL",
- type: "number",
- info: "影片标题最大显示行数,超出省略。0 不限制 (等高模式下最小有效值 1)",
- placeholder: "仅支持整数 ≥ 0",
- defaultVal: 1,
- },
- {
- name: "滚动加载",
- key: "L_SCROLL",
- type: "switch",
- info: "滚动自动加载下一页",
- defaultVal: true,
- },
- {
- name: "预览大图",
- key: "M_IMG",
- type: "switch",
- info: `获取自 <a href="https://blogjav.net/" class="link-primary">BlogJav</a>, <a href="https://javstore.net/" class="link-primary">JavStore</a>`,
- defaultVal: true,
- },
- {
- name: "预览视频",
- key: "M_VIDEO",
- type: "switch",
- info: `获取自 <a href="https://www.r18.com/" class="link-primary">R18</a>, <a href="http://dmm.xrmoo.com/" class="link-primary">闲人吧</a>`,
- defaultVal: true,
- },
- {
- name: "在线播放",
- key: "M_PLAYER",
- type: "switch",
- info: `获取自 <a href="https://netflav.com/" class="link-primary">Netflav</a>, <a href="https://avgle.com/" class="link-primary">Avgle</a>`,
- defaultVal: true,
- },
- {
- name: "标题机翻",
- key: "M_TITLE",
- type: "switch",
- info: `翻自 <a href="https://google.com/" class="link-primary">Google</a>`,
- defaultVal: true,
- },
- {
- name: "演员匹配",
- key: "M_STAR",
- type: "switch",
- info: `如无,获取自 <a href="https://javdb.com/" class="link-primary">JavDB</a>`,
- defaultVal: true,
- },
- {
- name: "字幕筛选",
- key: "M_SUB",
- type: "switch",
- info: "额外针对名称为 <code>大写字母</code> + <code>-C</code> 资源判断",
- defaultVal: false,
- },
- {
- name: "磁力排序",
- key: "M_SORT",
- type: "switch",
- info: "综合排序 <code>字幕</code> > <code>大小</code> > <code>日期</code>",
- defaultVal: true,
- },
- {
- name: "磁力搜索",
- key: "M_MAGNET",
- type: "switch",
- info: `自动去重,获取自 <a href="https://sukebei.nyaa.si/" class="link-primary">Sukebei</a>, <a href="https://btsow.rest/" class="link-primary">BTSOW</a>`,
- defaultVal: true,
- },
- {
- name: "网盘资源",
- key: "D_MATCH",
- type: "switch",
- info: "资源匹配 & 离线开关 (<strong>确保已登录网盘</strong>)",
- defaultVal: true,
- },
- {
- name: "下载目录",
- key: "D_CID",
- type: "input",
- info: "离线下载自定目录 <strong>cid</strong> 或 <strong>动态参数</strong>,建议 <strong>cid</strong> 效率更高<br><strong>动态参数</strong> 支持网盘根目录下文件夹名称<br>默认动态参数 <code>${云下载}</code>",
- placeholder: "cid 或 动态参数",
- defaultVal: "${云下载}",
- },
- {
- name: "文件验证",
- key: "D_VERIFY",
- type: "number",
- info: "『<strong>一键离线</strong>』可用,查询以验证离线下载结果,每次间隔一秒<br>设置次数上限,次数越多验证越精准<br>建议 3 ~ 5,默认 3",
- placeholder: "仅支持整数 ≥ 0",
- defaultVal: 3,
- },
- {
- name: "文件重命名",
- key: "D_RENAME",
- type: "input",
- info: '需要『<strong>文件验证</strong>』&『<strong>一键离线</strong>』可用,支持动态参数如下<br><code>${字幕}</code> "【中文字幕】",非字幕资源则为空<br><code>${番号}</code> 页面番号,字母自动转大写;番号为必须值,如新命名未包含将自动追加前缀<br><code>${标题}</code> 页面标题,标题可能已包含番号,自行判断',
- placeholder: "勿填写后缀,可能导致资源不可用",
- defaultVal: "${字幕}${番号} - ${标题}",
- },
- ],
- };
- route = null;
- pcUrl = "https://v.anxia.com/?pickcode=";
-
- init() {
- Store.init();
- this.route = Object.keys(this.routes).find(key => this.routes[key].test(location.pathname));
- this.createMenu();
- return { ...this, ...this[this.route] };
- }
- createMenu() {
- GM_addStyle(`
- .x-scrollbar-hide ::-webkit-scrollbar {
- display: none;
- }
- .x-mask {
- display: none;
- position: fixed;
- z-index: 9999;
- width: 100vw;
- height: 100vh;
- top: 0;
- left: 0;
- border: none;
- background: transparent;
- backdrop-filter: blur(50px);
- padding: 0;
- margin: 0;
- box-sizing: border-box;
- }
- iframe.x-mask {
- backdrop-filter: none;
- }
- .x-show {
- display: block !important;
- }
- `);
-
- let { tabs, commands, details } = this.menus;
-
- const exclude = this.excludeMenu;
- if (exclude?.length) {
- const regex = new RegExp(`^(?!${exclude.join("|")})`);
- commands = commands.filter(command => regex.test(command));
- tabs = tabs.filter(tab => commands.find(command => command.startsWith(tab.prefix)));
- }
- if (!commands.length) return;
-
- const domain = Matched.domain;
- const active = tabs.find(({ key }) => key === this.route) ?? tabs[0];
-
- let tabStr = "";
- let panelStr = "";
- for (let index = 0; index < tabs.length; index++) {
- const { title, key, prefix } = tabs[index];
-
- const curCommands = commands.filter(command => command.startsWith(prefix));
- const curLen = curCommands.length;
- if (!curLen) continue;
-
- const isActive = key === active.key;
- tabStr += `
- <a
- class="nav-link${isActive ? " active" : ""}"
- id="${key}-tab"
- aria-controls="${key}"
- aria-selected="${isActive}"
- data-bs-toggle="pill"
- href="#${key}"
- role="tab"
- >
- ${title}设置
- </a>`;
- panelStr += `
- <div
- class="tab-pane fade${isActive ? " show active" : ""}"
- id="${key}"
- aria-labelledby="${key}-tab"
- role="tabpanel"
- >
- `;
-
- for (let curIdx = 0; curIdx < curLen; curIdx++) {
- const {
- key: curKey,
- defaultVal,
- name,
- type,
- hotkey = "",
- placeholder = "",
- info,
- } = details.find(item => item.key === curCommands[curIdx]);
-
- const uniKey = `${domain}_${curKey}`;
- const val = GM_getValue(uniKey, defaultVal);
- this[curKey] = val;
-
- panelStr += `<div${curIdx + 1 === curLen ? "" : ` class="mb-3"`}>`;
-
- if (type === "switch") {
- if (curKey.startsWith("G")) {
- GM_registerMenuCommand(
- `${val ? "关闭" : "开启"}${name}`,
- () => {
- GM_setValue(uniKey, !val);
- location.reload();
- },
- hotkey
- );
- }
- panelStr += `
- <div class="form-check form-switch">
- <input
- type="checkbox"
- class="form-check-input"
- id="${curKey}"
- aria-describedby="${curKey}_Help"
- ${val ? "checked" : ""}
- name="${curKey}"
- >
- <label class="form-check-label" for="${curKey}">${name}</label>
- </div>
- `;
- } else {
- panelStr += `
- <label class="form-label" for="${curKey}">${name}</label>
- <input
- type="${type}"
- class="form-control"
- id="${curKey}"
- aria-describedby="${curKey}_Help"
- value="${val ?? ""}"
- placeholder="${placeholder}"
- name="${curKey}"
- >
- `;
- }
-
- if (info) panelStr += `<div id="${curKey}_Help" class="form-text">${info}</div>`;
- panelStr += `</div>`;
- }
-
- panelStr += `</div>`;
- }
-
- if (!tabStr || !panelStr) return;
- DOC.addEventListener("DOMContentLoaded", () => {
- DOC.body.insertAdjacentHTML(
- "beforeend",
- `<iframe
- class="x-mask"
- id="control-panel"
- name="control-panel"
- src="about:blank"
- title="控制面板"
- ></iframe>`
- );
- const iframe = DOC.querySelector("iframe#control-panel");
- const _DOC = iframe.contentWindow.document;
-
- _DOC.querySelector("head").insertAdjacentHTML(
- "beforeend",
- `<link
- href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
- rel="stylesheet"
- >
- <style>${this.style}</style>
- <base target="_blank">`
- );
- const body = _DOC.querySelector("body");
- body.classList.add("bg-transparent");
- GM_addElement(body, "script", {
- src: "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js",
- });
- body.insertAdjacentHTML(
- "afterbegin",
- `
- <button
- type="button"
- class="d-none"
- id="openModal"
- class="btn btn-primary"
- data-bs-toggle="modal"
- data-bs-target="#controlPanel"
- >
- open
- </button>
- <div
- class="modal fade"
- id="controlPanel"
- tabindex="-1"
- aria-labelledby="controlPanelLabel"
- aria-hidden="true"
- >
- <div class="modal-dialog modal-lg modal-fullscreen-lg-down modal-dialog-scrollable">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title" id="controlPanelLabel">
- 控制面板
- -
- <a
- href="https://sleazyfork.org/zh-CN/scripts/435360-javscript"
- class="link-secondary text-decoration-none"
- target="_blank"
- >
- ${GM_info.script.name} v${GM_info.script.version}
- </a>
- </h5>
- <button
- type="button"
- class="btn-close"
- data-bs-dismiss="modal"
- aria-label="Close"
- >
- </button>
- </div>
- <div class="modal-body">
- <form class="mb-0">
- <div class="d-flex align-items-start">
- <div
- class="nav flex-column nav-pills me-3 sticky-top"
- id="v-pills-tab"
- role="tablist"
- aria-orientation="vertical"
- >
- ${tabStr}
- </div>
- <div class="tab-content flex-fill" id="v-pills-tabContent">
- ${panelStr}
- </div>
- </div>
- </form>
- </div>
- <div class="modal-footer">
- <button
- type="button"
- class="btn btn-danger"
- data-bs-dismiss="modal"
- data-action="restart"
- >
- 重置脚本
- </button>
- <button
- type="button"
- class="btn btn-secondary"
- data-bs-dismiss="modal"
- data-action="reset"
- >
- 恢复所有默认
- </button>
- <button
- type="button"
- class="btn btn-primary"
- data-bs-dismiss="modal"
- data-action="save"
- >
- 保存设置
- </button>
- </div>
- </div>
- </div>
- </div>
- `
- );
- body.querySelector(".modal-footer").addEventListener("click", e => {
- const { action } = e.target.dataset;
- if (!action) return;
-
- e.preventDefault();
- e.stopPropagation();
-
- if (action === "save") {
- const data = Object.fromEntries(new FormData(body.querySelector("form")).entries());
- commands.forEach(key => GM_setValue(`${domain}_${key}`, data[key] ?? ""));
- }
- if (action === "reset") {
- GM_listValues().forEach(name => name.startsWith(domain) && GM_deleteValue(name));
- }
- if (action === "restart") {
- GM_listValues().forEach(name => GM_deleteValue(name));
- }
-
- location.reload();
- });
-
- const toggleIframe = () => {
- DOC.body.parentNode.classList.toggle("x-scrollbar-hide");
- iframe.classList.toggle("x-show");
- };
- const openModal = () => {
- if (iframe.classList.contains("x-show")) return;
- toggleIframe();
- _DOC.querySelector("#openModal").click();
- };
- GM_registerMenuCommand("控制面板", openModal, "s");
- _DOC.querySelector("#controlPanel").addEventListener("hidden.bs.modal", toggleIframe);
- });
- }
-
- // styles
- variables = `
- :root {
- --x-bgc: #121212;
- --x-sub-bgc: #202020;
- --x-ftc: #fffffff2;
- --x-sub-ftc: #aaaaaa;
- --x-grey: #313131;
- --x-blue: #0a84ff;
- --x-orange: #ff9f0a;
- --x-green: #30d158;
- --x-red: #ff453a;
-
- --x-line-h: 22px;
-
- --x-thumb-w: 190px;
- --x-cover-w: 295px;
-
- --x-thumb-ratio: 334 / 473;
- --x-cover-ratio: 135 / 91;
- --x-avatar-ratio: 1;
- --x-sprite-ratio: 4 / 3;
-
- --x-shadow: 0 1px 3px rgb(0 0 0 / 30%);
- }
- `;
- style = `
- ::-webkit-scrollbar {
- width: 8px !important;
- height: 8px !important;
- }
- ::-webkit-scrollbar-thumb {
- border-radius: 4px !important;
- background: #c1c1c1;
- }
- * {
- outline: none !important;
- text-shadow: none !important;
- text-decoration: none !important;
- }
- body {
- overflow-y: overlay;
- }
- footer {
- display: none !important;
- }
- `;
- dmStyle = `
- ::-webkit-scrollbar-thumb, button {
- background: var(--x-grey) !important;
- }
- * {
- box-shadow: none !important;
- }
- *:not(span[class]) {
- border-color: var(--x-grey) !important;
- }
- html, body, input, textarea {
- background: var(--x-bgc) !important;
- }
- body, *::placeholder {
- color: var(--x-sub-ftc) !important;
- }
- nav {
- background: var(--x-sub-bgc) !important;
- }
- a, button, h1, h2, h3, h4, h5, h6, input, p, textarea {
- color: var(--x-ftc) !important;
- }
- img {
- filter: brightness(.9) contrast(.9) !important;
- }
- `;
- customStyle = `
- #x-status {
- margin-bottom: 20px;
- color: var(--x-sub-ftc);
- text-align: center;
- font-size: 14px !important;
- }
- .x-in {
- transition: opacity .25s linear;
- opacity: 1 !important;
- }
- .x-out {
- transition: opacity .25s linear;
- opacity: 0 !important;
- }
- .x-cover {
- width: var(--x-cover-w) !important;
- }
- .x-cover > *:first-child {
- aspect-ratio: var(--x-cover-ratio) !important;
- }
- .x-ellipsis {
- overflow: hidden;
- text-overflow: ellipsis;
- display: -webkit-box !important;
- -webkit-line-clamp: 1;
- -webkit-box-orient: vertical;
- white-space: unset !important;
- }
- .x-line {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .x-title {
- line-height: var(--x-line-h) !important;
- }
- .x-matched {
- font-weight: bold;
- color: var(--x-blue) !important;
- }
- .x-player {
- position: relative;
- overflow: hidden;
- display: block;
- cursor: pointer;
- }
- .x-player::after {
- content: "";
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- transition: all .25s ease-out;
- background-color: rgba(0, 0, 0, .2);
- background-position: center;
- background-repeat: no-repeat;
- opacity: .85;
- background-image: url(${GM_getResourceURL("play")});
- background-size: 40px;
- }
- .x-player:hover::after {
- background-color: rgba(0, 0, 0, 0);
- }
- .x-player img {
- filter: none !important;
- }
- `;
-
- // G_DARK
- globalDark = (css = "", dark = "") => {
- if (this.G_DARK) css += dark;
- if (css) GM_addStyle(css.includes("var(--x-") ? `${this.variables}${css}` : css);
- };
- // G_SEARCH
- globalSearch = (selectors, pathname) => {
- if (!this.G_SEARCH) return;
-
- DOC.addEventListener("keydown", async e => {
- if (e.ctrlKey && e.keyCode === 191) {
- const text = (await navigator.clipboard.readText()).trim();
- if (!text) return;
-
- GM_openInTab(`${location.origin}${pathname.replace("%s", text)}`, {
- setParent: true,
- active: true,
- });
- }
- });
-
- DOC.addEventListener("keyup", e => {
- if (e.keyCode === 191 && !["INPUT", "TEXTAREA"].includes(DOC.activeElement.nodeName)) {
- DOC.querySelector(selectors).focus();
- }
- });
- };
- // G_CLICK
- globalClick = (selectors, node = DOC) => {
- node.addEventListener("click", e => {
- const { target } = e;
-
- let url = "";
- if (target.classList.contains("x-player")) {
- url = `${this.pcUrl}${target.dataset.code}`;
- } else if (this.G_CLICK) {
- const target = getTarget(e);
- if (target) url = target.href;
- }
- if (!url) return;
-
- e.preventDefault();
- e.stopPropagation();
- GM_openInTab(url, { setParent: true, active: true });
- });
-
- const getTarget = e => {
- const item = e.target.closest(selectors);
- return !item?.href || !node.contains(item) ? false : item;
- };
-
- if (!this.G_CLICK) return;
-
- let _event;
-
- node.addEventListener("mousedown", e => {
- if (e.button !== 2) return;
-
- const target = getTarget(e);
- if (!target) return;
-
- e.preventDefault();
-
- target.oncontextmenu = e => e.preventDefault();
- _event = e;
- });
-
- node.addEventListener("mouseup", e => {
- if (e.button !== 2) return;
-
- const target = getTarget(e);
- if (!target || !_event) return;
-
- e.preventDefault();
-
- const { clientX, clientY } = e;
- const { clientX: _clientX, clientY: _clientY } = _event;
- if (Math.abs(clientX - _clientX) + Math.abs(clientY - _clientY) > 5) return;
-
- GM_openInTab(target.href, { setParent: true, active: false });
- });
- };
-
- // L_MIT
- listMovieImgType = (node, condition) => {
- const img = node.querySelector("img");
- if (!this.L_MIT || !img) return;
-
- node.classList.add("x-cover");
- img.loading = "lazy";
- const { src = "" } = img;
- img.src = condition.find(({ regex }) => regex.test(src))?.replace(src);
- };
- // L_MTL, L_MTH
- listMovieTitle = () => {
- let num = parseInt(this.L_MTL ?? 0, 10);
- if (this.L_MTH && num < 1) num = 1;
-
- return `
- .x-title {
- -webkit-line-clamp: ${num <= 0 ? "unset" : num};
- ${this.L_MTH ? `height: calc(var(--x-line-h) * ${num}) !important;` : ""}
- }
- `;
- };
- // L_SCROLL
- listScroll = (container, itemSelector, path) => {
- const items = container.querySelectorAll(itemSelector);
- const msnry = new Masonry(container, {
- itemSelector,
- columnWidth: items[items.length - 2] ?? items[items.length - 1],
- fitWidth: true,
- visibleStyle: { opacity: 1 },
- hiddenStyle: { opacity: 0 },
- });
- container.classList.add("x-in");
-
- if (!this.L_SCROLL) return;
-
- let nextURL;
- const updateNextURL = (node = DOC) => {
- nextURL = node.querySelector(path)?.href;
- };
- updateNextURL();
-
- const infScroll = new InfiniteScroll(container, {
- path: () => nextURL,
- checkLastPage: path,
- outlayer: msnry,
- history: false,
- });
-
- infScroll?.on("request", async (_, fetchPromise) => {
- const { body } = await fetchPromise.then();
- if (body) updateNextURL(body);
- });
-
- const status = DOC.create("div", { id: "x-status" });
- container.insertAdjacentElement("afterend", status);
- let textContent = "加载中...";
- const noMore = "没有更多了";
- try {
- const path = infScroll.getPath() ?? "";
- if (!path) textContent = noMore;
- } catch (err) {
- textContent = noMore;
- }
- status.textContent = textContent;
-
- infScroll?.once("last", () => {
- status.textContent = noMore;
- });
-
- return infScroll;
- };
-
- // M_IMG
- movieImg = async ({ code }, start) => {
- if (!this.M_IMG) return;
-
- start && start();
- let img = Store.getDetail(code)?.img;
- if (!img) {
- img = await Apis.movieImg(code);
- if (img) Store.upDetail(code, { img });
- }
- return img;
- };
- // M_VIDEO
- movieVideo = async ({ code, studio }, start) => {
- if (!this.M_VIDEO) return;
-
- start && start();
- let video = Store.getDetail(code)?.video;
- if (!video) {
- video = await Apis.movieVideo(code, studio);
- if (video) Store.upDetail(code, { video });
- }
- return video;
- };
- // M_PLAYER
- moviePlayer = async ({ code }, start) => {
- if (!this.M_PLAYER) return;
-
- start && start();
- let player = Store.getDetail(code)?.player;
- if (!player) {
- player = await Apis.moviePlayer(code);
- if (player) Store.upDetail(code, { player });
- }
- return player;
- };
- // M_TITLE
- movieTitle = async ({ code, title }, start) => {
- if (!this.M_TITLE) return;
-
- start && start();
- let transTitle = Store.getDetail(code)?.transTitle;
- if (!transTitle) {
- transTitle = await Apis.movieTitle(title);
- if (transTitle) Store.upDetail(code, { transTitle });
- }
- return transTitle;
- };
- // M_STAR
- movieStar = async ({ code, star: hasStar }, start) => {
- if (!this.M_STAR || hasStar) return;
-
- start && start();
- let star = Store.getDetail(code)?.star;
- if (!star?.length) {
- star = await Apis.movieStar(code);
- if (star?.length) Store.upDetail(code, { star });
- }
- return star;
- };
- // M_SUB
- movieSub = (magnets, start) => {
- if (!this.M_SUB) return magnets;
-
- start && start();
- const regex = /[A-Z]+-\d+-C/;
- return magnets.map(item => {
- item.zh = item.zh && !regex.test(item.name);
- return item;
- });
- };
- // M_SORT
- movieSort = (magnets, start) => {
- if (!this.M_SORT) return magnets;
-
- start && start();
- return magnets.length <= 1
- ? magnets
- : magnets.sort((pre, next) => {
- if (pre.zh === next.zh) {
- if (pre.bytes === next.bytes) return next.date - pre.date;
- return next.bytes - pre.bytes;
- } else {
- return pre.zh > next.zh ? -1 : 1;
- }
- });
- };
- // M_MAGNET
- movieMagnet = async ({ code }, start) => {
- if (!this.M_MAGNET) return;
-
- start && start();
- let magnet = Store.getDetail(code)?.magnet;
- if (!magnet?.length) {
- magnet = unique(await Apis.movieMagnet(code), "link");
- if (magnet?.length) Store.upDetail(code, { magnet });
- }
- return magnet;
- };
-
- // D_MATCH
- driveMatch = async ({ code, res }, start) => {
- if (!this.D_MATCH) return;
-
- start && start();
- code = code.toUpperCase();
- const { prefix, regex } = codeParse(code);
-
- if (res === "list") {
- res = Store.getDetail(code)?.res;
- if (!res?.length) {
- const RESOURCE = GM_getValue("RESOURCE", []);
- let item = RESOURCE.find(item => item.prefix === prefix);
- if (!item) {
- item = { prefix, res: await Apis.searchVideo(prefix) };
- RESOURCE.push(item);
- GM_setValue("RESOURCE", RESOURCE);
- }
- res = await this.driveMatch({ ...item, code });
- }
- } else {
- let _res = res ?? (await Apis.searchVideo(prefix));
- if (_res?.length) {
- _res = _res.filter(({ n }) => regex.test(n));
- }
- if (!res) Store.upDetail(code, { res: _res });
- res = _res;
- }
-
- return res;
- };
- // D_CID
- driveCid = async () => {
- let cid = this.D_CID;
- if (/^\$\{.+\}$/.test(cid)) {
- cid = cid.replace(/\$|\{|\}/g, "").trim();
- const res = await Apis.getFile();
- cid = res.find(({ n }) => n === cid)?.cid ?? "";
- }
- return cid;
- };
- // D_VERIFY
- driveVerify = async ({ code, cid = "" }) => {
- let verify = this.D_VERIFY <= 0;
-
- for (let idx = 0; idx < this.D_VERIFY; idx++) {
- await delay(1);
-
- let res = await Apis.getVideo(cid);
- res = await this.driveMatch({ code, res });
-
- res = res.filter(({ t }) => t.startsWith(getDate()));
- if (!res.length) continue;
-
- const fids = (Store.getDetail(code)?.res ?? []).map(({ fid }) => fid);
- res = res.filter(({ fid }) => !fids.includes(fid));
- if (!res.length) continue;
-
- verify = res;
- break;
- }
-
- return verify;
- };
- // D_RENAME
- driveRename = ({ cid, res, zh, code, title }) => {
- let file_name = this.D_RENAME?.trim();
- if (!file_name) return;
-
- code = code.toUpperCase();
- file_name = file_name
- .replace(/\$\{字幕\}/g, zh ? "【中文字幕】" : "")
- .replace(/\$\{番号\}/g, code)
- .replace(/\$\{标题\}/g, title);
-
- if (!codeParse(code).regex.test(file_name)) file_name = `${code} - ${file_name}`;
-
- res = res
- .filter(item => item.ico)
- .map(item => {
- item.file_name = `${file_name}.${item.ico}`;
- return item;
- });
-
- unique(res.map(item => item.cid).filter(item => item !== cid)).forEach(fid => res.push({ fid, file_name }));
-
- return Apis.driveRename(res);
- };
-
- driveOffline = async (e, { magnets, code, title }) => {
- const { target } = e;
- const { magnet: type } = target.dataset;
- if (!type) return;
-
- e.preventDefault();
- e.stopPropagation();
-
- const { classList } = target;
- if (classList.contains("active")) return;
-
- classList.add("active");
- const originText = target.textContent;
- target.textContent = "请求中...";
-
- const wp_path_id = await this.driveCid();
-
- if (type === "all") {
- const warnMsg = { title: "一键离线任务失败", image: "warn" };
- const successMsg = { title: "一键离线任务成功", image: "success", timeout: 3000 };
-
- const magnetLen = magnets.length;
-
- for (let index = 0; index < magnetLen; index++) {
- const sign = await Apis.getSign();
- if (!sign) break;
-
- const isLast = index + 1 === magnetLen;
- const { link: url, zh } = magnets[index];
-
- let res = await Apis.addTaskUrl({ url, wp_path_id, ...sign });
- if (!res?.state) {
- if (res.errcode === 911) {
- notify({ title: "一键离线任务中断", text: res.error_msg, image: "warn", highlight: false });
- break;
- }
- if (!isLast) continue;
- notify(warnMsg);
- break;
- }
-
- const cid = wp_path_id;
-
- res = await this.driveVerify({ code, cid });
- if (!res) {
- if (!isLast) continue;
- notify(warnMsg);
- break;
- }
-
- if (res?.length) {
- successMsg.text = "点击跳转目录";
- successMsg.clickUrl = `https://115.com/?cid=${res[0].cid}&offset=0&mode=wangpan`;
- await this.driveRename({ cid, res, zh, code, title });
- }
-
- notify(successMsg);
- break;
- }
- } else if (type) {
- const url = type;
- const res = await Apis.addTaskUrl({ url, wp_path_id });
- if (res) {
- notify({
- title: `离线任务添加${res.state ? "成功" : "失败"}`,
- text: res.error_msg ?? "",
- image: res.state ? "info" : "warn",
- highlight: false,
- });
- }
- }
-
- classList.remove("active");
- target.textContent = originText;
- };
- }
-
- // javbus
- class JavBus extends Common {
- constructor() {
- super();
- return super.init();
- }
-
- excludeMenu = [];
- routes = {
- list: /^\/((uncensored|uncensored\/)?(page\/\d+)?$)|((uncensored\/)?((search|searchstar|actresses|genre|star|studio|label|series|director|member)+\/)|actresses(\/\d+)?)+/i,
- genre: /^\/(uncensored\/)?genre$/i,
- forum: /^\/forum\//i,
- movie: /^\/[\w]+(-|_)?[\d]*.*$/i,
- };
-
- // styles
- _style = `
- .ad-box {
- display: none !important;
- }
- `;
- _dmStyle = `
- .nav > li > a:hover,
- .nav > li > a:focus,
- .dropdown-menu > li > a:hover,
- .dropdown-menu > li > a:focus {
- background: var(--x-grey) !important;
- }
- .nav > li.active > a,
- .nav > .open > a,
- .nav > .open > a:hover,
- .nav > .open > a:focus,
- .dropdown-menu {
- background: var(--x-bgc) !important;
- }
- .modal-content, .alert {
- background: var(--x-sub-bgc) !important;
- }
- .btn-primary {
- background: var(--x-blue) !important;
- border-color: var(--x-blue) !important;
- }
- .btn-success {
- background: var(--x-green) !important;
- border-color: var(--x-green) !important;
- }
- .btn-warning {
- background: var(--x-orange) !important;
- border-color: var(--x-orange) !important;
- }
- .btn-danger {
- background: var(--x-red) !important;
- border-color: var(--x-red) !important;
- }
- .btn.disabled, .btn[disabled], fieldset[disabled] .btn {
- opacity: .8 !important;
- }
- `;
- boxStyle = `
- .movie-box,
- .avatar-box,
- .sample-box {
- width: var(--x-thumb-w) !important;
- border: none !important;
- margin: 10px !important;
- }
- .movie-box .photo-frame,
- .avatar-box .photo-frame,
- .sample-box .photo-frame {
- height: auto !important;
- margin: 10px !important;
- border: none !important;
- }
- .movie-box .photo-frame {
- aspect-ratio: var(--x-thumb-ratio);
- }
- .avatar-box .photo-frame {
- aspect-ratio: var(--x-avatar-ratio);
- }
- .sample-box .photo-frame {
- aspect-ratio: var(--x-sprite-ratio);
- }
- .movie-box img,
- .avatar-box img,
- .sample-box img {
- min-width: unset !important;
- min-height: unset !important;
- max-width: none !important;
- max-height: none !important;
- margin: 0 !important;
- width: 100% !important;
- height: 100% !important;
- object-fit: cover !important;
- }
- .movie-box > *,
- .avatar-box > *,
- .sample-box > * {
- background: unset !important;
- }
- .movie-box > *:nth-child(2),
- .avatar-box > *:nth-child(2),
- .sample-box > *:nth-child(2) {
- padding: 0 10px 10px !important;
- border: none !important;
- line-height: var(--x-line-h) !important;
- height: auto !important;
- }
- `;
- dmBoxStyle = `
- .movie-box,
- .avatar-box,
- .sample-box {
- background: var(--x-sub-bgc) !important;
- }
- .movie-box > *:nth-child(2),
- .avatar-box > *:nth-child(2),
- .sample-box > *:nth-child(2) {
- color: unset;
- }
- .movie-box date {
- color: var(--x-sub-ftc) !important;
- }
- `;
-
- // methods
- _globalSearch = () => {
- this.globalSearch("#search-input", "/search/%s");
- };
- _globalClick = () => {
- this.globalClick([".movie-box", ".avatar-box"]);
- };
- modifyMovieBox = (node = DOC) => {
- const items = node.querySelectorAll(".movie-box");
- for (const item of items) {
- const info = item.querySelector(".photo-info span");
- info.innerHTML = info.innerHTML.replace(/<\/?span.*>|<br>/g, "");
- const title = info.firstChild;
- if (!title) continue;
- const titleText = title.textContent.trim();
- const _title = DOC.create("div", { title: titleText, class: "x-ellipsis x-title" }, titleText);
- info.replaceChild(_title, title);
- }
- };
-
- // modules
- list = {
- docStart() {
- const style = `
- #waterfall {
- display: none;
- opacity: 0;
- }
- #waterfall .item {
- float: unset !important;
- }
- .search-header {
- padding: 0 !important;
- background: none !important;
- box-shadow: none !important;
- }
- .search-header .nav-tabs {
- display: none !important;
- }
- .alert-common {
- margin: 20px 20px 0 !important;
- }
- .alert-page {
- margin: 20px !important;
- }
- .text-center.hidden-xs {
- display: none;
- line-height: 0;
- }
- ul.pagination {
- margin-bottom: 40px;
- }
- .movie-box .x-title + div {
- height: var(--x-line-h) !important;
- margin: 4px 0;
- }
- .avatar-box .pb10 {
- padding: 0 !important;
- }
- .avatar-box .pb10:not(:last-child) {
- margin-bottom: 4px !important;
- }
- .avatar-box p {
- margin: 0 0 6px !important;
- }
- .mleft {
- display: flex !important;
- align-items: center;
- }
- .mleft .btn-xs {
- margin: 0 6px 0 0 !important;
- }
- `;
- const dmStyle = `
- .pagination > li > a {
- background-color: var(--x-sub-bgc) !important;
- color: var(--x-ftc) !important;
- }
- .pagination > li:not(.active) > a:hover {
- background-color: var(--x-grey) !important;
- }
- .nav-pills > li.active > a {
- background-color: var(--x-blue) !important;
- }
- `;
- this.globalDark(
- `${this.style}${this._style}${this.boxStyle}${this.customStyle}${style}${this.listMovieTitle()}`,
- `${this.dmStyle}${this._dmStyle}${this.dmBoxStyle}${dmStyle}`
- );
- },
- contentLoaded() {
- const nav = DOC.querySelector(".search-header .nav");
- if (nav) nav.classList.replace("nav-tabs", "nav-pills");
-
- this._globalSearch();
- this._globalClick();
-
- this.modifyLayout();
- },
- modifyLayout() {
- const waterfall = DOC.querySelector("#waterfall");
- if (!waterfall) return;
-
- const isStarDetail = /^\/(uncensored\/)?star\/\w+/i.test(location.pathname);
-
- const _waterfall = waterfall.cloneNode(true);
- _waterfall.removeAttribute("style");
- _waterfall.setAttribute("class", "x-show");
- const items = this.modifyItem(_waterfall);
-
- const itemsLen = items?.length;
- if (itemsLen) {
- _waterfall.innerHTML = "";
- for (let index = 0; index < itemsLen; index++) {
- if (isStarDetail && !index) continue;
- _waterfall.appendChild(items[index]);
- }
- }
- waterfall.parentElement.replaceChild(_waterfall, waterfall);
-
- const infScroll = this.listScroll(_waterfall, ".item", "#next");
- if (!infScroll) return DOC.querySelector(".text-center.hidden-xs")?.classList.add("x-show");
-
- infScroll?.on("request", async (_, fetchPromise) => {
- const { body } = await fetchPromise.then();
- if (!body) return;
-
- let items = this.modifyItem(body);
- if (isStarDetail) [_, ...items] = items;
- infScroll.appendItems(items);
- infScroll.options.outlayer.appended(items);
- });
- },
- modifyItem(container) {
- const items = container.querySelectorAll(".item");
- for (const item of items) {
- item.removeAttribute("style");
- item.setAttribute("class", "item");
- this._listMovieImgType(item);
- this.modifyAvatarBox(item);
- this.modifyMovieBox(item);
- }
- this._driveMatch(container);
- return items;
- },
- _listMovieImgType(node) {
- const item = node.querySelector(".movie-box");
- if (!item) return;
-
- const condition = [
- {
- regex: /\/thumb(s)?\//gi,
- replace: val => val.replace(/\/thumb(s)?\//gi, "/cover/").replace(/\.jpg/gi, "_b.jpg"),
- },
- {
- regex: /pics\.dmm\.co\.jp/gi,
- replace: val => val.replace("ps.jpg", "pl.jpg"),
- },
- ];
-
- this.listMovieImgType(item, condition);
- },
- modifyAvatarBox(node = DOC) {
- const items = node.querySelectorAll(".avatar-box");
- for (const item of items) {
- const span = item.querySelector("span");
- if (span.classList.contains("mleft")) {
- const title = span.firstChild;
- const titleText = title.textContent.trim();
- const _title = DOC.create("div", { title: titleText, class: "x-line" }, titleText);
- title.parentElement.replaceChild(_title, title);
- span.insertAdjacentElement("afterbegin", span.querySelector("button"));
- continue;
- }
- span.classList.add("x-line");
- }
- },
- async _driveMatch(node = DOC) {
- const items = node.querySelectorAll(".movie-box");
- for (const item of items) {
- const code = item.querySelector("date")?.textContent?.trim();
- if (!code) continue;
-
- const res = await this.driveMatch({ code, res: "list" });
- if (!res?.length) continue;
-
- const frame = item.querySelector(".photo-frame");
- frame.classList.add("x-player");
- frame.setAttribute("title", "点击播放");
- frame.setAttribute("data-code", res[0].pc);
- item.querySelector(".x-title").classList.add("x-matched");
- }
- },
- };
- genre = {
- docStart() {
- const style = `
- .alert-common {
- margin: 20px 0 0 !important;
- }
- .container-fluid {
- padding: 0 20px !important;
- }
- h4 {
- margin: 20px 0 10px 0 !important;
- }
- .genre-box {
- padding: 20px !important;
- margin: 10px 0 20px 0 !important;
- }
- .genre-box a {
- cursor: pointer !important;
- user-select: none !important;
- text-align: left !important;
- }
- .genre-box input {
- margin: 0 !important;
- vertical-align: middle !important;
- }
- .x-last-box {
- margin-bottom: 70px !important;
- }
- button.btn.btn-danger.btn-block.btn-genre {
- position: fixed !important;
- bottom: 0 !important;
- left: 0 !important;
- margin: 0 !important;
- border: none !important;
- border-radius: 0 !important;
- }
- `;
- const dmStyle = `
- .genre-box {
- background: var(--x-sub-bgc) !important;
- }
- `;
- this.globalDark(`${this.style}${this._style}${style}`, `${this.dmStyle}${this._dmStyle}${dmStyle}`);
- },
- contentLoaded() {
- this._globalSearch();
- if (!DOC.querySelector("button.btn.btn-danger.btn-block.btn-genre")) return;
-
- const box = DOC.querySelectorAll(".genre-box");
- box[box.length - 1].classList.add("x-last-box");
- DOC.querySelector(".container-fluid.pt10").addEventListener("click", ({ target }) => {
- if (target.nodeName !== "A" || !target.classList.contains("text-center")) return;
- const checkbox = target.querySelector("input");
- checkbox.checked = !checkbox.checked;
- });
- },
- };
- forum = {
- docStart() {
- const style = `
- .bcpic,
- .banner728,
- .banner300,
- .jav-footer {
- display: none !important;
- }
- #toptb {
- position: fixed !important;
- top: 0 !important;
- left: 0 !important;
- right: 0 !important;
- z-index: 999 !important;
- border-color: #e7e7e7;
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
- }
- #search-input {
- border-right: none !important;
- }
- .jav-button {
- margin-top: -3px !important;
- margin-left: -4px !important;
- }
- #wp {
- margin-top: 55px !important;
- }
- .biaoqicn_show a {
- width: 20px !important;
- height: 20px !important;
- line-height: 20px !important;
- opacity: .7;
- }
- #online .bm_h {
- border-bottom: none !important;
- }
- #online .bm_h .o img {
- margin-top: 48%;
- }
- #moquu_top {
- right: 20px;
- bottom: 20px;
- }
- `;
- this.globalDark(`${this.style}${style}`);
- },
- contentLoaded() {
- this._globalSearch();
- },
- };
- movie = {
- params: {},
- magnets: null,
-
- docStart() {
- const style = `
- #mag-submit-show,
- #mag-submit,
- #magnet-table,
- h4[style="position:relative"],
- h4[style="position:relative"] + .row,
- .info span.glyphicon-info-sign {
- display: none !important;
- }
- html {
- padding-right: 0 !important;
- }
- @media (min-width: 1270px) {
- .container { width: 1270px; }
- }
- .container {
- margin-bottom: 40px;
- }
- .row.movie {
- padding: 0 !important;
- }
- #magneturlpost + .movie {
- padding: 10px !important;
- margin-top: 20px !important;
- }
- .screencap, .info {
- padding: 10px !important;
- border: none !important;
- }
- .bigImage {
- position: relative;
- overflow: hidden;
- display: block;
- aspect-ratio: var(--x-cover-ratio);
- opacity: 0;
- }
- .info p {
- line-height: var(--x-line-h) !important;
- }
- .star-box img {
- width: 100% !important;
- height: auto !important;
- margin: 0 !important;
- }
- .star-box .star-name {
- padding: 6px 0 !important;
- background: unset !important;
- border: none !important;
- }
- #avatar-waterfall,
- #sample-waterfall,
- #related-waterfall {
- margin: -10px !important;
- word-spacing: -20px;
- }
- .avatar-box,
- .sample-box,
- .movie-box {
- word-spacing: 0 !important;
- vertical-align: top !important;
- }
- .movie-box > *:nth-child(2) {
- text-align: left !important;
- min-height: 32px !important;
- }
- .x-ml {
- margin-left: 10px;
- }
- .x-mr {
- margin-right: 10px;
- }
- .x-grass-img {
- object-fit: cover;
- }
- .x-grass-mask,
- .x-contain {
- position: absolute;
- width: 100% !important;
- height: 100% !important;
- top: 0;
- left: 0;
- }
- .x-grass-mask {
- backdrop-filter: blur(50px);
- }
- .x-contain {
- opacity: 0;
- object-fit: contain;
- z-index: -1;
- }
- .x-contain.x-in {
- z-index: 9 !important;
- }
- .mfp-img {
- max-height: unset !important;
- }
- .btn-group button {
- border-width: .5px !important;
- }
- .x-res * {
- color: #CC0000 !important;
- }
- .x-table {
- margin: 0 !important;
- }
- .x-caption {
- display: flex;
- align-items: center;
- }
- .x-caption .label {
- position: unset !important;
- }
- .x-table tr {
- table-layout: fixed;
- display: table;
- width: 100%;
- }
- .x-table tr > * {
- vertical-align: middle !important;
- border-left: none !important;
- }
- .x-table tr > *:first-child {
- width: 50px;
- }
- .x-table tr > *:nth-child(2) {
- width: 33.3%;
- }
- .x-table tr > *:last-child,
- .x-table tfoot tr > th:not(:nth-child(3)) {
- border-right: none !important;
- }
- .x-table tbody {
- display: block;
- max-height: calc(37px * 10 - 1px);
- overflow: overlay;
- table-layout: fixed;
- }
- .x-table tbody tr > * {
- border-top: none !important;
- }
- .x-table tbody tr:last-child > *,
- .x-table tfoot tr > * {
- border-bottom: none !important;
- }
- `;
- const dmStyle = `
- .movie,
- .btn-group button[disabled],
- .star-box-up li,
- .table-striped > tbody > tr:nth-of-type(odd) {
- background: var(--x-sub-bgc) !important;
- }
- .btn-group button.active,
- .x-offline.active {
- background: var(--x-bgc) !important;
- }
- tbody tr:hover,
- .table-striped > tbody > tr:nth-of-type(odd):hover {
- background: var(--x-grey) !important;
- }
- `;
- this.globalDark(
- `${this.style}${this._style}${this.boxStyle}${this.customStyle}${style}`,
- `${this.dmStyle}${this._dmStyle}${this.dmBoxStyle}${dmStyle}`
- );
- },
- contentLoaded() {
- this._globalSearch();
- this._globalClick();
-
- this.params = this.getParams();
-
- addCopyTarget("h3", { title: "复制标题" });
-
- this.initSwitch();
- this.updateSwitch({ key: "img", title: "预览大图" });
- this.updateSwitch({ key: "video", title: "预览视频" });
- this.updateSwitch({ key: "player", title: "在线播放", type: "link" });
-
- this._movieTitle();
- addCopyTarget("span[style='color:#CC0000;']", { title: "复制番号" });
- this._movieStar();
-
- this._driveMatch();
- DOC.querySelector(".x-offline")?.addEventListener("click", e => this._driveOffline(e));
-
- const tableObs = new MutationObserver((_, obs) => {
- obs.disconnect();
- this.refactorTable();
- });
- tableObs.observe(DOC.querySelector("#movie-loading"), { attributes: true, attributeFilter: ["style"] });
-
- this.modifyMovieBox();
- },
- getParams() {
- const info = DOC.querySelector(".info");
- const { textContent } = info;
- return {
- title: DOC.querySelector("h3").textContent,
- code: info.querySelector("span[style='color:#CC0000;']").textContent,
- date: info.querySelector("p:nth-child(2)").childNodes[1].textContent.trim(),
- studio: textContent.match(/(?<=製作商: ).+/g)?.pop(0),
- star: !/暫無出演者資訊/g.test(textContent),
- };
- },
- initSwitch() {
- const bigImage = DOC.querySelector(".bigImage");
-
- const img = bigImage.querySelector("img");
- img.classList.add("x-grass-img");
-
- bigImage.insertAdjacentHTML(
- "beforeend",
- `<div class="x-grass-mask"></div><img src="${img.src}" id="x-switch-cover" class="x-contain x-in">`
- );
- bigImage.classList.add("x-in");
-
- const info = DOC.querySelector(".info");
- info.insertAdjacentHTML(
- "afterbegin",
- `<div class="btn-group btn-group-justified mb10" hidden id="x-switch" role="group">
- <div class="btn-group btn-group-sm" role="group" title="点击放大">
- <button type="button" class="btn btn-default active" for="x-switch-cover">封面</button>
- </div>
- </div>`
- );
-
- const switcher = info.querySelector("#x-switch");
- switcher.addEventListener("click", ({ target }) => {
- const id = target.getAttribute("for");
- const { classList } = target;
-
- if (!id) return;
- if (classList.contains("active")) {
- const active = bigImage.querySelector(".x-contain.x-in");
- const { nodeName } = active;
- if (nodeName === "IMG") bigImage.click();
- if (nodeName === "VIDEO") active.muted = !active.muted;
- return;
- }
-
- const preActive = switcher.querySelector("button.active");
- preActive.classList.toggle("active");
- preActive.setAttribute("title", "点击切换");
- classList.toggle("active");
- target.removeAttribute("title");
-
- bigImage.querySelector(".x-contain.x-in").classList.toggle("x-in");
- const targetNode = bigImage.querySelector(`#${id}`);
- targetNode.classList.toggle("x-in");
-
- bigImage.querySelectorAll("video.x-contain:not(.x-in)").forEach(v => v?.pause());
-
- const { nodeName, src } = targetNode;
- if (nodeName === "VIDEO") {
- targetNode.focus();
- return targetNode.play();
- }
- bigImage.querySelector(".x-grass-img").src = src;
- bigImage.href = src;
- });
- },
- async updateSwitch({ key, title, type }) {
- const id = `x-switch-${key}`;
- if (!type) type = key;
- const switcher = DOC.querySelector("#x-switch");
-
- const start = () => {
- if (!switcher.classList.contains("x-show")) switcher.classList.add("x-show");
- switcher.insertAdjacentHTML(
- "beforeend",
- `<div class="btn-group btn-group-sm" role="group" title="查询中...">
- <button type="button" class="btn btn-default" for="${id}" disabled>${title}</button>
- </div>`
- );
- };
-
- const src = await this[`movie${key[0].toUpperCase()}${key.slice(1)}`](this.params, start);
- const node = switcher.querySelector(`button[for="${id}"]`);
- if (!node) return;
-
- const nodeParent = node.parentNode;
- if (!src) return nodeParent.setAttribute("title", "暂无资源");
-
- node.removeAttribute("disabled");
-
- if (type === "link") {
- nodeParent.removeAttribute("title");
- node.setAttribute("title", "跳转链接");
-
- node.addEventListener("click", e => {
- e.preventDefault();
- e.stopPropagation();
- GM_openInTab(src, { setParent: true, active: true });
- });
- } else {
- node.setAttribute("title", "点击切换");
- nodeParent.setAttribute("title", "点击放大");
-
- const item = DOC.create(type, { src, id, class: "x-contain" });
-
- if (type === "video") {
- nodeParent.setAttribute("title", "切换静音");
-
- item.controls = true;
- item.currentTime = 3;
- item.muted = true;
- item.preload = "metadata";
- item.addEventListener("click", e => {
- e.preventDefault();
- e.stopPropagation();
- const { target: video } = e;
- video.paused ? video.play() : video.pause();
- });
- }
-
- DOC.querySelector(".bigImage").insertAdjacentElement("beforeend", item);
- }
- },
- async _movieTitle() {
- const start = () => {
- DOC.querySelector("#x-switch").insertAdjacentHTML(
- "afterend",
- `<p><span class="header">机翻标题: </span><span class="x-transTitle">查询中...</span></p>`
- );
- };
-
- const transTitle = await this.movieTitle(this.params, start);
- const transTitleNode = DOC.querySelector(".x-transTitle");
- if (transTitleNode) transTitleNode.textContent = transTitle ?? "查询失败";
- },
- async _movieStar() {
- const start = () => {
- const starShow = DOC.querySelector("p.star-show");
- starShow.nextElementSibling.nextSibling.remove();
- starShow.insertAdjacentHTML("afterend", `<p class="x-star">查询中...</p>`);
- };
-
- const star = await this.movieStar(this.params, start);
- const starNode = DOC.querySelector(".x-star");
- if (!starNode) return;
-
- starNode.innerHTML = !star?.length
- ? "暂无演员数据"
- : star.reduce(
- (acc, cur) =>
- `${acc}<span class="genre"><label><a href="/search/${cur}">${cur}</a></label></span>`,
- ""
- );
- },
- async _driveMatch() {
- const start = () => {
- if (DOC.querySelector(".x-res")) return;
-
- GM_addStyle(`tbody a[data-magnet] { display: inline !important; }`);
- DOC.querySelector(".info").insertAdjacentHTML(
- "beforeend",
- `<p class="header">网盘资源:</p><p class="x-res">查询中...</p><button type="button" class="btn btn-default btn-sm btn-block x-offline" data-magnet="all">一键离线</button>`
- );
- };
-
- const res = await this.driveMatch(this.params, start);
- const resNode = DOC.querySelector(".x-res");
- if (!resNode) return;
-
- resNode.innerHTML = !res?.length
- ? "暂无网盘资源"
- : res.reduce(
- (acc, { pc, t, n }) =>
- `${acc}<div class="x-line"><a href="${this.pcUrl}${pc}" target="_blank" title="${t} / ${n}">${n}</a></div>`,
- ""
- );
- },
- refactorTable() {
- const table = DOC.querySelector("#magnet-table");
- table.parentElement.innerHTML = `
- <table class="table table-striped table-hover table-bordered x-table">
- <caption><div class="x-caption">重构的表格</div></caption>
- <thead>
- <tr>
- <th scope="col">#</th>
- <th scope="col">磁力名称</th>
- <th scope="col">档案大小</th>
- <th scope="col" class="text-center">分享日期</th>
- <th scope="col" class="text-center">来源</th>
- <th scope="col" class="text-center">字幕</th>
- <th scope="col">操作</th>
- </tr>
- </thead>
- <tbody>
- <tr><th scope="row" colspan="7" class="text-center text-muted">暂无数据</th></tr>
- </tbody>
- <tfoot>
- <tr>
- <th scope="row"></th>
- <th></th>
- <th colspan="4" class="text-right">总数</th>
- <td>0</td>
- </tr>
- </tfoot>
- </table>
- `;
-
- DOC.querySelector(".x-table tbody").addEventListener("click", e => {
- !handleCopyTxt(e, "复制成功") && this._driveOffline(e);
- });
-
- const magnets = [];
- for (const tr of table.querySelectorAll("tr")) {
- const [link, size, date] = tr.querySelectorAll("td");
- const _link = link?.querySelector("a");
- const _size = size?.textContent.trim();
- if (!_link || !_size || !date) continue;
-
- magnets.push({
- name: _link.textContent.trim(),
- link: _link.href.split("&")[0],
- zh: !!link.querySelector("a.btn.btn-mini-new.btn-warning.disabled"),
- size: _size,
- bytes: transToBytes(_size),
- date: date.textContent.trim(),
- });
- }
-
- this.refactorTd(magnets);
- this._movieMagnet();
- },
- async _movieMagnet() {
- const start = () => {
- DOC.querySelector(".x-caption").insertAdjacentHTML(
- "beforeend",
- `<span class="label label-success"><span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span> 磁力搜索</span><span class="label label-success"><span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span> 自动去重</span>`
- );
- };
-
- const magnets = await this.movieMagnet(this.params, start);
- if (magnets?.length) this.refactorTd(magnets);
- },
- refactorTd(magnets) {
- const table = DOC.querySelector(".x-table");
- const caption = table.querySelector(".x-caption");
-
- let subStart = () => {
- caption.insertAdjacentHTML(
- "beforeend",
- `<span class="label label-success"><span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span> 字幕筛选</span>`
- );
- };
- let sortStart = () => {
- caption.insertAdjacentHTML(
- "beforeend",
- `<span class="label label-success"><span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span> 磁力排序</span>`
- );
- };
-
- if (this.magnets) {
- subStart = null;
- sortStart = null;
- magnets = unique([...this.magnets, ...magnets], "link");
- }
-
- table.querySelector("tfoot td").textContent = magnets.length;
-
- magnets = this.movieSub(magnets, subStart);
- magnets = this.movieSort(magnets, sortStart);
- this.magnets = magnets;
-
- magnets = this.createTd(magnets);
- if (!magnets) return;
- table.querySelector("tbody").innerHTML = magnets;
-
- if (!subStart || !sortStart) return;
- const node = table.querySelector("thead th:last-child");
- node.innerHTML = `<a href="javascript:void(0);" title="复制所有磁力链接">全部复制</a>`;
- node.querySelector("a").addEventListener("click", e => {
- e.preventDefault();
- e.stopPropagation();
-
- GM_setClipboard(this.magnets.map(({ link }) => link).join("\n"));
- const { target } = e;
- target.textContent = "复制成功";
-
- const timer = setTimeout(() => {
- target.textContent = "全部复制";
- clearTimeout(timer);
- }, 300);
- });
- },
- createTd(magnets) {
- if (!magnets.length) return;
- return magnets.reduce(
- (acc, { name, link, size, date, from, href, zh }, index) => `
- ${acc}
- <tr>
- <th scope="row">${index + 1}</th>
- <th class="x-line" title="${name}">
- <a href="${link}">${name}</a>
- </th>
- <td>${size}</td>
- <td class="text-center">${date}</td>
- <td class="text-center">
- <a${href ? ` href="${href}" target="_blank" title="查看详情"` : ""}>
- <code>${from ?? Matched.domain}</code>
- </a>
- </td>
- <td class="text-center">
- <span
- class="glyphicon ${
- zh ? "glyphicon-ok-circle text-success" : "glyphicon-remove-circle text-danger"
- }"
- >
- </span>
- </td>
- <td>
- <a
- href="javascript:void(0);"
- data-copy="${link}"
- class="x-mr"
- title="复制磁力链接"
- >
- 链接复制
- </a>
- <a
- hidden
- href="javascript:void(0);"
- data-magnet="${link}"
- class="text-success"
- title="仅添加离线任务"
- >
- 添加离线
- </a>
- </td>
- </tr>
- `,
- ""
- );
- },
- async _driveOffline(e) {
- await this.driveOffline(e, { ...this.params, magnets: this.magnets });
- await delay(1);
- this._driveMatch();
- },
- };
- }
-
- // javdb
- class JavDB extends Common {
- constructor() {
- super();
- return super.init();
- }
-
- excludeMenu = ["G_DARK", "M", "D_CID", "D_VERIFY", "D_RENAME"];
- routes = {
- list: /^\/$|^\/(guess|censored|uncensored|western|fc2|anime|search|video_codes|tags|rankings|actors|series|makers|directors|publishers)/i,
- movie: /^\/v\//i,
- // user: /^\/users\//i,
- };
-
- // styles
- _style = `
- html {
- overflow: overlay;
- }
- body {
- min-height: 100%;
- }
- .app-desktop-banner,
- #footer {
- display: none !important;
- }
- .float-buttons {
- right: 8px;
- }
- `;
- _customStyle = `
- section.section {
- padding: 20px 20px 0;
- }
- #search-bar-container {
- overflow-x: hidden;
- margin: 0 0 20px !important;
- padding: 0 !important;
- }
- #search-bar-container .column {
- margin: 0 !important;
- padding: 0 !important;
- }
- #search-type,
- #video-search,
- #video-search:hover {
- border: none;
- box-shadow: none;
- }
- .notification:not(:last-child),
- .title:not(:last-child) {
- margin-bottom: 20px !important;
- }
- `;
-
- // methods
- _globalSearch = () => {
- this.globalSearch("#video-search", "/search?q=%s");
- };
- changeScrollBarColor = () => {
- if (DOC.documentElement.dataset.theme !== "dark") return;
- GM_addStyle(`
- ::-webkit-scrollbar {
- background: #0a0a0a !important;
- }
- ::-webkit-scrollbar-thumb {
- background: var(--x-grey) !important;
- }
- img {
- filter: brightness(.9) contrast(.9) !important;
- }
- `);
- };
-
- // modules
- list = {
- docStart() {
- let style = `
- .main-title {
- padding: 0 !important;
- }
- .tabs.is-boxed {
- margin: 0 !important;
- padding: 0 !important;
- }
- .index-toolbar,
- .actor-filter-toolbar {
- padding: 20px 0 0 !important;
- }
- #tags,
- .index-toolbar .button-group {
- margin: 0 !important;
- }
- .actor-filter {
- margin: 20px 0 0 !important;
- }
-
- .video-container:not(.awards) .columns,
- .section-container:not(.awards) {
- margin: 10px auto;
- display: none;
- opacity: 0;
- }
- nav.pagination {
- display: none;
- margin: 0 !important;
- border: none !important;
- padding: 20px 0 40px 0;
- }
- :root[data-theme=dark] nav.pagination {
- border: none !important;
- }
- `;
- if (/^\/(video_codes|series|makers|directors)/i.test(location.pathname)) {
- style = `${style}
- .columns.is-mobile.section-columns {
- padding: 0 !important;
- margin: 0 0 10px !important;
- }
- .columns.is-mobile.section-columns .column.section-title {
- margin: 0 !important;
- padding: 0 !important;
- }
- .columns.is-mobile.section-columns + .columns {
- margin: 0 0 20px !important;
- padding: 0 !important;
- }
- .columns.is-mobile.section-columns + .columns .column.section-addition {
- padding: 0 !important;
- margin: 0 !important;
- }
- `;
- }
- if (/^\/rankings/i.test(location.pathname)) {
- style = `${style}
- section.section .section {
- padding: 0 !important;
- }
- .title.is-4.divider-title {
- padding: 20px 0 !important;
- margin: 0 !important;
- }
- .awards.section-container {
- padding: 10px 0 !important;
- margin: 0 -10px !important;
- }
- .awards,
- .awards .videos {
- padding: 0 !important;
- }
- `;
- }
-
- const movieBoxStyle = `
- .video-container .columns .column {
- padding: 10px !important;
- margin: 0;
- min-width: unset;
- max-width: none;
- }
- .video-container .columns .column .box {
- padding: 10px;
- width: var(--x-thumb-w);
- box-shadow: var(--x-shadow) !important;
- }
- .video-container .columns .column .box:hover {
- box-shadow: var(--x-shadow) !important;
- }
- :root[data-theme=dark] .video-container .columns .column .box:hover {
- border: 1px solid #363636;
- }
- .video-container .columns .column .box .item-image {
- aspect-ratio: var(--x-thumb-ratio);
- }
- .video-container .columns .column .box .item-image:hover img {
- transform: none;
- }
- .video-container .columns .column .box .item-image img {
- min-height: unset !important;
- max-width: none !important;
- width: 100% !important;
- height: 100% !important;
- object-fit: cover !important;
- }
- .video-container .columns .column .box .uid,
- .video-container .columns .column .box .video-title2 {
- padding-top: 10px;
- font-size: 1em;
- line-height: 1.5;
- }
- .video-container .columns .column .box .video-title,
- .video-container .columns .column .box .uid2 {
- color: #4a4a4a;
- font-size: .8rem;
- }
- .video-container .columns .column .box .tags .tag {
- padding: .1rem .4rem;
- }
- `;
- const avatarBoxStyle = `
- .actor-box {
- padding: 10px !important;
- margin: 10px !important;
- width: var(--x-thumb-w) !important;
- box-shadow: var(--x-shadow) !important;
- }
- .actor-box a figure.image {
- aspect-ratio: var(--x-avatar-ratio);
- }
- .actor-box a figure span.avatar {
- width: 100% !important;
- height: 100% !important;
- }
- .actor-box a strong {
- padding-top: 10px;
- }
- `;
- const cardBoxStyle = `
- #series.section-container .x-item,
- #codes.section-container .x-item,
- #makers.section-container .x-item {
- padding: 10px;
- width: fit-content;
- }
- #series.section-container .x-item .box,
- #codes.section-container .x-item .box,
- #makers.section-container .x-item .box {
- padding: 10px;
- box-shadow: var(--x-shadow) !important;
- width: var(--x-cover-w) !important;
- }
- #series.section-container .x-item .box strong,
- #codes.section-container .x-item .box strong,
- #makers.section-container .x-item .box strong {
- display: block;
- }
- `;
- this.globalDark(
- `${this.style}${this._style}${this.customStyle}${style}${
- this._customStyle
- }${movieBoxStyle}${avatarBoxStyle}${cardBoxStyle}${this.listMovieTitle()}`
- );
- },
- contentLoaded() {
- this._globalSearch();
- this.globalClick([".video-container a", ".section-container a"]);
- this.modifyLayout();
- },
- load() {
- this.changeScrollBarColor();
- },
- getContainer(node = DOC) {
- const selectors = [".video-container:not(.awards) .columns", ".section-container:not(.awards)"];
- const container = node.querySelectorAll(selectors)[0];
- const id = container.id || container.parentElement.id;
- if (container && id) return { container, id };
- },
- modifyLayout() {
- const container = this.getContainer();
- if (!container) return;
- const { container: waterfall, id } = container;
-
- const _waterfall = waterfall.cloneNode(true);
- const items = this.modifyItem(_waterfall, id);
- if (items.length) {
- _waterfall.innerHTML = "";
- items.forEach(item => _waterfall.appendChild(item));
- }
- waterfall.parentElement.replaceChild(_waterfall, waterfall);
- if (!items.length) return _waterfall.classList.add("x-in");
-
- const infScroll = this.listScroll(_waterfall, ".x-item", ".pagination-next");
- if (!infScroll) {
- DOC.querySelector("nav.pagination").style.cssText += "display:flex;";
- return;
- }
-
- infScroll?.on("request", async (_, fetchPromise) => {
- const { body } = await fetchPromise.then();
- if (!body) return;
- const container = this.getContainer(body);
- if (!container) return;
-
- const { container: waterfall, id } = container;
- const items = this.modifyItem(waterfall, id);
- infScroll.appendItems(items);
- infScroll.options.outlayer.appended(items);
- });
- },
- modifyItem(container, type) {
- if (type === "videos") {
- container.setAttribute("class", "columns x-show");
- return this.modifyMovieBox(container);
- }
- container.classList.add("x-show");
- if (type === "actors") return this.modifyAvatarBox(container);
- if (["series", "makers", "codes"].includes(type)) return this.modifyCardBox(container);
- },
- modifyMovieBox(container) {
- const items = container.querySelectorAll(".column");
- for (const item of items) {
- item.setAttribute("class", "column x-item");
- // modify title
- const title = item.querySelectorAll([".video-title", ".uid2"])[0];
- if (title) {
- title.classList.add("x-ellipsis");
- title.classList.add("x-title");
- }
- // modify cover
- const img = item.querySelector("img");
- if (!img) continue;
- img.removeAttribute("class");
- const { src } = img.dataset;
- if (src !== img.src) img.src = src;
- // cover type
- this._listMovieImgType(item);
- }
- this._driveMatch(container);
- return items;
- },
- modifyAvatarBox(container) {
- const items = container.querySelectorAll(".box.actor-box");
- for (const item of items) {
- item.classList.add("x-item");
- }
- return items;
- },
- modifyCardBox(container) {
- const items = container.querySelectorAll(".column.is-3");
- for (const item of items) {
- item.classList.add("x-item");
- item.querySelector("strong").classList.add("x-ellipsis");
- }
- return items;
- },
-
- _listMovieImgType(node) {
- const item = node.querySelector(".box");
- if (!item) return;
-
- const condition = [{ regex: /\/thumbs\//gi, replace: val => val.replace(/\/thumbs\//gi, "/covers/") }];
- this.listMovieImgType(item, condition);
- },
- async _driveMatch(node) {
- const items = node.querySelectorAll(".column");
- for (const item of items) {
- const code = item.querySelector(".uid")?.textContent?.trim();
- if (!code) continue;
- const res = await this.driveMatch({ code, res: "list" });
- if (!res?.length) continue;
-
- const frame = item.querySelector(".item-image");
- frame.classList.add("x-player");
- frame.setAttribute("title", "点击播放");
- frame.setAttribute("data-code", res[0].pc);
- item.querySelector(".x-title").classList.add("x-matched");
- }
- },
- };
- movie = {
- docStart() {
- const style = `
- .video-meta-panel {
- padding: 0;
- margin-bottom: 20px;
- }
- .video-meta-panel > .columns {
- margin: 0;
- padding: 0;
- }
- .video-meta-panel > .columns > .column {
- padding: 10px;
- margin: 0;
- }
- `;
- this.globalDark(`${this.style}${this._style}${this._customStyle}${style}`);
- },
- contentLoaded() {
- this._globalSearch();
- this.globalClick([".message.video-panel .message-body .tile-images.tile-small a"]);
- },
- load() {
- this.changeScrollBarColor();
- },
- };
- // users = {};
- }
-
- // 115
- class Drive115 {
- contentLoaded() {
- window.focus();
- DOC.querySelector(".bottom button").addEventListener("click", () => {
- const interval = setInterval(() => {
- if (DOC.querySelector(".vcode-hint").getAttribute("style").indexOf("none") !== -1) {
- clearTimer();
- window.open("", "_self");
- window.close();
- }
- }, 300);
-
- const timeout = setTimeout(() => clearTimer(), 600);
-
- const clearTimer = () => {
- clearInterval(interval);
- clearTimeout(timeout);
- };
- });
- }
- }
-
- const Process = eval(`new ${Matched.domain}()`);
- Process.docStart && Process.docStart();
- Process.contentLoaded && DOC.addEventListener("DOMContentLoaded", () => Process.contentLoaded());
- Process.load && window.addEventListener("load", () => Process.load());
- })();