要货了源码集市 · 免费资源
==========================

名称：一个隐藏视频的小玩意
编号：#20393
分类：免费资源
技术栈：Go

说明
----
GitHub：https://github.com/IIIStudio/Copy-B CNB：https://cnb.cool/IIIStudio/HTML/Copy-B 演示：https://iiistudio.github.io/Copy-B/ 测试的文件： 原理是选择通过 copy /b 命令合并的图片+视频文件 过于简单，就不写使用过程了。 点击头像可以更换图片，可以拖拽视频到里面也可以点击上传！ &lt;!DOCTYPE html&gt; &lt;html lang=&quot;zh-CN&quot;&gt; &lt;head&gt; &lt;meta charset=&quot;UTF-8&quot;&gt; &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt; &lt;title&gt;Copy /B&lt;/title&gt; &lt;style&gt; body { font-family: &quot;Microsoft YaHei&quot;, 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=&quot;text&quot;] { 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=&quot;text&quot;] { 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; } &lt;/style&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js&quot;&gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;script&gt; 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 =&gt; { 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: ` &lt;label for=&quot;localFile&quot; id=&quot;localChooseLabel&quot; class=&quot;file-input-label&quot;&gt;选择本地文件&lt;/label&gt; &lt;input type=&quot;file&quot; id=&quot;localFile&quot; accept=&quot;.jpg,.jpeg,.png&quot; class=&quot;hidden&quot;&gt; &lt;div id=&quot;localStatus&quot; class=&quot;status&quot;&gt;等待选择文件...&lt;/div&gt; &lt;div class=&quot;progress-container&quot; id=&quot;localProgressContainer&quot;&gt; &lt;div id=&quot;localProgress&quot; class=&quot;progress-bar&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;img id=&quot;localPreview&quot; class=&quot;media-preview hidden&quot;&gt; &lt;div class=&quot;video-container&quot; id=&quot;localVideoContainer&quot;&gt; &lt;video controls id=&quot;localVideoPlayer&quot;&gt;&lt;/video&gt; &lt;/div&gt; &lt;div class=&quot;controls&quot;&gt; &lt;button id=&quot;localDownloadBtn&quot; class=&quot;btn btn-secondary hidden&quot;&gt;下载视频&lt;/button&gt; &lt;/div&gt; ` }, { id: 'url', active: false, title: 'URL转换工具', description: '输入图片URL，提取隐藏视频', content: ` &lt;input type=&quot;text&quot; id=&quot;imageUrl&quot; placeholder=&quot;输入图片URL (如: )&quot;&gt; &lt;button id=&quot;fetchUrlBtn&quot; class=&quot;btn&quot;&gt;提取视频&lt;/button&gt; &lt;div id=&quot;urlStatus&quot; class=&quot;status&quot;&gt;等待输入URL...&lt;/div&gt; &lt;div class=&quot;progress-container&quot; id=&quot;urlProgressContainer&quot;&gt; &lt;div id=&quot;urlProgress&quot; class=&quot;progress-bar&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;img id=&quot;urlPreview&quot; class=&quot;media-preview hidden&quot;&gt; &lt;div class=&quot;video-container&quot; id=&quot;urlVideoContainer&quot;&gt; &lt;video controls id=&quot;urlVideoPlayer&quot;&gt;&lt;/video&gt; &lt;/div&gt; &lt;div class=&quot;controls&quot;&gt; &lt;button id=&quot;urlDownloadBtn&quot; class=&quot;btn btn-secondary hidden&quot;&gt;下载视频&lt;/button&gt; &lt;/div&gt; ` }, { id: 'generate', active: true, title: '文件生成工具', description: '选择视频文件，生成合并的图片文件', content: ` &lt;div&gt; &lt;img id=&quot;defaultPreview&quot; src=&quot;./img/iii.jpg&quot; class=&quot;media-preview&quot; title=&quot;点击更换图片&quot;&gt; &lt;/div&gt; &lt;!-- 隐藏的封面图片选择器 --&gt; &lt;input type=&quot;file&quot; id=&quot;coverImageFile&quot; accept=&quot;image/*&quot; class=&quot;hidden&quot;&gt; &lt;label for=&quot;videoFile&quot; class=&quot;file-input-label&quot;&gt;选择视频文件&lt;/label&gt; &lt;input type=&quot;file&quot; id=&quot;videoFile&quot; accept=&quot;video/*&quot; class=&quot;hidden&quot;&gt; &lt;button id=&quot;generateBtn&quot; class=&quot;btn&quot;&gt;生成合并图片&lt;/button&gt; &lt;div id=&quot;generateStatus&quot; class=&quot;status&quot;&gt;等待选择视频文件...&lt;/div&gt; &lt;div class=&quot;progress-container&quot; id=&quot;generateProgressContainer&quot;&gt; &lt;div id=&quot;generateProgress&quot; class=&quot;progress-bar&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;controls&quot;&gt; &lt;button id=&quot;generateDownloadBtn&quot; class=&quot;btn btn-secondary hidden&quot;&gt;下载合并图片&lt;/button&gt; &lt;/div&gt; ` } ]; tabContents.forEach(contentData =&gt; { 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 =&gt; { tab.addEventListener('click', () =&gt; { // Remove active class from all tabs and contents document.querySelectorAll('.tab').forEach(t =&gt; t.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c =&gt; 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 =&gt; { tab.addEventListener('click', () =&gt; { document.querySelectorAll('.tab').forEach(t =&gt; t.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c =&gt; 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 = &quot;正在处理文件...&quot;; 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 = &quot;正在查找隐藏内容...&quot;; const uint8Array = new Uint8Array(arrayBuffer); let zipStart = -1; // 查找ZIP文件头 (PK\x03\x04) for (let i = 0; i &lt; uint8Array.length - 4; i++) { if (uint8Array === 0x50 &amp;&amp; uint8Array[i+1] === 0x4B &amp;&amp; uint8Array[i+2] === 0x03 &amp;&amp; uint8Array[i+3] === 0x04) { zipStart = i; break; } } if (zipStart === -1) throw new Error(&quot;未检测到隐藏内容&quot;); progressEl.style.width = '40%'; // 3. 提取ZIP部分 statusEl.textContent = &quot;正在提取隐藏数据...&quot;; const zipData = arrayBuffer.slice(zipStart); progressEl.style.width = '60%'; // 4. 使用JSZip解析ZIP statusEl.textContent = &quot;正在解析内容...&quot;; const zip = await JSZip.loadAsync(zipData); progressEl.style.width = '80%'; // 5. 查找视频文件 statusEl.textContent = &quot;正在查找视频文件...&quot;; let videoFile = null; const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv']; for (const [name, file] of Object.entries(zip.files)) { if (!file.dir &amp;&amp; videoExtensions.some(ext =&gt; name.toLowerCase().endsWith(ext))) { videoFile = file; break; } } if (!videoFile) throw new Error(&quot;未找到支持的视频文件&quot;); // 6. 提取视频内容 statusEl.textContent = &quot;正在提取视频...&quot;; 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 =&gt; { statusEl.textContent += &quot; (点击播放按钮开始播放)&quot;; }); } catch (error) { console.error(&quot;处理失败:&quot;, error); statusEl.textContent = `错误: ${error.message}`; progressEl.style.width = '0%'; videoContainer.style.display = 'none'; } finally { setTimeout(() =&gt; { progressContainer.style.display = 'none'; }, 500); } }); // 本地图片拖拽上传（整个面板区域可拖拽） (function() { const dropEl = document.getElementById('local'); if (!dropEl) return; dropEl.classList.add('drag-accept'); const isImage = (file) =&gt; { const name = (file.name || '').toLowerCase(); return file.type.startsWith('image/') || name.endsWith('.jpg') || name.endsWith('.jpeg') || name.endsWith('.png'); }; const prevent = (e) =&gt; { e.preventDefault(); e.stopPropagation(); }; ['dragenter','dragover'].forEach(ev =&gt; { dropEl.addEventListener(ev, (e) =&gt; { prevent(e); dropEl.classList.add('dragover'); }); }); ['dragleave','drop'].forEach(ev =&gt; { dropEl.addEventListener(ev, (e) =&gt; { prevent(e); dropEl.classList.remove('dragover'); }); }); dropEl.addEventListener('drop', async (e) =&gt; { 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 = &quot;正在处理文件...&quot;; 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 = &quot;正在查找隐藏内容...&quot;; const uint8Array = new Uint8Array(arrayBuffer); let zipStart = -1; for (let i = 0; i &lt; uint8Array.length - 4; i++) { if (uint8Array === 0x50 &amp;&amp; uint8Array[i+1] === 0x4B &amp;&amp; uint8Array[i+2] === 0x03 &amp;&amp; uint8Array[i+3] === 0x04) { zipStart = i; break; } } if (zipStart === -1) throw new Error(&quot;未检测到隐藏内容&quot;); progressEl.style.width = '40%'; // 提取/解析 ZIP statusEl.textContent = &quot;正在提取隐藏数据...&quot;; const zipData = arrayBuffer.slice(zipStart); progressEl.style.width = '60%'; statusEl.textContent = &quot;正在解析内容...&quot;; const zip = await JSZip.loadAsync(zipData); progressEl.style.width = '80%'; // 查找视频 statusEl.textContent = &quot;正在查找视频文件...&quot;; let videoFile = null; const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv']; for (const [name, zf] of Object.entries(zip.files)) { if (!zf.dir &amp;&amp; videoExtensions.some(ext =&gt; name.toLowerCase().endsWith(ext))) { videoFile = zf; break; } } if (!videoFile) throw new Error(&quot;未找到支持的视频文件&quot;); // 提取视频 statusEl.textContent = &quot;正在提取视频...&quot;; 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(() =&gt; { statusEl.textContent += &quot; (点击播放按钮开始播放)&quot;; }); } catch (err) { console.error('拖拽处理失败:', err); statusEl.textContent = `错误: ${err.message}`; progressEl.style.width = '0%'; videoContainer.style.display = 'none'; } finally { setTimeout(() =&gt; { 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(&quot;请输入图片URL&quot;); 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 = &quot;正在获取图片...&quot;; 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 = &quot;正在查找隐藏内容...&quot;; const uint8Array = new Uint8Array(arrayBuffer); let zipStart = -1; for (let i = 0; i &lt; uint8Array.length - 4; i++) { if (uint8Array === 0x50 &amp;&amp; uint8Array[i+1] === 0x4B &amp;&amp; uint8Array[i+2] === 0x03 &amp;&amp; uint8Array[i+3] === 0x04) { zipStart = i; break; } } if (zipStart === -1) throw new Error(&quot;未检测到隐藏内容&quot;); progressEl.style.width = '60%'; // 4. 提取ZIP部分 statusEl.textContent = &quot;正在提取隐藏数据...&quot;; const zipData = arrayBuffer.slice(zipStart); progressEl.style.width = '80%'; // 5. 使用JSZip解析ZIP statusEl.textContent = &quot;正在解析内容...&quot;; const zip = await JSZip.loadAsync(zipData); // 6. 查找视频文件 statusEl.textContent = &quot;正在查找视频文件...&quot;; let videoFile = null; const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv']; for (const [name, file] of Object.entries(zip.files)) { if (!file.dir &amp;&amp; videoExtensions.some(ext =&gt; name.toLowerCase().endsWith(ext))) { videoFile = file; break; } } if (!videoFile) throw new Error(&quot;未找到支持的视频文件&quot;); // 7. 提取视频内容 statusEl.textContent = &quot;正在提取视频...&quot;; 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 =&gt; { statusEl.textContent += &quot; (点击播放按钮开始播放)&quot;; }); } catch (error) { console.error(&quot;处理失败:&quot;, error); statusEl.textContent = `错误: ${error.message}`; progressEl.style.width = '0%'; videoContainer.style.display = 'none'; } finally { setTimeout(() =&gt; { 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', () =&gt; { // 提示并打开文件选择 coverInput.click(); }); coverInput.addEventListener('change', async () =&gt; { const file = coverInput.files &amp;&amp; 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) =&gt; file.type.startsWith('video/'); const prevent = (e) =&gt; { e.preventDefault(); e.stopPropagation(); }; ['dragenter','dragover'].forEach(ev =&gt; { dropEl.addEventListener(ev, (e) =&gt; { prevent(e); dropEl.classList.add('dragover'); }); }); ['dragleave','drop'].forEach(ev =&gt; { dropEl.addEventListener(ev, (e) =&gt; { prevent(e); dropEl.classList.remove('dragover'); }); }); dropEl.addEventListener('drop', (e) =&gt; { 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(&quot;请选择或拖拽视频文件&quot;); return; } try { statusEl.textContent = &quot;正在准备生成文件...&quot;; 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 = &quot;正在生成ZIP文件...&quot;; const zipBlob = await zip.generateAsync({type: 'blob'}, (metadata) =&gt; { progressEl.style.width = `${metadata.percent}%`; }); // 3. 获取封面图片（优先使用用户选择的图片） let imageBlob; if (customCoverImageBlob) { statusEl.textContent = &quot;已选择自定义图片，正在读取...&quot;; imageBlob = customCoverImageBlob; } else { statusEl.textContent = &quot;正在获取默认图片...&quot;; const imageResp = await fetch('./img/iii.jpg'); imageBlob = await imageResp.blob(); } // 4. 合并文件 statusEl.textContent = &quot;正在合并文件...&quot;; const mergedBlob = new Blob([ await imageBlob.arrayBuffer(), await zipBlob.arrayBuffer() ], { type: 'image/jpeg' }); currentGeneratedBlob = mergedBlob; statusEl.textContent = &quot;生成成功！&quot;; downloadBtn.classList.remove('hidden'); } catch (error) { console.error(&quot;生成失败:&quot;, error); statusEl.textContent = `错误: ${error.message}`; progressEl.style.width = '0%'; } finally { setTimeout(() =&gt; { progressContainer.style.display = 'none'; }, 500); } }); // 生成文件下载 document.getElementById('generateDownloadBtn').addEventListener('click', function() { if (currentGeneratedBlob) { saveAs(currentGeneratedBlob, 'merged_image.jpg'); } }); // 全局拦截，避免把文件拖到页面其它区域时被浏览器直接打开 (function() { const prevent = (e) =&gt; { e.preventDefault(); e.stopPropagation(); }; window.addEventListener('dragover', prevent); window.addEventListener('drop', prevent); })(); &lt;/script&gt; &lt;style&gt; .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; } &lt;/style&gt; &lt;div class=&quot;corner-links&quot; aria-label=&quot;页面固定链接&quot;&gt; &lt;a class=&quot;corner-link&quot; href=&quot;https://github.com/IIIStudio/Copy-B&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; aria-label=&quot;前往 GitHub 仓库&quot;&gt; &lt;!-- 内联 GitHub 图标，避免外部资源依赖 --&gt; &lt;svg viewBox=&quot;0 0 24 24&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt; &lt;path fill=&quot;#24292F&quot; d=&quot;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&quot;/&gt; &lt;/svg&gt; &lt;span class=&quot;label&quot;&gt;Copy-B&lt;/span&gt; &lt;/a&gt; &lt;a class=&quot;corner-link&quot; href=&quot;https://cnb.cool/IIIStudio/HTML/Copy-B/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; aria-label=&quot;前往 Copy-B 文档页面&quot;&gt; &lt;img src=&quot;https://docs.cnb.cool/images/logo/svg/LogoColorfulIcon.svg&quot; alt=&quot;CNB Logo&quot;&gt; &lt;/a&gt; &lt;/div&gt; &lt;/body&gt; &lt;/html&gt; 复制代码

交付说明
--------
当前商品尚未上传完整工程压缩包，本文件为授权与部署说明。请通过订单号在「订单查询」核对后联系站点补发源码包。

站点：https://www.yaohuole.homes
