Automatically scrape and download all automation workflows from AutoGineer
Size
12.1 KB
Version
1.1.2
Created
Dec 9, 2025
Updated
4 days ago
1// ==UserScript==
2// @name AutoGineer Workflow Scraper & Downloader
3// @description Automatically scrape and download all automation workflows from AutoGineer
4// @version 1.1.2
5// @match https://*.autogineer.com/*
6// @icon https://www.autogineer.com/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('AutoGineer Workflow Scraper initialized');
12
13 // State management
14 let isScrapingActive = false;
15 let scrapedWorkflows = [];
16 let totalDownloaded = 0;
17 let statusElement = null;
18
19 // Utility function to wait
20 function wait(ms) {
21 return new Promise(resolve => setTimeout(resolve, ms));
22 }
23
24 // Utility function to download JSON
25 function downloadJSON(data, filename) {
26 const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
27 const url = URL.createObjectURL(blob);
28 const a = document.createElement('a');
29 a.href = url;
30 a.download = filename;
31 document.body.appendChild(a);
32 a.click();
33 document.body.removeChild(a);
34 URL.revokeObjectURL(url);
35 console.log(`Downloaded: ${filename}`);
36 }
37
38 // Update status display
39 function updateStatus(message, isError = false) {
40 console.log(message);
41 if (statusElement) {
42 statusElement.textContent = message;
43 statusElement.style.color = isError ? '#ef4444' : '#10b981';
44 }
45 }
46
47 // Check if we're on the main vault page
48 function isVaultPage() {
49 return window.location.pathname === '/' || window.location.pathname.includes('/vault');
50 }
51
52 // Check if we're on a template detail page
53 function isTemplatePage() {
54 return window.location.pathname.includes('/template/');
55 }
56
57 // Get all workflow cards on the current page
58 function getWorkflowCards() {
59 const cards = document.querySelectorAll('[role="button"][aria-label*="View details for"]');
60 console.log(`Found ${cards.length} workflow cards on page`);
61 return Array.from(cards);
62 }
63
64 // Click the "Load More" button if it exists
65 async function clickLoadMore() {
66 const loadMoreBtn = document.querySelector('button:not([disabled])');
67 const btnText = loadMoreBtn?.textContent || '';
68
69 if (loadMoreBtn && btnText.includes('Load More')) {
70 console.log('Clicking Load More button...');
71 loadMoreBtn.click();
72 await wait(3000); // Wait for new workflows to load
73 return true;
74 }
75 return false;
76 }
77
78 // Download workflow from detail page
79 async function downloadWorkflowFromDetailPage() {
80 try {
81 updateStatus('Waiting for page to load...');
82 await wait(5000); // Increased wait time for data to load
83
84 // Find the download button by text content
85 const buttons = Array.from(document.querySelectorAll('button'));
86 const downloadBtn = buttons.find(btn => btn.textContent.includes('Download JSON'));
87
88 if (!downloadBtn) {
89 throw new Error('Download button not found');
90 }
91
92 // Get workflow name from the page
93 const workflowTitle = document.querySelector('h1.text-3xl')?.textContent?.trim() || 'workflow';
94
95 updateStatus(`Downloading: ${workflowTitle}`);
96
97 // Click download button
98 downloadBtn.click();
99 await wait(2000); // Wait for download to initiate
100
101 totalDownloaded++;
102 updateStatus(`Downloaded ${totalDownloaded} workflows: ${workflowTitle}`);
103
104 return { success: true, name: workflowTitle };
105 } catch (error) {
106 console.error('Error downloading workflow:', error);
107 updateStatus(`Error downloading workflow: ${error.message}`, true);
108 return { success: false, error: error.message };
109 }
110 }
111
112 // Navigate back to vault
113 async function navigateBackToVault() {
114 const backBtn = document.querySelector('button');
115 const btnText = backBtn?.textContent || '';
116
117 if (backBtn && btnText.includes('Back to Vault')) {
118 console.log('Navigating back to vault...');
119 backBtn.click();
120 await wait(2000); // Wait for navigation
121 return true;
122 } else {
123 // Fallback: navigate directly
124 console.log('Using fallback navigation...');
125 window.history.back();
126 await wait(2000);
127 return true;
128 }
129 }
130
131 // Main scraping function
132 async function startScraping() {
133 if (isScrapingActive) {
134 updateStatus('Scraping already in progress!', true);
135 return;
136 }
137
138 isScrapingActive = true;
139 totalDownloaded = 0;
140 scrapedWorkflows = [];
141
142 try {
143 updateStatus('Starting workflow scraping...');
144
145 // Make sure we're on the vault page
146 if (!isVaultPage()) {
147 updateStatus('Please navigate to the main vault page first', true);
148 isScrapingActive = false;
149 return;
150 }
151
152 let hasMoreWorkflows = true;
153 let pageCount = 0;
154
155 while (hasMoreWorkflows && isScrapingActive) {
156 pageCount++;
157 updateStatus(`Processing page ${pageCount}...`);
158
159 // Get all workflow cards on current page
160 const workflowCards = getWorkflowCards();
161
162 if (workflowCards.length === 0) {
163 updateStatus('No more workflows found', false);
164 break;
165 }
166
167 // Process each workflow card
168 for (let i = 0; i < workflowCards.length; i++) {
169 if (!isScrapingActive) break;
170
171 updateStatus(`Processing workflow ${i + 1}/${workflowCards.length} on page ${pageCount}...`);
172
173 // Click the workflow card
174 const cards = getWorkflowCards(); // Re-query to avoid stale references
175 if (cards[i]) {
176 cards[i].click();
177 await wait(2000); // Wait for navigation
178
179 // Download the workflow
180 const result = await downloadWorkflowFromDetailPage();
181
182 if (result.success) {
183 scrapedWorkflows.push(result.name);
184 }
185
186 // Navigate back to vault
187 await navigateBackToVault();
188 await wait(1500); // Wait for page to stabilize
189 }
190 }
191
192 // Try to load more workflows
193 updateStatus('Checking for more workflows...');
194 hasMoreWorkflows = await clickLoadMore();
195
196 if (!hasMoreWorkflows) {
197 updateStatus(`Scraping complete! Downloaded ${totalDownloaded} workflows`, false);
198 break;
199 }
200 }
201
202 // Final summary
203 updateStatus(`✓ Scraping finished! Total downloaded: ${totalDownloaded} workflows`, false);
204 console.log('All scraped workflows:', scrapedWorkflows);
205
206 } catch (error) {
207 console.error('Scraping error:', error);
208 updateStatus(`Error during scraping: ${error.message}`, true);
209 } finally {
210 isScrapingActive = false;
211 }
212 }
213
214 // Stop scraping
215 function stopScraping() {
216 isScrapingActive = false;
217 updateStatus('Scraping stopped by user', false);
218 }
219
220 // Create control panel UI
221 function createControlPanel() {
222 // Check if panel already exists
223 if (document.getElementById('workflow-scraper-panel')) {
224 return;
225 }
226
227 const panel = document.createElement('div');
228 panel.id = 'workflow-scraper-panel';
229 panel.style.cssText = `
230 position: fixed;
231 top: 20px;
232 right: 20px;
233 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
234 border: 2px solid rgba(255, 255, 255, 0.2);
235 border-radius: 16px;
236 padding: 20px;
237 z-index: 999999;
238 box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
239 min-width: 320px;
240 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
241 `;
242
243 const title = document.createElement('div');
244 title.textContent = '🤖 Workflow Scraper';
245 title.style.cssText = `
246 font-size: 18px;
247 font-weight: bold;
248 color: white;
249 margin-bottom: 15px;
250 text-align: center;
251 `;
252
253 const status = document.createElement('div');
254 status.id = 'scraper-status';
255 status.textContent = 'Ready to scrape workflows';
256 status.style.cssText = `
257 background: rgba(255, 255, 255, 0.15);
258 color: white;
259 padding: 12px;
260 border-radius: 8px;
261 margin-bottom: 15px;
262 font-size: 13px;
263 min-height: 40px;
264 word-wrap: break-word;
265 `;
266 statusElement = status;
267
268 const buttonContainer = document.createElement('div');
269 buttonContainer.style.cssText = `
270 display: flex;
271 gap: 10px;
272 `;
273
274 const startBtn = document.createElement('button');
275 startBtn.textContent = 'Start Scraping';
276 startBtn.style.cssText = `
277 flex: 1;
278 background: white;
279 color: #667eea;
280 border: none;
281 padding: 12px 20px;
282 border-radius: 8px;
283 font-weight: bold;
284 cursor: pointer;
285 font-size: 14px;
286 transition: all 0.3s;
287 `;
288 startBtn.onmouseover = () => startBtn.style.transform = 'scale(1.05)';
289 startBtn.onmouseout = () => startBtn.style.transform = 'scale(1)';
290 startBtn.onclick = startScraping;
291
292 const stopBtn = document.createElement('button');
293 stopBtn.textContent = 'Stop';
294 stopBtn.style.cssText = `
295 background: rgba(239, 68, 68, 0.9);
296 color: white;
297 border: none;
298 padding: 12px 20px;
299 border-radius: 8px;
300 font-weight: bold;
301 cursor: pointer;
302 font-size: 14px;
303 transition: all 0.3s;
304 `;
305 stopBtn.onmouseover = () => stopBtn.style.transform = 'scale(1.05)';
306 stopBtn.onmouseout = () => stopBtn.style.transform = 'scale(1)';
307 stopBtn.onclick = stopScraping;
308
309 const closeBtn = document.createElement('button');
310 closeBtn.textContent = '×';
311 closeBtn.style.cssText = `
312 position: absolute;
313 top: 10px;
314 right: 10px;
315 background: rgba(255, 255, 255, 0.2);
316 color: white;
317 border: none;
318 width: 28px;
319 height: 28px;
320 border-radius: 50%;
321 cursor: pointer;
322 font-size: 20px;
323 line-height: 1;
324 transition: all 0.3s;
325 `;
326 closeBtn.onmouseover = () => closeBtn.style.background = 'rgba(255, 255, 255, 0.3)';
327 closeBtn.onmouseout = () => closeBtn.style.background = 'rgba(255, 255, 255, 0.2)';
328 closeBtn.onclick = () => panel.remove();
329
330 buttonContainer.appendChild(startBtn);
331 buttonContainer.appendChild(stopBtn);
332
333 panel.appendChild(closeBtn);
334 panel.appendChild(title);
335 panel.appendChild(status);
336 panel.appendChild(buttonContainer);
337
338 document.body.appendChild(panel);
339 console.log('Control panel created');
340 }
341
342 // Initialize the extension
343 function init() {
344 console.log('Initializing AutoGineer Workflow Scraper...');
345
346 // Wait for page to be ready
347 if (document.readyState === 'loading') {
348 document.addEventListener('DOMContentLoaded', () => {
349 setTimeout(createControlPanel, 1000);
350 });
351 } else {
352 setTimeout(createControlPanel, 1000);
353 }
354 }
355
356 // Start the extension
357 init();
358})();