您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
一键将 Bilibili 或 YouTube 视频转换为Get笔记,并提供大强远程支持链接。
当前为
// ==UserScript== // @name B站/油管&视频转Get笔记 // @namespace http://tampermonkey.net/ // @version 2.2.0 // @description 一键将 Bilibili 或 YouTube 视频转换为Get笔记,并提供大强远程支持链接。 // @author ChatGPT & Gemini // @match https://www.bilibili.com/video/* // @match https://www.youtube.com/watch* // @match https://www.biji.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_openInTab // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; const CONFIG = { debug: true }; const logger = { log: (msg, ...args) => CONFIG.debug && console.log(`[视频转Get笔记] ${msg}`, ...args), error: (msg, ...args) => console.error(`[视频转Get笔记] ${msg}`, ...args), }; const PageType = { BILIBILI_VIDEO: 'bilibili_video', YOUTUBE_VIDEO: 'youtube_video', GET_NOTE: 'get_note', UNKNOWN: 'unknown' }; function getCurrentPageType() { const host = window.location.hostname; const pathname = window.location.pathname; if (host === 'www.biji.com') return PageType.GET_NOTE; if (host === 'www.bilibili.com' && pathname.startsWith('/video/')) return PageType.BILIBILI_VIDEO; if (host === 'www.youtube.com' && pathname === '/watch') return PageType.YOUTUBE_VIDEO; return PageType.UNKNOWN; } function extractBilibiliVideoInfo() { const currentUrl = window.location.href.split('?')[0]; let videoTitle = document.title.replace(/_哔哩哔哩 \(゜-゜\)つロ 干杯~-bilibili/, '').trim(); const titleElement = document.querySelector('.video-title.van-ellipsis') || document.querySelector('.tit'); if (titleElement?.textContent) videoTitle = titleElement.textContent.trim(); return { url: currentUrl, title: videoTitle, platform: 'Bilibili' }; } function extractYouTubeVideoInfo() { const currentUrl = window.location.href.split('&')[0]; let videoTitle = document.title.replace(/ - YouTube$/, '').trim(); const titleElement = document.querySelector('#title h1.ytd-watch-metadata'); if (titleElement?.textContent) videoTitle = titleElement.textContent.trim(); return { url: currentUrl, title: videoTitle, platform: 'YouTube' }; } function createStyles() { const styles = ` .gn-to-get-btn { position: fixed; z-index: 9999; background: rgba(0, 122, 255, 0.95); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); color: white; border: none; border-radius: 14px; padding: 12px 24px; font-size: 15px; font-weight: 600; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; cursor: pointer; box-shadow: 0 4px 20px rgba(0, 122, 255, 0.25), 0 1px 3px rgba(0, 0, 0, 0.1); transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); border: 0.5px solid rgba(255, 255, 255, 0.1); letter-spacing: -0.02em; user-select: none; -webkit-user-select: none; } .gn-to-get-btn:hover { transform: translateY(-1px) scale(1.02); box-shadow: 0 6px 25px rgba(0, 122, 255, 0.35), 0 2px 8px rgba(0, 0, 0, 0.15); } .gn-to-get-btn:active { transform: translateY(0px) scale(0.98); box-shadow: 0 2px 10px rgba(0, 122, 255, 0.3); transition: all 0.1s ease; } .gn-to-get-btn:disabled { opacity: 0.8; cursor: not-allowed; transform: none; } /* B站按钮样式 */ .gn-to-get-single-bili { top: 50%; right: 24px; transform: translateY(-50%); background: rgba(252, 98, 142, 0.95); box-shadow: 0 4px 20px rgba(252, 98, 142, 0.25); } .gn-to-get-single-bili:hover { background: rgba(252, 98, 142, 1); box-shadow: 0 6px 25px rgba(252, 98, 142, 0.35); } /* YouTube按钮样式 */ .gn-to-get-single-youtube { top: 50%; right: 24px; transform: translateY(-50%); background: rgba(255, 0, 0, 0.95); box-shadow: 0 4px 20px rgba(255, 0, 0, 0.25); } .gn-to-get-single-youtube:hover { background: rgba(255, 0, 0, 1); box-shadow: 0 6px 25px rgba(255, 0, 0, 0.35); } /* --- 广告/支持按钮样式 (已修改为蓝色) --- */ .gn-ad-btn { top: calc(50% + 65px); /* 动态计算位置,放在主按钮下方 */ right: 24px; transform: translateY(-50%); background: rgba(0, 122, 255, 0.85); /* <-- 修改为蓝色 */ backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); box-shadow: 0 2px 10px rgba(0, 122, 255, 0.2); /* <-- 修改为蓝色阴影 */ font-size: 13px; padding: 8px 18px; } .gn-ad-btn:hover { background: rgba(0, 122, 255, 1); /* <-- 修改为更亮的蓝色 */ box-shadow: 0 4px 15px rgba(0, 122, 255, 0.25); /* <-- 修改为更深的蓝色阴影 */ } /* Get笔记状态提示 */ #get-note-status { position: fixed; top: 24px; right: 24px; z-index: 10000; background: rgba(28, 28, 30, 0.95); backdrop-filter: blur(20px); color: white; padding: 12px 20px; border-radius: 16px; font-size: 15px; font-weight: 500; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); cursor: default; transition: all 0.3s ease; border: 0.5px solid rgba(255, 255, 255, 0.1); user-select: none; animation: slideInRight 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); } @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } `; const styleSheet = document.createElement('style'); styleSheet.textContent = styles; document.head.appendChild(styleSheet); } // 视频页面的转换器 (通用) class VideoPageConverter { constructor(pageType) { this.pageType = pageType; } init() { this.createButtons(); } createButtons() { // --- 1. 创建主功能按钮 (转Get笔记) --- const mainButton = document.createElement('button'); mainButton.textContent = '转Get笔记'; const platformClass = this.pageType === PageType.YOUTUBE_VIDEO ? 'gn-to-get-single-youtube' : 'gn-to-get-single-bili'; mainButton.className = `gn-to-get-btn ${platformClass}`; mainButton.addEventListener('click', () => { logger.log(`主按钮点击,平台: ${this.pageType}`); let videoInfo = (this.pageType === PageType.YOUTUBE_VIDEO) ? extractYouTubeVideoInfo() : extractBilibiliVideoInfo(); if (!videoInfo.url) { alert(`错误:无法获取当前 ${videoInfo.platform} 视频链接!`); return; } GM_setValue('singleUrl', videoInfo.url); GM_setValue('singleTitle', videoInfo.title); GM_setValue('platform', videoInfo.platform); GM_setValue('conversionMode', 'single_new_tab'); GM_setValue('initTime', Date.now().toString()); GM_openInTab('https://www.biji.com', true); mainButton.textContent = '已发送 ✓'; mainButton.disabled = true; setTimeout(() => { mainButton.textContent = '转Get笔记'; mainButton.disabled = false; }, 2000); }); document.body.appendChild(mainButton); // --- 2. 创建广告按钮 --- const adButton = document.createElement('button'); adButton.textContent = '大强远程支持'; adButton.className = 'gn-to-get-btn gn-ad-btn'; adButton.addEventListener('click', () => { logger.log('广告按钮点击,跳转到 742112.xyz'); GM_openInTab('https://742112.xyz', true); }); document.body.appendChild(adButton); } } // Get笔记页面的自动处理程序 (这部分代码无需修改) class GetNoteAutoProcessor { async init() { const mode = GM_getValue('conversionMode'); const initTime = GM_getValue('initTime'); const isScriptTriggered = initTime && (Date.now() - parseInt(initTime)) < 60000; if (isScriptTriggered && mode === 'single_new_tab') { await this.handleSingleConversion(); } } async handleSingleConversion() { const url = GM_getValue('singleUrl'), title = GM_getValue('singleTitle'), platform = GM_getValue('platform') || '视频'; this.cleanupStorage(); this.createStatusIndicator(); if (!url) { this.updateStatus('❌ 错误:未找到视频URL', '#dc3545'); return; } try { this.updateStatus('🚀 准备转换...', '#007bff'); await this.waitForPageLoad(); this.updateStatus('🔐 正在获取认证信息...', '#007bff'); const authInfo = await this.extractAuthInfo(); if (!authInfo.token && !authInfo.cookies) throw new Error('获取认证信息失败,请确保您已登录'); this.updateStatus(`🔄 正在转换 ${platform} 视频...`, '#007bff'); const result = await this.callApiWithAuth(url, title, platform, authInfo); if (result?.noteId) { this.updateStatus(`✅ 转换成功!3秒后刷新...`, '#28a745'); setTimeout(() => window.location.reload(), 3000); } else { throw new Error('API转换失败或未返回笔记ID'); } } catch (error) { logger.error('单条视频转换失败:', error); let userMessage = `❌ 转换失败: ${error.message}`; if (error.message.includes('超时')) userMessage += '。请检查网络或稍后再试。'; else if (error.message.includes('认证失败')) userMessage += '。请刷新页面确保您已登录。'; this.updateStatus(userMessage, '#dc3545'); } } cleanupStorage() { GM_setValue('singleUrl', ''); GM_setValue('singleTitle', ''); GM_setValue('platform', ''); GM_setValue('conversionMode', ''); GM_setValue('initTime', ''); } async callApiWithAuth(url, title, platform, authInfo) { const requestData = { attachments: [{ size: 100, type: "link", title: title || `${platform}视频`, url: url }], content: "", entry_type: "ai", note_type: "link", source: "web", prompt_template_id: "" }; const headers = { 'Content-Type': 'application/json', 'Accept': 'text/event-stream', 'Origin': 'https://www.biji.com', 'Referer': 'https://www.biji.com/', 'User-Agent': navigator.userAgent, 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }; if (authInfo.cookies) headers['Cookie'] = authInfo.cookies; if (authInfo.token) headers['Authorization'] = `Bearer ${authInfo.token}`; return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://get-notes.luojilab.com/voicenotes/web/notes/stream', headers: headers, data: JSON.stringify(requestData), timeout: 60000, onload: (response) => { if (response.status === 200) { const lines = response.responseText.split('\n'); let noteId = null; for (const line of lines) { if (line.startsWith('data: ')) { try { const data = JSON.parse(line.substring(6)); if (data.data?.note_id) { noteId = data.data.note_id; break; } } catch (e) {} } } if (noteId) resolve({ noteId, url }); else reject(new Error('未从API响应中找到笔记ID')); } else { let msg = `API请求失败: ${response.status}`; if (response.status === 403) msg += " (认证失败)"; reject(new Error(msg)); } }, onerror: (err) => reject(new Error('网络请求失败,请检查代理或网络连接')), ontimeout: () => reject(new Error('API 请求超时 (60秒)')) }); }); } async extractAuthInfo() { const authInfo = { cookies: document.cookie, token: null }; try { authInfo.token = localStorage.getItem('token') || localStorage.getItem('auth_token') || localStorage.getItem('access_token'); if (!authInfo.token) authInfo.token = sessionStorage.getItem('token') || sessionStorage.getItem('auth_token') || sessionStorage.getItem('access_token'); if (!authInfo.token) { const match = document.cookie.match(/(?:^|;)\s*(?:token|auth_token|jwt|access_token)=([^;]*)/); if (match) authInfo.token = match[1]; } } catch (error) { logger.error('提取认证信息失败:', error); } return authInfo; } createStatusIndicator() { if (document.getElementById('get-note-status')) return; const indicator = document.createElement('div'); indicator.id = 'get-note-status'; indicator.textContent = '🔄 脚本已激活'; document.body.appendChild(indicator); } updateStatus(message, color = '#28a745') { const indicator = document.getElementById('get-note-status'); if (indicator) { indicator.textContent = message; indicator.style.background = color; } } async waitForPageLoad() { if (document.readyState === 'complete') return; return new Promise(resolve => { window.addEventListener('load', resolve, { once: true }); setTimeout(resolve, 10000); }); } } // 页面管理器 (这部分代码无需修改) class PageManager { constructor() { this.currentUrl = location.href; this.urlCheckTimer = null; } init() { this.runForPage(); const pageType = getCurrentPageType(); if (pageType === PageType.BILIBILI_VIDEO || pageType === PageType.YOUTUBE_VIDEO) { this.startUrlMonitoring(); } } runForPage() { const pageType = getCurrentPageType(); logger.log(`当前页面类型: ${pageType}`); switch (pageType) { case PageType.BILIBILI_VIDEO: case PageType.YOUTUBE_VIDEO: document.querySelectorAll('.gn-to-get-btn').forEach(btn => btn.remove()); new VideoPageConverter(pageType).init(); break; case PageType.GET_NOTE: new GetNoteAutoProcessor().init(); break; } } startUrlMonitoring() { this.urlCheckTimer = setInterval(() => { if (location.href !== this.currentUrl) { this.currentUrl = location.href; logger.log('URL发生变化,重新运行脚本逻辑:', this.currentUrl); setTimeout(() => this.runForPage(), 500); } }, 1000); } destroy() { if (this.urlCheckTimer) clearInterval(this.urlCheckTimer); } } // 主程序初始化 (async function() { createStyles(); if (document.readyState === 'loading') await new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve)); const pageManager = new PageManager(); pageManager.init(); window.addEventListener('beforeunload', () => pageManager.destroy()); })().catch(error => { logger.error('脚本初始化失败:', error); }); })();