Merge pull request #2023 from PR0M3TH3AN/fix-webtorrent-webseed-regression-10274894627332192935

Fix WebTorrent webseed stream health check regression
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-01 09:14:43 -05:00
commit 5a67774011
32 files changed
+1165 -1058

No files matched your search

+1 -2
View File
@@ -3,7 +3,6 @@
import { signEventWithPrivateKey } from "../publishHelpers.js";
import { createPrivateKeyCipherClosures } from "../signerHelpers.js";
import { ensureNostrTools, getCachedNostrTools } from "../toolkit.js";
import { hexToBytes } from "../../utils/hex.js";
import { normalizeActorKey } from "../watchHistory.js";
const HEX64_REGEX = /^[0-9a-f]{64}$/i;
@@ -31,7 +30,7 @@ async function resolvePublicKey(privateKey, pubkey) {
const tools = (await ensureNostrTools()) || getCachedNostrTools();
if (tools?.getPublicKey && typeof tools.getPublicKey === "function") {
const derived = tools.getPublicKey(hexToBytes(privateKey));
const derived = tools.getPublicKey(privateKey);
const normalized = normalizeActorKey(derived);
if (normalized && HEX64_REGEX.test(normalized)) {
return normalized;
+17 -26
View File
@@ -130,7 +130,6 @@ import {
summarizeDmEventForLog,
} from "./dmDecryptDiagnostics.js";
import { devLogger, userLogger } from "../utils/logger.js";
import { hexToBytes } from "../utils/hex.js";
import { LRUCache } from "../utils/lruCache.js";
import { updateConversationFromMessage, writeMessages } from "../storage/dmDb.js";
import {
@@ -1630,7 +1629,7 @@ export class NostrClient {
if (!tools || typeof tools.getPublicKey !== "function") {
throw new Error("Public key derivation is unavailable for remote signing.");
}
publicKey = tools.getPublicKey(hexToBytes(privateKey));
publicKey = tools.getPublicKey(privateKey);
}
if (!publicKey || !HEX64_REGEX.test(publicKey)) {
@@ -2276,7 +2275,7 @@ export class NostrClient {
if (!tools || typeof tools.getPublicKey !== "function") {
throw new Error("Public key derivation is unavailable for the remote signer handshake.");
}
clientPublicKey = normalizeNostrPubkey(tools.getPublicKey(hexToBytes(clientPrivateKey)));
clientPublicKey = normalizeNostrPubkey(tools.getPublicKey(clientPrivateKey));
}
if (!clientPublicKey || !HEX64_REGEX.test(clientPublicKey)) {
@@ -3441,7 +3440,7 @@ export class NostrClient {
let pubkey = "";
try {
pubkey = getPublicKey(hexToBytes(normalizedPrivateKey));
pubkey = getPublicKey(normalizedPrivateKey);
} catch (error) {
let retrySuccess = false;
try {
@@ -3552,7 +3551,7 @@ export class NostrClient {
let pubkey = "";
try {
pubkey = getPublicKey(hexToBytes(privateKey));
pubkey = getPublicKey(privateKey);
} catch (error) {
const failure = new Error("Failed to derive the public key.");
failure.cause = error;
@@ -3596,7 +3595,7 @@ export class NostrClient {
throw new Error("Public key derivation is unavailable.");
}
try {
normalizedPubkey = getPublicKey(hexToBytes(normalizedPrivateKey));
normalizedPubkey = getPublicKey(normalizedPrivateKey);
} catch (error) {
const failure = new Error("Failed to derive the public key.");
failure.cause = error;
@@ -3734,7 +3733,7 @@ export class NostrClient {
throw new Error("Public key derivation is unavailable.");
}
try {
normalizedPubkey = getPublicKey(hexToBytes(normalizedPrivateKey));
normalizedPubkey = getPublicKey(normalizedPrivateKey);
} catch (error) {
const failure = new Error("Failed to derive the public key.");
failure.cause = error;
@@ -7651,7 +7650,6 @@ export class NostrClient {
const toProcess = eventBuffer;
eventBuffer = [];
const videosNeedingMetadata = [];
for (const evt of toProcess) {
try {
@@ -7708,30 +7706,23 @@ export class NostrClient {
this.activeMap.set(activeKey, video);
onVideo(video); // Trigger the callback that re-renders
// Defer NIP-71 fetch for batching
videosNeedingMetadata.push(video);
// Fetch NIP-71 metadata (categorization tags) in the background
this.populateNip71MetadataForVideos([video])
.then(() => {
this.applyRootCreatedAt(video);
})
.catch((error) => {
devLogger.warn(
"[nostr] Failed to hydrate NIP-71 metadata for live video:",
error
);
});
}
} catch (err) {
devLogger.error("[subscribeVideos] Error processing event:", err);
}
}
if (videosNeedingMetadata.length > 0) {
// Fetch NIP-71 metadata (categorization tags) in the background
this.populateNip71MetadataForVideos(videosNeedingMetadata)
.then(() => {
for (const video of videosNeedingMetadata) {
this.applyRootCreatedAt(video);
}
})
.catch((error) => {
devLogger.warn(
"[nostr] Failed to hydrate NIP-71 metadata for live video batch:",
error,
);
});
}
// Persist processed events after each flush so reloads warm quickly.
this.saveLocalData("subscribeVideos:flush");
};
+3 -10
View File
@@ -25,7 +25,6 @@ import {
assertAnyRelayAccepted as defaultAssertAnyRelayAccepted,
} from "../nostrPublish.js";
import { devLogger, userLogger } from "../utils/logger.js";
import { hexToBytes } from "../utils/hex.js";
import { Nip46RequestQueue, NIP46_PRIORITY } from "./nip46Queue.js";
export const HEX64_REGEX = /^[0-9a-f]{64}$/i;
@@ -1072,10 +1071,7 @@ function resolveAvailableNip46Ciphers(
typeof nip44v2GetConversationKey === "function"
) {
registerCipher("nip44.v2", () => {
const conversationKey = nip44v2GetConversationKey(
hexToBytes(privateKey),
remotePubkey,
);
const conversationKey = nip44v2GetConversationKey(privateKey, remotePubkey);
if (!conversationKey) {
throw new Error("Failed to derive a nip44 conversation key for remote signing.");
@@ -1099,10 +1095,7 @@ function resolveAvailableNip46Ciphers(
typeof nip44GetConversationKey === "function"
) {
registerCipher("nip44", () => {
const conversationKey = nip44GetConversationKey(
hexToBytes(privateKey),
remotePubkey,
);
const conversationKey = nip44GetConversationKey(privateKey, remotePubkey);
if (!conversationKey) {
throw new Error("Failed to derive a nip44 conversation key for remote signing.");
@@ -1501,7 +1494,7 @@ export class Nip46RpcClient {
if (!tools || typeof tools.getPublicKey !== "function") {
throw new Error("Public key derivation is unavailable.");
}
this.clientPublicKey = tools.getPublicKey(hexToBytes(this.clientPrivateKey));
this.clientPublicKey = tools.getPublicKey(this.clientPrivateKey);
if (!this.clientPublicKey || !HEX64_REGEX.test(this.clientPublicKey)) {
throw new Error("Failed to derive a valid public key for the remote signer session.");
}
+1 -2
View File
@@ -16,7 +16,6 @@ import {
getCachedNostrTools,
readToolkitFromScope,
} from "./toolkit.js";
import { hexToBytes } from "../utils/hex.js";
import { DEFAULT_NIP07_PERMISSION_METHODS } from "./nip07Permissions.js";
import { devLogger, userLogger } from "../utils/logger.js";
import { logRebroadcastCountFailure } from "./countDiagnostics.js";
@@ -135,7 +134,7 @@ export function signEventWithPrivateKey(event, privateKey) {
throw new Error("Missing signing primitives");
}
const finalized = finalizeFn(prepared, hexToBytes(normalizedKey));
const finalized = finalizeFn(prepared, normalizedKey);
if (
!finalized ||
typeof finalized.id !== "string" ||
-18
View File
@@ -9,21 +9,3 @@ export function normalizeHexString(value) {
export const normalizeHexId = normalizeHexString;
export const normalizeHexPubkey = normalizeHexString;
export function hexToBytes(hex) {
if (typeof hex !== "string") {
throw new TypeError("hexToBytes: expected string, got " + typeof hex);
}
const normalized = hex.trim();
if (normalized.length % 2) throw new Error("hex string must have even length");
const array = new Uint8Array(normalized.length / 2);
for (let i = 0; i < array.length; i++) {
const j = i * 2;
const byte = parseInt(normalized.substring(j, j + 2), 16);
if (Number.isNaN(byte) || byte < 0) {
throw new Error("Invalid hex string");
}
array[i] = byte;
}
return array;
}
+8 -6
View File
@@ -346,23 +346,25 @@ export class TorrentClient {
torrent.once("error", (err) => {
const peers = Math.max(0, Math.floor(normalizeNumber(torrent?.numPeers, 0)));
const webseedOnly = peers === 0 && hasWebSeed;
finalize({
healthy: false,
reason: "error",
healthy: webseedOnly,
reason: webseedOnly ? "webseed" : "error",
error: toError(err),
peers,
webseedOnly: peers === 0 && hasWebSeed,
webseedOnly,
});
});
if (safeTimeout > 0) {
timeoutId = setTimeout(() => {
const peers = Math.max(0, Math.floor(normalizeNumber(torrent?.numPeers, 0)));
const webseedOnly = peers === 0 && hasWebSeed;
finalize({
healthy: false,
healthy: webseedOnly,
peers,
reason: "timeout",
webseedOnly: peers === 0 && hasWebSeed,
reason: webseedOnly ? "webseed" : "timeout",
webseedOnly,
});
}, safeTimeout);
}