Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 4b8752ba088b72a72bdad8ee303f2be27810baf7
parent 08fef051b78888d31baeb4f784d9c91a1553df4c
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sun,  4 Oct 2026 16:09:37 -0400

common: sourceVideoQuality setting and per-channel override

Global settings.sourceVideoQuality (default original) and an optional
ChannelConfig.sourceVideoQuality; an invalid channel value is omitted, an
invalid global falls back to original. SETTINGS.md, CHANNEL.md and
settings.json.example regenerated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
MCHANNEL.md | 1+
MSETTINGS.md | 7+++++++
Mcommon/lib/channelConfig.ts | 6++++++
Mcommon/lib/channelConfigSchema.test.ts | 14+++++++++++++-
Mcommon/lib/channelConfigSchema.ts | 1+
Mcommon/lib/settingsSchema.test.ts | 17+++++++++++++++--
Mcommon/lib/settingsSchema.ts | 7+++++++
Msettings.json.example | 1+
8 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/CHANNEL.md b/CHANNEL.md @@ -23,6 +23,7 @@ Regenerate this file with `pnpm --filter yt-dlp-transcript-common exec tsx bin/f | `url` | config | The channel / playlist / account URL syncs enumerate. Absent = the channel is never auto-synced. | | `audioFormat` | config | `"m4a"`, `"mp3"` or `"opus"`: the audio a transcribe-handling download keeps. | | `downloadFormat` | config | Per-channel override for the yt-dlp `-f` download format preset. Absent = inherit the global `downloadFormat`, which itself falls back to the per-source "auto" selector. Lets a channel whose source serves full-length audio only in its `original` format (e.g. Odysee) force it. | +| `sourceVideoQuality` | config | Per-channel override for the quality of the source container a full persist keeps ("Persist source video", the whole-recording fetch, "Persist kept now"). `"original"` (best video + audio) or `"video_720"` (≤720p H.264, for clips/editing). Absent = inherit the global `sourceVideoQuality`. | | `keepSourceVideo` | config | Keep the downloaded source video beside the audio. | | `keepLatest` | config | Keep-latest window: the newest N videos (by upload date) are protected from the Clean-audio sweep AND have their source video persisted to the saved-video store. 0 or absent = disabled; positives clamp to [1, 100000]. A kept video later found deleted at the source is pinned permanently via the do-not-clean marker. | | `extractionMode` | config | `"ytdlp"` (default — yt-dlp's own `-x --audio-format` postprocessor, no source container kept) or `"app"` (yt-dlp downloads the source container and the app runs ffmpeg). The keep-latest persistence rule forces `"app"` for the videos it persists. | diff --git a/SETTINGS.md b/SETTINGS.md @@ -23,6 +23,7 @@ A copied example PINS every default it spells — including each lane's `autoQue | [`sleepBetweenDownloadsSeconds`](#sleepbetweendownloadsseconds) | `10` | | [`pacing`](#pacing) | object — see below | | [`downloadFormat`](#downloadformat) | `"auto"` | +| [`sourceVideoQuality`](#sourcevideoquality) | `"original"` | | [`minFreeDiskGB`](#minfreediskgb) | `5` | | [`resumeMarginGB`](#resumemargingb) | `2` | | [`parallelTranscriptions`](#paralleltranscriptions) | `2` | @@ -220,6 +221,12 @@ Default yt-dlp `-f` download format for every channel that doesn't set its own ( Default: `"auto"` +## `sourceVideoQuality` + +Quality of the source container a full persist keeps — "Persist source video", the whole-recording fetch (`full: true`) and "Persist kept now" — for every channel that doesn't set its own (ChannelConfig.sourceVideoQuality). "original" (default) = `bestvideo*+bestaudio/best`; "video_720" = ≤720p H.264/AAC mp4 for clip and editing work, falling back to 480p and then to anything (logged). A single persist can override it. See common/ytdlp/downloadFormat.ts. + +Default: `"original"` + ## `minFreeDiskGB` Minimum free disk space (GB) required on the transcripts data directory for downloads to run. When free space is below this floor, a download job is prevented from starting and a running batch stops launching new videos (the in-flight one finishes). 0 disables the gate. See common/lib/diskSpace.ts. diff --git a/common/lib/channelConfig.ts b/common/lib/channelConfig.ts @@ -1,7 +1,9 @@ import { PLATFORM_VALUES, type Platform } from "./platform"; import { isDownloadFormatPreset, + isSourceVideoQuality, type DownloadFormatPreset, + type SourceVideoQuality, } from "../ytdlp/downloadFormat"; import { isCookieMode, type CookieMode } from "./cookiePolicy"; import type { FieldDocs } from "./fieldDocs"; @@ -121,6 +123,7 @@ export type ChannelConfig = { url?: string; audioFormat?: AudioFormat; downloadFormat?: DownloadFormatPreset; + sourceVideoQuality?: SourceVideoQuality; keepSourceVideo?: boolean; keepLatest?: number; extractionMode?: ExtractionMode; @@ -160,6 +163,8 @@ export const CHANNEL_CONFIG_FIELD_DOCS: FieldDocs<ChannelConfig> = { audioFormat: '`"m4a"`, `"mp3"` or `"opus"`: the audio a transcribe-handling download keeps.', downloadFormat: "Per-channel override for the yt-dlp `-f` download format preset. Absent = inherit the global `downloadFormat`, which itself falls back to the per-source \"auto\" selector. Lets a channel whose source serves full-length audio only in its `original` format (e.g. Odysee) force it.", + sourceVideoQuality: + 'Per-channel override for the quality of the source container a full persist keeps ("Persist source video", the whole-recording fetch, "Persist kept now"). `"original"` (best video + audio) or `"video_720"` (≤720p H.264, for clips/editing). Absent = inherit the global `sourceVideoQuality`.', keepSourceVideo: "Keep the downloaded source video beside the audio.", keepLatest: "Keep-latest window: the newest N videos (by upload date) are protected from the Clean-audio sweep AND have their source video persisted to the saved-video store. 0 or absent = disabled; positives clamp to [1, 100000]. A kept video later found deleted at the source is pinned permanently via the do-not-clean marker.", @@ -366,6 +371,7 @@ export const CHANNEL_CONFIG_COERCIONS: { url: str, audioFormat: (v) => (v === "m4a" || v === "mp3" || v === "opus" ? v : undefined), downloadFormat: (v) => (isDownloadFormatPreset(v) ? v : undefined), + sourceVideoQuality: (v) => (isSourceVideoQuality(v) ? v : undefined), keepSourceVideo: bool, keepLatest: zeroOrClamped(1, KEEP_LATEST_MAX), extractionMode: (v) => (v === "ytdlp" || v === "app" ? v : undefined), diff --git a/common/lib/channelConfigSchema.test.ts b/common/lib/channelConfigSchema.test.ts @@ -39,7 +39,7 @@ test("one key list: docs = coercions = schema shape, sync-state keys inside it", assert.deepEqual(Object.keys(CHANNEL_CONFIG_COERCIONS), [...CHANNEL_CONFIG_KEYS]); assert.deepEqual(Object.keys(channelConfigObjectSchema.shape), [...CHANNEL_CONFIG_KEYS]); assert.deepEqual(Object.keys(CHANNEL_CONFIG_FIELD_DOCS), [...CHANNEL_CONFIG_KEYS]); - assert.equal(CHANNEL_CONFIG_KEYS.length, 30); + assert.equal(CHANNEL_CONFIG_KEYS.length, 31); assert.equal(sameKeys, true); assert.equal(fits, true); for (const k of CHANNEL_SYNC_STATE_KEYS) assert.ok(CHANNEL_CONFIG_KEYS.includes(k), k); @@ -69,6 +69,17 @@ test("an invalid optional key is OMITTED, not defaulted", () => { } }); +test("sourceVideoQuality: the two qualities parse, anything else is omitted", () => { + for (const q of ["original", "video_720"] as const) { + assert.equal(parse({ handling: "youtube", sourceVideoQuality: q })!.sourceVideoQuality, q); + assert.equal(parseChannelConfig({ handling: "youtube", sourceVideoQuality: q })!.sourceVideoQuality, q); + } + for (const junk of ["720", "bestvideo_audio", "", 720, null]) { + const cfg = parse({ handling: "youtube", sourceVideoQuality: junk })!; + assert.equal("sourceVideoQuality" in cfg, false, JSON.stringify(junk)); + } +}); + test("clamps: 0 is preserved as the disabled sentinel, positives clamp, fractions floor", () => { const cfg = parse({ handling: "transcribe", @@ -142,6 +153,7 @@ test("the zod schema and the client-safe parser agree, key order included", () = handling: ["youtube", "transcribe"], platform: ["youtube", "rumble", "nope"], cookieMode: ["always", "never", "nope"], + sourceVideoQuality: ["original", "video_720", "nope"], }; let seed = 7; const rand = () => ((seed = (seed * 1103515245 + 12345) % 2 ** 31) / 2 ** 31); diff --git a/common/lib/channelConfigSchema.ts b/common/lib/channelConfigSchema.ts @@ -52,6 +52,7 @@ export const channelConfigObjectSchema = z.object({ url: field("url"), audioFormat: field("audioFormat"), downloadFormat: field("downloadFormat"), + sourceVideoQuality: field("sourceVideoQuality"), keepSourceVideo: field("keepSourceVideo"), keepLatest: field("keepLatest"), extractionMode: field("extractionMode"), diff --git a/common/lib/settingsSchema.test.ts b/common/lib/settingsSchema.test.ts @@ -24,7 +24,10 @@ import type { Worker } from "./workers"; import type { AutoQueueSettings } from "./autoQueueTypes"; import type { ChannelPriority } from "./channelPriority"; import type { CookieMode } from "./cookiePolicy"; -import type { DownloadFormatPreset } from "../ytdlp/downloadFormat"; +import type { + DownloadFormatPreset, + SourceVideoQuality, +} from "../ytdlp/downloadFormat"; import type { StorageSettings } from "./storageLocations"; import type { AttributionSettings, @@ -70,6 +73,8 @@ type PreSchemaSiteSettings = { // Release 17 slice RL — the adaptive pace and the hold. pacing: PacingSettingsBlock; downloadFormat: DownloadFormatPreset; + // The source-video quality a full persist keeps. + sourceVideoQuality: SourceVideoQuality; minFreeDiskGB: number; resumeMarginGB: number; parallelTranscriptions: number; @@ -98,7 +103,7 @@ type PreSchemaSiteSettings = { type Same<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false; const shapeUnchanged: Same<SiteSettings, PreSchemaSiteSettings> = true; -test("SiteSettings keeps its 33 fields, in file order", () => { +test("SiteSettings keeps its 34 fields, in file order", () => { assert.equal(shapeUnchanged, true); assert.deepEqual(Object.keys(siteSettingsSchema.shape), [ "adminTitle", @@ -112,6 +117,7 @@ test("SiteSettings keeps its 33 fields, in file order", () => { "sleepBetweenDownloadsSeconds", "pacing", "downloadFormat", + "sourceVideoQuality", "minFreeDiskGB", "resumeMarginGB", "parallelTranscriptions", @@ -338,12 +344,19 @@ test("enum fields fall back to their default on junk", () => { const s = siteSettingsSchema.parse({ cookieMode: "sometimes", downloadFormat: "best", + sourceVideoQuality: "1080p", reportDebouncePreset: "instant", transcriptionApp: "no-such-app", }); const d = defaults(); assert.equal(s.cookieMode, d.cookieMode); assert.equal(s.downloadFormat, "auto"); + assert.equal(s.sourceVideoQuality, "original"); + assert.equal(d.sourceVideoQuality, "original"); + assert.equal( + siteSettingsSchema.parse({ sourceVideoQuality: "video_720" }).sourceVideoQuality, + "video_720", + ); assert.equal(s.reportDebouncePreset, d.reportDebouncePreset); assert.equal(s.transcriptionApp, d.transcriptionApp); }); diff --git a/common/lib/settingsSchema.ts b/common/lib/settingsSchema.ts @@ -44,8 +44,11 @@ import { } from "./settingsFieldSchemas"; import { SYNC_INTERVAL_MAX_MINUTES } from "./channelConfig"; import { + DEFAULT_SOURCE_VIDEO_QUALITY, isDownloadFormatPreset, + isSourceVideoQuality, type DownloadFormatPreset, + type SourceVideoQuality, } from "../ytdlp/downloadFormat"; import { type AppInstanceConfig, @@ -1566,6 +1569,10 @@ export const siteSettingsSchema = z.object({ isDownloadFormatPreset(v) ? v : "auto").describe( "Default yt-dlp `-f` download format for every channel that doesn't set its own (ChannelConfig.downloadFormat). \"auto\" picks per-source: `original` for Odysee (whose HLS rungs are CDN-truncated), `bestaudio/worst` elsewhere. See common/ytdlp/downloadFormat.ts.", ), + sourceVideoQuality: settingsField((v): SourceVideoQuality => + isSourceVideoQuality(v) ? v : DEFAULT_SOURCE_VIDEO_QUALITY).describe( + "Quality of the source container a full persist keeps — \"Persist source video\", the whole-recording fetch (`full: true`) and \"Persist kept now\" — for every channel that doesn't set its own (ChannelConfig.sourceVideoQuality). \"original\" (default) = `bestvideo*+bestaudio/best`; \"video_720\" = ≤720p H.264/AAC mp4 for clip and editing work, falling back to 480p and then to anything (logged). A single persist can override it. See common/ytdlp/downloadFormat.ts.", + ), minFreeDiskGB: settingsField((v): number => clampMinFreeDiskGB(v)).describe( "Minimum free disk space (GB) required on the transcripts data directory for downloads to run. When free space is below this floor, a download job is prevented from starting and a running batch stops launching new videos (the in-flight one finishes). 0 disables the gate. See common/lib/diskSpace.ts.", ), diff --git a/settings.json.example b/settings.json.example @@ -16,6 +16,7 @@ "holdProbeMinutes": 60 }, "downloadFormat": "auto", + "sourceVideoQuality": "original", "minFreeDiskGB": 5, "resumeMarginGB": 2, "parallelTranscriptions": 2,