[251124] fix: PlayerPanel,VideoPlayer 최적화-2
🕐 커밋 시간: 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 컴포넌트 아키텍처 개선 • 핵심 비즈니스 로직 개선 • 소규모 기능 개선
This commit is contained in:
@@ -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 변경 시에도 정리 로직 실행
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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: {},
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user