- // ==UserScript==
- // @name JavBus工具
- // @description 暗黑模式、滚动加载、预览视频、离线下载(115会员)...
- // @version 0.1.1
- // @icon https://z3.ax1x.com/2021/10/15/53gMFS.png
- // @include *://*.javbus.com/*
- // @include *://captchaapi.115.com/*
- // @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
- // @require https://cdn.jsdelivr.net/npm/localforage@1.10.0/dist/localforage.min.js
- // @resource fail https://z3.ax1x.com/2021/10/15/53gcex.png
- // @resource info https://z3.ax1x.com/2021/10/15/53g2TK.png
- // @resource success https://z3.ax1x.com/2021/10/15/53gqTf.png
- // @run-at document-start
- // @grant GM_xmlhttpRequest
- // @grant GM_addStyle
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_notification
- // @grant GM_setClipboard
- // @grant GM_getResourceURL
- // @grant GM_openInTab
- // @grant GM_info
- // @grant GM_registerMenuCommand
- // @connect *
- // @license MIT
- // @namespace https://greasyfork.org/users/175514
- // ==/UserScript==
-
- /**
- * TODO:
- * ✔️ 暗黑模式
- * ✔️ 点击事件
- * ✔️ 滚动加载
- * ✔️ 获取预览大图
- * ✔️ 获取预览视频
- * ✔️ 获取演员名单(如无)
- * ✔️ 离线下载(请求离线成功2秒后查询结果汇报)
- * ✔️ 一键离线(字幕>尺寸>日期)排队执行离线下载
- * ✔️ 115账号验证弹窗
- * ✔️ 查询115是否已有离线资源
- */
- (function () {
- "use strict";
-
- const SUFFIX = " !important";
-
- const dm = GM_getValue("dm") ?? matchMedia("(prefers-color-scheme: dark)").matches;
- const lm = GM_getValue("lm") ?? false;
- const ck = GM_getValue("ck") ?? true;
- const rootId = GM_getValue("rid") ?? "";
-
- let mousePoint = 0;
-
- const { host, pathname } = location;
-
- const doc = document;
- doc.create = (tag, attr = {}, child = "") => {
- if (!tag) return null;
- tag = doc.createElement(tag);
- Object.keys(attr).forEach(name => {
- tag.setAttribute(name, attr[name]);
- });
- typeof child === "string" && tag.appendChild(doc.createTextNode(child));
- typeof child === "object" && tag.appendChild(child);
- return tag;
- };
-
- const lf = localforage;
- lf.upItem = async (key, val) => {
- let item = (await lf.getItem(key)) ?? {};
- await lf.setItem(key, Object.assign(item, val));
- };
-
- // 网络请求
- const request = (url, method = "GET", data = {} | "", params = {}) => {
- if (!url) return;
- if (method === "POST") {
- params.headers = Object.assign(params.headers ?? {}, {
- "Content-Type": "application/x-www-form-urlencoded",
- });
- }
- if (typeof data === "object" && Object.keys(data).length) {
- data = Object.keys(data).reduce(
- (pre, cur, index, arr) =>
- `${pre}=${data[pre]}&${cur}=${data[cur]}${index !== arr.length - 1 ? "&" : ""}`
- );
- data = encodeURI(data);
- if (method === "GET") url = `${url}?${data}`;
- }
- return new Promise(resolve => {
- GM_xmlhttpRequest({
- url,
- method,
- data,
- timeout: 20000,
- onload: ({ responseText }) => {
- if (/<\/?[a-z][\s\S]*>/i.test(responseText)) {
- responseText = new DOMParser().parseFromString(responseText, "text/html");
- }
- if (/^{.*}$/.test(responseText)) {
- responseText = JSON.parse(responseText);
- }
- const errcode = responseText?.errcode;
- if (`${errcode}` === "911") verify();
- resolve(responseText);
- },
- ...params,
- });
- });
- };
-
- const notifiy = (title = "", text = "", icon = "info", clickUrl = "", params = {}) => {
- if (typeof title === "object") params = title;
- GM_notification({
- title,
- text: text || GM_info.script.name,
- image: GM_getResourceURL(params.icon || icon),
- highlight: true,
- timeout: 3000,
- onclick: () => {
- clickUrl = params.clickUrl || clickUrl;
- if (!clickUrl) return;
- GM_openInTab(clickUrl, { active: true });
- },
- ...params,
- });
- };
-
- // 115验证账号
- const verify = async () => {
- const time = new Date().getTime();
- let h = 667;
- let w = 375;
- let t = (window.screen.availHeight - h) / 2;
- let l = (window.screen.availWidth - w) / 2;
- window.open(
- `https://captchaapi.115.com/?ac=security_code&type=web&cb=Close911_${time}`,
- "请验证账号",
- `height=${h},width=${w},top=${t},left=${l},toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,status=no`
- );
- };
-
- const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
-
- class Common {
- docStart = () => {};
- contentLoaded = () => {};
- load = () => {};
- }
- class Waterfall extends Common {
- isHome = /^(\/(page\/\d+)?|\/uncensored(\/page\/\d+)?)+$/i.test(pathname);
- docStart = () => {
- GM_addStyle(`
- .search-header {
- padding: 0${SUFFIX};
- background: none${SUFFIX};
- box-shadow: none${SUFFIX};
- }
- .photo-frame {
- margin: 10px${SUFFIX};
- }
- .photo-frame img {
- height: 100%${SUFFIX};
- width: 100%${SUFFIX};
- object-fit: cover${SUFFIX};
- margin: 0${SUFFIX};
- }
- .photo-info {
- padding: 10px${SUFFIX};
- }
- .alert-page {
- margin: 20px${SUFFIX};
- }
- `);
- if (!lm) return;
- const itemSizer = `167px`;
- const gutterSizer = `20px`;
- GM_addStyle(`
- .pagination,
- footer {
- display: none${SUFFIX};
- }
- .page-load-status {
- display: none;
- padding-bottom: ${gutterSizer};
- text-align: center;
- }
- body {
- overflow: hidden;
- }
- .scrollBox {
- height: calc(100vh - 50px);
- overflow: hidden;
- overflow-y: scroll;
- }
- #waterfall {
- opacity: 0;
- margin: ${gutterSizer} auto 0 auto${SUFFIX};
- }
- .item-sizer,
- .item a {
- width: ${itemSizer}${SUFFIX};
- }
- .gutter-sizer {
- width: ${gutterSizer}${SUFFIX};
- }
- .item a {
- margin: 0 0 ${gutterSizer} 0${SUFFIX};
- }
- `);
- };
- contentLoaded = () => {
- const nav = doc.querySelector(".search-header .nav");
- if (nav) nav.setAttribute("class", "nav nav-pills");
- ck && this.handleClick();
- if (!lm) return;
- this.handleLoadMore();
- if (!this.isHome) this.modifyLayout();
- };
- load = () => {
- if (lm && this.isHome) this.modifyLayout();
- };
- handleClick = () => {
- const isItem = e => {
- const elem = e.target.offsetParent;
- let re = elem.nodeName.toLowerCase() === "div" && /^item/i.test(elem.className);
- if (re) re = elem.querySelector("a").href;
- return re;
- };
- doc.body.addEventListener("contextmenu", e => {
- isItem(e) && e.preventDefault();
- });
- doc.body.addEventListener("click", e => {
- const href = isItem(e);
- if (!href) return;
- e.preventDefault();
- GM_openInTab(href, { active: true });
- });
- doc.body.addEventListener("mousedown", e => {
- const href = isItem(e);
- if (!href || e.button !== 2) return;
- e.preventDefault();
- mousePoint = e.screenX + e.screenY;
- });
- doc.body.addEventListener("mouseup", e => {
- const href = isItem(e);
- const num = e.screenX + e.screenY - mousePoint;
- if (!href || e.button !== 2 || num > 5 || num < -5) return;
- e.preventDefault();
- GM_openInTab(href);
- });
- };
- handleLoadMore = () => {
- let oldWaterfall = doc.querySelector("#waterfall");
- if (!oldWaterfall) return GM_addStyle(`#waterfall { opacity: 1; }`);
- let newWaterfall = doc.querySelector("#waterfall #waterfall");
- if (newWaterfall) oldWaterfall.parentNode.replaceChild(newWaterfall, oldWaterfall);
- let itemSizer = doc.create("div", { class: "item-sizer" });
- let gutterSizer = doc.create("div", { class: "gutter-sizer" });
- let waterfall = doc.querySelector("#waterfall");
- let ref = doc.querySelector(".item");
- waterfall.insertBefore(itemSizer, ref);
- waterfall.insertBefore(gutterSizer, ref);
- doc.querySelectorAll("div.container-fluid")[1].querySelector(".row").classList.add("scrollBox");
- const status = doc.create("div", { class: "page-load-status" });
- const request = doc.create("span", { class: "loader-ellips infinite-scroll-request" }, "Loading...");
- const last = doc.create("span", { class: "infinite-scroll-last" }, "End of content");
- const error = doc.create("span", { class: "infinite-scroll-error" }, "No more pages to load");
- status.appendChild(request);
- status.appendChild(last);
- status.appendChild(error);
- doc.querySelector(".scrollBox").appendChild(status);
- };
- modifyLayout = () => {
- let waterfall = doc.querySelector("#waterfall");
- if (!waterfall) return;
- let msnry = new Masonry(waterfall, {
- itemSelector: "none",
- columnWidth: ".item-sizer",
- gutter: ".gutter-sizer",
- horizontalOrder: true,
- fitWidth: true,
- stagger: 30,
- visibleStyle: { transform: "translateY(0)", opacity: 1 },
- hiddenStyle: { transform: "translateY(120px)", opacity: 0 },
- });
- imagesLoaded(waterfall, () => {
- msnry.options.itemSelector = ".item";
- let elems = waterfall.querySelectorAll(".item");
- this.modifyItem();
- msnry.appended(elems);
- GM_addStyle(`#waterfall { opacity: 1; }`);
- });
- // 搜索页滚动加载需特殊处理
- const path = !/^\/(uncensored\/)?(search|searchstar)+\//i.test(pathname)
- ? "#next"
- : function () {
- const items = ["search", "searchstar"];
- for (const item of items) {
- if (pathname.indexOf(`${item}/`) < 0) continue;
- let [prefix, suffix] = pathname.split("&");
- suffix ??= "";
- prefix = prefix.split("/");
- let pre = "";
- for (let index = 0; index <= prefix.indexOf(item) + 1; index++) {
- pre = `${pre}${prefix[index]}/`;
- }
- return `${pre}${this.loadCount + 2}&${suffix}`;
- }
- };
- let infScroll = new InfiniteScroll(waterfall, {
- path,
- append: ".item",
- outlayer: msnry,
- elementScroll: ".scrollBox",
- history: false,
- historyTitle: false,
- hideNav: ".pagination",
- status: ".page-load-status",
- debug: false,
- });
- infScroll.on("load", this.modifyItem);
- };
- modifyItem = (node = doc, path) => {
- const infos = node.querySelectorAll(".item a .photo-info span:not(.mleft)");
- for (const info of infos) {
- const [titleNode, secondaryNode] = info.childNodes;
- const titleTxt = titleNode.nodeValue.trim();
- const ellipsis = doc.create("div", { class: "ellipsis", title: titleTxt }, titleTxt);
- if (secondaryNode && secondaryNode.nodeName === "BR") {
- info.removeChild(secondaryNode);
- ellipsis.classList.add("line-4");
- }
- titleNode.parentNode.replaceChild(ellipsis, titleNode);
- }
- };
- }
- class Details extends Common {
- codeKey = "";
- code = "";
- lfItem = {};
- config = async codeKey => {
- if (!codeKey) return;
- this.codeKey = codeKey;
- this.code = codeKey.split("/")[0];
- this.lfItem = (await lf.getItem(codeKey)) ?? {};
- };
- docStart = () => {
- GM_addStyle(`
- .info .glyphicon-info-sign,
- h4[style="position:relative"],
- h4[style="position:relative"] + .row {
- display: none${SUFFIX};
- }
- .info ul {
- margin: 0${SUFFIX};
- }
- .screencap {
- max-height: 600px;
- overflow: hidden;
- }
- #avatar-waterfall,
- #sample-waterfall,
- #related-waterfall {
- margin: -5px${SUFFIX};
- }
- .screencap {
- max-height: 600px;
- overflow: hidden;
- }
- .photo-info {
- height: auto${SUFFIX};
- }
- `);
- GM_addStyle(`
- #previewVideo { position: relative; }
- #previewVideo:after {
- background: url(https://javdb.com/packs/media/images/btn-play-b414746c.svg) 50% no-repeat;
- background-color: rgba(0,0,0,.2);
- background-size: 40px 40px;
- bottom: 0;
- content: "";
- display: block;
- left: 0;
- position: absolute;
- right: 0;
- top: 0;
- height: 100%;
- }
- `);
- GM_addStyle(`
- #mask {
- position: fixed;
- width: 100%;
- height: 100%;
- z-index: 9999;
- left: 0;
- top: 0;
- background: rgba(11,11,11,.8);
- display: flex;
- justify-content: center;
- align-items: center;
- display: none;
- }
- `);
- GM_addStyle(`
- #exp {
- display: none;
- }
- #expBtn {
- position: absolute;
- top: 0;
- height: 40px;
- line-height: 40px;
- left: 0;
- right: 0;
- cursor: pointer;
- background: rgba(0,0,0,.7);
- color: #fff;
- margin: 560px 15px 0 15px;
- z-index: 99;
- }
- #expBtn::after {
- content: "展开";
- }
- #exp:checked + .screencap {
- max-height: none;
- }
- #exp:checked + .screencap > #expBtn {
- top: auto;
- margin: 0 15px;
- bottom: 0;
- }
- #exp:checked + .screencap > #expBtn::after {
- content: "收起";
- }
- @media screen and (max-width: 480px) {
- #btnGrp {
- margin-bottom: 15px;
- }
- .screencap {
- max-height: 280px;
- }
- #expBtn {
- margin-left: 0;
- margin-right: 0;
- }
- }
- #resBox a {
- color: #CC0000${SUFFIX};
- }
- `);
- doc.addEventListener("DOMNodeInserted", e => {
- let node = e.target;
- if (node.nodeName.toLowerCase() !== "tr") return;
- let href = node.querySelector("td a")?.href;
- if (!href) return;
- let td = doc.create("td", { style: "text-align:center;white-space:nowrap" });
- let copy = doc.create("a", { href, title: href }, "复制");
- let offline = doc.create("a", { href, style: "margin-left:16px" }, "离线下载");
- copy.addEventListener("click", this.copyTxt);
- offline.addEventListener("click", this.offLine);
- td.appendChild(copy);
- td.appendChild(offline);
- node.appendChild(td);
- });
- };
- contentLoaded = async () => {
- ck && this.handleClick();
- // copy
- const handleCopy = tag => {
- const node = doc.querySelector(tag);
- if (!node) return;
- const href = node.innerText;
- const copy = doc.create("a", { title: href, href, style: "margin-left:16px" }, "复制");
- copy.addEventListener("click", this.copyTxt);
- node.appendChild(copy);
- };
- handleCopy("h3");
- handleCopy("span[style='color:#CC0000;']");
- // info
- const info = doc.querySelector(".col-md-3.info");
- // expBtn
- const movie = doc.querySelector(".row.movie");
- const screencap = doc.querySelector(".col-md-9.screencap");
- const exp = doc.create("input", { type: "checkbox", id: "exp" });
- const expBtn = doc.create("label", { for: "exp", id: "expBtn" });
- movie.insertBefore(exp, screencap);
- screencap.appendChild(expBtn);
- // resource
- let resBox = doc.create("p", { id: "resBox" });
- let span = doc.create("span", { class: "header" }, "已有资源:");
- resBox.appendChild(span);
- info.appendChild(resBox);
- // btnGrp
- let btnGrp = doc.create("div", {
- id: "btnGrp",
- class: "btn-group btn-group-justified",
- role: "group",
- "aria-label": "options",
- });
- const btns = { smartRes: "一键离线", refreshRes: "资源刷新" };
- Object.keys(btns).forEach(id => {
- let btn = doc.create("a", { id, class: "btn btn-default" }, btns[id]);
- btn.addEventListener("click", () => this.handleOpt(id));
- btnGrp.appendChild(btn);
- });
- info.appendChild(btnGrp);
- // table
- doc.querySelector("#magnet-table tbody tr").appendChild(
- doc.create("td", { style: "text-align:center;white-space:nowrap" }, "操作")
- );
- // photoinfo
- for (const item of doc.querySelectorAll(".photo-info")) {
- item.querySelector("span").classList.add("ellipsis");
- }
- // config
- if (!this.codeKey) {
- let [code, time] = doc.querySelectorAll("div.col-md-3.info p");
- code = code.querySelectorAll("span")[1].childNodes[0].nodeValue.trim().toUpperCase();
- time = time.childNodes[1].nodeValue.trim().toUpperCase();
- await this.config(`${code}/${time}`);
- }
- this.handleResource();
- this.handleVideo();
- this.handlePreview();
- this.handleStar();
- };
- handleClick = () => {
- const links = doc.querySelectorAll(".movie-box");
- for (const link of links) {
- const { href } = link;
- link.addEventListener("contextmenu", e => e.preventDefault());
- link.addEventListener("click", e => {
- e.preventDefault();
- GM_openInTab(href, { active: true });
- });
- link.addEventListener("mousedown", e => {
- if (e.button !== 2) return;
- e.preventDefault();
- mousePoint = e.screenX + e.screenY;
- });
- link.addEventListener("mouseup", e => {
- const num = e.screenX + e.screenY - mousePoint;
- if (e.button !== 2 || num > 5 || num < -5) return;
- e.preventDefault();
- GM_openInTab(href);
- });
- }
- };
- load = () => {
- const maxHei = 600;
- let styleHei = doc.querySelector(".col-md-9.screencap").querySelector(".bigImage img").height;
- if (styleHei > maxHei) {
- GM_addStyle(`
- .screencap { max-height: ${maxHei}px; }
- #expBtn { margin-top: ${maxHei - 40}px; }
- `);
- } else {
- GM_addStyle(`
- .screencap { max-height: ${styleHei + 40}px; }
- #expBtn { margin-top: ${styleHei}px; }
- `);
- }
- };
- copyTxt = e => {
- e.preventDefault();
- e.stopPropagation();
- let node = e.target;
- let { title } = node;
- if (!title) return;
- GM_setClipboard(title);
- const text = node.innerText;
- node.innerText = "成功";
- setTimeout(() => {
- node.innerText = text;
- }, 1000);
- };
- // 预览大图
- handlePreview = async () => {
- let image = this.lfItem?.image;
- if (!image) {
- let res = await request(`https://javstore.net/search/${this.code}.html`);
- const link = res?.querySelector("#content_news li a")?.href;
- if (link) res = await request(link);
- image = res?.querySelector(".news a img[alt*='.th']")?.src?.replace(".th", "");
- if (!image) return;
- lf.upItem(this.codeKey, { image });
- }
- const img = doc.create("img", { src: image, title: "点击收起", style: "cursor:pointer" });
- img.addEventListener("click", () => {
- const checkbox = doc.querySelector("#exp");
- checkbox.checked = !checkbox.checked;
- });
- const append = () => doc.querySelector(".col-md-9.screencap").appendChild(img);
- if (img.complete) return append();
- img.onload = append;
- };
- // 预览视频
- handleVideo = async () => {
- let video = this.lfItem?.video;
- if (!video) {
- const res = await request(`https://www.r18.com/common/search/searchword=${this.code}/`);
- video = res?.querySelector("a.js-view-sample")?.getAttribute("data-video-high");
- if (!video) return;
- lf.upItem(this.codeKey, { video });
- }
- const title = "视频预览";
- // 打开视频弹窗
- const playVideo = e => {
- e.preventDefault();
- e.stopPropagation();
- doc.body.setAttribute("style", "overflow: hidden;");
- doc.querySelector("#mask").setAttribute("style", "display: flex;");
- const video = doc.querySelector("video");
- video.play();
- video.focus();
- doc.onkeydown = event => {
- const e = event || window.event;
- if (e && e.keyCode == 27) pauseVideo();
- };
- };
- // 关闭视频弹窗
- const pauseVideo = () => {
- doc.body.setAttribute("style", "overflow: auto;");
- doc.querySelector("#mask").setAttribute("style", "display: none;");
- doc.querySelector("video").pause();
- doc.onkeydown = null;
- };
- // 视频播放窗口
- const videoNode = doc.create("video", { controls: "controls", src: video });
- videoNode.preload = "auto";
- videoNode.muted = true;
- const closeBtn = doc.create("button", { title: "Close (Esc)", type: "button", class: "mfp-close" }, "×");
- closeBtn.addEventListener("click", pauseVideo);
- const mask = doc.create("div", { id: "mask" });
- mask.appendChild(closeBtn);
- mask.appendChild(videoNode);
- doc.body.appendChild(mask);
- // 封面图点击播放
- const bImg = doc.querySelector(".bigImage img");
- bImg.setAttribute("title", title);
- bImg.addEventListener("click", playVideo);
- // ”样品图像“添加播放项目
- const thumb = doc.querySelector(".bigImage img").src;
- const box = doc.create("a", {
- class: "sample-box",
- id: "previewVideo",
- href: thumb,
- title,
- });
- box.addEventListener("click", playVideo);
- const frame = doc.create("div", { class: "photo-frame" });
- const img = doc.create("img", { src: thumb, title });
- frame.appendChild(img);
- box.appendChild(frame);
- let waterfall = doc.querySelector("#sample-waterfall");
- if (!waterfall) {
- const h4 = doc.create("h4", {}, "樣品圖像");
- waterfall = doc.create("div", { id: "sample-waterfall" });
- const ref = doc.querySelector(".clearfix");
- ref.parentNode.insertBefore(waterfall, ref);
- waterfall.parentNode.insertBefore(h4, waterfall);
- }
- const ref = waterfall.querySelector("a");
- ref ? waterfall.insertBefore(box, ref) : waterfall.appendChild(box);
- };
- // 演员列表
- handleStar = async () => {
- const nodes = doc.querySelector(".col-md-3.info").childNodes;
- let starNode = "";
- for (const node of nodes) {
- if (node.nodeType === 3 && node.nodeValue.trim() === "暫無出演者資訊") {
- starNode = node;
- break;
- }
- }
- if (!starNode) return;
- let star = this.lfItem?.star ?? [];
- if (!star.length) {
- const site = "https://javdb.com";
- let res = await request(`${site}/search?q=${this.code}`);
- const href = res.querySelector("#videos .grid-item a").getAttribute("href");
- if (href) res = await request(`${site}${href}`);
- let panels = res
- ?.querySelector(".video-meta-panel")
- ?.querySelectorAll(".column")[1]
- ?.querySelectorAll(".panel-block");
- if (!panels) return;
- panels = panels[panels.length - 3]?.querySelector(".value")?.querySelectorAll("a") || [];
- for (const panel of panels) {
- const starName = panel.innerHTML.trim();
- if (starName) star.push(starName);
- }
- if (!star.length) return;
- lf.upItem(this.codeKey, { star });
- }
- const p = doc.create("p");
- star.map(item => {
- const span = doc.create("span", { class: "genre" });
- const a = doc.create("a", { href: `https://www.javbus.com/search/${item}` }, item);
- span.appendChild(a);
- p.appendChild(span);
- });
- starNode.parentNode.replaceChild(p, starNode);
- };
- // 已有资源(本地)
- handleResource = async () => {
- const lfItem = await lf.getItem(this.codeKey);
- let resource = lfItem?.resource;
- let upDate = lfItem?.upDate;
- const bool = !upDate || Math.floor((new Date().getTime() - upDate) / 24 / 3600 / 1000) > 3;
- if (bool) resource = await this.fetchResource();
- let resBox = doc.querySelector("#resBox");
- let olds = resBox.querySelectorAll(".genre");
- for (const old of olds) resBox.removeChild(old);
- if (!resource?.length) {
- let genre = doc.create("span", { class: "genre" }, "无");
- return resBox.appendChild(genre);
- }
- resource.map(({ link, getDate, name }) => {
- let genre = doc.create("span", { class: "genre" });
- let thunbName = name.replace(/\.\w+$/gi, "");
- thunbName = thunbName.length > 20 ? `${thunbName.substr(0, 20)}...` : thunbName;
- let a = doc.create("a", { href: link, title: `${getDate}/${name}`, target: "_blank" }, thunbName);
- genre.appendChild(a);
- resBox.appendChild(genre);
- });
- };
- // 已有资源(115)
- fetchResource = async () => {
- const code = this.code;
- let codes = [
- code,
- code.replace(/-/g, ""),
- code.replace(/-/g, "-0"),
- code.replace(/-/g, "0"),
- code.replace(/-/g, "00"),
- code.replace(/-/g, "_"),
- code.replace(/-/g, "_0"),
- code.replace(/-0/g, ""),
- code.replace(/-0/g, "-"),
- code.replace(/-0/g, "00"),
- ];
- let { data } = await request("https://webapi.115.com/files/search", "GET", {
- search_value: encodeURIComponent(codes.join(" ")),
- format: "json",
- });
- let resource = [];
- if (data?.length) {
- const reg = new RegExp(`(${codes.join("|")})`, "gi");
- data = data.filter(({ n, play_long }) => n.match(reg) && play_long > 0);
- resource = data.map(item => {
- return {
- fid: item.fid,
- cid: item.cid,
- dir: `https://115.com/?cid=${item.cid}&offset=0&mode=wangpan`,
- link: `https://v.anxia.com/?pickcode=${item.pc}`,
- getDate: item.t,
- name: item.n,
- };
- });
- }
- await lf.upItem(this.codeKey, { upDate: new Date().getTime(), resource });
- return resource;
- };
- // 离线下载
- offLine = async e => {
- e.preventDefault();
- e.stopPropagation();
- let node = e.target;
- let { href } = node;
- if (!href) return;
- GM_setClipboard(href);
- const text = node.innerText;
- node.innerText = "请求中...";
- let zh = !!node.parentNode.parentNode
- .querySelector("td")
- .querySelector("a.btn.btn-mini-new.btn-warning.disabled");
- const obj = await this.offLineDownload({ link: href, zh });
- node.innerText = text;
- notifiy(obj);
- };
- // 排队离线/资源刷新
- handleOpt = async action => {
- const node = doc.querySelector(`#${action}`);
- node.classList.toggle("disabled");
- const text = node.innerText;
- node.innerText = "请求中...";
-
- if (action === "refreshRes") {
- await lf.upItem(this.codeKey, { upDate: 0 });
- await this.handleResource();
- }
- if (action === "smartRes") {
- const trs = doc.querySelector("#magnet-table").querySelectorAll("tr");
- let magnetArr = [];
- for (let index = 1; index < trs.length; index++) {
- let item = { zh: false, size: 0, date: 0 };
- const elem = trs[index];
- let [zh, size, date] = elem.querySelectorAll("td");
- for (const a of zh.querySelectorAll("a")) {
- item.zh = a.innerText.trim() === "字幕";
- if (item.zh) break;
- }
- size = size.querySelector("a").innerText.trim().replace(/gb/gi, "");
- if (/mb/gi.test(size)) size = (parseInt(size, 10) / 1024).toFixed(2);
- item.size = Number(size);
- date = date.querySelector("a");
- item.date = date.innerText.trim().replace(/-/g, "");
- item.link = date.getAttribute("href");
- magnetArr.push(item);
- }
- magnetArr.sort((pre, next) => {
- if (pre.zh === next.zh) {
- if (pre.size === next.size) return next.date - pre.date;
- return next.size - pre.size;
- } else {
- return pre.zh > next.zh ? -1 : 1;
- }
- });
- for (let index = 0; index < magnetArr.length; index++) {
- const obj = await this.offLineDownload(magnetArr[index]);
- if (obj?.icon !== "info") {
- notifiy(obj);
- break;
- }
- if (index !== magnetArr.length - 1) continue;
- notifiy(
- "一键离线失败",
- "远程未查找到新增资源,接口失效或资源被审核",
- "fail",
- "http://115.com/?tab=offline&mode=wangpan"
- );
- }
- }
-
- node.innerText = text;
- node.classList.toggle("disabled");
- };
- // 请求离线&结果查询
- offLineDownload = async ({ link, zh }) => {
- let fname = doc.querySelector("h3").childNodes[0].nodeValue;
- if (zh) fname = `【中文字幕】${fname}`;
- let notifiyObj = {
- title: "操作失败,115未登录",
- text: "请登录115账户后再离线下载",
- icon: "fail",
- clickUrl: "http://115.com/?mode=login",
- };
- let res = await request("http://115.com/", "GET", {
- ct: "offline",
- ac: "space",
- _: new Date().getTime(),
- });
- if (!res?.sign) return notifiyObj;
- const { sign, time } = res;
- res = await request(
- "http://115.com/web/lixian/?ct=lixian&ac=add_task_url",
- "POST",
- `url=${encodeURIComponent(link.substr(0, 60))}&uid=0&sign=${sign}&time=${time}`
- );
- let { state, errcode, error_msg } = res;
- notifiyObj = {
- title: "离线失败",
- text: error_msg,
- icon: "info",
- clickUrl: "http://115.com/?tab=offline&mode=wangpan",
- };
- if (`${errcode}` === "911") {
- notifiyObj.title += ",账号异常";
- notifiyObj.text = "验证后正常使用";
- notifiyObj.icon = "fail";
- }
- if (!state) return notifiyObj;
-
- // 获取旧的本地缓存数据
- let lfItem = await lf.getItem(this.codeKey);
- // 远程获取搜索结果
- await delay(2000);
- let resource = await this.fetchResource();
- this.handleResource();
- // 当前日期
- let date = new Date();
- const Y = date.getFullYear();
- const M = date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1;
- const D = date.getDate() < 10 ? `0${date.getDate()}` : date.getDate();
- date = `${Y}-${M}-${D}`;
-
- const newRes = resource.filter(item => item.getDate === date);
- // 搜索结果资源数至少比本地缓存多且获取日期为当前日期
- if (resource.length <= lfItem.resource.length || newRes.length < 1) {
- notifiyObj.text = "未找到新增资源,接口失效或资源审核";
- return notifiyObj;
- }
- this.afterAction(
- newRes.filter(item => item.name.indexOf(fname) === -1),
- fname
- );
- return {
- title: "离线成功",
- text: "点击跳转",
- icon: "success",
- clickUrl: `https://115.com/?cid=${rootId}&offset=0&mode=wangpan`,
- };
- };
- // 离线后重命名,移动,移除原目录等
- afterAction = async (items, fname) => {
- // 重命名
- for (const { fid } of items) {
- request(
- "http://webapi.115.com/files/edit",
- "POST",
- `fid=${fid}&file_name=${encodeURIComponent(fname)}`
- );
- }
- if (rootId) {
- const params = arr => arr.reduce((acc, cur, idx, src) => `${acc}&fid[${idx}]=${cur}`, "");
- const fids = Array.from(new Set(items.map(item => item.fid)));
- const cids = Array.from(new Set(items.filter(item => item !== rootId).map(item => item.cid)));
- // 移动
- const move_proid = `${new Date()}_${~(100 * Math.random())}_0`;
- await request(
- "https://webapi.115.com/files/move",
- "POST",
- `pid=${rootId}&move_proid=${move_proid}${params(fids)}`
- );
- // 删除
- request("https://webapi.115.com/rb/delete", "POST", `pid=${rootId}&ignore_warn=1${params(cids)}`);
- }
- lf.upItem(this.codeKey, { upDate: 0 });
- await delay(1000);
- this.handleResource();
- };
- }
-
- class JavBusScript {
- waterfall = new Waterfall();
- genre = {
- docStart: () => {
- GM_addStyle(`
- footer { display: none${SUFFIX}; }
- button.btn.btn-danger.btn-block.btn-genre {
- position: fixed${SUFFIX};
- bottom: 0${SUFFIX};
- margin: 0${SUFFIX};
- left: 0${SUFFIX};
- border: 0${SUFFIX};
- border-radius: 0${SUFFIX};
- }
- `);
- },
- contentLoaded: () => {
- if (!doc.querySelector("button.btn.btn-danger.btn-block.btn-genre")) return;
- let box = doc.querySelectorAll(".genre-box");
- box[box.length - 1].setAttribute("style", "margin-bottom: 65px;");
- },
- };
- forum = {
- docStart: function () {
- GM_addStyle(`
- .bcpic, .banner728 {
- display: none${SUFFIX};
- }
- .jav-button {
- margin-top: -3px${SUFFIX};
- }
- `);
- },
- };
- details = new Details();
- }
-
- const darkMode = path => {
- if (path === "forum") return;
- const Background = "rgb(18,18,18)";
- const SecondaryBackground = "rgb(32,32,32)";
- const LabelColor = "rgba(255,255,255,.95)";
- const SecondaryLabelColor = "rgb(170,170,170)";
- const Grey = "rgb(49,49,49)";
- const Blue = "rgb(10,132,255)";
- const Orange = "rgb(255,159,10)";
- const Green = "rgb(48,209,88)";
- const Pink = "rgb(255,55,95)";
- const Red = "rgb(255,69,58)";
- const Yellow = "rgb(255,214,10)";
- GM_addStyle(`
- ::-webkit-scrollbar {
- width: 16px;
- }
- ::-webkit-scrollbar-thumb {
- border-radius: 8px;
- border: 4px solid transparent;
- background-clip: content-box;
- background-color: ${Grey};
- }
- *:not(span) {
- border-color: ${Grey}${SUFFIX};
- text-shadow: none${SUFFIX};
- }
- body, footer {
- background: ${Background}${SUFFIX};
- color: ${SecondaryLabelColor}${SUFFIX};
- }
- img {
- filter: brightness(90%)${SUFFIX};
- }
- nav {
- background: ${SecondaryBackground}${SUFFIX};
- }
- input {
- background: ${Background}${SUFFIX};
- }
- *::placeholder {
- color: ${SecondaryLabelColor}${SUFFIX};
- }
- button, input, a, h1, h2, h3, h4, h5, h6 {
- color: ${LabelColor}${SUFFIX};
- box-shadow: none${SUFFIX};
- outline: none${SUFFIX};
- }
- .btn.disabled, .btn[disabled], fieldset[disabled] .btn {
- opacity: .85${SUFFIX};
- }
- button, .btn-default, .input-group-addon {
- background: ${Grey}${SUFFIX};
- color: ${LabelColor}${SUFFIX};
- }
- .btn-primary {
- background: ${Blue}${SUFFIX};
- border-color: ${Blue}${SUFFIX};
- }
- .btn-success {
- background: ${Green}${SUFFIX};
- border-color: ${Green}${SUFFIX};
- }
- .btn-danger {
- background: ${Red}${SUFFIX};
- border-color: ${Red}${SUFFIX};
- }
- .btn-warning {
- background: ${Orange}${SUFFIX};
- border-color: ${Orange}${SUFFIX};
- }
- .navbar-nav>.active>a, .navbar-nav>.active>a:focus, .navbar-nav>.active>a:hover, .navbar-nav>.open>a, .dropdown-menu {
- background: ${Background}${SUFFIX};
- }
- .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover {
- background: ${Grey}${SUFFIX};
- }
- .pagination .active a {
- border: none${SUFFIX};
- color: ${LabelColor}${SUFFIX};
- }
- .pagination>li>a, .pagination>li>span {
- background-color: ${Grey}${SUFFIX};
- border: none${SUFFIX};
- color: ${LabelColor}${SUFFIX};
- }
- tr, .modal-content, .alert {
- background: ${SecondaryBackground}${SUFFIX};
- box-shadow: none${SUFFIX};
- }
- tr:hover {
- background: ${Grey}${SUFFIX};
- }
- `);
- if (path === "waterfall") {
- GM_addStyle(`
- .item a {
- background: ${SecondaryBackground}${SUFFIX};
- }
- .photo-info {
- background: ${SecondaryBackground}${SUFFIX};
- color: ${LabelColor}${SUFFIX};
- }
- date {
- color: ${SecondaryLabelColor}${SUFFIX};
- }
- .nav-pills>li.active>a, .nav-pills>li.active>a:focus, .nav-pills>li.active>a:hover, .nav-pills>li>a:focus, .nav-pills>li>a:hover {
- background-color: ${Grey}${SUFFIX};
- }
- `);
- }
- if (path === "genre") {
- GM_addStyle(`
- .genre-box {
- background-color: ${SecondaryBackground}${SUFFIX};
- }
- `);
- }
- if (path === "details") {
- GM_addStyle(`
- .movie, .sample-box, .movie-box, .photo-info {
- background: ${SecondaryBackground}${SUFFIX};
- }
- .photo-info {
- color: ${LabelColor}${SUFFIX};
- }
- .avatar-box, .avatar-box span, .info ul li, .info .star-name {
- background: ${SecondaryBackground}${SUFFIX};
- border-color: ${Grey}${SUFFIX};
- color: ${LabelColor}${SUFFIX};
- }
- `);
- }
- };
-
- if (/javbus\.com/g.test(host)) {
- const menus = [
- { title: "点击事件", name: "ck", val: ck, key: "c" },
- { title: "黑暗模式", name: "dm", val: dm, key: "d" },
- { title: "滚动加载", name: "lm", val: lm, key: "s" },
- ];
- for (const { title, name, val, key } of menus) {
- GM_registerMenuCommand(
- `${val ? "关闭" : "开启"}${title}`,
- () => {
- GM_setValue(name, !val);
- location.reload();
- },
- key
- );
- }
- GM_registerMenuCommand(
- "离线后操作根目录cid",
- () => {
- const rid = prompt("用于离线后移动,删除等操作", rootId);
- GM_setValue("rid", rid);
- location.reload();
- },
- "a"
- );
-
- lf.config({ name: "JBDB", storeName: "AVS" });
-
- GM_addStyle(`
- .ad-box {
- display: none${SUFFIX};
- }
- .ellipsis {
- overflow : hidden;
- text-overflow: ellipsis;
- display: -webkit-box;
- -webkit-line-clamp: 1;
- -webkit-box-orient: vertical;
- }
- .line-4 {
- -webkit-line-clamp: 4;
- }
- `);
-
- const pathReg = {
- waterfall:
- /^\/((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,
- details: /^\/[\w]+(-|_)?[\d]*$/i,
- };
- const path = Object.keys(pathReg).filter(key => pathReg[key].test(pathname))[0];
- if (!path) return;
- dm && darkMode(path);
- let jav = new JavBusScript();
- jav = jav[path];
- if (!jav) return;
- const { docStart, contentLoaded, load } = jav;
-
- docStart && jav.docStart();
- contentLoaded && doc.addEventListener("DOMContentLoaded", jav.contentLoaded);
- load && window.addEventListener("load", jav.load);
- }
-
- if (/captchaapi\.115\.com/g.test(host)) {
- doc.addEventListener("DOMContentLoaded", () => {
- window.focus();
- const btn = doc.querySelector("#js_ver_code_box button[rel='verify']");
- btn.addEventListener("click", () => {
- const interval = setInterval(() => {
- if (doc.querySelector("div[rel='error_box']").getAttribute("style").indexOf("none") !== -1) {
- window.open("", "_self");
- window.close();
- }
- }, 300);
- setTimeout(() => clearInterval(interval), 600);
- });
- });
- }
- })();