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

unify video osd

This commit is contained in:
Luke Pulverenti 2017-01-03 01:51:36 -05:00
parent b3a664583a
commit 407f137095
24 changed files with 713 additions and 6466 deletions

View file

@ -153,6 +153,14 @@ define(['appStorage', 'browser'], function (appStorage, browser) {
features.push('imageanalysis'); features.push('imageanalysis');
} }
if (Dashboard.isConnectMode()) {
features.push('multiserver');
}
if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) {
features.push('physicalvolumecontrol');
}
return features; return features;
}(); }();

View file

@ -124,7 +124,7 @@
}); });
} }
if (Dashboard.isConnectMode()) { if (appHost.supports('multiserver')) {
commands.push({ commands.push({
name: globalize.translate('HeaderSelectServer'), name: globalize.translate('HeaderSelectServer'),
id: 'selectserver' id: 'selectserver'

View file

@ -1,47 +1,53 @@
define(['appSettings', 'events', 'browser', 'libraryMenu', 'loading'], function (appSettings, events, browser, libraryMenu, loading) { define(['appSettings', 'events', 'browser', 'libraryMenu', 'loading', 'playbackManager'], function (appSettings, events, browser, libraryMenu, loading, playbackManager) {
'use strict'; 'use strict';
var currentDisplayInfo; var currentDisplayInfo;
function mirrorItem(info) { function mirrorItem(info, player) {
var item = info.item; var item = info.item;
MediaController.getCurrentPlayer().displayContent({ playbackManager.displayContent({
ItemName: item.Name, ItemName: item.Name,
ItemId: item.Id, ItemId: item.Id,
ItemType: item.Type, ItemType: item.Type,
Context: info.context Context: info.context
}); }, player);
} }
function mirrorIfEnabled(info) { function mirrorIfEnabled(info) {
info = info || currentDisplayInfo; info = info || currentDisplayInfo;
if (info && MediaController.enableDisplayMirroring()) { if (info && playbackManager.enableDisplayMirroring()) {
var player = MediaController.getPlayerInfo(); var player = playbackManager.getPlayerInfo();
if (player) {
if (!player.isLocalPlayer && player.supportedCommands.indexOf('DisplayContent') != -1) { if (!player.isLocalPlayer && player.supportedCommands.indexOf('DisplayContent') != -1) {
mirrorItem(info); mirrorItem(info, player);
}
} }
} }
} }
function showPlayerSelection(button, enableHistory) { function showPlayerSelection(button, enableHistory) {
var playerInfo = MediaController.getPlayerInfo(); var currentPlayerInfo = playbackManager.getPlayerInfo();
if (!playerInfo.isLocalPlayer) { if (currentPlayerInfo) {
showActivePlayerMenu(playerInfo); if (!currentPlayerInfo.isLocalPlayer) {
showActivePlayerMenu(currentPlayerInfo);
return; return;
} }
}
var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
loading.show(); loading.show();
MediaController.getTargets().then(function (targets) { playbackManager.getTargets().then(function (targets) {
var menuItems = targets.map(function (t) { var menuItems = targets.map(function (t) {
@ -54,7 +60,7 @@
return { return {
name: name, name: name,
id: t.id, id: t.id,
selected: playerInfo.id == t.id selected: currentPlayerId === t.id
}; };
}); });
@ -84,7 +90,7 @@
return t.id == id; return t.id == id;
})[0]; })[0];
MediaController.trySetActivePlayer(target.playerName, target); playbackManager.trySetActivePlayer(target.playerName, target);
mirrorIfEnabled(); mirrorIfEnabled();
@ -127,7 +133,7 @@
if (playerInfo.supportedCommands.indexOf('DisplayContent') != -1) { if (playerInfo.supportedCommands.indexOf('DisplayContent') != -1) {
html += '<label class="checkboxContainer">'; html += '<label class="checkboxContainer">';
var checkedHtml = MediaController.enableDisplayMirroring() ? ' checked' : ''; var checkedHtml = playbackManager.enableDisplayMirroring() ? ' checked' : '';
html += '<input type="checkbox" is="emby-checkbox" class="chkMirror"' + checkedHtml + '/>'; html += '<input type="checkbox" is="emby-checkbox" class="chkMirror"' + checkedHtml + '/>';
html += '<span>' + Globalize.translate('OptionEnableDisplayMirroring') + '</span>'; html += '<span>' + Globalize.translate('OptionEnableDisplayMirroring') + '</span>';
html += '</label>'; html += '</label>';
@ -162,7 +168,7 @@
} }
dlg.querySelector('.btnDisconnect').addEventListener('click', function () { dlg.querySelector('.btnDisconnect').addEventListener('click', function () {
MediaController.disconnectFromPlayer(); playbackManager.disconnectFromPlayer();
dialogHelper.close(dlg); dialogHelper.close(dlg);
}); });
@ -178,7 +184,7 @@
} }
function onMirrorChange() { function onMirrorChange() {
MediaController.enableDisplayMirroring(this.checked); playbackManager.enableDisplayMirroring(this.checked);
} }
function onCastButtonClicked() { function onCastButtonClicked() {

View file

@ -1,4 +1,4 @@
define(['browser', 'datetime', 'libraryBrowser', 'listView', 'userdataButtons', 'imageLoader', 'cardStyle'], function (browser, datetime, libraryBrowser, listView, userdataButtons, imageLoader) { define(['browser', 'datetime', 'libraryBrowser', 'listView', 'userdataButtons', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'apphost', 'cardStyle'], function (browser, datetime, libraryBrowser, listView, userdataButtons, imageLoader, playbackManager, nowPlayingHelper, events, appHost) {
'use strict'; 'use strict';
function showSlideshowMenu(context) { function showSlideshowMenu(context) {
@ -16,24 +16,8 @@
var menuItems = streams.map(function (s) { var menuItems = streams.map(function (s) {
var name = (s.Codec || '').toUpperCase();
if (s.Profile) {
name += ' ' + s.Profile;
}
if (s.Language) {
name += ' · ' + s.Language;
}
if (s.Layout) {
name += ' · ' + s.Layout;
}
else if (s.Channels) {
name += ' · ' + s.Channels + ' ch';
}
var menuItem = { var menuItem = {
name: s.DisplayTitle || name, name: s.DisplayTitle,
id: s.Index id: s.Index
}; };
@ -67,24 +51,8 @@
var menuItems = streams.map(function (s) { var menuItems = streams.map(function (s) {
var name = (s.Language || Globalize.translate('LabelUnknownLanguage'));
if (s.IsDefault && s.IsForced) {
name += ' · ' + Globalize.translate('LabelDefaultForcedStream');
}
else if (s.IsDefault) {
name += ' · ' + Globalize.translate('LabelDefaultStream');
}
else if (s.IsForced) {
name += ' · ' + Globalize.translate('LabelForcedStream');
}
if (s.Codec) {
name += ' · ' + s.Codec.toUpperCase();
}
var menuItem = { var menuItem = {
name: s.DisplayTitle || name, name: s.DisplayTitle,
id: s.Index id: s.Index
}; };
@ -129,11 +97,22 @@
}).length > 0; }).length > 0;
} }
function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) {
var names = nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo);
return names.map(function (i) {
return i.text;
}).join('<br/>');
}
var currentImgUrl; var currentImgUrl;
function updateNowPlayingInfo(context, state) { function updateNowPlayingInfo(context, state) {
var item = state.NowPlayingItem; var item = state.NowPlayingItem;
var displayName = item ? MediaController.getNowPlayingNameHtml(item).replace('<br/>', ' - ') : ''; var displayName = item ? getNowPlayingNameHtml(item).replace('<br/>', ' - ') : '';
context.querySelector('.nowPlayingPageTitle').innerHTML = displayName; context.querySelector('.nowPlayingPageTitle').innerHTML = displayName;
@ -248,8 +227,10 @@
var dlg; var dlg;
var currentPlayer; var currentPlayer;
var currentPlayerSupportedCommands = [];
var lastPlayerState; var lastPlayerState;
var lastUpdateTime = 0; var lastUpdateTime = 0;
var currentRuntimeTicks = 0;
var self = this; var self = this;
var playlistNeedsRefresh = true; var playlistNeedsRefresh = true;
@ -260,13 +241,13 @@
var state = lastPlayerState; var state = lastPlayerState;
switch ((state.PlayState || {}).RepeatMode) { switch ((state.PlayState || {}).RepeatMode) {
case 'RepeatNone': case 'RepeatNone':
player.setRepeatMode('RepeatAll'); playbackManager.setRepeatMode('RepeatAll', player);
break; break;
case 'RepeatAll': case 'RepeatAll':
player.setRepeatMode('RepeatOne'); playbackManager.setRepeatMode('RepeatOne', player);
break; break;
case 'RepeatOne': case 'RepeatOne':
player.setRepeatMode('RepeatNone'); playbackManager.setRepeatMode('RepeatNone', player);
break; break;
} }
} }
@ -278,7 +259,7 @@
var item = state.NowPlayingItem; var item = state.NowPlayingItem;
var playerInfo = MediaController.getPlayerInfo(); var playerInfo = playbackManager.getPlayerInfo();
var supportedCommands = playerInfo.supportedCommands; var supportedCommands = playerInfo.supportedCommands;
var playState = state.PlayState || {}; var playState = state.PlayState || {};
@ -310,53 +291,16 @@
buttonEnabled(context.querySelector('.btnNextTrack'), item != null); buttonEnabled(context.querySelector('.btnNextTrack'), item != null);
buttonEnabled(context.querySelector('.btnPreviousTrack'), item != null); buttonEnabled(context.querySelector('.btnPreviousTrack'), item != null);
var btnPause = context.querySelector('.btnPause');
var btnPlay = context.querySelector('.btnPlay');
buttonEnabled(btnPause, item != null);
buttonEnabled(btnPlay, item != null);
if (playState.IsPaused) {
hideButton(btnPause);
showButton(btnPlay);
} else {
showButton(btnPause);
hideButton(btnPlay);
}
var positionSlider = context.querySelector('.nowPlayingPositionSlider'); var positionSlider = context.querySelector('.nowPlayingPositionSlider');
if (positionSlider && !positionSlider.dragging) {
if (!positionSlider.dragging) {
if (item && item.RunTimeTicks) {
var pct = playState.PositionTicks / item.RunTimeTicks;
pct *= 100;
positionSlider.value = pct;
} else {
positionSlider.value = 0;
}
positionSlider.disabled = !playState.CanSeek; positionSlider.disabled = !playState.CanSeek;
} }
if (playState.PositionTicks == null) { updatePlayPauseState(playState.IsPaused, item != null);
context.querySelector('.positionTime').innerHTML = '--:--';
} else {
context.querySelector('.positionTime').innerHTML = datetime.getDisplayRunningTime(playState.PositionTicks);
}
if (item && item.RunTimeTicks != null) { var runtimeTicks = item ? item.RunTimeTicks : null;
context.querySelector('.runtime').innerHTML = datetime.getDisplayRunningTime(item.RunTimeTicks); updateTimeDisplay(playState.PositionTicks, runtimeTicks);
} else { updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel);
context.querySelector('.runtime').innerHTML = '--:--';
}
if (item && item.MediaType == 'Video') { if (item && item.MediaType == 'Video') {
context.classList.remove('hideVideoButtons'); context.classList.remove('hideVideoButtons');
@ -364,7 +308,7 @@
context.classList.add('hideVideoButtons'); context.classList.add('hideVideoButtons');
} }
if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) { if (playerInfo.isLocalPlayer && appHost.supports('physicalvolumecontrol')) {
context.classList.add('hideVolumeButtons'); context.classList.add('hideVolumeButtons');
} else { } else {
context.classList.remove('hideVolumeButtons'); context.classList.remove('hideVolumeButtons');
@ -393,6 +337,65 @@
updateNowPlayingInfo(context, state); updateNowPlayingInfo(context, state);
} }
function updatePlayerVolumeState(isMuted, volumeLevel) {
}
function updatePlayPauseState(isPaused, isActive) {
var context = dlg;
var btnPause = context.querySelector('.btnPause');
var btnPlay = context.querySelector('.btnPlay');
buttonEnabled(btnPause, isActive);
buttonEnabled(btnPlay, isActive);
if (isPaused) {
hideButton(btnPause);
showButton(btnPlay);
} else {
showButton(btnPause);
hideButton(btnPlay);
}
}
function updateTimeDisplay(positionTicks, runtimeTicks) {
// See bindEvents for why this is necessary
var context = dlg;
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
if (positionSlider && !positionSlider.dragging) {
if (runtimeTicks) {
var pct = positionTicks / runtimeTicks;
pct *= 100;
positionSlider.value = pct;
} else {
positionSlider.value = 0;
}
}
if (positionTicks == null) {
context.querySelector('.positionTime').innerHTML = '--:--';
} else {
context.querySelector('.positionTime').innerHTML = datetime.getDisplayRunningTime(positionTicks);
}
if (runtimeTicks != null) {
context.querySelector('.runtime').innerHTML = datetime.getDisplayRunningTime(runtimeTicks);
} else {
context.querySelector('.runtime').innerHTML = '--:--';
}
}
function loadPlaylist(context) { function loadPlaylist(context) {
var html = ''; var html = '';
@ -420,7 +423,7 @@
//}); //});
html += listView.getListViewHtml({ html += listView.getListViewHtml({
items: MediaController.playlist(), items: playbackManager.playlist(),
smallIcon: true, smallIcon: true,
action: 'setplaylistindex' action: 'setplaylistindex'
}); });
@ -435,7 +438,7 @@
itemsContainer.innerHTML = html; itemsContainer.innerHTML = html;
var index = MediaController.currentPlaylistIndex(); var index = playbackManager.currentPlaylistIndex();
if (index != -1) { if (index != -1) {
@ -452,27 +455,12 @@
}); });
} }
function onStateChanged(e, state) {
if (e.type == 'positionchange') {
// Try to avoid hammering the document with changes
var now = new Date().getTime();
if ((now - lastUpdateTime) < 700) {
return;
}
lastUpdateTime = now;
}
updatePlayerState(dlg, state);
}
function onPlaybackStart(e, state) { function onPlaybackStart(e, state) {
console.log('remotecontrol event: ' + e.type);
var player = this; var player = this;
playbackManager.beginPlayerUpdates(player);
player.beginPlayerUpdates();
onStateChanged.call(player, e, state); onStateChanged.call(player, e, state);
loadPlaylist(dlg); loadPlaylist(dlg);
@ -480,26 +468,66 @@
function onPlaybackStopped(e, state) { function onPlaybackStopped(e, state) {
console.log('remotecontrol event: ' + e.type);
var player = this; var player = this;
player.endPlayerUpdates(); playbackManager.endPlayerUpdates(player);
onStateChanged.call(player, e, {});
loadPlaylist(dlg); loadPlaylist(dlg);
} }
function onPlayPauseStateChanged(e) {
var player = this;
updatePlayPauseState(player.paused(), true);
}
function onStateChanged(event, state) {
//console.log('nowplaying event: ' + e.type);
var player = this;
updatePlayerState(dlg, state);
}
function onTimeUpdate(e) {
// Try to avoid hammering the document with changes
var now = new Date().getTime();
if ((now - lastUpdateTime) < 700) {
return;
}
lastUpdateTime = now;
var player = this;
var state = lastPlayerState;
var nowPlayingItem = state.NowPlayingItem || {};
currentRuntimeTicks = playbackManager.duration(player);
updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks);
}
function onVolumeChanged(e) {
var player = this;
updatePlayerVolumeState(player.isMuted(), player.getVolume());
}
function releaseCurrentPlayer() { function releaseCurrentPlayer() {
if (currentPlayer) { var player = currentPlayer;
Events.off(currentPlayer, 'playbackstart', onPlaybackStart); if (player) {
Events.off(currentPlayer, 'playbackstop', onPlaybackStopped);
Events.off(currentPlayer, 'volumechange', onStateChanged);
Events.off(currentPlayer, 'playstatechange', onStateChanged);
Events.off(currentPlayer, 'positionchange', onStateChanged);
currentPlayer.endPlayerUpdates(); events.off(player, 'playbackstart', onPlaybackStart);
events.off(player, 'playbackstop', onPlaybackStopped);
events.off(player, 'volumechange', onVolumeChanged);
events.off(player, 'pause', onPlayPauseStateChanged);
events.off(player, 'playing', onPlayPauseStateChanged);
events.off(player, 'timeupdate', onTimeUpdate);
playbackManager.endPlayerUpdates(player);
currentPlayer = null; currentPlayer = null;
} }
} }
@ -510,67 +538,58 @@
currentPlayer = player; currentPlayer = player;
player.getPlayerState().then(function (state) { if (!player) {
return;
}
playbackManager.getPlayerState(player).then(function (state) {
if (state.NowPlayingItem) { if (state.NowPlayingItem) {
player.beginPlayerUpdates(); playbackManager.beginPlayerUpdates(player);
} }
onStateChanged.call(player, { type: 'init' }, state); onStateChanged.call(player, { type: 'init' }, state);
}); });
Events.on(player, 'playbackstart', onPlaybackStart); events.on(player, 'playbackstart', onPlaybackStart);
Events.on(player, 'playbackstop', onPlaybackStopped); events.on(player, 'playbackstop', onPlaybackStopped);
Events.on(player, 'volumechange', onStateChanged); events.on(player, 'volumechange', onVolumeChanged);
Events.on(player, 'playstatechange', onStateChanged); events.on(player, 'pause', onPlayPauseStateChanged);
Events.on(player, 'positionchange', onStateChanged); events.on(player, 'playing', onPlayPauseStateChanged);
events.on(player, 'timeupdate', onTimeUpdate);
var playerInfo = MediaController.getPlayerInfo(); var playerInfo = playbackManager.getPlayerInfo();
var supportedCommands = playerInfo.supportedCommands; var supportedCommands = playerInfo.supportedCommands;
currentPlayerSupportedCommands = supportedCommands;
updateSupportedCommands(context, supportedCommands); updateSupportedCommands(context, supportedCommands);
} }
function updateCastIcon(context) { function updateCastIcon(context) {
var info = MediaController.getPlayerInfo(); var info = playbackManager.getPlayerInfo();
var btnCast = context.querySelector('.nowPlayingCastIcon'); var btnCast = context.querySelector('.nowPlayingCastIcon');
if (info.isLocalPlayer) { if (info && !info.isLocalPlayer) {
btnCast.querySelector('i').innerHTML = 'cast';
btnCast.classList.remove('btnActiveCast');
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = '';
} else {
btnCast.querySelector('i').innerHTML = 'cast_connected'; btnCast.querySelector('i').innerHTML = 'cast_connected';
btnCast.classList.add('btnActiveCast'); btnCast.classList.add('btnActiveCast');
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = info.deviceName || info.name; context.querySelector('.nowPlayingSelectedPlayer').innerHTML = info.deviceName || info.name;
} else {
btnCast.querySelector('i').innerHTML = 'cast';
btnCast.classList.remove('btnActiveCast');
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = '';
} }
} }
function parentWithClass(elem, className) {
while (!elem.classList || !elem.classList.contains(className)) {
elem = elem.parentNode;
if (!elem) {
return null;
}
}
return elem;
}
function onBtnCommandClick() { function onBtnCommandClick() {
if (currentPlayer) { if (currentPlayer) {
if (this.classList.contains('repeatToggleButton')) { if (this.classList.contains('repeatToggleButton')) {
toggleRepeat(currentPlayer); toggleRepeat(currentPlayer);
} else { } else {
MediaController.sendCommand({ playbackManager.sendCommand({
Name: this.getAttribute('data-command') Name: this.getAttribute('data-command')
}, currentPlayer); }, currentPlayer);
@ -588,7 +607,7 @@
context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) { context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) {
if (currentPlayer) { if (currentPlayer) {
MediaController.sendCommand({ playbackManager.sendCommand({
Name: e.target.getAttribute('data-command') Name: e.target.getAttribute('data-command')
}, currentPlayer); }, currentPlayer);
@ -625,7 +644,7 @@
context.querySelector('.btnStop').addEventListener('click', function () { context.querySelector('.btnStop').addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.stop(); playbackManager.stop(currentPlayer);
} }
}); });
@ -646,14 +665,14 @@
context.querySelector('.btnNextTrack').addEventListener('click', function () { context.querySelector('.btnNextTrack').addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.nextTrack(); playbackManager.nextTrack(currentPlayer);
} }
}); });
context.querySelector('.btnPreviousTrack').addEventListener('click', function () { context.querySelector('.btnPreviousTrack').addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.previousTrack(); playbackManager.previousTrack(currentPlayer);
} }
}); });
@ -661,11 +680,10 @@
var value = this.value; var value = this.value;
if (currentPlayer && lastPlayerState) { if (currentPlayer) {
var newPercent = parseFloat(value); var newPercent = parseFloat(value);
var newPositionTicks = (newPercent / 100) * lastPlayerState.NowPlayingItem.RunTimeTicks; playbackManager.seekPercent(newPercent, currentPlayer);
currentPlayer.seek(Math.floor(newPositionTicks));
} }
}); });
@ -673,11 +691,11 @@
var state = lastPlayerState; var state = lastPlayerState;
if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) { if (!state || !state.NowPlayingItem || !currentRuntimeTicks) {
return '--:--'; return '--:--';
} }
var ticks = state.NowPlayingItem.RunTimeTicks; var ticks = currentRuntimeTicks;
ticks /= 100; ticks /= 100;
ticks *= value; ticks *= value;
@ -689,14 +707,14 @@
var context = dlg; var context = dlg;
updateCastIcon(context); updateCastIcon(context);
bindToPlayer(context, MediaController.getCurrentPlayer()); bindToPlayer(context, playbackManager.getCurrentPlayer());
} }
function onMessageSubmit(e) { function onMessageSubmit(e) {
var form = e.target; var form = e.target;
MediaController.sendCommand({ playbackManager.sendCommand({
Name: 'DisplayMessage', Name: 'DisplayMessage',
Arguments: { Arguments: {
@ -720,7 +738,7 @@
var form = e.target; var form = e.target;
MediaController.sendCommand({ playbackManager.sendCommand({
Name: 'SendString', Name: 'SendString',
Arguments: { Arguments: {
@ -774,14 +792,14 @@
} }
}); });
Events.on(MediaController, 'playerchange', onPlayerChange); events.on(playbackManager, 'playerchange', onPlayerChange);
} }
function onDialogClosed(e) { function onDialogClosed(e) {
releaseCurrentPlayer(); releaseCurrentPlayer();
Events.off(MediaController, 'playerchange', onPlayerChange); events.off(playbackManager, 'playerchange', onPlayerChange);
lastPlayerState = null; lastPlayerState = null;
} }
@ -790,7 +808,7 @@
currentImgUrl = null; currentImgUrl = null;
bindToPlayer(context, MediaController.getCurrentPlayer()); bindToPlayer(context, playbackManager.getCurrentPlayer());
updateCastIcon(context); updateCastIcon(context);
} }

View file

@ -1,7 +1,7 @@
define(['events'], function (events) { define(['events', 'playbackManager'], function (events, playbackManager) {
'use strict'; 'use strict';
function transferPlayback(oldPlayer) { function transferPlayback(oldPlayer, newPlayer) {
oldPlayer.getPlayerState().then(function (state) { oldPlayer.getPlayerState().then(function (state) {
@ -18,7 +18,7 @@
var itemId = item.Id; var itemId = item.Id;
var resumePositionTicks = playState.PositionTicks || 0; var resumePositionTicks = playState.PositionTicks || 0;
MediaController.play({ playbackManager.play({
ids: [itemId], ids: [itemId],
startPositionTicks: resumePositionTicks startPositionTicks: resumePositionTicks
}); });
@ -26,10 +26,9 @@
}); });
} }
events.on(MediaController, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) { events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) {
if (!oldPlayer) { if (!oldPlayer || !newPlayer) {
console.log('Skipping remote control autoplay because oldPlayer is null');
return; return;
} }
@ -45,7 +44,7 @@
// If playback is playing locally and a new player is activated, transfer the media to that player // If playback is playing locally and a new player is activated, transfer the media to that player
if (oldPlayer.isPlaying()) { if (oldPlayer.isPlaying()) {
transferPlayback(oldPlayer); transferPlayback(oldPlayer, newPlayer);
} }
}); });

View file

@ -13,7 +13,7 @@
.background-theme-b .backgroundContainer.withBackdrop { .background-theme-b .backgroundContainer.withBackdrop {
background-color: rgba(6, 6, 6, .94) !important; background-color: rgba(6, 6, 6, .94) !important;
background: linear-gradient(to right, rgba(0, 0, 0, .99), rgba(0, 0, 0, .92), rgba(0, 0, 0, .5)) !important; background: linear-gradient(to right, rgba(0, 0, 0, .99), rgba(0, 0, 0, .94), rgba(0, 0, 0, .5)) !important;
} }
.ui-body-b { .ui-body-b {
@ -797,17 +797,10 @@ span.itemCommunityRating:not(:empty) + .userDataIcons {
} }
.mediaInfoText { .mediaInfoText {
background: rgba(31,31,31,.7); padding: .3em .5em !important;
padding: .3em .5em;
border-radius: .25em;
color: #ddd;
margin-right: .5em; margin-right: .5em;
margin-bottom: .5em; margin-bottom: .5em;
font-size: 94%; font-size: 94% !important;
background: rgba(170,170,190, .2);
display: flex;
align-items: center;
white-space: nowrap;
} }
.mediaInfoText-upper { .mediaInfoText-upper {

View file

@ -54,6 +54,11 @@
will-change: transform; will-change: transform;
contain: layout style; contain: layout style;
font-size: 90%; font-size: 90%;
transition: transform 200ms ease-out;
}
.nowPlayingBar-hidden {
transform: translate3d(0,100%,0);
} }
.hiddenNowPlayingBar .nowPlayingBar { .hiddenNowPlayingBar .nowPlayingBar {

View file

@ -2,6 +2,14 @@
<html class="preload"> <html class="preload">
<head> <head>
<title>Emby</title> <title>Emby</title>
<style>
.transparentDocument, .backgroundContainer-transparent:not(.withBackdrop) {
background: none !important;
background-color: transparent !important;
}
</style>
</head> </head>
<body> <body>
<div class="backdropContainer"></div> <div class="backdropContainer"></div>

View file

@ -1,891 +0,0 @@
define(['appSettings'], function (appSettings) {
'use strict';
// Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js
var currentResolve;
var currentReject;
var PlayerName = 'Chromecast';
function sendConnectionResult(isOk) {
var resolve = currentResolve;
var reject = currentReject;
currentResolve = null;
currentReject = null;
if (isOk) {
if (resolve) {
resolve();
}
} else {
if (reject) {
reject();
} else {
MediaController.removeActivePlayer(PlayerName);
}
}
}
/**
* Constants of states for Chromecast device
**/
var DEVICE_STATE = {
'IDLE': 0,
'ACTIVE': 1,
'WARNING': 2,
'ERROR': 3
};
/**
* Constants of states for CastPlayer
**/
var PLAYER_STATE = {
'IDLE': 'IDLE',
'LOADING': 'LOADING',
'LOADED': 'LOADED',
'PLAYING': 'PLAYING',
'PAUSED': 'PAUSED',
'STOPPED': 'STOPPED',
'SEEKING': 'SEEKING',
'ERROR': 'ERROR'
};
var applicationID = "2D4B1DA3";
// This is the beta version used for testing new changes
//applicationID = '27C4EB5B';
var messageNamespace = 'urn:x-cast:com.connectsdk';
var CastPlayer = function () {
/* device variables */
// @type {DEVICE_STATE} A state for device
this.deviceState = DEVICE_STATE.IDLE;
/* Cast player variables */
// @type {Object} a chrome.cast.media.Media object
this.currentMediaSession = null;
// @type {string} a chrome.cast.Session object
this.session = null;
// @type {PLAYER_STATE} A state for Cast media player
this.castPlayerState = PLAYER_STATE.IDLE;
this.hasReceivers = false;
// bind once - commit 2ebffc2271da0bc5e8b13821586aee2a2e3c7753
this.errorHandler = this.onError.bind(this);
this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this);
this.initializeCastPlayer();
};
/**
* Initialize Cast media player
* Initializes the API. Note that either successCallback and errorCallback will be
* invoked once the API has finished initialization. The sessionListener and
* receiverListener may be invoked at any time afterwards, and possibly more than once.
*/
CastPlayer.prototype.initializeCastPlayer = function () {
var chrome = window.chrome;
if (!chrome) {
return;
}
if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(this.initializeCastPlayer.bind(this), 1000);
return;
}
// request session
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
this.sessionListener.bind(this),
this.receiverListener.bind(this),
"origin_scoped");
console.log('chromecast.initialize');
chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler);
};
/**
* Callback function for init success
*/
CastPlayer.prototype.onInitSuccess = function () {
this.isInitialized = true;
console.log("chromecast init success");
};
/**
* Generic error callback function
*/
CastPlayer.prototype.onError = function () {
console.log("chromecast error");
};
/**
* @param {!Object} e A new session
* This handles auto-join when a page is reloaded
* When active session is detected, playback will automatically
* join existing session and occur in Cast mode and media
* status gets synced up with current media of the session
*/
CastPlayer.prototype.sessionListener = function (e) {
this.session = e;
if (this.session) {
console.log('sessionListener ' + JSON.stringify(e));
if (this.session.media[0]) {
this.onMediaDiscovered('activeSession', this.session.media[0]);
}
this.onSessionConnected(e);
}
};
CastPlayer.prototype.messageListener = function (namespace, message) {
if (typeof (message) === 'string') {
message = JSON.parse(message);
}
if (message.type == 'playbackerror') {
var errorCode = message.data;
setTimeout(function () {
Dashboard.alert({
message: Globalize.translate('MessagePlaybackError' + errorCode),
title: Globalize.translate('HeaderPlaybackError')
});
}, 300);
}
else if (message.type == 'connectionerror') {
setTimeout(function () {
Dashboard.alert({
message: Globalize.translate('MessageChromecastConnectionError'),
title: Globalize.translate('HeaderError')
});
}, 300);
}
else if (message.type) {
Events.trigger(this, message.type, [message.data]);
}
};
/**
* @param {string} e Receiver availability
* This indicates availability of receivers but
* does not provide a list of device IDs
*/
CastPlayer.prototype.receiverListener = function (e) {
if (e === 'available') {
console.log("chromecast receiver found");
this.hasReceivers = true;
}
else {
console.log("chromecast receiver list empty");
this.hasReceivers = false;
}
};
/**
* session update listener
*/
CastPlayer.prototype.sessionUpdateListener = function (isAlive) {
console.log('sessionUpdateListener alive: ' + isAlive);
if (isAlive) {
}
else {
this.session = null;
this.deviceState = DEVICE_STATE.IDLE;
this.castPlayerState = PLAYER_STATE.IDLE;
console.log('sessionUpdateListener: setting currentMediaSession to null');
this.currentMediaSession = null;
sendConnectionResult(false);
}
};
/**
* Requests that a receiver application session be created or joined. By default, the SessionRequest
* passed to the API at initialization time is used; this may be overridden by passing a different
* session request in opt_sessionRequest.
*/
CastPlayer.prototype.launchApp = function () {
console.log("chromecast launching app...");
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this));
};
/**
* Callback function for request session success
* @param {Object} e A chrome.cast.Session object
*/
CastPlayer.prototype.onRequestSessionSuccess = function (e) {
console.log("chromecast session success: " + e.sessionId);
this.onSessionConnected(e);
};
CastPlayer.prototype.onSessionConnected = function (session) {
this.session = session;
this.deviceState = DEVICE_STATE.ACTIVE;
this.session.addMessageListener(messageNamespace, this.messageListener.bind(this));
this.session.addMediaListener(this.sessionMediaListener.bind(this));
this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
Events.trigger(this, 'connect');
this.sendMessage({
options: {},
command: 'Identify'
});
};
/**
* session update listener
*/
CastPlayer.prototype.sessionMediaListener = function (e) {
console.log('sessionMediaListener');
this.currentMediaSession = e;
this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler);
};
/**
* Callback function for launch error
*/
CastPlayer.prototype.onLaunchError = function () {
console.log("chromecast launch error");
this.deviceState = DEVICE_STATE.ERROR;
sendConnectionResult(false);
};
/**
* Stops the running receiver application associated with the session.
*/
CastPlayer.prototype.stopApp = function () {
if (this.session) {
this.session.stop(this.onStopAppSuccess.bind(this, 'Session stopped'),
this.errorHandler);
}
};
/**
* Callback function for stop app success
*/
CastPlayer.prototype.onStopAppSuccess = function (message) {
console.log(message);
this.deviceState = DEVICE_STATE.IDLE;
this.castPlayerState = PLAYER_STATE.IDLE;
console.log('onStopAppSuccess: setting currentMediaSession to null');
this.currentMediaSession = null;
};
/**
* Loads media into a running receiver application
* @param {Number} mediaIndex An index number to indicate current media content
*/
CastPlayer.prototype.loadMedia = function (options, command) {
if (!this.session) {
console.log("no session");
return Promise.reject();
}
// Convert the items to smaller stubs to send the minimal amount of information
options.items = options.items.map(function (i) {
return {
Id: i.Id,
Name: i.Name,
Type: i.Type,
MediaType: i.MediaType,
IsFolder: i.IsFolder
};
});
return this.sendMessage({
options: options,
command: command
});
};
CastPlayer.prototype.sendMessage = function (message) {
var player = this;
var receiverName = null;
if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName) {
receiverName = castPlayer.session.receiver.friendlyName;
}
message = Object.assign(message, {
userId: Dashboard.getCurrentUserId(),
deviceId: ApiClient.deviceId(),
accessToken: ApiClient.accessToken(),
serverAddress: ApiClient.serverAddress(),
receiverName: receiverName
});
var bitrateSetting = appSettings.maxChromecastBitrate();
if (bitrateSetting) {
message.maxBitrate = bitrateSetting;
}
return new Promise(function (resolve, reject) {
require(['chromecasthelpers'], function (chromecasthelpers) {
chromecasthelpers.getServerAddress(ApiClient).then(function (serverAddress) {
message.serverAddress = serverAddress;
player.sendMessageInternal(message).then(resolve, reject);
}, reject);
});
});
};
CastPlayer.prototype.sendMessageInternal = function (message) {
message = JSON.stringify(message);
//console.log(message);
this.session.sendMessage(messageNamespace, message, this.onPlayCommandSuccess.bind(this), this.errorHandler);
return Promise.resolve();
};
CastPlayer.prototype.onPlayCommandSuccess = function () {
console.log('Message was sent to receiver ok.');
};
/**
* Callback function for loadMedia success
* @param {Object} mediaSession A new media object.
*/
CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) {
console.log("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')');
this.currentMediaSession = mediaSession;
if (how == 'loadMedia') {
this.castPlayerState = PLAYER_STATE.PLAYING;
}
if (how == 'activeSession') {
this.castPlayerState = mediaSession.playerState;
}
this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler);
};
/**
* Callback function for media status update from receiver
* @param {!Boolean} e true/false
*/
CastPlayer.prototype.onMediaStatusUpdate = function (e) {
if (e == false) {
this.castPlayerState = PLAYER_STATE.IDLE;
}
console.log("chromecast updating media: " + e);
};
/**
* Set media volume in Cast mode
* @param {Boolean} mute A boolean
*/
CastPlayer.prototype.setReceiverVolume = function (mute, vol) {
if (!this.currentMediaSession) {
console.log('this.currentMediaSession is null');
return;
}
if (!mute) {
this.session.setReceiverVolumeLevel((vol || 1),
this.mediaCommandSuccessCallback.bind(this),
this.errorHandler);
}
else {
this.session.setReceiverMuted(true,
this.mediaCommandSuccessCallback.bind(this),
this.errorHandler);
}
};
/**
* Mute CC
*/
CastPlayer.prototype.mute = function () {
this.setReceiverVolume(true);
};
/**
* Callback function for media command success
*/
CastPlayer.prototype.mediaCommandSuccessCallback = function (info, e) {
console.log(info);
};
// Create Cast Player
var castPlayer;
function chromecastPlayer() {
var self = this;
// MediaController needs this
self.name = PlayerName;
self.getItemsForPlayback = function (query) {
var userId = Dashboard.getCurrentUserId();
if (query.Ids && query.Ids.split(',').length == 1) {
return ApiClient.getItem(userId, query.Ids.split(',')).then(function (item) {
return {
Items: [item],
TotalRecordCount: 1
};
});
}
else {
query.Limit = query.Limit || 100;
query.ExcludeLocationTypes = "Virtual";
return ApiClient.getItems(userId, query);
}
};
Events.on(castPlayer, "connect", function (e) {
if (currentResolve) {
sendConnectionResult(true);
} else {
MediaController.setActivePlayer(PlayerName, self.getCurrentTargetInfo());
}
console.log('cc: connect');
// Reset this so the next query doesn't make it appear like content is playing.
self.lastPlayerData = {};
});
Events.on(castPlayer, "playbackstart", function (e, data) {
console.log('cc: playbackstart');
castPlayer.initializeCastPlayer();
var state = self.getPlayerStateInternal(data);
Events.trigger(self, "playbackstart", [state]);
});
Events.on(castPlayer, "playbackstop", function (e, data) {
console.log('cc: playbackstop');
var state = self.getPlayerStateInternal(data);
Events.trigger(self, "playbackstop", [state]);
// Reset this so the next query doesn't make it appear like content is playing.
self.lastPlayerData = {};
});
Events.on(castPlayer, "playbackprogress", function (e, data) {
console.log('cc: positionchange');
var state = self.getPlayerStateInternal(data);
Events.trigger(self, "positionchange", [state]);
});
Events.on(castPlayer, "volumechange", function (e, data) {
console.log('cc: volumechange');
var state = self.getPlayerStateInternal(data);
Events.trigger(self, "volumechange", [state]);
});
Events.on(castPlayer, "playstatechange", function (e, data) {
console.log('cc: playstatechange');
var state = self.getPlayerStateInternal(data);
Events.trigger(self, "playstatechange", [state]);
});
self.play = function (options) {
return Dashboard.getCurrentUser().then(function (user) {
if (options.items) {
return self.playWithCommand(options, 'PlayNow');
} else {
return self.getItemsForPlayback({
Ids: options.ids.join(',')
}).then(function (result) {
options.items = result.Items;
return self.playWithCommand(options, 'PlayNow');
});
}
});
};
self.playWithCommand = function (options, command) {
if (!options.items) {
return ApiClient.getItem(Dashboard.getCurrentUserId(), options.ids[0]).then(function (item) {
options.items = [item];
return self.playWithCommand(options, command);
});
}
return castPlayer.loadMedia(options, command);
};
self.unpause = function () {
castPlayer.sendMessage({
options: {},
command: 'Unpause'
});
};
self.pause = function () {
castPlayer.sendMessage({
options: {},
command: 'Pause'
});
};
self.shuffle = function (id) {
var userId = Dashboard.getCurrentUserId();
ApiClient.getItem(userId, id).then(function (item) {
self.playWithCommand({
items: [item]
}, 'Shuffle');
});
};
self.instantMix = function (id) {
var userId = Dashboard.getCurrentUserId();
ApiClient.getItem(userId, id).then(function (item) {
self.playWithCommand({
items: [item]
}, 'InstantMix');
});
};
self.canQueueMediaType = function (mediaType) {
return mediaType == "Audio";
};
self.queue = function (options) {
self.playWithCommand(options, 'PlayLast');
};
self.queueNext = function (options) {
self.playWithCommand(options, 'PlayNext');
};
self.stop = function () {
castPlayer.sendMessage({
options: {},
command: 'Stop'
});
};
self.displayContent = function (options) {
castPlayer.sendMessage({
options: options,
command: 'DisplayContent'
});
};
self.mute = function () {
castPlayer.sendMessage({
options: {},
command: 'Mute'
});
//castPlayer.mute();
};
self.unMute = function () {
self.setVolume(getCurrentVolume() + 2);
};
self.setRepeatMode = function (mode) {
castPlayer.sendMessage({
options: {
RepeatMode: mode
},
command: 'SetRepeatMode'
});
};
self.toggleMute = function () {
var state = self.lastPlayerData || {};
state = state.PlayState || {};
if (state.IsMuted) {
self.unMute();
} else {
self.mute();
}
};
self.getTargets = function () {
var targets = [];
if (castPlayer.hasReceivers) {
targets.push(self.getCurrentTargetInfo());
}
return Promise.resolve(targets);
};
self.getCurrentTargetInfo = function () {
var appName = null;
if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName) {
appName = castPlayer.session.receiver.friendlyName;
}
return {
name: PlayerName,
id: PlayerName,
playerName: PlayerName,
playableMediaTypes: ["Audio", "Video"],
isLocalPlayer: false,
appName: PlayerName,
deviceName: appName,
supportedCommands: ["VolumeUp",
"VolumeDown",
"Mute",
"Unmute",
"ToggleMute",
"SetVolume",
"SetAudioStreamIndex",
"SetSubtitleStreamIndex",
"DisplayContent",
"SetRepeatMode",
"EndSession"]
};
};
self.seek = function (position) {
position = parseInt(position);
position = position / 10000000;
castPlayer.sendMessage({
options: {
position: position
},
command: 'Seek'
});
};
self.setAudioStreamIndex = function (index) {
castPlayer.sendMessage({
options: {
index: index
},
command: 'SetAudioStreamIndex'
});
};
self.setSubtitleStreamIndex = function (index) {
castPlayer.sendMessage({
options: {
index: index
},
command: 'SetSubtitleStreamIndex'
});
};
self.nextTrack = function () {
castPlayer.sendMessage({
options: {},
command: 'NextTrack'
});
};
self.previousTrack = function () {
castPlayer.sendMessage({
options: {},
command: 'PreviousTrack'
});
};
self.beginPlayerUpdates = function () {
// Setup polling here
};
self.endPlayerUpdates = function () {
// Stop polling here
};
function getCurrentVolume() {
var state = self.lastPlayerData || {};
state = state.PlayState || {};
return state.VolumeLevel == null ? 100 : state.VolumeLevel;
}
self.volumeDown = function () {
castPlayer.sendMessage({
options: {},
command: 'VolumeDown'
});
};
self.endSession = function () {
self.stop();
setTimeout(function () {
castPlayer.stopApp();
}, 1000);
};
self.volumeUp = function () {
castPlayer.sendMessage({
options: {},
command: 'VolumeUp'
});
};
self.setVolume = function (vol) {
vol = Math.min(vol, 100);
vol = Math.max(vol, 0);
//castPlayer.setReceiverVolume(false, (vol / 100));
castPlayer.sendMessage({
options: {
volume: vol
},
command: 'SetVolume'
});
};
self.getPlayerState = function () {
var result = self.getPlayerStateInternal();
return Promise.resolve(result);
};
self.lastPlayerData = {};
self.getPlayerStateInternal = function (data) {
data = data || self.lastPlayerData;
self.lastPlayerData = data;
console.log(JSON.stringify(data));
return data;
};
self.tryPair = function (target) {
if (castPlayer.deviceState != DEVICE_STATE.ACTIVE && castPlayer.isInitialized) {
return new Promise(function (resolve, reject) {
currentResolve = resolve;
currentReject = reject;
castPlayer.launchApp();
});
} else {
currentResolve = null;
currentReject = null;
return Promise.reject();
}
};
}
function initializeChromecast() {
castPlayer = new CastPlayer();
var registeredPlayer = new chromecastPlayer();
window.CCastPlayer = registeredPlayer;
MediaController.registerPlayer(registeredPlayer);
// To allow the native android app to override
document.dispatchEvent(new CustomEvent("chromecastloaded", {
detail: {
player: registeredPlayer
}
}));
}
var fileref = document.createElement('script');
fileref.setAttribute("type", "text/javascript");
fileref.onload = initializeChromecast;
fileref.setAttribute("src", "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js");
document.querySelector('head').appendChild(fileref);
});

