您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
通用嗅探 Bangumi 页面的视频链接(支持主流平台),并在链接下方添加可展开/收起的播放器。
// ==UserScript== // @name Bangumi 视频链接展开 // @name:en Bangumi Universal Video Expander // @namespace https://github.com/gemini/bangumi-video-embedder // @version 1.0.4 // @description 通用嗅探 Bangumi 页面的视频链接(支持主流平台),并在链接下方添加可展开/收起的播放器。 // @description:en Universally sniffs video links on Bangumi and adds a collapsible player below the link. // @author wataame // @match *://bgm.tv/* // @match *://bangumi.tv/* // @match *://chii.in/* // @license MIT // @icon https://bgm.tv/img/favicon.ico // ==/UserScript== (function() { 'use strict'; function addGlobalStyle(css) { const head = document.head || document.getElementsByTagName('head')[0]; if (!head) { return; } const style = document.createElement('style'); style.type = 'text/css'; style.textContent = css; head.appendChild(style); } // --- 样式定义 --- addGlobalStyle(` .bve-wrapper { position: relative; height: 0; overflow: hidden; max-width: 100%; background: #000; margin: 10px 0; border-radius: 8px; } .bve-wrapper-horizontal { padding-bottom: 56.25%; } .bve-wrapper-vertical { padding-bottom: 100%; max-width: 320px; margin-left: auto; margin-right: auto; } .bve-wrapper iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0; } .bve-play-button { display: inline-block; margin: 0 5px; padding: 1px 7px; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; line-height: 1.5; vertical-align: middle; user-select: none; transition: background-color 0.2s; background-color: #f09199; color: #fff; } .bve-play-button:hover { background-color: #e07179; } .bve-play-button.bve-expanded { background-color: #e07179; } .bve-play-button.bve-expanded:hover { background-color: #d16a71; } `); // --- 正则表达式 & 工具函数 --- const YOUTUBE_REGEX = /(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([\w-]{11})/; const BILIBILI_BV_REGEX = /(?:bilibili\.com\/(?:video\/|player\.html\?.*?bvid=))(BV[a-zA-Z0-9_]+)/; const BILIBILI_AV_REGEX = /(?:bilibili\.com\/(?:video\/av|player\.html\?.*?aid=))(\d+)/; const NICONICO_REGEX = /(?:nicovideo\.jp\/watch\/|nico\.ms\/)((?:[a-z]{2})?\d+)/; const ACFUN_REGEX = /(?:acfun\.cn\/v\/)(ac\d+)/; const VIMEO_REGEX = /(?:vimeo\.com\/)(\d+)/; const TIKTOK_REGEX = /(?:tiktok\.com\/(?:@.+?\/video\/|v\/)|open\.tiktok\.com\/embed\/video\/)(\d+)/; const DOUYIN_REGEX = /(?:douyin\.com\/video\/|open\.douyin\.com\/player\/video\?vid=)(\d+)/; const isVideoUrl = (url) => url && ( YOUTUBE_REGEX.test(url) || BILIBILI_BV_REGEX.test(url) || BILIBILI_AV_REGEX.test(url) || NICONICO_REGEX.test(url) || ACFUN_REGEX.test(url) || VIMEO_REGEX.test(url) || TIKTOK_REGEX.test(url) || DOUYIN_REGEX.test(url) ); /** * 创建播放器 */ function createPlayer(url) { let iframeSrc = null; let playerType = 'horizontal'; const ytMatch = url.match(YOUTUBE_REGEX); const biliBvMatch = url.match(BILIBILI_BV_REGEX); const biliAvMatch = url.match(BILIBILI_AV_REGEX); const nicoMatch = url.match(NICONICO_REGEX); const acfunMatch = url.match(ACFUN_REGEX); const vimeoMatch = url.match(VIMEO_REGEX); const tiktokMatch = url.match(TIKTOK_REGEX); const douyinMatch = url.match(DOUYIN_REGEX); if (ytMatch && ytMatch[1]) { iframeSrc = `https://www.youtube.com/embed/${ytMatch[1]}?rel=0&autoplay=1`; } else if (biliBvMatch && biliBvMatch[1]) { iframeSrc = `//player.bilibili.com/player.html?bvid=${biliBvMatch[1]}&page=1&as_wide=1&high_quality=1&danmaku=0&autoplay=1`; } else if (biliAvMatch && biliAvMatch[1]) { iframeSrc = `//player.bilibili.com/player.html?aid=${biliAvMatch[1]}&page=1&as_wide=1&high_quality=1&danmaku=0&autoplay=1`; } else if (nicoMatch && nicoMatch[1]) { iframeSrc = `https://embed.nicovideo.jp/watch/${nicoMatch[1]}?autoplay=1&oldScript=1`; } else if (acfunMatch && acfunMatch[1]) { iframeSrc = `https://www.acfun.cn/player/${acfunMatch[1]}`; } else if (vimeoMatch && vimeoMatch[1]) { iframeSrc = `https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=1`; } else if (tiktokMatch && tiktokMatch[1]) { iframeSrc = `https://www.tiktok.com/embed/${tiktokMatch[1]}?autoplay=1`; playerType = 'vertical'; } else if (douyinMatch && douyinMatch[1]) { iframeSrc = `https://open.douyin.com/player/video?vid=${douyinMatch[1]}&autoplay=1`; playerType = 'vertical'; } if (iframeSrc) { const wrapper = document.createElement('div'); wrapper.className = `bve-wrapper bve-wrapper-${playerType}`; const iframe = document.createElement('iframe'); iframe.src = iframeSrc; iframe.setAttribute('allow', 'autoplay; accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'); iframe.setAttribute('allowfullscreen', 'true'); iframe.setAttribute('frameborder', '0'); iframe.setAttribute('scrolling', 'no'); wrapper.appendChild(iframe); return wrapper; } return null; } /** * 按钮点击事件处理 */ function togglePlayer(event) { const button = event.currentTarget; const currentState = button.dataset.state; if (currentState === 'expanded') { if (button.playerElement && button.playerElement.parentNode) { button.playerElement.remove(); } delete button.playerElement; button.dataset.state = 'collapsed'; button.textContent = '▶ 播放'; button.classList.remove('bve-expanded'); } else { const player = createPlayer(button.dataset.videoUrl); if (player) { button.after(player); button.playerElement = player; button.dataset.state = 'expanded'; button.textContent = '▲ 收起'; button.classList.add('bve-expanded'); } } } /** * 核心处理函数 */ function processNode(node) { if (node.nodeType !== Node.ELEMENT_NODE || node.closest('.bve-wrapper, .bve-play-button')) { return; } const links = node.querySelectorAll('a:not(.bve-processed)'); links.forEach(link => { link.classList.add('bve-processed'); let urlToProcess = null; if (isVideoUrl(link.href)) { urlToProcess = link.href; } else { const text = link.textContent || ''; const urlRegex = /https?:\/\/[^\s"'<>`]+/g; const urlsInText = text.match(urlRegex) || []; for (const url of urlsInText) { if (isVideoUrl(url)) { urlToProcess = url; break; } } } if (urlToProcess) { const button = document.createElement('span'); button.className = 'bve-play-button'; button.textContent = '▶ 播放'; button.dataset.videoUrl = urlToProcess; button.dataset.state = 'collapsed'; button.addEventListener('click', togglePlayer); link.after(button); } }); } // --- 主执行逻辑 --- processNode(document.body); const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { processNode(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })();