define('application/models/broadcast', [
'antie/class',
'application/managers/channel',
'application/managers/api',
'antie/runtimecontext',
'rofl/lib/utils',
'application/models/relatedcontent',
'application/managers/session',
'rofl/lib/l10n',
'antie/storageprovider',
'application/managers/feature'
], function (
Abstract,
ChannelManager,
ApiManager,
RuntimeContext,
Utils,
RelatedContent,
SessionManager,
L10N,
StorageProvider,
FeatureManager
) {
'use strict';
var channelManager = ChannelManager.getInstance(),
app = RuntimeContext.getCurrentApplication(),
imageFormat = app.getLayout().imageFormat,
landscapeFormat = imageFormat.landscape,
getDetailsAction = function (actions) {
var i,
j,
detailsAction;
for (i = 0, j = actions.length; i < j; i++) {
if (actions[i].targetType === 'DETAILS_PAGE') {
detailsAction = actions[i].uri;
break;
}
}
return detailsAction;
},
l10n = L10N.getInstance(),
CONTENT_OPTIONS = {
TRICKPLAY: 'TRICKPLAY',
STARTOVER: 'STARTOVER',
CATCHUP: 'CATCHUP',
RECORDABLE: 'RECORDABLE'
},
Broadcast,
imageEndpoint;
Broadcast = Abstract.extend({
/**
* Initialises the search result model from API response object.
*
* @param {Object} response - API search result object.
*/
init: function (response) {
var metadata = response.metadata;
if (metadata.liveContentId) {
this._id = parseInt(metadata.liveContentId);
this._recordingId = parseInt(metadata.contentId);
} else {
this._id = parseInt(response.id);
}
this._title = metadata.title;
this._titleBrief = metadata.titleBrief;
this._imageUrl = metadata.pictureUrl;
this._channelId = metadata.channelId || Utils.getNested(response, 'channel', 'channelId');
this._assetId = this._getMasterAssetId(response.assets);
if (metadata.airingStartTime) {
this._startTime = (metadata.airingStartTime / 1000);
this._endTime = metadata.airingEndTime / 1000;
this._streamOffset = 5 * 60; // Hardcode the offset to 5 minutes.
} else if (metadata.promoStartTime) {
this._startTime = (metadata.promoStartTime / 1000);
this._endTime = metadata.promoEndTime / 1000;
this._streamOffset = 0;
} else {
this._startTime = (metadata.programStartTime / 1000);
this._endTime = (metadata.programStartTime / 1000) + metadata.programDuration;
this._streamOffset = (metadata.programStartTime - metadata.recordingStartTime) / 1000;
}
this._assets = response.assets;
this._duration = metadata.programDuration || metadata.duration;
this._catchupDuration = this._recordingDuration = metadata.duration || metadata.programDuration;
this._videoRatings = metadata.pcExtendedRatings;
this._detailsAction = getDetailsAction(response.actions || []);
this._description = metadata.longDescription || '';
this._ageRating = 'PC-' + metadata.pcLevel;
this._genres = metadata.genres || [];
this._contentOptions = metadata.contentOptions;
this._programType = metadata.programType || null;
this._episodeNumber = metadata.episodeNumber;
this._seriesId = parseInt(metadata.seriesId);
this._seriesTitle = metadata.seriesTitle;
this._season = metadata.season;
this._contentType = metadata.contentType;
this._contentSubtype = metadata.contentSubtype;
this._startDeltaTime = metadata.startDeltaTime || 0;
this._pcLevel = parseInt(metadata.pcLevel);
this._parentalGenres = metadata.pcExtendedRatings.length ? metadata.pcExtendedRatings : [];
this._parentalWhitelisted = false;
this._year = metadata.year;
this._actors = metadata.actors;
this._directors = metadata.directors;
this._seasons = metadata.seasons;
this._rights = '';
this._imdbRating = metadata.imdbRating || null;
if (this._pcLevel === 99) {
this._pcLevel = 0;
}
if (this._seriesId) {
this._relatedContent = new RelatedContent(this);
}
if (this.isVOD() || this.isBundles()) {
imageEndpoint = ApiManager.getImageAPI('vod/');
} else {
imageEndpoint = ApiManager.getImageAPI('epg/');
}
this._imageEndpoint = imageEndpoint += '{{imageId}}/{{size}}.jpg{{query}}';
},
/**
* Returns PC level.
*
* @returns {number} - PC level.
*/
getPCLevel: function () {
return this._pcLevel;
},
/**
* Returns parental genres.
*
* @returns {Array} - Parental genres.
*/
getParentalGenres: function () {
return this._parentalGenres;
},
/**
* Sets program to be parental whitelisted.
*/
setParentalWhitelisted: function () {
this._parentalWhitelisted = true;
},
/**
* Gets if program is whitelisted.
*
* @returns {boolean} - If is whitelisted.
*/
getParentalWhitelisted: function () {
return this._parentalWhitelisted;
},
/**
* Returns the duration.
*
* @returns {number} - The duration.
*/
getDuration: function () {
return this._duration;
},
/**
* Returns the duration (catchup duration contains +5min at beginning and +15min at the end).
*
* @returns {number} - The duration.
*/
getCatchupDuration: function () {
return this._catchupDuration;
},
/**
* Returns the duration.
*
* @returns {number} - The duration.
*/
getRecordingDuration: function () {
return this._recordingDuration;
},
/**
* Returns the start time.
*
* @returns {Date} - The start time.
*/
getStartTime: function () {
return this._startTime;
},
/**
* Returns the end time.
*
* @returns {Date} - The end time.
*/
getEndTime: function () {
return this._endTime;
},
/**
* Return id of the broadcast.
*
* @returns {string} - The id.
*/
getId: function () {
return this._id;
},
/**
* Returns title of the broadcast.
*
* @returns {string} - The title.
*/
getTitle: function () {
return this._title;
},
/**
* Returns the title brief (short title).
*
* @returns {string} - The title brief.
*/
getTitleBrief: function () {
return this._titleBrief;
},
/**
* Returns URL of preview image.
*
* @param {string} size - Size of the image.
* @returns {string} - URL of preview image.
*/
getImageUrl: function (size) {
var query = '';
size = size || Math.round(landscapeFormat.width) + 'x' + Math.round(landscapeFormat.height);
if (this.isLocked()) {
query += '?blurred=true';
}
return Utils.formatTemplate(this._imageEndpoint, {
imageId: this._imageUrl,
size: size,
query: query
});
},
/**
* Returns the background.
*
* @param {string} orientation - Orientation.
* @param {Object} [dimensions] - The dimensions. Should contain width and height.
* @returns {string} - The background url.
* @private
*/
getImage: function (orientation, dimensions) {
var format,
size,
query = '';
orientation = orientation || 'landscape'; // Default to landscape image.
if (orientation === 'manual') {
size = dimensions.width + 'x' + dimensions.height;
} else {
format = imageFormat[orientation];
size = Math.round(format.width) + 'x' + Math.round(format.height);
}
if (this.isLocked()) {
query += '?blurred=true';
}
return Utils.formatTemplate(this._imageEndpoint, {
imageId: this._imageUrl,
size: size,
query: query
});
},
/**
* Returns channel ID.
*
* @returns {number} - ID of channel.
*/
getChannelId: function () {
return this._channelId;
},
/**
* Returns the asset id.
*
* @returns {number} - The asset id.
*/
getAssetId: function () {
return this._assetId;
},
/**
* Returns the channel logo.
*
* @returns {string} - The channel logo.
*/
getChannelLogo: function () {
var channel = this.getChannelId() && channelManager.getChannelById(this.getChannelId());
return channel ? channel.getImage('128') : '';
},
/**
* Returns the video ratings.
*
* @returns {Array} - The video ratings.
*/
getVideoRatings: function () {
return this._videoRatings;
},
/**
* Returns the details action.
*
* @returns {string} - The details action url.
*/
getDetailsAction: function () {
return this._detailsAction;
},
/**
* Returns the description.
*
* @returns {string} - The description.
*/
getDescription: function () {
return this._description;
},
/**
* Returns the age rating.
*
* @returns {string} - The age rating.
*/
getAgeRating: function () {
return this._ageRating;
},
/**
* Returns the genres.
*
* @returns {Array} - The genres.
*/
getGenres: function () {
return this._genres;
},
/**
* Returns the episode number.
*
* @returns {number} - The episode number.
*/
getEpisodeNumber: function () {
return this._episodeNumber;
},
/**
* Returns true if the video can be played.
*
* @returns {boolean} - True if the video can be played.
*/
canPlay: function () {
var assets;
if (this.isLive()) {
// Program is currently playing and can be restarted.
return true;
} else if (this.isVOD()) {
return true;
} else if (this.isReplayItem()) {
if (this.getContentType() === 'RECORDING') {
assets = this.getAssets();
if (assets[0] && assets[0].status) {
return assets[0].status === 'RecordSuccess';
}
return false;
}
// Program is finished and can be caught up.
return this.canCatchup();
}
// Program is in the future and can't be watched.
return false;
},
/**
* Returns true if the item is a replay item.
*
* @returns {boolean} - True if the item is a replay item.
*/
isReplayItem: function () {
var now = app.getDate() / 1000;
return this.getEndTime() < now;
},
/**
* Returns true if the video is currently playing.
*
* @returns {boolean} - True if the video is currently playing.
*/
isLive: function () {
var now = app.getDate() / 1000;
return this.getStartTime() < now && this.getEndTime() > now;
},
/**
* Returns true if the video is type VOD.
*
* @returns {boolean} - True if the video is type VOD.
*/
isVOD: function () {
return this._contentType === 'VOD';
},
/**
* Returns true if the video is subtype EPISODE.
*
* @returns {boolean} - True if the video is subtype EPISODE.
*/
isVodEpisode: function () {
return this._contentSubtype === 'EPISODE';
},
/**
* Returns true if the content is type group of bundles.
*
* @returns {boolean} - True if the content is type group of bundles.
*/
isBundles: function () {
return this._contentType === 'GROUP_OF_BUNDLES';
},
/**
* Returns true if the asset can be live restarted.
*
* @returns {boolean} - True if can be live restarted.
* @private
*/
_canRestartLive: function () {
return this._contentOptions.indexOf(CONTENT_OPTIONS.STARTOVER) >= 0;
},
/**
* Returns true if the video can be caught up.
*
* @returns {boolean} - True if can be caught up.
* @private
*/
canCatchup: function () {
if (this._contentType === 'RECORDING') {
return this._assets.length > 0;
}
return this._contentOptions.indexOf(CONTENT_OPTIONS.CATCHUP) >= 0;
},
/**
* Returns true if seeking is allowed.
*
* @returns {boolean} - True if seeking is allowed.
*/
canSeek: function () {
return this.isVOD() ? true : this._contentOptions.indexOf(CONTENT_OPTIONS.TRICKPLAY) >= 0;
},
/**
* Returns true if recording is allowed.
*
* @returns {boolean} - True if recording is allowed.
*/
canRecord: function () {
return this._contentOptions.indexOf(CONTENT_OPTIONS.RECORDABLE) >= 0;
},
/** Returns the program type.
*
* @returns {string} - The program type.
*/
getProgramType: function () {
return this._programType;
},
/**
* Returns the related content model.
*
* @returns {Object} - The related content model.
*/
getRelatedContent: function () {
return this._relatedContent;
},
/**
* Returns the array with assets.
*
* @returns {Array} - An array with {{assetId: number, assetType: string}[]|*}.
*/
getAssets: function () {
return this._assets;
},
/**
* Returns a series id.
*
* @returns {number} - The series id.
*/
getSeriesId: function () {
return this._seriesId;
},
/**
* Returns the series title.
*
* @returns {string} - The series title.
*/
getSeriesTitle: function () {
return this._seriesTitle;
},
/**
* Returns the episode's season.
*
* @returns {string} - The episode's season.
*/
getSeason: function () {
return this._season;
},
/**
* Returns the recording id.
*
* @returns {string} - The recording id.
*/
getRecordingId: function () {
return this._recordingId;
},
/**
* Returns the start delta time.
*
* @returns {number} - The start delta time.
*/
getStartDeltaTime: function () {
return this._startDeltaTime;
},
/**
* Returns the content type.
*
* @returns {string} - The content type.
*/
getContentType: function () {
return this._contentType;
},
/**
* Returns the content subtype.
*
* @returns {string} - The content subtype.
*/
getContentSubtype: function () {
return this._contentSubtype;
},
/**
* Returns the year.
*
* @returns {string} - The year.
*/
getYear: function () {
return this._year;
},
/**
* Returns true if the content type is a recording.
*
* @returns {boolean} - Is recording.
*/
isRecording: function () {
return this._contentType === 'RECORDING';
},
/**
* Returns true if broadcast is in the future.
*
* @returns {boolean} - Is future item.
*/
isFutureItem: function () {
return this.getStartTime() && new Date(this.getStartTime() * 1000) > app.getDate();
},
/**
* Gets the master asset ID.
*
* @param {Array} [assets] - The assets available for the specific channel.
* @returns {number} - The master asset ID.
* @private
*/
_getMasterAssetId: function (assets) {
var assetId,
i;
for (i = 0; i < assets.length; i++) {
if (assets[i].assetType === 'MASTER') {
if (assets[i].programType) {
if (assets[i].programType === 'CUTV') {
assetId = assets[i].assetId;
break;
}
} else {
assetId = assets[i].assetId;
break;
}
}
}
return assetId;
},
/**
* Returns the recording starttime.
*
* @returns {number|null} - The stream offset.
*/
getStreamOffset: function () {
return this._streamOffset;
},
/**
* Returns true if the broadcast is locked.
*
* @returns {boolean} - True if the broadcast is locked.
*/
isLocked: function () {
var parentalControlParams = SessionManager.getInstance().getUserPCParams(),
parentalControlOn = SessionManager.getInstance().isParentalOn(),
parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalControlLevel')),
parentalControlGenres = Utils.getNested(parentalControlParams, 'parentalGenres'),
i,
self = this,
isGenreProtected = function () {
if (parentalControlGenres.length) {
for (i = 0; i < parentalControlGenres.length; i++) {
if (self.getParentalGenres().indexOf(parentalControlGenres[i]) > -1) {
return true;
}
}
}
return false;
},
isLocked = false,
VODContentTypes = ['VOD', 'GROUP_OF_BUNDLES', 'BUNDLE'];
if (VODContentTypes.indexOf(this.getContentType()) >= 0) {
parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalVODControlLevel'));
}
if (!FeatureManager.getInstance().isParentalEnabled() || !parentalControlOn) {
isLocked = false;
} else {
isLocked = !(parentalControlLevel >= this.getPCLevel()) || isGenreProtected();
}
return isLocked;
},
/**
* Returns true if the stream requires to be requested with pin.
*
* @returns {boolean} - True if stream requires pin.
*/
streamRequiresPin: function () {
var parentalControlParams = SessionManager.getInstance().getUserPCParams(),
parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalControlLevel')),
parentalControlGenres = Utils.getNested(parentalControlParams, 'parentalGenres'),
genres = this.getParentalGenres(),
isGenreProtected,
VODContentTypes = ['VOD', 'GROUP_OF_BUNDLES', 'BUNDLE'];
if (VODContentTypes.indexOf(this.getContentType()) >= 0) {
parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalVODControlLevel'));
}
isGenreProtected = function () {
var i;
if (parentalControlGenres.length) {
for (i = 0; i < parentalControlGenres.length; i++) {
if (genres.indexOf(parentalControlGenres[i]) > -1) {
return true;
}
}
}
return false;
};
return !(parentalControlLevel >= this.getPCLevel()) || isGenreProtected();
},
/**
* Returns true if the item is playable.
*
* @returns {boolean} - True if the item is playable.
*/
isPlayable: function () {
var now = app.getDate().getTime(),
endTime = this.getEndTime() * 1000,
replayBuffer = 15 * 60 * 1000;
return (now - endTime > replayBuffer);
},
/**
* Returns the formatted duration text.
*
* @returns {string} - The formatted duration text.
*/
getFormattedDuration: function () {
var hours,
duration,
durationText,
minutes;
if (!this._formattedDuration) {
duration = this.getDuration();
durationText = '';
hours = Math.floor(duration / 3600);
duration = duration - hours * 3600;
minutes = Math.floor(duration / 60);
if (hours) {
durationText += hours + l10n.get('hours') + ' ';
}
if (minutes) {
durationText += minutes + l10n.get('minutes');
}
this._formattedDuration = durationText;
}
return this._formattedDuration;
},
/**
* Returns the actors.
*
* @returns {Array} - The actors.
*/
getActors: function () {
if (this._directors && this._directors.length) {
return this._actors.slice(0, 4);
}
return this._actors.slice(0, 5);
},
/**
* Returns the directors.
*
* @returns {Array} - The directors.
*/
getDirectors: function () {
if (this._actors && this._actors.length) {
if (this._actors.length <= 3) {
return this._directors.slice(0, 2);
}
return this._directors.slice(0, 1);
}
return this._directors.slice(0, 5);
},
/**
* Check if the item's channel is found in the channel list.
* Channels with no right have already been filtered out on live channels request.
*
* @returns {boolean} True if has rights to watch (channel not found) false if has no rights to watch.
*/
hasChannelRights: function () {
return !!channelManager.getChannelById(this.getChannelId());
},
/**
* Returns the rights of the asset.
*
* @returns {string} - The rights type. ie 'watch', 'buy'.
*/
getRights: function () {
return this._rights;
},
/**
* Sets the rights obtained from user data for this content.
*
* @param {string} rights - The rights for this content.
*/
setRights: function (rights) {
this._rights = rights;
},
/**
* Returns the user rating scale of the content. (IMDB).
*
* @returns {number|null} - The user rating scale.
*/
getImdbRating: function () {
return this._imdbRating;
}
});
return Broadcast;
});