// ==UserScript==
// @name e-hance
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 一个脚本,用来改善e站的观看体验
// @author wof
// @match https://e-hentai.org/g/*
// @match https://exhentai.org/g/*
// @run-at document-end
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @require https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.js
// @resource customCSS https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.css
// @resource animeCSS https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css
// @license MIT
// ==/UserScript==
let util = {
getValue(name) {
return GM_getValue(name);
},
setValue(name, value) {
GM_setValue(name, value);
},
removeValue(name) {
GM_deleteValue(name);
},
addStyle(id, tag, css) {
tag = tag || 'style';
let doc = document, styleDom = doc.getElementById(id);
if (styleDom) return;
let style = doc.createElement(tag);
style.rel = 'stylesheet';
style.id = id;
tag === 'style' ? style.innerHTML = css : style.href = css;
doc.head.appendChild(style);
},
};
var totalpic = 0; // 总图片数
var nowpic = 1; // 当前图片序号
var nowpage = -1; // 当前页数
var nowpageurl = new Array(); // 现在页的所有图片url
var nowurl = ""; // 当前图片的url
var scale = 1; // 图片大小
var imgurl = {}; // 现在页的所有图片src url
var cacheAbortController = null; // 缓存控制
var cacheAbortSignal = null; // 缓存终止信号
var willshowads = false; // 是否显示广告
function getUrl(pic) {
return new Promise(async (resolve, reject) => {
try {
var nowpic = pic - 1;
var page = pic / 40;
if (page == nowpage && !nowpageurl) {
} else {
var host = window.location.href.replace(/\?.*$/, "");
host = host + "?p=" + page;
await fetch(host)
.then(response => response.text())
.then(res => {
var parser = new DOMParser();
var htmlDocument = parser.parseFromString(res, 'text/html');
var pages = htmlDocument.getElementById('gdt').children;
nowpageurl = [];
for (var i = 0; i < pages.length - 1; i++) {
nowpageurl.push(pages[i].firstChild.firstChild.href);
}
});
nowpage = page;
}
await fetch(nowpageurl[nowpic % 40])
.then(response => response.text())
.then(res => {
var parser = new DOMParser();
var htmlDocument = parser.parseFromString(res, 'text/html');
nowurl = htmlDocument.getElementById('img').src;
})
.catch(e => {
1;
});
if (!imgurl[pic]) {
var url = nowurl;
imgurl[pic] = url;
}
var p = {
pic: pic,
url: nowurl,
}
resolve(p);
} catch (error) {
reject(error);
}
});
}
function recover() {
var picture = document.getElementById('img');
picture.style.height = "100%";
scale = 1;
picture.style.transform = `translate(-50%) scale(${scale})`;
picture.style.top = "0px";
picture.style.left = "50%";
document.getElementById("page").innerHTML = `${nowpic}/${totalpic}`;
}
function buttonable() {
var preButton = document.getElementById('pre');
var nextButton = document.getElementById('next');
switch (nowpic) {
case totalpic: {
nextButton.style.pointerEvents = "none";
preButton.style.pointerEvents = "auto";
break;
}
case 1: {
preButton.style.pointerEvents = "none";
nextButton.style.pointerEvents = "auto";
break;
}
default: {
nextButton.style.pointerEvents = preButton.style.pointerEvents = "auto";
}
}
}
// 换页
function changepage(pic) {
var picture = document.getElementById('img');
var divMove = document.querySelector('div#viewer');
var loading = document.getElementById("loading");
var selecter = document.getElementById("pageselecter");
selecter.value = pic;
buttonable()
if (imgurl[pic]) {
if (pic != nowpic) {
}
else {
cache5Images(pic);
var newImage = new Image();
newImage.src = imgurl[pic];
if (newImage.complete) {
divMove.style.background_image = `url(${imgurl[pic]}})`;
picture.src = `${imgurl[pic]}`;
picture.style.opacity = 1;
loading.style.opacity = 0;
recover();
}
else {
picture.style.opacity = 0;
loading.style.opacity = 1;
recover();
newImage.onload = function () {
if (pic != nowpic) {
}
else {
divMove.style.background_image = `url(${imgurl[pic]}})`;
picture.src = `${imgurl[pic]}`;
picture.style.opacity = 1;
loading.style.opacity = 0;
recover();
}
}
}
}
}
else {
picture.style.opacity = 0;
loading.style.opacity = 1;
recover();
getUrl(pic).then((p) => {
imgurl[p.pic] = p.url;
if (p.pic != nowpic) {
}
else {
cache5Images(pic);
var newImage = new Image();
newImage.src = p.url;
if (newImage.complete) {
divMove.style.background_image = `url(${p.url}})`;
picture.src = `${p.url}`;
picture.style.opacity = 1;
loading.style.opacity = 0;
recover();
}
else {
newImage.onload = function () {
if (p.pic != nowpic) {
}
else {
divMove.style.background_image = `url(${p.url}})`;
picture.src = `${p.url}`;
picture.style.opacity = 1;
loading.style.opacity = 0;
recover();
}
}
}
}
});
}
}
function showSlider() {
var slider = document.getElementById("slider");
slider.classList.toggle("show");
removeShiftByKeyborad();
if (slider.classList.contains("show")) {
window.addEventListener("click", hideSliderOnClick);
} else {
window.removeEventListener("click", hideSliderOnClick);
}
}
function hideSliderOnClick(event) {
var slider = document.getElementById("slider");
var target = event.target;
if (!slider.contains(target) && target !== slider && target !== document.getElementById("page")) {
initShiftByKeyboard();
slider.classList.remove("show");
window.removeEventListener("click", hideSliderOnClick);
}
}
var shiftbykeyboard = function (e) {
if (e.key === "ArrowLeft" && nowpic != 1) {
changepage(--nowpic);
}
else if (e.key === "ArrowRight" && nowpic != totalpic) {
changepage(++nowpic);
}
else if (e.key === "M" || e.key === "m") {
addBookmark(nowpic);
}
else if (e.key === "N" || e.key === "n") {
removeBookmark();
}
}
function initShiftByKeyboard() {
document.body.getElementsByClassName("swal2-container swal2-center swal2-grow-fullscreen swal2-backdrop-show")[0].addEventListener("keydown",
shiftbykeyboard,
{ capture: true, passive: true }
);
}
function removeShiftByKeyborad() {
document.body.getElementsByClassName("swal2-container swal2-center swal2-grow-fullscreen swal2-backdrop-show")[0].removeEventListener("keydown",
shiftbykeyboard,
{ capture: true, passive: true }
);
}
function initShiftButton() {
var preButton = document.getElementById('pre');
var nextButton = document.getElementById('next');
nextButton.addEventListener("click", function () {
changepage(++nowpic);
});
preButton.addEventListener("click", function () {
changepage(--nowpic);
});
}
function initSelecter() {
var pageshower = document.getElementById("page");
var selecter = document.getElementById("pageselecter");
selecter.value = nowpic;
pageshower.addEventListener("click", showSlider);
selecter.addEventListener("keydown", function (e) {
e.preventDefault();
});
selecter.addEventListener("input", function () {
var value = selecter.value;
document.getElementById("page").innerHTML = `${value}/${totalpic}`;
});
selecter.addEventListener("change", function () {
var value = selecter.value;
nowpic = Number(value);
changepage(nowpic);
});
}
function initPicFunction() {
var picture = document.getElementById('img');
picture.onmousedown = function (e) {
if (e.button === 0) {
let disX = e.clientX - e.target.offsetLeft;
let disY = e.clientY - e.target.offsetTop;
window.onmousemove = function (event) {
picture.style.left = event.clientX - disX + 'px'
picture.style.top = event.clientY - disY + 'px'
}
}
}
picture.onmouseup = function (e) {
if (e.button === 0) {
window.onmousemove = null
}
}
picture.onwheel = function (e) {
if (e.wheelDelta > 0) {
scale += 0.1
picture.style.transform = `translate(-50%) scale(${scale})`
}
else {
if (scale - 0.1 > 0.1) {
scale -= 0.1
picture.style.transform = `translate(-50%) scale(${scale})`
}
}
}
}
// 缓存图片
function cacheImage(pic, src, signal) {
return new Promise(function (resolve, reject) {
var img = new Image();
img.onload = function () {
resolve();
};
img.onerror = function () {
1;
};
img.src = src;
// 监听终止信号
signal.addEventListener('abort', function () {
if(pic > nowpic + 5 || pic < nowpic){
reject();
}
});
});
}
// 缓存当前页及后五张
async function cache5Images(pic) {
if (cacheAbortController) {
cacheAbortController.abort();
}
// 创建 AbortController 对象和 AbortSignal
cacheAbortController = new AbortController();
cacheAbortSignal = cacheAbortController.signal;
// 创建 Promise 数组来存储每张图片的缓存操作
var cachePromises = [];
for (var i = pic; i <= pic + 5; i++) {
var imgSrc = "";
if (imgurl[i]) {
imgSrc = imgurl[i];
}
else {
await getUrl(i).then((p) => {
imgSrc = p.url;
})
}
var cachePromise = cacheImage(i, imgSrc, cacheAbortSignal);
cachePromises.push(cachePromise);
}
Promise.all(cachePromises)
.then(function () {
})
.catch(function () {
1;
})
}
// 书签相关
function addBookmark(pic) {
showMessage(`已将第${pic}页作为书签位置`);
util.setValue("bookmark", pic);
}
function checkBookmark() {
var bookmark = util.getValue("bookmark");
if (bookmark) {
bookmark = Number(bookmark);
if (isNaN(bookmark)) {
return 1;
}
return bookmark;
}
else {
return 1;
}
}
function removeBookmark() {
showMessage(`已移除书签`);
util.removeValue("bookmark");
}
// 屏幕中央的提示信息
var messagetimer = null;
function showMessage(text) {
if (messagetimer) {
clearTimeout(messagetimer);
messagetimer = null;
}
var messageBox = document.getElementById('viewer-messageBox');
var previousMessageBox = document.querySelector('.show');
if (previousMessageBox) {
previousMessageBox.classList.remove('show');
previousMessageBox.style.display = 'none';
}
messageBox.textContent = text;
messageBox.style.display = 'block';
messageBox.classList.add('show');
messagetimer = setTimeout(function () {
messageBox.classList.remove('show');
messageBox.style.display = 'none';
}, 3000);
}
(function () {
'use strict';
// 应用sweetalert和animate样式
var css1 = GM_getResourceText("customCSS");
GM_addStyle(css1);
var css2 = GM_getResourceText("animeCSS");
GM_addStyle(css2);
// 获取总页数
totalpic = Number((document.getElementsByClassName("gdt2")[5].innerHTML.split(" "))[0]);
nowpic = checkBookmark();
if (willshowads) {
showAds();
}
// 打开菜单
GM_registerMenuCommand('📚开启阅读器', async () => {
var pictureurl;
if (imgurl[nowpic]) {
pictureurl = imgurl[nowpic];
}
else {
pictureurl = await getUrl(nowpic);
pictureurl = pictureurl['url'];
imgurl[nowpic] = pictureurl;
}
let dom = `
<div id="viewer">
<div id="slider" class="slider-container">
<input id = "pageselecter" type="range" name="points" value="1" min="1" max="${totalpic}">
</div>
<p id="loading">加载中</p>
<img id = "img" src="${pictureurl}" alt="" draggable="false">
<div id="pre" class="circle left"></div>
<div id="next" class="circle right"></div>
<div id="viewer-messageBox"></div>
</div>`;
let style = `
.swal2-close {
user-select: none;
}
.swal2-container {
z-index: 9999;
}
div#viewer{
height: 90vh;
overflow: hidden;
position: relative;
}
img#img{
height: 100%;
top: 0px;
position: absolute;
background-image: url("${pictureurl}");
background-repeat: no-repeat;
background-size: 100% 100%;
user-select: none;
transform: translate(-50%);
z-index: 999;
}
button#next{
position: absolute;
}
.circle {
position: absolute;
top: 50%;
width: 80px; /* 调整按钮的宽度 */
height: 80px; /* 调整按钮的高度 */
border-radius: 50%;
background-color: rgba(128, 128, 128, 0.5); /* 灰色透明的背景颜色 */
transform: translateY(-50%);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 1001;
}
.left {
left: 5%;
}
.right {
right: 5%;
}
.circle.left:before {
content: "";
width: 20px;
height: 20px;
border-top: 2px solid #fff;
border-right: 2px solid #fff;
transform: rotate(225deg);
}
.circle.right:before {
content: "";
width: 20px;
height: 20px;
border-top: 2px solid #fff;
border-right: 2px solid #fff;
transform: rotate(45deg);
}
.circle:hover {
background-color: rgba(128, 128, 128, 0.8); /* 鼠标悬停时的背景颜色 */
}
.circle:hover:before {
border-color: #fff; /* 鼠标悬停时的箭头颜色 */
}
.slider-container {
position: absolute;
top: -100%; /* 初始位置在最上方 */
left: 15%;
width: 70%;
height: 3%;
background-color: black;
transition: top 0.5s; /* 添加过渡效果 */
z-index: 1000;
text-align:center;
}
.slider-container.show {
top: 0; /* 滑动下来时的位置 */
}
#pageselecter{
margin: 0 auto;
width: 90%;
user-select: none;
}
#loading{
user-select: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 40px;
opacity: 0;
}
#viewer-messageBox {
display: none;
user-select: none;
position: absolute;
font-size: 30px;
color: #FFFFFF;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: auto;
max-width: 80%;
background-color: rgb(128,128,128,0.8);
border-radius: 8px;
padding: 5px;
text-align: center;
opacity: 0;
transition: opacity 0.5s ease-in-out;
z-index: 999999;
}
#viewer-messageBox.show {
opacity: 1;
}
`;
util.addStyle('viewer-style', 'style', style);
// 生成阅读器
Swal.fire({
title: `<p id="page" style="user-select: none; cursor: pointer;">${nowpic}/${totalpic}</p>`,
html: dom,
showClass: {
popup: 'animate__animated animate__fadeInDown'
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp'
},
showCloseButton: true,
showConfirmButton: false,
background: "#000000",
grow: "fullscreen",
heightAuto: false,
}).then((res) => {
document.body.style.height = "";
document.body.style.overflow = "auto";
res.isConfirmed;
});
// 锁定页面高度
document.body.style.height = "100vh";
document.body.style.overflow = "hidden";
// 缓存后五张
cache5Images(nowpic);
// 检验按钮是否可用
buttonable()
// 初始化左右按钮
initShiftButton();
// 初始化键盘左右键换页
initShiftByKeyboard();
// 初始化选页器
initSelecter();
// 初始化图片拖拽和滚轮放大缩小功能
initPicFunction();
});
// 去除广告
window.addEventListener('DOMContentLoaded', function () {
var iframes = document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
var iframe = iframes[i];
iframe.style.display = "none";
iframe.style.visibility = "none";
iframe.remove();
}
});
// 加载完后再移除一次广告
window.onload = function () {
var iframes = document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
var iframe = iframes[i];
if (iframe.src != "https://ys.mihoyo.com/") {
iframe.style.display = "none";
iframe.style.visibility = "none";
}
}
}
})();