133 lines
4.7 KiB
JavaScript
133 lines
4.7 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const express_1 = require("express");
|
|
const multer_1 = __importDefault(require("multer"));
|
|
const path_1 = __importDefault(require("path"));
|
|
const fs_1 = __importDefault(require("fs"));
|
|
const ytDlp_1 = require("../services/ytDlp");
|
|
const UPLOADS_DIR = path_1.default.resolve('./uploads');
|
|
const storage = multer_1.default.diskStorage({
|
|
destination: (_req, _file, cb) => {
|
|
if (!fs_1.default.existsSync(UPLOADS_DIR)) {
|
|
fs_1.default.mkdirSync(UPLOADS_DIR, { recursive: true });
|
|
}
|
|
cb(null, UPLOADS_DIR);
|
|
},
|
|
filename: (_req, file, cb) => {
|
|
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
|
|
cb(null, uniqueSuffix + path_1.default.extname(file.originalname));
|
|
}
|
|
});
|
|
const upload = (0, multer_1.default)({
|
|
storage,
|
|
fileFilter: (_req, file, cb) => {
|
|
const allowedMimes = [
|
|
'video/mp4', 'video/webm', 'video/x-matroska', 'video/quicktime',
|
|
'video/x-msvideo', 'video/mpeg', 'video/x-ms-wmv', 'video/x-flv'
|
|
];
|
|
if (allowedMimes.includes(file.mimetype) || file.originalname.match(/\.(mp4|mkv|webm|mov|avi|mpg|mpeg|wmv|flv)$/i)) {
|
|
cb(null, true);
|
|
}
|
|
else {
|
|
cb(new Error('Invalid file type. Only video files are allowed.'));
|
|
}
|
|
},
|
|
limits: { fileSize: 10 * 1024 * 1024 * 1024 } // 10GB
|
|
});
|
|
const router = (0, express_1.Router)();
|
|
router.post('/upload', upload.single('video'), (req, res) => {
|
|
if (!req.file) {
|
|
res.status(400).json({ error: 'No video file uploaded' });
|
|
return;
|
|
}
|
|
res.json({
|
|
filePath: req.file.path,
|
|
filename: req.file.originalname,
|
|
size: req.file.size
|
|
});
|
|
});
|
|
router.get('/', (_req, res) => {
|
|
if (!fs_1.default.existsSync(UPLOADS_DIR)) {
|
|
res.json({ files: [] });
|
|
return;
|
|
}
|
|
const entries = fs_1.default.readdirSync(UPLOADS_DIR, { withFileTypes: true });
|
|
const files = entries
|
|
.filter(e => e.isFile())
|
|
.map(e => ({
|
|
filename: e.name,
|
|
filePath: path_1.default.join(UPLOADS_DIR, e.name),
|
|
size: fs_1.default.statSync(path_1.default.join(UPLOADS_DIR, e.name)).size
|
|
}))
|
|
.sort((a, b) => b.filePath.localeCompare(a.filePath));
|
|
res.json({ files });
|
|
});
|
|
router.delete('/:filename', (req, res) => {
|
|
const raw = req.params.filename;
|
|
const requested = Array.isArray(raw) ? raw[0] : raw;
|
|
if (!requested) {
|
|
res.status(400).json({ error: 'filename is required' });
|
|
return;
|
|
}
|
|
const resolved = path_1.default.resolve(UPLOADS_DIR, requested);
|
|
const uploadsWithSep = UPLOADS_DIR.endsWith(path_1.default.sep) ? UPLOADS_DIR : UPLOADS_DIR + path_1.default.sep;
|
|
if (!resolved.startsWith(uploadsWithSep)) {
|
|
res.status(400).json({ error: 'Invalid filename' });
|
|
return;
|
|
}
|
|
if (!fs_1.default.existsSync(resolved)) {
|
|
res.status(404).json({ error: 'File not found' });
|
|
return;
|
|
}
|
|
try {
|
|
fs_1.default.unlinkSync(resolved);
|
|
res.json({ ok: true });
|
|
}
|
|
catch (err) {
|
|
res.status(500).json({ error: `Failed to delete: ${err.message}` });
|
|
}
|
|
});
|
|
// Stream yt-dlp download progress over SSE.
|
|
// Returns events: {type:'progress', percent} ... {type:'done', filePath, filename, title}
|
|
// or {type:'error', message}
|
|
router.get('/youtube/stream', (req, res) => {
|
|
const url = req.query.url || '';
|
|
if (!url) {
|
|
res.status(400).json({ error: 'url query param is required' });
|
|
return;
|
|
}
|
|
if (!(0, ytDlp_1.isYtDlpAvailable)()) {
|
|
res.status(400).json({ error: 'yt-dlp is not installed or not in PATH' });
|
|
return;
|
|
}
|
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
res.setHeader('Cache-Control', 'no-cache');
|
|
res.setHeader('Connection', 'keep-alive');
|
|
res.setHeader('X-Accel-Buffering', 'no');
|
|
res.flushHeaders?.();
|
|
const send = (data) => {
|
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
};
|
|
let clientGone = false;
|
|
req.on('close', () => { clientGone = true; });
|
|
(0, ytDlp_1.downloadVideo)(url, UPLOADS_DIR, (percent) => {
|
|
if (clientGone)
|
|
return;
|
|
send({ type: 'progress', percent });
|
|
}).then((result) => {
|
|
if (clientGone)
|
|
return;
|
|
send({ type: 'done', ...result });
|
|
res.end();
|
|
}).catch((err) => {
|
|
if (clientGone)
|
|
return;
|
|
send({ type: 'error', message: err.message });
|
|
res.end();
|
|
});
|
|
});
|
|
exports.default = router;
|
|
//# sourceMappingURL=files.js.map
|