☰

javlibrary enhancement

javlibrary网站 增强内容包括:预览视频、在线播放、下载链接、预览图补全 etc.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴、Greasemonkey 油猴子 或 Violentmonkey 暴力猴,才能安装此脚本。

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴 或 Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴 或 Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         javlibrary enhancement
// @name:zh      javlibrary 图书馆网站增强
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  javlibrary网站 增强内容包括:预览视频、在线播放、下载链接、预览图补全 etc.
// @author       https://greasyfork.org/zh-CN/users/25283
// @match        https://*.javlibrary.com/*
// @icon         https://www.javlibrary.com/favicon.ico
// @grant        GM_xmlhttpRequest
// @connect      cc3001.dmm.co.jp
// @connect      pics.dmm.co.jp
// @connect      pics.dmm.com
// @connect      www.dmm.com
// @connect      www.dmm.co.jp
// @connect      sukebei.nyaa.si
// @connect      missav.ws
// @connect      av-wiki.net
// @connect      supjav.com
// @license      MIT
// ==/UserScript==
//在前人的基础上修改,增强内容包括:
//  1、首页热门影片预览图高度固定,在高分显示器上预览图太小了,放大画面的话就会显示不全,修改热门影片列表高度为自动。(建议在4K显示器放大画面设为175%或200%)
//  2、由于DMM反盗链,以前的视频地址失效了,更新了影片预览视频的方式,直接嵌入DMM的预览页面。需要注意,DMM的视频只允许日本IP预览,日本以外的IP会被拒绝。
//  3、添加VR影片的预览视频,无须VR眼镜也能观看,由于DMM的VR视频页禁止嵌入,只能做成链接形式,但好处是可以全屏放大,方法是按F11键,并且VR影片可以用鼠标拖动调整视角,提高观影体验。
//  4、如果图书馆的预览截图缺失,尝试重新补足 示例:REBD-669 和 ABF-226,切换插件开关可以看到效果。
//  5、如果你有下片时收藏封面图和预览图和预览视频的习惯,可以在浏览器扩展商店安装Chrono下载器插件,然后在预览图下方链接,右键可以批量保存预览图,省却你一张一张点的麻烦。
//  6、直接显示Sukebei.nyaa.si的搜索结果,在页内就可以直接下载影片。
//  7、将左侧导航栏和顶部Logo隐藏,并增加5项分类标签,方便直达特殊分类。
//本插件专注四项核心需求:预览视频、在线播放、下载链接、预览图补全,页面干净清爽,秒杀其他一堆乱七八糟功能搞得页面混乱不堪的脚本,请试着关闭其他脚本感受一下。
//尽量少修改页面元素,可以最大限度与其他脚本兼容。有疑问请站内私讯 Warma

