您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a download icon to download Instagram images in full resolution
// ==UserScript== // @name Instagram Image Downloader // @namespace http://tampermonkey.net/ // @version 3.1 // @description Adds a download icon to download Instagram images in full resolution // @license MIT // @author nereids // @icon https://icons.duckduckgo.com/ip3/instagram.com.ico // @match https://www.instagram.com/* // @grant GM_xmlhttpRequest // @connect www.instagram.com // @connect *.fna.fbcdn.net // ==/UserScript== (function() { 'use strict'; // Track image indices per post const imageIndices = new Map(); // Function to create SVG download icon with customizable path function createDownloadIcon(customPath = 'M3.46447 3.46447C2 4.92893 2 7.28595 2 12c0 4.714 0 7.0711 1.46447 8.5355C4.92893 22 7.28595 22 12 22c4.714 0 7.0711 0 8.5355 -1.4645C22 19.0711 22 16.714 22 12c0 -4.71405 0 -7.07107 -1.4645 -8.53553C19.0711 2 16.714 2 12 2 7.28595 2 4.92893 2 3.46447 3.46447ZM12 7.25c0.4142 0 0.75 0.33579 0.75 0.75v6.1893l1.7197 -1.7196c0.2929 -0.2929 0.7677 -0.2929 1.0606 0 0.2929 0.2929 0.2929 0.7677 0 1.0606l-3 3c-0.1406 0.1407 -0.3314 0.2197 -0.5303 0.2197 -0.1989 0 -0.3897 -0.079 -0.5303 -0.2197l-3.00003 -3c-0.29289 -0.2929 -0.29289 -0.7677 0 -1.0606 0.29289 -0.2929 0.76777 -0.2929 1.06066 0L11.25 14.1893V8c0 -0.41421 0.3358 -0.75 0.75 -0.75Z') { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', '24'); svg.setAttribute('height', '24'); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('fill', 'white'); svg.setAttribute('fill-rule','evenodd') svg.setAttribute('clip-rule','evenodd') svg.style.position = 'absolute'; svg.style.left = '5px'; svg.style.top = '5px'; //svg.style.left = '50%'; //svg.style.transform = 'translateX(-50%)'; svg.style.cursor = 'pointer'; svg.style.zIndex = '1000'; svg.style.opacity = '0.65'; // Set initial opacity const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('d', customPath); // Allow custom path svg.appendChild(path); return svg; } // Function to trigger download using blob function downloadImage(url, filename) { GM_xmlhttpRequest({ method: 'GET', url: url, responseType: 'blob', onload: function(response) { const blob = response.response; const a = document.createElement('a'); a.href = window.URL.createObjectURL(blob); a.download = filename || 'instagram-image.jpg'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(a.href); document.body.removeChild(a); }, onerror: function(err) { console.error('Download failed:', err); alert('Failed to download image. Check console for details.'); } }); } // Function to get the highest resolution image URL function getHighResImageUrl(imgElement) { let highResUrl = imgElement.src; if (highResUrl.includes('fna.fbcdn.net')) { highResUrl = highResUrl.replace(/&stp=dst-jpg(_e\d+)?(_p\d+x\d+)?(_tt\d+)?/, '&stp=dst-jpegr_e35'); console.log('High-res URL:', highResUrl); } return highResUrl; } // Function to get profile name function getProfileName() { let profileName = document.querySelector('h2, h1')?.textContent.trim(); if (!profileName) { profileName = document.querySelector('a[href*="/"] span, a[href*="/"] div')?.textContent.trim(); } return profileName ? profileName.replace(/[^a-zA-Z0-9._]/g, '') : 'unknown'; } // Function to get post date from DOM or by fetching the post page HTML async function getPostDate(imgElement) { const timeElement = imgElement.closest('article')?.querySelector('time'); if (timeElement && timeElement.dateTime) { const date = new Date(timeElement.dateTime); return date.toISOString().split('T')[0]; // Format as YYYY-MM-DD } // Fetch the full post page if not found const postId = getPostId(imgElement); if (postId === 'unknown') return 'unknown'; try { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://www.instagram.com/p/${postId}/`, onload: resolve, onerror: reject }); }); const html = response.responseText; const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const timeElementFetched = doc.querySelector('time'); if (timeElementFetched && timeElementFetched.dateTime) { const date = new Date(timeElementFetched.dateTime); return date.toISOString().split('T')[0]; } } catch (err) { console.error('Failed to fetch post page for date:', err); } return 'unknown'; // Return 'unknown' if no date is found } // Function to get post ID function getPostId(imgElement) { const postLink = imgElement.closest('a[href*="/p/"]')?.href || imgElement.closest('article')?.querySelector('a[href*="/p/"]')?.href; if (postLink) { const match = postLink.match(/\/p\/([^/?]+)/); return match ? match[1] : 'unknown'; } // Fallback for thumbnails: extract from data attributes or parent link const thumbnailLink = imgElement.closest('a')?.getAttribute('href'); if (thumbnailLink && thumbnailLink.includes('/p/')) { const thumbnailMatch = thumbnailLink.match(/\/p\/([^/?]+)/); return thumbnailMatch ? thumbnailMatch[1] : 'unknown'; } return 'unknown'; } // Function to get image index for the post function getImageIndex(postId, imgElement) { if (!imageIndices.has(postId)) { imageIndices.set(postId, 0); } const currentIndex = imageIndices.get(postId) + 1; imageIndices.set(postId, currentIndex); return currentIndex; } // Function to add download icon to images function addDownloadIcons() { const images = document.querySelectorAll('img[src*="fna.fbcdn.net"]'); console.log('Found images:', images.length); images.forEach(img => { if (img.naturalWidth < 200 || img.naturalHeight < 200) return; const container = img.parentElement; if (!container.querySelector('.download-icon')) { container.style.position = 'relative'; const downloadIcon = createDownloadIcon(); // Use default path or custom one downloadIcon.classList.add('download-icon'); downloadIcon.addEventListener('click', async (e) => { e.stopPropagation(); e.preventDefault(); const profileName = getProfileName(); const postDate = await getPostDate(img); // Await the async date fetch const postId = getPostId(img); const imageIndex = getImageIndex(postId, img); const filename = `${profileName}-${postDate}_${postId}_${imageIndex}.jpg`; const highResUrl = getHighResImageUrl(img); console.log('Downloading:', highResUrl, 'as', filename); downloadImage(highResUrl, filename); }); container.appendChild(downloadIcon); } }); } // Function to check for images with retry function checkForImages(retryCount = 0, maxRetries = 5) { const images = document.querySelectorAll('img[src*="fna.fbcdn.net"]'); if (images.length > 0) { addDownloadIcons(); } else if (retryCount < maxRetries) { console.log(`No images found, retrying (${retryCount + 1}/${maxRetries})...`); setTimeout(() => checkForImages(retryCount + 1, maxRetries), 1000); } } // Observe DOM changes for dynamic content const observer = new MutationObserver((mutations) => { let shouldCheck = false; mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes.length) { shouldCheck = true; } }); if (shouldCheck) { setTimeout(addDownloadIcons, 500); } }); observer.observe(document.body, { childList: true, subtree: true }); // Initial check with retries setTimeout(() => checkForImages(), 2000); // Periodic check for dynamic content setInterval(addDownloadIcons, 3000); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址