/ tableland / scripts / pipeline.ts
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 }