Skip to content

fix: don't restart the player when the 720p transform finalizes mid-review - #66

Open
maria-rcks wants to merge 1 commit into
pingdotgg:mainfrom
maria-rcks:fix/keep-original-playback-on-720p-finalize
Open

fix: don't restart the player when the 720p transform finalizes mid-review#66
maria-rcks wants to merge 1 commit into
pingdotgg:mainfrom
maria-rcks:fix/keep-original-playback-on-720p-finalize

Conversation

@maria-rcks

@maria-rcks maria-rcks commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

What happened

A reviewer watching a freshly uploaded video (playing the Original source while Mux was still encoding) got kicked off the player the moment the 720p transform finalized. The page appeared to restart: the player unmounted, showed "Loading 720p stream...", then came back at 0:00 on the new source.

Why

While a video is processing, the dashboard plays the original upload but the source preference silently defaults to mux720. When Convex reactively flips video.status to ready, selectDashboardPlaybackUrl immediately stops returning the (still perfectly good) original URL and returns the Mux session URL instead — which is still null because getPlaybackSession hasn't resolved yet. So the active URL goes original → null → mux. VideoPlayer only renders when the URL is truthy, so the null unmounts it and the remount starts from zero.

The fix

  • When an original playback URL is acquired while the video is processing, commit a video-scoped original source preference (reusing the existing sourcePreference state — no new state machine). The playback URL then never passes through null: the reviewer stays on the original source across the processing → ready flip until they explicitly switch quality or original playback fails.
  • Fresh loads of already-ready videos are unchanged: they still wait for the 720p session (no original flash).
  • Explicit quality switching and the existing original-failure → 720p recovery path are unchanged.
  • Playback session + original URL state are now scoped by video id so they can't leak across version navigation.
  • If the background 720p session fetch fails while original playback continues, a non-interrupting Retry 720p banner is shown.

The preference transition lives in a small pure helper (selectDashboardPlaybackPreferenceAfterOriginalLoad) next to the URL selector so the regression is pinned by unit tests.

Testing

  • bun test src/lib/dashboardPlaybackSource.test.ts — 8 pass, 0 fail, including the regression sequence: processing-on-original → ready-with-no-session → ready-with-session never returns null and never auto-switches.
  • tsc --noEmit, eslint, prettier --check on changed files — clean.
  • Not verified end-to-end in a browser (no local Convex env in this checkout); worth a manual check of: upload → play original mid-processing → wait for finalize → playback continues uninterrupted.

Note

Medium Risk
Touches core dashboard video playback and source-selection timing; behavior is well covered by unit tests but end-to-end encoding transitions weren't verified in-browser.

Overview
Fixes reviewers getting kicked out of the player when a video flips from processing to ready while they're watching the Original upload.

The active URL was briefly going original → null → 720p because the default preference stayed on mux720 once Mux was ready, but the 720p session wasn't loaded yet—so VideoPlayer unmounted and restarted at 0:00. When the original URL is fetched during a processing session, the page now commits a video-scoped original preference via selectDashboardPlaybackPreferenceAfterOriginalLoad, with sawProcessingVideoIdRef so a status flip mid-request doesn't drop that lock.

Playback session and original URL state are keyed by resolvedVideoId so version switches don't reuse stale URLs/posters. If 720p session load fails while Original keeps playing, a non-blocking Retry 720p banner is shown. Unit tests cover the processing → ready sequence and preference rules.

Reviewed by Cursor Bugbot for commit 89f09fa. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix player restart when 720p transform finalizes mid-review

  • Ties 720p and Original playback state to the current videoId in VideoPage to prevent cross-video state leakage when a transform completes during review.
  • Adds selectDashboardPlaybackPreferenceAfterOriginalLoad in dashboardPlaybackSource.ts: locks playback to 'original' only when the page started while the video was processing and an Original URL exists, leaving any existing preference unchanged.
  • Tracks whether the page observed a 'processing' status via sawProcessingVideoIdRef to inform the preference decision without re-triggering on unrelated state changes.
  • Shows a 'Retry 720p' button when the 720p session fails to load while Original playback continues, or when switching away from Original due to issues.
  • Behavioral Change: pages that open while a video is still processing will now stay on the Original source even after Mux becomes ready, rather than switching to 720p.

Macroscope summarized 89f09fa.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed video playback state to reliably match the currently selected video, avoiding stale sessions/URLs.
    • Improved switching logic between Original and Mux playback, including correct preference handling after Original loads.
    • Added an explicit Retry 720p action when 720p fails (outside automatic/manual retry conditions).
    • Updated the video poster to reflect the active playback session.
  • Tests

    • Expanded automated coverage for playback source selection, fallback behavior, loading states, and preference preservation.

@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

@maria-rcks is attempting to deploy a commit to the Ping Labs Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Video playback state is keyed by resolvedVideoId, preventing stale sessions and URLs across route changes. Original-load preference selection is centralized and tested, while playback errors expose conditional 720p retry controls and use the active session poster.

Changes

Video playback flow

Layer / File(s) Summary
Playback source preference selection
src/lib/dashboardPlaybackSource.ts, src/lib/dashboardPlaybackSource.test.ts
Adds preference selection after original playback loads and expands coverage for Mux readiness, Original preference preservation, fallback behavior, and unavailable sources.
Video-scoped playback state
app/routes/dashboard/-video.tsx
Associates playback sessions and original URLs with resolvedVideoId, filters stale derived values, resets scoped state, and applies the new preference helper after original playback loads.
Playback error and poster rendering
app/routes/dashboard/-video.tsx
Adds conditional “Retry 720p” rendering and uses the active playback session’s poster URL.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VideoPage
  participant getPlaybackSession
  participant selectDashboardPlaybackPreferenceAfterOriginalLoad
  participant VideoPlayer
  VideoPage->>getPlaybackSession: load session for resolvedVideoId
  getPlaybackSession-->>VideoPage: return video-scoped session
  VideoPage->>selectDashboardPlaybackPreferenceAfterOriginalLoad: evaluate loaded original URL
  selectDashboardPlaybackPreferenceAfterOriginalLoad-->>VideoPage: return playback preference
  VideoPage->>VideoPlayer: render active playback URL and poster
