1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #1100 from dmitrylyzo/playback_delayed_update-3

Add scheduled playback progress report
This commit is contained in:
dkanada 2020-04-18 11:03:06 +09:00 committed by GitHub
commit 45bf7db7ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 38 deletions

View file

@ -1,6 +1,9 @@
define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'playQueueManager', 'userSettings', 'globalize', 'connectionManager', 'loading', 'apphost', 'screenfull'], function (events, datetime, appSettings, itemHelper, pluginManager, PlayQueueManager, userSettings, globalize, connectionManager, loading, apphost, screenfull) { define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'playQueueManager', 'userSettings', 'globalize', 'connectionManager', 'loading', 'apphost', 'screenfull'], function (events, datetime, appSettings, itemHelper, pluginManager, PlayQueueManager, userSettings, globalize, connectionManager, loading, apphost, screenfull) {
'use strict'; 'use strict';
/** Delay time in ms for reportPlayback logging */
const reportPlaybackLogDelay = 1e3;
function enableLocalPlaylistManagement(player) { function enableLocalPlaylistManagement(player) {
if (player.getPlaylist) { if (player.getPlaylist) {
@ -38,6 +41,12 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
events.trigger(playbackManagerInstance, 'playerchange', [newPlayer, newTarget, previousPlayer]); events.trigger(playbackManagerInstance, 'playerchange', [newPlayer, newTarget, previousPlayer]);
} }
/** Last invoked method */
let reportPlaybackLastMethod;
/** Last invoke time of method */
let reportPlaybackLastTime;
function reportPlayback(playbackManagerInstance, state, player, reportPlaylist, serverId, method, progressEventName) { function reportPlayback(playbackManagerInstance, state, player, reportPlaylist, serverId, method, progressEventName) {
if (!serverId) { if (!serverId) {
@ -57,7 +66,14 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
addPlaylistToPlaybackReport(playbackManagerInstance, info, player, serverId); addPlaylistToPlaybackReport(playbackManagerInstance, info, player, serverId);
} }
console.debug(method + '-' + JSON.stringify(info)); const now = (new Date).getTime();
if (method !== reportPlaybackLastMethod || now - (reportPlaybackLastTime || 0) >= reportPlaybackLogDelay) {
console.debug(method + '-' + JSON.stringify(info));
reportPlaybackLastMethod = method;
reportPlaybackLastTime = now;
}
var apiClient = connectionManager.getApiClient(serverId); var apiClient = connectionManager.getApiClient(serverId);
apiClient[method](info); apiClient[method](info);
} }
@ -3280,7 +3296,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
function onPlaybackVolumeChange(e) { function onPlaybackVolumeChange(e) {
var player = this; var player = this;
sendProgressUpdateDelayed(player, 'volumechange'); sendProgressUpdate(player, 'volumechange');
} }
function onRepeatModeChange(e) { function onRepeatModeChange(e) {
@ -3375,16 +3391,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
pluginManager.ofType('mediaplayer').map(initMediaPlayer); pluginManager.ofType('mediaplayer').map(initMediaPlayer);
/** Delay timer for sendProgressUpdate */
var sendProgressUpdateTimer;
/** Delay time in ms for sendProgressUpdate */
var sendProgressUpdateDelay = 700;
function sendProgressUpdate(player, progressEventName, reportPlaylist) { function sendProgressUpdate(player, progressEventName, reportPlaylist) {
clearTimeout(sendProgressUpdateTimer);
sendProgressUpdateTimer = null;
if (!player) { if (!player) {
throw new Error('player cannot be null'); throw new Error('player cannot be null');
} }
@ -3409,14 +3416,6 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
} }
} }
function sendProgressUpdateDelayed(player, progressEventName, reportPlaylist) {
if (!sendProgressUpdateTimer) {
sendProgressUpdateTimer = setTimeout(function () {
sendProgressUpdate(player, progressEventName, reportPlaylist);
}, sendProgressUpdateDelay);
}
}
function getLiveStreamMediaInfo(player, streamInfo, mediaSource, liveStreamId, serverId) { function getLiveStreamMediaInfo(player, streamInfo, mediaSource, liveStreamId, serverId) {
console.debug('getLiveStreamMediaInfo'); console.debug('getLiveStreamMediaInfo');

View file

@ -1,6 +1,12 @@
define(["events", "appStorage"], function(events, appStorage) { define(["events", "appStorage"], function(events, appStorage) {
"use strict"; "use strict";
/** Report rate limits in ms for different events */
const reportRateLimits = {
"timeupdate": 10000,
"volumechange": 3000
};
function redetectBitrate(instance) { function redetectBitrate(instance) {
stopBitrateDetection(instance), instance.accessToken() && !1 !== instance.enableAutomaticBitrateDetection && setTimeout(redetectBitrateInternal.bind(instance), 6e3) stopBitrateDetection(instance), instance.accessToken() && !1 !== instance.enableAutomaticBitrateDetection && setTimeout(redetectBitrateInternal.bind(instance), 6e3)
} }
@ -231,6 +237,11 @@ define(["events", "appStorage"], function(events, appStorage) {
} }
return 0 return 0
} }
function cancelReportPlaybackProgressPromise(instance) {
if (typeof instance.reportPlaybackProgressCancel === "function") instance.reportPlaybackProgressCancel();
}
ApiClient.prototype.appName = function() { ApiClient.prototype.appName = function() {
return this._appName return this._appName
}, ApiClient.prototype.setRequestHeaders = function(headers) { }, ApiClient.prototype.setRequestHeaders = function(headers) {
@ -1417,6 +1428,7 @@ define(["events", "appStorage"], function(events, appStorage) {
}, ApiClient.prototype.reportPlaybackStart = function(options) { }, ApiClient.prototype.reportPlaybackStart = function(options) {
if (!options) throw new Error("null options"); if (!options) throw new Error("null options");
this.lastPlaybackProgressReport = 0, this.lastPlaybackProgressReportTicks = null, stopBitrateDetection(this); this.lastPlaybackProgressReport = 0, this.lastPlaybackProgressReportTicks = null, stopBitrateDetection(this);
cancelReportPlaybackProgressPromise(this);
var url = this.getUrl("Sessions/Playing"); var url = this.getUrl("Sessions/Playing");
return this.ajax({ return this.ajax({
type: "POST", type: "POST",
@ -1426,25 +1438,74 @@ define(["events", "appStorage"], function(events, appStorage) {
}) })
}, ApiClient.prototype.reportPlaybackProgress = function(options) { }, ApiClient.prototype.reportPlaybackProgress = function(options) {
if (!options) throw new Error("null options"); if (!options) throw new Error("null options");
var newPositionTicks = options.PositionTicks;
if ("timeupdate" === (options.EventName || "timeupdate")) { const eventName = options.EventName || "timeupdate";
var now = (new Date).getTime(), let reportRateLimitTime = reportRateLimits[eventName] || 0;
msSinceLastReport = now - (this.lastPlaybackProgressReport || 0);
if (msSinceLastReport <= 1e4) { const now = (new Date).getTime();
if (!newPositionTicks) return Promise.resolve(); const msSinceLastReport = now - (this.lastPlaybackProgressReport || 0);
var expectedReportTicks = 1e4 * msSinceLastReport + (this.lastPlaybackProgressReportTicks || 0); const newPositionTicks = options.PositionTicks;
if (Math.abs((newPositionTicks || 0) - expectedReportTicks) < 5e7) return Promise.resolve()
} if (msSinceLastReport < reportRateLimitTime && eventName === "timeupdate" && newPositionTicks) {
this.lastPlaybackProgressReport = now const expectedReportTicks = 1e4 * msSinceLastReport + (this.lastPlaybackProgressReportTicks || 0);
} else this.lastPlaybackProgressReport = 0; if (Math.abs(newPositionTicks - expectedReportTicks) >= 5e7) reportRateLimitTime = 0;
this.lastPlaybackProgressReportTicks = newPositionTicks; }
var url = this.getUrl("Sessions/Playing/Progress");
return this.ajax({ if (reportRateLimitTime < (this.reportPlaybackProgressTimeout !== undefined ? this.reportPlaybackProgressTimeout : 1e6)) {
type: "POST", cancelReportPlaybackProgressPromise(this);
data: JSON.stringify(options), }
contentType: "application/json",
url: url this.lastPlaybackProgressOptions = options;
})
if (this.reportPlaybackProgressPromise) return Promise.resolve();
let instance = this;
let promise;
let cancelled = false;
let resetPromise = function () {
if (instance.reportPlaybackProgressPromise !== promise) return;
delete instance.lastPlaybackProgressOptions;
delete instance.reportPlaybackProgressTimeout;
delete instance.reportPlaybackProgressPromise;
delete instance.reportPlaybackProgressCancel;
};
let sendReport = function (lastOptions) {
resetPromise();
if (!lastOptions) throw new Error("null options");
instance.lastPlaybackProgressReport = (new Date).getTime();
instance.lastPlaybackProgressReportTicks = lastOptions.PositionTicks;
const url = instance.getUrl("Sessions/Playing/Progress");
return instance.ajax({
type: "POST",
data: JSON.stringify(lastOptions),
contentType: "application/json",
url: url
});
};
let delay = Math.max(0, reportRateLimitTime - msSinceLastReport);
promise = new Promise((resolve, reject) => setTimeout(resolve, delay)).then(() => {
if (cancelled) return Promise.resolve();
return sendReport(instance.lastPlaybackProgressOptions);
}).finally(() => {
resetPromise();
});
this.reportPlaybackProgressTimeout = reportRateLimitTime;
this.reportPlaybackProgressPromise = promise;
this.reportPlaybackProgressCancel = function () {
cancelled = true;
resetPromise();
};
return promise;
}, ApiClient.prototype.reportOfflineActions = function(actions) { }, ApiClient.prototype.reportOfflineActions = function(actions) {
if (!actions) throw new Error("null actions"); if (!actions) throw new Error("null actions");
var url = this.getUrl("Sync/OfflineActions"); var url = this.getUrl("Sync/OfflineActions");
@ -1489,6 +1550,7 @@ define(["events", "appStorage"], function(events, appStorage) {
}, ApiClient.prototype.reportPlaybackStopped = function(options) { }, ApiClient.prototype.reportPlaybackStopped = function(options) {
if (!options) throw new Error("null options"); if (!options) throw new Error("null options");
this.lastPlaybackProgressReport = 0, this.lastPlaybackProgressReportTicks = null, redetectBitrate(this); this.lastPlaybackProgressReport = 0, this.lastPlaybackProgressReportTicks = null, redetectBitrate(this);
cancelReportPlaybackProgressPromise(this);
var url = this.getUrl("Sessions/Playing/Stopped"); var url = this.getUrl("Sessions/Playing/Stopped");
return this.ajax({ return this.ajax({
type: "POST", type: "POST",