您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
暗黑模式、滚动加载、预览视频、离线下载(115会员)...
当前为
// ==UserScript== // @name JavBus工具 // @description 暗黑模式、滚动加载、预览视频、离线下载(115会员)... // @version 0.1.2 // @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/[email protected]/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" }, "已有资源:"); let genre = doc.create("span", { class: "genre" }, "查询中..."); resBox.appendChild(span); resBox.appendChild(genre); 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)) ?? {}; if (!lfItem?.resource) lfItem.resource = []; // 远程获取搜索结果 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); }); }); } })();