1
0
mirror of https://github.com/snobu/destreamer.git synced 2026-01-29 19:32:16 +00:00

v2.0 RELEASE

This commit is contained in:
snobu
2020-04-18 15:34:26 +03:00
parent 65847cb29d
commit 609cf43ee0
10 changed files with 125 additions and 114 deletions

View File

@@ -5,43 +5,51 @@ import colors from 'colors';
import fs from 'fs';
export const argv = yargs.options({
url: {
videoUrls: {
alias: 'i',
describe: 'List of video urls',
type: 'array',
demandOption: false
},
'from-file': {
videoUrlsFile: {
alias: 'f',
describe: 'Path to txt file containing the urls',
type: 'string',
demandOption: false
},
username: {
alias: 'u',
type: 'string',
demandOption: false
},
outdir: {
outputDirectory: {
alias: 'o',
describe: 'The directory where destreamer will save your downloads [default: videos]',
type: 'string',
demandOption: false
},
'out-dirs-from-file': {
outputDirectories: {
alias: 'O',
describe: 'Path to a txt file containing one output directory per video',
type: 'string',
demandOption: false
},
'no-experiments': {
describe: `Disable experimental features (do not display video thumbnails)`,
noExperiments: {
alias: 'x',
describe: `Do not attempt to render video thumbnails in the console`,
type: 'boolean',
default: false,
demandOption: false
},
simulate: {
alias: 's',
describe: `Disable video download and print metadata information to the console`,
type: 'boolean',
default: false,
demandOption: false
},
verbose: {
alias: 'v',
describe: `Print additional information to the console (use this before opening an issue on GitHub)`,
type: 'boolean',
default: false,

View File

@@ -48,13 +48,13 @@ export const enum CLI_ERROR {
GRACEFULLY_STOP = ' ', // gracefully stop execution, yargs way
MISSING_REQUIRED_ARG = 'You must specify a URLs source.\n' +
'Valid options are --videoUrls or --videoUrlsFile.',
'Valid options are -i for one or more URLs separated by sapce or -f for URLs from file.',
VIDEOURLS_ARG_CONFLICT = 'Too many URLs sources specified!\n' +
'Please specify a single URLs source with either --videoUrls or --videoUrlsFile.',
'Please specify a single source, either -i or -f (URLs from file)',
OUTPUTDIR_ARG_CONFLICT = 'Too many output arguments specified!\n' +
'Please specify a single output argument with either --outputDirectory or --outputDirectories.',
'Please specify a single output argument, either -o or --outputDirectories.',
FILE_INPUT_VIDEOURLS_ARG = 'Wrong input for option --videoUrls.\n' +
'To read URLs from file, use --videoUrlsFile option.',

View File

@@ -2,6 +2,8 @@ import * as fs from 'fs';
import { Session } from './Types';
import { bgGreen, bgYellow, green } from 'colors';
import jwtDecode from 'jwt-decode';
import axios from 'axios';
import colors from 'colors';
export class TokenCache {
private tokenCacheFile: string = '.token_cache';
@@ -53,4 +55,27 @@ export class TokenCache {
console.info(green('Fresh access token dropped into .token_cache'));
});
}
public async RefreshToken(session: Session): Promise<string | null> {
let endpoint = `${session.ApiGatewayUri}refreshtoken?api-version=${session.ApiGatewayVersion}`;
let response = await axios.get(endpoint,
{
headers: {
Authorization: `Bearer ${session.AccessToken}`
}
});
let freshCookie: string | null = null;
try {
let cookie: string = response.headers["set-cookie"].toString();
freshCookie = cookie.split(',Authorization_Api=')[0];
}
catch (e) {
console.error(colors.yellow("Error when calling /refreshtoken: Missing or unexpected set-cookie header."));
}
return freshCookie;
}
}

View File

@@ -4,7 +4,6 @@ export type Session = {
ApiGatewayVersion: string;
}
export type Metadata = {
date: string;
totalChunks: number; // Abstraction of FFmpeg timemark

View File

@@ -136,6 +136,7 @@ export function ffmpegTimemarkToChunk(timemark: string) {
const hrs = parseInt(timeVals[0]);
const mins = parseInt(timeVals[1]);
const secs = parseInt(timeVals[2]);
const chunk = (hrs * 60) + mins + (secs / 60);
return (hrs * 60) + mins + (secs / 60);
return chunk;
}

View File

@@ -163,17 +163,34 @@ async function downloadVideo(videoUrls: string[], outputDirectories: string[], s
video.title = makeUniqueTitle(sanitize(video.title) + ' - ' + video.date, outputDirectories[j]);
// Very experimental inline thumbnail rendering
if (!argv.noThumbnails)
if (!argv.noExperiments)
await drawThumbnail(video.posterImage, session.AccessToken);
console.info('Spawning ffmpeg with access token and HLS URL. This may take a few seconds...\n');
console.info('Spawning ffmpeg with access token and HLS URL. This may take a few seconds...');
// Try to get a fresh cookie, else gracefully fall back
// to our session access token (Bearer)
let freshCookie = await tokenCache.RefreshToken(session);
let headers = `Authorization:\ Bearer\ ${session.AccessToken}`;
if (freshCookie) {
console.info(colors.green('Using a fresh cookie.'));
headers = `Cookie:\ ${freshCookie}`;
}
const outputPath = outputDirectories[j] + path.sep + video.title + '.mp4';
const ffmpegInpt = new FFmpegInput(video.playbackUrl, new Map([
['headers', `Authorization:\ Bearer\ ${session.AccessToken}`]
['headers', headers]
]));
const ffmpegOutput = new FFmpegOutput(outputPath);
const ffmpegCmd = new FFmpegCommand();
const cleanupFn = function () {
pbar.stop();
try {
fs.unlinkSync(outputPath);
} catch(e) {}
}
pbar.start(video.totalChunks, 0, {
speed: '0'
@@ -203,13 +220,7 @@ async function downloadVideo(videoUrls: string[], outputDirectories: string[], s
process.exit(ERROR_CODE.UNK_FFMPEG_ERROR);
});
process.on('SIGINT', () => {
pbar.stop();
try {
fs.unlinkSync(outputPath);
} catch (e) {}
});
process.on('SIGINT', cleanupFn);
// let the magic begin...
await new Promise((resolve: any, reject: any) => {
@@ -221,6 +232,8 @@ async function downloadVideo(videoUrls: string[], outputDirectories: string[], s
ffmpegCmd.spawn();
});
process.off('SIGINT', cleanupFn);
}
}