mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #4891 from Sky-High/playback-fixes-for-chromecast
Fix playback control issues with chromecast
This commit is contained in:
commit
34212614bc
5 changed files with 116 additions and 68 deletions
|
@ -170,17 +170,20 @@ function bindEvents(elem) {
|
||||||
|
|
||||||
elem.querySelector('.previousTrackButton').addEventListener('click', function (e) {
|
elem.querySelector('.previousTrackButton').addEventListener('click', function (e) {
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
if (lastPlayerState.NowPlayingItem.MediaType === 'Audio') {
|
if (playbackManager.isPlayingAudio(currentPlayer)) {
|
||||||
// Cancel this event if doubleclick is fired. The actual previousTrack will be processed by the 'dblclick' event
|
// Cancel this event if doubleclick is fired. The actual previousTrack will be processed by the 'dblclick' event
|
||||||
if (e.detail > 1 ) {
|
if (e.detail > 1 ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return to start of track, unless we are already (almost) at the beginning. In the latter case, continue and move
|
// Return to start of track, unless we are already (almost) at the beginning. In the latter case, continue and move
|
||||||
// to the previous track, unless we are at the first track so no previous track exists.
|
// to the previous track, unless we are at the first track so no previous track exists.
|
||||||
if (currentPlayer._currentTime >= 5 || playbackManager.getCurrentPlaylistIndex(currentPlayer) <= 1) {
|
// currentTime is in msec.
|
||||||
|
|
||||||
|
if (playbackManager.currentTime(currentPlayer) >= 5 || playbackManager.getCurrentPlaylistIndex(currentPlayer) <= 1) {
|
||||||
playbackManager.seekPercent(0, currentPlayer);
|
playbackManager.seekPercent(0, currentPlayer);
|
||||||
// This is done automatically by playbackManager, however, setting this here gives instant visual feedback.
|
// This is done automatically by playbackManager, however, setting this here gives instant visual feedback.
|
||||||
// TODO: Check why seekPercentage doesn't reflect the changes inmmediately, so we can remove this workaround.
|
// TODO: Check why seekPercent doesn't reflect the changes inmmediately, so we can remove this workaround.
|
||||||
positionSlider.value = 0;
|
positionSlider.value = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -574,7 +577,8 @@ function updateNowPlayingInfo(state) {
|
||||||
itemContextMenu.show(Object.assign({
|
itemContextMenu.show(Object.assign({
|
||||||
item: item,
|
item: item,
|
||||||
user: user
|
user: user
|
||||||
}, options));
|
}, options))
|
||||||
|
.catch(() => { /* no-op */ });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -642,7 +646,8 @@ function hideNowPlayingBar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPlaybackStopped(e, state) {
|
function onPlaybackStopped(e, state) {
|
||||||
console.debug('nowplaying event: ' + e.type);
|
console.debug('[nowPlayingBar:onPlaybackStopped] event: ' + e.type);
|
||||||
|
|
||||||
const player = this;
|
const player = this;
|
||||||
|
|
||||||
if (player.isLocalPlayer) {
|
if (player.isLocalPlayer) {
|
||||||
|
@ -669,7 +674,7 @@ function onStateChanged(event, state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug('nowplaying event: ' + event.type);
|
console.debug('[nowPlayingBar:onStateChanged] event: ' + event.type);
|
||||||
const player = this;
|
const player = this;
|
||||||
|
|
||||||
if (!state.NowPlayingItem || layoutManager.tv || state.IsFullscreen === false) {
|
if (!state.NowPlayingItem || layoutManager.tv || state.IsFullscreen === false) {
|
||||||
|
@ -792,4 +797,3 @@ document.addEventListener('viewbeforeshow', function (e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,8 @@ function updateNowPlayingInfo(context, state, serverId) {
|
||||||
itemContextMenu.show(Object.assign({
|
itemContextMenu.show(Object.assign({
|
||||||
item: fullItem,
|
item: fullItem,
|
||||||
user: user
|
user: user
|
||||||
}, options));
|
}, options))
|
||||||
|
.catch(() => { /* no-op */ });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -773,17 +774,20 @@ export default function () {
|
||||||
|
|
||||||
context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) {
|
context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) {
|
||||||
if (currentPlayer) {
|
if (currentPlayer) {
|
||||||
if (lastPlayerState.NowPlayingItem.MediaType === 'Audio') {
|
if (playbackManager.isPlayingAudio(currentPlayer)) {
|
||||||
// Cancel this event if doubleclick is fired. The actual previousTrack will be processed by the 'dblclick' event
|
// Cancel this event if doubleclick is fired. The actual previousTrack will be processed by the 'dblclick' event
|
||||||
if (e.detail > 1 ) {
|
if (e.detail > 1 ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return to start of track, unless we are already (almost) at the beginning. In the latter case, continue and move
|
// Return to start of track, unless we are already (almost) at the beginning. In the latter case, continue and move
|
||||||
// to the previous track, unless we are at the first track so no previous track exists.
|
// to the previous track, unless we are at the first track so no previous track exists.
|
||||||
if (currentPlayer._currentTime >= 5 || playbackManager.getCurrentPlaylistIndex(currentPlayer) <= 1) {
|
// currentTime is in msec.
|
||||||
|
|
||||||
|
if (playbackManager.currentTime(currentPlayer) >= 5 || playbackManager.getCurrentPlaylistIndex(currentPlayer) <= 1) {
|
||||||
playbackManager.seekPercent(0, currentPlayer);
|
playbackManager.seekPercent(0, currentPlayer);
|
||||||
// This is done automatically by playbackManager, however, setting this here gives instant visual feedback.
|
// This is done automatically by playbackManager, however, setting this here gives instant visual feedback.
|
||||||
// TODO: Check why seekPercentage doesn't reflect the changes inmmediately, so we can remove this workaround.
|
// TODO: Check why seekPercent doesn't reflect the changes inmmediately, so we can remove this workaround.
|
||||||
positionSlider.value = 0;
|
positionSlider.value = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,14 +121,15 @@ function showContextMenu(card, options) {
|
||||||
playlistId: playlistId,
|
playlistId: playlistId,
|
||||||
collectionId: collectionId,
|
collectionId: collectionId,
|
||||||
user: user
|
user: user
|
||||||
|
}, options || {}))
|
||||||
}, options || {})).then(result => {
|
.then(result => {
|
||||||
if (result.command === 'playallfromhere' || result.command === 'queueallfromhere') {
|
if (result.command === 'playallfromhere' || result.command === 'queueallfromhere') {
|
||||||
executeAction(card, options.positionTo, result.command);
|
executeAction(card, options.positionTo, result.command);
|
||||||
} else if (result.updated || result.deleted) {
|
} else if (result.updated || result.deleted) {
|
||||||
notifyRefreshNeeded(card, options.itemsContainer);
|
notifyRefreshNeeded(card, options.itemsContainer);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.catch(() => { /* no-op */ });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1966,13 +1966,15 @@ export default function (view, params) {
|
||||||
selectedItem = item;
|
selectedItem = item;
|
||||||
|
|
||||||
apiClient.getCurrentUser().then(function (user) {
|
apiClient.getCurrentUser().then(function (user) {
|
||||||
itemContextMenu.show(getContextMenuOptions(selectedItem, user, button)).then(function (result) {
|
itemContextMenu.show(getContextMenuOptions(selectedItem, user, button))
|
||||||
if (result.deleted) {
|
.then(function (result) {
|
||||||
appRouter.goHome();
|
if (result.deleted) {
|
||||||
} else if (result.updated) {
|
appRouter.goHome();
|
||||||
reload(self, view, params);
|
} else if (result.updated) {
|
||||||
}
|
reload(self, view, params);
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.catch(() => { /* no-op */ });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,20 @@ import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts';
|
||||||
|
|
||||||
// Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js
|
// Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js
|
||||||
|
|
||||||
let currentResolve;
|
|
||||||
let currentReject;
|
|
||||||
|
|
||||||
const PlayerName = 'Google Cast';
|
const PlayerName = 'Google Cast';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some async CastSDK function are completed with callbacks.
|
||||||
|
* sendConnectionResult turns this into completion as a promise.
|
||||||
|
*/
|
||||||
|
let _currentResolve = null;
|
||||||
|
let _currentReject = null;
|
||||||
function sendConnectionResult(isOk) {
|
function sendConnectionResult(isOk) {
|
||||||
const resolve = currentResolve;
|
const resolve = _currentResolve;
|
||||||
const reject = currentReject;
|
const reject = _currentReject;
|
||||||
|
|
||||||
currentResolve = null;
|
_currentResolve = null;
|
||||||
currentReject = null;
|
_currentReject = null;
|
||||||
|
|
||||||
if (isOk) {
|
if (isOk) {
|
||||||
if (resolve) {
|
if (resolve) {
|
||||||
|
@ -128,14 +131,14 @@ class CastPlayer {
|
||||||
*/
|
*/
|
||||||
onInitSuccess() {
|
onInitSuccess() {
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
console.debug('chromecast init success');
|
console.debug('[chromecastPlayer] init success');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic error callback function
|
* Generic error callback function
|
||||||
*/
|
*/
|
||||||
onError() {
|
onError() {
|
||||||
console.debug('chromecast error');
|
console.debug('[chromecastPlayer] error');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,6 +159,7 @@ class CastPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// messageListener - receive callback messages from the Cast receiver
|
||||||
messageListener(namespace, message) {
|
messageListener(namespace, message) {
|
||||||
if (typeof (message) === 'string') {
|
if (typeof (message) === 'string') {
|
||||||
message = JSON.parse(message);
|
message = JSON.parse(message);
|
||||||
|
@ -182,10 +186,10 @@ class CastPlayer {
|
||||||
*/
|
*/
|
||||||
receiverListener(e) {
|
receiverListener(e) {
|
||||||
if (e === 'available') {
|
if (e === 'available') {
|
||||||
console.debug('chromecast receiver found');
|
console.debug('[chromecastPlayer] receiver found');
|
||||||
this.hasReceivers = true;
|
this.hasReceivers = true;
|
||||||
} else {
|
} else {
|
||||||
console.debug('chromecast receiver list empty');
|
console.debug('[chromecastPlayer] receiver list empty');
|
||||||
this.hasReceivers = false;
|
this.hasReceivers = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +199,7 @@ class CastPlayer {
|
||||||
*/
|
*/
|
||||||
sessionUpdateListener(isAlive) {
|
sessionUpdateListener(isAlive) {
|
||||||
if (isAlive) {
|
if (isAlive) {
|
||||||
console.debug('sessionUpdateListener: already alive');
|
console.debug('[chromecastPlayer] sessionUpdateListener: already alive');
|
||||||
} else {
|
} else {
|
||||||
this.session = null;
|
this.session = null;
|
||||||
this.deviceState = DEVICE_STATE.IDLE;
|
this.deviceState = DEVICE_STATE.IDLE;
|
||||||
|
@ -203,7 +207,7 @@ class CastPlayer {
|
||||||
document.removeEventListener('volumeupbutton', onVolumeUpKeyDown, false);
|
document.removeEventListener('volumeupbutton', onVolumeUpKeyDown, false);
|
||||||
document.removeEventListener('volumedownbutton', onVolumeDownKeyDown, false);
|
document.removeEventListener('volumedownbutton', onVolumeDownKeyDown, false);
|
||||||
|
|
||||||
console.debug('sessionUpdateListener: setting currentMediaSession to null');
|
console.debug('[chromecastPlayer] sessionUpdateListener: setting currentMediaSession to null');
|
||||||
this.currentMediaSession = null;
|
this.currentMediaSession = null;
|
||||||
|
|
||||||
sendConnectionResult(false);
|
sendConnectionResult(false);
|
||||||
|
@ -216,7 +220,7 @@ class CastPlayer {
|
||||||
* session request in opt_sessionRequest.
|
* session request in opt_sessionRequest.
|
||||||
*/
|
*/
|
||||||
launchApp() {
|
launchApp() {
|
||||||
console.debug('chromecast launching app...');
|
console.debug('[chromecastPlayer] launching app...');
|
||||||
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this));
|
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +229,7 @@ class CastPlayer {
|
||||||
* @param {Object} e A chrome.cast.Session object
|
* @param {Object} e A chrome.cast.Session object
|
||||||
*/
|
*/
|
||||||
onRequestSessionSuccess(e) {
|
onRequestSessionSuccess(e) {
|
||||||
console.debug('chromecast session success: ' + e.sessionId);
|
console.debug('[chromecastPlayer] session success: ' + e.sessionId);
|
||||||
this.onSessionConnected(e);
|
this.onSessionConnected(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +263,7 @@ class CastPlayer {
|
||||||
* Callback function for launch error
|
* Callback function for launch error
|
||||||
*/
|
*/
|
||||||
onLaunchError() {
|
onLaunchError() {
|
||||||
console.debug('chromecast launch error');
|
console.debug('[chromecastPlayer] launch error');
|
||||||
this.deviceState = DEVICE_STATE.ERROR;
|
this.deviceState = DEVICE_STATE.ERROR;
|
||||||
sendConnectionResult(false);
|
sendConnectionResult(false);
|
||||||
}
|
}
|
||||||
|
@ -289,11 +293,12 @@ class CastPlayer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads media into a running receiver application
|
* Loads media into a running receiver application
|
||||||
* @param {Number} mediaIndex An index number to indicate current media content
|
* @param {Number} mediaIndex - An index number to indicate current media content
|
||||||
|
* @returns Promise
|
||||||
*/
|
*/
|
||||||
loadMedia(options, command) {
|
loadMedia(options, command) {
|
||||||
if (!this.session) {
|
if (!this.session) {
|
||||||
console.debug('no session');
|
console.debug('[chromecastPlayer] no session');
|
||||||
return Promise.reject(new Error('no session'));
|
return Promise.reject(new Error('no session'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +390,7 @@ class CastPlayer {
|
||||||
* @param {Object} mediaSession A new media object.
|
* @param {Object} mediaSession A new media object.
|
||||||
*/
|
*/
|
||||||
onMediaDiscovered(how, mediaSession) {
|
onMediaDiscovered(how, mediaSession) {
|
||||||
console.debug('chromecast new media session ID:' + mediaSession.mediaSessionId + ' (' + how + ')');
|
console.debug('[chromecastPlayer] new media session ID:' + mediaSession.mediaSessionId + ' (' + how + ')');
|
||||||
this.currentMediaSession = mediaSession;
|
this.currentMediaSession = mediaSession;
|
||||||
|
|
||||||
if (how === 'loadMedia') {
|
if (how === 'loadMedia') {
|
||||||
|
@ -404,7 +409,7 @@ class CastPlayer {
|
||||||
* @param {!Boolean} e true/false
|
* @param {!Boolean} e true/false
|
||||||
*/
|
*/
|
||||||
onMediaStatusUpdate(e) {
|
onMediaStatusUpdate(e) {
|
||||||
console.debug('chromecast updating media: ' + e);
|
console.debug('[chromecastPlayer] updating media: ' + e);
|
||||||
if (e === false) {
|
if (e === false) {
|
||||||
this.castPlayerState = PLAYER_STATE.IDLE;
|
this.castPlayerState = PLAYER_STATE.IDLE;
|
||||||
}
|
}
|
||||||
|
@ -498,12 +503,17 @@ function getItemsForPlayback(apiClient, query) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* relay castPlayer events to ChromecastPlayer events and include state info
|
||||||
|
*/
|
||||||
function bindEventForRelay(instance, eventName) {
|
function bindEventForRelay(instance, eventName) {
|
||||||
Events.on(instance._castPlayer, eventName, function (e, data) {
|
Events.on(instance._castPlayer, eventName, function (e, data) {
|
||||||
console.debug('cc: ' + eventName);
|
console.debug('[chromecastPlayer] ' + eventName);
|
||||||
const state = instance.getPlayerStateInternal(data);
|
// skip events without data
|
||||||
|
if (data?.ItemId) {
|
||||||
Events.trigger(instance, eventName, [state]);
|
const state = instance.getPlayerStateInternal(data);
|
||||||
|
Events.trigger(instance, eventName, [state]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,30 +529,39 @@ function initializeChromecast() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Events.on(instance._castPlayer, 'connect', function () {
|
Events.on(instance._castPlayer, 'connect', function () {
|
||||||
if (currentResolve) {
|
if (_currentResolve) {
|
||||||
sendConnectionResult(true);
|
sendConnectionResult(true);
|
||||||
} else {
|
} else {
|
||||||
playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo());
|
playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug('cc: connect');
|
console.debug('[chromecastPlayer] connect');
|
||||||
// Reset this so that statechange will fire
|
// Reset this so that statechange will fire
|
||||||
instance.lastPlayerData = null;
|
instance.lastPlayerData = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(instance._castPlayer, 'playbackstart', function (e, data) {
|
Events.on(instance._castPlayer, 'playbackstart', function (e, data) {
|
||||||
console.debug('cc: playbackstart');
|
console.debug('[chromecastPlayer] playbackstart');
|
||||||
|
|
||||||
instance._castPlayer.initializeCastPlayer();
|
instance._castPlayer.initializeCastPlayer();
|
||||||
|
|
||||||
const state = instance.getPlayerStateInternal(data);
|
const state = instance.getPlayerStateInternal(data);
|
||||||
Events.trigger(instance, 'playbackstart', [state]);
|
Events.trigger(instance, 'playbackstart', [state]);
|
||||||
|
|
||||||
|
// be prepared that after this media item a next one may follow. See playbackManager
|
||||||
|
instance._playNextAfterEnded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(instance._castPlayer, 'playbackstop', function (e, data) {
|
Events.on(instance._castPlayer, 'playbackstop', function (e, data) {
|
||||||
console.debug('cc: playbackstop');
|
console.debug('[chromecastPlayer] playbackstop');
|
||||||
|
|
||||||
let state = instance.getPlayerStateInternal(data);
|
let state = instance.getPlayerStateInternal(data);
|
||||||
|
|
||||||
|
if (!instance._playNextAfterEnded) {
|
||||||
|
// mark that no next media items are to be processed.
|
||||||
|
state.nextItem = null;
|
||||||
|
state.NextMediaType = null;
|
||||||
|
}
|
||||||
Events.trigger(instance, 'playbackstop', [state]);
|
Events.trigger(instance, 'playbackstop', [state]);
|
||||||
|
|
||||||
state = instance.lastPlayerData.PlayState || {};
|
state = instance.lastPlayerData.PlayState || {};
|
||||||
|
@ -550,14 +569,16 @@ function initializeChromecast() {
|
||||||
const mute = state.IsMuted || false;
|
const mute = state.IsMuted || false;
|
||||||
|
|
||||||
// Reset this so the next query doesn't make it appear like content is playing.
|
// Reset this so the next query doesn't make it appear like content is playing.
|
||||||
instance.lastPlayerData = {};
|
instance.lastPlayerData = {
|
||||||
instance.lastPlayerData.PlayState = {};
|
PlayState: {
|
||||||
instance.lastPlayerData.PlayState.VolumeLevel = volume;
|
VolumeLevel: volume,
|
||||||
instance.lastPlayerData.PlayState.IsMuted = mute;
|
IsMuted: mute
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(instance._castPlayer, 'playbackprogress', function (e, data) {
|
Events.on(instance._castPlayer, 'playbackprogress', function (e, data) {
|
||||||
console.debug('cc: positionchange');
|
console.debug('[chromecastPlayer] positionchange');
|
||||||
const state = instance.getPlayerStateInternal(data);
|
const state = instance.getPlayerStateInternal(data);
|
||||||
|
|
||||||
Events.trigger(instance, 'timeupdate', [state]);
|
Events.trigger(instance, 'timeupdate', [state]);
|
||||||
|
@ -571,9 +592,10 @@ function initializeChromecast() {
|
||||||
bindEventForRelay(instance, 'shufflequeuemodechange');
|
bindEventForRelay(instance, 'shufflequeuemodechange');
|
||||||
|
|
||||||
Events.on(instance._castPlayer, 'playstatechange', function (e, data) {
|
Events.on(instance._castPlayer, 'playstatechange', function (e, data) {
|
||||||
console.debug('cc: playstatechange');
|
console.debug('[chromecastPlayer] playstatechange');
|
||||||
const state = instance.getPlayerStateInternal(data);
|
|
||||||
|
|
||||||
|
// Updates the player and nowPlayingBar state to the current 'pause' state.
|
||||||
|
const state = instance.getPlayerStateInternal(data);
|
||||||
Events.trigger(instance, 'pause', [state]);
|
Events.trigger(instance, 'pause', [state]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -598,19 +620,21 @@ class ChromecastPlayer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cast button handling: select and connect to chromecast receiver
|
||||||
|
*/
|
||||||
tryPair() {
|
tryPair() {
|
||||||
const castPlayer = this._castPlayer;
|
const castPlayer = this._castPlayer;
|
||||||
|
|
||||||
if (castPlayer.deviceState !== DEVICE_STATE.ACTIVE && castPlayer.isInitialized) {
|
if (castPlayer.deviceState !== DEVICE_STATE.ACTIVE && castPlayer.isInitialized) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
currentResolve = resolve;
|
_currentResolve = resolve;
|
||||||
currentReject = reject;
|
_currentReject = reject;
|
||||||
castPlayer.launchApp();
|
castPlayer.launchApp();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
currentResolve = null;
|
_currentResolve = null;
|
||||||
currentReject = null;
|
_currentReject = null;
|
||||||
|
|
||||||
return Promise.reject(new Error('tryPair failed'));
|
return Promise.reject(new Error('tryPair failed'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -674,8 +698,6 @@ class ChromecastPlayer {
|
||||||
|
|
||||||
normalizeImages(data);
|
normalizeImages(data);
|
||||||
|
|
||||||
console.debug(JSON.stringify(data));
|
|
||||||
|
|
||||||
if (triggerStateChange) {
|
if (triggerStateChange) {
|
||||||
Events.trigger(this, 'statechange', [data]);
|
Events.trigger(this, 'statechange', [data]);
|
||||||
}
|
}
|
||||||
|
@ -824,6 +846,8 @@ class ChromecastPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
// suppress playing a next media item after this one. See playbackManager
|
||||||
|
this._playNextAfterEnded = false;
|
||||||
return this._castPlayer.sendMessage({
|
return this._castPlayer.sendMessage({
|
||||||
options: {},
|
options: {},
|
||||||
command: 'Stop'
|
command: 'Stop'
|
||||||
|
@ -1039,6 +1063,10 @@ class ChromecastPlayer {
|
||||||
this.playWithCommand(options, 'PlayNext');
|
this.playWithCommand(options, 'PlayNext');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* play
|
||||||
|
* options.items[]: Id, IsFolder, MediaType, Name, ServerId, Type, ...
|
||||||
|
*/
|
||||||
play(options) {
|
play(options) {
|
||||||
if (options.items) {
|
if (options.items) {
|
||||||
return this.playWithCommand(options, 'PlayNow');
|
return this.playWithCommand(options, 'PlayNow');
|
||||||
|
@ -1090,6 +1118,15 @@ class ChromecastPlayer {
|
||||||
getPlayerState() {
|
getPlayerState() {
|
||||||
return this.getPlayerStateInternal() || {};
|
return this.getPlayerStateInternal() || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCurrentPlaylistIndex() {
|
||||||
|
// tbd: update to support playlists and not only album with tracks
|
||||||
|
return this.getPlayerStateInternal()?.NowPlayingItem?.IndexNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearQueue(currentTime) { // eslint-disable-line no-unused-vars
|
||||||
|
// not supported yet
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ChromecastPlayer;
|
export default ChromecastPlayer;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue