您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
暗黑模式、滚动加载、115资源匹配 & 离线下载、预览视频、预览大图...
当前为
// ==UserScript== // @name JavScript // @namespace https://greasyfork.org/users/175514 // @description 暗黑模式、滚动加载、115资源匹配 & 离线下载、预览视频、预览大图... // @version 1.1.1 // @icon https://z3.ax1x.com/2021/10/15/53gMFS.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 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_registerMenuCommand // @grant GM_getResourceURL // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @grant GM_notification // @grant GM_openInTab // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_info // @connect * // @license MIT // ==/UserScript== (function () { // domains const createReg = items => new RegExp(`(${items.join("|").replace(/\./g, "\\.")})+`, "gi"); const MatchDomains = [ { domain: "JavBus", regex: createReg([ "javbus.com", "dmmbus.fun", "dmmsee.fun", "busjav.fun", "busjav.bar", "cdnbus.fun", "busfan.fun", "seedmm.fun", "busdmm.fun", "buscdn.fun", ]), }, ]; // document 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; }; // request const request = (url, data = {}, method = "GET", params = {}) => { if (typeof data === "object") { data = Object.keys(data).reduce( (pre, cur) => `${pre ? `${pre}&` : ""}${cur}=${encodeURIComponent(data[cur])}`, "" ); } if (method === "GET" && data) url = `${url}?${data}`; return new Promise(resolve => { GM_xmlhttpRequest({ url, data, method, timeout: 20000, onload: ({ status, response }) => { if (status === 404) resolve(false); const htmlReg = /<\/?[a-z][\s\S]*>/i; const jsonReg = /^{.*}$/; if (htmlReg.test(response)) response = new DOMParser().parseFromString(response, "text/html"); if (jsonReg.test(response)) response = JSON.parse(response); if (response?.errcode === 911) verify(); resolve(response); }, ...params, }); }); }; 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 notify = msg => { GM_notification({ ...msg, text: msg?.text ?? GM_info.script.name, image: GM_getResourceURL(msg?.image ?? "info"), onclick: msg?.clickUrl ? () => GM_openInTab(msg.clickUrl, { active: true }) : () => {}, highlight: true, timeout: 3000, }); }; const getDate = timestamp => { const date = timestamp ? new Date(timestamp) : 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(); return `${Y}-${M}-${D}`; }; const getScrollTop = () => { let scrollTop = 0; if (DOC.documentElement && DOC.documentElement.scrollTop) { scrollTop = DOC.documentElement.scrollTop; } else if (DOC.body) { scrollTop = document.body.scrollTop; } return scrollTop; }; const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); const getItem = key => { const details = GM_getValue("DETAILS") ?? {}; return details[key] ?? {}; }; const upItem = (key, val) => { const details = GM_getValue("DETAILS") ?? {}; val = { ...getItem(key), ...val }; details[key] = val; GM_setValue("DETAILS", details); }; class Common { docStart = () => {}; contentLoaded = () => {}; load = () => {}; fetchVideoByStudio = [ { 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", transform: code => code.replace(/HEYZO\-/gi, ""), match: "https://www.heyzo.com/contents/3000/%s/heyzo_hd_%s_sample.mp4", }, ]; mPoint = 0; pcPreview = "https://v.anxia.com/?pickcode="; menus = [ { title: "点击事件", key: "CK", command: "c" }, { title: "暗黑模式", key: "DM", command: "d" }, { title: "滚动加载", key: "LM", command: "s" }, { title: "离线后操作目录cid", key: "CID", command: "a", cb: uniKey => { const val = prompt("用于离线下载后移动,删除操作", GM_getValue(uniKey) ?? ""); if (!val) return; GM_setValue(uniKey, val); location.reload(); }, }, ]; registerMenu = (menus = this.menus) => { for (const menu of menus) { const { title, key, command } = menu; const uniKey = `${this.constructor.name}_${key}`; const val = GM_getValue(`${uniKey}`) ?? ""; GM_registerMenuCommand( `${menu?.cb ? "设置" : val ? "关闭" : "开启"}${title}`, menu?.cb ? () => menu.cb(uniKey) : () => { GM_setValue(uniKey, !val); location.reload(); }, command ); this[key] = val; } }; initDB = () => { const date = getDate(); const rcdKey = `CD`; if (GM_getValue(rcdKey) !== date) { GM_setValue("DETAILS", {}); GM_setValue("RESOURCE", []); GM_setValue(rcdKey, date); } }; customStyle = () => { GM_addStyle(` video, img { vertical-align: middle !important; } .ellipsis { overflow : hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; } .line-4 { -webkit-line-clamp: 4; } .playBtn { position: relative; } .playBtn:after { position: absolute; 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; content: ""; top: 0; right: 0; bottom: 0; left: 0; transition: all .3s ease-out; opacity: .85; } .playBtn:hover::after { background-color: rgba(0,0,0,0); } .playBtn img { filter: none !important; } .mask { position: fixed; width: 100%; height: 100%; z-index: 9999; left: 0; top: 0; background: rgba(11, 11, 11, .8); justify-content: center; align-items: center; display: none; } .matched { color: rgb(10,132,255) !important; } `); }; darkStyle = () => { 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 Red = "rgb(255,69,58)"; GM_addStyle(` ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-thumb { border-radius: 4px; background-color: ${Grey}; } *:not(span) { border-color: ${Grey} !important; outline: ${Grey} !important; text-shadow: none !important; } body, footer { background: ${Background} !important; color: ${SecondaryLabelColor} !important; } img { filter: brightness(90%) !important; } nav { background: ${SecondaryBackground} !important; } input { background: ${Background} !important; } *::placeholder { color: ${SecondaryLabelColor} !important; } button, input, a, h1, h2, h3, h4, h5, h6, p { color: ${LabelColor} !important; box-shadow: none !important; outline: none !important; } `); return { Background, SecondaryBackground, LabelColor, SecondaryLabelColor, Grey, Blue, Orange, Green, Red, }; }; handleClick = (selectors, node = DOC) => { for (const item of node.querySelectorAll(selectors)) { const href = item?.href; if (!href) continue; item.addEventListener("contextmenu", e => e.preventDefault()); item.addEventListener("click", e => { e.preventDefault(); GM_openInTab(href, { active: true }); }); item.addEventListener("mousedown", e => { if (e.button !== 2) return; e.preventDefault(); this.mPoint = e.screenX + e.screenY; }); item.addEventListener("mouseup", e => { const num = e.screenX + e.screenY - this.mPoint; if (e.button !== 2 || num > 5 || num < -5) return; e.preventDefault(); GM_openInTab(href); }); } }; waterfallLayout = (selectors, itemSelectors, mParams = {}, iParams = {}) => { const node = DOC.querySelector(selectors); if (!node) return; const msnry = new Masonry(node, { 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 }, ...mParams, }); imagesLoaded(node, () => { msnry.options.itemSelector = itemSelectors; const items = node.querySelectorAll(itemSelectors); msnry.appended(items); GM_addStyle(`${selectors} { opacity: 1; }`); }); const infScroll = new InfiniteScroll(node, { path: "#next", append: itemSelectors, outlayer: msnry, elementScroll: ".scrollBox", history: false, historyTitle: false, hideNav: ".pagination", status: ".page-load-status", debug: false, ...iParams, }); return infScroll; }; copyTxt = e => { e.preventDefault(); e.stopPropagation(); const { target: node } = e; if (!node) return; const { copy } = node.dataset; if (!copy) return; GM_setClipboard(copy); const initial = node?.textContent ?? ""; node.textContent = "成功"; setTimeout(() => { node.textContent = initial; }, 600); }; fetchMatch = async code => { let res = getItem(code)?.resource; if (!res) { const resource = GM_getValue("RESOURCE") ?? []; const prefix = code.split(/(-|_)/g)[0]; let item = resource.find(item => item.prefix === prefix); if (!item) { item = { prefix, res: await this.fetchSearch(prefix) }; resource.push(item); GM_setValue("RESOURCE", resource); } res = await this.fetchResource(code, item.res); } return res; }; fetchSearch = async search_value => { const res = await request( "https://webapi.115.com/files/search", { search_value, offset: 0, limit: 10000, date: "", aid: 1, cid: 0, pick_code: "", type: 4, source: "", format: "json", o: "user_ptime", asc: 0, star: "", suffix: "", }, "GET", { responseType: "json" } ); return (res?.data ?? []).map(({ fid, cid, n, pc, t, te, tp, play_long }) => { return { fid, cid, n, pc, t, te, tp, play_long }; }); }; fetchResource = async (code = "", res) => { if (!res) res = await this.fetchSearch(code.split(/(-|_)/g)[0]); if (res?.length) { let codes = [ code, code.replace(/-/g, ""), code.replace(/-/g, "."), code.replace(/-0/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"), ]; codes = Array.from(new Set(codes)); const reg = createReg(codes); res = res .filter(({ n, play_long }) => n.match(reg) && play_long > 0) .map(({ n: name, pc: pickCode, fid, cid, te, tp, t: date }) => { return { name, pickCode, fid, cid, timestamp: Math.max(te, tp), date }; }); } return res; }; fetchStar = async code => { const site = "https://javdb.com"; let res = await request(`${site}/search?q=${code}`); const href = res?.querySelector("#videos .grid-item a").getAttribute("href"); if (!href) return; res = await request(`${site}${href}`); res = res?.querySelectorAll(".panel-block"); if (!res?.length) return; res = res[res.length - 3]?.querySelector(".value").textContent.trim(); return res .split(/\n/) .filter(item => item.indexOf("♀") !== -1) .map(item => item.replace("♀", "").trim()); }; fetchImage = async (code, date) => { date = date.split("-"); const jpBukkake = `http://img.japanese-bukkake.net/${date[0]}/${date[1]}/${code}_s.jpg`; const javScreens = `http://javscreens.com/images/${code}.jpg`; const [jb, js] = await Promise.all([request(jpBukkake), request(javScreens)]); if (typeof jb === "object") return jpBukkake; if (typeof js === "object") return javScreens; let res = await request(`https://javstore.net/search/${code}.html`); const href = res?.querySelector("#content_news li a")?.href; if (!href) return; res = await request(href); res = res?.querySelector(".news a img[alt*='.th']").src.replace(".th", ""); if (!res || !(await request(res))) return; return res; }; fetchVideo = async ({ code, studio }) => { code = code.toLowerCase(); if (studio) { const matched = this.fetchVideoByStudio.find(({ name }) => name === studio); if (matched) return matched.match.replace(/%s/g, matched.transform ? matched.transform(code) : code); } const [r18, xrmoo] = await Promise.all([ request(`https://www.r18.com/common/search/searchword=${code}/`), request(`http://dmm.xrmoo.com/sindex.php?searchstr=${code}`), ]); return ( r18?.querySelector("a.js-view-sample")?.getAttribute("data-video-high") || xrmoo ?.querySelector(".card .card-footer a.viewVideo") ?.getAttribute("data-link") .replace("_sm_w", "_dmb_w") || "" ); }; fetchSign = async () => { const res = await request("http://115.com/", { ct: "offline", ac: "space", _: new Date().getTime() }); if (res?.sign) return { sign: res.sign, time: res.time }; notify({ title: "请求失败,115未登录", text: "请登录115账户后再离线下载", image: "fail", clickUrl: "http://115.com/?mode=login", }); }; fetchOffLine = async url => { let res = await this.fetchSign(); if (!res) return; return await request( "http://115.com/web/lixian/?ct=lixian&ac=add_task_url", { url, uid: 0, ...res }, "POST", { headers: { "Content-Type": "application/x-www-form-urlencoded" } } ); }; fetchRename = async res => { const data = {}; for (const { fid, file_name } of res) data[`files_new_name[${fid}]`] = file_name; return await request("https://webapi.115.com/files/batch_rename", data, "POST", { headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }, responseType: "json", }); }; fetchMove = async res => { const data = { pid: this.CID, move_proid: `${new Date()}_${~(100 * Math.random())}_0` }; for (let index = 0; index < res.length; index++) data[`fid[${index}]`] = res[index].fid; return await request("https://webapi.115.com/files/move", data, "POST", { headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }, responseType: "json", }); }; fetchDelDir = async res => { const data = { pid: this.CID, ignore_warn: 1 }; res = Array.from(new Set(res.map(({ cid }) => cid).filter(item => item !== this.CID))); for (let index = 0; index < res.length; index++) data[`fid[${index}]`] = res[index]; return await request("https://webapi.115.com/rb/delete", data, "POST", { headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }, responseType: "json", }); }; } class JavBus extends Common { constructor() { super(); this.start(); const tag = Object.keys(this.routeReg).find(key => this.routeReg[key].test(location.pathname)); return { ...this, ...this[tag] }; } maxHei = 600; routeReg = { 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, }; start = () => { this.registerMenu(); this.initDB(); this.customStyle(); this.customMode(); DOC.addEventListener("DOMContentLoaded", () => { DOC.addEventListener("keyup", event => { const e = event || window.event; if (e && e.keyCode === 191 && !["INPUT", "TEXTAREA"].includes(DOC.activeElement.nodeName)) { DOC.querySelector("#search-input").focus(); } }); }); }; customMode = () => { GM_addStyle(`.ad-box { display: none !important; }`); }; darkMode = dStyle => { const { Grey, LabelColor, Blue, Green, Red, Orange, Background, SecondaryBackground } = dStyle; GM_addStyle(` .btn.disabled, .btn[disabled], fieldset[disabled] .btn { opacity: .85 !important; } button, .btn-default, .input-group-addon { background: ${Grey} !important; color: ${LabelColor} !important; } .btn-primary { background: ${Blue} !important; border-color: ${Blue} !important; } .btn-success { background: ${Green} !important; border-color: ${Green} !important; } .btn-danger { background: ${Red} !important; border-color: ${Red} !important; } .btn-warning { background: ${Orange} !important; border-color: ${Orange} !important; } .navbar-nav>.active>a, .navbar-nav>.active>a:focus, .navbar-nav>.active>a:hover, .navbar-nav>.open>a, .dropdown-menu { background: ${Background} !important; } .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover { background: ${Grey} !important; } .pagination .active a { border: none !important; color: ${LabelColor} !important; } .pagination>li>a, .pagination>li>span { background-color: ${Grey} !important; border: none !important; color: ${LabelColor} !important; } tr, .modal-content, .alert { background: ${SecondaryBackground} !important; box-shadow: none !important; } tr:hover { background: ${Grey} !important; } `); }; waterfall = { docStart() { GM_addStyle(` .search-header { padding: 0 !important; background: none !important; box-shadow: none !important; } .photo-frame { position: relative; margin: 10px !important; } .photo-frame img { height: 100% !important; width: 100% !important; object-fit: cover !important; margin: 0 !important; } .photo-info { padding: 10px !important; } .alert-page { margin: 20px !important; } `); if (this.LM) { const itemSizer = `167px`; const gutterSizer = `20px`; GM_addStyle(` .pagination, footer { display: none !important; } .page-load-status { display: none; padding-bottom: ${gutterSizer}; text-align: center; } body { overflow: hidden; } .scrollBox { height: calc(100vh - 50px); overflow: hidden scroll; } #waterfall { opacity: 0; margin: ${gutterSizer} auto 0 auto !important; } .item-sizer, .item a { width: ${itemSizer} !important; } .gutter-sizer { width: ${gutterSizer} !important; } .item a { margin: 0 0 ${gutterSizer} 0 !important; } `); } if (this.DM) { const dStyle = this.darkStyle(); this.darkMode(dStyle); const { SecondaryBackground, LabelColor, SecondaryLabelColor, Grey } = dStyle; GM_addStyle(` .item a { background: ${SecondaryBackground} !important; } .photo-info { background: ${SecondaryBackground} !important; color: ${LabelColor} !important; } date { color: ${SecondaryLabelColor} !important; } .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} !important; } `); } }, contentLoaded() { const nav = DOC.querySelector(".search-header .nav"); if (nav) nav.setAttribute("class", "nav nav-pills"); this.modifyItem(); if (!this.LM) return; this.modifyLayout(); this.handleWaterfall(); }, async modifyItem(node = DOC) { if (this.CK) this.handleClick(".item a", node); const items = node.querySelectorAll(".item"); for (const item of items) { const info = item.querySelector("a .photo-info span:not(.mleft)"); if (!info) continue; const [titleNode, secondaryNode] = info.childNodes; const titleTxt = titleNode.textContent.trim(); const title = DOC.create("div", { class: "title", title: titleTxt }, titleTxt); if (this.LM) title.classList.add("ellipsis"); if (secondaryNode?.nodeName === "BR") { info.removeChild(secondaryNode); title.classList.add("line-4"); } info.replaceChild(title, titleNode); } for (const item of items) { let code = item.querySelector("date"); if (!code) continue; code = code.textContent.trim().toUpperCase(); const resource = await this.fetchMatch(code); if (!resource?.length) continue; item.querySelector(".title").classList.add("matched"); const photo = item.querySelector(".photo-frame"); photo.classList.add("playBtn"); photo.setAttribute("title", "点击播放"); photo.addEventListener("click", e => { e.stopPropagation(); e.preventDefault(); GM_openInTab(`${this.pcPreview}${resource[0].pickCode}`, { active: true }); }); } }, modifyLayout() { const oldWaterfall = DOC.querySelector("#waterfall"); if (!oldWaterfall) return; const newWaterfall = DOC.querySelector("#waterfall #waterfall"); if (newWaterfall) oldWaterfall.parentNode.replaceChild(newWaterfall, oldWaterfall); const waterfall = DOC.querySelector("#waterfall"); waterfall.insertAdjacentHTML( "afterbegin", `<div class="item-sizer"></div><div class="gutter-sizer"></div>` ); waterfall.insertAdjacentHTML( "afterend", `<div class="page-load-status"><span class="loader-ellips infinite-scroll-request">Loading...</span><span class="infinite-scroll-last">End of content</span><span class="infinite-scroll-error">No more pages to load</span></div>` ); DOC.querySelector(".container-fluid .row")?.classList.add("scrollBox"); }, handleWaterfall() { const infScroll = this.waterfallLayout( "#waterfall", ".item", {}, { path: !/^\/(uncensored\/)?(search|searchstar)+\//i.test(location.pathname) ? "#next" : function () { const { pathname } = location; const items = ["search", "searchstar"]; for (const item of items) { if (pathname.indexOf(`${item}/`) < 0) continue; let [prefix, suffix] = pathname.split("&"); suffix = 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}`; } }, } ); infScroll.on("load", e => this.modifyItem(e)); }, }; genre = { docStart() { GM_addStyle(` footer { display: none !important; } button.btn.btn-danger.btn-block.btn-genre { position: fixed !important; bottom: 0 !important; margin: 0 !important; left: 0 !important; border: 0 !important; border-radius: 0 !important; } `); if (this.DM) { const dStyle = this.darkStyle(); this.darkMode(dStyle); const { SecondaryBackground } = dStyle; GM_addStyle(`.genre-box { background-color: ${SecondaryBackground} !important; }`); } }, contentLoaded() { if (!DOC.querySelector("button.btn.btn-danger.btn-block.btn-genre")) return; const box = DOC.querySelectorAll(".genre-box"); box[box.length - 1].setAttribute("style", "margin-bottom:65px"); }, }; forum = { docStart() { GM_addStyle(` .bcpic, .banner728, .sd.sd_allbox > div:last-child { display: none !important; } .jav-button { margin-top: -3px !important; } #toptb { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; z-index: 999 !important; } #wp { margin-top: 55px !important; } `); }, }; details = { docStart() { GM_addStyle(` .info .glyphicon-info-sign, h4[style="position:relative"], h4[style="position:relative"] + .row { display: none !important; } .info ul { margin: 0 !important; } .screencap { max-height: 600px; overflow: hidden; } #avatar-waterfall, #sample-waterfall, #related-waterfall { margin: -5px !important; } .photo-info { height: auto !important; } .bigImage { display: block; } `); // insert dom 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: "收起"; } #resBox a { color: #CC0000 !important; } #smartOff { width: 100%; } `); if (this.DM) { const dStyle = this.darkStyle(); this.darkMode(dStyle); const { SecondaryBackground, LabelColor, Grey } = dStyle; GM_addStyle(` .movie, .sample-box, .movie-box, .photo-info { background: ${SecondaryBackground} !important; } .photo-info { color: ${LabelColor} !important; } .avatar-box, .avatar-box span, .info ul li, .info .star-name { background: ${SecondaryBackground} !important; border-color: ${Grey} !important; color: ${LabelColor} !important; } `); } DOC.addEventListener("DOMNodeInserted", ({ target: node }) => { if (node?.nodeName.toLowerCase() !== "tr") return; const href = node.querySelector("td a")?.href; if (!href) return; const td = DOC.create("td", { style: "text-align:center;white-space:nowrap" }); const copy = DOC.create( "a", { href, "data-copy": href, title: "复制磁力链接", style: "margin-right:16px" }, "复制" ); const offline = DOC.create("a", { href, title: "仅添加离线任务" }, "离线下载"); copy.addEventListener("click", this.copyTxt); offline.addEventListener("click", e => this.handleOffLine(e, href)); td.appendChild(copy); td.appendChild(offline); node.appendChild(td); }); }, contentLoaded() { if (this.CK) { this.handleClick("a.movie-box"); this.handleClick("a.avatar-box"); } // insert copy const handleCopy = selectors => { const node = DOC.querySelector(selectors); if (!node) return; const copy = node?.textContent.trim(); if (!copy) return; const copyNode = DOC.create( "a", { title: copy, "data-copy": copy, href: "", style: "margin-left:16px" }, "复制" ); copyNode.addEventListener("click", this.copyTxt); node.appendChild(copyNode); }; handleCopy("h3"); handleCopy("span[style='color:#CC0000;']"); // expBtn const screencap = DOC.querySelector(".screencap"); screencap.querySelector(".bigImage img").onload = () => this.load(); screencap.insertAdjacentHTML("beforebegin", `<input type="checkbox" id="exp">`); screencap.insertAdjacentHTML("beforeend", `<label for="exp" id="expBtn"></label>`); const info = DOC.querySelector(".col-md-3.info"); // resource info.insertAdjacentHTML( "beforeend", `<p id="resBox"><span class="header">已有资源:</span><span class="genre">查询中...</span></p>` ); // smart offLine const smartRes = DOC.create("button", { class: "btn btn-default", id: "smartOff" }, "一键离线"); smartRes.addEventListener("click", e => this.handleOffLine(e, "smart")); info.insertAdjacentElement("beforeend", smartRes); // table DOC.querySelector("#magnet-table tbody tr").insertAdjacentHTML( "beforeend", `<td style="text-align:center;white-space:nowrap">操作</td>` ); // ellipsis for (const item of DOC.querySelectorAll(".photo-info span")) item.classList.add("ellipsis"); // handleFetch const params = this.getParams(); if (!params) return; this.handleResource(params); this.handleStar(params); this.handleImage(params); this.handleVideo(params); }, load() { const maxHei = this.maxHei; const img = DOC.querySelector(".screencap").children[0]; const styleHei = img.height || img.clientHeight || img.offsetHeight; 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; }` ); } }, getParams() { // regex const charReg = /[\u4e00-\u9fa5:]/g; const studioReg = /製作商:/g; const starReg = /暫無出演者資訊/g; // dom const info = DOC.querySelector(".info"); const infos = info.querySelectorAll("p"); // params let [code, date] = infos; if (!code || !date) return; code = code.textContent.replace(charReg, "").trim().toUpperCase(); date = date.textContent.replace(charReg, "").trim().toUpperCase(); let studio = ""; for (const { textContent: text } of infos) { if (!studioReg.test(text)) continue; studio = text.replace(studioReg, "").trim(); break; } return { code, date, studio, star: !starReg.test(info.textContent), title: DOC.querySelector("h3").textContent.replace("复制", "").trim(), res: getItem(code), }; }, async handleResource({ code }) { // console.time("handleResource"); const resource = await this.fetchResource(code); upItem(code, { resource }); const resBox = DOC.querySelector("#resBox"); for (const old of resBox.querySelectorAll(".genre")) resBox.removeChild(old); if (!resource?.length) return resBox.insertAdjacentHTML("beforeend", `<span class="genre">无</span>`); resBox.insertAdjacentHTML( "beforeend", resource.reduce( (acc, { name, pickCode, date }) => `${acc} <span class="genre"> <a href="${this.pcPreview}${pickCode}" title="${date}/${name}" target="_blank"> ${name.length > 20 ? `${name.substr(0, 20)}...` : name} </a> </span>`, "" ) ); // console.timeEnd("handleResource"); return resource; }, async handleStar({ code, star, res }) { // console.time("handleStar"); if (star) return; star = res?.star ?? []; if (!star?.length) { star = await this.fetchStar(code); if (!star?.length) return; upItem(code, { star }); } const p = DOC.create("p"); p.insertAdjacentHTML( "beforeend", star.reduce( (acc, cur) => `${acc}<span class="genre"><a href="/search/${cur}">${cur}</a></span>`, "" ) ); DOC.querySelector(".info").replaceChild( p, DOC.querySelector("span.glyphicon.glyphicon-info-sign.mb20")?.nextSibling ); // console.timeEnd("handleStar"); }, async handleImage({ code, res, date }) { // console.time("handleImage"); let image = res?.image ?? ""; if (!image) { image = await this.fetchImage(code, date); if (!image) return; upItem(code, { image }); } const img = DOC.create("img", { src: image, title: "点击收起", style: "cursor:pointer" }); img.addEventListener("click", () => { if (getScrollTop() >= this.maxHei) window.scrollTo(0, 0); DOC.querySelector("#exp").checked = false; }); img.onload = () => DOC.querySelector(".screencap").appendChild(img); // console.timeEnd("handleImage"); }, async handleVideo({ code, studio, res }) { // console.time("handleVideo"); let video = res?.video ?? ""; if (!video) { video = await this.fetchVideo({ code, studio }); if (!video) return; upItem(code, { video }); } const title = "预览视频"; const playVideo = e => { e.preventDefault(); e.stopPropagation(); DOC.querySelector("#video-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.querySelector("#video-mask").setAttribute("style", "display:none"); DOC.querySelector("video").pause(); DOC.onkeydown = null; }; const videoNode = DOC.create("video", { controls: "controls", src: video, width: 720 }); videoNode.currentTime = 3; 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: "video-mask", class: "mask" }); mask.appendChild(closeBtn); mask.appendChild(videoNode); DOC.body.appendChild(mask); const bigImage = DOC.querySelector(".bigImage"); const bImg = bigImage.querySelector("img"); const playBtn = DOC.create("div", { class: "playBtn", title }); playBtn.addEventListener("click", playVideo); playBtn.appendChild(bImg); bigImage.appendChild(playBtn); const thumb = bImg.src; const box = DOC.create("a", { class: "sample-box", href: thumb, title }); box.addEventListener("click", playVideo); box.insertAdjacentHTML("beforeend", `<div class="photo-frame playBtn"><img src="${thumb}"></div>`); let waterfall = DOC.querySelector("#sample-waterfall"); if (!waterfall) { DOC.querySelector("div.clearfix").insertAdjacentHTML( "beforebegin", `<div id="sample-waterfall"></div>` ); waterfall = DOC.querySelector("#sample-waterfall"); waterfall.insertAdjacentHTML("beforebegin", `<h4>樣品圖像</h4>`); return waterfall.appendChild(box); } const ref = waterfall.querySelector("a"); waterfall.insertBefore(box, ref); const imgBtn = DOC.create( "button", { title: "樣品圖像", type: "button", class: "mfp-close", style: "right:44px" }, "📷" ); imgBtn.addEventListener("click", () => { pauseVideo(); ref.click(); }); mask.appendChild(imgBtn); // console.timeEnd("handleVideo"); }, async handleOffLine(e, link) { e.preventDefault(); e.stopPropagation(); if (!link) return; const node = e.target; const text = node?.textContent ?? ""; node.textContent = "请求中..."; node.setAttribute("disabled", true); node.setAttribute("style", "pointer-events:none"); const clickUrl = "http://115.com/?tab=offline&mode=wangpan"; if (link !== "smart") { let res = await this.fetchOffLine(link); const { state = false, error_msg = "" } = res; notify({ title: `请求${state ? "成功" : "失败"}`, text: `${error_msg ? error_msg : "仅添加离线任务"}`, image: `${state ? "info" : "fail"}`, clickUrl, }); } else { const params = this.getParams(); let links = []; const trs = DOC.querySelectorAll("#magnet-table tr"); for (let index = 1; index < trs.length; index++) { const item = { link: "", zh: false, size: 0, date: 0 }; let [name, size, date] = trs[index].querySelectorAll("td"); if (!name || !size || !date) continue; item.zh = !!name.querySelector("a.btn.btn-mini-new.btn-warning.disabled"); name = name.querySelector("a"); item.link = name.href; size = size.querySelector("a").textContent.trim().replace(/gb/gi, ""); if (/mb/gi.test(size)) size = (parseFloat(size) / 1024).toFixed(2); item.size = Number(size); item.date = date.querySelector("a").textContent.trim().replace(/-/g, ""); links.push(item); } if (links.length > 1) { links.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 < links.length; index++) { const item = links[index]; let res = await this.fetchOffLine(item.link); if (!res || res?.errcode === 911) break; if (res?.state) { res = await this.checkResource(params); if (res) { this.afterAction({ zh: item.zh, ...params, res }); notify({ title: "任务成功", text: "进行后续操作", image: "success", clickUrl, }); break; } } if (index !== links.length - 1) continue; notify({ title: "任务失败", image: "fail", clickUrl }); } } node.textContent = text; node.removeAttribute("disabled"); node.removeAttribute("style"); }, async checkResource({ code }) { const old = getItem(code)?.resource ?? []; await delay(2500); const res = await this.handleResource({ code }); if (Array.isArray(res) && res?.length) { const today = res.filter(({ date }) => date === getDate()); if (today.length && res.length > old.length) return today; } return false; }, async afterAction({ zh, res, title, code }) { res = res.filter(({ name }) => name.indexOf(title) === -1); if (!res.length) return; title = `${zh ? "【中文字幕】" : ""}${title}`; res = res.map(item => { const suffix = item.name.split(".").pop().toLowerCase(); return { ...item, file_name: `${title}.${suffix}` }; }); await this.fetchRename(res); if (this.CID) { const moveRes = await this.fetchMove(res); if (moveRes?.state) this.fetchDelDir(res); } this.handleResource({ code }); }, }; } let matched = MatchDomains.find(({ regex }) => regex.test(location.host))?.domain; if (matched) matched = eval(`new ${matched}();`); matched?.docStart(); DOC.addEventListener("DOMContentLoaded", () => matched?.contentLoaded()); window.addEventListener("load", () => matched?.load()); if (/captchaapi\.115\.com/g.test(location.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); }); }); } })();