← back to Animate Museum Posts
src/fetchFromMet.js
141 lines
import axios from 'axios';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const MET_API_BASE = 'https://collectionapi.metmuseum.org/public/collection/v1';
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Cache of highlight object IDs (public domain with images)
let cachedObjectIds = null;
async function getHighlightObjectIds() {
if (cachedObjectIds && cachedObjectIds.length > 0) {
return cachedObjectIds;
}
try {
// Search for highlighted public domain artworks with images
const response = await axios.get(`${MET_API_BASE}/search`, {
params: {
isHighlight: true,
hasImages: true,
q: '*' // All artworks
},
timeout: 30000
});
if (response.data && response.data.objectIDs) {
cachedObjectIds = response.data.objectIDs;
console.log(`Met Museum: Found ${cachedObjectIds.length} highlighted artworks`);
return cachedObjectIds;
}
} catch (error) {
console.error('Met Museum search failed:', error.message);
}
return [];
}
async function getObjectDetails(objectId) {
try {
const response = await axios.get(`${MET_API_BASE}/objects/${objectId}`, {
timeout: 15000
});
return response.data;
} catch (error) {
console.error(`Failed to fetch object ${objectId}:`, error.message);
return null;
}
}
export async function fetchFromMet() {
try {
console.log('Fetching from Metropolitan Museum of Art...');
const objectIds = await getHighlightObjectIds();
if (objectIds.length === 0) {
throw new Error('No artworks found from Met Museum');
}
// Try up to 10 random artworks to find one with valid public domain image
for (let attempt = 0; attempt < 10; attempt++) {
const randomId = objectIds[Math.floor(Math.random() * objectIds.length)];
const artwork = await getObjectDetails(randomId);
// Accept public domain OR CC0 licensed images
const hasValidLicense = artwork?.isPublicDomain ||
artwork?.rightsAndReproduction?.toLowerCase().includes('cc0') ||
artwork?.rightsAndReproduction?.toLowerCase().includes('public domain');
if (!artwork || !hasValidLicense || !artwork.primaryImage) {
if (attempt < 9) await sleep(500); // Small delay between attempts
continue;
}
// Download the image
console.log(`Downloading: ${artwork.title}`);
const imageResponse = await axios.get(artwork.primaryImage, {
responseType: 'arraybuffer',
timeout: 30000,
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MuseumBot/1.0)'
}
});
if (!imageResponse.data || imageResponse.data.length < 1000) {
continue;
}
// Save to output directory
const outputDir = path.join(__dirname, '..', 'output');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const timestamp = Date.now();
const fileName = `met_${timestamp}.jpg`;
const filePath = path.join(outputDir, fileName);
fs.writeFileSync(filePath, imageResponse.data);
// Return normalized metadata
const metadata = {
title: artwork.title || 'Untitled',
artist: artwork.artistDisplayName || 'Unknown Artist',
year: artwork.objectDate || 'Date Unknown',
description: artwork.medium || '',
objectNumber: `MET-${artwork.objectID}`,
imageUrl: artwork.primaryImage,
localPath: filePath,
fileName: fileName,
museum: 'The Metropolitan Museum of Art',
creditLine: artwork.creditLine || 'The Metropolitan Museum of Art (Public Domain)',
dimensions: artwork.dimensions ? [artwork.dimensions] : [],
department: artwork.department,
culture: artwork.culture,
period: artwork.period
};
console.log(`Met Museum: Fetched "${metadata.title}" by ${metadata.artist}`);
return metadata;
}
throw new Error('Could not find valid public domain artwork after 5 attempts');
} catch (error) {
console.error('Met Museum fetch failed:', error.message);
throw error;
}
}
// Test if run directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
fetchFromMet()
.then(m => console.log('Success:', JSON.stringify(m, null, 2)))
.catch(e => console.error('Failed:', e.message));
}