imran056's picture
Update server.js
81dffac verified
// ═══════════════════════════════════════════════════════════
// 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('═══════════════════════════════════════════════');
});