ehentai漫画管理器

exhentai-manga-manager的附加浏览器脚本

// ==UserScript==
// @name         ehentai漫画管理器
// @namespace    http://tampermonkey.net/
// @version      1.5.1
// @license      MIT
// @description  exhentai-manga-manager的附加浏览器脚本
// @author       You
// @match        *exhentai.org/*
// @match        *e-hentai.org/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=exhentai.org
// @grant        GM_addStyle
// @require      https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js
// ==/UserScript==

(function() {
    GM_addStyle(`
.download{
   cursor:pointer;
   position: relative;
}
.download::before {
text-align: center;
  content: "点击下载";
  position: absolute;
  width:50px;
  top: -5px;
  left: 50%;
  transform: translate(-50%,0);
  padding: 5px;
  background-color: #444;
  color: #fff;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
  border-radius: 5px;
}

.download:hover::before {
  opacity: 1;
  visibility: visible;
}
.mark {
  cursor:pointer;
  position: relative;
  background:rgba(0, 0, 0, 0.1);
}

.mark::before {
text-align: center;
  content: "录入meta";
  position: absolute;
  width:50px;
  top: -5px;
  left: 50%;
  transform: translate(-50%,0);
  padding: 5px;
  background-color: #444;
  color: #fff;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
  border-radius: 5px;
}

.mark:hover::before {
  opacity: 1;
  visibility: visible;
}
.copyed{
    font-size:30px;
    position:fixed;
    left: 50%;
    top:100px;
    transform: translate(-50%,0);
    z-index:99;
    padding:10px;
    background-color: #444;
    border-radius: 10px;
    display:none;
    color:#fff;

}
.showDialog{
      white-space: nowrap;
      position:fixed;
      background-color: #444;
      border-radius: 10px;
      padding:18.7px;
      z-index:99;
      color:#fff;
      left: 50%;
      transform: translate(-50%,0);
      vertical-align: middle;
      display:none;
  }
  .showDialog div
  .right{

  }
  .inputtext{
    width: 400px;
  }
  .pointer{
cursor: pointer;
  }
    `);

    'use strict';
    //下载重试次数
    var maxReload=10;
    $("body").prepend("<div class='showDialog'><div><label>端口号<input type='text' class='right inputtext port'></label></div> <br> <div> <label> <span title='点击选择文件路径' class='pointer'>database路径</span> <input type='file' style='display: none;' id='database' name='file'> <input type='text' class='right inputtext databasepath' name=''> </label> </div> <br> <div> <label> <span title='点击选择文件路径' class='pointer'>metabase路径</span> <input type='file' style='display: none;' id='metabase' name='file'> <input type='text' class='right inputtext metadatapath' > </label> </div> <br> <div><label>下载地址<input type='text' class='right inputtext downloadPath'></label></div><br><div class='right'> <button class='confim'>保存</button> <button class='close'>关闭</button> </div> </div>");

    $("body").prepend("<a class='setting'></a>");
    $("body").prepend("<div class='copyed'></div>");
    $(".showDialog .close").click(function(){
        $(".showDialog").hide();
    });

    //菜单保存配置
    $(".showDialog .confim").click(function(){
        if( ($(".showDialog .databasepath").val()!='')&&($(".showDialog .port").val()!='')&&($(".showDialog .metadatapath").val()!='')&&($(".showDialog .downloadPath").val()!='')){
            if(confirm("要保存当前配置吗?")){
                setting("del");
                setting("add");
            }
        }else{
            alert("配置不完整");
        }

    });
    $('#database').change(function() {
        $(".showDialog .databasepath").val(this.value);
    });
    $('#metabase').change(function() {
        $(".showDialog .metadatapath").val(this.value);
    });
    //元数据下载标记
    var yszy=0;
    var ehid;
    var url=window.location.href;
    var result_port;
    var result_appAppDatapath;
    var result_metadatapath;
    var metadatapath;
    var downloadPath;
    var result_downloadPath;
    const request = indexedDB.open('ex1', 1);
    request.onupgradeneeded = function(event) {
        const db = event.target.result;
        //  检查对象存储空间是否存在
        if (!db.objectStoreNames.contains('setting')) {

            // 创建对象存储空间
            const objectStore = db.createObjectStore('setting', { keyPath: 'name' });
        }
    };
    request.onsuccess = function(event) {
        const db = event.target.result;

        // 开始事务(读写模式)
        const transaction = db.transaction(['setting'], 'readwrite');

        // 获取对象存储空间
        const store = transaction.objectStore('setting');
        // 获取数据
        store.get("appAppDatapath").onsuccess = function(event) {
            result_appAppDatapath = event.target.result;

        };

        store.get("port").onsuccess = function(event) {
            result_port = event.target.result;
        };
        store.get("metadatapath").onsuccess = function(event) {
            result_metadatapath = event.target.result;
        };
        store.get("downloadPath").onsuccess = function(event) {
            result_downloadPath = event.target.result;
        };

        transaction.oncomplete = function() {
            if(result_appAppDatapath!=undefined){
                $(".showDialog .databasepath").val(result_appAppDatapath['val']);
            }
            if(result_port!=undefined){
                $(".showDialog .port").val(result_port['val']);
            }
            if(result_metadatapath!=undefined){
                $(".showDialog .metadatapath").val(result_metadatapath['val']);
            }
            if(result_downloadPath!=undefined){
                $(".showDialog .downloadPath").val(result_downloadPath['val']);
            }else{
                 $(".showDialog .downloadPath").val('exh');
            }
            $(".setting").html("点击设置");
            $(".setting").click(function(){
                $(".showDialog").show();
            });
            //配置完成后启用主功能
            if((result_appAppDatapath!=undefined)&&(result_port!=undefined)&&(result_metadatapath!=undefined)&&(result_downloadPath!=undefined)){
                //漫画详细页
                if(url.indexOf("hentai.org/g/")!=-1){
                    //详情页显示
                    $("#taglist").append("<div><span class='download'>未下载 </span><span class='mark'><span style='color:#999;text-align: center;'>未连接</span></span><div>");
                    $("body").append("<div class='expage' style='display:none'><div>");
                    //获取当前uid
                    ehid=url.split("/")[4];
                    var Length=$("#gdd tr:eq(5) td:eq(1)").text().split(" ")[0];
                    var urlarr=[];

                    var downloadedarr;
                    var page_check_deferred = $.Deferred();
                    //先完成已下载图片的检测
                    page_check(page_check_deferred);
                    $("#taglist .download").click(function(){
                        window.open(url+'?download=1', '_blank', 'width=240,height=80,menubar=no,toolbar=no,location=no,status=no');
                    });
                    //此页面自动下载页面
                    if(url.indexOf("download=1")!=-1){
                        $("body").append("<div class='jingdu' style='position:fixed; top:0px; font-size:60px; background-color: #444; z-index:99;  cursor:pointer;'>0/0<div>");
                        $(".jingdu").click(function(){
                            window.location.reload();
                        });
                        $.when(page_check_deferred).done(function(){
                            if(downloadedarr.length==Length){
                                setTimeout(function(){
                                    window.close();
                                },10000);

                            }
                            //收集全部详细页
                            collectPage(urlarr).then(function() {
                                $(".jingdu").html("<span class='downloadedcount'>"+downloadedarr.length+"</span>/<span class='allcount'>"+urlarr.length+"</span>");
                                for(var i=0;i<urlarr.length;i++){
                                    if(downloadedarr.includes(urlarr[i].split("-")[1])){
                                        //去除已下载的链接
                                        urlarr[i]=0;
                                    }

                                }
                                 alAll(urlarr);

                                
                            }).catch(function() {
                                // 处理错误
                            });

                        });
                    }
                    //显示有没有元数据
                    $.ajax({
                        url: "http://localhost:"+result_port['val']+"/data.php",  // 请求的URL
                        method: "POST",  // 请求方法,可选项包括 "GET", "POST", "PUT", "DELETE" 等.
                        data:{
                            appAppDatapath:result_appAppDatapath['val'],
                            metadatapath:result_metadatapath['val'],
                            ehid:'["/'+ehid+'/"]',
                            fun:'showDownload'
                        },  // 请求参数,如果不需要传递参数可以省略
                        success: function(response) {
                        

                                if(response.indexOf(ehid)!=-1){
                                    $(".mark").html("<span style='color:#1b1;text-align: center;'>存在元数据</span>");

                                }else{
                                    $(".mark").html("<span style='color:#999;text-align: center;'>无元数据</span>");
                               }
                           
                        },
                        error: function(xhr, status, error) {
                            console.log("请求错误:", error);
                        }
                    });
                    //点击后添加到数据库
                    $(".mark").click(function(){
                        updateSqlite(url,result_appAppDatapath['val'],result_port['val'],result_metadatapath['val'])
                    });
                    //单张下载状态
                    async function page_check(deferred){
                        $.ajax({
                            url: "http://localhost:"+result_port['val']+"/data.php",  // 请求的URL
                            method: "POST",
                            data:{
                                fun:'page_check',
                                downloadPath:result_downloadPath['val'],
                                ehid:ehid
                            },
                            success: function(response) {
                                var Length=$("#gdd tr:eq(5) td:eq(1)").text().split(" ")[0];
                                downloadedarr = JSON.parse(response);
                                if(yszy==0){
                                    if(Length>=8){
                                        if(downloadedarr.includes('8')){
                                            downloadedmeta('Page_8.jpg',url,result_metadatapath['val'],result_port['val'],ehid,result_downloadPath['val'])
                                        }
                                    }else if(Length<8){
                                        if(downloadedarr.includes('1')){
                                            downloadedmeta('Page_1.jpg',url,metadatapath,result_port['val'],ehid,result_downloadPath['val'])
                                        }
                                    }
                                }
                                //改变下载按钮的状态
                                if(downloadedarr==0){
                                }else if(downloadedarr.length<Length){
                                    $("#taglist .download").html(downloadedarr.length+"/"+Length);
                                }else if(downloadedarr.length==Length){
                                    $("#taglist .download").html("下载完成  ");
                                }
                                deferred.resolve('page_check_deferred success');
                            },
                            error: function() {
                                console.log("请求错误:", error);
                            }
                        });
                    }
                    //单张页面链接下载单张
                    async function downloadPage(urlarr,i,reload){
                        if(urlarr[i]!=0&&i<urlarr.length){

                            async function fetchData(url,cou) {
                                try {
                                    const response = await fetch(url);
                                    const html =await response.text();
                                    return html;
                                } catch (error) {
                                    console.error(error);
                                    // 请求失败,则重新发起请求
                                    cou++;
                                    if(cou>=10){
                                         window.location.reload();
                                    }else{
                                         return fetchData(url,cou);
                                    }

                                }
                            }
                            const html =await fetchData(urlarr[i],1);
                            var imageUrl;
                            var imageLinks = [];
                            var imgRegex = /<img\s[^>]*?\bid=['"]img['"][^>]*?src=['"](.*?)['"]/g;
                            var loadfailRegex= /<a\s[^>]*?\bid=['"]loadfail['"][^>]*?onclick=['"]return nl\('([^']*)'\)['"]/g;
                            var match;
                            var loadfail;
                            var lff;
                            if((loadfail = loadfailRegex.exec(html)) !== null) {
                                //重新加载图片代码
                                lff=loadfail[1];
                            }
                            if((match = imgRegex.exec(html)) !== null) {
                                imageUrl=match[1];
                            }

                            var gid=urlarr[i].split("/")[5].split("-")[0];
                            var filename=gid;
                            if($("#gj").length==1){
                                filename=filename+'-'+$("#gj").text();
                            }else{
                                filename=filename+'-'+$("#gn").text();
                            }

                            filename=filename.replace('|','');
                            var pagenum="Page_"+urlarr[i].split("/")[5].split("-")[1].split("?")[0];
                            const jieguo=await dl(imageUrl,filename,pagenum);
                            if(jieguo=='fail'){
                                if(urlarr[i].indexOf("?nl")!=-1){
                                    urlarr[i]= urlarr[i]+"&nl="+lff;
                                }else{
                                    urlarr[i]= urlarr[i]+"?nl="+lff;
                                }
                                downloadPage(urlarr,i,reload);
                            }

                        }
                    }
                    //循环下载
                    async function alAll(urlarr){

                        for(let a=0;a<urlarr.length;a=a+3){
                            await downloadPage(urlarr,a,1);
                        }
                        for(let a=1;a<urlarr.length;a=a+3){
                            await downloadPage(urlarr,a,1);
                        }
                        for(let a=2;a<urlarr.length;a=a+3){
                            await downloadPage(urlarr,a,1);
                        }
                    }
                    //下载
                    async function dl(imageUrl,filename,pagenum){
                        return new Promise((resolve, reject) =>{
                            $.ajax({
                                url: "http://localhost:"+result_port['val']+"/data.php",  // 请求的URL
                                method: "POST",
                                data:{
                                    imageUrl:imageUrl,
                                    filename:filename,
                                    page:pagenum,
                                    downloadPath:result_downloadPath['val'],
                                    fun:'download'
                                }
                            }).done(response => {
                                console.log('请求成功:', response);
                                if(response.indexOf("文件已成功下载并保存到")!=-1){

                                    console.log(pagenum+"已下载");
                                    page_count();
                                    resolve('success');
                                }else{
                                    console.log("图片502");
                                    //重载图片
                                    resolve('fail');
                                }
                                // 执行其他操作
                            }) .fail(error => {
                                console.log("下载超时");
                                resolve('fail');
                            });

                        })


                    }
                    //查看本地图片数量
                    async function page_count(){
                        $.ajax({
                                url: "http://localhost:"+result_port['val']+"/data.php",  // 请求的URL
                                method: "POST",
                                data:{
                                    ehid:ehid,
                                    downloadPath:result_downloadPath['val'],
                                    fun:'page_check'
                                },
                                 success: function(response) {
                                     var response_count=JSON.parse(response).length;
                                     $(".downloadedcount").html(response_count);
                                     if(urlarr.length==response_count){
                                          window.location.reload();
                                     }
                                 }
                        })
                    }
                }
                //e站搜索页面
                if(url.indexOf("hentai.org")!=-1&&$("#toppane").length==1){
                    ehid=[];

                    //右键清空搜索栏
                    $("#f_search").contextmenu(function(){
                        $("#f_search").val("");
                    });
                    //格式化搜索框内的内容
                    $("#f_search").change(function(){
                        var kw=$("#f_search").val();
                        kw=kw.replace(/\(.*?\)/g,'');
                        kw=kw.replaceAll('~','');
                        kw=kw.replace('.zip','');
                        if(!isNaN(kw.substring(0,kw.indexOf("-")))){
                            kw=kw.substring(kw.indexOf("-")+1,kw.length);
                        }
                        $("#f_search").val(kw);
                        $("#f_search").next().trigger("click");
                    });
                    $(".gl1t").append("<div><span></span><span class='mark'><div style='color:#999;text-align: center;'>未连接</span></div><div>");
                    $(".mark").click(function(){
                        updateSqlite($(this).parent().parent().find("a:eq(1)").attr("href"),result_appAppDatapath['val'],result_port['val'],result_metadatapath['val'])
                    });
                    $(".gl1t").each(function(){
                        var ehurl=$(this).find("a").attr("href");
                        ehurl=ehurl.split("/");
                        ehid.push("/"+ehurl[4]+"/");
                    });

                    //显示数据库有没有元数据
                    $.ajax({
                        url: "http://localhost:"+result_port['val']+"/data.php",  // 请求的URL
                        method: "POST",  // 请求方法,可选项包括 "GET", "POST", "PUT", "DELETE" 等.
                        data: {
                            appAppDatapath:result_appAppDatapath['val'],
                            result_metadatapath:result_metadatapath['val'],
                            ehid:JSON.stringify(ehid),
                            fun:'showDownload'
                        },  // 请求参数,如果不需要传递参数可以省略
                        success: function(response){
                            //转为对象
                            var back_obj = eval(response);
                            $(".mark").html("<div style='color:#999;text-align: center;'>未下载</div>");

                            //遍历网址
                            $(".gl1t").each(function(){
                                //修改下载图标的行为
                                var mgurl=$(this).find("a:eq(1)").attr("href");
                                $(this).find(".gldown a").attr("href","javascript:void(0);");
                                $(this).find(".gldown a").attr("onclick","");
                                $(this).find(".gldown").click(function(){
                                    window.open(mgurl+'?download=1', '_blank', 'width=240,height=80,menubar=no,toolbar=no,location=no,status=no');
                                });
                                //遍历返回的对象
                                for(let i=0;i<back_obj.length;i++){
                                    if($(this).find("a").attr("href").indexOf(back_obj[i])!=-1){
                                        $(this).find(".mark").html("<div style='color:#1c1;text-align: center;'>已下载</div>");
                                        break;
                                    }
                                }
                                //当前页面只有一个搜素结果时自动提取meta
                                if($(".gl1t").length==1&& $(".mark").text()=="未下载"){
                                    $(".mark").focus();
                                    $(".mark").trigger("click");
                                }
                            });
                        },
                        error: function(xhr, status, error) {
                            // 请求失败时的回调函数
                            console.log("请求错误:", error);
                        }
                    });

                }
            }

        }
            // 关闭数据库连接
            db.close();
    }


    //将元数据插入metadate
    async function downloadedmeta(page,url,metadatapath,port,ehid,downloadPath){
       url=url.replace('?download=1','');
       let match = /(\d+)\/([a-z0-9]+)/.exec(url);
       fetch('https://api.e-hentai.org/api.php', {
           method: "POST",
           body: JSON.stringify({
               'method': 'gdata',
               'gidlist': [
                   [match[1], match[2]]
               ],
               'namespace': 1
           })
       })
           .then(async res => {
           let metaobj = {}
           let gmetadata = await res.json()
           gmetadata = gmetadata.gmetadata[0]
           let tags = {}
           gmetadata.tags.forEach(tagString => {
               let match = /^(.+):(.+)$/.exec(tagString)
               if (tags[match[1]]) {
                   tags[match[1]].push(match[2])
               } else {
                   tags[match[1]] = [match[2]]
               }
           })
           metaobj.tags = tags
           metaobj.url = url
           metaobj.title = gmetadata.title
           metaobj.title_jpn = gmetadata.title_jpn
           metaobj.rating = +gmetadata.rating
           metaobj.posted = +gmetadata.posted
           metaobj.filecount = +gmetadata.filecount
           metaobj.category = gmetadata.category
           metaobj.filesize = gmetadata.filesize
           metaobj.status = 'tagged'
           $.ajax({
                url: "http://localhost:"+port+"/data.php",  // 请求的URL
                method: "POST",
                data:{
                    page:page,
                    ehid:ehid,
                    downloadPath:downloadPath,
                    metadatapath:metadatapath,
                    metaobj:JSON.stringify(metaobj),
                    fun:'sha1'
                },  
                success: function(response) {

                 
                },
                error: function(xhr, status, error) {
                    console.log("请求错误:", error);
                }
            });
   });
}
   //获取元数据后添加进本地数据库
    function updateSqlite(url,appAppDatapath,port,metadatapath){
        //获取元数据
        let match = /(\d+)\/([a-z0-9]+)/.exec(url);
        fetch('https://api.e-hentai.org/api.php', {
            method: "POST",
            body: JSON.stringify({
                'method': 'gdata',
                'gidlist': [
                    [match[1], match[2]]
                ],
                'namespace': 1
            })
        })
            .then(async res => {
            let metaobj = {}
            let gmetadata = await res.json()
            gmetadata = gmetadata.gmetadata[0]
            let tags = {}
            gmetadata.tags.forEach(tagString => {
                let match = /^(.+):(.+)$/.exec(tagString)
                if (tags[match[1]]) {
                    tags[match[1]].push(match[2])
                } else {
                    tags[match[1]] = [match[2]]
                }
            })
            metaobj.tags = tags
            metaobj.url = url
            metaobj.title = gmetadata.title
            metaobj.title_jpn = gmetadata.title_jpn
            metaobj.rating = +gmetadata.rating
            metaobj.posted = +gmetadata.posted
            metaobj.filecount = +gmetadata.filecount
            metaobj.category = gmetadata.category
            metaobj.filesize = gmetadata.filesize
            metaobj.status = 'tagged'
            var gid=gmetadata.gid
            //连接data.php
            //三次搜索,用gid搜索文件夹名,主要用于ehviewer下载的文件中所带的gid号,用日文搜索文件名,用英文搜索文件名
            $.ajax({
                url: "http://localhost:"+port+"/data.php",  // 请求的URL
                method: "POST",
                data:{
                    appAppDatapath:appAppDatapath,
                    metadatapath:metadatapath,
                    gid:gid,
                    metaobj:JSON.stringify(metaobj),
                    fun:'updateDatabase'
                },  // 请求参数,如果不需要传递参数可以省略
                success: function(response) {
                    //无搜索结果时使用手动输入
                    if(response.indexOf("notfind")!=-1){
                        //复制
                        navigator.clipboard.writeText(JSON.stringify(metaobj));
                        $(".copyed").text("录入失败,请手动录入元数据,元数据已复制到剪切版");
                    }
                    if(response.indexOf("success")!=-1){
                        $(".copyed").text("录入成功");
                    }
                    $('.copyed').fadeIn(); // 元素开始时显示
                    setTimeout(function() {
                        $('.copyed').fadeOut(); // 5秒后元素消失
                    }, 2000);
                },
                error: function(xhr, status, error) {
                    console.log("请求错误:", error);
                }
            });

        })



    }
    //设置端口和文件路径下载路径
    function setting(type){
        const request = indexedDB.open('ex1', 1);
        request.onupgradeneeded = function(event) {
            const db = event.target.result;
            //  检查对象存储空间是否存在
            if (!db.objectStoreNames.contains('setting')) {
                // 创建对象存储空间
                const objectStore = db.createObjectStore('setting', { keyPath: 'name' });
            }
        };
        request.onsuccess = function(event) {
            const db = event.target.result;

            // 开始事务(读写模式)
            const transaction = db.transaction(['setting'], 'readwrite');

            // 获取对象存储空间
            const store = transaction.objectStore('setting');
            if(type=='add'){
                var port = $(".showDialog .port").val();
                var appAppDatapath= $(".showDialog .databasepath").val();
                var metadatapath=$(".showDialog .metadatapath").val();
                var downloadPath=$(".showDialog .downloadPath").val();
                const data_port = { name:'port', val: port };
                store.add(data_port);
                const data_appAppDatapath = { name:'appAppDatapath', val: appAppDatapath };
                store.add(data_appAppDatapath);
                const data_metadatapath = { name:'metadatapath', val: metadatapath };
                store.add(data_metadatapath);
                const data_downloadPath = { name:'downloadPath', val: downloadPath };
                store.add(data_downloadPath);
            }else if(type=='del'){
                store.delete('port');
                store.delete('appAppDatapath');
                store.delete('metadatapath');
                store.delete('downloadPath');
            }
            transaction.oncomplete = function() {
                // 关闭数据库连接

                db.close();
                location.reload();
            };

        }


    }
    //收集该本漫画的全部单张页面链接 links 链接数组
    function collectPage(links){
        var Length=$("#gdd tr:eq(5) td:eq(1)").text().split(" ")[0];
        var onepageSize=$("#gdt a").length;
        //向上取余
        var  pagecont=Math.floor((Length-0 + onepageSize - 1) / onepageSize);
        var ajaxRequests = [];
        function ppp(url, i,cou) {
            return new Promise((resolve, reject) => {
                $.ajax({
                    url: url + "&p=" + i,
                    method: 'GET',
                    success: function(response) {
                        const html = response;
                        const pattern = /<a\s+(?:[^>]*?\s+)?href=(['"])(.*?)\1/g;
                        const divPattern = /<div\s+id="gdt"[^>]*>(.*?)class="gtb"/g;

                        let match;
                        let divmatch;
                        divmatch = divPattern.exec(html)
                        while ((match = pattern.exec(divmatch[0])) !== null) {
                            if (match[2].includes('/s/')) {
                                links.push(match[2]);
                            }
                        }
                        resolve(); // 请求成功后resolve Promise
                    },
                    error: function(xhr, status, error) {
                        console.log('请求错误:', error);

                        cou++;
                        if(cou>=10){
                            window.location.reload();
                        }else{
                            return ppp(url, i,cou)
                        }

                    }
                });
            });
        }


        for (var i = 0; i <= (pagecont - 1); i++) {
            ajaxRequests.push(ppp(url, i,1));
        }

        return Promise.all(ajaxRequests).then(function() {
            // 所有异步请求完成后的处理
        }).catch(function() {
            // 如果有任何一个请求失败,则进行错误处理
        });
    }  
})();