feat: add video title fetching from YouTube oEmbed API in YouTubeEmbed component
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { MouseEvent, useCallback, useState } from 'react';
|
||||
import React, { MouseEvent, useCallback, useEffect, useState } from 'react';
|
||||
import { Box, Icon, Icons, Spinner, Text, as } from 'folds';
|
||||
import * as css from './YouTubeEmbed.css';
|
||||
import { getYouTubeStream, isYouTubeStreamingAvailable, openExternalUrl } from '../../utils/tauri';
|
||||
@@ -24,6 +24,11 @@ const YOUTUBE_URL_PATTERNS = [
|
||||
*/
|
||||
const YOUTUBE_EMBED_BASE = 'https://www.youtube.com/embed';
|
||||
|
||||
/**
|
||||
* YouTube oEmbed API base - used to fetch video metadata
|
||||
*/
|
||||
const YOUTUBE_OEMBED_API = 'https://www.youtube.com/oembed';
|
||||
|
||||
/**
|
||||
* Get YouTube thumbnail URL for a video
|
||||
* @param videoId The YouTube video ID
|
||||
@@ -33,6 +38,23 @@ function getYouTubeThumbnailUrl(videoId: string): string {
|
||||
return `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch YouTube video title using oEmbed API
|
||||
* @param url The YouTube video URL
|
||||
* @returns The video title or null if fetch fails
|
||||
*/
|
||||
async function fetchYouTubeTitle(url: string): Promise<string | null> {
|
||||
try {
|
||||
const oembedUrl = `${YOUTUBE_OEMBED_API}?url=${encodeURIComponent(url)}&format=json`;
|
||||
const response = await fetch(oembedUrl);
|
||||
if (!response.ok) return null;
|
||||
const data = await response.json();
|
||||
return data.title || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a URL is a YouTube URL
|
||||
* @param url The URL to check
|
||||
@@ -101,6 +123,22 @@ type YouTubeEmbedProps = {
|
||||
export const YouTubeEmbed = as<'div', YouTubeEmbedProps>(({ url, ...props }, ref) => {
|
||||
const videoId = extractYouTubeVideoId(url);
|
||||
const [playerState, setPlayerState] = useState<PlayerState>({ status: 'thumbnail' });
|
||||
const [videoTitle, setVideoTitle] = useState<string | null>(null);
|
||||
|
||||
// Fetch video title on mount
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
fetchYouTubeTitle(url).then((title) => {
|
||||
if (!cancelled) {
|
||||
setVideoTitle(title);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
const handlePlay = useCallback(async () => {
|
||||
if (!isYouTubeStreamingAvailable()) {
|
||||
@@ -141,11 +179,19 @@ export const YouTubeEmbed = as<'div', YouTubeEmbedProps>(({ url, ...props }, ref
|
||||
</div>
|
||||
);
|
||||
|
||||
// Determine what to show in the header based on video title availability
|
||||
const getHeaderLabel = (suffix?: string) => {
|
||||
if (videoTitle) {
|
||||
return suffix ? `${videoTitle} ${suffix}` : videoTitle;
|
||||
}
|
||||
return suffix ? `YouTube ${suffix}` : 'YouTube';
|
||||
};
|
||||
|
||||
// Thumbnail preview - click to play
|
||||
if (playerState.status === 'thumbnail') {
|
||||
return (
|
||||
<Box shrink="No" className={css.YouTubeEmbed} direction="Column" {...props} ref={ref}>
|
||||
{renderHeader('YouTube')}
|
||||
{renderHeader(getHeaderLabel())}
|
||||
<button
|
||||
type="button"
|
||||
className={css.YouTubeThumbnailButton}
|
||||
@@ -169,7 +215,7 @@ export const YouTubeEmbed = as<'div', YouTubeEmbedProps>(({ url, ...props }, ref
|
||||
if (playerState.status === 'loading') {
|
||||
return (
|
||||
<Box shrink="No" className={css.YouTubeEmbed} direction="Column" {...props} ref={ref}>
|
||||
{renderHeader('YouTube - Loading...')}
|
||||
{renderHeader(getHeaderLabel('- Loading...'))}
|
||||
<Box
|
||||
className={css.YouTubeEmbedIframe}
|
||||
alignItems="Center"
|
||||
@@ -204,7 +250,7 @@ export const YouTubeEmbed = as<'div', YouTubeEmbedProps>(({ url, ...props }, ref
|
||||
const embedUrl = getYouTubeEmbedUrl(videoId);
|
||||
return (
|
||||
<Box shrink="No" className={css.YouTubeEmbed} direction="Column" {...props} ref={ref}>
|
||||
{renderHeader('YouTube')}
|
||||
{renderHeader(getHeaderLabel())}
|
||||
<iframe
|
||||
className={css.YouTubeEmbedIframe}
|
||||
src={embedUrl}
|
||||
|
||||
Reference in New Issue
Block a user