Downloads thumbnails from IMVU based on a JSON file
// ==UserScript==
// @name IMVU Thumbnail Downloader for IMVU Creator PID Backup Utility - JSON Numbers
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Downloads thumbnails from IMVU based on a JSON file
// @author heapsofjoy
// @match https://www.imvu.com/shop/web_search.php?manufacturers_id=*
// @grant GM_xmlhttpRequest
// @grant GM_download
// ==/UserScript==
(function() {
'use strict';
// Create a button to trigger thumbnail download
const downloadButton = document.createElement('button');
downloadButton.innerHTML = '📷 Download Thumbnails 📷'; // Add a cute emoji to the button
downloadButton.style.position = 'fixed';
downloadButton.style.bottom = '65px'; // Position above the scraping button
downloadButton.style.right = '10px'; // Position near the right edge of the page
downloadButton.style.zIndex = '10001';
downloadButton.style.padding = '12px 20px';
downloadButton.style.backgroundColor = '#ff69b4'; // Pink color
downloadButton.style.color = 'white';
downloadButton.style.border = 'none';
downloadButton.style.borderRadius = '20px'; // Rounded corners
downloadButton.style.fontFamily = 'Arial, sans-serif';
downloadButton.style.fontSize = '16px';
downloadButton.style.cursor = 'pointer';
downloadButton.style.boxShadow = '0px 4px 10px rgba(0,0,0,0.1)';
document.body.appendChild(downloadButton);
// Create a file input for uploading JSON
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json';
fileInput.style.display = 'none'; // Hidden by default
document.body.appendChild(fileInput);
// Add event listener to the download button
downloadButton.addEventListener('click', function() {
fileInput.click(); // Trigger the file input dialog
});
// Read the JSON file and download thumbnails
fileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const jsonData = JSON.parse(e.target.result);
downloadThumbnails(jsonData);
} catch (error) {
console.error("Error parsing JSON:", error); // Log any JSON parsing errors
alert("Failed to parse JSON: " + error.message); // Alert the user about the error
}
};
reader.readAsText(file);
});
// Function to sanitize file names
function sanitizeFileName(fileName) {
// Remove emojis and disallowed Windows characters
return fileName
.replace(/[\u{1F600}-\u{1F64F}|\u{1F300}-\u{1F5FF}|\u{1F680}-\u{1F6FF}|\u{1F700}-\u{1F77F}|\u{1F800}-\u{1F8FF}|\u{1F900}-\u{1F9FF}|\u{2600}-\u{26FF}|\u{2700}-\u{27BF}|\u{2700}-\u{27BF}|\u{2B50}]/gu, '') // Remove emojis
.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_') // Replace disallowed characters with an underscore
.replace(/^\s+|\s+$/g, '') // Trim leading/trailing whitespace
.replace(/\s+/g, '_') // Replace spaces with underscores
.substring(0, 255); // Limit the length to 255 characters
}
// Function to download thumbnails based on JSON data
async function downloadThumbnails(products) {
if (!Array.isArray(products)) {
console.error("Invalid JSON structure. Expected an array.");
alert("Invalid JSON structure. Please ensure the file contains an array of products.");
return;
}
let page = 1; // Start at page 1
let totalDownloaded = 0; // Count of downloaded images
// Extract manufacturers_id from the current URL
const urlParams = new URLSearchParams(window.location.search);
const manufacturersId = urlParams.get('manufacturers_id');
while (true) {
const currentPageUrl = `https://www.imvu.com/shop/web_search.php?manufacturers_id=${manufacturersId}&page=${page}`;
console.log(`Processing page ${page}...`);
// Fetch the current page's content
const response = await fetchPage(currentPageUrl);
if (!response) {
console.log(`No more pages to process or error fetching page ${page}.`);
break; // Exit if there's an error or no response
}
// Create a DOM parser to extract elements from the fetched HTML
const parser = new DOMParser();
const doc = parser.parseFromString(response, 'text/html');
// Process the thumbnails on the current page
const downloadedCount = await processPage(doc, products);
totalDownloaded += downloadedCount;
if (downloadedCount === 0) {
console.log(`No more products found on page ${page}.`);
break; // Exit if no products were found
}
page++; // Increment to the next page
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait for 2 seconds before fetching the next page
}
alert(`Downloaded a total of ${totalDownloaded} thumbnails successfully.`);
}
// Function to fetch the page content
function fetchPage(url) {
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: (response) => {
if (response.status === 200) {
resolve(response.responseText);
} else {
console.error(`Error fetching page: ${response.status}`);
resolve(null); // Resolve with null if there's an error
}
},
onerror: () => {
console.error(`Network error fetching page: ${url}`);
resolve(null);
}
});
});
}
// Function to process thumbnails on the current page
async function processPage(doc, products) {
const thumbnailLinks = doc.querySelectorAll('a[href*="products_id="]');
let downloadedCount = 0;
thumbnailLinks.forEach(productLink => {
const productIdMatch = productLink.href.match(/products_id=(\d+)/); // Extract the product ID
if (!productIdMatch) return;
const productId = productIdMatch[1];
const productData = products.find(p => p.id == productId);
if (!productData) return; // Skip if no matching data found
const order = productData.order;
const name = sanitizeFileName(productData.name); // Sanitize the product name for the filename
// Get the image source from the thumbnail link
const thumbnailImg = productLink.querySelector('img.thumbnail');
if (!thumbnailImg) {
console.warn(`No image found for product ID: ${productId}`);
return;
}
const thumbnailUrl = thumbnailImg.src; // Get the correct thumbnail URL
const extension = thumbnailUrl.split('.').pop(); // Get the file extension (png, jpg, gif, etc.)
console.log(`Downloading thumbnail from: ${thumbnailUrl}`); // Log the thumbnail URL for debugging
// Use GM_download to handle the download without redirecting
GM_download({
url: thumbnailUrl,
name: `Product_${order}_${name}.${extension}`, // Name the file based on the order, name, and extension
saveAs: false,
onerror: (error) => console.error("Download error: ", error)
});
downloadedCount++;
});
return downloadedCount; // Return number of downloaded images
}
})();