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

add new mirror mode

This commit is contained in:
Luke Pulverenti 2014-04-13 13:27:13 -04:00
parent 2835534c6d
commit 9f83edf9ec
18 changed files with 316 additions and 191 deletions

View file

@ -106,7 +106,7 @@
#mediaPlayer .ui-slider-track, .nowPlayingBar .ui-slider-track {
border-color: #2ad !important;
height: 8px!important;
height: 2px!important;
}
#mediaPlayer .ui-slider-handle, .nowPlayingBar .ui-slider-handle {

View file

@ -1,6 +1,6 @@
/* Now playing bar */
.nowPlayingBar {
padding: 10px 0 14px 0;
padding: 6px 0 14px 0;
border-top: 2px solid green;
}
@ -30,7 +30,7 @@
}
.nowPlayingDoubleText {
top: 0;
top: -3px;
}
.nowPlayingImage {
@ -57,7 +57,7 @@
}
.nowPlayingBar .currentTime {
top: -12px;
top: -15px;
}
.mediaSlider {
@ -75,8 +75,8 @@
width: 130px;
}
#nowPlayingBar .positionSliderContainer {
margin-top: 10px;
#nowPlayingBar .sliderContainer {
margin-top: 14px;
}
.volumeSliderContainer {

View file

@ -1267,6 +1267,10 @@
castPlayer.stopMedia();
};
self.displayContent = function (options) {
};
self.mute = function () {
castPlayer.mute();
};
@ -1280,12 +1284,19 @@
};
self.getTargets = function () {
var targets = [];
if (castPlayer.hasReceivers) {
targets.push(self.getCurrentTargetInfo());
}
return targets;
};
self.getCurrentTargetInfo = function () {
var appName = null;
if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.friendlyName) {
appName = castPlayer.session.friendlyName;
@ -1297,7 +1308,8 @@
playerName: self.name,
playableMediaTypes: ["Audio", "Video"],
isLocalPlayer: false,
appName: appName
appName: appName,
supportedCommands: []
};
};

View file

@ -66,18 +66,11 @@
renderDetails(page, item);
renderTabs(page, item);
if (ApiClient.isWebSocketOpen()) {
$(page).trigger('displayingitem', [{
var vals = [item.Type, item.Id, item.Name];
var context = getParameterByName('context');
if (context) {
vals.push(vals);
}
ApiClient.sendWebSocketMessage("Context", vals.join('|'));
}
item: item,
context: getParameterByName('context')
}]);
Dashboard.getCurrentUser().done(function (user) {

View file

@ -120,9 +120,11 @@
setPeopleHeader(page, item);
if (ApiClient.isWebSocketOpen()) {
ApiClient.sendWebSocketMessage("Context", [item.Type, item.Id, item.Name, context].join('|'));
}
$(page).trigger('displayingitem', [{
item: item,
context: context
}]);
Dashboard.hideLoadingMsg();
});

View file

@ -93,9 +93,10 @@
Dashboard.setPageTitle(name);
if (ApiClient.isWebSocketOpen()) {
ApiClient.sendWebSocketMessage("Context", [item.Type, item.Id, item.Name].join('|'));
}
$(page).trigger('displayingitem', [{
item: item
}]);
});

View file

@ -672,7 +672,7 @@
html += '<a data-itemid="' + item.Id + '" class="' + cssClass + '" data-mediasourcecount="' + mediaSourceCount + '" href="' + href + '">';
var style = "";
options.lazy = false;
if (imgUrl && !options.lazy) {
style += 'background-image:url(\'' + imgUrl + '\');';
}

View file

