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:
parent
b3a664583a
commit
407f137095
24 changed files with 713 additions and 6466 deletions
|
@ -153,6 +153,14 @@ define(['appStorage', 'browser'], function (appStorage, browser) {
|
|||
features.push('imageanalysis');
|
||||
}
|
||||
|
||||
if (Dashboard.isConnectMode()) {
|
||||
features.push('multiserver');
|
||||
}
|
||||
|
||||
if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) {
|
||||
features.push('physicalvolumecontrol');
|
||||
}
|
||||
|
||||
return features;
|
||||
}();
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
if (Dashboard.isConnectMode()) {
|
||||
if (appHost.supports('multiserver')) {
|
||||
commands.push({
|
||||
name: globalize.translate('HeaderSelectServer'),
|
||||
id: 'selectserver'
|
||||
|
|
|
@ -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';
|
||||
|
||||
var currentDisplayInfo;
|
||||
|
||||
function mirrorItem(info) {
|
||||
function mirrorItem(info, player) {
|
||||
|
||||
var item = info.item;
|
||||
|
||||
MediaController.getCurrentPlayer().displayContent({
|
||||
playbackManager.displayContent({
|
||||
|
||||
ItemName: item.Name,
|
||||
ItemId: item.Id,
|
||||
ItemType: item.Type,
|
||||
Context: info.context
|
||||
});
|
||||
}, player);
|
||||
}
|
||||
|
||||
function mirrorIfEnabled(info) {
|
||||
|
||||
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) {
|
||||
mirrorItem(info);
|
||||
mirrorItem(info, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showPlayerSelection(button, enableHistory) {
|
||||
|
||||
var playerInfo = MediaController.getPlayerInfo();
|
||||
var currentPlayerInfo = playbackManager.getPlayerInfo();
|
||||
|
||||
if (!playerInfo.isLocalPlayer) {
|
||||
showActivePlayerMenu(playerInfo);
|
||||
if (currentPlayerInfo) {
|
||||
if (!currentPlayerInfo.isLocalPlayer) {
|
||||
showActivePlayerMenu(currentPlayerInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
|
||||
|
||||
loading.show();
|
||||
|
||||
MediaController.getTargets().then(function (targets) {
|
||||
playbackManager.getTargets().then(function (targets) {
|
||||
|
||||
var menuItems = targets.map(function (t) {
|
||||
|
||||
|
@ -54,7 +60,7 @@
|
|||
return {
|
||||
name: name,
|
||||
id: t.id,
|
||||
selected: playerInfo.id == t.id
|
||||
selected: currentPlayerId === t.id
|
||||
};
|
||||
|
||||
});
|
||||
|
@ -84,7 +90,7 @@
|
|||
return t.id == id;
|
||||
})[0];
|
||||
|
||||
MediaController.trySetActivePlayer(target.playerName, target);
|
||||
playbackManager.trySetActivePlayer(target.playerName, target);
|
||||
|
||||
mirrorIfEnabled();
|
||||
|
||||
|
@ -127,7 +133,7 @@
|
|||
if (playerInfo.supportedCommands.indexOf('DisplayContent') != -1) {
|
||||
|
||||
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 += '<span>' + Globalize.translate('OptionEnableDisplayMirroring') + '</span>';
|
||||
html += '</label>';
|
||||
|
@ -162,7 +168,7 @@
|
|||
}
|
||||
|
||||
dlg.querySelector('.btnDisconnect').addEventListener('click', function () {
|
||||
MediaController.disconnectFromPlayer();
|
||||
playbackManager.disconnectFromPlayer();
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
|
||||
|
@ -178,7 +184,7 @@
|
|||
}
|
||||
|
||||
function onMirrorChange() {
|
||||
MediaController.enableDisplayMirroring(this.checked);
|
||||
playbackManager.enableDisplayMirroring(this.checked);
|
||||
}
|
||||
|
||||
function onCastButtonClicked() {
|
||||
|
|
|
@ -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';
|
||||
|
||||
function showSlideshowMenu(context) {
|
||||
|
@ -16,24 +16,8 @@
|
|||
|
||||
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 = {
|
||||
name: s.DisplayTitle || name,
|
||||
name: s.DisplayTitle,
|
||||
id: s.Index
|
||||
};
|
||||
|
||||
|
@ -67,24 +51,8 @@
|
|||
|
||||
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 = {
|
||||
name: s.DisplayTitle || name,
|
||||
name: s.DisplayTitle,
|
||||
id: s.Index
|
||||
};
|
||||
|
||||
|
@ -129,11 +97,22 @@
|
|||
}).length > 0;
|
||||
}
|
||||
|
||||
function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) {
|
||||
|
||||
var names = nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo);
|
||||
|
||||
return names.map(function (i) {
|
||||
|
||||
return i.text;
|
||||
|
||||
}).join('<br/>');
|
||||
}
|
||||
|
||||
var currentImgUrl;
|
||||
function updateNowPlayingInfo(context, state) {
|
||||
|
||||
var item = state.NowPlayingItem;
|
||||
var displayName = item ? MediaController.getNowPlayingNameHtml(item).replace('<br/>', ' - ') : '';
|
||||
var displayName = item ? getNowPlayingNameHtml(item).replace('<br/>', ' - ') : '';
|
||||
|
||||
context.querySelector('.nowPlayingPageTitle').innerHTML = displayName;
|
||||
|
||||
|
@ -248,8 +227,10 @@
|
|||
|
||||
var dlg;
|
||||
var currentPlayer;
|
||||
var currentPlayerSupportedCommands = [];
|
||||
var lastPlayerState;
|
||||
var lastUpdateTime = 0;
|
||||
var currentRuntimeTicks = 0;
|
||||
|
||||
var self = this;
|
||||
var playlistNeedsRefresh = true;
|
||||
|
@ -260,13 +241,13 @@
|
|||
var state = lastPlayerState;
|
||||
switch ((state.PlayState || {}).RepeatMode) {
|
||||
case 'RepeatNone':
|
||||
player.setRepeatMode('RepeatAll');
|
||||
playbackManager.setRepeatMode('RepeatAll', player);
|
||||
break;
|
||||
case 'RepeatAll':
|
||||
player.setRepeatMode('RepeatOne');
|
||||
playbackManager.setRepeatMode('RepeatOne', player);
|
||||
break;
|
||||
case 'RepeatOne':
|
||||
player.setRepeatMode('RepeatNone');
|
||||
playbackManager.setRepeatMode('RepeatNone', player);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -278,7 +259,7 @@
|
|||
|
||||
var item = state.NowPlayingItem;
|
||||
|
||||
var playerInfo = MediaController.getPlayerInfo();
|
||||
var playerInfo = playbackManager.getPlayerInfo();
|
||||
|
||||
var supportedCommands = playerInfo.supportedCommands;
|
||||
var playState = state.PlayState || {};
|
||||
|
@ -310,53 +291,16 @@
|
|||
buttonEnabled(context.querySelector('.btnNextTrack'), 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');
|
||||
|
||||
if (!positionSlider.dragging) {
|
||||
|
||||
if (item && item.RunTimeTicks) {
|
||||
|
||||
var pct = playState.PositionTicks / item.RunTimeTicks;
|
||||
pct *= 100;
|
||||
|
||||
positionSlider.value = pct;
|
||||
|
||||
} else {
|
||||
|
||||
positionSlider.value = 0;
|
||||
}
|
||||
|
||||
if (positionSlider && !positionSlider.dragging) {
|
||||
positionSlider.disabled = !playState.CanSeek;
|
||||
}
|
||||
|
||||
if (playState.PositionTicks == null) {
|
||||
context.querySelector('.positionTime').innerHTML = '--:--';
|
||||
} else {
|
||||
context.querySelector('.positionTime').innerHTML = datetime.getDisplayRunningTime(playState.PositionTicks);
|
||||
}
|
||||
updatePlayPauseState(playState.IsPaused, item != null);
|
||||
|
||||
if (item && item.RunTimeTicks != null) {
|
||||
context.querySelector('.runtime').innerHTML = datetime.getDisplayRunningTime(item.RunTimeTicks);
|
||||
} else {
|
||||
context.querySelector('.runtime').innerHTML = '--:--';
|
||||
}
|
||||
var runtimeTicks = item ? item.RunTimeTicks : null;
|
||||
updateTimeDisplay(playState.PositionTicks, runtimeTicks);
|
||||
updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel);
|
||||
|
||||
if (item && item.MediaType == 'Video') {
|
||||
context.classList.remove('hideVideoButtons');
|
||||
|
@ -364,7 +308,7 @@
|
|||
context.classList.add('hideVideoButtons');
|
||||
}
|
||||
|
||||
if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) {
|
||||
if (playerInfo.isLocalPlayer && appHost.supports('physicalvolumecontrol')) {
|
||||
context.classList.add('hideVolumeButtons');
|
||||
} else {
|
||||
context.classList.remove('hideVolumeButtons');
|
||||
|
@ -393,6 +337,65 @@
|
|||
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) {
|
||||
|
||||
var html = '';
|
||||
|
@ -420,7 +423,7 @@
|
|||
//});
|
||||
|
||||
html += listView.getListViewHtml({
|
||||
items: MediaController.playlist(),
|
||||
items: playbackManager.playlist(),
|
||||
smallIcon: true,
|
||||
action: 'setplaylistindex'
|
||||
});
|
||||
|
@ -435,7 +438,7 @@
|
|||
|
||||
itemsContainer.innerHTML = html;
|
||||
|
||||
var index = MediaController.currentPlaylistIndex();
|
||||
var index = playbackManager.currentPlaylistIndex();
|
||||
|
||||
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) {
|
||||
|
||||
console.log('remotecontrol event: ' + e.type);
|
||||
|
||||
var player = this;
|
||||
|
||||
player.beginPlayerUpdates();
|
||||
|
||||
playbackManager.beginPlayerUpdates(player);
|
||||
onStateChanged.call(player, e, state);
|
||||
|
||||
loadPlaylist(dlg);
|
||||
|
@ -480,26 +468,66 @@
|
|||
|
||||
function onPlaybackStopped(e, state) {
|
||||
|
||||
console.log('remotecontrol event: ' + e.type);
|
||||
var player = this;
|
||||
|
||||
player.endPlayerUpdates();
|
||||
|
||||
onStateChanged.call(player, e, {});
|
||||
playbackManager.endPlayerUpdates(player);
|
||||
|
||||
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() {
|
||||
|
||||
if (currentPlayer) {
|
||||
var player = currentPlayer;
|
||||
|
||||
Events.off(currentPlayer, 'playbackstart', onPlaybackStart);
|
||||
Events.off(currentPlayer, 'playbackstop', onPlaybackStopped);
|
||||
Events.off(currentPlayer, 'volumechange', onStateChanged);
|
||||
Events.off(currentPlayer, 'playstatechange', onStateChanged);
|
||||
Events.off(currentPlayer, 'positionchange', onStateChanged);
|
||||
if (player) {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -510,67 +538,58 @@
|
|||
|
||||
currentPlayer = player;
|
||||
|
||||
player.getPlayerState().then(function (state) {
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
playbackManager.getPlayerState(player).then(function (state) {
|
||||
|
||||
if (state.NowPlayingItem) {
|
||||
player.beginPlayerUpdates();
|
||||
playbackManager.beginPlayerUpdates(player);
|
||||
}
|
||||
|
||||
onStateChanged.call(player, { type: 'init' }, state);
|
||||
});
|
||||
|
||||
Events.on(player, 'playbackstart', onPlaybackStart);
|
||||
Events.on(player, 'playbackstop', onPlaybackStopped);
|
||||
Events.on(player, 'volumechange', onStateChanged);
|
||||
Events.on(player, 'playstatechange', onStateChanged);
|
||||
Events.on(player, 'positionchange', onStateChanged);
|
||||
events.on(player, 'playbackstart', onPlaybackStart);
|
||||
events.on(player, 'playbackstop', onPlaybackStopped);
|
||||
events.on(player, 'volumechange', onVolumeChanged);
|
||||
events.on(player, 'pause', onPlayPauseStateChanged);
|
||||
events.on(player, 'playing', onPlayPauseStateChanged);
|
||||
events.on(player, 'timeupdate', onTimeUpdate);
|
||||
|
||||
var playerInfo = MediaController.getPlayerInfo();
|
||||
var playerInfo = playbackManager.getPlayerInfo();
|
||||
|
||||
var supportedCommands = playerInfo.supportedCommands;
|
||||
currentPlayerSupportedCommands = supportedCommands;
|
||||
|
||||
updateSupportedCommands(context, supportedCommands);
|
||||
}
|
||||
|
||||
function updateCastIcon(context) {
|
||||
|
||||
var info = MediaController.getPlayerInfo();
|
||||
var info = playbackManager.getPlayerInfo();
|
||||
var btnCast = context.querySelector('.nowPlayingCastIcon');
|
||||
|
||||
if (info.isLocalPlayer) {
|
||||
|
||||
btnCast.querySelector('i').innerHTML = 'cast';
|
||||
btnCast.classList.remove('btnActiveCast');
|
||||
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = '';
|
||||
|
||||
} else {
|
||||
if (info && !info.isLocalPlayer) {
|
||||
|
||||
btnCast.querySelector('i').innerHTML = 'cast_connected';
|
||||
btnCast.classList.add('btnActiveCast');
|
||||
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() {
|
||||
if (currentPlayer) {
|
||||
|
||||
if (this.classList.contains('repeatToggleButton')) {
|
||||
toggleRepeat(currentPlayer);
|
||||
} else {
|
||||
MediaController.sendCommand({
|
||||
playbackManager.sendCommand({
|
||||
Name: this.getAttribute('data-command')
|
||||
|
||||
}, currentPlayer);
|
||||
|
@ -588,7 +607,7 @@
|
|||
context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) {
|
||||
|
||||
if (currentPlayer) {
|
||||
MediaController.sendCommand({
|
||||
playbackManager.sendCommand({
|
||||
Name: e.target.getAttribute('data-command')
|
||||
|
||||
}, currentPlayer);
|
||||
|
@ -625,7 +644,7 @@
|
|||
context.querySelector('.btnStop').addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.stop();
|
||||
playbackManager.stop(currentPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -646,14 +665,14 @@
|
|||
context.querySelector('.btnNextTrack').addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.nextTrack();
|
||||
playbackManager.nextTrack(currentPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
context.querySelector('.btnPreviousTrack').addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.previousTrack();
|
||||
playbackManager.previousTrack(currentPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -661,11 +680,10 @@
|
|||
|
||||
var value = this.value;
|
||||
|
||||
if (currentPlayer && lastPlayerState) {
|
||||
if (currentPlayer) {
|
||||
|
||||
var newPercent = parseFloat(value);
|
||||
var newPositionTicks = (newPercent / 100) * lastPlayerState.NowPlayingItem.RunTimeTicks;
|
||||
currentPlayer.seek(Math.floor(newPositionTicks));
|
||||
playbackManager.seekPercent(newPercent, currentPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -673,11 +691,11 @@
|
|||
|
||||
var state = lastPlayerState;
|
||||
|
||||
if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) {
|
||||
if (!state || !state.NowPlayingItem || !currentRuntimeTicks) {
|
||||
return '--:--';
|
||||
}
|
||||
|
||||
var ticks = state.NowPlayingItem.RunTimeTicks;
|
||||
var ticks = currentRuntimeTicks;
|
||||
ticks /= 100;
|
||||
ticks *= value;
|
||||
|
||||
|
@ -689,14 +707,14 @@
|
|||
|
||||
var context = dlg;
|
||||
updateCastIcon(context);
|
||||
bindToPlayer(context, MediaController.getCurrentPlayer());
|
||||
bindToPlayer(context, playbackManager.getCurrentPlayer());
|
||||
}
|
||||
|
||||
function onMessageSubmit(e) {
|
||||
|
||||
var form = e.target;
|
||||
|
||||
MediaController.sendCommand({
|
||||
playbackManager.sendCommand({
|
||||
Name: 'DisplayMessage',
|
||||
Arguments: {
|
||||
|
||||
|
@ -720,7 +738,7 @@
|
|||
|
||||
var form = e.target;
|
||||
|
||||
MediaController.sendCommand({
|
||||
playbackManager.sendCommand({
|
||||
Name: 'SendString',
|
||||
Arguments: {
|
||||
|
||||
|
@ -774,14 +792,14 @@
|
|||
}
|
||||
});
|
||||
|
||||
Events.on(MediaController, 'playerchange', onPlayerChange);
|
||||
events.on(playbackManager, 'playerchange', onPlayerChange);
|
||||
}
|
||||
|
||||
function onDialogClosed(e) {
|
||||
|
||||
releaseCurrentPlayer();
|
||||
|
||||
Events.off(MediaController, 'playerchange', onPlayerChange);
|
||||
events.off(playbackManager, 'playerchange', onPlayerChange);
|
||||
|
||||
lastPlayerState = null;
|
||||
}
|
||||
|
@ -790,7 +808,7 @@
|
|||
|
||||
currentImgUrl = null;
|
||||
|
||||
bindToPlayer(context, MediaController.getCurrentPlayer());
|
||||
bindToPlayer(context, playbackManager.getCurrentPlayer());
|
||||
|
||||
updateCastIcon(context);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
define(['events'], function (events) {
|
||||
define(['events', 'playbackManager'], function (events, playbackManager) {
|
||||
'use strict';
|
||||
|
||||
function transferPlayback(oldPlayer) {
|
||||
function transferPlayback(oldPlayer, newPlayer) {
|
||||
|
||||
oldPlayer.getPlayerState().then(function (state) {
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
|||
var itemId = item.Id;
|
||||
var resumePositionTicks = playState.PositionTicks || 0;
|
||||
|
||||
MediaController.play({
|
||||
playbackManager.play({
|
||||
ids: [itemId],
|
||||
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) {
|
||||
console.log('Skipping remote control autoplay because oldPlayer is null');
|
||||
if (!oldPlayer || !newPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -45,7 +44,7 @@
|
|||
|
||||
// If playback is playing locally and a new player is activated, transfer the media to that player
|
||||
if (oldPlayer.isPlaying()) {
|
||||
transferPlayback(oldPlayer);
|
||||
transferPlayback(oldPlayer, newPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
.background-theme-b .backgroundContainer.withBackdrop {
|
||||
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 {
|
||||
|
@ -797,17 +797,10 @@ span.itemCommunityRating:not(:empty) + .userDataIcons {
|
|||
}
|
||||
|
||||
.mediaInfoText {
|
||||
background: rgba(31,31,31,.7);
|
||||
padding: .3em .5em;
|
||||
border-radius: .25em;
|
||||
color: #ddd;
|
||||
padding: .3em .5em !important;
|
||||
margin-right: .5em;
|
||||
margin-bottom: .5em;
|
||||
font-size: 94%;
|
||||
background: rgba(170,170,190, .2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
font-size: 94% !important;
|
||||
}
|
||||
|
||||
.mediaInfoText-upper {
|
||||
|
|
|
@ -54,6 +54,11 @@
|
|||
will-change: transform;
|
||||
contain: layout style;
|
||||
font-size: 90%;
|
||||
transition: transform 200ms ease-out;
|
||||
}
|
||||
|
||||
.nowPlayingBar-hidden {
|
||||
transform: translate3d(0,100%,0);
|
||||
}
|
||||
|
||||
.hiddenNowPlayingBar .nowPlayingBar {
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
<html class="preload">
|
||||
<head>
|
||||
<title>Emby</title>
|
||||
|
||||
<style>
|
||||
.transparentDocument, .backgroundContainer-transparent:not(.withBackdrop) {
|
||||
background: none !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="backdropContainer"></div>
|
||||
|
|
|
@ -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);
|
||||
|
||||
});
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
define(['libraryBrowser', 'emby-tabs', 'emby-button'], function (libraryBrowser) {
|
||||
define(['libraryBrowser', 'playbackManager', 'emby-tabs', 'emby-button'], function (libraryBrowser, playbackManager) {
|
||||
'use strict';
|
||||
|
||||
var defaultFirstSection = 'smalllibrarytiles';
|
||||
|
@ -348,12 +348,12 @@
|
|||
}
|
||||
|
||||
view.addEventListener('viewshow', function (e) {
|
||||
Events.on(MediaController, 'playbackstop', onPlaybackStop);
|
||||
Events.on(playbackManager, 'playbackstop', onPlaybackStop);
|
||||
Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||
});
|
||||
|
||||
view.addEventListener('viewbeforehide', function (e) {
|
||||
Events.off(MediaController, 'playbackstop', onPlaybackStop);
|
||||
Events.off(playbackManager, 'playbackstop', onPlaybackStop);
|
||||
Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||
});
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
function getPromise(params) {
|
||||
|
@ -229,7 +229,7 @@
|
|||
hideAll(page, 'btnPlay');
|
||||
}
|
||||
}
|
||||
else if (MediaController.canPlay(item)) {
|
||||
else if (playbackManager.canPlay(item)) {
|
||||
hideAll(page, 'btnPlay', true);
|
||||
canPlay = true;
|
||||
}
|
||||
|
@ -2146,7 +2146,7 @@
|
|||
|
||||
function play(startPosition) {
|
||||
|
||||
MediaController.play({
|
||||
playbackManager.play({
|
||||
items: [currentItem],
|
||||
startPositionTicks: startPosition
|
||||
});
|
||||
|
@ -2176,17 +2176,7 @@
|
|||
|
||||
function playTrailer(page) {
|
||||
|
||||
if (!currentItem.LocalTrailerCount) {
|
||||
|
||||
shell.openUrl(currentItem.RemoteTrailers[0].Url);
|
||||
return;
|
||||
}
|
||||
|
||||
ApiClient.getLocalTrailers(Dashboard.getCurrentUserId(), currentItem.Id).then(function (trailers) {
|
||||
|
||||
MediaController.play({ items: trailers });
|
||||
|
||||
});
|
||||
playbackManager.playTrailers(currentItem);
|
||||
}
|
||||
|
||||
function showPlayMenu(item, target) {
|
||||
|
|
|
@ -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';
|
||||
|
||||
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>';
|
||||
|
||||
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>';
|
||||
}
|
||||
|
||||
|
@ -730,20 +730,19 @@
|
|||
return;
|
||||
}
|
||||
|
||||
var info = MediaController.getPlayerInfo();
|
||||
var info = playbackManager.getPlayerInfo();
|
||||
|
||||
if (info.isLocalPlayer) {
|
||||
|
||||
btnCast.querySelector('i').innerHTML = 'cast';
|
||||
btnCast.classList.remove('btnActiveCast');
|
||||
|
||||
context.querySelector('.headerSelectedPlayer').innerHTML = '';
|
||||
|
||||
} else {
|
||||
if (info && !info.isLocalPlayer) {
|
||||
|
||||
btnCast.querySelector('i').icon = 'cast_connected';
|
||||
btnCast.classList.add('btnActiveCast');
|
||||
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(MediaController, 'playerchange', updateCastIcon);
|
||||
Events.on(playbackManager, 'playerchange', updateCastIcon);
|
||||
|
||||
setDrawerClass();
|
||||
|
||||
|
|
|
@ -1,492 +1,10 @@
|
|||
define(['appSettings', 'events', 'browser'], function (appSettings, events, browser) {
|
||||
define(['appSettings', 'events'], function (appSettings, events) {
|
||||
'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() {
|
||||
|
||||
var self = this;
|
||||
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) {
|
||||
|
||||
|
@ -502,378 +20,8 @@
|
|||
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 () {
|
||||
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
|
@ -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';
|
||||
|
||||
function enableScrollX() {
|
||||
|
@ -332,11 +332,11 @@
|
|||
}
|
||||
|
||||
view.addEventListener('viewshow', function (e) {
|
||||
Events.on(MediaController, 'playbackstop', onPlaybackStop);
|
||||
Events.on(playbackManager, 'playbackstop', onPlaybackStop);
|
||||
});
|
||||
|
||||
view.addEventListener('viewbeforehide', function (e) {
|
||||
Events.off(MediaController, 'playbackstop', onPlaybackStop);
|
||||
Events.off(playbackManager, 'playbackstop', onPlaybackStop);
|
||||
});
|
||||
|
||||
require(["headroom-window"], function (headroom) {
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
if (Dashboard.isConnectMode()) {
|
||||
if (appHost.supports('multiserver')) {
|
||||
page.querySelector('.selectServer').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.selectServer').classList.add('hide');
|
||||
|
|
|
@ -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';
|
||||
|
||||
var currentPlayer;
|
||||
var currentPlayerSupportedCommands = [];
|
||||
|
||||
var currentTimeElement;
|
||||
var nowPlayingImageElement;
|
||||
|
@ -17,13 +18,16 @@
|
|||
var toggleRepeatButton;
|
||||
var toggleRepeatButtonIcon;
|
||||
|
||||
var lastPlayerState;
|
||||
var lastUpdateTime = 0;
|
||||
var lastPlayerState = {};
|
||||
var isEnabled;
|
||||
var currentRuntimeTicks = 0;
|
||||
|
||||
function getNowPlayingBarHtml() {
|
||||
|
||||
var html = '';
|
||||
|
||||
html += '<div class="nowPlayingBar hide">';
|
||||
html += '<div class="nowPlayingBar hide nowPlayingBar-hidden">';
|
||||
|
||||
html += '<div class="nowPlayingBarTop">';
|
||||
html += '<div class="nowPlayingBarPositionContainer sliderContainer">';
|
||||
|
@ -76,49 +80,39 @@
|
|||
return html;
|
||||
}
|
||||
|
||||
function onSlideDownComplete() {
|
||||
this.classList.add('hide');
|
||||
}
|
||||
|
||||
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 () {
|
||||
var keyframes = [
|
||||
{ transform: 'none', offset: 0 },
|
||||
{ transform: 'translateY(100%)', offset: 1 }];
|
||||
var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' };
|
||||
elem.animate(keyframes, timing).onfinish = onfinish;
|
||||
|
||||
// trigger reflow
|
||||
void elem.offsetWidth;
|
||||
|
||||
elem.classList.add('nowPlayingBar-hidden');
|
||||
|
||||
dom.addEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, {
|
||||
once: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function slideUp(elem) {
|
||||
|
||||
if (!elem.classList.contains('hide')) {
|
||||
return;
|
||||
}
|
||||
|
||||
elem.classList.remove('hide');
|
||||
|
||||
if (!browser.animate || browser.slow) {
|
||||
return;
|
||||
}
|
||||
dom.removeEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, {
|
||||
once: true
|
||||
});
|
||||
|
||||
requestAnimationFrame(function () {
|
||||
|
||||
var keyframes = [
|
||||
{ transform: 'translateY(100%)', offset: 0 },
|
||||
{ transform: 'none', offset: 1 }];
|
||||
var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' };
|
||||
elem.animate(keyframes, timing);
|
||||
// trigger reflow
|
||||
void elem.offsetWidth;
|
||||
|
||||
elem.classList.remove('nowPlayingBar-hidden');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -167,7 +161,7 @@
|
|||
unmuteButton.addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.unMute();
|
||||
currentPlayer.setMute(false);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -176,7 +170,7 @@
|
|||
muteButton.addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.mute();
|
||||
currentPlayer.setMute(true);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -184,7 +178,7 @@
|
|||
elem.querySelector('.stopButton').addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.stop();
|
||||
playbackManager.stop(currentPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -201,14 +195,14 @@
|
|||
elem.querySelector('.nextTrackButton').addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.nextTrack();
|
||||
playbackManager.nextTrack(currentPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
elem.querySelector('.previousTrackButton').addEventListener('click', function () {
|
||||
|
||||
if (currentPlayer) {
|
||||
currentPlayer.previousTrack();
|
||||
playbackManager.previousTrack(currentPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -230,13 +224,13 @@
|
|||
|
||||
switch ((state.PlayState || {}).RepeatMode) {
|
||||
case 'RepeatAll':
|
||||
currentPlayer.setRepeatMode('RepeatOne');
|
||||
playbackManager.setRepeatMode('RepeatOne', currentPlayer);
|
||||
break;
|
||||
case 'RepeatOne':
|
||||
currentPlayer.setRepeatMode('RepeatNone');
|
||||
playbackManager.setRepeatMode('RepeatNone', currentPlayer);
|
||||
break;
|
||||
default:
|
||||
currentPlayer.setRepeatMode('RepeatAll');
|
||||
playbackManager.setRepeatMode('RepeatAll', currentPlayer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +241,7 @@
|
|||
volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider');
|
||||
volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer');
|
||||
|
||||
if (AppInfo.hasPhysicalVolumeButtons) {
|
||||
if (appHost.supports('physicalvolumecontrol')) {
|
||||
volumeSliderContainer.classList.add('hide');
|
||||
} else {
|
||||
volumeSliderContainer.classList.remove('hide');
|
||||
|
@ -264,12 +258,11 @@
|
|||
positionSlider = elem.querySelector('.nowPlayingBarPositionSlider');
|
||||
positionSlider.addEventListener('change', function () {
|
||||
|
||||
if (currentPlayer && lastPlayerState) {
|
||||
if (currentPlayer) {
|
||||
|
||||
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;
|
||||
|
||||
if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) {
|
||||
if (!state || !state.NowPlayingItem || !currentRuntimeTicks) {
|
||||
return '--:--';
|
||||
}
|
||||
|
||||
var ticks = state.NowPlayingItem.RunTimeTicks;
|
||||
var ticks = currentRuntimeTicks;
|
||||
ticks /= 100;
|
||||
ticks *= value;
|
||||
|
||||
|
@ -342,47 +335,11 @@
|
|||
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;
|
||||
|
||||
if (playState.IsPaused) {
|
||||
if (isPaused) {
|
||||
|
||||
for (i = 0, length = pauseButtons.length; i < length; i++) {
|
||||
hideButton(pauseButtons[i]);
|
||||
|
@ -400,70 +357,22 @@
|
|||
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);
|
||||
|
||||
}
|
||||
|
||||
currentTimeElement.innerHTML = timeText;
|
||||
|
||||
updateNowPlayingInfo(state);
|
||||
}
|
||||
|
||||
function updatePlayerVolumeState(state, playerInfo) {
|
||||
|
||||
playerInfo = playerInfo || MediaController.getPlayerInfo();
|
||||
var playerInfo = playbackManager.getPlayerInfo();
|
||||
|
||||
var playState = state.PlayState || {};
|
||||
|
||||
updatePlayPauseState(playState.IsPaused);
|
||||
|
||||
var supportedCommands = playerInfo.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;
|
||||
}
|
||||
currentPlayerSupportedCommands = supportedCommands;
|
||||
|
||||
if (supportedCommands.indexOf('SetRepeatMode') == -1) {
|
||||
toggleRepeatButton.classList.add('hide');
|
||||
|
@ -483,11 +392,73 @@
|
|||
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) {
|
||||
showVolumeSlider = false;
|
||||
}
|
||||
|
||||
if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) {
|
||||
if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) {
|
||||
showMuteButton = false;
|
||||
showUnmuteButton = false;
|
||||
showVolumeSlider = false;
|
||||
|
@ -515,7 +486,7 @@
|
|||
}
|
||||
|
||||
if (!volumeSlider.dragging) {
|
||||
volumeSlider.value = playState.VolumeLevel || 0;
|
||||
volumeSlider.value = volumeLevel || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -536,7 +507,7 @@
|
|||
var currentImgUrl;
|
||||
function updateNowPlayingInfo(state) {
|
||||
|
||||
nowPlayingTextElement.innerHTML = MediaController.getNowPlayingNames(state.NowPlayingItem).map(function (nowPlayingName) {
|
||||
nowPlayingTextElement.innerHTML = nowPlayingHelper.getNowPlayingNames(state.NowPlayingItem).map(function (nowPlayingName) {
|
||||
|
||||
if (nowPlayingName.item) {
|
||||
return '<div>' + getTextActionButton(nowPlayingName.item, nowPlayingName.text) + '</div>';
|
||||
|
@ -616,8 +587,6 @@
|
|||
|
||||
var player = this;
|
||||
|
||||
player.beginPlayerUpdates();
|
||||
|
||||
onStateChanged.call(player, e, state);
|
||||
}
|
||||
|
||||
|
@ -628,6 +597,8 @@
|
|||
|
||||
function hideNowPlayingBar() {
|
||||
|
||||
isEnabled = false;
|
||||
|
||||
// Use a timeout to prevent the bar from hiding and showing quickly
|
||||
// in the event of a stop->play command
|
||||
|
||||
|
@ -643,83 +614,130 @@
|
|||
console.log('nowplaying event: ' + e.type);
|
||||
var player = this;
|
||||
|
||||
player.endPlayerUpdates();
|
||||
playbackManager.endPlayerUpdates(player);
|
||||
|
||||
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);
|
||||
var player = this;
|
||||
|
||||
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
|
||||
if (!state.NowPlayingItem) {
|
||||
hideNowPlayingBar();
|
||||
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() {
|
||||
|
||||
if (currentPlayer) {
|
||||
var player = currentPlayer;
|
||||
|
||||
events.off(currentPlayer, 'playbackstart', onPlaybackStart);
|
||||
events.off(currentPlayer, 'playbackstop', onPlaybackStopped);
|
||||
events.off(currentPlayer, 'volumechange', onVolumeChanged);
|
||||
events.off(currentPlayer, 'playstatechange', onStateChanged);
|
||||
events.off(currentPlayer, 'positionchange', onStateChanged);
|
||||
if (player) {
|
||||
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);
|
||||
|
||||
currentPlayer.endPlayerUpdates();
|
||||
playbackManager.endPlayerUpdates(player);
|
||||
currentPlayer = null;
|
||||
|
||||
hideNowPlayingBar();
|
||||
}
|
||||
}
|
||||
|
||||
function onVolumeChanged(e) {
|
||||
|
||||
var player = this;
|
||||
|
||||
Promise.all([player.getPlayerState(), getNowPlayingBar()]).then(function (responses) {
|
||||
|
||||
var state = responses[0];
|
||||
|
||||
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
updatePlayerVolumeState(state);
|
||||
});
|
||||
var player = this;
|
||||
|
||||
updatePlayerVolumeState(player.isMuted(), player.getVolume());
|
||||
}
|
||||
|
||||
function bindToPlayer(player) {
|
||||
|
||||
if (player === currentPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
releaseCurrentPlayer();
|
||||
|
||||
currentPlayer = player;
|
||||
|
||||
player.getPlayerState().then(function (state) {
|
||||
|
||||
if (state.NowPlayingItem) {
|
||||
player.beginPlayerUpdates();
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
playbackManager.getPlayerState(player).then(function (state) {
|
||||
|
||||
onStateChanged.call(player, { type: 'init' }, state);
|
||||
});
|
||||
|
||||
events.on(player, 'playbackstart', onPlaybackStart);
|
||||
events.on(player, 'playbackstop', onPlaybackStopped);
|
||||
events.on(player, 'volumechange', onVolumeChanged);
|
||||
events.on(player, 'playstatechange', onStateChanged);
|
||||
events.on(player, 'positionchange', onStateChanged);
|
||||
events.on(player, 'pause', onPlayPauseStateChanged);
|
||||
events.on(player, 'playing', onPlayPauseStateChanged);
|
||||
events.on(player, 'timeupdate', onTimeUpdate);
|
||||
}
|
||||
|
||||
events.on(MediaController, 'playerchange', function () {
|
||||
|
||||
bindToPlayer(MediaController.getCurrentPlayer());
|
||||
events.on(playbackManager, 'playerchange', function () {
|
||||
bindToPlayer(playbackManager.getCurrentPlayer());
|
||||
});
|
||||
|
||||
bindToPlayer(MediaController.getCurrentPlayer());
|
||||
bindToPlayer(playbackManager.getCurrentPlayer());
|
||||
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
|
@ -167,7 +167,7 @@ var Dashboard = {
|
|||
url = '/' + url;
|
||||
}
|
||||
}
|
||||
Emby.Page.show(url);
|
||||
return Emby.Page.show(url);
|
||||
},
|
||||
|
||||
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 () {
|
||||
|
||||
var caps = {
|
||||
|
@ -844,20 +839,7 @@ var Dashboard = {
|
|||
|
||||
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 || {}, {
|
||||
supportsCustomSeeking: supportsCustomSeeking
|
||||
}));
|
||||
|
||||
if (!(AppInfo.isNativeApp && browserInfo.android) && !browserInfo.edge && !browserInfo.msie) {
|
||||
|
@ -935,8 +917,6 @@ var AppInfo = {};
|
|||
// This currently isn't working on android, unfortunately
|
||||
AppInfo.supportsFileInput = !(AppInfo.isNativeApp && isAndroid);
|
||||
|
||||
AppInfo.hasPhysicalVolumeButtons = isCordova || browserInfo.mobile;
|
||||
|
||||
if (isCordova && isIOS) {
|
||||
AppInfo.moreIcon = 'more-horiz';
|
||||
} else {
|
||||
|
@ -1204,7 +1184,10 @@ var AppInfo = {};
|
|||
itemHelper: embyWebComponentsBowerPath + '/itemhelper',
|
||||
itemShortcuts: embyWebComponentsBowerPath + "/shortcuts",
|
||||
serverNotifications: embyWebComponentsBowerPath + '/servernotifications',
|
||||
playbackManager: embyWebComponentsBowerPath + '/playback/playbackmanager',
|
||||
nowPlayingHelper: embyWebComponentsBowerPath + '/playback/nowplayinghelper',
|
||||
pluginManager: embyWebComponentsBowerPath + '/pluginmanager',
|
||||
packageManager: embyWebComponentsBowerPath + '/packagemanager',
|
||||
webAnimations: bowerPath + '/web-animations-js/web-animations-next-lite.min'
|
||||
};
|
||||
|
||||
|
@ -1287,6 +1270,8 @@ var AppInfo = {};
|
|||
define("peoplecardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/peoplecardbuilder"], returnFirstDependency);
|
||||
define("chaptercardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/chaptercardbuilder"], returnFirstDependency);
|
||||
|
||||
define("mouseManager", [embyWebComponentsBowerPath + "/input/mouse"], returnFirstDependency);
|
||||
|
||||
define("deleteHelper", [embyWebComponentsBowerPath + "/deletehelper"], returnFirstDependency);
|
||||
define("tvguide", [embyWebComponentsBowerPath + "/guide/guide"], returnFirstDependency);
|
||||
define("programStyles", ['css!' + embyWebComponentsBowerPath + "/guide/programs"], returnFirstDependency);
|
||||
|
@ -1447,6 +1432,8 @@ var AppInfo = {};
|
|||
return dialoghelper;
|
||||
});
|
||||
|
||||
define("inputmanager", ['inputManager'], returnFirstDependency);
|
||||
|
||||
// alias
|
||||
define("historyManager", ['embyRouter'], returnFirstDependency);
|
||||
|
||||
|
@ -1454,60 +1441,6 @@ var AppInfo = {};
|
|||
define("hammer-main", ['hammer'], createMainContentHammer);
|
||||
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
|
||||
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 () {
|
||||
return ConnectionManager;
|
||||
});
|
||||
|
@ -1547,6 +1468,10 @@ var AppInfo = {};
|
|||
Dashboard.navigate('login.html?serverid=' + serverId);
|
||||
};
|
||||
|
||||
embyRouter.showVideoOsd = function () {
|
||||
return Dashboard.navigate('videoosd.html');
|
||||
};
|
||||
|
||||
embyRouter.showSelectServer = function () {
|
||||
Dashboard.navigate('selectserver.html');
|
||||
};
|
||||
|
@ -1720,18 +1645,12 @@ var AppInfo = {};
|
|||
|
||||
if (Dashboard.isRunningInCordova() && browserInfo.android) {
|
||||
|
||||
//define("audiorenderer", ["scripts/htmlmediarenderer"]);
|
||||
window.VlcAudio = true;
|
||||
define("audiorenderer", ["cordova/android/vlcplayer"]);
|
||||
define("videorenderer", ["cordova/android/vlcplayer"]);
|
||||
}
|
||||
else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
|
||||
define("audiorenderer", ["cordova/audioplayer"]);
|
||||
define("videorenderer", ["scripts/htmlmediarenderer"]);
|
||||
}
|
||||
else {
|
||||
define("audiorenderer", ["scripts/htmlmediarenderer"]);
|
||||
define("videorenderer", ["scripts/htmlmediarenderer"]);
|
||||
}
|
||||
|
||||
if (Dashboard.isRunningInCordova() && browserInfo.android) {
|
||||
|
@ -1747,14 +1666,7 @@ var AppInfo = {};
|
|||
|
||||
define("buttonenabled", ["legacy/buttonenabled"]);
|
||||
|
||||
var deps = [];
|
||||
|
||||
deps.push('scripts/mediacontroller');
|
||||
|
||||
require(deps, function () {
|
||||
|
||||
initAfterDependencies();
|
||||
});
|
||||
}
|
||||
|
||||
function getRequirePromise(deps) {
|
||||
|
@ -1794,7 +1706,6 @@ var AppInfo = {};
|
|||
createConnectionManager().then(function () {
|
||||
|
||||
console.log('initAfterDependencies promises resolved');
|
||||
MediaController.init();
|
||||
|
||||
require(['globalize'], function (globalize) {
|
||||
|
||||
|
@ -1850,7 +1761,7 @@ var AppInfo = {};
|
|||
|
||||
document.title = Globalize.translateDocument(document.title, 'core');
|
||||
|
||||
onAppReady();
|
||||
loadPlugins([], browserInfo).then(onAppReady);
|
||||
}
|
||||
|
||||
function defineRoute(newRoute, dictionary) {
|
||||
|
@ -2619,6 +2530,16 @@ var AppInfo = {};
|
|||
anonymous: true
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '/videoosd.html',
|
||||
dependencies: [],
|
||||
transition: 'fade',
|
||||
controller: 'scripts/videoosd',
|
||||
autoFocus: false,
|
||||
type: 'video-osd',
|
||||
supportsThemeMedia: true
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '/configurationpage',
|
||||
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');
|
||||
|
||||
var deps = [];
|
||||
|
@ -2689,7 +2673,6 @@ var AppInfo = {};
|
|||
var postInitDependencies = [];
|
||||
|
||||
postInitDependencies.push('bower_components/emby-webcomponents/thememediaplayer');
|
||||
postInitDependencies.push('scripts/remotecontrol');
|
||||
postInitDependencies.push('css!css/chromecast.css');
|
||||
postInitDependencies.push('scripts/autobackdrops');
|
||||
|
||||
|
@ -2708,9 +2691,6 @@ var AppInfo = {};
|
|||
|
||||
//postInitDependencies.push('cordova/backgroundfetch');
|
||||
}
|
||||
|
||||
} else if (browserInfo.chrome) {
|
||||
postInitDependencies.push('scripts/chromecast');
|
||||
}
|
||||
|
||||
postInitDependencies.push('scripts/nowplayingbar');
|
||||
|
@ -2724,6 +2704,7 @@ var AppInfo = {};
|
|||
}
|
||||
|
||||
postInitDependencies.push('bower_components/emby-webcomponents/input/api');
|
||||
postInitDependencies.push('mouseManager');
|
||||
|
||||
if (!browserInfo.tv) {
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
return function (view, params) {
|
||||
|
@ -274,13 +274,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
Events.on(MediaController, 'playbackstop', onPlaybackStop);
|
||||
Events.on(playbackManager, 'playbackstop', onPlaybackStop);
|
||||
Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||
});
|
||||
|
||||
view.addEventListener('viewbeforehide', function (e) {
|
||||
|
||||
Events.off(MediaController, 'playbackstop', onPlaybackStop);
|
||||
Events.off(playbackManager, 'playbackstop', onPlaybackStop);
|
||||
Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||
});
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
function seriesImageUrl(item, options) {
|
||||
|
@ -174,11 +174,11 @@
|
|||
|
||||
if (url) {
|
||||
|
||||
var pageTitle = document.querySelector('.pageTitle');
|
||||
pageTitle.style.backgroundImage = "url('" + url + "')";
|
||||
pageTitle.classList.add('pageTitleWithLogo');
|
||||
pageTitle.innerHTML = '';
|
||||
document.querySelector('.headerLogo').classList.add('hide');
|
||||
//var pageTitle = document.querySelector('.pageTitle');
|
||||
//pageTitle.style.backgroundImage = "url('" + url + "')";
|
||||
//pageTitle.classList.add('pageTitleWithLogo');
|
||||
//pageTitle.innerHTML = '';
|
||||
//document.querySelector('.headerLogo').classList.add('hide');
|
||||
} else {
|
||||
Emby.Page.setTitle('');
|
||||
}
|
||||
|
@ -739,11 +739,14 @@
|
|||
return opt;
|
||||
});
|
||||
|
||||
var positionTo = this;
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
|
||||
actionsheet.show({
|
||||
items: menuItems,
|
||||
title: globalize.translate('Audio')
|
||||
title: globalize.translate('Audio'),
|
||||
positionTo: positionTo
|
||||
}).then(function (id) {
|
||||
var index = parseInt(id);
|
||||
if (index !== currentIndex) {
|
||||
|
@ -785,11 +788,14 @@
|
|||
return opt;
|
||||
});
|
||||
|
||||
var positionTo = this;
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
|
||||
actionsheet.show({
|
||||
title: globalize.translate('Subtitles'),
|
||||
items: menuItems
|
||||
items: menuItems,
|
||||
positionTo: positionTo
|
||||
}).then(function (id) {
|
||||
var index = parseInt(id);
|
||||
if (index !== currentIndex) {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue