Advertisement
kkulczyk4

Fansly images download

Apr 30th, 2025
216
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function() {
  2.     // --- Configuration ---
  3.     const scriptIdentifier = 'downloader-script'; // Unique identifier for elements created by this script
  4.     const imageSelector = '.media-slider .selected-image img.image.contain-no-grow'; // Selector for images
  5.     const nextButtonSelector = '.modal-next-button'; // Selector for the website's next button
  6.     const prevButtonSelector = '.modal-prev-button'; // Selector for the website's previous button
  7.     const closeWebsiteButtonSelector = '.modal-close-button'; // Selector for the website's close button
  8.     const detectionDelay = 1000; // Increased milliseconds to wait after a click before checking for images
  9.     const updateDelay = 700; // Milliseconds to wait after simulating navigation before rescanning
  10.     const thumbnailWidth = '200px'; // Desired width for image thumbnails
  11.  
  12.     // --- Cleanup Previous Instances ---
  13.     console.log(`Checking for and removing previous instances of '${scriptIdentifier}' script elements...`);
  14.     const previousElements = document.querySelectorAll(`[data-${scriptIdentifier}="true"]`);
  15.     previousElements.forEach(el => {
  16.         console.log(`Removing previous element:`, el);
  17.         if (el.parentNode) {
  18.             el.parentNode.removeChild(el);
  19.         }
  20.     });
  21.     // Note: Removing previous global event listeners added anonymously is difficult.
  22.     // This cleanup focuses on removing the DOM elements created by previous runs.
  23.     // The logic below is designed to only operate with the *new* elements it creates.
  24.  
  25.  
  26.     // --- Global variables ---
  27.     let overlay = null; // Holds the main overlay element when active
  28.     let imageGridContainer = null; // Holds the div for image thumbnails inside the overlay
  29.     let statusIndicator = null; // Holds the persistent top-center status/button element
  30.     let statusTextElement = null; // Holds the "Downloader active" text element
  31.     let activateButtonElement = null; // Holds the "Downloader" button element
  32.  
  33.     // --- Function to simulate click on a website button ---
  34.     function simulateWebsiteButtonClick(selector) {
  35.          const targetButton = document.querySelector(selector);
  36.          if (targetButton) {
  37.              console.log(`Simulating click on website button: ${selector}`);
  38.              targetButton.click(); // Trigger the website's event handler
  39.              return true; // Indicate button was found and clicked
  40.          } else {
  41.              console.warn(`Website button not found: ${selector}.`);
  42.              return false; // Indicate button was not found
  43.          }
  44.     }
  45.  
  46.     // --- Function to display temporary messages in the overlay ---
  47.     function showOverlayMessage(messageText, color = 'yellow', duration = 3000) {
  48.          if (!overlay) {
  49.              console.error("Overlay not initialized, cannot show message.");
  50.              return;
  51.          }
  52.  
  53.          // Remove any previous messages first within this overlay instance
  54.          overlay.querySelectorAll('.overlay-message').forEach(msg => msg.remove());
  55.  
  56.          const messageElement = document.createElement('p');
  57.          messageElement.innerText = messageText;
  58.          messageElement.style.color = color;
  59.          messageElement.style.marginTop = '10px';
  60.          messageElement.style.padding = '10px';
  61.          messageElement.style.backgroundColor = 'rgba(0,0,0,0.5)';
  62.          messageElement.style.borderRadius = '5px';
  63.          messageElement.style.textAlign = 'center';
  64.          messageElement.classList.add('overlay-message'); // Add a class for easy selection/removal
  65.          messageElement.style.zIndex = '10002'; // Ensure message is above image grid
  66.  
  67.  
  68.          // Find a reference point below the close button and above the image grid
  69.          // This makes the message appear consistently near the top
  70.          const referenceElement = imageGridContainer || overlay.querySelector('button');
  71.           if(referenceElement) {
  72.                // Insert message before the reference element
  73.                overlay.insertBefore(messageElement, referenceElement);
  74.           } else {
  75.               // Fallback: append to the overlay
  76.               overlay.appendChild(messageElement);
  77.           }
  78.  
  79.  
  80.          // Remove the message after a duration
  81.          setTimeout(() => {
  82.              if(messageElement && messageElement.parentNode) {
  83.                  messageElement.parentNode.removeChild(messageElement);
  84.              }
  85.          }, duration);
  86.     }
  87.  
  88.  
  89.     // --- Function to select images and update the overlay content ---
  90.     function updateOverlayContent() {
  91.         // Ensure the image grid container exists for the *current* overlay instance
  92.         if (!imageGridContainer || !overlay || !document.body.contains(overlay)) {
  93.              console.warn("Overlay or image grid container not active/initialized. Cannot update content.");
  94.              return;
  95.         }
  96.  
  97.         // Clear previously loaded images and messages within the grid container
  98.         imageGridContainer.innerHTML = '';
  99.          // Also clear messages that might be outside the grid container but within overlay
  100.          overlay.querySelectorAll('.overlay-message').forEach(msg => msg.remove());
  101.  
  102.  
  103.         const images = document.querySelectorAll(imageSelector);
  104.  
  105.         if (images.length === 0) {
  106.             showOverlayMessage('No images found on this gallery slide with the provided selector.', 'yellow', 5000);
  107.             console.log('No images found with the provided selector on this slide.');
  108.             return;
  109.         }
  110.  
  111.         // Add new images and download buttons
  112.         images.forEach((img, index) => {
  113.             const thumbContainer = document.createElement('div');
  114.             thumbContainer.style.margin = '10px';
  115.             thumbContainer.style.border = '1px solid #ccc';
  116.             thumbContainer.style.padding = '5px';
  117.             thumbContainer.style.backgroundColor = '#fff';
  118.             thumbContainer.style.display = 'flex';
  119.             thumbContainer.style.flexDirection = 'column';
  120.             thumbContainer.style.alignItems = 'center';
  121.             thumbContainer.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
  122.             thumbContainer.style.width = `calc(${thumbnailWidth} + 10px)`; // Container slightly wider than thumb + padding
  123.             thumbContainer.style.flexShrink = '0'; // Prevent shrinking
  124.  
  125.  
  126.             const thumb = document.createElement('img');
  127.             thumb.src = img.src;
  128.             thumb.style.width = thumbnailWidth; // Use the configured width
  129.             thumb.style.height = 'auto'; // Maintain aspect ratio
  130.             thumb.style.display = 'block';
  131.             thumb.style.marginBottom = '10px';
  132.  
  133.             const downloadButton = document.createElement('a');
  134.             downloadButton.href = img.src;
  135.  
  136.             // Attempt to create a filename from the URL
  137.             const urlParts = img.src.split('/');
  138.             let filename = urlParts.pop();
  139.             if (!filename || filename.indexOf('.') === -1 || filename.startsWith('?')) { // Added check for query param only filename
  140.                  filename = `image_${index + 1}.png`;
  141.             } else {
  142.                 filename = filename.split('?')[0]; // Remove query parameters
  143.             }
  144.             downloadButton.download = filename;
  145.  
  146.             downloadButton.innerText = 'Download';
  147.             downloadButton.style.display = 'block';
  148.             downloadButton.style.width = '100%'; // Make button fill the container width
  149.             downloadButton.style.padding = '5px 0'; // Adjust padding
  150.             downloadButton.style.backgroundColor = '#007bff';
  151.             downloadButton.style.color = '#fff';
  152.             downloadButton.style.textDecoration = 'none';
  153.             downloadButton.style.borderRadius = '5px';
  154.             downloadButton.style.textAlign = 'center';
  155.             downloadButton.style.cursor = 'pointer';
  156.             downloadButton.style.transition = 'background-color 0.3s ease';
  157.  
  158.             downloadButton.onmouseover = function() { this.style.backgroundColor = '#0056b3'; };
  159.             downloadButton.onmouseout = function() { this.style.backgroundColor = '#007bff'; };
  160.  
  161.  
  162.             thumbContainer.appendChild(thumb);
  163.             thumbContainer.appendChild(downloadButton);
  164.             imageGridContainer.appendChild(thumbContainer);
  165.         });
  166.     }
  167.  
  168.      // --- Function to create and activate the downloader overlay ---
  169.     function activateDownloaderOverlay() {
  170.         // Check if overlay is already active by checking the global variable
  171.         if (overlay !== null) {
  172.             console.log("Downloader overlay is already active.");
  173.             return; // Don't create a new one if one exists
  174.         }
  175.  
  176.         console.log("Activating downloader overlay...");
  177.  
  178.         // Create the main overlay element
  179.         overlay = document.createElement('div');
  180.         overlay.setAttribute(`data-${scriptIdentifier}`, 'true'); // Mark element for cleanup
  181.         overlay.style.position = 'fixed';
  182.         overlay.style.top = '0';
  183.         overlay.style.left = '0';
  184.         overlay.style.width = '100%';
  185.         overlay.style.height = '100%';
  186.         overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; // Semi-transparent black
  187.         overlay.style.zIndex = '10000'; // Ensure it's on top
  188.         overlay.style.overflowY = 'auto'; // Enable scrolling
  189.         overlay.style.display = 'flex';
  190.         overlay.style.flexDirection = 'column'; // Stack content vertically
  191.         overlay.style.alignItems = 'center'; // Center items horizontally
  192.         overlay.style.padding = '20px';
  193.         overlay.style.boxSizing = 'border-box';
  194.         overlay.style.color = '#fff'; // Default text color for messages
  195.  
  196.         // Add a Close button to the overlay
  197.         const closeButton = document.createElement('button');
  198.         closeButton.innerText = 'Close Gallery View';
  199.         closeButton.style.position = 'absolute';
  200.         closeButton.style.top = '10px';
  201.         closeButton.style.right = '10px';
  202.         closeButton.style.padding = '10px';
  203.         closeButton.style.cursor = 'pointer';
  204.         closeButton.style.zIndex = '10001'; // Ensure button is above other content
  205.         closeButton.onclick = function() {
  206.             // --- Action when overlay close button is clicked ---
  207.             console.log("Closing downloader overlay...");
  208.  
  209.             // 1. Simulate click on the website's close button
  210.             simulateWebsiteButtonClick(closeWebsiteButtonSelector);
  211.  
  212.             // 2. Remove this overlay from the DOM
  213.             if (overlay && overlay.parentNode) {
  214.                overlay.parentNode.removeChild(overlay);
  215.             }
  216.  
  217.             // 3. Clean up global references
  218.             overlay = null;
  219.             imageGridContainer = null;
  220.              console.log("Overlay closed and references cleared.");
  221.  
  222.             // 4. Show the "Downloader active" status again
  223.              showStatusText();
  224.         };
  225.         overlay.appendChild(closeButton);
  226.  
  227.         // Create container for the image grid
  228.         imageGridContainer = document.createElement('div');
  229.         imageGridContainer.style.display = 'flex';
  230.         imageGridContainer.style.flexWrap = 'wrap';
  231.         imageGridContainer.style.justifyContent = 'center';
  232.         imageGridContainer.style.width = '100%';
  233.         imageGridContainer.style.marginTop = '60px'; // Adjust spacing below close button
  234.         imageGridContainer.style.marginBottom = '20px';
  235.         overlay.appendChild(imageGridContainer);
  236.  
  237.         // Create container for navigation buttons
  238.         const navButtonContainer = document.createElement('div');
  239.         navButtonContainer.style.display = 'flex';
  240.         navButtonContainer.style.gap = '20px';
  241.         navButtonContainer.style.marginBottom = '20px';
  242.  
  243.         const prevButton = document.createElement('button');
  244.         prevButton.innerText = 'Previous';
  245.         prevButton.style.padding = '10px 20px';
  246.         prevButton.style.cursor = 'pointer';
  247.         prevButton.onclick = function() {
  248.             const clicked = simulateWebsiteButtonClick(prevButtonSelector);
  249.              if(clicked) {
  250.                 // Only update overlay if the website button was found and clicked
  251.                 setTimeout(updateOverlayContent, updateDelay);
  252.              } else {
  253.                 // Optionally show a message in the overlay if nav button wasn't found
  254.                 showOverlayMessage(`Could not find website's Previous button: ${prevButtonSelector}`);
  255.             }
  256.        };
  257.        navButtonContainer.appendChild(prevButton);
  258.  
  259.        const nextButton = document.createElement('button');
  260.        nextButton.innerText = 'Next';
  261.        nextButton.style.padding = '10px 20px';
  262.        nextButton.style.cursor = 'pointer';
  263.        nextButton.onclick = function() {
  264.             const clicked = simulateWebsiteButtonClick(nextButtonSelector);
  265.             if(clicked) {
  266.                // Only update overlay if the website button was found and clicked
  267.                setTimeout(updateOverlayContent, updateDelay);
  268.             } else {
  269.                // Optionally show a message in the overlay if nav button wasn't found
  270.                 showOverlayMessage(`Could not find website's Next button: ${nextButtonSelector}`);
  271.             }
  272.        };
  273.        navButtonContainer.appendChild(nextButton);
  274.  
  275.        overlay.appendChild(navButtonContainer);
  276.  
  277.        // Add the overlay to the document body
  278.        document.body.appendChild(overlay);
  279.  
  280.        // Populate the overlay with images from the current view immediately
  281.        updateOverlayContent();
  282.    }
  283.  
  284.    // --- Functions to manage the status indicator ---
  285.    function setupStatusIndicator() {
  286.        // Check if status indicator is already set up by checking the global variable
  287.        if (statusIndicator !== null) {
  288.            return; // Already set up
  289.        }
  290.  
  291.        console.log("Setting up status indicator.");
  292.  
  293.        statusIndicator = document.createElement('div');
  294.        statusIndicator.setAttribute(`data-${scriptIdentifier}`, 'true'); // Mark element for cleanup
  295.        statusIndicator.style.position = 'fixed';
  296.        statusIndicator.style.top = '10px';
  297.        statusIndicator.style.left = '50%';
  298.        statusIndicator.style.transform = 'translateX(-50%)'; // Center horizontally
  299.        statusIndicator.style.zIndex = '9999'; // Below the main overlay
  300.        statusIndicator.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  301.        statusIndicator.style.color = '#fff';
  302.        statusIndicator.style.padding = '8px 15px';
  303.        statusIndicator.style.borderRadius = '5px';
  304.        statusIndicator.style.fontSize = '14px';
  305.        statusIndicator.style.display = 'flex'; // Use flex for internal elements
  306.        statusIndicator.style.alignItems = 'center';
  307.        statusIndicator.style.pointerEvents = 'none'; // Make the container non-interactive by default
  308.  
  309.  
  310.        statusTextElement = document.createElement('span');
  311.        statusTextElement.innerText = 'Downloader active';
  312.         statusTextElement.style.pointerEvents = 'none'; // Text is not clickable
  313.        statusIndicator.appendChild(statusTextElement);
  314.  
  315.        activateButtonElement = document.createElement('button');
  316.        activateButtonElement.innerText = 'Downloader';
  317.        activateButtonElement.style.display = 'none'; // Hidden initially
  318.        activateButtonElement.style.marginLeft = '10px'; // Space between text and button
  319.        activateButtonElement.style.padding = '5px 10px';
  320.        activateButtonElement.style.cursor = 'pointer';
  321.        activateButtonElement.style.pointerEvents = 'auto'; // Button is clickable
  322.         activateButtonElement.onclick = function(event) {
  323.              console.log("Activate button clicked.");
  324.              event.stopPropagation(); // Prevent the click from bubbling up
  325.              hideStatusIndicatorElements(); // Hide status indicator elements
  326.              activateDownloaderOverlay(); // Activate the overlay
  327.         };
  328.         statusIndicator.appendChild(activateButtonElement);
  329.  
  330.        document.body.appendChild(statusIndicator);
  331.  
  332.        // Initially show the "active" text
  333.        showStatusText();
  334.    }
  335.  
  336.    function showStatusText() {
  337.         // Check if the current status indicator elements exist before trying to update
  338.         if (statusTextElement && activateButtonElement && statusIndicator && document.body.contains(statusIndicator)) {
  339.             statusTextElement.style.display = 'inline';
  340.             activateButtonElement.style.display = 'none';
  341.             statusIndicator.style.cursor = 'default'; // Not clickable when just text
  342.             statusIndicator.style.pointerEvents = 'none'; // Make the entire indicator non-interactive
  343.             statusIndicator.onclick = null; // Ensure no click listener is attached to the container
  344.             console.log("Showing status text.");
  345.         } else {
  346.             console.warn("Status indicator elements not found, cannot show status text.");
  347.         }
  348.    }
  349.  
  350.    function showActivateButton() {
  351.        // Only show button if overlay is not active and status indicator elements exist
  352.        if (statusTextElement && activateButtonElement && statusIndicator && document.body.contains(statusIndicator) && overlay === null) {
  353.            statusTextElement.style.display = 'none';
  354.            activateButtonElement.style.display = 'inline-block';
  355.            statusIndicator.style.cursor = 'pointer'; // Make the indicator clickable
  356.            statusIndicator.style.pointerEvents = 'auto'; // Make the entire indicator interactive
  357.  
  358.             // Attach click listener to the indicator container to activate the overlay
  359.             statusIndicator.onclick = function(event) {
  360.                 console.log("Status indicator (button state) clicked.");
  361.                 event.stopPropagation(); // Prevent the click from bubbling
  362.                 hideStatusIndicatorElements(); // Hide the indicator elements
  363.                 activateDownloaderOverlay(); // Activate the overlay
  364.                 statusIndicator.onclick = null; // Remove this specific click listener after use
  365.             };
  366.  
  367.             // Note: The activateButtonElement also has its own click listener,
  368.             // which provides a smaller clickable target within the indicator.
  369.             // Both lead to the same activation logic.
  370.             console.log("Showing activate button.");
  371.  
  372.        } else {
  373.            console.warn("Status indicator elements not found or overlay is active, cannot show activate button.");
  374.             // If elements are missing but overlay is not active, maybe set up indicator again?
  375.             // Removed auto-setup logic here to keep it simpler and rely on user re-running script if needed.
  376.             if (overlay !== null) {
  377.                  console.log("Overlay is active, not showing activate button.");
  378.             }
  379.        }
  380.    }
  381.  
  382.    function hideStatusIndicatorElements() {
  383.         // Check if the current status indicator elements exist before trying to update
  384.         if (statusTextElement && activateButtonElement && statusIndicator && document.body.contains(statusIndicator)) {
  385.            statusTextElement.style.display = 'none';
  386.            activateButtonElement.style.display = 'none';
  387.            statusIndicator.style.cursor = 'default';
  388.            statusIndicator.style.pointerEvents = 'none'; // Make non-interactive
  389.            statusIndicator.onclick = null; // Ensure click listener is removed
  390.             console.log("Hiding status indicator elements.");
  391.         } else {
  392.              console.warn("Status indicator elements not found, cannot hide elements.");
  393.         }
  394.    }
  395.  
  396.  
  397.    // --- Global Event Listener to detect gallery opening ---
  398.    // This listener is added *once* when the script runs and persists.
  399.    // Its logic checks for the presence of our *current* statusIndicator before acting.
  400.    document.addEventListener('click', function(event) {
  401.        // If our overlay is already active, do not try to detect a new gallery
  402.        if (overlay !== null) {
  403.            return;
  404.        }
  405.  
  406.        // Only proceed if our status indicator element exists (means our script is running)
  407.        if (statusIndicator === null || !document.body.contains(statusIndicator)) {
  408.             console.log("Click detected, but status indicator not found. Script may not be fully initialized or its elements were removed manually.");
  409.             return; // Don't run detection logic if indicator is gone
  410.         }
  411.  
  412.         console.log(`Click detected. Waiting ${detectionDelay}ms to check for gallery images.`);
  413.  
  414.         // Wait a short time to allow DOM to update after click (e.g., gallery modal opening)
  415.         setTimeout(() => {
  416.              // Re-check if the statusIndicator is still valid after the timeout
  417.              if (statusIndicator === null || !document.body.contains(statusIndicator)) {
  418.                   console.log("Timeout finished, but status indicator not found. Skipping gallery detection.");
  419.                   return;
  420.              }
  421.  
  422.             // Check if images matching the selector are now present
  423.             const images = document.querySelectorAll(imageSelector);
  424.  
  425.             if (images.length > 0) {
  426.                 console.log(`Gallery detected (${images.length} images found). Checking if Downloader button should be shown.`);
  427.                 // If images are found and our overlay is not active, show the "Downloader" button
  428.                 if (overlay === null) {
  429.                     showActivateButton();
  430.                 } else {
  431.                     console.log("Gallery detected, but overlay is already active.");
  432.                 }
  433.             } else {
  434.                 console.log("No gallery images found after delay.");
  435.                 // If no images are found, ensure the status text is shown (gallery closed or not opened)
  436.                  if (overlay === null) { // Only show status text if overlay is not active
  437.                      showStatusText();
  438.                  } else {
  439.                      console.log("No images found, but overlay is active.");
  440.                  }
  441.             }
  442.         }, detectionDelay);
  443.     });
  444.  
  445.  
  446.     // --- Script Execution Start ---
  447.  
  448.     // 1. Set up the persistent status indicator
  449.     // This also handles checking if it's already set up by this *instance* of the script.
  450.     setupStatusIndicator();
  451.  
  452.     // The global click listener and the activate button will handle showing the overlay
  453.     // when a gallery is detected and the user clicks the button/indicator.
  454.  
  455.     console.log(`'${scriptIdentifier}' script initialized and listening for clicks.`);
  456.  
  457. })();
  458.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement