// ==UserScript== // @name JHS-enhance // @namespace JHS-enhance // @version 3.3.14 // @author xiebro,fireinrain // @description Jav-鉴黄师 增强脚本。列表页:作品状态标签、一键筛选、新作品检测、演员黑名单过滤;详情页:磁力链接高亮、DMM 多画质预览视频、标题翻译、整合字幕搜索(迅雷+SubTitleCat)并支持 115 直传、多源预览图(javfree/projectjav/javstore)含来源切换;数据:115 网盘目录匹配与多目录选择、云盘备份/恢复、跨 Tab 同步;其他:自动翻页、分类折叠、Top250、以图识图、热门榜单、评论查看、相关清单。支持 JavDB / JavBus / JavSee / SeeJav / FC2 / JavTrailers // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=javdb.com // @include https://javdb*.com/* // @include https://*javbus*/* // @include https://*javsee*/* // @include https://*seejav*/* // @include https://javtrailers.com/* // @include https://subtitlecat.com/* // @include https://www.aliyundrive.com/* // @include https://www.alipan.com/* // @include https://115.com/* // @match https://javdb.com/* // @match https://www.javbus.com/* // @exclude https://*javbus*/forum/* // @exclude https://*javbus*/*actresses // @exclude https://*javsee*/forum/* // @exclude https://*javsee*/*actresses // @exclude https://*seejav*/forum/* // @exclude https://*seejav*/*actresses // @require https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/tabulator-tables@6.5.2/dist/js/tabulator.min.js // @require https://cdn.jsdelivr.net/npm/layui-layer@1.0.9/dist/layer.min.js // @require https://cdn.jsdelivr.net/npm/blueimp-md5@2.19.0/js/md5.min.js // @require https://cdn.jsdelivr.net/npm/toastify-js@1.12.0/src/toastify.min.js // @require https://cdn.jsdelivr.net/npm/localforage@1.10.0/dist/localforage.min.js // @require https://cdn.jsdelivr.net/npm/viewerjs@1.11.1/dist/viewer.min.js // @require https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js // @require https://cdn.jsdelivr.net/npm/systemjs@6.15.1/dist/system.min.js // @require https://cdn.jsdelivr.net/npm/systemjs@6.15.1/dist/extras/named-register.min.js // @require data:application/javascript,%3B(typeof%20System!%3D'undefined')%26%26(System%3Dnew%20System.constructor())%3B // @connect xunlei.com // @connect geilijiasu.com // @connect aliyundrive.com // @connect aliyundrive.net // @connect ja.wikipedia.org // @connect beta.magnet.pics // @connect jdforrepam.com // @connect cc3001.dmm.co.jp // @connect cc3001.dmm.com // @connect www.dmm.co.jp // @connect www.dmm.com // @connect api.dmm.com // @connect special.dmm.co.jp // @connect adult.contents.fc2.com // @connect fc2ppvdb.com // @connect 123av.com // @connect u3c3.com // @connect u9a9.com // @connect btsow.lol // @connect sukebei.nyaa.si // @connect javstore.net // @connect javbest.net // @connect missav.live // @connect jable.tv // @connect www.av.gl // @connect jav.rs // @connect javtrailers.com // @connect javdb.com // @connect javbus.com // @connect supjav.com // @connect 115.com // @connect jianguoyun.com // @connect 127.0.0.1 // @connect * // @grant GM_addStyle // @grant GM_info // @grant GM_openInTab // @grant GM_xmlhttpRequest // @grant unsafeWindow // @grant window.close // @run-at document-idle // ==/UserScript== (a => { if (typeof GM_addStyle == "function") { GM_addStyle(a); return } const A = document.createElement("style"); A.textContent = a, document.head.append(A) })(" html #layuicss-skinlayercss{display:none;position:absolute;width:1989px}.layui-layer-shade,.layui-layer{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%}.layui-layer{-webkit-overflow-scrolling:touch}.layui-layer{top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px #0000004d}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px #0003}.layui-layer-load{background:url(data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs=) #eee center center no-repeat}.layui-layer-ico{background:url(data:image/png;base64,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) no-repeat}.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a,.layui-layer-btn a{display:inline-block;*display: inline;*zoom: 1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position: absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layui-layer{border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}to{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}to{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translate(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translate(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translate(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translate(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translate(-100%) rotate(-120deg)}to{opacity:1;-webkit-transform:translateX(0px) rotate(0deg);transform:translate(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translate(-100%) rotate(-120deg)}to{opacity:1;-webkit-transform:translateX(0px) rotate(0deg);-ms-transform:translateX(0px) rotate(0deg);transform:translate(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.layer-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,to{-webkit-transform:translateX(0);transform:translate(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translate(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translate(10px)}}@keyframes shake{0%,to{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translate(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translate(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translate(10px)}}.layer-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@-webkit-keyframes bounceOut{to{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{to{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#f8f8f8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right: 0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2e2d3c;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right: -18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 10px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:0 6px;padding:0 15px;border:1px #dedede solid;background-color:#f1f1f1;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#4898d5;background-color:#2e8ded;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:#0009;color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-page .layui-layer-btn,.layui-layer-iframe .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:none}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:none;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(data:image/gif;base64,R0lGODlhPAAYALMPAPPJp/HYxfSwkf50Df5TD/+HAPe5bvqHR/twOviZavyrMu/m3f9EAP9mAP+ZAO7u7iH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6QUNDRERBRjMxRTIwNjgxMTg4QzZERjVGQ0I0MDI1NDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTlDMEI5MzU5RTY2MTFFMTkxRDY5NkVCMDBGOTBFRUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTlDMEI5MzQ5RTY2MTFFMTkxRDY5NkVCMDBGOTBFRUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpENzU2N0YwMTMyMjA2ODExODhDNkRGNUZDQjQwMjU0OSIgc3RS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) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs=) no-repeat}.layui-layer-loading .layui-layer-loading2,.layui-layer-ico16{width:32px;height:32px;background:url(data:image/gif;base64,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) no-repeat}.layui-layer-tips{background:none;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px #0003;background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow: hidden}.layui-layer-tips i.layui-layer-TipsT,.layui-layer-tips i.layui-layer-TipsB{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsR,.layui-layer-tips i.layui-layer-TipsL{top:1px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476a7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#bbb5b5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1}.layui-layer-iconext{background:url(data:image/png;base64,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) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px #0000001a inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px #0006}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display: inline;*zoom: 1;vertical-align:top}.layui-layer-imguide,.layui-layer-imgbar{display:none}.layui-layer-imgprev,.layui-layer-imgnext{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:none;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px}.layui-layer-imgnext{right:10px;background-position:-5px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:#000c;background-color:#000 \\ ;filter:Alpha(opacity=80);color:#fff;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;font-size:0}.layui-layer-imgtit *{display:inline-block;*display: inline;*zoom: 1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} "); System.addImportMap({ imports: { "jquery": "user:jquery", "localforage": "user:localforage", "toastify-js": "user:toastify-js", "blueimp-md5": "user:blueimp-md5", "viewerjs": "user:viewerjs", "qrcodejs": "user:qrcodejs" } }); System.set("user:jquery", (() => { const _ = jQuery; ('default' in _) || (_.default = _); return _ })()); System.set("user:localforage", (() => { const _ = localforage; ('default' in _) || (_.default = _); return _ })()); System.set("user:toastify-js", (() => { const _ = Toastify; ('default' in _) || (_.default = _); return _ })()); System.set("user:blueimp-md5", (() => { const _ = md5; ('default' in _) || (_.default = _); return _ })()); System.set("user:viewerjs", (() => { const _ = Viewer; ('default' in _) || (_.default = _); return _ })()); System.set("user:qrcodejs", (() => { const _ = QRCode; ('default' in _) || (_.default = _); return _ })()); System.register("./__entry.js", ['jquery', 'localforage', 'toastify-js', 'blueimp-md5', 'viewerjs', 'qrcodejs'], (function (exports, module) { 'use strict'; var $$1, localforage$1, Toastify$1, md5$1, Viewer$1, QRCode$1; return { setters: [module => { $$1 = module.default; }, module => { localforage$1 = module.default; }, module => { Toastify$1 = module.default; }, module => { md5$1 = module.default; }, module => { Viewer$1 = module.default; }, module => { QRCode$1 = module.default; }], execute: (async function () { var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); const scriptRel = /* @__PURE__ */ (function detectScriptRel() { const relList = typeof document !== "undefined" && document.createElement("link").relList; return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload"; })(); const assetsURL = function (dep) { return "/" + dep; }; const seen = {}; const __vitePreload = function preload(baseModule, deps, importerUrl) { let promise = Promise.resolve(); if (deps && deps.length > 0) { let allSettled2 = function (promises) { return Promise.all( promises.map( (p) => Promise.resolve(p).then( (value) => ({status: "fulfilled", value}), (reason) => ({status: "rejected", reason}) ) ) ); }; document.getElementsByTagName("link"); const cspNonceMeta = document.querySelector( "meta[property=csp-nonce]" ); const cspNonce = (cspNonceMeta == null ? void 0 : cspNonceMeta.nonce) || (cspNonceMeta == null ? void 0 : cspNonceMeta.getAttribute("nonce")); promise = allSettled2( deps.map((dep) => { dep = assetsURL(dep); if (dep in seen) return; seen[dep] = true; const isCss = dep.endsWith(".css"); const cssSelector = isCss ? '[rel="stylesheet"]' : ""; if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) { return; } const link = document.createElement("link"); link.rel = isCss ? "stylesheet" : scriptRel; if (!isCss) { link.as = "script"; } link.crossOrigin = ""; link.href = dep; if (cspNonce) { link.setAttribute("nonce", cspNonce); } document.head.appendChild(link); if (isCss) { return new Promise((res, rej) => { link.addEventListener("load", res); link.addEventListener( "error", () => rej(new Error(`Unable to preload CSS for ${dep}`)) ); }); } }) ); } function handlePreloadError(err) { const e = new Event("vite:preloadError", { cancelable: true }); e.payload = err; window.dispatchEvent(e); if (!e.defaultPrevented) { throw err; } } return promise.then((res) => { for (const item of res || []) { if (item.status !== "rejected") continue; handlePreloadError(item.reason); } return baseModule().catch(handlePreloadError); }); }; var __defProp2 = Object.defineProperty, __typeError = (msg) => { throw TypeError(msg); }, __publicField2 = (obj, key, value) => ((obj2, key2, value2) => key2 in obj2 ? __defProp2(obj2, key2, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj2[key2] = value2)(obj, "symbol" != typeof key ? key + "" : key, value), __privateMethod = (obj, member, method) => (((obj2, member2, msg) => { member2.has(obj2) || __typeError("Cannot " + msg); })(obj, member, "access private method"), method); const currentHref$1 = window.location.href, isJavDb$1 = currentHref$1.includes("javdb"), isJavBus$1 = currentHref$1.includes("javbus") || currentHref$1.includes("seejav") || currentHref$1.includes("bus") || currentHref$1.includes("javsee") || "javbus" === (() => { try { const _alt = $(".hidden-xs").attr("alt"); return _alt ? _alt.trim().toLowerCase() : void 0; } catch (_e) { return void 0; } })(), isSearchPage = currentHref$1.includes("/search?q") || currentHref$1.includes("/search/") || currentHref$1.includes("/users/"), Status_RUNNING = "RUNNING", Status_SUCCESS = "SUCCESS", Status_FAIL = "FAIL", Status_LOADING = "LOADING", Status_FILTER$1 = "filter", Status_FAVORITE$1 = "favorite", Status_HAS_DOWN$1 = "hasDown", Status_HAS_WATCH$1 = "hasWatch", NO = "no", YES$1 = "yes", qualityOptions = [{ id: "video-mhb", quality: "dmb_w", text: "旧视频源-中画质宽版 (404p)", canSelect: false }, { id: "video-mhb", quality: "sm_s", text: "旧视频源-低画质 (240p)", canSelect: false }, { id: "video-mhb", quality: "dm_s", text: "旧视频源-中画质 (360p)", canSelect: false }, { id: "video-mhb", quality: "dmb_s", text: "旧视频源-中画质 (480p)", canSelect: false }, { id: "video-mhb", quality: "mhb_w", text: "旧视频源-高画质宽版 (404p)", canSelect: false }, { id: "video-mmb", quality: "mmb", text: "中画质 (432p)", canSelect: true }, { id: "video-dm", quality: "dm", text: "低画质 (1000kbps)", canSelect: true }, { id: "video-dmb", quality: "dmb", text: "中画质 (1500kbps)", canSelect: true }, { id: "video-mhb", quality: "mhb", text: "高画质 (576p)", canSelect: true }, { id: "video-hmb", quality: "hmb", text: "HD (720p)", canSelect: true }, { id: "video-hhb", quality: "hhb", text: "FullHD (1080p)", canSelect: true }, { id: "video-hhbs", quality: "hhbs", text: "FullHD (1080p60fps)", canSelect: true }, { id: "video-4k", quality: "4k", text: "4K (2160p)", canSelect: true }, { id: "video-4ks", quality: "4ks", text: "4K (2160p60fps)", canSelect: true }]; let detailPageCss$1 = ""; window.location.href.includes("hideNav=1") && (detailPageCss$1 = "\n .navbar-default {\n display: none !important;\n }\n body {\n padding-top:0px!important;\n }\n "); const javBusStyle = ` `; let detailPageCss = ""; window.location.href.includes("hideNav=1") && (detailPageCss = "\n .main-nav,#search-bar-container {\n display: none !important;\n }\n \n html {\n padding-top:0px!important;\n }\n "); const javdbStyle = ` `; const mainCss = ` `; const subtitleModalCss = ` `; function insertStyle(css) { if (css) if (css.includes("\n" ); insertStyle(mainCss); insertStyle(subtitleModalCss); const isEligibleDmmCoverCode = (value) => { const raw = String(value || "").trim().toUpperCase(); if (!/^[A-Z0-9]{2,10}-\d{1,6}$/.test(raw)) return false; const code = raw.replace(/\s+/g, "-").toUpperCase(); if (!/^[A-Z0-9]{2,10}-\d{1,6}$/.test(code) || /^FC2-/.test(code)) return false; if (/^\d{6}[-_]\d{2,3}$/.test(code)) return false; if (/^(DUGA|MYWIFE|HEYZO|PACO|10MU|1PONDO|CARIBBEAN|CARIB|TOKYO|GACHI|REAL|JUKU|AKA|NTR)-/.test(code)) return false; return true; }; window.$ = window.jQuery = $$1; if (typeof $$1.trim !== "function") { $$1.trim = function (text) { return text == null ? "" : (text + "").trim(); }; } if (typeof jQuery !== "undefined" && typeof layer === "undefined") { await __vitePreload(() => module.import('./layer-npqWxmx4-BZXOsKge.js').then((n) => n.l), void 0); } window.localforage = localforage$1; window.Toastify = Toastify$1; window.md5 = md5$1; window.Viewer = Viewer$1; window.QRCode = QRCode$1; window.currentHref = currentHref$1; window.isJavDb = isJavDb$1; window.isJavBus = isJavBus$1; window.isSearchPage = isSearchPage; window.Status_RUNNING = Status_RUNNING; window.Status_SUCCESS = Status_SUCCESS; window.Status_FAIL = Status_FAIL; window.Status_LOADING = Status_LOADING; window.Status_FILTER = Status_FILTER$1; window.Status_FAVORITE = Status_FAVORITE$1; window.Status_HAS_DOWN = Status_HAS_DOWN$1; window.Status_HAS_WATCH = Status_HAS_WATCH$1; window.NO = NO; window.YES = YES$1; const HAS_GM = typeof GM !== "undefined"; const NEW_GM = ((scope, GM2) => { if (typeof GM_info === "undefined" || GM_info.scriptHandler !== "Tampermonkey" || compareVersions(GM_info.version, "5.3.2") < 0) return; const GM_xmlhttpRequestOrig = GM_xmlhttpRequest; const GM_xmlHttpRequestOrig = GM2.xmlHttpRequest; function compareVersions(v1, v2) { const parts1 = v1.split(".").map(Number); const parts2 = v2.split(".").map(Number); const length = Math.max(parts1.length, parts2.length); for (let i = 0; i < length; i++) { const num1 = parts1[i] || 0; const num2 = parts2[i] || 0; if (num1 > num2) return 1; if (num1 < num2) return -1; } return 0; } function GM_xmlhttpRequestWrapper(odetails) { if (odetails.redirect !== void 0) { return GM_xmlhttpRequestOrig(odetails); } if (odetails.onprogress || odetails.fetch === false) { console.warn("Fetch mode does not support onprogress in the background."); } const { onload, onloadend, onerror, onabort, ontimeout, ...details } = odetails; const handleRedirects = (initialDetails) => { const request = GM_xmlhttpRequestOrig({ ...initialDetails, redirect: "manual", onload: function (response) { if (response.status >= 300 && response.status < 400) { const m = response.responseHeaders.match(/Location:\s*(\S+)/i); const redirectUrl = m && m[1]; if (redirectUrl) { const absoluteUrl = new URL(redirectUrl, initialDetails.url).href; handleRedirects({...initialDetails, url: absoluteUrl}); return; } } if (onload) onload.call(this, response); if (onloadend) onloadend.call(this, response); }, onerror: function (response) { if (onerror) onerror.call(this, response); if (onloadend) onloadend.call(this, response); }, onabort: function (response) { if (onabort) onabort.call(this, response); if (onloadend) onloadend.call(this, response); }, ontimeout: function (response) { if (ontimeout) ontimeout.call(this, response); if (onloadend) onloadend.call(this, response); } }); return request; }; return handleRedirects(details); } function GM_xmlHttpRequestWrapper(odetails) { let abort; const p = new Promise((resolve, reject) => { const {onload, ontimeout, onerror, ...send} = odetails; send.onerror = function (r) { if (onerror) { resolve(r); onerror.call(this, r); } else { reject(r); } }; send.ontimeout = function (r) { if (ontimeout) { resolve(r); ontimeout.call(this, r); } else { reject(r); } }; send.onload = function (r) { resolve(r); if (onload) onload.call(this, r); }; const a = GM_xmlhttpRequestWrapper(send).abort; if (abort === true) { a(); } else { abort = a; } }); p.abort = () => { if (typeof abort === "function") { abort(); } else { abort = true; } }; return p; } GM_xmlhttpRequest = GM_xmlhttpRequestWrapper; scope.GM_xmlhttpRequestOrig = GM_xmlhttpRequestOrig; const gopd = Object.getOwnPropertyDescriptor(GM2, "xmlHttpRequest"); if (gopd && gopd.configurable === false) { return { __proto__: GM2, xmlHttpRequest: GM_xmlHttpRequestWrapper, xmlHttpRequestOrig: GM_xmlHttpRequestOrig }; } else { GM2.xmlHttpRequest = GM_xmlHttpRequestWrapper; GM2.xmlHttpRequestOrig = GM_xmlHttpRequestOrig; } })(globalThis, HAS_GM ? GM : {}); if (HAS_GM && NEW_GM) GM = NEW_GM; var _StorageManager_instances, setItem_fn, saveFilterItem_fn; _StorageManager_instances = /* @__PURE__ */ new WeakSet(); setItem_fn = async function (key, data) { key === this.favorite_actresses_key && window.clean_cacheFavoriteActressList(); key === this.blacklist_car_list_key && window.clean_cacheBlacklistCarList(); key === this.setting_key && window.clean_cacheSettingObj(); key === this.car_list_key && window.clean_cacheCarList(); await this.forage.setItem(key, data); }; saveFilterItem_fn = async function (items, storageKey, itemName) { let itemList; if (Array.isArray(items)) itemList = [...items]; else { itemList = await this.forage.getItem(storageKey) || []; if (itemList.includes(items)) { const errorMsg = `${items} ${itemName}已存在`; show.error(errorMsg); throw new Error(errorMsg); } itemList.push(items); } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, storageKey, itemList); return itemList; }; let StorageManager = class _StorageManager { constructor() { obj = this, (member = _StorageManager_instances).has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var obj, member, value; __publicField2(this, "car_list_key", "car_list"); __publicField2(this, "filter_keyword_title_key", "filter_keyword_title"); __publicField2(this, "filter_keyword_review_key", "filter_keyword_review"); __publicField2(this, "setting_key", "setting"); __publicField2(this, "blacklist_key", "blacklist"); __publicField2(this, "blacklist_car_list_key", "blacklist_car_list"); __publicField2(this, "favorite_actresses_key", "favorite_actresses"); __publicField2(this, "highlighted_tags_key", "highlighted_tags"); __publicField2(this, "forage", localforage.createInstance({ driver: localforage.INDEXEDDB, name: "JAV-JHS", version: 1, storeName: "appData" })); __publicField2(this, "cacheCarList", null); __publicField2(this, "cacheBlacklistCarList", null); __publicField2(this, "cacheFavoriteActressList", null); __publicField2(this, "cacheSettingObj", null); if (_StorageManager.instance) throw new Error("StorageManager已被实例化过了!"); _StorageManager.instance = this; } async getCarList() { if (this.cacheCarList) return utils.copyObj(this.cacheCarList); this.cacheCarList = await this.forage.getItem(this.car_list_key) || []; return utils.copyObj(this.cacheCarList); } async getCar(carNum2) { return (await this.getCarList()).find(((item) => item.carNum === carNum2)); } _handleSingleCar(carParam, carList) { let {carNum: carNum2, url, names, actionType, publishTime, starId} = carParam; if (!carNum2) { show.error("番号为空!"); throw new Error("番号为空!"); } if (!url) { show.error("url为空!"); throw new Error("url为空!"); } url.includes("http") || (url = window.location.origin + url); names && (names = names.trim()); let carData = carList.find(((item) => item.carNum === carNum2)); if (carData) { names && (carData.names = names); url && (carData.url = url); publishTime && (carData.publishTime = publishTime); carData.updateDate = utils.getNowStr(); } else { let nowStr = utils.getNowStr(); carData = { carNum: carNum2, url, names, status: "", createDate: nowStr, updateDate: nowStr, publishTime }; starId && (carData.starId = starId); carList.push(carData); } switch (actionType) { case Status_FILTER: if (carData.status === Status_FILTER) { const msg2 = `${carNum2} 已在屏蔽列表中`; show.error(msg2); throw new Error(msg2); } carData.status = Status_FILTER; break; case Status_FAVORITE: if (carData.status === Status_FAVORITE) { const msg2 = `${carNum2} 已在收藏列表中`; show.error(msg2); throw new Error(msg2); } carData.status = Status_FAVORITE; break; case Status_HAS_DOWN: carData.status = Status_HAS_DOWN; break; case Status_HAS_WATCH: carData.status = Status_HAS_WATCH; break; default: const msg = "actionType错误, 请联系作者更正: " + actionType; show.error(msg); throw new Error(msg); } } async saveCar(carData) { const carList = await this.forage.getItem(this.car_list_key) || []; this._handleSingleCar(carData, carList); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, carList); await this.removeNewVideoList([carData.carNum]); } async updateCarInfo(carParam) { let {carNum: carNum2, url, names, actionType, publishTime, remark} = carParam; if (!carNum2) { show.error("番号为空!"); throw new Error("番号为空!"); } if (!url) { show.error("url为空!"); throw new Error("url为空!"); } names && (names = names.trim()); const carList = await this.forage.getItem(this.car_list_key) || []; let carData = carList.find(((item) => item.carNum === carNum2)); if (!carData) { const msg = "数据不存在: " + carNum2; show.error(msg); throw new Error(msg); } carData.names = names; carData.url = url; carData.remark = remark; carData.updateDate = utils.getNowStr(); switch (actionType) { case Status_FILTER: carData.status = Status_FILTER; break; case Status_FAVORITE: carData.status = Status_FAVORITE; break; case Status_HAS_DOWN: carData.status = Status_HAS_DOWN; break; case Status_HAS_WATCH: carData.status = Status_HAS_WATCH; break; default: const msg = "actionType错误, 请联系作者更正: " + actionType; show.error(msg); throw new Error(msg); } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, carList); } async saveCarList(carRecords) { if (!carRecords || !Array.isArray(carRecords) || 0 === carRecords.length) { show.error("记录列表为空!"); throw new Error("记录列表为空!"); } const carList = await this.forage.getItem(this.car_list_key) || []; for (const record of carRecords) try { this._handleSingleCar(record, carList); } catch (error) { throw error; } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, carList); } async removeNewVideoList(carNumList) { const favoriteActressesList = await this.getFavoriteActressList(); let hasAnyListBeenModified = false; const updatedActressList = favoriteActressesList.map(((actress) => { if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress; const newFilteredVideoList = actress.newVideoList.filter(((carNumString) => { const isRemoved = carNumList.includes(carNumString); if (isRemoved) { clog.log("移除关联女优新作品", actress.name, carNumString); hasAnyListBeenModified = true; } return !isRemoved; })); return { ...actress, newVideoList: newFilteredVideoList }; })); hasAnyListBeenModified && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, updatedActressList); } async removeCar(carNum2) { const carList = await this.getCarList(), initialLength = carList.length, updatedList = carList.filter(((car) => car.carNum !== carNum2)); if (updatedList.length === initialLength) { show.error(`${carNum2} 不存在`); return false; } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, updatedList); return true; } async batchRemoveCars(carNumList) { if (!carNumList || 0 === carNumList.length) throw new Error("未传入参数"); const carList = await this.getCarList(), initialLength = carList.length, carNumsSet = new Set(carNumList), updatedList = carList.filter(((car) => !carNumsSet.has(car.carNum))), removedCount = initialLength - updatedList.length; if (0 === removedCount) return removedCount; await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, updatedList); return removedCount; } async getBlacklist() { return await this.forage.getItem(this.blacklist_key) || []; } async addBlacklistItem(item) { let {starId, name: name2, allName, role, movieType, url} = item; if (!starId) throw new Error("缺失starId"); if (!name2) throw new Error("缺失name"); if (!role) throw new Error("缺失role"); const blacklist = await this.getBlacklist(), existData = blacklist.find(((item2) => item2.starId === starId)); if (existData) { existData.url = url; existData.role = role; existData.movieType = movieType; clog.log("更新黑名单演员信息", existData); } else { const info = { starId, name: name2, allName: allName || [name2], createTime: utils.getNowStr(), role, movieType, url }; blacklist.push(info); clog.log("增加黑名单演员信息", info); } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_key, blacklist); } async updateBlacklistItem(item) { if (!item || !item.starId) throw new Error("参数不全"); const filterActorActressInfoList = await this.getBlacklist(), waitUpdateObj = filterActorActressInfoList.find(((i) => i.starId === item.starId)); if (!waitUpdateObj) throw new Error(`未找到黑名单演员信息:${item.name} ${item.starId}`); item.checkTime && (waitUpdateObj.checkTime = item.checkTime); item.lastPublishTime && (waitUpdateObj.lastPublishTime = item.lastPublishTime); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_key, filterActorActressInfoList); } async deleteBlacklistItem(starId) { const blacklist = await this.getBlacklist(), updatedList = blacklist.filter(((item) => item.starId !== starId)); blacklist.length !== updatedList.length && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_key, updatedList); } async getBlacklistCarList() { if (this.cacheBlacklistCarList && this.cacheBlacklistCarList.length > 0) return utils.deepFreeze(this.cacheBlacklistCarList); this.cacheBlacklistCarList = await this.forage.getItem(this.blacklist_car_list_key) || []; setTimeout((() => { utils.deepFreeze(this.cacheBlacklistCarList); }), 0); return this.cacheBlacklistCarList; } async batchSaveBlacklistCarList(carDataList) { const carList = await this.getCarList(); let blacklistCarList = await this.getBlacklistCarList(), hasChanged = false, copyBlacklistCarList = null, carNumList = []; for (const carData of carDataList) { let existBlacklistCarData = blacklistCarList.find(((item) => item.carNum === carData.carNum)); if (existBlacklistCarData) { console.log("已在鉴定记录中", existBlacklistCarData); continue; } let existCarData = carList.find(((item) => item.carNum === carData.carNum)); if (existCarData) console.log("已在鉴定记录中", existCarData); else { copyBlacklistCarList || (copyBlacklistCarList = utils.copyObj(blacklistCarList)); this._handleSingleCar(carData, copyBlacklistCarList); clog.log(`屏蔽演员番号: ${carData.names} ${carData.carNum}`); hasChanged = true; carNumList.push(carData.carNum); } } if (hasChanged) { if (!copyBlacklistCarList) throw new Error("程序异常, 黑名单番号数据对象为空!"); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_car_list_key, copyBlacklistCarList); await this.removeNewVideoList(carNumList); } } async removeBlacklistCarList(starId) { const filterActorActressCarList = await this.getBlacklistCarList(), newList = filterActorActressCarList.filter(((car) => car.starId !== starId)); newList.length !== filterActorActressCarList.length && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_car_list_key, newList); } async batchRemoveBlacklistCars(carNumList) { if (!carNumList || 0 === carNumList.length) throw new Error("未传入参数"); const blacklistCarList = await this.getBlacklistCarList(), initialLength = blacklistCarList.length, carNumsSet = new Set(carNumList), updatedList = blacklistCarList.filter(((car) => !carNumsSet.has(car.carNum))), removedCount = initialLength - updatedList.length; if (0 === removedCount) return removedCount; await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_car_list_key, updatedList); return removedCount; } async getFavoriteActressList() { if (this.cacheFavoriteActressList) return utils.copyObj(this.cacheFavoriteActressList); this.cacheFavoriteActressList = await this.forage.getItem(this.favorite_actresses_key) || []; return utils.copyObj(this.cacheFavoriteActressList); } async addFavoriteActressList(actressList) { const favoriteActressesInfoList = await this.getFavoriteActressList(); let resultCount = 0; for (const actress of actressList) { let {starId, name: name2, allName, avatar, lastCheckTime, lastPublishTime, actressType} = actress; if (!starId) throw new Error("缺失starId"); if (!name2) throw new Error("缺失name"); allName || (allName = [name2]); const uncensoredText = "(無碼)"; if (!actressType) { actressType = name2.includes(uncensoredText) || allName.some(((element) => element.includes(uncensoredText))) ? "uncensored" : "censored"; } name2 = name2.replace(uncensoredText, ""); allName = allName.map(((n) => n.replace(uncensoredText, ""))); let favoriteActresses = favoriteActressesInfoList.find(((item) => item.starId === starId)); if (favoriteActresses) { if ((!favoriteActresses.avatar || !favoriteActresses.avatar.includes("https")) && avatar) { clog.log(avatar); favoriteActresses.avatar = avatar; clog.log(`补全女优头像: ${name2}`); resultCount++; } if (!favoriteActresses.actressType && actressType) { favoriteActresses.actressType = actressType; clog.log(`补全女优类别: ${name2} ${actressType}`); resultCount++; } if (favoriteActresses.name.includes(uncensoredText)) { favoriteActresses.name = name2; favoriteActresses.allName = allName; clog.log(`更正女优名字: ${name2} ${allName}`); resultCount++; } continue; } const nowStr = utils.getNowStr(); favoriteActressesInfoList.push({ starId, name: name2, allName, avatar, lastCheckTime, lastPublishTime, createDate: nowStr, updateDate: nowStr, actressType }); clog.log(`同步JavDB已收藏的演员: ${name2}`); resultCount++; } resultCount > 0 ? await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, favoriteActressesInfoList) : clog.log("信息已记录, 无需要进行同步收藏的演员"); return resultCount; } async removeFavoriteActress(starId) { const favoriteActressesInfoList = await this.getFavoriteActressList(), initialLength = favoriteActressesInfoList.length, updatedList = favoriteActressesInfoList.filter(((car) => car.starId !== starId)); if (updatedList.length === initialLength) { clog.error(`移除演员失败, ${starId} 不存在`); return false; } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, updatedList); return true; } async updateFavoriteActress(actress) { const favoriteActressesInfoList = await this.getFavoriteActressList(), { starId, name: name2, allName, avatar, lastCheckTime, newVideoList, lastPublishTime, actressType, remark } = actress; if (!starId) throw new Error("缺失starId"); let favoriteActresses = favoriteActressesInfoList.find(((item) => item.starId === starId)); if (!favoriteActresses) { clog.error("未找到演员信息", starId, name2); return false; } name2 && (favoriteActresses.name = name2); allName && (favoriteActresses.allName = allName); avatar && (favoriteActresses.avatar = avatar); null != actressType && (favoriteActresses.actressType = actressType); lastCheckTime && (favoriteActresses.lastCheckTime = lastCheckTime); newVideoList && (favoriteActresses.newVideoList = newVideoList); lastPublishTime && (favoriteActresses.lastPublishTime = lastPublishTime); remark && (favoriteActresses.remark = remark); favoriteActresses.updateDate = utils.getNowStr(); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, favoriteActressesInfoList); } async getHighlightedTags() { return await this.forage.getItem(this.highlighted_tags_key) || []; } async setHighlightedTags(highlightedTags) { return await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.highlighted_tags_key, highlightedTags); } async saveTitleFilterKeyword(keywords) { await __privateMethod(this, _StorageManager_instances, saveFilterItem_fn).call(this, keywords, this.filter_keyword_title_key, "标题关键词"); if (Array.isArray(keywords)) return null; const favoriteActressesList = await this.getFavoriteActressList(); let hasAnyListBeenModified = false; const updatedActressList = favoriteActressesList.map(((actress) => { if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress; const newFilteredVideoList = actress.newVideoList.filter(((carNumString) => { const isRemoved = carNumString.startsWith(keywords); if (isRemoved) { clog.log("移除关联女优新作品", actress.name, carNumString); hasAnyListBeenModified = true; } return !isRemoved; })); return { ...actress, newVideoList: newFilteredVideoList }; })); hasAnyListBeenModified && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, updatedActressList); } async getTitleFilterKeyword() { return await this.forage.getItem(this.filter_keyword_title_key) || []; } async getReviewFilterKeywordList() { return await this.forage.getItem(this.filter_keyword_review_key) || []; } async saveReviewFilterKeyword(keywords) { return __privateMethod(this, _StorageManager_instances, saveFilterItem_fn).call(this, keywords, this.filter_keyword_review_key, "评论关键词"); } async getSetting(attribute = null, defaultVal) { this.cacheSettingObj || (this.cacheSettingObj = await this.forage.getItem(this.setting_key) || {}); let settingObj = utils.copyObj(this.cacheSettingObj); if (null === attribute) return settingObj; const value = settingObj[attribute]; return value ? "true" === value || "false" === value ? "true" === value.toLowerCase() : "string" != typeof value || "" === value.trim() || isNaN(Number(value)) ? value : Number(value) : defaultVal; } async saveSetting(settingObj) { settingObj ? await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.setting_key, settingObj) : show.error("设置对象为空"); } async saveSettingItem(key, value) { if (!key) { show.error("key 不能为空"); return; } let settingObj = await this.getSetting(); settingObj[key] = value; await this.saveSetting(settingObj); } async importData(jsonData) { await this.forage.clear(); const importPromises = []; for (const key in jsonData) { const value = jsonData[key], setPromise = __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, key, value); importPromises.push(setPromise); } await Promise.all(importPromises); } async exportData() { const dataMap = {}; await this.forage.iterate(((value, key) => { dataMap[key] = value; })); if (0 === Object.keys(dataMap).length) throw new Error("没有可导出的数据"); return dataMap; } }; const apiUrl = "https://jdforrepam.com/api"; function buildSignature() { const curr = Math.floor(Date.now() / 1e3), stored_sign = localStorage.getItem("jhs_jdsignature"); if (stored_sign) { const parts = stored_sign.split("."); if (3 === parts.length) { if (curr - parseInt(parts[0]) <= 300) return stored_sign; } } const sign = `${curr}.lpw6vgqzsp.${md5(`${curr}71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa`)}`; localStorage.setItem("jhs_jdsignature", sign); return sign; } const javDbApi = { getReviews: async (movieId, pageNum = 1, pageSize = 20) => { let url = `${apiUrl}/v1/movies/${movieId}/reviews`, headers = { jdSignature: await buildSignature() }; return (await gmHttp.get(url, { page: pageNum, sort_by: "hotly", limit: pageSize }, headers)).data.reviews; }, searchMovie: async (keyword) => { let url = `${apiUrl}/v2/search`, headers = { "user-agent": "Dart/3.5 (dart:io)", "accept-language": "zh-TW", host: "jdforrepam.com", jdsignature: await buildSignature() }, params = { q: keyword, page: 1, type: "movie", limit: 1, movie_type: "all", from_recent: "false", movie_filter_by: "all", movie_sort_by: "relevance" }; return (await gmHttp.get(url, params, headers)).data.movies; }, getMovieDetail: async (movieId) => { let url = `${apiUrl}/v4/movies/${movieId}`, headers = { jdSignature: await buildSignature() }; const res = await gmHttp.get(url, null, headers); if (!res.data) { show.error("获取视频详情失败: " + res.message); throw new Error(res.message); } const movie = res.data.movie, preview_images = movie.preview_images, imgList = []; preview_images.forEach(((item) => { imgList.push(item.large_url.replace("https://tp.spfcas.com/rhe951l4q", "https://c0.jdbstatic.com")); })); return { movieId: movie.id, actors: movie.actors, duration: movie.duration, title: movie.origin_title, carNum: movie.number, score: movie.score, releaseDate: movie.release_date, watchedCount: movie.watched_count, imgList }; }, related: async (movieId, page = 1, limit = 20) => { let url = `${apiUrl}/v1/lists/related?movie_id=${movieId}&page=${page}&limit=${limit}`, headers = { jdSignature: await buildSignature() }; const res = await gmHttp.get(url, null, headers), dataList = []; res.data.lists.forEach(((item) => { dataList.push({ relatedId: item.id, name: item.name, movieCount: item.movies_count, collectionCount: item.collections_count, viewCount: item.views_count, createTime: utils.formatDate(item.created_at) }); })); return dataList; }, getMagnets: async (movieId) => { let url = `${apiUrl}/v1/movies/${movieId}/magnets`, headers = { jdSignature: await buildSignature() }; return (await gmHttp.get(url, null, headers)).data.magnets; }, playback: async (period = "daily", filter_by = "high_score") => { let url = `${apiUrl}/v1/rankings/playback?period=${period}&filter_by=${filter_by}`, headers = { jdSignature: await buildSignature() }; return (await gmHttp.get(url, null, headers)).data.movies; }, login: async (username, password) => { let url = `${apiUrl}/v1/sessions?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&device_uuid=04b9534d-5118-53de-9f87-2ddded77111e&device_name=iPhone&device_model=iPhone&platform=ios&system_version=17.4&app_version=official&app_version_number=1.9.29&app_channel=official`, headers = { "user-agent": "Dart/3.5 (dart:io)", "accept-language": "zh-TW", "content-type": "multipart/form-data; boundary=--dio-boundary-2210433284", jdsignature: await buildSignature() }; return await gmHttp.post(url, null, headers); }, top250: async (type = "all", type_value = "", page = 1, limit = 40) => { let url = `${apiUrl}/v1/movies/top?start_rank=1&type=${type}&type_value=${type_value}&ignore_watched=false&page=${page}&limit=${limit}`, headers = { "user-agent": "Dart/3.5 (dart:io)", "accept-language": "zh-TW", host: "jdforrepam.com", authorization: "Bearer " + localStorage.getItem("jhs_appAuthorization"), jdsignature: await buildSignature() }; return await gmHttp.get(url, null, headers); }, buildSignature, markDataListHtml: (movies) => { let moviesHtml = ""; movies.forEach(((movie) => { moviesHtml += `
${movie.number} ${movie.origin_title}
${movie.release_date}
${movie.has_cnsub ? '含中字磁鏈' : movie.magnets_count > 0 ? '含磁鏈' : '无磁鏈'} ${movie.new_magnets ? '今日新種' : ""}
`; })); return moviesHtml; } }; var _GM_openInTab = /* @__PURE__ */ (() => typeof GM_openInTab != "undefined" ? GM_openInTab : void 0)(); var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)(); class Utils { constructor() { __publicField2(this, "intervalContainer", {}); __publicField2(this, "mimeTypes", { txt: "text/plain", html: "text/html", css: "text/css", csv: "text/csv", json: "application/json", xml: "application/xml", jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp", svg: "image/svg+xml", pdf: "application/pdf", doc: "application/msword", docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", xls: "application/vnd.ms-excel", xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ppt: "application/vnd.ms-powerpoint", pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation", zip: "application/zip", rar: "application/x-rar-compressed", "7z": "application/x-7z-compressed", mp3: "audio/mpeg", wav: "audio/wav", mp4: "video/mp4", webm: "video/webm", ogg: "audio/ogg" }); __publicField2(this, "timers", /* @__PURE__ */ new Map()); __publicField2(this, "insertStyle", ((css) => { if (css) { -1 === css.indexOf(""); $("head").append(css); } })); __publicField2(this, "layerIndexStack", []); Utils.instance || (Utils.instance = this); return Utils.instance; } importResource(url) { let tag; if (url.indexOf("css") >= 0) { tag = document.createElement("link"); tag.setAttribute("rel", "stylesheet"); tag.href = url; } else { tag = document.createElement("script"); tag.setAttribute("type", "text/javascript"); tag.src = url; } document.documentElement.appendChild(tag); } openPage(url, title, shadeClose, event) { if (!url) throw new Error("未传入url"); shadeClose = shadeClose ?? true; if (event && (event.ctrlKey || event.metaKey)) { _GM_openInTab(url.includes("http") ? url : window.location.origin + url, { insert: 0 }); return; } let finalUrl = url; url.includes("/actors/") || url.includes("/star/") || (finalUrl = url.includes("?") ? `${url}&hideNav=1` : `${url}?hideNav=1`); layer.open({ type: 2, title, content: finalUrl, scrollbar: false, shadeClose, area: this.getResponsiveArea(["85%", "90%"]), isOutAnim: false, anim: -1, success: (layero, index) => { this.setupEscClose(index); } }); } _handleGlobalEscKey(e) { if ("Escape" !== e.key && 27 !== e.keyCode) return; if (0 === this.layerIndexStack.length) return; const topLayerIndex = this.layerIndexStack[this.layerIndexStack.length - 1], $layer = $(`#layui-layer${topLayerIndex}`); let viewerExists = false; if ($layer.find(".viewer-container").length > 0) viewerExists = true; else { const iframe = $layer.find(`#layui-layer-iframe${topLayerIndex}`)[0]; if (iframe && iframe.contentDocument) try { $(iframe.contentDocument).find(".viewer-container").length > 0 && (viewerExists = true); } catch (error) { clog.warn("无法检查跨域 iframe 内的 .viewer-container"); } } if (!viewerExists) { this.layerIndexStack.pop(); typeof layer !== "undefined" && layer.close(topLayerIndex); } } setupEscClose(layerIndex) { var _a2; if (!this._boundHandler) { this._boundHandler = this._handleGlobalEscKey.bind(this); $(document).off("keydown.globalLayerEsc"); $(document).on("keydown.globalLayerEsc", this._boundHandler); } -1 === this.layerIndexStack.indexOf(layerIndex) && this.layerIndexStack.push(layerIndex); const $iframe = $(`#layui-layer-iframe${layerIndex}`), eventNamespace = `keydown.layerEsc${layerIndex}`; try { const iframeDocument = null == (_a2 = $iframe[0]) ? void 0 : _a2.contentDocument; if (iframeDocument) { if ("yes" === $iframe.attr("data-esc-bound")) return; $(iframeDocument).off(eventNamespace); $(iframeDocument).on(eventNamespace, this._boundHandler); $iframe.attr("data-esc-bound", "yes"); } } catch (e) { clog.error("iframe监听失败 (跨域或未加载完毕):", e); } } closePage() { storageManager.getSetting("needClosePage", "yes").then(((needClosePage) => { if ("yes" !== needClosePage) return; parent.document.documentElement.style.overflow = "auto"; [".layui-layer-shade", ".layui-layer-move", ".layui-layer"].forEach((function (selector) { const elements = parent.document.querySelectorAll(selector); if (elements.length > 0) { const elementToRemove = elements.length > 1 ? elements[elements.length - 1] : elements[0]; elementToRemove.parentNode.removeChild(elementToRemove); } })); window.close(); })); } loopDetector(condition, after, detectInterval = 20, timeout = 1e4, runWhenTimeout = true) { const uuid = Math.random(), start = (/* @__PURE__ */ new Date()).getTime(), stopAndRun = (shouldRun) => { clearInterval(this.intervalContainer[uuid]); shouldRun && after && after(); delete this.intervalContainer[uuid]; }; this.intervalContainer[uuid] = setInterval((() => { const timeElapsed = (/* @__PURE__ */ new Date()).getTime() - start; condition() ? stopAndRun(true) : timeElapsed >= timeout && stopAndRun(runWhenTimeout); }), detectInterval); } rightClick(container, targetSelector, callback) { let containerElement; "string" == typeof container ? containerElement = document.querySelector(container) : container instanceof HTMLElement && (containerElement = container); if (!containerElement) { console.warn("rightClick(), 容器无效或未提供,将使用 document.body 进行全局委托。"); containerElement = document.body; } "string" == typeof targetSelector && "" !== targetSelector.trim() ? containerElement.addEventListener("contextmenu", ((event) => { const targetElement = event.target.closest(targetSelector); targetElement && callback(event, targetElement); })) : console.error("rightClick(), 必须提供有效的 targetSelector。"); } q(event, msg, fun, cancelFun) { let x, y; if (event) { x = event.clientX - 130; y = event.clientY - 120; } else { x = window.innerWidth / 2 - 120; y = window.innerHeight / 2 - 120; } let confirmIndex = layer.confirm(msg, { offset: [y, x], title: "提示", btn: ["确定", "取消"], shade: 0, zIndex: 999999991 }, (function () { fun && fun(); layer.close(confirmIndex); }), (function () { cancelFun && cancelFun(); })); } alert(event, msg, yesFun) { let offset; event && (offset = [event.clientX - 200, event.clientY - 120]); let confirmIndex = layer.alert(msg, { offset, shade: 0, zIndex: 999999991 }, (function () { yesFun && yesFun(); layer.close(confirmIndex); })); } getNowStr(dateSplitStr = "-", timeSplitStr = ":", dateString = null) { let now; now = dateString ? new Date(dateString) : /* @__PURE__ */ new Date(); const year = now.getFullYear(), month = String(now.getMonth() + 1).padStart(2, "0"), day = String(now.getDate()).padStart(2, "0"), hours = String(now.getHours()).padStart(2, "0"), minutes = String(now.getMinutes()).padStart(2, "0"), seconds = String(now.getSeconds()).padStart(2, "0"); return `${[year, month, day].join(dateSplitStr)} ${[hours, minutes, seconds].join(timeSplitStr)}`; } formatDate(date, dateSplitStr = "-", timeSplitStr = ":") { let targetDate; if (date instanceof Date) targetDate = date; else { if ("string" != typeof date) throw new Error("Invalid date input: must be Date object or date string"); targetDate = new Date(date); if (isNaN(targetDate.getTime())) throw new Error("Invalid date string"); } const year = targetDate.getFullYear(), month = String(targetDate.getMonth() + 1).padStart(2, "0"), day = String(targetDate.getDate()).padStart(2, "0"), hours = String(targetDate.getHours()).padStart(2, "0"), minutes = String(targetDate.getMinutes()).padStart(2, "0"), seconds = String(targetDate.getSeconds()).padStart(2, "0"); return `${[year, month, day].join(dateSplitStr)} ${[hours, minutes, seconds].join(timeSplitStr)}`; } getHourDifference(date1, date2) { const timestamp1 = date1.getTime(), timestamp2 = date2.getTime(), differenceInHours = Math.abs(timestamp2 - timestamp1) / 36e5; return Math.floor(differenceInHours); } download(data, fileName) { show.info("开始请求下载..."); const fileExtension = fileName.split(".").pop().toLowerCase(); let blob, mimeType = this.mimeTypes[fileExtension] || "application/octet-stream"; if (data instanceof Blob) blob = data; else if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) blob = new Blob([data], { type: mimeType }); else if ("string" == typeof data && data.startsWith("data:")) { const byteString = atob(data.split(",")[1]), arrayBuffer = new ArrayBuffer(byteString.length), uintArray = new Uint8Array(arrayBuffer); for (let i = 0; i < byteString.length; i++) uintArray[i] = byteString.charCodeAt(i); blob = new Blob([uintArray], { type: mimeType }); } else blob = new Blob([data], { type: mimeType }); const url = URL.createObjectURL(blob), a = document.createElement("a"); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout((() => { document.body.removeChild(a); URL.revokeObjectURL(url); }), 100); } smoothScrollToTop(duration = 500) { return new Promise(((resolve) => { const start = performance.now(), startPosition = window.pageYOffset; window.requestAnimationFrame((function scrollStep(timestamp) { const elapsed = timestamp - start, progress = Math.min(elapsed / duration, 1), easeInOutCubic = progress < 0.5 ? 4 * progress * progress * progress : 1 - Math.pow(-2 * progress + 2, 3) / 2; window.scrollTo(0, startPosition * (1 - easeInOutCubic)); progress < 1 ? window.requestAnimationFrame(scrollStep) : resolve(); })); })); } simpleId() { return crypto.randomUUID().replace("-", ""); } isUrl(urlString) { try { new URL(urlString); return true; } catch (_) { return false; } } setHrefParam(key, val) { const newUrl = new URL(window.location.href); newUrl.searchParams.set(key, val); window.history.pushState({}, "", newUrl.toString()); } getUrlParam(url, key) { const searchString = url.split("?")[1]; if (!searchString) return null; const regex = new RegExp(`(?:^|&)${key}=([^&]*)`), match = searchString.match(regex); let value = ""; match && match[1] && (value = decodeURIComponent(match[1].replace(/\+/g, " "))); return value ? "true" === value || "false" === value ? "true" === value.toLowerCase() : "string" != typeof value || "" === value.trim() || isNaN(Number(value)) ? value : Number(value) : value; } reBuildSignature() { return javDbApi.buildSignature(); } getResponsiveArea(defaultArea) { const screenWidth = window.innerWidth; return screenWidth >= 1200 ? defaultArea || this.getDefaultArea() : screenWidth >= 768 ? ["70%", "90%"] : ["95%", "95%"]; } getDefaultArea() { return ["85%", "90%"]; } isMobile() { const userAgent = navigator.userAgent.toLowerCase(); return ["iphone", "ipod", "ipad", "android", "blackberry", "windows phone", "nokia", "webos", "opera mini", "mobile", "mobi", "tablet"].some(((keyword) => userAgent.includes(keyword))); } copyToClipboard(type, text) { navigator.clipboard.writeText(text).then((() => show.info(`${type}已复制到剪切板, ${text}`))).catch(((err) => console.error("复制失败: ", err))); } htmlTo$dom(html) { const parser = new DOMParser(); return $(parser.parseFromString(html, "text/html")); } addCookie(cookieStr, options = {}) { const {maxAge = 604800, path = "/", domain = "", secure = false, sameSite = "Lax"} = options; cookieStr.split(";").forEach(((cookie) => { const trimmed = cookie.trim(); if (trimmed) { const parts = trimmed.split("="); if (parts.length >= 2 && parts[0].trim()) { let cookieParts = [`${parts[0].trim()}=${parts.slice(1).join("=")}`]; maxAge > 0 && cookieParts.push(`max-age=${maxAge}`); cookieParts.push(`path=${path}`); domain && cookieParts.push(`domain=${domain}`); secure && cookieParts.push("Secure"); sameSite && cookieParts.push(`SameSite=${sameSite}`); console.log("document.cookie = '" + cookieParts.join("; ") + "'"); document.cookie = cookieParts.join("; "); } } })); } isHidden(el) { const element = el.jquery ? el[0] : el; return !element || (element.offsetWidth <= 0 && element.offsetHeight <= 0 || "none" === window.getComputedStyle(element).display); } time(label = "default", unit = "s", precision = 2) { if (this.timers.has(label)) { const timer = this.timers.get(label), elapsedTime = performance.now() - timer.startTime; let formattedTime, unitLabel; if ("s" === timer.unit) { formattedTime = (elapsedTime / 1e3).toFixed(timer.precision); unitLabel = "秒"; } else { formattedTime = elapsedTime.toFixed(timer.precision); unitLabel = "毫秒"; } this.timers.delete(label); return `${label}: ${formattedTime}${unitLabel}`; } this.timers.set(label, { startTime: performance.now(), unit, precision }); } sleep(ms = 1e3) { return new Promise(((resolve) => setTimeout(resolve, ms))); } genericSort(arr, sortRules, emptyLast = true) { if (!Array.isArray(arr) || 0 === arr.length) return []; if (!Array.isArray(sortRules) || 0 === sortRules.length) return [...arr]; const sortedArr = [...arr], safeDateConvert = (value) => { if (value instanceof Date) return value; if ("string" == typeof value) { const date = new Date(value); if (!isNaN(date.getTime())) return date; } return value; }; return sortedArr.sort(((a, b) => { for (const rule of sortRules) { const {key, order = "asc"} = rule; let valA = a, valB = b; if (null != key) if ("function" == typeof key) { valA = key(a); valB = key(b); } else { valA = a && "object" == typeof a ? a[key] : void 0; valB = b && "object" == typeof b ? b[key] : void 0; } const dateA = safeDateConvert(valA), dateB = safeDateConvert(valB); let comparison = 0; const aIsNull = null == valA, bIsNull = null == valB; if (aIsNull && bIsNull) return 0; if (aIsNull) return emptyLast ? 1 : -1; if (bIsNull) return emptyLast ? 1 : -1; comparison = dateA instanceof Date && dateB instanceof Date ? dateA.getTime() - dateB.getTime() : "number" == typeof valA && "number" == typeof valB ? valA - valB : "string" == typeof valA && "string" == typeof valB ? valA.localeCompare(valB) : String(valA).localeCompare(String(valB)); "desc" === order && (comparison *= -1); if (0 !== comparison) return comparison; } return 0; })); } async retry(fun, tryCount = 3) { let runCount = 0; for (; runCount < tryCount;) try { const result = await fun(); runCount > 0 && clog.debug(`[重试] 成功,共发起 ${runCount + 1} 次。`); return result; } catch (e) { let errorString = String(e); errorString.startsWith("Error: ") && (errorString = errorString.replace("Error: ", "")); if (errorString.includes("Just a moment") || errorString.includes("重定向") || errorString.toLowerCase().includes("404 page not found") || errorString.toLowerCase().includes("404 not found")) throw e; runCount++; if (runCount === tryCount) { clog.debug(`[重试] 达到最大重试次数 (${tryCount}),最终失败:`, e); throw e; } clog.debug(`[重试] 准备第 ${runCount + 1} 次重试, 错误信息: ${errorString}`); } } copyObj(data) { return JSON.parse(JSON.stringify(data)); } deepFreeze(obj) { if (null === obj || "object" != typeof obj || Object.isFrozen(obj)) return obj; const propNames = Object.getOwnPropertyNames(obj); for (const name2 of propNames) { const value = obj[name2]; value && "object" == typeof value && this.deepFreeze(value); } return Object.freeze(obj); } } class AsyncQueue { constructor() { this.queue = Promise.resolve(); } addTask(fun) { this.queue = this.queue.then((() => fun())).catch(((e) => { console.error("执行异步队列任务失败:", e); })); } async waitAllFinished() { return this.queue; } } class GmHttp { async get(url, params = {}, headers = {}, noRedirect) { return this.gmRequest("GET", url, null, params, headers, noRedirect); } post(url, data = {}, headers = {}) { headers = { "Content-Type": "application/json", ...headers }; let jsonData = JSON.stringify(data); return this.gmRequest("POST", url, jsonData, null, headers); } postForm(url, data = {}, headers = {}) { headers || (headers = {}); headers["Content-Type"] || (headers["Content-Type"] = "application/x-www-form-urlencoded"); let body = ""; data && Object.keys(data).length > 0 && (body = Object.entries(data).map((([key, value]) => `${key}=${value}`)).join("&")); return this.gmRequest("POST", url, body, null, headers); } postFileFormData(url, data = {}, headers = {}) { headers || (headers = {}); const boundary = `----WebKitFormBoundary${Math.random().toString(36).substring(2)}`; headers["Content-Type"] = `multipart/form-data; boundary=${boundary}`; let body = ""; data && Object.keys(data).length > 0 && (body = Object.entries(data).map((([key, value]) => `--${boundary}\r Content-Disposition: form-data; name="${key}"\r \r ${value}\r `)).join("")); body += `--${boundary}--`; return this.gmRequest("POST", url, body, null, headers); } async downloadFileInChunks(url, headers = {}) { const httpTimeout = await storageManager.getSetting("httpTimeout", 5e3), httpRetryCount = await storageManager.getSetting("httpRetryCount", 3); clog.log("正在获取文件大小..."); let fileSize, mimeType; try { const sizeResponse = await utils.retry((() => new Promise(((resolve, reject) => { _GM_xmlhttpRequest({ method: "GET", url, headers: { ...headers, Range: "bytes=0-0" }, timeout: httpTimeout, onload: resolve, onerror: (e) => reject(new Error("网络错误:无法获取文件大小")), ontimeout: () => reject(new Error("超时:获取文件大小")) }); }))), httpRetryCount); if (206 !== sizeResponse.status && 200 !== sizeResponse.status) throw new Error(`请求文件大小失败,状态码: ${sizeResponse.status}`); { const rangeHeader = sizeResponse.responseHeaders.match(/content-range:\s*bytes\s*\d+-\d+\/(\d+)/i), typeHeader = sizeResponse.responseHeaders.match(/content-type:\s*([^\s;]+)/i); if (rangeHeader && rangeHeader[1]) fileSize = parseInt(rangeHeader[1], 10); else { if (!sizeResponse.responseHeaders.match(/content-length:\s*(\d+)/i) || 200 !== sizeResponse.status) throw new Error("无法从响应头中获取文件总大小,服务器可能不支持 Range 请求。"); { const lengthHeader = sizeResponse.responseHeaders.match(/content-length:\s*(\d+)/i); fileSize = parseInt(lengthHeader[1], 10); clog.warn("服务器返回 200 状态码,可能不支持 Range 请求。将尝试完整下载。"); } } typeHeader && typeHeader[1] && (mimeType = typeHeader[1]); clog.log(`文件总大小:${(fileSize / 1024 / 1024).toFixed(2)} MB, MIME 类型: ${mimeType || "未知"}`); } } catch (e) { clog.error("获取文件大小失败:", e.message); throw e; } if (!fileSize || fileSize <= 0) throw new Error("获取到的文件大小无效或服务器拒绝提供大小信息。"); const numChunks = Math.ceil(fileSize / 1048576), chunkPromises = [], downloadedChunks = new Array(numChunks); clog.log(`文件将被分为 ${numChunks} 块进行下载 (每块约 ${1.toFixed(2)} MB)`); for (let i = 0; i < numChunks; i++) { const start = 1048576 * i, rangeHeader = `bytes=${start}-${Math.min(start + 1048576 - 1, fileSize - 1)}`, chunkPromise = await utils.retry((() => new Promise(((resolve, reject) => { const currentHeaders = { ...headers, Range: rangeHeader, Accept: "application/octet-stream" }; _GM_xmlhttpRequest({ method: "GET", url, headers: currentHeaders, timeout: httpTimeout, responseType: "arraybuffer", onload: (response) => { if (206 === response.status || 200 === response.status) if (response.response instanceof ArrayBuffer) { downloadedChunks[i] = response.response; clog.log(`成功下载第 ${i + 1}/${numChunks} 块 (${rangeHeader})`); resolve(); } else reject(new Error(`第 ${i + 1} 块响应不是 ArrayBuffer。`)); else reject(new Error(`第 ${i + 1} 块请求失败,状态码: ${response.status}`)); }, onerror: (error) => reject(new Error(`第 ${i + 1} 块网络错误: ${error.error}`)), ontimeout: () => reject(new Error(`第 ${i + 1} 块超时。`)) }); }))), httpRetryCount); chunkPromises.push(chunkPromise); } try { await Promise.all(chunkPromises); clog.log("所有分块下载完成,开始合并..."); } catch (e) { clog.error("分块下载过程中发生错误:", e.message); throw e; } const finalBlob = new Blob(downloadedChunks); finalBlob.size !== fileSize && clog.warn(`警告:合并后的 Blob 大小 (${finalBlob.size}) 与预期文件大小 (${fileSize}) 不匹配!`); return await finalBlob.text(); } async gmRequest(method, url, data = {}, params = {}, headers = {}, noRedirect = false) { if (params && Object.keys(params).length) { const queryString = new URLSearchParams(params).toString(); url += (url.includes("?") ? "&" : "?") + queryString; } const httpTimeout = await storageManager.getSetting("httpTimeout", 5e3), httpRetryCount = await storageManager.getSetting("httpRetryCount", 3); data || (data = void 0); return await utils.retry((() => new Promise(((resolve, reject) => { _GM_xmlhttpRequest({ method, url, headers, timeout: httpTimeout, data, onload: (response) => { try { noRedirect && response.finalUrl !== url && reject("请求被重定向了,URL是:" + response.finalUrl); if (response.status >= 200 && response.status < 300) if (response.responseText) try { resolve(JSON.parse(response.responseText)); } catch (e) { resolve(response.responseText); } else resolve(response.responseText || response); else { clog.error("请求失败,状态码:", response.status, url); if (response.responseText) try { const errorData = JSON.parse(response.responseText); reject(errorData); } catch { reject(new Error(response.responseText || `请求发生错误 ${response.status}`)); } else reject(new Error(`请求发生错误 ${response.status}`)); } } catch (e) { reject(e); } }, onerror: (error) => { clog.error("网络错误:", url); reject(new Error(error.error || "网络错误")); }, ontimeout: () => { reject(new Error("请求超时: " + url)); } }); }))), httpRetryCount); } } class PluginManager { constructor() { this.plugins = /* @__PURE__ */ new Map(); } register(pluginClass) { if ("function" != typeof pluginClass) throw new Error("插件必须是一个类"); const instance = new pluginClass(); instance.pluginManager = this; const lowerName = instance.getName(); if (this.plugins.has(lowerName)) throw new Error(`插件"${name}"已注册`); this.plugins.set(lowerName, instance); } getBean(name2) { return this.plugins.get(name2); } async processCss() { const failedCssLoads = (await Promise.allSettled(Array.from(this.plugins).map((async ([name2, instance]) => { try { if ("function" == typeof instance.initCss) { const css = await instance.initCss(); css && utils.insertStyle(css); return { name: name2, status: "fulfilled" }; } return { name: name2, status: "skipped" }; } catch (e) { console.error(`插件 ${name2} 加载 CSS 失败`, e); return { name: name2, status: "rejected", error: e }; } })))).filter(((r) => "rejected" === r.status)); failedCssLoads.length && console.error("以下插件的 CSS 加载失败:", failedCssLoads.map(((p) => p.value.name))); } async processPlugins() { const failedPlugins = (await Promise.allSettled(Array.from(this.plugins).map((async ([name2, instance]) => { try { if ("function" == typeof instance.handle) { await instance.handle(); return { name: name2, status: "fulfilled" }; } } catch (e) { clog.error(`插件 ${name2} 执行失败`, e); return { name: name2, status: "rejected", error: e }; } })))).filter(((r) => "rejected" === r.status)); failedPlugins.length && console.error("以下插件执行失败:", failedPlugins.map(((p) => p.value.name))); } } class BasePlugin { constructor() { __publicField2(this, "pluginManager", null); __publicField2(this, "settingSvg", ''); __publicField2(this, "editSvg", ''); __publicField2(this, "deleteSvg", ''); __publicField2(this, "checkSvg", ''); __publicField2(this, "actressSvg", ''); __publicField2(this, "newSvg", ''); __publicField2(this, "refreshSvg", ''); __publicField2(this, "blacklistSvg", ''); __publicField2(this, "removeSvg", ''); __publicField2(this, "copySvg", ''); __publicField2(this, "titleSvg", ''); __publicField2(this, "carNumSvg", ''); __publicField2(this, "downSvg", ''); __publicField2(this, "handleSvg", ''); __publicField2(this, "siteSvg", ''); __publicField2(this, "videoSvg", ''); __publicField2(this, "screenSvg", ''); __publicField2(this, "recoveryVideoSvg", ''); __publicField2(this, "markDeleteSvg", ''); } getName() { throw new Error(`${this.constructor.name} 未显示getName()`); } getBean(name2) { return this.pluginManager.getBean(name2); } async initCss() { return ""; } async handle() { } getPageInfo() { let carNum2, url, actress, actors, publishTime, currentHref2 = window.location.href; if (isJavDb) { carNum2 = $('a[title="複製番號"]').attr("data-clipboard-text"); url = currentHref2.split("?")[0].split("#")[0]; actress = $(".female").prev().map(((i, el) => $(el).text())).get().join(" "); actors = $(".male").prev().map(((i, el) => $(el).text())).get().join(" "); publishTime = $('strong:contains("日期:")').parent(".panel-block").find(".value").text().trim(); } if (isJavBus) { url = currentHref2.split("?")[0]; carNum2 = url.split("/").filter(Boolean).pop().replace(/_\d{4}-\d{2}-\d{2}$/, ""); actress = $('span[onmouseover*="star_"] a').map(((i, el) => $(el).text())).get().join(" "); actors = ""; publishTime = $('span.header:contains("發行日期:")').parent("p").text().trim().replace("發行日期:", "").trim(); } return { carNum: carNum2, url, actress, actors, publishTime }; } getActressId() { const match = currentHref.match(/\/actors\/([^/?]+)/); return match && match.length > 1 ? match[1] : null; } getActressPageInfo() { let currentHref2 = window.location.href; if (!currentHref2.includes("/actors/") && !currentHref2.includes("/star/")) throw new Error("接口调用错误, 非演员详情页"); let nameList = [], $actor = isJavDb ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10"); $actor.length && $actor.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let $sectionMeta = $(".section-meta:not(:contains('影片'))"); $sectionMeta.length && $sectionMeta.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let role = $(".section-meta:contains('男優')").length > 0 ? "actor" : "actress", movieType = "censored"; nameList.some(((n) => n.includes("無碼"))) && (movieType = "uncensored"); currentHref2.includes("uncensored") && (movieType = "uncensored"); let blacklistUrl = null, starId = null; const urlObj = new URL(currentHref2); if (isJavDb) { starId = urlObj.pathname.split("/").filter(((segment) => "" !== segment.trim())).pop(); const params = urlObj.searchParams; params.delete("sort_type"); params.delete("page"); blacklistUrl = urlObj.toString(); } else if (isJavBus) { const splitStr = "/star/", parts = currentHref2.split(splitStr); if (parts.length < 2) throw new Error("提取演员url失败"); const host = parts[0]; starId = parts[1].split("/")[0]; blacklistUrl = host + splitStr + starId; } return { starId, name: nameList[0], allName: nameList, role, movieType, blacklistUrl }; } getSelector(type) { const key = type || (isJavDb ? "javdb" : isJavBus ? "javbus" : null), selectors = { javdb: { boxSelector: ".movie-list", itemSelector: ".movie-list .item", coverImgSelector: ".cover img", requestDomItemSelector: ".movie-list .item", nextPageSelector: ".pagination-next" }, javbus: { boxSelector: ".masonry", itemSelector: ".masonry .item", coverImgSelector: ".masonry .movie-box .photo-frame img", requestDomItemSelector: "#waterfall .item", nextPageSelector: "#next" } }; if (!key || !selectors[key]) throw new Error("类型错误: 无法确定选择器类型 (JavDb 或 JavBus)"); return selectors[key]; } parseMovieId(href) { return href.split("/").pop().split(/[?#]/)[0]; } getBoxCarInfo($box2) { var _a2, _b, _c; const $aLink = $box2.find("a"), url = $aLink.attr("href"); let carNum2 = null, title = null, publishTime = null; const $videoTitleEle = $box2.find(".video-title"); if ($videoTitleEle.length > 0) { const $strongEle = $videoTitleEle.find("strong"); $strongEle.length > 0 && (carNum2 = $strongEle.text().trim()); title = null == (_a2 = $aLink.attr("title")) ? void 0 : _a2.trim(); if (!title) { const fullTitleText = $videoTitleEle.text().trim(); title = carNum2 && fullTitleText.includes(carNum2) ? fullTitleText.replace(carNum2, "").trim() : fullTitleText; } publishTime = $box2.find(".meta").text().trim(); } if (!carNum2) { const $imgEle = $box2.find("img"); $imgEle.length > 0 && (title = (null == (_b = $imgEle.attr("title")) ? void 0 : _b.trim()) || (null == (_c = $imgEle.attr("data-title")) ? void 0 : _c.trim())); const dateElements = $box2.find("date").map(((i, el) => $(el).text().trim())).get(), isDate = (s) => /^\d{4}-\d{1,2}-\d{1,2}$/.test(s); publishTime = dateElements.find(isDate) || null; carNum2 = dateElements.find(((i) => !isDate(i))) || null; } if (!carNum2) { const msg = "提取番号信息失败: carNum 为空"; console.error("Error in getBoxCarInfo:", msg, "Box Element:", $box2.get(0)); show.error(msg); throw new Error(msg); } return { carNum: carNum2, url: url || "", aHref: url || "", title: title || "", publishTime: publishTime || "" }; } getBoxCarInfoList($boxList = null) { $boxList || ($boxList = $(this.getSelector().itemSelector)); if (0 === $boxList.length) { clog.error("获取当前列表页所有item的番号信息失败!"); return []; } const results = []; $boxList.each(((index, element) => { const $box2 = $(element); try { const carInfo = this.getBoxCarInfo($box2); results.push(carInfo); } catch (error) { clog.error("[getBoxCarInfoList] 提取单个 boxCar 信息失败:", error.message, "元素索引:", index); } })); return results; } checkDuplicateCarNumbers(currentList, newList) { if (!isJavDb) return false; if (!currentList || 0 === currentList.length || !newList || 0 === newList.length) return false; const currentCarNums = new Set(currentList.map(((item) => item.carNum)).filter(((n) => n))); if (0 === currentCarNums.size) return false; let consecutiveDuplicates = 0; for (let i = 0; i < newList.length; i++) { const newCarNum = newList[i] ? newList[i].carNum : null; if (newCarNum && currentCarNums.has(newCarNum)) { consecutiveDuplicates++; if (consecutiveDuplicates >= 2) { clog.warn("警告: 检测到连续番号信息重复, 该类别可能已被限制页码。"); return true; } } else consecutiveDuplicates = 0; } return false; } } const DMM_GRAPHQL_URL = "https://api.video.dmm.co.jp/graphql"; const dmmGraphQLKeyword = (code) => { const match = String(code || "").trim().toUpperCase().match(/^([A-Z0-9]{2,10})-(\d{1,6})$/); if (!match) return ""; return `${match[1].toLowerCase()}${match[2].padStart(5, "0")}`; }; const dmmGraphQLRequest = (query) => { return new Promise((resolve, reject) => { _GM_xmlhttpRequest({ method: "POST", url: DMM_GRAPHQL_URL, data: JSON.stringify({query}), timeout: 12e3, headers: { Accept: "application/json", "Content-Type": "application/json", Referer: "https://video.dmm.co.jp/", "Fanza-Device": "BROWSER", "Cache-Control": "no-cache" }, onload: (response) => { if (response.status < 200 || response.status >= 400) { reject(new Error(`DMM GraphQL HTTP ${response.status || 0}`)); return; } try { const json = JSON.parse(response.responseText || "{}"); if (Array.isArray(json.errors) && json.errors.length) { reject(new Error(json.errors[0].message || "DMM GraphQL error")); return; } resolve(json.data || {}); } catch (e) { reject(new Error(`DMM GraphQL JSON parse error: ${e.message}`)); } }, onerror: () => reject(new Error("DMM GraphQL network error")), ontimeout: () => reject(new Error("DMM GraphQL timeout")) }); }); }; const searchDmmContentIdsByGraphQL = async (code) => { const keyword = dmmGraphQLKeyword(code); if (!keyword) return []; const carNumNoHyphen = String(code || "").replace(/-/g, "").toLowerCase(); const results = []; const searchKeywords = [.../* @__PURE__ */ new Set([keyword, `${keyword}#`])]; for (const searchKeyword of searchKeywords) { const query = `{ legacySearchPPV(limit: 10, offset: 0, sort: SALES_RANK_SCORE, floor: AV, queryWord: ${JSON.stringify(searchKeyword)}) { result { contents { contentId serviceCode floorCode } } } }`; const data = await dmmGraphQLRequest(query); const contents = Array.isArray(data && data.legacySearchPPV && data.legacySearchPPV.result && data.legacySearchPPV.result.contents) ? data.legacySearchPPV.result.contents : []; for (const item of contents) { const contentId = String(item.contentId || ""); if (contentId.replace(/[^a-z0-9]/gi, "").toLowerCase().includes(carNumNoHyphen)) { results.push({ serviceCode: item.serviceCode || "digital", floorCode: item.floorCode || "AV", contentId }); } } if (results.length > 0) break; } return results; }; const getDmmGraphQLVideoUrls = async (code) => { const contentItems = await searchDmmContentIdsByGraphQL(code); if (!contentItems.length) return null; const results = await Promise.allSettled( contentItems.map((item) => extractTrailerLinks(item)) ); for (const result of results) { if (result.status === "fulfilled" && result.value) { return result.value; } } return null; }; const extractTrailerLinks = (item) => { const {contentId, serviceCode, floorCode} = item; return new Promise((resolve, reject) => { const trailerPageUrl = `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${contentId}/mtype=AhRVShI_/service=${serviceCode}/floor=${floorCode}/mode=/`; _GM_xmlhttpRequest({ method: "GET", url: trailerPageUrl, timeout: 15e3, headers: { "accept-language": "ja-JP,ja;q=0.9", Cookie: "age_check_done=1" }, onload: (response) => { const htmlContent = response.responseText || ""; if (htmlContent.includes("このサービスはお住まいの地域からは")) { reject(new Error("节点不可用,请将DMM域名分流到日本ip")); return; } const match = htmlContent.match(/const\s+args\s+=\s+(.*);/); if (!match) { reject(new Error("未在脚本中找到 const args = ... 变量")); return; } let bitrates; try { bitrates = JSON.parse(match[1]).bitrates; } catch (e) { reject(new Error(`解析播放器脚本 JSON 失败: ${e.message}`)); return; } if (!Array.isArray(bitrates)) { reject(new Error("解析画质链接失败: bitrates 字段不是一个数组或不存在")); return; } const qualityKeys = [ "4ks", "4k", "hhbs", "hhb", "hmb", "mhb", "mmb", "dmb_w", "dmb_s", "dm_s", "sm_s", "mhb_w" ]; const qualityNameRegex = new RegExp(`(${qualityKeys.join("|")})\\.mp4$`); const finalQualityMap = {}; for (const bitrate of bitrates) { const url = bitrate && bitrate.src; if (!url || typeof url !== "string" || !url.endsWith(".mp4")) continue; const qualityMatch = url.match(qualityNameRegex); let qualityKey = ""; if (qualityMatch && qualityMatch[1]) qualityKey = qualityMatch[1]; if (qualityKey && !finalQualityMap[qualityKey]) { finalQualityMap[qualityKey] = url; } } if (Object.keys(finalQualityMap).length === 0) { reject(new Error("未找到匹配要求的预览画质视频")); return; } resolve(finalQualityMap); }, onerror: () => reject(new Error("DMM 播放页请求网络错误")), ontimeout: () => reject(new Error("DMM 播放页请求超时")) }); }); }; const selectDefaultQuality = (dmmVideoQualityList, intendedDefault) => { if (!dmmVideoQualityList || 0 === dmmVideoQualityList.length) return null; const availableSet = new Set(dmmVideoQualityList); if (availableSet.has(intendedDefault)) return intendedDefault; const priorityOrder = qualityOptions.map(((option) => option.quality)).reverse(); for (const quality of priorityOrder) if (availableSet.has(quality)) return quality; return dmmVideoQualityList[0]; }, CACHE_KEY = "jhs_dmm_video"; class DmmVideoFetcher { constructor(carNum2, showErrorMessages = true) { this.carNum = carNum2; this.showErrorMessages = showErrorMessages; } _checkCache() { const cachedData = localStorage.getItem(CACHE_KEY) ? JSON.parse(localStorage.getItem(CACHE_KEY)) : {}; if (cachedData[this.carNum]) { clog.debug("缓存中存在预览视频信息", cachedData[this.carNum]); return cachedData[this.carNum]; } return null; } _updateCache(videoMap) { const cachedData = localStorage.getItem(CACHE_KEY) ? JSON.parse(localStorage.getItem(CACHE_KEY)) : {}; cachedData[this.carNum] = videoMap; clog.debug("成功解析出预览视频并已缓存:", videoMap); localStorage.setItem(CACHE_KEY, JSON.stringify(cachedData)); } async _searchContentIds() { const carNum2 = this.carNum, carNumNoHyphen = carNum2.replace(/-/g, ""), keywordAttempts = [{ keyword: carNum2.replace("-", "00"), name: "00-替换关键词" }, { keyword: carNum2, name: "原始番号关键词" }, { keyword: carNumNoHyphen, name: "无连字符关键词" }], carNumLower = carNum2.toLowerCase(); for (const attempt of keywordAttempts) { const {keyword, name: name2} = attempt, currentTempCarNumLower = keyword.toLowerCase(), apiUrl2 = `https://api.dmm.com/affiliate/v3/ItemList?${new URLSearchParams({ api_id: "UrwskPfkqQ0DuVry2gYL", affiliate_id: "10278-996", output: "json", site: "FANZA", sort: "match", keyword }).toString()}`; let response; try { response = await gmHttp.get(apiUrl2); } catch (e) { clog.error(`API 请求失败,跳过 ${name2}:`, e); continue; } if (!response || !response.result || !response.result.result_count) { clog.debug(`使用 ${name2} (${keyword}) 进行 API 搜索 返回无结果,尝试下一个关键词。`); continue; } const newItems = []; for (const item of response.result.items) { if (newItems.length >= 2) break; const contentId = item.content_id || "", makerProduct = item.maker_product || ""; if (contentId.includes(currentTempCarNumLower.replace("-", "")) || carNumLower === makerProduct.toLowerCase() || contentId.includes(carNumNoHyphen.toLowerCase())) { newItems.push({ serviceCode: item.service_code, floorCode: item.floor_code, contentId, pageUrl: item.URL }); clog.debug(`[${name2}] cid|makerProduct 匹配成功:`, contentId, makerProduct); } } if (newItems.length > 0) { clog.debug(`--- 成功通过 ${name2} 找到 Content IDs ---`); const $btn2 = $("#fanzaBtn"); let url = `https://www.dmm.co.jp/search/=/searchstr=${keyword}`, type = "single"; if (newItems.length > 1) { $btn2.attr("href", url); $btn2.append('多结果'); $btn2.css("backgroundColor", "#7bc73b"); type = "multiple"; } else { url = newItems[0].pageUrl; $btn2.attr("href", url); $btn2.css("backgroundColor", "#7bc73b"); } const dmmCacheKey = "jhs_other_site_dmm", dmmCacheData = localStorage.getItem(dmmCacheKey) ? JSON.parse(localStorage.getItem(dmmCacheKey)) : {}; dmmCacheData[this.carNum] = { type, url }; localStorage.setItem(dmmCacheKey, JSON.stringify(dmmCacheData)); return newItems; } clog.debug(`[${name2}] API 返回结果数 ${response.result.result_count},但无精确匹配的 Content ID。`); } clog.warn("所有关键词尝试均未找到匹配的Content ID, 解析Dmm视频失败"); const $btn = $("#fanzaBtn"); $btn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", "#de3333"); return null; } async _extractTrailerLinks({contentId, serviceCode, floorCode}) { const trailerPageUrl = `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${contentId}/mtype=AhRVShI_/service=${serviceCode}/floor=${floorCode}/mode=/`, htmlContent = await gmHttp.get(trailerPageUrl, null, { "accept-language": "ja-JP,ja;q=0.9", Cookie: "age_check_done=1" }); if ("string" != typeof htmlContent) { clog.error(htmlContent); throw new Error("解析播放页内容失败, 非文本内容"); } if (htmlContent.includes("このサービスはお住まいの地域からは")) throw new Error("节点不可用,请将DMM域名分流到日本ip"); const match = htmlContent.match(/const\s+args\s+=\s+(.*);/); if (!match) throw new Error("未在脚本中找到 const args = ... 变量"); let bitrates; try { ({bitrates} = JSON.parse(match[1])); } catch (e) { throw new Error(`解析播放器脚本 JSON 失败: ${e.message}`); } const finalQualityMap = {}, qualityKeys = qualityOptions.map(((o) => o.quality)).join("|"), qualityNameRegex = new RegExp(`(${qualityKeys})\\.mp4$`); if (!Array.isArray(bitrates)) { clog.error("解析画质链接失败: bitrates 字段不是一个数组或不存在"); throw new Error("解析画质链接失败: bitrates 字段不是一个数组或不存在"); } clog.debug("原始数据返回:", bitrates); for (const item of bitrates) { const url = null == item ? void 0 : item.src; if (!url || "string" != typeof url || !url.endsWith(".mp4")) continue; const qualityMatch = url.match(qualityNameRegex); let qualityKey = ""; qualityMatch && qualityMatch[1] && (qualityKey = qualityMatch[1]); qualityKey && !finalQualityMap[qualityKey] && (finalQualityMap[qualityKey] = url); } if (0 === Object.keys(finalQualityMap).length) throw new Error("未找到匹配要求的预览画质视频"); return finalQualityMap; } async _searchViaGraphQL() { clog.debug("尝试通过 GraphQL API 搜索视频..."); const videoMap = await getDmmGraphQLVideoUrls(this.carNum); if (videoMap && Object.keys(videoMap).length > 0) { this._updateCache(videoMap); clog.debug("GraphQL 成功解析出预览视频:", videoMap); const dmmCacheKey = "jhs_other_site_dmm"; const dmmCacheData = localStorage.getItem(dmmCacheKey) ? JSON.parse(localStorage.getItem(dmmCacheKey)) : {}; dmmCacheData[this.carNum] = { type: "graphql", url: `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}` }; localStorage.setItem(dmmCacheKey, JSON.stringify(dmmCacheData)); return videoMap; } return null; } async fetchVideo() { const cachedResult = this._checkCache(); if (cachedResult) return cachedResult; let contentItems; try { const testCarNum = this.carNum.toLowerCase(); if (testCarNum.startsWith("heyzo") || /^(n\d+|\d+(-\d+)*)$/.test(testCarNum) || /^n\d+$/.test(testCarNum)) throw new Error("无码番号类型, 取消dmm解析"); if (this.carNum.includes("VR-")) throw new Error("VR类型, 取消dmm解析"); contentItems = await this._searchContentIds(); } catch (e) { clog.error("DMM API 搜索失败:", e); } if (!contentItems || 0 === contentItems.length) { clog.warn("Affiliate API 未找到视频,尝试 GraphQL API..."); const graphQLResult = await this._searchViaGraphQL(); if (graphQLResult) return graphQLResult; const $btn = $("#fanzaBtn"); $btn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", "#de3333"); return null; } try { const finalVideoMap = await Promise.any(contentItems.map(((item) => this._extractTrailerLinks(item)))); this._updateCache(finalVideoMap); return finalVideoMap; } catch (error) { const errors = error.errors || [error]; clog.error(`Affiliate 解析失败: ${errors[0] && errors[0].message || errors[0]}`, errors); clog.warn("尝试 GraphQL API 作为 fallback..."); const graphQLResult = await this._searchViaGraphQL(); if (graphQLResult) return graphQLResult; if (errors.some(((err) => err.message.includes("节点不可用")))) this.showErrorMessages && show.error("节点不可用,请将DMM域名分流到日本ip"); else { const displayError = errors[0].message || errors[0]; this.showErrorMessages && show.error(`解析失败: ${displayError}`); } const $btn = $("#fanzaBtn"); $btn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", "#de3333"); return null; } } } const getDmmVideo = async (carNum2, showErrorMessages = true) => new DmmVideoFetcher(carNum2, showErrorMessages).fetchVideo(); class AutoPagePlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "preloadDistance", 500); __publicField2(this, "currentPage", this.getInitialPageNumber()); __publicField2(this, "pageItems", []); } getName() { return "AutoPagePlugin"; } async initCss() { return "\n \n "; } async handle() { this.waterfall().then(); } getInitialPageNumber() { if (isJavBus$1) { const match = currentHref$1.match(/\/(page|star\/[^/]+)\/(\d+)/); return match ? parseInt(match[2], 10) : 1; } if (isJavDb$1) { const match = currentHref$1.match(/[?&]page=(\d+)/); return match ? parseInt(match[1], 10) : 1; } return 1; } async waterfall() { if (await this.shouldDisablePaging()) return; const selector = this.getSelector(); this.container = document.querySelector(selector.boxSelector); if (!this.container) { console.error("没有找到容器节点,停止瀑布流!"); return; } this.loader = document.createElement("div"); this.loader.className = "jhs-scroll"; this.container.parentNode.insertBefore(this.loader, this.container.nextSibling); this.pageItems.push({ page: this.currentPage, top: 0, url: window.location.href }); this.loader.addEventListener("click", (() => { this.loader.classList.contains("waterfall-error") && this.loadNextPage().then(); })); window.addEventListener("scroll", (() => { this.checkLoad(); this.checkScrollPosition(); })); const nextLink = document.querySelector(selector.nextPageSelector); this.nextUrl = null == nextLink ? void 0 : nextLink.href; this.hasMore = !!this.nextUrl; setTimeout((() => { this.checkLoad(); }), 1e3); this.hasMore || this.setState("waterfall-no-more", "已经到底了"); } async loadNextPage() { var _a2; if (await storageManager.getSetting("autoPage", YES$1) === NO) { this.setState("waterfall-loading", ""); return; } if (this.isLoading || !this.nextUrl) return; this.isLoading = true; this.setState("waterfall-loading", "加载中..."); const selector = this.getSelector(); try { const pageNum = utils.getUrlParam(this.nextUrl, "page"); let maxPage = 60; currentHref$1.includes("c11") && (maxPage = 30); if (isJavDb$1 && pageNum > maxPage || currentHref$1.includes("month")) { const beyond60Plugin = this.getBean("Beyond60Plugin"); if (beyond60Plugin) { const {html: html2, nextUrl, hasMore} = await beyond60Plugin.handleBeyond60(this.nextUrl); if (html2) { const pageTop2 = this.container.scrollHeight; this.pageItems.push({ page: this.currentPage + 1, top: pageTop2, url: this.nextUrl }); $(".movie-list").append(html2); } this.hasMore = hasMore; this.nextUrl = nextUrl; const $ul = beyond60Plugin.createPagination(pageNum, hasMore); $(".pagination").html($ul); this.setState("waterfall-loading", ""); this.hasMore || this.setState("waterfall-no-more", "已经到底了"); return; } } const html = await gmHttp.get(this.nextUrl); clog.log("请求下一页内容:", this.nextUrl); const $dom = utils.htmlTo$dom(html); isJavBus$1 && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); let itemList = $dom.find(this.getSelector().requestDomItemSelector); const currentBoxCarInfoList = this.getBoxCarInfoList(), nextPageBoxCarInfoList = this.getBoxCarInfoList(itemList); if (this.checkDuplicateCarNumbers(currentBoxCarInfoList, nextPageBoxCarInfoList)) { this.nextUrl = null; this.hasMore = false; this.setState("waterfall-error", "翻页内容出现重复数据, 可能首页已更新了新视频 或 页码受JavDB限制, 停止瀑布流"); return; } const pageTop = this.container.scrollHeight; this.pageItems.push({ page: this.currentPage + 1, top: pageTop, url: this.nextUrl }); const listPagePlugin = this.getBean("ListPagePlugin"); let coverImgNodeList = $dom.find(this.getSelector().coverImgSelector); listPagePlugin.replaceHdImg(coverImgNodeList); $(this.getSelector().boxSelector).append(itemList); this.nextUrl = null == (_a2 = $dom.find(selector.nextPageSelector)) ? void 0 : _a2.attr("href"); this.hasMore = !!this.nextUrl; let pagination = $dom.find(".pagination"); $(".pagination").replaceWith(pagination); this.setState("waterfall-loading", ""); this.hasMore || this.setState("waterfall-no-more", "已经到底了"); } catch (e) { clog.error("加载失败:", e); this.setState("waterfall-error", "加载失败,点击重试"); } finally { this.isLoading = false; } } checkScrollPosition() { const scrollPosition = window.scrollY; for (let i = this.pageItems.length - 1; i >= 0; i--) { const page = this.pageItems[i]; if (scrollPosition >= page.top) { if (this.currentPage !== page.page) { this.currentPage = page.page; this.updatePageUrl(page.url); } break; } } } checkLoad() { if (!this.loader) return; this.loader.getBoundingClientRect().top < window.innerHeight + this.preloadDistance && this.loadNextPage().then(); } async shouldDisablePaging() { if (!window.isListPage) return true; await storageManager.getSetting("autoPage", YES$1); return ["search?q", "handlePlayback=1", "handleTop=1", "/want_watch_videos", "/watched_videos", "/advanced_search?type=100"].some(((path) => currentHref$1.includes(path))); } updatePageUrl_old(href) { window.history.pushState({}, "", href); if (isJavBus$1) { const match = href.match(/\/(page|star\/.*?)\/(\d+)/), pageNumber = match ? parseInt(match[2], 10) : null; document.title = document.title.replace(/第\d+頁/, "第" + pageNumber + "頁"); } } updatePageUrl(url) { window.history.replaceState({}, "", url); isJavBus$1 && (document.title = document.title.replace(/第\d+頁/, `第${this.currentPage}頁`)); } setState(state, text) { this.loader.className = `jhs-scroll ${state}`; this.loader.textContent = text; } } class AliyunApi { constructor(refresh_token) { this.baseApiUrl = "https://api.aliyundrive.com"; this.refresh_token = refresh_token; this.authorization = null; this.default_drive_id = null; this.backupFolderId = null; } async getDefaultDriveId() { if (this.default_drive_id) return this.default_drive_id; this.userInfo = await this.getUserInfo(); this.default_drive_id = this.userInfo.default_drive_id; return this.default_drive_id; } async getHeaders() { if (this.authorization) return { authorization: this.authorization }; this.authorization = await this.getAuthorization(); return { authorization: this.authorization }; } async getAuthorization() { let url = this.baseApiUrl + "/v2/account/token", data = { refresh_token: this.refresh_token, grant_type: "refresh_token" }; try { return "Bearer " + (await gmHttp.post(url, data)).access_token; } catch (e) { throw e.message.includes("is not valid") ? new Error("refresh_token无效, 请重新填写并保存") : e; } } async getUserInfo() { const headers = await this.getHeaders(); let url = this.baseApiUrl + "/v2/user/get"; return await gmHttp.post(url, {}, headers); } async deleteFile(file_id, drive_id = null) { if (!file_id) throw new Error("未传入file_id"); drive_id || (drive_id = await this.getDefaultDriveId()); let data = { file_id, drive_id }, url = this.baseApiUrl + "/v2/recyclebin/trash"; const headers = await this.getHeaders(); await gmHttp.post(url, data, headers); return {}; } async createFolder(name2, drive_id = null, parent_folder_id = "root") { drive_id || (drive_id = await this.getDefaultDriveId()); let url = this.baseApiUrl + "/adrive/v2/file/createWithFolders", data = { name: name2, type: "folder", parent_file_id: parent_folder_id, check_name_mode: "auto_rename", content_hash_name: "sha1", drive_id }; const headers = await this.getHeaders(); return await gmHttp.post(url, data, headers); } async getFileList(parent_folder_id = "root", drive_id = null) { drive_id || (drive_id = await this.getDefaultDriveId()); let url = this.baseApiUrl + "/adrive/v3/file/list"; const data = { drive_id, parent_file_id: parent_folder_id, limit: 200, all: false, url_expire_sec: 14400, image_thumbnail_process: "image/resize,w_256/format,avif", image_url_process: "image/resize,w_1920/format,avif", video_thumbnail_process: "video/snapshot,t_120000,f_jpg,m_lfit,w_256,ar_auto,m_fast", fields: "*", order_by: "updated_at", order_direction: "DESC" }, headers = await this.getHeaders(); return (await gmHttp.post(url, data, headers)).items; } async uploadFile(folder_id, fileName, uploadContent, drive_id = null) { show.info("请求存储空间中..."); let createFileUrl = this.baseApiUrl + "/adrive/v2/file/createWithFolders"; drive_id || (drive_id = await this.getDefaultDriveId()); let data = { drive_id, part_info_list: [{ part_number: 1 }], parent_file_id: folder_id, name: fileName, type: "file", check_name_mode: "auto_rename" }; const headers = await this.getHeaders(), createFileResult = await gmHttp.post(createFileUrl, data, headers), upload_id = createFileResult.upload_id, upload_file_id = createFileResult.file_id, upload_url = createFileResult.part_info_list[0].upload_url; show.info("开始上传文件..."); await this._doUpload(upload_url, uploadContent); await gmHttp.post("https://api.aliyundrive.com/v2/file/complete", data = { drive_id, file_id: upload_file_id, upload_id }, headers); } _doUpload(upload_url, uploadContent) { return new Promise(((resolve, reject) => { $.ajax({ type: "PUT", url: upload_url, data: uploadContent, contentType: " ", processData: false, success: (res, status, xhr) => { 200 === xhr.status ? resolve({}) : reject(xhr); }, error: (xhr) => { clog.error("上传失败", xhr.responseText); reject(xhr); } }); })); } async getDownloadUrl(file_id, drive_id = null) { drive_id || (drive_id = await this.getDefaultDriveId()); let url = this.baseApiUrl + "/v2/file/get_download_url"; const headers = await this.getHeaders(); let data = { file_id, drive_id }; return (await gmHttp.post(url, data, headers)).url; } async _createBackupFolder(folderName) { const fileList = await this.getFileList(); let folderObj = null; for (let i = 0; i < fileList.length; i++) { let file = fileList[i]; if (file.name === folderName) { folderObj = file; break; } } if (!folderObj) { show.info("不存在备份目录, 进行创建"); folderObj = await this.createFolder(folderName); } this.backupFolderId = folderObj.file_id; } async backup(folderName, fileName, uploadContent) { if (this.backupFolderId) await this.uploadFile(this.backupFolderId, fileName, uploadContent); else { await this._createBackupFolder(folderName); await this.uploadFile(this.backupFolderId, fileName, uploadContent); } } async getBackupList(folderName) { let dataList; if (this.backupFolderId) dataList = await this.getFileList(this.backupFolderId); else { await this._createBackupFolder(folderName); dataList = await this.getFileList(this.backupFolderId); } const fileList = []; dataList.forEach(((data) => { fileList.push({ name: data.name, fileId: data.file_id, createTime: data.created_at, size: data.size }); })); return fileList; } } class WebDavApi { constructor(davUrl, username, password) { this.davUrl = davUrl.endsWith("/") ? davUrl : davUrl + "/"; this.username = username; this.password = password; this.folderName = null; } _getAuthHeaders() { return { Authorization: `Basic ${btoa(`${this.username}:${this.password}`)}`, Depth: "1" }; } _sendRequest(method, path, headers = {}, data) { return new Promise(((resolve, reject) => { const url = this.davUrl + path, allHeaders = { ...this._getAuthHeaders(), ...headers }; _GM_xmlhttpRequest({ method, url, headers: allHeaders, data, onload: (response) => { if (response.status >= 200 && response.status < 300) resolve(response); else { console.error(response); reject(new Error(`请求失败 ${response.status}: ${response.statusText}`)); } }, onerror: (response) => { console.error("请求WebDav发生错误:", response); reject(new Error("请求WebDav失败, 请检查服务是否启动, 凭证是否正确")); } }); })); } async backup(folderName, fileName, uploadContent) { await this._sendRequest("MKCOL", folderName); const path = folderName + "/" + fileName; await this._sendRequest("PUT", path, { "Content-Type": "text/plain" }, uploadContent); } async getFileList(folderName) { var _a2, _b, _c; const xmlResponse = (await this._sendRequest("PROPFIND", folderName, { "Content-Type": "application/xml" }, '\n \n \n \n \n \n \n \n \n \n ')).responseText, items = new DOMParser().parseFromString(xmlResponse, "text/xml").getElementsByTagNameNS("DAV:", "response"), fileList = []; for (let i = 0; i < items.length; i++) { if (0 === i) continue; let item = items[i]; console.log(item); const name2 = item.getElementsByTagNameNS("DAV:", "displayname")[0].textContent, size = (null == (_a2 = item.getElementsByTagNameNS("DAV:", "getcontentlength")[0]) ? void 0 : _a2.textContent) || "0", createTime = (null == (_b = item.getElementsByTagNameNS("DAV:", "creationdate")[0]) ? void 0 : _b.textContent) || (null == (_c = item.getElementsByTagNameNS("DAV:", "getlastmodified")[0]) ? void 0 : _c.textContent) || ""; "0" !== size && fileList.push({ fileId: name2, name: name2, size: Number(size), createTime }); } fileList.reverse(); return fileList; } async deleteFile(fileId) { let path = this.folderName + "/" + encodeURI(fileId); await this._sendRequest("DELETE", path, { "Cache-Control": "no-cache" }); } async getBackupList(folderName) { this.folderName = folderName; await this._sendRequest("MKCOL", folderName); return this.getFileList(folderName); } async getFileContent(filePath) { let path = this.folderName + "/" + filePath; return (await this._sendRequest("GET", path, { Accept: "application/octet-stream" })).responseText; } } const SALT = "x7k9p3"; function simpleEncrypt(str) { return (SALT + str + SALT).split("").map(((char) => { const code = char.codePointAt(0); return String.fromCodePoint(code + 5); })).join(""); } class SettingPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "folderName", "JHS-数据备份"); __publicField2(this, "cacheItems", [{ key: "jhs_dmm_video", text: "🎥 预览视频缓存", title: "预览视频缓存" }, { key: "jhs_other_site", text: "🌍 第三方站点缓存", title: "第三方站点资源检测结果, 如missav,123Av等" }, { key: "jhs_screenShot", text: "🖼️ 缩略图缓存", title: "缩略图缓存" }, { key: "jhs_translate", text: "🆎 标题翻译", title: "标题翻译" }, { key: "jhs_actress_info", text: "👩 演员信息", title: "演员的年龄三围等数据信息" }, { key: "jhs_score_info", text: "⭐ Top250|热播 评分数据", title: "Top250及热播的评分数据" }]); } getName() { return "SettingPlugin"; } async initCss() { const settingObj = await storageManager.getSetting(); let containerWidth = (null == settingObj ? void 0 : settingObj.containerWidth) ?? "100", containerColumns = utils.isMobile() && window.innerWidth < 1e3 ? 1 : (null == settingObj ? void 0 : settingObj.containerColumns) ?? 5; this.applyImageMode().then(); let containerWidthCss = ` section .container{ max-width: 1000px !important; min-width: ${containerWidth}%; } .movie-list, .movie-list.v{ grid-template-columns: repeat(${containerColumns}, minmax(0, 1fr)); } `; isJavBus$1 && (containerWidthCss = ` .container-fluid .row{ max-width: 1000px !important; min-width: ${containerWidth}%; margin: auto auto; } .container { max-width: 1000px !important; min-width: 80%; margin: auto auto; } .masonry { grid-template-columns: repeat(${containerColumns}, minmax(0, 1fr)); } `); return ` `; } async handle() { await storageManager.getSetting("enableClog", YES$1) === YES$1 && clog.show(); if (isJavDb$1) { let handleResize2 = function () { if ($(".navbar-search").is(":hidden")) { $(".mini-setting-box").hide(); $(".setting-box").show(); } else { $(".mini-setting-box").show(); $(".setting-box").hide(); } }; $("#navbar-menu-user .navbar-end").prepend(''); utils.loopDetector((() => $("#miniHistoryBtn").length > 0), (() => { $(".miniHistoryBtnBox").before('\n \n '); handleResize2(); })); $(window).resize(handleResize2); } if (isJavBus$1) { utils.loopDetector((() => $("#waitCheckBtn").length), (() => { $("#waitCheckBtn").parent().append('\n
\n
\n \n 设置\n \n
\n
\n
\n '); }), 1, 1e4, false); window.isDetailPage && $("h3").before('\n
\n
\n
\n \n 设置\n \n
\n
\n
\n
\n '); } $(".main-nav, .container-fluid").on("click", "#setting-btn, #mini-setting-btn", (() => { clog.lowZIndex(); this.openSettingDialog(); })); $(".main-nav, .container-fluid").on("mouseenter", ".setting-box", (async () => { $(".simple-setting").html(await this.simpleSetting()).show(); this.initSimpleSettingForm().then(); clog.lowZIndex(); })).on("mouseleave", ".setting-box", (() => { $(".simple-setting").html("").hide(); })); $(".main-nav, .container-fluid").on("mouseenter", ".mini-setting-box", (async () => { $(".mini-simple-setting").html(await this.simpleSetting()).show(); this.initSimpleSettingForm().then(); clog.lowZIndex(); })).on("mouseleave", ".mini-setting-box", (() => { $(".mini-simple-setting").html("").hide(); })); } async openSettingDialog(defaultActivePanel = "backup-panel", fun) { const buttonsHTML = this.cacheItems.map(((item) => `
${item.text}
清理 查看
`)).join(""); let videoQualityHtml = ""; qualityOptions.forEach(((option) => { option.canSelect && (videoQualityHtml += ``); })); const coverButtonPlugin = this.getBean("CoverButtonPlugin"); let settingHtml = `
💾 数据备份
⚙️ 基础配置
🚫 屏蔽配置
📋 定时任务
🌐 外部网站
🗑️ 标记删除
⌨️ 快捷键配置
🧹 清理缓存
💵 打赏作者
导入数据 导出数据 获取refresh_token
阿里云盘备份
refresh_token:

WebDav备份
服务地址:
用户名:
密码:
已鉴定标签展示位置:
已鉴定内容处理方式:
鉴定补录演员信息 ❓

列表页功能按钮
按钮-打开待鉴定:
按钮-打开已收藏:
打开待鉴定|已收藏 窗口数:
随机打开已收藏:

封面快捷按钮
${coverButtonPlugin.screenSvg}长缩略图:
${coverButtonPlugin.videoSvg}预览视频:
${coverButtonPlugin.handleSvg}鉴定按钮:
${coverButtonPlugin.siteSvg}第三方跳转:
${coverButtonPlugin.copySvg}复制按钮:

预览视频默认画质:
评论区条数:
高亮已收藏演员 ❓
分类标签|高亮演员-边框样式:

请求超时时间(毫秒):
请求失败重试次数:

启用控制台日志:
日志最大行数:
请求并发数量:
请求间隔时间(毫秒):

自动检测屏蔽黑名单演员
任务开关: ❓
任务间隔时间:
检测规则:
自动同步已收藏的演员
任务开关: ❓
任务间隔时间:
自动检测已收藏演员的最新作品
任务开关: ❓
任务间隔时间:
检测规则:
域名 - MissAv:
域名 - Jable:
域名 - JavTrailer:
域名 - 123Av:
域名 - JavDb:
域名 - JavBus:
域名 - SupJav:
标记删除列表
加载中...

⚠ 作品删除目前暂无法删除115下载记录,如果需要重新下载,请去115云下载记录里删除或清空记录

快捷键修改后, 刷新页面生效

🚫 屏蔽:
⭐ 收藏:
📥️ 已下载:
🔍 已观看:
❓ 对视频列表页启用快捷键:
⏩ 快进:
▲ 折叠:
💻 控制台:
显示已鉴定内容
屏蔽单番号:
屏蔽演员:
屏蔽关键词:
收藏:
已下载:
已观看:
显示所有:
启用划词屏蔽 ❓

评论区屏蔽词:

视频标题屏蔽词:

以下操作, 不会对核心数据造成影响


${buttonsHTML}

如果JHS-enhance给您带来了便捷和价值,请考虑给予一点支持,您的鼓励是我持续创作的最大动力!感谢您的慷慨支持!

SOL二维码

SOL

点击地址可复制

`; layer.open({ type: 1, title: "设置", content: settingHtml, area: utils.getResponsiveArea(["55%", "90%"]), scrollbar: false, success: (layero, index) => { $(layero).find(".layui-layer-content").css("position", "relative"); this.loadForm(); this.bindClick(); utils.setupEscClose(index); fun && fun(); }, end: () => { this.getBean("CoverButtonPlugin").enableSvgBtn(); } }); } async simpleSetting() { let settingObj = await storageManager.getSetting(); const showFilterItemHotKey = settingObj.showFilterItemHotKey, showFilterActorItemHotKey = settingObj.showFilterActorItemHotKey, showFilterKeywordItemHotKey = settingObj.showFilterKeywordItemHotKey, showFavoriteItemHotKey = settingObj.showFavoriteItemHotKey, showHasDownItemHotKey = settingObj.showHasDownItemHotKey, showHasWatchItemHotKey = settingObj.showHasWatchItemHotKey, showAllItemHotKey = settingObj.showAllItemHotKey; return `
显示已鉴定内容:
屏蔽单番号${showFilterItemHotKey ? `(${showFilterItemHotKey})` : ""}:
屏蔽演员${showFilterActorItemHotKey ? `(${showFilterActorItemHotKey})` : ""}:
屏蔽关键词${showFilterKeywordItemHotKey ? `(${showFilterKeywordItemHotKey})` : ""}:
收藏${showFavoriteItemHotKey ? `(${showFavoriteItemHotKey})` : ""}:
已下载${showHasDownItemHotKey ? `(${showHasDownItemHotKey})` : ""}:
已观看${showHasWatchItemHotKey ? `(${showHasWatchItemHotKey})` : ""}:
❓ 显示所有${showAllItemHotKey ? `(${showAllItemHotKey})` : ""}:

❓ 弹窗方式打开页面:
鉴定后立即关闭页面:

❓ 瀑布流模式:
启用标题翻译:
启用悬浮大图:
❓ 不规则重命名:(刷新详情页生效,有延迟)
启用115视频匹配:

${isJavDb$1 ? '\n
\n \n ❓ 加载女优信息:\n \n
\n \n
\n
' : ""}
❓ 加载第三方视频资源:
❓ 加载长缩略图:
❓ 更高画质预览视频:

❓ 竖图模式:
页面列数:
页面宽度:
`; } async loadForm() { let settingObj = await storageManager.getSetting(); $("#videoQuality").val(settingObj.videoQuality); $("#reviewCount").val(settingObj.reviewCount || 20); $("#tagPosition").val(settingObj.tagPosition || "rightTop"); $("#movieShowType").val(settingObj.movieShowType || "hide"); $("#waitCheckCount").val(settingObj.waitCheckCount || 5); $("#showWaitCheckBtn").prop("checked", !settingObj.showWaitCheckBtn || settingObj.showWaitCheckBtn === YES$1); $("#showWaitDownBtn").prop("checked", !settingObj.showWaitDownBtn || settingObj.showWaitDownBtn === YES$1); $("#randomOpenWaitDown").prop("checked", !!settingObj.randomOpenWaitDown && settingObj.randomOpenWaitDown === YES$1); $("#checkConcurrencyCount").val(settingObj.checkConcurrencyCount || 2); $("#checkRequestSleep").val(settingObj.checkRequestSleep || 100); $("#enableCheckBlacklist").val(settingObj.enableCheckBlacklist || YES$1); $("#checkBlacklist_intervalTime").val(settingObj.checkBlacklist_intervalTime || 12); $("#checkBlacklist_ruleTime").val(settingObj.checkBlacklist_ruleTime || 8760); $("#enableCheckFavoriteActress").val(settingObj.enableCheckFavoriteActress || YES$1); $("#checkFavoriteActress_IntervalTime").val(settingObj.checkFavoriteActress_IntervalTime || 24); $("#enableCheckNewVideo").val(settingObj.enableCheckNewVideo || YES$1); $("#checkNewVideo_intervalTime").val(settingObj.checkNewVideo_intervalTime || 12); $("#checkNewVideo_ruleTime").val(settingObj.checkNewVideo_ruleTime || 8760); const highlightedTagNumber = settingObj.highlightedTagNumber || 1, highlightedTagColor = settingObj.highlightedTagColor || "#ce2222"; $("#highlightedTagNumber").val(settingObj.highlightedTagNumber || 1); $("#highlightedTagColor").val(settingObj.highlightedTagColor || "#ce2222"); $("#highlightedTagLabel").css("border", `${highlightedTagNumber}px solid ${highlightedTagColor}`); $("#enableClog").val(settingObj.enableClog || YES$1); $("#clogMsgCount").val(settingObj.clogMsgCount || 2e3); $("#refresh_token").val(settingObj.refresh_token || ""); $("#httpTimeout").val(settingObj.httpTimeout || 5e3); $("#httpRetryCount").val(settingObj.httpRetryCount || 3); $("#webDavUrl").val(settingObj.webDavUrl || ""); $("#webDavUsername").val(settingObj.webDavUsername || ""); $("#webDavPassword").val(settingObj.webDavPassword || ""); $("#enableTitleSelectFilter").prop("checked", !settingObj.enableTitleSelectFilter || settingObj.enableTitleSelectFilter === YES$1); $("#enableFavoriteActresses").prop("checked", !settingObj.enableFavoriteActresses || settingObj.enableFavoriteActresses === YES$1); $("#enableSaveActressCarInfo").prop("checked", !!settingObj.enableSaveActressCarInfo && settingObj.enableSaveActressCarInfo === YES$1); $("#enableScreenSvg").prop("checked", !settingObj.enableScreenSvg || settingObj.enableScreenSvg === YES$1); $("#enableVideoSvg").prop("checked", !settingObj.enableVideoSvg || settingObj.enableVideoSvg === YES$1); $("#enableHandleSvg").prop("checked", !settingObj.enableHandleSvg || settingObj.enableHandleSvg === YES$1); $("#enableSiteSvg").prop("checked", !settingObj.enableSiteSvg || settingObj.enableSiteSvg === YES$1); $("#enableCopySvg").prop("checked", !settingObj.enableCopySvg || settingObj.enableCopySvg === YES$1); const otherSitePlugin = this.getBean("OtherSitePlugin"), missAvUrl = await otherSitePlugin.getMissAvUrl(), jableUrl = await otherSitePlugin.getjableUrl(), javTrailersUrl = await otherSitePlugin.getJavTrailersUrl(), av123Url = await otherSitePlugin.getAv123Url(), javDbUrl = await otherSitePlugin.getJavDbUrl(), javBusUrl = await otherSitePlugin.getJavBusUrl(), supJavUrl = await otherSitePlugin.getSupJavUrl(); $("#missAvUrl").val(missAvUrl); $("#jableUrl").val(jableUrl); $("#javTrailersUrl").val(javTrailersUrl); $("#av123Url").val(av123Url); $("#javDbUrl").val(javDbUrl); $("#javBusUrl").val(javBusUrl); $("#supJavUrl").val(supJavUrl); let reviewKeywordList = await storageManager.getReviewFilterKeywordList(), filterKeywordList = await storageManager.getTitleFilterKeyword(); reviewKeywordList && reviewKeywordList.forEach(((reviewKeyword) => { this.addLabelTag("#reviewKeywordContainer", reviewKeyword); })); filterKeywordList && filterKeywordList.forEach(((reviewKeyword) => { this.addLabelTag("#filterKeywordContainer", reviewKeyword); })); ["#reviewKeywordContainer", "#filterKeywordContainer"].forEach(((containerId) => { $(`${containerId} .add-tag-btn`).on("click", ((event) => this.addKeyword(event, containerId))); $(`${containerId} .keyword-input`).on("keypress", ((event) => { "Enter" === event.key && this.addKeyword(event, containerId); })); })); $("#hotkey-panel [id]").map(((i, el) => el.id)).get().forEach(((containerId) => { const $element = $(`#${containerId}`), defaultValue = void 0 !== settingObj[containerId] ? settingObj[containerId] : $element.attr("data-default-hotkey") || ""; $element.val(defaultValue).on("input", ((event) => { let value = $(event.target).val(); if (/[\u4e00-\u9fa5]/.test(value) || /^Shift[a-zA-Z0-9]+$/.test(value)) { $(event.target).val(""); show.error("非法输入:不能输入中文或输入法转换错误"); } })).on("keydown", ((event) => this.handleHotkeyInput(event, $element))); })); $("#enableImageHotKey").prop("checked", !!settingObj.enableImageHotKey && settingObj.enableImageHotKey === YES$1); } handleHotkeyInput(event, $input) { event.preventDefault(); const hotkey = this.parseHotkey(event); "" !== hotkey ? this.isDuplicateHotkey(hotkey, $input.attr("id")) ? show.error("该快捷键已被其他功能使用!") : $input.val(hotkey) : $input.val(""); } parseHotkey(event) { if ("Backspace" === event.key || "Process" === event.key) return ""; const keys = []; event.ctrlKey && keys.push("Ctrl"); event.shiftKey && keys.push("Shift"); event.altKey && keys.push("Alt"); event.metaKey && keys.push("Cmd"); const key = { " ": "Space", Control: "Ctrl", Meta: "Cmd", ArrowUp: "Up", ArrowDown: "Down", ArrowLeft: "Left", ArrowRight: "Right" }[event.key] || (event.key.length > 1 ? event.key.replace("Arrow", "") : event.key); ["Control", "Shift", "Alt", "Meta"].includes(event.key) || keys.push(key); return keys.length > 0 ? keys.join("+") : ""; } isDuplicateHotkey(hotkey, currentInputId) { let isDuplicate = false; $("#hotkey-panel [id]").each(((i, el) => { if (el.id !== currentInputId && hotkey && hotkey === $(el).val()) { isDuplicate = true; return false; } })); return isDuplicate; } async initSimpleSettingForm() { let settingObj = await storageManager.getSetting(); $("#containerColumns").val(settingObj.containerColumns || 5); $("#showContainerColumns").text(settingObj.containerColumns || 5); $("#containerWidth").val((settingObj.containerWidth || 100) - 70); $("#showContainerWidth").text((settingObj.containerWidth || 100) + "%"); $("#dialogOpenDetail").prop("checked", !settingObj.dialogOpenDetail || settingObj.dialogOpenDetail === YES$1); $("#needClosePage").prop("checked", !settingObj.needClosePage || settingObj.needClosePage === YES$1); $("#autoPage").prop("checked", !settingObj.autoPage || settingObj.autoPage === YES$1); $("#translateTitle").prop("checked", !settingObj.translateTitle || settingObj.translateTitle === YES$1); $("#enableLoadActressInfo").prop("checked", !settingObj.enableLoadActressInfo || settingObj.enableLoadActressInfo === YES$1); $("#enableLoadOtherSite").prop("checked", !settingObj.enableLoadOtherSite || settingObj.enableLoadOtherSite === YES$1); $("#containerColumns").on("input", (async (event) => { let columns = $("#containerColumns").val(); $("#showContainerColumns").text(columns); if (isJavDb$1) { document.querySelector(".movie-list").style.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`; } if (isJavBus$1) { document.querySelector(".masonry").style.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`; } await storageManager.saveSettingItem("containerColumns", columns); this.applyImageMode(); })); $("#containerWidth").on("input", (async (event) => { let containerWidth = parseInt($(event.target).val()); const value = containerWidth + 70 + "%"; $("#showContainerWidth").text(value); if (isJavDb$1) { document.querySelector("section .container").style.minWidth = value; } if (isJavBus$1) { document.querySelector(".container-fluid .row").style.minWidth = value; } storageManager.saveSettingItem("containerWidth", containerWidth + 70); })); $("#dialogOpenDetail").on("change", ((event) => { let dialogOpenDetail = $("#dialogOpenDetail").is(":checked") ? YES$1 : NO; storageManager.saveSettingItem("dialogOpenDetail", dialogOpenDetail); })); $("#showFilterItem").prop("checked", !!settingObj.showFilterItem && settingObj.showFilterItem === YES$1); $("#showFilterActorItem").prop("checked", !!settingObj.showFilterActorItem && settingObj.showFilterActorItem === YES$1); $("#showFilterKeywordItem").prop("checked", !!settingObj.showFilterKeywordItem && settingObj.showFilterKeywordItem === YES$1); $("#showFavoriteItem").prop("checked", !settingObj.showFavoriteItem || settingObj.showFavoriteItem === YES$1); $("#showHasDownItem").prop("checked", !settingObj.showHasDownItem || settingObj.showHasDownItem === YES$1); $("#showHasWatchItem").prop("checked", !settingObj.showHasWatchItem || settingObj.showHasWatchItem === YES$1); $("#showFilterItem").on("change", (async (event) => { let showFilterItem = $("#showFilterItem").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("showFilterItem", showFilterItem); window.refresh(); })); $("#showFilterActorItem").on("change", (async (event) => { let showFilterActorItem = $("#showFilterActorItem").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("showFilterActorItem", showFilterActorItem); window.refresh(); })); $("#showFilterKeywordItem").on("change", (async (event) => { let showFilterKeywordItem = $("#showFilterKeywordItem").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("showFilterKeywordItem", showFilterKeywordItem); window.refresh(); })); $("#showFavoriteItem").on("change", (async (event) => { let showFavoriteItem = $("#showFavoriteItem").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("showFavoriteItem", showFavoriteItem); window.refresh(); })); $("#showHasDownItem").on("change", (async (event) => { let showHasDownItem = $("#showHasDownItem").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("showHasDownItem", showHasDownItem); window.refresh(); })); $("#showHasWatchItem").on("change", (async (event) => { let showHasWatchItem = $("#showHasWatchItem").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("showHasWatchItem", showHasWatchItem); window.refresh(); })); const $otherCheckboxes = $("#showFilterItem, #showFilterActorItem, #showFilterKeywordItem, #showFavoriteItem, #showHasDownItem, #showHasWatchItem"), updateOtherCheckboxesState = () => { const isShowAllChecked = $("#showAllItem").is(":checked"); $otherCheckboxes.prop("disabled", isShowAllChecked); isShowAllChecked ? $otherCheckboxes.attr("data-tip", "请先关闭显示所有才可点击") : $otherCheckboxes.removeAttr("data-tip"); }; $("#showAllItem").prop("checked", !!settingObj.showAllItem && settingObj.showAllItem === YES$1); $("#showAllItem").on("change", (async (event) => { let showAllItem = $("#showAllItem").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("showAllItem", showAllItem); updateOtherCheckboxesState(); window.refresh(); })); updateOtherCheckboxesState(); $("#needClosePage").on("change", (async (event) => { await storageManager.saveSettingItem("needClosePage", $("#needClosePage").is(":checked") ? YES$1 : NO); window.refresh(); })); $("#autoPage").on("change", (async (event) => { const autoPage = $("#autoPage").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("autoPage", autoPage); autoPage === YES$1 ? ($("#sort-toggle-btn").hide(), $("#one-time-sort-btn").show()) : ($("#sort-toggle-btn").show(), $("#one-time-sort-btn").hide()); })); $("#translateTitle").on("change", (async (event) => { const translateTitle = $("#translateTitle").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("translateTitle", translateTitle); if (translateTitle === YES$1) { await this.getBean("ListPagePlugin").doFilter(); window.isDetailPage && await this.getBean("TranslatePlugin").translate(); } else { await this.getBean("ListPagePlugin").revertTranslation(); $(".translated-title").remove(); } })); $("#hoverBigImg").prop("checked", !!settingObj.hoverBigImg && settingObj.hoverBigImg === YES$1); $("#hoverBigImg").on("change", (async (event) => { const hoverBigImg = $("#hoverBigImg").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("hoverBigImg", hoverBigImg); hoverBigImg === YES$1 ? window.imageHoverPreviewObj = new ImageHoverPreview({ selector: this.getSelector().coverImgSelector }) : window.imageHoverPreviewObj && window.imageHoverPreviewObj.destroy(); })); $("#enableLoadActressInfo").on("change", (async (event) => { const enableLoadActressInfo = $("#enableLoadActressInfo").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("enableLoadActressInfo", enableLoadActressInfo); enableLoadActressInfo === YES$1 ? this.getBean("ActressInfoPlugin").loadActressInfo() : $(".actress-info").remove(); })); $("#enableLoadOtherSite").on("change", (async (event) => { const enableLoadOtherSite = $("#enableLoadOtherSite").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("enableLoadOtherSite", enableLoadOtherSite); enableLoadOtherSite === YES$1 ? this.getBean("OtherSitePlugin").loadOtherSite().then() : $("#otherSiteBox").remove(); })); $("#enableLoadScreenShot").prop("checked", !settingObj.enableLoadScreenShot || settingObj.enableLoadScreenShot === YES$1); $("#enableLoadScreenShot").on("change", (async (event) => { const enableLoadScreenShot = $("#enableLoadScreenShot").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("enableLoadScreenShot", enableLoadScreenShot); enableLoadScreenShot === YES$1 ? this.getBean("ScreenShotPlugin").loadScreenShot().then() : $(".screen-container").remove(); })); $("#enableLoadPreviewVideo").prop("checked", !settingObj.enableLoadPreviewVideo || settingObj.enableLoadPreviewVideo === YES$1); $("#enableLoadPreviewVideo").on("change", (async (event) => { const enableLoadPreviewVideo = $("#enableLoadPreviewVideo").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("enableLoadPreviewVideo", enableLoadPreviewVideo); })); $("#enable115AutoRename").prop("checked", !!settingObj.enable115AutoRename && settingObj.enable115AutoRename === YES$1); $("#autoRenameScope").val(settingObj.autoRenameScope || "vr_only"); $("#autoRenameScopeRow").toggle(!!settingObj.enable115AutoRename && settingObj.enable115AutoRename === YES$1); $("#enable115AutoRename").on("change", (async (event) => { const enable115AutoRename = $("#enable115AutoRename").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("enable115AutoRename", enable115AutoRename); $("#autoRenameScopeRow").toggle(enable115AutoRename === YES$1); })); $("#autoRenameScope").on("change", (async (event) => { await storageManager.saveSettingItem("autoRenameScope", $("#autoRenameScope").val()); })); $("#enable115Match").prop("checked", !!settingObj.enable115Match && settingObj.enable115Match === YES$1); $("#enable115Match").on("change", (async (event) => { const enable115Match = $("#enable115Match").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("enable115Match", enable115Match); let movieList = $(this.getSelector().itemSelector).toArray(); await this.getBean("WangPan115MatchPlugin").matchMovieList(movieList); })); $("#enableVerticalModel").prop("checked", !!settingObj.enableVerticalModel && settingObj.enableVerticalModel === YES$1); $("#enableVerticalModel").on("change", (async (event) => { const enableVerticalModel = $("#enableVerticalModel").is(":checked") ? YES$1 : NO; await storageManager.saveSettingItem("enableVerticalModel", enableVerticalModel); this.applyImageMode(); })); $("#moreBtn").on("click", (() => { $(".simple-setting").html("").hide(); this.openSettingDialog("base-panel"); })); $("#helpBtn").on("click", (() => { layer.open({ type: 1, title: "", shadeClose: true, scrollbar: false, content: `

使用说明

1. 无法查看预览视频,提示分流?

JavDB限制日本IP的访问,而预览视频来自DMM,需要日本IP才能访问。

这样会导致二者无法同时使用,需要对其一进行代理转发。

将 cc3001.dmm.co.jp 及 dmm.co 分流到日本ip。

Clash Verge分流规则设置 (如果你是别的代理软件,自行搜索如何分流)

2. 如何屏蔽某一系列的番号?

方法一:设置中-添加视频标题关键词,如: VENX-

方法二:进入详情页,选中标题文字,右键可加入

进入详情页,选中标题,进行右键
3. 屏蔽某演员,如何只屏蔽单体影片?

屏蔽演员前,先筛选分类,再点屏蔽

屏蔽演员前,先筛选分类,再点屏蔽
4. 如何多浏览器同时登录115网盘?

① 访问115登录页, 选择JHS-扫码面板, 并扫码登录

② 进入网盘后, 右下角悬浮按钮, 复制Cookie

③ 打开另一个浏览器(需装JHS脚本), 进入登录页面, 选择JHS-扫码面板, 输入Cookie并回车

`, area: utils.getResponsiveArea(["50%", "90%"]) }); })); } async applyImageMode() { $("#verticalImgStyle").remove(); if (await storageManager.getSetting("enableVerticalModel", NO) === YES$1) { let imgPosition = "100% 50% !important"; window.location.href.includes("/advanced_search?type=100") && (imgPosition = "50% 50% !important"); const verticalStyle = ` .cover { min-height: 350px !important; overflow: hidden !important; padding-top: 142% !important; } .cover img { object-fit: cover !important; object-position: ${imgPosition}; } /* bus的 */ .masonry .movie-box img { min-height: 500px !important; object-fit: cover !important; object-position: top right; } `; $("\n "; } async showNewVideoCount() { const totalNewVideoCount = (await storageManager.getFavoriteActressList()).reduce(((accumulator, actress) => { var _a2; return accumulator + ((null == (_a2 = actress.newVideoList) ? void 0 : _a2.length) ?? 0); }), 0); $("#newVideoCount").text(`${totalNewVideoCount}`); } async resetBtnTip() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(), lastCheckTimeStr = localStorage.getItem(taskPlugin.lastCheckFavoriteActressTimeKey) || "无", checkFavoriteActress_IntervalTime = settingObj.checkFavoriteActress_IntervalTime, lastCheckNewVideoTimeStr = localStorage.getItem(taskPlugin.lastCheckNewVideoTimeKey) || "无", checkNewVideo_intervalTime = settingObj.checkNewVideo_intervalTime; $("#checkFavoriteActress").attr("data-tip", `上次自动同步时间: ${lastCheckTimeStr}; 检测间隔时间: ${checkFavoriteActress_IntervalTime}小时`); $("#checkNewVideo").attr("data-tip", `上次检测时间: ${lastCheckNewVideoTimeStr}; 检测间隔时间: ${checkNewVideo_intervalTime}小时`); } async openDialog() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(), lastCheckTimeStr = localStorage.getItem(taskPlugin.lastCheckFavoriteActressTimeKey) || "无", checkFavoriteActress_IntervalTime = settingObj.checkFavoriteActress_IntervalTime, lastCheckNewVideoTimeStr = localStorage.getItem(taskPlugin.lastCheckNewVideoTimeKey) || "无", checkNewVideo_intervalTime = settingObj.checkNewVideo_intervalTime; let html = `
${this.actressSvg}    手动同步演员 ${this.newSvg}    手动检测最新作品 ${this.settingSvg}    配置
${this.refreshSvg}    刷新
`; layer.open({ type: 1, title: '新作品检测 ❓', content: html, scrollbar: false, area: utils.getResponsiveArea(["80%", "90%"]), anim: -1, success: async (layero, index) => { this.loadData(); this.bindClick(); utils.setupEscClose(index); } }); } bindClick() { const taskPlugin = this.getBean("TaskPlugin"); $("#reLoad").on("click", ((event) => { this.loadData(); $("#checkNewVideoMsg").text(""); })); $("#toSetting").on("click", ((event) => { this.getBean("SettingPlugin").openSettingDialog("task-panel", (() => { $("#setting-checkFavoriteActress").css({ border: "1px solid #f40" }); $("#setting-checkNewVideo").css({ border: "1px solid #f40" }); })); })); $("#checkFavoriteActress").on("click", ((event) => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动同步演员?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (!lock) { show.error("当前有定时任务在后台执行中, 无法发起手动任务"); return; } if ($('a[href*="/users/profile"]').length > 0) { await taskPlugin.checkFavoriteActress(); this.loadData(); } else show.error("未登录JavDb, 同步失败"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); $("#checkNewVideo").on("click", ((event) => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动检测最新作品?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { lock ? await taskPlugin.checkNewVideo(true) : show.error("当前有定时任务在后台执行中, 无法发起手动任务"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); $("#paramActressType").on("change", ((event) => { this.loadData(); })); } loadData() { this.currentPage = 1; this.renderActressCards().then(); } async renderActressCards() { const $actressCardContainer = $("#actress-card-container"); if (!$actressCardContainer.length) return; let favoriteActressesList = await storageManager.getFavoriteActressList(); const paramActressType = $("#paramActressType").val(); "all" !== paramActressType && (favoriteActressesList = favoriteActressesList.filter(((actress) => actress.actressType === paramActressType))); const fullActressList = utils.genericSort(favoriteActressesList, [{ key: (item) => { var _a2; return (null == (_a2 = item.newVideoList) ? void 0 : _a2.length) ?? 0; }, order: "desc" }, { key: "lastPublishTime", order: "desc" }]), totalCount = fullActressList.length, totalPages = Math.ceil(totalCount / this.pageSize), startIndex = (this.currentPage - 1) * this.pageSize, endIndex = startIndex + this.pageSize, pagedData = fullActressList.slice(startIndex, endIndex), javDbUrl = await this.getBean("OtherSitePlugin").getJavDbUrl(), taskPlugin = this.getBean("TaskPlugin"), checkNewVideo_ruleTime = await storageManager.getSetting("checkNewVideo_ruleTime") || 8760, cardsHtml = pagedData.map(((data) => { var _a2, _b, _c; const allNamesStr = Array.isArray(data.allName) ? data.allName.join(",") : ""; Array.isArray(data.newVideoList) && data.newVideoList.join(","); const detailLink = `${javDbUrl}/actors/${data.starId}?t=d`; let isUnCheck = false; data.lastPublishTime && (isUnCheck = !taskPlugin.isUnnecessaryCheck(data.lastPublishTime, checkNewVideo_ruleTime)); let actressTypeText = "未知", actressTypeBgc = "#9E9E9E"; if ("uncensored" === data.actressType) { actressTypeText = "无码"; actressTypeBgc = "#4CAF50"; } else if ("censored" === data.actressType) { actressTypeText = "有码"; actressTypeBgc = "#FF9800"; } let cardUnnecessaryBtnCss = ""; isUnCheck && (cardUnnecessaryBtnCss = "background: linear-gradient(145deg, #e0e0e0 0%, #cabdbd 100%);box-shadow: none"); return `
${allNamesStr}
${data.name}
${allNamesStr}
上次检测: ${data.lastCheckTime || ""}
最后发行作品: ${data.lastPublishTime || ""}
${isUnCheck ? "停更" + checkNewVideo_ruleTime / 24 / 365 + "年以上, 下轮任务不再进行检测" : ""}
${data.remark || ""}
${this.editSvg} ${this.deleteSvg} ${this.checkSvg}
${actressTypeText}
🔔 ${(null == (_c = data.newVideoList) ? void 0 : _c.length) || 0}
`; })).join(""); $actressCardContainer.html(cardsHtml); $(".btn-delete-actress").off("click").on("click", ((e) => { e.preventDefault(); const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find(((item) => item.starId === starId)); utils.q(e, `是否取消收藏 ${actress.name}?`, (async () => { let deleteActressUrl = `${await this.getBean("OtherSitePlugin").getJavDbUrl()}/actors/${starId}/uncollect`; const csrfToken = document.querySelector("meta[name=csrf-token]").content, res = await gmHttp.post(deleteActressUrl, null, { "x-csrf-token": csrfToken }); if (res.includes("removeClass")) { await storageManager.removeFavoriteActress(starId); this.loadData(); } else { show.error("移除失败"); clog.error("移除失败,返回值:", res); } })); })); $(".btn-edit-actress").off("click").on("click", ((e) => { e.preventDefault(); const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find(((item) => item.starId === starId)); actress ? this.editActress(actress) : show.error(`未找到 starId 为 ${starId} 的女优记录。`); })); $(".btn-check-actress").off("click").on("click", ((e) => { e.preventDefault(); navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (!lock) { show.error("当前有定时任务在后台执行中, 无法发起手动任务"); return; } const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find(((item) => item.starId === starId)); await taskPlugin.checkOneNewVideo(actress); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); this.renderPagination(totalCount, totalPages); show.ok("加载完成"); } async editActress(actress) { const initialName = actress.name, initialAvatar = actress.avatar, initialRemark = actress.remark || "", initialAllName = Array.isArray(actress.allName) ? actress.allName.join(",") : "", initialNewVideoList = Array.isArray(actress.newVideoList) ? actress.newVideoList.join(",") : "", starId = actress.starId, textareaStyle = "width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; min-height: 60px; overflow-y: hidden;", initActressType = actress.actressType || "", editFormHtml = `
Avatar Preview
`; layer.open({ type: 1, title: `编辑女优: ${initialName} (${starId})`, area: ["500px", "750px"], content: editFormHtml, btn: ["保存", "取消"], success: (layero, index) => { const autoResizeTextarea = ($textarea) => { $textarea.css("height", "auto"); $textarea.css("height", $textarea[0].scrollHeight + 15 + "px"); }; $("#edit-actress-avatar").on("input", (function () { const newUrl = $(this).val(); $("#edit-avatar-preview").attr("src", newUrl); })); const $allNameTextarea = $("#edit-actress-allname"); $allNameTextarea.on("input", (function () { autoResizeTextarea($(this)); })); autoResizeTextarea($allNameTextarea); const $videoListTextarea = $("#edit-actress-newvideolist"); $videoListTextarea.on("input", (function () { autoResizeTextarea($(this)); })); autoResizeTextarea($videoListTextarea); $("#search-avatar-btn").on("click", (async () => { await this.searchAvatar(); })); $("#select-cdn-btn").on("click", (async () => { await (async function () { const initialIndex = currentCdnIndex, radioOptions = CDN_SOURCES.map(((source, index2) => `
`)).join(""), cdnSelectHtml = `

请选择头像数据源 (当前: ${CDN_SOURCES[initialIndex].name}):

${radioOptions}

切换源会清除本地缓存的数据,并在下次搜索时重新加载。

`; layer.open({ type: 1, title: "选择 CDN 源", area: ["400px", "auto"], content: cdnSelectHtml, btn: ["确定", "取消"], success: (layero2, index2) => { utils.setupEscClose(index2); }, yes: async (index2) => { const newIndexStr = $('input[name="cdn-source"]:checked').val(), newIndex = parseInt(newIndexStr, 10); if (newIndex !== currentCdnIndex) { currentCdnIndex = newIndex; localStorage.setItem("jhs_img_cdn_index", newIndex.toString()); window.G_FRIENDS_JSON_URL = CDN_SOURCES[newIndex].json; window.CDN_BASE_URL = CDN_SOURCES[newIndex].base; window.G_FRIENDS_DATA_CACHE = null; window.G_FRIENDS_AVATAR_MAP = null; try { await dbHelper.set("filetree_data", null); } catch (e) { clog.error("清除 IndexedDB 缓存失败:", e); } show.ok(`CDN 源已切换为: ${CDN_SOURCES[newIndex].name}`); layer.close(index2); } else layer.close(index2); } }); })(); })); utils.setupEscClose(index); }, yes: async (index) => { const newAvatar = $("#edit-actress-avatar").val().trim(), newName = $("#edit-actress-name").val().trim(), newAllNameStr = $("#edit-actress-allname").val().trim(), newVideoListStr = $("#edit-actress-newvideolist").val().trim(), newRemark = $("#edit-remark").val().trim(), newActressType = $("#actressType").val(); if (!newName) { show.error("主名称不能为空"); return false; } const newAllName = newAllNameStr.split(/[\uff0c,]/).map(((n) => n.trim())).filter(((n) => n.length > 0)), newVideoList = newVideoListStr.split(/[\uff0c,]/).map(((n) => n.trim())).filter(((n) => n.length > 0)); actress.avatar = newAvatar; actress.name = newName; actress.allName = newAllName; actress.newVideoList = newVideoList; actress.actressType = newActressType; actress.remark = newRemark; if (await storageManager.updateFavoriteActress(actress)) show.error("修改失败"); else { show.ok(`女优 ${newName} 信息已更新`); await this.renderActressCards(); layer.close(index); } } }); } renderPagination(totalCount, totalPages) { const currentPage = this.currentPage; let paginationHtml = ""; const $actressPagination = $("#actress-pagination"); if (0 === totalPages) { paginationHtml = '共 0 条记录'; $actressPagination.html(paginationHtml); return; } currentPage > 1 && totalPages > 5 && (paginationHtml += ''); currentPage > 1 && (paginationHtml += ``); let startPage = Math.max(1, currentPage - Math.floor(2.5)), endPage = Math.min(totalPages, startPage + 5 - 1); endPage - startPage < 4 && (startPage = Math.max(1, endPage - 5 + 1)); for (let i = startPage; i <= endPage; i++) { paginationHtml += ``; } currentPage < totalPages && (paginationHtml += ``); currentPage < totalPages && totalPages > 5 && (paginationHtml += ``); paginationHtml += `共 ${totalCount} 条记录 (第 ${currentPage}/${totalPages} 页)`; $actressPagination.html(paginationHtml); $(".pagination-btn").off("click").on("click", ((e) => { if ($(e.currentTarget).is("[disabled]")) return; const newPage = parseInt($(e.currentTarget).data("page")); if (newPage >= 1 && newPage <= totalPages && newPage !== this.currentPage) { this.currentPage = newPage; this.renderActressCards(); } })); } async searchAvatar() { const $mainNameInput = $("#edit-actress-name"), $aliasInput = $("#edit-actress-allname"), currentName = $mainNameInput.val().trim(), searchNames = $aliasInput.val().trim().split(/[\uff0c,]/).map(((n) => n.trim())).filter(((n) => n.length > 0)); currentName && searchNames.unshift(currentName); if (0 === searchNames.length) { show.error("请先填写女优主名称或别名进行搜索。"); return; } const loadObj = loading("正在搜索头像..."); let imageLinks = []; try { imageLinks = await searchActorAvatars(searchNames); } catch (e) { show.error(`头像数据加载或搜索失败: ${e.message || e}`); return; } finally { loadObj.close(); } if (0 === imageLinks.length) { show.error(`未找到与 '${searchNames.join("、")}' 相关的头像。请检查名称。`); return; } const imageItems = imageLinks.map(((url, index) => `
...
`)).join(""), imageListHtml = `

点击图片即可选择(初始共 ${imageLinks.length} 张)

${imageItems}
`; let errorCount = 0; layer.open({ type: 1, title: `选择女优头像 (${imageLinks.length} 张)`, area: utils.getResponsiveArea(["900px", "85%"]), content: imageListHtml, btn: ["关闭"], success: (selectionLayero, selectionIndex) => { const $container = $(selectionLayero), $images = $container.find(".gfriends-selectable-img"), $prompt = $container.find("#gfriends-prompt"); $images.each((function () { const $img = $(this), wrapperId = $img.data("wrapper-id"), $wrapper = $container.find(`#${wrapperId}`), $sizeTag = $container.find(`.gfriends-size-tag[data-size-for="${wrapperId}"]`); $img.on("load", (function () { const width = this.naturalWidth, height = this.naturalHeight; $sizeTag.text(`${width} x ${height}`); })); $img.on("error", (function () { $wrapper.remove(); errorCount++; const validCount = imageLinks.length - errorCount; $prompt.text(`点击图片即可选择(已移除 ${errorCount} 张错误图片,剩余 ${validCount} 张)`); if (0 === validCount) { show.error("所有搜索到的头像链接均已失效,无法选择。"); layer.close(selectionIndex); } })); this.complete && (this.naturalWidth > 0 ? $img.trigger("load") : $img.trigger("error")); })); $images.on("click", (function () { const $clickedImg = $(this), selectedUrl = $clickedImg.data("url"); $("#edit-actress-avatar").val(selectedUrl); $("#edit-avatar-preview").attr("src", selectedUrl); $images.removeClass("is-selected"); $clickedImg.addClass("is-selected"); setTimeout((() => { layer.close(selectionIndex); }), 150); })); utils.setupEscClose(selectionIndex); } }); } } class HistoryPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "tableObj", null); } getName() { return "HistoryPlugin"; } async initCss() { return "\n \n "; } handle() { let fc2Url = "/advanced_search?type=3&score_min=0&d=1"; $('.navbar-item:contains("FC2")').attr("href", fc2Url); $('.tabs a:contains("FC2")').attr("href", fc2Url); if (currentHref$1.includes("advanced_search?type=3")) { $("h2.section-title").contents().first().replaceWith("Fc2PPV"); $(".section .container > .box").remove(); } if (currentHref$1.includes("collection_codes?movieId")) { $("section").html(""); const urlParams = new URLSearchParams(window.location.search); let movieId = urlParams.get("movieId"), carNum2 = urlParams.get("carNum"), url = urlParams.get("url"); movieId && carNum2 && url && this.openFc2Dialog(movieId, carNum2, url); } } openFc2Dialog(movieId, carNum2, href) { let tempCarNum = carNum2.replace("FC2-", ""); if (href.includes("123av")) { this.getBean("Fc2By123AvPlugin").open123AvFc2Dialog(carNum2, href); return; } layer.open({ type: 1, title: carNum2, content: '\n
\n \n \n
\n
加载中...
\n
\n \n
第三方资源:
\n \n
\n 🚫 屏蔽\n ⭐ 收藏\n 📥️ 已下载\n 🔍 已观看\n \n \n 字幕搜索\n \n \n \n 磁力搜索\n \n
\n
\n \n
\n
\n
\n \n \n \n
\n ', area: utils.getResponsiveArea(["70%", "90%"]), skin: "movie-detail-layer", scrollbar: false, success: (layero, index) => { this.loadData(movieId, carNum2); $("#favoriteBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FAVORITE$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#filterBtn").on("click", ((event) => { utils.q(event, `是否屏蔽${carNum2}?`, (async () => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FILTER$1, publishTime }); window.refresh(); layer.closeAll(); window.location.href.includes("collection_codes?movieId") && utils.closePage(); })); })); $("#hasDownBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_DOWN$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#hasWatchBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_WATCH$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#subtitleSearchBtn").on("click", (() => { window.JavPackSubtitle.openSearchModal({details: {code: carNum2}}); })); window.JavPackSubtitle.preload115Matches(carNum2); $("#magnetSearchBtn").on("click", (() => { let magnetHub = this.getBean("MagnetHubPlugin").createMagnetHub(carNum2); layer.open({ type: 1, title: "磁力搜索", content: '
', area: utils.getResponsiveArea(["60%", "80%"]), scrollbar: false, success: () => { $("#magnetHubBox").append(magnetHub); } }); })); this.getBean("OtherSitePlugin").loadOtherSite(tempCarNum, carNum2).then(); utils.setupEscClose(index); }, end() { window.location.href.includes("collection_codes?movieId") && utils.closePage(); } }); } loadData(movieId, carNum2) { let tempCarNum = carNum2.replace("FC2-", ""); this.handleMovieDetail(movieId); this.handleLongImg(tempCarNum); this.handleMagnets(movieId); this.getBean("ReviewPlugin").showReview(movieId, $("#reviews-content")).then(); this.getBean("RelatedPlugin").showRelated($("#related-content"), movieId).then(); } handleMovieDetail(movieId) { javDbApi.getMovieDetail(movieId).then(((res) => { const actors = res.actors || [], imgList = res.imgList || []; let actorsHtml = ""; if (actors.length > 0) { let actress = ""; for (let i = 0; i < actors.length; i++) { let actor = actors[i]; actorsHtml += `${actor.name}`; 0 === actor.gender && (actress += actor.name + " "); } $("#data-actress").text(actress); } else actorsHtml = '暂无演员信息'; let imagesHtml = ""; imagesHtml = Array.isArray(imgList) && imgList.length > 0 ? imgList.map(((img, index) => ` `)).join("") : '
暂无剧照
'; $(".movie-info-container").html(`

${res.title || "无标题"}

番号: ${res.carNum || "未知"} 年份: ${res.releaseDate || "未知"} 评分: ${res.score || "无"} 时长: ${res.duration + " m" || "无"}
站点: fc2ppvdb fc2电子市场
主演: ${actorsHtml}
`); this.getBean("TranslatePlugin").translate(res.carNum, false).then(); })).catch(((err) => { console.error(err); $(".movie-info-container").html(`
加载失败: ${err.message}
`); })); } handleLongImg(carNum2) { utils.loopDetector((() => $(".movie-gallery .image-list").length > 0), (async () => { $(".movie-gallery .image-list").prepend('
正在加载缩略图
'); const screenShotPlugin = this.getBean("ScreenShotPlugin"), imgUrl = await screenShotPlugin.getScreenshot(carNum2); imgUrl && await screenShotPlugin.addImg("缩略图", imgUrl, carNum2); })); } handleMagnets(movieId) { javDbApi.getMagnets(movieId).then(((magnetList) => { let magnetsHtml = ""; if (magnetList.length > 0) for (let i = 0; i < magnetList.length; i++) { let magnet = magnetList[i], oddClass = ""; i % 2 == 0 && (oddClass = "odd"); magnetsHtml += `
${magnet.name}
${(magnet.size / 1024).toFixed(2)}GB, ${magnet.files_count}個文件
${magnet.hd ? '高清' : ""} ${magnet.cnsub ? '字幕' : ""}
${magnet.created_at}
`; } else magnetsHtml = '暂无磁力信息'; $("#magnets-content").html(magnetsHtml); $(".buttons button[data-clipboard-text*='magnet:']").each(((i, el) => { $(el).parent().append($("'); $tag.append(button); button.fadeIn(0); }), (function () { $(this).find(".highlight-btn").fadeOut(0, (function () { $(this).remove(); })); })); $(document).on("click", ".highlight-btn", (async function (e) { e.stopPropagation(); e.preventDefault(); const $tag = $(this).closest("a.tag"), clonedTag = $tag.clone(); clonedTag.find(".highlight-btn").remove(); const tagText = clonedTag.text().trim().replace(/\s*\(\d+\)$/, ""); let highlightedTags = await storageManager.getHighlightedTags(); if (highlightedTags.includes(tagText)) { highlightedTags = highlightedTags.filter(((item) => item !== tagText)); $tag.removeClass("highlighted"); } else { highlightedTags.push(tagText); $tag.addClass("highlighted"); } await storageManager.setHighlightedTags(highlightedTags); })); } async createFoldBtn() { const foldCategoryHotKey = await storageManager.getSetting("foldCategoryHotKey"); let $subTags = $("#tags"), checkTagStr = $("#tags dl div.tag.is-info").map((function () { return $(this).text().replaceAll("\n", "").replaceAll(" ", ""); })).get().join(" "); if (!checkTagStr) return; $(".tabs").append(`
已选分类: ${checkTagStr}
${foldCategoryHotKey ? ` (${foldCategoryHotKey})` : ""}
`); let $section = $("h2.section-title"); if ($section.length > 0) { $section.append('\n
\n \n \n \n \n
\n '); $subTags = $("section > div > div.box"); } if (!$subTags) return; let $foldCategoryBtn = $("#foldCategoryBtn"), isFolded = localStorage.getItem("jhs_foldCategory") === YES$1, [newText, newIcon] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"]; $foldCategoryBtn.find("span").text(newText).end().find("i").attr("class", newIcon); window.location.href.includes("noFold=1") || $subTags[isFolded ? "hide" : "show"](); $foldCategoryBtn.on("click", (async (event) => { event.preventDefault(); isFolded = !isFolded; localStorage.setItem("jhs_foldCategory", isFolded ? YES$1 : NO); const [newText2, newIcon2] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"]; $foldCategoryBtn.find("span").text(newText2).end().find("i").attr("class", newIcon2); $subTags[isFolded ? "hide" : "show"](); })); } } class ActressInfoPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "apiUrl", "https://ja.wikipedia.org/wiki/"); } getName() { return "ActressInfoPlugin"; } async handle() { "yes" === await storageManager.getSetting("enableLoadActressInfo", "yes") && this.loadActressInfo(); } loadActressInfo() { this.handleDetailPage().then(); this.handleStarPage().then(); } async initCss() { return "\n \n "; } async handleDetailPage() { if ($(".actress-info").length > 0) return; let nameList = $(".female").prev().map(((i, el) => $(el).text().trim())).get(); if (!nameList.length) return; const cacheKey = "jhs_actress_info", cacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}; let result = null, infoHtml = ""; for (let i = 0; i < nameList.length; i++) { let name2 = nameList[i]; result = cacheData[name2]; if (!result) try { result = await this.searchInfo(name2); result && (cacheData[name2] = result); } catch (e) { console.error("该名称查询失败,尝试其它名称"); } let contentHtml = ""; contentHtml = result ? `
${name2}: ${result.birthday} ${result.age} ${result.height} ${result.weight} ${result.threeSizeText} ${result.braSize}
` : `
${name2}:
`; infoHtml += contentHtml; } $('strong:contains("演員")').parent().after(infoHtml); localStorage.setItem(cacheKey, JSON.stringify(cacheData)); } async handleStarPage() { if ($(".actress-info").length > 0) return; let nameList = [], $actor = $(".actor-section-name"); $actor.length && $actor.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let $sectionMeta = $(".section-meta:not(:contains('影片'))"); $sectionMeta.length && $sectionMeta.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); if (!nameList.length) return; const cacheKey = "jhs_actress_info", cacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}; let result = null; for (let i = 0; i < nameList.length; i++) { let name2 = nameList[i]; result = cacheData[name2]; if (result) break; try { result = await this.searchInfo(name2); } catch (e) { console.error("该名称查询失败,尝试其它名称"); } if (result) break; } result && nameList.forEach(((name2) => { cacheData[name2] = result; })); let contentHtml = '
无此相关演员信息
'; result && (contentHtml = `
出生日期: ${result.birthday} 年龄: ${result.age} 身高: ${result.height}
体重: ${result.weight} 三围: ${result.threeSizeText} 罩杯: ${result.braSize}
`); $actor.parent().append(contentHtml); localStorage.setItem(cacheKey, JSON.stringify(cacheData)); } async searchInfo(name2) { "三上悠亞" === name2 && (name2 = "三上悠亜"); let url = this.apiUrl + name2; const html = await gmHttp.get(url), parser = new DOMParser(), $dom = $(parser.parseFromString(html, "text/html")); let birthday = $dom.find('a[title="誕生日"]').parent().parent().find("td").text().trim(), age = $dom.find("th:contains('現年齢')").parent().find("td").text().trim() ? parseInt($dom.find("th:contains('現年齢')").parent().find("td").text().trim()) + "岁" : "", height = $dom.find('tr:has(a[title="身長"]) td').text().trim().split(" ")[0] + "cm", weight = $dom.find('tr:has(a[title="体重"]) td').text().trim().split("/")[1].trim(); "― kg" === weight && (weight = ""); return { birthday, age, height, weight, threeSizeText: $dom.find('a[title="スリーサイズ"]').closest("tr").find("td").text().replace("cm", "").trim(), braSize: $dom.find('th:contains("ブラサイズ")').next("td").contents().first().text().trim(), url }; } } class AliyunPanPlugin extends BasePlugin { getName() { return "AliyunPanPlugin"; } handle() { $("body").append('获取refresh_token'); $("#refresh-token-btn").on("click", ((event) => { let tokenStr = localStorage.getItem("token"); if (!tokenStr) { alert("请先登录!"); return; } let refresh_token = JSON.parse(tokenStr).refresh_token; navigator.clipboard.writeText(refresh_token).then((() => { alert("已复制到剪切板 如失败, 请手动复制: " + refresh_token); })).catch(((err) => { console.error("Failed to copy refresh token: ", err); })); })); } } class HitShowPlugin extends BasePlugin { constructor() { super(); __publicField2(this, "$contentBox", $(".section .container")); } getName() { return "HitShowPlugin"; } handle() { $('a[href*="rankings/playback"]').on("click", ((event) => { event.preventDefault(); event.stopPropagation(); window.location.href = "/advanced_search?handlePlayback=1&period=daily"; })); this.handlePlayback().then(); } hookPage() { let $h2 = $("h2.section-title"); $h2.contents().first().replaceWith("热播"); $h2.css("marginBottom", "0"); $(".empty-message").remove(); $(".section .container .box").remove(); $("#sort-toggle-btn").remove(); this.$contentBox.append('
'); this.$contentBox.append('
'); } async handlePlayback() { if (!window.location.href.includes("handlePlayback=1")) return; let period = new URLSearchParams(window.location.search).get("period"); this.toolBar(period); this.hookPage(); let $movieBox = $(".movie-list"); $movieBox.html(""); let loadObj = loading(); try { const movies = await javDbApi.playback(period); let moviesHtml = javDbApi.markDataListHtml(movies); $movieBox.html(moviesHtml); } catch (e) { clog.error("发生错误:", e); } finally { loadObj.close(); } } toolBar(period) { let conditionHtml = `
日榜 周榜 月榜
`; this.$contentBox.append(conditionHtml); } getStarRating(score) { let stars = ""; const fullStars = Math.floor(score); for (let i = 0; i < fullStars; i++) stars += ''; for (let i = 0; i < 5 - fullStars; i++) stars += ''; return stars; } loadScore(movies) { if (0 === movies.length) return; (async () => { for (const movie of movies) try { const movieId = movie.id; if (!$(`#score_${movieId}`).length) return; if ($(`#${movieId}`).is(":hidden")) continue; const cacheData = localStorage.getItem("jhs_score_info") ? JSON.parse(localStorage.getItem("jhs_score_info")) : {}, cached = cacheData[movieId]; if (cached) { this.appendScoreHtml(movieId, cached); continue; } for (; !document.hasFocus();) await new Promise(((r) => setTimeout(r, 500))); const res = await javDbApi.getMovieDetail(movieId); let score = res.score, watchedCount = res.watchedCount, html = ` ${this.getStarRating(score)}   ${score}分,由${watchedCount}人評價 `; this.appendScoreHtml(movieId, html); cacheData[movieId] = html; localStorage.setItem("jhs_score_info", JSON.stringify(cacheData)); await new Promise(((r) => setTimeout(r, 500))); } catch (err) { clog.error(`🚨 解析评分数据失败 | 编号: ${movie.number} `, `错误详情: ${err.message} `, err.stack ? `调用栈: ${err.stack}` : ""); } })(); } appendScoreHtml(movieId, scoreHtml) { let $scoreBox = $(`#score_${movieId}`); $scoreBox.length && "" === $scoreBox.html().trim() && $scoreBox.slideUp(0, (function () { $(this).html(scoreHtml).slideDown(500); })); } } class TOP250Plugin extends BasePlugin { constructor() { super(); __publicField2(this, "has_cnsub", ""); __publicField2(this, "$contentBox", $(".section .container")); __publicField2(this, "movies", []); } getName() { return "TOP250Plugin"; } handle() { $('.main-tabs ul li:contains("猜你喜歡")').html('Top250'); $('a[href*="rankings/top"]').on("click", ((event) => { event.preventDefault(); event.stopPropagation(); const $target = $(event.target), href = ($target.is("a") ? $target : $target.closest("a")).attr("href"); let queryString = href.includes("?") ? href.split("?")[1] : href; const urlParams = new URLSearchParams(queryString); this.checkLogin(event, urlParams); })); this.handleTop().then(); } hookPage() { $("h2.section-title").contents().first().replaceWith("Top250"); $(".empty-message").remove(); $(".section .container .box").remove(); $("#sort-toggle-btn").remove(); this.$contentBox.append('
'); this.$contentBox.append('
'); this.renderPagination(); } renderPagination() { const urlParams = new URLSearchParams(window.location.search); let currentPage = parseInt(urlParams.get("page")) || 1; this.$contentBox.append(((page) => { const isNextDisabled = page >= 5; let paginationListHTML = ""; for (let i = 1; i <= 5; i++) { paginationListHTML += `
  • ${i}
  • `; } return ` `; })(currentPage)); this.$contentBox.on("click", ".pagination-link, .pagination-previous, .pagination-next", ((event) => { event.preventDefault(); const newPage = parseInt($(event.currentTarget).data("page")); !isNaN(newPage) && newPage > 0 && ((newPage2) => { urlParams.set("page", newPage2); window.history.pushState({}, "", "?" + urlParams.toString()); window.location.reload(); })(newPage); })); } async handleTop() { if (!window.location.href.includes("handleTop=1")) return; const urlParams = new URLSearchParams(window.location.search); let type = urlParams.get("handleType") || "all", type_value = urlParams.get("type_value") || ""; this.has_cnsub = urlParams.get("has_cnsub") || ""; let page = urlParams.get("page") || 1; this.toolBar(type, type_value, page); this.hookPage(); let $movieBox = $(".movie-list"); $movieBox.html(""); let loadObj = loading(); try { const res = await javDbApi.top250(type, type_value, page, 50); let successFlag = res.success, message = res.message, action = res.action; if (1 === successFlag) { let dataList = res.data.movies; if (0 === dataList.length) { show.error("无数据"); loadObj.close(); return; } this.movies = dataList; const filter_movies = dataList.filter(((item) => "1" === this.has_cnsub ? item.has_cnsub : "0" !== this.has_cnsub || !item.has_cnsub)); let moviesHtml = javDbApi.markDataListHtml(filter_movies); $movieBox.html(moviesHtml); } else { clog.error(res); $movieBox.html(`

    ${message}

    `); show.error(message); if ("JWTVerificationError" === action) { await localStorage.removeItem("jhs_appAuthorization"); await this.checkLogin(null, new URLSearchParams(window.location.search)); } } } catch (e) { clog.error("发生错误:", e); } finally { loadObj.close(); } } toolBar(type, type_value, currentPage) { "5" === currentPage.toString() && $(".pagination-next").remove(); $(".pagination-ellipsis").closest("li").remove(); $(".pagination-list li a").each((function () { parseInt($(this).text()) > 5 && $(this).closest("li").remove(); })); let yearHtml = ""; for (let year = (/* @__PURE__ */ new Date()).getFullYear(); year >= 2008; year--) yearHtml += ` ${year} `; let conditionHtml = `
    全部 有码 无码 欧美 Fc2 含中字磁鏈 无字幕 重置
    ${yearHtml}
    `; this.$contentBox.append(conditionHtml); $("a[data-cnsub-value]").on("click", ((event) => { const cnsubValue = $(event.currentTarget).data("cnsub-value"); this.has_cnsub = cnsubValue.toString(); $("a[data-cnsub-value]").removeClass("is-info"); $(event.currentTarget).addClass("is-info"); $(".toolbar a.button").not("[data-cnsub-value]").each(((index, element) => { const $link = $(element), url = new URL($link.attr("href"), window.location.origin); url.searchParams.set("has_cnsub", cnsubValue); $link.attr("href", url.toString()); })); const filter_movies = this.movies.filter(((item) => "1" === this.has_cnsub ? item.has_cnsub : "0" !== this.has_cnsub || !item.has_cnsub)); let moviesHtml = javDbApi.markDataListHtml(filter_movies); $(".movie-list").html(moviesHtml); })); } async checkLogin(event, urlParams) { if (!localStorage.getItem("jhs_appAuthorization")) { show.error("该类别依赖移动端接口,请先完成登录"); this.openLoginDialog(); return; } let type = "all", type_value = "", t = urlParams.get("t") || ""; if (/^y\d+$/.test(t)) { type = "year"; type_value = t.substring(1); } else if ("" !== t) { type = "video_type"; type_value = t; } let url = `/advanced_search?handleTop=1&handleType=${type}&type_value=${type_value}`; event && (event.ctrlKey || event.metaKey) ? _GM_openInTab(window.location.origin + url, { insert: 0 }) : window.location.href = url; } openLoginDialog() { layer.open({ type: 1, title: "JavDB", closeBtn: 1, area: ["360px", "auto"], shadeClose: false, content: `
    `, success: (layero, index) => { $("#loginBtn").click((function () { const username = $("#username").val(), password = $("#password").val(); if (!username || !password) { show.error("请输入用户名和密码"); return; } let loadObj = loading(); javDbApi.login(username, password).then((async (res) => { let success = res.success; if (0 === success) show.error(res.message); else { if (1 !== success) { clog.error("登录失败", res); throw new Error(res.message); } { let token = res.data.token; await localStorage.setItem("jhs_appAuthorization", token); show.ok("登录成功"); layer.close(index); window.location.href = "/advanced_search?handleTop=1&period=daily"; } } })).catch(((err) => { clog.error("登录异常:", err); show.error(err.message); })).finally((() => { loadObj.close(); })); })); } }); } } class NavBarPlugin extends BasePlugin { getName() { return "NavBarPlugin"; } async initCss() { return "\n .highlight-red {\n /* 核心要求:高亮红色文本 */\n color: red !important; \n \n /* 建议:增加字体加粗,效果更明显 */\n font-weight: bold;\n \n /* 建议:增加背景色,效果更突出 */\n /* background-color: yellow; */ \n}\n "; } handle() { this.margeNav(); this.hookSearch(); this.hookOldSearch(); this.toggleOtherNavItem(); $(window).resize(this.toggleOtherNavItem); if (window.location.href.includes("/search")) { const urlParams = new URLSearchParams(window.location.search); let q = urlParams.get("q"), f = urlParams.get("f"); $("#search-keyword").val(q); f && $("#search-type").val(f); q && this.highlightKeyword(q); } } highlightKeyword(keyword) { const trimedKeyword = keyword.trim(); if (!trimedKeyword) return; const lowerCaseKeyword = trimedKeyword.toLowerCase(); $(".video-title strong, .actor-box strong").each((function () { const $strongElement = $(this); $strongElement.text().toLowerCase().includes(lowerCaseKeyword) && $strongElement.addClass("highlight-red"); })); } hookSearch() { $("#navbar-menu-hero").after('\n \n '); $("#search-keyword").on("paste", ((event) => { const items = event.originalEvent.clipboardData.items; for (let i = 0; i < items.length; i++) if (-1 !== items[i].type.indexOf("image")) { const blob = items[i].getAsFile(); $("#search-keyword").blur(); const imageRecognitionPlugin = this.getBean("ImageRecognitionPlugin"); imageRecognitionPlugin.open((() => { imageRecognitionPlugin.handleImageFile(blob); imageRecognitionPlugin.resetSearchUI(); })); return; } setTimeout((() => { $("#search-btn").click(); }), 0); })).on("keypress", ((event) => { "Enter" === event.key && setTimeout((() => { $("#search-btn").click(); }), 0); })); $("#search-btn").on("click", ((event) => { let keyword = $("#search-keyword").val(), searchCurrentType = $("#search-type option:selected").val(); "" !== keyword && (window.location.href.includes("/search") ? window.location.href = "/search?q=" + keyword + "&f=" + searchCurrentType : window.open("/search?q=" + keyword + "&f=" + searchCurrentType)); })); $("#search-img-btn").on("click", (() => { this.getBean("ImageRecognitionPlugin").open(); })); } hookOldSearch() { const searchImage = document.querySelector(".search-image"); if (!searchImage) return; const clonedImage = searchImage.cloneNode(true); searchImage.parentNode.replaceChild(clonedImage, searchImage); $("#button-search-image").attr("data-tooltip", "以图识图"); $(".search-image").on("click", ((event) => { this.getBean("ImageRecognitionPlugin").open(); })); } margeNav() { $('a[href*="/feedbacks/new"]').remove(); $('a[href*="theporndude.com"]').remove(); $('a.navbar-link[href="/makers"]').parent().after('\n \n '); } toggleOtherNavItem() { let $searchBox = $("#search-box"), $oldSearchBox = $("#search-bar-container"); if ($(window).width() < 1600 && $(window).width() > 1023) { $searchBox.hide(); $oldSearchBox.show(); } if ($(window).width() > 1600) { $searchBox.show(); $oldSearchBox.hide(); } } } class BusDetailPagePlugin extends BasePlugin { getName() { return "BusDetailPagePlugin"; } async initCss() { if (!window.isDetailPage) return ""; $("h4:contains('推薦')").hide(); } async handle() { if (window.location.href.includes("/star/")) { const $avatarBox = $(".avatar-box"); if ($avatarBox.length > 0) { let parent2 = $avatarBox.parent(); parent2.css("position", "initial"); parent2.insertBefore(parent2.parent()); } } $(".genre a").each((function () { const href2 = $(this).attr("href"); href2 && (href2.startsWith("http://") || href2.startsWith("https://") || href2.startsWith("/")) && $(this).attr("target", "_blank"); })); this.addCopyCarNumBtn(); } addCopyCarNumBtn() { let headerSpan = null; const headerSpans = document.querySelectorAll("span.header"); for (const span of headerSpans) if ("識別碼:" === span.textContent.trim()) { headerSpan = span; break; } if (headerSpan) { const targetSpan = headerSpan.nextElementSibling; if (targetSpan && "SPAN" === targetSpan.tagName) { const identifierText = targetSpan.textContent.trim(), copyButton = document.createElement("button"); copyButton.textContent = "复制"; copyButton.style.marginLeft = "10px"; copyButton.style.padding = "0 10px"; copyButton.style.cursor = "pointer"; copyButton.style.border = "1px solid #ccc"; copyButton.style.borderRadius = "5px"; copyButton.style.backgroundColor = "#f0f0f0"; copyButton.style.fontSize = "12px"; copyButton.addEventListener("click", (function (event) { event.preventDefault(); const copyAction = (text) => { this.textContent = "已复制"; setTimeout((() => { this.textContent = "复制"; }), 1500); }; navigator.clipboard && navigator.clipboard.writeText && navigator.clipboard.writeText(identifierText).then((() => copyAction())).catch(((err) => { console.error("无法通过标准API复制:", err); alert("复制失败,请手动复制: " + identifierText); })); })); targetSpan.parentNode.insertBefore(copyButton, targetSpan.nextSibling); } } } } class BusPreviewVideoPlugin extends BasePlugin { getName() { return "BusPreviewVideoPlugin"; } async initCss() { return "\n /* 弹窗/Modal 样式 */\n .bus-preview-modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.95); \n /* 关键修改:更新 z-index */\n z-index: 12345699; \n display: flex;\n justify-content: center;\n align-items: center;\n opacity: 0; \n visibility: hidden; \n transition: opacity 0.2s ease;\n }\n .bus-preview-modal.is-open {\n opacity: 1;\n visibility: visible;\n }\n /* 垂直排列视频和按钮,并居中 */\n .bus-preview-modal-content {\n position: relative;\n max-width: 95%; \n max-height: 95%;\n display: flex; \n flex-direction: column; \n align-items: center; \n gap: 15px; \n }\n \n /* 移除 .bus-preview-close-btn 的样式 */\n\n /* 视频播放器容器 */\n .video-player-wrapper {\n /* 关键修改:更新 width 和 max-height */\n width: 80vw; \n max-height: 85vh; \n aspect-ratio: 16 / 9; \n position: relative; \n background-color: black; \n max-width: 100%; \n }\n /* 视频元素 */\n .video-player-wrapper #preview-video {\n position: absolute; \n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: block;\n }\n\n /* 画质控制盒 (底部按钮) */\n .video-control-box {\n display: flex;\n flex-direction: row; \n justify-content: center; \n flex-wrap: wrap; \n gap: 10px;\n padding: 10px 0; \n }\n\n /* 按钮样式 (保留) */\n .video-control-btn {\n min-width:80px;\n padding: 6px 12px;\n background: rgba(255,255,255,0.2);\n color: white;\n border: 1px solid rgba(255,255,255,0.5);\n border-radius: 4px;\n cursor: pointer;\n text-align: center;\n font-size: 14px;\n transition: background-color 0.2s, border-color 0.2s;\n }\n .video-control-btn:hover {\n background: rgba(255,255,255,0.4);\n }\n .video-control-btn.active {\n background-color: #1890ff; \n color: white;\n font-weight: bold;\n border: 1px solid #096dd9;\n }\n "; } initModal() { if (0 === $("#bus-preview-modal").length) { $("body").append('\n
    \n
    \n
    \n
    \n '); const $modal = $("#bus-preview-modal"); $modal.on("click", ((e) => { "bus-preview-modal" === e.target.id && this.closeVideoModal(); })); $(document).on("keydown", ((e) => { "Escape" === e.key && $modal.hasClass("is-open") && this.closeVideoModal(); })); } } closeVideoModal() { const $previewVideo = $("#preview-video"); if ($previewVideo.length > 0) { const videoEl = $previewVideo[0]; videoEl.pause(); } $("#bus-preview-modal").removeClass("is-open"); } async handle() { if (!window.isDetailPage) return; this.initModal(); const firstImageSrc = $("#sample-waterfall .sample-box .photo-frame img:first").attr("src"), videoPreview = $(`
    ▶
    `); $("#sample-waterfall").prepend(videoPreview); "yes" === await storageManager.getSetting("enableLoadPreviewVideo", "yes") && getDmmVideo(this.getPageInfo().carNum, false).then(); let isHandlingVideo = false, $preview = $(".preview-video-container"); $preview.on("click", (async (event) => { event.preventDefault(); event.stopPropagation(); if (isHandlingVideo) show.info("正在加载中, 勿重复点击"); else { isHandlingVideo = true; try { await this.handleVideo(); } finally { isHandlingVideo = false; } } })); window.location.href.includes("autoPlay=1") && $preview.trigger("click"); } async handleVideo() { const $modal = $("#bus-preview-modal"), $modalContent = $modal.find(".bus-preview-modal-content"); let $previewVideo = $("#preview-video"); if ($previewVideo.length > 0) { $modal.addClass("is-open"); const videoEl = $previewVideo[0]; videoEl.play().catch(((e) => console.warn("尝试播放失败 (可能被浏览器阻止):", e))); return; } let carNum2 = this.getPageInfo().carNum; const dmmVideoMap = await getDmmVideo(carNum2); if (dmmVideoMap && 0 !== Object.keys(dmmVideoMap).length) { await this.createVideoPlayerAndControls(dmmVideoMap, $modalContent); $previewVideo = $("#preview-video"); if ($previewVideo.length > 0) { $modal.addClass("is-open"); $previewVideo[0].play().catch(((e) => console.warn("尝试播放失败 (可能被浏览器阻止):", e))); } else show.error("视频播放器创建失败。"); } else show.error("未找到可用的视频源。"); } async createVideoPlayerAndControls(dmmVideoMap, $container) { let defaultVideoQuality = await storageManager.getSetting("videoQuality"); defaultVideoQuality = selectDefaultQuality(Object.keys(dmmVideoMap), defaultVideoQuality); let defaultVideoUrl = dmmVideoMap[defaultVideoQuality]; $container.html(`
    `); const $videoEl = $("#preview-video"), $qualityControlsBox = $container.find(".video-control-box"); if (!$videoEl.length) return; const videoEl = $videoEl[0]; const jhs_videoMuted = localStorage.getItem("jhs_videoMuted"); videoEl.muted = !jhs_videoMuted || "yes" === jhs_videoMuted; videoEl.addEventListener("volumechange", (function () { localStorage.setItem("jhs_videoMuted", videoEl.muted ? "yes" : "no"); })); let buttonsHtml = ""; qualityOptions.forEach(((option) => { let dmmVideoUrl = dmmVideoMap[option.quality]; if (dmmVideoUrl) { const isActive = defaultVideoQuality === option.quality; buttonsHtml += ` `; } })); $qualityControlsBox.html(buttonsHtml); const $buttons = $qualityControlsBox.find(".video-control-btn"); $qualityControlsBox.off("click").on("click", ".video-control-btn", (async (e) => { try { const $button = $(e.currentTarget); if ($button.hasClass("active")) return; let videoSrc = $button.attr("data-video-src"); const currentTime = videoEl.currentTime; videoEl.src = videoSrc; videoEl.load(); if (currentTime > 0 && Number.isFinite(currentTime)) { videoEl.currentTime = currentTime; } await videoEl.play(); $buttons.removeClass("active"); $button.addClass("active"); } catch (error) { console.error("切换画质失败:", error); } })); } } const HLS_SCRIPT_URL = "https://cdn.jsdelivr.net/npm/hls.js@1.5.18/dist/hls.min.js"; let hlsLoadPromise = null; const getHlsClass = () => { const HlsClass = window.Hls || globalThis.Hls || (typeof Hls !== "undefined" ? Hls : null); return HlsClass && HlsClass.isSupported && HlsClass.isSupported() ? HlsClass : null; }; const binaryTextToArrayBuffer = (value) => { const text = String(value || ""); const bytes = new Uint8Array(text.length); for (let index = 0; index < text.length; index += 1) { bytes[index] = text.charCodeAt(index) & 255; } return bytes.buffer; }; const loadHlsLibrary = () => { const readyHls = getHlsClass(); if (readyHls) return Promise.resolve(readyHls); if (hlsLoadPromise) return hlsLoadPromise; const loadByGm = () => new Promise((resolve) => { _GM_xmlhttpRequest({ method: "GET", url: HLS_SCRIPT_URL, timeout: 15e3, onload: (r) => { if (r.status >= 200 && r.status < 300 && r.responseText) { try { Function(`${r.responseText} //# sourceURL=${HLS_SCRIPT_URL}`).call(globalThis); } catch (err) { clog.error("HLS: hls.js 执行失败", err); } } resolve(getHlsClass()); }, onerror: () => resolve(getHlsClass()), ontimeout: () => resolve(getHlsClass()) }); }); hlsLoadPromise = new Promise((resolve) => { const existing = document.querySelector('script[data-jhs-hls="1"]'); if (existing) { existing.addEventListener("load", () => resolve(getHlsClass()), {once: true}); existing.addEventListener("error", () => loadByGm().then(resolve), {once: true}); setTimeout(() => { if (!getHlsClass()) loadByGm().then(resolve); }, 4e3); return; } const hlsScript = document.createElement("script"); hlsScript.src = HLS_SCRIPT_URL; hlsScript.async = true; hlsScript.dataset.jhsHls = "1"; hlsScript.onload = () => resolve(getHlsClass()); hlsScript.onerror = () => loadByGm().then(resolve); document.head.appendChild(hlsScript); }).then((HlsClass) => { if (!HlsClass) hlsLoadPromise = null; return HlsClass; }); return hlsLoadPromise; }; const createHlsLoader = () => { class GMHlsLoader { constructor(config) { this.config = config; this.context = null; this.callbacks = null; this.loader = null; this.stats = this.createStats(); } createStats() { return { aborted: false, loaded: 0, retry: 0, total: 0, chunkCount: 0, bwEstimate: 0, trequest: 0, tfirst: 0, tload: 0, loading: {start: 0, first: 0, end: 0}, parsing: {start: 0, end: 0}, buffering: {start: 0, first: 0, end: 0} }; } destroy() { this.abort(); } abort() { if (this.stats) this.stats.aborted = true; if (this.loader) { try { this.loader.abort(); } catch (e) { } } this.loader = null; } load(context, config, callbacks) { this.context = context; this.callbacks = callbacks; const requestUrl = context.url; const wantsArrayBuffer = context.responseType === "arraybuffer" || /\.(?:ts|m4s|mp4|key)(?:[?#]|$)/i.test(requestUrl); const startedAt = performance.now(); const stats = this.stats = this.createStats(); stats.trequest = startedAt; stats.tfirst = startedAt; stats.tload = startedAt; stats.loading.start = startedAt; this.loader = _GM_xmlhttpRequest({ method: "GET", url: requestUrl, responseType: "text", overrideMimeType: wantsArrayBuffer ? "text/plain; charset=x-user-defined" : void 0, timeout: config && config.timeout ? config.timeout : 2e4, headers: { Accept: wantsArrayBuffer ? "*/*" : "application/vnd.apple.mpegurl, application/x-mpegURL, */*" }, onprogress: (event) => { stats.loaded = Number(event && event.loaded ? event.loaded : stats.loaded || 0); stats.total = Number(event && event.total ? event.total : stats.total || stats.loaded || 0); if (!stats.loading.first && stats.loaded > 0) { stats.loading.first = performance.now(); } }, onload: (r) => { const status = Number(r.status || 0); const response = { code: status, text: r.statusText || "", url: r.finalUrl || requestUrl }; stats.tfirst = stats.tfirst || performance.now(); stats.tload = performance.now(); stats.loading.first = stats.loading.first || stats.tload; stats.loading.end = stats.tload; if (status < 200 || status >= 300) { callbacks.onError && callbacks.onError(response, context, null, stats); return; } const responseText = r.responseText || r.response || ""; const data = wantsArrayBuffer ? binaryTextToArrayBuffer(responseText) : responseText; stats.loaded = data && data.byteLength ? data.byteLength : data && data.length ? data.length : stats.loaded || 0; stats.total = stats.total || stats.loaded; stats.bwEstimate = stats.loading.end > stats.loading.first ? Math.round(stats.total * 8e3 / (stats.loading.end - stats.loading.first)) : 0; callbacks.onSuccess && callbacks.onSuccess({data, url: response.url}, stats, context, response); }, onerror: () => { callbacks.onError && callbacks.onError({ code: 0, text: "network error", url: requestUrl }, context, null, stats); }, ontimeout: () => { stats.tload = performance.now(); stats.loading.end = stats.tload; callbacks.onTimeout && callbacks.onTimeout(stats, context, null); } }); } } return GMHlsLoader; }; const isM3U8Url = (url) => /\.m3u8(?:[?#].*)?$/i.test(String(url || "")); const attachHlsToVideo = (videoEl, src) => { return new Promise((resolve, reject) => { if (!videoEl || !src) { reject(new Error("video element or source missing")); return; } const HlsClass = getHlsClass(); if (!HlsClass) { videoEl.src = src; videoEl.load && videoEl.load(); resolve(false); return; } const hls = new HlsClass({ enableWorker: false, lowLatencyMode: true, loader: createHlsLoader(), autoStartLoad: true, startPosition: 0, capLevelToPlayerSize: true, testBandwidth: false, preferManagedMediaSource: false, maxBufferLength: 6, maxMaxBufferLength: 12, backBufferLength: 30, maxBufferHole: 0.5, nudgeOffset: 0.1, manifestLoadingMaxRetry: 2, levelLoadingMaxRetry: 2, fragLoadingMaxRetry: 2, manifestLoadingTimeOut: 12e3, levelLoadingTimeOut: 12e3, fragLoadingTimeOut: 12e3, abrEwmaFastLive: 3, abrEwmaSlowLive: 9 }); hls.on(HlsClass.Events.MANIFEST_PARSED, () => { hls.startLoad(0); videoEl.play().catch(() => { }); resolve(true); }); hls.on(HlsClass.Events.ERROR, (_, data) => { if (!data || !data.fatal) return; clog.error("HLS: 播放失败", data); if (data.type === HlsClass.ErrorTypes.MEDIA_ERROR) { if (videoEl.readyState >= 2) { try { hls.recoverMediaError && hls.recoverMediaError(); } catch (e) { } return; } } reject(new Error(data.details || "HLS fatal error")); }); hls.loadSource(src); hls.attachMedia(videoEl); videoEl._hls = hls; }); }; const destroyHls = (videoEl) => { if (videoEl && videoEl._hls) { try { videoEl._hls.destroy(); } catch (e) { } videoEl._hls = null; } }; const COVER_API_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95, 10, 68, 85, 76, 10, 70, 74, 83, 64, 87].map((v) => String.fromCharCode(v ^ 37)).join(""); const TRAILER_API_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95, 10, 68, 85, 76, 10, 81, 87, 68, 76, 73, 64, 87].map((v) => String.fromCharCode(v ^ 37)).join(""); const BASE_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95].map((v) => String.fromCharCode(v ^ 37)).join(""); const COVER_API_TOKEN = [18, 28, 19, 19, 65, 65, 70, 28, 64, 29, 23, 21, 17, 29, 67, 70, 18, 64, 21, 22, 64, 16, 65, 29, 22, 19, 16, 28, 16, 17, 64, 29].map((v) => String.fromCharCode(v ^ 37)).join(""); const getCover = async (code) => { var _a; const query = String(code || "").trim(); if (!query) return null; try { const r = await gmHttp.get(`${COVER_API_URL}/${encodeURIComponent(query)}`, null, { "Authorization": `Bearer ${COVER_API_TOKEN}` }, {timeout: 15e3}); if ((_a = r == null ? void 0 : r.cover) == null ? void 0 : _a.large) { return r.cover.large; } return null; } catch (e) { clog.error("获取封面失败:", e.message); return null; } }; const getTrailer = async (code) => { const query = String(code || "").trim(); if (!query) return null; try { const r = await gmHttp.get(`${TRAILER_API_URL}/${encodeURIComponent(query)}`, null, { "Authorization": `Bearer ${COVER_API_TOKEN}` }, {timeout: 15e3}); if ((r == null ? void 0 : r.trailers) && Array.isArray(r.trailers) && r.trailers.length > 0) { const qualityMap = {}; r.trailers.forEach((item) => { if (item.proxy && item.quality) { qualityMap[item.quality] = `${BASE_URL}${item.proxy}`; } }); if (Object.keys(qualityMap).length > 0) { clog.log("预告片播放地址:", JSON.stringify(qualityMap)); return qualityMap; } } return null; } catch (e) { clog.error("获取预告片失败:", e.message); return null; } }; class JavopenPreviewVideoPlugin extends BasePlugin { getName() { return "JavopenPreviewVideoPlugin"; } _attachVideoSrc(videoEl, src) { if (!videoEl || !src) return; destroyHls(videoEl); if (isM3U8Url(src)) { const HlsClass = getHlsClass(); if (HlsClass) { attachHlsToVideo(videoEl, src).catch((err) => { clog.error("视频预览 HLS 加载失败:", err); }); } else { loadHlsLibrary().then((HlsClass2) => { if (videoEl && videoEl.isConnected && HlsClass2) { attachHlsToVideo(videoEl, src).catch((err) => { clog.error("视频预览 HLS 延迟加载失败:", err); }); } }); } } else { videoEl.src = src; videoEl.load && videoEl.load(); } } _selectDefaultQuality(qualityList, intendedDefault) { if (!qualityList || qualityList.length === 0) return null; const availableSet = new Set(qualityList); if (availableSet.has(intendedDefault)) return intendedDefault; const priorityOrder = qualityOptions.map((o) => o.quality).reverse(); for (const q of priorityOrder) if (availableSet.has(q)) return q; return qualityList[0]; } async initCss() { return ` .jhs-javopen-video-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.95); z-index: 12345700; display: flex; justify-content: center; align-items: center; opacity: 0; visibility: hidden; transition: opacity 0.2s; } .jhs-javopen-video-modal.is-open { opacity: 1; visibility: visible; } .jhs-javopen-video-modal-inner { display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: 90vw; max-height: 90vh; } .jhs-javopen-video-wrapper { width: 80vw; max-height: 80vh; aspect-ratio: 16/9; background: #000; position: relative; } .jhs-javopen-video-wrapper video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .jhs-javopen-quality-bar { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; } .jhs-javopen-quality-btn { min-width: 60px; padding: 5px 10px; font-size: 13px; background: rgba(255,255,255,0.2); color: #fff; border: 1px solid rgba(255,255,255,0.5); border-radius: 4px; cursor: pointer; transition: background 0.2s; } .jhs-javopen-quality-btn:hover { background: rgba(255,255,255,0.4); } .jhs-javopen-quality-btn.active { background: #1890ff; border-color: #096dd9; font-weight: bold; } .jhs-javopen-loading { color: #fff; font-size: 16px; text-align: center; } `; } async handle() { if (!window.isDetailPage) return; const carNum2 = this.getPageInfo().carNum; if (!carNum2) return; this._addJavopenButton(carNum2); } _addJavopenButton(carNum2) { let $target = null; if (isJavDb$1) { $target = $('a[title="複製番號"]'); } else if (isJavBus$1) { const headerSpans = document.querySelectorAll("span.header"); for (const span of headerSpans) { if (span.textContent.trim() === "識別碼:") { const nextSpan = span.nextElementSibling; if (nextSpan && nextSpan.tagName === "SPAN") { const copyBtn = nextSpan.nextElementSibling; if (copyBtn && copyBtn.tagName === "BUTTON" && copyBtn.textContent.trim() === "复制") { $target = $(copyBtn); } else { $target = $(nextSpan); } } break; } } } if (!$target || !$target.length) return; const $btn = $(`视频预览`); $btn.on("click", async (e) => { e.preventDefault(); e.stopPropagation(); $btn.css("pointer-events", "none").find("span").text("..."); try { await this._openJavopenPlayer(carNum2); } finally { $btn.css("pointer-events", "").find("span").text("视频预览"); } }); $target.after($btn); } async _openJavopenPlayer(carNum2) { if ($("#jhs-javopen-video-modal").length === 0) { $("body").append(`
    `); const $modal2 = $("#jhs-javopen-video-modal"); $modal2.on("click", (e) => { if (e.target.id === "jhs-javopen-video-modal") { $modal2.removeClass("is-open"); const videoEl2 = document.getElementById("jhs-javopen-video"); if (videoEl2) { destroyHls(videoEl2); videoEl2.pause(); } } }); $(document).on("keydown", (e) => { if (e.key === "Escape" && $modal2.hasClass("is-open")) { $modal2.removeClass("is-open"); const videoEl2 = document.getElementById("jhs-javopen-video"); if (videoEl2) { destroyHls(videoEl2); videoEl2.pause(); } } }); } const $modal = $("#jhs-javopen-video-modal"); const $wrapper = $modal.find(".jhs-javopen-video-wrapper"); const $qualityBar = $modal.find(".jhs-javopen-quality-bar"); $wrapper.html('
    视频预览加载中...
    '); $qualityBar.empty(); $modal.addClass("is-open"); let videoMap = null; try { videoMap = await getTrailer(carNum2); } catch (err) { $wrapper.html(`
    视频预览请求失败: ${err.message}
    `); return; } if (!videoMap || Object.keys(videoMap).length === 0) { $wrapper.html('
    未找到可用的视频源
    '); return; } const qualityList = Object.keys(videoMap); const defaultQuality = this._selectDefaultQuality(qualityList, "720p"); const defaultUrl = videoMap[defaultQuality]; $wrapper.html(``); const videoEl = document.getElementById("jhs-javopen-video"); if (!videoEl) return; if (isM3U8Url(defaultUrl)) { this._attachVideoSrc(videoEl, defaultUrl); } let buttonsHtml = ""; qualityOptions.forEach((option) => { const url = videoMap[option.quality]; if (url) { buttonsHtml += ``; } }); $qualityBar.html(buttonsHtml); $qualityBar.off("click").on("click", ".jhs-javopen-quality-btn", (e) => { const $btn = $(e.currentTarget); if ($btn.hasClass("active")) return; const src = $btn.attr("data-video-src"); const currentTime = videoEl.currentTime; destroyHls(videoEl); this._attachVideoSrc(videoEl, src); videoEl.load(); if (currentTime > 0 && Number.isFinite(currentTime)) { videoEl.currentTime = currentTime; } videoEl.play().catch(() => { }); $qualityBar.find(".jhs-javopen-quality-btn").removeClass("active"); $btn.addClass("active"); }); videoEl.play().catch((e) => console.warn("视频预览播放失败:", e)); } } class ImageRecognitionPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "siteList", [{ name: "Google旧版", url: "https://www.google.com/searchbyimage?image_url={占位符}&client=firefox-b-d", ico: "https://www.google.com/favicon.ico" }, { name: "Google", url: "https://lens.google.com/uploadbyurl?url={占位符}", ico: "https://www.google.com/favicon.ico" }, { name: "Yandex", url: "https://yandex.ru/images/search?rpt=imageview&url={占位符}", ico: "https://yandex.ru/favicon.ico" }]); __publicField2(this, "isUploading", false); } getName() { return "ImageRecognitionPlugin"; } async initCss() { return "\n \n "; } open(onOpenFun) { layer.open({ type: 1, title: "以图识图", content: '\n
    \n
    \n
    \n

    拖拽图片到此处 或 点击按钮选择图片

    \n

    也可以直接 Ctrl+V 粘贴图片或 图片URL

    \n
    \n \n \n
    \n \n \n \n \n \n
    \n ', area: utils.isMobile() ? utils.getResponsiveArea() : ["40%", "80%"], success: async (layero) => { this.initEventListeners(); onOpenFun && onOpenFun(); }, end: () => { $(document).off("paste.searchImg"); } }); } initEventListeners() { const $uploadArea = $("#upload-area"), $fileInput = $("#image-file"), $selectBtn = $("#select-image-btn"), $previewArea = $("#preview-area"), $previewImage = $("#preview-image"), $actionBtns = $("#action-btns"), $searchImage = $("#handle-btn"), $cancelBtn = $("#cancel-btn"), $urlInputContainer = $("#url-input-container"), $imageUrlInput = $("#image-url"), $searchResults = $("#search-results"), $siteBtnsContainer = $("#search-img-site-btns-container"); $uploadArea.on("dragover", ((e) => { e.preventDefault(); $uploadArea.addClass("highlight"); })).on("dragleave", (() => { $uploadArea.removeClass("highlight"); })).on("drop", ((e) => { e.preventDefault(); $uploadArea.removeClass("highlight"); if (e.originalEvent.dataTransfer.files && e.originalEvent.dataTransfer.files[0]) { this.handleImageFile(e.originalEvent.dataTransfer.files[0]); this.resetSearchUI(); } })); $selectBtn.on("click", (() => { $fileInput.trigger("click"); })); $fileInput.on("change", ((e) => { if (e.target.files && e.target.files[0]) { this.handleImageFile(e.target.files[0]); this.resetSearchUI(); } })); $(document).on("paste.searchImg", (async (e) => { const items = e.originalEvent.clipboardData.items; for (let i = 0; i < items.length; i++) if (-1 !== items[i].type.indexOf("image")) { const blob = items[i].getAsFile(); this.handleImageFile(blob); this.resetSearchUI(); return; } const text = e.originalEvent.clipboardData.getData("text"); if (text && utils.isUrl(text)) { $urlInputContainer.show(); $imageUrlInput.val(text); $previewImage.attr("src", text); $previewArea.show(); this.resetSearchUI(); } })); $searchImage.on("click", (async () => { const imageSrc = $previewImage.attr("src"); if (imageSrc) { if (!this.isUploading) { this.isUploading = true; try { const imgUrl = await this.searchByImage(imageSrc); $actionBtns.hide(); $searchResults.show(); $siteBtnsContainer.empty(); const key = "jhs_selectedSites", selectedSites = JSON.parse(localStorage.getItem(key) || "{}"); this.siteList.forEach(((site) => { const siteUrl = site.url.replace("{占位符}", encodeURIComponent(imgUrl)), isChecked = false !== selectedSites[site.name]; $siteBtnsContainer.append(` ${site.name} ${site.name} `); })); $siteBtnsContainer.on("change", ".site-checkbox", (function () { const siteName = $(this).data("site-name"); selectedSites[siteName] = $(this).is(":checked"); localStorage.setItem(key, JSON.stringify(selectedSites)); })); $siteBtnsContainer.show(); } finally { this.isUploading = false; } } } else show.info("请粘贴或上传图片"); })); $cancelBtn.on("click", (() => { $previewArea.hide(); $urlInputContainer.hide(); $fileInput.val(""); $imageUrlInput.val(""); })); $imageUrlInput.on("change", (() => { if (utils.isUrl($imageUrlInput.val())) { $previewImage.attr("src", $imageUrlInput.val()); $previewArea.show(); } })); $("#openAll").on("click", (() => { $(".search-img-site-btn").each((function () { $(this).find(".site-checkbox").is(":checked") && window.open($(this).attr("href")); })); })); } resetSearchUI() { $("#action-btns").show(); $("#search-results").hide(); $("#search-img-site-btns-container").hide().empty(); } handleImageFile(file) { const previewImage = document.getElementById("preview-image"), previewArea = document.getElementById("preview-area"), urlInputContainer = document.getElementById("url-input-container"); if (!file.type.match("image.*")) { show.info("请选择图片文件"); return; } const reader = new FileReader(); reader.onload = (e) => { previewImage.src = e.target.result; previewArea.style.display = "block"; urlInputContainer.style.display = "none"; $("#handle-btn")[0].click(); }; reader.readAsDataURL(file); } async searchByImage(imageSrc) { let loadObj = loading(); try { let imageUrl = imageSrc; if (imageSrc.startsWith("data:")) { show.info("开始上传图片..."); const imgurUrl = await (async function (base64Data) { var _a2; const matches = base64Data.match(/^data:(.+);base64,(.+)$/); if (!matches || matches.length < 3) throw new Error("无效的Base64图片数据"); const mimeType = matches[1], imageData = matches[2], byteCharacters = atob(imageData), byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) byteNumbers[i] = byteCharacters.charCodeAt(i); const byteArray = new Uint8Array(byteNumbers), blob = new Blob([byteArray], { type: mimeType }), formData = new FormData(); formData.append("image", blob); const idKey = "jhs_imgurClientId"; let clientId = localStorage.getItem(idKey); if (!clientId) { clientId = window.prompt("请输入您自己的Imgur Client-ID(可前往 https://api.imgur.com/oauth2/addclient 免费申请)用于图片上传搜索:"); if (!clientId) throw new Error("未提供Imgur Client-ID,无法上传图片"); localStorage.setItem(idKey, clientId); } const response = await fetch("https://api.imgur.com/3/image", { method: "POST", headers: { Authorization: `Client-ID ${clientId}` }, body: formData }), data = await response.json(); if (data.success && data.data && data.data.link) return data.data.link; throw new Error((null == (_a2 = data.data) ? void 0 : _a2.error) || "上传到Imgur失败"); })(imageSrc); if (!imgurUrl) { show.error("上传到失败"); return; } imageUrl = imgurUrl; } return imageUrl; } catch (error) { show.error(`搜索失败: ${error.message}`); console.error("搜索失败:", error); } finally { loadObj.close(); } } } class BusNavBarPlugin extends BasePlugin { getName() { return "BusNavBarPlugin"; } handle() { $("#navbar > div > div > span").append('\n \n '); $("#search-img-btn").on("click", (() => { this.getBean("ImageRecognitionPlugin").open(); })); } } class RelatedPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "floorIndex", 1); __publicField2(this, "isInit", false); } getName() { return "RelatedPlugin"; } async showRelated($eleBox, movieId) { const enableLoadRelated = await storageManager.getSetting("enableLoadRelated", NO), $magnets = $eleBox; if (movieId) { $magnets.append(`
    相关清单 ${enableLoadRelated === YES$1 ? "折叠" : "展开"} ${enableLoadRelated === YES$1 ? "▲" : "▼"}
    `); $("#relatedFold").on("click", ((event) => { event.preventDefault(); event.stopPropagation(); const $text = $("#relatedFold .toggle-text"), $icon = $("#relatedFold .toggle-icon"), isFolded = "展开" === $text.text(); $text.text(isFolded ? "折叠" : "展开"); $icon.text(isFolded ? "▲" : "▼"); if (isFolded) { $("#relatedContainer").show(); $("#relatedFooter").show(); if (!this.isInit) { this.fetchAndDisplayRelateds(movieId); this.isInit = true; } storageManager.saveSettingItem("enableLoadRelated", YES$1); } else { $("#relatedContainer").hide(); $("#relatedFooter").hide(); storageManager.saveSettingItem("enableLoadRelated", NO); } })); $magnets.append('
    '); $magnets.append('
    '); enableLoadRelated === YES$1 && await this.fetchAndDisplayRelateds(movieId); } else show.error("未传入movieId"); } async fetchAndDisplayRelateds(movieId) { const $relatedContainer = $("#relatedContainer"), $relatedFooter = $("#relatedFooter"); $relatedContainer.append('
    获取清单中...
    '); let dataList = null; try { dataList = await javDbApi.related(movieId, 1, 20); } catch (e) { console.error("获取清单失败:", e); } finally { $("#relatedLoading").remove(); } if (dataList) if (0 !== dataList.length) { this.displayRelateds(dataList, $relatedContainer); if (20 === dataList.length) { $relatedFooter.html('\n \n \n '); let currentPage = 1, $loadMoreRelateds = $("#loadMoreRelateds"); $loadMoreRelateds.on("click", (async () => { $loadMoreRelateds.text("加载中...").prop("disabled", true); currentPage++; let moreData; try { moreData = await javDbApi.related(movieId, currentPage, 20); } catch (e) { console.error("加载更多清单失败:", e); } finally { $loadMoreRelateds.text("加载失败, 请点击重试").prop("disabled", false); } if (moreData) { this.displayRelateds(moreData, $relatedContainer); if (moreData.length < 20) { $loadMoreRelateds.remove(); $("#relatedEnd").show(); } else $loadMoreRelateds.text("加载更多清单").prop("disabled", false); } })); } else $relatedFooter.html('
    已加载全部清单
    '); } else $relatedContainer.append('
    无清单
    '); else { $relatedContainer.append('\n
    \n 获取清单失败\n 重试\n
    \n '); $("#retryFetchRelateds").on("click", (async () => { $("#retryFetchRelateds").parent().remove(); await this.fetchAndDisplayRelateds(movieId); })); } } displayRelateds(dataList, $container) { dataList.length && dataList.forEach(((item) => { let commentHtml = `
    #${this.floorIndex++} 创建时间: ${item.createTime}

    ${item.name}

    视频个数: ${item.movieCount}

    收藏次数: ${item.collectionCount} 被查看次数: ${item.viewCount}

    `; $container.append(commentHtml); })); } } class WantAndWatchedVideosPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "type", null); } getName() { return "WantAndWatchedVideosPlugin"; } async handle() { if (window.location.href.includes("/want_watch_videos")) { $("h3").append('导入至 JHS'); $("#wantWatchBtn").on("click", ((event) => { this.type = Status_FAVORITE$1; this.importWantWatchVideos(event, "是否将 想看的影片 导入到 JHS-收藏?"); })); } if (window.location.href.includes("/watched_videos")) { $("h3").append('导入至 JHS'); $("#wantWatchBtn").on("click", ((event) => { this.type = Status_HAS_DOWN$1; this.importWantWatchVideos(event, "是否将 看过的影片 导入到 JHS-已下载?"); })); } } importWantWatchVideos(event, title) { utils.q(null, `${title}
    执行此功能前请记得备份数据`, (async () => { let loadObj = loading(); try { await this.parseMovieList(); } catch (e) { console.error(e); } finally { loadObj.close(); } })); } async parseMovieList($dom) { let movieList, nextPageLink; if ($dom) { movieList = $dom.find(this.getSelector().itemSelector); nextPageLink = $dom.find(".pagination-next").attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(".pagination-next").attr("href"); } for (const element of movieList) { const item = $(element), href = item.find("a").attr("href"), carNum2 = item.find(".video-title strong").text().trim(), publishTime = item.find(".meta").text().trim(); if (href && carNum2) try { if (await storageManager.getCar(carNum2)) { show.info(`${carNum2} 已存在, 跳过`); continue; } await storageManager.saveCar({ carNum: carNum2, url: href, names: null, actionType: this.type, publishTime }); } catch (error) { console.error(`保存失败 [${carNum2}]:`, error); } } if (nextPageLink) { show.info("发现下一页,正在解析:", nextPageLink); await new Promise(((resolve) => setTimeout(resolve, 1e3))); $.ajax({ url: nextPageLink, method: "GET", success: (html) => { const parser = new DOMParser(), next$dom = $(parser.parseFromString(html, "text/html")); this.parseMovieList(next$dom); }, error: function (err) { console.error(err); show.error("加载下一页失败:" + err.message); } }); } else { show.ok("导入结束!"); window.refresh(); } } } function showCopiedFeedback($btn) { const originalText = $btn.text(); $btn.addClass("copied").text("已复制"); setTimeout((() => { $btn.removeClass("copied").text(originalText); }), 2e3); } function fallbackCopy(text, $btn) { const textarea = document.createElement("textarea"); textarea.value = text; textarea.style.position = "fixed"; document.body.appendChild(textarea); textarea.select(); try { document.execCommand("copy"); showCopiedFeedback($btn); } catch (err) { console.error("复制失败:", err); alert("复制失败,请手动复制链接"); } document.body.removeChild(textarea); } function parseSize(sizeStr) { if (!sizeStr || sizeStr === "未知") return 0; const match = String(sizeStr).replace(/,/g, "").match(/([\d.]+)\s*(TiB|GiB|MiB|KiB|TB|GB|MB|KB|B)?/i); if (!match) return 0; const num = parseFloat(match[1]); const unit = (match[2] || "B").toUpperCase(); const multipliers = { TIB: 1099511627776, TB: 1099511627776, GIB: 1073741824, GB: 1073741824, MIB: 1048576, MB: 1048576, KIB: 1024, KB: 1024, B: 1 }; return Number.isFinite(num) ? num * (multipliers[unit] || 1) : 0; } function parseDate(dateStr) { if (!dateStr || dateStr === "未知") return 0; const ts = Date.parse(dateStr.replace(/\//g, "-")); return isNaN(ts) ? 0 : ts; } function formatMagnetDate(dateStr) { if (!dateStr) return ""; const match = String(dateStr).match(/(\d{4})[-/](\d{1,2})[-/](\d{1,2})/); if (match) return `${match[1]}-${match[2].padStart(2, "0")}-${match[3].padStart(2, "0")}`; const ts = parseDate(dateStr); return ts ? new Date(ts).toISOString().slice(0, 10) : dateStr; } function classifyQuality(title) { const text = String(title || "").replace(/【[^】]*(?:APP|夸克|UC搜|(?:[a-z0-9-]+\.)+(?:com|net|org|cc|sbs|top))[^】]*】/gi, " "); const hasCJK = /[\u4e00-\u9fff]/.test(text); const hasJP = /[\u3040-\u309f\u30a0-\u30ff]/.test(text); const isChinese = /(?:[^A-Za-z]|^)FHDC(?:[^A-Za-z]|$)/i.test(text) || /[-_](?:UC|CH?)(?:[^A-Za-z]|$)/.test(text) || /(?:\u4e2d\u5b57|\u4e2d\u6587|\u5b57\u5e55|\u4e2d\u6587\u5b57\u5e55|\u7e41\u9ad4\u4e2d\u5b57|\u7e41\u4f53\u4e2d\u5b57|\u7e41\u9ad4\u4e2d\u6587|\u7e41\u4f53\u4e2d\u6587|\u7e41\u9ad4\u5b57\u5e55|\u7e41\u4f53\u5b57\u5e55|\u7e41\u4e2d|\u7e41\u5b57|\u81ea\u63d0|\u5f81\u7528|\u5fb5\u7528|\u6f22\u5316|\u6c49\u5316|\u5167\u5d4c|\u5185\u5d4c|\u5167\u5c01|\u5185\u5c01|\u96d9\u8a9e|\u53cc\u8bed)/.test(text) || hasCJK && !hasJP; const is4K = /(?:[^A-Za-z0-9]|^)(?:4K(?:UHD)?|2160P)(?:[^A-Za-z0-9]|$)/i.test(text); const isCracked = /(?:\u7834\u89e3|\u7834\u574f|\u7834\u58de|\u7834\u58ca|\u65e0\u7801|\u7121\u78bc)/.test(text) || /\b(?:uncensored|mosaic)\b/i.test(text); return {isChinese, is4K, isCracked}; } function gmFetch(url, opts = {}) { return new Promise(((resolve) => { _GM_xmlhttpRequest({ method: opts.method || "GET", url, headers: opts.headers || {}, data: opts.data, onload: (r) => { resolve({ loadstuts: r.status >= 200 && r.status < 300, status: r.status, responseText: r.responseText, finalUrl: r.finalUrl || url }); }, onerror: () => resolve({loadstuts: false, status: 0, responseText: "", finalUrl: url}) }); })); } function parseHTML(html) { const parser = new DOMParser(); return parser.parseFromString(html, "text/html"); } class MagnetHubPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "currentEngine", null); __publicField2(this, "searchEngines", [ { name: "Sukebei", id: "Sukebei", url: "https://sukebei.nyaa.si/?f=0&c=0_0&q={keyword}", targetPage: "https://sukebei.nyaa.si/?f=0&c=0_0&q={keyword}", parseHtml: this.parseSukebei }, { name: "U9A9", id: "u9a9", url: "https://u9a9.com/?type=2&search={keyword}", targetPage: "https://u9a9.com/?type=2&search={keyword}", parseHtml: this.parseU9A9 }, { name: "U3C3", id: "u3c3", url: "https://u3c3.com/?search={keyword}", targetPage: "https://u3c3.com/?search={keyword}", parseHtml: this.parseU3C3 }, { name: "BTSearch", id: "btsearch", url: "https://btsearch.love/api/search?keyword={keyword}&limit=10&offset=0&sort=size&sort_type=desc", targetPage: "https://btsearch.love/search?keyword={keyword}", parseJson: this.parseBTSearch }, { name: "SoKitty", id: "sokitty", url: "https://w1.sokitty.me/search?key={keyword}", targetPage: "https://w1.sokitty.me/search?key={keyword}", parseHtml: this.parseSokitty }, { name: "ØMagnet", id: "omag", url: "https://xn--mag-zna.net/search?q={keyword}", targetPage: "https://xn--mag-zna.net/search?q={keyword}", parseHtml: this.parseOmag } ]); } getName() { return "MagnetHubPlugin"; } async initCss() { return ` `; } createMagnetHub(keyword) { keyword = keyword.replace("FC2-", ""); const $container = $('
    '); const $tabs = $('
    '); const key = "jhs_magnetHub_selectedEngine"; const savedEngineId = localStorage.getItem(key); let defaultEngineIndex = 0; const $tabBox = $('
    '); this.searchEngines.forEach(((engine, index) => { const $tab = $(`
    ${engine.name}
    `); if (savedEngineId && engine.id === savedEngineId) { $tab.addClass("active"); this.currentEngine = engine; defaultEngineIndex = index; } else if (!savedEngineId && engine.id === "Sukebei") { $tab.addClass("active"); this.currentEngine = engine; } $tabBox.append($tab); })); const sortMode = localStorage.getItem("jhs_magnetSort") || "size"; const $sortSelect = $(``); $tabBox.append($sortSelect); $tabs.append($tabBox); $tabs.append(`原网页`); $container.append($tabs); const $resultsContainer = $('
    '); $container.append($resultsContainer); $container.on("click", ".magnet-tab", ((e) => { const engineId = $(e.target).data("engine"); this.currentEngine = this.searchEngines.find(((engine) => engine.id === engineId)); this.resolveUrl(this.currentEngine, keyword).then(((url) => { $("#targetBox").attr("href", url); })); localStorage.setItem(key, engineId); $container.find(".magnet-tab").removeClass("active"); $(e.target).addClass("active"); this.searchEngine($resultsContainer, $sortSelect, this.currentEngine, keyword); })); $sortSelect.on("change", () => { localStorage.setItem("jhs_magnetSort", $sortSelect.val()); RerenderSort($resultsContainer, $sortSelect.val()); }); $container.on("click", ".copy-btn", function () { const $btn = $(this), magnet = $btn.data("magnet"); const dnCode = $btn.data("dncode") || ""; const magWithDn = magnet + (dnCode ? `&dn=${encodeURIComponent(dnCode)}` : ""); navigator.clipboard ? navigator.clipboard.writeText(magWithDn).then((() => { showCopiedFeedback($btn); })).catch((() => { fallbackCopy(magWithDn, $btn); })) : fallbackCopy(magWithDn, $btn); }); $container.on("click", ".down-115", (async (event) => { event.stopPropagation(); const magnet = $(event.currentTarget).data("magnet"); let loadObj = loading(); try { await this.getBean("WangPan115TaskPlugin").handleAddTask(magnet); } catch (e) { show.error("发生错误:" + e); console.error(e); } finally { loadObj.close(); } })); $container.on("click", ".check-btn", (async (event) => { event.stopPropagation(); const magnet = $(event.currentTarget).data("magnet"); this.checkWhatslink(magnet); })); this.searchEngine($resultsContainer, $sortSelect, this.currentEngine || this.searchEngines[defaultEngineIndex], keyword); this.resolveUrl(this.currentEngine || this.searchEngines[defaultEngineIndex], keyword).then(((url) => { $("#targetBox").attr("href", url); })); return $container; } searchEngine($container, $sortSelect, engine, keyword) { this.currentEngine = engine; this.currentKeyword = keyword; $container.html(`
    正在从 ${engine.name} 搜索 "${keyword}"...
    `); const cacheKey = `${engine.name}_${keyword}`; sessionStorage.getItem(cacheKey); this.resolveUrl(engine, keyword).then(((url) => { const requestOpts = {method: "GET", url}; if (engine.parseHtml) { _GM_xmlhttpRequest(Object.assign(requestOpts, { method: "GET", url, onload: async (response) => { try { const results = await engine.parseHtml.call(this, response.responseText, keyword); results.length > 0 && sessionStorage.setItem(cacheKey, JSON.stringify(results)); $container.data("_magnetData", results); $sortSelect.prop("disabled", results.length === 0); this.displayResults($container, results, engine.name); } catch (e) { this.showError($container, `解析 ${engine.name} 结果失败: ${e.message}`); } }, onerror: (error) => { this.showError($container, `从 ${engine.name} 获取数据失败: ${error.statusText}`); } })); } if (engine.parseJson) { engine.parseJson.call(this, $container, $sortSelect, engine, keyword, cacheKey); } })); } showError($container, message) { $container.html(`
    ${message} 🔄 刷新
    `); const $select = $container.closest(".magnet-container").find(".magnet-sort-select"); $container.find("#retrySearch").on("click", ((e) => { e.preventDefault(); this.searchEngine($container, $select, this.currentEngine, this.currentKeyword); })); } displayResults($container, results, engineName) { $container.empty(); const sortMode = localStorage.getItem("jhs_magnetSort") || "size"; if (0 !== results.length) { results = this.sortMagnetData(results, sortMode); const $table = $(`
    名称 大小 日期 操作
    `); const $tbody = $table.find("tbody"); results.forEach(((result) => { const quality = classifyQuality(result.title); let badges = ""; if (quality.is4K) badges += '4K'; if (quality.isChinese) badges += '中字'; if (quality.isCracked) badges += '破解'; const displayDate = formatMagnetDate(result.date); const dnCode = extractCode(result.title); const $row = $(`
    ${badges} ${result.title}
    ${result.size || "未知"} ${displayDate || "未知"} `); $tbody.append($row); })); $container.append($table); } else { $container.append(`
    无搜索结果 🔄 刷新
    `); $("#retrySearch").on("click", ((e) => { e.preventDefault(); this.searchEngine($container, $container.closest(".magnet-container").find(".magnet-sort-select"), this.currentEngine, this.currentKeyword); })); } } sortMagnetData(data, mode) { return [...data].sort((a, b) => { const sizeDelta = parseSize(b.size) - parseSize(a.size); if (mode === "size") return sizeDelta; const aTime = parseDate(a.date); const bTime = parseDate(b.date); if (!aTime && !bTime) return sizeDelta; if (!aTime) return 1; if (!bTime) return -1; const timeDelta = mode === "oldest" ? aTime - bTime : bTime - aTime; return timeDelta || sizeDelta; }); } async resolveUrl(engine, keyword) { return engine.url.replace("{keyword}", encodeURIComponent(keyword)); } async checkWhatslink(magnet) { var _a; (_a = document.querySelector(".whatslink-overlay")) == null ? void 0 : _a.remove(); const overlay = document.createElement("div"); overlay.className = "whatslink-overlay"; overlay.innerHTML = ''; document.body.appendChild(overlay); try { const url = `https://whatslink.info/api/v1/link?url=${encodeURIComponent(magnet)}`; const r = await gmFetch(url, {timeout: 2e4}); if (!r.loadstuts) throw new Error("WhatsLink 请求失败"); const data = JSON.parse(r.responseText || "{}"); overlay.remove(); this.showWhatslinkModal(data, magnet); } catch (e) { overlay.remove(); this.showWhatslinkModal({ error: e.message || "查询失败", name: "查询失败", type: "-", file_type: "-", size: 0, count: "-", screenshots: [] }, magnet); } } showWhatslinkModal(payload, magnet) { var _a; (_a = document.querySelector(".whatslink-overlay")) == null ? void 0 : _a.remove(); const shots = Array.isArray(payload == null ? void 0 : payload.screenshots) ? payload.screenshots.map((item) => item == null ? void 0 : item.screenshot).filter(Boolean) : []; let index = 0; const resourceName = (payload == null ? void 0 : payload.name) || "未知资源"; const resourceCount = (payload == null ? void 0 : payload.count) ?? "-"; const overlay = document.createElement("div"); overlay.className = "whatslink-overlay"; const modal = document.createElement("section"); modal.className = `whatslink-modal${shots.length ? "" : " no-shots"}`; modal.innerHTML = ``; overlay.appendChild(modal); document.body.appendChild(overlay); modal.querySelector(".whatslink-gallery-name").textContent = resourceName; modal.querySelector(".whatslink-gallery-count").textContent = `${resourceCount} 个文件`; const visual = modal.querySelector(".whatslink-gallery-visual"); const hero = modal.querySelector(".whatslink-gallery-hero"); const thumbs = modal.querySelector(".whatslink-gallery-thumbs"); const currentIndex = modal.querySelector(".whatslink-gallery-index"); const closeButton = modal.querySelector(".whatslink-gallery-close"); const prevButton = modal.querySelector(".whatslink-gallery-prev"); const nextButton = modal.querySelector(".whatslink-gallery-next"); const sizeGallery = () => { if (!shots.length || !hero.naturalWidth || !hero.naturalHeight) return; const infoHeight = modal.querySelector(".whatslink-gallery-info").offsetHeight || 23; const thumbsHeight = thumbs.offsetHeight || 58; const maxHeight = Math.max(180, window.innerHeight - 36 - infoHeight - thumbsHeight - 6); const maxWidth = modal.clientWidth; const ratio = hero.naturalWidth / hero.naturalHeight; const height = Math.min(maxHeight, maxWidth / ratio); const width = height * ratio; visual.style.width = `${width}px`; visual.style.height = `${height}px`; }; const positionControls = () => { if (!shots.length || !hero.naturalWidth || !hero.naturalHeight) return; const width = visual.clientWidth; const height = visual.clientHeight; const scale = Math.min(width / hero.naturalWidth, height / hero.naturalHeight); const imageWidth = hero.naturalWidth * scale; const imageHeight = hero.naturalHeight * scale; const imageLeft = (width - imageWidth) / 2; const imageTop = (height - imageHeight) / 2; const closeSize = closeButton.offsetWidth || 32; const arrowSize = prevButton.offsetWidth || 34; const sideGap = 12; closeButton.style.left = `${imageLeft + imageWidth - closeSize - sideGap}px`; closeButton.style.right = "auto"; closeButton.style.top = `${imageTop + sideGap}px`; prevButton.style.left = `${imageLeft + sideGap}px`; prevButton.style.right = "auto"; prevButton.style.top = `${imageTop + (imageHeight - arrowSize) / 2}px`; nextButton.style.left = "auto"; nextButton.style.right = `${width - imageLeft - imageWidth + sideGap}px`; nextButton.style.top = `${imageTop + (imageHeight - arrowSize) / 2}px`; }; const render = () => { modal.classList.toggle("has-shots", Boolean(shots.length)); if (!shots.length) { currentIndex.textContent = "NO PREVIEW"; return; } hero.src = shots[index]; const frame = `${index + 1} / ${shots.length}`; currentIndex.textContent = frame; [...thumbs.children].forEach((btn, i) => btn.classList.toggle("active", i === index)); if (hero.complete) { sizeGallery(); positionControls(); } }; shots.forEach((url, i) => { const btn = document.createElement("button"); btn.type = "button"; btn.className = "whatslink-gallery-thumb"; btn.innerHTML = `截图${i + 1}`; btn.addEventListener("click", () => { index = i; render(); }); thumbs.appendChild(btn); }); prevButton.addEventListener("click", () => { if (!shots.length) return; index = (index + shots.length - 1) % shots.length; render(); }); nextButton.addEventListener("click", () => { if (!shots.length) return; index = (index + 1) % shots.length; render(); }); const onKeydown = (event) => { if (!document.body.contains(overlay)) return; if (event.key === "ArrowLeft" && shots.length) { index = (index + shots.length - 1) % shots.length; render(); } if (event.key === "ArrowRight" && shots.length) { index = (index + 1) % shots.length; render(); } if (event.key === "Escape") close(); }; const onResize = () => { sizeGallery(); positionControls(); }; const removeResize = () => window.removeEventListener("resize", onResize); const close = () => { overlay.remove(); document.removeEventListener("keydown", onKeydown); removeResize(); }; closeButton.addEventListener("click", close); overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); }); hero.addEventListener("load", () => { sizeGallery(); positionControls(); }); window.addEventListener("resize", onResize); document.addEventListener("keydown", onKeydown); render(); } parseU9A9(html, keyword) { const $dom = utils.htmlTo$dom(html), results = []; const kwNorm = String(keyword).toUpperCase().replace(/[^A-Z0-9]/g, ""); $dom.find(".torrent-list tbody tr").each(((i, el) => { const $el = $(el); if ($el.text().includes("置顶")) return; const $titleA = $el.find("td:nth-child(2) a").first(); const title = $titleA.attr("title") || $titleA.text().trim(); if (!title) return; if (kwNorm) { const titleNorm = title.toUpperCase().replace(/[^A-Z0-9]/g, ""); if (!titleNorm.includes(kwNorm)) return; } const magnet = $el.find("td:nth-child(3) a[href^='magnet:']").attr("href"); const size = $el.find("td:nth-child(4)").text().trim(); const date = $el.find("td:nth-child(5)").text().trim(); magnet && results.push({title, magnet, size, date}); })); return results; } async parseU3C3(_html, keyword) { var _a; const base = "https://www.u3c3.com"; const referer = base + "/"; const home = await gmFetch(base + "/", { headers: {Referer: referer} }); if (!home.loadstuts) return []; const homeDoc = parseHTML(home.responseText); const searchScript = [...homeDoc.scripts].map((script) => script.textContent || "").find((text) => text.includes("function search21")) || ""; const token = ((_a = searchScript.match(/^\s*var\s+nmefafej\s*=\s*["']([^"']+)["'];?/m)) == null ? void 0 : _a[1]) || ""; if (!token) return []; const searchUrl = `${base}/?search2=${encodeURIComponent(token)}&search=${encodeURIComponent(keyword)}`; const r = await gmFetch(searchUrl, { headers: {Referer: referer} }); if (!r.loadstuts) return []; const doc = parseHTML(r.responseText); const normalizedKeyword = String(keyword || "").toUpperCase().replace(/[-_\s]/g, ""); const data = [...doc.querySelectorAll("table.torrent-list tbody tr.default, table.torrent-list tbody tr.success")].map((row) => { var _a2, _b, _c, _d, _e, _f; const titleA = row.querySelector('td:nth-child(2) a[href*="/view?id="]'); const magnetA = row.querySelector('td:nth-child(3) a[href^="magnet:"]'); const title = ((_a2 = titleA == null ? void 0 : titleA.getAttribute("title")) == null ? void 0 : _a2.trim()) || ((_b = titleA == null ? void 0 : titleA.textContent) == null ? void 0 : _b.trim()) || ""; if (!title || !(magnetA == null ? void 0 : magnetA.href)) return null; if (normalizedKeyword && !title.toUpperCase().replace(/[-_\s]/g, "").includes(normalizedKeyword)) return null; const href = titleA.getAttribute("href") || ""; return { title, magnet: magnetA.href, size: ((_d = (_c = row.querySelector("td:nth-child(4)")) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim()) || "", date: ((_f = (_e = row.querySelector("td:nth-child(5)")) == null ? void 0 : _e.textContent) == null ? void 0 : _f.trim()) || "", src: href ? new URL(href, base).href : searchUrl }; }).filter(Boolean); return data; } parseSukebei(html, keyword) { const $dom = utils.htmlTo$dom(html), results = []; $dom.find("tr.default, tr.success").each(((i, el) => { const $el = $(el); const title = $el.find("td:nth-child(2) a").first().attr("title") || $el.find("td:nth-child(2) a").first().text().trim(); if (!title) return; const magnet = $el.find("td:nth-child(3) a:last-child").attr("href"); const size = $el.find("td:nth-child(4)").text().trim(); const date = $el.find("td:nth-child(5)").text().trim(); magnet && results.push({title, magnet, size, date}); })); return results; } async parseOmag(html, keyword) { const doc = parseHTML(html); const base = "https://xn--mag-zna.net"; const entries = [...doc.querySelectorAll("tr")].map((row) => { const titleA = row.querySelector('td.result-title a[href^="/!"]'); if (!titleA) return null; const href = titleA.getAttribute("href") || ""; if (!href) return null; let src; try { src = new URL(href, base).href; } catch (_) { return null; } const title = titleA.textContent.trim(); const meta = [...row.querySelectorAll(".result-meta > div")].map((item) => item.textContent.trim()); return {title, size: meta[0] || "", date: meta[1] || "", src}; }).filter(Boolean); const searchUrl = `https://xn--mag-zna.net/search?q=${encodeURIComponent(keyword)}`; const results = []; for (const entry of entries) { try { const r = await gmFetch(entry.src, { headers: {Referer: searchUrl} }); if (!r.loadstuts) continue; const detailDoc = parseHTML(r.responseText); const magnetInput = detailDoc.querySelector("#input-magnet"); const magnetA = detailDoc.querySelector('a[href^="magnet:"]'); const maglink = ((magnetInput == null ? void 0 : magnetInput.getAttribute("value")) || (magnetA == null ? void 0 : magnetA.getAttribute("href")) || (magnetA == null ? void 0 : magnetA.href) || "").trim(); if (!maglink || !/^magnet:\?xt=urn:btih:/i.test(maglink)) continue; results.push({title: entry.title, magnet: maglink, size: entry.size, date: entry.date}); } catch (_) { } } return results; } parseBTSearch($container, $sortSelect, engine, keyword, cacheKey) { const _this = this; const apiUrl2 = engine.url.replace("{keyword}", encodeURIComponent(keyword)); const timestamp = Math.floor(Date.now() / 1e3).toString(); const nonce = randomString(8); const parts = [`timestamp=${timestamp}`, `nonce=${nonce}`, `keyword=${keyword}`, `limit=10`, `offset=0`, `sort=size`, `sort_type=desc`]; const signText = `${parts.sort().join("&")}&key=long2ice`; const sign = window.md5 ? window.md5(signText).toUpperCase() : ""; _GM_xmlhttpRequest({ method: "GET", url: apiUrl2, headers: { Accept: "application/json", Referer: engine.targetPage.replace("{keyword}", encodeURIComponent(keyword)), "x-timestamp": timestamp, "x-nonce": nonce, "x-sign": sign }, onload: (response) => { var _a; try { const json = JSON.parse(response.responseText); const items = Array.isArray(json == null ? void 0 : json.data) ? json.data : Array.isArray((_a = json == null ? void 0 : json.data) == null ? void 0 : _a.data) ? json.data.data : []; const results = items.map((item) => { const title = String((item == null ? void 0 : item.name) || "").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim(); const hash = String((item == null ? void 0 : item.hash) || "").replace(/^magnet:\?xt=urn:btih:/i, "").replace(/[^a-z0-9]/gi, ""); if (!/^[a-f0-9]{32,40}$/i.test(hash)) return null; const size = formatBytes(item == null ? void 0 : item.size); const date = (item == null ? void 0 : item.created_at) || (item == null ? void 0 : item.created_time) || (item == null ? void 0 : item.create_time) || ""; return { title: title || `magnet:?xt=urn:btih:${hash}`, magnet: `magnet:?xt=urn:btih:${hash}`, size, date }; }).filter(Boolean); results.length > 0 && sessionStorage.setItem(cacheKey, JSON.stringify(results)); $container.data("_magnetData", results); $sortSelect.prop("disabled", results.length === 0); _this.displayResults($container, results, engine.name); } catch (e) { _this.showError($container, `解析 ${engine.name} 结果失败: ${e.message}`); } }, onerror: (error) => { _this.showError($container, `从 ${engine.name} 获取数据失败: ${error.statusText}`); } }); } parseSokitty(html, keyword) { const doc = parseHTML(html); const kwNorm = String(keyword).toUpperCase().replace(/[-_\s]/g, ""); const results = []; doc.querySelectorAll(".panel.search-panel").forEach((panel) => { var _a, _b, _c, _d; const titleA = panel.querySelector("h3.panel-title > a.list-title"); if (!titleA) return; const href = titleA.getAttribute("href") || ""; if (!href.startsWith("/bt/")) return; const hash = href.replace("/bt/", ""); if (!hash) return; const title = titleA.textContent.trim(); if (!title.toUpperCase().replace(/[-_\s]/g, "").includes(kwNorm)) return; const maglink = `magnet:?xt=urn:btih:${hash}`; const infoItems = [...panel.querySelectorAll(".panel-footer .info-item")]; const size = ((_b = (_a = infoItems[0]) == null ? void 0 : _a.textContent) == null ? void 0 : _b.trim()) || ""; const date = ((_d = (_c = infoItems[2]) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim()) || ""; results.push({title, magnet: maglink, size, date}); }); return results; } } function formatBytes(bytes) { if (!bytes || bytes <= 0) return ""; const units = ["B", "KB", "MB", "GB", "TB"]; let i = 0; let size = bytes; while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; } return size.toFixed(1) + " " + units[i]; } function extractCode(text) { if (!text) return null; const patterns = [ /FC2[-\s_]?(?:PPV)?[-\s_]?(\d{6,9})/i, /([A-Z]{2,15})-(\d{2,10})(?:-(\d+))?/i, /([A-Z]{2,15})-([A-Z]{0,2}\d{2,10})/i, /^[A-Z0-9]+[-_](\d{6}[-_]\d{2,3})/i, /(\d{6}[-_]\d{2,3})[-_][A-Z0-9]+$/i, new RegExp("(? { const sizeDelta = parseSize(b.size) - parseSize(a.size); if (sortMode === "size") return sizeDelta; const aTime = parseDate(a.date), bTime = parseDate(b.date); if (!aTime && !bTime) return sizeDelta; if (!aTime) return 1; if (!bTime) return -1; const timeDelta = sortMode === "oldest" ? aTime - bTime : bTime - aTime; return timeDelta || sizeDelta; }); $container.empty(); const $table = $(`
    名称 大小 日期 操作
    `); const $tbody = $table.find("tbody"); results.forEach(((result) => { const quality = classifyQuality(result.title); let badges = ""; if (quality.is4K) badges += '4K'; if (quality.isChinese) badges += '中字'; if (quality.isCracked) badges += '破解'; const displayDate = formatMagnetDate(result.date); const dnCode = extractCode(result.title); const $row = $(`
    ${badges} ${result.title}
    ${result.size || "未知"} ${displayDate || "未知"} `); $tbody.append($row); })); $container.append($table); } const Thumbnail = { sources: ["javfree", "projectjav", "javstore"], cacheKey(code) { return `thumb_cache_v3_${code}`; }, lookupCode(code) { const text = String(code || "").trim(); const fc2 = text.match(/^(?:FC2[-_\s]?(?:PPV[-_\s]?)?)?(\d{6,9})$/i); return fc2 ? fc2[1] : text; }, sourceOrder() { const savedOrder = ["javfree", "projectjav", "javstore"]; const seen2 = /* @__PURE__ */ new Set(); return [...savedOrder, ...this.sources].filter((src) => { if (seen2.has(src) || typeof this[src] !== "function") return false; seen2.add(src); return true; }); }, async fetchFromSource(source, code) { try { return await this[source](this.lookupCode(code)); } catch (e) { clog.debug(`Thumbnail[${source}] 异常:`, e.message); return null; } }, normalizeForCompare(text) { return String(text || "").toLowerCase().replace(/[^a-z0-9]/g, ""); }, isCodeMatched(text, code) { const normalizedText = this.normalizeForCompare(text); const normalizedCode = this.normalizeForCompare(code); return !!normalizedCode && normalizedText.includes(normalizedCode); }, isDetailMatched(doc, url, code) { var _a, _b; const title = ((_a = doc == null ? void 0 : doc.querySelector("title")) == null ? void 0 : _a.textContent) || ""; const headings = [...(doc == null ? void 0 : doc.querySelectorAll("h1,h2,h3,.entry-title,.movie-title,.post-title")) || []].map((el) => el.textContent || "").join(" "); const bodyText = (((_b = doc == null ? void 0 : doc.body) == null ? void 0 : _b.textContent) || "").slice(0, 5e3); return this.isCodeMatched([url, title, headings, bodyText].join(" "), code); }, normalizePreviewUrl(url, baseUrl = "") { if (!url) return ""; const absolute = /^https?:\/\//i.test(url) ? url : baseUrl ? new URL(url, baseUrl).href : url; return absolute.replace(/^http:/, "https:"); }, isJavfreePreviewImage(url, code) { const cleanUrl = String(url || "").split("?")[0]; const lookupCode = this.lookupCode(code); const isFc2Numeric = /^\d{6,9}$/.test(lookupCode); const fc2ShotPattern = isFc2Numeric ? new RegExp(`${lookupCode}_\\d+\\.(?:jpe?g|png|webp)$`, "i") : null; return this.isCodeMatched(cleanUrl, code) && (/-(?:1080p|demosaic)\.(?:jpe?g|png|webp)$/i.test(cleanUrl) || isFc2Numeric && fc2ShotPattern.test(cleanUrl)); }, selectJavfreePreviewUrl(doc, detailUrl, code) { const urls = [...doc.querySelectorAll("p > img[src]")].map((img) => this.normalizePreviewUrl(img.getAttribute("src") || img.src || "", detailUrl)).filter((url) => this.isJavfreePreviewImage(url, code)); return urls.find((url) => /-1080p\./i.test(url)) || urls.find((url) => /-demosaic\./i.test(url)) || urls.find((url) => /_1\.(?:jpe?g|png|webp)$/i.test(url)) || ""; }, async javfree(code) { var _a; code = this.lookupCode(code); const cacheKey = this.cacheKey(code); const cached = sessionStorage.getItem(cacheKey); if (cached) return cached; try { const html = await gmHttp.get(`https://javfree.me/search/${code}`); const doc = new DOMParser().parseFromString(html, "text/html"); const link = (_a = [...doc.querySelectorAll(".entry-title>a")].find((a) => this.isCodeMatched([a.href, a.textContent].join(" "), code))) == null ? void 0 : _a.href; if (!link) return null; const dHtml = await gmHttp.get(link); const dDoc = new DOMParser().parseFromString(dHtml, "text/html"); if (!this.isDetailMatched(dDoc, link, code)) return null; const url = this.selectJavfreePreviewUrl(dDoc, link, code); if (url) { sessionStorage.setItem(cacheKey, url); return url; } return null; } catch { return null; } }, async javstore(code) { code = this.lookupCode(code); try { const normalizedCode = code.replace(/^fc2-?/i, "").replace(/-/g, "").toLowerCase(); const searchUrl = `https://javstore.net/search?q=${encodeURIComponent(code)}`; const searchHtml = await gmHttp.get(searchUrl); const searchDoc = new DOMParser().parseFromString(searchHtml, "text/html"); const candidateLinks = searchDoc.querySelectorAll('a[href*="/"]'); const detailUrls = []; for (const link of candidateLinks) { const href = link.getAttribute("href"); if (!href) continue; if (href.startsWith("http") && !href.includes("javstore.net")) continue; const urlObj = new URL(href, searchUrl); if (!/javstore\.net$/i.test(urlObj.hostname)) continue; if (/^\/search(?:[/?#]|$)/i.test(urlObj.pathname)) continue; const fullUrl = urlObj.href; const pathLastPart = decodeURIComponent(urlObj.pathname.split("/").pop() || ""); const normalizedPath = pathLastPart.toLowerCase().replace(/-/g, ""); const looksLikeDetail = /\.html$/i.test(urlObj.pathname) || /^\/\d+[-/]/.test(urlObj.pathname); if (looksLikeDetail && normalizedPath.includes(normalizedCode) && !detailUrls.includes(fullUrl)) { detailUrls.push(fullUrl); } } if (detailUrls.length === 0) return null; for (const detailUrl of detailUrls) { const imgUrl = await this._extractImgFromDetail(detailUrl, code); if (imgUrl) return imgUrl; } return null; } catch (e) { clog.debug("javstore 获取失败", e); return null; } }, async _extractImgFromDetail(detailUrl, code) { try { const detailHtml = await gmHttp.get(detailUrl); const detailDoc = new DOMParser().parseFromString(detailHtml, "text/html"); if (!this.isDetailMatched(detailDoc, detailUrl, code)) return null; for (const link of detailDoc.querySelectorAll("a")) { if (link.textContent.includes("CLICK HERE")) { const imgUrl = link.href || link.getAttribute("href") || ""; if (imgUrl) return this.normalizePreviewUrl(imgUrl, detailUrl); } } const img = detailDoc.querySelector('img[src*="_s.jpg"]'); if (img) { let src = img.getAttribute("src") || ""; if (!src.startsWith("http")) src = new URL(src, detailUrl).href; return this.normalizePreviewUrl(src.replace(/_s\.jpg$/, "_l.jpg"), detailUrl); } return null; } catch (e) { clog.debug("javstore: 详情页请求失败", detailUrl, e.message); return null; } }, async projectjav(code) { var _a; code = this.lookupCode(code); try { const request = (url) => gmHttp.get(url, {}, { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" }); const searchUrl = `https://projectjav.com/?searchTerm=${encodeURIComponent(code)}`; const searchHtml = await request(searchUrl); const searchDoc = new DOMParser().parseFromString(searchHtml, "text/html"); let detailUrl = ""; const allMovieLinks = [...searchDoc.querySelectorAll('a[href*="/movie/"]')]; const firstLink = ((_a = allMovieLinks[0]) == null ? void 0 : _a.getAttribute("href")) || ""; if (firstLink) { detailUrl = firstLink.startsWith("http") ? firstLink : `https://projectjav.com${firstLink}`; } if (!detailUrl) return null; const detailHtml = await request(detailUrl); const detailDoc = new DOMParser().parseFromString(detailHtml, "text/html"); const screenshotLink = [...detailDoc.querySelectorAll('.col-md-12.thumbnail a[data-featherlight="image"], .thumbnail a[data-featherlight="image"]')].find((a) => { var _a2; return this.isCodeMatched([a.outerHTML, (_a2 = a.closest(".thumbnail")) == null ? void 0 : _a2.outerHTML, detailUrl].join(" "), code); }); if (screenshotLink) { const thumbImg = screenshotLink.querySelector("img"); const href = screenshotLink.getAttribute("href") || ""; if (href) return this.normalizePreviewUrl(href, detailUrl); if (thumbImg) { const src = (thumbImg.getAttribute("src") || "").replace(/\?.*$/, ""); if (src) return this.normalizePreviewUrl(src, detailUrl); } } return null; } catch (e) { clog.debug("[projectjav] 异常:", e.message); return null; } }, async get(code) { const cacheKey = this.cacheKey(code); const cached = sessionStorage.getItem(cacheKey); if (cached) return {url: cached, source: null}; let url = null, source = null; for (const src of this.sourceOrder()) { url = await this.fetchFromSource(src, code); if (url) { source = src; break; } } if (url) sessionStorage.setItem(cacheKey, url); return {url, source}; } }; let previewOverlay = null; function showPreviewOverlay$1(imgUrl, code, source = null) { if (previewOverlay) { previewOverlay.close(); } let currentBlobUrl = null; const originalHtmlOverflow = document.documentElement.style.overflow; const originalBodyOverflow = document.body.style.overflow; document.documentElement.style.overflow = "hidden"; document.body.style.overflow = "hidden"; const container = document.createElement("div"); container.className = "jhs-preview-overlay"; const img = document.createElement("img"); img.src = imgUrl; img.className = "jhs-preview-img"; img.onclick = (e) => { e.stopPropagation(); img.classList.toggle("zoomed"); }; img.onerror = () => { img.src = imgUrl; }; const toolbar = document.createElement("div"); toolbar.className = "jhs-preview-toolbar"; const createButton = (text, className, onClick) => { const btn = document.createElement("button"); btn.className = `jhs-preview-btn ${className}`; btn.textContent = text; btn.onclick = onClick; return btn; }; const setActiveSource = (activeSource) => { toolbar.querySelectorAll(".jhs-preview-btn").forEach((b) => b.classList.remove("active")); const activeBtn = toolbar.querySelector(`.jhs-preview-btn-${activeSource}`); if (activeBtn) activeBtn.classList.add("active"); }; const changeSource = async (src, srcName) => { const loadingOverlay = document.createElement("div"); loadingOverlay.className = "jhs-loading-overlay"; loadingOverlay.innerHTML = '
    正在加载预览图...
    '; document.body.appendChild(loadingOverlay); try { const newUrl = src === "javfree" ? await Thumbnail.javfree(code) : src === "projectjav" ? await Thumbnail.projectjav(code) : await Thumbnail.javstore(code); if (newUrl) { if (currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; } img.src = newUrl; setActiveSource(src); } else { showToast(`${srcName} 未找到预览图`, "error"); } } catch (err) { showToast(`${srcName} 加载失败: ${err.message}`, "error"); } finally { loadingOverlay.remove(); } }; let toastTimer = null; const showToast = (msg, type = "info") => { const existing = container.querySelector(".jhs-preview-toast"); if (existing) existing.remove(); if (toastTimer) clearTimeout(toastTimer); const toast = document.createElement("div"); toast.className = `jhs-preview-toast jhs-preview-toast-${type}`; toast.textContent = msg; container.appendChild(toast); requestAnimationFrame(() => toast.classList.add("show")); toastTimer = setTimeout(() => { toast.classList.remove("show"); setTimeout(() => toast.remove(), 300); }, 3e3); }; const javfreeBtn = createButton("javfree", "jhs-preview-btn-javfree javfree", (e) => { e.stopPropagation(); changeSource("javfree", "javfree"); }); const projectjavBtn = createButton("projectjav", "jhs-preview-btn-projectjav projectjav", (e) => { e.stopPropagation(); changeSource("projectjav", "projectjav"); }); const javstoreBtn = createButton("javstore", "jhs-preview-btn-javstore javstore", (e) => { e.stopPropagation(); changeSource("javstore", "javstore"); }); const newWindowBtn = createButton("新窗口打开", "jhs-preview-btn-action action", (e) => { e.stopPropagation(); window.open(img.src); }); const downloadBtn = createButton("下载", "jhs-preview-btn-action action", (e) => { e.stopPropagation(); const a = document.createElement("a"); a.href = img.src; a.download = `${code}.jpg`; a.click(); }); toolbar.appendChild(javfreeBtn); toolbar.appendChild(projectjavBtn); toolbar.appendChild(javstoreBtn); toolbar.appendChild(newWindowBtn); toolbar.appendChild(downloadBtn); if (source === "javfree") setActiveSource("javfree"); else if (source === "projectjav") setActiveSource("projectjav"); else if (source === "javstore") setActiveSource("javstore"); else setActiveSource("javfree"); container.appendChild(img); const closeOverlay = () => { if (container.parentNode) { container.remove(); toolbar.remove(); document.documentElement.style.overflow = originalHtmlOverflow; document.body.style.overflow = originalBodyOverflow; if (currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; } } }; container.onclick = closeOverlay; const escHandler = (e) => { if (e.key === "Escape") { closeOverlay(); document.removeEventListener("keydown", escHandler); } }; document.addEventListener("keydown", escHandler); document.body.appendChild(container); document.body.appendChild(toolbar); previewOverlay = {container, toolbar, close: closeOverlay}; } class ScreenShotPlugin extends BasePlugin { getName() { return "ScreenShotPlugin"; } async handle() { this.loadScreenShot().then(); } async loadScreenShot() { if (!window.isDetailPage) return; if ("yes" !== await storageManager.getSetting("enableLoadScreenShot", "yes")) return; let carNum2 = this.getPageInfo().carNum; isJavDb$1 && $(".preview-images .tile-item").first().before( '
    正在加载缩略图
    ' ); isJavBus$1 && $("#sample-waterfall .sample-box:first").after( '
    正在加载缩略图
    ' ); try { const result = await Thumbnail.get(carNum2); if (result.url) { this.addImg("缩略图", result.url, carNum2, result.source); clog.log("加载缩略图:", result.url, result.source); } else { this.showErrorFallback(carNum2, new Error("所有来源均未找到预览图")); } } catch (e) { this.showErrorFallback(carNum2, e); } } async getScreenshot(carNum2) { const result = await Thumbnail.get(carNum2); if (result.url) return result.url; throw new Error("未找到预览图"); } async getJavStoreScreenShot(carNum2) { return await Thumbnail.javstore(carNum2); } async getJavFreeScreenShot(carNum2) { return await Thumbnail.javfree(carNum2); } addImg(title, imgUrl, code, source) { if (!imgUrl) return; isJavDb$1 && $(".screen-container").html( `${title}` ); isJavBus$1 && $(".screen-container").html( `
    ${title}
    ` ); $(".screen-container").on("click", (event) => { event.stopPropagation(); event.preventDefault(); showPreviewOverlay$1(imgUrl, code || title, source); }); } showErrorFallback(carNum2, error) { var _a, _b; console.error("获取缩略图失败:", ((_b = (_a = error == null ? void 0 : error.message) == null ? void 0 : _a.substring) == null ? void 0 : _b.call(_a, 0, 100)) || error); let differentCss = isJavBus$1 ? "margin-top:30px" : "margin-top:50px"; $(".screen-container").html( `
    获取缩略图失败

    点击重试 或 前往确认` ).off("click", ".retry-link").off("click", ".check-link").on("click", ".retry-link", async (e) => { e.stopPropagation(); e.preventDefault(); $(".screen-container").html( `
    正在重新加载...
    ` ); try { const result = await Thumbnail.get(carNum2); if (result.url) { this.addImg("缩略图", result.url, carNum2, result.source); } else { this.showErrorFallback(carNum2, new Error("所有来源均未找到预览图")); } } catch (err) { this.showErrorFallback(carNum2, err); } }).on("click", ".check-link", (e) => { e.stopPropagation(); e.preventDefault(); window.open(`https://javstore.net/search?q=${encodeURIComponent(carNum2)}`, "_blank"); }); } } window.showPreviewOverlay = showPreviewOverlay$1; const getDownPathList = async () => { const res = await gmHttp.get("https://webapi.115.com/offine/downpath"); return "object" == typeof res ? res.data : null; }; const searchFiles = async (search_value, offset = 0, limit = 30, params = {}) => { const queryParams = new URLSearchParams({search_value, offset, limit, ...params}); const url = `https://webapi.115.com/files/search?${queryParams}`; return await gmHttp.get(url); }; class WangPan115TaskPlugin extends BasePlugin { getName() { return "WangPan115TaskPlugin"; } async handle() { $(".buttons button[data-clipboard-text*='magnet:']").each(((i, el) => { const $copyBtn = $(el); const magnet = $copyBtn.attr("data-clipboard-text"); $copyBtn.css("margin-right", parseInt($copyBtn.css("margin-right")) - 4 + "px"); const $downloadBtn = $copyBtn.siblings("a.button, button").first(); if ($downloadBtn.length) { $downloadBtn.css("margin-right", parseInt($downloadBtn.css("margin-right")) + 3 + "px"); } $(" `; document.body.appendChild(panel); const header = document.getElementById("jhs-cookie-header"); document.getElementById("jhs-cookie-content"); const toggleIcon = document.getElementById("jhs-toggle-icon"), copyButton = document.getElementById("jhs-copy-btn"); header.addEventListener("click", (() => { const isExpanded = panel.classList.toggle("expanded"); toggleIcon.textContent = isExpanded ? "▲" : "▼"; })); copyButton.addEventListener("click", (async (e) => { e.stopPropagation(); try { await navigator.clipboard.writeText(cookieValue); this.showMessage("Cookie 已成功复制到剪贴板!"); } catch (err) { console.error("Failed to copy text using clipboard API: ", err); const tempTextArea = document.createElement("textarea"); tempTextArea.value = cookieValue; document.body.appendChild(tempTextArea); tempTextArea.select(); document.execCommand("copy"); document.body.removeChild(tempTextArea); this.showMessage("Cookie 已复制! (回退方案)"); } })); panel.classList.remove("expanded"); } } const _WangPan115MatchPlugin = class _WangPan115MatchPlugin2 extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "loginStatus", _WangPan115MatchPlugin2.LoginStatus.UNCHECKED); } getName() { return "WangPan115MatchPlugin"; } async initCss() { return "\n \n "; } async handle() { $(document).on("click", ".jhs-match-no-login-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); await this.handleLoginRedirect(); })); $(document).on("click", ".jhs-match-btn", ((event) => { event.preventDefault(); event.stopImmediatePropagation(); this.showMatchDetail(event.currentTarget); })); $(document).on("click", ".jhs-match-error-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); await this.retryMatch(event.currentTarget); })); $(document).on("click", ".jhs-detail-delete-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.currentTarget); const matchData = $btn.attr("data-match"); if (!matchData) return; const matchList = JSON.parse(matchData); const carNum2 = this.getPageInfo().carNum; this.showDeleteDialog(carNum2, matchList, (async (newMatchList) => { $("#115-match-table").remove(); await this.matchDetailPage(newMatchList); })); })); await this.matchDetailPage(); $(document).on("click", ".jhs-match-detail-error-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); $(event.currentTarget).replaceWith(`匹配中...`); try { const carNum2 = this.getPageInfo().carNum, matchList = await this.searchFiles(carNum2); $(".jhs-115-match-detail").remove(); await this.matchDetailPage(matchList); } catch (error) { console.error(`重新匹配失败 [${carNum}]:`, error); this.showMatchError($box, carNum, error); } })); } async matchDetailPage(matchList) { if (!window.isDetailPage) return; if (await storageManager.getSetting("enable115Match", NO) === NO) return; const $detail = $('\n
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    115匹配
    名称大小时间播放
    \n
    \n '), $tbody = $detail.find("tbody"); try { const carNum2 = this.getPageInfo().carNum; if (!matchList) { const {results, usedPhase2} = await this._searchFilesWithPhase(carNum2); matchList = results; if (usedPhase2 && matchList.length > 0) { await this.autoRenameFiles(carNum2, matchList); } } await this.checkLoginStatus(); if (this.loginStatus === _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT) $tbody.append(` 未登录 `); else if (matchList.length > 0) { const rowsHtml = matchList.map(((match) => ` ${match.name} ${this.formatSize(match.size)} ${match.createTime} 播放 `)).join(""); $tbody.append(rowsHtml); $detail.css("position", "relative"); $detail.append(`🗑️ 删除`); } else $tbody.append(` 未匹配 `); } catch (error) { $tbody.append(` 加载失败,请重试 `); console.error("加载文件列表时发生错误:", error); } if (isJavDb$1) if ($("#all-match-box").length) $("#all-match-box").append($detail); else { $("#tabs-container").before("
    "); $("#all-match-box").append($detail); } else $("#mag-submit-show").before($detail); } async matchMovieList(movieListElement) { if (await storageManager.getSetting("enable115Match", NO) !== NO) { await this.checkLoginStatus(); await this.processMovieElements(movieListElement); } else $(".video-title [class^='jhs-match-']").remove(); } showMatchDetail(buttonElement) { const $el = $(buttonElement), matchData = $el.attr("data-match"); $(".jhs-match-detail").remove(); const matches = this.parseMatchData(matchData); if (0 === matches.length) return; if (1 === matches.length) { const pickcode = matches[0].videoId; window.open(`https://115vod.com/?pickcode=${pickcode}&share_id=0`, "_blank"); return; } const $detail = this.createMatchDetailElement(matches); this.positionDetailElement($detail, $el); this.addOutsideClickHandler($detail); $detail.on("click", ((e) => { e.stopPropagation(); })); } parseMatchData(matchData) { try { return JSON.parse(matchData) || []; } catch (e) { console.error("解析匹配数据失败:", e); return []; } } createMatchDetailElement(matches) { const $detail = $(`
    ${matches.map(((match) => ` `)).join("")}
    名称 大小 时间 播放
    ${match.name} ${this.formatSize(match.size)} ${match.createTime} 播放
    `); $("body").append($detail); return $detail; } positionDetailElement($detail, $trigger) { const offset = $trigger.offset(); $detail.css({ top: offset.top - $detail.outerHeight() + 20, left: offset.left }); } addOutsideClickHandler($detail) { setTimeout((() => { $(document).on("click.jhs-match-detail", ((e) => { if (!$detail.is(e.target) && 0 === $detail.has(e.target).length) { $detail.remove(); $(document).off("click.jhs-match-detail"); } })); }), 100); } async retryMatch(buttonElement) { const $el = $(buttonElement), $box2 = $el.closest(".movie-box, .item"), carNum2 = $el.attr("data-keyword"); $el.replaceWith(`匹配中...`); try { const matchList = await this.searchFiles(carNum2); this.updateMatchStatus($box2, carNum2, matchList); } catch (error) { console.error(`重新匹配失败 [${carNum2}]:`, error); this.showMatchError($box2, carNum2, error); } } updateMatchStatus($box2, carNum2, matchList) { if (matchList.length > 0) { $box2.find(".jhs-match-btn").replaceWith(`匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList.some((m) => m.dirId != null)) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } if ($markBtn.length > 0) { $markBtn.show(); this.updateMarkBtnStyle($markBtn, carNum2); } } else { $box2.find(".jhs-match-btn").replaceWith(`未匹配`); const $deleteBtn = $box2.find(".delete115Svg"); const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0) { $deleteBtn.removeAttr("data-match"); $deleteBtn.hide(); } if ($markBtn.length > 0) { $markBtn.hide(); } } } async updateMarkBtnStyle($markBtn, carNum2) { const markedList = await storageManager.forage.getItem("markedDeleteList") || []; const isMarked = markedList.some((item) => item.carNum === carNum2); isMarked ? $markBtn.addClass("marked") : $markBtn.removeClass("marked"); $markBtn.attr("title", isMarked ? "取消标记删除" : "标记删除"); } async batchDeleteMarked(selectedItems, onProgress) { let deletedCount = 0; let failedCount = 0; const randomDelay = () => new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 2e3)); for (let i = 0; i < selectedItems.length; i++) { const item = selectedItems[i]; try { if (i > 0) { await randomDelay(); } const matchList = await this.searchFiles(item.carNum); if (matchList.length === 0) { failedCount++; onProgress && onProgress(i + 1, selectedItems.length, item, "未匹配到115文件"); continue; } const {regex: safeRegex} = codeParse(item.carNum); const safeMatchList = matchList.filter((m) => safeRegex.test(m.name)); if (safeMatchList.length === 0) { failedCount++; onProgress && onProgress(i + 1, selectedItems.length, item, "无安全可删除目录"); continue; } let itemDeleted = 0; const groupedByDir = {}; for (const m of safeMatchList) { if (!groupedByDir[m.dirId]) groupedByDir[m.dirId] = []; groupedByDir[m.dirId].push(m); } const entries = Object.entries(groupedByDir); for (let g = 0; g < entries.length; g++) { if (g > 0) { await randomDelay(); } const [dirId, items] = entries[g]; if (items.length === 1) { const result = await this._deleteOne(dirId, false, i, selectedItems, item, onProgress); if (result) { itemDeleted++; deletedCount++; } } else { for (let f = 0; f < items.length; f++) { if (f > 0) await randomDelay(); const fileDeleted = await this._deleteOne(items[f].folderId, true, i, selectedItems, item, onProgress); if (fileDeleted) { itemDeleted++; deletedCount++; } } } } onProgress && onProgress(i + 1, selectedItems.length, item, itemDeleted > 0 ? `已删除${itemDeleted}个` : "删除失败"); } catch (error) { failedCount++; onProgress && onProgress(i + 1, selectedItems.length, item, error.message || "出错"); console.error(`批量删除出错 [${item.carNum}]:`, error); } } return {deletedCount, failedCount}; } isRateLimited(response) { if (!response) return false; if (response.state === false) { const msg = (response.error || response.msg || "").toLowerCase(); if (msg.includes("频繁") || msg.includes("操作太频繁") || msg.includes("稍后再试") || msg.includes("rate") || msg.includes("limit") || msg.includes("too many")) { return true; } } if (response.status === 403 || response.status === 429) { return true; } return false; } async _deleteOne(deleteId, isFile, batchIndex, selectedItems, item, onProgress) { let retryCount = 0; const maxRetries = 3; const label = isFile ? "文件" : "目录"; while (retryCount <= maxRetries) { try { const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { "fid[0]": deleteId, ignore_warn: "1" }); if (result && result.state) { return 1; } else { const errMsg = result && result.error || "未知错误"; if (retryCount < maxRetries && this.isRateLimited(result)) { retryCount++; const backoff = (retryCount + 1) * 2e3 + Math.random() * 1e3; onProgress && onProgress(batchIndex + 1, selectedItems.length, item, `风控触发,${backoff / 1e3}秒后重试(${retryCount}/${maxRetries})`); await new Promise((resolve) => setTimeout(resolve, backoff)); continue; } console.error(`删除${label}失败: ${deleteId}`, errMsg); return 0; } } catch (e) { if (retryCount < maxRetries && this.isRateLimited(e)) { retryCount++; const backoff = (retryCount + 1) * 2e3 + Math.random() * 1e3; onProgress && onProgress(batchIndex + 1, selectedItems.length, item, `风控触发,${backoff / 1e3}秒后重试(${retryCount}/${maxRetries})`); await new Promise((resolve) => setTimeout(resolve, backoff)); continue; } console.error(`删除${label}失败: ${deleteId}`, e); return 0; } } return 0; } showDeleteDialog(carNum2, matchList, afterDelete) { const {regex: safeRegex} = codeParse(carNum2); const safeMatchList = matchList.filter((m) => safeRegex.test(m.name)); const unsafeCount = matchList.length - safeMatchList.length; if (safeMatchList.length === 0) { show.error(`作品 "${carNum2}" 匹配到的 ${matchList.length} 个项目均不含番号,无法安全删除。请手动在115中删除。`); return; } const formatSize = (bytes) => { if (!bytes) return "-"; const units = ["B", "KB", "MB", "GB", "TB"]; let size = parseFloat(bytes), unit = 0; for (; size >= 1024 && unit < units.length - 1;) size /= 1024, unit++; return size.toFixed(2) + " " + units[unit]; }; let html = '
    '; html += `
    作品 ${carNum2} 匹配到 ${matchList.length} 个文件夹(可删除 ${safeMatchList.length} 个):
    `; if (unsafeCount > 0) { html += `
    ⚠ 已自动排除 ${unsafeCount} 个目录名不含番号的项目,防止误删
    `; } html += '
    '; for (let i = 0; i < safeMatchList.length; i++) { const m = safeMatchList[i]; html += `
    `; } html += "
    "; const layerIndex = layer.open({ type: 1, title: "删除115作品文件夹", content: html, area: ["520px", "auto"], btn: ["确定删除", "取消"], shadeClose: false, yes: async (index) => { var _a, _b; const $layer = $(`#layui-layer${index}`); const checked = $layer.find(".jhs-delete-folder-cb:checked"); if (0 === checked.length) { show.error("请至少选择一个文件夹"); return; } const selected = checked.map((function () { return parseInt($(this).data("index")); })).get(); layer.close(index); let loadObj = loading(); let deletedCount = 0; try { const selectedItems = selected.map((i) => safeMatchList[i]); const totalByDir = {}; for (const m of safeMatchList) { totalByDir[m.dirId] = (totalByDir[m.dirId] || 0) + 1; } const groupedByDir = {}; for (const item of selectedItems) { if (!groupedByDir[item.dirId]) groupedByDir[item.dirId] = []; groupedByDir[item.dirId].push(item); } for (const [dirId, items] of Object.entries(groupedByDir)) { if (items.length === totalByDir[dirId]) { const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { "fid[0]": dirId, ignore_warn: "1" }); result && result.state ? deletedCount++ : console.error(`删除目录失败: ${dirId}`, result); } else { for (const item of items) { const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { "fid[0]": item.folderId, ignore_warn: "1" }); result && result.state ? deletedCount++ : console.error(`删除文件失败: ${item.folderId}`, result); } } } loadObj.close(); show.ok(`作品 "${carNum2}" 已删除 ${deletedCount} 个文件夹`); const newMatchList = await this.searchFiles(carNum2); afterDelete && afterDelete(newMatchList); const remainingCids = new Set(newMatchList.map((m) => m.dirId)); JavPackSubtitle.cached115Matches = JavPackSubtitle.cached115Matches.filter((m) => remainingCids.has(m.cid)); JavPackSubtitle.current115Cids = JavPackSubtitle.current115Cids.filter((cid) => remainingCids.has(cid)); try { (_b = (_a = this.getBean("WangPan115TaskPlugin")).deleteOfflineTasksByKeyword) == null ? void 0 : _b.call(_a, carNum2); } catch (e) { console.warn("删除离线任务暂不可用:", e.message); } } catch (error) { loadObj.close(); console.error("删除115文件失败:", error); show.error(`删除失败: ${error.message || "网络错误"}`); } } }); $(`#layui-layer${layerIndex}`).on("change", ".jhs-delete-select-all", (function () { const checked = $(this).prop("checked"); $(`#layui-layer${layerIndex} .jhs-delete-folder-cb`).prop("checked", checked); })); } async handleLoginRedirect() { window.open("https://115.com"); } async searchFiles(carNum2) { return (await this._searchFilesWithPhase(carNum2)).results; } async _searchFilesWithPhase(carNum2) { const {regex} = codeParse(carNum2); const searchKeyword = carNum2.toLowerCase().replace("fc2-", ""); const mapAndFilter = (rawData) => { return rawData.map(((data) => ({ folderId: data.fid, dirId: data.cid, videoId: data.pc, name: data.n, createTime: utils.formatDate(new Date(1e3 * data.te)), size: data.s, isVideo: true }))).filter(((x) => x.folderId && regex.test(x.name))); }; var _a2; const preciseData = (null == (_a2 = (await searchFiles(searchKeyword, 0, 30, {type: 4})).data) ? void 0 : _a2) || []; const preciseResults = mapAndFilter(preciseData); if (preciseResults.length > 0) return {results: preciseResults, usedPhase2: false}; const numberPart = extractNumberPart(carNum2); if (numberPart === searchKeyword) return {results: preciseResults, usedPhase2: false}; const fuzzyData = (null == (_a2 = (await searchFiles(numberPart, 0, 30, {type: 4})).data) ? void 0 : _a2) || []; return {results: mapAndFilter(fuzzyData), usedPhase2: true}; } buildRenameTarget(carNum2, originalName) { const {regex} = codeParse(carNum2); const match = regex.exec(originalName); if (!match) return null; const before = originalName.slice(0, match.index); const after = originalName.slice(match.index + match[0].length); return before + carNum2 + after; } async autoRenameFiles(carNum2, matchList) { try { const enableAutoRename = await storageManager.getSetting("enable115AutoRename", NO); if (enableAutoRename !== YES) return; const scope = await storageManager.getSetting("autoRenameScope", "vr_only"); if (scope === "vr_only" && !carNum2.toUpperCase().includes("VR")) return; const renameObj = {}; matchList.forEach((match) => { const newName = this.buildRenameTarget(carNum2, match.name); if (newName && newName !== match.name) { renameObj[match.folderId] = newName; } }); if (Object.keys(renameObj).length === 0) return; const formData = {}; Object.entries(renameObj).forEach(([fid, newName]) => { formData[`files_new_name[${fid}]`] = newName; }); const result = await gmHttp.postForm("https://webapi.115.com/files/batch_rename", formData); if (result && result.state) { Object.entries(renameObj).forEach(([fid, newName]) => { const match = matchList.find((m) => m.folderId === fid); console.log(`[115重命名] ${match.name} → ${newName}`); }); } else { console.warn("[115重命名] 重命名失败:", result && result.error || "未知错误"); } } catch (e) { console.warn("[115重命名] 重命名失败,跳过:", e.message); } } showMatchError($box2, carNum2, error) { $box2.find(".jhs-match-btn").replaceWith(`匹配失败`); show.error(`${carNum2} 匹配失败: ${error.message || "网络错误"}`); } async checkLoginStatus() { var _a2; if (this.loginStatus === _WangPan115MatchPlugin2.LoginStatus.UNCHECKED) try { const testResult = await searchFiles("test"); this.loginStatus = (null == (_a2 = testResult.error) ? void 0 : _a2.includes("登录")) ? _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT : _WangPan115MatchPlugin2.LoginStatus.LOGGED_IN; } catch { this.loginStatus = _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT; } } async processMovieElements(movieListElement) { const promises = Array.from(movieListElement).filter(((ele) => !utils.isHidden(ele))).filter(((ele) => !(isJavBus$1 && $(ele).find(".avatar-box").length > 0))).map(((ele) => this.processSingleMovieElement(ele))); await Promise.all(promises); } async processSingleMovieElement(element) { const $box2 = $(element), {carNum: carNum2} = this.getBoxCarInfo($box2); if (!($box2.find("[class^='jhs-match-']").length > 0)) if (this.loginStatus !== _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT) try { const matchList = await this.searchFiles(carNum2); this.addTag($box2, carNum2, matchList); } catch (error) { console.error(`搜索失败 [${carNum2}]:`, error); this.addTag($box2, carNum2, []); } else this.addTag($box2, carNum2, []); } addTag($box2, carNum2, matchList) { if (!($box2.find("[class^='jhs-match-']").length > 0)) if (this.loginStatus === _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT) $box2.find(".video-title").prepend(`未登录`); else if (matchList.length > 0) { const title = 1 === matchList.length ? "点击直接播放" : `点击查看${matchList.length}个匹配结果`; $box2.find(".video-title").prepend(`匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList.some((m) => m.dirId != null)) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } if ($markBtn.length > 0) { $markBtn.show(); this.updateMarkBtnStyle($markBtn, carNum2); } } else $box2.find(".video-title").prepend(`未匹配`); } formatSize(bytes) { if (!bytes) return "-"; const units = ["B", "KB", "MB", "GB", "TB"]; let size = parseFloat(bytes), unit = 0; for (; size >= 1024 && unit < units.length - 1;) { size /= 1024; unit++; } return `${size.toFixed(2)} ${units[unit]}`; } }; __publicField2(_WangPan115MatchPlugin, "LoginStatus", { UNCHECKED: -1, LOGGED_OUT: 0, LOGGED_IN: 1 }); let WangPan115MatchPlugin = _WangPan115MatchPlugin; const extractNumberPart = (carNum2) => { let cleaned = carNum2.replace(/^fc2[-_]?(ppv[-_])?/i, ""); const match = cleaned.match(/(\d+)$/); return match ? match[1] : cleaned; }; const codeParse = (code) => { const codes = code.split(/-|_/); const sep = "\\s?(0|-|_){0,2}\\s?"; let pattern = codes.join(sep); if (/^fc2/i.test(code)) pattern = `${codes[0]}${sep}(ppv)?${sep}${codes.at(-1)}`; return { code, codes, prefix: codes[0], regex: new RegExp(`(? { actress.starId && favoriteStarIdSet.add(String(actress.starId).trim()); })); 0 !== favoriteStarIdSet.size && $(".female").prev().each(((index, element) => { const $element = $(element), href = $element.attr("href"); let elementStarId = null; if (href) { const parts = (href.endsWith("/") ? href.slice(0, -1) : href).split("/"), lastPart = parts[parts.length - 1]; lastPart && (elementStarId = lastPart.trim()); } let isFavorite = false; elementStarId && (isFavorite = favoriteStarIdSet.has(elementStarId)); if (isFavorite) { $element.addClass("highlighted"); $element.attr("title", "高亮已收藏演员, 可在设置-基础配置中关闭"); } })); } async removeActorFromStorage(actorId) { await storageManager.removeFavoriteActress(actorId) && clog.log("移除演员成功"); } bindEvent() { const listIdRegex = /\/actors\/(\w+)\/(collect|uncollect)/; $(document).on("confirm:complete", 'a[href*="/actors/"][href*="/uncollect"]', (async (event) => { const [userConfirmed] = event.detail; if (!userConfirmed) return; const match = $(event.currentTarget).attr("href").match(listIdRegex), actorId = match ? match[1] : null; actorId && await this.removeActorFromStorage(actorId); })); $("#button-collect-actor").click((async (event) => { const match = $("#button-collect-actor").attr("href").match(listIdRegex), actorId = match ? match[1] : null; let nameList = [], $actor = $(".actor-section-name"); $actor.length && $actor.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let $sectionMeta = $(".section-meta:not(:contains('影片'))"); $sectionMeta.length && $sectionMeta.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); if (!nameList) { clog.error("获取演员名称失败"); return; } const actorName = nameList[0]; if (!actorId) { clog.error("无法获取演员ID进行收藏操作。"); return; } const avatar = ($(".avatar").first().css("background-image") || "").replace(/^url\(["']?|["']?\)$/g, ""), actressInfo = { starId: actorId, name: actorName, allName: nameList, avatar }; 1 === await storageManager.addFavoriteActressList([actressInfo]) ? clog.log(`收藏演员成功: ${actorName} (ID: ${actorId})`) : clog.log(`收藏演员失败: ${actorName} (ID: ${actorId})`); })); $("#button-uncollect-actor").click((async (event) => { const match = $("#button-uncollect-actor").attr("href").match(listIdRegex), actorId = match ? match[1] : null; actorId ? await this.removeActorFromStorage(actorId) : clog.error("无法获取演员ID进行取消收藏操作。"); })); } async replaceActressAvatar() { const actressId = this.getActressId(); if (!actressId) return; const actress = (await storageManager.getFavoriteActressList()).find(((item) => item.starId === actressId)); if (actress && actress.avatar) { const newAvatarUrl = `url('${actress.avatar}')`; let $avatarElement = $(".avatar").first(); if (0 === $avatarElement.length) { const newAvatarHtml = '
    '; $(".section-columns").prepend(newAvatarHtml); $avatarElement = $(".avatar").first(); } if (0 === $avatarElement.length) return; if ($avatarElement.css("background-image").trim().toLowerCase() !== newAvatarUrl.trim().toLowerCase()) { $avatarElement.css("background-image", newAvatarUrl); $avatarElement.css("background-size", "cover"); $avatarElement.css("background-position", "top center"); $avatarElement.css("background-repeat", "no-repeat"); } } } } class BusImgPlugin extends BasePlugin { getName() { return "BusImgPlugin"; } handle() { } async getVisibleImageItems(itemSelector, imgSelector) { let itemList = []; const allItems = document.querySelectorAll(itemSelector); for (const item of allItems) { if (!utils.isHidden(item)) { const imgElement = item.querySelector(imgSelector); if (!(imgElement instanceof HTMLImageElement)) continue; imgElement.style.removeProperty("height"); let imageHeight = imgElement.offsetHeight; imageHeight > 0 && itemList.push({ element: item, imgElement, height: imageHeight }); } } return itemList; } async logImageHeightsByRow() { if (await storageManager.getSetting("enableVerticalModel", NO) === YES$1) return; const itemSelector = this.getSelector().itemSelector, containerColumns = await storageManager.getSetting("containerColumns", 5), itemList = await this.getVisibleImageItems(itemSelector, "img"); if (0 === itemList.length) return; const groupedItems = []; for (let i = 0; i < itemList.length; i++) { const rowIndex = Math.floor(i / containerColumns); groupedItems[rowIndex] || (groupedItems[rowIndex] = []); groupedItems[rowIndex].push(itemList[i]); } groupedItems.forEach(((row, rowIndex) => { const originalHeights = row.map(((item) => item.height)); if (originalHeights.length < 2) return; const minHeight = Math.min(...originalHeights), maxHeight = Math.max(...originalHeights); let targetHeight = 0; if (maxHeight - minHeight > 50) { targetHeight = minHeight; row.forEach(((item) => { if (item.height !== targetHeight) { const heightValue = `${targetHeight}px`; item.imgElement.style.setProperty("height", heightValue, "important"); } })); } })); } } class ListPageButtonPlugin extends BasePlugin { getName() { return "ListPageButtonPlugin"; } async handle() { if (!window.isListPage) return; await this.createMenuBtn(); this.bindEvent(); await storageManager.getSetting("autoPage") === YES$1 ? $("#sort-toggle-btn").hide() : this.sortItems().then(); this.initOneTimeSortBtn(); } async createMenuBtn() { const showWaitCheckBtn = await storageManager.getSetting("showWaitCheckBtn", YES$1), showWaitDownBtn = await storageManager.getSetting("showWaitDownBtn", YES$1); if (isJavDb$1) { const isStarPage = currentHref$1.includes("/actors/"); let $el = $(".main-tabs, .tabs"), addBlacklistBtnText = "加入黑名单", addBlacklistBtnColor = "#d22020", otherCss = "", blacklistItem = null; if (isStarPage) { $el = $(".toolbar, .section-addition").filter(":last"); const blacklist = await storageManager.getBlacklist(), actressPageInfo = this.getActressPageInfo(); if (blacklist.find(((item) => item.starId === actressPageInfo.starId))) { addBlacklistBtnText = "已加入黑名单"; addBlacklistBtnColor = "#885d5d"; } } else currentHref$1.includes("/tags") && utils.loopDetector((() => $("#jhs-check-tag").text().trim()), (async () => { const $addBlacklistBtn = $("#addBlacklistBtn"); $addBlacklistBtn.attr("data-tip", "将当前分类标签加入到黑名单, 后续有作品更新也会纳入屏蔽中"); const checkTag = $("#jhs-check-tag").text().trim(); if (!checkTag) return; const tagStarId = "no-" + checkTag, blacklist = await storageManager.getBlacklist(); blacklistItem = blacklist.find(((item) => item.starId === tagStarId)); if (blacklistItem) { $addBlacklistBtn.css("backgroundColor", "#885d5d"); $("#addBlacklistBtn span").text("已加入黑名单"); } })); const isFc2Page2 = currentHref$1.includes("advanced_search"); isFc2Page2 ? $el = $("h2.section-title") : otherCss = "flex-grow:1;"; const jhs_sortMethod = localStorage.getItem("jhs_sortMethod"), sortText = "当前排序方式: " + ("rateCount" === jhs_sortMethod ? "评价人数" : "date" === jhs_sortMethod ? "时间" : "默认"); $el.append(`
    打开待鉴定 打开已收藏 ${isStarPage ? ` ${addBlacklistBtnText} 一键屏蔽所有作品 ` : ""} ${currentHref$1.includes("/tags") ? ` ${addBlacklistBtnText} ` : ""}
    一次性排序: 默认 新作品检测 (0) 演员黑名单 ${isSearchPage || isFc2Page2 ? "" : ` ${sortText} `}
    `); } if (isJavBus$1) { const isStarPage = currentHref$1.includes("/star/"); let addBlacklistBtnText = "加入黑名单", addBlacklistBtnColor = "#d22020"; if (isStarPage) { const blacklist = await storageManager.getBlacklist(), actressPageInfo = this.getActressPageInfo(); if (blacklist.find(((item) => item.starId === actressPageInfo.starId))) { addBlacklistBtnText = "已加入黑名单"; addBlacklistBtnColor = "#885d5d"; } } $(".masonry").parent().prepend(`
    打开待鉴定 打开已收藏 ${isStarPage ? ` ${addBlacklistBtnText} 一键屏蔽所有作品 ` : '演员黑名单'}
    `); } const newVideoPlugin = window.pluginManager.getBean("NewVideoPlugin"); newVideoPlugin && newVideoPlugin.showNewVideoCount().then(); } bindEvent() { $("#waitCheckBtn").on("click", ((event) => { this.openWaitCheck(event).then(); })); $("#waitDownBtn").on("click", ((event) => { this.openFavorite(event).then(); })); $("#newVideoBtn").on("click", ((event) => { this.getBean("NewVideoPlugin").openDialog(); })); $("#blacklistBtn").on("click", ((event) => { this.getBean("BlacklistPlugin").openBlacklistDialog(); })); $("#sort-toggle-btn").on("click", ((event) => { const currentMethod = localStorage.getItem("jhs_sortMethod") || "default"; const sortCycle = ["default", "rateCount", "totalScore", "date"]; let nextIndex = sortCycle.indexOf(currentMethod) + 1; if (nextIndex >= sortCycle.length || nextIndex < 0) nextIndex = 0; const newMethod = sortCycle[nextIndex]; const methodText = { default: "默认", rateCount: "评价人数", totalScore: "总分", date: "时间" }[newMethod]; $(event.target).text(`当前排序方式: ${methodText}`); localStorage.setItem("jhs_sortMethod", newMethod); this.sortItems().then(); })); $("#one-time-sort-btn").on("click", ((event) => { const currentMethod = localStorage.getItem("jhs_oneTimeSortMethod") || "default"; const sortCycle = ["default", "rateCount", "totalScore", "date"]; let nextIndex = sortCycle.indexOf(currentMethod) + 1; if (nextIndex >= sortCycle.length || nextIndex < 0) nextIndex = 0; const newMethod = sortCycle[nextIndex]; const methodText = { default: "默认", rateCount: "评价人数", totalScore: "总分", date: "时间" }[newMethod]; $("#one-time-sort-btn").text(`一次性排序: ${methodText}`); localStorage.setItem("jhs_oneTimeSortMethod", newMethod); this.oneTimeSortItems().then(); })); const blacklistPlugin = this.getBean("BlacklistPlugin"); $("#addBlacklistBtn").on("click", (async (event) => { await blacklistPlugin.addBlacklist(event); })); $("#filterAllVideo").on("click", (async (event) => { let tempEvent = { clientX: event.clientX, clientY: event.clientY + 80 }, $actor = isJavDb$1 ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10"); if (0 === $actor.length) { show.error("获取演员名称失败"); return; } let actorName = $actor.text().trim().split(",")[0]; utils.q(tempEvent, "一键屏蔽已选分类的视频列表至鉴定记录中?", (async () => { this.loadObj = loading(); try { await blacklistPlugin.filterAllVideo(actorName); window.refresh(); } catch (e) { console.error(e); } finally { this.loadObj.close(); } })); })); } async sortItems() { if (currentHref$1.includes("handle") || currentHref$1.includes("advanced_search")) return; const autoPage = await storageManager.getSetting("autoPage"); if (isSearchPage || autoPage === YES$1) return; const method = localStorage.getItem("jhs_sortMethod"); if (!method) return; $(".movie-list .item").each((function (index) { $(this).attr("data-original-index") || $(this).attr("data-original-index", index); })); const $container = $(".movie-list"), $items = $(".item", $container); if ("default" === method) { $items.sort((function (a, b) { return $(a).data("original-index") - $(b).data("original-index"); })).appendTo($container); } else { const items = $items.get(); items.sort((function (a, b) { if ("rateCount" === method) { const getScore = (el) => { const match = $(el).find(".score .value").text().match(/由(\d+)人/); return match ? parseFloat(match[1]) : 0; }; return getScore(b) - getScore(a); } else if ("totalScore" === method) { const getTotalScore = (el) => { const text = $(el).find(".score .value").text(); const scoreMatch = text.match(/([\d.]+)分/); const countMatch = text.match(/由(\d+)人/); const score = scoreMatch ? parseFloat(scoreMatch[1]) : 0; const count = countMatch ? parseFloat(countMatch[1]) : 0; return score * count; }; return getTotalScore(b) - getTotalScore(a); } else { const getDate = (el) => { const dateStr = $(el).find(".meta").text().trim(); return new Date(dateStr); }; return getDate(b) - getDate(a); } })); $container.empty().append(items); } } async initOneTimeSortBtn() { const autoPage = await storageManager.getSetting("autoPage"); if (autoPage !== YES$1) { $("#one-time-sort-btn").hide(); return; } const oneTimeMethod = localStorage.getItem("jhs_oneTimeSortMethod") || "default"; const methodText = { default: "默认", rateCount: "评价人数", totalScore: "总分", date: "时间" }[oneTimeMethod]; $("#one-time-sort-btn").text(`一次性排序: ${methodText}`).show(); } async oneTimeSortItems() { if (currentHref$1.includes("handle") || currentHref$1.includes("advanced_search")) return; if (isSearchPage) return; const method = localStorage.getItem("jhs_oneTimeSortMethod"); if (!method) return; $(".movie-list .item").each((function (index) { $(this).attr("data-original-index") || $(this).attr("data-original-index", index); })); const $container = $(".movie-list"), $items = $(".item", $container); if ("default" === method) { $items.sort((function (a, b) { return $(a).data("original-index") - $(b).data("original-index"); })).appendTo($container); } else { const items = $items.get(); items.sort((function (a, b) { if ("rateCount" === method) { const getScore = (el) => { const match = $(el).find(".score .value").text().match(/由(\d+)人/); return match ? parseFloat(match[1]) : 0; }; return getScore(b) - getScore(a); } else if ("totalScore" === method) { const getTotalScore = (el) => { const text = $(el).find(".score .value").text(); const scoreMatch = text.match(/([\d.]+)分/); const countMatch = text.match(/由(\d+)人/); const score = scoreMatch ? parseFloat(scoreMatch[1]) : 0; const count = countMatch ? parseFloat(countMatch[1]) : 0; return score * count; }; return getTotalScore(b) - getTotalScore(a); } else { const getDate = (el) => { const dateStr = $(el).find(".meta").text().trim(); return new Date(dateStr); }; return getDate(b) - getDate(a); } })); $container.empty().append(items); } } async openWaitCheck(event) { const maxCount = await storageManager.getSetting("waitCheckCount", 5); let count = 0; $(`${this.getSelector().itemSelector}:visible`).each(((i, el) => { if (count >= maxCount) return false; const $box2 = $(el); if ($box2.find(".status-tag").length > 0) return; const {carNum: carNum2, aHref} = this.getBoxCarInfo($box2); if (carNum2.includes("FC2-")) { const movieId = this.parseMovieId(aHref); this.getBean("Fc2Plugin").openFc2Page(movieId, carNum2, aHref); } else { let url = aHref + (aHref.includes("?") ? "&autoPlay=1" : "?autoPlay=1"); window.open(url); } count++; })); 0 === count && show.info("没有需鉴定的视频"); } async openFavorite() { let favoriteList, openCount = await storageManager.getSetting("waitCheckCount", 5), randomOpenWaitDown = await storageManager.getSetting("randomOpenWaitDown", NO), dataList = await storageManager.getCarList(); favoriteList = randomOpenWaitDown === YES$1 ? dataList.filter(((item) => item.status === Status_FAVORITE$1)).sort((() => Math.random() - 0.5)) : dataList.filter(((item) => item.status === Status_FAVORITE$1)).sort(((a, b) => b.createDate - a.createDate)); for (let i = 0; i < openCount; i++) { if (i >= favoriteList.length) return; let data = favoriteList[i], carNum2 = data.carNum, url = data.url; if (carNum2.includes("FC2-")) { const movieId = this.parseMovieId(url); await this.getBean("Fc2Plugin").openFc2Page(movieId, carNum2, url); } else window.open(url); clog.debug("打开已收藏", carNum2, url); } } } const translateText = async (text, sourceLang = "ja", targetLang = "zh-CN") => { if (!text) throw new Error("翻译文本不能为空"); const url = "https://translate-pa.googleapis.com/v1/translate?" + new URLSearchParams({ "params.client": "gtx", dataTypes: "TRANSLATION", key: "AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA", "query.sourceLanguage": sourceLang, "query.targetLanguage": targetLang, "query.text": text }), res = await fetch(url); if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); return (await res.json()).translation; }, TAG_STYLES = { IS_FILTERED: { text: "🚫 已屏蔽", color: "#de3333", reasonType: "单番号屏蔽", isCounted: true, countKey: "currentPageFilterCount" }, IS_FAVORITE: { text: "⭐ 已收藏", color: "#25b1dc", reasonType: "", isCounted: true, countKey: "currentPageFavoriteCount" }, IS_HAS_DOWN: { text: "📥️ 已下载", color: "#7bc73b", reasonType: "", isCounted: true, countKey: "currentPageHasDownCount" }, IS_HAS_WATCH: { text: "🔍 已观看", color: "#d7a80c", reasonType: "", isCounted: true, countKey: "currentPageHasWatchCount" }, IS_KEYWORD_FILTER: { text: "❌ 关键词屏蔽", color: "#de3333", reasonType: "", isCounted: true, countKey: "currentPageKeywordFilterCount" }, IS_ACTOR_FILTER: { text: "♂️ 男演员屏蔽", color: "#b22222", reasonType: "", isCounted: true, countKey: "currentPageActorFilterCount" }, IS_ACTRESS_FILTER: { text: "♀️ 女演员屏蔽", color: "#cd5c5c", reasonType: "", isCounted: true, countKey: "currentPageActorFilterCount" }, IS_WAIT_CHECK: { text: "", color: "", reasonType: "", isCounted: true, countKey: "currentPageWaitCheckCount" } }; class TranslatePlugin extends BasePlugin { getName() { return "TranslatePlugin"; } async initCss() { return "\n \n "; } handle() { window.isDetailPage && this.translate(); } async translate(carNum2, showCarNum = true) { if (await storageManager.getSetting("translateTitle", YES$1) !== YES$1) return; isJavBus$1 && (showCarNum = false); let $titleElement = $(".origin-title"); $titleElement.length || ($titleElement = $(".current-title")); $titleElement.length || ($titleElement = $("h3")); if (!$titleElement.length) return; const originalText = $titleElement.text().trim(); if (!originalText) { show.error("获取标题失败, 无法进行翻译"); return; } $titleElement.after('
    翻译中...
    '); const $loadingElement = $titleElement.next(".translated-title"); carNum2 || (carNum2 = this.getPageInfo().carNum); const cache = localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {}; cache[carNum2] ? $loadingElement.html(showCarNum ? carNum2 + "   " + cache[carNum2] : cache[carNum2]) : translateText(originalText, "ja", "zh-CN").then(((translatedText) => { $loadingElement.html(showCarNum ? carNum2 + "   " + translatedText : translatedText); })).catch(((error) => { console.error("翻译失败:", error); $loadingElement.replaceWith(`
    翻译失败: ${error.message}
    `); })); } } class OtherSitePlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "okBackgroundColor", "#7bc73b"); __publicField2(this, "errorBackgroundColor", "#de3333"); __publicField2(this, "warnBackgroundColor", "#d7a80c"); __publicField2(this, "domainErrorBackgroundColor", "#d7780c"); __publicField2(this, "asyncQueue", new AsyncQueue()); __publicField2(this, "siteConfigs", [ { id: "MissAvBtn", getBaseUrl: async () => await this.getMissAvUrl(), itemSelector: ".text-secondary", searchPath: (baseUrl, carNum2) => `${baseUrl}/search/${carNum2}`, getDetailPageHref: ($box2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.text() }, { id: "123AvBtn", getBaseUrl: async () => await this.getAv123Url() + "/ja", itemSelector: ".box-item", searchPath: (baseUrl, carNum2) => `${baseUrl}/search?keyword=${carNum2}`, getDetailPageHref: ($box2) => $box2.find(".detail a").attr("href"), findCarNumOrTitle: ($box2) => $box2.find("img").attr("title") }, { id: "JableBtn", getBaseUrl: async () => await this.getjableUrl(), itemSelector: "#list_videos_videos_list_search_result .detail .title a", searchPath: (baseUrl, carNum2) => `${baseUrl}/search/${carNum2}/`, getDetailPageHref: ($box2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.text() }, { id: "SupJavBtn", getBaseUrl: async () => await this.getSupJavUrl(), itemSelector: ".posts post", searchPath: (baseUrl, carNum2) => `${baseUrl}/?s=${carNum2}`, getDetailPageHref: ($box2, baseUrl, carNum2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.attr("title") }, { id: "JavTrailersBtn", getBaseUrl: async () => await this.getJavTrailersUrl(), itemSelector: ".videos-list .video-link", searchPath: (baseUrl, carNum2) => `${baseUrl}/search/${carNum2}`, getDetailPageHref: ($box2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.find("p.card-text").text() }, { id: "JavDbBtn", getBaseUrl: async () => await this.getJavDbUrl(), itemSelector: ".movie-list .item", searchPath: (baseUrl, carNum2) => `${baseUrl}/search?q=${carNum2}`, getDetailPageHref: ($box2) => $box2.find("a").attr("href"), findCarNumOrTitle: ($box2) => $box2.find(".video-title").text(), condition: (sourceCarNum) => isJavBus$1 }, { id: "JavBusBtn", getBaseUrl: async () => await this.getJavBusUrl(), itemSelector: ".container h3", searchPath: (baseUrl, carNum2) => `${baseUrl}/${carNum2}`, getDetailPageHref: ($box2, baseUrl, carNum2) => `${baseUrl}/${carNum2}`, findCarNumOrTitle: ($box2) => $box2.text(), condition: (sourceCarNum) => isJavDb$1 && sourceCarNum && !sourceCarNum.includes("FC2") } ]); __publicField2(this, "settingCache", null); __publicField2(this, "lastFetchTime", 0); __publicField2(this, "CACHE_DURATION", 1e4); } getName() { return "OtherSitePlugin"; } async initCss() { return "\n \n "; } async handle() { window.isDetailPage && this.loadOtherSite().then(); } async loadOtherSite(carNum2, sourceCarNum) { if ("yes" !== await storageManager.getSetting("enableLoadOtherSite", "yes")) return; carNum2 || (carNum2 = this.getPageInfo().carNum); const enabledSites = this.getEnabledSites(), html = `
    ${this.siteConfigs.map(((config) => { config.sourceCarNum = sourceCarNum; if (config.condition && false === config.condition(config.sourceCarNum)) return ""; return `${config.id.replace("Btn", "")}`; })).join("")} 跳转设置
    `; $(".movie-panel-info").append(html); $(".container .info").append(html); $("#javTrailersBtn").on("click", (async (event) => { event.preventDefault(); let settingObj = await storageManager.getSetting(); const filterHotKey = settingObj.filterHotKey, favoriteHotKey = settingObj.favoriteHotKey, speedVideoHotKey = settingObj.speedVideoHotKey; let href = $("#javTrailersBtn").attr("href"), url = href + `?handle=1&filterHotKey=${filterHotKey}&favoriteHotKey=${favoriteHotKey}&speedVideoHotKey=${speedVideoHotKey}`; event && (event.ctrlKey || event.metaKey) && (url = href); utils.openPage(url, carNum2, false, event); })); await Promise.all(this.siteConfigs.map((async (config) => { config.condition && false === config.condition(config.sourceCarNum) || await this.handleSite(carNum2, config); }))); this.renderSettingsArea(); this.setupEventListeners(); } async handleSite(carNum2, config) { const $btn = $(`#${config.id}`); try { if ($btn.attr("href")) return; if (utils.isHidden($btn)) return; const cacheKey = "jhs_other_site", cacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}, siteKey = carNum2 + "_" + config.id.replace("Btn", ""), cachedResult = cacheData[siteKey]; if (cachedResult) { if ("single" === cachedResult.type) { $btn.attr("href", cachedResult.url); $btn.css("backgroundColor", this.okBackgroundColor); } else if ("multiple" === cachedResult.type) { $btn.attr("href", cachedResult.url); $btn.append('多结果'); $btn.css("backgroundColor", this.okBackgroundColor); } return; } const baseUrl = await config.getBaseUrl(), searchUrl = config.searchPath(baseUrl, carNum2); $btn.attr("href", searchUrl); const html = await gmHttp.get(searchUrl, null, config.headers, true), $dom = utils.htmlTo$dom(html), resultUrlList = []; $dom.find(config.itemSelector).each(((index, element) => { const $box2 = $(element); if (!config.findCarNumOrTitle($box2).toLowerCase().includes(carNum2.toLowerCase())) return; let href = config.getDetailPageHref($box2, baseUrl, carNum2); if (!href) throw new Error("解析href失败"); href.includes("http") || (href = baseUrl + (href.startsWith("/") ? href : "/" + href)); resultUrlList.push(href); })); let tagHtml = "", insertCacheData = null; if (1 === resultUrlList.length) { let resultUrl = resultUrlList[0]; $btn.attr("href", resultUrl); $btn.css("backgroundColor", this.okBackgroundColor); insertCacheData = { type: "single", url: resultUrl }; } else if (resultUrlList.length > 1) { $btn.attr("href", searchUrl); tagHtml += '多结果'; $btn.css("backgroundColor", this.okBackgroundColor); insertCacheData = { type: "multiple", url: searchUrl }; } else { $btn.attr("href", searchUrl); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", this.errorBackgroundColor); } insertCacheData && this.asyncQueue.addTask((() => { const newCacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}; newCacheData[siteKey] = insertCacheData; localStorage.setItem(cacheKey, JSON.stringify(newCacheData)); })); tagHtml && $btn.append(tagHtml); } catch (e) { const errorString = String(e), siteName = config.id.replace("Btn", ""); if (errorString.includes("Just a moment")) { $btn.attr("title", "请求失败:Cloudflare 安全检查。"); $btn.css("backgroundColor", this.warnBackgroundColor); clog.warn(`检测第三方资源失败, ${siteName} 需Cloudflare安全检查`); } else if (errorString.includes("重定向")) { $btn.attr("title", "域名失效"); $btn.css("backgroundColor", this.domainErrorBackgroundColor); clog.warn(`检测第三方资源失败, ${siteName} 域名被重定向`); } else if (errorString.includes("404 Page Not Found")) { $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", this.errorBackgroundColor); } else { console.error(e); $btn.attr("title", "请求失败。"); $btn.css("backgroundColor", this.errorBackgroundColor); clog.warn(`检测第三方资源失败, ${siteName}`); } } } async getSettingCache() { const now = Date.now(); if (!this.settingCache || now - this.lastFetchTime > this.CACHE_DURATION) { this.settingCache = await storageManager.getSetting(); this.lastFetchTime = now; } return this.settingCache; } async getMissAvUrl() { return (await this.getSettingCache()).missAvUrl || "https://missav.live"; } async getjableUrl() { return (await this.getSettingCache()).jableUrl || "https://jable.tv"; } async getJavTrailersUrl() { return (await this.getSettingCache()).javTrailersUrl || "https://javtrailers.com"; } async getAv123Url() { return (await this.getSettingCache()).av123Url || "https://123av.com"; } async getJavDbUrl() { return (await this.getSettingCache()).javDbUrl || "https://javdb.com"; } async getJavBusUrl() { return (await this.getSettingCache()).javBusUrl || "https://www.javbus.com"; } async getSupJavUrl() { return (await this.getSettingCache()).supJavUrl || "https://supjav.com"; } getEnabledSites() { const enabledSites = localStorage.getItem("jhs_enabled_sites"); return enabledSites ? JSON.parse(enabledSites) : this.siteConfigs.map(((c) => c.id)); } saveEnabledSites(sites) { localStorage.setItem("jhs_enabled_sites", JSON.stringify(sites)); } renderSettingsArea() { const enabledSites = this.getEnabledSites(), checkboxesDiv = document.getElementById("siteCheckboxes"); checkboxesDiv && (checkboxesDiv.innerHTML = this.siteConfigs.map(((config) => { const isEnabled = enabledSites.includes(config.id); return `
    `; })).join("")); } setupEventListeners() { const settingsArea = document.getElementById("settingsArea"); document.addEventListener("click", ((event) => { if ("settingSiteBtn" === event.target.id || event.target.closest("#settingSiteBtn")) { const isHidden = "none" === settingsArea.style.display || "" === settingsArea.style.display; settingsArea.style.display = isHidden ? "block" : "none"; } })); settingsArea.addEventListener("change", ((event) => { if ("checkbox" === event.target.type) { const siteId = event.target.getAttribute("data-site-id"); if (event.target.checked) { $(`#${siteId}`).show(); const carNum2 = this.getPageInfo().carNum, config = this.siteConfigs.find(((item) => item.id === siteId)); this.handleSite(carNum2, config).then(); } else $(`#${siteId}`).hide(); const enabledSites = Array.from(settingsArea.querySelectorAll('input[type="checkbox"]:checked')).map(((checkbox) => checkbox.getAttribute("data-site-id"))); this.saveEnabledSites(enabledSites); } })); } } class ReviewPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "floorIndex", 1); __publicField2(this, "isInit", false); } getName() { return "ReviewPlugin"; } async handle() { $(document).on("click", ".down-115", (async (event) => { const magnet = $(event.currentTarget).data("magnet"); let loadObj = loading(); try { await this.getBean("WangPan115TaskPlugin").handleAddTask(magnet); } catch (e) { show.error("发生错误:" + e); console.error(e); } finally { loadObj.close(); } })); if (window.isDetailPage) { if (isJavDb$1) { const movieId = this.parseMovieId(window.location.href); await this.showReview(movieId); await this.getBean("RelatedPlugin").showRelated($("#magnets-content"), movieId); } if (isJavBus$1) { let carNum2 = this.getPageInfo().carNum; const movies = await javDbApi.searchMovie(carNum2); let movieId = null; for (let i = 0; i < movies.length; i++) { let item = movies[i]; if (item.number.toLowerCase() === carNum2.toLowerCase()) { movieId = item.id; break; } } if (!movieId) return; this.showReview(movieId, $("#sample-waterfall")).then(); } } } async showReview(movieId, $eleBox) { const enableLoadReview = await storageManager.getSetting("enableLoadReview", YES$1), $magnets = $eleBox || $("#magnets-content"); $magnets.append(`
    ❓ 评论区 ${enableLoadReview === YES$1 ? "折叠" : "展开"} ${enableLoadReview === YES$1 ? "▲" : "▼"}
    `); $("#reviewsFold").on("click", ((event) => { event.preventDefault(); event.stopPropagation(); const $text = $("#reviewsFold .toggle-text"), $icon = $("#reviewsFold .toggle-icon"), isFolded = "展开" === $text.text(); $text.text(isFolded ? "折叠" : "展开"); $icon.text(isFolded ? "▲" : "▼"); if (isFolded) { $("#reviewsContainer").show(); $("#reviewsFooter").show(); if (!this.isInit) { this.fetchAndDisplayReviews(movieId); this.isInit = true; } storageManager.saveSettingItem("enableLoadReview", YES$1); } else { $("#reviewsContainer").hide(); $("#reviewsFooter").hide(); storageManager.saveSettingItem("enableLoadReview", NO); } })); $magnets.append('
    '); $magnets.append('
    '); enableLoadReview === YES$1 && await this.fetchAndDisplayReviews(movieId); } async fetchAndDisplayReviews(movieId) { const $reviewsContainer = $("#reviewsContainer"), $reviewsFooter = $("#reviewsFooter"); $reviewsContainer.append('
    获取评论中...
    '); const reviewCount = await storageManager.getSetting("reviewCount", 20); let dataList = null; try { dataList = await javDbApi.getReviews(movieId, 1, reviewCount); } catch (e) { e.toString().includes("簽名已過期") && show.error("生成签名失败, 请检查系统时间及时区是否正确!"); clog.error("获取评论失败:", e); console.error("获取评论失败:", e); } finally { $("#reviewsLoading").remove(); } if (!dataList) { $reviewsContainer.append('\n
    \n 获取评论失败\n 重试\n
    \n '); $("#retryFetchReviews").on("click", (async () => { $("#retryFetchReviews").parent().remove(); await this.fetchAndDisplayReviews(movieId); })); return; } if (0 === dataList.length) { $reviewsContainer.append('
    无评论
    '); return; } const reviewKeywordList = await storageManager.getReviewFilterKeywordList(); this.displayReviews(dataList, $reviewsContainer, reviewKeywordList); if (dataList.length === reviewCount) { $reviewsFooter.html('\n \n \n '); let currentPage = 1, $loadMoreReviews = $("#loadMoreReviews"); $loadMoreReviews.on("click", (async () => { $loadMoreReviews.text("加载中...").prop("disabled", true); currentPage++; let moreData; try { moreData = await javDbApi.getReviews(movieId, currentPage, reviewCount); } catch (e) { console.error("加载更多评论失败:", e); } finally { $loadMoreReviews.text("加载失败, 请点击重试").prop("disabled", false); } if (moreData) { this.displayReviews(moreData, $reviewsContainer, reviewKeywordList); if (moreData.length < reviewCount) { $loadMoreReviews.remove(); $("#reviewsEnd").show(); } else $loadMoreReviews.text("加载更多评论").prop("disabled", false); } })); } else $reviewsFooter.html('
    已加载全部评论
    '); } displayReviews(dataList, $container, reviewKeywordList) { if (dataList.length) { dataList.forEach(((item) => { if (reviewKeywordList.some(((keyword) => item.content.includes(keyword)))) return; const starsHtml = Array(item.score).fill('').join(""), content = item.content.replace(/ed2k:\/\/\|file\|[^|]+\|\d+\|[a-fA-F0-9]{32}\|\/|magnet:\?[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+|https?:\/\/[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+/g, ((match) => match.startsWith("ed2k://") ? ` ${match} ` : match.startsWith("magnet:") ? ` ${match} ` : match.startsWith("http://") || match.startsWith("https://") ? ` ${match} ` : match)), commentHtml = `
    #${this.floorIndex++}楼 ${item.username}    ${starsHtml} ${utils.formatDate(item.created_at)}    点赞:${item.likes_count}

    ${content}

    `; $container.append(commentHtml); })); this.rightClickFilter(); } } async rightClickFilter() { await storageManager.getSetting("enableTitleSelectFilter", YES$1) === YES$1 && utils.rightClick(document.body, ".review-content", (async (event) => { const selectedText = window.getSelection().toString(); if (selectedText) { event.preventDefault(); await utils.q(event, `是否将 '${selectedText}' 加入评论区关键词?`, (async () => { await storageManager.saveReviewFilterKeyword(selectedText); show.ok("操作成功, 刷新页面后生效"); })); } })); } } class FilterTitleKeywordPlugin extends BasePlugin { getName() { return "FilterTitleKeywordPlugin"; } async handle() { if (!window.isDetailPage && !window.isFc2Page) return; if (await storageManager.getSetting("enableTitleSelectFilter", YES$1) !== YES$1) return; let targetSelector; isJavDb$1 ? targetSelector = ".title strong, .current-title" : isJavBus$1 && (targetSelector = "h3"); utils.rightClick(document.body, targetSelector, ((event) => { const selectedText = window.getSelection().toString(); if (selectedText) { event.preventDefault(); let tempEvent = { clientX: event.clientX, clientY: event.clientY + 80 }; utils.q(tempEvent, `是否屏蔽标题关键词 ${selectedText}?`, (async () => { await storageManager.saveTitleFilterKeyword(selectedText); window.refresh(); utils.closePage(); })); } })); } } class CoverButtonPlugin extends BasePlugin { getName() { return "CoverButtonPlugin"; } async initCss() { return ` `; } handle() { if (window.isListPage) { this.addSvgBtn(); this.bindClick().then(); } } async addSvgBtn() { $(this.getSelector().itemSelector).toArray().forEach(((ele) => { let $box2 = $(ele); if (!($box2.find(".tool-box").length > 0)) { isJavDb$1 && $box2.find(".tags").append(`
    ${this.screenSvg} ${this.videoSvg}
    ${this.handleSvg}
    ${this.siteSvg}
    ${this.copySvg}
    `); if (isJavBus$1) { if ($box2.find(".avatar-box").length > 0) return; $box2.find(".photo-info").append(`
    ${this.screenSvg} ${this.videoSvg}
    ${this.handleSvg}
    ${this.siteSvg}
    ${this.copySvg}
    `); } } })); this.enableSvgBtn(); } async enableSvgBtn() { const settingObj = await storageManager.getSetting(), { enableScreenSvg = YES$1, enableVideoSvg = YES$1, enableHandleSvg = YES$1, enableSiteSvg = YES$1, enableCopySvg = YES$1 } = settingObj; [{ selector: ".screenSvg", enabled: enableScreenSvg }, { selector: ".videoSvg", enabled: enableVideoSvg }, { selector: ".handleSvg", enabled: enableHandleSvg }, { selector: ".siteSvg", enabled: enableSiteSvg }, { selector: ".copySvg", enabled: enableCopySvg }].forEach((({selector, enabled}) => { $(selector).toggle(enabled === YES$1); })); } async bindClick() { this.getSelector(); const listPagePlugin = this.getBean("ListPagePlugin"); $(document).on("click", ".more-tools-container", ((event) => { event.preventDefault(); var $currentTools = $(event.target).closest(".more-tools-container").find(".more-tools"); $(".more-tools").not($currentTools).stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide(); $currentTools.is(":visible") ? $currentTools.stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide() : $currentTools.stop(true, true).removeClass("elastic-out").addClass("elastic-in").show(); })); $(document).on("click", (function (event) { $(event.target).closest(".more-tools-container").length || $(".more-tools").stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide(); })); $(document).on("click", ".videoSvg", ((event) => { event.preventDefault(); $('.videoSvg[title!="播放视频"]').each(((index, element) => { const $otherSvgElement = $(element); let $otherBox = $otherSvgElement.closest(".item"), $otherImg = $otherBox.find("img"), {carNum: carNum2} = this.getBoxCarInfo($otherBox); this.showImg($otherSvgElement, $otherImg, carNum2); $otherSvgElement.html(this.videoSvg).attr("title", "播放视频"); })); const $currentBox = $(event.target).closest(".item"), $svgElement = $currentBox.find(".videoSvg"); if ("播放视频" === $svgElement.attr("title")) { $svgElement.html(this.recoveryVideoSvg).attr("title", "切回封面"); const {carNum: carNum2} = this.getBoxCarInfo($currentBox); let $img = $currentBox.find("img"); if (!$img.length) { show.error("没有找到图片"); return; } this.showVideo($svgElement, $img, carNum2).then(); } })); $(document).on("click", ".screenSvg", (async (event) => { event.preventDefault(); let loadObj = loading(); try { const $box2 = $(event.currentTarget).closest(".item"); let {carNum: carNum2} = this.getBoxCarInfo($box2); carNum2 = carNum2.replace("FC2-", ""); const imgUrl = await this.getBean("ScreenShotPlugin").getScreenshot(carNum2); loadObj.close(); showPreviewOverlay(imgUrl, carNum2); } catch (error) { console.error("图片预览出错:", error); show.error("图片预览出错:" + error); } finally { loadObj.close(); } })); $(document).on("click", ".filterBtn, .favoriteBtn, .hasDownBtn, .hasWatchBtn", ((event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.target).closest(".menu-btn"), $box2 = $btn.closest(".item"), { carNum: carNum2, url, publishTime } = this.getBoxCarInfo($box2), handleAction = async (status) => { let actress = await listPagePlugin.parseActressName(url); await storageManager.saveCar({ carNum: carNum2, url, names: actress, actionType: status, publishTime }); window.refresh(); show.ok("操作成功"); }; $btn.hasClass("filterBtn") ? utils.q(event, `是否屏蔽${carNum2}?`, (() => handleAction(Status_FILTER$1))) : $btn.hasClass("favoriteBtn") ? handleAction(Status_FAVORITE$1).then() : $btn.hasClass("hasDownBtn") ? handleAction(Status_HAS_DOWN$1).then() : $btn.hasClass("hasWatchBtn") && handleAction(Status_HAS_WATCH$1).then(); $(".more-tools").stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide(); })); const otherSitePlugin = this.getBean("OtherSitePlugin"), missAvUrl = await otherSitePlugin.getMissAvUrl(), jableUrl = await otherSitePlugin.getjableUrl(), av123Url = await otherSitePlugin.getAv123Url(); $(document).on("click", ".site-jable, .site-miss-av, .site-123-av", ((event) => { event.preventDefault(); event.stopPropagation(); const $currentTarget = $(event.currentTarget), $box2 = $currentTarget.closest(".item"), {carNum: carNum2} = this.getBoxCarInfo($box2); let url = null; $currentTarget.hasClass("site-jable") ? url = `${jableUrl}/search/${carNum2}/` : $currentTarget.hasClass("site-miss-av") ? url = `${missAvUrl}/search/${carNum2}` : $currentTarget.hasClass("site-123-av") && (url = `${av123Url}/ja/search?keyword=${carNum2}`); event && (event.ctrlKey || event.metaKey) ? _GM_openInTab(url, { insert: 0 }) : window.open(url); })); $(document).on("click", ".titleSvg, .carNumSvg, .downSvg", ((event) => { event.preventDefault(); event.stopPropagation(); const $box2 = $(event.currentTarget).closest(".item"), { carNum: carNum2, title } = this.getBoxCarInfo($box2), $img = $box2.find(isJavBus$1 ? ".photo-frame img" : ".cover img"); $(event.currentTarget).hasClass("titleSvg") ? utils.copyToClipboard("标题", title) : $(event.currentTarget).hasClass("carNumSvg") ? utils.copyToClipboard("番号", carNum2) : $(event.currentTarget).hasClass("downSvg") && fetch($img.attr("src")).then(((response) => response.blob())).then(((blob) => { utils.download(blob, carNum2 + " " + title + ".jpg"); })); })); $(document).on("click", ".markDeleteSvg", (async (event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.currentTarget); const $box2 = $btn.closest(".item"); const {carNum: carNum2, title, publishTime, url} = this.getBoxCarInfo($box2); const score = $box2.find(".score").text().trim() || "无评分"; let markedList = await storageManager.forage.getItem("markedDeleteList") || []; const existingIndex = markedList.findIndex((item) => item.carNum === carNum2); if (existingIndex > -1) { markedList.splice(existingIndex, 1); $btn.removeClass("marked"); $btn.attr("title", "标记删除"); show.ok(`已取消标记: ${title}`); } else { markedList.push({ carNum: carNum2, title, publishTime, score, url, markedTime: (/* @__PURE__ */ new Date()).toISOString() }); $btn.addClass("marked"); $btn.attr("title", "取消标记删除"); show.ok(`已标记删除: ${title}`); } await storageManager.forage.setItem("markedDeleteList", markedList); })); $(document).on("click", ".delete115Svg", (async (event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.currentTarget); const matchData = $btn.attr("data-match"); if (!matchData) { show.error("未找到115匹配数据"); return; } const matchList = JSON.parse(matchData); const $box2 = $btn.closest(".item"); const {carNum: carNum2} = this.getBoxCarInfo($box2); this.getBean("WangPan115MatchPlugin").showDeleteDialog(carNum2, matchList, (async (newMatchList) => { this.getBean("WangPan115MatchPlugin").updateMatchStatus($box2, carNum2, newMatchList); })); })); } showImg($svgElement, $img, carNum2) { $svgElement.html(this.videoSvg).attr("title", "播放视频"); let $video = $(`#${`${carNum2}_preview_video`}`); if ($video.length > 0) { $video[0].pause(); $video.parent().hide(); } $img.show(); $img.removeClass("loading"); $img.next(".loading-spinner").remove(); } async showVideo($svgElement, $img, carNum2) { const id = `${carNum2}_preview_video`; let $video = $(`#${id}`); if ($video.length > 0) { $video.parent().show(); $video[0].play(); $img.hide(); return; } $img.addClass("loading"); $img.after('
    '); const poster = $img.attr("src"), dmmVideoMap = await getDmmVideo(carNum2); if (!dmmVideoMap) { show.error("未解析到视频"); this.showImg($svgElement, $img, carNum2); return; } let defaultVideoQuality = await storageManager.getSetting("videoQuality"); defaultVideoQuality = selectDefaultQuality(Object.keys(dmmVideoMap), defaultVideoQuality); let videoUrl = dmmVideoMap[defaultVideoQuality], videoHtml = `
    `; isJavBus$1 && (videoHtml = `
    `); $img.parent().append(videoHtml); $img.hide(); $img.removeClass("loading"); $img.next(".loading-spinner").remove(); $video = $(`#${id}`); let videoElement = $video[0]; videoElement.load(); videoElement.muted = false; videoElement.play(); $video.trigger("focus"); } } class Fc2By123AvPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "$contentBox", $(".section .container")); __publicField2(this, "urlParams", new URLSearchParams(window.location.search)); __publicField2(this, "sortVal", this.urlParams.get("sort") || "release_date"); __publicField2(this, "currentPage", this.urlParams.get("page") ? parseInt(this.urlParams.get("page")) : 1); __publicField2(this, "maxPage", null); __publicField2(this, "keyword", this.urlParams.get("keyword") || null); } getName() { return "Fc2By123AvPlugin"; } async getBaseUrl() { const otherSitePlugin = this.getBean("OtherSitePlugin"); return await otherSitePlugin.getAv123Url() + "/ja"; } handle() { $("#navbar-menu-hero > div > div:nth-child(1) > div > a:nth-child(4)").after('123Av-Fc2'); $('.tabs li:contains("FC2")').after('
  • 123Av-Fc2
  • '); if (currentHref$1.includes("/advanced_search?type=100")) { this.hookPage(); this.handleQuery().then(); } } hookPage() { let $h2 = $("h2.section-title"); $h2.contents().first().replaceWith("123Av"); $h2.css("marginBottom", "0"); $h2.append('\n
    \n \n 搜索\n 重置\n
    \n '); $("#search-123av-keyword").val(this.keyword); $("#search-123av-btn").on("click", (async () => { let keyword = $("#search-123av-keyword").val().trim(); if (keyword) { this.keyword = keyword; utils.setHrefParam("keyword", keyword); await this.handleQuery(); } })); $("#clear-123av-btn").on("click", (async () => { $("#search-123av-keyword").val(""); this.keyword = ""; utils.setHrefParam("keyword", ""); $(".page-box").show(); $(".tool-box").show(); await this.handleQuery(); })); $(".empty-message").remove(); $("#foldCategoryBtn").remove(); $(".section .container .box").remove(); $("#sort-toggle-btn").remove(); this.$contentBox.append('
    '); this.$contentBox.append('
    '); this.$contentBox.append('
    '); $(".tool-box").append('\n
    \n
    \n 发布日期\n 最近更新\n 热门\n 今天最多观看\n 本周最多观看\n 本月最多观看\n 最多观看\n 最受欢迎\n
    \n
    \n '); $(`#conditionBox a[data-sort="${this.sortVal}"]`).addClass("is-info"); utils.setHrefParam("sort", this.sortVal); utils.setHrefParam("page", this.currentPage); $("#conditionBox").on("click", "a.button", ((e) => { let $target = $(e.target); this.sortVal = $target.data("sort"); utils.setHrefParam("sort", this.sortVal); $target.siblings().removeClass("is-info"); $target.addClass("is-info"); this.handleQuery(); })); $(".page-box").append('\n \n '); $(document).on("click", ".pagination-link", ((e) => { e.preventDefault(); this.currentPage = parseInt($(e.target).data("page")); utils.setHrefParam("page", this.currentPage); this.renderPagination(); this.handleQuery(); })); $(".pagination-previous").on("click", ((e) => { e.preventDefault(); if (this.currentPage > 1) { this.currentPage--; utils.setHrefParam("page", this.currentPage); this.renderPagination(); this.handleQuery(); } })); $(".pagination-next").on("click", ((e) => { e.preventDefault(); if (this.currentPage < this.maxPage) { this.currentPage++; utils.setHrefParam("page", this.currentPage); this.renderPagination(); this.handleQuery(); } })); } renderPagination() { const $paginationList = $(".pagination-list"); $paginationList.empty(); let startPage = Math.max(1, this.currentPage - 2), endPage = Math.min(this.maxPage, this.currentPage + 2); this.currentPage <= 3 ? endPage = Math.min(6, this.maxPage) : this.currentPage >= this.maxPage - 2 && (startPage = Math.max(this.maxPage - 5, 1)); if (startPage > 1) { $paginationList.append('
  • 1
  • '); startPage > 2 && $paginationList.append('
  • …
  • '); } for (let i = startPage; i <= endPage; i++) { const activeClass = i === this.currentPage ? " is-current" : ""; $paginationList.append(`
  • ${i}
  • `); } if (endPage < this.maxPage) { endPage < this.maxPage - 1 && $paginationList.append('
  • …
  • '); $paginationList.append(`
  • ${this.maxPage}
  • `); } } async handleQuery() { let loadObj = loading(); try { let pagesToFetch = []; pagesToFetch = 1 === this.currentPage ? [1, 2] : [2 * this.currentPage - 1, 2 * this.currentPage]; if (this.keyword) { pagesToFetch = [1]; $(".page-box").hide(); $(".tool-box").hide(); } const baseUrl = await this.getBaseUrl(), fetchPromises = pagesToFetch.map(((page) => { let url = `${baseUrl}/tags/fc2?sort=${this.sortVal}&page=${page}`; this.keyword && (url = `${baseUrl}/search?keyword=${this.keyword}`); return gmHttp.get(url); })), htmlResults = await Promise.all(fetchPromises); let dataList = []; for (const html of htmlResults) { let $dom = $(html); $dom.find(".box-item").each(((index, element) => { const $item = $(element), imgSrc = $item.find("img").attr("data-src"); let carNum2 = $item.find("img").attr("title"); const detailLink = $item.find(".detail a"), link = detailLink.attr("href"), href = baseUrl + (link.startsWith("/") ? link : "/" + link), title = detailLink.text().trim().replace(carNum2 + " - ", ""); carNum2 = carNum2.replace("FC2-PPV", "FC2"); dataList.push({ imgSrc, carNum: carNum2, href, title }); })); if (!this.maxPage) { let rawMaxPage, lastPageItem = $dom.find(".page-item:not(.disabled)").last(); if (lastPageItem.find("a.page-link").length) { let href = lastPageItem.find("a.page-link").attr("href"); rawMaxPage = parseInt(href.split("page=")[1]); } else rawMaxPage = parseInt(lastPageItem.find("span.page-link").text()); this.maxPage = Math.ceil(rawMaxPage / 2); this.renderPagination(); } } if (0 === dataList.length) { console.log(dataList); show.error("无结果"); let errorUrl = `${baseUrl}/dm4/tags/fc2?sort=${this.sortVal}`; this.keyword && (errorUrl = `${baseUrl}/search?keyword=${this.keyword}`); console.error("获取数据失败!", errorUrl); } let movieHtml = this.markDataListHtml(dataList); $(".movie-list").html(movieHtml); await utils.smoothScrollToTop(); } catch (e) { console.error(e); } finally { loadObj.close(); } } async open123AvFc2Dialog(carNum2, href) { let otherHtml = ""; await storageManager.getSetting("enableLoadOtherSite", YES$1) === YES$1 && (otherHtml = '
    第三方站点:
    '); let pageHtml = `
    加载中...
    ${otherHtml}
    🚫 屏蔽 ⭐ 收藏 📥️ 已下载 🔍 已观看 字幕搜索
    `; layer.open({ type: 1, title: carNum2, content: pageHtml, area: utils.getDefaultArea(), skin: "movie-detail-layer", scrollbar: false, success: (layero, index) => { utils.setupEscClose(index); this.loadData(carNum2, href); let keyword = carNum2.replace("FC2-", ""); $("#magnets-content").append(this.getBean("MagnetHubPlugin").createMagnetHub(keyword)); $("#favoriteBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FAVORITE$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#filterBtn").on("click", ((event) => { utils.q(event, `是否屏蔽${carNum2}?`, (async () => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FILTER$1, publishTime }); window.refresh(); layer.closeAll(); window.location.href.includes("collection_codes?movieId") && utils.closePage(); })); })); $("#hasDownBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_DOWN$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#hasWatchBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_WATCH$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#subtitleSearchBtn").on("click", (() => { window.JavPackSubtitle.openSearchModal({details: {code: carNum2}}); })); window.JavPackSubtitle.preload115Matches(carNum2); let tempCarNum = carNum2.replace("FC2-", ""); this.getBean("OtherSitePlugin").loadOtherSite(tempCarNum, carNum2).then(); } }); } async loadData(carNum2, href) { let loadObj = loading(); try { const {id, publishDate, title, moviePoster} = await this.get123AvVideoInfo(href); $(".movie-info-container").html(`

    ${title || "无标题"}

    番号: ${carNum2 || "未知"} 年份: ${publishDate || "未知"} 站点: fc2ppvdb fc2电子市场
    主演:
    販売者:
    `); this.getImgList(carNum2).then(); this.getActressInfo(carNum2).then(); this.getBean("TranslatePlugin").translate(carNum2, false).then(); } catch (e) { console.error(e); } finally { loadObj.close(); } } handleLongImg(carNum2) { utils.loopDetector((() => $(".movie-gallery .image-list").length > 0), (async () => { $(".movie-gallery .image-list").prepend('
    正在加载缩略图
    '); const imgUrl = await this.getBean("ScreenShotPlugin").getScreenshot(carNum2); if (imgUrl) { $(".screen-container").html(``); $(".screen-container").on("click", ((event) => { event.stopPropagation(); event.preventDefault(); showPreviewOverlay(imgUrl, carNum2); })); } })); } async get123AvVideoInfo(href) { const html = await gmHttp.get(href), match = html.match(/v-scope="Movie\({id:\s*(\d+),/), id = match ? match[1] : null, $dom = utils.htmlTo$dom(html); return { id, publishDate: $dom.find('span:contains("リリース日:")').next("span").text(), title: $dom.find("h1").text().trim(), moviePoster: $dom.find("#player").attr("data-poster") }; } async getActressInfo(fc2Num) { let url = `https://fc2ppvdb.com/articles/${fc2Num.replace("FC2-", "")}`; const html = await gmHttp.get(url), $dom = $(html), actressNodeList = $dom.find("div").filter((function () { return 0 === $(this).text().trim().indexOf("女優:"); })); if (0 === actressNodeList.length || actressNodeList.length > 1) { show.error("解析女优信息失败"); return; } const $actress = $(actressNodeList[0]).find("a"); let actorsHtml = "主演: "; if ($actress.length > 0) { let actress = ""; $actress.each(((index, ele) => { let $actor = $(ele), name2 = $actor.text(), actressHref = $actor.attr("href"); actorsHtml += `${name2}`; actress += name2 + " "; })); $("#data-actress").text(actress); } else actorsHtml += "暂无演员信息"; $(".actor-list").html(actorsHtml); const sellerNodeList = $dom.find("div").filter((function () { return 0 === $(this).text().trim().indexOf("販売者:"); })); if (sellerNodeList.length > 0) { const $sellerA = $(sellerNodeList[0]).find("a"); if ($sellerA.length > 0) { const $seller = $($sellerA[0]); let name2 = $seller.text(), sellerHref = $seller.attr("href"); $(".movie-seller").html(`販売者: ${name2}`); } } } async getImgList(fc2Num) { let tempCarNum = fc2Num.replace("FC2-", ""), url = `https://adult.contents.fc2.com/article/${fc2Num.replace("FC2-", "")}/`; const html = await gmHttp.get(url, null, { referer: url }); let imgList = $(html).find(".items_article_SampleImagesArea img").map((function () { return $(this).attr("src"); })).get(), imagesHtml = ""; Array.isArray(imgList) && imgList.length > 0 ? imagesHtml = imgList.map(((img, index) => ` `)).join("") : $(".movie-gallery").html("

    剧照: 暂无剧照

    "); $(".image-list").html(imagesHtml); this.handleLongImg(tempCarNum); } async getMovie(id, moviePoster) { let url = `${await this.getBaseUrl()}/ajax/v/${id}/videos`, loadObj = loading(); try { let movieList = (await gmHttp.get(url)).result.watch; if (movieList.length > 0) { movieList.forEach(((movieItem) => { movieItem.url = movieItem.url + "?poster=" + moviePoster; })); return movieList; } return null; } catch (e) { console.error(e); } finally { loadObj.close(); } } markDataListHtml(movies) { let moviesHtml = ""; movies.forEach(((movie) => { moviesHtml += `
    ${movie.carNum} ${movie.title}
    `; })); return moviesHtml; } } class DetailPageButtonPlugin extends BasePlugin { getName() { return "DetailPageButtonPlugin"; } constructor() { super(); this.answerCount = 1; } async handle() { let settingObj = await storageManager.getSetting(); this.filterHotKey = settingObj.filterHotKey; this.favoriteHotKey = settingObj.favoriteHotKey; this.hasDownHotKey = settingObj.hasDownHotKey; this.hasWatchHotKey = settingObj.hasWatchHotKey; this.speedVideoHotKey = settingObj.speedVideoHotKey; this.bindHotkey().then(); this.hideVideoControls(); window.isDetailPage && this.createMenuBtn(); } async createMenuBtn() { const pageInfo = this.getPageInfo(), carNum2 = pageInfo.carNum, buttonsHtml = '\n
    \n
    \n \n 🚫 屏蔽\n \n \n ⭐ 收藏\n \n \n 📥️ 已下载\n \n \n 🔍 已观看\n \n
    \n \n
    \n \n 下载高清封面\n \n \n 排序与过滤\n \n \n 磁力搜索\n \n \n 字幕搜索\n \n \n
    \n
    \n '; isJavDb$1 && $(".tabs").after(buttonsHtml); isJavBus$1 && $("#mag-submit-show").before(buttonsHtml); $("#favoriteBtn").on("click", (() => this.favoriteOne())); $("#filterBtn").on("click", ((event) => this.filterOne(event))); $("#hasDownBtn").on("click", (async () => this.hasDownOne())); $("#hasWatchBtn").on("click", (async () => this.hasWatchOne())); $("#magnetSearchBtn").on("click", (() => { let magnetHub = this.getBean("MagnetHubPlugin").createMagnetHub(pageInfo.carNum); layer.open({ type: 1, title: "磁力搜索 " + pageInfo.carNum, content: '
    ', area: utils.getResponsiveArea(["60%", "80%"]), scrollbar: false, success: () => { $("#magnetHubBox").append(magnetHub); } }); })); const highlightMagnetPlugin = this.getBean("HighlightMagnetPlugin"), enableMagnetsFilter = await storageManager.getSetting("enableMagnetsFilter", NO); if (enableMagnetsFilter === YES$1) { const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}"); let settings; try { settings = JSON.parse(savedSettings); } catch (e) { settings = {}; } const sortEnabled = settings.sortEnabled || {}; const defaultFullSortOrder = isJavBus$1 ? ["date", "size"] : ["date", "size", "files"]; const fullSortOrder = settings.sortOrder && settings.sortOrder.length === defaultFullSortOrder.length ? settings.sortOrder : defaultFullSortOrder; const effectiveSortOrder = fullSortOrder.filter((key) => sortEnabled[key]); const applySettings = { filterHD: settings.filterHD || false, filter4K: settings.filter4K || false, filterSubtitle: settings.filterSubtitle || false, filterUncensored: settings.filterUncensored || false, sortOrder: effectiveSortOrder }; const rowReadyCondition = isJavBus$1 ? (() => $("#magnet-table tr").length > 1) : (() => $("#magnets-content .item[data-rank]").length > 0); utils.loopDetector(rowReadyCondition, () => { highlightMagnetPlugin.applySortAndFilter(applySettings); }); $("#magnets-span").text("取消磁力过滤"); } else { $("#magnets-span").text("排序与过滤"); } $("#enable-magnets-filter").on("click", ((event) => { const $span = $("#magnets-span"); if ("取消磁力过滤" === $span.text()) { highlightMagnetPlugin.showAll(); $span.text("排序与过滤"); storageManager.saveSettingItem("enableMagnetsFilter", NO); } else { this.openMagnetSortFilterPanel(highlightMagnetPlugin); } })); $("#subtitleSearchBtn").on("click", (() => { window.JavPackSubtitle.openSearchModal({details: {code: carNum2}}); })); $("#downloadCoverBtn").on("click", (async () => { if (!isEligibleDmmCoverCode(carNum2)) { show.error("当前番号不支持下载高清封面"); return; } const $btn = $("#downloadCoverBtn"); $btn.addClass("is-loading").prop("disabled", true); try { const coverUrl = await getCover(carNum2); if (!coverUrl) { show.error("未找到高清封面"); return; } const fileName = `${carNum2}-cover.jpg`; _GM_xmlhttpRequest({ method: "GET", url: coverUrl, responseType: "blob", onload: (response) => { if (response.status === 200) { const blobUrl = URL.createObjectURL(response.response); const a = document.createElement("a"); a.href = blobUrl; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(blobUrl); }, 100); show.ok("封面下载已开始"); } else { show.error(`封面下载失败: ${response.status}`); } }, onerror: () => show.error("封面下载网络错误"), ontimeout: () => show.error("封面下载超时") }); } catch (e) { show.error("获取封面信息失败"); } finally { $btn.removeClass("is-loading").prop("disabled", false); } })); window.JavPackSubtitle.preload115Matches(carNum2); this.showStatus(carNum2).then(); } async openMagnetSortFilterPanel(highlightMagnetPlugin) { const existingPanel = document.getElementById("magnet-sort-filter-panel"); if (existingPanel) { existingPanel.remove(); return; } const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}"); let settings; try { settings = JSON.parse(savedSettings); } catch (e) { settings = {}; } const sortEnabled = settings.sortEnabled || (isJavBus$1 ? {date: false, size: false} : { date: false, size: false, files: false }); const defaultSortOrder = isJavBus$1 ? ["date", "size"] : ["date", "size", "files"]; const sortOrder = settings.sortOrder && settings.sortOrder.length === defaultSortOrder.length ? settings.sortOrder : defaultSortOrder; const filterHD = settings.filterHD || false; const filter4K = settings.filter4K || false; const filterSubtitle = settings.filterSubtitle || false; const filterUncensored = settings.filterUncensored || false; const SORT_LABELS = isJavBus$1 ? {date: "发布日期", size: "文件大小"} : { date: "发布日期", size: "文件大小", files: "文件数量" }; const SORT_HINTS = isJavBus$1 ? {date: "从新到旧", size: "从大到小"} : { date: "从新到旧", size: "从大到小", files: "从少到多" }; const FILTER_ITEMS = [ {key: "HD", label: "高清", checked: filterHD}, {key: "4K", label: "4K", checked: filter4K}, {key: "Subtitle", label: "字幕", checked: filterSubtitle}, {key: "Uncensored", label: "无码", checked: filterUncensored} ]; const currentSortOrder = [...sortOrder]; const currentSortEnabled = {...sortEnabled}; const currentFilters = { HD: filterHD, "4K": filter4K, Subtitle: filterSubtitle, Uncensored: filterUncensored }; const buildSortItemsHTML = () => { return currentSortOrder.map((key) => { const checked = currentSortEnabled[key] ? "checked" : ""; return `
    ⋮⋮
    `; }).join(""); }; const buildFilterItemsHTML = () => { return FILTER_ITEMS.map((item) => { const checked = currentFilters[item.key] ? "checked" : ""; return ``; }).join(""); }; const panelHTML = `
    磁力排序与过滤 ×
    排序优先级(拖动调整顺序,勾选启用)
    ${buildSortItemsHTML()}
    过滤条件(可多选,并集关系)
    ${buildFilterItemsHTML()}
    `; const wrapper = document.createElement("div"); wrapper.innerHTML = panelHTML; document.body.appendChild(wrapper); const panel = document.getElementById("magnet-sort-filter-panel"); const overlay = document.getElementById("msf-overlay"); const closePanel = () => { panel.remove(); overlay.remove(); }; overlay.addEventListener("click", closePanel); document.getElementById("msf-close-btn").addEventListener("click", closePanel); document.getElementById("msf-cancel-btn").addEventListener("click", closePanel); const rebuildSortList = () => { const sortList = document.getElementById("msf-sort-list"); sortList.innerHTML = buildSortItemsHTML(); bindSortEvents(); }; const bindSortEvents = () => { const sortItems = document.querySelectorAll("#msf-sort-list .msf-sort-item"); sortItems.forEach((item) => { item.addEventListener("dragstart", (e) => { e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", item.dataset.sortKey); item.classList.add("msf-dragging"); }); item.addEventListener("dragend", () => { item.classList.remove("msf-dragging"); document.querySelectorAll("#msf-sort-list .msf-sort-item").forEach((el) => el.classList.remove("msf-drag-over")); }); item.addEventListener("dragover", (e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; item.classList.add("msf-drag-over"); }); item.addEventListener("dragleave", () => { item.classList.remove("msf-drag-over"); }); item.addEventListener("drop", (e) => { e.preventDefault(); item.classList.remove("msf-drag-over"); const fromKey = e.dataTransfer.getData("text/plain"); const toKey = item.dataset.sortKey; if (fromKey === toKey) return; const fromIdx = currentSortOrder.indexOf(fromKey); const toIdx = currentSortOrder.indexOf(toKey); currentSortOrder.splice(fromIdx, 1); currentSortOrder.splice(toIdx, 0, fromKey); rebuildSortList(); }); const checkbox = item.querySelector(".msf-sort-checkbox"); checkbox.addEventListener("change", () => { currentSortEnabled[item.dataset.sortKey] = checkbox.checked; }); }); }; bindSortEvents(); document.querySelectorAll(".msf-filter-checkbox").forEach((cb) => { cb.addEventListener("change", () => { currentFilters[cb.dataset.filterKey] = cb.checked; }); }); document.getElementById("msf-confirm-btn").addEventListener("click", async () => { try { const enabledSortOrder = currentSortOrder.filter((key) => currentSortEnabled[key]); const newSettings = { sortOrder: currentSortOrder, sortEnabled: currentSortEnabled, filterHD: currentFilters.HD, filter4K: currentFilters["4K"], filterSubtitle: currentFilters.Subtitle, filterUncensored: currentFilters.Uncensored }; await storageManager.saveSettingItem("magnetSortAndFilter", JSON.stringify(newSettings)); const hasAnyFilter = currentFilters.HD || currentFilters["4K"] || currentFilters.Subtitle || currentFilters.Uncensored; const hasAnySort = enabledSortOrder.length > 0; if (hasAnyFilter || hasAnySort) { await highlightMagnetPlugin.applySortAndFilter({ filterHD: currentFilters.HD, filter4K: currentFilters["4K"], filterSubtitle: currentFilters.Subtitle, filterUncensored: currentFilters.Uncensored, sortOrder: enabledSortOrder }); $("#magnets-span").text("取消磁力过滤"); await storageManager.saveSettingItem("enableMagnetsFilter", YES$1); } else { highlightMagnetPlugin.showAll(); $("#magnets-span").text("排序与过滤"); await storageManager.saveSettingItem("enableMagnetsFilter", NO); } } catch (e) { console.error("[磁力排序] 应用失败:", e); } closePanel(); }); } async showStatus(carNum2) { const $filterBtn = $("#filterBtn span"), $favoriteBtn = $("#favoriteBtn span"), $hasDownBtn = $("#hasDownBtn span"), $hasWatchBtn = $("#hasWatchBtn span"), hotKeyDisplay = (hotKey) => hotKey ? `(${hotKey})` : ""; $filterBtn.text(`🚫 屏蔽 ${hotKeyDisplay(this.filterHotKey)}`); $favoriteBtn.text(`⭐ 收藏 ${hotKeyDisplay(this.favoriteHotKey)}`); $hasDownBtn.text(`📥️ 已下载 ${hotKeyDisplay(this.hasDownHotKey)}`); $hasWatchBtn.text(`🔍 已观看 ${hotKeyDisplay(this.hasWatchHotKey)}`); const car = await storageManager.getCar(carNum2); if (car) switch (car.status) { case Status_FILTER$1: $filterBtn.text(`🚫 已屏蔽 ${hotKeyDisplay(this.filterHotKey)}`); break; case Status_FAVORITE$1: $favoriteBtn.text(`⭐ 已收藏 ${hotKeyDisplay(this.favoriteHotKey)}`); break; case Status_HAS_DOWN$1: $hasDownBtn.text(`📥️ 已标记下载 ${hotKeyDisplay(this.hasDownHotKey)}`); break; case Status_HAS_WATCH$1: $hasWatchBtn.text(`🔍 已标记观看 ${hotKeyDisplay(this.hasWatchHotKey)}`); } } async favoriteOne() { let pageInfo = this.getPageInfo(); await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_FAVORITE$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); } async hasDownOne() { let pageInfo = this.getPageInfo(); await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_HAS_DOWN$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); } async hasWatchOne() { let pageInfo = this.getPageInfo(); await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_HAS_WATCH$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); } /* searchXunLeiSubtitle(carNum2) { let loadObj = loading(); gmHttp.get(`https://api-shoulei-ssl.xunlei.com/oracle/subtitle?gcid=&cid=&name=${carNum2}`).then((res => { let dataList = res.data; dataList && 0 !== dataList.length ? layer.open({ type: 1, title: "迅雷字幕", content: '\n
    \n
    \n
    \n ', scrollbar: !1, area: utils.getResponsiveArea([ "60%", "70%" ]), anim: -1, success: (layero, index) => { new Tabulator("#xunlei-table-container", { layout: "fitColumns", placeholder: "暂无数据", virtualDom: !0, data: dataList, responsiveLayout: "collapse", responsiveLayoutCollapse: !0, columnDefaults: { headerHozAlign: "center", hozAlign: "center" }, columns: [ { title: "文件名", field: "name", headerSort: !1, responsive: 0 }, { title: "类型", field: "ext", headerSort: !1, responsive: 0 }, { title: "操作", responsive: 0, headerSort: !1, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); onRendered((() => { const previewButton = cell.getElement().querySelector(".a-primary"), downButton = cell.getElement().querySelector(".a-success"); previewButton && previewButton.addEventListener("click", (async e => { let url = item.url, name2 = carNum2 + "." + item.ext; this.previewSubtitle(url, name2); })); downButton && downButton.addEventListener("click", (async e => { let url = item.url, name2 = carNum2 + "." + item.ext, content = await gmHttp.get(url); utils.download(content, name2); })); })); return '\n 预览\n 下载\n '; } } ], locale: "zh-cn", langs: { "zh-cn": { pagination: { first: "首页", first_title: "首页", last: "尾页", last_title: "尾页", prev: "上一页", prev_title: "上一页", next: "下一页", next_title: "下一页", all: "所有", page_size: "每页行数" } } } }); utils.setupEscClose(index); } }) : show.error("迅雷中找不到相关字幕!"); })).catch((e => { console.error(e); show.error(e); })).finally((() => { loadObj.close(); })); } */ async filterOne(event, noAlert) { event && event.preventDefault(); let pageInfo = this.getPageInfo(); if (noAlert) { await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_FILTER$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); layer.closeAll(); this.answerCount = 1; } else utils.q(event, `是否屏蔽${pageInfo.carNum}?`, (async () => { await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_FILTER$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); }), (() => { this.answerCount = 1; })); } speedVideo() { if ($("#preview-video").is(":visible")) { const videoEl = document.getElementById("preview-video"); if (videoEl) { videoEl.muted = false; videoEl.controls = false; if (videoEl.currentTime + 5 < videoEl.duration) videoEl.currentTime += 5; else { show.info("预览视频结束, 已回到开头"); videoEl.currentTime = 1; } } return; } const iframe = $('iframe[id^="layui-layer-iframe"]'); if (iframe.length > 0) { iframe[0].contentWindow.postMessage("speedVideo", "*"); return; } let $videoPlayBtn = $(".preview-video-container"); if ($videoPlayBtn.length > 0) { $videoPlayBtn[0].click(); const videoEl = document.getElementById("preview-video"); if (videoEl) { videoEl.currentTime += 5; videoEl.muted = false; } } else $("#javTrailersBtn").click(); } hideVideoControls() { $(document).on("mouseenter", "#preview-video", (function () { $(this).prop("controls", true); })); } async bindHotkey() { const handlers = {}; this.filterHotKey && (handlers[this.filterHotKey] = () => { this.answerCount >= 2 ? this.filterOne(null, true) : this.filterOne(null); this.answerCount++; }); this.favoriteHotKey && (handlers[this.favoriteHotKey] = () => this.favoriteOne(null)); this.hasDownHotKey && (handlers[this.hasDownHotKey] = () => this.hasDownOne()); this.hasWatchHotKey && (handlers[this.hasWatchHotKey] = () => this.hasWatchOne()); this.speedVideoHotKey && (handlers[this.speedVideoHotKey] = () => this.speedVideo()); const registerHotkey = (key, handler) => { HotkeyManager.registerHotkey(key, ((event) => { const activeElement = document.activeElement; "INPUT" === activeElement.tagName || "TEXTAREA" === activeElement.tagName || activeElement.isContentEditable || (window.isDetailPage ? handler() : ((message) => { const childIframe = $(".layui-layer-content iframe"); if (0 === childIframe.length) return false; childIframe[0].contentWindow.postMessage(message, "*"); })(key)); })); }; window.isDetailPage && window.addEventListener("message", ((event) => { handlers[event.data] && handlers[event.data](); })); Object.entries(handlers).forEach((([key, handler]) => { registerHotkey(key, handler); })); } async previewSubtitle(url, name2) { if (!url) { console.error("未提供文件URL"); return; } const fileExt = url.split(".").pop().toLowerCase(); if ("ass" === fileExt || "srt" === fileExt) try { let resText = await gmHttp.get(url), title = "字幕预览"; "ass" === fileExt ? title = "ASS字幕预览 - " + name2 : "srt" === fileExt && (title = "SRT字幕预览 - " + name2); const lines = resText.split("\n"); let numberedContent = ""; const maxLineNumberLength = String(lines.length).length; lines.forEach(((line, index) => { const paddedLineNumber = String(index + 1).padStart(maxLineNumberLength, " "); numberedContent += `${paddedLineNumber}. ${line} `; })); const finalContent = numberedContent; layer.open({ type: 1, title, area: ["80%", "80%"], scrollbar: false, content: `
    ${finalContent}
    `, btn: ["下载", "关闭"], btn1: function (index, layero, that) { utils.download(resText, name2); return false; } }); } catch (error) { show.error(`预览失败: ${error.message}`); console.error("预览字幕文件出错:", error); } else show.error("仅支持预览ASS和SRT字幕文件"); } } class BlacklistPlugin extends BasePlugin { getName() { return "BlacklistPlugin"; } async addBlacklist(event) { let tempEvent = { clientX: event.clientX, clientY: event.clientY + 80 }; const hasAddBlacklist = $("#addBlacklistBtn span").text().includes("已加入"); let dataInfo, title; if (currentHref$1.includes("/tags")) { const urlObj = new URL(currentHref$1); urlObj.searchParams.delete("page"); const checkTag = $("#jhs-check-tag").text().trim(); dataInfo = { starId: "no-" + checkTag, name: "虚拟演员-" + checkTag, allName: ["虚拟演员"], role: "虚拟演员", movieType: "虚拟演员", blacklistUrl: urlObj.toString() }; title = `是否将分类 ${checkTag} 加入到黑名单中?`; hasAddBlacklist && (title = `分类 ${checkTag} 已在黑名单中, 是否从当前页开始追加屏蔽?`); } else { dataInfo = this.getActressPageInfo(); title = `是否将该演员 ${dataInfo.name} 加入到黑名单中?`; hasAddBlacklist && (title = `演员 ${dataInfo.name} 已在黑名单中, 是否从当前页开始追加屏蔽?`); } const {starId, name: name2, allName, role, movieType, blacklistUrl} = dataInfo; currentHref$1.includes("page") && !currentHref$1.includes("page=1") && (title += "
    注意: 当前页面非第一页, 屏蔽数据将从此页面开始"); if (isJavBus$1) { const afterStarParts = currentHref$1.split("/star/")[1].split("/"); if (afterStarParts.length > 1) { parseInt(afterStarParts[1]) > 1 && (title += "
    注意: 当前页面非第一页, 屏蔽数据将从此页面开始"); } } utils.q(tempEvent, title, (async () => { const taskPlugin = this.getBean("TaskPlugin"); navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (lock) { this.loadObj = loading(); try { await storageManager.addBlacklistItem({ starId, name: name2, allName, role, movieType, url: blacklistUrl }); await this.filterActorVideo(name2, starId); const toast = show.ok(`屏蔽结束,是否跳转到最后一页: ${this.lastPageLink || "无"}`, { duration: -1, close: true, onClick: () => { toast.closeShow(); this.lastPageLink && (window.location.href = this.lastPageLink); } }); } catch (e) { clog.error(e); const toast = show.error("发生错误, 是否填转到解析失败的那一页? (点击并跳转)", { duration: -1, close: true, onClick: () => { toast.closeShow(); window.location.href = this.nextPageLink; } }); } finally { this.loadObj.close(); } } else show.error("当前有定时任务在后台执行中, 无法发起此操作"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); } async resetBtnTip() { const taskPlugin = this.getBean("TaskPlugin"), lastCheckBlacklistTimeStr = localStorage.getItem(taskPlugin.lastCheckBlacklistTimeKey) || "无", checkBlacklist_intervalTime = await storageManager.getSetting("checkBlacklist_intervalTime", 12); this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime", 8760); $("#checkBlacklistBtn").attr("data-tip", `上次检测时间: ${lastCheckBlacklistTimeStr}; 检测间隔时间: ${checkBlacklist_intervalTime}小时`); } async openBlacklistDialog() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(); let html = `
    ${this.blacklistSvg}  手动检测黑名单 ${this.removeSvg}    清理数据 ${this.settingSvg}    配置
    重置
    `; layer.open({ type: 1, title: "演员黑名单", content: html, scrollbar: false, area: utils.getResponsiveArea(["80%", "90%"]), anim: -1, success: async (layero) => { await this.loadTableData(); $(".layui-layer-content").on("click", "#cleanQueryBtn", (async (event) => { $("#searchValue").val(""); $("#dataType").val(""); $("#statusType").val(""); await this.reloadTable(); })).on("focusout keydown", "#searchValue", (async (event) => { if ("focusout" === event.type || "Enter" === event.key) { "Enter" === event.key && event.preventDefault(); if ("keydown" === event.type && "Enter" !== event.key) return; $("#dataType").val(""); await this.reloadTable(); } })).on("change", "#dataType", (async () => { $("#searchValue").val(""); await this.reloadTable(); })).on("change", "#statusType", (async () => { await this.reloadTable(); })).on("change", "#urlType", (async () => { await this.reloadTable(); })).on("click", "#toSetting", (() => { this.getBean("SettingPlugin").openSettingDialog("task-panel", (() => { $("#setting-blacklist").css({ border: "1px solid #f40" }); })); })).on("click", "#clearKeywordBlacklist", (async () => { await this.clearKeywordBlacklist(); })).on("click", ".open-url", ((event) => { event.preventDefault(); const $el = $(event.currentTarget), url = $el.attr("data-url"), name2 = $el.attr("data-name"); utils.openPage(url, name2, true, event); })).on("click", "#checkBlacklistBtn", ((event) => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动检测黑名单?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (lock) { await taskPlugin.loadConfig(); await taskPlugin.checkBlacklist(true); } else show.error("当前有定时任务在后台执行中, 无法发起手动任务"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); }, end: () => { if (this.tableObj) { this.tableObj.destroy(); this.tableObj = null; } window.refresh(); } }); } async reloadTable() { if (!this.tableObj) return; const tableData = await this.getTableData(); this.tableObj.setData(tableData); } async getTableData() { const taskPlugin = this.getBean("TaskPlugin"), blacklist = await storageManager.getBlacklist(), blacklistCarList = await storageManager.getBlacklistCarList(), searchValue = $("#searchValue").val(), statusType = $("#statusType").val(), $dataType = $("#dataType"), dataType = $dataType.val(), urlType = $("#urlType").val(), blacklistCount = blacklist.length; let actorCount = 0, actressCount = 0, noStarCount = 0; const filteredBlacklist = blacklist.map(((item) => { "actor" === item.role ? actorCount++ : "actress" === item.role ? actressCount++ : "虚拟演员" === item.role && noStarCount++; let isUnCheck = false; item.lastPublishTime && (isUnCheck = !taskPlugin.isUnnecessaryCheck(item.lastPublishTime, this.checkBlacklist_ruleTime)); return { ...item, isUnCheck }; })).filter(((item) => !(searchValue && !item.name.includes(searchValue)) && (("normal" !== statusType || !item.isUnCheck) && (!("stop" === statusType && !item.isUnCheck) && (dataType ? item.role === dataType : !("hasT" === urlType && !item.url.includes("t=") && !item.url.includes("/tags")) && ("noT" !== urlType || !item.url.includes("t=") && !item.url.includes("/tags"))))))); $dataType.html(` `); $dataType.val(dataType); const carListMap = /* @__PURE__ */ new Map(); for (const car of blacklistCarList) { const starId = car.starId; carListMap.has(starId) || carListMap.set(starId, []); carListMap.get(starId).push(car); } const finalResult = filteredBlacklist.map(((item) => { const starId = item.starId, carList = carListMap.get(starId) || []; return { ...item, carList, count: carList.length }; })); this.currentCarCount = finalResult.reduce(((accumulator, currentObject) => accumulator + (currentObject.count || 0)), 0); return finalResult; } async loadTableData() { this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime") || 8760; const tableData = await this.getTableData(); this.tableObj = new Tabulator("#table-container", { layout: "fitColumns", placeholder: "暂无数据", virtualDom: true, data: tableData, pagination: true, paginationMode: "local", paginationSize: 20, paginationSizeSelector: [20, 50, 100, 1e3, 99999], paginationCounter: (pageSize, currentRow, currentPage, totalRows, totalPages) => `演员: ${totalRows}    番号总数: ${this.currentCarCount} `, responsiveLayout: "collapse", responsiveLayoutCollapse: true, columnDefaults: { headerHozAlign: "center", hozAlign: "center" }, index: "starId", columns: [{ title: "演员", field: "name", sorter: "string", minWidth: 100, responsive: 0, headerSort: false, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); return `${item.name}`; } }, { title: "性别角色", field: "role", sorter: "string", width: 120, responsive: 5, formatter: (cell, formatterParams, onRendered) => { const role = cell.getData().role; let content = role; "actor" === role ? content = "男演员" : "actress" === role && (content = "女演员"); return content; } }, { title: "影视类别", field: "movieType", sorter: "string", width: 120, responsive: 5, formatter: (cell, formatterParams, onRendered) => { const movieType = cell.getData().movieType; let content = movieType; "censored" === movieType ? content = "有码" : "uncensored" === movieType && (content = "无码"); return content; } }, { title: "屏蔽类型", field: "url", sorter: "string", minWidth: 120, responsive: 4, visible: isJavDb$1, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); if ("虚拟演员" === item.role) { const nameSplitList = item.name.split("-"); return `${nameSplitList.length > 0 ? nameSplitList[1] : item.name}`; } const tParam = utils.getUrlParam(item.url, "t"); if (!tParam) return "所有分类"; { const tParamList = tParam.toString().split(","); let selectTypeStr = ""; tParamList.forEach(((i) => { let value = categoryMap[i]; selectTypeStr += value ? value + " " : "未知类别:" + i + " "; })); selectTypeStr = selectTypeStr.trim(); if (selectTypeStr) return `${selectTypeStr}`; } } }, { title: "番号数量", field: "count", sorter: "number", width: 170, responsive: 1 }, { title: "创建时间", field: "createTime", sorter: "string", width: 170, responsive: 5 }, { title: "最后发行时间", field: "lastPublishTime", sorter: "string", width: 170, responsive: 1 }, { title: "状态", field: "isUnCheck", sorter: "string", width: 120, responsive: 1, formatter: (cell, formatterParams, onRendered) => { let dataTip = "", content = "正常检测"; if (cell.getData().isUnCheck) { dataTip = `停更${this.checkBlacklist_ruleTime / 24 / 365}年以上, 下轮任务不再进行检测`; content = "停止检测"; } return `${content}`; } }, { title: "操作", sorter: "string", cssClass: "action-cell-dropdown", minWidth: 150, responsive: 0, headerSort: false, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); onRendered((() => { var _a2, _b; null == (_a2 = cell.getElement().querySelector(".delete-btn")) || _a2.addEventListener("click", ((e) => { const name2 = item.name, starId = item.starId; name2 ? starId ? utils.q(e, `是否移除对 ${name2} 的屏蔽?`, (async () => { await storageManager.removeBlacklistCarList(starId); await storageManager.deleteBlacklistItem(starId); show.info("操作成功"); this.reloadTable().then(); })) : show.error("获取starId失败") : show.error("获取名称失败"); })); null == (_b = cell.getElement().querySelector(".keyword-btn")) || _b.addEventListener("click", ((e) => { const prefixMap = item.carList.reduce(((dataMap, carItem) => { const prefix = carItem.carNum.split("-")[0] + "-"; dataMap[prefix] = (dataMap[prefix] || 0) + 1; return dataMap; }), {}), sortedPrefixList = Object.entries(prefixMap).map((([prefix, count]) => ({ prefix, count }))).sort(((a, b) => b.count - a.count)); console.log(sortedPrefixList); })); })); return '\n \n ✂️ 删除 \n '; } }], initialSort: [{ column: "createTime", dir: "desc" }], locale: "zh-cn", langs: { "zh-cn": { pagination: { first: "首页", first_title: "首页", last: "尾页", last_title: "尾页", prev: "上一页", prev_title: "上一页", next: "下一页", next_title: "下一页", all: "所有", page_size: "每页行数" } } } }); } getCurrentStarUrl() { let urlWithoutSortType = window.location.href.replace(/([&?])sort_type=[^&]+(&|$)/, "$1"); urlWithoutSortType = urlWithoutSortType.replace(/[&?]$/, ""); urlWithoutSortType = urlWithoutSortType.replace(/\?&/, "?"); let urlWithoutPageNumber = urlWithoutSortType; urlWithoutPageNumber = urlWithoutPageNumber.replace(/([&?])page=\d+(&|$)/, "$1"); urlWithoutPageNumber = urlWithoutPageNumber.replace(/[&?]$/, ""); urlWithoutPageNumber = urlWithoutPageNumber.replace(/\?&/, "?"); urlWithoutPageNumber = urlWithoutPageNumber.replace(/\/(\d+)(?:\/(\d+))?(\?|$)/, ((match, id, page, suffix) => void 0 !== page ? `/${id}${suffix}` : match)); return urlWithoutPageNumber; } parseUrlId(url) { if (!url) throw new Error("url未传入"); return new URL(url).pathname.split("/").filter(((segment) => "" !== segment.trim())).pop(); } async filterAllVideo(actorName, $dom) { let movieList, nextPageLink; if ($dom) { isJavBus$1 && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); movieList = $dom.find(this.getSelector().requestDomItemSelector); nextPageLink = $dom.find(this.getSelector().nextPageSelector).attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(this.getSelector().nextPageSelector).attr("href"); } if (nextPageLink && 0 === movieList.length) { show.error("解析列表失败"); throw new Error("解析列表失败"); } for (const element of movieList) { const $item = $(element), {carNum: carNum2, url, publishTime} = this.getBoxCarInfo($item); if (url && carNum2) try { if (await storageManager.getCar(carNum2)) continue; await storageManager.saveCar({ carNum: carNum2, url, names: actorName, actionType: Status_FILTER$1, publishTime }); clog.log("屏蔽演员番号", actorName, carNum2); } catch (error) { console.error(`保存失败 [${carNum2}]:`, error); } } if (nextPageLink) { show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink); await new Promise(((resolve) => setTimeout(resolve, 500))); const html = await gmHttp.get(nextPageLink), parser = new DOMParser(), next$dom = $(parser.parseFromString(html, "text/html")); await this.filterAllVideo(actorName, next$dom); } else { show.ok("执行结束!"); window.refresh(); } } async filterActorVideo(actorName, starId, $dom) { let {nextPageLink} = await this.parseAndSaveFilterInfo($dom, actorName, starId); this.nextPageLink = nextPageLink; if (nextPageLink) { this.lastPageLink = nextPageLink; show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink); let next$dom; const pageNum = utils.getUrlParam(nextPageLink, "page") || 0, beyond60Plugin = this.getBean("Beyond60Plugin"); if (isJavDb$1 && beyond60Plugin && pageNum > 60) { let {html, nextUrl, hasMore} = await beyond60Plugin.handleBeyond60(nextPageLink), mergeHtml = `
    ${html}
    ${nextUrl ? `` : ""} `; next$dom = utils.htmlTo$dom(mergeHtml); } else { clog.log("正在请求下一页内容:", nextPageLink); const html = await gmHttp.get(nextPageLink); next$dom = utils.htmlTo$dom(html); } await this.filterActorVideo(actorName, starId, next$dom); } else { show.ok("执行结束!"); window.refresh(); } } async parseAndSaveFilterInfo($dom, actorName, starId) { let movieList, nextPageLink; if ($dom) { let tempIsJavBus = false, selectorType = "javdb"; if ($dom.text().includes("javbus")) { tempIsJavBus = true; selectorType = "javbus"; } tempIsJavBus && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); movieList = $dom.find(this.getSelector(selectorType).requestDomItemSelector); nextPageLink = $dom.find(this.getSelector(selectorType).nextPageSelector).attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(this.getSelector().nextPageSelector).attr("href"); } if (nextPageLink && 0 === movieList.length) return { nextPageLink: null, lastPublishTime: null }; const filterKeywordList = await storageManager.getTitleFilterKeyword(); let carDataList = [], lastPublishTime = null; for (const element of movieList) { const $item = $(element), {carNum: carNum2, url, publishTime, title} = this.getBoxCarInfo($item); lastPublishTime || (lastPublishTime = publishTime); filterKeywordList.find(((kw) => title.includes(kw) || carNum2.startsWith(kw))) || url && carNum2 && carDataList.push({ carNum: carNum2, url, names: actorName, actionType: Status_FILTER$1, starId, publishTime }); } await storageManager.batchSaveBlacklistCarList(carDataList); return { nextPageLink, lastPublishTime }; } async clearKeywordBlacklist() { let filterKeywordList = await storageManager.getTitleFilterKeyword(); const processedKeywordsSet = /* @__PURE__ */ new Set(); filterKeywordList.forEach(((item) => { /^[a-z]{2,}-/i.test(item) && processedKeywordsSet.add(item); })); if (0 === processedKeywordsSet.size) { show.info("没有需要清理的关键词数据"); return; } const carList = await storageManager.getCarList(), blacklistCarList = await storageManager.getBlacklistCarList(), carNumSet = new Set(carList.map(((car) => car.carNum))), keywords = Array.from(processedKeywordsSet), waitRemoveCarNumList = [], removableCarData = []; for (const car of blacklistCarList) { const carNum2 = car.carNum; let matched = false; for (const keyword of keywords) if (carNum2.startsWith(keyword)) { removableCarData.push({ carNum: carNum2, names: car.names, matchedKeyword: keyword }); waitRemoveCarNumList.push(carNum2); matched = true; break; } if (!matched && carNumSet.has(carNum2)) { removableCarData.push({ carNum: carNum2, names: car.names, matchedKeyword: "已在鉴定记录中" }); waitRemoveCarNumList.push(carNum2); } } const carNumCount = waitRemoveCarNumList.length; 0 !== carNumCount ? layer.open({ type: 1, title: `确认清理黑名单数据 (共 ${carNumCount} 个番号)`, area: ["80%", "70%"], anim: -1, content: `

    以下匹配到 ${carNumCount} 个黑名单番号存在于屏蔽关键词中, 是否移除?

    `, btn: ["确定清理", "取消"], success: function (layero, index) { new Tabulator("#wait-remove-table", { data: removableCarData, virtualDom: true, layout: "fitColumns", pagination: true, paginationMode: "local", paginationSize: 50, paginationSizeSelector: [50, 100, 1e3, 99999], columns: [{ title: "演员", field: "names" }, { title: "番号", field: "carNum" }, { title: "匹配关键词", field: "matchedKeyword" }], locale: "zh-cn", initialSort: [{ column: "names", dir: "asc" }], langs: { "zh-cn": { pagination: { first: "首页", first_title: "首页", last: "尾页", last_title: "尾页", prev: "上一页", prev_title: "上一页", next: "下一页", next_title: "下一页", all: "所有", page_size: "每页行数" } } } }); }, yes: async (index) => { const removedCount = await storageManager.batchRemoveBlacklistCars(waitRemoveCarNumList); if (0 === removedCount) show.error("移除失败"); else { show.ok(`🎉 清理完成!已移除${removedCount}个相关黑名单番号数据`); await this.reloadTable(); layer.close(index); } }, btn2: function (index) { layer.close(index); } }) : show.info("没有需要清理的黑名单番号数据"); } } class ListPagePlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "currentPageFilterCount", 0); __publicField2(this, "currentPageFavoriteCount", 0); __publicField2(this, "currentPageHasDownCount", 0); __publicField2(this, "currentPageHasWatchCount", 0); __publicField2(this, "currentPageKeywordFilterCount", 0); __publicField2(this, "currentPageActorFilterCount", 0); __publicField2(this, "currentPageWaitCheckCount", 0); __publicField2(this, "currentPageTotalCount", 0); __publicField2(this, "cache", localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {}); __publicField2(this, "writeQueue", Promise.resolve()); } getName() { return "ListPagePlugin"; } async handle() { this.cleanRepeatId(); this.replaceHdImg(); this.addJumpPageControl(); this.fixBusTitleBox(); await this.doFilter(); this.bindClick().then(); this.bindListPageHotKey().then(); this.rememberTagExpand(); $(this.getSelector().itemSelector + " a").attr("target", "_blank"); this.checkDom(); } rememberTagExpand() { if (!window.location.href.includes("actors")) return; const $expandButton = $(".tag-expand"); if (0 === $expandButton.length) return; const isExpanded = "true" === localStorage.getItem("jhs_tag_expand"), $actorTagsContent = $(".actor-tags .content"); isExpanded && $actorTagsContent.hasClass("collapse") && $expandButton[0].click(); $expandButton.on("click", (function () { const newState = !$actorTagsContent.hasClass("collapse"); localStorage.setItem("jhs_tag_expand", newState.toString()); })); } checkDom() { if (!window.isListPage) return; const selector = this.getSelector(), targetNode = document.querySelector(selector.boxSelector); if (!targetNode) { console.error("没有找到容器节点!"); return; } const observer = new MutationObserver((async (mutations) => { observer.disconnect(); try { this.replaceHdImg(); this.addJumpPageControl(); this.fixBusTitleBox(); this.getBean("CoverButtonPlugin").addSvgBtn().then(); await this.doFilter(); await this.getBean("ListPageButtonPlugin").sortItems(); $(this.getSelector().itemSelector + " a").attr("target", "_blank"); this.getBean("AutoPagePlugin").checkLoad(); } finally { observer.observe(targetNode, config); } })), config = { childList: true, subtree: false }; observer.observe(targetNode, config); } fixBusTitleBox() { if (!isJavBus$1) return; $(this.getSelector().itemSelector).toArray().forEach(((ele) => { var _a2; let $box2 = $(ele); if ($box2.find(".avatar-box").length > 0) return; const title = (null == (_a2 = $box2.find("img").attr("title")) ? void 0 : _a2.trim()) || ""; $box2.find(".photo-info span:first").contents().first().wrap(`${title}`); $box2.find("br").remove(); })); } cleanRepeatId() { if (!isJavBus$1) return; $("#waterfall_h").removeAttr("id").attr("id", "no-page"); const $waterfalls = $('[id="waterfall"]'); 0 !== $waterfalls.length && $waterfalls.each((function () { const $current = $(this); if (!$current.hasClass("masonry")) { $current.children().insertAfter($current); $current.remove(); } })); } async doFilter() { if (!window.isListPage) return; let movieList = $(this.getSelector().itemSelector).toArray(); if (movieList.length) { await this.filterMovieList(movieList); await this.getBean("WangPan115MatchPlugin").matchMovieList(movieList); isJavBus$1 && await this.getBean("BusImgPlugin").logImageHeightsByRow(); } } async filterMovieList(movieList) { utils.time("累计耗费时间"); utils.time("读取数据耗时"); const [carList, filterKeywordList, blacklist, blacklistCarList, settingObj] = await Promise.all([storageManager.getCarList(), storageManager.getTitleFilterKeyword(), storageManager.getBlacklist(), storageManager.getBlacklistCarList(), storageManager.getSetting()]), t1 = utils.time("读取数据耗时"), carNumSets = carList.reduce(((dataMap, item) => { const statusKey = item.status; dataMap.hasOwnProperty(statusKey) && dataMap[statusKey].add(item.carNum); return dataMap; }), { [Status_FILTER$1]: /* @__PURE__ */ new Set(), [Status_FAVORITE$1]: /* @__PURE__ */ new Set(), [Status_HAS_DOWN$1]: /* @__PURE__ */ new Set(), [Status_HAS_WATCH$1]: /* @__PURE__ */ new Set() }); utils.time("组装数据耗时"); const starIdToRoleMap = new Map(blacklist.map(((item) => [item.starId, item.role]))), { actorCarNumToNameMap, actressCarNumToNameMap } = blacklistCarList.reduce(((dataMap, carItem) => { const role = starIdToRoleMap.get(carItem.starId); if (!role) { clog.error("黑名单数据源丢失演员信息", carItem); return dataMap; } const targetMap = "actor" === role ? dataMap.actorCarNumToNameMap : dataMap.actressCarNumToNameMap; targetMap.has(carItem.carNum) || targetMap.set(carItem.carNum, carItem.names); return dataMap; }), { actorCarNumToNameMap: /* @__PURE__ */ new Map(), actressCarNumToNameMap: /* @__PURE__ */ new Map() }), t2 = utils.time("组装数据耗时"), settings = { showFilterItem: (null == settingObj ? void 0 : settingObj.showFilterItem) ?? NO, showFilterActorItem: (null == settingObj ? void 0 : settingObj.showFilterActorItem) ?? NO, showFilterKeywordItem: (null == settingObj ? void 0 : settingObj.showFilterKeywordItem) ?? NO, showFavoriteItem: (null == settingObj ? void 0 : settingObj.showFavoriteItem) ?? YES$1, showHasDownItem: (null == settingObj ? void 0 : settingObj.showHasDownItem) ?? YES$1, showHasWatchItem: (null == settingObj ? void 0 : settingObj.showHasWatchItem) ?? YES$1, showAllItem: (null == settingObj ? void 0 : settingObj.showAllItem) ?? NO, tagPosition: (null == settingObj ? void 0 : settingObj.tagPosition) || "rightTop", movieShowType: (null == settingObj ? void 0 : settingObj.movieShowType) || "hide" }; this.currentPageFilterCount = 0; this.currentPageFavoriteCount = 0; this.currentPageHasDownCount = 0; this.currentPageHasWatchCount = 0; this.currentPageKeywordFilterCount = 0; this.currentPageActorFilterCount = 0; this.currentPageWaitCheckCount = 0; this.currentPageTotalCount = 0; utils.time("处理页面耗时"); await Promise.all(movieList.map((async (ele) => { let $box2 = $(ele); if (isJavBus$1 && $box2.find(".avatar-box").length > 0) return; const {carNum: carNum2, title} = this.getBoxCarInfo($box2), { filter, favorite, hasDown, hasWatch } = carNumSets, isFavorite = favorite.has(carNum2), isHasDown = hasDown.has(carNum2), isHasWatch = hasWatch.has(carNum2), isFiltered = filter.has(carNum2), isFilterActorMale = actorCarNumToNameMap.has(carNum2), isFilterActorFemale = actressCarNumToNameMap.has(carNum2), isFilterActor = isFilterActorMale || isFilterActorFemale, foundKeyword = filterKeywordList.find(((kw) => title.includes(kw) || carNum2.startsWith(kw))), isFilterKeyword = !!foundKeyword; if (!isSearchPage) { let shouldHide = settings.showFavoriteItem === NO && isFavorite || settings.showHasDownItem === NO && isHasDown || settings.showHasWatchItem === NO && isHasWatch || settings.showFilterItem === NO && isFiltered && !(isFavorite || isHasDown || isHasWatch) || settings.showFilterActorItem === NO && isFilterActor || settings.showFilterKeywordItem === NO && isFilterKeyword; if ($box2.attr("data-movieShowType") !== settings.movieShowType) { $box2.css("border", ""); $box2.children().css("visibility", ""); $box2.removeAttr("data-hide"); $box2.show(); } const isCurrentlyHidden = $box2.attr("data-hide") === YES$1; settings.showAllItem === YES$1 && (shouldHide = false); if (shouldHide !== isCurrentlyHidden) { shouldHide ? $box2.attr("data-hide", YES$1) : $box2.removeAttr("data-hide"); if ("hide" === settings.movieShowType) shouldHide ? $box2.hide() : $box2.show(); else { if ("visibility" !== settings.movieShowType) throw new Error("movieShowType值有误:" + settings.movieShowType); { const $content = $box2.children(), borderStyle = shouldHide ? "1px solid rgb(192 176 176)" : "none", visibilityValue = shouldHide ? "hidden" : "visible"; $box2.css("border", borderStyle); $content.css("visibility", visibilityValue); } } $box2.attr("data-movieShowType") !== settings.movieShowType && $box2.attr("data-movieShowType", settings.movieShowType); } } let tag = TAG_STYLES.IS_WAIT_CHECK, filterReason = null; if (isFiltered) tag = TAG_STYLES.IS_FILTERED; else if (isFavorite) tag = TAG_STYLES.IS_FAVORITE; else if (isHasDown) tag = TAG_STYLES.IS_HAS_DOWN; else if (isHasWatch) tag = TAG_STYLES.IS_HAS_WATCH; else if (isFilterKeyword) { tag = TAG_STYLES.IS_KEYWORD_FILTER; filterReason = foundKeyword || "未知"; } else if (isFilterActorMale) { tag = TAG_STYLES.IS_ACTOR_FILTER; filterReason = actorCarNumToNameMap.get(carNum2) || ""; } else if (isFilterActorFemale) { tag = TAG_STYLES.IS_ACTRESS_FILTER; filterReason = actressCarNumToNameMap.get(carNum2) || ""; } filterReason || (filterReason = tag.reasonType); tag.isCounted && this[tag.countKey]++; this.currentPageTotalCount++; $box2.find(".status-tag").remove(); const tagPositionCss = "rightTop" === settings.tagPosition ? "right: 0; top:5px;" : "left: 0; top:5px;"; if (tag.text) { const tagHtml = isJavDb$1 ? ` ${tag.text} ` : ` ${tag.text} `; isJavDb$1 && $box2.find(".tags").append(tagHtml); if (isJavBus$1) { const $itemTag = $box2.find(".item-tag"); $itemTag.length ? $itemTag.append(tagHtml) : $box2.find(".photo-info > span > div").append(tagHtml); } } await this.translate($box2); }))); const t3 = utils.time("处理页面耗时"), t4 = utils.time("累计耗费时间"); $("#waitDownBtn span").text(`打开已收藏 (${carNumSets.favorite.size})`); clog.log(`
    ${t1} ${t2}
    ${t3} ${t4}
    项目 数量 项目 数量
    屏蔽单番号 ${this.currentPageFilterCount} 收藏 ${this.currentPageFavoriteCount}
    屏蔽演员 ${this.currentPageActorFilterCount} 已下载 ${this.currentPageHasDownCount}
    屏蔽关键词 ${this.currentPageKeywordFilterCount} 已观看 ${this.currentPageHasWatchCount}
    待鉴定 ${this.currentPageWaitCheckCount}
    总数 ${this.currentPageTotalCount}
    `); } async bindClick() { let selector = this.getSelector(); $(selector.boxSelector).on("click", ".item img", (async (event) => { event.preventDefault(); event.stopPropagation(); if ($(event.target).closest("div.meta-buttons").length) return; const $box2 = $(event.target).closest(".item"), {carNum: carNum2, aHref} = this.getBoxCarInfo($box2); let dialogOpenDetail = await storageManager.getSetting("dialogOpenDetail", YES$1); if (carNum2.includes("FC2-")) { let movieId = this.parseMovieId(aHref); this.getBean("Fc2Plugin").openFc2Dialog(movieId, carNum2, aHref); } else if (dialogOpenDetail === YES$1) { utils.openPage(aHref, carNum2, true, event); this.$currentImage = null; } else window.open(aHref); })); $(selector.boxSelector).on("click", ".item video", (async (event) => { const video = event.currentTarget; video.paused ? video.play().catch(((e) => console.error("播放失败:", e))) : video.pause(); event.preventDefault(); event.stopPropagation(); })); $(selector.boxSelector).on("click", ".item .video-title", (async (event) => { if ($(event.target).closest('[class^="jhs-match-"]').length) return; const $box2 = $(event.currentTarget).closest(".item"), { carNum: carNum2, aHref } = this.getBoxCarInfo($box2); if (carNum2.includes("FC2-")) { event.preventDefault(); let movieId = this.parseMovieId(aHref); this.getBean("Fc2Plugin").openFc2Dialog(movieId, carNum2, aHref); } })); $(selector.boxSelector).on("contextmenu", ".item img, .item video", (async (event) => { event.preventDefault(); const $box2 = $(event.target).closest(".item"), { carNum: carNum2, url, publishTime } = this.getBoxCarInfo($box2); let $actor = isJavDb$1 ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10"), actorName = ""; $actor.length && (actorName = $actor.text().trim().split(",")[0].replace("(無碼)", "")); utils.q(event, `是否屏蔽番号 ${carNum2}?`, (async () => { setTimeout((async () => { actorName || (actorName = await this.parseActressName(url)); await storageManager.saveCar({ carNum: carNum2, url, names: actorName, actionType: Status_FILTER$1, publishTime }); window.refresh(); show.ok("操作成功"); })); })); })); } async parseActressName(url) { let actorName = null; if (await storageManager.getSetting("enableSaveActressCarInfo", NO) === YES$1) { clog.debug("鉴定补录演员信息-已启用, 开始解析详情页"); clog.debug("开始解析演员详情页", url); const html = await gmHttp.get(url), $dom = utils.htmlTo$dom(html); isJavDb$1 ? actorName = $dom.find(".female").prev().map(((i, el) => $(el).text())).get().join(" ") : isJavBus$1 && (actorName = $dom.find('span[onmouseover*="star_"] a').map(((i, el) => $(el).text())).get().join(" ")); clog.debug("解析到名称:", actorName); } return actorName; } async bindListPageHotKey() { this.$currentImage = null; $(document).on("mouseenter", this.getSelector().coverImgSelector, ((e) => { this.$currentImage = $(e.currentTarget); })).on("mouseleave", this.getSelector().coverImgSelector, (() => { this.$currentImage = null; })); let settingObj = await storageManager.getSetting(); this.filterHotKey = settingObj.filterHotKey; this.favoriteHotKey = settingObj.favoriteHotKey; this.hasDownHotKey = settingObj.hasDownHotKey; this.hasWatchHotKey = settingObj.hasWatchHotKey; this.enableImageHotKey = settingObj.enableImageHotKey || NO; this.clogHotKey = settingObj.clogHotKey; this.foldCategoryHotKey = settingObj.foldCategoryHotKey; this.showFilterItemHotKey = settingObj.showFilterItemHotKey; this.showFilterActorItemHotKey = settingObj.showFilterActorItemHotKey; this.showFilterKeywordItemHotKey = settingObj.showFilterKeywordItemHotKey; this.showFavoriteItemHotKey = settingObj.showFavoriteItemHotKey; this.showHasDownItemHotKey = settingObj.showHasDownItemHotKey; this.showHasWatchItemHotKey = settingObj.showHasWatchItemHotKey; this.showAllItemHotKey = settingObj.showAllItemHotKey; const HOTKEY_REGISTRY = { showFilterItemHotKey: "showFilterItem", showFilterActorItemHotKey: "showFilterActorItem", showFilterKeywordItemHotKey: "showFilterKeywordItem", showFavoriteItemHotKey: "showFavoriteItem", showHasDownItemHotKey: "showHasDownItem", showHasWatchItemHotKey: "showHasWatchItem", showAllItemHotKey: "showAllItem" }, registerToggleHotkey = (hotkeyName, settingKey) => { const hotkeyString = this[hotkeyName]; hotkeyString && HotkeyManager.registerHotkey(hotkeyString, (async (event) => { const newVal = await storageManager.getSetting(settingKey) === YES$1 ? NO : YES$1; await storageManager.saveSettingItem(settingKey, newVal); window.refresh(); })); }; for (const hotkeyName in HOTKEY_REGISTRY) if (HOTKEY_REGISTRY.hasOwnProperty(hotkeyName)) { registerToggleHotkey(hotkeyName, HOTKEY_REGISTRY[hotkeyName]); } if (this.enableImageHotKey === NO) return; const handleAction = async (boxInfo, status) => { setTimeout((async () => { let actorName = await this.parseActressName(boxInfo.url); await storageManager.saveCar({ carNum: boxInfo.carNum, url: boxInfo.url, names: actorName, actionType: status, publishTime: boxInfo.publishTime }); window.refresh(); show.ok("操作成功"); })); }, handlers = {}; this.filterHotKey && (handlers[this.filterHotKey] = (boxInfo) => { handleAction(boxInfo, Status_FILTER$1); }); this.favoriteHotKey && (handlers[this.favoriteHotKey] = (boxInfo) => { handleAction(boxInfo, Status_FAVORITE$1); }); this.hasDownHotKey && (handlers[this.hasDownHotKey] = (boxInfo) => { handleAction(boxInfo, Status_HAS_DOWN$1); }); this.hasWatchHotKey && (handlers[this.hasWatchHotKey] = (boxInfo) => { handleAction(boxInfo, Status_HAS_WATCH$1); }); this.clogHotKey && HotkeyManager.registerHotkey(this.clogHotKey, ((event) => { clog.toggleExpandCollapsed(); })); this.foldCategoryHotKey && HotkeyManager.registerHotkey(this.foldCategoryHotKey, ((event) => { const $btn = $("#foldCategoryBtn"); $btn.length && $btn[0].click(); })); const registerImageHotkey = (key, handler) => { HotkeyManager.registerHotkey(key, ((event) => { const activeElement = document.activeElement; if (!("INPUT" === activeElement.tagName || "TEXTAREA" === activeElement.tagName || activeElement.isContentEditable) && this.$currentImage) { const $box2 = this.$currentImage.closest(".item"), boxInfo = this.getBoxCarInfo($box2); handler(boxInfo); } })); }; Object.entries(handlers).forEach((([key, handler]) => { registerImageHotkey(key, handler); })); } replaceHdImg(coverImgNodeList) { coverImgNodeList && "string" == typeof coverImgNodeList.jquery && (coverImgNodeList = coverImgNodeList.toArray()); coverImgNodeList || (coverImgNodeList = document.querySelectorAll(this.getSelector().coverImgSelector)); isJavDb$1 && coverImgNodeList.forEach(((img) => { img.src = img.src.replace("thumbs", "covers"); img.title = ""; })); if (isJavBus$1) { const THUMB_PATH_REGEX = /\/(imgs|pics)\/(thumb|thumbs)\//, IMG_EXT_REGEX = /(\.jpg|\.jpeg|\.png)$/i, replaceWithHd = (img) => { if (img.src && THUMB_PATH_REGEX.test(img.src) && "true" !== img.dataset.hdReplaced) { img.src = img.src.replace(THUMB_PATH_REGEX, "/$1/cover/").replace(IMG_EXT_REGEX, "_b$1"); img.dataset.hdReplaced = "true"; img.dataset.title = img.title; img.title = ""; } }, DMM_THUMB_REGEX = /ps(\.jpg|\.jpeg|\.png)$/i, replaceWithHdForDMM = (img) => { if (img.src && DMM_THUMB_REGEX.test(img.src) && "true" !== img.dataset.hdReplaced) { img.src = img.src.replace(DMM_THUMB_REGEX, "pl$1"); img.dataset.hdReplaced = "true"; img.dataset.title = img.title; img.title = ""; } }; coverImgNodeList.forEach(((img) => { replaceWithHd(img); replaceWithHdForDMM(img); })); } storageManager.getSetting("hoverBigImg", NO).then(((hoverBigImg) => { hoverBigImg === YES$1 && (window.imageHoverPreviewObj ? window.imageHoverPreviewObj.bindEvents() : window.imageHoverPreviewObj = new ImageHoverPreview({ selector: this.getSelector().coverImgSelector })); })); } async translate($box2) { if (await storageManager.getSetting("translateTitle", YES$1) !== YES$1) return; let content, carNum2, $title = $box2.find(".video-title"); if (isJavDb$1) { content = $title.contents().filter(((i, node) => 3 === node.nodeType && "" !== node.textContent.trim())).text().trim(); carNum2 = $box2.find(".video-title strong").text().trim(); } else { content = $box2.find("img").attr("data-title").trim(); carNum2 = $box2.find("a").attr("href").split("/").filter(Boolean).pop().trim(); } if (this.cache[carNum2]) { let _this = this; $title.attr("data-original-title", content); $title.contents().each((function () { 3 === this.nodeType && "" !== this.textContent.trim() && (this.textContent = " " + _this.cache[carNum2] + " "); })); $title.attr("title", _this.cache[carNum2]); } else translateText(content).then(((result) => { $title.attr("data-original-title", content); if (isJavDb$1) { $title.contents().each((function () { 3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(carNum2) || (this.textContent = " " + result + " "); })); $title.attr("title", result); } else $title.text(result); this.writeQueue = this.writeQueue.then((() => { this.cache[carNum2] = result; localStorage.setItem("jhs_translate", JSON.stringify(this.cache)); })); })).catch(((error) => { console.error("翻译失败:", error); })); } async revertTranslation() { $(this.getSelector().itemSelector).toArray().forEach(((ele) => { let $box2 = $(ele); const originalContent = $box2.find(".video-title").attr("data-original-title") || $box2.find(".box").attr("title") || $box2.find(".video-title").attr("title") || $box2.find("img").attr("data-title"); let carNum2; isJavDb$1 && (carNum2 = $box2.find(".video-title strong").text().trim()); const $title = $box2.find(".video-title"); $title.contents().each((function () { 3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(carNum2) || (this.textContent = " " + originalContent + " "); })); $title.removeAttr("title"); })); } addJumpPageControl() { if ($("#gemini-jump-page-control").length > 0) return; if (0 === $(".pagination-link.is-current").length) return; const currentPageNum = utils.getUrlParam(currentHref$1, "page") || 1, $input = $("", { type: "number", id: "jumpPageInput", placeholder: "页码", min: "1", style: "width: 60px; margin-left: 10px; padding: 10px; border: 1px solid #ccc; font-size: 14px;", value: currentPageNum + 1 }), $button = $("