@amazon/vinyl 3.2.2 → 3.2.3-next.20261001224604

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.
Files changed (92) hide show
  1. package/README.md +2 -2
  2. package/dist/index.cjs +62 -21
  3. package/dist/index.cjs.map +3 -3
  4. package/dist/index.js +65 -22
  5. package/dist/index.js.map +3 -3
  6. package/dist/test/benchmark.es5.js +7 -6
  7. package/dist/test/benchmark.js +35 -30
  8. package/dist/test/benchmark.js.map +3 -3
  9. package/dist/test/integ.es5.js +184 -171
  10. package/dist/test/integ.js +450 -165
  11. package/dist/test/integ.js.map +4 -4
  12. package/dist/types/current/ad/AdControllerImpl.d.ts.map +1 -1
  13. package/dist/types/current/ad/AdControllerImpl.js +15 -1
  14. package/dist/types/current/ad/discoverHlsInterstitials.js +13 -1
  15. package/dist/types/current/drm/commonEme/MsCommonEme.d.ts.map +1 -1
  16. package/dist/types/current/drm/commonEme/MsCommonEme.js +2 -2
  17. package/dist/types/current/drm/commonEme/StandardCommonEme.d.ts.map +1 -1
  18. package/dist/types/current/drm/commonEme/StandardCommonEme.js +3 -2
  19. package/dist/types/current/drm/commonEme/WebKitCommonEme.d.ts.map +1 -1
  20. package/dist/types/current/drm/commonEme/WebKitCommonEme.js +2 -2
  21. package/dist/types/current/drm/dom/MSMediaKeys.d.ts +1 -1
  22. package/dist/types/current/drm/dom/MSMediaKeys.d.ts.map +1 -1
  23. package/dist/types/current/drm/dom/WebKitMediaKeys.d.ts +1 -1
  24. package/dist/types/current/drm/dom/WebKitMediaKeys.d.ts.map +1 -1
  25. package/dist/types/current/drm/util/extractContentId.js +2 -2
  26. package/dist/types/current/playback/PlaybackControllerImpl.d.ts +1 -1
  27. package/dist/types/current/streaming/MediaQualityMetadata.d.ts +3 -1
  28. package/dist/types/current/streaming/MediaQualityMetadata.d.ts.map +1 -1
  29. package/dist/types/current/streaming/SegmentControllerImpl.d.ts +1 -1
  30. package/dist/types/current/streaming/buffering/BufferingController.d.ts +1 -1
  31. package/dist/types/current/streaming/buffering/BufferingController.d.ts.map +1 -1
  32. package/dist/types/current/streaming/buffering/BufferingController.js +14 -5
  33. package/dist/types/current/streaming/buffering/MediaSourceController.d.ts.map +1 -1
  34. package/dist/types/current/streaming/buffering/MediaSourceController.js +4 -0
  35. package/dist/types/current/text/discoverDashTextTracks.d.ts +2 -2
  36. package/dist/types/current/text/discoverDashTextTracks.js +4 -3
  37. package/dist/types/current/text/parseWebVtt.d.ts.map +1 -1
  38. package/dist/types/current/text/parseWebVtt.js +3 -2
  39. package/dist/types/current/track/TrackController.d.ts.map +1 -1
  40. package/dist/types/current/track/TrackController.js +7 -0
  41. package/dist/types/current/track/hls/HlsManifestData.d.ts +14 -1
  42. package/dist/types/current/track/hls/HlsManifestData.d.ts.map +1 -1
  43. package/dist/types/current/track/hls/buildHlsMediaTimeline.d.ts.map +1 -1
  44. package/dist/types/current/track/hls/buildHlsMediaTimeline.js +10 -2
  45. package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  46. package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts.map +1 -1
  47. package/dist/types/current/track/hls/createUrlHlsManifestProvider.js +8 -3
  48. package/dist/types/current/tsconfig.tsbuildinfo +1 -1
  49. package/dist/types/ts3.7/playback/PlaybackControllerImpl.d.ts +1 -1
  50. package/dist/types/ts3.7/streaming/MediaQualityMetadata.d.ts +3 -1
  51. package/dist/types/ts3.7/streaming/SegmentControllerImpl.d.ts +1 -1
  52. package/dist/types/ts3.7/streaming/buffering/BufferingController.d.ts +1 -1
  53. package/dist/types/ts3.7/text/discoverDashTextTracks.d.ts +2 -2
  54. package/dist/types/ts3.7/track/hls/HlsManifestData.d.ts +14 -1
  55. package/dist/types/ts3.7/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  56. package/dist/types/ts4.1/playback/PlaybackControllerImpl.d.ts +1 -1
  57. package/dist/types/ts4.1/streaming/MediaQualityMetadata.d.ts +3 -1
  58. package/dist/types/ts4.1/streaming/SegmentControllerImpl.d.ts +1 -1
  59. package/dist/types/ts4.1/streaming/buffering/BufferingController.d.ts +1 -1
  60. package/dist/types/ts4.1/text/discoverDashTextTracks.d.ts +2 -2
  61. package/dist/types/ts4.1/track/hls/HlsManifestData.d.ts +14 -1
  62. package/dist/types/ts4.1/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  63. package/dist/types/ts4.7/playback/PlaybackControllerImpl.d.ts +1 -1
  64. package/dist/types/ts4.7/streaming/MediaQualityMetadata.d.ts +3 -1
  65. package/dist/types/ts4.7/streaming/SegmentControllerImpl.d.ts +1 -1
  66. package/dist/types/ts4.7/streaming/buffering/BufferingController.d.ts +1 -1
  67. package/dist/types/ts4.7/text/discoverDashTextTracks.d.ts +2 -2
  68. package/dist/types/ts4.7/track/hls/HlsManifestData.d.ts +14 -1
  69. package/dist/types/ts4.7/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  70. package/dist/unit/types/current/ad/AdControllerImpl.test.js +66 -0
  71. package/dist/unit/types/current/ad/discoverHlsInterstitials.test.js +29 -2
  72. package/dist/unit/types/current/drm/commonEme/StandardCommonEme.test.js +12 -4
  73. package/dist/unit/types/current/drm/util/extractContentId.test.js +7 -0
  74. package/dist/unit/types/current/index.d.ts +1 -0
  75. package/dist/unit/types/current/index.d.ts.map +1 -1
  76. package/dist/unit/types/current/index.js +1 -0
  77. package/dist/unit/types/current/streaming/MediaQualityMetadata.test.js +8 -0
  78. package/dist/unit/types/current/streaming/buffering/BufferingController.test.js +26 -0
  79. package/dist/unit/types/current/streaming/buffering/MediaSourceController.test.js +29 -0
  80. package/dist/unit/types/current/text/TextTrack.test.d.ts +2 -0
  81. package/dist/unit/types/current/text/TextTrack.test.d.ts.map +1 -0
  82. package/dist/unit/types/current/text/TextTrack.test.js +12 -0
  83. package/dist/unit/types/current/text/discoverDashTextTracks.test.js +17 -0
  84. package/dist/unit/types/current/text/parseWebVtt.test.js +26 -0
  85. package/dist/unit/types/current/track/TrackController.test.js +15 -60
  86. package/dist/unit/types/current/track/hls/buildHlsMediaTimeline.test.js +22 -0
  87. package/dist/unit/types/current/track/hls/createUrlHlsManifestProvider.test.js +23 -0
  88. package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
  89. package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
  90. package/docs/CUSTOMIZATION_GUIDE.md +310 -0
  91. package/package.json +10 -10
  92. package/docs/DASH_MANIFEST_TRANSFORM_GUIDE.md +0 -236
