@blagoja/ts-dlp 0.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/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # ts-dlp
2
+
3
+ A lightweight, fluent TypeScript wrapper for yt-dlp made for humans.
4
+
5
+ `ts-dlp` aims to make downloading media with yt-dlp feel natural in Node.js and TypeScript projects through a chainable, strongly-typed API.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - Fluent builder-style API
12
+ - Fully typed with TypeScript
13
+ - Uses your local yt-dlp installation
14
+ - Promise-based API
15
+ - Easy process handling
16
+ - Cross-platform support
17
+
18
+ ---
19
+
20
+ ## Current Status
21
+
22
+ > Early development / proof of concept
23
+
24
+ Current functionality:
25
+
26
+ - Detects whether `yt-dlp` is installed and available in PATH
27
+
28
+ ---
29
+
30
+ # Installation
31
+
32
+ ```bash
33
+ npm install ts-dlp
34
+ ```
35
+
36
+ You must also have yt-dlp installed on your system.
37
+
38
+ ## Install yt-dlp
39
+
40
+ ### Windows
41
+
42
+ ```bash
43
+ winget install yt-dlp.yt-dlp
44
+ ```
45
+
46
+ ### macOS
47
+
48
+ ```bash
49
+ brew install yt-dlp
50
+ ```
51
+
52
+ ### Linux
53
+
54
+ ```bash
55
+ pip install -U yt-dlp
56
+ ```
57
+
58
+ Official project:
59
+
60
+ TBD
61
+
62
+ ---
63
+
64
+ # Vision
65
+
66
+ Instead of writing:
67
+
68
+ ```bash
69
+ yt-dlp -f "bestvideo[height<=1080]+bestaudio" URL
70
+ ```
71
+
72
+ You write:
73
+
74
+ ```ts
75
+ await ytdlp.download(url).resolution("1080p").audio("best").merge().run();
76
+ ```
77
+
78
+ ---
79
+
80
+ # Example API (Planned)
81
+
82
+ ```ts
83
+ import {ytdlp} from "ts-dlp";
84
+
85
+ await ytdlp
86
+ .download("https://youtube.com/watch?v=...")
87
+ .resolution("1080p")
88
+ .fps(60)
89
+ .format("mp4")
90
+ .output("./downloads")
91
+ .run();
92
+ ```
93
+
94
+ ---
95
+
96
+ # Goals
97
+
98
+ - Make yt-dlp easier to use from Node.js
99
+ - Avoid manually constructing CLI arguments
100
+ - Provide autocomplete and type safety
101
+ - Keep the wrapper lightweight
102
+ - Stay close to native yt-dlp functionality
103
+
104
+ ---
105
+
106
+ # Roadmap
107
+
108
+ ## v0.1.0 - Foundation
109
+
110
+ - [x] Detect yt-dlp installation
111
+ - [ ] Execute simple yt-dlp commands
112
+ - [ ] Basic command runner
113
+ - [ ] Error handling
114
+ - [ ] Capture stdout/stderr
115
+ - [ ] TypeScript support
116
+ - [ ] Unit test setup
117
+
118
+ ---
119
+
120
+ ## v0.2.0 - Download API
121
+
122
+ - [ ] `.download(url)`
123
+ - [ ] `.run()`
124
+ - [ ] `.output(path)`
125
+ - [ ] `.format(format)`
126
+ - [ ] `.audio()`
127
+ - [ ] `.video()`
128
+ - [ ] `.resolution()`
129
+ - [ ] `.fps()`
130
+
131
+ Example:
132
+
133
+ ```ts
134
+ await ytdlp.download(url).resolution("720p").run();
135
+ ```
136
+
137
+ ---
138
+
139
+ ## v0.3.0 - Metadata & Information
140
+
141
+ - [ ] `.info()`
142
+ - [ ] Video metadata parsing
143
+ - [ ] JSON output support
144
+ - [ ] Playlist support
145
+ - [ ] Progress events
146
+
147
+ Example:
148
+
149
+ ```ts
150
+ const info = await ytdlp.info(url);
151
+ ```
152
+
153
+ ---
154
+
155
+ ## v0.4.0 - Advanced Features
156
+
157
+ - [ ] Subtitle support
158
+ - [ ] Thumbnail downloads
159
+ - [ ] Playlist filtering
160
+ - [ ] Cookies support
161
+ - [ ] Proxy support
162
+ - [ ] Custom yt-dlp arguments
163
+
164
+ ---
165
+
166
+ ## v0.5.0 - Developer Experience
167
+
168
+ - [ ] Better typed builders
169
+ - [ ] Presets
170
+ - [ ] Config files
171
+ - [ ] Improved documentation
172
+ - [ ] More examples
173
+ - [ ] CI/CD pipeline
174
+ - [ ] Automated tests
175
+
176
+ ---
177
+
178
+ # Non-Goals
179
+
180
+ At least for now, `ts-dlp` will NOT:
181
+
182
+ - Bundle yt-dlp binaries
183
+ - Replace yt-dlp functionality
184
+ - Abstract every yt-dlp flag
185
+ - Become a GUI application
186
+
187
+ The goal is to remain a lightweight developer wrapper.
188
+
189
+ ---
190
+
191
+ # Contributing
192
+
193
+ Contributions, suggestions, and issue reports are welcome.
194
+
195
+ If you have an idea for the API design or developer experience, feel free to open an issue.
196
+
197
+ ---
198
+
199
+ # License
200
+
201
+ MIT
@@ -0,0 +1,43 @@
1
+ export declare class DownloadBuilder {
2
+ private readonly url;
3
+ private readonly args;
4
+ constructor(url: string);
5
+ /**
6
+ * Set the desired resolution for the video to be downloaded.
7
+ * This will instruct yt-dlp to download the best video and audio streams that are less than or equal to the specified height.
8
+ * For example, if you specify "720p", yt-dlp will download the best video and audio streams that are 720 pixels in height or less.
9
+ * If you want to download the best available quality regardless of resolution, you can omit this option or set it to a very high value like "9999p".
10
+ *
11
+ * @example
12
+ * download("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
13
+ * .resolution("720p")
14
+ * .output("downloads/%(title)s.%(ext)s")
15
+ *
16
+ * @param res The desired resolution for the video. For example: "720p", "1080p", etc. This will tell yt-dlp to download the best video and audio streams that are less than or equal to the specified height. If you want to download the best available quality, you can omit this option or set it to a very high value like "9999p".
17
+ * @returns The current instance of DownloadBuilder for method chaining.
18
+ */
19
+ resolution(res: string): this;
20
+ /**
21
+ * Set the output directory and filename template for the downloaded file.
22
+ *
23
+ * @param template The output template for the downloaded file. You can use placeholders like %(title)s, %(ext)s, etc. For example: "downloads/%(title)s.%(ext)s"
24
+ * @returns The current instance of DownloadBuilder for method chaining.
25
+ */
26
+ output(template: string): this;
27
+ /**
28
+ * Execute the download with the specified options.
29
+ * This will run yt-dlp with the accumulated arguments and the provided URL.
30
+ * The output will be displayed in the console.
31
+ *
32
+ * @throws Will throw an error if yt-dlp is not installed or not in PATH, or if any other error occurs during the execution of yt-dlp.
33
+ *
34
+ * @example
35
+ * download("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
36
+ * .resolution("720p")
37
+ * .output("downloads/%(title)s.%(ext)s")
38
+ * .run();
39
+ *
40
+ */
41
+ run(): Promise<void>;
42
+ }
43
+ //# sourceMappingURL=download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"AAEA,qBAAa,eAAe;IAGf,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;gBAER,GAAG,EAAE,MAAM;IAExC;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAS7B;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK9B;;;;;;;;;;;;;OAaG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAG1B"}
@@ -0,0 +1,55 @@
1
+ import { runYtDlp } from "./internal/runner.js";
2
+ export class DownloadBuilder {
3
+ url;
4
+ args = [];
5
+ constructor(url) {
6
+ this.url = url;
7
+ }
8
+ /**
9
+ * Set the desired resolution for the video to be downloaded.
10
+ * This will instruct yt-dlp to download the best video and audio streams that are less than or equal to the specified height.
11
+ * For example, if you specify "720p", yt-dlp will download the best video and audio streams that are 720 pixels in height or less.
12
+ * If you want to download the best available quality regardless of resolution, you can omit this option or set it to a very high value like "9999p".
13
+ *
14
+ * @example
15
+ * download("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
16
+ * .resolution("720p")
17
+ * .output("downloads/%(title)s.%(ext)s")
18
+ *
19
+ * @param res The desired resolution for the video. For example: "720p", "1080p", etc. This will tell yt-dlp to download the best video and audio streams that are less than or equal to the specified height. If you want to download the best available quality, you can omit this option or set it to a very high value like "9999p".
20
+ * @returns The current instance of DownloadBuilder for method chaining.
21
+ */
22
+ resolution(res) {
23
+ const height = res.replace("p", "");
24
+ this.args.push("-f", `bestvideo[height<=${height}]+bestaudio/best[height<=${height}]`);
25
+ return this;
26
+ }
27
+ /**
28
+ * Set the output directory and filename template for the downloaded file.
29
+ *
30
+ * @param template The output template for the downloaded file. You can use placeholders like %(title)s, %(ext)s, etc. For example: "downloads/%(title)s.%(ext)s"
31
+ * @returns The current instance of DownloadBuilder for method chaining.
32
+ */
33
+ output(template) {
34
+ this.args.push("-o", template);
35
+ return this;
36
+ }
37
+ /**
38
+ * Execute the download with the specified options.
39
+ * This will run yt-dlp with the accumulated arguments and the provided URL.
40
+ * The output will be displayed in the console.
41
+ *
42
+ * @throws Will throw an error if yt-dlp is not installed or not in PATH, or if any other error occurs during the execution of yt-dlp.
43
+ *
44
+ * @example
45
+ * download("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
46
+ * .resolution("720p")
47
+ * .output("downloads/%(title)s.%(ext)s")
48
+ * .run();
49
+ *
50
+ */
51
+ async run() {
52
+ await runYtDlp([...this.args, this.url]);
53
+ }
54
+ }
55
+ //# sourceMappingURL=download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAE9C,MAAM,OAAO,eAAe;IAGE;IAFZ,IAAI,GAAa,EAAE,CAAC;IAErC,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAE5C;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,GAAW;QACrB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,IAAI,CACb,IAAI,EACJ,qBAAqB,MAAM,4BAA4B,MAAM,GAAG,CAChE,CAAC;QACF,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,QAAgB;QACtB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,GAAG;QACR,MAAM,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,CAAC;CACD"}
@@ -0,0 +1,3 @@
1
+ import { DownloadBuilder } from "./download.js";
2
+ export declare function download(url: string): DownloadBuilder;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAE9C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAErD"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { DownloadBuilder } from "./download.js";
2
+ export function download(url) {
3
+ return new DownloadBuilder(url);
4
+ }
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAE9C,MAAM,UAAU,QAAQ,CAAC,GAAW;IACnC,OAAO,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function runYtDlp(args: string[]): Promise<void>;
2
+ export declare function handleEnoent(error: unknown): never;
3
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/internal/runner.ts"],"names":[],"mappings":"AAEA,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAOlD"}
@@ -0,0 +1,13 @@
1
+ import { execa, ExecaError } from "execa";
2
+ export async function runYtDlp(args) {
3
+ await execa("yt-dlp", ["--js-runtimes", "node", ...args], {
4
+ stdio: "inherit",
5
+ }).catch(handleEnoent);
6
+ }
7
+ export function handleEnoent(error) {
8
+ if (error instanceof ExecaError && error.code === "ENOENT") {
9
+ throw new Error("yt-dlp is not installed or not in PATH. Please refer to https://github.com/yt-dlp/yt-dlp#installation");
10
+ }
11
+ throw error;
12
+ }
13
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/internal/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAE,UAAU,EAAC,MAAM,OAAO,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAc;IAC5C,MAAM,KAAK,CAAC,QAAQ,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE;QACzD,KAAK,EAAE,SAAS;KAChB,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IAC1C,IAAI,KAAK,YAAY,UAAU,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACd,uGAAuG,CACvG,CAAC;IACH,CAAC;IACD,MAAM,KAAK,CAAC;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@blagoja/ts-dlp",
3
+ "version": "0.1.0",
4
+ "description": "A human readable TypeScript wrapper for yt-dlp",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsx src/index.ts",
16
+ "lint": "eslint src",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "yt-dlp",
21
+ "youtube-dl",
22
+ "downloader",
23
+ "wrapper"
24
+ ],
25
+ "author": "Blagoja Blazhevski",
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "license": "ISC",
33
+ "type": "module",
34
+ "devDependencies": {
35
+ "@types/node": "^25.9.1",
36
+ "typescript-eslint": "^8.60.0",
37
+ "eslint": "^10.4.0",
38
+ "jiti": "^2.7.0",
39
+ "tsx": "^4.22.3",
40
+ "typescript": "^6.0.3"
41
+ },
42
+ "dependencies": {
43
+ "execa": "^9.6.1"
44
+ }
45
+ }