diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx index 9ec8371f..314ff62b 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx +++ b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx @@ -159,11 +159,19 @@ export default function TReactPlayer({ useEffect(() => { return () => { const videoNode = playerRef.current?.getInternalPlayer(); - if (videoNode && videoNode.pause) { - console.log('[VIDEO-DEBUG] ๐Ÿงน ๋น„๋””์˜ค ์ •๋ฆฌ (URL ๋ณ€๊ฒฝ ๋˜๋Š” ์–ธ๋งˆ์šดํŠธ)'); - videoNode.pause(); - videoNode.src = ''; - videoNode.srcObject = null; + if (videoNode) { + try { + // VideoPlayer.js์—์„œ ์ด๋ฏธ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์ตœ์†Œํ•œ ์ •๋ฆฌ๋งŒ + if (videoNode.pause) { + videoNode.pause(); + } + // ์ค‘๋ณต ์ •๋ฆฌ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ์กฐ๊ฑด ๋” ์—„๊ฒฉํ•˜๊ฒŒ + if (typeof videoNode.stopVideo === 'function' && videoNode.stopVideo !== videoNode.pause) { + videoNode.stopVideo(); + } + } catch (err) { + console.warn('[TReactPlayer] cleanup warning:', err); + } } }; }, [url]); // โœ… URL ๋ณ€๊ฒฝ ์‹œ์—๋„ ์ •๋ฆฌ ๋กœ์ง ์‹คํ–‰ diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js index 292f1e40..35ff6c22 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js +++ b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js @@ -1040,6 +1040,54 @@ const VideoPlayerBase = class extends React.Component { this.announceJob.stop(); this.renderBottomControl.stop(); this.slider5WayPressJob.stop(); + // ํ”Œ๋ ˆ์ด์–ด ์ž์› ์ •๋ฆฌ: ์–ธ๋งˆ์šดํŠธ ์‹œ ๋””์ฝ”๋”/๋ฒ„ํผ ํ•ด์ œ + try { + // 1. ๋จผ์ € ์ค‘์ง€ (๊ฐ€์žฅ ์ค‘์š”) + if (this.video?.stopVideo) { + this.video.stopVideo(); + } else if (this.video?.pause) { + this.video.pause(); + } + + // 2. ์‹œ๊ฐ„ ์ดˆ๊ธฐํ™” + if (this.video?.seekTo) { + this.video.seekTo(0); + } else if (this.video) { + this.video.currentTime = 0; + } + + // 3. ๋ฆฌ์†Œ์Šค ํ•ด์ œ + if (this.video) { + this.video.src = ''; + if ('srcObject' in this.video) { + this.video.srcObject = null; + } + } + + // 4. ๋‚ด๋ถ€ ์ธ์Šคํ„ด์Šค ์ •๋ฆฌ (๋งˆ์ง€๋ง‰) + if (typeof this.video?.getInternalPlayer === 'function') { + try { + const hls = this.video.getInternalPlayer('hls'); + if (hls && typeof hls.destroy === 'function') { + hls.destroy(); + } + } catch (hlsErr) { + // HLS ์ •๋ฆฌ ์‹คํŒจ๋Š” ๋ฌด์‹œ + } + + try { + const yt = this.video.getInternalPlayer(); + if (yt?.stopVideo) { + yt.stopVideo(); + } + } catch (ytErr) { + // YouTube ์ •๋ฆฌ ์‹คํŒจ๋Š” ๋ฌด์‹œ + } + } + } catch (err) { + // ์ •๋ฆฌ ์ค‘ ์—๋Ÿฌ๋Š” ๋ฌด์‹œํ•˜๊ณ  ์–ธ๋งˆ์šดํŠธ ์ง„ํ–‰ + // console.warn('[VideoPlayer] cleanup error', err); + } if (this.floatingLayerController) { this.floatingLayerController.unregister(); } diff --git a/com.twin.app.shoptime/src/reducers/playReducer.js b/com.twin.app.shoptime/src/reducers/playReducer.js index a94fc7f3..78389d86 100644 --- a/com.twin.app.shoptime/src/reducers/playReducer.js +++ b/com.twin.app.shoptime/src/reducers/playReducer.js @@ -90,6 +90,13 @@ export const playReducer = (state = initialState, action) => { }; } case types.CLEAR_PLAYER_INFO: { + // ๊ธฐ์กด Blob URL๋“ค์„ ๋ชจ๋‘ ํ•ด์ œ + Object.values(state.subTitleBlobs).forEach(blobUrl => { + if (blobUrl && blobUrl.startsWith('blob:')) { + URL.revokeObjectURL(blobUrl); + } + }); + return { ...state, chatData: {}, diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx index c44e9fea..2676751f 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx @@ -1896,9 +1896,17 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // ์ž๋ง‰ Blob URL ์ˆ˜๋ช… ๊ด€๋ฆฌ: ์ด์ „ Blob์„ ํ•ด์ œํ•ด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€ useEffect(() => { const prevBlobUrl = previousSubtitleBlobRef.current; - if (prevBlobUrl && prevBlobUrl !== currentSubtitleBlob && typeof prevBlobUrl === 'string') { - if (prevBlobUrl.startsWith('blob:')) { + + // ๋” ์—„๊ฒฉํ•œ ํƒ€์ž…๊ณผ ํ˜•์‹ ์ฒดํฌ + if (prevBlobUrl && + typeof prevBlobUrl === 'string' && + prevBlobUrl.startsWith('blob:') && + prevBlobUrl !== currentSubtitleBlob) { + try { URL.revokeObjectURL(prevBlobUrl); + dlog('[PlayerPanel] Previous subtitle Blob URL revoked:', prevBlobUrl); + } catch (error) { + derror('[PlayerPanel] Failed to revoke Blob URL:', error); } } @@ -1907,7 +1915,12 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props return () => { const lastBlobUrl = previousSubtitleBlobRef.current; if (lastBlobUrl && typeof lastBlobUrl === 'string' && lastBlobUrl.startsWith('blob:')) { - URL.revokeObjectURL(lastBlobUrl); + try { + URL.revokeObjectURL(lastBlobUrl); + dlog('[PlayerPanel] Final subtitle Blob URL revoked:', lastBlobUrl); + } catch (error) { + derror('[PlayerPanel] Failed to revoke final Blob URL:', error); + } } previousSubtitleBlobRef.current = null; };