// ==UserScript== // @name For Imhentai // @namespace http://tampermonkey.net/ // @version 1.2 // @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_INIT = false; let IS_RUN = false; // 页码序列 [cover.jpg, 1.jpg, ..., 30.jpg] : 页数总数定为 30; 数组 [0] 定为 cover; cover 不一定加载 let CURRENT_PAGE = 0; // 当前页码 let MAX_BROWSE_PAGE = 0; // 最大浏览的页码,只增 let PAGE_LOADED = 0; // 已加载的页码 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]; } // let bzDataIterator; //漫画名去特殊字符处理 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) { for (let [index, root_url, imgInfo] of bzDataIterator) { await processImgInfoAsync(root_url, imgInfo); PAGE_LOADED = index; if (IS_DEBUG) console.log(`${index}:`, imgInfo); } } // 获取漫画名、页数、图片的 url function initData() { let bzData = new BzData(); let coverUrl; // cover 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; `; //