订单查询

所有订单均会带售后技术和客服联系方式

请购买后自行联系

一个隐藏视频的小玩意 免费

免费下载

暂无下载链接,请访问原帖获取

内容说明

GitHub:https://github.com/IIIStudio/Copy-B

CNB:https://cnb.cool/IIIStudio/HTML/Copy-B

演示:https://iiistudio.github.io/Copy-B/

测试的文件:

原理是选择通过 copy /b 命令合并的图片+视频文件

过于简单,就不写使用过程了。

点击头像可以更换图片,可以拖拽视频到里面也可以点击上传!



<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Copy /B</title>

    <style>

        body {

            font-family: "Microsoft YaHei", sans-serif;

            max-width: 900px;

            margin: 0 auto;

            padding: 20px;

            text-align: center;

            background-color: #f5f5f5;

        }

        .container {

        }

        .tab-container {

            display: flex;

            margin-bottom: 20px;

        }

        .tab {

            padding: 10px 20px;

            cursor: pointer;

            background-color: #e0e0e0;

            border-radius: 5px;

            margin-right: 5px;

        }

        .tab.active {

            background-color: #4CAF50;

            color: white;

        }

        .tab-content {

            display: none;

            padding: 20px;

            border: 1px solid #ddd;

            border-radius: 0 0 5px 5px;

            background-color: white;

        }

        .tab-content.active {

            display: block;

            border: 2px dashed #ccc;

            border-radius: 8px;

        }

        .media-preview {

            max-width: 100%;

            max-height: 300px;

            margin: 15px auto;

            border: 1px solid #ddd;

            border-radius: 5px;

            display: block;

        }

        .video-container {

            position: relative;

            width: 100%;

            max-width: 800px;

            margin: 15px auto;

            background-color: #000;

            border-radius: 5px;

            overflow: hidden;

            display: none;

        }

        .video-container video {

            width: 100%;

            height: auto;

            max-height: 500px;

            display: block;

        }

        .file-input-label, .btn {

            display: inline-flex;

            align-items: center;

            justify-content: center;

            padding: 10px 20px;

            min-width: 160px;

            box-sizing: border-box;

            white-space: nowrap;

            background-color: #4CAF50;

            color: white;

            border-radius: 4px;

            cursor: pointer;

            margin: 10px;

            text-align: center;

            border: none;

            transition: background-color 0.3s;

        }

        .file-input-label:hover, .btn:hover {

            background-color: #45a049;

        }

        .btn-secondary {

            background-color: #2196F3;

        }

        .btn-secondary:hover {

            background-color: #0b7dda;

        }

        .btn-danger {

            background-color: #f44336;

        }

        .btn-danger:hover {

            background-color: #da190b;

        }

        #status {

            margin: 15px 0;

            min-height: 20px;

            color: #666;

        }

        .progress-container {

            width: 100%;

            background-color: #f1f1f1;

            border-radius: 5px;

            margin: 10px 0;

            display: none;

        }

        .progress-bar {

            height: 20px;

            border-radius: 5px;

            background-color: #4CAF50;

            width: 0%;

            transition: width 0.3s;

        }

        input[type="text"] {

            padding: 10px;

            width: 80%;

            margin: 10px 0;

            border: 1px solid #ddd;

            border-radius: 4px;

        }

        .hidden {

            display: none;

        }

        .controls {

            margin-top: 20px;

        }

        @media (max-width: 600px) {

            .tab-container {

                flex-direction: column;

            }

            .tab {

                margin-right: 0;

                margin-bottom: 5px;

                border-radius: 5px;

            }

            input[type="text"] {

                width: 95%;

            }

            .video-container {

                max-height: 300px;

            }

        }

        #generate .file-input-label,

        #generate #generateBtn {

            width: 150px !important;        

            min-width: 150px !important;   

            height: 40px !important;         

            padding: 0 !important;         

            margin: 10px !important;         

            display: inline-flex !important;

            align-items: center !important;

            justify-content: center !important;

            box-sizing: border-box !important;

            line-height: 44px !important;   

            font-size: 16px !important;

            white-space: nowrap !important;

            vertical-align: middle !important;

        }

        /* 拖拽高亮样式(作用于整个面板区域) */

        .tab-content.drag-accept { position: relative; }

        .tab-content.drag-accept.dragover {

            border-color: #4CAF50 !important;

            box-shadow: 0 0 0 2px #4CAF50 inset;

            background: #f0fff4;

        }

    </style>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>

