H5视频播放器截图工具

适用于所有H5播放器的截图脚本,快捷键Alt+1

目前為 2025-07-14 提交的版本,檢視 最新版本

// ==UserScript==
// @name         H5视频播放器截图工具
// @version      2025.07.14.6
// @description  适用于所有H5播放器的截图脚本,快捷键Alt+1
// @author       嘉友友
// @match        *://www.youtube.com/*
// @match        *://www.bilibili.com/*
// @license      GPL-3.0
// @namespace https://greasyfork.org/users/1336389
// ==/UserScript==

(function() {
    'use strict';

    // 创建截图功能
    function captureVideoFrame() {
        // 查找页面中的视频元素
        const videos = document.querySelectorAll('video');
        
        if (videos.length === 0) {
            showMessage('未找到视频播放器!', 'error');
            return;
        }

        // 选择第一个可见的视频元素
        let targetVideo = null;
        for (let video of videos) {
            const rect = video.getBoundingClientRect();
            if (rect.width > 0 && rect.height > 0 && !video.hidden && video.readyState >= 2) {
                targetVideo = video;
                break;
            }
        }

        if (!targetVideo) {
            showMessage('未找到正在播放的视频!', 'error');
            return;
        }

        try {
            // 创建canvas元素
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            
            // 设置canvas尺寸为视频原始尺寸
            canvas.width = targetVideo.videoWidth || targetVideo.clientWidth;
            canvas.height = targetVideo.videoHeight || targetVideo.clientHeight;
            
            // 检查视频是否已加载
            if (targetVideo.readyState < 2) {
                showMessage('视频尚未加载完成,请稍后再试!', 'warning');
                return;
            }

            // 将视频当前帧绘制到canvas
            ctx.drawImage(targetVideo, 0, 0, canvas.width, canvas.height);
            
            // 转换为PNG格式的blob
            canvas.toBlob(function(blob) {
                if (!blob) {
                    showMessage('截图失败,可能是由于CORS限制!', 'error');
                    return;
                }
                
                // 创建下载链接
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                
                // 生成文件名(包含时间戳和视频源信息)
                const now = new Date();
                const timestamp = now.getFullYear() + 
                    String(now.getMonth() + 1).padStart(2, '0') + 
                    String(now.getDate()).padStart(2, '0') + '_' +
                    String(now.getHours()).padStart(2, '0') + 
                    String(now.getMinutes()).padStart(2, '0') + 
                    String(now.getSeconds()).padStart(2, '0');
                
                // 获取域名作为文件名的一部分
                const domain = window.location.hostname.replace(/\./g, '_');
                a.download = `${domain}_video_${timestamp}.png`;
                
                // 触发下载
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                
                // 清理URL对象
                URL.revokeObjectURL(url);
                
                // 显示成功消息
                const resolution = `${canvas.width}x${canvas.height}`;
                showMessage(`📸 截图成功!分辨率: ${resolution}`, 'success');
                
            }, 'image/png', 1.0); // 无损PNG格式,质量为1.0
            
        } catch (error) {
            console.error('截图错误:', error);
            showMessage('截图失败: ' + error.message, 'error');
        }
    }

    // 显示提示消息
    function showMessage(text, type = 'info') {
        const messageDiv = document.createElement('div');
        messageDiv.textContent = text;
        
        let bgColor;
        switch(type) {
            case 'success':
                bgColor = 'rgba(40, 167, 69, 0.9)';
                break;
            case 'error':
                bgColor = 'rgba(220, 53, 69, 0.9)';
                break;
            case 'warning':
                bgColor = 'rgba(255, 193, 7, 0.9)';
                break;
            default:
                bgColor = 'rgba(0, 0, 0, 0.8)';
        }
        
        messageDiv.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${bgColor};
            color: white;
            padding: 12px 20px;
            border-radius: 6px;
            font-size: 14px;
            font-family: Arial, sans-serif;
            z-index: 999999;
            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
            max-width: 300px;
            word-wrap: break-word;
            transition: all 0.3s ease;
        `;
        
        document.body.appendChild(messageDiv);
        
        // 3秒后自动消失
        setTimeout(() => {
            messageDiv.style.opacity = '0';
            messageDiv.style.transform = 'translateX(20px)';
            setTimeout(() => {
                if (document.body.contains(messageDiv)) {
                    document.body.removeChild(messageDiv);
                }
            }, 300);
        }, 3000);
    }

    // 获取当前页面视频信息
    function getVideoInfo() {
        const videos = document.querySelectorAll('video');
        if (videos.length === 0) {
            return '当前页面暂无视频';
        }
        
        let info = `发现 ${videos.length} 个视频元素:\n`;
        videos.forEach((video, index) => {
            const rect = video.getBoundingClientRect();
            const isVisible = rect.width > 0 && rect.height > 0 && !video.hidden;
            const isReady = video.readyState >= 2;
            const resolution = video.videoWidth ? `${video.videoWidth}x${video.videoHeight}` : '未知';
            info += `视频${index + 1}: ${resolution}, ${isVisible ? '可见' : '隐藏'}, ${isReady ? '已就绪' : '未就绪'}\n`;
        });
        
        return info;
    }

    // 监听键盘事件
    document.addEventListener('keydown', function(event) {
        // 检查是否按下Alt+1
        if (event.altKey && event.code === 'Digit1') {
            event.preventDefault();
            captureVideoFrame();
        }
    });

    // 注册Tampermonkey菜单命令
    GM_registerMenuCommand('📸 截取视频截图', captureVideoFrame);
    GM_registerMenuCommand('📺 查看视频信息', function() {
        const info = getVideoInfo();
        showMessage(info, 'info');
    });

    // 页面加载完成后的初始化
    function initialize() {
        const videos = document.querySelectorAll('video');
        if (videos.length > 0) {
            showMessage('H5视频截图工具已就绪\n快捷键: Alt+1\n或使用右键菜单', 'success');
        }
        
        // 为现有视频添加加载事件监听
        videos.forEach(video => {
            if (video.readyState < 2) {
                video.addEventListener('loadeddata', function() {
                    showMessage('视频加载完成,可以截图了!', 'info');
                }, { once: true });
            }
        });
    }

    // 监听动态添加的视频元素
    const observer = new MutationObserver(function(mutations) {
        let hasNewVideo = false;
        
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList') {
                const addedNodes = Array.from(mutation.addedNodes);
                const newVideos = addedNodes.filter(node => 
                    node.tagName === 'VIDEO' || 
                    (node.querySelectorAll && node.querySelectorAll('video').length > 0)
                );
                
                if (newVideos.length > 0) {
                    hasNewVideo = true;
                }
            }
        });
        
        if (hasNewVideo) {
            setTimeout(() => {
                showMessage('检测到新视频加载', 'info');
            }, 1000);
        }
    });

    // 页面加载状态检查
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            setTimeout(initialize, 1000);
        });
    } else {
        setTimeout(initialize, 1000);
    }

    // 开始观察DOM变化
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    console.log('H5视频截图工具已加载');
})();