hls.js 1.6.0-beta.1.0.canary.10788 → 1.6.0-beta.1.0.canary.10791

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.
@@ -208,9 +208,6 @@ class AudioTrackController extends BasePlaylistController {
208
208
  error,
209
209
  });
210
210
  }
211
- } else if (this.shouldReloadPlaylist(currentTrack)) {
212
- // Retry playlist loading if no playlist is or has been loaded yet
213
- this.setAudioTrack(this.trackId);
214
211
  }
215
212
  }
216
213
 
@@ -224,7 +221,6 @@ class AudioTrackController extends BasePlaylistController {
224
221
  data.context.id === this.trackId &&
225
222
  (!this.groupIds || this.groupIds.indexOf(data.context.groupId) !== -1)
226
223
  ) {
227
- this.requestScheduled = -1;
228
224
  this.checkRetry(data);
229
225
  }
230
226
  }
@@ -319,9 +315,6 @@ class AudioTrackController extends BasePlaylistController {
319
315
  return;
320
316
  }
321
317
 
322
- // stopping live reloading timer if any
323
- this.clearTimer();
324
-
325
318
  this.selectDefaultTrack = false;
326
319
  const lastTrack = this.currentTrack;
327
320
  const track = tracks[newId];
@@ -403,49 +396,42 @@ class AudioTrackController extends BasePlaylistController {
403
396
  }
404
397
 
