commit 743deb3cabab054800cbe60ed24fa09c79275f51
parent 0e84914b7fd62b967973d8b315704ffba3d483ee
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Wed, 7 Oct 2026 17:56:36 -0400
fetch-windows: a window's own URL picks its queue, not its channel's
A channel whose URL is no platform's (community-notes) holding Rumble videos
grouped them on platform:unknown, so a second Rumble job would run beside the
first, each pacing only itself. Seen on the jeralyzer-private dry run.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/editor/app/api/ops/fetch-windows/route.test.ts b/editor/app/api/ops/fetch-windows/route.test.ts
@@ -19,7 +19,7 @@ process.env.SETTINGS_FILE = path.join(ROOT, "settings.json");
const { POST } = await import("./route");
test.after(() => rm(ROOT, { recursive: true, force: true }));
-// Two channels on two platforms, one video each, the YouTube one with a window
+// Channels on two platforms, one video each, the YouTube one with a window
// already on disk.
const CH = path.join(ROOT, "channels");
async function channel(slug: string, url: string, id: string, webpage: string) {
@@ -35,6 +35,8 @@ async function channel(slug: string, url: string, id: string, webpage: string) {
}
await channel("yt-chan", "https://www.youtube.com/@yt", "vid1", "https://www.youtube.com/watch?v=vid1");
await channel("rb-chan", "https://rumble.com/c/rb", "rb1", "https://rumble.com/rb1-x.html");
+// A channel whose own URL is no platform's, holding a Rumble video.
+await channel("mix-chan", "https://example.test/mix", "rb2", "https://rumble.com/rb2-y.html");
await mkdir(path.join(CH, "yt-chan", "data", "vid1", "clips"), { recursive: true });
await writeFile(path.join(CH, "yt-chan", "data", "vid1", "clips", "0.00-60.00.mp4"), "mp4");
@@ -103,6 +105,7 @@ test("a dry run answers the cache, groups by platform queue, and names what it c
item({ from: 100, to: 110 }),
item({ from: 100, to: 110 }), // a duplicate collapses
item({ slug: "rb-chan", id: "rb1", from: 5, to: 15 }),
+ item({ slug: "mix-chan", id: "rb2", from: 5, to: 15 }),
item({ slug: "no-such", id: "x", from: 5, to: 15 }),
],
});
@@ -119,8 +122,11 @@ test("a dry run answers the cache, groups by platform queue, and names what it c
assert.deepEqual(j.cached.map((c) => c.from), [10]);
assert.deepEqual(
j.groups.map((g) => [g.platform, g.items.length]).sort(),
- [["rumble", 1], ["youtube", 1]],
+ [["rumble", 2], ["youtube", 1]],
);
+ // The window's own URL picks the queue: the mix channel's Rumble video joins
+ // the Rumble job rather than starting a second one beside it.
+ assert.deepEqual(j.groups.map((g) => g.queueKey).sort(), ["platform:rumble", "platform:youtube"]);
const yt = j.groups.find((g) => g.platform === "youtube")!;
assert.equal(yt.items[0].webpageUrl, "https://www.youtube.com/watch?v=vid1");
assert.equal(j.unresolved.length, 1);
diff --git a/editor/app/channels/[slug]/videos/fetchWindowsAction.ts b/editor/app/channels/[slug]/videos/fetchWindowsAction.ts
@@ -5,11 +5,8 @@ import { getPaths } from "yt-dlp-transcript-common/lib/paths";
import { getSettings } from "yt-dlp-transcript-common/lib/settings";
import { diskGate } from "yt-dlp-transcript-common/lib/diskSpace";
import { formatBytes } from "yt-dlp-transcript-common/lib/format";
-import { detectPlatform } from "yt-dlp-transcript-common/lib/platform";
-import {
- downloadQueueKey,
- resolveQueueKey,
-} from "yt-dlp-transcript-common/lib/queueKeys";
+import { resolveQueueKey } from "yt-dlp-transcript-common/lib/queueKeys";
+import { detectPlatform, queueKeyForUrl } from "yt-dlp-transcript-common/lib/platform";
import {
MAX_CLIP_WINDOW_SECONDS,
isFetchMaxHeight,
@@ -48,7 +45,8 @@ import { safeRevalidate } from "../../../lib/safeRevalidate";
// The batch form of fetchWindowAction (videos/[id]/videoActions.ts), with the
// same rules at the door: a window of at most MAX_CLIP_WINDOW_SECONDS, a height
// cap in range, a channel that exists, a URL that resolves. What it adds is the
-// fan-out: the list is grouped by `downloadQueueKey` and each group starts its
+// fan-out: the list is grouped by the queue of each window's own URL
+// (`queueKeyForUrl`) and each group starts its
// own job on that queue, so YouTube and Rumble run side by side, each behind
// its own platform's other downloads — persistVideosAction's shape.
//
@@ -164,7 +162,11 @@ async function planFetchWindows(
});
continue;
}
- const queueKey = resolveQueueKey(downloadQueueKey(config), queueOverride);
+ // THE WINDOW'S OWN URL decides the queue, not the channel's: a channel
+ // whose URL is no platform's (a curated mix) can hold Rumble videos, and
+ // its queue would put a second Rumble job beside the first, each pacing
+ // only itself.
+ const queueKey = resolveQueueKey(queueKeyForUrl(url), queueOverride);
const platform = detectPlatform(url) ?? "unknown";
// One job per queue. Two platforms sharing a queue (an override) share a
// job too; the controller keys the cooldown per item, so each is honoured.