pipeline.ts
1 #!/usr/bin/env node 2 import { config } from 'dotenv' 3 import { Wallet, JsonRpcProvider } from 'ethers' 4 import { TableManager } from './TableManager' 5 import { readFileSync, existsSync, writeFileSync } from 'fs' 6 import { join, resolve } from 'path' 7 8 // Load environment variables 9 config({ path: '../.env' }) 10 11 interface PipelineOptions { 12 songId: number 13 encryptionResultPath: string 14 metadata?: { 15 isrc?: string 16 iswc?: string 17 title?: string 18 artist?: string 19 duration?: number 20 language?: string 21 genius_id?: number 22 lrclib_id?: number 23 genius_slug?: string 24 streaming_links?: Record<string, string> 25 artwork_hash?: Record<string, any> 26 } 27 } 28 29 /** 30 * Complete pipeline: Takes encryption results and adds to Tableland 31 */ 32 async function processSong(options: PipelineOptions) { 33 console.log('š Processing song through complete pipeline...\n') 34 35 // Load encryption results 36 const encryptionPath = resolve(options.encryptionResultPath) 37 if (!existsSync(encryptionPath)) { 38 throw new Error(`Encryption result not found: ${encryptionPath}`) 39 } 40 41 const encryptionResult = JSON.parse(readFileSync(encryptionPath, 'utf-8')) 42 43 console.log(`š Loaded encryption results from: ${encryptionPath}`) 44 console.log(` MIDI CID: ${encryptionResult.midiCid}`) 45 console.log(` Translations CID: ${encryptionResult.translationsCid}`) 46 console.log() 47 48 // Prepare song data for Tableland 49 const songData = { 50 id: options.songId, 51 ...options.metadata, 52 stems: { 53 piano: encryptionResult.midiCid 54 }, 55 translations: encryptionResult.translationsCid ? { 56 encrypted: encryptionResult.translationsCid 57 } : undefined 58 } 59 60 // Create temporary file for add-song 61 const tempPath = `/tmp/song-${options.songId}-${Date.now()}.json` 62 writeFileSync(tempPath, JSON.stringify(songData, null, 2)) 63 console.log(`š¾ Created temporary song data at: ${tempPath}`) 64 65 // Setup provider and signer 66 const provider = new JsonRpcProvider(process.env.BASE_SEPOLIA_RPC_URL || 'https://sepolia.base.org') 67 const signer = new Wallet(process.env.PRIVATE_KEY!, provider) 68 69 // Initialize TableManager 70 const tableManager = new TableManager(signer, 'base-sepolia') 71 72 // Get songs table name 73 const songsTable = tableManager.getTableName('songs') 74 if (!songsTable) { 75 throw new Error('Songs table not deployed. Run deploy-tables.ts first.') 76 } 77 78 // Add to Tableland 79 console.log('\nš Adding to Tableland...') 80 // Use the add-song functionality 81 const { addSong } = await import('./add-song') 82 await addSong(tempPath) 83 84 // Clean up temp file 85 const { unlinkSync } = await import('fs') 86 unlinkSync(tempPath) 87 88 console.log('\nā Pipeline complete!') 89 console.log(` Song ID: ${options.songId}`) 90 console.log(` Tableland table: ${songsTable}`) 91 } 92 93 // CLI usage 94 if (require.main === module) { 95 const args = process.argv.slice(2) 96 97 if (args.length < 2) { 98 console.log('Usage: npx ts-node pipeline.ts <songId> <encryption-result.json> [metadata.json]') 99 console.log() 100 console.log('Example:') 101 console.log(' npx ts-node pipeline.ts 1 ../data/encrypted-v2/song-1.json ../data/songs-metadata.json') 102 process.exit(1) 103 } 104 105 const songId = parseInt(args[0]) 106 const encryptionPath = args[1] 107 const metadataPath = args[2] 108 109 let metadata = {} 110 if (metadataPath && existsSync(metadataPath)) { 111 const allMetadata = JSON.parse(readFileSync(metadataPath, 'utf-8')) 112 metadata = allMetadata.find((s: any) => s.id === songId) || {} 113 } 114 115 processSong({ 116 songId, 117 encryptionResultPath: encryptionPath, 118 metadata 119 }) 120 .then(() => { 121 process.exit(0) 122 }) 123 .catch((error) => { 124 console.error('Fatal error:', error) 125 process.exit(1) 126 }) 127 } 128 129 export { processSong }