@@ -42,6 +42,19 @@ describe('discoverDashTextTracks', () => {
42
42
  expect(result.length).toBe(1);
43
43
  expect(result[0].language).toBe('es');
44
44
  });
45
+ it('discovers a text AdaptationSet by its Representation mimeType', () => {
46
+ // DASH allows @mimeType on the Representation instead of the set.
47
+ const m = manifest(`
48
+ <AdaptationSet lang="fr">
49
+ <Representation id="fr-sub" mimeType="text/vtt" bandwidth="100">
50
+ <BaseURL>fr.vtt</BaseURL>
51
+ </Representation>
52
+ </AdaptationSet>`);
53
+ const result = discoverDashTextTracks(m, baseUrl);
54
+ expect(result.length).toBe(1);
55
+ expect(result[0].language).toBe('fr');
56
+ expect(result[0].uri).toBe('https://example.com/dash/fr.vtt');
57
+ });
45
58
  it('classifies kind=captions when role=caption is set', () => {
46
59
  const m = manifest(`
47
60
  <AdaptationSet contentType="text" lang="en" mimeType="text/vtt">
@@ -96,6 +109,10 @@ describe('discoverDashTextTracks', () => {
96
109
  </AdaptationSet>`);
97
110
  expect(discoverDashTextTracks(m, baseUrl)).toEqual([]);
98
111
  });
112
+ it('skips a non-text AdaptationSet with no Representation', () => {
113
+ const m = manifest(`<AdaptationSet contentType="audio"/>`);
114
+ expect(discoverDashTextTracks(m, baseUrl)).toEqual([]);
115
+ });
99
116
  it('skips text AdaptationSets carrying segmented codecs (stpp/wvtt)', () => {
100
117
  const m = manifest(`
101
118
  <AdaptationSet contentType="text" mimeType="application/mp4" codecs="stpp">
@@ -382,6 +382,32 @@ hi`;
382
382
  expect(cues.length).toBe(1);
383
383
  expect(cues[0].text).toBe('hi');
384
384
  });
385
+ it('parses a cue that follows the signature with no blank line', () => {
386
+ // The header block ends at a timing line as well as at a blank line
387
+ // (WebVTT file parsing breaks out of the header loop on `-->`), so a
388
+ // document omitting the blank line still yields its cues.
389
+ const text = `WEBVTT
390
+ 00:00:01.000 --> 00:00:02.000
391
+ hi
392
+
393
+ 00:00:03.000 --> 00:00:04.000
394
+ there`;
395
+ const cues = parseWebVtt(text).cues;
396
+ expect(cues.length).toBe(2);
397
+ expect(cues[0].startTime).toBe(1);
398
+ expect(cues[0].text).toBe('hi');
399
+ expect(cues[1].text).toBe('there');
400
+ });
401
+ it('keeps header lines as header when a cue follows with no blank line', () => {
402
+ const text = `WEBVTT
403
+ Kind: captions
404
+ 00:00:01.000 --> 00:00:02.000
405
+ hi`;
406
+ const cues = parseWebVtt(text).cues;
407
+ expect(cues.length).toBe(1);
408
+ expect(cues[0].id).toBeNull();
409
+ expect(cues[0].text).toBe('hi');
410
+ });
385
411
  it('handles trailing identifier without timing line', () => {
386
412
  // Identifier with no following timing line - cue is dropped, parser
387
413
  // breaks out cleanly without throwing.
@@ -597,49 +597,6 @@ describe('TrackControllerImpl', () => {
597
597
  expect(track.clearPrefetch).toHaveBeenCalledTimes(1);
598
598
  }
599
599
  });
600
- it('also clears prefetch on preloaded ad tracks', async () => {
601
- pending('REVIEW: the refactor no longer proactively preloads ad tracks ' +
602
- 'discovered on the current track (ad tracks are created on ' +
603
- 'adEntered only). Decide: intended removal (delete this spec) ' +
604
- 'or regression (restore discovery-time ad preloading).');
605
- const [main] = createLoadOptionsList(1);
606
- trackController.load(main);
607
- const track = trackController.activeTrack;
608
- const ads = {
609
- trackUri: main.uri,
610
- adBreaks: [
611
- {
612
- id: 'b1',
613
- startTime: 5,
614
- duration: 10,
615
- placement: 'midroll',
616
- restrict: {},
617
- once: false,
618
- resumeOffset: null,
619
- playoutLimit: null,
620
- resolutionTimeOffset: null,
621
- skipControl: () => null,
622
- ads: () => Promise.resolve([
623
- {
624
- id: 'a1',
625
- startTime: 5,
626
- duration: 10,
627
- uri: 'https://ads/a.m3u8',
628
- },
629
- ]),
630
- },
631
- ],
632
- };
633
- track.ads = ads;
634
- track.dispatch('adsChange', { previous: null, current: ads });
635
- // Ad resolution runs on microtasks.
636
- await Promise.resolve();
637
- await Promise.resolve();
638
- await Promise.resolve();
639
- const adTrack = trackController.getCachedTrack('https://ads/a.m3u8');
640
- trackController.clearPrefetch();
641
- expect(adTrack.clearPrefetch).toHaveBeenCalled();
642
- });
643
600
  });
