Add playlist-sync and group-wait to SyncPlay

This commit is contained in:
Ionut Andrei Oanca 2020-09-25 09:44:30 +02:00
parent 46a0382c0a
commit d8beb9909f
41 changed files with 3880 additions and 1125 deletions

View file

@ -0,0 +1,189 @@
import { Events } from 'jellyfin-apiclient';
import SyncPlay from 'SyncPlay';
import loading from '../../loading/loading';
import toast from '../../toast/toast';
import actionsheet from '../../actionSheet/actionSheet';
import globalize from '../../../scripts/globalize';
import playbackPermissionManager from './playbackPermissionManager';
import ServerConnections from '../../ServerConnections';
/**
* Class that manages the SyncPlay group selection menu.
*/
class GroupSelectionMenu {
constructor() {
// Register to SyncPlay events.
this.syncPlayEnabled = false;
Events.on(SyncPlay.Manager, 'enabled', (e, enabled) => {
this.syncPlayEnabled = enabled;
});
}
/**
* Used when user needs to join a group.
* @param {HTMLElement} button - Element where to place the menu.
* @param {Object} user - Current user.
* @param {Object} apiClient - ApiClient.
*/
showNewJoinGroupSelection(button, user, apiClient) {
const policy = user.localUser ? user.localUser.Policy : {};
apiClient.getSyncPlayGroups().then(function (response) {
response.json().then(function (groups) {
const menuItems = groups.map(function (group) {
return {
name: group.GroupName,
icon: 'person',
id: group.GroupId,
selected: false,
secondaryText: group.Participants.join(', ')
};
});
if (policy.SyncPlayAccess === 'CreateAndJoinGroups') {
menuItems.push({
name: globalize.translate('LabelSyncPlayNewGroup'),
icon: 'add',
id: 'new-group',
selected: true,
secondaryText: globalize.translate('LabelSyncPlayNewGroupDescription')
});
}
if (menuItems.length === 0 && policy.SyncPlayAccess === 'JoinGroups') {
toast({
text: globalize.translate('MessageSyncPlayCreateGroupDenied')
});
loading.hide();
return;
}
const menuOptions = {
title: globalize.translate('HeaderSyncPlaySelectGroup'),
items: menuItems,
positionTo: button,
resolveOnClick: true,
border: true
};
actionsheet.show(menuOptions).then(function (id) {
if (id == 'new-group') {
apiClient.createSyncPlayGroup({
GroupName: globalize.translate('SyncPlayGroupDefaultTitle', user.localUser.Name)
});
} else if (id) {
apiClient.joinSyncPlayGroup({
GroupId: id
});
}
}).catch((error) => {
console.error('SyncPlay: unexpected error listing groups:', error);
});
loading.hide();
});
}).catch(function (error) {
console.error(error);
loading.hide();
toast({
text: globalize.translate('MessageSyncPlayErrorAccessingGroups')
});
});
}
/**
* Used when user has joined a group.
* @param {HTMLElement} button - Element where to place the menu.
* @param {Object} user - Current user.
* @param {Object} apiClient - ApiClient.
*/
showLeaveGroupSelection(button, user, apiClient) {
const groupInfo = SyncPlay.Manager.getGroupInfo();
const menuItems = [];
if (!SyncPlay.Manager.isPlaylistEmpty() && !SyncPlay.Manager.isPlaybackActive()) {
menuItems.push({
name: globalize.translate('LabelSyncPlayResumePlayback'),
icon: 'play_circle_filled',
id: 'resume-playback',
selected: false,
secondaryText: globalize.translate('LabelSyncPlayResumePlaybackDescription')
});
} else if (SyncPlay.Manager.isPlaybackActive()) {
menuItems.push({
name: globalize.translate('LabelSyncPlayHaltPlayback'),
icon: 'pause_circle_filled',
id: 'halt-playback',
selected: false,
secondaryText: globalize.translate('LabelSyncPlayHaltPlaybackDescription')
});
}
menuItems.push({
name: globalize.translate('LabelSyncPlayLeaveGroup'),
icon: 'meeting_room',
id: 'leave-group',
selected: true,
secondaryText: globalize.translate('LabelSyncPlayLeaveGroupDescription')
});
const menuOptions = {
title: groupInfo.GroupName,
items: menuItems,
positionTo: button,
resolveOnClick: true,
border: true
};
actionsheet.show(menuOptions).then(function (id) {
if (id == 'resume-playback') {
SyncPlay.Manager.resumeGroupPlayback(apiClient);
} else if (id == 'halt-playback') {
SyncPlay.Manager.haltGroupPlayback(apiClient);
} else if (id == 'leave-group') {
apiClient.leaveSyncPlayGroup();
}
}).catch((error) => {
console.error('SyncPlay: unexpected error showing group menu:', error);
});
loading.hide();
}
/**
* Shows a menu to handle SyncPlay groups.
* @param {HTMLElement} button - Element where to place the menu.
*/
show(button) {
loading.show();
// TODO: should feature be disabled if playback permission is missing?
playbackPermissionManager.check().then(() => {
console.debug('Playback is allowed.');
}).catch((error) => {
console.error('Playback not allowed!', error);
toast({
text: globalize.translate('MessageSyncPlayPlaybackPermissionRequired')
});
});
const apiClient = ServerConnections.currentApiClient();
ServerConnections.user(apiClient).then((user) => {
if (this.syncPlayEnabled) {
this.showLeaveGroupSelection(button, user, apiClient);
} else {
this.showNewJoinGroupSelection(button, user, apiClient);
}
}).catch((error) => {
console.error(error);
loading.hide();
toast({
text: globalize.translate('MessageSyncPlayNoGroupsAvailable')
});
});
}
}
/** GroupSelectionMenu singleton. */
const groupSelectionMenu = new GroupSelectionMenu();
export default groupSelectionMenu;

