@yt-kit/core 1.0.5 → 1.1.0

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/dist/index.d.ts CHANGED
@@ -9,33 +9,39 @@ export interface ValueToCache {
9
9
  content: string;
10
10
  }
11
11
  export interface CacheConfig {
12
- method: CacheMethod;
12
+ method?: CacheMethod;
13
13
  cacheLocation?: string;
14
+ ttlInMs?: number;
15
+ ignoreTTL?: "never" | "next" | "always";
14
16
  }
15
17
  export type CacheMethod = "memory" | "disk" | "hybrid";
16
18
  export declare class CacheManager {
17
19
  private store;
18
- private defaultTtl;
20
+ private ttl;
19
21
  get(key: CacheKey): Promise<CachedData | null>;
20
22
  set(key: CacheKey, value: ValueToCache): Promise<void>;
21
- delete(key: string): void;
23
+ delete(key: CacheKey): Promise<void>;
22
24
  }
23
25
  export declare const cache: CacheManager;
26
+ export type CommandKey = "yt-dlp";
27
+ export type Commands = Record<CommandKey, string>;
24
28
  export type ConfigKey = keyof ConfigMap;
25
29
  export type ConfigValue<T extends ConfigKey> = ConfigMap[T];
26
30
  export type ConfigMap = {
27
31
  "cache": CacheConfig;
32
+ "commands": Commands;
28
33
  };
29
34
  export declare class Configuration {
30
35
  private store;
31
36
  get<K extends ConfigKey>(key: K): ConfigValue<K> | null;
32
- set<K extends ConfigKey>(key: K, value: ConfigValue<K>): void;
37
+ add<K extends ConfigKey>(key: K, value: ConfigValue<K>): void;
38
+ replace<K extends ConfigKey>(key: K, value: ConfigValue<K>): void;
33
39
  delete<K extends ConfigKey>(key: K): void;
34
40
  }
35
41
  export declare const config: Configuration;
36
42
  export interface DownloadOptions {
37
- id: string;
38
- outputPath?: string;
43
+ formatId: string | number;
44
+ outputDir?: string;
39
45
  filename?: string;
40
46
  }
