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');
|
features.push('imageanalysis');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Dashboard.isConnectMode()) {
|
||||||
|
features.push('multiserver');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) {
|
||||||
|
features.push('physicalvolumecontrol');
|
||||||
|
}
|
||||||
|
|
||||||
return features;
|
return features;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Dashboard.isConnectMode()) {
|
if (appHost.supports('multiserver')) {
|
||||||
commands.push({
|
commands.push({
|
||||||
name: globalize.translate('HeaderSelectServer'),
|
name: globalize.translate('HeaderSelectServer'),
|
||||||
id: 'selectserver'
|
id: 'selectserver'
|
||||||
|
|
|
@ -1,47 +1,53 @@
|
||||||
define(['appSettings', 'events', 'browser', 'libraryMenu', 'loading'], function (appSettings, events, browser, libraryMenu, loading) {
|
define(['appSettings', 'events', 'browser', 'libraryMenu', 'loading', 'playbackManager'], function (appSettings, events, browser, libraryMenu, loading, playbackManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var currentDisplayInfo;
|
var currentDisplayInfo;
|
||||||
|
|
||||||
function mirrorItem(info) {
|
function mirrorItem(info, player) {
|
||||||
|
|
||||||
var item = info.item;
|
var item = info.item;
|
||||||
|
|
||||||
MediaController.getCurrentPlayer().displayContent({
|
playbackManager.displayContent({
|
||||||
|
|
||||||
ItemName: item.Name,
|
ItemName: item.Name,
|
||||||
ItemId: item.Id,
|
ItemId: item.Id,
|
||||||
ItemType: item.Type,
|
ItemType: item.Type,
|
||||||
Context: info.context
|
Context: info.context
|
||||||
});
|
}, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mirrorIfEnabled(info) {
|
function mirrorIfEnabled(info) {
|
||||||
|
|
||||||
info = info || currentDisplayInfo;
|
info = info || currentDisplayInfo;
|
||||||
|
|
||||||
if (info && MediaController.enableDisplayMirroring()) {
|
if (info && playbackManager.enableDisplayMirroring()) {
|
||||||
|
|
||||||
var player = MediaController.getPlayerInfo();
|
var player = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
|
if (player) {
|
||||||
if (!player.isLocalPlayer && player.supportedCommands.indexOf('DisplayContent') != -1) {
|
if (!player.isLocalPlayer && player.supportedCommands.indexOf('DisplayContent') != -1) {
|
||||||
mirrorItem(info);
|
mirrorItem(info, player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPlayerSelection(button, enableHistory) {
|
function showPlayerSelection(button, enableHistory) {
|
||||||
|
|
||||||
var playerInfo = MediaController.getPlayerInfo();
|
var currentPlayerInfo = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
if (!playerInfo.isLocalPlayer) {
|
if (currentPlayerInfo) {
|
||||||
showActivePlayerMenu(playerInfo);
|
if (!currentPlayerInfo.isLocalPlayer) {
|
||||||
|
showActivePlayerMenu(currentPlayerInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
|
||||||
|
|
||||||
loading.show();
|
loading.show();
|
||||||
|
|
||||||
MediaController.getTargets().then(function (targets) {
|
playbackManager.getTargets().then(function (targets) {
|
||||||
|
|
||||||
var menuItems = targets.map(function (t) {
|
var menuItems = targets.map(function (t) {
|
||||||
|
|
||||||
|
@ -54,7 +60,7 @@
|
||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
id: t.id,
|
id: t.id,
|
||||||
selected: playerInfo.id == t.id
|
selected: currentPlayerId === t.id
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -84,7 +90,7 @@
|
||||||
return t.id == id;
|
return t.id == id;
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
MediaController.trySetActivePlayer(target.playerName, target);
|
playbackManager.trySetActivePlayer(target.playerName, target);
|
||||||
|
|
||||||
mirrorIfEnabled();
|
mirrorIfEnabled();
|
||||||
|
|
||||||
|
@ -127,7 +133,7 @@
|
||||||
if (playerInfo.supportedCommands.indexOf('DisplayContent') != -1) {
|
if (playerInfo.supportedCommands.indexOf('DisplayContent') != -1) {
|
||||||
|
|
||||||
html += '<label class="checkboxContainer">';
|
html += '<label class="checkboxContainer">';
|
||||||
var checkedHtml = MediaController.enableDisplayMirroring() ? ' checked' : '';
|
var checkedHtml = playbackManager.enableDisplayMirroring() ? ' checked' : '';
|
||||||
html += '<input type="checkbox" is="emby-checkbox" class="chkMirror"' + checkedHtml + '/>';
|
html += '<input type="checkbox" is="emby-checkbox" class="chkMirror"' + checkedHtml + '/>';
|
||||||
html += '<span>' + Globalize.translate('OptionEnableDisplayMirroring') + '</span>';
|
html += '<span>' + Globalize.translate('OptionEnableDisplayMirroring') + '</span>';
|
||||||
html += '</label>';
|
html += '</label>';
|
||||||
|
@ -162,7 +168,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
dlg.querySelector('.btnDisconnect').addEventListener('click', function () {
|
dlg.querySelector('.btnDisconnect').addEventListener('click', function () {
|
||||||
MediaController.disconnectFromPlayer();
|
playbackManager.disconnectFromPlayer();
|
||||||
dialogHelper.close(dlg);
|
dialogHelper.close(dlg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -178,7 +184,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMirrorChange() {
|
function onMirrorChange() {
|
||||||
MediaController.enableDisplayMirroring(this.checked);
|
playbackManager.enableDisplayMirroring(this.checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCastButtonClicked() {
|
function onCastButtonClicked() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['browser', 'datetime', 'libraryBrowser', 'listView', 'userdataButtons', 'imageLoader', 'cardStyle'], function (browser, datetime, libraryBrowser, listView, userdataButtons, imageLoader) {
|
define(['browser', 'datetime', 'libraryBrowser', 'listView', 'userdataButtons', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'apphost', 'cardStyle'], function (browser, datetime, libraryBrowser, listView, userdataButtons, imageLoader, playbackManager, nowPlayingHelper, events, appHost) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function showSlideshowMenu(context) {
|
function showSlideshowMenu(context) {
|
||||||
|
@ -16,24 +16,8 @@
|
||||||
|
|
||||||
var menuItems = streams.map(function (s) {
|
var menuItems = streams.map(function (s) {
|
||||||
|
|
||||||
var name = (s.Codec || '').toUpperCase();
|
|
||||||
|
|
||||||
if (s.Profile) {
|
|
||||||
name += ' ' + s.Profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.Language) {
|
|
||||||
name += ' · ' + s.Language;
|
|
||||||
}
|
|
||||||
if (s.Layout) {
|
|
||||||
name += ' · ' + s.Layout;
|
|
||||||
}
|
|
||||||
else if (s.Channels) {
|
|
||||||
name += ' · ' + s.Channels + ' ch';
|
|
||||||
}
|
|
||||||
|
|
||||||
var menuItem = {
|
var menuItem = {
|
||||||
name: s.DisplayTitle || name,
|
name: s.DisplayTitle,
|
||||||
id: s.Index
|
id: s.Index
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,24 +51,8 @@
|
||||||
|
|
||||||
var menuItems = streams.map(function (s) {
|
var menuItems = streams.map(function (s) {
|
||||||
|
|
||||||
var name = (s.Language || Globalize.translate('LabelUnknownLanguage'));
|
|
||||||
|
|
||||||
if (s.IsDefault && s.IsForced) {
|
|
||||||
name += ' · ' + Globalize.translate('LabelDefaultForcedStream');
|
|
||||||
}
|
|
||||||
else if (s.IsDefault) {
|
|
||||||
name += ' · ' + Globalize.translate('LabelDefaultStream');
|
|
||||||
}
|
|
||||||
else if (s.IsForced) {
|
|
||||||
name += ' · ' + Globalize.translate('LabelForcedStream');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.Codec) {
|
|
||||||
name += ' · ' + s.Codec.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
var menuItem = {
|
var menuItem = {
|
||||||
name: s.DisplayTitle || name,
|
name: s.DisplayTitle,
|
||||||
id: s.Index
|
id: s.Index
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -129,11 +97,22 @@
|
||||||
}).length > 0;
|
}).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) {
|
||||||
|
|
||||||
|
var names = nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo);
|
||||||
|
|
||||||
|
return names.map(function (i) {
|
||||||
|
|
||||||
|
return i.text;
|
||||||
|
|
||||||
|
}).join('<br/>');
|
||||||
|
}
|
||||||
|
|
||||||
var currentImgUrl;
|
var currentImgUrl;
|
||||||
function updateNowPlayingInfo(context, state) {
|
function updateNowPlayingInfo(context, state) {
|
||||||
|
|
||||||
var item = state.NowPlayingItem;
|
var item = state.NowPlayingItem;
|
||||||
var displayName = item ? MediaController.getNowPlayingNameHtml(item).replace('<br/>', ' - ') : '';
|
var displayName = item ? getNowPlayingNameHtml(item).replace('<br/>', ' - ') : '';
|
||||||
|
|
||||||
context.querySelector('.nowPlayingPageTitle').innerHTML = displayName;
|
context.querySelector('.nowPlayingPageTitle').innerHTML = displayName;
|
||||||
|
|
||||||
|
@ -248,8 +227,10 @@
|
||||||
|
|
||||||
var dlg;
|
var dlg;
|
||||||
var currentPlayer;
|
var currentPlayer;
|
||||||
|
var currentPlayerSupportedCommands = [];
|
||||||
var lastPlayerState;
|
var lastPlayerState;
|
||||||
var lastUpdateTime = 0;
|
var lastUpdateTime = 0;
|
||||||
|
var currentRuntimeTicks = 0;
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var playlistNeedsRefresh = true;
|
var playlistNeedsRefresh = true;
|
||||||
|
@ -260,13 +241,13 @@
|
||||||
var state = lastPlayerState;
|
var state = lastPlayerState;
|
||||||
switch ((state.PlayState || {}).RepeatMode) {
|
switch ((state.PlayState || {}).RepeatMode) {
|
||||||
case 'RepeatNone':
|
case 'RepeatNone':
|
||||||
player.setRepeatMode('RepeatAll');
|
playbackManager.setRepeatMode('RepeatAll', player);
|
||||||
break;
|
break;
|
||||||
case 'RepeatAll':
|
case 'RepeatAll':
|
||||||
player.setRepeatMode('RepeatOne');
|
playbackManager.setRepeatMode('RepeatOne', player);
|
||||||
break;
|
break;
|
||||||
case 'RepeatOne':
|
case 'RepeatOne':
|
||||||
player.setRepeatMode('RepeatNone');
|
playbackManager.setRepeatMode('RepeatNone', player);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +259,7 @@
|
||||||
|
|
||||||
var item = state.NowPlayingItem;
|
var item = state.NowPlayingItem;
|
||||||
|
|
||||||
var playerInfo = MediaController.getPlayerInfo();
|
var playerInfo = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
var supportedCommands = playerInfo.supportedCommands;
|
var supportedCommands = playerInfo.supportedCommands;
|
||||||
var playState = state.PlayState || {};
|
var playState = state.PlayState || {};
|
||||||
|
@ -310,53 +291,16 @@
|
||||||
buttonEnabled(context.querySelector('.btnNextTrack'), item != null);
|
buttonEnabled(context.querySelector('.btnNextTrack'), item != null);
|
||||||
buttonEnabled(context.querySelector('.btnPreviousTrack'), item != null);
|
buttonEnabled(context.querySelector('.btnPreviousTrack'), item != null);
|
||||||
|
|
||||||
var btnPause = context.querySelector('.btnPause');
|
|
||||||
var btnPlay = context.querySelector('.btnPlay');
|
|
||||||
|
|
||||||
buttonEnabled(btnPause, item != null);
|
|
||||||
buttonEnabled(btnPlay, item != null);
|
|
||||||
|
|
||||||
if (playState.IsPaused) {
|
|
||||||
|
|
||||||
hideButton(btnPause);
|
|
||||||
showButton(btnPlay);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
showButton(btnPause);
|
|
||||||
hideButton(btnPlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
|
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
|
||||||
|
if (positionSlider && !positionSlider.dragging) {
|
||||||
if (!positionSlider.dragging) {
|
|
||||||
|
|
||||||
if (item && item.RunTimeTicks) {
|
|
||||||
|
|
||||||
var pct = playState.PositionTicks / item.RunTimeTicks;
|
|
||||||
pct *= 100;
|
|
||||||
|
|
||||||
positionSlider.value = pct;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
positionSlider.value = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
positionSlider.disabled = !playState.CanSeek;
|
positionSlider.disabled = !playState.CanSeek;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playState.PositionTicks == null) {
|
updatePlayPauseState(playState.IsPaused, item != null);
|
||||||
context.querySelector('.positionTime').innerHTML = '--:--';
|
|
||||||
} else {
|
|
||||||
context.querySelector('.positionTime').innerHTML = datetime.getDisplayRunningTime(playState.PositionTicks);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item && item.RunTimeTicks != null) {
|
var runtimeTicks = item ? item.RunTimeTicks : null;
|
||||||
context.querySelector('.runtime').innerHTML = datetime.getDisplayRunningTime(item.RunTimeTicks);
|
updateTimeDisplay(playState.PositionTicks, runtimeTicks);
|
||||||
} else {
|
updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel);
|
||||||
context.querySelector('.runtime').innerHTML = '--:--';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item && item.MediaType == 'Video') {
|
if (item && item.MediaType == 'Video') {
|
||||||
context.classList.remove('hideVideoButtons');
|
context.classList.remove('hideVideoButtons');
|
||||||
|
@ -364,7 +308,7 @@
|
||||||
context.classList.add('hideVideoButtons');
|
context.classList.add('hideVideoButtons');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) {
|
if (playerInfo.isLocalPlayer && appHost.supports('physicalvolumecontrol')) {
|
||||||
context.classList.add('hideVolumeButtons');
|
context.classList.add('hideVolumeButtons');
|
||||||
} else {
|
} else {
|
||||||
context.classList.remove('hideVolumeButtons');
|
context.classList.remove('hideVolumeButtons');
|
||||||
|
@ -393,6 +337,65 @@
|
||||||
updateNowPlayingInfo(context, state);
|
updateNowPlayingInfo(context, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updatePlayerVolumeState(isMuted, volumeLevel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePlayPauseState(isPaused, isActive) {
|
||||||
|
|
||||||
|
var context = dlg;
|
||||||
|
|
||||||
|
var btnPause = context.querySelector('.btnPause');
|
||||||
|
var btnPlay = context.querySelector('.btnPlay');
|
||||||
|
|
||||||
|
buttonEnabled(btnPause, isActive);
|
||||||
|
buttonEnabled(btnPlay, isActive);
|
||||||
|
|
||||||
|
if (isPaused) {
|
||||||
|
|
||||||
|
hideButton(btnPause);
|
||||||
|
showButton(btnPlay);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
showButton(btnPause);
|
||||||
|
hideButton(btnPlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTimeDisplay(positionTicks, runtimeTicks) {
|
||||||
|
|
||||||
|
// See bindEvents for why this is necessary
|
||||||
|
var context = dlg;
|
||||||
|
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
|
||||||
|
|
||||||
|
if (positionSlider && !positionSlider.dragging) {
|
||||||
|
if (runtimeTicks) {
|
||||||
|
|
||||||
|
var pct = positionTicks / runtimeTicks;
|
||||||
|
pct *= 100;
|
||||||
|
|
||||||
|
positionSlider.value = pct;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
positionSlider.value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (positionTicks == null) {
|
||||||
|
context.querySelector('.positionTime').innerHTML = '--:--';
|
||||||
|
} else {
|
||||||
|
context.querySelector('.positionTime').innerHTML = datetime.getDisplayRunningTime(positionTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtimeTicks != null) {
|
||||||
|
context.querySelector('.runtime').innerHTML = datetime.getDisplayRunningTime(runtimeTicks);
|
||||||
|
} else {
|
||||||
|
context.querySelector('.runtime').innerHTML = '--:--';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadPlaylist(context) {
|
function loadPlaylist(context) {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
@ -420,7 +423,7 @@
|
||||||
//});
|
//});
|
||||||
|
|
||||||
html += listView.getListViewHtml({
|
html += listView.getListViewHtml({
|
||||||
items: MediaController.playlist(),
|
items: playbackManager.playlist(),
|
||||||
smallIcon: true,
|
smallIcon: true,
|
||||||
action: 'setplaylistindex'
|
action: 'setplaylistindex'
|
||||||
});
|
});
|
||||||
|
@ -435,7 +438,7 @@
|
||||||
|
|
||||||
itemsContainer.innerHTML = html;
|
itemsContainer.innerHTML = html;
|
||||||
|
|
||||||
var index = MediaController.currentPlaylistIndex();
|
var index = playbackManager.currentPlaylistIndex();
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
|
|
||||||
|
@ -452,27 +455,12 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStateChanged(e, state) {
|
|
||||||
|
|
||||||
if (e.type == 'positionchange') {
|
|
||||||
// Try to avoid hammering the document with changes
|
|
||||||
var now = new Date().getTime();
|
|
||||||
if ((now - lastUpdateTime) < 700) {
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastUpdateTime = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePlayerState(dlg, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPlaybackStart(e, state) {
|
function onPlaybackStart(e, state) {
|
||||||
|
|
||||||
|
console.log('remotecontrol event: ' + e.type);
|
||||||
|
|
||||||
var player = this;
|
var player = this;
|
||||||
|
playbackManager.beginPlayerUpdates(player);
|
||||||
player.beginPlayerUpdates();
|
|
||||||
|
|
||||||
onStateChanged.call(player, e, state);
|
onStateChanged.call(player, e, state);
|
||||||
|
|
||||||
loadPlaylist(dlg);
|
loadPlaylist(dlg);
|
||||||
|
@ -480,26 +468,66 @@
|
||||||
|
|
||||||
function onPlaybackStopped(e, state) {
|
function onPlaybackStopped(e, state) {
|
||||||
|
|
||||||
|
console.log('remotecontrol event: ' + e.type);
|
||||||
var player = this;
|
var player = this;
|
||||||
|
|
||||||
player.endPlayerUpdates();
|
playbackManager.endPlayerUpdates(player);
|
||||||
|
|
||||||
onStateChanged.call(player, e, {});
|
|
||||||
|
|
||||||
loadPlaylist(dlg);
|
loadPlaylist(dlg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPlayPauseStateChanged(e) {
|
||||||
|
|
||||||
|
var player = this;
|
||||||
|
updatePlayPauseState(player.paused(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStateChanged(event, state) {
|
||||||
|
|
||||||
|
//console.log('nowplaying event: ' + e.type);
|
||||||
|
var player = this;
|
||||||
|
|
||||||
|
updatePlayerState(dlg, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTimeUpdate(e) {
|
||||||
|
|
||||||
|
// Try to avoid hammering the document with changes
|
||||||
|
var now = new Date().getTime();
|
||||||
|
if ((now - lastUpdateTime) < 700) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastUpdateTime = now;
|
||||||
|
|
||||||
|
var player = this;
|
||||||
|
var state = lastPlayerState;
|
||||||
|
var nowPlayingItem = state.NowPlayingItem || {};
|
||||||
|
currentRuntimeTicks = playbackManager.duration(player);
|
||||||
|
updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onVolumeChanged(e) {
|
||||||
|
|
||||||
|
var player = this;
|
||||||
|
|
||||||
|
updatePlayerVolumeState(player.isMuted(), player.getVolume());
|
||||||
|
}
|
||||||
|
|
||||||
function releaseCurrentPlayer() {
|
function releaseCurrentPlayer() {
|
||||||
|
|
||||||
if (currentPlayer) {
|
var player = currentPlayer;
|
||||||
|
|
||||||
Events.off(currentPlayer, 'playbackstart', onPlaybackStart);
|
if (player) {
|
||||||
Events.off(currentPlayer, 'playbackstop', onPlaybackStopped);
|
|
||||||
Events.off(currentPlayer, 'volumechange', onStateChanged);
|
|
||||||
Events.off(currentPlayer, 'playstatechange', onStateChanged);
|
|
||||||
Events.off(currentPlayer, 'positionchange', onStateChanged);
|
|
||||||
|
|
||||||
currentPlayer.endPlayerUpdates();
|
events.off(player, 'playbackstart', onPlaybackStart);
|
||||||
|
events.off(player, 'playbackstop', onPlaybackStopped);
|
||||||
|
events.off(player, 'volumechange', onVolumeChanged);
|
||||||
|
events.off(player, 'pause', onPlayPauseStateChanged);
|
||||||
|
events.off(player, 'playing', onPlayPauseStateChanged);
|
||||||
|
events.off(player, 'timeupdate', onTimeUpdate);
|
||||||
|
|
||||||
|
playbackManager.endPlayerUpdates(player);
|
||||||
currentPlayer = null;
|
currentPlayer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,67 +538,58 @@
|
||||||
|
|
||||||
currentPlayer = player;
|
currentPlayer = player;
|
||||||
|
|
||||||
player.getPlayerState().then(function (state) {
|
if (!player) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
playbackManager.getPlayerState(player).then(function (state) {
|
||||||
|
|
||||||
if (state.NowPlayingItem) {
|
if (state.NowPlayingItem) {
|
||||||
player.beginPlayerUpdates();
|
playbackManager.beginPlayerUpdates(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStateChanged.call(player, { type: 'init' }, state);
|
onStateChanged.call(player, { type: 'init' }, state);
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(player, 'playbackstart', onPlaybackStart);
|
events.on(player, 'playbackstart', onPlaybackStart);
|
||||||
Events.on(player, 'playbackstop', onPlaybackStopped);
|
events.on(player, 'playbackstop', onPlaybackStopped);
|
||||||
Events.on(player, 'volumechange', onStateChanged);
|
events.on(player, 'volumechange', onVolumeChanged);
|
||||||
Events.on(player, 'playstatechange', onStateChanged);
|
events.on(player, 'pause', onPlayPauseStateChanged);
|
||||||
Events.on(player, 'positionchange', onStateChanged);
|
events.on(player, 'playing', onPlayPauseStateChanged);
|
||||||
|
events.on(player, 'timeupdate', onTimeUpdate);
|
||||||
|
|
||||||
var playerInfo = MediaController.getPlayerInfo();
|
var playerInfo = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
var supportedCommands = playerInfo.supportedCommands;
|
var supportedCommands = playerInfo.supportedCommands;
|
||||||
|
currentPlayerSupportedCommands = supportedCommands;
|
||||||
|
|
||||||
updateSupportedCommands(context, supportedCommands);
|
updateSupportedCommands(context, supportedCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCastIcon(context) {
|
function updateCastIcon(context) {
|
||||||
|
|
||||||
var info = MediaController.getPlayerInfo();
|
var info = playbackManager.getPlayerInfo();
|
||||||
var btnCast = context.querySelector('.nowPlayingCastIcon');
|
var btnCast = context.querySelector('.nowPlayingCastIcon');
|
||||||
|
|
||||||
if (info.isLocalPlayer) {
|
if (info && !info.isLocalPlayer) {
|
||||||
|
|
||||||
btnCast.querySelector('i').innerHTML = 'cast';
|
|
||||||
btnCast.classList.remove('btnActiveCast');
|
|
||||||
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = '';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
btnCast.querySelector('i').innerHTML = 'cast_connected';
|
btnCast.querySelector('i').innerHTML = 'cast_connected';
|
||||||
btnCast.classList.add('btnActiveCast');
|
btnCast.classList.add('btnActiveCast');
|
||||||
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = info.deviceName || info.name;
|
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = info.deviceName || info.name;
|
||||||
|
} else {
|
||||||
|
btnCast.querySelector('i').innerHTML = 'cast';
|
||||||
|
btnCast.classList.remove('btnActiveCast');
|
||||||
|
context.querySelector('.nowPlayingSelectedPlayer').innerHTML = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentWithClass(elem, className) {
|
|
||||||
|
|
||||||
while (!elem.classList || !elem.classList.contains(className)) {
|
|
||||||
elem = elem.parentNode;
|
|
||||||
|
|
||||||
if (!elem) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onBtnCommandClick() {
|
function onBtnCommandClick() {
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
|
|
||||||
if (this.classList.contains('repeatToggleButton')) {
|
if (this.classList.contains('repeatToggleButton')) {
|
||||||
toggleRepeat(currentPlayer);
|
toggleRepeat(currentPlayer);
|
||||||
} else {
|
} else {
|
||||||
MediaController.sendCommand({
|
playbackManager.sendCommand({
|
||||||
Name: this.getAttribute('data-command')
|
Name: this.getAttribute('data-command')
|
||||||
|
|
||||||
}, currentPlayer);
|
}, currentPlayer);
|
||||||
|
@ -588,7 +607,7 @@
|
||||||
context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) {
|
context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
MediaController.sendCommand({
|
playbackManager.sendCommand({
|
||||||
Name: e.target.getAttribute('data-command')
|
Name: e.target.getAttribute('data-command')
|
||||||
|
|
||||||
}, currentPlayer);
|
}, currentPlayer);
|
||||||
|
@ -625,7 +644,7 @@
|
||||||
context.querySelector('.btnStop').addEventListener('click', function () {
|
context.querySelector('.btnStop').addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.stop();
|
playbackManager.stop(currentPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -646,14 +665,14 @@
|
||||||
context.querySelector('.btnNextTrack').addEventListener('click', function () {
|
context.querySelector('.btnNextTrack').addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.nextTrack();
|
playbackManager.nextTrack(currentPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
context.querySelector('.btnPreviousTrack').addEventListener('click', function () {
|
context.querySelector('.btnPreviousTrack').addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.previousTrack();
|
playbackManager.previousTrack(currentPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -661,11 +680,10 @@
|
||||||
|
|
||||||
var value = this.value;
|
var value = this.value;
|
||||||
|
|
||||||
if (currentPlayer && lastPlayerState) {
|
if (currentPlayer) {
|
||||||
|
|
||||||
var newPercent = parseFloat(value);
|
var newPercent = parseFloat(value);
|
||||||
var newPositionTicks = (newPercent / 100) * lastPlayerState.NowPlayingItem.RunTimeTicks;
|
playbackManager.seekPercent(newPercent, currentPlayer);
|
||||||
currentPlayer.seek(Math.floor(newPositionTicks));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -673,11 +691,11 @@
|
||||||
|
|
||||||
var state = lastPlayerState;
|
var state = lastPlayerState;
|
||||||
|
|
||||||
if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) {
|
if (!state || !state.NowPlayingItem || !currentRuntimeTicks) {
|
||||||
return '--:--';
|
return '--:--';
|
||||||
}
|
}
|
||||||
|
|
||||||
var ticks = state.NowPlayingItem.RunTimeTicks;
|
var ticks = currentRuntimeTicks;
|
||||||
ticks /= 100;
|
ticks /= 100;
|
||||||
ticks *= value;
|
ticks *= value;
|
||||||
|
|
||||||
|
@ -689,14 +707,14 @@
|
||||||
|
|
||||||
var context = dlg;
|
var context = dlg;
|
||||||
updateCastIcon(context);
|
updateCastIcon(context);
|
||||||
bindToPlayer(context, MediaController.getCurrentPlayer());
|
bindToPlayer(context, playbackManager.getCurrentPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMessageSubmit(e) {
|
function onMessageSubmit(e) {
|
||||||
|
|
||||||
var form = e.target;
|
var form = e.target;
|
||||||
|
|
||||||
MediaController.sendCommand({
|
playbackManager.sendCommand({
|
||||||
Name: 'DisplayMessage',
|
Name: 'DisplayMessage',
|
||||||
Arguments: {
|
Arguments: {
|
||||||
|
|
||||||
|
@ -720,7 +738,7 @@
|
||||||
|
|
||||||
var form = e.target;
|
var form = e.target;
|
||||||
|
|
||||||
MediaController.sendCommand({
|
playbackManager.sendCommand({
|
||||||
Name: 'SendString',
|
Name: 'SendString',
|
||||||
Arguments: {
|
Arguments: {
|
||||||
|
|
||||||
|
@ -774,14 +792,14 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(MediaController, 'playerchange', onPlayerChange);
|
events.on(playbackManager, 'playerchange', onPlayerChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDialogClosed(e) {
|
function onDialogClosed(e) {
|
||||||
|
|
||||||
releaseCurrentPlayer();
|
releaseCurrentPlayer();
|
||||||
|
|
||||||
Events.off(MediaController, 'playerchange', onPlayerChange);
|
events.off(playbackManager, 'playerchange', onPlayerChange);
|
||||||
|
|
||||||
lastPlayerState = null;
|
lastPlayerState = null;
|
||||||
}
|
}
|
||||||
|
@ -790,7 +808,7 @@
|
||||||
|
|
||||||
currentImgUrl = null;
|
currentImgUrl = null;
|
||||||
|
|
||||||
bindToPlayer(context, MediaController.getCurrentPlayer());
|
bindToPlayer(context, playbackManager.getCurrentPlayer());
|
||||||
|
|
||||||
updateCastIcon(context);
|
updateCastIcon(context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
define(['events'], function (events) {
|
define(['events', 'playbackManager'], function (events, playbackManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function transferPlayback(oldPlayer) {
|
function transferPlayback(oldPlayer, newPlayer) {
|
||||||
|
|
||||||
oldPlayer.getPlayerState().then(function (state) {
|
oldPlayer.getPlayerState().then(function (state) {
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
var itemId = item.Id;
|
var itemId = item.Id;
|
||||||
var resumePositionTicks = playState.PositionTicks || 0;
|
var resumePositionTicks = playState.PositionTicks || 0;
|
||||||
|
|
||||||
MediaController.play({
|
playbackManager.play({
|
||||||
ids: [itemId],
|
ids: [itemId],
|
||||||
startPositionTicks: resumePositionTicks
|
startPositionTicks: resumePositionTicks
|
||||||
});
|
});
|
||||||
|
@ -26,10 +26,9 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
events.on(MediaController, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) {
|
events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) {
|
||||||
|
|
||||||
if (!oldPlayer) {
|
if (!oldPlayer || !newPlayer) {
|
||||||
console.log('Skipping remote control autoplay because oldPlayer is null');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@
|
||||||
|
|
||||||
// If playback is playing locally and a new player is activated, transfer the media to that player
|
// If playback is playing locally and a new player is activated, transfer the media to that player
|
||||||
if (oldPlayer.isPlaying()) {
|
if (oldPlayer.isPlaying()) {
|
||||||
transferPlayback(oldPlayer);
|
transferPlayback(oldPlayer, newPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
.background-theme-b .backgroundContainer.withBackdrop {
|
.background-theme-b .backgroundContainer.withBackdrop {
|
||||||
background-color: rgba(6, 6, 6, .94) !important;
|
background-color: rgba(6, 6, 6, .94) !important;
|
||||||
background: linear-gradient(to right, rgba(0, 0, 0, .99), rgba(0, 0, 0, .92), rgba(0, 0, 0, .5)) !important;
|
background: linear-gradient(to right, rgba(0, 0, 0, .99), rgba(0, 0, 0, .94), rgba(0, 0, 0, .5)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-body-b {
|
.ui-body-b {
|
||||||
|
@ -797,17 +797,10 @@ span.itemCommunityRating:not(:empty) + .userDataIcons {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mediaInfoText {
|
.mediaInfoText {
|
||||||
background: rgba(31,31,31,.7);
|
padding: .3em .5em !important;
|
||||||
padding: .3em .5em;
|
|
||||||
border-radius: .25em;
|
|
||||||
color: #ddd;
|
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
font-size: 94%;
|
font-size: 94% !important;
|
||||||
background: rgba(170,170,190, .2);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mediaInfoText-upper {
|
.mediaInfoText-upper {
|
||||||
|
|
|
@ -54,6 +54,11 @@
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
contain: layout style;
|
contain: layout style;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
|
transition: transform 200ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nowPlayingBar-hidden {
|
||||||
|
transform: translate3d(0,100%,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hiddenNowPlayingBar .nowPlayingBar {
|
.hiddenNowPlayingBar .nowPlayingBar {
|
||||||
|
|
|
@ -2,6 +2,14 @@
|
||||||
<html class="preload">
|
<html class="preload">
|
||||||
<head>
|
<head>
|
||||||
<title>Emby</title>
|
<title>Emby</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.transparentDocument, .backgroundContainer-transparent:not(.withBackdrop) {
|
||||||
|
background: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="backdropContainer"></div>
|
<div class="backdropContainer"></div>
|
||||||
|
|
|
@ -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';
|
'use strict';
|
||||||
|
|
||||||
var defaultFirstSection = 'smalllibrarytiles';
|
var defaultFirstSection = 'smalllibrarytiles';
|
||||||
|
@ -348,12 +348,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
view.addEventListener('viewshow', function (e) {
|
view.addEventListener('viewshow', function (e) {
|
||||||
Events.on(MediaController, 'playbackstop', onPlaybackStop);
|
Events.on(playbackManager, 'playbackstop', onPlaybackStop);
|
||||||
Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
|
Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
view.addEventListener('viewbeforehide', function (e) {
|
view.addEventListener('viewbeforehide', function (e) {
|
||||||
Events.off(MediaController, 'playbackstop', onPlaybackStop);
|
Events.off(playbackManager, 'playbackstop', onPlaybackStop);
|
||||||
Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
|
Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['layoutManager', 'cardBuilder', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'userdataButtons', 'dom', 'indicators', 'apphost', 'imageLoader', 'libraryMenu', 'shell', 'globalize', 'browser', 'events', 'scrollHelper', 'scrollStyles', 'emby-itemscontainer', 'emby-checkbox'], function (layoutManager, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, userdataButtons, dom, indicators, appHost, imageLoader, libraryMenu, shell, globalize, browser, events, scrollHelper) {
|
define(['layoutManager', 'cardBuilder', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'userdataButtons', 'dom', 'indicators', 'apphost', 'imageLoader', 'libraryMenu', 'globalize', 'browser', 'events', 'scrollHelper', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-checkbox'], function (layoutManager, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, userdataButtons, dom, indicators, appHost, imageLoader, libraryMenu, globalize, browser, events, scrollHelper, playbackManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function getPromise(params) {
|
function getPromise(params) {
|
||||||
|
@ -229,7 +229,7 @@
|
||||||
hideAll(page, 'btnPlay');
|
hideAll(page, 'btnPlay');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (MediaController.canPlay(item)) {
|
else if (playbackManager.canPlay(item)) {
|
||||||
hideAll(page, 'btnPlay', true);
|
hideAll(page, 'btnPlay', true);
|
||||||
canPlay = true;
|
canPlay = true;
|
||||||
}
|
}
|
||||||
|
@ -2146,7 +2146,7 @@
|
||||||
|
|
||||||
function play(startPosition) {
|
function play(startPosition) {
|
||||||
|
|
||||||
MediaController.play({
|
playbackManager.play({
|
||||||
items: [currentItem],
|
items: [currentItem],
|
||||||
startPositionTicks: startPosition
|
startPositionTicks: startPosition
|
||||||
});
|
});
|
||||||
|
@ -2176,17 +2176,7 @@
|
||||||
|
|
||||||
function playTrailer(page) {
|
function playTrailer(page) {
|
||||||
|
|
||||||
if (!currentItem.LocalTrailerCount) {
|
playbackManager.playTrailers(currentItem);
|
||||||
|
|
||||||
shell.openUrl(currentItem.RemoteTrailers[0].Url);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApiClient.getLocalTrailers(Dashboard.getCurrentUserId(), currentItem.Id).then(function (trailers) {
|
|
||||||
|
|
||||||
MediaController.play({ items: trailers });
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPlayMenu(item, target) {
|
function showPlayMenu(item, target) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['layoutManager', 'viewManager', 'libraryBrowser', 'embyRouter', 'paper-icon-button-light', 'material-icons'], function (layoutManager, viewManager, libraryBrowser, embyRouter) {
|
define(['layoutManager', 'viewManager', 'libraryBrowser', 'embyRouter', 'playbackManager', 'paper-icon-button-light', 'material-icons'], function (layoutManager, viewManager, libraryBrowser, embyRouter, playbackManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var enableBottomTabs = AppInfo.isNativeApp;
|
var enableBottomTabs = AppInfo.isNativeApp;
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight headerUserButton autoSize"><i class="md-icon">person</i></button>';
|
html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight headerUserButton autoSize"><i class="md-icon">person</i></button>';
|
||||||
|
|
||||||
if (!browserInfo.mobile && !Dashboard.isConnectMode()) {
|
if (!browserInfo.mobile) {
|
||||||
html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight dashboardEntryHeaderButton autoSize" onclick="return LibraryMenu.onSettingsClicked(event);"><i class="md-icon">settings</i></button>';
|
html += '<button is="paper-icon-button-light" class="headerButton headerButtonRight dashboardEntryHeaderButton autoSize" onclick="return LibraryMenu.onSettingsClicked(event);"><i class="md-icon">settings</i></button>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,20 +730,19 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = MediaController.getPlayerInfo();
|
var info = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
if (info.isLocalPlayer) {
|
if (info && !info.isLocalPlayer) {
|
||||||
|
|
||||||
btnCast.querySelector('i').innerHTML = 'cast';
|
|
||||||
btnCast.classList.remove('btnActiveCast');
|
|
||||||
|
|
||||||
context.querySelector('.headerSelectedPlayer').innerHTML = '';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
btnCast.querySelector('i').icon = 'cast_connected';
|
btnCast.querySelector('i').icon = 'cast_connected';
|
||||||
btnCast.classList.add('btnActiveCast');
|
btnCast.classList.add('btnActiveCast');
|
||||||
context.querySelector('.headerSelectedPlayer').innerHTML = info.deviceName || info.name;
|
context.querySelector('.headerSelectedPlayer').innerHTML = info.deviceName || info.name;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
btnCast.querySelector('i').innerHTML = 'cast';
|
||||||
|
btnCast.classList.remove('btnActiveCast');
|
||||||
|
|
||||||
|
context.querySelector('.headerSelectedPlayer').innerHTML = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1044,7 +1043,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(ConnectionManager, 'localusersignedout', updateUserInHeader);
|
Events.on(ConnectionManager, 'localusersignedout', updateUserInHeader);
|
||||||
Events.on(MediaController, 'playerchange', updateCastIcon);
|
Events.on(playbackManager, 'playerchange', updateCastIcon);
|
||||||
|
|
||||||
setDrawerClass();
|
setDrawerClass();
|
||||||
|
|
||||||
|
|
|
@ -1,492 +1,10 @@
|
||||||
define(['appSettings', 'events', 'browser'], function (appSettings, events, browser) {
|
define(['appSettings', 'events'], function (appSettings, events) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var datetime;
|
|
||||||
|
|
||||||
function monitorPlayer(player) {
|
|
||||||
|
|
||||||
events.on(player, 'playbackstart', function (e, state) {
|
|
||||||
|
|
||||||
var info = {
|
|
||||||
QueueableMediaTypes: state.NowPlayingItem.MediaType,
|
|
||||||
ItemId: state.NowPlayingItem.Id,
|
|
||||||
NowPlayingItem: state.NowPlayingItem
|
|
||||||
};
|
|
||||||
|
|
||||||
info = Object.assign(info, state.PlayState);
|
|
||||||
|
|
||||||
ApiClient.reportPlaybackStart(info);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
events.on(player, 'playbackstop', function (e, state) {
|
|
||||||
|
|
||||||
var stopInfo = {
|
|
||||||
itemId: state.NowPlayingItem.Id,
|
|
||||||
mediaSourceId: state.PlayState.MediaSourceId,
|
|
||||||
positionTicks: state.PlayState.PositionTicks
|
|
||||||
};
|
|
||||||
|
|
||||||
if (state.PlayState.LiveStreamId) {
|
|
||||||
stopInfo.LiveStreamId = state.PlayState.LiveStreamId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.PlayState.PlaySessionId) {
|
|
||||||
stopInfo.PlaySessionId = state.PlayState.PlaySessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApiClient.reportPlaybackStopped(stopInfo);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function mediaController() {
|
function mediaController() {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var currentPlayer;
|
var currentPlayer;
|
||||||
var currentTargetInfo;
|
|
||||||
var players = [];
|
|
||||||
|
|
||||||
self.registerPlayer = function (player) {
|
|
||||||
|
|
||||||
players.push(player);
|
|
||||||
|
|
||||||
if (player.isLocalPlayer) {
|
|
||||||
monitorPlayer(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
events.on(player, 'playbackstop', onPlaybackStop);
|
|
||||||
events.on(player, 'beforeplaybackstart', onBeforePlaybackStart);
|
|
||||||
};
|
|
||||||
|
|
||||||
function onBeforePlaybackStart(e, state) {
|
|
||||||
events.trigger(self, 'beforeplaybackstart', [state, this]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPlaybackStop(e, state) {
|
|
||||||
events.trigger(self, 'playbackstop', [state, this]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.getPlayerInfo = function () {
|
|
||||||
|
|
||||||
var player = currentPlayer || {};
|
|
||||||
var target = currentTargetInfo || {};
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
name: player.name,
|
|
||||||
isLocalPlayer: player.isLocalPlayer,
|
|
||||||
id: target.id,
|
|
||||||
deviceName: target.deviceName,
|
|
||||||
playableMediaTypes: target.playableMediaTypes,
|
|
||||||
supportedCommands: target.supportedCommands
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function triggerPlayerChange(newPlayer, newTarget, previousPlayer) {
|
|
||||||
|
|
||||||
events.trigger(self, 'playerchange', [newPlayer, newTarget, previousPlayer]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setActivePlayer = function (player, targetInfo) {
|
|
||||||
|
|
||||||
if (typeof (player) === 'string') {
|
|
||||||
player = players.filter(function (p) {
|
|
||||||
return p.name == player;
|
|
||||||
})[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!player) {
|
|
||||||
throw new Error('null player');
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousPlayer = currentPlayer;
|
|
||||||
|
|
||||||
currentPairingId = null;
|
|
||||||
currentPlayer = player;
|
|
||||||
currentTargetInfo = targetInfo;
|
|
||||||
|
|
||||||
console.log('Active player: ' + JSON.stringify(currentTargetInfo));
|
|
||||||
|
|
||||||
triggerPlayerChange(player, targetInfo, previousPlayer);
|
|
||||||
};
|
|
||||||
|
|
||||||
var currentPairingId = null;
|
|
||||||
self.trySetActivePlayer = function (player, targetInfo) {
|
|
||||||
|
|
||||||
if (typeof (player) === 'string') {
|
|
||||||
player = players.filter(function (p) {
|
|
||||||
return p.name == player;
|
|
||||||
})[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!player) {
|
|
||||||
throw new Error('null player');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPairingId == targetInfo.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPairingId = targetInfo.id;
|
|
||||||
|
|
||||||
player.tryPair(targetInfo).then(function () {
|
|
||||||
|
|
||||||
var previousPlayer = currentPlayer;
|
|
||||||
|
|
||||||
currentPlayer = player;
|
|
||||||
currentTargetInfo = targetInfo;
|
|
||||||
|
|
||||||
console.log('Active player: ' + JSON.stringify(currentTargetInfo));
|
|
||||||
|
|
||||||
triggerPlayerChange(player, targetInfo, previousPlayer);
|
|
||||||
}, function () {
|
|
||||||
|
|
||||||
if (currentPairingId == targetInfo.id) {
|
|
||||||
currentPairingId = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.trySetActiveDeviceName = function (name) {
|
|
||||||
|
|
||||||
function normalizeName(t) {
|
|
||||||
return t.toLowerCase().replace(' ', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
name = normalizeName(name);
|
|
||||||
|
|
||||||
self.getTargets().then(function (result) {
|
|
||||||
|
|
||||||
var target = result.filter(function (p) {
|
|
||||||
return normalizeName(p.name) == name;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (target) {
|
|
||||||
self.trySetActivePlayer(target.playerName, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.setDefaultPlayerActive = function () {
|
|
||||||
|
|
||||||
var player = self.getDefaultPlayer();
|
|
||||||
|
|
||||||
player.getTargets().then(function (targets) {
|
|
||||||
|
|
||||||
self.setActivePlayer(player, targets[0]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.removeActivePlayer = function (name) {
|
|
||||||
|
|
||||||
if (self.getPlayerInfo().name == name) {
|
|
||||||
self.setDefaultPlayerActive();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
self.removeActiveTarget = function (id) {
|
|
||||||
|
|
||||||
if (self.getPlayerInfo().id == id) {
|
|
||||||
self.setDefaultPlayerActive();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.disconnectFromPlayer = function () {
|
|
||||||
|
|
||||||
var playerInfo = self.getPlayerInfo();
|
|
||||||
|
|
||||||
if (playerInfo.supportedCommands.indexOf('EndSession') != -1) {
|
|
||||||
|
|
||||||
require(['dialog'], function (dialog) {
|
|
||||||
|
|
||||||
var menuItems = [];
|
|
||||||
|
|
||||||
menuItems.push({
|
|
||||||
name: Globalize.translate('ButtonYes'),
|
|
||||||
id: 'yes'
|
|
||||||
});
|
|
||||||
menuItems.push({
|
|
||||||
name: Globalize.translate('ButtonNo'),
|
|
||||||
id: 'no'
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog({
|
|
||||||
buttons: menuItems,
|
|
||||||
//positionTo: positionTo,
|
|
||||||
text: Globalize.translate('ConfirmEndPlayerSession')
|
|
||||||
|
|
||||||
}).then(function (id) {
|
|
||||||
switch (id) {
|
|
||||||
|
|
||||||
case 'yes':
|
|
||||||
MediaController.getCurrentPlayer().endSession();
|
|
||||||
self.setDefaultPlayerActive();
|
|
||||||
break;
|
|
||||||
case 'no':
|
|
||||||
self.setDefaultPlayerActive();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
self.setDefaultPlayerActive();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.getPlayers = function () {
|
|
||||||
return players;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.getTargets = function () {
|
|
||||||
|
|
||||||
var promises = players.map(function (p) {
|
|
||||||
return p.getTargets();
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(promises).then(function (responses) {
|
|
||||||
|
|
||||||
var targets = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < responses.length; i++) {
|
|
||||||
|
|
||||||
var subTargets = responses[i];
|
|
||||||
|
|
||||||
for (var j = 0; j < subTargets.length; j++) {
|
|
||||||
|
|
||||||
targets.push(subTargets[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
targets = targets.sort(function (a, b) {
|
|
||||||
|
|
||||||
var aVal = a.isLocalPlayer ? 0 : 1;
|
|
||||||
var bVal = b.isLocalPlayer ? 0 : 1;
|
|
||||||
|
|
||||||
aVal = aVal.toString() + a.name;
|
|
||||||
bVal = bVal.toString() + b.name;
|
|
||||||
|
|
||||||
return aVal.localeCompare(bVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
return targets;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function validatePlayback(player) {
|
|
||||||
|
|
||||||
if (!player.isLocalPlayer) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
requirejs(["registrationServices"], function (registrationServices) {
|
|
||||||
|
|
||||||
self.playbackTimeLimitMs = null;
|
|
||||||
|
|
||||||
registrationServices.validateFeature('playback').then(resolve, function () {
|
|
||||||
|
|
||||||
self.playbackTimeLimitMs = lockedTimeLimitMs;
|
|
||||||
startAutoStopTimer();
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var autoStopTimeout;
|
|
||||||
var lockedTimeLimitMs = 60000;
|
|
||||||
function startAutoStopTimer() {
|
|
||||||
stopAutoStopTimer();
|
|
||||||
autoStopTimeout = setTimeout(onAutoStopTimeout, lockedTimeLimitMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAutoStopTimeout() {
|
|
||||||
stopAutoStopTimer();
|
|
||||||
MediaController.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopAutoStopTimer() {
|
|
||||||
|
|
||||||
var timeout = autoStopTimeout;
|
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
autoStopTimeout = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.toggleDisplayMirroring = function () {
|
|
||||||
self.enableDisplayMirroring(!self.enableDisplayMirroring());
|
|
||||||
};
|
|
||||||
|
|
||||||
self.enableDisplayMirroring = function (enabled) {
|
|
||||||
|
|
||||||
if (enabled != null) {
|
|
||||||
|
|
||||||
var val = enabled ? '1' : '0';
|
|
||||||
appSettings.set('displaymirror--' + Dashboard.getCurrentUserId(), val);
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
mirrorIfEnabled();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (appSettings.get('displaymirror--' + Dashboard.getCurrentUserId()) || '') != '0';
|
|
||||||
};
|
|
||||||
|
|
||||||
self.play = function (options) {
|
|
||||||
|
|
||||||
if (options.enableRemotePlayers === false) {
|
|
||||||
if (!currentPlayer.isLocalPlayer) {
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatePlayback(currentPlayer).then(function () {
|
|
||||||
if (typeof (options) === 'string') {
|
|
||||||
options = { ids: [options] };
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentPlayer.play(options);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.shuffle = function (id) {
|
|
||||||
|
|
||||||
// accept both id and item being passed in
|
|
||||||
if (id.Id) {
|
|
||||||
id = id.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
validatePlayback(currentPlayer).then(function () {
|
|
||||||
currentPlayer.shuffle(id);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.instantMix = function (id) {
|
|
||||||
|
|
||||||
// accept both id and item being passed in
|
|
||||||
if (id.Id) {
|
|
||||||
id = id.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
validatePlayback(currentPlayer).then(function () {
|
|
||||||
currentPlayer.instantMix(id);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.queue = function (options) {
|
|
||||||
|
|
||||||
if (typeof (options) === 'string') {
|
|
||||||
options = { ids: [options] };
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPlayer.queue(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.queueNext = function (options) {
|
|
||||||
|
|
||||||
if (typeof (options) === 'string') {
|
|
||||||
options = { ids: [options] };
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPlayer.queueNext(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.canPlay = function (item) {
|
|
||||||
|
|
||||||
if (item.Type == "Program") {
|
|
||||||
if (new Date().getTime() > datetime.parseISO8601Date(item.EndDate).getTime() || new Date().getTime() < datetime.parseISO8601Date(item.StartDate).getTime()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.canPlayByAttributes(item.Type, item.MediaType, item.PlayAccess, item.LocationType);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.canPlayByAttributes = function (itemType, mediaType, playAccess, locationType) {
|
|
||||||
|
|
||||||
if (playAccess != 'Full') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locationType == "Virtual") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemType == "Program") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemType == "MusicGenre" || itemType == "Season" || itemType == "Series" || itemType == "BoxSet" || itemType == "MusicAlbum" || itemType == "MusicArtist" || itemType == "Playlist") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.getPlayerInfo().playableMediaTypes.indexOf(mediaType) != -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.canQueueMediaType = function (mediaType, itemType) {
|
|
||||||
|
|
||||||
if (itemType == 'MusicAlbum' || itemType == 'MusicArtist' || itemType == 'MusicGenre') {
|
|
||||||
mediaType = 'Audio';
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentPlayer.canQueueMediaType(mediaType);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.getLocalPlayer = function () {
|
|
||||||
|
|
||||||
return currentPlayer.isLocalPlayer ?
|
|
||||||
|
|
||||||
currentPlayer :
|
|
||||||
|
|
||||||
players.filter(function (p) {
|
|
||||||
return p.isLocalPlayer;
|
|
||||||
})[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
self.getDefaultPlayer = function () {
|
|
||||||
|
|
||||||
return currentPlayer.isLocalPlayer ?
|
|
||||||
|
|
||||||
currentPlayer :
|
|
||||||
|
|
||||||
players.filter(function (p) {
|
|
||||||
return p.isDefaultPlayer;
|
|
||||||
})[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
self.getCurrentPlayer = function () {
|
|
||||||
|
|
||||||
return currentPlayer;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.pause = function () {
|
|
||||||
currentPlayer.pause();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.stop = function () {
|
|
||||||
currentPlayer.stop();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.unpause = function () {
|
|
||||||
currentPlayer.unpause();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.seek = function (position) {
|
|
||||||
currentPlayer.seek(position);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.currentPlaylistIndex = function (i) {
|
self.currentPlaylistIndex = function (i) {
|
||||||
|
|
||||||
|
@ -502,378 +20,8 @@
|
||||||
currentPlayer.removeFromPlaylist(i);
|
currentPlayer.removeFromPlaylist(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.nextTrack = function () {
|
|
||||||
currentPlayer.nextTrack();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.previousTrack = function () {
|
|
||||||
currentPlayer.previousTrack();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.mute = function () {
|
|
||||||
currentPlayer.mute();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.unMute = function () {
|
|
||||||
currentPlayer.unMute();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.toggleMute = function () {
|
|
||||||
currentPlayer.toggleMute();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.volumeDown = function () {
|
|
||||||
currentPlayer.volumeDown();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.volumeUp = function () {
|
|
||||||
currentPlayer.volumeUp();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.setRepeatMode = function (mode) {
|
|
||||||
currentPlayer.setRepeatMode(mode);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.playlist = function () {
|
self.playlist = function () {
|
||||||
return currentPlayer.playlist || [];
|
return currentPlayer.playlist || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
self.sendCommand = function (cmd, player) {
|
|
||||||
|
|
||||||
player = player || self.getLocalPlayer();
|
|
||||||
|
|
||||||
// Full list
|
|
||||||
// https://github.com/MediaBrowser/MediaBrowser/blob/master/MediaBrowser.Model/Session/GeneralCommand.cs#L23
|
|
||||||
console.log('MediaController received command: ' + cmd.Name);
|
|
||||||
switch (cmd.Name) {
|
|
||||||
|
|
||||||
case 'SetRepeatMode':
|
|
||||||
player.setRepeatMode(cmd.Arguments.RepeatMode);
|
|
||||||
break;
|
|
||||||
case 'VolumeUp':
|
|
||||||
player.volumeUp();
|
|
||||||
break;
|
|
||||||
case 'VolumeDown':
|
|
||||||
player.volumeDown();
|
|
||||||
break;
|
|
||||||
case 'Mute':
|
|
||||||
player.mute();
|
|
||||||
break;
|
|
||||||
case 'Unmute':
|
|
||||||
player.unMute();
|
|
||||||
break;
|
|
||||||
case 'ToggleMute':
|
|
||||||
player.toggleMute();
|
|
||||||
break;
|
|
||||||
case 'SetVolume':
|
|
||||||
player.setVolume(cmd.Arguments.Volume);
|
|
||||||
break;
|
|
||||||
case 'SetAudioStreamIndex':
|
|
||||||
player.setAudioStreamIndex(parseInt(cmd.Arguments.Index));
|
|
||||||
break;
|
|
||||||
case 'SetSubtitleStreamIndex':
|
|
||||||
player.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index));
|
|
||||||
break;
|
|
||||||
case 'ToggleFullscreen':
|
|
||||||
player.toggleFullscreen();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
if (!player.isLocalPlayer) {
|
|
||||||
player.sendCommand(cmd);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// TOOD: This doesn't really belong here
|
|
||||||
self.getNowPlayingNames = function (nowPlayingItem, includeNonNameInfo) {
|
|
||||||
|
|
||||||
var topItem = nowPlayingItem;
|
|
||||||
var bottomItem = null;
|
|
||||||
var topText = nowPlayingItem.Name;
|
|
||||||
|
|
||||||
if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType == 'Audio') {
|
|
||||||
topItem = {
|
|
||||||
Id: nowPlayingItem.AlbumId,
|
|
||||||
Name: nowPlayingItem.Album,
|
|
||||||
Type: 'MusicAlbum',
|
|
||||||
IsFolder: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nowPlayingItem.MediaType == 'Video') {
|
|
||||||
if (nowPlayingItem.IndexNumber != null) {
|
|
||||||
topText = nowPlayingItem.IndexNumber + " - " + topText;
|
|
||||||
}
|
|
||||||
if (nowPlayingItem.ParentIndexNumber != null) {
|
|
||||||
topText = nowPlayingItem.ParentIndexNumber + "." + topText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bottomText = '';
|
|
||||||
|
|
||||||
if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) {
|
|
||||||
|
|
||||||
if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) {
|
|
||||||
|
|
||||||
bottomItem = {
|
|
||||||
Id: nowPlayingItem.ArtistItems[0].Id,
|
|
||||||
Name: nowPlayingItem.ArtistItems[0].Name,
|
|
||||||
Type: 'MusicArtist',
|
|
||||||
IsFolder: true
|
|
||||||
};
|
|
||||||
|
|
||||||
bottomText = bottomItem.Name;
|
|
||||||
} else {
|
|
||||||
bottomText = nowPlayingItem.Artists[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) {
|
|
||||||
bottomText = topText;
|
|
||||||
topText = nowPlayingItem.SeriesName || nowPlayingItem.Album;
|
|
||||||
|
|
||||||
bottomItem = topItem;
|
|
||||||
|
|
||||||
if (nowPlayingItem.SeriesId) {
|
|
||||||
topItem = {
|
|
||||||
Id: nowPlayingItem.SeriesId,
|
|
||||||
Name: nowPlayingItem.SeriesName,
|
|
||||||
Type: 'Series',
|
|
||||||
IsFolder: true
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
topItem = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) {
|
|
||||||
bottomText = nowPlayingItem.ProductionYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = [];
|
|
||||||
|
|
||||||
list.push({
|
|
||||||
text: topText,
|
|
||||||
item: topItem
|
|
||||||
});
|
|
||||||
|
|
||||||
if (bottomText) {
|
|
||||||
list.push({
|
|
||||||
text: bottomText,
|
|
||||||
item: bottomItem
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TOOD: This doesn't really belong here
|
|
||||||
self.getNowPlayingNameHtml = function (nowPlayingItem, includeNonNameInfo) {
|
|
||||||
|
|
||||||
var names = self.getNowPlayingNames(nowPlayingItem, includeNonNameInfo);
|
|
||||||
|
|
||||||
return names.map(function (i) {
|
|
||||||
|
|
||||||
return i.text;
|
|
||||||
|
|
||||||
}).join('<br/>');
|
|
||||||
};
|
|
||||||
|
|
||||||
self.showPlaybackInfoErrorMessage = function (errorCode) {
|
|
||||||
|
|
||||||
require(['alert'], function (alert) {
|
|
||||||
alert({
|
|
||||||
title: Globalize.translate('HeaderPlaybackError'),
|
|
||||||
text: Globalize.translate('MessagePlaybackError' + errorCode),
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
function getPlaybackInfoFromLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource) {
|
|
||||||
|
|
||||||
mediaSource.SupportsDirectPlay = true;
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
MediaSources: [mediaSource],
|
|
||||||
|
|
||||||
// Just dummy this up
|
|
||||||
PlaySessionId: new Date().getTime().toString()
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
self.getPlaybackInfo = function (itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId) {
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
require([], function () {
|
|
||||||
//require(['localassetmanager'], function (LocalAssetManager) {
|
|
||||||
|
|
||||||
var serverInfo = ApiClient.serverInfo();
|
|
||||||
|
|
||||||
//if (serverInfo.Id) {
|
|
||||||
// LocalAssetManager.getLocalMediaSource(serverInfo.Id, itemId).then(function (localMediaSource) {
|
|
||||||
// // Use the local media source if a specific one wasn't requested, or the smae one was requested
|
|
||||||
// if (localMediaSource && (!mediaSource || mediaSource.Id == localMediaSource.Id)) {
|
|
||||||
|
|
||||||
// var playbackInfo = getPlaybackInfoFromLocalMediaSource(itemId, deviceProfile, startPosition, localMediaSource);
|
|
||||||
|
|
||||||
// resolve(playbackInfo);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// getPlaybackInfoWithoutLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId, resolve, reject);
|
|
||||||
// });
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
getPlaybackInfoWithoutLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId, resolve, reject);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPlaybackInfoWithoutLocalMediaSource(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId, resolve, reject) {
|
|
||||||
self.getPlaybackInfoInternal(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId).then(resolve, reject);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.getPlaybackInfoInternal = function (itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId) {
|
|
||||||
|
|
||||||
var postData = {
|
|
||||||
DeviceProfile: deviceProfile
|
|
||||||
};
|
|
||||||
|
|
||||||
var query = {
|
|
||||||
UserId: Dashboard.getCurrentUserId(),
|
|
||||||
StartTimeTicks: startPosition || 0
|
|
||||||
};
|
|
||||||
|
|
||||||
if (audioStreamIndex != null) {
|
|
||||||
query.AudioStreamIndex = audioStreamIndex;
|
|
||||||
}
|
|
||||||
if (subtitleStreamIndex != null) {
|
|
||||||
query.SubtitleStreamIndex = subtitleStreamIndex;
|
|
||||||
}
|
|
||||||
if (mediaSource) {
|
|
||||||
query.MediaSourceId = mediaSource.Id;
|
|
||||||
}
|
|
||||||
if (liveStreamId) {
|
|
||||||
query.LiveStreamId = liveStreamId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiClient.ajax({
|
|
||||||
url: ApiClient.getUrl('Items/' + itemId + '/PlaybackInfo', query),
|
|
||||||
type: 'POST',
|
|
||||||
data: JSON.stringify(postData),
|
|
||||||
contentType: "application/json",
|
|
||||||
dataType: "json"
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.getLiveStream = function (itemId, playSessionId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex) {
|
|
||||||
|
|
||||||
var postData = {
|
|
||||||
DeviceProfile: deviceProfile,
|
|
||||||
OpenToken: mediaSource.OpenToken
|
|
||||||
};
|
|
||||||
|
|
||||||
var query = {
|
|
||||||
UserId: Dashboard.getCurrentUserId(),
|
|
||||||
StartTimeTicks: startPosition || 0,
|
|
||||||
ItemId: itemId,
|
|
||||||
PlaySessionId: playSessionId
|
|
||||||
};
|
|
||||||
|
|
||||||
if (audioStreamIndex != null) {
|
|
||||||
query.AudioStreamIndex = audioStreamIndex;
|
|
||||||
}
|
|
||||||
if (subtitleStreamIndex != null) {
|
|
||||||
query.SubtitleStreamIndex = subtitleStreamIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiClient.ajax({
|
|
||||||
url: ApiClient.getUrl('LiveStreams/Open', query),
|
|
||||||
type: 'POST',
|
|
||||||
data: JSON.stringify(postData),
|
|
||||||
contentType: "application/json",
|
|
||||||
dataType: "json"
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.supportsDirectPlay = function (mediaSource, itemType) {
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
if (mediaSource.SupportsDirectPlay) {
|
|
||||||
|
|
||||||
if (mediaSource.Protocol == 'Http' && !mediaSource.RequiredHttpHeaders.length) {
|
|
||||||
|
|
||||||
// If this is the only way it can be played, then allow it
|
|
||||||
if (!mediaSource.SupportsDirectStream && !mediaSource.SupportsTranscoding) {
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var val = mediaSource.Path.toLowerCase().replace('https:', 'http').indexOf(ApiClient.serverAddress().toLowerCase().replace('https:', 'http').substring(0, 14)) == 0;
|
|
||||||
//resolve(val || itemType !== 'TvChannel');
|
|
||||||
resolve(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mediaSource.Protocol == 'File') {
|
|
||||||
|
|
||||||
require(['filesystem'], function (fileSystem) {
|
|
||||||
|
|
||||||
fileSystem.fileExists(mediaSource.Path).then(function () {
|
|
||||||
console.log('fileSystem.fileExists: path: ' + mediaSource.Path + ' result: ' + true);
|
|
||||||
resolve(true);
|
|
||||||
}, function () {
|
|
||||||
console.log('fileSystem.fileExists: path: ' + mediaSource.Path + ' result: ' + false);
|
|
||||||
resolve(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
window.MediaController = new mediaController();
|
|
||||||
|
|
||||||
function onWebSocketMessageReceived(e, msg) {
|
|
||||||
|
|
||||||
if (msg.MessageType === "ServerShuttingDown") {
|
|
||||||
MediaController.setDefaultPlayerActive();
|
|
||||||
}
|
|
||||||
else if (msg.MessageType === "ServerRestarting") {
|
|
||||||
MediaController.setDefaultPlayerActive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeApiClient(apiClient) {
|
|
||||||
events.off(apiClient, "websocketmessage", onWebSocketMessageReceived);
|
|
||||||
events.on(apiClient, "websocketmessage", onWebSocketMessageReceived);
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaController.init = function () {
|
|
||||||
|
|
||||||
console.log('Beginning MediaController.init');
|
|
||||||
require(['datetime'], function (datetimeInstance) {
|
|
||||||
datetime = datetimeInstance;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (window.ApiClient) {
|
|
||||||
initializeApiClient(window.ApiClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
events.on(ConnectionManager, 'apiclientcreated', function (e, apiClient) {
|
|
||||||
initializeApiClient(apiClient);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
});
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
||||||
define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'dom', 'apphost', 'imageLoader', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, categorysyncbuttons, cardBuilder, dom, appHost, imageLoader) {
|
define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'dom', 'apphost', 'imageLoader', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, categorysyncbuttons, cardBuilder, dom, appHost, imageLoader, playbackManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function enableScrollX() {
|
function enableScrollX() {
|
||||||
|
@ -332,11 +332,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
view.addEventListener('viewshow', function (e) {
|
view.addEventListener('viewshow', function (e) {
|
||||||
Events.on(MediaController, 'playbackstop', onPlaybackStop);
|
Events.on(playbackManager, 'playbackstop', onPlaybackStop);
|
||||||
});
|
});
|
||||||
|
|
||||||
view.addEventListener('viewbeforehide', function (e) {
|
view.addEventListener('viewbeforehide', function (e) {
|
||||||
Events.off(MediaController, 'playbackstop', onPlaybackStop);
|
Events.off(playbackManager, 'playbackstop', onPlaybackStop);
|
||||||
});
|
});
|
||||||
|
|
||||||
require(["headroom-window"], function (headroom) {
|
require(["headroom-window"], function (headroom) {
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Dashboard.isConnectMode()) {
|
if (appHost.supports('multiserver')) {
|
||||||
page.querySelector('.selectServer').classList.remove('hide');
|
page.querySelector('.selectServer').classList.remove('hide');
|
||||||
} else {
|
} else {
|
||||||
page.querySelector('.selectServer').classList.add('hide');
|
page.querySelector('.selectServer').classList.add('hide');
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
define(['datetime', 'userdataButtons', 'itemHelper', 'events', 'browser', 'imageLoader', 'paper-icon-button-light'], function (datetime, userdataButtons, itemHelper, events, browser, imageLoader) {
|
define(['datetime', 'userdataButtons', 'itemHelper', 'events', 'browser', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'apphost', 'dom', 'paper-icon-button-light'], function (datetime, userdataButtons, itemHelper, events, browser, imageLoader, playbackManager, nowPlayingHelper, appHost, dom) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var currentPlayer;
|
var currentPlayer;
|
||||||
|
var currentPlayerSupportedCommands = [];
|
||||||
|
|
||||||
var currentTimeElement;
|
var currentTimeElement;
|
||||||
var nowPlayingImageElement;
|
var nowPlayingImageElement;
|
||||||
|
@ -17,13 +18,16 @@
|
||||||
var toggleRepeatButton;
|
var toggleRepeatButton;
|
||||||
var toggleRepeatButtonIcon;
|
var toggleRepeatButtonIcon;
|
||||||
|
|
||||||
var lastPlayerState;
|
var lastUpdateTime = 0;
|
||||||
|
var lastPlayerState = {};
|
||||||
|
var isEnabled;
|
||||||
|
var currentRuntimeTicks = 0;
|
||||||
|
|
||||||
function getNowPlayingBarHtml() {
|
function getNowPlayingBarHtml() {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
html += '<div class="nowPlayingBar hide">';
|
html += '<div class="nowPlayingBar hide nowPlayingBar-hidden">';
|
||||||
|
|
||||||
html += '<div class="nowPlayingBarTop">';
|
html += '<div class="nowPlayingBarTop">';
|
||||||
html += '<div class="nowPlayingBarPositionContainer sliderContainer">';
|
html += '<div class="nowPlayingBarPositionContainer sliderContainer">';
|
||||||
|
@ -76,49 +80,39 @@
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSlideDownComplete() {
|
||||||
|
this.classList.add('hide');
|
||||||
|
}
|
||||||
|
|
||||||
function slideDown(elem) {
|
function slideDown(elem) {
|
||||||
|
|
||||||
if (elem.classList.contains('hide')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var onfinish = function () {
|
|
||||||
elem.classList.add('hide');
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!browser.animate || browser.slow) {
|
|
||||||
onfinish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
var keyframes = [
|
|
||||||
{ transform: 'none', offset: 0 },
|
// trigger reflow
|
||||||
{ transform: 'translateY(100%)', offset: 1 }];
|
void elem.offsetWidth;
|
||||||
var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' };
|
|
||||||
elem.animate(keyframes, timing).onfinish = onfinish;
|
elem.classList.add('nowPlayingBar-hidden');
|
||||||
|
|
||||||
|
dom.addEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, {
|
||||||
|
once: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function slideUp(elem) {
|
function slideUp(elem) {
|
||||||
|
|
||||||
if (!elem.classList.contains('hide')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
elem.classList.remove('hide');
|
elem.classList.remove('hide');
|
||||||
|
|
||||||
if (!browser.animate || browser.slow) {
|
dom.removeEventListener(elem, dom.whichTransitionEvent(), onSlideDownComplete, {
|
||||||
return;
|
once: true
|
||||||
}
|
});
|
||||||
|
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
|
|
||||||
var keyframes = [
|
// trigger reflow
|
||||||
{ transform: 'translateY(100%)', offset: 0 },
|
void elem.offsetWidth;
|
||||||
{ transform: 'none', offset: 1 }];
|
|
||||||
var timing = { duration: 200, iterations: 1, fill: 'both', easing: 'ease-out' };
|
elem.classList.remove('nowPlayingBar-hidden');
|
||||||
elem.animate(keyframes, timing);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +161,7 @@
|
||||||
unmuteButton.addEventListener('click', function () {
|
unmuteButton.addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.unMute();
|
currentPlayer.setMute(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -176,7 +170,7 @@
|
||||||
muteButton.addEventListener('click', function () {
|
muteButton.addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.mute();
|
currentPlayer.setMute(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -184,7 +178,7 @@
|
||||||
elem.querySelector('.stopButton').addEventListener('click', function () {
|
elem.querySelector('.stopButton').addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.stop();
|
playbackManager.stop(currentPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -201,14 +195,14 @@
|
||||||
elem.querySelector('.nextTrackButton').addEventListener('click', function () {
|
elem.querySelector('.nextTrackButton').addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.nextTrack();
|
playbackManager.nextTrack(currentPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
elem.querySelector('.previousTrackButton').addEventListener('click', function () {
|
elem.querySelector('.previousTrackButton').addEventListener('click', function () {
|
||||||
|
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
currentPlayer.previousTrack();
|
playbackManager.previousTrack(currentPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -230,13 +224,13 @@
|
||||||
|
|
||||||
switch ((state.PlayState || {}).RepeatMode) {
|
switch ((state.PlayState || {}).RepeatMode) {
|
||||||
case 'RepeatAll':
|
case 'RepeatAll':
|
||||||
currentPlayer.setRepeatMode('RepeatOne');
|
playbackManager.setRepeatMode('RepeatOne', currentPlayer);
|
||||||
break;
|
break;
|
||||||
case 'RepeatOne':
|
case 'RepeatOne':
|
||||||
currentPlayer.setRepeatMode('RepeatNone');
|
playbackManager.setRepeatMode('RepeatNone', currentPlayer);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
currentPlayer.setRepeatMode('RepeatAll');
|
playbackManager.setRepeatMode('RepeatAll', currentPlayer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +241,7 @@
|
||||||
volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider');
|
volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider');
|
||||||
volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer');
|
volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer');
|
||||||
|
|
||||||
if (AppInfo.hasPhysicalVolumeButtons) {
|
if (appHost.supports('physicalvolumecontrol')) {
|
||||||
volumeSliderContainer.classList.add('hide');
|
volumeSliderContainer.classList.add('hide');
|
||||||
} else {
|
} else {
|
||||||
volumeSliderContainer.classList.remove('hide');
|
volumeSliderContainer.classList.remove('hide');
|
||||||
|
@ -264,12 +258,11 @@
|
||||||
positionSlider = elem.querySelector('.nowPlayingBarPositionSlider');
|
positionSlider = elem.querySelector('.nowPlayingBarPositionSlider');
|
||||||
positionSlider.addEventListener('change', function () {
|
positionSlider.addEventListener('change', function () {
|
||||||
|
|
||||||
if (currentPlayer && lastPlayerState) {
|
if (currentPlayer) {
|
||||||
|
|
||||||
var newPercent = parseFloat(this.value);
|
var newPercent = parseFloat(this.value);
|
||||||
var newPositionTicks = (newPercent / 100) * lastPlayerState.NowPlayingItem.RunTimeTicks;
|
|
||||||
|
|
||||||
currentPlayer.seek(Math.floor(newPositionTicks));
|
playbackManager.seekPercent(newPercent, currentPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -278,11 +271,11 @@
|
||||||
|
|
||||||
var state = lastPlayerState;
|
var state = lastPlayerState;
|
||||||
|
|
||||||
if (!state || !state.NowPlayingItem || !state.NowPlayingItem.RunTimeTicks) {
|
if (!state || !state.NowPlayingItem || !currentRuntimeTicks) {
|
||||||
return '--:--';
|
return '--:--';
|
||||||
}
|
}
|
||||||
|
|
||||||
var ticks = state.NowPlayingItem.RunTimeTicks;
|
var ticks = currentRuntimeTicks;
|
||||||
ticks /= 100;
|
ticks /= 100;
|
||||||
ticks *= value;
|
ticks *= value;
|
||||||
|
|
||||||
|
@ -342,47 +335,11 @@
|
||||||
button.classList.add('hide');
|
button.classList.add('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastUpdateTime = 0;
|
function updatePlayPauseState(isPaused) {
|
||||||
|
|
||||||
function updatePlayerState(event, state) {
|
|
||||||
|
|
||||||
if (!state.NowPlayingItem) {
|
|
||||||
hideNowPlayingBar();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nowPlayingBarElement) {
|
|
||||||
updatePlayerStateInternal(event, state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getNowPlayingBar().then(function () {
|
|
||||||
updatePlayerStateInternal(event, state);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePlayerStateInternal(event, state) {
|
|
||||||
|
|
||||||
showNowPlayingBar();
|
|
||||||
|
|
||||||
if (event.type == 'positionchange') {
|
|
||||||
// Try to avoid hammering the document with changes
|
|
||||||
var now = new Date().getTime();
|
|
||||||
if ((now - lastUpdateTime) < 700) {
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastUpdateTime = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastPlayerState = state;
|
|
||||||
|
|
||||||
var playerInfo = MediaController.getPlayerInfo();
|
|
||||||
|
|
||||||
var playState = state.PlayState || {};
|
|
||||||
var i, length;
|
var i, length;
|
||||||
|
|
||||||
if (playState.IsPaused) {
|
if (isPaused) {
|
||||||
|
|
||||||
for (i = 0, length = pauseButtons.length; i < length; i++) {
|
for (i = 0, length = pauseButtons.length; i < length; i++) {
|
||||||
hideButton(pauseButtons[i]);
|
hideButton(pauseButtons[i]);
|
||||||
|
@ -400,70 +357,22 @@
|
||||||
hideButton(unpauseButtons[i]);
|
hideButton(unpauseButtons[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlayerVolumeState(state, playerInfo);
|
|
||||||
|
|
||||||
var nowPlayingItem = state.NowPlayingItem || {};
|
|
||||||
|
|
||||||
// See bindEvents for why this is necessary
|
|
||||||
if (positionSlider) {
|
|
||||||
if (!positionSlider.dragging) {
|
|
||||||
|
|
||||||
if (nowPlayingItem.RunTimeTicks) {
|
|
||||||
|
|
||||||
var pct = playState.PositionTicks / nowPlayingItem.RunTimeTicks;
|
|
||||||
pct *= 100;
|
|
||||||
|
|
||||||
positionSlider.value = pct;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
positionSlider.value = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
positionSlider.disabled = !playState.CanSeek;
|
function updatePlayerStateInternal(event, state) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeText = playState.PositionTicks == null ? '--:--' : datetime.getDisplayRunningTime(playState.PositionTicks);
|
showNowPlayingBar();
|
||||||
|
|
||||||
if (nowPlayingItem.RunTimeTicks) {
|
lastPlayerState = state;
|
||||||
|
|
||||||
timeText += " / " + datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks);
|
var playerInfo = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTimeElement.innerHTML = timeText;
|
|
||||||
|
|
||||||
updateNowPlayingInfo(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePlayerVolumeState(state, playerInfo) {
|
|
||||||
|
|
||||||
playerInfo = playerInfo || MediaController.getPlayerInfo();
|
|
||||||
|
|
||||||
var playState = state.PlayState || {};
|
var playState = state.PlayState || {};
|
||||||
|
|
||||||
|
updatePlayPauseState(playState.IsPaused);
|
||||||
|
|
||||||
var supportedCommands = playerInfo.supportedCommands;
|
var supportedCommands = playerInfo.supportedCommands;
|
||||||
|
currentPlayerSupportedCommands = supportedCommands;
|
||||||
var showMuteButton = true;
|
|
||||||
var showUnmuteButton = true;
|
|
||||||
var showVolumeSlider = true;
|
|
||||||
|
|
||||||
if (supportedCommands.indexOf('Mute') == -1) {
|
|
||||||
showMuteButton = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (supportedCommands.indexOf('Unmute') == -1) {
|
|
||||||
showUnmuteButton = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playState.IsMuted) {
|
|
||||||
|
|
||||||
showMuteButton = false;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
showUnmuteButton = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (supportedCommands.indexOf('SetRepeatMode') == -1) {
|
if (supportedCommands.indexOf('SetRepeatMode') == -1) {
|
||||||
toggleRepeatButton.classList.add('hide');
|
toggleRepeatButton.classList.add('hide');
|
||||||
|
@ -483,11 +392,73 @@
|
||||||
toggleRepeatButton.classList.remove('repeatActive');
|
toggleRepeatButton.classList.remove('repeatActive');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePlayerVolumeState(playState.IsMuted, playState.VolumeLevel);
|
||||||
|
|
||||||
|
if (positionSlider && !positionSlider.dragging) {
|
||||||
|
positionSlider.disabled = !playState.CanSeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nowPlayingItem = state.NowPlayingItem || {};
|
||||||
|
updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks);
|
||||||
|
|
||||||
|
updateNowPlayingInfo(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTimeDisplay(positionTicks, runtimeTicks) {
|
||||||
|
|
||||||
|
// See bindEvents for why this is necessary
|
||||||
|
if (positionSlider && !positionSlider.dragging) {
|
||||||
|
if (runtimeTicks) {
|
||||||
|
|
||||||
|
var pct = positionTicks / runtimeTicks;
|
||||||
|
pct *= 100;
|
||||||
|
|
||||||
|
positionSlider.value = pct;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
positionSlider.value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeText = positionTicks == null ? '--:--' : datetime.getDisplayRunningTime(positionTicks);
|
||||||
|
|
||||||
|
if (runtimeTicks) {
|
||||||
|
timeText += " / " + datetime.getDisplayRunningTime(runtimeTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTimeElement.innerHTML = timeText;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePlayerVolumeState(isMuted, volumeLevel) {
|
||||||
|
|
||||||
|
var supportedCommands = currentPlayerSupportedCommands;
|
||||||
|
|
||||||
|
var showMuteButton = true;
|
||||||
|
var showUnmuteButton = true;
|
||||||
|
var showVolumeSlider = true;
|
||||||
|
|
||||||
|
if (supportedCommands.indexOf('Mute') == -1) {
|
||||||
|
showMuteButton = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportedCommands.indexOf('Unmute') == -1) {
|
||||||
|
showUnmuteButton = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMuted) {
|
||||||
|
|
||||||
|
showMuteButton = false;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
showUnmuteButton = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (supportedCommands.indexOf('SetVolume') == -1) {
|
if (supportedCommands.indexOf('SetVolume') == -1) {
|
||||||
showVolumeSlider = false;
|
showVolumeSlider = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerInfo.isLocalPlayer && AppInfo.hasPhysicalVolumeButtons) {
|
if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) {
|
||||||
showMuteButton = false;
|
showMuteButton = false;
|
||||||
showUnmuteButton = false;
|
showUnmuteButton = false;
|
||||||
showVolumeSlider = false;
|
showVolumeSlider = false;
|
||||||
|
@ -515,7 +486,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!volumeSlider.dragging) {
|
if (!volumeSlider.dragging) {
|
||||||
volumeSlider.value = playState.VolumeLevel || 0;
|
volumeSlider.value = volumeLevel || 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -536,7 +507,7 @@
|
||||||
var currentImgUrl;
|
var currentImgUrl;
|
||||||
function updateNowPlayingInfo(state) {
|
function updateNowPlayingInfo(state) {
|
||||||
|
|
||||||
nowPlayingTextElement.innerHTML = MediaController.getNowPlayingNames(state.NowPlayingItem).map(function (nowPlayingName) {
|
nowPlayingTextElement.innerHTML = nowPlayingHelper.getNowPlayingNames(state.NowPlayingItem).map(function (nowPlayingName) {
|
||||||
|
|
||||||
if (nowPlayingName.item) {
|
if (nowPlayingName.item) {
|
||||||
return '<div>' + getTextActionButton(nowPlayingName.item, nowPlayingName.text) + '</div>';
|
return '<div>' + getTextActionButton(nowPlayingName.item, nowPlayingName.text) + '</div>';
|
||||||
|
@ -616,8 +587,6 @@
|
||||||
|
|
||||||
var player = this;
|
var player = this;
|
||||||
|
|
||||||
player.beginPlayerUpdates();
|
|
||||||
|
|
||||||
onStateChanged.call(player, e, state);
|
onStateChanged.call(player, e, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,6 +597,8 @@
|
||||||
|
|
||||||
function hideNowPlayingBar() {
|
function hideNowPlayingBar() {
|
||||||
|
|
||||||
|
isEnabled = false;
|
||||||
|
|
||||||
// Use a timeout to prevent the bar from hiding and showing quickly
|
// Use a timeout to prevent the bar from hiding and showing quickly
|
||||||
// in the event of a stop->play command
|
// in the event of a stop->play command
|
||||||
|
|
||||||
|
@ -643,83 +614,130 @@
|
||||||
console.log('nowplaying event: ' + e.type);
|
console.log('nowplaying event: ' + e.type);
|
||||||
var player = this;
|
var player = this;
|
||||||
|
|
||||||
player.endPlayerUpdates();
|
playbackManager.endPlayerUpdates(player);
|
||||||
|
|
||||||
hideNowPlayingBar();
|
hideNowPlayingBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStateChanged(e, state) {
|
function onPlayPauseStateChanged(e) {
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = this;
|
||||||
|
updatePlayPauseState(player.paused());
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStateChanged(event, state) {
|
||||||
|
|
||||||
//console.log('nowplaying event: ' + e.type);
|
//console.log('nowplaying event: ' + e.type);
|
||||||
var player = this;
|
var player = this;
|
||||||
|
|
||||||
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
|
if (!state.NowPlayingItem) {
|
||||||
|
hideNowPlayingBar();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlayerState(e, state);
|
if (player.isLocalPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
|
||||||
|
hideNowPlayingBar();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnabled = true;
|
||||||
|
playbackManager.beginPlayerUpdates(player);
|
||||||
|
|
||||||
|
if (nowPlayingBarElement) {
|
||||||
|
updatePlayerStateInternal(event, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNowPlayingBar().then(function () {
|
||||||
|
updatePlayerStateInternal(event, state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTimeUpdate(e) {
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to avoid hammering the document with changes
|
||||||
|
var now = new Date().getTime();
|
||||||
|
if ((now - lastUpdateTime) < 700) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastUpdateTime = now;
|
||||||
|
|
||||||
|
var player = this;
|
||||||
|
var state = lastPlayerState;
|
||||||
|
var nowPlayingItem = state.NowPlayingItem || {};
|
||||||
|
currentRuntimeTicks = playbackManager.duration(player);
|
||||||
|
updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
function releaseCurrentPlayer() {
|
function releaseCurrentPlayer() {
|
||||||
|
|
||||||
if (currentPlayer) {
|
var player = currentPlayer;
|
||||||
|
|
||||||
events.off(currentPlayer, 'playbackstart', onPlaybackStart);
|
if (player) {
|
||||||
events.off(currentPlayer, 'playbackstop', onPlaybackStopped);
|
events.off(player, 'playbackstart', onPlaybackStart);
|
||||||
events.off(currentPlayer, 'volumechange', onVolumeChanged);
|
events.off(player, 'playbackstop', onPlaybackStopped);
|
||||||
events.off(currentPlayer, 'playstatechange', onStateChanged);
|
events.off(player, 'volumechange', onVolumeChanged);
|
||||||
events.off(currentPlayer, 'positionchange', onStateChanged);
|
events.off(player, 'pause', onPlayPauseStateChanged);
|
||||||
|
events.off(player, 'playing', onPlayPauseStateChanged);
|
||||||
|
events.off(player, 'timeupdate', onTimeUpdate);
|
||||||
|
|
||||||
currentPlayer.endPlayerUpdates();
|
playbackManager.endPlayerUpdates(player);
|
||||||
currentPlayer = null;
|
currentPlayer = null;
|
||||||
|
|
||||||
hideNowPlayingBar();
|
hideNowPlayingBar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onVolumeChanged(e) {
|
function onVolumeChanged(e) {
|
||||||
|
|
||||||
var player = this;
|
if (!isEnabled) {
|
||||||
|
|
||||||
Promise.all([player.getPlayerState(), getNowPlayingBar()]).then(function (responses) {
|
|
||||||
|
|
||||||
var state = responses[0];
|
|
||||||
|
|
||||||
if (player.isDefaultPlayer && state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlayerVolumeState(state);
|
var player = this;
|
||||||
});
|
|
||||||
|
updatePlayerVolumeState(player.isMuted(), player.getVolume());
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindToPlayer(player) {
|
function bindToPlayer(player) {
|
||||||
|
|
||||||
|
if (player === currentPlayer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
releaseCurrentPlayer();
|
releaseCurrentPlayer();
|
||||||
|
|
||||||
currentPlayer = player;
|
currentPlayer = player;
|
||||||
|
|
||||||
player.getPlayerState().then(function (state) {
|
if (!player) {
|
||||||
|
return;
|
||||||
if (state.NowPlayingItem) {
|
|
||||||
player.beginPlayerUpdates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playbackManager.getPlayerState(player).then(function (state) {
|
||||||
|
|
||||||
onStateChanged.call(player, { type: 'init' }, state);
|
onStateChanged.call(player, { type: 'init' }, state);
|
||||||
});
|
});
|
||||||
|
|
||||||
events.on(player, 'playbackstart', onPlaybackStart);
|
events.on(player, 'playbackstart', onPlaybackStart);
|
||||||
events.on(player, 'playbackstop', onPlaybackStopped);
|
events.on(player, 'playbackstop', onPlaybackStopped);
|
||||||
events.on(player, 'volumechange', onVolumeChanged);
|
events.on(player, 'volumechange', onVolumeChanged);
|
||||||
events.on(player, 'playstatechange', onStateChanged);
|
events.on(player, 'pause', onPlayPauseStateChanged);
|
||||||
events.on(player, 'positionchange', onStateChanged);
|
events.on(player, 'playing', onPlayPauseStateChanged);
|
||||||
|
events.on(player, 'timeupdate', onTimeUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
events.on(MediaController, 'playerchange', function () {
|
events.on(playbackManager, 'playerchange', function () {
|
||||||
|
bindToPlayer(playbackManager.getCurrentPlayer());
|
||||||
bindToPlayer(MediaController.getCurrentPlayer());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bindToPlayer(MediaController.getCurrentPlayer());
|
bindToPlayer(playbackManager.getCurrentPlayer());
|
||||||
|
|
||||||
});
|
});
|
|
@ -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;
|
url = '/' + url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Emby.Page.show(url);
|
return Emby.Page.show(url);
|
||||||
},
|
},
|
||||||
|
|
||||||
showLoadingMsg: function () {
|
showLoadingMsg: function () {
|
||||||
|
@ -584,11 +584,6 @@ var Dashboard = {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isServerlessPage: function () {
|
|
||||||
var url = window.location.href.toLowerCase();
|
|
||||||
return url.indexOf('connectlogin.html') != -1 || url.indexOf('selectserver.html') != -1 || url.indexOf('login.html') != -1 || url.indexOf('forgotpassword.html') != -1 || url.indexOf('forgotpasswordpin.html') != -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
capabilities: function () {
|
capabilities: function () {
|
||||||
|
|
||||||
var caps = {
|
var caps = {
|
||||||
|
@ -844,20 +839,7 @@ var Dashboard = {
|
||||||
|
|
||||||
require(['browserdeviceprofile', 'qualityoptions', 'appSettings'], function (profileBuilder, qualityoptions, appSettings) {
|
require(['browserdeviceprofile', 'qualityoptions', 'appSettings'], function (profileBuilder, qualityoptions, appSettings) {
|
||||||
|
|
||||||
var supportsCustomSeeking = false;
|
|
||||||
if (!browserInfo.mobile) {
|
|
||||||
supportsCustomSeeking = true;
|
|
||||||
} else if (AppInfo.isNativeApp && browserInfo.safari) {
|
|
||||||
if (navigator.userAgent.toLowerCase().indexOf('ipad') == -1) {
|
|
||||||
// Need to disable it in order to support picture in picture
|
|
||||||
supportsCustomSeeking = true;
|
|
||||||
}
|
|
||||||
} else if (AppInfo.isNativeApp) {
|
|
||||||
supportsCustomSeeking = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var profile = profileBuilder(Object.assign(profileOptions || {}, {
|
var profile = profileBuilder(Object.assign(profileOptions || {}, {
|
||||||
supportsCustomSeeking: supportsCustomSeeking
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (!(AppInfo.isNativeApp && browserInfo.android) && !browserInfo.edge && !browserInfo.msie) {
|
if (!(AppInfo.isNativeApp && browserInfo.android) && !browserInfo.edge && !browserInfo.msie) {
|
||||||
|
@ -935,8 +917,6 @@ var AppInfo = {};
|
||||||
// This currently isn't working on android, unfortunately
|
// This currently isn't working on android, unfortunately
|
||||||
AppInfo.supportsFileInput = !(AppInfo.isNativeApp && isAndroid);
|
AppInfo.supportsFileInput = !(AppInfo.isNativeApp && isAndroid);
|
||||||
|
|
||||||
AppInfo.hasPhysicalVolumeButtons = isCordova || browserInfo.mobile;
|
|
||||||
|
|
||||||
if (isCordova && isIOS) {
|
if (isCordova && isIOS) {
|
||||||
AppInfo.moreIcon = 'more-horiz';
|
AppInfo.moreIcon = 'more-horiz';
|
||||||
} else {
|
} else {
|
||||||
|
@ -1204,7 +1184,10 @@ var AppInfo = {};
|
||||||
itemHelper: embyWebComponentsBowerPath + '/itemhelper',
|
itemHelper: embyWebComponentsBowerPath + '/itemhelper',
|
||||||
itemShortcuts: embyWebComponentsBowerPath + "/shortcuts",
|
itemShortcuts: embyWebComponentsBowerPath + "/shortcuts",
|
||||||
serverNotifications: embyWebComponentsBowerPath + '/servernotifications',
|
serverNotifications: embyWebComponentsBowerPath + '/servernotifications',
|
||||||
|
playbackManager: embyWebComponentsBowerPath + '/playback/playbackmanager',
|
||||||
|
nowPlayingHelper: embyWebComponentsBowerPath + '/playback/nowplayinghelper',
|
||||||
pluginManager: embyWebComponentsBowerPath + '/pluginmanager',
|
pluginManager: embyWebComponentsBowerPath + '/pluginmanager',
|
||||||
|
packageManager: embyWebComponentsBowerPath + '/packagemanager',
|
||||||
webAnimations: bowerPath + '/web-animations-js/web-animations-next-lite.min'
|
webAnimations: bowerPath + '/web-animations-js/web-animations-next-lite.min'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1287,6 +1270,8 @@ var AppInfo = {};
|
||||||
define("peoplecardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/peoplecardbuilder"], returnFirstDependency);
|
define("peoplecardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/peoplecardbuilder"], returnFirstDependency);
|
||||||
define("chaptercardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/chaptercardbuilder"], returnFirstDependency);
|
define("chaptercardbuilder", [embyWebComponentsBowerPath + "/cardbuilder/chaptercardbuilder"], returnFirstDependency);
|
||||||
|
|
||||||
|
define("mouseManager", [embyWebComponentsBowerPath + "/input/mouse"], returnFirstDependency);
|
||||||
|
|
||||||
define("deleteHelper", [embyWebComponentsBowerPath + "/deletehelper"], returnFirstDependency);
|
define("deleteHelper", [embyWebComponentsBowerPath + "/deletehelper"], returnFirstDependency);
|
||||||
define("tvguide", [embyWebComponentsBowerPath + "/guide/guide"], returnFirstDependency);
|
define("tvguide", [embyWebComponentsBowerPath + "/guide/guide"], returnFirstDependency);
|
||||||
define("programStyles", ['css!' + embyWebComponentsBowerPath + "/guide/programs"], returnFirstDependency);
|
define("programStyles", ['css!' + embyWebComponentsBowerPath + "/guide/programs"], returnFirstDependency);
|
||||||
|
@ -1447,6 +1432,8 @@ var AppInfo = {};
|
||||||
return dialoghelper;
|
return dialoghelper;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
define("inputmanager", ['inputManager'], returnFirstDependency);
|
||||||
|
|
||||||
// alias
|
// alias
|
||||||
define("historyManager", ['embyRouter'], returnFirstDependency);
|
define("historyManager", ['embyRouter'], returnFirstDependency);
|
||||||
|
|
||||||
|
@ -1454,60 +1441,6 @@ var AppInfo = {};
|
||||||
define("hammer-main", ['hammer'], createMainContentHammer);
|
define("hammer-main", ['hammer'], createMainContentHammer);
|
||||||
define("appfooter-shared", ['appfooter'], createSharedAppFooter);
|
define("appfooter-shared", ['appfooter'], createSharedAppFooter);
|
||||||
|
|
||||||
// mock this for now. not used in this app
|
|
||||||
define("playbackManager", [], function () {
|
|
||||||
return {
|
|
||||||
isPlaying: function () {
|
|
||||||
return MediaPlayer.currentItem != null;
|
|
||||||
},
|
|
||||||
isPlayingVideo: function () {
|
|
||||||
return MediaPlayer.currentItem != null;
|
|
||||||
},
|
|
||||||
play: function (options) {
|
|
||||||
|
|
||||||
if (options.fullscreen === false) {
|
|
||||||
// theme backdrops - not supported
|
|
||||||
if (!options.items || options.items[0].MediaType == 'Video') {
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MediaController.play(options);
|
|
||||||
},
|
|
||||||
queue: function (options) {
|
|
||||||
|
|
||||||
MediaController.queue(options);
|
|
||||||
},
|
|
||||||
currentPlaylistIndex: function (options) {
|
|
||||||
return MediaController.currentPlaylistIndex(options);
|
|
||||||
},
|
|
||||||
canQueueMediaType: function (mediaType) {
|
|
||||||
return MediaController.canQueueMediaType(mediaType);
|
|
||||||
},
|
|
||||||
canPlay: function (item) {
|
|
||||||
return MediaController.canPlay(item);
|
|
||||||
},
|
|
||||||
canQueue: function (item) {
|
|
||||||
return MediaController.canQueueMediaType(item.MediaType, item.Type);
|
|
||||||
},
|
|
||||||
instantMix: function (item) {
|
|
||||||
return MediaController.instantMix(item);
|
|
||||||
},
|
|
||||||
shuffle: function (item) {
|
|
||||||
return MediaController.shuffle(item);
|
|
||||||
},
|
|
||||||
pause: function () {
|
|
||||||
return MediaController.pause();
|
|
||||||
},
|
|
||||||
stop: function () {
|
|
||||||
return MediaController.stop();
|
|
||||||
},
|
|
||||||
seek: function (ticks) {
|
|
||||||
return MediaController.seek(ticks);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// mock this for now. not used in this app
|
// mock this for now. not used in this app
|
||||||
define("skinManager", [], function () {
|
define("skinManager", [], function () {
|
||||||
|
|
||||||
|
@ -1519,18 +1452,6 @@ var AppInfo = {};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// mock this for now. not used in this app
|
|
||||||
define("playbackManager", [], function () {
|
|
||||||
return {
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// mock this for now. not used in this app
|
|
||||||
define("pluginManager", [], function () {
|
|
||||||
return {
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
define("connectionManager", [], function () {
|
define("connectionManager", [], function () {
|
||||||
return ConnectionManager;
|
return ConnectionManager;
|
||||||
});
|
});
|
||||||
|
@ -1547,6 +1468,10 @@ var AppInfo = {};
|
||||||
Dashboard.navigate('login.html?serverid=' + serverId);
|
Dashboard.navigate('login.html?serverid=' + serverId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
embyRouter.showVideoOsd = function () {
|
||||||
|
return Dashboard.navigate('videoosd.html');
|
||||||
|
};
|
||||||
|
|
||||||
embyRouter.showSelectServer = function () {
|
embyRouter.showSelectServer = function () {
|
||||||
Dashboard.navigate('selectserver.html');
|
Dashboard.navigate('selectserver.html');
|
||||||
};
|
};
|
||||||
|
@ -1720,18 +1645,12 @@ var AppInfo = {};
|
||||||
|
|
||||||
if (Dashboard.isRunningInCordova() && browserInfo.android) {
|
if (Dashboard.isRunningInCordova() && browserInfo.android) {
|
||||||
|
|
||||||
//define("audiorenderer", ["scripts/htmlmediarenderer"]);
|
|
||||||
window.VlcAudio = true;
|
window.VlcAudio = true;
|
||||||
define("audiorenderer", ["cordova/android/vlcplayer"]);
|
define("audiorenderer", ["cordova/android/vlcplayer"]);
|
||||||
define("videorenderer", ["cordova/android/vlcplayer"]);
|
define("videorenderer", ["cordova/android/vlcplayer"]);
|
||||||
}
|
}
|
||||||
else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
|
else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
|
||||||
define("audiorenderer", ["cordova/audioplayer"]);
|
define("audiorenderer", ["cordova/audioplayer"]);
|
||||||
define("videorenderer", ["scripts/htmlmediarenderer"]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
define("audiorenderer", ["scripts/htmlmediarenderer"]);
|
|
||||||
define("videorenderer", ["scripts/htmlmediarenderer"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Dashboard.isRunningInCordova() && browserInfo.android) {
|
if (Dashboard.isRunningInCordova() && browserInfo.android) {
|
||||||
|
@ -1747,14 +1666,7 @@ var AppInfo = {};
|
||||||
|
|
||||||
define("buttonenabled", ["legacy/buttonenabled"]);
|
define("buttonenabled", ["legacy/buttonenabled"]);
|
||||||
|
|
||||||
var deps = [];
|
|
||||||
|
|
||||||
deps.push('scripts/mediacontroller');
|
|
||||||
|
|
||||||
require(deps, function () {
|
|
||||||
|
|
||||||
initAfterDependencies();
|
initAfterDependencies();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRequirePromise(deps) {
|
function getRequirePromise(deps) {
|
||||||
|
@ -1794,7 +1706,6 @@ var AppInfo = {};
|
||||||
createConnectionManager().then(function () {
|
createConnectionManager().then(function () {
|
||||||
|
|
||||||
console.log('initAfterDependencies promises resolved');
|
console.log('initAfterDependencies promises resolved');
|
||||||
MediaController.init();
|
|
||||||
|
|
||||||
require(['globalize'], function (globalize) {
|
require(['globalize'], function (globalize) {
|
||||||
|
|
||||||
|
@ -1850,7 +1761,7 @@ var AppInfo = {};
|
||||||
|
|
||||||
document.title = Globalize.translateDocument(document.title, 'core');
|
document.title = Globalize.translateDocument(document.title, 'core');
|
||||||
|
|
||||||
onAppReady();
|
loadPlugins([], browserInfo).then(onAppReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
function defineRoute(newRoute, dictionary) {
|
function defineRoute(newRoute, dictionary) {
|
||||||
|
@ -2619,6 +2530,16 @@ var AppInfo = {};
|
||||||
anonymous: true
|
anonymous: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineRoute({
|
||||||
|
path: '/videoosd.html',
|
||||||
|
dependencies: [],
|
||||||
|
transition: 'fade',
|
||||||
|
controller: 'scripts/videoosd',
|
||||||
|
autoFocus: false,
|
||||||
|
type: 'video-osd',
|
||||||
|
supportsThemeMedia: true
|
||||||
|
});
|
||||||
|
|
||||||
defineRoute({
|
defineRoute({
|
||||||
path: '/configurationpage',
|
path: '/configurationpage',
|
||||||
dependencies: ['jQuery'],
|
dependencies: ['jQuery'],
|
||||||
|
@ -2636,13 +2557,76 @@ var AppInfo = {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAppReady() {
|
function loadPlugins(externalPlugins, browser, shell) {
|
||||||
|
|
||||||
require(['scripts/mediaplayer'], function () {
|
console.log('Loading installed plugins');
|
||||||
|
|
||||||
MediaPlayer.init();
|
// Load installed plugins
|
||||||
|
|
||||||
|
var list = [
|
||||||
|
//'plugins/defaultskin/plugin',
|
||||||
|
//'plugins/logoscreensaver/plugin',
|
||||||
|
//'plugins/backdropscreensaver/plugin',
|
||||||
|
//'plugins/defaultsoundeffects/plugin',
|
||||||
|
//'plugins/playbackvalidation/plugin'
|
||||||
|
];
|
||||||
|
|
||||||
|
list.push('bower_components/emby-webcomponents/htmlaudioplayer/plugin');
|
||||||
|
list.push('bower_components/emby-webcomponents/htmlvideoplayer/plugin');
|
||||||
|
list.push('bower_components/emby-webcomponents/sessionplayer');
|
||||||
|
|
||||||
|
if (browser.chrome) {
|
||||||
|
list.push('bower_components/emby-webcomponents/chromecastplayer');
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push('bower_components/emby-webcomponents/youtubeplayer/plugin');
|
||||||
|
|
||||||
|
//if (globalScope.webapis && webapis.avplay) {
|
||||||
|
// list.push('plugins/tizenavplayer/plugin');
|
||||||
|
//} else {
|
||||||
|
// list.push('plugins/htmlvideoplayer/plugin');
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (!browser.tv) {
|
||||||
|
// list.push('plugins/confirmstillplaying/plugin');
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (!browser.keyboard) {
|
||||||
|
// list.push('plugins/keyboard/plugin');
|
||||||
|
//}
|
||||||
|
|
||||||
|
for (var i = 0, length = externalPlugins.length; i < length; i++) {
|
||||||
|
list.push(externalPlugins[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (shell.canExec) {
|
||||||
|
// list.push('plugins/externalplayer/plugin');
|
||||||
|
//}
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
|
||||||
|
Promise.all(list.map(loadPlugin)).then(function () {
|
||||||
|
|
||||||
|
require(['packageManager'], function (packageManager) {
|
||||||
|
packageManager.init().then(resolve, reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
}, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPlugin(url) {
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
|
||||||
|
require(['pluginManager'], function (pluginManager) {
|
||||||
|
pluginManager.loadPlugin(url).then(resolve, reject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAppReady() {
|
||||||
|
|
||||||
console.log('Begin onAppReady');
|
console.log('Begin onAppReady');
|
||||||
|
|
||||||
var deps = [];
|
var deps = [];
|
||||||
|
@ -2689,7 +2673,6 @@ var AppInfo = {};
|
||||||
var postInitDependencies = [];
|
var postInitDependencies = [];
|
||||||
|
|
||||||
postInitDependencies.push('bower_components/emby-webcomponents/thememediaplayer');
|
postInitDependencies.push('bower_components/emby-webcomponents/thememediaplayer');
|
||||||
postInitDependencies.push('scripts/remotecontrol');
|
|
||||||
postInitDependencies.push('css!css/chromecast.css');
|
postInitDependencies.push('css!css/chromecast.css');
|
||||||
postInitDependencies.push('scripts/autobackdrops');
|
postInitDependencies.push('scripts/autobackdrops');
|
||||||
|
|
||||||
|
@ -2708,9 +2691,6 @@ var AppInfo = {};
|
||||||
|
|
||||||
//postInitDependencies.push('cordova/backgroundfetch');
|
//postInitDependencies.push('cordova/backgroundfetch');
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (browserInfo.chrome) {
|
|
||||||
postInitDependencies.push('scripts/chromecast');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
postInitDependencies.push('scripts/nowplayingbar');
|
postInitDependencies.push('scripts/nowplayingbar');
|
||||||
|
@ -2724,6 +2704,7 @@ var AppInfo = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
postInitDependencies.push('bower_components/emby-webcomponents/input/api');
|
postInitDependencies.push('bower_components/emby-webcomponents/input/api');
|
||||||
|
postInitDependencies.push('mouseManager');
|
||||||
|
|
||||||
if (!browserInfo.tv) {
|
if (!browserInfo.tv) {
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['libraryBrowser', 'dom', 'components/categorysyncbuttons', 'cardBuilder', 'apphost', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, dom, categorysyncbuttons, cardBuilder, appHost) {
|
define(['libraryBrowser', 'dom', 'components/categorysyncbuttons', 'cardBuilder', 'apphost', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, dom, categorysyncbuttons, cardBuilder, appHost, playbackManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -274,13 +274,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Events.on(MediaController, 'playbackstop', onPlaybackStop);
|
Events.on(playbackManager, 'playbackstop', onPlaybackStop);
|
||||||
Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
|
Events.on(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
view.addEventListener('viewbeforehide', function (e) {
|
view.addEventListener('viewbeforehide', function (e) {
|
||||||
|
|
||||||
Events.off(MediaController, 'playbackstop', onPlaybackStop);
|
Events.off(playbackManager, 'playbackstop', onPlaybackStop);
|
||||||
Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
|
Events.off(ApiClient, "websocketmessage", onWebSocketMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['playbackManager', 'dom', 'inputmanager', 'datetime', 'itemHelper', 'mediaInfo', 'focusManager', 'imageLoader', 'scrollHelper', 'events', 'connectionManager', 'browser', 'globalize', 'apphost', 'scrollStyles'], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost) {
|
define(['playbackManager', 'dom', 'inputmanager', 'datetime', 'itemHelper', 'mediaInfo', 'focusManager', 'imageLoader', 'scrollHelper', 'events', 'connectionManager', 'browser', 'globalize', 'apphost', 'scrollStyles', 'emby-slider'], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function seriesImageUrl(item, options) {
|
function seriesImageUrl(item, options) {
|
||||||
|
@ -174,11 +174,11 @@
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
|
|
||||||
var pageTitle = document.querySelector('.pageTitle');
|
//var pageTitle = document.querySelector('.pageTitle');
|
||||||
pageTitle.style.backgroundImage = "url('" + url + "')";
|
//pageTitle.style.backgroundImage = "url('" + url + "')";
|
||||||
pageTitle.classList.add('pageTitleWithLogo');
|
//pageTitle.classList.add('pageTitleWithLogo');
|
||||||
pageTitle.innerHTML = '';
|
//pageTitle.innerHTML = '';
|
||||||
document.querySelector('.headerLogo').classList.add('hide');
|
//document.querySelector('.headerLogo').classList.add('hide');
|
||||||
} else {
|
} else {
|
||||||
Emby.Page.setTitle('');
|
Emby.Page.setTitle('');
|
||||||
}
|
}
|
||||||
|
@ -739,11 +739,14 @@
|
||||||
return opt;
|
return opt;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var positionTo = this;
|
||||||
|
|
||||||
require(['actionsheet'], function (actionsheet) {
|
require(['actionsheet'], function (actionsheet) {
|
||||||
|
|
||||||
actionsheet.show({
|
actionsheet.show({
|
||||||
items: menuItems,
|
items: menuItems,
|
||||||
title: globalize.translate('Audio')
|
title: globalize.translate('Audio'),
|
||||||
|
positionTo: positionTo
|
||||||
}).then(function (id) {
|
}).then(function (id) {
|
||||||
var index = parseInt(id);
|
var index = parseInt(id);
|
||||||
if (index !== currentIndex) {
|
if (index !== currentIndex) {
|
||||||
|
@ -785,11 +788,14 @@
|
||||||
return opt;
|
return opt;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var positionTo = this;
|
||||||
|
|
||||||
require(['actionsheet'], function (actionsheet) {
|
require(['actionsheet'], function (actionsheet) {
|
||||||
|
|
||||||
actionsheet.show({
|
actionsheet.show({
|
||||||
title: globalize.translate('Subtitles'),
|
title: globalize.translate('Subtitles'),
|
||||||
items: menuItems
|
items: menuItems,
|
||||||
|
positionTo: positionTo
|
||||||
}).then(function (id) {
|
}).then(function (id) {
|
||||||
var index = parseInt(id);
|
var index = parseInt(id);
|
||||||
if (index !== currentIndex) {
|
if (index !== currentIndex) {
|
||||||
|
|
|
@ -1,4 +1,155 @@
|
||||||
<div id="videoOsdPage" data-role="page" class="page libraryPage">
|
<div id="videoOsdPage" data-role="page" class="page libraryPage" data-backbutton="true">
|
||||||
|
<style>
|
||||||
|
.osdHeader {
|
||||||
|
padding-bottom: 3vh;
|
||||||
|
transition: transform 300ms ease-out, opacity 300ms ease-out;
|
||||||
|
will-change: transform;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdHeader .viewMenuBar {
|
||||||
|
background-color: rgba(0, 0, 0, .3);
|
||||||
|
background: linear-gradient(rgba(0, 0, 0, .6), rgba(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdHeader-hidden {
|
||||||
|
transform: translate3d(0,-100%,0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdHeader .headerButton:not(.headerBackButton) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdHeader-hidden {
|
||||||
|
transform: translate3d(0,-100%,0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenePicker {
|
||||||
|
position: fixed;
|
||||||
|
left: 12%;
|
||||||
|
bottom: 20%;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chapterThumb {
|
||||||
|
height: 24vh;
|
||||||
|
background-position: center center;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
-moz-box-shadow: 0 0 1.9vh #000;
|
||||||
|
-webkit-box-shadow: 0 0 1.9vh #000;
|
||||||
|
box-shadow: 0 0 1.9vh #000;
|
||||||
|
border: solid 1px #222;
|
||||||
|
margin: 1vh;
|
||||||
|
transition: opacity ease-out .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chapterThumb:not(.selectedChapterThumb) {
|
||||||
|
height: 22vh;
|
||||||
|
opacity: .2;
|
||||||
|
border-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoOsdBottom {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba(0, 0, 0, .7);
|
||||||
|
color: #fff;
|
||||||
|
padding: 1%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
will-change: transform;
|
||||||
|
transition: transform 300ms ease-out, opacity 300ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoOsdBottom-hidden {
|
||||||
|
transform: translate3d(0,100%,0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdControls {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoOsdBottom .buttons {
|
||||||
|
padding: .25em 0 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoOsdBottom paper-icon-button {
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdVolumeSliderContainer {
|
||||||
|
width: 6.5em;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volumeButtons {
|
||||||
|
margin: 0 .5em 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdTimeText {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mouseIdle .videoOsdBottom .volumeButtons {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdPoster {
|
||||||
|
width: 10%;
|
||||||
|
position: relative;
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdPoster img {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: auto;
|
||||||
|
width: 100%;
|
||||||
|
-moz-box-shadow: 0 0 1.9vh #000;
|
||||||
|
-webkit-box-shadow: 0 0 1.9vh #000;
|
||||||
|
box-shadow: 0 0 1.9vh #000;
|
||||||
|
border: solid 1px #222;
|
||||||
|
user-drag: none;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdTitle {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: .4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdMediaInfo {
|
||||||
|
margin-left: 1em;
|
||||||
|
color: #eee;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osdTextContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<div class="pageContainer flex">
|
<div class="pageContainer flex">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue