1
0
mirror of https://github.com/snobu/destreamer.git synced 2026-01-24 17:02:20 +00:00

done decryption/merging of video/audio/sub traks

This commit is contained in:
Luca Armaroli
2020-09-09 04:56:12 +02:00
parent ec099e9124
commit f1476ffe39

View File

@@ -1,6 +1,6 @@
import { ApiClient } from './ApiClient';
import { argv, promptUser } from './CommandLineParser';
import { getDecrypter } from './Descrypter';
import { getDecrypter } from './Decrypter';
import { DownloadManager } from './DownloadManager';
import { ERROR_CODE } from './Errors';
import { setProcessEvents } from './Events';
@@ -10,7 +10,7 @@ import { getPuppeteerChromiumPath } from './PuppeteerHelper';
import { TokenCache/* , refreshSession */} from './TokenCache';
import { Video, Session } from './Types';
import { checkRequirements, /* ffmpegTimemarkToChunk, */parseInputFile, parseCLIinput, getUrlsFromPlaylist} from './Utils';
import { getVideoInfo, createUniquePath } from './VideoUtils';
import { getVideosInfo, createUniquePaths } from './VideoUtils';
import { exec, execSync } from 'child_process';
import fs from 'fs';
@@ -131,9 +131,9 @@ async function downloadVideo(videoGUIDs: Array<string>,
logger.info('Downloading video info, this might take a while...');
const videos: Array<Video> = createUniquePath (
await getVideoInfo(videoGUIDs, session, argv.closedCaptions),
outputDirectories, argv.format, argv.skip
const videos: Array<Video> = createUniquePaths (
await getVideosInfo(videoGUIDs, session, argv.closedCaptions),
outputDirectories, argv.outputTemplate ,argv.format, argv.skip
);
if (argv.simulate) {
@@ -152,10 +152,14 @@ async function downloadVideo(videoGUIDs: Array<string>,
// Launch aria2c
logger.info('Trying to launch and connect to aria2c...\n');
const aria2cExec = exec('aria2c --enable-rpc --pause=true --rpc-listen-port=6789', (err, stdout, stderr) => {
logger.error(err?.message ?? (stderr || stdout));
process.exit(ERROR_CODE.ARIA2C_CRASH);
});
const aria2cExec = exec(
'aria2c --enable-rpc --pause=true --rpc-listen-port=6789',(err, stdout, stderr) => {
logger.error(err?.message ?? (stderr || stdout));
process.exit(ERROR_CODE.ARIA2C_CRASH);
}
);
// Try to connect to aria2c webSocket
const downloadManager = new DownloadManager(6789);
try {
@@ -171,7 +175,6 @@ async function downloadVideo(videoGUIDs: Array<string>,
console.info(`\nDownloading video no.${videos.indexOf(video) + 1} \n`);
// TODO: check issue
if (argv.skip && fs.existsSync(video.outPath)) {
logger.info(`File already exists, skipping: ${video.outPath} \n`);
continue;
@@ -186,33 +189,40 @@ async function downloadVideo(videoGUIDs: Array<string>,
.filter(playlist =>
Object.prototype.hasOwnProperty.call(playlist.attributes, 'RESOLUTION'));
if (videoPlaylists.length === 1 || argv.bestQuality) {
if (videoPlaylists.length === 1 || argv.selectQuality === 5) {
videoPlaylistUrl = videoPlaylists.pop().uri;
}
else {
let resolutions = videoPlaylists.map(playlist =>
else if (argv.selectQuality === 0) {
const resolutions = videoPlaylists.map(playlist =>
playlist.attributes.RESOLUTION.width + 'x' +
playlist.attributes.RESOLUTION.height);
playlist.attributes.RESOLUTION.height
);
videoPlaylistUrl = videoPlaylists[promptUser(resolutions)].uri;
}
else {
const choiche = Math.round((argv.selectQuality * videoPlaylists.length) / 10);
videoPlaylistUrl = videoPlaylists[choiche].uri;
}
// audio playlist url
// TODO: better audio playlists parsing..?
// TODO: better audio playlists parsing? With language maybe?
let audioPlaylistUrl: string;
let audioPlaylists: Array<string> = Object.keys(masterParser.manifest.mediaGroups.AUDIO.audio);
if (audioPlaylists.length === 1 || argv.bestQuality){
audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO
.audio[audioPlaylists[0]].uri;
}
else {
audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO
.audio[audioPlaylists[promptUser(audioPlaylists)]].uri;
}
audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO.audio[audioPlaylists[0]].uri;
// if (audioPlaylists.length === 1){
// audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO
// .audio[audioPlaylists[0]].uri;
// }
// else {
// audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO
// .audio[audioPlaylists[promptUser(audioPlaylists)]].uri;
// }
const videoUrls = await getUrlsFromPlaylist(videoPlaylistUrl, session);
const audioUrls = await getUrlsFromPlaylist(audioPlaylistUrl, session);
const decrypter = await getDecrypter(videoPlaylistUrl, session);
const videoDecrypter = await getDecrypter(videoPlaylistUrl, session);
const audioDecrypter = await getDecrypter(videoPlaylistUrl, session);
// video download
const videoSegmentsDir = tmp.dirSync({
@@ -223,8 +233,7 @@ async function downloadVideo(videoGUIDs: Array<string>,
logger.info('\nDownloading and merging video segments \n');
await downloadManager.downloadUrls(videoUrls, videoSegmentsDir.name);
execSync('copy /b *.encr ..\\video.encr', {cwd: videoSegmentsDir.name});
videoSegmentsDir.removeCallback();
execSync(`copy /b *.encr "${video.filename}.video.encr"`, {cwd: videoSegmentsDir.name});
// audio download
@@ -236,31 +245,71 @@ async function downloadVideo(videoGUIDs: Array<string>,
logger.info('\nDownloading and merging audio segments \n');
await downloadManager.downloadUrls(audioUrls, audioSegmentsDir.name);
execSync('copy /b *.encr ..\\audio.encr', {cwd: audioSegmentsDir.name});
audioSegmentsDir.removeCallback();
execSync(`copy /b *.encr "${video.filename}.audio.encr"`, {cwd: audioSegmentsDir.name});
// subs download
if (argv.closedCaptions && video.captionsUrl) {
await apiClient.callUrl(video.captionsUrl, 'get', null, 'text')
.then(res => fs.writeFileSync('subs.vtt', res?.data));
.then(res => fs.writeFileSync(
path.join(videoSegmentsDir.name, 'CC.vtt'), res?.data));
}
logger.warn('START DECRYPT');
const input = fs.createReadStream( path.join(path.dirname(video.outPath), 'video.encr'));
const output = fs.createWriteStream(video.outPath);
logger.verbose('starting decrypt');
input.pipe(decrypter).pipe(output);
const videoDecryptInput = fs.createReadStream(
path.join(videoSegmentsDir.name, video.filename + '.video.encr'));
const videoDecryptOutput = fs.createWriteStream(
path.join(videoSegmentsDir.name, video.filename + '.video'));
logger.warn('DONE DECRYPT');
const decryptVideoPromise = new Promise(resolve => {
videoDecryptOutput.on('finish', resolve);
videoDecryptInput.pipe(videoDecrypter).pipe(videoDecryptOutput);
});
const audioDecryptInput = fs.createReadStream(
path.join(audioSegmentsDir.name, video.filename + '.audio.encr'));
const audioDecryptOutput = fs.createWriteStream(
path.join(audioSegmentsDir.name, video.filename + '.audio'));
const decryptAudioPromise = new Promise(resolve => {
audioDecryptOutput.on('finish', resolve);
audioDecryptInput.pipe(audioDecrypter).pipe(audioDecryptOutput);
});
await Promise.all([decryptVideoPromise, decryptAudioPromise]);
logger.verbose('done decrypt');
logger.info('\nMerging vdeo and audio together');
const mergeCommand = (
// add video input
`ffmpeg -i "${path.join(videoSegmentsDir.name, video.filename + '.video')}" ` +
// add audio input
`-i "${path.join(audioSegmentsDir.name, video.filename + '.audio')}" ` +
// add subtitles input if present and wanted
((argv.closedCaptions && video.captionsUrl) ?
`-i "${path.join(videoSegmentsDir.name, 'CC.vtt')}" ` : '') +
// copy codec and output path
`-c copy "${video.outPath}"`
);
logger.debug('[destreamer] ' + mergeCommand);
execSync(mergeCommand, { stdio: 'ignore' });
videoSegmentsDir.removeCallback();
audioSegmentsDir.removeCallback();
}
logger.debug('closing');
logger.debug('[destreamer] closing downloader socket');
await downloadManager.close();
logger.debug('closed websocket');
logger.debug('[destreamer] closed downloader. Stopping aria2c deamon');
aria2cExec.kill('SIGINT');
logger.debug('closed aria2c');
logger.debug('[destreamer] stopped aria2c');
return;
}
/*
@@ -444,6 +493,10 @@ async function main(): Promise<void> {
'process._getActiveRequests();' in the debug console to see lingering
Handles (where you can find the sockets) or Requests */
await downloadVideo(videoGUIDs, outDirs, session);
// workaround for issue above
process.exit(0);
}