644
601
  describe('clearQueue', () => {
645
602
  it('clears the queue without unloading the current track', async () => {
@@ -1124,6 +1081,21 @@ describe('TrackControllerImpl', () => {
1124
1081
  expect(adTrack).withContext('midroll ad track warmed').toBeDefined();
1125
1082
  expect(adTrack.preload).toHaveBeenCalled();
1126
1083
  });
1084
+ it('clears prefetch on a preloaded ad track', async () => {
1085
+ const [main] = createLoadOptionsList(1);
1086
+ factoryGivingBreak(main.uri, []); // records created tracks; no preroll
1087
+ trackController.load(main);
1088
+ await flushAds();
1089
+ deps.adController.dispatch('adPreload', {
1090
+ adBreak: midrollBreak('https://ads/mid.m3u8'),
1091
+ });
1092
+ await flushAds();
1093
+ const adTrack = created.get('https://ads/mid.m3u8');
1094
+ // Ad tracks are not in the content cache, so this only holds if
1095
+ // clearPrefetch reaches the per-parent ad track store too.
1096
+ trackController.clearPrefetch();
1097
+ expect(adTrack.clearPrefetch).toHaveBeenCalledTimes(1);
1098
+ });
1127
1099
  it('ignores adPreload when there is no parent track', () => {
1128
1100
  expect(() => deps.adController.dispatch('adPreload', {
1129
1101
  adBreak: midrollBreak('https://ads/mid.m3u8'),
@@ -1307,23 +1279,6 @@ describe('TrackControllerImpl', () => {
1307
1279
  const track = trackController.activeTrack;
1308
1280
  expect(track.activate).toHaveBeenCalledWith(objectContaining({ startTime: 3 }));
1309
1281
  });
1310
- it('skips the deferred resume if interrupted before the frame', async () => {
1311
- pending('REVIEW: content resume on adBreakCompleted is now synchronous ' +
1312
- '(no deferred/interruptible frame), so this interruption ' +
1313
- 'guard no longer applies. Decide: intended or restore deferral.');
1314
- const [main] = createLoadOptionsList(1);
1315
- trackController.load(main);
1316
- const changeSpy = createEventSpy(trackController, 'trackActivated');
1317
- deps.adController.dispatch('adBreakCompleted', {
1318
- adBreak: adBreak('midroll'),
1319
- resumePosition: 5,
1320
- });
1321
- // Interrupt: a new ad becomes current before the deferred resume runs.
1322
- deps.adController.currentAd = { ...ad, id: 'other' };
1323
- await clock.tick();
1324
- // The deferred setQueue was skipped; no track change occurred.
1325
- expect(changeSpy).not.toHaveBeenCalled();
1326
- });
1327
1282
  it('advances to the next track after a postroll ad when one exists', async () => {
1328
1283
  const list = createLoadOptionsList(2);
1329
1284
  trackController.load(...list);
@@ -542,6 +542,28 @@ describe('buildHlsMediaTimeline', () => {
542
542
  expect(initCall[1]?.headers).toEqual(jasmine.objectContaining({ Range: 'bytes=0-499' }));
543
543
  fetchSpy.and.callThrough();
544
544
  });
545
+ it('resolves segment and map URIs against the media playlist baseUrl', async () => {
546
+ const variant = createVariant('v1.m3u8', 128000);
547
+ const playlist = {
548
+ ...createMediaPlaylist([10]),
549
+ // The media playlist was redirected away from the main playlist's
550
+ // directory.
551
+ baseUrl: 'https://redirect.example.net/hls/v1.m3u8',
552
+ };
553
+ const manifestData = createManifestData([variant], playlist);
554
+ const fetchSpy = spyOn(globalThis, 'fetch').and.callFake(() => Promise.resolve(new Response(new ArrayBuffer(100))));
555
+ const timeline = buildHlsMediaTimeline(deps, manifestData);
556
+ const quality = timeline.periods[0].qualities[0];
557
+ const segment = await quality.getSegment(5);
558
+ await segment.initData();
559
+ await segment.data();
560
+ const urls = fetchSpy.calls.allArgs().map(([input]) => String(input));
561
+ expect(urls).toEqual([
562
+ 'https://redirect.example.net/hls/init.mp4',
563
+ 'https://redirect.example.net/hls/seg0.m4s',
564
+ ]);
565
+ fetchSpy.and.callThrough();
566
+ });
545
567
  it('getDuration throws when no variants', async () => {
546
568
  const manifestData = createManifestData([], createMediaPlaylist([]));
547
569
  const timeline = buildHlsMediaTimeline(deps, manifestData);
@@ -113,6 +113,29 @@ describe('fetchMediaPlaylist', () => {
113
113
  expect(playlist.segments.length).toBe(1);
114
114
  expect(String(mockRequester.request.calls.argsFor(0)[0])).toBe('https://example.com/path/audio.m3u8');
115
115
  });
116
+ it('sets the baseUrl from the response URL', async () => {
117
+ mockRequester.request.and.callFake(() => {
118
+ const resp = new Response(mediaM3u8);
119
+ Object.defineProperty(resp, 'url', {
120
+ value: 'https://redirected.com/hls/audio.m3u8',
121
+ });
122
+ return Promise.resolve(resp);
123
+ });
124
+ const playlist = await fetchMediaPlaylist({
125
+ uri: 'audio.m3u8',
126
+ baseUrl: 'https://example.com/path/main.m3u8',
127
+ defines: undefined,
128
+ });
129
+ expect(playlist.baseUrl).toBe('https://redirected.com/hls/audio.m3u8');
130
+ });
131
+ it('falls back to the requested URL when the response has no URL', async () => {
132
+ const playlist = await fetchMediaPlaylist({
133
+ uri: 'audio.m3u8',
134
+ baseUrl: 'https://example.com/path/main.m3u8',
135
+ defines: undefined,
136
+ });
137
+ expect(playlist.baseUrl).toBe('https://example.com/path/audio.m3u8');
138
+ });
116
139
  it('resolves EXT-X-DEFINE:QUERYPARAM from the playlist URL query string', async () => {
117
140
  mockRequester.request.and.callFake(() => Promise.resolve(new Response([
118
141
  '#EXTM3U',