您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
On-hover Steam thumbnail, description, and Steam‐provided tags for 1337x torrent titles
当前为
// ==UserScript== // @name 1337x - Steam Hover Preview // @namespace https://gf.qytechs.cn/en/users/1340389-deonholo // @version 2.3 // @description On-hover Steam thumbnail, description, and Steam‐provided tags for 1337x torrent titles // @icon https://greasyfork.s3.us-east-2.amazonaws.com/x432yc9hx5t6o2gbe9ccr7k5l6u8 // @author DeonHolo // @license MIT // @match *://*.1337x.to/* // @match *://*.1337x.ws/* // @match *://*.1337x.is/* // @match *://*.1337x.gd/* // @match *://*.x1337x.cc/* // @match *://*.1337x.st/* // @match *://*.x1337x.ws/* // @match *://*.1337x.eu/* // @match *://*.1337x.se/* // @match *://*.x1337x.eu/* // @match *://*.x1337x.se/* // @match http://l337xdarkkaqfwzntnfk5bmoaroivtl6xsbatabvlb52umg6v3ch44yd.onion/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @connect store.steampowered.com // @connect steamcdn-a.akamaihd.net // @run-at document-idle // ==/UserScript== (() => { 'use strict'; GM_addStyle(` .steamHoverTip { position: fixed; padding: 8px; background: rgba(255,255,255,0.95); border: 1px solid #444; border-radius: 4px; box-shadow: 0 2px 6px rgba(0,0,0,0.2); z-index: 2147483647; max-width: 300px; font-size: 12px; line-height: 1.3; display: none; pointer-events: none; white-space: normal !important; overflow-wrap: break-word; } .steamHoverTip p { margin: 0; white-space: normal; } `); const tip = document.createElement('div'); tip.className = 'steamHoverTip'; document.body.appendChild(tip); const MIN_INTERVAL = 50; let lastRequest = 0; const apiCache = new Map(); const MAX_CACHE = 100; function pruneCache(map) { if (map.size <= MAX_CACHE) return; const key = map.keys().next().value; map.delete(key); } function gmFetch(url, responseType = 'json') { const wait = Math.max(0, MIN_INTERVAL - (Date.now() - lastRequest)); return new Promise(r => setTimeout(r, wait)) .then(() => new Promise((resolve, reject) => { lastRequest = Date.now(); GM_xmlhttpRequest({ method: 'GET', url, responseType, onload: res => resolve(res.response), onerror: err => reject(err) }); })); } function fetchSteam(name) { if (apiCache.has(name)) return apiCache.get(name); const p = (async () => { // 1a. find the app id const search = await gmFetch( `https://store.steampowered.com/api/storesearch/?cc=us&l=en&term=${encodeURIComponent(name)}` ).catch(() => null); const id = search?.items?.[0]?.id; if (!id) return null; // 1b. HTML-scrape the Steam store page for popular user tags const html = await gmFetch( `https://store.steampowered.com/app/${id}/?cc=us&l=en`, 'text' ).catch(() => ''); let scrapedTags = []; if (html) { try { const doc = new DOMParser().parseFromString(html, 'text/html'); scrapedTags = Array.from( doc.querySelectorAll('.glance_tags.popular_tags a.app_tag') ).map(el => el.textContent.trim()); } catch (e) { scrapedTags = []; } } // 1c. always fetch the API too (for description, header_image, fallback) const resp = await gmFetch( `https://store.steampowered.com/api/appdetails?appids=${id}&cc=us&l=en&cc=us`, 'json' ).catch(() => null); const data = resp?.[id]?.data || null; if (!data) return null; // return both scrapedTags and API data return { ...data, scrapedTags }; })(); apiCache.set(name, p); pruneCache(apiCache); return p; } function cleanName(raw) { if (/soundtrack|ost|demo/i.test(raw)) return null; let name = raw.trim(); name = name.split(/(?:[-\/(\[]|Update|Edition|Deluxe)/i)[0].trim(); return name.replace(/ v[\d.].*$/i, '').trim() || null; } function positionTip(e) { let x = e.clientX + 12; let y = e.clientY + 12; const w = tip.offsetWidth; const h = tip.offsetHeight; if (x + w > window.innerWidth) x = window.innerWidth - w - 8; if (y + h > window.innerHeight) y = window.innerHeight - h - 8; tip.style.left = x + 'px'; tip.style.top = y + 'px'; } let hoverId = 0; let pointerOn = false; let lastEvent = null; async function showTip(e) { pointerOn = true; lastEvent = e; const thisId = ++hoverId; const raw = e.target.textContent; const name = cleanName(raw); if (!name) return hideTip(); tip.innerHTML = `<p>Loading <strong>${name}</strong>…</p>`; tip.style.display = 'block'; positionTip(e); await new Promise(r => setTimeout(r, 30)); if (thisId !== hoverId) return; const data = await fetchSteam(name); if (thisId !== hoverId) return; if (!data) { tip.innerHTML = `<p>No info for<br><strong>${name}</strong>.</p>`; return; } // build tags from Steam-provided genres & categories const genres = (data.genres || []).map(g => g.description); const categories = (data.categories|| []).map(c => c.description); const tags = (data.scrapedTags && data.scrapedTags.length) // use scraped user tags first ? data.scrapedTags.slice(0, 5) // otherwise fall back to genres + categories : [...new Set([ ...(data.genres || []).map(g => g.description), ...(data.categories|| []).map(c => c.description) ])].slice(0, 5); const tagHtml = tags.length ? `<p style="margin-top:6px"><strong>Tags:</strong> ${tags.join(' • ')}</p>` : ''; tip.innerHTML = ` <img src="${data.header_image}" style="width:100%;margin-bottom:6px"> <p>${data.short_description}</p> ${tagHtml} `; positionTip(e); } function hideTip() { pointerOn = false; tip.style.display = 'none'; hoverId++; } function onPointerMove(e) { if (!pointerOn) return; lastEvent = e; } function rafLoop() { if (pointerOn && lastEvent) { positionTip(lastEvent); } requestAnimationFrame(rafLoop); } const SEL = 'td.coll-1 a[href^="/torrent/"]'; document.addEventListener('pointerenter', e => { if (e.target.matches(SEL)) showTip(e); }, true); document.addEventListener('pointerleave', e => { if (e.target.matches(SEL)) hideTip(); }, true); document.addEventListener('pointermove', onPointerMove, true); rafLoop(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址