feat: functional download server
using micro
This commit is contained in:
10
src/logger.js
Normal file
10
src/logger.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const pino = require('pino');
|
||||
|
||||
const LOG_LEVEL = process.env.NODE_ENV === 'production' ? 'info' : 'debug';
|
||||
|
||||
const logger = pino({
|
||||
prettyPrint: process.env.NODE_ENV !== 'production',
|
||||
level: LOG_LEVEL,
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
@@ -1,3 +1,94 @@
|
||||
module.exports = function server() {
|
||||
console.log('Server...');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const spawn = require('cross-spawn');
|
||||
const logger = require('./logger');
|
||||
|
||||
const sendError = (res, code) => {
|
||||
res.writeHeader(code);
|
||||
res.write(code === 400 ? 'Bad Request' : 'Internal Server Error');
|
||||
res.end();
|
||||
};
|
||||
|
||||
module.exports = async function server(req, res) {
|
||||
let sent = false;
|
||||
let filename;
|
||||
const { url, format } = req.queryParams;
|
||||
|
||||
logger.debug(`Request: ${req.url}`);
|
||||
|
||||
if (!url) {
|
||||
sendError(res, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const dataPath = path.resolve(__dirname, '../data');
|
||||
|
||||
const params = [url];
|
||||
|
||||
if (format) {
|
||||
params.unshift(format);
|
||||
params.unshift('--format');
|
||||
}
|
||||
|
||||
logger.info(`Downloading ${url}`);
|
||||
const proc = spawn('youtube-dl', params, {
|
||||
stdio: 'pipe',
|
||||
cwd: dataPath,
|
||||
});
|
||||
|
||||
proc.stdout.on('data', data => {
|
||||
const match = data.toString().match(/\[download\] Destination: (.+)/i);
|
||||
const existsMatch = data.toString().match(/\[download\] (.+) has already been downloaded/i);
|
||||
|
||||
if (match !== null) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
filename = match[1];
|
||||
} else if (existsMatch !== null) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
filename = existsMatch[1];
|
||||
}
|
||||
});
|
||||
|
||||
proc.on('error', err => {
|
||||
logger.error(err);
|
||||
logger.info(`child process exited with code ${err.code}`);
|
||||
if (!sent) {
|
||||
sendError(res, 500);
|
||||
sent = true;
|
||||
}
|
||||
});
|
||||
|
||||
proc.on('close', code => {
|
||||
logger.info(`child process exited with code ${code}`);
|
||||
|
||||
if (!filename) {
|
||||
logger.error('Failed to capture download filename');
|
||||
sendError(res, 500);
|
||||
sent = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const dlFilepath = path.join(__dirname, '../data', filename);
|
||||
logger.debug(`Downloaded file is ${dlFilepath}`);
|
||||
|
||||
if (!sent) {
|
||||
fs.readFile(dlFilepath, (err, fileBuffer) => {
|
||||
if (err) {
|
||||
sendError(res, 500);
|
||||
sent = true;
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Length': fileBuffer.length,
|
||||
'Content-disposition': `attachment; filename=${filename}`,
|
||||
});
|
||||
res.write(fileBuffer);
|
||||
res.end();
|
||||
sent = true;
|
||||
});
|
||||
}
|
||||
sent = true;
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user