yt-dlp-bridge 0.5.0 → 0.6.1
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 +49 -3
- package/dist/audio-conversion.d.ts +13 -0
- package/dist/audio-conversion.d.ts.map +1 -0
- package/dist/audio-conversion.js +101 -0
- package/dist/audio-conversion.js.map +1 -0
- package/dist/fields.d.ts +6 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +12 -0
- package/dist/fields.js.map +1 -0
- package/dist/output-events.d.ts +7 -4
- package/dist/output-events.d.ts.map +1 -1
- package/dist/output-events.js +11 -23
- package/dist/output-events.js.map +1 -1
- package/dist/output-target.d.ts +26 -0
- package/dist/output-target.d.ts.map +1 -0
- package/dist/output-target.js +66 -0
- package/dist/output-target.js.map +1 -0
- package/dist/output-template.d.ts +15 -0
- package/dist/output-template.d.ts.map +1 -0
- package/dist/output-template.js +19 -0
- package/dist/output-template.js.map +1 -0
- package/dist/parsers.d.ts +1 -0
- package/dist/parsers.d.ts.map +1 -1
- package/dist/parsers.js +2 -11
- package/dist/parsers.js.map +1 -1
- package/dist/progress.d.ts +50 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +186 -0
- package/dist/progress.js.map +1 -0
- package/dist/schemas.d.ts +37 -29
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +3 -1
- package/dist/schemas.js.map +1 -1
- package/dist/workflow.d.ts +27 -11
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +67 -51
- package/dist/workflow.js.map +1 -1
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -50,6 +50,35 @@ Requires Node.js >= 22.13 and ESM.
|
|
|
50
50
|
- Source-derived option catalog generated from `yt_dlp.options.create_parser`.
|
|
51
51
|
- ESM and TypeScript declarations.
|
|
52
52
|
|
|
53
|
+
## What changed in 0.6.1
|
|
54
|
+
|
|
55
|
+
0.6.1 requires `ytdlp-errors` 1.5.0.
|
|
56
|
+
|
|
57
|
+
- `requested-formats` events carry `filesizeFromBitrate`: bitrate × duration, as yt-dlp's `filesize_from_tbr`. yt-dlp leaves `filesize_approx` empty for a fragmented (HLS/DASH manifest) stream, so this is all such a stream has before it starts. `YT_DLP_REQUESTED_FORMATS_TEMPLATE` now also prints `tbr` and the duration.
|
|
58
|
+
- `YtDlpDownloadProgressTracker` weighs each stream by the best size known for it (exact, an estimate from its finished fragments, `filesizeApprox`, then `filesizeFromBitrate`), and a stream with none by the mean of the others; equal shares remain only when no size is known. A stream of unknown size moves by its fragments, and the percent never goes back when a size turns out larger. Before, a small video then an HLS audio held at 50% through the audio's first fragment, then fell back.
|
|
59
|
+
|
|
60
|
+
## What changed in 0.6.0
|
|
61
|
+
|
|
62
|
+
0.6.0 contains breaking changes and requires `ytdlp-errors` 1.4.0.
|
|
63
|
+
|
|
64
|
+
- **Breaking:** a plan that writes files now carries `cwd`, and every path in its `args` is relative to it. Run it there (`runCommand(…, {cwd: plan.cwd})`): run elsewhere, the files land in the wrong folder. Create the folder first if it may not exist (`mkdir(plan.cwd, {recursive: true})`): yt-dlp used to make it through `--paths home`, and a spawn in a missing folder fails with `ENOENT`. Caller plans run in `output.directory`; `--paths home:` is gone, and `output.tempDirectory`, `-o infojson:`, the subtitle subfolder and a `resume.loadInfoJsonPath` inside the folder become relative. Managed plans run in the output root; the temp root and the download archive are relative when they are inside it. yt-dlp expands `$NAME`, `%NAME%` and `~` in every path it is given, with no escape on POSIX for `--paths`, and strips trailing spaces from `--paths` values. So a folder such as `$HOME`, `%USERPROFILE%` or `Downloads ` (trailing space) wrote somewhere else; now its name never reaches argv.
|
|
65
|
+
- **Breaking:** `output.tempDirectory` must be inside `output.directory`; a plan with one outside it throws.
|
|
66
|
+
- Caller subtitles take planned tracks: `subtitles: {tracks: [{key, auto}], format}`. Each key goes to `--sub-langs` as an exact, case-sensitive, escaped match, and the plan sets `--write-subs` only for a manual track and `--write-auto-subs` only for an automatic caption. The flags cover the whole run and yt-dlp takes a key from the manual tracks first, so a plan with both kinds should ask for an automatic caption only under a key the manual tracks lack (on YouTube, its `<lang>-orig` key). The languages form (`languages`, `writeAuto`, `autoOnly`) stays, sent as given, for a caller that cannot plan the tracks; on YouTube a bare code there also matches machine translations.
|
|
67
|
+
- Caller subtitles take `loadInfoJsonPath`: yt-dlp fetches the subtitle URLs stored in that info-json instead of extracting the video again (and extracts it again only if one of them fails). The URL is not passed.
|
|
68
|
+
- New `subtitle-listing` workflow: `-J` with `--write-subs --write-auto-subs --sub-langs all`, which fills `subtitles` and `automatic_captions` for extractors that list them only when asked (Vimeo, TikTok, Bilibili and others) and writes no files. Its JSON on stdout is what a caller plans tracks from, and what `loadInfoJsonPath` then loads. Exhaustive `switch` statements over `WorkflowPlan['kind']` need the new case.
|
|
69
|
+
- New `output.report`: `finalPath` has yt-dlp append the media's absolute path once every postprocessor and the move are done (`--print-to-file after_move:filepath`); `subtitles` has it append `requested_subtitles` as JSON, with each track's `filepath`, also under `--skip-download` (`after_video`). Both files must be inside `output.directory`.
|
|
70
|
+
- New `templateLiteral(text)`, also from the new `yt-dlp-bridge/output-template` subpath: literal text for an output template. Each `%`, `$` and `~` becomes an absent field whose default is that sign (`%(_absent|$)s`), which yt-dlp's expansion cannot see through. `%%` is not used: on Windows, ntpath pairs `%` signs, and a field before a `%%` lets a literal `%NAME%` expand.
|
|
71
|
+
- **Breaking:** progress is read from yt-dlp's numbers, not its human-readable `[download] 42.0% of …` line. The `progress` event no longer carries `percent`, `total`, `speed` or `eta`; it is now only the fallback for other lines (`raw`).
|
|
72
|
+
- Caller media, caller subtitles and managed download plans pass `--progress-template` with `YT_DLP_PROGRESS_TEMPLATE`, a prefixed JSON line per update. Caller media plans and every managed download plan (media, audio, subtitles, thumbnail and playlist) also pass `--print before_dl:` with `YT_DLP_REQUESTED_FORMATS_TEMPLATE`, one prefixed stdout line per video; caller media plans pass `--no-quiet` so that print does not silence yt-dlp's other output.
|
|
73
|
+
- `parseYtDlpOutputLine` emits two new event kinds: `download-progress` (`status`, `formatId`, `downloadedBytes`, `totalBytes`, `totalBytesEstimate`, `speedBps`, `etaSeconds`, `elapsedSeconds`, `fragmentIndex`, `fragmentCount`) and `requested-formats` (the streams yt-dlp is about to download, in order, with `filesize` / `filesizeApprox`). Exhaustive `switch` statements over `YtDlpOutputEvent` need the two new cases.
|
|
74
|
+
- New `YtDlpDownloadProgressTracker` turns those events into one percent over every stream of a video, so the audio download after the video counts instead of restarting at 0%. It also estimates the time left over all remaining streams.
|
|
75
|
+
- An audio conversion now converts a stream already in the target codec when the source has one (AAC 140 for m4a on YouTube, Opus 251 for opus). yt-dlp copies such a stream instead of re-encoding it, so there is no second lossy pass; `--audio-quality` does not apply to it. A stream below 96 kbps (`AUDIO_CONVERSION_COPY_MIN_KBPS`) is not worth copying: the best stream is re-encoded instead. Each tier of the source chain (the source's track, then any) tries non-DRC before DRC, and every stereo stream before Dolby surround, which libopus cannot take. The "original" track filter no longer matches a stream with no `format_note`.
|
|
76
|
+
- With `audio.convert`, a caller's `selection.formatSelector` (and `formatSort`) now picks the stream to convert, in place of the bridge's source chain. `--audio-format best` (`target: 'best'`) extracts the audio and keeps its codec where yt-dlp can.
|
|
77
|
+
- New `yt-dlp-bridge/audio-conversion` subpath: `audioConversionCopies(target, acodec)` says whether converting a stream to `target` copies it, `audioConversionCopyFilter(target)` is the `-f` filter for such a stream, `audioConversionOutcome(target, {ext, acodec})` gives the file's extension and whether the audio is kept as is, `audioCodecPreference(acodec)` ranks a codec as yt-dlp's format sorter does, and `audioConversionSourceSelector(target)` is the `-f` the planner gives a conversion that names no stream.
|
|
78
|
+
- An MP3 conversion also copies an MP3 stream inside an MP4 (`acodec` `mp4a.40.34`), where it used to pass it over and re-encode another one.
|
|
79
|
+
- A `[VideoRemuxer] Remuxing video from … to …; Destination: <path>` or `[VideoConvertor] Converting video …` line now gives its destination in the `postprocess` event; it came out without a path before, so a consumer following the file kept the pre-remux path, which yt-dlp deletes.
|
|
80
|
+
- A caller's directory that reaches `-o` no longer loses or expands `%` signs: a folder named `100%% done` or containing `%(id)s` keeps its name. Since every path is now relative to the plan's `cwd`, the folder text no longer reaches argv at all.
|
|
81
|
+
|
|
53
82
|
## What changed in 0.5.0
|
|
54
83
|
|
|
55
84
|
0.5.0 contains breaking changes and requires `ytdlp-errors` 1.3.0. 0.4.0 was never published; its change is listed under 0.4.0 below and ships in 0.5.0.
|
|
@@ -175,7 +204,7 @@ const plan = planWorkflow(input, { config });
|
|
|
175
204
|
|
|
176
205
|
```ts
|
|
177
206
|
{
|
|
178
|
-
kind: "probe" | "inspect" | "media" | "audio" | "subtitles" | "thumbnail" | "playlist" | "postprocess" | "expert",
|
|
207
|
+
kind: "probe" | "subtitle-listing" | "inspect" | "media" | "audio" | "subtitles" | "thumbnail" | "playlist" | "postprocess" | "expert",
|
|
179
208
|
args: string[],
|
|
180
209
|
redactedArgs: string[],
|
|
181
210
|
facts: {
|
|
@@ -236,9 +265,23 @@ if (event?.kind === "merge") {
|
|
|
236
265
|
}
|
|
237
266
|
```
|
|
238
267
|
|
|
268
|
+
Plans for media downloads ask yt-dlp for structured progress. Feed it to a tracker for one percent across every stream of a merged download:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { parseYtDlpOutputLine, YtDlpDownloadProgressTracker } from "yt-dlp-bridge/parsers";
|
|
272
|
+
|
|
273
|
+
const tracker = new YtDlpDownloadProgressTracker();
|
|
274
|
+
|
|
275
|
+
function onLine(line: string) {
|
|
276
|
+
const event = parseYtDlpOutputLine(line);
|
|
277
|
+
if (event?.kind === "requested-formats") tracker.setRequestedFormats(event.formats);
|
|
278
|
+
if (event?.kind === "download-progress") console.log(tracker.update(event.progress).percent);
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
239
282
|
### Run a planned command
|
|
240
283
|
|
|
241
|
-
The bridge runner is low-level. It executes a command path plus argv; it does not resolve binaries or decide retries. The child runs as its own process group on Unix, so it does not receive the terminal's Ctrl+C: stop it with `signal`.
|
|
284
|
+
The bridge runner is low-level. It executes a command path plus argv; it does not resolve binaries or decide retries. Pass the plan's `cwd`, which must exist: the paths in its argv are relative to it. The child runs as its own process group on Unix, so it does not receive the terminal's Ctrl+C: stop it with `signal`.
|
|
242
285
|
|
|
243
286
|
```ts
|
|
244
287
|
import { planWorkflow } from "yt-dlp-bridge";
|
|
@@ -253,6 +296,7 @@ const plan = planWorkflow({
|
|
|
253
296
|
|
|
254
297
|
try {
|
|
255
298
|
const result = await runCommand("yt-dlp", plan.args, {
|
|
299
|
+
cwd: plan.cwd,
|
|
256
300
|
timeoutMs: 900_000,
|
|
257
301
|
maxOutputBytes: 4 * 1024 * 1024,
|
|
258
302
|
onStderrLine: line => console.log(parseYtDlpOutputLine(line))
|
|
@@ -282,11 +326,13 @@ Prefer the root export for workflow planning and subpaths for lower-level utilit
|
|
|
282
326
|
|
|
283
327
|
| Area | Exports |
|
|
284
328
|
| --- | --- |
|
|
285
|
-
| Workflow planning | `planWorkflow`, workflow input/plan/fact types |
|
|
329
|
+
| Workflow planning | `planWorkflow`, `templateLiteral`, workflow input/plan/fact types |
|
|
286
330
|
| Schemas | `WorkflowInspectInputSchema`, `WorkflowDownloadInputSchema`, `WorkflowExecutionInputSchema`, `WorkflowPostprocessInputSchema`, `WorkflowExpertInputSchema`, `WorkflowInputSchema`, `ValidateWorkflowInputSchema` |
|
|
331
|
+
| Audio conversion | `audioConversionCopies`, `audioConversionOutcome`, `audioConversionCopyFilter`, `audioCodecPreference`, `audioConversionSourceSelector`, `AUDIO_CONVERSION_COPY_MIN_KBPS` from `yt-dlp-bridge/audio-conversion` |
|
|
287
332
|
| Config | `loadConfig` from `yt-dlp-bridge/config` |
|
|
288
333
|
| Environment | `checkEnvironment` from `yt-dlp-bridge/environment` |
|
|
289
334
|
| Filesystem policy | `resolveOutputPolicy`, `resolveManagedPath`, `ensureWithinRoot`, `isPathInside`, `isPathInsideWith`, `hasParentTraversal`, `ensureDirectory`, `readArchive` from `yt-dlp-bridge/filesystem` |
|
|
335
|
+
| Output templates | `templateLiteral` from `yt-dlp-bridge/output-template` (also from the root) |
|
|
290
336
|
| Option catalog | `UPSTREAM_OPTION_CATALOG`, `listLongFlags`, `findOptionByFlag`, `optionMetadata` from `yt-dlp-bridge/option-catalog` |
|
|
291
337
|
| info_dict | `infoDictSchema`, `ytDlpFormatSchema`, `ytDlpSubtitleTrackSchema`, `isPlaylistLike`, `isUrlRedirect` and the `InfoDict` family of types from `yt-dlp-bridge/info` |
|
|
292
338
|
| Parsers | `parseYtDlpOutputLine`, `parseJsonLines`, `sanitizeMetadataItem`, `sanitizeMetadataItems`, `paginate`, `parseFormats`, `parseSubtitles`, `parseThumbnails`, `parseFinalPaths` from `yt-dlp-bridge/parsers` |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const AUDIO_CONVERSION_COPY_MIN_KBPS = 96;
|
|
2
|
+
export declare function audioConversionCopies(target: string, acodec: string | undefined): boolean;
|
|
3
|
+
export declare function audioConversionOutcome(target: string, source: {
|
|
4
|
+
ext: string | undefined;
|
|
5
|
+
acodec: string | undefined;
|
|
6
|
+
}): {
|
|
7
|
+
ext: string;
|
|
8
|
+
copies: boolean;
|
|
9
|
+
} | undefined;
|
|
10
|
+
export declare function audioConversionCopyFilter(target: string): string | undefined;
|
|
11
|
+
export declare function audioCodecPreference(acodec: string | undefined): number;
|
|
12
|
+
export declare function audioConversionSourceSelector(target: string): string;
|
|
13
|
+
//# sourceMappingURL=audio-conversion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-conversion.d.ts","sourceRoot":"","sources":["../src/audio-conversion.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,8BAA8B,KAAK,CAAA;AAwBhD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAKzF;AAMD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GAAG;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,GAAG,SAAS,CAShK;AAID,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG5E;AAQD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMvE;AAiBD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMpE"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Port of the copy-or-encode choice in yt-dlp's FFmpegExtractAudioPP.run
|
|
2
|
+
// (postprocessor/ffmpeg.py): a source whose codec already is the target (AAC
|
|
3
|
+
// into m4a too) is copied with `-acodec copy` and --audio-quality does not
|
|
4
|
+
// apply; any other source is re-encoded. WAV has no codec of its own to match.
|
|
5
|
+
// `best` keeps the source's codec wherever yt-dlp can.
|
|
6
|
+
// yt-dlp's `acodec` names each codec as ffprobe does, except AAC: `mp4a.40.<n>`
|
|
7
|
+
// for its object types (LC, HE, HEv2, LD, ELD, USAC), never `mp4a.40.34` (MP3)
|
|
8
|
+
// or `mp4a.6B` (MPEG audio).
|
|
9
|
+
const AAC_PATTERN = '^(?:aac|mp4a(?:\\.40\\.(?:[1-6]|23|29|39|42))?)$';
|
|
10
|
+
const TARGET_CODEC_PATTERNS = { aac: AAC_PATTERN, m4a: AAC_PATTERN, opus: '^opus$', mp3: '^(?:mp3|mp4a\\.40\\.34)$', vorbis: '^vorbis$', flac: '^flac$', alac: '^alac$' };
|
|
11
|
+
// A copied stream keeps its bitrate: below this a stream is no better for
|
|
12
|
+
// being copied, and the best stream is re-encoded instead (YouTube's 48 kbps
|
|
13
|
+
// HE-AAC 139 where no 140 is offered, against a 130 kbps Opus 251).
|
|
14
|
+
export const AUDIO_CONVERSION_COPY_MIN_KBPS = 96;
|
|
15
|
+
function targetCodecPattern(target) {
|
|
16
|
+
return Object.hasOwn(TARGET_CODEC_PATTERNS, target) ? TARGET_CODEC_PATTERNS[target] : undefined;
|
|
17
|
+
}
|
|
18
|
+
// ffprobe's name for the codecs yt-dlp's ACODECS table can copy, from
|
|
19
|
+
// yt-dlp's `acodec` (which names AAC by its object type, MP3 in an MP4
|
|
20
|
+
// `mp4a.40.34`).
|
|
21
|
+
const COPYABLE_CODECS = ['opus', 'vorbis', 'mp3', 'flac', 'alac'];
|
|
22
|
+
function ffprobeCodec(acodec) {
|
|
23
|
+
if (acodec === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (new RegExp(AAC_PATTERN, 'i').test(acodec))
|
|
26
|
+
return 'aac';
|
|
27
|
+
if (/^mp4a\.40\.34$/i.test(acodec))
|
|
28
|
+
return 'mp3';
|
|
29
|
+
return COPYABLE_CODECS.find(codec => codec === acodec.toLowerCase());
|
|
30
|
+
}
|
|
31
|
+
// Each target's container (ACODECS in postprocessor/ffmpeg.py).
|
|
32
|
+
const TARGET_EXTENSIONS = { mp3: 'mp3', aac: 'm4a', m4a: 'm4a', opus: 'opus', vorbis: 'ogg', flac: 'flac', alac: 'm4a', wav: 'wav' };
|
|
33
|
+
// FFmpegExtractAudioPP.COMMON_AUDIO_EXTS: `best` leaves these files alone.
|
|
34
|
+
const COMMON_AUDIO_EXTENSIONS = new Set(['aiff', 'alac', 'flac', 'm4a', 'mka', 'mp3', 'ogg', 'opus', 'wav', 'wma']);
|
|
35
|
+
// Whether converting a stream with this `acodec` to `target` copies it.
|
|
36
|
+
// `best` copies any codec yt-dlp has a container for.
|
|
37
|
+
export function audioConversionCopies(target, acodec) {
|
|
38
|
+
const codec = ffprobeCodec(acodec);
|
|
39
|
+
if (codec === undefined)
|
|
40
|
+
return false;
|
|
41
|
+
if (target === 'best')
|
|
42
|
+
return true;
|
|
43
|
+
return (codec === 'aac' && target === 'm4a') || codec === target;
|
|
44
|
+
}
|
|
45
|
+
// What converting a stream to `target` gives: the file's extension, and
|
|
46
|
+
// whether the audio is kept as it is (copied, or left alone) rather than
|
|
47
|
+
// re-encoded. Undefined when yt-dlp's choice can't be told from the stream:
|
|
48
|
+
// an unknown target, or `best` for a stream whose codec isn't named.
|
|
49
|
+
export function audioConversionOutcome(target, source) {
|
|
50
|
+
if (target === 'best') {
|
|
51
|
+
if (source.ext !== undefined && COMMON_AUDIO_EXTENSIONS.has(source.ext))
|
|
52
|
+
return { ext: source.ext, copies: true };
|
|
53
|
+
if (source.acodec === undefined)
|
|
54
|
+
return undefined;
|
|
55
|
+
const codec = ffprobeCodec(source.acodec);
|
|
56
|
+
return codec === undefined ? { ext: 'mp3', copies: false } : { ext: TARGET_EXTENSIONS[codec] ?? codec, copies: true };
|
|
57
|
+
}
|
|
58
|
+
const ext = Object.hasOwn(TARGET_EXTENSIONS, target) ? TARGET_EXTENSIONS[target] : undefined;
|
|
59
|
+
return ext === undefined ? undefined : { ext, copies: audioConversionCopies(target, source.acodec) };
|
|
60
|
+
}
|
|
61
|
+
// The `-f` filter for a stream worth copying when converting to `target`:
|
|
62
|
+
// in its codec, and not below the copy floor (a stream naming no bitrate passes).
|
|
63
|
+
export function audioConversionCopyFilter(target) {
|
|
64
|
+
const pattern = targetCodecPattern(target);
|
|
65
|
+
return pattern === undefined ? undefined : `[acodec~='(?i)${pattern}'][abr>=?${AUDIO_CONVERSION_COPY_MIN_KBPS}]`;
|
|
66
|
+
}
|
|
67
|
+
// Port of FormatSorter's `acodec` order (utils/_utils.py): each pattern is
|
|
68
|
+
// matched at the start of the codec, first match wins; a codec matching none
|
|
69
|
+
// ranks at the empty entry, and an unknown one (no acodec) below it. Higher is
|
|
70
|
+
// preferred. On YouTube it is what puts Opus 251 ahead of AAC 140.
|
|
71
|
+
const ACODEC_ORDER = ['[af]lac', 'wav|aiff', 'opus', 'vorbis|ogg', 'aac', 'mp?4a?', 'mp3', 'ac-?4', 'e-?a?c-?3', 'ac-?3', 'dts', '', null, 'none'];
|
|
72
|
+
export function audioCodecPreference(acodec) {
|
|
73
|
+
const length = ACODEC_ORDER.length;
|
|
74
|
+
if (acodec === undefined)
|
|
75
|
+
return length - ACODEC_ORDER.indexOf(null);
|
|
76
|
+
const codec = acodec.toLowerCase();
|
|
77
|
+
const index = ACODEC_ORDER.findIndex(pattern => pattern !== null && pattern !== '' && new RegExp(`^(?:${pattern})`).test(codec));
|
|
78
|
+
return length - (index === -1 ? ACODEC_ORDER.indexOf('') : index);
|
|
79
|
+
}
|
|
80
|
+
const NON_DRC_AUDIO_FILTERS = "[format_id!~=?'(?i)(?:^|[-_\\s])drc(?:$|[-_\\s])'][format_note!~=?'(?i)\\bdrc\\b']";
|
|
81
|
+
// Dolby surround makes a poor conversion source: libopus refuses its 5.1(side)
|
|
82
|
+
// layout outright, and the other lossy targets downmix it. A stream with no
|
|
83
|
+
// codec named passes.
|
|
84
|
+
const NON_DOLBY_AUDIO_FILTER = "[acodec!~=?'^(?:e-?ac-?3|ec-3|ac-?3)$']";
|
|
85
|
+
// Strict: `~=?` would also take a stream with no note at all as the original.
|
|
86
|
+
const SOURCE_TRACK_AUDIO_FILTERS = ['[language_preference>0]', "[format_note~='(?i)\\boriginal\\b']", ''];
|
|
87
|
+
// The stream to convert, most wanted first: the source's own track before any
|
|
88
|
+
// other; within it the non-DRC master before the DRC one, a stream that is
|
|
89
|
+
// copied as-is before one that is re-encoded, and every stereo stream (DRC
|
|
90
|
+
// ones too) before Dolby surround.
|
|
91
|
+
// The `-f` the planner gives a conversion that names no stream (a caller's
|
|
92
|
+
// stream goes ahead of it). Exported so a caller can run it through its own
|
|
93
|
+
// copy of yt-dlp's selection and know which stream converts.
|
|
94
|
+
export function audioConversionSourceSelector(target) {
|
|
95
|
+
// A stream already in the target codec is copied, not re-encoded a second time.
|
|
96
|
+
const codecFilter = audioConversionCopyFilter(target);
|
|
97
|
+
const stereoFilters = [...(codecFilter ? [codecFilter] : []), NON_DOLBY_AUDIO_FILTER];
|
|
98
|
+
const streamFilters = [...[NON_DRC_AUDIO_FILTERS, ''].flatMap(drcFilter => stereoFilters.map(filter => `${filter}${drcFilter}`)), NON_DRC_AUDIO_FILTERS, ''];
|
|
99
|
+
return [...SOURCE_TRACK_AUDIO_FILTERS.flatMap(trackFilter => streamFilters.map(filters => `bestaudio${trackFilter}${filters}`)), 'best'].join('/');
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=audio-conversion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-conversion.js","sourceRoot":"","sources":["../src/audio-conversion.ts"],"names":[],"mappings":"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"}
|
package/dist/fields.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,KAAK,aAAa,CAAC,CAAC,IAAI;KAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;CAAC,CAAA;AAInE,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAO5F"}
|
package/dist/fields.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Drops undefined values so optional fields are absent rather than present-
|
|
2
|
+
// but-undefined (exactOptionalPropertyTypes).
|
|
3
|
+
export function definedFields(fields) {
|
|
4
|
+
const result = {};
|
|
5
|
+
for (const key of Object.keys(fields)) {
|
|
6
|
+
const value = fields[key];
|
|
7
|
+
if (value !== undefined)
|
|
8
|
+
result[key] = value;
|
|
9
|
+
}
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,8CAA8C;AAC9C,MAAM,UAAU,aAAa,CAAoC,MAAS;IACzE,MAAM,MAAM,GAAqB,EAAE,CAAA;IACnC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAgB,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QACzB,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAuC,CAAA;IAC/E,CAAC;IACD,OAAO,MAAM,CAAA;AACd,CAAC"}
|
package/dist/output-events.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type YtDlpDownloadProgress, type YtDlpRequestedFormat } from './progress.js';
|
|
1
2
|
import type { YtDlpOutputEventKind, YtDlpPostprocessPhase } from './schemas.js';
|
|
2
3
|
export type { YtDlpPostprocessPhase };
|
|
3
4
|
export type YtDlpOutputEvent = {
|
|
@@ -33,12 +34,14 @@ export type YtDlpOutputEvent = {
|
|
|
33
34
|
kind: Extract<YtDlpOutputEventKind, 'postprocess'>;
|
|
34
35
|
phase: YtDlpPostprocessPhase;
|
|
35
36
|
path?: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: Extract<YtDlpOutputEventKind, 'requested-formats'>;
|
|
39
|
+
formats: YtDlpRequestedFormat[];
|
|
40
|
+
} | {
|
|
41
|
+
kind: Extract<YtDlpOutputEventKind, 'download-progress'>;
|
|
42
|
+
progress: YtDlpDownloadProgress;
|
|
36
43
|
} | {
|
|
37
44
|
kind: Extract<YtDlpOutputEventKind, 'progress'>;
|
|
38
|
-
percent?: number;
|
|
39
|
-
total?: string;
|
|
40
|
-
speed?: string;
|
|
41
|
-
eta?: string;
|
|
42
45
|
raw: string;
|
|
43
46
|
};
|
|
44
47
|
export declare function parseYtDlpOutputLine(line: string): YtDlpOutputEvent | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-events.d.ts","sourceRoot":"","sources":["../src/output-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAE,qBAAqB,EAAC,MAAM,cAAc,CAAA;AAE7E,YAAY,EAAC,qBAAqB,EAAC,CAAA;AAEnC,MAAM,MAAM,gBAAgB,GACzB;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,aAAa,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GAClE;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,GAC7D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GACzE;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAC,GACvE;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GAC/D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC/D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAA;CAAC,GAC3D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GAC3F;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GACvF;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,aAAa,CAAC,CAAC;IAAC,KAAK,EAAE,qBAAqB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,
|
|
1
|
+
{"version":3,"file":"output-events.d.ts","sourceRoot":"","sources":["../src/output-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAC,MAAM,eAAe,CAAA;AACjI,OAAO,KAAK,EAAC,oBAAoB,EAAE,qBAAqB,EAAC,MAAM,cAAc,CAAA;AAE7E,YAAY,EAAC,qBAAqB,EAAC,CAAA;AAEnC,MAAM,MAAM,gBAAgB,GACzB;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,aAAa,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GAClE;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,GAC7D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GACzE;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAC,GACvE;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GAC/D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC/D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAA;CAAC,GAC3D;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GAC3F;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GACvF;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,aAAa,CAAC,CAAC;IAAC,KAAK,EAAE,qBAAqB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,GAGjG;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,CAAC;IAAC,OAAO,EAAE,oBAAoB,EAAE,CAAA;CAAC,GAC3F;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAA;CAAC,GAE3F;IAAC,IAAI,EAAE,OAAO,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,CAAA;AAEjE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAsD1E"}
|
package/dist/output-events.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { parseProgressLine, parseRequestedFormatsLine } from './progress.js';
|
|
1
2
|
export function parseYtDlpOutputLine(line) {
|
|
2
3
|
const clean = line.trim();
|
|
3
4
|
if (!clean)
|
|
4
5
|
return null;
|
|
6
|
+
const progress = parseProgressLine(clean);
|
|
7
|
+
if (progress)
|
|
8
|
+
return { kind: 'download-progress', progress };
|
|
9
|
+
const formats = parseRequestedFormatsLine(clean);
|
|
10
|
+
if (formats)
|
|
11
|
+
return { kind: 'requested-formats', formats };
|
|
5
12
|
const destination = /^\[download\] Destination:\s+(.+)$/.exec(clean);
|
|
6
13
|
if (destination?.[1])
|
|
7
14
|
return { kind: 'destination', path: destination[1] };
|
|
@@ -38,21 +45,12 @@ export function parseYtDlpOutputLine(line) {
|
|
|
38
45
|
return { kind: 'transfer-retry', attempt: Number(transferRetry[1]), total: Number(transferRetry[2]) };
|
|
39
46
|
const postprocess = postprocessPhase(clean);
|
|
40
47
|
if (postprocess) {
|
|
41
|
-
|
|
48
|
+
// ExtractAudio prints `Destination: <path>` alone; the video convertor and
|
|
49
|
+
// remuxer print `Remuxing video from mp4 to mkv; Destination: <path>`.
|
|
50
|
+
const destination = /^\[(?:ExtractAudio\]|(?:VideoConvertor|VideoRemuxer)\] (?:Converting|Remuxing) video from \S+ to \S+;) Destination:\s+(.+)$/.exec(clean);
|
|
42
51
|
return { kind: 'postprocess', phase: postprocess, ...(destination?.[1] ? { path: destination[1] } : {}) };
|
|
43
52
|
}
|
|
44
|
-
|
|
45
|
-
return { kind: 'progress', ...(percent === undefined ? {} : { percent }), ...downloadProgressFields(clean), raw: clean };
|
|
46
|
-
}
|
|
47
|
-
// `[download] 42.0% of ~10.00MiB at 1.00MiB/s ETA 00:05` (any part may be
|
|
48
|
-
// missing or `Unknown`).
|
|
49
|
-
function downloadProgressFields(line) {
|
|
50
|
-
if (!line.startsWith('[download]'))
|
|
51
|
-
return {};
|
|
52
|
-
const total = /\bof\s+(.+?)(?=\s+(?:at|ETA|in)\b|$)/i.exec(line)?.[1]?.trim();
|
|
53
|
-
const speed = /\bat\s+(\S+)/i.exec(line)?.[1];
|
|
54
|
-
const eta = /\bETA\s+(\S+)/i.exec(line)?.[1];
|
|
55
|
-
return { ...(total ? { total } : {}), ...(speed ? { speed } : {}), ...(eta ? { eta } : {}) };
|
|
53
|
+
return { kind: 'progress', raw: clean };
|
|
56
54
|
}
|
|
57
55
|
function postprocessPhase(line) {
|
|
58
56
|
if (line.startsWith('[ExtractAudio]'))
|
|
@@ -65,14 +63,4 @@ function postprocessPhase(line) {
|
|
|
65
63
|
return 'movingFiles';
|
|
66
64
|
return null;
|
|
67
65
|
}
|
|
68
|
-
// Only yt-dlp's own `[download] 42.0% of …` line carries a percent. Other
|
|
69
|
-
// lines may hold a `N%` that is no progress: ffmpeg's banner prints the media
|
|
70
|
-
// URL, whose encoded commas read as one (`met=1792423725%2C`).
|
|
71
|
-
function parsePercentFromLine(line) {
|
|
72
|
-
const match = /^\[download\]\s+(\d+(?:\.\d+)?)%/.exec(line);
|
|
73
|
-
if (!match?.[1])
|
|
74
|
-
return undefined;
|
|
75
|
-
const parsed = Number.parseFloat(match[1]);
|
|
76
|
-
return Number.isNaN(parsed) ? undefined : parsed;
|
|
77
|
-
}
|
|
78
66
|
//# sourceMappingURL=output-events.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-events.js","sourceRoot":"","sources":["../src/output-events.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"output-events.js","sourceRoot":"","sources":["../src/output-events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ResolvedOutputPolicy } from './filesystem.js';
|
|
2
|
+
import type { SubtitleMode } from './schemas.js';
|
|
3
|
+
export interface CallerOutput {
|
|
4
|
+
directory: string;
|
|
5
|
+
template?: string;
|
|
6
|
+
tempDirectory?: string;
|
|
7
|
+
subtitleMode?: SubtitleMode;
|
|
8
|
+
report?: {
|
|
9
|
+
finalPath?: string;
|
|
10
|
+
subtitles?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface CallerMediaOutputOptions {
|
|
14
|
+
skipDownload: boolean;
|
|
15
|
+
resume?: {
|
|
16
|
+
writeInfoJson?: boolean;
|
|
17
|
+
infoJsonBaseName?: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare function callerOutputTemplate(output: CallerOutput): string;
|
|
21
|
+
export declare function loadInfoJsonArgs(output: CallerOutput, file: string | undefined): string[];
|
|
22
|
+
export declare function callerMediaOutputArgs(output: CallerOutput, options: CallerMediaOutputOptions): string[];
|
|
23
|
+
export declare function callerSubtitlesOutputArgs(output: CallerOutput): string[];
|
|
24
|
+
export declare function managedOutputArgs(policy: ResolvedOutputPolicy): string[];
|
|
25
|
+
export declare function managedDownloadArchiveArgs(policy: ResolvedOutputPolicy, archive: string | undefined, allowArbitraryOutputPaths: boolean): string[];
|
|
26
|
+
//# sourceMappingURL=output-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-target.d.ts","sourceRoot":"","sources":["../src/output-target.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,oBAAoB,EAAC,MAAM,iBAAiB,CAAA;AAE7E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,cAAc,CAAA;AAW9C,MAAM,WAAW,YAAY;IAE5B,SAAS,EAAE,MAAM,CAAA;IAGjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,YAAY,CAAA;IAM3B,MAAM,CAAC,EAAE;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAC,CAAA;CACjD;AAED,MAAM,WAAW,wBAAwB;IACxC,YAAY,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE;QAAC,aAAa,CAAC,EAAE,OAAO,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAC,CAAA;CAC7D;AAMD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEjE;AA2BD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAGzF;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,wBAAwB,GAAG,MAAM,EAAE,CASvG;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAGxE;AAID,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAExE;AAGD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,yBAAyB,EAAE,OAAO,GAAG,MAAM,EAAE,CAIlJ"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { resolveManagedPath } from './filesystem.js';
|
|
3
|
+
import { templateLiteral } from './output-template.js';
|
|
4
|
+
const DEFAULT_CALLER_OUTPUT_TEMPLATE = '%(title).200B.%(ext)s';
|
|
5
|
+
const DEFAULT_INFO_JSON_BASE_NAME = '_arroxy';
|
|
6
|
+
const SUBTITLE_SUBFOLDER = 'subtitles';
|
|
7
|
+
export function callerOutputTemplate(output) {
|
|
8
|
+
return output.template ?? DEFAULT_CALLER_OUTPUT_TEMPLATE;
|
|
9
|
+
}
|
|
10
|
+
// Written with `/`, which yt-dlp takes on Windows too, so argv is the same on
|
|
11
|
+
// every OS.
|
|
12
|
+
function relativeInside(directory, file) {
|
|
13
|
+
const relative = path.isAbsolute(file) ? path.relative(directory, file) : path.normalize(file);
|
|
14
|
+
const escapes = relative === '' || relative === '.' || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative);
|
|
15
|
+
return escapes ? null : relative.split(path.sep).join('/');
|
|
16
|
+
}
|
|
17
|
+
// A path the run reads or writes inside the folder, relative to it.
|
|
18
|
+
function insideTarget(directory, file, label) {
|
|
19
|
+
const relative = relativeInside(directory, file);
|
|
20
|
+
if (relative === null)
|
|
21
|
+
throw new Error(`${label} must be inside output.directory`);
|
|
22
|
+
return relative;
|
|
23
|
+
}
|
|
24
|
+
function reportArgs(output) {
|
|
25
|
+
const args = [];
|
|
26
|
+
const { finalPath, subtitles } = output.report ?? {};
|
|
27
|
+
if (finalPath)
|
|
28
|
+
args.push('--print-to-file', 'after_move:filepath', templateLiteral(insideTarget(output.directory, finalPath, 'output.report.finalPath')));
|
|
29
|
+
if (subtitles)
|
|
30
|
+
args.push('--print-to-file', 'after_video:%(requested_subtitles)j', templateLiteral(insideTarget(output.directory, subtitles, 'output.report.subtitles')));
|
|
31
|
+
return args;
|
|
32
|
+
}
|
|
33
|
+
// A file from outside the folder (a probe's info-json) is the caller's own
|
|
34
|
+
// path and goes as given; yt-dlp expands it.
|
|
35
|
+
export function loadInfoJsonArgs(output, file) {
|
|
36
|
+
if (!file)
|
|
37
|
+
return [];
|
|
38
|
+
return ['--load-info-json', relativeInside(output.directory, file) ?? file];
|
|
39
|
+
}
|
|
40
|
+
export function callerMediaOutputArgs(output, options) {
|
|
41
|
+
const template = callerOutputTemplate(output);
|
|
42
|
+
const temp = output.tempDirectory ? insideTarget(output.directory, output.tempDirectory, 'output.tempDirectory') : undefined;
|
|
43
|
+
const args = temp ? ['--paths', `temp:${temp}`, '-o', template] : ['-o', template];
|
|
44
|
+
if (temp && !options.skipDownload && options.resume?.writeInfoJson !== false) {
|
|
45
|
+
args.push('--write-info-json', '-o', `infojson:${templateLiteral(`${temp}/${options.resume?.infoJsonBaseName ?? DEFAULT_INFO_JSON_BASE_NAME}`)}`);
|
|
46
|
+
}
|
|
47
|
+
args.push(...reportArgs(output));
|
|
48
|
+
return args;
|
|
49
|
+
}
|
|
50
|
+
export function callerSubtitlesOutputArgs(output) {
|
|
51
|
+
const template = callerOutputTemplate(output);
|
|
52
|
+
return ['-o', output.subtitleMode === 'subfolder' ? `${SUBTITLE_SUBFOLDER}/${template}` : template, ...reportArgs(output)];
|
|
53
|
+
}
|
|
54
|
+
// The output root is the run's working directory. A temp root inside it goes
|
|
55
|
+
// relative; one elsewhere is the server's own path and goes as configured.
|
|
56
|
+
export function managedOutputArgs(policy) {
|
|
57
|
+
return ['--paths', `temp:${relativeInside(policy.outputRoot, policy.tempRoot) ?? policy.tempRoot}`, '--output', policy.outputTemplate];
|
|
58
|
+
}
|
|
59
|
+
// Relative to the output root unless the server allows any path.
|
|
60
|
+
export function managedDownloadArchiveArgs(policy, archive, allowArbitraryOutputPaths) {
|
|
61
|
+
if (!archive)
|
|
62
|
+
return [];
|
|
63
|
+
const resolved = resolveManagedPath(policy.outputRoot, archive, 'downloadArchive', allowArbitraryOutputPaths);
|
|
64
|
+
return ['--download-archive', relativeInside(policy.outputRoot, resolved) ?? resolved];
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=output-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-target.js","sourceRoot":"","sources":["../src/output-target.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal text for an output template (`-o`, a `--print-to-file` file). Each
|
|
3
|
+
* `%`, `$` and `~` becomes an absent field whose default is that sign,
|
|
4
|
+
* `%(_absent|$)s`. yt-dlp expands before it fills fields, so expansion never
|
|
5
|
+
* sees a `$`, a leading `~` or a `%NAME%`, and the fill then puts the sign
|
|
6
|
+
* back. Lossless on POSIX and Windows.
|
|
7
|
+
*
|
|
8
|
+
* Not `%%`: ntpath pairs `%` signs left to right, and one field before a `%%`
|
|
9
|
+
* (yt-dlp's own escape) shifts the pairing so that a literal `%NAME%`
|
|
10
|
+
* expands. Here every run between two `%` signs starts with `(` or `)`, which
|
|
11
|
+
* no set variable's name does, and each sign adds two `%`, so the pairing of
|
|
12
|
+
* what follows stays as it was.
|
|
13
|
+
*/
|
|
14
|
+
export declare function templateLiteral(text: string): string;
|
|
15
|
+
//# sourceMappingURL=output-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-template.d.ts","sourceRoot":"","sources":["../src/output-template.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Pure, so a renderer can build an output template without the planner's
|
|
2
|
+
// Node imports.
|
|
3
|
+
/**
|
|
4
|
+
* Literal text for an output template (`-o`, a `--print-to-file` file). Each
|
|
5
|
+
* `%`, `$` and `~` becomes an absent field whose default is that sign,
|
|
6
|
+
* `%(_absent|$)s`. yt-dlp expands before it fills fields, so expansion never
|
|
7
|
+
* sees a `$`, a leading `~` or a `%NAME%`, and the fill then puts the sign
|
|
8
|
+
* back. Lossless on POSIX and Windows.
|
|
9
|
+
*
|
|
10
|
+
* Not `%%`: ntpath pairs `%` signs left to right, and one field before a `%%`
|
|
11
|
+
* (yt-dlp's own escape) shifts the pairing so that a literal `%NAME%`
|
|
12
|
+
* expands. Here every run between two `%` signs starts with `(` or `)`, which
|
|
13
|
+
* no set variable's name does, and each sign adds two `%`, so the pairing of
|
|
14
|
+
* what follows stays as it was.
|
|
15
|
+
*/
|
|
16
|
+
export function templateLiteral(text) {
|
|
17
|
+
return text.replace(/[%$~]/g, sign => `%(_absent|${sign})s`);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=output-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-template.js","sourceRoot":"","sources":["../src/output-template.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,gBAAgB;AAEhB;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,aAAa,IAAI,IAAI,CAAC,CAAA;AAC7D,CAAC"}
|
package/dist/parsers.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FinalPathResult, FormatSummary, PaginationResult, ParsedJsonLines, SanitizedMetadataItem, SubtitleSummary, ThumbnailSummary } from './types.js';
|
|
2
2
|
export { parseYtDlpOutputLine, type YtDlpOutputEvent, type YtDlpPostprocessPhase } from './output-events.js';
|
|
3
|
+
export { YT_DLP_PROGRESS_PREFIX, YT_DLP_PROGRESS_TEMPLATE, YT_DLP_REQUESTED_FORMATS_PREFIX, YT_DLP_REQUESTED_FORMATS_TEMPLATE, YtDlpDownloadProgressTracker, type CombinedDownloadProgress, type YtDlpDownloadProgress, type YtDlpRequestedFormat } from './progress.js';
|
|
3
4
|
export declare function parseJsonLines<T = unknown>(output: string): ParsedJsonLines<T>;
|
|
4
5
|
export declare function sanitizeMetadataItems(items: unknown[], maxFormats?: number): SanitizedMetadataItem[];
|
|
5
6
|
export declare function sanitizeMetadataItem(item: unknown, maxFormats?: number): SanitizedMetadataItem;
|
package/dist/parsers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parsers.d.ts","sourceRoot":"","sources":["../src/parsers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAA0B,qBAAqB,EAAE,eAAe,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"parsers.d.ts","sourceRoot":"","sources":["../src/parsers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAA0B,qBAAqB,EAAE,eAAe,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAA;AAEnL,OAAO,EAAC,oBAAoB,EAAE,KAAK,gBAAgB,EAAE,KAAK,qBAAqB,EAAC,MAAM,oBAAoB,CAAA;AAC1G,OAAO,EAAC,sBAAsB,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,iCAAiC,EAAE,4BAA4B,EAAE,KAAK,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAC,MAAM,eAAe,CAAA;AAEtQ,wBAAgB,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,CAiB9E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,UAAU,SAAK,GAAG,qBAAqB,EAAE,CAEhG;AAID,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,SAAK,GAAG,qBAAqB,CAgD1F;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAQ1F;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAe5D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE,CAkChE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAYlE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAqB/D"}
|
package/dist/parsers.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { infoDictSchema, isPlaylistLike, ytDlpFormatSchema } from './info.js';
|
|
2
|
+
import { definedFields } from './fields.js';
|
|
2
3
|
export { parseYtDlpOutputLine } from './output-events.js';
|
|
4
|
+
export { YT_DLP_PROGRESS_PREFIX, YT_DLP_PROGRESS_TEMPLATE, YT_DLP_REQUESTED_FORMATS_PREFIX, YT_DLP_REQUESTED_FORMATS_TEMPLATE, YtDlpDownloadProgressTracker } from './progress.js';
|
|
3
5
|
export function parseJsonLines(output) {
|
|
4
6
|
const rawLines = output.split(/\r?\n/).flatMap(line => {
|
|
5
7
|
const rawLine = line.trim();
|
|
@@ -218,15 +220,4 @@ function sanitizeFormatSummary(format) {
|
|
|
218
220
|
language: format.language
|
|
219
221
|
});
|
|
220
222
|
}
|
|
221
|
-
// Drops undefined values so optional fields are absent rather than present-
|
|
222
|
-
// but-undefined (exactOptionalPropertyTypes).
|
|
223
|
-
function definedFields(fields) {
|
|
224
|
-
const result = {};
|
|
225
|
-
for (const key of Object.keys(fields)) {
|
|
226
|
-
const value = fields[key];
|
|
227
|
-
if (value !== undefined)
|
|
228
|
-
result[key] = value;
|
|
229
|
-
}
|
|
230
|
-
return result;
|
|
231
|
-
}
|
|
232
223
|
//# sourceMappingURL=parsers.js.map
|