View file

@ -1,866 +0,0 @@
define(['browser'], function (browser) {
'use strict';
var supportsTextTracks;
var hlsPlayer;
var requiresSettingStartTimeOnStart;
var subtitleTrackIndexToSetOnPlaying;
var currentTrackList;
var currentPlayOptions;
function htmlMediaRenderer(options) {
var mediaElement;
var self = this;
function onEnded() {
destroyCustomTrack(this);
Events.trigger(self, 'ended');
}
function onTimeUpdate() {
//if (isViblastStarted) {
// // This is a workaround for viblast not stopping playback at the end
// var time = this.currentTime;
// var duration = this.duration;
// if (duration) {
// if (time >= (duration - 1)) {
// //onEnded();
// return;
// }
// }
//}
if (options.type == 'video') {
// Get the player position + the transcoding offset
var timeMs = this.currentTime * 1000;
timeMs += ((currentPlayOptions.startTimeTicksOffset || 0) / 10000);
updateSubtitleText(timeMs);
}
Events.trigger(self, 'timeupdate');
}
function onVolumeChange() {
Events.trigger(self, 'volumechange');
}
function onOneAudioPlaying(e) {
var elem = e.target;
elem.removeEventListener('playing', onOneAudioPlaying);
document.querySelector('.mediaPlayerAudioContainer').classList.add('hide');
}
function onPlaying() {
Events.trigger(self, 'playing');
}
function onPlay() {
Events.trigger(self, 'play');
}
function onPause() {
Events.trigger(self, 'pause');
}
function onClick() {
Events.trigger(self, 'click');
}
function onDblClick() {
Events.trigger(self, 'dblclick');
}
function onError(e) {
destroyCustomTrack(this);
var elem = e.target;
var errorCode = elem.error ? elem.error.code : '';
console.log('Media element error code: ' + errorCode);
Events.trigger(self, 'error');
}
function onLoadedMetadata(e) {
var elem = e.target;
elem.removeEventListener('loadedmetadata', onLoadedMetadata);
if (!hlsPlayer) {
elem.play();
}
}
function requireHlsPlayer(callback) {
require(['hlsjs'], function (hls) {
window.Hls = hls;
callback();
});
}
function onOneVideoPlaying(e) {
var element = e.target;
element.removeEventListener('playing', onOneVideoPlaying);
self.setCurrentTrackElement(subtitleTrackIndexToSetOnPlaying);
var requiresNativeControls = !self.enableCustomVideoControls();
if (requiresNativeControls) {
element.setAttribute('controls', 'controls');
}
if (requiresSettingStartTimeOnStart) {
var startPositionInSeekParam = currentPlayOptions.startPositionInSeekParam;
// Appending #t=xxx to the query string doesn't seem to work with HLS
if (startPositionInSeekParam && currentSrc.indexOf('.m3u8') != -1) {
var delay = browser.safari ? 2500 : 0;
if (delay) {
setTimeout(function () {
element.currentTime = startPositionInSeekParam;
}, delay);
} else {
element.currentTime = startPositionInSeekParam;
}
}
}
}
function createAudioElement() {
var elem = document.querySelector('.mediaPlayerAudio');
if (!elem) {
var html = '';
var requiresControls = !MediaPlayer.canAutoPlayAudio();
if (requiresControls) {
html += '<div class="mediaPlayerAudioContainer" style="position: fixed;top: 40%;text-align: center;left: 0;right: 0;z-index:999999;"><div class="mediaPlayerAudioContainerInner">';;
} else {
html += '<div class="mediaPlayerAudioContainer hide" style="padding: 1em;background: #222;"><div class="mediaPlayerAudioContainerInner">';;
}
html += '<audio class="mediaPlayerAudio" controls>';
html += '</audio></div></div>';
document.body.insertAdjacentHTML('beforeend', html);
elem = document.querySelector('.mediaPlayerAudio');
}
elem.addEventListener('playing', onOneAudioPlaying);
elem.addEventListener('timeupdate', onTimeUpdate);
elem.addEventListener('ended', onEnded);
elem.addEventListener('volumechange', onVolumeChange);
elem.addEventListener('error', onError);
elem.addEventListener('pause', onPause);
elem.addEventListener('play', onPlay);
elem.addEventListener('playing', onPlaying);
return elem;
}
function enableHlsPlayer(src, item, mediaSource) {
if (src) {
if (src.indexOf('.m3u8') == -1) {
return false;
}
}
if (MediaPlayer.canPlayHls()) {
if (window.MediaSource == null) {
return false;
}
if (MediaPlayer.canPlayNativeHls()) {
// simple playback should use the native support
if (mediaSource.RunTimeTicks) {
return false;
}
//return false;
}
// hls.js is only in beta. needs more testing.
if (browser.safari && !browser.osx) {
return false;
}
return true;
}
return false;
}
function getCrossOriginValue(mediaSource) {
return 'anonymous';
}
function createVideoElement() {
var html = '';
var requiresNativeControls = !self.enableCustomVideoControls();
// Safari often displays the poster under the video and it doesn't look good
var poster = !browser.safari && options.poster ? (' poster="' + options.poster + '"') : '';
// playsinline new for iOS 10
// https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html
// Can't autoplay in these browsers so we need to use the full controls
if (requiresNativeControls && AppInfo.isNativeApp && browser.android) {
html += '<video class="itemVideo" id="itemVideo" preload="metadata" autoplay="autoplay"' + poster + ' webkit-playsinline playsinline>';
}
else if (requiresNativeControls) {
html += '<video class="itemVideo" id="itemVideo" preload="metadata" autoplay="autoplay"' + poster + ' controls="controls" webkit-playsinline playsinline>';
}
else {
// Chrome 35 won't play with preload none
html += '<video class="itemVideo" id="itemVideo" preload="metadata" autoplay="autoplay"' + poster + ' webkit-playsinline playsinline>';
}
html += '</video>';
var elem = document.querySelector('#videoPlayer #videoElement');
elem.insertAdjacentHTML('afterbegin', html);
var itemVideo = elem.querySelector('.itemVideo');
itemVideo.addEventListener('loadedmetadata', onLoadedMetadata);
itemVideo.addEventListener('timeupdate', onTimeUpdate);
itemVideo.addEventListener('ended', onEnded);
itemVideo.addEventListener('volumechange', onVolumeChange);
itemVideo.addEventListener('play', onPlay);
itemVideo.addEventListener('pause', onPause);
itemVideo.addEventListener('playing', onPlaying);
itemVideo.addEventListener('click', onClick);
itemVideo.addEventListener('dblclick', onDblClick);
itemVideo.addEventListener('error', onError);
return itemVideo;
}
// Save this for when playback stops, because querying the time at that point might return 0
var _currentTime;
self.currentTime = function (val) {
if (mediaElement) {
if (val != null) {
mediaElement.currentTime = val / 1000;
return;
}
if (_currentTime) {
return _currentTime * 1000;
}
return (mediaElement.currentTime || 0) * 1000;
}
};
self.duration = function (val) {
if (mediaElement) {
var duration = mediaElement.duration;
if (duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY) {
return duration * 1000;
}
}
return null;
};
self.stop = function () {
destroyCustomTrack(mediaElement);
if (mediaElement) {
mediaElement.pause();
if (hlsPlayer) {
_currentTime = mediaElement.currentTime;
// Sometimes this fails
try {
hlsPlayer.destroy();
}
catch (err) {
console.log(err);
}
hlsPlayer = null;
}
}
};
self.pause = function () {
if (mediaElement) {
mediaElement.pause();
}
};
self.unpause = function () {
if (mediaElement) {
mediaElement.play();
}
};
self.volume = function (val) {
if (mediaElement) {
if (val != null) {
mediaElement.volume = val;
return;
}
return mediaElement.volume;
}
};
var currentSrc;
self.setCurrentSrc = function (streamInfo, item, mediaSource, tracks) {
var elem = mediaElement;
if (!elem) {
currentSrc = null;
currentPlayOptions = null;
return;
}
currentPlayOptions = streamInfo;
if (!streamInfo) {
currentSrc = null;
elem.src = null;
elem.src = "";
// When the browser regains focus it may start auto-playing the last video
if (browser.safari) {
elem.src = 'files/dummy.mp4';
elem.play();
}
return;
}
elem.crossOrigin = getCrossOriginValue(mediaSource);
var val = streamInfo.url;
if (AppInfo.isNativeApp && browser.safari) {
val = val.replace('file://', '');
}
requiresSettingStartTimeOnStart = false;
var startTime = streamInfo.startPositionInSeekParam;
var playNow = false;
if (elem.tagName.toLowerCase() == 'audio') {
elem.src = val;
playNow = true;
}
else {
elem.removeEventListener('playing', onOneVideoPlaying);
elem.addEventListener('playing', onOneVideoPlaying);
if (hlsPlayer) {
hlsPlayer.destroy();
hlsPlayer = null;
}
if (startTime) {
//try {
// elem.currentTime = startTime;
//} catch (err) {
// // IE will throw an invalid state exception when trying to set currentTime before starting playback
//}
//requiresSettingStartTimeOnStart = elem.currentTime == 0;
requiresSettingStartTimeOnStart = true;
}
tracks = tracks || [];
currentTrackList = tracks;
var currentTrackIndex = -1;
for (var i = 0, length = tracks.length; i < length; i++) {
if (tracks[i].isDefault) {
currentTrackIndex = tracks[i].index;
break;
}
}
subtitleTrackIndexToSetOnPlaying = currentTrackIndex;
if (enableHlsPlayer(val, item, mediaSource)) {
setTracks(elem, tracks);
requireHlsPlayer(function () {
var hls = new Hls({
manifestLoadingTimeOut: 20000
});
hls.loadSource(val);
hls.attachMedia(elem);
hls.on(Hls.Events.MANIFEST_PARSED, function () {
elem.play();
});
hls.on(Hls.Events.ERROR, function (event, data) {
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
// try to recover network error
console.log("fatal network error encountered, try to recover");
hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.log("fatal media error encountered, try to recover");
hls.recoverMediaError();
break;
default:
// cannot recover
hls.destroy();
break;
}
}
});
hlsPlayer = hls;
});
} else {
elem.src = val;
elem.autoplay = true;
setTracks(elem, tracks);
elem.addEventListener("loadedmetadata", onLoadedMetadata);
playNow = true;
}
// This is needed in setCurrentTrackElement
currentSrc = val;
self.setCurrentTrackElement(currentTrackIndex);
}
currentSrc = val;
if (playNow) {
elem.play();
}
};
function setTracks(elem, tracks) {
var html = tracks.map(function (t) {
var defaultAttribute = t.isDefault ? ' default' : '';
var label = t.language || 'und';
return '<track id="textTrack' + t.index + '" label="' + label + '" kind="subtitles" src="' + t.url + '" srclang="' + t.language + '"' + defaultAttribute + '></track>';
}).join('');
elem.innerHTML = html;
}
self.currentSrc = function () {
if (mediaElement) {
// We have to use this cached value because viblast will muck with the url
return currentSrc;
//return mediaElement.currentSrc;
}
};
self.paused = function () {
if (mediaElement) {
return mediaElement.paused;
}
return false;
};
self.cleanup = function (destroyRenderer) {
self.setCurrentSrc(null);
_currentTime = null;
var elem = mediaElement;
if (elem) {
if (elem.tagName == 'AUDIO') {
elem.removeEventListener('timeupdate', onTimeUpdate);
elem.removeEventListener('ended', onEnded);
elem.removeEventListener('volumechange', onVolumeChange);
elem.removeEventListener('playing', onOneAudioPlaying);
elem.removeEventListener('play', onPlay);
elem.removeEventListener('pause', onPause);
elem.removeEventListener('playing', onPlaying);
elem.removeEventListener('error', onError);
} else {
elem.removeEventListener('loadedmetadata', onLoadedMetadata);
elem.removeEventListener('playing', onOneVideoPlaying);
elem.removeEventListener('timeupdate', onTimeUpdate);
elem.removeEventListener('ended', onEnded);
elem.removeEventListener('volumechange', onVolumeChange);
elem.removeEventListener('play', onPlay);
elem.removeEventListener('pause', onPause);
elem.removeEventListener('playing', onPlaying);
elem.removeEventListener('click', onClick);
elem.removeEventListener('dblclick', onDblClick);
elem.removeEventListener('error', onError);
}
if (elem.tagName.toLowerCase() != 'audio') {
if (elem.parentNode) {
elem.parentNode.removeChild(elem);
}
}
}
};
self.supportsTextTracks = function () {
if (supportsTextTracks == null) {
supportsTextTracks = document.createElement('video').textTracks != null;
}
// For now, until ready
return supportsTextTracks;
};
function enableNativeTrackSupport(track) {
if (track) {
var format = (track.format || '').toLowerCase();
if (format == 'ssa' || format == 'ass') {
// libjass is needed here
return false;
}
}
return true;
}
function destroyCustomTrack(videoElement, isPlaying) {
window.removeEventListener('resize', onVideoResize);
window.removeEventListener('orientationchange', onVideoResize);
var videoSubtitlesElem = document.querySelector('.videoSubtitles');
if (videoSubtitlesElem) {
videoSubtitlesElem.parentNode.removeChild(videoSubtitlesElem);
}
if (isPlaying) {
var allTracks = videoElement.textTracks; // get list of tracks
for (var i = 0; i < allTracks.length; i++) {
var currentTrack = allTracks[i];
if (currentTrack.label.indexOf('manualTrack') != -1) {
currentTrack.mode = 'disabled';
}
}
}
customTrackIndex = -1;
currentClock = null;
var renderer = currentAssRenderer;
if (renderer) {
renderer.setEnabled(false);
}
currentAssRenderer = null;
}
function setTrackForCustomDisplay(videoElement, track) {
if (!track) {
destroyCustomTrack(videoElement, true);
return;
}
// if already playing this track, skip
if (customTrackIndex == track.index) {
return;
}
destroyCustomTrack(videoElement, true);
customTrackIndex = track.index;
renderTracksEvents(videoElement, track);
}
function renderWithLibjass(videoElement, track) {
var rendererSettings = {};
require(['libjass'], function (libjass) {
libjass.ASS.fromUrl(track.url).then(function (ass) {
var clock = currentClock = new libjass.renderers.ManualClock();
// Create a DefaultRenderer using the video element and the ASS object
var renderer = new libjass.renderers.WebRenderer(ass, clock, videoElement.parentNode.parentNode, rendererSettings);
currentAssRenderer = renderer;
renderer.addEventListener("ready", function () {
try {
renderer.resize(videoElement.offsetWidth, videoElement.offsetHeight, 0, 0);
window.removeEventListener('resize', onVideoResize);
window.addEventListener('resize', onVideoResize);
window.removeEventListener('orientationchange', onVideoResize);
window.addEventListener('orientationchange', onVideoResize);
//clock.pause();
}
catch (ex) {
}
});
});
});
}
function onVideoResize() {
var renderer = currentAssRenderer;
if (renderer) {
var videoElement = mediaElement;
var width = videoElement.offsetWidth;
var height = videoElement.offsetHeight;
console.log('videoElement resized: ' + width + 'x' + height);
renderer.resize(width, height, 0, 0);
}
}
function renderTracksEvents(videoElement, track) {
var format = (track.format || '').toLowerCase();
if (format == 'ssa' || format == 'ass') {
// libjass is needed here
renderWithLibjass(videoElement, track);
return;
}
}
var customTrackIndex = -1;
var currentClock;
var currentAssRenderer;
function updateSubtitleText(timeMs) {
var clock = currentClock;
if (clock) {
clock.seek(timeMs / 1000);
}
}
self.setCurrentTrackElement = function (streamIndex) {
console.log('Setting new text track index to: ' + streamIndex);
var track = streamIndex == -1 ? null : currentTrackList.filter(function (t) {
return t.index == streamIndex;
})[0];
if (enableNativeTrackSupport(track)) {
setTrackForCustomDisplay(mediaElement, null);
} else {
setTrackForCustomDisplay(mediaElement, track);
// null these out to disable the player's native display (handled below)
streamIndex = -1;
track = null;
}
var expectedId = 'textTrack' + streamIndex;
var trackIndex = streamIndex == -1 || !track ? -1 : currentTrackList.indexOf(track);
var modes = ['disabled', 'showing', 'hidden'];
var allTracks = mediaElement.textTracks; // get list of tracks
for (var i = 0; i < allTracks.length; i++) {
var currentTrack = allTracks[i];
console.log('currentTrack id: ' + currentTrack.id);
console.log('currentTrack label: ' + currentTrack.label);
var mode;
console.log('expectedId: ' + expectedId + '--currentTrack.Id:' + currentTrack.id);
// IE doesn't support track id
if (browser.msie || browser.edge) {
if (trackIndex == i) {
mode = 1; // show this track
} else {
mode = 0; // hide all other tracks
}
} else {
if (currentTrack.label.indexOf('manualTrack') != -1) {
continue;
}
if (currentTrack.id == expectedId) {
mode = 1; // show this track
} else {
mode = 0; // hide all other tracks
}
}
console.log('Setting track ' + i + ' mode to: ' + mode);
// Safari uses integers for the mode property
// http://www.jwplayer.com/html5/scripting/
// edit: not anymore
var useNumericMode = false;
if (!isNaN(currentTrack.mode)) {
//useNumericMode = true;
}
if (useNumericMode) {
currentTrack.mode = mode;
} else {
currentTrack.mode = modes[mode];
}
}
};
function replaceQueryString(url, param, value) {
var re = new RegExp("([?|&])" + param + "=.*?(&|$)", "i");
if (url.match(re))
return url.replace(re, '$1' + param + "=" + value + '$2');
else if (value) {
if (url.indexOf('?') == -1) {
return url + '?' + param + "=" + value;
}
return url + '&' + param + "=" + value;
}
return url;
}
self.updateTextStreamUrls = function (startPositionTicks) {
if (!self.supportsTextTracks()) {
return;
}
var allTracks = mediaElement.textTracks; // get list of tracks
var i;
for (i = 0; i < allTracks.length; i++) {
var track = allTracks[i];
// This throws an error in IE, but is fine in chrome
// In IE it's not necessary anyway because changing the src seems to be enough
try {
while (track.cues.length) {
track.removeCue(track.cues[0]);
}
} catch (e) {
console.log('Error removing cue from textTrack');
}
}
var trackElements = mediaElement.querySelectorAll('track');
for (i = 0; i < trackElements.length; i++) {
var trackElement = trackElements[i];
trackElement.src = replaceQueryString(trackElement.src, 'startPositionTicks', startPositionTicks);
}
};
self.enableCustomVideoControls = function () {
if (AppInfo.isNativeApp && browser.safari) {
if (browser.ipad) {
// Need to disable it in order to support picture in picture
return false;
}
return true;
}
return self.canAutoPlayVideo();
};
self.canAutoPlayVideo = function () {
if (AppInfo.isNativeApp) {
return true;
}
if (browser.mobile) {
return false;
}
return true;
};
self.init = function () {
return Promise.resolve();
};
if (options.type == 'audio') {
mediaElement = createAudioElement();
}
else {
mediaElement = createVideoElement();
}
}
if (!window.AudioRenderer) {
window.AudioRenderer = function (options) {
options = options || {};
options.type = 'audio';
return new htmlMediaRenderer(options);
};
}
if (!window.VideoRenderer) {
window.VideoRenderer = function (options) {
options = options || {};
options.type = 'video';
return new htmlMediaRenderer(options);
};
}
});

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'emby-tabs', 'emby-button'], function (libraryBrowser) { define(['libraryBrowser', 'playbackManager', 'emby-tabs', 'emby-button'], function (libraryBrowser, playbackManager) {
'use strict'; 'use strict';
var defaultFirstSection = 'smalllibrarytiles'; var defaultFirstSection = 'smalllibrarytiles';
@ -348,12 +348,12 @@
} }
view.addEventListener('viewshow', function (e) { view.addEventListener('viewshow', function (e) {
Events.on(MediaController, 'playbackstop', onPlaybackStop); Events.on(playbackManager, 'playbackstop', onPlaybackStop);
Events.on(ApiClient, "websocketmessage", onWebSocketMessage); Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
}); });
view.addEventListener('viewbeforehide', function (e) { view.addEventListener('viewbeforehide', function (e) {
Events.off(MediaController, 'playbackstop', onPlaybackStop); Events.off(playbackManager, 'playbackstop', onPlaybackStop);
Events.off(ApiClient, "websocketmessage", onWebSocketMessage); Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
}); });

View file

@ -1,4 +1,4 @@
define(['layoutManager', 'cardBuilder', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'userdataButtons', 'dom', 'indicators', 'apphost', 'imageLoader', 'libraryMenu', 'shell', 'globalize', 'browser', 'events', 'scrollHelper', 'scrollStyles', 'emby-itemscontainer', 'emby-checkbox'], function (layoutManager, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, userdataButtons, dom, indicators, appHost, imageLoader, libraryMenu, shell, globalize, browser, events, scrollHelper) { define(['layoutManager', 'cardBuilder', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'userdataButtons', 'dom', 'indicators', 'apphost', 'imageLoader', 'libraryMenu', 'globalize', 'browser', 'events', 'scrollHelper', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-checkbox'], function (layoutManager, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, userdataButtons, dom, indicators, appHost, imageLoader, libraryMenu, globalize, browser, events, scrollHelper, playbackManager) {
'use strict'; 'use strict';
function getPromise(params) { function getPromise(params) {
@ -229,7 +229,7 @@
hideAll(page, 'btnPlay'); hideAll(page, 'btnPlay');
} }
} }
else if (MediaController.canPlay(item)) { else if (playbackManager.canPlay(item)) {
hideAll(page, 'btnPlay', true); hideAll(page, 'btnPlay', true);
canPlay = true; canPlay = true;
} }
@ -2146,7 +2146,7 @@
function play(startPosition) { function play(startPosition) {
MediaController.play({ playbackManager.play({
items: [currentItem], items: [currentItem],
startPositionTicks: startPosition startPositionTicks: startPosition
}); });
@ -2176,17 +2176,7 @@
function playTrailer(page) { function playTrailer(page) {
if (!currentItem.LocalTrailerCount) { playbackManager.playTrailers(currentItem);
shell.openUrl(currentItem.RemoteTrailers[0].Url);
return;
}
ApiClient.getLocalTrailers(Dashboard.getCurrentUserId(), currentItem.Id).then(function (trailers) {
MediaController.play({ items: trailers });
});
} }
function showPlayMenu(item, target) { function showPlayMenu(item, target) {

View file

@ -1,4 +1,4 @@
define(['layoutManager', 'viewManager', 'libraryBrowser', 'embyRouter', 'paper-icon-button-light', 'material-icons'], function (layoutManager, viewManager, libraryBrowser, embyRouter) { define(['layoutManager', 'viewManager', 'libraryBrowser', 'embyRouter', 'playbackManager', 'paper-icon-button-light', 'material-icons'], function (layoutManager, viewManager, libraryBrowser, embyRouter, playbackManager) {
'use strict'; 'use strict';
var enableBottomTabs = AppInfo.isNativeApp; var enableBottomTabs = AppInfo.isNativeApp;
@ -37,7 +37,7 @@
html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight headerUserButton autoSize"><i class="md-icon">person</i></button>'; html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight headerUserButton autoSize"><i class="md-icon">person</i></button>';
if (!browserInfo.mobile && !Dashboard.isConnectMode()) { if (!browserInfo.mobile) {
html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight dashboardEntryHeaderButton autoSize" onclick="return LibraryMenu.onSettingsClicked(event);"><i class="md-icon">settings</i></button>'; html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight dashboardEntryHeaderButton autoSize" onclick="return LibraryMenu.onSettingsClicked(event);"><i class="md-icon">settings</i></button>';
} }
@ -730,20 +730,19 @@
return; return;
} }
var info = MediaController.getPlayerInfo(); var info = playbackManager.getPlayerInfo();
if (info.isLocalPlayer) { if (info && !info.isLocalPlayer) {
btnCast.querySelector('i').innerHTML = 'cast';
btnCast.classList.remove('btnActiveCast');
context.querySelector('.headerSelectedPlayer').innerHTML = '';
} else {
btnCast.querySelector('i').icon = 'cast_connected'; btnCast.querySelector('i').icon = 'cast_connected';
btnCast.classList.add('btnActiveCast'); btnCast.classList.add('btnActiveCast');
context.querySelector('.headerSelectedPlayer').innerHTML = info.deviceName || info.name; context.querySelector('.headerSelectedPlayer').innerHTML = info.deviceName || info.name;
} else {
btnCast.querySelector('i').innerHTML = 'cast';
btnCast.classList.remove('btnActiveCast');
context.querySelector('.headerSelectedPlayer').innerHTML = '';
} }
} }
@ -1044,7 +1043,7 @@
}); });
Events.on(ConnectionManager, 'localusersignedout', updateUserInHeader); Events.on(ConnectionManager, 'localusersignedout', updateUserInHeader);
Events.on(MediaController, 'playerchange', updateCastIcon); Events.on(playbackManager, 'playerchange', updateCastIcon);
setDrawerClass(); setDrawerClass();

View file

@ -1,492 +1,10 @@
define(['appSettings', 'events', 'browser'], function (appSettings, events, browser) { define(['appSettings', 'events'], function (appSettings, events) {
'use strict'; 'use strict';
var datetime;
function monitorPlayer(player) {
events.on(player, 'playbackstart', function (e, state) {
var info = {
QueueableMediaTypes: state.NowPlayingItem.MediaType,
ItemId: state.NowPlayingItem.Id,
NowPlayingItem: state.NowPlayingItem
};
info = Object.assign(info, state.PlayState);
ApiClient.reportPlaybackStart(info);
});
events.on(player, 'playbackstop', function (e, state) {
var stopInfo = {
itemId: state.NowPlayingItem.Id,
mediaSourceId: state.PlayState.MediaSourceId,
positionTicks: state.PlayState.PositionTicks
};
if (state.PlayState.LiveStreamId) {
stopInfo.LiveStreamId = state.PlayState.LiveStreamId;
}
if (state.PlayState.PlaySessionId) {
stopInfo.PlaySessionId = state.PlayState.PlaySessionId;
}
ApiClient.reportPlaybackStopped(stopInfo);
});
}
function mediaController() { function mediaController() {
var self = this; var self = this;
var currentPlayer; var currentPlayer;
var currentTargetInfo;
var players = [];
self.registerPlayer = function (player) {
players.push(player);
if (player.isLocalPlayer) {
monitorPlayer(player);
}
events.on(player, 'playbackstop', onPlaybackStop);
events.on(player, 'beforeplaybackstart', onBeforePlaybackStart);
};
function onBeforePlaybackStart(e, state) {
events.trigger(self, 'beforeplaybackstart', [state, this]);
}
function onPlaybackStop(e, state) {
events.trigger(self, 'playbackstop', [state, this]);
}
self.getPlayerInfo = function () {
var player = currentPlayer || {};
var target = currentTargetInfo || {};
return {
name: player.name,
isLocalPlayer: player.isLocalPlayer,
id: target.id,
deviceName: target.deviceName,
playableMediaTypes: target.playableMediaTypes,
supportedCommands: target.supportedCommands
};
};
function triggerPlayerChange(newPlayer, newTarget, previousPlayer) {
events.trigger(self, 'playerchange', [newPlayer, newTarget, previousPlayer]);
}
self.setActivePlayer = function (player, targetInfo) {
if (typeof (player) === 'string') {
player = players.filter(function (p) {
return p.name == player;
})[0];
}
if (!player) {
throw new Error('null player');
}
var previousPlayer = currentPlayer;
currentPairingId = null;
currentPlayer = player;
currentTargetInfo = targetInfo;
console.log('Active player: ' + JSON.stringify(currentTargetInfo));
triggerPlayerChange(player, targetInfo, previousPlayer);
};
var currentPairingId = null;
self.trySetActivePlayer = function (player, targetInfo) {
if (typeof (player) === 'string') {
player = players.filter(function (p) {
return p.name == player;
})[0];
}
if (!player) {
throw new Error('null player');
}
if (currentPairingId == targetInfo.id) {
return;
}
currentPairingId = targetInfo.id;
player.tryPair(targetInfo).then(function () {
var previousPlayer = currentPlayer;
currentPlayer = player;
currentTargetInfo = targetInfo;
console.log('Active player: ' + JSON.stringify(currentTargetInfo));
triggerPlayerChange(player, targetInfo, previousPlayer);
}, function () {
if (currentPairingId == targetInfo.id) {
currentPairingId = null;
}
});
};
self.trySetActiveDeviceName = function (name) {
function normalizeName(t) {
return t.toLowerCase().replace(' ', '');
}
name = normalizeName(name);
self.getTargets().then(function (result) {
var target = result.filter(function (p) {
return normalizeName(p.name) == name;
})[0];
if (target) {
self.trySetActivePlayer(target.playerName, target);
}
});
};
self.setDefaultPlayerActive = function () {
var player = self.getDefaultPlayer();
player.getTargets().then(function (targets) {
self.setActivePlayer(player, targets[0]);
});
};
self.removeActivePlayer = function (name) {
if (self.getPlayerInfo().name == name) {
self.setDefaultPlayerActive();
}
};
self.removeActiveTarget = function (id) {
if (self.getPlayerInfo().id == id) {
self.setDefaultPlayerActive();
}
};
self.disconnectFromPlayer = function () {
var playerInfo = self.getPlayerInfo();
if (playerInfo.supportedCommands.indexOf('EndSession') != -1) {
require(['dialog'], function (dialog) {
var menuItems = [];
menuItems.push({
name: Globalize.translate('ButtonYes'),
id: 'yes'
});
menuItems.push({
name: Globalize.translate('ButtonNo'),
id: 'no'
});
dialog({
buttons: menuItems,
//positionTo: positionTo,
text: Globalize.translate('ConfirmEndPlayerSession')
}).then(function (id) {
switch (id) {
case 'yes':
MediaController.getCurrentPlayer().endSession();
self.setDefaultPlayerActive();
break;
case 'no':
self.setDefaultPlayerActive();
break;
default:
break;
}
});
});
} else {
self.setDefaultPlayerActive();
}
};
self.getPlayers = function () {
return players;
};
self.getTargets = function () {
var promises = players.map(function (p) {
return p.getTargets();
});
return Promise.all(promises).then(function (responses) {
var targets = [];
for (var i = 0; i < responses.length; i++) {
var subTargets = responses[i];
for (var j = 0; j < subTargets.length; j++) {
targets.push(subTargets[j]);
}
}
targets = targets.sort(function (a, b) {
var aVal = a.isLocalPlayer ? 0 : 1;
var bVal = b.isLocalPlayer ? 0 : 1;
aVal = aVal.toString() + a.name;
bVal = bVal.toString() + b.name;
return aVal.localeCompare(bVal);
});
return targets;
});
};
function validatePlayback(player) {
if (!player.isLocalPlayer) {
return Promise.resolve();
}
return new Promise(function (resolve, reject) {
requirejs(["registrationServices"], function (registrationServices) {
self.playbackTimeLimitMs = null;
registrationServices.validateFeature('playback').then(resolve, function () {
self.playbackTimeLimitMs = lockedTimeLimitMs;
startAutoStopTimer();
resolve();
});
});
});
}
var autoStopTimeout;
var lockedTimeLimitMs = 60000;
function startAutoStopTimer() {
stopAutoStopTimer();
autoStopTimeout = setTimeout(onAutoStopTimeout, lockedTimeLimitMs);
}
function onAutoStopTimeout() {
stopAutoStopTimer();
MediaController.stop();
}
function stopAutoStopTimer() {
var timeout = autoStopTimeout;
if (timeout) {
clearTimeout(timeout);
autoStopTimeout = null;
}
}
self.toggleDisplayMirroring = function () {
self.enableDisplayMirroring(!self.enableDisplayMirroring());
};
self.enableDisplayMirroring = function (enabled) {
if (enabled != null) {
var val = enabled ? '1' : '0';
appSettings.set('displaymirror--' + Dashboard.getCurrentUserId(), val);
if (enabled) {
mirrorIfEnabled();
}
return;
}
return (appSettings.get('displaymirror--' + Dashboard.getCurrentUserId()) || '') != '0';
};
self.play = function (options) {
if (options.enableRemotePlayers === false) {
if (!currentPlayer.isLocalPlayer) {
return Promise.reject();
}
}
return validatePlayback(currentPlayer).then(function () {
if (typeof (options) === 'string') {
options = { ids: [options] };
}
return currentPlayer.play(options);
});
};
self.shuffle = function (id) {
// accept both id and item being passed in
if (id.Id) {
id = id.Id;
}
validatePlayback(currentPlayer).then(function () {
currentPlayer.shuffle(id);
});
};
self.instantMix = function (id) {
// accept both id and item being passed in
if (id.Id) {
id = id.Id;
}
validatePlayback(currentPlayer).then(function () {
currentPlayer.instantMix(id);
});
};
self.queue = function (options) {
if (typeof (options) === 'string') {
options = { ids: [options] };
}
currentPlayer.queue(options);
};
self.queueNext = function (options) {
if (typeof (options) === 'string') {
options = { ids: [options] };
}
currentPlayer.queueNext(options);
};
self.canPlay = function (item) {
if (item.Type == "Program") {
if (new Date().getTime() > datetime.parseISO8601Date(item.EndDate).getTime() || new Date().getTime() < datetime.parseISO8601Date(item.StartDate).getTime()) {
return false;
}
return true;
}
return self.canPlayByAttributes(item.Type, item.MediaType, item.PlayAccess, item.LocationType);
};
self.canPlayByAttributes = function (itemType, mediaType, playAccess, locationType) {
if (playAccess != 'Full') {
return false;
}
if (locationType == "Virtual") {
return false;
}
if (itemType == "Program") {
return false;
}
if (itemType == "MusicGenre" || itemType == "Season" || itemType == "Series" || itemType == "BoxSet" || itemType == "MusicAlbum" || itemType == "MusicArtist" || itemType == "Playlist") {
return true;
}
return self.getPlayerInfo().playableMediaTypes.indexOf(mediaType) != -1;
};
self.canQueueMediaType = function (mediaType, itemType) {
if (itemType == 'MusicAlbum' || itemType == 'MusicArtist' || itemType == 'MusicGenre') {
mediaType = 'Audio';
}
return currentPlayer.canQueueMediaType(mediaType);
};
self.getLocalPlayer = function () {
return currentPlayer.isLocalPlayer ?
currentPlayer :
players.filter(function (p) {
return p.isLocalPlayer;
})[0];
};
self.getDefaultPlayer = function () {
return currentPlayer.isLocalPlayer ?
currentPlayer :
players.filter(function (p) {
return p.isDefaultPlayer;
})[0];
};
self.getCurrentPlayer = function () {
return currentPlayer;
};
self.pause = function () {
currentPlayer.pause();
};
self.stop = function () {
currentPlayer.stop();
};
self.unpause = function () {
currentPlayer.unpause();
};
self.seek = function (position) {
currentPlayer.seek(position);
};
self.currentPlaylistIndex = function (i) { self.currentPlaylistIndex = function (i) {
@ -502,378 +20,8 @@
currentPlayer.removeFromPlaylist(i); currentPlayer.removeFromPlaylist(i);
}; };
self.nextTrack = function () {
currentPlayer.nextTrack();
};
self.previousTrack = function () {
currentPlayer.previousTrack();
};
self.mute = function () {
currentPlayer.mute();
};
self.unMute = function () {
currentPlayer.unMute();
};
self.toggleMute = function () {
currentPlayer.toggleMute();
};
self.volumeDown = function () {
currentPlayer.volumeDown();
};
self.volumeUp = function () {
currentPlayer.volumeUp();
};
self.setRepeatMode = function (mode) {
currentPlayer.setRepeatMode(mode);
};
self.playlist = function () { self.playlist = function () {
return currentPlayer.playlist || []; return currentPlayer.playlist || [];
}; };
self.sendCommand = function (cmd, player) {
player = player || self.getLocalPlayer();
// Full list
// https://github.com/MediaBrowser/MediaBrowser/blob/master/MediaBrowser.Model/Session/GeneralCommand.cs#L23
console.log('MediaController received command: ' + cmd.Name);
switch (cmd.Name) {
case 'SetRepeatMode':
player.setRepeatMode(cmd.Arguments.RepeatMode);
break;
case 'VolumeUp':
player.volumeUp();
break;
case 'VolumeDown':
player.volumeDown();
break;
case 'Mute':
player.mute();
break;
case 'Unmute':
player.unMute();
break;
case 'ToggleMute':
player.toggleMute();
break;
case 'SetVolume':
player.setVolume(cmd.Arguments.Volume);
break;
case 'SetAudioStreamIndex':
player.setAudioStreamIndex(parseInt(cmd.Arguments.Index));
break;
case 'SetSubtitleStreamIndex':
player.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index));
break;
case 'ToggleFullscreen':
player.toggleFullscreen();
break;
default:
{
if (!player.isLocalPlayer) {
player.sendCommand(cmd);
} }
break;
}
}
};
// TOOD: This doesn't really belong here
self.getNowPlayingNames = function (nowPlayingItem, includeNonNameInfo) {
var topItem = nowPlayingItem;
var bottomItem = null;
var topText = nowPlayingItem.Name;
if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType == 'Audio') {
topItem = {
Id: nowPlayingItem.AlbumId,
Name: nowPlayingItem.Album,
Type: 'MusicAlbum',
IsFolder: true
};
}
if (nowPlayingItem.MediaType == 'Video') {
if (nowPlayingItem.IndexNumber != null) {
topText = nowPlayingItem.IndexNumber + " - " + topText;
}
if (nowPlayingItem.ParentIndexNumber != null) {
topText = nowPlayingItem.ParentIndexNumber + "." + topText;
}
}
var bottomText = '';
if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) {
bottomItem = {
Id: nowPlayingItem.ArtistItems[0].Id,
Name: nowPlayingItem.ArtistItems[0].Name,
Type: 'MusicArtist',
IsFolder: true
};
bottomText = bottomItem.Name;
} else {
bottomText = nowPlayingItem.Artists[0];
}
}
else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
bottomText = topText;
topText = nowPlayingItem.SeriesName || nowPlayingItem.Album;
bottomItem = topItem;
if (nowPlayingItem.SeriesId) {
topItem = {
Id: nowPlayingItem.SeriesId,
Name: nowPlayingItem.SeriesName,
Type: 'Series',
IsFolder: true
};
} else {
topItem = null;
}
}
else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) {
bottomText = nowPlayingItem.ProductionYear;
}
var list = [];
list.push({
text: topText,
item: topItem
});
if (bottomText) {
list.push({
text: bottomText,
item: bottomItem
});
}
return list;
};
// TOOD: This doesn't really belong here
self.getNowPlayingNameHtml = function (nowPlayingItem, includeNonNameInfo) {
var names = self.getNowPlayingNames(nowPlayingItem, includeNonNameInfo);
return names.map(function (i) {
return i.text;
}).join('<br/>');
};
self.showPlaybackInfoErrorMessage = function (errorCode) {
require(['alert'], function (alert) {
alert({
title: Globalize.translate('HeaderPlaybackError'),
text: Globalize.translate('MessagePlaybackError' + errorCode),
type: 'error'
});
});
};
function getPlaybackInfoFromLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource) {
mediaSource.SupportsDirectPlay = true;
return {
MediaSources: [mediaSource],
// Just dummy this up
PlaySessionId: new Date().getTime().toString()
};
}
self.getPlaybackInfo = function (itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId) {
return new Promise(function (resolve, reject) {
require([], function () {
//require(['localassetmanager'], function (LocalAssetManager) {
var serverInfo = ApiClient.serverInfo();
//if (serverInfo.Id) {
// LocalAssetManager.getLocalMediaSource(serverInfo.Id, itemId).then(function (localMediaSource) {
// // Use the local media source if a specific one wasn't requested, or the smae one was requested
// if (localMediaSource && (!mediaSource || mediaSource.Id == localMediaSource.Id)) {
// var playbackInfo = getPlaybackInfoFromLocalMediaSource(itemId, deviceProfile, startPosition, localMediaSource);
// resolve(playbackInfo);
// return;
// }
// getPlaybackInfoWithoutLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId, resolve, reject);
// });
// return;
//}
getPlaybackInfoWithoutLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId, resolve, reject);
});
});
}
function getPlaybackInfoWithoutLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId, resolve, reject) {
self.getPlaybackInfoInternal(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId).then(resolve, reject);
}
self.getPlaybackInfoInternal = function (itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId) {
var postData = {
DeviceProfile: deviceProfile
};
var query = {
UserId: Dashboard.getCurrentUserId(),
StartTimeTicks: startPosition || 0
};
if (audioStreamIndex != null) {
query.AudioStreamIndex = audioStreamIndex;
}
if (subtitleStreamIndex != null) {
query.SubtitleStreamIndex = subtitleStreamIndex;
}
if (mediaSource) {
query.MediaSourceId = mediaSource.Id;
}
if (liveStreamId) {
query.LiveStreamId = liveStreamId;
}
return ApiClient.ajax({
url: ApiClient.getUrl('Items/' + itemId + '/PlaybackInfo', query),
type: 'POST',
data: JSON.stringify(postData),
contentType: "application/json",
dataType: "json"
});
}
self.getLiveStream = function (itemId, playSessionId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex) {
var postData = {
DeviceProfile: deviceProfile,
OpenToken: mediaSource.OpenToken
};
var query = {
UserId: Dashboard.getCurrentUserId(),
StartTimeTicks: startPosition || 0,
ItemId: itemId,
PlaySessionId: playSessionId
};
if (audioStreamIndex != null) {
query.AudioStreamIndex = audioStreamIndex;
}
if (subtitleStreamIndex != null) {
query.SubtitleStreamIndex = subtitleStreamIndex;
}
return ApiClient.ajax({
url: ApiClient.getUrl('LiveStreams/Open', query),
type: 'POST',
data: JSON.stringify(postData),
contentType: "application/json",
dataType: "json"
});
};
self.supportsDirectPlay = function (mediaSource, itemType) {
return new Promise(function (resolve, reject) {
if (mediaSource.SupportsDirectPlay) {
if (mediaSource.Protocol == 'Http' && !mediaSource.RequiredHttpHeaders.length) {
// If this is the only way it can be played, then allow it
if (!mediaSource.SupportsDirectStream && !mediaSource.SupportsTranscoding) {
resolve(true);
}
else {
var val = mediaSource.Path.toLowerCase().replace('https:', 'http').indexOf(ApiClient.serverAddress().toLowerCase().replace('https:', 'http').substring(0, 14)) == 0;
//resolve(val || itemType !== 'TvChannel');
resolve(val);
}
}
if (mediaSource.Protocol == 'File') {
require(['filesystem'], function (fileSystem) {
fileSystem.fileExists(mediaSource.Path).then(function () {
console.log('fileSystem.fileExists: path: ' + mediaSource.Path + ' result: ' + true);
resolve(true);
}, function () {
console.log('fileSystem.fileExists: path: ' + mediaSource.Path + ' result: ' + false);
resolve(false);
});
});
}
}
else {
resolve(false);
}
});
};
}
window.MediaController = new mediaController();
function onWebSocketMessageReceived(e, msg) {
if (msg.MessageType === "ServerShuttingDown") {
MediaController.setDefaultPlayerActive();
}
else if (msg.MessageType === "ServerRestarting") {
MediaController.setDefaultPlayerActive();
}
}
function initializeApiClient(apiClient) {
events.off(apiClient, "websocketmessage", onWebSocketMessageReceived);
events.on(apiClient, "websocketmessage", onWebSocketMessageReceived);
}
MediaController.init = function () {
console.log('Beginning MediaController.init');
require(['datetime'], function (datetimeInstance) {
datetime = datetimeInstance;
});
if (window.ApiClient) {
initializeApiClient(window.ApiClient);
}
events.on(ConnectionManager, 'apiclientcreated', function (e, apiClient) {
initializeApiClient(apiClient);
});
};
}); });

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'dom', 'apphost', 'imageLoader', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, categorysyncbuttons, cardBuilder, dom, appHost, imageLoader) { define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'dom', 'apphost', 'imageLoader', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, categorysyncbuttons, cardBuilder, dom, appHost, imageLoader, playbackManager) {
'use strict'; 'use strict';
function enableScrollX() { function enableScrollX() {
@ -332,11 +332,11 @@
} }
view.addEventListener('viewshow', function (e) { view.addEventListener('viewshow', function (e) {
Events.on(MediaController, 'playbackstop', onPlaybackStop); Events.on(playbackManager, 'playbackstop', onPlaybackStop);
}); });
view.addEventListener('viewbeforehide', function (e) { view.addEventListener('viewbeforehide', function (e) {
Events.off(MediaController, 'playbackstop', onPlaybackStop); Events.off(playbackManager, 'playbackstop', onPlaybackStop);
}); });
require(["headroom-window"], function (headroom) { require(["headroom-window"], function (headroom) {

View file

@ -44,7 +44,7 @@
} }
}); });
if (Dashboard.isConnectMode()) { if (appHost.supports('multiserver')) {
page.querySelector('.selectServer').classList.remove('hide'); page.querySelector('.selectServer').classList.remove('hide');
} else { } else {
page.querySelector('.selectServer').classList.add('hide'); page.querySelector('.selectServer').classList.add('hide');

View file

@ -1,7 +1,8 @@
define(['datetime', 'userdataButtons', 'itemHelper', 'events', 'browser', 'imageLoader', 'paper-icon-button-light'], function (datetime, userdataButtons, itemHelper, events, browser, imageLoader) { define(['datetime', 'userdataButtons', 'itemHelper', 'events', 'browser', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'apphost', 'dom', 'paper-icon-button-light'], function (datetime, userdataButtons, itemHelper, events, browser, imageLoader, playbackManager, nowPlayingHelper, appHost, dom) {
'use strict'; 'use strict';
var currentPlayer; var currentPlayer;
var currentPlayerSupportedCommands = [];
var currentTimeElement; var currentTimeElement;
var nowPlayingImageElement; var nowPlayingImageElement;
@ -17,13 +18,16 @@
var toggleRepeatButton; var toggleRepeatButton;
var toggleRepeatButtonIcon; var toggleRepeatButtonIcon;
var lastPlayerState; var lastUpdateTime = 0;
var lastPlayerState = {};
var isEnabled;
var currentRuntimeTicks = 0;
function getNowPlayingBarHtml() { function getNowPlayingBarHtml() {
var html = ''; var html = '';
html += '<div class="nowPlayingBar hide">'; html += '<div class="nowPlayingBar hide nowPlayingBar-hidden">';
html += '<div class="nowPlayingBarTop">'; html += '<div class="nowPlayingBarTop">';
html += '<div class="nowPlayingBarPositionContainer sliderContainer">'; html += '<div class="nowPlayingBarPositionContainer sliderContainer">';
@ -76,49 +80,39 @@
return html; return html;
} }
function onSlideDownComplete() {
this.classList.add('hide');
}
function slideDown(elem) { function slideDown(elem) {
if (elem.classList.contains('hide')) {
return;
}
var onfinish = function () {
elem.classList.add('hide');
};
if (!browser.animate || browser.slow) {
onfinish();
return;
}
requestAnimationFrame(function () { requestAnimationFrame(function () {
var keyframes = [
{ transform: 'none', offset: 0 }, // trigger reflow
{ transform: 'translateY(100%)', offset: 1 }]; void elem.offsetWidth;
var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' };
elem.animate(keyframes, timing).onfinish = onfinish; elem.classList.add('nowPlayingBar-hidden');
dom.addEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, {
once: true
});
}); });
} }
function slideUp(elem) { function slideUp(elem) {
if (!elem.classList.contains('hide')) {
return;
}
elem.classList.remove('hide'); elem.classList.remove('hide');
if (!browser.animate || browser.slow) { dom.removeEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, {
return; once: true
} });
requestAnimationFrame(function () { requestAnimationFrame(function () {
var keyframes = [ // trigger reflow
{ transform: 'translateY(100%)', offset: 0 }, void elem.offsetWidth;
{ transform: 'none', offset: 1 }];
var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' }; elem.classList.remove('nowPlayingBar-hidden');
elem.animate(keyframes, timing);
}); });
} }
@ -167,7 +161,7 @@
unmuteButton.addEventListener('click', function () { unmuteButton.addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.unMute(); currentPlayer.setMute(false);
} }
}); });
@ -176,7 +170,7 @@
muteButton.addEventListener('click', function () { muteButton.addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.mute(); currentPlayer.setMute(true);
} }
}); });
@ -184,7 +178,7 @@
elem.querySelector('.stopButton').addEventListener('click', function () { elem.querySelector('.stopButton').addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.stop(); playbackManager.stop(currentPlayer);
} }
}); });
@ -201,14 +195,14 @@
elem.querySelector('.nextTrackButton').addEventListener('click', function () { elem.querySelector('.nextTrackButton').addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.nextTrack(); playbackManager.nextTrack(currentPlayer);
} }
}); });
elem.querySelector('.previousTrackButton').addEventListener('click', function () { elem.querySelector('.previousTrackButton').addEventListener('click', function () {
if (currentPlayer) { if (currentPlayer) {
currentPlayer.previousTrack(); playbackManager.previousTrack(currentPlayer);
} }
}); });
@ -230,13 +224,13 @@
switch ((state.PlayState || {}).RepeatMode) { switch ((state.PlayState || {}).RepeatMode) {
case 'RepeatAll': case 'RepeatAll':
currentPlayer.setRepeatMode('RepeatOne'); playbackManager.setRepeatMode('RepeatOne', currentPlayer);
break; break;
case 'RepeatOne': case 'RepeatOne':
currentPlayer.setRepeatMode('RepeatNone'); playbackManager.setRepeatMode('RepeatNone', currentPlayer);
break; break;
default: default:
currentPlayer.setRepeatMode('RepeatAll'); playbackManager.setRepeatMode('RepeatAll', currentPlayer);
break; break;
} }
} }
@ -247,7 +241,7 @@
volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider'); volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider');
volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer'); volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer');
if (AppInfo.hasPhysicalVolumeButtons) { if (appHost.supports('physicalvolumecontrol')) {
volumeSliderContainer.classList.add('hide'); volumeSliderContainer.classList.add('hide');
} else { } else {
volumeSliderContainer.classList.remove('hide'); volumeSliderContainer.classList.remove('hide');
@ -264,12 +258,11 @@
positionSlider = elem.querySelector('.nowPlayingBarPositionSlider'); positionSlider = elem.querySelector('.nowPlayingBarPositionSlider');
positionSlider.addEventListener('change', function () { positionSlider.addEventListener('change', function () {
if (currentPlayer && lastPlayerState) { if (currentPlayer) {
var newPercent = parseFloat(this.value); var newPercent = parseFloat(this.value);
var newPositionTicks = (newPercent / 100) * lastPlayerState.NowPlayingItem.RunTimeTicks;
currentPlayer.seek(Math.floor(newPositionTicks)); playbackManager.seekPercent(newPercent, currentPlayer);
} }
}); });
@ -278,11 +271,11 @@
var state = lastPlayerState; var state = lastPlayerState;
if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) { if (!state || !state.NowPlayingItem || !currentRuntimeTicks) {
return '--:--'; return '--:--';
} }
var ticks = state.NowPlayingItem.RunTimeTicks; var ticks = currentRuntimeTicks;
ticks /= 100; ticks /= 100;
ticks *= value; ticks *= value;
@ -342,47 +335,11 @@
button.classList.add('hide'); button.classList.add('hide');
} }
var lastUpdateTime = 0; function updatePlayPauseState(isPaused) {
function updatePlayerState(event, state) {
if (!state.NowPlayingItem) {
hideNowPlayingBar();
return;
}
if (nowPlayingBarElement) {
updatePlayerStateInternal(event, state);
return;
}
getNowPlayingBar().then(function () {
updatePlayerStateInternal(event, state);
});
}
function updatePlayerStateInternal(event, state) {
showNowPlayingBar();
if (event.type == 'positionchange') {
// Try to avoid hammering the document with changes
var now = new Date().getTime();
if ((now - lastUpdateTime) < 700) {
return;
}
lastUpdateTime = now;
}
lastPlayerState = state;
var playerInfo = MediaController.getPlayerInfo();
var playState = state.PlayState || {};
var i, length; var i, length;
if (playState.IsPaused) { if (isPaused) {
for (i = 0, length = pauseButtons.length; i < length; i++) { for (i = 0, length = pauseButtons.length; i < length; i++) {
hideButton(pauseButtons[i]); hideButton(pauseButtons[i]);
@ -400,70 +357,22 @@
hideButton(unpauseButtons[i]); hideButton(unpauseButtons[i]);
} }
} }
updatePlayerVolumeState(state, playerInfo);
var nowPlayingItem = state.NowPlayingItem || {};
// See bindEvents for why this is necessary
if (positionSlider) {
if (!positionSlider.dragging) {
if (nowPlayingItem.RunTimeTicks) {
var pct = playState.PositionTicks / nowPlayingItem.RunTimeTicks;
pct *= 100;
positionSlider.value = pct;
} else {
positionSlider.value = 0;
} }
positionSlider.disabled = !playState.CanSeek; function updatePlayerStateInternal(event, state) {
}
}
var timeText = playState.PositionTicks == null ? '--:--' : datetime.getDisplayRunningTime(playState.PositionTicks); showNowPlayingBar();
if (nowPlayingItem.RunTimeTicks) { lastPlayerState = state;
timeText += " / " + datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks); var playerInfo = playbackManager.getPlayerInfo();
}
currentTimeElement.innerHTML = timeText;
updateNowPlayingInfo(state);
}
function updatePlayerVolumeState(state, playerInfo) {
playerInfo = playerInfo || MediaController.getPlayerInfo();
var playState = state.PlayState || {}; var playState = state.PlayState || {};
updatePlayPauseState(playState.IsPaused);
var supportedCommands = playerInfo.supportedCommands; var supportedCommands = playerInfo.supportedCommands;
currentPlayerSupportedCommands = supportedCommands;
var showMuteButton = true;
var showUnmuteButton = true;
var showVolumeSlider = true;
if (supportedCommands.indexOf('Mute') == -1) {
showMuteButton = false;
}
if (supportedCommands.indexOf('Unmute') == -1) {
showUnmuteButton = false;
}
if (playState.IsMuted) {
showMuteButton = false;
} else {
showUnmuteButton = false;
}
if (supportedCommands.indexOf('SetRepeatMode') == -1) { if (supportedCommands.indexOf('SetRepeatMode') == -1) {
toggleRepeatButton.classList.add('hide'); toggleRepeatButton.classList.add('hide');
@ -483,11 +392,73 @@
toggleRepeatButton.classList.remove('repeatActive'); toggleRepeatButton.classList.remove('repeatActive');
} }
updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel);
if (positionSlider && !positionSlider.dragging) {
positionSlider.disabled = !playState.CanSeek;
}
var nowPlayingItem = state.NowPlayingItem || {};
updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks);
updateNowPlayingInfo(state);
}
function updateTimeDisplay(positionTicks, runtimeTicks) {
// See bindEvents for why this is necessary
if (positionSlider && !positionSlider.dragging) {
if (runtimeTicks) {
var pct = positionTicks / runtimeTicks;
pct *= 100;
positionSlider.value = pct;
} else {
positionSlider.value = 0;
}
}
var timeText = positionTicks == null ? '--:--' : datetime.getDisplayRunningTime(positionTicks);
if (runtimeTicks) {
timeText += " / " + datetime.getDisplayRunningTime(runtimeTicks);
}
currentTimeElement.innerHTML = timeText;
}
function updatePlayerVolumeState(isMuted, volumeLevel) {
var supportedCommands = currentPlayerSupportedCommands;
var showMuteButton = true;
var showUnmuteButton = true;
var showVolumeSlider = true;
if (supportedCommands.indexOf('Mute') == -1) {
showMuteButton = false;
}
if (supportedCommands.indexOf('Unmute') == -1) {
showUnmuteButton = false;
}
if (isMuted) {
showMuteButton = false;
} else {
showUnmuteButton = false;
}
if (supportedCommands.indexOf('SetVolume') == -1) { if (supportedCommands.indexOf('SetVolume') == -1) {
showVolumeSlider = false; showVolumeSlider = false;
} }
if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) { if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) {
showMuteButton = false; showMuteButton = false;
showUnmuteButton = false; showUnmuteButton = false;
showVolumeSlider = false; showVolumeSlider = false;
@ -515,7 +486,7 @@
} }
if (!volumeSlider.dragging) { if (!volumeSlider.dragging) {
volumeSlider.value = playState.VolumeLevel || 0; volumeSlider.value = volumeLevel || 0;
} }
} }
} }
@ -536,7 +507,7 @@
var currentImgUrl; var currentImgUrl;
function updateNowPlayingInfo(state) { function updateNowPlayingInfo(state) {
nowPlayingTextElement.innerHTML = MediaController.getNowPlayingNames(state.NowPlayingItem).map(function (nowPlayingName) { nowPlayingTextElement.innerHTML = nowPlayingHelper.getNowPlayingNames(state.NowPlayingItem).map(function (nowPlayingName) {
if (nowPlayingName.item) { if (nowPlayingName.item) {
return '<div>' + getTextActionButton(nowPlayingName.item, nowPlayingName.text) + '</div>'; return '<div>' + getTextActionButton(nowPlayingName.item, nowPlayingName.text) + '</div>';
@ -616,8 +587,6 @@
var player = this; var player = this;
player.beginPlayerUpdates();
onStateChanged.call(player, e, state); onStateChanged.call(player, e, state);
} }
@ -628,6 +597,8 @@
function hideNowPlayingBar() { function hideNowPlayingBar() {
isEnabled = false;
// Use a timeout to prevent the bar from hiding and showing quickly // Use a timeout to prevent the bar from hiding and showing quickly
// in the event of a stop->play command // in the event of a stop->play command
@ -643,83 +614,130 @@
console.log('nowplaying event: ' + e.type); console.log('nowplaying event: ' + e.type);
var player = this; var player = this;
player.endPlayerUpdates(); playbackManager.endPlayerUpdates(player);
hideNowPlayingBar(); hideNowPlayingBar();
} }
function onStateChanged(e, state) { function onPlayPauseStateChanged(e) {
if (!isEnabled) {
return;
}
var player = this;
updatePlayPauseState(player.paused());
}
function onStateChanged(event, state) {
//console.log('nowplaying event: ' + e.type); //console.log('nowplaying event: ' + e.type);
var player = this; var player = this;
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { if (!state.NowPlayingItem) {
hideNowPlayingBar();
return; return;
} }
updatePlayerState(e, state); if (player.isLocalPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
hideNowPlayingBar();
return;
}
isEnabled = true;
playbackManager.beginPlayerUpdates(player);
if (nowPlayingBarElement) {
updatePlayerStateInternal(event, state);
return;
}
getNowPlayingBar().then(function () {
updatePlayerStateInternal(event, state);
});
}
function onTimeUpdate(e) {
if (!isEnabled) {
return;
}
// Try to avoid hammering the document with changes
var now = new Date().getTime();
if ((now - lastUpdateTime) < 700) {
return;
}
lastUpdateTime = now;
var player = this;
var state = lastPlayerState;
var nowPlayingItem = state.NowPlayingItem || {};
currentRuntimeTicks = playbackManager.duration(player);
updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks);
} }
function releaseCurrentPlayer() { function releaseCurrentPlayer() {
if (currentPlayer) { var player = currentPlayer;
events.off(currentPlayer, 'playbackstart', onPlaybackStart); if (player) {
events.off(currentPlayer, 'playbackstop', onPlaybackStopped); events.off(player, 'playbackstart', onPlaybackStart);
events.off(currentPlayer, 'volumechange', onVolumeChanged); events.off(player, 'playbackstop', onPlaybackStopped);
events.off(currentPlayer, 'playstatechange', onStateChanged); events.off(player, 'volumechange', onVolumeChanged);
events.off(currentPlayer, 'positionchange', onStateChanged); events.off(player, 'pause', onPlayPauseStateChanged);
events.off(player, 'playing', onPlayPauseStateChanged);
events.off(player, 'timeupdate', onTimeUpdate);
currentPlayer.endPlayerUpdates(); playbackManager.endPlayerUpdates(player);
currentPlayer = null; currentPlayer = null;
hideNowPlayingBar(); hideNowPlayingBar();
} }
} }
function onVolumeChanged(e) { function onVolumeChanged(e) {
var player = this; if (!isEnabled) {
Promise.all([player.getPlayerState(), getNowPlayingBar()]).then(function (responses) {
var state = responses[0];
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
return; return;
} }
updatePlayerVolumeState(state); var player = this;
});
updatePlayerVolumeState(player.isMuted(), player.getVolume());
} }
function bindToPlayer(player) { function bindToPlayer(player) {
if (player === currentPlayer) {
return;
}
releaseCurrentPlayer(); releaseCurrentPlayer();
currentPlayer = player; currentPlayer = player;
player.getPlayerState().then(function (state) { if (!player) {
return;
if (state.NowPlayingItem) {
player.beginPlayerUpdates();
} }
playbackManager.getPlayerState(player).then(function (state) {
onStateChanged.call(player, { type: 'init' }, state); onStateChanged.call(player, { type: 'init' }, state);
}); });
events.on(player, 'playbackstart', onPlaybackStart); events.on(player, 'playbackstart', onPlaybackStart);
events.on(player, 'playbackstop', onPlaybackStopped); events.on(player, 'playbackstop', onPlaybackStopped);
events.on(player, 'volumechange', onVolumeChanged); events.on(player, 'volumechange', onVolumeChanged);
events.on(player, 'playstatechange', onStateChanged); events.on(player, 'pause', onPlayPauseStateChanged);
events.on(player, 'positionchange', onStateChanged); events.on(player, 'playing', onPlayPauseStateChanged);
events.on(player, 'timeupdate', onTimeUpdate);
} }
events.on(MediaController, 'playerchange', function () { events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer());
bindToPlayer(MediaController.getCurrentPlayer());
}); });
bindToPlayer(MediaController.getCurrentPlayer()); bindToPlayer(playbackManager.getCurrentPlayer());
}); });

View file

@ -1,400 +0,0 @@
define([], function () {
'use strict';
function sendPlayCommand(options, playType) {
var sessionId = MediaController.getPlayerInfo().id;
var ids = options.ids || options.items.map(function (i) {
return i.Id;
});
var remoteOptions = {
ItemIds: ids.join(','),
PlayCommand: playType
};
if (options.startPositionTicks) {
remoteOptions.startPositionTicks = options.startPositionTicks;
}
return ApiClient.sendPlayCommand(sessionId, remoteOptions);
}
function sendPlayStateCommand(command, options) {
var sessionId = MediaController.getPlayerInfo().id;
ApiClient.sendPlayStateCommand(sessionId, command, options);
}
function remoteControlPlayer() {
var self = this;
self.name = 'Remote Control';
function sendCommandByName(name, options) {
var command = {
Name: name
};
if (options) {
command.Arguments = options;
}
self.sendCommand(command);
}
self.sendCommand = function (command) {
var sessionId = MediaController.getPlayerInfo().id;
ApiClient.sendCommand(sessionId, command);
};
self.play = function (options) {
return sendPlayCommand(options, 'PlayNow');
};
self.shuffle = function (id) {
sendPlayCommand({ ids: [id] }, 'PlayShuffle');
};
self.instantMix = function (id) {
sendPlayCommand({ ids: [id] }, 'PlayInstantMix');
};
self.queue = function (options) {
sendPlayCommand(options, 'PlayNext');
};
self.queueNext = function (options) {
sendPlayCommand(options, 'PlayLast');
};
self.canQueueMediaType = function (mediaType) {
return mediaType == 'Audio' || mediaType == 'Video';
};
self.stop = function () {
sendPlayStateCommand('stop');
};
self.nextTrack = function () {
sendPlayStateCommand('nextTrack');
};
self.previousTrack = function () {
sendPlayStateCommand('previousTrack');
};
self.seek = function (positionTicks) {
sendPlayStateCommand('seek',
{
SeekPositionTicks: positionTicks
});
};
self.pause = function () {
sendPlayStateCommand('Pause');
};
self.unpause = function () {
sendPlayStateCommand('Unpause');
};
self.mute = function () {
sendCommandByName('Mute');
};
self.unMute = function () {
sendCommandByName('Unmute');
};
self.toggleMute = function () {
sendCommandByName('ToggleMute');
};
self.setVolume = function (vol) {
sendCommandByName('SetVolume', {
Volume: vol
});
};
self.volumeUp = function () {
sendCommandByName('VolumeUp');
};
self.volumeDown = function () {
sendCommandByName('VolumeDown');
};
self.toggleFullscreen = function () {
sendCommandByName('ToggleFullscreen');
};
self.setAudioStreamIndex = function (index) {
sendCommandByName('SetAudioStreamIndex', {
Index: index
});
};
self.setSubtitleStreamIndex = function (index) {
sendCommandByName('SetSubtitleStreamIndex', {
Index: index
});
};
self.setRepeatMode = function (mode) {
sendCommandByName('SetRepeatMode', {
RepeatMode: mode
});
};
self.displayContent = function (options) {
sendCommandByName('DisplayContent', options);
};
self.getPlayerState = function () {
return new Promise(function (resolve, reject) {
var apiClient = window.ApiClient;
if (apiClient) {
apiClient.getSessions().then(function (sessions) {
var currentTargetId = MediaController.getPlayerInfo().id;
// Update existing data
//updateSessionInfo(popup, msg.Data);
var session = sessions.filter(function (s) {
return s.Id == currentTargetId;
})[0];
if (session) {
session = getPlayerState(session);
}
resolve(session);
});
} else {
resolve({});
}
});
};
var pollInterval;
function onPollIntervalFired() {
if (!ApiClient.isWebSocketOpen()) {
var apiClient = window.ApiClient;
if (apiClient) {
apiClient.getSessions().then(processUpdatedSessions);
}
}
}
self.subscribeToPlayerUpdates = function () {
self.isUpdating = true;
if (ApiClient.isWebSocketOpen()) {
ApiClient.sendWebSocketMessage("SessionsStart", "100,800");
}
if (pollInterval) {
clearInterval(pollInterval);
pollInterval = null;
}
pollInterval = setInterval(onPollIntervalFired, 5000);
};
function unsubscribeFromPlayerUpdates() {
self.isUpdating = true;
if (ApiClient.isWebSocketOpen()) {
ApiClient.sendWebSocketMessage("SessionsStop");
}
if (pollInterval) {
clearInterval(pollInterval);
pollInterval = null;
}
}
var playerListenerCount = 0;
self.beginPlayerUpdates = function () {
if (playerListenerCount <= 0) {
playerListenerCount = 0;
self.subscribeToPlayerUpdates();
}
playerListenerCount++;
};
self.endPlayerUpdates = function () {
playerListenerCount--;
if (playerListenerCount <= 0) {
unsubscribeFromPlayerUpdates();
playerListenerCount = 0;
}
};
self.getTargets = function () {
return new Promise(function (resolve, reject) {
var sessionQuery = {
ControllableByUserId: Dashboard.getCurrentUserId()
};
var apiClient = window.ApiClient;
if (apiClient) {
apiClient.getSessions(sessionQuery).then(function (sessions) {
var targets = sessions.filter(function (s) {
return s.DeviceId != apiClient.deviceId();
}).map(function (s) {
return {
name: s.DeviceName,
deviceName: s.DeviceName,
id: s.Id,
playerName: self.name,
appName: s.Client,
playableMediaTypes: s.PlayableMediaTypes,
isLocalPlayer: false,
supportedCommands: s.SupportedCommands
};
});
resolve(targets);
}, function () {
reject();
});
} else {
resolve([]);
}
});
};
self.tryPair = function(target) {
return new Promise(function (resolve, reject) {
resolve();
});
};
}
var player = new remoteControlPlayer();
MediaController.registerPlayer(player);
function getPlayerState(session) {
return session;
}
function firePlaybackEvent(name, session) {
Events.trigger(player, name, [getPlayerState(session)]);
}
function onWebSocketConnectionChange() {
// Reconnect
if (player.isUpdating) {
player.subscribeToPlayerUpdates();
}
}
function processUpdatedSessions(sessions) {
var currentTargetId = MediaController.getPlayerInfo().id;
// Update existing data
//updateSessionInfo(popup, msg.Data);
var session = sessions.filter(function (s) {
return s.Id == currentTargetId;
})[0];
if (session) {
firePlaybackEvent('playstatechange', session);
}
}
function onWebSocketMessageReceived(e, msg) {
var apiClient = this;
if (msg.MessageType === "Sessions") {
processUpdatedSessions(msg.Data);
}
else if (msg.MessageType === "SessionEnded") {
console.log("Server reports another session ended");
if (MediaController.getPlayerInfo().id == msg.Data.Id) {
MediaController.setDefaultPlayerActive();
}
}
else if (msg.MessageType === "PlaybackStart") {
if (msg.Data.DeviceId != apiClient.deviceId()) {
if (MediaController.getPlayerInfo().id == msg.Data.Id) {
firePlaybackEvent('playbackstart', msg.Data);
}
}
}
else if (msg.MessageType === "PlaybackStopped") {
if (msg.Data.DeviceId != apiClient.deviceId()) {
if (MediaController.getPlayerInfo().id == msg.Data.Id) {
firePlaybackEvent('playbackstop', msg.Data);
}
}
}
}
function initializeApiClient(apiClient) {
Events.on(apiClient, "websocketmessage", onWebSocketMessageReceived);
Events.on(apiClient, "websocketopen", onWebSocketConnectionChange);
}
if (window.ApiClient) {
initializeApiClient(window.ApiClient);
}
Events.on(ConnectionManager, 'apiclientcreated', function (e, apiClient) {
initializeApiClient(apiClient);
});
});

