@guardvideo/player-sdk 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/EventTracker.d.ts +3 -0
- package/dist/core/EventTracker.d.ts.map +1 -1
- package/dist/core/player.d.ts +13 -0
- package/dist/core/player.d.ts.map +1 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.esm.js +219 -26
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +219 -26
- package/dist/index.js.map +1 -1
- package/dist/react/GuardVideoPlayer.d.ts.map +1 -1
- package/dist/ui/PlayerUI.d.ts +1 -0
- package/dist/ui/PlayerUI.d.ts.map +1 -1
- package/dist/vanilla/guardvideo-player.js +219 -27
- package/dist/vanilla/guardvideo-player.js.map +1 -1
- package/dist/vanilla/guardvideo-player.min.js +1 -1
- package/dist/vanilla/guardvideo-player.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -36692,8 +36692,19 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36692
36692
|
}
|
|
36693
36693
|
}
|
|
36694
36694
|
|
|
36695
|
+
const VALID_EVENT_TYPES = [
|
|
36696
|
+
'watch_chunk',
|
|
36697
|
+
'play',
|
|
36698
|
+
'pause',
|
|
36699
|
+
'seek',
|
|
36700
|
+
'ended',
|
|
36701
|
+
'quality_change',
|
|
36702
|
+
'error',
|
|
36703
|
+
'security_event',
|
|
36704
|
+
];
|
|
36695
36705
|
const MAX_BATCH_SIZE = 25;
|
|
36696
36706
|
const DEFAULT_FLUSH_INTERVAL = 5000;
|
|
36707
|
+
const MAX_FLUSH_BACKOFF = 60000;
|
|
36697
36708
|
class EventTracker {
|
|
36698
36709
|
constructor(config) {
|
|
36699
36710
|
this.buffer = [];
|
|
@@ -36701,6 +36712,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36701
36712
|
this.accumulator = new WatchChunkAccumulator();
|
|
36702
36713
|
this.destroyed = false;
|
|
36703
36714
|
this._onBeforeUnload = null;
|
|
36715
|
+
this.flushBackoff = 0;
|
|
36716
|
+
this.flushing = false;
|
|
36704
36717
|
this.config = {
|
|
36705
36718
|
endpoint: config.endpoint,
|
|
36706
36719
|
tokenId: config.tokenId,
|
|
@@ -36716,6 +36729,12 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36716
36729
|
track(type, positionSeconds, payload) {
|
|
36717
36730
|
if (this.destroyed)
|
|
36718
36731
|
return;
|
|
36732
|
+
if (!VALID_EVENT_TYPES.includes(type)) {
|
|
36733
|
+
if (this.config.debug) {
|
|
36734
|
+
console.warn('[GuardVideo EventTracker] Skipping unknown event type:', type);
|
|
36735
|
+
}
|
|
36736
|
+
return;
|
|
36737
|
+
}
|
|
36719
36738
|
this.buffer.push({
|
|
36720
36739
|
event_type: type,
|
|
36721
36740
|
event_at: new Date().toISOString(),
|
|
@@ -36739,7 +36758,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36739
36758
|
this.config.sessionId = sessionId;
|
|
36740
36759
|
}
|
|
36741
36760
|
async flush() {
|
|
36742
|
-
if (this.destroyed)
|
|
36761
|
+
if (this.destroyed || this.flushing)
|
|
36743
36762
|
return;
|
|
36744
36763
|
const chunks = this.accumulator.drain();
|
|
36745
36764
|
for (const c of chunks) {
|
|
@@ -36747,8 +36766,14 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36747
36766
|
}
|
|
36748
36767
|
if (this.buffer.length === 0)
|
|
36749
36768
|
return;
|
|
36750
|
-
|
|
36751
|
-
|
|
36769
|
+
this.flushing = true;
|
|
36770
|
+
try {
|
|
36771
|
+
const batch = this.buffer.splice(0, MAX_BATCH_SIZE);
|
|
36772
|
+
await this.sendBatch(batch);
|
|
36773
|
+
}
|
|
36774
|
+
finally {
|
|
36775
|
+
this.flushing = false;
|
|
36776
|
+
}
|
|
36752
36777
|
}
|
|
36753
36778
|
destroy() {
|
|
36754
36779
|
this.destroyed = true;
|
|
@@ -36763,6 +36788,11 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36763
36788
|
startAutoFlush() {
|
|
36764
36789
|
this.flushTimer = setInterval(() => this.flush(), this.config.flushIntervalMs);
|
|
36765
36790
|
}
|
|
36791
|
+
restartAutoFlush(intervalMs) {
|
|
36792
|
+
if (this.flushTimer)
|
|
36793
|
+
clearInterval(this.flushTimer);
|
|
36794
|
+
this.flushTimer = setInterval(() => this.flush(), intervalMs);
|
|
36795
|
+
}
|
|
36766
36796
|
hookPageUnload() {
|
|
36767
36797
|
if (typeof window === 'undefined')
|
|
36768
36798
|
return;
|
|
@@ -36808,8 +36838,25 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36808
36838
|
body,
|
|
36809
36839
|
credentials: 'omit',
|
|
36810
36840
|
});
|
|
36811
|
-
if (!resp.ok
|
|
36812
|
-
|
|
36841
|
+
if (!resp.ok) {
|
|
36842
|
+
if (resp.status === 429) {
|
|
36843
|
+
this.flushBackoff = Math.min((this.flushBackoff || this.config.flushIntervalMs) * 2, MAX_FLUSH_BACKOFF);
|
|
36844
|
+
if (this.config.debug) {
|
|
36845
|
+
console.warn('[GuardVideo EventTracker] 429 — backing off to', this.flushBackoff, 'ms');
|
|
36846
|
+
}
|
|
36847
|
+
this.buffer.unshift(...events);
|
|
36848
|
+
this.restartAutoFlush(this.flushBackoff);
|
|
36849
|
+
return;
|
|
36850
|
+
}
|
|
36851
|
+
if (this.config.debug) {
|
|
36852
|
+
console.warn('[GuardVideo EventTracker] Flush failed:', resp.status);
|
|
36853
|
+
}
|
|
36854
|
+
}
|
|
36855
|
+
else {
|
|
36856
|
+
if (this.flushBackoff > 0) {
|
|
36857
|
+
this.flushBackoff = 0;
|
|
36858
|
+
this.restartAutoFlush(this.config.flushIntervalMs);
|
|
36859
|
+
}
|
|
36813
36860
|
}
|
|
36814
36861
|
}
|
|
36815
36862
|
catch (err) {
|
|
@@ -37025,6 +37072,15 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37025
37072
|
this._onSecurityEvent = null;
|
|
37026
37073
|
this.playlistNonce = null;
|
|
37027
37074
|
this.nonceRefreshInProgress = false;
|
|
37075
|
+
this.nonceRefreshTimer = null;
|
|
37076
|
+
this.retryBackoff = 1000;
|
|
37077
|
+
this.MAX_BACKOFF = 30000;
|
|
37078
|
+
this.rateLimitCooldownUntil = 0;
|
|
37079
|
+
this.nonceRefreshPromise = null;
|
|
37080
|
+
this.destroyed = false;
|
|
37081
|
+
this.networkRetryCount = 0;
|
|
37082
|
+
this.MAX_NETWORK_RETRIES = 6;
|
|
37083
|
+
this.pendingRetryTimer = null;
|
|
37028
37084
|
this._onRateChange = this.enforceMaxRate.bind(this);
|
|
37029
37085
|
this.videoElement = videoElement;
|
|
37030
37086
|
this.config = {
|
|
@@ -37235,9 +37291,18 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37235
37291
|
async solveChallenge(tokenId) {
|
|
37236
37292
|
if (!this.config.apiBaseUrl)
|
|
37237
37293
|
return undefined;
|
|
37294
|
+
if (Date.now() < this.rateLimitCooldownUntil) {
|
|
37295
|
+
this.log('Challenge skipped — rate limit cooldown active');
|
|
37296
|
+
return undefined;
|
|
37297
|
+
}
|
|
37238
37298
|
try {
|
|
37239
37299
|
const url = this.config.apiBaseUrl + '/videos/challenge/' + encodeURIComponent(tokenId);
|
|
37240
37300
|
const resp = await fetch(url, { credentials: 'omit' });
|
|
37301
|
+
if (resp.status === 429) {
|
|
37302
|
+
this.enterRateLimitCooldown();
|
|
37303
|
+
this.log('Challenge hit 429 — entering cooldown');
|
|
37304
|
+
return undefined;
|
|
37305
|
+
}
|
|
37241
37306
|
if (!resp.ok) {
|
|
37242
37307
|
this.log('Challenge fetch failed (challenge may be disabled)', resp.status);
|
|
37243
37308
|
return undefined;
|
|
@@ -37263,6 +37328,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37263
37328
|
async acquireNonce(tokenId) {
|
|
37264
37329
|
if (!this.config.apiBaseUrl)
|
|
37265
37330
|
return null;
|
|
37331
|
+
if (Date.now() < this.rateLimitCooldownUntil) {
|
|
37332
|
+
this.log('Nonce acquisition skipped — rate limit cooldown active');
|
|
37333
|
+
return this.playlistNonce;
|
|
37334
|
+
}
|
|
37266
37335
|
try {
|
|
37267
37336
|
const challengeResult = await this.solveChallenge(tokenId);
|
|
37268
37337
|
const url = this.config.apiBaseUrl + '/videos/playlist-session';
|
|
@@ -37276,12 +37345,20 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37276
37345
|
body: JSON.stringify(body),
|
|
37277
37346
|
credentials: 'omit',
|
|
37278
37347
|
});
|
|
37348
|
+
if (resp.status === 429) {
|
|
37349
|
+
this.enterRateLimitCooldown();
|
|
37350
|
+
this.log('Nonce acquisition hit 429 — entering cooldown');
|
|
37351
|
+
return this.playlistNonce;
|
|
37352
|
+
}
|
|
37279
37353
|
if (!resp.ok) {
|
|
37280
37354
|
this.log('Playlist session nonce acquisition failed', resp.status);
|
|
37281
37355
|
return null;
|
|
37282
37356
|
}
|
|
37283
37357
|
const data = await resp.json();
|
|
37284
37358
|
this.log('Playlist nonce acquired, expires in ' + data.expiresIn + 's');
|
|
37359
|
+
this.retryBackoff = 1000;
|
|
37360
|
+
this.networkRetryCount = 0;
|
|
37361
|
+
this.scheduleNonceRefresh(data.expiresIn);
|
|
37285
37362
|
return data.nonce;
|
|
37286
37363
|
}
|
|
37287
37364
|
catch (err) {
|
|
@@ -37289,19 +37366,58 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37289
37366
|
return null;
|
|
37290
37367
|
}
|
|
37291
37368
|
}
|
|
37369
|
+
scheduleNonceRefresh(expiresInSeconds) {
|
|
37370
|
+
if (this.nonceRefreshTimer)
|
|
37371
|
+
clearTimeout(this.nonceRefreshTimer);
|
|
37372
|
+
const refreshMs = Math.max(5000, expiresInSeconds * 750);
|
|
37373
|
+
this.nonceRefreshTimer = setTimeout(() => {
|
|
37374
|
+
if (this.destroyed)
|
|
37375
|
+
return;
|
|
37376
|
+
this.refreshNonce().catch(() => {
|
|
37377
|
+
this.log('Proactive nonce refresh failed');
|
|
37378
|
+
});
|
|
37379
|
+
}, refreshMs);
|
|
37380
|
+
}
|
|
37292
37381
|
async refreshNonce() {
|
|
37293
|
-
if (this.nonceRefreshInProgress)
|
|
37294
|
-
return;
|
|
37382
|
+
if (this.nonceRefreshInProgress && this.nonceRefreshPromise) {
|
|
37383
|
+
return this.nonceRefreshPromise;
|
|
37384
|
+
}
|
|
37295
37385
|
if (!this.embedToken)
|
|
37296
37386
|
return;
|
|
37297
37387
|
this.nonceRefreshInProgress = true;
|
|
37298
|
-
|
|
37299
|
-
|
|
37300
|
-
|
|
37301
|
-
|
|
37302
|
-
|
|
37303
|
-
|
|
37304
|
-
|
|
37388
|
+
this.nonceRefreshPromise = (async () => {
|
|
37389
|
+
try {
|
|
37390
|
+
const nonce = await this.acquireNonce(this.embedToken.tokenId);
|
|
37391
|
+
this.playlistNonce = nonce;
|
|
37392
|
+
}
|
|
37393
|
+
finally {
|
|
37394
|
+
this.nonceRefreshInProgress = false;
|
|
37395
|
+
this.nonceRefreshPromise = null;
|
|
37396
|
+
}
|
|
37397
|
+
})();
|
|
37398
|
+
return this.nonceRefreshPromise;
|
|
37399
|
+
}
|
|
37400
|
+
enterRateLimitCooldown() {
|
|
37401
|
+
this.rateLimitCooldownUntil = Date.now() + this.retryBackoff;
|
|
37402
|
+
this.retryBackoff = Math.min(this.retryBackoff * 2, this.MAX_BACKOFF);
|
|
37403
|
+
}
|
|
37404
|
+
scheduleRetry(fn, delayMs) {
|
|
37405
|
+
if (this.pendingRetryTimer)
|
|
37406
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37407
|
+
this.pendingRetryTimer = setTimeout(() => {
|
|
37408
|
+
this.pendingRetryTimer = null;
|
|
37409
|
+
if (!this.destroyed && this.hls)
|
|
37410
|
+
fn();
|
|
37411
|
+
}, delayMs);
|
|
37412
|
+
}
|
|
37413
|
+
stopLoading() {
|
|
37414
|
+
if (this.pendingRetryTimer) {
|
|
37415
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37416
|
+
this.pendingRetryTimer = null;
|
|
37417
|
+
}
|
|
37418
|
+
this.hls?.stopLoad();
|
|
37419
|
+
this.networkRetryCount = 0;
|
|
37420
|
+
this.retryBackoff = 1000;
|
|
37305
37421
|
}
|
|
37306
37422
|
async initializePlayer() {
|
|
37307
37423
|
if (!this.embedToken) {
|
|
@@ -37341,8 +37457,12 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37341
37457
|
enableWorker: true,
|
|
37342
37458
|
lowLatencyMode: false,
|
|
37343
37459
|
liveSyncDurationCount: 3,
|
|
37344
|
-
manifestLoadingMaxRetry:
|
|
37345
|
-
|
|
37460
|
+
manifestLoadingMaxRetry: 3,
|
|
37461
|
+
manifestLoadingRetryDelay: 2000,
|
|
37462
|
+
levelLoadingMaxRetry: 3,
|
|
37463
|
+
levelLoadingRetryDelay: 2000,
|
|
37464
|
+
fragLoadingMaxRetry: 4,
|
|
37465
|
+
fragLoadingRetryDelay: 1000,
|
|
37346
37466
|
xhrSetup(xhr, url) {
|
|
37347
37467
|
if (url.includes('/watermark-stream/') && url.includes('.m3u8')) {
|
|
37348
37468
|
if (self.playlistNonce) {
|
|
@@ -37350,10 +37470,6 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37350
37470
|
const nonceUrl = url + separator + 'nonce=' + encodeURIComponent(self.playlistNonce);
|
|
37351
37471
|
xhr.open('GET', nonceUrl, true);
|
|
37352
37472
|
self.log('Injected nonce into playlist request');
|
|
37353
|
-
self.playlistNonce = null;
|
|
37354
|
-
self.refreshNonce().catch(() => {
|
|
37355
|
-
self.log('Background nonce refresh failed');
|
|
37356
|
-
});
|
|
37357
37473
|
}
|
|
37358
37474
|
}
|
|
37359
37475
|
},
|
|
@@ -37365,6 +37481,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37365
37481
|
this.log('HLS manifest parsed', { levels: data.levels.map((l) => l.height + 'p') });
|
|
37366
37482
|
this.setState(exports.PlayerState.READY);
|
|
37367
37483
|
this.config.onReady();
|
|
37484
|
+
this.retryBackoff = 1000;
|
|
37485
|
+
this.networkRetryCount = 0;
|
|
37368
37486
|
if (this.config.autoplay)
|
|
37369
37487
|
this.play();
|
|
37370
37488
|
});
|
|
@@ -37380,28 +37498,77 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37380
37498
|
this.currentQuality = quality;
|
|
37381
37499
|
this.log('Quality switched to ' + quality.name);
|
|
37382
37500
|
this.config.onQualityChange(quality.name);
|
|
37501
|
+
this.eventTracker?.track('quality_change', this.videoElement.currentTime, {
|
|
37502
|
+
level: data.level,
|
|
37503
|
+
height: level.height,
|
|
37504
|
+
bitrate: level.bitrate,
|
|
37505
|
+
});
|
|
37383
37506
|
});
|
|
37384
37507
|
this.hls.on(Hls.Events.ERROR, (_event, data) => {
|
|
37385
37508
|
this.error('HLS Error', data);
|
|
37509
|
+
this.eventTracker?.track('error', this.videoElement.currentTime, {
|
|
37510
|
+
type: data.type,
|
|
37511
|
+
details: data.details,
|
|
37512
|
+
fatal: data.fatal,
|
|
37513
|
+
});
|
|
37514
|
+
if (this.destroyed)
|
|
37515
|
+
return;
|
|
37516
|
+
if (Date.now() < this.rateLimitCooldownUntil) {
|
|
37517
|
+
this.log('Suppressing retry — rate limit cooldown active (' +
|
|
37518
|
+
Math.ceil((this.rateLimitCooldownUntil - Date.now()) / 1000) + 's remaining)');
|
|
37519
|
+
if (data.fatal) {
|
|
37520
|
+
const delay = this.rateLimitCooldownUntil - Date.now() + 500;
|
|
37521
|
+
this.scheduleRetry(() => this.hls?.startLoad(), delay);
|
|
37522
|
+
}
|
|
37523
|
+
return;
|
|
37524
|
+
}
|
|
37525
|
+
const httpStatus = data.response?.code;
|
|
37526
|
+
if (httpStatus === 429) {
|
|
37527
|
+
this.enterRateLimitCooldown();
|
|
37528
|
+
this.log('429 detected — entering cooldown for ' + this.retryBackoff + 'ms');
|
|
37529
|
+
this.scheduleRetry(() => {
|
|
37530
|
+
if (this.embedToken?.forensicWatermark) {
|
|
37531
|
+
this.refreshNonce().then(() => this.hls?.startLoad()).catch(() => this.hls?.startLoad());
|
|
37532
|
+
}
|
|
37533
|
+
else {
|
|
37534
|
+
this.hls?.startLoad();
|
|
37535
|
+
}
|
|
37536
|
+
}, this.retryBackoff);
|
|
37537
|
+
return;
|
|
37538
|
+
}
|
|
37386
37539
|
if (data.type === Hls.ErrorTypes.NETWORK_ERROR &&
|
|
37387
37540
|
data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR &&
|
|
37388
37541
|
this.embedToken?.forensicWatermark) {
|
|
37389
|
-
this.
|
|
37542
|
+
this.networkRetryCount++;
|
|
37543
|
+
if (this.networkRetryCount > this.MAX_NETWORK_RETRIES) {
|
|
37544
|
+
this.error('Max network retries exceeded for playlist load');
|
|
37545
|
+
this.handleError({ code: 'NETWORK_ERROR', message: 'Playlist load failed after multiple retries', fatal: true, details: data });
|
|
37546
|
+
return;
|
|
37547
|
+
}
|
|
37548
|
+
this.log('Playlist load failed — refreshing nonce before retry (' + this.networkRetryCount + '/' + this.MAX_NETWORK_RETRIES + ')');
|
|
37390
37549
|
this.refreshNonce().then(() => {
|
|
37391
|
-
|
|
37550
|
+
this.scheduleRetry(() => this.hls?.startLoad(), this.retryBackoff);
|
|
37392
37551
|
}).catch(() => {
|
|
37393
|
-
|
|
37552
|
+
this.scheduleRetry(() => this.hls?.startLoad(), this.retryBackoff);
|
|
37394
37553
|
});
|
|
37554
|
+
this.retryBackoff = Math.min(this.retryBackoff * 1.5, this.MAX_BACKOFF);
|
|
37395
37555
|
return;
|
|
37396
37556
|
}
|
|
37397
37557
|
if (data.fatal) {
|
|
37398
37558
|
switch (data.type) {
|
|
37399
37559
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
37400
|
-
this.
|
|
37560
|
+
this.networkRetryCount++;
|
|
37561
|
+
if (this.networkRetryCount > this.MAX_NETWORK_RETRIES) {
|
|
37562
|
+
this.error('Max network retries exceeded — giving up');
|
|
37563
|
+
this.handleError({ code: 'NETWORK_ERROR', message: 'Network error after multiple retries. Please check your connection.', fatal: true, details: data });
|
|
37564
|
+
return;
|
|
37565
|
+
}
|
|
37566
|
+
this.error('Network error, attempting recovery (' + this.networkRetryCount + '/' + this.MAX_NETWORK_RETRIES + ')...');
|
|
37401
37567
|
if (this.embedToken?.forensicWatermark) {
|
|
37402
37568
|
this.refreshNonce().catch(() => { });
|
|
37403
37569
|
}
|
|
37404
|
-
|
|
37570
|
+
this.scheduleRetry(() => this.hls?.startLoad(), this.retryBackoff);
|
|
37571
|
+
this.retryBackoff = Math.min(this.retryBackoff * 1.5, this.MAX_BACKOFF);
|
|
37405
37572
|
break;
|
|
37406
37573
|
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
37407
37574
|
this.error('Media error, attempting recovery...');
|
|
@@ -37415,8 +37582,21 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37415
37582
|
this.setupVideoEventListeners();
|
|
37416
37583
|
}
|
|
37417
37584
|
setupVideoEventListeners() {
|
|
37418
|
-
this.videoElement.addEventListener('playing', () =>
|
|
37419
|
-
|
|
37585
|
+
this.videoElement.addEventListener('playing', () => {
|
|
37586
|
+
this.setState(exports.PlayerState.PLAYING);
|
|
37587
|
+
if (this.hls)
|
|
37588
|
+
this.hls.startLoad(-1);
|
|
37589
|
+
});
|
|
37590
|
+
this.videoElement.addEventListener('pause', () => {
|
|
37591
|
+
this.setState(exports.PlayerState.PAUSED);
|
|
37592
|
+
if (this.hls && !this.videoElement.seeking) {
|
|
37593
|
+
this.hls.stopLoad();
|
|
37594
|
+
if (this.pendingRetryTimer) {
|
|
37595
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37596
|
+
this.pendingRetryTimer = null;
|
|
37597
|
+
}
|
|
37598
|
+
}
|
|
37599
|
+
});
|
|
37420
37600
|
this.videoElement.addEventListener('waiting', () => this.setState(exports.PlayerState.BUFFERING));
|
|
37421
37601
|
this.videoElement.addEventListener('error', () => {
|
|
37422
37602
|
const error = this.videoElement.error;
|
|
@@ -37443,6 +37623,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37443
37623
|
}
|
|
37444
37624
|
async play() {
|
|
37445
37625
|
try {
|
|
37626
|
+
this.networkRetryCount = 0;
|
|
37627
|
+
this.retryBackoff = 1000;
|
|
37446
37628
|
await this.videoElement.play();
|
|
37447
37629
|
}
|
|
37448
37630
|
catch (err) {
|
|
@@ -37479,6 +37661,15 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37479
37661
|
getState() { return this.state; }
|
|
37480
37662
|
destroy() {
|
|
37481
37663
|
this.log('Destroying player');
|
|
37664
|
+
this.destroyed = true;
|
|
37665
|
+
if (this.pendingRetryTimer) {
|
|
37666
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37667
|
+
this.pendingRetryTimer = null;
|
|
37668
|
+
}
|
|
37669
|
+
if (this.nonceRefreshTimer) {
|
|
37670
|
+
clearTimeout(this.nonceRefreshTimer);
|
|
37671
|
+
this.nonceRefreshTimer = null;
|
|
37672
|
+
}
|
|
37482
37673
|
if (this.eventTracker) {
|
|
37483
37674
|
this.eventTracker.destroy();
|
|
37484
37675
|
this.eventTracker = null;
|
|
@@ -39189,6 +39380,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
39189
39380
|
}
|
|
39190
39381
|
play() { return this.corePlayer.play(); }
|
|
39191
39382
|
pause() { return this.corePlayer.pause(); }
|
|
39383
|
+
stopLoading() { return this.corePlayer.stopLoading(); }
|
|
39192
39384
|
seek(t) { return this.corePlayer.seek(t); }
|
|
39193
39385
|
getCurrentTime() { return this.corePlayer.getCurrentTime(); }
|
|
39194
39386
|
getDuration() { return this.corePlayer.getDuration(); }
|