您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
不翻墙下,更快加载 imhentai.xxx 的图片,并提供打包下载
// ==UserScript== // @name For Imhentai // @namespace http://tampermonkey.net/ // @version 2.5 // @description 不翻墙下,更快加载 imhentai.xxx 的图片,并提供打包下载 // @author 水母 // @match https://imhentai.xxx/gallery/* // @match https://*.imhentai.xxx/* // @icon https://imhentai.xxx/images/logo.png // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js#sha512-uVSVjE7zYsGz4ag0HEzfugJ78oHCI1KhdkivjQro8ABL/PRiEO4ROwvrolYAcZnky0Fl/baWKYilQfWvESliRA== // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_download // @grant GM_openInTab // @license MIT // ==/UserScript== (function () { "use strict"; // 全局数据 let IS_INIT = false; /** * 启动 */ let IS_RUN = false; let IS_DOWNLOADING = false; // 页码序列 [cover.jpg, 1.jpg, ..., 30.jpg] : cover不计入页数; eg.总页数定为 30; 数组 [0] 定为 cover /** * 当前页码 */ let CURRENT_PAGE = 0; /** * 最大浏览的页码,只增 */ let MAX_BROWSE_PAGE = 0; /** * 已加载的页码 */ let PAGE_LOADED = 0; /** * @desc 标识当前在哪个页面 */ let CURRENT_URL; /** * @desc 页面枚举 */ const CURRENT_URL_TYPE = { /** * @desc 标识在 https://imhentai.xxx/gallery/? 页面 */ gallery: "gallery", /** * @desc 标识在 https://imhentai.xxx/view/? 页面 */ view: "view", /** * @desc 标识在 https://?.imhentai.xxx/?/?/cover.jpg 页面 */ imgPage: "imgPage", /** * @desc 标识在 测试 gallery 页面 */ testGallery: "testGallery", /** * @desc 标识在 测试 imgPage 页面 */ testImgPage: "testImgPage", /** * @desc 标识为无法识别页面 */ unknow: "unknow", }; /** * 用户定义的下载页码区间 */ let UserCustemRange = { min: 0, max: 0, page_loaded: 0, }; // enum const CanEleID = { /** * 主体 */ app: "can-app", runBtn: "can-run", previousBtn: "can-pre", nextBtn: "can-next", downloadBtn: "can-down", scaleUpBtn: "can-sUp", scaleResetBtn: "can-sReset", scaleDownBtn: "can-sDown", /** * 页码 */ pageLabel: "can-page", /** * 页码跳转 */ changePageInput: "can-input", /** * 图片显示 */ showImg: "can-img", /** * 图片显示外包 <div> */ showImgDiv: "can-img-div", /** * 转 base64 */ canvas: "can-cvs", }; const BtnText = { runBtn: "启动🥰", previousBtn: "⫷", nextBtn: "⫸", downloadBtn: "下载🥵", scaleUpBtn: "⇲", scaleResetBtn: "↺◲", scaleDownBtn: "⇱", }; /** * FileReader 加载完毕计算器,由异步方法调用 */ const CounterForFileReader = { /** * 异步锁 */ is_lock: false, count: 0, /** * 如果更新成功,返回 true * @returns {boolean} */ update() { if (this.is_lock) return false; else { this.is_lock = true; this.count++; this.is_lock = false; return true; } }, }; // 避免没必要下载属性,被 JSON.stringify() const keyImageBase64 = Symbol("imageBase64"); const keyImageScale = Symbol("imageScale"); /** * * @param {string} imgName * @param {string} imgUrl * @param {string} imgType * @param {number} width * @param {number} height * @param {number} scale 仅用于页面浏览 * @param {string} imageBase64 */ function ImgInfo( imgName, imgUrl = "", imgType = "", width = 0, height = 0, scale = 1.0, imageBase64 = "" ) { this.imgName = imgName; this.imgUrl = imgUrl; this.imgType = imgType; this.width = width; this.height = height; this[keyImageScale] = scale; this[keyImageBase64] = imageBase64; } /** * 本子数据 * @param {string} name_en * @param {string} name_sub * @param {number} page * @param {string} origin_url 原预览页面 * @param {string} root_url 图片的根地址 * @param {ImgInfo[]} imgInfoList */ function BzData( name_en = "Null", name_sub = "Null", page = 0, origin_url = "", root_url = "", imgInfoList = [] ) { this.name_en = name_en; this.name_sub = name_sub; this.page = page; this.origin_url = origin_url; this.root_url = root_url; this.imgInfoList = imgInfoList; } /** * BzData 迭代器 * @param {BzData} bzData */ function* BzDataIterator(bzData) { let index = 0; while (index < bzData.imgInfoList.length) { let imgInfo = bzData.imgInfoList[index]; yield [index++, bzData.root_url, imgInfo]; } } /** * 保存跳转下载的存储数据 */ let downloadTabData = { dataKey: "", oriBzData: null, }; // <style> ((t) => { const e = document.createElement("style"); e.textContent = t; document.head.append(e); })( ` #${CanEleID.app} { top:40%; width:120px; height:200px; font-size:20px; color: #d71989; background-color:hsla(0, 0%, 90%, 50%); display:flex; flex-direction:column; justify-content:space-between; position:fixed; z-index:1000002; transform:translateX(calc(-50% * var(--direction))) translateY(-50%); } .can-button-sm { height: 30px; font-size: 20px; color: #d71989; flex: 1; } .can-button-lg { height: 34px; font-size:20px; color: #d71989; } #${CanEleID.app} div { width: 100%; } #${CanEleID.app} svg { display: none; width: 24px; height: 24px; } #${CanEleID.showImg} { -webkit-user-select: none; margin:0 auto; transition: background-color 300ms; } #${CanEleID.changePageInput} { width: 90%; height: 24px; font-size:18px; text-align:center; } #${CanEleID.pageLabel} { font-size:18px; text-align:center; margin: 0px 3px; background-color: hsla(0, 0%, 90%, 90%); flex: 1; } #${CanEleID.showImgDiv} { display:none; position: fixed; overflow: auto; width: 80%; height: 100%; top: 0%; z-index: 1000001; left: 0; right: 0; margin:0 auto; text-align: center; background-color: hsla(338, 100%, 70%, 0.8); } ` ); /** * 漫画名去特殊字符处理 * @param {string} filename 文件名 * @return {string} 处理后的文件名 */ function processFilename(filename) { return filename .replaceAll("\\", "-") .replaceAll("/", "-") .replaceAll(":", ":") .replaceAll("*", "-") .replaceAll("?", "?") .replaceAll('"', "“") .replaceAll("<", "《") .replaceAll(">", "》") .replaceAll("|", "~"); } /** * 判断图片 url 有效与否 * @returns {Promise<Image>} */ 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 和后缀格式,并生成 base64 * @param {string} root_url * @param {ImgInfo} imgInfo * @param {string[]} types ['.jpg', '.png', '.gif', '.err'] */ 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); // 图片有效,即加载图片的 base64 // 避开站点的跨域策略 if ( CURRENT_URL !== CURRENT_URL_TYPE.gallery && CURRENT_URL !== CURRENT_URL_TYPE.testGallery ) { // canvas 无法加载 gif if (type !== ".gif") { try { let c = document.createElement("canvas"); let ctx = c.getContext("2d"); c.height = ImgObj.naturalHeight; c.width = ImgObj.naturalWidth; ctx.drawImage( ImgObj, 0, 0, ImgObj.naturalWidth, ImgObj.naturalHeight ); // 图片格式的mime类型:image/png, image/jpeg imgInfo[keyImageBase64] = type === ".jpg" ? c.toDataURL("image/jpeg", 1.0) : c.toDataURL(); } catch (e1) { imgInfo[keyImageBase64] = "data:image/png;base64,null"; console.log(`[ERR] ${imgInfo.imgUrl} 无法处理为 base64 : ${e1}`); } } else { getGifBase64Async(imgInfo); } } imgInfo.width = ImgObj.width; imgInfo.height = ImgObj.height; break; // 结束循环 } catch (e2) { if (type !== ".err") { console.log(`[TEST] ${imgInfo.imgUrl} 不存在,尝试下一个扩展名`); } else { imgInfo[keyImageBase64] = "data:image/png;base64,null"; console.log(`[ERR] ${imgInfo.imgUrl} 不存在`); } // 继续循环 } } } /** * 处理所有图片 * @param {BzDataIterator} bzDataIterator */ async function processImgAsync(bzDataIterator) { let page_ = document.querySelector(`#${CanEleID.pageLabel}`); let div_img = document.querySelector(`#${CanEleID.showImgDiv}`); for (let [index, root_url, imgInfo] of bzDataIterator) { await processImgInfoAsync(root_url, imgInfo); updateImgInfoScale(imgInfo, false, div_img); PAGE_LOADED = index; page_.textContent = `${PAGE_LOADED}`; } document.querySelector(`#${CanEleID.app} svg`).style.display = "none"; } /** * 获取图片的 base64 编码,此处指定 Gif ,其他格式由 canvas 方式获取 * @param {ImgInfo} imgInfo */ const getGifBase64Async = async (imgInfo) => { try { let reader = new FileReader(); reader.onloadend = function () { imgInfo[keyImageBase64] = reader.result; // 持续,直至更新计数 let intervalID = setInterval(() => { if (CounterForFileReader.update()) clearInterval(intervalID); }, Math.round(Math.random() * 1000)); }; // 加载图片的 blob 类型数据 let imgBlob = await fetch(imgInfo.imgUrl).then((respone) => respone.blob() ); reader.readAsDataURL(imgBlob); // 将 blob 数据转换成 DataURL 数据 } catch (e) { console.error(e); } }; /** * 批量下载图片 * @param {BzData} bzData 图像数据 * @param {number} min * @param {number} max */ const downloadZip = async (bzData, min, max) => { console.log(bzData); document.querySelector(`#${CanEleID.downloadBtn}`).textContent = "正在打包"; const zip = new JSZip(); // 图片 url json 文件 let stringData = JSON.stringify(bzData, null, 2); zip.file( `${bzData.name_en} [${UserCustemRange.min}-${UserCustemRange.max}].json`, stringData ); // 创建图片文件夹 const fileFolder = zip.folder( `${bzData.name_en} [${UserCustemRange.min}-${UserCustemRange.max}]` ); const fileList = []; for (let i = min; i <= max; i++) { let name = bzData.imgInfoList[i].imgName + bzData.imgInfoList[i].imgType; let imageBase64 = bzData.imgInfoList[i][keyImageBase64].substring(22); // 截取 data:image/png;base64, 后的数据 fileList.push({ name: name, img: imageBase64 }); } // 往 zip 中,添加每张图片数据 for (let imgFile of fileList) { fileFolder.file(imgFile.name, imgFile.img, { base64: true, }); } document.querySelector(`#${CanEleID.downloadBtn}`).innerHTML = "<div style='font-size: 10px;'>浏览器酱正在响应</div>"; zip.generateAsync({ type: "blob" }).then((content) => { // saveAs( // content, // `${bzData.name_en} [${UserCustemRange.min}-${UserCustemRange.max}].zip` // ); const downloadUrl = URL.createObjectURL(content); GM_download({ url: downloadUrl, name: `${bzData.name_en} [${UserCustemRange.min}-${UserCustemRange.max}].zip`, saveAs: true, onload: () => { // 按钮还原 document.querySelector(`#${CanEleID.downloadBtn}`).textContent = BtnText.downloadBtn; document.querySelector(`#${CanEleID.downloadBtn}`).disabled = false; IS_DOWNLOADING = false; }, onerror: (error) => { console.log(error); // 按钮还原 document.querySelector(`#${CanEleID.downloadBtn}`).textContent = BtnText.downloadBtn; document.querySelector(`#${CanEleID.downloadBtn}`).disabled = false; IS_DOWNLOADING = false; }, }); }); }; /** * 数据初始化,获取漫画名、页数、图片的 url */ function initData() { let bzData = new BzData(); let bzDataIterator; console.log(`CURRENT_URL:${CURRENT_URL}`); if ( CURRENT_URL === CURRENT_URL_TYPE.gallery || CURRENT_URL === CURRENT_URL_TYPE.testGallery ) { let coverUrl; 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_sub !== "") { bzData.name_sub = processFilename(bzData.name_sub); } if (bzData.name_en !== "") { bzData.name_en = processFilename(bzData.name_en); } else { bzData.name_en = 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]); // 预览页面地址 bzData.origin_url = window.location.href; // 图片序列的 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); // 在 gallary 页面保存数据,跳转 imgPage 页面后使用 // https://m7.imhentai.xxx/023/mnsiote3jg/cover.jpg let dataKey = coverUrl.substring("https://".length); dataKey = dataKey.substring(0, dataKey.lastIndexOf("/")); // dataKey = "m7.imhentai.xxx/023/mnsiote3jg" downloadTabData.dataKey = dataKey; downloadTabData.oriBzData = new BzData( bzData.name_en, bzData.name_sub, bzData.page, bzData.origin_url, bzData.root_url ); console.log(downloadTabData); } else if ( CURRENT_URL === CURRENT_URL_TYPE.imgPage || CURRENT_URL === CURRENT_URL_TYPE.testImgPage ) { let dataKey = window.location.href.substring("https://".length); dataKey = dataKey.substring(0, dataKey.lastIndexOf("/")); bzData = GM_getValue(`${dataKey}`); if (!bzData) alert("数据为空,请先访问,预览页面 gallarg"); } // cover bzData.imgInfoList.push(new ImgInfo("cover")); // 图片序列的 url 生成, // eg: https://m7.imhentai.xxx/023/mnsiote3jg/1.jpg for (let p = 1; p <= bzData.page; p++) { bzData.imgInfoList.push(new ImgInfo(p.toString())); // 图片名未编码,数字序列就行 } bzDataIterator = BzDataIterator(bzData); // 初始化 cover 数据,next() 让 CURRENT_PAGE 与 PAGE_LOADED 能够对齐 let [index, root_url, coverInfo] = bzDataIterator.next().value; processImgInfoAsync(bzData.root_url, coverInfo); console.log(bzData); // alert(JSON.stringify(bzData)); return [bzData, bzDataIterator]; } /** * 初始化组件 * @param {BzData} bzData * @param {BzDataIterator} bzDataIterator */ function initComponents(bzData, bzDataIterator) { document.body.insertAdjacentHTML( "beforeend", ` <div id="${CanEleID.app}"> <button id="${CanEleID.runBtn}" class="can-button-lg">${BtnText.runBtn}</button> <div style="display: flex; flex-direction: column; justify-content: space-around; align-items: center; flex: 1;"> <div style="display: flex; flex-direction: row;"> <button id="${CanEleID.previousBtn}" class="can-button-sm" disabled>${BtnText.previousBtn}</button> <button id="${CanEleID.nextBtn}" class="can-button-sm" disabled>${BtnText.nextBtn}</button> </div> <div style="display: flex; flex-direction: row;"> <button id="${CanEleID.scaleUpBtn}" class="can-button-sm" disabled>${BtnText.scaleUpBtn}</button> <button id="${CanEleID.scaleResetBtn}" class="can-button-sm" disabled>${BtnText.scaleResetBtn}</button> <button id="${CanEleID.scaleDownBtn}" class="can-button-sm" disabled>${BtnText.scaleDownBtn}</button> </div> <input id="${CanEleID.changePageInput}" value="0" disabled> <div style="display: flex; flex-direction: row; align-items: center;"> <label id="${CanEleID.pageLabel}">0</label> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="#ff7cc9"> <path opacity=".25" d="M16 0 A16 16 0 0 0 16 32 A16 16 0 0 0 16 0 M16 4 A12 12 0 0 1 16 28 A12 12 0 0 1 16 4"/> <path d="M16 0 A16 16 0 0 1 32 16 L28 16 A12 12 0 0 0 16 4z"> <animateTransform attributeName="transform" type="rotate" from="0 16 16" to="360 16 16" dur="0.8s" repeatCount="indefinite" /> </path> </svg> </div> </div> <button id="${CanEleID.downloadBtn}" class="can-button-lg">${BtnText.downloadBtn}</button> </div> <div id="can-img-div" style="display: none;"> <img id="can-img" alt="null"> </div> <canvas id="can-cvs" style="display: none;"></canvas> ` ); document .querySelector(`#${CanEleID.runBtn}`) .addEventListener("click", (evt) => { evt.stopPropagation(); if (!IS_INIT) { IS_INIT = true; // 初始显示封面 document.querySelector(`#${CanEleID.showImgDiv}`).style.display = "block"; updateImgInfoScale(bzData.imgInfoList[0]); updateShowImgTag(bzData.imgInfoList[0]); // 异步加载图片信息 document.querySelector(`#${CanEleID.app} svg`).style.display = "block"; processImgAsync(bzDataIterator); } if (!IS_RUN) { IS_RUN = true; if ( CURRENT_URL === CURRENT_URL_TYPE.imgPage || CURRENT_URL === CURRENT_URL_TYPE.testImgPage ) { evt.target.textContent = `跳转预览页`; } else { evt.target.textContent = "显示预览页"; } // 生效按钮 let btns = document .querySelector(`#${CanEleID.app}`) .querySelectorAll("button"); for (const btn of btns) { btn.disabled = false; } let inputPage = document.querySelector( `#${CanEleID.changePageInput}` ); inputPage.disabled = false; // 显示 新 <img> document.querySelector(`#${CanEleID.showImgDiv}`).style.display = "block"; } else { if ( CURRENT_URL === CURRENT_URL_TYPE.imgPage || CURRENT_URL === CURRENT_URL_TYPE.testImgPage ) { GM_openInTab(bzData.origin_url, { active: true }); } else { IS_RUN = false; evt.target.textContent = BtnText.runBtn; // 无效按钮 let btns = document .querySelector(`#${CanEleID.app}`) .querySelectorAll("button"); for (const btn of btns) { btn.disabled = btn.id !== CanEleID.runBtn && btn.id !== CanEleID.downloadBtn ? true : false; } let inputPage = document.querySelector( `#${CanEleID.changePageInput}` ); inputPage.disabled = true; // 隐藏新 <img> document.querySelector(`#${CanEleID.showImgDiv}`).style.display = "none"; } } }); document .querySelector(`#${CanEleID.previousBtn}`) .addEventListener("click", (evt) => { evt.stopPropagation(); let imgInfo = bzData.imgInfoList[ CURRENT_PAGE > 0 ? --CURRENT_PAGE : (CURRENT_PAGE = MAX_BROWSE_PAGE) ]; updateShowImgTag(imgInfo); let inputPage = document.querySelector(`#${CanEleID.changePageInput}`); let page_ = document.querySelector(`#${CanEleID.pageLabel}`); inputPage.value = CURRENT_PAGE; }); document .querySelector(`#${CanEleID.nextBtn}`) .addEventListener("click", (evt) => { evt.stopPropagation(); let imgInfo = bzData.imgInfoList[ CURRENT_PAGE < PAGE_LOADED ? ++CURRENT_PAGE : (CURRENT_PAGE = PAGE_LOADED !== bzData.page ? CURRENT_PAGE : 0) // 完全加载完前不会 '溢出跳 0' ]; updateShowImgTag(imgInfo); let inputPage = document.querySelector(`#${CanEleID.changePageInput}`); let page_ = document.querySelector(`#${CanEleID.pageLabel}`); inputPage.value = CURRENT_PAGE; if (MAX_BROWSE_PAGE < CURRENT_PAGE) MAX_BROWSE_PAGE = CURRENT_PAGE; }); document .querySelector(`#${CanEleID.downloadBtn}`) .addEventListener("click", (evt) => { evt.stopPropagation(); if ( CURRENT_URL === CURRENT_URL_TYPE.gallery || CURRENT_URL === CURRENT_URL_TYPE.testGallery ) { // 传递本子数据,跳转到封面页面,再启动下载,避免 strict-origin-when-cross-origin console.log(`跳转至:${bzData.imgInfoList[0].imgUrl}`); GM_setValue(`${downloadTabData.dataKey}`, downloadTabData.oriBzData); GM_openInTab(bzData.imgInfoList[0].imgUrl, { active: true }); return; } // 打包 zip if (!IS_DOWNLOADING) { IS_DOWNLOADING = true; document.querySelector(`#${CanEleID.downloadBtn}`).disabled = true; UserCustemRange.page_loaded = PAGE_LOADED; if (UserCustemRange.page_loaded !== bzData.page) { let result = confirm( `当前${UserCustemRange.page_loaded}页,图片未加载完全,是否继续?🤨` ); if (!result) { IS_DOWNLOADING = false; document.querySelector( `#${CanEleID.downloadBtn}` ).disabled = false; return; } } let result = prompt( "选择下载页面区间,请使用 [英文符号 - ] 隔开😇", `0-${UserCustemRange.page_loaded}` ); if (result) { let rangeRegExp = result.match(/^(\d+)-(\d+)$/); if (rangeRegExp) { UserCustemRange.min = Number.parseInt(rangeRegExp[1]); UserCustemRange.max = Number.parseInt(rangeRegExp[2]); // 处理意外输入 if ( !rangeRegExp || 0 > UserCustemRange.min || UserCustemRange.min > UserCustemRange.max || UserCustemRange.max > UserCustemRange.page_loaded ) { alert("无效输入😥"); IS_DOWNLOADING = false; document.querySelector( `#${CanEleID.downloadBtn}` ).disabled = false; return; } } else { alert("无效输入😥"); IS_DOWNLOADING = false; document.querySelector( `#${CanEleID.downloadBtn}` ).disabled = false; return; } downloadZip(bzData, UserCustemRange.min, UserCustemRange.max); } else { IS_DOWNLOADING = false; document.querySelector(`#${CanEleID.downloadBtn}`).disabled = false; } } }); document .querySelector(`#${CanEleID.scaleUpBtn}`) .addEventListener("click", (evt) => { evt.stopPropagation(); let imgInfo = bzData.imgInfoList[CURRENT_PAGE]; imgInfo[keyImageScale] += 0.1; updateShowImgTag(imgInfo); }); document .querySelector(`#${CanEleID.scaleResetBtn}`) .addEventListener("click", (evt) => { evt.stopPropagation(); let imgInfo = bzData.imgInfoList[CURRENT_PAGE]; // 切换 原始尺寸-平铺尺寸 if (imgInfo[keyImageScale] !== 1.0) { imgInfo[keyImageScale] = 1.0; } else { updateImgInfoScale(imgInfo, true); } updateShowImgTag(imgInfo); }); document .querySelector(`#${CanEleID.scaleDownBtn}`) .addEventListener("click", (evt) => { evt.stopPropagation(); let imgInfo = bzData.imgInfoList[CURRENT_PAGE]; imgInfo[keyImageScale] -= 0.1; updateShowImgTag(imgInfo); }); document .querySelector(`#${CanEleID.changePageInput}`) .addEventListener("change", (evt) => { evt.stopPropagation(); if (0 <= evt.target.value && evt.target.value <= bzData.page) { CURRENT_PAGE = evt.target.value; let imgInfo = bzData.imgInfoList[CURRENT_PAGE]; updateShowImgTag(imgInfo); } }); } /** * @desc 更新显示图片 * @param {ImgInfo} imgInfo */ function updateShowImgTag(imgInfo) { let newImg_ = document.querySelector(`#${CanEleID.showImg}`); newImg_.src = imgInfo.imgUrl; newImg_.alt = imgInfo.imgName + imgInfo.imgType; if (imgInfo.imgType !== ".err") { newImg_.width = imgInfo.width * imgInfo[keyImageScale]; newImg_.height = imgInfo.height * imgInfo[keyImageScale]; } else { newImg_.style.removeProperty("width"); newImg_.style.removeProperty("height"); } } /** * @desc 更新图片的缩放,自动平铺 * @param {ImgInfo} imgInfo * @param {boolean} isUseToSm 是否将小图放大,默认:false * @param {HTMLDivElement} showImgDiv 包装 \<img\> 的 \<div\> 元素对象 */ function updateImgInfoScale(imgInfo, isUseToSm = false, showImgDiv = null) { let div_img = showImgDiv ? showImgDiv : document.querySelector(`#${CanEleID.showImgDiv}`); let HScale = imgInfo.height / div_img.offsetHeight; let WScale = imgInfo.width / div_img.offsetWidth; if ( imgInfo.height > div_img.offsetHeight || imgInfo.width > div_img.offsetWidth ) { if (HScale > 1 && HScale > WScale) imgInfo[keyImageScale] = 1 / HScale; if (WScale > 1 && WScale > HScale) imgInfo[keyImageScale] = 1 / WScale; // 误差 imgInfo[keyImageScale] -= 0.003; } else { if (isUseToSm) { if (HScale < 1 && HScale > WScale) imgInfo[keyImageScale] = 1 / HScale; if (WScale < 1 && WScale > HScale) imgInfo[keyImageScale] = 1 / WScale; // 误差 imgInfo[keyImageScale] -= 0.01; } } } // 标识当前页面 const currentUrl = window.location.href; if (currentUrl.match(/https:\/\/imhentai.xxx\/gallery\/\S*/g) !== null) CURRENT_URL = CURRENT_URL_TYPE.gallery; else if (currentUrl.match(/https:\/\/imhentai.xxx\/view\/\S*/g) !== null) CURRENT_URL = CURRENT_URL_TYPE.view; else if (currentUrl.match(/https:\/\/\w*.imhentai.xxx\/\S*/g) !== null) CURRENT_URL = CURRENT_URL_TYPE.imgPage; else if (currentUrl.match(/file:\/\/\/D:\/\S*\/\S*\/py\S*/g) !== null) CURRENT_URL = CURRENT_URL_TYPE.testGallery; else if (currentUrl.match(/https:\/\/www.anna\S*/g) !== null) CURRENT_URL = CURRENT_URL_TYPE.testImgPage; else { CURRENT_URL = CURRENT_URL_TYPE.unknow; return; } // 清除脚本存储数据 if ( CURRENT_URL === CURRENT_URL_TYPE.imgPage || CURRENT_URL === CURRENT_URL_TYPE.testImgPage ) { // 7天自动清除 if (GM_getValue("expire", null) === null) GM_setValue("expire", Date.now()); const expire = GM_getValue("expire"); const clearInterval = Date.parse("07 Jan 1970 00:00:00 GMT"); if (Date.now() - expire >= clearInterval) (() => { // 当所有 imgPage 页面关闭时,才清除数据;同时段浏览多个页面时,数据应该缓存;Be like 共享指针 // 直接关闭浏览器,unload 不会触发;使用变化的 LinkCountSign,保证某个未来时间会清除,依赖用户操作 const dateNow = new Date(); const LinkCountSign = `LC:${dateNow.getDate()}`; GM_setValue(LinkCountSign, GM_getValue(LinkCountSign, 0) + 1); window.addEventListener("unload", (evt) => { GM_setValue(LinkCountSign, GM_getValue(LinkCountSign, 0) - 1); if (GM_getValue(LinkCountSign, 0) <= 0) { let expireData = GM_listValues(); expireData.forEach((data) => GM_deleteValue(data)); } }); })(); } // 脚本~ 启动! initComponents(...initData()); })();