// ==UserScript== // @name YouTube 本地B站弹幕播放器 // @namespace https://github.com/ZBpine/bilibili-danmaku-download/ // @version 1.0.1 // @description 加载本地 B站弹幕 JSON文件,在 YouTube 视频上显示 // @author ZBpine // @match https://www.youtube.com/watch* // @grant none // @license MIT // @run-at document-end // @downloadURL none // ==/UserScript== (async () => { 'use strict'; // 创建按钮 function insertPlayerButton() { function baseButtonStyle() { return ` padding: 6px 10px; background: #555; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; width: 100px; `; } const panel = document.createElement('div'); Object.assign(panel.style, { position: 'fixed', left: '-110px', bottom: '40px', zIndex: '9999', transition: 'left 0.3s ease-in-out, opacity 0.3s ease', opacity: '0.2', display: 'flex', flexDirection: 'column', gap: '8px', background: '#333', borderRadius: '0px 20px 20px 0px', padding: '10px', width: '110px' }); panel.addEventListener('mouseenter', () => { panel.style.left = '0px'; panel.style.opacity = '1'; }); panel.addEventListener('mouseleave', () => { panel.style.left = '-110px'; panel.style.opacity = '0.2'; }); const loadBtn = document.createElement('button'); loadBtn.textContent = '📂 载入弹幕'; loadBtn.style.cssText = baseButtonStyle(); const toggleBtn = document.createElement('button'); toggleBtn.textContent = '✅ 弹幕开'; toggleBtn.style.cssText = baseButtonStyle(); panel.appendChild(loadBtn); panel.appendChild(toggleBtn); document.body.appendChild(panel); toggleBtn.onclick = () => { dmPlayer.toggle(); toggleBtn.textContent = dmPlayer.danmakuEnabled ? '✅ 弹幕开' : '❌ 弹幕关'; }; const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.style.display = 'none'; document.body.appendChild(fileInput); loadBtn.onclick = () => fileInput.click(); fileInput.addEventListener('change', (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { try { const json = JSON.parse(e.target.result); // ✅ 当用户载入 json 文件 dmPlayer.init(); dmPlayer.load(json.danmakuData); const count = json.danmakuData.length; const title = json.videoData?.title || '(未知标题)'; const readableTime = json.fetchtime ? new Date(json.fetchtime * 1000).toLocaleString('zh-CN', { hour12: false }) : '(未知)'; dmPlayer.logTag(`🎉 已载入:\n🎬 ${title}\n💬 共 ${count} 条弹幕\n🕒 抓取时间:${readableTime}`); alert(`🎉 已载入:\n🎬 ${title}\n💬 共 ${count} 条弹幕\n🕒 抓取时间:${readableTime}`); } catch (err) { dmPlayer.logTagError('❌ 弹幕 JSON 加载失败', err); alert('❌ 弹幕 JSON 加载失败:' + err.message); } }; reader.readAsText(file); }); } const urlOfPlayer = 'https://cdn.jsdelivr.net/gh/ZBpine/bili-danmaku-statistic/docs/BiliDanmakuPlayer.js'; const { BiliDanmakuPlayer } = await import(urlOfPlayer); const dmPlayer = new BiliDanmakuPlayer(); insertPlayerButton(); })();