1
0
mirror of https://github.com/snobu/destreamer.git synced 2026-01-17 05:22:18 +00:00

Major code refactoring (#164)

* Added Chromium caching of identity provider cookies

* Moved token expiry check in standalone method

* Created refreshSession function

* Session is now refreshed if the token expires

* Linting fixes

* Removed debug console.log()

* Added CC support

* Created function to prompt user for download parameters (interactive mode)

* Fix data folder for puppeteer

* Fixed multiple session error

* Fix token expire time

* Moved session refreshing to a more sensible place

* Changed Metadata name to Video (to better reflect the data structure)

* Complete CLI refactoring

* Removed useless sleep function

* Added outDir check from CLI

* Complete input parsing refactoring (both inline and file)

* Fixed and improved tests to work with the new input parsing

* Moved and improved output path generation to videoUtils

* Main code refactoring, added outpath to video type

* Minor changes in spacing and type definition style

* Updated readme after code refactoring

* Fix if inputFile doesn't start with url on line 1

* Minor naming change

* Use module 'winston' for logging

* Created logge, changed all console.log and similar to use the logger

* Added verbose logging, changed posterUrl property name on Video type

* Moved GUID extraction to input parsing

* Added support for group links

* Fixed test after last input parsing update

* Removed debug proces.exit()

* Changed from desc to asc order for group videos

* Updated test to reflect GUIDs output after parsing

* Added couple of comments and restyled some imports

* More readable verbose GUIDs logging

* Removed unused errors

* Temporary fix for timeout not working in ApiClient

* Explicit class member accessibility

* Defined array naming schema to be Array<T>

* Defined type/interface schema to be type only

* A LOT of type definitions
This commit is contained in:
lukaarma
2020-07-18 21:49:36 +02:00
committed by GitHub
parent 89a942eb24
commit 7bfc565a05
19 changed files with 981 additions and 638 deletions

View File

@@ -1,37 +1,43 @@
import { CLI_ERROR } from './Errors';
import { CLI_ERROR, ERROR_CODE } from './Errors';
import { checkOutDir } from './Utils';
import { logger } from './Logger';
import yargs from 'yargs';
import colors from 'colors';
import fs from 'fs';
import readlineSync from 'readline-sync';
import yargs from 'yargs';
export const argv = yargs.options({
export const argv: any = yargs.options({
username: {
alias: 'u',
type: 'string',
describe: 'The username used to log into Microsoft Stream (enabling this will fill in the email field for you)',
demandOption: false
},
videoUrls: {
alias: 'i',
describe: 'List of video urls',
type: 'array',
demandOption: false
},
videoUrlsFile: {
inputFile: {
alias: 'f',
describe: 'Path to txt file containing the urls',
type: 'string',
demandOption: false
},
username: {
alias: 'u',
describe: 'Path to text file containing URLs and optionally outDirs. See the README for more on outDirs.',
type: 'string',
demandOption: false
},
outputDirectory: {
alias: 'o',
describe: 'The directory where destreamer will save your downloads [default: videos]',
describe: 'The directory where destreamer will save your downloads',
type: 'string',
default: 'videos',
demandOption: false
},
outputDirectories: {
alias: 'O',
describe: 'Path to a txt file containing one output directory per video',
type: 'string',
keepLoginCookies: {
alias: 'k',
describe: 'Let Chromium cache identity provider cookies so you can use "Remember me" during login',
type: 'boolean',
default: false,
demandOption: false
},
noExperiments: {
@@ -55,6 +61,13 @@ export const argv = yargs.options({
default: false,
demandOption: false
},
closedCaptions: {
alias: 'cc',
describe: 'Check if closed captions are aviable and let the user choose which one to download (will not ask if only one aviable)',
type: 'boolean',
default: false,
demandOption: false
},
noCleanup: {
alias: 'nc',
describe: 'Do not delete the downloaded video file when an FFmpeg error occurs',
@@ -87,147 +100,74 @@ export const argv = yargs.options({
demandOption: false
}
})
/**
* Do our own argv magic before destreamer starts.
* ORDER IS IMPORTANT!
* Do not mess with this.
*/
.check(() => isShowHelpRequest())
.check(argv => checkRequiredArgument(argv))
.check(argv => checkVideoUrlsArgConflict(argv))
.check(argv => checkOutputDirArgConflict(argv))
.check(argv => checkVideoUrlsInput(argv))
.check(argv => windowsFileExtensionBadBehaviorFix(argv))
.check(argv => mergeVideoUrlsArguments(argv))
.check(argv => mergeOutputDirArguments(argv))
.wrap(120)
.check(() => noArguments())
.check((argv: any) => inputConflicts(argv.videoUrls, argv.inputFile))
.check((argv: any) => {
if (checkOutDir(argv.outputDirectory)) {
return true;
}
else {
logger.error(CLI_ERROR.INVALID_OUTDIR);
throw new Error(' ');
}
})
.argv;
function hasNoArgs() {
return process.argv.length === 2;
}
function isShowHelpRequest() {
if (hasNoArgs()) {
throw new Error(CLI_ERROR.GRACEFULLY_STOP);
function noArguments(): boolean {
// if only 2 args no other args (0: node path, 1: js script path)
if (process.argv.length === 2) {
logger.error(CLI_ERROR.MISSING_INPUT_ARG, {fatal: true});
// so that the output stays clear
throw new Error(' ');
}
return true;
}
function checkRequiredArgument(argv: any) {
if (hasNoArgs()) {
return true;
function inputConflicts(videoUrls: Array<string | number> | undefined,
inputFile: string | undefined): boolean {
// check if both inputs are declared
if ((videoUrls !== undefined) && (inputFile !== undefined)) {
logger.error(CLI_ERROR.INPUT_ARG_CONFLICT);
throw new Error(' ');
}
// check if no input is declared or if they are declared but empty
else if (!(videoUrls || inputFile) || (videoUrls?.length === 0) || (inputFile?.length === 0)) {
logger.error(CLI_ERROR.MISSING_INPUT_ARG);
if (!argv.videoUrls && !argv.videoUrlsFile) {
throw new Error(colors.red(CLI_ERROR.MISSING_REQUIRED_ARG));
throw new Error(' ');
}
else if (inputFile) {
// check if inputFile doesn't end in '.txt'
if (inputFile.substring(inputFile.length - 4) !== '.txt') {
logger.error(CLI_ERROR.INPUTFILE_WRONG_EXTENSION);
return true;
}
function checkVideoUrlsArgConflict(argv: any) {
if (hasNoArgs()) {
return true;
}
if (argv.videoUrls && argv.videoUrlsFile) {
throw new Error(colors.red(CLI_ERROR.VIDEOURLS_ARG_CONFLICT));
}
return true;
}
function checkOutputDirArgConflict(argv: any) {
if (hasNoArgs()) {
return true;
}
if (argv.outputDirectory && argv.outputDirectories) {
throw new Error(colors.red(CLI_ERROR.OUTPUTDIR_ARG_CONFLICT));
}
return true;
}
function checkVideoUrlsInput(argv: any) {
if (hasNoArgs() || !argv.videoUrls) {
return true;
}
if (!argv.videoUrls.length) {
throw new Error(colors.red(CLI_ERROR.MISSING_REQUIRED_ARG));
}
const t = argv.videoUrls[0] as string;
if (t.substring(t.length-4) === '.txt') {
throw new Error(colors.red(CLI_ERROR.FILE_INPUT_VIDEOURLS_ARG));
}
return true;
}
/**
* Users see 2 separate options, but we don't really care
* cause both options have no difference in code.
*
* Optimize and make this transparent to destreamer
*/
function mergeVideoUrlsArguments(argv: any) {
if (!argv.videoUrlsFile) {
return true;
}
argv.videoUrls = [argv.videoUrlsFile]; // noone will notice ;)
// these are not valid anymore
delete argv.videoUrlsFile;
delete argv.F;
return true;
}
/**
* Users see 2 separate options, but we don't really care
* cause both options have no difference in code.
*
* Optimize and make this transparent to destreamer
*/
function mergeOutputDirArguments(argv: any) {
if (!argv.outputDirectories && argv.outputDirectory) {
return true;
}
if (!argv.outputDirectory && !argv.outputDirectories) {
argv.outputDirectory = 'videos'; // default out dir
}
else if (argv.outputDirectories) {
argv.outputDirectory = argv.outputDirectories;
}
if (argv.outputDirectories) {
// these are not valid anymore
delete argv.outputDirectories;
delete argv.O;
}
return true;
}
// yeah this is for windows, but lets check everyone, who knows...
function windowsFileExtensionBadBehaviorFix(argv: any) {
if (hasNoArgs() || !argv.videoUrlsFile || !argv.outputDirectories) {
return true;
}
if (!fs.existsSync(argv.videoUrlsFile)) {
if (fs.existsSync(argv.videoUrlsFile + '.txt')) {
argv.videoUrlsFile += '.txt';
throw new Error(' ');
}
else {
throw new Error(colors.red(CLI_ERROR.INPUT_URLS_FILE_NOT_FOUND));
// check if the inputFile exists
else if (!fs.existsSync(inputFile)) {
logger.error(CLI_ERROR.INPUTFILE_NOT_FOUND);
throw new Error(' ');
}
}
return true;
}
export function promptUser(choices: Array<string>): number {
let index: number = readlineSync.keyInSelect(choices, 'Which resolution/format do you prefer?');
if (index === -1) {
process.exit(ERROR_CODE.CANCELLED_USER_INPUT);
}
return index;
}