var express = require('express'); var fs = require('fs'); var { getAudioDurationInSeconds } = require("get-audio-duration"); var path = require('path'); var urlParse = require("body-parser"); var id3 = require('node-id3'); var ytdl = require("ytdl-core"); var ffmpeg = require("fluent-ffmpeg"); var router = express.Router(); router.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); res.header('Cache-Control', 'no-cache'); next(); }); const rootdir = path.join(__dirname, '/../'); ffmpeg.setFfmpegPath(rootdir + "bin/ffmpeg"); var downloadQueue = []; /* GET home page. */ router.get('/', (_req, res, _next) => { res.render('index', { title: 'Express' }); }); // Download route to download youtube video's router.get('/download/:url', (req, res, _next) => { ytdl.getInfo(req.params.url, (_err, info) => { const index = downloadQueue.push({'progress': 0, 'info': info}) - 1; let stream = ytdl(req.params.url, { quality: "highestaudio" //filter: 'audioonly', }); ffmpeg(stream) .audioBitrate(128) .on("progress", p => { if (downloadQueue[index]) downloadQueue[index].progress = p; }) .save(`${rootdir}music/${info.title}.mp3`) .on("end", () => { downloadQueue.splice(index, 1); res.json({ result: info }); }); }); }); router.get('/info/:id', (req, res, _next) => { ytdl.getBasicInfo(req.params.id, (_err, info) => { res.json({ result: info }); }) }); router.get('/downloadqueue', (req, res, _next) => { res.json({ result: downloadQueue }); }) function dbGet(type, id) { JSON.parse(fs.readFileSync(rootdir + "database/albums.json").toString()); let db = { song: JSON.parse(fs.readFileSync(rootdir + "database/songs.json").toString()), album: JSON.parse(fs.readFileSync(rootdir + "database/albums.json").toString()), artist: JSON.parse(fs.readFileSync(rootdir + "database/artists.json").toString()) }; let result; if (id === 'all') { result = db[type]; result.forEach(element => { element.type = type; }); } else { id = parseInt(id); db[type].forEach(element => { if (id == element.id) { result = element; result.type = type; } }); } return result; } router.get('/get/:type/:id', (req, res, _next) => { // Get variables from url const type = req.params.type; const id = req.params.id; // Check if all info is requested if (id === 'all') { res.json({ result: dbGet(type, id) }); } else { // Split arguments and reset result const arg = id.split(","); resultArray = []; // For each array in argument, make object in resultArray for (let index = 0; index < arg.length; index++) { resultArray[index] = dbGet(type, arg[index]); } // Print result res.json({ result: resultArray }); } }); // Play route to stream track router.get('/play/:track', (req, res, _next) => { var key = req.params.track; var music = rootdir + "music/" + key; var stat = fs.statSync(music); range = req.headers.range; var readStream; if (range !== undefined) { var parts = range.replace(/bytes=/, "").split("-"); var partial_start = parts[0]; var partial_end = parts[1]; if ( (isNaN(partial_start) && partial_start.length > 1) || (isNaN(partial_end) && partial_end.length > 1) ) { return res.sendStatus(500); //ERR_INCOMPLETE_CHUNKED_ENCODING } var start = parseInt(partial_start, 10); var end = partial_end ? parseInt(partial_end, 10) : stat.size - 1; var content_length = end - start + 1; res.status(206).header({ "Content-Type": "audio/mpeg", "Content-Length": content_length, "Content-Range": "bytes " + start + "-" + end + "/" + stat.size }); readStream = fs.createReadStream(music, { start: start, end: end }); } else { res.header({ "Content-Type": "audio/mpeg", "Content-Length": stat.size }); readStream = fs.createReadStream(music); } readStream.pipe(res); }); // Image route to get album image router.get('/image/:path', (req, res, _next) => { const path = '/music/' + req.params.path res.sendFile(path, { root: rootdir }); }); // Search route to search for a query in the db router.get('/search/:query', (req, res, _next) => { const query = req.params.query; let result = []; fs.readdir(rootdir + 'database/', (err, files) => { files.forEach(file => { if (file !== '.gitkeep') { const content = require(rootdir + "database/" + file); console.log(rootdir + "database/" + file); content.forEach(element => { if (element.name.match(query)) { element.type = (file === 'songs.json' ? 'song' : (file === 'albums.json' ? 'album' : 'artist')); result.push(element); } }); } }); res.json({result: result}); }); }); router.use(urlParse.urlencoded({extended : true})); // Analyse route to scan .mp3 ID3 tags router.get('/analyse/*', (req, res, _next) => { let file = req.params[0]; if (fs.existsSync(file)) { var result = id3.read(file); if (typeof result.image.imageBuffer !== 'undefined') { result.image.base64 = result.image.imageBuffer.toString('base64'); } } else { var result = `File ${file} not found`; } res.json({ result: result }); }); router.get('/duration/:file', (req, res, _next) => { getAudioDurationInSeconds(__dirname + '/../music/' + req.params.file).then(duration => res.json({result: duration})); }) router.get('/update/:database/:index/:field/:value', (req, res, _next) => { const path = rootdir + "database/" + req.params.database + ".json"; const db = require(path); db[req.params.index][req.params.field] = req.params.value; fs.writeFileSync(path, JSON.stringify(db)); res.json({ result: true }); }); router.get('/insert/:database/', (req, res, _next) => { const path = rootdir + "database/" + req.params.database + ".json"; const db = JSON.parse(fs.readFileSync(path).toString()); const index = db.push({}) - 1; db[index].id = index; fs.writeFileSync(path, JSON.stringify(db)); res.json({ result: db[index] }); }); module.exports = router;