@ -135,14 +135,11 @@
$('.userDataIcons', page).html(LibraryBrowser.getUserDataIconsHtml(item));
if (ApiClient.isWebSocketOpen()) {
$(page).trigger('displayingitem', [{
var vals = [item.Type, item.Id, item.Name];
vals.push('livetv');
ApiClient.sendWebSocketMessage("Context", vals.join('|'));
}
item: item,
context: 'livetv'
}]);
Dashboard.getCurrentUser().done(function (user) {

View file

@ -50,14 +50,11 @@
LiveTvHelpers.renderMiscProgramInfo($('.miscTvProgramInfo', page), item);
if (ApiClient.isWebSocketOpen()) {
$(page).trigger('displayingitem', [{
var vals = [item.Type, item.Id, item.Name];
vals.push('livetv');
ApiClient.sendWebSocketMessage("Context", vals.join('|'));
}
item: item,
context: 'livetv'
}]);
if (item.TimerId) {
$('#cancelRecordingButtonContainer', page).show();

View file

@ -55,14 +55,11 @@
LiveTvHelpers.renderMiscProgramInfo($('.miscTvProgramInfo', page), item);
if (ApiClient.isWebSocketOpen()) {
$(page).trigger('displayingitem', [{
var vals = [item.Type, item.Id, item.Name];
vals.push('livetv');
ApiClient.sendWebSocketMessage("Context", vals.join('|'));
}
item: item,
context: 'livetv'
}]);
$('.recordingStatus', page).html('Status:&nbsp;&nbsp;&nbsp;' + item.Status);

View file

@ -1,11 +1,26 @@
(function ($, window) {
var enableMirrorMode;
var currentDisplayInfo;
function mirrorItem(info) {
var item = info.item;
MediaController.getCurrentPlayer().displayContent({
itemName: item.Name,
itemId: item.Id,
itemType: item.Type,
context: info.context
});
}
function mediaController() {
var self = this;
var currentPlayer;
var currentTargetInfo;
var players = [];
self.registerPlayer = function (player) {
@ -308,11 +323,11 @@
else if (cmd.Name === 'ToggleMute') {
localPlayer.toggleMute();
}
else if (msg.Data.Command === 'Fullscreen') {
else if (cmd.Name === 'Fullscreen') {
localPlayer.remoteFullscreen();
}
else if (msg.Data.Command === 'SetVolume') {
localPlayer.setVolume(cmd.Arguments.Volume);
else if (cmd.Name === 'SetVolume') {
localPlayer.setVolume(parseFloat(cmd.Arguments.Volume));
}
}
}
@ -324,7 +339,9 @@
var playerInfo = MediaController.getPlayerInfo();
var html = '';
html += '<h3>Select Player:</h3>';
html += '<form>';
html += '<form><h3>Select Player:</h3>';
html += '<fieldset data-role="controlgroup" data-mini="true">';
for (var i = 0, length = targets.length; i < length; i++) {
@ -336,7 +353,9 @@
var isChecked = target.id == playerInfo.id;
var checkedHtml = isChecked ? ' checked="checked"' : '';
html += '<input type="radio" class="radioSelectPlayerTarget" name="radioSelectPlayerTarget" data-mediatypes="' + target.playableMediaTypes.join(',') + '" data-playername="' + target.playerName + '" data-targetid="' + target.id + '" data-targetname="' + target.name + '" id="' + id + '" value="' + target.id + '"' + checkedHtml + '>';
var mirror = (!target.isLocalPlayer && target.supportedCommands.indexOf('DisplayContent') != -1) ? 'true' : 'false';
html += '<input type="radio" class="radioSelectPlayerTarget" name="radioSelectPlayerTarget" data-mirror="' + mirror + '" data-mediatypes="' + target.playableMediaTypes.join(',') + '" data-playername="' + target.playerName + '" data-targetid="' + target.id + '" data-targetname="' + target.name + '" id="' + id + '" value="' + target.id + '"' + checkedHtml + '>';
html += '<label for="' + id + '" style="font-weight:normal;">' + target.name;
if (target.appName) {
@ -350,6 +369,11 @@
html += '<p class="fieldDescription">All plays will be sent to the selected player.</p>';
var checkedHtml = enableMirrorMode ? ' checked="checked"' : '';
html += '<div style="margin-top:1.5em;" class="fldMirrorMode"><label for="chkEnableMirrorMode">Enable Mirror Mode</label><input type="checkbox" class="chkEnableMirrorMode" id="chkEnableMirrorMode" data-mini="true"' + checkedHtml + ' /></div>';
html += '</form>';
return html;
}
@ -374,8 +398,36 @@
$('.players', elem).html(getTargetsHtml(targets)).trigger('create');
$('.chkEnableMirrorMode', elem).on().on('change', function () {
enableMirrorMode = this.checked;
if (this.checked && currentDisplayInfo) {
mirrorItem(currentDisplayInfo);
}
});
$('.radioSelectPlayerTarget', elem).on('change', function () {
var supportsMirror = this.getAttribute('data-mirror') == 'true';
if (supportsMirror) {
$('.fldMirrorMode', elem).show();
} else {
$('.fldMirrorMode', elem).hide();
$('.chkEnableMirrorMode', elem).checked(false).trigger('change').checkboxradio('refresh');
}
}).each(function () {
if (this.checked) {
$(this).trigger('change');
}
}).on('change', function () {
var playerName = this.getAttribute('data-playername');
var targetId = this.getAttribute('data-targetid');
var targetName = this.getAttribute('data-targetname');
@ -401,4 +453,21 @@
});
});
$(document).on('pagebeforeshow', ".page", function () {
var page = this;
currentDisplayInfo = null;
}).on('displayingitem', ".libraryPage", function (e, info) {
var page = this;
currentDisplayInfo = info;
if (enableMirrorMode) {
mirrorItem(info);
}
});
})(jQuery, window);

View file

@ -28,7 +28,8 @@
id: ApiClient.deviceId(),
playerName: self.name,
playableMediaTypes: ['Audio', 'Video'],
isLocalPlayer: true
isLocalPlayer: true,
supportedCommands: Dashboard.getSupportedRemoteCommands()
}];
return targets;
@ -521,19 +522,60 @@
url = "css/images/items/detail/video.png";
}
var name = state.itemName;
var nowPlayingTextElement = $('.nowPlayingText', mediaControls);
var nameHtml = self.getNowPlayingNameHtml(state);
if (state.itemSubName) {
name += '<br/>' + state.itemSubName;
if (nameHtml.indexOf('<br/>') != -1) {
nowPlayingTextElement.addClass('nowPlayingDoubleText');
} else {
nowPlayingTextElement.removeClass('nowPlayingDoubleText');
}
$('.nowPlayingImage', mediaControls).html('<img src="' + url + '" />');
nowPlayingTextElement.html(name);
nowPlayingTextElement.html(nameHtml);
};
self.getNowPlayingNameHtml = function (playerState) {
var topText = playerState.itemName;
if (playerState.mediaType == 'Video') {
if (playerState.indexNumber != null) {
topText = playerState.indexNumber + " - " + topText;
}
if (playerState.parentIndexNumber != null) {
topText = playerState.parentIndexNumber + "." + topText;
}
}
var bottomText = '';
if (playerState.artists && playerState.artists.length) {
bottomText = topText;
topText = playerState.artists[0];
}
else if (playerState.seriesName || playerState.album) {
bottomText = topText;
topText = playerState.seriesName || playerState.album;
}
else if (playerState.productionYear) {
bottomText = playerState.productionYear;
}
return bottomText ? topText + '<br/>' + bottomText : topText;
};
self.displayContent = function (options) {
// Handle it the same as a remote control command
Dashboard.onBrowseCommand({
ItemName: options.itemName,
ItemType: options.itemType,
ItemId: options.itemId,
Context: options.context
});
};
self.getItemsForPlayback = function (query) {
@ -894,7 +936,7 @@
return currentMediaElement;
};
self.getPlayerState = function() {
self.getPlayerState = function () {
var deferred = $.Deferred();
@ -930,41 +972,15 @@
state.itemId = item.Id;
state.mediaType = item.MediaType;
state.itemType = item.Type;
var itemName = '';
var itemSubName = '';
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) {
seriesName = item.SeriesName || item.Album;
}
if (seriesName) {
itemName = seriesName;
itemSubName = name;
} else {
itemName = name;
}
if (!itemSubName && item.ProductionYear) {
itemSubName = item.ProductionYear;
}
state.indexNumber = item.IndexNumber;
state.indexNumberEnd = item.IndexNumberEnd;
state.parentIndexNumber = item.ParentIndexNumber;
state.productionYear = item.ProductionYear;
state.premiereDate = item.PremiereDate;
state.seriesName = item.SeriesName;
state.album = item.Album;
state.itemName = item.Name;
state.artists = item.Artists;
var imageTags = item.ImageTags || {};
@ -973,6 +989,16 @@
state.primaryImageItemId = item.Id;
state.primaryImageTag = imageTags.Primary;
}
else if (item.AlbumPrimaryImageTag) {
state.primaryImageItemId = item.AlbumId;
state.primaryImageTag = item.AlbumPrimaryImageTag;
}
else if (item.SeriesPrimaryImageTag) {
state.primaryImageItemId = item.SeriesId;
state.primaryImageTag = item.SeriesPrimaryImageTag;
}
if (item.BackdropImageTags && item.BackdropImageTags.length) {
@ -985,9 +1011,6 @@
state.thumbItemId = item.Id;
state.thumbImageTag = imageTags.Thumb;
}
state.itemName = itemName;
state.itemSubName = itemSubName;
}
return state;
@ -1123,6 +1146,7 @@
return $('.mediaPlayerAudio');
}
var supportsAac = document.createElement('audio').canPlayType('audio/aac').replace(/no/, '');
function playAudio(item, mediaSource, startPositionTicks) {
startPositionTicks = startPositionTicks || 0;
@ -1134,30 +1158,36 @@
mediaSourceId: mediaSource.Id
};
var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, {
audioCodec: 'mp3'
}));
var mediaStreams = mediaSource.MediaStreams;
var sourceContainer = (mediaSource.Container || '').toLowerCase();
var isStatic = false;
var seekParam = startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
for (var i = 0, length = mediaStreams.length; i < length; i++) {
if (sourceContainer == 'mp3' ||
(sourceContainer == 'aac' && supportsAac)) {
var stream = mediaStreams[i];
for (var i = 0, length = mediaSource.MediaStreams.length; i < length; i++) {
var stream = mediaSource.MediaStreams[i];
if (stream.Type == "Audio") {
var container = (mediaSource.Container || '').toLowerCase();
// Stream statically when possible
if (container == 'mp3' && stream.BitRate <= 256000) {
mp3Url += "&static=true" + seekParam;
if (stream.BitRate <= 256000) {
isStatic = true;
}
break;
}
}
}
var outputContainer = isStatic ? sourceContainer : 'mp3';
var audioUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.' + outputContainer, $.extend({}, baseParams, {
audioCodec: outputContainer
}));
if (isStatic) {
var seekParam = startPositionTicks ? '#t=' + (startPositionTicks / 10000000) : '';
audioUrl += "&static=true" + seekParam;
}
self.startTimeTicksOffset = isStatic ? 0 : startPositionTicks;
@ -1165,7 +1195,7 @@
return getAudioElement().each(function () {
this.src = mp3Url;
this.src = audioUrl;
this.volume = initialVolume;
this.play();

View file

@ -222,18 +222,18 @@
updateNowPlayingInfo(state);
}
var currentImgUrl;
function updateNowPlayingInfo(state) {
var name = state.itemName;
var nameHtml = MediaPlayer.getNowPlayingNameHtml(state);
if (state.itemSubName) {
name += '<br/>' + state.itemSubName;
if (nameHtml.indexOf('<br/>') != -1) {
nowPlayingTextElement.addClass('nowPlayingDoubleText');
} else {
nowPlayingTextElement.removeClass('nowPlayingDoubleText');
}
nowPlayingTextElement.html(name);
nowPlayingTextElement.html(nameHtml);
var url;
@ -273,6 +273,12 @@
url = "css/images/items/detail/video.png";
}
if (url == currentImgUrl) {
return;
}
currentImgUrl = url;
nowPlayingImageElement.html('<img src="' + url + '" />');
}

View file

@ -529,6 +529,18 @@
});
};
self.displayContent = function (options) {
sendCommand('DisplayContent', {
ItemName: options.itemName,
ItemType: options.itemType,
ItemId: options.itemId,
Context: options.context
});
};
self.getPlayerState = function () {
var deferred = $.Deferred();
@ -615,7 +627,8 @@
playerName: self.name,
appName: s.Client,
playableMediaTypes: s.PlayableMediaTypes,
isLocalPlayer: false
isLocalPlayer: false,
supportedCommands: s.SupportedCommands
};
});
@ -648,13 +661,17 @@
if (item) {
state.itemId = item.Id;
state.itemType = item.Type;
state.mediaType = item.MediaType;
state.runtimeTicks = item.RunTimeTicks;
state.mediaSource = item.MediaSourceId;
state.positionTicks = session.NowPlayingPositionTicks || 0;
state.itemType = item.Type;
state.indexNumber = item.IndexNumber;
state.indexNumberEnd = item.IndexNumberEnd;
state.parentIndexNumber = item.ParentIndexNumber;
state.productionYear = item.ProductionYear;
state.premiereDate = item.PremiereDate;
state.seriesName = item.SeriesName;
state.album = item.Album;
state.itemName = item.Name;
state.artists = item.Artists;
state.primaryImageItemId = item.PrimaryImageItemId;
state.primaryImageTag = item.PrimaryImageTag;
@ -664,6 +681,10 @@
state.thumbItemId = item.ThumbItemId;
state.thumbImageTag = item.ThumbImageTag;
state.mediaSource = item.MediaSourceId;
state.positionTicks = session.NowPlayingPositionTicks || 0;
state.runtimeTicks = item.RunTimeTicks;
}
return state;

