From a27c0209c1adbc352c820b99cab26fda24b7a411 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sat, 23 Dec 2023 20:28:37 -0800 Subject: [PATCH 01/69] Initial support for mpd --- @types/mpd-parser.d.ts | 71 +++++ crunchy.ts | 443 ++++++++++++++++++++++++-------- modules/hls-download.ts | 2 +- modules/module.transform-mpd.ts | 109 ++++++++ package.json | 1 + pnpm-lock.yaml | 27 ++ 6 files changed, 546 insertions(+), 107 deletions(-) create mode 100644 @types/mpd-parser.d.ts create mode 100644 modules/module.transform-mpd.ts diff --git a/@types/mpd-parser.d.ts b/@types/mpd-parser.d.ts new file mode 100644 index 0000000..516cf2a --- /dev/null +++ b/@types/mpd-parser.d.ts @@ -0,0 +1,71 @@ +declare module 'mpd-parser' { + export type Segment = { + uri: string, + timeline: number, + duration: number, + resolvedUri: string, + map: { + uri: string, + resolvedUri: string, + }, + number: number, + presentationTime: number + } + + export type Playlist = { + attributes: { + NAME: string, + BANDWIDTH: number, + CODECS: string, + 'PROGRAM-ID': number, + // Following for video only + 'FRAME-RATE'?: number, + AUDIO?: string, // audio stream name + SUBTITLES?: string, + RESOLUTION?: { + width: number, + height: number + } + }, + uri: string, + endList: boolean, + timeline: number, + resolvedUri: string, + targetDuration: number, + discontinuitySequence: number, + discontinuityStarts: [], + timelineStarts: { + start: number, + timeline: number + }[], + mediaSequence: number, + contentProtection?: { + [type: string]: { + pssh?: Uint8Array + } + } + segments: Segment[] + } + + export type Manifest = { + allowCache: boolean, + discontinuityStarts: [], + segments: [], + endList: true, + duration: number, + playlists: Playlist[], + mediaGroups: { + AUDIO: { + audio: { + [name: string]: { + language: string, + autoselect: boolean, + default: boolean, + playlists: Playlist[] + } + } + } + } + } + export function parse(manifest: string): Manifest +} diff --git a/crunchy.ts b/crunchy.ts index c1c0900..409b578 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -8,7 +8,7 @@ import packageJson from './package.json'; import { console } from './modules/log'; import shlp from 'sei-helper'; import m3u8 from 'm3u8-parsed'; -import streamdl from './modules/hls-download'; +import streamdl, { M3U8Json } from './modules/hls-download'; // custom modules import * as fontsData from './modules/module.fontsData'; @@ -35,6 +35,7 @@ import { AuthData, AuthResponse, Episode, ResponseBase, SearchData, SearchRespon import { ServiceClass } from './@types/serviceClassInterface'; import { CrunchyAndroidStreams } from './@types/crunchyAndroidStreams'; import { CrunchyAndroidEpisodes, CrunchyEpisode } from './@types/crunchyAndroidEpisodes'; +import { parse } from './modules/module.transform-mpd'; export type sxItem = { language: langsData.LanguageItem, @@ -1165,8 +1166,8 @@ export default class Crunchy implements ServiceClass { const pbStreams = pbData.streams; for(const s of Object.keys(pbStreams)){ - if(s.match(/hls/) && !s.match(/drm/) && !s.match(/trailer/)) { - //if((s.match(/hls/) || s.match(/dash/)) && !s.match(/trailer/)) { + //if(s.match(/hls/) && !s.match(/drm/) && !s.match(/trailer/)) { + if((s.match(/hls/) || s.match(/dash/)) && !s.match(/trailer/)) { const pb = Object.values(pbStreams[s]).map(v => { v.hardsub_lang = v.hardsub_locale ? langsData.fixAndFindCrLC(v.hardsub_locale).locale @@ -1267,120 +1268,139 @@ export default class Crunchy implements ServiceClass { dlFailed = true; } else{ - const streamPlaylists = m3u8(streamPlaylistsReq.res.body); - const plServerList: string[] = [], - plStreams: Record> = {}, - plQuality: { - str: string, - dim: string, - CODECS: string, - RESOLUTION: { - width: number, - height: number - } - }[] = []; - for(const pl of streamPlaylists.playlists){ - // set quality - const plResolution = pl.attributes.RESOLUTION; - const plResolutionText = `${plResolution.width}x${plResolution.height}`; - // set codecs - const plCodecs = pl.attributes.CODECS; - // parse uri - const plUri = new URL(pl.uri); - let plServer = plUri.hostname; - // set server list - if(plUri.searchParams.get('cdn')){ - plServer += ` (${plUri.searchParams.get('cdn')})`; - } - if(!plServerList.includes(plServer)){ - plServerList.push(plServer); - } - // add to server - if(!Object.keys(plStreams).includes(plServer)){ - plStreams[plServer] = {}; - } - if( - plStreams[plServer][plResolutionText] - && plStreams[plServer][plResolutionText] != pl.uri - && typeof plStreams[plServer][plResolutionText] != 'undefined' - ){ - console.error(`Non duplicate url for ${plServer} detected, please report to developer!`); - } - else{ - plStreams[plServer][plResolutionText] = pl.uri; - } - // set plQualityStr - const plBandwidth = Math.round(pl.attributes.BANDWIDTH/1024); - const qualityStrAdd = `${plResolutionText} (${plBandwidth}KiB/s)`; - const qualityStrRegx = new RegExp(qualityStrAdd.replace(/(:|\(|\)|\/)/g, '\\$1'), 'm'); - const qualityStrMatch = !plQuality.map(a => a.str).join('\r\n').match(qualityStrRegx); - if(qualityStrMatch){ - plQuality.push({ - str: qualityStrAdd, - dim: plResolutionText, - CODECS: plCodecs, - RESOLUTION: plResolution - }); - } - } + if (streamPlaylistsReq.res.body.match('MPD')) { + //Parse MPD Playlists + const streamPlaylists = parse(streamPlaylistsReq.res.body, langsData.findLang(langsData.fixLanguageTag(pbData.audio_locale as string) || '')); + const videoQuality: { + bandwidth: number, + str: string, + dim: string, + RESOLUTION: { + width: number, + height: number + } + }[] = [], audioQuality: { + bandwidth: number, + str: string, + dim: string, + RESOLUTION: number + }[] = []; - options.x = options.x > plServerList.length ? 1 : options.x; + //Get name of CDNs/Servers + const streamServers = Object.keys(streamPlaylists); - const plSelectedServer = plServerList[options.x - 1]; - const plSelectedList = plStreams[plSelectedServer]; - plQuality.sort((a, b) => { - const aMatch: RegExpMatchArray | never[] = a.dim.match(/[0-9]+/) || []; - const bMatch: RegExpMatchArray | never[] = b.dim.match(/[0-9]+/) || []; - return parseInt(aMatch[0]) - parseInt(bMatch[0]); - }); - let quality = options.q === 0 ? plQuality.length : options.q; - if(quality > plQuality.length) { - console.warn(`The requested quality of ${options.q} is greater than the maximun ${plQuality.length}.\n[WARN] Therefor the maximum will be capped at ${plQuality.length}.`); - quality = plQuality.length; - } - // When best selected video quality is already downloaded - if(dlVideoOnce && options.dlVideoOnce) { - // Select the lowest resolution with the same codecs - while(quality !=1 && plQuality[quality - 1].CODECS == plQuality[quality - 2].CODECS) { - quality--; + options.x = options.x > streamServers.length ? 1 : options.x; + + const selectedServer = streamServers[options.x - 1]; + const selectedList = streamPlaylists[selectedServer]; + + //set Video Qualities + selectedList.video.forEach(function(playlist) { + if (playlist.type == 'video') { + const bandwidth = Math.round(playlist.bandwidth/1024); + const resolutionText = `${playlist.quality.width}x${playlist.quality.height}`; + const resolutionTextAdd = `${resolutionText} (${bandwidth}KiB/s)`; + videoQuality.push({ + bandwidth: bandwidth, + str: resolutionTextAdd, + dim: resolutionText, + RESOLUTION: playlist.quality + }); + } else { + console.warn('Found non-video in video typed stream. Skipping...'); + } + }); + + //set Audio Qualities + selectedList.audio.forEach(function(playlist) { + if (playlist.type == 'audio') { + const bandwidth = Math.round(playlist.bandwidth/1000); + const resolutionText = `${bandwidth}Kb/s`; + audioQuality.push({ + bandwidth: bandwidth, + str: resolutionText, + dim: resolutionText, + RESOLUTION: playlist.bandwidth + }); + } else { + console.warn('Found non-audio in audio typed stream. Skipping...'); + } + }); + + videoQuality.sort((a, b) => { + const aMatch: RegExpMatchArray | never[] = a.dim.match(/[0-9]+/) || []; + const bMatch: RegExpMatchArray | never[] = b.dim.match(/[0-9]+/) || []; + return parseInt(aMatch[0]) - parseInt(bMatch[0]); + }); + + audioQuality.sort((a, b) => { + const aMatch: RegExpMatchArray | never[] = a.dim.match(/[0-9]+/) || []; + const bMatch: RegExpMatchArray | never[] = b.dim.match(/[0-9]+/) || []; + return parseInt(aMatch[0]) - parseInt(bMatch[0]); + }); + + let chosenVideoQuality = options.q === 0 ? videoQuality.length : options.q; + if(chosenVideoQuality > videoQuality.length) { + console.warn(`The requested quality of ${options.q} is greater than the maximum ${videoQuality.length}.\n[WARN] Therefor the maximum will be capped at ${videoQuality.length}.`); + chosenVideoQuality = videoQuality.length; } - } - const selPlUrl = plSelectedList[plQuality.map(a => a.dim)[quality - 1]] ? plSelectedList[plQuality.map(a => a.dim)[quality - 1]] : ''; - console.info(`Servers available:\n\t${plServerList.join('\n\t')}`); - console.info(`Available qualities:\n\t${plQuality.map((a, ind) => `[${ind+1}] ${a.str}`).join('\n\t')}`); - if(selPlUrl != ''){ + let chosenAudioQuality = options.q === 0 ? audioQuality.length : options.q; + if(chosenAudioQuality > audioQuality.length) { + chosenAudioQuality = audioQuality.length; + } + + + //TODO: fix this, lol, the below code I thought would work so that it actually chooses the right resolution but it didn't work and I need sleep + /*const chosenVideoSegments = selectedList.video.map(function(playlist) { + if (playlist.bandwidth == videoQuality[chosenVideoQuality - 1].bandwidth) { + return playlist; + } + }); + const chosenAudioSegments = selectedList.audio.map(function(playlist) { + if (playlist.bandwidth == audioQuality[chosenAudioQuality - 1].bandwidth) { + return playlist; + } + });*/ + + const chosenVideoSegments = selectedList.video[chosenVideoQuality - 1]; + const chosenAudioSegments = selectedList.audio[chosenAudioQuality - 1]; + + console.info(`Servers available:\n\t${streamServers.join('\n\t')}`); + console.info(`Available Video Qualities:\n\t${videoQuality.map((a, ind) => `[${ind+1}] ${a.str}`).join('\n\t')}`); + console.info(`Available Audio Qualities:\n\t${audioQuality.map((a, ind) => `[${ind+1}] ${a.str}`).join('\n\t')}`); + variables.push({ name: 'height', type: 'number', - replaceWith: quality === 0 ? plQuality[plQuality.length - 1].RESOLUTION.height as number : plQuality[quality - 1].RESOLUTION.height + replaceWith: chosenVideoQuality === 0 ? videoQuality[videoQuality.length - 1].RESOLUTION.height as number : videoQuality[chosenVideoQuality - 1].RESOLUTION.height }, { name: 'width', type: 'number', - replaceWith: quality === 0 ? plQuality[plQuality.length - 1].RESOLUTION.width as number : plQuality[quality - 1].RESOLUTION.width + replaceWith: chosenVideoQuality === 0 ? videoQuality[videoQuality.length - 1].RESOLUTION.width as number : videoQuality[chosenVideoQuality - 1].RESOLUTION.width }); + const lang = langsData.languages.find(a => a.code === curStream?.audio_lang); if (!lang) { console.error(`Unable to find language for code ${curStream.audio_lang}`); return; } - console.info(`Selected quality: ${Object.keys(plSelectedList).find(a => plSelectedList[a] === selPlUrl)} @ ${plSelectedServer}`); - console.info('Stream URL:', selPlUrl); + console.info(`Selected quality: \n\tVideo: ${videoQuality.map(a => a.dim)[chosenVideoQuality - 1]}\n\tAudio: ${audioQuality.map(a => a.dim)[chosenAudioQuality - 1]}\n\tServer: ${selectedServer}`); + console.info('Stream URL:', chosenVideoSegments.segments[0].uri.split(',.urlset')[0]); // TODO check filename fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep); const outFile = parseFileName(options.fileName + '.' + (mMeta.lang?.name || lang.name), variables, options.numbers, options.override).join(path.sep); - console.info(`Output filename: ${outFile}`); - const chunkPage = await this.req.getData(selPlUrl); - if(!chunkPage.ok || !chunkPage.res){ - console.error('CAN\'T FETCH VIDEO PLAYLIST!'); - dlFailed = true; - } - else{ - const chunkPlaylist = m3u8(chunkPage.res.body); - const totalParts = chunkPlaylist.segments.length; + + + // When best selected video quality is already downloaded + if(dlVideoOnce && options.dlVideoOnce) { + console.info('Already downloaded video, skipping video download...'); + } else { + //Download Video + const totalParts = chosenVideoSegments.segments.length; const mathParts = Math.ceil(totalParts / options.partsize); const mathMsg = `(${mathParts}*${options.partsize})`; - console.info('Total parts in stream:', totalParts, mathMsg); + console.info('Total parts in video stream:', totalParts, mathMsg); tsFile = path.isAbsolute(outFile as string) ? outFile : path.join(this.cfg.dir.content, outFile); const split = outFile.split(path.sep).slice(0, -1); split.forEach((val, ind, arr) => { @@ -1388,10 +1408,13 @@ export default class Crunchy implements ServiceClass { if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); }); - const dlStreamByPl = await new streamdl({ - output: `${tsFile}.ts`, + const videoJson: M3U8Json = { + segments: chosenVideoSegments.segments + }; + const videoDownload = await new streamdl({ + output: `${tsFile}.video.ts`, timeout: options.timeout, - m3u8json: chunkPlaylist, + m3u8json: videoJson, // baseurl: chunkPlaylist.baseUrl, threads: options.partsize, fsRetryTime: options.fsRetryTime * 1000, @@ -1406,22 +1429,230 @@ export default class Crunchy implements ServiceClass { language: lang }) : undefined }).download(); - if(!dlStreamByPl.ok){ - console.error(`DL Stats: ${JSON.stringify(dlStreamByPl.parts)}\n`); + if(!videoDownload.ok){ + console.error(`DL Stats: ${JSON.stringify(videoDownload.parts)}\n`); dlFailed = true; } files.push({ type: 'Video', - path: `${tsFile}.ts`, + path: `${tsFile}.video.ts`, lang: lang, isPrimary: isPrimary }); dlVideoOnce = true; } - } - else{ - console.error('Quality not selected!\n'); - dlFailed = true; + + if (chosenAudioSegments) { + //Download Audio (if available) + const totalParts = chosenAudioSegments.segments.length; + const mathParts = Math.ceil(totalParts / options.partsize); + const mathMsg = `(${mathParts}*${options.partsize})`; + console.info('Total parts in audio stream:', totalParts, mathMsg); + tsFile = path.isAbsolute(outFile as string) ? outFile : path.join(this.cfg.dir.content, outFile); + const split = outFile.split(path.sep).slice(0, -1); + split.forEach((val, ind, arr) => { + const isAbsolut = path.isAbsolute(outFile as string); + if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) + fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); + }); + const audioJson: M3U8Json = { + segments: chosenAudioSegments.segments + }; + const audioDownload = await new streamdl({ + output: `${tsFile}.audio.ts`, + timeout: options.timeout, + m3u8json: audioJson, + // baseurl: chunkPlaylist.baseUrl, + threads: options.partsize, + fsRetryTime: options.fsRetryTime * 1000, + override: options.force, + callback: options.callbackMaker ? options.callbackMaker({ + fileName: `${path.isAbsolute(outFile) ? outFile.slice(this.cfg.dir.content.length) : outFile}`, + image: medias.image, + parent: { + title: medias.seasonTitle + }, + title: medias.episodeTitle, + language: lang + }) : undefined + }).download(); + if(!audioDownload.ok){ + console.error(`DL Stats: ${JSON.stringify(audioDownload.parts)}\n`); + dlFailed = true; + } + files.push({ + type: 'Video', + path: `${tsFile}.audio.ts`, + lang: lang, + isPrimary: isPrimary + }); + } + + //Handle Decryption if needed + if (chosenVideoSegments.pssh) { + console.info('Decryption of Video Needed'); + } + + if (chosenAudioSegments.pssh) { + console.info('Decryption of Audio Needed'); + } + } else { + const streamPlaylists = m3u8(streamPlaylistsReq.res.body); + const plServerList: string[] = [], + plStreams: Record> = {}, + plQuality: { + str: string, + dim: string, + CODECS: string, + RESOLUTION: { + width: number, + height: number + } + }[] = []; + for(const pl of streamPlaylists.playlists){ + // set quality + const plResolution = pl.attributes.RESOLUTION; + const plResolutionText = `${plResolution.width}x${plResolution.height}`; + // set codecs + const plCodecs = pl.attributes.CODECS; + // parse uri + const plUri = new URL(pl.uri); + let plServer = plUri.hostname; + // set server list + if(plUri.searchParams.get('cdn')){ + plServer += ` (${plUri.searchParams.get('cdn')})`; + } + if(!plServerList.includes(plServer)){ + plServerList.push(plServer); + } + // add to server + if(!Object.keys(plStreams).includes(plServer)){ + plStreams[plServer] = {}; + } + if( + plStreams[plServer][plResolutionText] + && plStreams[plServer][plResolutionText] != pl.uri + && typeof plStreams[plServer][plResolutionText] != 'undefined' + ){ + console.error(`Non duplicate url for ${plServer} detected, please report to developer!`); + } + else{ + plStreams[plServer][plResolutionText] = pl.uri; + } + // set plQualityStr + const plBandwidth = Math.round(pl.attributes.BANDWIDTH/1024); + const qualityStrAdd = `${plResolutionText} (${plBandwidth}KiB/s)`; + const qualityStrRegx = new RegExp(qualityStrAdd.replace(/(:|\(|\)|\/)/g, '\\$1'), 'm'); + const qualityStrMatch = !plQuality.map(a => a.str).join('\r\n').match(qualityStrRegx); + if(qualityStrMatch){ + plQuality.push({ + str: qualityStrAdd, + dim: plResolutionText, + CODECS: plCodecs, + RESOLUTION: plResolution + }); + } + } + + options.x = options.x > plServerList.length ? 1 : options.x; + + const plSelectedServer = plServerList[options.x - 1]; + const plSelectedList = plStreams[plSelectedServer]; + plQuality.sort((a, b) => { + const aMatch: RegExpMatchArray | never[] = a.dim.match(/[0-9]+/) || []; + const bMatch: RegExpMatchArray | never[] = b.dim.match(/[0-9]+/) || []; + return parseInt(aMatch[0]) - parseInt(bMatch[0]); + }); + let quality = options.q === 0 ? plQuality.length : options.q; + if(quality > plQuality.length) { + console.warn(`The requested quality of ${options.q} is greater than the maximum ${plQuality.length}.\n[WARN] Therefor the maximum will be capped at ${plQuality.length}.`); + quality = plQuality.length; + } + // When best selected video quality is already downloaded + if(dlVideoOnce && options.dlVideoOnce) { + // Select the lowest resolution with the same codecs + while(quality !=1 && plQuality[quality - 1].CODECS == plQuality[quality - 2].CODECS) { + quality--; + } + } + const selPlUrl = plSelectedList[plQuality.map(a => a.dim)[quality - 1]] ? plSelectedList[plQuality.map(a => a.dim)[quality - 1]] : ''; + console.info(`Servers available:\n\t${plServerList.join('\n\t')}`); + console.info(`Available qualities:\n\t${plQuality.map((a, ind) => `[${ind+1}] ${a.str}`).join('\n\t')}`); + + if(selPlUrl != ''){ + variables.push({ + name: 'height', + type: 'number', + replaceWith: quality === 0 ? plQuality[plQuality.length - 1].RESOLUTION.height as number : plQuality[quality - 1].RESOLUTION.height + }, { + name: 'width', + type: 'number', + replaceWith: quality === 0 ? plQuality[plQuality.length - 1].RESOLUTION.width as number : plQuality[quality - 1].RESOLUTION.width + }); + const lang = langsData.languages.find(a => a.code === curStream?.audio_lang); + if (!lang) { + console.error(`Unable to find language for code ${curStream.audio_lang}`); + return; + } + console.info(`Selected quality: ${Object.keys(plSelectedList).find(a => plSelectedList[a] === selPlUrl)} @ ${plSelectedServer}`); + console.info('Stream URL:', selPlUrl); + // TODO check filename + fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep); + const outFile = parseFileName(options.fileName + '.' + (mMeta.lang?.name || lang.name), variables, options.numbers, options.override).join(path.sep); + console.info(`Output filename: ${outFile}`); + const chunkPage = await this.req.getData(selPlUrl); + if(!chunkPage.ok || !chunkPage.res){ + console.error('CAN\'T FETCH VIDEO PLAYLIST!'); + dlFailed = true; + } + else{ + const chunkPlaylist = m3u8(chunkPage.res.body); + const totalParts = chunkPlaylist.segments.length; + const mathParts = Math.ceil(totalParts / options.partsize); + const mathMsg = `(${mathParts}*${options.partsize})`; + console.info('Total parts in stream:', totalParts, mathMsg); + tsFile = path.isAbsolute(outFile as string) ? outFile : path.join(this.cfg.dir.content, outFile); + const split = outFile.split(path.sep).slice(0, -1); + split.forEach((val, ind, arr) => { + const isAbsolut = path.isAbsolute(outFile as string); + if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) + fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); + }); + const dlStreamByPl = await new streamdl({ + output: `${tsFile}.ts`, + timeout: options.timeout, + m3u8json: chunkPlaylist, + // baseurl: chunkPlaylist.baseUrl, + threads: options.partsize, + fsRetryTime: options.fsRetryTime * 1000, + override: options.force, + callback: options.callbackMaker ? options.callbackMaker({ + fileName: `${path.isAbsolute(outFile) ? outFile.slice(this.cfg.dir.content.length) : outFile}`, + image: medias.image, + parent: { + title: medias.seasonTitle + }, + title: medias.episodeTitle, + language: lang + }) : undefined + }).download(); + if(!dlStreamByPl.ok){ + console.error(`DL Stats: ${JSON.stringify(dlStreamByPl.parts)}\n`); + dlFailed = true; + } + files.push({ + type: 'Video', + path: `${tsFile}.ts`, + lang: lang, + isPrimary: isPrimary + }); + dlVideoOnce = true; + } + } + else{ + console.error('Quality not selected!\n'); + dlFailed = true; + } } } } diff --git a/modules/hls-download.ts b/modules/hls-download.ts index 3b2ce0c..38ba40c 100644 --- a/modules/hls-download.ts +++ b/modules/hls-download.ts @@ -26,7 +26,7 @@ const fixMiddleWare = (res: Response) => { export type HLSCallback = (data: ProgressData) => unknown; -type M3U8Json = { +export type M3U8Json = { segments: Record[], mediaSequence?: number, } diff --git a/modules/module.transform-mpd.ts b/modules/module.transform-mpd.ts new file mode 100644 index 0000000..9fc93b4 --- /dev/null +++ b/modules/module.transform-mpd.ts @@ -0,0 +1,109 @@ +import { Playlist, parse as mpdParse } from 'mpd-parser'; +import { LanguageItem } from './module.langsData'; + +type Segment = { + uri: string; + timeline: number; + duration: number; + map: { + uri: string; + }; + number: number; + presentationTime: number; +} + +type PlayListTypeItem = { + type: 'audio', + language: LanguageItem +} | { + type: 'video', + quality: { + width: number, + height: number + } +} + +export type PlaylistItem = { + pssh?: string, + bandwidth: number, + segments: Segment[] +} & PlayListTypeItem + +export type MPDParsed = { + [server: string]: { + [type in 'audio'|'video']: PlaylistItem[] + } +} + +export function parse(manifest: string, language: LanguageItem) { + const parsed = mpdParse(manifest); + const ret: MPDParsed = {}; + + for (const item of Object.values(parsed.mediaGroups.AUDIO.audio)){ + for (const playlist of item.playlists) { + const host = new URL(playlist.resolvedUri).hostname; + if (!Object.prototype.hasOwnProperty.call(ret, host)) + ret[host] = { audio: [], video: [] }; + + const pItem: PlaylistItem = { + bandwidth: playlist.attributes.BANDWIDTH, + language: language, + type: 'audio', + segments: playlist.segments.map((segment): Segment => { + const uri = segment.resolvedUri; + const map_uri = segment.map.resolvedUri; + return { + duration: segment.duration, + map: { uri: map_uri }, + number: segment.number, + presentationTime: segment.presentationTime, + timeline: segment.timeline, + uri + }; + }) + }; + + if (playlist.contentProtection && + playlist.contentProtection?.['com.widevine.alpha'].pssh) + pItem.pssh = arrayBufferToBase64(playlist.contentProtection['com.widevine.alpha'].pssh); + + ret[host].audio.push(pItem); + } + } + + for (const playlist of parsed.playlists) { + const host = new URL(playlist.resolvedUri).hostname; + if (!Object.prototype.hasOwnProperty.call(ret, host)) + ret[host] = { audio: [], video: [] }; + + const pItem: PlaylistItem = { + bandwidth: playlist.attributes.BANDWIDTH, + type: 'video', + quality: playlist.attributes.RESOLUTION!, + segments: playlist.segments.map((segment): Segment => { + const uri = segment.resolvedUri; + const map_uri = segment.map.resolvedUri; + return { + duration: segment.duration, + map: { uri: map_uri }, + number: segment.number, + presentationTime: segment.presentationTime, + timeline: segment.timeline, + uri + }; + }) + }; + + if (playlist.contentProtection && + playlist.contentProtection?.['com.widevine.alpha'].pssh) + pItem.pssh = arrayBufferToBase64(playlist.contentProtection['com.widevine.alpha'].pssh); + + ret[host].video.push(pItem); + } + + return ret; +} + +function arrayBufferToBase64(buffer: Uint8Array): string { + return Buffer.from(buffer).toString('base64'); +} diff --git a/package.json b/package.json index 044131c..02d26bf 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "log4js": "^6.9.1", "lookpath": "^1.2.2", "m3u8-parsed": "^1.3.0", + "mpd-parser": "^1.3.0", "open": "^8.4.2", "sei-helper": "^3.3.0", "typescript-eslint": "0.0.1-alpha.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9eaec3f..8db3f51 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ dependencies: m3u8-parsed: specifier: ^1.3.0 version: 1.3.0 + mpd-parser: + specifier: ^1.3.0 + version: 1.3.0 open: specifier: ^8.4.2 version: 8.4.2 @@ -2231,6 +2234,20 @@ packages: url-toolkit: 2.2.5 dev: false + /@videojs/vhs-utils@4.0.0: + resolution: {integrity: sha512-xJp7Yd4jMLwje2vHCUmi8MOUU76nxiwII3z4Eg3Ucb+6rrkFVGosrXlMgGnaLjq724j3wzNElRZ71D/CKrTtxg==} + engines: {node: '>=8', npm: '>=5'} + dependencies: + '@babel/runtime': 7.21.0 + global: 4.4.0 + url-toolkit: 2.2.5 + dev: false + + /@xmldom/xmldom@0.8.10: + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} + dev: false + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -4325,6 +4342,16 @@ packages: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: true + /mpd-parser@1.3.0: + resolution: {integrity: sha512-WgeIwxAqkmb9uTn4ClicXpEQYCEduDqRKfmUdp4X8vmghKfBNXZLYpREn9eqrDx/Tf5LhzRcJLSpi4ohfV742Q==} + hasBin: true + dependencies: + '@babel/runtime': 7.21.0 + '@videojs/vhs-utils': 4.0.0 + '@xmldom/xmldom': 0.8.10 + global: 4.4.0 + dev: false + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false From abc5747614e4501e8f241265a2d63f7de579642e Mon Sep 17 00:00:00 2001 From: AnidlSupport Date: Sun, 24 Dec 2023 11:19:01 -0800 Subject: [PATCH 02/69] MPD Refactoring --- crunchy.ts | 113 ++++++++++---------------------- modules/module.transform-mpd.ts | 34 +++++----- 2 files changed, 51 insertions(+), 96 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index 409b578..e04bff8 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -773,7 +773,7 @@ export default class Crunchy implements ServiceClass { 'season_id': id, 'Policy': this.cmsToken.cms.policy, 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, }), ].join(''); @@ -1116,7 +1116,7 @@ export default class Crunchy implements ServiceClass { 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, }), ].join(''); - + let playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders); //console.info(playbackReq.res.body); //let playbackReq = await this.req.getData(`${api.cms}/videos/${mediaId}/streams`, AuthHeaders); @@ -1271,20 +1271,6 @@ export default class Crunchy implements ServiceClass { if (streamPlaylistsReq.res.body.match('MPD')) { //Parse MPD Playlists const streamPlaylists = parse(streamPlaylistsReq.res.body, langsData.findLang(langsData.fixLanguageTag(pbData.audio_locale as string) || '')); - const videoQuality: { - bandwidth: number, - str: string, - dim: string, - RESOLUTION: { - width: number, - height: number - } - }[] = [], audioQuality: { - bandwidth: number, - str: string, - dim: string, - RESOLUTION: number - }[] = []; //Get name of CDNs/Servers const streamServers = Object.keys(streamPlaylists); @@ -1295,89 +1281,58 @@ export default class Crunchy implements ServiceClass { const selectedList = streamPlaylists[selectedServer]; //set Video Qualities - selectedList.video.forEach(function(playlist) { - if (playlist.type == 'video') { - const bandwidth = Math.round(playlist.bandwidth/1024); - const resolutionText = `${playlist.quality.width}x${playlist.quality.height}`; - const resolutionTextAdd = `${resolutionText} (${bandwidth}KiB/s)`; - videoQuality.push({ - bandwidth: bandwidth, - str: resolutionTextAdd, - dim: resolutionText, - RESOLUTION: playlist.quality - }); - } else { - console.warn('Found non-video in video typed stream. Skipping...'); + const videos = selectedList.video.map(item => { + return { + ...item, + resolutionText: `${item.quality.width}x${item.quality.height} (${Math.round(item.bandwidth/1024)}KiB/s)` } }); - //set Audio Qualities - selectedList.audio.forEach(function(playlist) { - if (playlist.type == 'audio') { - const bandwidth = Math.round(playlist.bandwidth/1000); - const resolutionText = `${bandwidth}Kb/s`; - audioQuality.push({ - bandwidth: bandwidth, - str: resolutionText, - dim: resolutionText, - RESOLUTION: playlist.bandwidth - }); - } else { - console.warn('Found non-audio in audio typed stream. Skipping...'); + const audios = selectedList.audio.map(item => { + return { + ...item, + resolutionText: `${Math.round(item.bandwidth/1000)}kB/s` } + }) + + + videos.sort((a, b) => { + return a.quality.width - b.quality.width }); - videoQuality.sort((a, b) => { - const aMatch: RegExpMatchArray | never[] = a.dim.match(/[0-9]+/) || []; - const bMatch: RegExpMatchArray | never[] = b.dim.match(/[0-9]+/) || []; - return parseInt(aMatch[0]) - parseInt(bMatch[0]); + audios.sort((a, b) => { + return a.bandwidth - b.bandwidth }); - audioQuality.sort((a, b) => { - const aMatch: RegExpMatchArray | never[] = a.dim.match(/[0-9]+/) || []; - const bMatch: RegExpMatchArray | never[] = b.dim.match(/[0-9]+/) || []; - return parseInt(aMatch[0]) - parseInt(bMatch[0]); - }); - - let chosenVideoQuality = options.q === 0 ? videoQuality.length : options.q; - if(chosenVideoQuality > videoQuality.length) { - console.warn(`The requested quality of ${options.q} is greater than the maximum ${videoQuality.length}.\n[WARN] Therefor the maximum will be capped at ${videoQuality.length}.`); - chosenVideoQuality = videoQuality.length; + let chosenVideoQuality = options.q === 0 ? videos.length : options.q; + if(chosenVideoQuality > videos.length) { + console.warn(`The requested quality of ${options.q} is greater than the maximum ${videos.length}.\n[WARN] Therefor the maximum will be capped at ${videos.length}.`); + chosenVideoQuality = videos.length; } + chosenVideoQuality--; - let chosenAudioQuality = options.q === 0 ? audioQuality.length : options.q; - if(chosenAudioQuality > audioQuality.length) { - chosenAudioQuality = audioQuality.length; + let chosenAudioQuality = options.q === 0 ? audios.length : options.q; + if(chosenAudioQuality > audios.length) { + chosenAudioQuality = audios.length; } + chosenAudioQuality--; - - //TODO: fix this, lol, the below code I thought would work so that it actually chooses the right resolution but it didn't work and I need sleep - /*const chosenVideoSegments = selectedList.video.map(function(playlist) { - if (playlist.bandwidth == videoQuality[chosenVideoQuality - 1].bandwidth) { - return playlist; - } - }); - const chosenAudioSegments = selectedList.audio.map(function(playlist) { - if (playlist.bandwidth == audioQuality[chosenAudioQuality - 1].bandwidth) { - return playlist; - } - });*/ - const chosenVideoSegments = selectedList.video[chosenVideoQuality - 1]; - const chosenAudioSegments = selectedList.audio[chosenAudioQuality - 1]; + const chosenVideoSegments = videos[chosenVideoQuality]; + const chosenAudioSegments = audios[chosenAudioQuality]; console.info(`Servers available:\n\t${streamServers.join('\n\t')}`); - console.info(`Available Video Qualities:\n\t${videoQuality.map((a, ind) => `[${ind+1}] ${a.str}`).join('\n\t')}`); - console.info(`Available Audio Qualities:\n\t${audioQuality.map((a, ind) => `[${ind+1}] ${a.str}`).join('\n\t')}`); + console.info(`Available Video Qualities:\n\t${videos.map((a, ind) => `[${ind+1}] ${a.resolutionText}`).join('\n\t')}`); + console.info(`Available Audio Qualities:\n\t${audios.map((a, ind) => `[${ind+1}] ${a.resolutionText}`).join('\n\t')}`); variables.push({ name: 'height', type: 'number', - replaceWith: chosenVideoQuality === 0 ? videoQuality[videoQuality.length - 1].RESOLUTION.height as number : videoQuality[chosenVideoQuality - 1].RESOLUTION.height + replaceWith: chosenVideoSegments.quality.height }, { name: 'width', type: 'number', - replaceWith: chosenVideoQuality === 0 ? videoQuality[videoQuality.length - 1].RESOLUTION.width as number : videoQuality[chosenVideoQuality - 1].RESOLUTION.width + replaceWith: chosenVideoSegments.quality.width }); const lang = langsData.languages.find(a => a.code === curStream?.audio_lang); @@ -1385,7 +1340,7 @@ export default class Crunchy implements ServiceClass { console.error(`Unable to find language for code ${curStream.audio_lang}`); return; } - console.info(`Selected quality: \n\tVideo: ${videoQuality.map(a => a.dim)[chosenVideoQuality - 1]}\n\tAudio: ${audioQuality.map(a => a.dim)[chosenAudioQuality - 1]}\n\tServer: ${selectedServer}`); + console.info(`Selected quality: \n\tVideo: ${chosenVideoSegments.resolutionText}\n\tAudio: ${chosenAudioSegments.resolutionText}\n\tServer: ${selectedServer}`); console.info('Stream URL:', chosenVideoSegments.segments[0].uri.split(',.urlset')[0]); // TODO check filename fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep); @@ -2084,7 +2039,7 @@ export default class Crunchy implements ServiceClass { 'season_id': item.id, 'Policy': this.cmsToken.cms.policy, 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, }), ].join(''); diff --git a/modules/module.transform-mpd.ts b/modules/module.transform-mpd.ts index 9fc93b4..f5c2073 100644 --- a/modules/module.transform-mpd.ts +++ b/modules/module.transform-mpd.ts @@ -12,26 +12,28 @@ type Segment = { presentationTime: number; } -type PlayListTypeItem = { - type: 'audio', - language: LanguageItem -} | { - type: 'video', - quality: { - width: number, - height: number - } -} - export type PlaylistItem = { pssh?: string, bandwidth: number, segments: Segment[] -} & PlayListTypeItem +} + + +type AudioPlayList = { + language: LanguageItem +} & PlaylistItem + +type VideoPlayList = { + quality: { + width: number, + height: number + } +} & PlaylistItem export type MPDParsed = { [server: string]: { - [type in 'audio'|'video']: PlaylistItem[] + audio: AudioPlayList[], + video: VideoPlayList[] } } @@ -45,10 +47,9 @@ export function parse(manifest: string, language: LanguageItem) { if (!Object.prototype.hasOwnProperty.call(ret, host)) ret[host] = { audio: [], video: [] }; - const pItem: PlaylistItem = { + const pItem: AudioPlayList = { bandwidth: playlist.attributes.BANDWIDTH, language: language, - type: 'audio', segments: playlist.segments.map((segment): Segment => { const uri = segment.resolvedUri; const map_uri = segment.map.resolvedUri; @@ -76,9 +77,8 @@ export function parse(manifest: string, language: LanguageItem) { if (!Object.prototype.hasOwnProperty.call(ret, host)) ret[host] = { audio: [], video: [] }; - const pItem: PlaylistItem = { + const pItem: VideoPlayList = { bandwidth: playlist.attributes.BANDWIDTH, - type: 'video', quality: playlist.attributes.RESOLUTION!, segments: playlist.segments.map((segment): Segment => { const uri = segment.resolvedUri; From 7121d254f69fe9cf781cb1b3433903cc9f8e83ba Mon Sep 17 00:00:00 2001 From: AnidlSupport Date: Sun, 24 Dec 2023 11:19:27 -0800 Subject: [PATCH 03/69] Linting Fixes --- crunchy.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index e04bff8..bddf846 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1285,23 +1285,23 @@ export default class Crunchy implements ServiceClass { return { ...item, resolutionText: `${item.quality.width}x${item.quality.height} (${Math.round(item.bandwidth/1024)}KiB/s)` - } + }; }); const audios = selectedList.audio.map(item => { return { ...item, resolutionText: `${Math.round(item.bandwidth/1000)}kB/s` - } - }) + }; + }); videos.sort((a, b) => { - return a.quality.width - b.quality.width + return a.quality.width - b.quality.width; }); audios.sort((a, b) => { - return a.bandwidth - b.bandwidth + return a.bandwidth - b.bandwidth; }); let chosenVideoQuality = options.q === 0 ? videos.length : options.q; From d1d9840629a234782a0b10c683b0fea5079bda27 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 24 Dec 2023 12:09:07 -0800 Subject: [PATCH 04/69] Fix muxing for MPD --- @types/crunchyTypes.d.ts | 5 +++++ crunchy.ts | 28 ++++++++++++++++++++++++---- package.json | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 7e4bf41..605553a 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -82,6 +82,11 @@ export type DownloadedMedia = { lang: LanguageItem, path: string, isPrimary?: boolean +} | { + type: 'Audio', + lang: LanguageItem, + path: string, + isPrimary?: boolean } | ({ type: 'Subtitle', cc: boolean diff --git a/crunchy.ts b/crunchy.ts index bddf846..be07a33 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1436,7 +1436,7 @@ export default class Crunchy implements ServiceClass { dlFailed = true; } files.push({ - type: 'Video', + type: 'Audio', path: `${tsFile}.audio.ts`, lang: lang, isPrimary: isPrimary @@ -1695,16 +1695,36 @@ export default class Crunchy implements ServiceClass { public async muxStreams(data: DownloadedMedia[], options: CrunchyMuxOptions) { this.cfg.bin = await yamlCfg.loadBinCfg(); + let hasAudioStreams = false; if (options.novids || data.filter(a => a.type === 'Video').length === 0) return console.info('Skip muxing since no vids are downloaded'); + if (data.some(a => a.type === 'Audio')) { + hasAudioStreams = true; + } const merger = new Merger({ - onlyVid: [], + onlyVid: hasAudioStreams ? data.filter(a => a.type === 'Video').map((a) : MergerInput => { + if (a.type === 'Subtitle') + throw new Error('Never'); + return { + lang: a.lang, + path: a.path, + }; + }) : [], skipSubMux: options.skipSubMux, - onlyAudio: [], + onlyAudio: hasAudioStreams ? data.filter(a => a.type === 'Audio').map((a) : MergerInput => { + if (a.type === 'Subtitle') + throw new Error('Never'); + return { + lang: a.lang, + path: a.path, + }; + }) : [], output: `${options.output}.${options.mp4 ? 'mp4' : 'mkv'}`, subtitles: data.filter(a => a.type === 'Subtitle').map((a) : SubtitleInput => { if (a.type === 'Video') throw new Error('Never'); + if (a.type === 'Audio') + throw new Error('Never'); return { file: a.path, language: a.language, @@ -1714,7 +1734,7 @@ export default class Crunchy implements ServiceClass { simul: false, keepAllVideos: options.keepAllVideos, fonts: Merger.makeFontsList(this.cfg.dir.fonts, data.filter(a => a.type === 'Subtitle') as sxItem[]), - videoAndAudio: data.filter(a => a.type === 'Video').map((a) : MergerInput => { + videoAndAudio: hasAudioStreams ? [] : data.filter(a => a.type === 'Video').map((a) : MergerInput => { if (a.type === 'Subtitle') throw new Error('Never'); return { diff --git a/package.json b/package.json index 02d26bf..33669af 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "multi-downloader-nx", "short_name": "aniDL", - "version": "4.4.1", + "version": "4.5.0", "description": "Downloader for Crunchyroll, Funimation, or Hidive via CLI or GUI", "keywords": [ "download", From 28518bb4611c9d8c4ab00aa0ec63e7940124b24c Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 24 Dec 2023 19:29:01 -0800 Subject: [PATCH 05/69] Initial commit for WV Decryption --- config/bin-path.yml | 1 + crunchy.ts | 114 +- docs/README.md | 2 + modules/cmac.ts | 114 + modules/cr_widevine.ts | 36 + modules/license.ts | 161 + modules/license_protocol.proto | 749 +++++ modules/license_protocol.ts | 4996 ++++++++++++++++++++++++++++++++ modules/module.cfg-loader.ts | 6 +- package.json | 4 +- pnpm-lock.yaml | 290 +- widevine/.gitkeep | 0 12 files changed, 6439 insertions(+), 34 deletions(-) create mode 100644 modules/cmac.ts create mode 100644 modules/cr_widevine.ts create mode 100644 modules/license.ts create mode 100644 modules/license_protocol.proto create mode 100644 modules/license_protocol.ts create mode 100644 widevine/.gitkeep diff --git a/config/bin-path.yml b/config/bin-path.yml index 3a5de1e..8c08eb7 100644 --- a/config/bin-path.yml +++ b/config/bin-path.yml @@ -1,3 +1,4 @@ ffmpeg: "ffmpeg.exe" mkvmerge: "mkvmerge.exe" ffprobe: "ffprobe.exe" +mp4decrypt: "mp4decrypt.exe" diff --git a/crunchy.ts b/crunchy.ts index be07a33..d360770 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -4,11 +4,13 @@ import fs from 'fs-extra'; // package program import packageJson from './package.json'; + // plugins import { console } from './modules/log'; import shlp from 'sei-helper'; import m3u8 from 'm3u8-parsed'; import streamdl, { M3U8Json } from './modules/hls-download'; +import { exec } from './modules/sei-helper-fixes'; // custom modules import * as fontsData from './modules/module.fontsData'; @@ -16,6 +18,7 @@ import * as langsData from './modules/module.langsData'; import * as yamlCfg from './modules/module.cfg-loader'; import * as yargs from './modules/module.app-args'; import Merger, { Font, MergerInput, SubtitleInput } from './modules/module.merger'; +import getKeys from './modules/cr_widevine'; // args @@ -1388,12 +1391,12 @@ export default class Crunchy implements ServiceClass { console.error(`DL Stats: ${JSON.stringify(videoDownload.parts)}\n`); dlFailed = true; } - files.push({ + /*files.push({ type: 'Video', path: `${tsFile}.video.ts`, lang: lang, isPrimary: isPrimary - }); + });*/ dlVideoOnce = true; } @@ -1435,6 +1438,104 @@ export default class Crunchy implements ServiceClass { console.error(`DL Stats: ${JSON.stringify(audioDownload.parts)}\n`); dlFailed = true; } + /*files.push({ + type: 'Audio', + path: `${tsFile}.audio.ts`, + lang: lang, + isPrimary: isPrimary + });*/ + } + + //Handle Decryption if needed + if (chosenVideoSegments.pssh || chosenAudioSegments.pssh) { + const assetIdRegex = chosenVideoSegments.segments[0].uri.match(/\/assets\/(?:p\/)?([^_,]+)/); + const assetId = assetIdRegex ? assetIdRegex[1] : null; + const sessionId = new Date().getUTCMilliseconds().toString().padStart(3, '0') + process.hrtime.bigint().toString().slice(0, 13); + console.info('Decryption Needed, attempting to decrypt'); + + const decReq = await this.req.getData('https://pl.crunchyroll.com/drm/v1/auth', { + 'method': 'POST', + 'body': JSON.stringify({ + 'accounting_id': 'crunchyroll', + 'asset_id': assetId, + 'session_id': sessionId, + 'user_id': this.token.account_id + }) + }); + console.info({ + 'body': JSON.stringify({ + 'accounting_id': 'crunchyroll', + 'asset_id': assetId, + 'session_id': sessionId, + 'user_id': this.token.account_id + }) + }); + if(!decReq.ok || !decReq.res){ + console.error('Request to DRM Authentication failed:', decReq.error?.code, decReq.error?.message); + return undefined; + } + const authData = JSON.parse(decReq.res.body) as {'custom_data': string, 'token': string}; + console.info(authData); + const encryptionKeys = await getKeys(chosenVideoSegments.pssh, 'https://lic.drmtoday.com/license-proxy-widevine/cenc/', { + 'dt-custom-data': authData.custom_data, + 'x-dt-auth-token': authData.token + }); + if (encryptionKeys.length == 0) { + console.error('Failed to get encryption keys'); + return undefined; + } + /*const keys = {} as Record; + encryptionKeys.forEach(function(key) { + keys[key.kid] = key.key; + });*/ + + if (this.cfg.bin.mp4decrypt) { + const commandBase = `--show-progress --key ${encryptionKeys[1].kid}:${encryptionKeys[1].key} `; + const commandVideo = commandBase+`"${tsFile}.video.ts" "${tsFile}.dec.video.ts"`; + const commandAudio = commandBase+`"${tsFile}.audio.ts" "${tsFile}.dec.audio.ts"`; + + console.info('Started decrypting video'); + const decryptVideo = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandVideo); + if (!decryptVideo.isOk) { + console.error(decryptVideo.err); + console.error(`Decryption failed with exit code ${decryptVideo.err.code}`); + return undefined; + } else { + console.info('Decryption done for video'); + } + + console.info('Started decrypting audio'); + const decryptAudio = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandAudio); + if (!decryptAudio.isOk) { + console.error(decryptAudio.err); + console.error(`Decryption failed with exit code ${decryptAudio.err.code}`); + return undefined; + } else { + console.info('Decryption done for video'); + } + + files.push({ + type: 'Video', + path: `${tsFile}.dec.video.ts`, + lang: lang, + isPrimary: isPrimary + }); + files.push({ + type: 'Audio', + path: `${tsFile}.dec.audio.ts`, + lang: lang, + isPrimary: isPrimary + }); + } else { + console.warn('mp4decrypt not found, files need decryption. Decryption Keys:', encryptionKeys); + } + } else { + files.push({ + type: 'Video', + path: `${tsFile}.video.ts`, + lang: lang, + isPrimary: isPrimary + }); files.push({ type: 'Audio', path: `${tsFile}.audio.ts`, @@ -1442,15 +1543,6 @@ export default class Crunchy implements ServiceClass { isPrimary: isPrimary }); } - - //Handle Decryption if needed - if (chosenVideoSegments.pssh) { - console.info('Decryption of Video Needed'); - } - - if (chosenAudioSegments.pssh) { - console.info('Decryption of Audio Needed'); - } } else { const streamPlaylists = m3u8(streamPlaylistsReq.res.body); const plServerList: string[] = [], diff --git a/docs/README.md b/docs/README.md index 0db5b24..046cadf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,7 @@ This application is not endorsed by or affiliated with *Funimation*, *Crunchyrol * PNPM >= 7.0.0 (https://pnpm.io/) * ffmpeg >= 4.0.0 (https://www.videohelp.com/software/ffmpeg) * MKVToolNix >= 60.0.0 (https://www.videohelp.com/software/MKVToolNix) +* mp4decrypt >= Any (http://www.bento4.com/) - Only required for decrypting ### Paths Configuration @@ -23,6 +24,7 @@ By default this application uses the following paths to programs (main executabl * `ffmpeg.exe` (From PATH) * `ffprobe.exe` (From PATH) * `mkvmerge.exe` (From PATH) +* `mp4decrypt.exe` (From PATH) To change these paths you need to edit `bin-path.yml` in `./config/` directory. diff --git a/modules/cmac.ts b/modules/cmac.ts new file mode 100644 index 0000000..87d94db --- /dev/null +++ b/modules/cmac.ts @@ -0,0 +1,114 @@ +//Originally from https://github.com/Frooastside/node-widevine/blob/main/src/cmac.ts + +import crypto from 'crypto'; + +export class AES_CMAC { + private readonly BLOCK_SIZE = 16; + private readonly XOR_RIGHT = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87]); + private readonly EMPTY_BLOCK_SIZE_BUFFER = Buffer.alloc(this.BLOCK_SIZE); + + private _key: Buffer; + private _subkeys: { first: Buffer; second: Buffer }; + + public constructor(key: Buffer) { + if (![16, 24, 32].includes(key.length)) { + throw new Error('Key size must be 128, 192, or 256 bits.'); + } + this._key = key; + this._subkeys = this._generateSubkeys(); + } + + public calculate(message: Buffer): Buffer { + const blockCount = this._getBlockCount(message); + + let x = this.EMPTY_BLOCK_SIZE_BUFFER; + let y; + + for (let i = 0; i < blockCount - 1; i++) { + const from = i * this.BLOCK_SIZE; + const block = message.subarray(from, from + this.BLOCK_SIZE); + y = this._xor(x, block); + x = this._aes(y); + } + + y = this._xor(x, this._getLastBlock(message)); + x = this._aes(y); + + return x; + } + + private _generateSubkeys(): { first: Buffer; second: Buffer } { + const l = this._aes(this.EMPTY_BLOCK_SIZE_BUFFER); + + let first = this._bitShiftLeft(l); + if (l[0] & 0x80) { + first = this._xor(first, this.XOR_RIGHT); + } + + let second = this._bitShiftLeft(first); + if (first[0] & 0x80) { + second = this._xor(second, this.XOR_RIGHT); + } + + return { first: first, second: second }; + } + + private _getBlockCount(message: Buffer): number { + const blockCount = Math.ceil(message.length / this.BLOCK_SIZE); + return blockCount === 0 ? 1 : blockCount; + } + + private _aes(message: Buffer): Buffer { + const cipher = crypto.createCipheriv(`aes-${this._key.length * 8}-cbc`, this._key, Buffer.alloc(this.BLOCK_SIZE)); + const result = cipher.update(message).subarray(0, 16); + cipher.destroy(); + return result; + } + + private _getLastBlock(message: Buffer): Buffer { + const blockCount = this._getBlockCount(message); + const paddedBlock = this._padding(message, blockCount - 1); + + let complete = false; + if (message.length > 0) { + complete = message.length % this.BLOCK_SIZE === 0; + } + + const key = complete ? this._subkeys.first : this._subkeys.second; + return this._xor(paddedBlock, key); + } + + private _padding(message: Buffer, blockIndex: number): Buffer { + const block = Buffer.alloc(this.BLOCK_SIZE); + + const from = blockIndex * this.BLOCK_SIZE; + + const slice = message.subarray(from, from + this.BLOCK_SIZE); + block.set(slice); + + if (slice.length !== this.BLOCK_SIZE) { + block[slice.length] = 0x80; + } + + return block; + } + + private _bitShiftLeft(input: Buffer): Buffer { + const output = Buffer.alloc(input.length); + let overflow = 0; + for (let i = input.length - 1; i >= 0; i--) { + output[i] = (input[i] << 1) | overflow; + overflow = input[i] & 0x80 ? 1 : 0; + } + return output; + } + + private _xor(a: Buffer, b: Buffer): Buffer { + const length = Math.min(a.length, b.length); + const output = Buffer.alloc(length); + for (let i = 0; i < length; i++) { + output[i] = a[i] ^ b[i]; + } + return output; + } +} \ No newline at end of file diff --git a/modules/cr_widevine.ts b/modules/cr_widevine.ts new file mode 100644 index 0000000..2235d27 --- /dev/null +++ b/modules/cr_widevine.ts @@ -0,0 +1,36 @@ +import { KeyContainer, Session } from './license'; +import fs from 'fs'; +import { console } from './log'; + +//read cdm files located in the same directory +const privateKey = fs.readFileSync('./widevine/device_private_key'); +const identifierBlob = fs.readFileSync('./widevine/device_client_id_blob'); + +export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record): Promise { + if (!pssh) return []; + //pssh found in the mpd manifest + const psshBuffer = Buffer.from( + pssh, + 'base64' + ); + + //Create a new widevine session + const session = new Session({ privateKey, identifierBlob }, psshBuffer); + + //Generate license + const response = await fetch(licenseServer, { + method: 'POST', + body: session.createLicenseRequest(), + headers: authData + }); + + if (response.ok) { + //Parse License and return keys + const json = await response.json(); + const keys = session.parseLicense(Buffer.from(json['license'], 'base64')); + return keys; + } else { + console.info('License request failed:', response.statusText); + return []; + } +} \ No newline at end of file diff --git a/modules/license.ts b/modules/license.ts new file mode 100644 index 0000000..e2a92ea --- /dev/null +++ b/modules/license.ts @@ -0,0 +1,161 @@ +//Originaly from https://github.com/Frooastside/node-widevine/blob/main/src/license.ts + +import crypto from 'crypto'; +import Long from 'long'; +import { AES_CMAC } from './cmac'; +import { + ClientIdentification, + License, + LicenseRequest, + LicenseRequest_RequestType, + LicenseType, + ProtocolVersion, + SignedMessage, + SignedMessage_MessageType, + SignedMessage_SessionKeyType, + WidevinePsshData +} from './license_protocol'; + +const WIDEVINE_SYSTEM_ID = new Uint8Array([237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237]); + +export type KeyContainer = { + kid: string; + key: string; +}; + +export type ContentDecryptionModule = { + privateKey: Buffer; + identifierBlob: Buffer; +}; + +export class Session { + private _devicePrivateKey: crypto.KeyObject; + private _identifierBlob: ClientIdentification; + private _identifier: Buffer; + private _pssh: Buffer; + private _rawLicenseRequest?: Buffer; + + constructor(contentDecryptionModule: ContentDecryptionModule, pssh: Buffer) { + this._devicePrivateKey = crypto.createPrivateKey(contentDecryptionModule.privateKey); + this._identifierBlob = ClientIdentification.decode(contentDecryptionModule.identifierBlob); + this._identifier = this._generateIdentifier(); + this._pssh = pssh; + } + + createLicenseRequest(): Buffer { + if (!this._pssh.subarray(12, 28).equals(Buffer.from(WIDEVINE_SYSTEM_ID))) { + throw new Error('the pssh is not an actuall pssh'); + } + const pssh = this._parsePSSH(this._pssh); + if (!pssh) { + throw new Error('pssh is invalid'); + } + + const licenseRequest: LicenseRequest = { + type: LicenseRequest_RequestType.NEW, + clientId: this._identifierBlob, + contentId: { + widevinePsshData: { + psshData: [this._pssh.subarray(32)], + licenseType: LicenseType.STREAMING, + requestId: this._identifier + } + }, + requestTime: Long.fromNumber(Date.now()).divide(1000), + protocolVersion: ProtocolVersion.VERSION_2_1, + keyControlNonce: crypto.randomInt(2 ** 31), + keyControlNonceDeprecated: Buffer.alloc(0), + encryptedClientId: undefined + }; + + this._rawLicenseRequest = Buffer.from(LicenseRequest.encode(licenseRequest).finish()); + + const signature = crypto + .createSign('sha1') + .update(this._rawLicenseRequest) + .sign({ key: this._devicePrivateKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: 20 }); + + const signedLicenseRequest: SignedMessage = { + type: SignedMessage_MessageType.LICENSE_REQUEST, + msg: this._rawLicenseRequest, + signature: Buffer.from(signature), + sessionKey: Buffer.alloc(0), + remoteAttestation: Buffer.alloc(0), + metricData: [], + serviceVersionInfo: undefined, + sessionKeyType: SignedMessage_SessionKeyType.UNDEFINED, + oemcryptoCoreMessage: Buffer.alloc(0) + }; + + return Buffer.from(SignedMessage.encode(signedLicenseRequest).finish()); + } + + parseLicense(rawLicense: Buffer) { + if (!this._rawLicenseRequest) { + throw new Error('please request a license first'); + } + const signedLicense = SignedMessage.decode(rawLicense); + const sessionKey = crypto.privateDecrypt(this._devicePrivateKey, signedLicense.sessionKey); + + const cmac = new AES_CMAC(Buffer.from(sessionKey)); + + const encKeyBase = Buffer.concat([ + Buffer.from('ENCRYPTION'), + Buffer.from('\x00', 'ascii'), + this._rawLicenseRequest, + Buffer.from('\x00\x00\x00\x80', 'ascii') + ]); + const authKeyBase = Buffer.concat([ + Buffer.from('AUTHENTICATION'), + Buffer.from('\x00', 'ascii'), + this._rawLicenseRequest, + Buffer.from('\x00\x00\x02\x00', 'ascii') + ]); + + const encKey = cmac.calculate(Buffer.concat([Buffer.from('\x01'), encKeyBase])); + const serverKey = Buffer.concat([ + cmac.calculate(Buffer.concat([Buffer.from('\x01'), authKeyBase])), + cmac.calculate(Buffer.concat([Buffer.from('\x02'), authKeyBase])) + ]); + /*const clientKey = Buffer.concat([ + cmac.calculate(Buffer.concat([Buffer.from("\x03"), authKeyBase])), + cmac.calculate(Buffer.concat([Buffer.from("\x04"), authKeyBase])) + ]);*/ + + const calculatedSignature = crypto.createHmac('sha256', serverKey).update(signedLicense.msg).digest(); + + if (!calculatedSignature.equals(signedLicense.signature)) { + throw new Error('signatures do not match'); + } + + const license = License.decode(signedLicense.msg); + + return license.key.map((keyContainer) => { + const keyId = keyContainer.id.length ? keyContainer.id.toString('hex') : keyContainer.type.toString(); + const decipher = crypto.createDecipheriv(`aes-${encKey.length * 8}-cbc`, encKey, keyContainer.iv); + const decryptedKey = decipher.update(keyContainer.key); + decipher.destroy(); + const key: KeyContainer = { + kid: keyId, + key: decryptedKey.toString('hex') + }; + return key; + }); + } + + private _parsePSSH(pssh: Buffer): WidevinePsshData | null { + try { + return WidevinePsshData.decode(pssh.subarray(32)); + } catch { + return null; + } + } + + private _generateIdentifier(): Buffer { + return Buffer.from(`${crypto.randomBytes(8).toString('hex')}${'01'}${'00000000000000'}`); + } + + get pssh(): Buffer { + return this._pssh; + } +} \ No newline at end of file diff --git a/modules/license_protocol.proto b/modules/license_protocol.proto new file mode 100644 index 0000000..7a9099c --- /dev/null +++ b/modules/license_protocol.proto @@ -0,0 +1,749 @@ +//Originally from https://github.com/Frooastside/node-widevine/blob/main/src/license_protocol.proto + +syntax = "proto2"; + +package license_protocol; + +enum LicenseType { + STREAMING = 1; + OFFLINE = 2; + // License type decision is left to provider. + AUTOMATIC = 3; +} + +enum PlatformVerificationStatus { + // The platform is not verified. + PLATFORM_UNVERIFIED = 0; + // Tampering detected on the platform. + PLATFORM_TAMPERED = 1; + // The platform has been verified by means of software. + PLATFORM_SOFTWARE_VERIFIED = 2; + // The platform has been verified by means of hardware (e.g. secure boot). + PLATFORM_HARDWARE_VERIFIED = 3; + // Platform verification was not performed. + PLATFORM_NO_VERIFICATION = 4; + // Platform and secure storage capability have been verified by means of + // software. + PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED = 5; +} + +// LicenseIdentification is propagated from LicenseRequest to License, +// incrementing version with each iteration. +message LicenseIdentification { + optional bytes request_id = 1; + optional bytes session_id = 2; + optional bytes purchase_id = 3; + optional LicenseType type = 4; + optional int32 version = 5; + optional bytes provider_session_token = 6; +} + +message License { + message Policy { + // Indicates that playback of the content is allowed. + optional bool can_play = 1 [default = false]; + + // Indicates that the license may be persisted to non-volatile + // storage for offline use. + optional bool can_persist = 2 [default = false]; + + // Indicates that renewal of this license is allowed. + optional bool can_renew = 3 [default = false]; + + // For the |*duration*| fields, playback must halt when + // license_start_time (seconds since the epoch (UTC)) + + // license_duration_seconds is exceeded. A value of 0 + // indicates that there is no limit to the duration. + + // Indicates the rental window. + optional int64 rental_duration_seconds = 4 [default = 0]; + + // Indicates the viewing window, once playback has begun. + optional int64 playback_duration_seconds = 5 [default = 0]; + + // Indicates the time window for this specific license. + optional int64 license_duration_seconds = 6 [default = 0]; + + // The |renewal*| fields only apply if |can_renew| is true. + + // The window of time, in which playback is allowed to continue while + // renewal is attempted, yet unsuccessful due to backend problems with + // the license server. + optional int64 renewal_recovery_duration_seconds = 7 [default = 0]; + + // All renewal requests for this license shall be directed to the + // specified URL. + optional string renewal_server_url = 8; + + // How many seconds after license_start_time, before renewal is first + // attempted. + optional int64 renewal_delay_seconds = 9 [default = 0]; + + // Specifies the delay in seconds between subsequent license + // renewal requests, in case of failure. + optional int64 renewal_retry_interval_seconds = 10 [default = 0]; + + // Indicates that the license shall be sent for renewal when usage is + // started. + optional bool renew_with_usage = 11 [default = false]; + + // Indicates to client that license renewal and release requests ought to + // include ClientIdentification (client_id). + optional bool always_include_client_id = 12 [default = false]; + + // Duration of grace period before playback_duration_seconds (short window) + // goes into effect. Optional. + optional int64 play_start_grace_period_seconds = 13 [default = 0]; + + // Enables "soft enforcement" of playback_duration_seconds, letting the user + // finish playback even if short window expires. Optional. + optional bool soft_enforce_playback_duration = 14 [default = false]; + + // Enables "soft enforcement" of rental_duration_seconds. Initial playback + // must always start before rental duration expires. In order to allow + // subsequent playbacks to start after the rental duration expires, + // soft_enforce_playback_duration must be true. Otherwise, subsequent + // playbacks will not be allowed once rental duration expires. Optional. + optional bool soft_enforce_rental_duration = 15 [default = true]; + } + + message KeyContainer { + enum KeyType { + SIGNING = 1; // Exactly one key of this type must appear. + CONTENT = 2; // Content key. + KEY_CONTROL = 3; // Key control block for license renewals. No key. + OPERATOR_SESSION = 4; // wrapped keys for auxiliary crypto operations. + ENTITLEMENT = 5; // Entitlement keys. + OEM_CONTENT = 6; // Partner-specific content key. + } + + // The SecurityLevel enumeration allows the server to communicate the level + // of robustness required by the client, in order to use the key. + enum SecurityLevel { + // Software-based whitebox crypto is required. + SW_SECURE_CRYPTO = 1; + + // Software crypto and an obfuscated decoder is required. + SW_SECURE_DECODE = 2; + + // The key material and crypto operations must be performed within a + // hardware backed trusted execution environment. + HW_SECURE_CRYPTO = 3; + + // The crypto and decoding of content must be performed within a hardware + // backed trusted execution environment. + HW_SECURE_DECODE = 4; + + // The crypto, decoding and all handling of the media (compressed and + // uncompressed) must be handled within a hardware backed trusted + // execution environment. + HW_SECURE_ALL = 5; + } + + message KeyControl { + // |key_control| is documented in: + // Widevine Modular DRM Security Integration Guide for CENC + // If present, the key control must be communicated to the secure + // environment prior to any usage. This message is automatically generated + // by the Widevine License Server SDK. + optional bytes key_control_block = 1; + optional bytes iv = 2; + } + + message OutputProtection { + // Indicates whether HDCP is required on digital outputs, and which + // version should be used. + enum HDCP { + HDCP_NONE = 0; + HDCP_V1 = 1; + HDCP_V2 = 2; + HDCP_V2_1 = 3; + HDCP_V2_2 = 4; + HDCP_V2_3 = 5; + HDCP_NO_DIGITAL_OUTPUT = 0xff; + } + optional HDCP hdcp = 1 [default = HDCP_NONE]; + + // Indicate the CGMS setting to be inserted on analog output. + enum CGMS { + CGMS_NONE = 42; + COPY_FREE = 0; + COPY_ONCE = 2; + COPY_NEVER = 3; + } + optional CGMS cgms_flags = 2 [default = CGMS_NONE]; + + enum HdcpSrmRule { + HDCP_SRM_RULE_NONE = 0; + // In 'required_protection', this means most current SRM is required. + // Update the SRM on the device. If update cannot happen, + // do not allow the key. + // In 'requested_protection', this means most current SRM is requested. + // Update the SRM on the device. If update cannot happen, + // allow use of the key anyway. + CURRENT_SRM = 1; + } + optional HdcpSrmRule hdcp_srm_rule = 3 [default = HDCP_SRM_RULE_NONE]; + // Optional requirement to indicate analog output is not allowed. + optional bool disable_analog_output = 4 [default = false]; + // Optional requirement to indicate digital output is not allowed. + optional bool disable_digital_output = 5 [default = false]; + } + + message VideoResolutionConstraint { + // Minimum and maximum video resolutions in the range (height x width). + optional uint32 min_resolution_pixels = 1; + optional uint32 max_resolution_pixels = 2; + // Optional output protection requirements for this range. If not + // specified, the OutputProtection in the KeyContainer applies. + optional OutputProtection required_protection = 3; + } + + message OperatorSessionKeyPermissions { + // Permissions/key usage flags for operator service keys + // (type = OPERATOR_SESSION). + optional bool allow_encrypt = 1 [default = false]; + optional bool allow_decrypt = 2 [default = false]; + optional bool allow_sign = 3 [default = false]; + optional bool allow_signature_verify = 4 [default = false]; + } + + optional bytes id = 1; + optional bytes iv = 2; + optional bytes key = 3; + optional KeyType type = 4; + optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO]; + optional OutputProtection required_protection = 6; + // NOTE: Use of requested_protection is not recommended as it is only + // supported on a small number of platforms. + optional OutputProtection requested_protection = 7; + optional KeyControl key_control = 8; + optional OperatorSessionKeyPermissions operator_session_key_permissions = 9; + // Optional video resolution constraints. If the video resolution of the + // content being decrypted/decoded falls within one of the specified ranges, + // the optional required_protections may be applied. Otherwise an error will + // be reported. + // NOTE: Use of this feature is not recommended, as it is only supported on + // a small number of platforms. + repeated VideoResolutionConstraint video_resolution_constraints = 10; + // Optional flag to indicate the key must only be used if the client + // supports anti rollback of the user table. Content provider can query the + // client capabilities to determine if the client support this feature. + optional bool anti_rollback_usage_table = 11 [default = false]; + // Optional not limited to commonly known track types such as SD, HD. + // It can be some provider defined label to identify the track. + optional string track_label = 12; + } + + optional LicenseIdentification id = 1; + optional Policy policy = 2; + repeated KeyContainer key = 3; + // Time of the request in seconds (UTC) as set in + // LicenseRequest.request_time. If this time is not set in the request, + // the local time at the license service is used in this field. + optional int64 license_start_time = 4; + optional bool remote_attestation_verified = 5 [default = false]; + // Client token generated by the content provider. Optional. + optional bytes provider_client_token = 6; + // 4cc code specifying the CENC protection scheme as defined in the CENC 3.0 + // specification. Propagated from Widevine PSSH box. Optional. + optional uint32 protection_scheme = 7; + // 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum + // HDCP SRM version (whether the version is for HDCP1 SRM or HDCP2 SRM + // depends on client max_hdcp_version). + // Additional details can be found in Widevine Modular DRM Security + // Integration Guide for CENC. + optional bytes srm_requirement = 8; + // If present this contains a signed SRM file (either HDCP1 SRM or HDCP2 SRM + // depending on client max_hdcp_version) that should be installed on the + // client device. + optional bytes srm_update = 9; + // Indicates the status of any type of platform verification performed by the + // server. + optional PlatformVerificationStatus platform_verification_status = 10 + [default = PLATFORM_NO_VERIFICATION]; + // IDs of the groups for which keys are delivered in this license, if any. + repeated bytes group_ids = 11; +} + +enum ProtocolVersion { + VERSION_2_0 = 20; + VERSION_2_1 = 21; + VERSION_2_2 = 22; +} + +message LicenseRequest { + message ContentIdentification { + message WidevinePsshData { + repeated bytes pssh_data = 1; + optional LicenseType license_type = 2; + optional bytes request_id = 3; // Opaque, client-specified. + } + + message WebmKeyId { + optional bytes header = 1; + optional LicenseType license_type = 2; + optional bytes request_id = 3; // Opaque, client-specified. + } + + message ExistingLicense { + optional LicenseIdentification license_id = 1; + optional int64 seconds_since_started = 2; + optional int64 seconds_since_last_played = 3; + optional bytes session_usage_table_entry = 4; + } + + message InitData { + enum InitDataType { + CENC = 1; + WEBM = 2; + } + + optional InitDataType init_data_type = 1 [default = CENC]; + optional bytes init_data = 2; + optional LicenseType license_type = 3; + optional bytes request_id = 4; + } + + oneof content_id_variant { + // Exactly one of these must be present. + WidevinePsshData widevine_pssh_data = 1; + WebmKeyId webm_key_id = 2; + ExistingLicense existing_license = 3; + InitData init_data = 4; + } + } + + enum RequestType { + NEW = 1; + RENEWAL = 2; + RELEASE = 3; + } + + // The client_id provides information authenticating the calling device. It + // contains the Widevine keybox token that was installed on the device at the + // factory. This field or encrypted_client_id below is required for a valid + // license request, but both should never be present in the same request. + optional ClientIdentification client_id = 1; + optional ContentIdentification content_id = 2; + optional RequestType type = 3; + // Time of the request in seconds (UTC) as set by the client. + optional int64 request_time = 4; + // Old-style decimal-encoded string key control nonce. + optional bytes key_control_nonce_deprecated = 5; + optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0]; + // New-style uint32 key control nonce, please use instead of + // key_control_nonce_deprecated. + optional uint32 key_control_nonce = 7; + // Encrypted ClientIdentification message, used for privacy purposes. + optional EncryptedClientIdentification encrypted_client_id = 8; +} + +message MetricData { + enum MetricType { + // The time spent in the 'stage', specified in microseconds. + LATENCY = 1; + // The UNIX epoch timestamp at which the 'stage' was first accessed in + // microseconds. + TIMESTAMP = 2; + } + + message TypeValue { + optional MetricType type = 1; + // The value associated with 'type'. For example if type == LATENCY, the + // value would be the time in microseconds spent in this 'stage'. + optional int64 value = 2 [default = 0]; + } + + // 'stage' that is currently processing the SignedMessage. Required. + optional string stage_name = 1; + // metric and associated value. + repeated TypeValue metric_data = 2; +} + +message VersionInfo { + // License SDK version reported by the Widevine License SDK. This field + // is populated automatically by the SDK. + optional string license_sdk_version = 1; + // Version of the service hosting the license SDK. This field is optional. + // It may be provided by the hosting service. + optional string license_service_version = 2; +} + +message SignedMessage { + enum MessageType { + LICENSE_REQUEST = 1; + LICENSE = 2; + ERROR_RESPONSE = 3; + SERVICE_CERTIFICATE_REQUEST = 4; + SERVICE_CERTIFICATE = 5; + SUB_LICENSE = 6; + CAS_LICENSE_REQUEST = 7; + CAS_LICENSE = 8; + EXTERNAL_LICENSE_REQUEST = 9; + EXTERNAL_LICENSE = 10; + } + + enum SessionKeyType { + UNDEFINED = 0; + WRAPPED_AES_KEY = 1; + EPHERMERAL_ECC_PUBLIC_KEY = 2; + } + optional MessageType type = 1; + optional bytes msg = 2; + // Required field that contains the signature of the bytes of msg. + // For license requests, the signing algorithm is determined by the + // certificate contained in the request. + // For license responses, the signing algorithm is HMAC with signing key based + // on |session_key|. + optional bytes signature = 3; + // If populated, the contents of this field will be signaled by the + // |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the + // key is the bytes of an encrypted AES key. If the |session_key_type| is + // EPHERMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1 + // serialized ECC public key. + optional bytes session_key = 4; + // Remote attestation data which will be present in the initial license + // request for ChromeOS client devices operating in verified mode. Remote + // attestation challenge data is |msg| field above. Optional. + optional bytes remote_attestation = 5; + + repeated MetricData metric_data = 6; + // Version information from the SDK and license service. This information is + // provided in the license response. + optional VersionInfo service_version_info = 7; + // Optional field that contains the algorithm type used to generate the + // session_key and signature in a LICENSE message. + optional SessionKeyType session_key_type = 8 [default = WRAPPED_AES_KEY]; + // The core message is the simple serialization of fields used by OEMCrypto. + // This field was introduced in OEMCrypto API v16. + optional bytes oemcrypto_core_message = 9; +} + +enum HashAlgorithmProto { + // Unspecified hash algorithm: SHA_256 shall be used for ECC based algorithms + // and SHA_1 shall be used otherwise. + HASH_ALGORITHM_UNSPECIFIED = 0; + HASH_ALGORITHM_SHA_1 = 1; + HASH_ALGORITHM_SHA_256 = 2; + HASH_ALGORITHM_SHA_384 = 3; +} + +// ClientIdentification message used to authenticate the client device. +message ClientIdentification { + enum TokenType { + KEYBOX = 0; + DRM_DEVICE_CERTIFICATE = 1; + REMOTE_ATTESTATION_CERTIFICATE = 2; + OEM_DEVICE_CERTIFICATE = 3; + } + + message NameValue { + optional string name = 1; + optional string value = 2; + } + + // Capabilities which not all clients may support. Used for the license + // exchange protocol only. + message ClientCapabilities { + enum HdcpVersion { + HDCP_NONE = 0; + HDCP_V1 = 1; + HDCP_V2 = 2; + HDCP_V2_1 = 3; + HDCP_V2_2 = 4; + HDCP_V2_3 = 5; + HDCP_NO_DIGITAL_OUTPUT = 0xff; + } + + enum CertificateKeyType { + RSA_2048 = 0; + RSA_3072 = 1; + ECC_SECP256R1 = 2; + ECC_SECP384R1 = 3; + ECC_SECP521R1 = 4; + } + + enum AnalogOutputCapabilities { + ANALOG_OUTPUT_UNKNOWN = 0; + ANALOG_OUTPUT_NONE = 1; + ANALOG_OUTPUT_SUPPORTED = 2; + ANALOG_OUTPUT_SUPPORTS_CGMS_A = 3; + } + + optional bool client_token = 1 [default = false]; + optional bool session_token = 2 [default = false]; + optional bool video_resolution_constraints = 3 [default = false]; + optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE]; + optional uint32 oem_crypto_api_version = 5; + // Client has hardware support for protecting the usage table, such as + // storing the generation number in secure memory. For Details, see: + // Widevine Modular DRM Security Integration Guide for CENC + optional bool anti_rollback_usage_table = 6 [default = false]; + // The client shall report |srm_version| if available. + optional uint32 srm_version = 7; + // A device may have SRM data, and report a version, but may not be capable + // of updating SRM data. + optional bool can_update_srm = 8 [default = false]; + repeated CertificateKeyType supported_certificate_key_type = 9; + optional AnalogOutputCapabilities analog_output_capabilities = 10 + [default = ANALOG_OUTPUT_UNKNOWN]; + optional bool can_disable_analog_output = 11 [default = false]; + // Clients can indicate a performance level supported by OEMCrypto. + // This will allow applications and providers to choose an appropriate + // quality of content to serve. Currently defined tiers are + // 1 (low), 2 (medium) and 3 (high). Any other value indicates that + // the resource rating is unavailable or reporting erroneous values + // for that device. For details see, + // Widevine Modular DRM Security Integration Guide for CENC + optional uint32 resource_rating_tier = 12 [default = 0]; + } + + message ClientCredentials { + optional TokenType type = 1 [default = KEYBOX]; + optional bytes token = 2; + } + + // Type of factory-provisioned device root of trust. Optional. + optional TokenType type = 1 [default = KEYBOX]; + // Factory-provisioned device root of trust. Required. + optional bytes token = 2; + // Optional client information name/value pairs. + repeated NameValue client_info = 3; + // Client token generated by the content provider. Optional. + optional bytes provider_client_token = 4; + // Number of licenses received by the client to which the token above belongs. + // Only present if client_token is specified. + optional uint32 license_counter = 5; + // List of non-baseline client capabilities. + optional ClientCapabilities client_capabilities = 6; + // Serialized VmpData message. Optional. + optional bytes vmp_data = 7; + // Optional field that may contain additional provisioning credentials. + repeated ClientCredentials device_credentials = 8; +} + +// EncryptedClientIdentification message used to hold ClientIdentification +// messages encrypted for privacy purposes. +message EncryptedClientIdentification { + // Provider ID for which the ClientIdentifcation is encrypted (owner of + // service certificate). + optional string provider_id = 1; + // Serial number for the service certificate for which ClientIdentification is + // encrypted. + optional bytes service_certificate_serial_number = 2; + // Serialized ClientIdentification message, encrypted with the privacy key + // using AES-128-CBC with PKCS#5 padding. + optional bytes encrypted_client_id = 3; + // Initialization vector needed to decrypt encrypted_client_id. + optional bytes encrypted_client_id_iv = 4; + // AES-128 privacy key, encrypted with the service public key using RSA-OAEP. + optional bytes encrypted_privacy_key = 5; +} + +// DRM certificate definition for user devices, intermediate, service, and root +// certificates. +message DrmCertificate { + enum Type { + ROOT = 0; // ProtoBestPractices: ignore. + DEVICE_MODEL = 1; + DEVICE = 2; + SERVICE = 3; + PROVISIONER = 4; + } + enum ServiceType { + UNKNOWN_SERVICE_TYPE = 0; + LICENSE_SERVER_SDK = 1; + LICENSE_SERVER_PROXY_SDK = 2; + PROVISIONING_SDK = 3; + CAS_PROXY_SDK = 4; + } + enum Algorithm { + UNKNOWN_ALGORITHM = 0; + RSA = 1; + ECC_SECP256R1 = 2; + ECC_SECP384R1 = 3; + ECC_SECP521R1 = 4; + } + + message EncryptionKey { + // Device public key. PKCS#1 ASN.1 DER-encoded. Required. + optional bytes public_key = 1; + // Required. The algorithm field contains the curve used to create the + // |public_key| if algorithm is one of the ECC types. + // The |algorithm| is used for both to determine the if the certificate is + // ECC or RSA. The |algorithm| also specifies the parameters that were used + // to create |public_key| and are used to create an ephemeral session key. + optional Algorithm algorithm = 2 [default = RSA]; + } + + // Type of certificate. Required. + optional Type type = 1; + // 128-bit globally unique serial number of certificate. + // Value is 0 for root certificate. Required. + optional bytes serial_number = 2; + // POSIX time, in seconds, when the certificate was created. Required. + optional uint32 creation_time_seconds = 3; + // POSIX time, in seconds, when the certificate should expire. Value of zero + // denotes indefinite expiry time. For more information on limited lifespan + // DRM certificates see (go/limited-lifespan-drm-certificates). + optional uint32 expiration_time_seconds = 12; + // Device public key. PKCS#1 ASN.1 DER-encoded. Required. + optional bytes public_key = 4; + // Widevine system ID for the device. Required for intermediate and + // user device certificates. + optional uint32 system_id = 5; + // Deprecated field, which used to indicate whether the device was a test + // (non-production) device. The test_device field in ProvisionedDeviceInfo + // below should be observed instead. + optional bool test_device_deprecated = 6 [deprecated = true]; + // Service identifier (web origin) for the provider which owns the + // certificate. Required for service and provisioner certificates. + optional string provider_id = 7; + // This field is used only when type = SERVICE to specify which SDK uses + // service certificate. This repeated field is treated as a set. A certificate + // may be used for the specified service SDK if the appropriate ServiceType + // is specified in this field. + repeated ServiceType service_types = 8; + // Required. The algorithm field contains the curve used to create the + // |public_key| if algorithm is one of the ECC types. + // The |algorithm| is used for both to determine the if the certificate is ECC + // or RSA. The |algorithm| also specifies the parameters that were used to + // create |public_key| and are used to create an ephemeral session key. + optional Algorithm algorithm = 9 [default = RSA]; + // Optional. May be present in DEVICE certificate types. This is the root + // of trust identifier that holds an encrypted value that identifies the + // keybox or other root of trust that was used to provision a DEVICE drm + // certificate. + optional bytes rot_id = 10; + // Optional. May be present in devices that explicitly support dual keys. When + // present the |public_key| is used for verification of received license + // request messages. + optional EncryptionKey encryption_key = 11; +} + +// DrmCertificate signed by a higher (CA) DRM certificate. +message SignedDrmCertificate { + // Serialized certificate. Required. + optional bytes drm_certificate = 1; + // Signature of certificate. Signed with root or intermediate + // certificate specified below. Required. + optional bytes signature = 2; + // SignedDrmCertificate used to sign this certificate. + optional SignedDrmCertificate signer = 3; + // Optional field that indicates the hash algorithm used in signature scheme. + optional HashAlgorithmProto hash_algorithm = 4; +} + +message WidevinePsshData { + enum Type { + SINGLE = 0; // Single PSSH to be used to retrieve content keys. + ENTITLEMENT = 1; // Primary PSSH used to retrieve entitlement keys. + ENTITLED_KEY = 2; // Secondary PSSH containing entitled key(s). + } + + message EntitledKey { + // ID of entitlement key used for wrapping |key|. + optional bytes entitlement_key_id = 1; + // ID of the entitled key. + optional bytes key_id = 2; + // Wrapped key. Required. + optional bytes key = 3; + // IV used for wrapping |key|. Required. + optional bytes iv = 4; + // Size of entitlement key used for wrapping |key|. + optional uint32 entitlement_key_size_bytes = 5 [default = 32]; + } + + // Entitlement or content key IDs. Can onnly present in SINGLE or ENTITLEMENT + // PSSHs. May be repeated to facilitate delivery of multiple keys in a + // single license. Cannot be used in conjunction with content_id or + // group_ids, which are the preferred mechanism. + repeated bytes key_ids = 2; + + // Content identifier which may map to multiple entitlement or content key + // IDs to facilitate the delivery of multiple keys in a single license. + // Cannot be present in conjunction with key_ids, but if used must be in all + // PSSHs. + optional bytes content_id = 4; + + // Crypto period index, for media using key rotation. Always corresponds to + // The content key period. This means that if using entitlement licensing + // the ENTITLED_KEY PSSHs will have sequential crypto_period_index's, whereas + // the ENTITELEMENT PSSHs will have gaps in the sequence. Required if doing + // key rotation. + optional uint32 crypto_period_index = 7; + + // Protection scheme identifying the encryption algorithm. The protection + // scheme is represented as a uint32 value. The uint32 contains 4 bytes each + // representing a single ascii character in one of the 4CC protection scheme + // values. To be deprecated in favor of signaling from content. + // 'cenc' (AES-CTR) protection_scheme = 0x63656E63, + // 'cbc1' (AES-CBC) protection_scheme = 0x63626331, + // 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73, + // 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373. + optional uint32 protection_scheme = 9; + + // Optional. For media using key rotation, this represents the duration + // of each crypto period in seconds. + optional uint32 crypto_period_seconds = 10; + + // Type of PSSH. Required if not SINGLE. + optional Type type = 11 [default = SINGLE]; + + // Key sequence for Widevine-managed keys. Optional. + optional uint32 key_sequence = 12; + + // Group identifiers for all groups to which the content belongs. This can + // be used to deliver licenses to unlock multiple titles / channels. + // Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, and + // not in conjunction with key_ids. + repeated bytes group_ids = 13; + + // Copy/copies of the content key used to decrypt the media stream in which + // the PSSH box is embedded, each wrapped with a different entitlement key. + // May also contain sub-licenses to support devices with OEMCrypto 13 or + // older. May be repeated if using group entitlement keys. Present only in + // PSSHs of type ENTITLED_KEY. + repeated EntitledKey entitled_keys = 14; + + // Video feature identifier, which is used in conjunction with |content_id| + // to determine the set of keys to be returned in the license. Cannot be + // present in conjunction with |key_ids|. + // Current values are "HDR". + optional string video_feature = 15; + + //////////////////////////// Deprecated Fields //////////////////////////// + enum Algorithm { + UNENCRYPTED = 0; + AESCTR = 1; + }; + optional Algorithm algorithm = 1 [deprecated = true]; + + // Content provider name. + optional string provider = 3 [deprecated = true]; + + // Track type. Acceptable values are SD, HD and AUDIO. Used to + // differentiate content keys used by an asset. + optional string track_type = 5 [deprecated = true]; + + // The name of a registered policy to be used for this asset. + optional string policy = 6 [deprecated = true]; + + // Optional protected context for group content. The grouped_license is a + // serialized SignedMessage. + optional bytes grouped_license = 8 [deprecated = true]; +} + +// File Hashes for Verified Media Path (VMP) support. +message FileHashes { + message Signature { + optional string filename = 1; + optional bool test_signing = 2; //0 - release, 1 - testing + optional bytes SHA512Hash = 3; + optional bool main_exe = 4; //0 for dlls, 1 for exe, this is field 3 in file + optional bytes signature = 5; + } + optional bytes signer = 1; + repeated Signature signatures = 2; +} \ No newline at end of file diff --git a/modules/license_protocol.ts b/modules/license_protocol.ts new file mode 100644 index 0000000..c7ef68c --- /dev/null +++ b/modules/license_protocol.ts @@ -0,0 +1,4996 @@ +//Originally from https://github.com/Frooastside/node-widevine/blob/main/src/license_protocol.ts + +import Long from 'long'; +import _m0 from 'protobufjs/minimal'; + +export const protobufPackage = 'license_protocol'; + +export enum LicenseType { + STREAMING = 1, + OFFLINE = 2, + /** AUTOMATIC - License type decision is left to provider. */ + AUTOMATIC = 3, + UNRECOGNIZED = -1 +} + +export function licenseTypeFromJSON(object: any): LicenseType { + switch (object) { + case 1: + case 'STREAMING': + return LicenseType.STREAMING; + case 2: + case 'OFFLINE': + return LicenseType.OFFLINE; + case 3: + case 'AUTOMATIC': + return LicenseType.AUTOMATIC; + case -1: + case 'UNRECOGNIZED': + default: + return LicenseType.UNRECOGNIZED; + } +} + +export function licenseTypeToJSON(object: LicenseType): string { + switch (object) { + case LicenseType.STREAMING: + return 'STREAMING'; + case LicenseType.OFFLINE: + return 'OFFLINE'; + case LicenseType.AUTOMATIC: + return 'AUTOMATIC'; + case LicenseType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum PlatformVerificationStatus { + /** PLATFORM_UNVERIFIED - The platform is not verified. */ + PLATFORM_UNVERIFIED = 0, + /** PLATFORM_TAMPERED - Tampering detected on the platform. */ + PLATFORM_TAMPERED = 1, + /** PLATFORM_SOFTWARE_VERIFIED - The platform has been verified by means of software. */ + PLATFORM_SOFTWARE_VERIFIED = 2, + /** PLATFORM_HARDWARE_VERIFIED - The platform has been verified by means of hardware (e.g. secure boot). */ + PLATFORM_HARDWARE_VERIFIED = 3, + /** PLATFORM_NO_VERIFICATION - Platform verification was not performed. */ + PLATFORM_NO_VERIFICATION = 4, + /** + * PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED - Platform and secure storage capability have been verified by means of + * software. + */ + PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED = 5, + UNRECOGNIZED = -1 +} + +export function platformVerificationStatusFromJSON(object: any): PlatformVerificationStatus { + switch (object) { + case 0: + case 'PLATFORM_UNVERIFIED': + return PlatformVerificationStatus.PLATFORM_UNVERIFIED; + case 1: + case 'PLATFORM_TAMPERED': + return PlatformVerificationStatus.PLATFORM_TAMPERED; + case 2: + case 'PLATFORM_SOFTWARE_VERIFIED': + return PlatformVerificationStatus.PLATFORM_SOFTWARE_VERIFIED; + case 3: + case 'PLATFORM_HARDWARE_VERIFIED': + return PlatformVerificationStatus.PLATFORM_HARDWARE_VERIFIED; + case 4: + case 'PLATFORM_NO_VERIFICATION': + return PlatformVerificationStatus.PLATFORM_NO_VERIFICATION; + case 5: + case 'PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED': + return PlatformVerificationStatus.PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED; + case -1: + case 'UNRECOGNIZED': + default: + return PlatformVerificationStatus.UNRECOGNIZED; + } +} + +export function platformVerificationStatusToJSON(object: PlatformVerificationStatus): string { + switch (object) { + case PlatformVerificationStatus.PLATFORM_UNVERIFIED: + return 'PLATFORM_UNVERIFIED'; + case PlatformVerificationStatus.PLATFORM_TAMPERED: + return 'PLATFORM_TAMPERED'; + case PlatformVerificationStatus.PLATFORM_SOFTWARE_VERIFIED: + return 'PLATFORM_SOFTWARE_VERIFIED'; + case PlatformVerificationStatus.PLATFORM_HARDWARE_VERIFIED: + return 'PLATFORM_HARDWARE_VERIFIED'; + case PlatformVerificationStatus.PLATFORM_NO_VERIFICATION: + return 'PLATFORM_NO_VERIFICATION'; + case PlatformVerificationStatus.PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED: + return 'PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED'; + case PlatformVerificationStatus.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum ProtocolVersion { + VERSION_2_0 = 20, + VERSION_2_1 = 21, + VERSION_2_2 = 22, + UNRECOGNIZED = -1 +} + +export function protocolVersionFromJSON(object: any): ProtocolVersion { + switch (object) { + case 20: + case 'VERSION_2_0': + return ProtocolVersion.VERSION_2_0; + case 21: + case 'VERSION_2_1': + return ProtocolVersion.VERSION_2_1; + case 22: + case 'VERSION_2_2': + return ProtocolVersion.VERSION_2_2; + case -1: + case 'UNRECOGNIZED': + default: + return ProtocolVersion.UNRECOGNIZED; + } +} + +export function protocolVersionToJSON(object: ProtocolVersion): string { + switch (object) { + case ProtocolVersion.VERSION_2_0: + return 'VERSION_2_0'; + case ProtocolVersion.VERSION_2_1: + return 'VERSION_2_1'; + case ProtocolVersion.VERSION_2_2: + return 'VERSION_2_2'; + case ProtocolVersion.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum HashAlgorithmProto { + /** + * HASH_ALGORITHM_UNSPECIFIED - Unspecified hash algorithm: SHA_256 shall be used for ECC based algorithms + * and SHA_1 shall be used otherwise. + */ + HASH_ALGORITHM_UNSPECIFIED = 0, + HASH_ALGORITHM_SHA_1 = 1, + HASH_ALGORITHM_SHA_256 = 2, + HASH_ALGORITHM_SHA_384 = 3, + UNRECOGNIZED = -1 +} + +export function hashAlgorithmProtoFromJSON(object: any): HashAlgorithmProto { + switch (object) { + case 0: + case 'HASH_ALGORITHM_UNSPECIFIED': + return HashAlgorithmProto.HASH_ALGORITHM_UNSPECIFIED; + case 1: + case 'HASH_ALGORITHM_SHA_1': + return HashAlgorithmProto.HASH_ALGORITHM_SHA_1; + case 2: + case 'HASH_ALGORITHM_SHA_256': + return HashAlgorithmProto.HASH_ALGORITHM_SHA_256; + case 3: + case 'HASH_ALGORITHM_SHA_384': + return HashAlgorithmProto.HASH_ALGORITHM_SHA_384; + case -1: + case 'UNRECOGNIZED': + default: + return HashAlgorithmProto.UNRECOGNIZED; + } +} + +export function hashAlgorithmProtoToJSON(object: HashAlgorithmProto): string { + switch (object) { + case HashAlgorithmProto.HASH_ALGORITHM_UNSPECIFIED: + return 'HASH_ALGORITHM_UNSPECIFIED'; + case HashAlgorithmProto.HASH_ALGORITHM_SHA_1: + return 'HASH_ALGORITHM_SHA_1'; + case HashAlgorithmProto.HASH_ALGORITHM_SHA_256: + return 'HASH_ALGORITHM_SHA_256'; + case HashAlgorithmProto.HASH_ALGORITHM_SHA_384: + return 'HASH_ALGORITHM_SHA_384'; + case HashAlgorithmProto.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +/** + * LicenseIdentification is propagated from LicenseRequest to License, + * incrementing version with each iteration. + */ +export interface LicenseIdentification { + requestId: Buffer; + sessionId: Buffer; + purchaseId: Buffer; + type: LicenseType; + version: number; + providerSessionToken: Buffer; +} + +export interface License { + id: LicenseIdentification | undefined; + policy: License_Policy | undefined; + key: License_KeyContainer[]; + /** + * Time of the request in seconds (UTC) as set in + * LicenseRequest.request_time. If this time is not set in the request, + * the local time at the license service is used in this field. + */ + licenseStartTime: Long; + remoteAttestationVerified: boolean; + /** Client token generated by the content provider. Optional. */ + providerClientToken: Buffer; + /** + * 4cc code specifying the CENC protection scheme as defined in the CENC 3.0 + * specification. Propagated from Widevine PSSH box. Optional. + */ + protectionScheme: number; + /** + * 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum + * HDCP SRM version (whether the version is for HDCP1 SRM or HDCP2 SRM + * depends on client max_hdcp_version). + * Additional details can be found in Widevine Modular DRM Security + * Integration Guide for CENC. + */ + srmRequirement: Buffer; + /** + * If present this contains a signed SRM file (either HDCP1 SRM or HDCP2 SRM + * depending on client max_hdcp_version) that should be installed on the + * client device. + */ + srmUpdate: Buffer; + /** + * Indicates the status of any type of platform verification performed by the + * server. + */ + platformVerificationStatus: PlatformVerificationStatus; + /** IDs of the groups for which keys are delivered in this license, if any. */ + groupIds: Buffer[]; +} + +export interface License_Policy { + /** Indicates that playback of the content is allowed. */ + canPlay: boolean; + /** + * Indicates that the license may be persisted to non-volatile + * storage for offline use. + */ + canPersist: boolean; + /** Indicates that renewal of this license is allowed. */ + canRenew: boolean; + /** Indicates the rental window. */ + rentalDurationSeconds: Long; + /** Indicates the viewing window, once playback has begun. */ + playbackDurationSeconds: Long; + /** Indicates the time window for this specific license. */ + licenseDurationSeconds: Long; + /** + * The window of time, in which playback is allowed to continue while + * renewal is attempted, yet unsuccessful due to backend problems with + * the license server. + */ + renewalRecoveryDurationSeconds: Long; + /** + * All renewal requests for this license shall be directed to the + * specified URL. + */ + renewalServerUrl: string; + /** + * How many seconds after license_start_time, before renewal is first + * attempted. + */ + renewalDelaySeconds: Long; + /** + * Specifies the delay in seconds between subsequent license + * renewal requests, in case of failure. + */ + renewalRetryIntervalSeconds: Long; + /** + * Indicates that the license shall be sent for renewal when usage is + * started. + */ + renewWithUsage: boolean; + /** + * Indicates to client that license renewal and release requests ought to + * include ClientIdentification (client_id). + */ + alwaysIncludeClientId: boolean; + /** + * Duration of grace period before playback_duration_seconds (short window) + * goes into effect. Optional. + */ + playStartGracePeriodSeconds: Long; + /** + * Enables "soft enforcement" of playback_duration_seconds, letting the user + * finish playback even if short window expires. Optional. + */ + softEnforcePlaybackDuration: boolean; + /** + * Enables "soft enforcement" of rental_duration_seconds. Initial playback + * must always start before rental duration expires. In order to allow + * subsequent playbacks to start after the rental duration expires, + * soft_enforce_playback_duration must be true. Otherwise, subsequent + * playbacks will not be allowed once rental duration expires. Optional. + */ + softEnforceRentalDuration: boolean; +} + +export interface License_KeyContainer { + id: Buffer; + iv: Buffer; + key: Buffer; + type: License_KeyContainer_KeyType; + level: License_KeyContainer_SecurityLevel; + requiredProtection: License_KeyContainer_OutputProtection | undefined; + /** + * NOTE: Use of requested_protection is not recommended as it is only + * supported on a small number of platforms. + */ + requestedProtection: License_KeyContainer_OutputProtection | undefined; + keyControl: License_KeyContainer_KeyControl | undefined; + operatorSessionKeyPermissions: License_KeyContainer_OperatorSessionKeyPermissions | undefined; + /** + * Optional video resolution constraints. If the video resolution of the + * content being decrypted/decoded falls within one of the specified ranges, + * the optional required_protections may be applied. Otherwise an error will + * be reported. + * NOTE: Use of this feature is not recommended, as it is only supported on + * a small number of platforms. + */ + videoResolutionConstraints: License_KeyContainer_VideoResolutionConstraint[]; + /** + * Optional flag to indicate the key must only be used if the client + * supports anti rollback of the user table. Content provider can query the + * client capabilities to determine if the client support this feature. + */ + antiRollbackUsageTable: boolean; + /** + * Optional not limited to commonly known track types such as SD, HD. + * It can be some provider defined label to identify the track. + */ + trackLabel: string; +} + +export enum License_KeyContainer_KeyType { + /** SIGNING - Exactly one key of this type must appear. */ + SIGNING = 1, + /** CONTENT - Content key. */ + CONTENT = 2, + /** KEY_CONTROL - Key control block for license renewals. No key. */ + KEY_CONTROL = 3, + /** OPERATOR_SESSION - wrapped keys for auxiliary crypto operations. */ + OPERATOR_SESSION = 4, + /** ENTITLEMENT - Entitlement keys. */ + ENTITLEMENT = 5, + /** OEM_CONTENT - Partner-specific content key. */ + OEM_CONTENT = 6, + UNRECOGNIZED = -1 +} + +export function license_KeyContainer_KeyTypeFromJSON(object: any): License_KeyContainer_KeyType { + switch (object) { + case 1: + case 'SIGNING': + return License_KeyContainer_KeyType.SIGNING; + case 2: + case 'CONTENT': + return License_KeyContainer_KeyType.CONTENT; + case 3: + case 'KEY_CONTROL': + return License_KeyContainer_KeyType.KEY_CONTROL; + case 4: + case 'OPERATOR_SESSION': + return License_KeyContainer_KeyType.OPERATOR_SESSION; + case 5: + case 'ENTITLEMENT': + return License_KeyContainer_KeyType.ENTITLEMENT; + case 6: + case 'OEM_CONTENT': + return License_KeyContainer_KeyType.OEM_CONTENT; + case -1: + case 'UNRECOGNIZED': + default: + return License_KeyContainer_KeyType.UNRECOGNIZED; + } +} + +export function license_KeyContainer_KeyTypeToJSON(object: License_KeyContainer_KeyType): string { + switch (object) { + case License_KeyContainer_KeyType.SIGNING: + return 'SIGNING'; + case License_KeyContainer_KeyType.CONTENT: + return 'CONTENT'; + case License_KeyContainer_KeyType.KEY_CONTROL: + return 'KEY_CONTROL'; + case License_KeyContainer_KeyType.OPERATOR_SESSION: + return 'OPERATOR_SESSION'; + case License_KeyContainer_KeyType.ENTITLEMENT: + return 'ENTITLEMENT'; + case License_KeyContainer_KeyType.OEM_CONTENT: + return 'OEM_CONTENT'; + case License_KeyContainer_KeyType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +/** + * The SecurityLevel enumeration allows the server to communicate the level + * of robustness required by the client, in order to use the key. + */ +export enum License_KeyContainer_SecurityLevel { + /** SW_SECURE_CRYPTO - Software-based whitebox crypto is required. */ + SW_SECURE_CRYPTO = 1, + /** SW_SECURE_DECODE - Software crypto and an obfuscated decoder is required. */ + SW_SECURE_DECODE = 2, + /** + * HW_SECURE_CRYPTO - The key material and crypto operations must be performed within a + * hardware backed trusted execution environment. + */ + HW_SECURE_CRYPTO = 3, + /** + * HW_SECURE_DECODE - The crypto and decoding of content must be performed within a hardware + * backed trusted execution environment. + */ + HW_SECURE_DECODE = 4, + /** + * HW_SECURE_ALL - The crypto, decoding and all handling of the media (compressed and + * uncompressed) must be handled within a hardware backed trusted + * execution environment. + */ + HW_SECURE_ALL = 5, + UNRECOGNIZED = -1 +} + +export function license_KeyContainer_SecurityLevelFromJSON(object: any): License_KeyContainer_SecurityLevel { + switch (object) { + case 1: + case 'SW_SECURE_CRYPTO': + return License_KeyContainer_SecurityLevel.SW_SECURE_CRYPTO; + case 2: + case 'SW_SECURE_DECODE': + return License_KeyContainer_SecurityLevel.SW_SECURE_DECODE; + case 3: + case 'HW_SECURE_CRYPTO': + return License_KeyContainer_SecurityLevel.HW_SECURE_CRYPTO; + case 4: + case 'HW_SECURE_DECODE': + return License_KeyContainer_SecurityLevel.HW_SECURE_DECODE; + case 5: + case 'HW_SECURE_ALL': + return License_KeyContainer_SecurityLevel.HW_SECURE_ALL; + case -1: + case 'UNRECOGNIZED': + default: + return License_KeyContainer_SecurityLevel.UNRECOGNIZED; + } +} + +export function license_KeyContainer_SecurityLevelToJSON(object: License_KeyContainer_SecurityLevel): string { + switch (object) { + case License_KeyContainer_SecurityLevel.SW_SECURE_CRYPTO: + return 'SW_SECURE_CRYPTO'; + case License_KeyContainer_SecurityLevel.SW_SECURE_DECODE: + return 'SW_SECURE_DECODE'; + case License_KeyContainer_SecurityLevel.HW_SECURE_CRYPTO: + return 'HW_SECURE_CRYPTO'; + case License_KeyContainer_SecurityLevel.HW_SECURE_DECODE: + return 'HW_SECURE_DECODE'; + case License_KeyContainer_SecurityLevel.HW_SECURE_ALL: + return 'HW_SECURE_ALL'; + case License_KeyContainer_SecurityLevel.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface License_KeyContainer_KeyControl { + /** + * |key_control| is documented in: + * Widevine Modular DRM Security Integration Guide for CENC + * If present, the key control must be communicated to the secure + * environment prior to any usage. This message is automatically generated + * by the Widevine License Server SDK. + */ + keyControlBlock: Buffer; + iv: Buffer; +} + +export interface License_KeyContainer_OutputProtection { + hdcp: License_KeyContainer_OutputProtection_HDCP; + cgmsFlags: License_KeyContainer_OutputProtection_CGMS; + hdcpSrmRule: License_KeyContainer_OutputProtection_HdcpSrmRule; + /** Optional requirement to indicate analog output is not allowed. */ + disableAnalogOutput: boolean; + /** Optional requirement to indicate digital output is not allowed. */ + disableDigitalOutput: boolean; +} + +/** + * Indicates whether HDCP is required on digital outputs, and which + * version should be used. + */ +export enum License_KeyContainer_OutputProtection_HDCP { + HDCP_NONE = 0, + HDCP_V1 = 1, + HDCP_V2 = 2, + HDCP_V2_1 = 3, + HDCP_V2_2 = 4, + HDCP_V2_3 = 5, + HDCP_NO_DIGITAL_OUTPUT = 255, + UNRECOGNIZED = -1 +} + +export function license_KeyContainer_OutputProtection_HDCPFromJSON(object: any): License_KeyContainer_OutputProtection_HDCP { + switch (object) { + case 0: + case 'HDCP_NONE': + return License_KeyContainer_OutputProtection_HDCP.HDCP_NONE; + case 1: + case 'HDCP_V1': + return License_KeyContainer_OutputProtection_HDCP.HDCP_V1; + case 2: + case 'HDCP_V2': + return License_KeyContainer_OutputProtection_HDCP.HDCP_V2; + case 3: + case 'HDCP_V2_1': + return License_KeyContainer_OutputProtection_HDCP.HDCP_V2_1; + case 4: + case 'HDCP_V2_2': + return License_KeyContainer_OutputProtection_HDCP.HDCP_V2_2; + case 5: + case 'HDCP_V2_3': + return License_KeyContainer_OutputProtection_HDCP.HDCP_V2_3; + case 255: + case 'HDCP_NO_DIGITAL_OUTPUT': + return License_KeyContainer_OutputProtection_HDCP.HDCP_NO_DIGITAL_OUTPUT; + case -1: + case 'UNRECOGNIZED': + default: + return License_KeyContainer_OutputProtection_HDCP.UNRECOGNIZED; + } +} + +export function license_KeyContainer_OutputProtection_HDCPToJSON(object: License_KeyContainer_OutputProtection_HDCP): string { + switch (object) { + case License_KeyContainer_OutputProtection_HDCP.HDCP_NONE: + return 'HDCP_NONE'; + case License_KeyContainer_OutputProtection_HDCP.HDCP_V1: + return 'HDCP_V1'; + case License_KeyContainer_OutputProtection_HDCP.HDCP_V2: + return 'HDCP_V2'; + case License_KeyContainer_OutputProtection_HDCP.HDCP_V2_1: + return 'HDCP_V2_1'; + case License_KeyContainer_OutputProtection_HDCP.HDCP_V2_2: + return 'HDCP_V2_2'; + case License_KeyContainer_OutputProtection_HDCP.HDCP_V2_3: + return 'HDCP_V2_3'; + case License_KeyContainer_OutputProtection_HDCP.HDCP_NO_DIGITAL_OUTPUT: + return 'HDCP_NO_DIGITAL_OUTPUT'; + case License_KeyContainer_OutputProtection_HDCP.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +/** Indicate the CGMS setting to be inserted on analog output. */ +export enum License_KeyContainer_OutputProtection_CGMS { + CGMS_NONE = 42, + COPY_FREE = 0, + COPY_ONCE = 2, + COPY_NEVER = 3, + UNRECOGNIZED = -1 +} + +export function license_KeyContainer_OutputProtection_CGMSFromJSON(object: any): License_KeyContainer_OutputProtection_CGMS { + switch (object) { + case 42: + case 'CGMS_NONE': + return License_KeyContainer_OutputProtection_CGMS.CGMS_NONE; + case 0: + case 'COPY_FREE': + return License_KeyContainer_OutputProtection_CGMS.COPY_FREE; + case 2: + case 'COPY_ONCE': + return License_KeyContainer_OutputProtection_CGMS.COPY_ONCE; + case 3: + case 'COPY_NEVER': + return License_KeyContainer_OutputProtection_CGMS.COPY_NEVER; + case -1: + case 'UNRECOGNIZED': + default: + return License_KeyContainer_OutputProtection_CGMS.UNRECOGNIZED; + } +} + +export function license_KeyContainer_OutputProtection_CGMSToJSON(object: License_KeyContainer_OutputProtection_CGMS): string { + switch (object) { + case License_KeyContainer_OutputProtection_CGMS.CGMS_NONE: + return 'CGMS_NONE'; + case License_KeyContainer_OutputProtection_CGMS.COPY_FREE: + return 'COPY_FREE'; + case License_KeyContainer_OutputProtection_CGMS.COPY_ONCE: + return 'COPY_ONCE'; + case License_KeyContainer_OutputProtection_CGMS.COPY_NEVER: + return 'COPY_NEVER'; + case License_KeyContainer_OutputProtection_CGMS.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum License_KeyContainer_OutputProtection_HdcpSrmRule { + HDCP_SRM_RULE_NONE = 0, + /** + * CURRENT_SRM - In 'required_protection', this means most current SRM is required. + * Update the SRM on the device. If update cannot happen, + * do not allow the key. + * In 'requested_protection', this means most current SRM is requested. + * Update the SRM on the device. If update cannot happen, + * allow use of the key anyway. + */ + CURRENT_SRM = 1, + UNRECOGNIZED = -1 +} + +export function license_KeyContainer_OutputProtection_HdcpSrmRuleFromJSON(object: any): License_KeyContainer_OutputProtection_HdcpSrmRule { + switch (object) { + case 0: + case 'HDCP_SRM_RULE_NONE': + return License_KeyContainer_OutputProtection_HdcpSrmRule.HDCP_SRM_RULE_NONE; + case 1: + case 'CURRENT_SRM': + return License_KeyContainer_OutputProtection_HdcpSrmRule.CURRENT_SRM; + case -1: + case 'UNRECOGNIZED': + default: + return License_KeyContainer_OutputProtection_HdcpSrmRule.UNRECOGNIZED; + } +} + +export function license_KeyContainer_OutputProtection_HdcpSrmRuleToJSON(object: License_KeyContainer_OutputProtection_HdcpSrmRule): string { + switch (object) { + case License_KeyContainer_OutputProtection_HdcpSrmRule.HDCP_SRM_RULE_NONE: + return 'HDCP_SRM_RULE_NONE'; + case License_KeyContainer_OutputProtection_HdcpSrmRule.CURRENT_SRM: + return 'CURRENT_SRM'; + case License_KeyContainer_OutputProtection_HdcpSrmRule.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface License_KeyContainer_VideoResolutionConstraint { + /** Minimum and maximum video resolutions in the range (height x width). */ + minResolutionPixels: number; + maxResolutionPixels: number; + /** + * Optional output protection requirements for this range. If not + * specified, the OutputProtection in the KeyContainer applies. + */ + requiredProtection: License_KeyContainer_OutputProtection | undefined; +} + +export interface License_KeyContainer_OperatorSessionKeyPermissions { + /** + * Permissions/key usage flags for operator service keys + * (type = OPERATOR_SESSION). + */ + allowEncrypt: boolean; + allowDecrypt: boolean; + allowSign: boolean; + allowSignatureVerify: boolean; +} + +export interface LicenseRequest { + /** + * The client_id provides information authenticating the calling device. It + * contains the Widevine keybox token that was installed on the device at the + * factory. This field or encrypted_client_id below is required for a valid + * license request, but both should never be present in the same request. + */ + clientId: ClientIdentification | undefined; + contentId: LicenseRequest_ContentIdentification | undefined; + type: LicenseRequest_RequestType; + /** Time of the request in seconds (UTC) as set by the client. */ + requestTime: Long; + /** Old-style decimal-encoded string key control nonce. */ + keyControlNonceDeprecated: Buffer; + protocolVersion: ProtocolVersion; + /** + * New-style uint32 key control nonce, please use instead of + * key_control_nonce_deprecated. + */ + keyControlNonce: number; + /** Encrypted ClientIdentification message, used for privacy purposes. */ + encryptedClientId: EncryptedClientIdentification | undefined; +} + +export enum LicenseRequest_RequestType { + NEW = 1, + RENEWAL = 2, + RELEASE = 3, + UNRECOGNIZED = -1 +} + +export function licenseRequest_RequestTypeFromJSON(object: any): LicenseRequest_RequestType { + switch (object) { + case 1: + case 'NEW': + return LicenseRequest_RequestType.NEW; + case 2: + case 'RENEWAL': + return LicenseRequest_RequestType.RENEWAL; + case 3: + case 'RELEASE': + return LicenseRequest_RequestType.RELEASE; + case -1: + case 'UNRECOGNIZED': + default: + return LicenseRequest_RequestType.UNRECOGNIZED; + } +} + +export function licenseRequest_RequestTypeToJSON(object: LicenseRequest_RequestType): string { + switch (object) { + case LicenseRequest_RequestType.NEW: + return 'NEW'; + case LicenseRequest_RequestType.RENEWAL: + return 'RENEWAL'; + case LicenseRequest_RequestType.RELEASE: + return 'RELEASE'; + case LicenseRequest_RequestType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface LicenseRequest_ContentIdentification { + /** Exactly one of these must be present. */ + widevinePsshData?: LicenseRequest_ContentIdentification_WidevinePsshData | undefined; + webmKeyId?: LicenseRequest_ContentIdentification_WebmKeyId | undefined; + existingLicense?: LicenseRequest_ContentIdentification_ExistingLicense | undefined; + initData?: LicenseRequest_ContentIdentification_InitData | undefined; +} + +export interface LicenseRequest_ContentIdentification_WidevinePsshData { + psshData: Buffer[]; + licenseType: LicenseType; + /** Opaque, client-specified. */ + requestId: Buffer; +} + +export interface LicenseRequest_ContentIdentification_WebmKeyId { + header: Buffer; + licenseType: LicenseType; + /** Opaque, client-specified. */ + requestId: Buffer; +} + +export interface LicenseRequest_ContentIdentification_ExistingLicense { + licenseId: LicenseIdentification | undefined; + secondsSinceStarted: Long; + secondsSinceLastPlayed: Long; + sessionUsageTableEntry: Buffer; +} + +export interface LicenseRequest_ContentIdentification_InitData { + initDataType: LicenseRequest_ContentIdentification_InitData_InitDataType; + initData: Buffer; + licenseType: LicenseType; + requestId: Buffer; +} + +export enum LicenseRequest_ContentIdentification_InitData_InitDataType { + CENC = 1, + WEBM = 2, + UNRECOGNIZED = -1 +} + +export function licenseRequest_ContentIdentification_InitData_InitDataTypeFromJSON( + object: any +): LicenseRequest_ContentIdentification_InitData_InitDataType { + switch (object) { + case 1: + case 'CENC': + return LicenseRequest_ContentIdentification_InitData_InitDataType.CENC; + case 2: + case 'WEBM': + return LicenseRequest_ContentIdentification_InitData_InitDataType.WEBM; + case -1: + case 'UNRECOGNIZED': + default: + return LicenseRequest_ContentIdentification_InitData_InitDataType.UNRECOGNIZED; + } +} + +export function licenseRequest_ContentIdentification_InitData_InitDataTypeToJSON( + object: LicenseRequest_ContentIdentification_InitData_InitDataType +): string { + switch (object) { + case LicenseRequest_ContentIdentification_InitData_InitDataType.CENC: + return 'CENC'; + case LicenseRequest_ContentIdentification_InitData_InitDataType.WEBM: + return 'WEBM'; + case LicenseRequest_ContentIdentification_InitData_InitDataType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface MetricData { + /** 'stage' that is currently processing the SignedMessage. Required. */ + stageName: string; + /** metric and associated value. */ + metricData: MetricData_TypeValue[]; +} + +export enum MetricData_MetricType { + /** LATENCY - The time spent in the 'stage', specified in microseconds. */ + LATENCY = 1, + /** + * TIMESTAMP - The UNIX epoch timestamp at which the 'stage' was first accessed in + * microseconds. + */ + TIMESTAMP = 2, + UNRECOGNIZED = -1 +} + +export function metricData_MetricTypeFromJSON(object: any): MetricData_MetricType { + switch (object) { + case 1: + case 'LATENCY': + return MetricData_MetricType.LATENCY; + case 2: + case 'TIMESTAMP': + return MetricData_MetricType.TIMESTAMP; + case -1: + case 'UNRECOGNIZED': + default: + return MetricData_MetricType.UNRECOGNIZED; + } +} + +export function metricData_MetricTypeToJSON(object: MetricData_MetricType): string { + switch (object) { + case MetricData_MetricType.LATENCY: + return 'LATENCY'; + case MetricData_MetricType.TIMESTAMP: + return 'TIMESTAMP'; + case MetricData_MetricType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface MetricData_TypeValue { + type: MetricData_MetricType; + /** + * The value associated with 'type'. For example if type == LATENCY, the + * value would be the time in microseconds spent in this 'stage'. + */ + value: Long; +} + +export interface VersionInfo { + /** + * License SDK version reported by the Widevine License SDK. This field + * is populated automatically by the SDK. + */ + licenseSdkVersion: string; + /** + * Version of the service hosting the license SDK. This field is optional. + * It may be provided by the hosting service. + */ + licenseServiceVersion: string; +} + +export interface SignedMessage { + type: SignedMessage_MessageType; + msg: Buffer; + /** + * Required field that contains the signature of the bytes of msg. + * For license requests, the signing algorithm is determined by the + * certificate contained in the request. + * For license responses, the signing algorithm is HMAC with signing key based + * on |session_key|. + */ + signature: Buffer; + /** + * If populated, the contents of this field will be signaled by the + * |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the + * key is the bytes of an encrypted AES key. If the |session_key_type| is + * EPHERMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1 + * serialized ECC public key. + */ + sessionKey: Buffer; + /** + * Remote attestation data which will be present in the initial license + * request for ChromeOS client devices operating in verified mode. Remote + * attestation challenge data is |msg| field above. Optional. + */ + remoteAttestation: Buffer; + metricData: MetricData[]; + /** + * Version information from the SDK and license service. This information is + * provided in the license response. + */ + serviceVersionInfo: VersionInfo | undefined; + /** + * Optional field that contains the algorithm type used to generate the + * session_key and signature in a LICENSE message. + */ + sessionKeyType: SignedMessage_SessionKeyType; + /** + * The core message is the simple serialization of fields used by OEMCrypto. + * This field was introduced in OEMCrypto API v16. + */ + oemcryptoCoreMessage: Buffer; +} + +export enum SignedMessage_MessageType { + LICENSE_REQUEST = 1, + LICENSE = 2, + ERROR_RESPONSE = 3, + SERVICE_CERTIFICATE_REQUEST = 4, + SERVICE_CERTIFICATE = 5, + SUB_LICENSE = 6, + CAS_LICENSE_REQUEST = 7, + CAS_LICENSE = 8, + EXTERNAL_LICENSE_REQUEST = 9, + EXTERNAL_LICENSE = 10, + UNRECOGNIZED = -1 +} + +export function signedMessage_MessageTypeFromJSON(object: any): SignedMessage_MessageType { + switch (object) { + case 1: + case 'LICENSE_REQUEST': + return SignedMessage_MessageType.LICENSE_REQUEST; + case 2: + case 'LICENSE': + return SignedMessage_MessageType.LICENSE; + case 3: + case 'ERROR_RESPONSE': + return SignedMessage_MessageType.ERROR_RESPONSE; + case 4: + case 'SERVICE_CERTIFICATE_REQUEST': + return SignedMessage_MessageType.SERVICE_CERTIFICATE_REQUEST; + case 5: + case 'SERVICE_CERTIFICATE': + return SignedMessage_MessageType.SERVICE_CERTIFICATE; + case 6: + case 'SUB_LICENSE': + return SignedMessage_MessageType.SUB_LICENSE; + case 7: + case 'CAS_LICENSE_REQUEST': + return SignedMessage_MessageType.CAS_LICENSE_REQUEST; + case 8: + case 'CAS_LICENSE': + return SignedMessage_MessageType.CAS_LICENSE; + case 9: + case 'EXTERNAL_LICENSE_REQUEST': + return SignedMessage_MessageType.EXTERNAL_LICENSE_REQUEST; + case 10: + case 'EXTERNAL_LICENSE': + return SignedMessage_MessageType.EXTERNAL_LICENSE; + case -1: + case 'UNRECOGNIZED': + default: + return SignedMessage_MessageType.UNRECOGNIZED; + } +} + +export function signedMessage_MessageTypeToJSON(object: SignedMessage_MessageType): string { + switch (object) { + case SignedMessage_MessageType.LICENSE_REQUEST: + return 'LICENSE_REQUEST'; + case SignedMessage_MessageType.LICENSE: + return 'LICENSE'; + case SignedMessage_MessageType.ERROR_RESPONSE: + return 'ERROR_RESPONSE'; + case SignedMessage_MessageType.SERVICE_CERTIFICATE_REQUEST: + return 'SERVICE_CERTIFICATE_REQUEST'; + case SignedMessage_MessageType.SERVICE_CERTIFICATE: + return 'SERVICE_CERTIFICATE'; + case SignedMessage_MessageType.SUB_LICENSE: + return 'SUB_LICENSE'; + case SignedMessage_MessageType.CAS_LICENSE_REQUEST: + return 'CAS_LICENSE_REQUEST'; + case SignedMessage_MessageType.CAS_LICENSE: + return 'CAS_LICENSE'; + case SignedMessage_MessageType.EXTERNAL_LICENSE_REQUEST: + return 'EXTERNAL_LICENSE_REQUEST'; + case SignedMessage_MessageType.EXTERNAL_LICENSE: + return 'EXTERNAL_LICENSE'; + case SignedMessage_MessageType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum SignedMessage_SessionKeyType { + UNDEFINED = 0, + WRAPPED_AES_KEY = 1, + EPHERMERAL_ECC_PUBLIC_KEY = 2, + UNRECOGNIZED = -1 +} + +export function signedMessage_SessionKeyTypeFromJSON(object: any): SignedMessage_SessionKeyType { + switch (object) { + case 0: + case 'UNDEFINED': + return SignedMessage_SessionKeyType.UNDEFINED; + case 1: + case 'WRAPPED_AES_KEY': + return SignedMessage_SessionKeyType.WRAPPED_AES_KEY; + case 2: + case 'EPHERMERAL_ECC_PUBLIC_KEY': + return SignedMessage_SessionKeyType.EPHERMERAL_ECC_PUBLIC_KEY; + case -1: + case 'UNRECOGNIZED': + default: + return SignedMessage_SessionKeyType.UNRECOGNIZED; + } +} + +export function signedMessage_SessionKeyTypeToJSON(object: SignedMessage_SessionKeyType): string { + switch (object) { + case SignedMessage_SessionKeyType.UNDEFINED: + return 'UNDEFINED'; + case SignedMessage_SessionKeyType.WRAPPED_AES_KEY: + return 'WRAPPED_AES_KEY'; + case SignedMessage_SessionKeyType.EPHERMERAL_ECC_PUBLIC_KEY: + return 'EPHERMERAL_ECC_PUBLIC_KEY'; + case SignedMessage_SessionKeyType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +/** ClientIdentification message used to authenticate the client device. */ +export interface ClientIdentification { + /** Type of factory-provisioned device root of trust. Optional. */ + type: ClientIdentification_TokenType; + /** Factory-provisioned device root of trust. Required. */ + token: Buffer; + /** Optional client information name/value pairs. */ + clientInfo: ClientIdentification_NameValue[]; + /** Client token generated by the content provider. Optional. */ + providerClientToken: Buffer; + /** + * Number of licenses received by the client to which the token above belongs. + * Only present if client_token is specified. + */ + licenseCounter: number; + /** List of non-baseline client capabilities. */ + clientCapabilities: ClientIdentification_ClientCapabilities | undefined; + /** Serialized VmpData message. Optional. */ + vmpData: Buffer; + /** Optional field that may contain additional provisioning credentials. */ + deviceCredentials: ClientIdentification_ClientCredentials[]; +} + +export enum ClientIdentification_TokenType { + KEYBOX = 0, + DRM_DEVICE_CERTIFICATE = 1, + REMOTE_ATTESTATION_CERTIFICATE = 2, + OEM_DEVICE_CERTIFICATE = 3, + UNRECOGNIZED = -1 +} + +export function clientIdentification_TokenTypeFromJSON(object: any): ClientIdentification_TokenType { + switch (object) { + case 0: + case 'KEYBOX': + return ClientIdentification_TokenType.KEYBOX; + case 1: + case 'DRM_DEVICE_CERTIFICATE': + return ClientIdentification_TokenType.DRM_DEVICE_CERTIFICATE; + case 2: + case 'REMOTE_ATTESTATION_CERTIFICATE': + return ClientIdentification_TokenType.REMOTE_ATTESTATION_CERTIFICATE; + case 3: + case 'OEM_DEVICE_CERTIFICATE': + return ClientIdentification_TokenType.OEM_DEVICE_CERTIFICATE; + case -1: + case 'UNRECOGNIZED': + default: + return ClientIdentification_TokenType.UNRECOGNIZED; + } +} + +export function clientIdentification_TokenTypeToJSON(object: ClientIdentification_TokenType): string { + switch (object) { + case ClientIdentification_TokenType.KEYBOX: + return 'KEYBOX'; + case ClientIdentification_TokenType.DRM_DEVICE_CERTIFICATE: + return 'DRM_DEVICE_CERTIFICATE'; + case ClientIdentification_TokenType.REMOTE_ATTESTATION_CERTIFICATE: + return 'REMOTE_ATTESTATION_CERTIFICATE'; + case ClientIdentification_TokenType.OEM_DEVICE_CERTIFICATE: + return 'OEM_DEVICE_CERTIFICATE'; + case ClientIdentification_TokenType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface ClientIdentification_NameValue { + name: string; + value: string; +} + +/** + * Capabilities which not all clients may support. Used for the license + * exchange protocol only. + */ +export interface ClientIdentification_ClientCapabilities { + clientToken: boolean; + sessionToken: boolean; + videoResolutionConstraints: boolean; + maxHdcpVersion: ClientIdentification_ClientCapabilities_HdcpVersion; + oemCryptoApiVersion: number; + /** + * Client has hardware support for protecting the usage table, such as + * storing the generation number in secure memory. For Details, see: + * Widevine Modular DRM Security Integration Guide for CENC + */ + antiRollbackUsageTable: boolean; + /** The client shall report |srm_version| if available. */ + srmVersion: number; + /** + * A device may have SRM data, and report a version, but may not be capable + * of updating SRM data. + */ + canUpdateSrm: boolean; + supportedCertificateKeyType: ClientIdentification_ClientCapabilities_CertificateKeyType[]; + analogOutputCapabilities: ClientIdentification_ClientCapabilities_AnalogOutputCapabilities; + canDisableAnalogOutput: boolean; + /** + * Clients can indicate a performance level supported by OEMCrypto. + * This will allow applications and providers to choose an appropriate + * quality of content to serve. Currently defined tiers are + * 1 (low), 2 (medium) and 3 (high). Any other value indicates that + * the resource rating is unavailable or reporting erroneous values + * for that device. For details see, + * Widevine Modular DRM Security Integration Guide for CENC + */ + resourceRatingTier: number; +} + +export enum ClientIdentification_ClientCapabilities_HdcpVersion { + HDCP_NONE = 0, + HDCP_V1 = 1, + HDCP_V2 = 2, + HDCP_V2_1 = 3, + HDCP_V2_2 = 4, + HDCP_V2_3 = 5, + HDCP_NO_DIGITAL_OUTPUT = 255, + UNRECOGNIZED = -1 +} + +export function clientIdentification_ClientCapabilities_HdcpVersionFromJSON(object: any): ClientIdentification_ClientCapabilities_HdcpVersion { + switch (object) { + case 0: + case 'HDCP_NONE': + return ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_NONE; + case 1: + case 'HDCP_V1': + return ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V1; + case 2: + case 'HDCP_V2': + return ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2; + case 3: + case 'HDCP_V2_1': + return ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2_1; + case 4: + case 'HDCP_V2_2': + return ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2_2; + case 5: + case 'HDCP_V2_3': + return ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2_3; + case 255: + case 'HDCP_NO_DIGITAL_OUTPUT': + return ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_NO_DIGITAL_OUTPUT; + case -1: + case 'UNRECOGNIZED': + default: + return ClientIdentification_ClientCapabilities_HdcpVersion.UNRECOGNIZED; + } +} + +export function clientIdentification_ClientCapabilities_HdcpVersionToJSON(object: ClientIdentification_ClientCapabilities_HdcpVersion): string { + switch (object) { + case ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_NONE: + return 'HDCP_NONE'; + case ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V1: + return 'HDCP_V1'; + case ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2: + return 'HDCP_V2'; + case ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2_1: + return 'HDCP_V2_1'; + case ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2_2: + return 'HDCP_V2_2'; + case ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_V2_3: + return 'HDCP_V2_3'; + case ClientIdentification_ClientCapabilities_HdcpVersion.HDCP_NO_DIGITAL_OUTPUT: + return 'HDCP_NO_DIGITAL_OUTPUT'; + case ClientIdentification_ClientCapabilities_HdcpVersion.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum ClientIdentification_ClientCapabilities_CertificateKeyType { + RSA_2048 = 0, + RSA_3072 = 1, + ECC_SECP256R1 = 2, + ECC_SECP384R1 = 3, + ECC_SECP521R1 = 4, + UNRECOGNIZED = -1 +} + +export function clientIdentification_ClientCapabilities_CertificateKeyTypeFromJSON( + object: any +): ClientIdentification_ClientCapabilities_CertificateKeyType { + switch (object) { + case 0: + case 'RSA_2048': + return ClientIdentification_ClientCapabilities_CertificateKeyType.RSA_2048; + case 1: + case 'RSA_3072': + return ClientIdentification_ClientCapabilities_CertificateKeyType.RSA_3072; + case 2: + case 'ECC_SECP256R1': + return ClientIdentification_ClientCapabilities_CertificateKeyType.ECC_SECP256R1; + case 3: + case 'ECC_SECP384R1': + return ClientIdentification_ClientCapabilities_CertificateKeyType.ECC_SECP384R1; + case 4: + case 'ECC_SECP521R1': + return ClientIdentification_ClientCapabilities_CertificateKeyType.ECC_SECP521R1; + case -1: + case 'UNRECOGNIZED': + default: + return ClientIdentification_ClientCapabilities_CertificateKeyType.UNRECOGNIZED; + } +} + +export function clientIdentification_ClientCapabilities_CertificateKeyTypeToJSON( + object: ClientIdentification_ClientCapabilities_CertificateKeyType +): string { + switch (object) { + case ClientIdentification_ClientCapabilities_CertificateKeyType.RSA_2048: + return 'RSA_2048'; + case ClientIdentification_ClientCapabilities_CertificateKeyType.RSA_3072: + return 'RSA_3072'; + case ClientIdentification_ClientCapabilities_CertificateKeyType.ECC_SECP256R1: + return 'ECC_SECP256R1'; + case ClientIdentification_ClientCapabilities_CertificateKeyType.ECC_SECP384R1: + return 'ECC_SECP384R1'; + case ClientIdentification_ClientCapabilities_CertificateKeyType.ECC_SECP521R1: + return 'ECC_SECP521R1'; + case ClientIdentification_ClientCapabilities_CertificateKeyType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum ClientIdentification_ClientCapabilities_AnalogOutputCapabilities { + ANALOG_OUTPUT_UNKNOWN = 0, + ANALOG_OUTPUT_NONE = 1, + ANALOG_OUTPUT_SUPPORTED = 2, + ANALOG_OUTPUT_SUPPORTS_CGMS_A = 3, + UNRECOGNIZED = -1 +} + +export function clientIdentification_ClientCapabilities_AnalogOutputCapabilitiesFromJSON( + object: any +): ClientIdentification_ClientCapabilities_AnalogOutputCapabilities { + switch (object) { + case 0: + case 'ANALOG_OUTPUT_UNKNOWN': + return ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_UNKNOWN; + case 1: + case 'ANALOG_OUTPUT_NONE': + return ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_NONE; + case 2: + case 'ANALOG_OUTPUT_SUPPORTED': + return ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_SUPPORTED; + case 3: + case 'ANALOG_OUTPUT_SUPPORTS_CGMS_A': + return ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_SUPPORTS_CGMS_A; + case -1: + case 'UNRECOGNIZED': + default: + return ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.UNRECOGNIZED; + } +} + +export function clientIdentification_ClientCapabilities_AnalogOutputCapabilitiesToJSON( + object: ClientIdentification_ClientCapabilities_AnalogOutputCapabilities +): string { + switch (object) { + case ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_UNKNOWN: + return 'ANALOG_OUTPUT_UNKNOWN'; + case ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_NONE: + return 'ANALOG_OUTPUT_NONE'; + case ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_SUPPORTED: + return 'ANALOG_OUTPUT_SUPPORTED'; + case ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.ANALOG_OUTPUT_SUPPORTS_CGMS_A: + return 'ANALOG_OUTPUT_SUPPORTS_CGMS_A'; + case ClientIdentification_ClientCapabilities_AnalogOutputCapabilities.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface ClientIdentification_ClientCredentials { + type: ClientIdentification_TokenType; + token: Buffer; +} + +/** + * EncryptedClientIdentification message used to hold ClientIdentification + * messages encrypted for privacy purposes. + */ +export interface EncryptedClientIdentification { + /** + * Provider ID for which the ClientIdentifcation is encrypted (owner of + * service certificate). + */ + providerId: string; + /** + * Serial number for the service certificate for which ClientIdentification is + * encrypted. + */ + serviceCertificateSerialNumber: Buffer; + /** + * Serialized ClientIdentification message, encrypted with the privacy key + * using AES-128-CBC with PKCS#5 padding. + */ + encryptedClientId: Buffer; + /** Initialization vector needed to decrypt encrypted_client_id. */ + encryptedClientIdIv: Buffer; + /** AES-128 privacy key, encrypted with the service public key using RSA-OAEP. */ + encryptedPrivacyKey: Buffer; +} + +/** + * DRM certificate definition for user devices, intermediate, service, and root + * certificates. + */ +export interface DrmCertificate { + /** Type of certificate. Required. */ + type: DrmCertificate_Type; + /** + * 128-bit globally unique serial number of certificate. + * Value is 0 for root certificate. Required. + */ + serialNumber: Buffer; + /** POSIX time, in seconds, when the certificate was created. Required. */ + creationTimeSeconds: number; + /** + * POSIX time, in seconds, when the certificate should expire. Value of zero + * denotes indefinite expiry time. For more information on limited lifespan + * DRM certificates see (go/limited-lifespan-drm-certificates). + */ + expirationTimeSeconds: number; + /** Device public key. PKCS#1 ASN.1 DER-encoded. Required. */ + publicKey: Buffer; + /** + * Widevine system ID for the device. Required for intermediate and + * user device certificates. + */ + systemId: number; + /** + * Deprecated field, which used to indicate whether the device was a test + * (non-production) device. The test_device field in ProvisionedDeviceInfo + * below should be observed instead. + * + * @deprecated + */ + testDeviceDeprecated: boolean; + /** + * Service identifier (web origin) for the provider which owns the + * certificate. Required for service and provisioner certificates. + */ + providerId: string; + /** + * This field is used only when type = SERVICE to specify which SDK uses + * service certificate. This repeated field is treated as a set. A certificate + * may be used for the specified service SDK if the appropriate ServiceType + * is specified in this field. + */ + serviceTypes: DrmCertificate_ServiceType[]; + /** + * Required. The algorithm field contains the curve used to create the + * |public_key| if algorithm is one of the ECC types. + * The |algorithm| is used for both to determine the if the certificate is ECC + * or RSA. The |algorithm| also specifies the parameters that were used to + * create |public_key| and are used to create an ephemeral session key. + */ + algorithm: DrmCertificate_Algorithm; + /** + * Optional. May be present in DEVICE certificate types. This is the root + * of trust identifier that holds an encrypted value that identifies the + * keybox or other root of trust that was used to provision a DEVICE drm + * certificate. + */ + rotId: Buffer; + /** + * Optional. May be present in devices that explicitly support dual keys. When + * present the |public_key| is used for verification of received license + * request messages. + */ + encryptionKey: DrmCertificate_EncryptionKey | undefined; +} + +export enum DrmCertificate_Type { + /** ROOT - ProtoBestPractices: ignore. */ + ROOT = 0, + DEVICE_MODEL = 1, + DEVICE = 2, + SERVICE = 3, + PROVISIONER = 4, + UNRECOGNIZED = -1 +} + +export function drmCertificate_TypeFromJSON(object: any): DrmCertificate_Type { + switch (object) { + case 0: + case 'ROOT': + return DrmCertificate_Type.ROOT; + case 1: + case 'DEVICE_MODEL': + return DrmCertificate_Type.DEVICE_MODEL; + case 2: + case 'DEVICE': + return DrmCertificate_Type.DEVICE; + case 3: + case 'SERVICE': + return DrmCertificate_Type.SERVICE; + case 4: + case 'PROVISIONER': + return DrmCertificate_Type.PROVISIONER; + case -1: + case 'UNRECOGNIZED': + default: + return DrmCertificate_Type.UNRECOGNIZED; + } +} + +export function drmCertificate_TypeToJSON(object: DrmCertificate_Type): string { + switch (object) { + case DrmCertificate_Type.ROOT: + return 'ROOT'; + case DrmCertificate_Type.DEVICE_MODEL: + return 'DEVICE_MODEL'; + case DrmCertificate_Type.DEVICE: + return 'DEVICE'; + case DrmCertificate_Type.SERVICE: + return 'SERVICE'; + case DrmCertificate_Type.PROVISIONER: + return 'PROVISIONER'; + case DrmCertificate_Type.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum DrmCertificate_ServiceType { + UNKNOWN_SERVICE_TYPE = 0, + LICENSE_SERVER_SDK = 1, + LICENSE_SERVER_PROXY_SDK = 2, + PROVISIONING_SDK = 3, + CAS_PROXY_SDK = 4, + UNRECOGNIZED = -1 +} + +export function drmCertificate_ServiceTypeFromJSON(object: any): DrmCertificate_ServiceType { + switch (object) { + case 0: + case 'UNKNOWN_SERVICE_TYPE': + return DrmCertificate_ServiceType.UNKNOWN_SERVICE_TYPE; + case 1: + case 'LICENSE_SERVER_SDK': + return DrmCertificate_ServiceType.LICENSE_SERVER_SDK; + case 2: + case 'LICENSE_SERVER_PROXY_SDK': + return DrmCertificate_ServiceType.LICENSE_SERVER_PROXY_SDK; + case 3: + case 'PROVISIONING_SDK': + return DrmCertificate_ServiceType.PROVISIONING_SDK; + case 4: + case 'CAS_PROXY_SDK': + return DrmCertificate_ServiceType.CAS_PROXY_SDK; + case -1: + case 'UNRECOGNIZED': + default: + return DrmCertificate_ServiceType.UNRECOGNIZED; + } +} + +export function drmCertificate_ServiceTypeToJSON(object: DrmCertificate_ServiceType): string { + switch (object) { + case DrmCertificate_ServiceType.UNKNOWN_SERVICE_TYPE: + return 'UNKNOWN_SERVICE_TYPE'; + case DrmCertificate_ServiceType.LICENSE_SERVER_SDK: + return 'LICENSE_SERVER_SDK'; + case DrmCertificate_ServiceType.LICENSE_SERVER_PROXY_SDK: + return 'LICENSE_SERVER_PROXY_SDK'; + case DrmCertificate_ServiceType.PROVISIONING_SDK: + return 'PROVISIONING_SDK'; + case DrmCertificate_ServiceType.CAS_PROXY_SDK: + return 'CAS_PROXY_SDK'; + case DrmCertificate_ServiceType.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export enum DrmCertificate_Algorithm { + UNKNOWN_ALGORITHM = 0, + RSA = 1, + ECC_SECP256R1 = 2, + ECC_SECP384R1 = 3, + ECC_SECP521R1 = 4, + UNRECOGNIZED = -1 +} + +export function drmCertificate_AlgorithmFromJSON(object: any): DrmCertificate_Algorithm { + switch (object) { + case 0: + case 'UNKNOWN_ALGORITHM': + return DrmCertificate_Algorithm.UNKNOWN_ALGORITHM; + case 1: + case 'RSA': + return DrmCertificate_Algorithm.RSA; + case 2: + case 'ECC_SECP256R1': + return DrmCertificate_Algorithm.ECC_SECP256R1; + case 3: + case 'ECC_SECP384R1': + return DrmCertificate_Algorithm.ECC_SECP384R1; + case 4: + case 'ECC_SECP521R1': + return DrmCertificate_Algorithm.ECC_SECP521R1; + case -1: + case 'UNRECOGNIZED': + default: + return DrmCertificate_Algorithm.UNRECOGNIZED; + } +} + +export function drmCertificate_AlgorithmToJSON(object: DrmCertificate_Algorithm): string { + switch (object) { + case DrmCertificate_Algorithm.UNKNOWN_ALGORITHM: + return 'UNKNOWN_ALGORITHM'; + case DrmCertificate_Algorithm.RSA: + return 'RSA'; + case DrmCertificate_Algorithm.ECC_SECP256R1: + return 'ECC_SECP256R1'; + case DrmCertificate_Algorithm.ECC_SECP384R1: + return 'ECC_SECP384R1'; + case DrmCertificate_Algorithm.ECC_SECP521R1: + return 'ECC_SECP521R1'; + case DrmCertificate_Algorithm.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface DrmCertificate_EncryptionKey { + /** Device public key. PKCS#1 ASN.1 DER-encoded. Required. */ + publicKey: Buffer; + /** + * Required. The algorithm field contains the curve used to create the + * |public_key| if algorithm is one of the ECC types. + * The |algorithm| is used for both to determine the if the certificate is + * ECC or RSA. The |algorithm| also specifies the parameters that were used + * to create |public_key| and are used to create an ephemeral session key. + */ + algorithm: DrmCertificate_Algorithm; +} + +/** DrmCertificate signed by a higher (CA) DRM certificate. */ +export interface SignedDrmCertificate { + /** Serialized certificate. Required. */ + drmCertificate: Buffer; + /** + * Signature of certificate. Signed with root or intermediate + * certificate specified below. Required. + */ + signature: Buffer; + /** SignedDrmCertificate used to sign this certificate. */ + signer: SignedDrmCertificate | undefined; + /** Optional field that indicates the hash algorithm used in signature scheme. */ + hashAlgorithm: HashAlgorithmProto; +} + +export interface WidevinePsshData { + /** + * Entitlement or content key IDs. Can onnly present in SINGLE or ENTITLEMENT + * PSSHs. May be repeated to facilitate delivery of multiple keys in a + * single license. Cannot be used in conjunction with content_id or + * group_ids, which are the preferred mechanism. + */ + keyIds: Buffer[]; + /** + * Content identifier which may map to multiple entitlement or content key + * IDs to facilitate the delivery of multiple keys in a single license. + * Cannot be present in conjunction with key_ids, but if used must be in all + * PSSHs. + */ + contentId: Buffer; + /** + * Crypto period index, for media using key rotation. Always corresponds to + * The content key period. This means that if using entitlement licensing + * the ENTITLED_KEY PSSHs will have sequential crypto_period_index's, whereas + * the ENTITELEMENT PSSHs will have gaps in the sequence. Required if doing + * key rotation. + */ + cryptoPeriodIndex: number; + /** + * Protection scheme identifying the encryption algorithm. The protection + * scheme is represented as a uint32 value. The uint32 contains 4 bytes each + * representing a single ascii character in one of the 4CC protection scheme + * values. To be deprecated in favor of signaling from content. + * 'cenc' (AES-CTR) protection_scheme = 0x63656E63, + * 'cbc1' (AES-CBC) protection_scheme = 0x63626331, + * 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73, + * 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373. + */ + protectionScheme: number; + /** + * Optional. For media using key rotation, this represents the duration + * of each crypto period in seconds. + */ + cryptoPeriodSeconds: number; + /** Type of PSSH. Required if not SINGLE. */ + type: WidevinePsshData_Type; + /** Key sequence for Widevine-managed keys. Optional. */ + keySequence: number; + /** + * Group identifiers for all groups to which the content belongs. This can + * be used to deliver licenses to unlock multiple titles / channels. + * Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, and + * not in conjunction with key_ids. + */ + groupIds: Buffer[]; + /** + * Copy/copies of the content key used to decrypt the media stream in which + * the PSSH box is embedded, each wrapped with a different entitlement key. + * May also contain sub-licenses to support devices with OEMCrypto 13 or + * older. May be repeated if using group entitlement keys. Present only in + * PSSHs of type ENTITLED_KEY. + */ + entitledKeys: WidevinePsshData_EntitledKey[]; + /** + * Video feature identifier, which is used in conjunction with |content_id| + * to determine the set of keys to be returned in the license. Cannot be + * present in conjunction with |key_ids|. + * Current values are "HDR". + */ + videoFeature: string; + /** @deprecated */ + algorithm: WidevinePsshData_Algorithm; + /** + * Content provider name. + * + * @deprecated + */ + provider: string; + /** + * Track type. Acceptable values are SD, HD and AUDIO. Used to + * differentiate content keys used by an asset. + * + * @deprecated + */ + trackType: string; + /** + * The name of a registered policy to be used for this asset. + * + * @deprecated + */ + policy: string; + /** + * Optional protected context for group content. The grouped_license is a + * serialized SignedMessage. + * + * @deprecated + */ + groupedLicense: Buffer; +} + +export enum WidevinePsshData_Type { + /** SINGLE - Single PSSH to be used to retrieve content keys. */ + SINGLE = 0, + /** ENTITLEMENT - Primary PSSH used to retrieve entitlement keys. */ + ENTITLEMENT = 1, + /** ENTITLED_KEY - Secondary PSSH containing entitled key(s). */ + ENTITLED_KEY = 2, + UNRECOGNIZED = -1 +} + +export function widevinePsshData_TypeFromJSON(object: any): WidevinePsshData_Type { + switch (object) { + case 0: + case 'SINGLE': + return WidevinePsshData_Type.SINGLE; + case 1: + case 'ENTITLEMENT': + return WidevinePsshData_Type.ENTITLEMENT; + case 2: + case 'ENTITLED_KEY': + return WidevinePsshData_Type.ENTITLED_KEY; + case -1: + case 'UNRECOGNIZED': + default: + return WidevinePsshData_Type.UNRECOGNIZED; + } +} + +export function widevinePsshData_TypeToJSON(object: WidevinePsshData_Type): string { + switch (object) { + case WidevinePsshData_Type.SINGLE: + return 'SINGLE'; + case WidevinePsshData_Type.ENTITLEMENT: + return 'ENTITLEMENT'; + case WidevinePsshData_Type.ENTITLED_KEY: + return 'ENTITLED_KEY'; + case WidevinePsshData_Type.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +/** ////////////////////////// Deprecated Fields //////////////////////////// */ +export enum WidevinePsshData_Algorithm { + UNENCRYPTED = 0, + AESCTR = 1, + UNRECOGNIZED = -1 +} + +export function widevinePsshData_AlgorithmFromJSON(object: any): WidevinePsshData_Algorithm { + switch (object) { + case 0: + case 'UNENCRYPTED': + return WidevinePsshData_Algorithm.UNENCRYPTED; + case 1: + case 'AESCTR': + return WidevinePsshData_Algorithm.AESCTR; + case -1: + case 'UNRECOGNIZED': + default: + return WidevinePsshData_Algorithm.UNRECOGNIZED; + } +} + +export function widevinePsshData_AlgorithmToJSON(object: WidevinePsshData_Algorithm): string { + switch (object) { + case WidevinePsshData_Algorithm.UNENCRYPTED: + return 'UNENCRYPTED'; + case WidevinePsshData_Algorithm.AESCTR: + return 'AESCTR'; + case WidevinePsshData_Algorithm.UNRECOGNIZED: + default: + return 'UNRECOGNIZED'; + } +} + +export interface WidevinePsshData_EntitledKey { + /** ID of entitlement key used for wrapping |key|. */ + entitlementKeyId: Buffer; + /** ID of the entitled key. */ + keyId: Buffer; + /** Wrapped key. Required. */ + key: Buffer; + /** IV used for wrapping |key|. Required. */ + iv: Buffer; + /** Size of entitlement key used for wrapping |key|. */ + entitlementKeySizeBytes: number; +} + +/** File Hashes for Verified Media Path (VMP) support. */ +export interface FileHashes { + signer: Buffer; + signatures: FileHashes_Signature[]; +} + +export interface FileHashes_Signature { + filename: string; + /** 0 - release, 1 - testing */ + testSigning: boolean; + SHA512Hash: Buffer; + /** 0 for dlls, 1 for exe, this is field 3 in file */ + mainExe: boolean; + signature: Buffer; +} + +function createBaseLicenseIdentification(): LicenseIdentification { + return { + requestId: Buffer.alloc(0), + sessionId: Buffer.alloc(0), + purchaseId: Buffer.alloc(0), + type: 1, + version: 0, + providerSessionToken: Buffer.alloc(0) + }; +} + +export const LicenseIdentification = { + encode(message: LicenseIdentification, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.requestId.length !== 0) { + writer.uint32(10).bytes(message.requestId); + } + if (message.sessionId.length !== 0) { + writer.uint32(18).bytes(message.sessionId); + } + if (message.purchaseId.length !== 0) { + writer.uint32(26).bytes(message.purchaseId); + } + if (message.type !== 1) { + writer.uint32(32).int32(message.type); + } + if (message.version !== 0) { + writer.uint32(40).int32(message.version); + } + if (message.providerSessionToken.length !== 0) { + writer.uint32(50).bytes(message.providerSessionToken); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LicenseIdentification { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicenseIdentification(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.requestId = reader.bytes() as Buffer; + break; + case 2: + message.sessionId = reader.bytes() as Buffer; + break; + case 3: + message.purchaseId = reader.bytes() as Buffer; + break; + case 4: + message.type = reader.int32() as any; + break; + case 5: + message.version = reader.int32(); + break; + case 6: + message.providerSessionToken = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LicenseIdentification { + return { + requestId: isSet(object.requestId) ? Buffer.from(bytesFromBase64(object.requestId)) : Buffer.alloc(0), + sessionId: isSet(object.sessionId) ? Buffer.from(bytesFromBase64(object.sessionId)) : Buffer.alloc(0), + purchaseId: isSet(object.purchaseId) ? Buffer.from(bytesFromBase64(object.purchaseId)) : Buffer.alloc(0), + type: isSet(object.type) ? licenseTypeFromJSON(object.type) : 1, + version: isSet(object.version) ? Number(object.version) : 0, + providerSessionToken: isSet(object.providerSessionToken) ? Buffer.from(bytesFromBase64(object.providerSessionToken)) : Buffer.alloc(0) + }; + }, + + toJSON(message: LicenseIdentification): unknown { + const obj: any = {}; + message.requestId !== undefined && (obj.requestId = base64FromBytes(message.requestId !== undefined ? message.requestId : Buffer.alloc(0))); + message.sessionId !== undefined && (obj.sessionId = base64FromBytes(message.sessionId !== undefined ? message.sessionId : Buffer.alloc(0))); + message.purchaseId !== undefined && (obj.purchaseId = base64FromBytes(message.purchaseId !== undefined ? message.purchaseId : Buffer.alloc(0))); + message.type !== undefined && (obj.type = licenseTypeToJSON(message.type)); + message.version !== undefined && (obj.version = Math.round(message.version)); + message.providerSessionToken !== undefined && + (obj.providerSessionToken = base64FromBytes(message.providerSessionToken !== undefined ? message.providerSessionToken : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>(object: I): LicenseIdentification { + const message = createBaseLicenseIdentification(); + message.requestId = object.requestId ?? Buffer.alloc(0); + message.sessionId = object.sessionId ?? Buffer.alloc(0); + message.purchaseId = object.purchaseId ?? Buffer.alloc(0); + message.type = object.type ?? 1; + message.version = object.version ?? 0; + message.providerSessionToken = object.providerSessionToken ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseLicense(): License { + return { + id: undefined, + policy: undefined, + key: [], + licenseStartTime: Long.ZERO, + remoteAttestationVerified: false, + providerClientToken: Buffer.alloc(0), + protectionScheme: 0, + srmRequirement: Buffer.alloc(0), + srmUpdate: Buffer.alloc(0), + platformVerificationStatus: 0, + groupIds: [] + }; +} + +export const License = { + encode(message: License, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== undefined) { + LicenseIdentification.encode(message.id, writer.uint32(10).fork()).ldelim(); + } + if (message.policy !== undefined) { + License_Policy.encode(message.policy, writer.uint32(18).fork()).ldelim(); + } + for (const v of message.key) { + License_KeyContainer.encode(v!, writer.uint32(26).fork()).ldelim(); + } + if (!message.licenseStartTime.isZero()) { + writer.uint32(32).int64(message.licenseStartTime); + } + if (message.remoteAttestationVerified === true) { + writer.uint32(40).bool(message.remoteAttestationVerified); + } + if (message.providerClientToken.length !== 0) { + writer.uint32(50).bytes(message.providerClientToken); + } + if (message.protectionScheme !== 0) { + writer.uint32(56).uint32(message.protectionScheme); + } + if (message.srmRequirement.length !== 0) { + writer.uint32(66).bytes(message.srmRequirement); + } + if (message.srmUpdate.length !== 0) { + writer.uint32(74).bytes(message.srmUpdate); + } + if (message.platformVerificationStatus !== 0) { + writer.uint32(80).int32(message.platformVerificationStatus); + } + for (const v of message.groupIds) { + writer.uint32(90).bytes(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): License { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicense(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = LicenseIdentification.decode(reader, reader.uint32()); + break; + case 2: + message.policy = License_Policy.decode(reader, reader.uint32()); + break; + case 3: + message.key.push(License_KeyContainer.decode(reader, reader.uint32())); + break; + case 4: + message.licenseStartTime = reader.int64() as Long; + break; + case 5: + message.remoteAttestationVerified = reader.bool(); + break; + case 6: + message.providerClientToken = reader.bytes() as Buffer; + break; + case 7: + message.protectionScheme = reader.uint32(); + break; + case 8: + message.srmRequirement = reader.bytes() as Buffer; + break; + case 9: + message.srmUpdate = reader.bytes() as Buffer; + break; + case 10: + message.platformVerificationStatus = reader.int32() as any; + break; + case 11: + message.groupIds.push(reader.bytes() as Buffer); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): License { + return { + id: isSet(object.id) ? LicenseIdentification.fromJSON(object.id) : undefined, + policy: isSet(object.policy) ? License_Policy.fromJSON(object.policy) : undefined, + key: Array.isArray(object?.key) ? object.key.map((e: any) => License_KeyContainer.fromJSON(e)) : [], + licenseStartTime: isSet(object.licenseStartTime) ? Long.fromValue(object.licenseStartTime) : Long.ZERO, + remoteAttestationVerified: isSet(object.remoteAttestationVerified) ? Boolean(object.remoteAttestationVerified) : false, + providerClientToken: isSet(object.providerClientToken) ? Buffer.from(bytesFromBase64(object.providerClientToken)) : Buffer.alloc(0), + protectionScheme: isSet(object.protectionScheme) ? Number(object.protectionScheme) : 0, + srmRequirement: isSet(object.srmRequirement) ? Buffer.from(bytesFromBase64(object.srmRequirement)) : Buffer.alloc(0), + srmUpdate: isSet(object.srmUpdate) ? Buffer.from(bytesFromBase64(object.srmUpdate)) : Buffer.alloc(0), + platformVerificationStatus: isSet(object.platformVerificationStatus) + ? platformVerificationStatusFromJSON(object.platformVerificationStatus) + : 0, + groupIds: Array.isArray(object?.groupIds) ? object.groupIds.map((e: any) => Buffer.from(bytesFromBase64(e))) : [] + }; + }, + + toJSON(message: License): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id ? LicenseIdentification.toJSON(message.id) : undefined); + message.policy !== undefined && (obj.policy = message.policy ? License_Policy.toJSON(message.policy) : undefined); + if (message.key) { + obj.key = message.key.map((e) => (e ? License_KeyContainer.toJSON(e) : undefined)); + } else { + obj.key = []; + } + message.licenseStartTime !== undefined && (obj.licenseStartTime = (message.licenseStartTime || Long.ZERO).toString()); + message.remoteAttestationVerified !== undefined && (obj.remoteAttestationVerified = message.remoteAttestationVerified); + message.providerClientToken !== undefined && + (obj.providerClientToken = base64FromBytes(message.providerClientToken !== undefined ? message.providerClientToken : Buffer.alloc(0))); + message.protectionScheme !== undefined && (obj.protectionScheme = Math.round(message.protectionScheme)); + message.srmRequirement !== undefined && + (obj.srmRequirement = base64FromBytes(message.srmRequirement !== undefined ? message.srmRequirement : Buffer.alloc(0))); + message.srmUpdate !== undefined && (obj.srmUpdate = base64FromBytes(message.srmUpdate !== undefined ? message.srmUpdate : Buffer.alloc(0))); + message.platformVerificationStatus !== undefined && + (obj.platformVerificationStatus = platformVerificationStatusToJSON(message.platformVerificationStatus)); + if (message.groupIds) { + obj.groupIds = message.groupIds.map((e) => base64FromBytes(e !== undefined ? e : Buffer.alloc(0))); + } else { + obj.groupIds = []; + } + return obj; + }, + + fromPartial, I>>(object: I): License { + const message = createBaseLicense(); + message.id = object.id !== undefined && object.id !== null ? LicenseIdentification.fromPartial(object.id) : undefined; + message.policy = object.policy !== undefined && object.policy !== null ? License_Policy.fromPartial(object.policy) : undefined; + message.key = object.key?.map((e) => License_KeyContainer.fromPartial(e)) || []; + message.licenseStartTime = + object.licenseStartTime !== undefined && object.licenseStartTime !== null ? Long.fromValue(object.licenseStartTime) : Long.ZERO; + message.remoteAttestationVerified = object.remoteAttestationVerified ?? false; + message.providerClientToken = object.providerClientToken ?? Buffer.alloc(0); + message.protectionScheme = object.protectionScheme ?? 0; + message.srmRequirement = object.srmRequirement ?? Buffer.alloc(0); + message.srmUpdate = object.srmUpdate ?? Buffer.alloc(0); + message.platformVerificationStatus = object.platformVerificationStatus ?? 0; + message.groupIds = object.groupIds?.map((e) => e) || []; + return message; + } +}; + +function createBaseLicense_Policy(): License_Policy { + return { + canPlay: false, + canPersist: false, + canRenew: false, + rentalDurationSeconds: Long.ZERO, + playbackDurationSeconds: Long.ZERO, + licenseDurationSeconds: Long.ZERO, + renewalRecoveryDurationSeconds: Long.ZERO, + renewalServerUrl: '', + renewalDelaySeconds: Long.ZERO, + renewalRetryIntervalSeconds: Long.ZERO, + renewWithUsage: false, + alwaysIncludeClientId: false, + playStartGracePeriodSeconds: Long.ZERO, + softEnforcePlaybackDuration: false, + softEnforceRentalDuration: false + }; +} + +export const License_Policy = { + encode(message: License_Policy, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.canPlay === true) { + writer.uint32(8).bool(message.canPlay); + } + if (message.canPersist === true) { + writer.uint32(16).bool(message.canPersist); + } + if (message.canRenew === true) { + writer.uint32(24).bool(message.canRenew); + } + if (!message.rentalDurationSeconds.isZero()) { + writer.uint32(32).int64(message.rentalDurationSeconds); + } + if (!message.playbackDurationSeconds.isZero()) { + writer.uint32(40).int64(message.playbackDurationSeconds); + } + if (!message.licenseDurationSeconds.isZero()) { + writer.uint32(48).int64(message.licenseDurationSeconds); + } + if (!message.renewalRecoveryDurationSeconds.isZero()) { + writer.uint32(56).int64(message.renewalRecoveryDurationSeconds); + } + if (message.renewalServerUrl !== '') { + writer.uint32(66).string(message.renewalServerUrl); + } + if (!message.renewalDelaySeconds.isZero()) { + writer.uint32(72).int64(message.renewalDelaySeconds); + } + if (!message.renewalRetryIntervalSeconds.isZero()) { + writer.uint32(80).int64(message.renewalRetryIntervalSeconds); + } + if (message.renewWithUsage === true) { + writer.uint32(88).bool(message.renewWithUsage); + } + if (message.alwaysIncludeClientId === true) { + writer.uint32(96).bool(message.alwaysIncludeClientId); + } + if (!message.playStartGracePeriodSeconds.isZero()) { + writer.uint32(104).int64(message.playStartGracePeriodSeconds); + } + if (message.softEnforcePlaybackDuration === true) { + writer.uint32(112).bool(message.softEnforcePlaybackDuration); + } + if (message.softEnforceRentalDuration === true) { + writer.uint32(120).bool(message.softEnforceRentalDuration); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): License_Policy { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicense_Policy(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.canPlay = reader.bool(); + break; + case 2: + message.canPersist = reader.bool(); + break; + case 3: + message.canRenew = reader.bool(); + break; + case 4: + message.rentalDurationSeconds = reader.int64() as Long; + break; + case 5: + message.playbackDurationSeconds = reader.int64() as Long; + break; + case 6: + message.licenseDurationSeconds = reader.int64() as Long; + break; + case 7: + message.renewalRecoveryDurationSeconds = reader.int64() as Long; + break; + case 8: + message.renewalServerUrl = reader.string(); + break; + case 9: + message.renewalDelaySeconds = reader.int64() as Long; + break; + case 10: + message.renewalRetryIntervalSeconds = reader.int64() as Long; + break; + case 11: + message.renewWithUsage = reader.bool(); + break; + case 12: + message.alwaysIncludeClientId = reader.bool(); + break; + case 13: + message.playStartGracePeriodSeconds = reader.int64() as Long; + break; + case 14: + message.softEnforcePlaybackDuration = reader.bool(); + break; + case 15: + message.softEnforceRentalDuration = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): License_Policy { + return { + canPlay: isSet(object.canPlay) ? Boolean(object.canPlay) : false, + canPersist: isSet(object.canPersist) ? Boolean(object.canPersist) : false, + canRenew: isSet(object.canRenew) ? Boolean(object.canRenew) : false, + rentalDurationSeconds: isSet(object.rentalDurationSeconds) ? Long.fromValue(object.rentalDurationSeconds) : Long.ZERO, + playbackDurationSeconds: isSet(object.playbackDurationSeconds) ? Long.fromValue(object.playbackDurationSeconds) : Long.ZERO, + licenseDurationSeconds: isSet(object.licenseDurationSeconds) ? Long.fromValue(object.licenseDurationSeconds) : Long.ZERO, + renewalRecoveryDurationSeconds: isSet(object.renewalRecoveryDurationSeconds) + ? Long.fromValue(object.renewalRecoveryDurationSeconds) + : Long.ZERO, + renewalServerUrl: isSet(object.renewalServerUrl) ? String(object.renewalServerUrl) : '', + renewalDelaySeconds: isSet(object.renewalDelaySeconds) ? Long.fromValue(object.renewalDelaySeconds) : Long.ZERO, + renewalRetryIntervalSeconds: isSet(object.renewalRetryIntervalSeconds) ? Long.fromValue(object.renewalRetryIntervalSeconds) : Long.ZERO, + renewWithUsage: isSet(object.renewWithUsage) ? Boolean(object.renewWithUsage) : false, + alwaysIncludeClientId: isSet(object.alwaysIncludeClientId) ? Boolean(object.alwaysIncludeClientId) : false, + playStartGracePeriodSeconds: isSet(object.playStartGracePeriodSeconds) ? Long.fromValue(object.playStartGracePeriodSeconds) : Long.ZERO, + softEnforcePlaybackDuration: isSet(object.softEnforcePlaybackDuration) ? Boolean(object.softEnforcePlaybackDuration) : false, + softEnforceRentalDuration: isSet(object.softEnforceRentalDuration) ? Boolean(object.softEnforceRentalDuration) : false + }; + }, + + toJSON(message: License_Policy): unknown { + const obj: any = {}; + message.canPlay !== undefined && (obj.canPlay = message.canPlay); + message.canPersist !== undefined && (obj.canPersist = message.canPersist); + message.canRenew !== undefined && (obj.canRenew = message.canRenew); + message.rentalDurationSeconds !== undefined && (obj.rentalDurationSeconds = (message.rentalDurationSeconds || Long.ZERO).toString()); + message.playbackDurationSeconds !== undefined && (obj.playbackDurationSeconds = (message.playbackDurationSeconds || Long.ZERO).toString()); + message.licenseDurationSeconds !== undefined && (obj.licenseDurationSeconds = (message.licenseDurationSeconds || Long.ZERO).toString()); + message.renewalRecoveryDurationSeconds !== undefined && + (obj.renewalRecoveryDurationSeconds = (message.renewalRecoveryDurationSeconds || Long.ZERO).toString()); + message.renewalServerUrl !== undefined && (obj.renewalServerUrl = message.renewalServerUrl); + message.renewalDelaySeconds !== undefined && (obj.renewalDelaySeconds = (message.renewalDelaySeconds || Long.ZERO).toString()); + message.renewalRetryIntervalSeconds !== undefined && + (obj.renewalRetryIntervalSeconds = (message.renewalRetryIntervalSeconds || Long.ZERO).toString()); + message.renewWithUsage !== undefined && (obj.renewWithUsage = message.renewWithUsage); + message.alwaysIncludeClientId !== undefined && (obj.alwaysIncludeClientId = message.alwaysIncludeClientId); + message.playStartGracePeriodSeconds !== undefined && + (obj.playStartGracePeriodSeconds = (message.playStartGracePeriodSeconds || Long.ZERO).toString()); + message.softEnforcePlaybackDuration !== undefined && (obj.softEnforcePlaybackDuration = message.softEnforcePlaybackDuration); + message.softEnforceRentalDuration !== undefined && (obj.softEnforceRentalDuration = message.softEnforceRentalDuration); + return obj; + }, + + fromPartial, I>>(object: I): License_Policy { + const message = createBaseLicense_Policy(); + message.canPlay = object.canPlay ?? false; + message.canPersist = object.canPersist ?? false; + message.canRenew = object.canRenew ?? false; + message.rentalDurationSeconds = + object.rentalDurationSeconds !== undefined && object.rentalDurationSeconds !== null ? Long.fromValue(object.rentalDurationSeconds) : Long.ZERO; + message.playbackDurationSeconds = + object.playbackDurationSeconds !== undefined && object.playbackDurationSeconds !== null + ? Long.fromValue(object.playbackDurationSeconds) + : Long.ZERO; + message.licenseDurationSeconds = + object.licenseDurationSeconds !== undefined && object.licenseDurationSeconds !== null + ? Long.fromValue(object.licenseDurationSeconds) + : Long.ZERO; + message.renewalRecoveryDurationSeconds = + object.renewalRecoveryDurationSeconds !== undefined && object.renewalRecoveryDurationSeconds !== null + ? Long.fromValue(object.renewalRecoveryDurationSeconds) + : Long.ZERO; + message.renewalServerUrl = object.renewalServerUrl ?? ''; + message.renewalDelaySeconds = + object.renewalDelaySeconds !== undefined && object.renewalDelaySeconds !== null ? Long.fromValue(object.renewalDelaySeconds) : Long.ZERO; + message.renewalRetryIntervalSeconds = + object.renewalRetryIntervalSeconds !== undefined && object.renewalRetryIntervalSeconds !== null + ? Long.fromValue(object.renewalRetryIntervalSeconds) + : Long.ZERO; + message.renewWithUsage = object.renewWithUsage ?? false; + message.alwaysIncludeClientId = object.alwaysIncludeClientId ?? false; + message.playStartGracePeriodSeconds = + object.playStartGracePeriodSeconds !== undefined && object.playStartGracePeriodSeconds !== null + ? Long.fromValue(object.playStartGracePeriodSeconds) + : Long.ZERO; + message.softEnforcePlaybackDuration = object.softEnforcePlaybackDuration ?? false; + message.softEnforceRentalDuration = object.softEnforceRentalDuration ?? false; + return message; + } +}; + +function createBaseLicense_KeyContainer(): License_KeyContainer { + return { + id: Buffer.alloc(0), + iv: Buffer.alloc(0), + key: Buffer.alloc(0), + type: 1, + level: 1, + requiredProtection: undefined, + requestedProtection: undefined, + keyControl: undefined, + operatorSessionKeyPermissions: undefined, + videoResolutionConstraints: [], + antiRollbackUsageTable: false, + trackLabel: '' + }; +} + +export const License_KeyContainer = { + encode(message: License_KeyContainer, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id.length !== 0) { + writer.uint32(10).bytes(message.id); + } + if (message.iv.length !== 0) { + writer.uint32(18).bytes(message.iv); + } + if (message.key.length !== 0) { + writer.uint32(26).bytes(message.key); + } + if (message.type !== 1) { + writer.uint32(32).int32(message.type); + } + if (message.level !== 1) { + writer.uint32(40).int32(message.level); + } + if (message.requiredProtection !== undefined) { + License_KeyContainer_OutputProtection.encode(message.requiredProtection, writer.uint32(50).fork()).ldelim(); + } + if (message.requestedProtection !== undefined) { + License_KeyContainer_OutputProtection.encode(message.requestedProtection, writer.uint32(58).fork()).ldelim(); + } + if (message.keyControl !== undefined) { + License_KeyContainer_KeyControl.encode(message.keyControl, writer.uint32(66).fork()).ldelim(); + } + if (message.operatorSessionKeyPermissions !== undefined) { + License_KeyContainer_OperatorSessionKeyPermissions.encode(message.operatorSessionKeyPermissions, writer.uint32(74).fork()).ldelim(); + } + for (const v of message.videoResolutionConstraints) { + License_KeyContainer_VideoResolutionConstraint.encode(v!, writer.uint32(82).fork()).ldelim(); + } + if (message.antiRollbackUsageTable === true) { + writer.uint32(88).bool(message.antiRollbackUsageTable); + } + if (message.trackLabel !== '') { + writer.uint32(98).string(message.trackLabel); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): License_KeyContainer { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicense_KeyContainer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.bytes() as Buffer; + break; + case 2: + message.iv = reader.bytes() as Buffer; + break; + case 3: + message.key = reader.bytes() as Buffer; + break; + case 4: + message.type = reader.int32() as any; + break; + case 5: + message.level = reader.int32() as any; + break; + case 6: + message.requiredProtection = License_KeyContainer_OutputProtection.decode(reader, reader.uint32()); + break; + case 7: + message.requestedProtection = License_KeyContainer_OutputProtection.decode(reader, reader.uint32()); + break; + case 8: + message.keyControl = License_KeyContainer_KeyControl.decode(reader, reader.uint32()); + break; + case 9: + message.operatorSessionKeyPermissions = License_KeyContainer_OperatorSessionKeyPermissions.decode(reader, reader.uint32()); + break; + case 10: + message.videoResolutionConstraints.push(License_KeyContainer_VideoResolutionConstraint.decode(reader, reader.uint32())); + break; + case 11: + message.antiRollbackUsageTable = reader.bool(); + break; + case 12: + message.trackLabel = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): License_KeyContainer { + return { + id: isSet(object.id) ? Buffer.from(bytesFromBase64(object.id)) : Buffer.alloc(0), + iv: isSet(object.iv) ? Buffer.from(bytesFromBase64(object.iv)) : Buffer.alloc(0), + key: isSet(object.key) ? Buffer.from(bytesFromBase64(object.key)) : Buffer.alloc(0), + type: isSet(object.type) ? license_KeyContainer_KeyTypeFromJSON(object.type) : 1, + level: isSet(object.level) ? license_KeyContainer_SecurityLevelFromJSON(object.level) : 1, + requiredProtection: isSet(object.requiredProtection) ? License_KeyContainer_OutputProtection.fromJSON(object.requiredProtection) : undefined, + requestedProtection: isSet(object.requestedProtection) ? License_KeyContainer_OutputProtection.fromJSON(object.requestedProtection) : undefined, + keyControl: isSet(object.keyControl) ? License_KeyContainer_KeyControl.fromJSON(object.keyControl) : undefined, + operatorSessionKeyPermissions: isSet(object.operatorSessionKeyPermissions) + ? License_KeyContainer_OperatorSessionKeyPermissions.fromJSON(object.operatorSessionKeyPermissions) + : undefined, + videoResolutionConstraints: Array.isArray(object?.videoResolutionConstraints) + ? object.videoResolutionConstraints.map((e: any) => License_KeyContainer_VideoResolutionConstraint.fromJSON(e)) + : [], + antiRollbackUsageTable: isSet(object.antiRollbackUsageTable) ? Boolean(object.antiRollbackUsageTable) : false, + trackLabel: isSet(object.trackLabel) ? String(object.trackLabel) : '' + }; + }, + + toJSON(message: License_KeyContainer): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = base64FromBytes(message.id !== undefined ? message.id : Buffer.alloc(0))); + message.iv !== undefined && (obj.iv = base64FromBytes(message.iv !== undefined ? message.iv : Buffer.alloc(0))); + message.key !== undefined && (obj.key = base64FromBytes(message.key !== undefined ? message.key : Buffer.alloc(0))); + message.type !== undefined && (obj.type = license_KeyContainer_KeyTypeToJSON(message.type)); + message.level !== undefined && (obj.level = license_KeyContainer_SecurityLevelToJSON(message.level)); + message.requiredProtection !== undefined && + (obj.requiredProtection = message.requiredProtection ? License_KeyContainer_OutputProtection.toJSON(message.requiredProtection) : undefined); + message.requestedProtection !== undefined && + (obj.requestedProtection = message.requestedProtection ? License_KeyContainer_OutputProtection.toJSON(message.requestedProtection) : undefined); + message.keyControl !== undefined && + (obj.keyControl = message.keyControl ? License_KeyContainer_KeyControl.toJSON(message.keyControl) : undefined); + message.operatorSessionKeyPermissions !== undefined && + (obj.operatorSessionKeyPermissions = message.operatorSessionKeyPermissions + ? License_KeyContainer_OperatorSessionKeyPermissions.toJSON(message.operatorSessionKeyPermissions) + : undefined); + if (message.videoResolutionConstraints) { + obj.videoResolutionConstraints = message.videoResolutionConstraints.map((e) => + e ? License_KeyContainer_VideoResolutionConstraint.toJSON(e) : undefined + ); + } else { + obj.videoResolutionConstraints = []; + } + message.antiRollbackUsageTable !== undefined && (obj.antiRollbackUsageTable = message.antiRollbackUsageTable); + message.trackLabel !== undefined && (obj.trackLabel = message.trackLabel); + return obj; + }, + + fromPartial, I>>(object: I): License_KeyContainer { + const message = createBaseLicense_KeyContainer(); + message.id = object.id ?? Buffer.alloc(0); + message.iv = object.iv ?? Buffer.alloc(0); + message.key = object.key ?? Buffer.alloc(0); + message.type = object.type ?? 1; + message.level = object.level ?? 1; + message.requiredProtection = + object.requiredProtection !== undefined && object.requiredProtection !== null + ? License_KeyContainer_OutputProtection.fromPartial(object.requiredProtection) + : undefined; + message.requestedProtection = + object.requestedProtection !== undefined && object.requestedProtection !== null + ? License_KeyContainer_OutputProtection.fromPartial(object.requestedProtection) + : undefined; + message.keyControl = + object.keyControl !== undefined && object.keyControl !== null ? License_KeyContainer_KeyControl.fromPartial(object.keyControl) : undefined; + message.operatorSessionKeyPermissions = + object.operatorSessionKeyPermissions !== undefined && object.operatorSessionKeyPermissions !== null + ? License_KeyContainer_OperatorSessionKeyPermissions.fromPartial(object.operatorSessionKeyPermissions) + : undefined; + message.videoResolutionConstraints = + object.videoResolutionConstraints?.map((e) => License_KeyContainer_VideoResolutionConstraint.fromPartial(e)) || []; + message.antiRollbackUsageTable = object.antiRollbackUsageTable ?? false; + message.trackLabel = object.trackLabel ?? ''; + return message; + } +}; + +function createBaseLicense_KeyContainer_KeyControl(): License_KeyContainer_KeyControl { + return { keyControlBlock: Buffer.alloc(0), iv: Buffer.alloc(0) }; +} + +export const License_KeyContainer_KeyControl = { + encode(message: License_KeyContainer_KeyControl, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.keyControlBlock.length !== 0) { + writer.uint32(10).bytes(message.keyControlBlock); + } + if (message.iv.length !== 0) { + writer.uint32(18).bytes(message.iv); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): License_KeyContainer_KeyControl { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicense_KeyContainer_KeyControl(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.keyControlBlock = reader.bytes() as Buffer; + break; + case 2: + message.iv = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): License_KeyContainer_KeyControl { + return { + keyControlBlock: isSet(object.keyControlBlock) ? Buffer.from(bytesFromBase64(object.keyControlBlock)) : Buffer.alloc(0), + iv: isSet(object.iv) ? Buffer.from(bytesFromBase64(object.iv)) : Buffer.alloc(0) + }; + }, + + toJSON(message: License_KeyContainer_KeyControl): unknown { + const obj: any = {}; + message.keyControlBlock !== undefined && + (obj.keyControlBlock = base64FromBytes(message.keyControlBlock !== undefined ? message.keyControlBlock : Buffer.alloc(0))); + message.iv !== undefined && (obj.iv = base64FromBytes(message.iv !== undefined ? message.iv : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>(object: I): License_KeyContainer_KeyControl { + const message = createBaseLicense_KeyContainer_KeyControl(); + message.keyControlBlock = object.keyControlBlock ?? Buffer.alloc(0); + message.iv = object.iv ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseLicense_KeyContainer_OutputProtection(): License_KeyContainer_OutputProtection { + return { hdcp: 0, cgmsFlags: 0, hdcpSrmRule: 0, disableAnalogOutput: false, disableDigitalOutput: false }; +} + +export const License_KeyContainer_OutputProtection = { + encode(message: License_KeyContainer_OutputProtection, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.hdcp !== 0) { + writer.uint32(8).int32(message.hdcp); + } + if (message.cgmsFlags !== 0) { + writer.uint32(16).int32(message.cgmsFlags); + } + if (message.hdcpSrmRule !== 0) { + writer.uint32(24).int32(message.hdcpSrmRule); + } + if (message.disableAnalogOutput === true) { + writer.uint32(32).bool(message.disableAnalogOutput); + } + if (message.disableDigitalOutput === true) { + writer.uint32(40).bool(message.disableDigitalOutput); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): License_KeyContainer_OutputProtection { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicense_KeyContainer_OutputProtection(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.hdcp = reader.int32() as any; + break; + case 2: + message.cgmsFlags = reader.int32() as any; + break; + case 3: + message.hdcpSrmRule = reader.int32() as any; + break; + case 4: + message.disableAnalogOutput = reader.bool(); + break; + case 5: + message.disableDigitalOutput = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): License_KeyContainer_OutputProtection { + return { + hdcp: isSet(object.hdcp) ? license_KeyContainer_OutputProtection_HDCPFromJSON(object.hdcp) : 0, + cgmsFlags: isSet(object.cgmsFlags) ? license_KeyContainer_OutputProtection_CGMSFromJSON(object.cgmsFlags) : 0, + hdcpSrmRule: isSet(object.hdcpSrmRule) ? license_KeyContainer_OutputProtection_HdcpSrmRuleFromJSON(object.hdcpSrmRule) : 0, + disableAnalogOutput: isSet(object.disableAnalogOutput) ? Boolean(object.disableAnalogOutput) : false, + disableDigitalOutput: isSet(object.disableDigitalOutput) ? Boolean(object.disableDigitalOutput) : false + }; + }, + + toJSON(message: License_KeyContainer_OutputProtection): unknown { + const obj: any = {}; + message.hdcp !== undefined && (obj.hdcp = license_KeyContainer_OutputProtection_HDCPToJSON(message.hdcp)); + message.cgmsFlags !== undefined && (obj.cgmsFlags = license_KeyContainer_OutputProtection_CGMSToJSON(message.cgmsFlags)); + message.hdcpSrmRule !== undefined && (obj.hdcpSrmRule = license_KeyContainer_OutputProtection_HdcpSrmRuleToJSON(message.hdcpSrmRule)); + message.disableAnalogOutput !== undefined && (obj.disableAnalogOutput = message.disableAnalogOutput); + message.disableDigitalOutput !== undefined && (obj.disableDigitalOutput = message.disableDigitalOutput); + return obj; + }, + + fromPartial, I>>(object: I): License_KeyContainer_OutputProtection { + const message = createBaseLicense_KeyContainer_OutputProtection(); + message.hdcp = object.hdcp ?? 0; + message.cgmsFlags = object.cgmsFlags ?? 0; + message.hdcpSrmRule = object.hdcpSrmRule ?? 0; + message.disableAnalogOutput = object.disableAnalogOutput ?? false; + message.disableDigitalOutput = object.disableDigitalOutput ?? false; + return message; + } +}; + +function createBaseLicense_KeyContainer_VideoResolutionConstraint(): License_KeyContainer_VideoResolutionConstraint { + return { minResolutionPixels: 0, maxResolutionPixels: 0, requiredProtection: undefined }; +} + +export const License_KeyContainer_VideoResolutionConstraint = { + encode(message: License_KeyContainer_VideoResolutionConstraint, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.minResolutionPixels !== 0) { + writer.uint32(8).uint32(message.minResolutionPixels); + } + if (message.maxResolutionPixels !== 0) { + writer.uint32(16).uint32(message.maxResolutionPixels); + } + if (message.requiredProtection !== undefined) { + License_KeyContainer_OutputProtection.encode(message.requiredProtection, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): License_KeyContainer_VideoResolutionConstraint { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicense_KeyContainer_VideoResolutionConstraint(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.minResolutionPixels = reader.uint32(); + break; + case 2: + message.maxResolutionPixels = reader.uint32(); + break; + case 3: + message.requiredProtection = License_KeyContainer_OutputProtection.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): License_KeyContainer_VideoResolutionConstraint { + return { + minResolutionPixels: isSet(object.minResolutionPixels) ? Number(object.minResolutionPixels) : 0, + maxResolutionPixels: isSet(object.maxResolutionPixels) ? Number(object.maxResolutionPixels) : 0, + requiredProtection: isSet(object.requiredProtection) ? License_KeyContainer_OutputProtection.fromJSON(object.requiredProtection) : undefined + }; + }, + + toJSON(message: License_KeyContainer_VideoResolutionConstraint): unknown { + const obj: any = {}; + message.minResolutionPixels !== undefined && (obj.minResolutionPixels = Math.round(message.minResolutionPixels)); + message.maxResolutionPixels !== undefined && (obj.maxResolutionPixels = Math.round(message.maxResolutionPixels)); + message.requiredProtection !== undefined && + (obj.requiredProtection = message.requiredProtection ? License_KeyContainer_OutputProtection.toJSON(message.requiredProtection) : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): License_KeyContainer_VideoResolutionConstraint { + const message = createBaseLicense_KeyContainer_VideoResolutionConstraint(); + message.minResolutionPixels = object.minResolutionPixels ?? 0; + message.maxResolutionPixels = object.maxResolutionPixels ?? 0; + message.requiredProtection = + object.requiredProtection !== undefined && object.requiredProtection !== null + ? License_KeyContainer_OutputProtection.fromPartial(object.requiredProtection) + : undefined; + return message; + } +}; + +function createBaseLicense_KeyContainer_OperatorSessionKeyPermissions(): License_KeyContainer_OperatorSessionKeyPermissions { + return { allowEncrypt: false, allowDecrypt: false, allowSign: false, allowSignatureVerify: false }; +} + +export const License_KeyContainer_OperatorSessionKeyPermissions = { + encode(message: License_KeyContainer_OperatorSessionKeyPermissions, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.allowEncrypt === true) { + writer.uint32(8).bool(message.allowEncrypt); + } + if (message.allowDecrypt === true) { + writer.uint32(16).bool(message.allowDecrypt); + } + if (message.allowSign === true) { + writer.uint32(24).bool(message.allowSign); + } + if (message.allowSignatureVerify === true) { + writer.uint32(32).bool(message.allowSignatureVerify); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): License_KeyContainer_OperatorSessionKeyPermissions { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicense_KeyContainer_OperatorSessionKeyPermissions(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.allowEncrypt = reader.bool(); + break; + case 2: + message.allowDecrypt = reader.bool(); + break; + case 3: + message.allowSign = reader.bool(); + break; + case 4: + message.allowSignatureVerify = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): License_KeyContainer_OperatorSessionKeyPermissions { + return { + allowEncrypt: isSet(object.allowEncrypt) ? Boolean(object.allowEncrypt) : false, + allowDecrypt: isSet(object.allowDecrypt) ? Boolean(object.allowDecrypt) : false, + allowSign: isSet(object.allowSign) ? Boolean(object.allowSign) : false, + allowSignatureVerify: isSet(object.allowSignatureVerify) ? Boolean(object.allowSignatureVerify) : false + }; + }, + + toJSON(message: License_KeyContainer_OperatorSessionKeyPermissions): unknown { + const obj: any = {}; + message.allowEncrypt !== undefined && (obj.allowEncrypt = message.allowEncrypt); + message.allowDecrypt !== undefined && (obj.allowDecrypt = message.allowDecrypt); + message.allowSign !== undefined && (obj.allowSign = message.allowSign); + message.allowSignatureVerify !== undefined && (obj.allowSignatureVerify = message.allowSignatureVerify); + return obj; + }, + + fromPartial, I>>( + object: I + ): License_KeyContainer_OperatorSessionKeyPermissions { + const message = createBaseLicense_KeyContainer_OperatorSessionKeyPermissions(); + message.allowEncrypt = object.allowEncrypt ?? false; + message.allowDecrypt = object.allowDecrypt ?? false; + message.allowSign = object.allowSign ?? false; + message.allowSignatureVerify = object.allowSignatureVerify ?? false; + return message; + } +}; + +function createBaseLicenseRequest(): LicenseRequest { + return { + clientId: undefined, + contentId: undefined, + type: 1, + requestTime: Long.ZERO, + keyControlNonceDeprecated: Buffer.alloc(0), + protocolVersion: 20, + keyControlNonce: 0, + encryptedClientId: undefined + }; +} + +export const LicenseRequest = { + encode(message: LicenseRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.clientId !== undefined) { + ClientIdentification.encode(message.clientId, writer.uint32(10).fork()).ldelim(); + } + if (message.contentId !== undefined) { + LicenseRequest_ContentIdentification.encode(message.contentId, writer.uint32(18).fork()).ldelim(); + } + if (message.type !== 1) { + writer.uint32(24).int32(message.type); + } + if (!message.requestTime.isZero()) { + writer.uint32(32).int64(message.requestTime); + } + if (message.keyControlNonceDeprecated.length !== 0) { + writer.uint32(42).bytes(message.keyControlNonceDeprecated); + } + if (message.protocolVersion !== 20) { + writer.uint32(48).int32(message.protocolVersion); + } + if (message.keyControlNonce !== 0) { + writer.uint32(56).uint32(message.keyControlNonce); + } + if (message.encryptedClientId !== undefined) { + EncryptedClientIdentification.encode(message.encryptedClientId, writer.uint32(66).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LicenseRequest { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicenseRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.clientId = ClientIdentification.decode(reader, reader.uint32()); + break; + case 2: + message.contentId = LicenseRequest_ContentIdentification.decode(reader, reader.uint32()); + break; + case 3: + message.type = reader.int32() as any; + break; + case 4: + message.requestTime = reader.int64() as Long; + break; + case 5: + message.keyControlNonceDeprecated = reader.bytes() as Buffer; + break; + case 6: + message.protocolVersion = reader.int32() as any; + break; + case 7: + message.keyControlNonce = reader.uint32(); + break; + case 8: + message.encryptedClientId = EncryptedClientIdentification.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LicenseRequest { + return { + clientId: isSet(object.clientId) ? ClientIdentification.fromJSON(object.clientId) : undefined, + contentId: isSet(object.contentId) ? LicenseRequest_ContentIdentification.fromJSON(object.contentId) : undefined, + type: isSet(object.type) ? licenseRequest_RequestTypeFromJSON(object.type) : 1, + requestTime: isSet(object.requestTime) ? Long.fromValue(object.requestTime) : Long.ZERO, + keyControlNonceDeprecated: isSet(object.keyControlNonceDeprecated) + ? Buffer.from(bytesFromBase64(object.keyControlNonceDeprecated)) + : Buffer.alloc(0), + protocolVersion: isSet(object.protocolVersion) ? protocolVersionFromJSON(object.protocolVersion) : 20, + keyControlNonce: isSet(object.keyControlNonce) ? Number(object.keyControlNonce) : 0, + encryptedClientId: isSet(object.encryptedClientId) ? EncryptedClientIdentification.fromJSON(object.encryptedClientId) : undefined + }; + }, + + toJSON(message: LicenseRequest): unknown { + const obj: any = {}; + message.clientId !== undefined && (obj.clientId = message.clientId ? ClientIdentification.toJSON(message.clientId) : undefined); + message.contentId !== undefined && + (obj.contentId = message.contentId ? LicenseRequest_ContentIdentification.toJSON(message.contentId) : undefined); + message.type !== undefined && (obj.type = licenseRequest_RequestTypeToJSON(message.type)); + message.requestTime !== undefined && (obj.requestTime = (message.requestTime || Long.ZERO).toString()); + message.keyControlNonceDeprecated !== undefined && + (obj.keyControlNonceDeprecated = base64FromBytes( + message.keyControlNonceDeprecated !== undefined ? message.keyControlNonceDeprecated : Buffer.alloc(0) + )); + message.protocolVersion !== undefined && (obj.protocolVersion = protocolVersionToJSON(message.protocolVersion)); + message.keyControlNonce !== undefined && (obj.keyControlNonce = Math.round(message.keyControlNonce)); + message.encryptedClientId !== undefined && + (obj.encryptedClientId = message.encryptedClientId ? EncryptedClientIdentification.toJSON(message.encryptedClientId) : undefined); + return obj; + }, + + fromPartial, I>>(object: I): LicenseRequest { + const message = createBaseLicenseRequest(); + message.clientId = object.clientId !== undefined && object.clientId !== null ? ClientIdentification.fromPartial(object.clientId) : undefined; + message.contentId = + object.contentId !== undefined && object.contentId !== null ? LicenseRequest_ContentIdentification.fromPartial(object.contentId) : undefined; + message.type = object.type ?? 1; + message.requestTime = object.requestTime !== undefined && object.requestTime !== null ? Long.fromValue(object.requestTime) : Long.ZERO; + message.keyControlNonceDeprecated = object.keyControlNonceDeprecated ?? Buffer.alloc(0); + message.protocolVersion = object.protocolVersion ?? 20; + message.keyControlNonce = object.keyControlNonce ?? 0; + message.encryptedClientId = + object.encryptedClientId !== undefined && object.encryptedClientId !== null + ? EncryptedClientIdentification.fromPartial(object.encryptedClientId) + : undefined; + return message; + } +}; + +function createBaseLicenseRequest_ContentIdentification(): LicenseRequest_ContentIdentification { + return { widevinePsshData: undefined, webmKeyId: undefined, existingLicense: undefined, initData: undefined }; +} + +export const LicenseRequest_ContentIdentification = { + encode(message: LicenseRequest_ContentIdentification, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.widevinePsshData !== undefined) { + LicenseRequest_ContentIdentification_WidevinePsshData.encode(message.widevinePsshData, writer.uint32(10).fork()).ldelim(); + } + if (message.webmKeyId !== undefined) { + LicenseRequest_ContentIdentification_WebmKeyId.encode(message.webmKeyId, writer.uint32(18).fork()).ldelim(); + } + if (message.existingLicense !== undefined) { + LicenseRequest_ContentIdentification_ExistingLicense.encode(message.existingLicense, writer.uint32(26).fork()).ldelim(); + } + if (message.initData !== undefined) { + LicenseRequest_ContentIdentification_InitData.encode(message.initData, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LicenseRequest_ContentIdentification { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicenseRequest_ContentIdentification(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.widevinePsshData = LicenseRequest_ContentIdentification_WidevinePsshData.decode(reader, reader.uint32()); + break; + case 2: + message.webmKeyId = LicenseRequest_ContentIdentification_WebmKeyId.decode(reader, reader.uint32()); + break; + case 3: + message.existingLicense = LicenseRequest_ContentIdentification_ExistingLicense.decode(reader, reader.uint32()); + break; + case 4: + message.initData = LicenseRequest_ContentIdentification_InitData.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LicenseRequest_ContentIdentification { + return { + widevinePsshData: isSet(object.widevinePsshData) + ? LicenseRequest_ContentIdentification_WidevinePsshData.fromJSON(object.widevinePsshData) + : undefined, + webmKeyId: isSet(object.webmKeyId) ? LicenseRequest_ContentIdentification_WebmKeyId.fromJSON(object.webmKeyId) : undefined, + existingLicense: isSet(object.existingLicense) + ? LicenseRequest_ContentIdentification_ExistingLicense.fromJSON(object.existingLicense) + : undefined, + initData: isSet(object.initData) ? LicenseRequest_ContentIdentification_InitData.fromJSON(object.initData) : undefined + }; + }, + + toJSON(message: LicenseRequest_ContentIdentification): unknown { + const obj: any = {}; + message.widevinePsshData !== undefined && + (obj.widevinePsshData = message.widevinePsshData + ? LicenseRequest_ContentIdentification_WidevinePsshData.toJSON(message.widevinePsshData) + : undefined); + message.webmKeyId !== undefined && + (obj.webmKeyId = message.webmKeyId ? LicenseRequest_ContentIdentification_WebmKeyId.toJSON(message.webmKeyId) : undefined); + message.existingLicense !== undefined && + (obj.existingLicense = message.existingLicense + ? LicenseRequest_ContentIdentification_ExistingLicense.toJSON(message.existingLicense) + : undefined); + message.initData !== undefined && + (obj.initData = message.initData ? LicenseRequest_ContentIdentification_InitData.toJSON(message.initData) : undefined); + return obj; + }, + + fromPartial, I>>(object: I): LicenseRequest_ContentIdentification { + const message = createBaseLicenseRequest_ContentIdentification(); + message.widevinePsshData = + object.widevinePsshData !== undefined && object.widevinePsshData !== null + ? LicenseRequest_ContentIdentification_WidevinePsshData.fromPartial(object.widevinePsshData) + : undefined; + message.webmKeyId = + object.webmKeyId !== undefined && object.webmKeyId !== null + ? LicenseRequest_ContentIdentification_WebmKeyId.fromPartial(object.webmKeyId) + : undefined; + message.existingLicense = + object.existingLicense !== undefined && object.existingLicense !== null + ? LicenseRequest_ContentIdentification_ExistingLicense.fromPartial(object.existingLicense) + : undefined; + message.initData = + object.initData !== undefined && object.initData !== null + ? LicenseRequest_ContentIdentification_InitData.fromPartial(object.initData) + : undefined; + return message; + } +}; + +function createBaseLicenseRequest_ContentIdentification_WidevinePsshData(): LicenseRequest_ContentIdentification_WidevinePsshData { + return { psshData: [], licenseType: 1, requestId: Buffer.alloc(0) }; +} + +export const LicenseRequest_ContentIdentification_WidevinePsshData = { + encode(message: LicenseRequest_ContentIdentification_WidevinePsshData, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.psshData) { + writer.uint32(10).bytes(v!); + } + if (message.licenseType !== 1) { + writer.uint32(16).int32(message.licenseType); + } + if (message.requestId.length !== 0) { + writer.uint32(26).bytes(message.requestId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LicenseRequest_ContentIdentification_WidevinePsshData { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicenseRequest_ContentIdentification_WidevinePsshData(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.psshData.push(reader.bytes() as Buffer); + break; + case 2: + message.licenseType = reader.int32() as any; + break; + case 3: + message.requestId = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LicenseRequest_ContentIdentification_WidevinePsshData { + return { + psshData: Array.isArray(object?.psshData) ? object.psshData.map((e: any) => Buffer.from(bytesFromBase64(e))) : [], + licenseType: isSet(object.licenseType) ? licenseTypeFromJSON(object.licenseType) : 1, + requestId: isSet(object.requestId) ? Buffer.from(bytesFromBase64(object.requestId)) : Buffer.alloc(0) + }; + }, + + toJSON(message: LicenseRequest_ContentIdentification_WidevinePsshData): unknown { + const obj: any = {}; + if (message.psshData) { + obj.psshData = message.psshData.map((e) => base64FromBytes(e !== undefined ? e : Buffer.alloc(0))); + } else { + obj.psshData = []; + } + message.licenseType !== undefined && (obj.licenseType = licenseTypeToJSON(message.licenseType)); + message.requestId !== undefined && (obj.requestId = base64FromBytes(message.requestId !== undefined ? message.requestId : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>( + object: I + ): LicenseRequest_ContentIdentification_WidevinePsshData { + const message = createBaseLicenseRequest_ContentIdentification_WidevinePsshData(); + message.psshData = object.psshData?.map((e) => e) || []; + message.licenseType = object.licenseType ?? 1; + message.requestId = object.requestId ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseLicenseRequest_ContentIdentification_WebmKeyId(): LicenseRequest_ContentIdentification_WebmKeyId { + return { header: Buffer.alloc(0), licenseType: 1, requestId: Buffer.alloc(0) }; +} + +export const LicenseRequest_ContentIdentification_WebmKeyId = { + encode(message: LicenseRequest_ContentIdentification_WebmKeyId, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.header.length !== 0) { + writer.uint32(10).bytes(message.header); + } + if (message.licenseType !== 1) { + writer.uint32(16).int32(message.licenseType); + } + if (message.requestId.length !== 0) { + writer.uint32(26).bytes(message.requestId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LicenseRequest_ContentIdentification_WebmKeyId { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicenseRequest_ContentIdentification_WebmKeyId(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.header = reader.bytes() as Buffer; + break; + case 2: + message.licenseType = reader.int32() as any; + break; + case 3: + message.requestId = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LicenseRequest_ContentIdentification_WebmKeyId { + return { + header: isSet(object.header) ? Buffer.from(bytesFromBase64(object.header)) : Buffer.alloc(0), + licenseType: isSet(object.licenseType) ? licenseTypeFromJSON(object.licenseType) : 1, + requestId: isSet(object.requestId) ? Buffer.from(bytesFromBase64(object.requestId)) : Buffer.alloc(0) + }; + }, + + toJSON(message: LicenseRequest_ContentIdentification_WebmKeyId): unknown { + const obj: any = {}; + message.header !== undefined && (obj.header = base64FromBytes(message.header !== undefined ? message.header : Buffer.alloc(0))); + message.licenseType !== undefined && (obj.licenseType = licenseTypeToJSON(message.licenseType)); + message.requestId !== undefined && (obj.requestId = base64FromBytes(message.requestId !== undefined ? message.requestId : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>( + object: I + ): LicenseRequest_ContentIdentification_WebmKeyId { + const message = createBaseLicenseRequest_ContentIdentification_WebmKeyId(); + message.header = object.header ?? Buffer.alloc(0); + message.licenseType = object.licenseType ?? 1; + message.requestId = object.requestId ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseLicenseRequest_ContentIdentification_ExistingLicense(): LicenseRequest_ContentIdentification_ExistingLicense { + return { + licenseId: undefined, + secondsSinceStarted: Long.ZERO, + secondsSinceLastPlayed: Long.ZERO, + sessionUsageTableEntry: Buffer.alloc(0) + }; +} + +export const LicenseRequest_ContentIdentification_ExistingLicense = { + encode(message: LicenseRequest_ContentIdentification_ExistingLicense, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.licenseId !== undefined) { + LicenseIdentification.encode(message.licenseId, writer.uint32(10).fork()).ldelim(); + } + if (!message.secondsSinceStarted.isZero()) { + writer.uint32(16).int64(message.secondsSinceStarted); + } + if (!message.secondsSinceLastPlayed.isZero()) { + writer.uint32(24).int64(message.secondsSinceLastPlayed); + } + if (message.sessionUsageTableEntry.length !== 0) { + writer.uint32(34).bytes(message.sessionUsageTableEntry); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LicenseRequest_ContentIdentification_ExistingLicense { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicenseRequest_ContentIdentification_ExistingLicense(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.licenseId = LicenseIdentification.decode(reader, reader.uint32()); + break; + case 2: + message.secondsSinceStarted = reader.int64() as Long; + break; + case 3: + message.secondsSinceLastPlayed = reader.int64() as Long; + break; + case 4: + message.sessionUsageTableEntry = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LicenseRequest_ContentIdentification_ExistingLicense { + return { + licenseId: isSet(object.licenseId) ? LicenseIdentification.fromJSON(object.licenseId) : undefined, + secondsSinceStarted: isSet(object.secondsSinceStarted) ? Long.fromValue(object.secondsSinceStarted) : Long.ZERO, + secondsSinceLastPlayed: isSet(object.secondsSinceLastPlayed) ? Long.fromValue(object.secondsSinceLastPlayed) : Long.ZERO, + sessionUsageTableEntry: isSet(object.sessionUsageTableEntry) ? Buffer.from(bytesFromBase64(object.sessionUsageTableEntry)) : Buffer.alloc(0) + }; + }, + + toJSON(message: LicenseRequest_ContentIdentification_ExistingLicense): unknown { + const obj: any = {}; + message.licenseId !== undefined && (obj.licenseId = message.licenseId ? LicenseIdentification.toJSON(message.licenseId) : undefined); + message.secondsSinceStarted !== undefined && (obj.secondsSinceStarted = (message.secondsSinceStarted || Long.ZERO).toString()); + message.secondsSinceLastPlayed !== undefined && (obj.secondsSinceLastPlayed = (message.secondsSinceLastPlayed || Long.ZERO).toString()); + message.sessionUsageTableEntry !== undefined && + (obj.sessionUsageTableEntry = base64FromBytes(message.sessionUsageTableEntry !== undefined ? message.sessionUsageTableEntry : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>( + object: I + ): LicenseRequest_ContentIdentification_ExistingLicense { + const message = createBaseLicenseRequest_ContentIdentification_ExistingLicense(); + message.licenseId = object.licenseId !== undefined && object.licenseId !== null ? LicenseIdentification.fromPartial(object.licenseId) : undefined; + message.secondsSinceStarted = + object.secondsSinceStarted !== undefined && object.secondsSinceStarted !== null ? Long.fromValue(object.secondsSinceStarted) : Long.ZERO; + message.secondsSinceLastPlayed = + object.secondsSinceLastPlayed !== undefined && object.secondsSinceLastPlayed !== null + ? Long.fromValue(object.secondsSinceLastPlayed) + : Long.ZERO; + message.sessionUsageTableEntry = object.sessionUsageTableEntry ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseLicenseRequest_ContentIdentification_InitData(): LicenseRequest_ContentIdentification_InitData { + return { initDataType: 1, initData: Buffer.alloc(0), licenseType: 1, requestId: Buffer.alloc(0) }; +} + +export const LicenseRequest_ContentIdentification_InitData = { + encode(message: LicenseRequest_ContentIdentification_InitData, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.initDataType !== 1) { + writer.uint32(8).int32(message.initDataType); + } + if (message.initData.length !== 0) { + writer.uint32(18).bytes(message.initData); + } + if (message.licenseType !== 1) { + writer.uint32(24).int32(message.licenseType); + } + if (message.requestId.length !== 0) { + writer.uint32(34).bytes(message.requestId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LicenseRequest_ContentIdentification_InitData { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLicenseRequest_ContentIdentification_InitData(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.initDataType = reader.int32() as any; + break; + case 2: + message.initData = reader.bytes() as Buffer; + break; + case 3: + message.licenseType = reader.int32() as any; + break; + case 4: + message.requestId = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LicenseRequest_ContentIdentification_InitData { + return { + initDataType: isSet(object.initDataType) ? licenseRequest_ContentIdentification_InitData_InitDataTypeFromJSON(object.initDataType) : 1, + initData: isSet(object.initData) ? Buffer.from(bytesFromBase64(object.initData)) : Buffer.alloc(0), + licenseType: isSet(object.licenseType) ? licenseTypeFromJSON(object.licenseType) : 1, + requestId: isSet(object.requestId) ? Buffer.from(bytesFromBase64(object.requestId)) : Buffer.alloc(0) + }; + }, + + toJSON(message: LicenseRequest_ContentIdentification_InitData): unknown { + const obj: any = {}; + message.initDataType !== undefined && (obj.initDataType = licenseRequest_ContentIdentification_InitData_InitDataTypeToJSON(message.initDataType)); + message.initData !== undefined && (obj.initData = base64FromBytes(message.initData !== undefined ? message.initData : Buffer.alloc(0))); + message.licenseType !== undefined && (obj.licenseType = licenseTypeToJSON(message.licenseType)); + message.requestId !== undefined && (obj.requestId = base64FromBytes(message.requestId !== undefined ? message.requestId : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>( + object: I + ): LicenseRequest_ContentIdentification_InitData { + const message = createBaseLicenseRequest_ContentIdentification_InitData(); + message.initDataType = object.initDataType ?? 1; + message.initData = object.initData ?? Buffer.alloc(0); + message.licenseType = object.licenseType ?? 1; + message.requestId = object.requestId ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseMetricData(): MetricData { + return { stageName: '', metricData: [] }; +} + +export const MetricData = { + encode(message: MetricData, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.stageName !== '') { + writer.uint32(10).string(message.stageName); + } + for (const v of message.metricData) { + MetricData_TypeValue.encode(v!, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MetricData { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMetricData(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.stageName = reader.string(); + break; + case 2: + message.metricData.push(MetricData_TypeValue.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MetricData { + return { + stageName: isSet(object.stageName) ? String(object.stageName) : '', + metricData: Array.isArray(object?.metricData) ? object.metricData.map((e: any) => MetricData_TypeValue.fromJSON(e)) : [] + }; + }, + + toJSON(message: MetricData): unknown { + const obj: any = {}; + message.stageName !== undefined && (obj.stageName = message.stageName); + if (message.metricData) { + obj.metricData = message.metricData.map((e) => (e ? MetricData_TypeValue.toJSON(e) : undefined)); + } else { + obj.metricData = []; + } + return obj; + }, + + fromPartial, I>>(object: I): MetricData { + const message = createBaseMetricData(); + message.stageName = object.stageName ?? ''; + message.metricData = object.metricData?.map((e) => MetricData_TypeValue.fromPartial(e)) || []; + return message; + } +}; + +function createBaseMetricData_TypeValue(): MetricData_TypeValue { + return { type: 1, value: Long.ZERO }; +} + +export const MetricData_TypeValue = { + encode(message: MetricData_TypeValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== 1) { + writer.uint32(8).int32(message.type); + } + if (!message.value.isZero()) { + writer.uint32(16).int64(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MetricData_TypeValue { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMetricData_TypeValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.type = reader.int32() as any; + break; + case 2: + message.value = reader.int64() as Long; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MetricData_TypeValue { + return { + type: isSet(object.type) ? metricData_MetricTypeFromJSON(object.type) : 1, + value: isSet(object.value) ? Long.fromValue(object.value) : Long.ZERO + }; + }, + + toJSON(message: MetricData_TypeValue): unknown { + const obj: any = {}; + message.type !== undefined && (obj.type = metricData_MetricTypeToJSON(message.type)); + message.value !== undefined && (obj.value = (message.value || Long.ZERO).toString()); + return obj; + }, + + fromPartial, I>>(object: I): MetricData_TypeValue { + const message = createBaseMetricData_TypeValue(); + message.type = object.type ?? 1; + message.value = object.value !== undefined && object.value !== null ? Long.fromValue(object.value) : Long.ZERO; + return message; + } +}; + +function createBaseVersionInfo(): VersionInfo { + return { licenseSdkVersion: '', licenseServiceVersion: '' }; +} + +export const VersionInfo = { + encode(message: VersionInfo, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.licenseSdkVersion !== '') { + writer.uint32(10).string(message.licenseSdkVersion); + } + if (message.licenseServiceVersion !== '') { + writer.uint32(18).string(message.licenseServiceVersion); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): VersionInfo { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVersionInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.licenseSdkVersion = reader.string(); + break; + case 2: + message.licenseServiceVersion = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): VersionInfo { + return { + licenseSdkVersion: isSet(object.licenseSdkVersion) ? String(object.licenseSdkVersion) : '', + licenseServiceVersion: isSet(object.licenseServiceVersion) ? String(object.licenseServiceVersion) : '' + }; + }, + + toJSON(message: VersionInfo): unknown { + const obj: any = {}; + message.licenseSdkVersion !== undefined && (obj.licenseSdkVersion = message.licenseSdkVersion); + message.licenseServiceVersion !== undefined && (obj.licenseServiceVersion = message.licenseServiceVersion); + return obj; + }, + + fromPartial, I>>(object: I): VersionInfo { + const message = createBaseVersionInfo(); + message.licenseSdkVersion = object.licenseSdkVersion ?? ''; + message.licenseServiceVersion = object.licenseServiceVersion ?? ''; + return message; + } +}; + +function createBaseSignedMessage(): SignedMessage { + return { + type: 1, + msg: Buffer.alloc(0), + signature: Buffer.alloc(0), + sessionKey: Buffer.alloc(0), + remoteAttestation: Buffer.alloc(0), + metricData: [], + serviceVersionInfo: undefined, + sessionKeyType: 0, + oemcryptoCoreMessage: Buffer.alloc(0) + }; +} + +export const SignedMessage = { + encode(message: SignedMessage, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== 1) { + writer.uint32(8).int32(message.type); + } + if (message.msg.length !== 0) { + writer.uint32(18).bytes(message.msg); + } + if (message.signature.length !== 0) { + writer.uint32(26).bytes(message.signature); + } + if (message.sessionKey.length !== 0) { + writer.uint32(34).bytes(message.sessionKey); + } + if (message.remoteAttestation.length !== 0) { + writer.uint32(42).bytes(message.remoteAttestation); + } + for (const v of message.metricData) { + MetricData.encode(v!, writer.uint32(50).fork()).ldelim(); + } + if (message.serviceVersionInfo !== undefined) { + VersionInfo.encode(message.serviceVersionInfo, writer.uint32(58).fork()).ldelim(); + } + if (message.sessionKeyType !== 0) { + writer.uint32(64).int32(message.sessionKeyType); + } + if (message.oemcryptoCoreMessage.length !== 0) { + writer.uint32(74).bytes(message.oemcryptoCoreMessage); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SignedMessage { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSignedMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.type = reader.int32() as any; + break; + case 2: + message.msg = reader.bytes() as Buffer; + break; + case 3: + message.signature = reader.bytes() as Buffer; + break; + case 4: + message.sessionKey = reader.bytes() as Buffer; + break; + case 5: + message.remoteAttestation = reader.bytes() as Buffer; + break; + case 6: + message.metricData.push(MetricData.decode(reader, reader.uint32())); + break; + case 7: + message.serviceVersionInfo = VersionInfo.decode(reader, reader.uint32()); + break; + case 8: + message.sessionKeyType = reader.int32() as any; + break; + case 9: + message.oemcryptoCoreMessage = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SignedMessage { + return { + type: isSet(object.type) ? signedMessage_MessageTypeFromJSON(object.type) : 1, + msg: isSet(object.msg) ? Buffer.from(bytesFromBase64(object.msg)) : Buffer.alloc(0), + signature: isSet(object.signature) ? Buffer.from(bytesFromBase64(object.signature)) : Buffer.alloc(0), + sessionKey: isSet(object.sessionKey) ? Buffer.from(bytesFromBase64(object.sessionKey)) : Buffer.alloc(0), + remoteAttestation: isSet(object.remoteAttestation) ? Buffer.from(bytesFromBase64(object.remoteAttestation)) : Buffer.alloc(0), + metricData: Array.isArray(object?.metricData) ? object.metricData.map((e: any) => MetricData.fromJSON(e)) : [], + serviceVersionInfo: isSet(object.serviceVersionInfo) ? VersionInfo.fromJSON(object.serviceVersionInfo) : undefined, + sessionKeyType: isSet(object.sessionKeyType) ? signedMessage_SessionKeyTypeFromJSON(object.sessionKeyType) : 0, + oemcryptoCoreMessage: isSet(object.oemcryptoCoreMessage) ? Buffer.from(bytesFromBase64(object.oemcryptoCoreMessage)) : Buffer.alloc(0) + }; + }, + + toJSON(message: SignedMessage): unknown { + const obj: any = {}; + message.type !== undefined && (obj.type = signedMessage_MessageTypeToJSON(message.type)); + message.msg !== undefined && (obj.msg = base64FromBytes(message.msg !== undefined ? message.msg : Buffer.alloc(0))); + message.signature !== undefined && (obj.signature = base64FromBytes(message.signature !== undefined ? message.signature : Buffer.alloc(0))); + message.sessionKey !== undefined && (obj.sessionKey = base64FromBytes(message.sessionKey !== undefined ? message.sessionKey : Buffer.alloc(0))); + message.remoteAttestation !== undefined && + (obj.remoteAttestation = base64FromBytes(message.remoteAttestation !== undefined ? message.remoteAttestation : Buffer.alloc(0))); + if (message.metricData) { + obj.metricData = message.metricData.map((e) => (e ? MetricData.toJSON(e) : undefined)); + } else { + obj.metricData = []; + } + message.serviceVersionInfo !== undefined && + (obj.serviceVersionInfo = message.serviceVersionInfo ? VersionInfo.toJSON(message.serviceVersionInfo) : undefined); + message.sessionKeyType !== undefined && (obj.sessionKeyType = signedMessage_SessionKeyTypeToJSON(message.sessionKeyType)); + message.oemcryptoCoreMessage !== undefined && + (obj.oemcryptoCoreMessage = base64FromBytes(message.oemcryptoCoreMessage !== undefined ? message.oemcryptoCoreMessage : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>(object: I): SignedMessage { + const message = createBaseSignedMessage(); + message.type = object.type ?? 1; + message.msg = object.msg ?? Buffer.alloc(0); + message.signature = object.signature ?? Buffer.alloc(0); + message.sessionKey = object.sessionKey ?? Buffer.alloc(0); + message.remoteAttestation = object.remoteAttestation ?? Buffer.alloc(0); + message.metricData = object.metricData?.map((e) => MetricData.fromPartial(e)) || []; + message.serviceVersionInfo = + object.serviceVersionInfo !== undefined && object.serviceVersionInfo !== null ? VersionInfo.fromPartial(object.serviceVersionInfo) : undefined; + message.sessionKeyType = object.sessionKeyType ?? 0; + message.oemcryptoCoreMessage = object.oemcryptoCoreMessage ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseClientIdentification(): ClientIdentification { + return { + type: 0, + token: Buffer.alloc(0), + clientInfo: [], + providerClientToken: Buffer.alloc(0), + licenseCounter: 0, + clientCapabilities: undefined, + vmpData: Buffer.alloc(0), + deviceCredentials: [] + }; +} + +export const ClientIdentification = { + encode(message: ClientIdentification, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== 0) { + writer.uint32(8).int32(message.type); + } + if (message.token.length !== 0) { + writer.uint32(18).bytes(message.token); + } + for (const v of message.clientInfo) { + ClientIdentification_NameValue.encode(v!, writer.uint32(26).fork()).ldelim(); + } + if (message.providerClientToken.length !== 0) { + writer.uint32(34).bytes(message.providerClientToken); + } + if (message.licenseCounter !== 0) { + writer.uint32(40).uint32(message.licenseCounter); + } + if (message.clientCapabilities !== undefined) { + ClientIdentification_ClientCapabilities.encode(message.clientCapabilities, writer.uint32(50).fork()).ldelim(); + } + if (message.vmpData.length !== 0) { + writer.uint32(58).bytes(message.vmpData); + } + for (const v of message.deviceCredentials) { + ClientIdentification_ClientCredentials.encode(v!, writer.uint32(66).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ClientIdentification { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClientIdentification(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.type = reader.int32() as any; + break; + case 2: + message.token = reader.bytes() as Buffer; + break; + case 3: + message.clientInfo.push(ClientIdentification_NameValue.decode(reader, reader.uint32())); + break; + case 4: + message.providerClientToken = reader.bytes() as Buffer; + break; + case 5: + message.licenseCounter = reader.uint32(); + break; + case 6: + message.clientCapabilities = ClientIdentification_ClientCapabilities.decode(reader, reader.uint32()); + break; + case 7: + message.vmpData = reader.bytes() as Buffer; + break; + case 8: + message.deviceCredentials.push(ClientIdentification_ClientCredentials.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ClientIdentification { + return { + type: isSet(object.type) ? clientIdentification_TokenTypeFromJSON(object.type) : 0, + token: isSet(object.token) ? Buffer.from(bytesFromBase64(object.token)) : Buffer.alloc(0), + clientInfo: Array.isArray(object?.clientInfo) ? object.clientInfo.map((e: any) => ClientIdentification_NameValue.fromJSON(e)) : [], + providerClientToken: isSet(object.providerClientToken) ? Buffer.from(bytesFromBase64(object.providerClientToken)) : Buffer.alloc(0), + licenseCounter: isSet(object.licenseCounter) ? Number(object.licenseCounter) : 0, + clientCapabilities: isSet(object.clientCapabilities) ? ClientIdentification_ClientCapabilities.fromJSON(object.clientCapabilities) : undefined, + vmpData: isSet(object.vmpData) ? Buffer.from(bytesFromBase64(object.vmpData)) : Buffer.alloc(0), + deviceCredentials: Array.isArray(object?.deviceCredentials) + ? object.deviceCredentials.map((e: any) => ClientIdentification_ClientCredentials.fromJSON(e)) + : [] + }; + }, + + toJSON(message: ClientIdentification): unknown { + const obj: any = {}; + message.type !== undefined && (obj.type = clientIdentification_TokenTypeToJSON(message.type)); + message.token !== undefined && (obj.token = base64FromBytes(message.token !== undefined ? message.token : Buffer.alloc(0))); + if (message.clientInfo) { + obj.clientInfo = message.clientInfo.map((e) => (e ? ClientIdentification_NameValue.toJSON(e) : undefined)); + } else { + obj.clientInfo = []; + } + message.providerClientToken !== undefined && + (obj.providerClientToken = base64FromBytes(message.providerClientToken !== undefined ? message.providerClientToken : Buffer.alloc(0))); + message.licenseCounter !== undefined && (obj.licenseCounter = Math.round(message.licenseCounter)); + message.clientCapabilities !== undefined && + (obj.clientCapabilities = message.clientCapabilities ? ClientIdentification_ClientCapabilities.toJSON(message.clientCapabilities) : undefined); + message.vmpData !== undefined && (obj.vmpData = base64FromBytes(message.vmpData !== undefined ? message.vmpData : Buffer.alloc(0))); + if (message.deviceCredentials) { + obj.deviceCredentials = message.deviceCredentials.map((e) => (e ? ClientIdentification_ClientCredentials.toJSON(e) : undefined)); + } else { + obj.deviceCredentials = []; + } + return obj; + }, + + fromPartial, I>>(object: I): ClientIdentification { + const message = createBaseClientIdentification(); + message.type = object.type ?? 0; + message.token = object.token ?? Buffer.alloc(0); + message.clientInfo = object.clientInfo?.map((e) => ClientIdentification_NameValue.fromPartial(e)) || []; + message.providerClientToken = object.providerClientToken ?? Buffer.alloc(0); + message.licenseCounter = object.licenseCounter ?? 0; + message.clientCapabilities = + object.clientCapabilities !== undefined && object.clientCapabilities !== null + ? ClientIdentification_ClientCapabilities.fromPartial(object.clientCapabilities) + : undefined; + message.vmpData = object.vmpData ?? Buffer.alloc(0); + message.deviceCredentials = object.deviceCredentials?.map((e) => ClientIdentification_ClientCredentials.fromPartial(e)) || []; + return message; + } +}; + +function createBaseClientIdentification_NameValue(): ClientIdentification_NameValue { + return { name: '', value: '' }; +} + +export const ClientIdentification_NameValue = { + encode(message: ClientIdentification_NameValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== '') { + writer.uint32(10).string(message.name); + } + if (message.value !== '') { + writer.uint32(18).string(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ClientIdentification_NameValue { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClientIdentification_NameValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + case 2: + message.value = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ClientIdentification_NameValue { + return { + name: isSet(object.name) ? String(object.name) : '', + value: isSet(object.value) ? String(object.value) : '' + }; + }, + + toJSON(message: ClientIdentification_NameValue): unknown { + const obj: any = {}; + message.name !== undefined && (obj.name = message.name); + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial, I>>(object: I): ClientIdentification_NameValue { + const message = createBaseClientIdentification_NameValue(); + message.name = object.name ?? ''; + message.value = object.value ?? ''; + return message; + } +}; + +function createBaseClientIdentification_ClientCapabilities(): ClientIdentification_ClientCapabilities { + return { + clientToken: false, + sessionToken: false, + videoResolutionConstraints: false, + maxHdcpVersion: 0, + oemCryptoApiVersion: 0, + antiRollbackUsageTable: false, + srmVersion: 0, + canUpdateSrm: false, + supportedCertificateKeyType: [], + analogOutputCapabilities: 0, + canDisableAnalogOutput: false, + resourceRatingTier: 0 + }; +} + +export const ClientIdentification_ClientCapabilities = { + encode(message: ClientIdentification_ClientCapabilities, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.clientToken === true) { + writer.uint32(8).bool(message.clientToken); + } + if (message.sessionToken === true) { + writer.uint32(16).bool(message.sessionToken); + } + if (message.videoResolutionConstraints === true) { + writer.uint32(24).bool(message.videoResolutionConstraints); + } + if (message.maxHdcpVersion !== 0) { + writer.uint32(32).int32(message.maxHdcpVersion); + } + if (message.oemCryptoApiVersion !== 0) { + writer.uint32(40).uint32(message.oemCryptoApiVersion); + } + if (message.antiRollbackUsageTable === true) { + writer.uint32(48).bool(message.antiRollbackUsageTable); + } + if (message.srmVersion !== 0) { + writer.uint32(56).uint32(message.srmVersion); + } + if (message.canUpdateSrm === true) { + writer.uint32(64).bool(message.canUpdateSrm); + } + writer.uint32(74).fork(); + for (const v of message.supportedCertificateKeyType) { + writer.int32(v); + } + writer.ldelim(); + if (message.analogOutputCapabilities !== 0) { + writer.uint32(80).int32(message.analogOutputCapabilities); + } + if (message.canDisableAnalogOutput === true) { + writer.uint32(88).bool(message.canDisableAnalogOutput); + } + if (message.resourceRatingTier !== 0) { + writer.uint32(96).uint32(message.resourceRatingTier); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ClientIdentification_ClientCapabilities { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClientIdentification_ClientCapabilities(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.clientToken = reader.bool(); + break; + case 2: + message.sessionToken = reader.bool(); + break; + case 3: + message.videoResolutionConstraints = reader.bool(); + break; + case 4: + message.maxHdcpVersion = reader.int32() as any; + break; + case 5: + message.oemCryptoApiVersion = reader.uint32(); + break; + case 6: + message.antiRollbackUsageTable = reader.bool(); + break; + case 7: + message.srmVersion = reader.uint32(); + break; + case 8: + message.canUpdateSrm = reader.bool(); + break; + case 9: + if ((tag & 7) === 2) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.supportedCertificateKeyType.push(reader.int32() as any); + } + } else { + message.supportedCertificateKeyType.push(reader.int32() as any); + } + break; + case 10: + message.analogOutputCapabilities = reader.int32() as any; + break; + case 11: + message.canDisableAnalogOutput = reader.bool(); + break; + case 12: + message.resourceRatingTier = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ClientIdentification_ClientCapabilities { + return { + clientToken: isSet(object.clientToken) ? Boolean(object.clientToken) : false, + sessionToken: isSet(object.sessionToken) ? Boolean(object.sessionToken) : false, + videoResolutionConstraints: isSet(object.videoResolutionConstraints) ? Boolean(object.videoResolutionConstraints) : false, + maxHdcpVersion: isSet(object.maxHdcpVersion) ? clientIdentification_ClientCapabilities_HdcpVersionFromJSON(object.maxHdcpVersion) : 0, + oemCryptoApiVersion: isSet(object.oemCryptoApiVersion) ? Number(object.oemCryptoApiVersion) : 0, + antiRollbackUsageTable: isSet(object.antiRollbackUsageTable) ? Boolean(object.antiRollbackUsageTable) : false, + srmVersion: isSet(object.srmVersion) ? Number(object.srmVersion) : 0, + canUpdateSrm: isSet(object.canUpdateSrm) ? Boolean(object.canUpdateSrm) : false, + supportedCertificateKeyType: Array.isArray(object?.supportedCertificateKeyType) + ? object.supportedCertificateKeyType.map((e: any) => clientIdentification_ClientCapabilities_CertificateKeyTypeFromJSON(e)) + : [], + analogOutputCapabilities: isSet(object.analogOutputCapabilities) + ? clientIdentification_ClientCapabilities_AnalogOutputCapabilitiesFromJSON(object.analogOutputCapabilities) + : 0, + canDisableAnalogOutput: isSet(object.canDisableAnalogOutput) ? Boolean(object.canDisableAnalogOutput) : false, + resourceRatingTier: isSet(object.resourceRatingTier) ? Number(object.resourceRatingTier) : 0 + }; + }, + + toJSON(message: ClientIdentification_ClientCapabilities): unknown { + const obj: any = {}; + message.clientToken !== undefined && (obj.clientToken = message.clientToken); + message.sessionToken !== undefined && (obj.sessionToken = message.sessionToken); + message.videoResolutionConstraints !== undefined && (obj.videoResolutionConstraints = message.videoResolutionConstraints); + message.maxHdcpVersion !== undefined && (obj.maxHdcpVersion = clientIdentification_ClientCapabilities_HdcpVersionToJSON(message.maxHdcpVersion)); + message.oemCryptoApiVersion !== undefined && (obj.oemCryptoApiVersion = Math.round(message.oemCryptoApiVersion)); + message.antiRollbackUsageTable !== undefined && (obj.antiRollbackUsageTable = message.antiRollbackUsageTable); + message.srmVersion !== undefined && (obj.srmVersion = Math.round(message.srmVersion)); + message.canUpdateSrm !== undefined && (obj.canUpdateSrm = message.canUpdateSrm); + if (message.supportedCertificateKeyType) { + obj.supportedCertificateKeyType = message.supportedCertificateKeyType.map((e) => + clientIdentification_ClientCapabilities_CertificateKeyTypeToJSON(e) + ); + } else { + obj.supportedCertificateKeyType = []; + } + message.analogOutputCapabilities !== undefined && + (obj.analogOutputCapabilities = clientIdentification_ClientCapabilities_AnalogOutputCapabilitiesToJSON(message.analogOutputCapabilities)); + message.canDisableAnalogOutput !== undefined && (obj.canDisableAnalogOutput = message.canDisableAnalogOutput); + message.resourceRatingTier !== undefined && (obj.resourceRatingTier = Math.round(message.resourceRatingTier)); + return obj; + }, + + fromPartial, I>>(object: I): ClientIdentification_ClientCapabilities { + const message = createBaseClientIdentification_ClientCapabilities(); + message.clientToken = object.clientToken ?? false; + message.sessionToken = object.sessionToken ?? false; + message.videoResolutionConstraints = object.videoResolutionConstraints ?? false; + message.maxHdcpVersion = object.maxHdcpVersion ?? 0; + message.oemCryptoApiVersion = object.oemCryptoApiVersion ?? 0; + message.antiRollbackUsageTable = object.antiRollbackUsageTable ?? false; + message.srmVersion = object.srmVersion ?? 0; + message.canUpdateSrm = object.canUpdateSrm ?? false; + message.supportedCertificateKeyType = object.supportedCertificateKeyType?.map((e) => e) || []; + message.analogOutputCapabilities = object.analogOutputCapabilities ?? 0; + message.canDisableAnalogOutput = object.canDisableAnalogOutput ?? false; + message.resourceRatingTier = object.resourceRatingTier ?? 0; + return message; + } +}; + +function createBaseClientIdentification_ClientCredentials(): ClientIdentification_ClientCredentials { + return { type: 0, token: Buffer.alloc(0) }; +} + +export const ClientIdentification_ClientCredentials = { + encode(message: ClientIdentification_ClientCredentials, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== 0) { + writer.uint32(8).int32(message.type); + } + if (message.token.length !== 0) { + writer.uint32(18).bytes(message.token); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ClientIdentification_ClientCredentials { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClientIdentification_ClientCredentials(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.type = reader.int32() as any; + break; + case 2: + message.token = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ClientIdentification_ClientCredentials { + return { + type: isSet(object.type) ? clientIdentification_TokenTypeFromJSON(object.type) : 0, + token: isSet(object.token) ? Buffer.from(bytesFromBase64(object.token)) : Buffer.alloc(0) + }; + }, + + toJSON(message: ClientIdentification_ClientCredentials): unknown { + const obj: any = {}; + message.type !== undefined && (obj.type = clientIdentification_TokenTypeToJSON(message.type)); + message.token !== undefined && (obj.token = base64FromBytes(message.token !== undefined ? message.token : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>(object: I): ClientIdentification_ClientCredentials { + const message = createBaseClientIdentification_ClientCredentials(); + message.type = object.type ?? 0; + message.token = object.token ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseEncryptedClientIdentification(): EncryptedClientIdentification { + return { + providerId: '', + serviceCertificateSerialNumber: Buffer.alloc(0), + encryptedClientId: Buffer.alloc(0), + encryptedClientIdIv: Buffer.alloc(0), + encryptedPrivacyKey: Buffer.alloc(0) + }; +} + +export const EncryptedClientIdentification = { + encode(message: EncryptedClientIdentification, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.providerId !== '') { + writer.uint32(10).string(message.providerId); + } + if (message.serviceCertificateSerialNumber.length !== 0) { + writer.uint32(18).bytes(message.serviceCertificateSerialNumber); + } + if (message.encryptedClientId.length !== 0) { + writer.uint32(26).bytes(message.encryptedClientId); + } + if (message.encryptedClientIdIv.length !== 0) { + writer.uint32(34).bytes(message.encryptedClientIdIv); + } + if (message.encryptedPrivacyKey.length !== 0) { + writer.uint32(42).bytes(message.encryptedPrivacyKey); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EncryptedClientIdentification { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEncryptedClientIdentification(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.providerId = reader.string(); + break; + case 2: + message.serviceCertificateSerialNumber = reader.bytes() as Buffer; + break; + case 3: + message.encryptedClientId = reader.bytes() as Buffer; + break; + case 4: + message.encryptedClientIdIv = reader.bytes() as Buffer; + break; + case 5: + message.encryptedPrivacyKey = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): EncryptedClientIdentification { + return { + providerId: isSet(object.providerId) ? String(object.providerId) : '', + serviceCertificateSerialNumber: isSet(object.serviceCertificateSerialNumber) + ? Buffer.from(bytesFromBase64(object.serviceCertificateSerialNumber)) + : Buffer.alloc(0), + encryptedClientId: isSet(object.encryptedClientId) ? Buffer.from(bytesFromBase64(object.encryptedClientId)) : Buffer.alloc(0), + encryptedClientIdIv: isSet(object.encryptedClientIdIv) ? Buffer.from(bytesFromBase64(object.encryptedClientIdIv)) : Buffer.alloc(0), + encryptedPrivacyKey: isSet(object.encryptedPrivacyKey) ? Buffer.from(bytesFromBase64(object.encryptedPrivacyKey)) : Buffer.alloc(0) + }; + }, + + toJSON(message: EncryptedClientIdentification): unknown { + const obj: any = {}; + message.providerId !== undefined && (obj.providerId = message.providerId); + message.serviceCertificateSerialNumber !== undefined && + (obj.serviceCertificateSerialNumber = base64FromBytes( + message.serviceCertificateSerialNumber !== undefined ? message.serviceCertificateSerialNumber : Buffer.alloc(0) + )); + message.encryptedClientId !== undefined && + (obj.encryptedClientId = base64FromBytes(message.encryptedClientId !== undefined ? message.encryptedClientId : Buffer.alloc(0))); + message.encryptedClientIdIv !== undefined && + (obj.encryptedClientIdIv = base64FromBytes(message.encryptedClientIdIv !== undefined ? message.encryptedClientIdIv : Buffer.alloc(0))); + message.encryptedPrivacyKey !== undefined && + (obj.encryptedPrivacyKey = base64FromBytes(message.encryptedPrivacyKey !== undefined ? message.encryptedPrivacyKey : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>(object: I): EncryptedClientIdentification { + const message = createBaseEncryptedClientIdentification(); + message.providerId = object.providerId ?? ''; + message.serviceCertificateSerialNumber = object.serviceCertificateSerialNumber ?? Buffer.alloc(0); + message.encryptedClientId = object.encryptedClientId ?? Buffer.alloc(0); + message.encryptedClientIdIv = object.encryptedClientIdIv ?? Buffer.alloc(0); + message.encryptedPrivacyKey = object.encryptedPrivacyKey ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseDrmCertificate(): DrmCertificate { + return { + type: 0, + serialNumber: Buffer.alloc(0), + creationTimeSeconds: 0, + expirationTimeSeconds: 0, + publicKey: Buffer.alloc(0), + systemId: 0, + testDeviceDeprecated: false, + providerId: '', + serviceTypes: [], + algorithm: 0, + rotId: Buffer.alloc(0), + encryptionKey: undefined + }; +} + +export const DrmCertificate = { + encode(message: DrmCertificate, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== 0) { + writer.uint32(8).int32(message.type); + } + if (message.serialNumber.length !== 0) { + writer.uint32(18).bytes(message.serialNumber); + } + if (message.creationTimeSeconds !== 0) { + writer.uint32(24).uint32(message.creationTimeSeconds); + } + if (message.expirationTimeSeconds !== 0) { + writer.uint32(96).uint32(message.expirationTimeSeconds); + } + if (message.publicKey.length !== 0) { + writer.uint32(34).bytes(message.publicKey); + } + if (message.systemId !== 0) { + writer.uint32(40).uint32(message.systemId); + } + if (message.testDeviceDeprecated === true) { + writer.uint32(48).bool(message.testDeviceDeprecated); + } + if (message.providerId !== '') { + writer.uint32(58).string(message.providerId); + } + writer.uint32(66).fork(); + for (const v of message.serviceTypes) { + writer.int32(v); + } + writer.ldelim(); + if (message.algorithm !== 0) { + writer.uint32(72).int32(message.algorithm); + } + if (message.rotId.length !== 0) { + writer.uint32(82).bytes(message.rotId); + } + if (message.encryptionKey !== undefined) { + DrmCertificate_EncryptionKey.encode(message.encryptionKey, writer.uint32(90).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): DrmCertificate { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDrmCertificate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.type = reader.int32() as any; + break; + case 2: + message.serialNumber = reader.bytes() as Buffer; + break; + case 3: + message.creationTimeSeconds = reader.uint32(); + break; + case 12: + message.expirationTimeSeconds = reader.uint32(); + break; + case 4: + message.publicKey = reader.bytes() as Buffer; + break; + case 5: + message.systemId = reader.uint32(); + break; + case 6: + message.testDeviceDeprecated = reader.bool(); + break; + case 7: + message.providerId = reader.string(); + break; + case 8: + if ((tag & 7) === 2) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.serviceTypes.push(reader.int32() as any); + } + } else { + message.serviceTypes.push(reader.int32() as any); + } + break; + case 9: + message.algorithm = reader.int32() as any; + break; + case 10: + message.rotId = reader.bytes() as Buffer; + break; + case 11: + message.encryptionKey = DrmCertificate_EncryptionKey.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): DrmCertificate { + return { + type: isSet(object.type) ? drmCertificate_TypeFromJSON(object.type) : 0, + serialNumber: isSet(object.serialNumber) ? Buffer.from(bytesFromBase64(object.serialNumber)) : Buffer.alloc(0), + creationTimeSeconds: isSet(object.creationTimeSeconds) ? Number(object.creationTimeSeconds) : 0, + expirationTimeSeconds: isSet(object.expirationTimeSeconds) ? Number(object.expirationTimeSeconds) : 0, + publicKey: isSet(object.publicKey) ? Buffer.from(bytesFromBase64(object.publicKey)) : Buffer.alloc(0), + systemId: isSet(object.systemId) ? Number(object.systemId) : 0, + testDeviceDeprecated: isSet(object.testDeviceDeprecated) ? Boolean(object.testDeviceDeprecated) : false, + providerId: isSet(object.providerId) ? String(object.providerId) : '', + serviceTypes: Array.isArray(object?.serviceTypes) ? object.serviceTypes.map((e: any) => drmCertificate_ServiceTypeFromJSON(e)) : [], + algorithm: isSet(object.algorithm) ? drmCertificate_AlgorithmFromJSON(object.algorithm) : 0, + rotId: isSet(object.rotId) ? Buffer.from(bytesFromBase64(object.rotId)) : Buffer.alloc(0), + encryptionKey: isSet(object.encryptionKey) ? DrmCertificate_EncryptionKey.fromJSON(object.encryptionKey) : undefined + }; + }, + + toJSON(message: DrmCertificate): unknown { + const obj: any = {}; + message.type !== undefined && (obj.type = drmCertificate_TypeToJSON(message.type)); + message.serialNumber !== undefined && + (obj.serialNumber = base64FromBytes(message.serialNumber !== undefined ? message.serialNumber : Buffer.alloc(0))); + message.creationTimeSeconds !== undefined && (obj.creationTimeSeconds = Math.round(message.creationTimeSeconds)); + message.expirationTimeSeconds !== undefined && (obj.expirationTimeSeconds = Math.round(message.expirationTimeSeconds)); + message.publicKey !== undefined && (obj.publicKey = base64FromBytes(message.publicKey !== undefined ? message.publicKey : Buffer.alloc(0))); + message.systemId !== undefined && (obj.systemId = Math.round(message.systemId)); + message.testDeviceDeprecated !== undefined && (obj.testDeviceDeprecated = message.testDeviceDeprecated); + message.providerId !== undefined && (obj.providerId = message.providerId); + if (message.serviceTypes) { + obj.serviceTypes = message.serviceTypes.map((e) => drmCertificate_ServiceTypeToJSON(e)); + } else { + obj.serviceTypes = []; + } + message.algorithm !== undefined && (obj.algorithm = drmCertificate_AlgorithmToJSON(message.algorithm)); + message.rotId !== undefined && (obj.rotId = base64FromBytes(message.rotId !== undefined ? message.rotId : Buffer.alloc(0))); + message.encryptionKey !== undefined && + (obj.encryptionKey = message.encryptionKey ? DrmCertificate_EncryptionKey.toJSON(message.encryptionKey) : undefined); + return obj; + }, + + fromPartial, I>>(object: I): DrmCertificate { + const message = createBaseDrmCertificate(); + message.type = object.type ?? 0; + message.serialNumber = object.serialNumber ?? Buffer.alloc(0); + message.creationTimeSeconds = object.creationTimeSeconds ?? 0; + message.expirationTimeSeconds = object.expirationTimeSeconds ?? 0; + message.publicKey = object.publicKey ?? Buffer.alloc(0); + message.systemId = object.systemId ?? 0; + message.testDeviceDeprecated = object.testDeviceDeprecated ?? false; + message.providerId = object.providerId ?? ''; + message.serviceTypes = object.serviceTypes?.map((e) => e) || []; + message.algorithm = object.algorithm ?? 0; + message.rotId = object.rotId ?? Buffer.alloc(0); + message.encryptionKey = + object.encryptionKey !== undefined && object.encryptionKey !== null + ? DrmCertificate_EncryptionKey.fromPartial(object.encryptionKey) + : undefined; + return message; + } +}; + +function createBaseDrmCertificate_EncryptionKey(): DrmCertificate_EncryptionKey { + return { publicKey: Buffer.alloc(0), algorithm: 0 }; +} + +export const DrmCertificate_EncryptionKey = { + encode(message: DrmCertificate_EncryptionKey, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.publicKey.length !== 0) { + writer.uint32(10).bytes(message.publicKey); + } + if (message.algorithm !== 0) { + writer.uint32(16).int32(message.algorithm); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): DrmCertificate_EncryptionKey { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDrmCertificate_EncryptionKey(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.publicKey = reader.bytes() as Buffer; + break; + case 2: + message.algorithm = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): DrmCertificate_EncryptionKey { + return { + publicKey: isSet(object.publicKey) ? Buffer.from(bytesFromBase64(object.publicKey)) : Buffer.alloc(0), + algorithm: isSet(object.algorithm) ? drmCertificate_AlgorithmFromJSON(object.algorithm) : 0 + }; + }, + + toJSON(message: DrmCertificate_EncryptionKey): unknown { + const obj: any = {}; + message.publicKey !== undefined && (obj.publicKey = base64FromBytes(message.publicKey !== undefined ? message.publicKey : Buffer.alloc(0))); + message.algorithm !== undefined && (obj.algorithm = drmCertificate_AlgorithmToJSON(message.algorithm)); + return obj; + }, + + fromPartial, I>>(object: I): DrmCertificate_EncryptionKey { + const message = createBaseDrmCertificate_EncryptionKey(); + message.publicKey = object.publicKey ?? Buffer.alloc(0); + message.algorithm = object.algorithm ?? 0; + return message; + } +}; + +function createBaseSignedDrmCertificate(): SignedDrmCertificate { + return { drmCertificate: Buffer.alloc(0), signature: Buffer.alloc(0), signer: undefined, hashAlgorithm: 0 }; +} + +export const SignedDrmCertificate = { + encode(message: SignedDrmCertificate, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.drmCertificate.length !== 0) { + writer.uint32(10).bytes(message.drmCertificate); + } + if (message.signature.length !== 0) { + writer.uint32(18).bytes(message.signature); + } + if (message.signer !== undefined) { + SignedDrmCertificate.encode(message.signer, writer.uint32(26).fork()).ldelim(); + } + if (message.hashAlgorithm !== 0) { + writer.uint32(32).int32(message.hashAlgorithm); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SignedDrmCertificate { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSignedDrmCertificate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.drmCertificate = reader.bytes() as Buffer; + break; + case 2: + message.signature = reader.bytes() as Buffer; + break; + case 3: + message.signer = SignedDrmCertificate.decode(reader, reader.uint32()); + break; + case 4: + message.hashAlgorithm = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SignedDrmCertificate { + return { + drmCertificate: isSet(object.drmCertificate) ? Buffer.from(bytesFromBase64(object.drmCertificate)) : Buffer.alloc(0), + signature: isSet(object.signature) ? Buffer.from(bytesFromBase64(object.signature)) : Buffer.alloc(0), + signer: isSet(object.signer) ? SignedDrmCertificate.fromJSON(object.signer) : undefined, + hashAlgorithm: isSet(object.hashAlgorithm) ? hashAlgorithmProtoFromJSON(object.hashAlgorithm) : 0 + }; + }, + + toJSON(message: SignedDrmCertificate): unknown { + const obj: any = {}; + message.drmCertificate !== undefined && + (obj.drmCertificate = base64FromBytes(message.drmCertificate !== undefined ? message.drmCertificate : Buffer.alloc(0))); + message.signature !== undefined && (obj.signature = base64FromBytes(message.signature !== undefined ? message.signature : Buffer.alloc(0))); + message.signer !== undefined && (obj.signer = message.signer ? SignedDrmCertificate.toJSON(message.signer) : undefined); + message.hashAlgorithm !== undefined && (obj.hashAlgorithm = hashAlgorithmProtoToJSON(message.hashAlgorithm)); + return obj; + }, + + fromPartial, I>>(object: I): SignedDrmCertificate { + const message = createBaseSignedDrmCertificate(); + message.drmCertificate = object.drmCertificate ?? Buffer.alloc(0); + message.signature = object.signature ?? Buffer.alloc(0); + message.signer = object.signer !== undefined && object.signer !== null ? SignedDrmCertificate.fromPartial(object.signer) : undefined; + message.hashAlgorithm = object.hashAlgorithm ?? 0; + return message; + } +}; + +function createBaseWidevinePsshData(): WidevinePsshData { + return { + keyIds: [], + contentId: Buffer.alloc(0), + cryptoPeriodIndex: 0, + protectionScheme: 0, + cryptoPeriodSeconds: 0, + type: 0, + keySequence: 0, + groupIds: [], + entitledKeys: [], + videoFeature: '', + algorithm: 0, + provider: '', + trackType: '', + policy: '', + groupedLicense: Buffer.alloc(0) + }; +} + +export const WidevinePsshData = { + encode(message: WidevinePsshData, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.keyIds) { + writer.uint32(18).bytes(v!); + } + if (message.contentId.length !== 0) { + writer.uint32(34).bytes(message.contentId); + } + if (message.cryptoPeriodIndex !== 0) { + writer.uint32(56).uint32(message.cryptoPeriodIndex); + } + if (message.protectionScheme !== 0) { + writer.uint32(72).uint32(message.protectionScheme); + } + if (message.cryptoPeriodSeconds !== 0) { + writer.uint32(80).uint32(message.cryptoPeriodSeconds); + } + if (message.type !== 0) { + writer.uint32(88).int32(message.type); + } + if (message.keySequence !== 0) { + writer.uint32(96).uint32(message.keySequence); + } + for (const v of message.groupIds) { + writer.uint32(106).bytes(v!); + } + for (const v of message.entitledKeys) { + WidevinePsshData_EntitledKey.encode(v!, writer.uint32(114).fork()).ldelim(); + } + if (message.videoFeature !== '') { + writer.uint32(122).string(message.videoFeature); + } + if (message.algorithm !== 0) { + writer.uint32(8).int32(message.algorithm); + } + if (message.provider !== '') { + writer.uint32(26).string(message.provider); + } + if (message.trackType !== '') { + writer.uint32(42).string(message.trackType); + } + if (message.policy !== '') { + writer.uint32(50).string(message.policy); + } + if (message.groupedLicense.length !== 0) { + writer.uint32(66).bytes(message.groupedLicense); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): WidevinePsshData { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseWidevinePsshData(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 2: + message.keyIds.push(reader.bytes() as Buffer); + break; + case 4: + message.contentId = reader.bytes() as Buffer; + break; + case 7: + message.cryptoPeriodIndex = reader.uint32(); + break; + case 9: + message.protectionScheme = reader.uint32(); + break; + case 10: + message.cryptoPeriodSeconds = reader.uint32(); + break; + case 11: + message.type = reader.int32() as any; + break; + case 12: + message.keySequence = reader.uint32(); + break; + case 13: + message.groupIds.push(reader.bytes() as Buffer); + break; + case 14: + message.entitledKeys.push(WidevinePsshData_EntitledKey.decode(reader, reader.uint32())); + break; + case 15: + message.videoFeature = reader.string(); + break; + case 1: + message.algorithm = reader.int32() as any; + break; + case 3: + message.provider = reader.string(); + break; + case 5: + message.trackType = reader.string(); + break; + case 6: + message.policy = reader.string(); + break; + case 8: + message.groupedLicense = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): WidevinePsshData { + return { + keyIds: Array.isArray(object?.keyIds) ? object.keyIds.map((e: any) => Buffer.from(bytesFromBase64(e))) : [], + contentId: isSet(object.contentId) ? Buffer.from(bytesFromBase64(object.contentId)) : Buffer.alloc(0), + cryptoPeriodIndex: isSet(object.cryptoPeriodIndex) ? Number(object.cryptoPeriodIndex) : 0, + protectionScheme: isSet(object.protectionScheme) ? Number(object.protectionScheme) : 0, + cryptoPeriodSeconds: isSet(object.cryptoPeriodSeconds) ? Number(object.cryptoPeriodSeconds) : 0, + type: isSet(object.type) ? widevinePsshData_TypeFromJSON(object.type) : 0, + keySequence: isSet(object.keySequence) ? Number(object.keySequence) : 0, + groupIds: Array.isArray(object?.groupIds) ? object.groupIds.map((e: any) => Buffer.from(bytesFromBase64(e))) : [], + entitledKeys: Array.isArray(object?.entitledKeys) ? object.entitledKeys.map((e: any) => WidevinePsshData_EntitledKey.fromJSON(e)) : [], + videoFeature: isSet(object.videoFeature) ? String(object.videoFeature) : '', + algorithm: isSet(object.algorithm) ? widevinePsshData_AlgorithmFromJSON(object.algorithm) : 0, + provider: isSet(object.provider) ? String(object.provider) : '', + trackType: isSet(object.trackType) ? String(object.trackType) : '', + policy: isSet(object.policy) ? String(object.policy) : '', + groupedLicense: isSet(object.groupedLicense) ? Buffer.from(bytesFromBase64(object.groupedLicense)) : Buffer.alloc(0) + }; + }, + + toJSON(message: WidevinePsshData): unknown { + const obj: any = {}; + if (message.keyIds) { + obj.keyIds = message.keyIds.map((e) => base64FromBytes(e !== undefined ? e : Buffer.alloc(0))); + } else { + obj.keyIds = []; + } + message.contentId !== undefined && (obj.contentId = base64FromBytes(message.contentId !== undefined ? message.contentId : Buffer.alloc(0))); + message.cryptoPeriodIndex !== undefined && (obj.cryptoPeriodIndex = Math.round(message.cryptoPeriodIndex)); + message.protectionScheme !== undefined && (obj.protectionScheme = Math.round(message.protectionScheme)); + message.cryptoPeriodSeconds !== undefined && (obj.cryptoPeriodSeconds = Math.round(message.cryptoPeriodSeconds)); + message.type !== undefined && (obj.type = widevinePsshData_TypeToJSON(message.type)); + message.keySequence !== undefined && (obj.keySequence = Math.round(message.keySequence)); + if (message.groupIds) { + obj.groupIds = message.groupIds.map((e) => base64FromBytes(e !== undefined ? e : Buffer.alloc(0))); + } else { + obj.groupIds = []; + } + if (message.entitledKeys) { + obj.entitledKeys = message.entitledKeys.map((e) => (e ? WidevinePsshData_EntitledKey.toJSON(e) : undefined)); + } else { + obj.entitledKeys = []; + } + message.videoFeature !== undefined && (obj.videoFeature = message.videoFeature); + message.algorithm !== undefined && (obj.algorithm = widevinePsshData_AlgorithmToJSON(message.algorithm)); + message.provider !== undefined && (obj.provider = message.provider); + message.trackType !== undefined && (obj.trackType = message.trackType); + message.policy !== undefined && (obj.policy = message.policy); + message.groupedLicense !== undefined && + (obj.groupedLicense = base64FromBytes(message.groupedLicense !== undefined ? message.groupedLicense : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>(object: I): WidevinePsshData { + const message = createBaseWidevinePsshData(); + message.keyIds = object.keyIds?.map((e) => e) || []; + message.contentId = object.contentId ?? Buffer.alloc(0); + message.cryptoPeriodIndex = object.cryptoPeriodIndex ?? 0; + message.protectionScheme = object.protectionScheme ?? 0; + message.cryptoPeriodSeconds = object.cryptoPeriodSeconds ?? 0; + message.type = object.type ?? 0; + message.keySequence = object.keySequence ?? 0; + message.groupIds = object.groupIds?.map((e) => e) || []; + message.entitledKeys = object.entitledKeys?.map((e) => WidevinePsshData_EntitledKey.fromPartial(e)) || []; + message.videoFeature = object.videoFeature ?? ''; + message.algorithm = object.algorithm ?? 0; + message.provider = object.provider ?? ''; + message.trackType = object.trackType ?? ''; + message.policy = object.policy ?? ''; + message.groupedLicense = object.groupedLicense ?? Buffer.alloc(0); + return message; + } +}; + +function createBaseWidevinePsshData_EntitledKey(): WidevinePsshData_EntitledKey { + return { + entitlementKeyId: Buffer.alloc(0), + keyId: Buffer.alloc(0), + key: Buffer.alloc(0), + iv: Buffer.alloc(0), + entitlementKeySizeBytes: 0 + }; +} + +export const WidevinePsshData_EntitledKey = { + encode(message: WidevinePsshData_EntitledKey, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.entitlementKeyId.length !== 0) { + writer.uint32(10).bytes(message.entitlementKeyId); + } + if (message.keyId.length !== 0) { + writer.uint32(18).bytes(message.keyId); + } + if (message.key.length !== 0) { + writer.uint32(26).bytes(message.key); + } + if (message.iv.length !== 0) { + writer.uint32(34).bytes(message.iv); + } + if (message.entitlementKeySizeBytes !== 0) { + writer.uint32(40).uint32(message.entitlementKeySizeBytes); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): WidevinePsshData_EntitledKey { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseWidevinePsshData_EntitledKey(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.entitlementKeyId = reader.bytes() as Buffer; + break; + case 2: + message.keyId = reader.bytes() as Buffer; + break; + case 3: + message.key = reader.bytes() as Buffer; + break; + case 4: + message.iv = reader.bytes() as Buffer; + break; + case 5: + message.entitlementKeySizeBytes = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): WidevinePsshData_EntitledKey { + return { + entitlementKeyId: isSet(object.entitlementKeyId) ? Buffer.from(bytesFromBase64(object.entitlementKeyId)) : Buffer.alloc(0), + keyId: isSet(object.keyId) ? Buffer.from(bytesFromBase64(object.keyId)) : Buffer.alloc(0), + key: isSet(object.key) ? Buffer.from(bytesFromBase64(object.key)) : Buffer.alloc(0), + iv: isSet(object.iv) ? Buffer.from(bytesFromBase64(object.iv)) : Buffer.alloc(0), + entitlementKeySizeBytes: isSet(object.entitlementKeySizeBytes) ? Number(object.entitlementKeySizeBytes) : 0 + }; + }, + + toJSON(message: WidevinePsshData_EntitledKey): unknown { + const obj: any = {}; + message.entitlementKeyId !== undefined && + (obj.entitlementKeyId = base64FromBytes(message.entitlementKeyId !== undefined ? message.entitlementKeyId : Buffer.alloc(0))); + message.keyId !== undefined && (obj.keyId = base64FromBytes(message.keyId !== undefined ? message.keyId : Buffer.alloc(0))); + message.key !== undefined && (obj.key = base64FromBytes(message.key !== undefined ? message.key : Buffer.alloc(0))); + message.iv !== undefined && (obj.iv = base64FromBytes(message.iv !== undefined ? message.iv : Buffer.alloc(0))); + message.entitlementKeySizeBytes !== undefined && (obj.entitlementKeySizeBytes = Math.round(message.entitlementKeySizeBytes)); + return obj; + }, + + fromPartial, I>>(object: I): WidevinePsshData_EntitledKey { + const message = createBaseWidevinePsshData_EntitledKey(); + message.entitlementKeyId = object.entitlementKeyId ?? Buffer.alloc(0); + message.keyId = object.keyId ?? Buffer.alloc(0); + message.key = object.key ?? Buffer.alloc(0); + message.iv = object.iv ?? Buffer.alloc(0); + message.entitlementKeySizeBytes = object.entitlementKeySizeBytes ?? 0; + return message; + } +}; + +function createBaseFileHashes(): FileHashes { + return { signer: Buffer.alloc(0), signatures: [] }; +} + +export const FileHashes = { + encode(message: FileHashes, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.signer.length !== 0) { + writer.uint32(10).bytes(message.signer); + } + for (const v of message.signatures) { + FileHashes_Signature.encode(v!, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FileHashes { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileHashes(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.signer = reader.bytes() as Buffer; + break; + case 2: + message.signatures.push(FileHashes_Signature.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileHashes { + return { + signer: isSet(object.signer) ? Buffer.from(bytesFromBase64(object.signer)) : Buffer.alloc(0), + signatures: Array.isArray(object?.signatures) ? object.signatures.map((e: any) => FileHashes_Signature.fromJSON(e)) : [] + }; + }, + + toJSON(message: FileHashes): unknown { + const obj: any = {}; + message.signer !== undefined && (obj.signer = base64FromBytes(message.signer !== undefined ? message.signer : Buffer.alloc(0))); + if (message.signatures) { + obj.signatures = message.signatures.map((e) => (e ? FileHashes_Signature.toJSON(e) : undefined)); + } else { + obj.signatures = []; + } + return obj; + }, + + fromPartial, I>>(object: I): FileHashes { + const message = createBaseFileHashes(); + message.signer = object.signer ?? Buffer.alloc(0); + message.signatures = object.signatures?.map((e) => FileHashes_Signature.fromPartial(e)) || []; + return message; + } +}; + +function createBaseFileHashes_Signature(): FileHashes_Signature { + return { filename: '', testSigning: false, SHA512Hash: Buffer.alloc(0), mainExe: false, signature: Buffer.alloc(0) }; +} + +export const FileHashes_Signature = { + encode(message: FileHashes_Signature, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.filename !== '') { + writer.uint32(10).string(message.filename); + } + if (message.testSigning === true) { + writer.uint32(16).bool(message.testSigning); + } + if (message.SHA512Hash.length !== 0) { + writer.uint32(26).bytes(message.SHA512Hash); + } + if (message.mainExe === true) { + writer.uint32(32).bool(message.mainExe); + } + if (message.signature.length !== 0) { + writer.uint32(42).bytes(message.signature); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FileHashes_Signature { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileHashes_Signature(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.filename = reader.string(); + break; + case 2: + message.testSigning = reader.bool(); + break; + case 3: + message.SHA512Hash = reader.bytes() as Buffer; + break; + case 4: + message.mainExe = reader.bool(); + break; + case 5: + message.signature = reader.bytes() as Buffer; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileHashes_Signature { + return { + filename: isSet(object.filename) ? String(object.filename) : '', + testSigning: isSet(object.testSigning) ? Boolean(object.testSigning) : false, + SHA512Hash: isSet(object.SHA512Hash) ? Buffer.from(bytesFromBase64(object.SHA512Hash)) : Buffer.alloc(0), + mainExe: isSet(object.mainExe) ? Boolean(object.mainExe) : false, + signature: isSet(object.signature) ? Buffer.from(bytesFromBase64(object.signature)) : Buffer.alloc(0) + }; + }, + + toJSON(message: FileHashes_Signature): unknown { + const obj: any = {}; + message.filename !== undefined && (obj.filename = message.filename); + message.testSigning !== undefined && (obj.testSigning = message.testSigning); + message.SHA512Hash !== undefined && (obj.SHA512Hash = base64FromBytes(message.SHA512Hash !== undefined ? message.SHA512Hash : Buffer.alloc(0))); + message.mainExe !== undefined && (obj.mainExe = message.mainExe); + message.signature !== undefined && (obj.signature = base64FromBytes(message.signature !== undefined ? message.signature : Buffer.alloc(0))); + return obj; + }, + + fromPartial, I>>(object: I): FileHashes_Signature { + const message = createBaseFileHashes_Signature(); + message.filename = object.filename ?? ''; + message.testSigning = object.testSigning ?? false; + message.SHA512Hash = object.SHA512Hash ?? Buffer.alloc(0); + message.mainExe = object.mainExe ?? false; + message.signature = object.signature ?? Buffer.alloc(0); + return message; + } +}; + +declare let self: any | undefined; +declare let window: any | undefined; +declare let global: any | undefined; +const tsProtoGlobalThis: any = (() => { + if (typeof globalThis !== 'undefined') { + return globalThis; + } + if (typeof self !== 'undefined') { + return self; + } + if (typeof window !== 'undefined') { + return window; + } + if (typeof global !== 'undefined') { + return global; + } + throw 'Unable to locate global object'; +})(); + +function bytesFromBase64(b64: string): Uint8Array { + if (tsProtoGlobalThis.Buffer) { + return Uint8Array.from(tsProtoGlobalThis.Buffer.from(b64, 'base64')); + } else { + const bin = tsProtoGlobalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if (tsProtoGlobalThis.Buffer) { + return tsProtoGlobalThis.Buffer.from(arr).toString('base64'); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(String.fromCharCode(byte)); + }); + return tsProtoGlobalThis.btoa(bin.join('')); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; //eslint-disable-line + +export type DeepPartial = T extends Builtin + ? T + : T extends Long + ? string | number | Long + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} //eslint-disable-line + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} \ No newline at end of file diff --git a/modules/module.cfg-loader.ts b/modules/module.cfg-loader.ts index e81ac82..b488500 100644 --- a/modules/module.cfg-loader.ts +++ b/modules/module.cfg-loader.ts @@ -82,7 +82,8 @@ export type ConfigObject = { bin: { ffmpeg?: string, mkvmerge?: string, - ffprobe?: string + ffprobe?: string, + mp4decrypt?: string }, cli: { [key: string]: any @@ -146,7 +147,8 @@ const loadBinCfg = async () => { const defaultBin = { ffmpeg: 'ffmpeg', mkvmerge: 'mkvmerge', - ffprobe: 'ffprobe' + ffprobe: 'ffprobe', + mp4decrypt: 'mp4decrypt' }; const keys = Object.keys(defaultBin) as (keyof typeof defaultBin)[]; for(const dir of keys){ diff --git a/package.json b/package.json index 33669af..f2d2a67 100644 --- a/package.json +++ b/package.json @@ -56,10 +56,12 @@ "got": "^11.8.6", "iso-639": "^0.2.2", "log4js": "^6.9.1", + "long": "^5.2.3", "lookpath": "^1.2.2", "m3u8-parsed": "^1.3.0", "mpd-parser": "^1.3.0", "open": "^8.4.2", + "protobufjs": "^7.2.5", "sei-helper": "^3.3.0", "typescript-eslint": "0.0.1-alpha.0", "ws": "^8.13.0", @@ -70,7 +72,6 @@ "@types/cors": "^2.8.13", "@types/express": "^4.17.17", "@types/ffprobe": "^1.1.4", - "@types/ffprobe-static": "^2.0.1", "@types/fs-extra": "^11.0.1", "@types/node": "^18.15.11", "@types/ws": "^8.5.5", @@ -83,6 +84,7 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "7.32.2", "pkg": "^5.8.1", + "protoc": "^1.1.3", "removeNPMAbsolutePaths": "^3.0.1", "ts-node": "^10.9.1", "typescript": "5.1.6" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8db3f51..18c21ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ dependencies: log4js: specifier: ^6.9.1 version: 6.9.1 + long: + specifier: ^5.2.3 + version: 5.2.3 lookpath: specifier: ^1.2.2 version: 1.2.2 @@ -59,6 +62,9 @@ dependencies: open: specifier: ^8.4.2 version: 8.4.2 + protobufjs: + specifier: ^7.2.5 + version: 7.2.5 sei-helper: specifier: ^3.3.0 version: 3.3.0 @@ -85,9 +91,6 @@ devDependencies: '@types/ffprobe': specifier: ^1.1.4 version: 1.1.4 - '@types/ffprobe-static': - specifier: ^2.0.1 - version: 2.0.1 '@types/fs-extra': specifier: ^11.0.1 version: 11.0.1 @@ -121,6 +124,9 @@ devDependencies: pkg: specifier: ^5.8.1 version: 5.8.1 + protoc: + specifier: ^1.1.3 + version: 1.1.3 removeNPMAbsolutePaths: specifier: ^3.0.1 version: 3.0.1 @@ -1780,6 +1786,49 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 + /@protobufjs/aspromise@1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + dev: false + + /@protobufjs/base64@1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + dev: false + + /@protobufjs/codegen@2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + dev: false + + /@protobufjs/eventemitter@1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + dev: false + + /@protobufjs/fetch@1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: false + + /@protobufjs/float@1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + dev: false + + /@protobufjs/inquire@1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: false + + /@protobufjs/path@1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: false + + /@protobufjs/pool@1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: false + + /@protobufjs/utf8@1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: false + /@rushstack/eslint-patch@1.2.0: resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} dev: true @@ -1857,10 +1906,6 @@ packages: '@types/serve-static': 1.15.1 dev: true - /@types/ffprobe-static@2.0.1: - resolution: {integrity: sha512-V5CrKUfms0lBGSXliKmKzSFFZWgJusQks1YfjRI/+2dXFF+aK7qBAarCe/ryYHQI44jYQX7xtlgH0fCuJepuGQ==} - dev: true - /@types/ffprobe@1.1.4: resolution: {integrity: sha512-gtfU+bD4FDoF1S2ybmIWEIz0K5ijeHpi+CgJUtXl3FTGnf+61HmsZksqDn8M9M9lRJU9SRyMLt5yrIwUSep4Uw==} dev: true @@ -1968,7 +2013,7 @@ packages: grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 - semver: 7.3.8 + semver: 7.5.4 tsutils: 3.21.0(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: @@ -2137,7 +2182,7 @@ packages: debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.8 + semver: 7.5.4 tsutils: 3.21.0(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: @@ -2178,7 +2223,7 @@ packages: '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.1.6) eslint: 8.45.0 eslint-scope: 5.1.1 - semver: 7.3.8 + semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript @@ -2492,6 +2537,18 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + dev: true + + /binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + dev: true + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -2499,6 +2556,10 @@ packages: inherits: 2.0.4 readable-stream: 3.6.2 + /bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + dev: true + /body-parser@1.20.1: resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -2556,12 +2617,22 @@ packages: node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) + /buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + dev: true + /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + /buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + dev: true + /bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -2602,6 +2673,12 @@ packages: /caniuse-lite@1.0.30001516: resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==} + /chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + dependencies: + traverse: 0.3.9 + dev: true + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2662,12 +2739,34 @@ packages: wrap-ansi: 7.0.0 dev: false + /clone-buffer@1.0.0: + resolution: {integrity: sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==} + engines: {node: '>= 0.10'} + dev: true + /clone-response@1.0.3: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: mimic-response: 1.0.1 dev: false + /clone-stats@1.0.0: + resolution: {integrity: sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==} + dev: true + + /clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + dev: true + + /cloneable-readable@1.1.3: + resolution: {integrity: sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==} + dependencies: + inherits: 2.0.4 + process-nextick-args: 2.0.1 + readable-stream: 2.3.8 + dev: true + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -2783,6 +2882,11 @@ packages: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true + /data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + dev: true + /date-format@4.0.14: resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} engines: {node: '>=4.0'} @@ -2958,6 +3062,12 @@ packages: engines: {node: '>=12'} dev: false + /duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + dependencies: + readable-stream: 2.3.8 + dev: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false @@ -3537,6 +3647,14 @@ packages: dependencies: reusify: 1.0.4 + /fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.2.1 + dev: true + /ffprobe@1.1.2: resolution: {integrity: sha512-a+oTbhyeM7Z8PRy+mpzmVUAnATZT7z4BO94HSKeqHupdmjiKZ1djzcZkyoyXA21zCOCG7oVRrsBMmvvtmzoz4g==} dependencies: @@ -3603,6 +3721,13 @@ packages: mime-types: 2.1.35 dev: false + /formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + dependencies: + fetch-blob: 3.2.0 + dev: true + /forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3655,6 +3780,16 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + /fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} @@ -4195,6 +4330,10 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + dev: true + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -4225,6 +4364,10 @@ packages: - supports-color dev: false + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: false + /lookpath@1.2.2: resolution: {integrity: sha512-k2Gmn8iV6qdME3ztZC2spubmQISimFOPLuQKiPaLcVdRz0IpdxrNClVepMlyTJlhodm/zG/VfbkWERm3kUIh+Q==} engines: {npm: '>=6.13.4'} @@ -4342,6 +4485,13 @@ packages: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: true + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + /mpd-parser@1.3.0: resolution: {integrity: sha512-WgeIwxAqkmb9uTn4ClicXpEQYCEduDqRKfmUdp4X8vmghKfBNXZLYpREn9eqrDx/Tf5LhzRcJLSpi4ohfV742Q==} hasBin: true @@ -4393,7 +4543,12 @@ packages: resolution: {integrity: sha512-jAlSOFR1Bls963NmFwxeQkNTzqjUF0NThm8Le7eRIRGzFUVJuMOFZDLv5Y30W/Oaw+KEebEJLAigwO9gQHoEmw==} engines: {node: '>=10'} dependencies: - semver: 7.3.8 + semver: 7.5.4 + dev: true + + /node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} dev: true /node-fetch@2.6.9: @@ -4408,6 +4563,15 @@ packages: whatwg-url: 5.0.0 dev: true + /node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + dev: true + /node-releases@2.0.10: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} dev: true @@ -4614,7 +4778,7 @@ packages: https-proxy-agent: 5.0.1 node-fetch: 2.6.9 progress: 2.0.3 - semver: 7.3.8 + semver: 7.5.4 tar-fs: 2.1.1 yargs: 16.2.0 transitivePeerDependencies: @@ -4695,6 +4859,39 @@ packages: react-is: 16.13.1 dev: true + /protobufjs@7.2.5: + resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==} + engines: {node: '>=12.0.0'} + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 18.15.11 + long: 5.2.3 + dev: false + + /protoc@1.1.3: + resolution: {integrity: sha512-Vy4OBxCcF0W38YrZZRFix659gFu8ujIxVDP1SUBK9ELzyeMSBe8m8tYyYlX1PI5j9gse9hWu4c4nzQaHesAf8Q==} + hasBin: true + requiresBuild: true + dependencies: + glob: 7.2.3 + mkdirp: 0.5.6 + node-fetch: 3.3.2 + rimraf: 3.0.2 + unzipper: 0.10.14 + uuid: 9.0.1 + vinyl: 2.2.1 + dev: true + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -4824,12 +5021,21 @@ packages: jsesc: 0.5.0 dev: true + /remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + dev: true + /removeNPMAbsolutePaths@3.0.1: resolution: {integrity: sha512-rJc1aHu5LT4rncs7gga/asiyQG+G1TkweO7L27D/oMBtfbHmSFSp7RgUtrfuZzk9c/4P2xHjHnD+cUNzWRFB4A==} engines: {node: '>=4.0.0'} hasBin: true dev: true + /replace-ext@1.0.1: + resolution: {integrity: sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==} + engines: {node: '>= 0.10'} + dev: true + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4873,6 +5079,13 @@ packages: resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} dev: false + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true @@ -4914,14 +5127,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - /semver@7.3.8: - resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} engines: {node: '>=10'} @@ -4962,6 +5167,10 @@ packages: - supports-color dev: false + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: true + /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} dev: false @@ -5167,6 +5376,10 @@ packages: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true + /traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + dev: true + /ts-api-utils@1.0.1(typescript@5.1.6): resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==} engines: {node: '>=16.13.0'} @@ -5313,6 +5526,21 @@ packages: engines: {node: '>= 0.8'} dev: false + /unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + dev: true + /update-browserslist-db@1.0.10(browserslist@4.21.5): resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true @@ -5351,6 +5579,11 @@ packages: engines: {node: '>= 0.4.0'} dev: false + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: true + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true @@ -5360,6 +5593,23 @@ packages: engines: {node: '>= 0.8'} dev: false + /vinyl@2.2.1: + resolution: {integrity: sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==} + engines: {node: '>= 0.10'} + dependencies: + clone: 2.1.2 + clone-buffer: 1.0.0 + clone-stats: 1.0.0 + cloneable-readable: 1.1.3 + remove-trailing-separator: 1.1.0 + replace-ext: 1.0.1 + dev: true + + /web-streams-polyfill@3.2.1: + resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} + engines: {node: '>= 8'} + dev: true + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true diff --git a/widevine/.gitkeep b/widevine/.gitkeep new file mode 100644 index 0000000..e69de29 From 64b2bf0b0899e847685413e9d342bbaa31e5401d Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 24 Dec 2023 11:52:57 -0800 Subject: [PATCH 06/69] Fix object downloading --- @types/crunchyAndroidObject.d.ts | 189 +++++++++++++++++++++++++++++++ @types/objectInfo.d.ts | 13 +++ crunchy.ts | 31 ++++- 3 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 @types/crunchyAndroidObject.d.ts diff --git a/@types/crunchyAndroidObject.d.ts b/@types/crunchyAndroidObject.d.ts new file mode 100644 index 0000000..b4d46fa --- /dev/null +++ b/@types/crunchyAndroidObject.d.ts @@ -0,0 +1,189 @@ +import { ImageType, Images, Image } from './objectInfo'; + +export interface CrunchyAndroidObject { + __class__: string; + __href__: string; + __resource_key__: string; + __links__: Actions; + __actions__: Actions; + total: number; + items: AndroidObject[]; +} + +export interface Actions { +} + +export interface AndroidObject { + __class__: string; + __href__: string; + __links__: Links; + __actions__: Actions; + id: string; + external_id: string; + channel_id: string; + title: string; + description: string; + promo_title: string; + promo_description: string; + type: string; + slug: string; + slug_title: string; + images: Images; + movie_listing_metadata?: MovieListingMetadata; + movie_metadata?: MovieMetadata; + playback?: string; + episode_metadata?: EpisodeMetadata; + streams_link?: string; + season_metadata?: SeasonMetadata; + linked_resource_key: string; + isSelected?: boolean; + f_num: string; + s_num: string; +} + +export interface Links { + 'episode/season': LinkData; + 'episode/series': LinkData; + resource: LinkData; + 'resource/channel': LinkData; + streams: LinkData; +} + +export interface LinkData { + href: string; +} + +export interface EpisodeMetadata { + audio_locale: Locale; + availability_ends: Date; + availability_notes: string; + availability_starts: Date; + available_date: null; + available_offline: boolean; + closed_captions_available: boolean; + duration_ms: number; + eligible_region: string; + episode: string; + episode_air_date: Date; + episode_number: number; + extended_maturity_rating: Record; + free_available_date: Date; + identifier: string; + is_clip: boolean; + is_dubbed: boolean; + is_mature: boolean; + is_premium_only: boolean; + is_subbed: boolean; + mature_blocked: boolean; + maturity_ratings: string[]; + premium_available_date: Date; + premium_date: null; + season_id: string; + season_number: number; + season_slug_title: string; + season_title: string; + sequence_number: number; + series_id: string; + series_slug_title: string; + series_title: string; + subtitle_locales: Locale[]; + tenant_categories?: string[]; + upload_date: Date; + versions: EpisodeMetadataVersion[]; +} + +export interface MovieListingMetadata { + availability_notes: string; + available_date: null; + available_offline: boolean; + duration_ms: number; + extended_description: string; + extended_maturity_rating: Record; + first_movie_id: string; + free_available_date: Date; + is_dubbed: boolean; + is_mature: boolean; + is_premium_only: boolean; + is_subbed: boolean; + mature_blocked: boolean; + maturity_ratings: string[]; + movie_release_year: number; + premium_available_date: Date; + premium_date: null; + subtitle_locales: Locale[]; + tenant_categories: string[]; +} + +export interface MovieMetadata { + availability_notes: string; + available_offline: boolean; + closed_captions_available: boolean; + duration_ms: number; + extended_maturity_rating: Record; + is_dubbed: boolean; + is_mature: boolean; + is_premium_only: boolean; + is_subbed: boolean; + mature_blocked: boolean; + maturity_ratings: string[]; + movie_listing_id: string; + movie_listing_slug_title: string; + movie_listing_title: string; +} + +export interface SeasonMetadata { + audio_locale: Locale; + audio_locales: Locale[]; + extended_maturity_rating: Record; + identifier: string; + is_mature: boolean; + mature_blocked: boolean; + maturity_ratings: string[]; + season_display_number: string; + season_sequence_number: number; + subtitle_locales: Locale[]; + versions: SeasonMetadataVersion[]; +} + +export interface SeasonMetadataVersion { + audio_locale: Locale; + guid: string; + original: boolean; + variant: string; +} +export interface SeriesMetadata { + audio_locales: Locale[]; + availability_notes: string; + episode_count: number; + extended_description: string; + extended_maturity_rating: Record; + is_dubbed: boolean; + is_mature: boolean; + is_simulcast: boolean; + is_subbed: boolean; + mature_blocked: boolean; + maturity_ratings: string[]; + season_count: number; + series_launch_year: number; + subtitle_locales: Locale[]; + tenant_categories?: string[]; +} + +export enum Locale { + enUS = 'en-US', + esLA = 'es-LA', + es419 = 'es-419', + esES = 'es-ES', + ptBR = 'pt-BR', + frFR = 'fr-FR', + deDE = 'de-DE', + arME = 'ar-ME', + arSA = 'ar-SA', + itIT = 'it-IT', + ruRU = 'ru-RU', + trTR = 'tr-TR', + hiIN = 'hi-IN', + zhCN = 'zh-CN', + koKR = 'ko-KR', + jaJP = 'ja-JP', +} \ No newline at end of file diff --git a/@types/objectInfo.d.ts b/@types/objectInfo.d.ts index 8a710c5..096e2c3 100644 --- a/@types/objectInfo.d.ts +++ b/@types/objectInfo.d.ts @@ -7,6 +7,7 @@ export interface ObjectInfo { } export interface CrunchyObject { + __links__?: Links; channel_id: string; slug: string; images: Images; @@ -31,6 +32,18 @@ export interface CrunchyObject { s_num: string; } +export interface Links { + 'episode/season': LinkData; + 'episode/series': LinkData; + resource: LinkData; + 'resource/channel': LinkData; + streams: LinkData; +} + +export interface LinkData { + href: string; +} + export interface EpisodeMetadata { audio_locale: Locale; availability_ends: Date; diff --git a/crunchy.ts b/crunchy.ts index d360770..e4de484 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -39,6 +39,7 @@ import { ServiceClass } from './@types/serviceClassInterface'; import { CrunchyAndroidStreams } from './@types/crunchyAndroidStreams'; import { CrunchyAndroidEpisodes, CrunchyEpisode } from './@types/crunchyAndroidEpisodes'; import { parse } from './modules/module.transform-mpd'; +import { CrunchyAndroidObject } from './@types/crunchyAndroidObject'; export type sxItem = { language: langsData.LanguageItem, @@ -964,7 +965,20 @@ export default class Crunchy implements ServiceClass { }; // reqs - const objectReq = await this.req.getData(`${api.cms}/objects/${doEpsFilter.values.join(',')}?preferred_audio_language=ja-JP`, AuthHeaders); + const objectReqOpts = [ + api.beta_cms, + this.cmsToken.cms.bucket, + '/objects/', + doEpsFilter.values.join(','), + '?', + new URLSearchParams({ + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const objectReq = await this.req.getData(objectReqOpts, AuthHeaders); + //const objectReq = await this.req.getData(`${api.cms}/objects/${doEpsFilter.values.join(',')}?preferred_audio_language=ja-JP`, AuthHeaders); if(!objectReq.ok || !objectReq.res){ console.error('Objects Request FAILED!'); if(objectReq.error && objectReq.error.res && objectReq.error.res.body){ @@ -976,7 +990,13 @@ export default class Crunchy implements ServiceClass { return []; } - const objectInfo = JSON.parse(objectReq.res.body) as ObjectInfo; + //const objectInfo = JSON.parse(objectReq.res.body) as ObjectInfo; + const objectInfoAndroid = JSON.parse(objectReq.res.body) as CrunchyAndroidObject; + const objectInfo: ObjectInfo = { + total: objectInfoAndroid.total, + data: objectInfoAndroid.items, + meta: {} + }; if(earlyReturn){ return objectInfo; } @@ -1041,6 +1061,13 @@ export default class Crunchy implements ServiceClass { } selectedMedia.push(epMeta); item.isSelected = true; + } else if (item.__links__) { + epMeta.data[0].playback = item.__links__.streams.href; + if(!item.playback) { + item.playback = item.__links__.streams.href; + } + selectedMedia.push(epMeta); + item.isSelected = true; } await this.logObject(item, 2); } From 3b37703d7a097d75a8ba6f12fd6d346f1febeb2e Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 24 Dec 2023 19:54:30 +0000 Subject: [PATCH 07/69] Fix object downloading + Documentation --- docs/DOCUMENTATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md index 82b6f79..c047a78 100644 --- a/docs/DOCUMENTATION.md +++ b/docs/DOCUMENTATION.md @@ -1,4 +1,4 @@ -# multi-downloader-nx (4.4.1v) +# multi-downloader-nx (4.4.2v) If you find any bugs in this documentation or in the programm itself please report it [over on GitHub](https://github.com/anidl/multi-downloader-nx/issues). From 7524f4c08fd2fbb94cafd0f9314b70f4b51d81bc Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 24 Dec 2023 19:40:42 -0800 Subject: [PATCH 08/69] Remove uneeded console logs --- crunchy.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index e4de484..9f05e00 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1489,20 +1489,11 @@ export default class Crunchy implements ServiceClass { 'user_id': this.token.account_id }) }); - console.info({ - 'body': JSON.stringify({ - 'accounting_id': 'crunchyroll', - 'asset_id': assetId, - 'session_id': sessionId, - 'user_id': this.token.account_id - }) - }); if(!decReq.ok || !decReq.res){ console.error('Request to DRM Authentication failed:', decReq.error?.code, decReq.error?.message); return undefined; } const authData = JSON.parse(decReq.res.body) as {'custom_data': string, 'token': string}; - console.info(authData); const encryptionKeys = await getKeys(chosenVideoSegments.pssh, 'https://lic.drmtoday.com/license-proxy-widevine/cenc/', { 'dt-custom-data': authData.custom_data, 'x-dt-auth-token': authData.token From ee38658d0d91f96928bf138f34f6d7e4c51dff04 Mon Sep 17 00:00:00 2001 From: AnidlSupport Date: Mon, 25 Dec 2023 10:46:13 -0800 Subject: [PATCH 09/69] Some refactoring --- .gitignore | 5 ++- config/bin-path.yml | 2 +- crunchy.ts | 94 ++++++++++++++++++++---------------------- modules/cr_widevine.ts | 16 +++---- 4 files changed, 59 insertions(+), 58 deletions(-) diff --git a/.gitignore b/.gitignore index 68f1799..cc9d16b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,7 @@ crunchyendpoints /logs /tmp/*/ /videos/*/ -/tmp/*.* \ No newline at end of file +/tmp/*.* +bin +widevine/* +!widevine/.gitkeep diff --git a/config/bin-path.yml b/config/bin-path.yml index 8c08eb7..0c948ef 100644 --- a/config/bin-path.yml +++ b/config/bin-path.yml @@ -1,4 +1,4 @@ ffmpeg: "ffmpeg.exe" mkvmerge: "mkvmerge.exe" ffprobe: "ffprobe.exe" -mp4decrypt: "mp4decrypt.exe" +mp4decrypt: "./bin/mp4decrypt" diff --git a/crunchy.ts b/crunchy.ts index 9f05e00..6142cda 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1376,6 +1376,7 @@ export default class Crunchy implements ServiceClass { fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep); const outFile = parseFileName(options.fileName + '.' + (mMeta.lang?.name || lang.name), variables, options.numbers, options.override).join(path.sep); + let [audioDownloaded, videoDownloaded] = [false, false]; // When best selected video quality is already downloaded if(dlVideoOnce && options.dlVideoOnce) { @@ -1397,7 +1398,7 @@ export default class Crunchy implements ServiceClass { segments: chosenVideoSegments.segments }; const videoDownload = await new streamdl({ - output: `${tsFile}.video.ts`, + output: `${tsFile}.video.enc.ts`, timeout: options.timeout, m3u8json: videoJson, // baseurl: chunkPlaylist.baseUrl, @@ -1418,13 +1419,8 @@ export default class Crunchy implements ServiceClass { console.error(`DL Stats: ${JSON.stringify(videoDownload.parts)}\n`); dlFailed = true; } - /*files.push({ - type: 'Video', - path: `${tsFile}.video.ts`, - lang: lang, - isPrimary: isPrimary - });*/ dlVideoOnce = true; + videoDownloaded = true; } if (chosenAudioSegments) { @@ -1444,7 +1440,7 @@ export default class Crunchy implements ServiceClass { segments: chosenAudioSegments.segments }; const audioDownload = await new streamdl({ - output: `${tsFile}.audio.ts`, + output: `${tsFile}.audio.enc.ts`, timeout: options.timeout, m3u8json: audioJson, // baseurl: chunkPlaylist.baseUrl, @@ -1465,16 +1461,11 @@ export default class Crunchy implements ServiceClass { console.error(`DL Stats: ${JSON.stringify(audioDownload.parts)}\n`); dlFailed = true; } - /*files.push({ - type: 'Audio', - path: `${tsFile}.audio.ts`, - lang: lang, - isPrimary: isPrimary - });*/ + audioDownloaded = true; } //Handle Decryption if needed - if (chosenVideoSegments.pssh || chosenAudioSegments.pssh) { + if ((chosenVideoSegments.pssh || chosenAudioSegments.pssh) && (videoDownloaded || audioDownloaded)) { const assetIdRegex = chosenVideoSegments.segments[0].uri.match(/\/assets\/(?:p\/)?([^_,]+)/); const assetId = assetIdRegex ? assetIdRegex[1] : null; const sessionId = new Date().getUTCMilliseconds().toString().padStart(3, '0') + process.hrtime.bigint().toString().slice(0, 13); @@ -1509,54 +1500,59 @@ export default class Crunchy implements ServiceClass { if (this.cfg.bin.mp4decrypt) { const commandBase = `--show-progress --key ${encryptionKeys[1].kid}:${encryptionKeys[1].key} `; - const commandVideo = commandBase+`"${tsFile}.video.ts" "${tsFile}.dec.video.ts"`; - const commandAudio = commandBase+`"${tsFile}.audio.ts" "${tsFile}.dec.audio.ts"`; + const commandVideo = commandBase+`"${tsFile}.video.enc.ts" "${tsFile}.video.ts"`; + const commandAudio = commandBase+`"${tsFile}.audio.enc.ts" "${tsFile}.audio.ts"`; - console.info('Started decrypting video'); - const decryptVideo = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandVideo); - if (!decryptVideo.isOk) { - console.error(decryptVideo.err); - console.error(`Decryption failed with exit code ${decryptVideo.err.code}`); - return undefined; - } else { - console.info('Decryption done for video'); + if (videoDownloaded) { + console.info('Started decrypting video'); + const decryptVideo = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandVideo); + if (!decryptVideo.isOk) { + console.error(decryptVideo.err); + console.error(`Decryption failed with exit code ${decryptVideo.err.code}`); + return undefined; + } else { + console.info('Decryption done for video'); + fs.removeSync(`${tsFile}.video.enc.ts`); + files.push({ + type: 'Video', + path: `${tsFile}.video.ts`, + lang: lang, + isPrimary: isPrimary + }); + } } - console.info('Started decrypting audio'); - const decryptAudio = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandAudio); - if (!decryptAudio.isOk) { - console.error(decryptAudio.err); - console.error(`Decryption failed with exit code ${decryptAudio.err.code}`); - return undefined; - } else { - console.info('Decryption done for video'); + if (audioDownloaded) { + console.info('Started decrypting audio'); + const decryptAudio = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandAudio); + if (!decryptAudio.isOk) { + console.error(decryptAudio.err); + console.error(`Decryption failed with exit code ${decryptAudio.err.code}`); + return undefined; + } else { + fs.removeSync(`${tsFile}.audio.enc.ts`); + files.push({ + type: 'Audio', + path: `${tsFile}.audio.ts`, + lang: lang, + isPrimary: isPrimary + }); + console.info('Decryption done for audio'); + } } - - files.push({ - type: 'Video', - path: `${tsFile}.dec.video.ts`, - lang: lang, - isPrimary: isPrimary - }); - files.push({ - type: 'Audio', - path: `${tsFile}.dec.audio.ts`, - lang: lang, - isPrimary: isPrimary - }); } else { console.warn('mp4decrypt not found, files need decryption. Decryption Keys:', encryptionKeys); } } else { files.push({ type: 'Video', - path: `${tsFile}.video.ts`, + path: `${tsFile}.video.enc.ts`, lang: lang, isPrimary: isPrimary }); files.push({ type: 'Audio', - path: `${tsFile}.audio.ts`, + path: `${tsFile}.audio.enc.ts`, lang: lang, isPrimary: isPrimary }); @@ -2053,7 +2049,7 @@ export default class Crunchy implements ServiceClass { e: epNum, image: images[Math.floor(images.length / 2)].source, }; - if (item.__links__.streams.href) { + if (item.__links__?.streams?.href) { epMeta.data[0].playback = item.__links__.streams.href; if(!item.playback) { item.playback = item.__links__.streams.href; diff --git a/modules/cr_widevine.ts b/modules/cr_widevine.ts index 2235d27..d748021 100644 --- a/modules/cr_widevine.ts +++ b/modules/cr_widevine.ts @@ -1,12 +1,13 @@ import { KeyContainer, Session } from './license'; import fs from 'fs'; import { console } from './log'; +import got from 'got'; //read cdm files located in the same directory const privateKey = fs.readFileSync('./widevine/device_private_key'); const identifierBlob = fs.readFileSync('./widevine/device_client_id_blob'); -export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record): Promise { +export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record): Promise { if (!pssh) return []; //pssh found in the mpd manifest const psshBuffer = Buffer.from( @@ -18,19 +19,20 @@ export default async function getKeys(pssh: string | undefined, licenseServer: s const session = new Session({ privateKey, identifierBlob }, psshBuffer); //Generate license - const response = await fetch(licenseServer, { + const response = await got(licenseServer, { method: 'POST', body: session.createLicenseRequest(), - headers: authData + headers: authData, + responseType: 'text' }); - if (response.ok) { + if (response.statusCode === 200) { //Parse License and return keys - const json = await response.json(); + const json = JSON.parse(response.body); const keys = session.parseLicense(Buffer.from(json['license'], 'base64')); return keys; } else { - console.info('License request failed:', response.statusText); + console.info('License request failed:', response.statusMessage); return []; } -} \ No newline at end of file +} From 942f673934728443221665669f1825e065d7616a Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 25 Dec 2023 11:11:55 -0800 Subject: [PATCH 10/69] Fix WV relative pathing --- modules/cr_widevine.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/cr_widevine.ts b/modules/cr_widevine.ts index d748021..63eb5b9 100644 --- a/modules/cr_widevine.ts +++ b/modules/cr_widevine.ts @@ -2,10 +2,12 @@ import { KeyContainer, Session } from './license'; import fs from 'fs'; import { console } from './log'; import got from 'got'; +import { workingDir } from './module.cfg-loader'; +import path from 'path'; //read cdm files located in the same directory -const privateKey = fs.readFileSync('./widevine/device_private_key'); -const identifierBlob = fs.readFileSync('./widevine/device_client_id_blob'); +const privateKey = fs.readFileSync(path.join(workingDir, 'widevine', 'device_private_key')); +const identifierBlob = fs.readFileSync(path.join(workingDir, 'widevine', 'device_client_id_blob')); export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record): Promise { if (!pssh) return []; From ca01c0496159a558a77b9242f919fb62319a3102 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Fri, 29 Dec 2023 21:14:12 -0800 Subject: [PATCH 11/69] API switching --- @types/crunchyAndroidEpisodes.d.ts | 56 +++--- @types/crunchyAndroidStreams.d.ts | 50 ++++- @types/crunchyEpisodeList.d.ts | 19 +- @types/crunchyTypes.d.ts | 6 +- @types/playbackData.d.ts | 2 +- crunchy.ts | 309 ++++++++++++++++++----------- modules/module.app-args.ts | 1 + modules/module.args.ts | 14 ++ package.json | 2 + pnpm-lock.yaml | 15 ++ 10 files changed, 314 insertions(+), 160 deletions(-) diff --git a/@types/crunchyAndroidEpisodes.d.ts b/@types/crunchyAndroidEpisodes.d.ts index 35845bd..c5488c0 100644 --- a/@types/crunchyAndroidEpisodes.d.ts +++ b/@types/crunchyAndroidEpisodes.d.ts @@ -1,3 +1,5 @@ +import { Images } from './crunchyEpisodeList'; + export interface CrunchyAndroidEpisodes { __class__: string; __href__: string; @@ -5,13 +7,13 @@ export interface CrunchyAndroidEpisodes { __links__: Actions; __actions__: Actions; total: number; - items: CrunchyEpisode[]; + items: CrunchyAndroidEpisode[]; } export interface Actions { } -export interface CrunchyEpisode { +export interface CrunchyAndroidEpisode { __class__: string; __href__: string; __resource_key__: string; @@ -19,7 +21,7 @@ export interface CrunchyEpisode { __actions__: Actions; playback: string; id: string; - channel_id: string; + channel_id: ChannelID; series_id: string; series_title: string; series_slug_title: string; @@ -37,19 +39,19 @@ export interface CrunchyEpisode { next_episode_id: string; next_episode_title: string; hd_flag: boolean; - maturity_ratings: string[]; + maturity_ratings: MaturityRating[]; extended_maturity_rating: Actions; is_mature: boolean; mature_blocked: boolean; - episode_air_date: string; - upload_date: string; - availability_starts: string; - availability_ends: string; + episode_air_date: Date; + upload_date: Date; + availability_starts: Date; + availability_ends: Date; eligible_region: string; available_date: Date; - free_available_date: string; + free_available_date: Date; premium_date: Date; - premium_available_date: string; + premium_available_date: Date; is_subbed: boolean; is_dubbed: boolean; is_clip: boolean; @@ -57,13 +59,13 @@ export interface CrunchyEpisode { seo_description: string; season_tags: string[]; available_offline: boolean; - subtitle_locales: string[]; + subtitle_locales: Locale[]; availability_notes: string; - audio_locale: string; + audio_locale: Locale; versions: Version[]; closed_captions_available: boolean; identifier: string; - media_type: string; + media_type: MediaType; slug: string; images: Images; duration_ms: number; @@ -76,21 +78,17 @@ export interface CrunchyEpisode { } export interface Links { - 'episode/channel': EpisodeChannel; - 'episode/next_episode': EpisodeChannel; - 'episode/season': EpisodeChannel; - 'episode/series': EpisodeChannel; - streams: EpisodeChannel; + 'episode/channel': Link; + 'episode/next_episode': Link; + 'episode/season': Link; + 'episode/series': Link; + streams: Link; } -export interface EpisodeChannel { +export interface Link { href: string; } -export interface Images { - thumbnail: Array; -} - export interface Thumbnail { width: number; height: number; @@ -117,6 +115,18 @@ export enum Locale { jaJP = 'ja-JP', } +export enum MediaType { + Episode = 'episode', +} + +export enum ChannelID { + Crunchyroll = 'crunchyroll', +} + +export enum MaturityRating { + Tv14 = 'TV-14', +} + export interface Version { audio_locale: Locale; guid: string; diff --git a/@types/crunchyAndroidStreams.d.ts b/@types/crunchyAndroidStreams.d.ts index 637995c..c1e56c3 100644 --- a/@types/crunchyAndroidStreams.d.ts +++ b/@types/crunchyAndroidStreams.d.ts @@ -5,8 +5,8 @@ export interface CrunchyAndroidStreams { __links__: Links; __actions__: Actions; media_id: string; - audio_locale: string; - subtitles: { [key: string]: Subtitle }; + audio_locale: Locale; + subtitles: Subtitles; closed_captions: Actions; streams: Streams; bifs: string[]; @@ -14,6 +14,26 @@ export interface CrunchyAndroidStreams { captions: Actions; } +export interface Subtitles { + '': Subtitle; + 'en-US'?: Subtitle; + 'es-LA'?: Subtitle; + 'es-419'?: Subtitle; + 'es-ES'?: Subtitle; + 'pt-BR'?: Subtitle; + 'fr-FR'?: Subtitle; + 'de-DE'?: Subtitle; + 'ar-ME'?: Subtitle; + 'ar-SA'?: Subtitle; + 'it-IT'?: Subtitle; + 'ru-RU'?: Subtitle; + 'tr-TR'?: Subtitle; + 'hi-IN'?: Subtitle; + 'zh-CN'?: Subtitle; + 'ko-KR'?: Subtitle; + 'ja-JP'?: Subtitle; +} + export interface Actions { } @@ -30,7 +50,7 @@ export interface Streams { } export interface Download { - hardsub_locale: string; + hardsub_locale: Locale; hardsub_lang?: string; url: string; } @@ -40,13 +60,13 @@ export interface Urls { } export interface Subtitle { - locale: string; + locale: Locale; url: string; format: string; } export interface Version { - audio_locale: string; + audio_locale: Locale; guid: string; original: boolean; variant: string; @@ -54,3 +74,23 @@ export interface Version { media_guid: string; is_premium_only: boolean; } + +export enum Locale { + default = '', + enUS = 'en-US', + esLA = 'es-LA', + es419 = 'es-419', + esES = 'es-ES', + ptBR = 'pt-BR', + frFR = 'fr-FR', + deDE = 'de-DE', + arME = 'ar-ME', + arSA = 'ar-SA', + itIT = 'it-IT', + ruRU = 'ru-RU', + trTR = 'tr-TR', + hiIN = 'hi-IN', + zhCN = 'zh-CN', + koKR = 'ko-KR', + jaJP = 'ja-JP', +} \ No newline at end of file diff --git a/@types/crunchyEpisodeList.d.ts b/@types/crunchyEpisodeList.d.ts index ee1fb15..2f57ca4 100644 --- a/@types/crunchyEpisodeList.d.ts +++ b/@types/crunchyEpisodeList.d.ts @@ -1,3 +1,5 @@ +import { Links } from './crunchyAndroidEpisodes'; + export interface CrunchyEpisodeList { total: number; data: CrunchyEpisode[]; @@ -41,27 +43,28 @@ export interface CrunchyEpisode { listing_id: string; episode_air_date: Date; slug: string; - available_date: null; + available_date: Date; subtitle_locales: Locale[]; slug_title: string; available_offline: boolean; description: string; is_subbed: boolean; - premium_date: null; + premium_date: Date; upload_date: Date; season_slug_title: string; closed_captions_available: boolean; episode_number: number; season_tags: any[]; maturity_ratings: MaturityRating[]; - streams_link: string; + streams_link?: string; mature_blocked: boolean; is_clip: boolean; hd_flag: boolean; - hide_season_title?: boolean; - hide_season_number?: boolean; - isSelected?: boolean; - seq_id: string; + hide_season_title?: boolean; + hide_season_number?: boolean; + isSelected?: boolean; + seq_id: string; + __links__?: Links; } export enum Locale { @@ -127,5 +130,5 @@ export interface Version { } export interface Meta { - versions_considered: boolean; + versions_considered?: boolean; } \ No newline at end of file diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 605553a..4252a46 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -31,13 +31,15 @@ export type CrunchyDownloadOptions = { dlVideoOnce: boolean, skipmux?: boolean, syncTiming: boolean, + apiType: 'web' | 'android' } -export type CurnchyMultiDownload = { +export type CrunchyMultiDownload = { dubLang: string[], all?: boolean, but?: boolean, - e?: string + e?: string, + crapi: 'web' | 'android' } export type CrunchyMuxOptions = { diff --git a/@types/playbackData.d.ts b/@types/playbackData.d.ts index f326a7b..3374c3e 100644 --- a/@types/playbackData.d.ts +++ b/@types/playbackData.d.ts @@ -1,7 +1,7 @@ // Generated by https://quicktype.io export interface PlaybackData { total: number; - data: { [key: string]: { [key: string]: StreamDetails } }; + data: [{ [key: string]: { [key: string]: StreamDetails } }]; meta: Meta; } diff --git a/crunchy.ts b/crunchy.ts index 6142cda..f5bdb37 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -26,21 +26,23 @@ import getKeys from './modules/cr_widevine'; import { domain, api } from './modules/module.api-urls'; import * as reqModule from './modules/module.req'; import { CrunchySearch } from './@types/crunchySearch'; -//import { CrunchyEpisodeList, CrunchyEpisode } from './@types/crunchyEpisodeList'; -import { CrunchyDownloadOptions, CrunchyEpMeta, CrunchyMuxOptions, CurnchyMultiDownload, DownloadedMedia, ParseItem, SeriesSearch, SeriesSearchItem } from './@types/crunchyTypes'; +import { CrunchyEpisodeList, CrunchyEpisode } from './@types/crunchyEpisodeList'; +import { CrunchyDownloadOptions, CrunchyEpMeta, CrunchyMuxOptions, CrunchyMultiDownload, DownloadedMedia, ParseItem, SeriesSearch, SeriesSearchItem } from './@types/crunchyTypes'; import { ObjectInfo } from './@types/objectInfo'; import parseFileName, { Variable } from './modules/module.filename'; -//import { PlaybackData } from './@types/playbackData'; +import { PlaybackData } from './@types/playbackData'; import { downloaded } from './modules/module.downloadArchive'; import parseSelect from './modules/module.parseSelect'; import { AvailableFilenameVars, getDefault } from './modules/module.args'; import { AuthData, AuthResponse, Episode, ResponseBase, SearchData, SearchResponse, SearchResponseItem } from './@types/messageHandler'; import { ServiceClass } from './@types/serviceClassInterface'; import { CrunchyAndroidStreams } from './@types/crunchyAndroidStreams'; -import { CrunchyAndroidEpisodes, CrunchyEpisode } from './@types/crunchyAndroidEpisodes'; +import { CrunchyAndroidEpisodes } from './@types/crunchyAndroidEpisodes'; import { parse } from './modules/module.transform-mpd'; import { CrunchyAndroidObject } from './@types/crunchyAndroidObject'; +import util from 'util'; + export type sxItem = { language: langsData.LanguageItem, path: string, @@ -112,7 +114,7 @@ export default class Crunchy implements ServiceClass { const selected = await this.downloadFromSeriesID(argv.series, { ...argv }); if (selected.isOk) { for (const select of selected.value) { - if (!(await this.downloadEpisode(select, {...argv, skipsubs: false }, true))) { + if (!(await this.downloadEpisode(select, {...argv, skipsubs: false, apiType: argv.crapi }, true))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -130,10 +132,10 @@ export default class Crunchy implements ServiceClass { console.info('One show can only be downloaded with one dub. Use --srz instead.'); } argv.dubLang = [argv.dubLang[0]]; - const selected = await this.getSeasonById(argv.s, argv.numbers, argv.e, argv.but, argv.all); + const selected = await this.getSeasonById(argv.s, argv.numbers, argv.e, argv.but, argv.all, argv.crapi); if (selected.isOk) { for (const select of selected.value) { - if (!(await this.downloadEpisode(select, {...argv, skipsubs: false }))) { + if (!(await this.downloadEpisode(select, {...argv, skipsubs: false, apiType: argv.crapi }))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -143,9 +145,9 @@ export default class Crunchy implements ServiceClass { } else if(argv.e){ await this.refreshToken(); - const selected = await this.getObjectById(argv.e, false); + const selected = await this.getObjectById(argv.crapi, argv.e, false); for (const select of selected as Partial[]) { - if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false }))) { + if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false, apiType: argv.crapi }))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -153,9 +155,9 @@ export default class Crunchy implements ServiceClass { return true; } else if (argv.extid) { await this.refreshToken(); - const selected = await this.getObjectById(argv.extid, false, true); + const selected = await this.getObjectById(argv.crapi, argv.extid, false, true); for (const select of selected as Partial[]) { - if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false }))) { + if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false, apiType: argv.crapi }))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -352,6 +354,7 @@ export default class Crunchy implements ServiceClass { this.cmsToken.cms.bucket, '/index?', new URLSearchParams({ + 'preferred_audio_language': 'ja-JP', 'Policy': this.cmsToken.cms.policy, 'Signature': this.cmsToken.cms.signature, 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, @@ -745,7 +748,7 @@ export default class Crunchy implements ServiceClass { console.info(` Total results: ${newlyAddedResults.total} (Page: ${pageCur}/${pageMax})`); } - public async getSeasonById(id: string, numbers: number, e: string|undefined, but: boolean, all: boolean) : Promise> { + public async getSeasonById(id: string, numbers: number, e: string|undefined, but: boolean, all: boolean, apiType: 'web' | 'android') : Promise> { if(!this.cmsToken.cms){ console.error('Authentication required!'); return { isOk: false, reason: new Error('Authentication required') }; @@ -768,27 +771,42 @@ export default class Crunchy implements ServiceClass { const showInfo = JSON.parse(showInfoReq.res.body); this.logObject(showInfo.data[0], 0); + let episodeList = { total: 0, data: [], meta: {} } as CrunchyEpisodeList; //get episode info - const reqEpsListOpts = [ - api.beta_cms, - this.cmsToken.cms.bucket, - '/episodes?', - new URLSearchParams({ - 'season_id': id, - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - - const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); - //const reqEpsList = await this.req.getData(`${api.cms}/seasons/${id}/episodes?preferred_audio_language=ja-JP`, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; + if (apiType == 'android') { + const reqEpsListOpts = [ + api.beta_cms, + this.cmsToken.cms.bucket, + '/episodes?', + new URLSearchParams({ + 'preferred_audio_language': 'ja-JP', + 'season_id': id, + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); + if(!reqEpsList.ok || !reqEpsList.res){ + console.error('Episode List Request FAILED!'); + return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; + } + //CrunchyEpisodeList + const episodeListAndroid = JSON.parse(reqEpsList.res.body) as CrunchyAndroidEpisodes; + episodeList = { + total: episodeListAndroid.total, + data: episodeListAndroid.items, + meta: {} + }; + } else { + const reqEpsList = await this.req.getData(`${api.cms}/seasons/${id}/episodes?preferred_audio_language=ja-JP`, AuthHeaders); + if(!reqEpsList.ok || !reqEpsList.res){ + console.error('Episode List Request FAILED!'); + return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; + } + //CrunchyEpisodeList + episodeList = JSON.parse(reqEpsList.res.body) as CrunchyEpisodeList; } - //CrunchyEpisodeList - const episodeList = JSON.parse(reqEpsList.res.body) as CrunchyAndroidEpisodes; const epNumList: { ep: number[], @@ -804,7 +822,7 @@ export default class Crunchy implements ServiceClass { const doEpsFilter = parseSelect(e as string); const selectedMedia: CrunchyEpMeta[] = []; - episodeList.items.forEach((item) => { + episodeList.data.forEach((item) => { item.hide_season_title = true; if(item.season_title == '' && item.series_title != ''){ item.season_title = item.series_title; @@ -853,12 +871,18 @@ export default class Crunchy implements ServiceClass { image: images[Math.floor(images.length / 2)].source }; // Check for streams_link and update playback var if needed - if (item.__links__.streams.href) { + if (item.__links__?.streams.href) { epMeta.data[0].playback = item.__links__.streams.href; if(!item.playback) { item.playback = item.__links__.streams.href; } } + if (item.streams_link) { + epMeta.data[0].playback = item.streams_link; + if(!item.playback) { + item.playback = item.streams_link; + } + } if (item.versions) { epMeta.data[0].versions = item.versions; } @@ -906,7 +930,7 @@ export default class Crunchy implements ServiceClass { return true; } - public async getObjectById(e?: string, earlyReturn?: boolean, external_id?: boolean): Promise[]|undefined> { + public async getObjectById(apiType: 'web' | 'android', e?: string, earlyReturn?: boolean, external_id?: boolean): Promise[]|undefined> { if(!this.cmsToken.cms){ console.error('Authentication required!'); return []; @@ -923,6 +947,7 @@ export default class Crunchy implements ServiceClass { '/channels/crunchyroll/objects', '?', new URLSearchParams({ + 'preferred_audio_language': 'ja-JP', 'external_id': ob, 'Policy': this.cmsToken.cms.policy, 'Signature': this.cmsToken.cms.signature, @@ -965,38 +990,53 @@ export default class Crunchy implements ServiceClass { }; // reqs - const objectReqOpts = [ - api.beta_cms, - this.cmsToken.cms.bucket, - '/objects/', - doEpsFilter.values.join(','), - '?', - new URLSearchParams({ - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - const objectReq = await this.req.getData(objectReqOpts, AuthHeaders); - //const objectReq = await this.req.getData(`${api.cms}/objects/${doEpsFilter.values.join(',')}?preferred_audio_language=ja-JP`, AuthHeaders); - if(!objectReq.ok || !objectReq.res){ - console.error('Objects Request FAILED!'); - if(objectReq.error && objectReq.error.res && objectReq.error.res.body){ - const objectInfo = JSON.parse(objectReq.error.res.body as string); - console.info('Body:', JSON.stringify(objectInfo, null, '\t')); - objectInfo.error = true; - return objectInfo; + let objectInfo: ObjectInfo = { total: 0, data: [], meta: {} }; + if (apiType == 'android') { + const objectReqOpts = [ + api.beta_cms, + this.cmsToken.cms.bucket, + '/objects/', + doEpsFilter.values.join(','), + '?', + new URLSearchParams({ + 'preferred_audio_language': 'ja-JP', + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const objectReq = await this.req.getData(objectReqOpts, AuthHeaders); + if(!objectReq.ok || !objectReq.res){ + console.error('Objects Request FAILED!'); + if(objectReq.error && objectReq.error.res && objectReq.error.res.body){ + const objectInfo = JSON.parse(objectReq.error.res.body as string); + console.info('Body:', JSON.stringify(objectInfo, null, '\t')); + objectInfo.error = true; + return objectInfo; + } + return []; } - return []; + const objectInfoAndroid = JSON.parse(objectReq.res.body) as CrunchyAndroidObject; + objectInfo = { + total: objectInfoAndroid.total, + data: objectInfoAndroid.items, + meta: {} + }; + } else { + const objectReq = await this.req.getData(`${api.cms}/objects/${doEpsFilter.values.join(',')}?preferred_audio_language=ja-JP`, AuthHeaders); + if(!objectReq.ok || !objectReq.res){ + console.error('Objects Request FAILED!'); + if(objectReq.error && objectReq.error.res && objectReq.error.res.body){ + const objectInfo = JSON.parse(objectReq.error.res.body as string); + console.info('Body:', JSON.stringify(objectInfo, null, '\t')); + objectInfo.error = true; + return objectInfo; + } + return []; + } + objectInfo = JSON.parse(objectReq.res.body) as ObjectInfo; } - //const objectInfo = JSON.parse(objectReq.res.body) as ObjectInfo; - const objectInfoAndroid = JSON.parse(objectReq.res.body) as CrunchyAndroidObject; - const objectInfo: ObjectInfo = { - total: objectInfoAndroid.total, - data: objectInfoAndroid.items, - meta: {} - }; if(earlyReturn){ return objectInfo; } @@ -1134,29 +1174,15 @@ export default class Crunchy implements ServiceClass { mediaId = mediaId.split(':')[1]; // /cms/v2/BUCKET/crunchyroll/videos/MEDIAID/streams - const videoStreamsReq = [ - api.beta_cms, - `${this.cmsToken.cms.bucket}/videos/${mediaId}/streams`, - '?', - new URLSearchParams({ - streams: 'all', - textType: 'all', - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - let playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders); - //console.info(playbackReq.res.body); - //let playbackReq = await this.req.getData(`${api.cms}/videos/${mediaId}/streams`, AuthHeaders); - if(!playbackReq.ok || !playbackReq.res){ - console.error('Request Stream URLs FAILED! Attempting fallback'); + let pbData = { total: 0, data: {}, meta: {} } as PlaybackData; + if (options.apiType == 'android') { const videoStreamsReq = [ - domain.api_beta, - mMeta.playback, + api.beta_cms, + `${this.cmsToken.cms.bucket}/videos/${mediaId}/streams`, '?', new URLSearchParams({ + 'preferred_audio_language': 'ja-JP', streams: 'all', textType: 'all', 'Policy': this.cmsToken.cms.policy, @@ -1164,16 +1190,42 @@ export default class Crunchy implements ServiceClass { 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, }), ].join(''); - playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders); - //playbackReq = await this.req.getData(`${domain.api_beta}${mMeta.playback}`, AuthHeaders); - if(!playbackReq.ok || !playbackReq.res){ - console.error('Fallback Request Stream URLs FAILED!'); - return undefined; - } - } - //const pbData = JSON.parse(playbackReq.res.body) as PlaybackData; - const pbData = JSON.parse(playbackReq.res.body) as CrunchyAndroidStreams; + let playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders); + if(!playbackReq.ok || !playbackReq.res){ + console.error('Request Stream URLs FAILED! Attempting fallback'); + playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders); + if(!playbackReq.ok || !playbackReq.res){ + console.error('Fallback Request Stream URLs FAILED!'); + return undefined; + } + } + const pbDataAndroid = JSON.parse(playbackReq.res.body) as CrunchyAndroidStreams; + pbData = { + total: 0, + data: [pbDataAndroid.streams], + meta: { + audio_locale: pbDataAndroid.audio_locale, + bifs: pbDataAndroid.bifs, + captions: pbDataAndroid.captions, + closed_captions: pbDataAndroid.closed_captions, + media_id: pbDataAndroid.media_id, + subtitles: pbDataAndroid.subtitles, + versions: pbDataAndroid.versions + } + }; + } else { + let playbackReq = await this.req.getData(`${api.cms}/videos/${mediaId}/streams`, AuthHeaders); + if(!playbackReq.ok || !playbackReq.res){ + console.error('Request Stream URLs FAILED! Attempting fallback'); + playbackReq = await this.req.getData(`${domain.api_beta}${mMeta.playback}`, AuthHeaders); + if(!playbackReq.ok || !playbackReq.res){ + console.error('Fallback Request Stream URLs FAILED!'); + return undefined; + } + } + pbData = JSON.parse(playbackReq.res.body) as PlaybackData; + } variables.push(...([ ['title', medias.episodeTitle, true], @@ -1193,11 +1245,11 @@ export default class Crunchy implements ServiceClass { let streams: any[] = []; let hsLangs: string[] = []; - const pbStreams = pbData.streams; + const pbStreams = pbData.data[0]; for(const s of Object.keys(pbStreams)){ //if(s.match(/hls/) && !s.match(/drm/) && !s.match(/trailer/)) { - if((s.match(/hls/) || s.match(/dash/)) && !s.match(/trailer/)) { + if((s.match(/hls/) || s.match(/dash/)) && !(s.match(/hls/) && s.match(/drm/)) && !s.match(/trailer/)) { const pb = Object.values(pbStreams[s]).map(v => { v.hardsub_lang = v.hardsub_locale ? langsData.fixAndFindCrLC(v.hardsub_locale).locale @@ -1219,7 +1271,7 @@ export default class Crunchy implements ServiceClass { return undefined; } - const audDub = langsData.findLang(langsData.fixLanguageTag(pbData.audio_locale as string) || '').code; + const audDub = langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || '').code; hsLangs = langsData.sortTags(hsLangs); streams = streams.map((s) => { @@ -1738,8 +1790,8 @@ export default class Crunchy implements ServiceClass { } if(!options.skipsubs && options.dlsubs.indexOf('none') == -1){ - if(pbData.subtitles && Object.values(pbData.subtitles).length > 0){ - const subsData = Object.values(pbData.subtitles); + if(pbData.meta.subtitles && Object.values(pbData.meta.subtitles).length > 0){ + const subsData = Object.values(pbData.meta.subtitles); const subsDataMapped = subsData.map((s) => { const subLang = langsData.fixAndFindCrLC(s.locale); return { @@ -1880,7 +1932,7 @@ export default class Crunchy implements ServiceClass { merger.cleanUp(); } - public async listSeriesID(id: string): Promise<{ list: Episode[], data: Record}> { @@ -1897,7 +1949,7 @@ export default class Crunchy implements ServiceClass { for(const season of Object.keys(result) as unknown as number[]) { for (const key of Object.keys(result[season])) { const s = result[season][key]; - (await this.getSeasonDataById(s))?.items?.forEach(episode => { + (await this.getSeasonDataById(s, apiType))?.data?.forEach(episode => { //TODO: Make sure the below code is ok //Prepare the episode array let item; @@ -1987,8 +2039,8 @@ export default class Crunchy implements ServiceClass { })}; } - public async downloadFromSeriesID(id: string, data: CurnchyMultiDownload) : Promise> { - const { data: episodes } = await this.listSeriesID(id); + public async downloadFromSeriesID(id: string, data: CrunchyMultiDownload) : Promise> { + const { data: episodes } = await this.listSeriesID(id, data.crapi); console.info(''); console.info('-'.repeat(30)); console.info(''); @@ -2133,7 +2185,7 @@ export default class Crunchy implements ServiceClass { return seasonsList; } - public async getSeasonDataById(item: SeriesSearchItem, log = false){ + public async getSeasonDataById(item: SeriesSearchItem, apiType: 'android' | 'web', log = false){ if(!this.cmsToken.cms){ console.error('Authentication required!'); return; @@ -2155,28 +2207,43 @@ export default class Crunchy implements ServiceClass { const showInfo = JSON.parse(showInfoReq.res.body); if (log) this.logObject(showInfo, 0); + + let episodeList = { total: 0, data: [], meta: {} } as CrunchyEpisodeList; //get episode info - - const reqEpsListOpts = [ - api.beta_cms, - this.cmsToken.cms.bucket, - '/episodes?', - new URLSearchParams({ - 'season_id': item.id, - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - - const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); - //const reqEpsList = await this.req.getData(`${api.cms}/seasons/${item.id}/episodes?preferred_audio_language=ja-JP`, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return; + if (apiType == 'android') { + const reqEpsListOpts = [ + api.beta_cms, + this.cmsToken.cms.bucket, + '/episodes?', + new URLSearchParams({ + 'preferred_audio_language': 'ja-JP', + 'season_id': item.id, + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); + if(!reqEpsList.ok || !reqEpsList.res){ + console.error('Episode List Request FAILED!'); + return; + } + //CrunchyEpisodeList + const episodeListAndroid = JSON.parse(reqEpsList.res.body) as CrunchyAndroidEpisodes; + episodeList = { + total: episodeListAndroid.total, + data: episodeListAndroid.items, + meta: {} + }; + } else { + const reqEpsList = await this.req.getData(`${api.cms}/seasons/${item.id}/episodes?preferred_audio_language=ja-JP`, AuthHeaders); + if(!reqEpsList.ok || !reqEpsList.res){ + console.error('Episode List Request FAILED!'); + return; + } + //CrunchyEpisodeList + episodeList = JSON.parse(reqEpsList.res.body) as CrunchyEpisodeList; } - //CrunchyEpisodeList - const episodeList = JSON.parse(reqEpsList.res.body) as CrunchyAndroidEpisodes; if(episodeList.total < 1){ console.info(' Season is empty!'); diff --git a/modules/module.app-args.ts b/modules/module.app-args.ts index 60c281e..8047cc1 100644 --- a/modules/module.app-args.ts +++ b/modules/module.app-args.ts @@ -64,6 +64,7 @@ let argvC: { _: (string | number)[]; $0: string; dlVideoOnce: boolean; + crapi: 'android' | 'web'; removeBumpers: boolean; originalFontSize: boolean; keepAllVideos: boolean; diff --git a/modules/module.args.ts b/modules/module.args.ts index b6e3374..07b728f 100644 --- a/modules/module.args.ts +++ b/modules/module.args.ts @@ -203,6 +203,20 @@ const args: TAppArg[] = [ default: false } }, + { + name: 'crapi', + describe: 'Selects the API type for Crunchyroll', + type: 'string', + group: 'dl', + service: ['crunchy'], + docDescribe: 'If set to Android, it has lower quality, but Non-DRM streams,' + + '\nIf set to Web, it has a higher quality adaptive stream, but everything is DRM.', + usage: '', + choices: ['android', 'web'], + default: { + default: 'android' + } + }, { name: 'removeBumpers', describe: 'Remove bumpers from final video', diff --git a/package.json b/package.json index f2d2a67..44c3c75 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@babel/core": "^7.22.9", "@babel/plugin-syntax-flow": "^7.22.5", "@babel/plugin-transform-react-jsx": "^7.22.5", + "@types/xmldom": "^0.1.34", "cheerio": "1.0.0-rc.12", "cors": "^2.8.5", "dotenv": "^16.3.1", @@ -65,6 +66,7 @@ "sei-helper": "^3.3.0", "typescript-eslint": "0.0.1-alpha.0", "ws": "^8.13.0", + "xmldom": "^0.6.0", "yaml": "^2.3.1", "yargs": "^17.7.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18c21ac..a292b25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@babel/plugin-transform-react-jsx': specifier: ^7.22.5 version: 7.22.5(@babel/core@7.22.9) + '@types/xmldom': + specifier: ^0.1.34 + version: 0.1.34 cheerio: specifier: 1.0.0-rc.12 version: 1.0.0-rc.12 @@ -74,6 +77,9 @@ dependencies: ws: specifier: ^8.13.0 version: 8.13.0 + xmldom: + specifier: ^0.6.0 + version: 0.6.0 yaml: specifier: ^2.3.1 version: 2.3.1 @@ -1982,6 +1988,10 @@ packages: '@types/node': 18.15.11 dev: true + /@types/xmldom@0.1.34: + resolution: {integrity: sha512-7eZFfxI9XHYjJJuugddV6N5YNeXgQE1lArWOcd1eCOKWb/FGs5SIjacSYuEJuwhsGS3gy4RuZ5EUIcqYscuPDA==} + dev: false + /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: true @@ -5681,6 +5691,11 @@ packages: optional: true dev: false + /xmldom@0.6.0: + resolution: {integrity: sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==} + engines: {node: '>=10.0.0'} + dev: false + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} From f9adcdea7fe6b908dfa47df31cacdf4e90bcbea5 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Fri, 29 Dec 2023 21:14:27 -0800 Subject: [PATCH 12/69] Fix bug with kstream 3 --- crunchy.ts | 2 +- modules/module.transform-mpd.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index f5bdb37..4f43211 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1352,7 +1352,7 @@ export default class Crunchy implements ServiceClass { else{ if (streamPlaylistsReq.res.body.match('MPD')) { //Parse MPD Playlists - const streamPlaylists = parse(streamPlaylistsReq.res.body, langsData.findLang(langsData.fixLanguageTag(pbData.audio_locale as string) || '')); + const streamPlaylists = parse(streamPlaylistsReq.res.body, langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || ''), curStream.url.match(/.*\.urlset\//)[0]); //Get name of CDNs/Servers const streamServers = Object.keys(streamPlaylists); diff --git a/modules/module.transform-mpd.ts b/modules/module.transform-mpd.ts index f5c2073..b3c5419 100644 --- a/modules/module.transform-mpd.ts +++ b/modules/module.transform-mpd.ts @@ -37,7 +37,10 @@ export type MPDParsed = { } } -export function parse(manifest: string, language: LanguageItem) { +export function parse(manifest: string, language: LanguageItem, url?: string) { + if (!manifest.includes('BaseURL') && url) { + manifest = manifest.replace(/()/gm, `$1${url}`); + } const parsed = mpdParse(manifest); const ret: MPDParsed = {}; From c3335db36658b905c3609b18f79c630af0cc3683 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Fri, 29 Dec 2023 21:17:55 -0800 Subject: [PATCH 13/69] remove util --- crunchy.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index 4f43211..3794659 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -41,8 +41,6 @@ import { CrunchyAndroidEpisodes } from './@types/crunchyAndroidEpisodes'; import { parse } from './modules/module.transform-mpd'; import { CrunchyAndroidObject } from './@types/crunchyAndroidObject'; -import util from 'util'; - export type sxItem = { language: langsData.LanguageItem, path: string, From 350337fddad130ef5fdf59a1dc9d3f7ab28c308f Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sat, 30 Dec 2023 15:40:54 -0800 Subject: [PATCH 14/69] Fix episode selection on web API --- crunchy.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crunchy.ts b/crunchy.ts index 3794659..e258299 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -2105,6 +2105,12 @@ export default class Crunchy implements ServiceClass { item.playback = item.__links__.streams.href; } } + if (item.streams_link) { + epMeta.data[0].playback = item.streams_link; + if(!item.playback) { + item.playback = item.streams_link; + } + } // find episode numbers if(item.playback && ((but && !doEpsFilter.isSelected([epNum, item.id])) || (all || (doEpsFilter.isSelected([epNum, item.id])) && !but))) { if (Object.prototype.hasOwnProperty.call(ret, key)) { From 5c751eb5515b53b78ebbf6fd60192b360feaa8f3 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 7 Jan 2024 22:07:24 -0800 Subject: [PATCH 15/69] switch to yao-pkg --- .github/workflows/auto-documentation.yml | 2 +- modules/build.ts | 2 +- package.json | 2 +- pnpm-lock.yaml | 246 ++++++++++------------- 4 files changed, 108 insertions(+), 144 deletions(-) diff --git a/.github/workflows/auto-documentation.yml b/.github/workflows/auto-documentation.yml index 2033c98..d969840 100644 --- a/.github/workflows/auto-documentation.yml +++ b/.github/workflows/auto-documentation.yml @@ -17,7 +17,7 @@ jobs: - uses: pnpm/action-setup@v2 with: version: 8.6.6 - - name: Use Node.js 16 + - name: Use Node.js uses: actions/setup-node@v3 with: node-version: 18 diff --git a/modules/build.ts b/modules/build.ts index e25a9b0..bd6681a 100644 --- a/modules/build.ts +++ b/modules/build.ts @@ -2,7 +2,7 @@ import fs from 'fs-extra'; import pkg from '../package.json'; import modulesCleanup from 'removeNPMAbsolutePaths'; -import { exec } from 'pkg'; +import { exec } from '@yao-pkg/pkg'; import { execSync } from 'child_process'; import { console } from './log'; diff --git a/package.json b/package.json index 44c3c75..61443ce 100644 --- a/package.json +++ b/package.json @@ -81,11 +81,11 @@ "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@vercel/webpack-asset-relocator-loader": "^1.7.3", + "@yao-pkg/pkg": "^5.11.1", "eslint": "^8.45.0", "eslint-config-react-app": "^7.0.1", "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "7.32.2", - "pkg": "^5.8.1", "protoc": "^1.1.3", "removeNPMAbsolutePaths": "^3.0.1", "ts-node": "^10.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a292b25..35a4081 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,9 @@ devDependencies: '@vercel/webpack-asset-relocator-loader': specifier: ^1.7.3 version: 1.7.3 + '@yao-pkg/pkg': + specifier: ^5.11.1 + version: 5.11.1 eslint: specifier: ^8.45.0 version: 8.45.0 @@ -127,9 +130,6 @@ devDependencies: eslint-plugin-react: specifier: 7.32.2 version: 7.32.2(eslint@8.45.0) - pkg: - specifier: ^5.8.1 - version: 5.8.1 protoc: specifier: ^1.1.3 version: 1.1.3 @@ -214,25 +214,6 @@ packages: semver: 6.3.1 dev: true - /@babel/generator@7.18.2: - resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - '@jridgewell/gen-mapping': 0.3.3 - jsesc: 2.5.2 - dev: true - - /@babel/generator@7.21.4: - resolution: {integrity: sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - jsesc: 2.5.2 - dev: true - /@babel/generator@7.22.9: resolution: {integrity: sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==} engines: {node: '>=6.9.0'} @@ -242,11 +223,21 @@ packages: '@jridgewell/trace-mapping': 0.3.18 jsesc: 2.5.2 + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + jsesc: 2.5.2 + dev: true + /@babel/helper-annotate-as-pure@7.18.6: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-annotate-as-pure@7.22.5: @@ -260,7 +251,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-compilation-targets@7.21.4(@babel/core@7.22.9): @@ -349,7 +340,7 @@ packages: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-function-name@7.21.0: @@ -357,7 +348,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-function-name@7.22.5: @@ -371,7 +362,7 @@ packages: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-hoist-variables@7.22.5: @@ -384,14 +375,14 @@ packages: resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-module-imports@7.21.4: resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-module-imports@7.22.5: @@ -408,10 +399,10 @@ packages: '@babel/helper-module-imports': 7.21.4 '@babel/helper-simple-access': 7.20.2 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 '@babel/template': 7.20.7 '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -433,7 +424,7 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-plugin-utils@7.20.2: @@ -455,7 +446,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -469,7 +460,7 @@ packages: '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.20.7 '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -478,7 +469,7 @@ packages: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-simple-access@7.22.5: @@ -491,14 +482,14 @@ packages: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 dev: true /@babel/helper-split-export-declaration@7.22.6: @@ -507,17 +498,12 @@ packages: dependencies: '@babel/types': 7.22.5 - /@babel/helper-string-parser@7.19.4: - resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.19.1: - resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} dev: true @@ -541,7 +527,7 @@ packages: '@babel/helper-function-name': 7.21.0 '@babel/template': 7.20.7 '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -560,7 +546,7 @@ packages: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 chalk: 2.4.2 js-tokens: 4.0.0 dev: true @@ -573,22 +559,6 @@ packages: chalk: 2.4.2 js-tokens: 4.0.0 - /@babel/parser@7.18.4: - resolution: {integrity: sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.21.4 - dev: true - - /@babel/parser@7.21.4: - resolution: {integrity: sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.21.4 - dev: true - /@babel/parser@7.22.7: resolution: {integrity: sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==} engines: {node: '>=6.0.0'} @@ -596,6 +566,14 @@ packages: dependencies: '@babel/types': 7.22.5 + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.0 + dev: true + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.22.9): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} @@ -1212,7 +1190,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -1529,7 +1507,7 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.22.9) '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.22.9) '@babel/preset-modules': 0.1.5(@babel/core@7.22.9) - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.22.9) babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.22.9) babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.22.9) @@ -1548,7 +1526,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.22.9) '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.22.9) - '@babel/types': 7.21.4 + '@babel/types': 7.22.5 esutils: 2.0.3 dev: true @@ -1598,8 +1576,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 + '@babel/parser': 7.22.7 + '@babel/types': 7.22.5 dev: true /@babel/template@7.22.5: @@ -1615,13 +1593,13 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.4 + '@babel/generator': 7.22.9 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.21.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 + '@babel/parser': 7.22.7 + '@babel/types': 7.22.5 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: @@ -1645,24 +1623,6 @@ packages: transitivePeerDependencies: - supports-color - /@babel/types@7.19.0: - resolution: {integrity: sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.19.4 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - dev: true - - /@babel/types@7.21.4: - resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.19.4 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - dev: true - /@babel/types@7.22.5: resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} engines: {node: '>=6.9.0'} @@ -1671,6 +1631,15 @@ packages: '@babel/helper-validator-identifier': 7.22.5 to-fast-properties: 2.0.0 + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -2303,6 +2272,46 @@ packages: engines: {node: '>=10.0.0'} dev: false + /@yao-pkg/pkg-fetch@3.5.7: + resolution: {integrity: sha512-DhuvjBZsdrUrkXC+eYZljxCxZ8QrjPQRGhJEA8+hUsKhmPyg+FUn6ebOfN6B7ioiJg7GsVpUJt57hALBCj9epA==} + hasBin: true + dependencies: + chalk: 4.1.2 + fs-extra: 9.1.0 + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + progress: 2.0.3 + semver: 7.5.4 + tar-fs: 2.1.1 + yargs: 16.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@yao-pkg/pkg@5.11.1: + resolution: {integrity: sha512-y++Kd/kMZcp0UeI2GWB9+55WsDqZp3XCjHGzC4Nsao0tlGjhnNOB+TPhZhLTOCL1V4ApaKkH8zzPcpaTCtvM8g==} + hasBin: true + dependencies: + '@babel/generator': 7.23.0 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + '@yao-pkg/pkg-fetch': 3.5.7 + chalk: 4.1.2 + fs-extra: 9.1.0 + globby: 11.1.0 + into-stream: 6.0.0 + is-core-module: 2.9.0 + minimist: 1.2.8 + multistream: 4.1.0 + prebuild-install: 7.1.1 + resolve: 1.22.2 + stream-meter: 1.0.4 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -3008,8 +3017,8 @@ packages: engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dev: false - /detect-libc@2.0.1: - resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} dev: true @@ -4549,8 +4558,8 @@ packages: engines: {node: '>= 0.6'} dev: false - /node-abi@3.35.0: - resolution: {integrity: sha512-jAlSOFR1Bls963NmFwxeQkNTzqjUF0NThm8Le7eRIRGzFUVJuMOFZDLv5Y30W/Oaw+KEebEJLAigwO9gQHoEmw==} + /node-abi@3.54.0: + resolution: {integrity: sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==} engines: {node: '>=10'} dependencies: semver: 7.5.4 @@ -4561,8 +4570,8 @@ packages: engines: {node: '>=10.5.0'} dev: true - /node-fetch@2.6.9: - resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 @@ -4779,63 +4788,18 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - /pkg-fetch@3.4.2: - resolution: {integrity: sha512-0+uijmzYcnhC0hStDjm/cl2VYdrmVVBpe7Q8k9YBojxmR5tG8mvR9/nooQq3QSXiQqORDVOTY3XqMEqJVIzkHA==} - hasBin: true - dependencies: - chalk: 4.1.2 - fs-extra: 9.1.0 - https-proxy-agent: 5.0.1 - node-fetch: 2.6.9 - progress: 2.0.3 - semver: 7.5.4 - tar-fs: 2.1.1 - yargs: 16.2.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - - /pkg@5.8.1: - resolution: {integrity: sha512-CjBWtFStCfIiT4Bde9QpJy0KeH19jCfwZRJqHFDFXfhUklCx8JoFmMj3wgnEYIwGmZVNkhsStPHEOnrtrQhEXA==} - hasBin: true - peerDependencies: - node-notifier: '>=9.0.1' - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@babel/generator': 7.18.2 - '@babel/parser': 7.18.4 - '@babel/types': 7.19.0 - chalk: 4.1.2 - fs-extra: 9.1.0 - globby: 11.1.0 - into-stream: 6.0.0 - is-core-module: 2.9.0 - minimist: 1.2.8 - multistream: 4.1.0 - pkg-fetch: 3.4.2 - prebuild-install: 7.1.1 - resolve: 1.22.2 - stream-meter: 1.0.4 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /prebuild-install@7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} engines: {node: '>=10'} hasBin: true dependencies: - detect-libc: 2.0.1 + detect-libc: 2.0.2 expand-template: 2.0.3 github-from-package: 0.0.0 minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 1.0.2 - node-abi: 3.35.0 + node-abi: 3.54.0 pump: 3.0.0 rc: 1.2.8 simple-get: 4.0.1 From e1c5ad2f2a7324b776ac617048b35f6e37317dc5 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 9 Jan 2024 08:32:34 -0800 Subject: [PATCH 16/69] Hotfix for CR Fixes authentication in CR Fixes #573 --- modules/module.api-urls.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/module.api-urls.ts b/modules/module.api-urls.ts index fd2d8a3..603f25b 100644 --- a/modules/module.api-urls.ts +++ b/modules/module.api-urls.ts @@ -61,7 +61,7 @@ const api: APIType = { // beta api beta_auth: `${domain.api_beta}/auth/v1/token`, beta_authBasic: 'Basic bm9haWhkZXZtXzZpeWcwYThsMHE6', - beta_authBasicMob: 'Basic YTZ5eGxvYW04c2VqaThsZDhldnc6aFQ3d2FjWHhNaURJcDhSNE9kekJybWVoQUtLTEVKUEE=', + beta_authBasicMob: 'Basic b2VkYXJteHN0bGgxanZhd2ltbnE6OWxFaHZIWkpEMzJqdVY1ZFc5Vk9TNTdkb3BkSnBnbzE=', beta_profile: `${domain.api_beta}/accounts/v1/me/profile`, beta_cmsToken: `${domain.api_beta}/index/v2`, search: `${domain.api_beta}/content/v2/discover/search`, From 5ab4d3e0c4a24a49caf7b9e08014bafa2a35db3d Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 9 Jan 2024 08:46:49 -0800 Subject: [PATCH 17/69] Add new CR languages --- modules/module.langsData.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/module.langsData.ts b/modules/module.langsData.ts index 565d8d9..4749986 100644 --- a/modules/module.langsData.ts +++ b/modules/module.langsData.ts @@ -34,9 +34,11 @@ const languages: LanguageItem[] = [ { cr_locale: 'ko-KR', hd_locale: 'Korean', locale: 'ko', code: 'kor', name: 'Korean' }, { cr_locale: 'ca-ES', locale: 'ca-ES', code: 'cat', name: 'Catalan' }, { cr_locale: 'pl-PL', locale: 'pl-PL', code: 'pol', name: 'Polish' }, - { cr_locale: 'th-TH', locale: 'th-TH', code: 'tha', name: 'Thai' }, - { cr_locale: 'ta-IN', locale: 'ta-IN', code: 'tam', name: 'Tamil (India)' }, - { cr_locale: 'ms-MY', locale: 'ms-MY', code: 'may', name: 'Malay (Malaysia)' }, + { cr_locale: 'th-TH', locale: 'th-TH', code: 'tha', name: 'Thai', language: 'ไทย' }, + { cr_locale: 'ta-IN', locale: 'ta-IN', code: 'tam', name: 'Tamil (India)', language: 'தமிழ்' }, + { cr_locale: 'ms-MY', locale: 'ms-MY', code: 'may', name: 'Malay (Malaysia)', language: 'Bahasa Melayu' }, + { cr_locale: 'vi-VN', locale: 'vi-VN', code: 'vie', name: 'Vietnamese', language: 'Tiếng Việt', }, + { cr_locale: 'id-ID', locale: 'id-ID', code: 'ind', name: 'Indonesian', language: 'Bahasa Indonesia', }, { cr_locale: 'ja-JP', hd_locale: 'Japanese', funi_locale: 'jaJP', locale: 'ja', code: 'jpn', name: 'Japanese' }, ]; From acc91a0eeddea314f94285af25dcc10eb5e4752f Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 9 Jan 2024 12:33:04 -0800 Subject: [PATCH 18/69] Fixed fallback request --- crunchy.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crunchy.ts b/crunchy.ts index e258299..8da2043 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1192,6 +1192,20 @@ export default class Crunchy implements ServiceClass { let playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders); if(!playbackReq.ok || !playbackReq.res){ console.error('Request Stream URLs FAILED! Attempting fallback'); + + const videoStreamsReq = [ + domain.api_beta, + mMeta.playback, + '?', + new URLSearchParams({ + 'preferred_audio_language': 'ja-JP', + streams: 'all', + textType: 'all', + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); playbackReq = await this.req.getData(videoStreamsReq as string, AuthHeaders); if(!playbackReq.ok || !playbackReq.res){ console.error('Fallback Request Stream URLs FAILED!'); From 0e91c59197f653926264cf1fcc259d4e9f73d244 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Wed, 10 Jan 2024 11:18:40 -0800 Subject: [PATCH 19/69] Add Telugu (India) Fixes #567 --- modules/module.langsData.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/module.langsData.ts b/modules/module.langsData.ts index 4749986..c4ffbf1 100644 --- a/modules/module.langsData.ts +++ b/modules/module.langsData.ts @@ -37,8 +37,9 @@ const languages: LanguageItem[] = [ { cr_locale: 'th-TH', locale: 'th-TH', code: 'tha', name: 'Thai', language: 'ไทย' }, { cr_locale: 'ta-IN', locale: 'ta-IN', code: 'tam', name: 'Tamil (India)', language: 'தமிழ்' }, { cr_locale: 'ms-MY', locale: 'ms-MY', code: 'may', name: 'Malay (Malaysia)', language: 'Bahasa Melayu' }, - { cr_locale: 'vi-VN', locale: 'vi-VN', code: 'vie', name: 'Vietnamese', language: 'Tiếng Việt', }, - { cr_locale: 'id-ID', locale: 'id-ID', code: 'ind', name: 'Indonesian', language: 'Bahasa Indonesia', }, + { cr_locale: 'vi-VN', locale: 'vi-VN', code: 'vie', name: 'Vietnamese', language: 'Tiếng Việt' }, + { cr_locale: 'id-ID', locale: 'id-ID', code: 'ind', name: 'Indonesian', language: 'Bahasa Indonesia' }, + { cr_locale: 'te-IN', locale: 'te-IN', code: 'tel', name: 'Telugu (India)', language: 'తెలుగు' }, { cr_locale: 'ja-JP', hd_locale: 'Japanese', funi_locale: 'jaJP', locale: 'ja', code: 'jpn', name: 'Japanese' }, ]; From fe997ecb321664d737c78301b11b232f75c5849b Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Thu, 11 Jan 2024 11:11:50 -0800 Subject: [PATCH 20/69] Fix edge case with vtt2ass --- modules/module.vtt2ass.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/module.vtt2ass.ts b/modules/module.vtt2ass.ts index 8982809..a661fa3 100644 --- a/modules/module.vtt2ass.ts +++ b/modules/module.vtt2ass.ts @@ -208,7 +208,7 @@ function loadVTT(vttStr: string): Vtt[] { time: { start: m[1], end: m[2], - ext: m[3].split(' ').map(x => x.split(':')).reduce((p, c) => ((p as any)[c[0]] = c[1]) && p, {}), + ext: m[3].split(' ').map(x => x.split(':')).reduce((p, c) => ((p as any)[c[0]] = c[1] ?? 'invalid-input') && p, {}), } }; lineBuf = []; @@ -312,20 +312,20 @@ function convertLine(css: Record, l: Record) { const end = convertTime(l.time.end); const txt = convertText(l.text); let type = txt.style.match(/Caption/i) ? 'caption' : (txt.style.match(/SongCap/i) ? 'song_cap' : 'subtitle'); - type = type == 'caption' && l.time.ext.position !== undefined ? 'capt_pos' : type; - if (l.time.ext.align === 'left') { + type = type == 'caption' && l.time.ext?.position !== undefined ? 'capt_pos' : type; + if (l.time.ext?.align === 'left') { txt.text = `{\\an7}${txt.text}`; } let ind = '', subInd = 1; const sMinus = 0; // (19.2 * 2); - if (l.time.ext.position !== undefined) { + if (l.time.ext?.position !== undefined) { const pos = parseInt(l.time.ext.position); const PosX = pos < 0 ? (1280 / 100 * (100 - pos)) : ((1280 - sMinus) / 100 * pos); const line = parseInt(l.time.ext.line) || 0; const PosY = line < 0 ? (720 / 100 * (100 - line)) : ((720 - sMinus) / 100 * line); txt.text = `{\\pos(${parseFloat(PosX.toFixed(3))},${parseFloat(PosY.toFixed(3))})}${txt.text}`; } - else if (l.time.ext.line !== undefined && type == 'caption') { + else if (l.time.ext?.line !== undefined && type == 'caption') { const line = parseInt(l.time.ext.line); const PosY = line < 0 ? (720 / 100 * (100 - line)) : ((720 - sMinus) / 100 * line); txt.text = `{\\pos(640,${parseFloat(PosY.toFixed(3))})}${txt.text}`; From f204b068ea1595bb6a7ae89c884faa4b538e7ebd Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Fri, 26 Jan 2024 10:38:25 -0800 Subject: [PATCH 21/69] Only show DRM streams if they can be decrypted --- crunchy.ts | 10 +++++++--- modules/cr_widevine.ts | 13 ++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index 8da2043..6cfb022 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -18,7 +18,7 @@ import * as langsData from './modules/module.langsData'; import * as yamlCfg from './modules/module.cfg-loader'; import * as yargs from './modules/module.app-args'; import Merger, { Font, MergerInput, SubtitleInput } from './modules/module.merger'; -import getKeys from './modules/cr_widevine'; +import getKeys, { canDecrypt } from './modules/cr_widevine'; // args @@ -1260,8 +1260,12 @@ export default class Crunchy implements ServiceClass { const pbStreams = pbData.data[0]; for(const s of Object.keys(pbStreams)){ - //if(s.match(/hls/) && !s.match(/drm/) && !s.match(/trailer/)) { - if((s.match(/hls/) || s.match(/dash/)) && !(s.match(/hls/) && s.match(/drm/)) && !s.match(/trailer/)) { + if ( + (s.match(/hls/) || s.match(/dash/)) + && !(s.match(/hls/) && s.match(/drm/)) + && !((!canDecrypt || !this.cfg.bin.mp4decrypt) && s.match(/drm/)) + && !s.match(/trailer/) + ) { const pb = Object.values(pbStreams[s]).map(v => { v.hardsub_lang = v.hardsub_locale ? langsData.fixAndFindCrLC(v.hardsub_locale).locale diff --git a/modules/cr_widevine.ts b/modules/cr_widevine.ts index 63eb5b9..5f54f01 100644 --- a/modules/cr_widevine.ts +++ b/modules/cr_widevine.ts @@ -6,11 +6,18 @@ import { workingDir } from './module.cfg-loader'; import path from 'path'; //read cdm files located in the same directory -const privateKey = fs.readFileSync(path.join(workingDir, 'widevine', 'device_private_key')); -const identifierBlob = fs.readFileSync(path.join(workingDir, 'widevine', 'device_client_id_blob')); +let privateKey: Buffer, identifierBlob: Buffer; +export let canDecrypt: boolean; +try { + privateKey = fs.readFileSync(path.join(workingDir, 'widevine', 'device_private_key')); + identifierBlob = fs.readFileSync(path.join(workingDir, 'widevine', 'device_client_id_blob')); + canDecrypt = true; +} catch (e) { + canDecrypt = false; +} export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record): Promise { - if (!pssh) return []; + if (!pssh || !canDecrypt) return []; //pssh found in the mpd manifest const psshBuffer = Buffer.from( pssh, From 615cef700aef2bde8ad713dda3cc0518d53d148c Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 28 Jan 2024 09:46:15 -0800 Subject: [PATCH 22/69] Fix issue with GUI decrypting --- config/bin-path.yml | 2 +- crunchy.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config/bin-path.yml b/config/bin-path.yml index 0c948ef..8c08eb7 100644 --- a/config/bin-path.yml +++ b/config/bin-path.yml @@ -1,4 +1,4 @@ ffmpeg: "ffmpeg.exe" mkvmerge: "mkvmerge.exe" ffprobe: "ffprobe.exe" -mp4decrypt: "./bin/mp4decrypt" +mp4decrypt: "mp4decrypt.exe" diff --git a/crunchy.ts b/crunchy.ts index 6cfb022..c178188 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1123,6 +1123,9 @@ export default class Crunchy implements ServiceClass { return; } + if (!this.cfg.bin.ffmpeg) + this.cfg.bin = await yamlCfg.loadBinCfg(); + let mediaName = '...'; let fileName; const variables: Variable[] = []; From 1b5dfa0f3ea498fb8acec84710ea960ee2419852 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 29 Jan 2024 18:16:14 -0800 Subject: [PATCH 23/69] Fix command line length issue Fixes long standing issue on windows where if the command line length was over 8191 characters, this was achieved by switching node.exec to use powershell.exe instead of cmd.exe on Windows --- modules/sei-helper-fixes.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/sei-helper-fixes.ts b/modules/sei-helper-fixes.ts index 4a6d6eb..8545b84 100644 --- a/modules/sei-helper-fixes.ts +++ b/modules/sei-helper-fixes.ts @@ -10,7 +10,11 @@ const exec = (pname: string, fpath: string, pargs: string, spc = false): { pargs = pargs ? ' ' + pargs : ''; console.info(`\n> "${pname}"${pargs}${spc ? '\n' : ''}`); try { - childProcess.execSync((fpath + pargs), { stdio: 'inherit' }); + if (process.platform === 'win32') { + childProcess.execSync(('& ' + fpath + pargs), { stdio: 'inherit', 'shell': 'powershell.exe', 'windowsHide': true }); + } else { + childProcess.execSync((fpath + pargs), { stdio: 'inherit' }); + } return { isOk: true }; From 169d48aa47b9c13e8a1f5940fddd54e6b5d336b4 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 29 Jan 2024 20:29:42 -0800 Subject: [PATCH 24/69] make --nocleanup work for encrypted files --- @types/crunchyTypes.d.ts | 1 + crunchy.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 4252a46..504cbc5 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -31,6 +31,7 @@ export type CrunchyDownloadOptions = { dlVideoOnce: boolean, skipmux?: boolean, syncTiming: boolean, + nocleanup: boolean, apiType: 'web' | 'android' } diff --git a/crunchy.ts b/crunchy.ts index c178188..cb49aca 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1583,7 +1583,9 @@ export default class Crunchy implements ServiceClass { return undefined; } else { console.info('Decryption done for video'); - fs.removeSync(`${tsFile}.video.enc.ts`); + if (!options.nocleanup) { + fs.removeSync(`${tsFile}.video.enc.ts`); + } files.push({ type: 'Video', path: `${tsFile}.video.ts`, @@ -1601,7 +1603,9 @@ export default class Crunchy implements ServiceClass { console.error(`Decryption failed with exit code ${decryptAudio.err.code}`); return undefined; } else { - fs.removeSync(`${tsFile}.audio.enc.ts`); + if (!options.nocleanup) { + fs.removeSync(`${tsFile}.audio.enc.ts`); + } files.push({ type: 'Audio', path: `${tsFile}.audio.ts`, From 388204cc43209dbf6ecfcc5c9b5797c45dc828cd Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 29 Jan 2024 21:31:23 -0800 Subject: [PATCH 25/69] Ignore guistate.json --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cc9d16b..2a83dc4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ hd_profile.yml hd_sess.yml hd_token.yml archive.json +guistate.json fonts .webpack/ out/ From ec8d8b3ab808445c464e445348ed10fee5f9f3ef Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 29 Jan 2024 21:31:28 -0800 Subject: [PATCH 26/69] Formatting fix --- config/gui.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/gui.yml b/config/gui.yml index b11e0a7..87c3f5a 100644 --- a/config/gui.yml +++ b/config/gui.yml @@ -1 +1 @@ -port: 3000 \ No newline at end of file +port: 3000 From 7be22ec132881302d9b3cc92fcfe6595bbd20fd1 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 29 Jan 2024 22:45:36 -0800 Subject: [PATCH 27/69] [CR] Fixes subtitles when directory doesn't exist Now creates the directory before making the files, similar to how videos work. --- crunchy.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crunchy.ts b/crunchy.ts index cb49aca..e03c95c 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1832,6 +1832,12 @@ export default class Crunchy implements ServiceClass { const isCC = langItem.code === audDub; sxData.file = langsData.subsFile(fileName as string, subsIndex, langItem, isCC, options.ccTag); sxData.path = path.join(this.cfg.dir.content, sxData.file); + const split = sxData.path.split(path.sep).slice(0, -1); + split.forEach((val, ind, arr) => { + const isAbsolut = path.isAbsolute(sxData.path as string); + if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) + fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); + }); if (files.some(a => a.type === 'Subtitle' && (a.language.cr_locale == langItem.cr_locale || a.language.locale == langItem.locale) && a.cc === isCC)) continue; if(options.dlsubs.includes('all') || options.dlsubs.includes(langItem.locale)){ From 02620ec5b55eb3a361748974316674356f5c7842 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 29 Jan 2024 22:47:56 -0800 Subject: [PATCH 28/69] [CR] Initial commit to support chapters TODO: 1) Add flag for chapters 2) Add ffmpeg merging for chapters 3) Add fallback to old CR API --- @types/crunchyChapters.d.ts | 16 +++++++ @types/crunchyTypes.d.ts | 4 ++ crunchy.ts | 91 ++++++++++++++++++++++++++++++++++++- modules/module.merger.ts | 12 ++++- 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 @types/crunchyChapters.d.ts diff --git a/@types/crunchyChapters.d.ts b/@types/crunchyChapters.d.ts new file mode 100644 index 0000000..6a4ad37 --- /dev/null +++ b/@types/crunchyChapters.d.ts @@ -0,0 +1,16 @@ +export interface CrunchyChapters { + [key: string]: CrunchyChapter; + lastUpdate: Date; + mediaId: string; +} + +export interface CrunchyChapter { + approverId: string; + distributionNumber: string; + end: number; + start: number; + title: string; + seriesId: string; + new: boolean; + type: string; +} diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 504cbc5..cd96708 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -90,6 +90,10 @@ export type DownloadedMedia = { lang: LanguageItem, path: string, isPrimary?: boolean +} | { + type: 'Chapters', + lang: LanguageItem, + path: string } | ({ type: 'Subtitle', cc: boolean diff --git a/crunchy.ts b/crunchy.ts index e03c95c..3b9e846 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -40,6 +40,7 @@ import { CrunchyAndroidStreams } from './@types/crunchyAndroidStreams'; import { CrunchyAndroidEpisodes } from './@types/crunchyAndroidEpisodes'; import { parse } from './modules/module.transform-mpd'; import { CrunchyAndroidObject } from './@types/crunchyAndroidObject'; +import { CrunchyChapters, CrunchyChapter } from './@types/crunchyChapters'; export type sxItem = { language: langsData.LanguageItem, @@ -1174,7 +1175,55 @@ export default class Crunchy implements ServiceClass { if (mediaId.includes(':')) mediaId = mediaId.split(':')[1]; - // /cms/v2/BUCKET/crunchyroll/videos/MEDIAID/streams + const chapterRequest = await this.req.getData(`https://static.crunchyroll.com/skip-events/production/${mMeta.mediaId}.json`); + let chapterData: CrunchyChapters; + const compiledChapters: string[] = []; + if(!chapterRequest.ok || !chapterRequest.res){ + console.warn('Chapter request failed'); + } else { + chapterData = JSON.parse(chapterRequest.res.body); + const chapters: CrunchyChapter[] = []; + for (const chapter in chapterData) { + if (typeof chapterData[chapter] == 'object') { + chapters.push(chapterData[chapter]); + } + } + if (chapters.length > 0) { + chapters.sort((a, b) => a.start - b.start); + for (const chapter of chapters) { + const startTime = new Date(0), endTime = new Date(0); + startTime.setSeconds(chapter.start); + endTime.setSeconds(chapter.end); + const startFormatted = startTime.toISOString().substring(11, 19)+'.00'; + const endFormatted = endTime.toISOString().substring(11, 19)+'.00'; + if (chapter.type == 'intro') { + if (chapter.start > 0) { + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=00:00:00.00`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Prologue` + ); + } + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${startFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Opening` + ); + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${endFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Episode` + ); + } else { + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${startFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=${chapter.type.charAt(0).toUpperCase() + chapter.type.slice(1)} Start` + ); + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${endFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=${chapter.type.charAt(0).toUpperCase() + chapter.type.slice(1)} End` + ); + } + } + } + } let pbData = { total: 0, data: {}, meta: {} } as PlaybackData; if (options.apiType == 'android') { @@ -1797,6 +1846,32 @@ export default class Crunchy implements ServiceClass { console.info('Downloading skipped!'); } + if (compiledChapters.length > 0) { + try { + fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep); + const outFile = parseFileName(options.fileName + '.' + mMeta.lang?.name, variables, options.numbers, options.override).join(path.sep); + tsFile = path.isAbsolute(outFile as string) ? outFile : path.join(this.cfg.dir.content, outFile); + const split = outFile.split(path.sep).slice(0, -1); + split.forEach((val, ind, arr) => { + const isAbsolut = path.isAbsolute(outFile as string); + if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) + fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); + }); + const lang = langsData.languages.find(a => a.code === curStream?.audio_lang); + if (!lang) { + console.error(`Unable to find language for code ${curStream.audio_lang}`); + return; + } + fs.writeFileSync(`${tsFile}.txt`, compiledChapters.join('\r\n')); + files.push({ + path: `${tsFile}.txt`, + lang: lang, + type: 'Chapters' + }); + } catch { + console.error('Failed to write chapter file'); + } + } if(options.dlsubs.indexOf('all') > -1){ options.dlsubs = ['all']; @@ -1912,6 +1987,8 @@ export default class Crunchy implements ServiceClass { throw new Error('Never'); if (a.type === 'Audio') throw new Error('Never'); + if (a.type === 'Chapters') + throw new Error('Never'); return { file: a.path, language: a.language, @@ -1929,6 +2006,18 @@ export default class Crunchy implements ServiceClass { path: a.path, }; }), + chapters: data.filter(a => a.type === 'Chapters').map((a) : MergerInput => { + if (a.type === 'Video') + throw new Error('Never'); + if (a.type === 'Audio') + throw new Error('Never'); + if (a.type === 'Subtitle') + throw new Error('Never'); + return { + path: a.path, + lang: a.lang + }; + }), videoTitle: options.videoTitle, options: { ffmpeg: options.ffmpegOptions, diff --git a/modules/module.merger.ts b/modules/module.merger.ts index 5cf98f5..989667f 100644 --- a/modules/module.merger.ts +++ b/modules/module.merger.ts @@ -37,6 +37,7 @@ export type MergerOptions = { onlyVid: MergerInput[], onlyAudio: MergerInput[], subtitles: SubtitleInput[], + chapters?: MergerInput[], ccTag: string, output: string, videoTitle?: string, @@ -162,7 +163,7 @@ class Merger { args.push(`-i "${sub.file}"`); } - if (this.options.output.split('.').pop() === 'mkv') + if (this.options.output.split('.').pop() === 'mkv') { if (this.options.fonts) { let fontIndex = 0; for (const font of this.options.fonts) { @@ -170,6 +171,9 @@ class Merger { fontIndex++; } } + } + + //TODO: Make it possible for chapters to work with ffmpeg merging args.push(...metaData); args.push(...this.options.subtitles.map((_, subIndex) => `-map ${subIndex + index}`)); @@ -296,6 +300,7 @@ class Merger { '--no-subtitles', ); } + if (this.options.fonts && this.options.fonts.length > 0) { for (const f of this.options.fonts) { args.push('--attachment-name', f.name); @@ -308,6 +313,10 @@ class Merger { ); } + if (this.options.chapters && this.options.chapters.length > 0) { + args.push(`--chapters "${this.options.chapters[0].path}"`); + } + return args.join(' '); }; @@ -405,6 +414,7 @@ class Merger { public cleanUp() { this.options.onlyAudio.concat(this.options.onlyVid).concat(this.options.videoAndAudio).forEach(a => fs.unlinkSync(a.path)); + this.options.chapters?.forEach(a => fs.unlinkSync(a.path)); this.options.subtitles.forEach(a => fs.unlinkSync(a.file)); } From 3502141c622adbcefe7fc9d9632f72220336355a Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 30 Jan 2024 10:06:40 -0800 Subject: [PATCH 29/69] [CR] Improve chapter support If new chapter request fails, use old request. Also adds new option to enable chapter downloading, `--chapters` --- @types/crunchyChapters.d.ts | 10 +++ @types/crunchyTypes.d.ts | 1 + crunchy.ts | 124 ++++++++++++++++++++++++------------ modules/module.app-args.ts | 1 + modules/module.args.ts | 13 ++++ 5 files changed, 108 insertions(+), 41 deletions(-) diff --git a/@types/crunchyChapters.d.ts b/@types/crunchyChapters.d.ts index 6a4ad37..5aafdb3 100644 --- a/@types/crunchyChapters.d.ts +++ b/@types/crunchyChapters.d.ts @@ -14,3 +14,13 @@ export interface CrunchyChapter { new: boolean; type: string; } + +export interface CrunchyOldChapter { + media_id: string; + startTime: number; + endTime: number; + duration: number; + comparedWith: string; + ordering: string; + last_updated: Date; +} \ No newline at end of file diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index cd96708..d4a42f0 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -32,6 +32,7 @@ export type CrunchyDownloadOptions = { skipmux?: boolean, syncTiming: boolean, nocleanup: boolean, + chapters: boolean, apiType: 'web' | 'android' } diff --git a/crunchy.ts b/crunchy.ts index 3b9e846..772f15c 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -40,7 +40,7 @@ import { CrunchyAndroidStreams } from './@types/crunchyAndroidStreams'; import { CrunchyAndroidEpisodes } from './@types/crunchyAndroidEpisodes'; import { parse } from './modules/module.transform-mpd'; import { CrunchyAndroidObject } from './@types/crunchyAndroidObject'; -import { CrunchyChapters, CrunchyChapter } from './@types/crunchyChapters'; +import { CrunchyChapters, CrunchyChapter, CrunchyOldChapter } from './@types/crunchyChapters'; export type sxItem = { language: langsData.LanguageItem, @@ -1175,51 +1175,93 @@ export default class Crunchy implements ServiceClass { if (mediaId.includes(':')) mediaId = mediaId.split(':')[1]; - const chapterRequest = await this.req.getData(`https://static.crunchyroll.com/skip-events/production/${mMeta.mediaId}.json`); - let chapterData: CrunchyChapters; const compiledChapters: string[] = []; - if(!chapterRequest.ok || !chapterRequest.res){ - console.warn('Chapter request failed'); - } else { - chapterData = JSON.parse(chapterRequest.res.body); - const chapters: CrunchyChapter[] = []; - for (const chapter in chapterData) { - if (typeof chapterData[chapter] == 'object') { - chapters.push(chapterData[chapter]); - } - } - if (chapters.length > 0) { - chapters.sort((a, b) => a.start - b.start); - for (const chapter of chapters) { + if (options.chapters) { + //Make Chapter Request + const chapterRequest = await this.req.getData(`https://static.crunchyroll.com/skip-events/production/${mMeta.mediaId}.json`); + if(!chapterRequest.ok || !chapterRequest.res){ + //Old Chapter Request Fallback + console.warn('Chapter request failed, attempting old API'); + const oldChapterRequest = await this.req.getData(`https://static.crunchyroll.com/datalab-intro-v2/${mMeta.mediaId}.json`); + if(!oldChapterRequest.ok || !oldChapterRequest.res) { + console.warn('Old Chapter API request failed'); + } else { + console.info('Old Chapter request successful'); + const chapterData = JSON.parse(oldChapterRequest.res.body) as CrunchyOldChapter; + + //Generate Timestamps const startTime = new Date(0), endTime = new Date(0); - startTime.setSeconds(chapter.start); - endTime.setSeconds(chapter.end); - const startFormatted = startTime.toISOString().substring(11, 19)+'.00'; - const endFormatted = endTime.toISOString().substring(11, 19)+'.00'; - if (chapter.type == 'intro') { - if (chapter.start > 0) { + startTime.setSeconds(chapterData.startTime); + endTime.setSeconds(chapterData.endTime); + const startFormatted = startTime.toISOString().substring(11, 19)+'.'+String(chapterData.startTime).split('.')[1]; + const endFormatted = endTime.toISOString().substring(11, 19)+'.'+String(chapterData.endTime).split('.')[1]; + + //Push Generated Chapters + if (chapterData.startTime > 1) { + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=00:00:00.00`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Prologue` + ); + } + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${startFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Opening` + ); + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${endFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Episode` + ); + } + } else { + //Chapter request succeeded, now let's parse them + console.info('Chapter request successful'); + const chapterData = JSON.parse(chapterRequest.res.body) as CrunchyChapters; + const chapters: CrunchyChapter[] = []; + + //Make a format more usable for the crunchy chapters + for (const chapter in chapterData) { + if (typeof chapterData[chapter] == 'object') { + chapters.push(chapterData[chapter]); + } + } + + if (chapters.length > 0) { + chapters.sort((a, b) => a.start - b.start); + //Loop through all the chapters + for (const chapter of chapters) { + //Generate timestamps + const startTime = new Date(0), endTime = new Date(0); + startTime.setSeconds(chapter.start); + endTime.setSeconds(chapter.end); + const startFormatted = startTime.toISOString().substring(11, 19)+'.00'; + const endFormatted = endTime.toISOString().substring(11, 19)+'.00'; + + //Push generated chapters + if (chapter.type == 'intro') { + if (chapter.start > 0) { + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=00:00:00.00`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Prologue` + ); + } compiledChapters.push( - `CHAPTER${(compiledChapters.length/2)+1}=00:00:00.00`, - `CHAPTER${(compiledChapters.length/2)+1}NAME=Prologue` + `CHAPTER${(compiledChapters.length/2)+1}=${startFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Opening` + ); + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${endFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=Episode` + ); + } else { + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${startFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=${chapter.type.charAt(0).toUpperCase() + chapter.type.slice(1)} Start` + ); + compiledChapters.push( + `CHAPTER${(compiledChapters.length/2)+1}=${endFormatted}`, + `CHAPTER${(compiledChapters.length/2)+1}NAME=${chapter.type.charAt(0).toUpperCase() + chapter.type.slice(1)} End` ); } - compiledChapters.push( - `CHAPTER${(compiledChapters.length/2)+1}=${startFormatted}`, - `CHAPTER${(compiledChapters.length/2)+1}NAME=Opening` - ); - compiledChapters.push( - `CHAPTER${(compiledChapters.length/2)+1}=${endFormatted}`, - `CHAPTER${(compiledChapters.length/2)+1}NAME=Episode` - ); - } else { - compiledChapters.push( - `CHAPTER${(compiledChapters.length/2)+1}=${startFormatted}`, - `CHAPTER${(compiledChapters.length/2)+1}NAME=${chapter.type.charAt(0).toUpperCase() + chapter.type.slice(1)} Start` - ); - compiledChapters.push( - `CHAPTER${(compiledChapters.length/2)+1}=${endFormatted}`, - `CHAPTER${(compiledChapters.length/2)+1}NAME=${chapter.type.charAt(0).toUpperCase() + chapter.type.slice(1)} End` - ); } } } diff --git a/modules/module.app-args.ts b/modules/module.app-args.ts index 8047cc1..a06d0ae 100644 --- a/modules/module.app-args.ts +++ b/modules/module.app-args.ts @@ -64,6 +64,7 @@ let argvC: { _: (string | number)[]; $0: string; dlVideoOnce: boolean; + chapters: boolean; crapi: 'android' | 'web'; removeBumpers: boolean; originalFontSize: boolean; diff --git a/modules/module.args.ts b/modules/module.args.ts index 07b728f..ad988e5 100644 --- a/modules/module.args.ts +++ b/modules/module.args.ts @@ -203,6 +203,19 @@ const args: TAppArg[] = [ default: false } }, + { + name: 'chapters', + describe: 'Will fetch the chapters and add them into the final video', + type: 'boolean', + group: 'dl', + service: ['crunchy'], + docDescribe: 'Will fetch the chapters and add them into the final video.' + + '\nCurrently only works with mkvmerge.', + usage: '', + default: { + default: false + } + }, { name: 'crapi', describe: 'Selects the API type for Crunchyroll', From b146ee71aef2986048f04c082fde7b874fafab56 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 30 Jan 2024 15:10:43 -0800 Subject: [PATCH 30/69] Linting fixes --- @types/crunchyTypes.d.ts | 4 ++-- crunchy.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index d4a42f0..a7c5a7a 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -33,7 +33,7 @@ export type CrunchyDownloadOptions = { syncTiming: boolean, nocleanup: boolean, chapters: boolean, - apiType: 'web' | 'android' + apiType?: 'web' | 'android' } export type CrunchyMultiDownload = { @@ -41,7 +41,7 @@ export type CrunchyMultiDownload = { all?: boolean, but?: boolean, e?: string, - crapi: 'web' | 'android' + crapi?: 'web' | 'android' } export type CrunchyMuxOptions = { diff --git a/crunchy.ts b/crunchy.ts index 772f15c..f836398 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -2092,7 +2092,7 @@ export default class Crunchy implements ServiceClass { merger.cleanUp(); } - public async listSeriesID(id: string, apiType: 'android' | 'web'): Promise<{ list: Episode[], data: Record}> { @@ -2351,7 +2351,7 @@ export default class Crunchy implements ServiceClass { return seasonsList; } - public async getSeasonDataById(item: SeriesSearchItem, apiType: 'android' | 'web', log = false){ + public async getSeasonDataById(item: SeriesSearchItem, apiType: 'android' | 'web' = 'android', log = false){ if(!this.cmsToken.cms){ console.error('Authentication required!'); return; From 27e02a85487578b0a2407a62354d9502fec7102e Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sun, 4 Feb 2024 15:55:15 -0800 Subject: [PATCH 31/69] Improve readme Adds instruction for DRM decryption, as well as some examples on how to use the CLI. It also splits up the dependencies as they are needed. --- docs/README.md | 60 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/docs/README.md b/docs/README.md index 046cadf..a80e8c1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,14 +8,10 @@ This downloader can download anime from different sites. Currently supported are This application is not endorsed by or affiliated with *Funimation*, *Crunchyroll*, or *Hidive*. This application enables you to download videos for offline viewing which may be forbidden by law in your country. The usage of this application may also cause a violation of the *Terms of Service* between you and the stream provider. This tool is not responsible for your actions; please make an informed decision before using this application. -## Prerequisites +## Dependencies -* NodeJS >= 14.6.0 (https://nodejs.org/) -* NPM >= 6.9.0 (https://www.npmjs.org/) -* PNPM >= 7.0.0 (https://pnpm.io/) * ffmpeg >= 4.0.0 (https://www.videohelp.com/software/ffmpeg) * MKVToolNix >= 60.0.0 (https://www.videohelp.com/software/MKVToolNix) -* mp4decrypt >= Any (http://www.bento4.com/) - Only required for decrypting ### Paths Configuration @@ -28,19 +24,49 @@ By default this application uses the following paths to programs (main executabl To change these paths you need to edit `bin-path.yml` in `./config/` directory. -### Node Modules +## CLI Information -After installing NodeJS with NPM go to directory with `package.json` file and type: `npm i`. Afterwards run `npm run tsc`. You can now find a lib folder containing the js code execute. +See [the documentation](https://github.com/anidl/multi-downloader-nx/blob/master/docs/DOCUMENTATION.md) for a complete list of what options are available. You can define defaults for the commands by editing the `cli-defaults.yml` file in the `./config/` directory. -* [check dependencies](https://david-dm.org/anidl/funimation-downloader-nx) +### Example usage -## CLI Options +#### Logging in -See [the documentation](https://github.com/anidl/multi-downloader-nx/blob/master/docs/DOCUMENTATION.md) +Most services require you to be logged in, in order to download from, an example of how you would login is: -## Build instructions +```shell +AniDL --service {ServiceName} --auth +``` -Please note that nodejs, npm, and pnpm must be installed in your system. For instructions on how to install pnpm, check (https://pnpm.io/installation) +#### Searching + +In order to find the IDs to download, you can search from each service by using the `--search` flag like this: + +```shell +AniDL --service {ServiceName} --search {SearchTerm} +``` + +#### Downloading + +Once you have the ID which you can obtain from using the search or other means, you are ready to download, which you can do like this: + +```shell +AniDL --service {ServiceName} -s {SeasonID} -e {EpisodeNumber} +``` + +## Building and running from source + +### Build Dependencies + +Dependencies that are only required for running from code. These are not required if you are using the prebuilt binaries. + +* NodeJS >= 14.6.0 (https://nodejs.org/) +* NPM >= 6.9.0 (https://www.npmjs.org/) +* PNPM >= 7.0.0 (https://pnpm.io/) + +### Build Instructions + +Please note that NodeJS, NPM, and PNPM must be installed on your system. For instructions on how to install pnpm, check (https://pnpm.io/installation) First clone this repo `git clone https://github.com/anidl/multi-downloader-nx.git`. @@ -50,3 +76,13 @@ Afterwards run `pnpm run tsc false [true if you want gui, false otherwise]`. If you want the `js` files you are done. Just `cd` into the `lib` folder, and run `node index.js --help` to get started with the CLI, or run `node gui.js` to run the GUI If you want to package the application, run pnpm run build-`{platform}`-`{type}` where `{platform}` is the operating system (currently the choices are windows, ubuntu, macos, and arm) and `{type}` is cli or gui. + +## DRM Decryption + +### Decryption Requirements + +* mp4decrypt >= Any (http://www.bento4.com/) - Only required for decrypting + +### Instructions + +In order to decrypt DRM content, you will need to have a dumped CDM, after that you will need to place the CDM files (`device_client_id_blob` and `device_private_key`) into the `./widevine/` directory. For legal reasons we do not include the CDM with the software, and you will have to source one yourself. From 103b17f4498a79826d0fa7d309a69abd7b6432ca Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 5 Feb 2024 10:12:47 -0800 Subject: [PATCH 32/69] Simplify API switching code Should also fix API switching on GUI --- @types/crunchyTypes.d.ts | 6 ++--- crunchy.ts | 37 ++++++++++++++++-------------- gui/server/services/crunchyroll.ts | 1 + 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index a7c5a7a..13ceee4 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -32,16 +32,14 @@ export type CrunchyDownloadOptions = { skipmux?: boolean, syncTiming: boolean, nocleanup: boolean, - chapters: boolean, - apiType?: 'web' | 'android' + chapters: boolean } export type CrunchyMultiDownload = { dubLang: string[], all?: boolean, but?: boolean, - e?: string, - crapi?: 'web' | 'android' + e?: string } export type CrunchyMuxOptions = { diff --git a/crunchy.ts b/crunchy.ts index f836398..7805640 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -52,6 +52,7 @@ export type sxItem = { export default class Crunchy implements ServiceClass { public cfg: yamlCfg.ConfigObject; + public api: 'android' | 'web'; private token: Record; private req: reqModule.Req; private cmsToken: { @@ -62,6 +63,7 @@ export default class Crunchy implements ServiceClass { this.cfg = yamlCfg.loadCfg(); this.token = yamlCfg.loadCRToken(); this.req = new reqModule.Req(domain, debug, false, 'cr'); + this.api = 'android'; } public checkToken(): boolean { @@ -71,6 +73,7 @@ export default class Crunchy implements ServiceClass { public async cli() { console.info(`\n=== Multi Downloader NX ${packageJson.version} ===\n`); const argv = yargs.appArgv(this.cfg.cli); + this.api = argv.crapi; if (argv.debug) this.debug = true; @@ -113,7 +116,7 @@ export default class Crunchy implements ServiceClass { const selected = await this.downloadFromSeriesID(argv.series, { ...argv }); if (selected.isOk) { for (const select of selected.value) { - if (!(await this.downloadEpisode(select, {...argv, skipsubs: false, apiType: argv.crapi }, true))) { + if (!(await this.downloadEpisode(select, {...argv, skipsubs: false}, true))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -131,10 +134,10 @@ export default class Crunchy implements ServiceClass { console.info('One show can only be downloaded with one dub. Use --srz instead.'); } argv.dubLang = [argv.dubLang[0]]; - const selected = await this.getSeasonById(argv.s, argv.numbers, argv.e, argv.but, argv.all, argv.crapi); + const selected = await this.getSeasonById(argv.s, argv.numbers, argv.e, argv.but, argv.all); if (selected.isOk) { for (const select of selected.value) { - if (!(await this.downloadEpisode(select, {...argv, skipsubs: false, apiType: argv.crapi }))) { + if (!(await this.downloadEpisode(select, {...argv, skipsubs: false }))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -144,9 +147,9 @@ export default class Crunchy implements ServiceClass { } else if(argv.e){ await this.refreshToken(); - const selected = await this.getObjectById(argv.crapi, argv.e, false); + const selected = await this.getObjectById(argv.e, false); for (const select of selected as Partial[]) { - if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false, apiType: argv.crapi }))) { + if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false}))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -154,9 +157,9 @@ export default class Crunchy implements ServiceClass { return true; } else if (argv.extid) { await this.refreshToken(); - const selected = await this.getObjectById(argv.crapi, argv.extid, false, true); + const selected = await this.getObjectById(argv.extid, false, true); for (const select of selected as Partial[]) { - if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false, apiType: argv.crapi }))) { + if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false}))) { console.error(`Unable to download selected episode ${select.episodeNumber}`); return false; } @@ -747,7 +750,7 @@ export default class Crunchy implements ServiceClass { console.info(` Total results: ${newlyAddedResults.total} (Page: ${pageCur}/${pageMax})`); } - public async getSeasonById(id: string, numbers: number, e: string|undefined, but: boolean, all: boolean, apiType: 'web' | 'android') : Promise> { + public async getSeasonById(id: string, numbers: number, e: string|undefined, but: boolean, all: boolean) : Promise> { if(!this.cmsToken.cms){ console.error('Authentication required!'); return { isOk: false, reason: new Error('Authentication required') }; @@ -772,7 +775,7 @@ export default class Crunchy implements ServiceClass { let episodeList = { total: 0, data: [], meta: {} } as CrunchyEpisodeList; //get episode info - if (apiType == 'android') { + if (this.api == 'android') { const reqEpsListOpts = [ api.beta_cms, this.cmsToken.cms.bucket, @@ -929,7 +932,7 @@ export default class Crunchy implements ServiceClass { return true; } - public async getObjectById(apiType: 'web' | 'android', e?: string, earlyReturn?: boolean, external_id?: boolean): Promise[]|undefined> { + public async getObjectById(e?: string, earlyReturn?: boolean, external_id?: boolean): Promise[]|undefined> { if(!this.cmsToken.cms){ console.error('Authentication required!'); return []; @@ -990,7 +993,7 @@ export default class Crunchy implements ServiceClass { // reqs let objectInfo: ObjectInfo = { total: 0, data: [], meta: {} }; - if (apiType == 'android') { + if (this.api == 'android') { const objectReqOpts = [ api.beta_cms, this.cmsToken.cms.bucket, @@ -1268,7 +1271,7 @@ export default class Crunchy implements ServiceClass { } let pbData = { total: 0, data: {}, meta: {} } as PlaybackData; - if (options.apiType == 'android') { + if (this.api == 'android') { const videoStreamsReq = [ api.beta_cms, `${this.cmsToken.cms.bucket}/videos/${mediaId}/streams`, @@ -2092,7 +2095,7 @@ export default class Crunchy implements ServiceClass { merger.cleanUp(); } - public async listSeriesID(id: string, apiType: 'android' | 'web' = 'android'): Promise<{ list: Episode[], data: Record}> { @@ -2109,7 +2112,7 @@ export default class Crunchy implements ServiceClass { for(const season of Object.keys(result) as unknown as number[]) { for (const key of Object.keys(result[season])) { const s = result[season][key]; - (await this.getSeasonDataById(s, apiType))?.data?.forEach(episode => { + (await this.getSeasonDataById(s))?.data?.forEach(episode => { //TODO: Make sure the below code is ok //Prepare the episode array let item; @@ -2200,7 +2203,7 @@ export default class Crunchy implements ServiceClass { } public async downloadFromSeriesID(id: string, data: CrunchyMultiDownload) : Promise> { - const { data: episodes } = await this.listSeriesID(id, data.crapi); + const { data: episodes } = await this.listSeriesID(id); console.info(''); console.info('-'.repeat(30)); console.info(''); @@ -2351,7 +2354,7 @@ export default class Crunchy implements ServiceClass { return seasonsList; } - public async getSeasonDataById(item: SeriesSearchItem, apiType: 'android' | 'web' = 'android', log = false){ + public async getSeasonDataById(item: SeriesSearchItem, log = false){ if(!this.cmsToken.cms){ console.error('Authentication required!'); return; @@ -2376,7 +2379,7 @@ export default class Crunchy implements ServiceClass { let episodeList = { total: 0, data: [], meta: {} } as CrunchyEpisodeList; //get episode info - if (apiType == 'android') { + if (this.api == 'android') { const reqEpsListOpts = [ api.beta_cms, this.cmsToken.cms.bucket, diff --git a/gui/server/services/crunchyroll.ts b/gui/server/services/crunchyroll.ts index 9bc2777..1b9f931 100644 --- a/gui/server/services/crunchyroll.ts +++ b/gui/server/services/crunchyroll.ts @@ -91,6 +91,7 @@ class CrunchyHandler extends Base implements MessageHandler { console.debug(`Got download options: ${JSON.stringify(data)}`); this.setDownloading(true); const _default = yargs.appArgv(this.crunchy.cfg.cli, true); + this.crunchy.api = _default.crapi; const res = await this.crunchy.downloadFromSeriesID(data.id, { dubLang: data.dubLang, e: data.e From 03e3c96f3cab2439c0e780137a06a0abc27190c6 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Mon, 5 Feb 2024 21:21:06 -0800 Subject: [PATCH 33/69] Rework Building Should now be more modular, at the cost of human-readable names. It can now be used to build android versions, alpine versions, as well as arm for each supported platform --- .github/workflows/release-matrix.yml | 7 +++--- Dockerfile | 4 ++-- docs/README.md | 2 +- modules/build.ts | 36 +++++++++++++++------------- package.json | 22 ++++++++++------- 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/.github/workflows/release-matrix.yml b/.github/workflows/release-matrix.yml index 288abcb..981cf59 100644 --- a/.github/workflows/release-matrix.yml +++ b/.github/workflows/release-matrix.yml @@ -8,9 +8,10 @@ jobs: build: strategy: matrix: - build_type: [ ubuntu, macos, windows ] + build_type: [ linux, macos, windows ] + build_arch: [ x64 ] gui: [ gui, cli ] - runs-on: ${{ matrix.build_type }}-latest + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 @@ -38,7 +39,7 @@ jobs: with: upload_url: ${{ github.event.release.upload_url }} asset_name: multi-downloader-nx-${{ matrix.build_type }}-${{ matrix.gui }}.7z - asset_path: ./lib/_builds/multi-downloader-nx-${{ matrix.build_type }}64-${{ matrix.gui }}.7z + asset_path: ./lib/_builds/multi-downloader-nx-${{ matrix.build_type }}-${{ matrix.build_arch }}-${{ matrix.gui }}.7z asset_content_type: application/x-7z-compressed env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile b/Dockerfile index 54b108e..036b6c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,13 +15,13 @@ RUN echo 'ffmpeg: "./bin/ffmpeg/ffmpeg"\nmkvmerge: "./bin/mkvtoolnix/mkvmerge"' RUN npm install -g pnpm RUN pnpm i -RUN pnpm run build-ubuntu-gui +RUN pnpm run build-linux-gui # Move build to new Clean Image FROM node WORKDIR "/app" -COPY --from=builder /app/lib/_builds/multi-downloader-nx-ubuntu64-gui ./ +COPY --from=builder /app/lib/_builds/multi-downloader-nx-linux64-gui ./ # Install mkvmerge and ffmpeg diff --git a/docs/README.md b/docs/README.md index a80e8c1..dab0de0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -75,7 +75,7 @@ Afterwards run `pnpm run tsc false [true if you want gui, false otherwise]`. If you want the `js` files you are done. Just `cd` into the `lib` folder, and run `node index.js --help` to get started with the CLI, or run `node gui.js` to run the GUI -If you want to package the application, run pnpm run build-`{platform}`-`{type}` where `{platform}` is the operating system (currently the choices are windows, ubuntu, macos, and arm) and `{type}` is cli or gui. +If you want to package the application, run pnpm run build-`{platform}`-`{type}` where `{platform}` is the operating system (currently the choices are windows, linux, macos, alpine, android, and arm) and `{type}` is cli or gui. ## DRM Decryption diff --git a/modules/build.ts b/modules/build.ts index bd6681a..9161b25 100644 --- a/modules/build.ts +++ b/modules/build.ts @@ -9,7 +9,7 @@ import { console } from './log'; const buildsDir = './_builds'; const nodeVer = 'node18-'; -type BuildTypes = `${'ubuntu'|'windows'|'macos'|'arm'}64` +type BuildTypes = `${'windows'|'macos'|'linux'|'linuxstatic'|'alpine'}-${'x64'|'arm64'}`|'linuxstatic-armv7' (async () => { const buildType = process.argv[2] as BuildTypes; @@ -21,16 +21,23 @@ type BuildTypes = `${'ubuntu'|'windows'|'macos'|'arm'}64` // main async function buildBinary(buildType: BuildTypes, gui: boolean) { const buildStr = 'multi-downloader-nx'; - const acceptableBuilds = ['windows64','ubuntu64','macos64']; + const acceptablePlatforms = ['windows','linux','linuxstatic','macos','alpine']; + const acceptableArchs = ['x64','arm64']; + const acceptableBuilds: string[] = ['linuxstatic-armv7']; + for (const platform of acceptablePlatforms) { + for (const arch of acceptableArchs) { + acceptableBuilds.push(platform+'-'+arch); + } + } if(!acceptableBuilds.includes(buildType)){ - console.error('[ERROR] unknown build type!'); + console.error('Unknown build type!'); process.exit(1); } await modulesCleanup('.'); if(!fs.existsSync(buildsDir)){ fs.mkdirSync(buildsDir); } - const buildFull = `${buildStr}-${buildType}-${gui ? 'gui' : 'cli'}`; + const buildFull = `${buildStr}-${getFriendlyName(buildType)}-${gui ? 'gui' : 'cli'}`; const buildDir = `${buildsDir}/${buildFull}`; if(fs.existsSync(buildDir)){ fs.removeSync(buildDir); @@ -38,7 +45,7 @@ async function buildBinary(buildType: BuildTypes, gui: boolean) { fs.mkdirSync(buildDir); const buildConfig = [ gui ? 'gui.js' : 'index.js', - '--target', nodeVer + getTarget(buildType), + '--target', nodeVer + buildType, '--output', `${buildDir}/${pkg.short_name}`, ]; console.info(`[Build] Build configuration: ${buildFull}`); @@ -70,15 +77,12 @@ async function buildBinary(buildType: BuildTypes, gui: boolean) { execSync(`7z a -t7z "${buildsDir}/${buildFull}.7z" "${buildDir}"`,{stdio:[0,1,2]}); } -function getTarget(bt: string) : string { - switch(bt){ - case 'windows64': - return 'windows-x64'; - case 'ubuntu64': - return 'linux-x64'; - case 'macos64': - return 'macos-x64'; - default: - return 'windows-x64'; +function getFriendlyName(buildString: string): string { + if (buildString.includes('armv7')) { + return 'android'; } -} + if (buildString.includes('linuxstatic')) { + buildString = buildString.replace('linuxstatic', 'linux'); + } + return buildString; +} \ No newline at end of file diff --git a/package.json b/package.json index 61443ce..e8b9758 100644 --- a/package.json +++ b/package.json @@ -97,18 +97,22 @@ "docs": "ts-node modules/build-docs.ts", "tsc": "ts-node tsc.ts", "prebuild-cli": "pnpm run tsc false false", - "build-windows-cli": "pnpm run prebuild-cli && cd lib && node modules/build windows64", - "build-ubuntu-cli": "pnpm run prebuild-cli && cd lib && node modules/build ubuntu64", - "build-arm-cli": "pnpm run prebuild-cli && cd lib && node modules/build arm64", - "build-macos-cli": "pnpm run prebuild-cli && cd lib && node modules/build macos64", + "build-windows-cli": "pnpm run prebuild-cli && cd lib && node modules/build windows-x64", + "build-linux-cli": "pnpm run prebuild-cli && cd lib && node modules/build linux-x64", + "build-arm-cli": "pnpm run prebuild-cli && cd lib && node modules/build linux-arm64", + "build-macos-cli": "pnpm run prebuild-cli && cd lib && node modules/build macos-x64", + "build-alpine-cli": "pnpm run prebuild-cli && cd lib && node modules/build alpine-x64", + "build-android-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-armv7", "prebuild-gui": "pnpm run tsc", - "build-windows-gui": "pnpm run prebuild-gui && cd lib && node modules/build windows64 true", - "build-ubuntu-gui": "pnpm run prebuild-gui && cd lib && node modules/build ubuntu64 true", - "build-arm-gui": "pnpm run prebuild-gui && cd lib && node modules/build arm64 true", - "build-macos-gui": "pnpm run prebuild-gui && cd lib && node modules/build macos64 true", + "build-windows-gui": "pnpm run prebuild-gui && cd lib && node modules/build windows-x64 true", + "build-linux-gui": "pnpm run prebuild-gui && cd lib && node modules/build linux-x64 true", + "build-arm-gui": "pnpm run prebuild-gui && cd lib && node modules/build linux-arm64 true", + "build-macos-gui": "pnpm run prebuild-gui && cd lib && node modules/build macos-x64 true", + "build-alpine-gui": "pnpm run prebuild-gui && cd lib && node modules/build alpine-x64 true", + "build-android-gui": "pnpm run prebuild-gui && cd lib && node modules/build linuxstatic-armv7 true", "eslint": "eslint *.js modules", "eslint-fix": "eslint *.js modules --fix", "pretest": "pnpm run tsc", - "test": "pnpm run pretest && cd lib && node modules/build windows64 && node modules/build ubuntu64 && node modules/build macos64" + "test": "pnpm run pretest && cd lib && node modules/build windows-x64 && node modules/build linux-x64 && node modules/build macos-x64 && node modules/build alpine-x64" } } From 205232f6d9741e1a49162f529b8abcba5f8b63ef Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 6 Feb 2024 17:44:36 -0800 Subject: [PATCH 34/69] Switch to linuxstatic for linux build This should allow the linux build to run on a much larger variety of linux distros. --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e8b9758..6b93a8e 100644 --- a/package.json +++ b/package.json @@ -98,14 +98,14 @@ "tsc": "ts-node tsc.ts", "prebuild-cli": "pnpm run tsc false false", "build-windows-cli": "pnpm run prebuild-cli && cd lib && node modules/build windows-x64", - "build-linux-cli": "pnpm run prebuild-cli && cd lib && node modules/build linux-x64", + "build-linux-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-x64", "build-arm-cli": "pnpm run prebuild-cli && cd lib && node modules/build linux-arm64", "build-macos-cli": "pnpm run prebuild-cli && cd lib && node modules/build macos-x64", "build-alpine-cli": "pnpm run prebuild-cli && cd lib && node modules/build alpine-x64", "build-android-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-armv7", "prebuild-gui": "pnpm run tsc", "build-windows-gui": "pnpm run prebuild-gui && cd lib && node modules/build windows-x64 true", - "build-linux-gui": "pnpm run prebuild-gui && cd lib && node modules/build linux-x64 true", + "build-linux-gui": "pnpm run prebuild-gui && cd lib && node modules/build linuxstatic-x64 true", "build-arm-gui": "pnpm run prebuild-gui && cd lib && node modules/build linux-arm64 true", "build-macos-gui": "pnpm run prebuild-gui && cd lib && node modules/build macos-x64 true", "build-alpine-gui": "pnpm run prebuild-gui && cd lib && node modules/build alpine-x64 true", @@ -113,6 +113,6 @@ "eslint": "eslint *.js modules", "eslint-fix": "eslint *.js modules --fix", "pretest": "pnpm run tsc", - "test": "pnpm run pretest && cd lib && node modules/build windows-x64 && node modules/build linux-x64 && node modules/build macos-x64 && node modules/build alpine-x64" + "test": "pnpm run pretest && cd lib && node modules/build windows-x64 && node modules/build linuxstatic-x64 && node modules/build macos-x64" } } From 66e1ee5702eb47f0e97c56a21a9bb48e71274b8d Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 6 Feb 2024 19:10:11 -0800 Subject: [PATCH 35/69] [CR] Add CC Subtitles CC Subtitles looks to be possible in the API now (it's not just always empty), so I've written some code to handle CC subtitles properly and download them Fixes #580 --- @types/crunchyAndroidStreams.d.ts | 9 +++---- @types/crunchyTypes.d.ts | 4 ++- @types/playbackData.d.ts | 2 +- crunchy.ts | 43 ++++++++++++++++++++++++------- modules/module.langsData.ts | 4 +-- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/@types/crunchyAndroidStreams.d.ts b/@types/crunchyAndroidStreams.d.ts index c1e56c3..c5e9889 100644 --- a/@types/crunchyAndroidStreams.d.ts +++ b/@types/crunchyAndroidStreams.d.ts @@ -3,15 +3,15 @@ export interface CrunchyAndroidStreams { __href__: string; __resource_key__: string; __links__: Links; - __actions__: Actions; + __actions__: Record; media_id: string; audio_locale: Locale; subtitles: Subtitles; - closed_captions: Actions; + closed_captions: Subtitles; streams: Streams; bifs: string[]; versions: Version[]; - captions: Actions; + captions: Record; } export interface Subtitles { @@ -34,9 +34,6 @@ export interface Subtitles { 'ja-JP'?: Subtitle; } -export interface Actions { -} - export interface Links { resource: Resource; } diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 13ceee4..7de374e 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -32,7 +32,9 @@ export type CrunchyDownloadOptions = { skipmux?: boolean, syncTiming: boolean, nocleanup: boolean, - chapters: boolean + chapters: boolean, + fontName: string | undefined, + fontSize: number, } export type CrunchyMultiDownload = { diff --git a/@types/playbackData.d.ts b/@types/playbackData.d.ts index 3374c3e..1b6548e 100644 --- a/@types/playbackData.d.ts +++ b/@types/playbackData.d.ts @@ -56,7 +56,7 @@ export interface Meta { bifs: string[]; versions: Version[]; audio_locale: Locale; - closed_captions: Record; + closed_captions: Subtitles; captions: Record; } diff --git a/crunchy.ts b/crunchy.ts index 7805640..edc431b 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -19,6 +19,7 @@ import * as yamlCfg from './modules/module.cfg-loader'; import * as yargs from './modules/module.app-args'; import Merger, { Font, MergerInput, SubtitleInput } from './modules/module.merger'; import getKeys, { canDecrypt } from './modules/cr_widevine'; +//import vttConvert from './modules/module.vttconvert'; // args @@ -1935,22 +1936,35 @@ export default class Crunchy implements ServiceClass { if(!options.skipsubs && options.dlsubs.indexOf('none') == -1){ if(pbData.meta.subtitles && Object.values(pbData.meta.subtitles).length > 0){ const subsData = Object.values(pbData.meta.subtitles); + const capsData = Object.values(pbData.meta.closed_captions); const subsDataMapped = subsData.map((s) => { const subLang = langsData.fixAndFindCrLC(s.locale); return { ...s, + isCC: false, locale: subLang, language: subLang.locale }; - }); + }).concat( + capsData.map((s) => { + const subLang = langsData.fixAndFindCrLC(s.locale); + return { + ...s, + isCC: true, + locale: subLang, + language: subLang.locale + }; + }) + ); const subsArr = langsData.sortSubtitles(subsDataMapped, 'language'); for(const subsIndex in subsArr){ const subsItem = subsArr[subsIndex]; const langItem = subsItem.locale; const sxData: Partial = {}; sxData.language = langItem; - const isCC = langItem.code === audDub; - sxData.file = langsData.subsFile(fileName as string, subsIndex, langItem, isCC, options.ccTag); + const isSigns = langItem.code === audDub && !subsItem.isCC; + const isCC = subsItem.isCC; + sxData.file = langsData.subsFile(fileName as string, subsIndex, langItem, isCC, options.ccTag, isSigns, subsItem.format); sxData.path = path.join(this.cfg.dir.content, sxData.file); const split = sxData.path.split(path.sep).slice(0, -1); split.forEach((val, ind, arr) => { @@ -1963,13 +1977,22 @@ export default class Crunchy implements ServiceClass { if(options.dlsubs.includes('all') || options.dlsubs.includes(langItem.locale)){ const subsAssReq = await this.req.getData(subsItem.url); if(subsAssReq.ok && subsAssReq.res){ - let sBody = '\ufeff' + subsAssReq.res.body; - const sBodySplit = sBody.split('\r\n'); - sBodySplit.splice(2, 0, 'ScaledBorderAndShadow: yes'); - sBody = sBodySplit.join('\r\n'); - sxData.title = sBody.split('\r\n')[1].replace(/^Title: /, ''); - sxData.title = `${langItem.language} / ${sxData.title}`; - sxData.fonts = fontsData.assFonts(sBody) as Font[]; + let sBody; + if (subsItem.format == 'vtt') { + //TODO: look into converting downloaded vtt into ASS + //sBody = vttConvert(subsAssReq.res.body, false, langItem.language, options.fontSize, options.fontName); + sBody = subsAssReq.res.body; + //TODO: look into parsing the fonts from the styles field in the VTT + sxData.fonts = options.fontName ? [options.fontName] as Font[] : []; + } else { + sBody = '\ufeff' + subsAssReq.res.body; + const sBodySplit = sBody.split('\r\n'); + sBodySplit.splice(2, 0, 'ScaledBorderAndShadow: yes'); + sBody = sBodySplit.join('\r\n'); + sxData.title = sBody.split('\r\n')[1].replace(/^Title: /, ''); + sxData.title = `${langItem.language} / ${sxData.title}`; + sxData.fonts = fontsData.assFonts(sBody) as Font[]; + } fs.writeFileSync(sxData.path, sBody); console.info(`Subtitle downloaded: ${sxData.file}`); files.push({ diff --git a/modules/module.langsData.ts b/modules/module.langsData.ts index c4ffbf1..e6453ef 100644 --- a/modules/module.langsData.ts +++ b/modules/module.langsData.ts @@ -142,9 +142,9 @@ const sortTags = (data: string[]) => { return sort.map(e => e.locale as string); }; -const subsFile = (fnOutput:string, subsIndex: string, langItem: LanguageItem, isCC: boolean, ccTag: string) => { +const subsFile = (fnOutput:string, subsIndex: string, langItem: LanguageItem, isCC: boolean, ccTag: string, isSigns?: boolean, format?: string) => { subsIndex = (parseInt(subsIndex) + 1).toString().padStart(2, '0'); - return `${fnOutput}.${subsIndex}.${langItem.code}.${langItem.language}${isCC ? `.${ccTag}` : ''}.ass`; + return `${fnOutput}.${subsIndex}.${langItem.code}.${langItem.language}${isCC ? `.${ccTag}` : ''}${isSigns ? '.signs' : ''}.${format ? format : 'ass'}`; }; // construct dub langs const From d2b4adf09baf435fc274ae5a64ac5b69309c8867 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Tue, 6 Feb 2024 19:46:14 -0800 Subject: [PATCH 36/69] [CR] Improve Captions support Renames what was previously called CC to `Signs`. Also prevents a possible collision where not all subs would be downloaded --- @types/crunchyTypes.d.ts | 1 + crunchy.ts | 4 +++- modules/module.merger.ts | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 7de374e..2ffa9c7 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -97,6 +97,7 @@ export type DownloadedMedia = { path: string } | ({ type: 'Subtitle', + signs: boolean, cc: boolean } & sxItem ) diff --git a/crunchy.ts b/crunchy.ts index edc431b..2f685fc 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1972,7 +1972,7 @@ export default class Crunchy implements ServiceClass { if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); }); - if (files.some(a => a.type === 'Subtitle' && (a.language.cr_locale == langItem.cr_locale || a.language.locale == langItem.locale) && a.cc === isCC)) + if (files.some(a => a.type === 'Subtitle' && (a.language.cr_locale == langItem.cr_locale || a.language.locale == langItem.locale) && a.cc === isCC && a.signs === isSigns)) continue; if(options.dlsubs.includes('all') || options.dlsubs.includes(langItem.locale)){ const subsAssReq = await this.req.getData(subsItem.url); @@ -1999,6 +1999,7 @@ export default class Crunchy implements ServiceClass { type: 'Subtitle', ...sxData as sxItem, cc: isCC, + signs: isSigns, }); } else{ @@ -2061,6 +2062,7 @@ export default class Crunchy implements ServiceClass { file: a.path, language: a.language, closedCaption: a.cc, + signs: a.signs, }; }), simul: false, diff --git a/modules/module.merger.ts b/modules/module.merger.ts index 989667f..e3d8b56 100644 --- a/modules/module.merger.ts +++ b/modules/module.merger.ts @@ -21,6 +21,7 @@ export type SubtitleInput = { language: LanguageItem, file: string, closedCaption?: boolean, + signs?: boolean, delay?: number } @@ -182,7 +183,7 @@ class Merger { '-c:a copy', this.options.output.split('.').pop()?.toLowerCase() === 'mp4' ? '-c:s mov_text' : '-c:s ass', ...this.options.subtitles.map((sub, subindex) => `-metadata:s:s:${subindex} title="${ - (sub.language.language || sub.language.name) + `${sub.closedCaption === true ? ` ${this.options.ccTag}` : ''}` + (sub.language.language || sub.language.name) + `${sub.closedCaption === true ? ` ${this.options.ccTag}` : ''}` + `${sub.signs === true ? ' Signs' : ''}` }" -metadata:s:s:${subindex} language=${sub.language.code}`) ); args.push(...this.options.options.ffmpeg); @@ -285,7 +286,7 @@ class Merger { `--sync 0:-${Math.ceil(subObj.delay*1000)}` ); } - args.push('--track-name', `0:"${(subObj.language.language || subObj.language.name) + `${subObj.closedCaption === true ? ` ${this.options.ccTag}` : ''}`}"`); + args.push('--track-name', `0:"${(subObj.language.language || subObj.language.name) + `${subObj.closedCaption === true ? ` ${this.options.ccTag}` : ''}` + `${subObj.signs === true ? ' Signs' : ''}`}"`); args.push('--language', `0:"${subObj.language.code}"`); //TODO: look into making Closed Caption default if it's the only sub of the default language downloaded if (this.options.defaults.sub.code === subObj.language.code && !subObj.closedCaption) { From 3582f1829f502dc9e926c41dc3d6d07791b38020 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Wed, 7 Feb 2024 15:59:22 -0800 Subject: [PATCH 37/69] [CR] Chapter Fix Chapters could fail if for whatever reason the start and end times weren't defined. --- crunchy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crunchy.ts b/crunchy.ts index 2f685fc..e4423df 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1233,7 +1233,8 @@ export default class Crunchy implements ServiceClass { chapters.sort((a, b) => a.start - b.start); //Loop through all the chapters for (const chapter of chapters) { - //Generate timestamps + if (!chapter.start || !chapter.end) continue; + //Generate timestamps const startTime = new Date(0), endTime = new Date(0); startTime.setSeconds(chapter.start); endTime.setSeconds(chapter.end); From 7cab2cbab3b574cf0a175de2dbb104a3e1a23839 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Thu, 8 Feb 2024 18:36:15 -0800 Subject: [PATCH 38/69] [CR] Fix old chapter API Could fail to mux into the file if time didn't contain milliseconds --- crunchy.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index e4423df..7061336 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1197,8 +1197,10 @@ export default class Crunchy implements ServiceClass { const startTime = new Date(0), endTime = new Date(0); startTime.setSeconds(chapterData.startTime); endTime.setSeconds(chapterData.endTime); - const startFormatted = startTime.toISOString().substring(11, 19)+'.'+String(chapterData.startTime).split('.')[1]; - const endFormatted = endTime.toISOString().substring(11, 19)+'.'+String(chapterData.endTime).split('.')[1]; + const startTimeMS = String(chapterData.startTime).split('.')[1], endTimeMS = String(chapterData.endTime).split('.')[1]; + const startMS = startTimeMS ? startTimeMS : '00', endMS = endTimeMS ? endTimeMS : '00'; + const startFormatted = startTime.toISOString().substring(11, 19)+'.'+startMS; + const endFormatted = endTime.toISOString().substring(11, 19)+'.'+endMS; //Push Generated Chapters if (chapterData.startTime > 1) { From 5417db41fd945da7a380eae0682cc105dce1bd59 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sat, 10 Feb 2024 08:55:22 -0800 Subject: [PATCH 39/69] [CR] Improve version detection Improves the version selection and detection to work for more than just --srz. This should help prevent API errors on non --srz requests --- @types/crunchyTypes.d.ts | 1 + crunchy.ts | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 2ffa9c7..a4d24f1 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -35,6 +35,7 @@ export type CrunchyDownloadOptions = { chapters: boolean, fontName: string | undefined, fontSize: number, + dubLang: string[], } export type CrunchyMultiDownload = { diff --git a/crunchy.ts b/crunchy.ts index 7061336..900c4a0 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -148,6 +148,10 @@ export default class Crunchy implements ServiceClass { } else if(argv.e){ await this.refreshToken(); + if (argv.dubLang.length > 1) { + console.info('One show can only be downloaded with one dub. Use --srz instead.'); + } + argv.dubLang = [argv.dubLang[0]]; const selected = await this.getObjectById(argv.e, false); for (const select of selected as Partial[]) { if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false}))) { @@ -158,6 +162,10 @@ export default class Crunchy implements ServiceClass { return true; } else if (argv.extid) { await this.refreshToken(); + if (argv.dubLang.length > 1) { + console.info('One show can only be downloaded with one dub. Use --srz instead.'); + } + argv.dubLang = [argv.dubLang[0]]; const selected = await this.getObjectById(argv.extid, false, true); for (const select of selected as Partial[]) { if (!(await this.downloadEpisode(select as CrunchyEpMeta, {...argv, skipsubs: false}))) { @@ -1165,10 +1173,17 @@ export default class Crunchy implements ServiceClass { //Get Media GUID let mediaId = mMeta.mediaId; - if (mMeta.versions && mMeta.lang) { - currentVersion = mMeta.versions.find(a => a.audio_locale == mMeta.lang?.cr_locale); + if (mMeta.versions) { + if (mMeta.lang) { + currentVersion = mMeta.versions.find(a => a.audio_locale == mMeta.lang?.cr_locale); + } else if (options.dubLang.length == 1) { + const currentLang = langsData.languages.find(a => a.code == options.dubLang[0]); + currentVersion = mMeta.versions.find(a => a.audio_locale == currentLang?.cr_locale); + } else if (mMeta.versions.length == 1) { + currentVersion = mMeta.versions[0]; + } if (!currentVersion?.media_guid) { - console.error('Selected language not found.'); + console.error('Selected language not found in versions.'); continue; } isPrimary = currentVersion.original; From fcb192f7fe0464f43cc1703f647154c8e876892e Mon Sep 17 00:00:00 2001 From: DAREKON <63023042+DAREK0N@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:06:15 +0100 Subject: [PATCH 40/69] initial --- gui/react/public/favicon.webp | Bin 0 -> 2258 bytes gui/react/public/index.html | 2 + gui/react/react.7z | Bin 0 -> 134161 bytes gui/react/src/Layout.tsx | 17 +- gui/react/src/Style.tsx | 2 - .../src/components/AddToQueue/AddToQueue.tsx | 10 +- .../DownloadSelector/DownloadSelector.tsx | 215 ++++++++- .../src/components/MainFrame/MainFrame.tsx | 3 +- .../src/components/MainFrame/Queue/Queue.tsx | 423 +++++++++++++++--- gui/react/src/components/MenuBar/MenuBar.tsx | 4 +- gui/react/src/provider/ServiceProvider.tsx | 6 +- gui/react/src/provider/Store.tsx | 5 +- 12 files changed, 573 insertions(+), 114 deletions(-) create mode 100644 gui/react/public/favicon.webp create mode 100644 gui/react/react.7z diff --git a/gui/react/public/favicon.webp b/gui/react/public/favicon.webp new file mode 100644 index 0000000000000000000000000000000000000000..279efcfdc9a13e3d3fd08cf6ba35cc3decfd7277 GIT binary patch literal 2258 zcmV;@2rc(gNk&G>2mkF7Xf zsthwD=KBiLF!OzH9T5`%43vD~o2kQF|G%oY`UW9Jzg2(r@YZ2!FYJ?m_wxBGnfo=3 zTCUaZXRe%=c_jwpxpz9Xq5dux4~pE8BkI!uwejGo8j(37gtKoAYJ1~tHZ1VLpDd54 zLr3MLpM9a@GeR9q&W=ND+rFz&$7{uHW(m9hnUsm<>8`*cmvV1RnK{0vxR?|8Wk{Ji z{3XB^_ZLmd+~yZy(>%T6BPPWKxcrN0RNt9HCq#InicffpWJX z+Dgh%4(BBsiARJ-beVuYz-?vGuOS#>P*YfwxMzUiB`o zKsjGnT7q&vv80VQ|Js{N@l+@RE1qSj1Or4}LbOX!*o|7|E>R?w+*W--ut2`BDA1&6 zYz8cG{Ynw}tIG;yh>4+Mfp9`mc_J*fJI2L$cd=87%m)i?o7gaS7AaD678eL<_|QW0 z9aDrJ&*eYE$eDkhBt>a*4xx;bvM?(=!pTUPm8D3{&XC_?<()h&VqnFHOj9XZ)hXgr zygU^rf&;t^f)jCy*!V;aGdTc|-(lvR3~*k@jD8;ADn;!TfDCRj0Ds};FOX`usYyN! zH<~X@k-NEvohvEqd~=AMgIn0S^$|3p=+Cv2&QhPHGQ3dtvN^eHv~wpH#z5O@hC0^A|t{HyMDdxVZvw zUdN1n9zcGFnRhY(&10ElBIJ`o(?We}VYpW@}IH~}hNGB7n_V8w_`liy6XN2#?5-2=g!&v}Tj^aB?KCHu~>pYblM2*6J{qCOqi zY4B8y$iQ1M7|*}c?V!KQ$AcnpT0Vaz^H)u?POJTuxq4m(-b=plUh44H!&miI-yp>3 zx9YDR-a1U}DLx4p2v$%yAQ%Jy0MHr$odGJK0H6RqZ8VoiBqJgrBa~{GfDMUhZruuK z-zxuA?UVdBE1C0upW+r(mO}qO+~w#7le}4f)b#Yum>6SyM*aI(*0F%p8~2qp@km1%#!Bnq6A%?dhGVpy)=GKGbS zr2tW?Inr@*BqR7sq=MO8F%;L`4eCqd1%MAtWYF~YGhCqH0RH?+000F!`nHp7j*B;c z{LB6EHSu+yQXMosODDsBEk5XT7=b#NckKM~o!LA`6esr7=c{;P5;iMyxz~#{DAZXm zzZb^W&YIhhAc9t2W?wmtbIm0Khv|T?!Om&k<&x(c1pOUfm3#ED-5h0eW=K;NaZ075Tp(hq7P!>1hr?ZWG!1Ir(6!tA zB>N4`uf8JXY)Md0sk(oheSK9o{}H;Ca3DA4abC)_AStGE+ldXR(%FW-!d!LU#HM;k z`p);eN3rh5CoErIDIS4U&0@Bqq`f_Vy%Lg~&I9h9y?RXS@8cFihU#x+K=`>9Z>-su zwC24G`JNYM%Q38Ap#FV0`IKfV!uLTD=Q`K9$xZ!PU!SFF{QaiQ6%uPe`zI2m0xRlU zZ*m=LzJ>_W{u}+iEX@9iooK~a_Y4r6lbd6N|J=eaB8Vc$T`Fph+S(&eNO^t(zQgi4 z9&-3&CjHr{GJqys4b4uJ*ZW$W%hdT#KXk;2{eq)b`$9~;pkork%#AWe1+}$%*Kd+N z0RL!oJ$qHRQCyvprK27yB2SVtib4Z*TLk7mHMY$+BT)7>THb}_24NlXB_BOo^QLNM zUBS0ARKIpZJX_-XZfbzW|E&1JNV!AD&Gf2k{irjT9B$Xrq0Ahx^Ri%-gk$+CEZ-QM z(a}5QmvMXqhbexK@NhM5Etddi ghe)^}c;A%vc~9g^YQ7Rgg3Ufoi!l~@FIE5m07%q4q5uE@ literal 0 HcmV?d00001 diff --git a/gui/react/public/index.html b/gui/react/public/index.html index 92cc7d8..e0f99bb 100644 --- a/gui/react/public/index.html +++ b/gui/react/public/index.html @@ -1,6 +1,8 @@ + Multi Downloader + vN(#){|c zz3LPHo^`-l*Pq-d-8p`#d<7RucaFDw@EJ*Bfi3RQ7Y3C4VlwzfZ4jP8Ira1x$w6aV zwt1r8uY~MLyrH6!#7t`{*?rLRoZ2Lt70m3#4eVwxO;bywO&N)Gu4UVWQpaL%`o@Rh zpjXoJdU~3enh}yp=VW$nCP+lEbw@$!DQQmp+sIy{9hu$lc(2#3{e(*;t}MNW`p3=R z47>Y!=b{pZ4Nb?vTmmeWOwLQ9BXVzQpm1CHNVP~yE9ljtn3rTgIwqUi&IU69M7|S@J)XIe@6e`&6X{m>jp3!E)CAylStKUd_I_Y`_BG1^zf^(i0hr+iQ?46=uF=p?< zz<8MU2HY%LE3&biCHwexp|&2`=qgF9421R2;R-GtKThf>2)Wj47&1M zDZU&5g800%w!GOyIur0ObBiP2Jx-FCf-PPOVpa0|wWJgU@m0(H9pQuC#gLvT@ z!I=)OnMIAEWv^nO34MaQ6f1@}(w`p`nQGV0Z^01GlI9sF|MAeHm(iaHeoH4Z6EV_K z?IerPH{x=I!;r<5?kal2>T!YxU0EOrW0$_W}hF=9&6mz`CoSu}wjNfqFs zeqy7N5_vJY_=~{&-O-gI>^@|={+p4V%VEI2h{FrC5iXAu4@ZS`gOrcTmhV0pt7a~Y zTs=e5ED#9&#>Hs$<~|9ekQO9;o}0PQej*9Bd6GxrjO!=2?i3Ta9^m}u4Y=l^dEmFn2c=DXD#{_oBb z(+?Ir)^8`~f-$?lFvN5xpw=-VPdYq-ZvMDJA8hh~G10I1auS<$mt2D{r|Gl`iF`KV za#vM;Qj)u({EYbggOnF(bj_o9)*JEhr60rR(8RK+2|;_tns2{X^!Lrh7*z!{B>M#U z%E$Z;t+>8*f##74#cB>y|4m9A>1-d*<>3ST{g z>B8O&>OXeTtfZv4^Id{S74%sY*(x)G-Qwo!lhI+opX-!0nOC=Fo{cGe7AQe#aGgj~ z%kZXBdxpc)19svPe319wHqiKEOFr@^3WP#HWngz-4mxoOB7v09kwI%5+2zQIGTEy} z6S$}o1v3px`kD(ANcv3gDLx9!gsik^g+{_F^STJDTEGCMvB7+Sr(Zy zS_&ht$hLf+F`-5gaH}tY_>Q#mwvjaFe1Dc8NEY>n#0~icN}gZ6BaVFyn>C(Y6ga{H zscCo4pOH&hKC3K!@Q0$Uz)*}FPnMg(u3%U

wVcJ_WD8!BuT$dM~hi8n4)oa zy_@0Jpibr@KBJgc;!1O#dIuesht-a!jAKlg{MV>r4lFV#Mf!(heH;kCttP^2PA%5h z2>Ea65k*~_D|dH$y_hGStgKHFI^za;)|B`xHBC@yu;YaSser%&E8nKY_ULz{KUzFkT}{b{bFA8?P$Z(?6_GFY8aTz3};2xR!2{T3qw7 zT$LWhf&}S-*dA;}Z&g(A56_LdNt!J-46l6Uz23cPAsnAa@>rE2y}EE!A5xpvRau2N zDtT-3+ONW_WoV5>HK=wP(Tv+`W}5LT*KeT06?tx8!${?3@|Vq{2G27Sp(N;3G2=zj zK;dQP6?ZCJbaH5kJqvB_J`!_T z2DYWh$m)w`oE#NRQO_5S53=7$y}m7;S^N+sA@dRa!5>UXXE?D4(v{u(t1Uup z=_=R#-Y-ztOEQfaI2K%kzlpsR1ky9(so7reX8M3V#^w~Ys&`Ve3^~~2+&Yqa@4-va z4(+loyu^a};VuEB{mi^}KH|_D&|o6)2xagKA$@&$3AB|Fp^x`!P^_xYO+Y;YGi$AS zlDC9odI3&pb+lvvbMKOYgwPtfxm?^wp(&+p7RWXBwP8P0iP2QmHu;mMvDB zpGB?Bw4pOB1O3&ndCe}a>bhRYGd-lY2D*2twYB4RVFKkp>-7W41c~Zbv&zAGsRQ;y zc*c<{H)51vgnmQurLrYYDBVKX)RCAbuZWzhSUQ!ycjUJ~ja^e&^Dp$K;TTLlPKqom z3i4~(qaui!MPD*Aw!`r6-G+M)%saqAOR_vWS&76XZQlK@nReyu>D4mV{?it=Od-Yjx zi~#Cj77MeK=`FdXlp%;&50;K8($RrybbjW^c?IIf#Zt@l4V8x5>KgPkFWT1M*+o6tJ`(gz(WW|X zm}2H_;XBdT=z$f{4l_2b};h0qAM3hJK+Cc!V3}2X=oAXE$%NwtjM;u|rtyitKPlMg4T< zuINH@!g}c+zx(n5JhfB)A`jbox$Y^T*T=Lq8TTKOWphze&p^r>q(B9mMa#ES5zc)p zSXqa%e7NyfNkaT)fnoPv>(ZuNa=fvh;#uMy)l99q^7KK0x~$fevNVUXnK-U_z2&kf zKK(`|VOScs4PL_XVwnR1uhF1#AKVMuOMU2(VKY4)>eKykfbnprvBjh&#t$ms3)fZ7 z!)uK+ZaQ*eP%|{(@j)@p=%IwDwDxxP4Qgp{N>IKaWchEtJ@?^M7!cig=4=z9VHVMc zPB{}?o0{a!>U~R2iki3LyUslw=6h%R7Y?u;gx+6;m8FJ@9Ub% zYt(*@YYF@W`si;<(Cal2!cWNkOCc385IiJZ06Ri?27qG%&0V2RtOVdV9TS%yZ63>r z(cDhCVyIQ&ys2KEnoI{XhM=IqDw%i0FrUJ|FEvvSJ`-%{P_iikuU<+7GN~~~ecP#m zPlx)d532FTBU|GsP(0bL?OwrBJBTx5$1t`8Se)deL1G_Hx+!)>+=)4yl7y;~i`;3aR1eN84uTyqJT}g<)Ah5MylM#;knpv@m zg18em&Q=+w*@HQUs6d|mYEJPQ38uEW43O&gX9~#xm<^QjBYrs7>ljr5a$il%-cpX^ z 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

87@6~Dg9Z(?sxXWI|* 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

Ve%Q{W1b>_OC;g&9CD*tSetIT^3>FeP9YI_!VSU z>_Wr>sQ=3{I)j#XM}K!=3@&!_Sbi{t|C{=F9xS6Sjmg&Hk;MupRP8{Yq)U9=St|@; zEjs_>I{H!{96MjAVf{KWt`_wisK*W@_i%WAl!a}xor*4)hc464qS_r3pHZ$qQ}kv4 z9QcOeNmRx0@(C3tb9iUnpLlvozdKppJqd~fKhiRnNw-fYFg?8jJiZii0&hXWjpj$a-a`@$?gb}ih$$|!vt&n@IS91NA-d8s|Z`U@-5glh^_ zWOj6C(M7;~>{R|iW?PCt+$<`AW-#_)T|K+Cku6V>MIRhEp~Q+pQeQKv3LsO-YIsCe zSs)+BVnj2g>w>_|fxi4#HCbCZ^6Vhh?U(|nG76oZZ+VHe#SdR{h5pFU2qgnt(&hfD z_W!0SlozCF%+1mTVs{!hq@eKQ+hgUHGe654Xdb_Ej6YKYcdW&vy>G{oSAjGPV}VT8 zV_d#FcsXEM&zNJOWNZNjS-1-X` z)W@?mPeFqhg_qVX)i4l}b7YKJ#%@0mBz;E^hNA;&R)>z4uv6xQF@mA?P+!4In`@UvLrRoW@jv)x^l{>6+c))?xLGzf*2{`R-K& zm4G&PQQ=pl%NpN-&-#TO2&4hI)-vLA+Ck+v&Vd9VOpC*2c+8LJ@?@8VOGMI(9@d1E zE^(+mV|{ppWnu5kH^r4})WF|)`e#S(9>jJ-WPgQMN&V~m+!JUp3yqX?4~CHwl4>${ zt?1(RkDpZD9$is?-|dj-=@X82{qd*KFNYfy+~)u<_1{oRYp$tNDs9%+icI?q=U)oa zoUZEkcW3En<-m3MJ;g7n{tjf|LLF7Hc*h-aKnKkl;fRW>e>V~qmkupk;*ecI6yMsw z9Z!x)cQlcy+L@>wB;W3$G~SE=$PmxzIBvS2CYp6nbXY*jZ1$+V2-c%)rg|5~88Ih{ zX2k)Uug;1;<0NV3@sbe~jdY9X@?Z;9yj7e!K}w=Uv7mj4-6ZxTdCg>ALg^WHxwk^% z0;_oUkGL^8;){r&#d+y7&%&nza|n};+M!+n$j@s@=YJXyS`X)~Kg9B}R=UabnE^ouMtp64RHCk>|x8CJ1nw1TPdhbwVC?OlY*d+)AEYxPSy{JV`YdYJ(0x(7VefW{1(@)$3XW8$&jyd~<`AM6JV|>}q~Y5v zOy+=F;1(Abe0-)UoyGE_JQ}Y}aW-^50@hkrayM11A#!13gdVx!wvLBhxc$j=k+>)) zhaVgC*jFN@vCp0wl`M0eYYRGedZTQ0?JPn@gNcIP?U%SEGd1GLVtysVpImseoOw6h z`WRcWVVx!RRdY@$0aNV=7V{sX_cX>+4$?Vq=WLYzsXQRyksBn}6f@0H`R7P(tAZmH z;zqZp=@Rg`ARwLHq^oYBP~wMR`!Y*`9&H3TT!jrGJjf^(!;prO*)PA?xTPRXOffZ# zKrDH4$YN!`H(#J7dyH3PAuZ}=FYlB(Z(0}?_|pikdIv=&gja{mseAy}Oxs=EOw`!# zck?bg-o@|IpacZZ96T%`vTao;Dl52c-uL}BuJuu_2 z$|h251g3jKh^YH`g$={?|=571C-Q893-S8{X>hW*A zzj^RPAu+Enke!sA?{Tp5LL7X*Q`%yyJhts4fX9B^k`14fpx4p}Y-BWK@(HQK^i&cI znQv7x;D8P_xtKhs$rxgf!--F4e%vvP@Y3gKs}Qw?9dow=?d!qFL*NV}c!6}?a+Fz4VA4LKZn6>;&d@D`jh{*dJLO&96aQ|<)Z zESpotTU=HOIe&kxAN}<4I6TDe|E5zApa^(|ZakB{`812r+}U*{&K&^lwsBHtsa3 z6ys~9=@n@mX?&`x?4zzl=~yJ|1WCH4BsQdk z6Ybry2ri|wFHv~X@*VlJbE@}=Ee#z6MBlu4IsO)o(TmL-2Uew`9se#L*P$%=2Z}sWvK$jeJ0&*tu~f1}ILb+qZ^D+KL=tRJ7TznUIs?N!{PGSg^rP zjX7DHmExE|r!fQct_bsbePEai!-Xi92IP(o-S1s{FB$!k2wrn$-nIpgE@Hf$iZxOE z?EiMye@n`Sz+o5_AAw;k7o~VbSoNDzss<Blemg=^HD0T0EC8#u0t4G)mvxP(rH+l@^tM@sLKw^JbFti=7Epg;1@NCPmz}zM9^uV(ksREv6m$WT+LPh1S4?Bzfsn z3HBxp<&uJW@IW*k{@&(DmNy!d_&1@G@GtI<$RCbWddKqR_Uq!0CoyOWNo?`-3%qxr zhxn;PJwzpOwef)Xwxpo7kbKez7RY~0sV;e|FGbrU;6>{}ij6O5m#nlwkgP>-DIiaZU%>5b&V`EODEWWW-%m71KKAF&PVz|o^fSqF@xT=F)36ME?Pa>r!< zto*cXmH7wiK|_0OjCZzAy53bFLC>uTTzd+RO+fOo^V_^ncgKBGgx<>8?e3J`o_l%C$q97?}{SSIVViPAjk}S2t<= zuvUZ(M61Ew$*|q$NWwv%C5C%*ogQ56Q(_@Z;y0RuCOpYGnXU;HIc_*O@d%CrzY?C zPfRYFr$*HCRLrWF@N;jMGVM&@KoWMypB%sS*?zeb7-}R+5*-(k`G17G{x^LfOj1h<>ms@s8 zkK)j5IBG9ux}8*`nBNo&Dm^71lY0n&AldT3#MQcV7wYiUBCmHL(qTM~ zzvRwxWz+f2FNa->VEFFMDK9Zo{=P)s&rjyh^c14~Xv;U1q}?i84=vcoVpQ%Ken>es z^|-1EQ}CpgUOxg_t9+6ZWKJ|^k!wUBT?i?|h^vw=bwNf9!dK$^-R5`c7?d^dM3$V5 z{uFMniyXjMgLd2IRtbPf3Nj~?r1TZqE~|}J&Mgg`zxqXfl4bl^gNV6CFY~MoHN+uD zR>#{ya6Jk}BTvSy4}@yWyo3WccIhH-JHPBkaJf6JOF53nlU%BBf2&$be*;7vNQ-f9 zshXj77m&OydELkj|7;-|x!A*hfI%Q#Oe#41m zZhX4Fp)ra`wIT>UtZ5F+plRIFiJ$8LXDD!$;Et8ulb^urWMT@sfnE4GNqZy1|y}y(b=7f?R3G8up*? z)4>U(4Pif3o+?cK;wlU8-|b$qPWtk21^i9=jrj5F_I6PNu&uuyl^j^D25|WwBu3>s|Yib4aFBwgZoRVpo&kT?*4{$T>&5cluPn0#+=!iu*~tW*w?q&j?= zi(HN9m<>9+gv)q*(3R?&fm<3adn8kGGmFiC2VPlUPWxvL)nVA|gLpZ?*#VJlgvz%{ zt7kKUT0b@t*V(VO=JT)u$$^~Hs23S;;cR59NM~AAtSdzgss3~|y>-F#1 zzzN<30zZCUofBXHd>l+5WQNZ7p+e>UWl9C@dn2eorjCej;Av}e=M<6Iq4U|61>v^m ze@l|-L)DC;yjSWe)RapDT%qafh(638by`q-8+hXf%kL&<29j{HS$wKI)35NJIzZ4> za7P(Reis+*T~Aj(H>URBU0 zdF{DdeXLJd{W3B-i8_`vws$#$!*$u>cxL)YsLlT=aA)>V7AWay3yAimrCmQKb9C6{ zR_GJsGw@aq|jGh!bl!bNGS-zS2nS zfBsSogUIckRXm~Ap$F0h#-}7Z6tQPzUsM7;+6#$`{qdmmf%p=>LPB)>x$XB-Ea`Ym zH*BGJ)lh;B2w-Xu4zHk~OzihRRQZgVh$3r!`P>1^3$_8lWdH8D5G7CDagV_?w2+gK z3!Xi4{47yNyoEOt`BNu%QHx%BFfl}QyXP990sn-0YqI2ts8e8O z)$}bb_fW#}`eL20X`Wf z9g1kA<|8X~2YI0To5`oVEucy3>^jUCkPABt1pv;3lCCKxPNLiPWjv$ZPGUFio8*@N zIUt_7-9%ppDPi2U+g17tEO-m(Vag*_1}TW+p*Evah1QjD>fQX!m6m$V^9zsSwB)-cTWp zl7pDM+&#h9bsQol?1`rq0VI=v`?(WzW^<3Zx2X=Oge%yZIA(o-wd5@i)arJc3v;%g z3m+Rr?PJfgN%^_x9?SJN9{oiDZ31qHmNFA&Uj>yFU*#?N8;OYp1^&_S-e=w}UMf?H z*k0JIN2uspwpnxc)Zf$E(7OObbN8bXV!4b<6#@LA+^A7=LjA-O`ILJl2`+s#UXUJA z)zF*qZs?+?zsV!7#P}5p7{0>+=8JtEz^tc#1|kb=?RZ9=%oUlq7$u=UCh3~N)it=InWhyuG8JKR!ZedAuQ zFpgoWDk|Mu`V<>$4vIITyi67jp_#*uhVQ|5nFhfVsHPZX<>=&M!lzs}S8P5-UYK7W z*D3XEi0*MpxK8lBEx0JkP#+HAbAV_i!f#d6{QqHv?GFL14=)i<=oq$mvBk20b{t8l$BAl<6>p z#@0V)ifJY=WP=%D^|*pHrfxI+mhgeOU_NJQe3AidgB3r#F$!kQJFpRo|&%_}tRYt!>IS87ozA@s%jbB%9V%Ke!3gV4Dg7fDS z%O}q#dYa>_*;Yqifi_#wKW?P_s9W2*Z7^EjHP&1+A{H=q1+5z7%6OeIay}(d0LY4x zNaUbyMz}el%_!2cISYuTB~9zQE;*P&QJ~UODoLxqM?5r!4pUU(E>s`&4*!?hsm|W| z!Zt)${x-(cQ^v!4!Zz`fY_%K8)yGNd(|}E+Nh}3X`3UPqzWB__qbcWbwHtWX4EIYa z8?WJS+X6wTTclCyC4nruV{}Q1HhJ+Mk!LxhF7>>lpKS3620FfOX#-4Y7oH2@IUsnkIDkr*a2` zRa06BYpiL0HUgy~M6Po?@-6I?zM@ixGL1kBpKi0q);Q}{G>K1~;XL0P#t|uXzH0}x z7!Wk*67OE0v~AUeHx<6b97f4z_dy9>xxxDwmS=7p1M@BQG&T2(M^5<(b)#X$x-8S41CUvSI#`!zEBn={old zNBgDi11s_g%Df@};=P~?QWjyfdRef|mmRfp`7=n-Hsa~QgDymuyBAktoE8;eB0z#B zp`6Bir>iWZgUNgMr97LH+{Oe=tuqDiWHsR{$);-814e5YU;4zlw<@^}Ma(JhRZost z8YSh4o8YeS;=Q4=(ba+a8`4l38#NWt%+!Q|u>;GMH=u{eJEZI|{-t(!da%_8Omla# z!@QSny^kZ&(ZpWJ{*bwiI-hYC_|yoxhh-d;oF>hk(>1Jpmnb`&UB#lCT|D~@sp(R^ z_;S2DYn=|pGc}fq>oi!JdpUXRl z*po84!I(&g4&NG%;Zv4zAaF;{DwH4cEv-yw<)GrVm5*zZ*hjG^3>7y_-!bR-X;~_; z+ymVgL5So>)&j&0>>TMFw)$fVpPlJ-b1Eg_kjwJA#iWYW+mZU8B@k#j1AfNGHg(C$ z3ehNZl2(YDbkFPPS=#2%WGXCjpejoe0LpUD=Mk^6F)Ist!)45)A-j}~cy7Iak2|SJ zb{}bOJjA=rj3jSg1Ku9kF(c)U68v=bn|7H9!MO)%xm)!fDMq6i)Revu-nAS`Jn-J)mHv+wEo=QPFC! zY|M+t8=^Gpy^EC2KB?-ACSpIHRKtJHUn{(>X#B#s~?))-c zo8a(J6g*3dxo4M1`TX$AG8*l9r^hEqHT{-t!wUZ1XN-3J^(!H!pR*$9J`J{^+N-(k ztBV_Sx^(qj=XQ((eVo0jXY-PlRqcricEROZE4|Nk^YwaBMPeK=hVDdtqyF#50)ck8 zu?;f@ag4f10z^`jG?-|RGIM@-o>U4296Gj*>SDtX-X>%&%i;!cgz5Jetc)MIn|MV( zFLQM#2M{4MUpta`TJ&M$D!T7#vbY1j{l-M;1t(I0JtVC>+(7?6LPQDwt%>`wq_UN- zF24<6s$O(%v4;~=UHbxkHd%5AWENtO>^R)8IAaR=YmAhjgGb+no3KNV-qV1t*Xs$^ znNAfM6}g$|D5u?ur^N^Z0h%J2+^iY8Pu?zCovWX6fyQrb%wi|_zYctJG;&pWCm=}X z*$jv7iwT}sYJNv%%4{*IfL5e7Ec5H%zt?C5f3B!5yo8S|pJ z3GkTmbq_R=;F;(8e6M$j813erIl=UvkU-be3L7K09EZ_YKW%q_43`9FZ6-un%Anb1 ziA^WmB_hH%ypa3EpOwG5mRgL48t8C_6UJKcNa8Xy;4BQ z7Ke3whj!5mkBghaS16sYEn&)%8ifR7oZ>)xP#~T#;#{`fmO0xfXW<}Z@qVqb2bR0) z+r@%z!e+9sD2Tg|8RRSM1vPuTpMRjbGb3>oe!>>meBG=;9$mt^ZmL*~3)2O5_qNbh zzf7Ac4Seke(ZdD%xT@kU_Am!(6jWFA5jG(dJfOh=ceYx6_Q^pT7#Lm@MNklH;B`!Z zZCjbWqGzFMv0E8U6o^5ex4tr0jT70s{3F)M(4O*m<0wY<&Sb#*%QW17gz(f;-loLU_;<@J&A15#ke?c!Gmlmo`~Q{ui2C3x^VvX{q5k%QK` zJCDURlFNzo3QZ$M1&(%!22W`-eEf+*}h6J_6e&EKn%Q#g8pDWzW8gOhsEnJ~Lc*dv^;Iax=a2`%@7(V}xJpt49b{wEeo)0X@6~n1FRrwGd-0;54qJ3_7oro;BrK^M)5p zd49v?`8z}*1vA0@Lw)@x<8fquD|{{Z^r4I=Js$qx0dGe6$MYaW;#M@R$CAqLD3K~C z#S_|+Pk#>F$LEF2DD~omOq+iQ(o!jZk9<8#18PhZ_Ael2l?q5Mw1uK(@_wzs0#w!{ zl8KKgnlo|7OOj=*#mLu3349hJ0Eb;+HJZ}O&4EEKL6qKEp9g6N;J~cl+pDhjAun7C zAW`<^J?+6TifX2R;Kw-5Hh$PujmXd_*<$pCSGm)5(b%=~yk+PlI;JIiy_6qvKlAdU zev}qcsFRkTWgQ9)@uo2~ICWK{z0^>z|BskVZb7l5uEY%~A*g5(RQ#_dZdX_4Ubis> zp2vtH``iLY_F##_S@rah9t);)RZ~qiO40YR$+z(PF-wcrJilW~crn2p?_PUtrDs>m zVc|e&3YTV1B4<4~Ysl4NKD7?KI@t^}^zK)=o&H_Oid1m8Aa&FJfn-W?YG|{!<@eD# z0i8{P!&s~l9a|=*y=g#&gj)tSP02;6lxjMp!l=K29#ysP8~L!k>+Y6L47A=UqkTNB z?kjRR;(z=n?!|gB)y=~j7gvTqYPHyAi;dY!-fEQl32LJj^sAG2SN}vYHa?H^kLjBu#Xu<}sam#MAl2v>T0`t1$ zd!4RXutw8J3x5)tW^+?{mxD2`|DuC1^`=ulw4;YOpxmSRMpf-BlZeckMuEz+-*2dl z?zIrwRh>QNxIZ$~`~sBG+WF+fsd!zO#=LK{(skZV`LknUtl<)Dn|htl=3L}TOKIS2 zmxPTkU~h9!B`FlyjSG_150RYS=BGg@GPeqNvwgP;f1N272JpQQt<8;)YJuC&1SmEP z@H3gSfLPHa)d!WR-ycMX@oTd)M(7=4vdYSpMDI(oVGG`JC&T1`WX!s6=@uuSUHOnASU3&&{mK#QOLJWM$XRL#$PZ@%?8l|GZS^8HlD~Er#inM;|PCqgK+aRDl)O@M4eOK_(nP zjz3bIWx^u*pP-TQE03pMpxH`QZx`x%)7b~mW<}-5@1gN%qe1PbF*H?hb&Z)N_S$1|{irh*VNR`lTtHyPCb&w?&o| z&8pvuUj^z>Oez~+Cz0x16@QEivfnO9ny12+x20jwLD7Af2&>X_R0;eZl`w242Elyq z)4SY}Ht>DdKaMPq%+d=R%PG@)TR?tz?{R zt7}gGbkZx+3#cO(6`V#_8^8jYfIfgVm^CeDyq8Ss1V$y42$~V`cr&Zf3N3DfVTzjxlZ)^MQhyt(T7g;{Gn3^4_`^sI25;kww z{(H{k;E_lQTQ54@3U^6GE%DCi46q`Sp1ViI<|c&(k+17&X+&qNI1wK5zV`5{y`K?N zR@FFu5zE*|z&kD06iSQ|qUD-&(n42dPZh9EOyPmUtj41r>p({qUzQYfuc@N~#y~E0 z^;li~x1C=4@yTM3Ph5eH5xc3EOS<>;0?w23b?%p3R=w!_Qqhr3!km63GxmwrnVfMm zTz^+EgX34!#L86AC0!P@kny~UO10u$S%a-&iRZT^D7Ru>8~Z&5!PTkyiE z=1$Nm2SR0rqRy-jQ;(7j_`!76wWz-p7dwUG#m*u`q;8e&y4ADsH2dhfL-^%E13u*p z{Pmq^D9847%4dfDS%r|~*a;6)`em^X7l(uDsSG9Ma<44wgPSph4=f{BFq%rkW1WCg zKIEten}H1fRu^sM%Vu3C((I`i+;4qS05%J{RRCCd+OPu4S`mC-j^*~37x>jIF7^9` z$S(5S#xCm6ZwB3OBrjeF(t$w&y|sl{BoY1YF}Wq^gF$8U{Uu|2fJLiOzyKpqrP%oJ z+OIKOWeNP$-T#$1=YVhQ#5VIciRb`sTt9ElCm~=v$b+K z{2W7OUMcs)U^h5^AxJOIQl(M*;u2}UZ=~^-dD{sE(d8~jQZKxmt55_wC+wuZXjPiL ze)iou3e;jp3MA-8`sybqbw1M>iNN~`Xv_iP^$*$d;tVrd@2F9#%6O$VyLDkJDSi+V zTk^#=z`k2-WeQu2IXp(2M&OIKi+^0gBC?hs2k6Z_LKn?;YytT(7sRTeF2XD=NP{ZO zR?#tiu|yX}5RlcnQpK>-clFP$@XJrziT=LOTv3%p+TaUVVpt2D#XkB zo`345_e2c-198BAVpBj3F3l9=^3ba^iZxM@=ifEN^IagNmJ}FcI)z^?uu&jw?^FLA zv4`jIKO01T4Dc*T0v~{H#(zM>%FCIMgn|x=#XfrnPJ|cP&n8S17lHW3M#`n^-@Cgc zaPJTsTE6NnEKkFNzO1k(kQ(3)5}I~qQrJ5lOuRwX^sfOq7@l`sYFY+H#1=%n1ABoR z78nljWM%(^{uF7LR;AS7%}mRKNHg?27gqd9KZIlLh5F?e0EFMOn_&S9XG`vsra4?t z_p?Xk;}V*KHX#jaj0FHiicSA1nCQJ{b2^7Wc4%Ri1WpOum3SwurP@_?Du8QjPEKVk z_6ZAWlE8yFbGcjDz%~qI2&1ViQVHl=20ho%Cu*BJ%l8rPt%ttdC%G6Q0WBZe&76*b zzU;M@dHj^9Xii-ZI|!@s4OT)`V%)m(t45$nM+rO4avpS??qB=L#@M)|VLr4T>gN}h zdoFTt!zU`)I?nv-3IPRyY5>J$g_T!Yv#8)e=Qy>FbPQS<5*=uuV2!+kiIcx(<;mek zdsKr~S#i0ct0}F85fL@3%NcYr>vnb&=DYo5(a2oLT%GMR^9vcf06 zA9xJAEQ!IZ_$Ty@zso0mf|x&Dm>MwCmkpw<)j$3PP)LMUWreJ zBp{zFWA=|4pY9b|L}2{hwjz`xnZ!}XUK8hJs_|V?(fu1`tXym!ON?V9vqztu597tS zmXj+(Q)2fHEv-=bM(sDXWqH8&2TQY=0>_5Z$x(O1Mk6!Dx%Rm{B_}znT&I$1A(Wn^v7V4j@&c5|{VA|Gysd1+9F1s8 zS3ISwsU{+&NRwjGz$0TKr=TGdRyS@GmP!8)>YYx#@A>>h-Gk8p3@=nPcn$;(u#h8P}Gzt?l;r;VqCy2t_DyH#(=Z%IyO+cW>ym}8nbsEaYZQ? zzjJ%;lnLJ*VN`h`e@p)SrB{Z{|NdSn%7b9y5a&<1vYjGQrH)GHoy4OY;LI#5W~r*S ziXBRR9}8!h2DbPS?@FE_KlS!mg#NCTyb{UH#Q03eko&OFO9CpK!T^gKvUUs(9qb$t z<#I$=fGygZ2+RmwNIliP$p5x$cV;lTNFh)#Dt|m|bJvz12AB9+I}_-+iNrIHsx6Z& zPt8v6UnJ3l@F_&J!W^9zI3R$5 z^CkLv7uK7Q9f)oPx!K?>$(BGR?o`=#SWPLx(R#^UOrjJ8uTq(P;yrV)1r1jCn;Q$~ zvKDC@6swvR88kR)nG-?6fg06VLpp z<#S)JsJ421`FdFSq;U)?JmKgg;D;bxt5~>`$8_o#-{?!{W*Ut+Y@YG}N^m>+24tGE zG(xD7RfbmT@#@|Kc8GcQNpx)U?aHD2PE>K~2yrHyGh4RNBWFAWtTEN`K^D<|W*8K* zZwzbA%(FJNqgcw_i0C=g>w_3gD0)GoJVzY_j@>=& zRukDx?=u|W_)Z&$?V&Vbq(iB{l*}LYX@RGh&+tG6o+d54=H>-<3?5w^!_e; zk1T>PpPKTO5eP&B?@DK+rzpM`CD8G1&-teQ$B<@$Ot)#Av7Uro|A z*>?Xq4LsFlt`l9y!TkuaKy%2_Y<7^Cc+Bkn+tc@Zwm0J4H$|mAlW%mw2gvJ5tU?J) z@X&l5$nw`cZ5A;B+hV{$y!12MeD2ILBBMp}W{@{M&$an7QctNxmsy_>j-0-fN}d~Q z0258G76l+xz{@HNX~drB2xp~)2VT3f25^u+v3tZamQs)#vchrzAen5_@!YvpcqY8_ zJ<5z%hj-A2S}0DHbzArGwb|Grf13j`ph^}V#g4t3QM2wa_eX@0T{P2LkS?3pWm`No zx$`h^!g;zo)5X`_WV>kZ47Aq8X#)m`*dd)WFxIBDtJ^;yM*Rf*ObbdJva@lGrrjBXqDzJowBgJ`jfOHN2$hZ`R# zOVkR;?OSVeoI69F;h)k|FS07z>bN?hkMvtjpT?GjA8HuNsdERdJHJ(&``y6O+`$ zN%~8l8Ll#l?ik((yMmcG?w63ZAEjGl`N$Nj>_AU_X=Rv2;5ZUsEO# z@MOAgupa@bt2nK~4TsnXdiWVIYioMWTyFH-3kpm##AYCkv1KO<_CuD)}EY z^d%6N8nbP>&Rtwd5&YH>&A>%fZST-cn`QyKyKYM6BN#S|_QtnkD)~vdAr795K?h`E zOtwToqs&ozET?+sCLoEPd(XlFphU-+`g&S{}P3cXtP7oQ(7(~F^{>k$B9`9t}} zS{r)i;4J%x{YZ(BMo2vx(rUAEz`vV`XfsANXV63nyl6&at%vaz3Qjzdk7N}vjd_IE z5P%ypWFR3-Ansoo@j72)ecS+VdcSp$foVN1U+yI5BO62vR?)T7hNyF|7o64f@3&Z- zy1gm@Jm7vkQt2RoS>G zW`k8{!Yjar{58dE1g9@$C<><|ap30XsB*pslV2GmJzsA~V8|dMm3zD&NKJq{>{B5P zi5SP$Un4e@7u}}7a7fq~1Na zI>8^MjLED?$!(o)s>GxT{lXY~@|()oZ(oQ)iuE?Ku<>pPUd&!WgS|h(W*+khJe}O)mbOI3eY{HBEA%c{?qaF2<@*EiV%(7#P8C)Jl z9!>dku6v%e+=5+(cMKWklfcU!Hu_wH|LI0wUwraB-Y%2mx%}A_ihjo|?r-F*Ya65B zffDEAi$7Dk&asc1W_-XL9@kL)jR+6XX`$k@WN@d*JOo(;LsxMc4F!~i>W&$6^oH`y z{E+riNJ}GiP?rSru$e1#Z`J1smP}oJ#3TSU&R)k$DGCIYa`neZw6~;o1h!8bAhLC6 zQy#>+d`s}R48Ec*qteoaS>2oP=`=uc?*H!wJ)Z~+bb5pLU%w#1JUh@9p$@1qoIdFg(+a|+G@ae;yU;Gdw!!9mb* zA#YY$me$)4jE2ZgoP?2tvHpl925O0gC$1o2Qzfr7G0Cq7qD~}g?L(Dp5tE)Ic)4d~ zZ?8@9*WH(YC?l#i2p3J>$0>s}_(8#Y2>w(XI`NVWP0Mc5WBUi$CHBEMt2^Q%ntS4u z=Jn0ZP)I6YR(X<2x{_2VxuZZpb?M-1mT6dAE%XaE#~sFV!yKG+Io*k8$jgpQqkA(M zrUPmX)scGb9izNBoR6)LtIF%B?7Tc~dzlr6c4)!>Cg%nwy)Fh0y@^qE#osYvdHZ*} zNE$QuFGD$AQ;0Wjvf+Zf{%+JIa*}9#im50$9()48Wv9GqP8uvrfRk_ch=6!T{ zK85`lNGNn52(9;bl^^KipeQ$j(*ZXd_22634XHQuKV)%4UpWmlBTVLy%?$3ORzhVa zMlA;z>zpF*QR>x{X1rXr4xDs~q}G)0eb1j^Ti_1gsy+(+TLmSMU^i|?S<5dE7C&;r ze+cDbgaMzcUO9@ha8I^~Cq=yljk4avhzuXN)}l#Dz4wn@10GA_vqD-iJrleh4AR z)GMIE_bW(zBnaehEPZ{bT~Spkm9j-fc@HpJi6k)ym^ua1-f2>(Z)!+*pgo12@zYme z=ny63%Ii&vm^~P{6g~#@3nA*cWxA^~rXs(F-B?J*&n_ zj{y-pozIY#QU+I}qtqK*O+JqOiwef0AQWYnl6jh%<6ISyK`+C5cT8n*U@mdI-<`6g zgjX0WWe!X7mA`$YuZi-*;4_UbuN4%-M@iJvpQlLR zTP~;$dsJ!7#;^((WCTuyJ}+De4%<=Rrc>s-_*F~glH50rZ|A>-~oCnF*;Y|V#3jr)N=M5mCk%1VDQ5h3}1jpuiOPt3_X6@{sr#5+iPfPVN7(>OhsovhJ1gb52MINprs-!#w}e)v?cA zaPhgZrjrpFmm{F+`5>fTGGdO)d@~yWzL@#3#logLL#D8CGM75C zbqY6QS5Mh7Sx-G*jQDd1^q?f-0=(H0?rlvho*SjYl5=F0;xDnwd60KaXZC@nmajw5 zWY?szY7T0Dd8WGvpbc+2JXUBeSroA+Z5-kSen=itM(A+g{HhcaXH!TX__ty|#-j4Y znLw!N>oHvJW9MvpPvl{WTFR4>=ehb$699g-Fd;V%=%41QOF0OsuTzJe`W!XOk?RuCpLdL1H#AKd zE7Y!z_p-b?<}X=s=!~Am#9w?_0m0yI){enaKgkK4krQG*qy{xX&A0c-&<=N?XEIRH zN9Gsj%B+a{;;p_+j&~VrQ#e>M#rsqOMSW{2+4E<=r1t2Z=4ALqhg}s&%!fV;$Fc4- zapp-dm`)r)gu{wp!XN*q_OMia08kQ9sSE_A`??nMF1RjHM0NI}B;ntm>?_Pq0qqI* zXmEvS8qShEu$v}euE0y?rEqE(NX+-E7N?1MXF)uPlc{_+a>>yw$0#PH+Uto4`egiq z(Wt1W-Qh*c07e~C_<|tACvvlTFZd1dFEv>NK``mjRcps)q(u5F9(dx0Ni>OW_lCvi z>*VZSA-7zyEg~*cH$2eQD`8)npt7I_jnjMzs43dj;c-5gy&PlbJGZaVok#8Te#n^7q?JS@lhYMU1oz`5`f>y!#sMx)MupyiWSRnxYTxZ{WYgR|%|Y41)&_#1X{KvH z2&VeMi#MSD@&W+kV-Q3E89BG;YKI~EIfESBw5gW7Ssr-?qn~vPT{X}A9EYYftKHPu z!gw6_%~2vneg-i9)1~_vWlYarxDagPs4AlJLsBr2wGazKc^y0_ zNv=gk*BUC-9?s%iBx1}l_xSz8M**g8@c+C86&zR|Prmhep&Hn(8GiJ{=Bc3gqy!pt z3UAS?wBca`BicJ4Z>&=aB;$ap5-0Z*B1XYHRzqz)JgCLlXcpGzEoNv=D)Meo_EPe9Ia zKyV2>cj~0RUjpAPYGF+ZFRHK_wFwMmqX$G)x0uGoiH;Cz6;gE1j~)07M(a;GjDD;{hyxb5!W$)xP{+?uSvwIP>;?w{T zi;7V(l^7oTqu1SgH?cMSi-3T6TD`02Emg%k1MytCgZG;r|)>9MXb$C~^LAIfafpSy~1hV|+DCHTa| z{OCDqZhGq*Z_ChsDbLRA_`ZNaKm;voUCgRf=X?D<{rJpaU?X;&4-6(<&Shd+Wy3-Q z%b%_dmYrlXfZ3$L6>Z{aDDWuO7KY+It|-J+ZN?{{LqrH1M6f(^*u;`PDjmR=Xq4)` zd`hBmt`!*rYMmak)r@sW!5qXb_F+vt03_u{2zvgIwq7x*zn!eIH8N05+3Zp?eJ;y$}g`FznIk^_EMDsD|FVlz-JTxCNupma<~BjQj6bT*{2Fjy{pDoa z1Y`N&xTitOTpWG1uJ1oUl4LZdCLvKgy) zqJr58ZXTCP@3S-+ArzbSb2!ldAOq&c^^Ta^k^ZxOCF#rxw3}b6(LLjUiz_LG(y6+z z{j9xiYX58|Bzr2XJd-t%Vh0PzcVMk&(O6rP z^_1`M1^xdqIDMvUwWZp@Gx+t>(B30Nmb#-s^Wgi$B_z}v7ZEmsz&O)JP+&xtb3lZ; zWCPADDlVM@ESZ856_@N$ezKebp6058$T&vC7N!XrX99ov)atVQYj){MG{Yf`Nkm0& zjeab63VQ1VoF~qS&T1u%hfmQRwMvI3{(8EMeSE*!`y zLJq?(_1K{;ajxju*HRc3?etK79E3x-moAAN-g5=R8-QmWskPFimEmR?`sKG0P$DNT zM>20*Uy7~W9Cdq;M{zCM&4;p!m7W;`TOQNBrw;`NEA}dB!&bo$P82fTr_`bP9K0MOGr5h z8p2!q&^vpWw_^mGEE{a2$RG-})K3VxxEU^QSS)hk#@W%f2~tALEz);}6pQk{yawWn zqbgvZ&3LW=`T&&YvDXOkS?WsMoJm_L|EHI9=vQ^Um@)_=pLpp z@|aCt=uqM7EcCAd)2vT>E&66n*d5SL2w|v^#XLzd4;%XPo54|D<&u(8KS9j@OhZmM z?C}CMv3oPSv_^x1AV+7`eQ;>jfh!&b8|NzOvL^XzO- z$sKM}7b*4k!lK(w3{oYbm7!L33hOB!wYYu=Bd3L#VOH}aFCxmVaW8y(5IB9xxEwH7 z>*b!B;h)xE2L{Ne<7OSe@sF2WP9M+Gfdg$8BOSuBQHFZOGI+|`@s{=s_#Fg|@r59l zd~qNBie{zdLs{naP>Iaautaj0aQ31|&D1HR6mS=n^Y44!x1oJXT4yRqLuh;p>{n72 z-&T1?9$pXVHIiP&<>7c?oMLQ+Zx-YVaXeWaN=7BKf|+6kKEvEO#W)F0x-Dm7wa+=W zgE3!mI76*}ts>T3nGmB3iXVk)`@o2Gr$=y#yFd-D;OtE~t%4QRc0)nEme}8Z@g-+5 z<(UNo%+xv^k{K9EA~Ep z)VEP!!z6iQGb8m9&(C&~V3EyKUBuB3^db-9<0ikl;I*7!ZX+`?F`d@-aNLM5YWso$y{UwXillN|HXjLt1tPH;aAAtRoa|F7WuM$ReCR`W+0 zCtzij*;5{_+z&)6`IbU6flyF*boPcWv?OQkLRB1N_gAWv(5UkyHHv~9UTLbJN}tZ! z+y&!v?!Yk1nq!MY2nc#DWcnfXKo%-BC-)cQq1-f;QODGTzU z5Cw@p_vPDgKYQYXrGjEutTLU_QbMWN9I0EuK(Ma+N49+F8gr70addcn@2Z4QD{8H^B}lQ)6zV4y&iT){iW^?I zpCSk+CTxJ!di(6Jsz*=Oqgor~fDb~uG;GS%%T8sXR3qe0p&fKmtB|9l%`7VsPgr4Y zGR8)KC}#q+#t!q@c3&1+Y$IK()`N2M5j8?iD4!j^{yMfc9qkEp?o23MzBT-NdLFtc zCcVpF%`&tnZzj&WxH$uMox|LnF)vq5#XJv^tMRD?^vR_!0FW{^~W^ybF1gamGHh0ro`@}cW`jciK&<=ZlM+Dx8#i5Ss zE(d21us>}OqK;VB?77_*W&XRCqq2G|6{r_9Kf+d6oag_3qrlFymr{SJ!|PB`<{j@9 zx1a=MV8_6}fEtmLsvPmomKh_2S-oU5rPFLsKfWH#PXYMcmOL@T= zZ6&!~ZI^hXloFx4=7Qa>8dLwAoMXF6uo4wcb~9s~xn)8fkz0|~DyTa)=Ze#>O$wtjERS;~r+a?N`VMQ`;Di&`5!ERghzLxh}2;?7A#sP~4na z(ZTV}Ol3@z+*1lbOVyQl)P1|wU*RLhWH$*AFBrKi(1}x!2`C&;MZrZo#8v4vE{NR6 z4<2$=2~Lj_Jg2rG5PV^$1Yw*~%PL_M@)i0Kxf(BBQq8 zd4M}$rL>61bD+ra2ys$t}z*gJUgk;PrPc`L7K`Y503Ml#OY(h)_ zHK83wL3WIy93wBEdYY3UosXrs_~_#2Wqm4$YzB^^P3*incLC2=i*NRfpg;ZLw1^{Q z^CXo!DN?RONXoQS0cNk1|0Qqf20O>*64`FF2r1DEwXK9)YMUq8foN1+otjVTDH1SQ z38!I!hl<3ZJ-3e%nuzl%AGrI~hyshlbfB(lNk;Asg?#9rvZn<`sd$es^;a8s*A1|| zGaGdE**cP*D?7%djfmJ!EiZhx37A??V;a?o)!M%Zq1h)+tewg%Z3Z~gNNZ$2$-nb| z4c9V#Jx+dC+;gtdkS3A>3;=Mpp25@sv|SzGY6kBjwjgpO!@M~gMduACb9l{fy_(jd zxP?w!#=u+nibU%#cLk$~1upORj-I|Hdqf`iW4dj37+6q?jxJvD33WB}^q)Y(R9fiC zV5?)c;bl?O3dN`bO`e<7uC%pzdQk$|$WOnk(PXK6*fjW~wy#s#tH_?<3XDh`Z z1iQIqo1;n`w3!@-?*#M;Zujc0&TU_-F^JXe7@0FYWLdFFAUcj?Ju3Z{iQYI zoR}5l5TuS)725rc@0iOzyeY3{CtgDVhNg$nFswx3k*wGj z-=~Cwv5Zl=vjnCvTUwt5GD!b*K9`%*cQM7PvcQxrTCo^f07c zOrC^Py9Ai^PlgLgQV7Aj062Nrh%Lv-s?01jFb%oi25_=iV9n;Ho)pB3+O)tvF;^P02Mb^4snFBu8mA-|C)RnIOMdav_)cD9;WqrS9Z%3KzavYGmW91pRPn zIYf2@$emK(7JV2*C+0PDBcSkpl)}sNsFYes9%Fd^>=pE5oq@?s$yzvGK7*nWJ0xl~ z8-4MfcW54?7xYPAYGVFv12e1CbY?kvq*e4WAc`Wro7x_Yc?G#nXZ30(xeVmb#YKnVr z)9UUS*ff`b=m*zo0HgPCV^JlLgHb4zi1yh}=Z`v`!}$Q}M;K4}*pZU!_hFj&hjJ_*H~5nL+K3m$JGF(Tln|93)CVc@qmF z8vj7HQw6W6Q6vCc@#Bo50b?#|NG4BR8iZW>%rXt z(Yet|t+g}aNg7cXfbBC`1nhJ7rGiKz+!cLwpp z4-2rYmzFIkRz|lbYBGa}`Uog}*OX1Zsik&g4k6qAM+O zpsq!`voP)aMmj5EH4`l1A?nXF_D#F)=fr+I+HpgLfp2cgqEPL$8j5JIW);? zY(xFkCT=S}(jmGEV}4~ttn3XJo&qcv=rbs##yEU<{OYX+o{QdIGQ&?1Ao{<;=Fm;N zyqC*v`D@&8N9B|eAzWR+xls8El>|ID?b?Lkjcj%@smltSFlX16hwQ6sYcppu3$srt zJPYu(*xQs|I}n{v;ZU?JhciL}w;EQeh?bzma+)4o4|GtLEC_v%;coU2!<2k<*@%qF$MQk+F|7V#S7V5JD zR*7A8cYkFSyj9Pae=0b)Yae-c$(%s*-Xebk8SsmI9-fpBWbF7hn#i^krzpXHGMik* zu8&O;`OIP>KPR&BB)!i<1+xBsnV%{NbOWescZ-a{<{0Gqs&e#+cbp1CF`DWPFM=fw z$E|@4mUU+G&8=YP4dYr!$5csCNu0}kZiPv-)e|E?m#m*;f)wzJ0M>ypMY9Q~IQ12m zl621haq|HYTR=3rgW_V^a(5et;C3#hi=EosDIlg%W&V4y+0_1JB$Z4bkEH!c+lthO zEKN*$C_A*$_}%3aOOb-fL9-=fASxzgz9wt z3%n5Wvtb5Ec#t@t&VYT$^CBp;1+RjZj>cl zm`V*_cPJxSI{+I8Z_~BESi=YVB%YG8bCrX^kCMFl@K!C(2rUqrrQ3&fe&k)aXqqHb z5^ZG<0gSGca8FDi=_{f>lTnR4GalpAbbki;j*H|yrS4tHi8WzLtiErh<*uh0BeAQa zFnhSTIxs6GwbLad6F8~UzPD(7RJVN=AzH3=3j=v1wf>GzV)!pL0j?-1>6piG?v70!l}(mj9X9u{a*{2@>2{|mxBFEhHG z&cKN+pXvfCH7`lN)BWeHi^o75Y$d4@6}@`cZMM@VK0@|ze#+Y`H>BzAywhw_BZNA5 zfsk{FP_E0JE&%*ENPhfLGfsSN#es3R-vplZ!>NHjg9~TI;K6RV_T5k<5QGFjD}j>- zbq2X)(rHMT3v&BFYqM9!nLC%eVk`Vm7tu09r4e+a`oB*~4TZ8gXThc}4pL!ul3g6S z3ht0hdUL!^YVVO2xYF$kN_#V9Vb6Nnf4$cBwQm7;?sD~L^aDi*w)>$8Pk|PWgUZ(8 z+GR=m?=2gXC646X7MfUiF3Ynxt3<&pqeizqK->SwzVVPprvku8-xca(cTR(xS<3BP=j*-VPK4-(Uc-){yQuG}sT%hu<4DEbk}anr6Ej_L{kM!M1xv4zOz0fS``f z>-VZqrw8OahM7{4o(X-DH2@~vM!?*nxr()nQ?B;07D+l!{;2`GZ1)@Fp>xJuH7-aM zkvn*rpjcV-z_atxQtkxvM$K!V{qrjWi_om*gYW>Yyrlk1V0wFYO>|)dEg~JTOz`PNByxI9CUhOxvh%4e=!67uLrtp z9Md-{Obi0DL(VQ}dluG@5j|R5ZXY~{6G+50>S>2-Hr| zkhuwiiXvVt=|pEA(LF?pGZBytd_@cG-4Q-)IX7w2xN=MYIz9H7r5P@(i3#bT1_#Yp1oxXVv|>+d9K7spMJ;T#W;trR{F#o0=>DEEb+wF; zbD+!*qjZRCXuV~Aq<*~(v&;Z~7NN_Cxt~h|hiblMT1{sXRa1W1Xy*f34HLVR$AuYw z1DC{blY>jR+s^}jE7q^n0hoPF*O!z*8J;!XIWJF5$5;4NP||^HO0pl2!Vg9BTr1JX zUk=0kStF0L8Y8oL6p4`;^T(>|XwzY3DGB2yIP_;OEKDfSIozy@d%9}HK2a$$95;Ob zN3z0>vB+m{Woop~-pqp12UdQ_ip%X~S6_Ux%tr9I!&5>Ezls7jnNn{`(@nV;LkJ>7kM3E%q>n-|jlaw# zX4K^D0Nh)1YO4^?;?IZNO`)6Myz#Q=zA0~0fJaqFODjC-E~R!o^#a0wu6dBtO^5HbV^WFJ~ zkOD?i8dBRPymUXs(ry~r=w5c<#h}|(n{o$SrJKaxNiIyiaU5`2tbtzO6ONUE(=P37 z5E0>0EcQ5ynptJICqGIElm-m2Au+}q;czXlQO5Arcc5Ms*F=JXow^WE2B~W_W#Zuc z{u2|9&m`I<)?oKD{Q3>3vt;SIz{g{FmgS6|{6L%GPLwnUtsvxZ5v4}Q?q;y|@!XPQ zNg#0(mJHUgY?}ZbLnF46kQSxe@2(vzp(K<6u6D;>U2B& ztJAK6T#n-mN`@G;mB2lMqd@8sCWO<^DdL!j8HA|;Gb1g~MVOjsRm?;_H_V7@Vj34H zNf#VS#_%K-29FkGHvSkqIt4I2EpZM#WuUb@+Ky6=7l~zlwT_+Qm5CZdf7c+1Iza@x z;L>x5eyM=bu74rCd@+9FdXVX>4owEmjGr79i{$*mCjrD;93mqdP%VPY7K#wXATy8I zPTI&x1gtr}1O94-73nhxCUv`GW~fAO787DQ@wU479!xW9`!^&hB?RRL-htN|6I(@C zMnWoIGKwzPya<>Cs%EIiKyk1Q6Sb3d&X=OvyKUO<=tw9Nj)4IoY2JnBi6>D#=1%lT z8j2|?_GcXhoQuP?z#?+o^A0bst@{%3R*>!rPx1cgP-~gRr1RvHc13H0YB% zAw`B`Q9&tRK8)Cxtzp7@s7NT7Ev z2|5Gj1qgf*E7;WSVS0SyffMF(rOJr$Wxft!UHsYCjDFYa14TaU2{QF7)v%K9K3)zm z`5$*YHrO0z+roJ4zxV^Xd}|JPG+Fju`dvIHZ_n<61JLE@kOnKg)u-&Lcds#|DwCX) zruj1uYetb+BM1q_dq(qf!~P+Uo8toZkL(Jq$@Iqb+LLfQYf<+84eBq6vjn>QNOz@{ zK*)TnPMNp!nGnl&?36>VO@Ij7lU;jzz6GHLzgmx>4Qw z9}GORr{LCC#kjo0>U+X$06E|vX0R%c8T|pQ95;-yL^ag}`yVQA0z9QsT?EPV_arP% zUsxb%u?F@_!-N+@40Nyb8=`oo=tzbw@l^KqJ5T|gP`Lwj2F@$&0ATfJ&FFVTWwL6z`7i*U|owh$;SpCduX;B3Z&hn+uK z(c=@zxYrY8OGgA==~c(F>MVlZT4M3JV)lYtEUcb8O{#mD+ols({BSnN_Ij%=0M}Ia zJWN}BjoVlY`3*%6cE{TYpsciK6>nBUT@QY+Re{Fh4N%C-f+nm!u)T^ifGeCF0mKPl z+F~$D4%TM0v0{1!f?D?eK%3!xn`_wicg*NC;%(zX_&DyeD1J?di`$UJa6Fc~uX0s1 zIP0m}G;(=h#Bt^$twPFfAVi~?^qOQQ9?9U1C4MI>SsnxmSVv%||6Fz_B78P=lx*O{ zxSTXUSRA;dZ*VM!{BL46F*LmPJW&mt1`6|{4ZzZ#Iu(!icByqqzkyR=f>M;4`-2dc zUx2xR9T3FKOtaa~?_awoA)P_2@X)6F-@-ukhpPqpKS{i`PjL_}OAo?qlC#(r3GPQu zL<*2S7)6iqY1N=6xeH<=Og{sPej55+!aB4$*%6K=vhc;xLd2aVdx#h7*xdin3kLHe zb!*bDJNG$3>-30mJ(Dpb-T8D7Tuxr56#iC4sF%O7)TryTe!q$zKKHr?!L)0Kvk-9N z!cbX{yzdkn`H-MxHrgtV@H>mOviFg*9Wd|=li2*lkDYA_hlrEk91`!CPHE}_W^my4 zO8m}ZYAt88uxmkbt2qfLw?X;G#8e`?~R%%xZ8?3L&Tm?CtFSfIMk~19rAy^16GAKzj zv8Q}LMsdT|Q!sJ*2^>Q)&bgQ$U8jBwwvZUZrju3tV`;(z&oAQg^OtUZdE zMor-E&L~PbR?Z3^*_&Mrm~MVyQrMB3=4&BR#JmH|Tsk!ahhSD+=@!e^gnOP>Dpmi9v*vT*F8^gWc4@SMZomA zG{`=fwgeJ7HVmS&qIhnHD>oqT>Xr)Gy@=7gMs_&Q906tune}och<|X#BV?vmFxnbR zIRRtB`*|XUPyshH&-cX+)J3765|Iuvs#4Sj=#KIH^+1gKXb|gUY%iCSDa%D#Fhag7 z&C;-}()>HgKeu1_*_1GMJIXg?aGq-9Rmzdo!US2CF@w z7vF#Gp#WW1OO>%lpf5{D;&3X6`r`0g{wu+u*!im?CO^OA!MB#BJIwT-cdLI38+=bm zT1>3cD69kU(?QkTT`|Vb5SDj{xC(9Bqef#foe)sz9v&UNY?3vzie9OpenEmkTC+Hs zMG(GHYO^TUMnr#$iFwP{(NRR=W}WAY#l}mRQTz=Z&3r}+GzZ8Lr1W7xMlt{!5;3%& z>A&loLkf7o`-E-n;Hk8Iz1PE4({TtlIXG0&Kw!oPHZo{Obbgfp$%^Iy#tsFKKXGuq zudV$^((Oxr`*fq!bL&yMQ_{12KxUHUAII+4_~1YjMq0* z!j~R-$~#daSe-IW|zYau{O#aMvbLiuo#u@Rq67ejw3!^ zBgOggT;RQ{p@mhca3GW!pdQHGT)oi1dv4cToB&_>S-aboP?~#i9?(j(*~9zcROu#k z4Ft>pI+id<$1wXXHGTTWc^!BmYSlaJ0$7GN^#%Mx2M%0%Dz zu(7TC*2lm_3FX5zxm$EEExFh~nX9ni%=E$wB--x(aSn!Oh+M+_^0r)FtS+b>#5t8z z@T1;Y60v@_cD>j!%CehDLF#MJ!vd5dpU_Q#l>leLvR=>dxq@Hw5HB>(yOiq?tNv#u znvT1FF8oa5K7p!%NxYPcIv9xbJsvazzRy``u)tr&dGoBA=5yYf>h8@uKxxZQ8;e5m zhi&uReb&GN(vV%W$Bsw%(NEM5ey)k*Suf8mrvU4zzVkjbK=UcvahwG+_5|-sa~~RZ z)szxIOpOPM2D(g->Y9+CSq^XY$s48>rX?+tNCE^29yyymjiHL2sm6^Mg&|c3=bD7r z#jOev<9kS^B%kVBtpdv>h@uNSf+WeX^}up4IX6|uEOm1U|EmO#oTF#pT!+g=2nw;Q z0C~v>=q$N-UE$4mo28OessHf4=AMj9`;GYvOUIJHWL55z+-OGT`+s*XR98a@3Ot3~ zlIc%L2mJi~*(QgwbxC!?1KHTlNzyVa90c&enke6>BbuUGai1=lp_9A|LYF0)w-B)uL!PSbId<3GjfL zCK2*5BA04Y&LRW2l8pRq7zj)+42c0>Z?2NyojcXzZySH4ql12O6AHkj@EoGv>zdZI zK#zO}ew;KkD@#sNu3l^|b{arV>OhAS3h}LfEW5jfaq>c9gIXQ+_cERtxmX%CRko9R z2|7N~;k@)CD2hpf(BK8h(Xx^&6gHNQs&9bH8I_b0)Kh7VDAR&payzl$0=Mx^#K83V zF79jaom3%%gUPsNb@Y{M*NB2#lyJDZyF{>#^rX)d0v(TgdNFQx zUflRwz4+&Xrx{d@fwHiX^gf>%XvR_~vamXS&KCk=DM5dBkUoSHe|qDOhpkda|3@;? ztFsi1$H__^tCA-jP|iHkxLdnaiTjkt@gm5ROF&{h1f+ir4l`3yo#zRCT>-(&`~l3Gv%2jdt#sWf0Y#K5Dq$abZUHxPB zo_3`7RkA(nn1hDUR_y1f&@>{a@$wOESyQ zV34{Hhdy#9iPf3JFe^?{N6WATcFj!!*yvUbH)f(ZJ-a#cDZ3!5PM%YySu7?>Em`DW zZC1!VP_FaSr_xE>0gu?v6;|th{u-y1{rX@#V``}IQ;HozewR0d%8EOrSleZ8@ZLYpBk z&yV+`txA^7?>h!fwxm`O0Ba99;<|9ua)41iKWu5W!Z;{>#Vw>csa*R?A-AAH>#X+;$nrl=|tgR4A4x z%RBR~r*?ZZY#!{NgX!_?X95{gMV%#YiYTElhD#T9eIaz7cf8QY;qG9pu8e+BWlEiKz zx}fraj8N*xD;l{H6pS3se$Wn}0Os)juX|ja(@3#qneQE?3elu@fVbZHeKwYv%q;O5 z1W$}$e`b$Y&RUh+7Y4%m2LTC#(*Oqx0RRCb0|5aAT>uaO01S?93IVwRV%-1$-jTyV literal 0 HcmV?d00001 diff --git a/gui/react/src/Layout.tsx b/gui/react/src/Layout.tsx index 450f00f..30cb33d 100644 --- a/gui/react/src/Layout.tsx +++ b/gui/react/src/Layout.tsx @@ -13,15 +13,20 @@ const Layout: React.FC = () => { const messageHandler = React.useContext(messageChannelContext); - return + return - + - - - - + + diff --git a/gui/react/src/Style.tsx b/gui/react/src/Style.tsx index 4f44a07..4f4306f 100644 --- a/gui/react/src/Style.tsx +++ b/gui/react/src/Style.tsx @@ -11,10 +11,8 @@ const makeTheme = (mode: 'dark'|'light') : Partial => { const Style: FCWithChildren = ({children}) => { return - {children} - ; }; diff --git a/gui/react/src/components/AddToQueue/AddToQueue.tsx b/gui/react/src/components/AddToQueue/AddToQueue.tsx index 98333aa..8bbd414 100644 --- a/gui/react/src/components/AddToQueue/AddToQueue.tsx +++ b/gui/react/src/components/AddToQueue/AddToQueue.tsx @@ -1,5 +1,5 @@ import { Add } from '@mui/icons-material'; -import { Box, Button, Dialog, Divider } from '@mui/material'; +import { Box, Button, Dialog, Divider, Typography } from '@mui/material'; import React from 'react'; import DownloadSelector from './DownloadSelector/DownloadSelector'; import EpisodeListing from './DownloadSelector/Listing/EpisodeListing'; @@ -10,14 +10,14 @@ const AddToQueue: React.FC = () => { return -

setOpen(false)} maxWidth='md'> - + setOpen(false)} maxWidth='md' PaperProps={{ elevation:4 }}> + - Options + setOpen(false)} /> - diff --git a/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx b/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx index 489b8f1..d1d5ed4 100644 --- a/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx +++ b/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import { Box, Button, TextField } from '@mui/material'; +import { Box, Button, Divider, InputBase, Link, MenuItem, Select, TextField, Tooltip, Typography } from '@mui/material'; import useStore from '../../../hooks/useStore'; import MultiSelect from '../../reusable/MultiSelect'; import { messageChannelContext } from '../../../provider/MessageChannel'; import LoadingButton from '@mui/lab/LoadingButton'; import { useSnackbar } from 'notistack'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; type DownloadSelectorProps = { onFinish?: () => unknown @@ -78,14 +79,36 @@ const DownloadSelector: React.FC = ({ onFinish }) => { setLoading(false); }; - return - + return + + + + + General Options + { dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, id: e.target.value } }); - }} label='Item ID' /> + }} label='Show ID'/> { const parsed = parseInt(e.target.value); if (isNaN(parsed) || parsed < 0 || parsed > 10) @@ -94,13 +117,77 @@ const DownloadSelector: React.FC = ({ onFinish }) => { type: 'downloadOptions', payload: { ...store.downloadOptions, q: parsed } }); - }} label='Quality Level (0 for max)' /> - { - dispatch({ - type: 'downloadOptions', - payload: { ...store.downloadOptions, e: e.target.value } - }); - }} label='Episode Select' /> + }} label='Quality Level (0 for max)'/> + + + + + + + Currently only supported on Hidive + + } + arrow + placement='top'> + + + + + + Episode Options + + + + { + dispatch({ + type: 'downloadOptions', + payload: { ...store.downloadOptions, e: e.target.value } + }); + }} placeholder='Episode Select'/> + + List
Episodes
+
+
+ + +
+ + + Language Options + = ({ onFinish }) => { }} allOption /> + = ({ onFinish }) => { }); }} /> - { + + Comming Soon™ + + } + arrow placement='top'> + + + + + + + + + + Burns the selected subtitle PERMANENTLY onto the video
You can choose only 1 subtitle per video + + } arrow placement='top'> + +
+
+
+
+
+ + + { dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, fileName: e.target.value } }); - }} sx={{ width: '50%' }} label='Filename' /> - - - - - - - - List episodes - Add to Queue + }} sx={{ width: '87%' }} label='Filename Overwrite' /> + + Click here to see the documentation + + } arrow placement='top'> + + + + + + + + Add to Queue + ; }; diff --git a/gui/react/src/components/MainFrame/MainFrame.tsx b/gui/react/src/components/MainFrame/MainFrame.tsx index de4db33..425b321 100644 --- a/gui/react/src/components/MainFrame/MainFrame.tsx +++ b/gui/react/src/components/MainFrame/MainFrame.tsx @@ -1,10 +1,9 @@ import { Box } from '@mui/material'; import React from 'react'; -import './MainFrame.css'; import Queue from './Queue/Queue'; const MainFrame: React.FC = () => { - return + return ; }; diff --git a/gui/react/src/components/MainFrame/Queue/Queue.tsx b/gui/react/src/components/MainFrame/Queue/Queue.tsx index 5a79c9e..e263784 100644 --- a/gui/react/src/components/MainFrame/Queue/Queue.tsx +++ b/gui/react/src/components/MainFrame/Queue/Queue.tsx @@ -1,7 +1,8 @@ -import { Box, Button, CircularProgress, Divider, LinearProgress, Skeleton, Typography } from '@mui/material'; +import { Badge, Box, Button, CircularProgress, Divider, IconButton, LinearProgress, Skeleton, Tooltip, Typography } from '@mui/material'; import React from 'react'; import { messageChannelContext } from '../../../provider/MessageChannel'; import { queueContext } from '../../../provider/QueueProvider'; +import DeleteIcon from '@mui/icons-material/Delete'; import useDownloadManager from '../DownloadManager/DownloadManager'; @@ -16,102 +17,384 @@ const Queue: React.FC = () => { return data || queue.length > 0 ? <> {data && <> - - Thumbnail - - - - - {data.downloadInfo.title} - - - Language: {data.downloadInfo.language.name} - - - + + + Thumbnail + + + + + {data.downloadInfo.parent.title} + + {data.downloadInfo.title} + - + + + + Downloading: {data.downloadInfo.language.name} + + + + + - + {data.progress.cur} / {(data.progress.total)} parts ({data.progress.percent}% | {formatTime(data.progress.time)} | {(data.progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s | {(data.progress.bytes / 1024 / 1024).toFixed(2)}MB) + + } { current && !data && <> - - Thumbnail - - - - - {current.title} - - - Language: - - - - {current.parent.title} - - - - - - 0 / ? parts (0% | X:XX | 0 MB/s | 0MB) - - + + + Thumbnail + + + + + + {current.parent.title} + + + {current.title} + + + + + Downloading: + + + + + + + + + + 0 / ? parts (0% | XX:XX | 0 MB/s | 0MB) + + + + + + } - {queue.length && data && } {queue.map((queueItem, index, { length }) => { - return - - Thumbnail - - - - - {queueItem.title} + return + + Thumbnail + + + + {queueItem.parent.title} - - Languages: {queueItem.dubLang.join(', ')} + + S{queueItem.parent.season}E{queueItem.episode} + + + {queueItem.title} + + + + + Dub(s): {queueItem.dubLang.join(', ')} + + + Sub(s): {queueItem.dlsubs.join(', ')} + + + Quality: {queueItem.q} - - {queueItem.parent.title} - - - - S{queueItem.parent.season}E{queueItem.episode}
- Quality: {queueItem.q} -
- + }} + sx={{ + backgroundColor: '#ff573a25', + height: '40px', + width: '40px', + margin: '4rem', + transition: '250ms', + '&:hover' : { + backgroundColor: '#ff573a', + } + }}> + + + +
+
- {index < length - 1 && } -
; + ; })} - : - + : + Selected episodes will be shown here - - - - - + + + + + + + + + + + + ; diff --git a/gui/react/src/components/MenuBar/MenuBar.tsx b/gui/react/src/components/MenuBar/MenuBar.tsx index 5d74377..b1125a4 100644 --- a/gui/react/src/components/MenuBar/MenuBar.tsx +++ b/gui/react/src/components/MenuBar/MenuBar.tsx @@ -44,7 +44,7 @@ const MenuBar: React.FC = () => { if (!msg) return <>; - return + return @@ -108,7 +108,7 @@ const MenuBar: React.FC = () => { Version: {store.version} - + {transformService(store.service)} ; diff --git a/gui/react/src/provider/ServiceProvider.tsx b/gui/react/src/provider/ServiceProvider.tsx index 9aecfb5..ed862b1 100644 --- a/gui/react/src/provider/ServiceProvider.tsx +++ b/gui/react/src/provider/ServiceProvider.tsx @@ -18,13 +18,11 @@ const ServiceProvider: FCWithChildren = ({ children }) => { }; return service === undefined ? - - Please choose your service + + Please select your service - - diff --git a/gui/react/src/provider/Store.tsx b/gui/react/src/provider/Store.tsx index 5054051..c82ef92 100644 --- a/gui/react/src/provider/Store.tsx +++ b/gui/react/src/provider/Store.tsx @@ -13,6 +13,8 @@ export type DownloadOptions = { all: boolean, but: boolean, novids: boolean, + hslang?: string, + simul: boolean, noaudio: boolean } @@ -48,7 +50,8 @@ const initialState: StoreState = { all: false, but: false, noaudio: false, - novids: false + novids: false, + simul: false }, service: undefined, episodeListing: [], From f0cf404c1d85ae076d6e0501ea9cb8693a6a7bfb Mon Sep 17 00:00:00 2001 From: DAREKON <63023042+DAREK0N@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:57:24 +0100 Subject: [PATCH 41/69] GUI update baseline --- @types/messageHandler.d.ts | 6 +- gui.diff | Bin 0 -> 83226 bytes gui/react/public/favicon.webp | Bin 0 -> 2258 bytes gui/react/public/index.html | 2 + gui/react/react.7z | Bin 0 -> 134161 bytes gui/react/src/Layout.tsx | 17 +- gui/react/src/Style.tsx | 2 - .../src/components/AddToQueue/AddToQueue.tsx | 10 +- .../DownloadSelector/DownloadSelector.tsx | 215 ++++++++- .../src/components/MainFrame/MainFrame.css | 4 - .../src/components/MainFrame/MainFrame.tsx | 3 +- .../src/components/MainFrame/Queue/Queue.tsx | 423 +++++++++++++++--- gui/react/src/components/MenuBar/MenuBar.tsx | 4 +- gui/react/src/provider/ServiceProvider.tsx | 6 +- gui/react/src/provider/Store.tsx | 5 +- gui/server/services/crunchyroll.ts | 2 +- gui/server/services/funimation.ts | 2 +- gui/server/services/hidive.ts | 2 +- 18 files changed, 580 insertions(+), 123 deletions(-) create mode 100644 gui.diff create mode 100644 gui/react/public/favicon.webp create mode 100644 gui/react/react.7z delete mode 100644 gui/react/src/components/MainFrame/MainFrame.css diff --git a/@types/messageHandler.d.ts b/@types/messageHandler.d.ts index 3929178..7e63da4 100644 --- a/@types/messageHandler.d.ts +++ b/@types/messageHandler.d.ts @@ -40,7 +40,7 @@ export type QueueItem = { q: number, dlVideoOnce: boolean, dubLang: string[], - image: string + image: string, } & ResolveItemsData export type ResolveItemsData = { @@ -106,7 +106,9 @@ export type FuniStreamData = { force?: 'Y'|'y'|'N'|'n'|'C'|'c', callbackMaker?: forceMuxer: AvailableMuxer | undefined, simul: boolean, skipSubMux: boolean, nocleanup: boolean, override: string[], videoTitle: string, ffmpegOptions: string[], mkvmergeOptions: string[], defaultAudio: LanguageItem, defaultSub: LanguageItem, ccTag: string } export type FuniSubsData = { nosubs?: boolean, sub: boolean, dlsubs: string[], ccTag: string } -export type DownloadData = { id: string, e: string, dubLang: string[], dlsubs: string[], fileName: string, q: number, novids: boolean, noaudio: boolean, dlVideoOnce: boolean } +export type DownloadData = { + hslang?: string; id: string, e: string, dubLang: string[], dlsubs: string[], fileName: string, q: number, novids: boolean, noaudio: boolean, dlVideoOnce: boolean +} export type AuthResponse = ResponseBase; export type FuniSearchReponse = ResponseBase; diff --git a/gui.diff b/gui.diff new file mode 100644 index 0000000000000000000000000000000000000000..6ce327bbdd5c7bbccd860349b64a58f854972c4e GIT binary patch literal 83226 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 KeT>dA_kRH-v>%xO literal 0 HcmV?d00001 diff --git a/gui/react/public/favicon.webp b/gui/react/public/favicon.webp new file mode 100644 index 0000000000000000000000000000000000000000..279efcfdc9a13e3d3fd08cf6ba35cc3decfd7277 GIT binary patch literal 2258 zcmV;@2rc(gNk&G>2mkF7Xf zsthwD=KBiLF!OzH9T5`%43vD~o2kQF|G%oY`UW9Jzg2(r@YZ2!FYJ?m_wxBGnfo=3 zTCUaZXRe%=c_jwpxpz9Xq5dux4~pE8BkI!uwejGo8j(37gtKoAYJ1~tHZ1VLpDd54 zLr3MLpM9a@GeR9q&W=ND+rFz&$7{uHW(m9hnUsm<>8`*cmvV1RnK{0vxR?|8Wk{Ji z{3XB^_ZLmd+~yZy(>%T6BPPWKxcrN0RNt9HCq#InicffpWJX z+Dgh%4(BBsiARJ-beVuYz-?vGuOS#>P*YfwxMzUiB`o zKsjGnT7q&vv80VQ|Js{N@l+@RE1qSj1Or4}LbOX!*o|7|E>R?w+*W--ut2`BDA1&6 zYz8cG{Ynw}tIG;yh>4+Mfp9`mc_J*fJI2L$cd=87%m)i?o7gaS7AaD678eL<_|QW0 z9aDrJ&*eYE$eDkhBt>a*4xx;bvM?(=!pTUPm8D3{&XC_?<()h&VqnFHOj9XZ)hXgr zygU^rf&;t^f)jCy*!V;aGdTc|-(lvR3~*k@jD8;ADn;!TfDCRj0Ds};FOX`usYyN! zH<~X@k-NEvohvEqd~=AMgIn0S^$|3p=+Cv2&QhPHGQ3dtvN^eHv~wpH#z5O@hC0^A|t{HyMDdxVZvw zUdN1n9zcGFnRhY(&10ElBIJ`o(?We}VYpW@}IH~}hNGB7n_V8w_`liy6XN2#?5-2=g!&v}Tj^aB?KCHu~>pYblM2*6J{qCOqi zY4B8y$iQ1M7|*}c?V!KQ$AcnpT0Vaz^H)u?POJTuxq4m(-b=plUh44H!&miI-yp>3 zx9YDR-a1U}DLx4p2v$%yAQ%Jy0MHr$odGJK0H6RqZ8VoiBqJgrBa~{GfDMUhZruuK z-zxuA?UVdBE1C0upW+r(mO}qO+~w#7le}4f)b#Yum>6SyM*aI(*0F%p8~2qp@km1%#!Bnq6A%?dhGVpy)=GKGbS zr2tW?Inr@*BqR7sq=MO8F%;L`4eCqd1%MAtWYF~YGhCqH0RH?+000F!`nHp7j*B;c z{LB6EHSu+yQXMosODDsBEk5XT7=b#NckKM~o!LA`6esr7=c{;P5;iMyxz~#{DAZXm zzZb^W&YIhhAc9t2W?wmtbIm0Khv|T?!Om&k<&x(c1pOUfm3#ED-5h0eW=K;NaZ075Tp(hq7P!>1hr?ZWG!1Ir(6!tA zB>N4`uf8JXY)Md0sk(oheSK9o{}H;Ca3DA4abC)_AStGE+ldXR(%FW-!d!LU#HM;k z`p);eN3rh5CoErIDIS4U&0@Bqq`f_Vy%Lg~&I9h9y?RXS@8cFihU#x+K=`>9Z>-su zwC24G`JNYM%Q38Ap#FV0`IKfV!uLTD=Q`K9$xZ!PU!SFF{QaiQ6%uPe`zI2m0xRlU zZ*m=LzJ>_W{u}+iEX@9iooK~a_Y4r6lbd6N|J=eaB8Vc$T`Fph+S(&eNO^t(zQgi4 z9&-3&CjHr{GJqys4b4uJ*ZW$W%hdT#KXk;2{eq)b`$9~;pkork%#AWe1+}$%*Kd+N z0RL!oJ$qHRQCyvprK27yB2SVtib4Z*TLk7mHMY$+BT)7>THb}_24NlXB_BOo^QLNM zUBS0ARKIpZJX_-XZfbzW|E&1JNV!AD&Gf2k{irjT9B$Xrq0Ahx^Ri%-gk$+CEZ-QM z(a}5QmvMXqhbexK@NhM5Etddi ghe)^}c;A%vc~9g^YQ7Rgg3Ufoi!l~@FIE5m07%q4q5uE@ literal 0 HcmV?d00001 diff --git a/gui/react/public/index.html b/gui/react/public/index.html index 92cc7d8..e0f99bb 100644 --- a/gui/react/public/index.html +++ b/gui/react/public/index.html @@ -1,6 +1,8 @@ + Multi Downloader + vN(#){|c zz3LPHo^`-l*Pq-d-8p`#d<7RucaFDw@EJ*Bfi3RQ7Y3C4VlwzfZ4jP8Ira1x$w6aV zwt1r8uY~MLyrH6!#7t`{*?rLRoZ2Lt70m3#4eVwxO;bywO&N)Gu4UVWQpaL%`o@Rh zpjXoJdU~3enh}yp=VW$nCP+lEbw@$!DQQmp+sIy{9hu$lc(2#3{e(*;t}MNW`p3=R z47>Y!=b{pZ4Nb?vTmmeWOwLQ9BXVzQpm1CHNVP~yE9ljtn3rTgIwqUi&IU69M7|S@J)XIe@6e`&6X{m>jp3!E)CAylStKUd_I_Y`_BG1^zf^(i0hr+iQ?46=uF=p?< zz<8MU2HY%LE3&biCHwexp|&2`=qgF9421R2;R-GtKThf>2)Wj47&1M zDZU&5g800%w!GOyIur0ObBiP2Jx-FCf-PPOVpa0|wWJgU@m0(H9pQuC#gLvT@ z!I=)OnMIAEWv^nO34MaQ6f1@}(w`p`nQGV0Z^01GlI9sF|MAeHm(iaHeoH4Z6EV_K z?IerPH{x=I!;r<5?kal2>T!YxU0EOrW0$_W}hF=9&6mz`CoSu}wjNfqFs zeqy7N5_vJY_=~{&-O-gI>^@|={+p4V%VEI2h{FrC5iXAu4@ZS`gOrcTmhV0pt7a~Y zTs=e5ED#9&#>Hs$<~|9ekQO9;o}0PQej*9Bd6GxrjO!=2?i3Ta9^m}u4Y=l^dEmFn2c=DXD#{_oBb z(+?Ir)^8`~f-$?lFvN5xpw=-VPdYq-ZvMDJA8hh~G10I1auS<$mt2D{r|Gl`iF`KV za#vM;Qj)u({EYbggOnF(bj_o9)*JEhr60rR(8RK+2|;_tns2{X^!Lrh7*z!{B>M#U z%E$Z;t+>8*f##74#cB>y|4m9A>1-d*<>3ST{g z>B8O&>OXeTtfZv4^Id{S74%sY*(x)G-Qwo!lhI+opX-!0nOC=Fo{cGe7AQe#aGgj~ z%kZXBdxpc)19svPe319wHqiKEOFr@^3WP#HWngz-4mxoOB7v09kwI%5+2zQIGTEy} z6S$}o1v3px`kD(ANcv3gDLx9!gsik^g+{_F^STJDTEGCMvB7+Sr(Zy zS_&ht$hLf+F`-5gaH}tY_>Q#mwvjaFe1Dc8NEY>n#0~icN}gZ6BaVFyn>C(Y6ga{H zscCo4pOH&hKC3K!@Q0$Uz)*}FPnMg(u3%U

87@6~Dg9Z(?sxXWI|* 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

Ve%Q{W1b>_OC;g&9CD*tSetIT^3>FeP9YI_!VSU z>_Wr>sQ=3{I)j#XM}K!=3@&!_Sbi{t|C{=F9xS6Sjmg&Hk;MupRP8{Yq)U9=St|@; zEjs_>I{H!{96MjAVf{KWt`_wisK*W@_i%WAl!a}xor*4)hc464qS_r3pHZ$qQ}kv4 z9QcOeNmRx0@(C3tb9iUnpLlvozdKppJqd~fKhiRnNw-fYFg?8jJiZii0&hXWjpj$a-a`@$?gb}ih$$|!vt&n@IS91NA-d8s|Z`U@-5glh^_ zWOj6C(M7;~>{R|iW?PCt+$<`AW-#_)T|K+Cku6V>MIRhEp~Q+pQeQKv3LsO-YIsCe zSs)+BVnj2g>w>_|fxi4#HCbCZ^6Vhh?U(|nG76oZZ+VHe#SdR{h5pFU2qgnt(&hfD z_W!0SlozCF%+1mTVs{!hq@eKQ+hgUHGe654Xdb_Ej6YKYcdW&vy>G{oSAjGPV}VT8 zV_d#FcsXEM&zNJOWNZNjS-1-X` z)W@?mPeFqhg_qVX)i4l}b7YKJ#%@0mBz;E^hNA;&R)>z4uv6xQF@mA?P+!4In`@UvLrRoW@jv)x^l{>6+c))?xLGzf*2{`R-K& zm4G&PQQ=pl%NpN-&-#TO2&4hI)-vLA+Ck+v&Vd9VOpC*2c+8LJ@?@8VOGMI(9@d1E zE^(+mV|{ppWnu5kH^r4})WF|)`e#S(9>jJ-WPgQMN&V~m+!JUp3yqX?4~CHwl4>${ zt?1(RkDpZD9$is?-|dj-=@X82{qd*KFNYfy+~)u<_1{oRYp$tNDs9%+icI?q=U)oa zoUZEkcW3En<-m3MJ;g7n{tjf|LLF7Hc*h-aKnKkl;fRW>e>V~qmkupk;*ecI6yMsw z9Z!x)cQlcy+L@>wB;W3$G~SE=$PmxzIBvS2CYp6nbXY*jZ1$+V2-c%)rg|5~88Ih{ zX2k)Uug;1;<0NV3@sbe~jdY9X@?Z;9yj7e!K}w=Uv7mj4-6ZxTdCg>ALg^WHxwk^% z0;_oUkGL^8;){r&#d+y7&%&nza|n};+M!+n$j@s@=YJXyS`X)~Kg9B}R=UabnE^ouMtp64RHCk>|x8CJ1nw1TPdhbwVC?OlY*d+)AEYxPSy{JV`YdYJ(0x(7VefW{1(@)$3XW8$&jyd~<`AM6JV|>}q~Y5v zOy+=F;1(Abe0-)UoyGE_JQ}Y}aW-^50@hkrayM11A#!13gdVx!wvLBhxc$j=k+>)) zhaVgC*jFN@vCp0wl`M0eYYRGedZTQ0?JPn@gNcIP?U%SEGd1GLVtysVpImseoOw6h z`WRcWVVx!RRdY@$0aNV=7V{sX_cX>+4$?Vq=WLYzsXQRyksBn}6f@0H`R7P(tAZmH z;zqZp=@Rg`ARwLHq^oYBP~wMR`!Y*`9&H3TT!jrGJjf^(!;prO*)PA?xTPRXOffZ# zKrDH4$YN!`H(#J7dyH3PAuZ}=FYlB(Z(0}?_|pikdIv=&gja{mseAy}Oxs=EOw`!# zck?bg-o@|IpacZZ96T%`vTao;Dl52c-uL}BuJuu_2 z$|h251g3jKh^YH`g$={?|=571C-Q893-S8{X>hW*A zzj^RPAu+Enke!sA?{Tp5LL7X*Q`%yyJhts4fX9B^k`14fpx4p}Y-BWK@(HQK^i&cI znQv7x;D8P_xtKhs$rxgf!--F4e%vvP@Y3gKs}Qw?9dow=?d!qFL*NV}c!6}?a+Fz4VA4LKZn6>;&d@D`jh{*dJLO&96aQ|<)Z zESpotTU=HOIe&kxAN}<4I6TDe|E5zApa^(|ZakB{`812r+}U*{&K&^lwsBHtsa3 z6ys~9=@n@mX?&`x?4zzl=~yJ|1WCH4BsQdk z6Ybry2ri|wFHv~X@*VlJbE@}=Ee#z6MBlu4IsO)o(TmL-2Uew`9se#L*P$%=2Z}sWvK$jeJ0&*tu~f1}ILb+qZ^D+KL=tRJ7TznUIs?N!{PGSg^rP zjX7DHmExE|r!fQct_bsbePEai!-Xi92IP(o-S1s{FB$!k2wrn$-nIpgE@Hf$iZxOE z?EiMye@n`Sz+o5_AAw;k7o~VbSoNDzss<Blemg=^HD0T0EC8#u0t4G)mvxP(rH+l@^tM@sLKw^JbFti=7Epg;1@NCPmz}zM9^uV(ksREv6m$WT+LPh1S4?Bzfsn z3HBxp<&uJW@IW*k{@&(DmNy!d_&1@G@GtI<$RCbWddKqR_Uq!0CoyOWNo?`-3%qxr zhxn;PJwzpOwef)Xwxpo7kbKez7RY~0sV;e|FGbrU;6>{}ij6O5m#nlwkgP>-DIiaZU%>5b&V`EODEWWW-%m71KKAF&PVz|o^fSqF@xT=F)36ME?Pa>r!< zto*cXmH7wiK|_0OjCZzAy53bFLC>uTTzd+RO+fOo^V_^ncgKBGgx<>8?e3J`o_l%C$q97?}{SSIVViPAjk}S2t<= zuvUZ(M61Ew$*|q$NWwv%C5C%*ogQ56Q(_@Z;y0RuCOpYGnXU;HIc_*O@d%CrzY?C zPfRYFr$*HCRLrWF@N;jMGVM&@KoWMypB%sS*?zeb7-}R+5*-(k`G17G{x^LfOj1h<>ms@s8 zkK)j5IBG9ux}8*`nBNo&Dm^71lY0n&AldT3#MQcV7wYiUBCmHL(qTM~ zzvRwxWz+f2FNa->VEFFMDK9Zo{=P)s&rjyh^c14~Xv;U1q}?i84=vcoVpQ%Ken>es z^|-1EQ}CpgUOxg_t9+6ZWKJ|^k!wUBT?i?|h^vw=bwNf9!dK$^-R5`c7?d^dM3$V5 z{uFMniyXjMgLd2IRtbPf3Nj~?r1TZqE~|}J&Mgg`zxqXfl4bl^gNV6CFY~MoHN+uD zR>#{ya6Jk}BTvSy4}@yWyo3WccIhH-JHPBkaJf6JOF53nlU%BBf2&$be*;7vNQ-f9 zshXj77m&OydELkj|7;-|x!A*hfI%Q#Oe#41m zZhX4Fp)ra`wIT>UtZ5F+plRIFiJ$8LXDD!$;Et8ulb^urWMT@sfnE4GNqZy1|y}y(b=7f?R3G8up*? z)4>U(4Pif3o+?cK;wlU8-|b$qPWtk21^i9=jrj5F_I6PNu&uuyl^j^D25|WwBu3>s|Yib4aFBwgZoRVpo&kT?*4{$T>&5cluPn0#+=!iu*~tW*w?q&j?= zi(HN9m<>9+gv)q*(3R?&fm<3adn8kGGmFiC2VPlUPWxvL)nVA|gLpZ?*#VJlgvz%{ zt7kKUT0b@t*V(VO=JT)u$$^~Hs23S;;cR59NM~AAtSdzgss3~|y>-F#1 zzzN<30zZCUofBXHd>l+5WQNZ7p+e>UWl9C@dn2eorjCej;Av}e=M<6Iq4U|61>v^m ze@l|-L)DC;yjSWe)RapDT%qafh(638by`q-8+hXf%kL&<29j{HS$wKI)35NJIzZ4> za7P(Reis+*T~Aj(H>URBU0 zdF{DdeXLJd{W3B-i8_`vws$#$!*$u>cxL)YsLlT=aA)>V7AWay3yAimrCmQKb9C6{ zR_GJsGw@aq|jGh!bl!bNGS-zS2nS zfBsSogUIckRXm~Ap$F0h#-}7Z6tQPzUsM7;+6#$`{qdmmf%p=>LPB)>x$XB-Ea`Ym zH*BGJ)lh;B2w-Xu4zHk~OzihRRQZgVh$3r!`P>1^3$_8lWdH8D5G7CDagV_?w2+gK z3!Xi4{47yNyoEOt`BNu%QHx%BFfl}QyXP990sn-0YqI2ts8e8O z)$}bb_fW#}`eL20X`Wf z9g1kA<|8X~2YI0To5`oVEucy3>^jUCkPABt1pv;3lCCKxPNLiPWjv$ZPGUFio8*@N zIUt_7-9%ppDPi2U+g17tEO-m(Vag*_1}TW+p*Evah1QjD>fQX!m6m$V^9zsSwB)-cTWp zl7pDM+&#h9bsQol?1`rq0VI=v`?(WzW^<3Zx2X=Oge%yZIA(o-wd5@i)arJc3v;%g z3m+Rr?PJfgN%^_x9?SJN9{oiDZ31qHmNFA&Uj>yFU*#?N8;OYp1^&_S-e=w}UMf?H z*k0JIN2uspwpnxc)Zf$E(7OObbN8bXV!4b<6#@LA+^A7=LjA-O`ILJl2`+s#UXUJA z)zF*qZs?+?zsV!7#P}5p7{0>+=8JtEz^tc#1|kb=?RZ9=%oUlq7$u=UCh3~N)it=InWhyuG8JKR!ZedAuQ zFpgoWDk|Mu`V<>$4vIITyi67jp_#*uhVQ|5nFhfVsHPZX<>=&M!lzs}S8P5-UYK7W z*D3XEi0*MpxK8lBEx0JkP#+HAbAV_i!f#d6{QqHv?GFL14=)i<=oq$mvBk20b{t8l$BAl<6>p z#@0V)ifJY=WP=%D^|*pHrfxI+mhgeOU_NJQe3AidgB3r#F$!kQJFpRo|&%_}tRYt!>IS87ozA@s%jbB%9V%Ke!3gV4Dg7fDS z%O}q#dYa>_*;Yqifi_#wKW?P_s9W2*Z7^EjHP&1+A{H=q1+5z7%6OeIay}(d0LY4x zNaUbyMz}el%_!2cISYuTB~9zQE;*P&QJ~UODoLxqM?5r!4pUU(E>s`&4*!?hsm|W| z!Zt)${x-(cQ^v!4!Zz`fY_%K8)yGNd(|}E+Nh}3X`3UPqzWB__qbcWbwHtWX4EIYa z8?WJS+X6wTTclCyC4nruV{}Q1HhJ+Mk!LxhF7>>lpKS3620FfOX#-4Y7oH2@IUsnkIDkr*a2` zRa06BYpiL0HUgy~M6Po?@-6I?zM@ixGL1kBpKi0q);Q}{G>K1~;XL0P#t|uXzH0}x z7!Wk*67OE0v~AUeHx<6b97f4z_dy9>xxxDwmS=7p1M@BQG&T2(M^5<(b)#X$x-8S41CUvSI#`!zEBn={old zNBgDi11s_g%Df@};=P~?QWjyfdRef|mmRfp`7=n-Hsa~QgDymuyBAktoE8;eB0z#B zp`6Bir>iWZgUNgMr97LH+{Oe=tuqDiWHsR{$);-814e5YU;4zlw<@^}Ma(JhRZost z8YSh4o8YeS;=Q4=(ba+a8`4l38#NWt%+!Q|u>;GMH=u{eJEZI|{-t(!da%_8Omla# z!@QSny^kZ&(ZpWJ{*bwiI-hYC_|yoxhh-d;oF>hk(>1Jpmnb`&UB#lCT|D~@sp(R^ z_;S2DYn=|pGc}fq>oi!JdpUXRl z*po84!I(&g4&NG%;Zv4zAaF;{DwH4cEv-yw<)GrVm5*zZ*hjG^3>7y_-!bR-X;~_; z+ymVgL5So>)&j&0>>TMFw)$fVpPlJ-b1Eg_kjwJA#iWYW+mZU8B@k#j1AfNGHg(C$ z3ehNZl2(YDbkFPPS=#2%WGXCjpejoe0LpUD=Mk^6F)Ist!)45)A-j}~cy7Iak2|SJ zb{}bOJjA=rj3jSg1Ku9kF(c)U68v=bn|7H9!MO)%xm)!fDMq6i)Revu-nAS`Jn-J)mHv+wEo=QPFC! zY|M+t8=^Gpy^EC2KB?-ACSpIHRKtJHUn{(>X#B#s~?))-c zo8a(J6g*3dxo4M1`TX$AG8*l9r^hEqHT{-t!wUZ1XN-3J^(!H!pR*$9J`J{^+N-(k ztBV_Sx^(qj=XQ((eVo0jXY-PlRqcricEROZE4|Nk^YwaBMPeK=hVDdtqyF#50)ck8 zu?;f@ag4f10z^`jG?-|RGIM@-o>U4296Gj*>SDtX-X>%&%i;!cgz5Jetc)MIn|MV( zFLQM#2M{4MUpta`TJ&M$D!T7#vbY1j{l-M;1t(I0JtVC>+(7?6LPQDwt%>`wq_UN- zF24<6s$O(%v4;~=UHbxkHd%5AWENtO>^R)8IAaR=YmAhjgGb+no3KNV-qV1t*Xs$^ znNAfM6}g$|D5u?ur^N^Z0h%J2+^iY8Pu?zCovWX6fyQrb%wi|_zYctJG;&pWCm=}X z*$jv7iwT}sYJNv%%4{*IfL5e7Ec5H%zt?C5f3B!5yo8S|pJ z3GkTmbq_R=;F;(8e6M$j813erIl=UvkU-be3L7K09EZ_YKW%q_43`9FZ6-un%Anb1 ziA^WmB_hH%ypa3EpOwG5mRgL48t8C_6UJKcNa8Xy;4BQ z7Ke3whj!5mkBghaS16sYEn&)%8ifR7oZ>)xP#~T#;#{`fmO0xfXW<}Z@qVqb2bR0) z+r@%z!e+9sD2Tg|8RRSM1vPuTpMRjbGb3>oe!>>meBG=;9$mt^ZmL*~3)2O5_qNbh zzf7Ac4Seke(ZdD%xT@kU_Am!(6jWFA5jG(dJfOh=ceYx6_Q^pT7#Lm@MNklH;B`!Z zZCjbWqGzFMv0E8U6o^5ex4tr0jT70s{3F)M(4O*m<0wY<&Sb#*%QW17gz(f;-loLU_;<@J&A15#ke?c!Gmlmo`~Q{ui2C3x^VvX{q5k%QK` zJCDURlFNzo3QZ$M1&(%!22W`-eEf+*}h6J_6e&EKn%Q#g8pDWzW8gOhsEnJ~Lc*dv^;Iax=a2`%@7(V}xJpt49b{wEeo)0X@6~n1FRrwGd-0;54qJ3_7oro;BrK^M)5p zd49v?`8z}*1vA0@Lw)@x<8fquD|{{Z^r4I=Js$qx0dGe6$MYaW;#M@R$CAqLD3K~C z#S_|+Pk#>F$LEF2DD~omOq+iQ(o!jZk9<8#18PhZ_Ael2l?q5Mw1uK(@_wzs0#w!{ zl8KKgnlo|7OOj=*#mLu3349hJ0Eb;+HJZ}O&4EEKL6qKEp9g6N;J~cl+pDhjAun7C zAW`<^J?+6TifX2R;Kw-5Hh$PujmXd_*<$pCSGm)5(b%=~yk+PlI;JIiy_6qvKlAdU zev}qcsFRkTWgQ9)@uo2~ICWK{z0^>z|BskVZb7l5uEY%~A*g5(RQ#_dZdX_4Ubis> zp2vtH``iLY_F##_S@rah9t);)RZ~qiO40YR$+z(PF-wcrJilW~crn2p?_PUtrDs>m zVc|e&3YTV1B4<4~Ysl4NKD7?KI@t^}^zK)=o&H_Oid1m8Aa&FJfn-W?YG|{!<@eD# z0i8{P!&s~l9a|=*y=g#&gj)tSP02;6lxjMp!l=K29#ysP8~L!k>+Y6L47A=UqkTNB z?kjRR;(z=n?!|gB)y=~j7gvTqYPHyAi;dY!-fEQl32LJj^sAG2SN}vYHa?H^kLjBu#Xu<}sam#MAl2v>T0`t1$ zd!4RXutw8J3x5)tW^+?{mxD2`|DuC1^`=ulw4;YOpxmSRMpf-BlZeckMuEz+-*2dl z?zIrwRh>QNxIZ$~`~sBG+WF+fsd!zO#=LK{(skZV`LknUtl<)Dn|htl=3L}TOKIS2 zmxPTkU~h9!B`FlyjSG_150RYS=BGg@GPeqNvwgP;f1N272JpQQt<8;)YJuC&1SmEP z@H3gSfLPHa)d!WR-ycMX@oTd)M(7=4vdYSpMDI(oVGG`JC&T1`WX!s6=@uuSUHOnASU3&&{mK#QOLJWM$XRL#$PZ@%?8l|GZS^8HlD~Er#inM;|PCqgK+aRDl)O@M4eOK_(nP zjz3bIWx^u*pP-TQE03pMpxH`QZx`x%)7b~mW<}-5@1gN%qe1PbF*H?hb&Z)N_S$1|{irh*VNR`lTtHyPCb&w?&o| z&8pvuUj^z>Oez~+Cz0x16@QEivfnO9ny12+x20jwLD7Af2&>X_R0;eZl`w242Elyq z)4SY}Ht>DdKaMPq%+d=R%PG@)TR?tz?{R zt7}gGbkZx+3#cO(6`V#_8^8jYfIfgVm^CeDyq8Ss1V$y42$~V`cr&Zf3N3DfVTzjxlZ)^MQhyt(T7g;{Gn3^4_`^sI25;kww z{(H{k;E_lQTQ54@3U^6GE%DCi46q`Sp1ViI<|c&(k+17&X+&qNI1wK5zV`5{y`K?N zR@FFu5zE*|z&kD06iSQ|qUD-&(n42dPZh9EOyPmUtj41r>p({qUzQYfuc@N~#y~E0 z^;li~x1C=4@yTM3Ph5eH5xc3EOS<>;0?w23b?%p3R=w!_Qqhr3!km63GxmwrnVfMm zTz^+EgX34!#L86AC0!P@kny~UO10u$S%a-&iRZT^D7Ru>8~Z&5!PTkyiE z=1$Nm2SR0rqRy-jQ;(7j_`!76wWz-p7dwUG#m*u`q;8e&y4ADsH2dhfL-^%E13u*p z{Pmq^D9847%4dfDS%r|~*a;6)`em^X7l(uDsSG9Ma<44wgPSph4=f{BFq%rkW1WCg zKIEten}H1fRu^sM%Vu3C((I`i+;4qS05%J{RRCCd+OPu4S`mC-j^*~37x>jIF7^9` z$S(5S#xCm6ZwB3OBrjeF(t$w&y|sl{BoY1YF}Wq^gF$8U{Uu|2fJLiOzyKpqrP%oJ z+OIKOWeNP$-T#$1=YVhQ#5VIciRb`sTt9ElCm~=v$b+K z{2W7OUMcs)U^h5^AxJOIQl(M*;u2}UZ=~^-dD{sE(d8~jQZKxmt55_wC+wuZXjPiL ze)iou3e;jp3MA-8`sybqbw1M>iNN~`Xv_iP^$*$d;tVrd@2F9#%6O$VyLDkJDSi+V zTk^#=z`k2-WeQu2IXp(2M&OIKi+^0gBC?hs2k6Z_LKn?;YytT(7sRTeF2XD=NP{ZO zR?#tiu|yX}5RlcnQpK>-clFP$@XJrziT=LOTv3%p+TaUVVpt2D#XkB zo`345_e2c-198BAVpBj3F3l9=^3ba^iZxM@=ifEN^IagNmJ}FcI)z^?uu&jw?^FLA zv4`jIKO01T4Dc*T0v~{H#(zM>%FCIMgn|x=#XfrnPJ|cP&n8S17lHW3M#`n^-@Cgc zaPJTsTE6NnEKkFNzO1k(kQ(3)5}I~qQrJ5lOuRwX^sfOq7@l`sYFY+H#1=%n1ABoR z78nljWM%(^{uF7LR;AS7%}mRKNHg?27gqd9KZIlLh5F?e0EFMOn_&S9XG`vsra4?t z_p?Xk;}V*KHX#jaj0FHiicSA1nCQJ{b2^7Wc4%Ri1WpOum3SwurP@_?Du8QjPEKVk z_6ZAWlE8yFbGcjDz%~qI2&1ViQVHl=20ho%Cu*BJ%l8rPt%ttdC%G6Q0WBZe&76*b zzU;M@dHj^9Xii-ZI|!@s4OT)`V%)m(t45$nM+rO4avpS??qB=L#@M)|VLr4T>gN}h zdoFTt!zU`)I?nv-3IPRyY5>J$g_T!Yv#8)e=Qy>FbPQS<5*=uuV2!+kiIcx(<;mek zdsKr~S#i0ct0}F85fL@3%NcYr>vnb&=DYo5(a2oLT%GMR^9vcf06 zA9xJAEQ!IZ_$Ty@zso0mf|x&Dm>MwCmkpw<)j$3PP)LMUWreJ zBp{zFWA=|4pY9b|L}2{hwjz`xnZ!}XUK8hJs_|V?(fu1`tXym!ON?V9vqztu597tS zmXj+(Q)2fHEv-=bM(sDXWqH8&2TQY=0>_5Z$x(O1Mk6!Dx%Rm{B_}znT&I$1A(Wn^v7V4j@&c5|{VA|Gysd1+9F1s8 zS3ISwsU{+&NRwjGz$0TKr=TGdRyS@GmP!8)>YYx#@A>>h-Gk8p3@=nPcn$;(u#h8P}Gzt?l;r;VqCy2t_DyH#(=Z%IyO+cW>ym}8nbsEaYZQ? zzjJ%;lnLJ*VN`h`e@p)SrB{Z{|NdSn%7b9y5a&<1vYjGQrH)GHoy4OY;LI#5W~r*S ziXBRR9}8!h2DbPS?@FE_KlS!mg#NCTyb{UH#Q03eko&OFO9CpK!T^gKvUUs(9qb$t z<#I$=fGygZ2+RmwNIliP$p5x$cV;lTNFh)#Dt|m|bJvz12AB9+I}_-+iNrIHsx6Z& zPt8v6UnJ3l@F_&J!W^9zI3R$5 z^CkLv7uK7Q9f)oPx!K?>$(BGR?o`=#SWPLx(R#^UOrjJ8uTq(P;yrV)1r1jCn;Q$~ zvKDC@6swvR88kR)nG-?6fg06VLpp z<#S)JsJ421`FdFSq;U)?JmKgg;D;bxt5~>`$8_o#-{?!{W*Ut+Y@YG}N^m>+24tGE zG(xD7RfbmT@#@|Kc8GcQNpx)U?aHD2PE>K~2yrHyGh4RNBWFAWtTEN`K^D<|W*8K* zZwzbA%(FJNqgcw_i0C=g>w_3gD0)GoJVzY_j@>=& zRukDx?=u|W_)Z&$?V&Vbq(iB{l*}LYX@RGh&+tG6o+d54=H>-<3?5w^!_e; zk1T>PpPKTO5eP&B?@DK+rzpM`CD8G1&-teQ$B<@$Ot)#Av7Uro|A z*>?Xq4LsFlt`l9y!TkuaKy%2_Y<7^Cc+Bkn+tc@Zwm0J4H$|mAlW%mw2gvJ5tU?J) z@X&l5$nw`cZ5A;B+hV{$y!12MeD2ILBBMp}W{@{M&$an7QctNxmsy_>j-0-fN}d~Q z0258G76l+xz{@HNX~drB2xp~)2VT3f25^u+v3tZamQs)#vchrzAen5_@!YvpcqY8_ zJ<5z%hj-A2S}0DHbzArGwb|Grf13j`ph^}V#g4t3QM2wa_eX@0T{P2LkS?3pWm`No zx$`h^!g;zo)5X`_WV>kZ47Aq8X#)m`*dd)WFxIBDtJ^;yM*Rf*ObbdJva@lGrrjBXqDzJowBgJ`jfOHN2$hZ`R# zOVkR;?OSVeoI69F;h)k|FS07z>bN?hkMvtjpT?GjA8HuNsdERdJHJ(&``y6O+`$ zN%~8l8Ll#l?ik((yMmcG?w63ZAEjGl`N$Nj>_AU_X=Rv2;5ZUsEO# z@MOAgupa@bt2nK~4TsnXdiWVIYioMWTyFH-3kpm##AYCkv1KO<_CuD)}EY z^d%6N8nbP>&Rtwd5&YH>&A>%fZST-cn`QyKyKYM6BN#S|_QtnkD)~vdAr795K?h`E zOtwToqs&ozET?+sCLoEPd(XlFphU-+`g&S{}P3cXtP7oQ(7(~F^{>k$B9`9t}} zS{r)i;4J%x{YZ(BMo2vx(rUAEz`vV`XfsANXV63nyl6&at%vaz3Qjzdk7N}vjd_IE z5P%ypWFR3-Ansoo@j72)ecS+VdcSp$foVN1U+yI5BO62vR?)T7hNyF|7o64f@3&Z- zy1gm@Jm7vkQt2RoS>G zW`k8{!Yjar{58dE1g9@$C<><|ap30XsB*pslV2GmJzsA~V8|dMm3zD&NKJq{>{B5P zi5SP$Un4e@7u}}7a7fq~1Na zI>8^MjLED?$!(o)s>GxT{lXY~@|()oZ(oQ)iuE?Ku<>pPUd&!WgS|h(W*+khJe}O)mbOI3eY{HBEA%c{?qaF2<@*EiV%(7#P8C)Jl z9!>dku6v%e+=5+(cMKWklfcU!Hu_wH|LI0wUwraB-Y%2mx%}A_ihjo|?r-F*Ya65B zffDEAi$7Dk&asc1W_-XL9@kL)jR+6XX`$k@WN@d*JOo(;LsxMc4F!~i>W&$6^oH`y z{E+riNJ}GiP?rSru$e1#Z`J1smP}oJ#3TSU&R)k$DGCIYa`neZw6~;o1h!8bAhLC6 zQy#>+d`s}R48Ec*qteoaS>2oP=`=uc?*H!wJ)Z~+bb5pLU%w#1JUh@9p$@1qoIdFg(+a|+G@ae;yU;Gdw!!9mb* zA#YY$me$)4jE2ZgoP?2tvHpl925O0gC$1o2Qzfr7G0Cq7qD~}g?L(Dp5tE)Ic)4d~ zZ?8@9*WH(YC?l#i2p3J>$0>s}_(8#Y2>w(XI`NVWP0Mc5WBUi$CHBEMt2^Q%ntS4u z=Jn0ZP)I6YR(X<2x{_2VxuZZpb?M-1mT6dAE%XaE#~sFV!yKG+Io*k8$jgpQqkA(M zrUPmX)scGb9izNBoR6)LtIF%B?7Tc~dzlr6c4)!>Cg%nwy)Fh0y@^qE#osYvdHZ*} zNE$QuFGD$AQ;0Wjvf+Zf{%+JIa*}9#im50$9()48Wv9GqP8uvrfRk_ch=6!T{ zK85`lNGNn52(9;bl^^KipeQ$j(*ZXd_22634XHQuKV)%4UpWmlBTVLy%?$3ORzhVa zMlA;z>zpF*QR>x{X1rXr4xDs~q}G)0eb1j^Ti_1gsy+(+TLmSMU^i|?S<5dE7C&;r ze+cDbgaMzcUO9@ha8I^~Cq=yljk4avhzuXN)}l#Dz4wn@10GA_vqD-iJrleh4AR z)GMIE_bW(zBnaehEPZ{bT~Spkm9j-fc@HpJi6k)ym^ua1-f2>(Z)!+*pgo12@zYme z=ny63%Ii&vm^~P{6g~#@3nA*cWxA^~rXs(F-B?J*&n_ zj{y-pozIY#QU+I}qtqK*O+JqOiwef0AQWYnl6jh%<6ISyK`+C5cT8n*U@mdI-<`6g zgjX0WWe!X7mA`$YuZi-*;4_UbuN4%-M@iJvpQlLR zTP~;$dsJ!7#;^((WCTuyJ}+De4%<=Rrc>s-_*F~glH50rZ|A>-~oCnF*;Y|V#3jr)N=M5mCk%1VDQ5h3}1jpuiOPt3_X6@{sr#5+iPfPVN7(>OhsovhJ1gb52MINprs-!#w}e)v?cA zaPhgZrjrpFmm{F+`5>fTGGdO)d@~yWzL@#3#logLL#D8CGM75C zbqY6QS5Mh7Sx-G*jQDd1^q?f-0=(H0?rlvho*SjYl5=F0;xDnwd60KaXZC@nmajw5 zWY?szY7T0Dd8WGvpbc+2JXUBeSroA+Z5-kSen=itM(A+g{HhcaXH!TX__ty|#-j4Y znLw!N>oHvJW9MvpPvl{WTFR4>=ehb$699g-Fd;V%=%41QOF0OsuTzJe`W!XOk?RuCpLdL1H#AKd zE7Y!z_p-b?<}X=s=!~Am#9w?_0m0yI){enaKgkK4krQG*qy{xX&A0c-&<=N?XEIRH zN9Gsj%B+a{;;p_+j&~VrQ#e>M#rsqOMSW{2+4E<=r1t2Z=4ALqhg}s&%!fV;$Fc4- zapp-dm`)r)gu{wp!XN*q_OMia08kQ9sSE_A`??nMF1RjHM0NI}B;ntm>?_Pq0qqI* zXmEvS8qShEu$v}euE0y?rEqE(NX+-E7N?1MXF)uPlc{_+a>>yw$0#PH+Uto4`egiq z(Wt1W-Qh*c07e~C_<|tACvvlTFZd1dFEv>NK``mjRcps)q(u5F9(dx0Ni>OW_lCvi z>*VZSA-7zyEg~*cH$2eQD`8)npt7I_jnjMzs43dj;c-5gy&PlbJGZaVok#8Te#n^7q?JS@lhYMU1oz`5`f>y!#sMx)MupyiWSRnxYTxZ{WYgR|%|Y41)&_#1X{KvH z2&VeMi#MSD@&W+kV-Q3E89BG;YKI~EIfESBw5gW7Ssr-?qn~vPT{X}A9EYYftKHPu z!gw6_%~2vneg-i9)1~_vWlYarxDagPs4AlJLsBr2wGazKc^y0_ zNv=gk*BUC-9?s%iBx1}l_xSz8M**g8@c+C86&zR|Prmhep&Hn(8GiJ{=Bc3gqy!pt z3UAS?wBca`BicJ4Z>&=aB;$ap5-0Z*B1XYHRzqz)JgCLlXcpGzEoNv=D)Meo_EPe9Ia zKyV2>cj~0RUjpAPYGF+ZFRHK_wFwMmqX$G)x0uGoiH;Cz6;gE1j~)07M(a;GjDD;{hyxb5!W$)xP{+?uSvwIP>;?w{T zi;7V(l^7oTqu1SgH?cMSi-3T6TD`02Emg%k1MytCgZG;r|)>9MXb$C~^LAIfafpSy~1hV|+DCHTa| z{OCDqZhGq*Z_ChsDbLRA_`ZNaKm;voUCgRf=X?D<{rJpaU?X;&4-6(<&Shd+Wy3-Q z%b%_dmYrlXfZ3$L6>Z{aDDWuO7KY+It|-J+ZN?{{LqrH1M6f(^*u;`PDjmR=Xq4)` zd`hBmt`!*rYMmak)r@sW!5qXb_F+vt03_u{2zvgIwq7x*zn!eIH8N05+3Zp?eJ;y$}g`FznIk^_EMDsD|FVlz-JTxCNupma<~BjQj6bT*{2Fjy{pDoa z1Y`N&xTitOTpWG1uJ1oUl4LZdCLvKgy) zqJr58ZXTCP@3S-+ArzbSb2!ldAOq&c^^Ta^k^ZxOCF#rxw3}b6(LLjUiz_LG(y6+z z{j9xiYX58|Bzr2XJd-t%Vh0PzcVMk&(O6rP z^_1`M1^xdqIDMvUwWZp@Gx+t>(B30Nmb#-s^Wgi$B_z}v7ZEmsz&O)JP+&xtb3lZ; zWCPADDlVM@ESZ856_@N$ezKebp6058$T&vC7N!XrX99ov)atVQYj){MG{Yf`Nkm0& zjeab63VQ1VoF~qS&T1u%hfmQRwMvI3{(8EMeSE*!`y zLJq?(_1K{;ajxju*HRc3?etK79E3x-moAAN-g5=R8-QmWskPFimEmR?`sKG0P$DNT zM>20*Uy7~W9Cdq;M{zCM&4;p!m7W;`TOQNBrw;`NEA}dB!&bo$P82fTr_`bP9K0MOGr5h z8p2!q&^vpWw_^mGEE{a2$RG-})K3VxxEU^QSS)hk#@W%f2~tALEz);}6pQk{yawWn zqbgvZ&3LW=`T&&YvDXOkS?WsMoJm_L|EHI9=vQ^Um@)_=pLpp z@|aCt=uqM7EcCAd)2vT>E&66n*d5SL2w|v^#XLzd4;%XPo54|D<&u(8KS9j@OhZmM z?C}CMv3oPSv_^x1AV+7`eQ;>jfh!&b8|NzOvL^XzO- z$sKM}7b*4k!lK(w3{oYbm7!L33hOB!wYYu=Bd3L#VOH}aFCxmVaW8y(5IB9xxEwH7 z>*b!B;h)xE2L{Ne<7OSe@sF2WP9M+Gfdg$8BOSuBQHFZOGI+|`@s{=s_#Fg|@r59l zd~qNBie{zdLs{naP>Iaautaj0aQ31|&D1HR6mS=n^Y44!x1oJXT4yRqLuh;p>{n72 z-&T1?9$pXVHIiP&<>7c?oMLQ+Zx-YVaXeWaN=7BKf|+6kKEvEO#W)F0x-Dm7wa+=W zgE3!mI76*}ts>T3nGmB3iXVk)`@o2Gr$=y#yFd-D;OtE~t%4QRc0)nEme}8Z@g-+5 z<(UNo%+xv^k{K9EA~Ep z)VEP!!z6iQGb8m9&(C&~V3EyKUBuB3^db-9<0ikl;I*7!ZX+`?F`d@-aNLM5YWso$y{UwXillN|HXjLt1tPH;aAAtRoa|F7WuM$ReCR`W+0 zCtzij*;5{_+z&)6`IbU6flyF*boPcWv?OQkLRB1N_gAWv(5UkyHHv~9UTLbJN}tZ! z+y&!v?!Yk1nq!MY2nc#DWcnfXKo%-BC-)cQq1-f;QODGTzU z5Cw@p_vPDgKYQYXrGjEutTLU_QbMWN9I0EuK(Ma+N49+F8gr70addcn@2Z4QD{8H^B}lQ)6zV4y&iT){iW^?I zpCSk+CTxJ!di(6Jsz*=Oqgor~fDb~uG;GS%%T8sXR3qe0p&fKmtB|9l%`7VsPgr4Y zGR8)KC}#q+#t!q@c3&1+Y$IK()`N2M5j8?iD4!j^{yMfc9qkEp?o23MzBT-NdLFtc zCcVpF%`&tnZzj&WxH$uMox|LnF)vq5#XJv^tMRD?^vR_!0FW{^~W^ybF1gamGHh0ro`@}cW`jciK&<=ZlM+Dx8#i5Ss zE(d21us>}OqK;VB?77_*W&XRCqq2G|6{r_9Kf+d6oag_3qrlFymr{SJ!|PB`<{j@9 zx1a=MV8_6}fEtmLsvPmomKh_2S-oU5rPFLsKfWH#PXYMcmOL@T= zZ6&!~ZI^hXloFx4=7Qa>8dLwAoMXF6uo4wcb~9s~xn)8fkz0|~DyTa)=Ze#>O$wtjERS;~r+a?N`VMQ`;Di&`5!ERghzLxh}2;?7A#sP~4na z(ZTV}Ol3@z+*1lbOVyQl)P1|wU*RLhWH$*AFBrKi(1}x!2`C&;MZrZo#8v4vE{NR6 z4<2$=2~Lj_Jg2rG5PV^$1Yw*~%PL_M@)i0Kxf(BBQq8 zd4M}$rL>61bD+ra2ys$t}z*gJUgk;PrPc`L7K`Y503Ml#OY(h)_ zHK83wL3WIy93wBEdYY3UosXrs_~_#2Wqm4$YzB^^P3*incLC2=i*NRfpg;ZLw1^{Q z^CXo!DN?RONXoQS0cNk1|0Qqf20O>*64`FF2r1DEwXK9)YMUq8foN1+otjVTDH1SQ z38!I!hl<3ZJ-3e%nuzl%AGrI~hyshlbfB(lNk;Asg?#9rvZn<`sd$es^;a8s*A1|| zGaGdE**cP*D?7%djfmJ!EiZhx37A??V;a?o)!M%Zq1h)+tewg%Z3Z~gNNZ$2$-nb| z4c9V#Jx+dC+;gtdkS3A>3;=Mpp25@sv|SzGY6kBjwjgpO!@M~gMduACb9l{fy_(jd zxP?w!#=u+nibU%#cLk$~1upORj-I|Hdqf`iW4dj37+6q?jxJvD33WB}^q)Y(R9fiC zV5?)c;bl?O3dN`bO`e<7uC%pzdQk$|$WOnk(PXK6*fjW~wy#s#tH_?<3XDh`Z z1iQIqo1;n`w3!@-?*#M;Zujc0&TU_-F^JXe7@0FYWLdFFAUcj?Ju3Z{iQYI zoR}5l5TuS)725rc@0iOzyeY3{CtgDVhNg$nFswx3k*wGj z-=~Cwv5Zl=vjnCvTUwt5GD!b*K9`%*cQM7PvcQxrTCo^f07c zOrC^Py9Ai^PlgLgQV7Aj062Nrh%Lv-s?01jFb%oi25_=iV9n;Ho)pB3+O)tvF;^P02Mb^4snFBu8mA-|C)RnIOMdav_)cD9;WqrS9Z%3KzavYGmW91pRPn zIYf2@$emK(7JV2*C+0PDBcSkpl)}sNsFYes9%Fd^>=pE5oq@?s$yzvGK7*nWJ0xl~ z8-4MfcW54?7xYPAYGVFv12e1CbY?kvq*e4WAc`Wro7x_Yc?G#nXZ30(xeVmb#YKnVr z)9UUS*ff`b=m*zo0HgPCV^JlLgHb4zi1yh}=Z`v`!}$Q}M;K4}*pZU!_hFj&hjJ_*H~5nL+K3m$JGF(Tln|93)CVc@qmF z8vj7HQw6W6Q6vCc@#Bo50b?#|NG4BR8iZW>%rXt z(Yet|t+g}aNg7cXfbBC`1nhJ7rGiKz+!cLwpp z4-2rYmzFIkRz|lbYBGa}`Uog}*OX1Zsik&g4k6qAM+O zpsq!`voP)aMmj5EH4`l1A?nXF_D#F)=fr+I+HpgLfp2cgqEPL$8j5JIW);? zY(xFkCT=S}(jmGEV}4~ttn3XJo&qcv=rbs##yEU<{OYX+o{QdIGQ&?1Ao{<;=Fm;N zyqC*v`D@&8N9B|eAzWR+xls8El>|ID?b?Lkjcj%@smltSFlX16hwQ6sYcppu3$srt zJPYu(*xQs|I}n{v;ZU?JhciL}w;EQeh?bzma+)4o4|GtLEC_v%;coU2!<2k<*@%qF$MQk+F|7V#S7V5JD zR*7A8cYkFSyj9Pae=0b)Yae-c$(%s*-Xebk8SsmI9-fpBWbF7hn#i^krzpXHGMik* zu8&O;`OIP>KPR&BB)!i<1+xBsnV%{NbOWescZ-a{<{0Gqs&e#+cbp1CF`DWPFM=fw z$E|@4mUU+G&8=YP4dYr!$5csCNu0}kZiPv-)e|E?m#m*;f)wzJ0M>ypMY9Q~IQ12m zl621haq|HYTR=3rgW_V^a(5et;C3#hi=EosDIlg%W&V4y+0_1JB$Z4bkEH!c+lthO zEKN*$C_A*$_}%3aOOb-fL9-=fASxzgz9wt z3%n5Wvtb5Ec#t@t&VYT$^CBp;1+RjZj>cl zm`V*_cPJxSI{+I8Z_~BESi=YVB%YG8bCrX^kCMFl@K!C(2rUqrrQ3&fe&k)aXqqHb z5^ZG<0gSGca8FDi=_{f>lTnR4GalpAbbki;j*H|yrS4tHi8WzLtiErh<*uh0BeAQa zFnhSTIxs6GwbLad6F8~UzPD(7RJVN=AzH3=3j=v1wf>GzV)!pL0j?-1>6piG?v70!l}(mj9X9u{a*{2@>2{|mxBFEhHG z&cKN+pXvfCH7`lN)BWeHi^o75Y$d4@6}@`cZMM@VK0@|ze#+Y`H>BzAywhw_BZNA5 zfsk{FP_E0JE&%*ENPhfLGfsSN#es3R-vplZ!>NHjg9~TI;K6RV_T5k<5QGFjD}j>- zbq2X)(rHMT3v&BFYqM9!nLC%eVk`Vm7tu09r4e+a`oB*~4TZ8gXThc}4pL!ul3g6S z3ht0hdUL!^YVVO2xYF$kN_#V9Vb6Nnf4$cBwQm7;?sD~L^aDi*w)>$8Pk|PWgUZ(8 z+GR=m?=2gXC646X7MfUiF3Ynxt3<&pqeizqK->SwzVVPprvku8-xca(cTR(xS<3BP=j*-VPK4-(Uc-){yQuG}sT%hu<4DEbk}anr6Ej_L{kM!M1xv4zOz0fS``f z>-VZqrw8OahM7{4o(X-DH2@~vM!?*nxr()nQ?B;07D+l!{;2`GZ1)@Fp>xJuH7-aM zkvn*rpjcV-z_atxQtkxvM$K!V{qrjWi_om*gYW>Yyrlk1V0wFYO>|)dEg~JTOz`PNByxI9CUhOxvh%4e=!67uLrtp z9Md-{Obi0DL(VQ}dluG@5j|R5ZXY~{6G+50>S>2-Hr| zkhuwiiXvVt=|pEA(LF?pGZBytd_@cG-4Q-)IX7w2xN=MYIz9H7r5P@(i3#bT1_#Yp1oxXVv|>+d9K7spMJ;T#W;trR{F#o0=>DEEb+wF; zbD+!*qjZRCXuV~Aq<*~(v&;Z~7NN_Cxt~h|hiblMT1{sXRa1W1Xy*f34HLVR$AuYw z1DC{blY>jR+s^}jE7q^n0hoPF*O!z*8J;!XIWJF5$5;4NP||^HO0pl2!Vg9BTr1JX zUk=0kStF0L8Y8oL6p4`;^T(>|XwzY3DGB2yIP_;OEKDfSIozy@d%9}HK2a$$95;Ob zN3z0>vB+m{Woop~-pqp12UdQ_ip%X~S6_Ux%tr9I!&5>Ezls7jnNn{`(@nV;LkJ>7kM3E%q>n-|jlaw# zX4K^D0Nh)1YO4^?;?IZNO`)6Myz#Q=zA0~0fJaqFODjC-E~R!o^#a0wu6dBtO^5HbV^WFJ~ zkOD?i8dBRPymUXs(ry~r=w5c<#h}|(n{o$SrJKaxNiIyiaU5`2tbtzO6ONUE(=P37 z5E0>0EcQ5ynptJICqGIElm-m2Au+}q;czXlQO5Arcc5Ms*F=JXow^WE2B~W_W#Zuc z{u2|9&m`I<)?oKD{Q3>3vt;SIz{g{FmgS6|{6L%GPLwnUtsvxZ5v4}Q?q;y|@!XPQ zNg#0(mJHUgY?}ZbLnF46kQSxe@2(vzp(K<6u6D;>U2B& ztJAK6T#n-mN`@G;mB2lMqd@8sCWO<^DdL!j8HA|;Gb1g~MVOjsRm?;_H_V7@Vj34H zNf#VS#_%K-29FkGHvSkqIt4I2EpZM#WuUb@+Ky6=7l~zlwT_+Qm5CZdf7c+1Iza@x z;L>x5eyM=bu74rCd@+9FdXVX>4owEmjGr79i{$*mCjrD;93mqdP%VPY7K#wXATy8I zPTI&x1gtr}1O94-73nhxCUv`GW~fAO787DQ@wU479!xW9`!^&hB?RRL-htN|6I(@C zMnWoIGKwzPya<>Cs%EIiKyk1Q6Sb3d&X=OvyKUO<=tw9Nj)4IoY2JnBi6>D#=1%lT z8j2|?_GcXhoQuP?z#?+o^A0bst@{%3R*>!rPx1cgP-~gRr1RvHc13H0YB% zAw`B`Q9&tRK8)Cxtzp7@s7NT7Ev z2|5Gj1qgf*E7;WSVS0SyffMF(rOJr$Wxft!UHsYCjDFYa14TaU2{QF7)v%K9K3)zm z`5$*YHrO0z+roJ4zxV^Xd}|JPG+Fju`dvIHZ_n<61JLE@kOnKg)u-&Lcds#|DwCX) zruj1uYetb+BM1q_dq(qf!~P+Uo8toZkL(Jq$@Iqb+LLfQYf<+84eBq6vjn>QNOz@{ zK*)TnPMNp!nGnl&?36>VO@Ij7lU;jzz6GHLzgmx>4Qw z9}GORr{LCC#kjo0>U+X$06E|vX0R%c8T|pQ95;-yL^ag}`yVQA0z9QsT?EPV_arP% zUsxb%u?F@_!-N+@40Nyb8=`oo=tzbw@l^KqJ5T|gP`Lwj2F@$&0ATfJ&FFVTWwL6z`7i*U|owh$;SpCduX;B3Z&hn+uK z(c=@zxYrY8OGgA==~c(F>MVlZT4M3JV)lYtEUcb8O{#mD+ols({BSnN_Ij%=0M}Ia zJWN}BjoVlY`3*%6cE{TYpsciK6>nBUT@QY+Re{Fh4N%C-f+nm!u)T^ifGeCF0mKPl z+F~$D4%TM0v0{1!f?D?eK%3!xn`_wicg*NC;%(zX_&DyeD1J?di`$UJa6Fc~uX0s1 zIP0m}G;(=h#Bt^$twPFfAVi~?^qOQQ9?9U1C4MI>SsnxmSVv%||6Fz_B78P=lx*O{ zxSTXUSRA;dZ*VM!{BL46F*LmPJW&mt1`6|{4ZzZ#Iu(!icByqqzkyR=f>M;4`-2dc zUx2xR9T3FKOtaa~?_awoA)P_2@X)6F-@-ukhpPqpKS{i`PjL_}OAo?qlC#(r3GPQu zL<*2S7)6iqY1N=6xeH<=Og{sPej55+!aB4$*%6K=vhc;xLd2aVdx#h7*xdin3kLHe zb!*bDJNG$3>-30mJ(Dpb-T8D7Tuxr56#iC4sF%O7)TryTe!q$zKKHr?!L)0Kvk-9N z!cbX{yzdkn`H-MxHrgtV@H>mOviFg*9Wd|=li2*lkDYA_hlrEk91`!CPHE}_W^my4 zO8m}ZYAt88uxmkbt2qfLw?X;G#8e`?~R%%xZ8?3L&Tm?CtFSfIMk~19rAy^16GAKzj zv8Q}LMsdT|Q!sJ*2^>Q)&bgQ$U8jBwwvZUZrju3tV`;(z&oAQg^OtUZdE zMor-E&L~PbR?Z3^*_&Mrm~MVyQrMB3=4&BR#JmH|Tsk!ahhSD+=@!e^gnOP>Dpmi9v*vT*F8^gWc4@SMZomA zG{`=fwgeJ7HVmS&qIhnHD>oqT>Xr)Gy@=7gMs_&Q906tune}och<|X#BV?vmFxnbR zIRRtB`*|XUPyshH&-cX+)J3765|Iuvs#4Sj=#KIH^+1gKXb|gUY%iCSDa%D#Fhag7 z&C;-}()>HgKeu1_*_1GMJIXg?aGq-9Rmzdo!US2CF@w z7vF#Gp#WW1OO>%lpf5{D;&3X6`r`0g{wu+u*!im?CO^OA!MB#BJIwT-cdLI38+=bm zT1>3cD69kU(?QkTT`|Vb5SDj{xC(9Bqef#foe)sz9v&UNY?3vzie9OpenEmkTC+Hs zMG(GHYO^TUMnr#$iFwP{(NRR=W}WAY#l}mRQTz=Z&3r}+GzZ8Lr1W7xMlt{!5;3%& z>A&loLkf7o`-E-n;Hk8Iz1PE4({TtlIXG0&Kw!oPHZo{Obbgfp$%^Iy#tsFKKXGuq zudV$^((Oxr`*fq!bL&yMQ_{12KxUHUAII+4_~1YjMq0* z!j~R-$~#daSe-IW|zYau{O#aMvbLiuo#u@Rq67ejw3!^ zBgOggT;RQ{p@mhca3GW!pdQHGT)oi1dv4cToB&_>S-aboP?~#i9?(j(*~9zcROu#k z4Ft>pI+id<$1wXXHGTTWc^!BmYSlaJ0$7GN^#%Mx2M%0%Dz zu(7TC*2lm_3FX5zxm$EEExFh~nX9ni%=E$wB--x(aSn!Oh+M+_^0r)FtS+b>#5t8z z@T1;Y60v@_cD>j!%CehDLF#MJ!vd5dpU_Q#l>leLvR=>dxq@Hw5HB>(yOiq?tNv#u znvT1FF8oa5K7p!%NxYPcIv9xbJsvazzRy``u)tr&dGoBA=5yYf>h8@uKxxZQ8;e5m zhi&uReb&GN(vV%W$Bsw%(NEM5ey)k*Suf8mrvU4zzVkjbK=UcvahwG+_5|-sa~~RZ z)szxIOpOPM2D(g->Y9+CSq^XY$s48>rX?+tNCE^29yyymjiHL2sm6^Mg&|c3=bD7r z#jOev<9kS^B%kVBtpdv>h@uNSf+WeX^}up4IX6|uEOm1U|EmO#oTF#pT!+g=2nw;Q z0C~v>=q$N-UE$4mo28OessHf4=AMj9`;GYvOUIJHWL55z+-OGT`+s*XR98a@3Ot3~ zlIc%L2mJi~*(QgwbxC!?1KHTlNzyVa90c&enke6>BbuUGai1=lp_9A|LYF0)w-B)uL!PSbId<3GjfL zCK2*5BA04Y&LRW2l8pRq7zj)+42c0>Z?2NyojcXzZySH4ql12O6AHkj@EoGv>zdZI zK#zO}ew;KkD@#sNu3l^|b{arV>OhAS3h}LfEW5jfaq>c9gIXQ+_cERtxmX%CRko9R z2|7N~;k@)CD2hpf(BK8h(Xx^&6gHNQs&9bH8I_b0)Kh7VDAR&payzl$0=Mx^#K83V zF79jaom3%%gUPsNb@Y{M*NB2#lyJDZyF{>#^rX)d0v(TgdNFQx zUflRwz4+&Xrx{d@fwHiX^gf>%XvR_~vamXS&KCk=DM5dBkUoSHe|qDOhpkda|3@;? ztFsi1$H__^tCA-jP|iHkxLdnaiTjkt@gm5ROF&{h1f+ir4l`3yo#zRCT>-(&`~l3Gv%2jdt#sWf0Y#K5Dq$abZUHxPB zo_3`7RkA(nn1hDUR_y1f&@>{a@$wOESyQ zV34{Hhdy#9iPf3JFe^?{N6WATcFj!!*yvUbH)f(ZJ-a#cDZ3!5PM%YySu7?>Em`DW zZC1!VP_FaSr_xE>0gu?v6;|th{u-y1{rX@#V``}IQ;HozewR0d%8EOrSleZ8@ZLYpBk z&yV+`txA^7?>h!fwxm`O0Ba99;<|9ua)41iKWu5W!Z;{>#Vw>csa*R?A-AAH>#X+;$nrl=|tgR4A4x z%RBR~r*?ZZY#!{NgX!_?X95{gMV%#YiYTElhD#T9eIaz7cf8QY;qG9pu8e+BWlEiKz zx}fraj8N*xD;l{H6pS3se$Wn}0Os)juX|ja(@3#qneQE?3elu@fVbZHeKwYv%q;O5 z1W$}$e`b$Y&RUh+7Y4%m2LTC#(*Oqx0RRCb0|5aAT>uaO01S?93IVwRV%-1$-jTyV literal 0 HcmV?d00001 diff --git a/gui/react/src/Layout.tsx b/gui/react/src/Layout.tsx index 450f00f..30cb33d 100644 --- a/gui/react/src/Layout.tsx +++ b/gui/react/src/Layout.tsx @@ -13,15 +13,20 @@ const Layout: React.FC = () => { const messageHandler = React.useContext(messageChannelContext); - return + return - + - - - - + + diff --git a/gui/react/src/Style.tsx b/gui/react/src/Style.tsx index 4f44a07..4f4306f 100644 --- a/gui/react/src/Style.tsx +++ b/gui/react/src/Style.tsx @@ -11,10 +11,8 @@ const makeTheme = (mode: 'dark'|'light') : Partial => { const Style: FCWithChildren = ({children}) => { return - {children} - ; }; diff --git a/gui/react/src/components/AddToQueue/AddToQueue.tsx b/gui/react/src/components/AddToQueue/AddToQueue.tsx index 98333aa..8bbd414 100644 --- a/gui/react/src/components/AddToQueue/AddToQueue.tsx +++ b/gui/react/src/components/AddToQueue/AddToQueue.tsx @@ -1,5 +1,5 @@ import { Add } from '@mui/icons-material'; -import { Box, Button, Dialog, Divider } from '@mui/material'; +import { Box, Button, Dialog, Divider, Typography } from '@mui/material'; import React from 'react'; import DownloadSelector from './DownloadSelector/DownloadSelector'; import EpisodeListing from './DownloadSelector/Listing/EpisodeListing'; @@ -10,14 +10,14 @@ const AddToQueue: React.FC = () => { return -

setOpen(false)} maxWidth='md'> - + setOpen(false)} maxWidth='md' PaperProps={{ elevation:4 }}> + - Options + setOpen(false)} /> - diff --git a/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx b/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx index 489b8f1..d1d5ed4 100644 --- a/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx +++ b/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import { Box, Button, TextField } from '@mui/material'; +import { Box, Button, Divider, InputBase, Link, MenuItem, Select, TextField, Tooltip, Typography } from '@mui/material'; import useStore from '../../../hooks/useStore'; import MultiSelect from '../../reusable/MultiSelect'; import { messageChannelContext } from '../../../provider/MessageChannel'; import LoadingButton from '@mui/lab/LoadingButton'; import { useSnackbar } from 'notistack'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; type DownloadSelectorProps = { onFinish?: () => unknown @@ -78,14 +79,36 @@ const DownloadSelector: React.FC = ({ onFinish }) => { setLoading(false); }; - return - + return + + + + + General Options + { dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, id: e.target.value } }); - }} label='Item ID' /> + }} label='Show ID'/> { const parsed = parseInt(e.target.value); if (isNaN(parsed) || parsed < 0 || parsed > 10) @@ -94,13 +117,77 @@ const DownloadSelector: React.FC = ({ onFinish }) => { type: 'downloadOptions', payload: { ...store.downloadOptions, q: parsed } }); - }} label='Quality Level (0 for max)' /> - { - dispatch({ - type: 'downloadOptions', - payload: { ...store.downloadOptions, e: e.target.value } - }); - }} label='Episode Select' /> + }} label='Quality Level (0 for max)'/> + + + + + + + Currently only supported on Hidive + + } + arrow + placement='top'> + + + + + + Episode Options + + + + { + dispatch({ + type: 'downloadOptions', + payload: { ...store.downloadOptions, e: e.target.value } + }); + }} placeholder='Episode Select'/> + + List
Episodes
+
+
+ + +
+ + + Language Options + = ({ onFinish }) => { }} allOption /> + = ({ onFinish }) => { }); }} /> - { + + Comming Soon™ + + } + arrow placement='top'> + + + + + + + + + + Burns the selected subtitle PERMANENTLY onto the video
You can choose only 1 subtitle per video + + } arrow placement='top'> + +
+
+
+
+
+ + + { dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, fileName: e.target.value } }); - }} sx={{ width: '50%' }} label='Filename' /> - - - - - - - - List episodes - Add to Queue + }} sx={{ width: '87%' }} label='Filename Overwrite' /> + + Click here to see the documentation + + } arrow placement='top'> + + + + + + + + Add to Queue + ; }; diff --git a/gui/react/src/components/MainFrame/MainFrame.css b/gui/react/src/components/MainFrame/MainFrame.css deleted file mode 100644 index 2bbc1ad..0000000 --- a/gui/react/src/components/MainFrame/MainFrame.css +++ /dev/null @@ -1,4 +0,0 @@ -.divider-width::before, .divider-width::after { - border-top: 3px solid white !important; - transform: translateY(40%) !important; -} \ No newline at end of file diff --git a/gui/react/src/components/MainFrame/MainFrame.tsx b/gui/react/src/components/MainFrame/MainFrame.tsx index de4db33..425b321 100644 --- a/gui/react/src/components/MainFrame/MainFrame.tsx +++ b/gui/react/src/components/MainFrame/MainFrame.tsx @@ -1,10 +1,9 @@ import { Box } from '@mui/material'; import React from 'react'; -import './MainFrame.css'; import Queue from './Queue/Queue'; const MainFrame: React.FC = () => { - return + return ; }; diff --git a/gui/react/src/components/MainFrame/Queue/Queue.tsx b/gui/react/src/components/MainFrame/Queue/Queue.tsx index 5a79c9e..319d37e 100644 --- a/gui/react/src/components/MainFrame/Queue/Queue.tsx +++ b/gui/react/src/components/MainFrame/Queue/Queue.tsx @@ -1,7 +1,8 @@ -import { Box, Button, CircularProgress, Divider, LinearProgress, Skeleton, Typography } from '@mui/material'; +import { Badge, Box, Button, CircularProgress, Divider, IconButton, LinearProgress, Skeleton, Tooltip, Typography } from '@mui/material'; import React from 'react'; import { messageChannelContext } from '../../../provider/MessageChannel'; import { queueContext } from '../../../provider/QueueProvider'; +import DeleteIcon from '@mui/icons-material/Delete'; import useDownloadManager from '../DownloadManager/DownloadManager'; @@ -16,102 +17,384 @@ const Queue: React.FC = () => { return data || queue.length > 0 ? <> {data && <> - - Thumbnail - - - - - {data.downloadInfo.title} - - - Language: {data.downloadInfo.language.name} - - - + + + Thumbnail + + + + + {data.downloadInfo.parent.title} + + {data.downloadInfo.title} + - + + + + Downloading: {data.downloadInfo.language.name} + + + + + - + {data.progress.cur} / {(data.progress.total)} parts ({data.progress.percent}% | {formatTime(data.progress.time)} | {(data.progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s | {(data.progress.bytes / 1024 / 1024).toFixed(2)}MB) + + } { current && !data && <> - - Thumbnail - - - - - {current.title} - - - Language: - - - - {current.parent.title} - - - - - - 0 / ? parts (0% | X:XX | 0 MB/s | 0MB) - - + + + Thumbnail + + + + + + {current.parent.title} + + + {current.title} + + + + + Downloading: + + + + + + + + + + 0 / ? parts (0% | XX:XX | 0 MB/s | 0MB) + + + + + + } - {queue.length && data && } {queue.map((queueItem, index, { length }) => { - return - - Thumbnail - - - - - {queueItem.title} + return + + Thumbnail + + + + {queueItem.parent.title} - - Languages: {queueItem.dubLang.join(', ')} + + S{queueItem.parent.season}E{queueItem.episode} + + + {queueItem.title} + + + + + Dub(s): {queueItem.dubLang.join(', ')} + + + Sub(s): {queueItem.dlsubs.join(', ')} + + + Quality: {queueItem.q} - - {queueItem.parent.title} - - - - S{queueItem.parent.season}E{queueItem.episode}
- Quality: {queueItem.q} -
- + }} + sx={{ + backgroundColor: '#ff573a25', + height: '40px', + width: '40px', + margin: '4rem', + transition: '250ms', + '&:hover' : { + backgroundColor: '#ff573a', + } + }}> + + + +
+
- {index < length - 1 && } -
; + ; })} - : - + : + Selected episodes will be shown here - - - - - + + + + + + + + + + + + ; diff --git a/gui/react/src/components/MenuBar/MenuBar.tsx b/gui/react/src/components/MenuBar/MenuBar.tsx index 5d74377..b1125a4 100644 --- a/gui/react/src/components/MenuBar/MenuBar.tsx +++ b/gui/react/src/components/MenuBar/MenuBar.tsx @@ -44,7 +44,7 @@ const MenuBar: React.FC = () => { if (!msg) return <>; - return + return @@ -108,7 +108,7 @@ const MenuBar: React.FC = () => { Version: {store.version} - + {transformService(store.service)} ; diff --git a/gui/react/src/provider/ServiceProvider.tsx b/gui/react/src/provider/ServiceProvider.tsx index 9aecfb5..ed862b1 100644 --- a/gui/react/src/provider/ServiceProvider.tsx +++ b/gui/react/src/provider/ServiceProvider.tsx @@ -18,13 +18,11 @@ const ServiceProvider: FCWithChildren = ({ children }) => { }; return service === undefined ? - - Please choose your service + + Please select your service - - diff --git a/gui/react/src/provider/Store.tsx b/gui/react/src/provider/Store.tsx index 5054051..c82ef92 100644 --- a/gui/react/src/provider/Store.tsx +++ b/gui/react/src/provider/Store.tsx @@ -13,6 +13,8 @@ export type DownloadOptions = { all: boolean, but: boolean, novids: boolean, + hslang?: string, + simul: boolean, noaudio: boolean } @@ -48,7 +50,8 @@ const initialState: StoreState = { all: false, but: false, noaudio: false, - novids: false + novids: false, + simul: false }, service: undefined, episodeListing: [], diff --git a/gui/server/services/crunchyroll.ts b/gui/server/services/crunchyroll.ts index 1b9f931..2cb635d 100644 --- a/gui/server/services/crunchyroll.ts +++ b/gui/server/services/crunchyroll.ts @@ -99,7 +99,7 @@ class CrunchyHandler extends Base implements MessageHandler { if (res.isOk) { for (const select of res.value) { if (!(await this.crunchy.downloadEpisode(select, {..._default, skipsubs: false, callbackMaker: this.makeProgressHandler.bind(this), q: data.q, fileName: data.fileName, dlsubs: data.dlsubs, dlVideoOnce: data.dlVideoOnce, force: 'y', - novids: data.novids }))) { + novids: data.novids, hslang: data.hslang || '' }))) { const er = new Error(`Unable to download episode ${data.e} from ${data.id}`); er.name = 'Download error'; this.alertError(er); diff --git a/gui/server/services/funimation.ts b/gui/server/services/funimation.ts index bee900e..3bfc4eb 100644 --- a/gui/server/services/funimation.ts +++ b/gui/server/services/funimation.ts @@ -70,7 +70,7 @@ class FunimationHandler extends Base implements MessageHandler { }, image: a.image, e: a.episodeID, - episode: a.epsiodeNumber, + episode: a.epsiodeNumber }; })); return true; diff --git a/gui/server/services/hidive.ts b/gui/server/services/hidive.ts index f3da018..e07eea2 100644 --- a/gui/server/services/hidive.ts +++ b/gui/server/services/hidive.ts @@ -118,7 +118,7 @@ class HidiveHandler extends Base implements MessageHandler { return this.alertError(new Error('Download failed upstream, check for additional logs')); for (const ep of res.value) { - await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids}); + await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids }); } this.sendMessage({ name: 'finish', data: undefined }); this.setDownloading(false); From 1f4d73aa0eb5c4d28cb802ad0367578212ee55a1 Mon Sep 17 00:00:00 2001 From: DAREKON <63023042+DAREK0N@users.noreply.github.com> Date: Fri, 16 Feb 2024 22:29:20 +0100 Subject: [PATCH 42/69] Updated GUI --- gui/react/src/Layout.tsx | 7 ++-- gui/react/src/Style.tsx | 2 +- .../src/components/AddToQueue/AddToQueue.tsx | 2 +- gui/react/src/components/LogoutButton.tsx | 1 + .../src/components/MainFrame/Queue/Queue.tsx | 41 +++++++++++-------- gui/react/src/components/MenuBar/MenuBar.tsx | 6 ++- gui/react/src/components/StartQueue.tsx | 1 + gui/react/src/index.tsx | 10 ++++- gui/react/src/provider/ServiceProvider.tsx | 2 +- 9 files changed, 46 insertions(+), 26 deletions(-) diff --git a/gui/react/src/Layout.tsx b/gui/react/src/Layout.tsx index 30cb33d..54bc212 100644 --- a/gui/react/src/Layout.tsx +++ b/gui/react/src/Layout.tsx @@ -13,14 +13,15 @@ const Layout: React.FC = () => { const messageHandler = React.useContext(messageChannelContext); - return + return diff --git a/gui/react/src/Style.tsx b/gui/react/src/Style.tsx index 4f4306f..8ac45f8 100644 --- a/gui/react/src/Style.tsx +++ b/gui/react/src/Style.tsx @@ -11,7 +11,7 @@ const makeTheme = (mode: 'dark'|'light') : Partial => { const Style: FCWithChildren = ({children}) => { return - + {children} ; }; diff --git a/gui/react/src/components/AddToQueue/AddToQueue.tsx b/gui/react/src/components/AddToQueue/AddToQueue.tsx index 8bbd414..6e19041 100644 --- a/gui/react/src/components/AddToQueue/AddToQueue.tsx +++ b/gui/react/src/components/AddToQueue/AddToQueue.tsx @@ -17,7 +17,7 @@ const AddToQueue: React.FC = () => { setOpen(false)} />
- diff --git a/gui/react/src/components/LogoutButton.tsx b/gui/react/src/components/LogoutButton.tsx index c324dda..916db7a 100644 --- a/gui/react/src/components/LogoutButton.tsx +++ b/gui/react/src/components/LogoutButton.tsx @@ -26,6 +26,7 @@ const LogoutButton: React.FC = () => { startIcon={} variant='contained' onClick={logout} + sx={{ maxHeight: '2.3rem' }} > Service select diff --git a/gui/react/src/components/MainFrame/Queue/Queue.tsx b/gui/react/src/components/MainFrame/Queue/Queue.tsx index 319d37e..315354a 100644 --- a/gui/react/src/components/MainFrame/Queue/Queue.tsx +++ b/gui/react/src/components/MainFrame/Queue/Queue.tsx @@ -26,8 +26,9 @@ const Queue: React.FC = () => { { current && !data && <> { margin: '5px', boxShadow: '0px 0px 10px #00000090', userSelect: 'none', + maxWidth: '20.5rem', }} src={current.image} height='auto' width='auto' alt="Thumbnail" /> { display: 'flex', flexDirection: 'column', width: '100%', - justifyContent: 'center' + justifyContent: 'center', + //backgroundColor: '#ffffff0f' }}> @@ -188,7 +192,8 @@ const Queue: React.FC = () => { { {queue.map((queueItem, index, { length }) => { return @@ -244,26 +248,30 @@ const Queue: React.FC = () => { marginTop: '1.5rem', marginBottom: '1.5rem', height: '11rem', - width: '90rem', + width: '90vw', + maxWidth: '90rem', backgroundColor: '#282828', boxShadow: '0px 0px 10px #00000090', borderRadius: '10px', display: 'flex', + overflow: 'hidden', }}> Thumbnail { { { sx={{ backgroundColor: '#ff573a25', height: '40px', - width: '40px', - margin: '4rem', transition: '250ms', '&:hover' : { backgroundColor: '#ff573a', diff --git a/gui/react/src/components/MenuBar/MenuBar.tsx b/gui/react/src/components/MenuBar/MenuBar.tsx index b1125a4..9efae16 100644 --- a/gui/react/src/components/MenuBar/MenuBar.tsx +++ b/gui/react/src/components/MenuBar/MenuBar.tsx @@ -44,13 +44,15 @@ const MenuBar: React.FC = () => { if (!msg) return <>; - return + return + + { msg.openFolder('config'); @@ -108,7 +110,7 @@ const MenuBar: React.FC = () => { Version: {store.version} - + {transformService(store.service)} ; diff --git a/gui/react/src/components/StartQueue.tsx b/gui/react/src/components/StartQueue.tsx index ecab378..05b630c 100644 --- a/gui/react/src/components/StartQueue.tsx +++ b/gui/react/src/components/StartQueue.tsx @@ -29,6 +29,7 @@ const StartQueueButton: React.FC = () => { startIcon={start ? : } variant='contained' onClick={change} + sx={{ maxHeight: '2.3rem' }} > { start ? 'Stop Queue' : 'Start Queue' diff --git a/gui/react/src/index.tsx b/gui/react/src/index.tsx index c1e1261..7f4bf6d 100644 --- a/gui/react/src/index.tsx +++ b/gui/react/src/index.tsx @@ -4,13 +4,17 @@ import App from './App'; import ServiceProvider from './provider/ServiceProvider'; import Style from './Style'; import MessageChannel from './provider/MessageChannel'; -import { IconButton } from '@mui/material'; +import { Box, IconButton } from '@mui/material'; import { CloseOutlined } from '@mui/icons-material'; import { SnackbarProvider, SnackbarKey } from 'notistack'; import Store from './provider/Store'; import ErrorHandler from './provider/ErrorHandler'; import QueueProvider from './provider/QueueProvider'; +document.body.style.backgroundColor = "rgb(0, 30, 60)"; +document.body.style.display = 'flex'; +document.body.style.justifyContent = 'center'; + const notistackRef = React.createRef(); const onClickDismiss = (key: SnackbarKey | undefined) => () => { if (notistackRef.current) @@ -34,7 +38,9 @@ root.render( - + + + diff --git a/gui/react/src/provider/ServiceProvider.tsx b/gui/react/src/provider/ServiceProvider.tsx index ed862b1..c254353 100644 --- a/gui/react/src/provider/ServiceProvider.tsx +++ b/gui/react/src/provider/ServiceProvider.tsx @@ -18,7 +18,7 @@ const ServiceProvider: FCWithChildren = ({ children }) => { }; return service === undefined ? - + Please select your service From 275b559f06d251d9f9328ab70255f619d60c677e Mon Sep 17 00:00:00 2001 From: DAREKON <63023042+DAREK0N@users.noreply.github.com> Date: Fri, 16 Feb 2024 22:31:59 +0100 Subject: [PATCH 43/69] delete unessesary --- gui.diff | Bin 83226 -> 0 bytes gui/react/react.7z | Bin 134161 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 gui.diff delete mode 100644 gui/react/react.7z diff --git a/gui.diff b/gui.diff deleted file mode 100644 index 6ce327bbdd5c7bbccd860349b64a58f854972c4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83226 zcmeI5>2g&^mhaE!8`1A@Om($z7$gG%gG;7Nf!*Z@PYqO6JGwi%(1-C%bw+oGj~US*4FubA85||1kMK>YFyVCNI>^ zwaKl?RsFj%`DAiatuO0yYjQ)sZ>sNAmAEqbSbsP4ygH#as&G>9@nd8&RfnW!;9nRCqzf7KKmi+rlzitWY)5&N0_e+@JR8P~%Pm@pe z|GrA?3EIH_Qu3BH`LiGi91kYnsoaL1sIji^2VuonyS&abz5P0@=}stnq}~t1dhAU8 zZSo%{rGf5LzwVu20j;Jb4T9Zeyx&)<`%n6JU!yw;Z(W}olP8mJ^!Y^X9flSSDnL5+^o>ULB9vfV z?o57GjUUy*BeemY#`jZ=WE$oHmTZT0-V(;p?@?IaXR1-d8QwopiKm)BIAd+rOYRE> z8=+_L?V0M-%N=N>%nN*DJd{eY%J6ThQjXOR^>iFsF3UQ08DzG#Ht??<(ZN&I_)#O; zSKDPR?_p1K`&o4u4P0(mEra1B&2U$J+|egI^&s@(H1$lYK#PvwKsv8y@ML6Nbv6Xi zhoP-<4%5l29x92L%gRLxi&)Ew9ffs0I>FnLXRtEVruXJMc#qdF$LBEapTT9psm>km zX?)MMg1h>BB=|Xxh%*2~%ASFB6RT_m`uQH~)ITd|2paoV;{tzbW2o_M_&E5_xD79x zP^ARe6|t&WW+>NpEcs|=7)KueSR;nJ>2Yf0Q-ZaLqIP6sJA8mM$WEj zwRr30BY&}8SnGYsBd_7;KTvBrdi`zc`e&!Mfm zVK(Ud`+9#_y499?e5O$YmzVnGdzCP(f2saxbsXIssVyi5-QuO%f|HoVhON~`FFlk0&tu*tTAy>?Ac8*2Mnu-V4bN?-4NRMJ;J(#JK?#TAX;XoPQ9HFl#D*4{WR zmRbrwWIS9K{Tyh1DJn9qcyaP!c)G3s-ExbVkhTYg9qF%TRYwl%Jv<8>Jz~oX^?Nbs z8?lYM0aN#dNn660yzGas`97mDYYxgd4!E|j(XLJYpxJ^GNBaLWjs6!sgKI~^TcrKV zpxZ2IjnQ{roLDRV?Vd`~A`mfc8z?OW81AZ8usc6#RV5?y~ueJto zTY80uFfyo(8Q=rp`)c^szfwEu*DNul0rjoDYkH0&PPut=G+<9G!Ig=*I@A0BYP4_27Fs4)TLl2RE9dS<(8s1;3-2 z!gTSiui)*+`UX8ac9k_;m#mkLxM=M+ zF<=N!C^4qlsp!NS_wGWnm9C2 z$L@B@!wUtZ?gpC1&qB$Re|K@{-;3)X2qRekwC#(+1|OMmge6G?I36AaewpftnW4`j zS5m$SR=f;)QMx)Mm+%#ZV=od$K*F^2J9=6CbC+8Mwq=Qy(b0VLH1S z46Fo%a!oa@kx`)snQWPlQ)UX6NYhVqMN9PXnfL{m%~T#=|j=vL8!R3`FkV z5#QOA&2W(5e>;DE@S20gh(JmSwVEX*AUo@kotjPQ+A=-A3_M`CebB6fB+RXYZD}a0 z;>sTjJBjA3YHX{*RlM*-pT=6!?c+5mjCIg&+0;iKEdz8UMrM{lYCq?80d^UhmwCX|k0b(dxE> zJZ(Y;2^nEwvXOO*I>4GnjAV|o9hpr_FT$Kmi~LOOywI$cPPl(bbv4Ze9~M!L+(N-S zM)YJ;ZJ`%8`Kn>)Z%^Krnr!~|Z_+$GmdQ$74*I%j>_hZH;t%Uu9jgJAFs2*&{P3iI zG(6Me?Hy8kTXUi}@D;tPuKm?kyoCKq1^Omm4Z`r6_Vst)9;0wiBR$Nw?FsxJzFwyNg>da>PUZDU0kg za@tF;{V(>!8{la1W>()~<@h6P?ZGbMpYWbNJuRumo^)Y+#_r!c66L`&@U5BU0Ix8d zysT1&SFvT|7tmTNZTXId4oAk)^6wszgfq2|rLF&u7T7-{dMLRKoUk*o@gM8^EaNI6 zeqXRN4^$3qu~qA&bdU* zGnLgTbzo7Om5VJ6%-#M-tw?0BX$PsbZT@vHe^Jy8&G9QbLS6pv1({v94Quj$J>Z^s z0f6L^yuH{<*aPNe-_-N-pcTP4Odl{=dsT0u$0FKAYZ-rdGZ2_6N&wx%=}aSgsu{Tt z*C-DUO4NIhJhj;v7SyrBHLSG|r9hiSe{?vz#u0HNmIp^tj#+whmc)(m4id`->S)+_ zobhdYT%9NnT10H;vR2A{iWgKT>eRV?2Ti3OH&zz^`LZB}WPnTKcd^wbs|MZkf!_R0 zB@IumsPt8pGJmDR4s3Op9tFQmKGSx;L|%>z7se3%TO~vsx(?I$3I64|5lvppjJ{Os zWG@|Q%}P&KAMMq9FRjjWOmOQcHA@ODJy*5)8z+o50NQz<$F7_QnQUa#MJpqv+f>e< z-3TI-cyjMRVLw7hsHz*N1uiGW&Ei^u>*<)aO^F z&yxXgQ;>xxEQ=>y(ZB1$GIaXcy%}!??#%BrwExAfK-8X6pA64e1aaQh?1iW&xP=xZfVng9P-(@S zefgTkd35;%Rsto}TJ=#_u3pSHYGyN~SOFeK9o&4uo(Y1yE_E@>pG++w;>1t%&$K!? zoAW>RLqzNG6G@^=@+^_zWhG2!GV8IA1J^9ceqA!o%zV=3M{drFRc>a&xV~~E>^&~9 zD!2IDcSw!glW6t@H2NlUSE7|u&zso`cEi=cJIiP|6xf|p)1Qry=bON@v6hGArcl;% zcK#w<^_-9;SyT2cMh{IpU=7{`ycy|fLITXnbCH;7Z9V%|EW*h#+#!5VYXa=Lv!ZJG zBg5CT*}GY^?C9D*v0txCj$6i7vvds#K93qSnk{(gXpfuKOr{dEZ_DxVdd-N-RcUrZ zYBpzzPpy5f-sAWc^_u+{IW|+sTb?arhzJp$i}qESe|^V8>&@bS2`itn0Cv0P|Y2P+EQ$aPuvOyQ@pczPQ9 z&PNyX(eQ8LktDLsjLUNNog4=|rN*JU$Le%dZ;5Nbbsq?} ztgP4dnmKgp*y2!U4C($=bd5Mi)_V*l{j^X#O0{R!_E2Lny!=d~jK5%okbT{?TD~GV zZ1{#x4y_p-5a0H(d{n|UI1G)EMz!0|$?ulEkV4Th>20E4+uxV3*CMEu3^3CJnKj;>El~@!3PBg|k!f)*Muv&k;OvU8R%;&61R!1)J(EvHFXV1}2-j zxQokz=19iKf;?`W`vy}y>X!_&;HhJ2-g?d6h-=!u3nkU~+}5evcdRG^N@?=dbQWyyS`K*)8^>dvNuR}F7vsaS-gEln2xz^<{kf99!Uq*emU44vfQ_dOM56ptc_7 z$E80>3LppY#M&Or_PskL<~$Szl9OWbHRJ&~$>hFZ0X404lUWag<(g;t)$o?m{M{Az z<7^enoZSr?B+|;BIIZKU{?H$hd2WACb;1)=LZ;1D>sZJEZu1`Gvo$?9{|zZYMC*B_ zecNjaREeQsX<;+sU3jX`+I)Y_(mX%2XB_p#x{IW^wAERutc+cuo06wR9GBCPEW%}H z2Q3fgWF^OGgwH;+Oy|hlJuJN_$s`Xm)`%9sCvlaXIC%1WvQ!TpRZ%U8DeW&g> z<&!=3OBLGh@^GHM54E(1a=&GB-MW3KImdf9@F!jWE8@3AjGFWAUM0^5jlS(>3nriJ z9I3;gksXFzCux@EZz-8^k>jk3u2_gev>B1RCU5kxy0dXW zdS24GfS(30v+;Jf^xAYHA8iWzyIO}FkK*q7?H*{oYK^0H;XI>QtK?&)euZ`#pf;x$ zfhXh(beD8FL233RwwGqa?Qd<3-d(?6oeuy@$G+14*It8Kv-knKTYMA#!1fVEzhJ!# z*n*b{tp(kiNROYFfc{;|#x&z;>9xnX-nQ+KFgxD%h+=b)JBfB5cO}q|(LMH%or2ll zc6)9bv9C1a4cjXJg*oSx@P$g?V~?XMOMIy|IicxD`*6sP1ZKo2^LD!4p@%+G8=R|_ zw$St>;@Lvi&7pr3)~fWiVI!I660RAgFStF%qj_7rtZv(VeKMLg$-2X^=HIj}Lj9`gi7mh{vVh9UrenWsni$EJCZdNNdnrT%09%NA$+-_}cj(Isf@J z`vum;VQfeEI1I3Qi^KqLanTrn#?2guVL~?_8EUh*ZgFngBS+&2R@n4t*|XU8b!n6G z>v?mdzLqp+udkoPyy)zx`u+lRZ=_WImwI}al!3#Urp&Ga&?eFA7%lxcw zYZD!T7CaY(><>;iw9`QGzp(T8-OU7X>n4XT-%R49zMCc_>#^r%InL~>?Hy^Br00BX zADqJ4TAR(ay&5uouF8HS(}&zyEJ-qgIN9>u&Gm^^OLLv;7~m_LXGYX>Um`7y%{$}7 z7_@%hn~WV{+B;{(!fWcei=}>&d!%y=>hpQj`DU9Yox%?f$)5g*n(t`j5A>bU|G&w9 zP5!&XXFwb68a_0U&(Hhp@+9A{K{XA*`39fy40GQ{a$7bmyXCn74v#jRh`s6T@rY%* z$4s4+Z?uiVxLhqe!^enDd={Bic1GNxX36^7iI?C3nfbQY)AFf|J4e0*1lPsAxozXV z;B-e8#25PaP`>dW0_=Eew#2b!65DouP3EF|-+xevXDUOagma~VKeJ~X)`n$zS)W&u z_M79fv!jqso*~(_W~@X#M$oKjZ~BT zb%6^)=}W;{{#aHKzL&1Js>f<=XW_MhG4Z|E-ZJuPZVaq7YdoBtOu2_Hje!r@FBBwT@3O7zSvfhj z1HTe?-xd#&^Rf8pD^_Y@u|)bbOP)hoKRz!mzoGP|!H>H&Bh!N7So|q!Ke_+U_*Rq; zO{*G;gy!8!1OMZ$Sx$OFvU8FRzA4H%h2=e!wxVg-=aFNwxX0}VGC#iQ&$H|TYUU7I zizXZ5J|Cm>_iGj3=jgBFMOXCq{aVd@U)=btj=Vk`&g?`!@iSn%q7#p&(X!=PWBkK+ z?7e?0YZlk9dDq?hwWjqQXXB~1-p(Sax2=cysThA(b8lSp+&nuo`47RO$KSDkqIXA( zC@s14P1BN_$~yA%%o51zAa!{meOhc&BnZBk%p}Je}NpWoH|hHw4dz!w*lReIwck z1zB#i$M0hnb#7t1y=I@l{;a);;JMu`9B&9-lK<3h=|vi|8wXzt-d(b^mgQe1Z(=3r zN_a_k^=x|*@g!ajvrq4+C)fS5dU>I8++2O2|EYmrpVcLY+91YhwssfupX3!bkMJGA zWw?Z$6R=CFmj^+5m`q*8`XlLx2#cs8{dw${lcKFWIVYoFhJ#&h4 z_Wv`C7oG5lzLmZZUp?FN@;3Nn-z7gO&@*mh7+8a@QN&pPr>~2(Z_=@Bz2*gc7yX}j zhwWYt^W1lNKAXH}t%KL6QS032irzqP?lnYb7{0f{CC04M{cVwaKr`}f$Fkq)H+z$x z){A$N*aP>VT|VKb#Dn0ic3%@*$WCKl5nr^s%8k>O_kMk&a?s3|DrJ5+VvXf*@M+Ft zz_)(-y)b9?eBvDh7tk%7PlG*n=TP%j0OmnG?w+}-k*;ghS2UMRar7V7A+ITq!GR-qdZ)^Ef@)hqYvV--9 zlKRR&QfZT#ehZ@60_&j-Mv1gVuQR@IO`q*I`>-=6y#vhrAL%KIF;ZxwG`k`G>N58S zWeKcm?!OJO|E^ZeYyIz<>$e)^R1n=#b{E}iR(K2SVe zTC!f4V@emS>8fC{u6P@}kGVPYs<*_Dy< z%G(b(lhiF`ET#HM|7fK~U3P0vJo`SSlJxGeyrsy+y=Hl*#*)^ri(h-aa{TcY+`LCx zi|^9s*b*3(-`3ic7fAK{!ZedX-8ZTk$6>C0XU_vqkJWyy@_s)jSX;&yhqRa0O7*-5 zff$jhNoRwE%jVOT%5hZbx-1K7i{>WXJ(V2gx>ki-6E^gZ2;;K;zp88u?#;d#BpDHR z?j`2NVrnm*qx6q>@m!_9&y=&UD~R)AF9{}*uaxv0`3llI_2KdTt~fcm96AG(VE0Cl zAz`OwQRbe?c)rnL&;iUUhZgHKh!eQ3h5w6IYMukACFd5O2TPs%LEAW8dDn=|4~>l7 z95zRC&+IA25BWaZY`+qGm_-S@*`SbGi;ILy$&Vwi%puIm8V`;9HImIhSs z%C~~%h5oY}PJkf_WxLgzYY4siJwW#SRCqG~y*P~PCt)gijEspjnQs)4-VE;}mOE@p zY`YgAU^ax~MGoJH!Fb~sW9^1p{76nA_zz8^b=xdo54)eIjFTfF$#YT4%l*-H7W(UK#C$H;Go0}?Wqy4*;P7i_ANxBssH zBR(t(GttR+^!~Blke`XALzHCg#5Wct@-N~S-qJtmG@Do^MlL8_^TRZ=&3vC`JvIS1 z#SdwjWd7};Mw|Ks!Dr6t-wPOHJ`CFZtwwPrtghGh$Vna>@o%=9Jl9|HIoZ6xF?N-i zEl<{mLj(&izZu{wjS1_DI|1O!;2fMg-Mhl6Ae>i=Wk=pk^j7i4^rSXY91E23k`0Jv|;>LXv?`eYd>i@u^g&YeMqu#??QLabs~zbi6Vw z9fFsJk4OnlGrxSo=itR~WY5m3#hMsl`d8l_+01vq=YKu440pjte|U10_%tE8xcg=EwdAsH8Ny2Qq?3uy=8vwwy2t1*;{&B;5|%ZWz-Sg?X#lX zFnE^`Zf~<2!Vb^;bC~5t7LUcwYw?1btxrT5Z^EH;AZl976Td=~rMdUXG28SR_drlhFFzb-z@Kgy z%xYab754Fpv*UjAN}9fzFyk_w0cY3=lrxMM_Ian8R6?)ae6d+)2<_$ms#+UksI%## z@LjT9`r$b(ITgjvaeU5sT|15UVfEr9V_lPHW>I3cm!Vt}i{eQIU3f&@l9%67o(8v2 zTB z9v^9wXJu^Uf+C7!^uAQwk%;0@>#EGcFUQ)}|IqereocPw{S2j5%C}40^wg8CRY`3P zA3^;ym9Lh|5i^9y<9;UUFpU(uq+9nTK1>9E_v9{8)11jT;~cNa^Gsj@_q0=i3- zZ}T47-}1(2I@TyX4d%;_?|Cwe#zTTFH&uQJ86qFNw$S_+&nsziEaz#d{B6qEcQ0=Q z=%i5*EMy`gImF&{o`{XIsYCbs4#)c{$L5Nh-Plt8-o#MG;eT~(tyu_fL(Ygn@J(k) zFp4JLm;SbHxZ6y+cGV84|29CPmNT-}n{V6n{apzDzPkUfJ2%X6*+1d^HOLIf3o0c_ z_bRk$g{4+X#?CBRCEdN(@vz&^-reG?kQ)xQ@pdo1o;p^{;uUtSpdAj*5&LZtqv0B+ z)v$XWyQF=zj`QTZq8UbywCmNDM~asDukx7vtNu*qq(-iVejL7=p4i>vqVOcw1|9OL z=2W+0YVyhbyzRDl>8t3Ea(kQi#G8g>kJd*!kFU4Ok2D-!U`gt0d3!RdhFPrT%AIFa zb$Q2TqYiN7Jfq6_Y5at556Pl)U*J^dfOu{PpZ!^M^mP^)Rg1FJ&ax@=jTe3*@EEUy3I5g3m$LtRMwF5haoHs|?MUHfI~IOf>ikFn?ZQw}RWyJ#aV z*YqWH+isiKGwDv(Qw_mYn1)Iavuku?UHSnAC@-y=*asU7Lgbeljwr+o6^*~@_D}f5_D)3_k#fw4@7*4u z#qyn$uhivwA12jnl^&b^7(`RQyrS$>X4`d6#jhbEekJT+=z|3Kl-juX?8Ea#pX)rF()^`d#dbvvJ^f{N{HEXeJiA>PDYV{4K{oeU;N>+-8A#M(2>12+ ze5SC!FRNik_K%&bxFlX~nor+My}h)p+ygqcrTNy?$FmsO?XCIe>0*ud!Wpu}TbKhU zR2p484mK*;q5D1it-T#Su?t`5f4|KsZ{a74fP?W zBW(Bm42x={ZSlOw6`{T7qBOJF*_!t?Nt?J8N=T66-)XvoD{Mo? z6XzTs`0S>rcK@@t@wVYqzdi3GPe0!koMOyx1Bb6;U$<8&g*VUhU>oUb^=pSj{quD6>qY8Wi`KDDU-us>H&~Ikjr*SK)nhCuR&dN}Q~qAi2QNw!`0(U3 zsG1LGv07u7&i(Fb2c8}pV|Dx5g99sPQ z_@C-}-%sLVSfFV?_2+J#uU^N0Ep@)W7;(){(9Hfg&3)=M&fM(*>6=p*p10Q>4!Jgc zq8YO$Hf3i{5f>kt*)_yZbDH)r8q>Nw1TojG9ctj+SPE!wtsC>Z>ZLca;e*`QNfUT$ zH+1hD@wSVNETV3iJi9eCJU^BFYW_Fef0`1noN6xG|?#X8EP?p_ocQN=giDOlPf?H|KT zElxt8K#*UFqnNjp_3&FyiEjBRyy-r0q6VC&(q-f)BTz4C61&d=u`+9v|JQ>iy@Rr52dKz$tTjRV#=2p&D7vFM!#k9Uf{lDRTIgs z@xi{_>wFC92u)Jtt7yN)nUP^_9Ln0EKH1h_yz424JUtM7 zpqp@4r=78yNAfrYl~pBAV=4;<>3$Tr_;YCpmaUJ*1J=Zr!LgbZ+{Ad}H7WkRJv#tW zA8E-A%DcB~v-k;`3$k7(wkf3PRz2;8mwycIV|`5zU0Gmt)*!N*TN~{ z?RVg|!-7>`6CUMvk=Nki@xy)yL|K``;B@KBwknDj|YOp z`qzxv81{iry!}ABAHMVQ1`D`(x+DB{@Y&ox@tT_l&2;s$WK;g7dZlTKsh^-vuWssC zv-FssYTMT*8d9o1F{g6>A)`QVPREy0c7HT&D~YFZk2q&>W1GjQ+eLk2EK6D=)`J)W zyZYHVfxpS@>)0z#Somp4usD`5-ctJA9s5|9o3HUN{kC`vjANQk{;V+{YF1JI{zc!2 zK>_9Ell>|FzLo>PyqqS!Qcc#Tgiczf>6xhcFPc{E1^Gg*S7Iox#oL{i+<^mkefhd% z&WAtK@9^}W!~5v3;`j6fG-7qp=5mYWpVhMO)45W$K-xT4WpsU(*`yCOqw%hJj*H>!t4uE2Z@HArF+SeA`z%Nu zV$~iWwtEi1f#{2K|5(X2>8Xv^q*06ufg`gou*M%tu0#~$T9B-f_l@Bnd%_9ed|m&H zV|*XvD!W?nX9HJaW%li*p15fZzLs)@ZXOVA7>7;H7yGlfn|v?wQkP)vt!DX z@zPO6O%MFE`|8Q>vDn0pAb|#3QXpCgSvkZqoG+k@bZ6=S*W*HZv3ronE7>Kcvoa3a zJ=FgmE63ZsHn|hVVecsAx1hlf&}i8;{HY`}7A~dGhBw4fynS>o&&}cfM6!SKvMZ{w zCv9a#bsnAQ2F!_bnJ8~waQxBe)?c2CJ8jpbeuQd`BN|oQQ=3t01G_{0Vs>~OCu?E5 zuWdH)09M3xJ?8{5LgK3%Kf%5@qBpnB8=4_24S#)kYO9_1f+kF?GOqWAycf>}XY>L% zzF{SvkNVp4CdY=$l0H|}3jZUou()!}xSP$?$8e4t;9i{v*l7ZuA!oOP&d&#)Yo(uS zjndX5CgiJHr|~}uE6ftx3LcJ>l2UEosV&v|bFN#yq1Ev|yPCEd?ckxlxgo8TT0XM= zx-@wH=aeSkcvBRBZg@+di`DYWIWd}fH2PY5QG358>3TymKzHZ)rk?FBxuE2AB8|{7 zU+Nn=VtI!2clzCB3po<_)_zH{AmH1(L6Q?$gD2sQ+gD!^OV$IIMzd-!!+8uQHB9%b zl{Ee9v1kn`4tM%}=oxxK#_&s;p@-bxQ{R2#qi@dU0do&3bFrDY_l0i`;niRfnb#6L zN+E2T1Y?_qv0?Z8sD5^(DVP+uvncpx{+{@I6VA|Kgf$e--X6SU5zdr}ux>BYoNrk+ zT0rHx#FF$IHRASi>54GId{y%HjN81*X2mR{R{$((+HNTn^1Pm}1#{NGu!GwFHu;Z} z_WL=N_ss~@Lib+-eJ z4~ZJgJE9ubnzv1-t6K?O#b{>gK2hDRclqT}S2%$&( zlJF|e{iikjsI>b?d}M14bXm~Ai@LA2;d`4p>*8rB$$lgA z7{)Q-!TcrQ*s@N&k4Mtr=e|Q@*7J zv50gwv2^;zOa58y1C;}<{l4Z}_CTFx85zq zC&BoUFn|?BYdO&WB{oC%DLzlbSduzywG*9E~vNUZMh%p$M4TXbo~{cj$-j?YbL^<~gpkUh~4l5@AymRXK( zxrXJx@@b<({z2!Dv{LbxRLe^1#IiZ%eI<&pI!%6-m-N2))uNs7)NchZeA-}tsYQDK zW7`2Uy{>)67=r(9-1B#ZW{o_GnE_ZFNMJ%6J9u-Jy$xwDO97TT-N$Z3u!R2s35-|(^_--uWPCKdc%Q`fvF@XE97a=>bf-2v(h*76JTCx1uVsb-mvBz*(NG)|ann&&H>S{o@2ysIm1T74-S z-8bg^BI>iq3+(UQw!SEx8{CRE8PPKAI(Xw_*$=?S@e;gi`h4mm$$LI-=;Y=Hx~SgZ KeT>dA_kRH-v>%xO diff --git a/gui/react/react.7z b/gui/react/react.7z deleted file mode 100644 index 2bfcd22c0f176e8888d49ce03c797be019501e21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134161 zcmV(uKvN(#){|c zz3LPHo^`-l*Pq-d-8p`#d<7RucaFDw@EJ*Bfi3RQ7Y3C4VlwzfZ4jP8Ira1x$w6aV zwt1r8uY~MLyrH6!#7t`{*?rLRoZ2Lt70m3#4eVwxO;bywO&N)Gu4UVWQpaL%`o@Rh zpjXoJdU~3enh}yp=VW$nCP+lEbw@$!DQQmp+sIy{9hu$lc(2#3{e(*;t}MNW`p3=R z47>Y!=b{pZ4Nb?vTmmeWOwLQ9BXVzQpm1CHNVP~yE9ljtn3rTgIwqUi&IU69M7|S@J)XIe@6e`&6X{m>jp3!E)CAylStKUd_I_Y`_BG1^zf^(i0hr+iQ?46=uF=p?< zz<8MU2HY%LE3&biCHwexp|&2`=qgF9421R2;R-GtKThf>2)Wj47&1M zDZU&5g800%w!GOyIur0ObBiP2Jx-FCf-PPOVpa0|wWJgU@m0(H9pQuC#gLvT@ z!I=)OnMIAEWv^nO34MaQ6f1@}(w`p`nQGV0Z^01GlI9sF|MAeHm(iaHeoH4Z6EV_K z?IerPH{x=I!;r<5?kal2>T!YxU0EOrW0$_W}hF=9&6mz`CoSu}wjNfqFs zeqy7N5_vJY_=~{&-O-gI>^@|={+p4V%VEI2h{FrC5iXAu4@ZS`gOrcTmhV0pt7a~Y zTs=e5ED#9&#>Hs$<~|9ekQO9;o}0PQej*9Bd6GxrjO!=2?i3Ta9^m}u4Y=l^dEmFn2c=DXD#{_oBb z(+?Ir)^8`~f-$?lFvN5xpw=-VPdYq-ZvMDJA8hh~G10I1auS<$mt2D{r|Gl`iF`KV za#vM;Qj)u({EYbggOnF(bj_o9)*JEhr60rR(8RK+2|;_tns2{X^!Lrh7*z!{B>M#U z%E$Z;t+>8*f##74#cB>y|4m9A>1-d*<>3ST{g z>B8O&>OXeTtfZv4^Id{S74%sY*(x)G-Qwo!lhI+opX-!0nOC=Fo{cGe7AQe#aGgj~ z%kZXBdxpc)19svPe319wHqiKEOFr@^3WP#HWngz-4mxoOB7v09kwI%5+2zQIGTEy} z6S$}o1v3px`kD(ANcv3gDLx9!gsik^g+{_F^STJDTEGCMvB7+Sr(Zy zS_&ht$hLf+F`-5gaH}tY_>Q#mwvjaFe1Dc8NEY>n#0~icN}gZ6BaVFyn>C(Y6ga{H zscCo4pOH&hKC3K!@Q0$Uz)*}FPnMg(u3%U

87@6~Dg9Z(?sxXWI|* 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

Ve%Q{W1b>_OC;g&9CD*tSetIT^3>FeP9YI_!VSU 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 From fcba060c9057055aec7b93a50e13467020c02d3d Mon Sep 17 00:00:00 2001 From: DAREKON <63023042+DAREK0N@users.noreply.github.com> Date: Tue, 20 Feb 2024 21:17:56 +0100 Subject: [PATCH 44/69] [GUI] Fixed Hardsubs --- gui/server/services/crunchyroll.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/server/services/crunchyroll.ts b/gui/server/services/crunchyroll.ts index 2cb635d..89ad89e 100644 --- a/gui/server/services/crunchyroll.ts +++ b/gui/server/services/crunchyroll.ts @@ -99,7 +99,7 @@ class CrunchyHandler extends Base implements MessageHandler { if (res.isOk) { for (const select of res.value) { if (!(await this.crunchy.downloadEpisode(select, {..._default, skipsubs: false, callbackMaker: this.makeProgressHandler.bind(this), q: data.q, fileName: data.fileName, dlsubs: data.dlsubs, dlVideoOnce: data.dlVideoOnce, force: 'y', - novids: data.novids, hslang: data.hslang || '' }))) { + novids: data.novids, hslang: data.hslang || 'none' }))) { const er = new Error(`Unable to download episode ${data.e} from ${data.id}`); er.name = 'Download error'; this.alertError(er); From 6575e2343d35d2f7272fa18820512dd124ea9d5b Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Fri, 8 Mar 2024 08:56:27 -0800 Subject: [PATCH 45/69] Fix subtitle downloading for hidive --- hidive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hidive.ts b/hidive.ts index 93b6c70..1681d11 100644 --- a/hidive.ts +++ b/hidive.ts @@ -316,7 +316,7 @@ export default class Hidive implements ServiceClass { public async genSubsUrl(type: string, file: string) { return [ - `${domain.hd_www}/caption/${type}/`, + `${domain.hd_api}/caption/${type}/`, ( type == 'css' ? '?id=' : '' ), `${file}.${type}` ].join(''); From 4742ea8d555bda1e8b516186257ae30dbaae2192 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Thu, 14 Mar 2024 19:14:50 -0700 Subject: [PATCH 46/69] [CR] Hotfix: Fixes non DRM downloads --- crunchy.ts | 6 +++--- modules/module.args.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index 900c4a0..827d6b1 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -57,7 +57,9 @@ export default class Crunchy implements ServiceClass { private token: Record; private req: reqModule.Req; private cmsToken: { - cms?: Record + cms?: Record, + cms_beta?: Record, + cms_web?: Record } = {}; constructor(private debug = false) { @@ -1297,8 +1299,6 @@ export default class Crunchy implements ServiceClass { '?', new URLSearchParams({ 'preferred_audio_language': 'ja-JP', - streams: 'all', - textType: 'all', 'Policy': this.cmsToken.cms.policy, 'Signature': this.cmsToken.cms.signature, 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, diff --git a/modules/module.args.ts b/modules/module.args.ts index ad988e5..ffedb2d 100644 --- a/modules/module.args.ts +++ b/modules/module.args.ts @@ -276,7 +276,7 @@ const args: TAppArg[] = [ describe: 'Select specific stream', choices: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], default: { - default: 2 + default: 4 }, docDescribe: true, service: ['crunchy'], From 172e6ac6ac636b32bb65ecf6d884174cb481d0f5 Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Thu, 14 Mar 2024 20:30:37 -0700 Subject: [PATCH 47/69] [CR] Get all crunchy streams for android (fallback) --- crunchy.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index 827d6b1..b848bff 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1315,8 +1315,6 @@ export default class Crunchy implements ServiceClass { '?', new URLSearchParams({ 'preferred_audio_language': 'ja-JP', - streams: 'all', - textType: 'all', 'Policy': this.cmsToken.cms.policy, 'Signature': this.cmsToken.cms.signature, 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, From f06dc21d56db04577499b87aa6b777dadff39f3e Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Fri, 15 Mar 2024 10:08:25 -0700 Subject: [PATCH 48/69] Fix docker builds --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 036b6c3..9d5ee24 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ RUN pnpm run build-linux-gui FROM node WORKDIR "/app" -COPY --from=builder /app/lib/_builds/multi-downloader-nx-linux64-gui ./ +COPY --from=builder /app/lib/_builds/multi-downloader-nx-linux-x64-gui ./ # Install mkvmerge and ffmpeg From 3c41414f4a82f13babe4eedf5d4f4b4aae4a83ee Mon Sep 17 00:00:00 2001 From: AnimeDL Date: Sat, 16 Mar 2024 11:14:21 -0700 Subject: [PATCH 49/69] Rename WV module --- crunchy.ts | 2 +- modules/cr_widevine.ts | 47 -------------------------- modules/widevine.ts | 77 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 48 deletions(-) delete mode 100644 modules/cr_widevine.ts create mode 100644 modules/widevine.ts diff --git a/crunchy.ts b/crunchy.ts index b848bff..d6ea96f 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -18,7 +18,7 @@ import * as langsData from './modules/module.langsData'; import * as yamlCfg from './modules/module.cfg-loader'; import * as yargs from './modules/module.app-args'; import Merger, { Font, MergerInput, SubtitleInput } from './modules/module.merger'; -import getKeys, { canDecrypt } from './modules/cr_widevine'; +import getKeys, { canDecrypt } from './modules/widevine'; //import vttConvert from './modules/module.vttconvert'; // args diff --git a/modules/cr_widevine.ts b/modules/cr_widevine.ts deleted file mode 100644 index 5f54f01..0000000 --- a/modules/cr_widevine.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { KeyContainer, Session } from './license'; -import fs from 'fs'; -import { console } from './log'; -import got from 'got'; -import { workingDir } from './module.cfg-loader'; -import path from 'path'; - -//read cdm files located in the same directory -let privateKey: Buffer, identifierBlob: Buffer; -export let canDecrypt: boolean; -try { - privateKey = fs.readFileSync(path.join(workingDir, 'widevine', 'device_private_key')); - identifierBlob = fs.readFileSync(path.join(workingDir, 'widevine', 'device_client_id_blob')); - canDecrypt = true; -} catch (e) { - canDecrypt = false; -} - -export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record): Promise { - if (!pssh || !canDecrypt) return []; - //pssh found in the mpd manifest - const psshBuffer = Buffer.from( - pssh, - 'base64' - ); - - //Create a new widevine session - const session = new Session({ privateKey, identifierBlob }, psshBuffer); - - //Generate license - const response = await got(licenseServer, { - method: 'POST', - body: session.createLicenseRequest(), - headers: authData, - responseType: 'text' - }); - - if (response.statusCode === 200) { - //Parse License and return keys - const json = JSON.parse(response.body); - const keys = session.parseLicense(Buffer.from(json['license'], 'base64')); - return keys; - } else { - console.info('License request failed:', response.statusMessage); - return []; - } -} diff --git a/modules/widevine.ts b/modules/widevine.ts new file mode 100644 index 0000000..3d7c88d --- /dev/null +++ b/modules/widevine.ts @@ -0,0 +1,77 @@ +import { KeyContainer, Session } from './license'; +import fs from 'fs'; +import { console } from './log'; +import got from 'got'; +import { workingDir } from './module.cfg-loader'; +import path from 'path'; +import { ReadError, Response } from 'got'; + +//read cdm files located in the same directory +let privateKey: Buffer, identifierBlob: Buffer; +export let canDecrypt: boolean; +try { + privateKey = fs.readFileSync(path.join(workingDir, 'widevine', 'device_private_key')); + identifierBlob = fs.readFileSync(path.join(workingDir, 'widevine', 'device_client_id_blob')); + canDecrypt = true; +} catch (e) { + canDecrypt = false; +} + +export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record): Promise { + if (!pssh || !canDecrypt) return []; + //pssh found in the mpd manifest + const psshBuffer = Buffer.from( + pssh, + 'base64' + ); + + //Create a new widevine session + const session = new Session({ privateKey, identifierBlob }, psshBuffer); + + //Generate license + let response; + try { + response = await got(licenseServer, { + method: 'POST', + body: session.createLicenseRequest(), + headers: authData, + responseType: 'text' + }); + } catch(_error){ + const error = _error as { + name: string + } & ReadError & { + res: Response + }; + if(error.response && error.response.statusCode && error.response.statusMessage){ + console.error(`${error.name} ${error.response.statusCode}: ${error.response.statusMessage}`); + } else{ + console.error(`${error.name}: ${error.code || error.message}`); + } + if(error.response && !error.res){ + error.res = error.response; + const docTitle = (error.res.body as string).match(/(.*)<\/title>/); + if(error.res.body && docTitle){ + console.error(docTitle[1]); + } + } + if(error.res && error.res.body && error.response.statusCode + && error.response.statusCode != 404 && error.response.statusCode != 403){ + console.error('Body:', error.res.body); + } + return []; + } + + if (response.statusCode === 200) { + //Parse License and return keys + try { + const json = JSON.parse(response.body); + return session.parseLicense(Buffer.from(json['license'], 'base64')); + } catch { + return session.parseLicense(response.rawBody); + } + } else { + console.info('License request failed:', response.statusMessage, response.body); + return []; + } +} From 19521be3ffa4ef801a917fcd571db4e58eac632b Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 12:21:33 -0700 Subject: [PATCH 50/69] Add missing proto compilation script --- package.json | 2 ++ pnpm-lock.yaml | 61 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 6b93a8e..e5c1b99 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "protoc": "^1.1.3", "removeNPMAbsolutePaths": "^3.0.1", "ts-node": "^10.9.1", + "ts-proto": "^1.169.1", "typescript": "5.1.6" }, "scripts": { @@ -96,6 +97,7 @@ "start": "pnpm prestart && cd lib && node gui.js", "docs": "ts-node modules/build-docs.ts", "tsc": "ts-node tsc.ts", + "proto:compile": "protoc --plugin=protoc-gen-ts_proto=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_opt=\"esModuleInterop=true\" --ts_proto_opt=\"forceLong=long\" --ts_proto_opt=\"env=node\" --ts_proto_out=. modules/*.proto", "prebuild-cli": "pnpm run tsc false false", "build-windows-cli": "pnpm run prebuild-cli && cd lib && node modules/build windows-x64", "build-linux-cli": "pnpm run prebuild-cli && cd lib && node modules/build linuxstatic-x64", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35a4081..4de008b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -139,6 +139,9 @@ devDependencies: ts-node: specifier: ^10.9.1 version: 10.9.1(@types/node@18.15.11)(typescript@5.1.6) + ts-proto: + specifier: ^1.169.1 + version: 1.169.1 typescript: specifier: 5.1.6 version: 5.1.6 @@ -1763,46 +1766,36 @@ packages: /@protobufjs/aspromise@1.1.2: resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - dev: false /@protobufjs/base64@1.1.2: resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - dev: false /@protobufjs/codegen@2.0.4: resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - dev: false /@protobufjs/eventemitter@1.1.0: resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - dev: false /@protobufjs/fetch@1.1.0: resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 - dev: false /@protobufjs/float@1.0.2: resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - dev: false /@protobufjs/inquire@1.1.0: resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - dev: false /@protobufjs/path@1.1.2: resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - dev: false /@protobufjs/pool@1.1.0: resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - dev: false /@protobufjs/utf8@1.1.0: resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - dev: false /@rushstack/eslint-patch@1.2.0: resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} @@ -2692,6 +2685,11 @@ packages: /caniuse-lite@1.0.30001516: resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==} + /case-anything@2.1.13: + resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==} + engines: {node: '>=12.13'} + dev: true + /chainsaw@0.1.0: resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} dependencies: @@ -3017,6 +3015,12 @@ packages: engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dev: false + /detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} @@ -3081,6 +3085,12 @@ packages: engines: {node: '>=12'} dev: false + /dprint-node@1.0.8: + resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==} + dependencies: + detect-libc: 1.0.3 + dev: true + /duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} dependencies: @@ -3671,7 +3681,7 @@ packages: engines: {node: ^12.20 || >= 14.13} dependencies: node-domexception: 1.0.0 - web-streams-polyfill: 3.2.1 + web-streams-polyfill: 3.3.3 dev: true /ffprobe@1.1.2: @@ -4385,7 +4395,6 @@ packages: /long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} - dev: false /lookpath@1.2.2: resolution: {integrity: sha512-k2Gmn8iV6qdME3ztZC2spubmQISimFOPLuQKiPaLcVdRz0IpdxrNClVepMlyTJlhodm/zG/VfbkWERm3kUIh+Q==} @@ -4850,7 +4859,6 @@ packages: '@protobufjs/utf8': 1.1.0 '@types/node': 18.15.11 long: 5.2.3 - dev: false /protoc@1.1.3: resolution: {integrity: sha512-Vy4OBxCcF0W38YrZZRFix659gFu8ujIxVDP1SUBK9ELzyeMSBe8m8tYyYlX1PI5j9gse9hWu4c4nzQaHesAf8Q==} @@ -5393,6 +5401,29 @@ packages: yn: 3.1.1 dev: true + /ts-poet@6.7.0: + resolution: {integrity: sha512-A0wvFtpkTCWPw7ftTIwbEH+L+7ul4CU0x3jXKQ+kCnmEQIAOwhpUaBmcAYKxZCxHae9/MUl4LbyTqw25BpzW5Q==} + dependencies: + dprint-node: 1.0.8 + dev: true + + /ts-proto-descriptors@1.15.0: + resolution: {integrity: sha512-TYyJ7+H+7Jsqawdv+mfsEpZPTIj9siDHS6EMCzG/z3b/PZiphsX+mWtqFfFVe5/N0Th6V3elK9lQqjnrgTOfrg==} + dependencies: + long: 5.2.3 + protobufjs: 7.2.5 + dev: true + + /ts-proto@1.169.1: + resolution: {integrity: sha512-MHdllDrtFCabxvIyUqze7/4vSh55SEgwirpthGVUGt3pMqIpmmrDyBv0vDk/RCjBxm0/LIWVMnXlOjBxYaE1rA==} + hasBin: true + dependencies: + case-anything: 2.1.13 + protobufjs: 7.2.5 + ts-poet: 6.7.0 + ts-proto-descriptors: 1.15.0 + dev: true + /tsconfig-paths@3.14.2: resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} dependencies: @@ -5579,8 +5610,8 @@ packages: replace-ext: 1.0.1 dev: true - /web-streams-polyfill@3.2.1: - resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} + /web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} dev: true From 5e95f600d9ede18f64086abba40f6f2ce36a3900 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 15:52:36 -0700 Subject: [PATCH 51/69] Fix required dependency mistakenly in dev Should allow the package to be built without installing devDependencies --- package.json | 2 +- pnpm-lock.yaml | 116 +++++++++++++++++++++++++++---------------------- 2 files changed, 66 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index e5c1b99..2d19b40 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@babel/plugin-syntax-flow": "^7.22.5", "@babel/plugin-transform-react-jsx": "^7.22.5", "@types/xmldom": "^0.1.34", + "@yao-pkg/pkg": "^5.11.1", "cheerio": "1.0.0-rc.12", "cors": "^2.8.5", "dotenv": "^16.3.1", @@ -81,7 +82,6 @@ "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@vercel/webpack-asset-relocator-loader": "^1.7.3", - "@yao-pkg/pkg": "^5.11.1", "eslint": "^8.45.0", "eslint-config-react-app": "^7.0.1", "eslint-plugin-import": "^2.27.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4de008b..bad88ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ dependencies: '@types/xmldom': specifier: ^0.1.34 version: 0.1.34 + '@yao-pkg/pkg': + specifier: ^5.11.1 + version: 5.11.1 cheerio: specifier: 1.0.0-rc.12 version: 1.0.0-rc.12 @@ -118,9 +121,6 @@ devDependencies: '@vercel/webpack-asset-relocator-loader': specifier: ^1.7.3 version: 1.7.3 - '@yao-pkg/pkg': - specifier: ^5.11.1 - version: 5.11.1 eslint: specifier: ^8.45.0 version: 8.45.0 @@ -234,7 +234,7 @@ packages: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.18 jsesc: 2.5.2 - dev: true + dev: false /@babel/helper-annotate-as-pure@7.18.6: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} @@ -508,7 +508,7 @@ packages: /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - dev: true + dev: false /@babel/helper-validator-identifier@7.22.5: resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} @@ -575,7 +575,7 @@ packages: hasBin: true dependencies: '@babel/types': 7.23.0 - dev: true + dev: false /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.22.9): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} @@ -1641,7 +1641,7 @@ packages: '@babel/helper-string-parser': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - dev: true + dev: false /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} @@ -2280,7 +2280,7 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true + dev: false /@yao-pkg/pkg@5.11.1: resolution: {integrity: sha512-y++Kd/kMZcp0UeI2GWB9+55WsDqZp3XCjHGzC4Nsao0tlGjhnNOB+TPhZhLTOCL1V4ApaKkH8zzPcpaTCtvM8g==} @@ -2303,7 +2303,7 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true + dev: false /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} @@ -2351,7 +2351,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color - dev: true + dev: false /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -2453,7 +2453,7 @@ packages: /at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} - dev: true + dev: false /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} @@ -2548,6 +2548,7 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false /big-integer@1.6.52: resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} @@ -2567,6 +2568,7 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + dev: false /bluebird@3.4.7: resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} @@ -2639,6 +2641,7 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + dev: false /buffers@0.1.1: resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} @@ -2737,7 +2740,7 @@ packages: /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - dev: true + dev: false /cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -2745,7 +2748,7 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true + dev: false /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} @@ -2847,7 +2850,6 @@ packages: /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true /cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} @@ -2946,6 +2948,7 @@ packages: engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 + dev: false /deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} @@ -2972,7 +2975,7 @@ packages: /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - dev: true + dev: false /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -3024,7 +3027,7 @@ packages: /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} - dev: true + dev: false /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} @@ -3110,6 +3113,7 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: false /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -3124,6 +3128,7 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 + dev: false /entities@4.4.0: resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} @@ -3611,7 +3616,7 @@ packages: /expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - dev: true + dev: false /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} @@ -3772,11 +3777,11 @@ packages: dependencies: inherits: 2.0.4 readable-stream: 2.3.8 - dev: true + dev: false /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: true + dev: false /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} @@ -3804,7 +3809,7 @@ packages: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 - dev: true + dev: false /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -3841,6 +3846,7 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + dev: false /get-intrinsic@1.2.0: resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} @@ -3865,7 +3871,7 @@ packages: /github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - dev: true + dev: false /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -4031,7 +4037,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color - dev: true + dev: false /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -4042,6 +4048,7 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} @@ -4069,7 +4076,7 @@ packages: /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true + dev: false /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} @@ -4085,7 +4092,7 @@ packages: dependencies: from2: 2.3.0 p-is-promise: 3.0.0 - dev: true + dev: false /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} @@ -4136,7 +4143,7 @@ packages: resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} dependencies: has: 1.0.3 - dev: true + dev: false /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} @@ -4157,6 +4164,7 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + dev: false /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -4249,7 +4257,6 @@ packages: /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -4318,6 +4325,7 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.11 + dev: false /jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} @@ -4494,6 +4502,7 @@ packages: /mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + dev: false /min-document@2.19.0: resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} @@ -4511,7 +4520,7 @@ packages: /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - dev: true + dev: false /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} @@ -4545,11 +4554,11 @@ packages: dependencies: once: 1.4.0 readable-stream: 3.6.2 - dev: true + dev: false /napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - dev: true + dev: false /native-promise-only@0.8.1: resolution: {integrity: sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==} @@ -4572,7 +4581,7 @@ packages: engines: {node: '>=10'} dependencies: semver: 7.5.4 - dev: true + dev: false /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} @@ -4589,7 +4598,7 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true + dev: false /node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} @@ -4719,7 +4728,7 @@ packages: /p-is-promise@3.0.0: resolution: {integrity: sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==} engines: {node: '>=8'} - dev: true + dev: false /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} @@ -4814,7 +4823,7 @@ packages: simple-get: 4.0.1 tar-fs: 2.1.1 tunnel-agent: 0.6.0 - dev: true + dev: false /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -4822,7 +4831,6 @@ packages: /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true /process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} @@ -4832,7 +4840,7 @@ packages: /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} - dev: true + dev: false /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -4887,6 +4895,7 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 + dev: false /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} @@ -4930,7 +4939,7 @@ packages: ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - dev: true + dev: false /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -4946,7 +4955,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} @@ -4955,6 +4963,7 @@ packages: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + dev: false /regenerate-unicode-properties@10.1.0: resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} @@ -5021,6 +5030,7 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + dev: false /resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -5081,10 +5091,10 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -5176,7 +5186,7 @@ packages: /simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - dev: true + dev: false /simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} @@ -5184,7 +5194,7 @@ packages: decompress-response: 6.0.0 once: 1.4.0 simple-concat: 1.0.1 - dev: true + dev: false /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} @@ -5206,7 +5216,7 @@ packages: resolution: {integrity: sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ==} dependencies: readable-stream: 2.3.8 - dev: true + dev: false /streamroller@3.1.5: resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} @@ -5230,6 +5240,7 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + dev: false /string.prototype.matchall@4.0.8: resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} @@ -5270,12 +5281,12 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: true /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 + dev: false /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} @@ -5290,7 +5301,7 @@ packages: /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} - dev: true + dev: false /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} @@ -5319,7 +5330,7 @@ packages: mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - dev: true + dev: false /tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -5330,7 +5341,7 @@ packages: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true + dev: false /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -5356,7 +5367,7 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true + dev: false /traverse@0.3.9: resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} @@ -5450,7 +5461,7 @@ packages: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 - dev: true + dev: false /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} @@ -5525,6 +5536,7 @@ packages: /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + dev: false /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} @@ -5617,14 +5629,14 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true + dev: false /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true + dev: false /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -5669,6 +5681,7 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + dev: false /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -5694,6 +5707,7 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + dev: false /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -5714,7 +5728,7 @@ packages: /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} - dev: true + dev: false /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} @@ -5732,7 +5746,7 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - dev: true + dev: false /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} From 1c39e349adb32e9bb5bc1936f482b8329e6f3a65 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 17:43:35 -0700 Subject: [PATCH 52/69] [HD] Initial support for new Hidive API The new API can be accessed with `--hdapi new` --- .gitignore | 1 + @types/newHidiveEpisode.d.ts | 43 + @types/newHidivePlayback.d.ts | 33 + @types/newHidiveSearch.d.ts | 91 ++ @types/newHidiveSeason.d.ts | 85 ++ @types/newHidiveSeries.d.ts | 35 + gui/react/src/provider/ServiceProvider.tsx | 2 +- gui/server/services/hidive.ts | 153 +++- hidive.ts | 921 +++++++++++++++++++-- modules/module.api-urls.ts | 10 +- modules/module.app-args.ts | 1 + modules/module.args.ts | 14 + modules/module.cfg-loader.ts | 26 +- modules/module.langsData.ts | 7 +- modules/module.req.ts | 4 +- modules/module.transform-mpd.ts | 22 +- package.json | 2 +- 17 files changed, 1315 insertions(+), 135 deletions(-) create mode 100644 @types/newHidiveEpisode.d.ts create mode 100644 @types/newHidivePlayback.d.ts create mode 100644 @types/newHidiveSearch.d.ts create mode 100644 @types/newHidiveSeason.d.ts create mode 100644 @types/newHidiveSeries.d.ts diff --git a/.gitignore b/.gitignore index 2a83dc4..9ac97e5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ cr_token.yml hd_profile.yml hd_sess.yml hd_token.yml +hd_new_token.yml archive.json guistate.json fonts diff --git a/@types/newHidiveEpisode.d.ts b/@types/newHidiveEpisode.d.ts new file mode 100644 index 0000000..90fe2c7 --- /dev/null +++ b/@types/newHidiveEpisode.d.ts @@ -0,0 +1,43 @@ +export interface NewHidiveEpisode { + description: string; + duration: number; + title: string; + categories: string[]; + contentDownload: ContentDownload; + favourite: boolean; + subEvents: any[]; + thumbnailUrl: string; + longDescription: string; + posterUrl: string; + offlinePlaybackLanguages: string[]; + externalAssetId: string; + maxHeight: number; + rating: Rating; + episodeInformation: EpisodeInformation; + id: number; + accessLevel: string; + playerUrlCallback: string; + thumbnailsPreview: string; + displayableTags: any[]; + plugins: any[]; + watchStatus: string; + computedReleases: any[]; + licences: any[]; + type: string; +} + +export interface ContentDownload { + permission: string; + period: string; +} + +export interface EpisodeInformation { + seasonNumber: number; + episodeNumber: number; + season: number; +} + +export interface Rating { + rating: string; + descriptors: any[]; +} \ No newline at end of file diff --git a/@types/newHidivePlayback.d.ts b/@types/newHidivePlayback.d.ts new file mode 100644 index 0000000..f25c863 --- /dev/null +++ b/@types/newHidivePlayback.d.ts @@ -0,0 +1,33 @@ +export interface NewHidivePlayback { + watermark: null; + skipMarkers: any[]; + annotations: null; + dash: Format[]; + hls: Format[]; +} + +export interface Format { + subtitles: Subtitle[]; + url: string; + drm: DRM; +} + +export interface DRM { + encryptionMode: string; + containerType: string; + jwtToken: string; + url: string; + keySystems: string[]; +} + +export interface Subtitle { + format: Formats; + language: string; + url: string; +} + +export enum Formats { + Scc = 'scc', + Srt = 'srt', + Vtt = 'vtt', +} diff --git a/@types/newHidiveSearch.d.ts b/@types/newHidiveSearch.d.ts new file mode 100644 index 0000000..60c70c2 --- /dev/null +++ b/@types/newHidiveSearch.d.ts @@ -0,0 +1,91 @@ +export interface NewHidiveSearch { + results: Result[]; +} + +export interface Result { + hits: Hit[]; + nbHits: number; + page: number; + nbPages: number; + hitsPerPage: number; + exhaustiveNbHits: boolean; + exhaustiveTypo: boolean; + exhaustive: Exhaustive; + query: string; + params: string; + index: string; + renderingContent: RenderingContent; + processingTimeMS: number; + processingTimingsMS: ProcessingTimingsMS; + serverTimeMS: number; +} + +export interface Exhaustive { + nbHits: boolean; + typo: boolean; +} + +export interface Hit { + type: string; + weight: number; + id: number; + name: string; + description: string; + meta: RenderingContent; + coverUrl: string; + smallCoverUrl: string; + seasonsCount: number; + tags: string[]; + localisations: HitLocalisations; + ratings: Ratings; + objectID: string; + _highlightResult: HighlightResult; +} + +export interface HighlightResult { + name: Description; + description: Description; + tags: Description[]; + localisations: HighlightResultLocalisations; +} + +export interface Description { + value: string; + matchLevel: string; + matchedWords: string[]; + fullyHighlighted?: boolean; +} + +export interface HighlightResultLocalisations { + en_US: PurpleEnUS; +} + +export interface PurpleEnUS { + title: Description; + description: Description; +} + +export interface HitLocalisations { + [language: string]: HitLocalization; +} + +export interface HitLocalization { + title: string; + description: string; +} + +export interface RenderingContent { +} + +export interface Ratings { + US: string[]; +} + +export interface ProcessingTimingsMS { + _request: Request; +} + +export interface Request { + queue: number; + roundTrip: number; +} diff --git a/@types/newHidiveSeason.d.ts b/@types/newHidiveSeason.d.ts new file mode 100644 index 0000000..5d59c29 --- /dev/null +++ b/@types/newHidiveSeason.d.ts @@ -0,0 +1,85 @@ +export interface NewHidiveSeason { + title: string; + description: string; + longDescription: string; + smallCoverUrl: string; + coverUrl: string; + titleUrl: string; + posterUrl: string; + seasonNumber: number; + episodeCount: number; + displayableTags: any[]; + rating: Rating; + contentRating: Rating; + id: number; + series: Series; + episodes: Episode[]; + paging: Paging; + licences: any[]; +} + +export interface Rating { + rating: string; + descriptors: any[]; +} + +export interface Episode { + accessLevel: string; + availablePurchases: any[]; + licenceIds: any[]; + type: string; + id: number; + title: string; + description: string; + thumbnailUrl: string; + posterUrl: string; + duration: number; + favourite: boolean; + contentDownload: ContentDownload; + offlinePlaybackLanguages: string[]; + externalAssetId: string; + subEvents: any[]; + maxHeight: number; + thumbnailsPreview: string; + longDescription: string; + episodeInformation: EpisodeInformation; + categories: string[]; + displayableTags: any[]; + watchStatus: string; + computedReleases: any[]; +} + +export interface ContentDownload { + permission: string; +} + +export interface EpisodeInformation { + seasonNumber: number; + episodeNumber: number; + season: number; +} + +export interface Paging { + moreDataAvailable: boolean; + lastSeen: number; +} + +export interface Series { + seriesId: number; + title: string; + description: string; + longDescription: string; + displayableTags: any[]; + rating: Rating; + contentRating: Rating; +} + +export interface NewHidiveEpisodeExtra extends Episode { + titleId: number; + nameLong: string; + seasonTitle: string; + seriesTitle: string; + seriesId?: number; + isSelected: boolean; + jwtToken?: string; +} \ No newline at end of file diff --git a/@types/newHidiveSeries.d.ts b/@types/newHidiveSeries.d.ts new file mode 100644 index 0000000..4391406 --- /dev/null +++ b/@types/newHidiveSeries.d.ts @@ -0,0 +1,35 @@ +export interface NewHidiveSeries { + id: number; + title: string; + description: string; + longDescription: string; + smallCoverUrl: string; + coverUrl: string; + titleUrl: string; + posterUrl: string; + seasons: Season[]; + rating: Rating; + contentRating: Rating; + displayableTags: any[]; + paging: Paging; +} + +export interface Rating { + rating: string; + descriptors: any[]; +} + +export interface Paging { + moreDataAvailable: boolean; + lastSeen: number; +} + +export interface Season { + title: string; + description: string; + longDescription: string; + seasonNumber: number; + episodeCount: number; + displayableTags: any[]; + id: number; +} diff --git a/gui/react/src/provider/ServiceProvider.tsx b/gui/react/src/provider/ServiceProvider.tsx index c254353..eb770c5 100644 --- a/gui/react/src/provider/ServiceProvider.tsx +++ b/gui/react/src/provider/ServiceProvider.tsx @@ -23,7 +23,7 @@ const ServiceProvider: FCWithChildren = ({ children }) => { <Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}> <Button size='large' variant="contained" onClick={() => setService('funi')} startIcon={<Avatar src={'https://static.funimation.com/static/img/favicon.ico'} />}>Funimation</Button> <Button size='large' variant="contained" onClick={() => setService('crunchy')} startIcon={<Avatar src={'https://static.crunchyroll.com/cxweb/assets/img/favicons/favicon-32x32.png'} />}>Crunchyroll</Button> - <Button size='large' variant="contained" onClick={() => setService('hidive')} startIcon={<Avatar src={'https://www.hidive.com/favicon.ico'} />}>Hidive</Button> + <Button size='large' variant="contained" onClick={() => setService('hidive')} startIcon={<Avatar src={'https://static.diceplatform.com/prod/original/dce.hidive/settings/HIDIVE_AppLogo_1024x1024.0G0vK.jpg'} />}>Hidive</Button> </Box> </Box> : <serviceContext.Provider value={service}> diff --git a/gui/server/services/hidive.ts b/gui/server/services/hidive.ts index e07eea2..a61dd72 100644 --- a/gui/server/services/hidive.ts +++ b/gui/server/services/hidive.ts @@ -26,7 +26,13 @@ class HidiveHandler extends Base implements MessageHandler { return { isOk: true, value: undefined }; } + public async getAPIVersion() { + const _default = yargs.appArgv(this.hidive.cfg.cli, true); + this.hidive.api = _default.hdapi; + } + public async search(data: SearchData): Promise<SearchResponse> { + await this.getAPIVersion(); console.debug(`Got search options: ${JSON.stringify(data)}`); const hidiveSearch = await this.hidive.doSearch(data); if (!hidiveSearch.isOk) { @@ -42,7 +48,7 @@ class HidiveHandler extends Base implements MessageHandler { public async availableDubCodes(): Promise<string[]> { const dubLanguageCodesArray: string[] = []; for(const language of languages){ - if (language.hd_locale) + if (language.new_hd_locale) dubLanguageCodesArray.push(language.code); } return [...new Set(dubLanguageCodesArray)]; @@ -51,7 +57,7 @@ class HidiveHandler extends Base implements MessageHandler { public async availableSubCodes(): Promise<string[]> { const subLanguageCodesArray: string[] = []; for(const language of languages){ - if (language.hd_locale) + if (language.new_hd_locale) subLanguageCodesArray.push(language.locale); } return ['all', 'none', ...new Set(subLanguageCodesArray)]; @@ -62,63 +68,120 @@ class HidiveHandler extends Base implements MessageHandler { if (isNaN(parse) || parse <= 0) return false; console.debug(`Got resolve options: ${JSON.stringify(data)}`); - const res = await this.hidive.getShow(parseInt(data.id), data.e, data.but, data.all); - if (!res.isOk || !res.value) - return res.isOk; - this.addToQueue(res.value.map(item => { - return { - ...data, - ids: [item.Id], - title: item.Name, - parent: { - title: item.seriesTitle, - season: parseFloat(item.SeasonNumberValue+'')+'' - }, - image: item.ScreenShotSmallUrl, - e: parseFloat(item.EpisodeNumberValue+'')+'', - episode: parseFloat(item.EpisodeNumberValue+'')+'', - }; - })); - return true; + await this.getAPIVersion(); + if (this.hidive.api == 'old') { + const res = await this.hidive.getShow(parseInt(data.id), data.e, data.but, data.all); + if (!res.isOk || !res.value) + return res.isOk; + this.addToQueue(res.value.map(item => { + return { + ...data, + ids: [item.Id], + title: item.Name, + parent: { + title: item.seriesTitle, + season: parseFloat(item.SeasonNumberValue+'')+'' + }, + image: item.ScreenShotSmallUrl, + e: parseFloat(item.EpisodeNumberValue+'')+'', + episode: parseFloat(item.EpisodeNumberValue+'')+'', + }; + })); + return true; + } else { + const res = await this.hidive.selectSeries(parseInt(data.id), data.e, data.but, data.all); + if (!res.isOk || !res.value) + return res.isOk; + this.addToQueue(res.value.map(item => { + return { + ...data, + ids: [item.id], + title: item.title, + parent: { + title: item.seriesTitle, + season: item.episodeInformation.seasonNumber+'' + }, + image: item.thumbnailUrl, + e: item.episodeInformation.episodeNumber+'', + episode: item.episodeInformation.episodeNumber+'', + }; + })); + return true; + } } public async listEpisodes(id: string): Promise<EpisodeListResponse> { const parse = parseInt(id); if (isNaN(parse) || parse <= 0) return { isOk: false, reason: new Error('The ID is invalid') }; - const request = await this.hidive.listShow(parse); - if (!request.isOk || !request.value) - return {isOk: false, reason: new Error('Unknown upstream error, check for additional logs')}; - return { isOk: true, value: request.value.Episodes.map(function(item) { - const language = item.Summary.match(/^Audio: (.*)/m); - language?.shift(); - const description = item.Summary.split('\r\n'); - return { - e: parseFloat(item.EpisodeNumberValue+'')+'', - lang: language ? language[0].split(', ') : [], - name: item.Name, - season: parseFloat(item.SeasonNumberValue+'')+'', - seasonTitle: request.value.Name, - episode: parseFloat(item.EpisodeNumberValue+'')+'', - id: item.Id+'', - img: item.ScreenShotSmallUrl, - description: description ? description[0] : '', - time: '' - }; - })}; + await this.getAPIVersion(); + if (this.hidive.api == 'old') { + const request = await this.hidive.listShow(parse); + if (!request.isOk || !request.value) + return {isOk: false, reason: new Error('Unknown upstream error, check for additional logs')}; + + return { isOk: true, value: request.value.Episodes.map(function(item) { + const language = item.Summary.match(/^Audio: (.*)/m); + language?.shift(); + const description = item.Summary.split('\r\n'); + return { + e: parseFloat(item.EpisodeNumberValue+'')+'', + lang: language ? language[0].split(', ') : [], + name: item.Name, + season: parseFloat(item.SeasonNumberValue+'')+'', + seasonTitle: request.value.Name, + episode: parseFloat(item.EpisodeNumberValue+'')+'', + id: item.Id+'', + img: item.ScreenShotSmallUrl, + description: description ? description[0] : '', + time: '' + }; + })}; + } else { + const request = await this.hidive.listSeries(parse); + if (!request.isOk || !request.value) + return {isOk: false, reason: new Error('Unknown upstream error, check for additional logs')}; + + return { isOk: true, value: request.value.map(function(item) { + const description = item.description.split('\r\n'); + return { + e: item.episodeInformation.episodeNumber+'', + lang: [], + name: item.title, + season: item.episodeInformation.seasonNumber+'', + seasonTitle: request.series.seasons[item.episodeInformation.seasonNumber-1].title, + episode: item.episodeInformation.episodeNumber+'', + id: item.id+'', + img: item.thumbnailUrl, + description: description ? description[0] : '', + time: '' + }; + })}; + } } public async downloadItem(data: DownloadData) { this.setDownloading(true); console.debug(`Got download options: ${JSON.stringify(data)}`); const _default = yargs.appArgv(this.hidive.cfg.cli, true); - const res = await this.hidive.getShow(parseInt(data.id), data.e, false, false); - if (!res.isOk || !res.showData) - return this.alertError(new Error('Download failed upstream, check for additional logs')); + this.hidive.api = _default.hdapi; + if (this.hidive.api == 'old') { + const res = await this.hidive.getShow(parseInt(data.id), data.e, false, false); + if (!res.isOk || !res.showData) + return this.alertError(new Error('Download failed upstream, check for additional logs')); - for (const ep of res.value) { - await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids }); + for (const ep of res.value) { + await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids }); + } + } else { + const res = await this.hidive.selectSeries(parseInt(data.id), data.e, false, false); + if (!res.isOk || !res.showData) + return this.alertError(new Error('Download failed upstream, check for additional logs')); + + for (const ep of res.value) { + await this.hidive.downloadEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids }); + } } this.sendMessage({ name: 'finish', data: undefined }); this.setDownloading(false); diff --git a/hidive.ts b/hidive.ts index 1681d11..933f30c 100644 --- a/hidive.ts +++ b/hidive.ts @@ -10,7 +10,7 @@ import packageJson from './package.json'; import { console } from './modules/log'; import shlp from 'sei-helper'; import m3u8 from 'm3u8-parsed'; -import streamdl from './modules/hls-download'; +import streamdl, { M3U8Json } from './modules/hls-download'; // custom modules import * as fontsData from './modules/module.fontsData'; @@ -34,12 +34,23 @@ import { ServiceClass } from './@types/serviceClassInterface'; import { sxItem } from './crunchy'; import { HidiveSearch } from './@types/hidiveSearch'; import { HidiveDashboard } from './@types/hidiveDashboard'; +import { Hit, NewHidiveSearch } from './@types/newHidiveSearch'; +import { NewHidiveSeries } from './@types/newHidiveSeries'; +import { Episode, NewHidiveEpisodeExtra, NewHidiveSeason } from './@types/newHidiveSeason'; +import { NewHidiveEpisode } from './@types/newHidiveEpisode'; +import { NewHidivePlayback, Subtitle } from './@types/newHidivePlayback'; +import { MPDParsed, parse } from './modules/module.transform-mpd'; +import getKeys, { canDecrypt } from './modules/widevine'; +import { exec } from './modules/sei-helper-fixes'; +import { KeyContainer } from './modules/license'; export default class Hidive implements ServiceClass { public cfg: yamlCfg.ConfigObject; private session: Record<string, any>; + private tokenOld: Record<string, any>; private token: Record<string, any>; private req: reqModule.Req; + public api: 'old' | 'new'; private client: { // base ipAddress: string, @@ -58,34 +69,17 @@ export default class Hidive implements ServiceClass { constructor(private debug = false) { this.cfg = yamlCfg.loadCfg(); this.session = yamlCfg.loadHDSession(); - this.token = yamlCfg.loadHDToken(); + this.tokenOld = yamlCfg.loadHDToken(); + this.token = yamlCfg.loadNewHDToken(); this.client = yamlCfg.loadHDProfile() as {ipAddress: string, xNonce: string, xSignature: string, visitId: string, profile: {userId: number, profileId: number, deviceId : string}}; this.req = new reqModule.Req(domain, debug, false, 'hd'); - } - - public async doInit() { - //get client ip - const newIp = await this.reqData('Ping', ''); - if (!newIp.ok || !newIp.res) return false; - this.client.ipAddress = JSON.parse(newIp.res.body).IPAddress; - //get device id - const newDevice = await this.reqData('InitDevice', { 'DeviceName': api.hd_devName }); - if (!newDevice.ok || !newDevice.res) return false; - this.client.profile = Object.assign(this.client.profile, { - deviceId: JSON.parse(newDevice.res.body).Data.DeviceId, - }); - //get visit id - const newVisitId = await this.reqData('InitVisit', {}); - if (!newVisitId.ok || !newVisitId.res) return false; - this.client.visitId = JSON.parse(newVisitId.res.body).Data.VisitId; - //save client - yamlCfg.saveHDProfile(this.client); - return true; + this.api = 'old'; } public async cli() { console.info(`\n=== Multi Downloader NX ${packageJson.version} ===\n`); const argv = yargs.appArgv(this.cfg.cli); + this.api = argv.hdapi; if (argv.debug) this.debug = true; @@ -96,6 +90,16 @@ export default class Hidive implements ServiceClass { console.info(searchItems.res.body); fs.writeFileSync('apitest.json', JSON.stringify(JSON.parse(searchItems.res.body), null, 2));*/ + //new api testing + /*if (this.api == 'new') { + await this.doInit(); + const apiTest = await this.apiReq('/v4/season/18871', '', 'auth', 'GET'); + if(!apiTest.ok || !apiTest.res){return;} + console.info(apiTest.res.body); + fs.writeFileSync('apitest.json', JSON.stringify(JSON.parse(apiTest.res.body), null, 2)); + return console.info('test done'); + }*/ + // load binaries this.cfg.bin = await yamlCfg.loadBinCfg(); if (argv.allDubs) { @@ -115,16 +119,29 @@ export default class Hidive implements ServiceClass { //Search await this.doSearch({ ...argv, search: argv.search as string }); } else if (argv.s && !isNaN(parseInt(argv.s,10)) && parseInt(argv.s,10) > 0) { + if (this.api == 'old') { //Initilize session - await this.doInit(); - //get selected episodes - const selected = await this.getShow(parseInt(argv.s), argv.e, argv.but, argv.all); - if (selected.isOk && selected.showData) { - for (const select of selected.value) { + await this.doInit(); + //get selected episodes + const selected = await this.getShow(parseInt(argv.s), argv.e, argv.but, argv.all); + if (selected.isOk && selected.showData) { + for (const select of selected.value) { //download episode - if (!(await this.getEpisode(select, {...argv}))) { - console.error(`Unable to download selected episode ${parseFloat(select.EpisodeNumberValue+'')}`); - return false; + if (!(await this.getEpisode(select, {...argv}))) { + console.error(`Unable to download selected episode ${parseFloat(select.EpisodeNumberValue+'')}`); + return false; + } + } + } + } else { + const selected = await this.selectSeries(parseInt(argv.s), argv.e, argv.but, argv.all); + if (selected.isOk && selected.showData) { + for (const select of selected.value) { + //download episode + if (!(await this.downloadEpisode(select, {...argv}))) { + console.error(`Unable to download selected episode ${select.episodeInformation.episodeNumber}`); + return false; + } } } } @@ -139,6 +156,31 @@ export default class Hidive implements ServiceClass { } } + public async doInit() { + if (this.api == 'old') { + //get client ip + const newIp = await this.reqData('Ping', ''); + if (!newIp.ok || !newIp.res) return false; + this.client.ipAddress = JSON.parse(newIp.res.body).IPAddress; + //get device id + const newDevice = await this.reqData('InitDevice', { 'DeviceName': api.hd_devName }); + if (!newDevice.ok || !newDevice.res) return false; + this.client.profile = Object.assign(this.client.profile, { + deviceId: JSON.parse(newDevice.res.body).Data.DeviceId, + }); + //get visit id + const newVisitId = await this.reqData('InitVisit', {}); + if (!newVisitId.ok || !newVisitId.res) return false; + this.client.visitId = JSON.parse(newVisitId.res.body).Data.VisitId; + //save client + yamlCfg.saveHDProfile(this.client); + return true; + } else { + //this.refreshToken(); + return true; + } + } + // Generate Nonce public generateNonce(){ const initDate = new Date(); @@ -296,22 +338,182 @@ export default class Hidive implements ServiceClass { } } - public async doAuth(data: AuthData): Promise<AuthResponse> { - const auth = await this.reqData('Authenticate', {'Email':data.username,'Password':data.password}); - if(!auth.ok || !auth.res) { - console.error('Authentication failed!'); - return { isOk: false, reason: new Error('Authentication failed') }; + public async apiReq(endpoint: string, body: string | object = '', authType: 'refresh' | 'auth' | 'both' | 'other' | 'none' = 'none', method: 'GET' | 'POST' = 'POST', authHeader?: string) { + const options = { + headers: { + 'X-Api-Key': api.hd_new_apiKey, + 'X-App-Var': api.hd_new_version, + 'realm': 'dce.hidive', + 'Referer': 'https://www.hidive.com/', + 'Origin': 'https://www.hidive.com' + } as Record<string, unknown>, + method: method as 'GET'|'POST', + url: api.hd_new_api+endpoint as string, + body: body, + useProxy: true + }; + // get request type + const isGet = method == 'GET' ? true : false; + if(!isGet){ + options.body = body == '' ? body : JSON.stringify(body); + options.headers['Content-Type'] = 'application/json'; } - const authData = JSON.parse(auth.res.body).Data; - this.client.profile = Object.assign(this.client.profile, { - userId: authData.User.Id, - profileId: authData.Profiles[0].Id, - }); - yamlCfg.saveHDProfile(this.client); - yamlCfg.saveHDToken(authData); - console.info('[INFO] Auth complete!'); - console.info(`[INFO] Service level for "${data.username}" is ${authData.User.ServiceLevel}`); - return { isOk: true, value: undefined }; + if (authType == 'other') { + options.headers['Authorization'] = authHeader; + } else if (authType == 'auth') { + options.headers['Authorization'] = `Bearer ${this.token.authorisationToken}`; + } else if (authType == 'refresh') { + options.headers['Authorization'] = `Bearer ${this.token.refreshToken}`; + } else if (authType == 'both') { + options.headers['Authorization'] = `Mixed ${this.token.authorisationToken} ${this.token.refreshToken}`; + } + if (this.debug) { + console.debug('[DEBUG] Request params:'); + console.debug(options); + } + const apiReqOpts: reqModule.Params = { + method: options.method, + headers: options.headers as Record<string, string>, + body: options.body as string + }; + let apiReq = await this.req.getData(options.url, apiReqOpts); + if(!apiReq.ok || !apiReq.res){ + if (apiReq.error && apiReq.error.res.statusCode == 401) { + console.warn('Token expired, refreshing token and retrying.'); + if (await this.refreshToken()) { + if (authType == 'other') { + options.headers['Authorization'] = authHeader; + } else if (authType == 'auth') { + options.headers['Authorization'] = `Bearer ${this.token.authorisationToken}`; + } else if (authType == 'refresh') { + options.headers['Authorization'] = `Bearer ${this.token.refreshToken}`; + } else if (authType == 'both') { + options.headers['Authorization'] = `Mixed ${this.token.authorisationToken} ${this.token.refreshToken}`; + } + apiReq = await this.req.getData(options.url, apiReqOpts); + if(!apiReq.ok || !apiReq.res) { + console.error('API Request Failed!'); + return { + ok: false, + res: apiReq.res, + error: apiReq.error + }; + } + } else { + console.error('Failed to refresh token...'); + return { + ok: false, + res: apiReq.res, + error: apiReq.error + }; + } + } else { + console.error('API Request Failed!'); + return { + ok: false, + res: apiReq.res, + error: apiReq.error + }; + } + } + return { + ok: true, + res: apiReq.res, + }; + } + + public async doAuth(data: AuthData): Promise<AuthResponse> { + if (this.api == 'old') { + const auth = await this.reqData('Authenticate', {'Email':data.username,'Password':data.password}); + if(!auth.ok || !auth.res) { + console.error('Authentication failed!'); + return { isOk: false, reason: new Error('Authentication failed') }; + } + const authData = JSON.parse(auth.res.body).Data; + this.client.profile = Object.assign(this.client.profile, { + userId: authData.User.Id, + profileId: authData.Profiles[0].Id, + }); + yamlCfg.saveHDProfile(this.client); + yamlCfg.saveHDToken(authData); + console.info('Auth complete!'); + console.info(`Service level for "${data.username}" is ${authData.User.ServiceLevel}`); + return { isOk: true, value: undefined }; + } else { + if (!this.token.refreshToken || !this.token.authorisationToken) { + await this.doAnonymousAuth(); + } + const authReq = await this.apiReq('/v2/login', { + id: data.username, + secret: data.password + }, 'auth'); + if(!authReq.ok || !authReq.res){ + console.error('Authentication failed!'); + return { isOk: false, reason: new Error('Authentication failed') }; + } + const tokens: Record<string, string> = JSON.parse(authReq.res.body); + for (const token in tokens) { + this.token[token] = tokens[token]; + } + this.token.guest = false; + yamlCfg.saveNewHDToken(this.token); + console.info('Auth complete!'); + return { isOk: true, value: undefined }; + } + } + + public async doAnonymousAuth() { + const authReq = await this.apiReq('/v2/login/guest/checkin'); + if(!authReq.ok || !authReq.res){ + console.error('Authentication failed!'); + return false; + } + const tokens: Record<string, string> = JSON.parse(authReq.res.body); + for (const token in tokens) { + this.token[token] = tokens[token]; + } + //this.token.expires = new Date(Date.now() + 300); + this.token.guest = true; + yamlCfg.saveNewHDToken(this.token); + return true; + } + + public async refreshToken() { + if (!this.token.refreshToken || !this.token.authorisationToken) { + return await this.doAnonymousAuth(); + } else { + const authReq = await this.apiReq('/v2/token/refresh', { + 'refreshToken': this.token.refreshToken + }, 'auth'); + if(!authReq.ok || !authReq.res){ + console.error('Token refresh failed, reinitializing session...'); + if (!this.initSession()) { + return false; + } else { + return true; + } + } + const tokens: Record<string, string> = JSON.parse(authReq.res.body); + for (const token in tokens) { + this.token[token] = tokens[token]; + } + yamlCfg.saveNewHDToken(this.token); + return true; + } + } + + public async initSession() { + const authReq = await this.apiReq('/v1/init/', '', 'both', 'GET'); + if(!authReq.ok || !authReq.res){ + console.error('Failed to initialize session.'); + return false; + } + const tokens: Record<string, string> = JSON.parse(authReq.res.body).authentication; + for (const token in tokens) { + this.token[token] = tokens[token]; + } + yamlCfg.saveNewHDToken(this.token); + return true; } public async genSubsUrl(type: string, file: string) { @@ -323,30 +525,75 @@ export default class Hidive implements ServiceClass { } public async doSearch(data: SearchData): Promise<SearchResponse> { - const searchReq = await this.reqData('Search', {'Query':data.search}); - if(!searchReq.ok || !searchReq.res){ - console.error('Search FAILED!'); - return { isOk: false, reason: new Error('Search failed. No more information provided') }; - } - const searchData = JSON.parse(searchReq.res.body) as HidiveSearch; - const searchItems = searchData.Data.TitleResults; - if(searchItems.length>0) { - console.info('[INFO] Search Results:'); - for(let i=0;i<searchItems.length;i++){ - console.info(`[#${searchItems[i].Id}] ${searchItems[i].Name} [${searchItems[i].ShowInfoTitle}]`); + if (this.api == 'old') { + const searchReq = await this.reqData('Search', {'Query':data.search}); + if(!searchReq.ok || !searchReq.res){ + console.error('Search FAILED!'); + return { isOk: false, reason: new Error('Search failed. No more information provided') }; } - } else{ - console.warn('Nothing found!'); + const searchData = JSON.parse(searchReq.res.body) as HidiveSearch; + const searchItems = searchData.Data.TitleResults; + if(searchItems.length>0) { + console.info('[INFO] Search Results:'); + for(let i=0;i<searchItems.length;i++){ + console.info(`[#${searchItems[i].Id}] ${searchItems[i].Name} [${searchItems[i].ShowInfoTitle}]`); + } + } else{ + console.warn('Nothing found!'); + } + return { isOk: true, value: searchItems.map((a): SearchResponseItem => { + return { + id: a.Id+'', + image: a.KeyArtUrl ?? '/notFound.png', + name: a.Name, + rating: a.OverallRating, + desc: a.LongSynopsis + }; + })}; + } else { + const searchReq = await this.req.getData('https://h99xldr8mj-dsn.algolia.net/1/indexes/*/queries?x-algolia-agent=Algolia%20for%20JavaScript%20(3.35.1)%3B%20Browser&x-algolia-application-id=H99XLDR8MJ&x-algolia-api-key=e55ccb3db0399eabe2bfc37a0314c346', { + method: 'POST', + body: JSON.stringify({'requests': + [ + {'indexName':'prod-dce.hidive-livestreaming-events','params':'query='+encodeURIComponent(data.search)+'&facetFilters=%5B%22type%3ALIVE_EVENT%22%5D&hitsPerPage=25'+(data.page ? '&page='+(data.page-1) : '')}, + {'indexName':'prod-dce.hidive-livestreaming-events','params':'query='+encodeURIComponent(data.search)+'&facetFilters=%5B%22type%3AVOD_VIDEO%22%5D&hitsPerPage=25'+(data.page ? '&page='+(data.page-1) : '')}, + {'indexName':'prod-dce.hidive-livestreaming-events','params':'query='+encodeURIComponent(data.search)+'&facetFilters=%5B%22type%3AVOD_PLAYLIST%22%5D&hitsPerPage=25'+(data.page ? '&page='+(data.page-1) : '')}, + {'indexName':'prod-dce.hidive-livestreaming-events','params':'query='+encodeURIComponent(data.search)+'&facetFilters=%5B%22type%3AVOD_SERIES%22%5D&hitsPerPage=25'+(data.page ? '&page='+(data.page-1) : '')} + ] + }) + }); + if(!searchReq.ok || !searchReq.res){ + console.error('Search FAILED!'); + return { isOk: false, reason: new Error('Search failed. No more information provided') }; + } + const searchData = JSON.parse(searchReq.res.body) as NewHidiveSearch; + const searchItems: Hit[] = []; + console.info('Search Results:'); + for (const category of searchData.results) { + for (const hit of category.hits) { + searchItems.push(hit); + let fullType: string; + if (hit.type == 'VOD_SERIES') { + fullType = `Z.${hit.id}`; + } else if (hit.type == 'VOD_VIDEO') { + fullType = `E.${hit.id}`; + } else { + fullType = `${hit.type} #${hit.id}`; + } + console.log(`[${fullType}] ${hit.name} ${hit.seasonsCount ? '('+hit.seasonsCount+' Seasons)' : ''}`); + } + } + return { isOk: true, value: searchItems.filter(a => a.type == 'VOD_SERIES').flatMap((a): SearchResponseItem => { + console.info(a); + return { + id: a.id+'', + image: a.coverUrl ?? '/notFound.png', + name: a.name, + rating: -1, + desc: a.description + }; + })}; } - return { isOk: true, value: searchItems.map((a): SearchResponseItem => { - return { - id: a.Id+'', - image: a.KeyArtUrl ?? '/notFound.png', - name: a.Name, - rating: a.OverallRating, - desc: a.LongSynopsis - }; - })}; } public async getNewlyAdded(page?: number) { @@ -376,11 +623,118 @@ export default class Hidive implements ServiceClass { } } + public async getSeries(id: number) { + const getSeriesData = await this.apiReq(`/v4/series/${id}?rpp=20`, '', 'auth', 'GET'); + if (!getSeriesData.ok || !getSeriesData.res) { + console.error('Failed to get Series Data'); + return { isOk: false }; + } + const seriesData = JSON.parse(getSeriesData.res.body) as NewHidiveSeries; + return { isOk: true, value: seriesData }; + } + + /** + * Function to get the season data from the API + * @param id ID of the season + * @param lastSeen Last episode ID seen, used for paging + * @returns + */ + public async getSeason(id: number, lastSeen?: number) { + const getSeasonData = await this.apiReq(`/v4/season/${id}?rpp=20${lastSeen ? '&lastSeen='+lastSeen : ''}`, '', 'auth', 'GET'); + if (!getSeasonData.ok || !getSeasonData.res) { + console.error('Failed to get Season Data'); + return { isOk: false }; + } + const seasonData = JSON.parse(getSeasonData.res.body) as NewHidiveSeason; + return { isOk: true, value: seasonData }; + } + + public async listSeries(id: number) { + const series = await this.getSeries(id); + if (!series.isOk || !series.value) { + console.error('Failed to list series data: Failed to get series'); + return { isOk: false }; + } + console.info(`[Z.${series.value.id}] ${series.value.title} (${series.value.seasons.length} Seasons)`); + for (const seasonData of series.value.seasons) { + const season = await this.getSeason(seasonData.id); + if (!season.isOk || !season.value) { + console.error('Failed to list series data: Failed to get season '+seasonData.id); + return { isOk: false }; + } + console.info(` [S.${season.value.id}] ${season.value.title} (${season.value.episodeCount} Episodes)`); + while (season.value.paging.moreDataAvailable) { + const seasonPage = await this.getSeason(seasonData.id, season.value.paging.lastSeen); + if (!seasonPage.isOk || !seasonPage.value) break; + season.value.episodes = season.value.episodes.concat(seasonPage.value.episodes); + season.value.paging.lastSeen = seasonPage.value.paging.lastSeen; + season.value.paging.moreDataAvailable = seasonPage.value.paging.moreDataAvailable; + } + const episodes: Episode[] = []; + for (const episode of season.value.episodes) { + if (episode.title.includes(' - ')) { + episode.episodeInformation.episodeNumber = parseFloat(episode.title.split(' - ')[0].replace('E', '')); + episode.title = episode.title.split(' - ')[1]; + } + //S${episode.episodeInformation.seasonNumber}E${episode.episodeInformation.episodeNumber} - + episodes.push(episode); + console.info(` [E.${episode.id}] ${episode.title}`); + } + return { isOk: true, value: episodes, series: series.value }; + } + console.info(' No Seasons found!'); + return { isOk: false }; + } + + /** + * Lists the requested series, and returns the selected episodes + * @param id Series ID + * @param e Selector + * @param but Download all but selected videos + * @param all Whether to download all available videos + * @returns + */ + public async selectSeries(id: number, e: string | undefined, but: boolean, all: boolean) { + const getShowData = await this.listSeries(id); + if (!getShowData.isOk || !getShowData.value) { + return { isOk: false, value: [] }; + } + const showData = getShowData.value; + const doEpsFilter = parseSelect(e as string); + // build selected episodes + const selEpsArr: NewHidiveEpisodeExtra[] = []; let ovaSeq = 1; let movieSeq = 1; + for (let i = 0; i < showData.length; i++) { + const titleId = showData[i].id; + const seriesTitle = getShowData.series.title; + const seasonTitle = getShowData.series.seasons[showData[i].episodeInformation.seasonNumber-1]?.title; + let nameLong = showData[i].title; + if (nameLong.match(/OVA/i)) { + nameLong = 'ova' + (('0' + ovaSeq).slice(-2)); ovaSeq++; + } else if (nameLong.match(/Theatrical/i)) { + nameLong = 'movie' + (('0' + movieSeq).slice(-2)); movieSeq++; + } + let selMark = ''; + if (all || + but && !doEpsFilter.isSelected([parseFloat(showData[i].episodeInformation.episodeNumber+'')+'', showData[i].id+'']) || + !but && doEpsFilter.isSelected([parseFloat(showData[i].episodeInformation.episodeNumber+'')+'', showData[i].id+'']) + ) { + selEpsArr.push({ isSelected: true, titleId, nameLong, seasonTitle, seriesTitle, ...showData[i] }); + selMark = '✓ '; + } + console.info('%s[%s] %s', + selMark, + 'S'+parseFloat(showData[i].episodeInformation.seasonNumber+'')+'E'+parseFloat(showData[i].episodeInformation.episodeNumber+''), + showData[i].title, + ); + } + return { isOk: true, value: selEpsArr, showData: getShowData.series }; + } + public async listShow(id: number) { const getShowData = await this.reqData('GetTitle', { 'Id': id }); if (!getShowData.ok || !getShowData.res) { console.error('Failed to get show data'); - return { isOk: false}; + return { isOk: false }; } const rawShowData = JSON.parse(getShowData.res.body) as HidiveEpisodeList; const showData = rawShowData.Data.Title; @@ -434,14 +788,14 @@ export default class Hidive implements ServiceClass { sumSub ); } - return { isOk: true, value: selEpsArr, showData: showData } ; + return { isOk: true, value: selEpsArr, showData: showData }; } public async getEpisode(selectedEpisode: HidiveEpisodeExtra, options: Record<any, any>) { const getVideoData = await this.reqData('GetVideos', { 'VideoKey': selectedEpisode.epKey, 'TitleId': selectedEpisode.titleId }); if (getVideoData.ok && getVideoData.res) { const videoData = JSON.parse(getVideoData.res.body) as HidiveVideoList; - const showTitle = `${selectedEpisode.seriesTitle} S${parseFloat(selectedEpisode.SeasonNumberValue+'')}}`; + const showTitle = `${selectedEpisode.seriesTitle} S${parseFloat(selectedEpisode.SeasonNumberValue+'')}`; console.info(`[INFO] ${showTitle} - ${parseFloat(selectedEpisode.EpisodeNumberValue+'')}`); const videoList = videoData.Data.VideoLanguages; const subsList = videoData.Data.CaptionLanguages; @@ -522,6 +876,403 @@ export default class Hidive implements ServiceClass { return { isOk: false, reason: new Error('Unknown download error') }; } + public async downloadEpisode(selectedEpisode: NewHidiveEpisodeExtra, options: Record<any, any>) { + //Get Episode data + const episodeDataReq = await this.apiReq(`/v4/vod/${selectedEpisode.id}?includePlaybackDetails=URL`, '', 'auth', 'GET'); + if (!episodeDataReq.ok || !episodeDataReq.res) { + console.error('Failed to get episode data'); + return { isOk: false, reason: new Error('Failed to get Episode Data') }; + } + const episodeData = JSON.parse(episodeDataReq.res.body) as NewHidiveEpisode; + + if (!episodeData.playerUrlCallback) { + console.error('Failed to download episode: You do not have access to this'); + return { isOk: false, reason: new Error('You do not have access to this') }; + } + + //Get Playback data + const playbackReq = await this.req.getData(episodeData.playerUrlCallback); + if(!playbackReq.ok || !playbackReq.res){ + console.error('Playback Request Failed'); + return { isOk: false, reason: new Error('Playback request failed') }; + } + const playbackData = JSON.parse(playbackReq.res.body) as NewHidivePlayback; + + //Get actual MPD + const mpdRequest = await this.req.getData(playbackData.dash[0].url); + if(!mpdRequest.ok || !mpdRequest.res){ + console.error('MPD Request Failed'); + return { isOk: false, reason: new Error('MPD request failed') }; + } + const mpd = mpdRequest.res.body as string; + + selectedEpisode.jwtToken = playbackData.dash[0].drm.jwtToken; + + //Output metadata and prepare for download + const availableSubs = playbackData.dash[0].subtitles.filter(a => a.format === 'vtt'); + const showTitle = `${selectedEpisode.seriesTitle} S${selectedEpisode.episodeInformation.seasonNumber}`; + console.info(`[INFO] ${showTitle} - ${selectedEpisode.episodeInformation.episodeNumber}`); + console.info('[INFO] Available dubs and subtitles:'); + console.info('\tAudios: ' + episodeData.offlinePlaybackLanguages.map(a => langsData.languages.find(b => b.code == a)?.name).join('\n\t\t')); + console.info('\tSubs : ' + availableSubs.map(a => langsData.languages.find(b => b.new_hd_locale == a.language)?.name).join('\n\t\t')); + console.info(`[INFO] Selected dub(s): ${options.dubLang.join(', ')}`); + const baseUrl = playbackData.dash[0].url.split('master')[0]; + const parsedmpd = parse(mpd, undefined, baseUrl); + const res = await this.downloadMPD(parsedmpd, availableSubs, selectedEpisode, options); + if (res === undefined || res.error) { + console.error('Failed to download media list'); + return { isOk: false, reason: new Error('Failed to download media list') }; + } else { + if (!options.skipmux) { + await this.muxStreams(res.data, { ...options, output: res.fileName }, false); + } else { + console.info('Skipping mux'); + } + downloaded({ + service: 'hidive', + type: 's' + }, selectedEpisode.titleId+'', [selectedEpisode.episodeInformation.episodeNumber+'']); + return { isOk: res, value: undefined }; + } + } + + public async downloadMPD(streamPlaylists: MPDParsed, subs: Subtitle[], selectedEpisode: NewHidiveEpisodeExtra, options: Record<any, any>) { + //let fileName: string; + const files: DownloadedMedia[] = []; + const variables: Variable[] = []; + let dlFailed = false; + /*const subsMargin = 0; + const videoIndex = 0; + const chosenFontSize = options.fontSize;*/ + let encryptionKeys: KeyContainer[] | undefined = undefined; + if (!canDecrypt) console.warn('Decryption not enabled!'); + + variables.push(...([ + ['title', selectedEpisode.title, true], + ['episode', selectedEpisode.episodeInformation.episodeNumber, false], + ['service', 'HD', false], + ['seriesTitle', selectedEpisode.seasonTitle, true], + ['showTitle', selectedEpisode.seriesTitle, true], + ['season', selectedEpisode.episodeInformation.seasonNumber, false] + ] as [AvailableFilenameVars, string|number, boolean][]).map((a): Variable => { + return { + name: a[0], + replaceWith: a[1], + type: typeof a[1], + sanitize: a[2] + } as Variable; + })); + + //Get name of CDNs/Servers + const streamServers = Object.keys(streamPlaylists); + + options.x = options.x > streamServers.length ? 1 : options.x; + + const selectedServer = streamServers[options.x - 1]; + const selectedList = streamPlaylists[selectedServer]; + + //set Video Qualities + const videos = selectedList.video.map(item => { + return { + ...item, + resolutionText: `${item.quality.width}x${item.quality.height} (${Math.round(item.bandwidth/1024)}KiB/s)` + }; + }); + + const audios = selectedList.audio.map(item => { + return { + ...item, + resolutionText: `${Math.round(item.bandwidth/1000)}kB/s` + }; + }); + + + videos.sort((a, b) => { + return a.bandwidth - b.bandwidth; + }); + + videos.sort((a, b) => { + return a.quality.width - b.quality.width; + }); + + audios.sort((a, b) => { + return a.bandwidth - b.bandwidth; + }); + + let chosenVideoQuality = options.q === 0 ? videos.length : options.q; + if(chosenVideoQuality > videos.length) { + console.warn(`The requested quality of ${options.q} is greater than the maximum ${videos.length}.\n[WARN] Therefor the maximum will be capped at ${videos.length}.`); + chosenVideoQuality = videos.length; + } + chosenVideoQuality--; + + const chosenVideoSegments = videos[chosenVideoQuality]; + + console.info(`Servers available:\n\t${streamServers.join('\n\t')}`); + console.info(`Available Video Qualities:\n\t${videos.map((a, ind) => `[${ind+1}] ${a.resolutionText}`).join('\n\t')}`); + console.info(`Available Audio Qualities:\n\t${audios.map((a, ind) => `[${ind+1}] ${a.resolutionText}`).join('\n\t')}`); + + variables.push({ + name: 'height', + type: 'number', + replaceWith: chosenVideoSegments.quality.height + }, { + name: 'width', + type: 'number', + replaceWith: chosenVideoSegments.quality.width + }); + + const chosenAudios: typeof audios[0][] = []; + const audioByLanguage: Record<string,typeof audios[0][]> = {}; + for (const audio of audios) { + if (!audioByLanguage[audio.language.code]) audioByLanguage[audio.language.code] = []; + audioByLanguage[audio.language.code].push(audio); + } + for (const dubLang of options.dubLang as string[]) { + if (audioByLanguage[dubLang]) { + let chosenAudioQuality = options.q === 0 ? audios.length : options.q; + if(chosenAudioQuality > audioByLanguage[dubLang].length) { + chosenAudioQuality = audioByLanguage[dubLang].length; + } + chosenAudioQuality--; + chosenAudios.push(audioByLanguage[dubLang][chosenAudioQuality]); + } + } + + const fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep); + + console.info(`Selected quality: \n\tVideo: ${chosenVideoSegments.resolutionText}\n\tAudio: ${chosenAudios[0].resolutionText}\n\tServer: ${selectedServer}`); + console.info(`Selected (Available) Audio Languages: ${chosenAudios.map(a => a.language.name).join(', ')}`); + console.info('Stream URL:', chosenVideoSegments.segments[0].map.uri.split('/init.mp4')[0]); + + if (!options.novids) { + //Download Video + const totalParts = chosenVideoSegments.segments.length; + const mathParts = Math.ceil(totalParts / options.partsize); + const mathMsg = `(${mathParts}*${options.partsize})`; + console.info('Total parts in video stream:', totalParts, mathMsg); + const tsFile = path.isAbsolute(fileName) ? fileName : path.join(this.cfg.dir.content, fileName); + const split = fileName.split(path.sep).slice(0, -1); + split.forEach((val, ind, arr) => { + const isAbsolut = path.isAbsolute(fileName); + if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) + fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); + }); + const videoJson: M3U8Json = { + segments: chosenVideoSegments.segments + }; + const videoDownload = await new streamdl({ + output: `${tsFile}.video.enc.ts`, + timeout: options.timeout, + m3u8json: videoJson, + // baseurl: chunkPlaylist.baseUrl, + threads: options.partsize, + fsRetryTime: options.fsRetryTime * 1000, + override: options.force, + callback: options.callbackMaker ? options.callbackMaker({ + fileName: `${path.isAbsolute(fileName) ? fileName.slice(this.cfg.dir.content.length) : fileName}`, + image: selectedEpisode.thumbnailUrl, + parent: { + title: selectedEpisode.seriesTitle + }, + title: selectedEpisode.title, + language: chosenAudios[0].language + }) : undefined + }).download(); + if(!videoDownload.ok){ + console.error(`DL Stats: ${JSON.stringify(videoDownload.parts)}\n`); + dlFailed = true; + } else { + if (chosenVideoSegments.pssh) { + console.info('Decryption Needed, attempting to decrypt'); + encryptionKeys = await getKeys(chosenVideoSegments.pssh, 'https://shield-drm.imggaming.com/api/v2/license', { + 'Authorization': `Bearer ${selectedEpisode.jwtToken}`, + 'X-Drm-Info': 'eyJzeXN0ZW0iOiJjb20ud2lkZXZpbmUuYWxwaGEifQ==', + }); + if (encryptionKeys.length == 0) { + console.error('Failed to get encryption keys'); + return undefined; + } + if (this.cfg.bin.mp4decrypt) { + const commandBase = `--show-progress --key ${encryptionKeys[1].kid}:${encryptionKeys[1].key} `; + const commandVideo = commandBase+`"${tsFile}.video.enc.ts" "${tsFile}.video.ts"`; + + console.info('Started decrypting video'); + const decryptVideo = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandVideo); + if (!decryptVideo.isOk) { + console.error(decryptVideo.err); + console.error(`Decryption failed with exit code ${decryptVideo.err.code}`); + return undefined; + } else { + console.info('Decryption done for video'); + if (!options.nocleanup) { + fs.removeSync(`${tsFile}.video.enc.ts`); + } + files.push({ + type: 'Video', + path: `${tsFile}.video.ts`, + lang: chosenAudios[0].language, + isPrimary: true + }); + } + } else { + console.warn('mp4decrypt not found, files need decryption. Decryption Keys:', encryptionKeys); + } + } + } + } else { + console.info('Skipping Video'); + } + + if (!options.noaudio) { + for (const audio of chosenAudios) { + const chosenAudioSegments = audio; + //Download Audio (if available) + const totalParts = chosenAudioSegments.segments.length; + const mathParts = Math.ceil(totalParts / options.partsize); + const mathMsg = `(${mathParts}*${options.partsize})`; + console.info('Total parts in audio stream:', totalParts, mathMsg); + const outFile = parseFileName(options.fileName + '.' + (chosenAudioSegments.language.name), variables, options.numbers, options.override).join(path.sep); + const tsFile = path.isAbsolute(outFile as string) ? outFile : path.join(this.cfg.dir.content, outFile); + const split = outFile.split(path.sep).slice(0, -1); + split.forEach((val, ind, arr) => { + const isAbsolut = path.isAbsolute(outFile as string); + if (!fs.existsSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val))) + fs.mkdirSync(path.join(isAbsolut ? '' : this.cfg.dir.content, ...arr.slice(0, ind), val)); + }); + const audioJson: M3U8Json = { + segments: chosenAudioSegments.segments + }; + const audioDownload = await new streamdl({ + output: `${tsFile}.audio.enc.ts`, + timeout: options.timeout, + m3u8json: audioJson, + // baseurl: chunkPlaylist.baseUrl, + threads: options.partsize, + fsRetryTime: options.fsRetryTime * 1000, + override: options.force, + callback: options.callbackMaker ? options.callbackMaker({ + fileName: `${path.isAbsolute(outFile) ? outFile.slice(this.cfg.dir.content.length) : outFile}`, + image: selectedEpisode.thumbnailUrl, + parent: { + title: selectedEpisode.seriesTitle + }, + title: selectedEpisode.title, + language: chosenAudioSegments.language + }) : undefined + }).download(); + if(!audioDownload.ok){ + console.error(`DL Stats: ${JSON.stringify(audioDownload.parts)}\n`); + dlFailed = true; + } + if (chosenAudioSegments.pssh) { + console.info('Decryption Needed, attempting to decrypt'); + if (!encryptionKeys) { + encryptionKeys = await getKeys(chosenVideoSegments.pssh, 'https://shield-drm.imggaming.com/api/v2/license', { + 'Authorization': `Bearer ${selectedEpisode.jwtToken}`, + 'X-Drm-Info': 'eyJzeXN0ZW0iOiJjb20ud2lkZXZpbmUuYWxwaGEifQ==', + }); + } + if (this.cfg.bin.mp4decrypt) { + const commandBase = `--show-progress --key ${encryptionKeys[1].kid}:${encryptionKeys[1].key} `; + const commandAudio = commandBase+`"${tsFile}.audio.enc.ts" "${tsFile}.audio.ts"`; + + console.info('Started decrypting audio'); + const decryptAudio = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandAudio); + if (!decryptAudio.isOk) { + console.error(decryptAudio.err); + console.error(`Decryption failed with exit code ${decryptAudio.err.code}`); + return undefined; + } else { + if (!options.nocleanup) { + fs.removeSync(`${tsFile}.audio.enc.ts`); + } + files.push({ + type: 'Audio', + path: `${tsFile}.audio.ts`, + lang: chosenAudioSegments.language, + isPrimary: chosenAudioSegments.default + }); + console.info('Decryption done for audio'); + } + } else { + console.warn('mp4decrypt not found, files need decryption. Decryption Keys:', encryptionKeys); + } + } + } + } else { + console.info('Skipping Audio'); + } + + if(options.dlsubs.indexOf('all') > -1){ + options.dlsubs = ['all']; + } + + if (options.nosubs) { + console.info('Subtitles downloading disabled from nosubs flag.'); + options.skipsubs = true; + } + + if(!options.skipsubs && options.dlsubs.indexOf('none') == -1) { + if(subs.length > 0) { + let subIndex = 0; + for(const sub of subs) { + const subLang = langsData.languages.find(a => a.new_hd_locale === sub.language); + if (!subLang) { + console.warn(`Language not found for subtitle language: ${sub.language}, Skipping`); + continue; + } + const sxData: Partial<sxItem> = {}; + sxData.file = langsData.subsFile(fileName as string, subIndex+'', subLang, false, options.ccTag); + sxData.path = path.join(this.cfg.dir.content, sxData.file); + sxData.language = subLang; + if(options.dlsubs.includes('all') || options.dlsubs.includes(subLang.locale)) { + /*const subs4XUrl = sub.url.split('/'); + const subsXUrl = subs4XUrl[subs4XUrl.length - 1].replace(/.vtt$/, ''); + const getCssContent = await this.req.getData(await this.genSubsUrl('css', subsXUrl)); + const getVttContent = await this.req.getData(await this.genSubsUrl('vtt', subsXUrl));*/ + const getVttContent = await this.req.getData(sub.url); + console.log(sub.url); + if (getVttContent.ok && getVttContent.res) { + //TODO: Get vtt2ass working with css format of new hidive app + /*const cssLines = []; + const cssGroups = getVttContent.res.body.matchAll(/::cue(?:.(.+)\))?{([^}]+)}/g); + for (const cssGroup of cssGroups) { + cssLines.push(`${cssGroup[1]} {${cssGroup[2]}}`); + }*/ + //vttConvert(getVttContent.res.body, false, subLang.name, fontSize); + //.replace(/#FFFF00/g, '#FFFFFF') + const sBody = getVttContent.res.body.replace(/yellow/g, '#FFFFFF'); + //const sBody = vtt(undefined, chosenFontSize, getVttContent.res.body, cssLines.join('\r\n'), subsMargin, options.fontName); + sxData.title = `${subLang.language} / ${sxData.title}`; + sxData.fonts = fontsData.assFonts(sBody) as Font[]; + fs.writeFileSync(sxData.path, sBody); + console.info(`Subtitle downloaded: ${sxData.file}`); + files.push({ + type: 'Subtitle', + ...sxData as sxItem, + cc: false + }); + } else{ + console.warn(`Failed to download subtitle: ${sxData.file}`); + } + } + subIndex++; + } + } else{ + console.warn('Can\'t find urls for subtitles!'); + } + } else{ + console.info('Subtitles downloading skipped!'); + } + + return { + error: dlFailed, + data: files, + fileName: fileName ? (path.isAbsolute(fileName) ? fileName : path.join(this.cfg.dir.content, fileName)) || './unknown' : './unknown' + }; + } + public async downloadMediaList(videoUrls: HidiveStreamInfo[], subUrls: HidiveSubtitleInfo[], fontSize: number, options: Record<any, any>) { let mediaName = '...'; let fileName; @@ -775,20 +1526,40 @@ export default class Hidive implements ServiceClass { }; } - public async muxStreams(data: DownloadedMedia[], options: Record<any, any>) { + public async muxStreams(data: DownloadedMedia[], options: Record<any, any>, inverseTrackOrder: boolean = true) { this.cfg.bin = await yamlCfg.loadBinCfg(); + let hasAudioStreams = false; if (options.novids || data.filter(a => a.type === 'Video').length === 0) return console.info('Skip muxing since no vids are downloaded'); + if (data.some(a => a.type === 'Audio')) { + hasAudioStreams = true; + } const merger = new Merger({ - onlyVid: [], + onlyVid: hasAudioStreams ? data.filter(a => a.type === 'Video').map((a) : MergerInput => { + if (a.type === 'Subtitle') + throw new Error('Never'); + return { + lang: a.lang, + path: a.path, + }; + }) : [], skipSubMux: options.skipSubMux, - inverseTrackOrder: true, + inverseTrackOrder: inverseTrackOrder, keepAllVideos: options.keepAllVideos, - onlyAudio: [], + onlyAudio: hasAudioStreams ? data.filter(a => a.type === 'Audio').map((a) : MergerInput => { + if (a.type === 'Subtitle') + throw new Error('Never'); + return { + lang: a.lang, + path: a.path, + }; + }) : [], output: `${options.output}.${options.mp4 ? 'mp4' : 'mkv'}`, subtitles: data.filter(a => a.type === 'Subtitle').map((a) : SubtitleInput => { if (a.type === 'Video') throw new Error('Never'); + if (a.type === 'Audio') + throw new Error('Never'); return { file: a.path, language: a.language, @@ -801,7 +1572,7 @@ export default class Hidive implements ServiceClass { return !a.uncut as boolean; })[0], fonts: Merger.makeFontsList(this.cfg.dir.fonts, data.filter(a => a.type === 'Subtitle') as sxItem[]), - videoAndAudio: data.filter(a => a.type === 'Video').map((a) : MergerInput => { + videoAndAudio: hasAudioStreams ? [] : data.filter(a => a.type === 'Video').map((a) : MergerInput => { if (a.type === 'Subtitle') throw new Error('Never'); return { diff --git a/modules/module.api-urls.ts b/modules/module.api-urls.ts index 603f25b..6dffd45 100644 --- a/modules/module.api-urls.ts +++ b/modules/module.api-urls.ts @@ -7,7 +7,8 @@ const domain = { www_beta: 'https://beta.crunchyroll.com', api_beta: 'https://beta-api.crunchyroll.com', hd_www: 'https://www.hidive.com', - hd_api: 'https://api.hidive.com' + hd_api: 'https://api.hidive.com', + hd_new: 'https://dce-frontoffice.imggaming.com' }; export type APIType = { @@ -41,6 +42,9 @@ export type APIType = { hd_clientWeb: string, hd_clientExo: string, hd_api: string, + hd_new_api: string, + hd_new_apiKey: string, + hd_new_version: string, } // api urls @@ -77,6 +81,10 @@ const api: APIType = { hd_clientWeb: 'okhttp/3.4.1', hd_clientExo: 'smartexoplayer/1.6.0.R (Linux;Android 6.0) ExoPlayerLib/2.6.0', hd_api: `${domain.hd_api}/api/v1`, + //Hidive New API + hd_new_api: `${domain.hd_new}/api`, + hd_new_apiKey: '857a1e5d-e35e-4fdf-805b-a87b6f8364bf', + hd_new_version: '6.0.1.bbf09a2' }; // set header diff --git a/modules/module.app-args.ts b/modules/module.app-args.ts index a06d0ae..7ff8c6d 100644 --- a/modules/module.app-args.ts +++ b/modules/module.app-args.ts @@ -66,6 +66,7 @@ let argvC: { dlVideoOnce: boolean; chapters: boolean; crapi: 'android' | 'web'; + hdapi: 'old' | 'new'; removeBumpers: boolean; originalFontSize: boolean; keepAllVideos: boolean; diff --git a/modules/module.args.ts b/modules/module.args.ts index ffedb2d..b287fa7 100644 --- a/modules/module.args.ts +++ b/modules/module.args.ts @@ -230,6 +230,20 @@ const args: TAppArg<boolean|number|string|unknown[]>[] = [ default: 'android' } }, + { + name: 'hdapi', + describe: 'Selects the API type for Hidive', + type: 'string', + group: 'dl', + service: ['hidive'], + docDescribe: 'If set to Old, it has lower quality, but Non-DRM streams, but some people can\'t use it,' + + '\nIf set to New, it has a higher quality stream, but everything is DRM.', + usage: '', + choices: ['old', 'new'], + default: { + default: 'old' + } + }, { name: 'removeBumpers', describe: 'Remove bumpers from final video', diff --git a/modules/module.cfg-loader.ts b/modules/module.cfg-loader.ts index b488500..6f39d8c 100644 --- a/modules/module.cfg-loader.ts +++ b/modules/module.cfg-loader.ts @@ -26,7 +26,8 @@ const stateFile = path.join(workingDir, 'config', 'guistate'); const tokenFile = { funi: path.join(workingDir, 'config', 'funi_token'), cr: path.join(workingDir, 'config', 'cr_token'), - hd: path.join(workingDir, 'config', 'hd_token') + hd: path.join(workingDir, 'config', 'hd_token'), + hdNew: path.join(workingDir, 'config', 'hd_new_token') }; export const ensureConfig = () => { @@ -242,7 +243,7 @@ const saveHDSession = (data: Record<string, unknown>) => { const loadHDToken = () => { - let token = loadYamlCfgFile(tokenFile.cr, true); + let token = loadYamlCfgFile(tokenFile.hd, true); if(typeof token !== 'object' || token === null || Array.isArray(token)){ token = {}; } @@ -292,6 +293,25 @@ const loadHDProfile = () => { return profile; }; +const loadNewHDToken = () => { + let token = loadYamlCfgFile(tokenFile.hdNew, true); + if(typeof token !== 'object' || token === null || Array.isArray(token)){ + token = {}; + } + return token; +}; + +const saveNewHDToken = (data: Record<string, unknown>) => { + const cfgFolder = path.dirname(tokenFile.hdNew); + try{ + fs.ensureDirSync(cfgFolder); + fs.writeFileSync(`${tokenFile.hdNew}.yml`, yaml.stringify(data)); + } + catch(e){ + console.error('Can\'t save token file to disk!'); + } +}; + const loadFuniToken = () => { const loadedToken = loadYamlCfgFile<{ token?: string @@ -363,6 +383,8 @@ export { loadHDSession, saveHDToken, loadHDToken, + saveNewHDToken, + loadNewHDToken, saveHDProfile, loadHDProfile, getState, diff --git a/modules/module.langsData.ts b/modules/module.langsData.ts index e6453ef..56ca413 100644 --- a/modules/module.langsData.ts +++ b/modules/module.langsData.ts @@ -3,6 +3,7 @@ export type LanguageItem = { cr_locale?: string, hd_locale?: string, + new_hd_locale?: string, locale: string, code: string, name: string, @@ -13,12 +14,12 @@ export type LanguageItem = { } const languages: LanguageItem[] = [ - { cr_locale: 'en-US', hd_locale: 'English', funi_locale: 'enUS', locale: 'en', code: 'eng', name: 'English' }, + { cr_locale: 'en-US', new_hd_locale: 'en-US', hd_locale: 'English', funi_locale: 'enUS', locale: 'en', code: 'eng', name: 'English' }, { cr_locale: 'en-IN', locale: 'en-IN', code: 'eng', name: 'English (India)', }, - { cr_locale: 'es-LA', hd_locale: 'Spanish LatAm', funi_name: 'Spanish (LAS)', funi_name_lagacy: 'Spanish (Latin Am)', funi_locale: 'esLA', locale: 'es-419', code: 'spa', name: 'Spanish', language: 'Latin American Spanish' }, + { cr_locale: 'es-LA', new_hd_locale: 'es-MX', hd_locale: 'Spanish LatAm', funi_name: 'Spanish (LAS)', funi_name_lagacy: 'Spanish (Latin Am)', funi_locale: 'esLA', locale: 'es-419', code: 'spa', name: 'Spanish', language: 'Latin American Spanish' }, { cr_locale: 'es-419',hd_locale: 'Spanish', locale: 'es-419', code: 'spa-419', name: 'Spanish', language: 'Latin American Spanish' }, { cr_locale: 'es-ES', hd_locale: 'Spanish Europe', locale: 'es-ES', code: 'spa-ES', name: 'Castilian', language: 'European Spanish' }, - { cr_locale: 'pt-BR', hd_locale: 'Portuguese', funi_name: 'Portuguese (Brazil)', funi_locale: 'ptBR', locale: 'pt-BR', code: 'por', name: 'Portuguese', language: 'Brazilian Portuguese' }, + { cr_locale: 'pt-BR', new_hd_locale: 'pt-BR', hd_locale: 'Portuguese', funi_name: 'Portuguese (Brazil)', funi_locale: 'ptBR', locale: 'pt-BR', code: 'por', name: 'Portuguese', language: 'Brazilian Portuguese' }, { cr_locale: 'pt-PT', locale: 'pt-PT', code: 'por', name: 'Portuguese (Portugal)', language: 'Portugues (Portugal)' }, { cr_locale: 'fr-FR', hd_locale: 'French', locale: 'fr', code: 'fra', name: 'French' }, { cr_locale: 'de-DE', hd_locale: 'German', locale: 'de', code: 'deu', name: 'German' }, diff --git a/modules/module.req.ts b/modules/module.req.ts index 31dbcc4..232abe0 100644 --- a/modules/module.req.ts +++ b/modules/module.req.ts @@ -61,7 +61,9 @@ class Req { options.headers = {...options.headers, ...params.headers}; } if(options.method == 'POST'){ - (options.headers as Headers)['Content-Type'] = 'application/x-www-form-urlencoded'; + if (!(options.headers as Headers)['Content-Type']) { + (options.headers as Headers)['Content-Type'] = 'application/x-www-form-urlencoded'; + } } if(params.body){ options.body = params.body; diff --git a/modules/module.transform-mpd.ts b/modules/module.transform-mpd.ts index b3c5419..a56a42c 100644 --- a/modules/module.transform-mpd.ts +++ b/modules/module.transform-mpd.ts @@ -1,5 +1,5 @@ -import { Playlist, parse as mpdParse } from 'mpd-parser'; -import { LanguageItem } from './module.langsData'; +import { parse as mpdParse } from 'mpd-parser'; +import { LanguageItem, findLang, languages } from './module.langsData'; type Segment = { uri: string; @@ -20,7 +20,8 @@ export type PlaylistItem = { type AudioPlayList = { - language: LanguageItem + language: LanguageItem, + default: boolean } & PlaylistItem type VideoPlayList = { @@ -37,9 +38,9 @@ export type MPDParsed = { } } -export function parse(manifest: string, language: LanguageItem, url?: string) { +export function parse(manifest: string, language?: LanguageItem, url?: string) { if (!manifest.includes('BaseURL') && url) { - manifest = manifest.replace(/(<MPD[^]^[^]*?>)/gm, `$1<BaseURL>${url}</BaseURL>`); + manifest = manifest.replace(/(<MPD*\b[^>]*>)/gm, `$1<BaseURL>${url}</BaseURL>`); } const parsed = mpdParse(manifest); const ret: MPDParsed = {}; @@ -50,9 +51,18 @@ export function parse(manifest: string, language: LanguageItem, url?: string) { if (!Object.prototype.hasOwnProperty.call(ret, host)) ret[host] = { audio: [], video: [] }; + //Find and add audio language if it is found in the MPD + let audiolang: LanguageItem; + const foundlanguage = findLang(languages.find(a => a.code === item.language)?.cr_locale ?? 'unknown'); + if (item.language) { + audiolang = foundlanguage; + } else { + audiolang = language ? language : foundlanguage; + } const pItem: AudioPlayList = { bandwidth: playlist.attributes.BANDWIDTH, - language: language, + language: audiolang, + default: item.default, segments: playlist.segments.map((segment): Segment => { const uri = segment.resolvedUri; const map_uri = segment.map.resolvedUri; diff --git a/package.json b/package.json index 2d19b40..a403b93 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "multi-downloader-nx", "short_name": "aniDL", - "version": "4.5.0", + "version": "4.5.0rc2", "description": "Downloader for Crunchyroll, Funimation, or Hidive via CLI or GUI", "keywords": [ "download", From f3fd33d241996a9d0753787568dd11ab35633c99 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 17:45:54 -0700 Subject: [PATCH 53/69] Add warning for decryption --- crunchy.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crunchy.ts b/crunchy.ts index d6ea96f..af1f009 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -1373,6 +1373,10 @@ export default class Crunchy implements ServiceClass { let hsLangs: string[] = []; const pbStreams = pbData.data[0]; + if (!canDecrypt) { + console.warn('Decryption not enabled!'); + } + for(const s of Object.keys(pbStreams)){ if ( (s.match(/hls/) || s.match(/dash/)) From 211a593796f4befe9cfa33c2ffa21054c9070536 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 17:58:48 -0700 Subject: [PATCH 54/69] [HD] Move -s to --srz, and add warnings Add warnings for commands that haven't yet been added to hidive, and move -s to --srz since it selects a series, and not a season. --- hidive.ts | 34 +++++++++++++++++++++------------- modules/module.app-args.ts | 3 ++- modules/module.args.ts | 3 +-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/hidive.ts b/hidive.ts index 933f30c..f994193 100644 --- a/hidive.ts +++ b/hidive.ts @@ -134,24 +134,32 @@ export default class Hidive implements ServiceClass { } } } else { - const selected = await this.selectSeries(parseInt(argv.s), argv.e, argv.but, argv.all); - if (selected.isOk && selected.showData) { - for (const select of selected.value) { - //download episode - if (!(await this.downloadEpisode(select, {...argv}))) { - console.error(`Unable to download selected episode ${select.episodeInformation.episodeNumber}`); - return false; - } + console.error('-s is not yet implemented in the new API, use --srz instead'); + } + return true; + } else if (argv.srz && !isNaN(parseInt(argv.srz,10)) && parseInt(argv.srz,10) > 0) { + const selected = await this.selectSeries(parseInt(argv.srz), argv.e, argv.but, argv.all); + if (selected.isOk && selected.showData) { + for (const select of selected.value) { + //download episode + if (!(await this.downloadEpisode(select, {...argv}))) { + console.error(`Unable to download selected episode ${select.episodeInformation.episodeNumber}`); + return false; } } } - return true; } else if (argv.new) { + if (this.api == 'old') { //Initilize session - await this.doInit(); - //Get Newly Added - await this.getNewlyAdded(argv.page); - } else { + await this.doInit(); + //Get Newly Added + await this.getNewlyAdded(argv.page); + } else { + console.error('--new is not yet implemented in the new API'); + } + } else if(argv.e) { + console.error('-e is not yet supported'); + }else { console.info('No option selected or invalid value entered. Try --help.'); } } diff --git a/modules/module.app-args.ts b/modules/module.app-args.ts index 7ff8c6d..7e7629e 100644 --- a/modules/module.app-args.ts +++ b/modules/module.app-args.ts @@ -31,7 +31,8 @@ let argvC: { new: boolean | undefined; 'movie-listing': string | undefined; series: string | undefined; - s: string | undefined; + s: string | undefined; + srz: string | undefined; e: string | undefined; extid: string | undefined; q: number; diff --git a/modules/module.args.ts b/modules/module.args.ts index b287fa7..3540483 100644 --- a/modules/module.args.ts +++ b/modules/module.args.ts @@ -138,8 +138,7 @@ const args: TAppArg<boolean|number|string|unknown[]>[] = [ group: 'dl', alias: 'srz', describe: 'Get season list by series ID', - docDescribe: 'This command is used only for crunchyroll.' - + '\n Requested is the ID of a show not a season.', + docDescribe: 'Requested is the ID of a show not a season.', service: ['crunchy'], type: 'string', usage: '${ID}' From 824154594d090b29912ad7df638eaa9f89e252a2 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 19:50:33 -0700 Subject: [PATCH 55/69] [HD] Add -s support to new hidive API --- @types/newHidiveSeason.d.ts | 4 ++ hidive.ts | 85 ++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/@types/newHidiveSeason.d.ts b/@types/newHidiveSeason.d.ts index 5d59c29..7ae7d04 100644 --- a/@types/newHidiveSeason.d.ts +++ b/@types/newHidiveSeason.d.ts @@ -74,6 +74,10 @@ export interface Series { contentRating: Rating; } +export interface NewHidiveSeriesExtra extends Series { + season: NewHidiveSeason; +} + export interface NewHidiveEpisodeExtra extends Episode { titleId: number; nameLong: string; diff --git a/hidive.ts b/hidive.ts index f994193..b5ea66b 100644 --- a/hidive.ts +++ b/hidive.ts @@ -36,7 +36,7 @@ import { HidiveSearch } from './@types/hidiveSearch'; import { HidiveDashboard } from './@types/hidiveDashboard'; import { Hit, NewHidiveSearch } from './@types/newHidiveSearch'; import { NewHidiveSeries } from './@types/newHidiveSeries'; -import { Episode, NewHidiveEpisodeExtra, NewHidiveSeason } from './@types/newHidiveSeason'; +import { Episode, NewHidiveEpisodeExtra, NewHidiveSeason, NewHidiveSeriesExtra } from './@types/newHidiveSeason'; import { NewHidiveEpisode } from './@types/newHidiveEpisode'; import { NewHidivePlayback, Subtitle } from './@types/newHidivePlayback'; import { MPDParsed, parse } from './modules/module.transform-mpd'; @@ -134,7 +134,16 @@ export default class Hidive implements ServiceClass { } } } else { - console.error('-s is not yet implemented in the new API, use --srz instead'); + const selected = await this.selectSeason(parseInt(argv.s), argv.e, argv.but, argv.all); + if (selected.isOk && selected.showData) { + for (const select of selected.value) { + //download episode + if (!(await this.downloadEpisode(select, {...argv}))) { + console.error(`Unable to download selected episode ${select.episodeInformation.episodeNumber}`); + return false; + } + } + } } return true; } else if (argv.srz && !isNaN(parseInt(argv.srz,10)) && parseInt(argv.srz,10) > 0) { @@ -694,6 +703,34 @@ export default class Hidive implements ServiceClass { return { isOk: false }; } + public async listSeason(id: number) { + const season = await this.getSeason(id); + if (!season.isOk || !season.value) { + console.error('Failed to list series data: Failed to get season '+id); + return { isOk: false }; + } + console.info(` [S.${season.value.id}] ${season.value.title} (${season.value.episodeCount} Episodes)`); + while (season.value.paging.moreDataAvailable) { + const seasonPage = await this.getSeason(id, season.value.paging.lastSeen); + if (!seasonPage.isOk || !seasonPage.value) break; + season.value.episodes = season.value.episodes.concat(seasonPage.value.episodes); + season.value.paging.lastSeen = seasonPage.value.paging.lastSeen; + season.value.paging.moreDataAvailable = seasonPage.value.paging.moreDataAvailable; + } + const episodes: Episode[] = []; + for (const episode of season.value.episodes) { + if (episode.title.includes(' - ')) { + episode.episodeInformation.episodeNumber = parseFloat(episode.title.split(' - ')[0].replace('E', '')); + episode.title = episode.title.split(' - ')[1]; + } + //S${episode.episodeInformation.seasonNumber}E${episode.episodeInformation.episodeNumber} - + episodes.push(episode); + console.info(` [E.${episode.id}] ${episode.title}`); + } + const series: NewHidiveSeriesExtra = {...season.value.series, season: season.value}; + return { isOk: true, value: episodes, series: series }; + } + /** * Lists the requested series, and returns the selected episodes * @param id Series ID @@ -738,6 +775,50 @@ export default class Hidive implements ServiceClass { return { isOk: true, value: selEpsArr, showData: getShowData.series }; } + /** + * Lists the requested season, and returns the selected episodes + * @param id Season ID + * @param e Selector + * @param but Download all but selected videos + * @param all Whether to download all available videos + * @returns + */ + public async selectSeason(id: number, e: string | undefined, but: boolean, all: boolean) { + const getShowData = await this.listSeason(id); + if (!getShowData.isOk || !getShowData.value) { + return { isOk: false, value: [] }; + } + const showData = getShowData.value; + const doEpsFilter = parseSelect(e as string); + // build selected episodes + const selEpsArr: NewHidiveEpisodeExtra[] = []; let ovaSeq = 1; let movieSeq = 1; + for (let i = 0; i < showData.length; i++) { + const titleId = showData[i].id; + const seriesTitle = getShowData.series.title; + const seasonTitle = getShowData.series.season.title; + let nameLong = showData[i].title; + if (nameLong.match(/OVA/i)) { + nameLong = 'ova' + (('0' + ovaSeq).slice(-2)); ovaSeq++; + } else if (nameLong.match(/Theatrical/i)) { + nameLong = 'movie' + (('0' + movieSeq).slice(-2)); movieSeq++; + } + let selMark = ''; + if (all || + but && !doEpsFilter.isSelected([parseFloat(showData[i].episodeInformation.episodeNumber+'')+'', showData[i].id+'']) || + !but && doEpsFilter.isSelected([parseFloat(showData[i].episodeInformation.episodeNumber+'')+'', showData[i].id+'']) + ) { + selEpsArr.push({ isSelected: true, titleId, nameLong, seasonTitle, seriesTitle, ...showData[i] }); + selMark = '✓ '; + } + console.info('%s[%s] %s', + selMark, + 'S'+parseFloat(showData[i].episodeInformation.seasonNumber+'')+'E'+parseFloat(showData[i].episodeInformation.episodeNumber+''), + showData[i].title, + ); + } + return { isOk: true, value: selEpsArr, showData: getShowData.series }; + } + public async listShow(id: number) { const getShowData = await this.reqData('GetTitle', { 'Id': id }); if (!getShowData.ok || !getShowData.res) { From c7addc1c1aafc17f2831227921812f54f075b9f2 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 19:58:47 -0700 Subject: [PATCH 56/69] Formatting fixes --- hidive.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hidive.ts b/hidive.ts index b5ea66b..33a91d4 100644 --- a/hidive.ts +++ b/hidive.ts @@ -120,13 +120,13 @@ export default class Hidive implements ServiceClass { await this.doSearch({ ...argv, search: argv.search as string }); } else if (argv.s && !isNaN(parseInt(argv.s,10)) && parseInt(argv.s,10) > 0) { if (this.api == 'old') { - //Initilize session + //Initilize session await this.doInit(); //get selected episodes const selected = await this.getShow(parseInt(argv.s), argv.e, argv.but, argv.all); if (selected.isOk && selected.showData) { for (const select of selected.value) { - //download episode + //download episode if (!(await this.getEpisode(select, {...argv}))) { console.error(`Unable to download selected episode ${parseFloat(select.EpisodeNumberValue+'')}`); return false; @@ -150,7 +150,7 @@ export default class Hidive implements ServiceClass { const selected = await this.selectSeries(parseInt(argv.srz), argv.e, argv.but, argv.all); if (selected.isOk && selected.showData) { for (const select of selected.value) { - //download episode + //download episode if (!(await this.downloadEpisode(select, {...argv}))) { console.error(`Unable to download selected episode ${select.episodeInformation.episodeNumber}`); return false; @@ -159,7 +159,7 @@ export default class Hidive implements ServiceClass { } } else if (argv.new) { if (this.api == 'old') { - //Initilize session + //Initilize session await this.doInit(); //Get Newly Added await this.getNewlyAdded(argv.page); From 34fa052bc2ece8ec5db5fa4fa88c3bd2d21008be Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 21:20:04 -0700 Subject: [PATCH 57/69] Modify vtt2ass to work with new hidive API Modifies vtt2ass to work with the new hidive API --- hidive.ts | 29 +++++++++++++---------------- modules/module.vtt2ass.ts | 13 ++++++++++++- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/hidive.ts b/hidive.ts index 33a91d4..13583e2 100644 --- a/hidive.ts +++ b/hidive.ts @@ -1030,9 +1030,8 @@ export default class Hidive implements ServiceClass { const files: DownloadedMedia[] = []; const variables: Variable[] = []; let dlFailed = false; - /*const subsMargin = 0; - const videoIndex = 0; - const chosenFontSize = options.fontSize;*/ + const subsMargin = 0; + const chosenFontSize = options.fontSize; let encryptionKeys: KeyContainer[] | undefined = undefined; if (!canDecrypt) console.warn('Decryption not enabled!'); @@ -1316,27 +1315,25 @@ export default class Hidive implements ServiceClass { sxData.path = path.join(this.cfg.dir.content, sxData.file); sxData.language = subLang; if(options.dlsubs.includes('all') || options.dlsubs.includes(subLang.locale)) { - /*const subs4XUrl = sub.url.split('/'); - const subsXUrl = subs4XUrl[subs4XUrl.length - 1].replace(/.vtt$/, ''); - const getCssContent = await this.req.getData(await this.genSubsUrl('css', subsXUrl)); - const getVttContent = await this.req.getData(await this.genSubsUrl('vtt', subsXUrl));*/ const getVttContent = await this.req.getData(sub.url); - console.log(sub.url); if (getVttContent.ok && getVttContent.res) { - //TODO: Get vtt2ass working with css format of new hidive app - /*const cssLines = []; + console.info(`Subtitle Downloaded: ${sub.url}`); + const cssLines = []; const cssGroups = getVttContent.res.body.matchAll(/::cue(?:.(.+)\))?{([^}]+)}/g); + let defaultCss = ''; for (const cssGroup of cssGroups) { - cssLines.push(`${cssGroup[1]} {${cssGroup[2]}}`); - }*/ + if (cssGroup[1]) { + cssLines.push(`${cssGroup[1]}{${defaultCss}${cssGroup[2]}}`); + } else { + defaultCss = cssGroup[2]; + } + } //vttConvert(getVttContent.res.body, false, subLang.name, fontSize); - //.replace(/#FFFF00/g, '#FFFFFF') - const sBody = getVttContent.res.body.replace(/yellow/g, '#FFFFFF'); - //const sBody = vtt(undefined, chosenFontSize, getVttContent.res.body, cssLines.join('\r\n'), subsMargin, options.fontName); + const sBody = vtt(undefined, chosenFontSize, getVttContent.res.body, cssLines.join('\r\n'), subsMargin, options.fontName); sxData.title = `${subLang.language} / ${sxData.title}`; sxData.fonts = fontsData.assFonts(sBody) as Font[]; fs.writeFileSync(sxData.path, sBody); - console.info(`Subtitle downloaded: ${sxData.file}`); + console.info(`Subtitle converted: ${sxData.file}`); files.push({ type: 'Subtitle', ...sxData as sxItem, diff --git a/modules/module.vtt2ass.ts b/modules/module.vtt2ass.ts index a661fa3..86b24f1 100644 --- a/modules/module.vtt2ass.ts +++ b/modules/module.vtt2ass.ts @@ -261,6 +261,7 @@ function convert(css: Css, vtt: Vtt[]) { song_cap: [], }; const linesMap: Record<string, number> = {}; + let previousLine: ReturnType<typeof convertLine> | undefined = undefined; for (const l in vtt) { const x = convertLine(stylesMap, vtt[l]); if (x.ind !== '' && linesMap[x.ind] !== undefined) { @@ -278,7 +279,17 @@ function convert(css: Css, vtt: Vtt[]) { linesMap[x.ind] = events[x.type as keyof typeof events].length - 1; } } - + /** + * What cursed code have I brought upon this land? + * This checks if a subtitle should be multi-line, and if it is, pops the just inserted + * subtitle and the previous subtitle, and merges them into a single subtitle. + */ + if (previousLine?.start == x.start && previousLine.type == x.type && previousLine.style == x.style) { + events[x.type as keyof typeof events].pop(); + const previousLinePop = events[x.type as keyof typeof events].pop(); + events[x.type as keyof typeof events].push(previousLinePop + '\\N'+x.text); + } + previousLine = x; } if (events.subtitle.length > 0) { ass = ass.concat( From 48d8a276d7f753c55a91244a046ccca3831bd818 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sat, 16 Mar 2024 22:26:56 -0700 Subject: [PATCH 58/69] [HD] Fix fontsize for vtt2ass for new API --- hidive.ts | 6 +++++- modules/module.vtt2ass.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hidive.ts b/hidive.ts index 13583e2..713f36a 100644 --- a/hidive.ts +++ b/hidive.ts @@ -1031,7 +1031,7 @@ export default class Hidive implements ServiceClass { const variables: Variable[] = []; let dlFailed = false; const subsMargin = 0; - const chosenFontSize = options.fontSize; + const chosenFontSize = options.originalFontSize ? undefined : options.fontSize; let encryptionKeys: KeyContainer[] | undefined = undefined; if (!canDecrypt) console.warn('Decryption not enabled!'); @@ -1322,6 +1322,10 @@ export default class Hidive implements ServiceClass { const cssGroups = getVttContent.res.body.matchAll(/::cue(?:.(.+)\))?{([^}]+)}/g); let defaultCss = ''; for (const cssGroup of cssGroups) { + //Below code will bulldoze defined sizes for custom ones + /*if (!options.originalFontSize) { + cssGroup[2] = cssGroup[2].replace(/( font-size:.+?;)/g, '').replace(/(font-size:.+?;)/g, ''); + }*/ if (cssGroup[1]) { cssLines.push(`${cssGroup[1]}{${defaultCss}${cssGroup[2]}}`); } else { diff --git a/modules/module.vtt2ass.ts b/modules/module.vtt2ass.ts index 86b24f1..648b539 100644 --- a/modules/module.vtt2ass.ts +++ b/modules/module.vtt2ass.ts @@ -69,7 +69,7 @@ function loadCSS(cssStr: string): Css { function parseStyle(stylegroup: string, line: string, style: any) { const defaultSFont = rFont == '' ? defaultStyleFont : rFont; //redeclare cause of let - if (stylegroup.startsWith('Subtitle') || stylegroup.startsWith('Song')) { //base for dialog, everything else use defaultStyle + if (stylegroup.startsWith('Subtitle') || stylegroup.startsWith('Song') || stylegroup.startsWith('Q')) { //base for dialog, everything else use defaultStyle style = `${defaultSFont},${fontSize},&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2.6,0,2,20,20,46,1`; } From c8b39301cb34c8be41da1722561d96ad44f717d3 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 10:48:32 -0700 Subject: [PATCH 59/69] [CR] Fix issue with finding all episodes Fixes issue where under certain circumstances some specials wouldn't be found. --- crunchy.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index af1f009..67db123 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -2162,13 +2162,13 @@ export default class Crunchy implements ServiceClass { //Prepare the episode array let item; const seasonIdentifier = s.identifier ? s.identifier.split('|')[1] : `S${episode.season_number}`; - if (!(Object.prototype.hasOwnProperty.call(episodes, `${seasonIdentifier}E${episode.episode_number || episode.episode}`))) { - item = episodes[`${seasonIdentifier}E${episode.episode_number || episode.episode}`] = { + if (!(Object.prototype.hasOwnProperty.call(episodes, `${seasonIdentifier}E${episode.episode || episode.episode_number}`))) { + item = episodes[`${seasonIdentifier}E${episode.episode || episode.episode_number}`] = { items: [] as CrunchyEpisode[], langs: [] as langsData.LanguageItem[] }; } else { - item = episodes[`${seasonIdentifier}E${episode.episode_number || episode.episode}`]; + item = episodes[`${seasonIdentifier}E${episode.episode || episode.episode_number}`]; } if (episode.versions) { From 12780eec2625561f43f862ea0d80ca4030d8a593 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 10:54:54 -0700 Subject: [PATCH 60/69] Only apply fontSize to normal subtitles --- modules/module.vtt2ass.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/module.vtt2ass.ts b/modules/module.vtt2ass.ts index 648b539..09aed87 100644 --- a/modules/module.vtt2ass.ts +++ b/modules/module.vtt2ass.ts @@ -69,7 +69,7 @@ function loadCSS(cssStr: string): Css { function parseStyle(stylegroup: string, line: string, style: any) { const defaultSFont = rFont == '' ? defaultStyleFont : rFont; //redeclare cause of let - if (stylegroup.startsWith('Subtitle') || stylegroup.startsWith('Song') || stylegroup.startsWith('Q')) { //base for dialog, everything else use defaultStyle + if (stylegroup.startsWith('Subtitle') || stylegroup.startsWith('Song') || stylegroup.startsWith('Q0')) { //base for dialog, everything else use defaultStyle style = `${defaultSFont},${fontSize},&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2.6,0,2,20,20,46,1`; } From a61d7d315d082e3428844636beb6997ac1760987 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 11:00:50 -0700 Subject: [PATCH 61/69] Make widevine folder during compilation --- modules/build.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/build.ts b/modules/build.ts index 9161b25..9901ce9 100644 --- a/modules/build.ts +++ b/modules/build.ts @@ -58,6 +58,7 @@ async function buildBinary(buildType: BuildTypes, gui: boolean) { } fs.mkdirSync(`${buildDir}/config`); fs.mkdirSync(`${buildDir}/videos`); + fs.mkdirSync(`${buildDir}/widevine`); fs.copySync('./config/bin-path.yml', `${buildDir}/config/bin-path.yml`); fs.copySync('./config/cli-defaults.yml', `${buildDir}/config/cli-defaults.yml`); fs.copySync('./config/dir-path.yml', `${buildDir}/config/dir-path.yml`); From 5b134978c7949c4fbf07fe6e93d8118091bf4165 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 11:10:05 -0700 Subject: [PATCH 62/69] Add to the ignored items for compilation --- tsc.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tsc.ts b/tsc.ts index a62dc55..8b2eedd 100644 --- a/tsc.ts +++ b/tsc.ts @@ -34,6 +34,7 @@ const ignore = [ './config/updates.json$', './config/cr_token.yml$', './config/funi_token.yml$', + './config/new_hd_token.yml$', './config/hd_token.yml$', './config/hd_sess.yml$', './config/hd_profile.yml$', @@ -42,7 +43,10 @@ const ignore = [ './fonts*', './gui/react*', './dev.js$', - '*/node_modules/*' + '*/node_modules/*', + './widevine/*', + './videos/*', + './logs/*', ].map(a => a.replace(/\*/g, '[^]*').replace(/\.\//g, escapeRegExp(__dirname) + '/').replace(/\//g, path.sep === '\\' ? '\\\\' : '/')).map(a => new RegExp(a, 'i')); export { ignore }; From 9645f6255bb8da810e2cabf7d2818dbbf703b2cc Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 11:24:52 -0700 Subject: [PATCH 63/69] Remove unneeded console log --- hidive.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/hidive.ts b/hidive.ts index 713f36a..e80a4dd 100644 --- a/hidive.ts +++ b/hidive.ts @@ -601,7 +601,6 @@ export default class Hidive implements ServiceClass { } } return { isOk: true, value: searchItems.filter(a => a.type == 'VOD_SERIES').flatMap((a): SearchResponseItem => { - console.info(a); return { id: a.id+'', image: a.coverUrl ?? '/notFound.png', From 7d62224d1143efff34f4514632c9dc4a70f70606 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 11:38:23 -0700 Subject: [PATCH 64/69] [HD] Update subtitle messages for old api --- hidive.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hidive.ts b/hidive.ts index e80a4dd..e994290 100644 --- a/hidive.ts +++ b/hidive.ts @@ -1584,12 +1584,13 @@ export default class Hidive implements ServiceClass { const getCssContent = await this.req.getData(await this.genSubsUrl('css', subsXUrl)); const getVttContent = await this.req.getData(await this.genSubsUrl('vtt', subsXUrl)); if (getCssContent.ok && getVttContent.ok && getCssContent.res && getVttContent.res) { + console.info(`Subtitle Downloaded: ${await this.genSubsUrl('vtt', subsXUrl)}`); //vttConvert(getVttContent.res.body, false, subLang.name, fontSize); const sBody = vtt(undefined, chosenFontSize, getVttContent.res.body, getCssContent.res.body, subsMargin, options.fontName); sxData.title = `${subLang.language} / ${sxData.title}`; sxData.fonts = fontsData.assFonts(sBody) as Font[]; fs.writeFileSync(sxData.path, sBody); - console.info(`Subtitle downloaded: ${sxData.file}`); + console.info(`Subtitle Converted: ${sxData.file}`); files.push({ type: 'Subtitle', ...sxData as sxItem, From 49519f1e176c541abe340e986aef5e14bf4c31a2 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 11:59:58 -0700 Subject: [PATCH 65/69] [HD] fontSize for q1 as well --- modules/module.vtt2ass.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/module.vtt2ass.ts b/modules/module.vtt2ass.ts index 09aed87..004f08b 100644 --- a/modules/module.vtt2ass.ts +++ b/modules/module.vtt2ass.ts @@ -69,7 +69,7 @@ function loadCSS(cssStr: string): Css { function parseStyle(stylegroup: string, line: string, style: any) { const defaultSFont = rFont == '' ? defaultStyleFont : rFont; //redeclare cause of let - if (stylegroup.startsWith('Subtitle') || stylegroup.startsWith('Song') || stylegroup.startsWith('Q0')) { //base for dialog, everything else use defaultStyle + if (stylegroup.startsWith('Subtitle') || stylegroup.startsWith('Song') || stylegroup.startsWith('Q0') || stylegroup.startsWith('Q1')) { //base for dialog, everything else use defaultStyle style = `${defaultSFont},${fontSize},&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2.6,0,2,20,20,46,1`; } From becaed79d1aca8da67f57714fff47875c72d0062 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 13:49:19 -0700 Subject: [PATCH 66/69] Migrate ::cue code to vtt2ass This should allow for generic styles in vtt files to be parsed and converted to an ASS file --- hidive.ts | 16 +--------------- modules/module.vtt2ass.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/hidive.ts b/hidive.ts index e994290..1282c95 100644 --- a/hidive.ts +++ b/hidive.ts @@ -1317,22 +1317,8 @@ export default class Hidive implements ServiceClass { const getVttContent = await this.req.getData(sub.url); if (getVttContent.ok && getVttContent.res) { console.info(`Subtitle Downloaded: ${sub.url}`); - const cssLines = []; - const cssGroups = getVttContent.res.body.matchAll(/::cue(?:.(.+)\))?{([^}]+)}/g); - let defaultCss = ''; - for (const cssGroup of cssGroups) { - //Below code will bulldoze defined sizes for custom ones - /*if (!options.originalFontSize) { - cssGroup[2] = cssGroup[2].replace(/( font-size:.+?;)/g, '').replace(/(font-size:.+?;)/g, ''); - }*/ - if (cssGroup[1]) { - cssLines.push(`${cssGroup[1]}{${defaultCss}${cssGroup[2]}}`); - } else { - defaultCss = cssGroup[2]; - } - } //vttConvert(getVttContent.res.body, false, subLang.name, fontSize); - const sBody = vtt(undefined, chosenFontSize, getVttContent.res.body, cssLines.join('\r\n'), subsMargin, options.fontName); + const sBody = vtt(undefined, chosenFontSize, getVttContent.res.body, '', subsMargin, options.fontName); sxData.title = `${subLang.language} / ${sxData.title}`; sxData.fonts = fontsData.assFonts(sBody) as Font[]; fs.writeFileSync(sxData.path, sBody); diff --git a/modules/module.vtt2ass.ts b/modules/module.vtt2ass.ts index 004f08b..9c5f7ea 100644 --- a/modules/module.vtt2ass.ts +++ b/modules/module.vtt2ass.ts @@ -410,6 +410,23 @@ function vtt(group: string | undefined, xFontSize: number | undefined, vttStr: s fontSize = xFontSize && xFontSize > 0 ? xFontSize : 34; // 1em to pix tmMrg = timeMargin ? timeMargin : 0; // rFont = replaceFont ? replaceFont : rFont; + if (vttStr.match(/::cue(?:.(.+)\))?{([^}]+)}/g)) { + const cssLines = []; + let defaultCss = ''; + const cssGroups = vttStr.matchAll(/::cue(?:.(.+)\))?{([^}]+)}/g); + for (const cssGroup of cssGroups) { + //Below code will bulldoze defined sizes for custom ones + /*if (!options.originalFontSize) { + cssGroup[2] = cssGroup[2].replace(/( font-size:.+?;)/g, '').replace(/(font-size:.+?;)/g, ''); + }*/ + if (cssGroup[1]) { + cssLines.push(`${cssGroup[1]}{${defaultCss}${cssGroup[2]}}`); + } else { + defaultCss = cssGroup[2]; + } + } + cssStr += cssLines.join('\r\n'); + } return convert( loadCSS(cssStr), loadVTT(vttStr) From 546a5327e828c00c421492e5855101d102ec5846 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 15:22:04 -0700 Subject: [PATCH 67/69] [HD] Implement -e downloading in new API Allows for single episode downloads by episode ID --- @types/newHidiveSeason.d.ts | 4 +- hidive.ts | 91 ++++++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/@types/newHidiveSeason.d.ts b/@types/newHidiveSeason.d.ts index 7ae7d04..0ed1b37 100644 --- a/@types/newHidiveSeason.d.ts +++ b/@types/newHidiveSeason.d.ts @@ -25,8 +25,8 @@ export interface Rating { export interface Episode { accessLevel: string; - availablePurchases: any[]; - licenceIds: any[]; + availablePurchases?: any[]; + licenceIds?: any[]; type: string; id: number; title: string; diff --git a/hidive.ts b/hidive.ts index 1282c95..f36ccc6 100644 --- a/hidive.ts +++ b/hidive.ts @@ -167,8 +167,15 @@ export default class Hidive implements ServiceClass { console.error('--new is not yet implemented in the new API'); } } else if(argv.e) { - console.error('-e is not yet supported'); - }else { + if (this.api == 'new') { + if (!(await this.downloadSingleEpisode(parseInt(argv.e), {...argv}))) { + console.error(`Unable to download selected episode ${argv.e}`); + return false; + } + } else { + console.error('-e is not supported in the old API'); + } + } else { console.info('No option selected or invalid value entered. Try --help.'); } } @@ -1024,6 +1031,86 @@ export default class Hidive implements ServiceClass { } } + public async downloadSingleEpisode(id: number, options: Record<any, any>) { + //Get Episode data + const episodeDataReq = await this.apiReq(`/v4/vod/${id}?includePlaybackDetails=URL`, '', 'auth', 'GET'); + if (!episodeDataReq.ok || !episodeDataReq.res) { + console.error('Failed to get episode data'); + return { isOk: false, reason: new Error('Failed to get Episode Data') }; + } + const episodeData = JSON.parse(episodeDataReq.res.body) as NewHidiveEpisode; + + if (episodeData.title.includes(' - ')) { + episodeData.episodeInformation.episodeNumber = parseFloat(episodeData.title.split(' - ')[0].replace('E', '')); + episodeData.title = episodeData.title.split(' - ')[1]; + } + + if (!episodeData.playerUrlCallback) { + console.error('Failed to download episode: You do not have access to this'); + return { isOk: false, reason: new Error('You do not have access to this') }; + } + + const seasonData = await this.getSeason(episodeData.episodeInformation.season); + if (!seasonData.isOk || !seasonData.value) { + console.error('Failed to get season data'); + return { isOk: false, reason: new Error('Failed to get season data') }; + } + + //Get Playback data + const playbackReq = await this.req.getData(episodeData.playerUrlCallback); + if(!playbackReq.ok || !playbackReq.res){ + console.error('Playback Request Failed'); + return { isOk: false, reason: new Error('Playback request failed') }; + } + const playbackData = JSON.parse(playbackReq.res.body) as NewHidivePlayback; + + //Get actual MPD + const mpdRequest = await this.req.getData(playbackData.dash[0].url); + if(!mpdRequest.ok || !mpdRequest.res){ + console.error('MPD Request Failed'); + return { isOk: false, reason: new Error('MPD request failed') }; + } + const mpd = mpdRequest.res.body as string; + + const selectedEpisode: NewHidiveEpisodeExtra = { + ...episodeData, + nameLong: episodeData.title, + titleId: episodeData.id, + seasonTitle: seasonData.value.title, + seriesTitle: seasonData.value.series.title, + isSelected: true + }; + + selectedEpisode.jwtToken = playbackData.dash[0].drm.jwtToken; + + //Output metadata and prepare for download + const availableSubs = playbackData.dash[0].subtitles.filter(a => a.format === 'vtt'); + const showTitle = `${selectedEpisode.seriesTitle} S${selectedEpisode.episodeInformation.seasonNumber}`; + console.info(`[INFO] ${showTitle} - ${selectedEpisode.episodeInformation.episodeNumber}`); + console.info('[INFO] Available dubs and subtitles:'); + console.info('\tAudios: ' + episodeData.offlinePlaybackLanguages.map(a => langsData.languages.find(b => b.code == a)?.name).join('\n\t\t')); + console.info('\tSubs : ' + availableSubs.map(a => langsData.languages.find(b => b.new_hd_locale == a.language)?.name).join('\n\t\t')); + console.info(`[INFO] Selected dub(s): ${options.dubLang.join(', ')}`); + const baseUrl = playbackData.dash[0].url.split('master')[0]; + const parsedmpd = parse(mpd, undefined, baseUrl); + const res = await this.downloadMPD(parsedmpd, availableSubs, selectedEpisode, options); + if (res === undefined || res.error) { + console.error('Failed to download media list'); + return { isOk: false, reason: new Error('Failed to download media list') }; + } else { + if (!options.skipmux) { + await this.muxStreams(res.data, { ...options, output: res.fileName }, false); + } else { + console.info('Skipping mux'); + } + downloaded({ + service: 'hidive', + type: 's' + }, selectedEpisode.titleId+'', [selectedEpisode.episodeInformation.episodeNumber+'']); + return { isOk: res, value: undefined }; + } + } + public async downloadMPD(streamPlaylists: MPDParsed, subs: Subtitle[], selectedEpisode: NewHidiveEpisodeExtra, options: Record<any, any>) { //let fileName: string; const files: DownloadedMedia[] = []; From 1500daf2076f5e2ce8602362bf9f3046255c6ba9 Mon Sep 17 00:00:00 2001 From: AnimeDL <multidownloadernx@gmail.com> Date: Sun, 17 Mar 2024 19:04:39 -0700 Subject: [PATCH 68/69] Update pnpm-lock.yaml --- pnpm-lock.yaml | 2456 +++++++++++++++++++++++++----------------------- 1 file changed, 1257 insertions(+), 1199 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ade06de..48aad3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,19 +7,19 @@ settings: dependencies: '@babel/core': specifier: ^7.22.9 - version: 7.22.9 + version: 7.24.0 '@babel/plugin-syntax-flow': specifier: ^7.22.5 - version: 7.22.5(@babel/core@7.22.9) + version: 7.23.3(@babel/core@7.24.0) '@babel/plugin-transform-react-jsx': specifier: ^7.22.5 - version: 7.22.5(@babel/core@7.22.9) + version: 7.23.4(@babel/core@7.24.0) '@types/xmldom': specifier: ^0.1.34 version: 0.1.34 '@yao-pkg/pkg': specifier: ^5.11.1 - version: 5.11.1 + version: 5.11.5 cheerio: specifier: 1.0.0-rc.12 version: 1.0.0-rc.12 @@ -28,13 +28,13 @@ dependencies: version: 2.8.5 dotenv: specifier: ^16.3.1 - version: 16.3.1 + version: 16.4.5 eslint-plugin-import: specifier: ^2.27.5 - version: 2.27.5(@typescript-eslint/parser@6.0.0)(eslint@8.45.0) + version: 2.29.1(@typescript-eslint/parser@6.0.0)(eslint@8.57.0) express: specifier: ^4.18.2 - version: 4.18.2 + version: 4.18.3 ffprobe: specifier: ^1.1.2 version: 1.1.2 @@ -43,7 +43,7 @@ dependencies: version: 4.0.0 fs-extra: specifier: ^11.1.1 - version: 11.1.1 + version: 11.2.0 got: specifier: ^11.8.6 version: 11.8.6 @@ -70,7 +70,7 @@ dependencies: version: 8.4.2 protobufjs: specifier: ^7.2.5 - version: 7.2.5 + version: 7.2.6 sei-helper: specifier: ^3.3.0 version: 3.3.0 @@ -79,13 +79,13 @@ dependencies: version: 0.0.1-alpha.0 ws: specifier: ^8.13.0 - version: 8.13.0 + version: 8.16.0 xmldom: specifier: ^0.6.0 version: 0.6.0 yaml: specifier: ^2.3.1 - version: 2.3.1 + version: 2.4.1 yargs: specifier: ^17.7.2 version: 17.7.2 @@ -93,46 +93,43 @@ dependencies: devDependencies: '@types/cors': specifier: ^2.8.13 - version: 2.8.13 + version: 2.8.17 '@types/express': specifier: ^4.17.17 - version: 4.17.17 + version: 4.17.21 '@types/ffprobe': specifier: ^1.1.4 - version: 1.1.4 + version: 1.1.8 '@types/fs-extra': specifier: ^11.0.1 - version: 11.0.1 + version: 11.0.4 '@types/node': specifier: ^18.15.11 version: 18.15.11 '@types/ws': specifier: ^8.5.5 - version: 8.5.5 + version: 8.5.10 '@types/yargs': specifier: ^17.0.24 - version: 17.0.24 + version: 17.0.32 '@typescript-eslint/eslint-plugin': specifier: ^6.0.0 - version: 6.0.0(@typescript-eslint/parser@6.0.0)(eslint@8.45.0)(typescript@5.1.6) + version: 6.0.0(@typescript-eslint/parser@6.0.0)(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/parser': specifier: ^6.0.0 - version: 6.0.0(eslint@8.45.0)(typescript@5.1.6) + version: 6.0.0(eslint@8.57.0)(typescript@5.1.6) '@vercel/webpack-asset-relocator-loader': specifier: ^1.7.3 version: 1.7.3 - '@yao-pkg/pkg': - specifier: ^5.11.1 - version: 5.11.1 eslint: specifier: ^8.45.0 - version: 8.45.0 + version: 8.57.0 eslint-config-react-app: specifier: ^7.0.1 - version: 7.0.1(@babel/plugin-syntax-flow@7.22.5)(@babel/plugin-transform-react-jsx@7.22.5)(eslint@8.45.0)(typescript@5.1.6) + version: 7.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(typescript@5.1.6) eslint-plugin-react: specifier: 7.32.2 - version: 7.32.2(eslint@8.45.0) + version: 7.32.2(eslint@8.57.0) protoc: specifier: ^1.1.3 version: 1.1.3 @@ -141,7 +138,7 @@ devDependencies: version: 3.0.1 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.15.11)(typescript@5.1.6) + version: 10.9.2(@types/node@18.15.11)(typescript@5.1.6) ts-proto: specifier: ^1.169.1 version: 1.169.1 @@ -162,43 +159,32 @@ packages: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.18 - /@babel/code-frame@7.21.4: - resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/highlight': 7.18.6 - dev: true + '@babel/highlight': 7.23.4 + chalk: 2.4.2 - /@babel/code-frame@7.22.5: - resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.22.5 - - /@babel/compat-data@7.21.4: - resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/compat-data@7.22.9: - resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} engines: {node: '>=6.9.0'} - /@babel/core@7.22.9: - resolution: {integrity: sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==} + /@babel/core@7.24.0: + resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.22.5 - '@babel/generator': 7.22.9 - '@babel/helper-compilation-targets': 7.22.9(@babel/core@7.22.9) - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.9) - '@babel/helpers': 7.22.6 - '@babel/parser': 7.22.7 - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.8 - '@babel/types': 7.22.5 - convert-source-map: 1.9.0 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helpers': 7.24.0 + '@babel/parser': 7.24.0 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 + convert-source-map: 2.0.0 debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.3 @@ -206,222 +192,155 @@ packages: transitivePeerDependencies: - supports-color - /@babel/eslint-parser@7.21.3(@babel/core@7.22.9)(eslint@8.45.0): + /@babel/eslint-parser@7.21.3(@babel/core@7.24.0)(eslint@8.57.0): resolution: {integrity: sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: '@babel/core': '>=7.11.0' eslint: ^7.5.0 || ^8.0.0 dependencies: - '@babel/core': 7.22.9 + '@babel/core': 7.24.0 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 8.45.0 + eslint: 8.57.0 eslint-visitor-keys: 2.1.0 semver: 6.3.1 dev: true - /@babel/generator@7.22.9: - resolution: {integrity: sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.5 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - jsesc: 2.5.2 - /@babel/generator@7.23.0: resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.24.0 '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.18 jsesc: 2.5.2 dev: false - /@babel/helper-annotate-as-pure@7.18.6: - resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 - dev: true + '@babel/types': 7.24.0 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + jsesc: 2.5.2 /@babel/helper-annotate-as-pure@7.22.5: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.24.0 /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9: resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.22.5 + '@babel/types': 7.24.0 dev: true - /@babel/helper-compilation-targets@7.21.4(@babel/core@7.22.9): - resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==} + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.22.9 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.21.5 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - - /@babel/helper-compilation-targets@7.22.9(@babel/core@7.22.9): - resolution: {integrity: sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.22.9 - '@babel/core': 7.22.9 - '@babel/helper-validator-option': 7.22.5 - browserslist: 4.21.9 + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.23.0 lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-create-class-features-plugin@7.21.4(@babel/core@7.22.9): + /@babel/helper-create-class-features-plugin@7.21.4(@babel/core@7.24.0): resolution: {integrity: sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-replace-supers': 7.20.7 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-split-export-declaration': 7.22.6 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-create-regexp-features-plugin@7.21.4(@babel/core@7.22.9): + /@babel/helper-create-regexp-features-plugin@7.21.4(@babel/core@7.24.0): resolution: {integrity: sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 dev: true - /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.22.9): + /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.24.0): resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} peerDependencies: '@babel/core': ^7.4.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4 lodash.debounce: 4.0.8 - resolve: 1.22.2 + resolve: 1.22.8 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-environment-visitor@7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-environment-visitor@7.22.5: - resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} /@babel/helper-explode-assignable-expression@7.18.6: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 + '@babel/types': 7.24.0 dev: true - /@babel/helper-function-name@7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.22.5 - dev: true - - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.23.0 - - /@babel/helper-hoist-variables@7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.5 - dev: true + '@babel/template': 7.24.0 + '@babel/types': 7.24.0 /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.24.0 /@babel/helper-member-expression-to-functions@7.21.0: resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 + '@babel/types': 7.24.0 dev: true - /@babel/helper-module-imports@7.21.4: - resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 - dev: true + '@babel/types': 7.24.0 - /@babel/helper-module-imports@7.22.5: - resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.0 - - /@babel/helper-module-transforms@7.21.2: - resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-simple-access': 7.20.2 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.22.5 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.22.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.9): - resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/core': 7.24.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-validator-identifier': 7.22.20 @@ -430,29 +349,24 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 - dev: true - - /@babel/helper-plugin-utils@7.20.2: - resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} - engines: {node: '>=6.9.0'} + '@babel/types': 7.24.0 dev: true /@babel/helper-plugin-utils@7.22.5: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} engines: {node: '>=6.9.0'} - /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.22.9): + /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.24.0): resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.22.5 + '@babel/types': 7.24.0 transitivePeerDependencies: - supports-color dev: true @@ -461,1108 +375,1057 @@ packages: resolution: {integrity: sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.22.5 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-simple-access@7.20.2: - resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.5 - dev: true - /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.24.0 /@babel/helper-skip-transparent-expression-wrappers@7.20.0: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 - dev: true - - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.5 + '@babel/types': 7.24.0 dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.5 + '@babel/types': 7.24.0 - /@babel/helper-string-parser@7.22.5: - resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - dev: false - /@babel/helper-validator-identifier@7.22.5: - resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-option@7.21.0: - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-validator-option@7.22.5: - resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} /@babel/helper-wrap-function@7.20.5: resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.21.0 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.22.5 + '@babel/helper-function-name': 7.23.0 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 transitivePeerDependencies: - supports-color dev: true - /@babel/helpers@7.22.6: - resolution: {integrity: sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==} + /@babel/helpers@7.24.0: + resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.8 - '@babel/types': 7.23.0 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.0 + '@babel/types': 7.24.0 transitivePeerDependencies: - supports-color - /@babel/highlight@7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.5 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - - /@babel/highlight@7.22.5: - resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==} + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 - /@babel/parser@7.22.7: - resolution: {integrity: sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.22.5 - /@babel/parser@7.23.0: resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.24.0 dev: false - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.22.9): + /@babel/parser@7.24.0: + resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.22.9): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.9) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.22.9): + /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.22.9) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.24.0) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.22.9): + /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-decorators@7.21.0(@babel/core@7.22.9): + /@babel/plugin-proposal-decorators@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/plugin-syntax-decorators': 7.21.0(@babel/core@7.22.9) + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/plugin-syntax-decorators': 7.21.0(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.22.9): + /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.24.0): resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.22.9): + /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.22.9): + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.22.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.22.9) + '@babel/compat-data': 7.23.5 + '@babel/core': 7.24.0 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.22.9): + /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.9) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0) dev: true - /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.22.9): + /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.22.9): + /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} engines: {node: '>=4'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.9): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.9): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.22.9): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-decorators@7.21.0(@babel/core@7.22.9): + /@babel/plugin-syntax-decorators@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.22.9): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.22.9): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-flow@7.22.5(@babel/core@7.22.9): - resolution: {integrity: sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==} + /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.22.9): + /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.24.0): resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.9): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.22.9): - resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==} + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.9): - resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.9 + '@babel/core': 7.24.0 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.9): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.9): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.9): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.9): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.9): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.9): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.22.9): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.22.9): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.22.9): + /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.24.0): resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.22.9): + /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.22.9): + /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.22.9): + /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-classes@7.21.0(@babel/core@7.22.9): + /@babel/plugin-transform-classes@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.22.9) - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.22.9): + /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/template': 7.20.7 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/template': 7.24.0 dev: true - /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.22.9): + /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.24.0): resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.22.9): + /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.24.0): resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 + '@babel/core': 7.24.0 '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-flow-strip-types@7.21.0(@babel/core@7.22.9): + /@babel/plugin-transform-flow-strip-types@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.22.9): + /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.24.0): resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.22.9): + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.24.0): resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.22.9) - '@babel/helper-function-name': 7.21.0 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-literals@7.18.9(@babel/core@7.22.9): + /@babel/plugin-transform-literals@7.18.9(@babel/core@7.24.0): resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.22.9): + /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.24.0): resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.24.0 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-commonjs@7.21.2(@babel/core@7.22.9): + /@babel/plugin-transform-modules-commonjs@7.21.2(@babel/core@7.24.0): resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-simple-access': 7.20.2 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.24.0 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-simple-access': 7.22.5 dev: true - /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.22.9): + /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.24.0): resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.22.5 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.24.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.24.0 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.22.9): + /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.24.0): resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.20.7 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.22.9): + /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.24.0): resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.0) dev: true - /@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.22.9): - resolution: {integrity: sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==} + /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.24.0): + resolution: {integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 + '@babel/core': 7.24.0 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.9) - '@babel/types': 7.22.5 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) + '@babel/types': 7.24.0 - /@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.22.9): + /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.24.0): resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 regenerator-transform: 0.15.1 dev: true - /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-runtime@7.21.4(@babel/core@7.22.9): + /@babel/plugin-transform-runtime@7.21.4(@babel/core@7.24.0): resolution: {integrity: sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.22.9) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.22.9) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.24.0) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.24.0) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.24.0) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-spread@7.20.7(@babel/core@7.22.9): + /@babel/plugin-transform-spread@7.20.7(@babel/core@7.24.0): resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 dev: true - /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.22.9): + /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.24.0): resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.22.9): + /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.24.0): resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.22.9): + /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.24.0): resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.22.9): + /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.24.0): resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.22.9): + /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.0 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.24.0) + '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/preset-env@7.21.4(@babel/core@7.22.9): + /@babel/preset-env@7.21.4(@babel/core@7.24.0): resolution: {integrity: sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.22.9 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.22.9) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.22.9) - '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.9) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.9) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.9) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.22.9) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.9) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.9) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.9) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.9) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.9) - '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.22.9) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.22.9) - '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.22.9) - '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.22.9) - '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.22.9) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.22.9) - '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.22.9) - '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.22.9) - '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.22.9) - '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.22.9) - '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.22.9) - '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.22.9) - '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.22.9) - '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.22.9) - '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.22.9) - '@babel/preset-modules': 0.1.5(@babel/core@7.22.9) - '@babel/types': 7.22.5 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.22.9) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.22.9) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.22.9) + '@babel/compat-data': 7.23.5 + '@babel/core': 7.24.0 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.24.0) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.24.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.24.0) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.24.0) + '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.24.0) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.24.0) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.24.0) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.24.0) + '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.24.0) + '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.24.0) + '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.24.0) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.24.0) + '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.24.0) + '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.24.0) + '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.24.0) + '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.24.0) + '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.24.0) + '@babel/preset-modules': 0.1.5(@babel/core@7.24.0) + '@babel/types': 7.24.0 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.24.0) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.24.0) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.24.0) core-js-compat: 3.30.0 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-modules@0.1.5(@babel/core@7.22.9): + /@babel/preset-modules@0.1.5(@babel/core@7.24.0): resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.22.9) - '@babel/types': 7.22.5 + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.24.0) + '@babel/types': 7.24.0 esutils: 2.0.3 dev: true - /@babel/preset-react@7.18.6(@babel/core@7.22.9): + /@babel/preset-react@7.18.6(@babel/core@7.24.0): resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.9) - '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.24.0) dev: true - /@babel/preset-typescript@7.21.4(@babel/core@7.22.9): + /@babel/preset-typescript@7.21.4(@babel/core@7.24.0): resolution: {integrity: sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.22.9) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.22.9) - '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.24.0) + '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true @@ -1577,75 +1440,48 @@ packages: dependencies: regenerator-runtime: 0.13.11 - /@babel/template@7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.21.4 - '@babel/parser': 7.22.7 - '@babel/types': 7.22.5 - dev: true + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 - /@babel/template@7.22.5: - resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} + /@babel/traverse@7.24.0: + resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.22.5 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - - /@babel/traverse@7.21.4: - resolution: {integrity: sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.22.9 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.22.7 - '@babel/types': 7.22.5 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/traverse@7.22.8: - resolution: {integrity: sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.5 - '@babel/generator': 7.23.0 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 + '@babel/parser': 7.24.0 + '@babel/types': 7.24.0 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color - /@babel/types@7.22.5: - resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 - to-fast-properties: 2.0.0 - /@babel/types@7.23.0: resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-string-parser': 7.22.5 + '@babel/helper-string-parser': 7.23.4 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 dev: false + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -1653,21 +1489,21 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.45.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.45.0 - eslint-visitor-keys: 3.4.1 + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 - /@eslint-community/regexpp@4.5.0: - resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==} + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - /@eslint/eslintrc@2.1.0: - resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 @@ -1682,15 +1518,15 @@ packages: transitivePeerDependencies: - supports-color - /@eslint/js@8.44.0: - resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@humanwhocodes/config-array@0.11.10: - resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 1.2.1 + '@humanwhocodes/object-schema': 2.0.2 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -1700,8 +1536,8 @@ packages: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} @@ -1854,8 +1690,8 @@ packages: '@types/node': 18.15.11 dev: true - /@types/cors@2.8.13: - resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==} + /@types/cors@2.8.17: + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} dependencies: '@types/node': 18.15.11 dev: true @@ -1868,8 +1704,8 @@ packages: '@types/range-parser': 1.2.4 dev: true - /@types/express@4.17.17: - resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} dependencies: '@types/body-parser': 1.19.2 '@types/express-serve-static-core': 4.17.33 @@ -1877,12 +1713,12 @@ packages: '@types/serve-static': 1.15.1 dev: true - /@types/ffprobe@1.1.4: - resolution: {integrity: sha512-gtfU+bD4FDoF1S2ybmIWEIz0K5ijeHpi+CgJUtXl3FTGnf+61HmsZksqDn8M9M9lRJU9SRyMLt5yrIwUSep4Uw==} + /@types/ffprobe@1.1.8: + resolution: {integrity: sha512-qPxx8Dy0HyH12hQCESN39NQOmU2Yl2b7tCyUhWy0l11HQv/8Yv7U+vcaveFXmXK8hcAP0oj29DPFuSM7vcC3Tg==} dev: true - /@types/fs-extra@11.0.1: - resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} + /@types/fs-extra@11.0.4: + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} dependencies: '@types/jsonfile': 6.1.1 '@types/node': 18.15.11 @@ -1892,8 +1728,8 @@ packages: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: false - /@types/json-schema@7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true /@types/json5@0.0.29: @@ -1936,8 +1772,8 @@ packages: '@types/node': 18.15.11 dev: false - /@types/semver@7.3.13: - resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true /@types/serve-static@1.15.1: @@ -1947,8 +1783,8 @@ packages: '@types/node': 18.15.11 dev: true - /@types/ws@8.5.5: - resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} + /@types/ws@8.5.10: + resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: '@types/node': 18.15.11 dev: true @@ -1961,13 +1797,13 @@ packages: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: true - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + /@types/yargs@17.0.32: + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} dependencies: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin@5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/eslint-plugin@5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-1MeobQkQ9tztuleT3v72XmY0XuKXVXusAhryoLuU5YZ+mXoYKZP9SQ7Flulh1NX4DTjpGTc2b/eMu4u7M7dhnQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1978,13 +1814,13 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.5.0 - '@typescript-eslint/parser': 5.57.1(eslint@8.45.0)(typescript@5.1.6) + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 5.57.1(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/scope-manager': 5.57.1 - '@typescript-eslint/type-utils': 5.57.1(eslint@8.45.0)(typescript@5.1.6) - '@typescript-eslint/utils': 5.57.1(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/type-utils': 5.57.1(eslint@8.57.0)(typescript@5.1.6) + '@typescript-eslint/utils': 5.57.1(eslint@8.57.0)(typescript@5.1.6) debug: 4.3.4 - eslint: 8.45.0 + eslint: 8.57.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 @@ -1995,7 +1831,7 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@6.0.0(@typescript-eslint/parser@6.0.0)(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/eslint-plugin@6.0.0(@typescript-eslint/parser@6.0.0)(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-xuv6ghKGoiq856Bww/yVYnXGsKa588kY3M0XK7uUW/3fJNNULKRfZfSBkMTSpqGG/8ZCXCadfh8G/z/B4aqS/A==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -2006,14 +1842,14 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.5.0 - '@typescript-eslint/parser': 6.0.0(eslint@8.45.0)(typescript@5.1.6) + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.0.0(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/scope-manager': 6.0.0 - '@typescript-eslint/type-utils': 6.0.0(eslint@8.45.0)(typescript@5.1.6) - '@typescript-eslint/utils': 6.0.0(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/type-utils': 6.0.0(eslint@8.57.0)(typescript@5.1.6) + '@typescript-eslint/utils': 6.0.0(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/visitor-keys': 6.0.0 debug: 4.3.4 - eslint: 8.45.0 + eslint: 8.57.0 grapheme-splitter: 1.0.4 graphemer: 1.4.0 ignore: 5.2.4 @@ -2026,20 +1862,20 @@ packages: - supports-color dev: true - /@typescript-eslint/experimental-utils@5.57.1(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/experimental-utils@5.57.1(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-5F5s8mpM1Y0RQ5iWzKQPQm5cmhARgcMfUwyHX1ZZFL8Tm0PyzyQ+9jgYSMaW74XXvpDg9/KdmMICLlwNwKtO7w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 5.57.1(eslint@8.45.0)(typescript@5.1.6) - eslint: 8.45.0 + '@typescript-eslint/utils': 5.57.1(eslint@8.57.0)(typescript@5.1.6) + eslint: 8.57.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/parser@5.57.1(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/parser@5.57.1(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-hlA0BLeVSA/wBPKdPGxoVr9Pp6GutGoY380FEhbVi0Ph4WNe8kLvqIRx76RSQt1lynZKfrXKs0/XeEk4zZycuA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2053,13 +1889,13 @@ packages: '@typescript-eslint/types': 5.57.1 '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.1.6) debug: 4.3.4 - eslint: 8.45.0 + eslint: 8.57.0 typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.0.0(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/parser@6.0.0(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-TNaufYSPrr1U8n+3xN+Yp9g31vQDJqhXzzPSHfQDLcaO4tU+mCfODPxCwf4H530zo7aUBE3QIdxCXamEnG04Tg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -2074,7 +1910,7 @@ packages: '@typescript-eslint/typescript-estree': 6.0.0(typescript@5.1.6) '@typescript-eslint/visitor-keys': 6.0.0 debug: 4.3.4 - eslint: 8.45.0 + eslint: 8.57.0 typescript: 5.1.6 transitivePeerDependencies: - supports-color @@ -2094,7 +1930,7 @@ packages: '@typescript-eslint/types': 6.0.0 '@typescript-eslint/visitor-keys': 6.0.0 - /@typescript-eslint/type-utils@5.57.1(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/type-utils@5.57.1(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-/RIPQyx60Pt6ga86hKXesXkJ2WOS4UemFrmmq/7eOyiYjYv/MUSHPlkhU6k9T9W1ytnTJueqASW+wOmW4KrViw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2105,16 +1941,16 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.1.6) - '@typescript-eslint/utils': 5.57.1(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/utils': 5.57.1(eslint@8.57.0)(typescript@5.1.6) debug: 4.3.4 - eslint: 8.45.0 + eslint: 8.57.0 tsutils: 3.21.0(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/type-utils@6.0.0(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/type-utils@6.0.0(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-ah6LJvLgkoZ/pyJ9GAdFkzeuMZ8goV6BH7eC9FPmojrnX9yNCIsfjB+zYcnex28YO3RFvBkV6rMV6WpIqkPvoQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -2125,9 +1961,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 6.0.0(typescript@5.1.6) - '@typescript-eslint/utils': 6.0.0(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/utils': 6.0.0(eslint@8.57.0)(typescript@5.1.6) debug: 4.3.4 - eslint: 8.45.0 + eslint: 8.57.0 ts-api-utils: 1.0.1(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: @@ -2184,19 +2020,19 @@ packages: transitivePeerDependencies: - supports-color - /@typescript-eslint/utils@5.57.1(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/utils@5.57.1(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) - '@types/json-schema': 7.0.11 - '@types/semver': 7.3.13 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.57.1 '@typescript-eslint/types': 5.57.1 '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.1.6) - eslint: 8.45.0 + eslint: 8.57.0 eslint-scope: 5.1.1 semver: 7.5.4 transitivePeerDependencies: @@ -2204,19 +2040,19 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.0.0(eslint@8.45.0)(typescript@5.1.6): + /@typescript-eslint/utils@6.0.0(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-SOr6l4NB6HE4H/ktz0JVVWNXqCJTOo/mHnvIte1ZhBQ0Cvd04x5uKZa3zT6tiodL06zf5xxdK8COiDvPnQ27JQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) - '@types/json-schema': 7.0.11 - '@types/semver': 7.3.13 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 6.0.0 '@typescript-eslint/types': 6.0.0 '@typescript-eslint/typescript-estree': 6.0.0(typescript@5.1.6) - eslint: 8.45.0 + eslint: 8.57.0 eslint-scope: 5.1.1 semver: 7.5.4 transitivePeerDependencies: @@ -2229,7 +2065,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: '@typescript-eslint/types': 5.57.1 - eslint-visitor-keys: 3.4.1 + eslint-visitor-keys: 3.4.3 dev: true /@typescript-eslint/visitor-keys@6.0.0: @@ -2237,7 +2073,10 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dependencies: '@typescript-eslint/types': 6.0.0 - eslint-visitor-keys: 3.4.1 + eslint-visitor-keys: 3.4.3 + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} /@vercel/webpack-asset-relocator-loader@1.7.3: resolution: {integrity: sha512-vizrI18v8Lcb1PmNNUBz7yxPxxXoOeuaVEjTG9MjvDrphjiSxFZrRJ5tIghk+qdLFRCXI5HBCshgobftbmrC5g==} @@ -2268,8 +2107,8 @@ packages: engines: {node: '>=10.0.0'} dev: false - /@yao-pkg/pkg-fetch@3.5.7: - resolution: {integrity: sha512-DhuvjBZsdrUrkXC+eYZljxCxZ8QrjPQRGhJEA8+hUsKhmPyg+FUn6ebOfN6B7ioiJg7GsVpUJt57hALBCj9epA==} + /@yao-pkg/pkg-fetch@3.5.9: + resolution: {integrity: sha512-usMwwqFCd2B7k+V87u6kiTesyDSlw+3LpiuYBWe+UgryvSOk/NXjx3XVCub8hQoi0bCREbdQ6NDBqminyHJJrg==} hasBin: true dependencies: chalk: 4.1.2 @@ -2285,14 +2124,14 @@ packages: - supports-color dev: false - /@yao-pkg/pkg@5.11.1: - resolution: {integrity: sha512-y++Kd/kMZcp0UeI2GWB9+55WsDqZp3XCjHGzC4Nsao0tlGjhnNOB+TPhZhLTOCL1V4ApaKkH8zzPcpaTCtvM8g==} + /@yao-pkg/pkg@5.11.5: + resolution: {integrity: sha512-NPFXCn+5bAYZKej7jI92+mXiWG/LA6pEIJCXgI4MM3aYhUFrQOPrYKYr3cGXGs9lkgKGovlnMcKGDjwJ3B7rCQ==} hasBin: true dependencies: '@babel/generator': 7.23.0 '@babel/parser': 7.23.0 '@babel/types': 7.23.0 - '@yao-pkg/pkg-fetch': 3.5.7 + '@yao-pkg/pkg-fetch': 3.5.9 chalk: 4.1.2 fs-extra: 9.1.0 globby: 11.1.0 @@ -2301,7 +2140,7 @@ packages: minimist: 1.2.8 multistream: 4.1.0 prebuild-install: 7.1.1 - resolve: 1.22.2 + resolve: 1.22.8 stream-meter: 1.0.4 transitivePeerDependencies: - encoding @@ -2341,12 +2180,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - /acorn@8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -2393,58 +2226,92 @@ packages: deep-equal: 2.2.0 dev: true - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - is-array-buffer: 3.0.2 + call-bind: 1.0.7 + is-array-buffer: 3.0.4 /array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} dev: false - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + get-intrinsic: 1.2.4 is-string: 1.0.7 /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - /array.prototype.flat@1.3.1: - resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + /array.prototype.filter@1.0.3: + resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-array-method-boxes-properly: 1.0.0 + is-string: 1.0.7 - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + /array.prototype.findlastindex@1.2.4: + resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 - /array.prototype.tosorted@1.1.1: - resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + + /array.prototype.tosorted@1.1.3: + resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 dev: true + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + /ast-types-flow@0.0.7: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} dev: true @@ -2458,9 +2325,11 @@ packages: engines: {node: '>= 4.0.0'} dev: false - /available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 /axe-core@4.6.3: resolution: {integrity: sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==} @@ -2479,41 +2348,41 @@ packages: dependencies: '@babel/runtime': 7.21.0 cosmiconfig: 7.1.0 - resolve: 1.22.2 + resolve: 1.22.8 dev: true - /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.22.9): + /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.24.0): resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.22.9 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.22.9) + '@babel/compat-data': 7.23.5 + '@babel/core': 7.24.0 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.24.0) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.22.9): + /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.24.0): resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.24.0) core-js-compat: 3.30.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.22.9): + /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.24.0): resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.9 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.24.0) transitivePeerDependencies: - supports-color dev: true @@ -2525,20 +2394,20 @@ packages: /babel-preset-react-app@10.0.1: resolution: {integrity: sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==} dependencies: - '@babel/core': 7.22.9 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-decorators': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.22.9) - '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.22.9) - '@babel/plugin-transform-runtime': 7.21.4(@babel/core@7.22.9) - '@babel/preset-env': 7.21.4(@babel/core@7.22.9) - '@babel/preset-react': 7.18.6(@babel/core@7.22.9) - '@babel/preset-typescript': 7.21.4(@babel/core@7.22.9) + '@babel/core': 7.24.0 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-decorators': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.24.0) + '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.24.0) + '@babel/plugin-transform-runtime': 7.21.4(@babel/core@7.24.0) + '@babel/preset-env': 7.21.4(@babel/core@7.24.0) + '@babel/preset-react': 7.18.6(@babel/core@7.24.0) + '@babel/preset-typescript': 7.21.4(@babel/core@7.24.0) '@babel/runtime': 7.21.0 babel-plugin-macros: 3.1.0 babel-plugin-transform-react-remove-prop-types: 0.4.24 @@ -2577,8 +2446,8 @@ packages: resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} dev: true - /body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dependencies: bytes: 3.1.2 @@ -2590,7 +2459,7 @@ packages: iconv-lite: 0.4.24 on-finished: 2.4.1 qs: 6.11.0 - raw-body: 2.5.1 + raw-body: 2.5.2 type-is: 1.6.18 unpipe: 1.0.0 transitivePeerDependencies: @@ -2613,26 +2482,15 @@ packages: dependencies: fill-range: 7.0.1 - /browserslist@4.21.5: - resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001474 - electron-to-chromium: 1.4.356 - node-releases: 2.0.10 - update-browserslist-db: 1.0.10(browserslist@4.21.5) - dev: true - - /browserslist@4.21.9: - resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001516 - electron-to-chromium: 1.4.461 - node-releases: 2.0.13 - update-browserslist-db: 1.0.11(browserslist@4.21.9) + caniuse-lite: 1.0.30001599 + electron-to-chromium: 1.4.708 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) /buffer-indexof-polyfill@1.0.2: resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} @@ -2674,22 +2532,22 @@ packages: responselike: 2.0.1 dev: false - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - /caniuse-lite@1.0.30001474: - resolution: {integrity: sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q==} - dev: true - - /caniuse-lite@1.0.30001516: - resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==} + /caniuse-lite@1.0.30001599: + resolution: {integrity: sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==} /case-anything@2.1.13: resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==} @@ -2833,8 +2691,8 @@ packages: engines: {node: '>= 0.6'} dev: false - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -2848,7 +2706,7 @@ packages: /core-js-compat@3.30.0: resolution: {integrity: sha512-P5A2h/9mRYZFIAP+5Ab8ns6083IyVpSclU74UNvbGVQ8VM7n3n3/g2yF3AkKQ9NXz2O+ioxLbEWKnDtgsFamhg==} dependencies: - browserslist: 4.21.5 + browserslist: 4.23.0 dev: true /core-util-is@1.0.3: @@ -2909,6 +2767,30 @@ packages: engines: {node: '>= 12'} dev: true + /data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + /data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + /data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + /date-format@4.0.14: resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} engines: {node: '>=4.0'} @@ -2956,23 +2838,23 @@ packages: /deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 es-get-iterator: 1.1.3 - get-intrinsic: 1.2.0 + get-intrinsic: 1.2.4 is-arguments: 1.1.1 - is-array-buffer: 3.0.2 + is-array-buffer: 3.0.4 is-date-object: 1.0.5 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 isarray: 2.0.5 object-is: 1.1.5 object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 side-channel: 1.0.4 which-boxed-primitive: 1.0.2 which-collection: 1.0.1 - which-typed-array: 1.1.9 + which-typed-array: 1.1.15 dev: true /deep-extend@0.6.0: @@ -2994,16 +2876,25 @@ packages: native-promise-only: 0.8.1 dev: false + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + /define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} dev: false - /define-properties@1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} dependencies: - has-property-descriptors: 1.0.0 + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 object-keys: 1.1.1 /delayed-stream@1.0.0: @@ -3086,8 +2977,8 @@ packages: domhandler: 5.0.3 dev: false - /dotenv@16.3.1: - resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} dev: false @@ -3107,12 +2998,8 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false - /electron-to-chromium@1.4.356: - resolution: {integrity: sha512-nEftV1dRX3omlxAj42FwqRZT0i4xd2dIg39sog/CnCJeCcL1TRd2Uh0i9Oebgv8Ou0vzTPw++xc+Z20jzS2B6A==} - dev: true - - /electron-to-chromium@1.4.461: - resolution: {integrity: sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==} + /electron-to-chromium@1.4.708: + resolution: {integrity: sha512-iWgEEvREL4GTXXHKohhh33+6Y8XkPI5eHihDmm8zUk5Zo7HICEW+wI/j5kJ2tbuNUCXJ/sNXa03ajW635DiJXA==} /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3144,50 +3031,121 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract@1.21.2: - resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} + /es-abstract@1.22.5: + resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==} engines: {node: '>= 0.4'} dependencies: - array-buffer-byte-length: 1.0.0 - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 es-to-primitive: 1.2.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.2.0 - get-symbol-description: 1.0.0 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 globalthis: 1.0.3 gopd: 1.0.1 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 has-symbols: 1.0.3 - internal-slot: 1.0.5 - is-array-buffer: 3.0.2 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 is-callable: 1.2.7 - is-negative-zero: 2.0.2 + is-negative-zero: 2.0.3 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 is-string: 1.0.7 - is-typed-array: 1.1.10 + is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.12.3 + object-inspect: 1.13.1 object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.7 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 - typed-array-length: 1.0.4 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 unbox-primitive: 1.0.2 - which-typed-array: 1.1.9 + which-typed-array: 1.1.15 + + /es-abstract@1.23.1: + resolution: {integrity: sha512-r+YVn6hTqQb+P5kK0u3KeDqrmhHKm+OhU/Mw4jSL4eQtOxXmp75fXIUUb3sUqFZOlb/YtW5JRaIfEC3UyjYUZQ==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + + /es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} /es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 has-symbols: 1.0.3 is-arguments: 1.1.1 is-map: 2.0.2 @@ -3197,18 +3155,24 @@ packages: stop-iteration-iterator: 1.0.0 dev: true - /es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + /es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 - has-tostringtag: 1.0.0 + es-errors: 1.3.0 - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} dependencies: - has: 1.0.3 + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.2 /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} @@ -3234,7 +3198,7 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - /eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.22.5)(@babel/plugin-transform-react-jsx@7.22.5)(eslint@8.45.0)(typescript@5.1.6): + /eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -3244,21 +3208,21 @@ packages: typescript: optional: true dependencies: - '@babel/core': 7.22.9 - '@babel/eslint-parser': 7.21.3(@babel/core@7.22.9)(eslint@8.45.0) + '@babel/core': 7.24.0 + '@babel/eslint-parser': 7.21.3(@babel/core@7.24.0)(eslint@8.57.0) '@rushstack/eslint-patch': 1.2.0 - '@typescript-eslint/eslint-plugin': 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.45.0)(typescript@5.1.6) - '@typescript-eslint/parser': 5.57.1(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/eslint-plugin': 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.57.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.57.1(eslint@8.57.0)(typescript@5.1.6) babel-preset-react-app: 10.0.1 confusing-browser-globals: 1.0.11 - eslint: 8.45.0 - eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.22.5)(@babel/plugin-transform-react-jsx@7.22.5)(eslint@8.45.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.45.0) - eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.57.1)(eslint@8.45.0)(typescript@5.1.6) - eslint-plugin-jsx-a11y: 6.7.1(eslint@8.45.0) - eslint-plugin-react: 7.32.2(eslint@8.45.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.45.0) - eslint-plugin-testing-library: 5.10.2(eslint@8.45.0)(typescript@5.1.6) + eslint: 8.57.0 + eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.57.1)(eslint@8.57.0) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.57.1)(eslint@8.57.0)(typescript@5.1.6) + eslint-plugin-jsx-a11y: 6.7.1(eslint@8.57.0) + eslint-plugin-react: 7.32.2(eslint@8.57.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) + eslint-plugin-testing-library: 5.10.2(eslint@8.57.0)(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - '@babel/plugin-syntax-flow' @@ -3269,17 +3233,17 @@ packages: - supports-color dev: true - /eslint-import-resolver-node@0.3.7: - resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: debug: 3.2.7 - is-core-module: 2.11.0 - resolve: 1.22.2 + is-core-module: 2.13.1 + resolve: 1.22.8 transitivePeerDependencies: - supports-color - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint@8.45.0): - resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + /eslint-module-utils@2.8.1(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -3299,16 +3263,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.57.1(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.57.1(eslint@8.57.0)(typescript@5.1.6) debug: 3.2.7 - eslint: 8.45.0 - eslint-import-resolver-node: 0.3.7 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.7.4(@typescript-eslint/parser@6.0.0)(eslint-import-resolver-node@0.3.7)(eslint@8.45.0): - resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + /eslint-module-utils@2.8.1(@typescript-eslint/parser@6.0.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -3328,15 +3292,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.0.0(eslint@8.45.0)(typescript@5.1.6) + '@typescript-eslint/parser': 6.0.0(eslint@8.57.0)(typescript@5.1.6) debug: 3.2.7 - eslint: 8.45.0 - eslint-import-resolver-node: 0.3.7 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color dev: false - /eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.22.5)(@babel/plugin-transform-react-jsx@7.22.5)(eslint@8.45.0): + /eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0): resolution: {integrity: sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -3344,15 +3308,15 @@ packages: '@babel/plugin-transform-react-jsx': ^7.14.9 eslint: ^8.1.0 dependencies: - '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.22.9) - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.9) - eslint: 8.45.0 + '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.0) + eslint: 8.57.0 lodash: 4.17.21 string-natural-compare: 3.0.1 dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.45.0): - resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.57.1)(eslint@8.57.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -3361,31 +3325,33 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.57.1(eslint@8.45.0)(typescript@5.1.6) - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 - array.prototype.flatmap: 1.3.1 + '@typescript-eslint/parser': 5.57.1(eslint@8.57.0)(typescript@5.1.6) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.4 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.45.0 - eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint@8.45.0) - has: 1.0.3 - is-core-module: 2.11.0 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + hasown: 2.0.2 + is-core-module: 2.13.1 is-glob: 4.0.3 minimatch: 3.1.2 - object.values: 1.1.6 - resolve: 1.22.2 - semver: 6.3.0 - tsconfig-paths: 3.14.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.2 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.15.0 transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@6.0.0)(eslint@8.45.0): - resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.0.0)(eslint@8.57.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -3394,30 +3360,32 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.0.0(eslint@8.45.0)(typescript@5.1.6) - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 - array.prototype.flatmap: 1.3.1 + '@typescript-eslint/parser': 6.0.0(eslint@8.57.0)(typescript@5.1.6) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.4 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.45.0 - eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@6.0.0)(eslint-import-resolver-node@0.3.7)(eslint@8.45.0) - has: 1.0.3 - is-core-module: 2.11.0 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.0.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + hasown: 2.0.2 + is-core-module: 2.13.1 is-glob: 4.0.3 minimatch: 3.1.2 - object.values: 1.1.6 - resolve: 1.22.2 - semver: 6.3.0 - tsconfig-paths: 3.14.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.2 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.15.0 transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color dev: false - /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.57.1)(eslint@8.45.0)(typescript@5.1.6): + /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.57.1)(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} peerDependencies: @@ -3430,15 +3398,15 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.45.0)(typescript@5.1.6) - '@typescript-eslint/experimental-utils': 5.57.1(eslint@8.45.0)(typescript@5.1.6) - eslint: 8.45.0 + '@typescript-eslint/eslint-plugin': 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.57.0)(typescript@5.1.6) + '@typescript-eslint/experimental-utils': 5.57.1(eslint@8.57.0)(typescript@5.1.6) + eslint: 8.57.0 transitivePeerDependencies: - supports-color - typescript dev: true - /eslint-plugin-jsx-a11y@6.7.1(eslint@8.45.0): + /eslint-plugin-jsx-a11y@6.7.1(eslint@8.57.0): resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} engines: {node: '>=4.0'} peerDependencies: @@ -3446,64 +3414,64 @@ packages: dependencies: '@babel/runtime': 7.21.0 aria-query: 5.1.3 - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 ast-types-flow: 0.0.7 axe-core: 4.6.3 axobject-query: 3.1.1 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 8.45.0 + eslint: 8.57.0 has: 1.0.3 jsx-ast-utils: 3.3.3 language-tags: 1.0.5 minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 + object.entries: 1.1.7 + object.fromentries: 2.0.7 semver: 6.3.1 dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.45.0): + /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.45.0 + eslint: 8.57.0 dev: true - /eslint-plugin-react@7.32.2(eslint@8.45.0): + /eslint-plugin-react@7.32.2(eslint@8.57.0): resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - array.prototype.tosorted: 1.1.1 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.3 doctrine: 2.1.0 - eslint: 8.45.0 + eslint: 8.57.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.3 minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.10 dev: true - /eslint-plugin-testing-library@5.10.2(eslint@8.45.0)(typescript@5.1.6): + /eslint-plugin-testing-library@5.10.2(eslint@8.57.0)(typescript@5.1.6): resolution: {integrity: sha512-f1DmDWcz5SDM+IpCkEX0lbFqrrTs8HRsEElzDEqN/EBI0hpRj8Cns5+IVANXswE8/LeybIJqPAOQIFu2j5Y5sw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} peerDependencies: eslint: ^7.5.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 5.57.1(eslint@8.45.0)(typescript@5.1.6) - eslint: 8.45.0 + '@typescript-eslint/utils': 5.57.1(eslint@8.57.0)(typescript@5.1.6) + eslint: 8.57.0 transitivePeerDependencies: - supports-color - typescript @@ -3517,8 +3485,8 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope@7.2.1: - resolution: {integrity: sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==} + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: esrecurse: 4.3.0 @@ -3529,30 +3497,31 @@ packages: engines: {node: '>=10'} dev: true - /eslint-visitor-keys@3.4.1: - resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /eslint@8.45.0: - resolution: {integrity: sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) - '@eslint-community/regexpp': 4.5.0 - '@eslint/eslintrc': 2.1.0 - '@eslint/js': 8.44.0 - '@humanwhocodes/config-array': 0.11.10 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 debug: 4.3.4 doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.1 - eslint-visitor-keys: 3.4.1 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 espree: 9.6.1 esquery: 1.5.0 esutils: 2.0.3 @@ -3584,7 +3553,7 @@ packages: dependencies: acorn: 8.10.0 acorn-jsx: 5.3.2(acorn@8.10.0) - eslint-visitor-keys: 3.4.1 + eslint-visitor-keys: 3.4.3 /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} @@ -3621,13 +3590,13 @@ packages: engines: {node: '>=6'} dev: false - /express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + /express@4.18.3: + resolution: {integrity: sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==} engines: {node: '>= 0.10.0'} dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.1 + body-parser: 1.20.2 content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.5.0 @@ -3786,8 +3755,8 @@ packages: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} dev: false - /fs-extra@11.1.1: - resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + /fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} engines: {node: '>=14.14'} dependencies: graceful-fs: 4.2.11 @@ -3830,13 +3799,16 @@ packages: /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 functions-have-names: 1.2.3 /functions-have-names@1.2.3: @@ -3851,12 +3823,15 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: false - /get-intrinsic@1.2.0: - resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - has: 1.0.3 + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 has-symbols: 1.0.3 + hasown: 2.0.2 /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} @@ -3865,12 +3840,13 @@ packages: pump: 3.0.0 dev: false - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 /github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} @@ -3919,7 +3895,7 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} dependencies: - define-properties: 1.2.0 + define-properties: 1.2.1 /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -3935,7 +3911,7 @@ packages: /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: - get-intrinsic: 1.2.0 + get-intrinsic: 1.2.4 /got@11.8.6: resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} @@ -3975,21 +3951,21 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - /has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} dependencies: - get-intrinsic: 1.2.0 + es-define-property: 1.0.0 - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} - /has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 @@ -4000,6 +3976,12 @@ packages: dependencies: function-bind: 1.1.1 + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + /htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} dependencies: @@ -4081,12 +4063,12 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: false - /internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 + es-errors: 1.3.0 + hasown: 2.0.2 side-channel: 1.0.4 /into-stream@6.0.0: @@ -4106,16 +4088,16 @@ packages: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 + call-bind: 1.0.7 + has-tostringtag: 1.0.2 dev: true - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - is-typed-array: 1.1.10 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -4130,8 +4112,8 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 + call-bind: 1.0.7 + has-tostringtag: 1.0.2 /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} @@ -4141,6 +4123,12 @@ packages: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.2 /is-core-module@2.9.0: resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} @@ -4148,11 +4136,17 @@ packages: has: 1.0.3 dev: false + /is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + dependencies: + is-typed-array: 1.1.13 + /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} @@ -4179,15 +4173,15 @@ packages: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} dev: true - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} /is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} @@ -4201,23 +4195,24 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 + call-bind: 1.0.7 + has-tostringtag: 1.0.2 /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} dev: true - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} @@ -4225,15 +4220,11 @@ packages: dependencies: has-symbols: 1.0.3 - /is-typed-array@1.1.10: - resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 + which-typed-array: 1.1.15 /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} @@ -4242,13 +4233,13 @@ packages: /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 /is-weakset@2.0.2: resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 dev: true /is-wsl@2.2.0: @@ -4263,7 +4254,6 @@ packages: /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -4339,8 +4329,8 @@ packages: resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} engines: {node: '>=4.0'} dependencies: - array-includes: 3.1.6 - object.assign: 4.1.4 + array-includes: 3.1.7 + object.assign: 4.1.5 dev: true /keyv@4.5.2: @@ -4612,12 +4602,8 @@ packages: formdata-polyfill: 4.0.10 dev: true - /node-releases@2.0.10: - resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} - dev: true - - /node-releases@2.0.13: - resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} /normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} @@ -4634,62 +4620,70 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - /object-inspect@1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} /object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 dev: true /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 dev: true - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + /object.groupby@1.0.2: + resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==} dependencies: - define-properties: 1.2.0 - es-abstract: 1.21.2 + array.prototype.filter: 1.0.3 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.22.5 dev: true - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 /on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} @@ -4755,7 +4749,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.23.5 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -4809,6 +4803,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + /prebuild-install@7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} engines: {node: '>=10'} @@ -4853,8 +4851,8 @@ packages: react-is: 16.13.1 dev: true - /protobufjs@7.2.5: - resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==} + /protobufjs@7.2.6: + resolution: {integrity: sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==} engines: {node: '>=12.0.0'} requiresBuild: true dependencies: @@ -4924,8 +4922,8 @@ packages: engines: {node: '>= 0.6'} dev: false - /raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} dependencies: bytes: 3.1.2 @@ -4988,13 +4986,14 @@ packages: '@babel/runtime': 7.21.0 dev: true - /regexp.prototype.flags@1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + /regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - functions-have-names: 1.2.3 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 /regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} @@ -5050,12 +5049,21 @@ packages: is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + dev: true - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.11.0 + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + dependencies: + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -5092,6 +5100,15 @@ packages: dependencies: queue-microtask: 1.2.3 + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -5099,11 +5116,12 @@ packages: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + es-errors: 1.3.0 is-regex: 1.1.4 /safer-buffer@2.1.2: @@ -5114,10 +5132,6 @@ packages: resolution: {integrity: sha512-tHKNxiY5H5YayQ0QkxOvDZl35bH5OW2ptjk04j887hHA/pbj8ggsIKs7tb+wI7zViHOOY4JQorpQ8Jnj4LMitQ==} dev: false - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true - /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -5162,6 +5176,26 @@ packages: - supports-color dev: false + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + /set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + /setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} dev: true @@ -5183,9 +5217,9 @@ packages: /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - object-inspect: 1.12.3 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 /simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} @@ -5212,7 +5246,7 @@ packages: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} dependencies: - internal-slot: 1.0.5 + internal-slot: 1.0.7 dev: true /stream-meter@1.0.4: @@ -5245,40 +5279,42 @@ packages: strip-ansi: 6.0.1 dev: false - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + get-intrinsic: 1.2.4 has-symbols: 1.0.3 - internal-slot: 1.0.5 - regexp.prototype.flags: 1.4.3 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.2 side-channel: 1.0.4 dev: true - /string.prototype.trim@1.2.7: - resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + /string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.1 + es-object-atoms: 1.0.0 - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + /string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -5384,8 +5420,8 @@ packages: dependencies: typescript: 5.1.6 - /ts-node@10.9.1(@types/node@18.15.11)(typescript@5.1.6): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + /ts-node@10.9.2(@types/node@18.15.11)(typescript@5.1.6): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: '@swc/core': '>=1.2.50' @@ -5404,7 +5440,7 @@ packages: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 '@types/node': 18.15.11 - acorn: 8.8.2 + acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 @@ -5425,7 +5461,7 @@ packages: resolution: {integrity: sha512-TYyJ7+H+7Jsqawdv+mfsEpZPTIj9siDHS6EMCzG/z3b/PZiphsX+mWtqFfFVe5/N0Th6V3elK9lQqjnrgTOfrg==} dependencies: long: 5.2.3 - protobufjs: 7.2.5 + protobufjs: 7.2.6 dev: true /ts-proto@1.169.1: @@ -5433,13 +5469,13 @@ packages: hasBin: true dependencies: case-anything: 2.1.13 - protobufjs: 7.2.5 + protobufjs: 7.2.6 ts-poet: 6.7.0 ts-proto-descriptors: 1.15.0 dev: true - /tsconfig-paths@3.14.2: - resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + /tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} dependencies: '@types/json5': 0.0.29 json5: 1.0.2 @@ -5484,12 +5520,45 @@ packages: mime-types: 2.1.35 dev: false - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 for-each: 0.3.3 - is-typed-array: 1.1.10 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + /typed-array-length@1.0.5: + resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 /typescript-eslint@0.0.1-alpha.0: resolution: {integrity: sha512-1hNKM37dAWML/2ltRXupOq2uqcdRQyDFphl+341NTPXFLLLiDhErXx8VtaSLh3xP7SyHZdcCgpt9boYYVb3fQg==} @@ -5503,7 +5572,7 @@ packages: /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 @@ -5561,24 +5630,13 @@ packages: setimmediate: 1.0.5 dev: true - /update-browserslist-db@1.0.10(browserslist@4.21.5): - resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.21.5 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - - /update-browserslist-db@1.0.11(browserslist@4.21.9): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.9 + browserslist: 4.23.0 escalade: 3.1.1 picocolors: 1.0.0 @@ -5659,16 +5717,15 @@ packages: is-weakset: 2.0.2 dev: true - /which-typed-array@1.1.9: - resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 - has-tostringtag: 1.0.0 - is-typed-array: 1.1.10 + has-tostringtag: 1.0.2 /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -5689,8 +5746,8 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - /ws@8.13.0: - resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -5723,9 +5780,10 @@ packages: engines: {node: '>= 6'} dev: true - /yaml@2.3.1: - resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + /yaml@2.4.1: + resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} engines: {node: '>= 14'} + hasBin: true dev: false /yargs-parser@20.2.9: From 9590aa56d15ff6b327de355aea9b670a672626a6 Mon Sep 17 00:00:00 2001 From: AnimeDL <AnimeDL@users.noreply.github.com> Date: Mon, 18 Mar 2024 02:04:51 +0000 Subject: [PATCH 69/69] + Documentation --- docs/DOCUMENTATION.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md index e6813d3..0101e26 100644 --- a/docs/DOCUMENTATION.md +++ b/docs/DOCUMENTATION.md @@ -1,4 +1,4 @@ -# multi-downloader-nx (4.4.4v) +# multi-downloader-nx (4.5.1v) If you find any bugs in this documentation or in the programm itself please report it [over on GitHub](https://github.com/anidl/multi-downloader-nx/issues). @@ -98,8 +98,7 @@ Get video list by Movie Listing ID | --- | --- | --- | --- | --- | ---| | Crunchyroll | `--series ${ID}` | `string` | `No`| `--srz` | `NaN` | -This command is used only for crunchyroll. - Requested is the ID of a show not a season. +Requested is the ID of a show not a season. #### `-s` | **Service** | **Usage** | **Type** | **Required** | **Alias** | **cli-default Entry** | --- | --- | --- | --- | --- | ---| @@ -136,6 +135,27 @@ If selected, the best selected quality will be downloaded only for the first lan then the worst video quality with the same audio quality will be downloaded for every other language. By the later merge of the videos, no quality difference will be present. This will speed up the download speed, if multiple languages are selected. +#### `--chapters` +| **Service** | **Usage** | **Type** | **Required** | **Alias** | **Default** |**cli-default Entry** +| --- | --- | --- | --- | --- | --- | ---| +| Crunchyroll | `--chapters ` | `boolean` | `No`| `NaN` | `false`| `chapters: ` | + +Will fetch the chapters and add them into the final video. +Currently only works with mkvmerge. +#### `--crapi` +| **Service** | **Usage** | **Type** | **Required** | **Alias** | **Choices** | **Default** |**cli-default Entry** +| --- | --- | --- | --- | --- | --- | --- | ---| +| Crunchyroll | `--crapi ` | `string` | `No`| `NaN` | [`android`, `web`] | `android`| `crapi: ` | + +If set to Android, it has lower quality, but Non-DRM streams, +If set to Web, it has a higher quality adaptive stream, but everything is DRM. +#### `--hdapi` +| **Service** | **Usage** | **Type** | **Required** | **Alias** | **Choices** | **Default** |**cli-default Entry** +| --- | --- | --- | --- | --- | --- | --- | ---| +| Hidive | `--hdapi ` | `string` | `No`| `NaN` | [`old`, `new`] | `old`| `hdapi: ` | + +If set to Old, it has lower quality, but Non-DRM streams, but some people can't use it, +If set to New, it has a higher quality stream, but everything is DRM. #### `--removeBumpers` | **Service** | **Usage** | **Type** | **Required** | **Alias** | **Default** |**cli-default Entry** | --- | --- | --- | --- | --- | --- | ---|

wVcJ_WD8!BuT$dM~hi8n4)oa zy_@0Jpibr@KBJgc;!1O#dIuesht-a!jAKlg{MV>r4lFV#Mf!(heH;kCttP^2PA%5h z2>Ea65k*~_D|dH$y_hGStgKHFI^za;)|B`xHBC@yu;YaSser%&E8nKY_ULz{KUzFkT}{b{bFA8?P$Z(?6_GFY8aTz3};2xR!2{T3qw7 zT$LWhf&}S-*dA;}Z&g(A56_LdNt!J-46l6Uz23cPAsnAa@>rE2y}EE!A5xpvRau2N zDtT-3+ONW_WoV5>HK=wP(Tv+`W}5LT*KeT06?tx8!${?3@|Vq{2G27Sp(N;3G2=zj zK;dQP6?ZCJbaH5kJqvB_J`!_T z2DYWh$m)w`oE#NRQO_5S53=7$y}m7;S^N+sA@dRa!5>UXXE?D4(v{u(t1Uup z=_=R#-Y-ztOEQfaI2K%kzlpsR1ky9(so7reX8M3V#^w~Ys&`Ve3^~~2+&Yqa@4-va z4(+loyu^a};VuEB{mi^}KH|_D&|o6)2xagKA$@&$3AB|Fp^x`!P^_xYO+Y;YGi$AS zlDC9odI3&pb+lvvbMKOYgwPtfxm?^wp(&+p7RWXBwP8P0iP2QmHu;mMvDB zpGB?Bw4pOB1O3&ndCe}a>bhRYGd-lY2D*2twYB4RVFKkp>-7W41c~Zbv&zAGsRQ;y zc*c<{H)51vgnmQurLrYYDBVKX)RCAbuZWzhSUQ!ycjUJ~ja^e&^Dp$K;TTLlPKqom z3i4~(qaui!MPD*Aw!`r6-G+M)%saqAOR_vWS&76XZQlK@nReyu>D4mV{?it=Od-Yjx zi~#Cj77MeK=`FdXlp%;&50;K8($RrybbjW^c?IIf#Zt@l4V8x5>KgPkFWT1M*+o6tJ`(gz(WW|X zm}2H_;XBdT=z$f{4l_2b};h0qAM3hJK+Cc!V3}2X=oAXE$%NwtjM;u|rtyitKPlMg4T< zuINH@!g}c+zx(n5JhfB)A`jbox$Y^T*T=Lq8TTKOWphze&p^r>q(B9mMa#ES5zc)p zSXqa%e7NyfNkaT)fnoPv>(ZuNa=fvh;#uMy)l99q^7KK0x~$fevNVUXnK-U_z2&kf zKK(`|VOScs4PL_XVwnR1uhF1#AKVMuOMU2(VKY4)>eKykfbnprvBjh&#t$ms3)fZ7 z!)uK+ZaQ*eP%|{(@j)@p=%IwDwDxxP4Qgp{N>IKaWchEtJ@?^M7!cig=4=z9VHVMc zPB{}?o0{a!>U~R2iki3LyUslw=6h%R7Y?u;gx+6;m8FJ@9Ub% zYt(*@YYF@W`si;<(Cal2!cWNkOCc385IiJZ06Ri?27qG%&0V2RtOVdV9TS%yZ63>r z(cDhCVyIQ&ys2KEnoI{XhM=IqDw%i0FrUJ|FEvvSJ`-%{P_iikuU<+7GN~~~ecP#m zPlx)d532FTBU|GsP(0bL?OwrBJBTx5$1t`8Se)deL1G_Hx+!)>+=)4yl7y;~i`;3aR1eN84uTyqJT}g<)Ah5MylM#;knpv@m zg18em&Q=+w*@HQUs6d|mYEJPQ38uEW43O&gX9~#xm<^QjBYrs7>ljr5a$il%-cpX^ z z{FK)(weV@b?f5TB>Ep?HvAc!LSlk#qX-vPk9(WS3k@AElI4jb$fgz71#KU-J<2)ntImw_fl3V@^ARG1FM&Q9gO$-6TMm z@~VB-p2YRRhWi`5kvW!!nGu{U6w$iVbxt;zIeF_ZKmK3mx5I#|K2V>g}?DFa7PJ^Gj^Lsl#gU~RjN0lItyUkA{ zjQ@omF*tv+4IGJB4L<)d$ESyH3iF$+|1|>5rBb=!V6x^*1gDu9C4zAmoC-#&V=2k# zc1^gE-cNyFvk1F@kx0NbdcjZ|3G>@Jv2t(8KM@;lp2U;5-pk0u6GWgCq{NwA=S%HG zi$xGwkL<2|&agIwJFcG(QJn4xCdr<}pgop3@oIdrb2&MBo zQWi9%?qgSW#uJs!TdOSDc8+kG!-6|q2&+UZ00xJgkI{ptmp=y)h(~2oZ?Y4orOtuR zN9tK1WJdW5kqCSUFdV+mqpQn`*jWQ(6`}|brn$0K_FxD1v1^Muc+KQZ?|+TQY6inx zlqd-109A7xq1Jm#ia z2hs0142b0hJU8@-CbIj+9i&bn1PW=4(c7_oIp*gHJ2J(;%Z;aXKO!0}S--eYf54_x~m&-pQCN<&9|7iOrEN00wlcPRJERh?`cGH4N+6&0Y~+9~4;xzRsE44m8bnquERxL`uscKg7seBpn6t=Xv_!0ed3?gajP!9W1O^@3YOeIb06{M=*0j?xH8J83S{|V z5`Sv2GNhcAM3NkgQb;|MNqyQ8a*t%tqioX~;MKiuR(~ScCUET1TaiDF%NF_H(_@QW zC{8(J62wvO0tN@b3li?W%mt4)<)Q_sF;kETer2xrZmiy{=JkVy&wLSy^<`Kq>E`z3 zlOKV2W9XA8De|S;(b2X2dK1s;L#<+bGH}s)_LMHc3#>m?Co9F5cL2%mNj;Ir6l*&= zxpieZso3y?!K8935fQ-m`JK009z_$JW=K3Y5@LY+PBk&W^v2`kJpxh&FjO`N_{A6z z*Vyc!?{u{e08iQ=R7I`ypQ4#CA8>gm-I}&?uXCdaOwV;r7KsUCGom)z;&n?P-b>KY zq4)?<-%NPW-{MJHNi-<54})kW6^ehSQOLuvz1DD2M6O$n z0=H_6FrT~o*X5*C_pe@8Z?=L@EpwuJdqf6FFJy8r9<<>PNG)e?6yj^Q>iCZKQYqGd zD+q6tt&y2_t$v%?dF>Z6H@;MfWR3>4#IYw;1OA3s1?O-+mALnzql$Ks^lpw8Gh=$L zgb%7v%s+Uf(NthJ^#3C%=0#a;CzA8gOzjQz28Hg0QNi4}P~^`*uj z+>EvkhaThq$MA&A^I8B!7P02Vp7gZXW7{i|kJHoB?Oew5J6_1Z_^IUwyOQ&e&E#=a zS|qz?lR55wd+MbI8*)NFZ;ddBMr1Ies0pmjq@C|3-H#I&SiufQv?yRu*1hkYpOPrI z$=n!sEEn)9)TLZ00vscf%s#B*!c{EBSJ;(F0_(c_iAP)a`DH$ODRq7W6=!fM zfRq0J|1pOAK9zXwK)nO=mA6f4Dhx=I(KS;v)~ZeNO(6gXWTYx43Dq?_DGaiKZ4@%k)qnFsVl=*e3WL|zaS!~iTbmq-HbIhFq{4npN;Mmbe+he zkUs;0149~8521BxCE?>GQnwQ;+_@bm2_Lm=n z-}gz_h5q6Nqp&aP88~(w?aIBy%GJKP$qG3s#Au0R1M*7jVvR5XQZrL%n^1z06XLvO zjddx-B@0!^XTB|$lU&d@+l}{Nk`hD{tJU)s24?Opdq-Y*flX1IdbeGSTh7QAxvo@V7FB=Fr`#ZWHvLMKD_|v z!A63b6hW^bpmdTX%+TW!e2^V)(oxu+veT3iLZQV#piypgx zk!Vn^hsZ-NgD-cy%2h ztvej5vh3TPzdYAZ>=c{un7elJ&A^wh3F!W5cC6+uBrQ;~r*?YU&;gMV2K-0B_4Hk5dWMfb~` z{uo%VM-0k1Lt-6_pVLpQeuZxtZV`rqRLQj`WG1GEa$%r6Xcy>$ z{*gV}&GOv`beAO06nERc##3;vZX#kR-Lv@lfn>y3Bz+njXFa21oUBoZrc0+yIs2t* z!LrDK-oz|G;plycD>!!R<_+%Ttc>_XW7oiiiWMn-VQNlkg(Y?{QUJS@_3&cPXSQ2h zg2pL^>TYO|7B`K<5z%~)<1W4O?eyeq&;u_J6N7@CX!K|EkesY_tE*NpRW35R%UbOs zrYfj;Nw(oHJLiT8v-6aZqqlZtofVz)hAaqRMB#w&A2y%WDFI|%Rr zvq`MqR2iB@tfD6;rpRdmc1EM;+X(;AG{r}{>1Z)Qj3Is(9i?MV+OE#hSuVuKi>kQ- zlC5r|v)R|cv$>wB=n2~PO5i9yS%JwiXGq%mle#G~|o{VzxWII716eBDf%o*)QZ z@1vNQU5E~2>&*v^-|1YJTQR)$JEEFa-`3saD!dDx*P6YR%Lx}fy)aOLJicv zXB8%)oT&1+gigECqNe{nAxH3xPtwvu4gl?j3w~K2*1KnMG&Ra`tK78r%o156T|U|S zEv3(fGLjHKkvF-oid?^zV37k-I^bIjO!8N0nne@ZlXdUB)%1QMG>yTNs#n1Evaf}V zr#e}mnk%E?KbFpVxaPH&gz?MnVU>{jNOjJ&8_hdTGZVkV<9t!w+LLV-S6n9z00Q8eDqNKEZ4flD1NH)4LpRxVUJd zL^IJvcS96omBimST7Z)t1tj7`3*d)y;_>O=#i5B2WE@(p&-s$)-%(A@L066I8%uE} zrY}7qJMfZ`p2yfqr?HNrkdtjsEL_3Qi6)kEqcg*sCcUOVBZ9ftNR&Upf6T%AZck$P zgFPE9te$CB9C`fxN=ZHf3%}3#Qx2nb7Pp<8bpEp%KxwLkcLzCGJ2A^gvkV$)I;Lc1 z-2XwtySWlX+nQrY?RFrtZ&uw!NK}2*O=C3?AgF{(oBiCKn9V8@Nci9}6F*ga2SPQpGy4r+x?!*Age4C%9=YYq%9Y&vfPVlbz+Fg3pFKDtp>IH;|R#V)3tiTqW@sCv_)MXD&;e z;}?a6NuS5h${70{{)CV2+g6C($>Mk4dGo5JFr*~9Lf<*OW8#;9gdM0_&T!1IAnrKL zn?g~SPYSjDC30yH1ZhnuVRAu3^~6KE0v&@>)k2mO{`a-LGGHE%i~iwEZ0U?9${h|9 zn;e{lMw2!2f!nxQai6^y!Uwn5f6dC5gI@Wpe@-7>-bD_O51|x0dH;!qA>K;sA;~1z zTvWzuNNecbERaWe9}o;_Eq^R~L!NP2T16aN@e)~sI6<%~JCf3^Wig-XwVh>$(Soq5k`n zAd%2;l~5%yjAjdLbimBPNuF&X`fns^i445Ib5(+0b%I0+{bl8crbOaIU?^uAaO&=* zyf(T2juP*kN*kx{$s5BO1Q~o1Ia7pI)8gGP-waU=t72EwGVJU4?gar3Ox9y{dx7Al z0?S`EH|ik{3>QK@jaC<9!ItWKiAF2FM&M=DevIB*dn4Q`ZB+?tWA9namRE?+K7T@& zWk=X_ruONR(XXG+KTOz%nZOBo+4cw2<)Rwq|0RhAl^ef;P+MrDH4P#ska!E#dLF@% z1o2GO_1pAu5y{ap^Als{zX%Boaa9RjtZ$;$ zb(;fxn_Uo=j;{B~!7Azy`7`EHe{1S}{cI@)ASE$39rU*A`Tu7ZLm+{3nT(PGhEAZj zo>rv^%qIzm1pN&8lbVN@$tcM9a}wG5VEN8i5|y56 zTOQQ~$C|steg%g`XzUjbgs@)LB0+K~%=!1DVrqxpSZS6>Lmg*+O+2LlpcCeI#}O1r z*ORTV+t%GyEEuW{okw7NU`7=Yd4Si_5&`Y0H1niXLd|Oh2e3|FSCGmI0Eokm`5l!e@EbrKOm}iA6p=bS#zdDT#Eug9kCRj=^G5g+_Q+(OW zSF8{^d%*J^PffVmg9oYnJ(7T7luSj_x%VHPl@vqHFm0{_mC{0Dx9#f+sDsua`bE&? zeNZK~=!{SCToaSX_(aqdVOJ1kpmSN@G4yYVB;V=JfT=NL^>BEy#=`7PiV7SS2G^NiK)qD9N+d&p6OdnCMF#E!225^_ zu)&F0h&K_^JlA%Tge!AgA!o+Z!j(}&6~6HmcOyxw_BN_Hl-@7`;UqE%;J+{_3ignD$)?-bTo#K zyO-%G0`pHEdQWGoLSUOl><~fLwF;oOk+uZyQZ;n$aO@9$aA*&*TKtQjghy_RmA@DR zUfT&T7BcGFi@2TttCx=OZ#$*;5j^)DUi9<=E~rSPlZaRvoSTAHJ}SQy_qVF7#)F{M26)0^I3b9PusBBZz{yc+gX(s2 zzZYO(gocD>yV$-7ctTHS&0RI3BQf&2DnjEfM89Jrrh?65@z{oC(&J`YYx(`|wlNzc ztxy4PhSE&s*ydW~ZZwo#OxC!ty=;u}JEuhuF0Fb)+9HzNbL*)>pl4tLL4hM^A(DSYPRo#fD;9B_8&?1PYgx1(+}QKT>dr)j^6@0W0?eb2 z0ZOeB>?-T%72*q}{qS1^fS*IdXpq5^Tl3e`*suD6fT?%W3|fXgs@&PwdZ(}Vq->E) zyqcDG9{{BSzAw@)a;Arps0YXM_(pXTV&AE@(ucz3CS4)?z_gop&(;PtnYsVq^b=UY zx*7D{A?@A8FYfF?B!;6l!__04mp3@DiBR2&CAHDZ{}F9&vEm;eF8HkSw~x5-%DBql zMXHTAKD>M+?t0IcpJ#QJ9sP%X+PCyOz{4&X)6Sz>TY388{nn>hKK;*H)oXFalw+6{ zAWj={uYWw6U?(;uj%oo8PRy~(BJkubDf@K>gt%Q3MUg)w6VJAS7K%)ahuFpTCRN=Q z#PH$_XR;b}u^Ph;)dW;6ZwHPkLUyS(F$%3#?iA`5%EE4RkDHGXd;ujqW@Le*UEK=Q z#et@1f2$9?a?y?BW0-7C?-K}9S>R9a64Ryv;Yq_X?Y~inXgk}d{!0RDtxvXzGrJ&l zrzRy26cSxuC92nLwrr`Y%yB=N59+Mn=MdDyYa#2jGu7&vXI*0XJ}lZkytEb#x5J#W z>HdBPU+c=?sa@~BsM8K~iD!Y2KYzpw5pjwQ)G(}QN~ZlP7tCL&2W?#ZhO%8D$!xkO zjWh9bg4Sx#|2P2ud10W#(7QjLeqf0d}jgOI8@U={B~>?avuH%BOG1h5VK8C(QoLOkJh zVpnkwbU=O1abx;9vTnzx<*!ptCdTy)SLiB$FS})B?*;%$S9MVZdZcdfW~OG&{QGBz z@2US-KMD*h4A%`XigRkNcKkir{UgdSvG%7PtPJ7C7~*Mr<5V0G99-PVI3ke>6&I)z z|1`S*mF`O%B!!_O9La5znijd&AblNa+q3mfB^*4DbT}gbZlL_OXn+m^N$-J z*NM_uTbUO;7tTE@#J-c&XAvKi)?o{dB_- zEf{k>0}E)#K&YS1SfI?d6eZZ~IB2vp7T)UHYaL&HS)lDbVu@Mvz@pu7J4?%*JRnG6 z{pj0*as|ngrS`YAbT_^mH~PInpp`Z2MIGZ$AFwC~F!BEK7u`q0ExwYDSr=Q5vm_;*XqyE9#d3`%j0zGh6HD11BRoCc&cdF@JdG3iqjSGw}h!h{t|E zRGMiT($45^0$f~)7dUM5`^i%&qA*#9Ar4rv5AnxKUePK1Rlj#r((AzSBo$K^#T)Wd zosMLw&_~JIf|}LC3ej`x{TqBqPbo36>nnapVtrjqHFcw+;PuFa3bOQE!`S+#F^R&# zDc=|xoR1?`ydmOo+$#9y=3V`-uUHAHy0ew47EVOs!ej1$`4lD#jM=Q9;x|~J8ZZ_w zpKa8iGxtJkoUVIQuQhcIIx(wGn&y2(;4Q-JEt(OH{6V{f3N+>Aj48&O(n+ajJI8>no*ql#3@Wuw-ONkLnXAVuQ4{5! z=4{U@oRO%YUSmT?T@@He#2iY#%}LF>XB=6YAl73p!N#CK8aE0?eOCCNz6PdaPXh1t zVR0_JVzFqzGxH^-URa{o5QsZ2y+P6}&Ku|Y>Q?h;XrAp~?iTRx?Do#;zy$vpV>-}f zyt0u7(4Z8$MT#1%QgLAm;yKjt&j6be#TB2YBSKsNNTn`(eEXT2L;bc*HTOnw95nI= z#CjCAaij|5?NkXn)icj~pbhI@GfQDD!Nb^YY(*yx_~{T?(a7XH(V{;#ujJ-*az|N! zLU)|Z>V4&cF!o((ot4BxC{n+}EybyY-0@tSRj_4(A2kjr1x>6rsr>e?@WjZu<#tb! z^qs~DTP(978qH@xMBcY!_{>s@oO+>6qRwa~a1%kCz;duAeJSG2=z9sf zN5)xj7<^+LP}I8c4AopA!u)sK%m${@pOuyK1Y(~D!SWXbr56a8c4oz>`?ExX_N%~p za=A68?8xYC{7*y&bD?`3>Ue}_BpYiRRG+1#P-8Rxl1c}hIi|9NY)}u#$9_V@VUE_3 zShd{BB9yyD=YGA`IE5NH=zHQL_k)pazohzc6kqTBJoCJw$$+bBz)# z36vS8>6*zs{t`b7?RDI2KyH@8ndYmI?{HU4*hv_W+l< zuLjM%{>bQD)#~jdW2t8P%-!=3KURzum{h>T-<9xa5ig-r;WA!20h zF1{i+^T{Fk(1^Hs3KBT~Vf!$Sq89PQ>_2pxt-)mjNQ}IXo0aquz=l<+ZBSaDMH}DQ zcV_8@d~T#5_xH63m@vZnOJnT{3o)#4WJqW-VSrg)^Rc0C7FrKX`fCd-vvU=tRkBm$ z2Bg52Rw<|5pyt20y&j{zybLI9%uxPKBO3^21#>$ZM}})jWd3J*4%ts6B9YIdS4Ab2 z&_!Gs6h6v%;SElym3;owP4OfNSQ;s0o)2nS=*9I^!H7rnv}HgGv%|st4M*5$i{q-U zMTu9N=avze^TUWZ@&>{yt0{2&5;p~L##MZg!AqS?rMyB9>Up0N{vLy^swx}_^RI~{ z0IzHNQ!cFsIAsiKs(a#4kKrv?65#Y3K`vZ(d|BL81%HNUmnGOsJ&Rk%S#{R#@p3|s ze36ba$H(e)xt)%W{nae7jl7D~p-%WcrJT6B@cy(OsL81fTGn&nMaL{jWpVDD(SqGg zMOjb#CF+bDwKjA!H@GemUfK&mNX0BvI>)1{GBrC(z0<%5{_%YelfutXZZ219XQLt`C?yHZVgsR-h9?>2%? zl(?xlV#{OVl<8H0S#u7unQS@OjP);;{Z`6?hSq=fc4~|ehavkSmP1JSa6O8ec+*>I zakHKrkT_gv9Z8jM*=q(D98Pn=g}g*TIKJ^&Wr2jwGYurGFKdNB-l%&XYjJPJ%ukAe zvV}Rjuxx+}ob7UO=HbSzlA`3+-={~QLznWR`lvPs!<6^bOi_qe~b4n-!XF7NtXV<~Ug|pF;FM-GG}LmqRbQ5m)H1 zwZFMl$j8FIt=X?^w5=lorYcL%7wpl)vOfVdIj2h&dmyEF!O>Hw7g{)6THFzo5|}(W z9~Z)_Q8Gi4(s7H_7@)7dNJ}nmQW7jgj?#@vC;}-7!XZ%?15C;iEZy~wYVN2=2&vq1aDOFzoj({sK z;PNZs3)%1taOu*03*#pcJmQvgx-KDhSR#P8RLq9tRiBQ)MU3{|~&4t{kE)lKZe>olvbD z?F8(DHGN)O8J!8g0nnMtaP$6CpdAgSPdCj+y_ zi`Vr1C@pyN|4pp41Y?p}nZ4aWSe#APX_sLTx0NerdG|hmXuck&-1+J%oB!Y-;>U9s zKS^{IBWp6a?d^lbCtMQ~H+_71V=LnZ>j5uV6E>&PTe{m^T?J^02?A4X?ul@+SxSXzkN6%pzVO#nYIsW zhJASZ?56o<5?lE4_qhf~%KNij|Cw7ur!_!MBYeaC)yJ0-ZL{vsFMAplhpq`7Z>@x@ z;Q;9fwvvkp+_!XZW}-$mYchPjCiGj5RAorGzM~b*n>M0dp4*Xc>Wk{-_PYfYYW@Ax zJh{fZV8=un*&onvitg}JQDrWQ5C}byc>P!%s1K8fkey{-laK7?JehCnWwTf+F2n=@ zj)1kZ4cKfN;>I1sf`h$BbBC=6J|AMkVU6X$FMCOP%c7Wa6X%LSu!v3i;p0EhQISq; zj0c2``#(5EvAtg7FAT$sc`z|e8Z1#yTb-SJ4Wgccr+t*XXNT*iY3l!J-MjY3O7iX& zA8Kmi^{~NXRqLPQNCMlL9t!cP|GR=}!5VaOYRU>ejL7+t{F%dD{4~DKlLh*xncc9K zIAwMu#T+F!biSY5e3%3!gMYxdUuTbutkpie7N0#K=hZ7P8QkN3$fb~fP=Su+Wq>Dh zmcW&@QL!(;i@N;wiq~Oj5WULZFJO7#xMS`)3ql=oLW%Hk1~oNBRXz5VNnoaHWNB}T zi#-6REa51o0xd#uge7RA%ynKut;PZY36qpNplE;VikQEbiT2rC&83-DJ+@HX0|2fT zb_qv^xhG{Z;_*iQzE~BvX{%`J9QW~(@eb?U0VFXFLNt_#3ADY$dHK$$L2@ddxH?BQ zNpVVIRCgMSm-{6z$f(u~#(yf#{fC z10RL;%AGB6v<1Ji25OmP*k#2vrHIQKRtA#fue^(@x(kQE3{=wm8st<>YE-pDjqb#) z1~^4-$FFPA5bADG^;J>2i=6M%4vDuSNGA{N;OpsjTBHq0)P)wJj)}!qw2B%n)a_xk z{pRBmHK$NPHm<$GhtI|^fm2kF&avOQHrmdpJF2jU>i(DobJD6_-vTeI`2xu%-n zRSq0O`R$jg$DfWiSA)x1nCuQ*uDY3jdaSs#os?91DY$yXJ~NC$;DoWe^WR3f^<^Qp zvmRj439or;t|yKyx2G>@=zt-(5RtzWL!iRc19qOQ3I8DCu<*s$?yD z1r*#KW_7c^LueF6(L9Gjy!lxCa@UHUk$t7e6VSbiteeCgLx zobtPnx6ZwnUp3e6+8LnGw2ew`Tw%)}3VI0t(s#B-+u9iy^@>=%^ixZ2YAWo%k&{H)fbpnH;&D07V$NH7(ROB@Pt;DW@*n z!LVHu28{ipnC`KiIt!scAZtwegh*hx3EeU=+rM+M>U(aEH)N99>{3X`O?+w5^8{#_ zrJlTr!vt-LnMvY81YnfrG8L7*Oi>%C z{si9K9gQL;8P-r)q}70VcZ6RKV&wmVDKcx(qv!=PK98URSIAf>Py<#R;3@6VF0sdH z6tjMhP6jzgdx_GGziv^@Wv-Z6G;>LjXVp$&9cjD5^sX$`vF-WzE}tWNR#Vh3~=rNUFD@VAw*1Ql7jdIDf6cV?U7 zCycwh{4Wp&LMf6)yR$l!U90trv5R=Z5yMNK`G~xHZ55X6>Nr^K;vd8MR4*2ACGXCn z4mdj$GP?vJ5|GVUq>h(6-_k)ixtfy^WT?1Go}UG;2NdWuqTQt!)lwT@-6UV>aSlpV zCt$ddmpg3Mv940V+*vnMDy$Bdk^-Fz^t}%+2&rBhXy^6Cof`{2x3o8hXF8Exb$$TKhhgztx2{ z|Ew!lqXPApwVKf*0vRd9rIG_ufg*`dwzwVi!gI|dEH34D$bM3&8m}ukcknGLK|`As z-|kt|?DHYHiIN`i?%{--7`z~}-y=|8UI>Li{Q+X$#;Lr;LRNmE<(*B8RX|dr=;A}Y z9Z}KeqTZ7rw^(R^G*SiZJyhrS}x7{IEbY?zfMtbYc zy-+t?>@QblhCNNM3h*6mbx3N6kx15qCLyVw38Ht-5_`R!5o8kHWjei>zttn&-*lG3 zs6SQ-Tv^hO_kjhu^JeiRIybWX!C!4)IPtk?Hv}scEFJkJD5X=RyILZtVT%-a-b()F zIlSuwfcNZ6FVw+Jmf%7=ECGs2GUk)*Y6U`>_S@$GFF?@0&(QNNK~+WJHls~8>b!73 zqd~&r!{P`?n$mb8R`^vC?&<31#i6tEi&w~sKg;l@i&6qSP7eZm=98I~#uD!)ddx7g z1A#K+7!R4_C;lQH8sfyW_-w2Nwq*T$vk~rXV|BMejR$HJo=4GG;&&mTm7&N^ajpnj zL+XFx%N$NLgjRHSj?o7=QnBj0iVzU#$ zgq2@AV@_IH>@;Yumskf{cb5MX-tmm}1B%X})&barz|Z;uz|;^Mk08Zcv1TNF;4Xla z6Hpm|UTW&6b)TX%nL2=x=_u-6K+Kx3#!*<63~6cR47$g5NV}ySi74MzSOAJqiSHV% zz33=`=70iHOezJ=`VX5@ zS^0kb4Gc?31gG!tbg5*x^o-n}%T5hIhwtbG9oDxU8pew)rE*PBmYa_x|68k8HB(fy zsEtyIs2YSAT%NbX7ipLiH2@@;USVuL3+PnGUOhK(EXu!Kln>>q{x+2NbeUN4>cxNy z<|~-yL#;@5cUyV*8I+@ufuO-lso%@XKzStvrc1N2jpB6ZrAV1^F8hjXcGbq2;C()3YLOB`%`x8~U-{ABsTC^gk|`??4}qqa-(wm88AW}sa! zKq?nuWvO=+={u2uxKGGmM)D&W!@{0qC-MItJDsm*EJ5YW2-&GZ4kQQlYE4OJ+R3}u zH0@vTWfrBWoz-m$$uAaBJS(`0uYaUNA#tT91rd$FjEbOBz`8?) zbo|rVWo@bez9QoH&;$jVI*wUO5tGE$O|fZreI(SdpOc{kCRCO2*|9D(yz<-Ot|O*k zlWe|l7erJx|5tiB!`daF8G?UBRogdGihDxBTP5J7C6b@djh9I(-SsL9gmmdh`@8`m zGK&f+WkAZPNDBJ+?_je-fN(${tW4L=A6!e+fk&wy7Us_O0Z+vp{l!1o#gF?R%A&$Y z+d`9jKNP%tOEzuH{&G0nV&KVgEvvy_F0^z>LQv7=22=-IQeG# zT4Aob#D)j-eB7AF7)yM9K&V0U!}6>qf6?pv;r1afse;0#Hd84vsDoR*h`vK^zFiFRl)MXGjj)wZypm=Zuduc}VfJP1;p5xzmsZQS<4iAJcK&3b%*8 z#;3M}(Diew0cY~EMHI+PYzr9*Q~AbmVj;o+V-dnSt8+3g&E@N}@X2!|Y#4zH1XhUc zW$8tIx|DObsn8(UvYU0)gUW$hMnLCHebD(3waIeqv;M>DVea^gjA0Y0=*Y4_#!N*s z)(P|*c@1ZNub@VV@pw6mdX6{%0peV(GrYbXbyYhzkCFejI)oalaqYTGBdJ-Z{xJm_ z&k`)_G7-Of`vyPpJM@O6-e(;oSHG@`rj!PdN>cqbo29co14k8(ww_3e3(gPtfoIa9 zw^gs{w`OB_FHzK2Zl}Mz*24K%06jeUXUez$dGT~TesTveYu0DMT8WP|syXg!r2 zW;t*Z*D*n_<|!r?P*f-$qMfFJnG{O4`0Y>!E<6{nv`uZvd-Bq^_G!92XPHimR~r7L zVlF0A&uapCbwP9=<8RjMuuo!wEg~EPJUP*m2|=q3lw>NMy8AjzFOt+x!bt!ZaXx1i z*J^3AOIQxff5D0o38vbUCa*a!qQHTxIqpbXsTumR5QsFmI9rB}!RXz~_Gh}`{m^Bt zXUvbJeL|p(Y4vL~O!g~%CQG;*`l%n<0&OAxJB$IzJD*!K%IJ*&;0XgpalSeG z9*W%TfA=NI;_Nv?Y?!lCG^k>@n~XeblDC1Sw`kQU*^hRVYGbH*h9S1H7d4l6ZBuKy ziQWZi(_!R=SH zEkO^{_hAOCA1+l8{YLA7UnY~aL7ri`Y$E}_0dy8}-G7zsPFwL$ZhNx}6?&+-1a}^Efg0;B0Ej_#7G5+h z9ygl^2fw_B=MNzB>7(PHtr#gxIMW)#du2XM+|_#Nt5 z?jC!+B=9FeDVMn^qdbA=8PkjP&gO4G>;gXF$gMKzD=I`>K5P8(^YQ7f2x0NwaiZfT zBynuFSU4F6+H<@^Ah?$6vZFFfl67yhUSEE9wc!0W{=7a6@vcK>_*vqbaUSlOhNMU4 z%}_9F5Kt0H_9I>Wm`EoDkt!~STNCn_Y!t!y1EeL-Y2NW&J9n`Qc)TtoFK$8BdM3wU zv%9zk?)v8!Khhsuqt1Wq+yJWQAT!Y@6XQn*VN=Wve+w%+J|AeuPM_OzRig7l+4t=) zL|reOvw1a4cye|f7FS-RwOQ>YeLf~|rg0G{&&LcrAB;;lR_J8^cM0i!1DD-ni(bL}cJc!U+avnC7b?dMpoYZ$^y=oTmSOOY%-k1tMn&n_ z2j!}e5zWNW*B?6%=leP3j|MHRH}GD&3|Y;W|Gn8R>UM!eZol{?Ci4v!`A$n1_L2jz zy{iNjVb*M>()yo*ywSa>)y4KTEsNrrnZ`4jv$*I^2d8=DtmRI`%6)lCCD;~~;r_h& z)7#uNDp0~FlE&^n89H#e?jZV-&CWW*-ww)BMK%$V%{ZY$vKtp6)pf&ng6!|rf$8WC z`hnHHr((&?rrl)NU?GpxXnV5C5kS1>5)5tL5vI!CZZz1fN_>7}Pn07*3ti&WaHatJ z;i?Fl=D64O2L}xuqZJg)qZ@76u{oL_yO=8^({+2x&o~(hJ|`NcX@1ltdjnc4vVeI| zjy{^Au`d?)lw$6Gj>w%?uC2t>4RJOi=8f|h3BIp8e?uvIWEqTpxmz*n2rM;x2+_Ia z@I0lA;Ds(AlfWJhL%kM&U6YQ6u0l&(G-@wocdoa@yY3=R-XrA z1(Cavxwr)e&ffIwuAcsyKO~?rEg?e_N~i*CZ$O@G~+3tJ=)i#WzOvES6V+@t$+@FiIREel=uW4N2sAQ5Z~LcQm`)Ntw961d5#&PI^4AES;t z;_|-&AHu6lB&S5e8G2AdG+kN5l|=@tDiyDMFCX?ikwFTl`$3x8>(ahe=|;)p!>b2wupLNZD`X;JluD6 z`wkNQpI321b;9pRX6&1*0ED!u>T~a1Hs|AVYl{Dt1VDon9EH_~Rk<=|Qb@wV3wOVo zIa~ZOh9`T6Z5#_+HYU)P3o!C}PgnI2Eo^iRR27NzZ1*AEa23p&0Vl@TaUGzk9TQY# zxzFT|XB7y2Mq$eOS^O>&e$%^*Ccvc5=1xPr;X*|9Luc%FiHD2g;(Oi^i1 zWU#i~77d*Hv#(WDV1+Pbwgd>q@iHb^7RY8|-SrSNvtNQ-t{Jb3t(o>`6%zv;gca`s_|3gi)mW3q?J65@N4~+OLx9=@ zzu9-SnbPY1a*n(xL+jXnyj@rB_t^#U`=Ev5u-@+kyL|Q5_zvm3BBwox&ZWzfQ3yOB z6P&W0z9|M5;#SFo0Tj{qcgN_yaU^ZLA7Waw6j5+T zZ9>s7VFVoE#)DtkMPY;fyD9JhuCzk$F&>Bq9pu3U8#2`_sF@wpz2&UA`W>rJH0=N1 z-NjxsjzZKUM7r0w8FE1xVxw^HxN}ji&4Atnn{QJBc#KucSO^? zFcr7~o5org&3;`>tQ7esD z_e{IT$|#Q5$Zd{4p=Sp4kWQ8*TpA1Vebz-(I;tdQMH~UITKNZNH*VL$iM+@B#U%3#!p{be|w=1xUJ|*AozayUWVPRf<~`TkAgkrcmFN#$XQ!3&}_FriqX~8$ai4h@m7jR=Uht52W_d zA>2kna=1vCD!$^(e4d^>Ct_O%pZJ(`Zdd!MK6VBJMvPR{FxwWSca9{qrCZj;#frvq z=`RcWN3vtB+y;&?^9W3C@t{DLM!%%gfAyDnY(Br~+C^>1h|apw$bq2YN&enhW3!Ue z9%Uy%Yv79}<|Z$J;_s23#oU2Y5N##0HK?>`6RXidjo+8njr#*cb_Phnas6sHsaqOY z(4Cti-|7z0Tf3!rX|HjHCpRI*_Y$rD8642;YOrD;~ayn$;ux(==b#{ztZ ze=b1}oogP67~OMP_95&UB)dzwtI6M(3Cw2s;B8v(FGYlsfL6b^GRTj3i!-ilSo#Z% zror4M@M0QkVYNNbsw=o8J}w$5V%0fQCzQhhA2oSdbHt>xZgs*z5gaiq!pqkp3OaN& z?8;+F7Kx8{`(VOgAtZZ1_&%d)$YS;qfNMI(O&1jVd{W`TGwe;HmQ;&BBx-qz%`xB! z(Qxq<;-m}vN7U|ZrcqK5R?H@Mg#Z~f_PgnME5_vvy7IHFZp z1i=`aiPzRsWXY)U2@ig5(jKGj4XCl&i8z@AY5w&6ivrEe7@!mqOL+^xttfJA6f#@i zX2@p^?EFh(+i&F5?th{4=yM1PnUKD~1_G$=A`v>cpTkkzHgLYBDt&N{tpV;;l4g{m4@gBDTw>nO>& zO||74*N|qA@0vz;(hHX|S+){%voKqba?DuQ%!S)1q6661VsOa6WYW%(93{IH;<`Tk z?5H7IFcmBey&owl*KQs?DCGpsrQZNuFnZec$QGgAN3q%b(uLZGWMqmyI~}#E#`nMb z22Y~QYa`|(H(^EmJJRWhLe1LBlKn9K*9y)zP&e5nE?NY@eLRe>k*OZM>P`v|vFdUH zI)~EH;2H9aOY-Mz79V4I@G~@DCAV3&JxuOA$_6K%%vX_JgQ&XNs-NcUY0c<1F36pP z$!-ri)?3XIq%`YJA|)qT2t|*MnHU5%_FP8}_kd~9xLy#NI`6$>->M7zSIGpnA{L_k zui|H!_?>nYK~l>h8Y6jU)>{C*x(27XdhI6HPZiLjC{}D4ToTfK zJiB;3?cXzd07)ra>hQocwB({_1Q}oZeDEpt=0g6Yq<(@}encQH#+->_!Ld47Nc$I#C*GxDr;=u_=w3kKD~Eb*$HA4pHLE&cu)11FqUKT`gMt~UlFwYGf(580xJPVFby z3ZdzZ1ym8(Tr>aJ7`2T)$RB1;bTYlC8s@Vo+iFKnQn&UB@iK7R=Db z5FjV5Ue29Z4&Iayu!J2@%FD87g@~4<5oyJ`pYw}!cSi22GO{|ETC-8s!}XSOuXY2P z!Vh!j=-0Me)%3Fbl$6{nk40QY`R6DCpmxnHl=!K|8aFjra@U~eql+3kuV*$o8PTJ- zjf>*1;BveCE&?|lZc)iJ7UGD#t3+M`b5&nNC?~A%A&kbm*S&So@&oE%8chG??LJL0 z?8DmpaZWV6fvsx8Iyeb}wiOZ_^k556wo^&i`3k=ZJ-Y(RhkS>Dd5M`G6bzl)1Er8n ziP;lcNVE7nMgLv&WT#c=|PQu&f>qsyUEcH_7dFUs}Q}pKy(nNrH=IVIR~~l}c?pM1Gr@k?x`&Ns)7x22)%y zWxbm__Pvi-It&5Rm*;M1*u(9@_iZ;F0neqJ%}&IqC1|2FtzH5eG1uk`G|x@GXrm_# zMziFs4J}BWB+Yz2N#2L$j^9n4=tB*eyNCd{ygSLS{Lc>}GpeLdi~11TU}G<=zz5te z?VvY7l=lt7+JGgJ;2T0IynOxuh!Rt7triI}BAO>A{}8z*!L#GAK(E`F;4)jPEWU0! z@?043ZZ?+J@TZK%Yg_YCZLmpu) zcfuw-NGSoim21Rg0EFIS8AmB*g+p}gkZcNC^T3cSWqB!~j-@G7xY?b%oa<8&ND>B`1va9+Rn3o0ET`_iUX!wo)md@`~mk_%rTtnH^Ub@cdikJ7qv&PFO~e7YKMdT#rP~-)aO_8o1u=OaSzg zO(+1~-w#!z9L`A%1YYZtbWzi+58?@VniSyI&6gZrjfAvUwzW#P{Qi;!X727z`Y*>z zwhE5db69frMkWZNpe-c$COEzyBw1r4rUXtmaaTw;->EDf!C|lcAl{E(fQ|)=&!LXf zhxf?u#Yh~ZN`D4D0cLf{+aMgo8>IWfBz5w+mB-NeAryZ|gVGN|zqPv6?|;DcY&{BExX zIe7tPhlN~d(z?YE^tsc)y82HJ{>c0x|Dv*^_$)p5sCC!~c`Mpgy_6+%)T-bvuF8I( z5_~2-c{^D5@F~}Y0qa>zM{bS;dD3aoeN*dpKmuzMOmf2s17aK7H`#bPAl`Bud|x6} z6P_n)yL?}t4phNqJ|S(;z^%j^6L7fM@Jr>Pvg}9YGwjxvZN`g>b6%7dK_<<)S|?S5 zkMfTEhdsQEfBxmBb5GChc+v}P=yt)|5)XUtY&Z}0#?8lqdc9;cglYc{#woe1%nk$Ii|V^rQ)N25wp=J6+ZV?Xq=TAthgkIf)>J>j*a z&Cx;ylO{qm9=5n)NI_A!S2LI?~Bpj!85{r)tOTGO% zg6Mnqak8v59_Cl(N0XfI11uQWs565?o7h^B=#>u1a{NRBwKy^Vsz?C#0YSf;qo#Ry zk!?(01J^!7Dl{EF{6T?|sTx5|pfC*!qBP?3`8LXmeUrfZAJwMIr`O*0S@|}^CZb7d ze9eR~w%t8qq^GmU z0lM5wv`9u5a4^+WcuE$k}DmV(JYPVMSTLS`MK3P-qf74WIxg}H#s_dg5CR%^Y3>$8ZI zs&nm*+B4uu!dWrSwW0Y>CyE^kuH8*L9dafU8k% zk1vu&Cd06sD>PYy$il_FU&2xO(Qp`vlEcef24Qvg2s{-K+cATw4m!^o=T)PQ6@+Jq zr-ga-V@q?WId~q)&?!~JEy|U0+@UD+$Etdkj!JwDW{2gp+~YsnI8ekLY|wp5w)~>% zlRtqAj^iQ!4u)TzpC!m@B~T2>}_LfGuvWEub7Ywoe;aMp({#!>REY^Q$@0{61$$Cqf^nA z<~x%;+-1uXK`2B2ne_t4pqOMqV=1t-a~WX={$TKaf+0ELi39yODUC9UK$2lCO}%Nm zS7gT}HBtsTY7t8$14*M*FJ)+|aLBG!wX8nI!`S$fSji#2+_cs-bc(FgG?iQJ;FW^7 z&}K?z-4!|4O6jTL2QVfIHYXNtHJWr1Fse=s9mQUHZnKY&@raufM~3u1YVbGZDYms` zXDbj@`VD}bSZjb284@ss&R-p`4|d>I>xg!XIPS?Y(KZ|yY2s|vEdPPM+IfG7 z^*c*qzZew3-WQpl0mgo#2_z7l*P#V7Uf|SD;e&O}5YtEvIEBkUzhCVwJ)eSH9(|=H zkF5;T^-ho(`J1qnkgD|xIiF~WqSkR*6Mt?^&aV%~bLs2c#snF}U7h>|#Q`15UzAcI z6`M-2rv=bF+y~mQd8PAhGbX)>)0*l=E~TQsAa6KDdJ9;>KY7~I(d%_)?Fuq?)?Z!~Ix<2fN(hfOMbEwDiAb=+4}>r`#s=&b!dO3hY@L zLf?Ujk-4vFMH&(Q6yrGP;k2^8Yo#nNi97k>A7}^4FUisMN$&wJ;o&C6-o z30q}KW}Whl-1?&0V3|REW52dYK~fCCQNkvHOdYA%)AA207G(1~oBnntyut%Ss$Gn} z$0K*I_(qhdlPmUkS~cr(C>{>ilHgiKqI#lm?M?xwUJ`dX+R+ufmWiuzJSin7hJ{k! zP*nxNn|*rBz^iOak-AiE+|wH%&2?VJA^Y@s2XY3S6GH!q_rE+cBODdQD9%lZGI}oi znbcJ$zmTfu#vEWu-kwYlff!nfgvYou<24f8_( zTGzyG(Yv^fGDw9T=$@KpVa0z!)7#ywmsAlwf_nZ+SVj34PT&~gcW55YTQdKFnm^Fl2==t+ z-DU^-hwhKYZXvf7^N6P&Nt2qur^Bl@VKvwxdo3c^*s!RIs@ zDw1X{Oplk~_4p~>n|BQ|$hXgCr~aBN=;5C+yhHHi0~n*PN6+>eys;4#NPrz9?b_^5W}8f#PKH1}A&YWo`bko-#XG`IKcO5e~$E$kOT&n8FeP>G8zy<>C+M*s7%Q_uLvN(fM3;Vgde~&yftqZ z4)mV*IF-sKgfIqrCr3vNihaVfF6&-Rq4I!g50%8SzK|%@)SJ{qJ7-VBz4uSp1jQQw z2kVfO0{7XOuYR&E5p{86YTQ(v|BSIm_UIJ_r~&(F%vmkza<7Aq3Zho^*koU-@u9>Z z51UX&z1vc|6vIkthK><(Q!%04l?K=>4IhDMBC*yh29&D&f3*V}^CnEbx=wct$){Hb9ocU*r) zI7s|XPY`IvZ;6O@zzFsAv+VwHqHb}x-VPY50K({xI73}P(fyElb#r~6HnDlr@~lzY z(g;o73dYqTggpuM3cLxO#}0kvAR$KNi8?<|KVxd54|~4AOiTBBPY8F~9|(ZYDG@Ug zG9`)j^kZ^0fd`@^vwLH}4!age2_2;;AF}&8i+;)fWBFTjrBa4er+N(>*WH4&EhH&K z=)yPe$Fz6@u7^jT1*3b|b#pqB^BIS#V8QrIssU-XxNui>!6siDZQEbQe4u7cN_=K5 zl_4Kf#JKV)(ZThegnPc*leO&D7yBfEo|>(t^KsTY1f##nYI(ne=;J)vu=BeOBs&-6 zd2GzIEgD?66(9d0ewfg!t$rj$?9mDYiRG(=!E?Vs3H49SU{@>;xF_ms$n%PJ+C0xJ zCBH`daV9k3LsB{OvtB0*M9M8j9;cy@RDyA6Nv5xcSo+=Xr_PGfqU;Z*G)C;G=>H#k}23spPlE1yBhrsy&c~hXFDFgJ_8^fWu=y|no7w{rloP%&8oZI z*QGR!P>07SK6QK+_KD}qhpXlD!c*nu2ZHEH>4_4$PMYLyO1L%Gn^@DS)p>;iF9snm zn-($KP-zMPl#Vcm12K#TqceN1Um;xfUeBF314lFrV`_E4>1y$CnJ5aR$kKp zVXPCN&|~Y!&yFWN1=`( zAItzQjQQQTd4GWJ7~^`_V_FMrs#85Q=|hh?ShK`iIJV-C#g>=)~5?uLwU(Xz(=A*ssSZ?NeS zT|sOk(|$V5Z6u^20irnzQ1HiDzK_kXjydbPf-IQ|E#KXWo$_#{lS`km_NC`C(#$2$ z0pt9~FUkd91X4`EB+Pn215iu5p>gK~QXeI^jkz^Ed&AF|MMc5yyYT@Z)Re;pDJ1Jo zkq`ODfmRXu?V*PW1w>?kFgA_~E(h@Kh}YoVU3V2-%Ux zDeL&zOr%{2Wi>BmxtFKpP&;sqewtddiCq^11v$q)O&-v- zYC&uPG7ed~v6BL&K%&SSL(z3PP!pt%Kja;vRjvS3*X(^1O^j^Q5%-Qs)ZD_C+s&=! zg~pt1NhTj;y+r}&K}$Hg$k%z_J{3*@KzaEbJQ_q#+wZ3J9rp#IpFOAr-WePtW@u*p z*8@c;NUr?WPFZCO9Yg@8G>uO=10vmc^2tVt=+7G!6P>2+p~9fk63ZZ58sR0gboOnP z_m*6iQe)e_ogIOHmsiWwzdgUuyNM)$4fI;gB^ygYyqf z2NA4rupOkyggQw&g{(97xTFJkV8XbuZBx;3<7V-j?^bCz+q>tvDY8NX$PwtdN?faC zKl7+l=alS~u!RnoSh-n_m~`ca>Ig1zI`Wbhogr=gFt0H8BQhmD=1xWrlkXi5bBnJ( z1|p?dob2bKGc{Oz$<9Nnoq5j(?Edf_jNVKN9g`~~zXMbx;_%X)i0+}yvtFm3L=#_8 z$n_Q)B15YmObp!0h~!}$b7V1azPRJ;eN+5L9>OU)6j7~w>$Rv(pk48oPkPV!kOD~UbsOy%lZL0?i?V;=`OVy8a&C6uf6HodU3^!%10nJG% z`0P)mdGUoYX^lTPYMkTj?V~2+aYLPSs+^kYSuywBFGT5_^nHa-{O?nB$5J-VerTHv zADe`sUW%J*4(wnt^~$?RMPHe;@qxtxwtsaXlj_QW!Q?kE4E-EKH*>}}r{sE-uGaF( z$N=|G=a;$k{ad3rc_oRAcd#!2ZolzbAWA-2!E)*c_oR%coW}7hUB^o-Rdb>tbag-+h+bs4Bp7!t?cU|jW(byATq(-kfula2o!+C>Sou=$(q}P_2XYw zypjZzJf>lCh+*y5TcZTKnQD``FodiMC2q#WoM%vkOieUkrs-!V8dOP2Q`?XM2pYXW zem@!g4$p09&YUOB`6z=HU*zK!5E~!#X+993=8}n^aNIkk$Gt|9B_IhuR}b!Tepa_O zYDzxxIqs`VCfKMFX?zw3vj3B20nlz3l0B=*Ov8wQGA30{3C$$1)a zsK|{Q)2bY`WeS+eD22@ky+ zjGNMy6iDtZA;AN5E-5^jV7#I2x-m=%&<{jJjollH;&1MOXGsi|g-2^@t{TwMNKMvI z1Z5fFXO^+~4!gyyB7%M{t=Gs};p_YZQx@nC1Zf?H$CMYb^(A( zf;*d$fpls;S|{abgl5J5-8UiaOr2m2zh(21v~CTSsU-D3v6)4Jc^%p3bu_;&n*TQ! z1eKg_cIoLFd#UE;hv;Qwi>_rWW`~5`GmwOjw}|$q+N|RTAHMh*{IR6-36BA1C-su1 z5x}%xJJ!UqNrG+#^U{e5Kk*dK9mNA!^-J?zCSR7*#E1bQp9LL&fgD)3Z>C{3=k!$F zBuI1zn-`qr`lIR<*B?W_YlxauS=x%AaZsGglSYS#4hUV80ro)CTyZb~Mw;etk0QZ8=|_x$Lb-<@Js)N#N$})Q#k5yO@VunUADb zd#QwD&$wk~2kyjKP`9fW&;Zq?3*tfY3OvIVL38UdYL`woCP@Id6$>B=&xOQS%`At@ zBM;H+80s$rwOgoA`+NKZRuJQka88)@*dhC_t_V2A>q^zACk{OSdVYOYb(_Wf1_5RP z!&b!GNXz`+d2Y{rZ!y8XMS&f&vhV_%fkcJGUJ7qm zqwrCtrO0y8#lp)Yxk}#dbfdpST?6H@A=*h9Rb8XjJT}1IqNO(8Nn0MBABOdZQaP*_ve^#q+S(0`{UgXMsGcbv6&`VQk4 z7dVKneBh&N3p`D@=NIPZA@(Gh#^VkOcDHfh+YSU3BXxxratrz8-|fAn4h^*fU2MB& za?cY}UQLRM$_@6sc^-!0Zli4MF3g1u4r?;OVN%UVfw*4Ms(DCx6JC*(-aXr*%3!D`ygC# zgp-r~&Dc0nrV3~j%332>9Ea3Y%;9=7RzAc;W z)u97pp`87UD%L56Xt~JTBK35pIFssekzZh6LQ-D`=|{;Jxkq>a{u!#Y?$RBl+iaUA zmSi68Y2a9)ijWHfN?RH?t5=c&jpApn@`(a#UmY!8|{ZuAjJ&wyJ3`Vzw z;Do!F=hz?X(>m~f_#YCJDKckQV3krdT(1}2$T%V`q&Yk^ ziBtRox^6DV+)CxbQN4<8q__3lY|g~#Hq))aj{xdOs4^NU^BXw#W*WlDVuF4gd>Zyc zB~d|KD)$S`ptI z9~hg@E(FfWC6W@LZ=F?W<*Gb@b;wup7!VtB`st*XClg%GVUyA`j;uJ1d)X;rm1#4u3Kz?H?Cc2hUV1Bg zL}Tk}bEL(T17;7rKG~qzY-ZihyoG_#Iin^J z79S%3AD2}dA@jH50|@^2s+Wx(SPM;tZ$$g-g)HA7uxwkgeyac1Dcmq??EG>-0>fzy zmhutnn))Povv5RSY3ov^RdH}z!79+!g+f#a_z`YRa3}sw z*M^^C+zH>Xv@a2GyaV}KIa(^ew>{-|7j+m}B?v)P0VOR7yzQT1FeGLm>PtU?PFfh~J^MrJCV?WlfP7i^;h!V1;^Jjt4m^=~i`)#rOn)`I$({`j+ z@XAq)Ru5s>JUgTAb|bIbt#nh(HKW~w5a`%ByQ6Fl6sC8I6?eepw6|grkHfkNtsFt@ zs&`QE#aViz44DtYWUu3@bz;;C%_5sKO-Uu(qZllt`~jCk$Tw(a%}2+2yTc5mSaq;4!k$RiRJQ zkw8#A5S!QJ)U|*Vf>kPeKM;^6sZ(Y5v*dPoMthK$J6^AaQiYBVlm>v54CsZG(!2~! zo1Kx;Y2ljoOrkNOR4LOWDGRq6O3&cdpSlcJ<6(-e=-KlFXeER%%flp0-Z^uxUTjqJ zkxxW7cUer5%{mWau{^omEgTsy`v5NgG2$3aN6G2nVosfMyhsz0LS%qPv3=N|rSyrX zkk*PHtDnL(%kTI5UR3#Xe2(A0*B?;g*DtLDTO(=JAZh{3$9-Bp93v$jqYs}1t#q0K zgW@j$bNWc$$c+FSL)BT$^#dI<<$=WG6;&29AxnWqu|DnWZ4`*7>%x=&*TMltzG;f> zfxj-@+lRS()=|Ec$jzH05MUA?W~2f4Us4K*f2>#j>a@Ta?$%f~5c*Lu90cgVL&b z3OL%gC8RV+xKa_SZXrvQlOH1vtOfY-=wG_*vK0Z;m!2wkzoaDtm8Sh)C*pO)Q=#XJ zc!As2sUgX2jnY18wV*RLFXSs>}!r=IVZv7GtP5kq>GEqy`P8-Q&zBo#A1uQ?Gp|EpuN!8VwR*Vw*M$^Q$u`h)M8R zHnv1(de76*SQYcS$AbvRrFO&gyRYk6J+C*%B3F%~=UbpL+u!JQ2d`=%u>L?nzXiJr zRRdC=EOcLuPu)RTfa$9B_%y}7@_BTzyZgnXk936R|FZnQOAd$4G``8oRNo?$*4ky%kd7Tmp&jwMQyebffDJ?vZ}S(*$*IQU9IROkgX`Rpr1Zl;P9mlZ?oeOGFnvj*nz${yjdJTb)wW_Fv^t`i%0Jxf&Ce zQD7PZbY(8~OY~`bk>a3YmvSd>=-Q#+QlGAmfAfDmpAc)JlPzx!+VSDWdVa|#LYXo! zV;*j=0;}iu8Avn@wyPaVcPKx?U&hFUNP|o5Bm&06BKf=-{K_SL;1h4X6^h4zre9Mn z&K;mAJn-gexChSr2?!fAm=wKypv`ne^7RH1ZUOh11n0{y++|KfZAX|?aJhDaN+)^jGH0zYNXe zhOp&&B5hXL#kCzd*5BqZwhL61N*jxE>p=c?@G;{Pa~~S=PnLs4=Xx0F+;<<(3?)}p z#vPE53*7Dg`8x-V$#1>_ltHrj8bEm7P)+qAr!Txv4eH#*mL<*yMu_3wCbF-n-5)th)s6z;bGoQ2Wi1Xur{$PW+tP5dsEeh->_nk~%B`m4Cp9kr z5f3QgKe`xPI5x-fyc1f|=bJRpK^sjc$Dy-xSdhmPA_rD4NO*ARh#iezsqWHXraS?2 zTp7oXwW*(sLEN7vTLiEr4aTj_>+<a`KK*OqD>F%|m^cNCW@a9pAp_JOF2orj6X`&glMD!Kaw5x%EJK4mSrH97Qi5al%tpn*bnWZP@3!cuB7<;niEHT~~Mn1KoqJt+aUUZ!J-c zixY<8!34N$FDOAkmb>g@mUGV_4kzU7trzVnj7`_S5mDX%H$ce0e8Ch<VIwlx6;ALz(afq7dgmK45jaXX2f{}5JX zQDgXj`qDaP%rs(b9?p!gZbf36s@!*;MN=I=uNnu7QlztK!!F-_rk=5?es_5c?N`)8 z%FJc5Gyl%V0)mvH%kxRJl}dezVayPr$UD8|Xj?SAN`e@)(!RO!%+joZX$?nvpmE1` z`Y7TM!bg(qq8tL%TDx4FMGu45MV^yYpa^54uZ%jrdNk&Up^JHyznj=1Vym9FMTOBB ziU6w{J;|EB0NIO9CbQcm#Q_ex*frLARlA$@FcUZIrGrB0*sVGblgW>O|Gt&_=Zyl<{<32YB+h9eqCJE+H+00ShY|sviH7S?=Cl8Z-Ir#o(W(t!d8C>yB~;0U@&}!0 zx-Sd(W7B}F4OaxMxM~|ZH15VhH`QDGj{FPnFZ3@p#yNWA0r7v}Lb0l`kqf^=JXJ8U ztV>9|e$<}NxDH44(<2a~Y&WPNUaB~i>vwiOI1EOO^hVM+B1Yc3Ln{LLbGU+Yt$1*B zlDoPjV?{*H5y$CjjU#~#$p;pek5uUx$+pZ@kXb~MAMZD5^DnZ;W$=dbP!^4wBxYgv zrXUxR!mFkM)5MdAhzEmp-sN9UDWGs(v4kJcAeuUMX|!xz9FP~`77Fi0&&TO}{ZoL8 zv%5fP6Z8`EO`>k;0EK@MFoD3%NKoF3Lr@FK2pOn_<46-Si9?qiIjfQTA-Gn;3MO?< zV;o@9FLWMn6ilxblr6(40B)9i~tOKjC0cus-V z5U!PXv+fBNK6Ati1ywiChW_T4d~->Ma(f6GaSe5R0AQoa6V)7%}&_7nD*%c zE55O%3BA}+xe#|$rw?k?&KDMC9khE<+SoB=XHp;La%z&v5r#AYI2WNxW&%3~|2Vrv z!C*WzAU1r9FFXmf2Y%4>#|Zw@FX?mjr8YeULD}3fK^%CT`5!`BYzQwM$t7VhG0UzY zVzYeg;4)cSv`=V2t3&CDA13qqQd^$Z-272C34Adoc|2E#xc5f{f@s?mMtAn&wcQ1H z{ygV73W-m(U68u>VR2i`I@`YQhbjV>@-O5{4%V6~X8D|J&+I5UgXu zmRu)VC-3T{kbiP@(ZSznVJ<@X6`(*oWm^E+u8o?gse6!dW$anB_pkSw%QiI4Dn(X& z>#EyUu_T*ZG#U4&hMD5+!&Qmy5;|Sec0;;LF4p8|^SLM9GS&-73R$L1yO=4FJP;vs zj)>aqCLq`geT!u}lLQzK!sgln0hsA=0Um_30YUE#b%TZ5ZTjW2S|3gRl#!0^x5UY) z&p04%>F8`Jz$Sg$>WtV8xu~>ccptl2n^i4ivF~QGx|z#S=sseUZieX~gU5E)`|lcY z5yOHJP)u{HYlaT3Vw0G}yq|cu6K&uLt;tCs_s_iG3p}^rEo}NrxD0@S{mqKl*gT6B z_K_=27PYdTjN@>SY5SIrsU9shKUC$h*$N)DAH6Kb90*PDHtO=uCwM^)K5lw3QgA5d zs5t{I5rdNGif_WoR-oWxP?urhbQ{@-p9t*HS8ikuHwUu4>QYnO~ta z7fYSz11)USS<+^>hT!?$oOIOLJB(*J^87M&Y)filu>T7o2c-k|STjfqN!&Kz4`Mic zK6i8_orfhbg?V+Tmb(za5o>JeQ(6Fv|5gEosg52d+va;OCt?A<+1q`S(RdiinXsO0;U-v| z!El~Pyb_rb z?*ndgn(aYV8Z_}i-51*fMqxUZeZ5}Om^<0NW#Gy+qZ(a0tS@tN(+`a9{FB33%95XlhDF#$Ga_CW+V!oD)0VMkf1T zV#_VK;H3*VI^q9{9T%QotZJ^BIEHqo1}~Nd=>wNwKZ^ zECWK=tmPFD{=bL+>Sp7F8sX`h1I2V6G8-w+CTr{1Rb;};uV77;fDM}1?31bVF`j`# z@xA%;j=p28u2{?mkWH``!W2r{m)o~sTn2ntStFv@HnsQEhI~Oa(|!c+~dJDD1NF2!(S-R(6R0RQbr6 z%@Z*vZ8XXrEd-UlU>NexO=|MWra)|KG^bD~3-bERkXa zdB!D2FD6K3txVjxc2Xv9nl(MVQ1fvWSV_uX>9T}mm`|I0?L>x5&>a&w(rC=+!aCa0 z^Ek*yTK$3q#IBX!x^WQ5iOSmIV!$%iKiS*CEQt3XL#+q8Gg`4WDvGn(u!kSuTOYNU zM|eU^$TSbLwsY}dXEZY)}9bV$#?#M(UQ#*SHh#oGz5sQjN<^>i&G6IObvN< zW-pAkjwggZTX(6h%#ng24&YpL{GJw+JVGR-&tt1HApcR%lYTjAxIHhGk1zn@JtywR z+q;Eh{`fQ{*gtthx($p!Uuz5^y@QoK5q+3M5yWQ7TmVJPntp+vA9_*|UuJq||K*M~ z_yd<>%<$8XNT@ycW%{&JA8j1Q{o`_L1CQKPm42QSEjMDs$C;X&hU{Mf0a7F`|Y!;_*QOmd^X{0M`>#G?fHj;B9ah*2;Pu`Sa z&c-qF^)uX!CWIVa&RAZPl}iwi0K)2ii)G*Q-4J)F|NmLFtP$m$kFL#f(v1qYFb&pKFHLO@}uyHqP>Ud|miNP8fN5!w6 z=oeMjfXwvfw8r|$AE`+Q%J<3yvqYO%A@vS&vz9-a=5}$-}I0z4zI#)u+NYlG> zUEb89`ijX?reVETMdhs2;*E5=6W?qoz&Umg?(`~Nx9HZ^L5MPR`cs5M+_GU)v|Ben zhBM?c>Iif%rH@9R4l(*ux+w$a3cs4XF55tGK@v7-v390o{l6WxBOHnjxHn{zCUx`c zwPOp^8lyT!c;LLj+V2$MmIHtKrUtYRxUCyAwS)yWJO>P~gXZRV4|sg}HG_q+Sxrei z20@obxJBt*YIW2{t-z9+5hrX%Yo71gS&~teGnlWA0f!T^vaq;pkxL0XZL?ZyFk500 zo;Pq_)7-cAsjv-DQRT|s?etXNZQC*PFO@cb;R;aY*0%4gWXF=snD$cSovF z2<{@(5K3+ew=flf1kS~%i|!=zVISr6Z&|ot?`;p?!!AdgGA6wd46|^_d^YW1fweQ2 zO(q2&5Oc^P9d9W%nEnPi5iqF$>}};=I=pKP@jBT!_=Az0J%q~$G=TbF%%PhbdWn-< zT6G6NI)3iK%RElf+;`6Eh}Z2XQTrv4Q~`Wug%6=s)v7TfC`Xsr`?oN~?Boav{11oH zdw|!|8nO;ZIzUwB{l}y$(vVhnH&wV%r#mM+A=`%ESi6!n?4n_+19AH+KH}F{ZrZ@L zv(*tTv0YCrs#CEjNsRGy>#j%I4_cXPFuQ#8KoB!^IdDNVf05;tco#2!5YBK>&*p-D z8L1|C*MLj}7R+>KXKxnVTP#A&OkDFdoI*ppVGo6*Gv9>z8ka$o1Ln!7clw}k>}%^)2(;l*UTlPMG?QdP5=d7f8~qKdo0rcj}1 z#FpNs8!8@5f^mwu&yV;JQ{nvxi;eI~|1VD7z1C&=z35qv-@PmnKf;OY9QQ*}BwQ#t znMBsYir9*1J6&53AOTnrYH5X>a?`AqIWa1bs-QW&MHOV<`(%2ogK8>HZY!7 zO9LUZGdwufup#1U)KBtlqp*~6uVqO7- z%($3f{3@Hrll1%-PF$(2DdX7^vld>6GP-B`&rrU?=VY6HzzVJ@y< zl;aVMJ9pSDGE#ste%X(aKN<+S9=q=glEoG}9wt-pGWrz#F*(itv$K*`{&&4e(sv70 z^8mip#&8QY4+yHB5;EFSH=g}<)su6?DedB$1|aWTlPF$YI@pAJu4y4z+jA{<^}#^U0Sw5<1RD6M88zZ!)HuMu7fQ94eVi5 zB%8oAFr`_5uu3~2Q*eQW1drB6(;{fCR$zQ|8!*4YFl>2CyF!PP$%hdI2~JCj z73cU%;HddA#z27A;h*dWF!K;ttM`5t-rX-_+)>TX)m=U@b)nHX7Vh_6KSush>k^RI z!uqJQDr^$|5TY;uY?3?QP1RaIgBdBLG*Y5?d>0^#qYf@-zffQqkvOkc2o$)q0J-iL zJUF1nR9T%F=fXgNGp%XzKtBm*W>s7lqo+Et6Mf~$6)^8j4g5Sc13|f+N1Qd~J*=T@ zlNHz^>htVI?abeU!n2UR2;$*9dbs;nw~MK4lc?7bTrj9TaqyvOukr7wpmx%$x^BA^ zdQniX7MD7kInzy}WY=z5%{a80Yo}-Ot$r&zFwm1_t26N}m zl3D6=Q|PO56yw7>zqfU}KRj4m51*CX@V$(?-J}WNs(-i(w;Qspv~OC^JNDA7f^nDj zuLB}dD=2U(FILhv8YnO3=rW(fLQI{hdr~gml6W`0K&+Q9BFy<4w*$KcH|r&J8+7um z{8v#JtZ|v))>2N>L7s9GmvS#gk5{fsGmMvu@hlutt~kS|6&&HID0#n3*}-6O_|%p~ z-Ay#n{TnRYP9gZvqMDHN8ZD)i=_tI z1BA^i^1D2+O-r=8zmkI{S`(;q2PZa;?c)Rxw2&E5XkvQH>BLWg8g}L#s*B6709vhf z?Vh7!Rvb!Kd(^zKm|x{m*9Qz}Hyt%>p+A~DaPvwQO0CxbWwy;Oi9se4&M?@3*ivJgIU6TW=K-zN68~Y z=C>103kQhk(Y%UvXa)V{!cZIIij&~^U5Q?N*F=jQU_yR}=qLhlQK^qal40GWait!G z&VR^Osr+yMk{lkG9$PRoikNS#$ClIqQ+R0f6#9YO5r_AdBNTm8b8xO5T3VmKQ1%Xv z^8!XGAgA{3(NKt~!9AP!nfC1#C?WJg)8prwLxeH(?_<&;Np+z>60BNsB5 zf*!^~m&#^YdVAKDF_!eICIN9CM64@vLn`j6#Ga zAo5hy7Na?Qf?s&0E#tEW@9c>(R7YrAW*acRahxaLl+GY0{ZsRSlpP-^ms1kX(zn}% zvK&J+yr#z~plK9P4*rtp1}iUbn1|*X7iRyIuQ4X$?S%oK4&%Tv<2S$r6~_LW_kkB{^=V6GXNL0aG}mh_b> zF;r$iC%q|upory?$WvvdTci~d4Qr4KG&k4)hk}CX2YNWoqYrhDLJDty zZaaEhTti=R?F+tFB*ApP{F)d+!Xn(VdveE|gppQzsU8}3G=!am+bQ)5%nXNaf*n{K zzMt0|rKO`s%8q1aN5dhK0!Dq9mHgP19RQtiK0by~SLb#m?uScbqP1THp2s(1Jth2M zQW^^MN+LmYb`&Et{MvY2k@vPK-$qMs7nI5zXdz9<>5CEO--aPV3O327gp^M#Yyt=@ zerIBjs5Th0IXp~5T&pl<3xU;>*2RnYjVe#>U*c2L@rry9GIfkI?Ze9Qt}4ilXE&~| za35{(;F@$GMX#oT0fN$s(fGy{d!0lVF#4KJO-0G3pQ_ug#57!|5HSp!FFr${&5z55 z(<%V=Uy-{?dbICNmPb+3;)y9(i33=HuhZk@1{;eu;M?`PygBFY z4?|&c=5Dn?D};@LH0cZ0G%Hr|%08T?e?&Q3lsLnQRN#-1&1}Ec=O|HVSB|3T?K3Vu zQ$m&c`}==Ux@z1-cEg``=|&ik>Q;qou13N1`A^ zXQWzyAyDP2q$~*_3SKn=%+h-$oUZNnP8#FTS!s6cchtCO zi=?}6xr=QWGxwmh!QZ^0ZvXxf`*$GHKCLbg+@cPS72eFkS_e_^UW+Es1^rLnv`dm7^9C@79dp85K znp*_?HaEJ?iQB!~|DMd1sDHZ)#A{uv96!1!hg!qZ4gtK<8-KANfA`G$d$Fl=Vu~K$ zCT4b_^A^872Ydw@cF{k-=)-yO&qzC;XuQ5I8CamoEc;Y(?1q%2TDA?U#Qc5skcON+Bjzwx97q%}q56I-vEUY`6#*R!Ld|1sj+CY`!7!vk$ z2(3RHObxL0l9uE3{(CNJw}LD#GyPN_O0Wvr2-b$Te))-mS<56*ZiQ3e{)PUy27uA-_VNSoyza%D2S0ACpH_+W@X>*C}Y}9Z^QhD9Tm{`|_2%23&9@eaY80K^g2oNGU zrHY{?_`S7Zgx0ZJj8rRyt7d1B&!E{-^L2WpolRQnIRx3z< z*6zpIAA((i1!0XerT6_S+8^TOgl3`Mc4QfHGK&u=lfL4T$=%zV-UMO$t^npP`d z-^$EWCH7^i)B5Mgb}?>g#j6tD@fHiKr+I zqBOoHCCC&@VVaekS})1d*0Z!~!~)rGCe5G?K8g!;P5Jpw0GykNqy1#oAVGz4KLqZlpgTMCnxzK^cIyXE9kh6I24-mK+JT6oehvXrpJyQ(TTGI zbSgRD_XSX8p)XKf`hl{|tS{I(Ba!_g&1xk;s)JWed)T2-EbwbR->YBO@}z9e?WbPo zs>>UI$ZM-C1cZRyZTnw)K<5!dj1BAOki`ohm>SJ?=j0nRUse z6QVUiKUxg{9LghA1dzF3-*}4RRD~rF(GH@siL)=&%N#L@Uz(pyA85t2-^_EEO!|oC zKQjGsfrf92452UK`L;REe)ws^*9u0nc|#bNxPA-}r6*aD)y1Ot%+zCK=`tGll}CprzP<=>v&s*vHbzR|8jEHZ1Yil%5ss5q0N~x9W zD4)p-g@5$_Kt0<%s7o@gdogAW~7q{i2f7;!d0J&ka%oGg#mGA z98bpeD~jDT{_9j*0zmUKnw2(CtP;tEO=128Bq97*bq2+PW4Yr>w?84vQRU1uM>4u` z-C;)XNk0}CWjceE(_4afi%UbpJwdqNxiO$~6{t4+dX ztBgWA@#JYDEU!#l1!h!$$H2~5(MGTcU_oL3FJ1!)Fg4 zM61NBckvZT7+TSk76oftElb}q%qY(unVm&sH_w-%F&#fz|{;e-?9Re50OVMv-IXL;P4&) zIOWo3Z4*>!*WEbIb~D|5aijr9)C#RgVL@avM{(X$_+6}D2b54&azZPp?sConQ zrfdT<-~dmo!@+EuS8&yk4v=H*n!~|c|GIM9@^@01Z!LUs$<@o6RrDc?O@_aRf@!&*W*Qus}V!vuQr)#&^)typf2NS4f>Bg~^_ zptDJ#dGy*~XtVaUQ}J2biPtWTf{hQ`8s&@1d4@K(BE6Xmg! zKV{9*9s(5ZW<>)Iz*0)+hXWOeEZ=>UnAk3W!B(=^1QDqZEr$%Se}6T7lO~V+pP5RX z_>VG*pP*a6K50b$&fR%YBNZ`U0$K(t?M1El;H${}6`S0il!bdnO?yKth=ECk$<&+0fk&8#6E zgLY5SqLMEgN8WR!J(DLUC}8;n8~eol75bHhs6AZ0n$J!k$Zd`$`~m;UG&c>aJ>x}^ z8wgC#^AhVhAoy2tDW3PYhUe+mBc#w|+bUV%>rgF7>J4hvlQ%+dOkkrYu!>t?r5?-eH}{ zC{UAUKTVcn0xkDde?60#Lqqkzp8SnBzJY;zznj(jDf+Bd?Ub&jbUO6IqjqPrLl^xYs-MMCNgw-U~PtaoosJ_7|i$p*G8v(-7pG zT=@;jT~hEy3`5>&%Bm8qPjto+2iDuQ>Gczdt{OZ{)7_x_S!GSu#{yGf}r<~%-q$;XS zi-*X@=U5RNGfKn7@i+mM%gi^bQYoAWjq9`ztI$vsT<*mnsO5sDMs$h1x4^v84Cl3p z5l3yt?9#gF6ViVG_lkLa$5P*sCKOwRUK$>DMiLH>U0l}pGf<3ofgS~4B;Q(<4evucb$4OgZj6Bw&v1-s z{ta_kVn0NJR7nCTVopARa2%oB{HXLW#Ge~WN~1MGDDWiyZb_sZc&K@vkheUe_Z z(^aPI4FQ96^Z%g?_FnVX;lNaSrB*Oy24wO~k`=tLjE#yqiv4geyuO;K3h9qS6P)6h{bDP~nB&xfV6{}i^;Uv(yq5-ye;57HG zYq3$XIjAR5cD-$?#OHX`ji;kbH+NR3)fLdq<aLts7 z3b84dEE^?UBi>fxcH4_ZUIPS+=tPTP#~CB|nc(!$$H#hP3ighJ3>pCKVhX}V8|1Xe zL~YqBh7V*!J#xJF?SiOX;R~z&G{bu0hcnKrg=R5GFlRxO0tH9d67!tq!oYNzPvv~h zS4Q@CTX2F0;+B)04equsVd}tDhzel9hP~4XN?sr%h3G9jj(r80KI71<#|wsY3O-EMmG(>OhO>u>W?00y7g4HT)zUs)0-(F98HIeaCQ zF8%TfH-YTq^z);}q3?HLA&#&}r(p4lLLFP}ueGzs*Oaxnj5@4WRk(Osn2Zl$k(?iD z94>C*Y&7+k$|SJ`x$aMu%dzu#7}41qC&V~YFUzP=RHX};bkU6kF+I!vA4 zXhe{Y?$74o-w*BB#ztvH2PQ{EzqED79K4SO24uC{P+6xcaNLVgO2U3RJ|@g z39c%@`xMU%kXCx(^`K3Dp!2I2cPi?2lUoWnh54b)*?a*CE2F|L%$Cx3ue~F&v)0gk zAaZj(_%mFW6kEv>mYcH)N2w2Z=2T8`4?cpehiJ02lrTYANX!$ zZ!vs%$s0HA+dPha{9 zY?lZ06xT>bJT0Fh{*A6_`^@$~ytWjg%@1DE2(#yJh{V0OaSPMY8|OZ-sUag9ym0)~ zq)RevK#deki!MGr;)&mhpjgfI+8&;=m;ML(`rqvs5+z6*==k22CzUjmGB%`x$gA?q zHUsGR{Ww|Z%T$%KuieRD;J9+t-nA_aafKllZ8=;2975@72MAj`jNS>?0a{e_DHGGQ zH}kqpHYUKu>D|Wj*4)${oYWz>7~yxm2A8UPtQk?!Ig^A1wlw6Wy`4DE^KSoC8hIVA zVlKU%`Tj!pe}U6+i4b7h>^?0+95>3t`?=;FSw-DJA2RYrB^bf)FP@VJ{z6c84Y> zT1$;XCmMX|JGr0-3RLS0Qpf-ZnnKGHd=k$&Vqjn_j`&R-_N zzs{1A9Mg%%G20F}S#Ji6dK>rBZHb_q&TbMbn(LdC!GU~*D+oN6=};tWBBvBf)483- z$JM)hT>h! zb%*P)yQ^>z88bSSJCzBwpn-y1{C>_DcABSo9`UrXm}-UGVcyisz*%6Jr()KEntre#Ae6$ zeF#i<-7l*ydtDq^6uEt}H8+#>4{wV|+rVva8m2(G$2@2^{wHZhOS!vPkQ(hffL)1wTyt_?sr* z$utoh6EC~fjXQ5#b`&#!0)P$@0#u4-VG=E(Z^)iKy;9kS+Depz#uu5+GVM!^Kk=DR zZZO#~CW%f^NV&2Ookvph}T96WYv2Yjezt)@<0{-;K#iMbef zUl~UPu_GjHBvt=G-oL8;T`o0MM$@PEQn)}#rr((}Stq<7t2>1}V~G0;Wgo$PIb4yl z?qv1fyPG_FMkPC#%9t5-F4p_TG%c({$V0U3pfM&e^vwL)hN}QQyRk_>`gZ$djd7dd3ZhP6U>5yoC5%=Y|cvsPF3IWv6!c zCD(4#bOIe)FG%M&A~;yJ2^|3_!67s??VG_dwf%^Zi#s?Ux*eVX&hOaQ#80{GucVMp z={Z9WGrSO7oZDN~x#VG=ufU%LO3xFtD>)ZQ3`&jQ4txRWEp;O)6stjehk&5^73}cg z65eoTs-_EsKJIs5bNG(`i5Me2b3~(Wb~w9UomE|pCC&P4@a9CyF%-oN9Vkr9RWlqZ(@EJSnf%=Gt92P4|^ zCQ*&8^#1Fk0$`Tl2tmRICQhw-GlnulUI9K`l^U~zH?gptGtf+SSn3W{k9@`J>S-qF$(;jNVJI;|^$W+NO ziiAd&qPFVz?+OimmrKSlyByZrlOijloD9Ae-D{f+M;4NK>=`?TmB$7oQcAbqPTWh0 z3&CY4Ib!c%ZP?Fl49p-fFh+*I1aLr3;;;pyJv73*8Vk_ck~#Wj`%P`=>PzWsUc@`s zAGI8j#s{`&cvvukuCKUEWC0$7=QT08cT4~p(^D51l?Qx@le zgyz*hj<{mHByr1@9rVFs$cf0+#;O{!boxI8=w==ztP|;lEd>0UcrlhXBL0yv0KMk0 zTd$9Ewi?a*hEOpW!P^RCeXf)Es-|T-(97|XEpimec%H~+WqLHOCv8)g+Ux!X2l-m5 z1mBCn=D42Q_!oxGN-ka$S&UBJTY3|;0=)DFt*g$*#y2y~Jz(jOJh6Z}x{FZMemL;o z{p;Qhf#|g|Z5r!I`rK{9f0VY*XT0f+z7RsSkqflhA8J9L*Me z%GF;@;+xRfH33?)IwV}pmvu{0quQD_ZOLw!%Nd|4E8}l?|C{V=w2)RWCF;Q;?qYzx z>Y#4on}PoF-|hS1O&IbktAN4b4tKvV1Q$bOBhX34?nI(PRmxva1w`BqR`ji*`LW?~ z(oe#{N2tfYcJy?4hy?Y|ztGi($vF0iQSB195)Hm2Pf5Cye0|D;^RW|N&`e~8^uFm6 zY!%l>4XqY|%?^M7cUS+2k5A;{L&!CHHemj+gXU80WyshFIQI6Oo-I2p>pfoyc#&&)%~W+%6yVF+_&T$MG%wQPUf7#dfziOkI@pIxb%#9f_^-&DtcSgH>4>S~AbooOja^R$d6}MZe3+gr2y9$f=AdR&T!7~sy}xr`s)^9VA-Uv6JC)f5d~X(vtXBgPCx@v|-v@1*$Hnk#k2^gVY?JlJT&ff*ztHCOs!;afx`TnvoCHELS{|CnZnADj_8 zc>-mVlBrVPjpO(3dX7(i@jB~)X+}XxnS)DW+|6oir&OCk4yC&$LiE=~9ueJczkp|8 z_#SJpp%ss^WkY9^SDl^+W67bsvp+-pP``+CXFZcS!uK9Jc-esS4^t&BDtq|mDfyBn zaFAnHr6EtUc|(YEW^%wm!AJgvcoXW zxgF4L*S^qPdw>9Q90j>I^h3vzS0b!83KDydj_kM1321vNPIw#FV|3;s z3Njq?H1Aqc!vSt1#=En1@H^9`!R1MK6`srWuzmWCFd@^G5JHrubI9jL&1@Lw`oe(0 zj#brK{Q7pKa@mryp!HIyKXVf{`V#PU5?4eLNPjTsc^2wT&h1puoWBcVPLn!I#DlZw z9v?obUi^_tRefKqzw>0PsP#Yk5CVDPAZh|9B%ATT?vv$0C-ecUEHwm%V+qG+X`85z zj?GDp9_V-Dt*^clWS4sJNLKqtIoe&A1PSAiKk3{;{2Lme``)$Ja?Bv^P8ugWR%U4V zihq40ZFQ6eFeLL_+}-8?uVwZCsOv@3>NB`%t;EUuw2)FX<~=iHh*y~nPHwg~gue16 zET%kjSgO@ku30>-((lFU3~>YTk~rd|iw{V#BD_q2Itl z6H8Xx;NrGI-WNUzordoOW6xCbxQIv~k@J^@HYcsUxP%NbJzYieHM1C85SioeMyR5G zE!^@M)-s=e2txtS#u8=~I5yM)t9k!f(FnfB|9toblI65xoNt22T6ob35^H=(& zBckF@4N0o;oef)Q+b7Q2pZiV$RgHsqbaDSWHbqd-W@lPC$2ujDnT6YuImaBa<>om4 z@<5ME(&;UZo5|CptyJ5H4VAgZaG{(-F=~SqM4?pXsF1-BuAjk{{}ZE0DOR&VGgmdl zg}P()?M_q0kW>P`b2i2C8-yp69T6TVnN*eh#}piua$w4m^U~QqV)vhKe1%JgtgWrO z1$;sHbZ>GR6Z5i+=Lkq*H_*`!1s@RdEoA=Rt;D#y9><|biFtVy<%to#j(ZT{*!pZi zX|-C~G}+H(?8~vzy5Nj+S!I5@EeTIsQq@Oyb2Cnr^R);z%Z^yKwYR+r3gRgQzTRxx zq5`(sFa=gWeC+Ob|r4Z zTC(r?T<27*`B}$76h_vQ{O>(_re>b#LOW|k1Pt)?i5$8Q+Uflo!3#-st`vwu{RzGc zo=swhkhS0$!nD|_#V8Ug*|!nm?92gFvZ*C85Sz$C)yH(9Or4O(wRHd_81nlfm@D6i z>77d!P4iS7=(T$rhwQL=mU1Q1DU|DzmVBd+2m_$AP6mRtKY_b~NNruB$gGuMSmI=V z?nF17{<)1kzAW{Me##b~duVZAz0nWYebGPdmef5&GaMY3ED9#7uBo99P0tLJ$l)0# zx7a`e7 z+sNdD#GHrfomNeU9i6_*`ZOx(na4V zgTwsvSz_fhsA^~3ZA*K-84;HC{VNCC#|Uq&Glj@WMR*L4=lRtZe2gx{bg&?ryqM@Z zf_#~RLp~AF3&>cra|A=3A(@mAdpjhgwQh`8YX-G((&Xvo-1GNz#E|-3E(r7oB#g=6l9)`)`(`k9_G#`|Vasj4wE;rPi{9I(Q3BNM7yGoPNZAD*!jb*L9&#_d z_yZ+rf+0wSmC95IcluEQz()daY|-4Gg&>pIxwUsuTQ{(70BG!)aTTptJ$Sd3xSM(( z+w|$c%MfuED|0m*$20@e**y4Cex|4w0w|@VE;FF!gTQqVaW5Uw+Sjv5VC9WP$h1X} z=P>S6jz1YvZ8h=>MeLGn4#yCtTDza7E8F68Zb|eoeaV9{ST8u+o(^3y$NGxnxM+Tx zn^E|Bljs`LXw|*`<`kt}Oy+7VNv-#6c=?)|w8RwNud+^JViv$5L6hnGtpeT29u)4? zs`q$IuE0}2`M?jdpBS~IG>2K?EV(Er^Hl5E*ae+XGtAn`I#T>4`RQF|I4UG`lcy$>FZd=K(T0P*HG^A{NWNV@ zk9_sM&f>7s?aAJpxb;hr!pbuI09~qK2R+l8@QX<<@44iHISeZh*i*xGtddIVopO*M z+WfZzV+2QiIQ2n)L0KoaXdBwYZ3K9JnHjw9lY~s)u!9;XVMJS2RhkI!bPY!XKy!z= zx?{XUgP(B21W4hLg!WHai8tk?8u6rA5L7@OQnO$%1sIjGpP1_Qd`E}RF4p8svHKf;K%u|Z~|N}jX=t0wMiDtM~o5wM%jSME{p}b z%CIru9iYslI!LiklJD-?yWOI$T>@1qcJ&Z=fkX_i+ysHk^To{HP>b^S$4vle%YGRM7Gyh3`0X zoUI2U)5Y{J3uM%?^cO0@6izK90rs~XcjVJv+a#Cawj*F8=@M}^=rGWt;#3%f)hj{y zqW=P^Zm-Ky^ogv|P7!XdZdOa)Iojzukp_cGpX`Jdo}xJchIsd#7{Vh_vZ2aI1rE;L zY2z0WTfObIkryAnB70N|9K0hb11&)Fx}p-r80dV3sJjSsz|NWSSk$8Xq$u+6%o1%j zf*!gSt=>08-{oEe#2X)w+`&TWDvF2zhgL~N<=9Kf)6MQn#*F%)#b(iPzm5nwaJc|y zuZA(eru!{ivZ=HjR$f)TQWRMc1gg;)Uc7Oh+%FhXD1NRq_+ zR)?hA5(tx!Gl)|`%=u-RK)U@X*(lKs4%S8RBh0O2eOoVQI8cQSLwlXKLtf=%RR2RF zpO#H|wfe^#cC}<;l;$K$FfE`Ff`!n)4FHzBBAh+`ht0*6UJ2nMAG7*xHq!Q{dIK5TL1??a$CQHjTq@F)Hx&VdKvt^tkYQ`fr&(rh|K4Siy&v zS8o5A#{M_l;HR;;2211eo7h~e#!Q<4LZ4!&qI6!{CW1k99PxwKJ>3INx|YG0qPcJy zyDwX+3zaUL?c75$mkKkDRXn(6)wJzYkcxf<1Pj#i2`?->MJ2GV(L&rWIfWd=^3>v! zh?I`Uu7*l%0i!jWT zc1GD%pH|yIGZ!MK{jWxPwDEjiC#mHcPE4(cBI_Xhfvr0nqo6QIZ>2-*uyIH`#w`nt z25_B~MX#>j8-c2G`DRgtD|)Vl5pr(yI=$0LZGh_Z;Z>f0Z*-pa3(7fa4p>zcy2F6- z3VOu9UO_}RG^8)@A|T*oDqfciLZcd9U^b-w9(!3e)PP)=Nc*mCqvBiF=k8M^8iJIX z`>DyTjk*vdw|6O4A9MpQ?$3xE{_-p1o^-LMjDmiKLZp@Y9CO}F%1U4dA>sR&`(|(H zV=d9ef!;Cx)|PEDNGN8R5L#G;7)9H6R1fk^Hu=MQmGjR@KJ_WuL*ZY5)T4{i28CfH zA_L_Z<`3jm=BgL`Wdi|p*+UmcA02@f;1P-MRiHZ(`SOU|>D1XFdoc{v$UOy~WA=V6 z!7m1#5|fpm{b}dAt!ai`S##P#^DB__L2s*I(q{f)`(|}8{4p)L^h0?N(n6n*i_q-= zlds<>1yq$R6$ti;63s!*(r6 zcprM9B66+U&8%;BQ~N!lb>88FFC0n!y#buUB+&O7$RzzxN;FJ8f9hkMdZW8tTUq}F z%r=;#)Z0(mf{nAIOReUd9v8d@q#j-*u7_x1+dvdgv4JIRNX-#n=JD_R!OsF=gnxCH zq;^PZa|f>Jy#g=PG&bp;EA=kj8744G5w4m+fR-S_}jp~cR2=z@WlxdN%0OjNgE>A8Tu!x zS#^9)JW@_aWK>wu4bt`+H}!|C0iPBVl-(`klpTL~rMoqU<#E_4rWZ;IN4<)Td}QA# z1psYH`P4Y?uD^ijd+%5G2+!VJ0T&=9fk~*#m;45YsEJ2}XhmPCnF3+2d(n&y>Upe~{Aa9J-O=b;la;L6##fi?PsBpl8e|sR?pd9;wf&PX)cmO^FhtxF=0Y+@TYWWFzM|Vop{}W=io!~2wjj#!D)B;f z5}0)HcgvR>+cS6G1)Jl|23V_RFhbxYoCUzX*7&v`rs~$iT8<{nl5MR}fcfZqQIY&D zQW55Jy&YEb)bJOg$iHr=pBv)(Y7moVUAV!~(%bm)9>HZS23Ek2PF^^33`EzNV9I3>fHNgf%cD{e~8puBk3X5&V;cCC@6$Zuc#Yufu*R zv#9aG0uQJcI%^v(0}u|kz6cA>T4pkxpXZpCD8bKOUfhJmdX@PJJ70@Hxs9S%EJh2D z>x#a9EWEB_eeG%Asw`*1Zw+?5AZVh`g=2sQ&KW*{K47iK;UuOEU`AkWYhXGiM(XR@ zr%T9%)Slb)X{7Hx@fk*Bso`&>tW(d8YdE%#g0sQX0d2!MuOoth{IR39D(NF67*fRN zQbCc5iRC;+R<|{F1cnvP)m}9sWAJWRztZNeWijQh^6Fm;+df9(jk+Y+@4(BG^LLhO zc*x%1<*~U-wMgfL=IVSzVvZc@bt(4KV|fAq2NX&6@V-+w3KQ`bvXY`#MX9>CE+b4y z7!7)Js2ijIiOJT$TcTwH1#Ux!0926kTEVL?eJ@kUnmb5iJU z0qGV3HXW{ObdCf*GnN;=P#8({gTsaKziRy2ae`@Bnq&#Wu;vC}u5WLBZ$gK~Ae3Kc z?39WI?K44>Jf=zCxkp-y*f;sPrauw(YL`J0L4(5IA_`_v zayx15B$s^Xl)?X*Q&=st(mfkS4w8zxngIb(WHF{mf!qU zryDBuvi^s~{I%;*S30}8g~sG@80J?(SqT>HJ$ceK8lL-n6#Azwle!ZDfR^`!6ybV= zqY*fC3^@7DZAy4OkBsx0!W+*I;dBPb285vF_4zAD_xg^WeA17RdGAK;*L+j7FkQ6o zPsCX^^zF2ad~7EATUb-uRVl^Z#m5B!MZxDh)$`cM*O>vLgdMXWviD4QwW9`OoHMwy zL5XZ7;g+X%OZySU!#+oqSR6500YliLdfb$-*=K$ngpZ|8Io?-)IO7wj`N~Gj(;g_A zJ9&KoKP1_PnC321PBUhBBPdMHhNtG{(83BRV|rnY;z0{Vh9=R^O*=-^=;Hy6yEV_oN_VQOXFQ*6Xk`J}KIK&PI_LhBV`3O6CQSh@X_g2b z$d=UZE^GAJ6=<>b>8U>ohK*^x2tchZt0BFm!-86GK~lz-gerCl6fU#HpoBXqn$Fcg zNq_MTMm}dpq85M{C$KG$Wnx()#-E#}Xj16#vBfRzi1AW)qL`c0-oc;JBonhxR2k-V z(0>@dPIalU%hDsF@<*-O&?d2_-BV6(MbD~c9GblvRV(b5M-DeYL{)nSpPk8g^&9V6 zx(Av<3g~W^_9HjA8myRH;Bz>aXg~$ACdmE6hs#-w80Fw5e{u`YeDN=!h=s_H#p5<0 zK3LcN8<78C0U(@4*^LjhO0k(PnQ^Qbb6m+v&Er`QCO=KH3jkI&Ko-Z~?56nuOT;;~ z&SNi#t1RRNUSYs#&F`@uUeUL%Qo?j&z_U4sNCE82Wfa=5=x7EN+dKIv^U5qhq@*5$ z#eg`9%PlG5DdPF|M*_%{7qo=WG-?hkZU#=d0}Rt8<5DkTpcf0@=7)W_St5>HVnBj8 zJS=3GKE8%V!bT7ws#Lm`RkIR-qFw*m{I+F2U9H1XeNU8=*oW?|T9RW%6zzJ(aMHni z_KrDY=!fM6XRfK^S3r86B<#Zh?WtZ?hPu)67AxW1b4E=Jlcxx)e4hM1JMny=b=*iJb7XMTb&L(@-uH%1r zs`$bH9~gadeAV?;CZIISV-#LX;MsIf@F5fDaC;CraOE2Klv@b!tR$r1!O-v%hvNoB z_d*;VB1pc1xRVVlmgnSF@FyFN`g_ajbeeP}tRaLUB3D;y+Pir?+Ueoue$l6&&1bCw zrz#Xt)2|7VhZ6h{spa1BLx_Ha$uc?rT)HLe7m^H7tjV(pyqw03?#a1b39^LGI9evC zuP*63YX=p$oZ!Ic%^MC|xr0zmYv^oZ$GTPGFV;4}#L=^@XD~D(ewCKZ2YjagPe20o zF1cYT;!`_Mj40HP6757Z(sNduMu({(NlEKIg2oB{Dfm@d z%cl(uQ8&nK+X})vaO0-FXd&u%Cu_uXe`oQdMTUA z$y?hYmHS`0_*w&R+-m*DSbxHHaR-ZAuKB})S3r2x4g-i`8`L*linqNWgzkp_%S4_s zP?!(kf#AlAB#`cwM9Wls#Qp+F;wW{qGP_34d~p^#HV%OB;RYWG8I*)lD2QlCo3vz| zfNKk3RK~2eVPeR^(dkbD{9z|Wcw`=+jz)p0&JD)D&R2-k+C-b4aKqPjPvR?jCo+`V z_iJ6@;g1N;?vrnG4@GOc$oFjn*k|NcFxl~aYj60A!v2EC^f6rd!q-m!$YV1dT3c;@^US z$xmE$?DDq%k^{dWd%ddYC9k%a7`zcEl5q&Xf$HC*D-puk8p^JBx6D$3FOy>olxNX^ zaL`b)&LK~rL^J&f3ZmrDfkc0%OTPCmtvn|P3B0ORoaCIO6FHFzT~VvUtOdEpJquO# z5c?(BTi|_*ny`ziO~$UqGHCX{egpDgQS}h_&B=jLV&TpNuLHaMk&Fc&E#?jY0xaSyNWsu^OXV@BT148yG+7vv>C2lQyin8knAMa0Bu&F+F78dw z8&0qbILAZ(%!A@kiThxS`+Rsbfq>XSAucs+mQ@8h7P}Xd>u_?vOhs^}l zf1Nnl><880EWdI%FEY8$1;>i-pJY5zPgp$YgUVCeyCBNN^<NWNt|BcV-p9H|}L8LNvvT2o|8n z$qwY7wIDxC-Dh?M4=&y5j&dEcu%G=fFY0GnuCOOx)k;NFa2u;qL@s_;n}nrjw)U@* z*|O1dMg$x&bkKu=-sG7B&MX6W9xnp}d69#@Pb6jF#EINYy{H$@(OJq^TmkP`gN0m> zk#Jea8Sztgujq(hk^DTb*wI66V}B4_m!LlQV$j|&y7yv7z>MZQZyQ!aq7L@`0Wgqi8_!WqOE_b)i< zw`*Gk&X()PLH24?BEY(_WkH*DG3}Xq446H3BdQc$+Oz2&O9`B8hKQ2`ldm#|UG`aQ z#jhn=Z+2{_)KV)P*;|yt(j`MR^vjXA2pNbveNnj)3WCl)HNlU$?h5dC9siLEs8hc{ zCU@jrSTMuEyezg`Va8X$_q_b^bFZ;dlol&R$q<)9m#T=t$nb7PDu96=JGK8d$g^4TjB#j9(Wq(rHHP zXtxJ=zHu~L#xAI5nM0(AnOw=jw9EM7K{{d!Ayq-(XC+m9i%C1hyq~Znu4s~dO_X5x z_8#F@fGw){3wt=(T6n7;2vx3%R@5k)P;-zsnvLKVOYElFop=|Pb84KS6B>4Angl0c zMPKT6Wn?0fvAkrDsp6?~>yl&0X+i9y` zL78YDXLR)>AbS9WA;+($HBG_+?!uQQo>zuOkwKnlMU_8L&x*xSSS%Gkg;v_CI2)7p z5;LSfL_-@&Sg(+UjyzdC4tM>=n(E%YbNN_}%=sRX8FtBO&NJo$rco&H*hG21}dyBzc9tcyZ}3`gYCV z1{gXGAcRM|O*$}+meWruxo^J03vy`6a%0&!cg?N3Vn?9ye*yk=Mxx{dh#+4p&Qbl| zKl?{IKOZW?{t5cUYt4s$p*%3VR9@DHT{bvpeO?Y_37>1VdF;K^ROLmUg_pEBcFTeR8CFo8t1GvB;TU z<5L4&TRkQEXli-qLZTifT4%7TI0?ajLo=r;%@zBs_>+ygbxMa-|A9TVNP5HBq?5c9 zfTf{JyzE$s+$EtayOQzdIt7NwwSSrvm_K~?f48V>SD}*;R4F)su=mC^@;9uFk1M*@ zaIMi|{Ixu#He8UogTmw|ciF#~zdi-nIp+Ir#yDm1QB!4;nUvAARO`nL`&H}wB@bpz8ZQ}ccY5)9!)J=^&m_%v3?HgYB%mm9o8+e!`_`m^ZOY=WHMO11) z+mYcAiZYO|~Tnz!)SW|Jy96yjm@Y zCjYt|wElC{AgP|Ir{b9%9l^T*&xF8vcJsrlafVh{{IyV1hhl6_Mo=~>s#iAi_2kqp zuM;*PaA;28gWYP=O;!AaG#rrTI&k?Xo>vw5|L-6A{b5aCubRzME*&kYS#7BO3_lZ+ z%$P$v3n^7%ZTuw{?-%xXt`u7hIQ|i^316?#W}PYc&HUEOJn;A7YXTUqHmxUWKD3D;ZQ{)=IfwlP|8LnCs_qGDsQ7*;h|%R_forjIHC zVW^T5x}T7E17`gbgc@#XMOTcdTFJpa?$DTb#VGml7Ilj3YPP~_^e3kQVv4FzQ^NQ7 zlr-#NK~b%pF*wTF+A876?7xRgN&2zhpI`>NEt`TC{4WLqJ&BsGQBRoc-wA|7W?vCD zF;f9iJ4N&yg77UWf9dQo8?pI_ILsmGP1ZZ_PBOOBq_>@XZW(Fp*1GibctEZp_No`~ zTN*wTg;1|J^ly%E2FH!md?<1LCTrGjH@>lP1%>292~~ zZtdB42n|KBjbXIzxZ8h+KX`FguH}oKH(Tkch3X>}h-p@8LGf2V&_0s5-#`VlD2F%? z-3z*#$!L0b`Z3ItgP)Rp%MwtbJ=rzJomOFX_a^VVUeFte~M! zTBQvNEaRDKE^Vm?zv?RLc;fP>W>}2)44VUq~e zsR4+u%Yk#dfgLo3yO%qX=#V?gG*JR`A~X-6WL6m(1$oY>mq`VEFL>*u$#;IslFwtz zSgbqQWZl{VCK$A}1?<%L@P<|>m_z<;j_eDrmHf>}cvWezT+^hJr4l+m3ZcIo3uWnC zP;6yty(!2hj{sRENI(X6{`89w!rOHju&caduvRf-M^*`Z*yLlgR{og{*~G!d*J`p{ zEf~Z}z*A>9jfj~~*q0|g%KY82*l+)?^>9172P8`J?&SE!y{|Gz<-A%?e-!Z{miP#B zNWbd4ivPLu&^8D=XV*>j+jrKfob45&3N!V*L%!mB$AYe24BeFhJkc+xPRm?gm|VLI z9K;Vd0cK`nLl_g|gIXisz}cGk4UlDW0`{28BQdez@~(M7l%_G1**&EQ$~}LDD;)r# zaFU~}evy!bT(V!w{@ic@tm>8o4jW34>vgHsCIFu%$6r_6b6gON%28l{3~~A#@*vjT zfrx%#`R6dx{Elx%w5zeOPcO29T6vS7FM%pA*h*C`14@5f&9$LH$#|*sf*Z#~ZkyRY zWq;PZ$B1`?c0Udlqh1=|UjPS_IC`e@n!{UI*`Rd%SQy77_vCNyDfe5RRP<QZE zwv`ubb*2G>s8+nfyRjj|J_!6diJ65ft_{fyv+^J#k!%lLPaxjE6MlG?kQ}+%UE;RX zBi`?>?poX+(dEB}bV@!;MHz7&Sjs zWWbb9Y?v>uunySKVffD<&|R|0no&OBi_HJXz^()8U9Mj6!h7)_Xn?&dORy0dC}e)P zB`}r-tHEk+kg?+eD7`A$La_{NK#|w}#-2jO3N_YmF^evTn2Egk;!W9sdF+9MQVF&a zygkDC9sk1s730>YA8X|Cbhn0D#MuCTR#l-5rw<`H@2E1$e~p5)R17w(0)0ah|LP|C zl7ACGv@q(yv_Kd0jMCV-5P&_jc0lOYJPn=ayyVGFSV^$mK*fwh3Gbzm$?!hleG+`Q zz{hPpF`M&KN)W;!c*0rkdUk~!a*TSM_F=0lMVrpej@VaOS%%ikv@N*OK-iWEEs5nZ z+bl2>8!=f=URQrr2<_On7sZb%mVHSKXV_h{|rvsvoRADpJtn|@&dem}K4BBLa z$CAkZ3Y%2S>95Z~g-W;3OB|Xljn5jgbMb-ckS_!h`DT(MKV7aNyEk=nTZZGcp*XqC z@I?rMK9`mFqG*3>#-7>)i@|`}^c}Lc2!z4@=BhKhvt2_13acZRbKupow`2oW)dI~A zeOsKT+4PLam%KsCroB&+vQUS}IiYlTyrDzXWN5Svxj5MYa0oSV4+5_Jc+BYq;+FMW z&zxeFQ|8@X={-|Y4sYB9L0T9DMtRwjC<0p`vTDGfG@>F!(LM1%l=0#CfJW0UQO{6C zE9BWPL`jsa{fZuXDOX21O=nbVQB9tBo$7LHTf%LHoqV&vwdMfm*XBLgWEmDL6gfl*x#j-OLAY@E0VJ3_VL8|+~0%Rz&Yj3 z@8>^J^pCy)KY>VyBDKVw?i%ho$TTzsY_XC-lNBkT1tlqTqdPc&NT@p3V|G z7gIvbGJBL_^>9;Lz@A$J6#6u7&h4r6jIww2G3;`vHHZm<#getnz#|D#S8S39Rf^_w$U_NTj<%@i<7+Hzivj^5 zQp#>+TR3)E%7FjFoF7PDgS^bL4$B|>+@Uo;nSYgo43RkqFn)5uw60q<-4;b}y&BRx zEDk85Q|st%_G)s4>Q$7fMNyyht-{cx%aXBX{@9{FrpyKyCTD3%%Ou>|h|6|b_^K}P zn!d-JiqGWDuX`-XS2;w8LoPa!UisS8Bn5{mK5J$CGCpoL#5f^EUIXMN>J~Gksn@VU3uf#@N7kHb$hxQ~0$EA-{G3>*1N**2ujyXn%+ZNMUiUW#T-bJwhafxd1=$ za%(&Jg`^7ZBbjipzVxuS92>rax{R^NXTpXSxP*4F7hr?kLS%&@ak(64;HNzAfRdYm zmkYjlx1kI#b6e;2&y2+CPAww+u?-8IN^8S;{ViCs-OK|>5mtX2w`TO&dP(;jhftki z(*uMh`v=T52nZ;71^$ZAqcTcqECllN{dnjRH~qraP!sgd4bOHXGe8{1izEThPXGnb z%I5#-+cNZ>jhZr=@V8v`@O6`uF1J`U-lubV)eNJk)d(S`Va|L17J%j;3_9RfBvAHm zn0IpWlWH!yKCi)sFqSnQ93u=AM)KItfDe)13TszpUh36;UeNilZNSoA;Qc|G6MxZc z65H>9!-!HoEhaI&6V)qdMF_zi}=4b2!5@;n76nfX#qRDS45ykg1VdtgX~8I z<58T)*Qfv26M8kS@vS$MQ^sisUd!z2jaW$ELG)Wa_?;87%JV*;cUO22M*j`$q6sB* zC5|A%5@~XbIwCZ#`uj1TCrS~4DJxVzJ517nFlik*Q6U!ZGY7O-mfySA5C~&=#K2mg zI~xF+R%hGg-wo0SM}p{fh=4Uc3B~AsFy3*YauxUYAAOLDA3ChO(Hq@NiDMhSHx$XB zcJmvvt9Nd6wBnmUiO|n^<6Vjrhg4DvT+gVr*O0{1d4Mt2mWtP3qaY5 zc*mx6Sj(gqnTiXd5^Jq9b}$T&AYv!Y<&ugB4S&O@>&UtJjzD%!Xw1{X>0i}dgml&z z-lXSn0f>`uwtE#!yz*=$O>^=8#Px8ooBaL2~@5rAzMb|eK?+?tXUG4N_=)RlU@k`0@0@UqNB zk#|Mp46PgWbh_t;9kiGg z>sEQI4)gH-)V8VHI7I++rLs3gmNmSAowX|WiQC0k4xWiQy!$cVbc~$6 z1K7%u9oMB{`>{4v|HU_5#qT7hs(vwn`Cyw8Al3NwHAiR;0I&H{d|!sgM<81#rXq5` zQB+1vByf)~(2hO>E85m1>c09{tgU44!DcQ3Sa}Ipnn{S$ux+$` zh4q%2+t9inb}FP4I}(FsQ*s0!iQ`K5QQf^V8m=UVm9i5g<5C(0D-J1!I3xrsE^?M* z-KQvo=Tx#mz-U+~z92%;E;mB#tyZKtkfTY&^Bvi@2fUrK4>h!?w$gE}BT{1)n?RAb z!l*RxsVSocCTC7*EBytFEpll>bUMPGtrq7Do#fSyS~TL51KosL=MfpT-UnMI{QiGa zMf^P`!RmmSG=4{#3UKE37g*_c&brVP+We}kqIiozw)*jfl zR;F^#?m%v2&kC*O(d%HpJr`%Y(^0BLrK^+0PCxU(lafU!lpBkOIjSt`-o8)3nO@7$m$GE8x5a%cp5HdIx!Ve`~a~9V%(0!CzZ%uzbzWuf8NIjS#Zv|-vHUauU zQbQtyd!%ZvJ~JZVH*TrH_CMDp{QQHQp?ygc{&^arwRZdq++ouEWy^Z`ylkRJ0>mj0 z-u@q(oiYR-@Q(nuO%SmW$M})uJwmJ?w@uyj)7kqb!A4htG-DbQ$>57BX3~=9XN|l@ z+R8Gm22XHFBnebO=9~HFrf`j>hIhLH!sq7@E-1ZuWrC(QA(t(i)w$S># zLM~-n=DL#3_H~a33Gtlxo4fg4Ae5+Y$lr0t;^JKJ@4*z7c}gT%B0tC`;B2bQCg!pS z9P{lZv~B^WuI!rvmJd8p3Z17SZ2!eJv z6+>PI)um$gMs%9Mk!h{1R=fbtqb+|WzGG`75s7@N%gg92V+_zHWUegllSe9<4pGuF{`F&}Im%suQ#@Tp7Oo~x{kVvXI) zQTSjJlIVmA7ESFhA70l3g}R`s{KB?*TyKSwOj&a3390~{l8OW7i)v!?+~ALFONCui zZ$;#^E((jb1jKV8j|DQ$l;D)td%vEi_u)99IGLH0g?EtXb<%>g%>kC>FeE(~UQ-DS3E1&s3)0?y#8-<0p-6|8zND6Q zcFNW9R1h+4J5OZ$29SjN60Z=j76D%<`^N?wg687Q@M2L%G6b{UU3z~Hkf_iTmUAE8 z1kEkv8d65Tmj)yhS2B~WvwxBIV0!k9uJO+1v}$S)%^`&L(WEWZ)kuOKCpuCg{(N{b z-QCIXVz%)D_6Y~yAv%suR*+syLe9hD@@37Xdb*Sy1kX1Oazd}#d+3u?iQ*k)k_l+D z8P|9n7_D>v0xp=CPE9U)io-)M3aDrkUNYe6@wWb}um6w7&JfX%A`UQ~FUqvUdBpn6 zE#~T&Rb#{gMsYe62<`qR)^=LUP%zCLrNa9ABA+v#hRCyZ+lnD;h&}$cfjm))q;uLJ$by+ko~FkTIf6nE zON>8tHIwklJBBIpDxqk&Wa?YC_=kaL5s@8vzz8XNMACqIQM}2H>R|1Lr%dNImw3l1 zDk?rE#3%&N%#8#aJm64^6Y0>f4zwV?uSs&_L*|6@Xw&bYx6Al*mSPeneb?ZdnDK>E zb1%cyOI9>@&HNmssqnLEgg564E2v(|SZ_zzyd<>|+Q(=PXb0ORXo)ok^#0Tb2GxU@ zF*HbmhwRSZRZJ{+9R_5yg8v~h_}SLptP7;aa7867sgcf_k*i^_42>CTLDFfY1dSK{ zb86p088P{F@njg%ib|H{q5kMfeezezAog1^HM_2~cv$Ez-NuI7rBv+)an$zV z4rOc(4=baf7!tv#hRAO@CyUj6;JBx?Qo5Aepq-XFyy>|8!BM3sbEhyBa{_p-xpuwU zFH1+sUw?%A<2_}l1K4tAnd^Mii&H`93((baG`>yq;D>cI{@1U{cKo z(qv;hnr;}Dt7ZrfeH6jUk|?P_nwUHHDUp#X2vH1D)isi~CE*PmLL9Hd4u(y(m+BMQ zbuP5wL)d<9nag)u6R~Ea0!8%1c3c^e-ZSm3P;|g4wUF z|I2r=YV6fpVqVpXY#`EHF0@1fOC>(lhf7%D6%m~7PR6v7& z##Nr>=#-DzGw#?*{dbw1#>1zfL`X2+`{^@_(Z{(NUoXzf2F(8u`A>mTW9XY&gChOB z>azajn6xW3zLKhL(9Ze0oea6~(k5*toJ7M{G5+tJvmoNosIqIkD`>JKsum`>9EwkI zbUaQzk#utX7=)QGTU=Y87Myw3tOuvS&@=Y=bpM7)Q^!4BQ`q*$7k@fcaBknCi-z)P zlNQ0Y1klC}PF8Qj*Oq%8ha_YhUK0>8wTdR^EU|Faa<7yiFr^(y)2jvAS&25NO*L2& zd23Od><^nyhvP!!%m{o8!n9BHpbVZtvF`WzFI<0TXW#?5r`_vUcIW0Q8`O*1i8S}enlT0KZ-X!)Voxm40O@2L2i!E$2ABF={e z_G=V?lDL~DnlK8;mH`D$Yd^_-Fg`Ay)IfIYhDRC7q+nb(1tZG3tDFj#=IxOY=D2}k zS%Tnkr_eSRz>o8(iciXe=Y$OafEK1&9XA>O@Sw-0%^u|X7EA~}#DO1j5V53CGR_*v z$aM!X^7=fOgD3CvmM^{MTzfnUu?(?Gm=Rb$W}O_yL^nc`PZ2s114J*OA^|q2e}4gS z$3)43@miwnM3Wcp`y&tjJFj2k!%`fM^FVo}^1O?!Wep8eJDo4&|7AilsL)~84%eVW z$ZnwvcDsAn8jl_@{p5eEc2Jn;hy*7Nw(PFh`u18j5hcUx`S9{shybB@D=|mccM3ag?HDv;3 zqeYr$dq2LDG?WTL?e?O^~9& zAS>l-HrYBS&`*zX+Q=?8)08h`M%{Kat>madozBaHzc3fx`hFMjU}J_DLL{dgWv*5t zOWLL*Z)57UA6a}b+n07&JB~Ds-;Tbf=lTZp61EX<(90HKmQg%mr&rlhik+%4Mx z!~6j)6>ES4J+${CT(Q=|DJJzhz3Dpx&urBs-kI z`@V_)ihDF<=;wPCII~V)BT#2p4X^_h=7ZOzV1<+hf}|i6>BDfg;?%^eO4sPHikV`) zFOF@%?7~v85j+64_1^HkzmJaO#fRlHba{ZOIy_9&ZSOx1+^8OZTl3OyV#xJ^_3)d5 z{eH*wM@X=kJvMGlYPJcOKoJVS(DhXmFKDZLiEA4T(_yn$WaoK8y;w}NJmZ3$WRch_ z`KUhq?DYMeX8N@9$hP~CQ83AN1a8+t@9Cys&ezH!poJR?9g6?7z-s-YdLc0tnEOyt zJ`=ISZ}9U$^b&tM(#O;1*v48|&oHm;(?_0X;@(1w#;#d6xfB>2+8Mnqth1ct;KH}fqljB3Dn{1!Kz-m()K((Imgy83k5A|m99!I0q|+*x+)m7G zJFWox@~?$Ayjb$;JdAq_(#hh~>Cm*Y^Jvzys|N-m$ew6D^&fn3F&)7duH&9L($nvh z66-Lj1lAsyf43-dE4(^q&e6PLxR+5g6OQ0zgce!~c;IIldy1#Ny# zstT}b^H*imi=H~UazN%~A-CN?pFFm2%h)_g;omf=h_OLr$(+6mcN0llgSvneODZe& z5+b3hAa3$|)HBphkdiK2g+`@Ga>SU9gx85JhWdM>d=_RqDgUZbMomDCvXm`0K7hA+ z2S}X-cl%N`5t|%fghW<1ZwwX;&6; zq)N~0-6WuFs2&6rtUvj3MAiVQYDuC6)87)Bl#*MvJ$2EW9U;07fFyQ7h{O|KD!}tl zvEYCK)3#iBc7j zuo5eK=3a%xz{sy9KvUXH93AYd(Asbsy=Tq+`Xy^Wz`|fw%HwSOqa{~epOE0XR@vOoImv5=q$w4Zr4TeqQd zhBdsN+4}?}6o!QIn(W_&T87Gs5La!Zuc;V z>5hj=fpxl3q)Wr(wxYUAo)cRe8GsO(p1(uQSPu(s(E5dVawDSmJ52H<*^~O)Hg5|{ zlA2^uoJGL$cI~4>NRsT>M) zCC0*~O)BL%1YStp0Lj}d8GcO&9nRvsruA9%h)$nU@9;fl>uoHubdL_RJPkvxd#x>W z=?0o9PLE*Sz$XN?lw`v%(wc~EQLPky=)CU5N)mW*bTtI*fxqS9Hj&2wEJIvg$nk%N zY4y=F)kqATBwH?vl}-$r<-x2i&Wj5~C-)?{3(}CAclKkLT*D5xh?CkZa*cyP)OeW$ z3z2pi%>|uU1Ds5Ar>7@<-Zc{}vD|Gwc^V;M>NhYG_2X;+(vQweJ-g;{SkuP3ECg#U z#HXI5XF;?gxAyx{y-8dCnZnwi7}><0jv@Elk;DVk)z~N`B;u8Q4@j(YL?@qwKAc!v zPD?4560BGxhSHv%f;RVvfBn?c#^5n%s8_%lEJj3rn#adUX3GD+yT@V0`CRMXF2S6D z=s#Y#H|4c1Pf`B*0ukE=?}`*BH9W%sJ*QWRa&Czj+(E+dTU zHxuo({WslqI9nLq|1+fom!r!J??@I)LQ zgN<*&Ehvx-xG2{lA#B+|N9Mup*a47Sx8g7e!Y7UgcubRWS9#VBgp^Q?$xrq5k&5iJ z?$Luhb9t}{w~aXmJ>jXaHYZ7b^VPHS%>m-cMV$p`)BOb(>6XLoY*`QokVT5EOK7tW z)%__xG0JL0(UB7XH$ce0JFH9aLaFrcBpLiSx&jB8$?DOef(4-62Q$QGQed=Xtv4v8 zzLp*eM_E#soA}ac&EM_F!oI$jTE+YCV z;=d4+dYae=JvD{|>uUYQcY(B!Ad&_9aB8S_I$oOm)+7En>3>!)%;y6i3|o+s{S z5+mhUCzp)1Vxrj&Vtb|02%fdW%TGw%X1W4}vZX;mjmAF4&@-t132^skM`D zeX)>pW+-X89oG37IXec|g#c-;e4r*b!^RmAD7RT7P5UzyFJX~$BM*8Jwf~S0%u1I! zYQQmZ?`^xd_}1}*tDDN^1c%3ACQPNjFZnSl6QLd?6R@XD7qdk=O>N~YkaD$OTuth1 z+Oe$kNwXIQVgi^ajneXzM^%N|=J=p@F<%n(6rZy==#qayd-E|Dh>qc*zQk|9pB2glYyu!pebLN1rqzgwJSY?U)Z($&j z>lz1NFKSh|91P*J69%tD)G0mH8C$Iv)9LNxW=M#sc&3)p_=%%)+LNXzlm6Z91Z^&f z3s%@s>GU-2ECccP5X5ZnugC$p?f7a$_WTebP`=9ra&%nm-bvjNHktnRQWX3mZ^ng>nugqcUL z5Jq!;zrLUDbky2|@SwDsIi5st!nVhq zr%f=LQJNH}HLgTA3sqWZsaF>3l%qp^GgcV=MU51a3u_<4OYB&0lb~CMMA`Me)Rft1 zVZJc0#+;7^(r3x%O{)s7b1^u=-CZ5k07f>+8Obp#6v3L{a|Q4Z@TN0gB?mN6#^#5h z5G*X|Rfv)P?|M_d53@0Z#@WjCYv&>6mzyASoiRLapHtym9x~3PvJKZ|3cQnYZ-Tij zNQ~bpqKjEXgsIN+U0|0hJSTtK-ytcV#&VlnV|D@6m^JAATIhP2P#c83ZVzk7TROrr z!Mi+=-@A|%)^jM>tO*hJ+1)C4$Ux3Yu^iSPd%ADL*xMo3 z8UgJNJpB-FKF_^nR^}^7quw+%R2RgQGB55ZoJ|*^gD8^gUe83gNNKy!s&(~*y*7m2*cy`W2RQP1C(qMP zLd{htV)7M1w7z24C|-&>sZ0?W=a8Mzh`)|pux7$s?uXO5Em0epJgE`bZu2hGAOW7p zF-CP&tgC>`*_IwrK*QdXqOh<+vlHfMF1{QbEtnqqrv-PSw$$}xLTRwd3Ks$qJ9kWei=-Qwyo#S4(2l~ zpYLLk{|uZ!S+69sFZDZyIjN;uBWjz_hSAoIeaysBx|$oNxjF;zIE~)HbOs?iFnGYd{7+nL0dyBfwJmI{o-nJ!Mz@{g{s4fmHcC1@l&lMmNlK!)+R!x8* zoeSmN8R-Y*7PYE?ZHF{_)`A)F z!ezjQw1Wo+2uR6f7hHWj>WZZg3qfCe9~hsg=opw9j>~iFg{#~hiHxqG8^wPgjMpU} zT4e2ep^UlG{SDBovPaXdMv-_$Jkt?#ZCwDlH&zlsvzqGQqXDol-cypFeLf4xS?RS` zG?}yt>o^T)BR$gFrTwnWH#)OXLV!Klo#Oj3%Y@UHWG##c+G!PaJ?@UvVV<~|Dg2Ds zV}xH7vWO>r@fDO&*^vh#ZS@nhiWFfci`JXz@A7Ve7xl)MmfA#*BA1H4m)v1llo5&+|?N6TPq1wN^#(6%LjjVRBd9oN@oL zm%48v;=BAXWw}bmVETSs69@UvzjVLhUs<#clX`n-BwZEkD0}R~{o;5JBXvY*B+rMI zBmpw=rhMlbZMrHl)oh2G6D!Xz_8ted2aZkHTa$x|WS3a>?%o@nNhww+AO+?D96z+M zHorDym-dNX!7J}93rVErS$dyvP!qj#t@lVjvn^H>XS8QbK9$9B3 zTFZ{@bT$$yuhCH9^@~0(UW-gT%7;*oQ}2`EhrVTY^Xi(d2bBTjqm-A)85`{HfGmc06nx4gWRI=rt*+?lA0p?$ z#PtSUL#?$@EH6hO#8R4^CZ^3Ww=A|iH?b@Eg~%OZ(t2s#aU$QVBJH4mx#YNt7q~a? z3&t3QLCMZD&1df!E(7I!vpNHXL#z)!{VgF|=@)PVBynLZ6$K$a7Xw?L@4SQ>-xGK~ z;^2*oD1MC&lV&Ctq=tzzTNC4VXMYKrsdq~16?irCWzA5o6rWD8RIAj_G!eo&dh}R` zFz!*&0D@eiTH|~L_Z|x&XGoeWs88MP^8&b@KiBK;9^t6VX(#`5HlCx7{j*Tf`^rFk z!^!zwRJzea$};TvB#j~Qmk9k@6jQ=Y?xqZ$6No~x#r*< z4Mr$XA`26|{@5f)9bC(WPnn^sCH`!YGVxrSz4v8+P%mGYfMzV`s8?NB#iYBpfMum? zsS+OYC$v)hhi26*5>nhZzYi?Tr5;aZj4oexuM?pF%^;vmQSTCj|3U3-KKt1GyU22F z@YtNGcLDskKjQMkQvA5OX=4P2AEY(nCA9m3hN+2S+2ymT5wJs{+)3KAy}xlJnd zkdTH!ha^u_0wm^m=FV2ylVSb=nQf{zjJmyoGl?tsxP&XhQT6HkfCR@I;(^3TUYm z?+^kConqy?=7%$nsV&Evqwew%TXuuuCgVfBSSqVkp|Dc}B&15seV~{^Ue1K(1UX5$ z7~z(goXU)QXo2Tb`2Ir4$5u0QN8ydqq5i>$>V1bOdT>jS*Up&z#;oO){0f-*OF+Wt zt7htfoYvYo5YME7(HE6#g5qVH?f7#Ug<_9t&a59BX6MrVd`P4@y35PhK278GrAN;a zzm!^FJa|EKt{tCQZ)I>Ani%};ZQX5=Jg8!dO}R=aHkH|9BnH`y#p!)2`|A7fDfqMD z(8#O0d7M|n8@0CIOE<{0Tm%ierqMl!4+d2xrY!sOq`--SZZ*z}Jk=DSLV{>;X$jcJ z^sx>>6E0Kv1}7b>ivw?z4bs_+KIISc^nr$byD7}is$v2MH8Q-#-@LUwSHbe=JOKlk~ij4#xolW)hO{o(Tz)aL~G$>6t%# zMAlU;j=c9ntEVknDv$Gk(`i@oP4q?978|okBeLzxgt(r98(a&alPm-8?OLe*%GNk3|c zCgBf-{CNSmfm0~C>fE1q{i&rnwXRgx$0OIxsKZjMaz(JQ1cXRj3p{4Vy>cCP^#6gI zTr?C2WHja3!fjWwo1g5vyZd)Kl|aU_h1$D*H#>;@<5W3E4D0~*LGRUQPqrDtxeIf- z5L2jFCcef{BaDCksh?-ZN110WL9kBGO-H_%A~Uh^B=%5Qs(EORKBc=UOq&~0xWUz= zX5@E7kyA`9u^bv{}3cOa-Ukrz@Aw*wxzcGlhr54C$UMPV_eHvBO(X{`EPxG=-qg@^m&iB(@s z$BiZ%9|&o&9@#^0-pUG7JUKPtKy0qwaIcy_kZY6@GQDg}IF+ZX)x=5kEz>;PKk0FU z*Z&4Z28xLrFl`KXL&GyhvS|P?RG`BbS8+yn5{p++X8a_1+G2d!ZiB)9>V3{?=1)ZD zoi*Lo({g)LcwrFf5X)w9Rn;q9j#YsMKvVvza zHh*;mwo0<{;&T>66LpMbKsuO6FT5gq6ZCyM*7k&1cCZ6;48Od^u{@(GYsUDb%Wz)jM`c@zBu#WiA2x30TZbh5F+{RQtLa!qHCnhnL3j0jf@r|I5-T!;b*aIYLge=47?j2L+Aota$sy^Epx|xAOWx2)R)cDWy z+p2-H1Z<96(kA&Siuu=Zk={UwhPZCQjz(5a=%b7Xt|2m7bkF_XeLqf0CN^(S?q+e8 z5B$lP9S6@+0bNc#1Me036f6)|-L?n***I?F%NQUX#(n;wAc}jg?U|UAe_yKLM|WrP zQ3?{USHKz}=!7xkw>?xz>};*<^sAOQ-~1@RKI9$E#kop^1-xwiBJvZXigFV1*#i1I z&Jw*uE&g1p0BG}<>QF$F?NKtFZ1#hKXJHvUa+N6>PH+PQ>iRtR{Q&8kbN6WOo1t@+>}XNo%fq7z3Oef)zMp=NwK6&c*9NOMh|x~2=6uDP zvj3(GQ}`A%Q%#Nc1{J!GC5EtSd2F7vcp<%{hOO1v^`~%CI1NFLCom2rDh?}d_de@} zq(gea4c?hzS4JC1fiUJVF1=x1T!K31x&Bu$uM9{GHN~7~tkjiu7__=eHOH+2kXh$H zBxZa<{jqsN!yT3kDijpA^$Fh$o<_By{4NXyEN;HUii<1a7q@L2^AH{@R%r4y*VS`l z<73wRSc@pvVXkwt&j&nVQDp=%af0W@1p}5ZDt+mNcrw)mOWp>^QY4wXRLEigc{dzI8}%x*Qx^Cqf%qalXyFCKChJiv;StT1n8%i@h?D0^op zysi7Dc0o`~JTM)tM}Px%Uer_4T-Vk_ASaplsKU#%EzlgS{`IE+w}NgQ_cg^PyKZ{; zZYJs&M^=^AH)@dl+7N@b9V$CP15b#0521VO&5sn0nzY5j$gA5C@@4j?=&S6W?vh%JTz1>?Y8THg@QBmW^q_%j?A%fn=1!OV4ds+g~Ea8riT3IGfkn~g&r`6+@K>AUp9O&sGxxi$blJ& zbG+B)Gw4$UHIO@vGKRJ2-;I$@G@-o?o~O($Q%6V71?r-?tY_rDhHddk1!n+ z6Tijl*rVHk?}XoV0|aR5^4}jtFfOxz*ujnjV@6lE+{p{D{~pf*i*YB2CaO({M&VB4 z6~E|&IzSxSF`Tevv)b>W1Vxuwa_BlE{0tc1I}$-FGqPn^aGab}<1&-|5dn+M4J*A@ z9*GRrkvr*Rn)VGV$l9;PvaDU8szQ7fxUp#j*hK51_OYOv7X8K~Gd9=@AzyLcprp6CLkNIjC{I&%xip`hWqNRz#CuOFGOY0s=JjS>MlK&UoDhG`- zcnj^TijmVUqjk88^zFkt`6oC{#q{bqIVlsvEvTYtEqzw4fEK`w`byzY_bGK3iKh)0 z6R~myLFj-{8@eT0G|3m5HfA3=;)?Q>*T2_tsq~+i&m$ zC$d-7XVMAjj>T9_g3aIDvuM;mK(Irah{}Huj&xc4lpax$z)(BCAo{U+`Eg|o8$&H< zhSbXC>)oN2m@ZlV6#xONb{YB@=k=RaGQ8vSC#0{}Lg=F(?zAK0EtXr={f14(^7mXGyhAEQfrVXI%^{Ov2>u? z_?U|hlIX0D)!(ENiWfTUi1Vi-RX^$1)k9_u5`OD(L@@@pR{qwmW0oh9N6e7FaQQ~I z#}wsYY9s{8Q>?TZERsVqHU6fd2IdvMK-Kt&;3icnQH(atQFzMl_LwVPZBDV?_OcSOLkAD`D7&Qg?rc@On>gy{NAS}R zH=mPzvHfY$j+FPHBPjng!CvtL_pYAo|Ami+!e&RP1wuVE>f;;jA2tC8E5FAmF76O~b;FiFWs`HlF>!V+Sf{99V z>>=Mz+Q(D?k|teiaM#FZDJ_@VAziz!{F~JKxG|C%I{6^rH3bfyNxGuKTKO42Xo9q! z)GCh`$lsI;Y#B+2YV13rONuTRYK?7Cy2EJwSs0i-EdVyC5`CrAMoiQ={?cwH3NwYJ zi1`AecdhTO1RpE|Jy#4Sc5%h9*7g}z(9v^|9^az`ZP6BGCpYSQ)2*g`HN^&@Q8nZZ zQ%y5+FyfRVR)PP@%YUP9m)mwl;;{^y)l0X-DwAgf=~%Z@Z+!6oMX~PG`U?yAVl=$U z0{(6hKtD%;dlp(8DyL)k+afQFyd?L#>jUJLVx$3{x>oL}TT|PO5>PZ=2B~*$bj5>h z+37H^Iodl|8>#5ZZV|=hZZO2*tWW0(2+2a+L!(uMTS7N&$LwKso7~!+Htqi!@%QJ1 z)+8SclCQ&2*-w`(^SOTAF};sp_qh{cwa}bl03@2WvUPrg0v-LIyZu}SljAObH^P@3 ztG_kTjPu>xGrM)QMj+^;eNNw1Md1dpTA48u13CJ zwvZ#)1^r);0YVL1Sbn67njCHB>R9`Yzz5f2iD`7&1RH}2qumU!f$IX)IhE)y;iixZN8B9y z)c0YX_nwL+5^C-Apt_;AG@~_!la4_%)O&R|+%a77NZ{uqiq(oS%ed{GA67lP*?`; zQ=<~E0D`i#$Do@*wi8Hua20EBTGyI6)tkL{u%2F>4TI9*??f|x%|l3vYxRogBQn)K zeTM8S=N5c5hQD^4UvY5~umf6xiVOWhJjAB`hRn;wc0c&wIKjMpz|D*o8-2+RnJG=b zLwPuGsHI#v*v>q*Rz3`+6@WH%YO}+P9RpEecG|@0d-{EG*n)kVeXpB`7;XKx?fb~q z9r$}U3P&5VzE8>*$FtyWL#GqXpdAw3b_TTbc|EJ@w={vC8iMa38h`RzYLBj{#k?vB z6BsLu8hxSdM{_bGmit;8FZumdb8KUR5)FOzOga1V*xZid^ccZ1;E4i5arfoRkuY3m zC^8EwphhG>q`8)aaH7VA^jKLlqzc&T-!H40Xe!)tN>LI`!dDs3qaGg-*1JAD7O1cE z-RpL4?kcZ)o#GN1(lggPg-uN^GtWvbkAL~Z`H>yqGFST&VMBx;2hju>JVF(f<5ZO0 zg51H=JCinehR)ix{45;bP@kv-@O z9_WEzy4I9BYm7*y6cyGQ<<@hZl%a zz~Af4l>)9*g|m8Gbw$ZkmqsB)kq4_q5g*aKCN5+5^GMM?-)fkW_>s)qI&d?rsqT(5kPOshhCM*Fn%;`;9M2eQ;>WY<6P~;bMInvsju}Z5>6NIg7;Pz zVQ_*=A+7xid;j2qC@=;d%u4YF8m~#ElM`hH(B^Buy^So`ajGu?je31&j_?LL3Z;{K zJJE9v&3P8*Gjj+aX}7@VB?%6z7-p>*U6uzu{)~r`+)8rZdzD4p0J%oMcQhgoYjNB% z`qib-HdSKEUzlNCKHTFnAC4Hcsry}bI$NvU4HQf=S$zG5by|2=0vp1Ok@W+7Np_{< zcJvyQLkA}#NY;ar%dXDZfP5ZQL!G>PB9c(IPOJL?cC&sT+V6hOw#h+6D*k%2MwDgx zAqtU%!>(HMwq{@P4b_JoP{Fc24n1&#SF_sGhpwO)$KL3;7$mM67p&EEeU*=VE=wBT zgRzE)7fpt|KV zXktf0J*iCJC%4G2@YF(Ta4Qrq%Sa8eE3iV-r9lY@=Aw0dXoTE{8mLv}e<6Ir!(OEk zpJ?ZTD~50t&T#t{7*k%OiC6~A9e1>LUJCHEF3cp5pIvvTfaZ;4|IF8}+1fa6yz8yn z(d$SPxxM4;r3`qay@VS>gbNy#FP8B?Mk_ln)2Ab;O;I4HCLdpuw;y*>@Nn3%qLd$E zDuWJaJ-sIXvpE}jph6Xs?^@g%rM$=pLt!wRV`lt$h%FAkx~LqZDvF3l=s2lzK(YXa z2Q{XkpyPprNCEJbC>tMPJ?RDy<|Q)uts1NxD<>aZfjf{zAx<%yYuA>JSF9e#6D*3%Q|G?@E8&q90H`uJMC~bL1tgNbfx%3a{kE z<}YqzpO{tS2Oc_0+UdWJ%;NrqGU#=i`OwYqh3WN_nMf&lMEOy+eJ9&%^;R5HT(`U} zJ&Hckw>g3zd?_t{DhG9eff>YBKD#O8oB1Jv)}2t#xciqkJGz_<<5&yKI^7+ z^W9DN_mD08cZ2*1bPX#JPh%Z=2YbxDhXMl(Cg-oYke9yhIw>_Dc=6;uYl~8F1?V6$ znk|q9btypGZcxUXj_fkRmKb(stITs=S=phT(oguNGT3TW7A@c0I)Ll;Fux25bfWvU zmx4&vIS>9%-Z}#^1z`>aD!tlQO(z9$_Wes{wBq!c$+mEu9X81^wwhtkp=x}$%2m}I z_4GfEkd&&qR=fIL*{i4>I(h#ang{p7#Bi8{3)FeWd`9TD_|QV0=c?vv4Nwb&>(eYZ zzeZ|dr*+0WG5E>qiGRc!%|-LCV!bUD*K46OoVpQlO}82Z0-oAOp~Y0dRL3U_bNN~F zV!hO-vbUqAP)hb8M8JLFzVGRKexQT4{>HWzKcQs(4KPU5$L|GW>9ArND3a6pax z^hIQF2x~_BM_9EjrL0xK77i}A_#M9p|9Rfg_;UOuU2_|*=OI6HX>^fo*1Um z-|H{bC33uuR#Y~d1Whn;`oci|8@0JVXSsg>R2_qNVgZ-oK6WVI`k~^n9{WG?;67F3 zgkZ)heh^a@zF64TrVKGf==t57)wedDmwx*~(4s|uB@!NvwNbWP2;kl7p6`&Aan~nG z#HK{D8oZt^n7YvCs#qZ<`oq6-tdYKIgGlkO_LjW*|0xVX97=yZ5t`GDmwes)xV#Fy zCwBwwH1@jDt z%ckvNl9#fSJbKMES1=esd;BC;7wY)f5yIjYz+Y`p`HXx|kzeyDtlj_P*pULjKJ|g0 zY5-@F4QnAzi{S2U<&D=w&lRSbxY0T+0ABMI;8IXV7a5z~6%Gl$LJl?38CI-~Vlf%j zZLG7&2;bF(YG9_j+|#{aR0KOYd@!7g63^|epcGiCKg*aml?1Zg}-65hgg{g2cqfn<7GWPe*O<&Wu#FR6in*_s&q~^00 zgN-_0e8AH}X=ljq2a_~~o#E$jmnUmt3WH!yi`~y53&ld<{C2M6o2n`<^y~6+xXZQXoDF!{TUz{6|=Xu8jZ(&ylNpV$tahDc8O9$**w z^L|;c5MvUDZbFQXYv-Bw3fjL(XriE*y57R%d;~^XZgG{drtuNrD9oJ&Qv> z4zyfaQ9O4fQZR2HM-z#plD|P@zn*4uA&f)Y0<)#N3~rP<@saFXOQ^Zbb57`P(4Y(# zi6l7%t@Dacw*hO#@jSrd)kD=h@I?W?vlUx|86dbT1&#}fMbRyR={5s^!#)7CS@De|G zDjk=Oz7<9^A&+(A+5{H$7h5jzE_`o$TBD=tW_lCgV(*4UKT6w%$L8c3y8f>v#vz6k zInA7jp|+p{xL>LaaO(Z5w40z|ZBg)W=f}}MT6V7~s)6;DjP^sacT~1a?2Peztea1^ z=E`H-^-U(y*a4r_+D80tW(txP0{>(Ow>Pez-XjH+4E@xDxtU#W7#dcQ_I$_=yG6oy z_FZ?4{11AI&QU0#s((NvQma#YjLE}pw>tdFYfkY)$R{x@ak%`zz%2^Ab9M;r7ORz5 z>kfi2l}66u`_lSf*|o$?1iO6O+7`|97vWhhm^$i*A>WZ(Z0p>p3MJ3=HZgQPCi%Lz z_~nUlvynbeF4;%O;>O-xsACO`tYY`fKwHNoz+!|SzjpK~GOSi2McYLDf(&1GJM!}> z66r-MMc-x5WFmILvJ|HhJ5UK}KRR^ZajaXcq1VM1gE0G~1D^AJv3u)a*W`SF>gj5! zGhH!f7)`wRnN^uG+2xW&ssr|VN!Lcb!=c-JAWv?nC=!PC8EIiPFTBEN!jucmg7Xc6 z65z4ounlV8`eCLs4KI+Gdw9;dQV)X+>UZ}&2Y|bQkTsnpBB*IfI3>XLhKps-E{#Ha zHKU5YH05tf#BqAjCw$EDM53ADgO8nP!fUl<M%ne zZ1lZ2tloP95ZUypQd(<1pm+7x3fv|pHf*4$X&PhRxThOp!+=Ry!idI}U45r)GHT0~ ziCnZ~Ym|1eKNck4DnL}LjqNgNY923UA0pJM?kZwQNre2sEl`OX!?}6b2?pL1xSqmN zS@#jy&f?z*I1s&-swvJHSXPe`Qe?W$Z7#E-}ji0c*&bu z0cT}}XpI6Kn)@#-a>E>tQUjdmm)nCUu;1*s;SvVw&_ypStQkVsde5JTU^sn=!Lv@Q zP&?J1S9$^E#&D%O{mi2)usF zn71-O+#&g^nbC6NQg6xMmehZw}5-ziAA!Ka3*wegJT<@;G4kxL?^O(MY zE`9cYAR{WEw*;K>^C@MT$hV_@--kNH^DeF1sN9hPg3fRrBh6CQW#mG_n@ZYl`Ak1( z=Eya6Po*BkEvWb=gIr3JXgyCYKJx<3LFBy-A3V~~Xi(3eCI9U@tDRPua1H%bwqy+y z9Hb@7=Y9nsskx8ZUe~0yQCvqNb(&1gos8FUasWb5`7KP-4cs@dCZv-PSGK$-H|GZ` z!hH!-LcZguI53IyMfs1`N2=zop7LE_NrC!S@e(Rj^3=b~_8^+jOU-Q`wboc01%Tz< z?IQ$8G>U`_nq0q6H#&pdlSgH-kXIwe%5RX3i^trzp_pF)PdVT|4j=Yq)z=tqi!T#eAq`BUoKY3q6~h1~*;o4a=g9J>m5ROl3B z;K&yi*+P4&)SnhaDq`gvWO9v+EQr9i*7o^@thy2pjgalR6rZxN%wTz z#q%-s&K?72W&|TLqpl(bw}xZPT4eQrnx1j()av>Jj6tq*y-kpUOmX!)U#w8S@#4#) z>S7ziWj!m(5Mgr}UjHJp79%a#ugO}r30x@cS>j%VrTw^J8nFYn@=nkLfDjNn!(91FUcA8S`c zZuBR{DywQq?~8aoudsWk-uvS?4zH4Da`=q{me|qx_SI{mT9JsNv=6|C(c|>c0@NB8 zXHig!gp!_=h^&PLsAa@kk3v`XXyyO@C&fdQ9+8qE&

wVcJ_WD8!BuT$dM~hi8n4)oa zy_@0Jpibr@KBJgc;!1O#dIuesht-a!jAKlg{MV>r4lFV#Mf!(heH;kCttP^2PA%5h z2>Ea65k*~_D|dH$y_hGStgKHFI^za;)|B`xHBC@yu;YaSser%&E8nKY_ULz{KUzFkT}{b{bFA8?P$Z(?6_GFY8aTz3};2xR!2{T3qw7 zT$LWhf&}S-*dA;}Z&g(A56_LdNt!J-46l6Uz23cPAsnAa@>rE2y}EE!A5xpvRau2N zDtT-3+ONW_WoV5>HK=wP(Tv+`W}5LT*KeT06?tx8!${?3@|Vq{2G27Sp(N;3G2=zj zK;dQP6?ZCJbaH5kJqvB_J`!_T z2DYWh$m)w`oE#NRQO_5S53=7$y}m7;S^N+sA@dRa!5>UXXE?D4(v{u(t1Uup z=_=R#-Y-ztOEQfaI2K%kzlpsR1ky9(so7reX8M3V#^w~Ys&`Ve3^~~2+&Yqa@4-va z4(+loyu^a};VuEB{mi^}KH|_D&|o6)2xagKA$@&$3AB|Fp^x`!P^_xYO+Y;YGi$AS zlDC9odI3&pb+lvvbMKOYgwPtfxm?^wp(&+p7RWXBwP8P0iP2QmHu;mMvDB zpGB?Bw4pOB1O3&ndCe}a>bhRYGd-lY2D*2twYB4RVFKkp>-7W41c~Zbv&zAGsRQ;y zc*c<{H)51vgnmQurLrYYDBVKX)RCAbuZWzhSUQ!ycjUJ~ja^e&^Dp$K;TTLlPKqom z3i4~(qaui!MPD*Aw!`r6-G+M)%saqAOR_vWS&76XZQlK@nReyu>D4mV{?it=Od-Yjx zi~#Cj77MeK=`FdXlp%;&50;K8($RrybbjW^c?IIf#Zt@l4V8x5>KgPkFWT1M*+o6tJ`(gz(WW|X zm}2H_;XBdT=z$f{4l_2b};h0qAM3hJK+Cc!V3}2X=oAXE$%NwtjM;u|rtyitKPlMg4T< zuINH@!g}c+zx(n5JhfB)A`jbox$Y^T*T=Lq8TTKOWphze&p^r>q(B9mMa#ES5zc)p zSXqa%e7NyfNkaT)fnoPv>(ZuNa=fvh;#uMy)l99q^7KK0x~$fevNVUXnK-U_z2&kf zKK(`|VOScs4PL_XVwnR1uhF1#AKVMuOMU2(VKY4)>eKykfbnprvBjh&#t$ms3)fZ7 z!)uK+ZaQ*eP%|{(@j)@p=%IwDwDxxP4Qgp{N>IKaWchEtJ@?^M7!cig=4=z9VHVMc zPB{}?o0{a!>U~R2iki3LyUslw=6h%R7Y?u;gx+6;m8FJ@9Ub% zYt(*@YYF@W`si;<(Cal2!cWNkOCc385IiJZ06Ri?27qG%&0V2RtOVdV9TS%yZ63>r z(cDhCVyIQ&ys2KEnoI{XhM=IqDw%i0FrUJ|FEvvSJ`-%{P_iikuU<+7GN~~~ecP#m zPlx)d532FTBU|GsP(0bL?OwrBJBTx5$1t`8Se)deL1G_Hx+!)>+=)4yl7y;~i`;3aR1eN84uTyqJT}g<)Ah5MylM#;knpv@m zg18em&Q=+w*@HQUs6d|mYEJPQ38uEW43O&gX9~#xm<^QjBYrs7>ljr5a$il%-cpX^ z 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