您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
YouTube 首页/搜索分段缩略图网格100%修复,Gemini一键总结/字幕
当前为
// ==UserScript== // @name YouTube to Gemini 自动总结与字幕 // @namespace http://tampermonkey.net/ // @version 2.0 // @description YouTube 首页/搜索分段缩略图网格100%修复,Gemini一键总结/字幕 // @author hengyu (优化 by Assistant) // @match *://www.youtube.com/* // @match *://gemini.google.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_addStyle // @run-at document-start // @license MIT // ==/UserScript== (function() { 'use strict'; // --- 终极分段网格修复 CSS --- // 只对首页和搜索结果页面应用网格布局修复 GM_addStyle(` /* 首页和搜索页面网格布局 */ body[data-is-home-page="true"] ytd-rich-grid-renderer > #contents, body[data-page-subtype="home"] ytd-rich-grid-renderer > #contents, body[data-page-type="search"] ytd-rich-grid-renderer > #contents { display: grid !important; grid-template-columns: repeat(2, 1fr) !important; gap: 24px 16px !important; width: 100% !important; margin: 0 auto !important; --ytd-rich-grid-items-per-row: 2 !important; --ytd-rich-grid-max-width: none !important; } @media (min-width: 1000px) { body[data-is-home-page="true"] ytd-rich-grid-renderer > #contents, body[data-page-subtype="home"] ytd-rich-grid-renderer > #contents, body[data-page-type="search"] ytd-rich-grid-renderer > #contents { grid-template-columns: repeat(3, 1fr) !important; --ytd-rich-grid-items-per-row: 3 !important; } } @media (min-width: 1400px) { body[data-is-home-page="true"] ytd-rich-grid-renderer > #contents, body[data-page-subtype="home"] ytd-rich-grid-renderer > #contents, body[data-page-type="search"] ytd-rich-grid-renderer > #contents { grid-template-columns: repeat(4, 1fr) !important; --ytd-rich-grid-items-per-row: 4 !important; } } @media (min-width: 1700px) { body[data-is-home-page="true"] ytd-rich-grid-renderer > #contents, body[data-page-subtype="home"] ytd-rich-grid-renderer > #contents, body[data-page-type="search"] ytd-rich-grid-renderer > #contents { grid-template-columns: repeat(5, 1fr) !important; --ytd-rich-grid-items-per-row: 5 !important; } } /* 确保只在首页和搜索页面修改布局结构 */ body[data-is-home-page="true"] ytd-rich-grid-row, body[data-is-home-page="true"] ytd-rich-grid-row > #contents, body[data-is-home-page="true"] ytd-rich-grid-row > #dismissible, body[data-is-home-page="true"] ytd-rich-grid-row > div, body[data-is-home-page="true"] ytd-rich-grid-row > #dismissible > #contents, body[data-is-home-page="true"] ytd-rich-grid-row > div > #contents, body[data-is-home-page="true"] ytd-rich-grid-row > div > #dismissible, body[data-is-home-page="true"] ytd-rich-grid-row > div > #dismissible > #contents, body[data-is-home-page="true"] ytd-rich-grid-row > .ytd-rich-grid-row, body[data-is-home-page="true"] ytd-rich-grid-row > div > .ytd-rich-grid-row, body[data-is-home-page="true"] ytd-rich-grid-row > div > div, body[data-is-home-page="true"] ytd-rich-grid-row > div > div > #contents, body[data-page-subtype="home"] ytd-rich-grid-row, body[data-page-subtype="home"] ytd-rich-grid-row > #contents, body[data-page-subtype="home"] ytd-rich-grid-row > #dismissible, body[data-page-subtype="home"] ytd-rich-grid-row > div, body[data-page-subtype="home"] ytd-rich-grid-row > #dismissible > #contents, body[data-page-subtype="home"] ytd-rich-grid-row > div > #contents, body[data-page-subtype="home"] ytd-rich-grid-row > div > #dismissible, body[data-page-subtype="home"] ytd-rich-grid-row > div > #dismissible > #contents, body[data-page-subtype="home"] ytd-rich-grid-row > .ytd-rich-grid-row, body[data-page-subtype="home"] ytd-rich-grid-row > div > .ytd-rich-grid-row, body[data-page-subtype="home"] ytd-rich-grid-row > div > div, body[data-page-subtype="home"] ytd-rich-grid-row > div > div > #contents, body[data-page-type="search"] ytd-rich-grid-row, body[data-page-type="search"] ytd-rich-grid-row > #contents, body[data-page-type="search"] ytd-rich-grid-row > #dismissible, body[data-page-type="search"] ytd-rich-grid-row > div, body[data-page-type="search"] ytd-rich-grid-row > #dismissible > #contents, body[data-page-type="search"] ytd-rich-grid-row > div > #contents, body[data-page-type="search"] ytd-rich-grid-row > div > #dismissible, body[data-page-type="search"] ytd-rich-grid-row > div > #dismissible > #contents, body[data-page-type="search"] ytd-rich-grid-row > .ytd-rich-grid-row, body[data-page-type="search"] ytd-rich-grid-row > div > .ytd-rich-grid-row, body[data-page-type="search"] ytd-rich-grid-row > div > div, body[data-page-type="search"] ytd-rich-grid-row > div > div > #contents { display: contents !important; } /* 视频项修复 - 仅限首页和搜索页面 */ body[data-is-home-page="true"] ytd-rich-item-renderer, body[data-is-home-page="true"] ytd-grid-video-renderer, body[data-is-home-page="true"] ytd-rich-grid-media, body[data-page-subtype="home"] ytd-rich-item-renderer, body[data-page-subtype="home"] ytd-grid-video-renderer, body[data-page-subtype="home"] ytd-rich-grid-media, body[data-page-type="search"] ytd-rich-item-renderer, body[data-page-type="search"] ytd-grid-video-renderer, body[data-page-type="search"] ytd-rich-grid-media { width: 100% !important; max-width: none !important; min-width: 0 !important; margin: 0 !important; box-sizing: border-box !important; } /* 弹性项 - 仅首页和搜索页面 */ body[data-is-home-page="true"] ytd-rich-grid-renderer > #contents > ytd-rich-section-renderer, body[data-is-home-page="true"] ytd-rich-grid-renderer > #contents > ytd-reel-shelf-renderer, body[data-page-subtype="home"] ytd-rich-grid-renderer > #contents > ytd-rich-section-renderer, body[data-page-subtype="home"] ytd-rich-grid-renderer > #contents > ytd-reel-shelf-renderer, body[data-page-type="search"] ytd-rich-grid-renderer > #contents > ytd-rich-section-renderer, body[data-page-type="search"] ytd-rich-grid-renderer > #contents > ytd-reel-shelf-renderer { grid-column: 1 / -1 !important; width: 100% !important; margin: 16px 0 !important; } /* 搜索页面修复 */ ytd-search ytd-video-renderer { display: block !important; position: relative !important; z-index: 1 !important; } ytd-search ytd-thumbnail { position: relative !important; z-index: 5 !important; } `); // --- Gemini 按钮与交互 --- const PROMPT_KEY = 'geminiPrompt'; const TITLE_KEY = 'videoTitle'; const ORIGINAL_TITLE_KEY = 'geminiOriginalVideoTitle'; const TIMESTAMP_KEY = 'timestamp'; const ACTION_TYPE_KEY = 'geminiActionType'; const VIDEO_TOTAL_DURATION_KEY = 'geminiVideoTotalDuration'; const FIRST_SEGMENT_END_TIME_KEY = 'geminiFirstSegmentEndTime'; const SUMMARY_BUTTON_ID = 'gemini-summarize-btn'; const SUBTITLE_BUTTON_ID = 'gemini-subtitle-btn'; const THUMBNAIL_BUTTON_CLASS = 'gemini-thumbnail-btn'; const THUMBNAIL_PROCESSED_FLAG = 'data-gemini-processed'; const YOUTUBE_NOTIFICATION_ID = 'gemini-yt-notification'; const YOUTUBE_CONFIRMATION_ID = 'gemini-yt-confirmation'; // 恢复原始通知样式 const YOUTUBE_NOTIFICATION_STYLE = { position: 'fixed', bottom: '20px', left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0,0,0,0.85)', color: 'white', padding: '15px 35px 15px 20px', borderRadius: '8px', zIndex: '99999', maxWidth: 'calc(100% - 40px)', textAlign: 'left', boxSizing: 'border-box', whiteSpace: 'pre-wrap', boxShadow: '0 4px 12px rgba(0,0,0,0.3)' }; const YOUTUBE_CONFIRMATION_STYLE = { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', backgroundColor: 'rgba(33, 33, 33, 0.95)', color: 'white', padding: '20px 25px', borderRadius: '12px', zIndex: '999999', maxWidth: 'calc(100% - 60px)', minWidth: '300px', boxSizing: 'border-box', boxShadow: '0 8px 24px rgba(0,0,0,0.5)', display: 'flex', flexDirection: 'column', gap: '15px' }; // 缩略图按钮样式 GM_addStyle(` .${THUMBNAIL_BUTTON_CLASS} { position: absolute; top: 5px; right: 5px; background-color: rgba(0, 0, 0, 0.7); color: white; border: none; border-radius: 4px; padding: 4px 8px; font-size: 12px; cursor: pointer; z-index: 120; display: flex; align-items: center; opacity: 0; transition: opacity 0.2s ease; pointer-events: auto !important; } #dismissible:hover .${THUMBNAIL_BUTTON_CLASS}, ytd-grid-video-renderer:hover .${THUMBNAIL_BUTTON_CLASS}, ytd-video-renderer:hover .${THUMBNAIL_BUTTON_CLASS}, ytd-rich-item-renderer:hover .${THUMBNAIL_BUTTON_CLASS}, ytd-compact-video-renderer:hover .${THUMBNAIL_BUTTON_CLASS}, ytd-playlist-video-renderer:hover .${THUMBNAIL_BUTTON_CLASS}, ytd-reel-item-renderer:hover .${THUMBNAIL_BUTTON_CLASS}, ytd-search ytd-video-renderer:hover .${THUMBNAIL_BUTTON_CLASS} { opacity: 1 !important; visibility: visible !important; pointer-events: auto !important; } .${THUMBNAIL_BUTTON_CLASS}:hover { background-color: rgba(0, 0, 0, 0.9); opacity: 1 !important; visibility: visible !important; } .gemini-confirmation-btn { padding: 8px 20px; border-radius: 4px; border: none; cursor: pointer; font-weight: 500; font-size: 14px; transition: background-color 0.2s ease; } .gemini-confirmation-confirm { background-color: #1a73e8; color: white; } .gemini-confirmation-confirm:hover { background-color: #0d65d9; } .gemini-confirmation-cancel { background-color: #5f6368; color: white; margin-right: 10px; } .gemini-confirmation-cancel:hover { background-color: #494c50; } `); // 辅助函数 function showNotification(elementId, message, styles, duration = 15000) { let existing = document.getElementById(elementId); if (existing) { clearTimeout(parseInt(existing.dataset.timeoutId)); existing.remove(); } const notif = document.createElement('div'); notif.id = elementId; notif.textContent = message; Object.assign(notif.style, styles); document.body.appendChild(notif); const btn = document.createElement('button'); btn.textContent = '✕'; Object.assign(btn.style, { position: 'absolute', top: '5px', right: '10px', background: 'transparent', border: 'none', color: 'inherit', fontSize: '16px', cursor: 'pointer', padding: '0', lineHeight: '1' }); btn.onclick = () => notif.remove(); notif.appendChild(btn); notif.dataset.timeoutId = setTimeout(() => notif.remove(), duration).toString(); return notif; } function showConfirmation(elementId, title, message, videoInfo, onConfirm, onCancel, styles) { let existing = document.getElementById(elementId); if (existing) existing.remove(); const dialog = document.createElement('div'); dialog.id = elementId; Object.assign(dialog.style, styles); document.body.appendChild(dialog); const titleElem = document.createElement('h3'); titleElem.textContent = title; titleElem.style.margin = '0 0 10px 0'; titleElem.style.fontSize = '18px'; const messageElem = document.createElement('div'); messageElem.textContent = message; messageElem.style.marginBottom = '15px'; messageElem.style.fontSize = '14px'; const videoTitleElem = document.createElement('div'); videoTitleElem.textContent = `视频标题: ${videoInfo.title}`; videoTitleElem.style.marginBottom = '5px'; videoTitleElem.style.fontWeight = 'bold'; const videoIdElem = document.createElement('div'); videoIdElem.textContent = `视频ID: ${videoInfo.id}`; videoIdElem.style.fontSize = '12px'; videoIdElem.style.color = '#aaa'; videoIdElem.style.marginBottom = '15px'; const buttonsContainer = document.createElement('div'); buttonsContainer.style.display = 'flex'; buttonsContainer.style.justifyContent = 'flex-end'; buttonsContainer.style.gap = '10px'; const cancelBtn = document.createElement('button'); cancelBtn.textContent = '取消'; cancelBtn.className = 'gemini-confirmation-btn gemini-confirmation-cancel'; cancelBtn.onclick = () => { dialog.remove(); if (onCancel) onCancel(); }; const confirmBtn = document.createElement('button'); confirmBtn.textContent = '确认'; confirmBtn.className = 'gemini-confirmation-btn gemini-confirmation-confirm'; confirmBtn.onclick = () => { dialog.remove(); if (onConfirm) onConfirm(videoInfo); }; buttonsContainer.appendChild(cancelBtn); buttonsContainer.appendChild(confirmBtn); dialog.appendChild(titleElem); dialog.appendChild(messageElem); dialog.appendChild(videoTitleElem); dialog.appendChild(videoIdElem); dialog.appendChild(buttonsContainer); return dialog; } function copyToClipboard(text) { navigator.clipboard.writeText(text).catch(() => { const ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); } catch {} document.body.removeChild(ta); }); } function isVideoPage() { return window.location.pathname === '/watch' && new URLSearchParams(window.location.search).has('v'); } // 验证YouTube视频ID格式 function isValidYouTubeVideoId(id) { // YouTube视频ID通常是11位字符,由字母、数字、下划线和连字符组成 return id && typeof id === 'string' && /^[A-Za-z0-9_-]{11}$/.test(id); } function getVideoInfoFromElement(element) { if (element.hasAttribute(THUMBNAIL_PROCESSED_FLAG)) return null; let videoId = ''; let videoTitle = ''; // 优先从数据属性中提取视频ID if (element.dataset && element.dataset.videoId) { videoId = element.dataset.videoId; } else if (element.getAttribute('video-id')) { videoId = element.getAttribute('video-id'); } // 尝试从各种可能的属性和元素中提取视频ID if (!isValidYouTubeVideoId(videoId)) { // 1. 首先从链接中查找 const linkElements = Array.from(element.querySelectorAll('a[href*="/watch?v="]')); for (const link of linkElements) { const match = link.href.match(/\/watch\?v=([^&]+)/); if (match && match[1] && isValidYouTubeVideoId(match[1])) { videoId = match[1]; break; } } // 2. 从缩略图元素寻找 if (!isValidYouTubeVideoId(videoId)) { const thumbnailElements = element.querySelectorAll('img[src*="/vi/"], img[src*="i.ytimg.com"]'); for (const img of thumbnailElements) { const match = img.src.match(/\/vi\/([^\/]+)\//) || img.src.match(/\/([A-Za-z0-9_-]{11})\/[\w]+\.jpg/); if (match && match[1] && isValidYouTubeVideoId(match[1])) { videoId = match[1]; break; } } } // 3. 从缩略图容器data属性获取 if (!isValidYouTubeVideoId(videoId)) { const thumbnails = element.querySelectorAll('#thumbnail, .thumbnail, ytd-thumbnail'); for (const thumb of thumbnails) { if (thumb.dataset && thumb.dataset.videoId && isValidYouTubeVideoId(thumb.dataset.videoId)) { videoId = thumb.dataset.videoId; break; } // 来自href属性 if (thumb.tagName === 'A' && thumb.href) { const match = thumb.href.match(/\/watch\?v=([^&]+)/); if (match && match[1] && isValidYouTubeVideoId(match[1])) { videoId = match[1]; break; } } } } // 4. 从视频渲染器元素获取 if (!isValidYouTubeVideoId(videoId)) { const renderers = element.closest('ytd-rich-item-renderer, ytd-grid-video-renderer, ytd-video-renderer, ytd-compact-video-renderer'); if (renderers && renderers.dataset && renderers.dataset.videoId && isValidYouTubeVideoId(renderers.dataset.videoId)) { videoId = renderers.dataset.videoId; } } } // 增强标题提取方法 const titleSelectors = [ '#video-title', '.title', '[title]', 'h3 a', 'h3', 'a[title]', 'span[title]', 'yt-formatted-string', '[aria-label]' ]; for (const selector of titleSelectors) { const titleElements = element.querySelectorAll(selector); for (const titleElement of titleElements) { const possibleTitle = titleElement.textContent?.trim() || titleElement.getAttribute('title')?.trim() || titleElement.getAttribute('aria-label')?.trim(); if (possibleTitle && possibleTitle.length > 5) { videoTitle = possibleTitle; break; } } if (videoTitle) break; } // 最后验证结果 if (!isValidYouTubeVideoId(videoId) || !videoTitle) { return null; } return { id: videoId, title: videoTitle, url: `https://www.youtube.com/watch?v=${videoId}` }; } function processVideoSummary(videoInfo) { const prompt = `请分析这个YouTube视频: ${videoInfo.url}\n\n提供一个全面的摘要,包括主要观点、关键见解和视频中讨论的重要细节,以结构化的方式分解内容,并包括任何重要的结论或要点。`; GM_setValue(PROMPT_KEY, prompt); GM_setValue(TITLE_KEY, videoInfo.title); GM_setValue(ORIGINAL_TITLE_KEY, videoInfo.title); GM_setValue(TIMESTAMP_KEY, Date.now()); GM_setValue(ACTION_TYPE_KEY, 'summary'); window.open('https://gemini.google.com/', '_blank'); showNotification( YOUTUBE_NOTIFICATION_ID, `已跳转到 Gemini!\n系统将尝试自动输入提示词并发送请求。\n\n视频: "${videoInfo.title}"\n\n(如果自动操作失败,提示词已复制到剪贴板,请手动粘贴)`, YOUTUBE_NOTIFICATION_STYLE, 10000 ); copyToClipboard(prompt); } function handleThumbnailButtonClick(event, videoInfo) { if (event) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); // 确保不会触发原始点击事件 if (event.cancelable) event.returnValue = false; } if (!videoInfo || !videoInfo.url || !videoInfo.title) { showNotification( YOUTUBE_NOTIFICATION_ID, "无法获取视频信息,请尝试直接在视频页面使用总结功能。", { ...YOUTUBE_NOTIFICATION_STYLE, backgroundColor: '#d93025' }, 5000 ); return false; } // 验证视频ID if (!isValidYouTubeVideoId(videoInfo.id)) { showNotification( YOUTUBE_NOTIFICATION_ID, `获取到的视频ID格式无效: ${videoInfo.id}\n请尝试直接在视频页面使用总结功能。`, { ...YOUTUBE_NOTIFICATION_STYLE, backgroundColor: '#d93025' }, 5000 ); return false; } // 显示确认对话框 - 保留缩略图总结的二次确认 showConfirmation( YOUTUBE_CONFIRMATION_ID, "确认视频信息", "请确认以下视频信息是否正确:", videoInfo, processVideoSummary, null, YOUTUBE_CONFIRMATION_STYLE ); return false; } function addThumbnailButtons() { if (isVideoPage()) return; const isSearchPage = window.location.pathname === '/results'; const videoElementSelectors = [ 'ytd-rich-item-renderer', 'ytd-grid-video-renderer', 'ytd-video-renderer', 'ytd-compact-video-renderer', 'ytd-playlist-video-renderer', 'ytd-reel-item-renderer', '.ytd-video-preview', '.video-card', '.ytd-compact-playlist-renderer', 'ytd-grid-playlist-renderer' ]; if (isSearchPage) videoElementSelectors.push('ytd-search ytd-video-renderer'); document.querySelectorAll(videoElementSelectors.join(',')).forEach(element => { if (element.hasAttribute(THUMBNAIL_PROCESSED_FLAG) || element.querySelector(`.${THUMBNAIL_BUTTON_CLASS}`)) { element.setAttribute(THUMBNAIL_PROCESSED_FLAG, 'true'); return; } // 增强缩略图容器选择 let thumbnailContainer = isSearchPage ? (element.querySelector('ytd-thumbnail') || element.querySelector('a#thumbnail') || element.querySelector('[id="thumbnail"]')) : element.querySelector('#thumbnail, .thumbnail, a[href*="/watch"], ytd-thumbnail'); if (!thumbnailContainer) return; const videoInfo = getVideoInfoFromElement(element); if (!videoInfo) return; const button = document.createElement('button'); button.className = THUMBNAIL_BUTTON_CLASS; button.textContent = '📝 总结'; button.title = '使用Gemini总结此视频'; // 增强事件处理 const eventHandler = (e) => { if (e.type === 'click') { return handleThumbnailButtonClick(e, videoInfo); } else { e.stopPropagation(); e.preventDefault(); if (e.cancelable) e.returnValue = false; return false; } }; ['click', 'mousedown', 'mouseup', 'touchstart', 'touchend'].forEach(type => { button.addEventListener(type, eventHandler, { capture: true, passive: false }); }); if (getComputedStyle(thumbnailContainer).position === 'static') { thumbnailContainer.style.position = 'relative'; } if (isSearchPage) { Object.assign(button.style, { zIndex: '999', pointerEvents: 'auto', position: 'absolute', top: '5px', right: '5px' }); } thumbnailContainer.appendChild(button); element.setAttribute(THUMBNAIL_PROCESSED_FLAG, 'true'); }); } function setupThumbnailButtonSystem() { addThumbnailButtons(); const obs = new MutationObserver(() => setTimeout(addThumbnailButtons, 300)); obs.observe(document.body, { childList: true, subtree: true }); setInterval(() => { if (!isVideoPage()) addThumbnailButtons(); }, 1500); window.addEventListener('load', () => setTimeout(addThumbnailButtons, 1000)); } function addYouTubeActionButtons() { if (!isVideoPage()) { removeYouTubeActionButtonsIfExists(); return; } if (document.getElementById(SUMMARY_BUTTON_ID) || document.getElementById(SUBTITLE_BUTTON_ID)) return; // 寻找顶部导航区域 - 搜索框右侧的区域 const container = document.querySelector('#end') || document.querySelector('ytd-masthead #container') || document.querySelector('ytd-masthead'); if (!container) return; // 创建一个容器包装我们的按钮 const buttonsWrapper = document.createElement('div'); buttonsWrapper.style.display = 'inline-flex'; buttonsWrapper.style.alignItems = 'center'; buttonsWrapper.style.marginRight = '16px'; const subtitleButton = document.createElement('button'); subtitleButton.id = SUBTITLE_BUTTON_ID; subtitleButton.textContent = '🎯 生成字幕'; Object.assign(subtitleButton.style, { backgroundColor: '#28a745', color: 'white', border: 'none', borderRadius: '18px', padding: '0 16px', margin: '0 8px 0 0', cursor: 'pointer', fontWeight: '500', height: '36px', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: '14px', zIndex: '100', whiteSpace: 'nowrap', transition: 'all 0.2s ease' }); const summaryButton = document.createElement('button'); summaryButton.id = SUMMARY_BUTTON_ID; summaryButton.textContent = '📝 Gemini摘要'; Object.assign(summaryButton.style, { backgroundColor: '#1a73e8', color: 'white', border: 'none', borderRadius: '18px', padding: '0 16px', margin: '0', cursor: 'pointer', fontWeight: '500', height: '36px', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: '14px', zIndex: '100', whiteSpace: 'nowrap', transition: 'all 0.2s ease' }); // 为移动设备添加响应式适应 const mediaQuery = window.matchMedia('(max-width: 768px)'); const adjustForMobile = () => { if (mediaQuery.matches) { subtitleButton.style.fontSize = '12px'; subtitleButton.style.padding = '0 10px'; subtitleButton.style.height = '32px'; summaryButton.style.fontSize = '12px'; summaryButton.style.padding = '0 10px'; summaryButton.style.height = '32px'; } else { subtitleButton.style.fontSize = '14px'; subtitleButton.style.padding = '0 16px'; subtitleButton.style.height = '36px'; summaryButton.style.fontSize = '14px'; summaryButton.style.padding = '0 16px'; summaryButton.style.height = '36px'; } }; mediaQuery.addEventListener('change', adjustForMobile); adjustForMobile(); subtitleButton.addEventListener('click', handleGenerateSubtitlesClick); summaryButton.addEventListener('click', handleSummarizeClick); buttonsWrapper.appendChild(subtitleButton); buttonsWrapper.appendChild(summaryButton); // 将按钮添加到顶部导航区域 // 在创建按钮之前插入 const createButton = container.querySelector('#create-icon') || container.querySelector('button[aria-label*="创建"]'); if (createButton) { container.insertBefore(buttonsWrapper, createButton); } else { // 如果找不到创建按钮,就插入到容器前面 container.insertBefore(buttonsWrapper, container.firstChild); } } // 视频页面摘要函数 - 移除确认步骤,直接处理 function handleSummarizeClick() { const youtubeUrl = window.location.href; // 从URL获取视频ID并验证 const urlParams = new URLSearchParams(window.location.search); const videoId = urlParams.get('v'); if (!isValidYouTubeVideoId(videoId)) { showNotification( YOUTUBE_NOTIFICATION_ID, "无法获取有效的视频ID,请确认当前是否在YouTube视频页面。", { ...YOUTUBE_NOTIFICATION_STYLE, backgroundColor: '#d93025' }, 5000 ); return; } // 增强标题选择 const titleSelectors = [ 'h1.ytd-watch-metadata', '#video-title', '#title h1', '.title', 'yt-formatted-string.ytd-watch-metadata' ]; let videoTitle = ''; for (const selector of titleSelectors) { const titleElement = document.querySelector(selector); if (titleElement) { videoTitle = titleElement.textContent?.trim(); if (videoTitle) break; } } if (!videoTitle) { videoTitle = document.title.replace(/ - YouTube$/, '').trim() || 'Unknown Video'; } const videoInfo = { id: videoId, title: videoTitle, url: youtubeUrl }; // 直接处理,不显示确认对话框 processVideoSummary(videoInfo); } // 完全保留原始字幕生成函数 function handleGenerateSubtitlesClick() { const youtubeUrl = window.location.href; const titleElement = document.querySelector('h1.ytd-watch-metadata, #video-title, #title h1, .title'); const videoTitle = titleElement?.textContent?.trim() || document.title.replace(/ - YouTube$/, '').trim() || 'Unknown Video'; let videoDurationInSeconds = 0; const durationMeta = document.querySelector('meta[itemprop="duration"]'); if (durationMeta?.content) { const match = durationMeta.content.match(/PT(\d+H)?(\d+M)?(\d+S)?/); if (match) { videoDurationInSeconds = 0; if (match[1]) videoDurationInSeconds += parseInt(match[1].replace('H', '')) * 3600; if (match[2]) videoDurationInSeconds += parseInt(match[2].replace('M', '')) * 60; if (match[3]) videoDurationInSeconds += parseInt(match[3].replace('S', '')); } } if (videoDurationInSeconds <= 0) { showNotification(YOUTUBE_NOTIFICATION_ID, "无法获取视频时长,无法启动字幕任务。", { ...YOUTUBE_NOTIFICATION_STYLE, backgroundColor: '#d93025' }, 15000); return; } const firstSegmentEnd = Math.min(videoDurationInSeconds, 1200); const prompt = `${youtubeUrl}\n1.不要添加自己的语言\n2.变成简体中文,流畅版本。\n\nYouTube\n请提取此视频从00:00:00到${new Date(firstSegmentEnd * 1000).toISOString().substr(11, 8)}的完整字幕文本。`; GM_setValue(PROMPT_KEY, prompt); GM_setValue(TITLE_KEY, `${videoTitle} (字幕 00:00:00-${new Date(firstSegmentEnd * 1000).toISOString().substr(11, 8)})`); GM_setValue(ORIGINAL_TITLE_KEY, videoTitle); GM_setValue(TIMESTAMP_KEY, Date.now()); GM_setValue(ACTION_TYPE_KEY, 'subtitle'); GM_setValue(VIDEO_TOTAL_DURATION_KEY, videoDurationInSeconds); GM_setValue(FIRST_SEGMENT_END_TIME_KEY, firstSegmentEnd); showNotification(YOUTUBE_NOTIFICATION_ID, `已跳转到 Gemini 生成字幕: 00:00:00 - ${new Date(firstSegmentEnd * 1000).toISOString().substr(11, 8)}...\n"${videoTitle}"`, YOUTUBE_NOTIFICATION_STYLE, 15000); window.open('https://gemini.google.com/', '_blank'); copyToClipboard(prompt); } function removeYouTubeActionButtonsIfExists() { [SUMMARY_BUTTON_ID, SUBTITLE_BUTTON_ID].forEach(id => { const button = document.getElementById(id); if (button) button.remove(); }); } // --- 页面类型检测函数 --- function detectYouTubePageType() { // 确保body元素存在 if (!document.body) return; // 确定页面类型 let isHomePage = window.location.pathname === '/' || window.location.pathname === '/feed/subscriptions'; let isChannelPage = window.location.pathname.includes('/channel/') || window.location.pathname.includes('/c/') || window.location.pathname.includes('/user/') || window.location.pathname.includes('/@'); let isSearchPage = window.location.pathname === '/results'; // 添加页面类型属性 if (isHomePage) { document.body.setAttribute('data-is-home-page', 'true'); document.body.setAttribute('data-page-subtype', 'home'); } else { document.body.removeAttribute('data-is-home-page'); } if (isChannelPage) { document.body.setAttribute('data-page-subtype', 'channels'); } else if (isSearchPage) { document.body.setAttribute('data-page-type', 'search'); } } // --- 页面初始化 --- if (window.location.hostname.includes('www.youtube.com')) { // 检测页面类型并设置相应的属性 detectYouTubePageType(); // 当URL变化时重新检测页面类型 let lastUrl = location.href; const urlObserver = new MutationObserver(() => { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(() => { detectYouTubePageType(); if (isVideoPage()) { addYouTubeActionButtons(); } else { removeYouTubeActionButtonsIfExists(); } }, 1000); } }); urlObserver.observe(document, { subtree: true, childList: true }); if (document.readyState === 'complete' || document.readyState === 'interactive') { setupThumbnailButtonSystem(); setTimeout(addYouTubeActionButtons, 1000); // 每隔一段时间检查视频页面按钮 setInterval(() => { if (isVideoPage()) { if (!document.getElementById(SUMMARY_BUTTON_ID) || !document.getElementById(SUBTITLE_BUTTON_ID)) { addYouTubeActionButtons(); } } // 重新检测页面类型 detectYouTubePageType(); }, 5000); } else { document.addEventListener('DOMContentLoaded', () => { detectYouTubePageType(); setupThumbnailButtonSystem(); setTimeout(addYouTubeActionButtons, 1000); }, { once: true }); } } else if (window.location.hostname.includes('gemini.google.com')) { // Gemini自动填充 - 简化版,删除了所有自动分段处理 const prompt = GM_getValue(PROMPT_KEY); const timestamp = GM_getValue(TIMESTAMP_KEY, 0); const actionType = GM_getValue(ACTION_TYPE_KEY); // 检查是否有有效的待处理请求且来源是YouTube const referrerIsYouTube = document.referrer.includes('youtube.com'); if (prompt && actionType && Date.now() - timestamp <= 300000 && referrerIsYouTube) { setTimeout(() => { const textarea = document.querySelector('textarea, div[contenteditable="true"]'); if (textarea) { if (textarea.isContentEditable) textarea.textContent = prompt; else textarea.value = prompt; textarea.dispatchEvent(new Event('input', { bubbles: true, cancelable: true })); textarea.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); setTimeout(() => { const sendBtn = document.querySelector('button[aria-label*="Send"],button[aria-label*="发送"],button[aria-label*="提交"],button[aria-label*="Run"],button[aria-label*="Submit"]'); if (sendBtn && !sendBtn.disabled) { sendBtn.click(); // 单次操作完成后清理数据 setTimeout(() => { GM_deleteValue(PROMPT_KEY); GM_deleteValue(TITLE_KEY); GM_deleteValue(ORIGINAL_TITLE_KEY); GM_deleteValue(TIMESTAMP_KEY); GM_deleteValue(ACTION_TYPE_KEY); GM_deleteValue(VIDEO_TOTAL_DURATION_KEY); GM_deleteValue(FIRST_SEGMENT_END_TIME_KEY); }, 5000); // 延迟清理以确保提交后处理完成 } }, 500); } }, 1200); } } })();