// ==UserScript==
// @name For Imhentai
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 不翻墙下,更快加载 imhentai.xxx 的图片
// @author 水母
// @match https://imhentai.xxx/gallery/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// @license MIT
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
// Your code here...
const IS_DEBUG = false;
// 全局数据
let IS_RUN = false;
let IS_PAGE_LOADING = false; // 加锁
// 页码序列 [cover.jpg, 1.jpg, ..., 30.jpg] : 页数总数定为 30; 数组 [0] 定为 cover; cover 不一定加载
let CURRENT_PAGE = 0; // 当前页码
let PAGE_LOADED = 0; // 已加载的页码
let PAGE_LOAD_STEP = IS_DEBUG ? 2 : 5; // 每次加载页数
let SCALE = IS_DEBUG ? 0.3 : 1; // 图片整体缩放
// class
function BzData(
name_en = 'Null',
name_sub = 'Null',
page = 0,
root_url = '',
imgInfoList = [],
types = ['.jpg', '.png', '.gif', '.err']
) {
this.name_en = name_en;
this.name_sub = name_sub;
this.page = page;
this.root_url = root_url;
this.imgInfoList = imgInfoList;
this.types = types;
}
// imgInfoList[ImgInfo]
function ImgInfo(
imgName,
imgAlt,
imgUrl = '',
imgType = '',
width = 0,
height = 0,
SCALE = 1
) {
this.imgName = imgName;
this.imgAlt = !imgAlt ? imgName : imgAlt;
this.imgUrl = imgUrl;
this.imgType = imgType;
this.width = width;
this.height = height;
this.SCALE = SCALE;
}
// 迭代器
function* BzDataIterator(bzData) {
let index = 0;
while (index < bzData.imgInfoList.length) {
let imgInfo = bzData.imgInfoList[index];
yield [index++, bzData.root_url, imgInfo];
}
yield [undefined, undefined, undefined];
}
//漫画名去特殊字符处理
function processFilename(filename) {
return filename
.replaceAll('\\', '-')
.replaceAll('/', '-')
.replaceAll(':', ':')
.replaceAll('*', '-')
.replaceAll('?', '?')
.replaceAll('"', '“')
.replaceAll('<', '《')
.replaceAll('>', '》')
.replaceAll('|', '~');
}
// 判断图片 url 有效与否
function verifyImgExists(imgUrl) {
return new Promise((resolve, reject) => {
let ImgObj = new Image();
ImgObj.src = imgUrl;
ImgObj.onload = () => resolve(ImgObj);
ImgObj.onerror = (rej) => reject(rej);
});
}
// 为 ImgInfo 生成有效 url
async function processImgInfoAsync(
root_url,
imgInfo,
types = ['.jpg', '.png', '.gif', '.err']
) {
// 测试三种后缀
for (let type of types) {
imgInfo.imgUrl = root_url + imgInfo.imgName + type;
imgInfo.imgType = type;
try {
let ImgObj = await verifyImgExists(imgInfo.imgUrl);
imgInfo.width = ImgObj.width;
imgInfo.height = ImgObj.height;
// console.log(imgInfo);
break;
} catch (e) {
continue; // 未测试最后一个,继续
}
}
}
// 迭代器处理图片序列
async function processImgAsync(bzDataIterator) {
let loadCount;
for (let i = 0; i < PAGE_LOAD_STEP; i++) {
const [index, root_url, imgInfo] = bzDataIterator.next().value;
if (!index) break;
await processImgInfoAsync(root_url, imgInfo);
loadCount = index;
if (IS_DEBUG) console.log(`${index}:`, imgInfo);
}
PAGE_LOADED = loadCount;
IS_PAGE_LOADING = false;
}
// 获取漫画名、页数、图片的 url
function initData() {
let bzData = new BzData();
let coverUrl;
// cover
if (!IS_DEBUG) {
bzData = new BzData();
bzData.imgInfoList.push(new ImgInfo('cover'));
const tag_div_main = document.querySelectorAll(
'body > div.overlay > div.container > div.row.gallery_first > div'
);
// 获取漫画名
bzData.name_en = tag_div_main[1].querySelector('h1').textContent;
bzData.name_sub = tag_div_main[1].querySelector('p.subtitle').textContent;
// 漫画名去特殊字符处理
if (bzData.name_en !== '') {
bzData.name_en = processFilename(bzData.name_en);
}
if (bzData.name_sub !== '') {
bzData.name_sub = processFilename(bzData.name_sub);
}
// 获取页数
let page_str = tag_div_main[1].querySelector('li.pages').textContent;
bzData.page = Number.parseInt(page_str.match(/Pages: ([0-9]*)/i)[1]);
// 图片序列的 url 前缀与封面 url 的相同,
// eg.封面 url=https://m7.imhentai.xxx/023/mnsiote3jg/cover.jpg
// eg.序列的 url=https://m7.imhentai.xxx/023/mnsiote3jg/
coverUrl = tag_div_main[0].querySelector('img').dataset.src;
bzData.root_url = coverUrl.slice(0, coverUrl.lastIndexOf('/') + 1);
// 图片序列的 url 生成,
// eg: https://m6.imhentai.xxx/021/fh5n1d304g/1.jpg
for (let p = 1; p <= bzData.page; p++) {
bzData.imgInfoList.push(new ImgInfo(p.toString())); // 图片名未编码,数字序列就行
}
}
let bzDataIterator = BzDataIterator(bzData);
// 初始化 cover 数据,让 CURRENT_PAGE 与 PAGE_LOADED 能够对齐
let [index, root_url, coverInfo] = bzDataIterator.next().value;
let ImgObj = new Image();
ImgObj.src = coverUrl;
ImgObj.onload = () => {
coverInfo.width = ImgObj.width;
coverInfo.height = ImgObj.height;
};
coverInfo.imgUrl = coverUrl;
if (IS_DEBUG) console.log(coverInfo);
return [bzData, bzDataIterator];
}
// 初始化组件
function initComponents(bzData, bzDataIterator) {
//
const newImg = document.createElement('img');
newImg.id = 'can-img';
newImg.style = `
-webkit-user-select: none;
margin:0 auto;
transition: background-color 300ms;
`;
//
const changePageInput = document.createElement('input');
changePageInput.id = 'can-input';
changePageInput.type = 'number';
changePageInput.value = `${CURRENT_PAGE}`;
changePageInput.disabled = true;
changePageInput.style = `
width: 45%;height: 80%;
font-size:18px;text-align:center;
`;
//