Commit Graph

124 Commits

Author SHA1 Message Date
Timo 869c64171b Fix Disney+ force sync, seek, and HCM regressions for v2.5.3
The v2.5.2 Disney+ page-API integration leaked blob-relative <video>
time into force sync, seeks, and heartbeats when the page-API bridge
had no fresh data, so force sync on Disney+ appeared broken.

- getSyncCurrentTime/getSyncDuration now refuse native values on Disney+
  (return null/0) so stale bridge data degrades to a clean no-op instead
  of broadcasting garbage to peers. The get_current_time handler and
  episode/lobby/hcmIsLive paths are routed through the same accessor.
- Validate FORCE_SYNC_PREPARE/SEEK payloads as finite before relaying;
  the internal coercion no longer treats '' as 0.
- Stop double-routing FORCE_SYNC_PREPARE from the popup path (the
  generic popup route now covers only play/pause/seek).
- popup force-sync: exclude null/empty peer times from the jump-to-others
  median (Number(null)===0 was dragging the target to 0), guard against
  NaN end-to-end, clear the dangling reset timer on failure, and retry
  without re-injecting when the content script responds but the Disney+
  bridge has not yet delivered a finite time.
- hcmIsLive skips the native-duration live signal on Disney+ only,
  preserving YouTube/Twitch Infinity-duration live detection.

Disney-specific logic remains strictly gated to disneyplus.com; no
Netflix/YouTube/Twitch/generic path is affected.
2026-07-02 16:57:48 +02:00
Timo 9ac8c28442 Remove Disney+ DOM scraping fallback 2026-07-02 15:08:54 +02:00
Timo 51da169d69 Remove temporary Media Session interceptor and Video Event Log diagnostics
Clean up the exploratory Disney+ diagnostics now that the Shadow DOM
timeline/seek integration is in place:

- Remove Proposal 1 (Media Session interceptor): the background.js
  interceptor injection and the content.js message listener capturing
  __koalaMediaSessionCapture, plus the now-unused mediaSessionPosition
  reporting in content.js and popup.js.
- Remove Proposal 3 (Video Event Log): videoEventsLog, logVideoEvent(),
  the per-video event logging listeners, and the popup.html/popup.js UI.
- Keep Proposal 2 (DOM timestamp scraper) for ongoing diagnostics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:18:50 +02:00
Timo 09d35f3e48 TEMPORARY DIAGNOSTICS: Implement Proposal 2 and 3 logs in Dev tools tab 2026-07-02 13:35:08 +02:00
Timo 066d2c9407 Implement temporary Media Session interceptor diagnostics for Disney+ and restore Netflix sessionId lookup guard 2026-07-02 13:19:47 +02:00
Timo 9cbeb661d6 Save baseline state with Disney+ seek adjustments and build identifier 2026-07-02 13:00:41 +02:00
KoalaDev 01762b0c2f Normalize shared tab titles 2026-07-02 10:20:25 +02:00
KoalaDev 57cd071d58 Add hidden remote seek test controls 2026-07-02 09:50:39 +02:00
KoalaDev 8a21fbe07f Fix Netflix seek handling 2026-07-02 09:43:11 +02:00
Timo fa7eea54d8 Split tab and media title privacy controls 2026-07-01 19:21:38 +02:00
Timo 4bae88e107 Fix title privacy popup import 2026-07-01 18:50:09 +02:00
Skrockle 3e4b97b11a Fix: Remove redundant TITLE_PRIVACY_MODES array in popup.js
- Remove duplicate local TITLE_PRIVACY_MODES array definition
- Use Object.values(TITLE_PRIVACY_MODES).includes() for validation instead
- Keeps single source of truth in title-privacy.js

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-01 18:37:25 +02:00
Timo e555906553 fix(extension): harden title privacy edge cases 2026-06-29 13:43:10 +02:00
Timo 4f1335242c feat(extension): add shared title privacy modes 2026-06-29 13:35:34 +02:00
KoalaDev 1f48e9df47 fix(popup): polish custom-server, host-control card & co-host UX
- Manual Connect: split the server picker into its own inset panel,
  visually separated from the Room ID / Password fields (they were
  welded together in one flat list).