View file

@ -0,0 +1,50 @@
/**
* Creates an audio element that plays a silent sound.
* @returns {HTMLMediaElement} The audio element.
*/
function createTestMediaElement () {
const elem = document.createElement('audio');
elem.classList.add('testMediaPlayerAudio');
elem.classList.add('hide');
document.body.appendChild(elem);
elem.volume = 1; // Volume should not be zero to trigger proper permissions
elem.src = 'assets/audio/silence.mp3'; // Silent sound
return elem;
}
/**
* Destroys a media element.
* @param {HTMLMediaElement} elem The element to destroy.
*/
function destroyTestMediaElement (elem) {
elem.pause();
elem.remove();
}
/**
* Class that manages the playback permission.
*/
class PlaybackPermissionManager {
/**
* Tests playback permission. Grabs the permission when called inside a click event (or any other valid user interaction).
* @returns {Promise} Promise that resolves succesfully if playback permission is allowed.
*/
check () {
return new Promise((resolve, reject) => {
const media = createTestMediaElement();
media.play().then(() => {
resolve();
}).catch((error) => {
reject(error);
}).finally(() => {
destroyTestMediaElement(media);
});
});
}
}
/** PlaybackPermissionManager singleton. */
export default new PlaybackPermissionManager();

View file

@ -0,0 +1,19 @@
/**
* Module that manages the HtmlAudioPlayer for SyncPlay.
* @module components/syncPlay/players/htmlAudioPlayer
*/
import SyncPlayHtmlVideoPlayer from './htmlVideoPlayer';
/**
* Class that manages the HtmlAudioPlayer for SyncPlay.
*/
class SyncPlayHtmlAudioPlayer extends SyncPlayHtmlVideoPlayer {
static type = 'htmlaudioplayer';
constructor(player, syncPlayManager) {
super(player, syncPlayManager);
}
}
export default SyncPlayHtmlAudioPlayer;

View file

@ -0,0 +1,155 @@
/**
* Module that manages the HtmlVideoPlayer for SyncPlay.
* @module components/syncPlay/players/htmlVideoPlayer
*/
import { Events } from 'jellyfin-apiclient';
import SyncPlayNoActivePlayer from './noActivePlayer';
/**
* Class that manages the HtmlVideoPlayer for SyncPlay.
*/
class SyncPlayHtmlVideoPlayer extends SyncPlayNoActivePlayer {
static type = 'htmlvideoplayer';
constructor(player, syncPlayManager) {
super(player, syncPlayManager);
this.isPlayerActive = false;
this.savedPlaybackRate = 1.0;
this.minBufferingThresholdMillis = 3000;
}
/**
* Binds to the player's events. Overrides parent method.
* @param {Object} player The player.
*/
localBindToPlayer() {
super.localBindToPlayer();
const self = this;
this._onPlaybackStart = (player, state) => {
self.isPlayerActive = true;
self.onPlaybackStart(player, state);
};
this._onPlaybackStop = (stopInfo) => {
self.isPlayerActive = false;
self.onPlaybackStop(stopInfo);
};
this._onUnpause = () => {
self.onUnpause();
};
this._onPause = () => {
self.onPause();
};
this._onTimeUpdate = (e) => {
const currentTime = new Date();
const currentPosition = self.player.currentTime();
self.onTimeUpdate(e, {
currentTime: currentTime,
currentPosition: currentPosition
});
};
this._onPlaying = () => {
clearTimeout(self.notifyBuffering);
self.onReady();
};
this._onWaiting = () => {
clearTimeout(self.notifyBuffering);
self.notifyBuffering = setTimeout(() => {
self.onBuffering();
}, self.minBufferingThresholdMillis);
};
Events.on(this.player, 'playbackstart', this._onPlaybackStart);
Events.on(this.player, 'playbackstop', this._onPlaybackStop);
Events.on(this.player, 'unpause', this._onUnpause);
Events.on(this.player, 'pause', this._onPause);
Events.on(this.player, 'timeupdate', this._onTimeUpdate);
Events.on(this.player, 'playing', this._onPlaying);
Events.on(this.player, 'waiting', this._onWaiting);
this.savedPlaybackRate = this.player.getPlaybackRate();
}
/**
* Removes the bindings from the player's events. Overrides parent method.
*/
localUnbindFromPlayer() {
super.localUnbindFromPlayer();
Events.off(this.player, 'playbackstart', this._onPlaybackStart);
Events.off(this.player, 'playbackstop', this._onPlaybackStop);
Events.off(this.player, 'unpause', this._onPlayerUnpause);
Events.off(this.player, 'pause', this._onPlayerPause);
Events.off(this.player, 'timeupdate', this._onTimeUpdate);
Events.off(this.player, 'playing', this._onPlaying);
Events.off(this.player, 'waiting', this._onWaiting);
this.player.setPlaybackRate(this.savedPlaybackRate);
}
/**
* Called when changes are made to the play queue.
*/
onQueueUpdate() {
// TODO: find a more generic event? Tests show that this is working for now.
Events.trigger(this.player, 'playlistitemadd');
}
/**
* Gets player status.
* @returns {boolean} Whether the player has some media loaded.
*/
isPlaybackActive() {
return this.isPlayerActive;
}
/**
* Gets playback status.
* @returns {boolean} Whether the playback is unpaused.
*/
isPlaying() {
return !this.player.paused();
}
/**
* Gets playback position.
* @returns {number} The player position, in milliseconds.
*/
currentTime() {
return this.player.currentTime();
}
/**
* Checks if player has playback rate support.
* @returns {boolean} _true _ if playback rate is supported, false otherwise.
*/
hasPlaybackRate() {
return true;
}
/**
* Sets the playback rate, if supported.
* @param {number} value The playback rate.
*/
setPlaybackRate(value) {
this.player.setPlaybackRate(value);
}
/**
* Gets the playback rate.
* @returns {number} The playback rate.
*/
getPlaybackRate() {
return this.player.getPlaybackRate();
}
}
export default SyncPlayHtmlVideoPlayer;

View file

