IMVU Creator PID Backup Utility - JSON Numbers

Scrapes product IDs and names from IMVU shop pages, navigating through all pages, showing a cute button, and saving results as JSON.

  1. // ==UserScript==
  2. // @name IMVU Creator PID Backup Utility - JSON Numbers
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Scrapes product IDs and names from IMVU shop pages, navigating through all pages, showing a cute button, and saving results as JSON.
  6. // @author heapsofjoy
  7. // @match https://www.imvu.com/shop/web_search.php?manufacturers_id=*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Create a button to trigger scraping
  15. const button = document.createElement('button');
  16. button.innerHTML = '💖 Scrape All Pages 💖'; // Add a cute emoji to the button
  17. button.style.position = 'fixed';
  18. button.style.bottom = '10px'; // Position near the bottom of the page
  19. button.style.right = '10px'; // Position near the right edge of the page
  20. button.style.zIndex = '10001';
  21. button.style.padding = '12px 20px';
  22. button.style.backgroundColor = '#ff69b4'; // Pink color
  23. button.style.color = 'white';
  24. button.style.border = 'none';
  25. button.style.borderRadius = '20px'; // Rounded corners
  26. button.style.fontFamily = 'Arial, sans-serif';
  27. button.style.fontSize = '16px';
  28. button.style.cursor = 'pointer';
  29. button.style.boxShadow = '0px 4px 10px rgba(0,0,0,0.1)';
  30. document.body.appendChild(button);
  31.  
  32. // Create a sidebar to hold product IDs and names (above the button)
  33. const sidebar = document.createElement('div');
  34. sidebar.id = 'product-sidebar';
  35. sidebar.style.position = 'fixed';
  36. sidebar.style.bottom = '115px'; // Position above the button
  37. sidebar.style.right = '10px';
  38. sidebar.style.width = '300px';
  39. sidebar.style.height = '80%';
  40. sidebar.style.overflowY = 'scroll';
  41. sidebar.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
  42. sidebar.style.color = 'white';
  43. sidebar.style.padding = '10px';
  44. sidebar.style.fontFamily = 'Arial, sans-serif';
  45. sidebar.style.fontSize = '14px';
  46. sidebar.style.zIndex = '10000';
  47. sidebar.style.borderRadius = '8px';
  48. sidebar.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.5)';
  49. sidebar.style.display = 'none'; // Hidden until button is pressed
  50. document.body.appendChild(sidebar);
  51.  
  52. const header = document.createElement('h2');
  53. header.textContent = 'Product List';
  54. header.style.color = 'yellow';
  55. sidebar.appendChild(header);
  56.  
  57. const productList = document.createElement('ul');
  58. productList.id = 'product-list';
  59. productList.style.listStyleType = 'none';
  60. productList.style.padding = '0';
  61. sidebar.appendChild(productList);
  62.  
  63. // Create a Save as JSON button (hidden initially)
  64. const saveButton = document.createElement('button');
  65. saveButton.textContent = 'Save as JSON';
  66. saveButton.style.display = 'none'; // Hidden until scraping completes
  67. saveButton.style.marginTop = '10px';
  68. saveButton.style.padding = '10px 15px';
  69. saveButton.style.backgroundColor = '#ff69b4'; // Pink color
  70. saveButton.style.color = 'white';
  71. saveButton.style.border = 'none';
  72. saveButton.style.borderRadius = '20px'; // Rounded corners
  73. saveButton.style.cursor = 'pointer';
  74. sidebar.appendChild(saveButton);
  75.  
  76. // Create loading spinner inside the button
  77. const loadingSpinner = document.createElement('div');
  78. loadingSpinner.id = 'loading-spinner';
  79. loadingSpinner.style.display = 'none'; // Hidden initially
  80. loadingSpinner.style.width = '16px';
  81. loadingSpinner.style.height = '16px';
  82. loadingSpinner.style.border = '3px solid #f3f3f3';
  83. loadingSpinner.style.borderTop = '3px solid white'; // Matching white to the button text
  84. loadingSpinner.style.borderRadius = '50%';
  85. loadingSpinner.style.animation = 'spin 1s linear infinite';
  86. loadingSpinner.style.marginLeft = '8px'; // Spacing between the text and the spinner
  87. button.appendChild(loadingSpinner); // Add spinner inside the button
  88.  
  89. // Add the CSS for the spinner animation
  90. const spinnerStyle = document.createElement('style');
  91. spinnerStyle.innerHTML = `
  92. @keyframes spin {
  93. 0% { transform: rotate(0deg); }
  94. 100% { transform: rotate(360deg); }
  95. }
  96. `;
  97. document.head.appendChild(spinnerStyle);
  98.  
  99. // To track all scraped products across pages
  100. const allProducts = [];
  101. const addedProductIds = new Set(); // To track unique product IDs
  102.  
  103. // Function to extract product IDs and names from the current page
  104. function scrapeProducts() {
  105. const productLinks = document.querySelectorAll('a[href*="products_id="]'); // Select all <a> tags that contain "products_id=" in href
  106. const productData = [];
  107.  
  108. productLinks.forEach(product => {
  109. const productIdMatch = product.href.match(/products_id=(\d+)/); // Extract the product ID from the href attribute
  110. const productName = product.getAttribute('title') || product.textContent.trim(); // Get the product name from the title or inner text
  111.  
  112. if (productIdMatch && productName) {
  113. const productId = productIdMatch[1];
  114. if (!addedProductIds.has(productId)) { // If the product hasn't been added yet
  115. productData.push({ id: parseInt(productId), name: productName }); // Ensure ID is treated as a number
  116. addedProductIds.add(productId); // Mark this product ID as added
  117. }
  118. }
  119. });
  120.  
  121. return productData;
  122. }
  123.  
  124. // Function to add leading zeros to numbers (e.g., 1 -> 0001)
  125. function formatNumber(number, digits) {
  126. return number.toString().padStart(digits, '0');
  127. }
  128.  
  129. // Function to add product data to the sidebar
  130. function displayProducts(products) {
  131. products.sort((a, b) => a.id - b.id); // Sort by product ID (numeric)
  132. productList.innerHTML = ''; // Clear any existing content
  133.  
  134. products.forEach((product, index) => {
  135. const listItem = document.createElement('li');
  136. const formattedIndex = formatNumber(index + 1, 4); // Add leading zeros to the index (e.g., 0001, 0002, ...)
  137. listItem.textContent = `${formattedIndex}. ${product.name} - (${product.id})`;
  138. productList.appendChild(listItem);
  139. });
  140. }
  141.  
  142. // Function to navigate to a specific page by modifying the URL
  143. function navigateToPage(pageNumber, manufacturerId) {
  144. const newUrl = `https://www.imvu.com/shop/web_search.php?manufacturers_id=${manufacturerId}&page=${pageNumber}`;
  145. window.history.pushState({}, '', newUrl); // Change URL without reloading the page
  146. return fetch(newUrl)
  147. .then(response => response.text())
  148. .then(html => {
  149. const parser = new DOMParser();
  150. const doc = parser.parseFromString(html, 'text/html');
  151. return doc;
  152. });
  153. }
  154.  
  155. // Function to scrape through all pages one by one
  156. function scrapeAllPages(pageNumber, manufacturerId) {
  157. navigateToPage(pageNumber, manufacturerId).then(doc => {
  158. const productLinks = doc.querySelectorAll('a[href*="products_id="]');
  159. if (productLinks.length === 0) {
  160. // No more products on this page, stop
  161. sidebar.style.display = 'block';
  162. loadingSpinner.style.display = 'none'; // Hide loading spinner
  163. button.innerHTML = '💖 Scrape All Pages 💖'; // Reset the button text
  164. displayProducts(allProducts); // Display all products collected
  165. navigateToPage(1, manufacturerId); // Navigate back to page 1
  166.  
  167. // Show the Save as JSON button
  168. saveButton.style.display = 'block';
  169.  
  170. return;
  171. }
  172.  
  173. // Scrape products from the current page
  174. const productData = [];
  175. productLinks.forEach(product => {
  176. const productIdMatch = product.href.match(/products_id=(\d+)/);
  177. const productName = product.getAttribute('title') || product.textContent.trim();
  178. if (productIdMatch && productName) {
  179. const productId = productIdMatch[1];
  180. if (!addedProductIds.has(productId)) {
  181. productData.push({ id: parseInt(productId), name: productName });
  182. addedProductIds.add(productId);
  183. }
  184. }
  185. });
  186.  
  187. allProducts.push(...productData); // Append new products
  188.  
  189. // Go to the next page
  190. scrapeAllPages(pageNumber + 1, manufacturerId);
  191. });
  192. }
  193.  
  194. // Add event listener to the button to trigger scraping
  195. button.addEventListener('click', function() {
  196. const manufacturerIdMatch = window.location.href.match(/manufacturers_id=(\d+)/);
  197. if (!manufacturerIdMatch) {
  198. alert('Manufacturer ID not found in URL.');
  199. return;
  200. }
  201.  
  202. const manufacturerId = manufacturerIdMatch[1];
  203.  
  204. // Clear previous products
  205. allProducts.length = 0;
  206. addedProductIds.clear();
  207. productList.innerHTML = ''; // Clear any existing content
  208.  
  209. // Show loading spinner and update button text
  210. loadingSpinner.style.display = 'inline-block';
  211. button.innerHTML = '💖 Scraping... ';
  212. button.appendChild(loadingSpinner);
  213.  
  214. // Start scraping from page 1
  215. scrapeAllPages(1, manufacturerId);
  216. });
  217.  
  218. // Add event listener to the Save as JSON button to save the data
  219. saveButton.addEventListener('click', function() {
  220. const productsWithOrder = allProducts.map((product, index) => ({
  221. order: formatNumber(index + 1, 4), // Add leading zeros to order (e.g., 0001)
  222. id: product.id,
  223. name: product.name
  224. }));
  225.  
  226. const jsonContent = JSON.stringify(productsWithOrder, null, 2);
  227. const blob = new Blob([jsonContent], { type: 'application/json' });
  228. const url = URL.createObjectURL(blob);
  229. const a = document.createElement('a');
  230. a.href = url;
  231. a.download = 'imvu_products.json';
  232. a.click();
  233. URL.revokeObjectURL(url);
  234. });
  235.  
  236. })();