Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Stealthgram Carousel Media Gallery Toggle
- // @namespace http://tampermonkey.net/
- // @version 1.4
- // @description Floating button to extract all images/videos from carousels and show in a modal gallery; cleans up on toggle-off
- // @author Your Name
- // @match *://stealthgram.com/*
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- // ----- Configurable parameters -----
- const BUTTON_LABEL = "ON";
- const BUTTON_POSITION = 'bottom: 32px; right: 32px;';
- const BUTTON_ACTIVE_STYLE = `
- background: #1976D2;
- color: #fff;
- border: 2px solid #1976D2;
- box-shadow: 0 2px 10px rgba(25,118,210,0.15);
- `;
- const BUTTON_INACTIVE_STYLE = `
- background: #fff;
- color: #1976D2;
- border: 2px solid #1976D2;
- box-shadow: 0 2px 10px rgba(25,118,210,0.08);
- `;
- const BUTTON_COMMON_STYLE = `
- position: fixed; z-index: 99999; ${BUTTON_POSITION}
- font-size: 1.1rem;
- font-family: inherit;
- font-weight: bold;
- padding: 14px 28px;
- border-radius: 32px;
- cursor: pointer;
- transition: all 0.2s;
- outline: none;
- opacity: .95;
- `;
- const SCRIPT_NAME = "Stealthgram Carousel Media Gallery Toggle";
- const MAX_CONSECUTIVE_NO_MEDIA = 3; // Stop after 3 cycles with no new media
- // ------- State --------
- let active = false;
- let overlay = null;
- let sessionId = Date.now(); // Unique session ID
- // ------- Main gallery extraction logic -------
- function isVisible(el) {
- return !!(el && (el.offsetWidth || el.offsetHeight || el.getClientRects().length));
- }
- function getCarousel() {
- return document.querySelector('.border-2');
- }
- function getClickableNextBtn(carousel) {
- if(!carousel) return null;
- let btns = Array.from(carousel.querySelectorAll('div.absolute.right-3,div[class*=right-3],div[style*=right]'));
- for(let btn of btns) {
- if (btn.querySelector('svg') && isVisible(btn) && typeof btn.click === 'function') return btn;
- }
- let backup = carousel.querySelector('div.absolute.right-3') || carousel.querySelector('div[class*=right-3]');
- if(backup && typeof backup.click === 'function') return backup;
- let svg = carousel.querySelector('svg[data-testid="ArrowForwardIosIcon"],svg[data-testid="KeyboardArrowDownIcon"]');
- if(svg) {
- let parentDiv = svg.closest('div');
- if(parentDiv && typeof parentDiv.click === 'function' && isVisible(parentDiv)) return parentDiv;
- }
- return null;
- }
- function collectMedia(carousel, seenSrc, collected) {
- const img = carousel.querySelector('img[src]:not([aria-hidden="true"])');
- const vid = carousel.querySelector('video, source');
- if (img && img.src && !seenSrc.has(img.src)) {
- seenSrc.add(img.src);
- // Get large URL if available
- let largeUrl = img.src;
- try {
- const linkParent = img.closest('a');
- if (linkParent && linkParent.href && linkParent.href !== img.src) {
- largeUrl = linkParent.href;
- }
- } catch(e) {
- console.error(`[${SCRIPT_NAME}] Error getting large URL:`, e);
- }
- collected.push({
- type: 'img',
- src: img.src,
- large: largeUrl
- });
- return true; // New media collected
- }
- if (vid && vid.src && !seenSrc.has(vid.src)) {
- seenSrc.add(vid.src);
- collected.push({
- type: 'video',
- src: vid.src,
- large: vid.src // Videos use same URL for both
- });
- return true; // New media collected
- }
- return false; // No new media collected
- }
- function extractMediaAndShowGallery() {
- console.log(`\n\n=== ${SCRIPT_NAME} v1.4 ===`);
- console.log(`Session ID: ${sessionId} | Started at: ${new Date().toLocaleTimeString()}`);
- console.log('Usage: Click floating button to extract media. Click thumbnails to view, click preview to toggle quality.');
- const carousel = getCarousel();
- if (!carousel) {
- console.error('[Gallery] Carousel not found!');
- alert('Carousel not found!');
- return;
- }
- const seenSrc = new Set();
- const collected = [];
- let cycleCount = 0;
- let consecutiveNoMediaCount = 0;
- const maxCycles = 100;
- const THROTTLE_DELAY = 1200;
- function hasNext() {
- const nextBtn = getClickableNextBtn(carousel);
- return nextBtn && isVisible(nextBtn) && !nextBtn.disabled;
- }
- function clickNext(cb) {
- const nextBtn = getClickableNextBtn(carousel);
- if(!nextBtn) {
- cb();
- return;
- }
- nextBtn.click();
- setTimeout(cb, THROTTLE_DELAY);
- }
- function cycleCarousel() {
- if (!active) return;
- // Collect media and track if new media was found
- const newMediaFound = collectMedia(carousel, seenSrc, collected);
- cycleCount++;
- if (newMediaFound) {
- console.log(`[Carousel] Collected ${collected.length} items so far`);
- consecutiveNoMediaCount = 0; // Reset no-media counter
- } else {
- consecutiveNoMediaCount++;
- console.log(`[Carousel] No new media (${consecutiveNoMediaCount}/${MAX_CONSECUTIVE_NO_MEDIA})`);
- }
- // Check early termination conditions
- if (consecutiveNoMediaCount >= MAX_CONSECUTIVE_NO_MEDIA) {
- console.log(`[Carousel] Early termination - ${MAX_CONSECUTIVE_NO_MEDIA} cycles with no new media`);
- console.log(`[Gallery] Collected ${collected.length} media items`);
- showGallery(collected);
- return;
- }
- if (cycleCount >= maxCycles) {
- console.log(`[Carousel] Cycle limit reached (${maxCycles})`);
- console.log(`[Gallery] Collected ${collected.length} media items`);
- showGallery(collected);
- return;
- }
- // Check if we should continue cycling
- const nextBtn = getClickableNextBtn(carousel);
- const nextBtnVisible = nextBtn && isVisible(nextBtn) && !nextBtn.disabled;
- if (nextBtnVisible) {
- console.log(`[Carousel] Cycling to next item (cycle ${cycleCount})`);
- clickNext(() => cycleCarousel());
- } else {
- console.log(`[Carousel] Reached end of carousel after ${cycleCount} cycles`);
- console.log(`[Gallery] Collected ${collected.length} media items`);
- showGallery(collected);
- }
- }
- console.log('[Extraction] Starting media collection...');
- cycleCarousel();
- }
- // ----- Modal Gallery -----
- function showGallery(collected) {
- if (!active) return;
- if (!collected.length) {
- console.error('[Gallery] No media items found');
- alert('No images or videos found');
- return;
- }
- console.log(`[Gallery] Showing ${collected.length} items`);
- removeOverlay();
- overlay = document.createElement('div');
- overlay.setAttribute('id', 'sgm-modal-gallery-overlay');
- overlay.style = `
- position:fixed;z-index:99998;
- top:0;left:0;width:100vw;height:100vh;
- background:rgba(34,37,54,0.93);
- display:flex;flex-direction:column;align-items:center;justify-content:center;
- animation:sgm-fadein .3s;
- `;
- overlay.innerHTML = `
- <style>
- @keyframes sgm-fadein { from { opacity:0 } to { opacity:1 } }
- .sgm-gallery-close { position:absolute;top:10px;right:30px;font-size:2.2em;background:#fff;border:none;cursor:pointer;z-index:2;border-radius:50%;width:48px;height:48px;line-height:48px;text-align:center;transition:box-shadow .2s; }
- .sgm-gallery-close:hover { box-shadow:0 0 10px #1976D277 }
- .sgm-gallery-grid { display:flex;gap:18px;max-width:80vw;max-height:60vh;overflow:auto;flex-wrap:wrap;justify-content:center; }
- .sgm-gallery-thumb { box-sizing:border-box;max-width:124px;max-height:124px;cursor:pointer;border:2.5px solid #fff;border-radius:7px;transition:box-shadow .2s; background:#000; }
- .sgm-gallery-thumb:hover { box-shadow:0 0 10px #1976D2bb }
- .sgm-gallery-viewer { margin-top:28px;text-align:center; }
- .sgm-gallery-viewer img, .sgm-gallery-viewer video { max-width:80vw;max-height:60vh;box-shadow:0 0 12px #222c; border-radius:12px; background:#000; cursor: pointer; }
- </style>
- <button class="sgm-gallery-close" title="Close Gallery" aria-label="Close Gallery">×</button>
- <div class="sgm-gallery-grid"></div>
- <div class="sgm-gallery-viewer"></div>
- `;
- document.body.appendChild(overlay);
- overlay.querySelector('.sgm-gallery-close').onclick = removeOverlay;
- const grid = overlay.querySelector('.sgm-gallery-grid');
- const viewer = overlay.querySelector('.sgm-gallery-viewer');
- collected.forEach((media, index) => {
- let thumb;
- if (media.type === 'img') {
- thumb = document.createElement('img');
- thumb.src = media.src;
- thumb.className = 'sgm-gallery-thumb';
- thumb.alt = "Image preview";
- } else {
- thumb = document.createElement('video');
- thumb.src = media.src;
- thumb.className = 'sgm-gallery-thumb';
- thumb.controls = true;
- }
- thumb.onclick = () => {
- viewer.innerHTML = '';
- if (media.type === 'img') {
- const big = document.createElement('img');
- big.src = media.src;
- big.alt = "Large Image";
- big.classList.add('sgm-preview-image');
- // Toggle quality on click
- big.addEventListener('click', (e) => {
- e.stopPropagation();
- if (big.src === media.src) {
- big.src = media.large;
- console.log(`[Preview] Loading HQ version: ${media.large}`);
- } else {
- big.src = media.src;
- console.log(`[Preview] Reverting to preview version`);
- }
- });
- viewer.appendChild(big);
- } else {
- const big = document.createElement('video');
- big.src = media.src;
- big.controls = true;
- big.autoplay = true;
- viewer.appendChild(big);
- }
- };
- grid.appendChild(thumb);
- });
- }
- function removeOverlay() {
- if (overlay && overlay.parentNode) {
- overlay.parentNode.removeChild(overlay);
- overlay = null;
- console.log('[Gallery] Closed media viewer');
- }
- }
- // ------ Floating Toggle Button ------
- function updateButtonState(btn, isActive) {
- btn.style.cssText = BUTTON_COMMON_STYLE + (isActive ? BUTTON_ACTIVE_STYLE : BUTTON_INACTIVE_STYLE);
- btn.textContent = BUTTON_LABEL;
- btn.title = isActive ? "Click to turn OFF gallery" : "Click to turn ON gallery";
- btn.setAttribute('aria-pressed', isActive ? 'true' : 'false');
- }
- function createToggleButton() {
- const btn = document.createElement('button');
- btn.type = 'button';
- btn.id = 'sgm-floating-media-toggle-btn';
- updateButtonState(btn, active);
- btn.onclick = () => {
- active = !active;
- updateButtonState(btn, active);
- if (active) {
- console.log(`\n[${SCRIPT_NAME}] ACTIVATED | v1.4`);
- sessionId = Date.now(); // Reset session ID
- removeOverlay();
- extractMediaAndShowGallery();
- } else {
- console.log(`[${SCRIPT_NAME}] DEACTIVATED`);
- removeOverlay();
- }
- };
- document.body.appendChild(btn);
- }
- // Cleanup
- window.addEventListener('beforeunload', () => {
- active = false;
- removeOverlay();
- const btn = document.getElementById('sgm-floating-media-toggle-btn');
- if (btn) btn.remove();
- });
- // Initialize
- console.log(`${SCRIPT_NAME} v1.4 loaded. Waiting for activation...`);
- createToggleButton();
- })();
Advertisement
Add Comment
Please, Sign In to add comment