@amazon/vinyl 1.2.0 → 1.2.2
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/index.cjs +24 -6
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +33 -13
- package/dist/index.js.map +3 -3
- package/dist/test/integ.es5.js +62 -62
- package/dist/test/integ.js +119 -6
- package/dist/test/integ.js.map +2 -2
- package/dist/types/current/ad/AdBreakInfo.d.ts +160 -0
- package/dist/types/current/ad/AdBreakInfo.d.ts.map +1 -0
- package/dist/types/current/ad/AdBreakInfo.js +5 -0
- package/dist/types/current/ad/AdController.d.ts +229 -0
- package/dist/types/current/ad/AdController.d.ts.map +1 -0
- package/dist/types/current/ad/AdController.js +19 -0
- package/dist/types/current/ad/AdControllerImpl.d.ts +162 -0
- package/dist/types/current/ad/AdControllerImpl.d.ts.map +1 -0
- package/dist/types/current/ad/AdControllerImpl.js +741 -0
- package/dist/types/current/ad/AdError.d.ts +6 -0
- package/dist/types/current/ad/AdError.d.ts.map +1 -0
- package/dist/types/current/ad/AdError.js +14 -0
- package/dist/types/current/ad/AdsProvider.d.ts +28 -0
- package/dist/types/current/ad/AdsProvider.d.ts.map +1 -0
- package/dist/types/current/ad/AdsProvider.js +5 -0
- package/dist/types/current/ad/defaultAdLoadOptionsResolver.d.ts +12 -0
- package/dist/types/current/ad/defaultAdLoadOptionsResolver.d.ts.map +1 -0
- package/dist/types/current/ad/defaultAdLoadOptionsResolver.js +26 -0
- package/dist/types/current/ad/discoverHlsInterstitials.d.ts +22 -0
- package/dist/types/current/ad/discoverHlsInterstitials.d.ts.map +1 -0
- package/dist/types/current/ad/discoverHlsInterstitials.js +248 -0
- package/dist/types/current/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/current/drm/DrmControllerImpl.d.ts.map +1 -1
- package/dist/types/current/drm/DrmControllerImpl.js +23 -1
- package/dist/types/current/playback/PlaybackControllerImpl.d.ts +1 -1
- package/dist/types/current/playback/createStallDetector.d.ts +43 -0
- package/dist/types/current/playback/createStallDetector.d.ts.map +1 -0
- package/dist/types/current/playback/createStallDetector.js +101 -0
- package/dist/types/current/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/current/playback/createStallMonitor.d.ts.map +1 -0
- package/dist/types/current/playback/createStallMonitor.js +99 -0
- package/dist/types/current/streaming/AudioOptions.d.ts +66 -0
- package/dist/types/current/streaming/AudioOptions.d.ts.map +1 -0
- package/dist/types/current/streaming/AudioOptions.js +19 -0
- package/dist/types/current/streaming/LoadMetric.d.ts +72 -0
- package/dist/types/current/streaming/LoadMetric.d.ts.map +1 -0
- package/dist/types/current/streaming/LoadMetric.js +5 -0
- package/dist/types/current/streaming/ManifestControllerImpl.d.ts +43 -0
- package/dist/types/current/streaming/ManifestControllerImpl.d.ts.map +1 -0
- package/dist/types/current/streaming/ManifestControllerImpl.js +104 -0
- package/dist/types/current/streaming/buffering/codecDenylist.d.ts +23 -0
- package/dist/types/current/streaming/buffering/codecDenylist.d.ts.map +1 -0
- package/dist/types/current/streaming/buffering/codecDenylist.js +62 -0
- package/dist/types/current/streaming/createAllowedContentTypesValue.d.ts +30 -0
- package/dist/types/current/streaming/createAllowedContentTypesValue.d.ts.map +1 -0
- package/dist/types/current/streaming/createAllowedContentTypesValue.js +43 -0
- package/dist/types/current/streaming/createTimelineContentTypesValue.d.ts +21 -0
- package/dist/types/current/streaming/createTimelineContentTypesValue.d.ts.map +1 -0
- package/dist/types/current/streaming/createTimelineContentTypesValue.js +25 -0
- package/dist/types/current/streaming/mediaTimelineAccessibilityFilter.d.ts +35 -0
- package/dist/types/current/streaming/mediaTimelineAccessibilityFilter.d.ts.map +1 -0
- package/dist/types/current/streaming/mediaTimelineAccessibilityFilter.js +75 -0
- package/dist/types/current/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/current/text/HtmlTextTrackRenderer.d.ts.map +1 -0
- package/dist/types/current/text/HtmlTextTrackRenderer.js +231 -0
- package/dist/types/current/text/SidecarTextTrackController.d.ts +103 -0
- package/dist/types/current/text/SidecarTextTrackController.d.ts.map +1 -0
- package/dist/types/current/text/SidecarTextTrackController.js +250 -0
- package/dist/types/current/text/SidecarTextTrackLoader.d.ts +65 -0
- package/dist/types/current/text/SidecarTextTrackLoader.d.ts.map +1 -0
- package/dist/types/current/text/SidecarTextTrackLoader.js +150 -0
- package/dist/types/current/text/TextTrack.d.ts +179 -0
- package/dist/types/current/text/TextTrack.d.ts.map +1 -0
- package/dist/types/current/text/TextTrack.js +29 -0
- package/dist/types/current/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/current/text/TextTrackRenderer.d.ts.map +1 -0
- package/dist/types/current/text/TextTrackRenderer.js +5 -0
- package/dist/types/current/text/VttCueStyle.d.ts +41 -0
- package/dist/types/current/text/VttCueStyle.d.ts.map +1 -0
- package/dist/types/current/text/VttCueStyle.js +45 -0
- package/dist/types/current/text/discoverDashTextTracks.d.ts +24 -0
- package/dist/types/current/text/discoverDashTextTracks.d.ts.map +1 -0
- package/dist/types/current/text/discoverDashTextTracks.js +110 -0
- package/dist/types/current/text/discoverHlsTextTracks.d.ts +19 -0
- package/dist/types/current/text/discoverHlsTextTracks.d.ts.map +1 -0
- package/dist/types/current/text/discoverHlsTextTracks.js +49 -0
- package/dist/types/current/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/current/text/mediaTextTrackProvider.d.ts.map +1 -0
- package/dist/types/current/text/mediaTextTrackProvider.js +42 -0
- package/dist/types/current/text/parseWebVtt.d.ts +60 -0
- package/dist/types/current/text/parseWebVtt.d.ts.map +1 -0
- package/dist/types/current/text/parseWebVtt.js +249 -0
- package/dist/types/current/text/pickPreferredTextTrack.d.ts +15 -0
- package/dist/types/current/text/pickPreferredTextTrack.d.ts.map +1 -0
- package/dist/types/current/text/pickPreferredTextTrack.js +71 -0
- package/dist/types/current/track/SeekRange.d.ts +9 -0
- package/dist/types/current/track/SeekRange.d.ts.map +1 -0
- package/dist/types/current/track/SeekRange.js +5 -0
- package/dist/types/current/track/hls/HlsManifestData.d.ts +19 -0
- package/dist/types/current/track/hls/HlsManifestData.d.ts.map +1 -0
- package/dist/types/current/track/hls/HlsManifestData.js +5 -0
- package/dist/types/current/track/hls/createHlsManifestProvider.d.ts +6 -0
- package/dist/types/current/track/hls/createHlsManifestProvider.d.ts.map +1 -0
- package/dist/types/current/track/hls/createHlsManifestProvider.js +9 -0
- package/dist/types/current/track/inferLoadOptionsFromUri.d.ts +15 -0
- package/dist/types/current/track/inferLoadOptionsFromUri.d.ts.map +1 -0
- package/dist/types/current/track/inferLoadOptionsFromUri.js +55 -0
- package/dist/types/current/track/mse/MseTrack.d.ts +0 -1
- package/dist/types/current/track/mse/MseTrack.d.ts.map +1 -1
- package/dist/types/current/track/mse/MseTrack.js +4 -5
- package/dist/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/types/current/util/media/codecOverrides.d.ts +33 -0
- package/dist/types/current/util/media/codecOverrides.d.ts.map +1 -0
- package/dist/types/current/util/media/codecOverrides.js +37 -0
- package/dist/types/current/util/media/codecSupport.d.ts +6 -0
- package/dist/types/current/util/media/codecSupport.d.ts.map +1 -0
- package/dist/types/current/util/media/codecSupport.js +17 -0
- package/dist/types/current/util/media/mimeType.d.ts +8 -0
- package/dist/types/current/util/media/mimeType.d.ts.map +1 -0
- package/dist/types/current/util/media/mimeType.js +13 -0
- package/dist/types/ts3.7/ad/AdBreakInfo.d.ts +160 -0
- package/dist/types/ts3.7/ad/AdController.d.ts +243 -0
- package/dist/types/ts3.7/ad/AdControllerImpl.d.ts +162 -0
- package/dist/types/ts3.7/ad/AdError.d.ts +6 -0
- package/dist/types/ts3.7/ad/AdsProvider.d.ts +28 -0
- package/dist/types/ts3.7/ad/defaultAdLoadOptionsResolver.d.ts +12 -0
- package/dist/types/ts3.7/ad/discoverHlsInterstitials.d.ts +22 -0
- package/dist/types/ts3.7/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/ts3.7/playback/PlaybackControllerImpl.d.ts +1 -1
- package/dist/types/ts3.7/playback/createStallDetector.d.ts +43 -0
- package/dist/types/ts3.7/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/ts3.7/streaming/AudioOptions.d.ts +66 -0
- package/dist/types/ts3.7/streaming/LoadMetric.d.ts +72 -0
- package/dist/types/ts3.7/streaming/ManifestControllerImpl.d.ts +40 -0
- package/dist/types/ts3.7/streaming/buffering/codecDenylist.d.ts +23 -0
- package/dist/types/ts3.7/streaming/createAllowedContentTypesValue.d.ts +30 -0
- package/dist/types/ts3.7/streaming/createTimelineContentTypesValue.d.ts +21 -0
- package/dist/types/ts3.7/streaming/mediaTimelineAccessibilityFilter.d.ts +35 -0
- package/dist/types/ts3.7/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/ts3.7/text/SidecarTextTrackController.d.ts +103 -0
- package/dist/types/ts3.7/text/SidecarTextTrackLoader.d.ts +65 -0
- package/dist/types/ts3.7/text/TextTrack.d.ts +183 -0
- package/dist/types/ts3.7/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/ts3.7/text/VttCueStyle.d.ts +41 -0
- package/dist/types/ts3.7/text/discoverDashTextTracks.d.ts +24 -0
- package/dist/types/ts3.7/text/discoverHlsTextTracks.d.ts +19 -0
- package/dist/types/ts3.7/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/ts3.7/text/parseWebVtt.d.ts +60 -0
- package/dist/types/ts3.7/text/pickPreferredTextTrack.d.ts +15 -0
- package/dist/types/ts3.7/track/SeekRange.d.ts +9 -0
- package/dist/types/ts3.7/track/hls/HlsManifestData.d.ts +19 -0
- package/dist/types/ts3.7/track/hls/createHlsManifestProvider.d.ts +6 -0
- package/dist/types/ts3.7/track/inferLoadOptionsFromUri.d.ts +15 -0
- package/dist/types/ts3.7/track/mse/MseTrack.d.ts +0 -1
- package/dist/types/ts3.7/util/media/codecOverrides.d.ts +33 -0
- package/dist/types/ts3.7/util/media/codecSupport.d.ts +6 -0
- package/dist/types/ts3.7/util/media/mimeType.d.ts +8 -0
- package/dist/types/ts4.1/ad/AdBreakInfo.d.ts +160 -0
- package/dist/types/ts4.1/ad/AdController.d.ts +243 -0
- package/dist/types/ts4.1/ad/AdControllerImpl.d.ts +162 -0
- package/dist/types/ts4.1/ad/AdError.d.ts +6 -0
- package/dist/types/ts4.1/ad/AdsProvider.d.ts +28 -0
- package/dist/types/ts4.1/ad/defaultAdLoadOptionsResolver.d.ts +12 -0
- package/dist/types/ts4.1/ad/discoverHlsInterstitials.d.ts +22 -0
- package/dist/types/ts4.1/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/ts4.1/playback/PlaybackControllerImpl.d.ts +1 -1
- package/dist/types/ts4.1/playback/createStallDetector.d.ts +43 -0
- package/dist/types/ts4.1/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/ts4.1/streaming/AudioOptions.d.ts +66 -0
- package/dist/types/ts4.1/streaming/LoadMetric.d.ts +72 -0
- package/dist/types/ts4.1/streaming/ManifestControllerImpl.d.ts +40 -0
- package/dist/types/ts4.1/streaming/buffering/codecDenylist.d.ts +23 -0
- package/dist/types/ts4.1/streaming/createAllowedContentTypesValue.d.ts +30 -0
- package/dist/types/ts4.1/streaming/createTimelineContentTypesValue.d.ts +21 -0
- package/dist/types/ts4.1/streaming/mediaTimelineAccessibilityFilter.d.ts +35 -0
- package/dist/types/ts4.1/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/ts4.1/text/SidecarTextTrackController.d.ts +104 -0
- package/dist/types/ts4.1/text/SidecarTextTrackLoader.d.ts +65 -0
- package/dist/types/ts4.1/text/TextTrack.d.ts +183 -0
- package/dist/types/ts4.1/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/ts4.1/text/VttCueStyle.d.ts +41 -0
- package/dist/types/ts4.1/text/discoverDashTextTracks.d.ts +24 -0
- package/dist/types/ts4.1/text/discoverHlsTextTracks.d.ts +19 -0
- package/dist/types/ts4.1/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/ts4.1/text/parseWebVtt.d.ts +60 -0
- package/dist/types/ts4.1/text/pickPreferredTextTrack.d.ts +15 -0
- package/dist/types/ts4.1/track/SeekRange.d.ts +9 -0
- package/dist/types/ts4.1/track/hls/HlsManifestData.d.ts +19 -0
- package/dist/types/ts4.1/track/hls/createHlsManifestProvider.d.ts +6 -0
- package/dist/types/ts4.1/track/inferLoadOptionsFromUri.d.ts +15 -0
- package/dist/types/ts4.1/track/mse/MseTrack.d.ts +0 -1
- package/dist/types/ts4.1/util/media/codecOverrides.d.ts +33 -0
- package/dist/types/ts4.1/util/media/codecSupport.d.ts +6 -0
- package/dist/types/ts4.1/util/media/mimeType.d.ts +8 -0
- package/dist/types/ts4.7/ad/AdBreakInfo.d.ts +160 -0
- package/dist/types/ts4.7/ad/AdController.d.ts +243 -0
- package/dist/types/ts4.7/ad/AdControllerImpl.d.ts +162 -0
- package/dist/types/ts4.7/ad/AdError.d.ts +6 -0
- package/dist/types/ts4.7/ad/AdsProvider.d.ts +28 -0
- package/dist/types/ts4.7/ad/defaultAdLoadOptionsResolver.d.ts +12 -0
- package/dist/types/ts4.7/ad/discoverHlsInterstitials.d.ts +22 -0
- package/dist/types/ts4.7/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/ts4.7/playback/PlaybackControllerImpl.d.ts +1 -1
- package/dist/types/ts4.7/playback/createStallDetector.d.ts +43 -0
- package/dist/types/ts4.7/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/ts4.7/streaming/AudioOptions.d.ts +66 -0
- package/dist/types/ts4.7/streaming/LoadMetric.d.ts +72 -0
- package/dist/types/ts4.7/streaming/ManifestControllerImpl.d.ts +43 -0
- package/dist/types/ts4.7/streaming/buffering/codecDenylist.d.ts +23 -0
- package/dist/types/ts4.7/streaming/createAllowedContentTypesValue.d.ts +30 -0
- package/dist/types/ts4.7/streaming/createTimelineContentTypesValue.d.ts +21 -0
- package/dist/types/ts4.7/streaming/mediaTimelineAccessibilityFilter.d.ts +35 -0
- package/dist/types/ts4.7/text/HtmlTextTrackRenderer.d.ts +60 -0
- package/dist/types/ts4.7/text/SidecarTextTrackController.d.ts +103 -0
- package/dist/types/ts4.7/text/SidecarTextTrackLoader.d.ts +65 -0
- package/dist/types/ts4.7/text/TextTrack.d.ts +183 -0
- package/dist/types/ts4.7/text/TextTrackRenderer.d.ts +24 -0
- package/dist/types/ts4.7/text/VttCueStyle.d.ts +41 -0
- package/dist/types/ts4.7/text/discoverDashTextTracks.d.ts +24 -0
- package/dist/types/ts4.7/text/discoverHlsTextTracks.d.ts +19 -0
- package/dist/types/ts4.7/text/mediaTextTrackProvider.d.ts +34 -0
- package/dist/types/ts4.7/text/parseWebVtt.d.ts +60 -0
- package/dist/types/ts4.7/text/pickPreferredTextTrack.d.ts +15 -0
- package/dist/types/ts4.7/track/SeekRange.d.ts +9 -0
- package/dist/types/ts4.7/track/hls/HlsManifestData.d.ts +19 -0
- package/dist/types/ts4.7/track/hls/createHlsManifestProvider.d.ts +6 -0
- package/dist/types/ts4.7/track/inferLoadOptionsFromUri.d.ts +15 -0
- package/dist/types/ts4.7/track/mse/MseTrack.d.ts +0 -1
- package/dist/types/ts4.7/util/media/codecOverrides.d.ts +33 -0
- package/dist/types/ts4.7/util/media/codecSupport.d.ts +6 -0
- package/dist/types/ts4.7/util/media/mimeType.d.ts +8 -0
- package/dist/unit/types/current/ad/AdController.test.d.ts +2 -0
- package/dist/unit/types/current/ad/AdController.test.d.ts.map +1 -0
- package/dist/unit/types/current/ad/AdController.test.js +10 -0
- package/dist/unit/types/current/ad/AdControllerImpl.test.d.ts +2 -0
- package/dist/unit/types/current/ad/AdControllerImpl.test.d.ts.map +1 -0
- package/dist/unit/types/current/ad/AdControllerImpl.test.js +1654 -0
- package/dist/unit/types/current/ad/defaultAdLoadOptionsResolver.test.d.ts +2 -0
- package/dist/unit/types/current/ad/defaultAdLoadOptionsResolver.test.d.ts.map +1 -0
- package/dist/unit/types/current/ad/defaultAdLoadOptionsResolver.test.js +56 -0
- package/dist/unit/types/current/ad/discoverHlsInterstitials.test.d.ts +2 -0
- package/dist/unit/types/current/ad/discoverHlsInterstitials.test.d.ts.map +1 -0
- package/dist/unit/types/current/ad/discoverHlsInterstitials.test.js +757 -0
- package/dist/unit/types/current/drm/DrmControllerImpl.test.js +96 -2
- package/dist/unit/types/current/playback/createStallDetector.test.d.ts +2 -0
- package/dist/unit/types/current/playback/createStallDetector.test.d.ts.map +1 -0
- package/dist/unit/types/current/playback/createStallDetector.test.js +163 -0
- package/dist/unit/types/current/playback/createStallMonitor.test.d.ts +2 -0
- package/dist/unit/types/current/playback/createStallMonitor.test.d.ts.map +1 -0
- package/dist/unit/types/current/playback/createStallMonitor.test.js +163 -0
- package/dist/unit/types/current/streaming/ManifestControllerImpl.test.d.ts +2 -0
- package/dist/unit/types/current/streaming/ManifestControllerImpl.test.d.ts.map +1 -0
- package/dist/unit/types/current/streaming/ManifestControllerImpl.test.js +197 -0
- package/dist/unit/types/current/streaming/MseTrack.test.js +15 -0
- package/dist/unit/types/current/streaming/buffering/codecDenylist.test.d.ts +2 -0
- package/dist/unit/types/current/streaming/buffering/codecDenylist.test.d.ts.map +1 -0
- package/dist/unit/types/current/streaming/buffering/codecDenylist.test.js +56 -0
- package/dist/unit/types/current/streaming/createAllowedContentTypesValue.test.d.ts +2 -0
- package/dist/unit/types/current/streaming/createAllowedContentTypesValue.test.d.ts.map +1 -0
- package/dist/unit/types/current/streaming/createAllowedContentTypesValue.test.js +77 -0
- package/dist/unit/types/current/streaming/createTimelineContentTypesValue.test.d.ts +2 -0
- package/dist/unit/types/current/streaming/createTimelineContentTypesValue.test.d.ts.map +1 -0
- package/dist/unit/types/current/streaming/createTimelineContentTypesValue.test.js +57 -0
- package/dist/unit/types/current/streaming/mediaTimelineAccessibilityFilter.test.d.ts +2 -0
- package/dist/unit/types/current/streaming/mediaTimelineAccessibilityFilter.test.d.ts.map +1 -0
- package/dist/unit/types/current/streaming/mediaTimelineAccessibilityFilter.test.js +99 -0
- package/dist/unit/types/current/text/HtmlTextTrackRenderer.test.d.ts +2 -0
- package/dist/unit/types/current/text/HtmlTextTrackRenderer.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/HtmlTextTrackRenderer.test.js +231 -0
- package/dist/unit/types/current/text/SidecarTextTrackController.test.d.ts +2 -0
- package/dist/unit/types/current/text/SidecarTextTrackController.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/SidecarTextTrackController.test.js +686 -0
- package/dist/unit/types/current/text/SidecarTextTrackLoader.test.d.ts +2 -0
- package/dist/unit/types/current/text/SidecarTextTrackLoader.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/SidecarTextTrackLoader.test.js +365 -0
- package/dist/unit/types/current/text/TextTrack.test.d.ts +2 -0
- package/dist/unit/types/current/text/TextTrack.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/TextTrack.test.js +12 -0
- package/dist/unit/types/current/text/VttCueStyle.test.d.ts +2 -0
- package/dist/unit/types/current/text/VttCueStyle.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/VttCueStyle.test.js +76 -0
- package/dist/unit/types/current/text/discoverDashTextTracks.test.d.ts +2 -0
- package/dist/unit/types/current/text/discoverDashTextTracks.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/discoverDashTextTracks.test.js +216 -0
- package/dist/unit/types/current/text/discoverHlsTextTracks.test.d.ts +2 -0
- package/dist/unit/types/current/text/discoverHlsTextTracks.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/discoverHlsTextTracks.test.js +177 -0
- package/dist/unit/types/current/text/mediaTextTrackProvider.test.d.ts +2 -0
- package/dist/unit/types/current/text/mediaTextTrackProvider.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/mediaTextTrackProvider.test.js +86 -0
- package/dist/unit/types/current/text/parseWebVtt.test.d.ts +2 -0
- package/dist/unit/types/current/text/parseWebVtt.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/parseWebVtt.test.js +419 -0
- package/dist/unit/types/current/text/pickPreferredTextTrack.test.d.ts +2 -0
- package/dist/unit/types/current/text/pickPreferredTextTrack.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/pickPreferredTextTrack.test.js +78 -0
- package/dist/unit/types/current/track/inferLoadOptionsFromUri.test.d.ts +2 -0
- package/dist/unit/types/current/track/inferLoadOptionsFromUri.test.d.ts.map +1 -0
- package/dist/unit/types/current/track/inferLoadOptionsFromUri.test.js +165 -0
- package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/unit/types/current/util/media/codecOverrides.test.d.ts +2 -0
- package/dist/unit/types/current/util/media/codecOverrides.test.d.ts.map +1 -0
- package/dist/unit/types/current/util/media/codecOverrides.test.js +42 -0
- package/dist/unit/types/current/util/media/codecSupport.test.d.ts +2 -0
- package/dist/unit/types/current/util/media/codecSupport.test.d.ts.map +1 -0
- package/dist/unit/types/current/util/media/codecSupport.test.js +26 -0
- package/dist/unit/types/current/util/media/mimeType.test.d.ts +2 -0
- package/dist/unit/types/current/util/media/mimeType.test.d.ts.map +1 -0
- package/dist/unit/types/current/util/media/mimeType.test.js +22 -0
- package/dist/unit/types/current/vinyl/VinylPlayerAdBreaks.test.d.ts +2 -0
- package/dist/unit/types/current/vinyl/VinylPlayerAdBreaks.test.d.ts.map +1 -0
- package/dist/unit/types/current/vinyl/VinylPlayerAdBreaks.test.js +142 -0
- package/dist/unit/types/current/vinyl/VinylPlayerTextTracks.test.d.ts +2 -0
- package/dist/unit/types/current/vinyl/VinylPlayerTextTracks.test.d.ts.map +1 -0
- package/dist/unit/types/current/vinyl/VinylPlayerTextTracks.test.js +188 -0
- package/dist/vinylTestUtil/types/current/mock/ad/MockAdController.d.ts +15 -0
- package/dist/vinylTestUtil/types/current/mock/ad/MockAdController.d.ts.map +1 -0
- package/dist/vinylTestUtil/types/current/mock/ad/MockAdController.js +26 -0
- package/dist/vinylTestUtil/types/current/mock/dash/mockDashManifest.d.ts +84 -84
- package/dist/vinylTestUtil/types/current/mock/text/MockTextTrackController.d.ts +10 -0
- package/dist/vinylTestUtil/types/current/mock/text/MockTextTrackController.d.ts.map +1 -0
- package/dist/vinylTestUtil/types/current/mock/text/MockTextTrackController.js +14 -0
- package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/vinylTestUtil/types/ts3.7/mock/ad/MockAdController.d.ts +15 -0
- package/dist/vinylTestUtil/types/ts3.7/mock/dash/mockDashManifest.d.ts +84 -84
- package/dist/vinylTestUtil/types/ts3.7/mock/text/MockTextTrackController.d.ts +10 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/ad/MockAdController.d.ts +15 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/dash/mockDashManifest.d.ts +84 -84
- package/dist/vinylTestUtil/types/ts4.1/mock/text/MockTextTrackController.d.ts +10 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/ad/MockAdController.d.ts +15 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/dash/mockDashManifest.d.ts +84 -84
- package/dist/vinylTestUtil/types/ts4.7/mock/text/MockTextTrackController.d.ts +10 -0
- package/package.json +10 -11
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { resolveSelectedTrack, SidecarTextTrackController, } from '@amazon/vinyl';
|
|
6
|
+
import { requesterWithRetryRef } from '@amazon/vinyl-util';
|
|
7
|
+
import { flushPromises } from '@amazon/vinyl-util/browserTestUtil';
|
|
8
|
+
import { MockRequester } from '@amazon/vinyl-util/testUtil';
|
|
9
|
+
import { data } from '@amazon/vinyl-observable';
|
|
10
|
+
/**
|
|
11
|
+
* Flushes the microtask/macrotask queue repeatedly until `predicate` holds or
|
|
12
|
+
* the attempt budget is exhausted. A single {@link flushPromises} is one
|
|
13
|
+
* macrotask, which is enough in Node but not always in a real browser: loading
|
|
14
|
+
* a sidecar VTT chains `fetch -> Response.text() -> parse -> onCues`, and the
|
|
15
|
+
* streamed body read can span several macrotasks. Polling avoids a flaky
|
|
16
|
+
* fixed-tick wait while still failing fast when the condition never becomes
|
|
17
|
+
* true.
|
|
18
|
+
*/
|
|
19
|
+
async function flushUntil(predicate) {
|
|
20
|
+
for (let i = 0; i < 100 && !predicate(); i++)
|
|
21
|
+
await flushPromises();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A memoized fake provider mirroring {@link MediaTextTrackProvider}: one track
|
|
25
|
+
* per `(kind,label,language)` key, so the number of distinct tracks reveals
|
|
26
|
+
* whether the controller accumulates tracks or reuses them.
|
|
27
|
+
*/
|
|
28
|
+
class FakeTextTrackProvider {
|
|
29
|
+
// Every distinct track created (one per rendition key).
|
|
30
|
+
created = [];
|
|
31
|
+
// The track handed out by the most recent getOrCreate (the active one).
|
|
32
|
+
lastTrack = null;
|
|
33
|
+
cache = new Map();
|
|
34
|
+
getOrCreate = jasmine
|
|
35
|
+
.createSpy('getOrCreate')
|
|
36
|
+
.and.callFake((kind, label, language) => {
|
|
37
|
+
const key = `${kind}-${label}-${language}`;
|
|
38
|
+
const existing = this.cache.get(key);
|
|
39
|
+
if (existing) {
|
|
40
|
+
this.lastTrack = existing.track;
|
|
41
|
+
return existing;
|
|
42
|
+
}
|
|
43
|
+
const storedCues = [];
|
|
44
|
+
const track = {
|
|
45
|
+
kind,
|
|
46
|
+
label: label ?? '',
|
|
47
|
+
language: language ?? '',
|
|
48
|
+
mode: 'disabled',
|
|
49
|
+
storedCues,
|
|
50
|
+
// Real TextTrack.cues returns null when mode is 'disabled'.
|
|
51
|
+
get cues() {
|
|
52
|
+
return this.mode === 'disabled' ? null : storedCues;
|
|
53
|
+
},
|
|
54
|
+
addCue(cue) {
|
|
55
|
+
storedCues.push(cue);
|
|
56
|
+
},
|
|
57
|
+
removeCue(cue) {
|
|
58
|
+
const i = storedCues.indexOf(cue);
|
|
59
|
+
if (i >= 0)
|
|
60
|
+
storedCues.splice(i, 1);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const ref = {
|
|
64
|
+
track: track,
|
|
65
|
+
clear() {
|
|
66
|
+
// Mirrors the real ref: cues only clear while not disabled.
|
|
67
|
+
if (track.mode === 'disabled')
|
|
68
|
+
return;
|
|
69
|
+
storedCues.length = 0;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
this.created.push(track);
|
|
73
|
+
this.cache.set(key, ref);
|
|
74
|
+
this.lastTrack = track;
|
|
75
|
+
return ref;
|
|
76
|
+
});
|
|
77
|
+
/** Tracks currently rendering captions (showing with at least one cue). */
|
|
78
|
+
showingWithCues() {
|
|
79
|
+
return this.created.filter((t) => t.mode === 'showing' && (t.cues?.length ?? 0) > 0);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function makeTrack(overrides = {}) {
|
|
83
|
+
return {
|
|
84
|
+
id: 'a',
|
|
85
|
+
kind: 'subtitles',
|
|
86
|
+
language: 'en',
|
|
87
|
+
label: 'English',
|
|
88
|
+
default: false,
|
|
89
|
+
forced: false,
|
|
90
|
+
characteristics: [],
|
|
91
|
+
uri: 'https://x.test/a.vtt',
|
|
92
|
+
mimeType: 'text/vtt',
|
|
93
|
+
...overrides,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
describe('SidecarTextTrackController', () => {
|
|
97
|
+
let requester;
|
|
98
|
+
let originalCue;
|
|
99
|
+
const disposers = [];
|
|
100
|
+
function build(opts) {
|
|
101
|
+
const provider = new FakeTextTrackProvider();
|
|
102
|
+
const textTracks$ = data(Promise.resolve([]));
|
|
103
|
+
const options$ = data(opts?.options ?? { selection: {} });
|
|
104
|
+
const playback = { currentTime: 0 };
|
|
105
|
+
const controller = new SidecarTextTrackController({
|
|
106
|
+
textTrackProvider: provider,
|
|
107
|
+
textTracks: textTracks$,
|
|
108
|
+
options: options$,
|
|
109
|
+
playbackController: playback,
|
|
110
|
+
requestInit: opts?.requestInit,
|
|
111
|
+
textTrackRenderer: opts?.textTrackRenderer,
|
|
112
|
+
});
|
|
113
|
+
// The owning media track activates the controller once current; do the
|
|
114
|
+
// same so cues render (unless a test wants to control activation).
|
|
115
|
+
if (opts?.activate !== false)
|
|
116
|
+
controller.activate();
|
|
117
|
+
disposers.push(() => controller.dispose());
|
|
118
|
+
return {
|
|
119
|
+
controller,
|
|
120
|
+
provider,
|
|
121
|
+
options$,
|
|
122
|
+
textTracks$,
|
|
123
|
+
playback,
|
|
124
|
+
async setTracks(tracks) {
|
|
125
|
+
textTracks$.value = Promise.resolve(tracks);
|
|
126
|
+
await flushPromises();
|
|
127
|
+
},
|
|
128
|
+
select(selection) {
|
|
129
|
+
options$.value = { ...options$.value, selection };
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
beforeEach(() => {
|
|
134
|
+
requester = new MockRequester();
|
|
135
|
+
requesterWithRetryRef.set(() => requester);
|
|
136
|
+
originalCue = globalThis.VTTCue;
|
|
137
|
+
globalThis.VTTCue = function VTTCue(startTime, endTime, text) {
|
|
138
|
+
this.startTime = startTime;
|
|
139
|
+
this.endTime = endTime;
|
|
140
|
+
this.text = text;
|
|
141
|
+
this.id = '';
|
|
142
|
+
this.size = 100;
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
afterEach(() => {
|
|
146
|
+
for (const dispose of disposers.splice(0))
|
|
147
|
+
dispose();
|
|
148
|
+
globalThis.VTTCue = originalCue;
|
|
149
|
+
});
|
|
150
|
+
function respondVtt(body) {
|
|
151
|
+
requester.request.and.resolveTo(new Response(body));
|
|
152
|
+
}
|
|
153
|
+
it('starts with no tracks and no active selection', () => {
|
|
154
|
+
const { controller } = build();
|
|
155
|
+
expect(controller.textTracks).toEqual([]);
|
|
156
|
+
expect(controller.activeTextTrack).toBeNull();
|
|
157
|
+
});
|
|
158
|
+
describe('resolveSelectedTrack', () => {
|
|
159
|
+
const full = makeTrack({ id: 'full', language: 'en', forced: false });
|
|
160
|
+
const forced = makeTrack({ id: 'forced', language: 'en', forced: true });
|
|
161
|
+
const captions = makeTrack({
|
|
162
|
+
id: 'cc',
|
|
163
|
+
language: 'en',
|
|
164
|
+
forced: false,
|
|
165
|
+
kind: 'captions',
|
|
166
|
+
});
|
|
167
|
+
it("renders nothing when enabled is 'off' (even with an id)", () => {
|
|
168
|
+
expect(resolveSelectedTrack([full, forced], {
|
|
169
|
+
enabled: 'off',
|
|
170
|
+
selection: { id: 'full' },
|
|
171
|
+
})).toBeNull();
|
|
172
|
+
});
|
|
173
|
+
it("enabled 'on' selects the full track", () => {
|
|
174
|
+
expect(resolveSelectedTrack([full, forced], {
|
|
175
|
+
enabled: 'on',
|
|
176
|
+
selection: { language: 'en' },
|
|
177
|
+
})?.id).toBe('full');
|
|
178
|
+
});
|
|
179
|
+
it("enabled 'forced' selects the forced track", () => {
|
|
180
|
+
expect(resolveSelectedTrack([full, forced], {
|
|
181
|
+
enabled: 'forced',
|
|
182
|
+
selection: { language: 'en' },
|
|
183
|
+
})?.id).toBe('forced');
|
|
184
|
+
});
|
|
185
|
+
it('selection.forced overrides the enabled mode', () => {
|
|
186
|
+
expect(resolveSelectedTrack([full, forced], {
|
|
187
|
+
enabled: 'on',
|
|
188
|
+
selection: { language: 'en', forced: true },
|
|
189
|
+
})?.id).toBe('forced');
|
|
190
|
+
});
|
|
191
|
+
it('an explicit id wins over other criteria', () => {
|
|
192
|
+
expect(resolveSelectedTrack([full, forced], {
|
|
193
|
+
selection: { id: 'forced', language: 'en', forced: false },
|
|
194
|
+
})?.id).toBe('forced');
|
|
195
|
+
});
|
|
196
|
+
it('unknown id selects nothing', () => {
|
|
197
|
+
expect(resolveSelectedTrack([full], { selection: { id: 'nope' } })).toBeNull();
|
|
198
|
+
});
|
|
199
|
+
it('forced defaults to true (only forced tracks)', () => {
|
|
200
|
+
expect(resolveSelectedTrack([full, forced], {
|
|
201
|
+
selection: { language: 'en' },
|
|
202
|
+
})?.id).toBe('forced');
|
|
203
|
+
});
|
|
204
|
+
it('forced:false selects the full track', () => {
|
|
205
|
+
expect(resolveSelectedTrack([full, forced], {
|
|
206
|
+
selection: { language: 'en', forced: false },
|
|
207
|
+
})?.id).toBe('full');
|
|
208
|
+
});
|
|
209
|
+
it('kind restricts the candidate pool', () => {
|
|
210
|
+
expect(resolveSelectedTrack([full, captions], {
|
|
211
|
+
selection: {
|
|
212
|
+
language: 'en',
|
|
213
|
+
forced: false,
|
|
214
|
+
kind: 'captions',
|
|
215
|
+
},
|
|
216
|
+
})?.id).toBe('cc');
|
|
217
|
+
});
|
|
218
|
+
it('falls back to navigator.languages when no language is given', () => {
|
|
219
|
+
// Node exposes navigator.languages === ['en-US'], relating to 'en'.
|
|
220
|
+
expect(resolveSelectedTrack([full], { selection: { forced: false } })
|
|
221
|
+
?.id).toBe('full');
|
|
222
|
+
});
|
|
223
|
+
it('an ordered language list prefers earlier entries', () => {
|
|
224
|
+
const es = makeTrack({ id: 'es', language: 'es', forced: false });
|
|
225
|
+
expect(resolveSelectedTrack([full, es], {
|
|
226
|
+
selection: { language: ['es', 'en'], forced: false },
|
|
227
|
+
})?.id).toBe('es');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
it('selecting a known id loads cues and creates a TextTrack', async () => {
|
|
231
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nfirst`);
|
|
232
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
233
|
+
const activeChange = jasmine.createSpy();
|
|
234
|
+
h.controller.on('activeTextTrackChange', activeChange);
|
|
235
|
+
await h.setTracks([makeTrack()]);
|
|
236
|
+
expect(h.controller.activeTextTrack?.id).toBe('a');
|
|
237
|
+
expect(activeChange).toHaveBeenCalledTimes(1);
|
|
238
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
239
|
+
expect(h.provider.getOrCreate).toHaveBeenCalledWith('subtitles', 'English', 'en');
|
|
240
|
+
expect(h.provider.lastTrack?.mode).toBe('showing');
|
|
241
|
+
expect(h.provider.lastTrack?.cues?.length).toBe(1);
|
|
242
|
+
});
|
|
243
|
+
describe('authored cue settings', () => {
|
|
244
|
+
it("applies the cue's own parsed settings to the VTTCue", async () => {
|
|
245
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000 align:left size:80%\nx`);
|
|
246
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
247
|
+
await h.setTracks([makeTrack()]);
|
|
248
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
249
|
+
const cue = h.provider.lastTrack?.cues?.[0];
|
|
250
|
+
expect(cue.align).toBe('left');
|
|
251
|
+
expect(cue.size).toBe(80);
|
|
252
|
+
});
|
|
253
|
+
it('leaves the cue at defaults when it declares no settings', async () => {
|
|
254
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nx`);
|
|
255
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
256
|
+
await h.setTracks([makeTrack()]);
|
|
257
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
258
|
+
// The VTTCue stub defaults size to 100; unset settings don't touch it.
|
|
259
|
+
const cue = h.provider.lastTrack?.cues?.[0];
|
|
260
|
+
expect(cue.size).toBe(100);
|
|
261
|
+
});
|
|
262
|
+
it('does not apply settings to non-VTTCue cues', async () => {
|
|
263
|
+
;
|
|
264
|
+
globalThis.VTTCue = undefined;
|
|
265
|
+
globalThis.TextTrackCue = function TextTrackCue(start, end, text) {
|
|
266
|
+
this.startTime = start;
|
|
267
|
+
this.endTime = end;
|
|
268
|
+
this.text = text;
|
|
269
|
+
this.id = '';
|
|
270
|
+
this.size = 100;
|
|
271
|
+
};
|
|
272
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000 size:80%\nignored`);
|
|
273
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
274
|
+
await h.setTracks([makeTrack()]);
|
|
275
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
276
|
+
const cue = h.provider.lastTrack?.cues?.[0];
|
|
277
|
+
expect(cue.size).toBe(100) // not a VTTCue → never styled
|
|
278
|
+
;
|
|
279
|
+
globalThis.TextTrackCue = undefined;
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
describe('text track renderer', () => {
|
|
283
|
+
function fakeRenderer() {
|
|
284
|
+
return {
|
|
285
|
+
setTextTrack: jasmine.createSpy('setTextTrack'),
|
|
286
|
+
setStyles: jasmine.createSpy('setStyles'),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
it("keeps the DOM track 'hidden' and hands it to the renderer", async () => {
|
|
290
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nx`);
|
|
291
|
+
const renderer = fakeRenderer();
|
|
292
|
+
const h = build({
|
|
293
|
+
options: { selection: { id: 'a' } },
|
|
294
|
+
textTrackRenderer: renderer,
|
|
295
|
+
});
|
|
296
|
+
await h.setTracks([makeTrack()]);
|
|
297
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
298
|
+
expect(h.provider.lastTrack?.mode).toBe('hidden');
|
|
299
|
+
expect(renderer.setTextTrack).toHaveBeenCalledWith(h.provider.lastTrack);
|
|
300
|
+
});
|
|
301
|
+
it('detaches the renderer when captions are turned off', async () => {
|
|
302
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nx`);
|
|
303
|
+
const renderer = fakeRenderer();
|
|
304
|
+
const h = build({
|
|
305
|
+
options: { selection: { id: 'a' } },
|
|
306
|
+
textTrackRenderer: renderer,
|
|
307
|
+
});
|
|
308
|
+
await h.setTracks([makeTrack()]);
|
|
309
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
310
|
+
renderer.setTextTrack.calls.reset();
|
|
311
|
+
h.options$.value = { enabled: 'off' };
|
|
312
|
+
expect(renderer.setTextTrack).toHaveBeenCalledWith(null);
|
|
313
|
+
});
|
|
314
|
+
it('forwards authored STYLE blocks to the renderer', async () => {
|
|
315
|
+
respondVtt(`WEBVTT\n\nSTYLE\n::cue { color: red }\n\n00:00:01.000 --> 00:00:02.000\nx`);
|
|
316
|
+
const renderer = fakeRenderer();
|
|
317
|
+
const h = build({
|
|
318
|
+
options: { selection: { id: 'a' } },
|
|
319
|
+
textTrackRenderer: renderer,
|
|
320
|
+
});
|
|
321
|
+
await h.setTracks([makeTrack()]);
|
|
322
|
+
await flushUntil(() => renderer.setStyles.calls.count() > 0);
|
|
323
|
+
expect(renderer.setStyles).toHaveBeenCalledWith([
|
|
324
|
+
'::cue { color: red }',
|
|
325
|
+
]);
|
|
326
|
+
});
|
|
327
|
+
it('ignores styles that arrive after the load is cancelled', async () => {
|
|
328
|
+
let resolveFetch = () => { };
|
|
329
|
+
requester.request.and.returnValue(new Promise((res) => {
|
|
330
|
+
resolveFetch = res;
|
|
331
|
+
}));
|
|
332
|
+
const renderer = fakeRenderer();
|
|
333
|
+
const h = build({
|
|
334
|
+
options: { selection: { id: 'a' } },
|
|
335
|
+
textTrackRenderer: renderer,
|
|
336
|
+
});
|
|
337
|
+
await h.setTracks([makeTrack()]);
|
|
338
|
+
h.options$.value = { enabled: 'off' }; // cancels the load
|
|
339
|
+
renderer.setStyles.calls.reset();
|
|
340
|
+
resolveFetch(new Response(`WEBVTT\n\nSTYLE\n::cue { color: red }\n\n00:00:01.000 --> 00:00:02.000\nx`));
|
|
341
|
+
await flushPromises();
|
|
342
|
+
expect(renderer.setStyles).not.toHaveBeenCalled();
|
|
343
|
+
});
|
|
344
|
+
it('works with a renderer that cannot consume styles', async () => {
|
|
345
|
+
respondVtt(`WEBVTT\n\nSTYLE\n::cue { color: red }\n\n00:00:01.000 --> 00:00:02.000\nx`);
|
|
346
|
+
// A renderer implementing only setTextTrack (setStyles is optional).
|
|
347
|
+
const renderer = {
|
|
348
|
+
setTextTrack: jasmine.createSpy('setTextTrack'),
|
|
349
|
+
};
|
|
350
|
+
const h = build({
|
|
351
|
+
options: { selection: { id: 'a' } },
|
|
352
|
+
textTrackRenderer: renderer,
|
|
353
|
+
});
|
|
354
|
+
await h.setTracks([makeTrack()]);
|
|
355
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
356
|
+
expect(renderer.setTextTrack).toHaveBeenCalled();
|
|
357
|
+
});
|
|
358
|
+
it("renders natively ('showing') when no renderer is injected", async () => {
|
|
359
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nx`);
|
|
360
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
361
|
+
await h.setTracks([makeTrack()]);
|
|
362
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
363
|
+
expect(h.provider.lastTrack?.mode).toBe('showing');
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
it('preserves cue id when present', async () => {
|
|
367
|
+
respondVtt(`WEBVTT\n\nidA\n00:00:01.000 --> 00:00:02.000\nfirst`);
|
|
368
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
369
|
+
await h.setTracks([makeTrack()]);
|
|
370
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
371
|
+
const cue = h.provider.lastTrack?.cues?.[0];
|
|
372
|
+
expect(cue.id).toBe('idA');
|
|
373
|
+
});
|
|
374
|
+
it('dedupes cues that appear in more than one segment', async () => {
|
|
375
|
+
// HLS caption packagers commonly repeat a cue at the boundary of two
|
|
376
|
+
// adjacent segments. When the loader emits both, the controller must
|
|
377
|
+
// only add it to the DOM TextTrack once.
|
|
378
|
+
const playlistBody = `#EXTM3U
|
|
379
|
+
#EXT-X-TARGETDURATION:5
|
|
380
|
+
#EXTINF:5.0,
|
|
381
|
+
a.vtt
|
|
382
|
+
#EXTINF:5.0,
|
|
383
|
+
b.vtt
|
|
384
|
+
#EXT-X-ENDLIST
|
|
385
|
+
`;
|
|
386
|
+
// Both segments contain the boundary cue "shared" and their own.
|
|
387
|
+
const segA = `WEBVTT\n\n00:00:00.500 --> 00:00:01.500\nonly-a\n\n00:00:04.500 --> 00:00:05.500\nshared`;
|
|
388
|
+
const segB = `WEBVTT\n\n00:00:04.500 --> 00:00:05.500\nshared\n\n00:00:07.000 --> 00:00:08.000\nonly-b`;
|
|
389
|
+
let call = 0;
|
|
390
|
+
requester.request.and.callFake(() => {
|
|
391
|
+
call++;
|
|
392
|
+
if (call === 1)
|
|
393
|
+
return Promise.resolve(new Response(playlistBody));
|
|
394
|
+
if (call === 2)
|
|
395
|
+
return Promise.resolve(new Response(segA));
|
|
396
|
+
return Promise.resolve(new Response(segB));
|
|
397
|
+
});
|
|
398
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
399
|
+
await h.setTracks([makeTrack({ uri: 'https://x.test/sub.m3u8' })]);
|
|
400
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) >= 3);
|
|
401
|
+
const texts = (h.provider.lastTrack?.cues ?? []).map((c) => c.text);
|
|
402
|
+
expect(texts).toEqual(['only-a', 'shared', 'only-b']);
|
|
403
|
+
});
|
|
404
|
+
it('selecting an unknown id renders nothing and stays inactive', async () => {
|
|
405
|
+
const handler = jasmine.createSpy();
|
|
406
|
+
const h = build({ options: { selection: { id: 'unknown' } } });
|
|
407
|
+
h.controller.on('activeTextTrackChange', handler);
|
|
408
|
+
await h.setTracks([makeTrack()]);
|
|
409
|
+
expect(handler).not.toHaveBeenCalled();
|
|
410
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
411
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
412
|
+
});
|
|
413
|
+
it('disabling clears the current active track', async () => {
|
|
414
|
+
respondVtt(`WEBVTT\n`);
|
|
415
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
416
|
+
await h.setTracks([makeTrack()]);
|
|
417
|
+
expect(h.controller.activeTextTrack?.id).toBe('a');
|
|
418
|
+
h.options$.value = { enabled: 'off' };
|
|
419
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
420
|
+
});
|
|
421
|
+
it('clears active selection when its track is removed', async () => {
|
|
422
|
+
respondVtt(`WEBVTT\n`);
|
|
423
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
424
|
+
const t1 = makeTrack({ id: 'a' });
|
|
425
|
+
const t2 = makeTrack({ id: 'b', uri: 'https://x.test/b.vtt' });
|
|
426
|
+
await h.setTracks([t1, t2]);
|
|
427
|
+
expect(h.controller.activeTextTrack?.id).toBe('a');
|
|
428
|
+
await h.setTracks([t2]);
|
|
429
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
430
|
+
});
|
|
431
|
+
it('keeps the active selection when the list updates with it still present', async () => {
|
|
432
|
+
respondVtt(`WEBVTT\n`);
|
|
433
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
434
|
+
const t1 = makeTrack({ id: 'a' });
|
|
435
|
+
const t2 = makeTrack({ id: 'b' });
|
|
436
|
+
await h.setTracks([t1]);
|
|
437
|
+
expect(h.controller.activeTextTrack?.id).toBe('a');
|
|
438
|
+
await h.setTracks([t1, t2]);
|
|
439
|
+
expect(h.controller.activeTextTrack?.id).toBe('a');
|
|
440
|
+
});
|
|
441
|
+
it('emits textTrackError on fetch failure', async () => {
|
|
442
|
+
requester.request.and.rejectWith(new Error('boom'));
|
|
443
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
444
|
+
const onError = jasmine.createSpy('textTrackError');
|
|
445
|
+
h.controller.on('textTrackError', onError);
|
|
446
|
+
const t = makeTrack();
|
|
447
|
+
await h.setTracks([t]);
|
|
448
|
+
await flushUntil(() => onError.calls.count() > 0);
|
|
449
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
450
|
+
const arg = onError.calls.mostRecent().args[0];
|
|
451
|
+
expect(arg.track).toBe(t);
|
|
452
|
+
expect(arg.error.message).toBe('boom');
|
|
453
|
+
});
|
|
454
|
+
it('wraps non-Error rejections', async () => {
|
|
455
|
+
requester.request.and.rejectWith('plain');
|
|
456
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
457
|
+
const onError = jasmine.createSpy();
|
|
458
|
+
h.controller.on('textTrackError', onError);
|
|
459
|
+
await h.setTracks([makeTrack()]);
|
|
460
|
+
await flushUntil(() => onError.calls.count() > 0);
|
|
461
|
+
expect(onError.calls.mostRecent().args[0].error.message).toBe('plain');
|
|
462
|
+
});
|
|
463
|
+
it('drops cues that arrive after the load has been cancelled', async () => {
|
|
464
|
+
// Delay the fetch by one macrotask so we can cancel between the
|
|
465
|
+
// network reply and the onCues delivery.
|
|
466
|
+
let resolveFetch = () => { };
|
|
467
|
+
requester.request.and.returnValue(new Promise((res) => {
|
|
468
|
+
resolveFetch = res;
|
|
469
|
+
}));
|
|
470
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
471
|
+
await h.setTracks([makeTrack()]);
|
|
472
|
+
const track = h.provider.lastTrack;
|
|
473
|
+
// Turn captions off — aborts the in-flight load.
|
|
474
|
+
h.options$.value = { enabled: 'off' };
|
|
475
|
+
resolveFetch(new Response(`WEBVTT\n\n00:00:00.500 --> 00:00:01.500\npost`));
|
|
476
|
+
await flushPromises();
|
|
477
|
+
expect(track?.storedCues.length ?? 0).toBe(0);
|
|
478
|
+
});
|
|
479
|
+
it('does not emit textTrackError when a load is cancelled by switching', async () => {
|
|
480
|
+
let rejectFirst = () => { };
|
|
481
|
+
let callCount = 0;
|
|
482
|
+
requester.request.and.callFake(() => new Promise((_, rej) => {
|
|
483
|
+
callCount++;
|
|
484
|
+
if (callCount === 1)
|
|
485
|
+
rejectFirst = rej;
|
|
486
|
+
else
|
|
487
|
+
rej(new Error('unused'));
|
|
488
|
+
}));
|
|
489
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
490
|
+
const onError = jasmine.createSpy('textTrackError');
|
|
491
|
+
h.controller.on('textTrackError', onError);
|
|
492
|
+
await h.setTracks([makeTrack()]);
|
|
493
|
+
h.options$.value = { enabled: 'off' };
|
|
494
|
+
rejectFirst(new Error('would-be error'));
|
|
495
|
+
await flushPromises();
|
|
496
|
+
expect(onError).not.toHaveBeenCalled();
|
|
497
|
+
});
|
|
498
|
+
it('switching language reuses the per-rendition track and swaps its cues', async () => {
|
|
499
|
+
let call = 0;
|
|
500
|
+
requester.request.and.callFake(() => {
|
|
501
|
+
call++;
|
|
502
|
+
return Promise.resolve(new Response(call === 1
|
|
503
|
+
? `WEBVTT\n\n00:00:00.500 --> 00:00:01.500\none`
|
|
504
|
+
: `WEBVTT\n\n00:00:01.000 --> 00:00:02.000\ntwo`));
|
|
505
|
+
});
|
|
506
|
+
const en = makeTrack({ id: 'en', language: 'en', label: 'English' });
|
|
507
|
+
const es = makeTrack({
|
|
508
|
+
id: 'es',
|
|
509
|
+
language: 'es',
|
|
510
|
+
label: 'Espanol',
|
|
511
|
+
uri: 'https://x.test/es.vtt',
|
|
512
|
+
});
|
|
513
|
+
const h = build({ options: { selection: { id: 'en' } } });
|
|
514
|
+
await h.setTracks([en, es]);
|
|
515
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
516
|
+
const enTrack = h.provider.lastTrack;
|
|
517
|
+
h.select({ id: 'es' });
|
|
518
|
+
// The outgoing English track is hidden and cleared; a distinct Spanish
|
|
519
|
+
// track (its own rendition) renders instead.
|
|
520
|
+
expect(enTrack?.storedCues.length).toBe(0);
|
|
521
|
+
expect(enTrack?.mode).toBe('disabled');
|
|
522
|
+
await flushUntil(() => h.provider.lastTrack !== enTrack &&
|
|
523
|
+
(h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
524
|
+
expect(h.provider.lastTrack).not.toBe(enTrack);
|
|
525
|
+
expect(h.provider.lastTrack?.language).toBe('es');
|
|
526
|
+
expect(h.provider.showingWithCues().length).toBe(1);
|
|
527
|
+
});
|
|
528
|
+
it('cycling languages never accumulates or double-shows cues', async () => {
|
|
529
|
+
requester.request.and.callFake(() => Promise.resolve(new Response(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\ncue`)));
|
|
530
|
+
const langs = ['en', 'es', 'ja'].map((language) => makeTrack({
|
|
531
|
+
id: language,
|
|
532
|
+
language,
|
|
533
|
+
uri: `https://x.test/${language}.vtt`,
|
|
534
|
+
}));
|
|
535
|
+
const h = build({ options: { selection: { id: 'en' } } });
|
|
536
|
+
await h.setTracks(langs);
|
|
537
|
+
for (const id of ['en', 'es', 'ja', 'en']) {
|
|
538
|
+
h.select({ id });
|
|
539
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
540
|
+
expect(h.provider.showingWithCues().length)
|
|
541
|
+
.withContext(`after selecting ${id}`)
|
|
542
|
+
.toBe(1);
|
|
543
|
+
}
|
|
544
|
+
// One track per distinct rendition (3), reused on the repeat of 'en' —
|
|
545
|
+
// never one per activation (which would be 4).
|
|
546
|
+
expect(h.provider.created.length).toBe(3);
|
|
547
|
+
});
|
|
548
|
+
it('deactivate hides the DOM track and activate rebuilds it, keeping the selection', async () => {
|
|
549
|
+
requester.request.and.callFake(() => Promise.resolve(new Response(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\none`)));
|
|
550
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
551
|
+
await h.setTracks([makeTrack()]);
|
|
552
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
553
|
+
const track = h.provider.lastTrack;
|
|
554
|
+
expect(track?.mode).toBe('showing');
|
|
555
|
+
expect(h.controller.active).toBeTrue();
|
|
556
|
+
h.controller.deactivate();
|
|
557
|
+
expect(h.controller.active).toBeFalse();
|
|
558
|
+
expect(track?.storedCues.length).toBe(0);
|
|
559
|
+
expect(track?.mode).toBe('disabled');
|
|
560
|
+
expect(h.controller.activeTextTrack?.id).toBe('a');
|
|
561
|
+
h.controller.activate();
|
|
562
|
+
expect(h.controller.active).toBeTrue();
|
|
563
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
564
|
+
expect(h.provider.lastTrack?.mode).toBe('showing');
|
|
565
|
+
expect(h.provider.lastTrack?.cues?.length).toBe(1);
|
|
566
|
+
});
|
|
567
|
+
it('activate is a no-op when nothing is selected', () => {
|
|
568
|
+
const h = build();
|
|
569
|
+
h.controller.activate();
|
|
570
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
571
|
+
});
|
|
572
|
+
it('activate is a no-op when already rendering', async () => {
|
|
573
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\none`);
|
|
574
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
575
|
+
await h.setTracks([makeTrack()]);
|
|
576
|
+
await flushUntil(() => (h.provider.lastTrack?.cues?.length ?? 0) > 0);
|
|
577
|
+
h.provider.getOrCreate.calls.reset();
|
|
578
|
+
h.controller.activate();
|
|
579
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
580
|
+
});
|
|
581
|
+
it('does not render while deactivated even as the selection resolves', async () => {
|
|
582
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\none`);
|
|
583
|
+
const h = build({
|
|
584
|
+
options: { selection: { id: 'a' } },
|
|
585
|
+
activate: false,
|
|
586
|
+
});
|
|
587
|
+
h.controller.deactivate();
|
|
588
|
+
await h.setTracks([makeTrack()]);
|
|
589
|
+
// Selection is tracked, but nothing is rendered until activate().
|
|
590
|
+
expect(h.controller.activeTextTrack?.id).toBe('a');
|
|
591
|
+
expect(h.provider.getOrCreate).not.toHaveBeenCalled();
|
|
592
|
+
});
|
|
593
|
+
it('skips DOM cue creation when no VTTCue ctor is available', async () => {
|
|
594
|
+
;
|
|
595
|
+
globalThis.VTTCue = undefined;
|
|
596
|
+
respondVtt(`WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nignored`);
|
|
597
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
598
|
+
await h.setTracks([makeTrack()]);
|
|
599
|
+
await flushPromises();
|
|
600
|
+
expect(h.provider.lastTrack?.cues?.length ?? 0).toBe(0);
|
|
601
|
+
});
|
|
602
|
+
it('forwards requestInit when configured', async () => {
|
|
603
|
+
respondVtt(`WEBVTT\n`);
|
|
604
|
+
const h = build({
|
|
605
|
+
options: { selection: { id: 'a' } },
|
|
606
|
+
requestInit: { credentials: 'include' },
|
|
607
|
+
});
|
|
608
|
+
const t = makeTrack();
|
|
609
|
+
await h.setTracks([t]);
|
|
610
|
+
await flushPromises();
|
|
611
|
+
expect(requester.request).toHaveBeenCalledWith(t.uri, jasmine.objectContaining({ credentials: 'include' }), jasmine.any(Object));
|
|
612
|
+
});
|
|
613
|
+
it('drives HLS caption segment order from the media playhead', async () => {
|
|
614
|
+
const playlistBody = `#EXTM3U
|
|
615
|
+
#EXT-X-TARGETDURATION:5
|
|
616
|
+
#EXTINF:5.0,
|
|
617
|
+
a.vtt
|
|
618
|
+
#EXTINF:5.0,
|
|
619
|
+
b.vtt
|
|
620
|
+
#EXTINF:5.0,
|
|
621
|
+
c.vtt
|
|
622
|
+
#EXTINF:5.0,
|
|
623
|
+
d.vtt
|
|
624
|
+
#EXT-X-ENDLIST
|
|
625
|
+
`;
|
|
626
|
+
const fetched = [];
|
|
627
|
+
let call = 0;
|
|
628
|
+
requester.request.and.callFake((url) => {
|
|
629
|
+
call++;
|
|
630
|
+
if (call === 1)
|
|
631
|
+
return Promise.resolve(new Response(playlistBody));
|
|
632
|
+
const path = String(url).split('/').pop();
|
|
633
|
+
fetched.push(path);
|
|
634
|
+
return Promise.resolve(new Response(`WEBVTT\n\n00:00:00.000 --> 00:00:01.000\n${path}`));
|
|
635
|
+
});
|
|
636
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
637
|
+
// Media at t=12s should cause seg3 (10s..15s) to be fetched first.
|
|
638
|
+
h.playback.currentTime = 12;
|
|
639
|
+
await h.setTracks([makeTrack({ uri: 'https://x.test/sub.m3u8' })]);
|
|
640
|
+
await flushUntil(() => fetched.length > 0);
|
|
641
|
+
expect(fetched[0]).toBe('c.vtt');
|
|
642
|
+
});
|
|
643
|
+
it('dispose aborts in-flight loads and clears state', async () => {
|
|
644
|
+
let resolveResponse = () => { };
|
|
645
|
+
requester.request.and.callFake(() => new Promise((res) => {
|
|
646
|
+
resolveResponse = res;
|
|
647
|
+
}));
|
|
648
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
649
|
+
await h.setTracks([makeTrack()]);
|
|
650
|
+
const track = h.provider.lastTrack;
|
|
651
|
+
expect(track).not.toBeNull();
|
|
652
|
+
h.controller.dispose();
|
|
653
|
+
// Resolving after dispose must not throw or push cues onto the track.
|
|
654
|
+
resolveResponse(new Response(`WEBVTT\n\n00:00:00.500 --> 00:00:01.500\npost`));
|
|
655
|
+
await flushPromises();
|
|
656
|
+
expect(track?.mode).toBe('disabled');
|
|
657
|
+
expect(track?.storedCues.length).toBe(0);
|
|
658
|
+
expect(h.controller.activeTextTrack).toBeNull();
|
|
659
|
+
expect(h.controller.textTracks).toEqual([]);
|
|
660
|
+
});
|
|
661
|
+
it('creates the DOM track with an undefined language for null-language tracks', async () => {
|
|
662
|
+
respondVtt(`WEBVTT\n`);
|
|
663
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
664
|
+
await h.setTracks([makeTrack({ language: null })]);
|
|
665
|
+
await flushPromises();
|
|
666
|
+
expect(h.provider.getOrCreate).toHaveBeenCalledWith('subtitles', 'English', undefined);
|
|
667
|
+
});
|
|
668
|
+
it('setting the same resolved track list twice emits textTracksChange once', async () => {
|
|
669
|
+
const handler = jasmine.createSpy();
|
|
670
|
+
const h = build();
|
|
671
|
+
h.controller.on('textTracksChange', handler);
|
|
672
|
+
const list = [makeTrack()];
|
|
673
|
+
await h.setTracks(list);
|
|
674
|
+
await h.setTracks([makeTrack()]); // deep-equal contents
|
|
675
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
676
|
+
});
|
|
677
|
+
it('re-selecting the same id is a no-op', async () => {
|
|
678
|
+
respondVtt(`WEBVTT\n`);
|
|
679
|
+
const h = build({ options: { selection: { id: 'a' } } });
|
|
680
|
+
await h.setTracks([makeTrack()]);
|
|
681
|
+
const handler = jasmine.createSpy();
|
|
682
|
+
h.controller.on('activeTextTrackChange', handler);
|
|
683
|
+
h.select({ id: 'a' });
|
|
684
|
+
expect(handler).not.toHaveBeenCalled();
|
|
685
|
+
});
|
|
686
|
+
});
|