您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Original project: https://github.com/Ocrosoft/PixivPreviewer.
// ==UserScript== // @name Pixiv Previewer L // @namespace https://github.com/LolipopJ/PixivPreviewer // @version 1.1.2-2025/5/31 // @description Original project: https://github.com/Ocrosoft/PixivPreviewer. // @author Ocrosoft, LolipopJ // @license GPL-3.0 // @supportURL https://github.com/LolipopJ/PixivPreviewer // @match *://www.pixiv.net/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM.xmlHttpRequest // @icon https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&size=32&url=https://www.pixiv.net // @icon64 https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&size=64&url=https://www.pixiv.net // @require https://update.gf.qytechs.cn/scripts/515994/1478507/gh_2215_make_GM_xhr_more_parallel_again.js // @require http://code.jquery.com/jquery-3.7.1.min.js // @run-at document-end // ==/UserScript== // src/constants/index.ts var g_version = "1.1.2"; var g_defaultSettings = { enablePreview: true, enableAnimePreview: true, previewDelay: 500, pageCount: 2, favFilter: 500, orderType: 0 /* BY_BOOKMARK_COUNT */, aiFilter: true, aiAssistedFilter: false, hideFavorite: true, hideByTag: false, hideByTagList: "", linkBlank: true, version: g_version }; var g_loadingImage = "https://pp-1252089172.cos.ap-chengdu.myqcloud.com/loading.gif"; var PREVIEW_WRAPPER_BORDER_WIDTH = 2; var PREVIEW_WRAPPER_BORDER_RADIUS = 8; var PREVIEW_WRAPPER_DISTANCE_TO_MOUSE = 20; var PREVIEW_PRELOAD_NUM = 5; var TOOLBAR_ID = "pp-toolbar"; var SORT_BUTTON_ID = "pp-sort"; var SORT_EVENT_NAME = "PIXIV_PREVIEWER_RUN_SORT"; var SORT_NEXT_PAGE_BUTTON_ID = "pp-sort-next-page"; var SORT_NEXT_PAGE_EVENT_NAME = "PIXIV_PREVIEWER_JUMP_TO_NEXT_PAGE"; // src/icons/download.svg var download_default = '<svg t="1742281193586" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"\n p-id="24408" width="10" height="10">\n <path\n d="M1024 896v128H0v-320h128v192h768v-192h128v192zM576 554.688L810.688 320 896 405.312l-384 384-384-384L213.312 320 448 554.688V0h128v554.688z"\n fill="#ffffff" p-id="24409"></path>\n</svg>'; // src/icons/loading.svg var loading_default = '<svg t="1742282291278" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"\n p-id="38665" width="48" height="48">\n <path\n d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3 0.1 19.9-16 36-35.9 36z"\n p-id="38666" fill="#1296db"></path>\n</svg>'; // src/icons/page.svg var page_default = '<svg viewBox="0 0 10 10" width="10" height="10">\n <path\n d="M 8 3 C 8.55228 3 9 3.44772 9 4 L 9 9 C 9 9.55228 8.55228 10 8 10 L 3 10 C 2.44772 10 2 9.55228 2 9 L 6 9 C 7.10457 9 8 8.10457 8 7 L 8 3 Z M 1 1 L 6 1 C 6.55228 1 7 1.44772 7 2 L 7 7 C 7 7.55228 6.55228 8 6 8 L 1 8 C 0.447715 8 0 7.55228 0 7 L 0 2 C 0 1.44772 0.447715 1 1 1 Z"\n fill="#ffffff"></path>\n</svg>'; // src/utils/logger.ts var ILog = class { prefix = "%c Pixiv Preview"; v(...values) { console.log( this.prefix + " [VERBOSE] ", "color:#333 ;background-color: #fff", ...values ); } i(...infos) { console.info( this.prefix + " [INFO] ", "color:#333 ;background-color: #fff;", ...infos ); } w(...warnings) { console.warn( this.prefix + " [WARNING] ", "color:#111 ;background-color:#ffa500;", ...warnings ); } e(...errors) { console.error( this.prefix + " [ERROR] ", "color:#111 ;background-color:#ff0000;", ...errors ); } d(...data) { console.log( this.prefix + " [DATA] ", "color:#333 ;background-color: #fff;", ...data ); } }; var iLog = new ILog(); function DoLog(level = 3 /* Info */, ...msgOrElement) { switch (level) { case 1 /* Error */: iLog.e(...msgOrElement); break; case 2 /* Warning */: iLog.w(...msgOrElement); break; case 3 /* Info */: iLog.i(...msgOrElement); break; case 4 /* Elements */: case 0 /* None */: default: iLog.v(...msgOrElement); } } // src/utils/utils.ts var pause = (ms) => { return new Promise((resolve) => setTimeout(resolve, ms)); }; // src/services/request.ts var xmlHttpRequest = window.GM.xmlHttpRequest; var request = (options) => { const { headers, ...restOptions } = options; return xmlHttpRequest({ responseType: "json", ...restOptions, headers: { referer: "https://www.pixiv.net/", ...headers } }); }; var requestWithRetry = async (options) => { const { retryDelay = 1e4, maxRetryTimes = Infinity, onRetry, ...restOptions } = options; let response; let retryTimes = 0; while (retryTimes < maxRetryTimes) { response = await request(restOptions); if (response.status === 200) { const responseData = response.response; if (!responseData.error) { return response; } } retryTimes += 1; onRetry?.(response, retryTimes); await pause(retryDelay); } throw new Error( `Request for ${restOptions.url} failed: ${response.responseText}` ); }; var request_default = request; // src/services/download.ts var downloadFile = (url, filename, options = {}) => { const { onload, onerror, ...restOptions } = options; request_default({ ...restOptions, url, method: "GET", responseType: "blob", onload: (resp) => { onload?.(resp); const blob = new Blob([resp.response], { // @ts-expect-error: specified in request options type: resp.responseType }); const blobUrl = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = blobUrl; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(blobUrl); }, onerror: (resp) => { onerror?.(resp); iLog.e(`Download ${filename} from ${url} failed: ${resp.responseText}`); } }); }; // src/services/preview.ts var downloadIllust = ({ url, filename, options = {} }) => { downloadFile(url, filename, { ...options, onerror: () => { window.open(url, "__blank"); } }); }; var getIllustPagesRequestUrl = (id) => { return `/ajax/illust/${id}/pages`; }; var getUgoiraMetadataRequestUrl = (id) => { return `/ajax/illust/${id}/ugoira_meta`; }; // src/services/user.ts var getUserIllustrations = async (userId) => { const response = await request_default({ url: `https://www.pixiv.net/ajax/user/${userId}/profile/all?sensitiveFilterMode=userSetting&lang=zh` }); const responseData = response.response.body; const illusts = Object.keys(responseData.illusts).reverse(); const manga = Object.keys(responseData.manga).reverse(); const artworks = [...illusts, ...manga].sort((a, b) => Number(b) - Number(a)); return { illusts, manga, artworks }; }; // src/utils/debounce.ts function debounce(func, delay = 100) { let timeout = null; return function(...args) { if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { func(...args); }, delay); }; } var debounce_default = debounce; // src/utils/event.ts var stopEventPropagation = (event) => { event.stopPropagation(); }; // src/utils/mouse-monitor.ts var MouseMonitor = class { /** 鼠标相对网页的位置 */ mousePos = [0, 0]; /** 鼠标相对视窗的绝对位置 */ mouseAbsPos = [0, 0]; constructor() { document.addEventListener("mousemove", (mouseMoveEvent) => { this.mousePos = [mouseMoveEvent.pageX, mouseMoveEvent.pageY]; this.mouseAbsPos = [mouseMoveEvent.clientX, mouseMoveEvent.clientY]; }); } }; var mouseMonitor = new MouseMonitor(); var mouse_monitor_default = mouseMonitor; // src/utils/ugoira-player.ts function ZipImagePlayer(options) { this.op = options; this._URL = window.URL || window.webkitURL || window.MozURL || window.MSURL; this._Blob = window.Blob || window.WebKitBlob || window.MozBlob || window.MSBlob; this._BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; this._Uint8Array = window.Uint8Array || window.WebKitUint8Array || window.MozUint8Array || window.MSUint8Array; this._DataView = window.DataView || window.WebKitDataView || window.MozDataView || window.MSDataView; this._ArrayBuffer = window.ArrayBuffer || window.WebKitArrayBuffer || window.MozArrayBuffer || window.MSArrayBuffer; this._maxLoadAhead = 0; if (!this._URL) { this._debugLog("No URL support! Will use slower data: URLs."); this._maxLoadAhead = 10; } if (!this._Blob) { this._error("No Blob support"); } if (!this._Uint8Array) { this._error("No Uint8Array support"); } if (!this._DataView) { this._error("No DataView support"); } if (!this._ArrayBuffer) { this._error("No ArrayBuffer support"); } this._isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor") > 0; this._loadingState = 0; this._dead = false; this._context = options.canvas.getContext("2d"); this._files = {}; this._frameCount = this.op.metadata.frames.length; this._debugLog("Frame count: " + this._frameCount); this._frame = 0; this._loadFrame = 0; this._frameImages = []; this._paused = false; this._loadTimer = null; this._startLoad(); if (this.op.autoStart) { this.play(); } else { this._paused = true; } } ZipImagePlayer.prototype = { _trailerBytes: 3e4, _failed: false, _mkerr: function(msg) { const _this = this; return function() { _this._error(msg); }; }, _error: function(msg) { this._failed = true; throw Error("ZipImagePlayer error: " + msg); }, _debugLog: function(msg) { if (this.op.debug) { console.log(msg); } }, _load: function(offset, length, callback) { const _this = this; const xhr = new XMLHttpRequest(); xhr.addEventListener( "load", function() { if (_this._dead) { return; } _this._debugLog( "Load: " + offset + " " + length + " status=" + xhr.status ); if (xhr.status == 200) { _this._debugLog("Range disabled or unsupported, complete load"); offset = 0; length = xhr.response.byteLength; _this._len = length; _this._buf = xhr.response; _this._bytes = new _this._Uint8Array(_this._buf); } else { if (xhr.status != 206) { _this._error("Unexpected HTTP status " + xhr.status); } if (xhr.response.byteLength != length) { _this._error( "Unexpected length " + xhr.response.byteLength + " (expected " + length + ")" ); } _this._bytes.set(new _this._Uint8Array(xhr.response), offset); } if (callback) { callback.apply(_this, [offset, length]); } }, false ); xhr.addEventListener("error", this._mkerr("Fetch failed"), false); xhr.open("GET", this.op.source); xhr.responseType = "arraybuffer"; if (offset != null && length != null) { const end = offset + length; xhr.setRequestHeader("Range", "bytes=" + offset + "-" + (end - 1)); if (this._isSafari) { xhr.setRequestHeader("Cache-control", "no-cache"); xhr.setRequestHeader("If-None-Match", Math.random().toString()); } } xhr.send(); }, _startLoad: function() { const _this = this; if (!this.op.source) { this._loadNextFrame(); return; } $.ajax({ url: this.op.source, type: "HEAD" }).done(function(data, status, xhr) { if (_this._dead) { return; } _this._pHead = 0; _this._pNextHead = 0; _this._pFetch = 0; const len = parseInt(String(xhr.getResponseHeader("Content-Length"))); if (!len) { _this._debugLog("HEAD request failed: invalid file length."); _this._debugLog("Falling back to full file mode."); _this._load(null, null, function(off2, len2) { _this._pTail = 0; _this._pHead = len2; _this._findCentralDirectory(); }); return; } _this._debugLog("Len: " + len); _this._len = len; _this._buf = new _this._ArrayBuffer(len); _this._bytes = new _this._Uint8Array(_this._buf); let off = len - _this._trailerBytes; if (off < 0) { off = 0; } _this._pTail = len; _this._load(off, len - off, function(off2) { _this._pTail = off2; _this._findCentralDirectory(); }); }).fail(this._mkerr("Length fetch failed")); }, _findCentralDirectory: function() { const dv = new this._DataView(this._buf, this._len - 22, 22); if (dv.getUint32(0, true) != 101010256) { this._error("End of Central Directory signature not found"); } const cd_count = dv.getUint16(10, true); const cd_size = dv.getUint32(12, true); const cd_off = dv.getUint32(16, true); if (cd_off < this._pTail) { this._load(cd_off, this._pTail - cd_off, function() { this._pTail = cd_off; this._readCentralDirectory(cd_off, cd_size, cd_count); }); } else { this._readCentralDirectory(cd_off, cd_size, cd_count); } }, _readCentralDirectory: function(offset, size, count) { const dv = new this._DataView(this._buf, offset, size); let p = 0; for (let i = 0; i < count; i++) { if (dv.getUint32(p, true) != 33639248) { this._error("Invalid Central Directory signature"); } const compMethod = dv.getUint16(p + 10, true); const uncompSize = dv.getUint32(p + 24, true); const nameLen = dv.getUint16(p + 28, true); const extraLen = dv.getUint16(p + 30, true); const cmtLen = dv.getUint16(p + 32, true); const off = dv.getUint32(p + 42, true); if (compMethod != 0) { this._error("Unsupported compression method"); } p += 46; const nameView = new this._Uint8Array(this._buf, offset + p, nameLen); let name = ""; for (let j = 0; j < nameLen; j++) { name += String.fromCharCode(nameView[j]); } p += nameLen + extraLen + cmtLen; this._files[name] = { off, len: uncompSize }; } if (this._pHead >= this._pTail) { this._pHead = this._len; $(this).triggerHandler("loadProgress", [this._pHead / this._len]); this._loadNextFrame(); } else { this._loadNextChunk(); this._loadNextChunk(); } }, _loadNextChunk: function() { if (this._pFetch >= this._pTail) { return; } const off = this._pFetch; let len = this.op.chunkSize; if (this._pFetch + len > this._pTail) { len = this._pTail - this._pFetch; } this._pFetch += len; this._load(off, len, function() { if (off == this._pHead) { if (this._pNextHead) { this._pHead = this._pNextHead; this._pNextHead = 0; } else { this._pHead = off + len; } if (this._pHead >= this._pTail) { this._pHead = this._len; } $(this).triggerHandler("loadProgress", [this._pHead / this._len]); if (!this._loadTimer) { this._loadNextFrame(); } } else { this._pNextHead = off + len; } this._loadNextChunk(); }); }, _fileDataStart: function(offset) { const dv = new DataView(this._buf, offset, 30); const nameLen = dv.getUint16(26, true); const extraLen = dv.getUint16(28, true); return offset + 30 + nameLen + extraLen; }, _isFileAvailable: function(name) { const info = this._files[name]; if (!info) { this._error("File " + name + " not found in ZIP"); } if (this._pHead < info.off + 30) { return false; } return this._pHead >= this._fileDataStart(info.off) + info.len; }, _loadNextFrame: function() { if (this._dead) { return; } const frame = this._loadFrame; if (frame >= this._frameCount) { return; } const meta = this.op.metadata.frames[frame]; if (!this.op.source) { this._loadFrame += 1; this._loadImage(frame, meta.file, false); return; } if (!this._isFileAvailable(meta.file)) { return; } this._loadFrame += 1; const off = this._fileDataStart(this._files[meta.file].off); const end = off + this._files[meta.file].len; let url; const mime_type = this.op.metadata.mime_type || "image/png"; if (this._URL) { let slice; if (!this._buf.slice) { slice = new this._ArrayBuffer(this._files[meta.file].len); const view = new this._Uint8Array(slice); view.set(this._bytes.subarray(off, end)); } else { slice = this._buf.slice(off, end); } let blob; try { blob = new this._Blob([slice], { type: mime_type }); } catch (err) { this._debugLog( "Blob constructor failed. Trying BlobBuilder... (" + err.message + ")" ); const bb = new this._BlobBuilder(); bb.append(slice); blob = bb.getBlob(); } url = this._URL.createObjectURL(blob); this._loadImage(frame, url, true); } else { url = "data:" + mime_type + ";base64," + base64ArrayBuffer(this._buf, off, end - off); this._loadImage(frame, url, false); } }, _loadImage: function(frame, url, isBlob) { const _this = this; const image = new Image(); const meta = this.op.metadata.frames[frame]; image.addEventListener("load", function() { _this._debugLog("Loaded " + meta.file + " to frame " + frame); if (isBlob) { _this._URL.revokeObjectURL(url); } if (_this._dead) { return; } _this._frameImages[frame] = image; $(_this).triggerHandler("frameLoaded", frame); if (_this._loadingState == 0) { _this._displayFrame.apply(_this); } if (frame >= _this._frameCount - 1) { _this._setLoadingState(2); _this._buf = null; _this._bytes = null; } else { if (!_this._maxLoadAhead || frame - _this._frame < _this._maxLoadAhead) { _this._loadNextFrame(); } else if (!_this._loadTimer) { _this._loadTimer = setTimeout(function() { _this._loadTimer = null; _this._loadNextFrame(); }, 200); } } }); image.src = url; }, _setLoadingState: function(state) { if (this._loadingState != state) { this._loadingState = state; $(this).triggerHandler("loadingStateChanged", [state]); } }, _displayFrame: function() { if (this._dead) { return; } const _this = this; const meta = this.op.metadata.frames[this._frame]; this._debugLog("Displaying frame: " + this._frame + " " + meta.file); const image = this._frameImages[this._frame]; if (!image) { this._debugLog("Image not available!"); this._setLoadingState(0); return; } if (this._loadingState != 2) { this._setLoadingState(1); } if (this.op.autosize) { if (this._context.canvas.width != image.width || this._context.canvas.height != image.height) { this._context.canvas.width = image.width; this._context.canvas.height = image.height; } } this._context.clearRect(0, 0, this.op.canvas.width, this.op.canvas.height); this._context.drawImage(image, 0, 0); $(this).triggerHandler("frame", this._frame); if (!this._paused) { this._timer = setTimeout(function() { _this._timer = null; _this._nextFrame.apply(_this); }, meta.delay); } }, _nextFrame: function() { if (this._frame >= this._frameCount - 1) { if (this.op.loop) { this._frame = 0; } else { this.pause(); return; } } else { this._frame += 1; } this._displayFrame(); }, play: function() { if (this._dead) { return; } if (this._paused) { $(this).triggerHandler("play", [this._frame]); this._paused = false; this._displayFrame(); } }, pause: function() { if (this._dead) { return; } if (!this._paused) { if (this._timer) { clearTimeout(this._timer); } this._paused = true; $(this).triggerHandler("pause", [this._frame]); } }, rewind: function() { if (this._dead) { return; } this._frame = 0; if (this._timer) { clearTimeout(this._timer); } this._displayFrame(); }, stop: function() { this._debugLog("Stopped!"); this._dead = true; if (this._timer) { clearTimeout(this._timer); } if (this._loadTimer) { clearTimeout(this._loadTimer); } this._frameImages = null; this._buf = null; this._bytes = null; $(this).triggerHandler("stop"); }, getCurrentFrame: function() { return this._frame; }, getLoadedFrames: function() { return this._frameImages.length; }, getFrameCount: function() { return this._frameCount; }, hasError: function() { return this._failed; } }; function base64ArrayBuffer(arrayBuffer, off, byteLength) { let base64 = ""; const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const bytes = new Uint8Array(arrayBuffer); const byteRemainder = byteLength % 3; const mainLength = off + byteLength - byteRemainder; let a, b, c, d; let chunk; for (let i = off; i < mainLength; i = i + 3) { chunk = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2]; a = (chunk & 16515072) >> 18; b = (chunk & 258048) >> 12; c = (chunk & 4032) >> 6; d = chunk & 63; base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; } if (byteRemainder == 1) { chunk = bytes[mainLength]; a = (chunk & 252) >> 2; b = (chunk & 3) << 4; base64 += encodings[a] + encodings[b] + "=="; } else if (byteRemainder == 2) { chunk = bytes[mainLength] << 8 | bytes[mainLength + 1]; a = (chunk & 64512) >> 10; b = (chunk & 1008) >> 4; c = (chunk & 15) << 2; base64 += encodings[a] + encodings[b] + encodings[c] + "="; } return base64; } var ugoira_player_default = ZipImagePlayer; // src/features/preview.ts var isInitialized = false; var loadIllustPreview = (options) => { if (isInitialized) return; const { previewDelay, enableAnimePreview, linkBlank } = options; const mouseHoverDebounceWait = previewDelay / 5; const mouseHoverPreviewWait = previewDelay - mouseHoverDebounceWait; const getIllustMetadata = (target) => { let imgLink = target; while (!imgLink.is("A")) { imgLink = imgLink.parent(); if (!imgLink.length) { return null; } } const illustHref = imgLink.attr("href"); const illustHrefMatch = illustHref?.match(/\/artworks\/(\d+)(#(\d+))?/); if (!illustHrefMatch) { return null; } const illustId = illustHrefMatch[1]; const previewPage = Number(illustHrefMatch[3] ?? 1); const ugoiraSvg = imgLink.children("div:first").find("svg:first"); const illustType = ugoiraSvg.length || imgLink.hasClass("ugoku-illust") ? 2 /* UGOIRA */ : ( // 合并漫画类型作品 IllustType.MANGA 为 IllustType.ILLUST 统一处理 0 /* ILLUST */ ); return { /** 作品 ID */ illustId, /** 作品页码 */ previewPage, /** 作品类型 */ illustType, /** 作品链接 DOM */ illustLinkDom: imgLink }; }; const previewIllust = (() => { const previewedIllust = new PreviewedIllust(); let currentHoveredIllustId = ""; let ajaxRequest = $.ajax(); const getIllustPagesCache = {}; const getUgoiraMetadataCache = {}; return ({ target, illustId, previewPage = 1, illustType }) => { ajaxRequest.abort(); currentHoveredIllustId = illustId; if (illustType === 2 /* UGOIRA */ && !enableAnimePreview) { iLog.i("\u52A8\u56FE\u9884\u89C8\u5DF2\u7981\u7528\uFF0C\u8DF3\u8FC7"); return; } if ([0 /* ILLUST */, 1 /* MANGA */].includes(illustType)) { if (getIllustPagesCache[illustId]) { previewedIllust.setImage({ illustId, illustElement: target, previewPage, ...getIllustPagesCache[illustId] }); return; } ajaxRequest = $.ajax(getIllustPagesRequestUrl(illustId), { method: "GET", success: (data) => { if (data.error) { iLog.e( `An error occurred while requesting preview urls of illust ${illustId}: ${data.message}` ); return; } const urls = data.body.map((item) => item.urls); const regularUrls = urls.map((url) => url.regular); const originalUrls = urls.map((url) => url.original); getIllustPagesCache[illustId] = { regularUrls, originalUrls }; if (currentHoveredIllustId !== illustId) return; previewedIllust.setImage({ illustId, illustElement: target, previewPage, regularUrls, originalUrls }); }, error: (err) => { iLog.e( `An error occurred while requesting preview urls of illust ${illustId}: ${err}` ); } }); } else if (illustType === 2 /* UGOIRA */) { if (getUgoiraMetadataCache[illustId]) { previewedIllust.setUgoira({ illustId, illustElement: target, ...getUgoiraMetadataCache[illustId] }); return; } ajaxRequest = $.ajax(getUgoiraMetadataRequestUrl(illustId), { method: "GET", success: (data) => { if (data.error) { iLog.e( `An error occurred while requesting metadata of ugoira ${illustId}: ${data.message}` ); return; } getUgoiraMetadataCache[illustId] = data.body; if (currentHoveredIllustId !== illustId) return; const { src, originalSrc, mime_type, frames } = data.body; previewedIllust.setUgoira({ illustId, illustElement: target, src, originalSrc, mime_type, frames }); }, error: (err) => { iLog.e( `An error occurred while requesting metadata of ugoira ${illustId}: ${err.responseText}` ); } }); } else { iLog.e("Unknown illust type."); return; } }; })(); const onMouseOverIllust = (target) => { const { illustId, previewPage, illustType, illustLinkDom } = getIllustMetadata(target) || {}; if (illustId === void 0 || illustType === void 0) { return; } const pathname = location.pathname; if (illustId === /^\/artworks\/(\d+)$/.exec(pathname)?.[1]) { return; } if (linkBlank) { illustLinkDom.attr({ target: "_blank", rel: "external" }); illustLinkDom.off("click", stopEventPropagation); illustLinkDom.on("click", stopEventPropagation); } const previewIllustTimeout = setTimeout(() => { previewIllust({ target, illustId, previewPage, illustType }); }, mouseHoverPreviewWait); const onMouseMove = (mouseMoveEvent) => { if (mouseMoveEvent.ctrlKey || mouseMoveEvent.metaKey) { clearTimeout(previewIllustTimeout); target.off("mousemove", onMouseMove); } }; target.on("mousemove", onMouseMove); const onMouseOut = () => { clearTimeout(previewIllustTimeout); target.off("mouseout", onMouseOut); }; target.on("mouseout", onMouseOut); }; const onMouseMoveDocument = (() => { const debouncedOnMouseOverIllust = debounce_default( onMouseOverIllust, mouseHoverDebounceWait ); let prevTarget; return (mouseMoveEvent) => { if (mouseMoveEvent.ctrlKey || mouseMoveEvent.metaKey) { return; } const currentTarget = $( mouseMoveEvent.target ); if (currentTarget.is(prevTarget)) { return; } prevTarget = currentTarget; debouncedOnMouseOverIllust(currentTarget); }; })(); $(document).on("mousemove", onMouseMoveDocument); (function inactiveUnexpectedDoms() { const styleRules = $("<style>").prop("type", "text/css"); styleRules.append(` @keyframes pp-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }`); styleRules.append(` ._layout-thumbnail img + div { pointer-events: none; }`); styleRules.appendTo("head"); })(); isInitialized = true; }; var PreviewedIllust = class { /** 当前正在预览的作品的 ID */ illustId = ""; /** 当前正在预览的作品 DOM 元素 */ illustElement = $(); /** 当前预览的作品是否加载完毕 */ illustLoaded = false; /** 图片的链接 */ regularUrls = []; /** 图片的原图链接 */ originalUrls = []; /** 当前预览图片的页数 */ currentPage = 1; /** 当前预览图片的总页数 */ pageCount = 1; /** 预览图片或动图容器 DOM */ previewWrapperElement = $(); /** 预览容器顶部栏 DOM */ previewWrapperHeader = $(); /** 当前预览的是第几张图片标记 DOM */ pageCountElement = $(); pageCountText = $(); /** 下载原图按钮 DOM */ downloadOriginalElement = $(); /** 预览图片或动图加载状态 DOM */ previewLoadingElement = $(); /** 当前预览的图片或动图 DOM */ previewImageElement = $(); /** 预加载图片的列表 */ #images = []; /** 保存的鼠标位置 */ #prevMousePos = [0, 0]; /** 当前预览图片的实际尺寸 */ #currentIllustSize = [0, 0]; /** 当前预览的动图播放器 */ // @ts-expect-error: ignore type defines #currentUgoiraPlayer; constructor() { this.reset(); } /** 初始化预览组件 */ reset() { this.illustId = ""; this.illustElement = $(); this.illustLoaded = false; this.regularUrls = []; this.originalUrls = []; this.currentPage = 1; this.pageCount = 1; this.previewWrapperElement?.remove(); this.previewWrapperElement = $(document.createElement("div")).attr({ id: "pp-wrapper" }).css({ position: "fixed", "z-index": "999999", border: `${PREVIEW_WRAPPER_BORDER_WIDTH}px solid rgb(0, 150, 250)`, "border-radius": `${PREVIEW_WRAPPER_BORDER_RADIUS}px`, background: "rgba(31, 31, 31, 0.8)", "backdrop-filter": "blur(4px)", "text-align": "center" }).hide().appendTo($("body")); this.previewWrapperHeader = $(document.createElement("div")).attr({ id: "pp-wrapper__header" }).css({ position: "absolute", top: "0px", left: "0px", right: "0px", padding: "5px", display: "flex", gap: "5px", "align-items": "center", "justify-content": "flex-end" }).appendTo(this.previewWrapperElement); this.pageCountText = $(document.createElement("span")).attr({ id: "pp-page-count__text" }).text("1/1"); this.pageCountElement = $(document.createElement("div")).attr({ id: "pp-page-count" }).css({ height: "20px", "border-radius": "10px", color: "white", background: "rgba(0, 0, 0, 0.32)", "font-size": "10px", "line-height": "1", "font-weight": "bold", padding: "3px 6px", cursor: "pointer", display: "flex", "align-items": "center", gap: "4px" }).append(page_default).append(this.pageCountText).hide().prependTo(this.previewWrapperHeader); this.downloadOriginalElement = $(document.createElement("a")).attr({ id: "pp-download-original" }).css({ height: "20px", "border-radius": "10px", color: "white", background: "rgba(0, 0, 0, 0.32)", "font-size": "10px", "line-height": "1", "font-weight": "bold", padding: "3px 6px", cursor: "pointer", display: "flex", "align-items": "center", gap: "4px" }).append(`${download_default}<span>\u539F\u56FE</span>`).hide().prependTo(this.previewWrapperHeader); this.previewLoadingElement = $(loading_default).attr({ id: "pp-loading" }).css({ padding: "12px", animation: "pp-spin 1s linear infinite" }).appendTo(this.previewWrapperElement); this.previewImageElement = $(new Image()).attr({ id: "pp-image" }).css({ "border-radius": `${PREVIEW_WRAPPER_BORDER_RADIUS}px` }).hide().appendTo(this.previewWrapperElement); this.#images.forEach((image) => { if (image) image.src = ""; }); this.#images = []; this.#prevMousePos = [0, 0]; this.#currentIllustSize = [0, 0]; this.#currentUgoiraPlayer?.stop(); this.unbindPreviewImageEvents(); this.unbindUgoiraPreviewEvents(); } //#region 预览图片功能 /** 初始化预览容器,默认显示第一张图片 */ setImage({ illustId, illustElement, previewPage = 1, regularUrls, originalUrls }) { this.reset(); this.initPreviewWrapper(); this.illustId = illustId; this.illustElement = illustElement; this.regularUrls = regularUrls; this.originalUrls = originalUrls; this.currentPage = previewPage; this.pageCount = regularUrls.length; this.preloadImages(); this.bindPreviewImageEvents(); this.updatePreviewImage(); } bindPreviewImageEvents() { this.previewImageElement.on("load", this.onImageLoad); this.previewImageElement.on("click", this.onPreviewImageMouseClick); this.downloadOriginalElement.on("click", this.onDownloadImage); $(document).on("wheel", this.onPreviewImageMouseWheel); $(document).on("keydown", this.onPreviewImageKeyDown); $(document).on("mousemove", this.onMouseMove); window.addEventListener("wheel", this.preventPageZoom, { passive: false }); } unbindPreviewImageEvents() { this.previewImageElement.off(); this.downloadOriginalElement.off(); $(document).off("wheel", this.onPreviewImageMouseWheel); $(document).off("keydown", this.onPreviewImageKeyDown); $(document).off("mousemove", this.onMouseMove); window.removeEventListener("wheel", this.preventPageZoom); } /** 显示 pageIndex 指向的图片 */ updatePreviewImage(page = this.currentPage) { const currentImageUrl = this.regularUrls[page - 1]; this.previewImageElement.attr("src", currentImageUrl); this.pageCountText.text(`${page}/${this.pageCount}`); } onImageLoad = () => { this.illustLoaded = true; this.previewLoadingElement.hide(); this.previewImageElement.show(); this.downloadOriginalElement.show(); if (this.pageCount > 1) { this.pageCountElement.show(); } this.previewImageElement.css({ width: "", height: "" }); this.#currentIllustSize = [ this.previewImageElement.width() ?? 0, this.previewImageElement.height() ?? 0 ]; this.adjustPreviewWrapper({ baseOnMousePos: false }); }; nextPage() { if (this.currentPage < this.pageCount) { this.currentPage += 1; } else { this.currentPage = 1; } this.updatePreviewImage(); this.preloadImages(); } prevPage() { if (this.currentPage > 1) { this.currentPage -= 1; } else { this.currentPage = this.pageCount; } this.updatePreviewImage(); } preloadImages(from = this.currentPage - 1, to = this.currentPage - 1 + PREVIEW_PRELOAD_NUM) { if (!this.#images.length) { this.#images = new Array(this.regularUrls.length); } for (let i = from; i < to && i < this.regularUrls.length; i += 1) { const preloadImage = new Image(); preloadImage.src = this.regularUrls[i]; this.#images[i] = preloadImage; } } onPreviewImageMouseClick = () => { this.nextPage(); }; onPreviewImageMouseWheel = (mouseWheelEvent) => { if (mouseWheelEvent.ctrlKey || mouseWheelEvent.metaKey) { mouseWheelEvent.preventDefault(); if (mouseWheelEvent.originalEvent.deltaY > 0) { this.nextPage(); } else { this.prevPage(); } } }; onPreviewImageKeyDown = (keyDownEvent) => { if (keyDownEvent.ctrlKey || keyDownEvent.metaKey) { keyDownEvent.preventDefault(); switch (keyDownEvent.key) { case "ArrowUp": case "ArrowRight": this.nextPage(); break; case "ArrowDown": case "ArrowLeft": this.prevPage(); break; } } }; onDownloadImage = (onClickEvent) => { onClickEvent.preventDefault(); const currentImageOriginalUrl = this.originalUrls[this.currentPage - 1]; const currentImageFilename = currentImageOriginalUrl.split("/").pop() || "illust.jpg"; downloadIllust({ url: currentImageOriginalUrl, filename: currentImageFilename }); }; //#endregion //#region 预览动图功能 setUgoira({ illustId, illustElement, src, // originalSrc, mime_type, frames }) { this.reset(); this.initPreviewWrapper(); this.illustId = illustId; this.illustElement = illustElement; illustElement.siblings("svg").css({ "pointer-events": "none" }); this.#currentUgoiraPlayer = this.createUgoiraPlayer({ source: src, metadata: { mime_type, frames } }); this.bindUgoiraPreviewEvents(); } createUgoiraPlayer(options) { const canvas = document.createElement("canvas"); const p = new ugoira_player_default({ canvas, chunkSize: 3e5, loop: true, autoStart: true, debug: false, ...options }); p.canvas = canvas; return p; } bindUgoiraPreviewEvents() { $(this.#currentUgoiraPlayer).on("frameLoaded", this.onUgoiraFrameLoaded); $(document).on("mousemove", this.onMouseMove); } unbindUgoiraPreviewEvents() { $(this.#currentUgoiraPlayer).off(); $(document).off("mousemove", this.onMouseMove); } onUgoiraFrameLoaded = (ev, frame) => { if (frame !== 0) { return; } this.illustLoaded = true; this.previewLoadingElement.hide(); const canvas = $(this.#currentUgoiraPlayer.canvas); this.previewImageElement.after(canvas); this.previewImageElement.remove(); this.previewImageElement = canvas; const ugoiraOriginWidth = ev.currentTarget._frameImages[0].width; const ugoiraOriginHeight = ev.currentTarget._frameImages[0].height; this.#currentIllustSize = [ugoiraOriginWidth, ugoiraOriginHeight]; this.previewImageElement.attr({ width: ugoiraOriginWidth, height: ugoiraOriginHeight }); this.adjustPreviewWrapper({ baseOnMousePos: false }); }; //#endregion /** 初始化显示预览容器 */ initPreviewWrapper() { this.previewWrapperElement.show(); this.previewLoadingElement.show(); this.adjustPreviewWrapper({ baseOnMousePos: true }); } /** 阻止页面缩放事件 */ preventPageZoom = (mouseWheelEvent) => { if (mouseWheelEvent.ctrlKey || mouseWheelEvent.metaKey) { mouseWheelEvent.preventDefault(); } }; /** * 根据鼠标移动调整预览容器位置与显隐 * @param mouseMoveEvent */ onMouseMove = (mouseMoveEvent) => { if (mouseMoveEvent.ctrlKey || mouseMoveEvent.metaKey) { return; } const currentElement = $(mouseMoveEvent.target); if (currentElement.is(this.illustElement)) { this.adjustPreviewWrapper({ baseOnMousePos: true }); } else { this.reset(); } }; /** * 调整预览容器的位置与大小 * @param `baseOnMousePos` 是否根据当前鼠标所在位置调整 * @param `illustSize` 作品的实际大小 */ adjustPreviewWrapper({ baseOnMousePos = true } = {}) { const [mousePosX, mousePosY] = baseOnMousePos ? mouse_monitor_default.mouseAbsPos : this.#prevMousePos; this.#prevMousePos = [mousePosX, mousePosY]; const [illustWidth, illustHeight] = this.#currentIllustSize; const screenWidth = document.documentElement.clientWidth; const screenHeight = document.documentElement.clientHeight; const isShowLeft = mousePosX > screenWidth / 2; const isShowTop = mousePosY > screenHeight / 2; const illustRatio = illustWidth / illustHeight; const screenRestWidth = isShowLeft ? mousePosX - PREVIEW_WRAPPER_DISTANCE_TO_MOUSE : screenWidth - mousePosX - PREVIEW_WRAPPER_DISTANCE_TO_MOUSE; const screenRestRatio = screenRestWidth / screenHeight; const isFitToFullHeight = screenRestRatio > illustRatio; let fitToScreenScale = 1; if (this.illustLoaded) { if (isFitToFullHeight) { fitToScreenScale = Number((screenHeight / illustHeight).toFixed(3)); } else { fitToScreenScale = Number((screenRestWidth / illustWidth).toFixed(3)); } } const previewImageFitWidth = Math.floor(illustWidth * fitToScreenScale); const previewImageFitHeight = Math.floor(illustHeight * fitToScreenScale); const previewWrapperElementPos = { left: "", right: "", top: "", bottom: "" }; if (isShowLeft) { previewWrapperElementPos.right = `${screenWidth - mousePosX + PREVIEW_WRAPPER_DISTANCE_TO_MOUSE}px`; } else { previewWrapperElementPos.left = `${mousePosX + PREVIEW_WRAPPER_DISTANCE_TO_MOUSE}px`; } if (this.illustLoaded) { if (isFitToFullHeight) { previewWrapperElementPos.top = "0px"; } else { const screenRestHeight = isShowTop ? mousePosY : screenHeight - mousePosY; if (previewImageFitHeight > screenRestHeight) { if (isShowTop) { previewWrapperElementPos.top = "0px"; } else { previewWrapperElementPos.bottom = "0px"; } } else { if (isShowTop) { previewWrapperElementPos.bottom = `${screenHeight - mousePosY}px`; } else { previewWrapperElementPos.top = `${mousePosY}px`; } } } } else { if (isShowTop) { previewWrapperElementPos.bottom = `${screenHeight - mousePosY}px`; } else { previewWrapperElementPos.top = `${mousePosY}px`; } } this.previewWrapperElement.css(previewWrapperElementPos); this.previewImageElement.css({ width: `${previewImageFitWidth}px`, height: `${previewImageFitHeight}px` }); } }; // src/databases/index.ts var INDEX_DB_NAME = "PIXIV_PREVIEWER_L"; var INDEX_DB_VERSION = 1; var ILLUSTRATION_DETAILS_CACHE_TABLE_KEY = "illustrationDetailsCache"; var ILLUSTRATION_DETAILS_CACHE_TIME = 1e3 * 60 * 60 * 12; var NEW_ILLUSTRATION_NOT_CACHE_TIME = 1e3 * 60 * 60 * 6; var request2 = indexedDB.open(INDEX_DB_NAME, INDEX_DB_VERSION); var db; request2.onupgradeneeded = (event) => { const db2 = event.target.result; db2.createObjectStore(ILLUSTRATION_DETAILS_CACHE_TABLE_KEY, { keyPath: "id" }); }; request2.onsuccess = (event) => { db = event.target.result; console.log("Open IndexedDB successfully:", db); }; request2.onerror = (event) => { iLog.e(`An error occurred while requesting IndexedDB`, event); }; var cacheIllustrationDetails = (illustrations, now = /* @__PURE__ */ new Date()) => { return new Promise(() => { const cachedIllustrationDetailsObjectStore = db.transaction(ILLUSTRATION_DETAILS_CACHE_TABLE_KEY, "readwrite").objectStore(ILLUSTRATION_DETAILS_CACHE_TABLE_KEY); illustrations.forEach((illustration) => { const createDate = new Date(illustration.createDate); if (now.getTime() - createDate.getTime() > NEW_ILLUSTRATION_NOT_CACHE_TIME) { const illustrationDetails = { ...illustration, cacheDate: now }; const addCachedIllustrationDetailsRequest = cachedIllustrationDetailsObjectStore.put(illustrationDetails); addCachedIllustrationDetailsRequest.onerror = (event) => { iLog.e(`An error occurred while caching illustration details`, event); }; } }); }); }; var getCachedIllustrationDetails = (id, now = /* @__PURE__ */ new Date()) => { return new Promise((resolve) => { const cachedIllustrationDetailsObjectStore = db.transaction(ILLUSTRATION_DETAILS_CACHE_TABLE_KEY, "readwrite").objectStore(ILLUSTRATION_DETAILS_CACHE_TABLE_KEY); const getCachedIllustrationDetailsRequest = cachedIllustrationDetailsObjectStore.get(id); getCachedIllustrationDetailsRequest.onsuccess = (event) => { const illustrationDetails = event.target.result; if (illustrationDetails) { const { cacheDate } = illustrationDetails; if (now.getTime() - cacheDate.getTime() <= ILLUSTRATION_DETAILS_CACHE_TIME) { resolve(illustrationDetails); } else { cachedIllustrationDetailsObjectStore.delete(id).onerror = (event2) => { iLog.e( `An error occurred while deleting outdated illustration details`, event2 ); }; } } resolve(void 0); }; getCachedIllustrationDetailsRequest.onerror = (event) => { iLog.e( `An error occurred while getting cached illustration details`, event ); resolve(void 0); }; }); }; // src/i18n/index.ts var Texts = { install_title: "\u6B22\u8FCE\u4F7F\u7528 Pixiv Previewer (LolipopJ Edition) v", upgrade_body: `<div> <p style="line-height: 1.6;"> \u672C\u811A\u672C\u57FA\u4E8E <a style="color: skyblue" href="https://gf.qytechs.cn/zh-CN/scripts/30766-pixiv-previewer" target="_blank" >Pixiv Previewer</a > \u4E8C\u6B21\u5F00\u53D1\uFF0C\u65E8\u5728\u6EE1\u8DB3\u5F00\u53D1\u8005\u81EA\u5DF1\u9700\u8981\u7684\u80FD\u529B\u3002\u5982\u679C\u60A8\u6709\u4E0D\u9519\u7684\u60F3\u6CD5\u6216\u5EFA\u8BAE\uFF0C\u8BF7\u524D\u5F80\u539F\u811A\u672C\u7684 <a style="color: skyblue" href="https://gf.qytechs.cn/zh-CN/scripts/30766-pixiv-previewer/feedback" target="_blank" >Greasy Fork镜像 \u53CD\u9988\u9875\u9762</a >\u6216\u5F00\u542F\u4E00\u4E2A\u65B0\u7684 <a style="color: skyblue" href="https://github.com/Ocrosoft/PixivPreviewer/issues" target="_blank" >Github \u8BAE\u9898</a >\uFF01 </p> </div> `, setting_language: "\u8BED\u8A00", setting_preview: "\u9884\u89C8", setting_animePreview: "\u52A8\u56FE\u9884\u89C8", setting_sort: "\u641C\u7D22\u9875\u81EA\u52A8\u6392\u5E8F", setting_anime: "\u52A8\u56FE\u4E0B\u8F7D\uFF08\u52A8\u56FE\u9884\u89C8\u53CA\u8BE6\u60C5\u9875\u751F\u6548\uFF09", setting_origin: "\u9884\u89C8\u65F6\u4F18\u5148\u663E\u793A\u539F\u56FE\uFF08\u6162\uFF09", setting_previewDelay: "\u5EF6\u8FDF\u663E\u793A\u9884\u89C8\u56FE\uFF08\u6BEB\u79D2\uFF09", setting_previewByKey: "\u4F7F\u7528\u6309\u952E\u63A7\u5236\u9884\u89C8\u56FE\u5C55\u793A\uFF08Ctrl\uFF09", setting_previewByKeyHelp: "\u5F00\u542F\u540E\u9F20\u6807\u79FB\u52A8\u5230\u56FE\u7247\u4E0A\u4E0D\u518D\u5C55\u793A\u9884\u89C8\u56FE\uFF0C\u6309\u4E0BCtrl\u952E\u624D\u5C55\u793A\uFF0C\u540C\u65F6\u201C\u5EF6\u8FDF\u663E\u793A\u9884\u89C8\u201D\u8BBE\u7F6E\u9879\u4E0D\u751F\u6548\u3002", setting_maxPage: "\u6BCF\u6B21\u6392\u5E8F\u65F6\u7EDF\u8BA1\u7684\u6700\u5927\u9875\u6570", setting_hideWork: "\u9690\u85CF\u6536\u85CF\u6570\u5C11\u4E8E\u8BBE\u5B9A\u503C\u7684\u4F5C\u54C1", setting_sortOrderByBookmark: "\u6309\u7167\u6536\u85CF\u6570\u6392\u5E8F\u4F5C\u54C1", setting_hideAiWork: "\u6392\u5E8F\u65F6\u9690\u85CF AI \u751F\u6210\u4F5C\u54C1", setting_hideAiAssistedWork: "\u6392\u5E8F\u65F6\u9690\u85CF AI \u8F85\u52A9\u4F5C\u54C1", setting_hideFav: "\u6392\u5E8F\u65F6\u9690\u85CF\u5DF2\u6536\u85CF\u7684\u4F5C\u54C1", setting_hideFollowed: "\u6392\u5E8F\u65F6\u9690\u85CF\u5DF2\u5173\u6CE8\u753B\u5E08\u4F5C\u54C1", setting_hideByTag: "\u6392\u5E8F\u65F6\u9690\u85CF\u6307\u5B9A\u6807\u7B7E\u7684\u4F5C\u54C1", setting_hideByTagPlaceholder: "\u8F93\u5165\u6807\u7B7E\u540D\uFF0C\u591A\u4E2A\u6807\u7B7E\u7528','\u5206\u9694", setting_clearFollowingCache: "\u6E05\u9664\u7F13\u5B58", setting_clearFollowingCacheHelp: "\u5173\u6CE8\u753B\u5E08\u4FE1\u606F\u4F1A\u5728\u672C\u5730\u4FDD\u5B58\u4E00\u5929\uFF0C\u5982\u679C\u5E0C\u671B\u7ACB\u5373\u66F4\u65B0\uFF0C\u8BF7\u70B9\u51FB\u6E05\u9664\u7F13\u5B58", setting_followingCacheCleared: "\u5DF2\u6E05\u9664\u7F13\u5B58\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u3002", setting_blank: "\u4F7F\u7528\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u4F5C\u54C1\u8BE6\u60C5\u9875", setting_turnPage: "\u4F7F\u7528\u952E\u76D8\u2190\u2192\u8FDB\u884C\u7FFB\u9875\uFF08\u6392\u5E8F\u540E\u7684\u641C\u7D22\u9875\uFF09", setting_save: "\u4FDD\u5B58\u8BBE\u7F6E", setting_reset: "\u91CD\u7F6E\u811A\u672C", setting_resetHint: "\u8FD9\u4F1A\u5220\u9664\u6240\u6709\u8BBE\u7F6E\uFF0C\u76F8\u5F53\u4E8E\u91CD\u65B0\u5B89\u88C5\u811A\u672C\uFF0C\u786E\u5B9A\u8981\u91CD\u7F6E\u5417\uFF1F", setting_novelSort: "\u5C0F\u8BF4\u6392\u5E8F", setting_novelMaxPage: "\u5C0F\u8BF4\u6392\u5E8F\u65F6\u7EDF\u8BA1\u7684\u6700\u5927\u9875\u6570", setting_novelHideWork: "\u9690\u85CF\u6536\u85CF\u6570\u5C11\u4E8E\u8BBE\u5B9A\u503C\u7684\u4F5C\u54C1", setting_novelHideFav: "\u6392\u5E8F\u65F6\u9690\u85CF\u5DF2\u6536\u85CF\u7684\u4F5C\u54C1", sort_noWork: "\u6CA1\u6709\u53EF\u4EE5\u663E\u793A\u7684\u4F5C\u54C1\uFF08\u9690\u85CF\u4E86 %1 \u4E2A\u4F5C\u54C1\uFF09", sort_getWorks: "\u6B63\u5728\u83B7\u53D6\u7B2C %1/%2 \u9875\u4F5C\u54C1", sort_getBookmarkCount: "\u83B7\u53D6\u6536\u85CF\u6570\uFF1A%1/%2", sort_getPublicFollowing: "\u83B7\u53D6\u516C\u5F00\u5173\u6CE8\u753B\u5E08", sort_getPrivateFollowing: "\u83B7\u53D6\u79C1\u6709\u5173\u6CE8\u753B\u5E08", sort_filtering: "\u8FC7\u6EE4%1\u6536\u85CF\u91CF\u4F4E\u4E8E%2\u7684\u4F5C\u54C1", sort_filteringHideFavorite: "\u5DF2\u6536\u85CF\u548C", sort_fullSizeThumb: "\u5168\u5C3A\u5BF8\u7F29\u7565\u56FE\uFF08\u641C\u7D22\u9875\u3001\u7528\u6237\u9875\uFF09", label_sort: "\u6392\u5E8F", label_sorting: "\u6392\u5E8F\u4E2D", label_nextPage: "\u4E0B\u4E00\u9875" }; var i18n_default = Texts; // src/icons/heart.svg var heart_default = '<svg viewBox="0 0 32 32" width="32" height="32">\n <path d="\nM21,5.5 C24.8659932,5.5 28,8.63400675 28,12.5 C28,18.2694439 24.2975093,23.1517313 17.2206059,27.1100183\nC16.4622493,27.5342993 15.5379984,27.5343235 14.779626,27.110148 C7.70250208,23.1517462 4,18.2694529 4,12.5\nC4,8.63400691 7.13400681,5.5 11,5.5 C12.829814,5.5 14.6210123,6.4144028 16,7.8282366\nC17.3789877,6.4144028 19.170186,5.5 21,5.5 Z"></path>\n <path d="M16,11.3317089 C15.0857201,9.28334665 13.0491506,7.5 11,7.5\nC8.23857625,7.5 6,9.73857647 6,12.5 C6,17.4386065 9.2519779,21.7268174 15.7559337,25.3646328\nC15.9076021,25.4494645 16.092439,25.4494644 16.2441073,25.3646326 C22.7480325,21.7268037 26,17.4385986 26,12.5\nC26,9.73857625 23.7614237,7.5 21,7.5 C18.9508494,7.5 16.9142799,9.28334665 16,11.3317089 Z" style="fill: #fafafa;">\n </path>\n</svg>'; // src/icons/heart-filled.svg var heart_filled_default = '<svg viewBox="0 0 32 32" width="32" height="32">\n <path d="\nM21,5.5 C24.8659932,5.5 28,8.63400675 28,12.5 C28,18.2694439 24.2975093,23.1517313 17.2206059,27.1100183\nC16.4622493,27.5342993 15.5379984,27.5343235 14.779626,27.110148 C7.70250208,23.1517462 4,18.2694529 4,12.5\nC4,8.63400691 7.13400681,5.5 11,5.5 C12.829814,5.5 14.6210123,6.4144028 16,7.8282366\nC17.3789877,6.4144028 19.170186,5.5 21,5.5 Z"></path>\n <path d="M16,11.3317089 C15.0857201,9.28334665 13.0491506,7.5 11,7.5\nC8.23857625,7.5 6,9.73857647 6,12.5 C6,17.4386065 9.2519779,21.7268174 15.7559337,25.3646328\nC15.9076021,25.4494645 16.092439,25.4494644 16.2441073,25.3646326 C22.7480325,21.7268037 26,17.4385986 26,12.5\nC26,9.73857625 23.7614237,7.5 21,7.5 C18.9508494,7.5 16.9142799,9.28334665 16,11.3317089 Z" style="fill: #dc2626;">\n </path>\n</svg>'; // src/icons/play.svg var play_default = '<svg viewBox="0 0 24 24"\n style="width: 48px; height: 48px; stroke: none; line-height: 0; font-size: 0px; vertical-align: middle;">\n <circle cx="12" cy="12" r="10" style="fill: rgba(0, 0, 0, 0.32);"></circle>\n <path d="M9,8.74841664 L9,15.2515834 C9,15.8038681 9.44771525,16.2515834 10,16.2515834\nC10.1782928,16.2515834 10.3533435,16.2039156 10.5070201,16.1135176 L16.0347118,12.8619342\nC16.510745,12.5819147 16.6696454,11.969013 16.3896259,11.4929799\nC16.3034179,11.3464262 16.1812655,11.2242738 16.0347118,11.1380658 L10.5070201,7.88648243\nC10.030987,7.60646294 9.41808527,7.76536339 9.13806578,8.24139652\nC9.04766776,8.39507316 9,8.57012386 9,8.74841664 Z" style="fill: rgb(245, 245, 245);"></path>\n</svg>'; // src/utils/promise.ts var execLimitConcurrentPromises = async (promises, limit = 48) => { const results = []; let index = 0; const executeNext = async () => { if (index >= promises.length) return Promise.resolve(); const currentIndex = index++; const result = await promises[currentIndex](); results[currentIndex] = result; return await executeNext(); }; const initialPromises = Array.from( { length: Math.min(limit, promises.length) }, () => executeNext() ); await Promise.all(initialPromises); return results; }; // src/features/sort.ts var USER_ARTWORKS_CACHE_PREFIX = "PIXIV_PREVIEWER_USER_ARTWORKS_"; var USER_TYPE_ARTWORKS_PER_PAGE = 48; var isInitialized2 = false; var loadIllustSort = (options) => { if (isInitialized2) return; const { pageCount: optionPageCount, favFilter: optionFavFilter, orderType = 0 /* BY_BOOKMARK_COUNT */, hideFavorite = false, hideByTag = false, hideByTagList: hideByTagListString, aiFilter = false, aiAssistedFilter = false // csrfToken, } = options; let pageCount = Number(optionPageCount), favFilter = Number(optionFavFilter); if (pageCount <= 0) { pageCount = g_defaultSettings.pageCount; } if (favFilter < 0) { favFilter = g_defaultSettings.favFilter; } const hideByTagList = hideByTagListString.split(",").map((tag) => tag.trim()).filter((tag) => !!tag); if (aiAssistedFilter) { hideByTagList.push("AI-assisted"); } class IllustSorter { type; illustrations; sorting = false; nextSortPage; listElement = $(); progressElement = $(); progressText = $(); sortButtonElement = $(`#${SORT_BUTTON_ID}`); reset({ type }) { try { this.type = type; this.illustrations = []; this.sorting = false; this.nextSortPage = void 0; this.listElement = getIllustrationsListDom(type); this.progressElement?.remove(); this.progressElement = $(document.createElement("div")).attr({ id: "pp-sort-progress" }).css({ width: "100%", display: "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center", gap: "6px" }).append( $(new Image(96, 96)).attr({ id: "sort-progress__loading", src: g_loadingImage }).css({ "border-radius": "50%" }) ).prependTo(this.listElement).hide(); this.progressText = $(document.createElement("div")).attr({ id: "pp-sort-progress__text" }).css({ "text-align": "center", "font-size": "16px", "font-weight": "bold", color: "initial" }).appendTo(this.progressElement); this.sortButtonElement.text(i18n_default.label_sort); } catch (error) { iLog.e(`An error occurred while resetting sorter:`, error); throw new Error(error); } } async sort({ type, api, searchParams }) { this.sorting = true; iLog.i("Start to sort illustrations."); this.sortButtonElement.text(i18n_default.label_sorting); try { let illustrations = []; const startPage = Number(searchParams.get("p") ?? 1); this.nextSortPage = startPage + pageCount; for (let page = startPage; page < startPage + pageCount; page += 1) { searchParams.set("p", String(page)); if ([ 5 /* USER_ARTWORK */, 6 /* USER_ILLUST */, 7 /* USER_MANGA */ ].includes(type)) { searchParams.set("is_first_page", page > 1 ? "0" : "1"); searchParams.delete("ids[]"); const userId = searchParams.get("user_id"); const userIllustrations = await getUserIllustrationsWithCache( userId, { onRequesting: () => this.setProgress(`Getting illustrations of current user...`) } ); const fromIndex = (page - 1) * USER_TYPE_ARTWORKS_PER_PAGE; const toIndex = page * USER_TYPE_ARTWORKS_PER_PAGE; switch (type) { case 5 /* USER_ARTWORK */: userIllustrations.artworks.slice(fromIndex, toIndex).forEach((id) => searchParams.append("ids[]", id)); break; case 6 /* USER_ILLUST */: userIllustrations.illusts.slice(fromIndex, toIndex).forEach((id) => searchParams.append("ids[]", id)); break; case 7 /* USER_MANGA */: userIllustrations.manga.slice(fromIndex, toIndex).forEach((id) => searchParams.append("ids[]", id)); break; } } else if ([8 /* USER_BOOKMARK */].includes(type)) { searchParams.set( "offset", String((page - 1) * USER_TYPE_ARTWORKS_PER_PAGE) ); } this.setProgress(`Getting illustration list of page ${page} ...`); const requestUrl = `${api}?${searchParams}`; const getIllustRes = await requestWithRetry({ url: requestUrl, onRetry: (response, retryTimes) => { iLog.w( `Get illustration list through \`${requestUrl}\` failed:`, response, `${retryTimes} times retrying...` ); this.setProgress( `Retry to get illustration list of page ${page} (${retryTimes} times)...` ); } }); const extractedIllustrations = getIllustrationsFromResponse( type, getIllustRes.response ); illustrations = illustrations.concat(extractedIllustrations); } const getDetailedIllustrationPromises = []; for (let i = 0; i < illustrations.length; i += 1) { getDetailedIllustrationPromises.push(async () => { this.setProgress( `Getting details of ${i + 1}/${illustrations.length} illustration...` ); const illustration = illustrations[i]; const illustrationId = illustration.id; const illustrationDetails = await getIllustrationDetailsWithCache(illustrationId); return { ...illustration, bookmark_user_total: illustrationDetails.bookmark_user_total }; }); } const detailedIllustrations = await execLimitConcurrentPromises( getDetailedIllustrationPromises ); cacheIllustrationDetails(detailedIllustrations); iLog.d("Queried detailed illustrations:", detailedIllustrations); this.setProgress("Filtering illustrations..."); const filteredIllustrations = detailedIllustrations.filter( (illustration) => { if (hideFavorite && illustration.bookmarkData) { return false; } if (aiFilter && illustration.aiType === 2 /* AI */) { return false; } if ((hideByTag || aiAssistedFilter) && hideByTagList.length) { for (const tag of illustration.tags) { if (hideByTagList.includes(tag)) { return false; } } } return illustration.bookmark_user_total >= favFilter; } ); this.setProgress("Sorting filtered illustrations..."); const sortedIllustrations = orderType === 0 /* BY_BOOKMARK_COUNT */ ? filteredIllustrations.sort( (a, b) => b.bookmark_user_total - a.bookmark_user_total ) : filteredIllustrations; iLog.d("Filtered and sorted illustrations:", sortedIllustrations); iLog.i("Sort illustrations successfully."); this.illustrations = sortedIllustrations; this.showIllustrations(); } catch (error) { iLog.e("Sort illustrations failed:", error); } this.hideProgress(); this.sorting = false; this.sortButtonElement.text(i18n_default.label_sort); } setProgress(text) { this.progressText.text(text); this.progressElement.show(); } hideProgress() { this.progressText.text(""); this.progressElement.hide(); } showIllustrations() { const fragment = document.createDocumentFragment(); for (const { aiType, alt, bookmarkData, bookmark_user_total, // createDate, id, illustType, pageCount: pageCount2, profileImageUrl, tags, title, url, userId, userName } of this.illustrations) { const isR18 = tags.includes("R-18"); const isUgoira = illustType === 2 /* UGOIRA */; const isAi = aiType === 2 /* AI */; const isAiAssisted = tags.includes("AI-assisted"); const listItem = document.createElement("li"); const container = document.createElement("div"); container.style = "width: 184px;"; const illustrationAnchor = document.createElement("a"); illustrationAnchor.setAttribute("data-gtm-value", id); illustrationAnchor.setAttribute("data-gtm-user-id", userId); illustrationAnchor.href = `/artworks/${id}`; illustrationAnchor.target = "_blank"; illustrationAnchor.rel = "external"; illustrationAnchor.style = "display: block; position: relative; width: 184px;"; const illustrationImageWrapper = document.createElement("div"); illustrationImageWrapper.style = "position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;"; const illustrationImage = document.createElement("img"); illustrationImage.src = url; illustrationImage.alt = alt; illustrationImage.style = "object-fit: cover; object-position: center center; width: 100%; height: 100%; border-radius: 4px; background-color: rgb(31, 31, 31);"; const ugoriaSvg = document.createElement("div"); ugoriaSvg.style = "position: absolute;"; ugoriaSvg.innerHTML = play_default; const illustrationMeta = document.createElement("div"); illustrationMeta.style = "position: absolute; top: 0px; left: 0px; right: 0px; display: flex; align-items: flex-start; padding: 4px 4px 0; pointer-events: none; font-size: 10px;"; illustrationMeta.innerHTML = ` ${isR18 ? '<div style="padding: 0px 4px; border-radius: 4px; color: rgb(245, 245, 245); background: rgb(255, 64, 96); font-weight: bold; line-height: 16px; user-select: none;">R-18</div>' : ""} ${isAi ? '<div style="padding: 0px 4px; border-radius: 4px; color: rgb(245, 245, 245); background: #1d4ed8; font-weight: bold; line-height: 16px; user-select: none;">AI</div>' : isAiAssisted ? '<div style="padding: 0px 4px; border-radius: 4px; color: rgb(245, 245, 245); background: #6d28d9; font-weight: bold; line-height: 16px; user-select: none;">AI-\u8F85\u52A9</div>' : ""} ${pageCount2 > 1 ? ` <div style="margin-left: auto;"> <div style="display: flex; justify-content: center; align-items: center; height: 20px; min-width: 20px; color: rgb(245, 245, 245); font-weight: bold; padding: 0px 6px; background: rgba(0, 0, 0, 0.32); border-radius: 10px; line-height: 10px;"> ${page_default} <span>${pageCount2}</span> </div> </div>` : ""} `; const illustrationToolbar = document.createElement("div"); illustrationToolbar.style = "position: absolute; top: 154px; left: 0px; right: 0px; display: flex; align-items: center; padding: 0 4px 4px; pointer-events: none; font-size: 12px;"; illustrationToolbar.innerHTML = ` <div style="padding: 0px 4px; border-radius: 4px; color: rgb(245, 245, 245); background: ${bookmark_user_total > 5e4 ? "#9f1239" : bookmark_user_total > 1e4 ? "#dc2626" : bookmark_user_total > 5e3 ? "#1d4ed8" : bookmark_user_total > 1e3 ? "#15803d" : "#475569"}; font-weight: bold; line-height: 16px; user-select: none;">\u2764 ${bookmark_user_total}</div> <div style="margin-left: auto;">${bookmarkData ? heart_filled_default : heart_default}</div> `; const illustrationTitle = document.createElement("div"); illustrationTitle.innerHTML = title; illustrationTitle.style = "margin-top: 4px; max-width: 100%; overflow: hidden; text-decoration: none; text-overflow: ellipsis; white-space: nowrap; line-height: 22px; font-size: 14px; font-weight: bold; color: rgb(245, 245, 245); transition: color 0.2s;"; const illustrationAuthor = document.createElement("a"); illustrationAuthor.setAttribute("data-gtm-value", userId); illustrationAuthor.href = `/users/${userId}`; illustrationAuthor.target = "_blank"; illustrationAuthor.rel = "external"; illustrationAuthor.style = "display: flex; align-items: center; margin-top: 4px;"; illustrationAuthor.innerHTML = ` <img src="${profileImageUrl}" alt="${userName}" style="object-fit: cover; object-position: center top; width: 24px; height: 24px; border-radius: 50%; margin-right: 4px;"> <span style="min-width: 0px; line-height: 22px; font-size: 14px; color: rgb(214, 214, 214); text-decoration: none; text-overflow: ellipsis; white-space: nowrap; overflow: hidden;">${userName}</span> `; illustrationImageWrapper.appendChild(illustrationImage); if (isUgoira) illustrationImageWrapper.appendChild(ugoriaSvg); illustrationAnchor.appendChild(illustrationImageWrapper); illustrationAnchor.appendChild(illustrationMeta); illustrationAnchor.appendChild(illustrationToolbar); illustrationAnchor.appendChild(illustrationTitle); container.appendChild(illustrationAnchor); container.appendChild(illustrationAuthor); listItem.appendChild(container); fragment.appendChild(listItem); } if ([ 3 /* BOOKMARK_NEW */, 4 /* BOOKMARK_NEW_R18 */, 5 /* USER_ARTWORK */, 6 /* USER_ILLUST */, 7 /* USER_MANGA */, 8 /* USER_BOOKMARK */ ].includes(this.type)) { this.listElement.css({ gap: "24px" }); } this.listElement.find("li").remove(); this.listElement.append(fragment); } } const illustSorter = new IllustSorter(); window.addEventListener(SORT_EVENT_NAME, () => { if (illustSorter.sorting) { iLog.w("Current is in sorting progress."); return; } const url = new URL(location.href); const { pathname, searchParams } = url; const { type, api, searchParams: defaultSearchParams } = getSortOptionsFromPathname(pathname); if (type === void 0) { iLog.w("Current page doesn't support sorting illustrations."); return; } const mergedSearchParams = new URLSearchParams(defaultSearchParams); searchParams.forEach((value, key) => { mergedSearchParams.set(key, value); }); illustSorter.reset({ type }); illustSorter.sort({ type, api, searchParams: mergedSearchParams }); }); window.addEventListener(SORT_NEXT_PAGE_EVENT_NAME, () => { const url = new URL(location.href); const { origin, pathname, searchParams } = url; const currentPage = Number(searchParams.get("p") ?? 1); let nextPage = currentPage + 1; if (illustSorter.listElement?.length && illustSorter.nextSortPage) { iLog.i( "Illustrations in current page are sorted, jump to next available page..." ); nextPage = illustSorter.nextSortPage; } searchParams.set("p", String(nextPage)); location.href = `${origin}${pathname}?${searchParams}`; }); isInitialized2 = true; }; function getIllustrationsListDom(type) { let dom; if ([ 0 /* TAG_ARTWORK */, 1 /* TAG_ILLUST */, 2 /* TAG_MANGA */ ].includes(type)) { dom = $("ul.sc-ad8346e6-1.iwHaa-d"); } else if ([ 3 /* BOOKMARK_NEW */, 4 /* BOOKMARK_NEW_R18 */, 5 /* USER_ARTWORK */, 6 /* USER_ILLUST */, 7 /* USER_MANGA */, 8 /* USER_BOOKMARK */ ].includes(type)) { dom = $("ul.sc-7d21cb21-1.jELUak"); } if (dom) { return dom; } else { throw new Error( `Illustrations list DOM not found. Please create a new issue here: ${"https://github.com/LolipopJ/PixivPreviewer/issues"}` ); } } function getSortOptionsFromPathname(pathname) { let type; let api; let defaultSearchParams; let match; if (match = pathname.match(/\/tags\/(.+)\/(artworks|illustrations|manga)$/)) { const tagName = match[1]; const filterType = match[2]; switch (filterType) { case "artworks": type = 0 /* TAG_ARTWORK */; api = `/ajax/search/artworks/${tagName}`; defaultSearchParams = `word=${tagName}&order=date_d&mode=all&p=1&csw=0&s_mode=s_tag_full&type=all&lang=zh`; break; case "illustrations": type = 1 /* TAG_ILLUST */; api = `/ajax/search/illustrations/${tagName}`; defaultSearchParams = `word=${tagName}&order=date_d&mode=all&p=1&csw=0&s_mode=s_tag_full&type=illust_and_ugoira&lang=zh`; break; case "manga": type = 2 /* TAG_MANGA */; api = `/ajax/search/manga/${tagName}`; defaultSearchParams = `word=${tagName}&order=date_d&mode=all&p=1&csw=0&s_mode=s_tag_full&type=manga&lang=zh`; break; } } else if (match = pathname.match(/\/bookmark_new_illust(_r18)?\.php$/)) { const isR18 = !!match[1]; api = "/ajax/follow_latest/illust"; if (isR18) { type = 3 /* BOOKMARK_NEW */; defaultSearchParams = "mode=r18&lang=zh"; } else { type = 4 /* BOOKMARK_NEW_R18 */; defaultSearchParams = "mode=all&lang=zh"; } } else if (match = pathname.match(/\/users\/(\d+)\/bookmarks\/artworks$/)) { const userId = match[1]; type = 8 /* USER_BOOKMARK */; api = `/ajax/user/${userId}/illusts/bookmarks`; defaultSearchParams = `tag=&offset=0&limit=${USER_TYPE_ARTWORKS_PER_PAGE}&rest=show&lang=zh`; } else if (match = pathname.match(/\/users\/(\d+)\/(artworks|illustrations|manga)$/)) { const userId = match[1]; const filterType = match[2]; api = `/ajax/user/${userId}/profile/illusts`; switch (filterType) { case "artworks": type = 5 /* USER_ARTWORK */; defaultSearchParams = `work_category=illustManga&is_first_page=1&sensitiveFilterMode=userSetting&user_id=${userId}&lang=zh`; break; case "illustrations": type = 6 /* USER_ILLUST */; defaultSearchParams = `work_category=illust&is_first_page=1&sensitiveFilterMode=userSetting&user_id=${userId}&lang=zh`; break; case "manga": type = 7 /* USER_MANGA */; defaultSearchParams = `work_category=manga&is_first_page=1&sensitiveFilterMode=userSetting&user_id=${userId}&lang=zh`; break; } } return { type, api, searchParams: new URLSearchParams(defaultSearchParams) }; } function getIllustrationsFromResponse(type, response) { if (type === 0 /* TAG_ARTWORK */) { return response.body.illustManga.data ?? []; } else if (type === 1 /* TAG_ILLUST */) { return response.body.illust.data ?? []; } else if (type === 2 /* TAG_MANGA */) { return response.body.manga.data ?? []; } else if ([3 /* BOOKMARK_NEW */, 4 /* BOOKMARK_NEW_R18 */].includes( type )) { return response.body.thumbnails.illust ?? []; } else if ([ 5 /* USER_ARTWORK */, 6 /* USER_ILLUST */, 7 /* USER_MANGA */, 8 /* USER_BOOKMARK */ ].includes(type)) { return Object.values( response.body.works ); } return []; } async function getUserIllustrationsWithCache(userId, { onRequesting } = {}) { let userIllustrations = { illusts: [], manga: [], artworks: [] }; const userIllustrationsCacheKey = `${USER_ARTWORKS_CACHE_PREFIX}${userId}`; try { const userIllustrationsCacheString = sessionStorage.getItem( userIllustrationsCacheKey ); if (!userIllustrationsCacheString) throw new Error("Illustrations cache not existed."); userIllustrations = JSON.parse(userIllustrationsCacheString); } catch (error) { iLog.i( `Illustrations of current user is not available in session storage, re-getting...`, error ); onRequesting?.(); userIllustrations = await getUserIllustrations(userId); sessionStorage.setItem( userIllustrationsCacheKey, JSON.stringify(userIllustrations) ); } return userIllustrations; } async function getIllustrationDetailsWithCache(id) { let illustDetails = await getCachedIllustrationDetails(id); if (illustDetails) { iLog.d(`Use cached details for illustration ${id}`, illustDetails); } else { const requestUrl = `/touch/ajax/illust/details?illust_id=${id}`; const getIllustDetailsRes = await requestWithRetry({ url: requestUrl, onRetry: (response, retryTimes) => { iLog.w( `Get illustration details through \`${requestUrl}\` failed:`, response, `${retryTimes} times retrying...` ); } }); illustDetails = getIllustDetailsRes.response.body.illust_details; } return illustDetails; } // src/utils/setting.ts var SETTINGS_KEY = "PIXIV_PREVIEWER_L_SETTINGS"; var toggleSettingBooleanValue = (key) => { const settings = getSettings(); const currentValue = Boolean(settings[key] ?? g_defaultSettings[key]); const newValue = !currentValue; GM_setValue(SETTINGS_KEY, { ...settings, [key]: newValue }); }; var setSettingStringValue = (key, label, { parseValue = (v) => v, onSet }) => { const settings = getSettings(); const currentValue = settings[key] ?? g_defaultSettings[key]; const newValue = prompt(label, String(currentValue)); if (newValue !== null) { const savedValue = parseValue(newValue); GM_setValue(SETTINGS_KEY, { ...settings, [key]: savedValue }); onSet?.(savedValue); } }; var setSettingValue = (key, value) => { const settings = getSettings(); const newValue = value ?? g_defaultSettings[key]; GM_setValue(SETTINGS_KEY, { ...settings, [key]: newValue }); }; var getSettings = () => { return GM_getValue(SETTINGS_KEY) ?? g_defaultSettings; }; var resetSettings = () => { GM_setValue(SETTINGS_KEY, g_defaultSettings); }; // src/index.ts var g_csrfToken = ""; var g_pageType; var g_settings; var Pages = { [0 /* Search */]: { PageTypeString: "SearchPage", CheckUrl: function(url) { return /^https?:\/\/www.pixiv.net(\/en)?\/tags\/.+\/(artworks|illustrations|manga)/.test( url ); }, GetToolBar: function() { return findToolbarCommon(); } }, [1 /* BookMarkNew */]: { PageTypeString: "BookMarkNewPage", CheckUrl: function(url) { return /^https:\/\/www.pixiv.net(\/en)?\/bookmark_new_illust(_r18)?.php.*/.test( url ); }, GetToolBar: function() { return findToolbarCommon(); } }, [2 /* Discovery */]: { PageTypeString: "DiscoveryPage", CheckUrl: function(url) { return /^https?:\/\/www.pixiv.net(\/en)?\/discovery.*/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } }, [3 /* Member */]: { PageTypeString: "MemberPage/MemberIllustPage/MemberBookMark", CheckUrl: function(url) { return /^https?:\/\/www.pixiv.net(\/en)?\/users\/\d+/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } }, [4 /* Home */]: { PageTypeString: "HomePage", CheckUrl: function(url) { return /https?:\/\/www.pixiv.net(\/en)?\/?$/.test(url) || /https?:\/\/www.pixiv.net(\/en)?\/illustration\/?$/.test(url) || /https?:\/\/www.pixiv.net(\/en)?\/manga\/?$/.test(url) || /https?:\/\/www.pixiv.net(\/en)?\/cate_r18\.php$/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } }, [5 /* Ranking */]: { PageTypeString: "RankingPage", CheckUrl: function(url) { return /^https?:\/\/www.pixiv.net(\/en)?\/ranking.php.*/.test(url); }, GetToolBar: function() { return findToolbarOld(); } }, [6 /* NewIllust */]: { PageTypeString: "NewIllustPage", CheckUrl: function(url) { return /^https?:\/\/www.pixiv.net(\/en)?\/new_illust.php.*/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } }, [7 /* R18 */]: { PageTypeString: "R18Page", CheckUrl: function(url) { return /^https?:\/\/www.pixiv.net(\/en)?\/cate_r18.php.*/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } }, [8 /* BookMark */]: { PageTypeString: "BookMarkPage", CheckUrl: function(url) { return /^https:\/\/www.pixiv.net(\/en)?\/bookmark.php\/?$/.test(url); }, GetToolBar: function() { return findToolbarOld(); } }, [9 /* Stacc */]: { PageTypeString: "StaccPage", CheckUrl: function(url) { return /^https:\/\/www.pixiv.net(\/en)?\/stacc.*/.test(url); }, GetToolBar: function() { return findToolbarOld(); } }, [10 /* Artwork */]: { PageTypeString: "ArtworkPage", CheckUrl: function(url) { return /^https:\/\/www.pixiv.net(\/en)?\/artworks\/.*/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } }, [11 /* NovelSearch */]: { PageTypeString: "NovelSearchPage", CheckUrl: function(url) { return /^https:\/\/www.pixiv.net(\/en)?\/tags\/.*\/novels/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } }, [12 /* SearchTop */]: { PageTypeString: "SearchTopPage", CheckUrl: function(url) { return /^https?:\/\/www.pixiv.net(\/en)?\/tags\/[^/*]/.test(url); }, GetToolBar: function() { return findToolbarCommon(); } } }; function findToolbarCommon() { const toolbar = $(`#${TOOLBAR_ID}`); if (toolbar.length > 0) { return toolbar.get(0); } $("body").append( `<div id="${TOOLBAR_ID}" style="position: fixed; right: 28px; bottom: 96px;"></div>` ); return $(`#${TOOLBAR_ID}`).get(0); } function findToolbarOld() { return $("._toolmenu").get(0); } function showSearchLinksForDeletedArtworks() { const searchEngines = [ { name: "Google", url: "https://www.google.com/search?q=" }, { name: "Bing", url: "https://www.bing.com/search?q=" }, { name: "Baidu", url: "https://www.baidu.com/s?wd=" } ]; const spans = document.querySelectorAll("span[to]"); spans.forEach((span) => { const artworkPath = span.getAttribute("to"); if (span.textContent.trim() === "-----" && artworkPath.startsWith("/artworks/")) { const keyword = `pixiv "${artworkPath.slice(10)}"`; const container = document.createElement("span"); container.className = span.className; searchEngines.forEach((engine, i) => { const link = document.createElement("a"); link.href = engine.url + encodeURIComponent(keyword); link.textContent = engine.name; link.target = "_blank"; container.appendChild(link); if (i < searchEngines.length - 1) { container.appendChild(document.createTextNode(" | ")); } }); span.parentNode.replaceChild(container, span); } }); } var menuIds = []; var registerSettingsMenu = () => { const settings = getSettings(); for (const menuId of menuIds) { GM_unregisterMenuCommand(menuId); } menuIds = []; menuIds.push( GM_registerMenuCommand( `\u{1F5BC}\uFE0F \u63D2\u753B\u4F5C\u54C1\u9884\u89C8 ${settings.enablePreview ? "\u2705" : "\u274C"}`, () => { toggleSettingBooleanValue("enablePreview"); registerSettingsMenu(); } ), GM_registerMenuCommand( `\u{1F3A6} \u52A8\u56FE\u4F5C\u54C1\u9884\u89C8 ${settings.enableAnimePreview ? "\u2705" : "\u274C"}`, () => { toggleSettingBooleanValue("enableAnimePreview"); registerSettingsMenu(); } ), GM_registerMenuCommand( `\u{1F557} \u5EF6\u8FDF ${settings.previewDelay} \u6BEB\u79D2\u663E\u793A\u9884\u89C8\u56FE`, () => { setSettingStringValue("previewDelay", "\u5EF6\u8FDF\u663E\u793A\u9884\u89C8\u56FE\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09", { parseValue: (newValue) => Number(newValue) || g_defaultSettings.previewDelay, onSet: () => registerSettingsMenu() }); } ), GM_registerMenuCommand(`\u{1F4DA}\uFE0F \u6BCF\u6B21\u6392\u5E8F ${settings.pageCount} \u9875`, () => { setSettingStringValue("pageCount", "\u6BCF\u6B21\u6392\u5E8F\u7684\u9875\u6570", { parseValue: (newValue) => Number(newValue) || g_defaultSettings.pageCount, onSet: () => registerSettingsMenu() }); }), GM_registerMenuCommand( `\u{1F468}\u200D\u{1F469}\u200D\u{1F467} \u6392\u5E8F\u9690\u85CF\u6536\u85CF\u6570\u5C11\u4E8E ${settings.favFilter} \u7684\u4F5C\u54C1`, () => { setSettingStringValue("favFilter", "\u6392\u5E8F\u9690\u85CF\u5C11\u4E8E\u8BBE\u5B9A\u6536\u85CF\u6570\u7684\u4F5C\u54C1", { parseValue: (newValue) => Number(newValue) || g_defaultSettings.favFilter, onSet: () => registerSettingsMenu() }); } ), GM_registerMenuCommand( `\u{1F3A8} \u6309\u7167 ${settings.orderType === 0 /* BY_BOOKMARK_COUNT */ ? "\u4F5C\u54C1\u6536\u85CF\u6570" : "\u4F5C\u54C1\u53D1\u5E03\u65F6\u95F4"} \u6392\u5E8F\u4F5C\u54C1`, () => { setSettingValue( "orderType", settings.orderType === 0 /* BY_BOOKMARK_COUNT */ ? 1 /* BY_DATE */ : 0 /* BY_BOOKMARK_COUNT */ ); registerSettingsMenu(); } ), GM_registerMenuCommand( `\u{1F916} \u6392\u5E8F\u8FC7\u6EE4 AI \u751F\u6210\u4F5C\u54C1 ${settings.aiFilter ? "\u2705" : "\u274C"}`, () => { toggleSettingBooleanValue("aiFilter"); registerSettingsMenu(); } ), GM_registerMenuCommand( `\u{1F9BE} \u6392\u5E8F\u8FC7\u6EE4 AI \u8F85\u52A9\uFF08\u52A0\u7B14\uFF09\u4F5C\u54C1 ${settings.aiAssistedFilter ? "\u2705" : "\u274C"}`, () => { toggleSettingBooleanValue("aiAssistedFilter"); registerSettingsMenu(); } ), GM_registerMenuCommand( `\u2764\uFE0F \u6392\u5E8F\u8FC7\u6EE4\u5DF2\u6536\u85CF\u4F5C\u54C1 ${settings.hideFavorite ? "\u2705" : "\u274C"}`, () => { toggleSettingBooleanValue("hideFavorite"); registerSettingsMenu(); } ), GM_registerMenuCommand( `\u{1F516} \u6392\u5E8F\u8FC7\u6EE4\u5305\u542B\u6307\u5B9A\u6807\u7B7E\u7684\u4F5C\u54C1 ${settings.hideByTag ? "\u2705" : "\u274C"}`, () => { toggleSettingBooleanValue("hideByTag"); registerSettingsMenu(); } ), GM_registerMenuCommand( `\u{1F516} \u6392\u5E8F\u8FC7\u6EE4\u7684\u6807\u7B7E\uFF1A${settings.hideByTagList}`, () => { setSettingStringValue( "hideByTagList", "\u8FC7\u6EE4\u7684\u6807\u7B7E\u5217\u8868\uFF0C\u4F7F\u7528`,`\u5206\u9694\u4E0D\u540C\u6807\u7B7E", { onSet: () => registerSettingsMenu() } ); } ), GM_registerMenuCommand( `\u{1F4D1} \u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u4F5C\u54C1 ${settings.linkBlank ? "\u2705" : "\u274C"}`, () => { toggleSettingBooleanValue("linkBlank"); registerSettingsMenu(); } ), GM_registerMenuCommand(`\u{1F501} \u91CD\u7F6E\u8BBE\u7F6E`, () => { if (confirm("\u60A8\u786E\u5B9A\u8981\u91CD\u7F6E\u6240\u6709\u8BBE\u7F6E\u5230\u811A\u672C\u7684\u9ED8\u8BA4\u503C\u5417\uFF1F")) { resetSettings(); location.reload(); } }) ); return settings; }; var ShowUpgradeMessage = () => { $("#pp-bg").remove(); const bg = $('<div id="pp-bg"></div>').css({ position: "fixed", "z-index": 9999, "background-color": "rgba(0, 0, 0, 0.8)", inset: "0px" }); $("body").append(bg); bg.get(0).innerHTML = '<img id="pps-close" src="https://pp-1252089172.cos.ap-chengdu.myqcloud.com/Close.png"style="position: absolute; right: 35px; top: 20px; width: 32px; height: 32px; cursor: pointer;"><div style="position: absolute; width: 40%; left: 30%; top: 25%; font-size: 25px; font-weight: bold; text-align: center; color: white;">' + i18n_default.install_title + g_version + '</div><br><div style="position: absolute; left: 50%; top: 35%; font-size: 20px; color: white; transform: translate(-50%,0); height: 50%; overflow: auto;">' + i18n_default.upgrade_body + "</div>"; $("#pps-close").on("click", () => { setSettingValue("version", g_version); $("#pp-bg").remove(); }); }; var initializePixivPreviewer = () => { try { g_settings = registerSettingsMenu(); iLog.i( "Start to initialize Pixiv Previewer with global settings:", g_settings ); if (g_settings.version !== g_version) { ShowUpgradeMessage(); } if (g_settings.enablePreview) { loadIllustPreview(g_settings); } $.get(location.href, function(data) { const matched = data.match(/token\\":\\"([a-z0-9]{32})/); if (matched.length > 0) { g_csrfToken = matched[1]; DoLog(3 /* Info */, "Got g_csrfToken: " + g_csrfToken); loadIllustSort({ ...g_settings, csrfToken: g_csrfToken }); } else { DoLog( 1 /* Error */, "Can not get g_csrfToken, sort function is disabled." ); } }); for (let i = 0; i < Object.keys(Pages).length; i++) { if (Pages[i].CheckUrl(location.href)) { g_pageType = i; break; } } if (g_pageType !== void 0) { DoLog( 3 /* Info */, "Current page is " + Pages[g_pageType].PageTypeString ); } else { DoLog(3 /* Info */, "Unsupported page."); return; } const toolBar = Pages[g_pageType].GetToolBar(); if (toolBar) { DoLog(4 /* Elements */, toolBar); } else { DoLog(2 /* Warning */, "Get toolbar failed."); return; } if (!$(`#${SORT_BUTTON_ID}`).length) { const newListItem = document.createElement("div"); newListItem.title = "Sort artworks"; newListItem.innerHTML = ""; const newButton = document.createElement("button"); newButton.id = SORT_BUTTON_ID; newButton.style.cssText = "box-sizing: border-box; background-color: rgba(0,0,0,0.32); color: #fff; margin-top: 5px; opacity: 0.8; cursor: pointer; border: none; padding: 0px; border-radius: 24px; width: 48px; height: 48px; font-size: 12px; font-weight: bold;"; newButton.innerHTML = i18n_default.label_sort; newListItem.appendChild(newButton); toolBar.appendChild(newListItem); $(newButton).on("click", () => { const sortEvent = new Event(SORT_EVENT_NAME); window.dispatchEvent(sortEvent); }); } if (!$(`#${SORT_NEXT_PAGE_BUTTON_ID}`).length) { const newListItem = document.createElement("div"); newListItem.title = "Jump to next page"; newListItem.innerHTML = ""; const newButton = document.createElement("button"); newButton.id = SORT_NEXT_PAGE_BUTTON_ID; newButton.style.cssText = "box-sizing: border-box; background-color: rgba(0,0,0,0.32); color: #fff; margin-top: 5px; opacity: 0.8; cursor: pointer; border: none; padding: 0px; border-radius: 24px; width: 48px; height: 48px; font-size: 12px; font-weight: bold;"; newButton.innerHTML = i18n_default.label_nextPage; newListItem.appendChild(newButton); toolBar.appendChild(newListItem); $(newButton).on("click", () => { const sortEvent = new Event(SORT_NEXT_PAGE_EVENT_NAME); window.dispatchEvent(sortEvent); }); } if (g_pageType === 3 /* Member */) { showSearchLinksForDeletedArtworks(); } } catch (e) { DoLog(1 /* Error */, "An error occurred while initializing:", e); } }; window.addEventListener("DOMContentLoaded", () => { setTimeout(initializePixivPreviewer, 1e3); });
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址