2016-09-18 13:38:38 -07:00
define ( [ 'appSettings' , 'datetime' , 'mediaInfo' , 'browser' , 'scrollStyles' , 'paper-icon-button-light' ] , function ( appSettings , datetime , mediaInfo , browser ) {
2014-03-08 01:09:45 -07:00
2014-06-21 22:52:31 -07:00
function createVideoPlayer ( self ) {
2014-03-08 01:09:45 -07:00
2014-03-20 01:34:54 -07:00
var initialVolume ;
2014-03-18 12:57:32 -07:00
var idleState = true ;
2014-03-08 01:09:45 -07:00
2014-04-11 08:36:25 -07:00
var muteButton = null ;
var unmuteButton = null ;
var volumeSlider = null ;
2016-06-24 09:51:13 -07:00
var volumeSliderContainer = null ;
2014-04-11 08:36:25 -07:00
var positionSlider ;
var currentTimeElement ;
2014-06-11 12:31:33 -07:00
self . currentSubtitleStreamIndex = null ;
2014-06-11 19:38:40 -07:00
self . getCurrentSubtitleStream = function ( ) {
return self . getSubtitleStream ( self . currentSubtitleStreamIndex ) ;
} ;
self . getSubtitleStream = function ( index ) {
return self . currentMediaSource . MediaStreams . filter ( function ( s ) {
return s . Type == 'Subtitle' && s . Index == index ;
} ) [ 0 ] ;
} ;
2014-03-08 01:09:45 -07:00
self . toggleFullscreen = function ( ) {
if ( self . isFullScreen ( ) ) {
2015-05-17 18:27:48 -07:00
self . exitFullScreen ( ) ;
2014-03-08 01:09:45 -07:00
} else {
2014-03-24 20:26:54 -07:00
requestFullScreen ( document . body ) ;
2014-03-08 01:09:45 -07:00
}
} ;
2016-06-06 17:44:15 -07:00
function setClass ( elems , method , className ) {
for ( var i = 0 , length = elems . length ; i < length ; i ++ ) {
elems [ i ] . classList [ method ] ( className ) ;
}
}
2014-03-08 01:09:45 -07:00
self . resetEnhancements = function ( ) {
2015-08-28 12:10:44 -07:00
2015-12-14 08:43:03 -07:00
if ( ! initComplete ) {
return ;
}
if ( self . isFullScreen ( ) ) {
self . exitFullScreen ( ) ;
}
2016-06-04 09:10:10 -07:00
var videoPlayerElement = document . querySelector ( '#videoPlayer' ) ;
fadeOut ( videoPlayerElement ) ;
videoPlayerElement . classList . remove ( 'fullscreenVideo' ) ;
videoPlayerElement . classList . remove ( 'idlePlayer' ) ;
2016-06-06 17:44:15 -07:00
setClass ( document . querySelectorAll ( '.hiddenOnIdle' ) , 'remove' , 'inactive' ) ;
var video = videoPlayerElement . querySelector ( 'video' ) ;
if ( video ) {
video . parentNode . removeChild ( video ) ;
}
2015-08-28 12:10:44 -07:00
document . querySelector ( '.mediaButton.infoButton' ) . classList . remove ( 'active' ) ;
document . querySelector ( '.videoControls .nowPlayingInfo' ) . classList . add ( 'hide' ) ;
document . querySelector ( '.videoControls' ) . classList . add ( 'hiddenOnIdle' ) ;
2014-03-08 01:09:45 -07:00
} ;
2014-03-20 20:31:40 -07:00
self . exitFullScreen = function ( ) {
2015-05-17 18:27:48 -07:00
2014-03-08 01:09:45 -07:00
if ( document . exitFullscreen ) {
document . exitFullscreen ( ) ;
2015-05-27 22:51:48 -07:00
} else if ( document . mozCancelFullScreen ) {
document . mozCancelFullScreen ( ) ;
2014-03-08 01:09:45 -07:00
} else if ( document . webkitExitFullscreen ) {
document . webkitExitFullscreen ( ) ;
2015-05-17 18:27:48 -07:00
} else if ( document . msExitFullscreen ) {
document . msExitFullscreen ( ) ;
2014-03-08 01:09:45 -07:00
}
2016-06-04 09:10:10 -07:00
document . querySelector ( '#videoPlayer' ) . classList . remove ( 'fullscreenVideo' ) ;
2014-03-17 07:48:16 -07:00
} ;
2014-03-08 01:09:45 -07:00
2014-03-20 20:31:40 -07:00
self . isFullScreen = function ( ) {
2014-03-08 01:09:45 -07:00
return document . fullscreen || document . mozFullScreen || document . webkitIsFullScreen || document . msFullscreenElement ? true : false ;
2014-03-17 07:48:16 -07:00
} ;
2014-03-08 01:09:45 -07:00
2015-05-21 13:53:14 -07:00
self . showSubtitleMenu = function ( ) {
2015-06-27 12:53:36 -07:00
var streams = self . currentMediaSource . MediaStreams . filter ( function ( currentStream ) {
return currentStream . Type == "Subtitle" ;
} ) ;
2015-05-21 13:53:14 -07:00
2015-12-23 10:46:01 -07:00
var currentIndex = self . currentSubtitleStreamIndex ;
if ( currentIndex == null ) {
currentIndex = - 1 ;
}
2015-06-27 12:53:36 -07:00
streams . unshift ( {
Index : - 1 ,
2015-12-23 10:46:01 -07:00
Language : Globalize . translate ( 'ButtonOff' )
2015-06-27 12:53:36 -07:00
} ) ;
var menuItems = streams . map ( function ( stream ) {
var attributes = [ ] ;
attributes . push ( stream . Language || Globalize . translate ( 'LabelUnknownLanguage' ) ) ;
if ( stream . Codec ) {
attributes . push ( stream . Codec ) ;
}
var name = attributes . join ( ' - ' ) ;
if ( stream . IsDefault ) {
name += ' (D)' ;
}
if ( stream . IsForced ) {
name += ' (F)' ;
}
if ( stream . External ) {
name += ' (EXT)' ;
}
var opt = {
2016-05-14 11:02:06 -07:00
name : stream . DisplayTitle || name ,
2015-06-27 12:53:36 -07:00
id : stream . Index
} ;
if ( stream . Index == currentIndex ) {
2016-04-16 12:22:09 -07:00
opt . selected = true ;
2015-06-27 12:53:36 -07:00
}
return opt ;
} ) ;
2016-01-30 21:04:00 -07:00
require ( [ 'actionsheet' ] , function ( actionsheet ) {
2015-06-27 12:53:36 -07:00
2016-01-30 21:04:00 -07:00
actionsheet . show ( {
2015-06-27 12:53:36 -07:00
items : menuItems ,
2016-02-01 10:02:46 -07:00
// history.back() will cause the video player to stop
enableHistory : false ,
2016-06-04 09:10:10 -07:00
positionTo : document . querySelector ( '.videoSubtitleButton' ) ,
2015-06-27 12:53:36 -07:00
callback : function ( id ) {
var index = parseInt ( id ) ;
if ( index != currentIndex ) {
self . onSubtitleOptionSelected ( index ) ;
}
}
} ) ;
2014-03-08 01:09:45 -07:00
2015-06-27 12:53:36 -07:00
} ) ;
2014-03-08 01:09:45 -07:00
} ;
2015-05-21 13:53:14 -07:00
self . showQualityFlyout = function ( ) {
2016-01-30 21:04:00 -07:00
require ( [ 'qualityoptions' , 'actionsheet' ] , function ( qualityoptions , actionsheet ) {
2014-03-08 01:09:45 -07:00
2015-12-30 10:02:11 -07:00
var currentSrc = self . getCurrentSrc ( self . currentMediaRenderer ) . toLowerCase ( ) ;
var isStatic = currentSrc . indexOf ( 'static=true' ) != - 1 ;
2015-06-27 12:53:36 -07:00
2015-12-30 10:02:11 -07:00
var videoStream = self . currentMediaSource . MediaStreams . filter ( function ( stream ) {
return stream . Type == "Video" ;
} ) [ 0 ] ;
var videoWidth = videoStream ? videoStream . Width : null ;
2015-06-27 12:53:36 -07:00
2016-02-29 23:02:03 -07:00
var options = qualityoptions . getVideoQualityOptions ( appSettings . maxStreamingBitrate ( ) , videoWidth ) ;
2015-06-27 12:53:36 -07:00
2015-12-30 10:02:11 -07:00
if ( isStatic ) {
options [ 0 ] . name = "Direct" ;
}
2015-06-27 12:53:36 -07:00
2015-12-30 10:02:11 -07:00
var menuItems = options . map ( function ( o ) {
2015-06-27 12:53:36 -07:00
2015-12-30 10:02:11 -07:00
var opt = {
name : o . name ,
id : o . bitrate
} ;
2014-03-08 01:09:45 -07:00
2015-12-30 10:02:11 -07:00
if ( o . selected ) {
2016-04-16 12:22:09 -07:00
opt . selected = true ;
2015-12-30 10:02:11 -07:00
}
2015-06-27 12:53:36 -07:00
2015-12-30 10:02:11 -07:00
return opt ;
} ) ;
2015-06-27 12:53:36 -07:00
2015-12-30 10:02:11 -07:00
var selectedId = options . filter ( function ( o ) {
return o . selected ;
} ) ;
selectedId = selectedId . length ? selectedId [ 0 ] . bitrate : null ;
2016-01-30 21:04:00 -07:00
actionsheet . show ( {
2015-06-27 12:53:36 -07:00
items : menuItems ,
2016-02-01 10:02:46 -07:00
// history.back() will cause the video player to stop
enableHistory : false ,
2016-06-04 09:10:10 -07:00
positionTo : document . querySelector ( '.videoQualityButton' ) ,
2015-06-27 12:53:36 -07:00
callback : function ( id ) {
var bitrate = parseInt ( id ) ;
if ( bitrate != selectedId ) {
self . onQualityOptionSelected ( bitrate ) ;
}
}
} ) ;
} ) ;
2014-03-08 01:09:45 -07:00
} ;
self . showAudioTracksFlyout = function ( ) {
2015-06-27 12:53:36 -07:00
var options = self . currentMediaSource . MediaStreams . filter ( function ( currentStream ) {
return currentStream . Type == "Audio" ;
} ) ;
2015-05-21 13:53:14 -07:00
2015-06-27 12:53:36 -07:00
var currentIndex = getParameterByName ( 'AudioStreamIndex' , self . getCurrentSrc ( self . currentMediaRenderer ) ) ;
var menuItems = options . map ( function ( stream ) {
var attributes = [ ] ;
attributes . push ( stream . Language || Globalize . translate ( 'LabelUnknownLanguage' ) ) ;
if ( stream . Codec ) {
attributes . push ( stream . Codec ) ;
}
if ( stream . Profile ) {
attributes . push ( stream . Profile ) ;
}
if ( stream . BitRate ) {
attributes . push ( ( Math . floor ( stream . BitRate / 1000 ) ) + ' kbps' ) ;
}
if ( stream . Channels ) {
attributes . push ( stream . Channels + ' ch' ) ;
}
var name = attributes . join ( ' - ' ) ;
if ( stream . IsDefault ) {
name += ' (D)' ;
}
var opt = {
2016-05-14 11:02:06 -07:00
name : stream . DisplayTitle || name ,
2015-06-27 12:53:36 -07:00
id : stream . Index
} ;
if ( stream . Index == currentIndex ) {
2016-04-16 12:22:09 -07:00
opt . selected = true ;
2015-06-27 12:53:36 -07:00
}
return opt ;
} ) ;
2016-01-30 21:04:00 -07:00
require ( [ 'actionsheet' ] , function ( actionsheet ) {
2015-06-27 12:53:36 -07:00
2016-01-30 21:04:00 -07:00
actionsheet . show ( {
2015-06-27 12:53:36 -07:00
items : menuItems ,
2016-02-01 10:02:46 -07:00
// history.back() will cause the video player to stop
enableHistory : false ,
2016-06-04 09:10:10 -07:00
positionTo : document . querySelector ( '.videoAudioButton' ) ,
2015-06-27 12:53:36 -07:00
callback : function ( id ) {
2014-03-08 01:09:45 -07:00
2015-06-27 12:53:36 -07:00
var index = parseInt ( id ) ;
if ( index != currentIndex ) {
self . onAudioOptionSelected ( index ) ;
}
}
} ) ;
} ) ;
2014-03-08 01:09:45 -07:00
} ;
2014-05-07 22:04:39 -07:00
self . setAudioStreamIndex = function ( index ) {
self . changeStream ( self . getCurrentTicks ( ) , { AudioStreamIndex : index } ) ;
} ;
self . setSubtitleStreamIndex = function ( index ) {
2014-06-11 19:38:40 -07:00
2015-06-13 07:46:59 -07:00
if ( ! self . currentMediaRenderer . supportsTextTracks ( ) ) {
2014-07-19 21:46:29 -07:00
2014-06-11 19:38:40 -07:00
self . changeStream ( self . getCurrentTicks ( ) , { SubtitleStreamIndex : index } ) ;
self . currentSubtitleStreamIndex = index ;
return ;
}
var currentStream = self . getCurrentSubtitleStream ( ) ;
var newStream = self . getSubtitleStream ( index ) ;
if ( ! currentStream && ! newStream ) return ;
var selectedTrackElementIndex = - 1 ;
if ( currentStream && ! newStream ) {
2015-03-30 12:57:37 -07:00
if ( currentStream . DeliveryMethod != 'External' ) {
2014-06-11 19:38:40 -07:00
// Need to change the transcoded stream to remove subs
self . changeStream ( self . getCurrentTicks ( ) , { SubtitleStreamIndex : - 1 } ) ;
}
}
else if ( ! currentStream && newStream ) {
2015-03-30 12:57:37 -07:00
if ( newStream . DeliveryMethod == 'External' ) {
2014-06-11 19:38:40 -07:00
selectedTrackElementIndex = index ;
} else {
// Need to change the transcoded stream to add subs
self . changeStream ( self . getCurrentTicks ( ) , { SubtitleStreamIndex : index } ) ;
}
}
2014-06-14 19:24:04 -07:00
else if ( currentStream && newStream ) {
2015-03-30 12:57:37 -07:00
if ( newStream . DeliveryMethod == 'External' ) {
2014-06-14 19:24:04 -07:00
selectedTrackElementIndex = index ;
2014-06-14 19:58:00 -07:00
2015-03-30 12:57:37 -07:00
if ( currentStream . DeliveryMethod != 'External' ) {
2014-06-14 19:24:04 -07:00
self . changeStream ( self . getCurrentTicks ( ) , { SubtitleStreamIndex : - 1 } ) ;
}
} else {
// Need to change the transcoded stream to add subs
self . changeStream ( self . getCurrentTicks ( ) , { SubtitleStreamIndex : index } ) ;
}
}
2014-06-11 19:38:40 -07:00
self . setCurrentTrackElement ( selectedTrackElementIndex ) ;
2014-07-19 21:46:29 -07:00
2014-06-11 19:38:40 -07:00
self . currentSubtitleStreamIndex = index ;
} ;
self . setCurrentTrackElement = function ( index ) {
2016-02-24 09:52:36 -07:00
self . currentMediaRenderer . setCurrentTrackElement ( index ) ;
2014-05-07 22:04:39 -07:00
} ;
2014-06-14 19:58:00 -07:00
self . updateTextStreamUrls = function ( startPositionTicks ) {
2015-06-13 07:46:59 -07:00
self . currentMediaRenderer . updateTextStreamUrls ( startPositionTicks ) ;
2014-06-14 19:58:00 -07:00
} ;
2014-06-28 12:35:30 -07:00
self . updateNowPlayingInfo = function ( item ) {
if ( ! item ) {
throw new Error ( 'item cannot be null' ) ;
}
2016-06-06 17:44:15 -07:00
var mediaControls = document . querySelector ( "#videoPlayer" ) ;
2014-06-28 12:35:30 -07:00
2015-08-30 21:57:12 -07:00
var state = self . getPlayerStateInternal ( self . currentMediaRenderer , item . CurrentProgram || item , self . currentMediaSource ) ;
2014-06-28 12:35:30 -07:00
var url = "" ;
2015-05-13 13:55:08 -07:00
var imageWidth = 400 ;
2015-05-13 22:42:55 -07:00
var imageHeight = 300 ;
2014-06-28 12:35:30 -07:00
if ( state . NowPlayingItem . PrimaryImageTag ) {
url = ApiClient . getScaledImageUrl ( state . NowPlayingItem . PrimaryImageItemId , {
type : "Primary" ,
2015-05-13 10:53:26 -07:00
width : imageWidth ,
2014-06-28 12:35:30 -07:00
tag : state . NowPlayingItem . PrimaryImageTag
} ) ;
}
else if ( state . NowPlayingItem . PrimaryImageTag ) {
url = ApiClient . getScaledImageUrl ( state . NowPlayingItem . PrimaryImageItemId , {
type : "Primary" ,
2015-05-13 10:53:26 -07:00
width : imageWidth ,
2014-06-28 12:35:30 -07:00
tag : state . NowPlayingItem . PrimaryImageTag
} ) ;
}
else if ( state . NowPlayingItem . BackdropImageTag ) {
url = ApiClient . getScaledImageUrl ( state . NowPlayingItem . BackdropItemId , {
type : "Backdrop" ,
2015-05-13 10:53:26 -07:00
height : imageHeight ,
2014-06-28 12:35:30 -07:00
tag : state . NowPlayingItem . BackdropImageTag ,
index : 0
} ) ;
}
else if ( state . NowPlayingItem . ThumbImageTag ) {
url = ApiClient . getScaledImageUrl ( state . NowPlayingItem . ThumbImageItemId , {
type : "Thumb" ,
2015-05-13 10:53:26 -07:00
height : imageHeight ,
2014-06-28 12:35:30 -07:00
tag : state . NowPlayingItem . ThumbImageTag
} ) ;
}
if ( url ) {
2016-06-06 17:44:15 -07:00
mediaControls . querySelector ( '.nowPlayingImage' ) . innerHTML = '<img src="' + url + '" />' ;
2014-06-28 12:35:30 -07:00
} else {
2016-06-06 17:44:15 -07:00
mediaControls . querySelector ( '.nowPlayingImage' ) . innerHTML = '' ;
2014-06-28 12:35:30 -07:00
}
if ( state . NowPlayingItem . LogoItemId ) {
url = ApiClient . getScaledImageUrl ( state . NowPlayingItem . LogoItemId , {
type : "Logo" ,
2014-06-28 19:30:20 -07:00
height : 42 ,
2014-06-28 12:35:30 -07:00
tag : state . NowPlayingItem . LogoImageTag
} ) ;
2016-06-06 17:44:15 -07:00
mediaControls . querySelector ( '.videoTopControlsLogo' ) . innerHTML = '<img src="' + url + '" />' ;
2014-06-28 12:35:30 -07:00
} else {
2016-06-06 17:44:15 -07:00
mediaControls . querySelector ( '.videoTopControlsLogo' ) . innerHTML = '' ;
2014-06-28 12:35:30 -07:00
}
2016-06-06 17:44:15 -07:00
var elem = mediaControls . querySelector ( '.nowPlayingTabs' ) ;
elem . innerHTML = getNowPlayingTabsHtml ( item . CurrentProgram || item ) ;
ImageLoader . lazyChildren ( elem ) ;
2015-05-13 20:24:25 -07:00
2016-06-06 17:44:15 -07:00
function onTabButtonClick ( ) {
if ( ! this . classList . contains ( 'selectedNowPlayingTabButton' ) ) {
2015-05-13 20:24:25 -07:00
2016-06-06 17:44:15 -07:00
var selectedNowPlayingTabButton = document . querySelector ( '.selectedNowPlayingTabButton' ) ;
if ( selectedNowPlayingTabButton ) {
selectedNowPlayingTabButton . classList . remove ( 'selectedNowPlayingTabButton' ) ;
}
this . classList . add ( 'selectedNowPlayingTabButton' ) ;
setClass ( document . querySelectorAll ( '.nowPlayingTab' ) , 'add' , 'hide' ) ;
document . querySelector ( '.' + this . getAttribute ( 'data-tab' ) ) . classList . remove ( 'hide' ) ;
2015-05-13 20:24:25 -07:00
}
2016-06-06 17:44:15 -07:00
}
2015-05-13 20:24:25 -07:00
2016-06-06 17:44:15 -07:00
var nowPlayingTabButtons = elem . querySelectorAll ( '.nowPlayingTabButton' ) ;
for ( var i = 0 , length = nowPlayingTabButtons . length ; i < length ; i ++ ) {
nowPlayingTabButtons [ i ] . addEventListener ( 'click' , onTabButtonClick ) ;
}
2016-06-06 22:42:26 -07:00
elem . addEventListener ( 'click' , function ( e ) {
2015-05-13 20:24:25 -07:00
2016-06-06 22:42:26 -07:00
var chapterCard = parentWithClass ( e . target , 'chapterCard' ) ;
if ( chapterCard ) {
self . seek ( parseInt ( chapterCard . getAttribute ( 'data-position' ) ) ) ;
}
2015-05-13 20:24:25 -07:00
} ) ;
} ;
2016-06-06 22:42:26 -07:00
function parentWithClass ( elem , className ) {
while ( ! elem . classList || ! elem . classList . contains ( className ) ) {
elem = elem . parentNode ;
if ( ! elem ) {
return null ;
}
}
return elem ;
}
2015-05-13 20:24:25 -07:00
function getNowPlayingTabsHtml ( item ) {
var html = '' ;
html += '<div class="nowPlayingTabButtons">' ;
html += '<a href="#" class="nowPlayingTabButton selectedNowPlayingTabButton" data-tab="tabInfo">' + Globalize . translate ( 'TabInfo' ) + '</a>' ;
if ( item . Chapters && item . Chapters . length ) {
html += '<a href="#" class="nowPlayingTabButton" data-tab="tabScenes">' + Globalize . translate ( 'TabScenes' ) + '</a>' ;
}
if ( item . People && item . People . length ) {
html += '<a href="#" class="nowPlayingTabButton" data-tab="tabCast">' + Globalize . translate ( 'TabCast' ) + '</a>' ;
}
html += '</div>' ;
html += '<div class="tabInfo nowPlayingTab">' ;
var nameHtml = MediaController . getNowPlayingNameHtml ( item , false ) ;
2015-05-13 10:53:26 -07:00
nameHtml = '<div class="videoNowPlayingName">' + nameHtml + '</div>' ;
2016-05-11 22:58:05 -07:00
var miscInfo = mediaInfo . getPrimaryMediaInfoHtml ( item ) ;
2015-05-13 10:53:26 -07:00
if ( miscInfo ) {
nameHtml += '<div class="videoNowPlayingRating">' + miscInfo + '</div>' ;
}
if ( item . Overview ) {
nameHtml += '<div class="videoNowPlayingOverview">' + item . Overview + '</div>' ;
}
2015-05-13 20:24:25 -07:00
html += nameHtml ;
html += '</div>' ;
if ( item . Chapters && item . Chapters . length ) {
2016-06-06 17:44:15 -07:00
html += '<div class="tabScenes nowPlayingTab smoothScrollX hide" style="white-space:nowrap;margin-bottom:2em;">' ;
2015-05-13 20:24:25 -07:00
var chapterIndex = 0 ;
html += item . Chapters . map ( function ( c ) {
2015-06-11 10:57:59 -07:00
var width = 240 ;
2015-05-13 20:24:25 -07:00
var chapterHtml = '<a class="card backdropCard chapterCard" href="#" style="margin-right:1em;width:' + width + 'px;" data-position="' + c . StartPositionTicks + '">' ;
chapterHtml += '<div class="cardBox">' ;
2016-08-23 23:13:15 -07:00
chapterHtml += '<div class="cardScalable visualCardBox-cardScalable">' ;
2016-08-11 13:28:49 -07:00
chapterHtml += '<div class="cardPadder cardPadder-backdrop"></div>' ;
2015-05-13 20:24:25 -07:00
chapterHtml += '<div class="cardContent">' ;
if ( c . ImageTag ) {
var chapterImageUrl = ApiClient . getScaledImageUrl ( item . Id , {
width : width ,
tag : c . ImageTag ,
type : "Chapter" ,
index : chapterIndex
} ) ;
chapterHtml += '<div class="cardImage lazy" data-src="' + chapterImageUrl + '"></div>' ;
} else {
chapterHtml += '<div class="cardImage" style="background:#444;"></div>' ;
}
chapterHtml += '<div class="cardFooter">' ;
if ( c . Name ) {
chapterHtml += '<div class="cardText">' + c . Name + '</div>' ;
}
2016-05-05 20:09:36 -07:00
chapterHtml += '<div class="cardText">' + datetime . getDisplayRunningTime ( c . StartPositionTicks ) + '</div>' ;
2015-05-13 20:24:25 -07:00
chapterHtml += '</div>' ;
chapterHtml += '</div>' ;
chapterHtml += '</div>' ;
chapterHtml += '</div>' ;
chapterHtml += '</a>' ;
chapterIndex ++ ;
return chapterHtml ;
} ) . join ( '' ) ;
html += '</div>' ;
}
if ( item . People && item . People . length ) {
2016-06-06 17:44:15 -07:00
html += '<div class="tabCast nowPlayingTab smoothScrollX hide" style="white-space:nowrap;">' ;
2015-05-13 20:24:25 -07:00
html += item . People . map ( function ( cast ) {
var personHtml = '<div class="tileItem smallPosterTileItem" style="width:300px;">' ;
var imgUrl ;
2015-07-03 04:51:45 -07:00
var height = 150 ;
2015-05-13 20:24:25 -07:00
if ( cast . PrimaryImageTag ) {
imgUrl = ApiClient . getScaledImageUrl ( cast . Id , {
2015-05-27 22:51:48 -07:00
height : height ,
2015-05-13 20:24:25 -07:00
tag : cast . PrimaryImageTag ,
type : "primary" ,
minScale : 2
} ) ;
2015-09-11 19:18:09 -07:00
personHtml += '<div class="tileImage lazy" data-src="' + imgUrl + '" style="height:' + height + 'px;"></div>' ;
2015-05-13 20:24:25 -07:00
} else {
imgUrl = "css/images/items/list/person.png" ;
2015-09-11 19:18:09 -07:00
personHtml += '<div class="tileImage" style="background-image:url(\'' + imgUrl + '\');height:' + height + 'px;"></div>' ;
2015-05-13 20:24:25 -07:00
}
personHtml += '<div class="tileContent">' ;
personHtml += '<p>' + cast . Name + '</p>' ;
var role = cast . Role ? Globalize . translate ( 'ValueAsRole' , cast . Role ) : cast . Type ;
if ( role == "GuestStar" ) {
role = Globalize . translate ( 'ValueGuestStar' ) ;
}
role = role || "" ;
var maxlength = 40 ;
if ( role . length > maxlength ) {
role = role . substring ( 0 , maxlength - 3 ) + '...' ;
}
personHtml += '<p>' + role + '</p>' ;
personHtml += '</div>' ;
personHtml += '</div>' ;
return personHtml ;
} ) . join ( '' ) ;
html += '</div>' ;
}
return html ;
}
2014-06-28 12:35:30 -07:00
2014-04-11 08:36:25 -07:00
function onPositionSliderChange ( ) {
2016-08-19 22:22:08 -07:00
var newPercent = parseFloat ( this . value ) ;
2014-04-11 08:36:25 -07:00
2016-09-18 09:08:32 -07:00
var newPositionTicks = ( newPercent / 100 ) * self . getSeekableDurationTicks ( ) ;
2014-04-11 08:36:25 -07:00
self . changeStream ( Math . floor ( newPositionTicks ) ) ;
}
2015-06-27 12:53:36 -07:00
self . onAudioOptionSelected = function ( index ) {
2015-05-21 13:53:14 -07:00
2015-06-27 12:53:36 -07:00
self . setAudioStreamIndex ( index ) ;
} ;
2015-05-22 13:15:29 -07:00
2015-06-27 12:53:36 -07:00
self . onSubtitleOptionSelected = function ( index ) {
self . setSubtitleStreamIndex ( index ) ;
2015-05-22 13:15:29 -07:00
} ;
2015-05-21 13:53:14 -07:00
2015-06-27 12:53:36 -07:00
self . onQualityOptionSelected = function ( bitrate ) {
2015-05-21 13:53:14 -07:00
2016-02-29 23:02:03 -07:00
appSettings . maxStreamingBitrate ( bitrate ) ;
appSettings . enableAutomaticBitrateDetection ( false ) ;
2015-05-21 13:53:14 -07:00
2015-06-27 12:53:36 -07:00
self . changeStream ( self . getCurrentTicks ( ) , {
Bitrate : bitrate
} ) ;
2015-05-22 13:15:29 -07:00
} ;
2015-08-28 12:10:44 -07:00
self . toggleInfo = function ( ) {
var button = document . querySelector ( '.mediaButton.infoButton' ) ;
var nowPlayingInfo = document . querySelector ( '.videoControls .nowPlayingInfo' ) ;
if ( button . classList . contains ( 'active' ) ) {
button . classList . remove ( 'active' ) ;
document . querySelector ( '.videoControls' ) . classList . add ( 'hiddenOnIdle' ) ;
fadeOutDown ( nowPlayingInfo ) ;
} else {
button . classList . add ( 'active' ) ;
document . querySelector ( '.videoControls' ) . classList . remove ( 'hiddenOnIdle' ) ;
nowPlayingInfo . classList . remove ( 'hide' ) ;
fadeInUp ( nowPlayingInfo ) ;
}
} ;
2015-10-08 12:12:53 -07:00
self . toggleGuide = function ( ) {
var button = document . querySelector ( '.mediaButton.guideButton' ) ;
var nowPlayingInfo = document . querySelector ( '.videoControls .guide' ) ;
if ( button . classList . contains ( 'active' ) ) {
button . classList . remove ( 'active' ) ;
document . querySelector ( '.videoControls' ) . classList . add ( 'hiddenOnIdle' ) ;
fadeOutDown ( nowPlayingInfo ) ;
} else {
button . classList . add ( 'active' ) ;
document . querySelector ( '.videoControls' ) . classList . remove ( 'hiddenOnIdle' ) ;
nowPlayingInfo . classList . remove ( 'hide' ) ;
fadeInUp ( nowPlayingInfo ) ;
if ( ! self . guideInstance ) {
require ( [ 'tvguide' ] , function ( tvguide ) {
self . guideInstance = new tvguide ( {
element : nowPlayingInfo ,
enablePaging : false
} ) ;
} ) ;
}
}
} ;
2015-08-28 12:10:44 -07:00
function fadeInUp ( elem ) {
var keyframes = [
{ transform : 'translate3d(0, 100%, 0)' , offset : 0 } ,
2016-04-11 21:03:07 -07:00
{ transform : 'translate3d(0, 0, 0)' , offset : 1 } ] ;
2015-08-28 12:10:44 -07:00
var timing = { duration : 300 , iterations : 1 } ;
2016-04-11 21:03:07 -07:00
if ( elem . animate ) {
elem . animate ( keyframes , timing ) ;
}
2015-08-28 12:10:44 -07:00
}
function fadeOutDown ( elem ) {
2016-04-11 21:03:07 -07:00
var keyframes = [ { transform : 'translate3d(0, 0, 0)' , offset : 0 } ,
2015-08-28 12:10:44 -07:00
{ transform : 'translate3d(0, 100%, 0)' , offset : 1 } ] ;
var timing = { duration : 300 , iterations : 1 } ;
2016-04-11 21:03:07 -07:00
var onFinish = function ( ) {
2015-08-28 12:10:44 -07:00
elem . classList . add ( 'hide' ) ;
} ;
2016-04-11 21:03:07 -07:00
if ( elem . animate ) {
elem . animate ( keyframes , timing ) . onfinish = onFinish ;
} else {
onFinish ( ) ;
}
2015-08-28 12:10:44 -07:00
}
2015-06-27 12:53:36 -07:00
function ensureVideoPlayerElements ( ) {
2015-05-22 13:15:29 -07:00
2015-12-14 08:43:03 -07:00
var html = '' ;
2015-05-22 13:15:29 -07:00
2015-12-14 08:43:03 -07:00
html += '<div id="videoPlayer" class="hide">' ;
2015-05-22 13:15:29 -07:00
2015-06-27 12:53:36 -07:00
html += '<div id="videoElement">' ;
html += '<div id="play" class="status"></div>' ;
html += '<div id="pause" class="status"></div>' ;
html += '</div>' ;
2015-05-22 13:15:29 -07:00
2016-09-18 13:38:38 -07:00
var hiddenOnIdleClass = AppInfo . isNativeApp && browser . android ? 'hiddenOnIdle hide' : 'hiddenOnIdle' ;
2016-05-09 22:00:50 -07:00
html += '<div class="videoTopControls ' + hiddenOnIdleClass + '">' ;
2015-06-27 12:53:36 -07:00
html += '<div class="videoTopControlsLogo"></div>' ;
html += '<div class="videoAdvancedControls">' ;
2015-05-22 13:15:29 -07:00
2016-06-14 20:12:32 -07:00
html += '<button is="paper-icon-button-light" class="previousTrackButton mediaButton videoTrackControl hide autoSize" onclick="MediaPlayer.previousTrack();"><i class="md-icon">skip_previous</i></button>' ;
html += '<button is="paper-icon-button-light" class="nextTrackButton mediaButton videoTrackControl hide autoSize" onclick="MediaPlayer.nextTrack();"><i class="md-icon">skip_next</i></button>' ;
2015-05-21 13:53:14 -07:00
2015-06-27 12:53:36 -07:00
// Embedding onclicks due to issues not firing in cordova safari
2016-06-14 20:12:32 -07:00
html += '<button is="paper-icon-button-light" class="mediaButton videoAudioButton autoSize" onclick="MediaPlayer.showAudioTracksFlyout();"><i class="md-icon">audiotrack</i></button>' ;
html += '<button is="paper-icon-button-light" class="mediaButton videoSubtitleButton autoSize" onclick="MediaPlayer.showSubtitleMenu();"><i class="md-icon">closed_caption</i></button>' ;
html += '<button is="paper-icon-button-light" class="mediaButton videoQualityButton autoSize" onclick="MediaPlayer.showQualityFlyout();"><i class="md-icon">settings</i></button>' ;
html += '<button is="paper-icon-button-light" class="mediaButton autoSize" onclick="MediaPlayer.stop();"><i class="md-icon">close</i></button>' ;
2014-04-11 08:36:25 -07:00
2015-06-27 12:53:36 -07:00
html += '</div>' ; // videoAdvancedControls
html += '</div>' ; // videoTopControls
2014-04-11 08:36:25 -07:00
2015-06-27 12:53:36 -07:00
// Create controls
2016-05-09 22:00:50 -07:00
html += '<div class="videoControls ' + hiddenOnIdleClass + '">' ;
2014-04-11 08:36:25 -07:00
2015-08-28 12:10:44 -07:00
html += '<div class="nowPlayingInfo hide">' ;
2015-06-27 12:53:36 -07:00
html += '<div class="nowPlayingImage"></div>' ;
html += '<div class="nowPlayingTabs"></div>' ;
html += '</div>' ; // nowPlayingInfo
2014-04-11 08:36:25 -07:00
2015-10-08 12:12:53 -07:00
html += '<div class="guide hide">' ;
html += '</div>' ; // guide
2015-08-28 12:10:44 -07:00
html += '<div class="videoControlButtons">' ;
2016-06-14 20:12:32 -07:00
html += '<button is="paper-icon-button-light" class="previousTrackButton mediaButton videoTrackControl hide autoSize" onclick="MediaPlayer.previousTrack();"><i class="md-icon">skip_previous</i></button>' ;
html += '<button is="paper-icon-button-light" id="video-playButton" class="mediaButton unpauseButton autoSize" onclick="MediaPlayer.unpause();"><i class="md-icon">play_arrow</i></button>' ;
html += '<button is="paper-icon-button-light" id="video-pauseButton" class="mediaButton pauseButton autoSize" onclick="MediaPlayer.pause();"><i class="md-icon">pause</i></button>' ;
html += '<button is="paper-icon-button-light" class="nextTrackButton mediaButton videoTrackControl hide autoSize" onclick="MediaPlayer.nextTrack();"><i class="md-icon">skip_next</i></button>' ;
2014-03-31 14:39:12 -07:00
2016-06-13 12:02:48 -07:00
html += '<div class="sliderContainer videoPositionSliderContainer" style="display:inline-flex;margin-right:2em;">' ;
html += '<input type="range" is="emby-slider" pin step=".1" min="0" max="100" value="0" class="videoPositionSlider" />' ;
html += '</div>' ; // guide
2014-03-31 14:39:12 -07:00
2015-06-27 12:53:36 -07:00
html += '<div class="currentTime">--:--</div>' ;
2014-03-31 14:39:12 -07:00
2016-06-14 20:12:32 -07:00
html += '<button is="paper-icon-button-light" class="muteButton mediaButton autoSize" onclick="MediaPlayer.mute();"><i class="md-icon">volume_up</i></button>' ;
html += '<button is="paper-icon-button-light" class="unmuteButton mediaButton autoSize" onclick="MediaPlayer.unMute();"><i class="md-icon">volume_off</i></button>' ;
2014-03-31 14:39:12 -07:00
2016-06-24 09:51:13 -07:00
html += '<div class="sliderContainer volumeSliderContainer" style="width:100px;vertical-align:middle;;margin-right:2em;display:inline-flex;">' ;
2016-06-13 12:02:48 -07:00
html += '<input type="range" is="emby-slider" pin step="1" min="0" max="100" value="0" class="videoVolumeSlider"/>' ;
html += '</div>' ; // guide
2014-03-31 14:39:12 -07:00
2016-08-06 23:38:46 -07:00
html += '<button is="paper-icon-button-light" class="mediaButton castButton autoSize" onclick="MediaController.showPlayerSelection(this, false);"><i class="md-icon">cast</i></button>' ;
2016-06-14 20:12:32 -07:00
html += '<button is="paper-icon-button-light" class="mediaButton fullscreenButton autoSize" onclick="MediaPlayer.toggleFullscreen();" id="video-fullscreenButton"><i class="md-icon">fullscreen</i></button>' ;
html += '<button is="paper-icon-button-light" class="mediaButton infoButton autoSize" onclick="MediaPlayer.toggleInfo();"><i class="md-icon">info</i></button>' ;
2016-05-07 10:47:41 -07:00
2015-08-28 12:10:44 -07:00
html += '</div>' ;
2014-03-31 14:39:12 -07:00
2015-06-27 12:53:36 -07:00
html += '</div>' ; // videoControls
2014-03-31 14:39:12 -07:00
2015-06-27 12:53:36 -07:00
html += '</div>' ; // videoPlayer
2014-03-31 14:39:12 -07:00
2015-06-29 11:45:42 -07:00
var div = document . createElement ( 'div' ) ;
div . innerHTML = html ;
document . body . appendChild ( div ) ;
2015-06-27 12:53:36 -07:00
}
2015-12-14 08:43:03 -07:00
var initComplete ;
function initVideoElements ( ) {
2015-06-27 12:53:36 -07:00
2015-12-14 08:43:03 -07:00
if ( initComplete ) {
return ;
}
initComplete = true ;
2015-06-27 12:53:36 -07:00
ensureVideoPlayerElements ( ) ;
2016-06-06 17:44:15 -07:00
var parent = document . querySelector ( "#videoPlayer" ) ;
2015-06-27 12:53:36 -07:00
2016-06-06 17:44:15 -07:00
muteButton = parent . querySelector ( '.muteButton' ) ;
unmuteButton = parent . querySelector ( '.unmuteButton' ) ;
currentTimeElement = parent . querySelector ( '.currentTime' ) ;
2015-06-27 12:53:36 -07:00
2016-06-06 17:44:15 -07:00
positionSlider = parent . querySelector ( ".videoPositionSlider" , parent ) ;
positionSlider . addEventListener ( 'change' , onPositionSliderChange ) ;
2015-06-27 12:53:36 -07:00
2016-06-13 23:40:21 -07:00
positionSlider . getBubbleText = function ( value ) {
2015-06-27 12:53:36 -07:00
2016-09-18 09:08:32 -07:00
var seekableDuration = self . getSeekableDurationTicks ( ) ;
2015-09-09 10:49:44 -07:00
if ( ! self . currentMediaSource || ! seekableDuration ) {
2016-06-13 23:40:21 -07:00
return '--:--' ;
2015-06-27 12:53:36 -07:00
}
2015-09-09 10:49:44 -07:00
var ticks = seekableDuration ;
2015-06-27 12:53:36 -07:00
ticks /= 100 ;
ticks *= value ;
2016-06-13 23:40:21 -07:00
return datetime . getDisplayRunningTime ( ticks ) ;
2015-06-27 12:53:36 -07:00
} ;
2016-06-06 17:44:15 -07:00
volumeSlider = parent . querySelector ( '.videoVolumeSlider' ) ;
2016-06-24 09:51:13 -07:00
volumeSliderContainer = parent . querySelector ( '.volumeSliderContainer' ) ;
2016-06-06 17:44:15 -07:00
volumeSlider . addEventListener ( 'change' , function ( ) {
2015-06-27 12:53:36 -07:00
var vol = this . value ;
updateVolumeButtons ( vol ) ;
self . setVolume ( vol ) ;
2016-06-06 17:44:15 -07:00
} ) ;
2015-12-14 08:43:03 -07:00
}
2014-03-08 01:09:45 -07:00
2015-05-25 10:32:22 -07:00
var idleHandlerTimeout ;
2014-03-18 12:57:32 -07:00
function idleHandler ( ) {
2014-06-21 22:52:31 -07:00
2015-05-25 10:32:22 -07:00
if ( idleHandlerTimeout ) {
window . clearTimeout ( idleHandlerTimeout ) ;
2014-03-18 12:57:32 -07:00
}
if ( idleState == true ) {
2016-06-06 17:44:15 -07:00
setClass ( document . querySelectorAll ( '.hiddenOnIdle' ) , 'remove' , 'inactive' ) ;
document . querySelector ( '#videoPlayer' ) . classList . remove ( 'idlePlayer' ) ;
2014-03-18 12:57:32 -07:00
}
idleState = false ;
2015-05-25 10:32:22 -07:00
idleHandlerTimeout = window . setTimeout ( function ( ) {
2014-03-18 12:57:32 -07:00
idleState = true ;
2016-06-06 17:44:15 -07:00
setClass ( document . querySelectorAll ( '.hiddenOnIdle' ) , 'add' , 'inactive' ) ;
document . querySelector ( '#videoPlayer' ) . classList . add ( 'idlePlayer' ) ;
2015-06-26 08:53:49 -07:00
} , 3500 ) ;
2014-04-06 19:06:09 -07:00
}
2014-03-18 12:57:32 -07:00
2014-04-11 08:36:25 -07:00
function updateVolumeButtons ( vol ) {
2016-06-24 09:51:13 -07:00
if ( ! AppInfo . hasPhysicalVolumeButtons ) {
if ( vol ) {
muteButton . classList . remove ( 'hide' ) ;
unmuteButton . classList . add ( 'hide' ) ;
} else {
muteButton . classList . add ( 'hide' ) ;
unmuteButton . classList . remove ( 'hide' ) ;
}
2014-04-11 08:36:25 -07:00
}
}
2014-03-08 01:09:45 -07:00
function requestFullScreen ( element ) {
2014-03-20 12:12:10 -07:00
2014-03-08 01:09:45 -07:00
// Supports most browsers and their versions.
2015-05-17 18:27:48 -07:00
var requestMethod = element . requestFullscreen || element . webkitRequestFullscreen || element . mozRequestFullScreen || element . msRequestFullscreen ;
2014-03-08 01:09:45 -07:00
if ( requestMethod ) { // Native full screen.
requestMethod . call ( element ) ;
} else {
enterFullScreen ( ) ;
}
2014-03-20 12:12:10 -07:00
2014-04-06 19:06:09 -07:00
}
2014-03-08 01:09:45 -07:00
function enterFullScreen ( ) {
2016-06-06 17:44:15 -07:00
document . querySelector ( "#videoPlayer" ) . classList . add ( "fullscreenVideo" ) ;
2014-04-06 19:06:09 -07:00
}
2014-03-08 01:09:45 -07:00
function exitFullScreenToWindow ( ) {
2016-06-06 17:44:15 -07:00
document . querySelector ( "#videoPlayer" ) . classList . remove ( "fullscreenVideo" ) ;
2014-04-06 19:06:09 -07:00
}
2014-03-08 01:09:45 -07:00
2015-06-29 11:45:42 -07:00
function onPopState ( ) {
// Stop playback on browser back button nav
2016-06-04 09:10:10 -07:00
window . removeEventListener ( "popstate" , onPopState ) ;
2015-06-29 11:45:42 -07:00
self . stop ( ) ;
return ;
}
2014-06-21 22:52:31 -07:00
2015-06-29 11:45:42 -07:00
function onFullScreenChange ( ) {
if ( self . isFullScreen ( ) ) {
enterFullScreen ( ) ;
idleState = true ;
} else {
exitFullScreenToWindow ( ) ;
2014-07-03 19:22:57 -07:00
}
2015-06-29 11:45:42 -07:00
}
2014-07-03 19:22:57 -07:00
2015-10-23 09:04:33 -07:00
var lastMousePosition = { } ;
function onMouseMove ( evt ) {
if ( evt . clientX == lastMousePosition . x && evt . clientY == lastMousePosition . y ) {
return ;
}
lastMousePosition . x = evt . clientX ;
lastMousePosition . y = evt . clientY ;
idleHandler ( ) ;
}
2015-06-29 11:45:42 -07:00
function bindEventsForPlayback ( mediaRenderer ) {
2014-06-21 22:52:31 -07:00
2015-12-23 10:46:01 -07:00
Events . on ( mediaRenderer , 'playing' , onOnePlaying ) ;
Events . on ( mediaRenderer , 'playing' , onPlaying ) ;
Events . on ( mediaRenderer , 'volumechange' , onVolumeChange ) ;
Events . on ( mediaRenderer , 'pause' , onPause ) ;
Events . on ( mediaRenderer , 'timeupdate' , onTimeUpdate ) ;
Events . on ( mediaRenderer , 'error' , onError ) ;
Events . on ( mediaRenderer , 'click' , onClick ) ;
Events . on ( mediaRenderer , 'dblclick' , onDoubleClick ) ;
2015-06-29 11:45:42 -07:00
var hideElementsOnIdle = true ;
2014-06-21 22:52:31 -07:00
2015-06-29 11:45:42 -07:00
if ( hideElementsOnIdle ) {
var itemVideo = document . querySelector ( '.itemVideo' ) ;
if ( itemVideo ) {
2015-10-23 09:04:33 -07:00
//Events.on(itemVideo, 'mousemove', onMouseMove);
2015-12-14 08:43:03 -07:00
itemVideo . addEventListener ( 'keydown' , idleHandler ) ;
itemVideo . addEventListener ( 'scroll' , idleHandler ) ;
itemVideo . addEventListener ( 'mousedown' , idleHandler ) ;
2015-06-29 11:45:42 -07:00
idleHandler ( ) ;
2014-06-21 22:52:31 -07:00
}
2015-06-29 11:45:42 -07:00
}
2014-06-21 22:52:31 -07:00
2016-06-04 09:10:10 -07:00
document . addEventListener ( 'webkitfullscreenchange' , onFullScreenChange ) ;
document . addEventListener ( 'mozfullscreenchange' , onFullScreenChange ) ;
document . addEventListener ( 'msfullscreenchange' , onFullScreenChange ) ;
document . addEventListener ( 'fullscreenchange' , onFullScreenChange ) ;
2014-06-21 22:52:31 -07:00
2016-06-04 09:10:10 -07:00
window . addEventListener ( "popstate" , onPopState ) ;
2014-06-21 22:52:31 -07:00
2015-06-29 11:45:42 -07:00
if ( hideElementsOnIdle ) {
2016-06-04 09:10:10 -07:00
document . body . addEventListener ( "mousemove" , onMouseMove ) ;
2014-07-03 19:22:57 -07:00
}
2014-06-21 22:52:31 -07:00
}
2015-06-29 11:45:42 -07:00
function unbindEventsForPlayback ( mediaRenderer ) {
2014-06-21 22:52:31 -07:00
2015-12-23 10:46:01 -07:00
Events . off ( mediaRenderer , 'playing' , onOnePlaying ) ;
Events . off ( mediaRenderer , 'playing' , onPlaying ) ;
Events . off ( mediaRenderer , 'volumechange' , onVolumeChange ) ;
Events . off ( mediaRenderer , 'pause' , onPause ) ;
Events . off ( mediaRenderer , 'timeupdate' , onTimeUpdate ) ;
Events . off ( mediaRenderer , 'error' , onError ) ;
Events . off ( mediaRenderer , 'click' , onClick ) ;
Events . off ( mediaRenderer , 'dblclick' , onDoubleClick ) ;
2016-06-04 09:10:10 -07:00
document . removeEventListener ( 'webkitfullscreenchange' , onFullScreenChange ) ;
document . removeEventListener ( 'mozfullscreenchange' , onFullScreenChange ) ;
document . removeEventListener ( 'msfullscreenchange' , onFullScreenChange ) ;
document . removeEventListener ( 'fullscreenchange' , onFullScreenChange ) ;
2014-06-21 22:52:31 -07:00
// Stop playback on browser back button nav
2016-06-04 09:10:10 -07:00
window . removeEventListener ( "popstate" , onPopState ) ;
2014-06-21 22:52:31 -07:00
2016-06-04 09:10:10 -07:00
document . body . removeEventListener ( "mousemove" , onMouseMove ) ;
2014-06-21 22:52:31 -07:00
2015-06-29 11:45:42 -07:00
var itemVideo = document . querySelector ( '.itemVideo' ) ;
if ( itemVideo ) {
2015-10-23 09:04:33 -07:00
//Events.off(itemVideo, 'mousemove', onMouseMove);
2015-12-14 08:43:03 -07:00
itemVideo . removeEventListener ( 'keydown' , idleHandler ) ;
itemVideo . removeEventListener ( 'scroll' , idleHandler ) ;
itemVideo . removeEventListener ( 'mousedown' , idleHandler ) ;
2015-06-29 11:45:42 -07:00
}
2014-06-21 22:52:31 -07:00
}
2014-08-10 15:13:17 -07:00
// Replace audio version
2015-06-07 13:06:18 -07:00
self . cleanup = function ( mediaRenderer ) {
2014-08-10 15:13:17 -07:00
2015-12-14 08:43:03 -07:00
if ( currentTimeElement ) {
2016-06-06 17:44:15 -07:00
currentTimeElement . innerHTML = '--:--' ;
2015-12-14 08:43:03 -07:00
}
2014-08-10 15:13:17 -07:00
2015-06-29 11:45:42 -07:00
unbindEventsForPlayback ( mediaRenderer ) ;
2014-08-10 15:13:17 -07:00
} ;
2015-07-08 09:10:34 -07:00
self . playVideo = function ( item , mediaSource , startPosition , callback ) {
2014-03-08 01:09:45 -07:00
2016-09-18 13:38:38 -07:00
if ( browser . msie ) {
2016-07-31 12:03:38 -07:00
2016-09-19 08:41:35 -07:00
if ( window . MediaSource == null || mediaSource . RunTimeTicks == null ) {
2016-07-31 12:03:38 -07:00
alert ( 'Playback of this content is not supported in Internet Explorer. For a better experience, please try a modern browser such as Google Chrome, Firefox, Opera, or Microsoft Edge.' ) ;
2016-09-19 08:41:35 -07:00
return ;
2016-07-31 12:03:38 -07:00
}
}
2015-12-14 08:43:03 -07:00
// TODO: remove dependency on nowplayingbar
2016-06-13 12:02:48 -07:00
requirejs ( [ 'videorenderer' , 'css!css/nowplayingbar.css' , 'css!css/mediaplayer-video.css' , 'emby-slider' ] , function ( ) {
2015-12-14 08:43:03 -07:00
initVideoElements ( ) ;
2015-03-26 11:23:46 -07:00
2015-12-14 08:43:03 -07:00
self . createStreamInfo ( 'Video' , item , mediaSource , startPosition ) . then ( function ( streamInfo ) {
2015-04-28 06:56:57 -07:00
2016-09-18 13:38:38 -07:00
var onReadyToPlay = function ( ) {
self . playVideoInternal ( item , mediaSource , startPosition , streamInfo , callback ) ;
} ;
2016-01-13 22:18:46 -07:00
var isHls = streamInfo . url . toLowerCase ( ) . indexOf ( '.m3u8' ) != - 1 ;
2015-09-21 08:43:10 -07:00
// Huge hack alert. Safari doesn't seem to like if the segments aren't available right away when playback starts
// This will start the transcoding process before actually feeding the video url into the player
2016-01-13 22:18:46 -07:00
// Edit: Also seeing stalls from hls.js
2016-09-26 22:13:56 -07:00
if ( ! mediaSource . RunTimeTicks && isHls ) {
2015-04-28 06:56:57 -07:00
2015-12-30 11:02:44 -07:00
var hlsPlaylistUrl = streamInfo . url . replace ( 'master.m3u8' , 'live.m3u8' ) ;
2016-09-18 13:38:38 -07:00
Dashboard . showLoadingMsg ( ) ;
2015-09-21 08:43:10 -07:00
ApiClient . ajax ( {
2015-12-14 08:43:03 -07:00
2015-09-21 08:43:10 -07:00
type : 'GET' ,
2015-12-30 11:02:44 -07:00
url : hlsPlaylistUrl
2015-08-24 13:37:34 -07:00
2015-12-14 08:43:03 -07:00
} ) . then ( function ( ) {
2015-09-21 08:43:10 -07:00
Dashboard . hideLoadingMsg ( ) ;
2015-12-30 11:02:44 -07:00
streamInfo . url = hlsPlaylistUrl ;
2016-04-14 12:12:00 -07:00
2016-09-19 08:41:35 -07:00
setTimeout ( onReadyToPlay , 0 ) ;
2016-04-14 12:12:00 -07:00
2015-12-14 08:43:03 -07:00
} , function ( ) {
Dashboard . hideLoadingMsg ( ) ;
2015-09-21 08:43:10 -07:00
} ) ;
2015-04-28 06:56:57 -07:00
2015-09-21 08:43:10 -07:00
} else {
2016-09-18 13:38:38 -07:00
onReadyToPlay ( ) ;
2015-09-21 08:43:10 -07:00
}
} ) ;
2015-06-07 20:16:42 -07:00
} ) ;
2015-04-28 06:56:57 -07:00
} ;
2015-12-14 08:43:03 -07:00
function fadeOut ( elem ) {
if ( elem . classList . contains ( 'hide' ) ) {
return ;
}
var onfinish = function ( ) {
elem . classList . add ( 'hide' ) ;
} ;
2015-12-30 11:02:44 -07:00
//if (!browserInfo.animate) {
2015-12-30 13:25:17 -07:00
onfinish ( ) ;
return ;
2015-12-30 11:02:44 -07:00
//}
2015-12-14 08:43:03 -07:00
requestAnimationFrame ( function ( ) {
var keyframes = [
{ opacity : '1' , offset : 0 } ,
{ opacity : '0' , offset : 1 } ] ;
var timing = { duration : 600 , iterations : 1 , easing : 'ease-out' } ;
elem . animate ( keyframes , timing ) . onfinish = onfinish ;
} ) ;
}
function fadeIn ( elem ) {
if ( ! elem . classList . contains ( 'hide' ) ) {
return ;
}
elem . classList . remove ( 'hide' ) ;
2016-09-18 13:38:38 -07:00
if ( ! browser . animate || browser . slow ) {
2015-12-14 08:43:03 -07:00
return ;
}
requestAnimationFrame ( function ( ) {
var keyframes = [
{ transform : 'scale3d(.2, .2, .2) ' , opacity : '.6' , offset : 0 } ,
{ transform : 'none' , opacity : '1' , offset : 1 }
] ;
var timing = { duration : 200 , iterations : 1 , easing : 'ease-out' } ;
elem . animate ( keyframes , timing ) ;
} ) ;
}
2015-07-08 09:10:34 -07:00
self . playVideoInternal = function ( item , mediaSource , startPosition , streamInfo , callback ) {
2015-04-28 06:56:57 -07:00
2015-03-26 11:23:46 -07:00
self . startTimeTicksOffset = streamInfo . startTimeTicksOffset ;
2014-03-08 01:09:45 -07:00
2015-03-26 09:58:02 -07:00
var mediaStreams = mediaSource . MediaStreams || [ ] ;
2014-06-11 12:31:33 -07:00
var subtitleStreams = mediaStreams . filter ( function ( s ) {
return s . Type == 'Subtitle' ;
} ) ;
2014-03-20 01:34:54 -07:00
// Create video player
2015-12-14 08:43:03 -07:00
var mediaPlayerContainer = document . querySelector ( '#videoPlayer' ) ;
fadeIn ( mediaPlayerContainer ) ;
2016-06-06 17:44:15 -07:00
var videoControls = mediaPlayerContainer . querySelector ( '.videoControls' ) ;
2014-03-08 01:09:45 -07:00
//show stop button
2016-06-06 17:44:15 -07:00
document . querySelector ( '#video-playButton' ) . classList . add ( 'hide' ) ;
document . querySelector ( '#video-pauseButton' ) . classList . remove ( 'hide' ) ;
2014-06-21 22:52:31 -07:00
2016-06-06 17:44:15 -07:00
document . querySelector ( '.videoTrackControl' ) . classList . add ( 'hide' ) ;
document . querySelector ( '.videoQualityButton' ) . classList . remove ( 'hide' ) ;
2014-03-08 01:09:45 -07:00
2014-06-11 12:31:33 -07:00
if ( mediaStreams . filter ( function ( s ) {
return s . Type == "Audio" ;
2014-03-08 01:09:45 -07:00
} ) . length ) {
2016-06-06 17:44:15 -07:00
document . querySelector ( '.videoAudioButton' ) . classList . remove ( 'hide' ) ;
2014-03-08 01:09:45 -07:00
} else {
2016-06-06 17:44:15 -07:00
document . querySelector ( '.videoAudioButton' ) . classList . add ( 'hide' ) ;
2014-03-08 01:09:45 -07:00
}
2014-06-11 12:31:33 -07:00
if ( subtitleStreams . length ) {
2016-06-06 17:44:15 -07:00
document . querySelector ( '.videoSubtitleButton' ) . classList . remove ( 'hide' ) ;
2014-03-08 01:09:45 -07:00
} else {
2016-06-06 17:44:15 -07:00
document . querySelector ( '.videoSubtitleButton' ) . classList . add ( 'hide' ) ;
2014-03-08 01:09:45 -07:00
}
2015-07-03 09:49:49 -07:00
var mediaRenderer = new VideoRenderer ( {
2015-09-09 20:22:52 -07:00
2015-07-03 09:49:49 -07:00
poster : self . getPosterUrl ( item )
} ) ;
2015-07-03 04:51:45 -07:00
var requiresNativeControls = ! mediaRenderer . enableCustomVideoControls ( ) ;
2015-09-08 07:35:52 -07:00
if ( requiresNativeControls || AppInfo . isNativeApp ) {
2016-06-06 17:44:15 -07:00
videoControls . querySelector ( '#video-fullscreenButton' ) . classList . add ( 'hide' ) ;
2014-03-08 01:09:45 -07:00
} else {
2016-06-06 17:44:15 -07:00
videoControls . querySelector ( '#video-fullscreenButton' ) . classList . remove ( 'hide' ) ;
2014-03-08 01:09:45 -07:00
}
2015-05-21 13:53:14 -07:00
if ( AppInfo . hasPhysicalVolumeButtons ) {
2016-06-24 09:51:13 -07:00
volumeSliderContainer . classList . add ( 'hide' ) ;
2016-06-06 17:44:15 -07:00
videoControls . querySelector ( '.muteButton' ) . classList . add ( 'hide' ) ;
videoControls . querySelector ( '.unmuteButton' ) . classList . add ( 'hide' ) ;
2014-06-28 12:35:30 -07:00
} else {
2016-06-24 09:51:13 -07:00
volumeSliderContainer . classList . remove ( 'hide' ) ;
2016-06-06 17:44:15 -07:00
videoControls . querySelector ( '.muteButton' ) . classList . remove ( 'hide' ) ;
videoControls . querySelector ( '.unmuteButton' ) . classList . remove ( 'hide' ) ;
2014-06-28 12:35:30 -07:00
}
if ( requiresNativeControls ) {
2016-06-06 17:44:15 -07:00
videoControls . classList . add ( 'hide' ) ;
2014-06-28 12:35:30 -07:00
} else {
2016-06-06 17:44:15 -07:00
videoControls . classList . remove ( 'hide' ) ;
2014-06-28 12:35:30 -07:00
}
2014-04-11 08:36:25 -07:00
initialVolume = self . getSavedVolume ( ) ;
2014-03-08 01:09:45 -07:00
2015-06-07 13:06:18 -07:00
mediaRenderer . volume ( initialVolume ) ;
2014-03-08 01:09:45 -07:00
2015-06-27 12:53:36 -07:00
volumeSlider . value = initialVolume * 100 ;
2014-04-11 08:36:25 -07:00
updateVolumeButtons ( initialVolume ) ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
bindEventsForPlayback ( mediaRenderer ) ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
self . currentSubtitleStreamIndex = mediaSource . DefaultSubtitleStreamIndex ;
2014-03-08 01:09:45 -07:00
2016-06-06 17:44:15 -07:00
document . body . classList . add ( 'bodyWithPopupOpen' ) ;
2014-08-10 15:13:17 -07:00
2015-12-23 10:46:01 -07:00
self . currentMediaRenderer = mediaRenderer ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
self . updateNowPlayingInfo ( item ) ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
mediaRenderer . init ( ) . then ( function ( ) {
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
self . onBeforePlaybackStart ( mediaRenderer , item , mediaSource ) ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
self . setSrcIntoRenderer ( mediaRenderer , streamInfo , item , self . currentMediaSource ) ;
self . streamInfo = streamInfo ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
if ( callback ) {
callback ( ) ;
}
} ) ;
} ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
function onOnePlaying ( ) {
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
Events . off ( this , 'playing' , onOnePlaying ) ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
// For some reason this is firing at the start, so don't bind until playback has begun
Events . on ( this , 'ended' , self . onPlaybackStopped ) ;
Events . on ( this , 'ended' , self . playNextAfterEnded ) ;
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
self . onPlaybackStart ( this , self . currentItem , self . currentMediaSource ) ;
}
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
function onPlaying ( ) {
2014-07-21 18:29:06 -07:00
2015-12-23 10:46:01 -07:00
var videoControls = document . querySelector ( '#videoPlayer .videoControls' ) ;
var videoElement = document . querySelector ( '#videoPlayer #videoElement' ) ;
2014-03-08 01:09:45 -07:00
2016-06-06 17:44:15 -07:00
videoControls . querySelector ( '#video-playButton' ) . classList . add ( 'hide' ) ;
videoControls . querySelector ( '#video-pauseButton' ) . classList . remove ( 'hide' ) ;
var buttonToAnimate = videoElement . querySelector ( '#play' ) ;
buttonToAnimate . classList . remove ( 'hide' ) ;
buttonToAnimate . classList . add ( 'fadeOut' ) ;
2015-12-23 10:46:01 -07:00
setTimeout ( function ( ) {
2016-06-06 17:44:15 -07:00
buttonToAnimate . classList . add ( 'hide' ) ;
buttonToAnimate . classList . remove ( 'fadeOut' ) ;
2015-12-23 10:46:01 -07:00
} , 300 ) ;
}
2015-08-27 08:58:07 -07:00
2015-12-23 10:46:01 -07:00
function onVolumeChange ( ) {
2014-03-20 01:34:54 -07:00
2015-12-23 10:46:01 -07:00
updateVolumeButtons ( this . volume ( ) ) ;
}
2014-03-20 01:34:54 -07:00
2015-12-23 10:46:01 -07:00
function onPause ( ) {
2014-03-20 01:34:54 -07:00
2015-12-23 10:46:01 -07:00
var videoControls = document . querySelector ( '#videoPlayer .videoControls' ) ;
var videoElement = document . querySelector ( '#videoPlayer #videoElement' ) ;
2014-03-20 01:34:54 -07:00
2016-06-06 17:44:15 -07:00
videoControls . querySelector ( '#video-playButton' ) . classList . remove ( 'hide' ) ;
videoControls . querySelector ( '#video-pauseButton' ) . classList . add ( 'hide' ) ;
var buttonToAnimate = videoElement . querySelector ( '#pause' ) ;
buttonToAnimate . classList . remove ( 'hide' ) ;
buttonToAnimate . classList . add ( 'fadeOut' ) ;
2015-12-23 10:46:01 -07:00
setTimeout ( function ( ) {
2016-06-06 17:44:15 -07:00
buttonToAnimate . classList . add ( 'hide' ) ;
buttonToAnimate . classList . remove ( 'fadeOut' ) ;
2015-12-23 10:46:01 -07:00
} , 300 ) ;
}
2014-03-20 01:34:54 -07:00
2015-12-23 10:46:01 -07:00
function onTimeUpdate ( ) {
if ( ! positionSlider . dragging ) {
2014-03-08 01:09:45 -07:00
2015-12-23 10:46:01 -07:00
self . setCurrentTime ( self . getCurrentTicks ( this ) , positionSlider , currentTimeElement ) ;
}
}
2014-06-28 12:35:30 -07:00
2015-12-23 10:46:01 -07:00
function onError ( ) {
var errorMsg = Globalize . translate ( 'MessageErrorPlayingVideo' ) ;
2015-04-28 06:56:57 -07:00
2016-01-21 13:40:10 -07:00
var item = self . currentItem ;
2016-08-29 14:06:24 -07:00
var mediaSource = self . currentMediaSource ;
2016-01-21 13:40:10 -07:00
if ( item && item . Type == "TvChannel" ) {
2016-08-29 14:06:24 -07:00
errorMsg += '<br/>' ;
errorMsg += '<br/>' ;
2015-12-23 10:46:01 -07:00
errorMsg += Globalize . translate ( 'MessageEnsureOpenTuner' ) ;
2016-08-29 14:06:24 -07:00
}
2016-09-12 11:10:09 -07:00
else if ( mediaSource && mediaSource . VideoType && mediaSource . VideoType != "VideoFile" ) {
2016-08-29 14:06:24 -07:00
errorMsg += '<br/>' ;
errorMsg += '<br/>' ;
errorMsg += Globalize . translate ( 'MessageFolderRipPlaybackExperimental' ) ;
2015-12-23 10:46:01 -07:00
}
2015-07-03 04:51:45 -07:00
2015-12-23 10:46:01 -07:00
Dashboard . alert ( {
title : Globalize . translate ( 'HeaderVideoError' ) ,
message : errorMsg
} ) ;
2015-07-03 09:49:49 -07:00
2016-01-21 13:41:09 -07:00
var mediaRenderer = self . currentMediaRenderer ;
if ( mediaRenderer ) {
self . onPlaybackStopped . call ( mediaRenderer ) ;
}
2015-12-23 10:46:01 -07:00
self . nextTrack ( ) ;
}
2015-09-25 19:31:13 -07:00
2015-12-23 10:46:01 -07:00
function onClick ( ) {
2015-07-03 04:51:45 -07:00
2016-09-18 13:38:38 -07:00
if ( ! browser . mobile ) {
2015-12-23 10:46:01 -07:00
if ( this . paused ( ) ) {
self . unpause ( ) ;
} else {
self . pause ( ) ;
2015-07-08 09:10:34 -07:00
}
2015-12-23 10:46:01 -07:00
}
}
function onDoubleClick ( ) {
2016-09-18 13:38:38 -07:00
if ( ! browser . mobile ) {
2015-12-23 10:46:01 -07:00
self . toggleFullscreen ( ) ;
}
}
2015-03-13 01:58:50 -07:00
self . updatePlaylistUi = function ( ) {
2015-12-14 08:43:03 -07:00
if ( ! initComplete ) {
return ;
}
2015-05-27 22:51:48 -07:00
var index = self . currentPlaylistIndex ( null ) ;
var length = self . playlist . length ;
2015-07-03 04:51:45 -07:00
var requiresNativeControls = false ;
2015-09-10 11:28:22 -07:00
if ( self . currentMediaRenderer && self . currentMediaRenderer . enableCustomVideoControls ) {
2015-07-03 04:51:45 -07:00
requiresNativeControls = self . currentMediaRenderer . enableCustomVideoControls ( ) ;
}
2015-03-23 10:19:21 -07:00
2015-03-13 01:58:50 -07:00
if ( length < 2 ) {
2016-06-06 17:44:15 -07:00
document . querySelector ( '.videoTrackControl' ) . classList . add ( 'hide' ) ;
2015-03-13 01:58:50 -07:00
return ;
}
2015-06-28 07:45:21 -07:00
var controls = requiresNativeControls ? '.videoAdvancedControls' : '.videoControls' ;
2015-06-29 11:45:42 -07:00
controls = document . querySelector ( controls ) ;
2015-06-28 07:45:21 -07:00
var previousTrackButton = controls . getElementsByClassName ( 'previousTrackButton' ) [ 0 ] ;
var nextTrackButton = controls . getElementsByClassName ( 'nextTrackButton' ) [ 0 ] ;
2015-03-13 01:58:50 -07:00
if ( index === 0 ) {
2015-06-28 07:45:21 -07:00
previousTrackButton . setAttribute ( 'disabled' , 'disabled' ) ;
2015-03-13 01:58:50 -07:00
} else {
2015-06-28 07:45:21 -07:00
previousTrackButton . removeAttribute ( 'disabled' ) ;
2015-03-13 01:58:50 -07:00
}
if ( ( index + 1 ) >= length ) {
2015-06-28 07:45:21 -07:00
nextTrackButton . setAttribute ( 'disabled' , 'disabled' ) ;
2015-03-13 01:58:50 -07:00
} else {
2015-06-28 07:45:21 -07:00
nextTrackButton . removeAttribute ( 'disabled' ) ;
2015-03-13 01:58:50 -07:00
}
2016-06-06 17:44:15 -07:00
previousTrackButton . classList . remove ( 'hide' ) ;
nextTrackButton . classList . remove ( 'hide' ) ;
2015-03-13 01:58:50 -07:00
} ;
2014-06-21 22:52:31 -07:00
}
createVideoPlayer ( MediaPlayer ) ;
2016-02-29 23:02:03 -07:00
} ) ;