[PlayerPanel] 비디오 관련 Js 파일 커스텀 , 큐레이션 아이디 추가 , live API 추가

This commit is contained in:
고동영
2024-04-05 10:17:17 +09:00
parent b762bf7492
commit afa7635dbc
25 changed files with 568 additions and 391 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

View File

@@ -67,6 +67,7 @@ export const types = {
SET_MAIN_LIKE_CATEGORY: "SET_MAIN_LIKE_CATEGORY",
SET_MAIN_LIVE_UPCOMING_ALARM: "SET_MAIN_LIVE_UPCOMING_ALARM",
GET_MAIN_LIVE_SHOW_NOW_PRODUCT: "GET_MAIN_LIVE_SHOW_NOW_PRODUCT",
GET_MAIN_LIVE_SHOW: "GET_MAIN_LIVE_SHOW",
GET_MAIN_CATEGORY_SHOW_DETAIL: "GET_MAIN_CATEGORY_SHOW_DETAIL",
// myPage actions

View File

@@ -6,6 +6,31 @@ import * as HelperMethods from '../utils/helperMethods';
import { types } from './actionTypes';
import { fetchingController } from './commonActions';
export const getMainLiveShow = (props) => (dispatch, getState) => {
const onSuccess = (response) => {
console.log("@@ getMainLiveShow onSuccess", response.data);
dispatch({
type: types.GET_MAIN_LIVE_SHOW,
payload: response.data.data,
});
};
const onFail = (error) => {
console.error("@@ getMainLiveShow onFail", error);
};
TAxios(
dispatch,
getState,
"get",
URLS.GET_MAIN_LIVE_SHOW,
{},
{},
onSuccess,
onFail
);
};
// Live 알람 설정/해제 IF-LGSP-012
export const setMainLiveUpcomingAlarm = (props) => (dispatch, getState) => {
const { alamDispFlag, endDt, patnrId, showId, strtDt } = props;
@@ -60,7 +85,7 @@ export const getMainCategoryDetail = (props) => (dispatch, getState) => {
// 영상 상세 보기 조회 LF-LGSP-047
export const getMainCategoryShowDetail = (props) => (dispatch, getState) => {
const { patnrId, showId } = props;
const { patnrId, showId, curationId } = props;
const onSuccess = (response) => {
console.log("getMainCategoryShowDetail onSuccess ", response.data);
@@ -79,7 +104,7 @@ export const getMainCategoryShowDetail = (props) => (dispatch, getState) => {
getState,
"get",
URLS.GET_MAIN_CATEGORY_SHOW_DETAIL,
{ patnrId, showId },
{ patnrId, showId, curationId },
{},
onSuccess,
onFail

View File

@@ -33,9 +33,8 @@ export const getChatLog =
// VOD 자막 가져오기 IF-LGSP-072
export const getSubTitle =
({ patnrId, showId }) =>
({ showSubtitleUrl }) =>
(dispatch, getState) => {
console.log("#patnrId, showId ", patnrId, showId);
const onSuccess = (response) => {
console.log("getSubTitle onSuccess", response.data);
@@ -54,7 +53,7 @@ export const getSubTitle =
getState,
"get",
URLS.SUBTITLE,
{ patnrId, showId },
{ showSubtitleUrl },
{},
onSuccess,
onFail

View File

@@ -81,6 +81,7 @@ export const URLS = {
SET_MAIN_LIVE_UPCOMING_ALARM: "/lgsp/v1/main/live/upcoming/alarm.lge",
GET_MAIN_LIVE_SHOW_NOW_PRODUCT: "/lgsp/v1/main/live/show/nowproduct.lge",
GET_MAIN_CATEGORY_SHOW_DETAIL: "/lgsp/v1/main/category/show/detail.lge",
GET_MAIN_LIVE_SHOW: "/lgsp/v1/main/live/show.lge",
//event controller
GET_WELCOME_EVENT_INFO: "/lgsp/v1/event/event.lge",

View File

@@ -1,31 +1,38 @@
import React from "react";
import ReactDOM from "react-dom";
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
import onlyUpdateForKeys from "recompose/onlyUpdateForKeys";
import PropTypes from 'prop-types';
import onlyUpdateForKeys from 'recompose/onlyUpdateForKeys';
import { off, on } from "@enact/core/dispatcher";
import { forward } from "@enact/core/handle";
import hoc from "@enact/core/hoc";
import ApiDecorator from "@enact/core/internal/ApiDecorator";
import { is } from "@enact/core/keymap";
import kind from "@enact/core/kind";
import { Job } from "@enact/core/util";
import ActionGuide from "@enact/sandstone/ActionGuide";
import Button from "@enact/sandstone/Button";
import $L from "@enact/sandstone/internal/$L";
import { compareChildren } from "@enact/sandstone/internal/util";
import Spotlight from "@enact/spotlight";
import Pause from "@enact/spotlight/Pause";
import {
off,
on,
} from '@enact/core/dispatcher';
import { forward } from '@enact/core/handle';
import hoc from '@enact/core/hoc';
import ApiDecorator from '@enact/core/internal/ApiDecorator';
import { is } from '@enact/core/keymap';
import kind from '@enact/core/kind';
import { Job } from '@enact/core/util';
import ActionGuide from '@enact/sandstone/ActionGuide';
import Button from '@enact/sandstone/Button';
import $L from '@enact/sandstone/internal/$L';
import { compareChildren } from '@enact/sandstone/internal/util';
import Spotlight from '@enact/spotlight';
import Pause from '@enact/spotlight/Pause';
import {
SpotlightContainerDecorator,
spotlightDefaultClass,
} from "@enact/spotlight/SpotlightContainerDecorator";
import Cancelable from "@enact/ui/Cancelable";
import Slottable from "@enact/ui/Slottable";
} from '@enact/spotlight/SpotlightContainerDecorator';
import Cancelable from '@enact/ui/Cancelable';
import Slottable from '@enact/ui/Slottable';
import css from "./MediaControls.module.less";
import { countReactChildren } from "./util";
import ShopTimePlayIcon
from '../../../assets/images/btn/btn-video-play-nor@3x.png';
import ShopTimePauseIcon
from '../../../assets/images/btn/btn-voc-pause-nor@3x.png';
import css from './MediaControls.module.less';
import { countReactChildren } from './util';
const OuterContainer = SpotlightContainerDecorator(
{
@@ -353,7 +360,7 @@ const MediaControlsBase = kind({
backgroundOpacity="transparent"
css={css}
disabled={mediaDisabled || playPauseButtonDisabled}
icon={paused ? playIcon : pauseIcon}
icon={paused ? ShopTimePlayIcon : ShopTimePauseIcon}
onClick={onPlayButtonClick}
size="large"
spotlightDisabled={spotlightDisabled}

View File

@@ -3,67 +3,93 @@
@import "~@enact/sandstone/styles/skin.less";
.controlsFrame {
position: relative;
display: block;
position: relative;
display: block;
bottom: 142px;
left: 47px;
&.hidden {
will-change: opacity;
opacity: 0;
}
&.hidden {
will-change: opacity;
opacity: 0;
}
.mediaControls {
text-align: center;
direction: ltr;
white-space: nowrap;
.mediaControls {
> *:first-child {
margin-inline-start: 0;
}
}
> *:first-child {
margin-inline-start: 0;
}
}
.actionGuide {
padding-top: 39px;
transition: opacity @sand-mediaplayer-controls-actionguide-time linear;
&.hidden {
opacity: 0;
}
}
.actionGuide {
padding-top: 39px;
transition: opacity @sand-mediaplayer-controls-actionguide-time linear;
&.hidden {
opacity: 0;
}
}
.moreComponents {
position: absolute;
left: 0;
right: 0;
height: 0px;
opacity: 0;
.moreComponents {
position: absolute;
left: 0;
right: 0;
height: 0px;
opacity: 0;
.moreButtonsComponents {
> * {
margin: 0;
margin-inline-start: 114px;
.moreButtonsComponents {
> * {
margin: 0;
margin-inline-start: 114px;
&:first-child {
margin-inline-start: 0;
}
}
}
&:first-child {
margin-inline-start: 0;
}
}
}
> :first-child {
margin-top: 21px;
}
}
> :first-child {
margin-top: 21px;
}
}
.button {
height: 78px;
width: 78px;
margin: 0;
margin-inline-start: 45px;
padding: 0 !important;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
&:focus {
// border-radius:50%;
// > div:nth-child(1) {
// display: none;
.bg {
width: 78px;
height: 78px;
border-radius: 50%;
transform: none;
background-color: #c70850 !important;
opacity: 0.5;
filter: none !important;
-webkit-filter: none !important;
}
// }
}
.button {
height: 90px;
min-width: 90px;
margin: 0;
margin-inline-start: 45px;
.client {
padding: 0 9px;
}
.bg {
border-radius: 48px;
}
}
.client {
width: 60px;
height: 60px;
padding: 0;
display: inline-block;
vertical-align: top;
line-height: 0;
> div:nth-child(1) {
width: 60px;
height: 60px;
background-size: 60px 60px;
background-position: center center;
}
}
}
}

View File

@@ -9,32 +9,38 @@
@sand-video-player-padding-side: 42px;
.sliderFrame {
@knob-transform-active: @sand-translate-center scale(1);
@knob-transform-resting: @sand-translate-center scale(@sand-mediaplayer-slider-knob-resting-state-scale);
@slider-padding-v: ((@sand-mediaplayer-slider-tap-area - @sand-mediaplayer-slider-height) / 2);
@slider-padding-h: @sand-mediaplayer-slider-knob-size;
margin-left: 130px;
margin-right: 130px;
flex: 1 0 auto;
@knob-transform-active: @sand-translate-center scale(1);
@knob-transform-resting: @sand-translate-center
scale(@sand-mediaplayer-slider-knob-resting-state-scale);
@slider-padding-v: (
(@sand-mediaplayer-slider-tap-area - @sand-mediaplayer-slider-height) / 2
);
@slider-padding-h: @sand-mediaplayer-slider-knob-size;
margin-left: 130px;
margin-right: 130px;
flex: 1 0 auto;
width: 1540px;
height: 6px;
&.hidden {
will-change: opacity;
opacity: 0;
}
&.hidden {
will-change: opacity;
opacity: 0;
}
.mediaSlider {
margin: 0 @slider-padding-h;
padding: @slider-padding-v 0;
height: @sand-mediaplayer-slider-height;
// Add a tap area that extends to the edges of the screen, to make the slider more accessible
&::before {
content: "";
position: absolute;
.position(0, -(@sand-video-player-padding-side));
}
.mediaSlider {
margin: 0 @slider-padding-h;
padding: @slider-padding-v 0;
height: @sand-mediaplayer-slider-height;
right: 154px;
// Add a tap area that extends to the edges of the screen, to make the slider more accessible
&::before {
content: "";
position: absolute;
.position(0, -(@sand-video-player-padding-side));
}
// Grow the knob when the Slider gets spotted
.focus({
// Grow the knob when the Slider gets spotted
.focus({
&.active,
&.pressed {
.knob::before {
@@ -44,7 +50,7 @@
}
});
.spottable({
.spottable({
&.pressed {
.knob::before {
transform: @knob-transform-active;
@@ -52,50 +58,50 @@
}
}
});
}
}
// Knob
.knob {
@activate-transition-function: cubic-bezier(0.15, 0.85, 0.6, 1.65);
//@slide-transition-function: cubic-bezier(0.15, 0.85, 0.53, 1.09);
// Knob
.knob {
@activate-transition-function: cubic-bezier(0.15, 0.85, 0.6, 1.65);
//@slide-transition-function: cubic-bezier(0.15, 0.85, 0.53, 1.09);
//-webkit-transition: -webkit-transform @slide-transition-function 0.2s;
//transition: transform @slide-transition-function 0.2s;
//-webkit-transition: -webkit-transform @slide-transition-function 0.2s;
//transition: transform @slide-transition-function 0.2s;
&::before {
width: @sand-mediaplayer-slider-knob-size;
height: @sand-mediaplayer-slider-knob-size;
border-width: 0;
border-radius: @sand-mediaplayer-slider-knob-size;
transform: @knob-transform-resting;
opacity: 0;
will-change: transform, opacity;
-webkit-transition: -webkit-transform @activate-transition-function 0.2s, opacity ease 0.2s;
transition: transform @activate-transition-function 0.2s, opacity ease 0.2s;
}
}
&.scrubbing {
.knob {
display: block;
}
}
&::before {
width: @sand-mediaplayer-slider-knob-size;
height: @sand-mediaplayer-slider-knob-size;
border-width: 0;
border-radius: 50%;
transform: @knob-transform-resting;
// opacity: 0;
will-change: transform, opacity;
-webkit-transition: -webkit-transform @activate-transition-function 0.2s,
opacity ease 0.2s;
transition: transform @activate-transition-function 0.2s,
opacity ease 0.2s;
}
}
}
.applySkins({
.sliderFrame {
.slider {
.bar {
background-color: @sand-mediaslider-bar-bg-color;
background-color: #fff;
}
.fill {
background-color: @sand-mediaslider-fill-bg-color;
background-color: #c70850;
}
.knob {
&::before {
background-color: @sand-mediaplayer-slider-knob-color;
width: 24px;
height: 24px;
background-color: #fff;
}
}

View File

@@ -1,10 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import onlyUpdateForKeys from 'recompose/onlyUpdateForKeys';
import kind from '@enact/core/kind';
import React from 'react';
import {secondsToPeriod, secondsToTime} from './util';
import css from './Times.module.less';
import {
secondsToPeriod,
secondsToTime,
} from './util';
/**
* Sandstone-styled formatted time component.
@@ -15,98 +20,108 @@ import css from './Times.module.less';
* @public
*/
const TimesBase = kind({
name: 'Times',
name: "Times",
propTypes: /** @lends sandstone/MediaPlayer.Times.prototype */ {
/**
* An instance of a Duration Formatter from i18n.
*
* Must has a `format()` method that returns a string.
*
* @type {Object}
* @required
* @public
*/
formatter: PropTypes.object.isRequired,
propTypes: /** @lends sandstone/MediaPlayer.Times.prototype */ {
/**
* An instance of a Duration Formatter from i18n.
*
* Must has a `format()` method that returns a string.
*
* @type {Object}
* @required
* @public
*/
formatter: PropTypes.object.isRequired,
/**
* The current time in seconds of the video source.
*
* @type {Number}
* @default 0
* @public
*/
current: PropTypes.number,
/**
* The current time in seconds of the video source.
*
* @type {Number}
* @default 0
* @public
*/
current: PropTypes.number,
/**
* Removes the current time.
*
* @type {Boolean}
* @public
*/
noCurrentTime: PropTypes.bool,
/**
* Removes the current time.
*
* @type {Boolean}
* @public
*/
noCurrentTime: PropTypes.bool,
/**
* Removes the total time.
*
* @type {Boolean}
* @public
*/
noTotalTime: PropTypes.bool,
/**
* Removes the total time.
*
* @type {Boolean}
* @public
*/
noTotalTime: PropTypes.bool,
/**
* The total time (duration) in seconds of the loaded video source.
*
* @type {Number}
* @default 0
* @public
*/
total: PropTypes.number
},
/**
* The total time (duration) in seconds of the loaded video source.
*
* @type {Number}
* @default 0
* @public
*/
total: PropTypes.number,
},
defaultProps: {
current: 0,
total: 0
},
defaultProps: {
current: 0,
total: 0,
},
styles: {
css,
className: 'times'
},
styles: {
css,
className: "times",
},
computed: {
currentPeriod: ({current}) => secondsToPeriod(current),
currentReadable: ({current, formatter}) => secondsToTime(current, formatter),
noSeparator: ({noCurrentTime, noTotalTime}) => noCurrentTime || noTotalTime,
totalPeriod: ({total}) => secondsToPeriod(total),
totalReadable: ({total, formatter}) => secondsToTime(total, formatter)
},
computed: {
currentPeriod: ({ current }) => secondsToPeriod(current),
currentReadable: ({ current, formatter }) =>
secondsToTime(current, formatter),
noSeparator: ({ noCurrentTime, noTotalTime }) =>
noCurrentTime || noTotalTime,
totalPeriod: ({ total }) => secondsToPeriod(total),
totalReadable: ({ total, formatter }) => secondsToTime(total, formatter),
},
render: ({currentPeriod, currentReadable, noCurrentTime, noSeparator, noTotalTime, totalPeriod, totalReadable, ...rest}) => {
delete rest.current;
delete rest.formatter;
delete rest.total;
render: ({
currentPeriod,
currentReadable,
noCurrentTime,
noSeparator,
noTotalTime,
totalPeriod,
totalReadable,
...rest
}) => {
delete rest.current;
delete rest.formatter;
delete rest.total;
return (
<div {...rest}>
{noCurrentTime ?
null :
<time className={css.currentTime} dateTime={currentPeriod}>{currentReadable}</time>
}
{noSeparator ?
null :
<span className={css.separator}>/</span>
}
{noTotalTime ?
null :
<time className={css.totalTime} dateTime={totalPeriod}>{totalReadable}</time>
}
</div>
);
}
return (
<div {...rest}>
{noCurrentTime ? null : (
<time className={css.currentTime} dateTime={currentPeriod}>
{currentReadable} <span className={css.separator}>/</span>
</time>
)}
{noTotalTime ? null : (
<time className={css.totalTime} dateTime={totalPeriod}>
{totalReadable}
</time>
)}
</div>
);
},
});
const Times = onlyUpdateForKeys(['current', 'formatter', 'total'])(TimesBase);
const Times = onlyUpdateForKeys(["current", "formatter", "total"])(TimesBase);
export default Times;
export {Times, TimesBase};
export { Times, TimesBase };

View File

@@ -4,25 +4,32 @@
@import "~@enact/sandstone/styles/mixins.less";
.times {
position: absolute;
font-family: "LG SmartFont SemiBold";
width: 100%;
top: 19px;
font-size: 30px;
font-weight: bold;
letter-spacing: -1.4px;
line-height: 30px;
white-space: nowrap;
position: absolute;
font-family: "LGSmartUI";
width: 100%;
top: 22px;
font-size: 24px;
font-weight: bold;
line-height: 30px;
white-space: nowrap;
text-align: right;
letter-spacing: 1.2px;
> * {
display: inline-block;
}
.currentTime {
// width: 225px;
padding-right: 140px;
}
> * {
display: inline-block;
}
.separator {
padding: 0 1ex;
}
.separator {
position: absolute;
top: 0;
right: 116px;
}
.enact-locale-rtl({
.enact-locale-rtl({
direction: ltr;
});
}

View File

@@ -1,4 +1,5 @@
import React from 'react';
// MediaPlayer utils.js
//
@@ -12,15 +13,15 @@ import React from 'react';
* @private
*/
const parseTime = (value) => {
value = parseFloat(value);
const time = {};
const hour = Math.floor(value / (60 * 60));
time.minute = Math.floor((value / 60) % 60);
time.second = Math.floor(value % 60);
if (hour) {
time.hour = hour;
}
return time;
value = parseFloat(value);
const time = {};
const hour = Math.floor(value / (60 * 60));
time.minute = Math.floor((value / 60) % 60);
time.second = Math.floor(value % 60);
if (hour) {
time.hour = hour;
}
return time;
};
/**
@@ -32,7 +33,7 @@ const parseTime = (value) => {
* @private
*/
const secondsToPeriod = (seconds) => {
return 'P' + seconds + 'S';
return "P" + seconds + "S";
};
/**
@@ -49,20 +50,20 @@ const secondsToPeriod = (seconds) => {
* @public
*/
const secondsToTime = (seconds, durfmt, config) => {
const includeHour = config && config.includeHour;
const includeHour = config && config.includeHour;
if (durfmt) {
const parsedTime = parseTime(seconds);
const timeString = durfmt.format(parsedTime).toString();
if (durfmt) {
const parsedTime = parseTime(seconds);
const timeString = durfmt.format(parsedTime).toString();
if (includeHour && !parsedTime.hour) {
return '00:' + timeString;
} else {
return timeString;
}
}
return includeHour ? '00:00:00' : '00:00';
if (includeHour && !parsedTime.hour) {
return "00:" + timeString;
} else {
return "00:" + timeString;
}
}
// includeHour ? '00:00:00' : '00:00';
return includeHour ? "00:00:00" : "00:00:00";
};
/**
@@ -73,11 +74,7 @@ const secondsToTime = (seconds, durfmt, config) => {
* @returns {Number} Number of children nodes
* @private
*/
const countReactChildren = (children) => React.Children.toArray(children).filter(n => n != null).length;
const countReactChildren = (children) =>
React.Children.toArray(children).filter((n) => n != null).length;
export {
countReactChildren,
parseTime,
secondsToPeriod,
secondsToTime
};
export { countReactChildren, parseTime, secondsToPeriod, secondsToTime };

View File

@@ -7,14 +7,17 @@
* @exports VideoPlayerBase
*/
import React from "react";
import ReactDOM from "react-dom";
import React from 'react';
import ReactDOM from 'react-dom';
import DurationFmt from "ilib/lib/DurationFmt";
import PropTypes from "prop-types";
import shallowEqual from "recompose/shallowEqual";
import DurationFmt from 'ilib/lib/DurationFmt';
import PropTypes from 'prop-types';
import shallowEqual from 'recompose/shallowEqual';
import { off, on } from "@enact/core/dispatcher";
import {
off,
on,
} from '@enact/core/dispatcher';
import {
adaptEvent,
call,
@@ -25,43 +28,56 @@ import {
preventDefault,
returnsTrue,
stopImmediate,
} from "@enact/core/handle";
import ApiDecorator from "@enact/core/internal/ApiDecorator";
import EnactPropTypes from "@enact/core/internal/prop-types";
import { is } from "@enact/core/keymap";
import { platform } from "@enact/core/platform";
import { Job, memoize, perfNow } from "@enact/core/util";
import { I18nContextDecorator } from "@enact/i18n/I18nDecorator";
import { toUpperCase } from "@enact/i18n/util";
import Skinnable from "@enact/sandstone/Skinnable";
import { getDirection, Spotlight } from "@enact/spotlight";
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
import { Spottable } from "@enact/spotlight/Spottable";
import Announce from "@enact/ui/AnnounceDecorator/Announce";
import ComponentOverride from "@enact/ui/ComponentOverride";
import { FloatingLayerDecorator } from "@enact/ui/FloatingLayer";
import { FloatingLayerContext } from "@enact/ui/FloatingLayer/FloatingLayerDecorator";
import Media from "@enact/ui/Media";
import Slottable from "@enact/ui/Slottable";
import Touchable from "@enact/ui/Touchable";
} from '@enact/core/handle';
import ApiDecorator from '@enact/core/internal/ApiDecorator';
import EnactPropTypes from '@enact/core/internal/prop-types';
import { is } from '@enact/core/keymap';
import { platform } from '@enact/core/platform';
import {
Job,
memoize,
perfNow,
} from '@enact/core/util';
import { I18nContextDecorator } from '@enact/i18n/I18nDecorator';
import { toUpperCase } from '@enact/i18n/util';
import Skinnable from '@enact/sandstone/Skinnable';
import {
getDirection,
Spotlight,
} from '@enact/spotlight';
import {
SpotlightContainerDecorator,
} from '@enact/spotlight/SpotlightContainerDecorator';
import { Spottable } from '@enact/spotlight/Spottable';
import Announce from '@enact/ui/AnnounceDecorator/Announce';
import ComponentOverride from '@enact/ui/ComponentOverride';
import { FloatingLayerDecorator } from '@enact/ui/FloatingLayer';
import {
FloatingLayerContext,
} from '@enact/ui/FloatingLayer/FloatingLayerDecorator';
import Media from '@enact/ui/Media';
import Slottable from '@enact/ui/Slottable';
import Touchable from '@enact/ui/Touchable';
import * as Config from "../../utils/Config";
import { $L } from "../../utils/helperMethods";
import * as Config from '../../utils/Config';
import { $L } from '../../utils/helperMethods';
//import SpotlightIds from '../../utils/SpotlightIds';
import { SpotlightIds } from "../../utils/SpotlightIds";
import { SpotlightIds } from '../../utils/SpotlightIds';
import PlayerOverlayHeader
from '../../views/PlayerPanel/PlayerOverlay/PlayerOverlayHeader';
import {
MediaControls,
MediaSlider,
secondsToTime,
Times,
} from "../MediaPlayer";
import TPlayerFeedBackBtn from "../TPlayerFeedBackBtn";
import FeedbackContent from "./FeedbackContent";
import FeedbackTooltip from "./FeedbackTooltip";
import MediaTitle from "./MediaTitle";
import Overlay from "./Overlay";
import Video from "./Video";
import css from "./VideoPlayer.module.less";
} from '../MediaPlayer';
import TPlayerFeedBackBtn from '../TPlayerFeedBackBtn';
import FeedbackContent from './FeedbackContent';
import FeedbackTooltip from './FeedbackTooltip';
import MediaTitle from './MediaTitle';
import Overlay from './Overlay';
import Video from './Video';
import css from './VideoPlayer.module.less';
const isEnter = is("enter");
const isLeft = is("left");
@@ -2027,6 +2043,7 @@ const VideoPlayerBase = class extends React.Component {
no5WayJump,
noAutoPlay,
noMiniFeedback,
playListInfo,
noSlider,
noSpinner,
selection,
@@ -2041,6 +2058,8 @@ const VideoPlayerBase = class extends React.Component {
onIntroDisabled,
videoComponent: VideoComponent,
cameraSettingsButton,
onBackButton,
panelInfo,
...mediaProps
} = this.props;
@@ -2134,23 +2153,11 @@ const VideoPlayerBase = class extends React.Component {
(this.state.infoVisible ? " " + css.lift : "")
}
>
{/* <TPlayerFeedBackBtn
spotlightId={"jumpleft"}
onClick={this.onClickLeftJump}
onSpotlightRight={this.SpotToRight}
onSpotlightDown={this.SpotToTitle}
>
{$L("10 sec")}
</TPlayerFeedBackBtn>
<TPlayerFeedBackBtn
type={"right"}
spotlightId={"jumpright"}
onClick={this.onClickRightJump}
onSpotlightLeft={this.SpotToLeft}
onSpotlightDown={this.SpotToTitle2}
>
{$L("10 sec")}
</TPlayerFeedBackBtn> */}
<PlayerOverlayHeader
playListInfo={playListInfo}
onClick={onBackButton}
panelInfo={panelInfo}
/>
</div>
)}
</Overlay>
@@ -2205,13 +2212,13 @@ const VideoPlayerBase = class extends React.Component {
>
{infoComponents}
</MediaTitle>
{noSlider ? (
{/* {noSlider ? (
<Times
current={this.state.currentTime}
total={this.state.duration}
formatter={durFmt}
/>
) : null}
) : null} */}
</div>
) : null}
{noSlider ? null : (
@@ -2223,6 +2230,7 @@ const VideoPlayerBase = class extends React.Component {
formatter={durFmt}
/>
) : null}
{this.state.mediaSliderVisible ? (
<Times
noTotalTime

View File

@@ -68,7 +68,7 @@
.controlFeedbackBtnLayer {
position: absolute;
z-index: 50;
top: 506px;
top: 0;
left: 0;
padding: 0;
width: 100%;
@@ -95,7 +95,7 @@
.bottom {
position: absolute;
z-index: 100; // Value assigned as part of the VideoPlayer API so layers may be inserted in-between
bottom: 10px;
bottom: -18px;
left: 0;
right: 0;
// left: @sand-video-player-padding-side;
@@ -125,8 +125,8 @@
// display: flex;
position: relative;
align-items: center;
margin-left: 90px;
margin-right: 90px;
margin-left: 60px;
margin-right: 59px;
height: 70px;
> *:first-child {
text-align: right;

View File

@@ -87,6 +87,13 @@ export const mainReducer = (state = initialState, action) => {
};
}
case types.GET_MAIN_LIVE_SHOW: {
return {
...state,
liveShowInfos: action.payload.showInfos,
};
}
default:
return state;
}

View File

@@ -1,15 +1,16 @@
import React, { useCallback } from "react";
import React, { useCallback } from 'react';
import classNames from "classnames";
import { useDispatch } from "react-redux";
import classNames from 'classnames';
import { useDispatch } from 'react-redux';
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { pushPanel } from "../../../../actions/panelActions";
import TVideoPlayer from "../../../../components/TVideoPlayer/TVideoPlayer";
import { panel_names } from "../../../../utils/Config";
import css from "./TCFV_3.module.less";
import { pushPanel } from '../../../../actions/panelActions';
import TVideoPlayer from '../../../../components/TVideoPlayer/TVideoPlayer';
import { panel_names } from '../../../../utils/Config';
import css from './TCFV_3.module.less';
const SpottableComponent = Spottable("li");
const SpottableComponentDiv = Spottable("div");
@@ -65,7 +66,7 @@ export default function TCFV_3({
dispatch(
pushPanel({
name: panel_names.PLAYER_PANEL,
playInfo: {
panelInfo: {
patnrId: data[0].patnrId,
showId: data[0].showId,
shptmBanrTpNm: "VOD",
@@ -137,7 +138,6 @@ export default function TCFV_3({
showUrl={showUrl}
width={1400}
height={630}
videoIsPlaying={false}
thumbNail={bgImgPath}
className={css.video}
videoIsPlaying

View File

@@ -14,6 +14,7 @@ export default function PlayerOverlayHeader({
selectedIndex,
onClick,
}) {
console.log("#playListInfo", playListInfo);
const onClickBack = () => {
if (onClick) {
onClick();
@@ -29,7 +30,7 @@ export default function PlayerOverlayHeader({
<img
src={
playListInfo
? playListInfo[selectedIndex]?.patncLogoPath
? playListInfo?.patncLogoPath
: panelInfo?.patncLogoPath
}
alt=""
@@ -38,14 +39,10 @@ export default function PlayerOverlayHeader({
<>
<h2 className={css.patnerName}>
{playListInfo
? playListInfo[selectedIndex]?.patncNm
: panelInfo?.patncNm}
{playListInfo ? playListInfo?.patncNm : panelInfo?.patncNm}
</h2>
<h3 className={css.title}>
{playListInfo
? playListInfo[selectedIndex]?.showNm
: panelInfo?.showNm}
{playListInfo ? playListInfo?.showNm : panelInfo?.showNm}
</h3>
</>
</div>

View File

@@ -3,7 +3,7 @@
.overIcon {
position: absolute;
z-index: 2;
z-index: 3;
&.backLiveicon {
display: flex;

View File

@@ -2,6 +2,7 @@ import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
@@ -18,14 +19,19 @@ import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import playButton from '../../../assets/images/btn/btn-video-play-nor@3x.png';
import * as CommonActions from '../../actions/commonActions';
import {
getHomeFullVideoInfo,
getMainCategoryShowDetail,
getMainLiveShow,
getMainLiveShowNowProduct,
} from '../../actions/mainActions';
import * as PanelActions from '../../actions/panelActions';
import { getChatLog } from '../../actions/playActions';
import {
getChatLog,
getSubTitle,
} from '../../actions/playActions';
import { MediaControls } from '../../components/MediaPlayer';
import TButton from '../../components/TButton/TButton';
import TButtonTab, { LIST_TYPE } from '../../components/TButtonTab/TButtonTab';
@@ -55,32 +61,33 @@ const unableToPlay = new Job((callback) => {
const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
const dispatch = useDispatch();
const videoPlayer = useRef(null);
const [playListInfo, setPlayListInfo] = useState("");
const [shopNowInfo, setShopNowInfo] = useState();
const [panelInfo, setPaneInfo] = useState([]);
const [tab, setTab] = useState(0);
const [isPlay, setIsPlay] = useState(false);
const [sideOpen, setSideOpen] = useState(true);
const [selectedIndex, setSelectedIndex] = useState(0);
const [isHide, setIsHide] = useState(true);
const panels = useSelector((state) => state.panels.panels);
const chatData = useSelector((state) => state.play.chatData);
//VOD
const youmaylikeInfos = useSelector((state) => state.main.youmaylikeInfos);
const showDetailInfo = useSelector((state) => state.main.showDetailInfo);
const featuredShowsInfos = useSelector(
(state) => state.main.featuredShowsInfos
);
//라이브
const liveChannelInfos = useSelector((state) => state.main.liveChannelInfos);
const showNowInfos = useSelector((state) => state.main.showNowInfo);
const showNowProduct = useSelector((state) => state.main.showNowProduct);
const liveShowInfos = useSelector((state) => state.main.liveShowInfos);
const tabList = [
$L("SHOP NOW"),
$L(panelInfo?.shptmBanrTpNm === "LIVE" ? "LIVE CHANNEL" : "FEATURED SHOWS"),
];
console.log("#showDetailInfo", showDetailInfo);
// 패널 정보 받기
const getPanelInfo = useCallback(() => {
if (panels) {
@@ -94,6 +101,7 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
const onClickBack = useCallback(
(ev) => {
console.log("#####################");
dispatch(PanelActions.popPanel());
},
[dispatch]
@@ -117,17 +125,24 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
setSideOpen((prev) => !prev);
};
const backButton = useCallback(() => {
return (
<TButton
className={css.backBtn}
spotlightId={"BACK_BUTTON"}
onClick={onClickBack}
onSpotlightDown={SpotToSlider}
size={"small"}
/>
);
}, [onClickBack, SpotToSlider]);
const getPlayer = useCallback((ref) => {
videoPlayer.current = ref;
console.log("#getPlayer ", ref);
}, []);
// const onPlay = useCallback(() => {
// setIsPlay((prev) => !prev);
// videoPlayer.current.play();
// }, [isPlay]);
// const playButton = useCallback(() => {
// console.log("#isPlay", isPlay);
// return (
// <SpottableBtn
// className={classNames(isPlay ? css.pauseButton : css.playButton)}
// onClick={onPlay}
// ></SpottableBtn>
// );
// }, [onPlay, isPlay]);
const isActive = useMemo(() => {
return playListInfo;
@@ -150,7 +165,7 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
}
setSelectedIndex((prev) => prev + 1);
};
console.log("#playListInfo", playListInfo);
const addPanelInfoToPlayList = (featuredShowsInfos) => {
const updatedPlayListInfo = [
{ ...showDetailInfo[0] },
@@ -169,6 +184,7 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
getMainCategoryShowDetail({
patnrId: panelInfo.patnrId,
showId: panelInfo.showId,
curationId: panelInfo.curationId,
})
);
@@ -187,9 +203,18 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
dispatch(
getChatLog({ patnrId: panelInfo.patnrId, showId: panelInfo.showId })
);
dispatch(getMainLiveShow());
}
}, [dispatch, panelInfo, selectedIndex]);
useEffect(() => {
if (playListInfo && playListInfo.length > 0) {
dispatch(
getSubTitle({ showSubtitleUrl: playListInfo[0].showSubtitlUrl })
);
}
}, [dispatch, playListInfo]);
// 10초후 탭 닫기
useEffect(() => {
let timer = setTimeout(() => {
@@ -223,7 +248,7 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
setShopNowInfo(showDetailInfo[0].productInfos);
}
if (panelInfo?.shptmBanrTpNm === "LIVE") {
setPlayListInfo(liveChannelInfos);
setPlayListInfo(liveChannelInfos ? liveChannelInfos : liveShowInfos);
setShopNowInfo(showNowProduct);
}
}, [panelInfo, showDetailInfo, liveChannelInfos, showNowProduct, dispatch]);
@@ -265,18 +290,17 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
delete props.panelInfo;
// console.log("#showUrl", playListInfo[selectedIndex]);
//TAB
return (
<TPanel isTabActivated={false} {...props} className={css.videoContainer}>
{typeof window !== "object" ? (
<VideoPlayer
className={css.backBtn}
setApiProvider={getPlayer}
onEnded={onClickBack} // 플레이어가 끝날때 호출
title={"동영상 타이틀 테스트 테스트"} // 동영상 플레이어
noAutoPlay={false} // 오토플레이
backButton={backButton()} // 뒤로가기 버튼
autoCloseTimeout={100000000000000} // 컨트롤 버튼 숨기는 시간
playListInfo={playListInfo && playListInfo[selectedIndex]} // 비디오 데이터
onBackButton={onClickBack} // 뒤로가기 버튼
panelInfo={panelInfo} // 패널정보 (라이브,VOD)
>
<source
src={
@@ -287,7 +311,13 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
type="type=application/mpegurl"
/>
<MediaControls></MediaControls>
{/* 테스트 */}
{/* <source
src={
" https://d130labfjte6iy.cloudfront.net/vod/1e62b39b-4301-4fe5-a16e-bebebef4e1c6/3e72500af7fc4d9d800e7a9533a29a36.mp4"
}
type="video/mp4"
/> */}
</VideoPlayer>
) : (
<ReactPlayer
@@ -305,16 +335,20 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
onEnded={onClickBack} // 플레이어가 끝날때 호출
/>
)}
{/* 백버튼, 로고 , 타이틀 */}
{isHide && (
{/* 리액트 플레이어 테스트용 */}
{typeof window !== "object" ? (
""
) : (
<PlayerOverlayHeader
selectedIndex={selectedIndex}
panelInfo={panelInfo}
playListInfo={playListInfo}
playListInfo={playListInfo && playListInfo[selectedIndex]}
onClick={onClickBack}
/>
)}
<div>
<SpottableBtn className={css.videoReduce} onClick={onClickBack} />
</div>
{/* 인디게이터 버튼 */}
<div className={classNames(css.overIcon, css.topIcon)}>
<SpottableBtn onClick={handleUpClick} />
@@ -363,11 +397,14 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
/>
)}
{panelInfo?.shptmBanrTpNm === "LIVE" && tab === 1 && (
<LiveChannelContents />
<LiveChannelContents
setSelectedIndex={setSelectedIndex}
liveInfos={playListInfo}
/>
)}
{shopNowInfo !== "undefined" &&
shopNowInfo &&
youmaylikeInfos &&
shopNowInfo.length < 3 &&
tab === 0 && <YouMayLikeContents shopNowInfo={shopNowInfo} />}
</Container>

View File

@@ -1,8 +1,34 @@
@import "../../style/CommonStyle.module.less";
@import "../../style/utils.module.less";
.videoContainer {
position: relative;
.playButton {
.size(@w: 60px, @h: 60px);
background-image: url("../../../assets/images/btn/btn-video-play-nor@3x.png");
background-size: cover;
&:focus {
.size(@w: 60px, @h: 60px);
border-radius: 50%;
background-color: #c70850;
opacity: 0.5;
}
}
.pauseButton {
.size(@w: 60px, @h: 60px);
background-image: url("../../../assets/images/btn/btn-voc-pause-nor@3x.png");
background-size: cover;
&:focus {
.size(@w: 60px, @h: 60px);
border-radius: 50%;
background-color: #c70850;
opacity: 0.5;
}
}
.toOpenBtn {
.size(@w: 147px, @h: 243px);
min-width: 60px !important;
@@ -49,7 +75,7 @@
}
}
.tabContainer {
.size(@w:660px, 100%);
.size(@w:660px, @h:100%);
background: #2c343f;
position: absolute;
right: 0;
@@ -75,6 +101,15 @@
bottom: 210px;
left: 60px;
}
.videoReduce {
.size(@w:60px, @h:60px);
background-image: url("../../../assets/images/btn/btn-video-min-nor@3x.png");
background-size: cover;
position: absolute;
right: 60px;
bottom: 150px;
z-index: 100;
}
.overIcon {
position: absolute;

View File

@@ -1,20 +1,25 @@
import React, { useCallback, useEffect, useState } from "react";
import React, {
useCallback,
useEffect,
useState,
} from 'react';
import { useDispatch, useSelector } from "react-redux";
import {
useDispatch,
useSelector,
} from 'react-redux';
import { pushPanel } from "../../../actions/panelActions";
import TVirtualGridList from "../../../components/TVirtualGridList/TVirtualGridList";
import { panel_names } from "../../../utils/Config";
import PlayerItemCard, { TYPES } from "../PlayerItemCard/PlayerItemCard";
import css from "./LiveChannelContents.module.less";
import PlayerTabLoading from "./PlayerTabLoading";
import { pushPanel } from '../../../actions/panelActions';
import TVirtualGridList
from '../../../components/TVirtualGridList/TVirtualGridList';
import { panel_names } from '../../../utils/Config';
import PlayerItemCard, { TYPES } from '../PlayerItemCard/PlayerItemCard';
import css from './LiveChannelContents.module.less';
import PlayerTabLoading from './PlayerTabLoading';
export default function LiveChannelContents() {
const dispatch = useDispatch();
export default function LiveChannelContents({ liveInfos }) {
const liveChannelInfos = useSelector((state) => state.main.liveChannelInfos);
console.log("#liveChannelInfos", liveChannelInfos);
const renderItem = useCallback(
({ index, ...rest }) => {
const {
@@ -26,9 +31,11 @@ export default function LiveChannelContents() {
strtDt,
endDt,
timezone,
} = liveChannelInfos[index];
} = liveInfos[index];
const handleItemClick = () => {};
const handleItemClick = () => {
setSelectedIndex(index);
};
return (
<PlayerItemCard
@@ -47,14 +54,14 @@ export default function LiveChannelContents() {
/>
);
},
[liveChannelInfos]
[liveInfos]
);
return (
<>
<div className={css.container}>
{liveChannelInfos && liveChannelInfos.length > 0 ? (
{liveInfos && liveInfos.length > 0 ? (
<TVirtualGridList
dataSize={liveChannelInfos.length}
dataSize={liveInfos.length}
direction="vertical"
renderItem={renderItem}
itemWidth={600}

View File

@@ -10,7 +10,10 @@ import {
useSelector,
} from 'react-redux';
import { pushPanel } from '../../../actions/panelActions';
import {
pushPanel,
updatePanel,
} from '../../../actions/panelActions';
import TItemCard, { TYPES } from '../../../components/TItemCard/TItemCard';
import TVirtualGridList
from '../../../components/TVirtualGridList/TVirtualGridList';

View File

@@ -23,7 +23,6 @@ export default function YouMayLikeContents({ shopNowInfo }) {
const dispatch = useDispatch();
const [height, setHeight] = useState(236);
const youmaylikeInfos = useSelector((state) => state.main.youmaylikeInfos);
const showNowProduct = useSelector((state) => state.main.showNowProduct);
const gridStyle = useMemo(() => ({ height: `${height}px` }), [height]);