(function () {
    'use strict';
const gmFetch = (url, { method, headers, anonymous } = {}) => new Promise((onload, onerror) => {GM_xmlhttpRequest({ url, method, headers, anonymous, onload, onerror })})
const parseHTML = (str) => {const tmp = document.implementation.createHTMLDocument();tmp.body.innerHTML = str; return tmp;}
//const check = async (src) => (await gmFetch(src, { method: 'HEAD' })).status === 200

function normalizeString(str) {
  if (typeof str !== 'string') return str;
  str.replace("55t38","55t3800") //T38-***系列的需要特殊处理
  // 惰性前缀 + 贪婪末尾数字(至少 3 位),一次性完成拆分
  const m = /^(.*?)(\d{3,})$/.exec(str);
  if (!m) return str; // 无法拆分,原样返回

  const prefix = m[1];
  const digits = m[2];
  const arr = [
    ['41hodv', '5642hodv'],
    ['9oae', 'oae'],
  ];

  const map = new Map(arr);

  const replacedFront = map.has(prefix) ? map.get(prefix) : prefix;

  // 只有恰好 3 位时才补两个 0
  let newDigits = digits.length === 3 ? '00' + digits : digits;

  // 总长度不足 5 位 → 前面补 0 至 5 位
  if (newDigits.length < 5) {
    newDigits = newDigits.padStart(5, '0');
  }
  return replacedFront + newDigits;
}

function mainpage(){ // 优化首页显示效果
console.log("首页");
try{
    var leftmenu = document.querySelector("#leftmenu");
    var rightcolumn = document.querySelector("#rightcolumn");
    // 左边导航菜单增加几项分类
    const node = document.querySelector("#leftmenu > div > ul > li:nth-child(3)");
    node.insertAdjacentHTML('afterend', '<li><a href="vl_genre.php?g=aazq" style="color:red">漫改作品</a></li>')
    node.insertAdjacentHTML('afterend', '<li><a href="vl_genre.php?g=anbq" style="color:red">高品质VR</a></li>')
    node.insertAdjacentHTML('afterend', '<li><a href="vl_genre.php?g=aaua" style="color:red">新发行VR</a></li>')
    node.insertAdjacentHTML('afterend', '<li><a href="vl_genre.php?g=a5gq" style="color:red">介绍影片</a></li>')
    node.insertAdjacentHTML('afterend', '<li><a href="vl_genre.php?g=my" style = "color:red">新人亮相</a></li>')

    leftmenu.style.display='none'; // 隐藏左侧导航栏
    document.querySelector("#toplogo").style.display='none'; // 隐藏LOGO
    rightcolumn.style.margin='0px 10px 10px 0px';
    var boxtitle = document.querySelector("#rightcolumn > div.boxtitle");
    if (!boxtitle) {const $position = document.querySelector("#video_title");
        const html = `<div class="boxtitle"><a onclick="leftmenu.style.display='inline-block';rightcolumn.style.margin='0px 10px 10px 180px';return false;"> &gt;&gt;&gt;<b style="color:red">显示</b>&lt;&lt;&lt; </a>左边导航菜单</div>`
        $position.insertAdjacentHTML('beforebegin', html);}
    else{boxtitle.innerHTML += '<a onclick="leftmenu.style.display=\'inline-block\';rightcolumn.style.margin=\'0px 10px 10px 180px\';return false;"> &gt;&gt;&gt;<b style="color:red">显示</b>&lt;&lt;&lt; </a>左边导航菜单';}
    var category = document.querySelector("#leftmenu > div > div.category");
    category.innerHTML += '<a onclick="leftmenu.style.display=\'none\';rightcolumn.style.margin=\'0px 10px 10px 0px\';return false;">   &gt;&gt;&gt;<b style="color:red">隐藏</b>&lt;&lt;&lt; </a>';

    document.querySelector("#rightcolumn > div.videothumblist").style.height = '100%'; // 首页热门影片高度自动调整
    document.querySelector("#leftmenu > div > ul:nth-child(2) > li:nth-child(2)").remove(); //18禁游戏
    document.body.insertAdjacentHTML('afterend', '<style>.videothumblist .videos .video{display:inline-flex !important;height:auto !important} .videothumblist .videos .video .title{height:auto !important} </style>')

    // 删除首页底部的一长串介绍文字
    var elements = document.getElementsByClassName("about");
    var array = Array.from(elements); array.forEach(function (element) {element.remove();}); //关于Javlibrary
    return;
}catch (error) {};
//try{}catch (error) {console.error("首页效果错误",error);};
}
mainpage()

try{ // 优化影片详情页显示效果
console.log("详情页");
    var video_jacket_img = document.querySelector("#video_jacket_img"); if (!video_jacket_img) return;
    var imgpath = video_jacket_img.src.replace("so/","/").split('/')[6]; var imgpath1 = imgpath.substr(0,1); var imgpath3 = imgpath.length>5 ? imgpath.substr(0,3):imgpath.substr(0,2)+"0"; //例子:BeFree系列 bf728
    var imgpathrest = imgpath.substr(0,imgpath.length-3); var imgpathnum = imgpath.substr(imgpath.length-3,3); var imgpath00 = imgpathrest+'00'+imgpathnum
    var avid = document.querySelector("head > meta[name=keywords]").content.split(", "); // 两种格式的番号:SONE-001,sone001 使用avid[0],avid[1]选择,未来扩展功能备用。
    const $stylesheet = document.querySelector("head > link[rel=stylesheet]")
    const html = '<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" type="text/css" rel="stylesheet">'
    $stylesheet.insertAdjacentHTML('afterend', html);
    document.querySelector("#video_id > table > tbody > tr > td.header").innerHTML += '<a href=\"https://123av.com/ja/search?keyword='+avid[0]+'\" title=\"搜索123AV\" target=\"sukebei\"><i class="fa fa-search"></i></a>'
    document.querySelector("#video_date > table > tbody > tr > td.header").innerHTML += '<a href=\"https://sukebei.nyaa.si/?q='+avid[0]+'\" title=\"搜索Sukebei\" target=\"sukebei\"><i class="fa fa-search"></i></a>'
    document.querySelector("#video_label > table > tbody > tr > td.header").innerHTML += '<a href=\"https://av-wiki.net/'+avid[0]+'/\" title=\"搜索AV-Wiki\" target=\"sukebei\"><i class="fa fa-search"></i></a>'
    document.querySelector("#video_maker > table > tbody > tr > td.header").innerHTML += '<a href=\"https://supjav.com/zh/?s='+avid[0]+'\" title=\"搜索SupJav\" target=\"sukebei\"><i class="fa fa-search"></i></a>'
    document.querySelector("#video_length > table > tbody > tr > td.header").innerHTML += '<a href=\"https://missav.ws/search/'+avid[0]+'\" title=\"搜索MissAV\" target=\"sukebei\"><i class="fa fa-search"></i></a>'
    document.querySelector("#video_director > table > tbody > tr > td.header").innerHTML += '<a href=\"https://www.javbus.com/'+avid[0]+'\" title=\"搜索JavBus\" target=\"sukebei\"><i class="fa fa-search"></i></a>'
    const $position = document.querySelector("#rightcolumn > hr");if (!$position) return;

function previewsection(){
    const race = (promises) => {
        console.log("测试预览视频");
        const newPromises = promises.map((p) => new Promise((resolve, reject) => p.then((v) => v && resolve(v), reject)));
        newPromises.push(Promise.all(promises).then(() => false));
        return Promise.race(newPromises)
    }

    const preview = async() => {
		const vpdm = async() => {var video1 = 'https://pics.dmm.co.jp/digital/video/'+imgpath+'/'+imgpath+'-1.jpg';
            try {
                const check = async (src) => (await gmFetch(src, { method: 'HEAD' })).status === 200
                if (await check(video1)) {return imgpath};} catch (error) {}
		}

        const vpdmm = async() => {var video2 = 'https://pics.dmm.co.jp/digital/video/'+imgpath00+'/'+imgpath00+'-1.jpg';
            try {
                const check = async (src) => (await gmFetch(src, { method: 'HEAD' })).status === 200
                if (await check(video2)) {return imgpath00};} catch (error) {}
        }

        let src = await race([vpdmm(), vpdm()])
        if (src){
        for (let i=1;i<=20;i++){ // 这里偷懒未做判断,直接显示20个预览图,所以有可能会有无效的预览图
                var thumbssrc ='https://pics.dmm.co.jp/digital/video/'+src+'/'+src+'jp-'+i+'.jpg'
                var thumbssrc2='https://pics.dmm.co.jp/digital/video/'+src+'/'+src+'-'+i+'.jpg'
                thumbsrc += '<a href="'+thumbssrc+'"><img width="120" height="90" border="0" src="'+thumbssrc2+'" ></a>';
            };
        try{document.querySelector("#rightcolumn > div.previewthumbs").remove();}catch (error) {}
        const html =`<div class="previewthumbs" style="display:block; margin:10px auto;"></div>`
        const $position = document.querySelector("#rightcolumn > div.socialmedia");
        $position.insertAdjacentHTML('afterend', html);
        document.querySelector("#rightcolumn > div.previewthumbs").innerHTML = thumbsrc;
        // 创建批量保存预览图的链接
        imgjacket_tag = document.querySelectorAll('#rightcolumn > div.previewthumbs > a');
        var imghref = 'https://pics.dmm.co.jp/digital/video/'+src+'/'+src+'jp-[1:20].jpg';
        const html1 =`<br> <a href=${imghref} onclick="return false;"> [右键用Chrono插件的菜单(链接另存为...)批量保存缩略图]</a> [预览补足] <a id='searchdmm' href='https://video.dmm.co.jp/av/list/?key=${normalizeString(avid[1])}' target=blank>  搜索DMM </a> `;
        $position.insertAdjacentHTML('beforebegin', html1);
        document.querySelector("#rightcolumn > div.socialmedia").remove(); // 删除社交网站图标栏
            preview2();
        }
    }

    const preview2 = async() => {
        var imgpath22 = document.querySelector("#rightcolumn > div.previewthumbs > a").href.split("/")[5]
        imgpath22 = normalizeString(imgpath22)
        var avid = document.querySelector("head > meta[name=keywords]").content.split(", ");
        if (avid[0].indexOf("VR") !== -1) {
            const $position = document.querySelector("#searchdmm")
            const html = `<a href="https://www.dmm.co.jp/digital/-/vr-sample-player/=/cid=${imgpath22}/" target=_blank >  点击打开VR预览视频(VR预览视频可以用鼠标拖动切换视角) </a>`;
            $position.insertAdjacentHTML('afterend', html)
        } else {
            var src = 'https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid='+imgpath22+'/';
        }
console.log(src);
        if (src) document.querySelector("#video_webplayer").style.display = 'block';
            document.querySelector("#WebPlayer").src = src;
    }

    try{ //预览补足
        console.log("预览补足");
        var imgjacket_tag = document.querySelectorAll('#rightcolumn > div.previewthumbs > a ');
        if (imgjacket_tag.length == 0) { // 如果预览图缺失,尝试补足 示例:REBD-937 和 ABF-226
            var thumbsrc = '';
            var thumbsrc00 = 'https://pics.dmm.co.jp/digital/video/'+imgpath00+'/'+imgpath00+'jp-1.jpg';
//          var thumbsrc00 = 'https://pics.dmm.co.jp/digital/video/h_346rebd00937/h_346rebd00937jp-1.jpg' 例子
//          var thumbsrc00 = 'https://pics.dmm.co.jp/digital/video/118abf226/118abf226jp-1.jpg' 例子
            preview();

        }//待扩展
        else { // 创建批量保存预览图的链接
            var imghref = imgjacket_tag[imgjacket_tag.length-1].getAttribute('href'); imghref = imghref.replace('jp-','jp-[1:'); imghref = imghref.replace('.jpg','].jpg');
            const html =`<br> <a href=${imghref} onclick="return false;"> [右键用Chrono插件的菜单(链接另存为...)批量保存缩略图]</a> <a id='searchdmm' href='https://video.dmm.co.jp/av/list/?key=${normalizeString(avid[1])}' target=blank>  搜索DMM </a>`;
            $position.insertAdjacentHTML('beforebegin', html);
            document.querySelector("#rightcolumn > div.socialmedia").remove(); // 删除社交网站图标栏
        }

        try { // 尝试获取预览视频,使用iframe
            const html = '<div id="video_webplayer" style="display:none;"><iframe src="" width="640" height="360" target="_self" scrolling="no" allow="autoplay; fullscreen;" style="border:0;" id="WebPlayer" name="WebPlayer"></iframe></div>'
            $position.insertAdjacentHTML('afterend', html);
        } catch (error) {console.error("预览视频错误");}

        preview2()

    }catch (error) {console.error("预览补足错误");}
}
previewsection()

function commentsection(){
//将评论里面只有推广链接没有评论内容的评论删掉
    console.log("评论");
    try{var elements = document.querySelectorAll("#video_comments > table > tbody > tr > td.t > div > b > a");
       var array = Array.from(elements); array.forEach(function (element) {element.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.remove();}); //使用者文章的种子链接
    }catch {};
    try{elements = document.querySelectorAll("#video_comments > table > tbody > tr > td.t > div > a");
       array = Array.from(elements); array.forEach(function (element) {element.parentNode.parentNode.parentNode.parentNode.parentNode.remove();}); //使用者文章的种子链接
    }catch {};
}
commentsection()

async function sukibeilinks() {
console.log("Sukibei")
try{ // 显示Sukebei的搜索结果磁力链
  const $position = document.querySelector("#videoimages");
  const res = await gmFetch(`https://sukebei.nyaa.si/?q=${avid[0]}`, {method: 'GET', anonymous: true, headers: {'Accept':'text/html,application/xhtml+xml,application/xml', 'User-Agent': 'Android'},});
  const sukebei = parseHTML(res.responseText).querySelector("body > div > div > table");
  if (sukebei){
  var tds = sukebei.querySelectorAll("body > div > div > table > tbody > tr > td:nth-child(1)")
  var array = Array.from(tds);
  array.forEach(element => {element.remove();});
  sukebei.querySelector("body > div > div > table > thead > tr > th").remove()
  tds = sukebei.querySelectorAll("body > div > div > table > tbody > tr > td:nth-child(1) > a")
  array = Array.from(tds);
  array.forEach(element => {
      element.removeAttribute("href");
      element.innerHTML = element.innerHTML.toString().replace("中文","<b style='color:red;'>中文</b>");
      element.innerHTML = element.innerHTML.toString().replace("FHDC","FHDC<b style='color:red;'>中文字幕</b>");
  });
  tds = sukebei.querySelectorAll("body > div > div > table > tbody > tr > td:nth-child(2)")
  array = Array.from(tds);
  array.forEach(element => {element.innerHTML = element.innerHTML.toString().replace('/download', 'https://sukebei.nyaa.si/download');});
  $position.insertAdjacentHTML('beforebegin', "<table border='1'>"+sukebei.innerHTML+'</table><br>');
  }
}catch (error) {console.error("Sukebei搜索结果错误");}
}
sukibeilinks()

const SupJav = async() => {
console.log("SupJav");
    try {
        const response = await new Promise((resolve, reject) => {GM_xmlhttpRequest({method:'GET', url:"https://supjav.com/zh/?s="+avid[0], onload:resolve, onerror:reject});});
        const SupLink = parseHTML(response.responseText).querySelector("div.post > a")
        const Suphtml = "<br><a href='"+SupLink.href+"' target=\"sukebei\">Supa "+SupLink.title+"</a>"
        const $position = document.querySelector("#owned")
        $position.insertAdjacentHTML('afterend', Suphtml)
    }catch (error) {console.error("SupJav错误");}
}
SupJav()

}catch (error) {}

function MissAV() { // 获取MissAV的链接,如果显示多条,可能是有无码和/或中文。
    async function checkVideoAvailability(avid) {
        try {
            const response = await new Promise((resolve, reject) => {GM_xmlhttpRequest({method:'GET', url:"https://missav.ws/search/"+avid, onload:resolve, onerror:reject});});

            if (response.status !== 200) throw new Error(`HTTP错误: ${response.status}`);

                 const MissLink = parseHTML(response.responseText).querySelectorAll("body > div:nth-child(47) > div.sm\\:container.mx-auto.px-4.content-with-search.pb-12 > div.grid.grid-cols-2.md\\:grid-cols-3.xl\\:grid-cols-4.gap-5 > div > div > div > a.text-secondary.group-hover\\:text-primary")
                 MissLink.forEach(element => {
                 const MissHtml = "<br><a href='"+element.href+"' target=\"sukebei\">Miss "+element.text+"</a>"
                 const $position = document.querySelector("#owned")
                 $position.insertAdjacentHTML('afterend', MissHtml)
             })
        } catch (error) {}
    }

    function displayMissAVLink() {
        const avid = document.querySelector("head > meta[name=keywords]").content.split(",")[0]
        checkVideoAvailability(avid);
    }

    displayMissAVLink();
}

MissAV();
})();