import React, { useCallback, useMemo, useRef, useEffect } from 'react'; import ReactPlayer from 'react-player'; import handle from '@enact/core/handle'; var handledMediaEventsMap = [ 'onReady', 'onStart', 'onPlay', 'onProgress', 'onDuration', 'onPause', 'onBuffer', 'onBufferEnd', 'onSeek', 'onEnded', 'onError', ]; export default function TReactPlayer({ mediaEventsMap = handledMediaEventsMap, videoRef, url, dispatch, ...rest }) { const playerRef = useRef(null); // ๐Ÿ”ฝ [์ตœ์ ํ™”] handleEvent์˜ ์ฃผ์š” ์˜์กด์„ฑ ์ถ”์ถœ const playing = rest?.playing; const config = rest?.config; const handleEvent = useCallback( (type) => (ev) => { if (type === 'onReady') { if (videoRef) { const videoNode = playerRef.current.getInternalPlayer(); videoRef(videoNode); const iframeEl = typeof playerRef.current?.getInternalPlayer === 'function' ? playerRef.current.getInternalPlayer('iframe') : null; if (iframeEl) { iframeEl.setAttribute('tabIndex', '-1'); iframeEl.setAttribute('aria-hidden', 'true'); } if ( videoNode.tagName && !Object.prototype.hasOwnProperty.call(videoNode, 'proportionPlayed') ) { Object.defineProperties(videoNode, { error: { get: function () { return videoNode.networkState === videoNode.NETWORK_NO_SOURCE; }, }, loading: { get: function () { return videoNode.readyState < videoNode.HAVE_ENOUGH_DATA; }, }, proportionLoaded: { get: function () { return ( videoNode.buffered.length && videoNode.buffered.end(videoNode.buffered.length - 1) / videoNode.duration ); }, }, proportionPlayed: { get: function () { return videoNode.currentTime / videoNode.duration; }, }, }); } else if (!Object.prototype.hasOwnProperty.call(videoNode, 'proportionPlayed')) { videoNode.play = videoNode.playVideo; videoNode.pause = videoNode.pauseVideo; videoNode.seek = videoNode.seekTo; Object.defineProperties(videoNode, { currentTime: { get: function () { return videoNode.getCurrentTime(); }, set: function (time) { videoNode.seekTo(time); }, }, duration: { get: function () { return videoNode.getDuration(); }, }, paused: { get: function () { return videoNode.getPlayerState() !== 1; }, }, error: { get: function () { return !!videoNode?.playerInfo?.videoData?.errorCode; }, }, loading: { get: function () { return !videoNode?.playerInfo?.videoData?.isPlayable; //todo }, }, proportionLoaded: { get: function () { return videoNode?.getVideoBytesLoaded() ?? 0; }, }, proportionPlayed: { get: function () { const duration = videoNode.getDuration(); return duration ? videoNode.getCurrentTime() / duration : 0; }, }, playbackRate: { get: function () { return videoNode?.playerInfo?.playbackRate; }, set: function (playbackRate) { if (videoNode && videoNode.setPlaybackRate) { videoNode.setPlaybackRate(playbackRate); } }, }, }); } } handle.forward('onLoadStart', { type, ev }, rest); } if (type === 'onEnded' && rest?.isYoutube && rest?.type === 'VOD') { // YouTube ์žฌ์ƒ ์ข…๋ฃŒ ์‹œ iframe์ด ํฌ์ปค์Šค๋ฅผ ๊ฐ€์ ธ๊ฐ€๋Š” ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ const iframeEl = typeof playerRef.current?.getInternalPlayer === 'function' ? playerRef.current.getInternalPlayer('iframe') : null; iframeEl?.blur(); } handle.forward('onUpdate', { type, ev }, rest); }, [videoRef, playing, config] // โœ… ์ฃผ์š” ์˜์กด์„ฑ ์ถ”๊ฐ€ ); const handledMediaEvents = useMemo(() => { const events = {}; for (let i = 0; i < mediaEventsMap.length; i++) { const eventName = mediaEventsMap[i]; events[eventName] = handleEvent(eventName); } return events; }, [handleEvent, mediaEventsMap]); // ๐Ÿ”ฝ [์ตœ์ ํ™”] URL ๋ณ€๊ฒฝ ๋˜๋Š” ์–ธ๋งˆ์šดํŠธ ์‹œ ์ด์ „ ๋น„๋””์˜ค ์ •๋ฆฌ (๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€) useEffect(() => { return () => { const videoNode = playerRef.current?.getInternalPlayer(); if (videoNode && videoNode.pause) { console.log('[VIDEO-DEBUG] ๐Ÿงน ๋น„๋””์˜ค ์ •๋ฆฌ (URL ๋ณ€๊ฒฝ ๋˜๋Š” ์–ธ๋งˆ์šดํŠธ)'); videoNode.pause(); videoNode.src = ''; videoNode.srcObject = null; } }; }, [url]); // โœ… URL ๋ณ€๊ฒฝ ์‹œ์—๋„ ์ •๋ฆฌ ๋กœ์ง ์‹คํ–‰ return ( ); }