Skip to content

Add delayed trailer playback preview to media cards#40

Open
Swastikdan wants to merge 1 commit into
masterfrom
codex/add-trailer-preview-on-mediacard-hover
Open

Add delayed trailer playback preview to media cards#40
Swastikdan wants to merge 1 commit into
masterfrom
codex/add-trailer-preview-on-mediacard-hover

Conversation

@Swastikdan
Copy link
Copy Markdown
Owner

@Swastikdan Swastikdan commented Apr 7, 2026

Motivation

  • Provide a richer hover experience on media cards by replacing the poster/backdrop with a short, autoplaying trailer preview when the user hovers for a sustained amount of time.
  • Only activate previews for movie/TV cards (not person cards) and avoid fetching video data until preview starts to reduce unnecessary network work.

Description

  • Added a new TrailerPreview wrapper component inside src/components/media-card.tsx that fetches TMDB videos via getVideos and selects a YouTube Trailer or Teaser to use as the preview iframe.
  • Implemented a 2-second hover delay using a timeout and per-card isPreviewActive state, with proper timeout cleanup on unmount and on mouse leave.
  • Swaps the static Image for an autoplaying, muted, looping YouTube iframe while preview is active and restores the image when hover ends.
  • Wired the preview behavior into both horizontal and vertical MediaCard variants; person cards remain unchanged.

Testing

  • Ran pnpm -s exec biome check src/components/media-card.tsx and the check completed successfully.
  • Ran pnpm -s exec tsc --noEmit and TypeScript checks completed successfully for the modified file.

Codex Task

Summary by CodeRabbit

  • New Features
    • Media cards now display an auto-playing, muted YouTube trailer preview when you hover over them. The trailer overlay appears after 2 seconds and disappears when you move your mouse away from the card.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pebbly Ready Ready Preview, Comment Apr 7, 2026 10:06am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Enhanced the media card component with a TrailerPreview wrapper that fetches and conditionally displays YouTube trailer videos on hover. Added hover-state management with a 2-second delay timer to HorizontalCard and VerticalCard, replacing the static image container with the new preview component.

Changes

Cohort / File(s) Summary
Trailer Preview Feature
src/components/media-card.tsx
Added TrailerPreview component that uses useQuery to fetch videos and overlay a looping, autoplaying YouTube iframe. Introduced hover-driven preview activation with isPreviewActive state and hoverTimeoutRef for 2-second delayed activation. Integrated new component into HorizontalCard and VerticalCard. Added React hooks (useState, useRef, useEffect) and React Query (useQuery) imports.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Card as HorizontalCard/<br/>VerticalCard
    participant Preview as TrailerPreview
    participant Query as useQuery
    participant API as getVideos API
    participant YouTube as YouTube iframe

    User->>Card: onMouseEnter
    Card->>Card: Set hoverTimeoutRef (2s delay)
    
    User->>Card: Wait 2 seconds
    Card->>Preview: Activate preview (isPreviewActive=true)
    
    Preview->>Query: Execute query for videos
    Query->>API: getVideos({id, type})
    API-->>Query: Return video list
    
    Query->>Preview: Select first YouTube<br/>Trailer/Teaser item
    Preview->>YouTube: Render iframe<br/>(looping, autoplaying, muted)
    YouTube-->>Preview: Display overlay on card
    
    User->>Card: onMouseLeave
    Card->>Card: Clear hoverTimeoutRef
    Card->>Preview: Deactivate preview<br/>(isPreviewActive=false)
    Preview->>YouTube: Remove iframe
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add delayed trailer playback preview to media cards' directly and clearly summarizes the main change: adding a trailer preview feature with delayed activation to media card components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/add-trailer-preview-on-mediacard-hover

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/media-card.tsx`:
- Around line 164-178: The hover enter/leave timers are currently attached to
the <Link> element (handlers using hoverTimeoutRef, setIsPreviewActive) which
causes the preview to collapse when moving to sibling elements like the
watchlist button; move the onMouseEnter/onMouseLeave logic (the hoverTimeoutRef
clear/set and setIsPreviewActive calls) from the Link to the outer card
container element (or to the TrailerPreview surface component) so the hover
region covers the entire visual card (and also update the same pattern at the
other occurrence around lines with the duplicate handlers). Ensure
hoverTimeoutRef is still used the same way and cleared on leave, and that the
watchlist button remains outside the Link but inside the new hover container so
preview persists while interacting with it.
- Around line 118-119: The iframe in the MediaCard component currently loads
from www.youtube.com when isPreviewActive becomes true, which permits immediate
cookies/tracking; update the src template used where trailerKey is injected (the
src prop in the iframe inside MediaCard) to use the privacy-enhanced domain
youtube-nocookie.com instead of www.youtube.com while preserving all query
params (autoplay, mute, controls, playsinline, loop, playlist, rel,
modestbranding) so the hover preview behavior and trailerKey usage remain
identical but without preloading YouTube cookies.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 6de0face-8693-4345-af37-0ddf3dbe6531

📥 Commits

Reviewing files that changed from the base of the PR and between ddb18d5 and 7acf4ae.

📒 Files selected for processing (1)
  • src/components/media-card.tsx

Comment on lines +118 to +119
src={`https://www.youtube.com/embed/${trailerKey}?autoplay=1&mute=1&controls=0&playsinline=1&loop=1&playlist=${trailerKey}&rel=0&modestbranding=1`}
allow="autoplay; encrypted-media; picture-in-picture"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "media-card.tsx" -type f

Repository: Swastikdan/Pebbly

Length of output: 92


