const images = $('img').toArray(); const videos = $('video').toArray(); // Combine and tag each element type const media = images.map((el, i) => ({ el, type: 'image', index: i })) .concat(videos.map((el, i) => ({ el, type: 'video', index: i + images.length }))); // Staggered download (500ms between each) media.forEach((item, i) => { setTimeout(() => { const $el = $(item.el); const src = $el.attr('src'); if (!src) return; let ext = 'bin'; // default fallback let mimeType = ''; if (src.startsWith('data:')) { mimeType = src.split(';')[0].split(':')[1]; // e.g., image/png or video/webm ext = mimeType.split('/')[1] || 'bin'; } else { const match = src.match(/\.(\w+)(\?.*)?$/); if (match) ext = match[1]; } // Correct known cases if (item.type === 'image' && ext === 'bin') ext = 'png'; if (item.type === 'video') { // don't fake mp4 if it's webm if (ext === 'bin' || ext === 'webm') ext = 'webm'; } const a = $('') .attr('href', src) .attr('download', `${item.type}${item.index + 1}.${ext}`) .css('display', 'none'); $('body').append(a); a[0].click(); a.remove(); }, i * 500); });