azot 5.0.0-alpha.2 → 5.0.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -4
- package/dist/api.mjs +1 -0
- package/dist/azot.mjs +20 -0
- package/dist/azt.mjs +2 -0
- package/dist/builtins/instagram/instagram.cjs +121 -0
- package/dist/builtins/ntv/ntv.cjs +104 -0
- package/dist/builtins/rbc/rbc.cjs +37 -0
- package/dist/builtins/rutube/rutube.cjs +92 -0
- package/dist/builtins/soundcloud/soundcloud.cjs +94 -0
- package/dist/builtins/tiktok/tiktok.cjs +76 -0
- package/dist/builtins/virtualroom/virtualroom.cjs +46 -0
- package/dist/builtins/vk/vk.cjs +122 -0
- package/dist/builtins/weibo/weibo.cjs +64 -0
- package/dist/context--_LoC7SX.mjs +3 -0
- package/dist/context.mjs +1 -0
- package/dist/vitest.mjs +1 -0
- package/package.json +62 -139
- package/types/{main.d.ts → api.d.ts} +31 -15
- package/types/extension-v2.d.ts +81 -0
- package/types/extension.d.ts +8 -8
- package/types/feedback.d.ts +86 -0
- package/types/manifest.d.ts +9 -0
- package/out/azt.js +0 -2
- package/out/chunk-nOFOJqeH.js +0 -1
- package/out/main.js +0 -1
- package/out/test.js +0 -3
- package/types/input.d.ts +0 -15
- /package/types/{test.d.ts → context.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
|
-
# Azot
|
|
1
|
+
# Azot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Extensions API
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Type definitions for the latest [Azot](https://azot.mov) API.
|
|
6
6
|
|
|
7
|
-
Check [TypeScript
|
|
7
|
+
Check [docs](https://azot.mov/docs/extensions/globals) or [TypeScript definitions](https://npmx.dev/package-code/azot/v/5.0.0-alpha.18/types) to get started with our API. If you want to discover and install extensions, check out [Extension List](https://azot.mov/extensions).
|
|
8
|
+
|
|
9
|
+
## Command-line interface
|
|
10
|
+
|
|
11
|
+
### Quick Start
|
|
12
|
+
|
|
13
|
+
Run following command to install Azot CLI globally ([Node.js](https://nodejs.org/en/download) 24 or higher is required):
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
npm install -g azot
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
> Use `azot --help` command to see all available options.
|
|
20
|
+
|
|
21
|
+
Install `premier` extension to download videos from [PREMIER](https://premier.one/)):
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
azot install premier
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Download a video from [RUTUBE](https://rutube.ru/) using built-in `rutube` extension:
|
|
28
|
+
|
|
29
|
+
```shell
|
|
30
|
+
azot https://rutube.ru/video/a5d8e842f1b9ea08d9f64cd78d7e722e/
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
11:19:44.874 INFO Fetching content metadata...
|
|
35
|
+
11:19:45.257 INFO Fetching metadata finished
|
|
36
|
+
11:19:45.289 INFO Как речной транспорт экономит время москвичей? — Москва 24
|
|
37
|
+
11:19:45.290 INFO Content Matched: HTTP Live Streaming
|
|
38
|
+
11:19:45.290 INFO Parsing streams...
|
|
39
|
+
11:19:46.223 INFO Extracted, there are 16 tracks, 8 video tracks, 8 audio tracks
|
|
40
|
+
11:19:46.226 INFO Как речной транспорт экономит время москвичей — Москва 24 (480p, avc) ∙ VIDEO ∙ 848x480 ∙ 485 KiB/s
|
|
41
|
+
✔:19:46.226 INFO Downloading ∙ 21.77 MB
|
|
42
|
+
11:19:52.761 INFO Как речной транспорт экономит время москвичей — Москва 24 (480p, avc) ∙ AUDIO ∙ UND ∙ 44100 kHz ∙ 485 KiB/s
|
|
43
|
+
✔:19:52.762 INFO Downloading ∙ 21.77 MB
|
|
44
|
+
✔:19:57.009 INFO Muxed
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
> You can find downloaded video in `~/Downloads/` directory by default.
|
|
8
48
|
|
|
9
49
|
## Community
|
|
10
50
|
|
package/dist/api.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=Azot??Streamyx??{};e&&(e.defineExtension||=e=>e);const t=e.defineExtension,n=e.version,r=e.sleep,i=e.input,a=e.prompt,o=e.notify,s=e.confirm,c=e.ask,l=e.utils,u=e.Input,d=e.UrlSource,f=e.ALL_FORMATS,p=e.DASH,m=e.DASH_FORMATS,h=e.HLS,g=e.HLS_FORMATS,_=e.MP4,v=e.MP3;export{f as ALL_FORMATS,p as DASH,m as DASH_FORMATS,h as HLS,g as HLS_FORMATS,u as Input,v as MP3,_ as MP4,d as UrlSource,c as ask,s as confirm,e as default,t as defineExtension,i as input,o as notify,a as prompt,r as sleep,l as utils,n as version};
|
package/dist/azot.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{A as shortVersion,C as SETTINGS_PATH,D as VITE_API_URL,E as APP_TITLE,O as VITE_PUBLIC_KEY,S as EXTENSIONS_SOURCES_DIR,T as APP_NAME,_ as APP_TEMP_DIR,a as prompt,b as EXTENSIONS_DIR,c as FileStorage,d as normalizeError,f as clearOutdatedLogs,g as APP_LOG_DIR,h as APP_DATA_DIR,i as httpClient,j as package_default,k as fullVersion,l as createConf,m as APP_CONFIG_DIR,n as load,o as getCurrentDateTimeString,p as logger,r as HttpClient,s as getLogPrefix,t as createContext,u as errors,v as DOWNLOAD_DIR,w as resolveDir,x as EXTENSIONS_INDEX_PATH,y as EXTENSIONS_DATA_DIR}from"./context--_LoC7SX.mjs";import{createRequire}from"node:module";import fs,{constants,existsSync,rmSync}from"node:fs";import*as path$1 from"node:path";import path,{basename,dirname,extname,join,parse,resolve}from"node:path";import yargs from"yargs";import"urlpattern-polyfill";import colors from"picocolors";import*as fs$1 from"node:fs/promises";import fsp,{open,readFile,readdir,rm,stat,writeFile}from"node:fs/promises";import process$1,{platform,stdout}from"node:process";import{hideBin}from"yargs/helpers";import{EventEmitter as EventEmitter$1}from"node:events";import{z}from"zod";import{DASH_FORMATS,HLS_FORMATS,Input,UrlSource}from"dasha";import kyImport from"ky";import{Agent,ProxyAgent}from"undici";import{checkbox,confirm}from"@inquirer/prompts";import{execFileSync}from"node:child_process";import{createAuthClient}from"better-auth/client";import{deviceAuthorizationClient}from"better-auth/client/plugins";import{tributeClient}from"@tribute-tg/better-auth";import{QueryCache,QueryClient}from"@tanstack/query-core";import{createHash}from"node:crypto";import{inspect}from"node:util";import{ALL_FORMATS as ALL_FORMATS$1,EncodedAudioPacketSource,EncodedPacketSink,EncodedVideoPacketSource,Input as Input$1,MkvOutputFormat,Mp4OutputFormat,Output,StreamSource,StreamTarget,TextSubtitleSource,UrlSource as UrlSource$1}from"mediabunny";import{finished,pipeline}from"node:stream/promises";import{Readable}from"node:stream";import{isatty}from"node:tty";import{createSpinner}from"nanospinner";import{RemoteCdm,requestMediaKeySystemAccess}from"okova";import{Decryption,FilePathSource,FilePathTarget,Input as Input$2,Output as Output$1}from"shifro";import{convert}from"@transcrobes/subs-convert";import open$1 from"open";var __create=Object.create,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__copyProps=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=__getOwnPropNames(t),a=0,o=i.length,s;a<o;a++)s=i[a],!__hasOwnProp.call(e,s)&&s!==n&&__defProp(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=__getOwnPropDesc(t,s))||r.enumerable});return e},__toESM=(e,t,n)=>(n=e==null?{}:__create(__getProtoOf(e)),__copyProps(t||!e||!e.__esModule?__defProp(n,`default`,{value:e,enumerable:!0}):n,e)),__require=createRequire(import.meta.url);const getWinConsoleAddonPath=()=>{let e=[];process.resourcesPath&&e.push(join(process.resourcesPath,`bin`,`winconsole.node`));let t=join(__dirname,`..`,`..`,`..`);return e.push(join(t,`assets`,`bin`,`win32`,`winconsole.node`)),e.push(join(t,`native`,`winconsole`,`build`,`Release`,`winconsole.node`)),e.find(e=>existsSync(e))||null},loadWinConsoleAddon=()=>{if(process.platform!==`win32`)return null;let e=getWinConsoleAddonPath();if(!e)return null;try{return __require(e)}catch{return null}};if(process.platform===`win32`){let e=loadWinConsoleAddon();e&&(e.attachParentConsole?.(),e.enableUtf8?.())}const getCliArgs=(e=process.argv,t=process.env)=>t.ELECTRON_RUN_AS_NODE===`1`?e.slice(2):hideBin(e),getDefaultLanguage=()=>Intl.DateTimeFormat().resolvedOptions().locale.startsWith(`ru`)?`ru`:`en`,THEME={dark:`dark`,light:`light`,system:`system`},VIDEO_CONTAINER_FORMAT={mp4:`mp4`,mkv:`mkv`},SettingsSchema=z.object({downloadDir:z.string().default(DOWNLOAD_DIR),binariesDir:z.string().default(resolveDir(join(APP_CONFIG_DIR,`bin`))),drmDir:z.string().default(resolveDir(join(APP_CONFIG_DIR,`drm`))),language:z.string().default(getDefaultLanguage()),theme:z.enum([THEME.dark,THEME.light,THEME.system]).default(THEME.system),preferHardsub:z.coerce.boolean().default(!1),retry:z.coerce.number().int().min(1).max(10).default(3),connections:z.coerce.number().int().min(1).max(64).default(4),maxActiveDownloads:z.coerce.number().int().min(1).default(1),proxy:z.string().optional(),proxyMeta:z.string().optional(),proxyMedia:z.string().optional(),movieFilenameTemplate:z.string().default(`{title} ({quality}, {codec})`),seriesFilenameTemplate:z.string().default(`{title} S{s}E{e} ({quality}, {codec})`),developerMode:z.coerce.boolean().default(!1),allowUnknownSources:z.coerce.boolean().default(!1),videoContainerFormat:z.enum([VIDEO_CONTAINER_FORMAT.mp4,VIDEO_CONTAINER_FORMAT.mkv]).default(VIDEO_CONTAINER_FORMAT.mp4),useAlternativeServer:z.coerce.boolean().default(!1),automaticUpdates:z.coerce.boolean().default(!0)}),defaultSettings=SettingsSchema.parse({});var SettingsStore=class{#e;constructor(e){this.#e=createConf({defaults:defaultSettings,cwd:dirname(e),configName:basename(e,`.json`)}),this.#t()}#t(){let e=this.#e.store,t=SettingsSchema.safeParse(e),n=`{title}.{audioType}.{quality}.{tag}.{format}.{codec}`,r=`{title}.S{s}E{e}.{episodeTitle}.{audioType}.{quality}.{tag}.{format}.{codec}`;if(e.movieFilenameTemplate===`{title}.{audioType}.{quality}.{tag}.{format}.{codec}`&&this.#e.set(`movieFilenameTemplate`,e.seriesFilenameTemplate),e.seriesFilenameTemplate===`{title}.S{s}E{e}.{episodeTitle}.{audioType}.{quality}.{tag}.{format}.{codec}`&&this.#e.set(`seriesFilenameTemplate`,e.seriesFilenameTemplate),!t.success){console.warn(`Settings validation failed! Resetting to defaults.`,t.error.flatten()),this.#e.set(defaultSettings);return}Object.hasOwn(e,`askForOptions`)&&this.#e.set(t.data)}getAll(){return this.#e.store}get(e){return this.#e.get(e)}set(e){let t={...this.#e.store,...e},n=SettingsSchema.safeParse(t);if(!n.success)throw console.error(`Invalid settings update:`,n.error.flatten()),Error(`Settings update failed validation.`);this.#e.set(n.data)}onDidChange(e){this.#e.onDidAnyChange(e)}};const exists=async e=>{try{return await fsp.access(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}},ls=async(e,{recursive:t=!1}={})=>await fsp.readdir(e,{withFileTypes:!0,recursive:t}),rm$1=async(e,{recursive:t=!0,force:n=!0}={})=>{await fsp.rm(e,{recursive:t,force:n})},mkdir=async(e,{recursive:t=!0}={})=>{await fsp.mkdir(e,{recursive:t})},assertDownloadDirWritable=async e=>{try{await fsp.access(e,constants.W_OK)}catch(e){let t=e.code;throw t===`EPERM`||t===`EACCES`||t===`ENOENT`?Object.assign(Error(`Download directory is not writable`),{code:`DOWNLOAD_DIR_NOT_WRITABLE`}):e}},readJson=async e=>{try{let t=await fsp.readFile(e,{encoding:`utf8`});return JSON.parse(t)}catch(t){throw Error(`Failed to read JSON file: ${e}`,{cause:t})}},writeJson=async(e,t)=>{let n=parse(e).dir;try{let r=await exists(n);n&&!r&&await fsp.mkdir(n),await fsp.writeFile(e,JSON.stringify(t,null,2),`utf8`)}catch(t){throw Error(`Failed to save data to file: ${e}`,{cause:t})}},tar={extract:(e,t)=>execFileSync(`tar`,[`-xf`,e,...t?[`-C`,t]:[]],{stdio:`inherit`})},INSTALL_EXTENSION_ERROR_CODES={CHANNEL_NOT_FOUND:`EXTENSION_INSTALL_CHANNEL_NOT_FOUND`,VERSION_NOT_FOUND:`EXTENSION_INSTALL_VERSION_NOT_FOUND`,RELEASE_NOT_FOUND:`EXTENSION_INSTALL_RELEASE_NOT_FOUND`,ASSET_NOT_FOUND:`EXTENSION_INSTALL_ASSET_NOT_FOUND`,UNSUPPORTED_SOURCE:`EXTENSION_INSTALL_UNSUPPORTED_SOURCE`,UNSUPPORTED_PACKAGE_TYPE:`EXTENSION_INSTALL_UNSUPPORTED_PACKAGE_TYPE`,IMPORT_INVALID:`EXTENSION_INSTALL_IMPORT_INVALID`,SOURCE_NOT_FOUND:`EXTENSION_INSTALL_SOURCE_NOT_FOUND`,UNKNOWN:`EXTENSION_INSTALL_UNKNOWN`},KNOWN_INSTALL_EXTENSION_ERROR_CODES=new Set(Object.values(INSTALL_EXTENSION_ERROR_CODES));var InstallExtensionError=class extends Error{code;details;hint;constructor(e){super(e.message),this.name=`InstallExtensionError`,this.code=e.code,this.details=e.details,this.hint=e.hint}toPayload(){return{code:this.code,message:this.message,details:this.details,hint:this.hint}}};const createInstallExtensionError=(e,t,n,r)=>new InstallExtensionError({code:e,message:t,details:n,hint:r}),getChannel=e=>{let t=(e?.startsWith(`v`)?e[1]:e?.[0])??``;return Number.isNaN(parseInt(t))?e?.split(`.`).at(0)||`latest`:e?.split(`-`).at(1)?.split(`.`).at(0)||`latest`},getReleaseTag=e=>(e.version||e.tag_name||``).replace(/^v/,``),toUniqueList=e=>[...new Set(e.filter(Boolean))],getAvailableReleaseDetails=e=>({availableVersions:toUniqueList((e||[]).map(e=>getReleaseTag(e))),availableChannels:toUniqueList((e||[]).map(e=>getChannel(getReleaseTag(e))))}),createRequestedReleaseNotFoundError=(e,t,n)=>{let r={source:n,preferredVersion:e.preferredVersion,preferredChannel:e.preferredChannel,appChannel:e.appChannel,...getAvailableReleaseDetails(t)};return e.preferredVersion?createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.VERSION_NOT_FOUND,`Release not found for requested version: ${e.preferredVersion}`,r):createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.CHANNEL_NOT_FOUND,`Release not found for requested channel: ${e.preferredChannel}`,r)},selectRelease=(e,t,n)=>{if(!e?.length)return null;if(t.preferredVersion)return e.find(e=>getReleaseTag(e)===t.preferredVersion)||null;if(!t.isExplicitSelection){let t=e.find(e=>getChannel(getReleaseTag(e))===`latest`);if(t)return t}return e.find(e=>getChannel(getReleaseTag(e))===t.preferredChannel)||(t.allowFallback&&t.preferredChannel!==`latest`?(logger.debug(`Release ${t.preferredChannel} not found for ${n}, falling back to latest stable`),e.find(e=>getChannel(getReleaseTag(e))===`latest`)||(logger.debug(`Latest stable release not found for ${n}, falling back to latest with any channel`),e.at(0)||null)):null)},parseReleasePreference=e=>{let[,t=``]=e.split(`@`),n=e.replace(t?`@${t}`:``,``),r=getChannel(package_default.version),i=!!t;return{name:n,preferredVersion:Number.isNaN(parseInt(t[0]??``))?``:t,preferredChannel:i?getChannel(t):r,appChannel:r,isExplicitSelection:i,allowFallback:!i&&r!==`latest`}},fetchExtensionAsset=async(e,t)=>{logger.info(`Fetching bundle...`),logger.debug(e);let n=await fetch(e);if(!n.ok)throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.ASSET_NOT_FOUND,`Failed to download extension bundle: ${n.status} ${n.statusText}`,{url:e,status:n.status,statusText:n.statusText});let r=n.headers.get(`content-disposition`)?.split(`filename=`)[1]?.split(`;`)[0]?.replaceAll(`"`,``),i=path.basename(e),a=t||r||i,o=await n.arrayBuffer(),s=path.join(EXTENSIONS_SOURCES_DIR,a);return await fsp.writeFile(s,Buffer.from(o)),await fsp.stat(s).then(e=>{logger.debug(`Bundle downloaded (${e.size/1e3} KB): ${s}`)}).catch(()=>{logger.error(`Bundle download failed: can't find output file`)}),s},downloadExtension=async(e,t)=>{if(e.startsWith(`npm:`)||e.startsWith(`@streamyx/`))throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.UNSUPPORTED_SOURCE,`NPM extension sources are no longer supported`,{source:e});let n=e,r=e,i=path.dirname(e),a=i!==`.`&&await fsp.stat(i).then(()=>!0,()=>!1),o=e.startsWith(`./`)||e.startsWith(`../`)||e.startsWith(`/`)||e.startsWith(`~`)||a,s=await fsp.stat(e).then(()=>!0,()=>!1),c=e.startsWith(`https://`),l=e.startsWith(`gl:`)||e.startsWith(`gitlab:`),u=e.startsWith(`github:`)||e.startsWith(`gh:`);if(o&&!s&&!c&&!u&&!l)throw logger.warn(`Installed extension not found: ${e}`),logger.warn(`Run 'azot list' and remove the extension from the list.`),createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.SOURCE_NOT_FOUND,`Installed extension source not found: ${e}`,{source:e});e=e.replace(/^gl:/,``).replace(/^gitlab:/,``).replace(/^gh:/,``).replace(/^github:/,``);let d=parseReleasePreference(e),{name:f}=d;if(c)n=await fetchExtensionAsset(e);else if(u){let[e,t]=f.split(`/`),r=`https://api.github.com/repos/${e}/${t}/releases`,i=await fetch(r).then(e=>e.json());if(i?.message&&!Array.isArray(i))throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.RELEASE_NOT_FOUND,`GitHub releases request failed: ${i.message}`,{source:f,provider:`github`,apiMessage:i.message});let a=Array.isArray(i)?i:[],o=selectRelease(a,d,f);if(!o)throw d.isExplicitSelection?createRequestedReleaseNotFoundError(d,a,f):createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.RELEASE_NOT_FOUND,`GitHub release not found`,{source:f,provider:`github`,...getAvailableReleaseDetails(a)});let s=o.assets?.find(e=>e.name.endsWith(`.tgz`)||e.name.endsWith(`.zip`));if(!s)throw logger.debug(o),createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.ASSET_NOT_FOUND,`GitHub release asset not found`,{source:f,provider:`github`,releaseTag:getReleaseTag(o)});let c=s.browser_download_url;n=await fetchExtensionAsset(c)}else if(!s){let e=t?.find(e=>e.name===f),r=e?.releases||[],i=selectRelease(r,d,f),a=i?.assets.find(e=>e.name.endsWith(`.tgz`));if(e&&!i&&d.isExplicitSelection)throw createRequestedReleaseNotFoundError(d,r,f);if(a)n=await fetchExtensionAsset(a.url,a.name);else{let[e,t=`streamyx-extensions`]=f.split(`/`).reverse(),r=`https://gitlab.com/api/v4/projects/${t}%2F${e}/releases`,i=await fetch(r).then(e=>e.json());if(i?.message&&!Array.isArray(i))throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.RELEASE_NOT_FOUND,`GitLab releases request failed: ${i.message}`,{source:f,provider:`gitlab`,apiMessage:i.message});let a=Array.isArray(i)?i:[],o=selectRelease(a,d,f);if(!o)throw d.isExplicitSelection?createRequestedReleaseNotFoundError(d,a,f):createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.RELEASE_NOT_FOUND,`GitLab release not found`,{source:f,provider:`gitlab`,...getAvailableReleaseDetails(a)});let s=o.assets?.links?.find(e=>e.name.endsWith(`.tgz`));if(!s)throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.ASSET_NOT_FOUND,`GitLab release asset not found`,{source:f,provider:`gitlab`,releaseTag:getReleaseTag(o)});n=await fetchExtensionAsset(s.direct_asset_url||s.url,s.name)}}let p=path.extname(n),m=p===`.zip`,h=p===`.tgz`,g=new Set([`.js`,`.mjs`,`.cjs`]).has(p),_=await fsp.stat(n).then(e=>e.isDirectory()).catch(()=>!1);if(g?r=path.dirname(n):_?r=n:(r=n.replace(`.tgz`,``).replace(`.zip`,``),await exists(r)||await mkdir(r)),m||h)tar.extract(n,r),await rm$1(n);else if(!(g||_))throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.UNSUPPORTED_PACKAGE_TYPE,`Unsupported package type`,{source:e,extensionSource:n,packageType:p||`unknown`});return r},getExports=e=>e&&`exports`in e?e.exports:e,getExtensionName=e=>e?.package?.name||`(unknown extension)`,getExtensionApiVersion=e=>{let t=getExports(e);return!t||typeof t!=`object`?null:typeof t.resolveEntries==`function`?`v2`:typeof t.getMediaInfo==`function`?`legacy-media`:typeof t.fetchContentMetadata==`function`?`legacy-content`:null},isSupportedExtensionExports=e=>getExtensionApiVersion(e)!==null,getCompatibleExtensionExports=e=>{let t=getExports(e);if(!t||typeof t!=`object`)throw Error(`Invalid extension exports`);return t},initializeExtension=async e=>{let t=getCompatibleExtensionExports(e),n=getExtensionName(e);try{if(typeof t.setup==`function`){await t.setup();return}typeof t.init==`function`&&await t.init()}catch(e){let r=typeof t.setup==`function`?`setup`:`init`,i=e instanceof Error?e:Error(String(e));throw logger.error(`Extension ${n} ${r} failed: ${i.message}`),i.stack&&logger.debug(i.stack),i}},loginWithExtension=async(e,t)=>{let n=getCompatibleExtensionExports(e),r=getExtensionName(e);try{if(typeof n.auth?.login==`function`){await n.auth.login(t);return}typeof n.login==`function`&&await n.login(t)}catch(e){let t=typeof n.auth?.login==`function`?`auth.login`:`login`,i=e instanceof Error?e:Error(String(e));throw logger.error(`Extension ${r} ${t} failed: ${i.message}`),i.stack&&logger.debug(i.stack),i}},logoutWithExtension=async e=>{let t=getCompatibleExtensionExports(e),n=getExtensionName(e);try{if(typeof t.auth?.logout==`function`){await t.auth.logout();return}typeof t.logout==`function`&&await t.logout()}catch(e){let r=typeof t.auth?.logout==`function`?`auth.logout`:`logout`,i=e instanceof Error?e:Error(String(e));throw logger.error(`Extension ${n} ${r} failed: ${i.message}`),i.stack&&logger.debug(i.stack),i}},runBeforeDownloadHook=async e=>{let t=getCompatibleExtensionExports(e);typeof t.hooks?.beforeDownload==`function`&&await t.hooks.beforeDownload()},runAfterDownloadHook=async(e,{skipMux:t=!1}={})=>{let n=getCompatibleExtensionExports(e),r=getExtensionName(e);if(!t&&typeof n.hooks?.afterMux==`function`)try{await n.hooks.afterMux()}catch(e){let t=e instanceof Error?e:Error(String(e));logger.error(`Extension ${r} hooks.afterMux failed: ${t.message}`),t.stack&&logger.debug(t.stack)}if(typeof n.onDownloadFinished==`function`)try{await n.onDownloadFinished()}catch(e){let t=e instanceof Error?e:Error(String(e));logger.error(`Extension ${r} onDownloadFinished failed: ${t.message}`),t.stack&&logger.debug(t.stack)}},loadExtension=async(e,{context:t,dirname:n,skipValidation:r})=>{let i=await load(e,{dirname:n,context:t}).finally(()=>{});if(!i)return null;let a=`default`in i.exports?i.exports.default:i.exports;if(!r&&!isSupportedExtensionExports(a))throw logger.error(`Incompatible extension interface`),createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.IMPORT_INVALID,`Incompatible extension interface`,{extensionPath:e});return i.exports=a,i},sanitizeExtensionName=e=>e.split(`/`).reverse()[0].replaceAll(`@`,``).replace(`extension-`,``).replace(`-extension`,``),readExtensionInfo=async e=>{let t=[join(e,`package.json`),join(e,`package`,`package.json`)],n;for(let e of t)if(await exists(e)){n=e;break}let r={},i=``,a=``,o=``;if(n){let e=dirname(n);r=__require(n)||{},i=join(e,r.main||`index.js`),a=r.name,o=e}else{let t=await ls(e),n=t.find(e=>e.name===`index.js`)||t.find(e=>e.name===`main.js`)||t.find(e=>e.name===`service.js`)||t.find(e=>e.name===`extension.js`)||t.find(e=>e.name.endsWith(`.js`));n&&(i=join(e,n.name),a=parse(i).name)}if(!i)throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.IMPORT_INVALID,`Extension main script not found: ${e}`,{dir:e});return o||=dirname(i),{name:sanitizeExtensionName(a),path:i,dir:o,package:r}},importExtension=async(e,t)=>{try{let n=await readExtensionInfo(e),r=n.package?.config?.title||n.name,i=createContext({name:n.name,title:r,dir:EXTENSIONS_DATA_DIR});await i.cookieStore.import(join(EXTENSIONS_DATA_DIR,n.name,`cookies.txt`));let a=await loadExtension(n.path,{context:i,dirname:n.dir});if(!a)throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.IMPORT_INVALID,`Failed to load extension script: ${n.path}`,{source:t,dir:e,path:n.path});return{source:t,exports:a.exports,context:i,...n}}catch(e){throw logger.error(`Error importing module: ${e.message}`),logger.debug(e instanceof Error?e.stack||e.message:String(e)),e}},PRIMARY_API_URL=VITE_API_URL;let apiBaseUrl=PRIMARY_API_URL;const normalizeBase=e=>e.replace(/\/+$/,``),getApiBaseUrl=()=>normalizeBase(apiBaseUrl),joinUrl=(e,t)=>{let n=normalizeBase(e);return t?`${n}/${t.startsWith(`/`)?t.slice(1):t}`:n},getApiUrl=e=>joinUrl(getApiBaseUrl(),e);let publicKey=null;async function verifyResponse(e){if(!publicKey){logger.debug(`Public key not found`);let e=await fetchPublicKey();if(e)logger.debug(`Public key found: ${e.slice(0,50)}...`);else return logger.error(`Public key not found in response`),!1;if(publicKey=await importPublicKey(e).catch(()=>null),!publicKey)return logger.error(`Public key import failed`),!1}let t=e.headers.get(`X-Signature-Timestamp`),n=e.headers.get(`X-Signature`);if(!t||!n)return logger.error(`Signature headers not found`),!1;if(Date.now()-parseInt(t)>3e5)return!1;let r=`${t}:${await e.clone().text()}`,i=base64ToArrayBuffer(n),a=new TextEncoder,o=crypto.subtle.verify({name:`ECDSA`,hash:`SHA-256`},publicKey,i,a.encode(r));return o||(publicKey=null),o}const getPublicKeyFromEnv=()=>atob(VITE_PUBLIC_KEY);async function fetchPublicKey(e=`/auth/.well-known/public-key`){try{let t=await fetch(`${getApiBaseUrl()}${e}`);return t.ok?await t.text():getPublicKeyFromEnv()}catch(e){return logger.error(e.message),getPublicKeyFromEnv()}}async function importPublicKey(e){let t=base64ToArrayBuffer(e.replace(`-----BEGIN PUBLIC KEY-----`,``).replace(`-----END PUBLIC KEY-----`,``).trim());return crypto.subtle.importKey(`spki`,t,{name:`ECDSA`,namedCurve:`P-256`},!0,[`verify`])}function base64ToArrayBuffer(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n.buffer}const createClient$1=e=>createAuthClient({baseURL:`${e}/auth`,plugins:[deviceAuthorizationClient(),tributeClient()],fetchOptions:{retry:0,onSuccess:e=>{let t=e.response.headers.get(`set-auth-token`);t&&(authState.accessToken=t,saveAuth(authState))},auth:{type:`Bearer`,token:()=>authState.accessToken||loadAuth().then(e=>e?.accessToken)},onResponse:async e=>await verifyResponse(e.response).catch(()=>!1)?(logger.debug(`Response verified: ${e.response.url}`),e):(logger.error(`Response verification failed: ${e.response.status} ${e.response.statusText} ${e.response.url}`),errors.emit(`error`,{code:`SIGNATURE_VERIFICATION_FAILED`}),new Response(JSON.stringify({error:`SIGNATURE_VERIFICATION_FAILED`,message:`Signature verification failed`}),{status:401,statusText:`Unauthorized`}))}});let authClientBaseUrl=``,authClient=createClient$1(getApiBaseUrl());const getAuthClient=()=>{let e=getApiBaseUrl();return(!authClient||authClientBaseUrl!==e)&&(authClientBaseUrl=e,authClient=createClient$1(e)),authClient},getJson=async(e,t,n={})=>{let{signal:r,timeoutMs:i=1e4}=n,a=new AbortController,o=()=>a.abort(r?.reason);r?.addEventListener(`abort`,o,{once:!0});let s=!1,c=setTimeout(()=>{s=!0,a.abort(Error(`Request timeout`))},i);try{let n=await fetch(e,{...t,signal:a.signal});if(!n.ok)throw Error(`Request failed: ${n.status}`);return n.json()}catch(e){throw s&&e instanceof DOMException&&e.name===`AbortError`?Error(`Request timed out after ${i}ms`,{cause:e}):e}finally{clearTimeout(c),r?.removeEventListener(`abort`,o)}},backendApi={getExtensions:(e,t)=>getJson(getApiUrl(`extensions`),{headers:e?{Authorization:`Bearer ${e}`}:void 0},{signal:t}),getSubscriptions:(e,t)=>getJson(getApiUrl(`subscriptions`),{headers:{Authorization:`Bearer ${e}`}},{signal:t})},STALE_TIME=300*1e3,GC_TIME=300*1e3,queryClient=new QueryClient({queryCache:new QueryCache,defaultOptions:{queries:{staleTime:STALE_TIME,gcTime:GC_TIME}}}),cache={get:e=>queryClient.getQueryData(e),set:(e,t)=>queryClient.setQueryData(e,t),reset:e=>{e?queryClient.removeQueries({queryKey:e}):queryClient.clear()},invalidate:e=>queryClient.invalidateQueries({queryKey:e}),refresh:async(e,t)=>(queryClient.invalidateQueries({queryKey:e}),useQuery({queryKey:e,queryFn:t}))},useQuery=async({queryKey:e,queryFn:t,staleTime:n,enabled:r=!0})=>{try{let i=queryClient.getQueryState(e),a=i?i?.isInvalidated||!i?.dataUpdatedAt&&!i?.errorUpdatedAt||Date.now()-i.dataUpdatedAt>(n??STALE_TIME):!0,o=()=>cache.reset(e),s=()=>cache.invalidate(e),c=()=>cache.refresh(e,t);return r?i&&!a?{data:i.data,reset:o,invalidate:s,refresh:c}:{data:await queryClient.fetchQuery({queryKey:e,queryFn:t,staleTime:n}),reset:o,invalidate:s,refresh:c}:{data:null,reset:o,invalidate:s,refresh:c}}catch(t){throw queryClient.removeQueries({queryKey:e}),t}},sessionStorage=new FileStorage(join(APP_DATA_DIR,`session.json`)),authState={accessToken:``,tokenType:``,scope:``,createdAt:``},saveAuth=async e=>{sessionStorage.setItem(`access_token`,e.access_token),sessionStorage.setItem(`token_type`,e.token_type||`Bearer`),sessionStorage.setItem(`scope`,e.scope),sessionStorage.setItem(`created_at`,new Date().toISOString()),authState.accessToken=e.access_token,authState.tokenType=e.token_type||`Bearer`,authState.scope=e.scope,authState.createdAt=new Date().toISOString()},loadAuth=async()=>{let e=sessionStorage.getItem(`access_token`),t=sessionStorage.getItem(`token_type`),n=sessionStorage.getItem(`scope`),r=sessionStorage.getItem(`created_at`);return!e||!t||!n||!r?null:{accessToken:e,tokenType:t,scope:n,createdAt:new Date(r)}},getAuth=async()=>{let e=await useQuery({queryKey:[`session`,getApiBaseUrl()],queryFn:()=>getAuthClient().getSession()});return{session:e.data?.data?.session,user:e.data?.data?.user,error:e.data?.error}},getSubscriptions=async()=>{let{user:e,error:t}=await getAuth(),n=await useQuery({queryKey:[`subscriptions`,getApiBaseUrl()],queryFn:async()=>getAuthClient().subscription.list(),enabled:!!e?.id});return{data:n.data?.data,error:n.data?.error??t}},basicBenefits=[`lowConnectionsCount`,`oneDevice`],plusBenefits=[`mediumConnectionsCount`,`twoDevices`,`proxy`,`decryption`,`customIcon`,`tags`,`parallelDownloads`,`userExtensions`,`portableBuild`],proBenefits=[`highConnectionsCount`,`threeDevices`,`customMuxFormat`,`customDecrypter`,`customDrmClient`,`premiumExtensions`],benefits={basic:basicBenefits,plus:plusBenefits,pro:[...basicBenefits,...plusBenefits,...proBenefits]},getUserSubscriptions=()=>useQuery({queryKey:[`lava-subscriptions`,getApiBaseUrl()],queryFn:async()=>{let e=authState.accessToken||(await loadAuth())?.accessToken;return e?backendApi.getSubscriptions(e):[]}}),getUserPlan=async()=>{let e=await getSubscriptions();return{plan:(await getUserSubscriptions()).data?.[0]?.productTitle||e.data?.find(e=>new Date(e.expiresAt)>new Date)?.tributeSubscriptionName,error:e.error}},getCustomerState=async()=>{let{user:e,session:t}=await getAuth(),{plan:n,error:r}=await getUserPlan();return{user:e,session:t,error:r,plan:n,grantedBenefits:n?benefits[n.toLowerCase()]??[]:[]}};var require_builtins=__commonJSMin(((e,t)=>{t.exports={instagram:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask,d.utils;let p=d.Input,m=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3;let h={Referer:`https://www.instagram.com/`},g={"X-IG-App-ID":`936619743392459`,"User-Agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36`},_=async e=>{let t=new URLSearchParams({doc_id:`8845758582119845`,variables:JSON.stringify({shortcode:e,child_comment_count:3,fetch_comment_count:40,parent_comment_count:24,has_threaded_comments:!0})});try{let e=await n(`https://www.instagram.com/graphql/query/?${t.toString()}`,{headers:g});return e.ok?(await e.json()).data?.xdt_shortcode_media??null:(a.error(`GraphQL request failed with status: ${e.status}`),null)}catch(e){return a.error(`Error during GraphQL fetch:`,e),null}},v=async e=>{try{let t=await n(`https://www.instagram.com/p/${e}/embed/captioned/`,{headers:{"User-Agent":g[`User-Agent`]}});if(!t.ok)return null;let r=(await t.text()).match(/window\.__additionalDataLoaded\('extra',\s*({.+?})\s*\);/);return r?.[1]?JSON.parse(r[1]).shortcode_media??null:null}catch(e){return a.error(`Error during embed page fetch:`,e),null}},y=e=>{let t=e.edge_media_to_caption?.edges?.[0]?.node?.text||`Instagram Post`,n=[],r=e=>{if(!e)return;let r=e.is_video&&e.video_url?e.video_url:e.display_url;r&&n.push({title:t,input:new p({source:new m(r,{requestInit:{headers:h}})})})},i=e.edge_sidecar_to_children?.edges;if(i?.length)for(let e of i)r(e.node);else r(e);return n};t.exports=f({canHandle:e=>{try{let t=new URL(e).hostname.toLowerCase();return t===`instagram.com`||t.endsWith(`.instagram.com`)}catch{return!1}},async resolveEntries(e){let t=new URLPattern({pathname:`/:type(p|reel|tv|reels|videos)/:shortcode{/*}?`,baseURL:new URL(e).origin}).exec(e)?.pathname.groups.shortcode;if(!t)return a.error(`Could not extract a valid Instagram shortcode from the URL`),a.debug(e),[];try{let e=await _(t);return e||=(a.warn(`GraphQL method failed. Falling back to embed page scraping.`),await v(t)),e?y(e):(a.error(`All extraction methods failed. The post may be private, deleted, or require a login.`),[])}catch(e){return a.error(`A critical error occurred while fetching Instagram metadata:`,e),[]}}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/41/91/13/419113d2-6246-c557-669e-0f0a5c62bcc5/Prod-0-0-1x_U007epad-0-1-0-sRGB-85-220.png/460x0w.webp`,title:`Instagram`,name:`Instagram`},pkg:{name:`instagram`,version:`0.0.1`,private:!0,keywords:[`social-networks`],homepage:`https://instagram.com/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`instagram.ts`,config:{icon:`https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/41/91/13/419113d2-6246-c557-669e-0f0a5c62bcc5/Prod-0-0-1x_U007epad-0-1-0-sRGB-85-220.png/460x0w.webp`,title:`Instagram`}}},ntv:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask,d.utils;let p=d.Input,m=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3;let h=(e,t,n)=>e.split(t)[1]?.split(n)[0],g=(e,t)=>{let n=h(e,`<${t}>`,`</${t}>`)?.trim();if(!n)return;let r=n.match(/<!\[CDATA\[(.*?)\]\]>/s);return r?.[1]?r[1].trim():n.split(`DATA[`)[1]?.split(`]`)[0]?.trim()||n},_=async(e,t)=>{let r=await n(e);return r.ok?await r.text():(a.error(`Failed to fetch ${t}. Status: ${r.status} ${r.statusText}`),null)},v=async(e,t,n)=>{let r=new URL(e).pathname,i=new Set;for(let n of t.split(`href="/serial`)){if(!n.startsWith(`/`))continue;let t=n.split(`"`)[0];!t||t.length<=1||t.includes(`/issues/`)||t.includes(`/about/`)||t.split(`/`).length<=2||i.add(e.replace(r,`/serial${t}`))}let a=[];for(let e of i)a.push(...await y(e,n));return a},y=async(e,t=new Set)=>{if(t.has(e))return[];t.add(e);let n=await _(e,`NTV page`);if(!n)return[];let r=h(n,`<meta property="ya:ovs:feed_url" content="`,`">`)??h(n,`<meta property="ya:ovs:feed_url" content="`,`" />`);if(!r)return new URL(e).pathname.includes(`/serial`)?v(e,n,t):(a.error(`Could not find video link in the page.`),[]);let i=r.split(`video/`)[1]?.split(`/`)[0];if(!i)return a.error(`Could not parse NTV video id.`),[];let o=await _(`https://www.ntv.ru/vi${i}/`,`NTV XML metadata`);if(!o)return[];let s=g(o,`dash`)||g(o,`hls`),c=g(o,`file`)||g(o,`video`),l=g(o,`hifile`)||g(o,`hd_video`)||s||c;if(!l)return a.error(`Could not resolve NTV media URL.`),[];let u=h(o,`<embed_tag>`,`</embed_tag>`)||`NTV`,d=h(n,`"name":"`,`"`),f=d?.includes(`серия`)?d.split(`-`)[0]:void 0,y=f?parseInt(f,10):void 0,b=h(o,`<subtitles>`,`</subtitles>`),x=b?`https://www.ntv.ru${b}`:void 0;x&&a.debug(`Subtitles: ${x}`);let S=new p({source:new m(l)});return x&&S.addSubtitleTrack(new m(x),{languageCode:`ru`,name:`Russian`}),[{type:y?`episode`:`video`,title:u,season:y?1:void 0,episode:y,input:S}]};t.exports=f({resolveEntries(e){return y(e,new Set)}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://static.ntv.ru/static/images/logo.png`,title:`НТВ`,name:`НТВ`},pkg:{name:`ntv`,version:`0.0.9`,private:!0,keywords:[`tv-channels`],homepage:`https://www.ntv.ru/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`ntv.ts`,config:{icon:`https://static.ntv.ru/static/images/logo.png`,title:`НТВ`}}},rbc:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask,d.utils;let p=d.Input,m=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3,t.exports=f({async resolveEntries(e){let t=await n(e);if(!t.ok)throw Error(`Failed to fetch RBC page: ${t.status} ${t.statusText}`);let r=(await t.text()).match(/(?:https?:)?\/\/[^\s"'<>]+\.m3u8(?:\?[^\s"'<>]*)?/i)?.[0];if(!r)throw Error(`Could not find RBC playlist URL.`);return[{type:`video`,input:new p({source:new m(r.startsWith(`//`)?`https:${r}`:r)})}]}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://static.rustore.ru/imgproxy/A-G-s2Q1x2iqq3qHCAo66-nllrtlqEr7XNU_JKXt4mQ/preset:web_app_icon_160/aHR0cHM6Ly9zdGF0aWMucnVzdG9yZS5ydS80MGViMWY3MS1kMDkzLTQ2NDMtOTkyNi0xZWM0ZjkwNzViMTQ=.webp`,title:`РБК`,name:`РБК`},pkg:{name:`rbc`,version:`0.0.2`,private:!0,keywords:[`news`],homepage:`https://rbc.ru/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`rbc.ts`,config:{icon:`https://static.rustore.ru/imgproxy/A-G-s2Q1x2iqq3qHCAo66-nllrtlqEr7XNU_JKXt4mQ/preset:web_app_icon_160/aHR0cHM6Ly9zdGF0aWMucnVzdG9yZS5ydS80MGViMWY3MS1kMDkzLTQ2NDMtOTkyNi0xZWM0ZjkwNzViMTQ=.webp`,title:`РБК`}}},rutube:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask,d.utils;let p=d.Input,m=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3;let h=[`/video/:id{/}?`,`/play/embed/:id{/}?`,`/shorts/:id{/}?`,`/yappy/:yappyId{/}?`,`/video/private/:id{/}?`],g=e=>{let t={yappyId:``,id:``,key:``};t.key=new URL(e).searchParams.get(`p`)??``;for(let n of h){let r=new URLPattern(n,`https://rutube.ru`).exec(e);r&&(Object.assign(t,r.pathname.groups),Object.assign(t,r.search.groups))}return t},_=async e=>{let t=(await n(`https://rutube.ru/pangolin/api/web/yappy/yappypage/?client=wdp&videoId=${e}&page=1&page_size=15`).then(e=>e.json()).catch(()=>null))?.results?.find(t=>String(t.id)===e)?.link;return t?[{type:`video`,title:`rutube_yappy_${e}`,input:new p({source:new m(t)})}]:(a.error(`ErrorEmptyDownload`),[])};t.exports=f({async resolveEntries(e){let t=g(e);if(t.yappyId)return _(t.yappyId);if(!t.id)return a.error(`Could not extract Rutube video id`),[];let r=new URL(`https://rutube.ru/api/play/options/${t.id}/?no_404=true&referer&pver=v2`);t.key&&r.searchParams.set(`p`,t.key);let i=await n(r).then(e=>e.json()).catch(e=>(e instanceof Error&&a.debug(e.message),null));return i?i.detail||!i.video_balancer?.m3u8?(a.error(`Play info is empty`),[]):i.live_streams?.hls?(a.error(`Live videos are not supported`),[]):[{type:`video`,title:i.title?.trim()||`rutube_${t.id}`,input:new p({source:new m(i.video_balancer.m3u8)})}]:(a.error(`Cannot fetch play info`),[])}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://static.rustore.ru/imgproxy/vcyBc0LG-3ybXcGs_-_OJXH9ULjoegHlLs-DacemG08/preset:app_card_icon/aHR0cHM6Ly9zdGF0aWMucnVzdG9yZS5ydS9hcGsvMjQ2MjA3L2NvbnRlbnQvSUNPTi9hNTZhZWJlMy01NjVhLTRiZjYtOTQyZC0xZjE0OTk0YjlhNmUucG5n.webp`,title:`RUTUBE`,name:`RUTUBE`},pkg:{name:`rutube`,version:`0.0.13`,private:!0,keywords:[`video-hostings`],homepage:`https://rutube.ru/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`rutube.ts`,config:{icon:`https://static.rustore.ru/imgproxy/vcyBc0LG-3ybXcGs_-_OJXH9ULjoegHlLs-DacemG08/preset:app_card_icon/aHR0cHM6Ly9zdGF0aWMucnVzdG9yZS5ydS9hcGsvMjQ2MjA3L2NvbnRlbnQvSUNPTi9hNTZhZWJlMy01NjVhLTRiZjYtOTQyZC0xZjE0OTk0YjlhNmUucG5n.webp`,title:`RUTUBE`}}},soundcloud:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask;let p=d.utils,m=d.Input,h=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3;let g={version:``,id:``},_=async()=>{try{let e=await n(`https://soundcloud.com/`).then(e=>e.text()).catch(e=>{let t=e instanceof Error&&e.cause!=null?String(e.cause):``;return e instanceof Error&&t.includes(`ECONNRESET`)&&a.warn(`Soundcloud may be blocked in your region. Use proxy or VPN to bypass restrictions.`),``}),t=e.match(/window\.__sc_version="([0-9]{10})"/)?.[1];if(t&&g.version===t)return g.id;let r=``;for(let t of e.matchAll(/<script.+src="(.+)">/g)){let e=t[1];if(!e?.startsWith(`https://a-v2.sndcdn.com/`))continue;let i=(await n(e).then(e=>e.text()).catch(()=>``)).match(/\("client_id=[A-Za-z0-9]{32}"\)/)?.[0].match(/[A-Za-z0-9]{32}/)?.[0];if(i){r=i;break}}return t&&(g.version=t,g.id=r),r}catch{return``}},v=e=>e.audioCodecs?.some(e=>e===`aac`||e===`mp3`)??!1;t.exports=f({async resolveEntries(e,t){let{author:r,song:i,accessKey:a,shortLink:o}=p.execUrlPatterns(e,[`:author/:song/s-:accessKey`,`:author/:song`,`:shortLink`],[`https://soundcloud.com`,`https://on.soundcloud.com`]).pathname,s=await _();if(!s)throw Error(`No client ID`);let c;if(new URL(e).hostname===`on.soundcloud.com`&&o&&(c=await n(`https://on.soundcloud.com/${o}/`,{redirect:`manual`}).then(e=>{let t=e.headers.get(`location`);if(e.status===302&&t?.startsWith(`https://soundcloud.com/`))return t.split(`?`,1)[0]}).catch(()=>void 0)),!c&&r&&i&&(c=`https://soundcloud.com/${r}/${i}${a?`/s-${a}`:``}`),!c)throw Error(`Could not fetch`);let l=await n(`https://api-v2.soundcloud.com/resolve?url=${c}&client_id=${s}`).then(e=>e.status===200?e.json():null).catch(()=>null);if(!l)throw Error(`Could not fetch`);let u=l.media?.transcodings;if(!u?.length)throw Error(`Empty download`);let d=u.find(e=>e.preset===`opus_0_0`),f=u.find(e=>e.preset===`mp3_0_0`);if(f&&(v(t)||!d)&&(d=f),!d)throw Error(`Empty download`);let g=`${d.url}${d.url.includes(`?`)?`&`:`?`}client_id=${s}&track_authorization=${l.track_authorization}`;if(!g.startsWith(`https://api-v2.soundcloud.com/media/soundcloud:tracks:`))throw Error(`Empty download`);let y=await n(g).then(async e=>(await e.json()).url??``).catch(()=>``);if(!y)throw Error(`Could not fetch`);return[{type:`audio`,title:l.title.trim(),author:l.user.username.trim(),input:new m({source:new h(y)})}]}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://play-lh.googleusercontent.com/JSSeB3XQtb9sHD5FWTqVM4AksY_Kg2q20eExenxGqPJZdn2uVQxi8eoHkP5MA-IR4w=w480-h960-rw`,title:`SoundCloud`,name:`SoundCloud`},pkg:{name:`soundcloud`,version:`0.0.6`,private:!0,keywords:[`music`],homepage:`https://soundcloud.com/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`soundcloud.ts`,config:{icon:`https://play-lh.googleusercontent.com/JSSeB3XQtb9sHD5FWTqVM4AksY_Kg2q20eExenxGqPJZdn2uVQxi8eoHkP5MA-IR4w=w480-h960-rw`,title:`SoundCloud`}}},tiktok:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask,d.utils;let p=d.Input,m=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3,t.exports=f({canHandle:e=>{try{let t=new URL(e).hostname.toLowerCase();return t===`tiktok.com`||t.endsWith(`.tiktok.com`)}catch{return!1}},async resolveEntries(e){let t=await n(e);if(!t.ok)return a.error(`Failed to fetch TikTok page. Status: ${t.status}`),[];let r=(await t.text()).split(`<script id="__UNIVERSAL_DATA_FOR_REHYDRATION__" type="application/json">`)[1]?.split(`<\/script>`)[0];if(!r)return a.error(`Could not find target script tag. TikTok might have updated their page structure.`),[];let i;try{i=JSON.parse(r)}catch(t){return a.error(`Failed to parse TikTok page JSON`,t,{url:e}),[]}let o=i.__DEFAULT_SCOPE__?.[`webapp.video-detail`],s=o?.itemInfo?.itemStruct;if(!s)return a.error(`Could not find video data in the JSON payload. The content might be unavailable or the structure has changed.`),o?.statusMsg&&a.error(`Error ${o.statusCode}: ${o.statusMsg}`),[];let l=s.video?.playAddr;return l?[{type:`video`,title:s.desc||`TikTok Video`,input:new p({source:new m(l,{requestInit:{headers:{Referer:`https://www.tiktok.com/`,Cookie:c.cookie}}})})}]:(a.error(`Could not find a video URL (playAddr) in the JSON payload.`),[])}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{name:`TikTok`,tag:`TT`,icon:`https://play-lh.googleusercontent.com/LdBITldj-tJxqLm-CsWSbnt4BMo2gk53cgM7mWIL-zn44m1ywLyQgxRsHKqp8r0qceXs=w480-h960-rw`,permissions:[],matches:[`https://*.tiktok.com/*`],manifestVersion:1,title:`TikTok`},pkg:{name:`tiktok`,version:`0.0.2`,private:!0,keywords:[`social-networks`],homepage:`https://www.tiktok.com/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`tiktok.ts`,config:{name:`TikTok`,tag:`TT`,icon:`https://play-lh.googleusercontent.com/LdBITldj-tJxqLm-CsWSbnt4BMo2gk53cgM7mWIL-zn44m1ywLyQgxRsHKqp8r0qceXs=w480-h960-rw`,permissions:[],matches:[`https://*.tiktok.com/*`],manifestVersion:1,title:`TikTok`}}},virtualroom:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask,d.utils;let p=d.Input,m=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3,t.exports=f({async resolveEntries(e){let t=new URL(e).searchParams.get(`recordId`);if(!t)return a.error(`Could not extract VirtualRoom recordId.`),[];let r=await n(`https://mv1.virtualroom.ru/vr/player/records/${t}/info`).then(e=>e.ok?e.json():null).catch(()=>null),i=await n(`https://mv1.virtualroom.ru/vr/player/records/${t}/events`).then(e=>e.ok?e.json():null).catch(()=>null),o=r?.data?.roomParameters?.name??`VirtualRoom`;return(i?.data?.translations??[]).filter(e=>!!e.url).map(e=>({title:[o,e.type,e.source,e.start].filter(Boolean).join(` `),input:new p({source:new m(e.url)})}))}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://sun9-50.userapi.com/impg/rY5N7sdA93TFdjkeYqPTnPIjvCJ0rALo_V7V8g/zLAaAv0PpvY.jpg?size=400x400&quality=95&sign=5ddcd6358131b204b8619cb8eeff943f&type=album`,title:`VirtualRoom`,name:`VirtualRoom`},pkg:{name:`virtualroom`,version:`0.0.7`,private:!0,keywords:[`videotelephony`],homepage:`https://virtualroom.ru/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`virtualroom.ts`,config:{icon:`https://sun9-50.userapi.com/impg/rY5N7sdA93TFdjkeYqPTnPIjvCJ0rALo_V7V8g/zLAaAv0PpvY.jpg?size=400x400&quality=95&sign=5ddcd6358131b204b8619cb8eeff943f&type=album`,title:`VirtualRoom`}}},vk:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask,d.utils;let p=d.Input,m=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3;let h=[`2160`,`1440`,`1080`,`720`,`480`,`360`,`240`,`144`],g=e=>{let t=e.match(/video-(\d+)_(\d+)/);return t?{ownerId:t[1],videoId:t[2]}:null},_=e=>{if(!e)return null;for(let t of e)if(Array.isArray(t)){for(let e of t)if(e&&typeof e==`object`&&`player`in e)return e}return null},v=(e,t)=>{let n=t.videoQuality?.replace(`p`,``).trim(),r=h.filter(t=>!!e[`url${t}`]);return r.length?n&&r.includes(n)?n:r[0]:null};t.exports=f({async resolveEntries(e,t){let r=g(e);if(!r)return a.error(`Could not extract VK video identifiers`),[];let i=await n(`https://vkvideo.ru/al_video.php?act=show`,{method:`POST`,headers:{"content-type":`application/x-www-form-urlencoded; charset=utf-8`,accept:`*/*`,"accept-language":`ru-RU,ru;q=0.9`,"sec-ch-ua":`"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"`,"sec-ch-ua-mobile":`?0`,"sec-ch-ua-platform":`"macOS"`,"sec-fetch-dest":`empty`,"sec-fetch-mode":`cors`,"sec-fetch-site":`same-origin`,"x-requested-with":`XMLHttpRequest`,Referer:e,"Referrer-Policy":`strict-origin-when-cross-origin`},body:new URLSearchParams({al:`1`,autoplay:`1`,claim:``,force_no_repeat:`true`,is_video_page:`true`,list:``,module:`direct`,t:``,video:`-${r.ownerId}_${r.videoId}`}).toString()});if(!i.ok)throw Error(`VK request failed: ${i.status} ${i.statusText}`);let o=_((await i.json()).payload),s=o?.player?.params,l=s&&s.length>0?s[0]:void 0;if(!o||!l)return a.error(`Could not find VK player payload`),[];if(o.mvData?.is_active_live===1||o.mvData?.is_active_live===`1`)return a.error(`Live videos are not supported`),[];let u=v(l,t),d=u?l[`url${u}`]:void 0,f=l.dash_sep||d;return f?[{type:`video`,title:[l.md_title?.trim(),l.md_author?.trim()].filter(Boolean).join(` `),input:new p({source:new m(f,{requestInit:{headers:{Referer:e,Cookie:c.cookie}}})})}]:(a.error(`Could not find video URL`),[])}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://static.rustore.ru/imgproxy/kSjDiz_MKxD_jzwgEqqaIjQZTE-NEP1Xt8HtJT7EU4s/preset:app_card_icon/aHR0cHM6Ly9zdGF0aWMucnVzdG9yZS5ydS9hcGsvMjAyNzgyMzI5NS9jb250ZW50L0lDT04vOTc0ODYwMTgtNTc4NS00MjRkLTljYWItNGZlNDk2ZDI3Yzc2LnBuZw==.webp`,title:`VK`,name:`VK`},pkg:{name:`vk`,version:`0.0.8`,private:!0,keywords:[`video-hostings`],homepage:`https://vkvideo.ru/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`vk.ts`,config:{icon:`https://static.rustore.ru/imgproxy/kSjDiz_MKxD_jzwgEqqaIjQZTE-NEP1Xt8HtJT7EU4s/preset:app_card_icon/aHR0cHM6Ly9zdGF0aWMucnVzdG9yZS5ydS9hcGsvMjAyNzgyMzI5NS9jb250ZW50L0lDT04vOTc0ODYwMTgtNTc4NS00MjRkLTljYWItNGZlNDk2ZDI3Yzc2LnBuZw==.webp`,title:`VK`}}},weibo:{create:e=>{let t={exports:{}};return((e,n,r,i,a,o,s,c,l,u)=>{let d=e??Streamyx??{};d&&(d.defineExtension||=e=>e);let f=d.defineExtension;d.version,d.sleep,d.input,d.prompt,d.notify,d.confirm,d.ask;let p=d.utils,m=d.Input,h=d.UrlSource;d.ALL_FORMATS,d.DASH,d.DASH_FORMATS,d.HLS,d.HLS_FORMATS,d.MP4,d.MP3,t.exports=f({async resolveEntries(e){let t=p.execUrlPatterns(e,[`/:userId(\\d+)/:bid`,`/detail/:mid`],[`https://weibo.com/`,`https://m.weibo.cn`]),r=t.pathname.bid||t.pathname.mid;if(!r)return[];let i=await n(`https://m.weibo.cn/statuses/show?id=${r}`,{credentials:`include`,referrer:`https://m.weibo.cn/status/${t.pathname.bid??r}?jumpfrom=weibocom`,method:`GET`,mode:`cors`}).then(e=>e.json()).catch(()=>null);if(!i?.data)return a.error(`Could not resolve Weibo post metadata`),[];let o=[],s=i.data.pics,c=i.data.page_info;if(s?.length)for(let e of s.filter(e=>e.type===`video`||e.type===`livephoto`))e.videoSrc&&o.push({type:`video`,title:e.pid,input:new m({source:new h(e.videoSrc)})});else if(c?.type===`video`){let e=Object.values(c.urls??{})[0],t=c.media_info?.stream_url_hd||c.media_info?.stream_url,n=e||t;n&&o.push({type:`video`,title:c.title||c.page_title,input:new m({source:new h(n)})})}return o}})})(e.Azot,e.fetch,e.localStorage,e.cookieStore,e.console,e.prompt,e.alert,e.document,e.navigator,e.window),t.exports.default||t.exports},manifest:{icon:`https://play-lh.googleusercontent.com/TTKq2trnzgtoyM0jc1pyrrmXtA_Yd3A2hxubZtDahdZStYf4GPANZa-wjDb1ELHrGqh_=w480-h960-rw`,title:`Weibo`,name:`Weibo`},pkg:{name:`weibo`,version:`0.0.6`,private:!0,keywords:[`social-networks`],homepage:`https://weibo.com/`,author:{name:`Azot`,url:`https://azot.mov/`},contributors:[{name:`Vitaly Gashkov`,email:`vitalygashkov@vk.com`,url:`https://github.com/vitalygashkov`}],type:`module`,main:`weibo.ts`,config:{icon:`https://play-lh.googleusercontent.com/TTKq2trnzgtoyM0jc1pyrrmXtA_Yd3A2hxubZtDahdZStYf4GPANZa-wjDb1ELHrGqh_=w480-h960-rw`,title:`Weibo`}}}}})),import_builtins=__toESM(require_builtins(),1);const extractExtensionInfo=e=>({name:e.name,source:e.source,dir:e.dir,path:e.path,package:e.package,installedAt:e.installedAt||new Date().toISOString()});var ExtensionManager=class ExtensionManager extends EventEmitter$1{extensions=new Map;extensionDirs;indexPath;infoList=[];constructor({dirs:e,indexPath:t}={}){super(),this.extensionDirs=e??[process.cwd()],this.indexPath=t??path$1.join(process.cwd(),`index.json`)}async init(){return await this.ensureValidIndexPath(),await this.loadInfo(),this}async loadInfo(){return this.infoList=await readJson(this.indexPath).catch(()=>[]),this.infoList}async saveInfo(){return await this.ensureValidIndexPath(),await writeJson(this.indexPath,this.infoList)}async ensureValidIndexPath(){(await fs$1.stat(this.indexPath).catch(()=>null))?.isDirectory()&&(logger.warn(`Invalid extensions index path repaired: ${this.indexPath}`),await rm$1(this.indexPath),await fs$1.writeFile(this.indexPath,`[]`,`utf8`))}static async getAvailableExtensions(){try{return(await backendApi.getExtensions(authState.accessToken,AbortSignal.timeout(8e3))).data}catch(e){return logger.warn(`Failed to fetch extensions catalog: ${e instanceof Error?e.message:String(e)}`),[]}}async discover(){for(let e of this.extensionDirs){logger.debug(`Loading extensions from ${e}`);let t=await fs$1.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let n of t)if(n.isDirectory()){let t=path$1.join(e,n.name);if(await fs$1.readdir(t).then(e=>e.filter(e=>!e.startsWith(`.`))).then(e=>!e.length))continue;await this.loadExtension(t)}}for(let e of this.infoList)this.extensionDirs.some(t=>e.dir?.includes(t))||!e.dir||(logger.debug(`Loading dev extension from ${e.dir}`),await this.loadExtension(e.dir))}async#e(scriptPath,context){const code=await fs$1.readFile(scriptPath,`utf8`),{Azot,fetch,console,localStorage,cookieStore,window,document,navigator}=context,module={exports:{}};eval(`(() => {${code}})();`);const exports=module.exports;return{exports,context}}async prepareContext(e,t){let n=createContext({name:e,dir:EXTENSIONS_DATA_DIR,title:t});return await n.cookieStore.import(path$1.join(EXTENSIONS_DATA_DIR,e,`cookies.txt`)),n}async loadScript(e,t){let n=t.name,r=t.config?.title||n,i=await this.prepareContext(n,r),a=await load(e,{dirname:t.dir,context:i});if(a)return a.exports=`default`in a.exports?a.exports.default:a.exports,a}async loadCoreExtensions(){let e=Object.keys(import_builtins.default);logger.debug(`Loading ${e.length} built-in extensions: ${e.join(`, `)}`);for(let e of Object.values(import_builtins.default)){let t=e.pkg.name,n=e.manifest?.name;try{let r=await this.prepareContext(t,n),i={exports:e.create(r),package:e.pkg,context:r};this.validate(i),this.extensions.set(t,i),this.emit(`loaded`,t)}catch(e){logger.debug(e instanceof Error?e.message:String(e)),this.emit(`error`,e,t)}}}async loadExtension(e){try{logger.debug(`Loading extension from ${e}`);let t=await readExtensionInfo(e).catch(e=>logger.error(e));if(!t)return;t.package.dir=t.dir;let n=await this.loadScript(t.path,t.package);if(!n)return;let r={package:t.package,exports:n.exports,context:n.context};this.validate(r),this.extensions.set(t.package.name,r),this.emit(`loaded`,t.package.name)}catch(t){this.emit(`error`,t,e)}}validate(e){if(typeof e!=`object`||!e||!isSupportedExtensionExports(e.exports))throw Error(`Invalid extension interface: ${e.package.name}`)}getExtension(e){return this.extensions.get(e)??Array.from(this.extensions.values()).find(t=>t.package.name===e)??null}async getExtensionForUrl(e){for(let t of this.extensions.values()){let n=null,r=t.exports.canHandle,i=new URL(e).hostname.includes(t.package.name);if(n=typeof r==`function`?await r(e):typeof r==`string`?new URLPattern(r).test(e):i,n)return t}return null}async install(e){let t=await ExtensionManager.getAvailableExtensions(),n=[];for(let r of e){let e=await stat(r).then(e=>e.isDirectory()).catch(()=>!1)?r:await downloadExtension(r,t);if(!e)throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.SOURCE_NOT_FOUND,`Extension source not found: ${r}`,{source:r});let i=await importExtension(e,r);if(!i)throw createInstallExtensionError(INSTALL_EXTENSION_ERROR_CODES.IMPORT_INVALID,`Extension import failed: ${r}`,{source:r,dir:e});n.push(i);let a=this.infoList.find(({name:e})=>e===i.name);if(a){let e=a.source;e!==i.source&&await rm$1(e);let t=a.dir?this.#t(a.dir):null,n=i.dir?this.#t(i.dir):null;t&&t!==n&&await rm$1(t),a.installedAt=new Date().toISOString(),Object.assign(a,extractExtensionInfo(i)),logger.info(`Extension ${i.name} updated`)}else this.infoList.push(extractExtensionInfo(i)),logger.info(`Extension ${i.name} added`)}if(await this.saveInfo(),n.length)for(let e of n)this.extensions.set(e.name,e)}#t(e){let t=path$1.parse(e);return t.dir===EXTENSIONS_SOURCES_DIR?e:t.dir}async uninstall(e){for(let t of e){let e=this.infoList.find(e=>e.name===t);if(!e){logger.error(`Extension ${t} not found`);return}let n=e.dir??``,r=n?.includes(EXTENSIONS_SOURCES_DIR);await exists(n)?(r&&await rm$1(this.#t(n)),this.infoList=this.infoList.filter(e=>e.name!==t),logger.info(`Extension ${t} removed`),this.extensions.delete(t)):logger.warn(`Extension ${t} directory not found: ${n}`)}await this.saveInfo()}async importCookies(e,t){let n=this.getExtension(e);if(!n)throw Error(`Extension ${e} not found`);return await n.context.cookieStore.import(t,{removeSource:!1})}async login(e,t={}){let n=this.getExtension(e);n&&await loginWithExtension(n,t)}async logout(e){for(let t of e){let e=this.getExtension(t);if(!e)return;logger.info(`Logging out...`),await logoutWithExtension(e),logger.debug(`Clearing localStorage...`),e.context.localStorage.clear(),logger.debug(`Clearing cookies store...`);let n=await e.context.cookieStore.getAll();for(let t of n)await e.context.cookieStore.delete(t);logger.info(`Logged out from extension ${t}`)}}list(){return Array.from(this.extensions.values()).map(e=>({name:e.package.name,version:e.package.version,description:e.package.description,package:e.package}))}};const mergeExtensionHeadersIntoDrm=(e,t)=>!e||`payload`in e||`keys`in e?e:{...e,headers:{...e.headers,...t?.context.http.headers}},defaultLegacyRequestFilter=async(e,t)=>{if(!t)return e;let n=await e.clone().arrayBuffer(),r=Buffer.from(n).toString(`base64`),i=t.replace(`{{challenge}}`,r);return new Request(e.url,{method:e.method,headers:e.headers,body:i})},defaultLegacyResponseFilter=async e=>{let t=await e.clone().arrayBuffer();if(t[0]===123){let n=null;try{n=JSON.parse(Buffer.from(t).toString(`utf8`))}catch(e){logger.error(`Can't parse license response as JSON`),logger.debug(e.message),logger.debug(`Base64 response data: ${Buffer.from(t).toString(`base64`)}`)}if(!n)return e;try{let n=`CAIS`,r=n+Buffer.from(t).toString(`utf8`).split(`"${n}`)[1].split(`"`)[0],i=Buffer.from(r,`base64`);return new Response(i,{status:e.status,headers:e.headers})}catch{logger.error(`Can't recognize license response`),logger.error(`Response: ${JSON.stringify(n)}`)}}return e},normalizeLegacyMediaInfoList=e=>typeof e==`string`?[{sources:[{url:e}]}]:Array.isArray(e)&&typeof e[0]==`string`?e.map(e=>({sources:[{url:e}]})):Array.isArray(e)?e:e&&typeof e==`object`?[e]:[],normalizeLegacyStreams=e=>e.sources??e.streams??[],resolveLegacyMediaMetadata=(e,t)=>({id:e.id,title:e.title,tag:t.package.config?.tag,seasonNumber:e.seasonNumber??e.season,episodeNumber:e.episodeNumber??e.episode,episodeTitle:e.episodeTitle}),resolveLegacyMediaResources=async(e,t,n)=>{let r=getCompatibleExtensionExports(n);if(typeof r.getMediaInfo==`function`){let i=normalizeLegacyMediaInfoList(await r.getMediaInfo(e,t));if(!i.length)return logger.info(`No metadata found`),[];let a=[];for(let e of i){if(typeof r.refreshMediaInfo==`function`){let n=await r.refreshMediaInfo(e,t);n&&typeof n==`object`&&Object.assign(e,n)}let i=normalizeLegacyStreams(e);for(let t of i){let r=typeof t==`string`?{url:t}:t;a.push({source:{url:r.url,headers:r.headers,drm:r.drm&&!Array.isArray(r.drm)&&typeof r.drm==`object`?{server:r.drm.server}:void 0},metadata:resolveLegacyMediaMetadata(e,n),drm:r.drm&&!Array.isArray(r.drm)&&typeof r.drm==`object`?{server:r.drm.server}:void 0})}}return logger.info(`Fetching metadata finished`),a}let i=await r.fetchContentMetadata?.(e,t);if(!i?.length)return logger.info(`No metadata found`),[];let a=[];for(let e of i){let r={...e},i;if(!r.source&&n.exports.fetchContentSource&&r.id){let e=await n.exports.fetchContentSource(r.id,t);e&&(r.source=e)}if(!r.source?.url){logger.error(`Can't find content source`);continue}r.source.drm&&`payload`in r.source.drm&&n.exports.fetchContentDrm&&(i=r.source.drm.payload,r.source.drm=void 0);let{source:o,...s}=r,c=mergeExtensionHeadersIntoDrm(r.source.drm,n);o.drm=c,o.headers={...o.headers,...n.context.http.headers},n.exports.tag&&(s.tag=n.exports.tag),a.push({source:o,metadata:s,drm:c,drmPayload:i})}return logger.info(`Fetching metadata finished`),a},resolveLegacyMediaResourceDrm=async(e,t,n)=>{if(e.drmPayload&&n?.exports.fetchContentDrm){let r=mergeExtensionHeadersIntoDrm(await n.exports.fetchContentDrm(e.drmPayload,t),n);return e.source.drm=r,e.drm=r,r}let r=mergeExtensionHeadersIntoDrm(e.source.drm??e.drm,n);return e.source.drm=r,e.drm=r,r},createLegacyDrmLicenseResolver=e=>{let t=e.template?defaultLegacyRequestFilter:void 0,n=defaultLegacyResponseFilter,r=new HttpClient;return async({url:i,data:a})=>{let o=new Request(i,{body:Buffer.from(a),method:`POST`,headers:e.headers}),s=await t?.(o,e.template)||o,c=await r.fetch(s.url,{body:await s.arrayBuffer(),headers:s.headers,method:s.method}),l=await n?.(c)||c;return new Uint8Array(await l.arrayBuffer())}},resolvedInputs=new Map,normalizeHeaders=e=>{if(!e)return;let t=Object.fromEntries(new Headers(e).entries());return Object.keys(t).length?t:void 0},serializeMediaEntry=e=>{let{input:t,...n}=e;return n},createEntryCacheKey=(e,t,n)=>createHash(`sha1`).update(JSON.stringify({extension:e.package.name,url:t,id:n.id??null,title:n.title??null,season:n.season??null,episode:n.episode??null})).digest(`hex`),extractInputSource=(e,t)=>{let n=e.source??e._rootSource,r=n?.rootPath;if(typeof r!=`string`||!r)return t??null;let i=n?._url instanceof Request?n._url.headers:void 0,a=n?._options?.requestInit?.headers,o={...normalizeHeaders(i),...normalizeHeaders(a)};return{...t??{},url:r,headers:Object.keys(o).length?o:t?.headers}},createEntryMetadata=(e,t)=>{let n=e.id??e.details?.id;return{id:n==null?void 0:String(n),title:e.title,tag:t.package.config?.tag,seasonNumber:e.season,episodeNumber:e.episode,episodeTitle:e.episodeTitle}},normalizeResolvedResources=e=>e.length?e.length===1?e[0]:e:null,createDirectMediaMetadata=e=>{let t;try{t=new URL(e)}catch{throw Error(`Invalid direct media URL: ${e}`)}let n=t.pathname.replace(/\/+$/,``).split(`/`).filter(Boolean).pop(),r=n?.split(`.`).slice(0,-1).join(`.`)||n||t.hostname||`download`;return{source:{url:e},metadata:{title:r}}},configureExtensionProxy=(e,t)=>{let n=t.proxy||t.proxyMeta;n&&(e.context.http.configure({proxyUrl:n}),httpClient.configure({proxyUrl:n}))},analyzeMediaResource=async(e,t,n)=>{if(!n){logger.warn(`No suitable extension available. Downloading as a direct link...`);let{source:n,metadata:r}=createDirectMediaMetadata(e);return n.audioLanguage=t.languages?.[0],n.http2=t.http2,{source:n,metadata:r}}if(configureExtensionProxy(n,t),await initializeExtension(n),getExtensionApiVersion(n)===`v2`){let r=getCompatibleExtensionExports(n);logger.info(`Resolving media entries...`);let i=await r.resolveEntries(e,t);if(!i?.length)return logger.info(`No metadata found`),null;let a=i.map(t=>{let r=t,i=serializeMediaEntry(t),a=t.input?createEntryCacheKey(n,e,r):void 0;a&&t.input&&resolvedInputs.set(a,t.input);let o=t.input?extractInputSource(t.input):null;return{source:o??{url:e},metadata:createEntryMetadata(i,n),extension:{api:`v2`,entry:i,cacheKey:a,hasResolvedSource:!!o}}});return logger.info(`Resolving media entries finished`),normalizeResolvedResources(a)}return logger.info(`Fetching content metadata...`),normalizeResolvedResources(await resolveLegacyMediaResources(e,t,n))},materializeMediaResource=async({resource:e,url:t,options:n,extension:r})=>{if(!r||!e.extension||e.extension.api!==`v2`||getExtensionApiVersion(r)!==`v2`)return{source:e.source};configureExtensionProxy(r,n),await initializeExtension(r);let i=getCompatibleExtensionExports(r),a=e.extension.entry,o=e.extension.cacheKey?resolvedInputs.get(e.extension.cacheKey):void 0;if(!o&&typeof i.resolveMedia==`function`&&(o=await i.resolveMedia(t,n,a)??void 0,o&&e.extension.cacheKey&&resolvedInputs.set(e.extension.cacheKey,o)),!o){if(e.extension.hasResolvedSource)return{source:e.source,entry:a};throw Error(`Extension ${r.package.name} could not materialize media entry. Provide resolveMedia() or an inline input rooted in UrlSource.`)}return{source:extractInputSource(o,e.source)??e.source,input:o,entry:a}},resolveMediaResourceDrm=async(e,t,n)=>resolveLegacyMediaResourceDrm(e,t,n);var DownloadRunState=class{#e=[];#t=new Set;#n=new Map;#r=new Map;#i=new Set;get activeRunCount(){return this.#r.size}get queuedCount(){return this.#e.length}getActiveRun(e){return this.#r.get(e)}setActiveRun(e,t){this.#r.set(e,t)}deleteActiveRun(e){this.#r.delete(e)}getActiveJob(e){return this.#n.get(e)}setActiveJob(e){this.#n.set(e.id,e)}deleteActiveJob(e){this.#n.delete(e)}queueResumeAfterStop(e){this.#i.add(e)}consumeResumeAfterStop(e){let t=this.#i.has(e);return t&&this.#i.delete(e),t}enqueue(e){this.#t.has(e.id)||this.#r.has(e.id)||(this.#e.push(e.id),this.#t.add(e.id))}dequeue(e){if(!this.#t.has(e))return!1;let t=this.#e.indexOf(e);return t!==-1&&this.#e.splice(t,1),this.#t.delete(e),!0}dequeueNext(e){for(;this.#e.length>0;){let t=this.#e.shift();if(!t)return;this.#t.delete(t);let n=e(t);if(n&&n.status===`queued`)return n}}};const ERROR_DETAIL_KEYS=[`code`,`errno`,`syscall`,`address`,`port`,`host`,`hostname`,`path`,`type`],isResponseLike=e=>typeof e==`object`&&!!e&&`status`in e&&`statusText`in e&&`url`in e,getOwnErrorDetails=e=>{let t=[],n=e;for(let e of ERROR_DETAIL_KEYS){let r=n[e];r==null||r===``||t.push(`${e}=${String(r)}`)}return t},formatErrorDetails=(e,t=0)=>{if(t>3)return`[max-depth]`;if(e instanceof Error){let n=getOwnErrorDetails(e),r=`cause`in e?e.cause:void 0,i=[`${e.name}: ${e.message}`];return n.length&&i.push(`details=${n.join(`, `)}`),r!==void 0&&r!==e&&i.push(`cause=${formatErrorDetails(r,t+1)}`),i.join(` | `)}return isResponseLike(e)?`Response ${e.status} ${e.statusText} url=${e.url}`:inspect(e,{depth:2,breakLength:1/0})},logErrorDetails=(e,t)=>{logger.debug(`${e}: ${formatErrorDetails(t)}`),t instanceof Error&&t.stack&&logger.debug(t.stack)},getTrackDescription=(e,t)=>{let n=`${t} ∙ `;return e.type===`subtitle`?n+=`SUBTITLES`:n+=e.type?.toUpperCase(),e.type===`video`&&(e.width&&(n+=` ∙ ${e.width}x${e.height}`),e.bitrate&&(n+=` ∙ ${Math.round(e.bitrate/1e3)} KiB/s`)),e.type===`audio`&&(e.languageCode&&(n+=` ∙ ${e.languageCode.toUpperCase()}`),e.name&&(n+=` (${e.name})`),e.sampleRate&&(n+=` ∙ ${e.sampleRate} kHz`),e.bitrate&&(n+=` ∙ ${Math.round(e.bitrate/1e3)} KiB/s`)),e.type===`subtitle`&&(e.languageCode&&(n+=` ∙ ${e.languageCode.toUpperCase()}`),e.name&&(n+=` (${e.name})`),e.forced&&(n+=` FORCED`)),n},getTrackId=e=>e.id,findDuplicateTrackIds=e=>{let t=new Map;for(let n of e){let e=getTrackId(n),r=t.get(e);if(r){r.push(n);continue}t.set(e,[n])}return Array.from(t.entries()).filter(([,e])=>e.length>1)},assertUniqueTrackIds=e=>{let t=findDuplicateTrackIds(e);if(!t.length)return;let n=t.map(([e,t])=>`${e} (${t.length} copies)`).join(`, `);throw Object.assign(Error(`Duplicate tracks selected: ${n}`),{code:`DUPLICATE_TRACKS_SELECTED`})},getErrorCode=e=>{if(!e||typeof e!=`object`||!(`code`in e))return;let t=e.code;return typeof t==`string`?t:void 0};var DownloaderError=class extends Error{code;constructor(e,t=e,n){super(t,n),this.name=`DownloaderError`,this.code=e}};const normalizeDownloaderError=(e,t=`DOWNLOAD_FAILED`)=>{if(e instanceof DownloaderError||e instanceof Error&&e.name===`AbortError`)return e;if(e instanceof Error){let n=new DownloaderError(getErrorCode(e)??t,e.message,{cause:e});return e.stack&&(n.stack=e.stack),n}return new DownloaderError(t,typeof e==`string`?e:`Unknown error`)},sanitizeFilename=e=>{let t=e.replace(/[<>:"/\\|?*\x00-\x1F]/g,``);t=t.replace(/^[\s.]+|[\s.]+$/g,``),t=t.replace(/\s+/g,` `);let n=t.lastIndexOf(`.`),r=n>0,i=r?t.slice(n):``,a=r?t.slice(0,n):t;if(i.length>=255)t=t.slice(0,255);else{let e=255-i.length;a.length>e&&(a=a.slice(0,e)),t=a+i}let o=/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i;if(i.length<255){let e=t.lastIndexOf(`.`),n=e>0;if(i=n?t.slice(e):``,a=n?t.slice(0,e):t,o.test(a)){let e=255-i.length;t=`_${a}`.slice(0,e)+i}}return t.length===0?`untitled`:t},fillTemplate=(e,t)=>Object.keys(t).reduce((e,n)=>{let r=`{${n}}`,i=t[n];if(!i){let t=RegExp(`(?:\\.|^)\\{${n}\\}(?=\\.|$)|\\{${n}\\}(\\.|$)|\\{${n}\\}`,`g`);return e.replace(t,(e,t)=>``).replaceAll(`....`,`...`)}return e.replaceAll(r,i)},e).replaceAll(` `,` `),resolveContainerFormat=e=>typeof e==`string`&&e.trim()||`mp4`,createDownloadMetadata=e=>{let t=`seasonNumber`in e?`${e.seasonNumber}`:``,n=`episodeNumber`in e?`${e.episodeNumber}`:``,r=t?`S${t}`:``,i=n?`E${n}`:``,a=t&&n?`${r}:${i}`:``,o=`episodeTitle`in e&&e.episodeTitle?.trim()?`"${e.episodeTitle}"`:``;return{title:`${e.title??``} ${a} ${o}`.trim()}},resolveDownloadOutputPlan=({settings:e,options:t,source:n,meta:r,allTracks:i})=>{let a=r.title,o=r.tag,s=`seasonNumber`in r?r.seasonNumber:void 0,c=`episodeNumber`in r?r.episodeNumber:void 0,l=`episodeTitle`in r?r.episodeTitle:``,u=i.find(e=>e.type===`video`)?.codec??``,d=e.get(`downloadDir`),f=t.movieTemplate||e.get(`movieFilenameTemplate`),p=t.episodeTemplate||e.get(`seriesFilenameTemplate`),m=`${sanitizeFilename(fillTemplate(c?p:f,{title:a??``,s:s?s.toString().padStart(2,`0`):``,e:c?c.toString().padStart(2,`0`):``,quality:t.videoQuality??``,codec:u,tag:o??``,episodeTitle:l??``,audioType:n.audioType?n.audioType.toUpperCase():``,format:`WEB-DL`}))}.${resolveContainerFormat(t.format||e.get(`videoContainerFormat`))}`,h=t.outputPath||join(d,m),g=parse(h);return{downloadDir:d,outputPath:h,resolvedFilenameWithoutExtension:g.name,filenameWithExtension:g.base,outputDirectory:g.dir||d,downloadMetadata:createDownloadMetadata(r)}},openFileHandle=async(e,t,n)=>n??open(e,t),createMediabunnyFileSource=e=>{let t=null;return new StreamSource({getSize:async()=>(t=await openFileHandle(e,`r`,t),(await t.stat()).size),read:async(n,r)=>{t=await openFileHandle(e,`r`,t);let i=new Uint8Array(r-n);return await t.read(i,0,r-n,n),i},dispose:async()=>{let e=t;t=null,await e?.close()},prefetchProfile:`fileSystem`})},createMediabunnyFileTarget=e=>{let t=null;return new StreamTarget(new WritableStream({start:async()=>{t=await openFileHandle(e,`w`,t)},write:async n=>{if(!t)throw Error(`MediaBunny output handle was not initialized for ${e}`);await t.write(n.data,0,n.data.byteLength,n.position)},close:async()=>{let e=t;t=null,await e?.close()},abort:async()=>{let e=t;t=null,await e?.close()}}),{chunked:!0})},inspectMedia=async(e,t)=>{let n=new Input$1({formats:ALL_FORMATS$1,source:e.startsWith(`https`)?new UrlSource$1(e,{requestInit:{headers:t}}):createMediabunnyFileSource(e)});return{tracks:await n.getTracks(),primaryVideoTrack:await n.getPrimaryVideoTrack(),primaryAudioTrack:await n.getPrimaryAudioTrack()}},ALL_DASHA_FORMATS=[...DASH_FORMATS,...HLS_FORMATS],SENSITIVE_HEADER_KEYS=new Set([`authorization`,`cookie`,`set-cookie`,`api-key`]),getSafeHeadersForLogging=e=>Object.fromEntries(Object.keys(e).map(t=>[t,SENSITIVE_HEADER_KEYS.has(t.toLowerCase())?`[REDACTED]`:e[t]])),getManifestFormats=e=>e===`mpd`?DASH_FORMATS:e===`m3u8`?HLS_FORMATS:ALL_DASHA_FORMATS,createManifestInput=({http:e,source:t,detection:n})=>(logger.debug(`Fetching manifest...`),logger.debug(t.url),t.headers&&logger.debug(JSON.stringify(getSafeHeadersForLogging(t.headers))),new Input({source:new UrlSource(t.url,{requestInit:{headers:t.headers},fetchFn:e.fetch.bind(e)}),formats:getManifestFormats(n.kind)})),getStableHash=e=>createHash(`sha1`).update(e).digest(`hex`),normalizeTrackLanguageCode=(e,t)=>{if(e)switch(e){case`aar`:case`abk`:return`rus`;case`ave`:return`eng`;case`zu`:return t?.includes(`start.film`)?`ru`:`zu`;default:return e}},createPreparedTrackId=({origin:e,type:t,sourceUrl:n,languageCode:r,name:i,codec:a})=>`${e}:${t}:${getStableHash([n??``,r??``,i??``,a??``].join(`|`))}`,createPreparedTrackFromManifestTrack=async(e,t)=>{let[n,r,i,a,o,s]=await Promise.all([e.getCodec(),e.getBitrate(),e.getLanguageCode(),e.getName(),e.getDisposition(),e.hasOnlyKeyPackets()]),c={id:`manifest:${e.type}:${e.id}`,origin:`manifest`,type:e.type,codec:n,bitrate:r,languageCode:normalizeTrackLanguageCode(i,t),name:a,forced:o.forced,hearingImpaired:o.hearingImpaired,visuallyImpaired:o.visuallyImpaired,isDefault:o.default,isIFrameOnly:s,inputTrack:e};if(e.isVideoTrack()){let[t,n]=await Promise.all([e.getDisplayWidth(),e.getDisplayHeight()]);c.width=t,c.height=n}if(e.isAudioTrack()){let[t,n]=await Promise.all([e.getNumberOfChannels(),e.getSampleRate()]);c.numberOfChannels=t,c.sampleRate=n}return c},ensurePreparedTrackSegments=async e=>e.segments?e.segments:e.inputTrack?(e.segmentsPromise??=e.inputTrack.getSegments().then(t=>(e.segments=t,e.segmentCount=t.length,t)),e.segmentsPromise):(e.segments=[],e.segmentCount=0,e.segments),filterByCodecs=(e,t)=>{let n=e.filter(e=>t.some(t=>t===e.codec));return n.length?n:e.toSorted((e,t)=>(t.bitrate??0)-(e.bitrate??0))},filterByQuality=(e,t)=>{let n=t?parseInt(String(t),10)*16/9:void 0,r=e.toSorted((e,t)=>(t.bitrate??0)-(e.bitrate??0));if(!t)return r;let i=e.filter(e=>Number(e.height)===Number(t));if(i.length)return i;let a=e.filter(e=>Number(e.width)===Number(n));return a.length?a:r},filterByChannels=(e,t)=>e.filter(e=>e.numberOfChannels===Number(t)),normalizeLanguage=e=>e?.trim().toLowerCase()??``,getLanguageFamily=e=>normalizeLanguage(e).slice(0,2),getLanguageVariant=e=>normalizeLanguage(e)||`und`,matchesRequestedLanguage=(e,t)=>{let n=normalizeLanguage(e),r=normalizeLanguage(t);return!n||!r?!1:n===r||n.startsWith(`${r}-`)?!0:getLanguageFamily(n)===getLanguageFamily(r)},getUniqueLanguageFamilies=e=>Array.from(new Map(e.map(e=>normalizeLanguage(e)).filter(Boolean).map(e=>[getLanguageFamily(e),e])).values()),takeBestTracksPerVariant=(e,t)=>{let n=new Map;for(let r of e){let e=getLanguageVariant(r.languageCode),i=n.get(e);if(!i){n.set(e,[r]);continue}i.length<t&&i.push(r)}return Array.from(n.values()).flat()},filterByLanguages=(e,t,n=1)=>{let r=e.toSorted((e,t)=>(t.bitrate??0)-(e.bitrate??0)),i=getUniqueLanguageFamilies(t),a=[];for(let e of i){let t=r.filter(t=>matchesRequestedLanguage(t.languageCode,e));t.length&&a.push(...takeBestTracksPerVariant(t,n))}let o=getUniqueLanguageFamilies(e.map(e=>e.languageCode||`und`));if(!a.length)for(let e of o){let t=r.filter(t=>matchesRequestedLanguage(t.languageCode??`und`,e));t.length&&a.push(...takeBestTracksPerVariant(t,n))}return a},filterPairableAudios=(e,t)=>{if(!t?.inputTrack)return e;let n=t.inputTrack,r=e.filter(e=>e.inputTrack?e.inputTrack.canBePairedWith(n):!0);return r.length?r:e},toKbps=e=>(e??0)/1e3,getSegmentCountLabel=async e=>{if(typeof e.segmentCount==`number`)return`${e.segmentCount} Segments`;if(!e.inputTrack)return`Direct`;try{return`${(await ensurePreparedTrackSegments(e)).length} Segments`}catch{return`? Segments`}},buildSelectedTracks=({videos:e,audios:t,subtitles:n})=>({videos:e,audios:t,subtitles:n,all:[...e,...t,...n]});async function getTracks(e,t,n){let r=t.videoQuality,i=e.filter(e=>e.type===`video`&&!e.isIFrameOnly),a=e.filter(e=>e.type===`audio`),o=e.filter(e=>e.type===`subtitle`);if(t.select){let e=await Promise.all(i.map(async(e,t)=>({name:`${e.width??`?`}x${e.height??`?`} | ${toKbps(e.bitrate)||`?`} Kbps | ${e.codec??`?`} | ${await getSegmentCountLabel(e)}`,value:e.id,checked:t===0}))),t=i.length?await checkbox({message:`Select video streams`,pageSize:12,choices:e,loop:!1}):[],n=i.filter(e=>t.includes(e.id)),r=await Promise.all(a.map(async(e,t)=>({name:`${e.name?`${e.name} | `:``}${toKbps(e.bitrate)||`?`} Kbps | ${e.codec?`${e.codec} | `:``}${e.languageCode||`und`} | ${e.numberOfChannels??`?`}CH | ${await getSegmentCountLabel(e)}`,value:e.id,checked:t===0}))),s=a.length?await checkbox({message:`Select audio streams`,pageSize:12,choices:r,loop:!1}):[],c=a.filter(e=>s.includes(e.id)),l=await Promise.all(o.map(async(e,t)=>({name:`${e.codec??`?`} | ${e.languageCode||`und`} | ${await getSegmentCountLabel(e)}`,value:e.id,checked:t===0}))),u=o.length?await checkbox({message:`Select subtitles`,pageSize:12,choices:l,loop:!1}):[];return buildSelectedTracks({videos:n,audios:c,subtitles:o.filter(e=>u.includes(e.id))})}t.videoCodecs&&(i=filterByCodecs(i,t.videoCodecs)),i=filterByQuality(i,r||NaN);let s=i[0],c=[...t.languages||[]],l=n.audioLanguage?.slice(0,2);!c.length&&l&&c.push(l);let u=filterPairableAudios(a,s);t.audioChannels&&(u=filterByChannels(u,t.audioChannels)),t.audioCodecs&&(u=filterByCodecs(u,t.audioCodecs));let d=filterByLanguages(u,c,1);d.length&&(u=d);let f=c.at(-1),p=f?u.map(e=>!e.languageCode||e.languageCode===`und`?{...e,languageCode:f}:e):u,m=o.filter(e=>t.subtitleLanguages?.length?t.subtitleLanguages.some(t=>matchesRequestedLanguage(e.languageCode||`und`,t)):!1);return buildSelectedTracks({videos:t.skipVideo||!s?[]:[s],audios:t.skipAudio?[]:p,subtitles:t.skipSubtitles?[]:m})}const createEmptyPreparedTracks=()=>({videos:[],audios:[],subtitles:[],all:[]}),audioCodecMap={aac:`aac`,mp4a:`aac`,"mp4a.40.2":`aac`,ac3:`ac3`,"ac-3":`ac3`,eac3:`eac3`,"ec-3":`eac3`,opus:`opus`,vorbis:`vorbis`,flac:`flac`},tryGetUrl=e=>{if(!e)return null;try{return new URL(e)}catch{return null}},normalizePathname=e=>{let t=tryGetUrl(e);if(t)try{return decodeURIComponent(t.pathname)}catch{return t.pathname}},normalizeAudioStreamCodec=e=>{if(!e)return`aac`;let t=e.toLowerCase();return audioCodecMap[t]??audioCodecMap[t.split(`.`)[0]??``]??`aac`},countTracksByType=(e,t)=>e.filter(e=>e.type===t).length,logExtractedTracks=e=>{let t=`Extracted, there are `;if(e.length===0)t+=`no tracks`;else{t+=`${e.length} tracks`;let n=countTracksByType(e,`video`);n&&(t+=`, ${n} video tracks`);let r=countTracksByType(e,`audio`);r&&(t+=`, ${r} audio tracks`);let i=countTracksByType(e,`subtitle`);i&&(t+=`, ${i} subtitle tracks`)}logger.info(t)},appendAdditionalSubtitles=(e,t=[])=>{let n=[...t];for(let t of e.subtitles??[]){let r=normalizePathname(t.url),i=normalizeTrackLanguageCode(t.language,e.url);n.some(e=>{let t=normalizePathname(e.sourceUrl);return!r||!t?!1:r===t})||n.push({id:createPreparedTrackId({origin:`external-subtitle`,type:`subtitle`,sourceUrl:t.url,languageCode:i,name:t.label||t.title,codec:t.format}),origin:`external-subtitle`,type:`subtitle`,sourceUrl:t.url,languageCode:i,codec:t.format,forced:t.forced,name:t.label||t.title})}return n},prepareProgressiveSource=async({options:e,source:t,meta:n,detection:r})=>{let i={...e},a={...n};if(!a.title){let e=tryGetUrl(t.url)?.pathname.split(`/`).pop();e&&(a.title=e)}let{primaryVideoTrack:o,primaryAudioTrack:s}=await inspectMedia(t.url,t.headers),c=await o?.getCodec(),l=await o?.getCodedWidth(),u=await o?.getCodedHeight();i.videoQuality||=u?`${u}p`:void 0;let d=createEmptyPreparedTracks();if(d.subtitles=appendAdditionalSubtitles(t),d.all.push(...d.subtitles),r.isAudio&&!i.skipAudio){let e={id:createPreparedTrackId({origin:`progressive`,type:`audio`,sourceUrl:t.url,languageCode:t.audioLanguage,codec:await s?.getCodec()}),origin:`progressive`,type:`audio`,codec:normalizeAudioStreamCodec(await s?.getCodec()),languageCode:normalizeTrackLanguageCode(await s?.getLanguageCode(),t.url)??normalizeTrackLanguageCode(t.audioLanguage,t.url)??`und`,numberOfChannels:await s?.getNumberOfChannels(),sampleRate:await s?.getSampleRate(),sourceUrl:t.url};return d.audios.push(e),d.all.push(e),{options:i,source:t,meta:a,allTracks:[e],tracks:d}}if(!i.skipVideo){let e=parseInt((i.videoQuality??``).replace(`p`,``),10),n={id:createPreparedTrackId({origin:`progressive`,type:`video`,sourceUrl:t.url,codec:c}),origin:`progressive`,type:`video`,codec:c??`avc`,width:l??void 0,height:u??(e||void 0),sourceUrl:t.url};return d.videos.push(n),d.all.push(n),{options:i,source:t,meta:a,allTracks:[n],tracks:d}}return{options:i,source:t,meta:a,allTracks:[],tracks:d}},prepareInputSource=async({options:e,source:t,meta:n,input:r,disposeInput:i=!0})=>{let a={...e},o={...n},s=i;try{let e=await r.getTracks();if(!e.length)throw new DownloaderError(`MANIFEST_PARSE_FAILED`,`Failed to parse manifest`);let n=await Promise.all(e.map(e=>createPreparedTrackFromManifestTrack(e,t.url)));logExtractedTracks(n);let c=await getTracks(n,a,t),l=c.videos[0]?.height;if(l&&(a.videoQuality=`${l}p`),!o.title){let e=tryGetUrl(t.url)?.host;e&&(o.title=`${e}_${getCurrentDateTimeString()}`)}return s=!1,{options:a,source:t,meta:o,allTracks:n,tracks:c,dispose:i?()=>r.dispose?.():void 0}}finally{s&&r.dispose?.()}},prepareManifestSource=async({http:e,options:t,source:n,meta:r,detection:i})=>prepareInputSource({options:t,source:n,meta:r,input:createManifestInput({http:e,source:n,detection:i})}),prepareSource=async({http:e,options:t,source:n,meta:r,input:i,detection:a})=>i?prepareInputSource({options:t,source:n,meta:r,input:i,disposeInput:!1}):a.strategy===`progressive`?prepareProgressiveSource({options:t,source:n,meta:r,detection:a}):prepareManifestSource({http:e,options:t,source:n,meta:r,detection:a}),hasSuffix=(e,t)=>{try{let n=new URL(e);return n.pathname.endsWith(t)||n.search.endsWith(t)}catch{return!1}},selectPreparationStrategy=async(e,t)=>{let n=hasSuffix(t.url,`.m3u8`),r=hasSuffix(t.url,`.mpd`),i=hasSuffix(t.url,`.mp4`),a=hasSuffix(t.url,`.mov`),o=!1,s=!1;if(!n&&!r&&!i&&!a)try{let n=await e.fetch(t.url,{method:`HEAD`,headers:t.headers});if(!n.ok)logger.warn(`HEAD request for ${t.url} returned ${n.status} ${n.statusText}`);else{let e=n.headers.get(`content-type`);o=!!e?.includes(`audio`),s=!!e?.includes(`video`)}}catch(e){let n=e instanceof Error?e.message:String(e);logger.warn(`HEAD request failed for ${t.url}: ${n}`)}return r?{strategy:`manifest`,kind:`mpd`,isAudio:o,isVideo:s,matchedContentLabel:`Dynamic Adaptive Streaming over HTTP`}:n?{strategy:`manifest`,kind:`m3u8`,isAudio:o,isVideo:s,matchedContentLabel:`HTTP Live Streaming`}:o||s||i||a?{strategy:`progressive`,kind:`progressive`,isAudio:o,isVideo:s,matchedContentLabel:`Progressive Stream`}:{strategy:`manifest`,kind:`unknown`,isAudio:o,isVideo:s}};var require_reusify=__commonJSMin(((e,t)=>{function n(e){var t=new e,n=t;function r(){var r=t;return r.next?t=r.next:(t=new e,n=t),r.next=null,r}function i(e){n.next=e,n=e}return{get:r,release:i}}t.exports=n})),require_queue=__commonJSMin(((e,t)=>{var n=require_reusify();function r(e,t,r){if(typeof e==`function`&&(r=t,t=e,e=null),!(r>=1))throw Error(`fastqueue concurrency must be equal to or greater than 1`);var o=n(a),s=null,c=null,l=0,u=null,d={push:v,drain:i,saturated:i,pause:p,paused:!1,get concurrency(){return r},set concurrency(e){if(!(e>=1))throw Error(`fastqueue concurrency must be equal to or greater than 1`);if(r=e,!d.paused)for(;s&&l<r;)l++,b()},running:f,resume:g,idle:_,length:m,getQueue:h,unshift:y,empty:i,kill:x,killAndDrain:S,error:w,abort:C};return d;function f(){return l}function p(){d.paused=!0}function m(){for(var e=s,t=0;e;)e=e.next,t++;return t}function h(){for(var e=s,t=[];e;)t.push(e.value),e=e.next;return t}function g(){if(d.paused){if(d.paused=!1,s===null){l++,b();return}for(;s&&l<r;)l++,b()}}function _(){return l===0&&d.length()===0}function v(n,a){var f=o.get();f.context=e,f.release=b,f.value=n,f.callback=a||i,f.errorHandler=u,l>=r||d.paused?c?(c.next=f,c=f):(s=f,c=f,d.saturated()):(l++,t.call(e,f.value,f.worked))}function y(n,a){var f=o.get();f.context=e,f.release=b,f.value=n,f.callback=a||i,f.errorHandler=u,l>=r||d.paused?s?(f.next=s,s=f):(s=f,c=f,d.saturated()):(l++,t.call(e,f.value,f.worked))}function b(n){n&&o.release(n);var i=s;i&&l<=r?d.paused?l--:(c===s&&(c=null),s=i.next,i.next=null,t.call(e,i.value,i.worked),c===null&&d.empty()):--l===0&&d.drain()}function x(){s=null,c=null,d.drain=i}function S(){s=null,c=null,d.drain(),d.drain=i}function C(){var e=s;for(s=null,c=null;e;){var t=e.next,n=e.callback,r=e.errorHandler,a=e.value,o=e.context;e.value=null,e.callback=i,e.errorHandler=null,r&&r(Error(`abort`),a),n.call(o,Error(`abort`)),e.release(e),e=t}d.drain=i}function w(e){u=e}}function i(){}function a(){this.value=null,this.callback=i,this.next=null,this.release=i,this.context=null,this.errorHandler=null;var e=this;this.worked=function(t,n){var r=e.callback,a=e.errorHandler,o=e.value;e.value=null,e.callback=i,e.errorHandler&&a(t,o),r.call(e.context,t,n),e.release(e)}}function o(e,t,n){typeof e==`function`&&(n=t,t=e,e=null);function a(e,n){t.call(this,e).then(function(e){n(null,e)},n)}var o=r(e,a,n),s=o.push,c=o.unshift;return o.push=l,o.unshift=u,o.drained=d,o;function l(e){var t=new Promise(function(t,n){s(e,function(e,r){if(e){n(e);return}t(r)})});return t.catch(i),t}function u(e){var t=new Promise(function(t,n){c(e,function(e,r){if(e){n(e);return}t(r)})});return t.catch(i),t}function d(){return new Promise(function(e){process.nextTick(function(){if(o.idle())e();else{var t=o.drain;o.drain=function(){typeof t==`function`&&t(),e(),o.drain=t}}})})}}t.exports=r,t.exports.promise=o})),import_queue=__toESM(require_queue(),1);const DEFAULT_CONNECT_TIMEOUT=3e5,createClient=(e={})=>{let t=e.maxRetries??5,{proxy:n}=e,r=n?new ProxyAgent({uri:n,allowH2:!0}):new Agent({allowH2:!0});return kyImport.create({fetch:e.fetch,dispatcher:r,proxy:n,timeout:DEFAULT_CONNECT_TIMEOUT,retry:{limit:t,methods:[`get`,`head`],statusCodes:[408,413,429,500,502,503,504],backoffLimit:3e4},throwHttpErrors:!0,redirect:`follow`,headers:{"user-agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36`}})},sleep=e=>new Promise(t=>setTimeout(t,e)),createTempFileDir=async(e,t)=>{let n=await fsp.stat(t).catch(()=>null);(!n||!n.isDirectory())&&await fsp.mkdir(t,{recursive:!0});let r=getTempFileDirPath(e,t),i=await fsp.stat(r).catch(()=>null);return(!i||!i.isDirectory())&&await fsp.mkdir(r,{recursive:!0}),r},getTempFileDirPath=(e,t)=>{let n=path.resolve(e),r=path.parse(e).name,i=createHash(`sha1`).update(n).digest(`hex`).slice(0,10);return path.join(t,`${r}-${i}`)},ensureDirExists=async e=>{let t=path.dirname(e),n=path.resolve(t);path.parse(n).root!==n&&(await fsp.stat(t).then(()=>!0).catch(()=>!1)||await fsp.mkdir(t,{recursive:!0}))},clear=async(...e)=>{let t=[];for(let n of e)n&&t.push(fsp.rm(n,{recursive:!0,force:!0}).catch(()=>void 0));await Promise.all(t)},getDefaultResumeFilePath=e=>`${e}.part.json`,resolvePath=e=>{if(e.filePath)return e.filePath;if(e.output)return getDefaultResumeFilePath(e.output);throw Error(`Either output or filePath is required for FileResumeStore`)},isRetryableRenameError=e=>{if(!e||typeof e!=`object`)return!1;let t=`code`in e?e.code:void 0;return t===`EPERM`||t===`EACCES`||t===`EBUSY`},replaceFile=async(e,t)=>{let n=process$1.platform===`win32`?[0,50,150,300,500]:[0],r;for(let i of n){i>0&&await sleep(i);try{await fsp.rename(e,t);return}catch(n){if(r=n,!isRetryableRenameError(n))break;await fsp.rm(t,{force:!0}).catch(()=>void 0);try{await fsp.rename(e,t);return}catch(e){if(r=e,!isRetryableRenameError(e))break}}}throw r};var FileResumeStore=class{async get(e){let t=resolvePath(e);if(!await fsp.stat(t).then(()=>!0).catch(()=>!1))return null;try{let e=await fsp.readFile(t,`utf8`);return JSON.parse(e)}catch{return null}}async set(e,t){let n=resolvePath(e);await ensureDirExists(n);let r=`${n}.${process$1.pid}.${Date.now().toString(36)}.tmp`;await fsp.writeFile(r,JSON.stringify(t));try{await replaceFile(r,n)}finally{try{await fsp.rm(r,{force:!0})}catch{}}}async delete(e){let t=resolvePath(e);await fsp.rm(t,{force:!0}).catch(()=>void 0)}};const toAbsoluteOutputPath=e=>path.resolve(e),getDefaultResumeKey=e=>createHash(`sha1`).update(toAbsoluteOutputPath(e)).digest(`hex`),toStateParts=e=>e.map((e,t)=>({index:t,start:e.start,end:e.end,output:e.output,completed:!1,bytesWritten:0})),hasSameLayout=(e,t)=>e.parts.length===t.length?e.parts.every((e,n)=>{let r=t[n];return r?e.start===r.start&&e.end===r.end&&(e.output??null)===(r.output??null):!1}):!1,hasSameSignature=(e,t)=>!t||!e.sourceSignature?!0:e.sourceSignature===t,normalizeDownloadedBytes=e=>{let t=e.parts.reduce((e,t)=>e+t.bytesWritten,0);return t>0?t:e.bytesDownloaded},isValidResumeState=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return t.version===2&&typeof t.key==`string`&&Array.isArray(t.parts)&&typeof t.bytesDownloaded==`number`};var ResumeManager=class{key;output;filePath;cleanupOnSuccess;store;target;trustExplicitKey;constructor(e={}){if(this.output=e.output,this.filePath=e.filePath,this.key=e.key??(this.output?getDefaultResumeKey(this.output):``),!this.key)throw Error(`Resume key is required when output is not provided`);this.store=e.store??new FileResumeStore,this.cleanupOnSuccess=e.cleanupOnSuccess??!0,this.target={key:this.key,output:this.output,filePath:this.filePath},this.trustExplicitKey=typeof e.key==`string`&&e.key.length>0}async read(){let e=await this.store.get(this.target);return isValidResumeState(e)?e:null}async write(e){let t=new Date().toISOString(),n={...e,version:2,key:this.key,output:e.output??this.output,bytesDownloaded:normalizeDownloadedBytes(e),createdAt:e.createdAt||t,updatedAt:t};await this.store.set(this.target,n)}async delete(){await this.store.delete(this.target)}createState(e){let t=new Date().toISOString();return{version:2,key:this.key,output:this.output,input:e.input,bytesTotal:e.bytesTotal,bytesDownloaded:0,parts:toStateParts(e.parts),sourceSignature:e.sourceSignature,planner:e.planner,createdAt:t,updatedAt:t}}reconcileState(e,t){let n=e.version!==2,r=e.key!==this.key,i=!this.trustExplicitKey&&!hasSameSignature(e,t.sourceSignature),a=!hasSameLayout(e,t.parts);if(n||r||i||a)return{state:this.createState(t),reset:!0};let o={...e,key:this.key,output:this.output,input:t.input,bytesTotal:t.bytesTotal,sourceSignature:t.sourceSignature,planner:t.planner,parts:t.parts.map((t,n)=>{let r=e.parts[n];return{index:n,start:t.start,end:t.end,output:t.output,completed:r?.completed??!1,bytesWritten:r?.bytesWritten??0}}),createdAt:e.createdAt,updatedAt:e.updatedAt};return o.bytesDownloaded=normalizeDownloadedBytes(o),{state:o,reset:!1}}markPartCompleted(e,t,n){let r=e.parts[t];if(!r)return e;let i=r.bytesWritten;return r.completed=!0,r.bytesWritten=n,e.bytesDownloaded=Math.max(0,e.bytesDownloaded-i+n),e.updatedAt=new Date().toISOString(),e}async getProgress(){let e=await this.read();if(!e)return null;let t=e.bytesDownloaded;if(e.bytesTotal){let n=e.bytesTotal;return{bytesDownloaded:t,totalBytes:n,percentComplete:n>0?Math.round(t/n*100):0}}let n=e.parts.length,r=e.parts.filter(e=>e.completed).length;return{bytesDownloaded:t,totalBytes:null,percentComplete:n>0?Math.round(r/n*100):0,segmentsCompleted:r,segmentsTotal:n}}};const DEFAULT_MAX_PART_SIZE=8*2**20,getPartOutput=(e,t,n)=>{let r=`part_${n}.part`;return path.join(t,r)},getPartSize=(e,t,n)=>{let r=Math.ceil(e/Math.max(1,t));return Math.max(1,Math.min(r,Math.max(1,n)))},getRanges=(e,t,n)=>{let r=getPartSize(e,t,n),i=[],a=0;for(;a<e;){let t=Math.min(e-1,a+r-1);i.push([a,t]),a=t+1}return i},getRangeHeader=e=>{let[t,n]=e;return`bytes=${t}-${n||``}`},getParts=e=>getRanges(e.contentLength,e.maxWorkerCount,e.maxChunkSize??DEFAULT_MAX_PART_SIZE).map(t=>{let[n,r]=t,i=new Headers(e.requestInit?.headers);return i.set(`Range`,getRangeHeader([n,r])),{url:e.url,headers:i,start:n,end:r}}),createAbortError$1=e=>{let t=e?Error(`The operation was aborted`,{cause:e}):Error(`The operation was aborted`);return t.name=`AbortError`,t},isAbortLikeError=e=>{if(!(e instanceof Error))return!1;if(e.name===`AbortError`||e.code===`ABORT_ERR`)return!0;let t=e.cause;return t instanceof Error&&t.name===`AbortError`},fetchWorker=async e=>await e.client.get(e.url,{redirect:`follow`,...e.requestInit}),saveData=(e,t,n)=>async r=>{if(n?.aborted)throw createAbortError$1();let i=typeof t==`number`?{flags:`r+`,start:t}:{flags:`w`},a=fs.createWriteStream(e,i),o=Readable.fromWeb(r.body);try{await pipeline(o,a,{signal:n})}catch(e){throw isAbortLikeError(e)?createAbortError$1(e):e}if(n?.aborted)throw createAbortError$1();return r},updateProgress=(e,t,n)=>r=>{if(n?.aborted)throw createAbortError$1();let i=parseInt(r.headers.get(`content-length`)||`0`);if(e.increase(i),n?.aborted)throw createAbortError$1();return t&&!n?.aborted?t(e.toProgressInfo()):e.log(),r},resolveResumeOptions=e=>typeof e==`boolean`?{enabled:e,cleanupOnSuccess:!0}:e?{enabled:e.enabled??!0,key:e.key,store:e.store,filePath:e.filePath,cleanupOnSuccess:e.cleanupOnSuccess??!0}:{enabled:!1,cleanupOnSuccess:!0},resolveDownloadConfig=e=>({tempDir:e.tempDir??process.cwd(),maxRetries:e.maxRetries??5,maxChunkSize:e.maxChunkSize??DEFAULT_MAX_PART_SIZE,maxWorkerCount:e.maxWorkerCount??4,resume:resolveResumeOptions(e.resume),proxy:e.proxy,requestInit:e.requestInit,fetchFn:e.fetchFn}),DEFAULT_STATE={total:{b:0,mb:0},current:{b:0,mb:0},chunkSize:0,chunkSizes:[],averageSize:{b:0,mb:0},averageTotal:{b:0,mb:0},speed:{bps:0,mbps:0}},formatSeconds=e=>{let t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=e%60,i=t>0?`${t}h`:``,a=n>0?`${n}m`:``,o=r>0?`${r}s`:``;return t>0?`${i} ${a||`0m`} ${o&&`${o}`}`:!t&&n>0?`${a} ${o&&`${o}`}`:o},createProgress=(e=0)=>{let t=structuredClone(DEFAULT_STATE),n=0,r=setInterval(()=>{n>1&&(t.speed.bps=t.current.b/n),n+=.5},500);return{state:t,setTotal:e=>{t.total.b=e,t.total.mb=e/1024/1024},setCurrent(e){t.current.b=e,t.current.mb=e/1024/1024},increase(n){t.current.b+=n,t.current.mb=t.current.b/1024/1024,t.speed.mbps=t.speed.bps/1024/1024,t.chunkSize=n,t.chunkSizes.push(n),t.averageSize.b=t.current.b/t.chunkSizes.length,t.averageSize.mb=t.averageSize.b/1024/1024,t.averageTotal.b=e*t.averageSize.b,t.averageTotal.mb=t.averageTotal.b/1024/1024},stop(){clearInterval(r),Object.assign(t,DEFAULT_STATE)},log(){let e=t.current.b,n=t.total.b,r=e+(n?`/${n}`:``);stdout.write(`\rDownloading [${r}]`)},stopLog(){stdout.write(`
|
|
3
|
+
`)},toString(){let e=``;t.speed.bps&&(e+=`${t.speed.mbps.toFixed(1)} MB/s – `),e+=`${t.current.mb.toFixed(1)}`,t.total.b?e+=` of ${t.total.mb.toFixed(1)} MB`:e+=` MB`;let r=t.total.b||t.averageTotal.b;if(r){let i=r-t.speed.bps*n,a=Math.round(i/t.speed.bps),o=formatSeconds(a);a&&isFinite(a)&&o&&(e+=`, ${o} left`)}return e},toProgressInfo(){let e=t.total.b||null;return{bytesDownloaded:t.current.b,totalBytes:e,percentComplete:e?t.current.b/e*100:null,bytesPerSecond:t.speed.bps,estimatedSecondsRemaining:t.speed.bps>0&&e?(e-t.current.b)/t.speed.bps:null}}}},VOLATILE_HEADER_NAMES=new Set([`authorization`,`cookie`,`set-cookie`,`x-amz-date`,`x-amz-security-token`,`x-amz-content-sha256`,`x-goog-date`,`x-goog-security-token`]),serializeHeaders=(e,t={})=>{if(!e)return[];let n=t.includeVolatile??!1,r=[];return new Headers(e).forEach((e,t)=>{!n&&VOLATILE_HEADER_NAMES.has(t.toLowerCase())||r.push([t,e])}),r.sort(([e],[t])=>e.localeCompare(t)),r},normalizeUrlForSignature=e=>{try{let t=new URL(e),n=t.pathname.split(`/`).map(e=>e&&(e.includes(`=`)||e.includes(`,`)?`:volatile`:e)).join(`/`);return`${t.origin}${n}`}catch{let t=String(e);return(t.split(`#`)[0]??t).split(`?`)[0]||t}},createPartSignature=e=>({url:normalizeUrlForSignature(e.url),start:e.start,end:e.end,output:e.output,headers:serializeHeaders(e.headers)}),createSourceSignature=e=>{let t={input:Array.isArray(e.input)?e.input.map(e=>createPartSignature(e)):normalizeUrlForSignature(String(e.resolvedUrl)),requestHeaders:serializeHeaders(e.requestHeaders),parts:e.parts.map(e=>createPartSignature(e)),maxWorkerCount:e.maxWorkerCount,maxChunkSize:e.maxChunkSize};return createHash(`sha1`).update(JSON.stringify(t)).digest(`hex`)},loadOrCreateResumeState=async e=>{if(!e.manager)return null;let t={input:Array.isArray(e.input)?e.input:e.resolvedUrl,parts:e.parts,bytesTotal:e.bytesTotal,sourceSignature:e.sourceSignature,planner:{maxWorkerCount:e.maxWorkerCount,maxChunkSize:e.maxChunkSize}},n=await e.manager.read(),r=(n?e.manager.reconcileState(n,t):null)?.state??e.manager.createState(t);return await e.manager.write(r),r},ensureAllPartsCompleted=e=>{if(e&&!e.parts.every(e=>e.completed))throw Error(`Not all segments were downloaded successfully. Try resuming later.`)},planDownloadParts=e=>{if(Array.isArray(e.input))return e.input.map((t,n)=>({...t,output:getPartOutput(e.url,e.tempFileDir,n)}));if(!e.acceptsRanges)throw Error(`Unable to get parts`);if(!e.size)throw Error(`Size is not known`);return getParts({url:e.url,requestInit:e.requestInit,contentLength:e.size,maxWorkerCount:e.maxWorkerCount,maxChunkSize:e.maxChunkSize})},ensureOutputReadyForRanges=async e=>{if(!e.parts.some(t=>typeof t.start==`number`&&(t.output??e.output)===e.output)||await fsp.stat(e.output).then(()=>!0).catch(()=>!1))return;let t=await fsp.open(e.output,`w`);e.size&&e.size>0&&await t.truncate(e.size),await t.close()},createAbortError=()=>{let e=Error(`The operation was aborted`);return e.name=`AbortError`,e},parseContentRange=e=>{let t=e.match(/bytes\s+(\d+)-(\d+)\/(\d+|\*)/i);if(!t||!t[1]||!t[2]||!t[3])return null;let n=parseInt(t[1],10),r=parseInt(t[2],10),i=t[3];if(!Number.isFinite(n)||!Number.isFinite(r)||r<n)return null;if(i===`*`)return{start:n,end:r,total:null};let a=parseInt(i,10);return!Number.isFinite(a)||a<=0?null:{start:n,end:r,total:a}},validateRangedPartResponse=(e,t)=>{if(typeof t.start!=`number`||typeof t.end!=`number`)return;if(e.status!==206)throw Error(`Range request failed for bytes=${t.start}-${t.end}: expected 206, got ${e.status}`);let n=e.headers.get(`content-range`);if(!n)throw Error(`Range request failed for bytes=${t.start}-${t.end}: missing Content-Range header`);let r=parseContentRange(n);if(!r)throw Error(`Range request failed for bytes=${t.start}-${t.end}: invalid Content-Range "${n}"`);if(r.start!==t.start||r.end!==t.end)throw Error(`Range request failed for bytes=${t.start}-${t.end}: got bytes=${r.start}-${r.end}`)},resolvePartSize=async(e,t,n)=>{let r=e.headers.get(`content-length`);if(r){let e=parseInt(r,10);if(Number.isFinite(e)&&e>0)return e}let i=e.headers.get(`content-range`);if(i){let e=i.match(/bytes\s+(\d+)-(\d+)\//i);if(e&&e[1]&&e[2]){let t=parseInt(e[1],10),n=parseInt(e[2],10);if(Number.isFinite(t)&&Number.isFinite(n)&&n>=t)return n-t+1}}return typeof t.start==`number`&&typeof t.end==`number`?Math.max(0,t.end-t.start+1):t.output&&t.output!==n?fsp.stat(t.output).then(e=>e.size).catch(()=>0):0},getPendingPartIndexes=(e,t)=>e.map((e,t)=>t).filter(e=>!(t?.parts[e]?.completed??!1)),createStateWriteScheduler=e=>{let t=Promise.resolve();return{enqueue:()=>(t=t.then(e),t),onIdle:()=>t}},runPartQueue=async e=>{let t=e.signal??e.requestInit?.signal??void 0,n=()=>{if(t?.aborted)throw createAbortError()};n();let r=getPendingPartIndexes(e.parts,e.resumeState),i=import_queue.promise(async r=>{let{index:i,part:a}=r;n();let o=await fetchWorker({index:i,client:e.client,url:a.url,requestInit:{...e.requestInit,signal:t,headers:a.headers??e.requestInit?.headers}});if(n(),validateRangedPartResponse(o,a),await saveData(a.output??e.output,a.start,t)(o),updateProgress(e.progress,e.onProgress,t)(o),n(),e.resumeState&&e.resumeManager){let t=await resolvePartSize(o,a,e.output);e.resumeManager.markPartCompleted(e.resumeState,i,t),await c.enqueue()}},e.maxWorkerCount),a=()=>{i.pause(),i.killAndDrain(),i.kill()};if(t?.aborted)throw a(),createAbortError();let o=()=>{},s=t?new Promise((e,n)=>{let r=()=>{a(),n(createAbortError())};t.addEventListener(`abort`,r,{once:!0}),o=()=>t.removeEventListener(`abort`,r)}):null,c=createStateWriteScheduler(async()=>{!e.resumeState||!e.resumeManager||await e.resumeManager.write(e.resumeState)}),l=r.map(t=>{let r=e.parts[t];return r?(n(),i.push({index:t,part:r})):Promise.resolve()}),u=[];try{let e=Promise.allSettled(l);s&&(await Promise.race([e,s]),n()),u=await e}finally{o(),await c.onIdle()}let d=u.find(e=>e.status===`rejected`);if(d?.status===`rejected`)throw d.reason},pipePartToOutput=async(e,t)=>{await new Promise((n,r)=>{let i=fs.createReadStream(e),a=()=>{t.off(`error`,o),i.off(`error`,s),i.off(`end`,c)},o=e=>{a(),r(Error(`Output write stream error`,{cause:e}))},s=e=>{a(),r(Error(`Segment read stream error`,{cause:e}))},c=()=>{a(),n()};t.on(`error`,o),i.on(`error`,s),i.on(`end`,c),i.pipe(t,{end:!1})})},mergePartOutputs=async e=>{let t=e.parts.map(e=>e.output).filter(t=>!!t&&t!==e.output);if(!t.length)return;let n=fs.createWriteStream(e.output);for(let e of t)await pipePartToOutput(e,n);n.end(),await finished(n)},validateOutputSize=async e=>{if(!e.expectedSize)return;let t=await fsp.stat(e.output).then(e=>e.size).catch(()=>null);if(t&&t!==e.expectedSize)throw Error(`Actual size ${t} doesn't match expected size ${e.expectedSize}`)},finalizeDownload=async e=>{e.resumeManager&&e.resumeManager.cleanupOnSuccess&&await e.resumeManager.delete(),await clear(e.tempFileDir)},executeDownloadFlow=async e=>{let t=createSourceSignature({input:e.input,resolvedUrl:e.url,parts:e.parts,requestHeaders:e.requestInit?.headers,maxWorkerCount:e.maxWorkerCount,maxChunkSize:e.maxChunkSize}),n=await loadOrCreateResumeState({manager:e.resumeManager,input:e.input,resolvedUrl:e.url,parts:e.parts,bytesTotal:e.size??void 0,sourceSignature:t,maxWorkerCount:e.maxWorkerCount,maxChunkSize:e.maxChunkSize}),r=createProgress(e.parts.length);e.size&&r.setTotal(e.size),n?.bytesDownloaded&&r.setCurrent(n.bytesDownloaded),await ensureOutputReadyForRanges({output:e.output,size:e.size,parts:e.parts});try{await runPartQueue({parts:e.parts,output:e.output,maxWorkerCount:e.maxWorkerCount,client:e.client,requestInit:e.requestInit,progress:r,onProgress:e.onProgress,resumeState:n,resumeManager:e.resumeManager,signal:e.signal})}finally{r.stop(),e.onProgress||r.stopLog()}ensureAllPartsCompleted(n),await mergePartOutputs({output:e.output,parts:e.parts}),await validateOutputSize({output:e.output,expectedSize:Array.isArray(e.input)?null:e.size}),await finalizeDownload({tempFileDir:e.tempFileDir,resumeManager:e.resumeManager})},probeInput=async e=>{let t=new Headers(e.requestInit?.headers);t.set(`Range`,`bytes=0-0`);let n=Array.isArray(e.input)?e.input[0]?.url:e.input;if(!n)throw Error(`Input is empty`);let r=await e.client.head(n,{...e.requestInit,headers:t}),i=r.headers.get(`content-type`),a=r.headers.get(`content-length`),o=r.headers.get(`content-range`),s=o?.split(`/`)?.[1]||a,c=s&&!Array.isArray(e.input)?parseInt(s,10):null,l=r.headers.get(`accept-ranges`)===`bytes`||!!o?.includes(`bytes`)||!!i?.includes(`video`)||!!i?.includes(`audio`)||!!i?.includes(`octet-stream`);return{url:r.url,size:c,acceptsRanges:l}};var Download=class e{input;output;resume;onProgress;config;client;resumeManager;probe;tempFileDir;constructor(e){this.input=e.input,this.output=e.output,this.resume=e.resume,this.onProgress=e.onProgress,this.config=resolveDownloadConfig(e),this.client=createClient({maxRetries:this.config.maxRetries,proxy:this.config.proxy,fetch:this.config.fetchFn}),this.config.resume.enabled&&(this.resumeManager=new ResumeManager({output:this.output,key:this.config.resume.key,filePath:this.config.resume.filePath,store:this.config.resume.store,cleanupOnSuccess:this.config.resume.cleanupOnSuccess}))}static async init(t){let n=new e(t);return await n._init(),n}async _init(){this.probe=await probeInput({client:this.client,input:this.input,requestInit:this.config.requestInit}),await ensureDirExists(this.output),this.tempFileDir=await createTempFileDir(this.output,this.config.tempDir),this.config.resume.enabled||(await clear(this.output,getDefaultResumeFilePath(this.output),this.tempFileDir),this.tempFileDir=await createTempFileDir(this.output,this.config.tempDir))}async execute(){if(!this.probe||!this.tempFileDir)throw Error(`Download was not initialized. Use Download.init(options).`);let e=planDownloadParts({input:this.input,url:this.probe.url,size:this.probe.size,acceptsRanges:this.probe.acceptsRanges,requestInit:this.config.requestInit,tempFileDir:this.tempFileDir,maxWorkerCount:this.config.maxWorkerCount,maxChunkSize:this.config.maxChunkSize});await executeDownloadFlow({input:this.input,output:this.output,url:this.probe.url,size:this.probe.size,requestInit:this.config.requestInit,maxWorkerCount:this.config.maxWorkerCount,maxChunkSize:this.config.maxChunkSize,onProgress:this.onProgress,client:this.client,parts:e,tempFileDir:this.tempFileDir,resumeManager:this.resumeManager,signal:this.config.requestInit?.signal??void 0})}async getSize(){if(this.probe||=await probeInput({client:this.client,input:this.input,requestInit:this.config.requestInit}),!this.probe.size)throw Error(`Size is not known`);return this.probe.size}};const retry=async({attempts:e,fn:t,shouldRetry:n,onRetry:r})=>{let i=Math.max(1,Math.trunc(e)),a;for(let e=1;e<=i;e+=1)try{return await t(e)}catch(t){if(a=t,e>=i||!n?.(t,e))throw t;await r?.(t,e)}throw a instanceof Error?a:Error(String(a))},forceNanospinnerTtyIfNeeded=()=>{if(process.platform===`win32`&&!isatty(process.stderr.fd)&&loadWinConsoleAddon()?.getConsoleSize?.()?.columns)try{let e=__require(`nanospinner/dist/consts.js`);e&&e.isTTY===!1&&(e.isTTY=!0,e.symbols?.frames?.length===1&&(e.symbols.frames=[`-`,`\\`,`|`,`/`]))}catch{}},getSpinnerStream=e=>{let t=process.stderr;if(!e||process.platform!==`win32`)return t;if(!t.columns||t.columns!==e){let n=Object.getOwnPropertyDescriptor(t,`columns`)??Object.getOwnPropertyDescriptor(Object.getPrototypeOf(t),`columns`);if(!n||n.writable||n.set)try{t.columns=e}catch{}if(t.columns!==e){let n=Object.create(t);return Object.defineProperty(n,`columns`,{value:e,configurable:!0,enumerable:!0,writable:!0}),logger.warn(`Unable to override stderr.columns; using spinner-local width ${e}.`),n}}return t},logLevelsColors={debug:`gray`,info:`green`,warn:`yellow`,error:`red`},getColorizedLogLevel=e=>{let t=e.toUpperCase(),n=colors[logLevelsColors[e]];return n(t)},withPrefixedText=(e,t)=>typeof t==`string`?`${e} ${t}`:t&&{...t,text:`${e} ${t.text??``}`.trimEnd()},createSpinner$1=(e,t)=>{forceNanospinnerTtyIfNeeded();let n=getLogPrefix(getColorizedLogLevel(`info`)),r=createSpinner(e,{stream:getSpinnerStream(loadWinConsoleAddon()?.getConsoleSize?.()?.columns??process.stderr.columns??process.stdout.columns??(process.platform===`win32`?120:80))}).start({text:`${n} ${e}`,color:t}),i=r.update.bind(r),a=r.success.bind(r),o=r.error.bind(r);return r.update=e=>i(withPrefixedText(n,e)),r.success=e=>a(withPrefixedText(n,e)),r.error=e=>o(withPrefixedText(n,e)),r},MAX_SAFE_MOLNIA_WORKERS=16,getWidevinePssh=e=>{if(!(!e||typeof e!=`object`||!(`drm`in e)))return e.drm?.widevine?.pssh},getTrackResumeKey=(e,t)=>createHash(`sha1`).update(`${e}:${getTrackId(t)}`).digest(`hex`),buildTrackArtifactPath=({outputDirectory:e,resolvedFilenameWithoutExtension:t,track:n})=>join(e,`${t}.${createHash(`sha256`).update(getTrackId(n)).digest(`hex`).slice(0,6)}.${n.type===`subtitle`?`vtt`:n.type===`audio`?`m4a`:`mp4`}`),getSegmentRangeHeaders=(e,t)=>{if(t)return{Range:`bytes=${e}-${e+t-1}`}},buildTrackDownloadInput=async e=>{if(e.sourceUrl&&!e.inputTrack)return e.sourceUrl;let t=[],n=await ensurePreparedTrackSegments(e),r=n[0];r?.initSegment&&t.push({url:r.initSegment.location.path,headers:getSegmentRangeHeaders(r.initSegment.location.offset,r.initSegment.location.length)});for(let e of n)t.push({url:e.location.path,headers:getSegmentRangeHeaders(e.location.offset,e.location.length)});return!t.length&&e.sourceUrl?e.sourceUrl:t.length===1&&!t[0]?.headers?t[0].url:t},resolveMaxWorkerCount=e=>{let t=Math.max(1,Math.trunc(e)),n=Math.min(MAX_SAFE_MOLNIA_WORKERS,t);return{normalizedRequestedConnections:t,maxWorkerCount:n,wasClamped:t!==n}},resolveMaxAttempts=e=>Math.max(1,Math.trunc(e)+1),buildTrackResume=(e,t)=>t.origin===`manifest`?{enabled:!0,key:getTrackResumeKey(e,t)}:!0,buildTrackProgressInfo=({title:e,progress:t,track:n,filepath:r,target:i})=>{let a={b:t.totalBytes??NaN,mb:(t.totalBytes??NaN)/1024/1024},o={b:t.bytesDownloaded??NaN,mb:(t.bytesDownloaded??NaN)/1024/1024},s=t.totalBytes?Math.round(t.bytesDownloaded/t.totalBytes*100):void 0;return{title:e,total:a,current:o,percentage:s,speed:{bps:t.bytesPerSecond??NaN,mbps:(t.bytesPerSecond??NaN)/1024/1024},trackName:basename(r),trackPath:r,finalName:i.finalName,finalPath:i.finalPath,state:s===100?`downloaded`:`downloading`,quality:n.type===`video`&&n.width?`${n.width}x${n.height}`:void 0,codec:n.type===`video`?n.codec??void 0:void 0}},buildTrackProgressMessage=e=>`Downloading ∙ ${`${(e.bytesDownloaded/1024/1024).toFixed(2)} MB`}${e.totalBytes?` of ${(e.totalBytes/1024/1024).toFixed(2)} MB`:``}`,collectPsshList=e=>e.flatMap(e=>{let t=e.segments?.[0],n=getWidevinePssh(t?.initSegment?.encryption)??getWidevinePssh(t?.encryption);return n?[n]:[]}).map(e=>{let t=e;return typeof t==`string`?t:t instanceof Uint8Array||t instanceof ArrayBuffer?Buffer.from(t).toString(`base64`):ArrayBuffer.isView(t)?Buffer.from(t.buffer).toString(`base64`):String(t)}),toDownloadArtifact=(e,t)=>({path:t,type:e.type,language:e.languageCode,label:e.name??void 0,forced:e.type===`subtitle`?e.forced:!1}),downloadTrack=async({track:e,filepath:t,description:n,target:r,source:i,options:a,settings:o,abortSignal:s,title:c,emitProgress:l})=>{logger.info(n),logger.debug(`Path: ${t}`);let u=a.connections??o.get(`connections`)??1,{maxWorkerCount:d,wasClamped:f}=resolveMaxWorkerCount(u);f&&logger.warn(`Connections value ${u} was clamped to ${d} for stability`),logger.debug(`Max worker count: ${d}`);let p=createSpinner$1(`Downloading`,`yellow`),m=``,h=await buildTrackDownloadInput(e),g=resolveMaxAttempts(a.retry??o.get(`retry`)??1);try{await retry({attempts:g,fn:async()=>{let n=await Download.init({input:h,output:t,tempDir:APP_TEMP_DIR,requestInit:{headers:i.headers,signal:s},proxy:a.proxyMedia||a.proxy||void 0,resume:buildTrackResume(i.url,e),maxRetries:a.retry,maxWorkerCount:d,onProgress:n=>{l(buildTrackProgressInfo({title:c,progress:n,track:e,filepath:t,target:r})),m=buildTrackProgressMessage(n),p.update(m)}});if(s?.aborted){logger.debug(`Track download skipped, already aborted: ${t}`);let e=Error(`The operation was aborted`);throw e.name=`AbortError`,e}await n.execute()},shouldRetry:e=>!(e instanceof Error&&e.name===`AbortError`),onRetry:async(e,n)=>{let r=e instanceof Error?e.message:String(e);logger.warn(`Track download retry ${n}/${g-1} scheduled for ${t}: ${r}`)}}),p.success(m)}catch(e){if(p.error(n),e instanceof Error&&e.name===`AbortError`)throw e;let t=e instanceof Error?e.message:String(e);throw logger.error(`Download track failed: ${t}`),e instanceof Error&&e.stack?logger.debug(e.stack):logger.debug(t),e instanceof Error?e:Error(t)}},isAbortError=e=>e instanceof Error&&e.name===`AbortError`,isAbortSignal=e=>!!e&&typeof e==`object`&&`aborted`in e&&`addEventListener`in e&&typeof e.addEventListener==`function`;var Downloader=class extends EventEmitter$1{#e;#t;#n;#r;#i;#a;constructor({settings:e,downloadsStorage:t}){super(),this.#i=e,this.#a=t}async start(e,t,n={id:crypto.randomUUID()},r,i){isAbortSignal(r)?(this.#r=new AbortController,r.addEventListener(`abort`,()=>{this.#r?.abort()})):this.#r=void 0;let a=new HttpClient({proxyUrl:e.proxy||e.proxyMeta||void 0});this.#e={...e},this.#t=t,this.#n={...n};let o;try{this.#o();let e=await selectPreparationStrategy(a,this.#t);e.matchedContentLabel&&logger.info(`Content Matched: ${e.matchedContentLabel}`),logger.info(`Parsing streams...`),o=await prepareSource({http:a,options:this.#e,source:this.#t,meta:this.#n,input:i,detection:e}),this.#e=o.options,this.#n=o.meta,await assertDownloadDirWritable(this.#i.get(`downloadDir`));let t=resolveDownloadOutputPlan({settings:this.#i,options:this.#e,source:this.#t,meta:this.#n,allTracks:o.allTracks});return this.#s(t.outputPath,t.resolvedFilenameWithoutExtension),await this.#c(o,t)}catch(e){if(isAbortError(e))throw e;let t=normalizeDownloaderError(e);throw this.emit(`error`,t),t}finally{await o?.dispose?.()}}#o(){logger.debug(`URL: ${this.#t.url}`);let e=`seasonNumber`in this.#n?this.#n.seasonNumber:void 0,t=`episodeNumber`in this.#n?this.#n.episodeNumber:void 0,n=`episodeTitle`in this.#n?this.#n.episodeTitle:``,r=[this.#n.title,e?`S${e}`:``,t?`E${t}`:``,n].filter(Boolean).join(` ∙ `);r&&logger.info(r)}#s(e,t){this.emit(`output-path`,e),logger.debug(`Resolved output path: ${e}`);let n=createDownloadMetadata(this.#n),r=this.#a.getByFilePath(e);r&&(r.title=n.title||t),logger.debug(`Emit download start`),this.emit(`start`,this.#a.jobs)}async#c(e,t){logger.debug(`Downloading tracks`);let n=[];assertUniqueTrackIds(e.tracks.all);for(let r of e.tracks.all){let e=buildTrackArtifactPath({outputDirectory:t.outputDirectory,resolvedFilenameWithoutExtension:t.resolvedFilenameWithoutExtension,track:r}),i=getTrackDescription(r,t.resolvedFilenameWithoutExtension);r.type===`subtitle`&&logger.debug(`URL: ${r.sourceUrl??`manifest subtitle track`}`),await downloadTrack({track:r,filepath:e,description:i,target:{finalName:t.filenameWithExtension,finalPath:t.outputPath},source:this.#t,options:this.#e,settings:this.#i,abortSignal:this.#r?.signal,title:createDownloadMetadata(this.#n).title,emitProgress:e=>{this.emit(`progress`,e,t.outputPath)}}),await exists(e)&&n.push(toDownloadArtifact(r,e))}return{downloaded:n,psshList:collectPsshList(e.tracks.all),selectedTracks:e.tracks.all}}};const describeUrl=e=>{try{let t=new URL(e),n=[...new Set(t.searchParams.keys())],r=n.length?`?${n.join(`,`)}`:``;return`${t.origin}${t.pathname}${r}`}catch{return e}},getContentKeys=async(e,t)=>{let{verbose:n,url:r,resolveLicense:i}=t,a=Buffer.from(e,`base64`),o=`cenc`,s=joinUrl(getApiBaseUrl(),`drm`),c=new RemoteCdm({keySystem:`com.widevine.alpha`,baseUrl:s,headers:{Authorization:`Bearer ${authState.accessToken}`}}),l=`initializing`,u=null;try{logger.debug(`DRM key request started: license=${describeUrl(r)}, remoteCdm=${describeUrl(s)}, psshLength=${e.length}, initDataBytes=${a.length}`),l=`requesting-media-key-system-access`;let t=requestMediaKeySystemAccess(c.keySystem,[]);l=`creating-media-keys`;let o=await t.createMediaKeys({cdm:c});l=`creating-session`,u=o.createSession(),l=`generating-license-request`,logger.debug(`Generating license request...`);let d=u.waitForLicenseRequest();await u.generateRequest(`cenc`,a);let f=await d;logger.debug(`License request of ${f.length} bytes generated for session ${u.sessionId}`),l=`requesting-license`,logger.debug(`Requesting license at ${r}`);let p=await i({url:r,pssh:e,data:new Uint8Array(f),sessionId:u.sessionId});if(p[0]===123||p[0]===60){logger.debug(`License response is not binary`);let e=Buffer.from(p).toString(`utf8`);if(logger.debug(e),e.includes(`<html>`))throw Error(`Invalid license response`)}l=`updating-session`,logger.debug(`Updating session...`),await u.update(new Uint8Array(p)),l=`waiting-for-key-statuses-change`,logger.debug(`Waiting for key statuses change...`);let m=await u.waitForKeyStatusesChange();logger.debug(`Keys count: ${m.length}`),l=`closing-session`,await u.close(),await u.remove();for(let e of m){let t=`Key: ${e.keyId}:${e.key}`;n?logger.info(t):logger.debug(t)}return m.map(e=>({kid:e.keyId,key:e.key}))}catch(e){throw logger.error(`Failed to get DRM content keys at stage=${l} for license=${describeUrl(r)} via remoteCdm=${describeUrl(s)}`),logErrorDetails(`DRM content key error`,e),e}finally{if(u){try{await u.close()}catch{}try{await u.remove()}catch{}}}},extractDecryptionKeys=async({keys:e,drm:t,psshList:n,drmDir:r,resolveLicense:i,licenseUrl:a})=>{logger.debug(`Extracting decryption keys`);let o=[],s=t&&`keys`in t?t.keys:[],c=[...e||[],...s];c.length&&o.push(...c);let l=a??(t&&`server`in t?t.server:void 0);if(i&&l){let e=[...new Set(n??[])];for(let[t,n]of e.entries()){logger.debug(`Resolving DRM keys for PSSH ${t+1}/${e.length} using ${describeUrl(l)}`),logger.debug(`PSSH: ${n}`);let a=await getContentKeys(n,{url:l,resolveLicense:i,clientDir:r});a&&a.length&&o.push(...a)}o.length||(logger.warn(`Can't get decryption keys`),logger.debug(`PSSH: ${e.join(`, `)}`))}return o};var Decrypter=class{strategy;constructor(e){this.strategy=e}async prepare(){await this.strategy.prepare?.()}async process(e){let t=await this.strategy.decryptFile(e);return e.cleanup&&await rm(e.input,{force:!0}),t}};const normalizeHex=e=>e.replaceAll(`-`,``).toLowerCase();var ShifroDecrypter=class{async decryptFile(e){let t=new Map;for(let n of e.keys){if(!n.kid)throw Error(`Shifro requires a KID for every decryption key`);t.set(normalizeHex(n.kid),normalizeHex(n.key))}return logger.debug(`Decrypting via shifro: ${e.input}`),await(await Decryption.init({input:new Input$2({source:new FilePathSource(e.input),keys:t}),output:new Output$1({target:new FilePathTarget(e.output)})})).execute(),e.output}};const cueBlockHeaderRegex=/(?:(.+?)\n)?((?:\d{2}:)?\d{2}:\d{2}\.\d{3})\s+-->\s+((?:\d{2}:)?\d{2}:\d{2}\.\d{3})([^\n]*)/g,preambleStartRegex=/^\uFEFF?WEBVTT(?:.|\n)*?\n{2}/,subtitleTimestampRegex=/(?:(\d{2}):)?(\d{2}):(\d{2})\.(\d{3})/,knownLanguageCodes={ar:`ara`,de:`deu`,en:`eng`,es:`spa`,fr:`fra`,hi:`hin`,it:`ita`,ja:`jpn`,ko:`kor`,pl:`pol`,pt:`por`,ru:`rus`,th:`tha`,tr:`tur`,uk:`ukr`,und:`und`,vi:`vie`,zh:`zho`},isSubtitleInput=e=>e===`subtitle`||e===`text`,parseMuxTimestamp=e=>{let t=e.trim().split(`:`);if(t.length<2||t.length>3)throw Error(`Invalid trim timestamp: ${e}`);let n=Number(t.at(-1)),r=Number(t.at(-2)),i=t.length===3?Number(t[0]):0;if(![i,r,n].every(e=>Number.isFinite(e)&&e>=0))throw Error(`Invalid trim timestamp: ${e}`);return i*3600+r*60+n},parseSubtitleTimestamp=e=>{let t=subtitleTimestampRegex.exec(e);if(!t)throw Error(`Invalid WebVTT timestamp: ${e}`);return 3600*1e3*Number(t[1]||`0`)+60*1e3*Number(t[2])+1e3*Number(t[3])+Number(t[4])},formatWebVttTimestamp=e=>{let t=Math.floor(e/(3600*1e3)),n=Math.floor(e%(3600*1e3)/(60*1e3)),r=Math.floor(e%(60*1e3)/1e3),i=e%1e3;return`${t.toString().padStart(2,`0`)}:${n.toString().padStart(2,`0`)}:${r.toString().padStart(2,`0`)}.${i.toString().padStart(3,`0`)}`},formatSubtitleConversionIssues=e=>{let t=[e.invalidEntries?.length?`${e.invalidEntries.length} invalid entries`:void 0,e.invalidTimecodes?.length?`${e.invalidTimecodes.length} invalid timecodes`:void 0,e.invalidIndices?.length?`${e.invalidIndices.length} invalid indices`:void 0,e.timecodeIssues?.reversedTimecodes?.length?`${e.timecodeIssues.reversedTimecodes.length} reversed timecodes`:void 0,e.timecodeIssues?.overlappingTimecodes?.length?`${e.timecodeIssues.overlappingTimecodes.length} overlapping timecodes`:void 0].filter(Boolean);return t.length?` (${t.join(`, `)})`:``},convertSubtitleTextToWebVtt=e=>{let t=e.replaceAll(`\r
|
|
4
|
+
`,`
|
|
5
|
+
`).replaceAll(`\r`,`
|
|
6
|
+
`).trim();if(preambleStartRegex.test(t))return t;let{subtitle:n,status:r}=convert(e,`.vtt`);if(!r.success)throw Error(`Failed to convert subtitle to WebVTT${formatSubtitleConversionIssues(r)}.`);return n.replaceAll(`\r
|
|
7
|
+
`,`
|
|
8
|
+
`).replaceAll(`\r`,`
|
|
9
|
+
`).trim()},normalizeLanguageCode=e=>{if(!e)return;let t=e.trim().toLowerCase();if(!t)return;let n=t.split(`-`)[0]??t;return/^[a-z]{3}$/.test(n)?n:knownLanguageCodes[n]},createOutputFormat=e=>{switch(extname(e).toLowerCase()){case`.mp4`:return new Mp4OutputFormat;case`.mkv`:return new MkvOutputFormat;default:throw Error(`Unsupported mux output format: ${e}`)}},createInput=e=>new Input$1({formats:ALL_FORMATS$1,source:createMediabunnyFileSource(e)}),getTrimRange=e=>{let t=e.trimBegin?parseMuxTimestamp(e.trimBegin):void 0,n=e.trimEnd?parseMuxTimestamp(e.trimEnd):void 0;if(t!==void 0&&n!==void 0&&n<=t)throw Error(`trimEnd must be greater than trimBegin`);return{trimBegin:t,trimEnd:n}},prepareVideoTrack=async(e,t,n,r,i)=>{let a=createInput(t),o=await a.getPrimaryVideoTrack();if(!o)throw Error(`Missing video track in ${t}`);let s=o.codec;if(!s)throw Error(`Unknown video codec in ${t}`);if(i!==void 0&&i>=await o.computeDuration())throw Error(`trimBegin excludes the full video track in ${t}`);let c=new EncodedPacketSink(o),l=i===void 0?await c.getFirstKeyPacket({verifyKeyPackets:!0}):await c.getKeyPacket(i,{verifyKeyPackets:!0})??await c.getFirstKeyPacket({verifyKeyPackets:!0});if(!l)throw Error(`No video packets found in ${t}`);let u=new EncodedVideoPacketSource(s);return e.addVideoTrack(u,{disposition:o.disposition,name:n??o.name??void 0,rotation:r.supportsVideoRotationMetadata?o.rotation:void 0}),{kind:`video`,input:a,sink:c,source:u,startPacket:l,startTimestamp:l.timestamp,emit:async e=>{let t={decoderConfig:await o.getDecoderConfig()??void 0},n=!0;for await(let r of c.packets(l,void 0,{verifyKeyPackets:!0}))await u.add(r.clone({timestamp:r.timestamp-e}),n?t:void 0),n=!1;u.close()}}},prepareAudioTrack=async(e,t,n,r,i)=>{let a=createInput(t),o=await a.getPrimaryAudioTrack();if(!o)throw Error(`Missing audio track in ${t}`);let s=o.codec;if(!s)throw Error(`Unknown audio codec in ${t}`);if(i!==void 0&&i>=await o.computeDuration())throw Error(`trimBegin excludes the full audio track in ${t}`);let c=new EncodedPacketSink(o),l=i===void 0?await c.getFirstPacket():await c.getPacket(i)??await c.getFirstPacket();if(!l)throw Error(`No audio packets found in ${t}`);let u=new EncodedAudioPacketSource(s);return e.addAudioTrack(u,{disposition:o.disposition,languageCode:normalizeLanguageCode(n)??normalizeLanguageCode(o.languageCode),name:r??o.name??void 0}),{kind:`audio`,input:a,sink:c,source:u,startPacket:l,startTimestamp:l.timestamp,emit:async e=>{let t={decoderConfig:await o.getDecoderConfig()??void 0},n=!0;for await(let r of c.packets(l))await u.add(r.clone({timestamp:r.timestamp-e}),n?t:void 0),n=!1;u.close()}}},rebaseWebVtt=(e,{trimBegin:t,trimEnd:n,commonStart:r})=>{let i=e.replaceAll(`\r
|
|
10
|
+
`,`
|
|
11
|
+
`).replaceAll(`\r`,`
|
|
12
|
+
`).trim();if(!preambleStartRegex.test(i))throw Error(`Only WebVTT subtitles are supported by the MediaBunny muxer.`);let a=t===void 0?void 0:Math.round(t*1e3),o=n===void 0?void 0:Math.round(n*1e3),s=Math.round(r*1e3);cueBlockHeaderRegex.lastIndex=0;let c=cueBlockHeaderRegex.exec(i),l=i.slice(0,c?.index??i.length).trimEnd(),u=[],d=c?i.slice(c.index):``;for(;;){cueBlockHeaderRegex.lastIndex=0;let e=cueBlockHeaderRegex.exec(d);if(!e)break;let t=e.index+e[0].length,n=d.indexOf(`
|
|
13
|
+
`,t)+1,r=d.indexOf(`
|
|
14
|
+
|
|
15
|
+
`,t);r===-1&&(r=d.length);let i=e[1]?.trim(),c=e[4]?.trim(),l=d.slice(n,r).trim(),f=parseSubtitleTimestamp(e[2]),p=parseSubtitleTimestamp(e[3]),m=Math.max(f,a??f)-s,h=Math.min(p,o??p)-s;if(h>m){let e=`${formatWebVttTimestamp(m)} --> ${formatWebVttTimestamp(h)}${c?` ${c}`:``}`;u.push([i,e,l].filter(Boolean).join(`
|
|
16
|
+
`))}d=d.slice(r).trimStart()}return[l,...u].filter(Boolean).join(`
|
|
17
|
+
|
|
18
|
+
`)},addTrackToOutput=async(e,t,n,r,i)=>{if(n.type===`video`)return prepareVideoTrack(e,n.path,n.label,t,r);if(n.type===`audio`)return prepareAudioTrack(e,n.path,n.language,n.label,r);if(isSubtitleInput(n.type)){let t=convertSubtitleTextToWebVtt(await readFile(n.path,`utf8`)),a=new TextSubtitleSource(`webvtt`);return e.addSubtitleTrack(a,{disposition:n.forced?{forced:!0}:void 0,languageCode:normalizeLanguageCode(n.language),name:n.label}),{kind:`subtitle`,source:a,emit:async e=>{let n=rebaseWebVtt(t,{trimBegin:r,trimEnd:i,commonStart:e});await a.add(n),a.close()}}}throw Error(`Unsupported mux input type: ${n.type}`)};var MediaBunnyMuxer=class{async mux(e){let{trimBegin:t,trimEnd:n}=getTrimRange(e),r=createOutputFormat(e.output),i=new Output({format:r,target:createMediabunnyFileTarget(e.output)}),a=[];try{for(let o of e.inputs)a.push(await addTrackToOutput(i,r,o,t,n));let o=a.filter(e=>e.kind!==`subtitle`);if(!a.length)throw Error(`No tracks provided for muxing.`);let s=o.length>0?Math.min(...o.map(e=>e.startTimestamp)):t??0;await i.start();for(let e of a){if(e.kind===`subtitle`){await e.emit(s);continue}await e.emit(s)}return await i.finalize(),e.output}catch(e){throw await i.cancel().catch(()=>void 0),e}finally{for(let e of a)e.kind!==`subtitle`&&e.input.dispose()}}},Muxer=class{strategy;constructor(e){this.strategy=e}async prepare(){await this.strategy.prepare?.()}async process(e){let t=await this.strategy.mux(e);if(e.cleanup)for(let t of new Set(e.inputs.map(e=>e.path)))await rm(t,{force:!0});return t}},ProcessingHandler=class{fn;next=null;constructor(e){this.fn=e,this.next=null}},ProcessingChain=class{first=null;last=null;constructor(){this.first=null,this.last=null}add(e){let t=new ProcessingHandler(e);return this.first?this.last.next=t:this.first=t,this.last=t,this}async process(e){let t=this.first,n=async()=>await t.fn(e,async()=>{if(t=t.next,t)return await n();throw Error(`No handler detected`)});return await n()}};const decryption=async(e,t)=>{if(!e.keys.length)return t();let n=createSpinner$1(`Decrypting`),r=new Decrypter(new ShifroDecrypter);await r.prepare();for(let t of e.inputs){let n=extname(t.path),i=t.path.replace(n,`.dec`+n);if(!(t.type!==`video`&&t.type!==`audio`)){if(await r.process({input:t.path,output:i,keys:e.keys,type:t.type,cleanup:!0}),!existsSync(i))throw Error(`Decryption failed for file ${t.path}. Unable to find ${i}`);t.path=i}}n.success(`Decrypted`),await t()},muxing=async(e,t)=>{if(e.skipMux)return t();let n=createSpinner$1(`Muxing`),r=new Muxer(new MediaBunnyMuxer);await r.prepare(),await r.process({inputs:e.inputs,output:e.output,trimBegin:e.trimBegin,trimEnd:e.trimEnd,cleanup:!0}),existsSync(e.output)?(n.success(`Muxed`),logger.debug(`Muxed: ${e.output}`)):n.error(`Muxing failed. Output file not found: ${e.output}`)},MIN_CONNECTIONS=1,BASIC_MAX_CONNECTIONS=4,PLUS_MAX_CONNECTIONS=8,PRO_MAX_CONNECTIONS=16,normalizeConnections=(e,t)=>Math.max(MIN_CONNECTIONS,Math.trunc(typeof e==`number`&&Number.isFinite(e)?e:t)),resolveMaxConnectionsByBenefits=e=>e.includes(`highConnectionsCount`)?PRO_MAX_CONNECTIONS:e.includes(`mediumConnectionsCount`)?PLUS_MAX_CONNECTIONS:BASIC_MAX_CONNECTIONS,sanitizeConnectionsByBenefits=({requestedConnections:e,fallbackConnections:t=MIN_CONNECTIONS,grantedBenefits:n})=>{let r=normalizeConnections(e,normalizeConnections(t,MIN_CONNECTIONS)),i=resolveMaxConnectionsByBenefits(n),a=Math.min(r,i);return{connections:a,normalizedRequestedConnections:r,maxAllowedConnections:i,wasClamped:a<r}},verifySettings=async e=>{let{grantedBenefits:t}=await getCustomerState(),n=null,r=t.includes(`parallelDownloads`),i=e.maxActiveDownloads&&e.maxActiveDownloads>1;if(!r&&i&&(n=Error(`PARALLEL_DOWNLOADS_NOT_ALLOWED`),e.maxActiveDownloads=1),e.proxy||e.proxyMeta||e.proxyMedia){let r=t.includes(`proxy`),i=e.proxy||e.proxyMeta||e.proxyMedia;!r&&i&&(n=Error(`PROXY_NOT_ALLOWED`),delete e.proxy,delete e.proxyMeta,delete e.proxyMedia)}if(e.videoContainerFormat&&e.videoContainerFormat!==`mp4`&&(t.includes(`customMuxFormat`)||(n=Error(`CUSTOM_MUX_FORMAT_NOT_ALLOWED`),e.videoContainerFormat=`mp4`)),typeof e.connections==`number`){let{connections:n}=sanitizeConnectionsByBenefits({requestedConnections:e.connections,grantedBenefits:t});e.connections=n}return{error:n,data:e}},getBaseDownloadOptions=e=>({retry:e.get(`retry`),connections:e.get(`connections`),proxy:e.get(`proxy`),proxyMeta:e.get(`proxyMeta`),proxyMedia:e.get(`proxyMedia`),movieTemplate:e.get(`movieFilenameTemplate`),episodeTemplate:e.get(`seriesFilenameTemplate`),hardsub:e.get(`preferHardsub`),format:e.get(`videoContainerFormat`)}),applyBenefitLimitedConnections=({options:e,fallbackConnections:t,grantedBenefits:n})=>{let r=sanitizeConnectionsByBenefits({requestedConnections:e.connections,fallbackConnections:t,grantedBenefits:n});return{options:{...e,connections:r.connections},limit:r}},resolveDownloadJobOptions=({settings:e,requestedOptions:t,filePath:n,grantedBenefits:r})=>{let i=getBaseDownloadOptions(e),{options:a,limit:o}=applyBenefitLimitedConnections({options:{...i,...t},fallbackConnections:i.connections,grantedBenefits:r});return n&&(a.outputPath=n),{baseOptions:i,options:a,limit:o}};var DownloadJobRunner=class{settings;extensionManager;downloadsStorage;callbacks;constructor(e,t,n,r){this.settings=e,this.extensionManager=t,this.downloadsStorage=n,this.callbacks=r}async run(e,t,n){let r;try{e.status=`downloading`,await this.callbacks.persistJob(e),this.callbacks.emitStatusChanged(e);let{options:i,limit:a}=resolveDownloadJobOptions({settings:this.settings,requestedOptions:e.options,filePath:e.filePath,grantedBenefits:n});a.wasClamped&&logger.warn(`Connections value ${a.normalizedRequestedConnections} exceeds tier limit (${a.maxAllowedConnections}). Using ${a.connections}.`);let o=new Downloader({settings:this.settings,downloadsStorage:this.downloadsStorage});this.#e(e,t,o);let s=e.resource;if(!s)throw Error(`Download job is missing a media resource`);let c=e.url?await this.extensionManager.getExtensionForUrl(e.url):null,l=e.url?await materializeMediaResource({resource:s,url:e.url,options:i,extension:c}):{source:s.source};r=l.input,s.source=l.source,await this.callbacks.persistJob(e),c&&await runBeforeDownloadHook(c);let{downloaded:u=[],psshList:d,selectedTracks:f}=await o.start(i,l.source,s.metadata,t.signal,l.input);if(t.signal.aborted)return;if(!u.length)throw errors.emit(`error`,{code:`NO_FILES_DOWNLOADED`}),Error(`No files were downloaded`);u.length&&!e.filePath&&(e.filePath=u[0]?.path),this.callbacks.trackArtifact(e.id,e.filePath);for(let t of u)this.callbacks.trackArtifact(e.id,t.path);if(e.status!==`downloading`)return;e.status=`processing`,this.callbacks.emitStatusChanged(e);let p=await this.#r({job:e,options:i,psshList:d??[],selectedTracks:f,extension:c,resolvedEntry:l.entry,resolvedInput:l.input}),m=new ProcessingChain;m.add(decryption).add(muxing),await m.process({inputs:u,output:e.filePath,keys:p,trimBegin:i.trimBegin,trimEnd:i.trimEnd,binariesDir:this.settings.get(`binariesDir`),skipMux:i.skipMux}),c&&await runAfterDownloadHook(c,{skipMux:i.skipMux}),e.status=`completed`,e.progress=100,this.callbacks.emitStatusChanged(e),this.callbacks.emitJobCompleted(e)}catch(t){if(t instanceof Error&&t.name===`AbortError`){logger.debug(`Download was aborted (paused): ${e.id}`);return}let{normalizedError:n,alreadyHandled:r}=this.#t(e,t);if(this.#n(n),r)return;logger.error(`Download job failed: ${n}`),logErrorDetails(`Download job failure`,n),this.callbacks.emitStatusChanged(e),this.callbacks.emitJobError(e,n)}finally{await r?.dispose?.()}}#e(e,t,n){n.on(`output-path`,t=>{e.filePath=t,this.callbacks.trackArtifact(e.id,t),this.callbacks.persistJob(e),logger.debug(`Job output path persisted early: id=${e.id}, path=${t}`)});let r={};n.on(`progress`,n=>{if(t.signal.aborted||e.status!==`downloading`)return;r[n.trackPath]=n,this.callbacks.trackArtifact(e.id,n.trackPath),this.callbacks.trackArtifact(e.id,n.finalPath);let i=0,a=0;for(let e in r)i+=r[e].current.b,a+=r[e].total.b;let o=a>0?i/a*100:0;e.filePath=n.finalPath,e.progress=o,e.downloadedBytes=i,e.totalBytes=a,n.quality&&(e.quality=n.quality),n.codec&&(e.codec=n.codec),this.callbacks.emitProgress(e),this.callbacks.persistJob(e)}),n.on(`error`,n=>{if(e.status===`paused`||t.signal.aborted)return;let{normalizedError:r,alreadyHandled:i}=this.#t(e,n);i||(logger.error(`Downloader process failed: ${r.message}`),r.stack&&logger.debug(r.stack),this.callbacks.emitStatusChanged(e),this.callbacks.emitJobError(e,r))})}#t(e,t){let n=t instanceof Error?t:Error(String(t)),r=e.status===`error`&&e.error===n.message;return r||(e.status=`error`,e.error=n.message),{normalizedError:n,alreadyHandled:r}}#n(e){(e&&typeof e==`object`&&`code`in e?String(e.code):``)===`DOWNLOAD_DIR_NOT_WRITABLE`&&errors.emit(`error`,{code:`DOWNLOAD_DIR_NOT_WRITABLE`})}async#r({job:e,options:t,psshList:n,selectedTracks:r,extension:i,resolvedEntry:a,resolvedInput:o}){let s=e.resource?.source?.drm??e.resource?.drm;e.resource?.drmPayload&&e.url&&(s=await resolveMediaResourceDrm(e.resource,t,i));let c=await this.#i({extension:i,selectedTracks:r,resolvedEntry:a,resolvedInput:o,options:t}),l=await this.#a({extension:i,selectedTracks:r,resolvedEntry:a,resolvedInput:o,options:t}),u=s&&`server`in s?createLegacyDrmLicenseResolver(s):void 0,d=l.resolveLicense??u,f=l.licenseUrl??(s&&`server`in s?s.server:void 0);return extractDecryptionKeys({keys:[...t.keys??[],...c],drm:s,psshList:n,drmDir:this.settings.get(`drmDir`),resolveLicense:d,licenseUrl:f}).catch(r=>{throw logger.error(`Failed to extract decryption keys for job ${e.id}`),logger.debug(`DRM extraction context: title=${e.title||e.resource?.metadata?.title||`(unknown)`}, drmServer=${s&&`server`in s?s.server:`(none)`}, psshCount=${n.length}, providedKeyCount=${t.keys?.length??0}`),r instanceof Error?(logErrorDetails(`DRM extraction failure`,r),r.message===`Unauthorized`&&errors.emit(`error`,{code:`DRM_UNAUTHORIZED`}),r.message===`Forbidden`&&errors.emit(`error`,{code:`DRM_FORBIDDEN`})):logger.debug(String(r)),r})}async#i({extension:e,selectedTracks:t,resolvedEntry:n,resolvedInput:r,options:i}){if(!e||!n||!r)return[];let a=getCompatibleExtensionExports(e);if(typeof a.drm?.getKeys!=`function`)return[];let o=new Map,s={...n,input:r};for(let e of t){if(!e.inputTrack)continue;let t=(await ensurePreparedTrackSegments(e).catch(()=>[]))[0],n=t?.initSegment?.encryption??t?.encryption,r=[n?.drm?.widevine?`widevine`:null,n?.drm?.playready?`playready`:null,n?.drm?.fairplay?`fairplay`:null].filter(Boolean)[0];if(!r)continue;let c=await a.drm.getKeys({system:r,entry:s,track:e.inputTrack,options:i});for(let[e,t]of c??[])o.set(e,t)}return Array.from(o,([e,t])=>({kid:e,key:t}))}async#a({extension:e,selectedTracks:t,resolvedEntry:n,resolvedInput:r,options:i}){if(!e||!n||!r)return{};let a=getCompatibleExtensionExports(e);if(typeof a.drm?.getLicense!=`function`)return{};let o=await this.#o(t);if(!o.size)return{};let s={...n,input:r};return{licenseUrl:`extension://${e.package.name}/license`,resolveLicense:async({pssh:e,data:t})=>{let n=o.get(e);if(!n)throw Error(`No DRM track binding found for PSSH ${e}`);return await a.drm.getLicense({system:n.system,entry:s,track:n.track,options:i,data:t})}}}async#o(e){let t=new Map;for(let n of e){if(!n.inputTrack)continue;let e=(await ensurePreparedTrackSegments(n).catch(()=>[]))[0],r=e?.initSegment?.encryption??e?.encryption;for(let e of[this.#s(n,`widevine`,r?.drm?.widevine?.pssh),this.#s(n,`playready`,r?.drm?.playready?.pssh),this.#s(n,`fairplay`,r?.drm?.fairplay?.pssh)])!e||t.has(e.pssh)||t.set(e.pssh,e)}return t}#s(e,t,n){if(!e.inputTrack||!n)return null;let r=typeof n==`string`?n:n instanceof Uint8Array||n instanceof ArrayBuffer?Buffer.from(n).toString(`base64`):null;return r?{pssh:r,system:t,track:e.inputTrack}:null}},DownloadManager=class extends EventEmitter$1{settings;extensionManager;downloadsStorage;#e=new DownloadRunState;#t=new Map;#n=null;#r;maxActiveDownloads;constructor(e,t,n){super(),this.settings=e,this.extensionManager=t,this.downloadsStorage=n,this.maxActiveDownloads=e.get(`maxActiveDownloads`),this.#r=new DownloadJobRunner(e,t,n,{persistJob:e=>this.downloadsStorage.update(e),emitStatusChanged:e=>this.emit(`job-status-changed`,e),emitProgress:e=>this.emit(`job-progress`,e),emitJobError:(e,t)=>this.emit(`job-error`,e,t),emitJobCompleted:e=>this.emit(`job-completed`,e),trackArtifact:(e,t)=>this.#m(e,t)}),logger.debug(`Created download manager with max active downloads: ${this.maxActiveDownloads}`)}async initializeFromStorage(){let e=new Set([`downloading`,`queued`,`processing`,`pending`,`analyzing`]);for(let t of this.downloadsStorage.jobs)t.status===`paused`||t.status===`completed`||t.status===`error`||e.has(t.status)&&(t.status!==`queued`&&(t.status=`queued`,t.error=void 0,await this.downloadsStorage.update(t)),this.#d(t));this.#s()}async analyze(e,t={}){if(!e){let e=Error(`Cannot prepare job without a URL.`);throw this.emit(`error`,e),e}let n=await analyzeMediaResource(e,t,await this.extensionManager.getExtensionForUrl(e));if(!n)throw Error(`No media resource found`);return n}async add(e){if(!e.url&&!e.resource)throw Error(`Job must have either a URL or a pre-prepared resource.`);let t={id:crypto.randomUUID(),status:`queued`,progress:0,downloadedBytes:0,createdAt:new Date().toISOString(),...e};logger.debug(`Adding new job: ${t.id}`),logger.debug(`URL: ${t.url}`),await this.downloadsStorage.add(t),this.emit(`job-added`,t),this.#d(t),this.#s()}async pause(e){let t=await this.#i(e,`pause`);t&&logger.debug(`Paused download: ${t.id}`)}async resume(e){let t=this.#e.getActiveRun(e);if(t){t.cancellationReason===`pause`||t.controller.signal.aborted?(this.#e.queueResumeAfterStop(e),logger.debug(`Resume queued until stop completes: ${e}`)):logger.debug(`Resume skipped while job is active: ${e}`);return}let n=this.#y(e);if(!n){logger.warn(`Resume failed: Job ${e} not found`);return}if(n.status===`paused`){await this.#a(n),logger.debug(`Resumed download: ${n.id}`);return}n.status===`error`&&await this.#o(n)&&logger.debug(`Retried download: ${n.id}`)}async remove(e){await this.#i(e,`remove`),await this.downloadsStorage.delete(e),this.#t.delete(e)}async removeWithFile(e){let t=this.#y(e),n=await this.#i(e,`remove-with-file`)||t,r=this.#h(e,n);await this.downloadsStorage.delete(e),await this.#_(r),this.#t.delete(e)}async#i(e,t){let n=this.#f(e),r=this.#e.getActiveRun(e),i=this.#y(e);return r?t===`pause`&&r.job.status!==`downloading`?(logger.debug(`Pause ignored for non-downloading stage: id=${e}, stage=${r.job.status}`),r.job):(r.cancellationReason=t,t===`pause`&&(r.job.status=`paused`,r.job.error=void 0,await this.downloadsStorage.update(r.job),this.emit(`job-status-changed`,r.job)),r.controller.abort(),await r.runPromise,r.job):(t===`pause`&&i&&(n||i.status===`queued`)&&(i.status=`paused`,i.error=void 0,await this.downloadsStorage.update(i),this.emit(`job-status-changed`,i)),i??void 0)}async#a(e){e.status=`queued`,e.error=void 0,await this.downloadsStorage.update(e),this.emit(`job-status-changed`,e),this.#d(e),this.#s()}async#o(e){let t=this.#h(e.id,e);this.#t.delete(e.id),await this.#_(t),e.status=`analyzing`,e.error=void 0,e.progress=0,e.downloadedBytes=0,e.totalBytes=void 0,e.quality=void 0,e.codec=void 0,await this.downloadsStorage.update(e),this.emit(`job-status-changed`,e);try{if(e.url){let t=await this.analyze(e.url,e.options??{}),n=this.#g(e,t);if(!n)throw Error(`No media resource found`);e.resource=n}return await this.#a(e),!0}catch(t){return e.status=`error`,e.error=t instanceof Error?t.message:String(t),await this.downloadsStorage.update(e),this.emit(`job-status-changed`,e),this.emit(`job-error`,e,t),!1}}#s(){for(this.maxActiveDownloads=this.settings.get(`maxActiveDownloads`);this.#e.activeRunCount<this.maxActiveDownloads;){let e=this.#p();if(!e)break;this.#c(e)}this.#e.queuedCount===0&&this.#e.activeRunCount===0&&this.emit(`idle`)}#c(e){if(e.status!==`queued`){logger.warn(`Skipping non-queued job start: ${e.id} (${e.status})`);return}let t=new AbortController,n={controller:t,job:e,runPromise:Promise.resolve()};this.#e.setActiveRun(e.id,n),n.runPromise=this.#u(e,t).catch(e=>{let t=e instanceof Error?e:Error(`Download run failed: ${String(e)}`);logger.error(t.message),this.emit(`error`,t)})}async#l(){if(this.#n)return this.#n;try{let{grantedBenefits:e}=await getCustomerState();this.#n=e}catch(e){let t=e instanceof Error?e.message:String(e);logger.warn(`Failed to resolve customer plan for connection limits. Falling back to Free limits: ${t}`),this.#n=[]}return this.#n}async#u(e,t){this.#e.setActiveJob(e);try{let n=await this.#l();await this.#r.run(e,t,n)}finally{let t=this.#e.getActiveRun(e.id);if(t){let n=t?.cancellationReason,r=n===`pause`&&this.#e.consumeResumeAfterStop(e.id);n===`pause`?(e.status=`paused`,e.error=void 0,await this.downloadsStorage.update(e),this.emit(`job-status-changed`,e)):n!==`remove`&&n!==`remove-with-file`&&await this.downloadsStorage.update(e),this.#e.deleteActiveJob(e.id),this.#e.deleteActiveRun(e.id),r&&e.status===`paused`&&(e.status=`queued`,e.error=void 0,await this.downloadsStorage.update(e),this.emit(`job-status-changed`,e),this.#d(e)),this.#s()}}}#d(e){this.#e.enqueue(e)}#f(e){return this.#e.dequeue(e)}#p(){return this.#e.dequeueNext(e=>this.#y(e))}#m(e,t){if(!t)return;let n=this.#t.get(e);n||(n=new Set,this.#t.set(e,n)),n.add(t)}#h(e,t){let n=new Set;for(let t of this.#t.get(e)??[])n.add(t);t?.filePath&&n.add(t.filePath);let r=new Set;for(let e of n)r.add(e),r.add(`${e}.part.json`),r.add(this.#v(e));return r}#g(e,t){let n=Array.isArray(t)?t:[t];if(n.length<=1)return n[0];let r=e.resource?.metadata,i=e.resource?.source.url,a=r&&`seasonNumber`in r?r.seasonNumber:null,o=r&&`episodeNumber`in r?r.episodeNumber:null,s=r&&`episodeTitle`in r?r.episodeTitle:null;return n.find(e=>{let t=e.metadata;if(r?.id&&t?.id)return r.id===t.id;let n=t&&`seasonNumber`in t?t.seasonNumber:null,c=t&&`episodeNumber`in t?t.episodeNumber:null,l=t&&`episodeTitle`in t?t.episodeTitle:null;return r?.title&&t?.title&&r.title===t.title&&a===n&&o===c&&s===l?!0:!!i&&i===e.source.url})??n[0]}async#_(e){e.size!==0&&await Promise.all([...e].map(e=>rm(e,{recursive:!0,force:!0}).catch(()=>void 0)))}#v(e){let t=resolve(e),n=parse(e).name;return join(APP_TEMP_DIR,`${n}-${createHash(`sha1`).update(t).digest(`hex`).slice(0,10)}`)}#y(e){return this.#e.getActiveJob(e)||this.downloadsStorage.get(e)}onJobAdded(e){return this.on(`job-added`,e),()=>this.removeListener(`job-added`,e)}onJobStatusChanged(e){return this.on(`job-status-changed`,e),()=>this.removeListener(`job-status-changed`,e)}onJobProgress(e){return this.on(`job-progress`,e),()=>this.removeListener(`job-progress`,e)}onJobCompleted(e){return this.on(`job-completed`,e),()=>this.removeListener(`job-completed`,e)}onJobError(e){return this.on(`job-error`,e),()=>this.removeListener(`job-error`,e)}onIdle(e){return this.on(`idle`,e),()=>this.removeListener(`idle`,e)}onError(e){return this.on(`error`,e),()=>this.removeListener(`error`,e)}waitForIdle(){return new Promise(e=>this.onIdle(e))}};const parseSingleEpisode=e=>{if(!e)return null;let[t,n]=e.toLowerCase().split(`e`);return[parseInt(t.trim().toLowerCase().replace(`s`,``)),parseFloat(n?.trim())]},parseEpisodesBySeasons=e=>{let t=0,n=9999,r=[],i=e.split(`,`);for(let e of i){let[t,i]=e.split(`-`),a=parseSingleEpisode(t),o=parseSingleEpisode(i);if(a&&!i){let[e,t]=a;if(isNaN(t))for(let t=0;t<=n;t++)r.push([e,t]);else r.push([e,t])}else if(a&&o){let[e,t]=a,[i,s]=o;for(let a=e;a<=i;a++){let e=t||0,o=a===i?s:n;for(let t=e;t<=o;t++)r.push([a,t])}}}let a=new Map;for(let[e,t]of r)a.has(e)||a.set(e,new Set),a.get(e)?.add(t);return a},parseHeaders=e=>{if(!e)return{};let t={},n=typeof e==`string`?e.split(`|`):e;for(let e of n){let[n,...r]=e.split(`:`),i=r.join(`:`);if(!i)throw Error(`Can't parse header: ${e}`);t[n.trim()]=i.trim()}return t},validateUrl=async e=>{let t=!1,n=e;do try{n?t=!!new URL(n):n=(await prompt.ask({fields:{url:{label:`URL`}}})).url}catch{n=(await prompt.ask({fields:{url:{label:`URL`}}})).url}while(!t);return n!==e&&logger.debug(`URL: ${n}`),n},compareJobsByCreatedAtDesc=(e,t)=>new Date(t.createdAt??0).getTime()-new Date(e.createdAt??0).getTime();var DownloadsStorage=class{filePath;jobs=[];#e=Promise.resolve();constructor(e){this.filePath=e}parse(e){let t=JSON.parse(e),n=Object.entries(t);return new Map(n)}async load(){await this.#e;try{let e=await readFile(this.filePath,{encoding:`utf8`});this.jobs=JSON.parse(e).toSorted(compareJobsByCreatedAtDesc)}catch{this.jobs=[]}return this}get size(){return this.jobs.length}async add(e){await this.#t(async()=>{this.jobs.unshift(e),await this.save()})}async update(e){await this.#t(async()=>{let t=this.jobs.findIndex(t=>t.id===e.id);t!==-1&&(this.jobs[t]=e,await this.save())})}getByFilePath(e){return this.jobs.find(t=>t.filePath===e)}get(e){return this.jobs.find(t=>t.id===e)}has(e){return this.jobs.some(t=>t.id===e)}async delete(e){await this.#t(async()=>{let t=this.jobs.findIndex(t=>t.id===e);t!==-1&&(this.jobs.splice(t,1),await this.save())})}async deleteWithFile(e){let t=await this.#t(async()=>{let t=this.jobs.findIndex(t=>t.id===e);if(t===-1)return null;let[n]=this.jobs.splice(t,1);return await this.save(),n??null});t?.filePath&&await rm(t.filePath,{recursive:!0,force:!0})}async clear(){await this.#t(async()=>{this.jobs=[],await this.save()})}async save(){return writeFile(this.filePath,JSON.stringify(this.jobs,null,2))}#t(e){let t=this.#e.then(e,e);return this.#e=t.then(()=>void 0,()=>void 0),t}};const downloadCommand={command:[`download [urls..]`,`$0 [urls..]`],aliases:[`dl`],describe:`Download media content by URL`,builder:e=>e.option(`quality`,{alias:`q`,describe:`Video resolution quality - 2160p/1080p/720p/..., defaults to highest available`,type:`string`}).option(`video-codec`,{alias:`v`,describe:`Video codec - avc | hevc | vp8 | vp9 | av1 | vc1, defaults to any`,type:`string`}).option(`video-range`,{alias:`r`,describe:`Video color range - sdr | hlg | hdr10 | hdr10+ | dv, defaults to sdr`,type:`string`}).option(`video-bitrate`,{describe:`Video bitrate (in kbps), defaults to highest available`,type:`number`}).option(`video-dimension`,{describe:`Video dimension - 2D/3D (HSBS/HOU/FSBS/OU), defaults to 2D`,default:`2D`,type:`string`}).option(`audio-codec`,{alias:`a`,describe:`Audio codec - aac | opus | mp3 | vorbis | flac | alac | ac3 | eac3 | dts, defaults to any`,type:`string`}).option(`audio-channels`,{alias:`c`,describe:`Audio channels, matches sub-channel layouts like 5.1 with 6.0`,type:`number`}).option(`audio-bitrate`,{describe:`Audio bitrate (in kbps), defaults to highest available`,type:`number`}).option(`subs-format`,{alias:`s`,describe:`Subtitle format - SRT/SSA/ASS/TTML/VTT/STPP/WVTT`,type:`string`}).option(`subs-lang`,{alias:`S`,describe:`Subtitles language(s)`,type:`string`}).option(`lang`,{alias:`l`,describe:`Video/audio language(s)`,type:`string`}).option(`skip-video`,{describe:`do not download video`,type:`boolean`}).option(`skip-audio`,{describe:`do not download audio`,type:`boolean`}).option(`skip-subtitles`,{describe:`do not download subtitles`,type:`boolean`}).option(`skip-mux`,{describe:`Disable merge audio/video/subs into single file`,type:`boolean`}).option(`select`,{describe:`Select audio/video/subtitle streams from manifest manually`,type:`boolean`}).option(`episodes`,{alias:`e`,describe:"Seasons/episodes to download, e.g. `S01E01`, `S01-S05,S07`, `S01E01-S02E03`, `S02-S02E03`, etc.",type:`string`,coerce:e=>parseEpisodesBySeasons(e)}).option(`retry`,{alias:`R`,describe:`Number of retries in case of unsuccessful request`,type:`number`,default:defaultSettings.retry}).option(`connections`,{alias:`C`,describe:`Number of parallel http connections per download`,type:`number`,default:defaultSettings.connections}).option(`proxy`,{describe:`Use specified HTTP/HTTPS proxy for all requests`,type:`string`}).option(`proxy-meta`,{describe:`Use specified HTTP/HTTPS proxy for metadata fetching`,type:`string`}).option(`proxy-media`,{describe:`Use specified HTTP/HTTPS proxy for media downloading`,type:`string`}).option(`movie-template`,{describe:`Movie filename template`,type:`string`,default:defaultSettings.movieFilenameTemplate}).option(`episode-template`,{describe:`Episode filename template`,type:`string`,default:defaultSettings.seriesFilenameTemplate}).option(`hardsub`,{describe:`Download hardsubbed video if available`,type:`boolean`}).option(`trim-begin`,{describe:`Trim video at the beginning; example: 00:00:06`,type:`string`}).option(`trim-end`,{describe:`Trim video at the end; example: 00:01:30`,type:`string`}).option(`key`,{describe:`Set content decryption key (if manifest URL as main URL passed), format: KID:KEY`,type:`array`,string:!0,coerce:e=>e.map(e=>{let[t,n]=e.split(`:`);return{kid:t,key:n}})}).option(`header`,{alias:`H`,describe:`Pass custom header(s) for manifest request`,type:`array`,string:!0,coerce:e=>parseHeaders(e)}).option(`format`,{alias:`f`,describe:`Video container format - mp4 | mkv (Pro only)`,type:`string`,choices:[`mp4`,`mkv`]}),handler:async e=>{let t=e;t.videoQuality=e.quality;let n=new SettingsStore(SETTINGS_PATH);if(t.format){let{error:e}=await verifySettings({videoContainerFormat:t.format});e&&(logger.error(`Invalid format: ${e.message}`),logger.info(`Learn more: https://azot.mov/pricing`),process.exit(1))}let r=[];try{({grantedBenefits:r}=await getCustomerState())}catch(e){let t=e instanceof Error?e.message:String(e);logger.warn(`Failed to resolve customer plan for connection limits. Falling back to Free limits: ${t}`)}let{options:i,limit:{normalizedRequestedConnections:a,maxAllowedConnections:o,wasClamped:s,connections:c}}=applyBenefitLimitedConnections({options:t,fallbackConnections:n.get(`connections`),grantedBenefits:r});Object.assign(t,i),s&&logger.warn(`Connections value ${a} exceeds tier limit (${o}). Using ${c}.`);let l=new ExtensionManager({dirs:[EXTENSIONS_SOURCES_DIR],indexPath:EXTENSIONS_INDEX_PATH});await l.init(),await l.loadCoreExtensions(),await l.discover();let u=new DownloadsStorage(join(APP_CONFIG_DIR,`downloads.json`));await u.load();let d=new DownloadManager(n,l,u);loadAuth().then(e=>Object.assign(authState,e)),d.onJobCompleted(e=>{logger.debug(`Job completed: ${e.id}`)}),d.onJobError(e=>{logger.debug(`Job failed: ${e.error}`)}),d.onError(e=>{logger.error(`Download manager error: ${e.message}`)});let f=(Array.isArray(e.urls)?e.urls:[]).filter(e=>e!==process.argv[1]),p=f.length>0?f:[``];for(let e of p){let n=await validateUrl(e),r=await d.analyze(n,t),i=Array.isArray(r)?r:[r];for(let e of i)d.add({url:n,options:t,resource:e})}await d.waitForIdle()}},CLIENT_ID=`azot-desktop`,login=async()=>{if(await loadAuth()&&!await confirm({message:`You're already logged in. Do you want to log in again?`,default:!1})){logger.debug(`Login cancelled`);return}let e=createSpinner(`Requesting device authorization...`);e.start();try{let t=getAuthClient(),{data:n,error:r}=await t.device.code({client_id:CLIENT_ID,scope:`openid profile email`});if(e.stop(),r||!n)logger.error(`Failed to request device authorization: ${r?.error_description||r?.statusText||`Unknown error`}`),logger.debug(r);else{let{device_code:e,user_code:r,verification_uri:i,verification_uri_complete:a,interval:o=5,expires_in:s}=n;console.log(``),console.log(colors.cyan(`📱 Device Authorization Required`)),console.log(``),console.log(`Please visit: ${colors.underline(colors.blue(i))}`),console.log(`Enter code: ${colors.bold(colors.green(r))}`),console.log(``),await confirm({message:`Open browser automatically?`,default:!0})&&await open$1(a||i),console.log(colors.gray(`Waiting for authorization (expires in ${Math.floor(s/60)} minutes)...`));let c=await pollForToken(t,e,CLIENT_ID,o);if(c){await saveAuth(c);let{data:e}=await t.getSession({fetchOptions:{headers:{Authorization:`Bearer ${c.access_token}`}}});console.log(colors.green(`✅ Login successful! Logged in as ${e?.user?.name||e?.user?.email||`User`}`))}}}catch(t){e.stop(),logger.error(`Login failed: ${t instanceof Error?t.message:`Unknown error`}`)}};async function pollForToken(e,t,n,r){let i=r,a=createSpinner(``,{color:`cyan`}),o=0;return new Promise(r=>{let s=async()=>{o=(o+1)%4,a.update(colors.gray(`Polling for authorization${`.`.repeat(o)}${` `.repeat(3-o)}`)),a.isSpinning()||a.start();try{let{data:o,error:s}=await e.device.token({grant_type:`urn:ietf:params:oauth:grant-type:device_code`,device_code:t,client_id:n,fetchOptions:{headers:{"user-agent":`Azot Desktop`}}});if(o?.access_token){a.stop(),r(o);return}else if(s)switch(s.error){case`authorization_pending`:break;case`slow_down`:i+=5,a.update(colors.yellow(`Slowing down polling to ${i}s`));break;case`access_denied`:a.stop(),logger.error(`Access was denied by the user`);break;case`expired_token`:a.stop(),logger.error(`The device code has expired. Please try again.`);break;default:a.stop(),logger.error(`Error: ${s.error_description}`)}}catch(e){a.stop(),logger.error(`Network error: ${e instanceof Error?e.message:`Unknown error`}`);return}setTimeout(s,i*1e3)};setTimeout(s,i*1e3)})}const logout=async()=>{logger.info(`Logging out...`);let e=await getAuthClient().signOut();e.error?logger.error(`Logout failed: ${e.error.message}`):(logger.info(`Logout successful`),await saveAuth({}))},loginCommand={command:`login [extension]`,describe:`Login to extension (or if no extension is specified, login to Azot account)`,builder:e=>e.option(`username`,{alias:`u`,describe:`Username`,type:`string`}).option(`password`,{alias:`p`,describe:`Password`,type:`string`}).option(`otp`,{alias:`o`,describe:`One-time password`,type:`string`}),handler:async e=>{let t=e.extension,n={username:e.username,password:e.password,otp:e.otp};if(t){let e=new ExtensionManager({dirs:[EXTENSIONS_SOURCES_DIR],indexPath:EXTENSIONS_INDEX_PATH});await e.init(),await e.discover(),await e.login(t,n)}else login()}},listCommand={command:`list`,aliases:[`ls`],describe:`Lists information about installed/active extensions`,handler:async()=>{let e=await new ExtensionManager({indexPath:EXTENSIONS_INDEX_PATH}).init();if(!e.infoList.length)return console.log(`No added extensions found`);for(let t of e.infoList){let e=`${colors.bold(colors.blue(t.name.padEnd(15)))}`,n=colors.green(t.package?.version);console.log(e+n)}console.log(`
|
|
19
|
+
`+colors.dim(`Location: ${EXTENSIONS_DIR}`))}},rtf=new Intl.RelativeTimeFormat(`en`,{style:`long`}),getTimeAgo=e=>{let t=Math.floor(e/24),n=Math.abs(t);return n>365?rtf.format(Math.floor(t/365),`year`):n>30?rtf.format(Math.floor(t/30),`month`):n>7?rtf.format(Math.floor(t/7),`week`):n>1?rtf.format(Math.floor(t),`day`):rtf.format(Math.floor(e),`hour`)},row=(e,t,n)=>{let r=`${colors.bold(colors.blue(e.padEnd(15)))}`,i=colors.green(t.padEnd(10)),a=colors.dim(n);console.log(r+i+a)},listRemoteCommand={command:`list-remote`,aliases:[`ls-remote`],describe:`List all available extensions`,handler:async()=>{loadAuth().then(e=>Object.assign(authState,e));let e=await ExtensionManager.getAvailableExtensions(),t=Date.now();for(let n of e){let e=n.releases.toSorted((e,t)=>new Date(t.createdAt).getTime()-new Date(e.createdAt).getTime()).at(0);if(!e)continue;let r=e.createdAt,i=new Date(r).getTime()-t,a=getTimeAgo(Math.floor(i/36e5));row(n.name,e.version,a)}}},logoutCommand={command:`logout [extensions..]`,describe:`Logout from extension (or if no extension is specified, logout from Azot account)`,handler:async e=>{let t=e.extensions;if(t?.length){let e=new ExtensionManager({dirs:[EXTENSIONS_SOURCES_DIR],indexPath:EXTENSIONS_INDEX_PATH});await e.init(),await e.discover(),await e.logout(t)}else logout()}},settingsCommand={command:`settings [key] [value]`,describe:`CLI access to get/set configuration settings`,handler:async e=>{let t=new SettingsStore(SETTINGS_PATH),n=e.key,r=e.value;if(n&&r){t.set({[n]:r}),console.log(`Setting ${colors.bold(colors.blue(n))} set to ${colors.green(r)}`);return}else if(n){let e=t.get(n);console.log(e?`Setting ${colors.bold(colors.blue(n))} is ${colors.green(e)}`:`Setting ${colors.bold(colors.blue(n))} is not found`);return}let i=t.getAll();for(let[e,t]of Object.entries(i)){let n=`${e}`.padEnd(25),r=String(t).replaceAll(`{`,`{{`).replaceAll(`}`,`}}`);console.log(`${colors.bold(colors.blue(n))} ${r}`)}console.log(`
|
|
20
|
+
`+colors.dim(`Location: ${SETTINGS_PATH}`))}},uninstallCommand={command:`uninstall <extensions..>`,aliases:[`rm`],describe:`Uninstall an extension`,handler:async e=>{let t=e.extensions,n=new ExtensionManager({dirs:[EXTENSIONS_SOURCES_DIR],indexPath:EXTENSIONS_INDEX_PATH});await n.init(),await n.uninstall(t)}},installCommand={command:`install <extensions..>`,aliases:[`i`],describe:`Install extension`,handler:async e=>{let t=e.extensions,n=new ExtensionManager({dirs:[EXTENSIONS_SOURCES_DIR],indexPath:EXTENSIONS_INDEX_PATH});await n.init(),await n.install(t)}},logsCommand={command:`logs`,describe:`Show logs list`,handler:async()=>{let e=APP_LOG_DIR,t=await readdir(e).then(e=>e.filter(e=>e.endsWith(`.log`))),n=t=>join(e,t);t.length||logger.info(`No logs found`);for(let e of t)logger.info(n(e))}};logger.debug(fullVersion);const cliArgs=getCliArgs(process.argv);logger.debug(`${APP_NAME} ${cliArgs.join(` `)}`),errors.on(`error`,e=>{let t=normalizeError(e);logger.error(t.message)});const description=`${colors.magenta(colors.bold(APP_TITLE))} is ${package_default.description.toLocaleLowerCase()}`,usage=colors.bold(`Usage: $0 <command> ${colors.cyan(`[...options]`)}`);yargs(cliArgs).scriptName(APP_NAME).usage(`${description}\n\n${usage}`).command(downloadCommand).command(listCommand).command(listRemoteCommand).command(installCommand).command(uninstallCommand).command(loginCommand).command(logoutCommand).command(settingsCommand).command(logsCommand).option(`debug`,{describe:`Debug mode`,type:`boolean`}).wrap(yargs().terminalWidth()).help().alias(`h`,`help`).showHelpOnFail(!1).version(shortVersion).updateStrings({"Commands:":colors.bold(`Commands:`),"Options:":colors.bold(`Options:`),default:colors.dim(`default`),aliases:colors.dim(`aliases`),boolean:colors.dim(`boolean`),string:colors.dim(`string`),number:colors.dim(`number`)}).epilogue(`Learn more about Azot: ${colors.magenta(`https://azot.mov`)}.`).parse(),clearOutdatedLogs(APP_LOG_DIR),process.on(`uncaughtException`,e=>{logger.fatal(`Unexpected error: ${e.message}`),e.stack&&logger.debug(e.stack),rmSync(APP_TEMP_DIR,{recursive:!0,force:!0}),process.exit(1)});export{};
|
package/dist/azt.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{createRequire as e}from"node:module";import{parseArgs as t}from"node:util";import{readFileSync as n,readdirSync as r,rmSync as i,writeFileSync as a}from"node:fs";import o from"node:path";import{build as s}from"tsdown";import{createCipheriv as c,createDecipheriv as l,privateDecrypt as u,publicEncrypt as d,randomBytes as f}from"node:crypto";import p from"javascript-obfuscator";var m=e(import.meta.url);const h=Buffer.from(`STX`,`utf8`),g=(e,t,r)=>{let i=f(32),o=f(16),s=n(t),l=c(`aes-256-cbc`,i,o),u=Buffer.concat([l.update(s),l.final()]),p=d(e,i),m=Buffer.concat([h,o,p,u]);return r&&a(r,m),{aesIv:o,encryptedAesKey:p,encryptedCode:u}},_=(e,t,r)=>{let i=n(t),o=h.byteLength;if(!i.subarray(0,o).equals(h))throw Error(`Incorrect file format`);let s=o,c=s+16,d=c+512,f=i.subarray(s,c),p=i.subarray(c,d),m=i.subarray(d),g=l(`aes-256-cbc`,u(e,p),f),_=Buffer.concat([g.update(m),g.final()]).toString(`utf8`);return r&&a(r,_),_},v=(e,t)=>{let r=n(e,`utf8`);console.log(`Obfuscating ${e}...`),console.time(`Obfuscation complete in`);let i=p.obfuscate(r,{target:`node`,compact:!0,controlFlowFlattening:!0,controlFlowFlatteningThreshold:1,deadCodeInjection:!0,debugProtection:!0,identifierNamesGenerator:`hexadecimal`,numbersToExpressions:!0,selfDefending:!0,simplify:!0,splitStrings:!0,splitStringsChunkLength:5,stringArray:!0,stringArrayCallsTransform:!0,stringArrayCallsTransformThreshold:1,stringArrayEncoding:[`base64`],stringArrayIndexShift:!0,stringArrayRotate:!0,stringArrayShuffle:!0,stringArrayWrappersCount:2,stringArrayWrappersChainedCalls:!0,stringArrayWrappersType:`function`,stringArrayThreshold:1,transformObjectKeys:!0,unicodeEscapeSequence:!1});console.timeEnd(`Obfuscation complete in`);let o=i.getObfuscatedCode();return a(t||e,o),o},y=t({allowPositionals:!0,allowNegative:!0,options:{input:{type:`string`,short:`i`,description:`Path to the input file`},key:{type:`string`,short:`k`,description:`Path to the key file (public for encrypt, private for decrypt)`},output:{type:`string`,short:`o`,description:`Path to the output file`},format:{type:`string`,short:`f`,description:`Output format`},watch:{type:`boolean`,description:`Watch file changes`},minify:{type:`boolean`,description:`Minify output`},obfuscate:{type:`boolean`,description:`Obfuscate output`},encrypt:{type:`boolean`,description:`Encrypt extension source code (.js will be packed inside .stx)`},decrypt:{type:`boolean`,description:`Decrypt encrypted extension`}}}),b=()=>{let e=[process.cwd(),o.join(process.cwd(),`src`),o.join(process.cwd(),`lib`)];for(let t of e){let e=r(t).filter(e=>!e.endsWith(`.d.ts`)).filter(e=>new Set([`.ts`,`.js`,`.cjs`,`.mjs`]).has(o.extname(e))),n=e=>o.basename(e,o.extname(e)),i=e.find(e=>n(e)===`index`)||e.find(e=>n(e)===`main`)||e[0];if(i)return o.join(t,i)}},x=e=>e===`esm`?`mjs`:`cjs`,S=(e,t)=>o.join(process.cwd(),`dist`,`${o.parse(e).name}.${x(t)}`),C=(e,t)=>{let{dir:n,name:r}=o.parse(e);return o.join(n,`${r}-${t}.azt`)};(async()=>{let[e,...t]=y.positionals,r=y.values.input||t[0]||b();if(!r)throw Error(`No entry file found`);if(e===`build`||e===`pack`){let t=typeof y.values.minify==`boolean`?y.values.minify:!0,a=y.values.format??`cjs`;await s({entry:r.replace(process.cwd(),`.`).replace(o.sep,`/`),format:a,platform:`node`,target:`node24`,watch:y.values.watch,minify:t,inlineOnly:!1}),y.values.input=S(r,a);let c=m(o.join(process.cwd(),`package.json`));if((typeof y.values.obfuscate==`boolean`?y.values.obfuscate:c.private)&&v(y.values.input,y.values.input),y.values.encrypt||e===`pack`){let e=n(y.values.key||o.join(__dirname,`../public.pem`),`utf8`),t=y.values.output||C(y.values.input,c.version);g(e,y.values.input,t),i(y.values.input)}}else if(e===`decrypt`)_(n(y.values.key||o.join(__dirname,`../private.pem`),`utf8`),r,y.values.output||r.replace(/\.stx$/,`.js`)),y.values.input&&i(y.values.input);else if(e===`dev`||e===`develop`||!e){let e=typeof y.values.minify==`boolean`?y.values.minify:!1;await s({entry:r,format:y.values.format??`cjs`,platform:`node`,target:`node22`,watch:!0,minify:e})}})();export{};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
//#region src/extensions-api/main.ts
|
|
2
|
+
const runtimeContext = Azot ?? Streamyx ?? {};
|
|
3
|
+
if (runtimeContext) {
|
|
4
|
+
if (!runtimeContext.defineExtension) runtimeContext.defineExtension = (extension) => extension;
|
|
5
|
+
}
|
|
6
|
+
const defineExtension = runtimeContext.defineExtension;
|
|
7
|
+
runtimeContext.version;
|
|
8
|
+
runtimeContext.sleep;
|
|
9
|
+
runtimeContext.input;
|
|
10
|
+
runtimeContext.prompt;
|
|
11
|
+
runtimeContext.notify;
|
|
12
|
+
runtimeContext.confirm;
|
|
13
|
+
runtimeContext.ask;
|
|
14
|
+
runtimeContext.utils;
|
|
15
|
+
const Input = runtimeContext.Input;
|
|
16
|
+
const UrlSource = runtimeContext.UrlSource;
|
|
17
|
+
runtimeContext.ALL_FORMATS;
|
|
18
|
+
runtimeContext.DASH;
|
|
19
|
+
runtimeContext.DASH_FORMATS;
|
|
20
|
+
runtimeContext.HLS;
|
|
21
|
+
runtimeContext.HLS_FORMATS;
|
|
22
|
+
runtimeContext.MP4;
|
|
23
|
+
runtimeContext.MP3;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/builtins/instagram/instagram.ts
|
|
26
|
+
const INSTAGRAM_HEADERS = { Referer: "https://www.instagram.com/" };
|
|
27
|
+
const INSTAGRAM_API_HEADERS = {
|
|
28
|
+
"X-IG-App-ID": "936619743392459",
|
|
29
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
|
|
30
|
+
};
|
|
31
|
+
const getPostViaGraphQL = async (shortcode) => {
|
|
32
|
+
const params = new URLSearchParams({
|
|
33
|
+
doc_id: "8845758582119845",
|
|
34
|
+
variables: JSON.stringify({
|
|
35
|
+
shortcode,
|
|
36
|
+
child_comment_count: 3,
|
|
37
|
+
fetch_comment_count: 40,
|
|
38
|
+
parent_comment_count: 24,
|
|
39
|
+
has_threaded_comments: true
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetch(`https://www.instagram.com/graphql/query/?${params.toString()}`, { headers: INSTAGRAM_API_HEADERS });
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
console.error(`GraphQL request failed with status: ${response.status}`);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return (await response.json()).data?.xdt_shortcode_media ?? null;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("Error during GraphQL fetch:", error);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const getPostViaEmbedPage = async (shortcode) => {
|
|
55
|
+
try {
|
|
56
|
+
const response = await fetch(`https://www.instagram.com/p/${shortcode}/embed/captioned/`, { headers: { "User-Agent": INSTAGRAM_API_HEADERS["User-Agent"] } });
|
|
57
|
+
if (!response.ok) return null;
|
|
58
|
+
const dataMatch = (await response.text()).match(/window\.__additionalDataLoaded\('extra',\s*({.+?})\s*\);/);
|
|
59
|
+
if (!dataMatch?.[1]) return null;
|
|
60
|
+
return JSON.parse(dataMatch[1]).shortcode_media ?? null;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error("Error during embed page fetch:", error);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const processPostData = (postData) => {
|
|
67
|
+
const title = postData.edge_media_to_caption?.edges?.[0]?.node?.text || "Instagram Post";
|
|
68
|
+
const entries = [];
|
|
69
|
+
const pushEntry = (item) => {
|
|
70
|
+
if (!item) return;
|
|
71
|
+
const mediaUrl = item.is_video && item.video_url ? item.video_url : item.display_url;
|
|
72
|
+
if (!mediaUrl) return;
|
|
73
|
+
entries.push({
|
|
74
|
+
title,
|
|
75
|
+
input: new Input({ source: new UrlSource(mediaUrl, { requestInit: { headers: INSTAGRAM_HEADERS } }) })
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const carouselItems = postData.edge_sidecar_to_children?.edges;
|
|
79
|
+
if (carouselItems?.length) for (const edge of carouselItems) pushEntry(edge.node);
|
|
80
|
+
else pushEntry(postData);
|
|
81
|
+
return entries;
|
|
82
|
+
};
|
|
83
|
+
const isInstagramHostname = (url) => {
|
|
84
|
+
try {
|
|
85
|
+
const hostname = new URL(url).hostname.toLowerCase();
|
|
86
|
+
return hostname === "instagram.com" || hostname.endsWith(".instagram.com");
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
var instagram_default = defineExtension({
|
|
92
|
+
canHandle: isInstagramHostname,
|
|
93
|
+
async resolveEntries(url) {
|
|
94
|
+
const shortcode = new URLPattern({
|
|
95
|
+
pathname: "/:type(p|reel|tv|reels|videos)/:shortcode{/*}?",
|
|
96
|
+
baseURL: new URL(url).origin
|
|
97
|
+
}).exec(url)?.pathname.groups.shortcode;
|
|
98
|
+
if (!shortcode) {
|
|
99
|
+
console.error("Could not extract a valid Instagram shortcode from the URL");
|
|
100
|
+
console.debug(url);
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
let postData = await getPostViaGraphQL(shortcode);
|
|
105
|
+
if (!postData) {
|
|
106
|
+
console.warn("GraphQL method failed. Falling back to embed page scraping.");
|
|
107
|
+
postData = await getPostViaEmbedPage(shortcode);
|
|
108
|
+
}
|
|
109
|
+
if (!postData) {
|
|
110
|
+
console.error("All extraction methods failed. The post may be private, deleted, or require a login.");
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return processPostData(postData);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error("A critical error occurred while fetching Instagram metadata:", error);
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
//#endregion
|
|
121
|
+
module.exports = instagram_default;
|