405
398
  protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters): void {
399
+ super.loadPlaylist();
406
400
  const audioTrack = this.currentTrack;
407
- if (!audioTrack) {
401
+ if (!this.shouldLoadPlaylist(audioTrack)) {
408
402
  return;
409
403
  }
410
- let url = audioTrack.url;
411
- if (
412
- this.shouldLoadPlaylist(audioTrack) &&
413
- url !== this.hls.levels[this.hls.loadLevel]?.uri
414
- ) {
415
- super.loadPlaylist();
416
- const id = audioTrack.id;
417
- const groupId = audioTrack.groupId as string;
418
- if (hlsUrlParameters) {
419
- try {
420
- url = hlsUrlParameters.addDirectives(url);
421
- } catch (error) {
422
- this.warn(
423
- `Could not construct new URL with HLS Delivery Directives: ${error}`,
424
- );
425
- }
426
- }
427
- // track not retrieved yet, or live playlist we need to (re)load it
428
- const details = audioTrack.details;
429
- const age = details?.age;
430
- this.log(
431
- `Loading audio-track ${id} "${audioTrack.name}" lang:${audioTrack.lang} group:${groupId}${
432
- hlsUrlParameters?.msn !== undefined
433
- ? ' at sn ' +
434
- hlsUrlParameters.msn +
435
- ' part ' +
436
- hlsUrlParameters.part
437
- : ''
438
- }${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
439
- );
440
- this.clearTimer();
441
- this.hls.trigger(Events.AUDIO_TRACK_LOADING, {
442
- url,
443
- id,
444
- groupId,
445
- deliveryDirectives: hlsUrlParameters || null,
446
- track: audioTrack,
447
- });
404
+ if (audioTrack.url === this.hls.levels[this.hls.loadLevel]?.uri) {
405
+ // Do not load audio rendition with URI matching main variant URI
406
+ return;
448
407
  }
408
+ this.scheduleLoading(audioTrack, hlsUrlParameters);
409
+ }
410
+
411
+ protected loadingPlaylist(
412
+ audioTrack: MediaPlaylist,
413
+ hlsUrlParameters: HlsUrlParameters | undefined,
414
+ ) {
415
+ super.loadingPlaylist(audioTrack, hlsUrlParameters);
416
+ const id = audioTrack.id;
417
+ const groupId = audioTrack.groupId as string;
418
+ const url = this.getUrlWithDirectives(audioTrack.url, hlsUrlParameters);
419
+ const details = audioTrack.details;
420
+ const age = details?.age;
421
+ this.log(
422
+ `Loading audio-track ${id} "${audioTrack.name}" lang:${audioTrack.lang} group:${groupId}${
423
+ hlsUrlParameters?.msn !== undefined
424
+ ? ' at sn ' + hlsUrlParameters.msn + ' part ' + hlsUrlParameters.part
425
+ : ''
426
+ }${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
427
+ );
428
+ this.hls.trigger(Events.AUDIO_TRACK_LOADING, {
429
+ url,
430
+ id,
431
+ groupId,
432
+ deliveryDirectives: hlsUrlParameters || null,
433
+ track: audioTrack,
434
+ });
449
435
  }
450
436
  }
451
437
 
@@ -24,35 +24,33 @@ export default class BasePlaylistController
24
24
  implements NetworkComponentAPI
25
25
  {
26
26
  protected hls: Hls;
27
- protected timer: number = -1;
28
- protected requestScheduled: number = -1;
29
- protected canLoad: boolean = false;
27
+ private timer: number = -1;
28
+ private canLoad: boolean = false;
30
29
 
31
30
  constructor(hls: Hls, logPrefix: string) {
32
31
  super(logPrefix, hls.logger);
33
32
  this.hls = hls;
34
33
  }
35
34
 
36
- public destroy(): void {
35
+ public destroy() {
37
36
  this.clearTimer();
38
37
  // @ts-ignore
39
38
  this.hls = this.log = this.warn = null;
40
39
  }
41
40
 
42
- protected clearTimer(): void {
41
+ private clearTimer() {
43
42
  if (this.timer !== -1) {
44
43
  self.clearTimeout(this.timer);
45
44
  this.timer = -1;
46
45
  }
47
46
  }
48
47
 
49
- public startLoad(): void {
48
+ public startLoad() {
50
49
  this.canLoad = true;
51
- this.requestScheduled = -1;
52
50
  this.loadPlaylist();
53
51
  }
54
52
 
55
- public stopLoad(): void {
53
+ public stopLoad() {
56
54
  this.canLoad = false;
57
55
  this.clearTimer();
58
56
  }
@@ -104,16 +102,22 @@ export default class BasePlaylistController
104
102
  }
105
103
  }
106
104
 
107
- protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters): void {
108
- if (this.requestScheduled === -1) {
109
- this.requestScheduled = self.performance.now();
110
- }
105
+ protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters) {
106
+ // Loading is handled by the subclasses
107
+ this.clearTimer();
108
+ }
109
+
110
+ protected loadingPlaylist(
111
+ playlist: Level | MediaPlaylist,
112
+ hlsUrlParameters?: HlsUrlParameters,
113
+ ) {
111
114
  // Loading is handled by the subclasses
115
+ this.clearTimer();
112
116
  }
113
117
 
114
118
  protected shouldLoadPlaylist(
115
119
  playlist: Level | MediaPlaylist | null | undefined,
116
- ): boolean {
120
+ ): playlist is Level | MediaPlaylist {
117
121
  return (
118
122
  this.canLoad &&
119
123
  !!playlist &&
@@ -122,14 +126,20 @@ export default class BasePlaylistController
122
126
  );
123
127
  }
124
128
 
125
- protected shouldReloadPlaylist(
126
- playlist: Level | MediaPlaylist | null | undefined,
127
- ): boolean {
128
- return (
129
- this.timer === -1 &&
130
- this.requestScheduled === -1 &&
131
- this.shouldLoadPlaylist(playlist)
132
- );
129
+ protected getUrlWithDirectives(
130
+ uri: string,
131
+ hlsUrlParameters: HlsUrlParameters | undefined,
132
+ ): string {
133
+ if (hlsUrlParameters) {
134
+ try {
135
+ return hlsUrlParameters.addDirectives(uri);
136
+ } catch (error) {
137
+ this.warn(
138
+ `Could not construct new URL with HLS Delivery Directives: ${error}`,
139
+ );
140
+ }
141
+ }
142
+ return uri;
133
143
  }
134
144
 
135
145
  protected playlistLoaded(
@@ -158,18 +168,17 @@ export default class BasePlaylistController
158
168
 
159
169
  // if current playlist is a live playlist, arm a timer to reload it
160
170
  if (details.live || previousDetails?.live) {
171
+ const levelOrTrack = 'levelInfo' in data ? data.levelInfo : data.track;
161
172
  details.reloaded(previousDetails);
162
- if (previousDetails) {
163
- this.log(
164
- `live playlist ${index} ${
165
- details.advanced
166
- ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex
167
- : details.updated
168
- ? 'UPDATED'
169
- : 'MISSED'
170
- }`,
171
- );
172
- }
173
+ this.log(
174
+ `live playlist ${index} ${
175
+ details.advanced
176
+ ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex
177
+ : details.updated
178
+ ? 'UPDATED'
179
+ : 'MISSED'
180
+ }`,
181
+ );
173
182
  // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments
174
183
  if (previousDetails && details.fragments.length > 0) {
175
184
  mergeDetails(previousDetails, details);
@@ -250,7 +259,7 @@ export default class BasePlaylistController
250
259
  part,
251
260
  );
252
261
  if (lowLatencyMode || !lastPart) {
253
- this.loadPlaylist(deliveryDirectives);
262
+ this.loadingPlaylist(levelOrTrack, deliveryDirectives);
254
263
  return;
255
264
  }
256
265
  } else if (details.canBlockReload || details.canSkipUntil) {
@@ -261,6 +270,12 @@ export default class BasePlaylistController
261
270
  part,
262
271
  );
263
272
  }
273
+ if (details.requestScheduled === -1) {
274
+ details.requestScheduled = stats.loading.start;
275
+ }
276
+ if (deliveryDirectives && msn !== undefined && details.canBlockReload) {
277
+ details.requestScheduled -= details.partTarget * 1000 || 1000;
278
+ }
264
279
  const bufferInfo = this.hls.mainForwardBufferInfo;
265
280
  const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0;
266
281
  const distanceToLiveEdgeMs = (details.edge - position) * 1000;
@@ -268,55 +283,61 @@ export default class BasePlaylistController
268
283
  details,
269
284
  distanceToLiveEdgeMs,
270
285
  );
271
- if (details.updated && now > this.requestScheduled + reloadInterval) {
272
- this.requestScheduled = stats.loading.start;
273
- }
274
-
275
- if (msn !== undefined && details.canBlockReload) {
276
- this.requestScheduled =
277
- stats.loading.first +
278
- reloadInterval -
279
- (details.partTarget * 1000 || 1000);
280
- } else if (
281
- this.requestScheduled === -1 ||
282
- this.requestScheduled + reloadInterval < now
283
- ) {
284
- this.requestScheduled = now;
285
- } else if (this.requestScheduled - now <= 0) {
286
- this.requestScheduled += reloadInterval;
286
+ if (details.requestScheduled + reloadInterval < now) {
287
+ details.requestScheduled = now;
288
+ } else {
289
+ details.requestScheduled += reloadInterval;
287
290
  }
288
- let estimatedTimeUntilUpdate = this.requestScheduled - now;
289
- estimatedTimeUntilUpdate = Math.max(0, estimatedTimeUntilUpdate);
290
- this.log(
291
- `reload live playlist ${index} in ${Math.round(
292
- estimatedTimeUntilUpdate,
293
- )} ms`,
294
- );
295
- // this.log(
296
- // `live reload ${details.updated ? 'REFRESHED' : 'MISSED'}
297
- // reload in ${estimatedTimeUntilUpdate / 1000}
298
- // round trip ${(stats.loading.end - stats.loading.start) / 1000}
299
- // diff ${
300
- // (reloadInterval -
301
- // (estimatedTimeUntilUpdate +
302
- // stats.loading.end -
303
- // stats.loading.start)) /
304
- // 1000
305
- // }
306
- // reload interval ${reloadInterval / 1000}
307
- // target duration ${details.targetduration}
308
- // distance to edge ${distanceToLiveEdgeMs / 1000}`
309
- // );
310
-
311
- this.timer = self.setTimeout(
312
- () => this.loadPlaylist(deliveryDirectives),
313
- estimatedTimeUntilUpdate,
314
- );
291
+ this.scheduleLoading(levelOrTrack, deliveryDirectives);
315
292
  } else {
316
293
  this.clearTimer();
317
294
  }
318
295
  }
319
296
 
297
+ protected scheduleLoading(
298
+ levelOrTrack: Level | MediaPlaylist,
299
+ deliveryDirectives?: HlsUrlParameters,
300
+ ) {
301
+ const details = levelOrTrack.details;
302
+ if (!details) {
303
+ this.loadingPlaylist(levelOrTrack, deliveryDirectives);
304
+ return;
305
+ }
306
+ const now = self.performance.now();
307
+ const requestScheduled = details.requestScheduled;
308
+ if (now >= requestScheduled) {
309
+ this.loadingPlaylist(levelOrTrack, deliveryDirectives);
310
+ return;
311
+ }
312
+
313
+ const estimatedTimeUntilUpdate = requestScheduled - now;
314
+ this.log(
315
+ `reload live playlist ${levelOrTrack.name || levelOrTrack.bitrate + 'bps'} in ${Math.round(
316
+ estimatedTimeUntilUpdate,
317
+ )} ms`,
318
+ );
319
+ // this.log(
320
+ // `live reload ${details.updated ? 'REFRESHED' : 'MISSED'}
321
+ // reload in ${estimatedTimeUntilUpdate / 1000}
322
+ // round trip ${(stats.loading.end - stats.loading.start) / 1000}
323
+ // diff ${
324
+ // (reloadInterval -
325
+ // (estimatedTimeUntilUpdate +
326
+ // stats.loading.end -
327
+ // stats.loading.start)) /
328
+ // 1000
329
+ // }
330
+ // reload interval ${reloadInterval / 1000}
331
+ // target duration ${details.targetduration}
332
+ // distance to edge ${distanceToLiveEdgeMs / 1000}`
333
+ // );
334
+
335
+ this.timer = self.setTimeout(
336
+ () => this.loadingPlaylist(levelOrTrack, deliveryDirectives),
337
+ estimatedTimeUntilUpdate,
338
+ );
339
+ }
340
+
320
341
  private getDeliveryDirectives(
321
342
  details: LevelDetails,
322
343
  previousDeliveryDirectives: HlsUrlParameters | null,
@@ -344,7 +365,6 @@ export default class BasePlaylistController
344
365
  (!errorAction.resolved &&
345
366
  action === NetworkErrorAction.SendAlternateToPenaltyBox));
346
367
  if (retry) {
347
- this.requestScheduled = -1;
348
368
  if (retryCount >= retryConfig.maxNumRetry) {
349
369
  return false;
350
370
  }
@@ -1663,7 +1663,7 @@ export default class BaseStreamController
1663
1663
  const { media } = this;
1664
1664
  // if we have not yet loaded any fragment, start loading from start position
1665
1665
  let pos = 0;
1666
- if (this.hls.hasEnoughToStart && media) {
1666
+ if (this.hls?.hasEnoughToStart && media) {
1667
1667
  pos = media.currentTime;
1668
1668
  } else if (this.nextLoadPosition >= 0) {
1669
1669
  pos = this.nextLoadPosition;
@@ -442,9 +442,7 @@ export default class LevelController extends BasePlaylistController {
442
442
  lastLevel &&
443
443
  lastPathwayId === pathwayId
444
444
  ) {
445
- if (level.details || this.requestScheduled !== -1) {
446
- return;
447
- }
445
+ return;
448
446
  }
449
447
 
450
448
  this.log(
@@ -572,10 +570,7 @@ export default class LevelController extends BasePlaylistController {
572
570
  data.context.type === PlaylistContextType.LEVEL &&
573
571
  data.context.level === this.level
574
572
  ) {
575
- const retry = this.checkRetry(data);
576
- if (!retry) {
577
- this.requestScheduled = -1;
578
- }
573
+ this.checkRetry(data);
579
574
  }
580
575
  }
581
576
 
@@ -632,47 +627,37 @@ export default class LevelController extends BasePlaylistController {
632
627
 
633
628
  protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters) {
634
629
  super.loadPlaylist();
635
- const currentLevelIndex = this.currentLevelIndex;
636
- const currentLevel = this.currentLevel;
637
-
638
- if (currentLevel && this.shouldLoadPlaylist(currentLevel)) {
639
- let url = currentLevel.uri;
640
- if (hlsUrlParameters) {
641
- try {
642
- url = hlsUrlParameters.addDirectives(url);
643
- } catch (error) {
644
- this.warn(
645
- `Could not construct new URL with HLS Delivery Directives: ${error}`,
646
- );
647
- }
648
- }
630
+ if (this.shouldLoadPlaylist(this.currentLevel)) {
631
+ this.scheduleLoading(this.currentLevel, hlsUrlParameters);
632
+ }
633
+ }
649
634
 
650
- const pathwayId = currentLevel.attrs['PATHWAY-ID'];
651
- const details = currentLevel.details;
652
- const age = details?.age;
653
- this.log(
654
- `Loading level index ${currentLevelIndex}${
655
- hlsUrlParameters?.msn !== undefined
656
- ? ' at sn ' +
657
- hlsUrlParameters.msn +
658
- ' part ' +
659
- hlsUrlParameters.part
660
- : ''
661
- }${pathwayId ? ' Pathway ' + pathwayId : ''}${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
662
- );
635
+ protected loadingPlaylist(
636
+ currentLevel: Level,
637
+ hlsUrlParameters: HlsUrlParameters | undefined,
638
+ ) {
639
+ super.loadingPlaylist(currentLevel, hlsUrlParameters);
640
+ const url = this.getUrlWithDirectives(currentLevel.uri, hlsUrlParameters);
641
+ const currentLevelIndex = this.currentLevelIndex;
642
+ const pathwayId = currentLevel.attrs['PATHWAY-ID'];
643
+ const details = currentLevel.details;
644
+ const age = details?.age;
645
+ this.log(
646
+ `Loading level index ${currentLevelIndex}${
647
+ hlsUrlParameters?.msn !== undefined
648
+ ? ' at sn ' + hlsUrlParameters.msn + ' part ' + hlsUrlParameters.part
649
+ : ''
650
+ }${pathwayId ? ' Pathway ' + pathwayId : ''}${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
651
+ );
663
652
 
664
- // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId);
665
- // console.log('New video quality level audio group id:', levelObject.attrs.AUDIO, level);
666
- this.clearTimer();
667
- this.hls.trigger(Events.LEVEL_LOADING, {
668
- url,
669
- level: currentLevelIndex,
670
- levelInfo: currentLevel,
671
- pathwayId: currentLevel.attrs['PATHWAY-ID'],
672
- id: 0, // Deprecated Level urlId
673
- deliveryDirectives: hlsUrlParameters || null,
674
- });
675
- }
653
+ this.hls.trigger(Events.LEVEL_LOADING, {
654
+ url,
655
+ level: currentLevelIndex,
656
+ levelInfo: currentLevel,
657
+ pathwayId: currentLevel.attrs['PATHWAY-ID'],
658
+ id: 0, // Deprecated Level urlId
659
+ deliveryDirectives: hlsUrlParameters || null,
660
+ });
676
661
  }
677
662
 
678
663
  get nextLoadLevel() {
@@ -292,9 +292,6 @@ class SubtitleTrackController extends BasePlaylistController {
292
292
  if (trackId !== -1 && this.trackId === -1) {
293
293
  this.setSubtitleTrack(trackId);
294
294
  }
295
- } else if (this.shouldReloadPlaylist(currentTrack)) {
296
- // Retry playlist loading if no playlist is or has been loaded yet
297
- this.setSubtitleTrack(this.trackId);
298
295
  }
299
296
  }
300
297
 
@@ -433,42 +430,37 @@ class SubtitleTrackController extends BasePlaylistController {
433
430
 
434
431
  protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters): void {
435
432
  super.loadPlaylist();
436
- const currentTrack = this.currentTrack;
437
- if (this.shouldLoadPlaylist(currentTrack) && currentTrack) {
438
- const id = currentTrack.id;
439
- const groupId = currentTrack.groupId as string;
440
- let url = currentTrack.url;
441
- if (hlsUrlParameters) {
442
- try {
443
- url = hlsUrlParameters.addDirectives(url);
444
- } catch (error) {
445
- this.warn(
446
- `Could not construct new URL with HLS Delivery Directives: ${error}`,
447
- );
448
- }
449
- }
450
- const details = currentTrack.details;
451
- const age = details?.age;
452
- this.log(
453
- `Loading subtitle ${id} "${currentTrack.name}" lang:${currentTrack.lang} group:${groupId}${
454
- hlsUrlParameters?.msn !== undefined
455
- ? ' at sn ' +
456
- hlsUrlParameters.msn +
457
- ' part ' +
458
- hlsUrlParameters.part
459
- : ''
460
- }${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
461
- );
462
- this.hls.trigger(Events.SUBTITLE_TRACK_LOADING, {
463
- url,
464
- id,
465
- groupId,
466
- deliveryDirectives: hlsUrlParameters || null,
467
- track: currentTrack,
468
- });
433
+ if (this.shouldLoadPlaylist(this.currentTrack)) {
434
+ this.scheduleLoading(this.currentTrack, hlsUrlParameters);
469
435
  }
470
436
  }
471
437
 
438
+ protected loadingPlaylist(
439
+ currentTrack: MediaPlaylist,
440
+ hlsUrlParameters: HlsUrlParameters | undefined,
441
+ ) {
442
+ super.loadingPlaylist(currentTrack, hlsUrlParameters);
443
+ const id = currentTrack.id;
444
+ const groupId = currentTrack.groupId as string;
445
+ const url = this.getUrlWithDirectives(currentTrack.url, hlsUrlParameters);
446
+ const details = currentTrack.details;
447
+ const age = details?.age;
448
+ this.log(
449
+ `Loading subtitle ${id} "${currentTrack.name}" lang:${currentTrack.lang} group:${groupId}${
450
+ hlsUrlParameters?.msn !== undefined
451
+ ? ' at sn ' + hlsUrlParameters.msn + ' part ' + hlsUrlParameters.part
452
+ : ''
453
+ }${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
454
+ );
455
+ this.hls.trigger(Events.SUBTITLE_TRACK_LOADING, {
456
+ url,
457
+ id,
458
+ groupId,
459
+ deliveryDirectives: hlsUrlParameters || null,
460
+ track: currentTrack,
461
+ });
462
+ }
463
+
472
464
  /**
473
465
  * Disables the old subtitleTrack and sets current mode on the next subtitleTrack.
474
466
  * This operates on the DOM textTracks.
@@ -528,9 +520,6 @@ class SubtitleTrackController extends BasePlaylistController {
528
520
  return;
529
521
  }
530
522
 
531
- // stopping live reloading timer if any
532
- this.clearTimer();
533
-
534
523
  this.selectDefaultTrack = false;
535
524
  const lastTrack = this.currentTrack;
536
525
  const track: MediaPlaylist | null = tracks[newId] || null;
@@ -20,6 +20,7 @@ export class LevelDetails {
20
20
  public dateRanges: Record<string, DateRange>;
21
21
  public dateRangeTagCount: number = 0;
22
22
  public live: boolean = true;
23
+ public requestScheduled: number = -1;
23
24
  public ageHeader: number = 0;
24
25
  public advancedDateTime?: number;
25
26
  public updated: boolean = true;
@@ -315,6 +315,9 @@ export function mergeDetails(
315
315
  newDetails.driftEnd = oldDetails.driftEnd;
316
316
  newDetails.advancedDateTime = oldDetails.advancedDateTime;
317
317
  }
318
+ if (newDetails.requestScheduled === -1) {
319
+ newDetails.requestScheduled = oldDetails.requestScheduled;
320
+ }
318
321
  }
319
322
 
320
323
  function mergeDateRanges(