mirror of
https://github.com/snobu/destreamer.git
synced 2026-03-13 07:28:24 +00:00
Created proper error logging (#55)
* changes in the evaluation of sessionInfo * added Errors struct * changed error handling if FFmpeg not present * fixed error loggin thanks to the new Errors struct * minor fix after changes in sanitizeUrls * fix for succsesful execution and unknown code
This commit is contained in:
25
Types.ts
25
Types.ts
@@ -4,9 +4,32 @@ export type Session = {
|
|||||||
ApiGatewayVersion: string;
|
ApiGatewayVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
date: string;
|
date: string;
|
||||||
title: string;
|
title: string;
|
||||||
playbackUrl: string;
|
playbackUrl: string;
|
||||||
posterImage: string;
|
posterImage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface Errors {
|
||||||
|
[key: number]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// I didn't use an enum because there is no real advantage that i can find and
|
||||||
|
// we can't use multiline string for long errors
|
||||||
|
// TODO: create better errors descriptions
|
||||||
|
export const Errors: Errors = {
|
||||||
|
22: 'FFmpeg is missing. \n' +
|
||||||
|
'Destreamer requires a fairly recent release of FFmpeg to work properly. \n' +
|
||||||
|
'Please install it with your preferred package manager or copy FFmpeg binary in destreamer root directory. \n',
|
||||||
|
|
||||||
|
33: 'cannot split videoID from videUrl \n',
|
||||||
|
|
||||||
|
44: 'couldn\'t evaluate sessionInfo in the page \n',
|
||||||
|
|
||||||
|
55: 'running in an elevated shell \n',
|
||||||
|
|
||||||
|
66: 'no valid URL in the input \n'
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { sleep, parseVideoUrls, checkRequirements, makeUniqueTitle } from './utils';
|
import { sleep, parseVideoUrls, checkRequirements, makeUniqueTitle } from './utils';
|
||||||
import { TokenCache } from './TokenCache';
|
import { TokenCache } from './TokenCache';
|
||||||
import { getVideoMetadata } from './Metadata';
|
import { getVideoMetadata } from './Metadata';
|
||||||
import { Metadata, Session } from './Types';
|
import { Metadata, Session, Errors } from './Types';
|
||||||
import { drawThumbnail } from './Thumbnail';
|
import { drawThumbnail } from './Thumbnail';
|
||||||
|
|
||||||
import isElevated from 'is-elevated';
|
import isElevated from 'is-elevated';
|
||||||
@@ -14,14 +14,6 @@ import yargs from 'yargs';
|
|||||||
import sanitize from 'sanitize-filename';
|
import sanitize from 'sanitize-filename';
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* exitCode 22 = ffmpeg not found in $PATH
|
|
||||||
* exitCode 25 = cannot split videoID from videUrl
|
|
||||||
* exitCode 27 = no hlsUrl in the API response
|
|
||||||
* exitCode 29 = invalid response from API
|
|
||||||
* exitCode 88 = error extracting cookies
|
|
||||||
*/
|
|
||||||
|
|
||||||
let tokenCache = new TokenCache();
|
let tokenCache = new TokenCache();
|
||||||
|
|
||||||
const argv = yargs.options({
|
const argv = yargs.options({
|
||||||
@@ -66,16 +58,23 @@ const argv = yargs.options({
|
|||||||
}).argv;
|
}).argv;
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const isValidUser = !(await isElevated());
|
|
||||||
|
|
||||||
if (!isValidUser) {
|
process.on('unhandledRejection', (reason) => {
|
||||||
const usrName = process.platform === 'win32' ? 'Admin':'root';
|
console.error(colors.red('Unhandled error!\nTimeout or fatal error, please check your downloads and try again if necessary.\n'));
|
||||||
|
console.error(colors.red(reason as string));
|
||||||
|
});
|
||||||
|
|
||||||
console.error(colors.red(
|
process.on('exit', (code) => {
|
||||||
'\nERROR: Destreamer does not run as '+usrName+'!\nPlease run destreamer with a non-privileged user.\n'
|
if (code === 0)
|
||||||
));
|
console.log(colors.bgGreen('\n\nDestreamer finished successfully! \n'))
|
||||||
process.exit(-1);
|
else if (code in Errors)
|
||||||
}
|
console.error(colors.bgRed(`\n\nError: ${Errors[code]} \n`))
|
||||||
|
else
|
||||||
|
console.error(colors.bgRed(`\n\nUnknown exit code ${code} \n`))
|
||||||
|
});
|
||||||
|
|
||||||
|
if (await isElevated())
|
||||||
|
process.exit(55);
|
||||||
|
|
||||||
// create output directory
|
// create output directory
|
||||||
if (!fs.existsSync(argv.outputDirectory)) {
|
if (!fs.existsSync(argv.outputDirectory)) {
|
||||||
@@ -100,9 +99,7 @@ async function init() {
|
|||||||
|
|
||||||
async function DoInteractiveLogin(url: string, username?: string): Promise<Session> {
|
async function DoInteractiveLogin(url: string, username?: string): Promise<Session> {
|
||||||
|
|
||||||
let videoId = url.split("/").pop() ?? (
|
let videoId = url.split("/").pop() ?? process.exit(33)
|
||||||
console.log('Couldn\'t split the video Id from the first videoUrl'), process.exit(25)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('Launching headless Chrome to perform the OpenID Connect dance...');
|
console.log('Launching headless Chrome to perform the OpenID Connect dance...');
|
||||||
const browser = await puppeteer.launch({
|
const browser = await puppeteer.launch({
|
||||||
@@ -126,7 +123,6 @@ async function DoInteractiveLogin(url: string, username?: string): Promise<Sessi
|
|||||||
let session = null;
|
let session = null;
|
||||||
let tries: number = 0;
|
let tries: number = 0;
|
||||||
|
|
||||||
//TODO: add proper process exit and corrisponding code
|
|
||||||
while (!session) {
|
while (!session) {
|
||||||
try {
|
try {
|
||||||
let sessionInfo: any;
|
let sessionInfo: any;
|
||||||
@@ -145,7 +141,7 @@ async function DoInteractiveLogin(url: string, username?: string): Promise<Sessi
|
|||||||
tries++;
|
tries++;
|
||||||
await sleep(3000);
|
await sleep(3000);
|
||||||
} else {
|
} else {
|
||||||
throw(error);
|
process.exit(44)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +165,7 @@ function extractVideoGuid(videoUrls: string[]): string[] {
|
|||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Could not split the video GUID from URL: ${e.message}`);
|
console.error(`Could not split the video GUID from URL: ${e.message}`);
|
||||||
process.exit(25);
|
process.exit(33);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guid)
|
if (guid)
|
||||||
@@ -227,23 +223,13 @@ async function downloadVideo(videoUrls: string[], outputDirectory: string, sessi
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME
|
|
||||||
process.on('unhandledRejection', (reason) => {
|
|
||||||
console.error(colors.red('Unhandled error!\nTimeout or fatal error, please check your downloads and try again if necessary.\n'));
|
|
||||||
console.error(colors.red(reason as string));
|
|
||||||
throw new Error('Killing process..\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
checkRequirements();
|
checkRequirements() ?? process.exit(22);
|
||||||
await init();
|
await init();
|
||||||
|
|
||||||
const videoUrls: string[] = parseVideoUrls(argv.videoUrls);
|
const videoUrls: string[] = parseVideoUrls(argv.videoUrls) ?? process.exit(66);
|
||||||
|
|
||||||
if (videoUrls.length === 0) {
|
|
||||||
console.error(colors.red('\nERROR: No valid URL has been found!\n'));
|
|
||||||
process.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let session = tokenCache.Read();
|
let session = tokenCache.Read();
|
||||||
|
|
||||||
@@ -254,5 +240,5 @@ async function main() {
|
|||||||
downloadVideo(videoUrls, argv.outputDirectory, session);
|
downloadVideo(videoUrls, argv.outputDirectory, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
// run
|
|
||||||
main();
|
main();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ describe('Destreamer', () => {
|
|||||||
|
|
||||||
fs.writeFileSync(tmpFile.fd, testIn.join('\r\n'));
|
fs.writeFileSync(tmpFile.fd, testIn.join('\r\n'));
|
||||||
|
|
||||||
testOut = parseVideoUrls([tmpFile.name]);
|
testOut = parseVideoUrls([tmpFile.name])!;
|
||||||
if (testOut.length !== expectedOut.length)
|
if (testOut.length !== expectedOut.length)
|
||||||
assert.strictEqual(testOut, expectedOut, "URL list not sanitized");
|
assert.strictEqual(testOut, expectedOut, "URL list not sanitized");
|
||||||
|
|
||||||
|
|||||||
17
utils.ts
17
utils.ts
@@ -3,6 +3,7 @@ import colors from 'colors';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
|
||||||
function sanitizeUrls(urls: string[]) {
|
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 rex = new RegExp(/(?:https:\/\/)?.*\/video\/[a-z0-9]{8}-(?:[a-z0-9]{4}\-){3}[a-z0-9]{12}$/, 'i');
|
||||||
const sanitized: string[] = [];
|
const sanitized: string[] = [];
|
||||||
@@ -25,9 +26,10 @@ function sanitizeUrls(urls: string[]) {
|
|||||||
sanitized.push(url+query);
|
sanitized.push(url+query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sanitized;
|
return sanitized.length ? sanitized : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function parseVideoUrls(videoUrls: any) {
|
export function parseVideoUrls(videoUrls: any) {
|
||||||
const t = videoUrls[0] as string;
|
const t = videoUrls[0] as string;
|
||||||
const isPath = t.substring(t.length-4) === '.txt';
|
const isPath = t.substring(t.length-4) === '.txt';
|
||||||
@@ -41,24 +43,25 @@ export function parseVideoUrls(videoUrls: any) {
|
|||||||
return sanitizeUrls(urls);
|
return sanitizeUrls(urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function sleep(ms: number) {
|
export function sleep(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function checkRequirements() {
|
export function checkRequirements() {
|
||||||
try {
|
try {
|
||||||
const ffmpegVer = execSync('ffmpeg -version').toString().split('\n')[0];
|
const ffmpegVer = execSync('ffmpeg -version').toString().split('\n')[0];
|
||||||
console.info(colors.green(`Using ${ffmpegVer}\n`));
|
console.info(colors.green(`Using ${ffmpegVer}\n`));
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(colors.red(
|
return null;
|
||||||
'FFmpeg is missing.\nDestreamer requires a fairly recent release of FFmpeg to work properly.\n' +
|
|
||||||
'Please install it with your preferred package manager or copy FFmpeg binary in destreamer root directory.\n'
|
|
||||||
));
|
|
||||||
process.exit(22);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function makeUniqueTitle(title: string, outDir: string) {
|
export function makeUniqueTitle(title: string, outDir: string) {
|
||||||
let ntitle = title;
|
let ntitle = title;
|
||||||
let k = 0;
|
let k = 0;
|
||||||
@@ -67,4 +70,4 @@ export function makeUniqueTitle(title: string, outDir: string) {
|
|||||||
ntitle = title + ' - ' + (++k).toString();
|
ntitle = title + ' - ' + (++k).toString();
|
||||||
|
|
||||||
return ntitle;
|
return ntitle;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user