server/router/track.js

223 lines
6.8 KiB
JavaScript
Raw Normal View History

2023-02-08 12:30:56 +01:00
console.log("router/track INIT");
const express = require("express");
const fs = require("fs");
const meta_data = require("music-metadata");
const database = require("../services/database");
const node_id3 = require("node-id3");
const Lame = require("node-lame").Lame;
const Fdkaac = require("node-fdkaac").Fdkaac;
const notifier = require("../services/notifier");
const server = require("../server");
const passport = server.passport;
const config = server.config;
const checkGuest = server.checkGuest;
const router = new express.Router();
const audio_cache_dir = config.cache_folder + "/audio_cache/";
const resizer = require("../services/cover/resizer");
if (!fs.existsSync(audio_cache_dir)) {
fs.mkdirSync(audio_cache_dir);
}
server.lists.audio_quality.forEach(bmp => {
let dir = audio_cache_dir + bmp;
if (!fs.existsSync(dir)) {
fs.mkdir(dir, () => { });
}
});
router.route("")
.post(passport.authenticate("jwt", { session: false }), (req, res) => {
if (req.files) {
let track = JSON.parse(req.body.track);
let url_user = config.upload_folder + "/" + req.user._id;
let folder = Math.floor(Date.now() / 1000 / 60);
let url_folder = url_user + "/" + folder
let file = url_folder + "/" + req.files.file.name;
req.files.file.mv(file).then(() => {
meta_data
.parseFile(file, { duration: true })
.then(meta => {
let item = {
path: file,
artist: track.artist,
album: track.album,
title: track.title,
duration: meta.format.duration,
bitrate: meta.format.bitrate,
mime: req.files.file.mimetype,
year: track.year,
track: track.track,
disk: meta.common.disk,
genre: track.genre || meta.common.genre,
owner: req.user
};
if (meta.common.picture && meta.common.picture.length > 0) {
resizer.resize_image_for_album(meta.common.picture[0].data, (covers) => {
item.covers = covers;
notifier.emit("metadata_excluded", item);
res.status(200).end();
});
} else {
notifier.emit("metadata_excluded", item);
res.status(200).end();
}
})
.catch(err => {
process.stdout.write(file);
console.log(err);
});
});
} else {
res.end();
}
});
router
.route("/most_listened")
.get(checkGuest, (req, res) => {
let filter = undefined;
if (req.user._id == -1) {
filter = ['global'];
}
database.tracks.mostListened(filter, (result) => {
res.json(result);
});
});
router
.route("/:id/stream")
.get((req, res) => {
database.tracks.byId(req.params.id, result => {
process.stdout.write("audio stream: " + result.title);
if (!fs.existsSync(config.music_folder + result.path)) {
return res.end();
}
stream(req, res, config.music_folder + result.path, result.mime);
});
});
router
.route("/:id/stream/:rate")
.get((req, res) => {
if (!server.lists.audio_quality.includes(req.params.rate)) {
res.end();
}
let music = audio_cache_dir + req.params.rate + "/" + req.params.id + ".mp3";
if (fs.existsSync(music)) {
stream(req, res, music, "audio/mpeg");
} else {
convert(req.params.id, req.params.rate).then((file) => {
if (file) {
stream(req, res, file.path, file.mime);
} else {
res.end();
}
});
}
});
router.route("/:id/convert/:rate")
.put((req, res) => {
let music = audio_cache_dir + req.params.rate + "/" + req.params.id + ".mp3";
if (!fs.existsSync(music)) {
convert(req.params.id, req.params.rate);
}
res.end();
});
function stream(req, res, music, mime) {
process.stdout.write("audio stream: " + music + "\n");
let stat = fs.statSync(music);
if (req.headers.range) {
var range = req.headers.range;
var parts = range.replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
var start = parseInt(partialstart, 10);
var end = partialend ? parseInt(partialend, 10) : stat.size - 1;
var chunksize = end - start + 1;
var readStream = fs.createReadStream(music, { start: start, end: end });
res.writeHead(206, {
"Content-Range": "bytes " + start + "-" + end + "/" + stat.size,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": mime
});
readStream.pipe(res);
} else {
res.writeHead(200, {
"Content-Length": stat.size,
"Content-Type": mime
});
fs.createReadStream(music).pipe(res);
}
}
function convert(id, rate) {
return new Promise((resolve) => {
database.tracks.byId(id, result => {
let full_path = config.music_folder + result.path;
let cache_path = audio_cache_dir + rate + "/" + id + ".mp3";
if (!fs.existsSync(full_path)) {
full_path = config.upload_folder + result.path;
if (!fs.existsSync(full_path)) {
resolve();
}
}
if (result.mime == "audio/mpeg" && result.bitrate / 1000 <= rate) {
process.stdout.write("stream non converted file: " + result.titel + "\n");
resolve({ path: full_path, mime: result.mime });
return;
}
process.stdout.write("audio convert: " + result.title + " (" + result.mime + ")\n");
if (result.mime.includes("mp4")) {
process.stdout.write("audio decode: " + result.title + " (" + result.mime + ")\n");
const decoder = new Fdkaac({ output: "buffer" }).setFile(full_path);
decoder
.decode()
.then(() => {
const buffer = decoder.getBuffer();
const lame_encoder = new Lame({
output: cache_path,
bitrate: rate
}).setBuffer(buffer);
lame_encoder
.encode()
.then(() => {
node_id3.removeTags(cache_path);
resolve({ path: cache_path, mime: "audio/mpeg" });
})
.catch(err => {
process.stdout.write(err);
});
})
.catch(err => {
process.stdout.write(err);
});
} else {
const lame_encoder = new Lame({ output: cache_path, bitrate: rate }).setFile(full_path);
lame_encoder
.encode()
.then(() => {
node_id3.removeTags(cache_path);
resolve({ path: cache_path, mime: "audio/mpeg" });
})
.catch(err => {
process.stdout.write(err);
process.stdout.write("stream original");
resolve({ path: full_path, mime: result.mime });
});
}
});
});
}
module.exports = router;