Skip to content

Commit 2448669

Browse files
committed
Drastically simplify API logic to prevent rate limiting
- Switch to sequential requests with 200ms delays (no parallel batching) - Reduce to 8 artworks max to minimize API calls - Add request collision prevention with global pending flag - Change search from q=a to q=painting for more predictable results - Simplify error handling and improve console logging with emojis - Keep aggressive 10-minute caching for instant subsequent loads This ultra-conservative approach should prevent the Failed to fetch errors by being much gentler on the Met Museum API.
1 parent af0b4a1 commit 2448669

File tree

1 file changed

+57
-91
lines changed

1 file changed

+57
-91
lines changed

art.html

Lines changed: 57 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -297,135 +297,101 @@ <h2 class="text-2xl font-light text-gray-800 mb-2">${collection.title}</h2>
297297
]
298298
};
299299

300-
// Robust API fetching with rate limiting and retry logic
300+
// Simple, reliable API fetching with aggressive caching
301301
async function getCuratedArtworks(collectionKey) {
302-
// Check cache first
302+
// Check cache first - more aggressive caching
303303
const cacheKey = `collection_${collectionKey}`;
304304
const cachedData = artworkCache.get(cacheKey);
305305

306306
if (cachedData && (Date.now() - cachedData.timestamp) < CACHE_DURATION) {
307-
console.log(`Loading ${collectionKey} from cache...`);
308-
309-
// Show quick cache indicator
310-
gallery.innerHTML = `
311-
<div class="col-span-full mb-6">
312-
<div class="bg-green-50 border border-green-200 rounded-lg p-2 text-center">
313-
<i class="fas fa-check-circle text-green-500 mr-2"></i>
314-
<span class="text-green-700 text-xs">Loaded from cache (${cachedData.artworks.length} artworks)</span>
315-
</div>
316-
</div>
317-
`;
318-
319-
// Small delay to show the cache message, then display artworks
320-
setTimeout(() => {
321-
displayArtworks(cachedData.artworks);
322-
}, 500);
323-
307+
console.log(`✓ Loading ${collectionKey} from cache (${cachedData.artworks.length} artworks)`);
308+
displayArtworks(cachedData.artworks);
324309
return;
325310
}
326311

312+
// If we have multiple pending requests, prevent them
313+
if (window.pendingAPIRequest) {
314+
console.log('Another API request in progress, showing fallback...');
315+
showFallbackWithRetry(collectionKey, 'Another request in progress');
316+
return;
317+
}
318+
319+
window.pendingAPIRequest = true;
320+
327321
try {
328322
// Show loading state
329323
loadingIndicator.style.display = 'block';
330324
gallery.innerHTML = '';
331325

332-
console.log(`Fetching fresh ${collectionKey} artworks from Met Museum API...`);
333-
334-
// Fetch search results with timeout
335-
const controller = new AbortController();
336-
const timeoutId = setTimeout(() => controller.abort(), 10000);
326+
console.log(`🔄 Fetching fresh ${collectionKey} artworks from Met Museum API...`);
337327

338-
const response = await fetch('https://collectionapi.metmuseum.org/public/collection/v1/search?isHighlight=true&hasImages=true&q=a', {
339-
signal: controller.signal,
340-
headers: {
341-
'Accept': 'application/json'
342-
}
328+
// Ultra-simple API call - no complex batching
329+
const searchResponse = await fetch('https://collectionapi.metmuseum.org/public/collection/v1/search?isHighlight=true&hasImages=true&q=painting', {
330+
headers: { 'Accept': 'application/json' }
343331
});
344332

345-
clearTimeout(timeoutId);
346-
console.log('Response status:', response.status, response.statusText);
347-
348-
if (!response.ok) {
349-
throw new Error(`API returned ${response.status}: ${response.statusText}`);
333+
if (!searchResponse.ok) {
334+
throw new Error(`Search failed: ${searchResponse.status}`);
350335
}
351336

352-
const data = await response.json();
353-
console.log('Search data received:', data);
337+
const searchData = await searchResponse.json();
338+
console.log(`📦 Got ${searchData.total} search results`);
354339

355-
const objectIDs = data.objectIDs;
356-
357-
if (!objectIDs || objectIDs.length === 0) {
358-
throw new Error('No artworks found in search results');
340+
if (!searchData.objectIDs || searchData.objectIDs.length === 0) {
341+
throw new Error('No artworks found');
359342
}
360-
361-
console.log(`Found ${objectIDs.length} artworks, fetching first 15 details...`);
362-
363-
// Fetch artwork details with rate limiting (staggered requests)
343+
344+
// Fetch just 8 artworks sequentially (no parallel requests)
364345
const artworks = [];
365-
const batchSize = 5; // Smaller batches to respect rate limits
366-
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
346+
const maxArtworks = 8;
367347

368-
for (let i = 0; i < Math.min(15, objectIDs.length); i += batchSize) {
369-
const batch = objectIDs.slice(i, i + batchSize);
370-
console.log(`Fetching batch ${Math.floor(i/batchSize) + 1}:`, batch);
371-
372-
const batchPromises = batch.map(async (id) => {
373-
try {
374-
const res = await fetch(`https://collectionapi.metmuseum.org/public/collection/v1/objects/${id}`, {
375-
headers: { 'Accept': 'application/json' }
376-
});
377-
if (!res.ok) {
378-
console.warn(`Failed to fetch artwork ${id}: ${res.status}`);
379-
return null;
348+
console.log(`🎨 Loading ${maxArtworks} artwork details...`);
349+
350+
for (let i = 0; i < Math.min(maxArtworks, searchData.objectIDs.length); i++) {
351+
try {
352+
const objectId = searchData.objectIDs[i];
353+
354+
const artworkResponse = await fetch(`https://collectionapi.metmuseum.org/public/collection/v1/objects/${objectId}`, {
355+
headers: { 'Accept': 'application/json' }
356+
});
357+
358+
if (artworkResponse.ok) {
359+
const artwork = await artworkResponse.json();
360+
if (artwork && artwork.primaryImageSmall) {
361+
artworks.push(artwork);
362+
console.log(`✓ Loaded: ${artwork.title}`);
380363
}
381-
return await res.json();
382-
} catch (err) {
383-
console.warn(`Network error fetching artwork ${id}:`, err.message);
384-
return null;
385364
}
386-
});
387-
388-
const batchResults = await Promise.all(batchPromises);
389-
artworks.push(...batchResults.filter(artwork => artwork !== null));
390-
391-
// Small delay between batches to respect rate limits
392-
if (i + batchSize < Math.min(15, objectIDs.length)) {
393-
await delay(100);
365+
366+
// Small delay between requests
367+
await new Promise(resolve => setTimeout(resolve, 200));
368+
369+
} catch (err) {
370+
console.warn(`⚠️ Failed to load artwork ${i}:`, err.message);
394371
}
395372
}
396-
397-
// Filter and display only artworks with images
398-
const validArtworks = artworks.filter(artwork => artwork && artwork.primaryImageSmall);
399-
console.log(`Successfully loaded ${validArtworks.length} artworks with images`);
400373

401-
if (validArtworks.length === 0) {
402-
throw new Error('No artworks with images found in current batch');
374+
console.log(`✅ Successfully loaded ${artworks.length} artworks with images`);
375+
376+
if (artworks.length === 0) {
377+
throw new Error('No artworks with images found');
403378
}
404379

405-
// Cache the successful results
380+
// Cache the results
406381
artworkCache.set(cacheKey, {
407-
artworks: validArtworks,
382+
artworks: artworks,
408383
timestamp: Date.now()
409384
});
410-
console.log(`Cached ${validArtworks.length} artworks for ${collectionKey}`);
411385

412-
displayArtworks(validArtworks);
386+
displayArtworks(artworks);
413387

414388
} catch (error) {
415-
console.error('Met Museum API Error:', error);
416-
417-
// Only show fallback for actual API failures, not rate limiting issues
418-
if (error.name === 'AbortError') {
419-
console.log('Request timed out, showing fallback...');
420-
} else if (error.message.includes('Failed to fetch')) {
421-
console.log('Network error, showing fallback...');
422-
} else {
423-
console.log('API error, showing fallback...');
424-
}
425-
389+
console.error('🚨 API Error:', error.message);
426390
showFallbackWithRetry(collectionKey, error.message);
391+
427392
} finally {
428393
loadingIndicator.style.display = 'none';
394+
window.pendingAPIRequest = false;
429395
}
430396
}
431397

0 commit comments

Comments
 (0)