您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在Bangumi游戏条目上添加实用的按钮
当前为
// ==UserScript== // @name Bangumi jump to multiple sites // @namespace http://tampermonkey.net/ // @version 0.9.5 // @description 在Bangumi游戏条目上添加实用的按钮 // @author Sedoruee // @include /https?:\/\/(bgm\.tv|bangumi\.tv|chii\.in).*/ // @grant GM_setClipboard // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== (function() { 'use strict'; // 获取条目类型和标题 const subjectType = document.querySelector('.nameSingle > .grey')?.textContent; const gameTitle = document.querySelector('.nameSingle > a')?.textContent; if (subjectType === '游戏' && gameTitle) { const nameSingle = document.querySelector('.nameSingle'); // 添加统一样式 GM_addStyle(` .combined-button, .multisearch-select-container .combined-button, .jump-button { /* 统一高度应用到所有按钮 */ display: inline-flex; align-items: center; margin-left: 5px; border: 1px solid #ccc; border-radius: 3px; background-color: #f0f0f0; color: black; font-size: 14px; cursor: pointer; height: 32px; /* 统一按钮高度 */ box-sizing: border-box; overflow: hidden; } .button-name { padding: 5px 10px; border: none; background-color: transparent; color: inherit; font-size: inherit; cursor: pointer; text-align: center; flex-grow: 1; flex-shrink: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 50px; } .select-arrow { -webkit-appearance: none; -moz-appearance: none; appearance: none; background-color: transparent; border: none; padding: 5px 10px; cursor: pointer; font-size: inherit; color: inherit; position: relative; z-index: 1; width: 20px; flex-shrink: 0; /* 使用 background-image 替代 ::after 实现箭头 */ background-image: url('data:image/svg+xml;utf8,<svg fill="black" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'); background-repeat: no-repeat; background-position: center; /* 居中箭头 */ border-left: 1px solid #ddd; /* 弱化左边框颜色 */ margin-left: 2px; /* 稍微减小箭头区域左边距,更紧凑 */ } .select-arrow::-ms-expand { display: none; } .combined-button:hover, .multisearch-select-container .combined-button:hover, .jump-button:hover, .button-name:hover, .select-arrow:hover { background-color: #e0e0e0; } .combined-button:active, .multisearch-select-container .combined-button:active, .jump-button:active, .button-name:active, .select-arrow:active { background-color: #d0d0d0; } .jump-button { /* 确保 jump-button 也应用统一高度,虽然在上面已经统一设置了,这里再次强调 */ height: 32px; line-height: 32px; /* 垂直居中文字,如果需要 */ padding-top: 0; padding-bottom: 0; display: inline-flex; /* 使其可以垂直对齐,虽然默认已经是 inline-block */ align-items: center; /* 垂直居中按钮内的文字 */ text-align: center; /* 确保文本居中 */ } .multisearch-select-container { display: inline-block; position: relative; margin-left: 5px; } .multisearch-select-dropdown { position: absolute; top: 100%; left: 0; z-index: 10; border: 1px solid #ccc; border-radius: 3px; background-color: white; padding: 5px 0; min-width: 150px; display: none; } .multisearch-select-dropdown.show { display: block; } .multisearch-select-dropdown label { display: block; padding: 5px 15px; cursor: pointer; } .multisearch-select-dropdown label:hover { background-color: #f0f0f0; } `); // 辅助函数:创建按钮 const createButton = (text, clickHandler) => { const button = document.createElement('button'); button.textContent = text; button.className = 'jump-button'; if (clickHandler) { button.addEventListener('click', clickHandler); } nameSingle.appendChild(button); return button; }; // 辅助函数:创建合并按钮 (名称左侧点击,箭头右侧下拉) const createCombinedButton = (defaultSiteValue, siteOptions, storageKey, openAction) => { const container = document.createElement('div'); container.className = 'combined-button'; // 统一class const nameButton = document.createElement('button'); nameButton.className = 'button-name'; container.appendChild(nameButton); const selectArrow = document.createElement('select'); selectArrow.className = 'select-arrow'; container.appendChild(selectArrow); siteOptions.forEach(site => { const option = document.createElement('option'); option.value = site.value; option.text = site.text; selectArrow.appendChild(option); }); // 读取上次选择的站点 const storedSite = GM_getValue(storageKey, defaultSiteValue); selectArrow.value = storedSite; updateButtonName(nameButton, selectArrow.options[selectArrow.selectedIndex].text); // 初始化按钮名称 // 监听下拉框变化 selectArrow.addEventListener('change', function() { GM_setValue(storageKey, this.value); updateButtonName(nameButton, this.options[this.selectedIndex].text); // 更新按钮名称 }); // 初始化点击事件 nameButton.addEventListener('click', () => { openAction(selectArrow.value); }); // selectArrow点击时打开下拉菜单,并阻止事件冒泡,避免触发nameButton的点击 selectArrow.addEventListener('click', function(event) { this.focus(); // 聚焦select元素以打开下拉菜单 event.stopPropagation(); // 阻止事件冒泡到容器或nameButton }); container.addEventListener('click', function(event) { if (!container.contains(event.target)) { selectArrow.blur(); // 当点击容器外部时,移除select焦点,关闭下拉菜单 } }); function updateButtonName(button, siteName) { button.textContent = siteName; } nameSingle.appendChild(container); return container; }; // 辅助函数:创建多搜索下拉选择框 (类似合并按钮样式) const createMultiSearchSelect = () => { const container = document.createElement('div'); container.className = 'multisearch-select-container'; const buttonArea = document.createElement('div'); buttonArea.className = 'combined-button'; // 统一class container.appendChild(buttonArea); const nameButton = document.createElement('button'); nameButton.className = 'button-name'; nameButton.textContent = '多搜索'; buttonArea.appendChild(nameButton); const selectArrow = document.createElement('button'); // 使用 button 替代 select,用于触发下拉菜单 selectArrow.className = 'select-arrow'; buttonArea.appendChild(selectArrow); const dropdown = document.createElement('div'); dropdown.className = 'multisearch-select-dropdown'; dropdown.id = 'multisearchDropdown'; container.appendChild(dropdown); const sites = [ { value: 'ai2', text: 'ai2.moe', url: `https://www.ai2.moe/search/?q=${encodeURIComponent(gameTitle)}&updated_after=any&sortby=relevancy&search_in=titles`, checked: true }, { value: 'moyu', text: 'moyu.moe', url: `https://www.moyu.moe/search?q=${encodeURIComponent(gameTitle)}`, checked: true }, { value: '2dfan_preview', text: '2dfan(预览)', url: `https://2dfan.com/subjects/search?keyword=${encodeURIComponent(gameTitle)}`, checked: true } ]; // 读取上次选择的站点 let storedMultiSearchSites = GM_getValue('multiSearchSites', sites.filter(site => site.checked).map(site => site.value).join(',')); let selectedSitesValues = storedMultiSearchSites ? storedMultiSearchSites.split(',') : sites.filter(site => site.checked).map(site => site.value); sites.forEach(site => { const label = document.createElement('label'); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.value = site.value; checkbox.checked = selectedSitesValues.includes(site.value); // 根据存储状态设置选中 checkbox.addEventListener('change', function() { let currentSelectedValues = Array.from(dropdown.querySelectorAll('input[type="checkbox"]:checked')).map(cb => cb.value); GM_setValue('multiSearchSites', currentSelectedValues.join(',')); }); label.appendChild(checkbox); label.appendChild(document.createTextNode(' ' + site.text)); dropdown.appendChild(label); }); selectArrow.addEventListener('click', function(event) { dropdown.classList.toggle('show'); event.stopPropagation(); // 防止事件冒泡到 document 导致立即关闭下拉菜单 }); nameButton.addEventListener('click', () => { // 延迟打开预览窗口,如果需要立即打开,可以直接调用 openPreviewWindows(); setTimeout(() => { openPreviewWindows(); }, 200); }); // 点击文档其他地方关闭下拉菜单 document.addEventListener('click', function(event) { if (!container.contains(event.target)) { dropdown.classList.remove('show'); } }); return container; }; // 添加换行符 nameSingle.appendChild(document.createElement('br')); // VNDB按钮 createButton('VNDB', () => { window.location.href = `https://vndb.org/v?q=${encodeURIComponent(gameTitle)}`; }); // Hitomi按钮 createButton('Hitomi', () => { // 1. Hitomi之前先把所有标题的特殊字符转为空格,只搜索最长一块 const delimitersRegex = /[~]+/g; // 正则表达式匹配一个或多个 -, ~ 字符 const titleSegments = gameTitle.split(delimitersRegex); let longestSegment = ""; let maxLength = 0; for (const segment of titleSegments) { if (segment.length > maxLength) { maxLength = segment.length; longestSegment = segment; } } const hitomiTitle = longestSegment.trim(); // 使用最长文本段, trim去除首尾空格 window.location.href = `https://hitomi.la/search.html?type%3Agamecg%20${encodeURIComponent(hitomiTitle)}%20orderby%3Apopular%20orderbykey%3Ayear`; }); // 魔皇地狱/zi0.cc 合并按钮 createCombinedButton( 'mhdy', [ { value: 'mhdy', text: '魔皇地狱' }, { value: 'zi0', text: 'zi0.cc' } ], 'selectedSite', (selectedSite) => { GM_setClipboard(gameTitle); let siteUrl; if (selectedSite === 'mhdy') { siteUrl = 'https://pan1.mhdy.shop/'; } else if (selectedSite === 'zi0') { siteUrl = 'https://zi0.cc/'; } else { siteUrl = 'https://pan1.mhdy.shop/'; // 默认魔皇地狱 } window.open(siteUrl); } ); // 2dfan按钮 createButton('2dfan(网页)', () => { GM_setClipboard(gameTitle); window.open(`https://2dfan.com/subjects/search?keyword=${encodeURIComponent(gameTitle)}`); }); // “多搜索” 按钮 (合并样式) const multiSearchSelectContainer = createMultiSearchSelect(); nameSingle.appendChild(multiSearchSelectContainer); let previewWindows = []; let monitorInterval = null; let focusMonitorDelayTimer = null; // 打开预览窗口 function openPreviewWindows() { // ... (多搜索窗口打开和管理逻辑 - 与之前版本相同,无需修改) closePreviewWindows(); // 获取选中的多搜索站点 const dropdownElement = multiSearchSelectContainer.querySelector('.multisearch-select-dropdown'); const selectedCheckboxes = dropdownElement.querySelectorAll('input[type="checkbox"]:checked'); const selectedSitesValues = Array.from(selectedCheckboxes).map(cb => cb.value); const sites = [ { value: 'ai2', text: 'ai2.moe', url: `https://www.ai2.moe/search/?q=${encodeURIComponent(gameTitle)}&updated_after=any&sortby=relevancy&search_in=titles` }, { value: 'moyu', text: 'moyu.moe', url: `https://www.moyu.moe/search?q=${encodeURIComponent(gameTitle)}` }, { value: '2dfan_preview', text: '2dfan(预览)', url: `https://2dfan.com/subjects/search?keyword=${encodeURIComponent(gameTitle)}` } ]; const urls = sites.filter(site => selectedSitesValues.includes(site.value)).map(site => site.url); if (urls.length === 0) { alert("请选择至少一个多搜索站点。"); return; } // 设置窗口尺寸和间隔 const gap = 10; const winWidth = Math.floor(screen.width / urls.length); // 等分屏幕宽度 const winHeight = 1600; const totalWidth = winWidth * urls.length + gap * (urls.length -1 ); // 修正 totalWidth 计算 // 屏幕正中位置 const leftStart = Math.floor((screen.width - totalWidth) / 2); const topPos = Math.floor((screen.height - winHeight) / 2); previewWindows = []; // Reset the array before opening new windows urls.forEach((url, index) => { const leftPos = leftStart + index * (winWidth + gap); const features = `width=${winWidth},height=${winHeight},left=${leftPos},top=${topPos},resizable=yes,scrollbars=yes`; const newWin = window.open(url, '_blank', features); if (newWin) { previewWindows.push(newWin); newWin.onload = () => { newWin.document.addEventListener('click', function(event) { if (event.target.tagName === 'A') { event.preventDefault(); // 阻止默认链接行为在弹窗中打开 const href = event.target.href; closePreviewWindows(); // 关闭所有弹窗 window.open(href, '_blank'); // 在新标签页中打开链接 } }); }; } else { console.warn("弹窗被拦截,无法打开:", url); } }); // 延迟 2 秒启动定时器,监控焦点 focusMonitorDelayTimer = setTimeout(() => { startFocusMonitor(); }, 2000); } // 关闭所有预览窗口 function closePreviewWindows() { stopFocusMonitor(); if (focusMonitorDelayTimer) { clearTimeout(focusMonitorDelayTimer); focusMonitorDelayTimer = null; } previewWindows.forEach(win => { if (win && !win.closed) { win.close(); } }); previewWindows = []; } // --- 焦点监控逻辑 --- function startFocusMonitor() { if (!monitorInterval) { monitorInterval = setInterval(monitorFocus, 300); } } function stopFocusMonitor() { if (monitorInterval) { clearInterval(monitorInterval); monitorInterval = null; } } function monitorFocus() { for (let i = 0; i < previewWindows.length; i++) { if (previewWindows[i] && previewWindows[i].closed) { closePreviewWindows(); return; } } if (!document.hasFocus()) { let previewWindowFocused = false; for (let win of previewWindows) { if (win && !win.closed && win.document.hasFocus()) { previewWindowFocused = true; break; } } if (!previewWindowFocused) { closePreviewWindows(); } } } } })();