commit 08fef051b78888d31baeb4f784d9c91a1553df4c
parent b99630d909dab9bfca1e0cdb36a9898d92a562a4
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sun, 4 Oct 2026 16:09:37 -0400
common: video_720 download preset; clipFormatSelector moves to downloadFormat; source-video quality resolver
video_720 resolves to the 720p clip selector, then the same at 480p, then
bv*+ba/b. clipFormatSelector now lives in downloadFormat.ts and
fetchWindowManaged.ts re-exports it. SourceVideoQuality (original |
video_720), sourceVideoFormatSelector and resolveSourceVideoQuality
(override > channel > global > original) are the one effective-quality API.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
3 files changed, 200 insertions(+), 21 deletions(-)
diff --git a/common/ytdlp/downloadFormat.test.ts b/common/ytdlp/downloadFormat.test.ts
@@ -0,0 +1,86 @@
+import { test } from "node:test";
+import assert from "node:assert/strict";
+import {
+ DOWNLOAD_FORMAT_LABELS,
+ DOWNLOAD_FORMAT_PRESETS,
+ ORIGINAL_SOURCE_FORMAT_SELECTOR,
+ SOURCE_VIDEO_QUALITIES,
+ VIDEO_720_PRESET,
+ clipFormatSelector,
+ isDownloadFormatPreset,
+ isSourceVideoQuality,
+ resolveDownloadFormatSelector,
+ resolveSourceVideoQuality,
+ sourceVideoFormatSelector,
+} from "./downloadFormat";
+import { clipFormatSelector as reexported } from "./fetchWindowManaged";
+
+// Run with:
+// pnpm --filter yt-dlp-transcript-common exec tsx --test ytdlp/downloadFormat.test.ts
+
+const CLIP_720 =
+ "bv*[vcodec^=avc1][height<=720]+ba[acodec^=mp4a]/" +
+ "bv*[ext=mp4][height<=720]+ba[ext=m4a]/" +
+ "b[ext=mp4][height<=720]/" +
+ "b[height<=720]";
+const CLIP_480 = CLIP_720.replaceAll("720", "480");
+
+test("clipFormatSelector is the selector umtool uses, and the old import path still serves it", () => {
+ assert.equal(clipFormatSelector(720), CLIP_720);
+ assert.equal(reexported, clipFormatSelector);
+});
+
+test("video_720: the 720 rungs, then the 480 rungs, then the last resort", () => {
+ assert.equal(VIDEO_720_PRESET, "video_720");
+ assert.ok(isDownloadFormatPreset("video_720"));
+ assert.ok(DOWNLOAD_FORMAT_PRESETS.includes("video_720"));
+ assert.equal(DOWNLOAD_FORMAT_LABELS.video_720, "Video 720p (H.264, for clips/editing)");
+ const expected = `${CLIP_720}/${CLIP_480}/bv*+ba/b`;
+ assert.equal(resolveDownloadFormatSelector("video_720", null), expected);
+ // An explicit preset ignores the platform, as every explicit preset does.
+ assert.equal(resolveDownloadFormatSelector("video_720", "odysee"), expected);
+});
+
+test("the existing presets resolve exactly as before", () => {
+ assert.equal(resolveDownloadFormatSelector("auto", "youtube"), "bestaudio/worst");
+ assert.equal(resolveDownloadFormatSelector("auto", "odysee"), "original/bestaudio/worst");
+ assert.equal(resolveDownloadFormatSelector("original", null), "original/bestaudio/worst");
+ assert.equal(resolveDownloadFormatSelector("bestaudio", null), "bestaudio/worst");
+ assert.equal(resolveDownloadFormatSelector("bestvideo_audio", null), "bestvideo*+bestaudio/best");
+});
+
+test("sourceVideoFormatSelector: original is today's persist selector, byte for byte", () => {
+ assert.deepEqual([...SOURCE_VIDEO_QUALITIES], ["original", "video_720"]);
+ assert.equal(ORIGINAL_SOURCE_FORMAT_SELECTOR, "bestvideo*+bestaudio/best");
+ assert.equal(sourceVideoFormatSelector("original"), "bestvideo*+bestaudio/best");
+ assert.equal(
+ sourceVideoFormatSelector("video_720"),
+ resolveDownloadFormatSelector("video_720", null),
+ );
+});
+
+test("resolveSourceVideoQuality: override beats channel beats global; original when unset", () => {
+ assert.equal(resolveSourceVideoQuality({}), "original");
+ assert.equal(resolveSourceVideoQuality({ global: "video_720" }), "video_720");
+ // The channel wins over the global, either way round.
+ assert.equal(
+ resolveSourceVideoQuality({ channel: "original", global: "video_720" }),
+ "original",
+ );
+ assert.equal(
+ resolveSourceVideoQuality({ channel: "video_720", global: "original" }),
+ "video_720",
+ );
+ assert.equal(
+ resolveSourceVideoQuality({ override: "original", channel: "video_720", global: "video_720" }),
+ "original",
+ );
+ // Junk at any level falls through to the next, never reaching yt-dlp.
+ assert.equal(
+ resolveSourceVideoQuality({ override: "1080p", channel: undefined, global: "video_720" }),
+ "video_720",
+ );
+ assert.equal(resolveSourceVideoQuality({ override: "", channel: 720 }), "original");
+ assert.ok(isSourceVideoQuality("video_720"));
+ assert.ok(!isSourceVideoQuality("bestvideo_audio"));
+});
diff --git a/common/ytdlp/downloadFormat.ts b/common/ytdlp/downloadFormat.ts
@@ -9,13 +9,20 @@ export type DownloadFormatPreset =
| "auto"
| "original"
| "bestaudio"
- | "bestvideo_audio";
+ | "bestvideo_audio"
+ | "video_720";
+
+// The clip/editing preset's id, named once for every caller that asks for it
+// by name (the persist control, the source-video quality setting, the batch
+// persist and the clip fetch).
+export const VIDEO_720_PRESET = "video_720" as const;
export const DOWNLOAD_FORMAT_PRESETS: ReadonlyArray<DownloadFormatPreset> = [
"auto",
"original",
"bestaudio",
"bestvideo_audio",
+ VIDEO_720_PRESET,
];
export function isDownloadFormatPreset(
@@ -33,8 +40,46 @@ export const DOWNLOAD_FORMAT_LABELS: Record<DownloadFormatPreset, string> = {
original: "Original (full file)",
bestaudio: "Best audio only",
bestvideo_audio: "Best video + audio",
+ video_720: "Video 720p (H.264, for clips/editing)",
};
+// THE FORMAT SELECTOR A CLIP IS CUT FROM, and why it is not "bestvideo_audio".
+//
+// "bestvideo_audio" answers "what should we ARCHIVE" — best available,
+// container-agnostic. This answers "what can ffmpeg cut and re-encode cheaply,
+// right now". Those differ on one specific trap: left alone yt-dlp picks
+// VP9+Opus at these heights, and since --force-keyframes-at-cuts re-encodes,
+// that means libvpx-vp9 — 27 s to cut a 5 s clip, measured — and it writes
+// .webm, which it then appends to the -o name. So H.264/AAC in mp4 is pinned,
+// with three progressively looser fallbacks. Byte-identical to the selector
+// umtool's build-video.mjs uses, on purpose: a window this fetches and a window
+// that fetched must be the same file, or the two caches diverge.
+//
+// Lives here (not in fetchWindowManaged.ts, which re-exports it) because the
+// "video_720" preset below is built from it, and a persist must not import the
+// clip-window fetch to learn a selector.
+export function clipFormatSelector(maxHeight: number): string {
+ return [
+ `bv*[vcodec^=avc1][height<=${maxHeight}]+ba[acodec^=mp4a]`,
+ `bv*[ext=mp4][height<=${maxHeight}]+ba[ext=m4a]`,
+ `b[ext=mp4][height<=${maxHeight}]`,
+ `b[height<=${maxHeight}]`,
+ ].join("/");
+}
+
+// The height "video_720" asks for, and the rung it falls back to.
+export const VIDEO_720_MAX_HEIGHT = 720;
+export const VIDEO_720_FALLBACK_HEIGHT = 480;
+
+// The last rung of "video_720": anything at all. Reached only when the source
+// has nothing the 720 and 480 rungs accept, so a file above 720p is possible —
+// the download logs it (downloadOneManaged, logPersistedFormat) rather than
+// letting it pass silently.
+export const LAST_RESORT_FORMAT_SELECTOR = "bv*+ba/b";
+
+// The source container a persist has always taken: best video + best audio.
+export const ORIGINAL_SOURCE_FORMAT_SELECTOR = "bestvideo*+bestaudio/best";
+
// Resolve a preset into the concrete yt-dlp `-f` selector string. The platform
// only matters for "auto"; explicit presets apply literally regardless of
// source (so an explicit choice always wins over the per-source default).
@@ -48,7 +93,13 @@ export function resolveDownloadFormatSelector(
case "bestaudio":
return "bestaudio/worst";
case "bestvideo_audio":
- return "bestvideo*+bestaudio/best";
+ return ORIGINAL_SOURCE_FORMAT_SELECTOR;
+ case "video_720":
+ return [
+ clipFormatSelector(VIDEO_720_MAX_HEIGHT),
+ clipFormatSelector(VIDEO_720_FALLBACK_HEIGHT),
+ LAST_RESORT_FORMAT_SELECTOR,
+ ].join("/");
case "auto":
default:
return platform === "odysee"
@@ -66,3 +117,60 @@ export function resolveDownloadFormatPreset(opts: {
}): DownloadFormatPreset {
return opts.override ?? opts.channel ?? opts.global ?? "auto";
}
+
+// ── SOURCE-VIDEO QUALITY ───────────────────────────────────────────────────
+//
+// What a FULL-SOURCE persist downloads: "original" (best video + best audio,
+// the behaviour every persist had before this setting) or "video_720" (≤720p
+// H.264, for clip and editing work). A separate axis from DownloadFormatPreset:
+// that one picks what a download fetches to get AUDIO from; this one picks the
+// container a persist keeps. Set globally (settings `sourceVideoQuality`), per
+// channel (ChannelConfig.sourceVideoQuality) and per persist (the editor's
+// "Persist source video" control).
+export type SourceVideoQuality = "original" | typeof VIDEO_720_PRESET;
+
+export const SOURCE_VIDEO_QUALITIES: ReadonlyArray<SourceVideoQuality> = [
+ "original",
+ VIDEO_720_PRESET,
+];
+
+export const DEFAULT_SOURCE_VIDEO_QUALITY: SourceVideoQuality = "original";
+
+export const SOURCE_VIDEO_QUALITY_LABELS: Record<SourceVideoQuality, string> = {
+ original: "Original (best video + audio)",
+ video_720: DOWNLOAD_FORMAT_LABELS.video_720,
+};
+
+export function isSourceVideoQuality(
+ value: unknown,
+): value is SourceVideoQuality {
+ return (
+ typeof value === "string" &&
+ (SOURCE_VIDEO_QUALITIES as ReadonlyArray<string>).includes(value)
+ );
+}
+
+// The `-f` selector a persist at this quality passes. "original" is the
+// selector a persist has always used, byte for byte.
+export function sourceVideoFormatSelector(quality: SourceVideoQuality): string {
+ return quality === VIDEO_720_PRESET
+ ? resolveDownloadFormatSelector(VIDEO_720_PRESET, null)
+ : ORIGINAL_SOURCE_FORMAT_SELECTOR;
+}
+
+// THE ONE EFFECTIVE-QUALITY RESOLVER. Every surface that persists a full
+// source — the persist control, the whole-recording fetch, "Persist kept now",
+// the batch persist — asks this, so a channel's choice cannot win on one and
+// lose on another. Per-persist override beats the channel beats the global;
+// "original" when nothing is set. Each input is checked, so a stale or
+// hand-typed value falls through to the next rather than reaching yt-dlp.
+export function resolveSourceVideoQuality(opts: {
+ override?: unknown;
+ channel?: unknown;
+ global?: unknown;
+}): SourceVideoQuality {
+ for (const v of [opts.override, opts.channel, opts.global]) {
+ if (isSourceVideoQuality(v)) return v;
+ }
+ return DEFAULT_SOURCE_VIDEO_QUALITY;
+}
diff --git a/common/ytdlp/fetchWindowManaged.ts b/common/ytdlp/fetchWindowManaged.ts
@@ -42,31 +42,16 @@ import {
import { channelExtraArgs } from "./channelArgs";
import { runOneYtdlp } from "./runOneYtdlp";
import { FULL_LOG_PROGRESS_ARGS } from "./downloadOneManaged";
+import { clipFormatSelector } from "./downloadFormat";
// The source height a clip is worth fetching at. 720 is umtool's number and the
// reason is the render: the deliverable is 1080p with a clip inset, so pixels
// above 720 are thrown away after paying for them.
export const DEFAULT_CLIP_MAX_HEIGHT = 720;
-// THE FORMAT SELECTOR, and why it is not downloadFormatPreset's.
-//
-// resolveDownloadFormatSelector answers "what should we ARCHIVE" — best
-// available, container-agnostic. This answers "what can ffmpeg cut and
-// re-encode cheaply, right now". Those differ on one specific trap: left alone
-// yt-dlp picks VP9+Opus at these heights, and since --force-keyframes-at-cuts
-// re-encodes, that means libvpx-vp9 — 27 s to cut a 5 s clip, measured — and it
-// writes .webm, which it then appends to the -o name. So H.264/AAC in mp4 is
-// pinned, with three progressively looser fallbacks. Byte-identical to the
-// selector umtool's build-video.mjs uses, on purpose: a window this fetches and
-// a window that fetched must be the same file, or the two caches diverge.
-export function clipFormatSelector(maxHeight: number): string {
- return [
- `bv*[vcodec^=avc1][height<=${maxHeight}]+ba[acodec^=mp4a]`,
- `bv*[ext=mp4][height<=${maxHeight}]+ba[ext=m4a]`,
- `b[ext=mp4][height<=${maxHeight}]`,
- `b[height<=${maxHeight}]`,
- ].join("/");
-}
+// The clip format selector lives with the download presets (the "video_720"
+// preset is built from it); re-exported so existing importers keep working.
+export { clipFormatSelector };
export type FetchWindowProvenance = {
requestedBy: string;