[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_CHAT_LOG: 'GET_CHAT_LOG',
|
||||||
GET_SUBTITLE: 'GET_SUBTITLE',
|
GET_SUBTITLE: 'GET_SUBTITLE',
|
||||||
CLEAR_PLAYER_INFO: 'CLEAR_PLAYER_INFO',
|
CLEAR_PLAYER_INFO: 'CLEAR_PLAYER_INFO',
|
||||||
|
CLEAR_SUBTITLE_BLOB: 'CLEAR_SUBTITLE_BLOB',
|
||||||
UPDATE_VIDEO_PLAY_STATE: 'UPDATE_VIDEO_PLAY_STATE',
|
UPDATE_VIDEO_PLAY_STATE: 'UPDATE_VIDEO_PLAY_STATE',
|
||||||
|
|
||||||
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 재생 상태
|
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 재생 상태
|
||||||
|
|||||||
@@ -786,6 +786,28 @@ export const CLEAR_PLAYER_INFO = () => ({
|
|||||||
type: types.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에 업데이트합니다.
|
* 비디오 재생 상태를 Redux에 업데이트합니다.
|
||||||
* @param {Object} playState - 업데이트할 재생 상태
|
* @param {Object} playState - 업데이트할 재생 상태
|
||||||
|
|||||||
@@ -169,6 +169,11 @@ export default function TReactPlayer({
|
|||||||
if (typeof videoNode.stopVideo === 'function' && videoNode.stopVideo !== videoNode.pause) {
|
if (typeof videoNode.stopVideo === 'function' && videoNode.stopVideo !== videoNode.pause) {
|
||||||
videoNode.stopVideo();
|
videoNode.stopVideo();
|
||||||
}
|
}
|
||||||
|
// HLS 인스턴스가 존재하면 명시적으로 파괴
|
||||||
|
const hls = playerRef.current?.getInternalPlayer?.('hls');
|
||||||
|
if (hls && typeof hls.destroy === 'function') {
|
||||||
|
hls.destroy();
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[TReactPlayer] cleanup warning:', err);
|
console.warn('[TReactPlayer] cleanup warning:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,18 @@ export const playReducer = (state = initialState, action) => {
|
|||||||
subTitleBlobs: {},
|
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: {
|
case types.UPDATE_VIDEO_PLAY_STATE: {
|
||||||
const newState = {
|
const newState = {
|
||||||
...state.videoPlayState,
|
...state.videoPlayState,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import * as PanelActions from '../../actions/panelActions';
|
|||||||
import { updatePanel } from '../../actions/panelActions';
|
import { updatePanel } from '../../actions/panelActions';
|
||||||
import {
|
import {
|
||||||
CLEAR_PLAYER_INFO,
|
CLEAR_PLAYER_INFO,
|
||||||
|
clearSubtitleBlob,
|
||||||
getChatLog,
|
getChatLog,
|
||||||
getSubTitle,
|
getSubTitle,
|
||||||
startVideoPlayer,
|
startVideoPlayer,
|
||||||
@@ -261,6 +262,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
const liveShowInfos = USE_SELECTOR('liveShowInfos', (state) => state.main.liveShowInfos);
|
const liveShowInfos = USE_SELECTOR('liveShowInfos', (state) => state.main.liveShowInfos);
|
||||||
const vodSubtitleData = USE_SELECTOR('vodSubtitleData', (state) => state.play.subTitleBlobs);
|
const vodSubtitleData = USE_SELECTOR('vodSubtitleData', (state) => state.play.subTitleBlobs);
|
||||||
const previousSubtitleBlobRef = useRef(null);
|
const previousSubtitleBlobRef = useRef(null);
|
||||||
|
const previousSubtitleUrlRef = useRef(null);
|
||||||
const broadcast = USE_SELECTOR('broadcast', (state) => state.common.broadcast);
|
const broadcast = USE_SELECTOR('broadcast', (state) => state.common.broadcast);
|
||||||
const videoPlayState = USE_SELECTOR('videoPlayState', (state) => state.play.videoPlayState);
|
const videoPlayState = USE_SELECTOR('videoPlayState', (state) => state.play.videoPlayState);
|
||||||
|
|
||||||
@@ -1493,6 +1495,13 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
}
|
}
|
||||||
}, [panelInfo?.shptmBanrTpNm, playListInfo]);
|
}, [panelInfo?.shptmBanrTpNm, playListInfo]);
|
||||||
|
|
||||||
|
// 컴포넌트 언마운트 시 Job 정리
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
initialFocusTimeoutJob.current?.stop?.();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// live subtitle Luna API
|
// live subtitle Luna API
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentSubtitleBlob) {
|
if (currentSubtitleBlob) {
|
||||||
@@ -1953,7 +1962,23 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
if (currentSubtitleUrl) {
|
if (currentSubtitleUrl) {
|
||||||
dispatch(getSubTitle({ showSubtitleUrl: 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(() => {
|
useEffect(() => {
|
||||||
setVideoLoaded(false);
|
setVideoLoaded(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user