mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Fix code issues
This commit is contained in:
parent
a2ba96ab82
commit
11f6217bb2
17 changed files with 340 additions and 242 deletions
|
@ -520,8 +520,8 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
|
|||
var list = [];
|
||||
var audio = document.createElement('audio');
|
||||
|
||||
if (typeof audio.playbackRate === "number") {
|
||||
list.push("PlaybackRate");
|
||||
if (typeof audio.playbackRate === 'number') {
|
||||
list.push('PlaybackRate');
|
||||
}
|
||||
|
||||
return list;
|
||||
|
|
|
@ -1442,8 +1442,8 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
|||
list.push('AirPlay');
|
||||
}
|
||||
|
||||
if (typeof video.playbackRate === "number") {
|
||||
list.push("PlaybackRate");
|
||||
if (typeof video.playbackRate === 'number') {
|
||||
list.push('PlaybackRate');
|
||||
}
|
||||
|
||||
list.push('SetBrightness');
|
||||
|
|
|
@ -54,6 +54,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
|||
if (!serverId) {
|
||||
// Not a server item
|
||||
// We can expand on this later and possibly report them
|
||||
events.trigger(playbackManagerInstance, 'reportplayback', [false]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,7 +78,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
|||
}
|
||||
|
||||
var apiClient = connectionManager.getApiClient(serverId);
|
||||
apiClient[method](info);
|
||||
var reportPlaybackPromise = apiClient[method](info);
|
||||
// Notify that report has been sent
|
||||
reportPlaybackPromise.then(() => {
|
||||
events.trigger(playbackManagerInstance, 'reportplayback', [true]);
|
||||
});
|
||||
}
|
||||
|
||||
function getPlaylistSync(playbackManagerInstance, player) {
|
||||
|
@ -3777,18 +3782,14 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
|||
}
|
||||
};
|
||||
|
||||
PlaybackManager.prototype.setPlaybackRate = function (value, player) {
|
||||
player = player || this._currentPlayer;
|
||||
|
||||
if (player) {
|
||||
PlaybackManager.prototype.setPlaybackRate = function (value, player = this._currentPlayer) {
|
||||
if (player && player.setPlaybackRate) {
|
||||
player.setPlaybackRate(value);
|
||||
}
|
||||
};
|
||||
|
||||
PlaybackManager.prototype.getPlaybackRate = function (player) {
|
||||
player = player || this._currentPlayer;
|
||||
|
||||
if (player) {
|
||||
PlaybackManager.prototype.getPlaybackRate = function (player = this._currentPlayer) {
|
||||
if (player && player.getPlaybackRate) {
|
||||
return player.getPlaybackRate();
|
||||
}
|
||||
|
||||
|
|
|
@ -332,17 +332,17 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'syncplay
|
|||
var stats = syncplayManager.getStats();
|
||||
|
||||
syncStats.push({
|
||||
label: globalize.translate("LabelSyncplayTimeOffset"),
|
||||
value: stats.TimeOffset + "ms"
|
||||
label: globalize.translate('LabelSyncplayTimeOffset'),
|
||||
value: stats.TimeOffset + globalize.translate('MillisecondsUnit')
|
||||
});
|
||||
|
||||
syncStats.push({
|
||||
label: globalize.translate("LabelSyncplayPlaybackDiff"),
|
||||
value: stats.PlaybackDiff + "ms"
|
||||
label: globalize.translate('LabelSyncplayPlaybackDiff'),
|
||||
value: stats.PlaybackDiff + globalize.translate('MillisecondsUnit')
|
||||
});
|
||||
|
||||
syncStats.push({
|
||||
label: globalize.translate("LabelSyncplaySyncMethod"),
|
||||
label: globalize.translate('LabelSyncplaySyncMethod'),
|
||||
value: stats.SyncMethod
|
||||
});
|
||||
|
||||
|
|
|
@ -187,9 +187,9 @@ define(['connectionManager', 'playbackManager', 'syncplayManager', 'events', 'in
|
|||
events.trigger(serverNotifications, 'UserDataChanged', [apiClient, msg.Data.UserDataList[i]]);
|
||||
}
|
||||
}
|
||||
} else if (msg.MessageType === "SyncplayCommand") {
|
||||
} else if (msg.MessageType === 'SyncplayCommand') {
|
||||
syncplayManager.processCommand(msg.Data, apiClient);
|
||||
} else if (msg.MessageType === "SyncplayGroupUpdate") {
|
||||
} else if (msg.MessageType === 'SyncplayGroupUpdate') {
|
||||
syncplayManager.processGroupUpdate(msg.Data, apiClient);
|
||||
} else {
|
||||
events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]);
|
||||
|
|
|
@ -3,7 +3,6 @@ import connectionManager from 'connectionManager';
|
|||
import playbackManager from 'playbackManager';
|
||||
import syncplayManager from 'syncplayManager';
|
||||
import loading from 'loading';
|
||||
import datetime from 'datetime';
|
||||
import toast from 'toast';
|
||||
import actionsheet from 'actionsheet';
|
||||
import globalize from 'globalize';
|
||||
|
@ -18,13 +17,6 @@ function getActivePlayerId () {
|
|||
return info ? info.id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to avoid console logs about uncaught promises
|
||||
*/
|
||||
function emptyCallback () {
|
||||
// avoid console logs about uncaught promises
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when user needs to join a group.
|
||||
* @param {HTMLElement} button - Element where to place the menu.
|
||||
|
@ -32,47 +24,43 @@ function emptyCallback () {
|
|||
* @param {Object} apiClient - ApiClient.
|
||||
*/
|
||||
function showNewJoinGroupSelection (button, user, apiClient) {
|
||||
let sessionId = getActivePlayerId();
|
||||
sessionId = sessionId ? sessionId : "none";
|
||||
const inSession = sessionId !== "none";
|
||||
const sessionId = getActivePlayerId() || 'none';
|
||||
const inSession = sessionId !== 'none';
|
||||
const policy = user.localUser ? user.localUser.Policy : {};
|
||||
let playingItemId;
|
||||
try {
|
||||
const playState = playbackManager.getPlayerState();
|
||||
playingItemId = playState.NowPlayingItem.Id;
|
||||
console.debug('Item', playingItemId, 'is currently playing.');
|
||||
} catch (error) {
|
||||
playingItemId = "";
|
||||
playingItemId = '';
|
||||
console.debug('No item is currently playing.');
|
||||
}
|
||||
|
||||
apiClient.sendSyncplayCommand(sessionId, "ListGroups").then(function (response) {
|
||||
response.json().then(function (groups) {
|
||||
apiClient.sendSyncplayCommand(sessionId, 'ListGroups').then(function (response) {
|
||||
response.json().then(function (groups) {
|
||||
var menuItems = groups.map(function (group) {
|
||||
// TODO: update running time if group is playing?
|
||||
var name = datetime.getDisplayRunningTime(group.PositionTicks);
|
||||
if (!inSession) {
|
||||
name = group.PlayingItemName;
|
||||
}
|
||||
return {
|
||||
name: name,
|
||||
icon: "group",
|
||||
name: group.PlayingItemName,
|
||||
icon: 'group',
|
||||
id: group.GroupId,
|
||||
selected: false,
|
||||
secondaryText: group.Participants.join(", ")
|
||||
secondaryText: group.Participants.join(', ')
|
||||
};
|
||||
});
|
||||
|
||||
if (inSession && policy.SyncplayAccess === "CreateAndJoinGroups") {
|
||||
if (inSession && policy.SyncplayAccess === 'CreateAndJoinGroups') {
|
||||
menuItems.push({
|
||||
name: globalize.translate('LabelSyncplayNewGroup'),
|
||||
icon: "add",
|
||||
id: "new-group",
|
||||
icon: 'add',
|
||||
id: 'new-group',
|
||||
selected: true,
|
||||
secondaryText: globalize.translate('LabelSyncplayNewGroupDescription')
|
||||
});
|
||||
}
|
||||
|
||||
if (menuItems.length === 0) {
|
||||
if (inSession && policy.SyncplayAccess === "JoinGroups") {
|
||||
if (inSession && policy.SyncplayAccess === 'JoinGroups') {
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayCreateGroupDenied')
|
||||
});
|
||||
|
@ -94,15 +82,17 @@ function showNewJoinGroupSelection (button, user, apiClient) {
|
|||
};
|
||||
|
||||
actionsheet.show(menuOptions).then(function (id) {
|
||||
if (id == "new-group") {
|
||||
apiClient.sendSyncplayCommand(sessionId, "NewGroup");
|
||||
if (id == 'new-group') {
|
||||
apiClient.sendSyncplayCommand(sessionId, 'NewGroup');
|
||||
} else {
|
||||
apiClient.sendSyncplayCommand(sessionId, "JoinGroup", {
|
||||
apiClient.sendSyncplayCommand(sessionId, 'JoinGroup', {
|
||||
GroupId: id,
|
||||
PlayingItemId: playingItemId
|
||||
});
|
||||
}
|
||||
}, emptyCallback);
|
||||
}).catch((error) => {
|
||||
console.error('Syncplay: unexpected error listing groups:', error);
|
||||
});
|
||||
|
||||
loading.hide();
|
||||
});
|
||||
|
@ -110,7 +100,7 @@ function showNewJoinGroupSelection (button, user, apiClient) {
|
|||
console.error(error);
|
||||
loading.hide();
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayNoGroupsAvailable')
|
||||
text: globalize.translate('MessageSyncplayErrorAccessingGroups')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -126,17 +116,16 @@ function showLeaveGroupSelection (button, user, apiClient) {
|
|||
if (!sessionId) {
|
||||
syncplayManager.signalError();
|
||||
toast({
|
||||
// TODO: translate
|
||||
text: "Syncplay error occured."
|
||||
text: globalize.translate('MessageSyncplayErrorNoActivePlayer')
|
||||
});
|
||||
showNewJoinGroupSelection(button, user, apiClient);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const menuItems = [{
|
||||
name: globalize.translate('LabelSyncplayLeaveGroup'),
|
||||
icon: "meeting_room",
|
||||
id: "leave-group",
|
||||
icon: 'meeting_room',
|
||||
id: 'leave-group',
|
||||
selected: true,
|
||||
secondaryText: globalize.translate('LabelSyncplayLeaveGroupDescription')
|
||||
}];
|
||||
|
@ -150,17 +139,19 @@ function showLeaveGroupSelection (button, user, apiClient) {
|
|||
};
|
||||
|
||||
actionsheet.show(menuOptions).then(function (id) {
|
||||
if (id == "leave-group") {
|
||||
apiClient.sendSyncplayCommand(sessionId, "LeaveGroup");
|
||||
if (id == 'leave-group') {
|
||||
apiClient.sendSyncplayCommand(sessionId, 'LeaveGroup');
|
||||
}
|
||||
}, emptyCallback);
|
||||
}).catch((error) => {
|
||||
console.error('Syncplay: unexpected error showing group menu:', error);
|
||||
});
|
||||
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
// Register to Syncplay events
|
||||
let syncplayEnabled = false;
|
||||
events.on(syncplayManager, 'SyncplayEnabled', function (e, enabled) {
|
||||
events.on(syncplayManager, 'enabled', function (e, enabled) {
|
||||
syncplayEnabled = enabled;
|
||||
});
|
||||
|
||||
|
@ -173,11 +164,11 @@ export function show (button) {
|
|||
|
||||
// TODO: should feature be disabled if playback permission is missing?
|
||||
playbackPermissionManager.check().then(() => {
|
||||
console.debug("Playback is allowed.");
|
||||
console.debug('Playback is allowed.');
|
||||
}).catch((error) => {
|
||||
console.error("Playback not allowed!", error);
|
||||
console.error('Playback not allowed!', error);
|
||||
toast({
|
||||
text: globalize.translate("MessageSyncplayPlaybackPermissionRequired")
|
||||
text: globalize.translate('MessageSyncplayPlaybackPermissionRequired')
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ function createTestMediaElement () {
|
|||
document.body.appendChild(elem);
|
||||
|
||||
elem.volume = 1; // Volume should not be zero to trigger proper permissions
|
||||
elem.src = "assets/audio/silence.wav"; // Silent sound
|
||||
elem.src = 'assets/audio/silence.mp3'; // Silent sound
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ function destroyTestMediaElement (elem) {
|
|||
*/
|
||||
class PlaybackPermissionManager {
|
||||
/**
|
||||
* Tests playback permission. Grabs the permission when called inside a click event (or any other valid user interaction).
|
||||
* 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 () {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable indent */
|
||||
|
||||
/**
|
||||
* Module that manages the Syncplay feature.
|
||||
* @module components/syncplay/syncplayManager
|
||||
|
@ -13,15 +11,25 @@ import toast from 'toast';
|
|||
import globalize from 'globalize';
|
||||
|
||||
/**
|
||||
* Waits for an event to be triggered on an object.
|
||||
* Waits for an event to be triggered on an object. An optional timeout can specified after which the promise is rejected.
|
||||
* @param {Object} emitter Object on which to listen for events.
|
||||
* @param {string} eventType Event name to listen for.
|
||||
* @param {number} timeout Time in milliseconds before rejecting promise if event does not trigger.
|
||||
* @returns {Promise} A promise that resolves when the event is triggered.
|
||||
*/
|
||||
function waitForEvent(emitter, eventType) {
|
||||
return new Promise((resolve) => {
|
||||
var callback = () => {
|
||||
function waitForEventOnce(emitter, eventType, timeout) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let rejectTimeout;
|
||||
if (timeout) {
|
||||
rejectTimeout = setTimeout(() => {
|
||||
reject('Timed out.');
|
||||
}, timeout);
|
||||
}
|
||||
const callback = () => {
|
||||
events.off(emitter, eventType, callback);
|
||||
if (rejectTimeout) {
|
||||
clearTimeout(rejectTimeout);
|
||||
}
|
||||
resolve(arguments);
|
||||
};
|
||||
events.on(emitter, eventType, callback);
|
||||
|
@ -33,7 +41,7 @@ function waitForEvent(emitter, eventType) {
|
|||
* @returns {string} The player's id.
|
||||
*/
|
||||
function getActivePlayerId() {
|
||||
var info = playbackManager.getPlayerInfo();
|
||||
var info = playbackManager.getPlayerInfo();
|
||||
return info ? info.id : null;
|
||||
}
|
||||
|
||||
|
@ -48,11 +56,10 @@ const MaxAttemptsSpeedToSync = 3; // attempts before disabling SpeedToSync
|
|||
const MaxAttemptsSync = 5; // attempts before disabling syncing at all
|
||||
|
||||
/**
|
||||
* Time estimation
|
||||
* Other constants
|
||||
*/
|
||||
const PingIntervalTimeoutGreedy = 1000; // milliseconds
|
||||
const PingIntervalTimeoutLowProfile = 60000; // milliseconds
|
||||
const GreedyPingCount = 3;
|
||||
const WaitForEventDefaultTimeout = 30000; // milliseconds
|
||||
const WaitForPlayerEventTimeout = 500; // milliseconds
|
||||
|
||||
/**
|
||||
* Class that manages the Syncplay feature.
|
||||
|
@ -62,7 +69,7 @@ class SyncplayManager {
|
|||
this.playbackRateSupported = false;
|
||||
this.syncEnabled = false;
|
||||
this.playbackDiffMillis = 0; // used for stats
|
||||
this.syncMethod = "None"; // used for stats
|
||||
this.syncMethod = 'None'; // used for stats
|
||||
this.syncAttempts = 0;
|
||||
this.lastSyncTime = new Date();
|
||||
this.syncWatcherTimeout = null; // interval that watches playback time and syncs it
|
||||
|
@ -71,6 +78,7 @@ class SyncplayManager {
|
|||
this.minBufferingThresholdMillis = 1000;
|
||||
|
||||
this.currentPlayer = null;
|
||||
this.localPlayerPlaybackRate = 1.0; // used to restore user PlaybackRate
|
||||
|
||||
this.syncplayEnabledAt = null; // Server time of when Syncplay has been enabled
|
||||
this.syncplayReady = false; // Syncplay is ready after first ping to server
|
||||
|
@ -84,24 +92,37 @@ class SyncplayManager {
|
|||
this.timeOffsetWithServer = 0; // server time minus local time
|
||||
this.roundTripDuration = 0;
|
||||
this.notifySyncplayReady = false;
|
||||
|
||||
events.on(playbackManager, "playerchange", () => {
|
||||
|
||||
events.on(playbackManager, 'playbackstart', (player, state) => {
|
||||
this.onPlaybackStart(player, state);
|
||||
});
|
||||
|
||||
events.on(playbackManager, 'playbackstop', (stopInfo) => {
|
||||
this.onPlaybackStop(stopInfo);
|
||||
});
|
||||
|
||||
events.on(playbackManager, 'playerchange', () => {
|
||||
this.onPlayerChange();
|
||||
});
|
||||
|
||||
this.bindToPlayer(playbackManager.getCurrentPlayer());
|
||||
|
||||
events.on(this, "TimeUpdate", (event) => {
|
||||
events.on(this, 'timeupdate', (event) => {
|
||||
this.syncPlaybackTime();
|
||||
});
|
||||
|
||||
events.on(timeSyncManager, "Update", (event, timeOffset, ping) => {
|
||||
events.on(timeSyncManager, 'update', (event, error, timeOffset, ping) => {
|
||||
if (error) {
|
||||
console.debug('Syncplay, time update issue', error);
|
||||
return;
|
||||
}
|
||||
|
||||
this.timeOffsetWithServer = timeOffset;
|
||||
this.roundTripDuration = ping * 2;
|
||||
|
||||
if (this.notifySyncplayReady) {
|
||||
this.syncplayReady = true;
|
||||
events.trigger(this, "SyncplayReady");
|
||||
events.trigger(this, 'ready');
|
||||
this.notifySyncplayReady = false;
|
||||
}
|
||||
|
||||
|
@ -113,33 +134,55 @@ class SyncplayManager {
|
|||
if (!sessionId) {
|
||||
this.signalError();
|
||||
toast({
|
||||
// TODO: translate
|
||||
text: "Syncplay error occured."
|
||||
text: globalize.translate('MessageSyncplayErrorMissingSession')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
apiClient.sendSyncplayCommand(sessionId, "UpdatePing", {
|
||||
apiClient.sendSyncplayCommand(sessionId, 'UpdatePing', {
|
||||
Ping: ping
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when playback starts.
|
||||
*/
|
||||
onPlaybackStart (player, state) {
|
||||
events.trigger(this, 'playbackstart', [player, state]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when playback stops.
|
||||
*/
|
||||
onPlaybackStop (stopInfo) {
|
||||
events.trigger(this, 'playbackstop', [stopInfo]);
|
||||
if (this.isSyncplayEnabled()) {
|
||||
this.disableSyncplay(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the player changes.
|
||||
*/
|
||||
onPlayerChange () {
|
||||
this.bindToPlayer(playbackManager.getCurrentPlayer());
|
||||
events.trigger(this, "PlayerChange", [this.currentPlayer]);
|
||||
events.trigger(this, 'playerchange', [this.currentPlayer]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on playback state changes.
|
||||
* @param {Object} e The playback state change event.
|
||||
* Called when playback unpauses.
|
||||
*/
|
||||
onPlayPauseStateChanged (e) {
|
||||
events.trigger(this, "PlayPauseStateChange", [this.currentPlayer]);
|
||||
onPlayerUnpause () {
|
||||
events.trigger(this, 'unpause', [this.currentPlayer]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when playback pauses.
|
||||
*/
|
||||
onPlayerPause() {
|
||||
events.trigger(this, 'pause', [this.currentPlayer]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +192,7 @@ class SyncplayManager {
|
|||
onTimeUpdate (e) {
|
||||
// NOTICE: this event is unreliable, at least in Safari
|
||||
// which just stops firing the event after a while.
|
||||
events.trigger(this, "TimeUpdate", [e]);
|
||||
events.trigger(this, 'timeupdate', [e]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,7 +201,7 @@ class SyncplayManager {
|
|||
onPlaying () {
|
||||
// TODO: implement group wait
|
||||
this.lastPlaybackWaiting = null;
|
||||
events.trigger(this, "PlayerPlaying");
|
||||
events.trigger(this, 'playing');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,7 +212,7 @@ class SyncplayManager {
|
|||
if (!this.lastPlaybackWaiting) {
|
||||
this.lastPlaybackWaiting = new Date();
|
||||
}
|
||||
events.trigger(this, "PlayerWaiting");
|
||||
events.trigger(this, 'waiting');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,15 +234,18 @@ class SyncplayManager {
|
|||
this.currentPlayer = player;
|
||||
if (!player) return;
|
||||
}
|
||||
|
||||
// TODO: remove this extra functions
|
||||
|
||||
// FIXME: the following are needed because the 'events' module
|
||||
// is changing the scope when executing the callbacks.
|
||||
// For instance, calling 'onPlayerUnpause' from the wrong scope breaks things because 'this'
|
||||
// points to 'player' (the event emitter) instead of pointing to the SyncplayManager singleton.
|
||||
const self = this;
|
||||
this._onPlayPauseStateChanged = () => {
|
||||
self.onPlayPauseStateChanged();
|
||||
this._onPlayerUnpause = () => {
|
||||
self.onPlayerUnpause();
|
||||
};
|
||||
|
||||
this._onPlayPauseStateChanged = (e) => {
|
||||
self.onPlayPauseStateChanged(e);
|
||||
this._onPlayerPause = () => {
|
||||
self.onPlayerPause();
|
||||
};
|
||||
|
||||
this._onTimeUpdate = (e) => {
|
||||
|
@ -214,12 +260,17 @@ class SyncplayManager {
|
|||
self.onWaiting();
|
||||
};
|
||||
|
||||
events.on(player, "pause", this._onPlayPauseStateChanged);
|
||||
events.on(player, "unpause", this._onPlayPauseStateChanged);
|
||||
events.on(player, "timeupdate", this._onTimeUpdate);
|
||||
events.on(player, "playing", this._onPlaying);
|
||||
events.on(player, "waiting", this._onWaiting);
|
||||
this.playbackRateSupported = player.supports("PlaybackRate");
|
||||
events.on(player, 'unpause', this._onPlayerUnpause);
|
||||
events.on(player, 'pause', this._onPlayerPause);
|
||||
events.on(player, 'timeupdate', this._onTimeUpdate);
|
||||
events.on(player, 'playing', this._onPlaying);
|
||||
events.on(player, 'waiting', this._onWaiting);
|
||||
this.playbackRateSupported = player.supports('PlaybackRate');
|
||||
|
||||
// Save player current PlaybackRate value
|
||||
if (this.playbackRateSupported) {
|
||||
this.localPlayerPlaybackRate = player.getPlaybackRate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,13 +279,15 @@ class SyncplayManager {
|
|||
releaseCurrentPlayer () {
|
||||
var player = this.currentPlayer;
|
||||
if (player) {
|
||||
events.off(player, "pause", this._onPlayPauseStateChanged);
|
||||
events.off(player, "unpause", this._onPlayPauseStateChanged);
|
||||
events.off(player, "timeupdate", this._onTimeUpdate);
|
||||
events.off(player, "playing", this._onPlaying);
|
||||
events.off(player, "waiting", this._onWaiting);
|
||||
events.off(player, 'unpause', this._onPlayerUnpause);
|
||||
events.off(player, 'pause', this._onPlayerPause);
|
||||
events.off(player, 'timeupdate', this._onTimeUpdate);
|
||||
events.off(player, 'playing', this._onPlaying);
|
||||
events.off(player, 'waiting', this._onWaiting);
|
||||
// Restore player original PlaybackRate value
|
||||
if (this.playbackRateSupported) {
|
||||
player.setPlaybackRate(1);
|
||||
player.setPlaybackRate(this.localPlayerPlaybackRate);
|
||||
this.localPlayerPlaybackRate = 1.0;
|
||||
}
|
||||
this.currentPlayer = null;
|
||||
this.playbackRateSupported = false;
|
||||
|
@ -262,8 +315,7 @@ class SyncplayManager {
|
|||
});
|
||||
break;
|
||||
case 'GroupJoined':
|
||||
const enabledAt = new Date(cmd.Data);
|
||||
this.enableSyncplay(apiClient, enabledAt, true);
|
||||
this.enableSyncplay(apiClient, new Date(cmd.Data), true);
|
||||
break;
|
||||
case 'NotInGroup':
|
||||
case 'GroupLeft':
|
||||
|
@ -274,28 +326,28 @@ class SyncplayManager {
|
|||
text: globalize.translate('MessageSyncplayGroupWait', cmd.Data)
|
||||
});
|
||||
break;
|
||||
case 'GroupNotJoined':
|
||||
case 'GroupDoesNotExist':
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayGroupNotJoined', cmd.Data)
|
||||
text: globalize.translate('MessageSyncplayGroupDoesNotExist')
|
||||
});
|
||||
break;
|
||||
case 'CreateGroupDenied':
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayCreateGroupDenied', cmd.Data)
|
||||
text: globalize.translate('MessageSyncplayCreateGroupDenied')
|
||||
});
|
||||
break;
|
||||
case 'JoinGroupDenied':
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayJoinGroupDenied', cmd.Data)
|
||||
text: globalize.translate('MessageSyncplayJoinGroupDenied')
|
||||
});
|
||||
break;
|
||||
case 'LibraryAccessDenied':
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayLibraryAccessDenied', cmd.Data)
|
||||
text: globalize.translate('MessageSyncplayLibraryAccessDenied')
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.error('processSyncplayGroupUpdate does not recognize: ' + cmd.Type);
|
||||
console.error('processSyncplayGroupUpdate: command is not recognised: ' + cmd.Type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -309,12 +361,12 @@ class SyncplayManager {
|
|||
if (cmd === null) return;
|
||||
|
||||
if (!this.isSyncplayEnabled()) {
|
||||
console.debug("Syncplay processCommand: ignoring command", cmd);
|
||||
console.debug('Syncplay processCommand: SyncPlay not enabled, ignoring command', cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.syncplayReady) {
|
||||
console.debug("Syncplay processCommand: queued command", cmd);
|
||||
console.debug('Syncplay processCommand: SyncPlay not ready, queued command', cmd);
|
||||
this.queuedCommand = cmd;
|
||||
return;
|
||||
}
|
||||
|
@ -323,7 +375,7 @@ class SyncplayManager {
|
|||
cmd.EmittedAt = new Date(cmd.EmitttedAt);
|
||||
|
||||
if (cmd.EmitttedAt < this.syncplayEnabledAt) {
|
||||
console.debug("Syncplay processCommand: ignoring old command", cmd);
|
||||
console.debug('Syncplay processCommand: ignoring old command', cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -333,12 +385,12 @@ class SyncplayManager {
|
|||
this.lastCommand.PositionTicks === cmd.PositionTicks &&
|
||||
this.Command === cmd.Command
|
||||
) {
|
||||
console.debug("Syncplay processCommand: ignoring duplicate command", cmd);
|
||||
console.debug('Syncplay processCommand: ignoring duplicate command', cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastCommand = cmd;
|
||||
console.log("Syncplay will", cmd.Command, "at", cmd.When, "PositionTicks", cmd.PositionTicks);
|
||||
console.log('Syncplay will', cmd.Command, 'at', cmd.When, 'PositionTicks', cmd.PositionTicks);
|
||||
|
||||
switch (cmd.Command) {
|
||||
case 'Play':
|
||||
|
@ -351,7 +403,7 @@ class SyncplayManager {
|
|||
this.scheduleSeek(cmd.When, cmd.PositionTicks);
|
||||
break;
|
||||
default:
|
||||
console.error('processSyncplayCommand does not recognize: ' + cmd.Type);
|
||||
console.error('processCommand: command is not recognised: ' + cmd.Type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +415,7 @@ class SyncplayManager {
|
|||
* @param {Object} sessionData Info about the content to load.
|
||||
*/
|
||||
prepareSession (apiClient, groupId, sessionData) {
|
||||
var serverId = apiClient.serverInfo().Id;
|
||||
const serverId = apiClient.serverInfo().Id;
|
||||
playbackManager.play({
|
||||
ids: sessionData.ItemIds,
|
||||
startPositionTicks: sessionData.StartPositionTicks,
|
||||
|
@ -373,14 +425,12 @@ class SyncplayManager {
|
|||
startIndex: sessionData.StartIndex,
|
||||
serverId: serverId
|
||||
}).then(() => {
|
||||
waitForEvent(this, "PlayerChange").then(() => {
|
||||
playbackManager.pause();
|
||||
waitForEventOnce(this, 'playbackstart', WaitForEventDefaultTimeout).then(() => {
|
||||
var sessionId = getActivePlayerId();
|
||||
if (!sessionId) {
|
||||
console.error("Missing sessionId!");
|
||||
console.error('Missing sessionId!');
|
||||
toast({
|
||||
// TODO: translate
|
||||
text: "Failed to enable Syncplay! Missing session id."
|
||||
text: globalize.translate('MessageSyncplayErrorMissingSession')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -390,21 +440,38 @@ class SyncplayManager {
|
|||
const playState = playbackManager.getPlayerState();
|
||||
playingItemId = playState.NowPlayingItem.Id;
|
||||
} catch (error) {
|
||||
playingItemId = "";
|
||||
playingItemId = '';
|
||||
}
|
||||
// Sometimes JoinGroup fails, maybe because server hasn't been updated yet
|
||||
setTimeout(() => {
|
||||
apiClient.sendSyncplayCommand(sessionId, "JoinGroup", {
|
||||
// Make sure the server has received the player state
|
||||
waitForEventOnce(playbackManager, 'reportplayback', WaitForEventDefaultTimeout).then((success) => {
|
||||
this.localPause();
|
||||
if (!success) {
|
||||
console.warning('Error reporting playback state to server. Joining group will fail.');
|
||||
}
|
||||
apiClient.sendSyncplayCommand(sessionId, 'JoinGroup', {
|
||||
GroupId: groupId,
|
||||
PlayingItemId: playingItemId
|
||||
});
|
||||
}, 500);
|
||||
}).catch(() => {
|
||||
console.error('Timed out while waiting for `reportplayback` event!');
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayErrorMedia')
|
||||
});
|
||||
return;
|
||||
});
|
||||
}).catch(() => {
|
||||
console.error('Timed out while waiting for `playbackstart` event!');
|
||||
if (!this.isSyncplayEnabled()) {
|
||||
toast({
|
||||
text: globalize.translate('MessageSyncplayErrorMedia')
|
||||
});
|
||||
}
|
||||
return;
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
toast({
|
||||
// TODO: translate
|
||||
text: "Failed to enable Syncplay! Media error."
|
||||
text: globalize.translate('MessageSyncplayErrorMedia')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -418,12 +485,13 @@ class SyncplayManager {
|
|||
enableSyncplay (apiClient, enabledAt, showMessage = false) {
|
||||
this.syncplayEnabledAt = enabledAt;
|
||||
this.injectPlaybackManager();
|
||||
events.trigger(this, "SyncplayEnabled", [true]);
|
||||
events.trigger(this, 'enabled', [true]);
|
||||
|
||||
waitForEvent(this, "SyncplayReady").then(() => {
|
||||
waitForEventOnce(this, 'ready').then(() => {
|
||||
this.processCommand(this.queuedCommand, apiClient);
|
||||
this.queuedCommand = null;
|
||||
});
|
||||
|
||||
this.syncplayReady = false;
|
||||
this.notifySyncplayReady = true;
|
||||
|
||||
|
@ -446,7 +514,7 @@ class SyncplayManager {
|
|||
this.lastCommand = null;
|
||||
this.queuedCommand = null;
|
||||
this.syncEnabled = false;
|
||||
events.trigger(this, "SyncplayEnabled", [false]);
|
||||
events.trigger(this, 'enabled', [false]);
|
||||
this.restorePlaybackManager();
|
||||
|
||||
if (showMessage) {
|
||||
|
@ -461,7 +529,7 @@ class SyncplayManager {
|
|||
* @returns {boolean} _true_ if user joined a group, _false_ otherwise.
|
||||
*/
|
||||
isSyncplayEnabled () {
|
||||
return this.syncplayEnabledAt !== null ? true : false;
|
||||
return this.syncplayEnabledAt !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -471,15 +539,15 @@ class SyncplayManager {
|
|||
*/
|
||||
schedulePlay (playAtTime, positionTicks) {
|
||||
this.clearScheduledCommand();
|
||||
var currentTime = new Date();
|
||||
var playAtTimeLocal = timeSyncManager.serverDateToLocal(playAtTime);
|
||||
const currentTime = new Date();
|
||||
const playAtTimeLocal = timeSyncManager.serverDateToLocal(playAtTime);
|
||||
|
||||
if (playAtTimeLocal > currentTime) {
|
||||
var playTimeout = playAtTimeLocal - currentTime;
|
||||
playbackManager.syncplay_seek(positionTicks);
|
||||
const playTimeout = playAtTimeLocal - currentTime;
|
||||
this.localSeek(positionTicks);
|
||||
|
||||
this.scheduledCommand = setTimeout(() => {
|
||||
playbackManager.syncplay_unpause();
|
||||
this.localUnpause();
|
||||
|
||||
this.syncTimeout = setTimeout(() => {
|
||||
this.syncEnabled = true;
|
||||
|
@ -487,12 +555,14 @@ class SyncplayManager {
|
|||
|
||||
}, playTimeout);
|
||||
|
||||
// console.debug("Syncplay schedulePlay:", playTimeout);
|
||||
console.debug('Scheduled play in', playTimeout / 1000.0, 'seconds.');
|
||||
} else {
|
||||
// Group playback already started
|
||||
var serverPositionTicks = positionTicks + (currentTime - playAtTimeLocal) * 10000;
|
||||
playbackManager.syncplay_unpause();
|
||||
playbackManager.syncplay_seek(serverPositionTicks);
|
||||
const serverPositionTicks = positionTicks + (currentTime - playAtTimeLocal) * 10000;
|
||||
waitForEventOnce(this, 'unpause').then(() => {
|
||||
this.localSeek(serverPositionTicks);
|
||||
});
|
||||
this.localUnpause();
|
||||
|
||||
this.syncTimeout = setTimeout(() => {
|
||||
this.syncEnabled = true;
|
||||
|
@ -507,24 +577,26 @@ class SyncplayManager {
|
|||
*/
|
||||
schedulePause (pauseAtTime, positionTicks) {
|
||||
this.clearScheduledCommand();
|
||||
var currentTime = new Date();
|
||||
var pauseAtTimeLocal = timeSyncManager.serverDateToLocal(pauseAtTime);
|
||||
const currentTime = new Date();
|
||||
const pauseAtTimeLocal = timeSyncManager.serverDateToLocal(pauseAtTime);
|
||||
|
||||
const callback = () => {
|
||||
waitForEventOnce(this, 'pause', WaitForPlayerEventTimeout).then(() => {
|
||||
this.localSeek(positionTicks);
|
||||
}).catch(() => {
|
||||
// Player was already paused, seeking
|
||||
this.localSeek(positionTicks);
|
||||
});
|
||||
this.localPause();
|
||||
};
|
||||
|
||||
if (pauseAtTimeLocal > currentTime) {
|
||||
var pauseTimeout = pauseAtTimeLocal - currentTime;
|
||||
const pauseTimeout = pauseAtTimeLocal - currentTime;
|
||||
this.scheduledCommand = setTimeout(callback, pauseTimeout);
|
||||
|
||||
this.scheduledCommand = setTimeout(() => {
|
||||
playbackManager.syncplay_pause();
|
||||
setTimeout(() => {
|
||||
playbackManager.syncplay_seek(positionTicks);
|
||||
}, 800);
|
||||
|
||||
}, pauseTimeout);
|
||||
console.debug('Scheduled pause in', pauseTimeout / 1000.0, 'seconds.');
|
||||
} else {
|
||||
playbackManager.syncplay_pause();
|
||||
setTimeout(() => {
|
||||
playbackManager.syncplay_seek(positionTicks);
|
||||
}, 800);
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,10 +630,10 @@ class SyncplayManager {
|
|||
if (!this.isSyncplayEnabled()) return;
|
||||
if (playbackManager.syncplayEnabled) return;
|
||||
|
||||
// TODO: make this less hacky
|
||||
playbackManager.syncplay_unpause = playbackManager.unpause;
|
||||
playbackManager.syncplay_pause = playbackManager.pause;
|
||||
playbackManager.syncplay_seek = playbackManager.seek;
|
||||
// TODO: make this less hacky
|
||||
playbackManager._localUnpause = playbackManager.unpause;
|
||||
playbackManager._localPause = playbackManager.pause;
|
||||
playbackManager._localSeek = playbackManager.seek;
|
||||
|
||||
playbackManager.unpause = this.playRequest;
|
||||
playbackManager.pause = this.pauseRequest;
|
||||
|
@ -576,9 +648,9 @@ class SyncplayManager {
|
|||
if (this.isSyncplayEnabled()) return;
|
||||
if (!playbackManager.syncplayEnabled) return;
|
||||
|
||||
playbackManager.unpause = playbackManager.syncplay_unpause;
|
||||
playbackManager.pause = playbackManager.syncplay_pause;
|
||||
playbackManager.seek = playbackManager.syncplay_seek;
|
||||
playbackManager.unpause = playbackManager._localUnpause;
|
||||
playbackManager.pause = playbackManager._localPause;
|
||||
playbackManager.seek = playbackManager._localSeek;
|
||||
playbackManager.syncplayEnabled = false;
|
||||
}
|
||||
|
||||
|
@ -588,7 +660,7 @@ class SyncplayManager {
|
|||
playRequest (player) {
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
var sessionId = getActivePlayerId();
|
||||
apiClient.sendSyncplayCommand(sessionId, "PlayRequest");
|
||||
apiClient.sendSyncplayCommand(sessionId, 'PlayRequest');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -597,9 +669,9 @@ class SyncplayManager {
|
|||
pauseRequest (player) {
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
var sessionId = getActivePlayerId();
|
||||
apiClient.sendSyncplayCommand(sessionId, "PauseRequest");
|
||||
apiClient.sendSyncplayCommand(sessionId, 'PauseRequest');
|
||||
// Pause locally as well, to give the user some little control
|
||||
playbackManager.syncplay_pause();
|
||||
playbackManager._localUnpause(player);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -608,14 +680,47 @@ class SyncplayManager {
|
|||
seekRequest (PositionTicks, player) {
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
var sessionId = getActivePlayerId();
|
||||
apiClient.sendSyncplayCommand(sessionId, "SeekRequest", {
|
||||
apiClient.sendSyncplayCommand(sessionId, 'SeekRequest', {
|
||||
PositionTicks: PositionTicks
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls original PlaybackManager's unpause method.
|
||||
*/
|
||||
localUnpause(player) {
|
||||
if (playbackManager.syncplayEnabled) {
|
||||
playbackManager._localUnpause(player);
|
||||
} else {
|
||||
playbackManager.unpause(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls original PlaybackManager's pause method.
|
||||
*/
|
||||
localPause(player) {
|
||||
if (playbackManager.syncplayEnabled) {
|
||||
playbackManager._localPause(player);
|
||||
} else {
|
||||
playbackManager.pause(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls original PlaybackManager's seek method.
|
||||
*/
|
||||
localSeek(PositionTicks, player) {
|
||||
if (playbackManager.syncplayEnabled) {
|
||||
playbackManager._localSeek(PositionTicks, player);
|
||||
} else {
|
||||
playbackManager.seek(PositionTicks, player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to sync playback time with estimated server time.
|
||||
*
|
||||
*
|
||||
* When sync is enabled, the following will be checked:
|
||||
* - check if local playback time is close enough to the server playback time
|
||||
* If it is not, then a playback time sync will be attempted.
|
||||
|
@ -637,18 +742,15 @@ class SyncplayManager {
|
|||
|
||||
const playAtTime = this.lastCommand.When;
|
||||
|
||||
const CurrentPositionTicks = playbackManager.currentTime();
|
||||
const currentPositionTicks = playbackManager.currentTime();
|
||||
// Estimate PositionTicks on server
|
||||
const ServerPositionTicks = this.lastCommand.PositionTicks + ((currentTime - playAtTime) + this.timeOffsetWithServer) * 10000;
|
||||
const serverPositionTicks = this.lastCommand.PositionTicks + ((currentTime - playAtTime) + this.timeOffsetWithServer) * 10000;
|
||||
// Measure delay that needs to be recovered
|
||||
// diff might be caused by the player internally starting the playback
|
||||
const diff = ServerPositionTicks - CurrentPositionTicks;
|
||||
const diffMillis = diff / 10000;
|
||||
const diffMillis = (serverPositionTicks - currentPositionTicks) / 10000.0;
|
||||
|
||||
this.playbackDiffMillis = diffMillis;
|
||||
|
||||
// console.debug("Syncplay onTimeUpdate", diffMillis, CurrentPositionTicks, ServerPositionTicks);
|
||||
|
||||
if (this.syncEnabled) {
|
||||
const absDiffMillis = Math.abs(diffMillis);
|
||||
// TODO: SpeedToSync sounds bad on songs
|
||||
|
@ -664,7 +766,7 @@ class SyncplayManager {
|
|||
this.currentPlayer.setPlaybackRate(speed);
|
||||
this.syncEnabled = false;
|
||||
this.syncAttempts++;
|
||||
this.showSyncIcon("SpeedToSync (x" + speed + ")");
|
||||
this.showSyncIcon('SpeedToSync (x' + speed + ')');
|
||||
|
||||
this.syncTimeout = setTimeout(() => {
|
||||
this.currentPlayer.setPlaybackRate(1);
|
||||
|
@ -675,13 +777,13 @@ class SyncplayManager {
|
|||
// Disable SkipToSync if it keeps failing
|
||||
if (this.syncAttempts > MaxAttemptsSync) {
|
||||
this.syncEnabled = false;
|
||||
this.showSyncIcon("Sync disabled (too many attempts)");
|
||||
this.showSyncIcon('Sync disabled (too many attempts)');
|
||||
}
|
||||
// SkipToSync method
|
||||
playbackManager.syncplay_seek(ServerPositionTicks);
|
||||
this.localSeek(serverPositionTicks);
|
||||
this.syncEnabled = false;
|
||||
this.syncAttempts++;
|
||||
this.showSyncIcon("SkipToSync (" + this.syncAttempts + ")");
|
||||
this.showSyncIcon('SkipToSync (' + this.syncAttempts + ')');
|
||||
|
||||
this.syncTimeout = setTimeout(() => {
|
||||
this.syncEnabled = true;
|
||||
|
@ -690,7 +792,7 @@ class SyncplayManager {
|
|||
} else {
|
||||
// Playback is synced
|
||||
if (this.syncAttempts > 0) {
|
||||
// console.debug("Playback has been synced after", this.syncAttempts, "attempts.");
|
||||
console.debug('Playback has been synced after', this.syncAttempts, 'attempts.');
|
||||
}
|
||||
this.syncAttempts = 0;
|
||||
}
|
||||
|
@ -706,7 +808,7 @@ class SyncplayManager {
|
|||
TimeOffset: this.timeOffsetWithServer,
|
||||
PlaybackDiff: this.playbackDiffMillis,
|
||||
SyncMethod: this.syncMethod
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -714,15 +816,15 @@ class SyncplayManager {
|
|||
*/
|
||||
showSyncIcon (syncMethod) {
|
||||
this.syncMethod = syncMethod;
|
||||
events.trigger(this, "SyncplayError", [true]);
|
||||
events.trigger(this, 'syncing', [true, this.syncMethod]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event to clear the Syncplay status icon.
|
||||
*/
|
||||
clearSyncIcon () {
|
||||
this.syncMethod = "None";
|
||||
events.trigger(this, "SyncplayError", [false]);
|
||||
this.syncMethod = 'None';
|
||||
events.trigger(this, 'syncing', [false, this.syncMethod]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable indent */
|
||||
|
||||
/**
|
||||
* Module that manages time syncing with server.
|
||||
* @module components/syncplay/timeSyncManager
|
||||
|
@ -22,30 +20,30 @@ const GreedyPingCount = 3;
|
|||
class Measurement {
|
||||
/**
|
||||
* Creates a new measurement.
|
||||
* @param {Date} t0 Client's timestamp of the request transmission
|
||||
* @param {Date} t1 Server's timestamp of the request reception
|
||||
* @param {Date} t2 Server's timestamp of the response transmission
|
||||
* @param {Date} t3 Client's timestamp of the response reception
|
||||
* @param {Date} requestSent Client's timestamp of the request transmission
|
||||
* @param {Date} requestReceived Server's timestamp of the request reception
|
||||
* @param {Date} responseSent Server's timestamp of the response transmission
|
||||
* @param {Date} responseReceived Client's timestamp of the response reception
|
||||
*/
|
||||
constructor(t0, t1, t2, t3) {
|
||||
this.t0 = t0.getTime();
|
||||
this.t1 = t1.getTime();
|
||||
this.t2 = t2.getTime();
|
||||
this.t3 = t3.getTime();
|
||||
constructor(requestSent, requestReceived, responseSent, responseReceived) {
|
||||
this.requestSent = requestSent.getTime();
|
||||
this.requestReceived = requestReceived.getTime();
|
||||
this.responseSent = responseSent.getTime();
|
||||
this.responseReceived = responseReceived.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Time offset from server.
|
||||
*/
|
||||
getOffset () {
|
||||
return ((this.t1 - this.t0) + (this.t2 - this.t3)) / 2;
|
||||
return ((this.requestReceived - this.requestSent) + (this.responseSent - this.responseReceived)) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get round-trip delay.
|
||||
*/
|
||||
getDelay () {
|
||||
return (this.t3 - this.t0) - (this.t2 - this.t1);
|
||||
return (this.responseReceived - this.requestSent) - (this.responseSent - this.requestReceived);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,7 +74,7 @@ class TimeSyncManager {
|
|||
* @returns {boolean} _true_ if a measurement has been done, _false_ otherwise.
|
||||
*/
|
||||
isReady() {
|
||||
return this.measurement ? true : false;
|
||||
return !!this.measurement;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,14 +117,14 @@ class TimeSyncManager {
|
|||
this.poller = setTimeout(() => {
|
||||
this.poller = null;
|
||||
const apiClient = connectionManager.currentApiClient();
|
||||
const t0 = new Date(); // pingStartTime
|
||||
const requestSent = new Date();
|
||||
apiClient.getServerTime().then((response) => {
|
||||
const t3 = new Date(); // pingEndTime
|
||||
const responseReceived = new Date();
|
||||
response.json().then((data) => {
|
||||
const t1 = new Date(data.RequestReceptionTime); // request received
|
||||
const t2 = new Date(data.ResponseTransmissionTime); // response sent
|
||||
const requestReceived = new Date(data.RequestReceptionTime);
|
||||
const responseSent = new Date(data.ResponseTransmissionTime);
|
||||
|
||||
const measurement = new Measurement(t0, t1, t2, t3);
|
||||
const measurement = new Measurement(requestSent, requestReceived, responseSent, responseReceived);
|
||||
this.updateTimeOffset(measurement);
|
||||
|
||||
// Avoid overloading server
|
||||
|
@ -136,11 +134,11 @@ class TimeSyncManager {
|
|||
this.pings++;
|
||||
}
|
||||
|
||||
events.trigger(this, "Update", [this.getTimeOffset(), this.getPing()]);
|
||||
events.trigger(this, 'update', [null, this.getTimeOffset(), this.getPing()]);
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
events.trigger(this, "Error", [error]);
|
||||
events.trigger(this, 'update', [error, null, null]);
|
||||
}).finally(() => {
|
||||
this.requestPing();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue