Commit Graph
8 Commits
Author SHA1 Message Date
thePR0M3TH3ANandClaude Opus 4.8 8ab531b241 fix(access): canAccess cross-derives hex+npub so whitelisted authors aren't blocked (#32)
A video from a genuinely whitelisted author refused to play with "not from a
whitelisted author" (the source URL was confirmed healthy — HTTP 200, video/mp4,
range, CORS *). Root cause: accessControl keeps the whitelist in two sets —
whitelistPubkeys (hex) and whitelist (npub) — and canAccess consulted only one
based on the caller's input format without cross-deriving. playbackCoordinator
passes the author's npub, so it checked only the npub set; an entry present in
only the hex representation read as "not allowed."

- canAccess now derives BOTH hex and npub from the candidate and checks both sets
  (only fixes false-negatives; a non-whitelisted author is still denied).
- Dev-gated diagnostic at the playback rejection logs hex/npub, whitelistMode,
  set sizes, and inNpubSet/inHexSet to pinpoint any remaining denial.
- Tests: tests/access-control-canaccess.test.mjs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-25 10:39:11 -04:00
thePR0M3TH3ANandClaude Opus 4.8 0a37e625bb playback: fail over across hosted mirrors before dropping to P2P (#5)
The liveness probe already tries all of a video's sources, but actual playback
only ever tried the single primary URL and then fell straight to WebTorrent. Now
playback fails over across hosted mirrors first.

- PlaybackSession builds an ordered, de-duped hostedSourceCandidates (primary URL
  first, then the video's imeta `sources`; accepts string or {url} entries).
- attemptHostedPlayback takes a candidateUrl; the url-first execution loop tries
  each candidate and advances to the next mirror on probe-fail/stall/timeout,
  cleaning up watchdogs + resetting the <video> between candidates, only calling
  WebTorrent after every hosted mirror fails.
- `sources` plumbed coordinator -> strategy -> session. Single-source videos
  (no `sources`) behave exactly as before.

Tests: tests/services/playbackService_multisource.test.mjs (candidate
construction; the full async fail-over flow is covered by the existing
playbackService_order suite in CI — that mock-timers harness doesn't run in this
sandbox). ROLLBACK: revert the loop to the single attemptHostedPlayback() call
and drop the sources plumbing.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-24 13:30:30 -04:00
thePR0M3TH3ANandClaude Opus 4.8 065706a3cf videos: cross-ecosystem dedup (prefer bitvid, show one) across all grids
Phase 1 of the on-boarding plan, and the shared layer for unified grid
loading. A video that exists as both a bitvid kind-30078 note and a NIP-71
mirror/original previously could appear twice (e.g. independent dual-posts).

js/utils/videoDeduper.js: collapseCrossEcosystem() groups events by a
per-author identity (fileSha256 / originalFileSha256 / infoHash, plus an
`eid` namespace that links an on-boarded note's `importedFrom` to the
original foreign event) via union-find, and keeps the bitvid-native version
(else newest). dedupeVideos() = root-dedup THEN cross-ecosystem collapse.

Routed through the shared chokepoint app.dedupeVideosByRoot (playback
coordinator), so the feed stage, channel profile, and search all collapse
duplicates identically — "show one, prefer bitvid" everywhere. Channel
fallback path updated too.

Limitation (documented): hash-less independent dual-posts can't be matched
until the bitvid side carries on-board provenance (Phase 2).

Tests + mutation-verified (prefer-bitvid, per-author identity, infohash,
provenance link, root+cross composition). 1262/1263 unit (known flake).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-22 19:51:43 -04:00
thePR0M3TH3ANandClaude Opus 4.8 006403b7b4 [playback] stop the WebTorrent webseed flood on multi-file torrents
The video's hosted CDN URL was added to WebTorrent's `urlList`, but a
standalone hosted-file URL is only a valid BEP19 webseed for a SINGLE-file
torrent. For a multi-file torrent WebTorrent treats it as a base directory and
appends "<torrent name>/<file path>", producing a doubled URL that 404s on
every request — the flood seen on e.g. archive.org videos.

The CDN URL is no longer placed in urlList. It's forwarded separately as
`hostedUrlWebSeed` and attached post-metadata (handleTorrentStream) via
torrent.addWebSeed ONLY when the torrent is a single file, where the URL maps
directly to the file. Multi-file torrents get no file-pointing webseed, so no
flood. Magnet-declared `ws=` webseeds are unaffected (passed via urlList as
before). With CDN-first, dual-source videos play the CDN directly anyway, so
this only affects forced-torrent / URL-less paths.

Tests: a single-file torrent receives the hosted URL via addWebSeed; a
multi-file torrent does not. The single-file guard is mutation-verified.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-18 16:05:54 -04:00
thePR0M3TH3ANandClaude Opus 4.8 4e5bfaa6af [playback] harden WebTorrent streaming + add forced-source test deep links
WebTorrent (handleTorrentStream) hardening:
- Resolve only when playback is actually viable (loadeddata/canplay/playing),
  not the instant streamTo() is called. A dead/too-slow swarm is no longer
  reported as success, so the caller's fallback can run.
- Pre-playback torrent errors reject (caller falls back); post-playback errors
  go to an onPlaybackError hook instead of a dead reject().
- Mid-stream stall watchdog surfaces an onStall hook ("waiting for peers").
- Fix a <video> listener leak: stream listeners are tracked and torn down
  before the next stream and on cleanup() (the modal reuses one element).
- Stream the largest decodable file from a multi-file torrent and deselect the
  rest (wider ext set: mp4/m4v/webm/mkv/mov/ogv/ogg) so the swarm isn't spent
  downloading files we never play.
playbackCoordinator wires onStall/onPlaybackError to user-facing status.

Forced-source deep links (test affordance):
- The main app honors ?v=<nevent>&playback=torrent|url (mirrors the embed
  player) to force a playback path; a missing source shows a clear message.
- The video share menu gains "Copy WebTorrent URL" / "Copy CDN URL" items that
  copy the forced-source deep link (disabled with a reason when unavailable).

Tests: webtorrent-handlers (resolve-on-readiness, pre/post-ready errors, stall
fires/clears, largest-file select/deselect, no listener accumulation),
playback-forced-source (param normalization), videoMenuRenderers (copy items +
availability). All mutation-verified.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-18 12:15:27 -04:00
google-labs-jules[bot]andPR0M3TH3AN b28e62963f perf: Batch video timestamp resolution to reduce N+1 network requests
- Implemented `hydrateVideoHistoryBatch` in `NostrClient` to fetch missing root events and sparse histories in a single `pool.list` request.
- Refactored `hydrateVideoHistory` to use shared helper methods `getLocalVideoHistory` and `_processAndCacheEvent`.
- Added `resolveVideoPostedAtBatch` to `PlaybackCoordinator` which delegates to the batched client method.
- Updated `VideoListView` to collect videos needing timestamp resolution and call the batch method, falling back to individual calls if necessary.
- Added regression test `tests/performance/resolvePostedAt.test.mjs` to verify reduced network calls.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-11 12:01:39 +00:00
google-labs-jules[bot]andPR0M3TH3AN 0da9401235 Refactor playback selection strategy into dedicated service
Extracted the complex playback logic from `js/app/playbackCoordinator.js` into a new `js/services/playbackStrategyService.js`.
This improves maintainability by separating the orchestration logic from the application glue code.
The coordinator now delegates to the service, passing a context object for UI interactions and state synchronization.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-10 04:27:31 +00:00
Claude 41a12669f6 refactor: extract Application method clusters into coordinator modules
Move cohesive method groups out of the monolithic Application class
(js/app.js, ~10K lines) into five coordinator modules under js/app/:

- feedCoordinator: feed registration, runtime building, refresh,
  video loading, URL-health helpers, and feed telemetry
- playbackCoordinator: URL-first + magnet fallback pipeline, probing,
  torrent status intervals, and video timestamp resolution
- authSessionCoordinator: signer lifecycle, profile switching,
  relay/block/wallet management, and cleanup
- modalCoordinator: modal open/close, view-count subscriptions,
  watch-history metadata, and playback logging
- moderationCoordinator: moderation settings, video decoration,
  override/block/hide action routing

Each coordinator factory receives injected dependencies instead of
importing globals. Application retains thin delegator methods so all
public call-sites remain unchanged. Coordinator initialisation is
idempotent (via _initCoordinators) and supports both constructor-based
and Object.create()-based instantiation used by test harnesses.

All existing unit tests, lint checks, and syntax validation pass.

https://claude.ai/code/session_01C3PKJTCdavsGbk5jjC85EB
2026-02-06 20:47:10 +00:00