Loading

Possibly related PRs

  • pingdotgg/lawn#49: Modifies the dashboard VideoPage playback recovery and original-versus-720p selection flow.
  • pingdotgg/lawn#50: Updates the shared dashboard playback source helper, tests, and video route integration.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: preventing player restarts when the 720p transform finalizes during review.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 624991a. Configure here.

Comment thread app/routes/dashboard/-video.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/routes/dashboard/-video.tsx (1)

557-566: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Avoid unnecessary re-renders by returning the existing state object.

When nextPreference matches the currentPreference, returning a newly constructed object { videoId: resolvedVideoId, source: nextPreference } will cause React to perform an unnecessary re-render because object references won't match. You can return the existing preference object when the resolved source hasn't changed.

💡 Proposed refactor
         setSourcePreference((preference) => {
           const currentPreference =
             preference?.videoId === resolvedVideoId ? preference.source : null;
           const nextPreference = selectDashboardPlaybackPreferenceAfterOriginalLoad({
             currentPreference,
             startedWhileProcessing,
             originalUrl: result.url,
           });
+          if (currentPreference === nextPreference) return preference;
           return nextPreference ? { videoId: resolvedVideoId, source: nextPreference } : preference;
         });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/dashboard/-video.tsx` around lines 557 - 566, Update the
setSourcePreference callback around
selectDashboardPlaybackPreferenceAfterOriginalLoad so it returns the existing
preference object when nextPreference equals currentPreference; only construct a
new { videoId, source } object when the resolved source actually changes, while
preserving the existing fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/routes/dashboard/-video.tsx`:
- Around line 1173-1181: Update both affected Button elements in
app/routes/dashboard/-video.tsx at lines 1173-1181 and 1155-1162: remove
variant="outline" so they use the solid variant, and add font-bold rounded-none
to their classes. For the banner at lines 1173-1181, replace its background and
text classes with bg-[`#f0f0e8`] and text-[`#1a1a1a`].

---

Nitpick comments:
In `@app/routes/dashboard/-video.tsx`:
- Around line 557-566: Update the setSourcePreference callback around
selectDashboardPlaybackPreferenceAfterOriginalLoad so it returns the existing
preference object when nextPreference equals currentPreference; only construct a
new { videoId, source } object when the resolved source actually changes, while
preserving the existing fallback behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 987a1f08-2f84-41b9-b37e-f34bfc24089a

📥 Commits

Reviewing files that changed from the base of the PR and between fc6b979 and 624991a.

📒 Files selected for processing (3)
  • app/routes/dashboard/-video.tsx
  • src/lib/dashboardPlaybackSource.test.ts
  • src/lib/dashboardPlaybackSource.ts

Comment on lines +1173 to 1181
<div
className="flex flex-shrink-0 items-center justify-between gap-3 bg-[#2a2114] px-4 py-2 text-sm text-[#fff1d5]"
role="status"
>
<span>{playbackLoadError} Original playback is continuing.</span>
<Button variant="outline" size="sm" onClick={handleRetryPlaybackSession}>
Retry 720p
</Button>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align new UI elements with the coding guidelines.

Both newly added UI sections violate the required visual guidelines for banners and buttons. As per coding guidelines, backgrounds must use warm cream (#f0f0e8), primary text must use near-black (#1a1a1a), buttons must have solid backgrounds with bold text, and primary UI elements must not have rounded corners.

  • app/routes/dashboard/-video.tsx#L1173-L1181: Update the banner to use bg-[#f0f0e8] and text-[#1a1a1a]. Remove variant="outline" from the button (falling back to a solid variant) and add font-bold rounded-none to its classes.
  • app/routes/dashboard/-video.tsx#L1155-L1162: Remove variant="outline" from the button and append font-bold rounded-none to its classes to adhere to the solid background and sharp edge constraints.
📍 Affects 1 file
  • app/routes/dashboard/-video.tsx#L1173-L1181 (this comment)
  • app/routes/dashboard/-video.tsx#L1155-L1162
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/dashboard/-video.tsx` around lines 1173 - 1181, Update both
affected Button elements in app/routes/dashboard/-video.tsx at lines 1173-1181
and 1155-1162: remove variant="outline" so they use the solid variant, and add
font-bold rounded-none to their classes. For the banner at lines 1173-1181,
replace its background and text classes with bg-[`#f0f0e8`] and text-[`#1a1a1a`].

Source: Coding guidelines

While a video is still processing, the dashboard player falls back to
the original upload. When Mux finalized the 720p asset mid-review, the
reactive status flip made the playback selector demand the Mux session
URL before it had been fetched, so the active URL passed through null,
unmounting the player and restarting playback from zero.

Now, when the original URL is acquired during processing, the session
commits a video-scoped 'original' preference, so the player stays on
the original source across the processing->ready transition until the
reviewer explicitly switches quality or original playback fails. Fresh
loads of already-ready videos still wait for the 720p session as
before. Playback session and original URL state are scoped by video id
so they can't leak across version navigation, and a failed background
720p session fetch now surfaces a Retry action without interrupting
original playback.
@maria-rcks
maria-rcks force-pushed the fix/keep-original-playback-on-720p-finalize branch from 624991a to 89f09fa Compare July 17, 2026 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant