diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9529102 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/dist +/temp +/ffmpeg/* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..61ab961 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. diff --git a/README.md b/README.md new file mode 100644 index 0000000..99aa364 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +[![License: CC BY-NC-SA 4.0](https://img.shields.io/badge/license-CC%20BY--NC--SA%204.0-blue)](https://creativecommons.org/licenses/by-nc-sa/4.0/) + +

Hidive Downloader

+ +# Features +- User friendly (Completely CLI controlled) +- Download episodes simultaneously +- Search function +- Video files are converted to MKV lossless +- Subtitles are added to MKV + +# How to use +- Download the latest [release](https://github.com/philhk/hidive-downloader/releases) +- Extract the content to a folder +- Run `npm install` > `npm run build` in a terminal +- After that you can run the application with `npm run start` in a terminal + +# Example Video +https://user-images.githubusercontent.com/66958695/194079411-073d6dfe-6886-4d64-83bb-2679fe3a4f6a.mp4 \ No newline at end of file diff --git a/db/database.db b/db/database.db new file mode 100644 index 0000000..918049e Binary files /dev/null and b/db/database.db differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..37e0d90 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "hidive-downloader", + "main": "index.js", + "scripts": { + "start:dev": "ts-node-esm src/main.ts", + "start": "node dist/main.js", + "build:tsc": "tsc", + "build": "npm run build:tsc" + }, + "type": "module", + "author": "PhilHK", + "devDependencies": { + "@types/async": "^3.2.15", + "@types/better-sqlite3": "^7.6.0", + "@types/cli-progress": "^3.11.0", + "@types/ffmpeg": "^1.0.4", + "@types/fs-extra": "^9.0.13", + "@types/inquirer": "^9.0.2", + "@types/node": "^18.8.0", + "ts-node": "^10.9.1", + "typescript": "^4.8.4" + }, + "dependencies": { + "async": "^3.2.4", + "axios": "^0.27.2", + "better-sqlite3": "^7.6.2", + "cli-progress": "^3.11.2", + "fs-extra": "^10.1.0", + "inquirer": "^9.1.2", + "m3u8-parser": "^6.0.0" + } +} diff --git a/sql/createDatabase.sql b/sql/createDatabase.sql new file mode 100644 index 0000000..7e0edac --- /dev/null +++ b/sql/createDatabase.sql @@ -0,0 +1,17 @@ +-- SQLite +DROP TABLE IF EXISTS Account; +DROP TABLE IF EXISTS ApiKey; +CREATE TABLE Account ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email VARCHAR(50) NOT NULL, + password VARCHAR(50) NOT NULL, + userId INTEGER, + profileId INTEGER, + deviceId VARCHAR(50), + visitId VARCHAR(50) +); +CREATE TABLE ApiKey ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key VARCHAR(50) NOT NULL +); +INSERT INTO ApiKey (key) VALUES ('508efd7b42d546e19cc24f4d0b414e57e351ca73'); \ No newline at end of file diff --git a/src/api/HidiveDao.ts b/src/api/HidiveDao.ts new file mode 100644 index 0000000..72b2229 --- /dev/null +++ b/src/api/HidiveDao.ts @@ -0,0 +1,579 @@ +import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosInterceptorManager } from "axios" +import crypto from 'crypto' +import { ApiKey } from "../models/ApiKey" +import { User } from "../models/User" +//@ts-expect-error +import { Parser } from 'm3u8-parser' + +export interface Manifest { + allowCache: boolean, + endList: boolean, + mediaSequence: number, + discontinuitySequence: number, + playlistType: string, + custom: {}, + playlists: [ + { + attributes: T, + uri: string + } + ], + mediaGroups: { + AUDIO: { + 'GROUP-ID': { + NAME: { + default: boolean, + autoselect: boolean, + language: string, + uri: string, + instreamId: string, + characteristics: string, + forced: boolean + } + } + }, + VIDEO: {}, + 'CLOSED-CAPTIONS': {}, + SUBTITLES: {} + }, + dateTimeString: string, + dateTimeObject: Date, + targetDuration: number, + totalDuration: number, + discontinuityStarts: [number], + segments: [ + { + byterange: { + length: number, + offset: number + }, + duration: number, + attributes: {}, + discontinuity: number, + uri: string, + timeline: number, + key: { + method: string, + uri: string, + iv: string + }, + map: { + uri: string, + byterange: { + length: number, + offset: number + } + }, + 'cue-out': string, + 'cue-out-cont': string, + 'cue-in': string, + custom: {} + } & D + ] +} + +export const BASEROUTE = 'https://api.hidive.com/api/v1' +export const ROUTES = { + ping: () => `${BASEROUTE}/Ping`, + initDevice: () => `${BASEROUTE}/InitDevice`, + initVisit: () => `${BASEROUTE}/InitVisit`, + getGlobalSettings: () => `${BASEROUTE}/GetGlobalSettings`, + authenticate: () => `${BASEROUTE}/Authenticate`, + getDashboard: () => `${BASEROUTE}/GetDashboard`, + getTitle: () => `${BASEROUTE}/GetTitle`, + getMightLike: () => `${BASEROUTE}/GetMightLike`, + getVideos: () => `${BASEROUTE}/GetVideos`, + startVideo: () => `${BASEROUTE}/StartVideo`, + search: () => `${BASEROUTE}/Search`, +} + +export interface RequiredHidiveDaoOptions { + user: User, + apiKey: ApiKey +} +export interface OptionalHidiveDaoOptions { + +} +export type HidiveDaoOptions = RequiredHidiveDaoOptions & OptionalHidiveDaoOptions +export type UserHidiveDaoOptions = RequiredHidiveDaoOptions & Partial + +export interface RESTApiStandardResponseBody { + Code: number, + Status: string, + Message: string | null, + Messages: any, + Data: T, + Timestamp: string, + IPAddress: string +} + +export interface RESTApiPostPingResponseBodyData { + +} + +export interface RESTApiPostPingResponseBody extends RESTApiStandardResponseBody { + +} + +export interface RESTApiPostInitDeviceRequestBody { + DeviceName: string, + DeviceProfile?: { + DeviceType: number, + HardwareModel: number, + HardwareName: number, + HardwareVendor: number, + PlatformVendor: number, + PlatformVersion: number + }, + MacAddress?: string +} + +export interface RESTApiPostInitDeviceResponseBodyData { + DeviceId: string, + VisitId: string +} + +export interface RESTApiPostInitDeviceResponseBody extends RESTApiStandardResponseBody { + +} + +export interface RESTApiUser { + Id: number, + Email: null | string, + CountryCode: null | string, + CountryName: null | string, + ServiceLevel: string, + NextBillDate: null | string +} + +export interface RESTApiProfile { + Id: number, + Primary: boolean, + Nickname: string, + PinEnabled: boolean, + AvatarJPGUrl: string, + AvatarPNGUrl: string, + AllowMatureContent: boolean, + PreferSubsOverDubs: boolean, + PreferredLanguage: string, + PreferredSubtitleColor: number +} + +export interface RESTApiPostInitVisitResponseBodyData { + VisitId: string, + User: RESTApiUser, + Profiles: RESTApiProfile[] +} + +export interface RESTApiPostInitVisitResponseBody extends RESTApiStandardResponseBody { + +} + +export interface RESTApiPostAuthenticateRequestBody { + Email: string, + Password: string +} + +export interface RESTApiPostAuthenticateResponseBodyData { + VisitId: string, + User: RESTApiUser, + Profiles: RESTApiProfile[] +} + +export interface RESTApiPostAuthenticateResponseBody extends RESTApiStandardResponseBody { + +} + +export interface RESTApiPostGetTitleRequestBody { + Id: number +} + +export interface RESTApiEpisode { + Id: number, + Number: number, + Name: string, + Summary: string, + HIDIVEPremiereDate: string, + ScreenShotSmallUrl: string, + ScreenShotCompressedUrl: string, + SeasonNumber: number, + TitleId: number, + SeasonNumberValue: number, + EpisodeNumberValue: number, + VideoKey: string, + DisplayNameLong: string, + PercentProgress: number, + LoadTime: number +} + +export interface RESTApiTitle { + Id: number, + Name: string, + ShortSynopsis: string, + MediumSynopsis: string, + LongSynopsis: string, + KeyArtUrl: string, + MasterArtUrl: string, + Rating: string, + OverallRating: number, + RatingCount: number, + MALScore: string, + UserRating: number, + RunTime: number, + ShowInfoTitle: string, + FirstPremiereDate: string, + EpisodeCount: number, + SeasonName: string, + RokuHDArtUrl: string, + RokuSDArtUrl: string, + IsRateable: boolean, + InQueue: boolean, + IsFavorite: boolean, + IsContinueWatching: boolean, + ContinueWatching: null | boolean, + Episodes: RESTApiEpisode[], + LoadTime: number +} + +export interface RESTApiPostGetTitleResponseBodyData { + Title: RESTApiTitle +} + +export interface RESTApiPostGetTitleResponseBody extends RESTApiStandardResponseBody { + +} + + + +export const defaultHidiveDaoOptions: OptionalHidiveDaoOptions & Partial = { + +} + +export interface RESTApiPostGetVideosRequestBody { + TitleId: number, + VideoKey: string +} + +export interface RESTApiPostGetVideosResponseBodyData { + ShowAds: boolean, + CaptionCssUrl: string, + FontSize: number, + FontScale: number, + CaptionLanguages: string[], + CaptionLanguage: string, + CaptionVttUrls: { + [key: string]: string + } + VideoLanguages: string[], + VideoLanguage: string, + VideoUrls: { + [key: string]: { + hls: string[], + drm: string[], + drmEnabled: boolean + } + }, + FontColorName: string, + AutoPlayNextEpisode: boolean, + MaxStreams: number, + CurrentTime: number, + FontColorCode: string, + RunTime: number, + AdUrl: null | string +} + +export interface RESTApiPostGetVideosResponseBody extends RESTApiStandardResponseBody { + +} + +export interface RESTApiPostSearchRequestBody { + Query: string +} + +export interface RESTApiPostSearchResponseBodyData { + Query: string, + Slug: string, + TitleResults: RESTApiTitle[], + SearchId: number, + IsSearchedPinned: boolean, + IsPinnedSearchAvailable: boolean +} + +export interface RESTApiPostSearchResponseBody extends RESTApiStandardResponseBody { + +} + +export interface Resolution { + width: number, + height: number +} + +export interface RESTApiMediaSource { + 'program-Id': number, + BANDWIDTH: number, + RESOLUTION: Resolution, + CODECS: string[], +} + +export interface RESTApiMedia { + duration: number, + uri: string, + key: { + method: string, + uri: string, + iv: Uint32Array + }, + timeline: number +} + +export type RESTApiResponse = + Partial> + & Omit + & { + Data: Partial< + & RESTApiPostGetVideosResponseBodyData + & RESTApiPostAuthenticateResponseBodyData + & RESTApiPostPingResponseBodyData + & RESTApiPostGetTitleResponseBodyData + & RESTApiPostInitDeviceResponseBodyData + & RESTApiPostInitVisitResponseBodyData> + } + +export interface XHeadersHidive { + 'X-Applicationid': string, + 'X-Deviceid': string, + 'X-Nonce': string, + 'X-Profileid': number, + 'X-Signature': string, + 'X-Userid': number, + 'X-Visitid': string +} + +export const defaultXHeadersHidive: XHeadersHidive = { + "X-Applicationid": '24i-Android', + "X-Profileid": 0, + "X-Userid": 0, + "X-Deviceid": '', + "X-Nonce": '', + "X-Signature": '', + "X-Visitid": '' +} + +export class HidiveDao { + private readonly axios: AxiosInstance + public readonly options: HidiveDaoOptions + private readonly axiosOptions: AxiosRequestConfig + private xHeadersHidive: XHeadersHidive + public user?: RESTApiUser + public profile?: RESTApiProfile + private ipAddress?: string + private cookie: { [key: string]: string } = {} + + constructor(options: UserHidiveDaoOptions) { + this.options = { + ...defaultHidiveDaoOptions, + ...options + } + this.xHeadersHidive = { + ...defaultXHeadersHidive, + ...(this.options.user.userId ? { "X-Userid": this.options.user.userId } : {}), + ...(this.options.user.profileId ? { "X-Profileid": this.options.user.profileId } : {}), + ...(this.options.user.deviceId ? { "X-Deviceid": this.options.user.deviceId } : {}), + ...(this.options.user.visitId ? { "X-Visitid": this.options.user.visitId } : {}) + } + this.axiosOptions = { + headers: { + 'accept-encoding': 'gzip, deflate, br', + 'user-agent': 'okhttp/3.12.1', + 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8' + }, + } + this.axios = axios.create({ + ...this.axiosOptions + }) + this.setInterceptors() + } + + private setInterceptors() { + this.axios.interceptors.request.use((config) => { + const { data } = config + + this.xHeadersHidive["X-Nonce"] = this.generateNonce() + this.xHeadersHidive["X-Signature"] = this.generateSignature(data) + config.headers = { + ...config.headers, + 'X-Applicationid': this.xHeadersHidive["X-Applicationid"], + 'X-Deviceid': this.xHeadersHidive["X-Deviceid"], + 'X-Nonce': this.xHeadersHidive["X-Nonce"], + 'X-Profileid': this.xHeadersHidive["X-Profileid"], + 'X-Signature': this.xHeadersHidive["X-Signature"], + 'X-Userid': this.xHeadersHidive["X-Userid"], + 'X-Visitid': this.xHeadersHidive["X-Visitid"], + ...(Object.entries(this.cookie).length > 0 ? { cookie: Object.entries(this.cookie).map(cookie => `${cookie[0]}=${cookie[1]}`).join('; ') } : {}) + } + + return config + }); + + + (this.axios.interceptors.response as AxiosInterceptorManager>).use((res) => { + const { data, headers, config } = res + if (headers["set-cookie"]) { + this.cookie = { + ...this.cookie, + ...Object.assign({}, ...headers["set-cookie"].map(cookie => { + const [name, value] = cookie.split(';')[0].split('=') + return { + [name]: value + } + })) + } + } + + if (config.url?.startsWith(BASEROUTE) && data.Data) { + const { IPAddress } = data + + if (data.Data.User) + this.user = data.Data.User + + if (data.Data.Profiles && data.Data.Profiles.length > 0) + this.profile = data.Data.Profiles.find(profile => profile.Primary) || data.Data.Profiles[0] + + if (IPAddress) + this.ipAddress = IPAddress + + this.xHeadersHidive = { + ...this.xHeadersHidive, + ...(data.Data.DeviceId ? { "X-Deviceid": data.Data.DeviceId } : {}), + ...(data.Data.VisitId ? { "X-Visitid": data.Data.VisitId } : {}), + ...(this.user ? { "X-Userid": this.user.Id } : {}), + ...(this.profile ? { "X-Profileid": this.profile.Id } : {}), + } + + if (data.Code !== 0 || data.Status !== 'Success') { + return Promise.reject(new Error(`Response returned code ${data.Code} with status ${data.Status}`)) + } + } + + return res + }) + } + + private generateNonce() { + const initDate = new Date(); + const nonceDate = [ + initDate.getUTCFullYear().toString().slice(-2), + ('0' + (initDate.getUTCMonth() + 1)).slice(-2), + ('0' + initDate.getUTCDate()).slice(-2), + ('0' + initDate.getUTCHours()).slice(-2), + ('0' + initDate.getUTCMinutes()).slice(-2) + ].join(''); + const nonceCleanStr = nonceDate + this.options.apiKey.key; + const nonceHash = crypto.createHash('sha256').update(nonceCleanStr).digest('hex'); + return nonceHash; + } + + private generateSignature(requestBody: any) { + const sigCleanStr = [ + this.ipAddress, + this.xHeadersHidive["X-Applicationid"], + this.xHeadersHidive["X-Deviceid"], + this.xHeadersHidive["X-Visitid"], + this.xHeadersHidive["X-Userid"], + this.xHeadersHidive["X-Profileid"], + JSON.stringify(requestBody), + this.xHeadersHidive["X-Nonce"], + this.options.apiKey.key, + ].join(''); + return crypto.createHash('sha256').update(sigCleanStr).digest('hex'); + } + + public async ping() { + return (await this.axios.post(ROUTES.ping())).data + } + + public async initDevice() { + return (await this.axios.post, RESTApiPostInitDeviceRequestBody>(ROUTES.initDevice(), { + DeviceName: 'Android' + })).data + } + + public async initVisit() { + return (await this.axios.post(ROUTES.initVisit(), { + + })).data + } + + public async authenticate() { + return (await this.axios.post, RESTApiPostAuthenticateRequestBody>(ROUTES.authenticate(), { + Email: this.options.user.email, + Password: this.options.user.password + })).data + } + + public async getTitle(id: number) { + return (await this.axios.post, RESTApiPostGetTitleRequestBody>(ROUTES.getTitle(), { + Id: id + })).data + } + + public async getVideos(titleId: number, VideoKey: string) { + return (await this.axios.post, RESTApiPostGetVideosRequestBody>(ROUTES.getVideos(), { + TitleId: titleId, + VideoKey: VideoKey + })).data + } + + public async search(query: string) { + return (await this.axios.post, RESTApiPostSearchRequestBody>(ROUTES.search(), { + Query: query + })).data + } + + public async getMedia(mediaUrl: string): Promise> { + const parser = new Parser() + parser.push((await this.axios.get(mediaUrl)).data) + parser.end() + return parser.manifest + } + + public async getMediaSources(mediaSourceUrl: string): Promise> { + const parser = new Parser() + parser.push((await this.axios.get(mediaSourceUrl)).data) + parser.end() + return parser.manifest + } + + public async getTs(tsUrl: string): Promise { + return (await this.axios.get(tsUrl, { + headers: { + 'accept-encoding': 'identity', + 'user-agent': 'smartexoplayer/1.9.20.R (Linux;Android 9) ExoPlayerLib/2.7.2' + }, + responseType: 'arraybuffer' + })).data + } + + public async getKey(keyUrl: string): Promise { + return (await this.axios.get(keyUrl, { + headers: { + 'user-agent': 'smartexoplayer/1.9.20.R (Linux;Android 9) ExoPlayerLib/2.7.2' + }, + responseType: 'arraybuffer' + })).data + } + + public async getSub(subUrl: string): Promise { + return (await this.axios.get(subUrl, { + headers: { + 'user-agent': 'smartexoplayer/1.9.20.R (Linux;Android 9) ExoPlayerLib/2.7.2' + } + })).data + } +} \ No newline at end of file diff --git a/src/cli/CliHandler.ts b/src/cli/CliHandler.ts new file mode 100644 index 0000000..cafc55f --- /dev/null +++ b/src/cli/CliHandler.ts @@ -0,0 +1,166 @@ +import inquirer, { AllChoiceMap, Answers, AsyncDynamicQuestionProperty, DistinctChoice } from 'inquirer' +import { Resolution, RESTApiEpisode, RESTApiMediaSource, RESTApiTitle } from '../api/HidiveDao' +import { ApiKey } from '../models/ApiKey' +import { User } from '../models/User' + +export class CliHandler { + public async promptConcurrentDownloads(): Promise { + return (await inquirer.prompt({ + type: 'number', + message: 'How many simultaneous downloads would you like to have?', + default: 1, + name: 'concurrent' + })).concurrent + } + + public async promptActions(actions: { message: string, value: string }[]): Promise { + return this.promptList('What do you want to do?', + actions.map(action => { + return { + name: action.message, + value: action.value + } + }) + ) + } + + public async promptEpisodesList(episodes: RESTApiEpisode[]): Promise { + return this.promptCheckbox('Which episodes would you like to download?', + episodes.map(episode => { + return { + name: ` E${episode.EpisodeNumberValue} | ${episode.Name}`, + value: episode + } + }) + ) + } + + public async promptSeriesList(titles: RESTApiTitle[]): Promise { + return this.promptList('Which series would you like to download?', + titles.map(series => { + return { + name: series.Name, + value: series + } + }) + ) + } + + public async promptUserList(users: User[]): Promise { + return this.promptList('Please select your desired account', + users.map(user => { + return { + name: user.email, + value: user + } + }) + ) + } + + public async promptApiKey(apiKeys: ApiKey[]): Promise { + return this.promptList('Please select your desired API key', + apiKeys.map(apiKey => { + return { + name: apiKey.key, + value: apiKey + } + }) + ) + } + + public async promptGetApiKey(): Promise> { + const { key } = await inquirer.prompt([ + { + type: 'input', + message: 'Please enter an API key', + name: 'key' + } + ]) + + return { key } + } + + public async promptGetAuth(): Promise> { + const { email, password } = await inquirer.prompt([ + { + type: 'input', + message: 'Please enter your E-Mail', + name: 'email' + }, + { + type: 'input', + message: 'Please enter your password', + name: 'password' + }, + ]) + + return { email, password } + } + + public async promptResolutionFromMedia(medias: RESTApiMediaSource[]) { + return this.promptList('Which resolution would you like to have? (Unavailable resolutions will fall back to the best)', + medias.map(media => { + return { + name: `${media.RESOLUTION.width}x${media.RESOLUTION.height} (${media.BANDWIDTH / 1000} kB/s)`, + value: media.RESOLUTION + } + }) + ) + } + + public async promptVideoLanguage(languages: string[]) { + return this.promptList('Which video language would you like to have? (Unavailable languages will fall back to Japanese)', + languages.map(language => { + return { + name: language + } + }) + ) + } + + public async promptList(message: string, choices: AsyncDynamicQuestionProperty>>, Answers> | undefined): Promise { + return (await inquirer.prompt({ + type: 'list', + message, + name: 'item', + choices: choices + }, + { clearPromptOnDone: true })).item + } + + public async promptCheckbox(message: string, choices: AsyncDynamicQuestionProperty>>, Answers> | undefined): Promise { + return (await inquirer.prompt({ + type: 'checkbox', + message, + name: 'item', + choices: choices + })).item + } + + public async promptInput(message: string): Promise { + return (await inquirer.prompt({ + type: 'input', + message, + name: 'input' + })).input + } + + public async promptConfirm(message: string): Promise { + return (await inquirer.prompt({ + type: 'list', + message, + name: 'confirmed', + choices: [ + { + name: 'Yes', + value: true + }, + { + name: 'No', + value: false, + checked: true + } + ] + })).confirmed + } +} \ No newline at end of file diff --git a/src/database/AccountDao.ts b/src/database/AccountDao.ts new file mode 100644 index 0000000..c236eab --- /dev/null +++ b/src/database/AccountDao.ts @@ -0,0 +1,41 @@ +import { Database } from 'better-sqlite3' +import { User } from '../models/User' + +const createUserStmt = `INSERT INTO Account (email, password, userId, profileId, deviceId, visitId) VALUES (?, ?, ?, ?, ?, ?)` +const deleteUserStmt = `DELETE FROM Account WHERE id = ?` +const getUserStmt = `SELECT * FROM Account WHERE id = ?` +const updateUserStmt = `UPDATE Account SET email = ?, password = ?, userId = ?, profileId = ?, deviceId = ?, visitId = ? WHERE id = ?` +const getAllStmt = `SELECT * FROM Account` + +export class AccountDao { + private readonly database: Database + + constructor(database: Database) { + this.database = database + } + + public deleteAccount(id: number) { + const stmt = this.database.prepare(deleteUserStmt) + return stmt.run(id) + } + + public createAccount(user: Omit) { + const stmt = this.database.prepare(createUserStmt) + return stmt.run(user.email, user.password, user.userId, user.profileId, user.deviceId, user.visitId) + } + + public getAccount(id: number): User | undefined { + const stmt = this.database.prepare(getUserStmt) + return stmt.get(id) + } + + public updateAccount(id: number, user: User) { + const stmt = this.database.prepare(updateUserStmt) + return stmt.run(user.email, user.password, user.userId, user.profileId, user.deviceId, user.visitId, id) + } + + public getAll(): User[] { + const stmt = this.database.prepare(getAllStmt) + return stmt.all() + } +} \ No newline at end of file diff --git a/src/database/ApiKeyDao.ts b/src/database/ApiKeyDao.ts new file mode 100644 index 0000000..3a30efb --- /dev/null +++ b/src/database/ApiKeyDao.ts @@ -0,0 +1,35 @@ +import { Database } from 'better-sqlite3' +import { ApiKey } from '../models/ApiKey' + +const createApiKeyStmt = `INSERT INTO ApiKey (key) VALUES (?)` +const deleteApiKeyStmt = `DELETE FROM ApiKey WHERE id = ?` +const getApiKeyStmt = `SELECT * FROM ApiKey WHERE id = ?` +const getAllStmt = `SELECT * FROM ApiKey` + +export class ApiKeyDao { + private readonly database: Database + + constructor(database: Database) { + this.database = database + } + + public deleteApiKey(id: number) { + const stmt = this.database.prepare(deleteApiKeyStmt) + return stmt.run(id) + } + + public createApiKey(apiKey: Omit) { + const stmt = this.database.prepare(createApiKeyStmt) + return stmt.run(apiKey.key) + } + + public getApiKey(id: number): ApiKey | undefined { + const stmt = this.database.prepare(getApiKeyStmt) + return stmt.get(id) + } + + public getAll(): ApiKey[] { + const stmt = this.database.prepare(getAllStmt) + return stmt.all() + } +} \ No newline at end of file diff --git a/src/database/Database.ts b/src/database/Database.ts new file mode 100644 index 0000000..578233a --- /dev/null +++ b/src/database/Database.ts @@ -0,0 +1,6 @@ +import path from 'path' +import Database from 'better-sqlite3' + +export function createDatabase() { + return new Database(path.resolve('./db/database.db'), { fileMustExist: false }) +} \ No newline at end of file diff --git a/src/ffmpeg/FFmpeg.ts b/src/ffmpeg/FFmpeg.ts new file mode 100644 index 0000000..08ef2c8 --- /dev/null +++ b/src/ffmpeg/FFmpeg.ts @@ -0,0 +1,78 @@ +import { ChildProcessWithoutNullStreams, spawn } from 'child_process' +import path from 'path' + +export interface Subtitle { + name: string, + path: string +} + +export interface Map { + map: number +} + +export class FFmpeg { + public convertSubtitle(filePath: string, data: Buffer, language: string) { + const process = spawn(path.resolve('./ffmpeg/ffmpeg.exe'), + [ + '-y', + '-i', '-', + '-map', '0', + '-c', 'srt', + '-metadata:s:0', `title="${language}"`, + filePath + ] + ) + process.stdin.write(data) + process.stdin.end() + return this.promisifyClose(process, filePath) + } + + public convertVideo(filePath: string) { + const process = spawn(path.resolve('./ffmpeg/ffmpeg.exe'), + [ + '-y', + '-i', '-', + '-map', '0', + '-c', 'copy', + filePath + ] + ) + return { + write: (data: Uint8Array) => process.stdin.write(data), + end: () => this.promisifyClose(process, filePath) + } + } + + public mergeVideoAndSubtitles(filePath: string, videoPath: string, subtitles: Subtitle[]) { + let currentMap = 0 + const getNextMap = () => currentMap = currentMap + 1 + const subtitlesMaps: (Subtitle & Map)[] = subtitles.map(subtitle => { + return { + map: getNextMap(), + name: subtitle.name, + path: subtitle.path + } + }) + + const process = spawn(path.resolve('./ffmpeg/ffmpeg.exe'), + [ + '-y', + '-i', videoPath, + ...subtitlesMaps.flatMap((subtitle) => ['-i', `${subtitle.path}`]), + '-map', '0', + ...subtitlesMaps.flatMap((subtitle) => ['-map', `${subtitle.map}`]), + '-c', 'copy', + ...subtitlesMaps.flatMap((subtitle) => [`-metadata:s:s:${subtitle.map - 1}`, `title=${subtitle.name}`]), + filePath + ] + ) + return this.promisifyClose(process, filePath) + } + + private promisifyClose(process: ChildProcessWithoutNullStreams, value?: T) { + return new Promise((res) => { + process.stdin.end() + return process.on('close', () => res(value as any)) + }) + } +} \ No newline at end of file diff --git a/src/file/FileManager.ts b/src/file/FileManager.ts new file mode 100644 index 0000000..f7d9a2d --- /dev/null +++ b/src/file/FileManager.ts @@ -0,0 +1,40 @@ +import fs from 'fs' +import path from 'path' + +const tempPath = path.resolve('./temp') +const outputPath = path.resolve('./output') + +export class FileManager { + public getTempPath(filePath: string) { + return path.join(tempPath, filePath) + } + + public getOutputPath(filePath: string) { + return path.join(outputPath, filePath) + } + + public saveFile(filePath: string, data: Buffer) { + fs.writeFileSync(filePath, data) + return filePath + } + + public deleteFile(filePath: string) { + fs.unlinkSync(filePath) + return filePath + } + + public createOutput() { + if (!fs.existsSync(outputPath)) + fs.mkdirSync(outputPath) + } + + public createTemp() { + if (!fs.existsSync(tempPath)) + fs.mkdirSync(tempPath) + } + + public deleteTemp() { + if (fs.existsSync(tempPath)) + fs.rmSync(tempPath, { recursive: true, force: true }) + } +} \ No newline at end of file diff --git a/src/hsl/HslManager.ts b/src/hsl/HslManager.ts new file mode 100644 index 0000000..721331e --- /dev/null +++ b/src/hsl/HslManager.ts @@ -0,0 +1,8 @@ +import crypto from 'crypto' + +export class HslManager { + public decrypt(data: NodeJS.ArrayBufferView, key: Uint8Array, iv: Uint32Array) { + const cipher = crypto.createDecipheriv('aes-128-cbc', key, iv) + return cipher.update(data) + } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..5a6c719 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,309 @@ +import { HidiveDao, Resolution } from './api/HidiveDao.js' +import { CliHandler } from './cli/CliHandler.js' +import { ApiKeyDao } from './database/ApiKeyDao.js' +import { createDatabase } from './database/Database.js' +import { AccountDao } from './database/AccountDao.js' +import { FFmpeg, Subtitle } from './ffmpeg/FFmpeg.js' +import { HslManager } from './hsl/HslManager.js' +import { FileManager } from './file/FileManager.js' +import async from 'async' +import cliProgress from 'cli-progress' + +const database = createDatabase() +const accountDao = new AccountDao(database) +const apiKeyDao = new ApiKeyDao(database) +const cliHandler = new CliHandler() +const ffmpeg = new FFmpeg() +const hslManager = new HslManager() +const fileManager = new FileManager() + +fileManager.createOutput() +fileManager.deleteTemp() + +while (true) { + console.clear() + const userAction = await cliHandler.promptActions([ + { + message: 'Login', + value: 'login' + }, + { + message: 'Add API key', + value: 'addApiKey' + }, + { + message: 'Delete API key', + value: 'deleteApiKey' + }, + { + message: 'Add account', + value: 'addAccount' + }, + { + message: 'Delete account', + value: 'deleteAccount' + } + ]) + + switch (userAction) { + case 'login': { + await hidiveAction() + break + } + case 'addApiKey': { + await addApiKey() + break + } + case 'deleteApiKey': { + await deleteApiKey() + break + } + case 'addAccount': { + await addAccount() + break + } + case 'deleteAccount': { + await deleteAccount() + break + } + } +} + + +async function hidiveAction() { + const hidiveDao = await login() + try { + const pingRes = await hidiveDao.ping() + const initDeviceRes = await hidiveDao.initDevice() + const initVisitRes = await hidiveDao.initVisit() + const authenticateRes = await hidiveDao.authenticate() + + if (!hidiveDao.user || !hidiveDao.profile) + throw new Error(`Failed to authenticate response returned code ${authenticateRes.Code} with status ${authenticateRes.Status}`) + + accountDao.updateAccount(hidiveDao.options.user.id, { + ...hidiveDao.options.user, + deviceId: initDeviceRes.Data.DeviceId, + userId: hidiveDao.user.Id, + profileId: hidiveDao.profile.Id, + visitId: initVisitRes.Data.VisitId + }) + + console.log(`Successfully logged into ${hidiveDao.profile.Nickname}`) + } catch (error) { + console.error(error) + return + } + + while (true) { + console.clear() + const hidiveAction = await cliHandler.promptActions([ + { + message: 'Download a series', + value: 'download' + }, + { + message: 'Return', + value: 'return' + } + ]) + + switch (hidiveAction) { + case 'download': { + await downloadSeries(hidiveDao) + break + } + case 'return': { + return + } + } + } +} + +async function login() { + console.clear() + let users = accountDao.getAll() + let apiKeys = apiKeyDao.getAll() + + if (apiKeys.length <= 0) { + const apiKey = await cliHandler.promptGetApiKey() + apiKeyDao.createApiKey(apiKey) + apiKeys = apiKeyDao.getAll() + } + + if (users.length <= 0) { + const user = await cliHandler.promptGetAuth() + accountDao.createAccount(user) + users = accountDao.getAll() + } + + const user = await cliHandler.promptUserList(users) + const apiKey = await cliHandler.promptApiKey(apiKeys) + + const hidiveDao = new HidiveDao({ + apiKey: apiKey, + user: user + }) + + return hidiveDao +} + +async function deleteApiKey() { + const apiKey = await cliHandler.promptApiKey(apiKeyDao.getAll()) + const confirmed = await cliHandler.promptConfirm('Do you really want to delete this API key?') + if (confirmed) + apiKeyDao.deleteApiKey(apiKey.id) +} + +async function addApiKey() { + const apiKey = await cliHandler.promptGetApiKey() + apiKeyDao.createApiKey(apiKey) +} + +async function deleteAccount() { + const account = await cliHandler.promptUserList(accountDao.getAll()) + const confirmed = await cliHandler.promptConfirm('Do you really want to delete this account?') + if (confirmed) + accountDao.deleteAccount(account.id) +} + +async function addAccount() { + const account = await cliHandler.promptGetAuth() + accountDao.createAccount(account) +} + +async function downloadSeries(hidiveDao: HidiveDao) { + console.clear() + const searchQuery = await cliHandler.promptInput('What\'s your series called?') + const searchResultsRes = await hidiveDao.search(searchQuery) + + if (searchResultsRes.Data.TitleResults.length <= 0) { + console.log(`Oh no! No series named ${searchQuery} or similar was found.`) + + const retry = await cliHandler.promptConfirm('Do you want to search again?') + if (retry) + await downloadSeries(hidiveDao) + return + } + + const selectedSeries = await cliHandler.promptSeriesList(searchResultsRes.Data.TitleResults) + const getTitleRes = await hidiveDao.getTitle(selectedSeries.Id) + const checkedEpisodes = await cliHandler.promptEpisodesList(getTitleRes.Data.Title.Episodes) + const concurrentDownloads = await cliHandler.promptConcurrentDownloads() + if (checkedEpisodes.length <= 0) + return + + let standardVideoLanguage: string | undefined + let standardMediaResolution: Resolution | undefined + let resolveVideoLanguage: (language: string) => void + let resolveMediaResolution: (resolution: Resolution) => void + const videoLanguagePromise = new Promise(res => resolveVideoLanguage = (language) => { + standardVideoLanguage = language, + res(undefined) + }) + const mediaResolutionPromise = new Promise(res => resolveMediaResolution = (resolution) => { + standardMediaResolution = resolution, + res(undefined) + }) + + const keys: { [key: string]: Uint8Array } = {} + + const multiBar = new cliProgress.MultiBar({ + clearOnComplete: true, + format: '{bar} | {value}/{total} | {download} Mb/s | {episode}' + }, cliProgress.Presets.rect) + + await async.eachLimit(checkedEpisodes, concurrentDownloads, async (episode) => { + fileManager.createTemp() + const getVideoRes = await hidiveDao.getVideos(episode.TitleId, episode.VideoKey) + + const { Data } = getVideoRes + const { VideoLanguages, CaptionLanguages, VideoUrls, CaptionVttUrls, FontSize, VideoLanguage } = Data + + if (!standardVideoLanguage && checkedEpisodes.indexOf(episode) === 0) { + resolveVideoLanguage(await cliHandler.promptVideoLanguage(VideoLanguages)) + } + await videoLanguagePromise + + let videoLanguage = standardVideoLanguage + let videoUrl = VideoUrls[standardVideoLanguage || ''] + if (!videoUrl) { + videoUrl = VideoUrls[VideoLanguage] + videoLanguage = VideoLanguage + } + + const mediaSourcesRes = await hidiveDao.getMediaSources(videoUrl.hls[0]) + + if (!standardMediaResolution && checkedEpisodes.indexOf(episode) === 0) { + resolveMediaResolution(await cliHandler.promptResolutionFromMedia(mediaSourcesRes.playlists.map(playlist => playlist.attributes))) + console.clear() + } + await mediaResolutionPromise + + let videoResolution = standardMediaResolution + let playlist = mediaSourcesRes.playlists.find(playlist => playlist.attributes.RESOLUTION === standardMediaResolution) + if (!playlist) { + videoResolution = mediaSourcesRes.playlists[0].attributes.RESOLUTION + playlist = mediaSourcesRes.playlists[0] + } + + const episodeFileName = Array.from(`[E${`${episode.EpisodeNumberValue}`.padStart(2, '0')}S${`${episode.SeasonNumber}`.padStart(2, '0')}] ${selectedSeries.Name} - ${episode.Name} [${videoResolution?.height}p] [${videoLanguage}]`.matchAll(/[^/./\\:*?\"<>|]/g)).join('') + const mediasRes = await hidiveDao.getMedia(playlist.uri) + const tempEpisodePath = fileManager.getTempPath(`${episodeFileName}.mkv`) + const episodePath = fileManager.getOutputPath(`${episodeFileName}.mkv`) + + const ffmpegConvertVideoProcess = ffmpeg.convertVideo(tempEpisodePath) + + const bar = multiBar.create(mediasRes.segments.length, 0, { download: 0, episode: `${selectedSeries.Name} - ${episode.Name}` }) + + const startTime = Date.now() + let bytesDownloaded = 0 + let segmentsDownloaded = 0 + + for (const segment of mediasRes.segments) { + let segmentData = await hidiveDao.getTs(segment.uri) + + if (segment.key) { + const { uri, iv } = segment.key + + let key = keys[uri] + if (!key) + key = keys[uri] = await hidiveDao.getKey(uri) + + segmentData = hslManager.decrypt(segmentData, key, iv) + } + + bytesDownloaded += segmentData.byteLength + segmentsDownloaded++ + ffmpegConvertVideoProcess.write(segmentData) + bar.update(segmentsDownloaded, { download: ((bytesDownloaded / 1000000) / ((Date.now() - startTime) / 1000)).toFixed(2), episode: `${selectedSeries.Name} - ${episode.Name}` }) + } + await ffmpegConvertVideoProcess.end() + + const subtitles: { + name: string, + value: Buffer + }[] = await Promise.all(Object.entries(CaptionVttUrls) + .map(async ([name, url]) => { + return { + name, + value: Buffer.from(await hidiveDao.getSub(url)) + } + })) + + const subtitlePaths: Subtitle[] = await Promise.all(subtitles + .map(async (subtitle) => { + return { + name: subtitle.name, + path: await ffmpeg + .convertSubtitle(fileManager + .getTempPath(`${episodeFileName}[${subtitle.name}].srt`), subtitle.value, subtitle.name) + } + })) + + await ffmpeg.mergeVideoAndSubtitles(episodePath, tempEpisodePath, subtitlePaths) + }) + fileManager.deleteTemp() +} + +export { } \ No newline at end of file diff --git a/src/models/ApiKey.ts b/src/models/ApiKey.ts new file mode 100644 index 0000000..35b1c09 --- /dev/null +++ b/src/models/ApiKey.ts @@ -0,0 +1,4 @@ +export interface ApiKey { + id: number, + key: string, +} \ No newline at end of file diff --git a/src/models/User.ts b/src/models/User.ts new file mode 100644 index 0000000..e83127a --- /dev/null +++ b/src/models/User.ts @@ -0,0 +1,9 @@ +export interface User { + id: number, + email: string, + password: string, + userId?: number, + profileId?: number, + deviceId?: string, + visitId?: string +} \ No newline at end of file diff --git a/src/util/utils.ts b/src/util/utils.ts new file mode 100644 index 0000000..3b5cc28 --- /dev/null +++ b/src/util/utils.ts @@ -0,0 +1 @@ +export const sleep = (ms: number) => new Promise((res) => setTimeout(() => res, ms)) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f8ec739 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,105 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "ES2022", /* Specify what module code is generated. */ + "rootDir": "./src", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..7e88ed5 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,890 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.12.5": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" + integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== + dependencies: + regenerator-runtime "^0.13.4" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@types/async@^3.2.15": + version "3.2.15" + resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.15.tgz#26d4768fdda0e466f18d6c9918ca28cc89a4e1fe" + integrity sha512-PAmPfzvFA31mRoqZyTVsgJMsvbynR429UTTxhmfsUCrWGh3/fxOrzqBtaTPJsn4UtzTv4Vb0+/O7CARWb69N4g== + +"@types/better-sqlite3@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-7.6.0.tgz#603d1c7a72527dd946e2bf641ed4c0b74a547423" + integrity sha512-rnSP9vY+fVsF3iJja5yRGBJV63PNBiezJlYrCkqUmQWFoB16cxAHwOkjsAYEu317miOfKaJpa65cbp0P4XJ/jw== + dependencies: + "@types/node" "*" + +"@types/cli-progress@^3.11.0": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.11.0.tgz#ec79df99b26757c3d1c7170af8422e0fc95eef7e" + integrity sha512-XhXhBv1R/q2ahF3BM7qT5HLzJNlIL0wbcGyZVjqOTqAybAnsLisd7gy1UCyIqpL+5Iv6XhlSyzjLCnI2sIdbCg== + dependencies: + "@types/node" "*" + +"@types/ffmpeg@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/ffmpeg/-/ffmpeg-1.0.4.tgz#e82c080f7846de13e17bf0a2d49dd841651b8686" + integrity sha512-USbiJIZRuoO1n/HUTkxCPH0hvjVUOLUbkicwbh+9GOjwg0IFZOU0aDVMJV0A27nC42Bx6rujunU6pfdjw866JQ== + +"@types/fs-extra@^9.0.13": + version "9.0.13" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" + integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== + dependencies: + "@types/node" "*" + +"@types/inquirer@^9.0.2": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-9.0.2.tgz#846b141c17608aad9fc62572280753bb6ec09184" + integrity sha512-MQc3adiIh/rDxLkjnvL03rSvuHg+/dKif4dn/MRsnE+oU1bAdyuDbW0w+ewR1M/M/u/Z0YAbw7NZYCpgQ5SW8A== + dependencies: + "@types/through" "*" + rxjs "^7.2.0" + +"@types/node@*", "@types/node@^18.8.0": + version "18.8.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.0.tgz#b8ee8d83a99470c0661bd899417fcd77060682fe" + integrity sha512-u+h43R6U8xXDt2vzUaVP3VwjjLyOJk6uEciZS8OSyziUQGOwmk+l+4drxcsDboHXwyTaqS1INebghmWMRxq3LA== + +"@types/through@*": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + dependencies: + "@types/node" "*" + +"@videojs/vhs-utils@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz#665ba70d78258ba1ab977364e2fe9f4d4799c46c" + integrity sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw== + dependencies: + "@babel/runtime" "^7.12.5" + global "^4.4.0" + url-toolkit "^2.2.1" + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.1.tgz#63cd61c72283a71cb30bd881dbb60adada74bc70" + integrity sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +async@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +better-sqlite3@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-7.6.2.tgz#47cd8cad5b9573cace535f950ac321166bc31384" + integrity sha512-S5zIU1Hink2AH4xPsN0W43T1/AJ5jrPh7Oy07ocuW/AKYYY02GWzz9NH0nbSMn/gw6fDZ5jZ1QsHt1BXAwJ6Lg== + dependencies: + bindings "^1.5.0" + prebuild-install "^7.1.0" + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bl@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-5.0.0.tgz#6928804a41e9da9034868e1c50ca88f21f57aea2" + integrity sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +chalk@^5.0.0, chalk@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" + integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-progress@^3.11.2: + version "3.11.2" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.11.2.tgz#f8c89bd157e74f3f2c43bcfb3505670b4d48fc77" + integrity sha512-lCPoS6ncgX4+rJu5bS3F/iCz17kZ9MPZ6dpuTtI0KXKABkhyXIdYB3Inby1OpaGti3YlI3EeEkM9AuWpelJrVA== + dependencies: + string-width "^4.2.3" + +cli-spinners@^2.6.1: + version "2.7.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + +cli-width@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6" + integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw== + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== + dependencies: + clone "^1.0.2" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +figures@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-5.0.0.tgz#126cd055052dea699f8a54e8c9450e6ecfc44d5f" + integrity sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg== + dependencies: + escape-string-regexp "^5.0.0" + is-unicode-supported "^1.2.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +follow-redirects@^1.14.9: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +global@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inquirer@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.1.2.tgz#37f5486f3de0e38820aad83a1f75c52c747e2f9a" + integrity sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg== + dependencies: + ansi-escapes "^5.0.0" + chalk "^5.0.1" + cli-cursor "^4.0.0" + cli-width "^4.0.0" + external-editor "^3.0.3" + figures "^5.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^6.1.2" + run-async "^2.4.0" + rxjs "^7.5.6" + string-width "^5.1.2" + strip-ansi "^7.0.1" + through "^2.3.6" + wrap-ansi "^8.0.1" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-unicode-supported@^1.1.0, is-unicode-supported@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" + integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== + dependencies: + chalk "^5.0.0" + is-unicode-supported "^1.1.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +m3u8-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-6.0.0.tgz#e9143313b44f07bb25fdea1c8aac1098d9ada192" + integrity sha512-s3JfDtqhxTilZQf+P1m9dZc4ohL4O/aylP1VV6g9lhKuQNfAcVUzq7d2wgJ9nZR4ibjuXaP87QzGCV6vB0kV6g== + dependencies: + "@babel/runtime" "^7.12.5" + "@videojs/vhs-utils" "^3.0.5" + global "^4.4.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== + dependencies: + dom-walk "^0.1.0" + +minimist@^1.2.0, minimist@^1.2.3: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +node-abi@^3.3.0: + version "3.25.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.25.0.tgz#ca57dd23ae67679ce152b6c45cae2c57ed04faff" + integrity sha512-p+0xx5ruIQ+8X57CRIMxbTZRT7tU0Tjn2C/aAK68AEMrbGsCo6IjnDdPNhEyyjWCT4bRtzomXchYd3sSgk3BJQ== + dependencies: + semver "^7.3.5" + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +ora@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/ora/-/ora-6.1.2.tgz#7b3c1356b42fd90fb1dad043d5dbe649388a0bf5" + integrity sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw== + dependencies: + bl "^5.0.0" + chalk "^5.0.0" + cli-cursor "^4.0.0" + cli-spinners "^2.6.1" + is-interactive "^2.0.0" + is-unicode-supported "^1.1.0" + log-symbols "^5.1.0" + strip-ansi "^7.0.1" + wcwidth "^1.0.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +prebuild-install@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rxjs@^7.2.0, rxjs@^7.5.6: + version "7.5.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" + integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== + dependencies: + tslib "^2.1.0" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^7.3.5: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tslib@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +typescript@^4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +url-toolkit@^2.2.1: + version "2.2.5" + resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.2.5.tgz#58406b18e12c58803e14624df5e374f638b0f607" + integrity sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +wrap-ansi@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.0.1.tgz#2101e861777fec527d0ea90c57c6b03aac56a5b3" + integrity sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==