11import events from './events' ;
22import appStorage from './appStorage' ;
33
4+ /** Report rate limits in ms for different events */
5+ const reportRateLimits = {
6+ timeupdate : 10000 ,
7+ volumechange : 3000
8+ } ;
9+
410function redetectBitrate ( instance ) {
511 stopBitrateDetection ( instance ) ;
612
@@ -104,6 +110,10 @@ function getFetchPromise(request) {
104110 return fetchWithTimeout ( request . url , fetchRequest , request . timeout ) ;
105111}
106112
113+ function cancelReportPlaybackProgressPromise ( instance ) {
114+ if ( typeof instance . reportPlaybackProgressCancel === 'function' ) instance . reportPlaybackProgressCancel ( ) ;
115+ }
116+
107117/**
108118 * Creates a new api client instance
109119 * @param {String } serverAddress
@@ -2962,6 +2972,7 @@ class ApiClient {
29622972 this . lastPlaybackProgressReportTicks = null ;
29632973 stopBitrateDetection ( this ) ;
29642974
2975+ cancelReportPlaybackProgressPromise ( this ) ;
29652976 const url = this . getUrl ( 'Sessions/Playing' ) ;
29662977
29672978 return this . ajax ( {
@@ -2982,39 +2993,79 @@ class ApiClient {
29822993 throw new Error ( 'null options' ) ;
29832994 }
29842995
2996+ const eventName = options . EventName || 'timeupdate' ;
2997+ let reportRateLimitTime = reportRateLimits [ eventName ] || 0 ;
2998+
2999+ const now = new Date ( ) . getTime ( ) ;
3000+ const msSinceLastReport = now - ( this . lastPlaybackProgressReport || 0 ) ;
29853001 const newPositionTicks = options . PositionTicks ;
29863002
2987- if ( ( options . EventName || 'timeupdate' ) === 'timeupdate' ) {
2988- const now = new Date ( ) . getTime ( ) ;
2989- const msSinceLastReport = now - ( this . lastPlaybackProgressReport || 0 ) ;
3003+ if ( msSinceLastReport < reportRateLimitTime && eventName === 'timeupdate' && newPositionTicks ) {
3004+ const expectedReportTicks = 1e4 * msSinceLastReport + ( this . lastPlaybackProgressReportTicks || 0 ) ;
3005+ if ( Math . abs ( newPositionTicks - expectedReportTicks ) >= 5e7 ) reportRateLimitTime = 0 ;
3006+ }
29903007
2991- if ( msSinceLastReport <= 10000 ) {
2992- if ( ! newPositionTicks ) {
2993- return Promise . resolve ( ) ;
2994- }
3008+ if (
3009+ reportRateLimitTime <
3010+ ( this . reportPlaybackProgressTimeout !== undefined ? this . reportPlaybackProgressTimeout : 1e6 )
3011+ ) {
3012+ cancelReportPlaybackProgressPromise ( this ) ;
3013+ }
29953014
2996- const expectedReportTicks = msSinceLastReport * 10000 + ( this . lastPlaybackProgressReportTicks || 0 ) ;
3015+ this . lastPlaybackProgressOptions = options ;
29973016
2998- if ( Math . abs ( ( newPositionTicks || 0 ) - expectedReportTicks ) < 5000 * 10000 ) {
2999- return Promise . resolve ( ) ;
3000- }
3001- }
3017+ /* eslint-disable-next-line @typescript-eslint/no-misused-promises */
3018+ if ( this . reportPlaybackProgressPromise ) return Promise . resolve ( ) ;
30023019
3003- this . lastPlaybackProgressReport = now ;
3004- } else {
3005- // allow the next timeupdate
3006- this . lastPlaybackProgressReport = 0 ;
3007- }
3020+ let instance = this ;
3021+ let promise ;
3022+ let cancelled = false ;
30083023
3009- this . lastPlaybackProgressReportTicks = newPositionTicks ;
3010- const url = this . getUrl ( 'Sessions/Playing/Progress' ) ;
3024+ let resetPromise = function ( ) {
3025+ if ( instance . reportPlaybackProgressPromise !== promise ) return ;
30113026
3012- return this . ajax ( {
3013- type : 'POST' ,
3014- data : JSON . stringify ( options ) ,
3015- contentType : 'application/json' ,
3016- url
3017- } ) ;
3027+ delete instance . lastPlaybackProgressOptions ;
3028+ delete instance . reportPlaybackProgressTimeout ;
3029+ delete instance . reportPlaybackProgressPromise ;
3030+ delete instance . reportPlaybackProgressCancel ;
3031+ } ;
3032+
3033+ let sendReport = function ( lastOptions ) {
3034+ resetPromise ( ) ;
3035+
3036+ if ( ! lastOptions ) throw new Error ( 'null options' ) ;
3037+
3038+ instance . lastPlaybackProgressReport = new Date ( ) . getTime ( ) ;
3039+ instance . lastPlaybackProgressReportTicks = lastOptions . PositionTicks ;
3040+
3041+ const url = instance . getUrl ( 'Sessions/Playing/Progress' ) ;
3042+ return instance . ajax ( {
3043+ type : 'POST' ,
3044+ data : JSON . stringify ( lastOptions ) ,
3045+ contentType : 'application/json' ,
3046+ url : url
3047+ } ) ;
3048+ } ;
3049+
3050+ let delay = Math . max ( 0 , reportRateLimitTime - msSinceLastReport ) ;
3051+
3052+ promise = new Promise ( ( resolve , reject ) => setTimeout ( resolve , delay ) )
3053+ . then ( ( ) => {
3054+ if ( cancelled ) return Promise . resolve ( ) ;
3055+ return sendReport ( instance . lastPlaybackProgressOptions ) ;
3056+ } )
3057+ . finally ( ( ) => {
3058+ resetPromise ( ) ;
3059+ } ) ;
3060+
3061+ this . reportPlaybackProgressTimeout = reportRateLimitTime ;
3062+ this . reportPlaybackProgressPromise = promise ;
3063+ this . reportPlaybackProgressCancel = function ( ) {
3064+ cancelled = true ;
3065+ resetPromise ( ) ;
3066+ } ;
3067+
3068+ return promise ;
30183069 }
30193070
30203071 reportOfflineActions ( actions ) {
@@ -3102,6 +3153,7 @@ class ApiClient {
31023153 this . lastPlaybackProgressReportTicks = null ;
31033154 redetectBitrate ( this ) ;
31043155
3156+ cancelReportPlaybackProgressPromise ( this ) ;
31053157 const url = this . getUrl ( 'Sessions/Playing/Stopped' ) ;
31063158
31073159 return this . ajax ( {
0 commit comments