u9a9-预览

u9a9·列表预览图片

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         u9a9-预览
// @version      0.1.0
// @namespace    https://sleazyfork.org/zh-CN/users/1461640-%E6%98%9F%E5%AE%BF%E8%80%81%E9%AD%94
// @author       星宿老魔
// @description  u9a9·列表预览图片
// @match        https://u9a9.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=u9a9.com
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// ==/UserScript==

(function(){"use strict";const e=200,t="1px solid #ddd",n="4px",s="contain",i="pointer",r="transform 0.2s ease",CONFIG={MAX_PREVIEW_IMAGES:5,
PREVIEW_IMAGE_HEIGHT:e,getPreviewImageStyle:(o=1)=>{const a=o<=3?"auto":Math.floor(100/o)-1+"%"
;return`height: ${e}px; width: ${a}; object-fit: ${s}; cursor: ${i}; border: ${t}; border-radius: ${n}; flex-shrink: 0; transition: ${r};`},
selectors:{listTable:"table.torrent-list",listRows:"table.torrent-list tbody tr.default",titleLinks:"td:nth-child(2) a",
imgContainer:"div.img-container",images:"div.img-container img"},regex:{viewUrl:/^\/view\/\d+\/[a-f0-9]+$/,imageUrl:/\.(jpg|jpeg|png|gif|webp)$/i},
styles:{previewImages:"display: flex; gap: 4px; flex-wrap: nowrap; overflow: hidden; width: 100%; padding: 4px; margin: 0;",
previewRow:"\n      .u9a9-preview-row {\n        background-color: #f8f9fa !important;\n      }\n      .u9a9-preview-row td {\n        padding: 10px !important;\n        text-align: center !important;\n      }\n      .u9a9-preview-row img {\n        border-radius: 4px;\n        transition: transform 0.2s ease;\n      }\n      .u9a9-preview-row img:hover {\n        transform: scale(1.05);\n      }\n    "
}},o=class{static info(...e){this.enabled,0}static error(...e){this.enabled,0}};o.enabled=!1,o.load=(...e)=>{},o.loadError=()=>{};let a=o
;function debounce(e,t){let n;return(...s)=>{clearTimeout(n),n=setTimeout(()=>e(...s),t)}}function l(e){return CONFIG.regex.imageUrl.test(e)}
function c(e){return e.startsWith("//")?`https:${e}`:e.startsWith("/")?`${window.location.origin}${e}`:e}function h(e,...t){a.info(e,...t)}
const d=class{static init(){this.overlay||(this.overlay=document.createElement("div"),
this.overlay.style.cssText="\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      background: rgba(0, 0, 0, 0.95);\n      z-index: 999999;\n      display: none;\n      align-items: center;\n      justify-content: center;\n    ",
this.img=document.createElement("img"),
this.img.style.cssText="\n      width: 80vw;\n      height: 80vh;\n      max-width: 90%;\n      max-height: 90%;\n      object-fit: contain;\n      border-radius: 4px;\n    ",
this.counter=document.createElement("div"),
this.counter.style.cssText="\n      position: absolute;\n      top: 20px;\n      left: 50%;\n      transform: translateX(-50%);\n      color: white;\n      background: rgba(0, 0, 0, 0.6);\n      padding: 8px 16px;\n      border-radius: 20px;\n      font-size: 14px;\n    ",
this.prevBtn=this.createNavButton("‹","left"),this.nextBtn=this.createNavButton("›","right"),this.closeBtn=this.createCloseButton(),
this.overlay.appendChild(this.img),this.overlay.appendChild(this.counter),this.overlay.appendChild(this.prevBtn),
this.overlay.appendChild(this.nextBtn),this.overlay.appendChild(this.closeBtn),document.body.appendChild(this.overlay),this.setupEvents())}
static createNavButton(e,t){const n=document.createElement("button")
;return n.innerHTML="‹"===e?'<svg viewBox="0 0 24 24" fill="white" width="50" height="50"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>':'<svg viewBox="0 0 24 24" fill="white" width="50" height="50"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>',
n.style.cssText=`\n      position: fixed;\n      ${t}: 16px;\n      top: 50%;\n      transform: translateY(-50%);\n      width: 60px;\n      height: 60px;\n      background: rgba(255, 255, 255, 0.2);\n      border-radius: 50%;\n      border: none;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      color: white;\n      cursor: pointer;\n      user-select: none;\n      z-index: 10002;\n    `,
n}static createCloseButton(){const e=document.createElement("button")
;return e.innerHTML='<svg viewBox="0 0 24 24" fill="white" width="30" height="30"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>',
e.style.cssText="\n      position: fixed;\n      right: 20px;\n      top: 20px;\n      width: 50px;\n      height: 50px;\n      background: rgba(255, 255, 255, 0.2);\n      border-radius: 50%;\n      border: none;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      cursor: pointer;\n      user-select: none;\n      z-index: 10002;\n      transition: background 0.2s;\n    ",
e.onmouseover=()=>{e.style.background="rgba(255, 255, 255, 0.3)"},e.onmouseout=()=>{e.style.background="rgba(255, 255, 255, 0.2)"},e}
static setupEvents(){this.overlay.onclick=()=>{this.close()},this.prevBtn.onclick=e=>{e.stopPropagation(),this.prev()},this.nextBtn.onclick=e=>{
e.stopPropagation(),this.next()},this.closeBtn.onclick=e=>{e.stopPropagation(),this.close()},document.addEventListener("keydown",e=>{
"flex"===this.overlay?.style.display&&("Escape"===e.key?this.close():"ArrowLeft"===e.key?this.prev():"ArrowRight"===e.key&&this.next())})}
static show(e,t=0){this.init(),this.images=e,this.currentIndex=t,this.updateImage(),this.overlay.style.display="flex"}static close(){
this.overlay&&(this.overlay.style.display="none")}static prev(){this.currentIndex=(this.currentIndex-1+this.images.length)%this.images.length,
this.updateImage()}static next(){this.currentIndex=(this.currentIndex+1)%this.images.length,this.updateImage()}static updateImage(){
const e=this.images[this.currentIndex];this.img.style.display="none",this.img.src="",
this.counter.textContent=`${this.currentIndex+1} / ${this.images.length}`,this.images.length<=1?(this.prevBtn.style.display="none",
this.nextBtn.style.display="none",this.counter.style.display="none"):(this.prevBtn.style.display="flex",this.nextBtn.style.display="flex",
this.counter.style.display="block"),this.img.onload=()=>{this.img.style.display="block"},this.img.onerror=()=>{this.img.alt="图片加载失败"},this.img.src=e}}
;d.overlay=null,d.img=null,d.counter=null,d.prevBtn=null,d.nextBtn=null,d.closeBtn=null,d.images=[],d.currentIndex=0;let u=d
;const m=class _SimpleCache{constructor(){this.cache=new Map}static getInstance(){return this.instance||(this.instance=new _SimpleCache),this.instance
}get(e){return this.cache.get(e)||null}set(e,t){this.cache.set(e,t)}delete(e){return this.cache.delete(e)}clear(){this.cache.clear()}static get(e){
return _SimpleCache.getInstance().get(e)}static set(e,t){_SimpleCache.getInstance().set(e,t)}};m.instance=null;let g=m;class PreviewImageLoader{
constructor(){this.loading=new Set}async processAllRows(){const e=document.querySelectorAll("table.torrent-list tbody tr.default")
;if(!e||0===e.length)return a.info("预览脚本:未在本页找到任何帖子行"),void 0;a.info(`找到 ${e.length} 行,开始加载预览图`);const t=Array.from(e).map(e=>this.processRow(e))
;await Promise.all(t)}async processRow(e){const t=e.querySelector("td:nth-child(2) a");if(!t)return;const n=t.href
;if("true"===e.dataset.previewProcessed)return;e.dataset.previewProcessed="true";const s=`images_${n}`,i=g.get(s)
;if(i)return this.insertPreviewRow(e,i,n),void 0;if(!this.loading.has(n)){this.loading.add(n);try{const t=await this.fetchImages(n);g.set(s,t),
this.insertPreviewRow(e,t,n)}catch(r){a.error(`处理 ${n} 时发生错误:`,r)}finally{this.loading.delete(n)}}}async fetchImages(e){const t=await fetch(e)
;if(!t.ok)throw new Error(`HTTP 错误!状态: ${t.status}`)
;const n=await t.text(),s=(new DOMParser).parseFromString(n,"text/html").querySelector("div.img-container");if(!s)return[]
;const i=Array.from(s.querySelectorAll("img")).map(e=>c(e.src)).filter(e=>e&&l(e)).slice(0,CONFIG.MAX_PREVIEW_IMAGES)
;return a.info(`从 ${e} 提取到 ${i.length} 张图片`),i}insertPreviewRow(e,t,n){const s=e.nextElementSibling
;if(s&&s.classList.contains("u9a9-preview-row"))return this.updatePreviewRow(s,t,n),void 0;const i=document.createElement("tr")
;i.className="u9a9-preview-row";const r=document.createElement("td"),o=e.cells.length;r.setAttribute("colspan",o.toString()),
r.style.cssText="padding: 10px; text-align: center; background: #f8f9fa;",0===t.length?r.textContent="无预览图":this.renderImages(r,t,n),i.appendChild(r),
e.insertAdjacentElement("afterend",i)}updatePreviewRow(e,t,n){const s=e.querySelector("td");s&&(s.innerHTML="",
0===t.length?s.textContent="无预览图":this.renderImages(s,t,n))}renderImages(e,t,n){const s=document.createElement("div")
;s.style.cssText=CONFIG.styles.previewImages,t.forEach((e,n)=>{const i=document.createElement("img");i.src=e,i.loading="lazy",i.alt=`预览图 ${n+1}`,
i.style.cssText=CONFIG.getPreviewImageStyle(t.length),i.addEventListener("click",()=>{u.show(t,n)}),i.addEventListener("error",()=>{
i.style.display="none"}),i.addEventListener("mouseenter",()=>{i.style.transform="scale(1.05)"}),i.addEventListener("mouseleave",()=>{
i.style.transform="scale(1)"}),s.appendChild(i)}),e.appendChild(s)}}class StyleManager{constructor(){this.styleElement=null}init(){this.applyStyles(),
"loading"===document.readyState&&document.addEventListener("DOMContentLoaded",()=>{this.applyStyles()}),h("样式管理器初始化完成")}applyStyles(){
this.styleElement&&this.styleElement.remove(),this.styleElement=document.createElement("style"),this.styleElement.type="text/css",
this.styleElement.textContent=CONFIG.styles.previewRow,(document.head||document.getElementsByTagName("head")[0]).appendChild(this.styleElement),
h("预览行样式已应用")}destroy(){this.styleElement&&(this.styleElement.remove(),this.styleElement=null),h("样式管理器已销毁")}}class TorrentLinkRemover{constructor(){
this.observer=null,this.debouncedRemove=debounce(this.removeTorrentLinks.bind(this),100)}init(){this.removeTorrentLinks(),this.startObserver(),
h("种子链接移除器初始化完成")}removeTorrentLinks(){const e=document.querySelectorAll('a[href*=".torrent"]:not([href^="magnet:"])');let t=0;e.forEach(e=>{try{
const n=e.getAttribute("href");if(n&&n.includes(".torrent")&&!n.startsWith("magnet:")){const n=e.nextElementSibling
;if(n&&n.classList.contains("ext-push-resource-to-115")){const e=n.getAttribute("data-resource-url");e&&e.includes(".torrent")&&n.remove()}e.remove(),
t++}}catch(n){}}),t>0&&h(`已移除 ${t} 个种子下载链接`)}startObserver(){this.observer&&this.observer.disconnect(),this.observer=new MutationObserver(e=>{let t=!1
;e.forEach(e=>{"childList"===e.type&&e.addedNodes.length>0&&e.addedNodes.forEach(e=>{
e instanceof HTMLElement&&("A"===e.tagName&&e.getAttribute("href")?.includes(".torrent")||e.querySelector&&e.querySelector('a[href*=".torrent"]'))&&(t=!0)
})}),t&&this.debouncedRemove()}),this.observer.observe(document,{childList:!0,subtree:!0})}destroy(){this.observer&&(this.observer.disconnect(),
this.observer=null),h("种子链接移除器已销毁")}}class U9A9Preview{constructor(){this.imageLoader=new PreviewImageLoader,this.styleManager=new StyleManager,
this.torrentLinkRemover=new TorrentLinkRemover}init(){if(h("u9a9预览脚本启动(简化版)"),!this.isSupportedDomain())return h("不支持的域名,退出"),void 0
;this.styleManager.init(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{this.startFeatures()
}):this.startFeatures()}startFeatures(){try{this.torrentLinkRemover.init(),this.imageLoader.processAllRows(),h("u9a9预览脚本功能初始化完成")}catch(e){
h("功能初始化失败:",e)}}isSupportedDomain(){return window.location.hostname.includes("u9a9.com")}}(new U9A9Preview).init()})();