diff --git a/js/nostr/client.js b/js/nostr/client.js index 276e1912..c9abd471 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -2521,96 +2521,29 @@ export class NostrClient { throw new Error("You do not own this video (pubkey mismatch)."); } - const { - event, - videoRootId - } = prepareVideoEditPayload({ + const context = prepareVideoEditPayload({ baseEvent, originalEventStub, updatedData, userPubkey, - resolveEventDTag: (evt, stub) => this.resolveEventDTag(evt, stub) + resolveEventDTag: (evt, stub) => this.resolveEventDTag(evt, stub), }); - devLogger.log("Creating edited event with root ID:", videoRootId); - devLogger.log("Event content:", event.content); + devLogger.log("Creating edited event with root ID:", context.videoRootId); + devLogger.log("Event content:", context.event.content); await this.ensureActiveSignerForPubkey(userPubkeyLower); - const signer = this.signerManager.resolveActiveSigner(userPubkeyLower); - if (!signer || typeof signer.signEvent !== "function") { - const error = new Error( - "An active signer with signEvent support is required to edit videos.", - ); - error.code = "nostr-extension-missing"; - throw error; - } - - if (shouldRequestExtensionPermissions(signer)) { - const permissionResult = await this.ensureExtensionPermissions( - DEFAULT_NIP07_CORE_METHODS, - ); - if (!permissionResult.ok) { - userLogger.warn( - "[nostr] Signer permissions denied while editing a video.", - permissionResult.error, - ); - const error = new Error( - "The active signer must allow signing before editing a video.", - ); - error.code = "extension-permission-denied"; - error.cause = permissionResult.error; - throw error; - } - } - try { - const signedEvent = await queueSignEvent(signer, event); - devLogger.log("Signed edited event:", signedEvent); + const { signedEvent } = await this.signAndPublishEvent(context.event, { + context: "edited video note", + logName: "Edited video note", + devLogLabel: "edited video note", + resolveActiveSigner: (p) => this.signerManager.resolveActiveSigner(p), + }); - const publishResults = await publishEventToRelays( - this.pool, - this.relays, - signedEvent - ); - - let publishSummary; - try { - publishSummary = assertAnyRelayAccepted(publishResults, { - context: "edited video note", - }); - } catch (publishError) { - if (publishError?.relayFailures?.length) { - publishError.relayFailures.forEach( - ({ url, error: relayError, reason }) => { - userLogger.error( - `[nostr] Edited video rejected by ${url}: ${reason}`, - relayError || reason - ); - } - ); - } - throw publishError; - } - - publishSummary.accepted.forEach(({ url }) => - devLogger.log(`Edited video published to ${url}`) - ); - - if (publishSummary.failed.length) { - publishSummary.failed.forEach(({ url, error: relayError }) => { - const reason = - relayError instanceof Error - ? relayError.message - : relayError - ? String(relayError) - : "publish failed"; - userLogger.warn( - `[nostr] Edited video not accepted by ${url}: ${reason}`, - relayError - ); - }); - } + await handlePublishNip94(this, signedEvent, context); + await handlePublishNip71(this, signedEvent, context); return signedEvent; } catch (err) { diff --git a/js/nostr/videoPayloadBuilder.js b/js/nostr/videoPayloadBuilder.js index a54ff2f5..2c5e2524 100644 --- a/js/nostr/videoPayloadBuilder.js +++ b/js/nostr/videoPayloadBuilder.js @@ -457,9 +457,11 @@ export function prepareVideoEditPayload(params) { ? [["s", storagePointer], ...nip71Tags] : nip71Tags; + const createdAt = Math.floor(Date.now() / 1000); + const event = buildVideoPostEvent({ pubkey: userPubkeyLower, - created_at: Math.floor(Date.now() / 1000), + created_at: createdAt, dTagValue: finalDTagValue, content: contentObject, additionalTags, @@ -472,6 +474,18 @@ export function prepareVideoEditPayload(params) { wantPrivate, videoRootId: oldRootId, dTagValue: finalDTagValue, - userPubkeyLower + normalizedPubkey: userPubkeyLower, + userPubkeyLower, + createdAt, + finalUrl, + finalMagnet, + finalThumbnail: contentObject.thumbnail, + finalDescription: contentObject.description, + finalTitle: contentObject.title, + mimeType: updatedData.mimeType || "", + fileSha256, + originalFileSha256, + videoData: updatedData, + videoPayload: updatedData, }; } diff --git a/tests/nostr/edit-video-dtag.test.mjs b/tests/nostr/edit-video-dtag.test.mjs index edc15ce1..40407370 100644 --- a/tests/nostr/edit-video-dtag.test.mjs +++ b/tests/nostr/edit-video-dtag.test.mjs @@ -83,8 +83,8 @@ test("editVideo preserves existing d tag", async (t) => { const result = await client.editVideo({ id: baseEvent.id }, { title: "Updated" }, pubkey); assert.ok(result, "editVideo should return the signed event"); - assert.equal(signedEvents.length, 1, "editVideo should sign exactly one event"); - const signedEvent = signedEvents[0]; + assert.ok(signedEvents.length >= 1, "editVideo should sign at least one event"); + const signedEvent = signedEvents.find((e) => e.kind === 30078) || signedEvents[0]; assert.ok(signedEvent, "signed event should be captured"); const dTags = signedEvent.tags.filter((tag) => Array.isArray(tag) && tag[0] === "d"); assert.deepEqual(dTags, [["d", existingD]], "editVideo should preserve the existing d tag"); @@ -147,8 +147,8 @@ test("editVideo falls back to base event id when d tag missing", async (t) => { const result = await client.editVideo({ id: baseEvent.id }, { title: "Updated" }, pubkey); assert.ok(result, "editVideo should return the signed event"); - assert.equal(signedEvents.length, 1, "editVideo should sign exactly one event"); - const signedEvent = signedEvents[0]; + assert.ok(signedEvents.length >= 1, "editVideo should sign at least one event"); + const signedEvent = signedEvents.find((e) => e.kind === 30078) || signedEvents[0]; assert.ok(signedEvent, "signed event should be captured"); const dTags = signedEvent.tags.filter((tag) => Array.isArray(tag) && tag[0] === "d"); assert.deepEqual(