Merge pull request #245 from PR0M3TH3AN/codex/add-logging-for-watch-history-updates-36ejak

Decouple watch history updates from view logging timers
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-02 10:22:04 -04:00
commit 6d1bbea045
2 files changed
+160 -68

No files matched your search

+91 -42
View File
@@ -6983,8 +6983,12 @@ class bitvidApp {
return;
}
if (state.timerId) {
clearTimeout(state.timerId);
if (state.viewTimerId) {
clearTimeout(state.viewTimerId);
}
if (state.historyTimerId) {
clearTimeout(state.historyTimerId);
}
if (Array.isArray(state.handlers) && state.videoEl) {
@@ -7080,24 +7084,41 @@ class bitvidApp {
return;
}
const THRESHOLD_SECONDS = 12;
const VIEW_THRESHOLD_SECONDS = 12;
const WATCH_HISTORY_THRESHOLD_SECONDS = 10;
const state = {
videoEl,
pointer,
pointerKey,
viewerIdentityKey,
handlers: [],
timerId: null,
fired: false,
viewTimerId: null,
historyTimerId: null,
viewFired: false,
historyFired: false,
};
const finalize = () => {
if (state.fired) {
const maybeCleanup = () => {
if (state.viewFired && state.historyFired) {
this.cancelPendingViewLogging();
}
};
const clearTimer = (timerKey) => {
const property = `${timerKey}TimerId`;
if (state[property]) {
clearTimeout(state[property]);
state[property] = null;
}
};
const finalizeView = () => {
if (state.viewFired) {
return;
}
state.fired = true;
cancelTimer();
this.cancelPendingViewLogging();
state.viewFired = true;
clearTimer("view");
maybeCleanup();
const { pointer: thresholdPointer, pointerKey: thresholdPointerKey } =
state;
@@ -7140,23 +7161,6 @@ class bitvidApp {
viewResult
);
}
try {
const historyResult = await updateWatchHistoryList(thresholdPointer);
if (!historyResult?.ok && isDevMode) {
console.warn(
"[playVideoWithFallback] Watch history update failed:",
historyResult
);
}
} catch (historyError) {
if (isDevMode) {
console.warn(
"[playVideoWithFallback] Exception while updating watch history:",
historyError
);
}
}
})().catch((error) => {
if (isDevMode) {
console.warn(
@@ -7167,15 +7171,45 @@ class bitvidApp {
});
};
const cancelTimer = () => {
if (state.timerId) {
clearTimeout(state.timerId);
state.timerId = null;
const finalizeWatchHistory = () => {
if (state.historyFired) {
return;
}
state.historyFired = true;
clearTimer("history");
maybeCleanup();
const { pointer: thresholdPointer } = state;
(async () => {
try {
const historyResult = await updateWatchHistoryList(thresholdPointer);
if (!historyResult?.ok) {
console.warn(
"[playVideoWithFallback] Watch history update failed:",
historyResult
);
}
} catch (historyError) {
console.warn(
"[playVideoWithFallback] Exception while updating watch history:",
historyError
);
}
})().catch((error) => {
if (isDevMode) {
console.warn(
"[playVideoWithFallback] Unexpected error while updating watch history:",
error
);
}
});
};
const scheduleTimer = () => {
if (state.fired) {
const scheduleTimer = (timerKey, thresholdSeconds, callback) => {
const firedKey = `${timerKey}Fired`;
const idKey = `${timerKey}TimerId`;
if (state[firedKey] || state[idKey]) {
return;
}
const currentSeconds = Number.isFinite(videoEl.currentTime)
@@ -7183,13 +7217,13 @@ class bitvidApp {
: 0;
const remainingMs = Math.max(
0,
Math.ceil((THRESHOLD_SECONDS - currentSeconds) * 1000)
Math.ceil((thresholdSeconds - currentSeconds) * 1000)
);
if (remainingMs <= 0) {
finalize();
callback();
return;
}
state.timerId = window.setTimeout(finalize, remainingMs);
state[idKey] = window.setTimeout(callback, remainingMs);
};
const registerHandler = (eventName, handler) => {
@@ -7198,14 +7232,23 @@ class bitvidApp {
};
registerHandler("timeupdate", () => {
if (videoEl.currentTime >= THRESHOLD_SECONDS) {
finalize();
if (
!state.historyFired &&
videoEl.currentTime >= WATCH_HISTORY_THRESHOLD_SECONDS
) {
finalizeWatchHistory();
}
if (!state.viewFired && videoEl.currentTime >= VIEW_THRESHOLD_SECONDS) {
finalizeView();
}
});
const cancelOnPause = () => {
if (!state.fired) {
cancelTimer();
if (!state.viewFired) {
clearTimer("view");
}
if (!state.historyFired) {
clearTimer("history");
}
};
@@ -7214,9 +7257,15 @@ class bitvidApp {
);
const resumeIfNeeded = () => {
if (!state.fired && !state.timerId) {
scheduleTimer();
if (state.viewFired && state.historyFired) {
return;
}
scheduleTimer(
"history",
WATCH_HISTORY_THRESHOLD_SECONDS,
finalizeWatchHistory
);
scheduleTimer("view", VIEW_THRESHOLD_SECONDS, finalizeView);
};
["play", "playing"].forEach((event) =>
+69 -26
View File
@@ -2552,39 +2552,82 @@ class NostrClient {
overallSuccess = false;
}
const publishAttemptSummaries = [];
const acceptedRelaySet = new Set();
const recordPublishAttempt = (label, results) => {
if (!Array.isArray(results) || !results.length) {
return;
}
const accepted = results
.filter((result) => result && result.success)
.map((result) => result.url)
.filter((url) => typeof url === "string" && url);
if (accepted.length) {
accepted.forEach((url) => acceptedRelaySet.add(url));
}
publishAttemptSummaries.push({ label, accepted, results });
};
chunkResults.forEach((chunk) => {
recordPublishAttempt(`chunk#${chunk.chunkIndex}`, chunk.results);
});
recordPublishAttempt("index", indexPublishResults);
const failedAttempts = publishAttemptSummaries.filter(
(attempt) => !attempt.accepted.length
);
if (acceptedRelaySet.size) {
console.info(
`[nostr] Watch history publish accepted by ${acceptedRelaySet.size} relay(s):`,
Array.from(acceptedRelaySet).join(", ")
);
}
if (failedAttempts.length) {
console.warn(
"[nostr] Watch history publish rejected by relays:",
failedAttempts.map((attempt) => ({
label: attempt.label,
results: attempt.results,
}))
);
} else if (!acceptedRelaySet.size && publishAttemptSummaries.length) {
console.warn("[nostr] Watch history publish had no relay attempts.");
}
if (overallSuccess) {
this.cancelWatchHistoryRepublish(normalizedActor);
} else if (allowRetry && persistedItems.length) {
this.scheduleWatchHistoryRepublish(normalizedActor, persistedItems);
}
if (isDevMode) {
const latestItem = persistedItems[0] || null;
const latestPointerKey = pointerKey(latestItem);
const latestPointerSummary = latestPointerKey
? latestPointerKey.length > 32
? `${latestPointerKey.slice(0, 32)}…`
: latestPointerKey
: null;
const actorPreview =
normalizedActor.length > 16
? `${normalizedActor.slice(0, 8)}…${normalizedActor.slice(-4)}`
: normalizedActor;
const summary = {
actor: actorPreview,
itemCount: persistedItems.length,
snapshot: snapshotId,
latestPointer: latestPointerSummary,
latestWatchedAt: Number.isFinite(latestItem?.watchedAt)
? new Date(Math.max(0, Math.floor(latestItem.watchedAt))).toISOString()
: null,
};
const latestItem = persistedItems[0] || null;
const latestPointerKey = pointerKey(latestItem);
const latestPointerSummary = latestPointerKey
? latestPointerKey.length > 32
? `${latestPointerKey.slice(0, 32)}…`
: latestPointerKey
: null;
const actorPreview =
normalizedActor.length > 16
? `${normalizedActor.slice(0, 8)}…${normalizedActor.slice(-4)}`
: normalizedActor;
const summary = {
actor: actorPreview,
itemCount: persistedItems.length,
snapshot: snapshotId,
latestPointer: latestPointerSummary,
latestWatchedAt: Number.isFinite(latestItem?.watchedAt)
? new Date(Math.max(0, Math.floor(latestItem.watchedAt))).toISOString()
: null,
};
if (overallSuccess) {
console.info("[nostr] Watch history snapshot updated:", summary);
} else {
console.warn("[nostr] Watch history snapshot publish incomplete:", summary);
}
if (overallSuccess) {
console.info("[nostr] Watch history snapshot updated:", summary);
} else {
console.warn("[nostr] Watch history snapshot publish incomplete:", summary);
}
const pointerEvent =