mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-02 10:52:27 +00:00
Decouple view logging and watch history timers
This commit is contained in:
1 parent
d17047c6ea
commit
7149255004
3 files changed
+124
-29
No files matched your search
@@ -0,0 +1,15 @@
|
||||
# Watch History Logging Cheatsheet
|
||||
|
||||
The watch history publisher emits developer-mode console messages from `publishWatchHistorySnapshot()`.
|
||||
|
||||
- `Watch history snapshot updated` (info) — all chunks and the index event were accepted by at least one relay. The summary includes:
|
||||
- `actor`: abbreviated pubkey that authored the snapshot.
|
||||
- `itemCount`: number of watch history entries that made it into the snapshot payload.
|
||||
- `snapshot`: identifier constructed from the current unix timestamp and a random suffix.
|
||||
- `latestPointer`: truncated pointer key for the newest watch entry that survived chunking.
|
||||
- `latestWatchedAt`: ISO timestamp derived from the most recent entry's `watchedAt` (falls back to `Date.now()` when missing).
|
||||
- `Watch history snapshot publish incomplete` (warning) — at least one chunk or the index event failed to reach any relay. When this fires the snapshot is still cached locally and the client will retry publication later.
|
||||
|
||||
Errors that precede the info log do **not** necessarily indicate a failure. For example, a message like `WebSocket connection to 'ws://127.0.0.1:4869/' failed` simply means one relay in the user's configured relay list is offline. The publisher only requires that at least one relay accepts each chunk before reporting success.
|
||||
|
||||
Refer to the log source in `js/nostr.js` for the exact structure and retry behavior.
|
||||
@@ -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,7 +7161,27 @@ class bitvidApp {
|
||||
viewResult
|
||||
);
|
||||
}
|
||||
})().catch((error) => {
|
||||
if (isDevMode) {
|
||||
console.warn(
|
||||
"[playVideoWithFallback] Unexpected error while recording video view:",
|
||||
error
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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 && isDevMode) {
|
||||
@@ -7160,22 +7201,17 @@ class bitvidApp {
|
||||
})().catch((error) => {
|
||||
if (isDevMode) {
|
||||
console.warn(
|
||||
"[playVideoWithFallback] Unexpected error while recording video view:",
|
||||
"[playVideoWithFallback] Unexpected error while updating watch history:",
|
||||
error
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancelTimer = () => {
|
||||
if (state.timerId) {
|
||||
clearTimeout(state.timerId);
|
||||
state.timerId = null;
|
||||
}
|
||||
};
|
||||
|
||||
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 +7219,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 +7234,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 +7259,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) =>
|
||||
|
||||
+29
@@ -2558,6 +2558,35 @@ class NostrClient {
|
||||
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,
|
||||
};
|
||||
|
||||
if (overallSuccess) {
|
||||
console.info("[nostr] Watch history snapshot updated:", summary);
|
||||
} else {
|
||||
console.warn("[nostr] Watch history snapshot publish incomplete:", summary);
|
||||
}
|
||||
}
|
||||
|
||||
const pointerEvent =
|
||||
signedIndexEvent ||
|
||||
chunkResults.find((chunk) => chunk.success)?.event ||
|
||||
|
||||
Reference in new issue
Block a user