[251217] fix: VOD 경과시간 표시
🕐 커밋 시간: 2025. 12. 17. 16:09:01 📊 변경 통계: • 총 파일: 2개 • 추가: +18줄 • 삭제: -205줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js 🔧 주요 변경 내용: • UI 컴포넌트 아키텍처 개선 • 코드 정리 및 최적화 Performance: 코드 최적화로 성능 개선 기대
This commit is contained in:
@@ -840,9 +840,11 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.state.mediaSliderVisible === nextState.mediaSliderVisible &&
|
this.state.mediaSliderVisible === nextState.mediaSliderVisible &&
|
||||||
this.state.loading === nextState.loading &&
|
this.state.loading === nextState.loading &&
|
||||||
this.props.loading === nextProps.loading &&
|
this.props.loading === nextProps.loading &&
|
||||||
(this.state.currentTime !== nextState.currentTime ||
|
this.state.currentTime === nextState.currentTime &&
|
||||||
this.state.proportionPlayed !== nextState.proportionPlayed ||
|
this.state.proportionPlayed === nextState.proportionPlayed &&
|
||||||
this.state.sliderTooltipTime !== nextState.sliderTooltipTime)
|
this.state.sliderTooltipTime === nextState.sliderTooltipTime &&
|
||||||
|
this.state.mediaControlsVisible === nextState.mediaControlsVisible &&
|
||||||
|
this.state.bottomControlsRendered === nextState.bottomControlsRendered
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1279,14 +1281,11 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
sourceUnavailable: true,
|
sourceUnavailable: true,
|
||||||
proportionPlayed: 0,
|
proportionPlayed: 0,
|
||||||
proportionLoaded: 0,
|
proportionLoaded: 0,
|
||||||
|
bottomControlsRendered: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.props.noAutoShowMediaControls) {
|
if (!this.props.noAutoShowMediaControls) {
|
||||||
if (!this.state.bottomControlsRendered) {
|
this.showControls();
|
||||||
this.renderBottomControl.idle();
|
|
||||||
} else {
|
|
||||||
this.showControls();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -855,9 +855,11 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.state.mediaSliderVisible === nextState.mediaSliderVisible &&
|
this.state.mediaSliderVisible === nextState.mediaSliderVisible &&
|
||||||
this.state.loading === nextState.loading &&
|
this.state.loading === nextState.loading &&
|
||||||
this.props.loading === nextProps.loading &&
|
this.props.loading === nextProps.loading &&
|
||||||
(this.state.currentTime !== nextState.currentTime ||
|
this.state.currentTime === nextState.currentTime &&
|
||||||
this.state.proportionPlayed !== nextState.proportionPlayed ||
|
this.state.proportionPlayed === nextState.proportionPlayed &&
|
||||||
this.state.sliderTooltipTime !== nextState.sliderTooltipTime)
|
this.state.sliderTooltipTime === nextState.sliderTooltipTime &&
|
||||||
|
this.state.mediaControlsVisible === nextState.mediaControlsVisible &&
|
||||||
|
this.state.bottomControlsRendered === nextState.bottomControlsRendered
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1423,14 +1425,11 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
sourceUnavailable: true,
|
sourceUnavailable: true,
|
||||||
proportionPlayed: 0,
|
proportionPlayed: 0,
|
||||||
proportionLoaded: 0,
|
proportionLoaded: 0,
|
||||||
|
bottomControlsRendered: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.props.noAutoShowMediaControls) {
|
if (!this.props.noAutoShowMediaControls) {
|
||||||
if (!this.state.bottomControlsRendered) {
|
this.showControls();
|
||||||
this.renderBottomControl.idle();
|
|
||||||
} else {
|
|
||||||
this.showControls();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1636,103 +1635,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
updatedState.thumbnailUrl = null;
|
updatedState.thumbnailUrl = null;
|
||||||
}
|
}
|
||||||
this.setState(updatedState);
|
this.setState(updatedState);
|
||||||
|
|
||||||
// Redux에 비디오 재생 상태 업데이트 (기존 로직 유지)
|
|
||||||
if (this.props.dispatch) {
|
|
||||||
// 🔥 onProgress 이벤트는 Redux 업데이트하지 않음 (빈번한 이벤트)
|
|
||||||
const shouldUpdateRedux = !['onProgress'].includes(ev.type);
|
|
||||||
|
|
||||||
if (shouldUpdateRedux) {
|
|
||||||
const updateState = {
|
|
||||||
isPlaying: !updatedState.paused,
|
|
||||||
isPaused: updatedState.paused,
|
|
||||||
currentTime: updatedState.currentTime,
|
|
||||||
duration: updatedState.duration,
|
|
||||||
playbackRate: updatedState.playbackRate,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 가장 중요한 이벤트만 로그
|
|
||||||
const shouldLogEvent = ['play', 'pause', 'ended'].includes(ev.type);
|
|
||||||
if (shouldLogEvent) {
|
|
||||||
dlog('🔄 [PlayerPanel][VideoPlayer] Event-driven Redux update', {
|
|
||||||
eventType: ev.type,
|
|
||||||
videoState: updatedState,
|
|
||||||
updateState,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔍 Redux dispatch 확인
|
|
||||||
dlog('📤 [PlayerPanel][VideoPlayer] Dispatching Redux update', {
|
|
||||||
eventType: ev.type,
|
|
||||||
updateState,
|
|
||||||
hasDispatch: !!this.props.dispatch,
|
|
||||||
propsVideoPlayState: this.props.videoPlayState,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.dispatch(updateVideoPlayState(updateState));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
derror('❌ [PlayerPanel][VideoPlayer] No dispatch prop available', {
|
|
||||||
props: Object.keys(this.props),
|
|
||||||
hasDispatch: !!this.props.dispatch,
|
|
||||||
hasVideoPlayState: !!this.props.videoPlayState,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔹 [강화] 내부 상태와 Redux 상태 동기화
|
|
||||||
// Redux 상태를 우선적으로 사용하여 내부 상태 일관성 확보
|
|
||||||
if (this.props.videoPlayState && typeof this.props.videoPlayState === 'object') {
|
|
||||||
// Redux 상태 디버깅 (최소한의 중요 이벤트만)
|
|
||||||
if (ev.type === 'play' || ev.type === 'pause') {
|
|
||||||
dlog('🔍 [PlayerPanel][VideoPlayer] Redux state debug', {
|
|
||||||
videoPlayState: this.props.videoPlayState,
|
|
||||||
isPaused: this.props.videoPlayState?.isPaused,
|
|
||||||
isPlaying: this.props.videoPlayState?.isPlaying,
|
|
||||||
currentTime: this.props.videoPlayState?.currentTime,
|
|
||||||
eventType: ev.type,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const { currentTime, paused, playbackRate } = this.props.videoPlayState;
|
|
||||||
|
|
||||||
// Redux 상태와 현재 내부 상태가 크게 다를 경우 내부 상태 업데이트
|
|
||||||
const timeDiff = Math.abs(currentTime - this.state.currentTime);
|
|
||||||
const shouldUpdateTime = timeDiff > 0.5; // 0.5초 이상 차이 시 업데이트
|
|
||||||
|
|
||||||
// 빈번한 이벤트는 로그에서 제외
|
|
||||||
const isFrequentEvent = [
|
|
||||||
'onProgress',
|
|
||||||
'onBuffer',
|
|
||||||
'onBufferEnd',
|
|
||||||
'onReady',
|
|
||||||
'onDuration',
|
|
||||||
'onStart',
|
|
||||||
].includes(ev.type);
|
|
||||||
const hasSignificantChange =
|
|
||||||
shouldUpdateTime || (paused !== this.state.paused && !isFrequentEvent);
|
|
||||||
|
|
||||||
// 중요한 상태 변화가 있고 빈번한 이벤트가 아닐 때만 로그
|
|
||||||
if (hasSignificantChange && !isFrequentEvent) {
|
|
||||||
dlog('🔄 [PlayerPanel][VideoPlayer] Syncing internal state with Redux', {
|
|
||||||
timeDiff,
|
|
||||||
shouldUpdateTime,
|
|
||||||
pausedDiff: paused !== this.state.paused,
|
|
||||||
reduxPaused: paused,
|
|
||||||
internalPaused: this.state.paused,
|
|
||||||
eventType: ev.type,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasSignificantChange) {
|
|
||||||
this.setState({
|
|
||||||
currentTime: shouldUpdateTime ? currentTime : this.state.currentTime,
|
|
||||||
paused: paused !== undefined ? paused : this.state.paused,
|
|
||||||
playbackRate: playbackRate !== undefined ? playbackRate : this.state.playbackRate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
renderBottomControl = new Job(() => {
|
renderBottomControl = new Job(() => {
|
||||||
@@ -1744,7 +1646,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
/**
|
/**
|
||||||
* Returns an object with the current state of the media including `currentTime`, `duration`,
|
* Returns an object with the current state of the media including `currentTime`, `duration`,
|
||||||
* `paused`, `playbackRate`, `proportionLoaded`, and `proportionPlayed`.
|
* `paused`, `playbackRate`, `proportionLoaded`, and `proportionPlayed`.
|
||||||
* Redux 상태와 내부 상태를 우선적으로 사용하여 일관성 보장
|
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
* @memberof sandstone/VideoPlayer.VideoPlayerBase.prototype
|
* @memberof sandstone/VideoPlayer.VideoPlayerBase.prototype
|
||||||
@@ -1752,19 +1653,13 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getMediaState = () => {
|
getMediaState = () => {
|
||||||
// Redux 상태를 우선적으로 사용하여 일관성 보장
|
|
||||||
// Redux 상태가 없으면 내부 상태 사용 (fallback)
|
|
||||||
const reduxState = this.props.videoPlayState;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentTime: reduxState?.currentTime ?? this.state.currentTime,
|
currentTime: this.state.currentTime,
|
||||||
duration: reduxState?.duration ?? this.state.duration,
|
duration: this.state.duration,
|
||||||
paused: reduxState?.isPaused ?? this.state.paused,
|
paused: this.state.paused,
|
||||||
playbackRate: reduxState?.playbackRate ?? this.video?.playbackRate ?? this.state.playbackRate,
|
playbackRate: this.video?.playbackRate,
|
||||||
proportionLoaded: this.state.proportionLoaded,
|
proportionLoaded: this.state.proportionLoaded,
|
||||||
proportionPlayed: this.state.proportionPlayed,
|
proportionPlayed: this.state.proportionPlayed,
|
||||||
// Redux 상태 정보도 포함
|
|
||||||
isPlaying: reduxState?.isPlaying ?? !this.state.paused,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1793,16 +1688,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
play = () => {
|
play = () => {
|
||||||
dlog('🟢 [PlayerPanel][VideoPlayer] play() called', {
|
|
||||||
currentTime: this.state.currentTime,
|
|
||||||
duration: this.state.duration,
|
|
||||||
paused: this.state.paused,
|
|
||||||
sourceUnavailable: this.state.sourceUnavailable,
|
|
||||||
prevCommand: this.prevCommand,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.state.sourceUnavailable) {
|
if (this.state.sourceUnavailable) {
|
||||||
dwarn('⚠️ [PlayerPanel][VideoPlayer] play() aborted - source unavailable');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1814,19 +1700,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.send('play');
|
this.send('play');
|
||||||
this.announce($L('Play'));
|
this.announce($L('Play'));
|
||||||
this.startDelayedMiniFeedbackHide(5000);
|
this.startDelayedMiniFeedbackHide(5000);
|
||||||
|
|
||||||
// Redux 상태 업데이트 - 재생 상태로 변경
|
|
||||||
if (this.props.dispatch) {
|
|
||||||
this.props.dispatch(
|
|
||||||
updateVideoPlayState({
|
|
||||||
isPlaying: true,
|
|
||||||
isPaused: false,
|
|
||||||
currentTime: this.state.currentTime,
|
|
||||||
duration: this.state.duration,
|
|
||||||
playbackRate: 1,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1837,16 +1710,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
pause = () => {
|
pause = () => {
|
||||||
dlog('🔴 [VideoPlayer] pause() called', {
|
|
||||||
currentTime: this.state.currentTime,
|
|
||||||
duration: this.state.duration,
|
|
||||||
paused: this.state.paused,
|
|
||||||
sourceUnavailable: this.state.sourceUnavailable,
|
|
||||||
prevCommand: this.prevCommand,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.state.sourceUnavailable) {
|
if (this.state.sourceUnavailable) {
|
||||||
dwarn('⚠️ [VideoPlayer] pause() aborted - source unavailable');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1858,22 +1722,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.send('pause');
|
this.send('pause');
|
||||||
this.announce($L('Pause'));
|
this.announce($L('Pause'));
|
||||||
this.stopDelayedMiniFeedbackHide();
|
this.stopDelayedMiniFeedbackHide();
|
||||||
|
|
||||||
// Redux 상태 업데이트 - 일시정지 상태로 변경
|
|
||||||
if (this.props.dispatch) {
|
|
||||||
const pauseState = {
|
|
||||||
isPlaying: false,
|
|
||||||
isPaused: true,
|
|
||||||
currentTime: this.state.currentTime,
|
|
||||||
duration: this.state.duration,
|
|
||||||
playbackRate: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
dlog('📤 [VideoPlayer] Dispatching pause state', pauseState);
|
|
||||||
this.props.dispatch(updateVideoPlayState(pauseState));
|
|
||||||
} else {
|
|
||||||
dwarn('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1885,15 +1733,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
seek = (timeIndex) => {
|
seek = (timeIndex) => {
|
||||||
dlog('⏩ [VideoPlayer] seek() called', {
|
|
||||||
timeIndex,
|
|
||||||
currentTime: this.state.currentTime,
|
|
||||||
duration: this.state.duration,
|
|
||||||
videoDuration: this.video?.duration,
|
|
||||||
seekDisabled: this.props.seekDisabled,
|
|
||||||
sourceUnavailable: this.state.sourceUnavailable,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.video) {
|
if (this.video) {
|
||||||
if (
|
if (
|
||||||
!this.props.seekDisabled &&
|
!this.props.seekDisabled &&
|
||||||
@@ -1904,34 +1743,9 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
const actualSeekTime =
|
const actualSeekTime =
|
||||||
timeIndex >= this.video.duration ? this.video.duration - 1 : timeIndex;
|
timeIndex >= this.video.duration ? this.video.duration - 1 : timeIndex;
|
||||||
this.video.currentTime = actualSeekTime;
|
this.video.currentTime = actualSeekTime;
|
||||||
|
|
||||||
dlog('⏩ [VideoPlayer] Video seek completed', {
|
|
||||||
requestedTime: timeIndex,
|
|
||||||
actualTime: actualSeekTime,
|
|
||||||
videoDuration: this.video.duration,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Redux 상태 업데이트 - 시간 이동 상태 반영
|
|
||||||
if (this.props.dispatch) {
|
|
||||||
const seekState = {
|
|
||||||
isPlaying: !this.state.paused,
|
|
||||||
isPaused: this.state.paused,
|
|
||||||
currentTime: actualSeekTime,
|
|
||||||
duration: this.state.duration,
|
|
||||||
playbackRate: this.state.playbackRate,
|
|
||||||
};
|
|
||||||
|
|
||||||
dlog('📤 [VideoPlayer] Dispatching seek state', seekState);
|
|
||||||
this.props.dispatch(updateVideoPlayState(seekState));
|
|
||||||
} else {
|
|
||||||
dwarn('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
derror('❌ [VideoPlayer] seek failed - disabled or source unavailable');
|
|
||||||
forward('onSeekFailed', {}, this.props);
|
forward('onSeekFailed', {}, this.props);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
derror('❌ [VideoPlayer] seek failed - no video element');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user