Spaces:
Sleeping
Sleeping
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // FILE: server.js - Complete Backend with Smart Instruments | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const express = require('express'); | |
| const multer = require('multer'); | |
| const cors = require('cors'); | |
| const path = require('path'); | |
| const fs = require('fs'); | |
| const { exec } = require('child_process'); | |
| const util = require('util'); | |
| const execPromise = util.promisify(exec); | |
| const app = express(); | |
| const PORT = process.env.PORT || 7860; | |
| app.use(cors()); | |
| app.use(express.json()); | |
| const uploadsDir = path.join(__dirname, 'uploads'); | |
| const outputsDir = path.join(__dirname, 'outputs'); | |
| if (!fs.existsSync(uploadsDir)) fs.mkdirSync(uploadsDir, { recursive: true }); | |
| if (!fs.existsSync(outputsDir)) fs.mkdirSync(outputsDir, { recursive: true }); | |
| const storage = multer.diskStorage({ | |
| destination: (req, file, cb) => cb(null, uploadsDir), | |
| filename: (req, file, cb) => cb(null, `${Date.now()}-${file.originalname}`) | |
| }); | |
| const upload = multer({ | |
| storage: storage, | |
| limits: { fileSize: 50 * 1024 * 1024 } | |
| }); | |
| // πΉ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // INSTRUMENT LIBRARY - Categorized by Mood | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const INSTRUMENTS = { | |
| // π SAD INSTRUMENTS | |
| sad_piano: { | |
| name: "Sad Piano", | |
| mood: "sad", | |
| command: "aevalsrc='0.13*sin(2*PI*(220+5*sin(0.08*t))*t)*exp(-0.15*mod(t,4))':d=12,aecho=0.7:0.85:600:0.35,lowpass=f=3000" | |
| }, | |
| melancholic_violin: { | |
| name: "Melancholic Violin", | |
| mood: "sad", | |
| command: "aevalsrc='0.12*sin(2*PI*330*t)*sin(2*PI*0.3*t)*exp(-0.1*mod(t,5))':d=12,vibrato=f=0.15:d=0.5,aecho=0.6:0.8:500:0.3" | |
| }, | |
| lonely_cello: { | |
| name: "Lonely Cello", | |
| mood: "sad", | |
| command: "aevalsrc='0.14*sin(2*PI*165*t)*sin(2*PI*0.2*t)':d=12,chorus=0.4:0.7:40:0.3:0.2:2,lowpass=f=2500" | |
| }, | |
| tearful_flute: { | |
| name: "Tearful Flute", | |
| mood: "sad", | |
| command: "aevalsrc='0.11*sin(2*PI*440*t)*sin(2*PI*1.5*t)*exp(-0.2*mod(t,3))':d=12,aecho=0.5:0.75:400:0.3,vibrato=f=0.2:d=0.4" | |
| }, | |
| // β€οΈ ROMANTIC INSTRUMENTS | |
| romantic_piano: { | |
| name: "Romantic Piano", | |
| mood: "romantic", | |
| command: "aevalsrc='0.12*(sin(2*PI*330*t)+sin(2*PI*415*t))*exp(-0.12*mod(t,3.5))':d=12,aecho=0.6:0.8:450:0.35,chorus=0.3:0.6:35:0.25:0.15:2" | |
| }, | |
| love_strings: { | |
| name: "Love Strings", | |
| mood: "romantic", | |
| command: "aevalsrc='0.13*(sin(2*PI*440*t)+sin(2*PI*554*t))*sin(2*PI*0.4*t)':d=12,vibrato=f=0.18:d=0.45,aecho=0.5:0.7:350:0.3" | |
| }, | |
| gentle_harp: { | |
| name: "Gentle Harp", | |
| mood: "romantic", | |
| command: "aevalsrc='0.11*sin(2*PI*(660+8*sin(0.15*t))*t)*exp(-0.25*mod(t,2.5))':d=12,chorus=0.4:0.7:45:0.3:0.2:2,aecho=0.6:0.8:300:0.35" | |
| }, | |
| sweet_bells: { | |
| name: "Sweet Bells", | |
| mood: "romantic", | |
| command: "aevalsrc='0.09*sin(2*PI*880*t)*exp(-0.4*mod(t,2))':d=12,aecho=0.5:0.75:250:0.4,vibrato=f=0.12:d=0.3" | |
| }, | |
| // πβ€οΈ SAD ROMANTIC MIX | |
| bittersweet_piano: { | |
| name: "Bittersweet Piano", | |
| mood: "sad_romantic", | |
| command: "aevalsrc='0.13*(sin(2*PI*247*t)+sin(2*PI*330*t))*exp(-0.15*mod(t,4))*(1+0.3*sin(0.5*t))':d=12,aecho=0.7:0.85:500:0.35,vibrato=f=0.1:d=0.4" | |
| }, | |
| nostalgic_violin: { | |
| name: "Nostalgic Violin", | |
| mood: "sad_romantic", | |
| command: "aevalsrc='0.12*sin(2*PI*370*t)*sin(2*PI*0.35*t)*(1+0.2*sin(0.3*t))':d=12,chorus=0.5:0.75:40:0.3:0.2:2,aecho=0.6:0.8:450:0.3" | |
| }, | |
| heartbreak_strings: { | |
| name: "Heartbreak Strings", | |
| mood: "sad_romantic", | |
| command: "aevalsrc='0.13*(sin(2*PI*220*t)+sin(2*PI*330*t))*exp(-0.1*mod(t,5))':d=12,vibrato=f=0.15:d=0.5,lowpass=f=3500" | |
| }, | |
| // π AMBIENT/DREAMY INSTRUMENTS | |
| dreamy_pad: { | |
| name: "Dreamy Pad", | |
| mood: "ambient", | |
| command: "aevalsrc='0.11*(sin(2*PI*165*t)+sin(2*PI*220*t)+sin(2*PI*330*t))':d=12,aphaser=in_gain=0.4:out_gain=0.7:delay=3:decay=0.4:speed=0.25,aecho=0.5:0.7:400:0.3" | |
| }, | |
| ethereal_synth: { | |
| name: "Ethereal Synth", | |
| mood: "ambient", | |
| command: "aevalsrc='0.1*(sin(2*PI*220*t)+sin(2*PI*277*t))*sin(2*PI*0.5*t)':d=12,chorus=0.6:0.85:50:0.4:0.25:2,aecho=0.6:0.8:500:0.3" | |
| }, | |
| celestial_bells: { | |
| name: "Celestial Bells", | |
| mood: "ambient", | |
| command: "aevalsrc='0.09*(sin(2*PI*880*t)+sin(2*PI*1108*t))*exp(-0.3*t)':d=12,aecho=0.7:0.9:600:0.4,aphaser=in_gain=0.3:out_gain=0.6:delay=2:decay=0.3:speed=0.2" | |
| }, | |
| // πΈ CHILL/LOFI INSTRUMENTS | |
| lofi_guitar: { | |
| name: "LoFi Guitar", | |
| mood: "chill", | |
| command: "aevalsrc='0.12*sin(2*PI*196*t)*exp(-0.3*mod(t,3))*(1+0.15*random(0))':d=12,chorus=0.5:0.7:40:0.25:0.15:2,lowpass=f=4000" | |
| }, | |
| jazzy_piano: { | |
| name: "Jazzy Piano", | |
| mood: "chill", | |
| command: "aevalsrc='0.11*(sin(2*PI*262*t)+sin(2*PI*330*t))*exp(-0.2*mod(t,2.5))':d=12,vibrato=f=0.18:d=0.4,aecho=0.4:0.6:300:0.25" | |
| }, | |
| smooth_sax: { | |
| name: "Smooth Saxophone", | |
| mood: "chill", | |
| command: "aevalsrc='0.12*sin(2*PI*294*t)*sin(2*PI*2*t)':d=12,vibrato=f=0.25:d=0.5,chorus=0.4:0.7:45:0.3:0.2:2" | |
| }, | |
| // β‘ UPBEAT/ENERGETIC INSTRUMENTS | |
| bright_synth: { | |
| name: "Bright Synth", | |
| mood: "upbeat", | |
| command: "aevalsrc='0.11*(sin(2*PI*440*t)+sin(2*PI*554*t)+sin(2*PI*659*t))':d=12,chorus=0.6:0.8:50:0.35:0.25:2,highpass=f=200" | |
| }, | |
| sparkle_bells: { | |
| name: "Sparkle Bells", | |
| mood: "upbeat", | |
| command: "aevalsrc='0.1*(sin(2*PI*1320*t)+sin(2*PI*1760*t))*exp(-0.5*mod(t,1.5))':d=12,aecho=0.4:0.6:150:0.3,highpass=f=500" | |
| }, | |
| happy_piano: { | |
| name: "Happy Piano", | |
| mood: "upbeat", | |
| command: "aevalsrc='0.11*(sin(2*PI*523*t)+sin(2*PI*659*t))*exp(-0.25*mod(t,2))':d=12,chorus=0.5:0.7:40:0.3:0.2:2" | |
| } | |
| }; | |
| // π― βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // SMART EFFECT CONFIGURATIONS - Each effect has mood-matched instruments | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const EFFECTS = { | |
| lofi: { | |
| name: "LoFi Hip Hop", | |
| description: "Vintage warmth with jazzy chill vibes", | |
| baseCommand: `-af "asetrate=44100*0.90,aresample=44100,bass=g=5:f=85:width_type=h:width=100,treble=g=-3:f=8000,equalizer=f=200:t=q:width=1.5:g=-2,equalizer=f=1000:t=q:width=2:g=2,acompressor=threshold=-20dB:ratio=3:attack=10:release=120:makeup=3,vibrato=f=0.2:d=0.4,highpass=f=70,lowpass=f=4500,volume=1.3"`, | |
| instrumentVolume: 0.14, | |
| // LoFi uses chill + some sad romantic vibes | |
| instrumentMoods: ["chill", "sad_romantic", "ambient"] | |
| }, | |
| slowed_reverb: { | |
| name: "Slowed + Reverb", | |
| description: "Dreamy slow with sad romantic atmosphere", | |
| baseCommand: `-af "atempo=0.90,asetrate=44100*0.95,aresample=44100,bass=g=4:f=80:width_type=h:width=120,aecho=0.8:0.88:250:0.5,aecho=0.7:0.78:400:0.4,aecho=0.6:0.68:600:0.3,equalizer=f=100:t=q:width=2:g=3.5,equalizer=f=4000:t=q:width=4:g=-4,acompressor=threshold=-20dB:ratio=2:attack=15:release=200:makeup=2,highpass=f=50,lowpass=f=7000,volume=1.4"`, | |
| instrumentVolume: 0.13, | |
| // Slowed+Reverb is emotional - sad, romantic, sad_romantic mix | |
| instrumentMoods: ["sad", "romantic", "sad_romantic", "ambient"] | |
| }, | |
| nightcore: { | |
| name: "Nightcore", | |
| description: "High energy with bright sparkling sounds", | |
| baseCommand: `-af "atempo=1.30,asetrate=44100*1.18,aresample=44100,treble=g=6:f=5000:width_type=h:width=3000,bass=g=-3:f=100,equalizer=f=2000:t=q:width=2:g=2.5,equalizer=f=6000:t=q:width=3:g=4,equalizer=f=10000:t=q:width=4:g=5,acompressor=threshold=-18dB:ratio=3:attack=3:release=30:makeup=4,highpass=f=120,volume=1.4"`, | |
| instrumentVolume: 0.11, | |
| // Nightcore is upbeat and energetic | |
| instrumentMoods: ["upbeat", "romantic"] | |
| }, | |
| vaporwave: { | |
| name: "Vaporwave", | |
| description: "Retro nostalgic with dreamy sad vibes", | |
| baseCommand: `-af "asetrate=44100*0.82,aresample=44100,equalizer=f=300:t=q:width=2:g=4,equalizer=f=1500:t=q:width=3:g=-2,equalizer=f=6000:t=q:width=4:g=-4,aphaser=in_gain=0.5:out_gain=0.75:delay=4:decay=0.5:speed=0.2,chorus=0.6:0.85:55:0.4:0.25:2,vibrato=f=0.12:d=0.5,aecho=0.5:0.65:180:0.35,acompressor=threshold=-20dB:ratio=2.5:attack=12:release=150:makeup=2,highpass=f=70,lowpass=f=5500,volume=1.2"`, | |
| instrumentVolume: 0.13, | |
| // Vaporwave is nostalgic - sad_romantic + ambient | |
| instrumentMoods: ["sad_romantic", "ambient", "sad"] | |
| }, | |
| "8d_audio": { | |
| name: "8D Audio", | |
| description: "Immersive 360Β° with ambient dreamy layers", | |
| baseCommand: `-af "apulsator=hz=0.08:mode=sine:width=0.9,extrastereo=m=3:c=1,aecho=0.7:0.85:100:0.35,haas=level_in=1:level_out=1.1:side_gain=0.9:middle_source=mid,bass=g=3:f=85,acompressor=threshold=-18dB:ratio=2:attack=10:release=100:makeup=2,highpass=f=60,volume=1.2"`, | |
| instrumentVolume: 0.12, | |
| // 8D is immersive - ambient + romantic | |
| instrumentMoods: ["ambient", "romantic", "sad_romantic"] | |
| }, | |
| bass_boosted: { | |
| name: "Bass Boosted", | |
| description: "Heavy bass with deep atmospheric tones", | |
| baseCommand: `-af "bass=g=18:f=55:width_type=o:width=1.5,bass=g=14:f=95:width_type=h:width=90,equalizer=f=35:t=q:width=1.2:g=12,equalizer=f=75:t=q:width=1.5:g=10,equalizer=f=140:t=q:width=2:g=8,equalizer=f=500:t=q:width=2:g=-3,equalizer=f=4000:t=q:width=4:g=-6,volume='mod(t,31):if(lt(MOD,15),0.4,if(lt(MOD,18),0.05,if(lt(MOD,26),0.5+0.0625*(MOD-18),1.5)))':eval=frame,acompressor=threshold=-22dB:ratio=8:attack=3:release=40:makeup=8,tremolo=f=0.15:d=0.3,highpass=f=28,alimiter=limit=0.95:attack=5:release=50"`, | |
| instrumentVolume: 0.09, | |
| // Bass boosted - deep ambient + chill | |
| instrumentMoods: ["ambient", "chill"] | |
| }, | |
| ambient: { | |
| name: "Ambient Chill", | |
| description: "Ethereal meditation with sad peaceful vibes", | |
| baseCommand: `-af "asetrate=44100*0.94,aresample=44100,aecho=0.6:0.8:200:0.4,aecho=0.5:0.7:400:0.3,chorus=0.6:0.85:50:0.4:0.25:2,aphaser=in_gain=0.5:out_gain=0.8:delay=3:decay=0.4:speed=0.2,equalizer=f=1500:t=q:width=3:g=-2,equalizer=f=6000:t=q:width=4:g=-4,acompressor=threshold=-18dB:ratio=2:attack=15:release=200:makeup=3,highpass=f=50,lowpass=f=9000,volume=1.5"`, | |
| instrumentVolume: 0.15, | |
| // Ambient - all ambient + sad moods | |
| instrumentMoods: ["ambient", "sad", "sad_romantic"] | |
| }, | |
| tiktok_lofi: { | |
| name: "TikTok LoFi", | |
| description: "Viral style with modern romantic chill", | |
| baseCommand: `-af "asetrate=44100*0.88,aresample=44100,bass=g=6:f=90:width_type=h:width=100,treble=g=-4:f=7500,equalizer=f=200:t=q:width=1.8:g=-1.5,equalizer=f=800:t=q:width=2:g=2,equalizer=f=3000:t=q:width=3:g=-2,acompressor=threshold=-20dB:ratio=4:attack=5:release=100:makeup=4,vibrato=f=0.2:d=0.5,chorus=0.5:0.8:45:0.3:0.2:2,highpass=f=70,lowpass=f=5000,volume=1.35"`, | |
| instrumentVolume: 0.13, | |
| // TikTok LoFi - chill + romantic | |
| instrumentMoods: ["chill", "romantic", "sad_romantic"] | |
| }, | |
| chill_lofi: { | |
| name: "Chill LoFi", | |
| description: "Study vibes with jazzy peaceful atmosphere", | |
| baseCommand: `-af "asetrate=44100*0.92,aresample=44100,bass=g=5:f=88:width_type=h:width=90,treble=g=-4:f=7000,equalizer=f=250:t=q:width=2:g=-1.5,equalizer=f=1000:t=q:width=2.5:g=1.2,equalizer=f=3500:t=q:width=3:g=-2.5,acompressor=threshold=-21dB:ratio=3:attack=10:release=140:makeup=2.5,vibrato=f=0.15:d=0.45,chorus=0.4:0.8:45:0.28:0.18:2,aecho=0.4:0.6:150:0.25,highpass=f=70,lowpass=f=5000,volume=1.2"`, | |
| instrumentVolume: 0.12, | |
| // Chill LoFi - pure chill + ambient | |
| instrumentMoods: ["chill", "ambient"] | |
| } | |
| }; | |
| // π² Smart Random Instrument Selector | |
| function getSmartInstruments(effectType, count = 2) { | |
| const effect = EFFECTS[effectType]; | |
| if (!effect || !effect.instrumentMoods) { | |
| return getRandomInstruments(count); | |
| } | |
| // Get instruments that match the effect's moods | |
| const matchingInstruments = Object.keys(INSTRUMENTS).filter(key => | |
| effect.instrumentMoods.includes(INSTRUMENTS[key].mood) | |
| ); | |
| // Shuffle and select | |
| const shuffled = matchingInstruments.sort(() => Math.random() - 0.5); | |
| const selected = []; | |
| for (let i = 0; i < Math.min(count, shuffled.length); i++) { | |
| selected.push(shuffled[i]); | |
| } | |
| return selected; | |
| } | |
| // Fallback random selector | |
| function getRandomInstruments(count = 2) { | |
| const instrumentKeys = Object.keys(INSTRUMENTS); | |
| const shuffled = instrumentKeys.sort(() => Math.random() - 0.5); | |
| return shuffled.slice(0, count); | |
| } | |
| // π₯ Health Check | |
| app.get('/', (req, res) => { | |
| res.json({ | |
| status: 'online', | |
| message: 'π΅ LoFi Remix API v3.1 - Smart Mood-Based Instruments', | |
| version: '3.1', | |
| features: [ | |
| '9 unique effects', | |
| 'Smart mood-based instrument selection', | |
| '20+ instruments across 5 moods', | |
| 'Sad, Romantic, Sad-Romantic, Ambient, Chill, Upbeat' | |
| ], | |
| total_effects: Object.keys(EFFECTS).length, | |
| total_instruments: Object.keys(INSTRUMENTS).length, | |
| instrument_moods: ['sad', 'romantic', 'sad_romantic', 'ambient', 'chill', 'upbeat'] | |
| }); | |
| }); | |
| // π΅ Process Audio | |
| app.post('/process', upload.single('audio'), async (req, res) => { | |
| try { | |
| if (!req.file) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'No audio file uploaded' | |
| }); | |
| } | |
| const effectType = req.body.effect || 'lofi'; | |
| const addInstruments = req.body.instruments !== 'false'; | |
| if (!EFFECTS[effectType]) { | |
| fs.unlinkSync(req.file.path); | |
| return res.status(400).json({ | |
| success: false, | |
| error: `Unknown effect: ${effectType}`, | |
| available_effects: Object.keys(EFFECTS) | |
| }); | |
| } | |
| const inputPath = req.file.path; | |
| const outputFilename = `${effectType}-${Date.now()}.mp3`; | |
| const outputPath = path.join(outputsDir, outputFilename); | |
| const effect = EFFECTS[effectType]; | |
| console.log(`π΅ Processing: ${effect.name}`); | |
| console.log(`π ${effect.description}`); | |
| console.log(`π Moods: ${effect.instrumentMoods?.join(', ') || 'default'}`); | |
| let ffmpegCommand; | |
| let usedInstruments = []; | |
| if (addInstruments) { | |
| // π² Smart selection - 2 instruments matching effect mood | |
| const selectedKeys = getSmartInstruments(effectType, 2); | |
| usedInstruments = selectedKeys.map(key => ({ | |
| id: key, | |
| name: INSTRUMENTS[key].name, | |
| mood: INSTRUMENTS[key].mood | |
| })); | |
| console.log(`πΉ Instruments: ${usedInstruments.map(i => `${i.name} (${i.mood})`).join(', ')}`); | |
| // Generate instruments | |
| const inst1Path = path.join(outputsDir, `inst1-${Date.now()}.wav`); | |
| const inst2Path = path.join(outputsDir, `inst2-${Date.now()}.wav`); | |
| await execPromise(`ffmpeg -f lavfi -i "${INSTRUMENTS[selectedKeys[0]].command}" -t 12 -y "${inst1Path}"`); | |
| await execPromise(`ffmpeg -f lavfi -i "${INSTRUMENTS[selectedKeys[1]].command}" -t 12 -y "${inst2Path}"`); | |
| // Mix with smart volume | |
| const vol = effect.instrumentVolume; | |
| ffmpegCommand = `ffmpeg -i "${inputPath}" -stream_loop -1 -i "${inst1Path}" -stream_loop -1 -i "${inst2Path}" -filter_complex "[1]volume=${vol}[i1];[2]volume=${vol}[i2];[0][i1][i2]amix=inputs=3:duration=first:dropout_transition=2,${effect.baseCommand.replace('-af "', '').replace('"', '')}" -ar 44100 -b:a 192k -y "${outputPath}"`; | |
| // Cleanup | |
| setTimeout(() => { | |
| [inst1Path, inst2Path].forEach(p => { | |
| if (fs.existsSync(p)) fs.unlinkSync(p); | |
| }); | |
| }, 5000); | |
| } else { | |
| ffmpegCommand = `ffmpeg -i "${inputPath}" -threads 0 ${effect.baseCommand} -ar 44100 -b:a 192k -y "${outputPath}"`; | |
| } | |
| console.log(`βοΈ Processing...`); | |
| const startTime = Date.now(); | |
| await execPromise(ffmpegCommand, { | |
| maxBuffer: 50 * 1024 * 1024 | |
| }); | |
| const processingTime = ((Date.now() - startTime) / 1000).toFixed(2); | |
| fs.unlinkSync(inputPath); | |
| const stats = fs.statSync(outputPath); | |
| const fileSizeKB = (stats.size / 1024).toFixed(2); | |
| console.log(`β Done: ${fileSizeKB}KB in ${processingTime}s`); | |
| res.json({ | |
| success: true, | |
| message: `Processed with ${effect.name}`, | |
| downloadUrl: `/download/${outputFilename}`, | |
| filename: outputFilename, | |
| effect: { | |
| id: effectType, | |
| name: effect.name, | |
| description: effect.description, | |
| moods: effect.instrumentMoods || [] | |
| }, | |
| instruments: addInstruments ? usedInstruments : [], | |
| output: { | |
| size_kb: fileSizeKB | |
| }, | |
| processing_time_seconds: processingTime | |
| }); | |
| } catch (error) { | |
| console.error('β Error:', error); | |
| if (req.file && fs.existsSync(req.file.path)) { | |
| fs.unlinkSync(req.file.path); | |
| } | |
| res.status(500).json({ | |
| success: false, | |
| error: 'Processing failed', | |
| details: error.message | |
| }); | |
| } | |
| }); | |
| // π₯ Download | |
| app.get('/download/:filename', (req, res) => { | |
| const filename = req.params.filename; | |
| const filePath = path.join(outputsDir, filename); | |
| if (!fs.existsSync(filePath)) { | |
| return res.status(404).json({ | |
| success: false, | |
| error: 'File not found' | |
| }); | |
| } | |
| console.log(`π₯ Download: ${filename}`); | |
| res.download(filePath, filename, (err) => { | |
| if (!err) { | |
| setTimeout(() => { | |
| if (fs.existsSync(filePath)) { | |
| fs.unlinkSync(filePath); | |
| console.log(`ποΈ Cleaned: ${filename}`); | |
| } | |
| }, 10000); | |
| } | |
| }); | |
| }); | |
| // ποΈ Get Effects Info | |
| app.get('/effects', (req, res) => { | |
| res.json({ | |
| total: Object.keys(EFFECTS).length, | |
| effects: Object.keys(EFFECTS).map(key => ({ | |
| id: key, | |
| name: EFFECTS[key].name, | |
| description: EFFECTS[key].description, | |
| moods: EFFECTS[key].instrumentMoods || [] | |
| })) | |
| }); | |
| }); | |
| // πΉ Get Instruments Info | |
| app.get('/instruments', (req, res) => { | |
| const byMood = {}; | |
| Object.keys(INSTRUMENTS).forEach(key => { | |
| const inst = INSTRUMENTS[key]; | |
| if (!byMood[inst.mood]) { | |
| byMood[inst.mood] = []; | |
| } | |
| byMood[inst.mood].push({ | |
| id: key, | |
| name: inst.name | |
| }); | |
| }); | |
| res.json({ | |
| total: Object.keys(INSTRUMENTS).length, | |
| by_mood: byMood, | |
| all: Object.keys(INSTRUMENTS).map(key => ({ | |
| id: key, | |
| name: INSTRUMENTS[key].name, | |
| mood: INSTRUMENTS[key].mood | |
| })) | |
| }); | |
| }); | |
| // π Start Server | |
| app.listen(PORT, '0.0.0.0', () => { | |
| console.log('βββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| console.log('π LoFi Remix API v3.1 - SMART INSTRUMENTS'); | |
| console.log('βββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| console.log(`π Server: http://0.0.0.0:${PORT}`); | |
| console.log(`β‘ Effects: ${Object.keys(EFFECTS).length}`); | |
| console.log(`πΉ Instruments: ${Object.keys(INSTRUMENTS).length}`); | |
| console.log(`π Moods: Sad, Romantic, Sad-Romantic, Ambient, Chill, Upbeat`); | |
| console.log(`π² Smart mood-based selection!`); | |
| console.log('βββββββββββββββββββββββββββββββββββββββββββββββ'); | |
| }); |