diff --git a/src/app/components/url-preview/YouTubeEmbed.tsx b/src/app/components/url-preview/YouTubeEmbed.tsx index 1e8f66f..0438a6e 100644 --- a/src/app/components/url-preview/YouTubeEmbed.tsx +++ b/src/app/components/url-preview/YouTubeEmbed.tsx @@ -21,8 +21,9 @@ const YOUTUBE_URL_PATTERNS = [ /** * YouTube embed URL base - fallback when yt-dlp is not available + * Using youtube-nocookie.com to avoid Error 153 and embedding restrictions */ -const YOUTUBE_EMBED_BASE = 'https://www.youtube.com/embed'; +const YOUTUBE_EMBED_BASE = 'https://www.youtube-nocookie.com/embed'; /** * YouTube oEmbed API base - used to fetch video metadata @@ -88,10 +89,15 @@ export function extractYouTubeVideoId(url: string): string | null { /** * Generates a YouTube embed URL for a video (fallback) * @param videoId The YouTube video ID - * @returns The embed URL + * @returns The embed URL with parameters to avoid Error 153 */ export function getYouTubeEmbedUrl(videoId: string): string { - return `${YOUTUBE_EMBED_BASE}/${videoId}?autoplay=1`; + // Use youtube-nocookie.com and add parameters to help with embedding + // autoplay=1 - start playing when loaded + // rel=0 - only show related videos from same channel + // modestbranding=1 - minimize YouTube branding + // enablejsapi=1 - enable JavaScript API for better compatibility + return `${YOUTUBE_EMBED_BASE}/${videoId}?autoplay=1&rel=0&modestbranding=1&enablejsapi=1`; } /** @@ -255,8 +261,9 @@ export const YouTubeEmbed = as<'div', YouTubeEmbedProps>(({ url, ...props }, ref className={css.YouTubeEmbedIframe} src={embedUrl} title="YouTube video player" - allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" + allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen + referrerPolicy="strict-origin-when-cross-origin" /> ); diff --git a/src/app/utils/tauri.ts b/src/app/utils/tauri.ts index 26082c5..45f82f7 100644 --- a/src/app/utils/tauri.ts +++ b/src/app/utils/tauri.ts @@ -66,11 +66,28 @@ export interface YouTubeStreamInfo { * @throws If yt-dlp is not installed or fails */ export const getYouTubeStream = async (url: string): Promise => { - if (typeof window !== 'undefined' && (window as any).__TAURI__) { + if (typeof window === 'undefined') { + throw new Error('YouTube streaming requires desktop app with yt-dlp installed'); + } + + // Check for Electron + const electron = (window as any).electron; + if (electron?.youtube?.getStream) { + const result = await electron.youtube.getStream(url); + // Electron returns { success: true, data: { video_url, title } } or { success: false, error } + if (result.success === false) { + throw new Error(result.error || 'Failed to get YouTube stream'); + } + return result.data; + } + + // Check for Tauri + if ((window as any).__TAURI__) { const { invoke } = await import('@tauri-apps/api/core'); return invoke('get_youtube_stream', { url }); } - throw new Error('YouTube streaming requires Tauri desktop app with yt-dlp installed'); + + throw new Error('YouTube streaming requires desktop app with yt-dlp installed'); }; /** @@ -78,7 +95,18 @@ export const getYouTubeStream = async (url: string): Promise * @returns True if yt-dlp streaming is available */ export const isYouTubeStreamingAvailable = (): boolean => { - return typeof window !== 'undefined' && !!(window as any).__TAURI__; + if (typeof window === 'undefined') { + return false; + } + + // Check for Electron + const electron = (window as any).electron; + if (electron?.youtube?.getStream) { + return true; + } + + // Check for Tauri + return !!(window as any).__TAURI__; }; /**