- Custom server URL: stop auto-prepending ws:// — default to wss://
  for real hosts, ws:// only for localhost/127.0.0.1 (matches what
  the background already enforces; the displayed URL no longer lies).
- Host Control card: replace the raw native checkbox with the proper
  .toggle-switch slider used in Options; move the toggle row from a
  <label> to a <div> to avoid an illegally nested label.
- Co-host discoverability: owner-only hint in the Host Control card
  pointing to the participant list (new NOTICE_COHOST_HINT across all
  15 locales). Shown only when actionable: owner + co-host-capable
  relay + host-only active.
- Peer row: group right-side badges/actions into one container so they
  no longer scatter via space-between or collide with long names;
  restyle the give/revoke button (padding, weight, hover fill, tooltip).
- Gate promote/demote (and role badges) on host-only mode being ON,
  in addition to the existing owner-privilege check — roles are moot
  in 'everyone' mode, so no badge/button noise there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 02:43:28 +02:00
KoalaDev e23f9ab226 fix: wire up Regenerate Peer ID button + remove arbitrary MAX_CONTROLLERS cap
Two pre-existing issues surfaced during audit follow-up:

1. popup.js bound elements.regenId but never attached a click handler —
   the 'Regenerate Peer ID' button has been dead UI since it was added
   (verified across full git history). Wired it up to send REGENERATE_ID;
   the background handler already did the right thing (rotate peerId,
   persist, force reconnect). The button is functional (regen ID on
   duplicate-identity errors), not privacy-theater — username is already
   user-changeable in settings, so it stays out of scope.

2. MAX_CONTROLLERS=10 was arbitrary. Payload math (25 controllers ×
   16-char IDs ≈ 500 bytes) is well under the 4KB maxHttpBufferSize,
   controllers.has() is O(1) on a Set, and 'just use everyone mode' is
   not a real answer when a host wants 12 controllers + 3 guests in a
   15-person room. Removed the cap entirely; the only real upper bound
   is MAX_PEERS_PER_ROOM (25).

Added TOAST_ID_REGENERATED to en.json — the i18n fallback merge
(Object.assign({}, enDict, targetDict)) covers the other 14 locales
automatically with English until they're translated.
2026-06-28 14:03:59 +02:00
KoalaDev 17ac5c0a6f fix(host-control-mode): adversarial audit — force-sync stall, desync/lobby, gate parity, BC tests
Audit fixes (each verified against the actual code path):

H-1 (server): track force-sync initiator on PREPARE; let the demoted
initiator's EXECUTE through the host-only gate so mid-sync demotion no
longer strands the whole room paused. Clear on EXECUTE/peer-leave.

M-1 (background): episode-lobby gate now uses !amController() for parity
with CONTENT_EVENT and server gates — co-hosts can drive the room and
initiate lobbies, not just the owner.

M-2/M-3 (popup/content/background): forceSyncReset respects hcmGuestLocked;
desynced guest skips EPISODE_LOBBY so they don't get frozen in pause after
lobby completion, and checkEpisodeLobbyCompletion excludes desynced peers
from the required count so they don't block the lobby.

M-4 (background): getHostSyncTarget clamps extrapolation to 2x heartbeat
interval so a stale host heartbeat can't snap the guest tens of seconds
past the host's real position.

L-1..L-4 (server/content/background/popup): clarify dedup comment re:
network-blip window, enforce desync invariant on SW-restore, add
forceSyncBtn guest-locked backstop, refresh badge text in place.

Backward compatibility (verified by BC-1..BC-4 regression tests):
- Old client ↔ new server: server adds fields only, never requires; old
  heartbeats stripped of desynced; host-only enforced server-side even
  when the client has no awareness.
