2013-12-17 13:19:23 -07:00
( function ( document , setTimeout , clearTimeout , screen , localStorage , $ , setInterval , window ) {
2013-02-20 18:33:05 -07:00
2013-04-10 14:03:28 -07:00
function mediaPlayer ( ) {
2013-05-23 18:47:07 -07:00
2013-04-10 14:03:28 -07:00
var self = this ;
2013-04-08 21:26:02 -07:00
2013-04-10 14:03:28 -07:00
var testableAudioElement = document . createElement ( 'audio' ) ;
var testableVideoElement = document . createElement ( 'video' ) ;
var currentMediaElement ;
var currentProgressInterval ;
2013-05-23 13:09:01 -07:00
var positionSlider ;
var isPositionSliderActive ;
var currentTimeElement ;
var currentItem ;
var volumeSlider ;
var muteButton ;
var unmuteButton ;
var startTimeTicksOffset ;
var curentDurationTicks ;
var isStaticStream ;
2013-05-25 21:52:14 -07:00
var culturesPromise ;
2013-07-09 19:00:13 -07:00
var timeout ;
var idleState = true ;
2013-05-23 13:09:01 -07:00
2013-06-07 09:06:32 -07:00
self . playlist = [ ] ;
var currentPlaylistIndex = 0 ;
2014-01-03 21:53:49 -07:00
var channelsListPromise ;
var channelsListPromiseTime ;
function getChannelsListPromise ( ) {
var lastUpdateTime = channelsListPromiseTime || 0 ;
// Update every three minutes
if ( ! channelsListPromise || ! lastUpdateTime || ( new Date ( ) . getTime ( ) - lastUpdateTime ) > 10800000 ) {
channelsListPromise = ApiClient . getLiveTvChannels ( {
userId : Dashboard . getCurrentUserId ( )
} ) ;
}
return channelsListPromise ;
}
2013-05-23 13:09:01 -07:00
2013-05-23 20:33:33 -07:00
function requestFullScreen ( element ) {
// Supports most browsers and their versions.
var requestMethod = element . requestFullScreen || element . webkitRequestFullScreen || element . mozRequestFullScreen || element . msRequestFullScreen ;
if ( requestMethod ) { // Native full screen.
requestMethod . call ( element ) ;
2013-07-16 14:55:50 -07:00
} else {
2013-06-16 10:47:25 -07:00
$ ( '.itemVideo' ) . addClass ( 'fullscreenVideo' ) ;
2013-05-23 20:33:33 -07:00
}
}
2014-01-02 21:58:22 -07:00
2013-12-29 19:41:22 -07:00
function exitFullScreen ( ) {
if ( document . exitFullscreen ) {
document . exitFullscreen ( ) ;
} else if ( document . mozExitFullScreen ) {
document . mozExitFullScreen ( ) ;
} else if ( document . webkitExitFullscreen ) {
document . webkitExitFullscreen ( ) ;
}
}
2013-05-24 07:35:15 -07:00
2013-05-23 20:33:33 -07:00
function isFullScreen ( ) {
2013-05-26 12:58:37 -07:00
return document . fullscreenEnabled || document . mozFullscreenEnabled || document . webkitIsFullScreen || document . mozFullScreen ? true : false ;
2013-05-23 20:33:33 -07:00
}
2013-05-24 07:35:15 -07:00
$ ( document ) . on ( 'webkitfullscreenchange mozfullscreenchange fullscreenchange' , function ( ) {
2013-07-17 20:36:32 -07:00
var nowPlayingBar = ( '#nowPlayingBar' ) ;
2013-12-27 21:42:40 -07:00
$ ( '.itemVideo' ) . off ( 'mousemove keydown scroll' , idleHandler ) ;
2013-05-23 20:33:33 -07:00
if ( isFullScreen ( ) ) {
$ ( '.itemVideo' ) . addClass ( 'fullscreenVideo' ) ;
2013-07-17 20:36:32 -07:00
idleState = true ;
2013-12-27 21:42:40 -07:00
$ ( '.itemVideo' ) . on ( 'mousemove keydown scroll' , idleHandler ) . trigger ( 'mousemove' ) ;
2013-05-23 20:33:33 -07:00
} else {
2013-12-28 00:09:05 -07:00
$ ( ".mediaButton,.currentTime,.nowPlayingMediaInfo,.sliderContainer,.barBackground" , nowPlayingBar ) . removeClass ( "highPosition" ) ;
2013-05-23 20:33:33 -07:00
$ ( '.itemVideo' ) . removeClass ( 'fullscreenVideo' ) ;
}
} ) ;
2013-07-29 07:18:55 -07:00
$ ( window ) . on ( "beforeunload" , function ( ) {
var item = currentItem ;
var media = currentMediaElement ;
2013-08-09 08:55:22 -07:00
2013-07-29 07:18:55 -07:00
// Try to report playback stopped before the browser closes
if ( item && media && currentProgressInterval ) {
2013-08-09 08:55:22 -07:00
2013-07-29 07:18:55 -07:00
var endTime = currentMediaElement . currentTime ;
var position = Math . floor ( 10000000 * endTime ) + startTimeTicksOffset ;
ApiClient . reportPlaybackStopped ( Dashboard . getCurrentUserId ( ) , currentItem . Id , position ) ;
}
} ) ;
2013-05-23 13:09:01 -07:00
function replaceQueryString ( url , param , value ) {
var re = new RegExp ( "([?|&])" + param + "=.*?(&|$)" , "i" ) ;
if ( url . match ( re ) )
return url . replace ( re , '$1' + param + "=" + value + '$2' ) ;
else
return url + '&' + param + "=" + value ;
}
function updateVolumeButtons ( vol ) {
if ( vol ) {
muteButton . show ( ) ;
unmuteButton . hide ( ) ;
} else {
muteButton . hide ( ) ;
unmuteButton . show ( ) ;
}
}
2013-05-25 21:52:14 -07:00
function getCurrentTicks ( mediaElement ) {
return Math . floor ( 10000000 * ( mediaElement || currentMediaElement ) . currentTime ) + startTimeTicksOffset ;
}
2013-05-23 13:09:01 -07:00
function onPlaybackStopped ( ) {
2013-06-07 10:23:23 -07:00
$ ( this ) . off ( 'ended.playbackstopped' ) ;
2013-05-26 12:58:37 -07:00
currentTimeElement . empty ( ) ;
2013-05-23 13:09:01 -07:00
var endTime = this . currentTime ;
this . currentTime = 0 ;
clearProgressInterval ( ) ;
var position = Math . floor ( 10000000 * endTime ) + startTimeTicksOffset ;
ApiClient . reportPlaybackStopped ( Dashboard . getCurrentUserId ( ) , currentItem . Id , position ) ;
2013-12-01 13:17:24 -07:00
if ( currentItem . MediaType == "Video" ) {
ApiClient . stopActiveEncodings ( ) ;
}
2013-06-07 10:23:23 -07:00
}
2013-07-16 14:55:50 -07:00
2013-06-07 10:23:23 -07:00
function playNextAfterEnded ( ) {
$ ( this ) . off ( 'ended.playnext' ) ;
2013-05-23 13:09:01 -07:00
2013-07-18 20:12:52 -07:00
self . nextTrack ( ) ;
2013-05-23 13:09:01 -07:00
}
function startProgressInterval ( itemId ) {
clearProgressInterval ( ) ;
2014-01-04 08:41:35 -07:00
var intervalTime = ApiClient . isWebSocketOpen ( ) ? 2000 : 20000 ;
2013-05-23 13:09:01 -07:00
currentProgressInterval = setInterval ( function ( ) {
2013-05-23 15:39:51 -07:00
if ( currentMediaElement ) {
sendProgressUpdate ( itemId ) ;
}
2013-04-08 21:26:02 -07:00
2013-05-23 13:09:01 -07:00
} , intervalTime ) ;
}
2013-05-03 21:02:46 -07:00
2013-05-23 15:39:51 -07:00
function sendProgressUpdate ( itemId ) {
2013-08-29 14:00:27 -07:00
ApiClient . reportPlaybackProgress ( Dashboard . getCurrentUserId ( ) , itemId , getCurrentTicks ( ) , currentMediaElement . paused , currentMediaElement . volume == 0 ) ;
2013-05-23 15:39:51 -07:00
}
2013-05-23 13:09:01 -07:00
function clearProgressInterval ( ) {
if ( currentProgressInterval ) {
clearTimeout ( currentProgressInterval ) ;
currentProgressInterval = null ;
}
}
2014-01-08 21:45:09 -07:00
function getTranscodingExtension ( ) {
var media = testableVideoElement ;
// safari
if ( media . canPlayType ( 'application/x-mpegURL' ) . replace ( /no/ , '' ) ) {
return '.m3u8' ;
}
// Chrome or IE with plugin installed
if ( media . canPlayType ( 'video/webm' ) . replace ( /no/ , '' ) ) {
return '.webm' ;
}
return '.mp4' ;
}
2013-05-23 13:09:01 -07:00
2013-05-25 21:52:14 -07:00
function changeStream ( ticks , params ) {
2013-05-23 18:47:07 -07:00
var element = currentMediaElement ;
2013-05-25 21:52:14 -07:00
if ( isStaticStream && params == null ) {
2013-05-23 18:47:07 -07:00
2013-05-25 18:53:34 -07:00
element . currentTime = ticks / ( 1000 * 10000 ) ;
2013-05-23 18:47:07 -07:00
} else {
2013-05-25 21:52:14 -07:00
params = params || { } ;
2013-05-23 18:47:07 -07:00
2013-05-25 21:52:14 -07:00
var currentSrc = element . currentSrc ;
2014-01-08 21:45:09 -07:00
console . log ( currentSrc ) ;
2013-05-25 21:52:14 -07:00
currentSrc = replaceQueryString ( currentSrc , 'starttimeticks' , ticks ) ;
2013-05-23 18:47:07 -07:00
2013-05-25 21:52:14 -07:00
if ( params . AudioStreamIndex != null ) {
currentSrc = replaceQueryString ( currentSrc , 'AudioStreamIndex' , params . AudioStreamIndex ) ;
}
if ( params . SubtitleStreamIndex != null ) {
currentSrc = replaceQueryString ( currentSrc , 'SubtitleStreamIndex' , params . SubtitleStreamIndex ) ;
2013-05-23 18:47:07 -07:00
}
2013-05-26 12:58:37 -07:00
if ( params . MaxWidth != null ) {
currentSrc = replaceQueryString ( currentSrc , 'MaxWidth' , params . MaxWidth ) ;
}
if ( params . VideoBitrate != null ) {
currentSrc = replaceQueryString ( currentSrc , 'VideoBitrate' , params . VideoBitrate ) ;
}
2014-01-08 21:45:09 -07:00
if ( params . AudioBitrate != null ) {
currentSrc = replaceQueryString ( currentSrc , 'AudioBitrate' , params . AudioBitrate ) ;
}
2014-01-08 13:29:09 -07:00
if ( params . AudioCodec ) {
currentSrc = replaceQueryString ( currentSrc , 'AudioCodec' , params . AudioCodec ) ;
}
2014-01-08 21:45:09 -07:00
if ( params . VideoCodec ) {
2014-01-08 13:29:09 -07:00
currentSrc = replaceQueryString ( currentSrc , 'VideoCodec' , params . VideoCodec ) ;
}
2014-01-08 21:45:09 -07:00
if ( params . Static != null ) {
currentSrc = replaceQueryString ( currentSrc , 'Static' , params . Static ) ;
if ( params . Static == 'true' ) {
currentSrc = currentSrc . replace ( '.webm' , '.mp4' ) . replace ( '.m3u8' , '.mp4' ) ;
} else {
currentSrc = currentSrc . replace ( '.mp4' , getTranscodingExtension ( ) ) ;
}
}
2013-05-23 18:47:07 -07:00
clearProgressInterval ( ) ;
2013-06-07 10:23:23 -07:00
$ ( element ) . off ( 'ended.playbackstopped' ) . off ( 'ended.playnext' ) . on ( "play.onceafterseek" , function ( ) {
2013-05-23 18:47:07 -07:00
2013-06-07 10:23:23 -07:00
$ ( this ) . off ( 'play.onceafterseek' ) . on ( 'ended.playbackstopped' , onPlaybackStopped ) . on ( 'ended.playnext' , playNextAfterEnded ) ;
2013-07-16 14:55:50 -07:00
2013-05-23 18:47:07 -07:00
startProgressInterval ( currentItem . Id ) ;
sendProgressUpdate ( currentItem . Id ) ;
} ) ;
2013-12-01 13:17:24 -07:00
ApiClient . stopActiveEncodings ( ) . done ( function ( ) {
startTimeTicksOffset = ticks ;
element . src = currentSrc ;
} ) ;
2013-05-23 18:47:07 -07:00
}
}
2013-05-25 18:53:34 -07:00
function onPositionSliderChange ( ) {
isPositionSliderActive = false ;
var newPercent = parseInt ( this . value ) ;
var newPositionTicks = ( newPercent / 100 ) * currentItem . RunTimeTicks ;
2013-05-28 10:25:10 -07:00
changeStream ( Math . floor ( newPositionTicks ) ) ;
2013-05-25 18:53:34 -07:00
}
2013-05-23 13:09:01 -07:00
$ ( function ( ) {
muteButton = $ ( '#muteButton' ) ;
unmuteButton = $ ( '#unmuteButton' ) ;
currentTimeElement = $ ( '.currentTime' ) ;
2013-12-27 21:42:40 -07:00
volumeSlider = $ ( '.volumeSlider' ) . on ( 'slidestop' , function ( ) {
2013-05-23 13:09:01 -07:00
var vol = this . value ;
2013-12-27 21:42:40 -07:00
2013-05-23 13:09:01 -07:00
updateVolumeButtons ( vol ) ;
currentMediaElement . volume = vol ;
} ) ;
2013-12-27 21:42:40 -07:00
positionSlider = $ ( ".positionSlider" ) . on ( 'slidestart' , function ( ) {
2013-05-23 13:09:01 -07:00
isPositionSliderActive = true ;
2013-12-27 21:42:40 -07:00
} ) . on ( 'slidestop' , onPositionSliderChange ) ;
2013-05-25 18:53:34 -07:00
$ ( '#chaptersFlyout' ) . on ( 'click' , '.mediaFlyoutOption' , function ( ) {
var ticks = parseInt ( this . getAttribute ( 'data-positionticks' ) ) ;
2013-05-25 21:52:14 -07:00
changeStream ( ticks ) ;
2013-05-25 18:53:34 -07:00
hideFlyout ( $ ( '#chaptersFlyout' ) ) ;
} ) ;
2013-05-25 21:52:14 -07:00
$ ( '#audioTracksFlyout' ) . on ( 'click' , '.mediaFlyoutOption' , function ( ) {
if ( ! $ ( this ) . hasClass ( 'selectedMediaFlyoutOption' ) ) {
var index = parseInt ( this . getAttribute ( 'data-index' ) ) ;
2014-01-08 13:29:09 -07:00
changeStream ( getCurrentTicks ( ) , { AudioStreamIndex : index , Static : false } ) ;
2013-05-25 21:52:14 -07:00
}
hideFlyout ( $ ( '#audioTracksFlyout' ) ) ;
} ) ;
$ ( '#subtitleFlyout' ) . on ( 'click' , '.mediaFlyoutOption' , function ( ) {
if ( ! $ ( this ) . hasClass ( 'selectedMediaFlyoutOption' ) ) {
var index = parseInt ( this . getAttribute ( 'data-index' ) ) ;
2014-01-08 13:29:09 -07:00
changeStream ( getCurrentTicks ( ) , { SubtitleStreamIndex : index , Static : false } ) ;
2013-05-25 21:52:14 -07:00
}
hideFlyout ( $ ( '#subtitleFlyout' ) ) ;
} ) ;
2013-05-26 12:58:37 -07:00
$ ( '#qualityFlyout' ) . on ( 'click' , '.mediaFlyoutOption' , function ( ) {
if ( ! $ ( this ) . hasClass ( 'selectedMediaFlyoutOption' ) ) {
var maxWidth = parseInt ( this . getAttribute ( 'data-maxwidth' ) ) ;
var videoBitrate = parseInt ( this . getAttribute ( 'data-videobitrate' ) ) ;
2014-01-08 21:45:09 -07:00
var audioBitrate = parseInt ( this . getAttribute ( 'data-audiobitrate' ) ) ;
2013-05-26 12:58:37 -07:00
2014-01-08 13:29:09 -07:00
var mp4AudioCodec = this . getAttribute ( 'data-mp4audio' ) ;
var mp4VideoCodec = this . getAttribute ( 'data-mp4video' ) ;
2014-01-08 21:45:09 -07:00
var isStatic = this . getAttribute ( 'data-static' ) ;
2014-01-08 13:29:09 -07:00
2014-01-08 21:45:09 -07:00
localStorage . setItem ( 'preferredVideoBitrate' , videoBitrate + audioBitrate ) ;
2014-01-08 13:29:09 -07:00
var isMp4 = currentMediaElement . currentSrc . toLowerCase ( ) . indexOf ( '.mp4' ) != - 1 ;
changeStream ( getCurrentTicks ( ) , {
MaxWidth : maxWidth ,
VideoBitrate : videoBitrate ,
2014-01-08 21:45:09 -07:00
AudioBitrate : audioBitrate ,
Static : isStatic ,
2014-01-08 13:29:09 -07:00
AudioCodec : isMp4 ? mp4AudioCodec : null ,
VideoCodec : isMp4 ? mp4VideoCodec : null
} ) ;
2013-05-26 12:58:37 -07:00
}
hideFlyout ( $ ( '#qualityFlyout' ) ) ;
} ) ;
2014-01-02 21:58:22 -07:00
$ ( '#channelsFlyout' ) . on ( 'click' , '.mediaFlyoutOption' , function ( ) {
if ( ! $ ( this ) . hasClass ( 'selectedMediaFlyoutOption' ) ) {
var channelId = this . getAttribute ( 'data-channelid' ) ;
self . playById ( channelId , 'Channel' ) ;
}
hideFlyout ( $ ( '#channelsFlyout' ) ) ;
} ) ;
2013-05-23 13:09:01 -07:00
} ) ;
2013-05-03 21:02:46 -07:00
2013-04-30 12:13:06 -07:00
function endsWith ( text , pattern ) {
text = text . toLowerCase ( ) ;
pattern = pattern . toLowerCase ( ) ;
var d = text . length - pattern . length ;
return d >= 0 && text . lastIndexOf ( pattern ) === d ;
}
2013-05-23 13:09:01 -07:00
function setCurrentTime ( ticks , item , updateSlider ) {
// Convert to ticks
ticks = Math . floor ( ticks ) ;
2013-06-07 10:29:33 -07:00
var timeText = Dashboard . getDisplayTime ( ticks ) ;
2013-05-23 13:09:01 -07:00
if ( curentDurationTicks ) {
2013-06-07 10:29:33 -07:00
timeText += " / " + Dashboard . getDisplayTime ( curentDurationTicks ) ;
2013-05-23 13:09:01 -07:00
if ( updateSlider ) {
var percent = ticks / curentDurationTicks ;
percent *= 100 ;
2013-12-27 21:42:40 -07:00
positionSlider . val ( percent ) . slider ( 'enable' ) . slider ( 'refresh' ) ;
2013-05-23 13:09:01 -07:00
}
2013-05-26 18:25:09 -07:00
} else {
2014-01-02 21:58:22 -07:00
positionSlider . slider ( 'disable' ) . slider ( 'refresh' ) ;
2013-05-23 13:09:01 -07:00
}
currentTimeElement . html ( timeText ) ;
}
function playAudio ( item , params ) {
2013-04-27 11:26:29 -07:00
2013-04-10 14:03:28 -07:00
var baseParams = {
audioChannels : 2 ,
audioBitrate : 128000
} ;
2013-04-08 21:26:02 -07:00
2013-04-10 14:03:28 -07:00
$ . extend ( baseParams , params ) ;
2013-04-08 21:26:02 -07:00
2013-04-10 14:03:28 -07:00
var mp3Url = ApiClient . getUrl ( 'Audio/' + item . Id + '/stream.mp3' , $ . extend ( { } , baseParams , {
audioCodec : 'mp3'
} ) ) ;
2013-04-08 21:26:02 -07:00
2013-04-10 14:03:28 -07:00
var aacUrl = ApiClient . getUrl ( 'Audio/' + item . Id + '/stream.aac' , $ . extend ( { } , baseParams , {
audioCodec : 'aac'
} ) ) ;
2013-04-08 21:26:02 -07:00
2013-04-10 14:03:28 -07:00
var webmUrl = ApiClient . getUrl ( 'Audio/' + item . Id + '/stream.webm' , $ . extend ( { } , baseParams , {
audioCodec : 'Vorbis'
} ) ) ;
2013-04-08 21:26:02 -07:00
2013-04-30 12:13:06 -07:00
var mediaStreams = item . MediaStreams || [ ] ;
for ( var i = 0 , length = mediaStreams . length ; i < length ; i ++ ) {
var stream = mediaStreams [ i ] ;
if ( stream . Type == "Audio" ) {
// Stream statically when possible
if ( endsWith ( item . Path , ".aac" ) && stream . BitRate <= 256000 ) {
aacUrl += "&static=true" ;
}
else if ( endsWith ( item . Path , ".mp3" ) && stream . BitRate <= 256000 ) {
mp3Url += "&static=true" ;
}
break ;
}
}
2013-04-10 14:03:28 -07:00
var html = '' ;
2013-05-23 13:09:01 -07:00
2013-08-05 14:18:37 -07:00
var requiresControls = $ . browser . android || ( $ . browser . webkit && ! $ . browser . chrome ) ;
2013-05-24 13:22:19 -07:00
2013-05-24 07:35:15 -07:00
// Can't autoplay in these browsers so we need to use the full controls
2013-05-24 13:22:19 -07:00
if ( requiresControls ) {
2013-05-24 07:35:15 -07:00
html += '<audio preload="auto" autoplay controls>' ;
} else {
html += '<audio preload="auto" style="display:none;" autoplay>' ;
}
2013-04-10 14:03:28 -07:00
html += '<source type="audio/mpeg" src="' + mp3Url + '" />' ;
html += '<source type="audio/aac" src="' + aacUrl + '" />' ;
html += '<source type="audio/webm" src="' + webmUrl + '" />' ;
html += '</audio' ;
2013-04-08 21:26:02 -07:00
2013-04-10 14:03:28 -07:00
var nowPlayingBar = $ ( '#nowPlayingBar' ) . show ( ) ;
//show stop button
$ ( '#stopButton' , nowPlayingBar ) . show ( ) ;
2013-05-23 13:09:01 -07:00
$ ( '#playButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#pauseButton' , nowPlayingBar ) . show ( ) ;
2013-05-23 20:33:33 -07:00
$ ( '#fullscreenButton' , nowPlayingBar ) . hide ( ) ;
2013-04-08 21:26:02 -07:00
2013-08-05 14:18:37 -07:00
$ ( '#previousTrackButton' , nowPlayingBar ) . show ( ) ;
$ ( '#nextTrackButton' , nowPlayingBar ) . show ( ) ;
$ ( '#playlistButton' , nowPlayingBar ) . show ( ) ;
2013-05-24 18:51:17 -07:00
2013-05-25 17:53:51 -07:00
$ ( '#qualityButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#audioTracksButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#subtitleButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#chaptersButton' , nowPlayingBar ) . hide ( ) ;
2014-01-02 21:58:22 -07:00
$ ( '#channelsButton' , nowPlayingBar ) . hide ( ) ;
2013-05-25 17:53:51 -07:00
2013-05-24 18:51:17 -07:00
$ ( '#mediaElement' , nowPlayingBar ) . html ( html ) ;
2013-05-23 13:09:01 -07:00
var audioElement = $ ( "audio" , nowPlayingBar ) ;
2013-05-23 15:39:51 -07:00
var initialVolume = localStorage . getItem ( "volume" ) || 0.5 ;
2013-05-23 13:09:01 -07:00
audioElement . each ( function ( ) {
2013-05-23 15:39:51 -07:00
this . volume = initialVolume ;
2013-04-27 12:38:24 -07:00
} ) ;
2013-12-27 21:42:40 -07:00
volumeSlider . val ( initialVolume ) . slider ( 'refresh' ) ;
2013-05-23 15:39:51 -07:00
updateVolumeButtons ( initialVolume ) ;
2013-05-23 13:09:01 -07:00
audioElement . on ( "volumechange" , function ( ) {
2013-04-28 10:36:20 -07:00
2013-05-23 13:09:01 -07:00
var vol = this . volume ;
2013-04-27 11:26:29 -07:00
2013-05-23 13:09:01 -07:00
localStorage . setItem ( "volume" , vol ) ;
2013-04-27 11:26:29 -07:00
2013-05-23 13:09:01 -07:00
updateVolumeButtons ( vol ) ;
2013-04-28 10:36:20 -07:00
2013-05-23 13:09:01 -07:00
} ) . on ( "play.once" , function ( ) {
2013-05-03 21:02:46 -07:00
2013-05-24 13:22:19 -07:00
if ( ! requiresControls ) {
audioElement . hide ( ) ;
}
2013-05-24 18:51:17 -07:00
2013-05-23 13:09:01 -07:00
var duration = this . duration ;
isStaticStream = duration && ! isNaN ( duration ) && duration != Number . POSITIVE _INFINITY && duration != Number . NEGATIVE _INFINITY ;
2013-04-08 21:26:02 -07:00
2013-05-24 07:35:15 -07:00
audioElement . off ( "play.once" ) ;
2013-04-28 11:02:35 -07:00
2013-09-24 08:08:51 -07:00
ApiClient . reportPlaybackStart ( Dashboard . getCurrentUserId ( ) , item . Id , true , item . MediaType ) ;
2013-05-23 13:09:01 -07:00
startProgressInterval ( item . Id ) ;
} ) . on ( "pause" , function ( ) {
$ ( '#playButton' , nowPlayingBar ) . show ( ) ;
$ ( '#pauseButton' , nowPlayingBar ) . hide ( ) ;
} ) . on ( "playing" , function ( ) {
$ ( '#playButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#pauseButton' , nowPlayingBar ) . show ( ) ;
} ) . on ( "timeupdate" , function ( ) {
if ( ! isPositionSliderActive ) {
2013-05-25 21:52:14 -07:00
setCurrentTime ( getCurrentTicks ( this ) , item , true ) ;
2013-05-23 13:09:01 -07:00
}
2013-06-07 10:23:23 -07:00
} ) . on ( "ended.playbackstopped" , onPlaybackStopped ) . on ( 'ended.playnext' , playNextAfterEnded ) ;
2013-05-23 13:09:01 -07:00
currentItem = item ;
curentDurationTicks = item . RunTimeTicks ;
return audioElement [ 0 ] ;
2013-04-30 10:21:21 -07:00
}
2014-01-08 21:45:09 -07:00
function canPlayVideoDirect ( item , videoStream ) {
2014-01-08 13:29:09 -07:00
if ( item . VideoType != "VideoFile" || item . LocationType != "FileSystem" ) {
return false ;
}
if ( ( videoStream . Codec || '' ) . toLowerCase ( ) . indexOf ( 'h264' ) == - 1 ) {
return false ;
}
2014-01-08 21:45:09 -07:00
var audioStreams = ( item . MediaStreams || [ ] ) . filter ( function ( stream ) {
return stream . Type == "Audio" ;
} ) ;
2014-01-08 13:29:09 -07:00
2014-01-08 21:45:09 -07:00
if ( audioStreams . length && ! audioStreams . filter ( canPlayAudioStreamDirect ) . length ) {
2014-01-08 13:29:09 -07:00
return false ;
}
var extension = item . Path . substring ( item . Path . lastIndexOf ( '.' ) + 1 ) ;
2014-01-08 21:45:09 -07:00
return extension . toLowerCase ( ) == 'mp4' ;
}
function canPlayAudioStreamDirect ( audioStream ) {
var audioCodec = ( audioStream . Codec || '' ) . toLowerCase ( ) . replace ( '-' , '' ) ;
if ( audioCodec . indexOf ( 'aac' ) == - 1 &&
audioCodec . indexOf ( 'mp3' ) == - 1 &&
audioCodec . indexOf ( 'mpeg' ) == - 1 ) {
2014-01-08 13:29:09 -07:00
return false ;
}
2014-01-08 21:45:09 -07:00
if ( audioStream . Channels == null ) {
return false ;
}
2014-01-08 13:29:09 -07:00
2014-01-08 21:45:09 -07:00
// IE won't play at all if more than two channels
if ( audioStream . Channels > 2 && $ . browser . msie ) {
return false ;
}
2014-01-08 13:29:09 -07:00
2014-01-08 21:45:09 -07:00
return true ;
2014-01-08 13:29:09 -07:00
}
2014-01-08 21:45:09 -07:00
function getVideoQualityOptions ( item , audioStreamIndex ) {
2014-01-08 13:29:09 -07:00
var videoStream = ( item . MediaStreams || [ ] ) . filter ( function ( stream ) {
return stream . Type == "Video" ;
} ) [ 0 ] ;
2014-01-08 21:45:09 -07:00
var audioStreams = ( item . MediaStreams || [ ] ) . filter ( function ( stream ) {
return stream . Type == "Audio" ;
} ) ;
var audioStream = audioStreamIndex == null ? null : audioStreams [ audioStreamIndex ] ;
var canPlayDirect = canPlayVideoDirect ( item , videoStream , audioStream ) ;
var bitrateSetting = parseInt ( localStorage . getItem ( 'preferredVideoBitrate' ) || '' ) || 1500000 ;
2014-01-08 13:29:09 -07:00
var maxAllowedWidth = Math . max ( screen . height , screen . width ) ;
var options = [ ] ;
// We have media info
if ( videoStream && videoStream . Width ) {
maxAllowedWidth = videoStream . Width ;
}
2014-01-08 21:45:09 -07:00
// Some 1080- videos are reported as 1912?
2014-01-08 13:29:09 -07:00
if ( maxAllowedWidth >= 1910 ) {
2014-01-08 21:45:09 -07:00
options . push ( { name : '1080p - 10Mbps' , maxWidth : 1920 , bitrate : 10000000 } ) ;
options . push ( { name : '1080p - 8Mbps' , maxWidth : 1920 , bitrate : 8000000 } ) ;
options . push ( { name : '1080p - 6Mbps' , maxWidth : 1920 , bitrate : 6000000 } ) ;
options . push ( { name : '1080p - 5Mbps' , maxWidth : 1920 , bitrate : 5000000 } ) ;
}
else if ( maxAllowedWidth >= 1270 ) {
options . push ( { name : '720p - 10Mbps' , maxWidth : 1280 , bitrate : 10000000 } ) ;
options . push ( { name : '720p - 8Mbps' , maxWidth : 1280 , bitrate : 8000000 } ) ;
options . push ( { name : '720p - 6Mbps' , maxWidth : 1280 , bitrate : 6000000 } ) ;
options . push ( { name : '720p - 5Mbps' , maxWidth : 1280 , bitrate : 5000000 } ) ;
}
else if ( maxAllowedWidth >= 470 ) {
options . push ( { name : '480p - 1.5Mbps' , maxWidth : 720 , bitrate : 1500000 } ) ;
options . push ( { name : '480p - 1.0Mbps' , maxWidth : 720 , bitrate : 1000000 } ) ;
2014-01-08 13:29:09 -07:00
}
if ( maxAllowedWidth >= 1270 ) {
2014-01-08 21:45:09 -07:00
options . push ( { name : '720p - 4Mbps' , maxWidth : 1280 , bitrate : 4000000 } ) ;
options . push ( { name : '720p - 3Mbps' , maxWidth : 1280 , bitrate : 3000000 } ) ;
options . push ( { name : '720p - 2Mbps' , maxWidth : 1280 , bitrate : 2000000 } ) ;
options . push ( { name : '720p - 1.5Mbps' , maxWidth : 1280 , bitrate : 1500000 } ) ;
2014-01-08 13:29:09 -07:00
}
2014-01-08 21:45:09 -07:00
options . push ( { name : '480p - 720 kbps' , maxWidth : 720 , bitrate : 700000 } ) ;
options . push ( { name : '480p - 420 kbps' , maxWidth : 720 , bitrate : 420000 } ) ;
options . push ( { name : '360p' , maxWidth : 640 , bitrate : 400000 } ) ;
options . push ( { name : '240p' , maxWidth : 426 , bitrate : 320000 } ) ;
2014-01-08 13:29:09 -07:00
2014-01-08 21:45:09 -07:00
var canPlayVideoCodecDirect = ( videoStream ? videoStream . Codec : '' ) . toLowerCase ( ) . indexOf ( 'h264' ) != - 1 ;
var canPlayAudioDirect = audioStream ? canPlayAudioStreamDirect ( audioStream ) : false ;
2014-01-08 13:29:09 -07:00
2014-01-08 21:45:09 -07:00
var videoBitrate = videoStream ? videoStream . BitRate : null ;
var audioBitrate = audioStream ? audioStream . BitRate : null ;
var totalBitrate = ( videoBitrate || 0 ) + ( audioBitrate || 0 ) ;
var videoWidth = videoStream ? videoStream . Width : null ;
2013-04-28 10:36:20 -07:00
2014-01-08 21:45:09 -07:00
var i , length , option ;
2014-01-08 13:29:09 -07:00
var selectedIndex = - 1 ;
for ( i = 0 , length = options . length ; i < length ; i ++ ) {
2014-01-08 21:45:09 -07:00
option = options [ i ] ;
if ( selectedIndex == - 1 && option . bitrate <= bitrateSetting ) {
2014-01-08 13:29:09 -07:00
selectedIndex = i ;
}
2014-01-08 21:45:09 -07:00
option . audioBitrate = option . bitrate >= 700000 ? 128000 : 64000 ;
if ( canPlayVideoCodecDirect &&
totalBitrate &&
option . bitrate >= totalBitrate &&
videoWidth &&
option . maxWidth >= videoWidth ) {
if ( canPlayDirect ) {
option . isStatic = true ;
option . mp4VideoCodec = 'copy' ;
option . mp4AudioCodec = 'copy' ;
}
}
option . isStatic = option . isStatic || false ;
option . mp4VideoCodec = option . mp4VideoCodec || 'h264' ;
option . mp4AudioCodec = option . mp4AudioCodec || 'aac' ;
option . videoBitrate = option . bitrate - option . audioBitrate ;
2014-01-08 13:29:09 -07:00
}
if ( selectedIndex == - 1 ) {
selectedIndex = options . length - 1 ;
}
options [ selectedIndex ] . selected = true ;
2014-01-08 21:45:09 -07:00
var firstOption = options [ 0 ] ;
if ( firstOption . isStatic ) {
firstOption . name = 'Direct' ;
}
2014-01-08 13:29:09 -07:00
return options ;
}
function playVideo ( item , startPosition , user ) {
2013-04-10 14:03:28 -07:00
2013-12-21 11:37:34 -07:00
var mediaStreams = item . MediaStreams || [ ] ;
2013-12-27 21:42:40 -07:00
2013-04-10 14:03:28 -07:00
var baseParams = {
audioChannels : 2 ,
2014-01-08 21:45:09 -07:00
StartTimeTicks : startPosition || 0 ,
2013-12-21 11:37:34 -07:00
SubtitleStreamIndex : getInitialSubtitleStreamIndex ( mediaStreams , user ) ,
AudioStreamIndex : getInitialAudioStreamIndex ( mediaStreams , user ) ,
deviceId : ApiClient . deviceId ( ) ,
2014-01-08 13:29:09 -07:00
Type : item . Type ,
Static : false
2013-04-10 14:03:28 -07:00
} ;
2014-01-08 21:45:09 -07:00
var qualityOption = getVideoQualityOptions ( item , baseParams . AudioStreamIndex ) . filter ( function ( opt ) {
2014-01-08 13:29:09 -07:00
return opt . selected ;
2013-05-26 12:58:37 -07:00
} ) [ 0 ] ;
2014-01-08 13:29:09 -07:00
baseParams . maxWidth = qualityOption . maxWidth ;
baseParams . videoBitrate = qualityOption . videoBitrate ;
2014-01-08 21:45:09 -07:00
baseParams . audioBitrate = qualityOption . audioBitrate ;
2013-05-26 12:58:37 -07:00
2014-01-08 13:29:09 -07:00
// Webm must be ahead of mp4 due to the issue of mp4 playing too fast in chrome
var prioritizeWebmOverH264 = $ . browser . chrome || $ . browser . msie ;
2013-05-26 12:58:37 -07:00
2014-01-08 21:45:09 -07:00
var staticMp4 = qualityOption . isStatic ;
2013-04-08 21:26:02 -07:00
2013-05-23 20:33:33 -07:00
var mp4VideoUrl = ApiClient . getUrl ( 'Videos/' + item . Id + '/stream.mp4' , $ . extend ( { } , baseParams , {
2014-01-08 21:45:09 -07:00
VideoCodec : qualityOption . mp4VideoCodec ,
AudioCodec : qualityOption . mp4AudioCodec ,
2013-05-28 18:45:39 -07:00
profile : 'baseline' ,
2014-01-08 13:29:09 -07:00
level : 3 ,
Static : staticMp4
2013-05-28 18:45:39 -07:00
} ) ) ;
2013-05-23 20:33:33 -07:00
var webmVideoUrl = ApiClient . getUrl ( 'Videos/' + item . Id + '/stream.webm' , $ . extend ( { } , baseParams , {
2014-01-08 21:45:09 -07:00
VideoCodec : 'vpx' ,
AudioCodec : 'Vorbis'
2013-05-23 20:33:33 -07:00
} ) ) ;
var hlsVideoUrl = ApiClient . getUrl ( 'Videos/' + item . Id + '/stream.m3u8' , $ . extend ( { } , baseParams , {
2014-01-08 21:45:09 -07:00
VideoCodec : qualityOption . mp4VideoCodec ,
AudioCodec : qualityOption . mp4AudioCodec ,
2013-05-26 12:58:37 -07:00
profile : 'baseline' ,
2013-09-23 07:26:20 -07:00
level : 3 ,
timeStampOffsetMs : 0
2013-05-23 20:33:33 -07:00
} ) ) ;
2013-04-10 14:03:28 -07:00
2013-05-23 20:33:33 -07:00
var html = '' ;
2013-07-29 07:18:55 -07:00
var requiresControls = $ . browser . msie || $ . browser . android || ( $ . browser . webkit && ! $ . browser . chrome ) ;
2013-07-10 05:37:14 -07:00
2013-05-24 07:35:15 -07:00
// Can't autoplay in these browsers so we need to use the full controls
2013-07-10 05:37:14 -07:00
if ( requiresControls ) {
2013-05-28 21:00:24 -07:00
html += '<video class="itemVideo" autoplay controls preload="none">' ;
2013-05-23 20:33:33 -07:00
} else {
2013-05-28 21:00:24 -07:00
html += '<video class="itemVideo" autoplay preload="none">' ;
2013-05-23 20:33:33 -07:00
}
2014-01-08 13:29:09 -07:00
if ( ! staticMp4 ) {
// HLS must be at the top for safari
html += '<source type="application/x-mpegURL" src="' + hlsVideoUrl + '" />' ;
}
2013-09-13 18:03:55 -07:00
2014-01-08 13:29:09 -07:00
if ( prioritizeWebmOverH264 && ! staticMp4 ) {
2013-09-13 18:03:55 -07:00
html += '<source type="video/webm" src="' + webmVideoUrl + '" />' ;
2014-01-08 13:29:09 -07:00
}
html += '<source type="video/mp4" src="' + mp4VideoUrl + '" />' ;
if ( ! prioritizeWebmOverH264 && ! staticMp4 ) {
2013-09-13 18:03:55 -07:00
html += '<source type="video/webm" src="' + webmVideoUrl + '" />' ;
}
2013-05-23 20:33:33 -07:00
html += '</video' ;
var nowPlayingBar = $ ( '#nowPlayingBar' ) . show ( ) ;
//show stop button
$ ( '#stopButton' , nowPlayingBar ) . show ( ) ;
$ ( '#playButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#pauseButton' , nowPlayingBar ) . show ( ) ;
$ ( '#playlistButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#previousTrackButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#nextTrackButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#mediaElement' , nowPlayingBar ) . html ( html ) ;
2013-05-25 17:53:51 -07:00
$ ( '#qualityButton' , nowPlayingBar ) . show ( ) ;
2013-05-25 21:52:14 -07:00
2013-12-21 11:37:34 -07:00
if ( mediaStreams . filter ( function ( i ) {
2013-05-26 18:25:09 -07:00
return i . Type == "Audio" ;
} ) . length ) {
$ ( '#audioTracksButton' , nowPlayingBar ) . show ( ) ;
} else {
$ ( '#audioTracksButton' , nowPlayingBar ) . hide ( ) ;
}
2013-05-25 17:53:51 -07:00
2013-12-21 11:37:34 -07:00
if ( mediaStreams . filter ( function ( i ) {
2013-05-25 17:53:51 -07:00
return i . Type == "Subtitle" ;
} ) . length ) {
$ ( '#subtitleButton' , nowPlayingBar ) . show ( ) ;
} else {
$ ( '#subtitleButton' , nowPlayingBar ) . hide ( ) ;
}
2013-12-21 11:37:34 -07:00
if ( item . Chapters && item . Chapters . length ) {
2013-05-25 17:53:51 -07:00
$ ( '#chaptersButton' , nowPlayingBar ) . show ( ) ;
} else {
$ ( '#chaptersButton' , nowPlayingBar ) . hide ( ) ;
}
2013-07-10 05:37:14 -07:00
if ( requiresControls ) {
$ ( '#fullscreenButton' , nowPlayingBar ) . hide ( ) ;
} else {
$ ( '#fullscreenButton' , nowPlayingBar ) . show ( ) ;
}
2013-05-23 20:33:33 -07:00
2014-01-02 21:58:22 -07:00
var channelsButton = $ ( '#channelsButton' , nowPlayingBar ) . hide ( ) ;
2013-05-23 20:33:33 -07:00
var videoElement = $ ( "video" , nowPlayingBar ) ;
var initialVolume = localStorage . getItem ( "volume" ) || 0.5 ;
videoElement . each ( function ( ) {
this . volume = initialVolume ;
2013-04-08 21:26:02 -07:00
} ) ;
2013-02-20 18:33:05 -07:00
2013-12-27 21:42:40 -07:00
volumeSlider . val ( initialVolume ) . slider ( 'refresh' ) ;
2013-05-23 20:33:33 -07:00
updateVolumeButtons ( initialVolume ) ;
2013-02-27 10:53:42 -07:00
2013-05-23 20:33:33 -07:00
videoElement . on ( "volumechange" , function ( ) {
2013-04-28 11:02:35 -07:00
2013-05-23 20:33:33 -07:00
var vol = this . volume ;
2013-04-28 10:36:20 -07:00
2013-05-23 20:33:33 -07:00
localStorage . setItem ( "volume" , vol ) ;
2013-05-10 05:18:07 -07:00
2013-05-23 20:33:33 -07:00
updateVolumeButtons ( vol ) ;
} ) . on ( "play.once" , function ( ) {
2013-02-20 18:33:05 -07:00
2013-05-23 20:33:33 -07:00
var duration = this . duration ;
isStaticStream = duration && ! isNaN ( duration ) && duration != Number . POSITIVE _INFINITY && duration != Number . NEGATIVE _INFINITY ;
2013-02-20 18:33:05 -07:00
2013-06-16 12:32:53 -07:00
videoElement . off ( "play.once" ) ;
2013-03-27 19:01:26 -07:00
2014-01-08 21:45:09 -07:00
if ( startPosition && staticMp4 ) {
self . seek ( startPosition ) ;
}
2013-09-24 08:08:51 -07:00
ApiClient . reportPlaybackStart ( Dashboard . getCurrentUserId ( ) , item . Id , true , item . MediaType ) ;
2013-05-23 13:09:01 -07:00
2013-05-23 20:33:33 -07:00
startProgressInterval ( item . Id ) ;
} ) . on ( "pause" , function ( ) {
$ ( '#playButton' , nowPlayingBar ) . show ( ) ;
$ ( '#pauseButton' , nowPlayingBar ) . hide ( ) ;
} ) . on ( "playing" , function ( ) {
$ ( '#playButton' , nowPlayingBar ) . hide ( ) ;
$ ( '#pauseButton' , nowPlayingBar ) . show ( ) ;
} ) . on ( "timeupdate" , function ( ) {
if ( ! isPositionSliderActive ) {
2013-05-25 21:52:14 -07:00
setCurrentTime ( getCurrentTicks ( this ) , item , true ) ;
2013-05-23 20:33:33 -07:00
}
2013-11-30 11:32:39 -07:00
} ) . on ( "error" , function ( ) {
var errorCode = this . error ? this . error . code : '' ;
console . log ( 'Html5 Video error code: ' + errorCode ) ;
2013-06-07 10:23:23 -07:00
} ) . on ( "ended.playbackstopped" , onPlaybackStopped ) . on ( 'ended.playnext' , playNextAfterEnded ) ;
2013-05-23 20:33:33 -07:00
currentItem = item ;
curentDurationTicks = item . RunTimeTicks ;
2013-12-22 11:58:51 -07:00
2014-01-03 21:53:49 -07:00
getChannelsListPromise ( ) . done ( function ( result ) {
2014-01-02 21:58:22 -07:00
2014-01-03 21:53:49 -07:00
if ( result . Items . length ) {
channelsButton . show ( ) ;
}
} ) ;
2014-01-02 21:58:22 -07:00
2013-05-23 20:33:33 -07:00
return videoElement [ 0 ] ;
2013-05-24 19:45:12 -07:00
} ;
function getInitialAudioStreamIndex ( mediaStreams , user ) {
2013-08-28 15:18:14 -07:00
// Find all audio streams with at least one channel
2013-05-25 21:52:14 -07:00
var audioStreams = mediaStreams . filter ( function ( stream ) {
2013-08-28 15:18:14 -07:00
return stream . Type == "Audio" && stream . Channels ;
2013-05-25 21:52:14 -07:00
} ) ;
2013-05-24 19:45:12 -07:00
if ( user . Configuration . AudioLanguagePreference ) {
2013-05-25 21:52:14 -07:00
for ( var i = 0 , length = audioStreams . length ; i < length ; i ++ ) {
var mediaStream = audioStreams [ i ] ;
2013-05-24 19:45:12 -07:00
2013-08-28 15:18:14 -07:00
if ( mediaStream . Language == user . Configuration . AudioLanguagePreference ) {
2013-05-24 19:45:12 -07:00
return mediaStream . Index ;
}
}
}
2013-05-25 21:52:14 -07:00
// Just use the first audio stream
return audioStreams . length ? audioStreams [ 0 ] . Index : null ;
2013-05-24 19:45:12 -07:00
}
function getInitialSubtitleStreamIndex ( mediaStreams , user ) {
var i , length , mediaStream ;
// Find the first forced subtitle stream
for ( i = 0 , length = mediaStreams . length ; i < length ; i ++ ) {
mediaStream = mediaStreams [ i ] ;
if ( mediaStream . Type == "Subtitle" && mediaStream . IsForced ) {
return mediaStream . Index ;
}
}
// If none then look at user configuration
2013-07-23 05:29:28 -07:00
if ( user . Configuration . SubtitleLanguagePreference ) {
2013-05-24 19:45:12 -07:00
for ( i = 0 , length = mediaStreams . length ; i < length ; i ++ ) {
mediaStream = mediaStreams [ i ] ;
2013-07-23 05:29:28 -07:00
if ( mediaStream . Type == "Subtitle" && mediaStream . Language == user . Configuration . SubtitleLanguagePreference ) {
2013-05-24 19:45:12 -07:00
if ( user . Configuration . UseForcedSubtitlesOnly ) {
if ( mediaStream . IsForced ) {
return mediaStream . Index ;
}
} else {
return mediaStream . Index ;
}
}
}
}
return null ;
2013-04-10 14:03:28 -07:00
}
2013-07-16 14:55:50 -07:00
2013-07-09 19:00:13 -07:00
function idleHandler ( ) {
var nowPlayingBar = $ ( "#nowPlayingBar" ) ;
if ( timeout ) {
window . clearTimeout ( timeout ) ;
}
if ( idleState == true ) {
2013-12-28 00:09:05 -07:00
$ ( ".mediaButton,.currentTime,.nowPlayingMediaInfo,.sliderContainer,.barBackground" , nowPlayingBar ) . addClass ( "highPosition" ) ;
2013-07-09 19:00:13 -07:00
}
idleState = false ;
timeout = window . setTimeout ( function ( ) {
idleState = true ;
2013-12-28 00:09:05 -07:00
$ ( ".mediaButton,.currentTime,.nowPlayingMediaInfo,.sliderContainer,.barBackground" , nowPlayingBar ) . removeClass ( "highPosition" ) ;
2013-07-09 19:00:13 -07:00
} , 4000 ) ;
}
2013-07-16 14:55:50 -07:00
2013-04-10 14:03:28 -07:00
self . canPlay = function ( item ) {
2013-05-23 18:47:07 -07:00
2013-11-21 13:48:26 -07:00
if ( item . Type == "MusicAlbum" || item . Type == "MusicArtist" || item . Type == "MusicGenre" ) {
2013-06-07 09:06:32 -07:00
return true ;
}
2013-10-26 16:19:26 -07:00
if ( item . GameSystem == "Nintendo" && item . MediaType == "Game" && item . ProviderIds . NesBox && item . ProviderIds . NesBoxRom ) {
return true ;
}
if ( item . GameSystem == "Super Nintendo" && item . MediaType == "Game" && item . ProviderIds . NesBox && item . ProviderIds . NesBoxRom ) {
return true ;
}
2013-05-23 18:47:07 -07:00
return self . canPlayMediaType ( item . MediaType ) ;
} ;
2013-11-29 23:07:45 -07:00
self . getPlayUrl = function ( item ) {
2013-10-26 16:19:26 -07:00
if ( item . GameSystem == "Nintendo" && item . MediaType == "Game" && item . ProviderIds . NesBox && item . ProviderIds . NesBoxRom ) {
return "http://nesbox.com/game/" + item . ProviderIds . NesBox + '/rom/' + item . ProviderIds . NesBoxRom ;
}
if ( item . GameSystem == "Super Nintendo" && item . MediaType == "Game" && item . ProviderIds . NesBox && item . ProviderIds . NesBoxRom ) {
return "http://snesbox.com/game/" + item . ProviderIds . NesBox + '/rom/' + item . ProviderIds . NesBoxRom ;
}
return null ;
} ;
2013-05-23 18:47:07 -07:00
self . canPlayMediaType = function ( mediaType ) {
2013-04-10 14:03:28 -07:00
var media ;
2013-03-31 18:52:07 -07:00
2013-05-23 18:47:07 -07:00
if ( mediaType === "Video" ) {
2013-04-10 14:03:28 -07:00
media = testableVideoElement ;
if ( media . canPlayType ) {
2013-03-27 19:01:26 -07:00
2013-04-10 14:03:28 -07:00
return media . canPlayType ( 'video/mp4' ) . replace ( /no/ , '' ) || media . canPlayType ( 'video/mp2t' ) . replace ( /no/ , '' ) || media . canPlayType ( 'video/webm' ) . replace ( /no/ , '' ) || media . canPlayType ( 'application/x-mpegURL' ) . replace ( /no/ , '' ) || media . canPlayType ( 'video/ogv' ) . replace ( /no/ , '' ) ;
}
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
return false ;
2013-04-08 21:26:02 -07:00
}
2013-04-08 21:16:32 -07:00
2013-05-23 18:47:07 -07:00
if ( mediaType === "Audio" ) {
2013-04-10 14:03:28 -07:00
media = testableAudioElement ;
if ( media . canPlayType ) {
2013-04-08 21:16:32 -07:00
2013-05-23 18:47:07 -07:00
return media . canPlayType ( 'audio/mpeg' ) . replace ( /no/ , '' ) || media . canPlayType ( 'audio/webm' ) . replace ( /no/ , '' ) || media . canPlayType ( 'audio/aac' ) . replace ( /no/ , '' ) ;
2013-04-10 14:03:28 -07:00
}
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
return false ;
2013-04-08 21:16:32 -07:00
}
2013-04-08 21:26:02 -07:00
return false ;
2013-04-10 14:03:28 -07:00
} ;
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
self . play = function ( items , startPosition ) {
2013-04-08 21:16:32 -07:00
2013-05-24 19:45:12 -07:00
Dashboard . getCurrentUser ( ) . done ( function ( user ) {
2013-05-24 18:51:17 -07:00
2013-05-24 19:45:12 -07:00
var item = items [ 0 ] ;
2013-11-29 23:07:45 -07:00
2013-05-24 19:45:12 -07:00
var videoType = ( item . VideoType || "" ) . toLowerCase ( ) ;
2013-05-24 18:51:17 -07:00
2013-10-12 09:57:27 -07:00
var expirementalText = "This feature is experimental. It may not work at all with some titles. Do you wish to continue?" ;
2013-07-28 07:35:18 -07:00
2013-05-24 19:45:12 -07:00
if ( videoType == "dvd" ) {
2013-05-24 18:51:17 -07:00
2013-07-28 07:35:18 -07:00
self . playWithWarning ( items , startPosition , user , "dvdstreamconfirmed" , "Dvd Folder Streaming" , expirementalText ) ;
2013-05-24 18:51:17 -07:00
return ;
}
2013-05-24 19:45:12 -07:00
else if ( videoType == "bluray" ) {
2013-05-24 18:51:17 -07:00
2013-07-28 07:35:18 -07:00
self . playWithWarning ( items , startPosition , user , "bluraystreamconfirmed" , "Blu-ray Folder Streaming" , expirementalText ) ;
2013-05-24 18:51:17 -07:00
return ;
}
2013-05-24 19:45:12 -07:00
else if ( videoType == "iso" ) {
var isoType = ( item . IsoType || "" ) . toLowerCase ( ) ;
if ( isoType == "dvd" ) {
2013-05-24 18:51:17 -07:00
2013-07-28 07:35:18 -07:00
self . playWithWarning ( items , startPosition , user , "dvdisostreamconfirmed" , "Dvd Iso Streaming" , expirementalText ) ;
2013-05-24 19:45:12 -07:00
return ;
}
else if ( isoType == "bluray" ) {
2013-07-28 07:35:18 -07:00
self . playWithWarning ( items , startPosition , user , "blurayisostreamconfirmed" , "Blu-ray Iso Streaming" , expirementalText ) ;
2013-05-24 19:45:12 -07:00
return ;
}
}
2013-07-28 07:35:18 -07:00
else if ( $ . browser . msie && videoType ) {
2013-08-09 08:55:22 -07:00
2013-07-28 07:35:18 -07:00
self . playWithWarning ( items , startPosition , user , "iewebmplugin" , "Internet Explorer Playback" , "For optimal video playback of Internet Explorer desktop edition, please install google's webm plugin for IE.<br/><br/><a target='_blank' href='https://tools.google.com/dlpage/webmmf'>https://tools.google.com/dlpage/webmmf</a>" ) ;
return ;
}
2013-05-24 19:45:12 -07:00
2013-06-07 09:06:32 -07:00
self . playInternal ( items [ 0 ] , startPosition , user ) ;
self . onPlaybackStarted ( items ) ;
2013-05-24 19:45:12 -07:00
} ) ;
2013-05-24 18:51:17 -07:00
} ;
2013-07-28 07:35:18 -07:00
self . playWithWarning = function ( items , startPosition , user , localStorageKeyName , header , text ) {
2013-05-24 18:51:17 -07:00
2013-06-20 13:29:24 -07:00
localStorageKeyName += new Date ( ) . getMonth ( ) ;
2013-07-16 14:55:50 -07:00
2013-05-24 18:51:17 -07:00
if ( localStorage . getItem ( localStorageKeyName ) == "1" ) {
2013-06-07 09:06:32 -07:00
self . playInternal ( items [ 0 ] , startPosition , user ) ;
self . onPlaybackStarted ( items ) ;
2013-05-24 18:51:17 -07:00
return ;
}
2013-07-28 07:35:18 -07:00
Dashboard . confirm ( text , header , function ( result ) {
2013-05-24 19:45:12 -07:00
2013-05-24 18:51:17 -07:00
if ( result ) {
localStorage . setItem ( localStorageKeyName , "1" ) ;
2013-06-07 09:06:32 -07:00
self . playInternal ( items [ 0 ] , startPosition , user ) ;
self . onPlaybackStarted ( items ) ;
2013-05-24 18:51:17 -07:00
}
} ) ;
} ;
2013-06-07 09:06:32 -07:00
self . onPlaybackStarted = function ( items ) {
self . playlist = items ;
currentPlaylistIndex = 0 ;
} ;
self . playInternal = function ( item , startPosition , user ) {
2013-05-24 18:51:17 -07:00
2013-07-11 13:25:12 -07:00
if ( item == null ) {
throw new Error ( "item cannot be null" ) ;
}
2013-07-16 14:55:50 -07:00
2013-06-07 10:23:23 -07:00
if ( self . isPlaying ( ) ) {
2013-04-10 14:03:28 -07:00
self . stop ( ) ;
}
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
var mediaElement ;
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
if ( item . MediaType === "Video" ) {
2013-04-08 21:16:32 -07:00
2013-05-24 19:45:12 -07:00
mediaElement = playVideo ( item , startPosition , user ) ;
2013-04-10 14:03:28 -07:00
} else if ( item . MediaType === "Audio" ) {
2013-04-08 21:16:32 -07:00
2013-04-30 10:21:21 -07:00
mediaElement = playAudio ( item ) ;
2013-06-07 09:06:32 -07:00
} else {
throw new Error ( "Unrecognized media type" ) ;
2013-04-10 14:03:28 -07:00
}
2013-04-08 21:16:32 -07:00
2013-05-23 13:09:01 -07:00
startTimeTicksOffset = startPosition || 0 ;
2013-04-10 14:03:28 -07:00
currentMediaElement = mediaElement ;
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
var nowPlayingBar = $ ( '#nowPlayingBar' ) . show ( ) ;
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
//display image and title
var imageTags = item . ImageTags || { } ;
var html = '' ;
var url = "" ;
2013-05-25 18:53:34 -07:00
if ( imageTags . Primary ) {
url = ApiClient . getImageUrl ( item . Id , {
type : "Primary" ,
height : 80 ,
tag : item . ImageTags . Primary
} ) ;
}
else if ( item . BackdropImageTags && item . BackdropImageTags . length ) {
2013-04-10 14:03:28 -07:00
url = ApiClient . getImageUrl ( item . Id , {
type : "Backdrop" ,
2013-05-25 18:53:34 -07:00
height : 80 ,
2013-04-10 14:03:28 -07:00
tag : item . BackdropImageTags [ 0 ]
} ) ;
} else if ( imageTags . Thumb ) {
url = ApiClient . getImageUrl ( item . Id , {
type : "Thumb" ,
2013-05-25 18:53:34 -07:00
height : 80 ,
2013-04-10 14:03:28 -07:00
tag : item . ImageTags . Thumb
} ) ;
2014-01-02 21:58:22 -07:00
}
else if ( item . Type == "Channel" || item . Type == "Recording" ) {
url = "css/images/items/detail/tv.png" ;
}
else if ( item . MediaType == "Audio" ) {
url = "css/images/items/detail/audio.png" ;
}
else {
2013-04-10 14:03:28 -07:00
url = "css/images/items/detail/video.png" ;
}
2013-04-08 21:16:32 -07:00
2013-04-10 14:03:28 -07:00
var name = item . Name ;
var seriesName = '' ;
2013-04-08 21:16:32 -07:00
2014-01-02 21:58:22 -07:00
// Channel number
if ( item . Number ) {
name = item . Number + ' ' + name ;
}
2013-04-10 14:03:28 -07:00
if ( item . IndexNumber != null ) {
name = item . IndexNumber + " - " + name ;
}
if ( item . ParentIndexNumber != null ) {
name = item . ParentIndexNumber + "." + name ;
}
if ( item . SeriesName || item . Album || item . ProductionYear ) {
seriesName = item . SeriesName || item . Album || item . ProductionYear ;
}
2014-01-02 21:58:22 -07:00
if ( item . CurrentProgram ) {
seriesName = item . CurrentProgram . Name ;
}
2013-04-08 21:16:32 -07:00
2014-01-02 21:58:22 -07:00
var href = LibraryBrowser . getHref ( item . CurrentProgram || item ) ;
2013-05-27 18:59:26 -07:00
2014-01-03 13:32:27 -07:00
html += "<div><a href='" + href + "'><img class='nowPlayingBarImage ' alt='' title='' src='" + url + "' style='height:40px;display:inline-block;' /></a></div>" ;
2014-01-02 21:58:22 -07:00
if ( item . SeriesName || item . Album || item . CurrentProgram ) {
2013-05-24 18:51:17 -07:00
html += '<div class="nowPlayingText">' + seriesName + '<br/>' + name + '</div>' ;
2013-05-27 18:59:26 -07:00
} else {
html += '<div class="nowPlayingText">' + name + '<br/>' + seriesName + '</div>' ;
}
2013-03-27 19:01:26 -07:00
2013-05-23 13:09:01 -07:00
$ ( '.nowPlayingMediaInfo' , nowPlayingBar ) . html ( html ) ;
2013-04-10 14:03:28 -07:00
} ;
2013-03-27 19:01:26 -07:00
2013-09-17 19:43:34 -07:00
var getItemFields = "MediaStreams,Chapters,Path" ;
2013-08-09 08:55:22 -07:00
2013-06-07 09:06:32 -07:00
self . getItemsForPlayback = function ( query ) {
var userId = Dashboard . getCurrentUserId ( ) ;
query . Limit = query . Limit || 100 ;
2013-08-09 08:55:22 -07:00
query . Fields = getItemFields ;
2013-06-07 09:06:32 -07:00
return ApiClient . getItems ( userId , query ) ;
} ;
2013-12-22 10:16:24 -07:00
self . playById = function ( id , itemType , startPositionTicks ) {
if ( itemType == "Recording" ) {
2013-12-27 21:42:40 -07:00
2013-12-22 10:16:24 -07:00
ApiClient . getLiveTvRecording ( id , Dashboard . getCurrentUserId ( ) ) . done ( function ( item ) {
self . play ( [ item ] , startPositionTicks ) ;
} ) ;
2013-12-27 21:42:40 -07:00
2013-12-22 10:16:24 -07:00
return ;
}
2013-12-27 21:42:40 -07:00
2013-12-22 10:16:24 -07:00
if ( itemType == "Channel" ) {
ApiClient . getLiveTvChannel ( id , Dashboard . getCurrentUserId ( ) ) . done ( function ( item ) {
self . play ( [ item ] , startPositionTicks ) ;
} ) ;
return ;
}
2013-04-30 10:21:21 -07:00
ApiClient . getItem ( Dashboard . getCurrentUserId ( ) , id ) . done ( function ( item ) {
2013-06-07 09:06:32 -07:00
if ( item . IsFolder ) {
2013-04-30 10:21:21 -07:00
2013-06-07 09:06:32 -07:00
self . getItemsForPlayback ( {
2013-04-30 10:21:21 -07:00
2013-06-07 09:06:32 -07:00
ParentId : id ,
Recursive : true ,
SortBy : "SortName"
} ) . done ( function ( result ) {
self . play ( result . Items , startPositionTicks ) ;
} ) ;
} else {
self . play ( [ item ] , startPositionTicks ) ;
}
} ) ;
2013-04-30 10:21:21 -07:00
2013-05-23 13:09:01 -07:00
} ;
2013-05-03 21:02:46 -07:00
2013-08-09 08:55:22 -07:00
self . playInstantMixFromSong = function ( id ) {
ApiClient . getInstantMixFromSong ( id , {
UserId : Dashboard . getCurrentUserId ( ) ,
Fields : getItemFields ,
Limit : 50
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
self . playInstantMixFromAlbum = function ( id ) {
ApiClient . getInstantMixFromAlbum ( id , {
UserId : Dashboard . getCurrentUserId ( ) ,
Fields : getItemFields ,
Limit : 50
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
self . playInstantMixFromArtist = function ( name ) {
ApiClient . getInstantMixFromArtist ( name , {
UserId : Dashboard . getCurrentUserId ( ) ,
Fields : getItemFields ,
Limit : 50
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
self . playInstantMixFromMusicGenre = function ( name ) {
ApiClient . getInstantMixFromMusicGenre ( name , {
UserId : Dashboard . getCurrentUserId ( ) ,
Fields : getItemFields ,
Limit : 50
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
2013-08-08 07:53:25 -07:00
self . playArtist = function ( artist ) {
self . getItemsForPlayback ( {
Artists : artist ,
Recursive : true ,
SortBy : "Album,SortName" ,
IncludeItemTypes : "Audio"
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
2013-09-03 08:52:46 -07:00
self . shuffleArtist = function ( artist ) {
self . getItemsForPlayback ( {
Artists : artist ,
Recursive : true ,
SortBy : "Random" ,
IncludeItemTypes : "Audio"
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
self . shuffleMusicGenre = function ( genre ) {
self . getItemsForPlayback ( {
Genres : genre ,
Recursive : true ,
SortBy : "Random" ,
IncludeItemTypes : "Audio"
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
self . shuffleFolder = function ( id ) {
self . getItemsForPlayback ( {
ParentId : id ,
Recursive : true ,
SortBy : "Random"
} ) . done ( function ( result ) {
self . play ( result . Items ) ;
} ) ;
} ;
2013-05-23 20:33:33 -07:00
self . toggleFullscreen = function ( ) {
if ( isFullScreen ( ) ) {
if ( document . cancelFullScreen ) { document . cancelFullScreen ( ) ; }
else if ( document . mozCancelFullScreen ) { document . mozCancelFullScreen ( ) ; }
2013-06-16 10:47:25 -07:00
else if ( document . webkitCancelFullScreen ) {
document . webkitCancelFullScreen ( ) ;
} else {
$ ( '.itemVideo' ) . removeClass ( 'fullscreenVideo' ) ;
}
2013-05-23 20:33:33 -07:00
} else {
requestFullScreen ( document . body ) ;
}
} ;
2013-06-07 09:06:32 -07:00
self . removeFromPlaylist = function ( index ) {
2013-06-07 10:23:23 -07:00
2013-06-07 09:06:32 -07:00
self . playlist . remove ( index ) ;
2013-06-07 10:23:23 -07:00
2013-05-23 13:09:01 -07:00
} ;
2013-05-03 21:02:46 -07:00
2013-06-07 09:06:32 -07:00
// Gets or sets the current playlist index
2013-06-07 10:23:23 -07:00
self . currentPlaylistIndex = function ( i ) {
2013-05-03 21:02:46 -07:00
2013-06-07 09:06:32 -07:00
if ( i == null ) {
return currentPlaylistIndex ;
}
2013-05-23 13:09:01 -07:00
2013-06-07 09:06:32 -07:00
var newItem = self . playlist [ i ] ;
2013-05-23 13:09:01 -07:00
2013-06-07 09:06:32 -07:00
Dashboard . getCurrentUser ( ) . done ( function ( user ) {
2013-05-23 13:09:01 -07:00
2013-06-07 09:06:32 -07:00
self . playInternal ( newItem , 0 , user ) ;
2013-06-07 10:23:23 -07:00
currentPlaylistIndex = i ;
2013-06-07 09:06:32 -07:00
} ) ;
2013-04-30 10:21:21 -07:00
} ;
2013-07-18 20:12:52 -07:00
self . nextTrack = function ( ) {
2013-05-23 13:09:01 -07:00
2013-06-07 09:06:32 -07:00
var newIndex = currentPlaylistIndex + 1 ;
var newItem = self . playlist [ newIndex ] ;
2013-05-23 13:09:01 -07:00
2013-07-16 14:55:50 -07:00
if ( newItem ) {
Dashboard . getCurrentUser ( ) . done ( function ( user ) {
2013-06-07 09:06:32 -07:00
2013-07-16 14:55:50 -07:00
self . playInternal ( newItem , 0 , user ) ;
currentPlaylistIndex = newIndex ;
} ) ;
}
2013-05-23 13:09:01 -07:00
} ;
2013-08-09 08:55:22 -07:00
2013-07-18 20:12:52 -07:00
self . previousTrack = function ( ) {
var newIndex = currentPlaylistIndex - 1 ;
if ( newIndex >= 0 ) {
var newItem = self . playlist [ newIndex ] ;
if ( newItem ) {
Dashboard . getCurrentUser ( ) . done ( function ( user ) {
self . playInternal ( newItem , 0 , user ) ;
currentPlaylistIndex = newIndex ;
} ) ;
}
}
} ;
2013-05-03 21:02:46 -07:00
2013-11-30 11:32:39 -07:00
self . queueItemsNext = function ( items ) {
var insertIndex = 1 ;
for ( var i = 0 , length = items . length ; i < length ; i ++ ) {
self . playlist . splice ( insertIndex , 0 , items [ i ] ) ;
insertIndex ++ ;
}
} ;
2013-11-29 23:07:45 -07:00
self . queueItems = function ( items ) {
2013-06-07 09:06:32 -07:00
2013-11-29 23:07:45 -07:00
for ( var i = 0 , length = items . length ; i < length ; i ++ ) {
2013-06-07 09:06:32 -07:00
2013-11-29 23:07:45 -07:00
self . playlist . push ( items [ i ] ) ;
}
2013-04-30 10:21:21 -07:00
} ;
2013-06-07 09:06:32 -07:00
self . queue = function ( id ) {
ApiClient . getItem ( Dashboard . getCurrentUserId ( ) , id ) . done ( function ( item ) {
if ( item . IsFolder ) {
self . getItemsForPlayback ( {
ParentId : id ,
Recursive : true ,
SortBy : "SortName"
} ) . done ( function ( result ) {
2013-11-29 23:07:45 -07:00
self . queueItems ( result . Items ) ;
2013-06-07 09:06:32 -07:00
} ) ;
} else {
2013-11-29 23:07:45 -07:00
self . queueItems ( [ item ] ) ;
2013-06-07 09:06:32 -07:00
}
2013-05-23 13:09:01 -07:00
} ) ;
} ;
2013-08-08 07:53:25 -07:00
self . queueArtist = function ( artist ) {
self . getItemsForPlayback ( {
Artists : artist ,
Recursive : true ,
SortBy : "Album,SortName" ,
IncludeItemTypes : "Audio"
} ) . done ( function ( result ) {
2013-11-29 23:07:45 -07:00
self . queueItems ( result . Items ) ;
2013-08-08 07:53:25 -07:00
} ) ;
} ;
2013-05-23 13:09:01 -07:00
self . pause = function ( ) {
currentMediaElement . pause ( ) ;
} ;
self . unpause = function ( ) {
currentMediaElement . play ( ) ;
} ;
2013-07-17 20:18:16 -07:00
self . seek = function ( position ) {
2013-07-25 12:43:40 -07:00
changeStream ( position ) ;
2013-07-17 20:18:16 -07:00
} ;
2013-08-09 08:55:22 -07:00
2013-05-23 13:09:01 -07:00
self . mute = function ( ) {
2013-08-27 21:31:34 -07:00
if ( currentMediaElement ) {
currentMediaElement . volume = 0 ;
}
2013-05-23 13:09:01 -07:00
} ;
self . unmute = function ( ) {
2013-08-27 21:31:34 -07:00
if ( currentMediaElement ) {
currentMediaElement . volume = volumeSlider . val ( ) ;
}
} ;
self . toggleMute = function ( ) {
if ( currentMediaElement ) {
currentMediaElement . volume = currentMediaElement . volume ? 0 : volumeSlider . val ( ) ;
}
} ;
2013-08-27 22:44:43 -07:00
self . volumeDown = function ( ) {
2013-08-27 21:31:34 -07:00
if ( currentMediaElement ) {
2013-08-28 07:01:04 -07:00
currentMediaElement . volume = Math . max ( currentMediaElement . volume - . 02 , 0 ) ;
2013-08-27 22:44:43 -07:00
2013-12-27 21:42:40 -07:00
volumeSlider . val ( currentMediaElement . volume ) . slider ( 'refresh' ) ;
2013-08-27 21:31:34 -07:00
}
} ;
2013-08-27 22:44:43 -07:00
self . volumeUp = function ( ) {
2013-08-27 21:31:34 -07:00
if ( currentMediaElement ) {
2013-08-28 07:01:04 -07:00
currentMediaElement . volume = Math . min ( currentMediaElement . volume + . 02 , 1 ) ;
2013-08-27 22:44:43 -07:00
2013-12-27 21:42:40 -07:00
volumeSlider . val ( currentMediaElement . volume ) . slider ( 'refresh' ) ;
2013-08-27 21:31:34 -07:00
}
2013-04-30 10:21:21 -07:00
} ;
2013-04-10 14:03:28 -07:00
self . stop = function ( ) {
2013-03-31 18:52:07 -07:00
2013-04-10 14:03:28 -07:00
var elem = currentMediaElement ;
2013-03-31 18:52:07 -07:00
2013-05-23 20:33:33 -07:00
elem . pause ( ) ;
2013-02-20 18:33:05 -07:00
2013-06-07 10:23:23 -07:00
$ ( elem ) . off ( 'ended.playnext' ) . on ( 'ended' , function ( ) {
$ ( this ) . remove ( ) ;
elem . src = "" ;
currentMediaElement = null ;
2013-03-26 14:32:01 -07:00
2013-06-07 10:23:23 -07:00
} ) . trigger ( 'ended' ) ;
2013-02-27 10:53:42 -07:00
2013-04-10 14:03:28 -07:00
$ ( '#nowPlayingBar' ) . hide ( ) ;
2013-03-26 14:32:01 -07:00
2013-12-29 19:41:22 -07:00
if ( isFullScreen ( ) ) {
exitFullScreen ( ) ;
}
2013-04-10 14:03:28 -07:00
} ;
2013-03-26 14:32:01 -07:00
2013-04-10 14:03:28 -07:00
self . isPlaying = function ( ) {
return currentMediaElement ;
} ;
2013-05-25 17:53:51 -07:00
2013-05-25 18:53:34 -07:00
function hideFlyout ( flyout ) {
2014-01-02 21:58:22 -07:00
2013-05-25 18:53:34 -07:00
flyout . hide ( ) . empty ( ) ;
2014-01-02 21:58:22 -07:00
2013-05-25 18:53:34 -07:00
$ ( document . body ) . off ( "mousedown.hidesearchhints" ) ;
}
2013-05-25 17:53:51 -07:00
function showFlyout ( flyout , button ) {
$ ( document . body ) . off ( "mousedown.mediaflyout" ) . on ( "mousedown.mediaflyout" , function ( e ) {
var elem = $ ( e . target ) ;
var flyoutId = flyout [ 0 ] . id ;
var safeItems = button + ',#' + flyoutId ;
if ( ! elem . is ( safeItems ) && ! elem . parents ( safeItems ) . length ) {
2013-05-25 18:53:34 -07:00
hideFlyout ( flyout ) ;
2013-05-25 17:53:51 -07:00
}
} ) ;
flyout . show ( ) ;
}
2013-05-25 18:53:34 -07:00
function getChaptersFlyoutHtml ( item ) {
var html = '' ;
2013-05-25 21:52:14 -07:00
var currentTicks = getCurrentTicks ( ) ;
2013-12-21 11:37:34 -07:00
var chapters = item . Chapters || [ ] ;
for ( var i = 0 , length = chapters . length ; i < length ; i ++ ) {
2013-05-25 18:53:34 -07:00
2013-12-21 11:37:34 -07:00
var chapter = chapters [ i ] ;
2013-05-25 18:53:34 -07:00
var isSelected = false ;
if ( currentTicks >= chapter . StartPositionTicks ) {
2013-12-21 11:37:34 -07:00
var nextChapter = chapters [ i + 1 ] ;
2013-05-25 18:53:34 -07:00
isSelected = ! nextChapter || currentTicks < nextChapter . StartPositionTicks ;
}
if ( isSelected ) {
html += '<div data-positionticks="' + chapter . StartPositionTicks + '" class="mediaFlyoutOption selectedMediaFlyoutOption">' ;
} else {
html += '<div data-positionticks="' + chapter . StartPositionTicks + '" class="mediaFlyoutOption">' ;
}
var imgUrl ;
if ( chapter . ImageTag ) {
imgUrl = ApiClient . getImageUrl ( item . Id , {
maxwidth : 200 ,
tag : chapter . ImageTag ,
type : "Chapter" ,
index : i
} ) ;
} else {
imgUrl = "css/images/media/chapterflyout.png" ;
}
html += '<img class="mediaFlyoutOptionImage" src="' + imgUrl + '" />' ;
html += '<div class="mediaFlyoutOptionContent">' ;
var name = chapter . Name || "Chapter " + ( i + 1 ) ;
html += '<div class="mediaFlyoutOptionName">' + name + '</div>' ;
2013-06-07 10:29:33 -07:00
html += '<div class="mediaFlyoutOptionSecondaryText">' + Dashboard . getDisplayTime ( chapter . StartPositionTicks ) + '</div>' ;
2013-05-25 18:53:34 -07:00
html += '</div>' ;
html += "</div>" ;
}
return html ;
}
2013-05-25 21:52:14 -07:00
function getAudioTracksHtml ( item , cultures ) {
var streams = item . MediaStreams . filter ( function ( i ) {
return i . Type == "Audio" ;
} ) ;
var currentIndex = getParameterByName ( 'AudioStreamIndex' , currentMediaElement . currentSrc ) ;
var html = '' ;
for ( var i = 0 , length = streams . length ; i < length ; i ++ ) {
var stream = streams [ i ] ;
if ( stream . Index == currentIndex ) {
html += '<div data-index="' + stream . Index + '" class="mediaFlyoutOption selectedMediaFlyoutOption">' ;
} else {
html += '<div data-index="' + stream . Index + '" class="mediaFlyoutOption">' ;
}
html += '<img class="mediaFlyoutOptionImage" src="css/images/media/audioflyout.png" />' ;
html += '<div class="mediaFlyoutOptionContent">' ;
var language = null ;
if ( stream . Language && stream . Language != "und" ) {
var culture = cultures . filter ( function ( current ) {
return current . ThreeLetterISOLanguageName . toLowerCase ( ) == stream . Language . toLowerCase ( ) ;
} ) ;
if ( culture . length ) {
language = culture [ 0 ] . DisplayName ;
}
}
html += '<div class="mediaFlyoutOptionName">' + ( language || 'Unknown language' ) + '</div>' ;
var options = [ ] ;
if ( stream . Codec ) {
options . push ( stream . Codec ) ;
}
if ( stream . Profile ) {
options . push ( stream . Profile ) ;
}
if ( stream . BitRate ) {
2013-05-28 10:25:10 -07:00
options . push ( ( Math . floor ( stream . BitRate / 1000 ) ) + ' kbps' ) ;
2013-05-25 21:52:14 -07:00
}
if ( stream . Channels ) {
options . push ( stream . Channels + ' ch' ) ;
}
if ( options . length ) {
html += '<div class="mediaFlyoutOptionSecondaryText">' + options . join ( ' • ' ) + '</div>' ;
}
options = [ ] ;
if ( stream . IsDefault ) {
options . push ( 'Default' ) ;
}
if ( stream . IsForced ) {
options . push ( 'Forced' ) ;
}
if ( options . length ) {
html += '<div class="mediaFlyoutOptionSecondaryText">' + options . join ( ' • ' ) + '</div>' ;
}
html += "</div>" ;
html += "</div>" ;
}
return html ;
}
function getSubtitleTracksHtml ( item , cultures ) {
var streams = item . MediaStreams . filter ( function ( i ) {
return i . Type == "Subtitle" ;
} ) ;
2013-10-23 11:58:05 -07:00
var currentIndex = getParameterByName ( 'SubtitleStreamIndex' , currentMediaElement . currentSrc ) || - 1 ;
2013-05-25 21:52:14 -07:00
var html = '' ;
for ( var i = 0 , length = streams . length ; i < length ; i ++ ) {
var stream = streams [ i ] ;
if ( stream . Index == currentIndex ) {
html += '<div data-index="' + stream . Index + '" class="mediaFlyoutOption selectedMediaFlyoutOption">' ;
} else {
html += '<div data-index="' + stream . Index + '" class="mediaFlyoutOption">' ;
}
2013-05-26 12:58:37 -07:00
html += '<img class="mediaFlyoutOptionImage" src="css/images/media/subtitleflyout.png" />' ;
2013-05-25 21:52:14 -07:00
html += '<div class="mediaFlyoutOptionContent">' ;
var language = null ;
if ( stream . Language && stream . Language != "und" ) {
var culture = cultures . filter ( function ( current ) {
return current . ThreeLetterISOLanguageName . toLowerCase ( ) == stream . Language . toLowerCase ( ) ;
} ) ;
if ( culture . length ) {
language = culture [ 0 ] . DisplayName ;
}
}
html += '<div class="mediaFlyoutOptionName">' + ( language || 'Unknown language' ) + '</div>' ;
var options = [ ] ;
if ( stream . Codec ) {
options . push ( stream . Codec ) ;
}
if ( options . length ) {
html += '<div class="mediaFlyoutOptionSecondaryText">' + options . join ( ' • ' ) + '</div>' ;
}
options = [ ] ;
if ( stream . IsDefault ) {
options . push ( 'Default' ) ;
}
if ( stream . IsForced ) {
options . push ( 'Forced' ) ;
}
if ( stream . IsExternal ) {
options . push ( 'External' ) ;
}
if ( options . length ) {
html += '<div class="mediaFlyoutOptionSecondaryText">' + options . join ( ' • ' ) + '</div>' ;
}
html += "</div>" ;
html += "</div>" ;
}
return html ;
}
2013-05-26 12:58:37 -07:00
function getQualityFlyoutHtml ( item ) {
var html = '' ;
2014-01-08 21:45:09 -07:00
var currentAudioStreamIndex = getParameterByName ( 'AudioStreamIndex' , currentMediaElement . currentSrc ) ;
var options = getVideoQualityOptions ( item , currentAudioStreamIndex ) ;
2013-05-26 12:58:37 -07:00
for ( var i = 0 , length = options . length ; i < length ; i ++ ) {
var option = options [ i ] ;
var cssClass = "mediaFlyoutOption" ;
2013-05-27 18:59:26 -07:00
2014-01-08 13:29:09 -07:00
if ( option . selected ) {
2013-05-26 12:58:37 -07:00
cssClass += " selectedMediaFlyoutOption" ;
}
2014-01-08 21:45:09 -07:00
html += '<div data-static="' + option . isStatic + '" data-mp4video="' + option . mp4VideoCodec + '" data-mp4audio="' + option . mp4AudioCodec + '" data-maxwidth="' + option . maxWidth + '" data-videobitrate="' + option . videoBitrate + '" data-audiobitrate="' + option . audioBitrate + '" class="' + cssClass + '">' ;
2013-05-26 12:58:37 -07:00
html += '<div class="mediaFlyoutOptionContent">' ;
html += '<div class="mediaFlyoutOptionName" style="padding:.5em;">' + option . name + '</div>' ;
html += "</div>" ;
html += "</div>" ;
}
return html ;
}
2014-01-02 21:58:22 -07:00
function getChannelsFlyoutHtml ( channels ) {
var html = '' ;
for ( var i = 0 , length = channels . length ; i < length ; i ++ ) {
var channel = channels [ i ] ;
html += '<div data-channelid="' + channel . Id + '" class="mediaFlyoutOption">' ;
var imgUrl ;
if ( channel . ImageTags . Primary ) {
imgUrl = ApiClient . getUrl ( "LiveTV/Channels/" + channel . Id + "/Images/Primary" , {
maxwidth : 200 ,
tag : channel . ImageTags . Primary ,
type : "Primary"
} ) ;
}
else {
imgUrl = "css/images/media/tvflyout.png" ;
}
html += '<img class="mediaFlyoutOptionImage" src="' + imgUrl + '" />' ;
html += '<div class="mediaFlyoutOptionContent">' ;
var name = channel . Number + ' ' + channel . Name ;
html += '<div class="mediaFlyoutOptionName">' + name + '</div>' ;
html += '<div class="mediaFlyoutOptionSecondaryText">' + channel . CurrentProgram . Name + '</div>' ;
html += '</div>' ;
html += "</div>" ;
}
return html ;
}
self . showChannelsFlyout = function ( ) {
var flyout = $ ( '#channelsFlyout' ) ;
2014-01-03 21:53:49 -07:00
if ( ! flyout . is ( ':visible' ) ) {
getChannelsListPromise ( ) . done ( function ( result ) {
showFlyout ( flyout , '#channelsButton' ) ;
2014-01-02 21:58:22 -07:00
2014-01-03 21:53:49 -07:00
flyout . html ( getChannelsFlyoutHtml ( result . Items ) ) . scrollTop ( 0 ) ;
} ) ;
}
2014-01-02 21:58:22 -07:00
} ;
2013-05-25 17:53:51 -07:00
self . showAudioTracksFlyout = function ( ) {
var flyout = $ ( '#audioTracksFlyout' ) ;
if ( ! flyout . is ( ':visible' ) ) {
2013-05-25 21:52:14 -07:00
culturesPromise = culturesPromise || ApiClient . getCultures ( ) ;
2013-05-25 17:53:51 -07:00
2013-05-25 21:52:14 -07:00
culturesPromise . done ( function ( cultures ) {
showFlyout ( flyout , '#audioTracksButton' ) ;
flyout . html ( getAudioTracksHtml ( currentItem , cultures ) ) . scrollTop ( 0 ) ;
} ) ;
2013-05-25 17:53:51 -07:00
}
} ;
self . showChaptersFlyout = function ( ) {
var flyout = $ ( '#chaptersFlyout' ) ;
if ( ! flyout . is ( ':visible' ) ) {
showFlyout ( flyout , '#chaptersButton' ) ;
2013-05-25 21:52:14 -07:00
flyout . html ( getChaptersFlyoutHtml ( currentItem ) ) . scrollTop ( 0 ) ;
2013-05-25 17:53:51 -07:00
}
} ;
self . showQualityFlyout = function ( ) {
var flyout = $ ( '#qualityFlyout' ) ;
if ( ! flyout . is ( ':visible' ) ) {
showFlyout ( flyout , '#qualityButton' ) ;
2013-05-26 12:58:37 -07:00
flyout . html ( getQualityFlyoutHtml ( currentItem ) ) . scrollTop ( 0 ) ;
2013-05-25 17:53:51 -07:00
}
} ;
self . showSubtitleMenu = function ( ) {
var flyout = $ ( '#subtitleFlyout' ) ;
if ( ! flyout . is ( ':visible' ) ) {
2013-05-25 21:52:14 -07:00
culturesPromise = culturesPromise || ApiClient . getCultures ( ) ;
culturesPromise . done ( function ( cultures ) {
showFlyout ( flyout , '#subtitleButton' ) ;
flyout . html ( getSubtitleTracksHtml ( currentItem , cultures ) ) . scrollTop ( 0 ) ;
} ) ;
2013-05-25 17:53:51 -07:00
}
} ;
2014-01-03 21:53:49 -07:00
self . showSendMediaMenu = function ( ) {
RemoteControl . showMenuForItem ( {
item : currentItem
} ) ;
2014-01-08 13:29:09 -07:00
2014-01-03 21:53:49 -07:00
} ;
2013-04-10 14:03:28 -07:00
}
2013-03-26 14:32:01 -07:00
2013-04-10 14:03:28 -07:00
window . MediaPlayer = new mediaPlayer ( ) ;
2013-04-08 21:26:02 -07:00
2013-05-23 20:33:33 -07:00
} ) ( document , setTimeout , clearTimeout , screen , localStorage , $ , setInterval , window ) ;