mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
299 lines
8.8 KiB
JavaScript
299 lines
8.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
buildNip71VideoEvent,
|
|
extractNip71MetadataFromTags,
|
|
} from "../js/nostr.js";
|
|
|
|
const participantNpub = "npub1bitvidparticipantfixture";
|
|
const participantHexFromNpub = "c".repeat(64);
|
|
|
|
const hadWindow = typeof globalThis.window !== "undefined";
|
|
const windowRef = hadWindow ? globalThis.window : {};
|
|
if (!hadWindow) {
|
|
globalThis.window = windowRef;
|
|
}
|
|
const hadNostrTools = typeof windowRef.NostrTools !== "undefined";
|
|
const nostrToolsRef = hadNostrTools ? windowRef.NostrTools : {};
|
|
if (!hadNostrTools) {
|
|
windowRef.NostrTools = nostrToolsRef;
|
|
}
|
|
const previousNip19 = nostrToolsRef.nip19;
|
|
nostrToolsRef.nip19 = {
|
|
...previousNip19,
|
|
decode: (value) => {
|
|
if (value === participantNpub) {
|
|
return { type: "npub", data: participantHexFromNpub };
|
|
}
|
|
if (typeof previousNip19?.decode === "function") {
|
|
return previousNip19.decode(value);
|
|
}
|
|
throw new Error("Unsupported npub fixture");
|
|
},
|
|
};
|
|
|
|
test.after(() => {
|
|
if (previousNip19) {
|
|
nostrToolsRef.nip19 = previousNip19;
|
|
} else {
|
|
delete nostrToolsRef.nip19;
|
|
}
|
|
if (!hadNostrTools) {
|
|
delete windowRef.NostrTools;
|
|
}
|
|
if (!hadWindow) {
|
|
delete globalThis.window;
|
|
}
|
|
});
|
|
|
|
test("buildNip71VideoEvent assembles rich metadata", () => {
|
|
const metadata = {
|
|
kind: 21,
|
|
summary: "Custom summary",
|
|
publishedAt: "1700000000",
|
|
alt: "Alt text for accessibility",
|
|
duration: 123.456,
|
|
contentWarning: "Flashing lights",
|
|
imeta: [
|
|
{
|
|
dim: "1920x1080",
|
|
url: "https://cdn.example/video-1080.mp4",
|
|
x: "abc123",
|
|
m: "video/mp4",
|
|
duration: 123.456,
|
|
bitrate: 4500000,
|
|
image: [
|
|
"https://cdn.example/video-1080.jpg",
|
|
"https://backup.example/video-1080.jpg",
|
|
],
|
|
fallback: ["https://fallback.example/video-1080.mp4"],
|
|
service: ["nip96"],
|
|
},
|
|
{
|
|
url: "https://cdn.example/video-hls.m3u8",
|
|
m: "application/x-mpegURL",
|
|
duration: "45.789",
|
|
bitrate: "2200000",
|
|
},
|
|
],
|
|
textTracks: [
|
|
{
|
|
url: "https://cdn.example/captions-en.vtt",
|
|
type: "captions",
|
|
language: "en",
|
|
},
|
|
{
|
|
url: "https://cdn.example/captions-es.vtt",
|
|
language: "es",
|
|
},
|
|
],
|
|
segments: [
|
|
{ start: "00:00:00", end: "00:01:00", title: "Intro" },
|
|
{ start: 120, title: "Middle" },
|
|
],
|
|
hashtags: ["bitvid", "nostr"],
|
|
participants: [
|
|
{ pubkey: "A".repeat(64), relay: "wss://relay.example" },
|
|
{ pubkey: ` ${participantNpub} ` },
|
|
],
|
|
references: [
|
|
"https://example.com/info",
|
|
"https://example.com/docs",
|
|
],
|
|
};
|
|
|
|
const event = buildNip71VideoEvent({
|
|
metadata,
|
|
pubkey: "f".repeat(64),
|
|
title: "Test Video",
|
|
summaryFallback: "Fallback summary",
|
|
createdAt: 42,
|
|
});
|
|
|
|
assert.ok(event, "builder should return an event");
|
|
assert.equal(event.kind, 21, "kind should honor metadata");
|
|
assert.equal(event.pubkey, "f".repeat(64));
|
|
assert.equal(event.created_at, 42);
|
|
assert.equal(event.content, "Custom summary");
|
|
|
|
const tags = new Map(event.tags.map((tag) => [tag[0], tag]));
|
|
assert.deepEqual(tags.get("title"), ["title", "Test Video"]);
|
|
assert.deepEqual(tags.get("published_at"), ["published_at", "1700000000"]);
|
|
assert.deepEqual(tags.get("alt"), ["alt", "Alt text for accessibility"]);
|
|
assert.ok(!tags.has("duration"), "duration should no longer be emitted as a top-level tag");
|
|
assert.deepEqual(tags.get("content-warning"), [
|
|
"content-warning",
|
|
"Flashing lights",
|
|
]);
|
|
|
|
const imetaTags = event.tags.filter((tag) => tag[0] === "imeta");
|
|
assert.equal(imetaTags.length, 2, "should include both imeta variants");
|
|
assert.deepEqual(imetaTags[0], [
|
|
"imeta",
|
|
"dim 1920x1080",
|
|
"url https://cdn.example/video-1080.mp4",
|
|
"x abc123",
|
|
"m video/mp4",
|
|
"duration 123.456",
|
|
"bitrate 4500000",
|
|
"image https://cdn.example/video-1080.jpg",
|
|
"image https://backup.example/video-1080.jpg",
|
|
"fallback https://fallback.example/video-1080.mp4",
|
|
"service nip96",
|
|
]);
|
|
assert.deepEqual(imetaTags[1], [
|
|
"imeta",
|
|
"url https://cdn.example/video-hls.m3u8",
|
|
"m application/x-mpegurl",
|
|
"duration 45.789",
|
|
"bitrate 2200000",
|
|
]);
|
|
|
|
const textTrackTags = event.tags.filter((tag) => tag[0] === "text-track");
|
|
assert.deepEqual(textTrackTags, [
|
|
[
|
|
"text-track",
|
|
"https://cdn.example/captions-en.vtt",
|
|
"captions",
|
|
"en",
|
|
],
|
|
["text-track", "https://cdn.example/captions-es.vtt", "", "es"],
|
|
]);
|
|
|
|
const segmentTags = event.tags.filter((tag) => tag[0] === "segment");
|
|
assert.deepEqual(segmentTags, [
|
|
["segment", "00:00:00", "00:01:00", "Intro"],
|
|
["segment", "120", "", "Middle"],
|
|
]);
|
|
|
|
const hashtagTags = event.tags.filter((tag) => tag[0] === "t");
|
|
assert.deepEqual(hashtagTags, [
|
|
["t", "bitvid"],
|
|
["t", "nostr"],
|
|
]);
|
|
|
|
const participantTags = event.tags.filter((tag) => tag[0] === "p");
|
|
assert.deepEqual(participantTags, [
|
|
["p", "a".repeat(64), "wss://relay.example"],
|
|
["p", participantHexFromNpub],
|
|
]);
|
|
|
|
const referenceTags = event.tags.filter((tag) => tag[0] === "r");
|
|
assert.deepEqual(referenceTags, [
|
|
["r", "https://example.com/info"],
|
|
["r", "https://example.com/docs"],
|
|
]);
|
|
});
|
|
|
|
test("buildNip71VideoEvent falls back to summary and selects kind", () => {
|
|
const metadata = {
|
|
kind: "22",
|
|
publishedAt: "2024-01-01T12:00:00Z",
|
|
imeta: [{ url: "https://cdn.example/video.mp4" }],
|
|
};
|
|
|
|
const event = buildNip71VideoEvent({
|
|
metadata,
|
|
pubkey: "c".repeat(64),
|
|
title: "Fallback Title",
|
|
summaryFallback: "Fallback description",
|
|
createdAt: 100,
|
|
});
|
|
|
|
assert.ok(event, "builder should create minimal event");
|
|
assert.equal(event.kind, 22, "string kind should coerce to number");
|
|
const expectedPublished = String(Math.floor(Date.parse("2024-01-01T12:00:00Z") / 1000));
|
|
assert.deepEqual(event.tags.find((tag) => tag[0] === "published_at"), [
|
|
"published_at",
|
|
expectedPublished,
|
|
]);
|
|
assert.equal(
|
|
event.content,
|
|
"Fallback description",
|
|
"should use fallback summary when metadata summary missing"
|
|
);
|
|
});
|
|
|
|
test("buildNip71VideoEvent attaches pointer tags", () => {
|
|
const metadata = {
|
|
kind: 21,
|
|
summary: "Pointer test",
|
|
imeta: [{ url: "https://cdn.example/clip.mp4" }],
|
|
};
|
|
|
|
const pubkey = "f".repeat(64);
|
|
const pointerIdentifiers = {
|
|
videoRootId: "root-123",
|
|
eventId: "event-456",
|
|
dTag: "pointer-d-tag",
|
|
};
|
|
|
|
const event = buildNip71VideoEvent({
|
|
metadata,
|
|
pubkey,
|
|
title: "Pointer Demo",
|
|
pointerIdentifiers,
|
|
});
|
|
|
|
assert.ok(event, "event should be built with pointer tags");
|
|
const pointerTags = event.tags.filter((tag) =>
|
|
["a", "video-root", "e", "d"].includes(tag[0])
|
|
);
|
|
assert.deepEqual(pointerTags, [
|
|
["a", `30078:${pubkey}:${pointerIdentifiers.videoRootId}`],
|
|
["video-root", pointerIdentifiers.videoRootId],
|
|
["e", pointerIdentifiers.eventId],
|
|
["d", pointerIdentifiers.dTag],
|
|
]);
|
|
});
|
|
|
|
test("extractNip71MetadataFromTags parses metadata and pointers", () => {
|
|
const event = {
|
|
id: "nip71-event",
|
|
kind: 22,
|
|
created_at: 123,
|
|
content: "Summary text",
|
|
tags: [
|
|
["title", "Metadata Title"],
|
|
["published_at", "1700000100"],
|
|
["alt", "alt text"],
|
|
["content-warning", "Bright lights"],
|
|
[
|
|
"imeta",
|
|
"url https://cdn.example/video.mp4",
|
|
"m video/mp4",
|
|
"duration 321.125",
|
|
"bitrate 2500000",
|
|
],
|
|
["text-track", "https://cdn.example/captions.vtt", "captions", "en"],
|
|
["segment", "00:00", "00:10", "Intro", "https://cdn.example/thumb.jpg"],
|
|
["t", "nostr"],
|
|
["p", "c".repeat(64), "wss://relay.example"],
|
|
["r", "https://example.com/info"],
|
|
["a", "30078:deadbeef:root-123"],
|
|
["video-root", "root-123"],
|
|
["e", "event-456"],
|
|
["d", "d-pointer"],
|
|
],
|
|
};
|
|
|
|
const parsed = extractNip71MetadataFromTags(event);
|
|
assert.ok(parsed, "parser should produce a result");
|
|
assert.deepEqual(parsed.metadata.title, "Metadata Title");
|
|
assert.deepEqual(parsed.metadata.summary, "Summary text");
|
|
assert.equal(parsed.metadata.duration, 321.125);
|
|
assert.equal(parsed.metadata.alt, "alt text");
|
|
assert.equal(parsed.metadata.contentWarning, "Bright lights");
|
|
assert.deepEqual(parsed.metadata.hashtags, ["nostr"]);
|
|
assert.deepEqual(parsed.metadata.references, ["https://example.com/info"]);
|
|
assert.equal(parsed.metadata.textTracks.length, 1);
|
|
assert.equal(parsed.metadata.imeta.length, 1);
|
|
assert.equal(parsed.metadata.imeta[0].duration, 321.125);
|
|
assert.equal(parsed.metadata.imeta[0].bitrate, 2500000);
|
|
assert(parsed.pointers.videoRootIds.has("root-123"));
|
|
assert(parsed.pointers.videoEventIds.has("event-456"));
|
|
assert(parsed.pointers.dTags.has("d-pointer"));
|
|
assert(parsed.pointers.pointerValues.has("30078:deadbeef:root-123"));
|
|
});
|