View file

@ -167,7 +167,7 @@ var Dashboard = {
url = '/' + url; url = '/' + url;
} }
} }
Emby.Page.show(url); return Emby.Page.show(url);
}, },
showLoadingMsg: function () { showLoadingMsg: function () {
@ -584,11 +584,6 @@ var Dashboard = {
}, },
isServerlessPage: function () {
var url = window.location.href.toLowerCase();
return url.indexOf('connectlogin.html') != -1 || url.indexOf('selectserver.html') != -1 || url.indexOf('login.html') != -1 || url.indexOf('forgotpassword.html') != -1 || url.indexOf('forgotpasswordpin.html') != -1;
},
capabilities: function () { capabilities: function () {
var caps = { var caps = {
@ -844,20 +839,7 @@ var Dashboard = {
require(['browserdeviceprofile', 'qualityoptions', 'appSettings'], function (profileBuilder, qualityoptions, appSettings) { require(['browserdeviceprofile', 'qualityoptions', 'appSettings'], function (profileBuilder, qualityoptions, appSettings) {
var supportsCustomSeeking = false;
if (!browserInfo.mobile) {
supportsCustomSeeking = true;
} else if (AppInfo.isNativeApp && browserInfo.safari) {
if (navigator.userAgent.toLowerCase().indexOf('ipad') == -1) {
// Need to disable it in order to support picture in picture
supportsCustomSeeking = true;
}
} else if (AppInfo.isNativeApp) {
supportsCustomSeeking = true;
}
var profile = profileBuilder(Object.assign(profileOptions || {}, { var profile = profileBuilder(Object.assign(profileOptions || {}, {
supportsCustomSeeking: supportsCustomSeeking
})); }));
if (!(AppInfo.isNativeApp && browserInfo.android) && !browserInfo.edge && !browserInfo.msie) { if (!(AppInfo.isNativeApp && browserInfo.android) && !browserInfo.edge && !browserInfo.msie) {
@ -935,8 +917,6 @@ var AppInfo = {};
// This currently isn't working on android, unfortunately // This currently isn't working on android, unfortunately
AppInfo.supportsFileInput = !(AppInfo.isNativeApp && isAndroid); AppInfo.supportsFileInput = !(AppInfo.isNativeApp && isAndroid);
AppInfo.hasPhysicalVolumeButtons = isCordova || browserInfo.mobile;
if (isCordova && isIOS) { if (isCordova && isIOS) {
AppInfo.moreIcon = 'more-horiz'; AppInfo.moreIcon = 'more-horiz';
} else { } else {
@ -1204,7 +1184,10 @@ var AppInfo = {};
itemHelper: embyWebComponentsBowerPath + '/itemhelper', itemHelper: embyWebComponentsBowerPath + '/itemhelper',
itemShortcuts: embyWebComponentsBowerPath + "/shortcuts", itemShortcuts: embyWebComponentsBowerPath + "/shortcuts",
serverNotifications: embyWebComponentsBowerPath + '/servernotifications', serverNotifications: embyWebComponentsBowerPath + '/servernotifications',
playbackManager: embyWebComponentsBowerPath + '/playback/playbackmanager',
nowPlayingHelper: embyWebComponentsBowerPath + '/playback/nowplayinghelper',
pluginManager: embyWebComponentsBowerPath + '/pluginmanager', pluginManager: embyWebComponentsBowerPath + '/pluginmanager',
packageManager: embyWebComponentsBowerPath + '/packagemanager',
webAnimations: bowerPath + '/web-animations-js/web-animations-next-lite.min' webAnimations: bowerPath + '/web-animations-js/web-animations-next-lite.min'
}; };
@ -1287,6 +1270,8 @@ var AppInfo = {};
define("peoplecardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/peoplecardbuilder"], returnFirstDependency); define("peoplecardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/peoplecardbuilder"], returnFirstDependency);
define("chaptercardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/chaptercardbuilder"], returnFirstDependency); define("chaptercardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/chaptercardbuilder"], returnFirstDependency);
define("mouseManager", [embyWebComponentsBowerPath + "/input/mouse"], returnFirstDependency);
define("deleteHelper", [embyWebComponentsBowerPath + "/deletehelper"], returnFirstDependency); define("deleteHelper", [embyWebComponentsBowerPath + "/deletehelper"], returnFirstDependency);
define("tvguide", [embyWebComponentsBowerPath + "/guide/guide"], returnFirstDependency); define("tvguide", [embyWebComponentsBowerPath + "/guide/guide"], returnFirstDependency);
define("programStyles", ['css!' + embyWebComponentsBowerPath + "/guide/programs"], returnFirstDependency); define("programStyles", ['css!' + embyWebComponentsBowerPath + "/guide/programs"], returnFirstDependency);
@ -1447,6 +1432,8 @@ var AppInfo = {};
return dialoghelper; return dialoghelper;
}); });
define("inputmanager", ['inputManager'], returnFirstDependency);
// alias // alias
define("historyManager", ['embyRouter'], returnFirstDependency); define("historyManager", ['embyRouter'], returnFirstDependency);
@ -1454,60 +1441,6 @@ var AppInfo = {};
define("hammer-main", ['hammer'], createMainContentHammer); define("hammer-main", ['hammer'], createMainContentHammer);
define("appfooter-shared", ['appfooter'], createSharedAppFooter); define("appfooter-shared", ['appfooter'], createSharedAppFooter);
// mock this for now. not used in this app
define("playbackManager", [], function () {
return {
isPlaying: function () {
return MediaPlayer.currentItem != null;
},
isPlayingVideo: function () {
return MediaPlayer.currentItem != null;
},
play: function (options) {
if (options.fullscreen === false) {
// theme backdrops - not supported
if (!options.items || options.items[0].MediaType == 'Video') {
return Promise.reject();
}
}
return MediaController.play(options);
},
queue: function (options) {
MediaController.queue(options);
},
currentPlaylistIndex: function (options) {
return MediaController.currentPlaylistIndex(options);
},
canQueueMediaType: function (mediaType) {
return MediaController.canQueueMediaType(mediaType);
},
canPlay: function (item) {
return MediaController.canPlay(item);
},
canQueue: function (item) {
return MediaController.canQueueMediaType(item.MediaType, item.Type);
},
instantMix: function (item) {
return MediaController.instantMix(item);
},
shuffle: function (item) {
return MediaController.shuffle(item);
},
pause: function () {
return MediaController.pause();
},
stop: function () {
return MediaController.stop();
},
seek: function (ticks) {
return MediaController.seek(ticks);
}
};
});
// mock this for now. not used in this app // mock this for now. not used in this app
define("skinManager", [], function () { define("skinManager", [], function () {
@ -1519,18 +1452,6 @@ var AppInfo = {};
}; };
}); });
// mock this for now. not used in this app
define("playbackManager", [], function () {
return {
};
});
// mock this for now. not used in this app
define("pluginManager", [], function () {
return {
};
});
define("connectionManager", [], function () { define("connectionManager", [], function () {
return ConnectionManager; return ConnectionManager;
}); });
@ -1547,6 +1468,10 @@ var AppInfo = {};
Dashboard.navigate('login.html?serverid=' + serverId); Dashboard.navigate('login.html?serverid=' + serverId);
}; };
embyRouter.showVideoOsd = function () {
return Dashboard.navigate('videoosd.html');
};
embyRouter.showSelectServer = function () { embyRouter.showSelectServer = function () {
Dashboard.navigate('selectserver.html'); Dashboard.navigate('selectserver.html');
}; };
@ -1720,18 +1645,12 @@ var AppInfo = {};
if (Dashboard.isRunningInCordova() && browserInfo.android) { if (Dashboard.isRunningInCordova() && browserInfo.android) {
//define("audiorenderer", ["scripts/htmlmediarenderer"]);
window.VlcAudio = true; window.VlcAudio = true;
define("audiorenderer", ["cordova/android/vlcplayer"]); define("audiorenderer", ["cordova/android/vlcplayer"]);
define("videorenderer", ["cordova/android/vlcplayer"]); define("videorenderer", ["cordova/android/vlcplayer"]);
} }
else if (Dashboard.isRunningInCordova() && browserInfo.safari) { else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
define("audiorenderer", ["cordova/audioplayer"]); define("audiorenderer", ["cordova/audioplayer"]);
define("videorenderer", ["scripts/htmlmediarenderer"]);
}
else {
define("audiorenderer", ["scripts/htmlmediarenderer"]);
define("videorenderer", ["scripts/htmlmediarenderer"]);
} }
if (Dashboard.isRunningInCordova() && browserInfo.android) { if (Dashboard.isRunningInCordova() && browserInfo.android) {
@ -1747,14 +1666,7 @@ var AppInfo = {};
define("buttonenabled", ["legacy/buttonenabled"]); define("buttonenabled", ["legacy/buttonenabled"]);
var deps = [];
deps.push('scripts/mediacontroller');
require(deps, function () {
initAfterDependencies(); initAfterDependencies();
});
} }
function getRequirePromise(deps) { function getRequirePromise(deps) {
@ -1794,7 +1706,6 @@ var AppInfo = {};
createConnectionManager().then(function () { createConnectionManager().then(function () {
console.log('initAfterDependencies promises resolved'); console.log('initAfterDependencies promises resolved');
MediaController.init();
require(['globalize'], function (globalize) { require(['globalize'], function (globalize) {
@ -1850,7 +1761,7 @@ var AppInfo = {};
document.title = Globalize.translateDocument(document.title, 'core'); document.title = Globalize.translateDocument(document.title, 'core');
onAppReady(); loadPlugins([], browserInfo).then(onAppReady);
} }
function defineRoute(newRoute, dictionary) { function defineRoute(newRoute, dictionary) {
@ -2619,6 +2530,16 @@ var AppInfo = {};
anonymous: true anonymous: true
}); });
defineRoute({
path: '/videoosd.html',
dependencies: [],
transition: 'fade',
controller: 'scripts/videoosd',
autoFocus: false,
type: 'video-osd',
supportsThemeMedia: true
});
defineRoute({ defineRoute({
path: '/configurationpage', path: '/configurationpage',
dependencies: ['jQuery'], dependencies: ['jQuery'],
@ -2636,13 +2557,76 @@ var AppInfo = {};
}); });
} }
function onAppReady() { function loadPlugins(externalPlugins, browser, shell) {
require(['scripts/mediaplayer'], function () { console.log('Loading installed plugins');
MediaPlayer.init(); // Load installed plugins
var list = [
//'plugins/defaultskin/plugin',
//'plugins/logoscreensaver/plugin',
//'plugins/backdropscreensaver/plugin',
//'plugins/defaultsoundeffects/plugin',
//'plugins/playbackvalidation/plugin'
];
list.push('bower_components/emby-webcomponents/htmlaudioplayer/plugin');
list.push('bower_components/emby-webcomponents/htmlvideoplayer/plugin');
list.push('bower_components/emby-webcomponents/sessionplayer');
if (browser.chrome) {
list.push('bower_components/emby-webcomponents/chromecastplayer');
}
list.push('bower_components/emby-webcomponents/youtubeplayer/plugin');
//if (globalScope.webapis && webapis.avplay) {
// list.push('plugins/tizenavplayer/plugin');
//} else {
// list.push('plugins/htmlvideoplayer/plugin');
//}
//if (!browser.tv) {
// list.push('plugins/confirmstillplaying/plugin');
//}
//if (!browser.keyboard) {
// list.push('plugins/keyboard/plugin');
//}
for (var i = 0, length = externalPlugins.length; i < length; i++) {
list.push(externalPlugins[i]);
}
//if (shell.canExec) {
// list.push('plugins/externalplayer/plugin');
//}
return new Promise(function (resolve, reject) {
Promise.all(list.map(loadPlugin)).then(function () {
require(['packageManager'], function (packageManager) {
packageManager.init().then(resolve, reject);
}); });
}, reject);
});
}
function loadPlugin(url) {
return new Promise(function (resolve, reject) {
require(['pluginManager'], function (pluginManager) {
pluginManager.loadPlugin(url).then(resolve, reject);
});
});
}
function onAppReady() {
console.log('Begin onAppReady'); console.log('Begin onAppReady');
var deps = []; var deps = [];
@ -2689,7 +2673,6 @@ var AppInfo = {};
var postInitDependencies = []; var postInitDependencies = [];
postInitDependencies.push('bower_components/emby-webcomponents/thememediaplayer'); postInitDependencies.push('bower_components/emby-webcomponents/thememediaplayer');
postInitDependencies.push('scripts/remotecontrol');
postInitDependencies.push('css!css/chromecast.css'); postInitDependencies.push('css!css/chromecast.css');
postInitDependencies.push('scripts/autobackdrops'); postInitDependencies.push('scripts/autobackdrops');
@ -2708,9 +2691,6 @@ var AppInfo = {};
//postInitDependencies.push('cordova/backgroundfetch'); //postInitDependencies.push('cordova/backgroundfetch');
} }
} else if (browserInfo.chrome) {
postInitDependencies.push('scripts/chromecast');
} }
postInitDependencies.push('scripts/nowplayingbar'); postInitDependencies.push('scripts/nowplayingbar');
@ -2724,6 +2704,7 @@ var AppInfo = {};
} }
postInitDependencies.push('bower_components/emby-webcomponents/input/api'); postInitDependencies.push('bower_components/emby-webcomponents/input/api');
postInitDependencies.push('mouseManager');
if (!browserInfo.tv) { if (!browserInfo.tv) {

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'dom', 'components/categorysyncbuttons', 'cardBuilder', 'apphost', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, dom, categorysyncbuttons, cardBuilder, appHost) { define(['libraryBrowser', 'dom', 'components/categorysyncbuttons', 'cardBuilder', 'apphost', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, dom, categorysyncbuttons, cardBuilder, appHost, playbackManager) {
'use strict'; 'use strict';
return function (view, params) { return function (view, params) {
@ -274,13 +274,13 @@
} }
} }
Events.on(MediaController, 'playbackstop', onPlaybackStop); Events.on(playbackManager, 'playbackstop', onPlaybackStop);
Events.on(ApiClient, "websocketmessage", onWebSocketMessage); Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
}); });
view.addEventListener('viewbeforehide', function (e) { view.addEventListener('viewbeforehide', function (e) {
Events.off(MediaController, 'playbackstop', onPlaybackStop); Events.off(playbackManager, 'playbackstop', onPlaybackStop);
Events.off(ApiClient, "websocketmessage", onWebSocketMessage); Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
}); });

