u9a9-预览

u9a9·列表预览图片

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         u9a9-预览
// @version      0.0.4
// @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 t=200,e="1px solid #ddd",n="4px",i="contain",r="pointer",CONFIG={MAX_PREVIEW_IMAGES:5,PREVIEW_IMAGE_HEIGHT:t,
getPreviewImageStyle:(s=1)=>{const a=s<=3?"auto":Math.floor(100/s)-1+"%"
;return`height: ${t}px; width: ${a}; object-fit: ${i}; cursor: ${r}; border: ${e}; border-radius: ${n}; flex-shrink: 0;`},selectors:{
listTable:".table.table-bordered.table-hover.table-striped.torrent-list",listRows:"tbody tr",titleLinks:'a[href^="/view/"]',
titleCell:"td:nth-child(2)",imgContainer:".img-container",images:".img-container img"},regex:{viewUrl:/^\/view\/\d+\/[a-f0-9]+$/,
imageUrl:/\.(jpg|jpeg|png|gif|webp)$/i},styles:{
pageLayout:"\n      .container-fluid, .container {\n        max-width: none !important;\n        width: 95% !important;\n        margin: 0 auto !important;\n      }\n      .table.torrent-list {\n        width: 100% !important;\n        table-layout: auto !important;\n      }\n      .u9a9-reconstructed {\n        margin-top: 0 !important;\n      }\n      .u9a9-reconstructed th:first-child {\n        width: auto !important;\n        min-width: 400px;\n      }\n      .u9a9-reconstructed th:nth-child(2) {\n        width: 80px;\n        text-align: center;\n      }\n      .u9a9-reconstructed th:nth-child(3) {\n        width: 120px;\n        text-align: center;\n      }\n      .u9a9-reconstructed th:nth-child(4) {\n        width: 140px;\n        text-align: center;\n      }\n      .u9a9-preview-row {\n        background-color: #f8f9fa !important;\n      }\n      .u9a9-preview-row td {\n        padding: 12px !important;\n        border-top: none !important;\n      }\n      .u9a9-data-row td {\n        border-bottom: none !important;\n      }\n    ",
previewImages:"display: flex; gap: 4px; flex-wrap: nowrap; overflow: hidden; width: 100%; padding: 4px; margin: 0;",
previewImage:`height: ${t}px; min-width: 60px; object-fit: ${i}; cursor: ${r}; border: ${e}; border-radius: ${n}; transition: transform 0.2s ease; flex-shrink: 0;`
}},s=class{static info(...t){this.enabled,0}static error(...t){this.enabled,0}};s.enabled=!1,s.load=(...t)=>{},s.loadError=()=>{};let a=s
;function debounce(t,e){let n;return(...i)=>{clearTimeout(n),n=setTimeout(()=>t(...i),e)}}function createElement(t,e,n){
const i=document.createElement(t);return e&&Object.entries(e).forEach(([t,e])=>{
null!=e&&("style"===t&&"string"==typeof e?i.setAttribute("style",e):"style"===t&&"object"==typeof e?Object.assign(i.style,e):"className"===t&&"string"==typeof e||"class"===t&&"string"==typeof e?i.className=e:"function"==typeof e&&t.startsWith("on")?i[t]=e:"textContent"===t?i.textContent=e:"innerHTML"===t?i.innerHTML=e:"string"==typeof e||"number"==typeof e?i.setAttribute(t,String(e)):i[t]=e)
}),void 0!==n&&(Array.isArray(n)?n:[n]).forEach(t=>{"string"==typeof t?i.appendChild(document.createTextNode(t)):i.appendChild(t)}),i}function o(t){
return CONFIG.regex.imageUrl.test(t)}function l(t){return t.startsWith("//")?`https:${t}`:t.startsWith("/")?`${window.location.origin}${t}`:t}
function c(t,...e){a.info(t,...e)}const h=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.overlay.appendChild(this.img),
this.overlay.appendChild(this.counter),this.overlay.appendChild(this.prevBtn),this.overlay.appendChild(this.nextBtn),
document.body.appendChild(this.overlay),this.setupEvents())}static createNavButton(t,e){const n=document.createElement("button")
;return n.innerHTML="‹"===t?'<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      ${e}: 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 setupEvents(){this.overlay.onclick=()=>{this.close()},this.prevBtn.onclick=t=>{t.stopPropagation(),this.prev()},this.nextBtn.onclick=t=>{
t.stopPropagation(),this.next()},document.addEventListener("keydown",t=>{
"flex"===this.overlay?.style.display&&("Escape"===t.key?this.close():"ArrowLeft"===t.key?this.prev():"ArrowRight"===t.key&&this.next())})}
static show(t,e=0){this.init(),this.images=t,this.currentIndex=e,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 t=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=t}}
;h.overlay=null,h.img=null,h.counter=null,h.prevBtn=null,h.nextBtn=null,h.images=[],h.currentIndex=0;let d=h;const u=class _SimpleCache{constructor(){
this.cache=new Map}static getInstance(){return this.instance||(this.instance=new _SimpleCache),this.instance}get(t){return this.cache.get(t)||null}
set(t,e){this.cache.set(t,e)}delete(t){return this.cache.delete(t)}clear(){this.cache.clear()}static get(t){return _SimpleCache.getInstance().get(t)}
static set(t,e){_SimpleCache.getInstance().set(t,e)}};u.instance=null;let p=u;class PreviewImageLoader{constructor(){this.loading=new Set}
async loadPreviewImages(t,e){if(this.loading.has(t))return;const n=`images_${t}`,i=p.get(n);if(i)return this.renderImages(e,i),void 0
;this.loading.add(t),e.textContent="📷 加载中...";try{const i=await this.fetchImagesWithFetch(t);p.set(n,i),this.renderImages(e,i)}catch(r){
a.loadError(),e.textContent="❌ 加载失败"}finally{this.loading.delete(t)}}async loadMultiplePreviewImages(t){
await Promise.all(t.map(t=>this.loadPreviewImages(t.viewUrl,t.previewCell)))}async fetchImagesWithFetch(t){const e=await fetch(t)
;if(!e.ok)throw new Error(`HTTP ${e.status}: ${e.statusText}`)
;const n=await e.text(),i=(new DOMParser).parseFromString(n,"text/html").querySelector(CONFIG.selectors.imgContainer);if(!i)return[]
;const r=Array.from(i.querySelectorAll("img")).map(t=>l(t.src)).filter(t=>t&&o(t)).slice(0,CONFIG.MAX_PREVIEW_IMAGES);return a.load(r.length),r}
renderImages(t,e){if(t.innerHTML="",0===e.length)return t.textContent="无预览图",void 0;const n=createElement("div",{style:CONFIG.styles.previewImages})
;e.forEach((t,i)=>{const r=createElement("img",{src:t,alt:`预览图 ${i+1}`,style:CONFIG.getPreviewImageStyle(e.length)});r.addEventListener("click",()=>{
d.show(e,i)}),r.addEventListener("error",()=>{r.style.display="none"}),r.addEventListener("mouseenter",()=>{r.style.transform="scale(1.05)"}),
r.addEventListener("mouseleave",()=>{r.style.transform="scale(1)"}),n.appendChild(r)}),t.appendChild(n)}}class PageReconstructor{constructor(){
this.originalTable=null,this.rowsData=[],this.imageLoader=new PreviewImageLoader}init(){
if(this.originalTable=document.querySelector(CONFIG.selectors.listTable),!this.originalTable)return c("未找到原始表格"),void 0;c("开始页面重构"),
this.extractRowsData(),this.reconstructTable(),c("页面重构完成")}extractRowsData(){if(!this.originalTable)return
;const t=this.originalTable.querySelectorAll("tbody tr");c(`提取 ${t.length} 行数据`),t.forEach(t=>{if(t instanceof HTMLTableRowElement){
const e=this.extractRowData(t);e&&this.rowsData.push(e)}}),c(`成功提取 ${this.rowsData.length} 行数据`)}extractRowData(t){
const e=t.cells[1],n=t.cells[2],i=t.cells[3],r=t.cells[4];if(!(e&&n&&i&&r))return null;const s=e.querySelector('a[href^="/view/"]');if(!s)return null
;const a=n.querySelector('a[href^="magnet:"]'),o=a?a.href:"";return{title:s.textContent?.trim()||"",titleLink:s.href,magnetLink:o,
size:i.textContent?.trim()||"",date:r.textContent?.trim()||"",originalRow:t}}reconstructTable(){
if(!this.originalTable||0===this.rowsData.length)return;const t=this.createNewTable()
;this.originalTable.parentNode?.replaceChild(t,this.originalTable)}createNewTable(){const t=createElement("table",{
className:"table table-bordered table-hover table-striped torrent-list u9a9-reconstructed",style:"width: 100%; table-layout: auto;"
}),e=createElement("thead"),n=createElement("tr");["Name","Link","Size","Date"].forEach(t=>{const e=createElement("th",{
className:`hdr-${t.toLowerCase()} text-center`,style:"Name"===t?"width: auto;":""},[t]);n.appendChild(e)}),e.appendChild(n),t.appendChild(e)
;const i=createElement("tbody"),r=[];return this.rowsData.forEach((t,e)=>{const n=this.createDataRow(t);i.appendChild(n)
;const s=this.createPreviewRow();i.appendChild(s);const a=s.cells[0];r.push({viewUrl:t.titleLink,previewCell:a})}),
r.length>0&&this.imageLoader.loadMultiplePreviewImages(r),t.appendChild(i),t}createDataRow(t){const e=createElement("tr",{
className:"default u9a9-data-row"}),n=createElement("td",{},[createElement("a",{href:t.titleLink,title:t.title,target:"_blank"
},[t.title])]),i=createElement("td",{className:"text-center"});if(t.magnetLink){const e=createElement("i",{
className:"glyphicon glyphicon-magnet fa-fw"}),n=createElement("a",{href:t.magnetLink,target:"_blank"});n.appendChild(e),i.appendChild(n)}
const r=createElement("td",{className:"text-center"},[t.size]),s=createElement("td",{className:"text-center"},[t.date]);return e.appendChild(n),
e.appendChild(i),e.appendChild(r),e.appendChild(s),e}createPreviewRow(){const t=createElement("tr",{className:"u9a9-preview-row",
style:"background: #f8f9fa; border-top: none;"}),e=createElement("td",{style:"padding: 8px; background: #f8f9fa; border-top: none;"})
;return e.colSpan=4,e.textContent="⏳ 准备加载...",t.appendChild(e),t}getReconstructedData(){return this.rowsData}}class StyleManager{constructor(){
this.styleElement=null}init(){this.applyPageStyles(),"loading"===document.readyState&&document.addEventListener("DOMContentLoaded",()=>{
this.applyPageStyles()}),c("样式管理器初始化完成")}applyPageStyles(){this.styleElement&&this.styleElement.remove(),
this.styleElement=document.createElement("style"),this.styleElement.type="text/css",this.styleElement.textContent=CONFIG.styles.pageLayout,
(document.head||document.getElementsByTagName("head")[0]).appendChild(this.styleElement),c("页面样式已应用")}destroy(){
this.styleElement&&(this.styleElement.remove(),this.styleElement=null),c("样式管理器已销毁")}}class TorrentLinkRemover{constructor(){this.observer=null,
this.debouncedRemove=debounce(this.removeTorrentLinks.bind(this),100)}init(){this.removeTorrentLinks(),this.startObserver(),c("种子链接移除器初始化完成")}
removeTorrentLinks(){const t=document.querySelectorAll('a[href*=".torrent"]:not([href^="magnet:"])');let e=0;t.forEach(t=>{try{
const n=t.getAttribute("href");if(n&&n.includes(".torrent")&&!n.startsWith("magnet:")){const n=t.nextElementSibling
;if(n&&n.classList.contains("ext-push-resource-to-115")){const t=n.getAttribute("data-resource-url");t&&t.includes(".torrent")&&n.remove()}t.remove(),
e++}}catch(n){}}),e>0&&c(`已移除 ${e} 个种子下载链接`)}startObserver(){this.observer&&this.observer.disconnect(),this.observer=new MutationObserver(t=>{let e=!1
;t.forEach(t=>{"childList"===t.type&&t.addedNodes.length>0&&t.addedNodes.forEach(t=>{
t instanceof HTMLElement&&("A"===t.tagName&&t.getAttribute("href")?.includes(".torrent")||t.querySelector&&t.querySelector('a[href*=".torrent"]'))&&(e=!0)
})}),e&&this.debouncedRemove()}),this.observer.observe(document,{childList:!0,subtree:!0})}destroy(){this.observer&&(this.observer.disconnect(),
this.observer=null),c("种子链接移除器已销毁")}}class U9A9Preview{constructor(){this.pageReconstructor=new PageReconstructor,this.styleManager=new StyleManager,
this.torrentLinkRemover=new TorrentLinkRemover}init(){if(c("u9a9预览脚本启动"),!this.isSupportedDomain())return c("不支持的域名,退出"),void 0
;this.styleManager.init(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{this.startFeatures()
}):this.startFeatures()}startFeatures(){try{this.torrentLinkRemover.init(),this.pageReconstructor.init(),c("u9a9预览脚本功能初始化完成")}catch(t){c("功能初始化失败:",t)
}}isSupportedDomain(){return window.location.hostname.includes("u9a9.com")}}(new U9A9Preview).init()})();