yt-dlp-bridge 0.1.5 → 0.2.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.
Files changed (59) hide show
  1. package/README.md +220 -201
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +33 -14
  4. package/dist/config.js.map +1 -1
  5. package/dist/environment.d.ts +2 -2
  6. package/dist/environment.d.ts.map +1 -1
  7. package/dist/environment.js +26 -52
  8. package/dist/environment.js.map +1 -1
  9. package/dist/errors.d.ts +1 -1
  10. package/dist/errors.d.ts.map +1 -1
  11. package/dist/errors.js +25 -43
  12. package/dist/errors.js.map +1 -1
  13. package/dist/filesystem.d.ts +7 -3
  14. package/dist/filesystem.d.ts.map +1 -1
  15. package/dist/filesystem.js +18 -19
  16. package/dist/filesystem.js.map +1 -1
  17. package/dist/generated/ytdlp-options.json +2087 -5511
  18. package/dist/index.d.ts +3 -12
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3 -12
  21. package/dist/index.js.map +1 -1
  22. package/dist/option-catalog.d.ts +4 -3
  23. package/dist/option-catalog.d.ts.map +1 -1
  24. package/dist/option-catalog.js +65 -69
  25. package/dist/option-catalog.js.map +1 -1
  26. package/dist/output-events.d.ts +34 -0
  27. package/dist/output-events.d.ts.map +1 -0
  28. package/dist/output-events.js +51 -0
  29. package/dist/output-events.js.map +1 -0
  30. package/dist/parsers.d.ts +2 -1
  31. package/dist/parsers.d.ts.map +1 -1
  32. package/dist/parsers.js +90 -98
  33. package/dist/parsers.js.map +1 -1
  34. package/dist/redaction.d.ts.map +1 -1
  35. package/dist/redaction.js +32 -18
  36. package/dist/redaction.js.map +1 -1
  37. package/dist/runner.d.ts +1 -1
  38. package/dist/runner.d.ts.map +1 -1
  39. package/dist/runner.js +27 -31
  40. package/dist/runner.js.map +1 -1
  41. package/dist/schemas.d.ts +480 -40
  42. package/dist/schemas.d.ts.map +1 -1
  43. package/dist/schemas.js +47 -42
  44. package/dist/schemas.js.map +1 -1
  45. package/dist/types.d.ts +5 -5
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/workflow.d.ts +165 -0
  48. package/dist/workflow.d.ts.map +1 -0
  49. package/dist/workflow.js +807 -0
  50. package/dist/workflow.js.map +1 -0
  51. package/package.json +88 -72
  52. package/dist/argv.d.ts +0 -18
  53. package/dist/argv.d.ts.map +0 -1
  54. package/dist/argv.js +0 -276
  55. package/dist/argv.js.map +0 -1
  56. package/dist/planner.d.ts +0 -10
  57. package/dist/planner.d.ts.map +0 -1
  58. package/dist/planner.js +0 -233
  59. package/dist/planner.js.map +0 -1
