- // ==UserScript==
- // @name IMVU Creator PID Backup Utility - JSON Numbers
- // @namespace http://tampermonkey.net/
- // @version 1.1
- // @description Scrapes product IDs and names from IMVU shop pages, navigating through all pages, showing a cute button, and saving results as JSON.
- // @author heapsofjoy
- // @match https://www.imvu.com/shop/web_search.php?manufacturers_id=*
- // @grant none
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- // Create a button to trigger scraping
- const button = document.createElement('button');
- button.innerHTML = '💖 Scrape All Pages 💖'; // Add a cute emoji to the button
- button.style.position = 'fixed';
- button.style.bottom = '10px'; // Position near the bottom of the page
- button.style.right = '10px'; // Position near the right edge of the page
- button.style.zIndex = '10001';
- button.style.padding = '12px 20px';
- button.style.backgroundColor = '#ff69b4'; // Pink color
- button.style.color = 'white';
- button.style.border = 'none';
- button.style.borderRadius = '20px'; // Rounded corners
- button.style.fontFamily = 'Arial, sans-serif';
- button.style.fontSize = '16px';
- button.style.cursor = 'pointer';
- button.style.boxShadow = '0px 4px 10px rgba(0,0,0,0.1)';
- document.body.appendChild(button);
-
- // Create a sidebar to hold product IDs and names (above the button)
- const sidebar = document.createElement('div');
- sidebar.id = 'product-sidebar';
- sidebar.style.position = 'fixed';
- sidebar.style.bottom = '115px'; // Position above the button
- sidebar.style.right = '10px';
- sidebar.style.width = '300px';
- sidebar.style.height = '80%';
- sidebar.style.overflowY = 'scroll';
- sidebar.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
- sidebar.style.color = 'white';
- sidebar.style.padding = '10px';
- sidebar.style.fontFamily = 'Arial, sans-serif';
- sidebar.style.fontSize = '14px';
- sidebar.style.zIndex = '10000';
- sidebar.style.borderRadius = '8px';
- sidebar.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.5)';
- sidebar.style.display = 'none'; // Hidden until button is pressed
- document.body.appendChild(sidebar);
-
- const header = document.createElement('h2');
- header.textContent = 'Product List';
- header.style.color = 'yellow';
- sidebar.appendChild(header);
-
- const productList = document.createElement('ul');
- productList.id = 'product-list';
- productList.style.listStyleType = 'none';
- productList.style.padding = '0';
- sidebar.appendChild(productList);
-
- // Create a Save as JSON button (hidden initially)
- const saveButton = document.createElement('button');
- saveButton.textContent = 'Save as JSON';
- saveButton.style.display = 'none'; // Hidden until scraping completes
- saveButton.style.marginTop = '10px';
- saveButton.style.padding = '10px 15px';
- saveButton.style.backgroundColor = '#ff69b4'; // Pink color
- saveButton.style.color = 'white';
- saveButton.style.border = 'none';
- saveButton.style.borderRadius = '20px'; // Rounded corners
- saveButton.style.cursor = 'pointer';
- sidebar.appendChild(saveButton);
-
- // Create loading spinner inside the button
- const loadingSpinner = document.createElement('div');
- loadingSpinner.id = 'loading-spinner';
- loadingSpinner.style.display = 'none'; // Hidden initially
- loadingSpinner.style.width = '16px';
- loadingSpinner.style.height = '16px';
- loadingSpinner.style.border = '3px solid #f3f3f3';
- loadingSpinner.style.borderTop = '3px solid white'; // Matching white to the button text
- loadingSpinner.style.borderRadius = '50%';
- loadingSpinner.style.animation = 'spin 1s linear infinite';
- loadingSpinner.style.marginLeft = '8px'; // Spacing between the text and the spinner
- button.appendChild(loadingSpinner); // Add spinner inside the button
-
- // Add the CSS for the spinner animation
- const spinnerStyle = document.createElement('style');
- spinnerStyle.innerHTML = `
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- `;
- document.head.appendChild(spinnerStyle);
-
- // To track all scraped products across pages
- const allProducts = [];
- const addedProductIds = new Set(); // To track unique product IDs
-
- // Function to extract product IDs and names from the current page
- function scrapeProducts() {
- const productLinks = document.querySelectorAll('a[href*="products_id="]'); // Select all <a> tags that contain "products_id=" in href
- const productData = [];
-
- productLinks.forEach(product => {
- const productIdMatch = product.href.match(/products_id=(\d+)/); // Extract the product ID from the href attribute
- const productName = product.getAttribute('title') || product.textContent.trim(); // Get the product name from the title or inner text
-
- if (productIdMatch && productName) {
- const productId = productIdMatch[1];
- if (!addedProductIds.has(productId)) { // If the product hasn't been added yet
- productData.push({ id: parseInt(productId), name: productName }); // Ensure ID is treated as a number
- addedProductIds.add(productId); // Mark this product ID as added
- }
- }
- });
-
- return productData;
- }
-
- // Function to add leading zeros to numbers (e.g., 1 -> 0001)
- function formatNumber(number, digits) {
- return number.toString().padStart(digits, '0');
- }
-
- // Function to add product data to the sidebar
- function displayProducts(products) {
- products.sort((a, b) => a.id - b.id); // Sort by product ID (numeric)
- productList.innerHTML = ''; // Clear any existing content
-
- products.forEach((product, index) => {
- const listItem = document.createElement('li');
- const formattedIndex = formatNumber(index + 1, 4); // Add leading zeros to the index (e.g., 0001, 0002, ...)
- listItem.textContent = `${formattedIndex}. ${product.name} - (${product.id})`;
- productList.appendChild(listItem);
- });
- }
-
- // Function to navigate to a specific page by modifying the URL
- function navigateToPage(pageNumber, manufacturerId) {
- const newUrl = `https://www.imvu.com/shop/web_search.php?manufacturers_id=${manufacturerId}&page=${pageNumber}`;
- window.history.pushState({}, '', newUrl); // Change URL without reloading the page
- return fetch(newUrl)
- .then(response => response.text())
- .then(html => {
- const parser = new DOMParser();
- const doc = parser.parseFromString(html, 'text/html');
- return doc;
- });
- }
-
- // Function to scrape through all pages one by one
- function scrapeAllPages(pageNumber, manufacturerId) {
- navigateToPage(pageNumber, manufacturerId).then(doc => {
- const productLinks = doc.querySelectorAll('a[href*="products_id="]');
- if (productLinks.length === 0) {
- // No more products on this page, stop
- sidebar.style.display = 'block';
- loadingSpinner.style.display = 'none'; // Hide loading spinner
- button.innerHTML = '💖 Scrape All Pages 💖'; // Reset the button text
- displayProducts(allProducts); // Display all products collected
- navigateToPage(1, manufacturerId); // Navigate back to page 1
-
- // Show the Save as JSON button
- saveButton.style.display = 'block';
-
- return;
- }
-
- // Scrape products from the current page
- const productData = [];
- productLinks.forEach(product => {
- const productIdMatch = product.href.match(/products_id=(\d+)/);
- const productName = product.getAttribute('title') || product.textContent.trim();
- if (productIdMatch && productName) {
- const productId = productIdMatch[1];
- if (!addedProductIds.has(productId)) {
- productData.push({ id: parseInt(productId), name: productName });
- addedProductIds.add(productId);
- }
- }
- });
-
- allProducts.push(...productData); // Append new products
-
- // Go to the next page
- scrapeAllPages(pageNumber + 1, manufacturerId);
- });
- }
-
- // Add event listener to the button to trigger scraping
- button.addEventListener('click', function() {
- const manufacturerIdMatch = window.location.href.match(/manufacturers_id=(\d+)/);
- if (!manufacturerIdMatch) {
- alert('Manufacturer ID not found in URL.');
- return;
- }
-
- const manufacturerId = manufacturerIdMatch[1];
-
- // Clear previous products
- allProducts.length = 0;
- addedProductIds.clear();
- productList.innerHTML = ''; // Clear any existing content
-
- // Show loading spinner and update button text
- loadingSpinner.style.display = 'inline-block';
- button.innerHTML = '💖 Scraping... ';
- button.appendChild(loadingSpinner);
-
- // Start scraping from page 1
- scrapeAllPages(1, manufacturerId);
- });
-
- // Add event listener to the Save as JSON button to save the data
- saveButton.addEventListener('click', function() {
- const productsWithOrder = allProducts.map((product, index) => ({
- order: formatNumber(index + 1, 4), // Add leading zeros to order (e.g., 0001)
- id: product.id,
- name: product.name
- }));
-
- const jsonContent = JSON.stringify(productsWithOrder, null, 2);
- const blob = new Blob([jsonContent], { type: 'application/json' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = 'imvu_products.json';
- a.click();
- URL.revokeObjectURL(url);
- });
-
- })();