视频下载器

捕捉浏览器页面中的视频,支持webm转mp4格式并下载

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         视频下载器
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  捕捉浏览器页面中的视频,支持webm转mp4格式并下载
// @author       You
// @match        *://*/*
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/ffmpeg.min.js
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 视频捕捉函数
    function captureVideos() {
        const videos = [];
        const videoElements = document.querySelectorAll('video');
        
        videoElements.forEach((video, index) => {
            let videoUrl = '';
            
            // 直接获取src属性
            if (video.src && video.src !== '') {
                videoUrl = video.src;
            } 
            // 从source元素获取
            else {
                const sources = video.querySelectorAll('source');
                if (sources.length > 0) {
                    videoUrl = sources[0].src;
                }
            }
            
            // 处理blob URL
            if (videoUrl.startsWith('blob:')) {
                // 尝试从网络请求中获取原始URL
                // 这部分可能需要更复杂的处理
                console.log('发现blob URL,需要特殊处理:', videoUrl);
            }
            
            if (videoUrl) {
                videos.push({
                    id: index,
                    url: videoUrl,
                    type: getVideoType(videoUrl),
                    element: video
                });
            }
        });
        
        return videos;
    }
    
    // 获取视频类型
    function getVideoType(url) {
        if (url.includes('.mp4') || url.includes('video/mp4')) return 'mp4';
        if (url.includes('.webm') || url.includes('video/webm')) return 'webm';
        if (url.includes('.m3u8') || url.includes('application/x-mpegURL')) return 'm3u8';
        if (url.includes('.flv') || url.includes('video/x-flv')) return 'flv';
        return 'unknown';
    }
    
    // 视频格式转换函数
    async function convertVideo(videoUrl, fromType, toType = 'mp4') {
        if (fromType === toType) {
            // 如果格式已经是目标格式,直接返回原始URL
            return videoUrl;
        }
        
        if (fromType !== 'webm' || toType !== 'mp4') {
            // 目前只支持webm转mp4
            console.log('目前只支持webm转mp4格式');
            return videoUrl;
        }
        
        try {
            // 初始化FFmpeg
            const { createFFmpeg, fetchFile } = FFmpeg;
            const ffmpeg = createFFmpeg({
                log: true,
                corePath: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/ffmpeg-core.js'
            });
            
            await ffmpeg.load();
            
            // 下载视频文件
            const inputFileName = `input.${fromType}`;
            const outputFileName = `output.${toType}`;
            
            // 从URL获取视频数据
            const response = await fetch(videoUrl);
            const arrayBuffer = await response.arrayBuffer();
            
            // 写入到FFmpeg文件系统
            ffmpeg.FS('writeFile', inputFileName, new Uint8Array(arrayBuffer));
            
            // 执行转换命令
            await ffmpeg.run('-i', inputFileName, outputFileName);
            
            // 获取转换后的文件数据
            const data = ffmpeg.FS('readFile', outputFileName);
            
            // 创建Blob URL
            const blob = new Blob([data.buffer], { type: `video/${toType}` });
            const convertedUrl = URL.createObjectURL(blob);
            
            // 清理资源
            ffmpeg.FS('unlink', inputFileName);
            ffmpeg.FS('unlink', outputFileName);
            
            return convertedUrl;
        } catch (error) {
            console.error('视频转换失败:', error);
            return videoUrl; // 转换失败时返回原始URL
        }
    }
    
    // 视频下载函数
    function downloadVideo(videoUrl, fileName = '', fileType = 'mp4') {
        // 生成默认文件名
        if (!fileName) {
            const timestamp = new Date().getTime();
            fileName = `video_${timestamp}.${fileType}`;
        }
        
        // 添加文件扩展名
        if (!fileName.includes('.')) {
            fileName += `.${fileType}`;
        }
        
        try {
            // 使用GM_download下载
            GM_download({
                url: videoUrl,
                name: fileName,
                saveAs: true, // 显示保存对话框
                onload: function() {
                    console.log('视频下载完成:', fileName);
                },
                onerror: function(error) {
                    console.error('视频下载失败:', error);
                    // 如果GM_download失败,尝试使用原生下载
                    fallbackDownload(videoUrl, fileName);
                }
            });
        } catch (error) {
            console.error('GM_download失败:', error);
            // 回退到原生下载
            fallbackDownload(videoUrl, fileName);
        }
    }
    
    // 原生下载回退函数
    function fallbackDownload(videoUrl, fileName) {
        const link = document.createElement('a');
        link.href = videoUrl;
        link.download = fileName;
        link.style.display = 'none';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        
        // 对于blob URL,需要清理
        if (videoUrl.startsWith('blob:')) {
            setTimeout(() => {
                URL.revokeObjectURL(videoUrl);
            }, 1000);
        }
    }
    
    // 完整的视频下载流程
    async function processAndDownload(video, convertToMp4 = true) {
        let videoUrl = video.url;
        let fileType = video.type;
        
        // 如果需要转换为mp4且当前不是mp4格式
        if (convertToMp4 && video.type !== 'mp4') {
            console.log('正在转换视频格式...');
            const convertedUrl = await convertVideo(video.url, video.type, 'mp4');
            videoUrl = convertedUrl;
            fileType = 'mp4';
        }
        
        // 下载视频
        console.log('开始下载视频...');
        downloadVideo(videoUrl, '', fileType);
    }
    
    // 创建用户界面
    function createUI() {
        // 检查界面是否已存在
        if (document.getElementById('video-downloader-ui')) {
            return;
        }
        
        // 创建UI容器
        const container = document.createElement('div');
        container.id = 'video-downloader-ui';
        container.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            width: 350px;
            max-height: 500px;
            background: rgba(255, 255, 255, 0.95);
            border: 1px solid #ccc;
            border-radius: 8px;
            padding: 15px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            z-index: 9999;
            font-family: Arial, sans-serif;
            overflow-y: auto;
        `;
        
        // 创建标题
        const title = document.createElement('h3');
        title.textContent = '视频下载器';
        title.style.cssText = `
            margin: 0 0 15px 0;
            font-size: 18px;
            color: #333;
        `;
        container.appendChild(title);
        
        // 创建捕捉按钮
        const captureBtn = document.createElement('button');
        captureBtn.textContent = '捕捉视频';
        captureBtn.id = 'capture-videos-btn';
        captureBtn.style.cssText = `
            width: 100%;
            padding: 10px;
            margin-bottom: 15px;
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        `;
        container.appendChild(captureBtn);
        
        // 创建自动转换选项
        const convertOption = document.createElement('div');
        convertOption.style.cssText = `
            margin-bottom: 15px;
            display: flex;
            align-items: center;
        `;
        
        const convertCheckbox = document.createElement('input');
        convertCheckbox.type = 'checkbox';
        convertCheckbox.id = 'auto-convert-checkbox';
        convertCheckbox.checked = true;
        convertCheckbox.style.marginRight = '8px';
        
        const convertLabel = document.createElement('label');
        convertLabel.htmlFor = 'auto-convert-checkbox';
        convertLabel.textContent = '自动转换为MP4格式';
        convertLabel.style.fontSize = '14px';
        
        convertOption.appendChild(convertCheckbox);
        convertOption.appendChild(convertLabel);
        container.appendChild(convertOption);
        
        // 创建视频列表容器
        const videoList = document.createElement('div');
        videoList.id = 'video-list';
        videoList.style.cssText = `
            max-height: 300px;
            overflow-y: auto;
        `;
        container.appendChild(videoList);
        
        // 添加到页面
        document.body.appendChild(container);
        
        // 添加关闭按钮
        const closeBtn = document.createElement('button');
        closeBtn.textContent = '×';
        closeBtn.style.cssText = `
            position: absolute;
            top: 5px;
            right: 10px;
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            color: #666;
        `;
        
        closeBtn.addEventListener('click', () => {
            container.style.display = 'none';
        });
        container.appendChild(closeBtn);
        
        // 绑定捕捉按钮事件
        captureBtn.addEventListener('click', () => {
            const videos = captureVideos();
            displayVideos(videos);
        });
    }
    
    // 显示视频列表
    function displayVideos(videos) {
        const videoList = document.getElementById('video-list');
        videoList.innerHTML = '';
        
        if (videos.length === 0) {
            videoList.innerHTML = '<p style="text-align: center; color: #666; padding: 20px;">未找到视频</p>';
            return;
        }
        
        videos.forEach((video, index) => {
            const videoItem = document.createElement('div');
            videoItem.style.cssText = `
                background: #f9f9f9;
                padding: 10px;
                margin-bottom: 10px;
                border-radius: 4px;
                border: 1px solid #eee;
            `;
            
            // 视频信息
            const videoInfo = document.createElement('div');
            videoInfo.style.cssText = `
                font-size: 12px;
                margin-bottom: 8px;
                color: #666;
                word-break: break-all;
            `;
            
            const videoUrlShort = video.url.length > 50 ? video.url.substring(0, 50) + '...' : video.url;
            videoInfo.innerHTML = `
                <p>视频 ${index + 1}</p>
                <p>格式: ${video.type}</p>
                <p>URL: ${videoUrlShort}</p>
            `;
            
            // 下载按钮容器
            const buttonContainer = document.createElement('div');
            buttonContainer.style.cssText = `
                display: flex;
                gap: 5px;
            `;
            
            // 原始下载按钮
            const downloadBtn = document.createElement('button');
            downloadBtn.textContent = '下载';
            downloadBtn.style.cssText = `
                flex: 1;
                padding: 6px;
                background: #2196F3;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 14px;
            `;
            
            downloadBtn.addEventListener('click', () => {
                processAndDownload(video, false);
            });
            
            // 转换下载按钮
            const convertDownloadBtn = document.createElement('button');
            convertDownloadBtn.textContent = '转换下载';
            convertDownloadBtn.style.cssText = `
                flex: 1;
                padding: 6px;
                background: #FF9800;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 14px;
            `;
            
            convertDownloadBtn.addEventListener('click', () => {
                processAndDownload(video, true);
            });
            
            // 添加按钮
            buttonContainer.appendChild(downloadBtn);
            buttonContainer.appendChild(convertDownloadBtn);
            
            // 添加到视频项
            videoItem.appendChild(videoInfo);
            videoItem.appendChild(buttonContainer);
            
            // 添加到列表
            videoList.appendChild(videoItem);
        });
    }
    
    // 脚本加载完成后执行
    window.addEventListener('load', () => {
        // 延迟创建UI,确保页面已完全加载
        setTimeout(createUI, 1000);
    });

})();