1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

extract nowplayingbar into standalone widget to work with any player

This commit is contained in:
Luke Pulverenti 2014-04-11 11:36:25 -04:00
parent ad3f285ded
commit bd697d36fc
15 changed files with 879 additions and 715 deletions

View file

@ -212,10 +212,6 @@
/****************************************/
@media all and (max-width: 1200px), all and (max-height: 720px) {
#videoControls .nowPlayingMediaInfo {
display: none;
}
#videoControls .currentTime {
margin-right: 0;
min-width: 120px;
@ -371,3 +367,86 @@
.cursor-inactive {
cursor: none;
}
.mediaFlyoutContainer {
display: inline-block;
}
.mediaPlayerFlyout {
width: 200px;
color: #000;
background-color: #fff;
border: 1px solid #999;
position: absolute;
z-index: 99999;
bottom: 78px;
margin-left: -125px;
max-height: 300px;
overflow-y: auto;
font-size: 13px;
}
.chaptersFlyout, .audioTracksFlyout {
width: 250px;
margin-left: -150px;
}
.mediaFlyoutOption {
display: block;
text-decoration: none;
color: #000;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.mediaFlyoutOption:hover, .mediaFlyoutOption:focus {
background-color: #eee;
}
.selectedMediaFlyoutOption {
background-color: #d9F4FF;
background-image: url(images/media/selected.png);
background-repeat: no-repeat;
background-position: right top;
background-size: 16px 16px;
}
.mediaFlyoutOptionImage {
display: inline-block;
width: 15%;
vertical-align: middle;
}
.mediaFlyoutOptionImage + .mediaFlyoutOptionContent {
vertical-align: top;
display: inline-block;
width: 85%;
}
.chaptersFlyout .mediaFlyoutOptionImage {
width: 40%;
}
.chaptersFlyout .mediaFlyoutOptionImage + .mediaFlyoutOptionContent {
width: 60%;
}
.mediaFlyoutOptionName {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
padding-left: 5px;
font-size: 13px;
font-weight: normal;
}
.mediaFlyoutOptionSecondaryText {
font-size: 13px;
color: #333;
font-weight: normal;
margin-top: 3px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
padding-left: 5px;
}

View file

@ -1,21 +1,9 @@
/* Now playing bar */
.nowPlayingBar {
padding: 6px 0 20px 0;
padding: 10px 0 14px 0;
border-top: 2px solid green;
}
.nowPlayingBar .barBackground {
border-top: 2px solid green;
position: absolute;
margin: -8px -0.5em -22px !important;
width: 100%;
height: 100%;
}
.nowPlayingBar > *:not(#mediaElement):not(.mediaFlyoutContainer):not(.barBackground ) {
position: relative;
}
.nowPlayingBarImage {
vertical-align: bottom;
}
@ -29,27 +17,32 @@
position: relative;
}
.nowPlayingMediaInfo div {
display: inline-block;
}
.nowPlayingMediaInfo a {
margin-right: .25em;
}
.nowPlayingMediaInfo {
display: inline-block;
}
.nowPlayingText {
position: relative;
top: -3px;
margin-left: 3px;
margin-right: 2em;
display: inline-block;
font-weight: normal;
position: relative;
top: -7px;
margin-left: 3px;
max-width: 200px;
overflow: hidden;
margin-right: 2em;
white-space: nowrap;
}
.nowPlayingDoubleText {
top: 0;
}
.nowPlayingImage {
display: inline-block;
}
.nowPlayingImage img {
height: 40px;
margin-right: .5em;
vertical-align: bottom;
}
.mediaButton img {
height: 24px;
}
@ -60,7 +53,6 @@
top: -10px;
max-width: 110px;
margin-right: 2em;
font-size: 14px;
font-weight: normal;
}
@ -127,129 +119,44 @@ input[type="range"]::-ms-fill-upper {
}
@media all and (max-width: 800px) {
#nowPlayingBar .volumeButton, #nowPlayingBar .volumeSliderContainer, #nowPlayingBar .muteButton, #nowPlayingBar .unmuteButton, #nowPlayingBar .nowPlayingMediaInfo {
display: none; /*!important;*/
}
}
@media all and (max-width: 650px) {
#nowPlayingBar .nowPlayingMediaInfo {
#nowPlayingBar .mediaButton {
margin-top: 0;
margin-bottom: 0;
}
#nowPlayingBar .mediaButton:not(#playButton):not(#pauseButton) {
display: none;
}
}
@media all and (max-width: 600px) {
#nowPlayingBar .chaptersButton, #nowPlayingBar .audioTracksButton {
display: none; /*!important;*/
#nowPlayingBar #playButton, #nowPlayingBar #pauseButton {
float: right;
}
#nowPlayingBar .positionSliderContainer, #nowPlayingBar .currentTime {
top: 0!important;
position: relative!important;
}
}
@media all and (max-width: 500px) {
#nowPlayingBar .positionSliderContainer {
width: 80px;
#nowPlayingBar .currentTime, #nowPlayingBar .positionSliderContainer, #nowPlayingBar .volumeSliderContainer, #nowPlayingBar .muteButton, #nowPlayingBar .unmuteButton {
display: none !important;
}
#nowPlayingBar .previousTrackButton, #nowPlayingBar .nextTrackButton {
display: none; /*!important;*/
.nowPlayingBar {
padding: 10px 5px 10px 0;
}
}
@media all and (max-width: 400px) {
#nowPlayingBar .playlistButton {
display: none; /*!important;*/
}
}
.mediaFlyoutContainer {
display: inline-block;
}
.mediaPlayerFlyout {
width: 200px;
color: #000;
background-color: #fff;
border: 1px solid #999;
position: absolute;
z-index: 99999;
bottom: 78px;
margin-left: -125px;
max-height: 300px;
overflow-y: auto;
font-size: 13px;
}
.chaptersFlyout,.audioTracksFlyout {
width: 250px;
margin-left: -150px;
}
.mediaFlyoutOption {
display: block;
text-decoration: none;
color: #000;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.mediaFlyoutOption:hover, .mediaFlyoutOption:focus {
background-color: #eee;
}
.selectedMediaFlyoutOption {
background-color: #d9F4FF;
background-image: url(images/media/selected.png);
background-repeat: no-repeat;
background-position: right top;
background-size: 16px 16px;
}
.mediaFlyoutOptionImage {
display: inline-block;
width: 15%;
vertical-align: middle;
}
.mediaFlyoutOptionImage + .mediaFlyoutOptionContent {
vertical-align: top;
display: inline-block;
width: 85%;
}
.chaptersFlyout .mediaFlyoutOptionImage {
width: 40%;
}
.chaptersFlyout .mediaFlyoutOptionImage + .mediaFlyoutOptionContent {
width: 60%;
}
.mediaFlyoutOptionName {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
padding-left: 5px;
font-size: 13px;
font-weight: normal;
}
.mediaFlyoutOptionSecondaryText {
font-size: 13px;
color: #333;
font-weight: normal;
margin-top: 3px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
padding-left: 5px;
}
@media (min-width: 1440px) {
#nowPlayingBar .positionSliderContainer {
width: 300px;
}
}
.mediaPlayerAudioContainer {
position: fixed;
top: 40%;
text-align: center;
left: 0;
right: 0;
}
.mediaPlayerAudioContainerInner {
padding: 1em;
background: #222;
}

