您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show previews, organize image types, and allow ZIP download with proper naming
当前为
// ==UserScript== // @name testIMVU _contents.json Viewer with Zip Download // @namespace http://tampermonkey.net/ // @version 1.2.4 // @auther heapsofjoy // @description Show previews, organize image types, and allow ZIP download with proper naming // @match https://userimages-akm.imvu.com/productdata/*/*/_contents.json // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js // ==/UserScript== (function () { 'use strict'; let files; try { files = JSON.parse(document.body.innerText); } catch (e) { console.error("Failed to parse JSON:", e); return; } const baseUrl = window.location.href.replace(/\/_contents\.json$/, ''); const parts = window.location.pathname.split('/'); const prodId = parts[2]; const rev = parts[3]; const zipFilename = `${prodId}_rev${rev}_files.zip`; // Basic styling document.body.innerHTML = ''; document.body.style.background = '#111'; document.body.style.color = '#eee'; document.body.style.fontFamily = 'Arial, sans-serif'; document.body.style.padding = '20px'; const heading = document.createElement('h1'); heading.textContent = '📦 Product Files'; heading.style.marginBottom = '10px'; document.body.appendChild(heading); // Download ZIP button const downloadAllBtn = document.createElement('button'); downloadAllBtn.textContent = '⬇ Download All as ZIP'; downloadAllBtn.style.marginBottom = '25px'; downloadAllBtn.style.padding = '10px 20px'; downloadAllBtn.style.fontSize = '16px'; downloadAllBtn.style.backgroundColor = '#ff69b4'; downloadAllBtn.style.color = 'white'; downloadAllBtn.style.border = 'none'; downloadAllBtn.style.borderRadius = '8px'; downloadAllBtn.style.cursor = 'pointer'; downloadAllBtn.onclick = async () => { downloadAllBtn.disabled = true; downloadAllBtn.textContent = 'Zipping...'; const zip = new JSZip(); for (const file of files) { const originalName = file.name || file.url; if (isExcluded(originalName)) continue; const url = `${baseUrl}/${file.url || file.name}`; try { const res = await fetch(url); const blob = await res.blob(); let adjustedName = originalName; // Check MIME type and adjust the file extension accordingly const mime = blob.type; // If it's an image, we will try to adjust the extension based on the mime type if (mime.startsWith('image/')) { // Handle PNG and JPEG files if (mime === 'image/png') { adjustedName = originalName.replace(/\.(jpe?g|gif|bmp|tga)$/i, '.png'); } else if (mime === 'image/jpeg' || mime === 'image/jpg') { adjustedName = originalName.replace(/\.(png|gif|bmp|tga)$/i, '.jpg'); } else if (mime === 'image/gif') { adjustedName = originalName.replace(/\.(png|jpeg|bmp|tga)$/i, '.gif'); } else if (mime === 'image/bmp') { adjustedName = originalName.replace(/\.(png|jpeg|gif|tga)$/i, '.bmp'); } else if (mime === 'image/tga') { // Handle .tga files adjustedName = originalName.replace(/\.tga$/i, '.png'); // You can decide on a default format like PNG } // Add other image formats here if necessary } else { // Non-image files should be left as-is // This handles non-image files or files with unrecognized mime types adjustedName = originalName; } // Add the file to the zip with the adjusted name zip.file(adjustedName, blob); } catch (e) { console.error('Failed to fetch:', originalName, e); } } zip.generateAsync({ type: 'blob' }).then(blob => { const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = zipFilename; document.body.appendChild(a); a.click(); a.remove(); downloadAllBtn.textContent = '⬇ Download All as ZIP'; downloadAllBtn.disabled = false; }); }; document.body.appendChild(downloadAllBtn); // Layout containers const imageGrid = document.createElement('div'); imageGrid.style.display = 'grid'; imageGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(250px, 1fr))'; imageGrid.style.gap = '15px'; imageGrid.style.marginBottom = '50px'; document.body.appendChild(imageGrid); const otherImagesSection = document.createElement('div'); otherImagesSection.style.marginTop = '30px'; document.body.appendChild(otherImagesSection); const fileList = document.createElement('div'); fileList.style.paddingTop = '20px'; fileList.style.borderTop = '1px solid #444'; fileList.style.marginTop = '30px'; document.body.appendChild(fileList); const imageBoxColor = [ String.fromCharCode(120, 109, 102), String.fromCharCode(120, 115, 102) ]; const isExcluded = (filename) => { return imageBoxColor.some(ext => filename.toLowerCase().endsWith(ext)); }; const isPreviewImage = (filename) => /\.(png|jpe?g|gif|webp|bmp|tga)$/i.test(filename); const isOtherImageType = (filename) => /\.(dds)$/i.test(filename); files.forEach(async (file) => { const name = file.name || file.url || 'Unnamed'; const urlPart = file.url || file.name; if (isExcluded(name)) return; const fileUrl = `${baseUrl}/${urlPart}`; try { const res = await fetch(fileUrl); const blob = await res.blob(); const mime = blob.type; const isImage = mime.startsWith('image/'); const actualExt = mime.split('/')[1] || 'unknown'; // If the image is a .tga, move it to the "other images" section if (actualExt === 'x-tga') { const otherImageEntry = document.createElement('div'); otherImageEntry.style.marginBottom = '10px'; const link = document.createElement('a'); link.href = fileUrl; link.download = name; link.textContent = name; link.style.color = '#ff69b4'; link.style.textDecoration = 'none'; link.style.wordBreak = 'break-all'; otherImageEntry.appendChild(link); otherImagesSection.appendChild(otherImageEntry); } else if (isImage && isPreviewImage(name)) { const container = document.createElement('div'); container.style.background = '#222'; container.style.padding = '10px'; container.style.borderRadius = '10px'; container.style.boxShadow = '0 0 10px rgba(0,0,0,0.3)'; container.style.textAlign = 'center'; const img = document.createElement('img'); img.src = URL.createObjectURL(blob); img.alt = name; img.style.maxWidth = '100%'; img.style.maxHeight = '200px'; img.style.width = 'auto'; // Ensures the image maintains its aspect ratio img.style.height = 'auto'; // Keeps the height flexible based on the width img.style.minWidth = '150px'; // Sets a minimum width for the image box img.style.display = 'block'; img.style.margin = '0 auto 10px'; container.appendChild(img); const link = document.createElement('a'); link.href = fileUrl; link.download = name; link.textContent = name; link.style.color = '#ff69b4'; link.style.textDecoration = 'none'; link.style.wordBreak = 'break-all'; container.appendChild(link); const mimeInfo = document.createElement('div'); mimeInfo.textContent = `(${mime})`; mimeInfo.style.color = '#999'; mimeInfo.style.fontSize = '12px'; mimeInfo.style.marginTop = '5px'; container.appendChild(mimeInfo); imageGrid.appendChild(container); } else { // For non-image files, no preview const fileEntry = document.createElement('div'); fileEntry.style.marginBottom = '10px'; const link = document.createElement('a'); link.href = fileUrl; link.download = name; link.textContent = name; link.style.color = '#ff69b4'; link.style.textDecoration = 'none'; link.style.wordBreak = 'break-all'; fileEntry.appendChild(link); fileList.appendChild(fileEntry); } } catch (e) { console.error('Failed to fetch or analyze file:', name, e); } }); })();