1
0
mirror of https://github.com/snobu/destreamer.git synced 2026-03-11 06:28:25 +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 { ApiClient } from './ApiClient';
import { argv, promptUser } from './CommandLineParser'; import { argv, promptUser } from './CommandLineParser';
import { getDecrypter } from './Descrypter'; import { getDecrypter } from './Decrypter';
import { DownloadManager } from './DownloadManager'; import { DownloadManager } from './DownloadManager';
import { ERROR_CODE } from './Errors'; import { ERROR_CODE } from './Errors';
import { setProcessEvents } from './Events'; import { setProcessEvents } from './Events';
@@ -10,7 +10,7 @@ import { getPuppeteerChromiumPath } from './PuppeteerHelper';
import { TokenCache/* , refreshSession */} from './TokenCache'; import { TokenCache/* , refreshSession */} from './TokenCache';
import { Video, Session } from './Types'; import { Video, Session } from './Types';
import { checkRequirements, /* ffmpegTimemarkToChunk, */parseInputFile, parseCLIinput, getUrlsFromPlaylist} from './Utils'; 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 { exec, execSync } from 'child_process';
import fs from 'fs'; import fs from 'fs';
@@ -131,9 +131,9 @@ async function downloadVideo(videoGUIDs: Array<string>,
logger.info('Downloading video info, this might take a while...'); logger.info('Downloading video info, this might take a while...');
const videos: Array<Video> = createUniquePath ( const videos: Array<Video> = createUniquePaths (
await getVideoInfo(videoGUIDs, session, argv.closedCaptions), await getVideosInfo(videoGUIDs, session, argv.closedCaptions),
outputDirectories, argv.format, argv.skip outputDirectories, argv.outputTemplate ,argv.format, argv.skip
); );
if (argv.simulate) { if (argv.simulate) {
@@ -152,10 +152,14 @@ async function downloadVideo(videoGUIDs: Array<string>,
// Launch aria2c // Launch aria2c
logger.info('Trying to launch and connect to aria2c...\n'); 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)); const aria2cExec = exec(
process.exit(ERROR_CODE.ARIA2C_CRASH); '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 // Try to connect to aria2c webSocket
const downloadManager = new DownloadManager(6789); const downloadManager = new DownloadManager(6789);
try { try {
@@ -171,7 +175,6 @@ async function downloadVideo(videoGUIDs: Array<string>,
console.info(`\nDownloading video no.${videos.indexOf(video) + 1} \n`); console.info(`\nDownloading video no.${videos.indexOf(video) + 1} \n`);
// TODO: check issue
if (argv.skip && fs.existsSync(video.outPath)) { if (argv.skip && fs.existsSync(video.outPath)) {
logger.info(`File already exists, skipping: ${video.outPath} \n`); logger.info(`File already exists, skipping: ${video.outPath} \n`);
continue; continue;
@@ -186,33 +189,40 @@ async function downloadVideo(videoGUIDs: Array<string>,
.filter(playlist => .filter(playlist =>
Object.prototype.hasOwnProperty.call(playlist.attributes, 'RESOLUTION')); Object.prototype.hasOwnProperty.call(playlist.attributes, 'RESOLUTION'));
if (videoPlaylists.length === 1 || argv.bestQuality) { if (videoPlaylists.length === 1 || argv.selectQuality === 5) {
videoPlaylistUrl = videoPlaylists.pop().uri; videoPlaylistUrl = videoPlaylists.pop().uri;
} }
else { else if (argv.selectQuality === 0) {
let resolutions = videoPlaylists.map(playlist => const resolutions = videoPlaylists.map(playlist =>
playlist.attributes.RESOLUTION.width + 'x' + playlist.attributes.RESOLUTION.width + 'x' +
playlist.attributes.RESOLUTION.height); playlist.attributes.RESOLUTION.height
);
videoPlaylistUrl = videoPlaylists[promptUser(resolutions)].uri; videoPlaylistUrl = videoPlaylists[promptUser(resolutions)].uri;
} }
else {
const choiche = Math.round((argv.selectQuality * videoPlaylists.length) / 10);
videoPlaylistUrl = videoPlaylists[choiche].uri;
}
// audio playlist url // audio playlist url
// TODO: better audio playlists parsing..? // TODO: better audio playlists parsing? With language maybe?
let audioPlaylistUrl: string; let audioPlaylistUrl: string;
let audioPlaylists: Array<string> = Object.keys(masterParser.manifest.mediaGroups.AUDIO.audio); let audioPlaylists: Array<string> = Object.keys(masterParser.manifest.mediaGroups.AUDIO.audio);
audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO.audio[audioPlaylists[0]].uri;
if (audioPlaylists.length === 1 || argv.bestQuality){ // if (audioPlaylists.length === 1){
audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO // audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO
.audio[audioPlaylists[0]].uri; // .audio[audioPlaylists[0]].uri;
} // }
else { // else {
audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO // audioPlaylistUrl = masterParser.manifest.mediaGroups.AUDIO
.audio[audioPlaylists[promptUser(audioPlaylists)]].uri; // .audio[audioPlaylists[promptUser(audioPlaylists)]].uri;
} // }
const videoUrls = await getUrlsFromPlaylist(videoPlaylistUrl, session); const videoUrls = await getUrlsFromPlaylist(videoPlaylistUrl, session);
const audioUrls = await getUrlsFromPlaylist(audioPlaylistUrl, 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 // video download
const videoSegmentsDir = tmp.dirSync({ const videoSegmentsDir = tmp.dirSync({
@@ -223,8 +233,7 @@ async function downloadVideo(videoGUIDs: Array<string>,
logger.info('\nDownloading and merging video segments \n'); logger.info('\nDownloading and merging video segments \n');
await downloadManager.downloadUrls(videoUrls, videoSegmentsDir.name); await downloadManager.downloadUrls(videoUrls, videoSegmentsDir.name);
execSync('copy /b *.encr ..\\video.encr', {cwd: videoSegmentsDir.name}); execSync(`copy /b *.encr "${video.filename}.video.encr"`, {cwd: videoSegmentsDir.name});
videoSegmentsDir.removeCallback();
// audio download // audio download
@@ -236,31 +245,71 @@ async function downloadVideo(videoGUIDs: Array<string>,
logger.info('\nDownloading and merging audio segments \n'); logger.info('\nDownloading and merging audio segments \n');
await downloadManager.downloadUrls(audioUrls, audioSegmentsDir.name); await downloadManager.downloadUrls(audioUrls, audioSegmentsDir.name);
execSync('copy /b *.encr ..\\audio.encr', {cwd: audioSegmentsDir.name}); execSync(`copy /b *.encr "${video.filename}.audio.encr"`, {cwd: audioSegmentsDir.name});
audioSegmentsDir.removeCallback();
// subs download // subs download
if (argv.closedCaptions && video.captionsUrl) { if (argv.closedCaptions && video.captionsUrl) {
await apiClient.callUrl(video.captionsUrl, 'get', null, 'text') 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')); logger.verbose('starting decrypt');
const output = fs.createWriteStream(video.outPath);
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(); await downloadManager.close();
logger.debug('closed websocket'); logger.debug('[destreamer] closed downloader. Stopping aria2c deamon');
aria2cExec.kill('SIGINT'); 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 'process._getActiveRequests();' in the debug console to see lingering
Handles (where you can find the sockets) or Requests */ Handles (where you can find the sockets) or Requests */
await downloadVideo(videoGUIDs, outDirs, session); await downloadVideo(videoGUIDs, outDirs, session);
// workaround for issue above
process.exit(0);
} }