View file

@ -241,6 +241,7 @@
};
function getCodecLimits() {
return {
maxVideoAudioChannels: 6,
@ -608,7 +609,7 @@
url += '&maxheight=' + codecLimits.maxHeight;
url += '&videoCodec=h264';
url += '&audioCodec=aac';
url += '&audioCodec=aac,mp3';
url += '&audiosamplerate=' + codecLimits.maxSampleRate;
url += '&mediasourceid=' + mediaSourceInfo.mediaSource.Id;
@ -1004,7 +1005,7 @@
var castPlayer = new CastPlayer();
function chromecastPlayer(castPlayer) {
function chromecastPlayer() {
var self = this;
@ -1018,11 +1019,11 @@
self.playlistIndex = 0;
$.subscribe("/playback/complete", function (e) {
if (self.playlistIndex < (self.playlist.items || []).length) {
self.play(self.playlist);
}
});
//$.subscribe("/playback/complete", function (e) {
// if (self.playlistIndex < (self.playlist.items || []).length) {
// self.play(self.playlist);
// }
//});
////$(".positionSlider", "#footer").off("slidestart slidestop")
//// .on('slidestart', function (e) {
@ -1043,13 +1044,13 @@
query.ExcludeLocationTypes = "Virtual";
return ApiClient.getItems(userId, query);
};
}
function queueItems (items) {
for (var i = 0, length = items.length; i < length; i++) {
self.playlist.push(items[i]);
}
};
}
function queueItemsNext(items) {
var insertIndex = 1;
@ -1057,7 +1058,7 @@
self.playlist.splice(insertIndex, 0, items[i]);
insertIndex++;
}
};
}
function translateItemsForPlayback(items) {
var deferred = $.Deferred();
@ -1100,7 +1101,7 @@
}
return deferred.promise();
};
}
self.play = function (options) {
console.log("play", options);
@ -1371,7 +1372,7 @@
};
}
MediaController.registerPlayer(new chromecastPlayer(castPlayer));
MediaController.registerPlayer(new chromecastPlayer());
$(MediaController).on('playerchange', function () {

View file

@ -57,7 +57,7 @@
if (person.PrimaryImageTag) {
imgUrl = ApiClient.getPersonImageUrl(person.Name, {
height: 280,
width: 150,
tag: person.PrimaryImageTag,
type: "primary"
});

View file

@ -1238,7 +1238,7 @@
if (cast.PrimaryImageTag) {
imgUrl = ApiClient.getPersonImageUrl(cast.Name, {
width: 130,
width: 100,
tag: cast.PrimaryImageTag,
type: "primary"
});

View file

@ -31,7 +31,7 @@
if (user.PrimaryImageTag) {
var url = ApiClient.getUserImageUrl(user.Id, {
height: 40,
height: 24,
tag: user.PrimaryImageTag,
type: "Primary"
});

View file

@ -175,6 +175,11 @@
})[0];
};
self.getCurrentPlayer = function () {
return currentPlayer;
};
self.pause = function () {
currentPlayer.pause();
};
@ -188,7 +193,7 @@
};
self.seek = function (position) {
currentPlayer.changeStream(position);
currentPlayer.seek(position);
};
self.currentPlaylistIndex = function (i) {

View file

@ -23,6 +23,13 @@
var idleState = true;
var remoteFullscreen = false;
var muteButton = null;
var unmuteButton = null;
var volumeSlider = null;
var positionSlider;
var isPositionSliderActive;
var currentTimeElement;
self.initVideoPlayer = function () {
video = playVideo(item, mediaSource, startPosition, user);
return video;
@ -66,7 +73,7 @@
self.resetEnhancements = function () {
$("#mediaPlayer").hide();
$('#videoPlayer').removeClass('fullscreenVideo');
$("#videoControls").removeClass("inactive")
$("#videoControls").removeClass("inactive");
$("video").remove();
$("html").css("cursor", "default");
$(".ui-loader").hide();
@ -155,7 +162,38 @@
}
});
function onPositionSliderChange() {
isPositionSliderActive = false;
var newPercent = parseInt(this.value);
var newPositionTicks = (newPercent / 100) * currentMediaSource.RunTimeTicks;
self.changeStream(Math.floor(newPositionTicks));
}
$(function () {
var parent = $("#mediaPlayer");
muteButton = $('.muteButton', parent);
unmuteButton = $('.unmuteButton', parent);
currentTimeElement = $('.currentTime', parent);
positionSlider = $(".positionSlider", parent).on('slidestart', function (e) {
isPositionSliderActive = true;
}).on('slidestop', onPositionSliderChange);
volumeSlider = $('.volumeSlider', parent).on('slidestop', function () {
var vol = this.value;
updateVolumeButtons(vol);
self.setVolume(vol * 100);
});
$('#video-chaptersFlyout').on('click', '.mediaFlyoutOption', function () {
var ticks = parseInt(this.getAttribute('data-positionticks'));
@ -264,6 +302,17 @@
}, 4000);
}
function updateVolumeButtons(vol) {
if (vol) {
muteButton.show();
unmuteButton.hide();
} else {
muteButton.hide();
unmuteButton.show();
}
}
function requestFullScreen(element) {
// Supports most browsers and their versions.
@ -771,7 +820,10 @@
videoBitrate: mp4Quality.videoBitrate,
audioBitrate: mp4Quality.audioBitrate,
VideoCodec: mp4Quality.videoCodec,
AudioCodec: mp4Quality.audioCodec
AudioCodec: mp4Quality.audioCodec,
// None of the browsers seem to like this
EnableAutoStreamCopy: false
})) + seekParam;
@ -781,7 +833,8 @@
AudioCodec: 'Vorbis',
maxWidth: webmQuality.maxWidth,
videoBitrate: webmQuality.videoBitrate,
audioBitrate: webmQuality.audioBitrate
audioBitrate: webmQuality.audioBitrate,
EnableAutoStreamCopy: false
})) + seekParam;
@ -874,42 +927,26 @@
var video = $("video", videoElement);
initialVolume = localStorage.getItem("volume") || 0.5;
initialVolume = self.getSavedVolume();
video.each(function () {
this.volume = initialVolume;
});
self.volumeSlider.val(initialVolume).slider('refresh');
self.updateVolumeButtons(initialVolume);
volumeSlider.val(initialVolume).slider('refresh');
updateVolumeButtons(initialVolume);
video.on("volumechange", function (e) {
var muted = this.muted;
var vol = this.volume;
if (!muted && this.volume > 0) {
localStorage.setItem("volume", vol);
}
this.muted = this.volume == 0;
self.updateVolumeButtons(vol);
}).on("play.once", function () {
video.off("play.once");
updateVolumeButtons(vol);
}).on("playing.once", function () {
self.updateCanClientSeek(this);
video.off("playing.once");
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, mediaSource.Id, true, item.MediaType);
self.startProgressInterval(item.Id, mediaSource.Id);
self.onPlaybackStart(this, item, mediaSource);
}).on("pause", function (e) {
@ -939,9 +976,9 @@
}).on("timeupdate", function () {
if (!self.isPositionSliderActive) {
if (!isPositionSliderActive) {
self.setCurrentTime(self.getCurrentTicks(this), item, true);
self.setCurrentTime(self.getCurrentTicks(this), positionSlider, currentTimeElement);
}
}).on("error", function () {
@ -995,8 +1032,13 @@
$(".ui-loader").hide();
$("html").css("cursor", "default");
}).on("ended.playbackstopped", self.onPlaybackStopped)
.on('ended.playnext', self.playNextAfterEnded);
}).on("ended.playbackstopped", function () {
currentTimeElement.empty();
self.onPlaybackStopped.call(this);
}).on('ended.playnext', self.playNextAfterEnded);
// Stop playback on browser back button nav
$(window).on("popstate", function () {

View file

@ -13,12 +13,6 @@
var currentPlaylistIndex = 0;
self.currentDurationTicks = null;
self.currentTimeElement = null;
self.unmuteButton = null;
self.muteButton = null;
self.positionSlider = null;
self.isPositionSliderActive = null;
self.volumeSlider = null;
self.startTimeTicksOffset = null;
self.playlist = [];
@ -45,22 +39,11 @@
canClientSeek = duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY;
};
self.updateVolumeButtons = function (vol) {
if (vol) {
self.muteButton.show().prop("disabled", false);
self.unmuteButton.hide().prop("disabled", true);
} else {
self.muteButton.hide().prop("disabled", true);
self.unmuteButton.show().prop("disabled", false);
}
};
self.getCurrentTicks = function (mediaElement) {
return Math.floor(10000000 * (mediaElement || currentMediaElement).currentTime) + self.startTimeTicksOffset;
};
self.clearPauseStop = function() {
self.clearPauseStop = function () {
if (self.pauseStop) {
console.log('clearing pause stop timer');
@ -69,31 +52,6 @@
}
};
self.onPlaybackStopped = function () {
self.clearPauseStop();
$(this).off('ended.playbackstopped');
self.currentTimeElement.empty();
var endTime = this.currentTime;
clearProgressInterval();
var position = Math.floor(10000000 * endTime) + self.startTimeTicksOffset;
ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentItem.Id, currentMediaSource.Id, position);
if (currentItem.MediaType == "Video") {
ApiClient.stopActiveEncodings();
if (self.isFullScreen()) {
self.exitFullScreen();
}
self.resetEnhancements();
}
};
self.playNextAfterEnded = function () {
$(this).off('ended.playnext');
@ -210,7 +168,7 @@
}
};
self.setCurrentTime = function (ticks, item, updateSlider) {
self.setCurrentTime = function (ticks, positionSlider, currentTimeElement) {
// Convert to ticks
ticks = Math.floor(ticks);
@ -221,17 +179,28 @@
timeText += " / " + Dashboard.getDisplayTime(self.currentDurationTicks);
if (updateSlider) {
if (positionSlider) {
var percent = ticks / self.currentDurationTicks;
percent *= 100;
self.positionSlider.val(percent).slider('enable').slider('refresh');
positionSlider.val(percent).slider('enable').slider('refresh');
}
} else {
self.positionSlider.slider('disable').slider('refresh');
if (positionSlider) {
positionSlider.slider('disable').slider('refresh');
}
}
self.currentTimeElement.html(timeText);
if (currentTimeElement) {
currentTimeElement.html(timeText);
}
var state = self.getPlayerState(currentMediaElement, currentItem, currentMediaSource);
$(self).trigger('positionchange', [state]);
};
self.canPlayVideoDirect = function (mediaSource, videoStream, audioStream, subtitleStream, maxWidth, bitrate) {
@ -459,8 +428,6 @@
var mediaElement;
var mediaControls = $('#nowPlayingBar');
if (item.MediaType === "Video") {
currentItem = item;
@ -470,15 +437,12 @@
mediaElement = self.initVideoPlayer();
self.currentDurationTicks = currentMediaSource.RunTimeTicks;
mediaControls = $("#videoControls");
} else if (item.MediaType === "Audio") {
currentItem = item;
currentMediaSource = getOptimalMediaSource(item.MediaType, item.MediaSources);
mediaElement = playAudio(item, currentMediaSource, startPosition);
mediaControls.show();
self.currentDurationTicks = currentMediaSource.RunTimeTicks;
@ -488,36 +452,50 @@
currentMediaElement = mediaElement;
//display image and title
var imageTags = item.ImageTags || {};
var html = '';
if (item.MediaType === "Video") {
self.updateNowPlayingInfo(item);
}
};
self.updateNowPlayingInfo = function (item) {
if (!item) {
throw new Error('item cannot be null');
}
var mediaControls = $("#videoControls");
var state = self.getPlayerState(currentMediaElement, item, currentMediaSource);
var url = "";
if (imageTags.Primary) {
if (state.primaryImageTag) {
url = ApiClient.getImageUrl(item.Id, {
url = ApiClient.getImageUrl(state.primaryImageItemId, {
type: "Primary",
height: 80,
tag: item.ImageTags.Primary
tag: state.primaryImageTag
});
}
else if (item.BackdropImageTags && item.BackdropImageTags.length) {
else if (state.backdropImageTag) {
url = ApiClient.getImageUrl(item.Id, {
url = ApiClient.getImageUrl(state.backdropItemId, {
type: "Backdrop",
height: 80,
tag: item.BackdropImageTags[0]
tag: state.backdropImageTag,
index: 0
});
} else if (imageTags.Thumb) {
url = ApiClient.getImageUrl(item.Id, {
} else if (state.thumbImageTag) {
url = ApiClient.getImageUrl(state.thumbImageItemId, {
type: "Thumb",
height: 80,
tag: item.ImageTags.Thumb
tag: state.thumbImageTag
});
}
else if (item.Type == "TvChannel" || item.Type == "Recording") {
url = "css/images/items/detail/tv.png";
}
@ -528,40 +506,19 @@
url = "css/images/items/detail/video.png";
}
var name = item.Name;
var seriesName = '';
var name = state.itemName;
// Channel number
if (item.Number) {
name = item.Number + ' ' + name;
}
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;
}
if (item.CurrentProgram) {
seriesName = item.CurrentProgram.Name;
var nowPlayingTextElement = $('.nowPlayingText', mediaControls);
if (state.itemSubName) {
name += '<br/>' + state.itemSubName;
nowPlayingTextElement.addClass('nowPlayingDoubleText');
} else {
nowPlayingTextElement.removeClass('nowPlayingDoubleText');
}
var href = LibraryBrowser.getHref(item.CurrentProgram || item);
var nowPlayingText = (name ? name + "\n" : "") + (seriesName || "---");
if (item.SeriesName || item.Album || item.CurrentProgram) {
nowPlayingText = (seriesName ? seriesName : "") + "\n" + (name || "---");
}
// Fix for apostrophes and quotes
var htmlTitle = trimTitle(nowPlayingText).replace(/'/g, '&apos;').replace(/"/g, '&quot;');
html += "<div><a href='" + href + "'><img class='nowPlayingBarImage' alt='" + htmlTitle +
"' title='" + htmlTitle + "' src='" + url + "' style='height:40px;display:inline-block;' /></a></div>";
html += "<div class='nowPlayingText' title='" + htmlTitle + "'>" + titleHtml(nowPlayingText) + "</div>";
$('.nowPlayingMediaInfo', mediaControls).html(html);
$('.nowPlayingImage', mediaControls).html('<img src="' + url + '" />');
nowPlayingTextElement.html(name);
};
self.getItemsForPlayback = function (query) {
@ -727,51 +684,65 @@
self.mute = function () {
if (currentMediaElement) {
currentMediaElement.volume = 0;
currentMediaElement.muted = true;
self.volumeSlider.val(0).slider('refresh');
}
self.setVolume(0);
};
self.unmute = function () {
self.unMute = function () {
if (currentMediaElement) {
var volume = localStorage.getItem("volume") || self.volumeSlider.val();
currentMediaElement.volume = volume;
currentMediaElement.muted = false;
self.volumeSlider.val(volume).slider('refresh');
}
self.setVolume(self.getSavedVolume() * 100);
};
self.toggleMute = function () {
if (currentMediaElement) {
var volume = localStorage.getItem("volume") || self.volumeSlider.val();
currentMediaElement.volume = currentMediaElement.volume ? 0 : volume;
currentMediaElement.muted = currentMediaElement.volume == 0;
self.volumeSlider.val(volume).slider('refresh');
if (currentMediaElement.volume) {
self.mute();
} else {
self.unMute();
}
}
};
self.volumeDown = function () {
if (currentMediaElement) {
currentMediaElement.volume = Math.max(currentMediaElement.volume - .02, 0);
localStorage.setItem("volume", currentMediaElement.volume);
self.volumeSlider.val(currentMediaElement.volume).slider('refresh');
self.setVolume(Math.max(currentMediaElement.volume - .02, 0) * 100);
}
};
self.volumeUp = function () {
if (currentMediaElement) {
currentMediaElement.volume = Math.min(currentMediaElement.volume + .02, 1);
localStorage.setItem("volume", currentMediaElement.volume);
self.volumeSlider.val(currentMediaElement.volume).slider('refresh');
self.setVolume(Math.min(currentMediaElement.volume + .02, 1) * 100);
}
};
// Sets volume using a 0-100 scale
self.setVolume = function (val) {
if (currentMediaElement) {
currentMediaElement.volume = val / 100;
self.onVolumeChanged(currentMediaElement);
self.saveVolume();
}
};
self.saveVolume = function (val) {
if (val) {
localStorage.setItem("volume", val);
}
};
self.getSavedVolume = function () {
return localStorage.getItem("volume") || 0.5;
};
self.shuffle = function (id) {
var userId = Dashboard.getCurrentUserId();
@ -882,7 +853,12 @@
$(elem).off("ended.playnext").on("ended", function () {
$(this).off();
if (this.tagName.toLowerCase() != 'audio') {
$(this).remove();
}
elem.src = "";
currentMediaElement = null;
@ -893,8 +869,6 @@
self.exitFullScreen();
}
self.resetEnhancements();
} else {
$('#nowPlayingBar').hide();
}
};
@ -902,55 +876,150 @@
return currentMediaElement;
};
self.bindPositionSlider = function () {
self.positionSlider.on('slidestart', function (e) {
self.getPlayerState = function (playerElement, item, mediaSource) {
self.isPositionSliderActive = true;
var itemName = '';
var itemSubName = '';
}).on('slidestop', onPositionSliderChange);
if (item) {
var name = item.Name;
var seriesName = '';
// Channel number
if (item.Number) {
name = item.Number + ' ' + name;
}
if (item.IndexNumber != null) {
name = item.IndexNumber + " - " + name;
}
if (item.ParentIndexNumber != null) {
name = item.ParentIndexNumber + "." + name;
}
if (item.CurrentProgram) {
seriesName = item.CurrentProgram.Name;
}
else if (item.SeriesName || item.Album || item.ProductionYear) {
seriesName = item.SeriesName || item.Album || item.ProductionYear;
}
if (seriesName) {
itemName = item.SeriesName || item.Album || item.CurrentProgram;
itemSubName = name;
} else {
itemName = name;
}
}
var state = {
itemId: item.Id,
mediaSourceId: mediaSource.Id,
volumeLevel: playerElement.volume * 100,
isMuted: playerElement.volume == 0,
isPaused: playerElement.paused,
runtimeTicks: mediaSource.RunTimeTicks,
positionTicks: self.getCurrentTicks(playerElement),
canSeek: mediaSource.RunTimeTicks && mediaSource.RunTimeTicks > 0,
mediaType: item.MediaType,
itemName: itemName,
itemSubName: itemSubName,
itemType: item.Type
};
self.bindVolumeSlider = function () {
self.volumeSlider.on('slidestop', function () {
var imageTags = item.ImageTags || {};
var vol = this.value;
if (imageTags.Primary) {
self.updateVolumeButtons(vol);
currentMediaElement.volume = vol;
});
state.primaryImageItemId = item.Id;
state.primaryImageTag = imageTags.Primary;
}
if (item.BackdropImageTags && item.BackdropImageTags.length) {
state.backdropItemId = item.Id;
state.backdropImageTag = item.BackdropImageTags[0];
}
if (imageTags.Thumb) {
state.thumbItemId = item.Id;
state.thumbImageTag = imageTags.Thumb;
}
return state;
};
self.onPlaybackStart = function (playerElement, item, mediaSource) {
self.updateCanClientSeek(playerElement);
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, mediaSource.Id, true, item.MediaType);
self.startProgressInterval(item.Id, mediaSource.Id);
var state = self.getPlayerState(playerElement, item, mediaSource);
$(self).trigger('playbackstart', [state]);
};
self.onVolumeChanged = function (playerElement) {
self.saveVolume(playerElement.volume);
var state = self.getPlayerState(playerElement, currentItem, currentMediaSource);
$(self).trigger('volumechange', [state]);
};
self.onPlaybackStopped = function () {
self.clearPauseStop();
var playerElement = this;
$(playerElement).off('ended.playbackstopped');
var endTime = playerElement.currentTime;
clearProgressInterval();
var position = Math.floor(10000000 * endTime) + self.startTimeTicksOffset;
var item = currentItem;
var mediaSource = currentMediaSource;
ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), item.Id, mediaSource.Id, position);
if (item.MediaType == "Video") {
ApiClient.stopActiveEncodings();
if (self.isFullScreen()) {
self.exitFullScreen();
}
self.resetEnhancements();
}
var state = self.getPlayerState(playerElement, item, mediaSource);
$(self).trigger('playbackstop', [state]);
};
self.onPlaystateChange = function (playerElement) {
var state = self.getPlayerState(playerElement, currentItem, currentMediaSource);
$(self).trigger('playstatechange', [state]);
};
$(window).on("beforeunload popstate", function () {
var item = currentItem;
var media = currentMediaElement;
// Try to report playback stopped before the browser closes
if (item && media && currentProgressInterval) {
if (currentItem && currentMediaElement && currentProgressInterval) {
var endTime = currentMediaElement.currentTime;
var position = Math.floor(10000000 * endTime) + self.startTimeTicksOffset;
ApiClient.reportPlaybackStopped(Dashboard.getCurrentUserId(), currentItem.Id, currentMediaSource.Id, position);
self.onPlaybackStopped.call(currentMediaElement);
}
});
$(function () {
initPlayer();
});
function initPlayer() {
self.muteButton = $('.muteButton');
self.unmuteButton = $('.unmuteButton');
self.currentTimeElement = $('.currentTime');
self.volumeSlider = $('.volumeSlider');
self.positionSlider = $(".positionSlider");
self.bindVolumeSlider();
self.bindPositionSlider();
}
function replaceQueryString(url, param, value) {
var re = new RegExp("([?|&])" + param + "=.*?(&|$)", "i");
if (url.match(re))
@ -977,15 +1046,30 @@
return testableVideoElement.canPlayType('video/webm').replace(/no/, '');
}
function onPositionSliderChange() {
function getAudioElement() {
self.isPositionSliderActive = false;
var elem = $('.mediaPlayerAudio');
var newPercent = parseInt(this.value);
if (elem.length) {
return elem;
}
var newPositionTicks = (newPercent / 100) * currentMediaSource.RunTimeTicks;
var html = '';
self.changeStream(Math.floor(newPositionTicks));
var requiresControls = $.browser.android || ($.browser.webkit && !$.browser.chrome);
if (requiresControls) {
html += '<div class="mediaPlayerAudioContainer"><div class="mediaPlayerAudioContainerInner">';;
} else {
html += '<div class="mediaPlayerAudioContainer" style="display:none;"><div class="mediaPlayerAudioContainerInner">';;
}
html += '<audio class="mediaPlayerAudio" preload="auto" controls>';
html += '</audio></div></div>';
$(document.body).append(html);
return $('.mediaPlayerAudio');
}
function playAudio(item, mediaSource, startPositionTicks) {
@ -1003,18 +1087,10 @@
audioCodec: 'mp3'
}));
var aacUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.aac', $.extend({}, baseParams, {
audioCodec: 'aac'
}));
var webmUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.webm', $.extend({}, baseParams, {
audioCodec: 'Vorbis'
}));
var mediaStreams = mediaSource.MediaStreams;
var isStatic = false;
var seekParam = isStatic && startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
var seekParam = startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
for (var i = 0, length = mediaStreams.length; i < length; i++) {
@ -1024,11 +1100,7 @@
var container = (mediaSource.Container || '').toLowerCase();
// Stream statically when possible
if (container == 'aac' && stream.BitRate <= 256000) {
aacUrl += "&static=true" + seekParam;
isStatic = true;
}
else if (container == 'mp3' && stream.BitRate <= 256000) {
if (container == 'mp3' && stream.BitRate <= 256000) {
mp3Url += "&static=true" + seekParam;
isStatic = true;
}
@ -1038,91 +1110,39 @@
self.startTimeTicksOffset = isStatic ? 0 : startPositionTicks;
var html = '';
var initialVolume = self.getSavedVolume();
var requiresControls = $.browser.android || ($.browser.webkit && !$.browser.chrome);
return getAudioElement().each(function () {
// Can't autoplay in these browsers so we need to use the full controls
if (requiresControls) {
html += '<audio preload="auto" autoplay controls>';
} else {
html += '<audio preload="auto" style="display:none;" autoplay>';
}
html += '<source type="audio/mpeg" src="' + mp3Url + '" />';
html += '<source type="audio/aac" src="' + aacUrl + '" />';
html += '<source type="audio/webm" src="' + webmUrl + '" />';
html += '</audio>';
var nowPlayingBar = $('#nowPlayingBar').show();
//show stop button
$('#stopButton', nowPlayingBar).show();
$('#playButton', nowPlayingBar).hide();
$('#pauseButton', nowPlayingBar).show();
$('#fullscreenButton', nowPlayingBar).hide();
$('#previousTrackButton', nowPlayingBar).show();
$('#nextTrackButton', nowPlayingBar).show();
$('#playlistButton', nowPlayingBar).show();
$('#qualityButton', nowPlayingBar).hide();
$('#audioTracksButton', nowPlayingBar).hide();
$('#subtitleButton', nowPlayingBar).hide();
$('#chaptersButton', nowPlayingBar).hide();
var mediaElement = $('#mediaElement', nowPlayingBar).html(html);
var audioElement = $("audio", mediaElement);
var initialVolume = localStorage.getItem("volume") || 0.5;
audioElement.each(function () {
this.src = mp3Url;
this.volume = initialVolume;
});
this.play();
self.volumeSlider.val(initialVolume).slider('refresh');
self.updateVolumeButtons(initialVolume);
}).on("volumechange", function () {
audioElement.on("volumechange", function () {
self.onVolumeChanged(this);
var vol = this.volume;
}).on("playing.once", function () {
localStorage.setItem("volume", vol);
$('.mediaPlayerAudioContainer').hide();
self.updateVolumeButtons(vol);
$(this).off("playing.once");
}).on("play.once", function () {
if (!requiresControls) {
audioElement.hide();
}
self.updateCanClientSeek(this);
audioElement.off("play.once");
ApiClient.reportPlaybackStart(Dashboard.getCurrentUserId(), item.Id, mediaSource.Id, true, item.MediaType);
self.startProgressInterval(item.Id, mediaSource.Id);
self.onPlaybackStart(this, item, mediaSource);
}).on("pause", function () {
$('#playButton', nowPlayingBar).show();
$('#pauseButton', nowPlayingBar).hide();
self.onPlaystateChange(this);
}).on("playing", function () {
$('#playButton', nowPlayingBar).hide();
$('#pauseButton', nowPlayingBar).show();
self.onPlaystateChange(this);
}).on("timeupdate", function () {
if (!self.isPositionSliderActive) {
self.setCurrentTime(self.getCurrentTicks(this));
self.setCurrentTime(self.getCurrentTicks(this), item, true);
}
}).on("ended.playbackstopped", self.onPlaybackStopped).on('ended.playnext', self.playNextAfterEnded);
return audioElement[0];
}).on("ended.playbackstopped", self.onPlaybackStopped).on('ended.playnext', self.playNextAfterEnded)[0];
};
function canPlayAudioStreamDirect(audioStream) {
@ -1146,27 +1166,7 @@
}
return true;
};
function trunc(string, len) {
if (string.length > 0 && string.length < len) return string;
var trimmed = $.trim(string).substring(0, len).split(" ").slice(0, -1).join(" ");
if (trimmed) {
trimmed += "...";
} else {
trimmed = "---"
}
return trimmed;
};
function trimTitle(title) {
return title.replace("\n---", "");
};
function titleHtml(title) {
var titles = title.split("\n");
return (trunc(titles[0], 30) + "<br />" + trunc(titles[1], 30)).replace("---", "&nbsp;");
};
var getItemFields = "MediaSources,Chapters";

View file

@ -0,0 +1,359 @@
(function (window, document, $, setTimeout, clearTimeout) {
var currentPlayer;
var currentTimeElement;
var nowPlayingImageElement;
var nowPlayingTextElement;
var unmuteButton;
var muteButton;
var volumeSlider;
var isVolumeSliderActive;
var unpauseButton;
var pauseButton;
var positionSlider;
var isPositionSliderActive;
var lastPlayerState;
function getNowPlayingBarHtml() {
var html = '';
html += '<div id="nowPlayingBar" class="nowPlayingBar" style="display:none;">';
html += '<div style="display:inline-block;width:12px;"></div>';
html += '<a id="playlistButton" class="mediaButton playlistButton" href="playlist.html" data-role="button" data-icon="bullets" data-iconpos="notext" data-inline="true" title="Playlist">Playlist</a>';
html += '<button id="previousTrackButton" class="mediaButton previousTrackButton" title="Previous Track" type="button" data-icon="previous-track" data-iconpos="notext" data-inline="true">Previous Track</button>';
html += '<button id="playButton" class="mediaButton unpauseButton" title="Play" type="button" data-icon="play" data-iconpos="notext" data-inline="true">Play</button>';
html += '<button id="pauseButton" class="mediaButton pauseButton" title="Pause" type="button" data-icon="pause" data-iconpos="notext" data-inline="true">Pause</button>';
html += '<button id="stopButton" class="mediaButton stopButton" title="Stop" type="button" data-icon="stop" data-iconpos="notext" data-inline="true">Stop</button>';
html += '<button id="nextTrackButton" class="mediaButton nextTrackButton" title="Next Track" type="button" data-icon="next-track" data-iconpos="notext" data-inline="true">Next Track</button>';
html += '<div id="mediaElement"></div>';
html += '<div class="positionSliderContainer sliderContainer">';
html += '<input type="range" class="mediaSlider positionSlider slider" step=".001" min="0" max="100" value="0" style="display:none;" data-mini="true" data-theme="a" data-highlight="true" />';
html += '</div>';
html += '<div class="currentTime"></div>';
html += '<div class="nowPlayingImage"></div>';
html += '<div class="nowPlayingText"></div>';
html += '<button id="muteButton" class="mediaButton muteButton" title="Mute" type="button" data-icon="audio" data-iconpos="notext" data-inline="true">Mute</button>';
html += '<button id="unmuteButton" class="mediaButton unmuteButton" title="Unmute" type="button" data-icon="volume-off" data-iconpos="notext" data-inline="true">Unmute</button>';
html += '<div class="volumeSliderContainer sliderContainer">';
html += '<input type="range" class="mediaSlider volumeSlider slider" step=".05" min="0" max="100" value="0" style="display:none;" data-mini="true" data-theme="a" data-highlight="true" />';
html += '</div>';
html += '</div>';
return html;
}
function bindEvents(elem) {
currentTimeElement = $('.currentTime', elem);
nowPlayingImageElement = $('.nowPlayingImage', elem);
nowPlayingTextElement = $('.nowPlayingText', elem);
unmuteButton = $('.unmuteButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.unMute();
}
});
muteButton = $('.muteButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.mute();
}
});
$('.stopButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.stop();
}
});
pauseButton = $('.pauseButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.pause();
}
});
unpauseButton = $('.unpauseButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.unpause();
}
});
$('.nextTrackButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.nextTrack();
}
});
$('.previousTrackButton', elem).on('click', function () {
if (currentPlayer) {
currentPlayer.previousTrack();
}
});
volumeSlider = $('.volumeSlider', elem).on('slidestart', function () {
isVolumeSliderActive = true;
}).on('slidestop', function () {
isVolumeSliderActive = false;
if (currentPlayer) {
currentPlayer.setVolume(this.value);
}
});
positionSlider = $('.positionSlider', elem).on('slidestart', function () {
isPositionSliderActive = true;
}).on('slidestop', function () {
isPositionSliderActive = false;
if (currentPlayer && lastPlayerState) {
var ticks = lastPlayerState.runtimeTicks * this.value / 100;
currentPlayer.seek(ticks);
}
});
}
function getNowPlayingBar() {
var elem = $('.nowPlayingBar');
if (elem.length) {
return elem;
}
elem = $(getNowPlayingBarHtml()).insertBefore('#footerNotifications').trigger('create');
bindEvents(elem);
return elem;
}
function updatePlayerState(state) {
lastPlayerState = state;
if (!muteButton) {
getNowPlayingBar();
}
if (state.isMuted) {
muteButton.hide();
unmuteButton.show();
} else {
muteButton.show();
unmuteButton.hide();
}
if (state.isPaused) {
pauseButton.hide();
unpauseButton.show();
} else {
pauseButton.show();
unpauseButton.hide();
}
if (!isVolumeSliderActive) {
volumeSlider.val(state.volumeLevel || 0).slider('refresh');
}
if (!isPositionSliderActive) {
if (state.canSeek) {
var pct = state.positionTicks / state.runtimeTicks;
pct *= 100;
positionSlider.val(pct).prop('disabled', '');
} else {
positionSlider.val(0).prop('disabled', 'disabled');
}
positionSlider.slider('refresh');
}
var timeText = Dashboard.getDisplayTime(state.positionTicks);
if (state.runtimeTicks) {
timeText += " / " + Dashboard.getDisplayTime(state.runtimeTicks);
}
currentTimeElement.html(timeText);
}
function updateNowPlayingInfo(state) {
var name = state.itemName;
if (state.itemSubName) {
name += '<br/>' + state.itemSubName;
nowPlayingTextElement.addClass('nowPlayingDoubleText');
} else {
nowPlayingTextElement.removeClass('nowPlayingDoubleText');
}
nowPlayingTextElement.html(name);
var url;
if (state.primaryImageTag) {
url = ApiClient.getImageUrl(state.primaryImageItemId, {
type: "Primary",
height: 80,
tag: state.primaryImageTag
});
}
else if (state.backdropImageTag) {
url = ApiClient.getImageUrl(state.backdropItemId, {
type: "Backdrop",
height: 80,
tag: state.backdropImageTag,
index: 0
});
} else if (state.thumbImageTag) {
url = ApiClient.getImageUrl(state.thumbImageItemId, {
type: "Thumb",
height: 80,
tag: state.thumbImageTag
});
}
else if (item.Type == "TvChannel" || item.Type == "Recording") {
url = "css/images/items/detail/tv.png";
}
else if (item.MediaType == "Audio") {
url = "css/images/items/detail/audio.png";
}
else {
url = "css/images/items/detail/video.png";
}
nowPlayingImageElement.html('<img src="' + url + '" />');
}
function onPlaybackStart(e, state) {
var player = this;
if (player.isDefaultPlayer && state.mediaType == 'Video') {
return;
}
showNowPlayingBar();
updatePlayerState(state);
updateNowPlayingInfo(state);
}
var nowPlayingBarTimeout;
function showNowPlayingBar() {
if (nowPlayingBarTimeout) {
clearTimeout(nowPlayingBarTimeout);
nowPlayingBarTimeout = null;
}
var nowPlayingBar = getNowPlayingBar();
nowPlayingBar.show();
}
function hideNowPlayingBar() {
if (nowPlayingBarTimeout) {
clearTimeout(nowPlayingBarTimeout);
nowPlayingBarTimeout = null;
}
// Use a timeout to prevent the bar from hiding and showing quickly
// in the event of a stop->play command
nowPlayingBarTimeout = setTimeout(function () {
getNowPlayingBar().hide();
}, 200);
}
function onPlaybackStopped(e, state) {
hideNowPlayingBar();
}
function onVolumeChanged(e, state) {
var player = this;
if (player.isDefaultPlayer && state.mediaType == 'Video') {
return;
}
updatePlayerState(state);
}
function releaseCurrentPlayer() {
if (currentPlayer) {
$(currentPlayer).off('.nowplayingbar');
currentPlayer = null;
}
}
function bindToPlayer(player) {
releaseCurrentPlayer();
currentPlayer = player;
$(player).on('playbackstart.nowplayingbar', onPlaybackStart)
.on('playbackstop.nowplayingbar', onPlaybackStopped)
.on('volumechange.nowplayingbar', onVolumeChanged)
.on('playstatechange.nowplayingbar', onVolumeChanged)
.on('positionchange.nowplayingbar', onVolumeChanged);
}
$(function () {
$(MediaController).on('playerchange', function () {
bindToPlayer(MediaController.getCurrentPlayer());
});
bindToPlayer(MediaController.getCurrentPlayer());
});
})(window, document, jQuery, setTimeout, clearTimeout);

