@amazon/vinyl 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +9225 -6633
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +9080 -6435
- package/dist/index.js.map +4 -4
- package/dist/test/benchmark.es5.js +266 -253
- package/dist/test/benchmark.js +3256 -3190
- package/dist/test/benchmark.js.map +4 -4
- package/dist/test/integ.es5.js +1404 -1093
- package/dist/test/integ.js +45924 -33873
- package/dist/test/integ.js.map +4 -4
- 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/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 +146 -0
- package/dist/types/current/ad/AdControllerImpl.d.ts.map +1 -0
- package/dist/types/current/ad/AdControllerImpl.js +705 -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 +246 -0
- package/dist/types/current/drm/DrmController.d.ts +29 -4
- package/dist/types/current/drm/DrmController.d.ts.map +1 -1
- package/dist/types/current/drm/DrmControllerImpl.d.ts +13 -4
- package/dist/types/current/drm/DrmControllerImpl.d.ts.map +1 -1
- package/dist/types/current/drm/DrmControllerImpl.js +85 -14
- package/dist/types/current/drm/util/unpackPlayReadyChallenge.d.ts +5 -0
- package/dist/types/current/drm/util/unpackPlayReadyChallenge.d.ts.map +1 -1
- package/dist/types/current/drm/util/unpackPlayReadyChallenge.js +5 -0
- package/dist/types/current/index.d.ts +151 -126
- package/dist/types/current/index.d.ts.map +1 -1
- package/dist/types/current/index.js +151 -126
- package/dist/types/current/playback/PlaybackControllerImpl.d.ts +1 -0
- package/dist/types/current/playback/PlaybackControllerImpl.d.ts.map +1 -1
- package/dist/types/current/playback/PlaybackControllerImpl.js +44 -5
- package/dist/types/current/playback/PlaybackSource.d.ts +1 -1
- package/dist/types/current/playback/ReadonlyPlaybackController.d.ts +6 -1
- package/dist/types/current/playback/ReadonlyPlaybackController.d.ts.map +1 -1
- package/dist/types/current/playback/ReadonlyPlaybackController.js +1 -0
- package/dist/types/current/streaming/ContentStream.d.ts +8 -1
- package/dist/types/current/streaming/ContentStream.d.ts.map +1 -1
- package/dist/types/current/streaming/ContentStream.js +19 -2
- package/dist/types/current/streaming/LoadMetric.d.ts +58 -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/ManifestController.d.ts +16 -1
- package/dist/types/current/streaming/ManifestController.d.ts.map +1 -1
- 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/MediaQualityMetadata.d.ts +16 -0
- package/dist/types/current/streaming/MediaQualityMetadata.d.ts.map +1 -1
- package/dist/types/current/streaming/MediaQualityMetadata.js +6 -0
- package/dist/types/current/streaming/MediaTimeline.d.ts +6 -0
- package/dist/types/current/streaming/MediaTimeline.d.ts.map +1 -1
- package/dist/types/current/streaming/SegmentController.d.ts +5 -0
- package/dist/types/current/streaming/SegmentController.d.ts.map +1 -1
- package/dist/types/current/streaming/SegmentControllerImpl.d.ts +12 -0
- package/dist/types/current/streaming/SegmentControllerImpl.d.ts.map +1 -1
- package/dist/types/current/streaming/SegmentControllerImpl.js +67 -10
- package/dist/types/current/streaming/StreamingEventMap.d.ts +38 -1
- package/dist/types/current/streaming/StreamingEventMap.d.ts.map +1 -1
- package/dist/types/current/streaming/StreamingEventMap.js +4 -0
- package/dist/types/current/streaming/abr/QualitySelectorImpl.d.ts +28 -0
- package/dist/types/current/streaming/abr/QualitySelectorImpl.d.ts.map +1 -1
- package/dist/types/current/streaming/abr/QualitySelectorImpl.js +39 -4
- package/dist/types/current/streaming/buffering/BufferingController.d.ts +37 -0
- package/dist/types/current/streaming/buffering/BufferingController.d.ts.map +1 -1
- package/dist/types/current/streaming/buffering/BufferingController.js +79 -3
- package/dist/types/current/streaming/buffering/MediaSourceController.d.ts +16 -11
- package/dist/types/current/streaming/buffering/MediaSourceController.d.ts.map +1 -1
- package/dist/types/current/streaming/buffering/MediaSourceController.js +76 -51
- package/dist/types/current/streaming/buffering/SourceBufferController.d.ts.map +1 -1
- package/dist/types/current/streaming/buffering/SourceBufferController.js +5 -2
- 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/createDefaultMediaTimelineTransformer.d.ts +4 -1
- package/dist/types/current/streaming/createDefaultMediaTimelineTransformer.d.ts.map +1 -1
- package/dist/types/current/streaming/createDefaultMediaTimelineTransformer.js +32 -1
- 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/streaming/mediaTimelineLanguageFilter.d.ts +24 -6
- package/dist/types/current/streaming/mediaTimelineLanguageFilter.d.ts.map +1 -1
- package/dist/types/current/streaming/mediaTimelineLanguageFilter.js +61 -16
- package/dist/types/current/text/SidecarTextTrackController.d.ts +91 -0
- package/dist/types/current/text/SidecarTextTrackController.d.ts.map +1 -0
- package/dist/types/current/text/SidecarTextTrackController.js +237 -0
- package/dist/types/current/text/SidecarTextTrackLoader.d.ts +60 -0
- package/dist/types/current/text/SidecarTextTrackLoader.d.ts.map +1 -0
- package/dist/types/current/text/SidecarTextTrackLoader.js +144 -0
- package/dist/types/current/text/TextTrack.d.ts +125 -0
- package/dist/types/current/text/TextTrack.d.ts.map +1 -0
- package/dist/types/current/text/TextTrack.js +9 -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 +109 -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/parseWebVtt.d.ts +46 -0
- package/dist/types/current/text/parseWebVtt.d.ts.map +1 -0
- package/dist/types/current/text/parseWebVtt.js +120 -0
- package/dist/types/current/text/pickPreferredTextTrack.d.ts +14 -0
- package/dist/types/current/text/pickPreferredTextTrack.d.ts.map +1 -0
- package/dist/types/current/text/pickPreferredTextTrack.js +68 -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/Track.d.ts +50 -7
- package/dist/types/current/track/Track.d.ts.map +1 -1
- package/dist/types/current/track/TrackBase.d.ts +30 -29
- package/dist/types/current/track/TrackBase.d.ts.map +1 -1
- package/dist/types/current/track/TrackBase.js +68 -16
- package/dist/types/current/track/TrackController.d.ts +100 -36
- package/dist/types/current/track/TrackController.d.ts.map +1 -1
- package/dist/types/current/track/TrackController.js +368 -115
- package/dist/types/current/track/TrackFactory.d.ts +23 -3
- package/dist/types/current/track/TrackFactory.d.ts.map +1 -1
- package/dist/types/current/track/TrackFactory.js +9 -3
- package/dist/types/current/track/dash/DashManifestController.d.ts +1 -40
- package/dist/types/current/track/dash/DashManifestController.d.ts.map +1 -1
- package/dist/types/current/track/dash/DashManifestController.js +1 -79
- package/dist/types/current/track/dash/DashMediaQualityMetadataResolver.d.ts.map +1 -1
- package/dist/types/current/track/dash/DashMediaQualityMetadataResolver.js +13 -0
- package/dist/types/current/track/dash/DashTrack.d.ts +4 -5
- package/dist/types/current/track/dash/DashTrack.d.ts.map +1 -1
- package/dist/types/current/track/dash/DashTrack.js +3 -5
- package/dist/types/current/track/dash/buildDashMediaTimeline.d.ts.map +1 -1
- package/dist/types/current/track/dash/buildDashMediaTimeline.js +3 -2
- package/dist/types/current/track/dash/createDashContentTypesValue.js +2 -2
- package/dist/types/current/track/dash/createDashFactories.d.ts +39 -12
- package/dist/types/current/track/dash/createDashFactories.d.ts.map +1 -1
- package/dist/types/current/track/dash/createDashFactories.js +33 -5
- package/dist/types/current/track/dash/createSegmentControllerFactory.d.ts +0 -5
- package/dist/types/current/track/dash/createSegmentControllerFactory.d.ts.map +1 -1
- package/dist/types/current/track/dash/timeline/DashRepresentationSegmentProvider.d.ts.map +1 -1
- package/dist/types/current/track/dash/timeline/DashRepresentationSegmentProvider.js +2 -1
- package/dist/types/current/track/dash/util/mpd.d.ts +10 -0
- package/dist/types/current/track/dash/util/mpd.d.ts.map +1 -1
- package/dist/types/current/track/dash/util/mpd.js +16 -0
- package/dist/types/current/track/hls/HlsManifestController.d.ts +1 -25
- package/dist/types/current/track/hls/HlsManifestController.d.ts.map +1 -1
- package/dist/types/current/track/hls/HlsManifestController.js +1 -68
- package/dist/types/{ts3.7/track/hls/HlsManifestProvider.d.ts → current/track/hls/HlsManifestData.d.ts} +4 -6
- 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/HlsMediaQualityMetadataResolver.js +9 -6
- package/dist/types/current/track/hls/HlsTrack.d.ts +5 -6
- package/dist/types/current/track/hls/HlsTrack.d.ts.map +1 -1
- package/dist/types/current/track/hls/HlsTrack.js +3 -5
- package/dist/types/current/track/hls/buildHlsMediaTimeline.d.ts +8 -1
- package/dist/types/current/track/hls/buildHlsMediaTimeline.d.ts.map +1 -1
- package/dist/types/current/track/hls/buildHlsMediaTimeline.js +134 -67
- package/dist/types/current/track/hls/createDefaultHlsManifestTransformer.d.ts +1 -1
- package/dist/types/current/track/hls/createDefaultHlsManifestTransformer.d.ts.map +1 -1
- package/dist/types/current/track/hls/createHlsContentTypesValue.d.ts +1 -1
- package/dist/types/current/track/hls/createHlsContentTypesValue.d.ts.map +1 -1
- package/dist/types/current/track/hls/createHlsFactories.d.ts +40 -13
- package/dist/types/current/track/hls/createHlsFactories.d.ts.map +1 -1
- package/dist/types/current/track/hls/createHlsFactories.js +35 -6
- 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/hls/createUrlHlsManifestProvider.d.ts +4 -4
- package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts.map +1 -1
- package/dist/types/current/track/hls/createUrlHlsManifestProvider.js +22 -3
- package/dist/types/current/track/hls/index.d.ts +1 -1
- package/dist/types/current/track/hls/index.d.ts.map +1 -1
- package/dist/types/current/track/hls/index.js +1 -1
- 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 +66 -8
- package/dist/types/current/track/mse/MseTrack.d.ts.map +1 -1
- package/dist/types/current/track/mse/MseTrack.js +216 -52
- package/dist/types/current/track/source/SourceObjectTrack.d.ts +2 -2
- package/dist/types/current/track/source/SourceObjectTrack.d.ts.map +1 -1
- package/dist/types/current/track/source/SourceTrack.d.ts +2 -2
- package/dist/types/current/track/source/SourceTrack.d.ts.map +1 -1
- package/dist/types/current/track/source/SourceTrack.js +1 -2
- package/dist/types/current/track/source/SourceTrackBase.d.ts +7 -5
- package/dist/types/current/track/source/SourceTrackBase.d.ts.map +1 -1
- package/dist/types/current/track/source/SourceTrackBase.js +26 -9
- 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/mediaSource.d.ts +5 -0
- package/dist/types/current/util/media/mediaSource.d.ts.map +1 -1
- package/dist/types/current/util/media/mediaSource.js +6 -1
- package/dist/types/current/vinyl/ReadonlyPlaybackStreamingState.d.ts +9 -2
- package/dist/types/current/vinyl/ReadonlyPlaybackStreamingState.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylDeps.d.ts +5 -0
- package/dist/types/current/vinyl/VinylDeps.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylOptions.d.ts +68 -4
- package/dist/types/current/vinyl/VinylOptions.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylOptions.js +17 -2
- package/dist/types/current/vinyl/VinylPlayer.d.ts +63 -4
- package/dist/types/current/vinyl/VinylPlayer.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylPlayer.js +216 -36
- package/dist/types/current/vinyl/createVinylFactories.d.ts +55 -12
- package/dist/types/current/vinyl/createVinylFactories.d.ts.map +1 -1
- package/dist/types/current/vinyl/createVinylFactories.js +4 -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 +146 -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/DrmController.d.ts +29 -4
- package/dist/types/ts3.7/drm/DrmControllerImpl.d.ts +13 -4
- package/dist/types/ts3.7/drm/util/unpackPlayReadyChallenge.d.ts +5 -0
- package/dist/types/ts3.7/index.d.ts +151 -126
- package/dist/types/ts3.7/playback/PlaybackControllerImpl.d.ts +1 -0
- package/dist/types/ts3.7/playback/PlaybackSource.d.ts +1 -1
- package/dist/types/ts3.7/playback/ReadonlyPlaybackController.d.ts +6 -0
- package/dist/types/ts3.7/streaming/ContentStream.d.ts +8 -1
- package/dist/types/ts3.7/streaming/LoadMetric.d.ts +58 -0
- package/dist/types/ts3.7/streaming/ManifestController.d.ts +16 -1
- package/dist/types/ts3.7/streaming/ManifestControllerImpl.d.ts +40 -0
- package/dist/types/ts3.7/streaming/MediaQualityMetadata.d.ts +19 -0
- package/dist/types/ts3.7/streaming/MediaTimeline.d.ts +6 -0
- package/dist/types/ts3.7/streaming/SegmentController.d.ts +5 -0
- package/dist/types/ts3.7/streaming/SegmentControllerImpl.d.ts +12 -0
- package/dist/types/ts3.7/streaming/StreamingEventMap.d.ts +42 -1
- package/dist/types/ts3.7/streaming/abr/QualitySelectorImpl.d.ts +28 -0
- package/dist/types/ts3.7/streaming/buffering/BufferingController.d.ts +37 -0
- package/dist/types/ts3.7/streaming/buffering/MediaSourceController.d.ts +16 -11
- 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/createDefaultMediaTimelineTransformer.d.ts +4 -1
- package/dist/types/ts3.7/streaming/mediaTimelineAccessibilityFilter.d.ts +35 -0
- package/dist/types/ts3.7/streaming/mediaTimelineLanguageFilter.d.ts +24 -6
- package/dist/types/ts3.7/text/SidecarTextTrackController.d.ts +91 -0
- package/dist/types/ts3.7/text/SidecarTextTrackLoader.d.ts +60 -0
- package/dist/types/ts3.7/text/TextTrack.d.ts +129 -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/parseWebVtt.d.ts +46 -0
- package/dist/types/ts3.7/text/pickPreferredTextTrack.d.ts +14 -0
- package/dist/types/ts3.7/track/SeekRange.d.ts +9 -0
- package/dist/types/ts3.7/track/Track.d.ts +50 -7
- package/dist/types/ts3.7/track/TrackBase.d.ts +21 -29
- package/dist/types/ts3.7/track/TrackController.d.ts +102 -37
- package/dist/types/ts3.7/track/TrackFactory.d.ts +22 -2
- package/dist/types/ts3.7/track/dash/DashManifestController.d.ts +1 -36
- package/dist/types/ts3.7/track/dash/DashTrack.d.ts +4 -5
- package/dist/types/ts3.7/track/dash/createDashFactories.d.ts +39 -12
- package/dist/types/ts3.7/track/dash/createSegmentControllerFactory.d.ts +0 -5
- package/dist/types/ts3.7/track/dash/util/mpd.d.ts +10 -0
- package/dist/types/ts3.7/track/hls/HlsManifestController.d.ts +1 -22
- package/dist/types/{current/track/hls/HlsManifestProvider.d.ts → ts3.7/track/hls/HlsManifestData.d.ts} +4 -6
- package/dist/types/ts3.7/track/hls/HlsTrack.d.ts +5 -6
- package/dist/types/ts3.7/track/hls/buildHlsMediaTimeline.d.ts +8 -1
- package/dist/types/ts3.7/track/hls/createDefaultHlsManifestTransformer.d.ts +1 -1
- package/dist/types/ts3.7/track/hls/createHlsContentTypesValue.d.ts +1 -1
- package/dist/types/ts3.7/track/hls/createHlsFactories.d.ts +40 -13
- package/dist/types/ts3.7/track/hls/createHlsManifestProvider.d.ts +6 -0
- package/dist/types/ts3.7/track/hls/createUrlHlsManifestProvider.d.ts +4 -4
- package/dist/types/ts3.7/track/hls/index.d.ts +1 -1
- package/dist/types/ts3.7/track/inferLoadOptionsFromUri.d.ts +15 -0
- package/dist/types/ts3.7/track/mse/MseTrack.d.ts +65 -7
- package/dist/types/ts3.7/track/source/SourceObjectTrack.d.ts +2 -2
- package/dist/types/ts3.7/track/source/SourceTrack.d.ts +2 -2
- package/dist/types/ts3.7/track/source/SourceTrackBase.d.ts +7 -5
- 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/mediaSource.d.ts +5 -0
- package/dist/types/ts3.7/vinyl/ReadonlyPlaybackStreamingState.d.ts +9 -2
- package/dist/types/ts3.7/vinyl/VinylDeps.d.ts +5 -0
- package/dist/types/ts3.7/vinyl/VinylOptions.d.ts +68 -4
- package/dist/types/ts3.7/vinyl/VinylPlayer.d.ts +33 -4
- package/dist/types/ts3.7/vinyl/createVinylFactories.d.ts +55 -12
- 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 +146 -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/DrmController.d.ts +29 -4
- package/dist/types/ts4.1/drm/DrmControllerImpl.d.ts +13 -4
- package/dist/types/ts4.1/drm/util/unpackPlayReadyChallenge.d.ts +5 -0
- package/dist/types/ts4.1/index.d.ts +151 -126
- package/dist/types/ts4.1/playback/PlaybackControllerImpl.d.ts +1 -0
- package/dist/types/ts4.1/playback/PlaybackSource.d.ts +1 -1
- package/dist/types/ts4.1/playback/ReadonlyPlaybackController.d.ts +6 -0
- package/dist/types/ts4.1/streaming/ContentStream.d.ts +8 -1
- package/dist/types/ts4.1/streaming/LoadMetric.d.ts +58 -0
- package/dist/types/ts4.1/streaming/ManifestController.d.ts +16 -1
- package/dist/types/ts4.1/streaming/ManifestControllerImpl.d.ts +40 -0
- package/dist/types/ts4.1/streaming/MediaQualityMetadata.d.ts +19 -0
- package/dist/types/ts4.1/streaming/MediaTimeline.d.ts +6 -0
- package/dist/types/ts4.1/streaming/SegmentController.d.ts +5 -0
- package/dist/types/ts4.1/streaming/SegmentControllerImpl.d.ts +12 -0
- package/dist/types/ts4.1/streaming/StreamingEventMap.d.ts +42 -1
- package/dist/types/ts4.1/streaming/abr/QualitySelectorImpl.d.ts +28 -0
- package/dist/types/ts4.1/streaming/buffering/BufferingController.d.ts +37 -0
- package/dist/types/ts4.1/streaming/buffering/MediaSourceController.d.ts +16 -11
- 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/createDefaultMediaTimelineTransformer.d.ts +4 -1
- package/dist/types/ts4.1/streaming/mediaTimelineAccessibilityFilter.d.ts +35 -0
- package/dist/types/ts4.1/streaming/mediaTimelineLanguageFilter.d.ts +24 -6
- package/dist/types/ts4.1/text/SidecarTextTrackController.d.ts +92 -0
- package/dist/types/ts4.1/text/SidecarTextTrackLoader.d.ts +60 -0
- package/dist/types/ts4.1/text/TextTrack.d.ts +129 -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/parseWebVtt.d.ts +46 -0
- package/dist/types/ts4.1/text/pickPreferredTextTrack.d.ts +14 -0
- package/dist/types/ts4.1/track/SeekRange.d.ts +9 -0
- package/dist/types/ts4.1/track/Track.d.ts +50 -7
- package/dist/types/ts4.1/track/TrackBase.d.ts +21 -29
- package/dist/types/ts4.1/track/TrackController.d.ts +102 -37
- package/dist/types/ts4.1/track/TrackFactory.d.ts +22 -2
- package/dist/types/ts4.1/track/dash/DashManifestController.d.ts +1 -36
- package/dist/types/ts4.1/track/dash/DashTrack.d.ts +4 -5
- package/dist/types/ts4.1/track/dash/createDashFactories.d.ts +39 -12
- package/dist/types/ts4.1/track/dash/createSegmentControllerFactory.d.ts +0 -5
- package/dist/types/ts4.1/track/dash/util/mpd.d.ts +10 -0
- package/dist/types/ts4.1/track/hls/HlsManifestController.d.ts +1 -22
- package/dist/types/ts4.1/track/hls/{HlsManifestProvider.d.ts → HlsManifestData.d.ts} +4 -6
- package/dist/types/ts4.1/track/hls/HlsTrack.d.ts +5 -6
- package/dist/types/ts4.1/track/hls/buildHlsMediaTimeline.d.ts +8 -1
- package/dist/types/ts4.1/track/hls/createDefaultHlsManifestTransformer.d.ts +1 -1
- package/dist/types/ts4.1/track/hls/createHlsContentTypesValue.d.ts +1 -1
- package/dist/types/ts4.1/track/hls/createHlsFactories.d.ts +41 -13
- package/dist/types/ts4.1/track/hls/createHlsManifestProvider.d.ts +6 -0
- package/dist/types/ts4.1/track/hls/createUrlHlsManifestProvider.d.ts +4 -4
- package/dist/types/ts4.1/track/hls/index.d.ts +1 -1
- package/dist/types/ts4.1/track/inferLoadOptionsFromUri.d.ts +15 -0
- package/dist/types/ts4.1/track/mse/MseTrack.d.ts +65 -7
- package/dist/types/ts4.1/track/source/SourceObjectTrack.d.ts +2 -2
- package/dist/types/ts4.1/track/source/SourceTrack.d.ts +2 -2
- package/dist/types/ts4.1/track/source/SourceTrackBase.d.ts +7 -5
- 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/mediaSource.d.ts +5 -0
- package/dist/types/ts4.1/vinyl/ReadonlyPlaybackStreamingState.d.ts +9 -2
- package/dist/types/ts4.1/vinyl/VinylDeps.d.ts +5 -0
- package/dist/types/ts4.1/vinyl/VinylOptions.d.ts +68 -4
- package/dist/types/ts4.1/vinyl/VinylPlayer.d.ts +33 -4
- package/dist/types/ts4.1/vinyl/createVinylFactories.d.ts +56 -12
- 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 +146 -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/DrmController.d.ts +29 -4
- package/dist/types/ts4.7/drm/DrmControllerImpl.d.ts +13 -4
- package/dist/types/ts4.7/drm/util/unpackPlayReadyChallenge.d.ts +5 -0
- package/dist/types/ts4.7/index.d.ts +151 -126
- package/dist/types/ts4.7/playback/PlaybackControllerImpl.d.ts +1 -0
- package/dist/types/ts4.7/playback/PlaybackSource.d.ts +1 -1
- package/dist/types/ts4.7/playback/ReadonlyPlaybackController.d.ts +6 -0
- package/dist/types/ts4.7/streaming/ContentStream.d.ts +8 -1
- package/dist/types/ts4.7/streaming/LoadMetric.d.ts +58 -0
- package/dist/types/ts4.7/streaming/ManifestController.d.ts +16 -1
- package/dist/types/ts4.7/streaming/ManifestControllerImpl.d.ts +43 -0
- package/dist/types/ts4.7/streaming/MediaQualityMetadata.d.ts +19 -0
- package/dist/types/ts4.7/streaming/MediaTimeline.d.ts +6 -0
- package/dist/types/ts4.7/streaming/SegmentController.d.ts +5 -0
- package/dist/types/ts4.7/streaming/SegmentControllerImpl.d.ts +12 -0
- package/dist/types/ts4.7/streaming/StreamingEventMap.d.ts +42 -1
- package/dist/types/ts4.7/streaming/abr/QualitySelectorImpl.d.ts +28 -0
- package/dist/types/ts4.7/streaming/buffering/BufferingController.d.ts +37 -0
- package/dist/types/ts4.7/streaming/buffering/MediaSourceController.d.ts +16 -11
- 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/createDefaultMediaTimelineTransformer.d.ts +4 -1
- package/dist/types/ts4.7/streaming/mediaTimelineAccessibilityFilter.d.ts +35 -0
- package/dist/types/ts4.7/streaming/mediaTimelineLanguageFilter.d.ts +24 -6
- package/dist/types/ts4.7/text/SidecarTextTrackController.d.ts +91 -0
- package/dist/types/ts4.7/text/SidecarTextTrackLoader.d.ts +60 -0
- package/dist/types/ts4.7/text/TextTrack.d.ts +129 -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/parseWebVtt.d.ts +46 -0
- package/dist/types/ts4.7/text/pickPreferredTextTrack.d.ts +14 -0
- package/dist/types/ts4.7/track/SeekRange.d.ts +9 -0
- package/dist/types/ts4.7/track/Track.d.ts +50 -7
- package/dist/types/ts4.7/track/TrackBase.d.ts +30 -29
- package/dist/types/ts4.7/track/TrackController.d.ts +102 -37
- package/dist/types/ts4.7/track/TrackFactory.d.ts +23 -3
- package/dist/types/ts4.7/track/dash/DashManifestController.d.ts +1 -40
- package/dist/types/ts4.7/track/dash/DashTrack.d.ts +4 -5
- package/dist/types/ts4.7/track/dash/createDashFactories.d.ts +39 -12
- package/dist/types/ts4.7/track/dash/createSegmentControllerFactory.d.ts +0 -5
- package/dist/types/ts4.7/track/dash/util/mpd.d.ts +10 -0
- package/dist/types/ts4.7/track/hls/HlsManifestController.d.ts +1 -25
- package/dist/types/ts4.7/track/hls/{HlsManifestProvider.d.ts → HlsManifestData.d.ts} +4 -6
- package/dist/types/ts4.7/track/hls/HlsTrack.d.ts +5 -6
- package/dist/types/ts4.7/track/hls/buildHlsMediaTimeline.d.ts +8 -1
- package/dist/types/ts4.7/track/hls/createDefaultHlsManifestTransformer.d.ts +1 -1
- package/dist/types/ts4.7/track/hls/createHlsContentTypesValue.d.ts +1 -1
- package/dist/types/ts4.7/track/hls/createHlsFactories.d.ts +40 -13
- package/dist/types/ts4.7/track/hls/createHlsManifestProvider.d.ts +6 -0
- package/dist/types/ts4.7/track/hls/createUrlHlsManifestProvider.d.ts +4 -4
- package/dist/types/ts4.7/track/hls/index.d.ts +1 -1
- package/dist/types/ts4.7/track/inferLoadOptionsFromUri.d.ts +15 -0
- package/dist/types/ts4.7/track/mse/MseTrack.d.ts +66 -8
- package/dist/types/ts4.7/track/source/SourceObjectTrack.d.ts +2 -2
- package/dist/types/ts4.7/track/source/SourceTrack.d.ts +2 -2
- package/dist/types/ts4.7/track/source/SourceTrackBase.d.ts +7 -5
- 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/mediaSource.d.ts +5 -0
- package/dist/types/ts4.7/vinyl/ReadonlyPlaybackStreamingState.d.ts +9 -2
- package/dist/types/ts4.7/vinyl/VinylDeps.d.ts +5 -0
- package/dist/types/ts4.7/vinyl/VinylOptions.d.ts +68 -4
- package/dist/types/ts4.7/vinyl/VinylPlayer.d.ts +63 -4
- package/dist/types/ts4.7/vinyl/createVinylFactories.d.ts +55 -12
- 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 +1456 -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 +756 -0
- package/dist/unit/types/current/drm/DrmControllerImpl.test.js +205 -4
- package/dist/unit/types/current/index.d.ts +117 -98
- package/dist/unit/types/current/index.d.ts.map +1 -1
- package/dist/unit/types/current/index.js +117 -98
- package/dist/unit/types/current/playback/PlaybackControllerImpl.test.js +92 -3
- package/dist/unit/types/current/streaming/ContentStream.test.js +11 -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/MediaTimeline.test.js +2 -0
- package/dist/unit/types/current/streaming/MseTrack.test.js +478 -14
- package/dist/unit/types/current/streaming/SegmentControllerImpl.test.js +66 -0
- package/dist/unit/types/current/streaming/StreamingEventMap.test.js +2 -2
- package/dist/unit/types/current/streaming/abr/QualitySelectorImpl.test.js +110 -0
- package/dist/unit/types/current/streaming/buffering/BufferingController.test.js +123 -21
- package/dist/unit/types/current/streaming/buffering/MediaSourceController.test.js +37 -10
- package/dist/unit/types/current/streaming/buffering/SourceBufferController.test.js +4 -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/createDefaultMediaTimelineTransformer.test.js +144 -2
- 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/streaming/mediaTimelineFilter.test.js +9 -0
- package/dist/unit/types/current/streaming/mediaTimelineLanguageFilter.test.js +92 -3
- 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 +587 -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 +336 -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 +199 -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/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 +212 -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 +67 -0
- package/dist/unit/types/current/track/TrackBase.test.js +50 -5
- package/dist/unit/types/current/track/TrackController.test.js +712 -109
- package/dist/unit/types/current/track/TrackFactory.test.js +13 -25
- package/dist/unit/types/current/track/dash/createDashContentStreamFactories.test.js +1 -0
- package/dist/unit/types/current/track/dash/createDashFactories.test.js +63 -2
- package/dist/unit/types/current/track/dash/createSegmentControllerFactory.test.js +1 -0
- package/dist/unit/types/current/track/dash/util/mpd.test.js +24 -1
- package/dist/unit/types/current/track/hls/HlsManifestController.test.js +4 -4
- package/dist/unit/types/current/track/hls/HlsMediaQualityMetadataResolver.test.js +4 -2
- package/dist/unit/types/current/track/hls/buildHlsMediaTimeline.test.js +260 -14
- package/dist/unit/types/current/track/hls/createHlsContentStreamFactories.test.js +1 -0
- package/dist/unit/types/current/track/hls/createHlsFactories.test.js +182 -4
- package/dist/unit/types/current/track/hls/createUrlHlsManifestProvider.test.js +19 -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/track/source/SourceTrack.test.js +5 -0
- package/dist/unit/types/current/track/source/SourceTrackBase.test.js +24 -3
- 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/mediaSource.test.js +6 -0
- package/dist/unit/types/current/vinyl/VinylPlayer.test.js +177 -75
- 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 +263 -0
- package/dist/vinylTestUtil/index.js +2393 -2232
- package/dist/vinylTestUtil/index.js.map +4 -4
- package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.d.ts +14 -0
- package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.js +49 -30
- package/dist/vinylTestUtil/types/current/index.d.ts +53 -51
- package/dist/vinylTestUtil/types/current/index.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/index.js +53 -51
- 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/MockDashManifestController.d.ts +8 -1
- package/dist/vinylTestUtil/types/current/mock/dash/MockDashManifestController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/dash/MockDashManifestController.js +15 -0
- package/dist/vinylTestUtil/types/current/mock/dash/mockDashManifest.d.ts +12 -12
- package/dist/vinylTestUtil/types/current/mock/drm/MockDrmController.d.ts +2 -2
- package/dist/vinylTestUtil/types/current/mock/drm/MockDrmController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/hls/MockHlsManifestController.d.ts +7 -1
- package/dist/vinylTestUtil/types/current/mock/hls/MockHlsManifestController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/hls/MockHlsManifestController.js +12 -0
- package/dist/vinylTestUtil/types/current/mock/hls/mockHlsManifest.d.ts +3 -3
- package/dist/vinylTestUtil/types/current/mock/hls/mockHlsManifest.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/hls/mockHlsManifest.js +1 -0
- package/dist/vinylTestUtil/types/current/mock/player/MockVinylPlayerDependencies.d.ts +8 -0
- package/dist/vinylTestUtil/types/current/mock/player/MockVinylPlayerDependencies.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/player/MockVinylPlayerDependencies.js +17 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/MockContentStream.d.ts +2 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/MockContentStream.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/MockContentStream.js +1 -0
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockBufferingController.d.ts +1 -0
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockBufferingController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockBufferingController.js +1 -0
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockMediaSourceController.d.ts +2 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockMediaSourceController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockMediaSourceController.js +2 -1
- 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/mock/track/MockTrack.d.ts +13 -4
- package/dist/vinylTestUtil/types/current/mock/track/MockTrack.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/track/MockTrack.js +11 -0
- package/dist/vinylTestUtil/types/current/mock/track/MockTrackController.d.ts +3 -2
- package/dist/vinylTestUtil/types/current/mock/track/MockTrackController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/track/MockTrackController.js +2 -1
- package/dist/vinylTestUtil/types/current/mock/track/dash/createMockDashDependencies.d.ts +6 -0
- package/dist/vinylTestUtil/types/current/mock/track/dash/createMockDashDependencies.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/track/dash/createMockDashDependencies.js +8 -1
- package/dist/vinylTestUtil/types/current/mock/track/hls/createMockHlsDependencies.d.ts +4 -0
- package/dist/vinylTestUtil/types/current/mock/track/hls/createMockHlsDependencies.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/track/hls/createMockHlsDependencies.js +6 -1
- package/dist/vinylTestUtil/types/current/player/createVinylSuite.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/player/createVinylSuite.js +18 -7
- package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/eventPromises.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/eventPromises.js +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/expectTrackCanSeek.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/expectTrackCanSeek.js +5 -2
- package/dist/vinylTestUtil/types/current/util/playback/expectTrackPlays.js +3 -3
- package/dist/vinylTestUtil/types/current/util/playback/expectTrackPlaysUntil.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/expectTrackPlaysUntil.js +11 -7
- package/dist/vinylTestUtil/types/ts3.7/assets/vinylTestAssets.d.ts +14 -0
- package/dist/vinylTestUtil/types/ts3.7/index.d.ts +53 -51
- package/dist/vinylTestUtil/types/ts3.7/mock/ad/MockAdController.d.ts +15 -0
- package/dist/vinylTestUtil/types/ts3.7/mock/dash/MockDashManifestController.d.ts +8 -1
- package/dist/vinylTestUtil/types/ts3.7/mock/dash/mockDashManifest.d.ts +12 -12
- package/dist/vinylTestUtil/types/ts3.7/mock/drm/MockDrmController.d.ts +2 -2
- package/dist/vinylTestUtil/types/ts3.7/mock/hls/MockHlsManifestController.d.ts +7 -1
- package/dist/vinylTestUtil/types/ts3.7/mock/hls/mockHlsManifest.d.ts +3 -3
- package/dist/vinylTestUtil/types/ts3.7/mock/player/MockVinylPlayerDependencies.d.ts +8 -0
- package/dist/vinylTestUtil/types/ts3.7/mock/streaming/MockContentStream.d.ts +2 -1
- package/dist/vinylTestUtil/types/ts3.7/mock/streaming/buffering/MockBufferingController.d.ts +1 -0
- package/dist/vinylTestUtil/types/ts3.7/mock/streaming/buffering/MockMediaSourceController.d.ts +2 -1
- package/dist/vinylTestUtil/types/ts3.7/mock/text/MockTextTrackController.d.ts +10 -0
- package/dist/vinylTestUtil/types/ts3.7/mock/track/MockTrack.d.ts +13 -4
- package/dist/vinylTestUtil/types/ts3.7/mock/track/MockTrackController.d.ts +3 -2
- package/dist/vinylTestUtil/types/ts3.7/mock/track/dash/createMockDashDependencies.d.ts +6 -0
- package/dist/vinylTestUtil/types/ts3.7/mock/track/hls/createMockHlsDependencies.d.ts +4 -0
- package/dist/vinylTestUtil/types/ts4.1/assets/vinylTestAssets.d.ts +14 -0
- package/dist/vinylTestUtil/types/ts4.1/index.d.ts +53 -51
- package/dist/vinylTestUtil/types/ts4.1/mock/ad/MockAdController.d.ts +15 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/dash/MockDashManifestController.d.ts +8 -1
- package/dist/vinylTestUtil/types/ts4.1/mock/dash/mockDashManifest.d.ts +12 -12
- package/dist/vinylTestUtil/types/ts4.1/mock/drm/MockDrmController.d.ts +2 -2
- package/dist/vinylTestUtil/types/ts4.1/mock/hls/MockHlsManifestController.d.ts +7 -1
- package/dist/vinylTestUtil/types/ts4.1/mock/hls/mockHlsManifest.d.ts +3 -3
- package/dist/vinylTestUtil/types/ts4.1/mock/player/MockVinylPlayerDependencies.d.ts +8 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/streaming/MockContentStream.d.ts +2 -1
- package/dist/vinylTestUtil/types/ts4.1/mock/streaming/buffering/MockBufferingController.d.ts +1 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/streaming/buffering/MockMediaSourceController.d.ts +2 -1
- package/dist/vinylTestUtil/types/ts4.1/mock/text/MockTextTrackController.d.ts +10 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/track/MockTrack.d.ts +13 -4
- package/dist/vinylTestUtil/types/ts4.1/mock/track/MockTrackController.d.ts +3 -2
- package/dist/vinylTestUtil/types/ts4.1/mock/track/dash/createMockDashDependencies.d.ts +6 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/track/hls/createMockHlsDependencies.d.ts +4 -0
- package/dist/vinylTestUtil/types/ts4.7/assets/vinylTestAssets.d.ts +14 -0
- package/dist/vinylTestUtil/types/ts4.7/index.d.ts +53 -51
- package/dist/vinylTestUtil/types/ts4.7/mock/ad/MockAdController.d.ts +15 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/dash/MockDashManifestController.d.ts +8 -1
- package/dist/vinylTestUtil/types/ts4.7/mock/dash/mockDashManifest.d.ts +12 -12
- package/dist/vinylTestUtil/types/ts4.7/mock/drm/MockDrmController.d.ts +2 -2
- package/dist/vinylTestUtil/types/ts4.7/mock/hls/MockHlsManifestController.d.ts +7 -1
- package/dist/vinylTestUtil/types/ts4.7/mock/hls/mockHlsManifest.d.ts +3 -3
- package/dist/vinylTestUtil/types/ts4.7/mock/player/MockVinylPlayerDependencies.d.ts +8 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/streaming/MockContentStream.d.ts +2 -1
- package/dist/vinylTestUtil/types/ts4.7/mock/streaming/buffering/MockBufferingController.d.ts +1 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/streaming/buffering/MockMediaSourceController.d.ts +2 -1
- package/dist/vinylTestUtil/types/ts4.7/mock/text/MockTextTrackController.d.ts +10 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/track/MockTrack.d.ts +13 -4
- package/dist/vinylTestUtil/types/ts4.7/mock/track/MockTrackController.d.ts +3 -2
- package/dist/vinylTestUtil/types/ts4.7/mock/track/dash/createMockDashDependencies.d.ts +6 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/track/hls/createMockHlsDependencies.d.ts +4 -0
- package/docs/ADS.md +264 -0
- package/docs/USAGE.md +142 -9
- package/package.json +16 -16
- package/dist/types/current/track/hls/HlsManifestProvider.d.ts.map +0 -1
- package/dist/unit/types/current/track/dash/DashManifestController.test.d.ts +0 -2
- package/dist/unit/types/current/track/dash/DashManifestController.test.d.ts.map +0 -1
- package/dist/unit/types/current/track/dash/DashManifestController.test.js +0 -186
- /package/dist/types/current/{track/hls/HlsManifestProvider.js → ad/AdBreakInfo.js} +0 -0
|
@@ -0,0 +1,1456 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { AdControllerImpl } from '@amazon/vinyl';
|
|
6
|
+
import { MockPlaybackController, MockTrack } from '@amazon/vinyl/vinylTestUtil';
|
|
7
|
+
describe('AdControllerImpl', () => {
|
|
8
|
+
let playbackController;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
playbackController = new MockPlaybackController();
|
|
11
|
+
});
|
|
12
|
+
function createController() {
|
|
13
|
+
return new AdControllerImpl({ playbackController });
|
|
14
|
+
}
|
|
15
|
+
function updateTime(time) {
|
|
16
|
+
playbackController.currentTime = time;
|
|
17
|
+
playbackController.dispatch('timeUpdate', {
|
|
18
|
+
previous: 0,
|
|
19
|
+
current: time,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Simulates a user seek: moves the playhead and fires `seeked`. */
|
|
23
|
+
function seekTo(time) {
|
|
24
|
+
playbackController.currentTime = time;
|
|
25
|
+
playbackController.dispatch('seeked', {
|
|
26
|
+
started: 0,
|
|
27
|
+
ended: 0,
|
|
28
|
+
duration: 0,
|
|
29
|
+
reason: 'seeked',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const defaultAd = {
|
|
33
|
+
id: 'a1',
|
|
34
|
+
startTime: 10,
|
|
35
|
+
duration: 5,
|
|
36
|
+
uri: 'ad.m3u8',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Builds an ad break. `ads` accepts a plain array (wrapped in an immediate
|
|
40
|
+
* resolver) or a resolver function for async cases.
|
|
41
|
+
*/
|
|
42
|
+
function makeBreak(overrides = {}) {
|
|
43
|
+
const { ads, ...rest } = overrides;
|
|
44
|
+
const adsResolver = typeof ads === 'function'
|
|
45
|
+
? ads
|
|
46
|
+
: () => Promise.resolve(ads ?? [defaultAd]);
|
|
47
|
+
return {
|
|
48
|
+
id: 'b1',
|
|
49
|
+
startTime: 10,
|
|
50
|
+
duration: 5,
|
|
51
|
+
placement: 'midroll',
|
|
52
|
+
restrict: {},
|
|
53
|
+
once: false,
|
|
54
|
+
resumeOffset: null,
|
|
55
|
+
playoutLimit: null,
|
|
56
|
+
resolutionTimeOffset: null,
|
|
57
|
+
skipControl: () => null,
|
|
58
|
+
ads: adsResolver,
|
|
59
|
+
...rest,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** Wraps ad breaks in the TrackAds shape a track's getAds() resolves. */
|
|
63
|
+
function trackAds(...adBreaks) {
|
|
64
|
+
return { trackUri: 't1', adBreaks };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Drains the pending microtask queue so multi-hop ad resolution settles
|
|
68
|
+
* (setAdsProvider → getAds → setTrackAds, then nextAdOrBreak → nextAd →
|
|
69
|
+
* getAds → startAd, and any follow-on break/ad advance). Uses microtask
|
|
70
|
+
* rounds rather than a macrotask so it never fires the ad-load-timeout
|
|
71
|
+
* timer.
|
|
72
|
+
*/
|
|
73
|
+
async function flush() {
|
|
74
|
+
for (let i = 0; i < 12; i++)
|
|
75
|
+
await Promise.resolve();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Activates a content parent track whose `getAds()` resolves the given ads
|
|
79
|
+
* and marks playback as playing — the state `onTimeUpdate` requires before
|
|
80
|
+
* it will scan for midroll ingress. Returns the mock track so a test can
|
|
81
|
+
* dispatch `adsChange` (via {@link refreshAds}) for a live manifest update.
|
|
82
|
+
*/
|
|
83
|
+
async function setContent(c, ads) {
|
|
84
|
+
const track = new MockTrack();
|
|
85
|
+
track.uri = ads.trackUri;
|
|
86
|
+
track.active = true;
|
|
87
|
+
track.ads = ads;
|
|
88
|
+
playbackController.playing = true;
|
|
89
|
+
c.setAdsProvider(track);
|
|
90
|
+
await flush(); // let setAdsProvider's getAds() resolve → setTrackAds
|
|
91
|
+
return track;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Simulates a live manifest refresh on the SAME content track: swaps the
|
|
95
|
+
* ads and fires `adsChange`, which the controller re-reads via getAds().
|
|
96
|
+
* Unlike a new {@link setContent}, this preserves completed/spent break
|
|
97
|
+
* suppression (no content change).
|
|
98
|
+
*/
|
|
99
|
+
async function refreshAds(track, ads) {
|
|
100
|
+
track.ads = ads;
|
|
101
|
+
track.dispatch('adsChange', {});
|
|
102
|
+
await flush();
|
|
103
|
+
}
|
|
104
|
+
it('starts with no ads and no active break', () => {
|
|
105
|
+
const c = createController();
|
|
106
|
+
expect(c.currentTrackAds).toBeNull();
|
|
107
|
+
expect(c.currentAdBreak).toBeNull();
|
|
108
|
+
expect(c.currentAd).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
describe('adPreload', () => {
|
|
111
|
+
// Default preloadAheadTime is 10s.
|
|
112
|
+
it('emits adPreload as the playhead approaches a midroll', async () => {
|
|
113
|
+
const c = createController();
|
|
114
|
+
await setContent(c, trackAds(makeBreak({ id: 'm', startTime: 30, duration: 5 })));
|
|
115
|
+
const preloaded = [];
|
|
116
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
117
|
+
updateTime(19); // outside the 10s window [20, 30)
|
|
118
|
+
await flush();
|
|
119
|
+
expect(preloaded).toEqual([]);
|
|
120
|
+
updateTime(21); // inside the window
|
|
121
|
+
await flush();
|
|
122
|
+
expect(preloaded).toEqual(['m']);
|
|
123
|
+
});
|
|
124
|
+
it('emits adPreload only once while approaching', async () => {
|
|
125
|
+
const c = createController();
|
|
126
|
+
await setContent(c, trackAds(makeBreak({ id: 'm', startTime: 30, duration: 5 })));
|
|
127
|
+
const preloaded = [];
|
|
128
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
129
|
+
updateTime(21);
|
|
130
|
+
updateTime(22);
|
|
131
|
+
updateTime(23);
|
|
132
|
+
await flush();
|
|
133
|
+
expect(preloaded).toEqual(['m']);
|
|
134
|
+
});
|
|
135
|
+
it('does not emit adPreload for a preroll', async () => {
|
|
136
|
+
const c = createController();
|
|
137
|
+
await setContent(c, trackAds(makeBreak({ id: 'pre', startTime: 0, placement: 'preroll' })));
|
|
138
|
+
const preloaded = [];
|
|
139
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
140
|
+
updateTime(1);
|
|
141
|
+
await flush();
|
|
142
|
+
expect(preloaded).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
it('emits adPreload as the playhead approaches a postroll', async () => {
|
|
145
|
+
const c = createController();
|
|
146
|
+
await setContent(c, trackAds(makeBreak({
|
|
147
|
+
id: 'post',
|
|
148
|
+
startTime: 60,
|
|
149
|
+
duration: 5,
|
|
150
|
+
placement: 'postroll',
|
|
151
|
+
})));
|
|
152
|
+
const preloaded = [];
|
|
153
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
154
|
+
updateTime(49); // outside [50, 60)
|
|
155
|
+
await flush();
|
|
156
|
+
expect(preloaded).toEqual([]);
|
|
157
|
+
updateTime(51);
|
|
158
|
+
await flush();
|
|
159
|
+
expect(preloaded).toEqual(['post']);
|
|
160
|
+
});
|
|
161
|
+
it("uses the break's resolutionTimeOffset over the preloadAheadTime option", async () => {
|
|
162
|
+
const c = createController();
|
|
163
|
+
await setContent(c, trackAds(makeBreak({
|
|
164
|
+
id: 'm',
|
|
165
|
+
startTime: 30,
|
|
166
|
+
duration: 5,
|
|
167
|
+
resolutionTimeOffset: 5,
|
|
168
|
+
})));
|
|
169
|
+
const preloaded = [];
|
|
170
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
171
|
+
updateTime(21); // inside the default 10s window but outside the 5s offset
|
|
172
|
+
await flush();
|
|
173
|
+
expect(preloaded).toEqual([]);
|
|
174
|
+
updateTime(26); // inside the 5s window [25, 30)
|
|
175
|
+
await flush();
|
|
176
|
+
expect(preloaded).toEqual(['m']);
|
|
177
|
+
});
|
|
178
|
+
it('honors a custom preloadAheadTime option', async () => {
|
|
179
|
+
const c = new AdControllerImpl({ playbackController }, { preloadAheadTime: 3 });
|
|
180
|
+
await setContent(c, trackAds(makeBreak({ id: 'm', startTime: 30, duration: 5 })));
|
|
181
|
+
const preloaded = [];
|
|
182
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
183
|
+
updateTime(26); // outside the 3s window [27, 30)
|
|
184
|
+
await flush();
|
|
185
|
+
expect(preloaded).toEqual([]);
|
|
186
|
+
updateTime(28);
|
|
187
|
+
await flush();
|
|
188
|
+
expect(preloaded).toEqual(['m']);
|
|
189
|
+
});
|
|
190
|
+
it('does not emit adPreload for a played-once (suppressed) break', async () => {
|
|
191
|
+
const c = createController();
|
|
192
|
+
await setContent(c, trackAds(makeBreak({
|
|
193
|
+
id: 'm',
|
|
194
|
+
startTime: 30,
|
|
195
|
+
duration: 5,
|
|
196
|
+
once: true,
|
|
197
|
+
})));
|
|
198
|
+
updateTime(30); // enter the break
|
|
199
|
+
await flush();
|
|
200
|
+
c.skipAd(); // -> completed, suppressed for the presentation
|
|
201
|
+
await flush();
|
|
202
|
+
const preloaded = [];
|
|
203
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
204
|
+
seekTo(15); // seek back and approach again
|
|
205
|
+
updateTime(22);
|
|
206
|
+
await flush();
|
|
207
|
+
expect(preloaded).toEqual([]);
|
|
208
|
+
});
|
|
209
|
+
it('re-arms the preload signal for a replayable break after a seek back', async () => {
|
|
210
|
+
const c = createController();
|
|
211
|
+
await setContent(c, trackAds(makeBreak({ id: 'm', startTime: 30, duration: 5 })));
|
|
212
|
+
const preloaded = [];
|
|
213
|
+
c.on('adPreload', (e) => preloaded.push(e.adBreak.id));
|
|
214
|
+
updateTime(22); // approach -> preload #1
|
|
215
|
+
await flush();
|
|
216
|
+
expect(preloaded).toEqual(['m']);
|
|
217
|
+
seekTo(10); // seek back re-arms the preload signal
|
|
218
|
+
updateTime(23); // approach again -> preload #2
|
|
219
|
+
await flush();
|
|
220
|
+
expect(preloaded).toEqual(['m', 'm']);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe('ad discovery failures', () => {
|
|
224
|
+
/** A parent track whose getAds() rejects (e.g. a manifest fetch error). */
|
|
225
|
+
function rejectingTrack() {
|
|
226
|
+
const track = new MockTrack();
|
|
227
|
+
track.uri = 't1';
|
|
228
|
+
track.active = true;
|
|
229
|
+
track.getAds = () => Promise.reject(new Error('discovery failed'));
|
|
230
|
+
return track;
|
|
231
|
+
}
|
|
232
|
+
it('logs and does not throw when parent-track ad discovery rejects', async () => {
|
|
233
|
+
const c = createController();
|
|
234
|
+
c.setAdsProvider(rejectingTrack());
|
|
235
|
+
await flush();
|
|
236
|
+
// The failure is swallowed (logged) — no ads, no active break, no throw.
|
|
237
|
+
expect(c.currentTrackAds).toBeNull();
|
|
238
|
+
expect(c.currentAdBreak).toBeNull();
|
|
239
|
+
});
|
|
240
|
+
it('ignores a rejected discovery after the parent track has changed', async () => {
|
|
241
|
+
const c = createController();
|
|
242
|
+
c.setAdsProvider(rejectingTrack());
|
|
243
|
+
c.setAdsProvider(null); // interrupts the in-flight discovery
|
|
244
|
+
await flush();
|
|
245
|
+
expect(c.currentTrackAds).toBeNull();
|
|
246
|
+
});
|
|
247
|
+
it('resolves enterPreroll to null when ad discovery rejects', async () => {
|
|
248
|
+
const c = createController();
|
|
249
|
+
c.setAdsProvider(rejectingTrack());
|
|
250
|
+
await flush();
|
|
251
|
+
expect(await c.enterPreroll()).toBeNull();
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
describe('defensive no-ops and disposal', () => {
|
|
255
|
+
it('is a no-op when the same parent track is set again', async () => {
|
|
256
|
+
const c = createController();
|
|
257
|
+
const track = new MockTrack();
|
|
258
|
+
track.uri = 't1';
|
|
259
|
+
track.active = true;
|
|
260
|
+
track.ads = trackAds(makeBreak());
|
|
261
|
+
c.setAdsProvider(track);
|
|
262
|
+
await flush();
|
|
263
|
+
let changes = 0;
|
|
264
|
+
c.on('currentTrackAdsChange', () => changes++);
|
|
265
|
+
c.setAdsProvider(track); // same provider → early return, no re-read
|
|
266
|
+
await flush();
|
|
267
|
+
expect(changes).toBe(0);
|
|
268
|
+
});
|
|
269
|
+
it('resolves enterPreroll and enterPostroll to null with no parent track', async () => {
|
|
270
|
+
const c = createController();
|
|
271
|
+
expect(await c.enterPreroll()).toBeNull();
|
|
272
|
+
expect(await c.enterPostroll()).toBeNull();
|
|
273
|
+
});
|
|
274
|
+
it('does not scan midrolls when there is no active content track', async () => {
|
|
275
|
+
const c = createController();
|
|
276
|
+
playbackController.playing = true;
|
|
277
|
+
updateTime(15); // contentTrackActive is false (no parent track)
|
|
278
|
+
await flush();
|
|
279
|
+
expect(c.currentAdBreak).toBeNull();
|
|
280
|
+
});
|
|
281
|
+
it('ignores a late ad-list rejection after dispose', async () => {
|
|
282
|
+
const c = createController();
|
|
283
|
+
let rejectAds = () => { };
|
|
284
|
+
const track = new MockTrack();
|
|
285
|
+
track.uri = 't1';
|
|
286
|
+
track.active = true;
|
|
287
|
+
track.ads = trackAds(makeBreak({
|
|
288
|
+
startTime: 0,
|
|
289
|
+
duration: 5,
|
|
290
|
+
ads: () => new Promise((_res, rej) => {
|
|
291
|
+
rejectAds = rej;
|
|
292
|
+
}),
|
|
293
|
+
}));
|
|
294
|
+
c.setAdsProvider(track);
|
|
295
|
+
playbackController.playing = true;
|
|
296
|
+
await flush(); // getAds resolves → midroll registered
|
|
297
|
+
updateTime(1); // enters the break; nextAd awaits the ad list
|
|
298
|
+
await flush();
|
|
299
|
+
c.dispose();
|
|
300
|
+
rejectAds(new Error('late')); // nextAd catch: `if (this.disposed) return`
|
|
301
|
+
await flush();
|
|
302
|
+
expect(c.currentAdBreak).toBeNull();
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
it('emits currentTrackAdsChange with the TrackAds when a parent track is set', async () => {
|
|
306
|
+
const c = createController();
|
|
307
|
+
const events = [];
|
|
308
|
+
c.on('currentTrackAdsChange', (e) => events.push(e.current));
|
|
309
|
+
await setContent(c, trackAds(makeBreak()));
|
|
310
|
+
expect(events.at(-1)?.adBreaks.map((b) => b.id)).toEqual(['b1']);
|
|
311
|
+
expect(c.currentTrackAds?.adBreaks.map((b) => b.id)).toEqual(['b1']);
|
|
312
|
+
});
|
|
313
|
+
it('clears the ads when the parent track is cleared', async () => {
|
|
314
|
+
const c = createController();
|
|
315
|
+
await setContent(c, trackAds(makeBreak()));
|
|
316
|
+
c.setAdsProvider(null);
|
|
317
|
+
expect(c.currentTrackAds).toBeNull();
|
|
318
|
+
});
|
|
319
|
+
describe('midroll entry', () => {
|
|
320
|
+
it('activates a break when the playhead crosses its start time', async () => {
|
|
321
|
+
const c = createController();
|
|
322
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 5 })));
|
|
323
|
+
const entered = [];
|
|
324
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
325
|
+
updateTime(9);
|
|
326
|
+
await flush();
|
|
327
|
+
expect(entered).toEqual([]);
|
|
328
|
+
expect(c.currentAdBreak).toBeNull();
|
|
329
|
+
updateTime(10);
|
|
330
|
+
await flush();
|
|
331
|
+
expect(entered).toEqual(['b1']);
|
|
332
|
+
expect(c.currentAdBreak?.id).toBe('b1');
|
|
333
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
334
|
+
});
|
|
335
|
+
it('does not re-emit while remaining inside the same break', async () => {
|
|
336
|
+
const c = createController();
|
|
337
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 10 })));
|
|
338
|
+
let changes = 0;
|
|
339
|
+
c.on('adBreakEntered', () => changes++);
|
|
340
|
+
updateTime(11);
|
|
341
|
+
await flush();
|
|
342
|
+
updateTime(12);
|
|
343
|
+
updateTime(13);
|
|
344
|
+
await flush();
|
|
345
|
+
expect(changes).toBe(1);
|
|
346
|
+
});
|
|
347
|
+
it('treats a null-duration break as open-ended (still enters)', async () => {
|
|
348
|
+
const c = createController();
|
|
349
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: null })));
|
|
350
|
+
updateTime(11);
|
|
351
|
+
await flush();
|
|
352
|
+
expect(c.currentAdBreak?.id).toBe('b1');
|
|
353
|
+
});
|
|
354
|
+
it('enters tied breaks that share a start time', async () => {
|
|
355
|
+
// Regression: checkMidrollIngress previously examined only the
|
|
356
|
+
// single nearest preceding break, silently dropping a break tied on
|
|
357
|
+
// startTime.
|
|
358
|
+
const c = createController();
|
|
359
|
+
const entered = [];
|
|
360
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
361
|
+
await setContent(c, trackAds(makeBreak({ id: 'A', startTime: 10, duration: 5 }), makeBreak({ id: 'B', startTime: 10, duration: 5 })));
|
|
362
|
+
updateTime(10);
|
|
363
|
+
await flush();
|
|
364
|
+
// Both tied breaks enter (A first, then B after A completes).
|
|
365
|
+
expect(entered).toContain('A');
|
|
366
|
+
c.skipAdBreak();
|
|
367
|
+
await flush();
|
|
368
|
+
expect(entered).toEqual(['A', 'B']);
|
|
369
|
+
});
|
|
370
|
+
it('enters an earlier still-open break after seeking into it', async () => {
|
|
371
|
+
// Regression: a long break started earlier that contains the
|
|
372
|
+
// playhead must be entered even when a shorter later break was
|
|
373
|
+
// already passed by the seek.
|
|
374
|
+
const c = createController();
|
|
375
|
+
const entered = [];
|
|
376
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
377
|
+
await setContent(c, trackAds(makeBreak({ id: 'long', startTime: 10, duration: 100 }), makeBreak({ id: 'short', startTime: 50, duration: 5 })));
|
|
378
|
+
// Seek to 60: inside `long` (10–110); `short` (50–55) already ended.
|
|
379
|
+
updateTime(60);
|
|
380
|
+
await flush();
|
|
381
|
+
expect(entered).toEqual(['long']);
|
|
382
|
+
expect(c.currentAdBreak?.id).toBe('long');
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
describe('preroll', () => {
|
|
386
|
+
it('enters when enterPreroll is called', async () => {
|
|
387
|
+
const c = createController();
|
|
388
|
+
await setContent(c, trackAds(makeBreak({ id: 'pre', startTime: 0, placement: 'preroll' })));
|
|
389
|
+
// Prerolls are not entered by setting ads or crossing a range; the
|
|
390
|
+
// track controller enters them via enterPreroll.
|
|
391
|
+
expect(c.currentAdBreak).toBeNull();
|
|
392
|
+
await c.enterPreroll();
|
|
393
|
+
expect(c.currentAdBreak?.id).toBe('pre');
|
|
394
|
+
await flush();
|
|
395
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
describe('skipAd', () => {
|
|
399
|
+
function multiAdBreak() {
|
|
400
|
+
return makeBreak({
|
|
401
|
+
id: 'b1',
|
|
402
|
+
startTime: 0,
|
|
403
|
+
duration: 30,
|
|
404
|
+
ads: [
|
|
405
|
+
{ id: 'a1', startTime: 0, duration: 10, uri: 'ad1.m3u8' },
|
|
406
|
+
{ id: 'a2', startTime: 10, duration: 20, uri: 'ad2.m3u8' },
|
|
407
|
+
],
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
it('advances to the next ad within a multi-ad break', async () => {
|
|
411
|
+
const c = createController();
|
|
412
|
+
await setContent(c, trackAds(multiAdBreak()));
|
|
413
|
+
updateTime(1);
|
|
414
|
+
await flush();
|
|
415
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
416
|
+
c.skipAd();
|
|
417
|
+
await flush();
|
|
418
|
+
expect(c.currentAd?.id).toBe('a2');
|
|
419
|
+
expect(c.currentAdBreak?.id).toBe('b1');
|
|
420
|
+
});
|
|
421
|
+
it('ends the break after skipping the last ad', async () => {
|
|
422
|
+
const c = createController();
|
|
423
|
+
await setContent(c, trackAds(multiAdBreak()));
|
|
424
|
+
updateTime(1);
|
|
425
|
+
await flush();
|
|
426
|
+
c.skipAd(); // to a2
|
|
427
|
+
await flush();
|
|
428
|
+
const changes = [];
|
|
429
|
+
c.on('adBreakEntered', (e) => changes.push(e.adBreak.id));
|
|
430
|
+
c.on('adBreakCompleted', () => changes.push(null));
|
|
431
|
+
c.skipAd(); // past last -> break ends
|
|
432
|
+
await flush();
|
|
433
|
+
expect(changes).toEqual([null]);
|
|
434
|
+
expect(c.currentAdBreak).toBeNull();
|
|
435
|
+
});
|
|
436
|
+
it('is a no-op when no ad is active', async () => {
|
|
437
|
+
const c = createController();
|
|
438
|
+
await setContent(c, trackAds(makeBreak()));
|
|
439
|
+
const spy = jasmine.createSpy('adBreakChange');
|
|
440
|
+
c.on('adBreakEntered', spy);
|
|
441
|
+
c.on('adBreakCompleted', spy);
|
|
442
|
+
c.skipAd();
|
|
443
|
+
expect(spy).not.toHaveBeenCalled();
|
|
444
|
+
});
|
|
445
|
+
it('prevents re-entry into a skipped single-ad break', async () => {
|
|
446
|
+
const c = createController();
|
|
447
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
448
|
+
updateTime(5);
|
|
449
|
+
await flush();
|
|
450
|
+
c.skipAd();
|
|
451
|
+
const spy = jasmine.createSpy('adBreakChange');
|
|
452
|
+
c.on('adBreakEntered', spy);
|
|
453
|
+
c.on('adBreakCompleted', spy);
|
|
454
|
+
updateTime(7);
|
|
455
|
+
await flush();
|
|
456
|
+
expect(spy).not.toHaveBeenCalled();
|
|
457
|
+
expect(c.currentAdBreak).toBeNull();
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
describe('skipAdBreak', () => {
|
|
461
|
+
it('is a no-op when no break is active', () => {
|
|
462
|
+
const c = createController();
|
|
463
|
+
const spy = jasmine.createSpy('adBreakChange');
|
|
464
|
+
c.on('adBreakEntered', spy);
|
|
465
|
+
c.on('adBreakCompleted', spy);
|
|
466
|
+
c.skipAdBreak();
|
|
467
|
+
expect(spy).not.toHaveBeenCalled();
|
|
468
|
+
});
|
|
469
|
+
it('skips the entire active break', async () => {
|
|
470
|
+
const c = createController();
|
|
471
|
+
await setContent(c, trackAds(makeBreak({
|
|
472
|
+
startTime: 0,
|
|
473
|
+
duration: 30,
|
|
474
|
+
ads: [
|
|
475
|
+
{
|
|
476
|
+
id: 'a1',
|
|
477
|
+
startTime: 0,
|
|
478
|
+
duration: 10,
|
|
479
|
+
uri: 'ad1.m3u8',
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
id: 'a2',
|
|
483
|
+
startTime: 10,
|
|
484
|
+
duration: 20,
|
|
485
|
+
uri: 'ad2.m3u8',
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
})));
|
|
489
|
+
updateTime(1);
|
|
490
|
+
await flush();
|
|
491
|
+
const events = [];
|
|
492
|
+
c.on('adBreakEntered', (e) => events.push(e.adBreak.id));
|
|
493
|
+
c.on('adBreakCompleted', () => events.push(null));
|
|
494
|
+
c.skipAdBreak();
|
|
495
|
+
await flush();
|
|
496
|
+
expect(events).toEqual([null]);
|
|
497
|
+
expect(c.currentAdBreak).toBeNull();
|
|
498
|
+
// Not re-entered by a later time update within its range.
|
|
499
|
+
updateTime(5);
|
|
500
|
+
await flush();
|
|
501
|
+
expect(c.currentAdBreak).toBeNull();
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
describe('a break with no ads', () => {
|
|
505
|
+
it('completes immediately with a resume position and plays no ad', async () => {
|
|
506
|
+
const c = createController();
|
|
507
|
+
const entered = [];
|
|
508
|
+
const completed = [];
|
|
509
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
510
|
+
c.on('adBreakCompleted', (e) => completed.push({
|
|
511
|
+
id: e.adBreak.id,
|
|
512
|
+
resumePosition: e.resumePosition,
|
|
513
|
+
}));
|
|
514
|
+
await setContent(c, trackAds(makeBreak({
|
|
515
|
+
id: 'mid',
|
|
516
|
+
startTime: 10,
|
|
517
|
+
duration: 5,
|
|
518
|
+
resumeOffset: 2,
|
|
519
|
+
ads: [],
|
|
520
|
+
})));
|
|
521
|
+
updateTime(10);
|
|
522
|
+
await flush();
|
|
523
|
+
// The break was entered and immediately completed (resume =
|
|
524
|
+
// startTime + offset), and no ad ever played.
|
|
525
|
+
expect(entered).toEqual(['mid']);
|
|
526
|
+
expect(completed).toEqual([{ id: 'mid', resumePosition: 12 }]);
|
|
527
|
+
expect(c.currentAd).toBeNull();
|
|
528
|
+
expect(c.currentAdBreak).toBeNull();
|
|
529
|
+
});
|
|
530
|
+
it('advances to the next break rather than trapping the playhead', async () => {
|
|
531
|
+
const c = createController();
|
|
532
|
+
const entered = [];
|
|
533
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
534
|
+
await setContent(c, trackAds(makeBreak({ id: 'pre1', placement: 'preroll', ads: [] }), makeBreak({ id: 'pre2', placement: 'preroll' })));
|
|
535
|
+
await c.enterPreroll();
|
|
536
|
+
await flush();
|
|
537
|
+
// The empty first preroll completes and the second is entered.
|
|
538
|
+
expect(entered).toEqual(['pre1', 'pre2']);
|
|
539
|
+
expect(c.currentAdBreak?.id).toBe('pre2');
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
describe('clearCompletedAds', () => {
|
|
543
|
+
it('allows a previously skipped break to be entered again', async () => {
|
|
544
|
+
const c = createController();
|
|
545
|
+
const track = await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
546
|
+
updateTime(5);
|
|
547
|
+
await flush();
|
|
548
|
+
c.skipAd();
|
|
549
|
+
expect(c.currentAdBreak).toBeNull();
|
|
550
|
+
// A content change clears completed-ad suppression. Re-set the same
|
|
551
|
+
// ads on a fresh content track so the break can be entered again.
|
|
552
|
+
await setContent(c, track.ads);
|
|
553
|
+
updateTime(6);
|
|
554
|
+
await flush();
|
|
555
|
+
expect(c.currentAdBreak?.id).toBe('b1');
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
describe('empty / failing ad lists', () => {
|
|
559
|
+
it('completes a break whose resolver returns no ads', async () => {
|
|
560
|
+
const c = createController();
|
|
561
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10, ads: [] })));
|
|
562
|
+
updateTime(5);
|
|
563
|
+
await flush();
|
|
564
|
+
expect(c.currentAdBreak).toBeNull();
|
|
565
|
+
updateTime(7);
|
|
566
|
+
await flush();
|
|
567
|
+
expect(c.currentAdBreak).toBeNull();
|
|
568
|
+
});
|
|
569
|
+
it('emits adError and clears the break when the resolver rejects', async () => {
|
|
570
|
+
const c = createController();
|
|
571
|
+
const errors = [];
|
|
572
|
+
const completedReasons = [];
|
|
573
|
+
c.on('adError', (e) => errors.push(e.error));
|
|
574
|
+
c.on('adBreakCompleted', (e) => completedReasons.push(e.reason));
|
|
575
|
+
await setContent(c, trackAds(makeBreak({
|
|
576
|
+
startTime: 0,
|
|
577
|
+
duration: 10,
|
|
578
|
+
ads: () => Promise.reject(new Error('boom')),
|
|
579
|
+
})));
|
|
580
|
+
updateTime(5);
|
|
581
|
+
await flush();
|
|
582
|
+
expect(errors.length).toBe(1);
|
|
583
|
+
// The break must complete (reason 'error') so content can resume —
|
|
584
|
+
// it must not stall non-null.
|
|
585
|
+
expect(completedReasons).toEqual(['error']);
|
|
586
|
+
expect(c.currentAdBreak).toBeNull();
|
|
587
|
+
});
|
|
588
|
+
it('recovers: a later midroll still enters after an ad-list rejection', async () => {
|
|
589
|
+
// Regression for the session-fatal hang — a transient ad-list fetch
|
|
590
|
+
// failure previously left currentAdBreak stuck non-null, killing all
|
|
591
|
+
// subsequent midroll ingress.
|
|
592
|
+
const c = createController();
|
|
593
|
+
const entered = [];
|
|
594
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
595
|
+
await setContent(c, trackAds(makeBreak({
|
|
596
|
+
id: 'bad',
|
|
597
|
+
startTime: 10,
|
|
598
|
+
duration: 5,
|
|
599
|
+
ads: () => Promise.reject(new Error('fetch failed')),
|
|
600
|
+
}), makeBreak({ id: 'good', startTime: 20, duration: 5 })));
|
|
601
|
+
updateTime(10);
|
|
602
|
+
await flush();
|
|
603
|
+
expect(c.currentAdBreak).toBeNull(); // 'bad' failed and completed
|
|
604
|
+
updateTime(20);
|
|
605
|
+
await flush();
|
|
606
|
+
expect(c.currentAdBreak?.id).toBe('good');
|
|
607
|
+
expect(entered).toEqual(['bad', 'good']);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
describe('failAd', () => {
|
|
611
|
+
it('emits adError carrying the failing ad and completes it', async () => {
|
|
612
|
+
const c = createController();
|
|
613
|
+
const errorEvents = [];
|
|
614
|
+
c.on('adError', (e) => errorEvents.push({
|
|
615
|
+
adId: e.currentAd?.id ?? null,
|
|
616
|
+
breakId: e.adBreak.id,
|
|
617
|
+
}));
|
|
618
|
+
const completed = [];
|
|
619
|
+
c.on('adCompleted', (e) => completed.push(e.reason));
|
|
620
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
621
|
+
updateTime(5);
|
|
622
|
+
await flush();
|
|
623
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
624
|
+
c.failAd(new Error('decode failed'));
|
|
625
|
+
expect(errorEvents).toEqual([{ adId: 'a1', breakId: 'b1' }]);
|
|
626
|
+
expect(completed).toEqual(['error']);
|
|
627
|
+
expect(c.currentAdBreak).toBeNull();
|
|
628
|
+
});
|
|
629
|
+
it('is a no-op when no break is active', () => {
|
|
630
|
+
const c = createController();
|
|
631
|
+
const spy = jasmine.createSpy('adError');
|
|
632
|
+
c.on('adError', spy);
|
|
633
|
+
c.failAd(new Error('boom'));
|
|
634
|
+
expect(spy).not.toHaveBeenCalled();
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
describe('enterPostroll', () => {
|
|
638
|
+
it('activates a pending postroll break', async () => {
|
|
639
|
+
const c = createController();
|
|
640
|
+
await setContent(c, trackAds(makeBreak({
|
|
641
|
+
id: 'post',
|
|
642
|
+
startTime: 60,
|
|
643
|
+
duration: 10,
|
|
644
|
+
placement: 'postroll',
|
|
645
|
+
})));
|
|
646
|
+
// A postroll is not entered by the playhead crossing its range.
|
|
647
|
+
updateTime(60);
|
|
648
|
+
await flush();
|
|
649
|
+
expect(c.currentAdBreak).toBeNull();
|
|
650
|
+
await c.enterPostroll();
|
|
651
|
+
expect(c.currentAdBreak?.id).toBe('post');
|
|
652
|
+
await flush();
|
|
653
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
describe('playback progress events', () => {
|
|
657
|
+
it('dispatches adEntered and adPlaying', async () => {
|
|
658
|
+
const c = createController();
|
|
659
|
+
const entered = [];
|
|
660
|
+
const playing = [];
|
|
661
|
+
c.on('adEntered', (e) => entered.push(e.ad.id));
|
|
662
|
+
c.on('adPlaying', (e) => playing.push(e.ad.id));
|
|
663
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
664
|
+
updateTime(1);
|
|
665
|
+
await flush();
|
|
666
|
+
expect(entered).toEqual(['a1']);
|
|
667
|
+
playbackController.dispatch('playing', {});
|
|
668
|
+
expect(playing).toEqual(['a1']);
|
|
669
|
+
});
|
|
670
|
+
it('dispatches adEnded when playback ends', async () => {
|
|
671
|
+
const c = createController();
|
|
672
|
+
const ended = [];
|
|
673
|
+
c.on('adEnded', (e) => ended.push(e.ad.id));
|
|
674
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
675
|
+
updateTime(1);
|
|
676
|
+
await flush();
|
|
677
|
+
playbackController.dispatch('ended', {
|
|
678
|
+
previous: false,
|
|
679
|
+
current: true,
|
|
680
|
+
});
|
|
681
|
+
expect(ended).toEqual(['a1']);
|
|
682
|
+
expect(c.currentAdBreak).toBeNull();
|
|
683
|
+
});
|
|
684
|
+
it('dispatches adFirstQuartile after 25% of the ad has played', async () => {
|
|
685
|
+
const c = createController();
|
|
686
|
+
const quartiles = [];
|
|
687
|
+
c.on('adFirstQuartile', (e) => quartiles.push(e.ad.id));
|
|
688
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
689
|
+
updateTime(0);
|
|
690
|
+
await flush();
|
|
691
|
+
// Ad begins playing at time 0.
|
|
692
|
+
playbackController.dispatch('playing', {});
|
|
693
|
+
playbackController.duration = 10;
|
|
694
|
+
playbackController.playbackRate = 1;
|
|
695
|
+
playbackController.currentTimePercent = 0.3;
|
|
696
|
+
updateTime(3);
|
|
697
|
+
await flush();
|
|
698
|
+
expect(quartiles).toEqual(['a1']);
|
|
699
|
+
});
|
|
700
|
+
it('dispatches adMidpoint after 50% of the ad has played', async () => {
|
|
701
|
+
const c = createController();
|
|
702
|
+
const midpoints = [];
|
|
703
|
+
c.on('adMidpoint', (e) => midpoints.push(e.playbackRateAvg));
|
|
704
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
705
|
+
updateTime(0);
|
|
706
|
+
await flush();
|
|
707
|
+
playbackController.dispatch('playing', {});
|
|
708
|
+
playbackController.duration = 10;
|
|
709
|
+
playbackController.playbackRate = 1;
|
|
710
|
+
playbackController.currentTimePercent = 0.6;
|
|
711
|
+
updateTime(6);
|
|
712
|
+
await flush();
|
|
713
|
+
expect(midpoints.length).toBe(1);
|
|
714
|
+
});
|
|
715
|
+
it('dispatches adThirdQuartile after 75% of the ad has played', async () => {
|
|
716
|
+
const c = createController();
|
|
717
|
+
const thirds = [];
|
|
718
|
+
c.on('adThirdQuartile', (e) => thirds.push(e.ad.id));
|
|
719
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
720
|
+
updateTime(0);
|
|
721
|
+
await flush();
|
|
722
|
+
playbackController.dispatch('playing', {});
|
|
723
|
+
playbackController.duration = 10;
|
|
724
|
+
playbackController.playbackRate = 1;
|
|
725
|
+
playbackController.currentTimePercent = 0.8;
|
|
726
|
+
updateTime(8);
|
|
727
|
+
await flush();
|
|
728
|
+
expect(thirds).toEqual(['a1']);
|
|
729
|
+
});
|
|
730
|
+
it('does not emit quartiles or throw when the ad duration is zero', async () => {
|
|
731
|
+
// Regression: a zero reported duration must not divide-by-zero into
|
|
732
|
+
// NaN progress and must not emit bogus quartile events.
|
|
733
|
+
const c = createController();
|
|
734
|
+
const events = [];
|
|
735
|
+
c.on('adFirstQuartile', () => events.push('first'));
|
|
736
|
+
c.on('adMidpoint', () => events.push('mid'));
|
|
737
|
+
c.on('adThirdQuartile', () => events.push('third'));
|
|
738
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
739
|
+
updateTime(0);
|
|
740
|
+
await flush();
|
|
741
|
+
playbackController.dispatch('playing', {});
|
|
742
|
+
playbackController.duration = 0;
|
|
743
|
+
playbackController.currentTimePercent = 0.9;
|
|
744
|
+
expect(() => updateTime(1)).not.toThrow();
|
|
745
|
+
await flush();
|
|
746
|
+
expect(events).toEqual([]);
|
|
747
|
+
});
|
|
748
|
+
it('fails the ad if it does not start within the load timeout', async () => {
|
|
749
|
+
const c = new AdControllerImpl({ playbackController }, { adLoadTimeout: 0 });
|
|
750
|
+
const errors = [];
|
|
751
|
+
c.on('adError', (e) => errors.push(e.error));
|
|
752
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
753
|
+
updateTime(1);
|
|
754
|
+
// Never dispatch 'playing'; wait for the (zero-second) load timeout
|
|
755
|
+
// to elapse and fail the ad.
|
|
756
|
+
await flush();
|
|
757
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
758
|
+
expect(errors.length).toBe(1);
|
|
759
|
+
expect(c.currentAdBreak).toBeNull();
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
describe('edge cases', () => {
|
|
763
|
+
it('tears down an active ad when the content track changes', async () => {
|
|
764
|
+
const c = createController();
|
|
765
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
766
|
+
updateTime(1);
|
|
767
|
+
await flush();
|
|
768
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
769
|
+
const entered = [];
|
|
770
|
+
const completedReasons = [];
|
|
771
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
772
|
+
c.on('adBreakCompleted', (e) => completedReasons.push(e.reason));
|
|
773
|
+
// A content change to a different track tears down the in-flight ad
|
|
774
|
+
// so it cannot leak into the new presentation; the break completes
|
|
775
|
+
// with reason 'contentChange' and no new break is entered.
|
|
776
|
+
await setContent(c, { trackUri: 't2', adBreaks: [] });
|
|
777
|
+
expect(c.currentAd).toBeNull();
|
|
778
|
+
expect(c.currentAdBreak).toBeNull();
|
|
779
|
+
expect(entered).toEqual([]);
|
|
780
|
+
expect(completedReasons).toEqual(['contentChange']);
|
|
781
|
+
});
|
|
782
|
+
it('does not emit a stray adEntered when the content swaps mid-resolve', async () => {
|
|
783
|
+
// Regression: an ad whose list was still resolving when the parent
|
|
784
|
+
// track changed must not fire adEntered for the abandoned break.
|
|
785
|
+
const c = createController();
|
|
786
|
+
let resolveAds = () => { };
|
|
787
|
+
const enteredAds = [];
|
|
788
|
+
c.on('adEntered', (e) => enteredAds.push(e.ad.id));
|
|
789
|
+
await setContent(c, trackAds(makeBreak({
|
|
790
|
+
startTime: 10,
|
|
791
|
+
ads: () => new Promise((resolve) => {
|
|
792
|
+
resolveAds = resolve;
|
|
793
|
+
}),
|
|
794
|
+
})));
|
|
795
|
+
updateTime(10); // enter the break; its ads are still resolving
|
|
796
|
+
await flush();
|
|
797
|
+
// Content changes before the ad list resolves.
|
|
798
|
+
await setContent(c, { trackUri: 't2', adBreaks: [] });
|
|
799
|
+
resolveAds([defaultAd]);
|
|
800
|
+
await flush();
|
|
801
|
+
expect(enteredAds).toEqual([]);
|
|
802
|
+
expect(c.currentAd).toBeNull();
|
|
803
|
+
});
|
|
804
|
+
it('ignores an ended event when no ad is active', () => {
|
|
805
|
+
const c = createController();
|
|
806
|
+
const ended = jasmine.createSpy('adEnded');
|
|
807
|
+
c.on('adEnded', ended);
|
|
808
|
+
playbackController.dispatch('ended', {
|
|
809
|
+
previous: false,
|
|
810
|
+
current: true,
|
|
811
|
+
});
|
|
812
|
+
expect(ended).not.toHaveBeenCalled();
|
|
813
|
+
});
|
|
814
|
+
it('does not start another ad while one is active', async () => {
|
|
815
|
+
const c = createController();
|
|
816
|
+
const track = await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
817
|
+
updateTime(1);
|
|
818
|
+
await flush();
|
|
819
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
820
|
+
// A live manifest update arrives mid-ad; the active ad must not be
|
|
821
|
+
// interrupted by newly discovered breaks.
|
|
822
|
+
await refreshAds(track, trackAds(makeBreak({ id: 'pre', startTime: 0, placement: 'preroll' })));
|
|
823
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
824
|
+
});
|
|
825
|
+
it('enters the nearest preceding midroll on a forward crossing', async () => {
|
|
826
|
+
const c = createController();
|
|
827
|
+
await setContent(c, trackAds(makeBreak({ id: 'near', startTime: 5, duration: 10 }), makeBreak({ id: 'far', startTime: 50, duration: 10 })));
|
|
828
|
+
updateTime(6);
|
|
829
|
+
await flush();
|
|
830
|
+
expect(c.currentAdBreak?.id).toBe('near');
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
describe('X-RESUME-OFFSET resume position', () => {
|
|
834
|
+
function resumesOf(c) {
|
|
835
|
+
const resumes = [];
|
|
836
|
+
c.on('adBreakCompleted', (e) => resumes.push(e.resumePosition));
|
|
837
|
+
return resumes;
|
|
838
|
+
}
|
|
839
|
+
it('resumes at the scheduled start plus a present offset', async () => {
|
|
840
|
+
const c = createController();
|
|
841
|
+
const resumes = resumesOf(c);
|
|
842
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6, resumeOffset: 5 })));
|
|
843
|
+
updateTime(10);
|
|
844
|
+
await flush();
|
|
845
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
846
|
+
c.skipAd();
|
|
847
|
+
expect(resumes.at(-1)).toBe(15);
|
|
848
|
+
});
|
|
849
|
+
it('treats a present offset of 0 as resume-in-place', async () => {
|
|
850
|
+
const c = createController();
|
|
851
|
+
const resumes = resumesOf(c);
|
|
852
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6, resumeOffset: 0 })));
|
|
853
|
+
updateTime(10);
|
|
854
|
+
await flush();
|
|
855
|
+
c.skipAd();
|
|
856
|
+
expect(resumes.at(-1)).toBe(10);
|
|
857
|
+
});
|
|
858
|
+
it('clamps a negative offset to the natural resume position', async () => {
|
|
859
|
+
const c = createController();
|
|
860
|
+
const resumes = resumesOf(c);
|
|
861
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6, resumeOffset: -4 })));
|
|
862
|
+
updateTime(10);
|
|
863
|
+
await flush();
|
|
864
|
+
c.skipAd();
|
|
865
|
+
expect(resumes.at(-1)).toBe(10);
|
|
866
|
+
});
|
|
867
|
+
it('resumes at the cue point regardless of how long the ad played', async () => {
|
|
868
|
+
const c = createController();
|
|
869
|
+
const resumes = resumesOf(c);
|
|
870
|
+
await setContent(c, trackAds(makeBreak({
|
|
871
|
+
startTime: 10,
|
|
872
|
+
duration: 20,
|
|
873
|
+
// Null per-ad duration so the per-ad cap does not end it.
|
|
874
|
+
ads: [
|
|
875
|
+
{
|
|
876
|
+
id: 'a1',
|
|
877
|
+
startTime: 0,
|
|
878
|
+
duration: null,
|
|
879
|
+
uri: 'ad.m3u8',
|
|
880
|
+
},
|
|
881
|
+
],
|
|
882
|
+
})));
|
|
883
|
+
updateTime(10);
|
|
884
|
+
await flush();
|
|
885
|
+
playbackController.dispatch('playing', {}); // timeStart = 10
|
|
886
|
+
updateTime(16); // the ad plays 6s on its own track
|
|
887
|
+
playbackController.dispatch('ended', {
|
|
888
|
+
previous: false,
|
|
889
|
+
current: true,
|
|
890
|
+
});
|
|
891
|
+
// The content timeline did not advance during the ad, so with no
|
|
892
|
+
// explicit offset content resumes at the cue point, not startTime +
|
|
893
|
+
// the 6s of ad playout.
|
|
894
|
+
expect(resumes.at(-1)).toBe(10);
|
|
895
|
+
});
|
|
896
|
+
it('resumes at the cue point even after a seek within the ad', async () => {
|
|
897
|
+
const c = createController();
|
|
898
|
+
const resumes = [];
|
|
899
|
+
c.on('adBreakCompleted', (e) => resumes.push(e.resumePosition));
|
|
900
|
+
await setContent(c, trackAds(makeBreak({
|
|
901
|
+
startTime: 10,
|
|
902
|
+
duration: 20,
|
|
903
|
+
ads: [
|
|
904
|
+
{
|
|
905
|
+
id: 'a1',
|
|
906
|
+
startTime: 0,
|
|
907
|
+
duration: null,
|
|
908
|
+
uri: 'ad.m3u8',
|
|
909
|
+
},
|
|
910
|
+
],
|
|
911
|
+
})));
|
|
912
|
+
updateTime(10);
|
|
913
|
+
await flush();
|
|
914
|
+
playbackController.dispatch('playing', {}); // timeStart = 10
|
|
915
|
+
seekTo(14); // seek within the ad (re-baselines its playout accounting)
|
|
916
|
+
updateTime(18);
|
|
917
|
+
playbackController.dispatch('ended', {
|
|
918
|
+
previous: false,
|
|
919
|
+
current: true,
|
|
920
|
+
});
|
|
921
|
+
// Ad-track playout — however it is measured — does not move the
|
|
922
|
+
// content resume: with no explicit offset it stays at the cue point.
|
|
923
|
+
expect(resumes.at(-1)).toBe(10);
|
|
924
|
+
});
|
|
925
|
+
it('parks a postroll resume at the content end, not past it', async () => {
|
|
926
|
+
const c = createController();
|
|
927
|
+
const resumes = [];
|
|
928
|
+
c.on('adBreakCompleted', (e) => resumes.push(e.resumePosition));
|
|
929
|
+
playbackController.duration = 100;
|
|
930
|
+
await setContent(c, trackAds(makeBreak({
|
|
931
|
+
id: 'post',
|
|
932
|
+
startTime: 100,
|
|
933
|
+
duration: 10,
|
|
934
|
+
placement: 'postroll',
|
|
935
|
+
ads: [
|
|
936
|
+
{
|
|
937
|
+
id: 'a1',
|
|
938
|
+
startTime: 0,
|
|
939
|
+
duration: null,
|
|
940
|
+
uri: 'ad.m3u8',
|
|
941
|
+
},
|
|
942
|
+
],
|
|
943
|
+
})));
|
|
944
|
+
await c.enterPostroll();
|
|
945
|
+
await flush();
|
|
946
|
+
playbackController.dispatch('playing', {}); // timeStart = 0
|
|
947
|
+
playbackController.currentTime = 8; // ad played 8s
|
|
948
|
+
playbackController.dispatch('ended', {
|
|
949
|
+
previous: false,
|
|
950
|
+
current: true,
|
|
951
|
+
});
|
|
952
|
+
// A postroll's cue point is the content end and it carries no
|
|
953
|
+
// offset, so the resume parks at the content end (100).
|
|
954
|
+
expect(resumes.at(-1)).toBe(100);
|
|
955
|
+
});
|
|
956
|
+
it('preserves a forward seek into the break over the offset', async () => {
|
|
957
|
+
const c = createController();
|
|
958
|
+
const resumes = resumesOf(c);
|
|
959
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6, resumeOffset: 2 })));
|
|
960
|
+
updateTime(13); // seek/enter at 13; natural resume = 13 > 10 + 2
|
|
961
|
+
await flush();
|
|
962
|
+
c.skipAd();
|
|
963
|
+
expect(resumes.at(-1)).toBe(13);
|
|
964
|
+
});
|
|
965
|
+
});
|
|
966
|
+
describe('ad duration and playout limits', () => {
|
|
967
|
+
it('advances to the next ad when one reaches its declared duration', async () => {
|
|
968
|
+
const c = createController();
|
|
969
|
+
await setContent(c, trackAds(makeBreak({
|
|
970
|
+
startTime: 0,
|
|
971
|
+
duration: 30,
|
|
972
|
+
ads: [
|
|
973
|
+
{
|
|
974
|
+
id: 'a1',
|
|
975
|
+
startTime: 0,
|
|
976
|
+
duration: 5,
|
|
977
|
+
uri: 'ad1.m3u8',
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
id: 'a2',
|
|
981
|
+
startTime: 0,
|
|
982
|
+
duration: 20,
|
|
983
|
+
uri: 'ad2.m3u8',
|
|
984
|
+
},
|
|
985
|
+
],
|
|
986
|
+
})));
|
|
987
|
+
updateTime(0);
|
|
988
|
+
await flush();
|
|
989
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
990
|
+
playbackController.dispatch('playing', {}); // timeStart = 0
|
|
991
|
+
updateTime(6); // elapsed 6 >= a1.duration 5 → advance
|
|
992
|
+
await flush();
|
|
993
|
+
expect(c.currentAd?.id).toBe('a2');
|
|
994
|
+
});
|
|
995
|
+
it('ends the whole break when the playout limit is reached, dropping remaining ads', async () => {
|
|
996
|
+
const c = createController();
|
|
997
|
+
const resumes = [];
|
|
998
|
+
c.on('adBreakCompleted', (e) => resumes.push(e.resumePosition));
|
|
999
|
+
await setContent(c, trackAds(makeBreak({
|
|
1000
|
+
startTime: 0,
|
|
1001
|
+
duration: null,
|
|
1002
|
+
playoutLimit: 8,
|
|
1003
|
+
ads: [
|
|
1004
|
+
{
|
|
1005
|
+
id: 'a1',
|
|
1006
|
+
startTime: 0,
|
|
1007
|
+
duration: 20,
|
|
1008
|
+
uri: 'ad1.m3u8',
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
id: 'a2',
|
|
1012
|
+
startTime: 0,
|
|
1013
|
+
duration: 20,
|
|
1014
|
+
uri: 'ad2.m3u8',
|
|
1015
|
+
},
|
|
1016
|
+
],
|
|
1017
|
+
})));
|
|
1018
|
+
updateTime(0);
|
|
1019
|
+
await flush();
|
|
1020
|
+
playbackController.dispatch('playing', {}); // timeStart = 0
|
|
1021
|
+
updateTime(4); // under the limit and under a1.duration → keep playing
|
|
1022
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
1023
|
+
updateTime(8); // cumulative 8 >= playoutLimit 8 → end break
|
|
1024
|
+
await flush();
|
|
1025
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1026
|
+
// The playout limit ends the break, but with no explicit offset the
|
|
1027
|
+
// content still resumes at the cue point (0), not startTime + playout.
|
|
1028
|
+
expect(resumes.at(-1)).toBe(0);
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
describe('CUE=ONCE and replay', () => {
|
|
1032
|
+
it('replays a non-ONCE break after the playhead seeks back before it', async () => {
|
|
1033
|
+
const c = createController();
|
|
1034
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6 })));
|
|
1035
|
+
updateTime(11);
|
|
1036
|
+
await flush();
|
|
1037
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
1038
|
+
c.skipAd(); // completes → "spent"
|
|
1039
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1040
|
+
seekTo(5); // seek back before start → re-arm
|
|
1041
|
+
await flush();
|
|
1042
|
+
updateTime(11); // forward crossing → re-enter
|
|
1043
|
+
await flush();
|
|
1044
|
+
expect(c.currentAdBreak?.id).toBe('b1');
|
|
1045
|
+
});
|
|
1046
|
+
it('does not replay a ONCE break after a seek back', async () => {
|
|
1047
|
+
const c = createController();
|
|
1048
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6, once: true })));
|
|
1049
|
+
updateTime(11);
|
|
1050
|
+
await flush();
|
|
1051
|
+
c.skipAd();
|
|
1052
|
+
seekTo(5);
|
|
1053
|
+
await flush();
|
|
1054
|
+
updateTime(11);
|
|
1055
|
+
await flush();
|
|
1056
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1057
|
+
});
|
|
1058
|
+
it('does not replay a spent break on a non-user seek settle', async () => {
|
|
1059
|
+
const c = createController();
|
|
1060
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6 })));
|
|
1061
|
+
updateTime(11);
|
|
1062
|
+
await flush();
|
|
1063
|
+
c.skipAd(); // spent
|
|
1064
|
+
// The ad→content source swap fires a 'seeked' with reason 'emptied'
|
|
1065
|
+
// while the MediaSource re-inits and transiently reports time 0.
|
|
1066
|
+
// This must NOT re-arm the break (only a real user seek does).
|
|
1067
|
+
playbackController.currentTime = 0;
|
|
1068
|
+
playbackController.dispatch('seeked', {
|
|
1069
|
+
started: 0,
|
|
1070
|
+
ended: 0,
|
|
1071
|
+
duration: 0,
|
|
1072
|
+
reason: 'emptied',
|
|
1073
|
+
});
|
|
1074
|
+
await flush();
|
|
1075
|
+
updateTime(11); // forward crossing again
|
|
1076
|
+
await flush();
|
|
1077
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1078
|
+
});
|
|
1079
|
+
it('does not replay a non-ONCE break when a seek stays at or after its start', async () => {
|
|
1080
|
+
const c = createController();
|
|
1081
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 6 })));
|
|
1082
|
+
updateTime(11);
|
|
1083
|
+
await flush();
|
|
1084
|
+
c.skipAd();
|
|
1085
|
+
seekTo(12); // seek, but not before start → not re-armed
|
|
1086
|
+
await flush();
|
|
1087
|
+
updateTime(13); // forward: still suppressed
|
|
1088
|
+
await flush();
|
|
1089
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1090
|
+
});
|
|
1091
|
+
it('permanently suppresses a completed preroll within the presentation', async () => {
|
|
1092
|
+
const c = createController();
|
|
1093
|
+
const track = await setContent(c, trackAds(makeBreak({ id: 'pre', startTime: 0, placement: 'preroll' })));
|
|
1094
|
+
await c.enterPreroll();
|
|
1095
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
1096
|
+
c.skipAd(); // completes preroll → permanent suppression
|
|
1097
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1098
|
+
// A live manifest refresh re-sets the same ads on the same track;
|
|
1099
|
+
// the preroll must not replay within the same presentation.
|
|
1100
|
+
await refreshAds(track, trackAds(makeBreak({ id: 'pre', startTime: 0, placement: 'preroll' })));
|
|
1101
|
+
await c.enterPreroll();
|
|
1102
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1103
|
+
});
|
|
1104
|
+
it('suppresses a completed postroll so enterPostroll does not replay it', async () => {
|
|
1105
|
+
const c = createController();
|
|
1106
|
+
const entered = [];
|
|
1107
|
+
c.on('adBreakEntered', (e) => {
|
|
1108
|
+
entered.push(e.adBreak.id);
|
|
1109
|
+
});
|
|
1110
|
+
await setContent(c, trackAds(makeBreak({
|
|
1111
|
+
id: 'post',
|
|
1112
|
+
startTime: 60,
|
|
1113
|
+
duration: 10,
|
|
1114
|
+
placement: 'postroll',
|
|
1115
|
+
})));
|
|
1116
|
+
// Content ends: the postroll activates and its single ad plays out.
|
|
1117
|
+
await c.enterPostroll();
|
|
1118
|
+
await flush();
|
|
1119
|
+
expect(c.currentAd?.id).toBe('a1');
|
|
1120
|
+
playbackController.dispatch('ended', {
|
|
1121
|
+
previous: false,
|
|
1122
|
+
current: true,
|
|
1123
|
+
});
|
|
1124
|
+
expect(c.currentAdBreak).toBeNull();
|
|
1125
|
+
// A re-fired content `ended` calls enterPostroll again; the
|
|
1126
|
+
// already-played postroll must not replay.
|
|
1127
|
+
await c.enterPostroll();
|
|
1128
|
+
await flush();
|
|
1129
|
+
expect(c.currentAdBreak)
|
|
1130
|
+
.withContext('postroll must not replay')
|
|
1131
|
+
.toBeNull();
|
|
1132
|
+
expect(c.currentAd).toBeNull();
|
|
1133
|
+
expect(entered).toEqual(['post']);
|
|
1134
|
+
});
|
|
1135
|
+
});
|
|
1136
|
+
describe('adTimeUpdate', () => {
|
|
1137
|
+
function timingsOf(c) {
|
|
1138
|
+
const seen = [];
|
|
1139
|
+
c.on('adTimeUpdate', (e) => seen.push({
|
|
1140
|
+
adCurrentTime: e.adCurrentTime,
|
|
1141
|
+
adTimeRemaining: e.adTimeRemaining,
|
|
1142
|
+
breakCurrentTime: e.breakCurrentTime,
|
|
1143
|
+
breakTimeRemaining: e.breakTimeRemaining,
|
|
1144
|
+
}));
|
|
1145
|
+
return seen;
|
|
1146
|
+
}
|
|
1147
|
+
it('reports elapsed and remaining time for the ad and the break', async () => {
|
|
1148
|
+
const c = createController();
|
|
1149
|
+
const seen = timingsOf(c);
|
|
1150
|
+
await setContent(c, trackAds(makeBreak({
|
|
1151
|
+
startTime: 0,
|
|
1152
|
+
duration: 30,
|
|
1153
|
+
playoutLimit: 20,
|
|
1154
|
+
ads: [
|
|
1155
|
+
{
|
|
1156
|
+
id: 'a1',
|
|
1157
|
+
startTime: 0,
|
|
1158
|
+
duration: 10,
|
|
1159
|
+
uri: 'ad.m3u8',
|
|
1160
|
+
},
|
|
1161
|
+
],
|
|
1162
|
+
})));
|
|
1163
|
+
updateTime(0);
|
|
1164
|
+
await flush();
|
|
1165
|
+
playbackController.dispatch('playing', {}); // timeStart = 0
|
|
1166
|
+
updateTime(4);
|
|
1167
|
+
await flush();
|
|
1168
|
+
const last = seen.at(-1);
|
|
1169
|
+
expect(last.adCurrentTime).toBe(4);
|
|
1170
|
+
expect(last.adTimeRemaining).toBe(6); // ad.duration 10 - 4
|
|
1171
|
+
expect(last.breakCurrentTime).toBe(4);
|
|
1172
|
+
expect(last.breakTimeRemaining).toBe(16); // playoutLimit 20 - 4
|
|
1173
|
+
});
|
|
1174
|
+
it('falls back to the media duration when the ad duration is unknown', async () => {
|
|
1175
|
+
const c = createController();
|
|
1176
|
+
const seen = timingsOf(c);
|
|
1177
|
+
playbackController.duration = 12;
|
|
1178
|
+
await setContent(c, trackAds(makeBreak({
|
|
1179
|
+
startTime: 0,
|
|
1180
|
+
duration: 12,
|
|
1181
|
+
ads: [
|
|
1182
|
+
{
|
|
1183
|
+
id: 'a1',
|
|
1184
|
+
startTime: 0,
|
|
1185
|
+
duration: null,
|
|
1186
|
+
uri: 'ad.m3u8',
|
|
1187
|
+
},
|
|
1188
|
+
],
|
|
1189
|
+
})));
|
|
1190
|
+
updateTime(0);
|
|
1191
|
+
await flush();
|
|
1192
|
+
playbackController.dispatch('playing', {});
|
|
1193
|
+
updateTime(5);
|
|
1194
|
+
await flush();
|
|
1195
|
+
const last = seen.at(-1);
|
|
1196
|
+
expect(last.adTimeRemaining).toBe(7); // media duration 12 - 5
|
|
1197
|
+
expect(last.breakTimeRemaining).toBe(7); // break duration 12 - 5
|
|
1198
|
+
});
|
|
1199
|
+
it('reports null remaining when the ad and break durations are unknown', async () => {
|
|
1200
|
+
const c = createController();
|
|
1201
|
+
const seen = timingsOf(c);
|
|
1202
|
+
playbackController.duration = Infinity;
|
|
1203
|
+
await setContent(c, trackAds(makeBreak({
|
|
1204
|
+
startTime: 0,
|
|
1205
|
+
duration: null,
|
|
1206
|
+
playoutLimit: null,
|
|
1207
|
+
resolutionTimeOffset: null,
|
|
1208
|
+
ads: [
|
|
1209
|
+
{
|
|
1210
|
+
id: 'a1',
|
|
1211
|
+
startTime: 0,
|
|
1212
|
+
duration: null,
|
|
1213
|
+
uri: 'ad.m3u8',
|
|
1214
|
+
},
|
|
1215
|
+
],
|
|
1216
|
+
})));
|
|
1217
|
+
updateTime(0);
|
|
1218
|
+
await flush();
|
|
1219
|
+
playbackController.dispatch('playing', {});
|
|
1220
|
+
updateTime(3);
|
|
1221
|
+
await flush();
|
|
1222
|
+
const last = seen.at(-1);
|
|
1223
|
+
expect(last.adCurrentTime).toBe(3);
|
|
1224
|
+
expect(last.adTimeRemaining).toBeNull();
|
|
1225
|
+
expect(last.breakTimeRemaining).toBeNull();
|
|
1226
|
+
});
|
|
1227
|
+
it('caps ad time remaining by the break playout limit', async () => {
|
|
1228
|
+
const c = createController();
|
|
1229
|
+
const seen = timingsOf(c);
|
|
1230
|
+
await setContent(c, trackAds(makeBreak({
|
|
1231
|
+
startTime: 0,
|
|
1232
|
+
duration: 8,
|
|
1233
|
+
playoutLimit: 8,
|
|
1234
|
+
ads: [
|
|
1235
|
+
{
|
|
1236
|
+
id: 'a1',
|
|
1237
|
+
startTime: 0,
|
|
1238
|
+
// Natural duration exceeds the playout limit, so
|
|
1239
|
+
// the ad is cut off at the limit and the reported
|
|
1240
|
+
// total must reflect that (not the full 20s).
|
|
1241
|
+
duration: 20,
|
|
1242
|
+
uri: 'ad.m3u8',
|
|
1243
|
+
},
|
|
1244
|
+
],
|
|
1245
|
+
})));
|
|
1246
|
+
updateTime(0);
|
|
1247
|
+
await flush();
|
|
1248
|
+
playbackController.dispatch('playing', {}); // timeStart = 0
|
|
1249
|
+
updateTime(4);
|
|
1250
|
+
await flush();
|
|
1251
|
+
const last = seen.at(-1);
|
|
1252
|
+
expect(last.adCurrentTime).toBe(4);
|
|
1253
|
+
expect(last.adTimeRemaining).toBe(4); // min(ad 20, limit 8) - 4
|
|
1254
|
+
expect(last.breakTimeRemaining).toBe(4); // playoutLimit 8 - 4
|
|
1255
|
+
});
|
|
1256
|
+
it('uses the playout-limit remainder when the ad duration is unknown', async () => {
|
|
1257
|
+
const c = createController();
|
|
1258
|
+
const seen = timingsOf(c);
|
|
1259
|
+
playbackController.duration = Infinity;
|
|
1260
|
+
await setContent(c, trackAds(makeBreak({
|
|
1261
|
+
startTime: 0,
|
|
1262
|
+
duration: 8,
|
|
1263
|
+
playoutLimit: 8,
|
|
1264
|
+
ads: [
|
|
1265
|
+
{
|
|
1266
|
+
id: 'a1',
|
|
1267
|
+
startTime: 0,
|
|
1268
|
+
duration: null,
|
|
1269
|
+
uri: 'ad.m3u8',
|
|
1270
|
+
},
|
|
1271
|
+
],
|
|
1272
|
+
})));
|
|
1273
|
+
updateTime(0);
|
|
1274
|
+
await flush();
|
|
1275
|
+
playbackController.dispatch('playing', {});
|
|
1276
|
+
updateTime(3);
|
|
1277
|
+
await flush();
|
|
1278
|
+
const last = seen.at(-1);
|
|
1279
|
+
// Without a known ad duration and with an infinite media duration,
|
|
1280
|
+
// the limit remainder bounds the report instead of leaving it null.
|
|
1281
|
+
expect(last.adTimeRemaining).toBe(5); // limit 8 - 3
|
|
1282
|
+
expect(last.breakTimeRemaining).toBe(5);
|
|
1283
|
+
});
|
|
1284
|
+
});
|
|
1285
|
+
describe('skip window', () => {
|
|
1286
|
+
function skipStatesOf(c) {
|
|
1287
|
+
const seen = [];
|
|
1288
|
+
c.on('adTimeUpdate', (e) => seen.push({ canSkip: e.canSkip, skipIn: e.skipIn }));
|
|
1289
|
+
return seen;
|
|
1290
|
+
}
|
|
1291
|
+
/** A break whose (async) skip window opens `offset`s in for `duration`s. */
|
|
1292
|
+
function breakWithSkip(offset, duration) {
|
|
1293
|
+
return makeBreak({
|
|
1294
|
+
startTime: 0,
|
|
1295
|
+
duration: 60,
|
|
1296
|
+
ads: [{ id: 'a1', startTime: 0, duration: null, uri: 'ad' }],
|
|
1297
|
+
skipControl: () => Promise.resolve({ offset, duration }),
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
it('is not skippable before the window and counts down to it', async () => {
|
|
1301
|
+
const c = createController();
|
|
1302
|
+
const seen = skipStatesOf(c);
|
|
1303
|
+
await setContent(c, trackAds(breakWithSkip(6, null)));
|
|
1304
|
+
updateTime(0);
|
|
1305
|
+
await flush();
|
|
1306
|
+
playbackController.dispatch('playing', {}); // timeStart = 0
|
|
1307
|
+
updateTime(2);
|
|
1308
|
+
await flush();
|
|
1309
|
+
const last = seen.at(-1);
|
|
1310
|
+
expect(last.canSkip).toBeFalse();
|
|
1311
|
+
expect(last.skipIn).toBe(4); // offset 6 - played 2
|
|
1312
|
+
});
|
|
1313
|
+
it('becomes skippable once inside the window', async () => {
|
|
1314
|
+
const c = createController();
|
|
1315
|
+
const seen = skipStatesOf(c);
|
|
1316
|
+
await setContent(c, trackAds(breakWithSkip(6, 5)));
|
|
1317
|
+
updateTime(0);
|
|
1318
|
+
await flush();
|
|
1319
|
+
playbackController.dispatch('playing', {});
|
|
1320
|
+
updateTime(8); // within [6, 11)
|
|
1321
|
+
await flush();
|
|
1322
|
+
const last = seen.at(-1);
|
|
1323
|
+
expect(last.canSkip).toBeTrue();
|
|
1324
|
+
expect(last.skipIn).toBeNull();
|
|
1325
|
+
});
|
|
1326
|
+
it('stays skippable for the rest of the break when the window is open-ended', async () => {
|
|
1327
|
+
const c = createController();
|
|
1328
|
+
const seen = skipStatesOf(c);
|
|
1329
|
+
await setContent(c, trackAds(breakWithSkip(6, null)));
|
|
1330
|
+
updateTime(0);
|
|
1331
|
+
await flush();
|
|
1332
|
+
playbackController.dispatch('playing', {});
|
|
1333
|
+
updateTime(10); // past offset 6, no window end
|
|
1334
|
+
await flush();
|
|
1335
|
+
const last = seen.at(-1);
|
|
1336
|
+
expect(last.canSkip).toBeTrue();
|
|
1337
|
+
expect(last.skipIn).toBeNull();
|
|
1338
|
+
});
|
|
1339
|
+
it('stops being skippable after the window closes', async () => {
|
|
1340
|
+
const c = createController();
|
|
1341
|
+
const seen = skipStatesOf(c);
|
|
1342
|
+
await setContent(c, trackAds(breakWithSkip(6, 5)));
|
|
1343
|
+
updateTime(0);
|
|
1344
|
+
await flush();
|
|
1345
|
+
playbackController.dispatch('playing', {});
|
|
1346
|
+
updateTime(12); // past 6 + 5 = 11
|
|
1347
|
+
await flush();
|
|
1348
|
+
const last = seen.at(-1);
|
|
1349
|
+
expect(last.canSkip).toBeFalse();
|
|
1350
|
+
expect(last.skipIn).toBeNull();
|
|
1351
|
+
});
|
|
1352
|
+
it('never allows skipping when the break restricts it', async () => {
|
|
1353
|
+
const c = createController();
|
|
1354
|
+
const seen = skipStatesOf(c);
|
|
1355
|
+
await setContent(c, trackAds(makeBreak({
|
|
1356
|
+
startTime: 0,
|
|
1357
|
+
duration: 60,
|
|
1358
|
+
restrict: { skip: true },
|
|
1359
|
+
ads: [
|
|
1360
|
+
{
|
|
1361
|
+
id: 'a1',
|
|
1362
|
+
startTime: 0,
|
|
1363
|
+
duration: null,
|
|
1364
|
+
uri: 'ad',
|
|
1365
|
+
},
|
|
1366
|
+
],
|
|
1367
|
+
})));
|
|
1368
|
+
updateTime(0);
|
|
1369
|
+
await flush();
|
|
1370
|
+
playbackController.dispatch('playing', {});
|
|
1371
|
+
updateTime(3);
|
|
1372
|
+
await flush();
|
|
1373
|
+
const last = seen.at(-1);
|
|
1374
|
+
expect(last.canSkip).toBeFalse();
|
|
1375
|
+
expect(last.skipIn).toBeNull();
|
|
1376
|
+
});
|
|
1377
|
+
it('leaves the break with no skip window if the control fails to resolve', async () => {
|
|
1378
|
+
const c = createController();
|
|
1379
|
+
const seen = skipStatesOf(c);
|
|
1380
|
+
await setContent(c, trackAds(makeBreak({
|
|
1381
|
+
startTime: 0,
|
|
1382
|
+
duration: 60,
|
|
1383
|
+
ads: [
|
|
1384
|
+
{
|
|
1385
|
+
id: 'a1',
|
|
1386
|
+
startTime: 0,
|
|
1387
|
+
duration: null,
|
|
1388
|
+
uri: 'ad',
|
|
1389
|
+
},
|
|
1390
|
+
],
|
|
1391
|
+
skipControl: () => Promise.reject(new Error('boom')),
|
|
1392
|
+
})));
|
|
1393
|
+
updateTime(0);
|
|
1394
|
+
await flush();
|
|
1395
|
+
playbackController.dispatch('playing', {});
|
|
1396
|
+
updateTime(3);
|
|
1397
|
+
await flush();
|
|
1398
|
+
const last = seen.at(-1);
|
|
1399
|
+
// No resolved window → freely skippable.
|
|
1400
|
+
expect(last.canSkip).toBeTrue();
|
|
1401
|
+
expect(last.skipIn).toBeNull();
|
|
1402
|
+
});
|
|
1403
|
+
});
|
|
1404
|
+
describe('disposal during pending async work', () => {
|
|
1405
|
+
it('ignores a break ad list that resolves after disposal', async () => {
|
|
1406
|
+
const c = createController();
|
|
1407
|
+
let resolveAds = () => { };
|
|
1408
|
+
const entered = jasmine.createSpy('adEntered');
|
|
1409
|
+
const playing = jasmine.createSpy('adPlaying');
|
|
1410
|
+
c.on('adEntered', entered);
|
|
1411
|
+
c.on('adPlaying', playing);
|
|
1412
|
+
await setContent(c, trackAds(makeBreak({
|
|
1413
|
+
startTime: 10,
|
|
1414
|
+
ads: () => new Promise((resolve) => {
|
|
1415
|
+
resolveAds = resolve;
|
|
1416
|
+
}),
|
|
1417
|
+
})));
|
|
1418
|
+
updateTime(10);
|
|
1419
|
+
c.dispose();
|
|
1420
|
+
resolveAds([defaultAd]);
|
|
1421
|
+
await flush();
|
|
1422
|
+
// The ad list resolves after disposal: the controller must not act
|
|
1423
|
+
// on it — no ad is entered or started.
|
|
1424
|
+
expect(entered).not.toHaveBeenCalled();
|
|
1425
|
+
expect(playing).not.toHaveBeenCalled();
|
|
1426
|
+
});
|
|
1427
|
+
it('does not fail an ad after disposal when the load timeout elapses', async () => {
|
|
1428
|
+
// A small non-zero timeout so the timer is a real setTimeout that
|
|
1429
|
+
// fires after we dispose (sleep(0) would resolve on a microtask,
|
|
1430
|
+
// before dispose).
|
|
1431
|
+
const c = new AdControllerImpl({ playbackController }, { adLoadTimeout: 0.001 });
|
|
1432
|
+
const errors = [];
|
|
1433
|
+
c.on('adError', (e) => errors.push(e.error));
|
|
1434
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
1435
|
+
updateTime(1);
|
|
1436
|
+
await flush();
|
|
1437
|
+
// Dispose before the load-timeout timer fires.
|
|
1438
|
+
c.dispose();
|
|
1439
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
1440
|
+
expect(errors).toEqual([]);
|
|
1441
|
+
});
|
|
1442
|
+
});
|
|
1443
|
+
describe('dispose', () => {
|
|
1444
|
+
it('stops responding to time updates', async () => {
|
|
1445
|
+
const c = createController();
|
|
1446
|
+
await setContent(c, trackAds(makeBreak({ startTime: 10, duration: 5 })));
|
|
1447
|
+
c.dispose();
|
|
1448
|
+
const spy = jasmine.createSpy('adBreakChange');
|
|
1449
|
+
c.on('adBreakEntered', spy);
|
|
1450
|
+
c.on('adBreakCompleted', spy);
|
|
1451
|
+
updateTime(11);
|
|
1452
|
+
await flush();
|
|
1453
|
+
expect(spy).not.toHaveBeenCalled();
|
|
1454
|
+
});
|
|
1455
|
+
});
|
|
1456
|
+
});
|