package/README.md CHANGED
@@ -1,313 +1,332 @@
1
1
  # yt-dlp-bridge
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/yt-dlp-bridge.svg)](https://www.npmjs.com/package/yt-dlp-bridge)
4
- [![npm downloads](https://img.shields.io/npm/dm/yt-dlp-bridge.svg)](https://www.npmjs.com/package/yt-dlp-bridge)
5
- [![CI](https://github.com/antonio-orionus/yt-dlp-bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/antonio-orionus/yt-dlp-bridge/actions/workflows/ci.yml)
3
+ [![registry version](https://img.shields.io/npm/v/yt-dlp-bridge.svg)](https://www.npmjs.com/package/yt-dlp-bridge)
4
+ [![registry downloads](https://img.shields.io/npm/dm/yt-dlp-bridge.svg)](https://www.npmjs.com/package/yt-dlp-bridge)
5
+ [![CI](https://github.com/antonio-orionus/Arroxy/actions/workflows/ci.yml/badge.svg)](https://github.com/antonio-orionus/Arroxy/actions/workflows/ci.yml)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
- [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
8
- [![Node.js >=22.13](https://img.shields.io/badge/node-%3E%3D22.13-brightgreen.svg)](package.json)
9
7
 
10
- TypeScript primitives for building safe, policy-aware integrations on top of `yt-dlp`: command planning, argv generation, environment checks, process execution, output parsing, redaction, and structured errors.
8
+ TypeScript library for safe, no-shell `yt-dlp` integration in Node.js services: workflow planning, argv generation, command facts, environment checks, process execution, output parsing, secret redaction, filesystem policy, and structured errors.
11
9
 
12
- `yt-dlp-bridge` is intentionally MCP-agnostic. It is the integration layer used by `yt-dlp-mcp-server`, but it can be used anywhere a Node.js service needs to plan or run `yt-dlp` without shell interpolation.
10
+ `yt-dlp-bridge` is intentionally app-agnostic. It is used by Arroxy and `yt-dlp-mcp-server`, but it does not own Electron token minting, binary resolution, retries, queue lifecycle, settings stores, plugin installation, or UI progress policy.
13
11
 
14
- ## Contents
12
+ Source and contributions live in the Arroxy monorepo at [`packages/yt-dlp-bridge`](https://github.com/antonio-orionus/Arroxy/tree/main/packages/yt-dlp-bridge).
15
13
 
14
+ - [Install](#install)
16
15
  - [Features](#features)
17
- - [Quick Start](#quick-start)
18
- - [Installation](#installation)
16
+ - [What changed in 0.2.0](#what-changed-in-020)
19
17
  - [Usage](#usage)
20
- - [API Surface](#api-surface)
18
+ - [API](#api)
21
19
  - [Configuration](#configuration)
22
20
  - [Requirements](#requirements)
23
21
  - [Development](#development)
24
- - [Release](#release)
25
22
  - [Troubleshooting](#troubleshooting)
26
23
  - [License](#license)
27
24
 
28
- ## Features
29
-
30
- - Source-derived option catalog generated from `yt_dlp.options.create_parser`.
31
- - Safe argv builders that return argument arrays instead of shell command strings.
32
- - Dependency-aware download and post-processing plans for `yt-dlp`, `ffmpeg`, `ffprobe`, external downloaders, browser impersonation, and JavaScript runtimes.
33
- - Managed output policy for output roots, temp roots, archive files, overwrite behavior, and parent-directory traversal checks.
34
- - Secret redaction for auth flags, cookies, signed URLs, and command previews.
35
- - Parsers for JSON lines, format lists, subtitle lists, thumbnail lists, progress output, and final output paths.
36
- - Structured error adaptation through [`ytdlp-errors`](https://www.npmjs.com/package/ytdlp-errors).
37
- - ESM and TypeScript declarations out of the box.
38
-
39
- ## Quick Start
25
+ ## Install
40
26
 
41
27
  ```bash
42
- npm install yt-dlp-bridge
28
+ bun add yt-dlp-bridge
43
29
  # pnpm add yt-dlp-bridge
44
30
  # yarn add yt-dlp-bridge
45
- # bun add yt-dlp-bridge
46
31
  ```
47
32
 
48
- ```ts
49
- import {
50
- PlanDownloadInputSchema,
51
- loadConfig,
52
- planDownload
53
- } from "yt-dlp-bridge";
54
-
55
- const config = loadConfig();
56
- const input = PlanDownloadInputSchema.parse({
57
- url: "https://www.youtube.com/watch?v=BaW_jenozKc",
58
- kind: "audio",
59
- postprocess: {
60
- audioFormat: "mp3"
61
- },
62
- output: {
63
- outputRoot: "audio"
64
- }
65
- });
33
+ Requires Node.js >= 22.13 and ESM.
66
34
 
67
- const plan = await planDownload(input, config);
35
+ ## Features
68
36
 
69
- console.log(plan.commandPreview.command);
70
- console.log(plan.commandPreview.redactedArgs);
71
- console.log(plan.requiredDependencies);
72
- ```
37
+ - One canonical `planWorkflow(input, options?)` planner with caller-owned app inputs and managed/server schema inputs.
38
+ - Caller-owned probe/media/subtitle inputs for apps that already resolved output directories, format selection, subtitles, embeds, resume info JSON, and audio conversion policy.
39
+ - Managed schema inputs for inspect, media, audio, subtitles, thumbnail, playlist, postprocess, and expert/raw workflows that need bridge-owned normalization.
40
+ - Workflow facts for media-vs-sidecar behavior, output roots/templates, playlist sentinel scope, subtitle format, dependencies, risks, and side effects.
41
+ - Safe argv arrays instead of shell command strings.
42
+ - Managed output policy for output roots, temp roots, archive files, overwrite behavior, and parent-directory traversal checks.
43
+ - Secret redaction for auth flags, cookies, PO tokens, visitor data, proxies, signed URLs, and command previews.
44
+ - Parsers for JSON lines, format lists, subtitle lists, thumbnail lists, progress output, final output paths, and live yt-dlp output events.
45
+ - Low-level process runner using `spawn` with `shell: false`, bounded output capture, timeouts, and structured failures.
46
+ - Source-derived option catalog generated from `yt_dlp.options.create_parser`.
47
+ - ESM and TypeScript declarations.
73
48
 
74
- ## Installation
49
+ ## What changed in 0.2.0
75
50
 
76
- For package consumers:
51
+ `0.2.0` keeps `planWorkflow` as the single planner, but separates the public input surface into two paths:
77
52
 
78
- ```bash
79
- npm install yt-dlp-bridge
80
- ```
53
+ - Use caller-owned inputs (`ProbeWorkflowInput`, `CallerMediaWorkflowInput`, `CallerSubtitlesWorkflowInput`) when your app owns job state, output folders, format selectors, subtitle mode, resume metadata, and user-facing workflow policy.
54
+ - Use managed Zod schemas (`WorkflowDownloadInputSchema`, `WorkflowInspectInputSchema`, `WorkflowPostprocessInputSchema`, `WorkflowExpertInputSchema`) when a server integration wants the bridge to normalize config, auth, network, output roots, and yt-dlp workflow options from request-like input.
81
55
 
82
- For source development, this repository uses pnpm:
56
+ Migration notes for `0.1.x` consumers:
83
57
 
84
- ```bash
85
- git clone https://github.com/antonio-orionus/yt-dlp-bridge.git
86
- cd yt-dlp-bridge
87
- pnpm install
88
- pnpm run build
89
- pnpm test
90
- ```
58
+ - If you used managed download schemas only to plan app-owned downloads, map your app job to `CallerMediaWorkflowInput` or `CallerSubtitlesWorkflowInput` instead.
59
+ - `WorkflowInputSchema` validates the managed/server schema family. Caller-owned app inputs are TypeScript interfaces, so validate local job state before mapping it into the bridge.
60
+ - Media planning now includes app-oriented resume hardening, including `--continue`, chunk sizing, temp info JSON output, and `resume.loadInfoJsonPath` planning that omits the original URL.
61
+ - Embedded subtitle planning forces the safe MKV merge container and suppresses thumbnail embedding when needed for subtitle muxing.
62
+ - App runtime decisions still stay outside the package: binary selection, token minting, retries, queue lifecycle, cancellation UI, progress display, and settings persistence remain caller-owned.
91
63
 
92
64
  ## Usage
93
65
 
94
- ### Build Inspect Arguments
66
+ Choose the input style based on ownership. App integrations should usually start with caller-owned inputs. MCP/server-style integrations should usually start with managed schemas.
95
67
 
96
- Use the Zod schemas to fill defaults before passing inputs into the low-level builders.
68
+ ### Plan from a caller-owned app adapter
97
69
 
98
- ```ts
99
- import { InspectInputSchema, buildMetadataArgs } from "yt-dlp-bridge";
70
+ App adapters should map local state into `WorkflowInput`, then keep app-owned orchestration outside the bridge.
100
71
 
101
- const input = InspectInputSchema.parse({
102
- url: "https://www.youtube.com/watch?v=BaW_jenozKc"
103
- });
72
+ ```ts
73
+ import { planWorkflow, type CallerMediaWorkflowInput } from "yt-dlp-bridge";
74
+
75
+ function toWorkflowInput(job: PreparedJob): CallerMediaWorkflowInput {
76
+ return {
77
+ kind: "media",
78
+ url: job.url,
79
+ output: { directory: job.outputDir, tempDirectory: job.tempDir },
80
+ selection: {
81
+ formatSelector: job.formatSelector,
82
+ mergeOutputFormat: job.mergeOutputFormat
83
+ },
84
+ subtitles: job.embedSubtitles
85
+ ? { embed: true, languages: job.subtitleLanguages, writeAuto: job.writeAutoSubtitles }
86
+ : undefined,
87
+ embed: {
88
+ metadata: job.embedMetadata,
89
+ thumbnail: job.embedThumbnail,
90
+ chapters: job.embedChapters
91
+ },
92
+ resume: job.infoJsonPath ? { loadInfoJsonPath: job.infoJsonPath } : undefined
93
+ };
94
+ }
104
95
 
105
- const args = buildMetadataArgs(input);
96
+ const plan = planWorkflow(toWorkflowInput(job));
106
97
 
107
- console.log(args);
108
- // ["--ignore-config", "--no-warnings", "--dump-json", "https://www.youtube.com/watch?v=BaW_jenozKc"]
98
+ console.log(plan.args);
99
+ console.log(plan.facts.sideEffects);
109
100
  ```
110
101
 
111
- ### Plan a Download
102
+ The bridge owns yt-dlp semantics such as audio conversion arguments, subtitle/embed container rules, playlist sentinel logic, dependency reasons, risk/side-effect facts, output path facts, resume argv, and redaction. The adapter owns local job models, runtime selection, retries, token minting, cancellation, progress policy, and user-facing status.
103
+
104
+ ### Plan a managed workflow
112
105
 
113
- `planDownload` resolves output policy, builds the final argv, redacts secrets, checks available dependencies, and explains risks and side effects before anything is downloaded.
106
+ Use managed schemas when a consumer accepts request-like input and wants the bridge to normalize defaults before planning a stable `yt-dlp` argv plus facts.
114
107
 
115
108
  ```ts
116
- import {
117
- PlanDownloadInputSchema,
118
- loadConfig,
119
- planDownload
120
- } from "yt-dlp-bridge";
109
+ import { WorkflowDownloadInputSchema, planWorkflow } from "yt-dlp-bridge";
110
+ import { loadConfig } from "yt-dlp-bridge/config";
121
111
 
122
112
  const config = loadConfig({
123
113
  YTDLP_MCP_OUTPUT_ROOT: "/srv/downloads",
124
114
  YTDLP_MCP_TEMP_ROOT: "/srv/downloads/.tmp"
125
115
  });
126
116
 
127
- const input = PlanDownloadInputSchema.parse({
117
+ const input = WorkflowDownloadInputSchema.parse({
118
+ kind: "audio",
128
119
  url: "https://www.youtube.com/watch?v=BaW_jenozKc",
129
- kind: "video",
120
+ postprocess: { audioFormat: "mp3" },
121
+ output: { outputTemplate: "%(title).180B [%(id)s].%(ext)s" }
122
+ });
123
+
124
+ const plan = planWorkflow(input, {
125
+ config,
126
+ configFiles: { mode: "disabled" },
127
+ plugins: { mode: "native" }
128
+ });
129
+
130
+ console.log(plan.args);
131
+ console.log(plan.redactedArgs);
132
+ console.log(plan.facts.dependencies.required);
133
+ ```
134
+
135
+ Managed schemas are the runtime-validation layer for this path:
136
+
137
+ ```ts
138
+ import { WorkflowInputSchema } from "yt-dlp-bridge";
139
+
140
+ const input = WorkflowInputSchema.parse(request.body);
141
+ const plan = planWorkflow(input, { config });
142
+ ```
143
+
144
+ `WorkflowPlan` has this shape:
145
+
146
+ ```ts
147
+ {
148
+ kind: "probe" | "inspect" | "media" | "audio" | "subtitles" | "thumbnail" | "playlist" | "postprocess" | "expert",
149
+ args: string[],
150
+ redactedArgs: string[],
151
+ facts: {
152
+ isMediaDownload: boolean,
153
+ output?: {
154
+ directory?: string,
155
+ tempDirectory?: string,
156
+ outputRoot?: string,
157
+ tempRoot?: string,
158
+ template?: string,
159
+ allowOverwrite?: boolean
160
+ },
161
+ playlistScope?: {
162
+ requestedCount: number,
163
+ sentinel: boolean,
164
+ ytDlpFlag: "--playlist-end" | "--playlist-items",
165
+ ytDlpValue: string
166
+ },
167
+ effectiveSubtitleFormat?: "srt" | "vtt" | "ass",
168
+ dependencies: { required: unknown[], optional: unknown[] },
169
+ risks: string[],
170
+ sideEffects: string[]
171
+ }
172
+ }
173
+ ```
174
+
175
+ ### Probe playlists
176
+
177
+ Probe planning owns playlist mode and sentinel range behavior.
178
+
179
+ ```ts
180
+ import { planWorkflow } from "yt-dlp-bridge";
181
+
182
+ const probe = planWorkflow({
183
+ kind: "probe",
184
+ url: "https://www.youtube.com/playlist?list=...",
130
185
  selection: {
131
- downloadArchive: "archive.txt"
132
- },
133
- format: {
134
- format: "bv*+ba/b"
135
- },
136
- output: {
137
- outputTemplate: "%(playlist_index)s-%(title).180B [%(id)s].%(ext)s"
186
+ playlistMode: "playlist",
187
+ playlistScope: { items: { kind: "first", count: 50 } }
138
188
  }
139
189
  });
140
190
 
141
- const plan = await planDownload(input, config);
191
+ console.log(probe.args);
192
+ console.log(probe.facts.playlistScope);
193
+ ```
194
+
195
+ ### Parse live yt-dlp output
196
+
197
+ Use `parseYtDlpOutputLine` to turn noisy output into stable events. App-specific adapters can map these events to progress bars, queue resume evidence, logs, or MCP notifications.
198
+
199
+ ```ts
200
+ import { parseYtDlpOutputLine } from "yt-dlp-bridge/parsers";
201
+
202
+ const event = parseYtDlpOutputLine('[Merger] Merging formats into "/tmp/video.mp4"');
142
203
 
143
- console.log(plan.argv);
144
- console.log(plan.risks);
145
- console.log(plan.sideEffects);
204
+ if (event?.kind === "merge") {
205
+ console.log(event.path);
206
+ }
146
207
  ```
147
208
 
148
- ### Run a Planned Command
209
+ ### Run a planned command
149
210
 
150
- The runner uses `spawn` with `shell: false`, bounded output capture, timeouts, and structured failures.
211
+ The bridge runner is low-level. It executes a command path plus argv; it does not resolve binaries or decide retries.
151
212
 
152
213
  ```ts
153
- import {
154
- PlanDownloadInputSchema,
155
- loadConfig,
156
- planDownload,
157
- runCommand,
158
- toStructuredError
159
- } from "yt-dlp-bridge";
160
-
161
- const config = loadConfig();
162
- const input = PlanDownloadInputSchema.parse({
163
- url: "https://www.youtube.com/watch?v=BaW_jenozKc",
164
- kind: "video"
165
- });
214
+ import { planWorkflow } from "yt-dlp-bridge";
215
+ import { runCommand } from "yt-dlp-bridge/runner";
216
+ import { toStructuredError } from "yt-dlp-bridge/errors";
166
217
 
167
- const plan = await planDownload(input, config);
218
+ const plan = planWorkflow({
219
+ kind: "media",
220
+ url: "https://www.youtube.com/watch?v=BaW_jenozKc"
221
+ });
168
222
 
169
223
  try {
170
- const result = await runCommand(plan.commandPreview.command, plan.commandPreview.args, {
171
- timeoutMs: config.defaultTimeoutMs,
172
- maxOutputBytes: config.maxOutputBytes
224
+ const result = await runCommand("yt-dlp", plan.args, {
225
+ timeoutMs: 900_000,
226
+ maxOutputBytes: 4 * 1024 * 1024
173
227
  });
174
-
175
228
  console.log(result.stdout);
176
229
  } catch (error) {
177
230
  console.error(toStructuredError(error));
178
231
  }
179
232
  ```
180
233
 
181
- ### Inspect the Option Catalog
234
+ ### Query the option catalog
182
235
 
183
- The bundled catalog in this release is generated from `yt_dlp.options.create_parser` for yt-dlp `2026.03.17` and contains 323 upstream options.
236
+ The bundled catalog is generated from `yt_dlp.options.create_parser` for yt-dlp `2026.03.17` and contains 323 upstream options.
184
237
 
185
238
  ```ts
186
- import {
187
- UPSTREAM_OPTION_CATALOG,
188
- findOptionByFlag,
189
- listLongFlags,
190
- optionMetadata
191
- } from "yt-dlp-bridge";
239
+ import { UPSTREAM_OPTION_CATALOG, findOptionByFlag, listLongFlags, optionMetadata } from "yt-dlp-bridge/option-catalog";
192
240
 
193
241
  const format = findOptionByFlag("--format");
194
-
195
242
  console.log(UPSTREAM_OPTION_CATALOG.optionCount);
196
243
  console.log(listLongFlags().includes("--dump-json"));
197
244
  console.log(format ? optionMetadata(format) : undefined);
198
245
  ```
199
246
 
200
- ## API Surface
247
+ ## API
248
+
249
+ Prefer the root export for workflow planning and subpaths for lower-level utilities.
201
250
 
202
251
  | Area | Exports |
203
252
  | --- | --- |
204
- | Argv builders | `ytdlpCommand`, `buildMetadataArgs`, `buildSingleJsonArgs`, `buildListArgs`, `buildSimulationArgs`, `buildDownloadArgs` |
205
- | Planning | `planDownload`, `planPostprocess` |
206
- | Configuration and environment | `loadConfig`, `CONFIG`, `checkEnvironment` |
207
- | Filesystem policy | `resolveOutputPolicy`, `resolveManagedPath`, `ensureWithinRoot`, `isPathInside`, `isPathInsideWith`, `hasParentTraversal`, `ensureDirectory`, `readArchive` |
208
- | Option catalog | `UPSTREAM_OPTION_CATALOG`, `listLongFlags`, `findOptionByFlag`, `optionMetadata` |
209
- | Parsers | `parseJsonLines`, `sanitizeMetadataItem`, `sanitizeMetadataItems`, `paginate`, `parseFormats`, `parseSubtitles`, `parseThumbnails`, `parseProgress`, `parseFinalPaths` |
210
- | Process and errors | `runCommand`, `CommandExecutionError`, `toStructuredError` |
211
- | Redaction | `redactArgs`, `redactText`, `excerpt` |
212
- | Schemas and types | Zod input schemas and TypeScript interfaces exported from `src/index.ts` |
253
+ | Workflow planning | `planWorkflow`, workflow input/plan/fact types |
254
+ | Schemas | `WorkflowInspectInputSchema`, `WorkflowDownloadInputSchema`, `WorkflowExecutionInputSchema`, `WorkflowPostprocessInputSchema`, `WorkflowExpertInputSchema`, `WorkflowInputSchema`, `ValidateWorkflowInputSchema` |
255
+ | Config | `loadConfig`, `CONFIG` from `yt-dlp-bridge/config` |
256
+ | Environment | `checkEnvironment` from `yt-dlp-bridge/environment` |
257
+ | Filesystem policy | `resolveOutputPolicy`, `resolveManagedPath`, `ensureWithinRoot`, `isPathInside`, `isPathInsideWith`, `hasParentTraversal`, `ensureDirectory`, `readArchive` from `yt-dlp-bridge/filesystem` |
258
+ | Option catalog | `UPSTREAM_OPTION_CATALOG`, `listLongFlags`, `findOptionByFlag`, `optionMetadata` from `yt-dlp-bridge/option-catalog` |
259
+ | Parsers | `parseYtDlpOutputLine`, `parseJsonLines`, `sanitizeMetadataItem`, `sanitizeMetadataItems`, `paginate`, `parseFormats`, `parseSubtitles`, `parseThumbnails`, `parseProgress`, `parseFinalPaths` from `yt-dlp-bridge/parsers` |
260
+ | Runner | `runCommand`, `CommandExecutionError` from `yt-dlp-bridge/runner` |
261
+ | Redaction | `redactArgs`, `redactText`, `excerpt` from `yt-dlp-bridge/redaction` |
262
+ | Structured errors | `toStructuredError` from `yt-dlp-bridge/errors` |
263
+
264
+ Example imports:
265
+
266
+ ```ts
267
+ import { planWorkflow } from "yt-dlp-bridge";
268
+ import { parseYtDlpOutputLine } from "yt-dlp-bridge/parsers";
269
+ import { runCommand } from "yt-dlp-bridge/runner";
270
+ import { redactArgs } from "yt-dlp-bridge/redaction";
271
+ ```
213
272
 
214
273
  ## Configuration
215
274
 
216
- `loadConfig()` reads environment variables and applies conservative defaults.
275
+ `loadConfig(env?)` reads environment variables and applies conservative defaults. Pass a partial `ProcessEnv` object to override specific values.
217
276
 
218
277
  | Variable | Default | Purpose |
219
278
  | --- | --- | --- |
220
- | `YTDLP_MCP_YTDLP_PATH` or `YTDLP_PATH` | `yt-dlp` | Path or command name used for `yt-dlp`. |
279
+ | `YTDLP_MCP_YTDLP_PATH` or `YTDLP_PATH` | `yt-dlp` | Path or command name for `yt-dlp`. |
221
280
  | `YTDLP_MCP_FFMPEG_PATH` or `FFMPEG_PATH` | `ffmpeg` | Optional ffmpeg override for environment checks. |
222
281
  | `YTDLP_MCP_FFPROBE_PATH` or `FFPROBE_PATH` | `ffprobe` | Optional ffprobe override for environment checks. |
223
282
  | `YTDLP_MCP_OUTPUT_ROOT` | `~/Downloads/yt-dlp-mcp` | Managed output root. |
224
- | `YTDLP_MCP_TEMP_ROOT` | OS temp directory under `yt-dlp-mcp` | Managed temp root. |
225
- | `YTDLP_MCP_ALLOW_ARBITRARY_OUTPUT_PATHS` | `false` | Allows output and temp paths outside the configured roots. |
226
- | `YTDLP_MCP_ALLOW_CONFIG_FILES` | `false` | Allows user-provided cookies, netrc, and related config-file inputs unless they match server-level settings. |
283
+ | `YTDLP_MCP_TEMP_ROOT` | OS temp under `yt-dlp-mcp` | Managed temp root. |
284
+ | `YTDLP_MCP_ALLOW_ARBITRARY_OUTPUT_PATHS` | `false` | Allow output paths outside the configured roots. |
285
+ | `YTDLP_MCP_ALLOW_CONFIG_FILES` | `false` | Allow user-provided cookies, netrc, and config-file inputs. |
286
+ | `YTDLP_MCP_ALLOW_PLUGIN_DIRS` | `false` | Allow user-provided plugin directory paths. |
227
287
  | `YTDLP_MCP_COOKIES_FILE` | unset | Server-level cookies file. |
228
288
  | `YTDLP_MCP_COOKIES_FROM_BROWSER` | unset | Server-level browser cookie source for `--cookies-from-browser`. |
229
- | `YTDLP_MCP_ENABLE_EXPERT` | `false` | Enables higher-risk expert-mode integrations in callers that expose them. |
230
- | `YTDLP_MCP_TIMEOUT_MS` | `900000` | Default command timeout. |
231
- | `YTDLP_MCP_MAX_OUTPUT_BYTES` | `4194304` | Maximum retained stdout or stderr bytes. |
289
+ | `YTDLP_MCP_ENABLE_EXPERT` | `false` | Enable higher-risk expert-mode integrations. |
290
+ | `YTDLP_MCP_TIMEOUT_MS` | `900000` | Default command timeout (ms). |
291
+ | `YTDLP_MCP_MAX_OUTPUT_BYTES` | `4194304` | Maximum retained stdout/stderr bytes. |
232
292
  | `YTDLP_MCP_JS_RUNTIMES` | `deno,node,bun,quickjs` | JavaScript runtimes considered for extractor challenges. |
233
293
 
234
294
  ## Requirements
235
295
 
236
- - Node.js >= 22.13.
237
- - ESM import support.
238
- - `yt-dlp` installed when planning or running real yt-dlp workflows.
239
- - `ffmpeg` and `ffprobe` for audio extraction, audio/video merge, remuxing, recoding, segment editing, subtitle conversion, thumbnail conversion, and embedding workflows.
240
- - Python `yt_dlp` package, or `PYTHONPATH` pointed at a yt-dlp source checkout, when regenerating or verifying the option catalog.
296
+ - Node.js >= 22.13, ESM.
297
+ - `yt-dlp` installed when running yt-dlp workflows.
298
+ - `ffmpeg` and `ffprobe` for audio extraction, merge, remux, recode, segment editing, subtitle/thumbnail conversion, and embedding workflows.
299
+ - Python `yt_dlp` package, or `PYTHONPATH` pointing at a yt-dlp checkout, when regenerating or verifying the option catalog.
241
300
 
242
301
  ## Development
243
302
 
244
- | Command | Description |
245
- | --- | --- |
246
- | `pnpm run build` | Compile TypeScript and copy generated assets into `dist`. |
247
- | `pnpm test` | Run Vitest tests. |
248
- | `pnpm run typecheck` | Run TypeScript without emitting files. |
249
- | `pnpm run generate:options` | Regenerate the source-derived yt-dlp option catalog from Python `yt_dlp`. |
250
- | `pnpm run verify:options` | Verify that the generated option catalog matches Python `yt_dlp`. |
251
- | `pnpm pack --dry-run` | Preview the package contents before publishing. |
252
-
253
- ## Release
254
-
255
- GitHub Actions handles CI and npm publishing:
256
-
257
- - `.github/workflows/ci.yml` runs typecheck, tests, build, and package dry-run on pull requests and pushes to `main`.
258
- - `.github/workflows/release.yml` publishes to npm when a `vX.Y.Z` tag is pushed and the tag matches `package.json`.
259
- - Publishing uses npm trusted publishing with GitHub Actions OIDC and publishes an attested npm tarball with `npm publish ./npm-artifacts/*.tgz --access public`; no `NPM_TOKEN` secret is required when the trusted publisher is configured. npm automatically generates provenance for trusted publishes from GitHub Actions.
260
-
261
- The npm trusted publisher should point at:
262
-
263
- ```text
264
- repository: antonio-orionus/yt-dlp-bridge
265
- workflow file: release.yml
266
- environment: npm
267
- ```
268
-
269
- Release flow:
270
-
271
303
  ```bash
272
- pnpm version patch
273
- git push origin main --follow-tags
304
+ git clone https://github.com/antonio-orionus/Arroxy.git
305
+ cd Arroxy
306
+ bun install
274
307
  ```
275
308
 
276
- ## Project Structure
277
-
278
- ```text
279
- yt-dlp-bridge/
280
- ├── .github/workflows/ # CI and npm publish automation
281
- ├── src/ # TypeScript source and generated option catalog
282
- ├── tests/ # Vitest coverage for argv, policy, parsers, planning, and errors
283
- ├── scripts/ # Option catalog generation and package asset scripts
284
- ├── dist/ # Built package output
285
- ├── package.json # Package metadata, exports, scripts, and npm keywords
286
- └── README.md # Package and repository documentation
287
- ```
309
+ | Command | Description |
310
+ | --- | --- |
311
+ | `bun run --filter yt-dlp-bridge build` | Compile TypeScript and copy generated assets into `dist`. |
312
+ | `bun run --filter yt-dlp-bridge test` | Run Vitest tests. |
313
+ | `bun run --filter yt-dlp-bridge typecheck` | TypeScript check without emitting. |
314
+ | `bun run --filter yt-dlp-bridge generate:options` | Regenerate the option catalog from Python `yt_dlp`. |
315
+ | `bun run --filter yt-dlp-bridge verify:options` | Verify the catalog matches Python `yt_dlp`. |
316
+ | `bun run bridge:check` | Run the package gate used by Arroxy CI and registry publishing. |
288
317
 
289
318
  ## Troubleshooting
290
319
 
291
- ### `yt-dlp` is missing
292
-
293
- Install `yt-dlp` or set `YTDLP_MCP_YTDLP_PATH` to the executable path. `checkEnvironment()` reports the detected command, status, version, and notes.
294
-
295
- ### `ffmpeg` or `ffprobe` is missing
296
-
297
- Install the missing binary or set `YTDLP_MCP_FFMPEG_PATH` and `YTDLP_MCP_FFPROBE_PATH`. Planning still returns dependency information so callers can explain which workflow requires the missing tool.
298
-
299
- ### `auth.cookiesFile requires YTDLP_MCP_ALLOW_CONFIG_FILES=true`
300
-
301
- User-provided config-file paths are blocked by default. Prefer server-level `YTDLP_MCP_COOKIES_FILE` or enable `YTDLP_MCP_ALLOW_CONFIG_FILES=true` only when the caller has reviewed the trust boundary.
320
+ **`yt-dlp` is missing** - Install `yt-dlp` or set `YTDLP_MCP_YTDLP_PATH`. `checkEnvironment()` reports detected command, status, version, and notes.
302
321
 
303
- ### `outputTemplate must not contain parent-directory traversal`
322
+ **`ffmpeg`/`ffprobe` missing** - Install the binary or set `YTDLP_MCP_FFMPEG_PATH` / `YTDLP_MCP_FFPROBE_PATH`. Planning still returns dependency info so callers can surface which workflow requires the missing tool.
304
323
 
305
- Relative output templates cannot contain `..` path segments unless arbitrary output paths are explicitly allowed by policy.
324
+ **`auth.cookiesFile requires YTDLP_MCP_ALLOW_CONFIG_FILES=true`** - User-provided config-file paths are blocked by default. Prefer server-level `YTDLP_MCP_COOKIES_FILE` or enable `YTDLP_MCP_ALLOW_CONFIG_FILES=true` only after reviewing the trust boundary.
306
325
 
307
- ## Contributing
326
+ **`outputTemplate must not contain parent-directory traversal`** - Relative templates cannot contain `..` segments unless `YTDLP_MCP_ALLOW_ARBITRARY_OUTPUT_PATHS=true`.
308
327
 
309
- Use the development commands above before opening a pull request. Bugs and feature requests can be reported through [GitHub Issues](https://github.com/antonio-orionus/yt-dlp-bridge/issues).
328
+ Bugs and feature requests: [GitHub Issues](https://github.com/antonio-orionus/Arroxy/issues).
310
329
 
311
330
  ## License
312
331
 
313
- [MIT](LICENSE)
332
+ [MIT](LICENSE) © 2026 Antonio Orionus
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB,EAAE,OAAO,CAAC;IACnC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAWD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAiB7E;AAED,eAAO,MAAM,MAAM,cAAe,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,yBAAyB,EAAE,OAAO,CAAA;IAClC,gBAAgB,EAAE,OAAO,CAAA;IACzB,eAAe,EAAE,OAAO,CAAA;IACxB,gBAAgB,EAAE,OAAO,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,UAAU,EAAE,MAAM,EAAE,CAAA;CACpB;AA2BD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAoB7E;AAED,eAAO,MAAM,MAAM,cAAe,CAAA"}
package/dist/config.js CHANGED
@@ -1,35 +1,54 @@
1
- import os from "node:os";
2
- import path from "node:path";
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
3
  function bool(value) {
4
- return value ? ["1", "true", "yes", "on"].includes(value.toLowerCase()) : false;
4
+ const normalized = blankToUndefined(value);
5
+ return normalized ? ['1', 'true', 'yes', 'on'].includes(normalized.toLowerCase()) : false;
5
6
  }
6
7
  function int(value, fallback) {
7
- const parsed = value ? Number.parseInt(value, 10) : Number.NaN;
8
+ const normalized = blankToUndefined(value);
9
+ const parsed = normalized ? Number.parseInt(normalized, 10) : Number.NaN;
8
10
  return Number.isFinite(parsed) ? parsed : fallback;
9
11
  }
12
+ function blankToUndefined(value) {
13
+ const normalized = value?.trim();
14
+ if (!normalized)
15
+ return undefined;
16
+ return normalized;
17
+ }
18
+ function firstNonBlank(...values) {
19
+ for (const value of values) {
20
+ const normalized = blankToUndefined(value);
21
+ if (normalized !== undefined)
22
+ return normalized;
23
+ }
24
+ return undefined;
25
+ }
10
26
  export function expandHome(input) {
11
- if (input === "~")
27
+ if (input === '~')
12
28
  return os.homedir();
13
- if (input.startsWith("~/") || input.startsWith("~\\"))
29
+ if (input.startsWith('~/') || input.startsWith('~\\'))
14
30
  return path.join(os.homedir(), input.slice(2));
15
31
  return input;
16
32
  }
17
33
  export function loadConfig(env = process.env) {
18
34
  return {
19
- ytdlpPath: env.YTDLP_MCP_YTDLP_PATH ?? env.YTDLP_PATH ?? "yt-dlp",
20
- ffmpegPath: env.YTDLP_MCP_FFMPEG_PATH ?? env.FFMPEG_PATH,
21
- ffprobePath: env.YTDLP_MCP_FFPROBE_PATH ?? env.FFPROBE_PATH,
22
- outputRoot: path.resolve(expandHome(env.YTDLP_MCP_OUTPUT_ROOT ?? path.join(os.homedir(), "Downloads", "yt-dlp-mcp"))),
23
- tempRoot: path.resolve(expandHome(env.YTDLP_MCP_TEMP_ROOT ?? path.join(os.tmpdir(), "yt-dlp-mcp"))),
35
+ ytdlpPath: firstNonBlank(env.YTDLP_MCP_YTDLP_PATH, env.YTDLP_PATH) ?? 'yt-dlp',
36
+ ffmpegPath: firstNonBlank(env.YTDLP_MCP_FFMPEG_PATH, env.FFMPEG_PATH),
37
+ ffprobePath: firstNonBlank(env.YTDLP_MCP_FFPROBE_PATH, env.FFPROBE_PATH),
38
+ outputRoot: path.resolve(expandHome(firstNonBlank(env.YTDLP_MCP_OUTPUT_ROOT) ?? path.join(os.homedir(), 'Downloads', 'yt-dlp-mcp'))),
39
+ tempRoot: path.resolve(expandHome(firstNonBlank(env.YTDLP_MCP_TEMP_ROOT) ?? path.join(os.tmpdir(), 'yt-dlp-mcp'))),
24
40
  allowArbitraryOutputPaths: bool(env.YTDLP_MCP_ALLOW_ARBITRARY_OUTPUT_PATHS),
25
41
  allowConfigFiles: bool(env.YTDLP_MCP_ALLOW_CONFIG_FILES),
26
42
  allowPluginDirs: bool(env.YTDLP_MCP_ALLOW_PLUGIN_DIRS),
27
43
  enableExpertMode: bool(env.YTDLP_MCP_ENABLE_EXPERT),
28
44
  defaultTimeoutMs: int(env.YTDLP_MCP_TIMEOUT_MS, 15 * 60 * 1000),
29
45
  maxOutputBytes: int(env.YTDLP_MCP_MAX_OUTPUT_BYTES, 4 * 1024 * 1024),
30
- cookiesFile: env.YTDLP_MCP_COOKIES_FILE,
31
- cookiesFromBrowser: env.YTDLP_MCP_COOKIES_FROM_BROWSER,
32
- jsRuntimes: (env.YTDLP_MCP_JS_RUNTIMES ?? "deno,node,bun,quickjs").split(",").map((v) => v.trim()).filter(Boolean)
46
+ cookiesFile: firstNonBlank(env.YTDLP_MCP_COOKIES_FILE),
47
+ cookiesFromBrowser: firstNonBlank(env.YTDLP_MCP_COOKIES_FROM_BROWSER),
48
+ jsRuntimes: (firstNonBlank(env.YTDLP_MCP_JS_RUNTIMES) ?? 'deno,node,bun,quickjs')
49
+ .split(',')
50
+ .map(v => v.trim())
51
+ .filter(Boolean)
33
52
  };
34
53
  }
35
54
  export const CONFIG = loadConfig();
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAmB7B,SAAS,IAAI,CAAC,KAAyB;IACrC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAClF,CAAC;AAED,SAAS,GAAG,CAAC,KAAyB,EAAE,QAAgB;IACtD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IAC/D,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACvC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACtG,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,OAAO;QACL,SAAS,EAAE,GAAG,CAAC,oBAAoB,IAAI,GAAG,CAAC,UAAU,IAAI,QAAQ;QACjE,UAAU,EAAE,GAAG,CAAC,qBAAqB,IAAI,GAAG,CAAC,WAAW;QACxD,WAAW,EAAE,GAAG,CAAC,sBAAsB,IAAI,GAAG,CAAC,YAAY;QAC3D,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,qBAAqB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;QACrH,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;QACnG,yBAAyB,EAAE,IAAI,CAAC,GAAG,CAAC,sCAAsC,CAAC;QAC3E,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,4BAA4B,CAAC;QACxD,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC;QACtD,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,uBAAuB,CAAC;QACnD,gBAAgB,EAAE,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAC/D,cAAc,EAAE,GAAG,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;QACpE,WAAW,EAAE,GAAG,CAAC,sBAAsB;QACvC,kBAAkB,EAAE,GAAG,CAAC,8BAA8B;QACtD,UAAU,EAAE,CAAC,GAAG,CAAC,qBAAqB,IAAI,uBAAuB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;KACnH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { type ServerConfig } from "./config.js";
2
- import type { DetectedDependency } from "./types.js";
1
+ import { type ServerConfig } from './config.js';
2
+ import type { DetectedDependency } from './types.js';
3
3
  export interface EnvironmentReport {
4
4
  platform: NodeJS.Platform;
5
5
  arch: string;