🏁 Script executed:

cat -n ./src/components/media-card.tsx | head -150

Repository: Swastikdan/Pebbly

Length of output: 5068


🏁 Script executed:

sed -n '130,200p' ./src/components/media-card.tsx | cat -n

Repository: Swastikdan/Pebbly

Length of output: 2910


🏁 Script executed:

sed -n '100,127p' ./src/components/media-card.tsx | cat -n

Repository: Swastikdan/Pebbly

Length of output: 1080


🏁 Script executed:

web_search
What is the privacy difference between youtube.com and youtube-nocookie.com embed URLs

Repository: Swastikdan/Pebbly

Length of output: 154


🌐 Web query:

YouTube youtube-nocookie.com privacy embed differences

💡 Result:

  • youtube.com/embed/... (standard embed): Loads the regular YouTube embedded player and is more likely to allow YouTube-related storage/cookies and personalization signals to be used as soon as the embed loads (even before the user presses play). [3]

  • youtube-nocookie.com/embed/... (“Privacy-Enhanced Mode”):

    • It’s the same embed player, but served from a different domain that YouTube documents as “Privacy Enhanced Mode.” [1]
    • Personalization impact: Views from this embedded player won’t be used to personalize the viewer’s YouTube browsing experience, and ads shown in this embedded player are non-personalized. Those views also won’t be used to personalize ads shown to the viewer outside your site/app. [1]
    • Cookie behavior: Commonly described as not setting cookies until the user starts playback / interacts (i.e., no cookie on initial page load). [2][3]
    • How to use it: Replace https://www.youtube.com with https://www.youtube-nocookie.com in the iframe src (and allowlist youtube-nocookie.com if you use a firewall/allowlist). [1]

Sources:
[1] YouTube Help — “Embed videos & playlists” (Privacy Enhanced Mode section) (support.google.com)
[2] Ignite.video — explanation of NoCookie / privacy-enhanced embeds (ignite.video)
[3] Stack Overflow — summary that nocookie waits until playback before dropping cookies (stackoverflow.com)

Citations:


Use the privacy-enhanced YouTube embed for hover previews.

The iframe is rendered when isPreviewActive becomes true (after a 2-second hover delay), which creates a www.youtube.com connection before any click. This allows YouTube to set cookies and track the user immediately on load. Using www.youtube-nocookie.com defers cookie/personalization tracking until the user actually interacts with the player.

Suggested change
-						src={`https://www.youtube.com/embed/${trailerKey}?autoplay=1&mute=1&controls=0&playsinline=1&loop=1&playlist=${trailerKey}&rel=0&modestbranding=1`}
+						src={`https://www.youtube-nocookie.com/embed/${trailerKey}?autoplay=1&mute=1&controls=0&playsinline=1&loop=1&playlist=${trailerKey}&rel=0&modestbranding=1`}
+						referrerPolicy="strict-origin-when-cross-origin"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
src={`https://www.youtube.com/embed/${trailerKey}?autoplay=1&mute=1&controls=0&playsinline=1&loop=1&playlist=${trailerKey}&rel=0&modestbranding=1`}
allow="autoplay; encrypted-media; picture-in-picture"
src={`https://www.youtube-nocookie.com/embed/${trailerKey}?autoplay=1&mute=1&controls=0&playsinline=1&loop=1&playlist=${trailerKey}&rel=0&modestbranding=1`}
referrerPolicy="strict-origin-when-cross-origin"
allow="autoplay; encrypted-media; picture-in-picture"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/media-card.tsx` around lines 118 - 119, The iframe in the
MediaCard component currently loads from www.youtube.com when isPreviewActive
becomes true, which permits immediate cookies/tracking; update the src template
used where trailerKey is injected (the src prop in the iframe inside MediaCard)
to use the privacy-enhanced domain youtube-nocookie.com instead of
www.youtube.com while preserving all query params (autoplay, mute, controls,
playsinline, loop, playlist, rel, modestbranding) so the hover preview behavior
and trailerKey usage remain identical but without preloading YouTube cookies.

Comment on lines +164 to +178
onMouseEnter={() => {
if (hoverTimeoutRef.current) {
clearTimeout(hoverTimeoutRef.current);
}
hoverTimeoutRef.current = setTimeout(() => {
setIsPreviewActive(true);
}, 2000);
}}
onMouseLeave={() => {
if (hoverTimeoutRef.current) {
clearTimeout(hoverTimeoutRef.current);
hoverTimeoutRef.current = null;
}
setIsPreviewActive(false);
}}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Attach the hover timer to the card shell instead of the link.

The preview is scoped to <Link>, but the watchlist button still sits outside it. Moving from the artwork onto that button fires onMouseLeave and tears the preview down even though the pointer is still over the same visual card. Put the enter/leave handlers on the outer card container or the TrailerPreview surface so the hover region matches what users see.

Also applies to: 271-285

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/media-card.tsx` around lines 164 - 178, The hover enter/leave
timers are currently attached to the <Link> element (handlers using
hoverTimeoutRef, setIsPreviewActive) which causes the preview to collapse when
moving to sibling elements like the watchlist button; move the
onMouseEnter/onMouseLeave logic (the hoverTimeoutRef clear/set and
setIsPreviewActive calls) from the Link to the outer card container element (or
to the TrailerPreview surface component) so the hover region covers the entire
visual card (and also update the same pattern at the other occurrence around
lines with the duplicate handlers). Ensure hoverTimeoutRef is still used the
same way and cleared on leave, and that the watchlist button remains outside the
Link but inside the new hover container so preview persists while interacting
with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant