From 372334fdfc7a1e822039cdbb9af0c8582636fdcd Mon Sep 17 00:00:00 2001 From: optrader Date: Mon, 24 Nov 2025 17:35:58 +0900 Subject: [PATCH] =?UTF-8?q?[251124]=20fix:=20PlayerPanel,VideoPlayer=20?= =?UTF-8?q?=EC=B5=9C=EC=A0=81=ED=99=94-2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 24. 17:35:58 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 4๊ฐœ โ€ข ์ถ”๊ฐ€: +84์ค„ โ€ข ์‚ญ์ œ: -8์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js ~ com.twin.app.shoptime/src/reducers/playReducer.js ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข UI ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„  โ€ข ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ์„  โ€ข ์†Œ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  --- .../components/VideoPlayer/TReactPlayer.jsx | 18 +++++-- .../src/components/VideoPlayer/VideoPlayer.js | 48 +++++++++++++++++++ .../src/reducers/playReducer.js | 7 +++ .../src/views/PlayerPanel/PlayerPanel.jsx | 19 ++++++-- 4 files changed, 84 insertions(+), 8 deletions(-) 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; };