YouTube 一键截图

按 S 键快速截图,智能处理播放列表/直播/加密内容

目前为 2025-02-22 提交的版本。查看 最新版本

// ==UserScript==
// @name         YouTube 一键截图
// @description  按 S 键快速截图,智能处理播放列表/直播/加密内容
// @version      0.2.2
// @author       DeepSeek
// @license      MIT
// @namespace    http://tampermonkey.net/
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==
 
(function() {
    'use strict';
 
    let initialized = false;
    let videoElement = null;
    let observer = null;
 
    const init = () => {
        if (initialized) return;
 
        // 精准定位主播放器视频元素
        videoElement = document.querySelector('#movie_player video');
        if (!videoElement) return;
 
        initialized = true;
        console.log('[截图插件] 初始化成功');
 
        // 动态样式注入
        const style = document.createElement('style');
        style.textContent = `
            .screenshot-toast {
                position: fixed;
                top: 20px;
                right: 20px;
                background: rgba(0,0,0,0.8);
                color: white;
                padding: 12px 24px;
                border-radius: 4px;
                font-family: Arial;
                z-index: 9999;
                animation: fadeIn 0.3s;
            }
            @keyframes fadeIn {
                from { opacity: 0; transform: translateY(-20px); }
                to { opacity: 1; transform: translateY(0); }
            }
        `;
        document.head.appendChild(style);
    };
 
    const showToast = (message, duration=2000) => {
        const toast = document.createElement('div');
        toast.className = 'screenshot-toast';
        toast.textContent = message;
        document.body.appendChild(toast);
        setTimeout(() => toast.remove(), duration);
    };
 
    const getVideoInfo = () => {
        try {
            // 处理直播流
            const isLive = document.querySelector('.ytp-live') !== null;
            
            // 获取精确标题
            const titleElement = document.querySelector('#title h1.ytd-watch-metadata")');
            const title = (titleElement?.textContent || 'untitled')
                .replace(/[/\\:*?"<>|]/g, '') // 过滤非法字符
                .substring(0, 100); // 限制文件名长度
 
            // 播放列表处理
            const playlistItem = document.querySelector(
                'ytd-playlist-panel-renderer #items ytd-playlist-panel-video-renderer[selected]'
            );
            const episode = playlistItem?.querySelector('#text')?.textContent.trim();
 
            // 时间格式处理
            const time = videoElement.currentTime;
            const formatTime = t => {
                const date = new Date(t * 1000);
                return t >= 3600 
                    ? date.toISOString().substr(11, 8).replace(/:/g, '-')
                    : date.toISOString().substr(14, 5).replace(/:/g, '-');
            };
 
            return {
                filename: [
                    title,
                    episode && `EP${episode}`,
                    isLive ? 'LIVE' : formatTime(time)
                ].filter(Boolean).join('_') + '.png',
                isDRM: videoElement.getAttribute('decoding') === 'hardware'
            };
        } catch (e) {
            console.error('[截图插件] 信息获取失败:', e);
            return { filename: `screenshot_${Date.now()}.png`, isDRM: false };
        }
    };
 
    const captureFrame = async () => {
        if (!initialized) {
            showToast('请等待视频加载完成');
            return;
        }
 
        if (videoElement.readyState < 2) {
            showToast('视频尚未就绪...');
            return;
        }
 
        const { filename, isDRM } = getVideoInfo();
        
        if (isDRM) {
            showToast('受保护内容无法截图');
            return;
        }
 
        try {
            const canvas = document.createElement('canvas');
            canvas.width = videoElement.videoWidth;
            canvas.height = videoElement.videoHeight;
            
            const ctx = canvas.getContext('2d');
            ctx.drawImage(videoElement, 0, 0);
 
            canvas.toBlob(blob => {
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = filename;
                a.click();
                URL.revokeObjectURL(url);
                showToast('截图已保存');
            }, 'image/png');
        } catch (e) {
            console.error('[截图插件] 截图失败:', e);
            showToast('截图失败,请重试');
        }
    };
 
    // 智能初始化系统
    const startObserver = () => {
        observer = new MutationObserver(mutations => {
            if (document.querySelector('#movie_player')) {
                init();
                observer.disconnect();
            }
        });
        observer.observe(document, {
            childList: true,
            subtree: true,
            attributes: false
        });
    };
 
    // 事件监听优化
    const handleKeyPress = (e) => {
        if (e.key.toLowerCase() !== 's') return;
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
        if (e.ctrlKey || e.altKey || e.metaKey) return;
 
        e.preventDefault();
        captureFrame();
    };
 
    // 执行初始化
    if (document.readyState === 'complete') {
        startObserver();
    } else {
        window.addEventListener('load', startObserver);
    }
    document.addEventListener('keydown', handleKeyPress);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址