Compare commits
2 Commits
ChatBox
...
feat/new_r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afeedf27a5 | ||
|
|
80c929436c |
15
logtrans.txt
15
logtrans.txt
@@ -590,3 +590,18 @@ Gio 18/12 ORE 16:52: [<b>Circuito RIS Venezia</b>]: Inviate Monete da surya1977
|
|||||||
Saldi:
|
Saldi:
|
||||||
surya1977: -27.50 RIS]
|
surya1977: -27.50 RIS]
|
||||||
amandadi: 105.50 RIS]
|
amandadi: 105.50 RIS]
|
||||||
|
Gio 18/12 ORE 18:30: [<b>Circuito RIS Bologna</b>]: Inviate Monete da surya1977 a ElenaEspx 1 RIS [causale: prova 1]
|
||||||
|
|
||||||
|
Saldi:
|
||||||
|
surya1977: 34.90 RIS]
|
||||||
|
ElenaEspx: 39.05 RIS]
|
||||||
|
Gio 18/12 ORE 18:42: [<b>Circuito RIS Pordenone</b>]: Inviate Monete da surya1977 a GruppoYurta 3 RIS [causale: ECCOLO]
|
||||||
|
|
||||||
|
Saldi:
|
||||||
|
surya1977: -3.00 RIS]
|
||||||
|
GruppoYurta: -1.00 RIS]
|
||||||
|
Gio 18/12 ORE 18:53: [<b>Circuito RIS Venezia</b>]: Inviate Monete da surya1977 a amandadi 50 RIS [causale: asdasdasda]
|
||||||
|
|
||||||
|
Saldi:
|
||||||
|
surya1977: -77.50 RIS]
|
||||||
|
amandadi: 155.50 RIS]
|
||||||
588
src/controllers/VideoController.js
Normal file
588
src/controllers/VideoController.js
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
|
||||||
|
class VideoController {
|
||||||
|
constructor(baseUploadPath = 'uploads/videos') {
|
||||||
|
this.basePath = path.resolve(baseUploadPath);
|
||||||
|
this._ensureDirectory(this.basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ PRIVATE METHODS ============
|
||||||
|
|
||||||
|
_ensureDirectory(dir) {
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_isVideoFile(filename) {
|
||||||
|
return /\.(mp4|webm|ogg|mov|avi|mkv)$/i.test(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getFileInfo(filePath, relativePath = '') {
|
||||||
|
const stat = fs.statSync(filePath);
|
||||||
|
const filename = path.basename(filePath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: uuidv4(),
|
||||||
|
filename,
|
||||||
|
folder: relativePath,
|
||||||
|
path: `/videos/${relativePath ? relativePath + '/' : ''}${filename}`,
|
||||||
|
size: stat.size,
|
||||||
|
createdAt: stat.birthtime.toISOString(),
|
||||||
|
modifiedAt: stat.mtime.toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_scanFolders(dir, relativePath = '') {
|
||||||
|
const folders = [];
|
||||||
|
|
||||||
|
if (!fs.existsSync(dir)) return folders;
|
||||||
|
|
||||||
|
const items = fs.readdirSync(dir);
|
||||||
|
|
||||||
|
items.forEach((item) => {
|
||||||
|
const fullPath = path.join(dir, item);
|
||||||
|
const relPath = relativePath ? `${relativePath}/${item}` : item;
|
||||||
|
|
||||||
|
if (fs.statSync(fullPath).isDirectory()) {
|
||||||
|
folders.push({
|
||||||
|
name: item,
|
||||||
|
path: relPath,
|
||||||
|
level: relPath.split('/').length,
|
||||||
|
});
|
||||||
|
// Ricorsione per sottocartelle
|
||||||
|
folders.push(...this._scanFolders(fullPath, relPath));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ FOLDER METHODS ============
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ottiene tutte le cartelle
|
||||||
|
*/
|
||||||
|
getFolders = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const folders = this._scanFolders(this.basePath);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { folders },
|
||||||
|
message: 'Cartelle recuperate con successo',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crea una nuova cartella
|
||||||
|
*/
|
||||||
|
createFolder = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folderName, parentPath = '' } = req.body;
|
||||||
|
|
||||||
|
if (!folderName || !folderName.trim()) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Nome cartella richiesto',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitizza il nome cartella
|
||||||
|
const sanitizedName = folderName.replace(/[<>:"/\\|?*]/g, '_').trim();
|
||||||
|
|
||||||
|
const basePath = parentPath ? path.join(this.basePath, parentPath) : this.basePath;
|
||||||
|
|
||||||
|
const newFolderPath = path.join(basePath, sanitizedName);
|
||||||
|
|
||||||
|
if (fs.existsSync(newFolderPath)) {
|
||||||
|
return res.status(409).json({
|
||||||
|
success: false,
|
||||||
|
error: 'La cartella esiste già',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.mkdirSync(newFolderPath, { recursive: true });
|
||||||
|
|
||||||
|
const folderData = {
|
||||||
|
name: sanitizedName,
|
||||||
|
path: parentPath ? `${parentPath}/${sanitizedName}` : sanitizedName,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
data: { folder: folderData },
|
||||||
|
message: 'Cartella creata con successo',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rinomina una cartella
|
||||||
|
*/
|
||||||
|
renameFolder = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folderPath } = req.params;
|
||||||
|
const { newName } = req.body;
|
||||||
|
|
||||||
|
if (!newName || !newName.trim()) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Nuovo nome richiesto',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitizedName = newName.replace(/[<>:"/\\|?*]/g, '_').trim();
|
||||||
|
const oldPath = path.join(this.basePath, folderPath);
|
||||||
|
const parentDir = path.dirname(oldPath);
|
||||||
|
const newPath = path.join(parentDir, sanitizedName);
|
||||||
|
|
||||||
|
if (!fs.existsSync(oldPath)) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Cartella non trovata',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(newPath)) {
|
||||||
|
return res.status(409).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Una cartella con questo nome esiste già',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.renameSync(oldPath, newPath);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Cartella rinominata con successo',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Elimina una cartella
|
||||||
|
*/
|
||||||
|
deleteFolder = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folderPath } = req.params;
|
||||||
|
const fullPath = path.join(this.basePath, folderPath);
|
||||||
|
|
||||||
|
if (!fs.existsSync(fullPath)) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Cartella non trovata',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica che sia una directory
|
||||||
|
if (!fs.statSync(fullPath).isDirectory()) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Il percorso non è una cartella',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.rmSync(fullPath, { recursive: true, force: true });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Cartella eliminata con successo',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============ VIDEO METHODS ============
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ottiene i video di una cartella
|
||||||
|
*/
|
||||||
|
getVideos = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const folder = req.query.folder || '';
|
||||||
|
const targetPath = folder ? path.join(this.basePath, folder) : this.basePath;
|
||||||
|
|
||||||
|
if (!fs.existsSync(targetPath)) {
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
videos: [],
|
||||||
|
folders: [],
|
||||||
|
currentPath: folder,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = fs.readdirSync(targetPath);
|
||||||
|
const videos = [];
|
||||||
|
const subfolders = [];
|
||||||
|
|
||||||
|
items.forEach((item) => {
|
||||||
|
const itemPath = path.join(targetPath, item);
|
||||||
|
const stat = fs.statSync(itemPath);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
subfolders.push({
|
||||||
|
name: item,
|
||||||
|
path: folder ? `${folder}/${item}` : item,
|
||||||
|
});
|
||||||
|
} else if (stat.isFile() && this._isVideoFile(item)) {
|
||||||
|
videos.push(this._getFileInfo(itemPath, folder));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ordina per data di creazione (più recenti prima)
|
||||||
|
videos.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
videos,
|
||||||
|
folders: subfolders,
|
||||||
|
currentPath: folder,
|
||||||
|
totalVideos: videos.length,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload singolo video
|
||||||
|
*/
|
||||||
|
uploadVideo = async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!req.file) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Nessun file caricato',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Legge da query parameter
|
||||||
|
const folder = req.query.folder || 'default';
|
||||||
|
|
||||||
|
const videoInfo = {
|
||||||
|
id: uuidv4(),
|
||||||
|
originalName: req.file.originalname,
|
||||||
|
filename: req.file.filename,
|
||||||
|
folder: folder,
|
||||||
|
path: `/videos/${folder}/${req.file.filename}`,
|
||||||
|
size: req.file.size,
|
||||||
|
mimetype: req.file.mimetype,
|
||||||
|
uploadedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
data: { video: videoInfo },
|
||||||
|
message: 'Video caricato con successo',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload multiplo video
|
||||||
|
*/
|
||||||
|
uploadVideos = async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!req.files || req.files.length === 0) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Nessun file caricato',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Legge da query parameter
|
||||||
|
const folder = req.query.folder || 'default';
|
||||||
|
|
||||||
|
const videos = req.files.map((file) => ({
|
||||||
|
id: uuidv4(),
|
||||||
|
originalName: file.originalname,
|
||||||
|
filename: file.filename,
|
||||||
|
folder: folder,
|
||||||
|
path: `/videos/${folder}/${file.filename}`,
|
||||||
|
size: file.size,
|
||||||
|
mimetype: file.mimetype,
|
||||||
|
uploadedAt: new Date().toISOString(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
data: { videos },
|
||||||
|
message: `${videos.length} video caricati con successo`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ottiene info di un singolo video
|
||||||
|
*/
|
||||||
|
getVideo = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folder, filename } = req.params;
|
||||||
|
const videoPath = path.join(this.basePath, folder, filename);
|
||||||
|
|
||||||
|
if (!fs.existsSync(videoPath)) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Video non trovato',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoInfo = this._getFileInfo(videoPath, folder);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { video: videoInfo },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rinomina un video
|
||||||
|
*/
|
||||||
|
renameVideo = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folder, filename } = req.params;
|
||||||
|
const { newFilename } = req.body;
|
||||||
|
|
||||||
|
if (!newFilename || !newFilename.trim()) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Nuovo nome file richiesto',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldPath = path.join(this.basePath, folder, filename);
|
||||||
|
const newPath = path.join(this.basePath, folder, newFilename);
|
||||||
|
|
||||||
|
if (!fs.existsSync(oldPath)) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Video non trovato',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(newPath)) {
|
||||||
|
return res.status(409).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Un file con questo nome esiste già',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.renameSync(oldPath, newPath);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Video rinominato con successo',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sposta un video in un'altra cartella
|
||||||
|
*/
|
||||||
|
moveVideo = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folder, filename } = req.params;
|
||||||
|
const { destinationFolder } = req.body;
|
||||||
|
|
||||||
|
const sourcePath = path.join(this.basePath, folder, filename);
|
||||||
|
const destDir = path.join(this.basePath, destinationFolder);
|
||||||
|
const destPath = path.join(destDir, filename);
|
||||||
|
|
||||||
|
if (!fs.existsSync(sourcePath)) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Video non trovato',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ensureDirectory(destDir);
|
||||||
|
|
||||||
|
if (fs.existsSync(destPath)) {
|
||||||
|
return res.status(409).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Un file con questo nome esiste già nella destinazione',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.renameSync(sourcePath, destPath);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Video spostato con successo',
|
||||||
|
data: {
|
||||||
|
newPath: `/videos/${destinationFolder}/${filename}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Elimina un video
|
||||||
|
*/
|
||||||
|
deleteVideo = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folder, filename } = req.params;
|
||||||
|
const videoPath = path.join(this.basePath, folder, filename);
|
||||||
|
|
||||||
|
if (!fs.existsSync(videoPath)) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Video non trovato',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.unlinkSync(videoPath);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Video eliminato con successo',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream video (per player)
|
||||||
|
*/
|
||||||
|
streamVideo = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { folder, filename } = req.params;
|
||||||
|
const videoPath = path.join(this.basePath, folder, filename);
|
||||||
|
|
||||||
|
if (!fs.existsSync(videoPath)) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Video non trovato',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const stat = fs.statSync(videoPath);
|
||||||
|
const fileSize = stat.size;
|
||||||
|
const range = req.headers.range;
|
||||||
|
|
||||||
|
if (range) {
|
||||||
|
const parts = range.replace(/bytes=/, '').split('-');
|
||||||
|
const start = parseInt(parts[0], 10);
|
||||||
|
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
|
||||||
|
const chunkSize = end - start + 1;
|
||||||
|
|
||||||
|
const file = fs.createReadStream(videoPath, { start, end });
|
||||||
|
const headers = {
|
||||||
|
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
|
||||||
|
'Accept-Ranges': 'bytes',
|
||||||
|
'Content-Length': chunkSize,
|
||||||
|
'Content-Type': 'video/mp4',
|
||||||
|
};
|
||||||
|
|
||||||
|
res.writeHead(206, headers);
|
||||||
|
file.pipe(res);
|
||||||
|
} else {
|
||||||
|
const headers = {
|
||||||
|
'Content-Length': fileSize,
|
||||||
|
'Content-Type': 'video/mp4',
|
||||||
|
};
|
||||||
|
|
||||||
|
res.writeHead(200, headers);
|
||||||
|
fs.createReadStream(videoPath).pipe(res);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============ ERROR HANDLER MIDDLEWARE ============
|
||||||
|
|
||||||
|
static errorHandler = (error, req, res, next) => {
|
||||||
|
if (error.code === 'LIMIT_FILE_SIZE') {
|
||||||
|
return res.status(413).json({
|
||||||
|
success: false,
|
||||||
|
error: 'File troppo grande. Dimensione massima: 500MB',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.code === 'LIMIT_FILE_COUNT') {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Troppi file. Massimo 10 file per upload',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.message.includes('Tipo file non supportato')) {
|
||||||
|
return res.status(415).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message || 'Errore interno del server',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = VideoController;
|
||||||
81
src/middleware/uploadMiddleware.js
Normal file
81
src/middleware/uploadMiddleware.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
const multer = require('multer');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
|
||||||
|
class UploadMiddleware {
|
||||||
|
constructor(baseUploadPath = 'uploads/videos') {
|
||||||
|
this.baseUploadPath = path.resolve(baseUploadPath);
|
||||||
|
this._ensureDirectory(this.baseUploadPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ensureDirectory(dir) {
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_createStorage() {
|
||||||
|
return multer.diskStorage({
|
||||||
|
destination: (req, file, cb) => {
|
||||||
|
// ✅ Legge SOLO da req.query (affidabile con multer)
|
||||||
|
const folder = req.query.folder || 'default';
|
||||||
|
|
||||||
|
console.log('📁 Upload folder:', folder); // Debug
|
||||||
|
|
||||||
|
const uploadPath = path.join(this.baseUploadPath, folder);
|
||||||
|
this._ensureDirectory(uploadPath);
|
||||||
|
cb(null, uploadPath);
|
||||||
|
},
|
||||||
|
filename: (req, file, cb) => {
|
||||||
|
const ext = path.extname(file.originalname);
|
||||||
|
const uniqueName = `${uuidv4()}-${Date.now()}${ext}`;
|
||||||
|
cb(null, uniqueName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileFilter(req, file, cb) {
|
||||||
|
const allowedMimes = [
|
||||||
|
'video/mp4',
|
||||||
|
'video/webm',
|
||||||
|
'video/ogg',
|
||||||
|
'video/quicktime',
|
||||||
|
'video/x-msvideo',
|
||||||
|
'video/x-matroska'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (allowedMimes.includes(file.mimetype)) {
|
||||||
|
cb(null, true);
|
||||||
|
} else {
|
||||||
|
cb(new Error(`Tipo file non supportato: ${file.mimetype}`), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getUploader(options = {}) {
|
||||||
|
const config = {
|
||||||
|
storage: this._createStorage(),
|
||||||
|
fileFilter: this._fileFilter.bind(this),
|
||||||
|
limits: {
|
||||||
|
fileSize: options.maxSize || 500 * 1024 * 1024,
|
||||||
|
files: options.maxFiles || 10
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return multer(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
single(fieldName = 'video') {
|
||||||
|
return this.getUploader().single(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
multiple(fieldName = 'videos', maxCount = 10) {
|
||||||
|
return this.getUploader({ maxFiles: maxCount }).array(fieldName, maxCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBasePath() {
|
||||||
|
return this.baseUploadPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UploadMiddleware;
|
||||||
@@ -33,6 +33,13 @@ const { MyElem } = require('../models/myelem');
|
|||||||
|
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
||||||
|
// Importa le routes video
|
||||||
|
const videoRoutes = require('../routes/videoRoutes');
|
||||||
|
|
||||||
|
// Monta le routes video
|
||||||
|
router.use('/video', videoRoutes);
|
||||||
|
|
||||||
|
|
||||||
router.use('/templates', authenticate, templatesRouter);
|
router.use('/templates', authenticate, templatesRouter);
|
||||||
router.use('/posters', authenticate, postersRouter);
|
router.use('/posters', authenticate, postersRouter);
|
||||||
router.use('/assets', authenticate, assetsRouter);
|
router.use('/assets', authenticate, assetsRouter);
|
||||||
|
|||||||
@@ -660,6 +660,7 @@ router.post('/notifs', authenticate, async (req, res) => {
|
|||||||
router.post('/newtok', async (req, res) => {
|
router.post('/newtok', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const refreshToken = req.body.refreshToken;
|
const refreshToken = req.body.refreshToken;
|
||||||
|
const browser_random = req.body.br;
|
||||||
|
|
||||||
// return res.status(403).send({ error: 'Refresh token non valido' });
|
// return res.status(403).send({ error: 'Refresh token non valido' });
|
||||||
|
|
||||||
|
|||||||
58
src/routes/videoRoutes.js
Normal file
58
src/routes/videoRoutes.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const VideoController = require('../controllers/VideoController');
|
||||||
|
const UploadMiddleware = require('../middleware/uploadMiddleware');
|
||||||
|
|
||||||
|
const {
|
||||||
|
authenticate,
|
||||||
|
authenticate_noerror,
|
||||||
|
authenticate_noerror_WithUser,
|
||||||
|
authenticate_noerror_WithUserLean,
|
||||||
|
} = require('../middleware/authenticate');
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// Configurazione
|
||||||
|
const UPLOAD_PATH = process.env.VIDEO_UPLOAD_PATH || 'uploads/videos';
|
||||||
|
|
||||||
|
// Istanze
|
||||||
|
const videoController = new VideoController(UPLOAD_PATH);
|
||||||
|
const uploadMiddleware = new UploadMiddleware(UPLOAD_PATH);
|
||||||
|
|
||||||
|
// ============ FOLDER ROUTES ============
|
||||||
|
router.get('/folders', authenticate, videoController.getFolders);
|
||||||
|
router.post('/folders', authenticate, videoController.createFolder);
|
||||||
|
router.put('/folders/:folderPath(*)', authenticate, videoController.renameFolder);
|
||||||
|
router.delete('/folders/:folderPath(*)', authenticate, videoController.deleteFolder);
|
||||||
|
|
||||||
|
// ============ VIDEO ROUTES ============
|
||||||
|
router.get('/videos', authenticate, videoController.getVideos);
|
||||||
|
router.get('/videos/:folder/:filename', authenticate, videoController.getVideo);
|
||||||
|
|
||||||
|
// Upload
|
||||||
|
router.post(
|
||||||
|
'/videos/upload',
|
||||||
|
uploadMiddleware.single('video'),
|
||||||
|
videoController.uploadVideo
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
'/videos/upload-multiple',
|
||||||
|
uploadMiddleware.multiple('videos', 10),
|
||||||
|
videoController.uploadVideos
|
||||||
|
);
|
||||||
|
|
||||||
|
// Modifica
|
||||||
|
router.put('/videos/:folder/:filename/rename', authenticate, videoController.renameVideo);
|
||||||
|
router.put('/videos/:folder/:filename/move', authenticate, videoController.moveVideo);
|
||||||
|
|
||||||
|
// Elimina
|
||||||
|
router.delete('/videos/:folder/:filename', authenticate, videoController.deleteVideo);
|
||||||
|
|
||||||
|
// Stream
|
||||||
|
router.get('/stream/:folder/:filename', authenticate, videoController.streamVideo);
|
||||||
|
|
||||||
|
// Error Handler
|
||||||
|
router.use(VideoController.errorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -150,6 +150,8 @@ connectToDatabase(connectionUrl, options)
|
|||||||
|
|
||||||
const { MyEvent } = require('./models/myevent');
|
const { MyEvent } = require('./models/myevent');
|
||||||
|
|
||||||
|
app.use('/videos', express.static(path.join(__dirname, 'uploads/videos')));
|
||||||
|
|
||||||
app.use(bodyParser.json({ limit: '50mb' }));
|
app.use(bodyParser.json({ limit: '50mb' }));
|
||||||
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
||||||
|
|
||||||
@@ -1064,6 +1066,7 @@ connectToDatabase(connectionUrl, options)
|
|||||||
const { domains, domainsAllowed } = parseDomains();
|
const { domains, domainsAllowed } = parseDomains();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log('domains:', domains);
|
console.log('domains:', domains);
|
||||||
console.log('isProduction:', isProduction);
|
console.log('isProduction:', isProduction);
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.2.86
|
1.2.87
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user