Freepik Bulk Image Downloader

Scarica tutte le immagini gratuite dalla pagina, organizzate per tag in un file ZIP

Size

11.2 KB

Version

1.0.1

Created

Nov 25, 2025

Updated

5 months ago

1// ==UserScript==
2// @name		Freepik Bulk Image Downloader
3// @description		Scarica tutte le immagini gratuite dalla pagina, organizzate per tag in un file ZIP
4// @version		1.0.1
5// @match		https://*.freepik.com/*
6// @icon		https://cdn-front.freepik.com/favicons/favicon.ico?v=2
7// @require		https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
8// @grant		GM.xmlhttpRequest
9// ==/UserScript==
10(function() {
11    'use strict';
12
13    console.log('Freepik Bulk Image Downloader inizializzato');
14
15    // Stili per il pulsante di download
16    const styles = `
17        #freepik-bulk-download-btn {
18            position: fixed;
19            bottom: 30px;
20            right: 30px;
21            z-index: 10000;
22            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
23            color: white;
24            border: none;
25            padding: 16px 24px;
26            border-radius: 50px;
27            font-size: 16px;
28            font-weight: bold;
29            cursor: pointer;
30            box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
31            transition: all 0.3s ease;
32            display: flex;
33            align-items: center;
34            gap: 10px;
35        }
36        
37        #freepik-bulk-download-btn:hover {
38            transform: translateY(-2px);
39            box-shadow: 0 12px 28px rgba(102, 126, 234, 0.6);
40        }
41        
42        #freepik-bulk-download-btn:disabled {
43            background: #ccc;
44            cursor: not-allowed;
45            transform: none;
46        }
47        
48        #freepik-download-progress {
49            position: fixed;
50            bottom: 100px;
51            right: 30px;
52            z-index: 10000;
53            background: white;
54            padding: 20px;
55            border-radius: 12px;
56            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
57            min-width: 300px;
58            display: none;
59        }
60        
61        #freepik-download-progress.active {
62            display: block;
63        }
64        
65        .progress-bar {
66            width: 100%;
67            height: 8px;
68            background: #e0e0e0;
69            border-radius: 4px;
70            overflow: hidden;
71            margin-top: 10px;
72        }
73        
74        .progress-bar-fill {
75            height: 100%;
76            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
77            transition: width 0.3s ease;
78            width: 0%;
79        }
80        
81        .progress-text {
82            font-size: 14px;
83            color: #333;
84            margin-bottom: 5px;
85        }
86        
87        .progress-detail {
88            font-size: 12px;
89            color: #666;
90            margin-top: 8px;
91        }
92    `;
93
94    // Aggiungi gli stili
95    const styleSheet = document.createElement('style');
96    styleSheet.textContent = styles;
97    document.head.appendChild(styleSheet);
98
99    // Funzione per estrarre i tag dall'URL o dal titolo
100    function extractTags(url, title) {
101        const tags = [];
102        
103        // Estrai dalla query string
104        const urlObj = new URL(url);
105        const query = urlObj.searchParams.get('query');
106        if (query) {
107            tags.push(query.replace(/\+/g, '_'));
108        }
109        
110        // Estrai dal titolo (es: "Free Vector gradient product card template")
111        if (title) {
112            const match = title.match(/Free\s+(Vector|PSD|Photo|AI)\s+(.+)/i);
113            if (match) {
114                const type = match[1].toLowerCase();
115                tags.push(type);
116                
117                // Prendi le prime 2-3 parole del titolo come tag
118                const words = match[2].split(' ').slice(0, 3).join('_').toLowerCase();
119                tags.push(words);
120            }
121        }
122        
123        return tags.length > 0 ? tags : ['uncategorized'];
124    }
125
126    // Funzione per scaricare un'immagine
127    async function downloadImage(url) {
128        return new Promise((resolve, reject) => {
129            GM.xmlhttpRequest({
130                method: 'GET',
131                url: url,
132                responseType: 'blob',
133                onload: function(response) {
134                    if (response.status === 200) {
135                        resolve(response.response);
136                    } else {
137                        reject(new Error(`Failed to download: ${response.status}`));
138                    }
139                },
140                onerror: function(error) {
141                    reject(error);
142                },
143                ontimeout: function() {
144                    reject(new Error('Timeout'));
145                }
146            });
147        });
148    }
149
150    // Funzione per aggiornare la barra di progresso
151    function updateProgress(current, total, message) {
152        const progressDiv = document.getElementById('freepik-download-progress');
153        const progressText = progressDiv.querySelector('.progress-text');
154        const progressDetail = progressDiv.querySelector('.progress-detail');
155        const progressFill = progressDiv.querySelector('.progress-bar-fill');
156        
157        const percentage = Math.round((current / total) * 100);
158        progressText.textContent = `Download in corso: ${percentage}%`;
159        progressDetail.textContent = message || `${current} di ${total} immagini`;
160        progressFill.style.width = `${percentage}%`;
161    }
162
163    // Funzione principale per scaricare tutte le immagini
164    async function downloadAllFreeImages() {
165        const button = document.getElementById('freepik-bulk-download-btn');
166        const progressDiv = document.getElementById('freepik-download-progress');
167        
168        button.disabled = true;
169        button.textContent = '⏳ Raccolta immagini...';
170        progressDiv.classList.add('active');
171        
172        try {
173            // Trova tutte le figure con immagini
174            const figures = document.querySelectorAll('figure[data-cy="resource-thumbnail"]');
175            console.log(`Trovate ${figures.length} immagini totali`);
176            
177            const freeImages = [];
178            
179            // Filtra solo le immagini gratuite
180            figures.forEach(fig => {
181                const premiumBadge = fig.querySelector('[class*="Premium"]');
182                if (!premiumBadge) {
183                    const link = fig.querySelector('a');
184                    const img = fig.querySelector('img');
185                    const title = img?.alt || '';
186                    
187                    if (img && img.src && link) {
188                        freeImages.push({
189                            url: img.src,
190                            title: title,
191                            pageUrl: link.href,
192                            tags: extractTags(link.href, title)
193                        });
194                    }
195                }
196            });
197            
198            console.log(`Trovate ${freeImages.length} immagini gratuite`);
199            
200            if (freeImages.length === 0) {
201                alert('Nessuna immagine gratuita trovata su questa pagina!');
202                return;
203            }
204            
205            // Crea il file ZIP
206            const zip = new JSZip();
207            
208            // Scarica tutte le immagini
209            for (let i = 0; i < freeImages.length; i++) {
210                const imageData = freeImages[i];
211                updateProgress(i + 1, freeImages.length, `Scaricamento: ${imageData.title.substring(0, 40)}...`);
212                
213                try {
214                    console.log(`Scaricamento immagine ${i + 1}/${freeImages.length}: ${imageData.url}`);
215                    const blob = await downloadImage(imageData.url);
216                    
217                    // Estrai l'estensione del file
218                    const extension = imageData.url.split('.').pop().split('?')[0] || 'jpg';
219                    
220                    // Crea il nome del file
221                    const fileName = `image_${i + 1}.${extension}`;
222                    
223                    // Organizza per tag (usa il primo tag come cartella principale)
224                    const folderName = imageData.tags[0].replace(/[^a-z0-9_-]/gi, '_');
225                    
226                    // Aggiungi al ZIP nella cartella appropriata
227                    zip.folder(folderName).file(fileName, blob);
228                    
229                    console.log(`Aggiunta immagine a cartella: ${folderName}/${fileName}`);
230                } catch (error) {
231                    console.error(`Errore nel download dell'immagine ${i + 1}:`, error);
232                }
233            }
234            
235            // Genera il file ZIP
236            updateProgress(freeImages.length, freeImages.length, 'Creazione file ZIP...');
237            console.log('Generazione file ZIP...');
238            
239            const zipBlob = await zip.generateAsync({
240                type: 'blob',
241                compression: 'DEFLATE',
242                compressionOptions: { level: 6 }
243            });
244            
245            // Scarica il file ZIP
246            const downloadLink = document.createElement('a');
247            downloadLink.href = URL.createObjectURL(zipBlob);
248            downloadLink.download = `freepik_images_${Date.now()}.zip`;
249            document.body.appendChild(downloadLink);
250            downloadLink.click();
251            document.body.removeChild(downloadLink);
252            
253            console.log('Download completato!');
254            updateProgress(freeImages.length, freeImages.length, `✅ Completato! ${freeImages.length} immagini scaricate`);
255            
256            setTimeout(() => {
257                progressDiv.classList.remove('active');
258                button.disabled = false;
259                button.innerHTML = '📥 Scarica Immagini Free';
260            }, 3000);
261            
262        } catch (error) {
263            console.error('Errore durante il download:', error);
264            alert('Errore durante il download delle immagini. Controlla la console per i dettagli.');
265            progressDiv.classList.remove('active');
266            button.disabled = false;
267            button.innerHTML = '📥 Scarica Immagini Free';
268        }
269    }
270
271    // Crea il pulsante di download
272    function createDownloadButton() {
273        // Verifica se siamo su una pagina di ricerca
274        if (!window.location.pathname.includes('/search') && !window.location.pathname.includes('/free-')) {
275            console.log('Non siamo su una pagina di ricerca, pulsante non aggiunto');
276            return;
277        }
278        
279        const button = document.createElement('button');
280        button.id = 'freepik-bulk-download-btn';
281        button.innerHTML = '📥 Scarica Immagini Free';
282        button.addEventListener('click', downloadAllFreeImages);
283        
284        const progressDiv = document.createElement('div');
285        progressDiv.id = 'freepik-download-progress';
286        progressDiv.innerHTML = `
287            <div class="progress-text">Preparazione...</div>
288            <div class="progress-bar">
289                <div class="progress-bar-fill"></div>
290            </div>
291            <div class="progress-detail">Inizializzazione...</div>
292        `;
293        
294        document.body.appendChild(button);
295        document.body.appendChild(progressDiv);
296        
297        console.log('Pulsante di download aggiunto alla pagina');
298    }
299
300    // Inizializza quando il DOM è pronto
301    function init() {
302        if (document.readyState === 'loading') {
303            document.addEventListener('DOMContentLoaded', createDownloadButton);
304        } else {
305            createDownloadButton();
306        }
307    }
308
309    init();
310})();