Skip to content

feat: youtube embeds #4841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

Conversation

renja-g
Copy link

@renja-g renja-g commented Jul 9, 2025

Is discussed in #4834
This pull request introduces functionality to embed YouTube videos in the MemoView component by extracting video IDs from markdown nodes and rendering them using a new MemoYoutubeEmbedListView component. The changes involve adding utility functions for video ID extraction, updating the MemoView component to use these functions, and creating a dedicated component for rendering embedded YouTube videos.
image

@renja-g renja-g requested a review from boojack as a code owner July 9, 2025 17:07
@boojack boojack requested a review from Copilot July 10, 2025 02:20
Copilot

This comment was marked as outdated.

Copy link
Collaborator

@johnnyjoygh johnnyjoygh left a comment

Choose a reason for hiding this comment

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

LGTM

@johnnyjoygh johnnyjoygh requested a review from Copilot July 17, 2025 08:59
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request introduces YouTube video embedding functionality to the MemoView component by automatically detecting YouTube URLs in markdown content and rendering them as embedded video players.

  • Adds utility functions to extract YouTube video IDs from various URL formats and traverse markdown nodes
  • Creates a new component for rendering YouTube embeds in a horizontal scrollable list
  • Integrates the YouTube embed functionality into the existing MemoView component

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
web/src/utils/youtube.ts New utility functions for YouTube URL parsing and video ID extraction from markdown nodes
web/src/components/MemoYoutubeEmbedListView.tsx New component that renders YouTube videos as embedded iframes in a horizontal layout
web/src/components/MemoView.tsx Updates to integrate YouTube embed functionality using the new utility and component

const stack: Node[] = [...nodes];

while (stack.length) {
const node = stack.pop()!;
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

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

Using the non-null assertion operator (!) on stack.pop() assumes the stack is never empty, but this could lead to runtime errors if the stack becomes empty unexpectedly. Consider using a safer approach like checking if the popped value exists before proceeding.

Suggested change
const node = stack.pop()!;
const node = stack.pop();
if (!node) continue;

Copilot uses AI. Check for mistakes.

<div className="relative w-full pt-[56.25%] rounded-lg overflow-hidden border border-border/60 bg-popover">
<iframe
className="absolute top-0 left-0 w-full h-full"
src={`https://www.youtube.com/embed/${videoId}`}
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

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

The videoId is directly interpolated into the iframe src without validation or sanitization. Although the video IDs are extracted using regex patterns, consider adding additional validation to ensure the videoId contains only expected characters (alphanumeric, hyphens, underscores) to prevent potential XSS attacks.

Copilot uses AI. Check for mistakes.

className="absolute top-0 left-0 w-full h-full"
src={`https://www.youtube.com/embed/${videoId}`}
title="YouTube video player"
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

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

[nitpick] The iframe permissions are quite permissive, including clipboard-write and accelerometer access. Consider restricting permissions to only what's necessary for video playback (e.g., encrypted-media, picture-in-picture) to follow the principle of least privilege.

Suggested change
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allow="encrypted-media; picture-in-picture"

Copilot uses AI. Check for mistakes.

Comment on lines +13 to +38
const EmbedCard = ({ videoId, className }: { videoId: string; className?: string }) => {
return (
<div className={cn("relative w-full", className)}>
<div className="relative w-full pt-[56.25%] rounded-lg overflow-hidden border border-border/60 bg-popover">
<iframe
className="absolute top-0 left-0 w-full h-full"
src={`https://www.youtube.com/embed/${videoId}`}
title="YouTube video player"
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
/>
</div>
</div>
);
};

return (
<div className="w-full flex flex-row justify-start overflow-auto gap-2">
{videoIds.map((id) => (
<div key={id} className="w-80 flex flex-col justify-start items-start shrink-0">
<EmbedCard videoId={id} className="max-h-64 grow" />
</div>
))}
</div>
);
};
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

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

The EmbedCard component is defined inside the render function, which means it will be recreated on every render. Consider moving this component outside of the main component or using useCallback to optimize performance.

Suggested change
const EmbedCard = ({ videoId, className }: { videoId: string; className?: string }) => {
return (
<div className={cn("relative w-full", className)}>
<div className="relative w-full pt-[56.25%] rounded-lg overflow-hidden border border-border/60 bg-popover">
<iframe
className="absolute top-0 left-0 w-full h-full"
src={`https://www.youtube.com/embed/${videoId}`}
title="YouTube video player"
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
/>
</div>
</div>
);
};
return (
<div className="w-full flex flex-row justify-start overflow-auto gap-2">
{videoIds.map((id) => (
<div key={id} className="w-80 flex flex-col justify-start items-start shrink-0">
<EmbedCard videoId={id} className="max-h-64 grow" />
</div>
))}
</div>
);
};
const EmbedCard = memo(({ videoId, className }: { videoId: string; className?: string }) => {
return (
<div className={cn("relative w-full", className)}>
<div className="relative w-full pt-[56.25%] rounded-lg overflow-hidden border border-border/60 bg-popover">
<iframe
className="absolute top-0 left-0 w-full h-full"
src={`https://www.youtube.com/embed/${videoId}`}
title="YouTube video player"
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
/>

Copilot uses AI. Check for mistakes.

Copy link
Author

Choose a reason for hiding this comment

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

I already commented on Copilots "suggestions" once.

renja-g and others added 4 commits July 20, 2025 15:22
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Renja <76645494+renja-g@users.noreply.github.com>
@renja-g renja-g force-pushed the feature/youtube-embeds branch from d529be9 to f97d525 Compare July 20, 2025 13:23
@renja-g
Copy link
Author

renja-g commented Jul 20, 2025

If there is anything you want changed, let me know.

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.

2 participants