- New client ↔ old server: empty capabilities → host-control UI hidden,
  all gates default to everyone, behavior byte-identical to pre-HCM.
- Mixed rooms: every pre-HCM event type relays cleanly in both directions.
2026-06-28 05:54:50 +02:00
KoalaDev 1ec2396c41 feat(co-host): popup UI — promote/demote, controller badges, controller-aware lock
- updateHostControlUI takes a state object now; role badge shows Host / Controller
  / Guest; remote-control lock uses amController (a co-host can drive, so their
  buttons aren't locked).
- Peer list: Host/Controller badges and, for the owner, a per-peer Give control /
  Revoke button (SET_PEER_ROLE). Shown only when the feature is active and the room
  is host-only or the viewer is the owner — no role noise for guests in normal rooms.
  Hidden entirely against a relay without the co-host capability.
- i18n: BADGE_CONTROLLER, BTN_GIVE_CONTROL, BTN_REVOKE_CONTROL across all 15 locales.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 03:45:00 +02:00
KoalaDev dfa0a3d03e fix(host-control-mode): audit findings — host dedup demotion, episode-lobby freeze, +cleanups
Findings recovered from the multi-agent audit (verification phase was cut off by a
usage limit; verified inline against the code):

HIGH:
- Host was demoted on peerId dedup (fast reconnect / second tab): the dedup path
  removes the old socket and the kicked socket's 'disconnect' both ran the
  host-leave fallback before the same peerId re-joined → room silently unlocked on
  every host network blip. Now skip the fallback while a join for that peerId is in
  flight (peerJoinLocks). Regression test added. (A long real disconnect still
  falls back — that's the deferred host-grace EC-10.)
- Episode auto-advance froze a gated guest: in host-only the guest's EPISODE_LOBBY
  is dropped server-side, but the guest still self-paused (PAUSE_FOR_LOBBY) waiting
  for readies that never came → 60s freeze. A host-only guest now skips creating a
  lobby (the host drives episode sync).

LOW / cleanup:
- GET_HCM_STRINGS could return a raw key name ("HCM_DIALOG_TITLE") if the locale
  dictionary failed to load (getMessage returns the key on miss) → omit it so
  content keeps its English fallback.
- hcmReset now also clears the snap-back cooldown + buffering grace, so a stale
  cooldown can't swallow the first snap-back after a room/host change.
- Popup play/pause labels reset on lock too (not just unlock), so a button can't
  freeze on "Playing…" when host-only activates mid-click.
- Fix a stale comment (heartbeat now carries 'desynced').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 02:25:11 +02:00
KoalaDev 9963da2ebc feat(host-control-mode): explicit server capabilities for feature detection
Replace the implicit "hostPeerId present ⇒ feature supported" heuristic with an
explicit capabilities list the relay advertises in ROOM_DATA. Cleaner, self-
documenting, and the extensible hook for the planned co-host feature (owner
promotes guests to extra controllers) — a future 'co-host' capability + events
slot in without a protocol bump.

- shared: CAPABILITIES { HOST_CONTROL }.
- server: SERVER_CAPABILITIES advertised in ROOM_DATA.
- background: track serverCapabilities (empty against older relay), serverSupports(),
  thread hostControlSupported through GET_STATUS / GET_CONTROL_MODE / CONTROL_MODE.
- popup: gate the host-control card on the explicit capability instead of hostPeerId.

Backwards-compatible both ways: old relay omits the field → feature stays hidden
(no errors); old client ignores the field. WS test asserts ROOM_DATA advertises
the capability. Adds docs/host-control-mode-TESTING.md (beta-server setup, wss
caveat, two-new-clients note, verification checklist).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 04:48:45 +02:00
KoalaDev d26b95985d fix(host-control-mode): only show host-control card when meaningful
The card was rendered in every room with a "Guest" badge by default — confusing
in normal rooms, and outright misleading against a relay that doesn't yet support
host control (older server omits hostPeerId, so amHost is always false and nobody
is ever recognized as host).

Now the card shows only when it's meaningful: the host always sees it (to toggle
host-only), a guest sees it only while host-only is active, and it's hidden in a
normal "everyone" room and whenever the server doesn't advertise hostPeerId
(feature unavailable). Thread hostPeerId through updateHostControlUI to detect
server support.

Note: the feature still requires the relay to run the host-control server code;
this just makes the client degrade cleanly instead of showing a stray "Guest".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 01:29:00 +02:00
KoalaDev 58af258fd3 fix(host-control-mode): harden gates, close UX races, debounce toggles, surface desync state
Bis auf peerId-Auth (akzeptiert) alle Audit-Findings behoben:
- Snap-back spielt nicht mehr blind ab, wenn Host-Status unbekannt
- Teardown-Pfade broadcasten jetzt CONTROL_MODE → kein verwaistes Badge/Dialog
- Dialog-Timer bei Ersetzung sauber gecleart
- Server re-synchronisiert Sender bei nicht-Host-Reject
- Popup-Toggle revertiert bei Server-Ablehnung
- Desync-Zweig sendet keine Phantom-ACKs mehr an Host
- Toggle-Debouncing serverseitig (500ms pro Raum)
- Button-Text auf Unlock zurückgesetzt
- Live-Erkennung defensiver, Resync-Retry bei fehlendem Target, Badge-Retry

Neu: Host sieht 'Solo'-Badge für desyncte Gäste via PEER_STATUS-Heartbeat
(16 Sprachen, neuer i18n-Key BADGE_DESYNCED/TOOLTIP_PEER_DESYNCED)

Tests: H-5-Reject-Unicast, M-4-Debounce, Desync-Heartbeat-Relay
2026-06-26 13:09:34 +02:00
KoalaDev af9cf34f0b fix(extension): pre-test self-audit fixes for host control mode
- popup: lock remote-control buttons (Play/Pause/SYNC) for guests in host-only
  mode so clicks don't silently get gated and leave the button stuck; backstop
  guards in the handlers and the 2.5s safety reset respects the lock.
- content: rebuild the desync dialog + resync badge with the DOM API inside a
  Shadow DOM. innerHTML inline style="" attributes are stripped by strict
  style-src CSP (Netflix/YouTube/Disney+); CSSOM .style is CSP-safe and the
  shadow root isolates from page CSS.
- content: detect live-DVR in hcmIsLive() via a sliding seekable window
  (seekable.start(0) > 1), not just duration === Infinity (EC-15).

Records remaining audit items (snap-back thrash, join race, dialog i18n) in the
edge-case log for the device-testing pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 09:54:45 +02:00
KoalaDev 9006570bd1 feat(extension): host control toggle + role badge in popup (step 5)
Final wiring of host control mode — the user-facing UI:

- Active-room section gains a Host Control card: a Host/Guest role badge, a
  host-only toggle ("Only I can control playback") shown to the host, and a
  guest notice when the room is host-only.
- Toggle sends SET_CONTROL_MODE; UI refreshes from the server's CONTROL_MODE
  broadcast (with optimistic revert on failure). Card updates on GET_STATUS,
  CONTROL_MODE, and reconnect.
- i18n: 6 new keys (LABEL_HOST_CONTROL[_TOOLTIP], LABEL_HOST_ONLY_TOGGLE,
  NOTICE_HOST_CONTROLS, BADGE_HOST, BADGE_GUEST) translated across all 15
  popup locales.

Completes the feature end to end (server + background + content + popup).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 04:14:55 +02:00
KoalaDev 6276ee134c fix(ui): Correct false idle state on connection errors
Bump version to 2.4.6
2026-06-23 18:36:05 +02:00
KoalaDev a1bdcf4325 fix(extension): stop storing room/settings in chrome.storage.sync
Room ID, password, and username were resurrected from synced storage on a
fresh install (sync survives an uninstall in the user's Google account), which
made the extension silently auto-connect to a dead room and appear permanently
connected. getSettings() and all settings reads (popup, content, audio-options,
applyAudioSettingsToTab) are now local-only, and legacy keys are actively
purged from sync on install/update/startup. Only onboardingComplete and
dismissedHints remain in sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:38:36 +02:00
KoalaDev 36f494fa55 feat(extension): one-click invite button in empty peer list + v2.4.1 changelog
- "No peers yet" state now shows a localized "Copy invite link" button (reuses BTN_COPY_INVITE, present in all 13 locales) so users can share without hunting for the field
- Add v2.4.1 changelog entry covering the onboarding tour improvements, empty-state action, and the mobile landing-page fixes from this session

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 10:50:04 +02:00
KoalaDev b5b3885d19 feat(extension): finish onboarding tour with closing step & cleaner welcome
- Add the previously-orphaned 5th onboarding step (ONBOARDING_5) so the tour ends on a closing card instead of stopping at the username field
- Make step 1 a centered welcome card instead of spotlighting the logo; guard querySelector for target-less steps
- Fix stale static placeholders in popup.html (Step 1 of 3 -> 5, progress 33% -> 20%)

Verified ONBOARDING_5/DONE exist across all 13 locales; rendered with the longest (German) copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 10:40:40 +02:00
Timo 91aa76e842 fix: revert premature isConnecting reset + remove popup disconnected flicker
- background.js: remove isConnecting=false at line 608 (was set before
  WebSocket handshake completed, defeating the guard for the entire
  CONNECTING phase). isConnecting is already properly reset in onclose,
  onopen/40 handler, catch block, and socket guard path.
- background.js: restore isConnecting=false in forceDisconnect() so
  subsequent connect() calls can proceed after intentional disconnect.
- popup.js: remove hardcoded applyConnectionStatus('disconnected') that
  caused a guaranteed red flicker on every popup open. Status is now
  set only by the async GET_STATUS response.
2026-06-16 10:44:57 +02:00
Timo 5f0a189451 fix(popup): trigger CONNECT when popup opens with saved roomId
If user has a room configured but the background is not connected
(e.g. service worker was killed, startup race, or stale state), the
popup now sends CONNECT on open. Previously it only called GET_STATUS
and showed 'disconnected' with 0 logs — the user had no way to know
whether the extension was working.
2026-06-16 10:35:13 +02:00
KoalaDev b2da17ab62 fix(lazy-connect): harden connection lifecycle against race conditions and edge cases
- Prevent concurrent connect() by removing isConnecting reset from
  forceDisconnect(); the guard in connect() now reliably blocks
  re-entry since no external caller can defeat it.
- Reset isConnecting at end of connect() so subsequent calls can
  proceed without waiting for the async '40' handler.
- Clear connectIntent and cancel reconnect timer on server ERROR
  when no currentRoom exists, preventing infinite reconnect loops
  after failed join attempts (e.g. wrong password via invite link).
- Move connectIntent assignment after !roomId guard in
  WEB_JOIN_REQUEST to avoid clobbering existing intent on invalid
  input.
- Broadcast JOIN_STATUS failure to popup and bridge tabs when
  WEB_JOIN_REQUEST receives an invalid room ID, so the website
  join page receives feedback instead of hanging forever.
- Clear joinBtnTimeout on CONNECTION_STATUS connected/disconnected
  to avoid stale timeout error messages in the popup.
2026-06-16 05:39:17 +02:00
Timo 3f8cf33dc9 fix: WEB_JOIN_REQUEST sendResponse leak + popup validation state cleanup
- WEB_JOIN_REQUEST: add missing sendResponse when already in target room
- popup join: reset isProcessingConnection + clear timeout on validation failures
2026-06-15 14:40:15 +02:00
Timo 1685b6a327 fix(popup): join timeout checks connection status instead of blind 15s timer
Lazy-connect introduces a new state where the user clicks 'Raum erstellen'
and the WebSocket takes 1-2s to establish (previously always pre-connected).
The old 15s blind timeout would re-enable the button while background was
still connecting, causing silent no-op clicks. Now polls GET_STATUS and
extends the window if still connecting.
2026-06-15 14:19:18 +02:00
Timo ed80856803 fix: restore missing Create Room onboarding step and target h1 for welcome step 2026-06-14 05:10:57 +02:00
KoalaDev 4fbf309e5a fix(extension): resolve critical race conditions and infinite seek loop
- Fix infinite seek loop by transitioning to exact expectedSeekTime tracking
- Fix zombie connections by calling forceDisconnect on ping timeouts
- Fix popup Join/Leave race conditions using isProcessingConnection lock
- Fix cross-room join bugs by properly disconnecting old rooms and bypassing same-room joins
- Update onboarding UI to switch to Sync tab automatically
- Prepare CHANGELOG.md for v2.3.0 release
2026-06-13 10:50:46 +02:00
KoalaDev 54d03df0af fix: add ping to copy debug report 2026-06-09 07:15:02 +02:00
KoalaDev 3e7afd7592 feat: add server ping display with peer ping foundation
Adds application-level ping/pong between extension and relay server.
Extension sends PING every 15s, server echoes PONG. Round-trip time
displayed in Status tab (green/yellow/red color-coded).

Server also forwards PING with target peerId and routes PONG back,
enabling future peer-to-peer ping without server restart.
Extension already responds to incoming peer PINGs.

See CHANGELOG.md for details.
2026-06-09 07:08:14 +02:00
Timo 08ce689f4c move all prefs from sync to local storage (audio, filter, autosync, etc.) 2026-06-08 21:07:40 +02:00
Timo c134e1bfae swap review/coffee order in footers 2026-06-08 20:58:44 +02:00
Timo cad4b4a6db v2.2.0 polish: inline pulse dot, coffee theme, remove toggle, GitHub issue link, beautify audio page, locale cleanup 2026-06-08 20:56:41 +02:00
Skrockle 7bba36c505 Add support badges and audio processing 2026-06-08 19:25:07 +02:00
Timo 0a34d804fb v2.1.3: Fix debug report showing oldest logs instead of newest 2026-06-07 00:43:25 +02:00
Timo c88f6fb121 v2.1.2
- Fixed episode guard regex to support Sxx:Exx title format (Jellyfin/Emby)
  and any non-alphanumeric separator between season and episode numbers
- Migrated username, roomId, password, serverUrl, useCustomServer from
  chrome.storage.sync to chrome.storage.local for per-device isolation,
  preventing automatic cross-device room joins with duplicate names
- Added one-time migration from sync to local for existing users
2026-06-06 22:16:30 +02:00
Timo d59fc4777d chore: release v2.1.0 with stabilization and audit fixes 2026-06-04 15:18:57 +02:00
Timo 9d849a2996 feat: add 7 new languages to extension and website, bump version to 2.1.0 2026-06-04 14:59:14 +02:00
Koala a7481d42a2 fix(extension): resolve language selector UI overwrite and dynamic version reporting 2026-06-03 12:09:39 +02:00
Koala 595ea297f5 chore(release): release v2.0.5 2026-06-03 11:33:54 +02:00
Koala 81c50eff16 Harden room discovery and health metrics 2026-06-03 10:48:49 +02:00
Koala a06cca8b9d fix: apply boundary-safe hostname matching to remaining platform and blacklist checks 2026-06-02 08:02:47 +02:00
Timo ebf3178e32 feat: multi-video overview in debug report
- GET_VIDEO_STATE now returns allVideos[] with summary per video element
- Copy report shows markdown table when multiple videos on page
- Table includes: resolution, muted, paused, readyState, duration, and marks selected target
- Helps diagnose Prime Video scenarios where preview vs main video both exist
2026-06-01 15:58:31 +02:00