Source: components/players/vod.js

define('application/components/players/vod', [
    'application/components/player',
    'application/decorators/player/interfaces/playerinterface',
    'application/helpers/playerproperties',
    'application/helpers/drmconfig',
    'application/views/player/vod',
    'rofl/lib/utils',
    'application/utils',
    'application/managers/api',
    'application/constants',
    'antie/runtimecontext',
    'application/events/bingewatchevent',
    'application/events/nextepisodeevent',
    'product-layer/player/widgets/seeker',
    'application/managers/player'
], function (
    Component,
    AppCorePlaybackInterface,
    PlayerProperties,
    DRMConfig,
    VODView,
    Utils,
    AppUtils,
    ApiManager,
    Constants,
    RuntimeContext,
    BingeWatchEvent,
    NextEpisodeEvent,
    Seeker,
    PlayerManager
) {
    'use strict';

    var api = ApiManager.getKPNAPI(),
        application = RuntimeContext.getCurrentApplication(),
        configuration = application.getConfiguration();

    return Component.extend({

        /**
         * Initializes component.
         */
        init: function init () {
            init.base.call(this);
        },

        /**
         * BeforeShow event.
         *
         * @param {Object} e - The event data.
         */
        onBeforeShow: function onBeforeShow (e) {
            var args = e.args;

            this._bingewatch = this._nextEpisodes = args.bingewatch || [];

            onBeforeShow.base.call(this, e);
        },

        /**
         * Sets the view for the player.
         *
         * @private
         */
        _setView: function () {
            this._view = new VODView({
                mouseOverHandler: Utils.bind(function () {
                    if (this._view.supportsPointer()) {
                        this._showUI();
                    }
                }, this)
            });

            this.appendChildWidget(this._view);
        },

        /**
         * Prepares playback: Checks parental or start requesting bookmarks and program data to request and play stream.
         *
         * @param {Object} playbackData - The playback content data.
         * @private
         */
        _preparePlayback: function (playbackData) {

            // This will set program with 0 startTime, after receving the bookmark the position is set to it.
            this._setProgram(playbackData.data);

            if (AppUtils.isBroadcastLocked(playbackData.data)) {
                application.hideLoader();
                this._checkingParental = true;

                this.playerInterface.unload();
                this._playbackStatus.active = false;
                this._view.hideUI();

                if (this.isFocussed()) {
                    this._showParental({
                        successCallback: Utils.bind(this._onParentalSuccess, this, playbackData),
                        errorCallback: Utils.bind(this._onPlayerError, this),
                        escapeCallback: Utils.bind(this._onBack, this)
                    });
                }
            } else {
                this._requestBookmark(playbackData)
                    .then(Utils.bind(this._setProgram, this, playbackData.data))
                    .then(Utils.bind(this._requestStream, this, playbackData))
                    .then(Utils.bind(this._startPlayback, this))
                    ['catch'](Utils.bind(this._onPlayerError, this));
            }
        },

        /**
         * Requests bookmark data for the given playback content data.
         *
         * @param {Object} playbackData - The playback content data.
         * @returns {Promise} - The userdata resolved promise.
         * @private
         */
        _requestBookmark: function (playbackData) {
            return api.read('userdata', {
                params: playbackData,
                withCredentials: true
            })
                .then(Utils.bind(this._onRequestBookmark, this, playbackData));
        },

        /**
         * Sets the bookmark.
         *
         * @param {Object} playbackData - The playback content data.
         * @param {Object} data - Bookmark data.
         * @private
         */
        _onRequestBookmark: function (playbackData, data) {

            // Check for empty assetId and set bookmarkTime.
            playbackData.data = AppUtils.checkForEmptyAsset(playbackData.data, data);
            this._bookmarkTime = this.bookmarkManager.parsedBookmarkTime(data);
        },

        /**
         * Requests the content's stream data.
         *
         * @param {Object} playbackData - Playback content data.
         * @returns {Object} - Stream data.
         */
        _requestStream: function (playbackData) {
            var program = playbackData.data;

            return api.read('streams/vod', {
                params: playbackData,
                withCredentials: true,
                headers: this.getStreamHeaders(program)
            });
        },

        /**
         * Prepares the player properties and attempts to start playback.
         *
         * @param {Object} data - Contains the stream data.
         * @private
         */
        _startPlayback: function _startPlayback (data) {
            var properties = new PlayerProperties({
                source: {
                    src: data.url,
                    mimeType: data.mimeType || PlayerProperties.MIME_TYPES.mpd
                },
                async: true,
                autoplay: true,
                startTime: this._startTime,
                drmConfig: new DRMConfig({
                    type: DRMConfig.TYPES.PLAYREADY,
                    options: {
                        licenseServer: data.licenseUrl
                    }
                })
            });

            _startPlayback.base.call(this, properties);

            this._bingewatchActive = false;
        },

        /**
         * Requests the content's stream data.
         *
         * @param {Object} program - The requested program.
         */
        _setProgram: function (program) {
            this._program = program;
            this._startTime = this._bookmarkTime || 0;
            this._endTime = new Date(program.getEndTime() * 1000);
            this._contentId = program.getId();
            this._type = AppUtils.getContentVideoType(program.getContentType(), program.getContentSubtype());

            this._view.setProgramInfoButton(program.getDetailsAction());
            this._view.setNextEpisodeButton(!!this._nextEpisodes.length);
            this._view.setProgram(program);
            this._setProgress({
                duration: program.getDuration(),
                currentTime: this._startTime,
                percentage: (this._startTime / program.getDuration()) * 100
            });
            this._buildSeeker();
        },

        /**
         * Parental pin success callback.
         *
         * @param {Object} playbackData - Playback content data.
         * @private
         */
        _onParentalSuccess: function (playbackData) {

            // Update Program info.
            this._preparePlayback(playbackData);
        },

        /**
         * On before hide event.
         */
        onBeforeHide: function () {
            this.playerInterface.unload();
        },

        /**
         * Select event.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onSelect: function _onSelect (e) {
            var target = e.target,
                episode;

            if (this._view.isUIVisible()) {

                switch (target.id) {
                    case VODView.CONTROLS.INFO:
                    case VODView.CONTROLS.MENU:
                    case VODView.CONTROLS.PLAYPAUSE:
                    case VODView.CONTROLS.REWIND:
                    case VODView.CONTROLS.FORWARD:
                    case VODView.CONTROLS.RESTART:

                        // Falls through common actions
                        _onSelect.base.call(this, e);
                        break;

                    case VODView.CONTROLS.NEXT:
                        if (!this._mediaChangeInProgress) {
                            episode = this._nextEpisodes.splice(0, 1)[0];

                            application.broadcastEvent(new NextEpisodeEvent(episode));
                            this._onNextEpisode(episode);
                        }
                        this._view.closeExpandedContents();
                        break;

                    case VODView.CONTROLS.BACK:
                        this._view.closeExpandedContents();
                        this._onBack();
                        break;

                    case VODView.CONTROLS.LIVE:
                        this._onLive();
                        break;
                }
            } else {

                this._view.showUI();
            }
        },

        /**
         * Launches bingewatching overlay.
         */
        _launchBingeWatch: function () {
            this._bingewatchActive = true;
            this._view.hideUI({
                skipAnim: true
            });

            application.route('bingewatching', {
                episode: this._bingewatch.splice(0, 1)[0],
                successCallback: Utils.bind(this._onBingeWatch, this),
                cancelCallback: Utils.bind(this._onCancelBingeWatch, this)
            });
        },

        /**
         * Bingewatch callback.
         *
         * @param {Object} episode - The episode to watch.
         * @private
         */
        _onBingeWatch: function (episode) {
            application.broadcastEvent(new BingeWatchEvent(episode));
            this._view.closeExpandedContents();
            application.showLoader(true);
            this._preparePlayer({
                data: episode,
                bingewatch: this._bingewatch,
                callingPage: this._callingPage,
                analyticsType: this._analyticsType,
                type: this._type
            });
        },

        /**
         * Cancels bingewatch callback.
         *
         * @private
         */
        _onCancelBingeWatch: function () {
            this._onBack();
        },

        /**
         * PlayerEvent.
         *
         * @param {Object} e - The player event data.
         * @private
         */
        onPlayerEvent: function onPlayerEvent (e) {
            switch (e.type) {
                case this.MEDIA_PLAYER_EVENTS.ERROR:
                case this.MEDIA_PLAYER_EVENTS.BITRATE_CHANGED:

                    onPlayerEvent.base.call(this, e);
                    break;

                case this.MEDIA_PLAYER_EVENTS.STATUS:
                    if (this._currentProgram === this._program) {
                        this._setProgress(e);
                        application.hideLoader();

                        if (!this._playbackStatus.active) {
                            if (!this._bingewatchActive) {
                                this._view.showUI();
                            }
                            this._playbackStatus.active = true;
                        }
                    }
                    break;

                case this.MEDIA_PLAYER_EVENTS.PLAYING:
                    this.bookmarkManager.onPlayStart(this._contentId, this._type);
                    onPlayerEvent.base.call(this, e);
                    break;

                case this.MEDIA_PLAYER_EVENTS.PAUSED:
                    onPlayerEvent.base.call(this, e);
                    break;

                case this.MEDIA_PLAYER_EVENTS.COMPLETE:
                    if (!this._bingewatchActive) {
                        this._onBack();
                    }
                    break;
            }
        },

        /**
         * 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
         */
        _onMouseSeek: function (e) {

            if (this.playerInterface.isPlaying()) {
                this.playerInterface.pause();
            }

            if (e.finished) {
                this.playerInterface.seek((this.playerInterface.getDuration() / 100) * e.percentage);
            }
        },

        /**
         * Updates the progress view and checks for bingewatching activation.
         *
         * @param {Object} e - Object that contains a player event..
         * @private
         */
        _setProgress: function (e) {
            var duration = Math.floor(e.duration),
                currentTime = Math.floor(e.currentTime),
                percentage = (e.currentTime / e.duration) * 100;

            this._view.setProgress({
                duration: duration,
                currentTime: currentTime,
                percentage: percentage
            });

            if (this._bingewatch.length
                && !this._bingewatchActive
                && currentTime >= duration - 5) {
                this._launchBingeWatch();
            }
        },

        /**
         *
         *
         * @param {number} time - The current seek time.
         * @private
         */
        _onSeekCurrentTime: function (time) {
            var duration = this.playerInterface.getDuration(),
                maxTime = PlayerManager.getMaxSeekValue();

            this._seekCurrentTime = time;

            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._setProgress({
                currentTime: time,
                duration: duration
            });
        },

        /**
         * Gets executed when the seek is confirmed.
         *
         * @private
         */
        _onSeek: function () {
            this._seekSpeed = null;
            this._seekCurrentTime = null;

            this._view.resetControls();
            this._view.setProgressMaxPointerPosition();
        },

        /**
         * Checks the bookmark threshold so bookmark can be stored.
         */
        storeBookmark: function () {
            var currentTime = this.playerInterface.getCurrentTime(),
                duration = this.playerInterface.getDuration();

            if (currentTime / duration < configuration.BOOKMARK_THRESHOLD) {
                this.bookmarkManager.onStop(null, currentTime);
            } else {
                this.bookmarkManager.onStop(null, 0);
            }
        },

        /**
         * On back action.
         *
         * @private
         */
        _onBack: function _onBack () {
            this._view.hideUI({
                skipAnim: true
            });
            this._view.resetControls();
            this.storeBookmark();
            this.playerInterface.destroy();
            _onBack.base.call(this);
            this._playbackStatus.active = false;
            this._bingewatchActive = false;
        },

        /**
         * Close player.
         */
        closePlayer: function closePlayer () {
            this._view.hideUI({
                skipAnim: true
            });
            this._view.resetControls();
            this.storeBookmark();
            this.playerInterface.destroy();
            closePlayer.base.call(this);
            this._playbackStatus.active = false;
            this._bingewatchActive = false;
        }
    });
});