View file

@ -263,6 +263,8 @@ var Dashboard = {
var html = '<span style="margin-right: 1em;">Please refresh this page to receive new updates from the server.</span>';
html += '<button type="button" data-icon="refresh" onclick="$(this).buttonEnabled(false);Dashboard.reloadPage();" data-theme="b" data-inline="true" data-mini="true">Refresh</button>';
Dashboard.showFooterNotification({ id: "dashboardVersionWarning", html: html, forceShow: true, allowHide: false });
},
@ -801,6 +803,16 @@ var Dashboard = {
ApiClient.openWebSocket(webSocketUrl);
},
onWebSocketOpened: function () {
ApiClient.reportCapabilities({
PlayableMediaTypes: "Audio,Video",
SupportedCommands: Dashboard.getSupportedRemoteCommands().join(',')
});
},
onWebSocketMessageReceived: function (e, data) {
var msg = data;
@ -878,10 +890,6 @@ var Dashboard = {
}
});
}
else if (msg.MessageType === "Browse") {
Dashboard.onBrowseCommand(msg.Data);
}
else if (msg.MessageType === "GeneralCommand") {
var cmd = msg.Data;
@ -892,6 +900,9 @@ var Dashboard = {
else if (cmd.Name === 'GoToSettings') {
Dashboard.navigate('dashboard.html');
}
else if (cmd.Name === 'DisplayContent') {
Dashboard.onBrowseCommand(cmd.Arguments);
}
}
else if (msg.MessageType === "MessageCommand") {
@ -1237,6 +1248,27 @@ var Dashboard = {
}
$(select).html(html).selectmenu("refresh");
},
getSupportedRemoteCommands: function () {
// Full list
// https://github.com/MediaBrowser/MediaBrowser/blob/master/MediaBrowser.Model/Session/GeneralCommand.cs
return [
"GoHome",
"GoToSettings",
"VolumeUp",
"VolumeDown",
"Mute",
"Unmute",
"ToggleMute",
"SetVolume",
"ToggleFullscreen",
"SetAudioStreamIndex",
"SetSubtitleStreamIndex",
"DisplayContent"
];
}
};
@ -1252,7 +1284,7 @@ else if (!IsStorageEnabled()) {
var ApiClient = MediaBrowser.ApiClient.create("Dashboard", window.dashboardVersion);
$(ApiClient).on("websocketmessage", Dashboard.onWebSocketMessageReceived);
$(ApiClient).on("websocketopen", Dashboard.onWebSocketOpened).on("websocketmessage", Dashboard.onWebSocketMessageReceived);
$(function () {

View file

@ -1,29 +1,11 @@
var SupporterPage = {
(function () {
onPageShow: function () {
SupporterPage.load();
},
$(document).on('pageshow', "#supporterPage", function () {
onPageHide: function () {
var page = this;
},
load: function() {
Dashboard.showLoadingMsg();
var page = $.mobile.activePage;
ApiClient.getPluginSecurityInfo().done(function (info) {
$('#txtSupporterKey', page).val(info.SupporterKey);
if (info.IsMBSupporter) {
$('.supporterOnly', page).show();
} else {
$('.supporterOnly', page).hide();
}
$('#paypalReturnUrl', page).val(ApiClient.getUrl("supporterkey.html"));
Dashboard.hideLoadingMsg();
});
}
};
$(document).on('pageshow', "#supporterPage", SupporterPage.onPageShow)
.on('pagehide', "#supporterPage", SupporterPage.onPageHide);
});
})();

View file

@ -18,15 +18,9 @@
</div>
<h3>Support the Media Browser Team</h3>
<p>
Help ensure the continued development of this product by donating a minimum of $10 (greater amounts greatly appreciated). A portion of all donations will be contributed to other <a href="about.html">free tools</a> we depend on.
Help ensure the continued development of this project by donating. A portion of all donations will be contributed to other <a href="about.html">free tools</a> we depend on.
</p>
<p style="display: none; padding: 1em; border-radius: 5px; font-weight: normal;" class="ui-bar-a supporterOnly">
<strong>Thank You</strong> for your past support of the Media Browser Team. Users like you make it possible for
Media Browser to exist and keep getting better and better. You can always support us again if you feel you are getting maximum
value from the product.
</p>
<br />
<form name="_xclick" action="https://www.paypal.com/cgi-bin/webscr"
method="post">

View file

@ -1728,6 +1728,16 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
});
};
self.reportCapabilities = function (options) {
var url = self.getUrl("Sessions/Capabilities", options);
return self.ajax({
type: "POST",
url: url
});
};
self.updateItemImageIndex = function (itemId, itemType, itemName, imageType, imageIndex, newIndex) {
if (!imageType) {
@ -3758,24 +3768,6 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
});
};
self.sendBrowseCommand = function (sessionId, options) {
if (!sessionId) {
throw new Error("null sessionId");
}
if (!options) {
throw new Error("null options");
}
var url = self.getUrl("Sessions/" + sessionId + "/Viewing", options);
return self.ajax({
type: "POST",
url: url
});
};
self.sendPlayCommand = function (sessionId, options) {
if (!sessionId) {