</head>

<body>

    <script>

    document.body.innerHTML = '';

    // Create container

    const container = document.createElement('div');

    container.className = 'container';

    document.body.appendChild(container);

    // Create heading

    //const h1 = document.createElement('h1');

    //h1.textContent = '隐藏视频播放器';

    //container.appendChild(h1);

    // Create tab container

    const tabContainer = document.createElement('div');

    tabContainer.className = 'tab-container';

    container.appendChild(tabContainer);

    // Create tabs

    const tabs = [

      { text: '文件生成', tab: 'generate', active: true },

      { text: '本地文件', tab: 'local', active: false },

      { text: 'URL转换', tab: 'url', active: false }

    ];

    tabs.forEach(tabData => {

      const tab = document.createElement('div');

      tab.className = `tab ${tabData.active ? 'active' : ''}`;

      tab.textContent = tabData.text;

      tab.setAttribute('data-tab', tabData.tab);

      tabContainer.appendChild(tab);

    });

    // Create tab contents

    const tabContents = [

      {

        id: 'local',

        active: false,

        title: '本地文件提取',

        description: '选择通过 copy /b 命令合并的图片+视频文件',

        content: `

          <label for="localFile" id="localChooseLabel" class="file-input-label">选择本地文件</label>

          <input type="file" id="localFile" accept=".jpg,.jpeg,.png" class="hidden">

           

          <div id="localStatus" class="status">等待选择文件...</div>

          <div class="progress-container" id="localProgressContainer">

            <div id="localProgress" class="progress-bar"></div>

          </div>

           

          <img id="localPreview" class="media-preview hidden">

          <div class="video-container" id="localVideoContainer">

            <video controls id="localVideoPlayer"></video>

          </div>

           

          <div class="controls">

            <button id="localDownloadBtn" class="btn btn-secondary hidden">下载视频</button>

          </div>

        `

      },

      {

        id: 'url',

        active: false,

        title: 'URL转换工具',

        description: '输入图片URL,提取隐藏视频',

        content: `

          <input type="text" id="imageUrl" placeholder="输入图片URL (如: )">

           

          <button id="fetchUrlBtn" class="btn">提取视频</button>

           

          <div id="urlStatus" class="status">等待输入URL...</div>

          <div class="progress-container" id="urlProgressContainer">

            <div id="urlProgress" class="progress-bar"></div>

          </div>

           

          <img id="urlPreview" class="media-preview hidden">

          <div class="video-container" id="urlVideoContainer">

            <video controls id="urlVideoPlayer"></video>

          </div>

           

          <div class="controls">

            <button id="urlDownloadBtn" class="btn btn-secondary hidden">下载视频</button>

          </div>

        `

      },

      {

        id: 'generate',

        active: true,

        title: '文件生成工具',

        description: '选择视频文件,生成合并的图片文件',

        content: `

          <div>

            <img id="defaultPreview" src="./img/iii.jpg" class="media-preview" title="点击更换图片">

          </div>

          <!-- 隐藏的封面图片选择器 -->

          <input type="file" id="coverImageFile" accept="image/*" class="hidden">

           

          <label for="videoFile" class="file-input-label">选择视频文件</label>

          <input type="file" id="videoFile" accept="video/*" class="hidden">

           

          <button id="generateBtn" class="btn">生成合并图片</button>

           

          <div id="generateStatus" class="status">等待选择视频文件...</div>

          <div class="progress-container" id="generateProgressContainer">

            <div id="generateProgress" class="progress-bar"></div>

          </div>

           

          <div class="controls">

            <button id="generateDownloadBtn" class="btn btn-secondary hidden">下载合并图片</button>

          </div>

        `

      }

    ];

    tabContents.forEach(contentData => {

      const content = document.createElement('div');

      content.id = contentData.id;

      content.className = `tab-content ${contentData.active ? 'active' : ''}`;

      

      const h2 = document.createElement('h2');

      h2.textContent = contentData.title;

      if (contentData.id === 'local') h2.id = 'localTitle';

      if (contentData.id === 'url')   h2.id = 'urlTitle';

      content.appendChild(h2);

      

      const p = document.createElement('p');

      p.textContent = contentData.description;

      if (contentData.id === 'local') p.id = 'localDesc';

      if (contentData.id === 'url')   p.id = 'urlDesc';

      content.appendChild(p);

      

      content.innerHTML += contentData.content;

      container.appendChild(content);

    });

    // Add tab switching functionality

    const tabsElements = document.querySelectorAll('.tab');

    tabsElements.forEach(tab => {

      tab.addEventListener('click', () => {

        // Remove active class from all tabs and contents

        document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));

        document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));

         

        // Add active class to clicked tab and corresponding content

        tab.classList.add('active');

        const tabId = tab.getAttribute('data-tab');

        document.getElementById(tabId).classList.add('active');

      });

    });

        // 全局变量

        let currentVideoBlob = null;

        let currentGeneratedBlob = null;

        let droppedVideoFile = null; // 拖拽到“文件生成工具”的视频文件

        let customCoverImageBlob = null; // 用户自选的封面图片

         

        // 切换选项卡

        document.querySelectorAll('.tab').forEach(tab => {

            tab.addEventListener('click', () => {

                document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));

                document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));

                 

                tab.classList.add('active');

                document.getElementById(tab.dataset.tab).classList.add('active');

            });

        });

         

        // 本地文件处理

        document.getElementById('localFile').addEventListener('change', async function(e) {

            const file = e.target.files[0];

            if (!file) return;

            

            const statusEl = document.getElementById('localStatus');

            const progressContainer = document.getElementById('localProgressContainer');

            const progressEl = document.getElementById('localProgress');

            const videoContainer = document.getElementById('localVideoContainer');

            const videoPlayer = document.getElementById('localVideoPlayer');

            const previewImg = document.getElementById('localPreview');

            const downloadBtn = document.getElementById('localDownloadBtn');

            

            // 重置状态

            statusEl.textContent = "正在处理文件...";

            progressContainer.style.display = 'block';

            progressEl.style.width = '0%';

            videoContainer.style.display = 'none';

            downloadBtn.classList.add('hidden');

            currentVideoBlob = null;

            

            try {

                // 显示预览图片

                previewImg.src = URL.createObjectURL(file);

                previewImg.classList.remove('hidden');

                 

                // 1. 读取文件内容

                const arrayBuffer = await file.arrayBuffer();

                progressEl.style.width = '20%';

                 

                // 2. 查找ZIP文件起始位置

                statusEl.textContent = "正在查找隐藏内容...";

                const uint8Array = new Uint8Array(arrayBuffer);

                let zipStart = -1;

                 

                // 查找ZIP文件头 (PK\x03\x04)

                for (let i = 0; i < uint8Array.length - 4; i++) {

                    if (uint8Array === 0x50 && uint8Array[i+1] === 0x4B &&

                        uint8Array[i+2] === 0x03 && uint8Array[i+3] === 0x04) {

                        zipStart = i;

                        break;

                    }

                }

                 

                if (zipStart === -1) throw new Error("未检测到隐藏内容");

                progressEl.style.width = '40%';

                 

                // 3. 提取ZIP部分

                statusEl.textContent = "正在提取隐藏数据...";

                const zipData = arrayBuffer.slice(zipStart);

                progressEl.style.width = '60%';

                 

                // 4. 使用JSZip解析ZIP

                statusEl.textContent = "正在解析内容...";

                const zip = await JSZip.loadAsync(zipData);

                progressEl.style.width = '80%';

                 

                // 5. 查找视频文件

                statusEl.textContent = "正在查找视频文件...";

                let videoFile = null;

                const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv'];

                 

                for (const [name, file] of Object.entries(zip.files)) {

                    if (!file.dir && videoExtensions.some(ext => name.toLowerCase().endsWith(ext))) {

                        videoFile = file;

                        break;

                    }

                }

                 

                if (!videoFile) throw new Error("未找到支持的视频文件");

                 

                // 6. 提取视频内容

                statusEl.textContent = "正在提取视频...";

                const videoData = await videoFile.async('blob');

                currentVideoBlob = videoData;

                progressEl.style.width = '100%';

                 

                // 7. 播放视频

                videoPlayer.src = URL.createObjectURL(videoData);

                videoContainer.style.display = 'block';

                downloadBtn.classList.remove('hidden');

                 

                statusEl.textContent = `已加载: ${videoFile.name}`;

                 

                // 成功后:隐藏标题、描述、选择按钮与预览图,仅保留文件名与视频

                const lt = document.getElementById('localTitle');

                const ld = document.getElementById('localDesc');

                const ll = document.getElementById('localChooseLabel');

                if (lt) lt.style.display = 'none';

                if (ld) ld.style.display = 'none';

                if (ll) ll.style.display = 'none';

                previewImg.classList.add('hidden');

                // 自动播放

                videoPlayer.play().catch(e => {

                    statusEl.textContent += " (点击播放按钮开始播放)";

                });

                 

            } catch (error) {

                console.error("处理失败:", error);

                statusEl.textContent = `错误: ${error.message}`;

                progressEl.style.width = '0%';

                videoContainer.style.display = 'none';

            } finally {

                setTimeout(() => {

                    progressContainer.style.display = 'none';

                }, 500);

            }

        });

         

        // 本地图片拖拽上传(整个面板区域可拖拽)

        (function() {

            const dropEl = document.getElementById('local');

            if (!dropEl) return;

            dropEl.classList.add('drag-accept');

            const isImage = (file) => {

                const name = (file.name || '').toLowerCase();

                return file.type.startsWith('image/') || name.endsWith('.jpg') || name.endsWith('.jpeg') || name.endsWith('.png');

            };

            const prevent = (e) => { e.preventDefault(); e.stopPropagation(); };

            ['dragenter','dragover'].forEach(ev => {

                dropEl.addEventListener(ev, (e) => { prevent(e); dropEl.classList.add('dragover'); });

            });

            ['dragleave','drop'].forEach(ev => {

                dropEl.addEventListener(ev, (e) => { prevent(e); dropEl.classList.remove('dragover'); });

            });

            dropEl.addEventListener('drop', async (e) => {

                const file = e.dataTransfer?.files?.[0];

                if (!file) return;

                if (!isImage(file)) {

                    alert('请拖入 JPG/PNG 图片文件');

                    return;

                }

                const statusEl = document.getElementById('localStatus');

                const progressContainer = document.getElementById('localProgressContainer');

                const progressEl = document.getElementById('localProgress');

                const videoContainer = document.getElementById('localVideoContainer');

                const videoPlayer = document.getElementById('localVideoPlayer');

                const previewImg = document.getElementById('localPreview');

                const downloadBtn = document.getElementById('localDownloadBtn');

                currentVideoBlob = null;

                try {

                    statusEl.textContent = "正在处理文件...";

                    progressContainer.style.display = 'block';

                    progressEl.style.width = '0%';

                    videoContainer.style.display = 'none';

                    downloadBtn.classList.add('hidden');

                    // 预览图片

                    previewImg.src = URL.createObjectURL(file);

                    previewImg.classList.remove('hidden');

                    // 读取内容

                    const arrayBuffer = await file.arrayBuffer();

                    progressEl.style.width = '20%';

                    // 查找 ZIP 头

                    statusEl.textContent = "正在查找隐藏内容...";

                    const uint8Array = new Uint8Array(arrayBuffer);

                    let zipStart = -1;

                    for (let i = 0; i < uint8Array.length - 4; i++) {

                        if (uint8Array === 0x50 && uint8Array[i+1] === 0x4B && uint8Array[i+2] === 0x03 && uint8Array[i+3] === 0x04) {

                            zipStart = i;

                            break;

                        }

                    }

                    if (zipStart === -1) throw new Error("未检测到隐藏内容");

                    progressEl.style.width = '40%';

                    // 提取/解析 ZIP

                    statusEl.textContent = "正在提取隐藏数据...";

                    const zipData = arrayBuffer.slice(zipStart);

                    progressEl.style.width = '60%';

                    statusEl.textContent = "正在解析内容...";

                    const zip = await JSZip.loadAsync(zipData);

                    progressEl.style.width = '80%';

                    // 查找视频

                    statusEl.textContent = "正在查找视频文件...";

                    let videoFile = null;

                    const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv'];

                    for (const [name, zf] of Object.entries(zip.files)) {

                        if (!zf.dir && videoExtensions.some(ext => name.toLowerCase().endsWith(ext))) {

                            videoFile = zf;

                            break;

                        }

                    }

                    if (!videoFile) throw new Error("未找到支持的视频文件");

                    // 提取视频

                    statusEl.textContent = "正在提取视频...";

                    const videoData = await videoFile.async('blob');

                    currentVideoBlob = videoData;

                    progressEl.style.width = '100%';

                    // 播放展示

                    videoPlayer.src = URL.createObjectURL(videoData);

                    videoContainer.style.display = 'block';

                    downloadBtn.classList.remove('hidden');

                    statusEl.textContent = `已加载: ${videoFile.name}`;

                    // 成功后:隐藏标题、描述、选择按钮与预览图,仅保留文件名与视频

                    const lt = document.getElementById('localTitle');

                    const ld = document.getElementById('localDesc');

                    const ll = document.getElementById('localChooseLabel');

                    if (lt) lt.style.display = 'none';

                    if (ld) ld.style.display = 'none';

                    if (ll) ll.style.display = 'none';

                    previewImg.classList.add('hidden');

                    videoPlayer.play().catch(() => {

                        statusEl.textContent += " (点击播放按钮开始播放)";

                    });

                } catch (err) {

                    console.error('拖拽处理失败:', err);

                    statusEl.textContent = `错误: ${err.message}`;

                    progressEl.style.width = '0%';

                    videoContainer.style.display = 'none';

                } finally {

                    setTimeout(() => { progressContainer.style.display = 'none'; }, 500);

                }

            });

        })();

        // 本地文件下载

        document.getElementById('localDownloadBtn').addEventListener('click', function() {

            if (currentVideoBlob) {

                saveAs(currentVideoBlob, 'extracted_video.mp4');

            }

        });

         

        // URL转换处理

        document.getElementById('fetchUrlBtn').addEventListener('click', async function() {

            const imageUrl = document.getElementById('imageUrl').value.trim();

            

            if (!imageUrl) {

                alert("请输入图片URL");

                return;

            }

            

            const statusEl = document.getElementById('urlStatus');

            const progressContainer = document.getElementById('urlProgressContainer');

            const progressEl = document.getElementById('urlProgress');

            const videoContainer = document.getElementById('urlVideoContainer');

            const videoPlayer = document.getElementById('urlVideoPlayer');

            const previewImg = document.getElementById('urlPreview');

            const downloadBtn = document.getElementById('urlDownloadBtn');

            

            // 重置状态

            statusEl.textContent = "正在获取图片...";

            progressContainer.style.display = 'block';

            progressEl.style.width = '0%';

            videoContainer.style.display = 'none';

            downloadBtn.classList.add('hidden');

            currentVideoBlob = null;

            

            try {

                // 显示预览图片

                previewImg.src = imageUrl;

                previewImg.classList.remove('hidden');

                 

                // 1. 获取图片

                const response = await fetch(imageUrl);

                if (!response.ok) throw new Error(`获取图片失败: ${response.status}`);

                 

                progressEl.style.width = '20%';

                 

                // 2. 读取图片内容

                const arrayBuffer = await response.arrayBuffer();

                progressEl.style.width = '40%';

                 

                // 3. 查找ZIP文件起始位置

                statusEl.textContent = "正在查找隐藏内容...";

                const uint8Array = new Uint8Array(arrayBuffer);

                let zipStart = -1;

                 

                for (let i = 0; i < uint8Array.length - 4; i++) {

                    if (uint8Array === 0x50 && uint8Array[i+1] === 0x4B &&

                        uint8Array[i+2] === 0x03 && uint8Array[i+3] === 0x04) {

                        zipStart = i;

                        break;

                    }

                }

                 

                if (zipStart === -1) throw new Error("未检测到隐藏内容");

                progressEl.style.width = '60%';

                 

                // 4. 提取ZIP部分

                statusEl.textContent = "正在提取隐藏数据...";

                const zipData = arrayBuffer.slice(zipStart);

                progressEl.style.width = '80%';

                 

                // 5. 使用JSZip解析ZIP

                statusEl.textContent = "正在解析内容...";

                const zip = await JSZip.loadAsync(zipData);

                 

                // 6. 查找视频文件

                statusEl.textContent = "正在查找视频文件...";

                let videoFile = null;

                const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv'];

                 

                for (const [name, file] of Object.entries(zip.files)) {

                    if (!file.dir && videoExtensions.some(ext => name.toLowerCase().endsWith(ext))) {

                        videoFile = file;

                        break;

                    }

                }

                 

                if (!videoFile) throw new Error("未找到支持的视频文件");

                 

                // 7. 提取视频内容

                statusEl.textContent = "正在提取视频...";

                const videoData = await videoFile.async('blob');

                currentVideoBlob = videoData;

                progressEl.style.width = '100%';

                 

                // 8. 播放视频

                videoPlayer.src = URL.createObjectURL(videoData);

                videoContainer.style.display = 'block';

                 

                statusEl.textContent = `已加载: ${videoFile.name}`;

                 

                // 成功后:仅保留文件名与视频,隐藏其它控件

                const ut = document.getElementById('urlTitle');

                const ud = document.getElementById('urlDesc');

                const urlInput = document.getElementById('imageUrl');

                const fetchBtn = document.getElementById('fetchUrlBtn');

                const urlPrev = document.getElementById('urlPreview');

                const urlDl  = document.getElementById('urlDownloadBtn');

                if (ut) ut.style.display = 'none';

                if (ud) ud.style.display = 'none';

                if (urlInput) urlInput.style.display = 'none';

                if (fetchBtn) fetchBtn.style.display = 'none';

                if (urlPrev) urlPrev.classList.add('hidden');

                if (urlDl) urlDl.classList.add('hidden');

                // 自动播放

                videoPlayer.play().catch(e => {

                    statusEl.textContent += " (点击播放按钮开始播放)";

                });

                 

            } catch (error) {

                console.error("处理失败:", error);

                statusEl.textContent = `错误: ${error.message}`;

                progressEl.style.width = '0%';

                videoContainer.style.display = 'none';

            } finally {

                setTimeout(() => {

                    progressContainer.style.display = 'none';

                }, 500);

            }

        });

         

        // URL文件下载

        document.getElementById('urlDownloadBtn').addEventListener('click', function() {

            if (currentVideoBlob) {

                saveAs(currentVideoBlob, 'extracted_video.mp4');

            }

        });

         

        // 点击生成面板图片以更换封面

        (function() {

            const previewImg = document.getElementById('defaultPreview');

            const coverInput = document.getElementById('coverImageFile');

            if (!previewImg || !coverInput) return;

            previewImg.style.cursor = 'pointer';

            previewImg.addEventListener('click', () => {

                // 提示并打开文件选择

                coverInput.click();

            });

            coverInput.addEventListener('change', async () => {

                const file = coverInput.files && coverInput.files[0];

                if (!file) return;

                // 保存自定义封面并更新预览

                customCoverImageBlob = file;

                try {

                    const objectUrl = URL.createObjectURL(file);

                    previewImg.src = objectUrl;

                    const statusEl = document.getElementById('generateStatus');

                    if (statusEl) statusEl.textContent = `已选择封面图片:${file.name}`;

                } catch (_) {}

            });

        })();

        // “文件生成工具”拖拽上传(整个面板区域可拖拽)

        (function() {

            const dropEl = document.getElementById('generate');

            if (!dropEl) return;

            dropEl.classList.add('drag-accept');

            const isVideo = (file) => file.type.startsWith('video/');

            const prevent = (e) => { e.preventDefault(); e.stopPropagation(); };

            ['dragenter','dragover'].forEach(ev => {

                dropEl.addEventListener(ev, (e) => { prevent(e); dropEl.classList.add('dragover'); });

            });

            ['dragleave','drop'].forEach(ev => {

                dropEl.addEventListener(ev, (e) => { prevent(e); dropEl.classList.remove('dragover'); });

            });

            dropEl.addEventListener('drop', (e) => {

                const file = e.dataTransfer?.files?.[0];

                if (!file) return;

                if (!isVideo(file)) {

                    alert('请拖入视频文件');

                    return;

                }

                droppedVideoFile = file;

                const statusEl = document.getElementById('generateStatus');

                statusEl.textContent = `已选择(拖拽):${file.name}`;

            });

        })();

        // 文件生成处理

        document.getElementById('generateBtn').addEventListener('click', async function() {

            const videoFileInput = document.getElementById('videoFile');

            const statusEl = document.getElementById('generateStatus');

            const progressEl = document.getElementById('generateProgress');

            const progressContainer = document.getElementById('generateProgressContainer');

            const downloadBtn = document.getElementById('generateDownloadBtn');

            const videoFile = videoFileInput.files[0] || droppedVideoFile;

            if (!videoFile) {

                alert("请选择或拖拽视频文件");

                return;

            }

            try {

                statusEl.textContent = "正在准备生成文件...";

                progressContainer.style.display = 'block';

                progressEl.style.width = '0%';

                downloadBtn.classList.add('hidden');

                 

                // 1. 创建ZIP

                const zip = new JSZip();

                zip.file(videoFile.name, videoFile);

                 

                // 2. 生成ZIP

                statusEl.textContent = "正在生成ZIP文件...";

                const zipBlob = await zip.generateAsync({type: 'blob'}, (metadata) => {

                    progressEl.style.width = `${metadata.percent}%`;

                });

                 

                // 3. 获取封面图片(优先使用用户选择的图片)

                let imageBlob;

                if (customCoverImageBlob) {

                    statusEl.textContent = "已选择自定义图片,正在读取...";

                    imageBlob = customCoverImageBlob;

                } else {

                    statusEl.textContent = "正在获取默认图片...";

                    const imageResp = await fetch('./img/iii.jpg');

                    imageBlob = await imageResp.blob();

                }

                 

                // 4. 合并文件

                statusEl.textContent = "正在合并文件...";

                const mergedBlob = new Blob([

                    await imageBlob.arrayBuffer(),

                    await zipBlob.arrayBuffer()

                ], { type: 'image/jpeg' });

                currentGeneratedBlob = mergedBlob;

                statusEl.textContent = "生成成功!";

                downloadBtn.classList.remove('hidden');

            } catch (error) {

                console.error("生成失败:", error);

                statusEl.textContent = `错误: ${error.message}`;

                progressEl.style.width = '0%';

            } finally {

                setTimeout(() => {

                    progressContainer.style.display = 'none';

                }, 500);

            }

        });

         

        // 生成文件下载

        document.getElementById('generateDownloadBtn').addEventListener('click', function() {

            if (currentGeneratedBlob) {

                saveAs(currentGeneratedBlob, 'merged_image.jpg');

            }

        });

    // 全局拦截,避免把文件拖到页面其它区域时被浏览器直接打开

    (function() {

        const prevent = (e) => { e.preventDefault(); e.stopPropagation(); };

        window.addEventListener('dragover', prevent);

        window.addEventListener('drop', prevent);

    })();

    </script>

    <style>

        .corner-links {

            position: fixed;

            right: 20px;

            bottom: 20px;

            display: flex;

            align-items: center;

            z-index: 9999;

        }

        .corner-link {

            display: inline-flex;

            align-items: center;

            gap: 8px;

            padding: 8px 10px;

            border-radius: 8px;

            color: #212529;

            text-decoration: none;

            transition: all 0.3s ease;

        }

        .corner-link:hover {

            transform: translateY(-2px);

            box-shadow: 0 6px 12px rgba(0,0,0,0.15);

        }

        .corner-link img,

        .corner-link svg {

            width: 22px;

            height: 22px;

        }

        .corner-link .label {

            font-weight: 600;

            font-size: 0.95rem;

        }

    </style>

    <div class="corner-links" aria-label="页面固定链接">

        <a class="corner-link" href="https://github.com/IIIStudio/Copy-B" target="_blank" rel="noopener noreferrer" aria-label="前往 GitHub 仓库">

            <!-- 内联 GitHub 图标,避免外部资源依赖 -->

            <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg">

                <path fill="#24292F" d="M12 .5a12 12 0 0 0-3.79 23.41c.6.11.82-.26.82-.58v-2.02c-3.35.73-4.06-1.61-4.06-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.75.08-.74.08-.74 1.2.09 1.83 1.23 1.83 1.23 1.07 1.83 2.8 1.3 3.49.99.11-.78.42-1.3.76-1.6-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.51.12-3.15 0 0 1.01-.32 3.3 1.23.96-.27 1.99-.4 3.01-.4s2.05.14 3.01.4c2.29-1.55 3.3-1.23 3.3-1.23.66 1.64.25 2.85.12 3.15.77.84 1.24 1.91 1.24 3.22 0 4.61-2.8 5.63-5.47 5.93.43.37.81 1.1.81 2.22v3.29c0 .32.22.7.83.58A12 12 0 0 0 12 .5Z"/>

            </svg>

            <span class="label">Copy-B</span>

        </a>

        <a class="corner-link" href="https://cnb.cool/IIIStudio/HTML/Copy-B/" target="_blank" rel="noopener noreferrer" aria-label="前往 Copy-B 文档页面">

            <img src="https://docs.cnb.cool/images/logo/svg/LogoColorfulIcon.svg" alt="CNB Logo">

        </a>

    </div>

</body>

</html>

复制代码