您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
1.豆瓣可直接跳转imdb。可区分剧集与电影并显示必要按钮,imdb页显示家长引导。2.按钮分为3个,分别是imdb首页、系列第一部的首页和跳转该季评分页。3.功能依赖豆瓣信息栏与下方首个同系列作品内的的tt号
// ==UserScript== // @name imdb豆瓣直达 // @namespace http://tampermonkey.net/ // @version 2.3.1 // @description 1.豆瓣可直接跳转imdb。可区分剧集与电影并显示必要按钮,imdb页显示家长引导。2.按钮分为3个,分别是imdb首页、系列第一部的首页和跳转该季评分页。3.功能依赖豆瓣信息栏与下方首个同系列作品内的的tt号 // @author PH365 // @match https://movie.douban.com/subject/* // @match https://www.douban.com/movie/subject/* // @match https://www.douban.com/tv/subject/* // @match https://www.imdb.com/title/* // @icon https://www.google.com/s2/favicons?sz=64&domain=douban.com // @license MIT // @grant GM_addStyle // @grant GM_xmlhttpRequest // ==/UserScript== (function() { 'use strict'; const hostname = window.location.hostname; if (hostname.includes('douban.com')) { initDoubanFeature(); } else if (hostname.includes('imdb.com')) { initIMDbFeature(); } // =================================================================== // ================== 功能 1, 2, 3: 豆瓣直达IMDB ================ // =================================================================== function initDoubanFeature() { const CURRENT_PAGE_BUTTON_ID = 'douban-to-imdb-button'; const SERIES_BUTTON_ID = 'douban-to-series-imdb-button'; const SEASON_RATINGS_BUTTON_ID = 'douban-to-season-ratings-button'; const SERIES_MENU_WRAPPER_ID = 'douban-series-menu-wrapper'; const SERIES_MENU_BUTTON_ID = 'douban-series-menu-button'; const SERIES_MENU_DROPDOWN_ID = 'douban-series-dropdown-menu'; function chineseNumeralToArabic(text) { const map = {'一':1,'二':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9,'十':10,'十一':11,'十二':12,'十三':13,'十四':14,'十五':15,'十六':16,'十七':17,'十八':18,'十九':19,'二十':20}; return map[text] || null; } function getCurrentSeasonNumber() { const title = document.title; let match = title.match(/Season\s+(\d+)/i); if (match) return parseInt(match[1], 10); match = title.match(/第\s*([一二三四五六七八九十]+)\s*季/); if (match && match[1]) return chineseNumeralToArabic(match[1]); return null; } function getSeriesIMDbInfoFromList() { return new Promise(resolve => { const seriesSectionTitle = Array.from(document.querySelectorAll('h2')).find(h => h.textContent.trim().includes('同系列作品')); const firstSeriesLink = seriesSectionTitle?.nextElementSibling?.querySelector('a'); if (!firstSeriesLink?.href) return resolve(null); GM_xmlhttpRequest({ method: "GET", url: firstSeriesLink.href, onload: response => resolve(response.responseText.match(/tt\d{7,}/)?.[0] || null), onerror: () => resolve(null) }); }); } function createInitialButton() { if (document.getElementById(CURRENT_PAGE_BUTTON_ID)) return null; const infoDiv = document.getElementById('info'); if (!infoDiv) return null; const match = (infoDiv.textContent || '').match(/tt\d{7,}/); if (!match) return null; const currentPageId = match[0]; let referenceElement = Array.from(infoDiv.getElementsByTagName('span')).find(span => span.textContent?.includes('IMDb:')); if (!referenceElement) referenceElement = infoDiv.querySelector(`a[href*="${currentPageId}"]`); if (!referenceElement) return null; GM_addStyle(` /* 按钮样式 */ #${CURRENT_PAGE_BUTTON_ID}, #${SERIES_BUTTON_ID}, #${SEASON_RATINGS_BUTTON_ID} { display: inline-block; margin-left: 10px; padding: 1px 8px; font-size: 13px; border-radius: 4px; text-decoration: none; vertical-align: baseline; transition: filter 0.2s; } #${CURRENT_PAGE_BUTTON_ID}:hover, #${SERIES_BUTTON_ID}:hover, #${SEASON_RATINGS_BUTTON_ID}:hover { text-decoration: none; filter: brightness(0.9); } #${CURRENT_PAGE_BUTTON_ID} { background-color: #ff953c; color: #fff !important; } #${SERIES_BUTTON_ID} { background-color: #3B7ABE; color: #fff !important; } #${SEASON_RATINGS_BUTTON_ID} { background-color: #28a745; color: #fff !important; } #${SERIES_MENU_WRAPPER_ID} { position: relative; display: inline-block; } #${SERIES_MENU_BUTTON_ID} { display: inline-block; padding: 1px 8px; font-size: 13px; color: #fff !important; background-color: #6c757d; border-radius: 4px; text-decoration: none; cursor: pointer; transition: filter 0.2s; } #${SERIES_MENU_BUTTON_ID}:hover { filter: brightness(0.9); } /* 系列菜单智能展开的CSS */ #${SERIES_MENU_DROPDOWN_ID} { display: none; position: absolute; left: 0; background-color: white; min-width: 250px; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 6px 12px rgba(0,0,0,0.175); z-index: 1000; max-height: 300px; overflow-y: auto; text-align: left; /* 默认向下展开 */ top: 100%; margin-top: 5px; } /* 向上展开的样式 */ #${SERIES_MENU_DROPDOWN_ID}.is-up { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 5px; } #${SERIES_MENU_DROPDOWN_ID}.is-visible { display: block; } #${SERIES_MENU_DROPDOWN_ID} a { color: #333; padding: 8px 15px; text-decoration: none; display: block; font-size: 13px; white-space: nowrap; transition: background-color 0.2s; } #${SERIES_MENU_DROPDOWN_ID} a:hover { background-color: #f5f5f5; text-decoration: none; } `); const button = document.createElement('a'); button.id = CURRENT_PAGE_BUTTON_ID; button.textContent = '主页'; button.href = `https://www.imdb.com/title/${currentPageId}`; button.target = '_blank'; button.title = '跳转到当前页对应的 IMDb 页面'; referenceElement.after(button); return { button, imdbId: currentPageId }; } async function createAdditionalButtons(initialButton, currentPageId) { const infoDiv = document.getElementById('info'); if (!infoDiv) return; const isTVShow = infoDiv.textContent.includes('集数'); const hasSeriesSection = !!Array.from(document.querySelectorAll('h2')).find(h => h.textContent.trim().includes('同系列作品')); let lastButton = initialButton; if (hasSeriesSection) { const seriesId = await getSeriesIMDbInfoFromList(); const seasonNumber = getCurrentSeasonNumber(); if (seriesId && seriesId !== currentPageId && (!isTVShow || (isTVShow && seasonNumber > 1))) { const seriesButton = document.createElement('a'); seriesButton.id = SERIES_BUTTON_ID; seriesButton.textContent = '系列页'; seriesButton.href = `https://www.imdb.com/title/${seriesId}`; seriesButton.target = '_blank'; seriesButton.title = '跳转到整个系列的 IMDb 页面'; lastButton.after(seriesButton); lastButton = seriesButton; } } if (isTVShow) { const seasonNumber = getCurrentSeasonNumber() ?? 1; const seriesButton = document.getElementById(SERIES_BUTTON_ID); const idForRatings = seriesButton ? seriesButton.href.match(/tt\d{7,}/)[0] : currentPageId; const seasonRatingsButton = document.createElement('a'); seasonRatingsButton.id = SEASON_RATINGS_BUTTON_ID; seasonRatingsButton.textContent = '本季评分'; seasonRatingsButton.href = `https://www.imdb.com/title/${idForRatings}/episodes/?season=${seasonNumber}&ref_=ttep`; seasonRatingsButton.target = '_blank'; seasonRatingsButton.title = `跳转到 IMDb S${seasonNumber} 评分页面`; lastButton.after(seasonRatingsButton); } if(hasSeriesSection) { const seriesMenuRow = createSeriesMenuRow(); if (seriesMenuRow) { let imdbLine = Array.from(infoDiv.children).find(el => el.textContent.includes('IMDb')); if (imdbLine) { // 寻找IMDb行末尾的 <br> 标签作为插入点 let imdbLineBreak = null; let currentNode = imdbLine; while(currentNode.nextSibling) { if(currentNode.nodeName === 'BR') { imdbLineBreak = currentNode; break; } currentNode = currentNode.nextSibling; } if (imdbLineBreak) { imdbLineBreak.after(seriesMenuRow); } else { infoDiv.appendChild(seriesMenuRow); } } else { infoDiv.appendChild(seriesMenuRow); } } } } /** * 创建“系列菜单”的完整行,并绑定定位逻辑 */ function createSeriesMenuRow() { const seriesSectionTitle = Array.from(document.querySelectorAll('h2')).find(h => h.textContent.trim().includes('同系列作品')); if (!seriesSectionTitle) return null; const links = seriesSectionTitle.nextElementSibling?.querySelectorAll('dl > dd > a'); if (!links || links.length === 0) return null; const fragment = document.createDocumentFragment(); const label = document.createElement('span'); label.className = 'pl'; label.textContent = '系列菜单'; fragment.appendChild(label); fragment.appendChild(document.createTextNode(': ')); const menuWrapper = document.createElement('span'); menuWrapper.id = SERIES_MENU_WRAPPER_ID; const menuButton = document.createElement('a'); menuButton.id = SERIES_MENU_BUTTON_ID; menuButton.textContent = '点击展开'; menuButton.href = 'javascript:void(0);'; const dropdown = document.createElement('div'); dropdown.id = SERIES_MENU_DROPDOWN_ID; links.forEach(link => { const item = document.createElement('a'); item.href = link.href; item.textContent = link.textContent.trim(); item.target = '_blank'; dropdown.appendChild(item); }); menuWrapper.appendChild(menuButton); menuWrapper.appendChild(dropdown); fragment.appendChild(menuWrapper); fragment.appendChild(document.createElement('br')); // 定位点击交互逻辑 menuButton.addEventListener('click', (event) => { event.stopPropagation(); // 切换菜单的显示状态 dropdown.classList.toggle('is-visible'); // 如果菜单是隐藏的,则什么都不做 if (!dropdown.classList.contains('is-visible')) { return; } // 如果菜单是可见的,则开始计算位置 dropdown.classList.remove('is-up'); // 先重置为默认向下 const buttonRect = menuButton.getBoundingClientRect(); const dropdownHeight = dropdown.offsetHeight; // 计算按钮下方和上方的可用空间 const spaceBelow = window.innerHeight - buttonRect.bottom; const spaceAbove = buttonRect.top; // 如果下方空间不足,并且上方空间比下方空间更大,则向上展开 if (spaceBelow < dropdownHeight && spaceAbove > spaceBelow) { dropdown.classList.add('is-up'); } }); // 点击外部区域关闭菜单 document.addEventListener('click', () => { if (dropdown.classList.contains('is-visible')) { dropdown.classList.remove('is-visible'); dropdown.classList.remove('is-up'); // 关闭时重置方向 } }); return fragment; } function run() { const initialData = createInitialButton(); if (initialData) { createAdditionalButtons(initialData.button, initialData.imdbId); return true; } return false; } if (run()) return; const observer = new MutationObserver((mutations, obs) => { if (run()) obs.disconnect(); }); observer.observe(document.body, { childList: true, subtree: true }); } // =================================================================== // ================= 功能 4: IMDb页面添加家长指引按钮 ================= // =================================================================== function initIMDbFeature() { const BUTTON_ID = 'imdb-parental-guide-button'; function createIMDbButton() { if (document.getElementById(BUTTON_ID)) return true; const anchor = document.querySelector('[data-testid="hero-subnav-bar-right-block"]'); if (!anchor) return false; const urlMatch = window.location.href.match(/(tt\d{7,})/); if (!urlMatch || !urlMatch[0]) return false; const ttNumber = urlMatch[0]; const parentalGuideUrl = `https://www.imdb.com/title/${ttNumber}/parentalguide/?ref_=tt_stry_pg`; const button = document.createElement('a'); button.id = BUTTON_ID; button.className = 'ipc-chip ipc-chip--on-base'; button.textContent = 'Parental Guide'; button.href = parentalGuideUrl; button.title = '跳转到家长指引页面'; GM_addStyle(` #${BUTTON_ID} { margin-right: 8px !important; color: #FFFFFF !important; font-weight: bold !important; background-color: #4A4A4A !important; transition: background-color 0.2s; } #${BUTTON_ID}:hover { background-color: #606060 !important; color: #FFFFFF !important; } `); anchor.prepend(button); return true; } const observer = new MutationObserver((mutations, obs) => { if (createIMDbButton()) obs.disconnect(); }); observer.observe(document.body, { childList: true, subtree: true }); } })();