您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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() { // 如果有任何一个请求失败,则进行错误处理 }); } })();