diff --git a/src/components/YoutubeEmbeddedPlayer/Player.tsx b/src/components/YoutubeEmbeddedPlayer/Player.tsx index c1bd2cc..3c6590a 100644 --- a/src/components/YoutubeEmbeddedPlayer/Player.tsx +++ b/src/components/YoutubeEmbeddedPlayer/Player.tsx @@ -4,6 +4,30 @@ import mediaManager from '@/services/media-manager.service' import { YouTubePlayer } from '@/types/youtube' import { memo, useEffect, useRef, useState } from 'react' +let ytApiReady = false +const ytApiCallbacks: (() => void)[] = [] + +function ensureYTApi(callback: () => void) { + if (ytApiReady && window.YT?.Player) { + callback() + return + } + + ytApiCallbacks.push(callback) + + if (!document.querySelector('script[src="https://www.youtube.com/iframe_api"]')) { + const script = document.createElement('script') + script.src = 'https://www.youtube.com/iframe_api' + document.body.appendChild(script) + + window.onYouTubeIframeAPIReady = () => { + ytApiReady = true + ytApiCallbacks.forEach((cb) => cb()) + ytApiCallbacks.length = 0 + } + } +} + interface PlayerProps { videoId: string isShort: boolean @@ -25,19 +49,11 @@ const Player = memo(({ videoId, isShort, className }: PlayerProps) => { if (!videoId || !containerRef.current) return - if (!window.YT) { - const script = document.createElement('script') - script.src = 'https://www.youtube.com/iframe_api' - document.body.appendChild(script) - - window.onYouTubeIframeAPIReady = () => { - if (!unmountedRef.current) { - initPlayer() - } + ensureYTApi(() => { + if (!unmountedRef.current) { + initPlayer() } - } else { - initPlayer() - } + }) let checkMutedInterval: NodeJS.Timeout | null = null function initPlayer() { diff --git a/src/constants.ts b/src/constants.ts index 6a0784e..e4e948e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -142,9 +142,9 @@ export const LN_INVOICE_REGEX = /(ln(?:bc|tb|bcrt))([0-9]+[munp]?)?1([02-9ac-hj- export const EMOJI_REGEX = /[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F900}-\u{1F9FF}]|[\u{1FA70}-\u{1FAFF}]|[\u{1F004}]|[\u{1F0CF}]|[\u{1F18E}]|[\u{3030}]|[\u{2B50}]|[\u{2B55}]|[\u{2934}-\u{2935}]|[\u{2B05}-\u{2B07}]|[\u{2B1B}-\u{2B1C}]|[\u{3297}]|[\u{3299}]|[\u{303D}]|[\u{00A9}]|[\u{00AE}]|[\u{2122}]|[\u{23E9}-\u{23EF}]|[\u{23F0}]|[\u{23F3}]|[\u{FE00}-\u{FE0F}]|[\u{200D}]/gu export const YOUTUBE_URL_REGEX = - /https?:\/\/(?:(?:www|m)\.)?(?:youtube\.com\/(?:watch\?[^#\s]*|embed\/[\w-]+|shorts\/[\w-]+|live\/[\w-]+)|youtu\.be\/[\w-]+)(?:\?[^#\s]*)?(?:#[^\s]*)?/gi + /https?:\/\/(?:(?:www|m)\.)?(?:youtube\.com\/(?:watch\?[^#\s]*|embed\/[\w-]+|shorts\/[\w-]+|live\/[\w-]+)|youtu\.be\/[\w-]+)(?:\?[^#\s]*)?(?:#[^\s]*)?/i export const X_URL_REGEX = - /https?:\/\/(?:www\.)?(twitter\.com|x\.com)\/(?:#!\/)?(\w+)\/status(?:es)?\/(\d+)(?:[?#].*)?/gi + /https?:\/\/(?:www\.)?(twitter\.com|x\.com)\/(?:#!\/)?(\w+)\/status(?:es)?\/(\d+)(?:[?#].*)?/i export const JUMBLE_PUBKEY = 'f4eb8e62add1340b9cadcd9861e669b2e907cea534e0f7f3ac974c11c758a51a' export const CODY_PUBKEY = '8125b911ed0e94dbe3008a0be48cfe5cd0c0b05923cfff917ae7e87da8400883'