Lil fixes
This commit is contained in:
134
index.js
134
index.js
@@ -6,7 +6,7 @@ const path = require('path');
|
|||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
var mpdParser = require('mpd-parser');
|
const mpdParser = require('mpd-parser');
|
||||||
const softwareService = require('./services/softwares');
|
const softwareService = require('./services/softwares');
|
||||||
|
|
||||||
const BASE_PATH = process.env.DATA_PATH || `./data`;
|
const BASE_PATH = process.env.DATA_PATH || `./data`;
|
||||||
@@ -86,7 +86,12 @@ const runProgressCommand = (command) => {
|
|||||||
return { executeCommand, emitter };
|
return { executeCommand, emitter };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Route pour démarrer le processus
|
app.use((req, res, next) => {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
console.log(`[${timestamp}] ${req.method} ${req.url} - ${req.ip}`);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/start-process', async (req, res, next) => {
|
app.post('/start-process', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { mp4Filename, mpdUrl, keys, wantedResolution, wantedAudioTracks, wantedSubtitles } = req.body;
|
const { mp4Filename, mpdUrl, keys, wantedResolution, wantedAudioTracks, wantedSubtitles } = req.body;
|
||||||
@@ -106,7 +111,6 @@ app.post('/start-process', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Route pour vérifier le statut du job
|
|
||||||
app.get('/job-status/:jobId', async (req, res) => {
|
app.get('/job-status/:jobId', async (req, res) => {
|
||||||
const job = await videoQueue.getJob(req.params.jobId);
|
const job = await videoQueue.getJob(req.params.jobId);
|
||||||
if (job === null) {
|
if (job === null) {
|
||||||
@@ -118,6 +122,126 @@ app.get('/job-status/:jobId', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.delete('/job/:jobId', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const job = await videoQueue.getJob(req.params.jobId);
|
||||||
|
|
||||||
|
if (job === null) {
|
||||||
|
return res.status(404).json({ error: 'Job not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = await job.getState();
|
||||||
|
|
||||||
|
if (state === 'active' || state === 'waiting') {
|
||||||
|
const { mp4Filename } = job.data;
|
||||||
|
const workdir = path.join(TMP_PATH, mp4Filename);
|
||||||
|
|
||||||
|
if (fs.existsSync(workdir)) {
|
||||||
|
await runCommand(`rm -rf ${workdir}`);
|
||||||
|
console.log(`Cleaned up temp files for cancelled job: ${workdir}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await job.remove();
|
||||||
|
res.json({
|
||||||
|
message: 'Job deleted successfully',
|
||||||
|
jobId: req.params.jobId,
|
||||||
|
previousState: state
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting job:', error);
|
||||||
|
res.status(500).json({ error: error.message || 'Failed to delete job' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/jobs/completed', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const completedJobs = await videoQueue.getJobs(['completed', 'failed'], 0, -1);
|
||||||
|
|
||||||
|
if (completedJobs.length === 0) {
|
||||||
|
return res.json({
|
||||||
|
message: 'No completed jobs to delete',
|
||||||
|
deletedCount: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletePromises = completedJobs.map(job => job.remove());
|
||||||
|
await Promise.all(deletePromises);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `Successfully deleted ${completedJobs.length} completed jobs`,
|
||||||
|
deletedCount: completedJobs.length
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting completed jobs:', error);
|
||||||
|
res.status(500).json({ error: error.message || 'Failed to delete completed jobs' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/jobs/all', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const allJobs = await videoQueue.getJobs(['waiting', 'active', 'completed', 'failed', 'delayed'], 0, -1);
|
||||||
|
|
||||||
|
if (allJobs.length === 0) {
|
||||||
|
return res.json({
|
||||||
|
message: 'Queue is already empty',
|
||||||
|
deletedCount: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let cleanedDirs = 0;
|
||||||
|
|
||||||
|
for (const job of allJobs) {
|
||||||
|
const state = await job.getState();
|
||||||
|
if (state === 'active' || state === 'waiting') {
|
||||||
|
const { mp4Filename } = job.data;
|
||||||
|
const workdir = path.join(TMP_PATH, mp4Filename);
|
||||||
|
|
||||||
|
if (fs.existsSync(workdir)) {
|
||||||
|
await runCommand(`rm -rf ${workdir}`);
|
||||||
|
cleanedDirs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletePromises = allJobs.map(job => job.remove());
|
||||||
|
await Promise.all(deletePromises);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `Successfully deleted all ${allJobs.length} jobs`,
|
||||||
|
deletedCount: allJobs.length,
|
||||||
|
cleanedTempDirs: cleanedDirs
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting all jobs:', error);
|
||||||
|
res.status(500).json({ error: error.message || 'Failed to delete all jobs' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/jobs/stats', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const waiting = await videoQueue.getWaiting();
|
||||||
|
const active = await videoQueue.getActive();
|
||||||
|
const completed = await videoQueue.getCompleted();
|
||||||
|
const failed = await videoQueue.getFailed();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
waiting: waiting.length,
|
||||||
|
active: active.length,
|
||||||
|
completed: completed.length,
|
||||||
|
failed: failed.length,
|
||||||
|
total: waiting.length + active.length + completed.length + failed.length
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting queue stats:', error);
|
||||||
|
res.status(500).json({ error: error.message || 'Failed to get queue stats' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.get('/jobs-status', async (req, res) => {
|
app.get('/jobs-status', async (req, res) => {
|
||||||
const jobs = await videoQueue.getJobs();
|
const jobs = await videoQueue.getJobs();
|
||||||
res.json(await Promise.all(jobs.splice(0, 25).map(async job => ({
|
res.json(await Promise.all(jobs.splice(0, 25).map(async job => ({
|
||||||
@@ -248,6 +372,10 @@ app.post('/processMPD', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
res.status(500).json({ error: err.message || err.toString() || 'An error occured' });
|
||||||
|
})
|
||||||
|
|
||||||
// Processus de la file d'attente
|
// Processus de la file d'attente
|
||||||
videoQueue.process((job) => {
|
videoQueue.process((job) => {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
|
|||||||
@@ -186,30 +186,72 @@ const processUpdate = async (data) => {
|
|||||||
try {
|
try {
|
||||||
let downloadURL = data.binType === 'downloader' ? data.details.remoteInfos.browser_download_url : data.details.remoteInfos.downloadUrl;
|
let downloadURL = data.binType === 'downloader' ? data.details.remoteInfos.browser_download_url : data.details.remoteInfos.downloadUrl;
|
||||||
let filename = data.binType === 'downloader' ? data.details.remoteInfos.name : data.details.remoteInfos.filename;
|
let filename = data.binType === 'downloader' ? data.details.remoteInfos.name : data.details.remoteInfos.filename;
|
||||||
fs.mkdirSync(tmpUpdatePath);
|
fs.mkdirSync(tmpUpdatePath, { recursive: true });
|
||||||
const zipDest = `${tmpUpdatePath}/${filename}`;
|
const zipDest = `${tmpUpdatePath}/${filename}`;
|
||||||
console.log('Will download file from ' + downloadURL);
|
console.log('Will download file from ' + downloadURL);
|
||||||
await downloadFile(downloadURL, zipDest);
|
await downloadFile(downloadURL, zipDest);
|
||||||
console.log('File downloaded to ' + zipDest)
|
console.log('File downloaded to ' + zipDest)
|
||||||
console.log('Will decompress downloaded file');
|
console.log('Will decompress downloaded file');
|
||||||
const unzippedFolderDest = `${tmpUpdatePath}/${data.binType}_tmp`
|
const unzippedFolderDest = `${tmpUpdatePath}/${data.binType}_tmp`
|
||||||
fs.mkdirSync(unzippedFolderDest);
|
fs.mkdirSync(unzippedFolderDest, { recursive: true });
|
||||||
await extractFile(zipDest, unzippedFolderDest);
|
await extractFile(zipDest, unzippedFolderDest);
|
||||||
console.log('Unzipped !');
|
console.log('Unzipped !');
|
||||||
const uncompressedFoldersS = fs.readdirSync(unzippedFolderDest);
|
|
||||||
console.log(uncompressedFoldersS)
|
const extractedItems = fs.readdirSync(unzippedFolderDest);
|
||||||
if (uncompressedFoldersS.length !== 1)
|
console.log('Extracted items:', extractedItems);
|
||||||
throw new Error('Unable to retrieve decompressed folder');
|
|
||||||
const uncompressedFolders = fs.readdirSync(`${unzippedFolderDest}/${uncompressedFoldersS[0]}`);
|
if (extractedItems.length === 0) {
|
||||||
if (uncompressedFolders.length === 0)
|
throw new Error('No files found after extraction');
|
||||||
throw new Error('Unable to retrieve archive content');
|
}
|
||||||
if (data.binType === 'downloader')
|
|
||||||
fs.renameSync(`${unzippedFolderDest}/${uncompressedFoldersS[0]}/${uncompressedFolders[0]}`, data.details.localInfos.path);
|
if (data.binType === 'downloader') {
|
||||||
else if (data.binType === 'mp4decrypt')
|
let binaryPath = null;
|
||||||
fs.renameSync(`${unzippedFolderDest}/${uncompressedFoldersS[0]}/bin/mp4decrypt`, data.details.localInfos.path);
|
|
||||||
|
for (const item of extractedItems) {
|
||||||
|
const itemPath = `${unzippedFolderDest}/${item}`;
|
||||||
|
const stat = fs.statSync(itemPath);
|
||||||
|
|
||||||
|
if (stat.isFile() && (item === 'N_m3u8DL-RE' || item.includes('N_m3u8DL-RE'))) {
|
||||||
|
binaryPath = itemPath;
|
||||||
|
break;
|
||||||
|
} else if (stat.isDirectory()) {
|
||||||
|
const subItems = fs.readdirSync(itemPath);
|
||||||
|
const binary = subItems.find(subItem =>
|
||||||
|
subItem === 'N_m3u8DL-RE' || subItem.includes('N_m3u8DL-RE')
|
||||||
|
);
|
||||||
|
if (binary) {
|
||||||
|
binaryPath = `${itemPath}/${binary}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!binaryPath) {
|
||||||
|
throw new Error('N_m3u8DL-RE binary not found in archive');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.renameSync(binaryPath, data.details.localInfos.path);
|
||||||
|
|
||||||
|
} else if (data.binType === 'mp4decrypt') {
|
||||||
|
if (extractedItems.length !== 1) {
|
||||||
|
throw new Error('Unable to retrieve decompressed folder for mp4decrypt');
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainFolder = `${unzippedFolderDest}/${extractedItems[0]}`;
|
||||||
|
const mp4decryptPath = `${mainFolder}/bin/mp4decrypt`;
|
||||||
|
|
||||||
|
if (!fs.existsSync(mp4decryptPath)) {
|
||||||
|
throw new Error('mp4decrypt binary not found at expected path: bin/mp4decrypt');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.renameSync(mp4decryptPath, data.details.localInfos.path);
|
||||||
|
}
|
||||||
|
|
||||||
fs.chmodSync(data.details.localInfos.path, 0o755);
|
fs.chmodSync(data.details.localInfos.path, 0o755);
|
||||||
writeBinVersion(`${BIN_PATH}/.${data.binType}.version`, data.details.remoteInfos[data.binType === 'downloader' ? 'id' : 'version'])
|
writeBinVersion(`${BIN_PATH}/.${data.binType}.version`, data.details.remoteInfos[data.binType === 'downloader' ? 'id' : 'version'])
|
||||||
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
console.error('Error during update process:', e);
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
fs.rmSync(tmpUpdatePath, { recursive: true, force: true });
|
fs.rmSync(tmpUpdatePath, { recursive: true, force: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user