41
47
  export interface DownloadResult {
@@ -55,9 +61,6 @@ export declare const STANDARD_RESOLUTIONS: readonly [
55
61
  1440,
56
62
  2160
57
63
  ];
58
- declare const COMMANDS: {
59
- readonly "yt-dlp": "yt-dlp";
60
- };
61
64
  declare const PATTERNS: {
62
65
  readonly ID: "%(id)s";
63
66
  readonly YT_ID: "%(ytId)s";
@@ -66,6 +69,10 @@ export type Height = typeof STANDARD_RESOLUTIONS[number];
66
69
  export type Resolution = `${Height}p`;
67
70
  export type FormatsToFind = Resolution | "best-video" | "worst-video" | "best-audio" | "worst-audio";
68
71
  export type MediaType = "audio" | "video";
72
+ export declare function findFormatId(ytId: string, formatToFind: FormatsToFind): Promise<{
73
+ foundSpecific: string | boolean;
74
+ formatId: string | undefined;
75
+ }>;
69
76
  export type YtDlpFormat = {
70
77
  format_id: string;
71
78
  format_note: string;
@@ -127,14 +134,9 @@ export type AcceptLanguage = "en-us,enq=0.5";
127
134
  export type SECFetchMode = "navigate";
128
135
  export type Protocol = "mhtml" | "https";
129
136
  export type VideoEXT = "none" | "mp4" | "webm";
130
- export declare function findFormatId(ytId: string, formatToFind: FormatsToFind): Promise<{
131
- foundSpecific: string | boolean;
132
- formatId: string | undefined;
133
- desiredFormat: YtDlpFormat | undefined;
134
- }>;
137
+ export declare function getAllFormats(ytId: string): Promise<YtDlpFormat[]>;
135
138
  export declare function getFromDisk(key: CacheKey): Promise<CachedData | undefined>;
136
139
  export declare function saveInDisk(key: CacheKey, value: CachedData): Promise<void>;
137
- export declare function formYoutubeUrl(ytId: string): string;
138
140
  export interface CompareOptions {
139
141
  type: MediaType;
140
142
  compareResolution: boolean;
@@ -151,7 +153,5 @@ export type Pattern = typeof PATTERNS[keyof typeof PATTERNS];
151
153
  export type PatternData = string;
152
154
  export declare function expandPattern(pattern: string, patternMap: Map<Pattern, PatternData>): string;
153
155
  export declare function resolvePath(input: string): string;
154
- export type CommandKey = keyof typeof COMMANDS;
155
- export type Command = typeof COMMANDS[keyof typeof COMMANDS];
156
156
 
157
157
  export {};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- class F{store=new Map;get(t){let e=this.store.get(t);if(!e)return null;return e}set(t,e){this.store.set(t,e)}delete(t){this.store.delete(t)}}var l=new F;var N={day:86400000,hrs:3600000,min:60000,sec:1000,nano:0.001};function O(t,e){if(!N[e])return t;return t*N[e]}function K(t){if(typeof t!=="string")return!1;return!0}import{readFile as J}from"node:fs/promises";import{homedir as B}from"node:os";import{join as $,resolve as z,sep as W}from"node:path";function d(t){if(!t)return"";if(t.match(/~[^/]+/))throw Error("Ruta inválida. Ten más cuidado usando '~'");if(t.includes(" "))throw Error("La ruta no puede tener espacios");if(t==="."||t==="..")throw Error(`'${t}' No es una ruta válida. Tal vez faltó agregar '/'?`);let e=t;if(t.startsWith("~"))e=$(B(),W,t.slice(1));return z(e)}import{join as Q}from"node:path";async function T(t){let e=l.get("cache")?.cacheLocation;if(!e)return;let r=d(e);if(t.includes("formats:")){let s=t.split(":")[1];if(!s)throw Error(`Falta especificar ytId en ${t}`);r=Q(d(e),"formats",s)}let o;try{o=await J(r,"utf8")}catch{}if(o)try{o=JSON.parse(o)}catch(s){console.error(`Error convirtiendo la caché de ${t} a JSON:`,s)}if(!o)return;return o}import{rm as G}from"node:fs/promises";async function V(t){let e=l.get("cache")?.cacheLocation;if(!e)return;let r=d(e);try{await G(r)}catch(o){console.error(`Error borrando la caché de ${t} en el almacenamiento:`,o)}}import{mkdir as H,writeFile as L}from"node:fs/promises";import{dirname as X,join as Z}from"node:path";async function h(t,e){let r=l.get("cache")?.cacheLocation;if(!r)return;let o=d(r);if(t.includes("formats:")){let n=t.split(":")[1];if(!n)throw Error(`Falta especificar ytId en ${t}`);o=Z(d(r),"formats",n)}let s=X(o);try{H(s,{recursive:!0}),await L(o,JSON.stringify(e),"utf8")}catch(n){console.error(`Error cacheando ${t} en el almacenamiento:`,n)}}class E{store=new Map;defaultTtl=O(10,"day");async get(t){if(!K(t))throw Error("CacheKey inválida",{cause:t});let e=l.get("cache")?.method??"memory",r=null;if(e==="memory")r=this.store.get(t);if(e==="disk")r=await T(t);if(e==="hybrid"){if(r=this.store.get(t),!r)r=await T(t)}if(!r)return null;if(Date.now()-r.timestamp>this.defaultTtl){if(e!=="disk")this.delete(t);if(e==="disk")V(t);return null}return r}async set(t,e){if(!K(t))throw Error("CacheKey inválida",{cause:t});let r=l.get("cache")?.method??"memory",o={...e,timestamp:Date.now()};if(r==="memory")this.store.set(t,o);if(r==="disk")await h(t,o);if(r==="hybrid")this.store.set(t,o),await h(t,o)}delete(t){this.store.delete(t)}}var g=new E;var j=[18,144,240,360,480,720,1080,1440,2160],_={"yt-dlp":"yt-dlp"},q="%(ytId)s.%(ext)s",R={ID:"%(id)s",YT_ID:"%(ytId)s"};import{spawn as k}from"node:child_process";import{stdout as b,stderr as I}from"node:process";function Y(t,e){(t==="data"?b:I).write(e)}function w(t,e,r){let o=_[t];return new Promise((s,n)=>{let m=k(o,e),p="",u="";m.stdout.on("data",(i)=>{p+=i;let a=i.toString();if(r)Y("data",a)}),m.stderr.on("data",(i)=>{u+=i;let a=i.toString();if(r)Y("err",a)}),m.on("close",(i)=>{if(i===0)s(p.toString());else n(Error(u.toString()))}),m.on("error",(i)=>{n(i)})})}function A(t,e){let r=t;for(let[o,s]of e)r=r.replaceAll(o,s);return r}function v(t){return t.replaceAll("/","⁄")}function M({filename:t,id:e,ytId:r}){let o=new Map([[R.ID,e],[R.YT_ID,r]]),s=A(t,o);return v(s)}class C{async download(t,e,r){let o=this.buildYtDlpArgs(t,e,r);return await w("yt-dlp",o,!0),{duration:-1,path:"unknown"}}buildYtDlpArgs(t,e,r){let{id:o,type:s}=r,n=s==="video",m=r.outputPath,p=M({filename:r.filename,id:r.id,ytId:e}),u="aac";return["-f",o,...n?[]:["-x","--audio-format","aac"],"-o",p,"-P",m,t]}}async function tt(t,e){if(!t||!e.id)return;let r=P("video",e);return new C().download(t,t,r)}async function et(t,e){if(!t||!e.id)return;let r=P("audio",e);return new C().download(t,t,r)}function P(t,e){return{id:e.id,type:t,outputPath:e.outputPath??".",filename:e.filename??q}}function U({a:t,b:e,compareResolution:r,type:o}){let s=o==="video"&&r&&t.quality!==null&&e.quality!==null,n=o==="video"&&r&&t.height!==null&&e.height!==null,m=o==="video"&&t.fps!==null&&e.fps!==null,p=o==="audio"&&t.asr!==null&&e.asr!==null,u=t.tbr!==null&&e.tbr!==null,i=0,a=0;if(s){if((t.quality??0)>(e.quality??0))i+=3;else if((t.quality??0)<(e.quality??0))a+=3}if(n){if((t.height??0)>(e.height??0))i+=3;else if((t.height??0)<(e.height??0))a+=3}if(m){if((t.fps??0)>(e.fps??0))i+=2;else if((t.fps??0)<(e.fps??0))a+=2}if(p){if((t.asr??0)>(e.asr??0))i+=2;else if((t.asr??0)<(e.asr??0))a+=2}if(u){if((t.tbr??0)>(e.tbr??0))i+=1;else if((t.tbr??0)<(e.tbr??0))a+=1}return{aScore:i,bScore:a}}function y(t,e,{type:r,compareResolution:o}){if(!t||!e)return;let{aScore:s,bScore:n}=U({a:t,b:e,type:r,compareResolution:o});return s>n?t:e}function D(t,e,{type:r,compareResolution:o}){if(!t||!e)return;let{aScore:s,bScore:n}=U({a:t,b:e,type:r,compareResolution:o});return s>n?e:t}async function rt(t,e){let r=Boolean(e.match(/\d/)),o=r?!1:"N/A",s=["--print","%(formats)j",t],m=(await g.get(`formats:${t}`))?.content??"";if(!m){let f;try{f=await w("yt-dlp",s)}catch(S){throw console.error("Error consiguiendo el ID del formato"),S}if(typeof f!=="string")throw Error("La salida del proceso es de tipo inválido (se esperaba string)");m=f,g.set(`formats:${t}`,{content:m})}let p=[];try{p=JSON.parse(m)}catch(f){throw console.error("Error convirtiendo la salida de yt-dlp a JSON"),f}if(!Array.isArray(p))throw Error("Se esperaba que los formatos fuesen un array. Puede que el contenido cacheado esté corrupto o sea inválido.");let u=void 0,i=void 0,a=void 0,x=void 0,c=void 0;for(let f of p){let S=f.resolution==="audio only";if(r&&f.format_note===e){o=!0,c=y(c,f,{compareResolution:!1,type:"video"})??f;continue}if(S)a=y(a,f,{compareResolution:!1,type:"audio"})??f,x=D(x,f,{compareResolution:!1,type:"audio"})??f;else u=y(u,f,{compareResolution:!0,type:"video"})??f,i=D(i,f,{compareResolution:!0,type:"video"})??f}if(!r)c={"best-video":u,"worst-video":i,"best-audio":a,"worst-audio":x}[e];return{foundSpecific:o,formatId:c?.format_id,desiredFormat:c}}function ot(t){return`https://youtube.com/watch?v=${t}`}export{h as saveInDisk,d as resolvePath,M as resolveFilenamePattern,D as getWorstFormat,T as getFromDisk,y as getBetterFormat,ot as formYoutubeUrl,rt as findFormatId,A as expandPattern,tt as downloadVideo,et as downloadAudio,l as config,g as cache,j as STANDARD_RESOLUTIONS,F as Configuration,E as CacheManager};
1
+ var N={day:86400000,hrs:3600000,min:60000,sec:1000,nano:0.001};function y(e,t){if(!N[t])return e;return e*N[t]}var P={cacheLocation:void 0,ignoreTTL:void 0,method:"memory",ttlInMs:y(10,"day")},$={"yt-dlp":"yt-dlp"},V=new Map([["cache",P],["commands",$]]);class K{store=new Map(V);get(e){let t=this.store.get(e);if(!t)return null;return t}add(e,t){let r=this.store.get(e)??{};this.store.set(e,{...r,...t})}replace(e,t){this.store.set(e,t)}delete(e){this.store.delete(e)}}var m=new K;function D(e){if(typeof e!=="string")return!1;return!0}import{readFile as U}from"node:fs/promises";import{homedir as z}from"node:os";import{join as B,resolve as W,sep as J}from"node:path";function h(e){if(!e)return"";if(e.match(/~[^/]+/))throw Error("Ruta inválida. Ten más cuidado usando '~'");if(e.includes(" "))throw Error("La ruta no puede tener espacios");if(e==="."||e==="..")throw Error(`'${e}' No es una ruta válida. Tal vez faltó agregar '/'?`);let t=e;if(e.startsWith("~"))t=B(z(),J,e.slice(1));return W(t)}import{join as Q}from"node:path";function l(e){let t=m.get("cache")?.cacheLocation;if(!t)throw Error("Es necesario configurar cacheLocation para buscar en el almacenamiento.");let r=h(t);if(e.includes("formats:")){let o=e.split(":")[1];if(!o)throw Error(`Falta especificar ytId en ${e}`);if(o==="${ytId}")throw Error("${ytId} no es un ID válido. Eso debes evaluarlo como `${ytId}`");r=Q(h(t),"formats",o)}return r}async function C(e){let t=l(e);if(!t)return;let r;try{r=await U(t,"utf8")}catch{}if(r)try{r=JSON.parse(r)}catch(o){console.error(`Error convirtiendo la caché de ${e} a JSON:`,o)}if(!r)return;return r}import{rm as G}from"node:fs/promises";async function S(e){let t=l(e);if(!t)return;try{await G(t)}catch(r){console.error(`Error borrando la caché de ${e} en el almacenamiento:`,r)}}import{mkdir as H,writeFile as X}from"node:fs/promises";import{dirname as Z}from"node:path";async function T(e,t){let r=l(e);if(!r)return;let o=Z(r);try{H(o,{recursive:!0})}catch{console.error(`Error creando la carpeta para guardar ${e}`)}try{await X(r,JSON.stringify(t,null,2),"utf8")}catch{console.error(`Error cacheando "${e}" en el almacenamiento`)}}class v{store=new Map;ttl=m.get("cache")?.ttlInMs??y(10,"day");async get(e){if(!D(e))throw Error("CacheKey inválida",{cause:e});let t=m.get("cache")?.method??"memory",r=null;if(t==="memory")r=this.store.get(e);if(t==="disk")r=await C(e);if(t==="hybrid"){if(r=this.store.get(e),!r)r=await C(e)}if(!r)return null;let o=m.get("cache")?.ignoreTTL??"never";if(o==="always")return r;if(o==="next")return m.add("cache",{ignoreTTL:"never"}),r;if(Date.now()-r.timestamp>this.ttl)return await this.delete(e),null;return r}async set(e,t){if(!D(e))throw Error("CacheKey inválida",{cause:e});let r=m.get("cache")?.method??"memory",o={...t,timestamp:Date.now()};if(r==="memory")this.store.set(e,o);if(r==="disk")await T(e,o);if(r==="hybrid")this.store.set(e,o),await T(e,o)}async delete(e){let t=m.get("cache")?.method;if(t==="memory")this.store.delete(e);if(t==="disk")await S(e);if(t==="hybrid")this.store.delete(e),await S(e)}}var u=new v;var j=[18,144,240,360,480,720,1080,1440,2160],q="%(ytId)s.%(ext)s",R={ID:"%(id)s",YT_ID:"%(ytId)s"};import{spawn as k}from"node:child_process";import{stdout as b,stderr as I}from"node:process";function A(e,t){(e==="data"?b:I).write(t)}function w(e,t,r){let s=m.get("commands")?.[e];if(!s)throw Error(`No se encontró un comando configurado para ${e}`);return new Promise((i,f)=>{let p=k(s,t),c="",a="";p.stdout.on("data",(n)=>{c+=n;let d=n.toString();if(r)A("data",d)}),p.stderr.on("data",(n)=>{a+=n;let d=n.toString();if(r)A("err",d)}),p.on("close",(n)=>{if(n===0)i(c.toString());else f(Error(a.toString()))}),p.on("error",(n)=>{f(n)})})}function Y(e,t){let r=e;for(let[o,s]of t)r=r.replaceAll(o,s);return r}function L(e){return e.replaceAll("/","⁄")}function E({filename:e,id:t,ytId:r}){let o=new Map([[R.ID,t],[R.YT_ID,r]]),s=Y(e,o);return L(s)}class x{async download(e,t){let r=this.buildYtDlpArgs(e,t);return await w("yt-dlp",r,!0),{duration:-1,path:"unknown"}}buildYtDlpArgs(e,t){let{id:r,type:o}=t,s=o==="video",i=t.outputDir,f=E({filename:t.filename,id:t.id,ytId:e}),p="aac";return["-f",r,...s?[]:["-x","--audio-format","aac"],"-o",f,"-P",i,e]}}async function ee(e,t){if(!e||!t.formatId)return;let r=O("video",t);return new x().download(e,r)}async function te(e,t){if(!e||!t.formatId)return;let r=O("audio",t);return new x().download(e,r)}function O(e,t){return{id:`${t.formatId}`,type:e,outputDir:t.outputDir??".",filename:t.filename??q}}function _({a:e,b:t,compareResolution:r,type:o}){let s=o==="video"&&r&&e.quality!==null&&t.quality!==null,i=o==="video"&&r&&e.height!==null&&t.height!==null,f=o==="video"&&e.fps!==null&&t.fps!==null,p=o==="audio"&&e.asr!==null&&t.asr!==null,c=e.tbr!==null&&t.tbr!==null,a=0,n=0;if(s){if((e.quality??0)>(t.quality??0))a+=3;else if((e.quality??0)<(t.quality??0))n+=3}if(i){if((e.height??0)>(t.height??0))a+=3;else if((e.height??0)<(t.height??0))n+=3}if(f){if((e.fps??0)>(t.fps??0))a+=2;else if((e.fps??0)<(t.fps??0))n+=2}if(p){if((e.asr??0)>(t.asr??0))a+=2;else if((e.asr??0)<(t.asr??0))n+=2}if(c){if((e.tbr??0)>(t.tbr??0))a+=1;else if((e.tbr??0)<(t.tbr??0))n+=1}return{aScore:a,bScore:n}}function g(e,t,{type:r,compareResolution:o}){if(!e||!t)return;let{aScore:s,bScore:i}=_({a:e,b:t,type:r,compareResolution:o});return s>i?e:t}function F(e,t,{type:r,compareResolution:o}){if(!e||!t)return;let{aScore:s,bScore:i}=_({a:e,b:t,type:r,compareResolution:o});return s>i?t:e}async function M(e){let t=["--print","%(formats)j",e],o=(await u.get(`formats:${e}`))?.content??"";if(!o){let i;try{i=await w("yt-dlp",t)}catch(f){throw console.error("Error consiguiendo el ID del formato"),f}if(typeof i!=="string")throw Error("La salida del proceso es de tipo inválido (se esperaba string)");o=i,u.set(`formats:${e}`,{content:o})}let s=[];try{s=JSON.parse(o)}catch(i){throw console.error("Error convirtiendo la salida de yt-dlp a JSON"),i}if(!Array.isArray(s))throw Error("Se esperaba que los formatos fuesen un array. Puede que el contenido cacheado esté corrupto o sea inválido.");return s}async function re(e,t){let r=Boolean(t.match(/\d/)),o=r?!1:"N/A",s=await M(e),i=void 0,f=void 0,p=void 0,c=void 0,a=void 0;for(let n of s){let d=n.resolution==="audio only";if(r&&n.format_note===t){o=!0,a=g(a,n,{compareResolution:!1,type:"video"})??n;continue}if(d)p=g(p,n,{compareResolution:!1,type:"audio"})??n,c=F(c,n,{compareResolution:!1,type:"audio"})??n;else i=g(i,n,{compareResolution:!0,type:"video"})??n,f=F(f,n,{compareResolution:!0,type:"video"})??n}if(!r)a={"best-video":i,"worst-video":f,"best-audio":p,"worst-audio":c}[t];return{foundSpecific:o,formatId:a?.format_id}}export{T as saveInDisk,h as resolvePath,E as resolveFilenamePattern,F as getWorstFormat,C as getFromDisk,g as getBetterFormat,M as getAllFormats,re as findFormatId,Y as expandPattern,ee as downloadVideo,te as downloadAudio,m as config,u as cache,j as STANDARD_RESOLUTIONS,K as Configuration,v as CacheManager};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yt-kit/core",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"