sound-parade/server.js

423 lines
11 KiB
JavaScript

//dotenv
require('dotenv').config();
//ffmpeg
var ffmpeg = require('fluent-ffmpeg');
//nextcloud client
const { Client, Server, GetFilesRecursivelyCommand, CommandStatus } = require("nextcloud-node-client");
const server = new Server({
basicAuth: {
password: process.env.nextcloud_PASSWD,
username: process.env.nextcloud_ID,
},
url: process.env.nextcloud_URL,
});
//built-in
const path = require("path");
const fs = require('fs').promises;
const util = require('util');
// const { pipeline } = require('stream');
// const pump = util.promisify(pipeline);
//uuid
const {
v1: uuidv1,
v4: uuidv4,
} = require('uuid');
//moment
const moment = require("moment-timezone");
//fastify
const fastify = require("fastify")({
logger: false,
});
fastify.register(require("fastify-static"), {
root: path.join(__dirname, "public"),
prefix: "/"
});
fastify.register(require("fastify-formbody"));
fastify.register(require("fastify-multipart"));
fastify.register(require("point-of-view"), {
engine: {
handlebars: require("handlebars")
}
});
//socket.io
var io = require("socket.io")(fastify.server, {
pingInterval: 1000,
pingTimeout: 3000
});
//get '/'
fastify.get("/", async function (request, reply) {
//console.log(request.url);
let url = request.url.replace(/\/$/, '');
//get list
let list = await fs.readdir('/media/storage/public/sound-parade/');
list.reverse();
// console.log(list);
let folders = [];
for (const item of list) {
let json = await fs.readFile('/media/storage/public/sound-parade/' + item + '/fields.json')
.catch((err) => {
console.error(err);
});
if (json != undefined) {
var fields = JSON.parse(json.toString('utf8'));
folders.push({
foldername: item,
group: fields.group,
title: fields.title,
comment: fields.comment,
});
}
}
// console.log(folders);
//
reply.view("/src/pages/parade.html", {
list: folders,
});
});
//get '/live
fastify.get("/live", function (request, reply) {
reply.view("/src/pages/live.html", {});
});
//get '/preview/:foldername' --> request.params.foldername
fastify.get("/preview/:foldername", function (request, reply) {
reply.view("/src/pages/preview.html", {});
});
//get '/entry', '/entry/', '/en/entry', '/en/entry/', '/entry/test'
["/entry", "/entry/", "/en/entry", "/en/entry/", "/entry/test"].forEach(function(item) {
fastify.get(item, async function (request, reply) {
//console.log(request.url);
let url = request.url.replace(/\/$/, '');
//get list
let list = await fs.readdir('/media/storage/public/sound-parade/');
list.reverse();
// console.log(list);
let folders = [];
for (const item of list) {
let json = await fs.readFile('/media/storage/public/sound-parade/' + item + '/fields.json')
.catch((err) => {
console.error(err);
});
if (json != undefined) {
var fields = JSON.parse(json.toString('utf8'));
folders.push({
foldername: item,
group: fields.group,
title: fields.title,
comment: fields.comment,
});
}
}
// console.log(folders);
//
if (url == "/entry") {
reply.view("/src/pages/entry.html", {
list: folders,
});
} else if (url == "/en/entry") {
reply.view("/src/pages/entry.en.html", {
list: folders,
});
} else if (url == "/entry/test") {
reply.view("/src/pages/entry.test.html", {
list: folders,
});
}
});
});
// --> https://stackoverflow.com/a/40899275
// all the regex didn't work for me -- a 'last resort' method
//get '/entries'
fastify.get("/entries", async function (request, reply) {
//get list
let list = await fs.readdir('/media/storage/public/sound-parade/');
reply.send(list);
});
//get '/fields'
fastify.get("/fields", async function (request, reply) {
//get list
let list = await fs.readdir('/media/storage/public/sound-parade/');
//list.reverse();
// console.log(list);
let folders = [];
for (const item of list) {
let json = await fs.readFile('/media/storage/public/sound-parade/' + item + '/fields.json')
.catch((err) => {
console.error(err);
});
if (json != undefined) {
var fields = JSON.parse(json.toString('utf8'));
folders.push({
foldername: item,
group: fields.group,
title: fields.title,
comment: fields.comment,
});
}
}
reply.send(folders);
});
//get '/delete'
fastify.get("/delete/:foldername/:pass", async function (request, reply) {
//get pw
var fields = JSON.parse((await fs.readFile('/media/storage/public/sound-parade/' + request.params.foldername + '/fields.json')).toString('utf8'));
var res = false;
if (fields.pass == request.params.pass) {
// console.log('good pass');
//ok. let's move it to trashbin. (mv ../sound-parade.trash)
// await fs.rename('/media/storage/public/sound-parade/' + request.params.foldername, '/media/storage/public/sound-parade.trash/' + request.params.foldername);
const client = new Client(server);
const folder = await client.getFolder("/Storage/public/sound-parade/" + request.params.foldername);
await folder.move("/Storage/public/sound-parade.trash/" + request.params.foldername);
//
res = true;
} else {
// console.log('wrong pass');
}
reply.send({ result: res });
});
//post on '/entry'
fastify.post("/entry", async function (request, reply) {
// stores files to tmp dir and return paths
const files = await request.saveRequestFiles().catch(err => {
console.error(err);
});
let audiofile = files.find(f => f.fieldname == 'audiofile');
let pixelfile = files.find(f => f.fieldname == 'pixels');
let tmpdir = path.dirname(audiofile.filepath);
// console.log(audiofile.fields.message.value);
console.log("-- hi."); // got all files.
//conversion needed?
var conversion = false;
if (path.extname(audiofile.filename) !== ".mp3") {
conversion = true;
console.log("-- well.."); //conversion. is scheduled.
} else {
console.log("-- good"); //no conversion_
}
//upload
const client = new Client(server);
console.log("-- ready"); //file server opened
//create unique folder ==> timestamp + uuid
const folder = await client.createFolder("Storage/public/sound-parade/" + moment().tz('Asia/Seoul').format('YYYYMMDD-HHmmss-') + uuidv1());
//
const json = await folder.createFile("fields.json", Buffer.from(JSON.stringify({
group: audiofile.fields.group.value,
title: audiofile.fields.title.value,
comment: audiofile.fields.comment.value,
pass: audiofile.fields.pass.value
})));
const image = await folder.createFile("pixels.png", await fs.readFile(pixelfile.filepath));
//
if (conversion) {
console.log('---- hi conv');
//save original file as is. + we have scheduled a conversion.
let afile = await fs.readFile(audiofile.filepath)
.catch(err => {
console.error(err);
});
//
if (afile != undefined) {
const file = await folder.createFile(audiofile.filename, afile)
.catch(err => {
console.error(err);
console.log('---- createFile err.');
console.log('afile', afile);
});
} else {
console.log('afile == undef!');
}
console.log('---- yes conv');
//
} else {
console.log('---- no conv');
//rename & save original file.
const file = await folder.createFile("audio.mp3", await fs.readFile(audiofile.filepath).catch(err => console.error(err)));
}
console.log("-- saved"); //saved in file server.
//conversion needed?
var converted = false;
if (conversion) {
console.log("-- mp3..."); //converting to mp3...
function converter() {
return new Promise((resolve, reject) => {
//
let outputFile = tmpdir + "/converted.mp3";
// --> https://stackoverflow.com/a/36109219 (a quick tip on fluent-ffmpeg)
ffmpeg()
.addInput(audiofile.filepath)
.on("error", function(err) {
console.log("-- err:", err);
reject(err);
})
.on("end", function() {
console.log("-- fine."); //conversion succeesful
converted = true;
resolve(outputFile);
})
.outputOptions('-b:a 192000')
.output(outputFile)
.run();
});
}
await converter().catch((err) => { console.error(err); });
if (converted) {
const file = await folder.createFile("audio.mp3", await fs.readFile(tmpdir + '/converted.mp3'));
console.log("-- done");
}
}
console.log("-- well done");
reply.send('done!');
//reply.redirect('/submit');
});
//socket.io
//var score = require("./public/score.json");
//
//there will be 16 rooms called: "room0", "room1", ... , "room15"
//if any other room is requested.. well, we will simply reject.
var roommax = 16;
//
io.on("connection", function(socket) {
console.log("someone connected.");
socket.on("disconnect", function() { console.log("someone disconnected."); });
socket.on("room", function(room, fn) {
// parseInt(room)
if (room >= 0 && room < roommax) {
socket.join("room" + room);
fn(true);
} else {
fn(false);
}
});
socket.on("flow", function(req) {
io.emit("flow", req);
});
socket.on("info", async (fn) => {
//get list
let list = await fs.readdir('/media/storage/public/sound-parade/');
//list.reverse();
// console.log(list);
let folders = [];
for (const item of list) {
var fields = JSON.parse((await fs.readFile('/media/storage/public/sound-parade/' + item + '/fields.json')).toString('utf8'));
folders.push({
foldername: item,
group: fields.group,
title: fields.title,
comment: fields.comment,
});
}
fn(folders);
});
});
//
var pointer = 0; // pointer : 0 ~ (length-1)
var looper;
(looper = function(timeout) {
setTimeout(async function() {
//pointer = 20;
// console.log(score[pointer]);
//
for (var index = 0; index < roommax; index++) {
// NOTE: 'pointer' must be 'remembered' since 'pointer' will increase almost immediately! pass as argument => 'pointed'
// NOTE: 'index' is same => 'indexed'
setTimeout(function(pointed, indexed) {
// io.to("room" + indexed).emit("post", score[pointed]);
io.to("room" + indexed).emit("post", pointed);
// }, score[pointer].object.showtime * index, pointer, index);
}, 30000 * index, pointer, index);
}
//var timegap = 10000 + Math.random()*(40000);
var timegap = 1000 + Math.random()*(40000);
console.log(timegap);
//get # list
let list = await fs.readdir('/media/storage/public/sound-parade/');
console.log(list.length);
//loop over...
pointer++;
if (pointer >= list.length) pointer = 0;
looper(timegap);
}, timeout);
})(1000);
//listen
fastify.listen(10000, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
console.log(`Your app is listening on ${address}`)
fastify.log.info(`server listening on ${address}`)
});