diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index 5fdd3aa..0179092 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -107,52 +107,62 @@ function hasNoArgs() { } function isShowHelpRequest() { - if (hasNoArgs()) + if (hasNoArgs()) { throw new Error(CLI_ERROR.GRACEFULLY_STOP); + } return true; } function checkRequiredArgument(argv: any) { - if (hasNoArgs()) + if (hasNoArgs()) { return true; + } - if (!argv.videoUrls && !argv.videoUrlsFile) + if (!argv.videoUrls && !argv.videoUrlsFile) { throw new Error(colors.red(CLI_ERROR.MISSING_REQUIRED_ARG)); + } return true; } function checkVideoUrlsArgConflict(argv: any) { - if (hasNoArgs()) + if (hasNoArgs()) { return true; + } - if (argv.videoUrls && argv.videoUrlsFile) + if (argv.videoUrls && argv.videoUrlsFile) { throw new Error(colors.red(CLI_ERROR.VIDEOURLS_ARG_CONFLICT)); + } return true; } function checkOutputDirArgConflict(argv: any) { - if (hasNoArgs()) + if (hasNoArgs()) { return true; + } - if (argv.outputDirectory && argv.outputDirectories) + 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) + if (hasNoArgs() || !argv.videoUrls) { return true; + } - if (!argv.videoUrls.length) + 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') + if (t.substring(t.length-4) === '.txt') { throw new Error(colors.red(CLI_ERROR.FILE_INPUT_VIDEOURLS_ARG)); + } return true; } @@ -164,8 +174,9 @@ function checkVideoUrlsInput(argv: any) { * Optimize and make this transparent to destreamer */ function mergeVideoUrlsArguments(argv: any) { - if (!argv.videoUrlsFile) + if (!argv.videoUrlsFile) { return true; + } argv.videoUrls = [argv.videoUrlsFile]; // noone will notice ;) @@ -183,13 +194,16 @@ function mergeVideoUrlsArguments(argv: any) { * Optimize and make this transparent to destreamer */ function mergeOutputDirArguments(argv: any) { - if (!argv.outputDirectories && argv.outputDirectory) + if (!argv.outputDirectories && argv.outputDirectory) { return true; + } - if (!argv.outputDirectory && !argv.outputDirectories) + if (!argv.outputDirectory && !argv.outputDirectories) { argv.outputDirectory = 'videos'; // default out dir - else if (argv.outputDirectories) + } + else if (argv.outputDirectories) { argv.outputDirectory = argv.outputDirectories; + } if (argv.outputDirectories) { // these are not valid anymore @@ -202,14 +216,17 @@ function mergeOutputDirArguments(argv: any) { // yeah this is for windows, but lets check everyone, who knows... function windowsFileExtensionBadBehaviorFix(argv: any) { - if (hasNoArgs() || !argv.videoUrlsFile || !argv.outputDirectories) + if (hasNoArgs() || !argv.videoUrlsFile || !argv.outputDirectories) { return true; + } if (!fs.existsSync(argv.videoUrlsFile)) { - if (fs.existsSync(argv.videoUrlsFile + '.txt')) + if (fs.existsSync(argv.videoUrlsFile + '.txt')) { argv.videoUrlsFile += '.txt'; - else + } + else { throw new Error(colors.red(CLI_ERROR.INPUT_URLS_FILE_NOT_FOUND)); + } } return true; diff --git a/src/Events.ts b/src/Events.ts index aa8cd27..2a11b5e 100644 --- a/src/Events.ts +++ b/src/Events.ts @@ -12,8 +12,9 @@ import colors from 'colors'; export function setProcessEvents() { // set exit event first so that we can always print cute errors process.on('exit', (code) => { - if (code == 0) + if (code == 0) { return; + } const msg = code in Error ? `\n\n${Error[code]} \n` : `\n\nUnknown error: exit code ${code} \n`; diff --git a/src/Metadata.ts b/src/Metadata.ts index 3319156..bd52736 100644 --- a/src/Metadata.ts +++ b/src/Metadata.ts @@ -9,8 +9,9 @@ function publishedDateToString(date: string) { const dateJs = new Date(date); const day = dateJs.getDate().toString().padStart(2, '0'); const month = (dateJs.getMonth() + 1).toString(10).padStart(2, '0'); + const publishedDate = day + '-' + month + '-' + dateJs.getFullYear(); - return day+'-'+month+'-'+dateJs.getFullYear(); + return publishedDate; } function durationToTotalChunks(duration: string) { @@ -39,7 +40,9 @@ export async function getVideoMetadata(videoGuids: string[], session: Session): playbackUrl = response?.data['playbackUrls'] .filter((item: { [x: string]: string; }) => item['mimeType'] == 'application/vnd.apple.mpegurl') - .map((item: { [x: string]: string }) => { return item['playbackUrl']; })[0]; + .map((item: { [x: string]: string }) => { + return item['playbackUrl']; + })[0]; posterImage = response?.data['posterImage']['medium']['url']; date = publishedDateToString(response?.data['publishedDate']); diff --git a/src/PuppeteerHelper.ts b/src/PuppeteerHelper.ts index c67da84..209acf0 100644 --- a/src/PuppeteerHelper.ts +++ b/src/PuppeteerHelper.ts @@ -8,10 +8,12 @@ export function getPuppeteerChromiumPath() { const win32_rex = /^.*?\\node_modules\\puppeteer\\\.local-chromium/; const replaceRegex = process.platform === 'win32' ? win32_rex : macOS_Linux_rex; - if (!isPkg) + if (!isPkg) { return puppeteer.executablePath(); + } - return puppeteer - .executablePath() + const browserPath = puppeteer.executablePath() .replace(replaceRegex, path.join(path.dirname(process.execPath), 'chromium')); + + return browserPath; } \ No newline at end of file diff --git a/src/Utils.ts b/src/Utils.ts index ceea7d2..6af12d3 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -10,24 +10,27 @@ function sanitizeUrls(urls: string[]) { const rex = new RegExp(/(?:https:\/\/)?.*\/video\/[a-z0-9]{8}-(?:[a-z0-9]{4}\-){3}[a-z0-9]{12}$/, 'i'); const sanitized: string[] = []; - for (let i=0, l=urls.length; i { - if (dir !== '') + if (dir !== '') { sanitized.push(dir); + } }); return sanitized; @@ -48,92 +52,95 @@ function readFileToArray(path: string) { } export async function forEachAsync(array: any, callback: any) { - for (let i=0, l=array.length; i { if (!fs.existsSync(dir)) { console.info(colors.yellow('Creating output directory:')); - console.info(colors.green(dir)+'\n'); + console.info(colors.green(dir) + '\n'); try { fs.mkdirSync(dir, { recursive: true }); - - } catch(e) { + } + catch (e) { process.exit(ERROR_CODE.INVALID_OUTPUT_DIR); } } }); } - export function checkOutDirsUrlsMismatch(dirsList: string[], urlsList: string[]) { const dirsListL = dirsList.length; const urlsListL = urlsList.length; - if (dirsListL == 1) // one out dir, treat this as the chosen one for all + // single out dir, treat this as the chosen one for all + if (dirsListL == 1) { return; - else if (dirsListL != urlsListL) + } + else if (dirsListL != urlsListL) { process.exit(ERROR_CODE.OUTDIRS_URLS_MISMATCH); + } } - export function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } - export function checkRequirements() { try { const ffmpegVer = execSync('ffmpeg -version').toString().split('\n')[0]; console.info(colors.green(`Using ${ffmpegVer}\n`)); - } catch (e) { + } + catch (e) { process.exit(ERROR_CODE.MISSING_FFMPEG); } } - export function makeUniqueTitle(title: string, outDir: string, skip?: boolean, format?: string) { let ntitle = title; let k = 0; - while (!skip && fs.existsSync(outDir + path.sep + ntitle + '.' + format)) + while (!skip && fs.existsSync(outDir + path.sep + ntitle + '.' + format)) { ntitle = title + ' - ' + (++k).toString(); + } return ntitle; } - export function ffmpegTimemarkToChunk(timemark: string) { const timeVals: string[] = timemark.split(':'); const hrs = parseInt(timeVals[0]); diff --git a/src/destreamer.ts b/src/destreamer.ts index fecc5a8..379eb44 100644 --- a/src/destreamer.ts +++ b/src/destreamer.ts @@ -26,16 +26,19 @@ const tokenCache = new TokenCache(); async function init() { setProcessEvents(); // must be first! - if (await isElevated()) + if (await isElevated()) { process.exit(ERROR_CODE.ELEVATED_SHELL); + } checkRequirements(); - if (argv.username) + if (argv.username) { console.info('Username: %s', argv.username); + } - if (argv.simulate) + if (argv.simulate) { console.info(colors.yellow('Simulate mode, there will be no video download.\n')); + } if (argv.verbose) { console.info('Video URLs:'); @@ -65,7 +68,8 @@ async function DoInteractiveLogin(url: string, username?: string): Promise 5) + } + catch (error) { + if (tries > 5) { process.exit(ERROR_CODE.NO_SESSION_INFO); + } session = null; tries++; @@ -126,13 +132,15 @@ function extractVideoGuid(videoUrls: string[]): string[] { try { const urlObj = new URL(url); guid = urlObj.pathname.split('/').pop(); - } catch (e) { + } + catch (e) { console.error(`Unrecognized URL format in ${url}: ${e.message}`); process.exit(ERROR_CODE.INVALID_VIDEO_GUID); } - if (guid) + if (guid) { videoGuids.push(guid); + } } if (argv.verbose) { @@ -183,7 +191,6 @@ async function downloadVideo(videoUrls: string[], outputDirectories: string[], s console.log(colors.yellow(`\nDownloading Video: ${video.title}\n`)); video.title = makeUniqueTitle(sanitize(video.title) + ' - ' + video.date, outputDirectories[j], argv.skip, argv.format); - console.info('Spawning ffmpeg with access token and HLS URL. This may take a few seconds...'); if (!process.stdout.columns) { @@ -214,8 +221,9 @@ async function downloadVideo(videoUrls: string[], outputDirectories: string[], s const cleanupFn = (): void => { pbar.stop(); - if (argv.noCleanup) + if (argv.noCleanup) { return; + } try { fs.unlinkSync(outputPath); @@ -255,7 +263,8 @@ async function downloadVideo(videoUrls: string[], outputDirectories: string[], s pbar.update(video.totalChunks); // set progress bar to 100% console.log(colors.yellow(`\nFile already exists, skipping: ${outputPath}`)); resolve(); - } else { + } + else { cleanupFn(); console.log(`\nffmpeg returned an error: ${error.message}`);