[251124] fix: PlayerPanel,VideoPlayer 최적화-3
🕐 커밋 시간: 2025. 11. 24. 17:55:07 📊 변경 통계: • 총 파일: 5개 • 추가: +66줄 • 삭제: -1줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/actionTypes.js ~ com.twin.app.shoptime/src/actions/playActions.js ~ com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx ~ com.twin.app.shoptime/src/reducers/playReducer.js ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx 🔧 주요 변경 내용: • 타입 시스템 안정성 강화 • 핵심 비즈니스 로직 개선 • UI 컴포넌트 아키텍처 개선 • 소규모 기능 개선
This commit is contained in:
@@ -257,6 +257,7 @@ export const types = {
|
||||
GET_CHAT_LOG: 'GET_CHAT_LOG',
|
||||
GET_SUBTITLE: 'GET_SUBTITLE',
|
||||
CLEAR_PLAYER_INFO: 'CLEAR_PLAYER_INFO',
|
||||
CLEAR_SUBTITLE_BLOB: 'CLEAR_SUBTITLE_BLOB',
|
||||
UPDATE_VIDEO_PLAY_STATE: 'UPDATE_VIDEO_PLAY_STATE',
|
||||
|
||||
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 재생 상태
|
||||
|
||||
@@ -786,6 +786,28 @@ export const CLEAR_PLAYER_INFO = () => ({
|
||||
type: types.CLEAR_PLAYER_INFO,
|
||||
});
|
||||
|
||||
// 특정 자막 Blob URL을 해제하는 액션 생성자
|
||||
export const clearSubtitleBlob = (subtitleUrl) => (dispatch, getState) => {
|
||||
const currentBlobs = getState().play.subTitleBlobs;
|
||||
const blobUrl = currentBlobs[subtitleUrl];
|
||||
|
||||
// Blob URL 해제
|
||||
if (blobUrl && blobUrl.startsWith('blob:')) {
|
||||
try {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
dlog('[clearSubtitleBlob] Revoked Blob URL:', subtitleUrl);
|
||||
} catch (error) {
|
||||
derror('[clearSubtitleBlob] Failed to revoke Blob URL:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Redux 상태에서 제거
|
||||
dispatch({
|
||||
type: types.CLEAR_SUBTITLE_BLOB,
|
||||
payload: { subtitleUrl }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 비디오 재생 상태를 Redux에 업데이트합니다.
|
||||
* @param {Object} playState - 업데이트할 재생 상태
|
||||
|
||||
@@ -169,6 +169,11 @@ export default function TReactPlayer({
|
||||
if (typeof videoNode.stopVideo === 'function' && videoNode.stopVideo !== videoNode.pause) {
|
||||
videoNode.stopVideo();
|
||||
}
|
||||
// HLS 인스턴스가 존재하면 명시적으로 파괴
|
||||
const hls = playerRef.current?.getInternalPlayer?.('hls');
|
||||
if (hls && typeof hls.destroy === 'function') {
|
||||
hls.destroy();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[TReactPlayer] cleanup warning:', err);
|
||||
}
|
||||
|
||||
@@ -103,6 +103,18 @@ export const playReducer = (state = initialState, action) => {
|
||||
subTitleBlobs: {},
|
||||
};
|
||||
}
|
||||
case types.CLEAR_SUBTITLE_BLOB: {
|
||||
const { subtitleUrl } = action.payload;
|
||||
const newSubTitleBlobs = { ...state.subTitleBlobs };
|
||||
|
||||
// 특정 URL만 제거
|
||||
delete newSubTitleBlobs[subtitleUrl];
|
||||
|
||||
return {
|
||||
...state,
|
||||
subTitleBlobs: newSubTitleBlobs,
|
||||
};
|
||||
}
|
||||
case types.UPDATE_VIDEO_PLAY_STATE: {
|
||||
const newState = {
|
||||
...state.videoPlayState,
|
||||
|
||||
@@ -34,6 +34,7 @@ import * as PanelActions from '../../actions/panelActions';
|
||||
import { updatePanel } from '../../actions/panelActions';
|
||||
import {
|
||||
CLEAR_PLAYER_INFO,
|
||||
clearSubtitleBlob,
|
||||
getChatLog,
|
||||
getSubTitle,
|
||||
startVideoPlayer,
|
||||
@@ -261,6 +262,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
const liveShowInfos = USE_SELECTOR('liveShowInfos', (state) => state.main.liveShowInfos);
|
||||
const vodSubtitleData = USE_SELECTOR('vodSubtitleData', (state) => state.play.subTitleBlobs);
|
||||
const previousSubtitleBlobRef = useRef(null);
|
||||
const previousSubtitleUrlRef = useRef(null);
|
||||
const broadcast = USE_SELECTOR('broadcast', (state) => state.common.broadcast);
|
||||
const videoPlayState = USE_SELECTOR('videoPlayState', (state) => state.play.videoPlayState);
|
||||
|
||||
@@ -1493,6 +1495,13 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
}
|
||||
}, [panelInfo?.shptmBanrTpNm, playListInfo]);
|
||||
|
||||
// 컴포넌트 언마운트 시 Job 정리
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
initialFocusTimeoutJob.current?.stop?.();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// live subtitle Luna API
|
||||
useEffect(() => {
|
||||
if (currentSubtitleBlob) {
|
||||
@@ -1953,7 +1962,23 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
if (currentSubtitleUrl) {
|
||||
dispatch(getSubTitle({ showSubtitleUrl: currentSubtitleUrl }));
|
||||
}
|
||||
}, [currentSubtitleUrl]);
|
||||
|
||||
// 이전 자막 URL 정리 (Redux 메모리 누수 방지)
|
||||
const prevSubtitleUrl = previousSubtitleUrlRef.current;
|
||||
if (prevSubtitleUrl && prevSubtitleUrl !== currentSubtitleUrl) {
|
||||
dispatch(clearSubtitleBlob(prevSubtitleUrl));
|
||||
dlog('[PlayerPanel] Clearing previous subtitle URL:', prevSubtitleUrl);
|
||||
}
|
||||
|
||||
previousSubtitleUrlRef.current = currentSubtitleUrl;
|
||||
|
||||
// 컴포넌트 언마운트 시 마지막 자막 URL 정리
|
||||
return () => {
|
||||
if (previousSubtitleUrlRef.current) {
|
||||
dispatch(clearSubtitleBlob(previousSubtitleUrlRef.current));
|
||||
}
|
||||
};
|
||||
}, [currentSubtitleUrl, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
setVideoLoaded(false);
|
||||
|
||||
Reference in New Issue
Block a user