- // ==UserScript==
- // @name 喵绅士(nyahentai)
- // @namespace https://github.com/dffxd-suntra/nyahentai-plus
- // @version 3.0
- // @description 正式可用,让新版喵绅士有长条预览功能
- // @homepageURL https://github.com/dffxd-suntra/nyahentai-plus
- // @supportURL https://github.com/dffxd-suntra/nyahentai-plus
- // @match *://nyahentai.red/*
- // @match *://nhentai.xxx/*
- // @match *://nhentai.net/*
- // @icon https://nyahentai.red/front/favicon.ico
- // @require https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js
- // @require https://cdn.jsdelivr.net/npm/lazysizes@5.3.2/lazysizes.min.js
- // @require https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js
- // @require https://cdn.jsdelivr.net/npm/keyboardjs@2.7.0/dist/keyboard.min.js
- // @author Suntra
- // @grant GM_getValue
- // @grant GM_setValue
- // @license MIT
- // ==/UserScript==
-
- (function () {
- let loadingImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE7WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIzLTA1LTMwVDIyOjMzOjE0KzA4OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIzLTA1LTMwVDIyOjMzOjE0KzA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMy0wNS0zMFQyMjozMzoxNCswODowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTlmYWFhY2MtZGQ5Zi0yMDRlLTk5MGQtMWZiNzFiYjhhYThhIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjE5ZmFhYWNjLWRkOWYtMjA0ZS05OTBkLTFmYjcxYmI4YWE4YSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjE5ZmFhYWNjLWRkOWYtMjA0ZS05OTBkLTFmYjcxYmI4YWE4YSIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMSI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MTlmYWFhY2MtZGQ5Zi0yMDRlLTk5MGQtMWZiNzFiYjhhYThhIiBzdEV2dDp3aGVuPSIyMDIzLTA1LTMwVDIyOjMzOjE0KzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoV2luZG93cykiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+ejvILQAAAApJREFUCJljKAcAAHkAeO/tISkAAAAASUVORK5CYII=";
- let imgWidth = GM_getValue("imgWidth", 60);
- let scrolling = false;
- let preUrl = null;
- // 讲htmlStr加载为document
- async function loadHtml(url) {
- const response = await axios(url);
- if (response.data == "" || response.status != 200) {
- console.error(response);
- throw new Error("网页获取错误");
- }
- return new window.DOMParser().parseFromString(response.data, "text/html");
- }
- // 根据图片阅览页面获取链接,这是备用方法,图片加载错误触发
- async function getPicByPage(url) {
- let detailDocument = await loadHtml(url);
- return $("#image-container > a > img", detailDocument).attr("src");
- }
- // 开始阅览
- async function startView(url) {
- if (preUrl != url) {
- $("#nyap-read-page-img").html("");
- }
- if (!url.endsWith("/")) {
- url += "/";
- }
- preUrl = url;
- let detailDocument = await loadHtml(url);
- let pages = parseInt($("#tags", detailDocument).children(":contains('Pages')").find(".tag").text());
- let tempUrl = $("#cover > a > img", detailDocument).attr("src").split("/").slice(0, -1).join("/") + "/";
- // 根据封面的图片后缀预测正文后缀
- let picSuffix = $("#cover > a > img", detailDocument).attr("src").split(".").pop();
- console.log(`pages: ${pages}\ntempUrl: ${tempUrl}\npicSuffix: ${picSuffix}`);
-
- // 一下都添加,但是有lazysize, 懒加载让页面不卡
- for (let i = 1; i <= pages; i++) {
- $("#nyap-read-page-img").append(
- $("<span>")
- .text(`${i}/${pages} page`)
- .css({
- color: "gray",
- position: "absolute",
- left: 0
- }),
- $("<img>")
- .attr("src", loadingImg)
- .attr("data-src", `${tempUrl + i}.${picSuffix}`)
- .addClass("lazyload")
- .css({
- width: "100%",
- padding: 0,
- margin: 0,
- display: "block"
- })
- // 出错是图片后缀出问题了,将图片浏览页面打开获取链接
- .on("error", async function (event) {
- $(this).attr("src", loadingImg);
- $(this).attr("src", await getPicByPage(`${url}${i}/`));
- })
- );
- }
- }
- // 记忆化改变宽度
- function changeWidth(x) {
- imgWidth = Math.max(1, imgWidth + x);
- GM_setValue("imgWidth", imgWidth);
- // 以屏幕中线缩放,避免改变阅览进度
- let readProgress = ($("#nyap-read-page").scrollTop() + $(window).height() / 2) / $("#nyap-read-page").prop("scrollHeight");
- $("#nyap-read-page-img").css("width", imgWidth + "%");
- $("#showWidth").text(imgWidth + "%");
- $("#nyap-read-page").scrollTop(readProgress * $("#nyap-read-page").prop("scrollHeight") - $(window).height() / 2);
- }
- // 滚动函数,用 requestAnimationFrame, 不会卡顿
- // 因为 requestAnimationFrame 一般是一帧运行一次
- function startScroll(ms) {
- scrolling = true;
- let previousTimeStamp;
- let sum = 0;
- function step(time) {
- if (previousTimeStamp != undefined) {
- sum += ($(window).height() / ms) * (time - previousTimeStamp);
- $("#nyap-read-page").scrollTop($("#nyap-read-page").scrollTop() + sum);
- // 页面的滚动是以像素为单位(int),所以小于1像素要等下一次一起滚动
- sum %= 1;
- }
- if (scrolling) {
- previousTimeStamp = time;
- // 进行下一次的滚动
- window.requestAnimationFrame(step);
- }
- }
- window.requestAnimationFrame(step);
- $("#nyap-read-page-scroll").text("结束");
- }
- function endScroll() {
- scrolling = false;
- $("#nyap-read-page-scroll").text("滚动");
- }
- // 页面样式
- $("body").prepend(`
- <div style="position: fixed;top: 0;left: 0;right: 0;bottom: 0;z-index: 114514;display: none;background: rgba(0, 0, 0, 95%);overflow: auto;-webkit-user-select: none;user-select: none;" id="nyap-read-page">
- <div style="position: fixed;bottom: 10px;right: 10px;">
- <h1 id="showWidth" style="text-shadow: 0px 0px 4px black;">${imgWidth}%</h1>
- <div id="nyap-read-page-img-change-width">
- <button type="button" class="btn btn-primary">+1</button><br>
- <button type="button" class="btn btn-primary">+5</button><br>
- <button type="button" class="btn btn-primary">+10</button><br>
- <button type="button" class="btn btn-primary">-10</button><br>
- <button type="button" class="btn btn-primary">-5</button><br>
- <button type="button" class="btn btn-primary">-1</button><br>
- </div>
- <input type="text" id="nyap-read-page-scroll-speed" class="btn btn-secondary" title="几毫秒滚完一个屏幕" style="width: 5em;" /><br>
- <button type="button" id="nyap-read-page-scroll" class="btn btn-primary">滚动</button><br>
- <button type="button" id="nyap-read-page-hide" class="btn btn-primary">关闭</button><br>
- <button type="button" id="nyap-read-page-toTop" class="btn btn-primary">顶部</button>
- </div>
- <center>
- <div id="nyap-read-page-img" style="width: ${imgWidth}%"></div>
- </center>
- </div>
- `);
- // 检测详情界面
- if (/^\/g\/.+\/?$/.test(location.pathname)) {
- $("#info > div").prepend($(`<button class="btn btn-primary" id="nyap-read-page-show">垂直阅读</button>`).data("page-link", location.href));
- }
- // 添加快捷阅读标志
- $(".gallery > .cover").each(function (index, node) {
- $(node).append(
- $(`<a id="nyap-read-page-show" style="position: absolute;right: 0;bottom: 0;"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 16 16"><path d="M1 2.828c.885-.37 2.154-.769 3.388-.893 1.33-.134 2.458.063 3.112.752v9.746c-.935-.53-2.12-.603-3.213-.493-1.18.12-2.37.461-3.287.811V2.828zm7.5-.141c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z"/></svg></a>`).data("page-link", $(node).attr("href"))
- );
- });
- // 记忆化
- $("#nyap-read-page-scroll-speed").val(GM_getValue("scrollSpeed", 8000));
- // 切换开,关
- $("*#nyap-read-page-show").click(function (event) {
- event.preventDefault();
- keyboardJS.setContext("view");
- $("#nyap-read-page").scrollTop(0);
- $("body").css("overflow", "hidden");
- $("#nyap-read-page").show();
- startView($(this).data("page-link"));
- });
- $("#nyap-read-page-hide").click(function () {
- keyboardJS.setContext("index");
- $("body").css("overflow", "");
- $("#nyap-read-page").hide();
- endScroll();
- });
- // totop
- $("#nyap-read-page-toTop").click(function () {
- $("#nyap-read-page").scrollTop(0);
- });
- // 切换宽度
- $("#nyap-read-page-img-change-width").click(function ({ target }) {
- if ($(target).attr("id") == "nyap-read-page-img-change-width") {
- return;
- }
- changeWidth(parseInt($(target).text()));
- });
- // 设置滚动
- $("#nyap-read-page-scroll").click(function () {
- if (scrolling) {
- endScroll();
- } else {
- let ms = parseInt($("#nyap-read-page-scroll-speed").val());
- if (!ms) {
- return;
- }
- startScroll(ms);
- }
- });
- $("#nyap-read-page-scroll-speed").on("input", function () {
- GM_setValue("scrollSpeed", $(this).val());
- });
- // 快捷键
- keyboardJS.withContext("index", function () {
- keyboardJS.bind("left", function (e) {
- let url = new URL(location.href);
- let page = Math.max(parseInt(url.searchParams.get("page")), 1) || 1;
- if (page == 1) {
- return;
- }
- page--;
- url.searchParams.set("page", page);
- location.href = url.href;
- });
- keyboardJS.bind("right", function (e) {
- let url = new URL(location.href);
- let page = Math.max(parseInt(url.searchParams.get("page")), 1) || 1;
- page++;
- url.searchParams.set("page", page);
- location.href = url.href;
- });
- });
- keyboardJS.withContext("view", function () {
- keyboardJS.bind("esc", function (e) {
- if (scrolling) {
- endScroll();
- return;
- }
- $("#nyap-read-page-hide").click();
- });
- });
- keyboardJS.setContext("index");
- })();