2013-05-23 20:33:33 -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 ;
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
}
}
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' ) ;
$ ( '.itemVideo' ) . unbind ( '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 ;
$ ( '.itemVideo' ) . bind ( 'mousemove keydown scroll' , idleHandler ) . trigger ( 'mousemove' ) ;
2013-05-23 20:33:33 -07:00
} else {
2013-07-17 20:36:32 -07:00
$ ( ".mediaButton,.currentTime,.nowPlayingMediaInfo,.mediaSlider,.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-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 ( ) ;
2013-07-11 13:25:12 -07:00
var intervalTime = ApiClient . isWebSocketOpen ( ) ? 5000 : 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-07-25 12:43:40 -07:00
ApiClient . reportPlaybackProgress ( Dashboard . getCurrentUserId ( ) , itemId , getCurrentTicks ( ) , currentMediaElement . paused ) ;
2013-05-23 15:39:51 -07:00
}
2013-05-23 13:09:01 -07:00
function clearProgressInterval ( ) {
if ( currentProgressInterval ) {
clearTimeout ( currentProgressInterval ) ;
currentProgressInterval = null ;
}
}
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 ;
2013-05-23 18:47:07 -07:00
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 ) ;
}
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-05-25 18:53:34 -07:00
startTimeTicksOffset = ticks ;
2013-05-23 18:47:07 -07:00
element . src = currentSrc ;
}
}
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' ) ;
volumeSlider = $ ( '.volumeSlider' ) . on ( 'change' , function ( ) {
var vol = this . value ;
updateVolumeButtons ( vol ) ;
currentMediaElement . volume = vol ;
} ) ;
2013-05-23 15:39:51 -07:00
$ ( ".jqueryuislider" ) . slider ( { orientation : "horizontal" } ) ;
2013-05-23 18:47:07 -07:00
positionSlider = $ ( ".positionSlider" ) . on ( 'mousedown' , function ( ) {
2013-05-23 13:09:01 -07:00
isPositionSliderActive = true ;
2013-05-23 18:47:07 -07:00
} ) ;
2013-05-23 13:09:01 -07:00
2013-05-23 18:47:07 -07:00
if ( $ . browser . mozilla ) {
2013-05-23 13:09:01 -07:00
2013-05-23 18:47:07 -07:00
positionSlider . on ( 'change' , onPositionSliderChange ) ;
2013-05-23 13:09:01 -07:00
2013-05-23 18:47:07 -07:00
} else {
2013-05-23 13:09:01 -07:00
2013-05-23 18:47:07 -07:00
positionSlider . on ( 'change' , function ( ) {
2013-05-23 13:09:01 -07:00
2013-05-23 18:47:07 -07:00
isPositionSliderActive = true ;
2013-05-23 13:09:01 -07:00
2013-05-23 18:47:07 -07:00
setCurrentTimePercent ( parseInt ( this . value ) , currentItem ) ;
2013-05-23 13:09:01 -07:00
2013-05-23 18:47:07 -07:00
} ) . on ( 'changed' , onPositionSliderChange ) ;
}
2013-05-23 13:09:01 -07:00
( function ( el , timeout ) {
var timer , trig = function ( ) { el . trigger ( "changed" ) ; } ;
el . bind ( "change" , function ( ) {
if ( timer ) {
clearTimeout ( timer ) ;
}
timer = setTimeout ( trig , timeout ) ;
} ) ;
} ) ( positionSlider , 500 ) ;
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' ) ) ;
changeStream ( getCurrentTicks ( ) , { AudioStreamIndex : index } ) ;
}
hideFlyout ( $ ( '#audioTracksFlyout' ) ) ;
} ) ;
$ ( '#subtitleFlyout' ) . on ( 'click' , '.mediaFlyoutOption' , function ( ) {
if ( ! $ ( this ) . hasClass ( 'selectedMediaFlyoutOption' ) ) {
var index = parseInt ( this . getAttribute ( 'data-index' ) ) ;
changeStream ( getCurrentTicks ( ) , { SubtitleStreamIndex : index } ) ;
}
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' ) ) ;
changeStream ( getCurrentTicks ( ) , { MaxWidth : maxWidth , VideoBitrate : videoBitrate } ) ;
}
hideFlyout ( $ ( '#qualityFlyout' ) ) ;
} ) ;
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 setCurrentTimePercent ( percent , item ) {
2013-04-08 21:26:02 -07:00
2013-05-23 13:09:01 -07:00
var position = ( percent / 100 ) * curentDurationTicks ;
setCurrentTime ( position , item , false ) ;
}
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 ;
positionSlider . val ( percent ) ;
2013-05-27 18:59:26 -07:00
2013-05-26 18:25:09 -07:00
positionSlider . removeAttr ( 'disabled' ) ;
2013-05-23 13:09:01 -07:00
}
2013-05-26 18:25:09 -07:00
} else {
positionSlider . attr ( 'disabled' , 'disabled' ) ;
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 ( ) ;
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-05-23 15:39:51 -07:00
volumeSlider . val ( initialVolume ) ;
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-05-23 13:09:01 -07:00
ApiClient . reportPlaybackStart ( Dashboard . getCurrentUserId ( ) , item . Id ) ;
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
}
2013-05-24 19:45:12 -07:00
function playVideo ( item , startPosition , user ) {
2013-04-28 10:36:20 -07:00
2013-04-10 14:03:28 -07:00
// Account for screen rotation. Use the larger dimension as the width.
var screenWidth = Math . max ( screen . height , screen . width ) ;
var baseParams = {
audioChannels : 2 ,
audioBitrate : 128000 ,
2013-05-26 12:58:37 -07:00
videoBitrate : 2000000 ,
maxWidth : Math . min ( screenWidth , 1280 ) ,
2013-04-16 21:38:42 -07:00
StartTimeTicks : 0 ,
2013-05-24 19:45:12 -07:00
SubtitleStreamIndex : getInitialSubtitleStreamIndex ( item . MediaStreams , user ) ,
AudioStreamIndex : getInitialAudioStreamIndex ( item . MediaStreams , user )
2013-04-10 14:03:28 -07:00
} ;
2013-05-26 12:58:37 -07:00
var videoStream = item . MediaStreams . filter ( function ( i ) {
return i . Type == "Video" ;
} ) [ 0 ] ;
if ( videoStream && videoStream . Width ) {
if ( videoStream . Width >= 1280 ) {
baseParams . videoBitrate = 2000000 ;
}
else if ( videoStream . Width >= 720 ) {
baseParams . videoBitrate = 400000 ;
}
}
2013-05-23 20:33:33 -07:00
if ( startPosition ) {
baseParams . StartTimeTicks = startPosition ;
2013-04-10 14:03:28 -07:00
}
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 , {
videoCodec : 'h264' ,
audioCodec : 'aac' ,
2013-05-26 12:58:37 -07:00
profile : 'baseline' ,
level : 3
2013-05-23 20:33:33 -07:00
} ) ) ;
2013-02-20 18:33:05 -07:00
2013-05-28 18:45:39 -07:00
var tsVideoUrl = ApiClient . getUrl ( 'Videos/' + item . Id + '/stream.ts' , $ . extend ( { } , baseParams , {
videoCodec : 'h264' ,
audioCodec : 'aac' ,
profile : 'baseline' ,
level : 3
} ) ) ;
2013-05-23 20:33:33 -07:00
var webmVideoUrl = ApiClient . getUrl ( 'Videos/' + item . Id + '/stream.webm' , $ . extend ( { } , baseParams , {
videoCodec : 'vpx' ,
2013-05-26 12:58:37 -07:00
audioCodec : 'Vorbis'
2013-05-23 20:33:33 -07:00
} ) ) ;
var hlsVideoUrl = ApiClient . getUrl ( 'Videos/' + item . Id + '/stream.m3u8' , $ . extend ( { } , baseParams , {
videoCodec : 'h264' ,
2013-05-26 12:58:37 -07:00
audioCodec : 'aac' ,
profile : 'baseline' ,
level : 3
2013-05-23 20:33:33 -07:00
} ) ) ;
2013-04-10 14:03:28 -07:00
2013-05-28 21:00:24 -07:00
var ogvVideoUrl = ApiClient . getUrl ( 'Videos/' + item . Id + '/stream.ogv' , $ . extend ( { } , baseParams , {
videoCodec : 'theora' ,
audioCodec : 'Vorbis'
} ) ) ;
2013-05-23 20:33:33 -07:00
var html = '' ;
// HLS must be at the top for safari
// Webm must be ahead of mp4 due to the issue of mp4 playing too fast in chrome
2013-04-17 18:32:38 -07:00
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
}
html += '<source type="application/x-mpegURL" src="' + hlsVideoUrl + '" />' ;
2013-05-28 18:45:39 -07:00
html += '<source type="video/mp2t" src="' + tsVideoUrl + '" />' ;
2013-05-23 20:33:33 -07:00
html += '<source type="video/webm" src="' + webmVideoUrl + '" />' ;
html += '<source type="video/mp4" src="' + mp4VideoUrl + '" />' ;
2013-05-28 21:00:24 -07:00
html += '<source type="video/ogg" src="' + ogvVideoUrl + '" />' ;
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-05-27 18:59:26 -07:00
if ( item . 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
if ( item . MediaStreams . filter ( function ( i ) {
return i . Type == "Subtitle" ;
} ) . length ) {
$ ( '#subtitleButton' , nowPlayingBar ) . show ( ) ;
} else {
$ ( '#subtitleButton' , nowPlayingBar ) . hide ( ) ;
}
if ( item . Chapters . length ) {
$ ( '#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
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-05-23 20:33:33 -07:00
volumeSlider . val ( initialVolume ) ;
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
2013-05-23 20:33:33 -07:00
ApiClient . reportPlaybackStart ( Dashboard . getCurrentUserId ( ) , item . Id ) ;
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-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 ;
return videoElement [ 0 ] ;
2013-05-24 19:45:12 -07:00
} ;
function getInitialAudioStreamIndex ( mediaStreams , user ) {
2013-05-25 21:52:14 -07:00
var audioStreams = mediaStreams . filter ( function ( stream ) {
return stream . Type == "Audio" ;
} ) ;
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
if ( mediaStream . Type == "Audio" && mediaStream . Language == user . Configuration . AudioLanguagePreference ) {
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 ) {
$ ( ".mediaButton,.currentTime,.nowPlayingMediaInfo,.mediaSlider,.barBackground" , nowPlayingBar ) . addClass ( "highPosition" ) ;
}
idleState = false ;
timeout = window . setTimeout ( function ( ) {
idleState = true ;
$ ( ".mediaButton,.currentTime,.nowPlayingMediaInfo,.mediaSlider,.barBackground" , nowPlayingBar ) . removeClass ( "highPosition" ) ;
} , 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-08-09 08:55:22 -07:00
if ( item . Type == "MusicAlbum" || item . Type == "MusicArtist" || item . Type == "Artist" || item . Type == "MusicGenre" ) {
2013-06-07 09:06:32 -07:00
return true ;
}
2013-05-23 18:47:07 -07:00
return self . canPlayMediaType ( item . MediaType ) ;
} ;
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-05-24 18:51:17 -07:00
2013-05-24 19:45:12 -07:00
var videoType = ( item . VideoType || "" ) . toLowerCase ( ) ;
2013-05-24 18:51:17 -07:00
2013-07-28 07:35:18 -07:00
var expirementalText = "This feature is expiremental. It may not work at all with some titles. Do you wish to continue?" ;
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 ;
var nowPlayingBar = $ ( '#nowPlayingBar' ) ;
} ;
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
} ) ;
} else {
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
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 ;
}
2013-04-08 21:16:32 -07:00
2013-04-27 12:38:24 -07:00
html += "<div><a href='itemdetails.html?id=" + item . Id + "'><img class='nowPlayingBarImage ' alt='' title='' src='" + url + "' style='height:36px;display:inline-block;' /></a></div>" ;
2013-05-27 18:59:26 -07:00
if ( item . SeriesName || item . Album ) {
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-08-09 08:55:22 -07:00
var getItemFields = "MediaStreams,UserData,DisplayMediaType,Chapters,Path" ;
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-04-30 10:21:21 -07:00
self . playById = function ( id , startPositionTicks ) {
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-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-06-07 09:06:32 -07:00
self . queueItem = function ( item ) {
self . playlist . push ( item ) ;
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 ) {
for ( var i = 0 , length = result . Items . length ; i < length ; i ++ ) {
self . queueItem ( result . Items [ i ] ) ;
}
} ) ;
} else {
self . queueItem ( item ) ;
}
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 ) {
for ( var i = 0 , length = result . Items . length ; i < length ; i ++ ) {
self . queueItem ( result . Items [ i ] ) ;
}
} ) ;
} ;
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
volumeSlider . val ( currentMediaElement . volume ) ;
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
volumeSlider . val ( currentMediaElement . volume ) ;
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-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 ) {
flyout . hide ( ) . empty ( ) ;
$ ( 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-05-25 18:53:34 -07:00
for ( var i = 0 , length = item . Chapters . length ; i < length ; i ++ ) {
var chapter = item . Chapters [ i ] ;
var isSelected = false ;
if ( currentTicks >= chapter . StartPositionTicks ) {
var nextChapter = item . Chapters [ i + 1 ] ;
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" ;
} ) ;
var currentIndex = getParameterByName ( 'SubtitleStreamIndex' , 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">' ;
}
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 = '' ;
var videoStream = item . MediaStreams . filter ( function ( i ) {
return i . Type == "Video" ;
} ) [ 0 ] ;
var currentVideoBitrate = getParameterByName ( 'videoBitrate' , currentMediaElement . currentSrc ) || getParameterByName ( 'VideoBitrate' , currentMediaElement . currentSrc ) ;
var maxAllowedWidth = Math . max ( screen . height , screen . width ) ;
var options = [ ] ;
// We have media info
if ( videoStream && videoStream . Width ) {
maxAllowedWidth = Math . min ( videoStream . Width , maxAllowedWidth ) ;
}
// Some 1080- videos are reported as 1912?
if ( maxAllowedWidth >= 1910 ) {
options . push ( { name : '1080p+' , maxWidth : 1920 , videoBitrate : 4000000 } ) ;
options . push ( { name : '1080p' , maxWidth : 1920 , videoBitrate : 2500000 } ) ;
}
if ( maxAllowedWidth >= 1280 ) {
options . push ( { name : '720p+' , maxWidth : 1280 , videoBitrate : 2000000 } ) ;
options . push ( { name : '720p' , maxWidth : 1280 , videoBitrate : 1000000 } ) ;
}
if ( maxAllowedWidth >= 480 ) {
options . push ( { name : '480p' , maxWidth : 720 , videoBitrate : 400000 } ) ;
}
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
2013-05-26 12:58:37 -07:00
if ( option . videoBitrate == currentVideoBitrate ) {
cssClass += " selectedMediaFlyoutOption" ;
}
html += '<div data-maxwidth="' + option . maxWidth + '" data-videobitrate="' + option . videoBitrate + '" class="' + cssClass + '">' ;
html += '<div class="mediaFlyoutOptionContent">' ;
html += '<div class="mediaFlyoutOptionName" style="padding:.5em;">' + option . name + '</div>' ;
html += "</div>" ;
html += "</div>" ;
}
return html ;
}
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
}
} ;
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 ) ;