View file

@ -1,4 +1,4 @@
define(['playbackManager', 'dom', 'inputmanager', 'datetime', 'itemHelper', 'mediaInfo', 'focusManager', 'imageLoader', 'scrollHelper', 'events', 'connectionManager', 'browser', 'globalize', 'apphost', 'scrollStyles'], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost) { define(['playbackManager', 'dom', 'inputmanager', 'datetime', 'itemHelper', 'mediaInfo', 'focusManager', 'imageLoader', 'scrollHelper', 'events', 'connectionManager', 'browser', 'globalize', 'apphost', 'scrollStyles', 'emby-slider'], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost) {
'use strict'; 'use strict';
function seriesImageUrl(item, options) { function seriesImageUrl(item, options) {
@ -174,11 +174,11 @@
if (url) { if (url) {
var pageTitle = document.querySelector('.pageTitle'); //var pageTitle = document.querySelector('.pageTitle');
pageTitle.style.backgroundImage = "url('" + url + "')"; //pageTitle.style.backgroundImage = "url('" + url + "')";
pageTitle.classList.add('pageTitleWithLogo'); //pageTitle.classList.add('pageTitleWithLogo');
pageTitle.innerHTML = ''; //pageTitle.innerHTML = '';
document.querySelector('.headerLogo').classList.add('hide'); //document.querySelector('.headerLogo').classList.add('hide');
} else { } else {
Emby.Page.setTitle(''); Emby.Page.setTitle('');
} }
@ -739,11 +739,14 @@
return opt; return opt;
}); });
var positionTo = this;
require(['actionsheet'], function (actionsheet) { require(['actionsheet'], function (actionsheet) {
actionsheet.show({ actionsheet.show({
items: menuItems, items: menuItems,
title: globalize.translate('Audio') title: globalize.translate('Audio'),
positionTo: positionTo
}).then(function (id) { }).then(function (id) {
var index = parseInt(id); var index = parseInt(id);
if (index !== currentIndex) { if (index !== currentIndex) {
@ -785,11 +788,14 @@
return opt; return opt;
}); });
var positionTo = this;
require(['actionsheet'], function (actionsheet) { require(['actionsheet'], function (actionsheet) {
actionsheet.show({ actionsheet.show({
title: globalize.translate('Subtitles'), title: globalize.translate('Subtitles'),
items: menuItems items: menuItems,
positionTo: positionTo
}).then(function (id) { }).then(function (id) {
var index = parseInt(id); var index = parseInt(id);
if (index !== currentIndex) { if (index !== currentIndex) {

View file

@ -1,4 +1,155 @@
<div id="videoOsdPage" data-role="page" class="page libraryPage"> <div id="videoOsdPage" data-role="page" class="page libraryPage" data-backbutton="true">
<style>
.osdHeader {
padding-bottom: 3vh;
transition: transform 300ms ease-out, opacity 300ms ease-out;
will-change: transform;
position: relative;
z-index: 1;
}
.osdHeader .viewMenuBar {
background-color: rgba(0, 0, 0, .3);
background: linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, 0));
}
.osdHeader-hidden {
transform: translate3d(0,-100%,0);
opacity: 0;
}
.osdHeader .headerButton:not(.headerBackButton) {
display: none;
}
.osdHeader-hidden {
transform: translate3d(0,-100%,0);
opacity: 0;
}
.scenePicker {
position: fixed;
left: 12%;
bottom: 20%;
right: 0;
display: flex;
flex-direction: row;
align-items: center;
}
.chapterThumb {
height: 24vh;
background-position: center center;
background-size: contain;
background-repeat: no-repeat;
-moz-box-shadow: 0 0 1.9vh #000;
-webkit-box-shadow: 0 0 1.9vh #000;
box-shadow: 0 0 1.9vh #000;
border: solid 1px #222;
margin: 1vh;
transition: opacity ease-out .3s;
}
.chapterThumb:not(.selectedChapterThumb) {
height: 22vh;
opacity: .2;
border-color: black;
}
.videoOsdBottom {
position: fixed;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, .7);
color: #fff;
padding: 1%;
display: flex;
flex-direction: row;
will-change: transform;
transition: transform 300ms ease-out, opacity 300ms ease-out;
}
.videoOsdBottom-hidden {
transform: translate3d(0,100%,0);
opacity: 0;
}
.osdControls {
flex-grow: 1;
}
.videoOsdBottom .buttons {
padding: .25em 0 0;
display: flex;
align-items: center;
}
.videoOsdBottom paper-icon-button {
color: #eee;
}
.osdVolumeSliderContainer {
width: 6.5em;
flex-grow: 1;
}
.volumeButtons {
margin: 0 .5em 0 auto;
display: flex;
align-items: center;
}
.osdTimeText {
margin-left: 1em;
}
.mouseIdle .videoOsdBottom .volumeButtons {
display: none;
}
.osdPoster {
width: 10%;
position: relative;
margin-right: .5em;
}
.osdPoster img {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: auto;
width: 100%;
-moz-box-shadow: 0 0 1.9vh #000;
-webkit-box-shadow: 0 0 1.9vh #000;
box-shadow: 0 0 1.9vh #000;
border: solid 1px #222;
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
.osdTitle {
margin: 0;
padding-left: .4em;
}
.osdMediaInfo {
margin-left: 1em;
color: #eee;
display: flex;
align-items: flex-end;
}
.osdTextContainer {
display: flex;
align-items: baseline;
}
</style>
<div class="pageContainer flex"> <div class="pageContainer flex">
</div> </div>