const fs = require('fs'); const path = require('path'); const csvParser = require('csv-parser'); const koillectionBaseUrl = 'https://koillection.nelson-household.com/api'; // Verify this URL const authenticationUrl = `${koillectionBaseUrl}/authentication_token`; // URL to obtain JWT const imagesDir = path.join(__dirname, 'master-set-images/perfect-order'); // Use __dirname for correct path // Define mapping of collection names to their corresponding CSV files const collectionsMapping = { 'Perfect Order': './csv/ME03PerfectOrderProductsAndPrices.csv', }; const createJWTToken = async (username, password) => { try { const response = await fetch(authenticationUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username, password }), }); if (!response.ok) { throw new Error(`Failed to obtain JWT token: ${response.status} - ${response.statusText}`); } const data = await response.json(); console.log(`JWT Token obtained: ${data.token}`); return data.token; } catch (error) { console.error(`Error obtaining JWT token:`, error); throw error; // Re-throw to be caught in other functions } }; const createCollectionIfNotExists = async (name, parentCollectionId, token) => { try { const url = `${koillectionBaseUrl}/collections`; const response = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ title: name, visibility: 'public', parent: `/api/collections/${parentCollectionId}` }), }); if (!response.ok) { throw new Error(`Failed to create collection "${name}": ${response.status} - ${response.statusText} - ${JSON.stringify(await response.json())}`); } const data = await response.json(); console.log(`Successfully created collection "${name}" with ID: ${data.id}`); return data.id; } catch (error) { console.error(`Error creating collection ${name} from CSV:`, error); throw error; } }; const createItem = async (payload, token) => { try { const response = await fetch(`${koillectionBaseUrl}/items`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error(`Failed to create item: ${response.status} - ${await response.text()}`); } const createdItem = await response.json(); return createdItem; } catch (error) { console.error('Error creating item:', error); throw error; } }; async function downloadTcgImage(productId, imageUrl) { try { // 1. Ensure directory exists if (!fs.existsSync(imagesDir)) { fs.mkdirSync(imagesDir, { recursive: true }); } // 2. Construct file destination const filename = `${productId}_${path.basename(new URL(imageUrl).pathname)}`; const dest = path.join(imagesDir, filename); // 3. Fetch the image const response = await fetch(imageUrl); if (!response.ok) { console.error(`Failed to fetch ${imageUrl}: ${response.statusText}`); return {}; } const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); fs.writeFileSync(dest, buffer); // Use the correct destination path console.log(`Successfully saved: ${dest}`); return { success: true, destination: dest }; } catch (error) { console.error('Error downloading image:', error); return {}; } } const applyImageToItem = async (itemId, localImagePath, fileName, token) => { try { console.log(`Local Image Path: ${localImagePath}`); if (!fs.existsSync(localImagePath)) { console.log('Looks like the image does not exists?'); return {}; } const formData = new FormData(); formData.append('image', fs.readFileSync(localImagePath), filename); const response = await fetch(`${koillectionBaseUrl}/items/${itemId}/image`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, }, body: formData }); if (!response.ok) { const errorData = await response.json(); throw new Error(`HTTP error! Status: ${response.status}, Error: ${JSON.stringify(errorData)}`); } const data = await response.json(); console.log(`Returned item with image: ${JSON.stringify(data, null, 2)}`); return data; // Returns the item with the new image } catch (error) { console.error('Error applying image:', error); throw error; // Re-throw for handling in the caller } }; const parseCsv = async (collectionId, csvFilePath, token) => { try { const rows = []; const results = {}; const stream = fs.createReadStream(csvFilePath) .pipe(csvParser()); // Now using the stream directly return await new Promise((resolve, reject) => { stream.on('data', (row) => rows.push(row)); stream.on('end', async () => { try { const processingPromises = rows.map(async (row) => { const payload = { name: row.name, collection: `/api/collections/${collectionId}`, visibility: 'public', }; const createdItem = await createItem(payload, token); console.log(`Created item: ${createdItem.name}`); //const imageUrl = await downloadTcgImage(itemId, row.imageUrl); // Await the image download results[row.productId] = { imageUrl: row.imageUrl, itemId: createdItem.id }; }); await Promise.all(processingPromises); resolve(results); } catch (err) { reject(err); } }); stream.on('error', (err) => reject(err)); }); } catch (error) { console.error(`Error creating collection ${name} from CSV:`, error); return {}; } } function mergeObjects(o1, o2) { const merged = { ...o1 }; for (const key in o2) { if (merged[key]) { // Key exists, convert itemId to itemIds array const existing = merged[key]; const incoming = o2[key]; // Keep existing itemId if it's already an array, otherwise make it one const items = Array.isArray(existing.itemIds) ? existing.itemIds : [existing.itemId]; items.push(incoming.itemId); merged[key] = { ...existing, ...incoming, itemIds: items, itemId: undefined // Remove old field }; // Optionally cleanup undefined delete merged[key].itemId; } else { // Key is new, add as is merged[key] = o2[key]; } } return merged; } const createCollectionFromCSV = async (name, csvFilePath) => { const token = await createJWTToken('bigassdragon', 'd7Bis4Y4EUhfMf'); const joeCollectionId = await createCollectionIfNotExists(name, '019dc310-a130-799e-8c0d-db90774b7b77', token); const tylerCollectionId = await createCollectionIfNotExists(name, '019dc310-d175-7e59-b465-c8e684ee7186', token); const joeCollectionData = await parseCsv(joeCollectionId, csvFilePath, token).then((results) => results); const tylerCollectionData = await parseCsv(tylerCollectionId, csvFilePath, token).then((results) => results); const mergedCollectionData = mergeObjects(joeCollectionData, tylerCollectionData); const itemIdImageLocations = []; for (const productId of Object.keys(mergedCollectionData)) { const item = mergedCollectionData[productId]; // Await each download before starting the next // const { destination } = await downloadTcgImage(productId, item.imageUrl); item.itemIds.forEach((id) => itemIdImageLocations.push({ itemId: id, imageDest: imagesDir, fileName: `${productId}.jpg` })); } // After processing all collections, check the imagesDir console.log("Finished processing collections."); const files = fs.readdirSync(imagesDir); console.log(`Files in ${imagesDir}: ${files}`); console.log(`Item ID Image Locations: ${JSON.stringify(itemIdImageLocations, null, 2)}`); // Process multiple images concurrently using Promise.all() const promises = itemIdImageLocations.map(async (item) => { await applyImageToItem(item.itemId, item.imageDest, item.fileName, token); }); await Promise.all(promises); }; // Main execution async function main() { for (const collectionName in collectionsMapping) { try { console.log(`Processing collection: ${collectionName}`); await createCollectionFromCSV(collectionName, collectionsMapping[collectionName]); console.log(`Collection ${collectionName} processed successfully.`); } catch (error) { console.error(`Error processing collection - ${collectionName}:`, error); // Break the loop if an error occurs break; } } } main();