mirror of
https://github.com/snobu/destreamer.git
synced 2026-01-22 16:02: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:
106
src/VideoUtils.ts
Normal file
106
src/VideoUtils.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { ApiClient } from './ApiClient';
|
||||
import { promptUser } from './CommandLineParser';
|
||||
import { logger } from './Logger';
|
||||
import { Video, Session } from './Types';
|
||||
|
||||
import { AxiosResponse } from 'axios';
|
||||
import fs from 'fs';
|
||||
import { parse } from 'iso8601-duration';
|
||||
import path from 'path';
|
||||
import sanitize from 'sanitize-filename';
|
||||
|
||||
|
||||
function publishedDateToString(date: string): string {
|
||||
const dateJs: Date = new Date(date);
|
||||
const day: string = dateJs.getDate().toString().padStart(2, '0');
|
||||
const month: string = (dateJs.getMonth() + 1).toString(10).padStart(2, '0');
|
||||
|
||||
return `${dateJs.getFullYear()}-${month}-${day}`;
|
||||
}
|
||||
|
||||
|
||||
function durationToTotalChunks(duration: string): number {
|
||||
const durationObj: any = parse(duration);
|
||||
const hrs: number = durationObj.hours ?? 0;
|
||||
const mins: number = durationObj.minutes ?? 0;
|
||||
const secs: number = Math.ceil(durationObj.seconds ?? 0);
|
||||
|
||||
return (hrs * 60) + mins + (secs / 60);
|
||||
}
|
||||
|
||||
|
||||
export async function getVideoInfo(videoGuids: Array<string>, session: Session, subtitles?: boolean): Promise<Array<Video>> {
|
||||
let metadata: Array<Video> = [];
|
||||
let title: string;
|
||||
let date: string;
|
||||
let totalChunks: number;
|
||||
let playbackUrl: string;
|
||||
let posterImageUrl: string;
|
||||
let captionsUrl: string | undefined;
|
||||
|
||||
const apiClient: ApiClient = ApiClient.getInstance(session);
|
||||
|
||||
for (const GUID of videoGuids) {
|
||||
let response: AxiosResponse<any> | undefined= await apiClient.callApi('videos/' + GUID, 'get');
|
||||
|
||||
title = sanitize(response?.data['name']);
|
||||
playbackUrl = response?.data['playbackUrls']
|
||||
.filter((item: { [x: string]: string; }) =>
|
||||
item['mimeType'] == 'application/vnd.apple.mpegurl')
|
||||
.map((item: { [x: string]: string }) => {
|
||||
return item['playbackUrl'];
|
||||
})[0];
|
||||
|
||||
posterImageUrl = response?.data['posterImage']['medium']['url'];
|
||||
date = publishedDateToString(response?.data['publishedDate']);
|
||||
totalChunks = durationToTotalChunks(response?.data.media['duration']);
|
||||
|
||||
if (subtitles) {
|
||||
let captions: AxiosResponse<any> | undefined = await apiClient.callApi(`videos/${GUID}/texttracks`, 'get');
|
||||
|
||||
if (!captions?.data.value.length) {
|
||||
captionsUrl = undefined;
|
||||
}
|
||||
else if (captions?.data.value.length === 1) {
|
||||
logger.info(`Found subtitles for ${title}. \n`);
|
||||
captionsUrl = captions?.data.value.pop().url;
|
||||
}
|
||||
else {
|
||||
const index: number = promptUser(captions.data.value.map((item: { language: string; autoGenerated: string; }) => {
|
||||
return `[${item.language}] autogenerated: ${item.autoGenerated}`;
|
||||
}));
|
||||
captionsUrl = captions.data.value[index].url;
|
||||
}
|
||||
}
|
||||
|
||||
metadata.push({
|
||||
date: date,
|
||||
totalChunks: totalChunks,
|
||||
title: title,
|
||||
outPath: '',
|
||||
playbackUrl: playbackUrl,
|
||||
posterImageUrl: posterImageUrl,
|
||||
captionsUrl: captionsUrl
|
||||
});
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
|
||||
export function createUniquePath(videos: Array<Video>, outDirs: Array<string>, format: string, skip?: boolean): Array<Video> {
|
||||
|
||||
videos.forEach((video: Video, index: number) => {
|
||||
let title = `${video.title} - ${video.date}`;
|
||||
let i = 0;
|
||||
|
||||
while (!skip && fs.existsSync(path.join(outDirs[index], title + '.' + format))) {
|
||||
title = `${video.title} - ${video.date}_${++i}`;
|
||||
}
|
||||
|
||||
|
||||
video.outPath = path.join(outDirs[index], title + '.' + format);
|
||||
});
|
||||
|
||||
return videos;
|
||||
}
|
||||
Reference in New Issue
Block a user