您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download images, audio, and video files from a webpage into a ZIP file with filters and draggable UI panel.
// ==UserScript== // @name MediaZipper // @license MIT // @namespace http://tampermonkey.net/ // @version 1.3 // @description Download images, audio, and video files from a webpage into a ZIP file with filters and draggable UI panel. // @author Kanha Pise // @match *://*/* // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // ==/UserScript== (function waitForLibraries() { if (typeof JSZip === 'undefined' || typeof window.saveAs === 'undefined') { console.log('Waiting for JSZip and FileSaver...'); setTimeout(waitForLibraries, 100); return; } (function () { 'use strict'; const panel = document.createElement('div'); panel.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 220px; background: white; border: 1px solid #ccc; padding: 10px; z-index: 9999; box-shadow: 0 0 10px rgba(0,0,0,0.3); font-family: sans-serif; `; panel.innerHTML = ` <div id="mz-header" style="cursor: move; font-weight: bold; margin-bottom: 5px;">📦 MediaZipper</div> <div id="mz-body"> <label><input type="checkbox" id="filterJPG" checked> .jpg</label><br> <label><input type="checkbox" id="filterPNG" checked> .png</label><br> <label><input type="checkbox" id="filterMP3" checked> .mp3</label><br> <label><input type="checkbox" id="filterMP4" checked> .mp4</label><br> <button id="downloadBtn" style="margin-top: 8px; width: 100%;">Download ZIP</button> <div id="progress" style="margin-top: 10px; display: none;"> <progress id="progressBar" value="0" max="100" style="width: 100%;"></progress> <span id="progressText">0%</span> </div> </div> <button id="toggleBtn" style="margin-top: 5px; width: 100%;">Collapse</button> `; document.body.appendChild(panel); // Draggable Panel const header = document.getElementById('mz-header'); let isDragging = false, offsetX, offsetY; header.addEventListener('mousedown', function (e) { isDragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; e.preventDefault(); }); document.addEventListener('mousemove', function (e) { if (isDragging) { panel.style.left = `${e.clientX - offsetX}px`; panel.style.top = `${e.clientY - offsetY}px`; } }); document.addEventListener('mouseup', function () { isDragging = false; }); // Toggle Collapse const toggleBtn = document.getElementById('toggleBtn'); const body = document.getElementById('mz-body'); toggleBtn.onclick = function () { const hidden = body.style.display === 'none'; body.style.display = hidden ? 'block' : 'none'; toggleBtn.textContent = hidden ? 'Collapse' : 'Expand'; }; // Main ZIP download function (Promise-based) function fetchAndZipMedia() { const zip = new JSZip(); const filters = { jpg: document.getElementById('filterJPG').checked, png: document.getElementById('filterPNG').checked, mp3: document.getElementById('filterMP3').checked, mp4: document.getElementById('filterMP4').checked }; const mediaElements = Array.from(document.querySelectorAll('img[src], audio[src], video[src], source[src]')); const mediaSrcs = new Set(); const progress = document.getElementById('progress'); const progressBar = document.getElementById('progressBar'); const progressText = document.getElementById('progressText'); progress.style.display = 'block'; let completed = 0; let addedCount = 0; function updateProgress() { const percent = Math.round((completed / mediaElements.length) * 100); progressBar.value = percent; progressText.textContent = percent + '%'; } const fetchPromises = mediaElements.map(function (el) { let src = el.src || el.currentSrc || el.getAttribute('src'); if (!src || mediaSrcs.has(src)) { completed++; updateProgress(); return Promise.resolve(); // Skip duplicate or empty src } mediaSrcs.add(src); const ext = src.split('.').pop().split('?')[0].toLowerCase(); if ((ext === 'jpg' && !filters.jpg) || (ext === 'png' && !filters.png) || (ext === 'mp3' && !filters.mp3) || (ext === 'mp4' && !filters.mp4)) { completed++; updateProgress(); return Promise.resolve(); } return fetch(src, { mode: 'cors' }).then(function (res) { return res.blob(); }).then(function (blob) { const filename = decodeURIComponent(src.split('/').pop().split('?')[0]).replace(/[\\/:*?"<>|]/g, '_'); zip.file(filename, blob); addedCount++; }).catch(function (err) { console.warn('Failed to fetch:', src, err); }).finally(function () { completed++; updateProgress(); }); }); Promise.all(fetchPromises).then(function () { if (addedCount === 0) { alert("No media files matched the selected filters or were blocked by CORS."); progress.style.display = 'none'; return; } zip.generateAsync({ type: 'blob' }).then(function (content) { window.saveAs(content, 'media.zip'); progress.style.display = 'none'; }).catch(function (err) { alert("Failed to generate ZIP file."); console.error(err); progress.style.display = 'none'; }); }); } document.getElementById('downloadBtn').addEventListener('click', fetchAndZipMedia); })(); })();