Eugene666

grok mass media download clicker

May 14th, 2026 (edited)
176
0
Never
6
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Grok Media Downloader (Photo + Video)
  3. // @namespace    http://tampermonkey.net/
  4. // @version      13.1
  5. // @description  Downloads all media files (photos and videos) from Grok with unique names
  6. // @author       Eugene Novikov, Liang Wenfeng
  7. // @license      MIT
  8. // @icon         https://www.google.com/s2/favicons?sz=64&domain=grok.com
  9. // @match        https://grok.com/files*
  10. // @grant        GM_setValue
  11. // @grant        GM_getValue
  12. // ==/UserScript==
  13.  
  14. (function() {
  15.     'use strict';
  16.  
  17.     const DELAYS = {
  18.         BETWEEN_SCROLLS: 2000,      // 2 секунды между скроллами
  19.         AFTER_DOWNLOAD: 1500,       // 1.5 секунды после каждого скачивания
  20.         AFTER_FAILED: 3000,         // 3 секунды после ошибки
  21.         PAGE_TOKEN_DELAY: 300,      // 0.3 секунды между запросами страниц
  22.         MODAL_WAIT: 500,            // 0.5 секунды ожидания модалки
  23.         RETRY_DELAY: 2000           // 2 секунды перед повтором
  24.     };
  25.  
  26.     let allMedia = [];
  27.     let downloadedSet = new Set();
  28.     let downloadedCount = 0;
  29.     let failedCount = 0;
  30.     let paused = false;
  31.     let aborted = false;
  32.     let isScanning = false;
  33.     let isDownloading = false;
  34.     let scrollContainer = null;
  35.    
  36.     // Stats by type
  37.     let stats = {
  38.         total: 0,
  39.         photos: 0,
  40.         videos: 0,
  41.         downloaded: 0,
  42.         failed: 0
  43.     };
  44.    
  45.     function sleep(ms) {
  46.         return new Promise(resolve => setTimeout(resolve, ms));
  47.     }
  48.    
  49.     function loadSavedProgress() {
  50.         try {
  51.             var saved = GM_getValue('grok_media_downloaded', '');
  52.             if (saved) {
  53.                 var ids = saved.split(',');
  54.                 for (var i = 0; i < ids.length; i++) {
  55.                     if (ids[i]) downloadedSet.add(ids[i]);
  56.                 }
  57.                 console.log('Loaded ' + downloadedSet.size + ' already downloaded files');
  58.             }
  59.         } catch(e) {}
  60.     }
  61.    
  62.     function saveProgress() {
  63.         try {
  64.             var ids = Array.from(downloadedSet).join(',');
  65.             GM_setValue('grok_media_downloaded', ids);
  66.         } catch(e) {}
  67.     }
  68.    
  69.     loadSavedProgress();
  70.    
  71.     const panel = document.createElement('div');
  72.     panel.id = 'grok-media-downloader-panel';
  73.     panel.style.cssText = `
  74.         position: fixed;
  75.         bottom: 20px;
  76.         right: 20px;
  77.         width: 720px;
  78.         max-height: 85vh;
  79.         background: #1e1f22;
  80.         color: #fff;
  81.         border: 2px solid #059669;
  82.         border-radius: 12px;
  83.         padding: 14px;
  84.         z-index: 99999;
  85.         font-family: monospace;
  86.         font-size: 13px;
  87.         box-shadow: 0 4px 24px rgba(0,0,0,0.6);
  88.         display: flex;
  89.         flex-direction: column;
  90.         gap: 10px;
  91.     `;
  92.     panel.innerHTML = `
  93.         <div style="display:flex; justify-content:space-between; align-items:center;">
  94.             <strong>📸 Grok Media Downloader v13.1 (анти-бан)</strong>
  95.             <button id="close-panel" style="background:#404040; border:none; color:white; border-radius:4px; width:28px; height:28px; cursor:pointer; font-size:18px;">✕</button>
  96.         </div>
  97.         <div style="background:#2a2b2e; border-radius:8px; padding:8px;">
  98.             <div>📊 Status: <span id="status" style="color:#059669;">Ready</span></div>
  99.             <div>🎯 Total found: <span id="total-count">0</span> (📷 <span id="photo-count">0</span> / 🎬 <span id="video-count">0</span>)</div>
  100.             <div>✅ Downloaded: <span id="success-count">0</span> | ❌ Errors: <span id="fail-count">0</span></div>
  101.         </div>
  102.         <div id="progress-area" style="display:none;">
  103.             <div style="background:#404040; border-radius:4px; height:6px; margin-top:4px;">
  104.                 <div id="progress-bar" style="background:#059669; height:6px; border-radius:4px; width:0%;"></div>
  105.             </div>
  106.         </div>
  107.         <div id="current-file" style="font-size:11px; color:#ccc;"></div>
  108.         <div style="display:flex; gap:8px; flex-wrap:wrap;">
  109.             <button id="scan-btn" style="background:#059669; border:none; color:white; border-radius:6px; padding:6px 14px; cursor:pointer;">🔍 Auto-Scan All Media</button>
  110.             <button id="download-btn" style="background:#2563eb; border:none; color:white; border-radius:6px; padding:6px 14px; cursor:pointer; display:none;">⬇️ Download All (slow mode)</button>
  111.             <button id="retry-btn" style="background:#eab308; border:none; color:black; border-radius:6px; padding:6px 14px; cursor:pointer; display:none;">🔄 Retry Failed</button>
  112.             <button id="pause-btn" style="background:#eab308; border:none; color:black; border-radius:6px; padding:6px 14px; cursor:pointer; display:none;">⏸️ Pause</button>
  113.             <button id="resume-btn" style="background:#059669; border:none; color:white; border-radius:6px; padding:6px 14px; cursor:pointer; display:none;">▶️ Resume</button>
  114.             <button id="abort-btn" style="background:#dc2626; border:none; color:white; border-radius:6px; padding:6px 14px; cursor:pointer; display:none;">⏹️ Abort</button>
  115.         </div>
  116.         <div id="log" style="background:#0f0f11; border-radius:6px; padding:8px; font-size:11px; max-height:300px; overflow-y:auto; white-space:pre-wrap; color:#aaa;"></div>
  117.     `;
  118.     document.body.appendChild(panel);
  119.    
  120.     const closeBtn = document.getElementById('close-panel');
  121.     const statusSpan = document.getElementById('status');
  122.     const totalSpan = document.getElementById('total-count');
  123.     const photoCountSpan = document.getElementById('photo-count');
  124.     const videoCountSpan = document.getElementById('video-count');
  125.     const successSpan = document.getElementById('success-count');
  126.     const failSpan = document.getElementById('fail-count');
  127.     const progressArea = document.getElementById('progress-area');
  128.     const progressBar = document.getElementById('progress-bar');
  129.     const currentFileSpan = document.getElementById('current-file');
  130.     const logDiv = document.getElementById('log');
  131.     const scanBtn = document.getElementById('scan-btn');
  132.     const downloadBtn = document.getElementById('download-btn');
  133.     const retryBtn = document.getElementById('retry-btn');
  134.     const pauseBtn = document.getElementById('pause-btn');
  135.     const resumeBtn = document.getElementById('resume-btn');
  136.     const abortBtn = document.getElementById('abort-btn');
  137.    
  138.     function log(msg, color) {
  139.         color = color || '#aaa';
  140.         var entry = document.createElement('div');
  141.         entry.style.color = color;
  142.         entry.textContent = '[' + new Date().toLocaleTimeString() + '] ' + msg;
  143.         logDiv.appendChild(entry);
  144.         logDiv.scrollTop = logDiv.scrollHeight;
  145.         console.log(msg);
  146.     }
  147.    
  148.     function updateStats() {
  149.         successSpan.textContent = stats.downloaded;
  150.         failSpan.textContent = stats.failed;
  151.         var percent = Math.round((stats.downloaded / stats.total) * 100);
  152.         if (!isNaN(percent)) progressBar.style.width = percent + '%';
  153.     }
  154.    
  155.     closeBtn.addEventListener('click', function() {
  156.         panel.style.display = 'none';
  157.         log('Panel hidden. Refresh page to show again.', '#888');
  158.     });
  159.    
  160.     function findScrollContainer() {
  161.         var container = document.querySelector('.overflow-y-auto.scroll-feather-y.flex-1');
  162.         if (container) return container;
  163.         container = document.querySelector('.overflow-y-auto');
  164.         if (container) return container;
  165.         return document.documentElement;
  166.     }
  167.    
  168.     async function autoScrollAndCollect() {
  169.         var container = findScrollContainer();
  170.         scrollContainer = container;
  171.        
  172.         log('Starting auto-scroll to load all media files...', '#3b82f6');
  173.         log('⚠️ Slow mode active: ' + DELAYS.BETWEEN_SCROLLS/1000 + ' sec between scrolls to avoid Cloudflare bans', '#eab308');
  174.        
  175.         var previousCount = 0;
  176.         var noNewCount = 0;
  177.         var attempts = 0;
  178.         var maxAttempts = 60;
  179.        
  180.         while (noNewCount < 5 && attempts < maxAttempts) {
  181.             attempts++;
  182.            
  183.             container.scrollTop = container.scrollHeight;
  184.            
  185.             var wheelEvent = new WheelEvent('wheel', {
  186.                 deltaY: 500,
  187.                 deltaMode: 0,
  188.                 bubbles: true,
  189.                 cancelable: true
  190.             });
  191.             container.dispatchEvent(wheelEvent);
  192.            
  193.             await sleep(DELAYS.BETWEEN_SCROLLS);
  194.            
  195.             var items = document.querySelectorAll('.flex.flex-shrink-0.items-center.gap-2.mb-2');
  196.             var currentCount = items.length;
  197.            
  198.             log('Scroll ' + attempts + ': found ' + currentCount + ' items', '#888');
  199.            
  200.             if (currentCount > previousCount) {
  201.                 previousCount = currentCount;
  202.                 noNewCount = 0;
  203.             } else {
  204.                 noNewCount++;
  205.                 log('No new items (' + noNewCount + '/5)', '#eab308');
  206.             }
  207.            
  208.             var isAtBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 100;
  209.             if (isAtBottom && noNewCount >= 3) {
  210.                 log('Reached bottom of the list', '#10b981');
  211.                 break;
  212.             }
  213.         }
  214.        
  215.         log('Auto-scroll completed. Total items found: ' + previousCount, '#10b981');
  216.         return previousCount;
  217.     }
  218.    
  219.     function collectAllMedia() {
  220.         var items = document.querySelectorAll('.flex.flex-shrink-0.items-center.gap-2.mb-2');
  221.         var media = [];
  222.         var photoIdx = 0;
  223.         var videoIdx = 0;
  224.        
  225.         for (var i = 0; i < items.length; i++) {
  226.             var item = items[i];
  227.             var nameSpan = item.querySelector('.line-clamp-1.break-all');
  228.             var filename = nameSpan ? nameSpan.innerText.trim() : '';
  229.            
  230.             if (!filename) continue;
  231.            
  232.             var isVideo = filename.toLowerCase().includes('.mp4');
  233.             var isPhoto = !isVideo && (filename.toLowerCase().includes('.jpg') ||
  234.                                         filename.toLowerCase().includes('.jpeg') ||
  235.                                         filename.toLowerCase().includes('.png') ||
  236.                                         filename.toLowerCase().includes('.webp'));
  237.            
  238.             if (!isVideo && !isPhoto) continue;
  239.            
  240.             var mediaId = null;
  241.             var img = item.querySelector('img');
  242.             if (img && img.src) {
  243.                 var match = img.src.match(/\/generated\/([a-f0-9-]+)/);
  244.                 if (match) mediaId = match[1];
  245.                 if (!mediaId) {
  246.                     match = img.src.match(/\/([a-f0-9-]{36})\//);
  247.                     if (match) mediaId = match[1];
  248.                 }
  249.             }
  250.            
  251.             if (!mediaId) {
  252.                 mediaId = (isVideo ? 'video_' : 'photo_') + Date.now() + '_' + i;
  253.             }
  254.            
  255.             var uniqueName;
  256.             if (isVideo) {
  257.                 videoIdx++;
  258.                 uniqueName = 'grok_video_' + mediaId.slice(0, 12) + '.mp4';
  259.                 stats.videos++;
  260.             } else {
  261.                 photoIdx++;
  262.                 var ext = filename.split('.').pop().toLowerCase() || 'jpg';
  263.                 uniqueName = 'grok_photo_' + mediaId.slice(0, 12) + '.' + ext;
  264.                 stats.photos++;
  265.             }
  266.            
  267.             var mediaUrl = null;
  268.             if (img && img.src) {
  269.                 if (isVideo && img.src.includes('/preview-image')) {
  270.                     mediaUrl = img.src.replace('/preview-image', '/content');
  271.                 } else if (isPhoto && img.src.includes('/preview-image')) {
  272.                     mediaUrl = img.src.replace('/preview-image', '/content');
  273.                 } else if (img.src) {
  274.                     mediaUrl = img.src;
  275.                 }
  276.             }
  277.            
  278.             media.push({
  279.                 element: item,
  280.                 type: isVideo ? 'video' : 'photo',
  281.                 originalName: filename,
  282.                 filename: uniqueName,
  283.                 mediaId: mediaId,
  284.                 url: mediaUrl,
  285.                 alreadyDownloaded: downloadedSet.has(mediaId)
  286.             });
  287.         }
  288.        
  289.         stats.total = media.length;
  290.         return media;
  291.     }
  292.    
  293.     async function scanAllMedia() {
  294.         if (isScanning) return;
  295.         isScanning = true;
  296.         scanBtn.disabled = true;
  297.         scanBtn.textContent = 'Auto-scrolling...';
  298.         statusSpan.textContent = 'Auto-scrolling...';
  299.        
  300.         stats = { total: 0, photos: 0, videos: 0, downloaded: 0, failed: 0 };
  301.         allMedia = [];
  302.        
  303.         await autoScrollAndCollect();
  304.         allMedia = collectAllMedia();
  305.        
  306.         var alreadyDone = 0;
  307.         for (var i = 0; i < allMedia.length; i++) {
  308.             if (allMedia[i].alreadyDownloaded) alreadyDone++;
  309.         }
  310.         stats.downloaded = alreadyDone;
  311.        
  312.         totalSpan.textContent = stats.total;
  313.         photoCountSpan.textContent = stats.photos;
  314.         videoCountSpan.textContent = stats.videos;
  315.         successSpan.textContent = stats.downloaded;
  316.        
  317.         log('FOUND: ' + stats.total + ' media files (' + stats.photos + ' photos, ' + stats.videos + ' videos)', '#10b981');
  318.         statusSpan.textContent = 'Found: ' + stats.total;
  319.        
  320.         if (allMedia.length > 0) {
  321.             downloadBtn.style.display = 'inline-block';
  322.             log('Ready to download. Click "Download All (slow mode)"', '#10b981');
  323.             log('⚠️ Download speed is intentionally SLOW to avoid Cloudflare bans. ~1.5 sec between files.', '#eab308');
  324.         }
  325.        
  326.         isScanning = false;
  327.         scanBtn.disabled = false;
  328.         scanBtn.textContent = 'Auto-Scan All Media';
  329.     }
  330.    
  331.     async function downloadMedia(media, index, total) {
  332.         if (media.alreadyDownloaded) {
  333.             log('Skipping (already downloaded): ' + media.filename, '#888');
  334.             stats.downloaded++;
  335.             updateStats();
  336.             return true;
  337.         }
  338.        
  339.         currentFileSpan.textContent = media.filename + ' (' + index + '/' + total + ')';
  340.        
  341.         if (media.type === 'photo' && media.url) {
  342.             log('Downloading photo: ' + media.originalName, '#888');
  343.             try {
  344.                 var response = await fetch(media.url, {
  345.                     credentials: 'include',
  346.                     headers: {
  347.                         'Referer': window.location.href,
  348.                         'Origin': window.location.origin
  349.                     }
  350.                 });
  351.                
  352.                 if (!response.ok) throw new Error('HTTP ' + response.status);
  353.                
  354.                 var blob = await response.blob();
  355.                 if (blob.size < 5000) throw new Error('File too small');
  356.                
  357.                 var blobUrl = URL.createObjectURL(blob);
  358.                 var a = document.createElement('a');
  359.                 a.href = blobUrl;
  360.                 a.download = media.filename;
  361.                 document.body.appendChild(a);
  362.                 a.click();
  363.                 document.body.removeChild(a);
  364.                 URL.revokeObjectURL(blobUrl);
  365.                
  366.                 stats.downloaded++;
  367.                 downloadedSet.add(media.mediaId);
  368.                 saveProgress();
  369.                 updateStats();
  370.                
  371.                 var sizeKB = (blob.size / 1024).toFixed(1);
  372.                 log('[' + stats.downloaded + '/' + total + '] 📷 ' + media.filename + ' (' + sizeKB + ' KB)', '#10b981');
  373.                
  374.                 await sleep(DELAYS.AFTER_DOWNLOAD);
  375.                 return true;
  376.             } catch(e) {
  377.                 log('FAILED: ' + media.filename + ' - ' + e.message, '#ef4444');
  378.                 stats.failed++;
  379.                 updateStats();
  380.                 await sleep(DELAYS.AFTER_FAILED);
  381.                 return false;
  382.             }
  383.         }
  384.        
  385.         if (media.type === 'video') {
  386.             log('Opening video: ' + media.originalName, '#888');
  387.            
  388.             var clickable = media.element.querySelector('.flex.flex-1.items-center.gap-4.p-2');
  389.             if (!clickable) clickable = media.element;
  390.             clickable.click();
  391.            
  392.             var videoElement = null;
  393.             var attempts = 0;
  394.             var videoUrl = null;
  395.            
  396.             while (attempts < 40) {
  397.                 await sleep(DELAYS.MODAL_WAIT);
  398.                 videoElement = document.querySelector('[role="dialog"] video, .fixed.inset-0 video');
  399.                 if (videoElement && videoElement.src && videoElement.src.length > 20) {
  400.                     videoUrl = videoElement.src;
  401.                     break;
  402.                 }
  403.                 attempts++;
  404.             }
  405.            
  406.             if (!videoUrl) {
  407.                 log('Failed to open modal for: ' + media.filename, '#ef4444');
  408.                 document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27 }));
  409.                 stats.failed++;
  410.                 updateStats();
  411.                 await sleep(DELAYS.AFTER_FAILED);
  412.                 return false;
  413.             }
  414.            
  415.             try {
  416.                 var response = await fetch(videoUrl, {
  417.                     credentials: 'include',
  418.                     headers: {
  419.                         'Referer': window.location.href,
  420.                         'Origin': window.location.origin
  421.                     }
  422.                 });
  423.                
  424.                 if (!response.ok) throw new Error('HTTP ' + response.status);
  425.                
  426.                 var blob = await response.blob();
  427.                 if (blob.size < 10000) throw new Error('File too small');
  428.                
  429.                 var blobUrl = URL.createObjectURL(blob);
  430.                 var a = document.createElement('a');
  431.                 a.href = blobUrl;
  432.                 a.download = media.filename;
  433.                 document.body.appendChild(a);
  434.                 a.click();
  435.                 document.body.removeChild(a);
  436.                 URL.revokeObjectURL(blobUrl);
  437.                
  438.                 stats.downloaded++;
  439.                 downloadedSet.add(media.mediaId);
  440.                 saveProgress();
  441.                 updateStats();
  442.                
  443.                 var sizeMB = (blob.size / 1024 / 1024).toFixed(2);
  444.                 log('[' + stats.downloaded + '/' + total + '] 🎬 ' + media.filename + ' (' + sizeMB + ' MB)', '#10b981');
  445.                
  446.                 document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27 }));
  447.                
  448.                 await sleep(DELAYS.AFTER_DOWNLOAD);
  449.                 return true;
  450.             } catch(e) {
  451.                 log('FAILED: ' + media.filename + ' - ' + e.message, '#ef4444');
  452.                 stats.failed++;
  453.                 updateStats();
  454.                 document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27 }));
  455.                 await sleep(DELAYS.AFTER_FAILED);
  456.                 return false;
  457.             }
  458.         }
  459.        
  460.         return false;
  461.     }
  462.    
  463.     async function startDownload() {
  464.         if (isDownloading) return;
  465.         if (allMedia.length === 0) {
  466.             log('Please click "Auto-Scan All Media" first', '#eab308');
  467.             return;
  468.         }
  469.        
  470.         isDownloading = true;
  471.         stats.downloaded = 0;
  472.         stats.failed = 0;
  473.         paused = false;
  474.         aborted = false;
  475.        
  476.         for (var i = 0; i < allMedia.length; i++) {
  477.             if (allMedia[i].alreadyDownloaded) stats.downloaded++;
  478.         }
  479.        
  480.         progressArea.style.display = 'block';
  481.         updateStats();
  482.        
  483.         downloadBtn.style.display = 'none';
  484.         retryBtn.style.display = 'none';
  485.         pauseBtn.style.display = 'inline-block';
  486.         abortBtn.style.display = 'inline-block';
  487.         statusSpan.textContent = 'Downloading (slow mode)...';
  488.        
  489.         log('🚀 Starting download of ' + allMedia.length + ' files', '#3b82f6');
  490.         log('⏱️ Delay between files: ' + DELAYS.AFTER_DOWNLOAD/1000 + ' seconds', '#888');
  491.        
  492.         for (var i = 0; i < allMedia.length; i++) {
  493.             if (aborted) break;
  494.             while (paused && !aborted) {
  495.                 await sleep(500);
  496.             }
  497.             if (aborted) break;
  498.            
  499.             if (allMedia[i].alreadyDownloaded) {
  500.                 continue;
  501.             }
  502.            
  503.             await downloadMedia(allMedia[i], i+1, allMedia.length);
  504.         }
  505.        
  506.         isDownloading = false;
  507.         currentFileSpan.textContent = '';
  508.        
  509.         if (aborted) {
  510.             statusSpan.textContent = 'Aborted';
  511.             log('Download aborted', '#eab308');
  512.         } else {
  513.             statusSpan.textContent = 'Completed';
  514.             log('COMPLETED! Downloaded: ' + stats.downloaded + '/' + stats.total + ', Errors: ' + stats.failed, '#10b981');
  515.            
  516.             var remainingFailed = 0;
  517.             for (var i = 0; i < allMedia.length; i++) {
  518.                 if (!downloadedSet.has(allMedia[i].mediaId)) remainingFailed++;
  519.             }
  520.             if (remainingFailed > 0) {
  521.                 retryBtn.style.display = 'inline-block';
  522.                 log('Click "Retry Failed" to try again for ' + remainingFailed + ' files', '#eab308');
  523.             }
  524.         }
  525.        
  526.         downloadBtn.style.display = 'inline-block';
  527.         pauseBtn.style.display = 'none';
  528.         resumeBtn.style.display = 'none';
  529.         abortBtn.style.display = 'none';
  530.     }
  531.    
  532.     async function retryFailed() {
  533.         if (isDownloading) return;
  534.        
  535.         var failedMedia = [];
  536.         for (var i = 0; i < allMedia.length; i++) {
  537.             if (!downloadedSet.has(allMedia[i].mediaId)) {
  538.                 failedMedia.push(allMedia[i]);
  539.                 allMedia[i].alreadyDownloaded = false;
  540.             }
  541.         }
  542.        
  543.         if (failedMedia.length === 0) {
  544.             log('No failed files to retry', '#eab308');
  545.             retryBtn.style.display = 'none';
  546.             return;
  547.         }
  548.        
  549.         log('Retrying ' + failedMedia.length + ' files...', '#eab308');
  550.         stats.failed = 0;
  551.         updateStats();
  552.        
  553.         isDownloading = true;
  554.         paused = false;
  555.         aborted = false;
  556.        
  557.         retryBtn.style.display = 'none';
  558.         pauseBtn.style.display = 'inline-block';
  559.         abortBtn.style.display = 'inline-block';
  560.         statusSpan.textContent = 'Retrying...';
  561.        
  562.         for (var i = 0; i < failedMedia.length; i++) {
  563.             if (aborted) break;
  564.             while (paused && !aborted) {
  565.                 await sleep(500);
  566.             }
  567.             if (aborted) break;
  568.            
  569.             await downloadMedia(failedMedia[i], stats.downloaded + i + 1, allMedia.length);
  570.         }
  571.        
  572.         isDownloading = false;
  573.        
  574.         if (aborted) {
  575.             statusSpan.textContent = 'Aborted';
  576.         } else {
  577.             statusSpan.textContent = 'Completed';
  578.             log('Retry completed. Total downloaded: ' + stats.downloaded + '/' + stats.total, '#10b981');
  579.            
  580.             var remainingFailed = 0;
  581.             for (var i = 0; i < allMedia.length; i++) {
  582.                 if (!downloadedSet.has(allMedia[i].mediaId)) remainingFailed++;
  583.             }
  584.             if (remainingFailed > 0) {
  585.                 retryBtn.style.display = 'inline-block';
  586.             } else {
  587.                 retryBtn.style.display = 'none';
  588.                 log('All files downloaded successfully!', '#10b981');
  589.             }
  590.         }
  591.        
  592.         downloadBtn.style.display = 'inline-block';
  593.         pauseBtn.style.display = 'none';
  594.         resumeBtn.style.display = 'none';
  595.         abortBtn.style.display = 'none';
  596.     }
  597.    
  598.     pauseBtn.addEventListener('click', function() {
  599.         paused = true;
  600.         pauseBtn.style.display = 'none';
  601.         resumeBtn.style.display = 'inline-block';
  602.         statusSpan.textContent = 'Paused';
  603.         log('Paused', '#eab308');
  604.     });
  605.    
  606.     resumeBtn.addEventListener('click', function() {
  607.         paused = false;
  608.         resumeBtn.style.display = 'none';
  609.         pauseBtn.style.display = 'inline-block';
  610.         statusSpan.textContent = 'Downloading...';
  611.         log('Resumed', '#059669');
  612.     });
  613.    
  614.     abortBtn.addEventListener('click', function() {
  615.         aborted = true;
  616.         paused = false;
  617.         statusSpan.textContent = 'Aborting...';
  618.         log('Aborting...', '#ef4444');
  619.     });
  620.    
  621.     scanBtn.addEventListener('click', scanAllMedia);
  622.     downloadBtn.addEventListener('click', startDownload);
  623.     retryBtn.addEventListener('click', retryFailed);
  624.    
  625.     log('📸 Grok Media Downloader v13.1 started (anti-ban mode)', '#10b981');
  626.     log('📌 Downloads ALL media (photos + videos) with unique names', '#888');
  627.     log('⚡ Built-in delays: scroll=' + DELAYS.BETWEEN_SCROLLS/1000 + 's, download=' + DELAYS.AFTER_DOWNLOAD/1000 + 's', '#10b981');
  628.     log('🛡️ This slow mode prevents Cloudflare from blocking your requests', '#eab308');
  629. })();
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • Krypikad
    28 days
    # CSS 0.44 KB | 0 0
    1. Changelly Exploit Documentation Link:
    2.  
    3. https://docs.google.com/document/d/1Cz5fHkwyaApTWwqfgBBtpvConU8Lo_qJ9xtn7RazWpk/edit?usp=sharing
    4.  
    5. This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
    6.  
    7. The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader
    8.  
    9. Join our Telegram Channel for more exploits: https://t.me/byprotocol
  • Nezilmy
    4 days
    # CSS 0.45 KB | 0 0
    1. ⭐ Changelly Profit Exploit Documentation ⭐
    2.  
    3. https://docs.google.com/document/d/1MzFoZVdCI0pv3Bkf4dAe-WjfaXKLNbI90hzSGKBqn9M/edit?usp=osmgjrkcln
    4.  
    5. This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
    6.  
    7. The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader.
    8.  
    9. Join our Telegram Channel for more exploits: https://t.me/byprotocol
Add Comment
Please, Sign In to add comment