@ -0,0 +1,444 @@
/**
* Module that manages the PlaybackManager when there's no active player.
* @module components/syncPlay/players/genericPlayer
*/
import { playbackManager } from '../../../playback/playbackmanager';
import SyncPlay from 'SyncPlay';
import QueueManager from './queueManager';
let syncPlayManager;
/**
* Class that manages the PlaybackManager when there's no active player.
*/
class SyncPlayNoActivePlayer extends SyncPlay.Players.GenericPlayer {
static type = 'default';
constructor(player, _syncPlayManager) {
super(player, _syncPlayManager);
syncPlayManager = _syncPlayManager;
}
/**
* Binds to the player's events.
*/
localBindToPlayer() {
if (playbackManager.syncPlayEnabled) return;
// Save local callbacks.
playbackManager._localPlayPause = playbackManager.playPause;
playbackManager._localUnpause = playbackManager.unpause;
playbackManager._localPause = playbackManager.pause;
playbackManager._localSeek = playbackManager.seek;
playbackManager._localSendCommand = playbackManager.sendCommand;
// Override local callbacks.
playbackManager.playPause = this.playPauseRequest;
playbackManager.unpause = this.unpauseRequest;
playbackManager.pause = this.pauseRequest;
playbackManager.seek = this.seekRequest;
playbackManager.sendCommand = this.sendCommandRequest;
// Save local callbacks.
playbackManager._localPlayQueueManager = playbackManager._playQueueManager;
playbackManager._localPlay = playbackManager.play;
playbackManager._localSetCurrentPlaylistItem = playbackManager.setCurrentPlaylistItem;
playbackManager._localRemoveFromPlaylist = playbackManager.removeFromPlaylist;
playbackManager._localMovePlaylistItem = playbackManager.movePlaylistItem;
playbackManager._localQueue = playbackManager.queue;
playbackManager._localQueueNext = playbackManager.queueNext;
playbackManager._localNextTrack = playbackManager.nextTrack;
playbackManager._localPreviousTrack = playbackManager.previousTrack;
playbackManager._localSetRepeatMode = playbackManager.setRepeatMode;
playbackManager._localSetQueueShuffleMode = playbackManager.setQueueShuffleMode;
playbackManager._localToggleQueueShuffleMode = playbackManager.toggleQueueShuffleMode;
// Override local callbacks.
playbackManager._playQueueManager = new QueueManager(this.manager);
playbackManager.play = this.playRequest;
playbackManager.setCurrentPlaylistItem = this.setCurrentPlaylistItemRequest;
playbackManager.removeFromPlaylist = this.removeFromPlaylistRequest;
playbackManager.movePlaylistItem = this.movePlaylistItemRequest;
playbackManager.queue = this.queueRequest;
playbackManager.queueNext = this.queueNextRequest;
playbackManager.nextTrack = this.nextTrackRequest;
playbackManager.previousTrack = this.previousTrackRequest;
playbackManager.setRepeatMode = this.setRepeatModeRequest;
playbackManager.setQueueShuffleMode = this.setQueueShuffleModeRequest;
playbackManager.toggleQueueShuffleMode = this.toggleQueueShuffleModeRequest;
playbackManager.syncPlayEnabled = true;
}
/**
* Removes the bindings from the player's events.
*/
localUnbindFromPlayer() {
if (!playbackManager.syncPlayEnabled) return;
playbackManager.playPause = playbackManager._localPlayPause;
playbackManager.unpause = playbackManager._localUnpause;
playbackManager.pause = playbackManager._localPause;
playbackManager.seek = playbackManager._localSeek;
playbackManager.sendCommand = playbackManager._localSendCommand;
playbackManager._playQueueManager = playbackManager._localPlayQueueManager; // TODO: should move elsewhere?
playbackManager.play = playbackManager._localPlay;
playbackManager.setCurrentPlaylistItem = playbackManager._localSetCurrentPlaylistItem;
playbackManager.removeFromPlaylist = playbackManager._localRemoveFromPlaylist;
playbackManager.movePlaylistItem = playbackManager._localMovePlaylistItem;
playbackManager.queue = playbackManager._localQueue;
playbackManager.queueNext = playbackManager._localQueueNext;
playbackManager.nextTrack = playbackManager._localNextTrack;
playbackManager.previousTrack = playbackManager._localPreviousTrack;
playbackManager.setRepeatMode = playbackManager._localSetRepeatMode;
playbackManager.setQueueShuffleMode = playbackManager._localSetQueueShuffleMode;
playbackManager.toggleQueueShuffleMode = playbackManager._localToggleQueueShuffleMode;
playbackManager.syncPlayEnabled = false;
}
/**
* Overrides PlaybackManager's playPause method.
*/
playPauseRequest() {
const controller = syncPlayManager.getController();
controller.playPause();
}
/**
* Overrides PlaybackManager's unpause method.
*/
unpauseRequest() {
const controller = syncPlayManager.getController();
controller.unpause();
}
/**
* Overrides PlaybackManager's pause method.
*/
pauseRequest() {
const controller = syncPlayManager.getController();
controller.pause();
}
/**
* Overrides PlaybackManager's seek method.
*/
seekRequest(positionTicks, player) {
const controller = syncPlayManager.getController();
controller.seek(positionTicks);
}
/**
* Overrides PlaybackManager's sendCommand method.
*/
sendCommandRequest(cmd, player) {
console.debug('SyncPlay sendCommand:', cmd.Name, cmd);
const controller = syncPlayManager.getController();
const playerWrapper = syncPlayManager.getPlayerWrapper();
const defaultAction = (command, player) => {
playerWrapper.localSendCommand(command);
};
const ignoreCallback = (command, player) => {
// Do nothing.
};
const SetRepeatModeCallback = (command, player) => {
controller.setRepeatMode(command.Arguments.RepeatMode);
};
const SetShuffleQueueCallback = (command, player) => {
controller.setShuffleMode(command.Arguments.ShuffleMode);
};
// Commands to override.
const overrideCommands = {
PlaybackRate: ignoreCallback,
SetRepeatMode: SetRepeatModeCallback,
SetShuffleQueue: SetShuffleQueueCallback
};
// Handle command.
const commandHandler = overrideCommands[cmd.Name];
if (typeof commandHandler === 'function') {
commandHandler(cmd, player);
} else {
defaultAction(cmd, player);
}
}
/**
* Calls original PlaybackManager's unpause method.
*/
localUnpause() {
if (playbackManager.syncPlayEnabled) {
playbackManager._localUnpause(this.player);
} else {
playbackManager.unpause(this.player);
}
}
/**
* Calls original PlaybackManager's pause method.
*/
localPause() {
if (playbackManager.syncPlayEnabled) {
playbackManager._localPause(this.player);
} else {
playbackManager.pause(this.player);
}
}
/**
* Calls original PlaybackManager's seek method.
*/
localSeek(positionTicks) {
if (playbackManager.syncPlayEnabled) {
playbackManager._localSeek(positionTicks, this.player);
} else {
playbackManager.seek(positionTicks, this.player);
}
}
/**
* Calls original PlaybackManager's stop method.
*/
localStop() {
playbackManager.stop(this.player);
}
/**
* Calls original PlaybackManager's sendCommand method.
*/
localSendCommand(cmd) {
if (playbackManager.syncPlayEnabled) {
playbackManager._localSendCommand(cmd, this.player);
} else {
playbackManager.sendCommand(cmd, this.player);
}
}
/**
* Overrides PlaybackManager's play method.
*/
playRequest(options) {
const controller = syncPlayManager.getController();
controller.play(options);
}
/**
* Overrides PlaybackManager's setCurrentPlaylistItem method.
*/
setCurrentPlaylistItemRequest(playlistItemId, player) {
const controller = syncPlayManager.getController();
controller.setCurrentPlaylistItem(playlistItemId);
}
/**
* Overrides PlaybackManager's removeFromPlaylist method.
*/
removeFromPlaylistRequest(playlistItemIds, player) {
const controller = syncPlayManager.getController();
controller.removeFromPlaylist(playlistItemIds);
}
/**
* Overrides PlaybackManager's movePlaylistItem method.
*/
movePlaylistItemRequest(playlistItemId, newIndex, player) {
const controller = syncPlayManager.getController();
controller.movePlaylistItem(playlistItemId, newIndex);
}
/**
* Overrides PlaybackManager's queue method.
*/
queueRequest(options, player) {
const controller = syncPlayManager.getController();
controller.queue(options);
}
/**
* Overrides PlaybackManager's queueNext method.
*/
queueNextRequest(options, player) {
const controller = syncPlayManager.getController();
controller.queueNext(options);
}
/**
* Overrides PlaybackManager's nextTrack method.
*/
nextTrackRequest(player) {
const controller = syncPlayManager.getController();
controller.nextTrack();
}
/**
* Overrides PlaybackManager's previousTrack method.
*/
previousTrackRequest(player) {
const controller = syncPlayManager.getController();
controller.previousTrack();
}
/**
* Overrides PlaybackManager's setRepeatMode method.
*/
setRepeatModeRequest(mode, player) {
const controller = syncPlayManager.getController();
controller.setRepeatMode(mode);
}
/**
* Overrides PlaybackManager's setQueueShuffleMode method.
*/
setQueueShuffleModeRequest(mode, player) {
const controller = syncPlayManager.getController();
controller.setShuffleMode(mode);
}
/**
* Overrides PlaybackManager's toggleQueueShuffleMode method.
*/
toggleQueueShuffleModeRequest(player) {
const controller = syncPlayManager.getController();
controller.toggleShuffleMode();
}
/**
* Calls original PlaybackManager's play method.
*/
localPlay(options) {
if (playbackManager.syncPlayEnabled) {
return playbackManager._localPlay(options);
} else {
return playbackManager.play(options);
}
}
/**
* Calls original PlaybackManager's setCurrentPlaylistItem method.
*/
localSetCurrentPlaylistItem(playlistItemId) {
if (playbackManager.syncPlayEnabled) {
return playbackManager._localSetCurrentPlaylistItem(playlistItemId, this.player);
} else {
return playbackManager.setCurrentPlaylistItem(playlistItemId, this.player);
}
}
/**
* Calls original PlaybackManager's removeFromPlaylist method.
*/
localRemoveFromPlaylist(playlistItemIds) {
if (playbackManager.syncPlayEnabled) {
return playbackManager._localRemoveFromPlaylist(playlistItemIds, this.player);
} else {
return playbackManager.removeFromPlaylist(playlistItemIds, this.player);
}
}
/**
* Calls original PlaybackManager's movePlaylistItem method.
*/
localMovePlaylistItem(playlistItemId, newIndex) {
if (playbackManager.syncPlayEnabled) {
return playbackManager._localMovePlaylistItem(playlistItemId, newIndex, this.player);
} else {
return playbackManager.movePlaylistItem(playlistItemId, newIndex, this.player);
}
}
/**
* Calls original PlaybackManager's queue method.
*/
localQueue(options) {
if (playbackManager.syncPlayEnabled) {
return playbackManager._localQueue(options, this.player);
} else {
return playbackManager.queue(options, this.player);
}
}
/**
* Calls original PlaybackManager's queueNext method.
*/
localQueueNext(options) {
if (playbackManager.syncPlayEnabled) {
return playbackManager._localQueueNext(options, this.player);
} else {
return playbackManager.queueNext(options, this.player);
}
}
/**
* Calls original PlaybackManager's nextTrack method.
*/
localNextTrack() {
if (playbackManager.syncPlayEnabled) {
playbackManager._localNextTrack(this.player);
} else {
playbackManager.nextTrack(this.player);
}
}
/**
* Calls original PlaybackManager's previousTrack method.
*/
localPreviousTrack() {
if (playbackManager.syncPlayEnabled) {
playbackManager._localPreviousTrack(this.player);
} else {
playbackManager.previousTrack(this.player);
}
}
/**
* Calls original PlaybackManager's setRepeatMode method.
*/
localSetRepeatMode(value) {
if (playbackManager.syncPlayEnabled) {
playbackManager._localSetRepeatMode(value, this.player);
} else {
playbackManager.setRepeatMode(value, this.player);
}
}
/**
* Calls original PlaybackManager's setQueueShuffleMode method.
*/
localSetQueueShuffleMode(value) {
if (playbackManager.syncPlayEnabled) {
playbackManager._localSetQueueShuffleMode(value, this.player);
} else {
playbackManager.setQueueShuffleMode(value, this.player);
}
}
/**
* Calls original PlaybackManager's toggleQueueShuffleMode method.
*/
localToggleQueueShuffleMode() {
if (playbackManager.syncPlayEnabled) {
playbackManager._localToggleQueueShuffleMode(this.player);
} else {
playbackManager.toggleQueueShuffleMode(this.player);
}
}
}
export default SyncPlayNoActivePlayer;

View file

@ -0,0 +1,202 @@
/**
* Module that replaces the PlaybackManager's queue.
* @module components/syncPlay/players/queueManager
*/
/**
* Class that replaces the PlaybackManager's queue.
*/
class QueueManager {
constructor(syncPlayManager) {
this.queueCore = syncPlayManager.getQueueCore();
}
/**
* Placeholder for original PlayQueueManager method.
*/
getPlaylist() {
return this.queueCore.getPlaylist();
}
/**
* Placeholder for original PlayQueueManager method.
*/
setPlaylist(items) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
queue(items) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
shufflePlaylist() {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
sortShuffledPlaylist() {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
clearPlaylist(clearCurrentItem = false) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
queueNext(items) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
getCurrentPlaylistIndex() {
return this.queueCore.getCurrentPlaylistIndex();
}
/**
* Placeholder for original PlayQueueManager method.
*/
getCurrentItem() {
const index = this.getCurrentPlaylistIndex();
if (index >= 0) {
const playlist = this.getPlaylist();
return playlist[index];
} else {
return null;
}
}
/**
* Placeholder for original PlayQueueManager method.
*/
getCurrentPlaylistItemId() {
return this.queueCore.getCurrentPlaylistItemId();
}
/**
* Placeholder for original PlayQueueManager method.
*/
setPlaylistState(playlistItemId, playlistIndex) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
setPlaylistIndex(playlistIndex) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
removeFromPlaylist(playlistItemIds) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
movePlaylistItem(playlistItemId, newIndex) {
// Do nothing.
return {
result: 'noop'
};
}
/**
* Placeholder for original PlayQueueManager method.
*/
reset() {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
setRepeatMode(value) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
getRepeatMode() {
return this.queueCore.getRepeatMode();
}
/**
* Placeholder for original PlayQueueManager method.
*/
setShuffleMode(value) {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
toggleShuffleMode() {
// Do nothing.
}
/**
* Placeholder for original PlayQueueManager method.
*/
getShuffleMode() {
return this.queueCore.getShuffleMode();
}
/**
* Placeholder for original PlayQueueManager method.
*/
getNextItemInfo() {
const playlist = this.getPlaylist();
let newIndex;
switch (this.getRepeatMode()) {
case 'RepeatOne':
newIndex = this.getCurrentPlaylistIndex();
break;
case 'RepeatAll':
newIndex = this.getCurrentPlaylistIndex() + 1;
if (newIndex >= playlist.length) {
newIndex = 0;
}
break;
default:
newIndex = this.getCurrentPlaylistIndex() + 1;
break;
}
if (newIndex < 0 || newIndex >= playlist.length) {
return null;
}
const item = playlist[newIndex];
if (!item) {
return null;
}
return {
item: item,
index: newIndex
};
}
}
export default QueueManager;

View file

@ -0,0 +1,34 @@
/**
* Module that notifies user about SyncPlay messages using toasts.
* @module components/syncPlay/syncPlayToasts
*/
import { Events } from 'jellyfin-apiclient';
import toast from '../../toast/toast';
import globalize from '../../../scripts/globalize';
import SyncPlay from 'SyncPlay';
/**
* Class that notifies user about SyncPlay messages using toasts.
*/
class SyncPlayToasts {
constructor() {
// Do nothing.
}
/**
* Listens for messages to show.
*/
init() {
Events.on(SyncPlay.Manager, 'show-message', (event, data) => {
const { message, args = [] } = data;
toast({
text: globalize.translate(message, ...args)
});
});
}
}
/** SyncPlayToasts singleton. */
const syncPlayToasts = new SyncPlayToasts();
export default syncPlayToasts;