diff --git a/docs/nostr-event-schemas.md b/docs/nostr-event-schemas.md index eb2feb11..5de8cf6d 100644 --- a/docs/nostr-event-schemas.md +++ b/docs/nostr-event-schemas.md @@ -163,7 +163,7 @@ import { updateWatchHistoryListWithDefaultClient } from "./nostrWatchHistoryFaca > **Publishing note:** Session actors only emit passive telemetry (for example, view counters) and must **not** sign video comments. Require a logged-in Nostr signer for comment publishing via [`commentEvents`](../js/nostr/commentEvents.js). -Video posts now include a required storage pointer tag (`['s', ':']`). The `` should resolve to the public storage base for the video assets (for example, a public bucket URL plus the object key without its extension). Clients derive `info.json` by appending `.info.json` to the prefix (or by combining a path-style prefix with the hosted URL origin if needed). +Video posts now include a required storage pointer tag (`['s', ':']`). The `buildVideoPostEvent` helper automatically generates this tag from available metadata (`infoHash`, `url`, or `videoRootId`) if it is omitted from the input. The `` should resolve to the public storage base for the video assets (for example, a public bucket URL plus the object key without its extension). Clients derive `info.json` by appending `.info.json` to the prefix (or by combining a path-style prefix with the hosted URL origin if needed). Video posts should treat `videoRootId` as the stable series identifier that remains unchanged across edits and deletes. File fingerprints such as `infoHash` or `fileSha256` live alongside the content payload so media swaps do not break the version chain. diff --git a/js/nostrEventSchemas.js b/js/nostrEventSchemas.js index 8a111d55..e01a3436 100644 --- a/js/nostrEventSchemas.js +++ b/js/nostrEventSchemas.js @@ -9,6 +9,10 @@ import { WATCH_HISTORY_LIST_IDENTIFIER, WATCH_HISTORY_VERSION_TAG_VALUE, } from "./config.js"; +import { + buildStoragePointerValue, + deriveStoragePointerFromUrl, +} from "./utils/storagePointer.js"; export const NOTE_TYPES = Object.freeze({ VIDEO_POST: "videoPost", @@ -889,6 +893,38 @@ export function buildVideoPostEvent(params) { tags.push(...sanitizedAdditionalTags.map((tag) => tag.slice())); } + const hasSTag = tags.some((t) => t[0] === "s"); + if (!hasSTag) { + let contentObj = content; + if (typeof content === "string") { + try { + contentObj = JSON.parse(content); + } catch (e) { + contentObj = {}; + } + } + contentObj = contentObj || {}; + + let pointer = ""; + if (contentObj.infoHash) { + pointer = buildStoragePointerValue({ + provider: "btih", + prefix: contentObj.infoHash, + }); + } else if (contentObj.url) { + pointer = deriveStoragePointerFromUrl(contentObj.url); + } + + if (!pointer && (dTagValue || contentObj.videoRootId)) { + const prefix = dTagValue || contentObj.videoRootId; + pointer = buildStoragePointerValue({ provider: "nostr", prefix }); + } + + if (pointer) { + tags.push(["s", pointer]); + } + } + let serializedContent = ""; if (typeof content === "string") { serializedContent = content; diff --git a/scripts/agent/validate-events.mjs b/scripts/agent/validate-events.mjs index dd4fe0bd..d104e29d 100644 --- a/scripts/agent/validate-events.mjs +++ b/scripts/agent/validate-events.mjs @@ -1,3 +1,10 @@ +/** + * Event Schema Validation Tool + * + * Scans the codebase for event builder usage and verifies that the output + * conforms to the defined schemas in `js/nostrEventSchemas.js`. + */ + import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto';