define('application/components/oldplayer', [
'rofl/widgets/component',
'antie/runtimecontext',
'application/managers/api',
'application/managers/session',
'rofl/lib/utils',
'rofl/events/keyevent',
'application/managers/channel',
'rofl/lib/promise',
'application/managers/progress',
'rofl/devices/mediaplayer/mediaplayer',
'application/views/player',
'application/widgets/warmwelcome/overlay',
'product-layer/player/widgets/seeker',
'application/widgets/player/liveseeker',
'application/decorators/player/manipulation',
'rofl/analytics/web/google',
'rofl/media/source',
'application/widgets/player/warningbox',
'rofl/lib/l10n',
'rofl/logging/graylog',
'application/managers/player',
'application/managers/bookmark',
'application/helpers/parentalHelper',
'application/events/unlockedevent',
'application/models/epg/item',
'application/events/bingewatchevent',
'application/events/nextepisodeevent'
], function (
Component,
RuntimeContext,
ApiManager,
SessionManager,
Utils,
KeyEvent,
ChannelManager,
Promise,
ProgressManager,
MediaPlayer,
PlayerView,
WarmWelcome,
Seeker,
LiveSeeker,
PlayerManipulation,
GoogleAnalytics,
MediaSource,
WarningBox,
L10N,
Graylog,
PlayerManager,
BookmarkManager,
ParentalHelper,
UnlockedEvent,
EPGItem,
BingeWatchEvent,
NextEpisodeEvent
) {
'use strict';
var application = RuntimeContext.getCurrentApplication(),
api = ApiManager.getKPNAPI(),
ApiErrorCodes = ApiManager.getApiErrorCodes(),
channelManager = ChannelManager.getInstance(),
device = RuntimeContext.getDevice(),
configuration = application.getConfiguration(),
mediaPlayer = device.getMediaPlayer(),
sessionManager = SessionManager.getInstance(),
LIVE_PROGRESS_TIMEOUT = configuration.player.liveProgressTimeout,
KEY_HOLD_TIMEOUT = configuration.player.keyHoldTimeout,
GA = GoogleAnalytics.getInstance(),
l10n = L10N.getInstance(),
graylog = Graylog.getInstance(),
TYPES = {
LIVE: 'LIVE',
VOD: 'VOD',
VOD_MOVIE: 'VOD_MOVIE',
RESTART: 'RESTART',
RECORDING: 'RECORDING',
PROMO: 'PROMO'
},
EVENT = {
startVideo: 'StartVideo',
stopVideo: 'StopVideo',
startOver: 'StartOver',
trickplay: 'Trickplay'
},
DIRECTION = {
forward: 'forward',
backward: 'backward'
},
GALabels = {
live: 'LiveTV',
restart: 'Restart',
vod: 'CatchUp',
recording: 'Recording',
forward: 'Forward',
backward: 'Backward',
vod_movie: 'VOD',
series: 'SERIES'
},
PlayerComponent,
zapping = false,
// to include all the promises to channel up/down
promiseStack = [];
PlayerComponent = Component.extend({
/**
* Initialises the player component.
*
* @param {string} [id] - Id of the player. Optional.
*/
init: function init (id) {
init.base.call(this, id || 'player');
application.showLoader(true, true);
this._deviceBrand = device.getBrand();
this.decorate([PlayerManipulation]);
this._build();
this._setBindings();
this._warningBox = new WarningBox();
this.appendChildWidget(this._warningBox);
this._showErrorOnPlayerFocus = null;
this._previousChannelResolved = true;
this._playbackStatus = {};
this._playbackStatus.active = false;
},
/**
* Sets the bindings.
*
* @private
*/
_setBindings: function () {
this._onSeekSpeedChangeBound = Utils.bind(this._onSeekSpeedChanged, this);
this._onSeekCurrentTimeChangeBound = Utils.bind(this._onSeekCurrentTimeChanged, this);
this._onSeekBound = Utils.bind(this._onSeek, this);
this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
this._onKeyUpBound = Utils.bind(this._onKeyUp, this);
this._onControlKeyDownBound = Utils.bind(this._onControlKeyDown, this);
this._onSelectBound = Utils.bind(this._onSelect, this);
this._onFocusBound = Utils.bind(this._onFocus, this);
this._multitaskHandler = Utils.bind(this._onVisibilityChanged, this);
this._onMouseSeekEventBound = Utils.bind(this._onMouseSeekEvent, this);
this._onParentalControlChangedEventBound = Utils.bind(this._onParentalControlChangedEvent, this);
},
/**
* Builds the seeker.
*
* @private
*/
_buildSeeker: function () {
this._seeker = Seeker();
this._seeker.attach(
this._onSeekSpeedChangeBound,
this._onSeekCurrentTimeChangeBound,
this._onSeekBound,
configuration.player.seekSteps,
configuration.player.turnTrickplayImmediately
);
},
/**
* Builds the live seeker.
*
* @private
*/
_buildLiveSeeker: function () {
var program = this._program,
playbackTime = application.getDate().getTime(),
startTime = program.getStartTime(),
endTime = program.getEndTime();
this._seeker = LiveSeeker();
this._seeker.attach({
onSpeedChanged: this._onSeekSpeedChangeBound,
onSeek: this._onSeekBound,
onCurrentTimeUpdated: this._onSeekCurrentTimeChangeBound,
steps: configuration.player.seekSteps
});
if (this._pauseTime) {
playbackTime = this._pauseTime;
}
this._streamStart = playbackTime;
this._seeker.setProgramTime(startTime * 1000, endTime * 1000, playbackTime);
},
/**
* Builds the player component.
*
* @private
*/
_build: function () {
var playerView = this._view = new PlayerView(),
warmWelcome = this._warmWelcome = new WarmWelcome();
this.appendChildWidget(warmWelcome);
this.appendChildWidget(playerView);
},
/**
* BeforeRender event.
*/
onBeforeRender: function () {
this._loadingTime = application.getDate();
this._showErrorOnPlayerFocus = null;
},
/**
* BeforeShow event.
*
* @param {Object} e - The event parameters.
*/
onBeforeShow: function (e) {
var args = e.args || {},
videoType = args.type,
subType = args.subType || null;
this._bingewatch = this._nextEpisodes = args.bingewatch;
this._analyticsType = args.analyticsType || null;
this._lastPlayerArguments = args;
this._triggerParentalOnFocus = false;
this._subType = subType;
this._callingPage = args.callingPage;
this._backButtonActive = false;
this._seekCurrentTime = null;
this._setEventListeners();
this._watchingVideoTimeStart = application.getDate();
if (!(args.channelId
&& (this._lastChannelId === args.channelId)
&& videoType === TYPES.LIVE
&& videoType === this._type)) {
this._resetPlayer(false, false, true);
}
switch (videoType) {
case TYPES.LIVE:
this._onPlayLive(args);
break;
case TYPES.RESTART:
this._onPlayRestart(args);
break;
case TYPES.RECORDING:
this._onPlayRecording(args);
break;
case TYPES.VOD:
case TYPES.VOD_MOVIE:
this._onPlayVOD(args);
break;
case TYPES.PROMO:
this._playPromo(args);
break;
}
this._view.showUI(true);
this._pauseTime = application.getDate().getTime();
},
_onPlayLive: function (args) {
var videoType = args.type;
if (!(args.channelId
&& (this._lastChannelId === args.channelId)
&& videoType === this._type)
|| this._parentalEscaped) {
this._playChannel(args.channelId);
} else {
this._view.getMiniEPG().setChannel(args.channelId);
application.hideLoader();
}
},
_onPlayRestart: function (args) {
this._onRestart(args.channelId, args.startTime);
this._restartButtonPressed = true;
},
_onPlayRecording: function (args) {
if (this._mediaChangeInProgress) {
this._afterMediaChangeStuff = Utils.bind(function () {
this._playRecording(args);
}, this);
} else {
this._mediaChangeInProgress = true;
this._playRecording(args);
}
},
_onPlayVOD: function (args) {
if (this._mediaChangeInProgress) {
this._afterMediaChangeStuff = Utils.bind(function () {
this._mediaChangeInProgress = true;
this._playVOD(args);
}, this);
} else {
this._mediaChangeInProgress = true;
this._playVOD(args);
}
},
/**
* Route to the specified page after player has been initialized.
*
* @param {string} page - The page to route.
* @param {Number|undefined} channelId - The last channel ID.
*/
routeToPage: function (page, channelId) {
var main = application.getComponent('main');
channelId = channelId || null;
application.route(page, {
lastChannelId: channelId,
callback: Utils.bind(function () {
this.setStyleTo('display', 'none');
main.setStyleTo('display', 'block');
}, this)
});
},
/**
* Activates the warm welcome.
*
* @private
*/
_activateWarmWelcome: function () {
var warmWelcome = this._warmWelcome;
application.hideLoader();
sessionManager.increaseWarmWelcomeDisplayCount();
warmWelcome.activate(true);
warmWelcome.focus();
this._view.hideUI({
skipAnim: true
});
},
/**
* Returns true if the player should show the warm welcome.
*
* @returns {boolean} - True if should show warm welcome.
* @private
*/
_shouldActivateWarmWelcome: function () {
return sessionManager.shouldShowWarmWelcome();
},
/**
* Sets the event listeners.
*
* @private
*/
_setEventListeners: function () {
this._setTizenMultitaskHandler();
this.addEventListener('focus', this._onFocusBound);
this.addEventListener('keydown', this._onKeyDownBound);
this.addEventListener('keyup', this._onKeyUpBound);
this.addEventListener('select', this._onSelectBound);
this.addEventListener('mouseseek', this._onMouseSeekEventBound);
application.addEventListener('$parentalcontrol', this._onParentalControlChangedEventBound);
this._view.getControls().addEventListener('keydown', this._onControlKeyDownBound);
mediaPlayer.addEventCallback(this, this._onPlayerEvent);
},
/**
* Removes the event listeners.
*
* @private
*/
_removeEventListeners: function () {
this._removeTizenMultitaskHandler();
this.removeEventListener('focus', this._onFocusBound);
this.removeEventListener('keydown', this._onKeyDownBound);
this.removeEventListener('keyup', this._onKeyUpBound);
this.removeEventListener('select', this._onSelectBound);
this.removeEventListener('mouseseek', this._onMouseSeekEventBound);
application.removeEventListener('$parentalcontrol', this._onParentalControlChangedEventBound);
this._view.getControls().removeEventListener('keydown', this._onControlKeyDownBound);
mediaPlayer.removeEventCallback(this, this._onPlayerEvent);
},
/**
* Select event.
*
* @param {Object} e - The event data.
* @private
*/
_onSelect: function (e) {
var target = e.target,
dataItem,
programDetails;
if (this._warmWelcome.isActive()) {
this._onSelectWarmWelcome();
e.preventDefault();
e.stopPropagation();
return;
}
if (this._view.isUIVisible()) {
switch (target.id) {
case 'menuBtn':
this._onShownMenu();
break;
case 'info':
programDetails = this._view.getProgramDetails();
if (programDetails.hasClass('expand')) {
this._onInfo(false, null);
} else {
this._setDetailInfo();
}
break;
case 'rewind':
this._onRewind();
break;
case 'playpause':
this._onPlayPause();
break;
case 'fastforward':
this._onFastForward();
break;
case 'restartBtn':
if (!this._restartButtonPressed && !this._mediaChangeInProgress) {
if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
mediaPlayer.playFrom(0);
} else {
this._resetPlayer();
this._onRestart();
this._restartButtonPressed = true;
}
GA.onEvent('Action', EVENT.startOver);
}
break;
case 'recordBtn':
this._onRecord();
break;
case 'now-next':
this._onMiniEPG();
break;
case 'next-episode':
if (!this._mediaChangeInProgress) {
this._onNextEpisode(this._nextEpisodes.splice(0, 1)[0]);
}
this._view.closeExpandedContents();
break;
case 'back-button':
this._view.closeExpandedContents();
this._onBack();
break;
case 'liveBtn':
this._onLive();
break;
}
if (target.hasClass('carouselItem')) {
dataItem = target.getChildWidgetByIndex(0).getDataItem().item;
if (dataItem.isLocked() && !sessionManager.getUserPin()) {
application.route('parentalpin', {
successCallback: Utils.bind(function () {
this._onMiniEPGProgram(target);
}, this),
errorCallback: Utils.bind(function (result) {
if (Utils.isUndefined(result)) {
this._view.getControls().focus();
this._view.getMiniEPG().focus();
}
}, this),
escapeCallback: Utils.bind(function () {
this._view.getControls().focus();
this._view.getMiniEPG().focus();
}, this),
closeOnError: false
});
} else {
this._onMiniEPGProgram(target);
}
}
} else {
this._view.showUI(true);
}
},
/**
* Gets executed when a program is selected from the mini epg.
*
* @param {Object} target - The target program.
* @private
*/
_onMiniEPGProgram: function (target) {
this._view.getControls().focus();
this._view.closeExpandedContents();
this._playChannel(target.getDataItem());
GA.onEvent(
'Action',
'MiniEPGProgramSelected',
{
eventLabel: 'LiveTV'
});
},
/**
* Function solves selecting restart button.
*
* @param {number} [inputChannelId] - Number of channel.
* @param {number} [inputStartTime] - Time number of start of the program.
* @private
*/
_onRestart: function (inputChannelId, inputStartTime) {
var channelId = inputChannelId || null,
startTime = inputStartTime || null;
this._restartStarted = true;
if (channelId && startTime) {
this._getCurrentBroadcast(channelId)
.then(Utils.bind(function (epgItem) {
// Set the new program data.
this._view.setProgram({program: epgItem, type: this._type});
this._program = epgItem;
}, this))
.then(Utils.bind(this._prepareRestartStream, this))
['catch'](Utils.bind(this._onPlayerError, this));
} else {
if (this._type === TYPES.LIVE) {
this._type = TYPES.RESTART;
}
if (this._type !== TYPES.RECORDING) {
this._view.setProgram({
program: this._program,
type: this._type,
hasNextEpisode: !!(this._nextEpisodes && this._nextEpisodes.length)
});
}
this._prepareRestart();
}
},
/**
* Prepares restart of playback.
*
* @private
*/
_prepareRestart: function () {
switch (this._type) {
case TYPES.RESTART:
this._prepareRestartStream();
break;
case TYPES.VOD_MOVIE:
this._prepareRestartVOD();
break;
case TYPES.RECORDING:
this._mediaChangeInProgress = false;
this._recordingContentRestarted = true;
this._onPlayRecording({
data: this._program
});
break;
case TYPES.VOD:
this._mediaChangeInProgress = false;
this._vodContentRestarted = true;
this._onPlayVOD({
data: this._program,
type: TYPES.VOD
});
break;
}
},
/**
* Prepares the restart stream.
*
* @private
*/
_prepareRestartStream: function () {
var program = this._program,
startTime = application.getDate();
this._prepareMediaSource({
type: TYPES.RESTART,
assetId: program.getChannel().getAssetId(),
contentId: program.getChannelId(),
startTime: program.getStartTime() * 1000
}).then(Utils.bind(function (mediaSource) {
if (this._delayedStreamTime) {
startTime = this._delayedStreamTime;
}
this._playMediaSource(mediaSource);
this._startRestartProgressTimeOut(startTime);
this._setCurrentTimeToSeeker();
}, this))
['catch'](Utils.bind(this._onPlayerError, this));
},
/**
* Prepares the restart VOD.
*
* @private
*/
_prepareRestartVOD: function () {
var program = this._program;
this._vodContentRestarted = true;
this._prepareMediaSource({
type: TYPES.VOD_MOVIE,
assetId: program.getAssetId(),
contentId: program.getId()
}).then(Utils.bind(function (mediaSource) {
this._playMediaSource(mediaSource);
this._view.setVODProgress({
percentage: 0,
currentTime: 0,
duration: this._program.getDuration()
});
this._restartStarted = false;
this._restartButtonPressed = false;
}, this))
['catch'](Utils.bind(this._onPlayerError, this));
},
/**
* Handle switching the channel with numeric input.
*
* @param {Object} e - The event parameters.
* @private
*/
_handleSwitchChannel: function (e) {
var view = this._view;
if (!this._canSwitchChannel()) {
return;
}
if (this._keys.length >= 3) {
this._keys = '';
}
this._keys += e.keyChar;
if (view.isUIVisible()) {
this._view.hideUI({
duration: 500
});
}
this._setChannelSwitchNumber();
},
/**
* Sets the channel switching number.
*
* @private
*/
_setChannelSwitchNumber: function () {
var view = this._view,
channelSwitcher = view.getChannelSwitcher(),
timeoutValue = this._deviceBrand === 'samsung' ? 3000 : 1000;
clearTimeout(this._inputTimeout);
channelSwitcher.setLabel(this._keys);
this._inputTimeout = setTimeout(Utils.bind(function () {
this._switchChannel(channelManager.getChannelByNumber(this._keys));
this._keys = '';
}, this), timeoutValue);
},
/**
* BeforeHide event.
*/
onBeforeHide: function () {
var value = ((application.getDate() - this._watchingVideoTimeStart) / 60000).toFixed(2),
channelId;
if (channelManager.getChannels().length > 0) {
// TODO: Temporary get channel by id (NPO 1). Otherwise last watched: channelManager.getLastWatchedChannel()
channelId = channelManager.getChannelById(18).getId() || channelManager.getChannelByNumber(1).getId();
GA.onEvent('player', 'time', {
eventLabel: channelId,
eventValue: value
});
if (this._warmWelcome.isActive()) {
this._warmWelcome.activate(false);
}
this._removeEventListeners();
}
},
/**
* PlayerEvent.
*
* @param {Object} e - The player event data.
* @private
*/
_onPlayerEvent: function (e) {
var playerComponent = application.getComponent('player'),
main = application.getComponent('main'),
landingPage = main.getChildWidget('landingPage'),
detail = application.getComponent('detail'),
view = this._view,
channelId,
startTime,
timeOfLoadingChannel;
switch (e.type) {
case MediaPlayer.EVENT.PLAYING:
BookmarkManager.onPlayStart(this._contentId, this._type);
if (zapping) {
zapping = false;
timeOfLoadingChannel = application.getDate() - this._switchChannelStart;
channelId = this._program.getChannelId();
GA.onEvent('player', 'zap', {
eventLabel: channelId,
eventValue: timeOfLoadingChannel
});
}
if (this._delayedStreamTime && this._type === TYPES.RESTART) {
this._delayedSeek();
}
if (this._delayedBack) {
this._delayedBack();
this._delayedBack = null;
}
if (playerComponent.hasClass('focus') && this._canShowUI()) {
if (!this._seeker.isActive()) {
view.showUI(true);
}
}
application.hideLoader(2000);
break;
case MediaPlayer.EVENT.PAUSED:
BookmarkManager.onPause();
if (this._progress !== null && (this._progress.behind >= 0 || isNaN(this._progress.behind))) {
this._pauseTime = application.getDate().getTime();
}
view.onPause();
view.showUI(false);
this._playbackStatus.active = false;
break;
case MediaPlayer.EVENT.STATUS:
// Set playack status to active after receiving the first STATUS event after PLAYING.
this._playbackStatus.active = true;
if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
this._setVODProgress(e);
}
break;
case MediaPlayer.EVENT.COMPLETE:
if (this._bingewatchActive) {
return;
}
this._resetPlayer();
channelId = this._program.getChannelId();
if (this._type === TYPES.RECORDING) {
if (this._callingPage === 'detail') {
this._callingPage = null;
this._playChannel(channelId);
this.setStyleTo('display', 'none');
main.setStyleTo('display', 'block');
detail.setStyleTo('display', 'block');
detail.focus();
}
} else if (this._type === TYPES.VOD_MOVIE) {
this._mediaChangeInProgress = false;
this._onBack();
} else if (this._subType === 'promo') {
this.setStyleTo('display', 'none');
this._removeEventListeners();
landingPage.togglePlayers(false);
} else {
startTime = this._program.getStartTime();
this._checkNextPlayableItem(channelId, startTime);
}
this._playbackStatus.active = false;
break;
case MediaPlayer.EVENT.ERROR:
this._playbackStatus.active = false;
this._onPlayerError(e.message);
break;
case MediaPlayer.EVENT.BITRATE_CHANGED:
this._reportBitrateChange(e);
break;
// No default.
}
},
/**
* Returns next playable item in channel.
*
* @param {number} channelId - Id of current channel.
* @param {number} itemStartTime - Start time of current item.
* @private
*/
_checkNextPlayableItem: function (channelId, itemStartTime) {
channelManager.getNextBroadcastAtTime(channelId, itemStartTime)
.then(Utils.bind(function (epgItem) {
var args = {};
if (!epgItem) {
this._playChannel(channelId);
this._delayedBack = function () {
this._onBack();
};
} else if (epgItem.isLive()) {
if (epgItem.isLocked()) {
application.route('parentalpin', {
successCallback: Utils.bind(function () {
this._playChannel(channelId);
application.broadcastEvent(new UnlockedEvent());
}, this),
errorCallback: Utils.bind(function (result) {
if (Utils.isUndefined(result)) {
this.routeToPage('channellist', channelId);
}
}, this),
escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true),
callingPageKeyEvent: {
keyCodes: [KeyEvent.VK_CHANNEL_UP, KeyEvent.VK_CHANNEL_DOWN],
keyEventCallback: Utils.bind(this._skipCl, this, epgItem)
}
});
} else {
this._playChannel(channelId);
}
} else if (epgItem.canPlay() && epgItem.isPlayable()) {
api.read('detail', {
params: {
endpoint: epgItem.getDetailsAction()
},
withCredentials: true
}).then(Utils.bind(function (broadcast) {
args.data = broadcast;
this._playVOD(args);
}, this));
} else {
itemStartTime = epgItem.getStartTime();
this._checkNextPlayableItem(channelId, itemStartTime);
}
}, this));
},
/**
* Attempts to play a channel.
*
* @param {string} [channelId] - The channel id.
* @param {boolean} [reset] - True if should reset channelId.
* @private
*/
_playChannel: function (channelId, reset) {
var assetId;
this._bingewatch = this._nextEpisodes = null;
if (!channelId) {
if (!reset) {
// TODO: Temporary get channel by ID (NPO 1). Otherwise last watched: channelManager.getLastWatchedChannel()
channelId = channelManager.getChannelById(18).getId() || channelManager.getChannelByNumber(1).getId();
} else {
channelId = channelManager.getChannelByNumber(1).getId();
}
/*
* We are playing something for the first time.
* Stop the old playback first to lower the concurrent stream amount.
*/
this._program = null;
this._type = TYPES.LIVE;
}
this._checkingParental = false;
this._lastChannelId = channelId;
assetId = channelManager.getChannelById(channelId).getAssetId();
this._type = TYPES.LIVE;
// Save last played channel id
channelManager.setLastWatchedChannel(channelId);
this._loadChannelData(channelId, assetId)
.then(Utils.bind(this._onChannelReady, this))
['catch'](Utils.bind(this._onPlayerError, this));
},
/**
* Plays the VOD stream.
*
* @param {Object} args - The arguments.
* @private
*/
_playVOD: function (args) {
var data = args.data,
channelId = data.getChannelId(),
type = args.type,
self = this,
goToChannelListOnEscapeParental = channelId ? true : false,
playContent = function () {
application.showLoader(true, true);
self._resetPlayer();
self._view.setProgram({
program: data,
type: self._type,
hasNextEpisode: !!(self._nextEpisodes && self._nextEpisodes.length)
});
self._prepareMediaSource({
startTime: channelId ? new Date(data.getStartTime() * 1000).getTime() : null,
endTime: channelId ? new Date(data.getEndTime() * 1000).getTime() : null,
assetId: data.getAssetId(),
contentId: data.getId(),
type: self._type,
// NOTE: Pay attention to this. Is it right?
replayAssetId: channelId ? channelManager.getChannelById(channelId).getAssetId() : null,
replayContentId: channelId
})
.then(Utils.bind(function (mediaSource) {
self._playMediaSource(mediaSource);
}, this))
['catch'](Utils.bind(self._onPlayerError, self));
};
self._view.hideProgress({skipAnim: true});
this._type = type || TYPES.VOD;
this._program = data;
if (!sessionManager.getUserPin() && data.isLocked()) {
if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
mediaPlayer.pause();
}
application.route('parentalpin', {
successCallback: function () {
playContent();
application.broadcastEvent(new UnlockedEvent());
},
errorCallback: Utils.bind(function (result) {
if (Utils.isUndefined(result)) {
ParentalHelper.parentaleEscapeCallback.call(this, goToChannelListOnEscapeParental);
}
}, this),
escapeCallback: Utils.bind(ParentalHelper.parentaleEscapeCallback, this, goToChannelListOnEscapeParental)
});
} else {
playContent();
}
},
/**
* Plays the Promo video.
*
* @param {Object} args - The arguments.
* @private
*/
_playPromo: function (args) {
var data = args.data,
promoUrl = args.promo_url,
mediaSource;
this._resetPlayer();
this._type = TYPES.VOD;
this._program = data;
this._view.setPromo(data, 'promo');
this._isPromo = true;
mediaSource = new MediaSource(promoUrl);
this._playMediaSource(mediaSource);
application.showPlayer();
},
/**
* Plays the RECORDING stream.
*
* @param {Object} args - The arguments.
* @private
*/
_playRecording: function (args) {
var data = args.data;
this._resetPlayer();
this._program = data;
this._type = TYPES.RECORDING;
this._view.setProgram({program: data, type: 'VOD'});
this._prepareMediaSource({
assetId: data.getAssetId(),
contentId: data.getRecordingId(),
type: TYPES.RECORDING,
startTime: new Date(data.getStartDeltaTime()).getTime()
})
.then(Utils.bind(function (mediaSource) {
this._playMediaSource(mediaSource);
}, this))
['catch'](Utils.bind(this._onPlayerError, this));
},
/**
* Renders the UI part of the new channel.
*
* @param {Object[]} args - All the arguments required to render player UI.
* args[0] - MediaSource of the channel.
* args[1] - EPG Item of the channel.
* @private
*/
_onChannelUI: function (args) {
var epgItem = args[1],
view = this._view;
// If we came from channel switching, hide the input.
this._view.getChannelSwitcher().setLabel('');
this._keys = '';
this._program = epgItem;
view.setProgram({program: epgItem, type: this._type});
if (this._warmWelcome.isActive()) {
view.disablePointerSupport();
}
if (this._canShowUI()) {
view.showUI(false);
}
this._startLiveProgressTimeOut();
},
/**
* Gets executed when the channel data finishes loading.
*
* @param {Array} args - The arguments.
* @private
*/
_onChannelReady: function (args) {
var channelId = args[1].getChannelId();
if (args[0] === 'locked') {
if (!this._deferredPlayerInteraction) {
application.route('parentalpin', {
successCallback: Utils.bind(function () {
this._playChannel();
application.broadcastEvent(new UnlockedEvent());
}, this),
errorCallback: Utils.bind(function (result) {
if (Utils.isUndefined(result)) {
this.routeToPage('channellist', channelId);
}
}, this),
escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true)
});
} else {
this._lastChannelId = null;
this._triggerParentalOnFocus = true;
this._mediaChangeInProgress = false;
}
} else {
this._resetPlayer();
this._onChannelUI(args);
this._playMediaSource(args[0]);
if (this._view.getProgramDetails().hasClass('expand')) {
this._setDetailInfo();
}
}
},
/**
* Attempts to play the video.
*
* @param {Object} mediaSource - The mediasource.
* @private
*/
_playMediaSource: function (mediaSource) {
var isLive = this._type === TYPES.LIVE,
isRestart = this._type === TYPES.RESTART,
isVodMovie = this._type === TYPES.VOD_MOVIE,
isSeries = !!(isVodMovie && this._program._seriesId),
mediaPlayerState = mediaPlayer.getState(),
contentRestarted = this._vodContentRestarted || this._recordingContentRestarted,
self = this,
beginPlaybackFrom;
this._createAndAttachSeeker();
if (this._deviceBrand !== 'lg') {
mediaPlayer.setMediaSource(mediaSource);
}
if (this._analyticsType) {
this._GATypeLabel = this._analyticsType;
} else {
this._GATypeLabel = isSeries ? GALabels.series : GALabels[this._type.toLowerCase()];
}
this._mediaSource = mediaSource;
mediaSource.prepare()
.then(function () {
mediaPlayerState = mediaPlayer.getState();
self._mediaChangeInProgress = false;
if (self._deviceBrand === 'lg') {
self._resetMediaPlayer();
mediaPlayer.setMediaSource(mediaSource);
}
if (mediaPlayer.getState() === MediaPlayer.STATE.EMPTY) {
return;
}
if (Utils.isFunction(self._afterMediaChangeStuff)) {
self._afterMediaChangeStuff();
self._afterMediaChangeStuff = null;
}
if (isLive) {
mediaPlayer.beginPlayback({
async: true
});
} else if (isRestart) {
// Samsung live streams are 0 time based, so we don't need the relative streamtime.
if (self._delayedStreamTime && self._deviceBrand === 'samsung') {
mediaPlayer.beginLivePlayback({
async: true
});
} else if (self._deviceBrand === 'lg' && mediaPlayerState === MediaPlayer.STATE.EMPTY) {
// When restarting stream on LG from zero, TV is trying to seek 3 times
mediaPlayer.beginPlaybackFrom((self._program.getStartTime() + 1), {
async: true
});
} else {
if (!self._delayedStreamTime) {
self._delayedStreamTime = self._program.getStartTime() * 1000;
}
mediaPlayer.beginPlayback({
async: true
});
}
} else {
if (self._isPromo) {
// Dirt yuck yuck because Samsung.
mediaPlayer.beginLivePlayback();
} else if (isVodMovie) {
beginPlaybackFrom = contentRestarted || !self._bookmarkTime ? 0 : self._bookmarkTime;
mediaPlayer.beginPlaybackFrom(beginPlaybackFrom, {
async: true
});
self._vodContentRestarted = self._recordingContentRestarted = false;
} else {
beginPlaybackFrom = contentRestarted ? self._program.getStreamOffset() : (self._bookmarkTime ||
self._program.getStreamOffset());
mediaPlayer.beginPlaybackFrom(beginPlaybackFrom, {
async: true
});
self._vodContentRestarted = self._recordingContentRestarted = false;
}
}
self._restartStarted = false;
self._restartButtonPressed = false;
GA.onEvent('Action', EVENT.startVideo, {eventLabel: self._GATypeLabel});
graylog.onPlaybackStart(self._program._detailsAction, self._watchingVideoTimeStart);
})
['catch'](Utils.bind(this._onPlayerError, this));
},
/**
* Parse the bookmark object and retrieve the new starttime.
*
* @param {Object} data - The data.
* @private
*/
_parsedBookmarkTime: function (data) {
var containers = Utils.getNested(data, 'resultObj', 'containers'),
bookmarks;
if (!Utils.isUndefined(containers) && containers.length) {
bookmarks = Utils.getNested(containers[0], 'metadata', 'bookmarks');
}
if (!Utils.isUndefined(bookmarks) && bookmarks.length) {
this._bookmarkTime = Utils.getNested(bookmarks[0], 'startDeltaTime');
}
},
/**
* Prepares the media source.
*
* @param {Object} params - The parameters.
* @returns {Promise} - Promise resolving with the media source.
* @private
*/
_prepareMediaSource: function (params) {
var headers = {},
url; // browser hack to play video
this._contentId = params.contentId;
this._assetId = params.assetId;
this._type = params.type;
if (device.getModel() === 'webbbkit') {
url = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4';
return Promise.resolve(new MediaSource(url));
}
if (this._streamWithParental
|| (this._program && this._program.isLocked())
|| (this._program && this._program.streamRequiresPin())
) {
if (sessionManager.getUserPin()) {
headers['pcPin'] = sessionManager.getUserPin();
}
this._streamWithParental = false;
}
if (params.type === TYPES.VOD || params.type === TYPES.VOD_MOVIE || params.type === TYPES.RECORDING) {
return api.read('userdata', {
params: params,
withCredentials: true
}).then(Utils.bind(function (data) {
// Check for empty assetId
params = this._checkForEmptyAsset(params, data);
this._parsedBookmarkTime(data);
return api.read('stream', {
params: params,
withCredentials: true,
headers: headers
});
}, this));
}
return api.read('stream', {
params: params,
withCredentials: true,
headers: headers
});
},
/**
* Check if there is an asset in the params.
*
* @param {Object} params - The _prepareMediaSource params.
* @param {Object} data - The userdata response data.
* @returns {Object} - The updated _prepareMediaSource params.
*
* @private
*/
_checkForEmptyAsset: function (params, data) {
var containers,
assets,
assetId,
i;
if (Utils.isUndefined(params.assetId)) {
containers = Utils.getNested(data, 'resultObj', 'containers');
if (!Utils.isUndefined(containers) && containers.length) {
assets = Utils.getNested(containers[0], 'entitlement', 'assets');
if (!Utils.isUndefined(assets) && assets.length) {
for (i = 0; i < assets.length; i++) {
if (assets[i].assetType === 'MASTER') {
if (assets[i].programType === 'CUTV') {
assetId = assets[i].assetId;
this._assetId = assetId;
break;
}
}
}
if (!Utils.isEmpty(assetId)) {
params.assetId = assetId;
}
}
}
}
return params;
},
/**
* Creates and attaches the seeker.
*
* @private
*/
_createAndAttachSeeker: function () {
if (this._type !== TYPES.VOD && this._type !== TYPES.VOD_MOVIE && this._type !== TYPES.RECORDING) {
this._buildLiveSeeker();
} else {
this._buildSeeker();
}
},
/**
* Sets the live progress.
*
* @param {booleand} [preventSeekingWhileLoading] - Prevent seeking on timeline while loading restart..
* @private
*/
_setLiveProgress: function (preventSeekingWhileLoading) {
var program = this._program,
startTime = new Date(program.getStartTime() * 1000),
endTime = new Date(program.getEndTime() * 1000),
progress,
currentTime;
progress = this._progress = ProgressManager.getLiveProgressPercentage(startTime, endTime, this._pauseTime);
if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING && progress.behind < 0) {
currentTime = application.getDate();
this._pauseTime = currentTime.getTime() + progress.behind + 1000;
}
if (preventSeekingWhileLoading && !this._pauseTime && this._restartStarted) {
return;
}
this._restartStarted = false;
PlayerManager.setLivePlaybackPosition(this._pauseTime || application.getDate().getTime());
if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
if (this._seeker && typeof this._seeker.setCurrentTime === 'function') {
if (this._pauseTime) {
this._seeker.setCurrentTime(this._pauseTime);
} else {
this._seeker.setCurrentTime(application.getDate().getTime());
}
}
}
if (progress.secondHead >= 100) {
// First cancel the timeout.
this._stopLiveProgressTimeout();
// Retrieve the new program.
this._getCurrentBroadcast(program.getChannelId())
.then(Utils.bind(function (epgItem) {
if (!epgItem) {
// Create epgData when missing.
epgItem = new EPGItem({}, channelManager.getChannelById(program.getChannelId()));
}
if (!sessionManager.getUserPin() && epgItem.isLocked()) {
if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
mediaPlayer.pause();
}
application.route('parentalpin', {
successCallback: Utils.bind(function () {
this._checkingParental = false;
this._playChannel(epgItem.getChannelId());
application.broadcastEvent(new UnlockedEvent());
}, this),
errorCallback: Utils.bind(function (result) {
this._checkingParental = false;
if (Utils.isUndefined(result)) {
this.routeToPage('channellist', program.getChannelId());
}
}, this),
escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true),
callingPageKeyEvent: {
keyCodes: [KeyEvent.VK_CHANNEL_UP, KeyEvent.VK_CHANNEL_DOWN],
keyEventCallback: Utils.bind(this._skipCl, this, epgItem)
}
});
} else {
this._checkingParental = false;
// Set the new program data.
this._view.setProgram({program: epgItem, type: this._type});
this._program = epgItem;
if (this._view.getProgramDetails().hasClass('expand')) {
this._setDetailInfo();
}
// Restart the timeout.
this._startLiveProgressTimeOut();
}
}, this));
}
this._view.setLiveProgress(progress);
},
/**
* Sets the live progress.
*
* @param {Date} fixedTime - Time when button was pressed.
* @private
*/
_setRestartProgress: function (fixedTime) {
var program = this._program,
startTime = new Date(program.getStartTime() * 1000),
endTime = new Date(program.getEndTime() * 1000),
currentTime = application.getDate(),
behind = fixedTime - startTime,
progress;
this._pauseTime = currentTime.getTime() - behind;
progress = this._progress = ProgressManager.getRestartProgressPercentage(
startTime,
endTime,
behind);
if (progress.secondHead >= 100) {
// First cancel the timeout.
this._stopRestartProgressTimeout();
// Retrieve the new program.
this._getCurrentBroadcast(program.getChannelId())
.then(Utils.bind(function (epgItem) {
// Set the new program data.
this._view.setProgram({program: epgItem, type: this._type});
this._program = epgItem;
// Restart the timeout.
this._startRestartProgressTimeOut(currentTime);
}, this));
}
this._view.setLiveProgress(progress);
},
/**
* Sets the VOD progress.
*
* @param {Object} e - The player event.
* @private
*/
_setVODProgress: function (e) {
var currentTime = e.currentTime,
duration = e.duration,
percentage = currentTime / duration * 100,
view = this._view;
view.setVODProgress({
percentage: percentage,
currentTime: Math.floor(currentTime),
duration: Math.floor(duration)
});
// Execute callback once the progress has been set. Callback shows progressbar.
if (this._playbackStatus.callback) {
this._playbackStatus.callback();
delete this._playbackStatus.callback;
}
if (this._bingewatch
&& this._bingewatch.length
&& !this._bingewatchActive
&& currentTime >= duration - 5) {
this._bingewatchActive = true;
view.hideUI({
skipAnim: true
});
application.route('bingewatching', {
episode: this._bingewatch.splice(0, 1)[0],
successCallback: Utils.bind(this._onBingeWatch, this),
cancelCallback: Utils.bind(this._onBack, this, true)
});
}
},
/**
* Starts the live progress timeout.
*
* @private
*/
_startLiveProgressTimeOut: function () {
this._setLiveProgress();
this._stopLiveProgressTimeout();
this._liveProgressTimeout = setInterval(
Utils.bind(this._setLiveProgress, this),
LIVE_PROGRESS_TIMEOUT);
},
/**
* Starts the live progress timeout.
*
* @param {Date} currentTime - Time when button was pressed.
* @private
*/
_startRestartProgressTimeOut: function (currentTime) {
if (this._restartButtonPressed) {
this._setRestartProgress(currentTime);
this._restartButtonPressed = false;
}
this._liveProgressTimeout = setInterval(
Utils.bind(this._setLiveProgress, this, true),
LIVE_PROGRESS_TIMEOUT);
},
/**
* Stops the live progress timeout.
*
* @private
*/
_stopLiveProgressTimeout: function () {
clearInterval(this._liveProgressTimeout);
},
/**
* Stops the replay progress timeout.
*
* @private
*/
_stopRestartProgressTimeout: function () {
clearInterval(this._liveProgressTimeout);
},
/**
* Sets the value of time to seeker.
*
* @private
*/
_setCurrentTimeToSeeker: function () {
var timeValue,
program = this._program,
seeker = this._seeker,
currentTime = application.getDate();
timeValue = currentTime.getTime() / 1000 - program.getStartTime();
seeker.setCurrentTime(timeValue);
},
/**
* KeyDown event for warm welcome overlay.
*
* @param {Object} e - The event data.
* @private
*/
_onKeyDownWarmWelcome: function (e) {
var menu = application.getComponent('menu'),
view = this._view,
currentContent = this._warmWelcome.getActivePageId();
this._warmWelcome.onKeyDown(e);
if (currentContent === 'page-2') {
if (!view.isUIVisible()) {
if (e.keyCode === KeyEvent.VK_DOWN) {
view.showUI(false, true);
this._warmWelcome.next();
}
}
} else if (currentContent === 'page-4') {
if (menu.hasClass('invisible') || menu.hasClass('collapse')) {
if (e.keyCode === KeyEvent.VK_LEFT) {
application.showMenu('overlay', true);
this._warmWelcome.next();
}
}
}
if (e.keyCode === KeyEvent.VK_RIGHT || e.keyCode === KeyEvent.VK_UP) {
e.preventDefault();
e.stopPropagation();
}
},
/**
* Select event for warm welcome overlay.
*
* @private
*/
_onSelectWarmWelcome: function () {
var currentContent = this._warmWelcome.getActivePageId(),
view = this._view;
if (currentContent === 'page-4') {
view.hideUI();
}
},
/**
* KeyDown event.
*
* @param {Object} e - The event data.
* @private
* @returns {boolean} False if user input is blocked.
*/
_onKeyDown: function (e) {
var view = this._view,
backButton = view.getBackButton(),
controls = view.getControls(),
showUI = false,
miniEpgView;
if (this._warmWelcome.isActive()) {
this._onKeyDownWarmWelcome(e);
e.preventDefault();
e.stopPropagation();
return;
}
if (application.blockUserInput) {
e.preventDefault();
e.stopPropagation();
return false;
}
switch (e.keyCode) {
case KeyEvent.VK_LEFT:
showUI = true;
if (this._callingPage !== 'landing') {
application.focusMenu('player');
view.closeExpandedContents();
}
e.stopPropagation();
break;
case KeyEvent.VK_RIGHT:
e.stopPropagation();
break;
case KeyEvent.VK_PAUSE:
this._onPause();
controls.focusPlayPauseButton();
break;
case KeyEvent.VK_PLAY:
this._onPlay();
controls.focusPlayPauseButton();
break;
case KeyEvent.VK_PLAY_PAUSE:
this._onPlayPause();
controls.focusPlayPauseButton();
break;
case KeyEvent.VK_REWIND:
this._onRewind();
controls.focusRewindButton();
break;
case KeyEvent.VK_FAST_FWD:
this._onFastForward();
controls.focusFastForwardButton();
break;
case KeyEvent.VK_CHANNEL_UP:
case KeyEvent.VK_CHANNEL_DOWN:
if (this._type === TYPES.LIVE || this._type === TYPES.RESTART) {
if (this._deviceBrand === 'samsung') {
this._setKeyHoldData(e, this._onKeyHoldSamsungHandler);
} else {
this._zapChannel(e);
}
}
break;
case KeyEvent.VK_BACK:
if (this._view.isContentExpanded()) {
this._view.showUI(true);
this._view.closeExpandedContents();
controls.focus();
} else {
this._onBack();
}
break;
case KeyEvent.VK_UP:
miniEpgView = this._view.getMiniEPG();
if (this._backButtonActive) {
view.hideUI();
}
if (miniEpgView.hasClass('expand') && miniEpgView.isFocussed()) {
this._view.showMiniEPG(true);
controls.focus();
} else {
backButton.focus();
}
break;
case KeyEvent.VK_DOWN:
if (this._backButtonActive) {
controls.focus();
} else if (this._view.getMiniEPG().hasClass('expand')) {
this._view.getMiniEPG().focus();
} else {
if (this._view.isVisible()) {
this._view.showProgramInfo(false);
this._view.showMiniEPG(false);
this._view.getMiniEPG().focus();
}
}
view.showUI(true);
break;
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
this._switchChannelStart = application.getDate();
this._handleSwitchChannel(e);
break;
}
if (showUI && this._keys === '') {
// UI disappears after 3 seconds (configurable) of no user input.
view.showUI(true);
}
},
/**
* KeyUp event.
*
* @param {Object} e - The keyEvent data.
* @private
*/
_onKeyUp: function (e) {
if (this._keyHoldTimeout) {
this._onKeyHoldCancelled(e);
}
},
/**
* Sets the keyHold data.
*
* @param {Object} e - The keyEvent data.
* @param {Function} keyHoldHandler - Function that handles the keyEvent.
*/
_setKeyHoldData: function (e, keyHoldHandler) {
var keyCode = e.keyCode;
// Don't set keyHold while parental is being loaded. Prevents async triggers.
if (this._checkingParental) {
return;
}
this._keyHoldData = {};
this._keyHoldData[keyCode] = {
keyHoldHandler: Utils.bind(keyHoldHandler, this, e)
};
this._keyHoldTimeout = setTimeout(Utils.bind(this._onKeyHoldTriggered, this, e), KEY_HOLD_TIMEOUT);
},
/**
* This function is triggered when keyHold times out. KeyHoldHandler executed with boolean meaning keyhold
* behaviour should be executed.
*
* @param {Object} e - The keyEvent data.
* @private
**/
_onKeyHoldTriggered: function (e) {
var keyCode = e.keyCode,
keyHoldData = this._keyHoldData[keyCode];
this._keyHoldTimeout = null;
if (keyHoldData && Utils.isFunction(keyHoldData.keyHoldHandler)) {
keyHoldData.keyHoldHandler(true);
delete this._keyHoldData[keyCode];
}
},
/**
* This function is triggered when keyHold is cancelled. KeyHoldHandler executed with boolean meaning keyhold
* behaviour should not be executed.
*
* @param {Object} e - The keyEvent data.
* @private
**/
_onKeyHoldCancelled: function (e) {
var keyCode = e.keyCode,
keyHoldData = this._keyHoldData[keyCode];
clearTimeout(this._keyHoldTimeout);
this._keyHoldTimeout = null;
if (keyHoldData && Utils.isFunction(keyHoldData.keyHoldHandler)) {
keyHoldData.keyHoldHandler(false);
delete this._keyHoldData[keyCode];
}
},
/**
* KeyDown event for Samsung rc.
*
* @param {Object} e - The event data.
* @param {boolean} keyHoldTriggered - True if triggered by keyHold timeout or cancel by keyup event.
* @private
*/
_onKeyHoldSamsungHandler: function (e, keyHoldTriggered) {
switch (e.keyCode) {
case KeyEvent.VK_CHANNEL_UP:
case KeyEvent.VK_CHANNEL_DOWN:
if (keyHoldTriggered && !this._checkingParental) {
this._view.showMiniEPG();
if (!this._view.getMiniEPG().hasClass('expand')) {
this._view.getControls().focus();
}
} else {
this._zapChannel(e);
}
break;
}
},
/**
* KeyDown event for controls.
*
* @param {Object} e - The event data.
* @private
*/
_onControlKeyDown: function (e) {
var view = this._view,
shouldHide = true,
controls = view.getControls();
if (controls.isVisible() &&
(e.keyCode === KeyEvent.VK_LEFT || e.keyCode === KeyEvent.VK_RIGHT)) {
if (mediaPlayer.getState() === MediaPlayer.STATE.PAUSED) {
shouldHide = false;
}
view.showUI(shouldHide);
}
},
/**
* Focus event.
*
* @param {Object} e - The event data.
*
* @private
*/
_onFocus: function (e) {
var target = e.target,
main = application.getComponent('main'),
activeMainWidget = main.getActiveChildWidget(),
landingPage = activeMainWidget ? activeMainWidget.getId() === 'landingPage' : false;
this._backButtonActive = target.id === 'back-button';
if (this._showErrorOnPlayerFocus === true) {
this._cannotLoadVideoError();
} else {
if (!this._warmWelcome.isActive() && !landingPage) {
application.getComponent('player').setStyleTo('display', 'block');
application.hideMenu('player');
}
}
},
/**
* Resets the player.
*
* @param {boolean} [fromSeek] - From seek.
* @param {boolean} [fromChannelSwitch] - From channel switch.
* @param {boolean} [initialReset] - Initial reset.
* @private
*/
_resetPlayer: function (fromSeek, fromChannelSwitch, initialReset) {
var mediaPlayerState = mediaPlayer.getState(),
seeker = this._seeker;
this._deferredPlayerInteraction = false;
this._playbackStatus.active = false;
if (seeker) {
if (seeker.isActive()) {
seeker.cancel();
}
seeker.detach();
this._seeker = null;
}
if (!fromSeek) {
this._delayedStreamTime = null;
this._streamStart = null;
this._pauseTime = null;
}
this._errorState = false;
this._isPromo = false;
this._progress = null;
this._bookmarkTime = null;
this._view.resetControls();
this._view.hideWarningBox(true);
this._keys = '';
this._view.resetLiveLabels();
this._view.getZappbanner().resetPauseLabel();
if (fromChannelSwitch) {
BookmarkManager.onStop(this._contentId);
} else {
if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
if (mediaPlayer.getCurrentTime() / mediaPlayer.getDuration() < configuration.BOOKMARK_THRESHOLD) {
BookmarkManager.onStop(null, mediaPlayer.getCurrentTime());
} else {
BookmarkManager.onStop(null, 0);
}
} else {
BookmarkManager.onStop();
}
}
if (mediaPlayerState !== MediaPlayer.STATE.STOPPED
&& mediaPlayerState !== MediaPlayer.STATE.EMPTY
&& mediaPlayerState !== MediaPlayer.STATE.ERROR) {
mediaPlayer.stop();
}
this._view.onPlay();
mediaPlayerState = mediaPlayer.getState();
if (mediaPlayerState !== MediaPlayer.STATE.EMPTY) {
mediaPlayer.reset();
if (!initialReset) {
this._mediaChangeInProgress = true;
}
}
this._stopLiveProgressTimeout();
this._stopRestartProgressTimeout();
},
/**
* Checks whether broadcast is parental protected and if yes, starts parental flow.
*
* @param {string} channelId - The channel id.
* @param {string} assetId - The asset id.
* @returns {Promise} - Promise resolving with the channel data.
* @private
*/
_checkChannelParental: function (channelId, assetId) {
return this._getCurrentBroadcast(channelId, assetId)
.then(Utils.bind(function (broadcast) {
if (!broadcast) {
broadcast = EPGItem({}, channelManager.getChannelById(channelId));
}
if (broadcast.isLocked()) {
this._streamWithParental = true;
}
return new Promise(Utils.bind(function (resolve, reject) {
if (!sessionManager.getUserPin() && broadcast.isLocked()) {
application.route('parentalpin', {
successCallback: Utils.bind(function () {
this._program = broadcast;
this._checkingParental = false;
resolve();
}, this),
errorCallback: reject, // ParentalHelper.parentaleErrorCallback.bind(this),
escapeCallback: Utils.bind(ParentalHelper.parentaleEscapeCallback, this),
callingPageKeyEvent: {
keyCodes: [KeyEvent.VK_CHANNEL_UP, KeyEvent.VK_CHANNEL_DOWN],
keyEventCallback: Utils.bind(this._skipCl, this, broadcast)
}
});
} else {
this._program = broadcast;
this._checkingParental = false;
resolve();
}
}, this));
}, this));
},
/**
* Skips child lock from current locked channel.
*
* @param {Object} broadcast - The current program.
* @param {Object} ev - Key event.
* @private
*/
_skipCl: function (broadcast, ev) {
this._program = broadcast;
this._checkingParental = false;
this._zapChannel(ev);
},
/**
* Zaps next or previous channel depending on key event.
*
* @param {Object} ev - Key Event.
*/
_zapChannel: function (ev) {
var channelToSwitch;
if (this._type === TYPES.LIVE || this._type === TYPES.RESTART) {
if (ev.keyCode === KeyEvent.VK_CHANNEL_UP) {
channelToSwitch = channelManager.getNextChannel(this._program.getChannel());
} else {
channelToSwitch = channelManager.getPreviousChannel(this._program.getChannel());
}
this._switchChannelStart = application.getDate();
this._switchChannel(channelToSwitch);
}
},
/**
* Attempts to switch to the new channel.
*
* @param {Object} channel - The new channel.
* @private
*/
_switchChannel: function (channel) {
var self = this,
channelId = channel.getId(),
assetId = channel.getAssetId(),
promise,
main = application.getComponent('main'),
detail = application.getComponent('detail'),
menu = application.getComponent('menu'),
controls;
if (this._callingPage) {
if (this._callingPage === 'detail') {
detail.setStyleTo('display', '');
detail._historyStack = [];
detail.back();
}
if (this._callingPage !== 'channelList') {
this._callingPage = null;
main.setStyleTo('display', '');
main._historyStack = [];
main.back();
}
menu.getChildWidgetByIndex(0).setSelectedIndex({section: 'MIDDLE', index: 0});
this.focus();
}
if (!this._canSwitchChannel() || !this._previousChannelResolved || this._checkingParental) {
return;
}
this._checkingParental = true;
this._checkChannelParental(channelId, assetId).then(Utils.bind(function () {
this._inputTimeout = null;
this._resetPlayer(false, true);
// Save last played channel id
channelManager.setLastWatchedChannel(channelId);
this._channelPromise = this._loadNextChannelMediaSource(channelId, assetId);
this._lastChannelId = channelId;
this._previousChannelResolved = false;
this._checkingParental = false;
clearTimeout(this._channelTimer);
// Close any expanded content before channel switch.
if (this._view.isContentExpanded()) {
controls = this._view.getControls();
this._view.closeExpandedContents();
controls.focus();
}
// todo append this into a stack of promises and resolve only when processed all
promise = Utils.clone(this._channelPromise
.then(function (results) {
self._onChannelUI(results);
// render next channel UI first but play only the last promise in the stack
if (promise === (promiseStack[promiseStack.length - 1])) {
// player timeout to avoid UI get stucked
self._channelTimer = setTimeout(function () {
if (mediaPlayer.getState() !== MediaPlayer.STATE.EMPTY) {
self._resetPlayer();
}
self._playMediaSource(results[0]);
self._previousChannelResolved = true;
GA.onEvent('Action', EVENT.stopVideo, {eventLabel: self._GATypeLabel});
}, 0);
promiseStack = [];
}
}))
['catch'](function () {
/*
* When player going to error state while using DirectDial,
* set previousChannelResolved to true, otherwise cannot use direct dial anymore.
*/
self._previousChannelResolved = true;
self._checkingParental = false;
self._onPlayerError();
});
promiseStack.push(promise);
zapping = true;
}, this)).catch(Utils.bind(function () {
this._keys = '';
this._inputTimeout = null;
this._checkingParental = false;
this._view.getChannelSwitcher().setLabel('');
}, this));
},
/**
* Attempts to load channel data.
*
* @param {string} channelId - The channel id.
* @param {string} assetId - The asset id.
* @returns {Promise} - Promise resolving with the channel data.
* @private
*/
_loadChannelData: function (channelId, assetId) {
// Only show the loader if WarmWelcome is not active.
if (!this._warmWelcome.isActive()) {
application.showLoader(true, true);
}
return this._getCurrentBroadcast(channelId, assetId)
.then(Utils.bind(function (broadcast) {
// create epgData when missing.
if (!broadcast) {
broadcast = new EPGItem({}, channelManager.getChannelById(channelId));
}
this._program = broadcast;
if (broadcast.isLocked() && !sessionManager.getUserPin()) {
return Promise.resolve([
'locked',
broadcast
]);
}
return this._prepareMediaSource({
contentId: channelId,
assetId: assetId,
type: 'LIVE'
})
.then(function (mediaSource) {
return [
mediaSource,
broadcast
];
});
}, this));
},
/**
* Attempts to load media source for given channel and asset.
*
* @param {string} channelId - The channel id.
* @param {string} assetId - The asset id.
* @returns {Promise} - Promise resolving with the channel data.
* @private
*/
_loadNextChannelMediaSource: function (channelId, assetId) {
var broadcast = this._program;
return this._prepareMediaSource({
contentId: channelId,
assetId: assetId,
type: 'LIVE'
}).then(function (mediaSource) {
return [
mediaSource,
broadcast
];
});
},
/**
* Returns the current broadcast.
*
* @param {string} channelId - The channel id.
* @returns {Promise} - Promise resolving with the broadcast.
* @private
*/
_getCurrentBroadcast: function (channelId) {
if (this._pauseTime) {
return channelManager.getBroadcastAtTime(
channelId,
application.getDate() - Math.abs(this._progress.behind)
);
}
return channelManager.getCurrentBroadcastForChannel(channelId);
},
/**
* Removes the tizen multitask handler.
*/
_removeTizenMultitaskHandler: function removeTizenMultitaskHandlerFn () {
document.removeEventListener('visibilitychange', this._multitaskHandler);
},
/**
* Sets the tizen multitask handler.
*/
_setTizenMultitaskHandler: function setTizenMultitaskHandlerFn () {
document.addEventListener('visibilitychange', this._multitaskHandler);
},
/**
* Visibility changed event.
*
* @private
*/
_onVisibilityChanged: function () {
if (!document.hidden) {
if (sessionManager.isLoggedIn()) {
sessionManager.refreshToken()
.then(Utils.bind(function () {
this._callingPage = null;
this._lastChannelId = null;
this._resetPlayer();
this._playChannel(null, true);
}, this));
}
}
},
/**
* Returns true if the channel can be switched.
*
* @returns {boolean} - True if the channel can be switched.
* @private
*/
_canSwitchChannel: function () {
return this._type === TYPES.LIVE || this._type === TYPES.RESTART;
},
/**
* Gets executed when the seek is confirmed.
*
* @param {Object} e - The seek event data.
* @private
*/
_onSeek: function (e) {
this._seekSpeed = null;
this._seekCurrentTime = null;
this._view.resetControls();
this._view.getZappbanner().setTrickplaySettings();
if (this._type !== TYPES.VOD && this._type !== TYPES.VOD_MOVIE && this._type !== TYPES.RECORDING) {
this._startLiveProgressTimeOut();
}
// Below only gets executed for live trickplay.
if (e && e.seekTo && this._type === TYPES.LIVE) {
if (e.currentTime >= application.getDate()) {
this._resetPlayer();
this._playChannel(this._program.getChannelId());
} else {
this._pauseTime = e.currentTime;
this._delayedStreamTime = e.currentTime;
this._resetPlayer(true);
this._onRestart();
}
} else if (this._type === TYPES.RESTART) {
if (e.currentTime >= application.getDate()) {
this._resetPlayer();
this._playChannel(this._program.getChannelId());
} else {
mediaPlayer.resume();
if (e.relativePlaybackTime === 0) {
this._pauseTime = this._program.getStartTime() * 1000;
if (this._deviceBrand === 'lg') {
mediaPlayer.playFrom(this._program.getStartTime() + 1);
} else {
mediaPlayer.playFrom(0);
}
} else {
this._pauseTime = e.currentTime;
mediaPlayer.playFrom(e.seekTo);
}
}
}
},
/**
* Gets executed when the current time changes.
*
* @param {number|Object} data - The seek data.
* @private
*/
_onSeekCurrentTimeChanged: function (data) {
if (!this._seeker.isActive()) {
return;
}
if (this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
this._seekCurrentTime = data;
this._onSeekVODTime(data);
} else {
this._onSeekLIVETime(data);
}
},
/**
* Gets executed when the current seek time changes for live video.
*
* @param {Object} data - The data.
* @param {number} data.currentTime - The current seek time.
* @param {number} data.relativePlaybackTime - The current relative playback time.
*
* @private
*/
_onSeekLIVETime: function (data) {
var programStart = this._program.getStartTime() * 1000,
seeker = this._seeker,
currentSeekTime = data.currentTime,
maxSeekTime = PlayerManager.getMaxSeekValue() * 1000,
playFrom;
if (currentSeekTime < programStart) {
if (this._type === TYPES.LIVE) {
this._resetPlayer();
this._pauseTime = this._delayedStreamTime = programStart;
this._onRestart();
} else {
seeker.confirm(programStart);
}
return;
}
if (currentSeekTime >= application.getDate()) {
/*
* Intentionally set the playfrom time higher than the current date.
* This way the seeker will think it's past the live moment and start playing from live.
*/
playFrom = application.getDate();
playFrom.setMinutes(playFrom.getMinutes() + 1);
seeker.setCurrentTime(playFrom.getTime());
seeker.confirm(playFrom.getTime());
}
if (!this._program.canSeek()
&& seeker.getDirection() === 1
&& data.relativePlaybackTime > maxSeekTime) {
seeker.confirm(programStart + maxSeekTime);
return;
}
this._pauseTime = currentSeekTime;
this._setLiveProgress();
},
/**
* Gets executed when the current seek time changes for VOD.
*
* @param {number} time - The current seek time.
* @private
*/
_onSeekVODTime: function (time) {
var duration = mediaPlayer.getDuration(),
maxTime = PlayerManager.getMaxSeekValue();
if (!this._program.canSeek() && this._seekSpeed > 0 && time >= maxTime) {
this._seeker.confirm(maxTime);
time = maxTime;
}
if (time <= 0) {
this._seeker.confirm();
} else if (time >= duration) {
this._seeker.confirm(duration - 5);
}
this._setVODProgress({
currentTime: time,
duration: duration
});
},
/**
* Gets executed when the speed changes.
*
* @param {number} speed - The speed.
* @private
*/
_onSeekSpeedChanged: function (speed) {
this._seekSpeed = speed;
if (speed) {
this._view.updateSeekSpeed(speed);
}
},
/**
* Returns true if the UI can be shown.
*
* @returns {boolean} - True if the UI can be shown.
* @private
*/
_canShowUI: function () {
return !this._warmWelcome.isActive();
},
/**
* Returns the value of playbackStatus property.
*
* @returns {Object} - Container properties regarding the current playback state.
*/
getPlaybackStatus: function () {
return this._playbackStatus;
},
/**
* Gets executed when the player error triggers.
*
* @param {Object} e - The player error event.
* @private
*/
_onPlayerError: function (e) {
var message;
if (this._errorState) {
return;
}
this._errorState = true;
application.hideLoader();
if (e && e.resultCode && e.resultCode === '406') {
this._onPlaybackIssueMessage(e);
return;
}
if (e && e.toString && (e.toString().indexOf('PLAYER_ERROR_INVALID_STATE') >= 0)) {
/*
* We want to ignore this error from Samsung Tizen,
* which is caused when zapping fast.
*/
return;
}
if (e) {
message = e.toString ? e.toString() : JSON.stringify(e);
if (this._mediaSource) {
Graylog.getInstance().onPlaybackError(this._mediaSource.getMediaUrl(), 'n.a.', 'playready', message);
} else {
Graylog.getInstance().onPlaybackError('n.a', 'n.a', 'n.a.', message);
}
}
if (this._deviceBrand === 'default') {
// We want to disable the player errors for the development browser.
return;
}
if (this._warmWelcome.isActive()) {
this._view.hideUI();
}
if (application.getComponent('player').isFocussed()) {
this._cannotLoadVideoError(Utils.getNested(e, 'errorDescription') || '');
} else {
this._showErrorOnPlayerFocus = true;
}
},
/**
* Focus.
*/
focus: function () {
if (this.outputElement.style.display === 'none') {
this.setStyleTo('display', 'block');
}
if (this._warmWelcome.isActive()) {
this._warmWelcome.focus();
} else {
this._view.focus();
}
if (this._triggerParentalOnFocus) {
application.route('parentalpin', {
successCallback: Utils.bind(function () {
this.onBeforeShow({
args: this._lastPlayerArguments
});
application.broadcastEvent(new UnlockedEvent());
}, this),
errorCallback: Utils.bind(function (result) {
if (Utils.isUndefined(result)) {
this.routeToPage('channellist');
}
}, this),
escapeCallback: ParentalHelper.parentaleEscapeCallback.bind(this, true)
});
}
},
/**
* Reports the bitrate change.
*
* @param {Object} e - The event data.
* @private
*/
_reportBitrateChange: function (e) {
GA.onEvent('player', 'bitrate', {
eventLabel: e.bitrate
});
},
/**
* Return the current player view.
*
* @returns {Object} - The current player view.
*/
getView: function () {
return this._view;
},
/**
* Hides the warning box.
*
* @param {boolean} [skipAnim] - True if should skip the animations.
*/
hideWarningBox: function (skipAnim) {
this._warningBox.hide({
skipAnim: skipAnim
});
},
/**
* Shows the warning box.
*
* @param {Object} config - The config.
*/
showWarningBox: function (config) {
this._warningBox.show(config);
},
/**
* Popups the error message.
*
* @param {string} errorCode - The errorcode.
*
* @private
*/
_cannotLoadVideoError: function (errorCode) {
var self = this,
type = 'fullscreen',
imgUrl = 'src/assets/images/error-icon.png',
title,
text,
button;
errorCode = errorCode || '';
switch (errorCode) {
case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_1:
case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_2:
case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_3:
case ApiErrorCodes.CONCURRENT_STREAM_LIMIT_REACHED_4:
title = L10N.getInstance().get('errors.stream.max_concurrent_streams_title');
text = L10N.getInstance().get('errors.stream.max_concurrent_streams_text');
button = {id: 'error-close-button', label: L10N.getInstance().get('errors.ok')};
break;
default:
title = L10N.getInstance().get('errors.cannot_load_video');
button = {id: 'error-close-button', label: L10N.getInstance().get('errors.close')};
}
this._showErrorOnPlayerFocus = false;
application.hideLoader();
application.route('error', {
type: type,
title: title,
text: text,
button: button,
imgUrl: imgUrl,
errorCode: errorCode,
callback: function () {
self._resetPlayer();
self._playChannel(null, true);
self._onBack();
}
});
},
/**
* Gets called when a mouse seek event happens.
*
* @param {Object} e - The event data.
* @param {number} e.percentage - The seek percentage.
* @param {boolean} e.finished - True if the mouse seek event has finished.
* @private
*/
_onMouseSeekEvent: function (e) {
var type = this._type;
if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
mediaPlayer.pause();
}
if (type === TYPES.VOD || type === TYPES.VOD_MOVIE || this._type === TYPES.RECORDING) {
this._onVODMouseSeek(e);
} else {
this._onLiveRestartMouseSeek(e);
}
},
/**
* Determine direction of mouse seekening.
*
* @param {number} currentTime - Current time of the stream.
* @param {number} newTime - New time of the stream that we want to play.
* @returns {string} - Direction of the seeking.
* @private
*/
_getMouseSeekDirection: function (currentTime, newTime) {
var direction;
if (currentTime > newTime) {
direction = DIRECTION.backward;
} else {
direction = DIRECTION.forward;
}
return direction;
},
/**
* VOD Mouse Seek event.
*
* @param {Object} e - The event data.
* @param {number} e.percentage - The seek percentage.
* @param {boolean} e.finished - True if the mouse seek event has finished.
* @private
*/
_onVODMouseSeek: function (e) {
var percentage = e.percentage,
duration,
currentTime;
duration = mediaPlayer.getDuration();
currentTime = (duration * percentage) / 100;
if (!this._prevCurrentTime) {
this._prevCurrentTime = currentTime;
}
this._view.setVODProgress({
percentage: percentage,
currentTime: Math.floor(currentTime),
duration: Math.floor(duration)
});
if (e.finished) {
if (this._getMouseSeekDirection(this._prevCurrentTime, currentTime) === DIRECTION.backward) {
GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.backward});
} else {
GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.forward});
}
this._prevCurrentTime = currentTime;
this._seeker.confirm(currentTime);
}
},
/**
* Live and Restart Mouse Seek event.
*
* @param {Object} e - The event data.
* @param {number} e.percentage - The seek percentage.
* @param {boolean} e.finished - True if the mouse seek event has finished.
* @private
*/
_onLiveRestartMouseSeek: function (e) {
var start = this._program.getStartTime() * 1000,
end = this._program.getEndTime() * 1000,
seeker = this._seeker,
duration = end - start,
difference = (duration / 100) * (100 - e.percentage),
seekTo = end - difference,
playFrom;
this._pauseTime = Math.round(seekTo);
this._setLiveProgress();
if (!this._prevProgress) {
this._prevProgress = e.percentage;
}
if (e.finished) {
if (this._getMouseSeekDirection(this._prevProgress, e.percentage) === DIRECTION.backward) {
GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.backward});
} else {
GA.onEvent('Action', EVENT.trickplay, {eventLabel: GALabels.forward});
}
this._prevProgress = e.percentage;
if (seekTo < start) {
if (this._type === TYPES.LIVE) {
this._pauseTime = start;
this._resetPlayer();
this._onRestart();
} else {
seeker.confirm(start);
}
return;
}
// Because of timing add 10 seconds to this check.
if ((seekTo + 10000) >= application.getDate()) {
/*
* Intentionally set the playfrom time higher than the current date.
* This way the seeker will think it's past the live moment and start playing from live.
*/
playFrom = application.getDate();
playFrom.setMinutes(playFrom.getMinutes() + 1);
seeker.setCurrentTime(playFrom);
seeker.confirm(playFrom);
} else {
seeker.confirm(seekTo);
}
}
},
/**
* Executes a delayed seek.
*
* @private
*/
_delayedSeek: function () {
var delayedTime = this._delayedStreamTime,
startTime = this._program.getStartTime() * 1000,
currentTime = mediaPlayer.getCurrentTime(),
seekTo;
this._pauseTime = delayedTime;
if (this._deviceBrand === 'samsung') {
seekTo = (delayedTime / 1000) - (startTime / 1000);
} else {
seekTo = currentTime + ((delayedTime / 1000) - (startTime / 1000));
}
this._delayedStreamTime = null;
mediaPlayer.playFrom(Math.round(seekTo));
},
/**
* Behaviour when go back from player.
*
* @param {boolean} [skipBingewatch] - True if bingewatch popup should be skipped.
* @private
*/
_onBack: function (skipBingewatch) {
var main = application.getComponent('main'),
detail = application.getComponent('detail'),
landingPage = main.getChildWidget('landingPage'),
view = this._view,
self = this,
percentage = (100 / mediaPlayer.getDuration()) * mediaPlayer.getCurrentTime(),
changeMedia = function () {
self._resetPlayer();
self._deferredPlayerInteraction = true;
self._playChannel(self._lastChannelId);
application.hideLoader(2000);
};
if (this._bingewatchActive && !skipBingewatch) {
return;
}
if (this._view.isContentExpanded()) {
this._view.closeExpandedContents();
}
if (!skipBingewatch
&& this._bingewatch
&& this._bingewatch.length
&& percentage > 95
&& !this._bingewatchActive) {
this._bingewatchActive = true;
view.hideUI({
skipAnim: true
});
application.route('bingewatching', {
episode: this._bingewatch.splice(0, 1)[0],
successCallback: Utils.bind(this._onBingeWatch, this),
cancelCallback: Utils.bind(this._onBack, this, true)
});
return;
}
this._bingewatchActive = false;
application.showLoader(true, true);
GA.onEvent('Action', EVENT.stopVideo, {eventLabel: self._GATypeLabel});
if (view.isUIVisible()) {
view.hideUI({
skipAnim: true
});
view.hideWarningBox(true);
}
if (this._callingPage === 'detail' || this._callingPage === 'movies') {
if (main.getContent()) {
this.setStyleTo('display', 'none');
main.setStyleTo('display', 'block');
application.showMenu(this._callingPage, false, true);
if (this._callingPage === 'detail') {
this._callingPage = null;
detail.setStyleTo('display', 'block');
detail.focus();
} else {
main.focus();
}
} else {
application.focusMenu('player');
}
if (this._type === TYPES.RECORDING || this._type === TYPES.VOD || this._type === TYPES.VOD_MOVIE) {
if (this._mediaChangeInProgress) {
this._afterMediaChangeStuff = changeMedia;
} else {
changeMedia();
}
} else {
application.hideLoader();
}
} else if (this._callingPage === 'landing') {
this._callingPage = null;
if (this._seeker.isActive()) {
this._seeker.cancel();
}
this._removeEventListeners();
landingPage.togglePlayers(false);
} else if (this._callingPage === 'channelList') {
if (this._lastChannelId) {
this.routeToPage('channellist', this._lastChannelId);
}
this._callingPage = null;
this.setStyleTo('display', 'none');
main.setStyleTo('display', 'block');
main.focus();
} else {
application.focusMenu('player');
application.hideLoader();
}
},
/**
* Shorthand for resetting the device mediaplayer.
*
* @private
*/
_resetMediaPlayer: function () {
var mediaPlayerState = mediaPlayer.getState();
if (mediaPlayerState !== MediaPlayer.STATE.STOPPED
&& mediaPlayerState !== MediaPlayer.STATE.EMPTY
&& mediaPlayerState !== MediaPlayer.STATE.ERROR) {
mediaPlayer.stop();
}
mediaPlayerState = mediaPlayer.getState();
if (mediaPlayerState !== MediaPlayer.STATE.EMPTY) {
mediaPlayer.reset();
}
},
/**
* Playback issue message displayer.
*
* @private
*/
_onPlaybackIssueMessage: function () {
this._view.showWarningBox({
icon: 'icon-alert-v2',
text: l10n.get('player.warningbox.cantplay')
});
GA.onEvent('playback_failed', 'player');
},
/**
* Bingewatch callback.
*
* @param {Object} episode - The episode to watch.
* @private
*/
_onBingeWatch: function (episode) {
application.broadcastEvent(new BingeWatchEvent(episode));
this._bingewatchActive = false;
this._view.closeExpandedContents();
this.onBeforeShow({
args: {
data: episode,
bingewatch: this._bingewatch,
callingPage: this._callingPage,
analyticsType: this._analyticsType,
type: this._type
}
});
},
/**
* Attempts to play the next episode.
*
* @param {Object} episode - The episode to watch.
**/
_playNextEpisode: function (episode) {
application.broadcastEvent(new NextEpisodeEvent(episode));
this.onBeforeShow({
args: {
data: episode,
bingewatch: this._nextEpisodes,
callingPage: this._callingPage,
analyticsType: this._analyticsType,
type: this._type
}
});
},
/**
* Reactivates the player.
*/
reactivate: function () {
var view = this._view;
view.enablePointerSupport();
view.focus();
this.routeToPage('home');
view.removeClass('warm-welcome');
this._playChannel();
},
/**
* On parental control event changed.
*
* @param {Object} e - Parental control event.
* @param {boolean} e.status - Parental control status (on or off).
*/
_onParentalControlChangedEvent: function (e) {
var parentalControlOn = e.status;
if (parentalControlOn && this._program && this._program.isLocked()) {
if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
mediaPlayer.pause();
}
this._lastChannelId = null;
this._triggerParentalOnFocus = parentalControlOn;
}
},
/**
* Requests and sets the content's detail info.
*
* @private
*/
_setDetailInfo: function () {
api.read('detail', {
params: {
endpoint: this._program.getDetailsAction()
},
withCredentials: true
}).then(Utils.bind(this._onInfo, this, true));
}
});
return PlayerComponent;
});