src/app/modules/player-helper/components/player/player.component.ts
OnInit
AfterViewInit
OnChanges
OnDestroy
selector | app-player |
styleUrls | ./player.component.scss |
templateUrl | ./player.component.html |
constructor(configService: ConfigService, router: Router, toasterService: ToasterService, resourceService: ResourceService, navigationHelperService: NavigationHelperService, deviceDetectorService: DeviceDetectorService, userService: UserService, formService: FormService, contentUtilsServiceService: ContentUtilsServiceService, contentService: ContentService, cdr: ChangeDetectorRef, playerService: PublicPlayerService, utilService: UtilService)
|
||||||||||||||||||||||||||||||||||||||||||
Parameters :
|
contentData | |
Type : any
|
|
contentProgressEvents$ | |
Type : Subject<any>
|
|
isContentDeleted | |
Type : Subject<any>
|
|
isSingleContent | |
Type : boolean
|
|
overlayImagePath | |
Type : string
|
|
pageId | |
Type : string
|
|
playerConfig | |
Type : PlayerConfig
|
|
playerOption | |
Type : any
|
|
telemetryObject | |
Type : literal type
|
|
assessmentEvents | |
Type : EventEmitter
|
|
closePlayerEvent | |
Type : EventEmitter
|
|
playerOnDestroyEvent | |
Type : EventEmitter
|
|
questionScoreReviewEvents | |
Type : EventEmitter
|
|
questionScoreSubmitEvents | |
Type : EventEmitter
|
|
ratingPopupClose | |
Type : EventEmitter
|
|
sceneChangeEvent | |
Type : EventEmitter
|
|
selfAssessLastAttempt | |
Type : EventEmitter
|
|
window:orientationchange |
Arguments : '$event'
|
window:orientationchange()
|
window:popstate |
Arguments : '$event'
|
window:popstate(event)
|
Public addUserDataToContext |
addUserDataToContext()
|
Returns :
void
|
adjustPlayerHeight |
adjustPlayerHeight()
|
Adjust player height after load
Returns :
void
|
checkForQumlPlayer |
checkForQumlPlayer()
|
Returns :
void
|
closeContentFullScreen |
closeContentFullScreen()
|
Returns :
void
|
closeFullscreen |
closeFullscreen()
|
when user clicks on close button this method will let the player to exit from fullscreen mode and
Returns :
void
|
closeModal |
closeModal()
|
Returns :
void
|
emitSceneChangeEvent | ||||||||
emitSceneChangeEvent(timer: number)
|
||||||||
Parameters :
Returns :
void
|
enablePlayer | ||||||
enablePlayer(mode: boolean)
|
||||||
this method will handle play button click and turn the player into landscape
Parameters :
Returns :
void
|
eventHandler | ||||
eventHandler(event)
|
||||
Parameters :
Returns :
void
|
focusOnReplay |
focusOnReplay()
|
Returns :
void
|
generateContentReadEvent | |||||||||
generateContentReadEvent(event: any, newPlayerEvent?)
|
|||||||||
Parameters :
Returns :
void
|
generatelimitedAttemptEvent | ||||
generatelimitedAttemptEvent(event)
|
||||
Parameters :
Returns :
void
|
generateScoreSubmitEvent | ||||||
generateScoreSubmitEvent(event: any)
|
||||||
Parameters :
Returns :
void
|
Public handleOrientationChange |
handleOrientationChange()
|
Decorators :
@HostListener('window:orientationchange', ['$event'])
|
Returns :
void
|
loadCdnPlayer |
loadCdnPlayer()
|
Returns :
void
|
loadDefaultPlayer | ||||||
loadDefaultPlayer(url)
|
||||||
Parameters :
Returns :
void
|
loadNewPlayer |
loadNewPlayer()
|
Returns :
void
|
loadOldPlayer |
loadOldPlayer()
|
Returns :
void
|
loadPlayer |
loadPlayer()
|
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
loadPlayer method will be called
Returns :
void
|
ngOnChanges | ||||
ngOnChanges(changes)
|
||||
Parameters :
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onPopState | ||||
onPopState(event)
|
||||
Decorators :
@HostListener('window:popstate', ['$event'])
|
||||
Parameters :
Returns :
void
|
rotatePlayer |
rotatePlayer()
|
this method checks for the browser capability to be fullscreen via if-else ladder if match found, it will turn the player along will be close button into fullscreen and then rotate it to landscape mode
Returns :
void
|
setTelemetryData |
setTelemetryData()
|
Returns :
void
|
showRatingPopup | ||||
showRatingPopup(event)
|
||||
Parameters :
Returns :
void
|
updateMetadataForDesktop |
updateMetadataForDesktop()
|
Returns :
void
|
buildNumber |
Type : string
|
closeButtonInteractEdata |
Type : IInteractEventEdata
|
collectionId |
Type : string
|
Public configService |
Type : ConfigService
|
CONSTANT |
Type : object
|
Default value : {
ACCESSEVENT: 'renderer:question:submitscore',
ISLASTATTEMPT: 'renderer:selfassess:lastattempt',
MAXATTEMPT: 'renderer:maxLimitExceeded',
ACCESSREVIEWEVENT: 'renderer:question:reviewAssessment'
}
|
contentDeleted |
Default value : false
|
contentId |
Type : string
|
contentIframe |
Type : ElementRef
|
Decorators :
@ViewChild('contentIframe')
|
contentRatingModal |
Default value : false
|
Public contentUtilsServiceService |
Type : ContentUtilsServiceService
|
Public formService |
Type : FormService
|
isCdnWorking |
Type : string
|
isDesktopApp |
Default value : false
|
isFullScreenView |
Default value : false
|
isMobileOrTab |
Type : boolean
|
loadPlayerInteractEdata |
Type : IInteractEventEdata
|
mobileViewDisplay |
Type : string
|
Default value : 'block'
|
modal |
Decorators :
@ViewChild('modal')
|
Dom element reference of contentRatingModal |
Public navigationHelperService |
Type : NavigationHelperService
|
playerLoaded |
Default value : false
|
playerOverlayImage |
Type : string
|
Public playerService |
Type : PublicPlayerService
|
playerType |
Type : string
|
previewCdnUrl |
Type : string
|
Public resourceService |
Type : ResourceService
|
Public router |
Type : Router
|
Public showNewPlayer |
Default value : false
|
showPlayIcon |
Default value : true
|
showQumlPlayer |
Default value : false
|
showRatingModalAfterClose |
Default value : false
|
Public unsubscribe |
Default value : new Subject<void>()
|
import { ConfigService, NavigationHelperService, UtilService } from '@sunbird/shared';
import { Component, AfterViewInit, ViewChild, ElementRef, Input, Output, EventEmitter,
OnChanges, HostListener, OnInit, ChangeDetectorRef } from '@angular/core';
import * as _ from 'lodash-es';
import { PlayerConfig } from '@sunbird/shared';
import { Router } from '@angular/router';
import { ToasterService, ResourceService, ContentUtilsServiceService } from '@sunbird/shared';
const OFFLINE_ARTIFACT_MIME_TYPES = ['application/epub'];
import { Subject } from 'rxjs';
import { DeviceDetectorService } from 'ngx-device-detector';
import { IInteractEventEdata } from '@sunbird/telemetry';
import { UserService, FormService } from '../../../core/services';
import { OnDestroy } from '@angular/core';
import { takeUntil } from 'rxjs/operators';
import { CsContentProgressCalculator } from '@project-sunbird/client-services/services/content/utilities/content-progress-calculator';
import { ContentService } from '@sunbird/core';
import { PublicPlayerService } from '@sunbird/public';
@Component({
selector: 'app-player',
templateUrl: './player.component.html',
styleUrls: ['./player.component.scss']
})
export class PlayerComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {
@Input() playerConfig: PlayerConfig;
@Output() assessmentEvents = new EventEmitter<any>();
@Output() questionScoreSubmitEvents = new EventEmitter<any>();
@Output() questionScoreReviewEvents = new EventEmitter<any>();
@ViewChild('contentIframe') contentIframe: ElementRef;
@Output() playerOnDestroyEvent = new EventEmitter<any>();
@Output() sceneChangeEvent = new EventEmitter<any>();
@Input() contentProgressEvents$: Subject<any>;
playerLoaded = false;
buildNumber: string;
@Input() playerOption: any;
contentRatingModal = false;
showRatingModalAfterClose = false;
previewCdnUrl: string;
isCdnWorking: string;
CONSTANT = {
ACCESSEVENT: 'renderer:question:submitscore',
ISLASTATTEMPT: 'renderer:selfassess:lastattempt',
MAXATTEMPT: 'renderer:maxLimitExceeded',
ACCESSREVIEWEVENT: 'renderer:question:reviewAssessment'
};
@Input() overlayImagePath: string;
@Input() isSingleContent: boolean;
@Input() telemetryObject: {};
@Input() pageId: string;
@Input() contentData;
@Input() isContentDeleted: Subject<any>;
@Output() closePlayerEvent = new EventEmitter<any>();
@Output() ratingPopupClose = new EventEmitter<any>();
@Output() selfAssessLastAttempt = new EventEmitter<any>();
contentDeleted = false;
isMobileOrTab: boolean;
showPlayIcon = true;
closeButtonInteractEdata: IInteractEventEdata;
loadPlayerInteractEdata: IInteractEventEdata;
playerOverlayImage: string;
isFullScreenView = false;
public unsubscribe = new Subject<void>();
public showNewPlayer = false;
mobileViewDisplay = 'block';
playerType: string;
isDesktopApp = false;
showQumlPlayer = false;
contentId: string;
collectionId:string;
/**
* Dom element reference of contentRatingModal
*/
@ViewChild('modal') modal;
@HostListener('window:popstate', ['$event'])
onPopState(event) {
this.closeContentFullScreen();
}
constructor(public configService: ConfigService, public router: Router, private toasterService: ToasterService,
public resourceService: ResourceService, public navigationHelperService: NavigationHelperService,
private deviceDetectorService: DeviceDetectorService, private userService: UserService, public formService: FormService
, public contentUtilsServiceService: ContentUtilsServiceService, private contentService: ContentService,
private cdr: ChangeDetectorRef, public playerService: PublicPlayerService, private utilService: UtilService) {
this.buildNumber = (<HTMLInputElement>document.getElementById('buildNumber'))
? (<HTMLInputElement>document.getElementById('buildNumber')).value : '1.0';
this.previewCdnUrl = (<HTMLInputElement>document.getElementById('previewCdnUrl'))
? (<HTMLInputElement>document.getElementById('previewCdnUrl')).value : undefined;
this.isCdnWorking = (<HTMLInputElement>document.getElementById('cdnWorking'))
? (<HTMLInputElement>document.getElementById('cdnWorking')).value : 'no';
}
@HostListener('window:orientationchange', ['$event'])
public handleOrientationChange() {
const screenType = _.get(screen, 'orientation.type');
if ( screenType === 'portrait-primary' || screenType === 'portrait-secondary' ) {
this.closeFullscreen();
}
}
ngOnInit() {
this.checkForQumlPlayer()
// If `sessionStorage` has UTM data; append the UTM data to context.cdata
if (this.playerConfig && sessionStorage.getItem('UTM')) {
let utmData;
try {
utmData = JSON.parse(sessionStorage.getItem('UTM'));
} catch (error) {
throw new Error('JSON Parse Error => UTM data');
}
if (utmData && _.get(this.playerConfig, 'context.cdata')) {
this.playerConfig.context.cdata = _.union(this.playerConfig.context.cdata, utmData);
}
if (utmData && !_.get(this.playerConfig, 'context.cdata')) {
this.playerConfig.context['cdata'] = [];
this.playerConfig.context.cdata = _.union(this.playerConfig.context.cdata, utmData);
}
}
this.isDesktopApp = this.utilService.isDesktopApp;
// Check for loggedIn user; and append user data to context object
// User data (`firstName` and `lastName`) is used to show at the end of quiz
if (this.playerConfig) {
this.addUserDataToContext();
}
this.isMobileOrTab = this.deviceDetectorService.isMobile() || this.deviceDetectorService.isTablet();
if (this.isSingleContent === false) {
this.showPlayIcon = false;
}
this.setTelemetryData();
this.navigationHelperService.contentFullScreenEvent.
pipe(takeUntil(this.unsubscribe)).subscribe(isFullScreen => {
this.isFullScreenView = isFullScreen;
const root: HTMLElement = document.getElementsByTagName( 'html' )[0];
if (isFullScreen) {
root.classList.add('PlayerMediaQueryClass');
document.body.classList.add('o-y-hidden');
} else {
root.classList.remove('PlayerMediaQueryClass');
document.body.classList.remove('o-y-hidden');
}
if (this.isDesktopApp) {
const hideCM = isFullScreen ? true : false;
this.navigationHelperService.handleContentManagerOnFullscreen(hideCM);
}
this.loadPlayer();
});
this.contentUtilsServiceService.contentShareEvent.pipe(takeUntil(this.unsubscribe)).subscribe(data => {
if (this.isMobileOrTab && data === 'close') {
this.mobileViewDisplay = 'block';
}
});
}
/**
* loadPlayer method will be called
*/
ngAfterViewInit() {
if (this.playerConfig) {
this.loadPlayer();
}
}
ngOnChanges(changes) {
this.contentRatingModal = false;
this.showNewPlayer = false;
this.cdr.detectChanges();
if (this.playerConfig) {
this.playerOverlayImage = this.overlayImagePath ? this.overlayImagePath : _.get(this.playerConfig, 'metadata.appIcon');
this.loadPlayer();
}
}
loadCdnPlayer() {
const iFrameSrc = this.configService.appConfig.PLAYER_CONFIG.cdnUrl + '&build_number=' + this.buildNumber;
setTimeout(() => {
const playerElement = this.contentIframe.nativeElement;
playerElement.src = iFrameSrc;
playerElement.onload = (event) => {
try {
this.adjustPlayerHeight();
playerElement.contentWindow.initializePreview(this.playerConfig);
if (this.playerLoaded) {
playerElement.removeEventListener('renderer:telemetry:event', telemetryEvent => this.generateContentReadEvent(telemetryEvent));
window.frames['contentPlayer'].removeEventListener('message', accessEvent => this.generateScoreSubmitEvent(accessEvent), false);
}
this.playerLoaded = true;
playerElement.addEventListener('renderer:telemetry:event', telemetryEvent => this.generateContentReadEvent(telemetryEvent));
window.frames['contentPlayer'].addEventListener('message', accessEvent => this.generateScoreSubmitEvent(accessEvent), false);
} catch (err) {
this.loadDefaultPlayer();
}
};
}, 0);
}
loadDefaultPlayer(url = this.configService.appConfig.PLAYER_CONFIG.baseURL) {
const iFrameSrc = url + '&build_number=' + this.buildNumber;
setTimeout(() => {
const playerElement = this.contentIframe.nativeElement;
playerElement.src = iFrameSrc;
playerElement.onload = (event) => {
try {
this.adjustPlayerHeight();
playerElement.contentWindow.initializePreview(this.playerConfig);
if (this.playerLoaded) {
playerElement.removeEventListener('renderer:telemetry:event', telemetryEvent => this.generateContentReadEvent(telemetryEvent));
window.frames['contentPlayer'].removeEventListener('message', accessEvent => this.generateScoreSubmitEvent(accessEvent), false);
}
this.playerLoaded = true;
playerElement.addEventListener('renderer:telemetry:event', telemetryEvent => this.generateContentReadEvent(telemetryEvent));
window.frames['contentPlayer'].addEventListener('message', accessEvent => this.generateScoreSubmitEvent(accessEvent), false);
} catch (err) {
const prevUrls = this.navigationHelperService.history;
if (this.isCdnWorking.toLowerCase() === 'yes' && prevUrls[prevUrls.length - 2]) {
history.back();
}
}
};
}, 0);
}
loadPlayer() {
this.checkForQumlPlayer();
this.playerType = null;
const formReadInputParams = {
formType: 'content',
formAction: 'play',
contentType: 'player'
};
this.formService.getFormConfig(formReadInputParams).subscribe(
(data: any) => {
let isNewPlayer = false;
_.forEach(data, (value) => {
if (_.includes(_.get(value, 'mimeType'), _.get(this.playerConfig, 'metadata.mimeType')) && _.get(value, 'version') === 2) {
this.playerConfig.context.threshold = _.get(value, 'threshold');
this.playerType = _.get(value, 'type');
isNewPlayer = true;
}
});
if (isNewPlayer) {
this.playerLoaded = false;
this.loadNewPlayer();
} else {
this.loadOldPlayer();
}
},
(error) => {
this.loadOldPlayer();
}
);
}
checkForQumlPlayer() {
if (_.get(this.playerConfig, 'metadata.mimeType') === this.configService.appConfig.PLAYER_CONFIG.MIME_TYPE.questionset) {
this.playerConfig.config.sideMenu.showDownload = false;
if (!_.get(this.playerConfig, 'metadata.instructions')) {
this.playerService.getQuestionSetRead(_.get(this.playerConfig, 'metadata.identifier')).subscribe((data: any) => {
this.playerConfig.metadata.instructions = _.get(data, 'result.questionset.instructions');
this.showQumlPlayer = true;
}, (error) => {
this.showQumlPlayer = true;
});
} else {
this.showQumlPlayer = true;
}
}
}
loadOldPlayer() {
this.showNewPlayer = false;
if (this.isDesktopApp) {
this.updateMetadataForDesktop();
const downloadStatus = Boolean(_.get(this.playerConfig, 'metadata.desktopAppMetadata.isAvailable'));
let playerUrl = this.configService.appConfig.PLAYER_CONFIG.localBaseUrl;
if (!downloadStatus) {
playerUrl = `${playerUrl}webview=true`;
}
this.loadDefaultPlayer(playerUrl);
return;
}
if (this.isMobileOrTab) {
this.rotatePlayer();
}
if (this.previewCdnUrl !== '' && (this.isCdnWorking).toLowerCase() === 'yes') {
this.loadCdnPlayer();
return;
}
this.loadDefaultPlayer();
}
loadNewPlayer() {
const downloadStatus = Boolean(_.get(this.playerConfig, 'metadata.desktopAppMetadata.isAvailable'));
const artifactUrl = _.get(this.playerConfig, 'metadata.artifactUrl');
this.contentId = _.get(this.playerConfig, 'metadata.identifier');
this.collectionId = _.get(this.playerConfig, 'context.objectRollup.l1');
if (downloadStatus && artifactUrl && !_.startsWith(artifactUrl, 'http://')) {
this.playerConfig.metadata.artifactUrl = `${location.origin}/${artifactUrl}`;
}
this.addUserDataToContext();
if (this.isMobileOrTab) {
this.isFullScreenView = true;
if (_.get(this.playerConfig, 'metadata.mimeType') !== this.configService.appConfig.PLAYER_CONFIG.MIME_TYPE.questionset) {
this.rotatePlayer();
}
}
this.showNewPlayer = true;
if (this.userService.loggedIn) {
this.userService.userData$.subscribe((user: any) => {
if (user && !user.err) {
const userProfile = user.userProfile;
const userId = userProfile.id;
const varName = (userId + '_' + (this.collectionId ? this.collectionId : '') + '_' + (this.contentId ? this.contentId : '') + '_config');
const playerConfig: any = JSON.parse(localStorage.getItem(varName)) || {};
this.playerConfig['config'] = { ...this.playerConfig['config'], ...playerConfig };
}
});
} else {
const varName = ('guest' + '_' + (this.collectionId ? this.collectionId : '') + '_' + (this.contentId ? this.contentId : '') + '_config');;
const playerConfig: any = JSON.parse(localStorage.getItem(varName)) || {};
this.playerConfig['config'] = { ...this.playerConfig['config'], ...playerConfig };
}
}
// Update ArtifactUrl for old Player
updateMetadataForDesktop() {
const downloadStatus = Boolean(_.get(this.playerConfig, 'metadata.desktopAppMetadata.isAvailable'));
if (downloadStatus) {
this.playerConfig.data = '';
if (_.get(this.playerConfig, 'metadata.artifactUrl')
&& _.includes(OFFLINE_ARTIFACT_MIME_TYPES, this.playerConfig.metadata.mimeType)) {
const artifactFileName = this.playerConfig.metadata.artifactUrl.split('/');
this.playerConfig.metadata.artifactUrl = artifactFileName[artifactFileName.length - 1];
}
}
}
/**
* Adjust player height after load
*/
adjustPlayerHeight() {
const playerWidth = $('#contentPlayer').width();
if (playerWidth) {
let height = playerWidth * (9 / 16);
if (_.get(screen, 'orientation.type') === 'landscape-primary' && this.isMobileOrTab) {
height = window.innerHeight;
}
$('#contentPlayer').css('height', height + 'px');
}
}
generateScoreSubmitEvent(event: any) {
if (event.data.toLowerCase() === (this.CONSTANT.ACCESSEVENT).toLowerCase()) {
this.questionScoreSubmitEvents.emit(event);
}
if (event.data.toLowerCase() === (this.CONSTANT.ISLASTATTEMPT).toLowerCase()) {
this.selfAssessLastAttempt.emit(event);
}
if (event.data.toLowerCase() === (this.CONSTANT.MAXATTEMPT).toLowerCase()) {
this.selfAssessLastAttempt.emit(event);
}
if (event.data.toLowerCase() === (this.CONSTANT.ACCESSREVIEWEVENT).toLowerCase()) {
this.questionScoreReviewEvents.emit(event);
}
}
generatelimitedAttemptEvent(event) {
if (_.get(event, 'edata.isLastAttempt')) {
this.selfAssessLastAttempt.emit(event);
} else if (_.get(event, 'edata.maxLimitExceeded')) {
this.selfAssessLastAttempt.emit(event);
}
}
eventHandler(event) {
if (event.eid === 'END') {
const metaDataconfig = event.metaData;
if (this.userService.loggedIn) {
this.userService.userData$.subscribe((user: any) => {
if (user && !user.err) {
const userProfile = user.userProfile;
const userId = userProfile.id;
const varName = (userId + '_' + (this.collectionId ? this.collectionId : '') + '_' + (this.contentId ? this.contentId : '') + '_config');
localStorage.setItem(varName, JSON.stringify(metaDataconfig));
}
});
} else {
const userId = 'guest';
const varName = (userId + '_' + (this.collectionId ? this.collectionId : '') + '_' + (this.contentId ? this.contentId : '') + '_config');
localStorage.setItem(varName, JSON.stringify(metaDataconfig));
}
}
if (event.eid === 'exdata') {
this.generatelimitedAttemptEvent(event);
return;
}
if (_.get(event, 'edata.type') === 'SHARE') {
this.contentUtilsServiceService.contentShareEvent.emit('open');
this.mobileViewDisplay = 'none';
}
if (_.get(event, 'edata.type') === 'PRINT') {
const windowFrame = window.document.querySelector('pdf-viewer iframe');
if (windowFrame) {
windowFrame['contentWindow'].print();
}
this.mobileViewDisplay = 'none';
}
}
generateContentReadEvent(event: any, newPlayerEvent?) {
let eventCopy = newPlayerEvent ? _.cloneDeep(event) : event;
if (!eventCopy) {
return;
}
if (newPlayerEvent) {
eventCopy = { detail: {telemetryData: eventCopy}};
}
const eid = _.get(eventCopy, 'detail.telemetryData.eid');
const contentId = _.get(eventCopy, 'detail.telemetryData.object.id');
// this.contentId = contentId;
if (eid && (eid === 'START' || eid === 'END') && contentId === _.get(this.playerConfig, 'metadata.identifier')) {
this.showRatingPopup(eventCopy);
if (this.contentProgressEvents$) {
this.contentProgressEvents$.next(eventCopy);
}
} else if (eid && (eid === 'IMPRESSION')) {
this.emitSceneChangeEvent();
}
if (eid && (eid === 'ASSESS') || eid === 'START' || eid === 'END') {
this.assessmentEvents.emit(eventCopy);
}
if (_.get(this.playerConfig, 'metadata.mimeType') === this.configService.appConfig.PLAYER_CONFIG.MIME_TYPE.questionset && eid === 'END') {
this.questionScoreSubmitEvents.emit(event);
}
}
emitSceneChangeEvent(timer = 0) {
setTimeout(() => {
if (_.get(this, 'contentIframe.nativeElement')) {
const stageId = this.contentIframe.nativeElement.contentWindow.EkstepRendererAPI.getCurrentStageId();
const eventData = { stageId };
this.sceneChangeEvent.emit(eventData);
}
}, timer); // waiting for player to load, then fetching stageId (if we dont wait stageId will be undefined)
}
showRatingPopup(event) {
let contentProgress;
const playerSummary: Array<any> = _.get(event, 'detail.telemetryData.edata.summary');
if (playerSummary) {
const contentMimeType = this.playerConfig.metadata.mimeType;
contentProgress = CsContentProgressCalculator.calculate(playerSummary, contentMimeType);
}
if (event.detail.telemetryData.eid === 'END' && contentProgress === 100) {
this.contentRatingModal = !this.isFullScreenView;
this.showRatingModalAfterClose = true;
if (this.modal) {
this.modal.showContentRatingModal = true;
}
}
}
/**
* this method will handle play button click and turn the player into landscape
*/
enablePlayer(mode: boolean) {
this.showPlayIcon = mode;
this.loadPlayer();
}
/** this method checks for the browser capability to be fullscreen via if-else ladder
* if match found, it will turn the player along will be close button into fullscreen and then
* rotate it to landscape mode
*/
rotatePlayer() {
setTimeout(() => {
const playVideo: any = document.querySelector('#playerFullscreen');
try {
if (playVideo.requestFullscreen) {
playVideo.requestFullscreen();
} else if (playVideo.mozRequestFullScreen) { /* Firefox */
playVideo.mozRequestFullScreen();
} else if (playVideo.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
playVideo.webkitRequestFullscreen();
} else if (playVideo.msRequestFullscreen) { /* IE/Edge */
playVideo.msRequestFullscreen();
}
screen.orientation.lock('landscape');
} catch (error) {}
});
}
/** when user clicks on close button
* this method will let the player to exit from fullscreen mode and
* 1. video thumbnail will be shown for single content
* 2. content-details page will be shown ( for multi-result dial-code search flow)
*/
closeFullscreen() {
/** to exit the fullscreen mode */
if (document['exitFullscreen']) {
document['exitFullscreen']();
} else if (document['mozCancelFullScreen']) { /* Firefox */
document['mozCancelFullScreen']();
} else if (document['webkitExitFullscreen']) { /* Chrome, Safari and Opera */
document['webkitExitFullscreen']();
} else if (document['msExitFullscreen']) { /* IE/Edge */
document['msExitFullscreen']();
}
if (this.showRatingModalAfterClose) {
this.contentRatingModal = true;
if (this.modal) {
this.modal.showContentRatingModal = true;
}
}
/** to change the view of the content-details page */
this.showPlayIcon = true;
this.closePlayerEvent.emit();
}
setTelemetryData() {
this.closeButtonInteractEdata = {
id: 'player-close-button',
type: 'click',
pageid: this.pageId
};
this.loadPlayerInteractEdata = {
id: 'play-button',
type: 'click',
pageid: this.pageId
};
}
closeContentFullScreen() {
this.navigationHelperService.emitFullScreenEvent(false);
this.loadPlayer();
}
closeModal() {
this.focusOnReplay();
this.ratingPopupClose.emit({});
}
focusOnReplay() {
if (this.playerType === 'quml-player') {
const replayButton: HTMLElement = document.querySelector('.replay-section');
if (replayButton) {
replayButton.focus();
}
}
}
public addUserDataToContext() {
if (this.userService.loggedIn) {
this.userService.userData$.subscribe((user: any) => {
if (user && !user.err) {
const userProfile = user.userProfile;
this.playerConfig.context['userData'] = {
firstName: userProfile.firstName ? userProfile.firstName : 'Guest',
lastName: userProfile.lastName ? userProfile.lastName : ''
};
}
});
} else {
this.playerConfig.context.userData = {
firstName: this.userService.guestUserProfile.formatedName || 'Guest',
lastName: ''
};
}
}
ngOnDestroy() {
const playerElement = _.get(this.contentIframe, 'nativeElement');
if (playerElement) {
if (_.get(playerElement, 'contentWindow.telemetry_web.tList.length')) {
const request = {
url: this.configService.urlConFig.URLS.TELEMETRY.SYNC,
data: {
'id': 'api.sunbird.telemetry',
'ver': '3.0',
'events': playerElement.contentWindow.telemetry_web.tList.map(item => JSON.parse(item))
}
};
this.contentService.post(request).subscribe();
}
playerElement.remove();
}
this.unsubscribe.next();
this.unsubscribe.complete();
}
}
<!-- Mobile browser player-->
<div *ngIf="isMobileOrTab && !showNewPlayer" >
<div data-ratio="16:9" class="player-thumbnail" appTelemetryInteract [telemetryInteractObject]="telemetryObject" [telemetryInteractEdata]="loadPlayerInteractEdata"
[ngStyle]="{'background': 'url(' + playerOverlayImage + ') center/cover no-repeat '}" *ngIf="showPlayIcon" id="contentPlayer" tabindex="0" (click)="enablePlayer(false)">
<img class="play-icon" src="assets/images/play_circle.svg">
</div>
<div id="playerFullscreen" *ngIf="!showPlayIcon" >
<!-- <img appTelemetryInteract [telemetryInteractObject]="telemetryInteractObject" [telemetryInteractEdata]="closeButtonInteractEdata" (click)="closeFullscreen()" class="close-button" src="assets/images/close-Black.svg" alt=""> -->
<iframe #contentIframe id="contentPlayer" title="Content Player" aria-label="Content Player" class="contentViewerIframeShadow" name="contentPlayer"></iframe>
</div>
</div>
<!-- Portal player-->
<div class="h-100" *ngIf="!isMobileOrTab && !showNewPlayer && !isContentDeleted">
<iframe #contentIframe id="contentPlayer" title="Content Player" aria-label="Content Player" class="contentViewerIframeShadow" [ngClass]="{'player-fullscreen': isFullScreenView}" name="contentPlayer"></iframe>
</div>
<div *ngIf="!isMobileOrTab && showNewPlayer && !isContentDeleted" class="w-100 h-100">
<div class="contentViewerIframeShadow" [ngClass]="{'player-fullscreen content-full-video': isFullScreenView}">
<sunbird-pdf-player *ngIf="playerType === 'pdf-player'" [playerConfig]="playerConfig"
(playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)">
</sunbird-pdf-player>
<sunbird-video-player *ngIf="playerType === 'video-player'" [playerConfig]="playerConfig"
(playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)">
</sunbird-video-player>
<sunbird-epub-player *ngIf="playerType === 'epub-player'" [playerConfig]="playerConfig" [showFullScreen]="isFullScreenView"
(playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)"></sunbird-epub-player>
<quml-main-player *ngIf="playerType === 'quml-player' && showQumlPlayer" [playerConfig]="playerConfig" (playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)"></quml-main-player>
</div>
</div>
<!--If Content is deleted-->
<div class="d-flex flex-dc flex-jc-center text-center flex-ai-center no-content-player" *ngIf="isContentDeleted">
<div class="no-content-player__text"></div>
<div>
<img src="assets/images/shape.svg" width="90px" height="70px">
</div>
<div class="fnormal">{{resourceService?.messages?.stmsg?.desktop?.deleteMessage}}</div>
</div>
<div *ngIf="isMobileOrTab && showNewPlayer" class="w-100 h-100">
<div data-ratio="16:9" class="player-thumbnail" appTelemetryInteract [telemetryInteractObject]="telemetryObject" [telemetryInteractEdata]="loadPlayerInteractEdata"
[ngStyle]="{'background': 'url(' + playerOverlayImage + ') center/cover no-repeat '}" *ngIf="showPlayIcon" id="contentPlayer" tabindex="0" (click)="enablePlayer(false)">
<img class="play-icon" src="assets/images/play_circle.svg">
</div>
<div id="playerFullscreen" *ngIf="!showPlayIcon" class="player-fullscreen" [ngStyle]="{'display': mobileViewDisplay}">
<!-- <img *ngIf="playerType !== 'quml-player'" appTelemetryInteract [telemetryInteractObject]="telemetryInteractObject" [telemetryInteractEdata]="closeButtonInteractEdata" (click)="closeFullscreen()" class="close-button" src="assets/images/close-Black.svg" alt=""> -->
<sunbird-pdf-player *ngIf="playerType === 'pdf-player'" [playerConfig]="playerConfig"
(playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)"></sunbird-pdf-player>
<sunbird-video-player *ngIf="playerType === 'video-player'" [playerConfig]="playerConfig"
(playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)">
</sunbird-video-player>
<sunbird-epub-player *ngIf="playerType === 'epub-player'" [playerConfig]="playerConfig" [showFullScreen]="isFullScreenView"
(playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)"></sunbird-epub-player>
<quml-main-player *ngIf="playerType === 'quml-player' && showQumlPlayer" [playerConfig]="playerConfig" (playerEvent)="eventHandler($event)" (telemetryEvent)="generateContentReadEvent($event, true)"></quml-main-player>
</div>
</div>
<!--Rating popup -->
<app-content-rating #modal *ngIf="contentRatingModal && playerConfig"
[contentData]="playerConfig.metadata" (closeModal)="closeModal()"></app-content-rating>
<button *ngIf="playerType !== 'quml-player'" class="sb-btn sb-btn-primary sb-btn-normal pull-right view-fullscreen"[ngClass]="{'d-none': !viewFullscreenBtn}" tabindex="0" (click)="viewInFullscreen()" appTelemetryInteract [telemetryInteractEdata]="viewFullScreenIntractEdata" [telemetryInteractObject]="viewFullScreenIntractObject"> <i class="expand icon"></i> View Full Screen</button>
./player.component.scss
@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;
@use 'components/video' as *;
.player-thumbnail {
width: 100%;
cursor: pointer;
border-radius: .3125rem;
position: relative;
padding-bottom: 56.25%;
border: calculateRem(1px) solid var(--black);
}
.play-icon {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
left: 50%;
height: calculateRem(80px);
width: calculateRem(80px)
}
.overlay-image {
width: 100%;
height: 100%;
}
@media only screen and (max-width: 768px) {
.play-icon {
height: auto;
width: auto;
}
}
.content-full-video{
position: fixed;
top: 0;
left: 0;
right: 0;
height: calculateRem(50px);
z-index: 999999;
align-items: center;
.video-close-btn{
position: fixed;
right: calculateRem(32px);
left: auto;
cursor: pointer;
background: var(--black);
height: calculateRem(32px);
width: calculateRem(32px);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
svg{
width: calculateRem(16px);
height: calculateRem(16px);
}
html[dir="rtl"] & {
left: calculateRem(32px);
right: auto;
}
}
}
.player-fullscreen {
position:fixed !important;
border:none;
margin:0;
padding:0;
overflow:hidden;
z-index:9999;
// height: calc(100% - 4rem) !important;
background:var(--white);
top: 0;
left: 0;
bottom: 0;
right: 0;
border: none;
}
.contentViewerIframeShadow {
width: 100%;
border: 0;
height: 100%;
left:50%;
top:50%;
right:50%;
bottom:50%;
transform: translate(-50%, -50%);
position: absolute;
transition: all ease .3s;
iframe {
height: calc(100vh - 4rem) !important;
border:calculateRem(10px) solid var(--primary-400);
}
}
.close-button {
position: absolute;
z-index: 100;
right: calculateRem(8px);
top: calculateRem(8px);
cursor: pointer;
}
::ng-deep{
iframe {
width: 100%;
height: 100% !important;
}
.video-js{height:100% !important;}
.video-js {
padding-top:inherit !important;
}
}
.no-content-player{
background: var(--black);
color: var(--white);
width: 100%;
height: 100%;
&__text{
max-width: calculateRem(300px);
}
}