Add alot of new cool feats
This commit is contained in:
121
index.js
121
index.js
@@ -90,7 +90,7 @@ const runProgressCommand = (command) => {
|
|||||||
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;
|
||||||
console.log(req.body);
|
console.log(JSON.stringify(req.body, null, 2));
|
||||||
const job = await videoQueue.add({
|
const job = await videoQueue.add({
|
||||||
mp4Filename,
|
mp4Filename,
|
||||||
mpdUrl,
|
mpdUrl,
|
||||||
@@ -202,24 +202,27 @@ const parseMPDStream = async (mpdUrl) => {
|
|||||||
videoTracks: [],
|
videoTracks: [],
|
||||||
subtitles: []
|
subtitles: []
|
||||||
};
|
};
|
||||||
for (const [key, value] of Object.entries(parsedManifest?.mediaGroups?.AUDIO?.audio)) {
|
const toParse = [{
|
||||||
|
rootProp: 'AUDIO',
|
||||||
|
subProp: 'audio',
|
||||||
|
targetProp: 'audioTracks'
|
||||||
|
}, {
|
||||||
|
rootProp: 'SUBTITLES',
|
||||||
|
subProp: 'subs',
|
||||||
|
targetProp: 'subtitles'
|
||||||
|
}]
|
||||||
|
|
||||||
|
toParse.forEach(({ rootProp, subProp, targetProp }) => {
|
||||||
|
for (const [key, value] of Object.entries(parsedManifest?.mediaGroups?.[rootProp]?.[subProp])) {
|
||||||
for (let i = 0; i < value.playlists.length; i++) {
|
for (let i = 0; i < value.playlists.length; i++) {
|
||||||
obj.audioTracks.push({
|
obj[targetProp].push({
|
||||||
name: key,
|
name: key,
|
||||||
language: value.language,
|
language: value.language,
|
||||||
attributes: value.playlists[i].attributes
|
attributes: value.playlists[i].attributes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const [key, value] of Object.entries(parsedManifest?.mediaGroups?.SUBTITLES?.subs)) {
|
|
||||||
for (let i = 0; i < value.playlists.length; i++) {
|
|
||||||
obj.subtitles.push({
|
|
||||||
name: key,
|
|
||||||
language: value.language,
|
|
||||||
attributes: value.playlists[i].attributes
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < parsedManifest.playlists.length; i++) {
|
for (let i = 0; i < parsedManifest.playlists.length; i++) {
|
||||||
obj.videoTracks.push({
|
obj.videoTracks.push({
|
||||||
name: `${parsedManifest.playlists?.[i]?.attributes?.RESOLUTION?.width || 'N/C'}x${parsedManifest.playlists?.[i]?.attributes?.RESOLUTION?.height || 'N/C'}`,
|
name: `${parsedManifest.playlists?.[i]?.attributes?.RESOLUTION?.width || 'N/C'}x${parsedManifest.playlists?.[i]?.attributes?.RESOLUTION?.height || 'N/C'}`,
|
||||||
@@ -253,85 +256,93 @@ videoQueue.process((job) => {
|
|||||||
const { mp4Filename, mpdUrl, keys, wantedResolution, wantedAudioTracks, wantedSubtitles } = job.data;
|
const { mp4Filename, mpdUrl, keys, wantedResolution, wantedAudioTracks, wantedSubtitles } = job.data;
|
||||||
const downloaderPath = softwareService.getLocalBinFileInfo('downloader').path;
|
const downloaderPath = softwareService.getLocalBinFileInfo('downloader').path;
|
||||||
const mp4decryptPath = softwareService.getLocalBinFileInfo('mp4decrypt').path;
|
const mp4decryptPath = softwareService.getLocalBinFileInfo('mp4decrypt').path;
|
||||||
const mp4TmpFilepath = path.join(TMP_PATH, `${mp4Filename}.mp4`);
|
|
||||||
const mp4FinalFilepath = path.join(OUTPUT_PATH, `${mp4Filename}.mp4`);
|
const workdir = path.join(TMP_PATH, mp4Filename);
|
||||||
console.log('1')
|
if (!fs.existsSync(workdir))
|
||||||
//await parseMPDStream(mpdUrl);
|
fs.mkdirSync(workdir);
|
||||||
const filesExist = await checkFilesExistance('encrypted', TMP_PATH);
|
const mp4FilenameWithExt= `${mp4Filename}.mp4`;
|
||||||
console.log('2')
|
const finalPath = path.join(OUTPUT_PATH, mp4Filename);
|
||||||
|
|
||||||
|
const filesExist = await checkFilesExistance('encrypted', workdir);
|
||||||
if (filesExist.length === 0) {
|
if (filesExist.length === 0) {
|
||||||
// const resPattern = {
|
|
||||||
// '4k': [3840, 2160],
|
|
||||||
// '1080p': [1920, 1080],
|
|
||||||
// '720p': [1280, 720],
|
|
||||||
// '480p': [854, 480],
|
|
||||||
// '360p': [640, 360],
|
|
||||||
// '240p': [426, 240]
|
|
||||||
// }[wantedResolution] || [1920, 1080];
|
|
||||||
console.log('Encrypted files not found, downloading...');
|
console.log('Encrypted files not found, downloading...');
|
||||||
job.progress(10);
|
let objectsDownloaded = -1, previousPercentage = -1;
|
||||||
const { executeCommand, emitter } = runProgressCommand(`${downloaderPath} \"${mpdUrl}\" --save-dir ${TMP_PATH} --save-name ${mp4Filename}.mp4_encrypted --select-video res=\"${wantedResolution.resolution.width}*\" --select-audio lang=\"${wantedAudioTracks.map(elem => elem.language).join('|')}\":for=best2 --select-subtitle all`, true);
|
const objectsToDownload = 1 + wantedAudioTracks.length + wantedSubtitles.length;
|
||||||
|
job.progress(10); // Début à 10%
|
||||||
|
|
||||||
|
const subPart = wantedSubtitles.length > 0 ? `--select-subtitle lang=\"${wantedSubtitles.map(elem => elem.language).join('|')}\":bwMin=\"${wantedSubtitles.map(elem => Math.floor(elem.attributes.BANDWIDTH / 1000 -1)).join('|')}\":bwMax=\"${wantedSubtitles.map(elem => Math.round(elem.attributes.BANDWIDTH / 1000 + 1)).join('|')}\"` : '--drop-subtitle lang=\".*\"';
|
||||||
|
const audioPart = wantedAudioTracks.length > 0 ? `--select-audio lang=\"${wantedAudioTracks.map(elem => elem.language).join('|')}\":codecs=\"${wantedAudioTracks.map(elem => elem.attributes.CODECS).join('|')}\":bwMin=\"${wantedAudioTracks.map(elem => Math.floor(elem.attributes.BANDWIDTH / 1000 -1)).join('|')}\":bwMax=\"${wantedAudioTracks.map(elem => Math.round(elem.attributes.BANDWIDTH / 1000 + 1)).join('|')}\"` : '--drop-audio lang=\".*\"';
|
||||||
|
|
||||||
|
const { executeCommand, emitter } = runProgressCommand(`${downloaderPath} \"${mpdUrl}\" --save-dir ${workdir} --save-name ${mp4Filename}_encrypted --select-video res=\"${wantedResolution.resolution.width}*\" ${audioPart} ${subPart}`, true);
|
||||||
|
|
||||||
emitter.on('percentage', (percentage) => {
|
emitter.on('percentage', (percentage) => {
|
||||||
console.log(`Download Progression : ${percentage}%`);
|
if (percentage < previousPercentage) {
|
||||||
job.progress(Math.round(10 + (percentage / 5)));
|
objectsDownloaded++;
|
||||||
|
}
|
||||||
|
previousPercentage = percentage;
|
||||||
|
|
||||||
|
const subPercMax = 50 / objectsToDownload;
|
||||||
|
job.progress(Math.round((10 + (objectsDownloaded * subPercMax)) + (percentage * subPercMax / 100)));
|
||||||
});
|
});
|
||||||
|
|
||||||
await executeCommand;
|
await executeCommand;
|
||||||
} else {
|
} else {
|
||||||
console.log('Encrypted files already exist, bypassing download...')
|
console.log('Encrypted files already exist, bypassing download...')
|
||||||
}
|
}
|
||||||
|
job.progress(60);
|
||||||
|
|
||||||
job.progress(30);
|
// Decrypt video stream
|
||||||
|
await runCommand(`${mp4decryptPath} ${keys.map(k => `--key ${k.key}:${k.value}`).join(' ')} "${workdir}/${mp4Filename}_encrypted.mp4" "${workdir}/${mp4Filename}_decrypted.mp4"`);
|
||||||
// Décryptage vidéo
|
|
||||||
await runCommand(`${mp4decryptPath} ${keys.map(k => `--key ${k.key}:${k.value}`).join(' ')} "${mp4TmpFilepath}_encrypted.mp4" "${mp4TmpFilepath}_decrypted.mp4"`);
|
|
||||||
|
|
||||||
job.progress(50);
|
|
||||||
|
|
||||||
// Décryptage audio
|
|
||||||
const audioFiles = fs.readdirSync(mp4TmpFilepath);
|
|
||||||
const finalAudio = [];
|
|
||||||
for (const file of audioFiles) {
|
|
||||||
if (file.startsWith(`${mp4TmpFilepath}_encrypted`) && file.endsWith('.m4a')) {
|
|
||||||
const baseName = path.basename(file, '.m4a');
|
|
||||||
await runCommand(`${mp4decryptPath} ${keys.map(k => `--key ${k.key}:${k.value}`).join(' ')} "${file}" "${baseName}_decrypted.m4a"`);
|
|
||||||
finalAudio.push(`${baseName}_decrypted.m4a`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
job.progress(70);
|
job.progress(70);
|
||||||
|
|
||||||
|
// Decrypt audio streams
|
||||||
|
const audioFiles = fs.readdirSync(workdir);
|
||||||
|
const finalAudio = [];
|
||||||
|
for (const file of audioFiles) {
|
||||||
|
if (file.startsWith(`${mp4Filename}_encrypted`) && file.endsWith('.m4a')) {
|
||||||
|
const baseName = path.basename(file, '.m4a');
|
||||||
|
await runCommand(`${mp4decryptPath} ${keys.map(k => `--key ${k.key}:${k.value}`).join(' ')} "${workdir}/${file}" "${workdir}/${baseName}_decrypted.m4a"`);
|
||||||
|
finalAudio.push(`${workdir}/${baseName}_decrypted.m4a`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
job.progress(80);
|
||||||
|
|
||||||
// Combinaison avec ffmpeg
|
// Combinaison avec ffmpeg
|
||||||
let ffmpegCommand = `ffmpeg -y -i ${mp4TmpFilepath}_decrypted.mp4`;
|
let ffmpegCommand = `ffmpeg -y -i ${workdir}/${mp4Filename}_decrypted.mp4`;
|
||||||
let mapCommand = ' -map 0:v';
|
let mapCommand = ' -map 0:v';
|
||||||
let inputIndex = 1;
|
let inputIndex = 1;
|
||||||
|
|
||||||
for (const file of finalAudio) {
|
for (const file of finalAudio) {
|
||||||
//if (file.startsWith(`${mp4Filename}_encrypted`) && file.endsWith('_decrypted.m4a')) {
|
|
||||||
ffmpegCommand += ` -i ${file}`;
|
ffmpegCommand += ` -i ${file}`;
|
||||||
mapCommand += ` -map ${inputIndex}:a`;
|
mapCommand += ` -map ${inputIndex}:a`;
|
||||||
inputIndex++;
|
inputIndex++;
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ffmpegCommand += `${mapCommand} -c copy ${mp4FinalFilepath}.mp4`;
|
if (!fs.existsSync(finalPath))
|
||||||
|
fs.mkdirSync(finalPath);
|
||||||
|
|
||||||
|
ffmpegCommand += `${mapCommand} -c copy ${finalPath}/${mp4FilenameWithExt}`;
|
||||||
await runCommand(ffmpegCommand);
|
await runCommand(ffmpegCommand);
|
||||||
|
|
||||||
job.progress(90);
|
job.progress(90);
|
||||||
|
|
||||||
// Renommage des fichiers SRT
|
// Renommage des fichiers SRT
|
||||||
|
const subFiles = fs.readdirSync(workdir);
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
for (const file of audioFiles) {
|
for (const file of subFiles) {
|
||||||
if (file.startsWith(`${mp4TmpFilepath}_encrypted`) && file.endsWith('.srt')) {
|
if (file.startsWith(`${mp4Filename}_encrypted`) && file.endsWith('.srt')) {
|
||||||
fs.renameSync(file, `${mp4FinalFilepath}_${counter}.srt`);
|
fs.renameSync(`${workdir}/${file}`, `${finalPath}/${mp4Filename}_${counter}.srt`);
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nettoyage (commenté pour correspondre au script original)
|
// Nettoyage (commenté pour correspondre au script original)
|
||||||
await runCommand(`rm ${mp4TmpFilepath}_encrypted* && rm ${mp4TmpFilepath}_decrypted*`);
|
await runCommand(`rm -fr ${workdir}`);
|
||||||
|
|
||||||
job.progress(100);
|
job.progress(100);
|
||||||
resolve({ message: `File fetched and decrypted with success: ${mp4Filename}.mp4`, filePath: `${mp4FinalFilepath}.mp4`, fileName: `${mp4Filename}.mp4` });
|
resolve({ message: `File fetched and decrypted with success: ${mp4Filename}.mp4`, filePath: `${OUTPUT_PATH}/${mp4Filename}.mp4`, fileName: `${mp4Filename}.mp4` });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Error while processing task', error)
|
console.log('Error while processing task', error)
|
||||||
reject(new Error(`${error.toString() || error}`));
|
reject(new Error(`${error.toString() || error}`));
|
||||||
|
|||||||
Reference in New Issue
Block a user