mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-08 13:52:58 +00:00
Deep dive into the "nothing loads / Fetching recent videos… hangs" symptom found the real blocker: it was NOT the moderation/WoT stage (that uses cached reads and passes unknown authors through clean). It was the feed's resolve-posted-at stage. resolveVideoPostedAt() short-circuits when a video has nip71.publishedAt, otherwise it does a per-video `nostrClient.hydrateVideoHistory()` network fetch (looking up kind-30078 history). Foreign NIP-71 videos have no such history, and the ingest adapter wasn't setting nip71.publishedAt — so a burst of ~32 ingested videos triggered ~32 blocking history fetches that stalled the render and flooded relays. Fix: the ingest adapter now surfaces nip71.publishedAt from the NIP-71 published_at tag (falling back to created_at). The feed short-circuits and never fetches history for ingested videos. Re-enables FEATURE_NIP71_INGEST by default now that the hang is addressed. Tests: adapter published_at extraction + created_at fallback; mutation-verified. 1237/1238 unit (known UploadModal flake). Co-Authored-By: Claude Opus 4.8 <[email protected]>
162 lines
5.2 KiB
JavaScript
162 lines
5.2 KiB
JavaScript
// Foreign NIP-71 video event -> bitvid video object adapter. Scenario-style:
|
|
// asserts the externally observable shape a feed item needs, and the safety
|
|
// behaviors (own-mirror skip, content-warning -> isNsfw, source provenance).
|
|
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {
|
|
buildVideoFromNip71Event,
|
|
isBitvidMirrorEvent,
|
|
} from "../js/nostr/nip71IngestAdapter.js";
|
|
|
|
const SHA = "a".repeat(64);
|
|
|
|
function foreignEvent(overrides = {}) {
|
|
return {
|
|
id: "evt-1",
|
|
pubkey: "pk-foreign",
|
|
kind: 21,
|
|
created_at: 1_700_000_000,
|
|
content: "A description from another client",
|
|
tags: [
|
|
["title", "Foreign Video"],
|
|
[
|
|
"imeta",
|
|
"url https://example.com/v.mp4",
|
|
"m video/mp4",
|
|
"dim 1920x1080",
|
|
"image https://example.com/thumb.jpg",
|
|
`x ${SHA}`,
|
|
],
|
|
["t", "music"],
|
|
["duration", "240"],
|
|
],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test("maps a foreign NIP-71 event to a playable bitvid video", () => {
|
|
const v = buildVideoFromNip71Event(foreignEvent());
|
|
assert.equal(v.invalid, false);
|
|
assert.equal(v.title, "Foreign Video");
|
|
assert.equal(v.url, "https://example.com/v.mp4");
|
|
assert.equal(v.thumbnail, "https://example.com/thumb.jpg");
|
|
assert.equal(v.description, "A description from another client");
|
|
assert.equal(v.width, 1920);
|
|
assert.equal(v.height, 1080);
|
|
assert.equal(v.duration, 240);
|
|
assert.deepEqual(v.hashtags, ["music"]);
|
|
assert.equal(v.fileSha256, SHA);
|
|
assert.equal(v.pubkey, "pk-foreign");
|
|
// Provenance markers so the app can distinguish ingested content.
|
|
assert.equal(v.source, "nip71-ingest");
|
|
assert.equal(v.foreign, true);
|
|
assert.equal(v.nip71Kind, 21);
|
|
// Never treat foreign content as private (no encryption in NIP-71).
|
|
assert.equal(v.isPrivate, false);
|
|
});
|
|
|
|
test("surfaces nip71.publishedAt so the feed skips per-video history fetches", () => {
|
|
// The published_at tag becomes nip71.publishedAt (feed resolve-posted-at uses
|
|
// it to avoid a blocking kind-30078 history fetch per ingested video).
|
|
const withPublished = buildVideoFromNip71Event(
|
|
foreignEvent({
|
|
tags: [
|
|
["title", "Has published_at"],
|
|
["published_at", "1700000123"],
|
|
["imeta", "url https://example.com/v.mp4", "m video/mp4"],
|
|
],
|
|
}),
|
|
);
|
|
assert.equal(withPublished.nip71.publishedAt, 1700000123);
|
|
|
|
// Falls back to created_at when there's no published_at tag.
|
|
const noPublished = buildVideoFromNip71Event(foreignEvent({ created_at: 1699999999 }));
|
|
assert.equal(noPublished.nip71.publishedAt, 1699999999);
|
|
});
|
|
|
|
test("content-warning maps to isNsfw so the NSFW gate applies", () => {
|
|
const clean = buildVideoFromNip71Event(foreignEvent());
|
|
assert.equal(clean.isNsfw, false);
|
|
|
|
const flagged = buildVideoFromNip71Event(
|
|
foreignEvent({
|
|
tags: [
|
|
["title", "Spicy"],
|
|
["imeta", "url https://example.com/v.mp4", "m video/mp4"],
|
|
["content-warning", "explicit"],
|
|
],
|
|
}),
|
|
);
|
|
assert.equal(flagged.isNsfw, true);
|
|
});
|
|
|
|
test("addressable kinds key the root on the d-tag; regular kinds on the event id", () => {
|
|
const regular = buildVideoFromNip71Event(foreignEvent({ id: "regular-id", kind: 22 }));
|
|
assert.equal(regular.videoRootId, "regular-id");
|
|
|
|
const addressable = buildVideoFromNip71Event(
|
|
foreignEvent({
|
|
id: "addr-id",
|
|
kind: 34235,
|
|
tags: [
|
|
["d", "stable-root"],
|
|
["title", "Addr"],
|
|
["imeta", "url https://example.com/v.mp4", "m video/mp4"],
|
|
],
|
|
}),
|
|
);
|
|
assert.equal(addressable.videoRootId, "stable-root");
|
|
});
|
|
|
|
test("skips bitvid's own outbound mirrors (avoids duplicate listings)", () => {
|
|
const mirror = foreignEvent({
|
|
kind: 34235,
|
|
tags: [
|
|
["d", "root"],
|
|
["client", "bitvid"],
|
|
["title", "Our mirror"],
|
|
["imeta", "url https://example.com/v.mp4", "m video/mp4"],
|
|
],
|
|
});
|
|
assert.equal(isBitvidMirrorEvent(mirror), true);
|
|
const v = buildVideoFromNip71Event(mirror);
|
|
assert.equal(v.invalid, true);
|
|
assert.equal(v.reason, "bitvid-mirror");
|
|
});
|
|
|
|
test("rejects events with no playable source or no title", () => {
|
|
const noSource = buildVideoFromNip71Event(
|
|
foreignEvent({ tags: [["title", "No media"]] }),
|
|
);
|
|
assert.equal(noSource.invalid, true);
|
|
assert.equal(noSource.reason, "missing playable source");
|
|
|
|
const noTitle = buildVideoFromNip71Event(
|
|
foreignEvent({ tags: [["imeta", "url https://example.com/v.mp4", "m video/mp4"]] }),
|
|
);
|
|
assert.equal(noTitle.invalid, true);
|
|
assert.equal(noTitle.reason, "missing title");
|
|
});
|
|
|
|
test("rejects non-NIP-71 kinds", () => {
|
|
const v = buildVideoFromNip71Event(foreignEvent({ kind: 30078 }));
|
|
assert.equal(v.invalid, true);
|
|
assert.equal(v.reason, "not a NIP-71 video kind");
|
|
});
|
|
|
|
test("extracts a webtorrent magnet + infohash when present", () => {
|
|
const magnet = `magnet:?xt=urn:btih:${"b".repeat(40)}&dn=clip`;
|
|
const v = buildVideoFromNip71Event(
|
|
foreignEvent({
|
|
tags: [
|
|
["title", "Torrent clip"],
|
|
["imeta", `magnet ${magnet}`, "m video/mp4", `i ${"c".repeat(40)}`],
|
|
],
|
|
}),
|
|
);
|
|
assert.equal(v.invalid, false);
|
|
assert.equal(v.magnet, magnet);
|
|
assert.equal(v.infoHash, "c".repeat(40), "prefers the imeta `i` infohash field");
|
|
});
|