您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在HHCLUB、CHDBits、HDSky、Audiences、PTerClub和SSD 站点种子列表中,添加YouTube、抖音和豆瓣图标,点击后可即时在小窗口中搜索预告片
// ==UserScript== // @name PT 影视预告片/豆瓣,快速搜索插件 // @namespace http://tampermonkey.net/ // @version 1.2 // @description 在HHCLUB、CHDBits、HDSky、Audiences、PTerClub和SSD 站点种子列表中,添加YouTube、抖音和豆瓣图标,点击后可即时在小窗口中搜索预告片 // @author Gemini & User // @match *://hhanclub.top/torrents.php* // @match *://hhan.club/torrents.php* // @match *://ptchdbits.co/torrents.php* // @match *://hdsky.my/torrents.php* // @match *://audiences.me/torrents.php* // @match *://pterclub.com/torrents.php* // @match *://springsunday.net/torrents.php* // @grant GM_addStyle // @license MIT // @icon https://p-f-rc.byteimg.com/tos-cn-i-b4xunb0e52/f8f047395c37492c902a24d55233d403~tplv-b4xunb0e52-image.image // ==/UserScript== (function() { 'use strict'; // 通用符号屏蔽处理函数 const removeExcludedContent = (text) => { // 移除【符号及其后面的所有内容 const chineseBracketIndex = text.indexOf('【'); if (chineseBracketIndex !== -1) { text = text.substring(0, chineseBracketIndex).trim(); } // 移除[符号及其后面的所有内容 const squareBracketIndex = text.indexOf('['); if (squareBracketIndex !== -1) { text = text.substring(0, squareBracketIndex).trim(); } return text; }; // 站点配置 const getSiteConfig = () => { const hostname = location.hostname; if (/hhan(club\.top|\.club)/.test(hostname)) { return { name: 'HHCLUB', selectors: { row: '.torrent-table-sub-info', title: 'a.torrent-info-text-name', subtitle: '.torrent-info-text-small_name', rating: 'img[src*="icon-imdb-new.svg"], img[src*="icon-douban.svg"]', table: '.torrents-table' }, buttonSize: '19px', extractChineseTitle: (text) => { text = removeExcludedContent(text); // HHCLUB专用:移除/符号及其后面的所有内容 const slashIndex = text.indexOf('/'); if (slashIndex !== -1) { text = text.substring(0, slashIndex).trim(); } return text.split('|')[0].trim(); } }; } else if (hostname.includes('ptchdbits.co')) { return { name: 'CHDBits', selectors: { row: 'table.torrents tr', title: 'a[href*="details.php"] b', subtitle: 'font.subtitle', rating: 'img[src*="imdb.gif"]', table: 'table.torrents' }, buttonSize: '16px', extractChineseTitle: (text) => { text = removeExcludedContent(text); const tags = ['官方', '独占', 'DIY', '国语', '中字', '繁体', '简体', '字幕', '双语', '原版', '特效', '限转', '首发', '杜比', 'DTS', 'HD', 'MA', 'TrueHD', '内嵌', '外挂', '英字', '日字', '韩字', '4K', '1080p', '720p', '2160p', 'UHD', 'HDR', '重编码', '重制', '修复', '收藏', '蓝光', '原盘', '压制']; let content = text.trim(); let changed = true; while (changed) { changed = false; for (let tag of tags) { if (content.startsWith(tag)) { content = content.substring(tag.length).trim(); changed = true; break; } } } content = content.split('|')[0].trim(); const match = content.match(/^([^/]*?)(?=\s*[/第全]|\s*h[357]|$)/i); const result = (match ? match[1] : content).replace(/\s*h[357]\s*$/i, '').trim(); return result && /[\u4e00-\u9fa5]/.test(result) && result.length >= 2 ? result : null; } }; } else if (hostname.includes('hdsky.my')) { return { name: 'HDSky', selectors: { row: 'table.torrents tr', title: 'a[href*="details.php"] b span, a[href*="details.php"] b', subtitle: 'td.embedded', rating: 'img[src*="icon-imdb.png"], img[src*="icon-douban.png"]', table: 'table.torrents' }, buttonSize: '16px', extractChineseTitle: (text) => { text = removeExcludedContent(text); const tags = ['官组', '禁转', 'DIY', '国语', '中字', '繁体', '简体', '字幕', '双语', '原版', '特效', '限转', '首发', '杜比', 'DTS', 'HD', 'MA', 'TrueHD', '内嵌', '外挂', '英字', '日字', '韩字', '4K', '1080p', '720p', '2160p', 'UHD', 'HDR', '重编码', '重制', '修复', '收藏', '蓝光', '原盘', '压制', 'DoVi+HDR', 'Dolby Vision', 'HDR10', 'Atmos', '次世代国语', 'DIY纯净版', 'DTS-X', '全景声国语', '粤语', '去头尾广告纯净版']; let content = text.trim(); let changed = true; while (changed) { changed = false; for (let tag of tags) { if (content.startsWith(tag)) { content = content.substring(tag.length).trim(); changed = true; break; } } } content = content.split('|')[0].trim(); const match = content.match(/^([^/]*?)(?=\s*[/第全]|\s*h[357]|$)/i); const result = (match ? match[1] : content).replace(/\s*h[357]\s*$/i, '').trim(); return result && /[\u4e00-\u9fa5]/.test(result) && result.length >= 2 ? result : null; } }; } else if (hostname.includes('audiences.me')) { return { name: 'Audiences', selectors: { row: 'table.torrents tr', title: 'a[href*="details.php"] b', subtitle: 'span[style*="padding: 2px"]', table: 'table.torrents' }, buttonSize: '16px', extractChineseTitle: (text) => { text = removeExcludedContent(text); // Audiences特殊处理:移除又名及其后面的内容 const alsoKnownIndex = text.indexOf('又名'); if (alsoKnownIndex !== -1) { text = text.substring(0, alsoKnownIndex).trim(); } // Audiences特殊处理:移除*符号及其后面的内容 const asteriskIndex = text.indexOf('*'); if (asteriskIndex !== -1) { text = text.substring(0, asteriskIndex).trim(); } const tags = ['官方', '中字', '完结', 'Dolby Vision', '应求', 'HDR10', 'HDR10+', '官字组', '禁转', '动画', 'DIY', '限转', '国语', '粤语']; let content = text.trim(); let changed = true; while (changed) { changed = false; for (let tag of tags) { if (content.startsWith(tag)) { content = content.substring(tag.length).trim(); changed = true; break; } } } // 处理特殊情况:如 "陆地键仙 第130集",提取主要标题 content = content.split('|')[0].trim(); // 匹配中文标题,特别处理剧集格式 let match = content.match(/^([^\s第]*[\u4e00-\u9fa5][^\s第]*?)(?:\s+第\d+集)?/); if (!match) { match = content.match(/^([^/]*?)(?=\s*[/第全]|\s*h[357]|$)/i); } const result = (match ? match[1] : content).replace(/\s*h[357]\s*$/i, '').trim(); return result && /[\u4e00-\u9fa5]/.test(result) && result.length >= 2 ? result : null; } }; } else if (hostname.includes('pterclub.com')) { return { name: 'PTerClub', selectors: { row: 'table.torrents tr', title: 'a[href*="details.php"] b', subtitle: 'div[style*="margin-top: 4px"]', table: 'table.torrents' }, buttonSize: '16px', extractChineseTitle: (text) => { text = removeExcludedContent(text); // PTerClub特殊处理:移除又名及其后面的内容 const alsoKnownIndex = text.indexOf('又名'); if (alsoKnownIndex !== -1) { text = text.substring(0, alsoKnownIndex).trim(); } // PTerClub特殊处理:移除*符号及其后面的内容 const asteriskIndex = text.indexOf('*'); if (asteriskIndex !== -1) { text = text.substring(0, asteriskIndex).trim(); } const tags = ['官方', '中字', '完结', 'Dolby Vision', '应求', 'HDR10', 'HDR10+', '官字组', '禁转', '动画', 'DIY', '限转', '国语', '粤语', '杜比', 'DTS', 'HD', 'MA', 'TrueHD']; let content = text.trim(); let changed = true; while (changed) { changed = false; for (let tag of tags) { if (content.startsWith(tag)) { content = content.substring(tag.length).trim(); changed = true; break; } } } // 处理特殊情况:如 "鹦鹉 第1-15集",提取主要标题 content = content.split('|')[0].trim(); // 匹配中文标题,特别处理剧集格式 let match = content.match(/^([^\s第]*[\u4e00-\u9fa5][^\s第]*?)(?:\s+第\d+.*)?/); if (!match) { match = content.match(/^([^/]*?)(?=\s*[/第全]|\s*h[357]|$)/i); } const result = (match ? match[1] : content).replace(/\s*h[357]\s*$/i, '').trim(); return result && /[\u4e00-\u9fa5]/.test(result) && result.length >= 2 ? result : null; } }; } else if (hostname.includes('springsunday.net')) { return { name: 'SSD', selectors: { row: 'table.torrents tr', title: 'a[href*="details.php"]', subtitle: 'div.torrent-smalldescr', table: 'table.torrents' }, buttonSize: '16px', extractChineseTitle: (text) => { text = removeExcludedContent(text); // SSD特殊处理:移除/符号及其后面的所有内容 const slashIndex = text.indexOf('/'); if (slashIndex !== -1) { text = text.substring(0, slashIndex).trim(); } // SSD特殊处理:移除*符号及其后面的内容 const asteriskIndex = text.indexOf('*'); if (asteriskIndex !== -1) { text = text.substring(0, asteriskIndex).trim(); } const tags = ['CMCTV', '官方', '国配', '中字', '动画', '特效', '原生', '自购', '合集', '禁转', '驻站', 'CatEDU', 'HDR10', 'CMCTA', 'DIY', 'DoVi', 'HDR10+', '杜比', 'DTS', 'HD', 'MA', 'TrueHD', '内嵌', '外挂', '英字', '日字', '韩字', '4K', '1080p', '720p', '2160p', 'UHD', 'HDR', '重编码', '重制', '修复', '收藏', '蓝光', '原盘', '压制', 'Dolby Vision', '菁彩HDR', 'HLG', 'CC', '3D', '应求', '活动', '国语', '粤语']; let content = text.trim(); let changed = true; while (changed) { changed = false; for (let tag of tags) { if (content.startsWith(tag)) { content = content.substring(tag.length).trim(); changed = true; break; } } } // 处理特殊情况 content = content.split('|')[0].trim(); // 匹配中文标题,特别处理剧集格式 let match = content.match(/^([^\s第]*[\u4e00-\u9fa5][^\s第]*?)(?:\s+第\d+.*)?/); if (!match) { match = content.match(/^([^/]*?)(?=\s*[/第全]|\s*h[357]|$)/i); } const result = (match ? match[1] : content).replace(/\s*h[357]\s*$/i, '').trim(); return result && /[\u4e00-\u9fa5]/.test(result) && result.length >= 2 ? result : null; }, // SSD专用:从英文标题提取电影/剧名和年份 extractEnglishTitleForYouTube: (englishTitle) => { let title = englishTitle; // 第一步:提取年份 const yearMatch = title.match(/\.(\d{4})\./); const year = yearMatch ? yearMatch[1] : ''; // 第二步:移除剧集信息(在其他处理之前) // 匹配各种剧集格式:S任意数字E任意数字、S任意数字E任意数字-E任意数字、S任意数字等 title = title.replace(/\.S\d+E\d+(-E\d+)?/gi, '.'); title = title.replace(/\.S\d+/gi, '.'); // 第三步:如果找到年份,截取年份之前的部分作为标题部分 if (year) { const yearIndex = title.indexOf(`.${year}.`); if (yearIndex !== -1) { title = title.substring(0, yearIndex); } } else { // 如果没有年份,使用第一个技术信息之前的部分 const techPatterns = [ /\.\d{3,4}p\./i, // 分辨率 /\.BluRay\./i, // 来源 /\.Blu-ray\./i, // 来源 /\.WEB-DL\./i, // 来源 /\.HDTV\./i, // 来源 /\.ATVP\./i, // 来源:Apple TV+ /\.H26[45]\./i, // 编码 /\.x26[45]\./i, // 编码 ]; let cutIndex = title.length; for (let pattern of techPatterns) { const match = title.match(pattern); if (match && match.index < cutIndex) { cutIndex = match.index; } } title = title.substring(0, cutIndex); } // 第四步:移除地区代码(CEE、JPN、USA、GBR等) title = title.replace(/\.(CEE|JPN|USA|GBR|FRA|GER|ITA|ESP|KOR|CHN|TWN|HKG|IND|RUS)\./gi, '.'); // 第五步:清理多余的点号并替换为空格 title = title.replace(/\.+/g, '.'); // 多个点号合并为一个 title = title.replace(/^\.|\.$/g, ''); // 移除开头和结尾的点号 title = title.replace(/\./g, ' ').trim(); // 将点号替换为空格 title = title.replace(/\s+/g, ' '); // 多个空格合并为一个 return { title: title, year: year }; } }; } return null; }; const config = getSiteConfig(); if (!config) return; const processedClass = `${config.name.toLowerCase()}-processed`; // 样式和图标 GM_addStyle(` .trailer-btn { display: inline-flex !important; align-items: center; justify-content: center; height: ${config.buttonSize}; width: ${config.buttonSize}; margin-right: ${config.name === 'HHCLUB' ? '8px' : '4px'}; cursor: pointer; vertical-align: middle; } .trailer-btn:hover { opacity: 0.7; } .trailer-douyin { color: #000; } .trailer-douban { color: #00b600; } `); const icons = { youtube: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="fill: #ff0000;"><path d="M21.582,6.186c-0.23-0.854-0.908-1.532-1.762-1.762C18.254,4,12,4,12,4S5.746,4,4.18,4.424 c-0.854,0.23-1.532,0.908-1.762,1.762C2,7.754,2,12,2,12s0,4.246,0.418,5.814c0.23,0.854,0.908,1.532,1.762,1.762 C5.746,20,12,20,12,20s6.254,0,7.82-0.424c0.854-0.23,1.532-0.908,1.762-1.762C22,16.246,22,12,22,12S22,7.754,21.582,6.186z M10,15.464V8.536L16,12L10,15.464z"></path></svg>`, douyin: `<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z"/></svg>`, douban: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="fill: #00b600;"><path d="M17.9 3H6.1C4.39 3 3 4.39 3 6.1v11.8C3 19.61 4.39 21 6.1 21h11.8c1.71 0 3.1-1.39 3.1-3.1V6.1C21 4.39 19.61 3 17.9 3zM18 6v1H6V6h12zm-1 8h-1v1h1v1H7v-1h1v-1H7v-3h1v-1H7V9h1V8h8v1h1v1h-1v1h1v3zM7 18v-1h10v1H7z"/><path d="M9 11h6v2H9z"/></svg>` }; // 创建按钮 const createButton = (type, term, title) => { const btn = document.createElement('a'); btn.className = `trailer-btn trailer-${type}`; btn.title = `在${type === 'youtube' ? 'YouTube' : type === 'douyin' ? '抖音' : '豆瓣'}搜索: ${term}`; btn.innerHTML = icons[type]; let url; if (type === 'youtube') { url = `https://www.youtube.com/results?search_query=${encodeURIComponent(term)}`; } else if (type === 'douyin') { url = `https://www.douyin.com/search/${encodeURIComponent(term)}`; } else if (type === 'douban') { url = `https://search.douban.com/movie/subject_search?search_text=${encodeURIComponent(term)}`; } btn.onclick = e => { e.preventDefault(); e.stopPropagation(); window.open(url, `${type}_search`, 'width=800,height=600,resizable=yes,scrollbars=yes'); }; return btn; }; // 生成YouTube搜索词 const getYouTubeSearch = (title, chineseTitle, mainTitle) => { // SSD站点特殊处理:使用英文标题+年份 if (config.name === 'SSD' && config.extractEnglishTitleForYouTube) { const { title: englishTitle, year } = config.extractEnglishTitleForYouTube(mainTitle); return year ? `${englishTitle} ${year} Trailer` : `${englishTitle} Trailer`; } // 其他站点使用原有逻辑 const tvMatch = title.match(/^(.*?)\s+(S\d{1,2})\s+(\d{4})/); if (tvMatch) return `${tvMatch[1].trim()} ${tvMatch[2]} ${tvMatch[3]} Trailer`; const movieMatch = title.match(/^(.*?)\s+(\d{4})/); return movieMatch ? `${movieMatch[1].trim()} ${movieMatch[2]} Trailer` : `${title} Trailer`; }; // 处理种子行 const processRow = row => { if (row.classList.contains(processedClass) || row.querySelector('td.colhead') || row.querySelector('.trailer-btn')) return; const titleEl = row.querySelector(config.selectors.title); const subtitleEl = row.querySelector(config.selectors.subtitle); if (!titleEl || !subtitleEl) return; const mainTitle = titleEl.textContent.trim(); let subtitleText = ''; // 特殊处理不同站点的副标题提取 if (config.name === 'HDSky') { const spans = subtitleEl.querySelectorAll('span'); for (let span of spans) { const text = span.textContent.trim(); if (/[\u4e00-\u9fa5]/.test(text) && !span.classList.contains('optiontag')) { subtitleText = text; break; } } } else if (config.name === 'PTerClub') { // PTerClub:从副标题div中找到包含中文的span const span = subtitleEl.querySelector('span'); if (span) { subtitleText = span.textContent || ''; } } else if (config.name === 'SSD') { // SSD:从div.torrent-smalldescr中找到包含中文的最后一个span const spans = subtitleEl.querySelectorAll('span'); for (let i = spans.length - 1; i >= 0; i--) { const span = spans[i]; const text = span.textContent.trim(); if (span.hasAttribute('title') && /[\u4e00-\u9fa5]/.test(text)) { subtitleText = text; break; } } } else { subtitleText = subtitleEl.textContent || ''; } // 创建按钮 const chTitle = config.extractChineseTitle(subtitleText); const ytBtn = createButton('youtube', getYouTubeSearch(mainTitle, chTitle, mainTitle), chTitle || mainTitle); const dyBtn = chTitle ? createButton('douyin', `${chTitle} 预告片`, chTitle) : null; const dbBtn = chTitle ? createButton('douban', chTitle, chTitle) : null; // 插入按钮 if (config.name === 'HHCLUB') { const ratingEl = row.querySelector(config.selectors.rating); if (ratingEl) { const container = ratingEl.parentElement.parentElement; const ratingSpan = ratingEl.parentElement; container.insertBefore(ytBtn, ratingSpan); if (dyBtn) container.insertBefore(dyBtn, ratingSpan); if (dbBtn) container.insertBefore(dbBtn, ratingSpan); } } else if (config.name === 'HDSky') { const firstSpan = subtitleEl.querySelector('span.optiontag'); if (firstSpan) { subtitleEl.insertBefore(ytBtn, firstSpan); if (dyBtn) subtitleEl.insertBefore(dyBtn, firstSpan); if (dbBtn) subtitleEl.insertBefore(dbBtn, firstSpan); } else { const firstChild = subtitleEl.firstChild; if (firstChild) { subtitleEl.insertBefore(ytBtn, firstChild); if (dyBtn) subtitleEl.insertBefore(dyBtn, firstChild); if (dbBtn) subtitleEl.insertBefore(dbBtn, firstChild); } } } else if (config.name === 'Audiences') { // Audiences:在副标题容器的父元素最前面插入图标 const container = subtitleEl.parentElement; // td.embedded if (dbBtn) container.insertBefore(dbBtn, container.firstChild); if (dyBtn) container.insertBefore(dyBtn, container.firstChild); container.insertBefore(ytBtn, container.firstChild); } else if (config.name === 'PTerClub') { // PTerClub:在副标题div的最前面插入图标 if (dbBtn) subtitleEl.insertBefore(dbBtn, subtitleEl.firstChild); if (dyBtn) subtitleEl.insertBefore(dyBtn, subtitleEl.firstChild); subtitleEl.insertBefore(ytBtn, subtitleEl.firstChild); } else if (config.name === 'SSD') { // SSD:在torrent-smalldescr div的最前面插入图标 if (dbBtn) subtitleEl.insertBefore(dbBtn, subtitleEl.firstChild); if (dyBtn) subtitleEl.insertBefore(dyBtn, subtitleEl.firstChild); subtitleEl.insertBefore(ytBtn, subtitleEl.firstChild); } else { // CHDBits const tagContainer = subtitleEl.querySelector('div[style*="display:inline-block"]'); const insertBefore = tagContainer || subtitleEl.firstChild; subtitleEl.insertBefore(ytBtn, insertBefore); if (dyBtn) subtitleEl.insertBefore(dyBtn, insertBefore); if (dbBtn) subtitleEl.insertBefore(dbBtn, insertBefore); } row.classList.add(processedClass); }; // 处理所有行 const processAll = () => document.querySelectorAll(config.selectors.row).forEach(processRow); // 初始化 const init = () => { setTimeout(processAll, 500); const targetNode = document.querySelector(config.selectors.table); if (targetNode) { new MutationObserver(mutations => { if (mutations.some(m => m.addedNodes.length > 0 && Array.from(m.addedNodes).some(n => n.nodeType === 1 && (n.tagName === 'TR' || n.querySelector?.('tr') || n.classList?.contains('torrent-table-sub-info'))))) { setTimeout(processAll, 250); } }).observe(targetNode, { childList: true, subtree: true }); } else { setInterval(processAll, 1000); } }; document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', init) : init(); })();