View file

@ -482,7 +482,7 @@ var Dashboard = {
if (user.PrimaryImageTag) {
var imageUrl = ApiClient.getUserImageUrl(user.Id, {
width: 60,
width: 28,
tag: user.PrimaryImageTag,
type: "Primary"
@ -589,7 +589,7 @@ var Dashboard = {
if (user.PrimaryImageTag) {
var url = ApiClient.getUserImageUrl(user.Id, {
width: 225,
width: 28,
tag: user.PrimaryImageTag,
type: "Primary"
});
@ -1286,7 +1286,8 @@ $(function () {
videoPlayerHtml += '<button id="video-nextTrackButton" class="mediaButton nextTrackButton" title="Next Track" type="button" onclick="MediaPlayer.nextTrack();" data-icon="next-track" data-iconpos="notext" data-inline="true">Next Track</button>';
videoPlayerHtml += '<div class="currentTime"></div>';
videoPlayerHtml += '<div class="nowPlayingMediaInfo"></div>';
videoPlayerHtml += '<div class="nowPlayingImage"></div>';
videoPlayerHtml += '<div class="nowPlayingText"></div>';
videoPlayerHtml += '<button id="video-muteButton" class="mediaButton muteButton" title="Mute" type="button" onclick="MediaPlayer.mute();" data-icon="audio" data-iconpos="notext" data-inline="true">Mute</button>';
videoPlayerHtml += '<button id="video-unmuteButton" class="mediaButton unmuteButton" title="Unmute" type="button" onclick="MediaPlayer.unMute();" data-icon="volume-off" data-iconpos="notext" data-inline="true">Unmute</button>';
@ -1326,48 +1327,6 @@ $(function () {
mediaPlayerElem.trigger('create');
var footerHtml = '<div id="footer" data-theme="b" class="ui-bar-b">';
footerHtml += '<div id="nowPlayingBar" class="nowPlayingBar" style="display:none;">';
footerHtml += '<div class="barBackground ui-bar-b"></div>';
footerHtml += '<div style="display:inline-block;width:12px;"></div>';
footerHtml += '<a id="playlistButton" class="mediaButton playlistButton" href="playlist.html" data-role="button" data-icon="bullets" data-iconpos="notext" data-inline="true" title="Playlist">Playlist</a>';
footerHtml += '<button id="previousTrackButton" class="mediaButton previousTrackButton" title="Previous Track" type="button" onclick="MediaPlayer.previousTrack();" data-icon="previous-track" data-iconpos="notext" data-inline="true">Previous Track</button>';
footerHtml += '<button id="playButton" class="mediaButton" title="Play" type="button" onclick="MediaPlayer.unpause();" data-icon="play" data-iconpos="notext" data-inline="true">Play</button>';
footerHtml += '<button id="pauseButton" class="mediaButton" title="Pause" type="button" onclick="MediaPlayer.pause();" data-icon="pause" data-iconpos="notext" data-inline="true">Pause</button>';
footerHtml += '<div id="mediaElement"></div>';
footerHtml += '<button id="stopButton" class="mediaButton" title="Stop" type="button" onclick="MediaPlayer.stop();" data-icon="stop" data-iconpos="notext" data-inline="true">Stop</button>';
footerHtml += '<button id="nextTrackButton" class="mediaButton nextTrackButton" title="Next Track" type="button" onclick="MediaPlayer.nextTrack();" data-icon="next-track" data-iconpos="notext" data-inline="true">Next Track</button>';
footerHtml += '<div class="positionSliderContainer sliderContainer">';
footerHtml += '<input type="range" class="mediaSlider positionSlider slider" step=".001" min="0" max="100" value="0" style="display:none;" data-mini="true" data-theme="a" data-highlight="true" />';
footerHtml += '</div>';
footerHtml += '<div class="currentTime"></div>';
footerHtml += '<div class="nowPlayingMediaInfo"></div>';
footerHtml += '<button id="muteButton" class="mediaButton muteButton" title="Mute" type="button" onclick="MediaPlayer.mute();" data-icon="audio" data-iconpos="notext" data-inline="true">Mute</button>';
footerHtml += '<button id="unmuteButton" class="mediaButton unmuteButton" title="Unmute" type="button" onclick="MediaPlayer.unMute();" data-icon="volume-off" data-iconpos="notext" data-inline="true">Unmute</button>';
footerHtml += '<div class="volumeSliderContainer sliderContainer">';
footerHtml += '<input type="range" class="mediaSlider volumeSlider slider" step=".05" min="0" max="1" value="0" style="display:none;" data-mini="true" data-theme="a" data-highlight="true" />';
footerHtml += '</div>';
footerHtml += '<button onclick="MediaPlayer.showQualityFlyout();" id="qualityButton" class="mediaButton qualityButton" title="Quality" type="button" data-icon="gear" data-iconpos="notext" data-inline="true">Quality</button>';
footerHtml += '<div class="mediaFlyoutContainer"><div id="qualityFlyout" style="display:none;" class="mediaPlayerFlyout"></div></div>';
footerHtml += '<button onclick="MediaPlayer.showAudioTracksFlyout();" id="audioTracksButton" class="imageButton mediaButton audioTracksButton" title="Audio tracks" type="button" data-icon="audiocd" data-iconpos="notext" data-inline="true">Audio Tracks</button>';
footerHtml += '<div class="mediaFlyoutContainer"><div id="audioTracksFlyout" style="display:none;" class="mediaPlayerFlyout audioTracksFlyout"></div></div>';
footerHtml += '<button onclick="MediaPlayer.showSubtitleMenu();" id="subtitleButton" class="imageButton mediaButton subtitleButton" title="Subtitles" type="button" data-icon="subtitles" data-iconpos="notext" data-inline="true">Subtitles</button>';
footerHtml += '<div class="mediaFlyoutContainer"><div id="subtitleFlyout" style="display:none;" class="mediaPlayerFlyout subtitleFlyout"></div></div>';
footerHtml += '<button onclick="MediaPlayer.showChaptersFlyout();" id="chaptersButton" class="mediaButton chaptersButton" title="Scenes" type="button" data-icon="video" data-iconpos="notext" data-inline="true">Scenes</button>';
footerHtml += '<div class="mediaFlyoutContainer"><div id="chaptersFlyout" style="display:none;" class="mediaPlayerFlyout chaptersFlyout"></div></div>';
footerHtml += '<button onclick="MediaPlayer.toggleFullscreen();" id="fullscreenButton" class="mediaButton fullscreenButton" title="Fullscreen" type="button" data-icon="action" data-iconpos="notext" data-inline="true">Fullscreen</button>';
footerHtml += '</div>';
footerHtml += '<div id="footerNotifications"></div>';
footerHtml += '</div>';

View file

@ -17,7 +17,7 @@
if (user.PrimaryImageTag) {
var imageUrl = ApiClient.getUserImageUrl(user.Id, {
height: 450,
height: 200,
tag: user.PrimaryImageTag,
type: "Primary"
});

View file

@ -27,7 +27,7 @@
if (user.PrimaryImageTag) {
var url = ApiClient.getUserImageUrl(user.Id, {
width: 225,
width: 80,
tag: user.PrimaryImageTag,
type: "Primary"
});

View file

@ -1,4 +0,0 @@
/* jQuery Tiny Pub/Sub - v0.7 - 10/27/2011
* http://benalman.com/
* Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */
(function (a) { var b = a({}); a.subscribe = function () { b.on.apply(b, arguments) }, a.unsubscribe = function () { b.off.apply(b, arguments) }, a.publish = function () { b.trigger.apply(b, arguments) } })(jQuery)

View file

@ -2198,6 +2198,28 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
});
};
function normalizeImageOptions(options) {
var ratio = window.devicePixelRatio;
if (ratio) {
if (options.width) {
options.width = options.width * ratio;
}
if (options.height) {
options.height = options.height * ratio;
}
if (options.maxWidth) {
options.maxWidth = options.maxWidth * ratio;
}
if (options.maxHeight) {
options.maxHeight = options.maxHeight * ratio;
}
}
}
/**
* Constructs a url for a user image
* @param {String} userId
@ -2230,6 +2252,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
delete options.type;
delete options.index;
normalizeImageOptions(options);
return self.getUrl(url, options);
};
@ -2265,215 +2289,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
delete options.type;
delete options.index;
return self.getUrl(url, options);
};
/**
* Constructs a url for a year image
* @param {String} year
* @param {Object} options
* Options supports the following properties:
* width - download the image at a fixed width
* height - download the image at a fixed height
* maxWidth - download the image at a maxWidth
* maxHeight - download the image at a maxHeight
* quality - A scale of 0-100. This should almost always be omitted as the default will suffice.
* For best results do not specify both width and height together, as aspect ratio might be altered.
*/
self.getYearImageUrl = function (year, options) {
if (!year) {
throw new Error("null year");
}
options = options || {
};
var url = "Years/" + year + "/Images/" + options.type;
if (options.index != null) {
url += "/" + options.index;
}
// Don't put these on the query string
delete options.type;
delete options.index;
return self.getUrl(url, options);
};
/**
* Constructs a url for a genre image
* @param {String} name
* @param {Object} options
* Options supports the following properties:
* width - download the image at a fixed width
* height - download the image at a fixed height
* maxWidth - download the image at a maxWidth
* maxHeight - download the image at a maxHeight
* quality - A scale of 0-100. This should almost always be omitted as the default will suffice.
* For best results do not specify both width and height together, as aspect ratio might be altered.
*/
self.getGenreImageUrl = function (name, options) {
if (!name) {
throw new Error("null name");
}
options = options || {
};
var url = "Genres/" + self.encodeName(name) + "/Images/" + options.type;
if (options.index != null) {
url += "/" + options.index;
}
// Don't put these on the query string
delete options.type;
delete options.index;
return self.getUrl(url, options);
};
/**
* Constructs a url for a genre image
* @param {String} name
* @param {Object} options
* Options supports the following properties:
* width - download the image at a fixed width
* height - download the image at a fixed height
* maxWidth - download the image at a maxWidth
* maxHeight - download the image at a maxHeight
* quality - A scale of 0-100. This should almost always be omitted as the default will suffice.
* For best results do not specify both width and height together, as aspect ratio might be altered.
*/
self.getMusicGenreImageUrl = function (name, options) {
if (!name) {
throw new Error("null name");
}
options = options || {
};
var url = "MusicGenres/" + self.encodeName(name) + "/Images/" + options.type;
if (options.index != null) {
url += "/" + options.index;
}
// Don't put these on the query string
delete options.type;
delete options.index;
return self.getUrl(url, options);
};
/**
* Constructs a url for a genre image
* @param {String} name
* @param {Object} options
* Options supports the following properties:
* width - download the image at a fixed width
* height - download the image at a fixed height
* maxWidth - download the image at a maxWidth
* maxHeight - download the image at a maxHeight
* quality - A scale of 0-100. This should almost always be omitted as the default will suffice.
* For best results do not specify both width and height together, as aspect ratio might be altered.
*/
self.getGameGenreImageUrl = function (name, options) {
if (!name) {
throw new Error("null name");
}
options = options || {
};
var url = "GameGenres/" + self.encodeName(name) + "/Images/" + options.type;
if (options.index != null) {
url += "/" + options.index;
}
// Don't put these on the query string
delete options.type;
delete options.index;
return self.getUrl(url, options);
};
/**
* Constructs a url for a artist image
* @param {String} name
* @param {Object} options
* Options supports the following properties:
* width - download the image at a fixed width
* height - download the image at a fixed height
* maxWidth - download the image at a maxWidth
* maxHeight - download the image at a maxHeight
* quality - A scale of 0-100. This should almost always be omitted as the default will suffice.
* For best results do not specify both width and height together, as aspect ratio might be altered.
*/
self.getArtistImageUrl = function (name, options) {
if (!name) {
throw new Error("null name");
}
options = options || {
};
var url = "Artists/" + self.encodeName(name) + "/Images/" + options.type;
if (options.index != null) {
url += "/" + options.index;
}
// Don't put these on the query string
delete options.type;
delete options.index;
return self.getUrl(url, options);
};
/**
* Constructs a url for a studio image
* @param {String} name
* @param {Object} options
* Options supports the following properties:
* width - download the image at a fixed width
* height - download the image at a fixed height
* maxWidth - download the image at a maxWidth
* maxHeight - download the image at a maxHeight
* quality - A scale of 0-100. This should almost always be omitted as the default will suffice.
* For best results do not specify both width and height together, as aspect ratio might be altered.
*/
self.getStudioImageUrl = function (name, options) {
if (!name) {
throw new Error("null name");
}
options = options || {
};
var url = "Studios/" + self.encodeName(name) + "/Images/" + options.type;
if (options.index != null) {
url += "/" + options.index;
}
// Don't put these on the query string
delete options.type;
delete options.index;
normalizeImageOptions(options);
return self.getUrl(url, options);
};