rx-player 4.5.0-dev.2026033100 → 4.5.0-dev.2026041501
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/CHANGELOG.md +22 -5
- package/VERSION +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/commonjs/compat/can_patch_out_pssh.d.ts +42 -0
- package/dist/commonjs/compat/can_patch_out_pssh.d.ts.map +1 -0
- package/dist/commonjs/compat/can_patch_out_pssh.js +53 -0
- package/dist/commonjs/compat/env_detector.d.ts +2 -0
- package/dist/commonjs/compat/env_detector.d.ts.map +1 -1
- package/dist/commonjs/compat/env_detector.js +5 -0
- package/dist/commonjs/core/adaptive/network_analyzer.d.ts +1 -2
- package/dist/commonjs/core/adaptive/network_analyzer.d.ts.map +1 -1
- package/dist/commonjs/core/adaptive/network_analyzer.js +3 -3
- package/dist/commonjs/core/adaptive/utils/representation_score_calculator.js +2 -2
- package/dist/commonjs/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
- package/dist/commonjs/core/cmcd/cmcd_data_builder.js +9 -3
- package/dist/commonjs/core/entry/content_preparer.d.ts.map +1 -1
- package/dist/commonjs/core/entry/content_preparer.js +5 -7
- package/dist/commonjs/core/entry/core_entry.d.ts.map +1 -1
- package/dist/commonjs/core/entry/core_entry.js +6 -2
- package/dist/commonjs/core/entry/core_text_displayer_interface.js +3 -3
- package/dist/commonjs/core/fetchers/manifest/manifest_fetcher.d.ts.map +1 -1
- package/dist/commonjs/core/fetchers/manifest/manifest_fetcher.js +12 -0
- package/dist/commonjs/core/fetchers/thumbnails/thumbnail_fetcher.js +1 -1
- package/dist/commonjs/core/fetchers/utils/schedule_request.d.ts.map +1 -1
- package/dist/commonjs/core/fetchers/utils/schedule_request.js +4 -3
- package/dist/commonjs/core/segment_sinks/garbage_collector.d.ts +0 -2
- package/dist/commonjs/core/segment_sinks/garbage_collector.d.ts.map +1 -1
- package/dist/commonjs/core/segment_sinks/garbage_collector.js +0 -3
- package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.d.ts +1 -1
- package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
- package/dist/commonjs/core/segment_sinks/implementations/text/text_segment_sink.js +2 -2
- package/dist/commonjs/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
- package/dist/commonjs/core/stream/adaptation/adaptation_stream.js +6 -6
- package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
- package/dist/commonjs/core/stream/orchestrator/stream_orchestrator.js +37 -40
- package/dist/commonjs/default_config.d.ts +5 -0
- package/dist/commonjs/default_config.d.ts.map +1 -1
- package/dist/commonjs/default_config.js +5 -0
- package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/commonjs/main_thread/api/public_api.js +23 -16
- package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/content_decryptor.js +4 -1
- package/dist/commonjs/main_thread/decrypt/session_events_listener.js +1 -1
- package/dist/commonjs/main_thread/decrypt/set_server_certificate.d.ts +2 -0
- package/dist/commonjs/main_thread/decrypt/set_server_certificate.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/set_server_certificate.js +4 -0
- package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +0 -8
- package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/media_source_content_initializer.js +58 -50
- package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts +35 -5
- package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/utils/stream_events_emitter/stream_events_emitter.js +60 -19
- package/dist/commonjs/main_thread/render_thumbnail.d.ts.map +1 -1
- package/dist/commonjs/main_thread/render_thumbnail.js +4 -0
- package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
- package/dist/commonjs/main_thread/tracks_store/media_element_tracks_store.js +1 -0
- package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts +1 -1
- package/dist/commonjs/main_thread/tracks_store/tracks_store.js +1 -1
- package/dist/{es2017/parsers/containers/isobmff/take_pssh_out.d.ts → commonjs/parsers/containers/isobmff/extract_pssh.d.ts} +6 -4
- package/dist/commonjs/parsers/containers/isobmff/extract_pssh.d.ts.map +1 -0
- package/dist/commonjs/parsers/containers/isobmff/{take_pssh_out.js → extract_pssh.js} +22 -17
- package/dist/commonjs/parsers/containers/isobmff/index.d.ts +2 -2
- package/dist/commonjs/parsers/containers/isobmff/index.d.ts.map +1 -1
- package/dist/commonjs/parsers/containers/isobmff/index.js +4 -4
- package/dist/commonjs/playback_observer/core_playback_observer.d.ts +4 -4
- package/dist/commonjs/playback_observer/core_playback_observer.d.ts.map +1 -1
- package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts +1 -2
- package/dist/commonjs/playback_observer/media_element_playback_observer.d.ts.map +1 -1
- package/dist/commonjs/transports/dash/segment_parser.js +1 -1
- package/dist/commonjs/transports/local/segment_parser.js +1 -1
- package/dist/commonjs/utils/test-utils.d.ts +30 -0
- package/dist/commonjs/utils/test-utils.d.ts.map +1 -0
- package/dist/commonjs/utils/test-utils.js +79 -0
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/es2017/compat/can_patch_out_pssh.d.ts +42 -0
- package/dist/es2017/compat/can_patch_out_pssh.d.ts.map +1 -0
- package/dist/es2017/compat/can_patch_out_pssh.js +50 -0
- package/dist/es2017/compat/env_detector.d.ts +2 -0
- package/dist/es2017/compat/env_detector.d.ts.map +1 -1
- package/dist/es2017/compat/env_detector.js +5 -0
- package/dist/es2017/core/adaptive/network_analyzer.d.ts +1 -2
- package/dist/es2017/core/adaptive/network_analyzer.d.ts.map +1 -1
- package/dist/es2017/core/adaptive/network_analyzer.js +3 -3
- package/dist/es2017/core/adaptive/utils/representation_score_calculator.js +2 -2
- package/dist/es2017/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
- package/dist/es2017/core/cmcd/cmcd_data_builder.js +9 -3
- package/dist/es2017/core/entry/content_preparer.d.ts.map +1 -1
- package/dist/es2017/core/entry/content_preparer.js +3 -5
- package/dist/es2017/core/entry/core_entry.d.ts.map +1 -1
- package/dist/es2017/core/entry/core_entry.js +6 -2
- package/dist/es2017/core/entry/core_text_displayer_interface.js +3 -3
- package/dist/es2017/core/fetchers/manifest/manifest_fetcher.d.ts.map +1 -1
- package/dist/es2017/core/fetchers/manifest/manifest_fetcher.js +12 -0
- package/dist/es2017/core/fetchers/thumbnails/thumbnail_fetcher.js +1 -1
- package/dist/es2017/core/fetchers/utils/schedule_request.d.ts.map +1 -1
- package/dist/es2017/core/fetchers/utils/schedule_request.js +2 -3
- package/dist/es2017/core/segment_sinks/garbage_collector.d.ts +0 -2
- package/dist/es2017/core/segment_sinks/garbage_collector.d.ts.map +1 -1
- package/dist/es2017/core/segment_sinks/garbage_collector.js +0 -3
- package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.d.ts +1 -1
- package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.d.ts.map +1 -1
- package/dist/es2017/core/segment_sinks/implementations/text/text_segment_sink.js +2 -2
- package/dist/es2017/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
- package/dist/es2017/core/stream/adaptation/adaptation_stream.js +6 -6
- package/dist/es2017/core/stream/orchestrator/stream_orchestrator.d.ts.map +1 -1
- package/dist/es2017/core/stream/orchestrator/stream_orchestrator.js +40 -39
- package/dist/es2017/default_config.d.ts +5 -0
- package/dist/es2017/default_config.d.ts.map +1 -1
- package/dist/es2017/default_config.js +5 -0
- package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/es2017/main_thread/api/public_api.js +20 -13
- package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/content_decryptor.js +4 -1
- package/dist/es2017/main_thread/decrypt/session_events_listener.js +1 -1
- package/dist/es2017/main_thread/decrypt/set_server_certificate.d.ts +2 -0
- package/dist/es2017/main_thread/decrypt/set_server_certificate.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/set_server_certificate.js +4 -0
- package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +0 -8
- package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/media_source_content_initializer.js +58 -50
- package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts +35 -5
- package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/utils/stream_events_emitter/stream_events_emitter.js +60 -19
- package/dist/es2017/main_thread/render_thumbnail.d.ts.map +1 -1
- package/dist/es2017/main_thread/render_thumbnail.js +4 -0
- package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.d.ts.map +1 -1
- package/dist/es2017/main_thread/tracks_store/media_element_tracks_store.js +1 -0
- package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts +1 -1
- package/dist/es2017/main_thread/tracks_store/tracks_store.js +1 -1
- package/dist/{commonjs/parsers/containers/isobmff/take_pssh_out.d.ts → es2017/parsers/containers/isobmff/extract_pssh.d.ts} +6 -4
- package/dist/es2017/parsers/containers/isobmff/extract_pssh.d.ts.map +1 -0
- package/dist/es2017/parsers/containers/isobmff/{take_pssh_out.js → extract_pssh.js} +21 -16
- package/dist/es2017/parsers/containers/isobmff/index.d.ts +2 -2
- package/dist/es2017/parsers/containers/isobmff/index.d.ts.map +1 -1
- package/dist/es2017/parsers/containers/isobmff/index.js +2 -2
- package/dist/es2017/playback_observer/core_playback_observer.d.ts +4 -4
- package/dist/es2017/playback_observer/core_playback_observer.d.ts.map +1 -1
- package/dist/es2017/playback_observer/media_element_playback_observer.d.ts +1 -2
- package/dist/es2017/playback_observer/media_element_playback_observer.d.ts.map +1 -1
- package/dist/es2017/transports/dash/segment_parser.js +2 -2
- package/dist/es2017/transports/local/segment_parser.js +2 -2
- package/dist/es2017/utils/test-utils.d.ts +30 -0
- package/dist/es2017/utils/test-utils.d.ts.map +1 -0
- package/dist/es2017/utils/test-utils.js +36 -0
- package/dist/mpd-parser.wasm +0 -0
- package/dist/worker.js +6 -6
- package/package.json +4 -2
- package/src/README.md +7 -7
- package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
- package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
- package/src/compat/__tests__/can_patch_out_pssh.test.ts +40 -0
- package/src/compat/can_patch_out_pssh.ts +53 -0
- package/src/compat/env_detector.ts +4 -0
- package/src/core/README.md +1 -1
- package/src/core/adaptive/README.md +3 -3
- package/src/core/adaptive/__tests__/adaptive_representation_selector.test.ts +181 -110
- package/src/core/adaptive/__tests__/guess_based_chooser.test.ts +229 -123
- package/src/core/adaptive/__tests__/mocks.ts +100 -0
- package/src/core/adaptive/__tests__/network_analyzer.test.ts +152 -59
- package/src/core/adaptive/network_analyzer.ts +4 -4
- package/src/core/adaptive/utils/__tests__/filter_by_bitrate.test.ts +11 -19
- package/src/core/adaptive/utils/__tests__/filter_by_resolution.test.ts +12 -12
- package/src/core/adaptive/utils/__tests__/last_estimate_storage.test.ts +25 -12
- package/src/core/adaptive/utils/__tests__/pending_requests_store.test.ts +13 -9
- package/src/core/adaptive/utils/__tests__/representation_score_calculator.test.ts +11 -11
- package/src/core/adaptive/utils/__tests__/select_optimal_representation.test.ts +13 -23
- package/src/core/adaptive/utils/representation_score_calculator.ts +2 -2
- package/src/core/cmcd/__tests__/cmcd_data_builder.test.ts +60 -15
- package/src/core/cmcd/cmcd_data_builder.ts +9 -3
- package/src/core/entry/README.md +2 -2
- package/src/core/entry/__tests__/core_text_displayer_interface.test.ts +20 -0
- package/src/core/entry/content_preparer.ts +2 -5
- package/src/core/entry/core_entry.ts +6 -2
- package/src/core/entry/core_text_displayer_interface.ts +3 -3
- package/src/core/fetchers/manifest/__tests__/manifest_fetcher.test.ts +52 -3
- package/src/core/fetchers/manifest/manifest_fetcher.ts +12 -0
- package/src/core/fetchers/thumbnails/__tests__/thumbnail_fetcher.test.ts +70 -0
- package/src/core/fetchers/thumbnails/thumbnail_fetcher.ts +1 -1
- package/src/core/fetchers/utils/schedule_request.ts +5 -3
- package/src/core/segment_sinks/__tests__/garbage_collector.test.ts +434 -0
- package/src/core/segment_sinks/__tests__/mocks.ts +49 -0
- package/src/core/segment_sinks/garbage_collector.ts +0 -3
- package/src/core/segment_sinks/implementations/text/__tests__/text_segment_sink.test.ts +177 -0
- package/src/core/segment_sinks/implementations/text/text_segment_sink.ts +2 -2
- package/src/core/segment_sinks/inventory/__tests__/buffered_history.test.ts +215 -0
- package/src/core/segment_sinks/inventory/__tests__/segment_inventory.test.ts +448 -0
- package/src/core/stream/adaptation/__tests__/adaptation_stream.test.ts +973 -0
- package/src/core/stream/adaptation/__tests__/get_representations_switch_strategy.test.ts +283 -374
- package/src/core/stream/adaptation/adaptation_stream.ts +6 -8
- package/src/core/stream/orchestrator/README.md +4 -4
- package/src/core/stream/orchestrator/__tests__/stream_orchestrator.test.ts +707 -0
- package/src/core/stream/orchestrator/stream_orchestrator.ts +41 -46
- package/src/core/stream/period/utils/__tests__/get_adaptation_switch_strategy.test.ts +290 -220
- package/src/core/stream/representation/__tests__/encryption_data_notifier.test.ts +93 -63
- package/src/core/stream/representation/utils/__tests__/append_segment_to_buffer.test.ts +106 -63
- package/src/core/stream/representation/utils/__tests__/check_for_discontinuity.test.ts +179 -204
- package/src/core/stream/representation/utils/__tests__/get_segment_priority.test.ts +7 -7
- package/src/core/stream/representation/utils/__tests__/push_init_segment.test.ts +103 -60
- package/src/core/stream/representation/utils/__tests__/push_media_segment.test.ts +231 -165
- package/src/default_config.ts +6 -0
- package/src/experimental/README.md +1 -1
- package/src/features/README.md +3 -3
- package/src/main_thread/api/README.md +6 -7
- package/src/main_thread/api/public_api.ts +16 -10
- package/src/main_thread/decrypt/README.md +4 -4
- package/src/main_thread/decrypt/__tests__/__global__/content_decryptor.test.ts +135 -0
- package/src/main_thread/decrypt/__tests__/__global__/get_license.test.ts +70 -0
- package/src/main_thread/decrypt/__tests__/__global__/server_certificate.test.ts +44 -0
- package/src/main_thread/decrypt/__tests__/__global__/utils.ts +2 -2
- package/src/main_thread/decrypt/content_decryptor.ts +6 -1
- package/src/main_thread/decrypt/session_events_listener.ts +1 -1
- package/src/main_thread/decrypt/set_server_certificate.ts +5 -0
- package/src/main_thread/init/media_source_content_initializer.ts +69 -55
- package/src/main_thread/init/utils/__tests__/stream_events_emitter.test.ts +175 -0
- package/src/main_thread/init/utils/stream_events_emitter/stream_events_emitter.ts +90 -26
- package/src/main_thread/render_thumbnail.ts +4 -0
- package/src/main_thread/tracks_store/README.md +12 -0
- package/src/main_thread/tracks_store/__tests__/media_element_tracks_store.test.ts +25 -18
- package/src/main_thread/tracks_store/media_element_tracks_store.ts +1 -0
- package/src/main_thread/tracks_store/tracks_store.ts +1 -1
- package/src/manifest/classes/__tests__/mocks.ts +202 -0
- package/src/parsers/containers/isobmff/__tests__/extract_pssh.test.ts +199 -0
- package/src/parsers/containers/isobmff/{take_pssh_out.ts → extract_pssh.ts} +21 -17
- package/src/parsers/containers/isobmff/index.ts +2 -2
- package/src/parsers/manifest/dash/wasm-parser/README.md +9 -9
- package/src/playback_observer/__tests__/mocks.ts +152 -0
- package/src/playback_observer/core_playback_observer.ts +4 -4
- package/src/playback_observer/media_element_playback_observer.ts +1 -1
- package/src/tools/README.md +2 -2
- package/src/transports/README.md +5 -5
- package/src/transports/dash/segment_parser.ts +2 -2
- package/src/transports/local/segment_parser.ts +2 -2
- package/src/transports/metaplaylist/README.md +4 -4
- package/src/utils/README.md +3 -3
- package/src/utils/test-utils.ts +50 -0
- package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts +0 -11
- package/dist/commonjs/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
- package/dist/commonjs/core/entry/synchronize_sinks_on_observation.js +0 -20
- package/dist/commonjs/parsers/containers/isobmff/take_pssh_out.d.ts.map +0 -1
- package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts +0 -11
- package/dist/es2017/core/entry/synchronize_sinks_on_observation.d.ts.map +0 -1
- package/dist/es2017/core/entry/synchronize_sinks_on_observation.js +0 -17
- package/dist/es2017/parsers/containers/isobmff/take_pssh_out.d.ts.map +0 -1
- package/src/core/adaptive/utils/__tests__/bandwith_estimator.test.ts +0 -117
- package/src/core/entry/synchronize_sinks_on_observation.ts +0 -22
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
DummyManifest,
|
|
4
|
+
DummyPeriod,
|
|
5
|
+
DummyAdaptation,
|
|
6
|
+
DummyRepresentation,
|
|
7
|
+
createSegment,
|
|
8
|
+
} from "../../../../manifest/classes/__tests__/mocks";
|
|
2
9
|
import PendingRequestsStore from "../pending_requests_store";
|
|
3
10
|
import type {
|
|
4
11
|
IPendingRequestStoreBegin,
|
|
5
12
|
IPendingRequestStoreProgress,
|
|
6
13
|
} from "../pending_requests_store";
|
|
7
14
|
|
|
8
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
9
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
10
|
-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
11
|
-
|
|
12
15
|
const mocks = vi.hoisted(() => ({
|
|
13
16
|
warn: vi.fn(),
|
|
14
17
|
}));
|
|
@@ -24,11 +27,11 @@ describe("PendingRequestsStore", () => {
|
|
|
24
27
|
let store: PendingRequestsStore;
|
|
25
28
|
|
|
26
29
|
const createMockContent = (segmentTime: number = 0) => ({
|
|
27
|
-
manifest:
|
|
28
|
-
period:
|
|
29
|
-
adaptation:
|
|
30
|
-
representation:
|
|
31
|
-
segment: { time: segmentTime }
|
|
30
|
+
manifest: new DummyManifest(),
|
|
31
|
+
period: new DummyPeriod(),
|
|
32
|
+
adaptation: new DummyAdaptation(),
|
|
33
|
+
representation: new DummyRepresentation(),
|
|
34
|
+
segment: createSegment({ time: segmentTime }),
|
|
32
35
|
});
|
|
33
36
|
|
|
34
37
|
const createMockRequest = (
|
|
@@ -231,6 +234,7 @@ describe("PendingRequestsStore", () => {
|
|
|
231
234
|
|
|
232
235
|
store.add(request);
|
|
233
236
|
// Manually corrupt the internal state to test filtering
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
234
238
|
(store as any)._currentRequests.corrupted = null;
|
|
235
239
|
|
|
236
240
|
const requests = store.getRequests();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import type { IRepresentation } from "../../../../manifest";
|
|
3
|
+
import { DummyRepresentation } from "../../../../manifest/classes/__tests__/mocks";
|
|
3
4
|
import RepresentationScoreCalculator, {
|
|
4
5
|
ScoreConfidenceLevel,
|
|
5
6
|
} from "../representation_score_calculator";
|
|
@@ -40,15 +41,15 @@ describe("RepresentationScoreCalculator", () => {
|
|
|
40
41
|
beforeEach(() => {
|
|
41
42
|
calculator = new RepresentationScoreCalculator();
|
|
42
43
|
|
|
43
|
-
mockRepresentation1 = {
|
|
44
|
+
mockRepresentation1 = new DummyRepresentation({
|
|
44
45
|
id: "rep1",
|
|
45
46
|
bitrate: 1000000,
|
|
46
|
-
}
|
|
47
|
+
});
|
|
47
48
|
|
|
48
|
-
mockRepresentation2 = {
|
|
49
|
+
mockRepresentation2 = new DummyRepresentation({
|
|
49
50
|
id: "rep2",
|
|
50
51
|
bitrate: 2000000,
|
|
51
|
-
}
|
|
52
|
+
});
|
|
52
53
|
});
|
|
53
54
|
afterEach(() => {
|
|
54
55
|
vi.resetAllMocks();
|
|
@@ -118,7 +119,7 @@ describe("RepresentationScoreCalculator", () => {
|
|
|
118
119
|
expect(estimate).toBeUndefined();
|
|
119
120
|
});
|
|
120
121
|
|
|
121
|
-
it("should return LOW confidence when less than
|
|
122
|
+
it("should return LOW confidence when less than 6 segments loaded", () => {
|
|
122
123
|
calculator.addSample(mockRepresentation1, 2, 4);
|
|
123
124
|
calculator.addSample(mockRepresentation1, 2, 4);
|
|
124
125
|
|
|
@@ -127,8 +128,8 @@ describe("RepresentationScoreCalculator", () => {
|
|
|
127
128
|
});
|
|
128
129
|
|
|
129
130
|
it("should return LOW confidence when loaded duration is less than 10 seconds", () => {
|
|
130
|
-
// Add
|
|
131
|
-
for (let i = 0; i <
|
|
131
|
+
// Add 6 segments but with short duration
|
|
132
|
+
for (let i = 0; i < 6; i++) {
|
|
132
133
|
calculator.addSample(mockRepresentation1, 1, 1);
|
|
133
134
|
}
|
|
134
135
|
|
|
@@ -136,8 +137,7 @@ describe("RepresentationScoreCalculator", () => {
|
|
|
136
137
|
expect(estimate?.confidenceLevel).toBe(ScoreConfidenceLevel.LOW);
|
|
137
138
|
});
|
|
138
139
|
|
|
139
|
-
it("should return HIGH confidence when
|
|
140
|
-
// Add 5 segments with 2 seconds each (total 10 seconds)
|
|
140
|
+
it("should return HIGH confidence when 6 segments and >= 10 seconds loaded", () => {
|
|
141
141
|
for (let i = 0; i < 6; i++) {
|
|
142
142
|
calculator.addSample(mockRepresentation1, 1, 2);
|
|
143
143
|
}
|
|
@@ -146,8 +146,8 @@ describe("RepresentationScoreCalculator", () => {
|
|
|
146
146
|
expect(estimate?.confidenceLevel).toBe(ScoreConfidenceLevel.HIGH);
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
it("should return HIGH confidence when >
|
|
150
|
-
// Add
|
|
149
|
+
it("should return HIGH confidence when > 6 segments and > 10 seconds loaded", () => {
|
|
150
|
+
// Add 6 segments with 3 seconds each (total 15 seconds)
|
|
151
151
|
for (let i = 0; i < 6; i++) {
|
|
152
152
|
calculator.addSample(mockRepresentation1, 1, 3);
|
|
153
153
|
}
|
|
@@ -1,48 +1,38 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import
|
|
2
|
+
import { DummyRepresentation } from "../../../../manifest/classes/__tests__/mocks";
|
|
3
3
|
import selectOptimalRepresentation from "../select_optimal_representation";
|
|
4
4
|
|
|
5
5
|
describe("ABR - selectOptimalRepresentation", () => {
|
|
6
6
|
const fakeReps = [
|
|
7
|
-
{ bitrate: 100 },
|
|
8
|
-
{ bitrate: 1000 },
|
|
9
|
-
{ bitrate: 10000 },
|
|
10
|
-
{ bitrate: 100000 },
|
|
7
|
+
new DummyRepresentation({ bitrate: 100 }),
|
|
8
|
+
new DummyRepresentation({ bitrate: 1000 }),
|
|
9
|
+
new DummyRepresentation({ bitrate: 10000 }),
|
|
10
|
+
new DummyRepresentation({ bitrate: 100000 }),
|
|
11
11
|
];
|
|
12
12
|
|
|
13
13
|
it("should return the best representation when the optimal bitrate given is Infinity", () => {
|
|
14
|
-
expect(selectOptimalRepresentation(fakeReps
|
|
14
|
+
expect(selectOptimalRepresentation(fakeReps, Infinity)).toBe(
|
|
15
15
|
fakeReps[fakeReps.length - 1],
|
|
16
16
|
);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it("should return the best representation when both the optimal bitrate and the higher limit given are higher or equal than the highest Representation", () => {
|
|
20
|
-
expect(selectOptimalRepresentation(fakeReps
|
|
20
|
+
expect(selectOptimalRepresentation(fakeReps, 100000)).toBe(
|
|
21
21
|
fakeReps[fakeReps.length - 1],
|
|
22
22
|
);
|
|
23
|
-
expect(selectOptimalRepresentation(fakeReps
|
|
23
|
+
expect(selectOptimalRepresentation(fakeReps, 900000)).toBe(
|
|
24
24
|
fakeReps[fakeReps.length - 1],
|
|
25
25
|
);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
it("should return the worst representation when the optimal bitrate given is 0", () => {
|
|
29
|
-
expect(selectOptimalRepresentation(fakeReps
|
|
30
|
-
fakeReps[0],
|
|
31
|
-
);
|
|
29
|
+
expect(selectOptimalRepresentation(fakeReps, 0)).toBe(fakeReps[0]);
|
|
32
30
|
});
|
|
33
31
|
|
|
34
32
|
it("should return the worst representation when the optimal bitrate is lower or equal than the lowest Representation", () => {
|
|
35
|
-
expect(selectOptimalRepresentation(fakeReps
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
expect(selectOptimalRepresentation(fakeReps
|
|
39
|
-
fakeReps[0],
|
|
40
|
-
);
|
|
41
|
-
expect(selectOptimalRepresentation(fakeReps as IRepresentation[], 0)).toBe(
|
|
42
|
-
fakeReps[0],
|
|
43
|
-
);
|
|
44
|
-
expect(selectOptimalRepresentation(fakeReps as IRepresentation[], 100)).toBe(
|
|
45
|
-
fakeReps[0],
|
|
46
|
-
);
|
|
33
|
+
expect(selectOptimalRepresentation(fakeReps, 4)).toBe(fakeReps[0]);
|
|
34
|
+
expect(selectOptimalRepresentation(fakeReps, 100)).toBe(fakeReps[0]);
|
|
35
|
+
expect(selectOptimalRepresentation(fakeReps, 0)).toBe(fakeReps[0]);
|
|
36
|
+
expect(selectOptimalRepresentation(fakeReps, 100)).toBe(fakeReps[0]);
|
|
47
37
|
});
|
|
48
38
|
});
|
|
@@ -110,7 +110,7 @@ export default class RepresentationScoreCalculator {
|
|
|
110
110
|
representation,
|
|
111
111
|
ewma: currentEWMA,
|
|
112
112
|
loadedDuration: segmentDuration,
|
|
113
|
-
loadedSegments:
|
|
113
|
+
loadedSegments: 1,
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -143,7 +143,7 @@ export default class RepresentationScoreCalculator {
|
|
|
143
143
|
const { ewma, loadedSegments, loadedDuration } = this._currentRepresentationData;
|
|
144
144
|
const estimate = ewma.getEstimate();
|
|
145
145
|
const confidenceLevel =
|
|
146
|
-
loadedSegments >=
|
|
146
|
+
loadedSegments >= 6 && loadedDuration >= 10
|
|
147
147
|
? ScoreConfidenceLevel.HIGH
|
|
148
148
|
: ScoreConfidenceLevel.LOW;
|
|
149
149
|
|
|
@@ -358,9 +358,7 @@ describe("CmcdDataBuilder", () => {
|
|
|
358
358
|
speed: 1,
|
|
359
359
|
rebuffering: null,
|
|
360
360
|
});
|
|
361
|
-
builder.startMonitoringPlayback(
|
|
362
|
-
observer as Parameters<typeof builder.startMonitoringPlayback>[0],
|
|
363
|
-
);
|
|
361
|
+
builder.startMonitoringPlayback(observer);
|
|
364
362
|
|
|
365
363
|
const payload: any = builder.getCmcdDataForSegmentRequest(makeSegmentInfo());
|
|
366
364
|
const requestHeader = payload.value["CMCD-Request"];
|
|
@@ -371,9 +369,7 @@ describe("CmcdDataBuilder", () => {
|
|
|
371
369
|
it("includes pr when playback speed is not 1", () => {
|
|
372
370
|
const builder = new CmcdDataBuilder({ communicationType: "headers" });
|
|
373
371
|
const observer = makePlaybackObserver({ speed: 2, rebuffering: null });
|
|
374
|
-
builder.startMonitoringPlayback(
|
|
375
|
-
observer as Parameters<typeof builder.startMonitoringPlayback>[0],
|
|
376
|
-
);
|
|
372
|
+
builder.startMonitoringPlayback(observer);
|
|
377
373
|
|
|
378
374
|
const payload: any = builder.getCmcdDataForManifest("dash");
|
|
379
375
|
const sessionHeader = payload.value["CMCD-Session"];
|
|
@@ -383,9 +379,17 @@ describe("CmcdDataBuilder", () => {
|
|
|
383
379
|
it("does not include pr when speed is 1", () => {
|
|
384
380
|
const builder = new CmcdDataBuilder({ communicationType: "headers" });
|
|
385
381
|
const observer = makePlaybackObserver({ speed: 1, rebuffering: null });
|
|
386
|
-
builder.startMonitoringPlayback(
|
|
387
|
-
|
|
388
|
-
);
|
|
382
|
+
builder.startMonitoringPlayback(observer);
|
|
383
|
+
|
|
384
|
+
const payload: any = builder.getCmcdDataForManifest("dash");
|
|
385
|
+
const sessionHeader = payload.value["CMCD-Session"];
|
|
386
|
+
expect(sessionHeader).not.toContain("pr=");
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("does not include pr when playback speed is negative", () => {
|
|
390
|
+
const builder = new CmcdDataBuilder({ communicationType: "headers" });
|
|
391
|
+
const observer = makePlaybackObserver({ speed: -1, rebuffering: null });
|
|
392
|
+
builder.startMonitoringPlayback(observer);
|
|
389
393
|
|
|
390
394
|
const payload: any = builder.getCmcdDataForManifest("dash");
|
|
391
395
|
const sessionHeader = payload.value["CMCD-Session"];
|
|
@@ -395,23 +399,40 @@ describe("CmcdDataBuilder", () => {
|
|
|
395
399
|
it("includes su when rebuffering is active", () => {
|
|
396
400
|
const builder = new CmcdDataBuilder({ communicationType: "headers" });
|
|
397
401
|
const observer = makePlaybackObserver({ rebuffering: { timestamp: 0 }, speed: 1 });
|
|
398
|
-
builder.startMonitoringPlayback(
|
|
399
|
-
observer as Parameters<typeof builder.startMonitoringPlayback>[0],
|
|
400
|
-
);
|
|
402
|
+
builder.startMonitoringPlayback(observer);
|
|
401
403
|
|
|
402
404
|
const payload: any = builder.getCmcdDataForManifest("dash");
|
|
403
405
|
const requestHeader = payload.value["CMCD-Request"];
|
|
404
406
|
expect(requestHeader).toContain("su");
|
|
405
407
|
});
|
|
408
|
+
|
|
409
|
+
it("does not include dl or rtp when playback speed is 0", () => {
|
|
410
|
+
const builder = new CmcdDataBuilder({ communicationType: "headers" });
|
|
411
|
+
const observer = makePlaybackObserver({
|
|
412
|
+
buffered: {
|
|
413
|
+
video: [{ start: 5, end: 20 }],
|
|
414
|
+
audio: null,
|
|
415
|
+
text: null,
|
|
416
|
+
},
|
|
417
|
+
speed: 0,
|
|
418
|
+
rebuffering: null,
|
|
419
|
+
});
|
|
420
|
+
builder.startMonitoringPlayback(observer);
|
|
421
|
+
|
|
422
|
+
const payload: any = builder.getCmcdDataForSegmentRequest(makeSegmentInfo());
|
|
423
|
+
const requestHeader = payload.value["CMCD-Request"];
|
|
424
|
+
const statusHeader = payload.value["CMCD-Status"];
|
|
425
|
+
expect(requestHeader).toContain("bl=10000");
|
|
426
|
+
expect(requestHeader).not.toContain("dl=");
|
|
427
|
+
expect(statusHeader).not.toContain("rtp=");
|
|
428
|
+
});
|
|
406
429
|
});
|
|
407
430
|
|
|
408
431
|
describe("stopMonitoringPlayback", () => {
|
|
409
432
|
it("clears the playback observer so subsequent calls work without observation data", () => {
|
|
410
433
|
const builder = new CmcdDataBuilder({ communicationType: "headers" });
|
|
411
434
|
const observer = makePlaybackObserver({ speed: 2, rebuffering: null });
|
|
412
|
-
builder.startMonitoringPlayback(
|
|
413
|
-
observer as Parameters<typeof builder.startMonitoringPlayback>[0],
|
|
414
|
-
);
|
|
435
|
+
builder.startMonitoringPlayback(observer);
|
|
415
436
|
builder.stopMonitoringPlayback();
|
|
416
437
|
|
|
417
438
|
const payload: any = builder.getCmcdDataForManifest("dash");
|
|
@@ -460,5 +481,29 @@ describe("CmcdDataBuilder", () => {
|
|
|
460
481
|
}
|
|
461
482
|
}
|
|
462
483
|
});
|
|
484
|
+
|
|
485
|
+
it("escapes all backslashes and quotes in header strings", () => {
|
|
486
|
+
const builder = new CmcdDataBuilder({
|
|
487
|
+
sessionId: 's\\"id\\tail',
|
|
488
|
+
contentId: 'c\\"id\\tail',
|
|
489
|
+
communicationType: "headers",
|
|
490
|
+
});
|
|
491
|
+
const payload: any = builder.getCmcdDataForManifest("dash");
|
|
492
|
+
const sessionHeader = payload.value["CMCD-Session"];
|
|
493
|
+
expect(sessionHeader).toContain('cid="c\\\\\\"id\\\\tail"');
|
|
494
|
+
expect(sessionHeader).toContain('sid="s\\\\\\"id\\\\tail"');
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("preserves all backslashes and quotes after query decoding", () => {
|
|
498
|
+
const builder = new CmcdDataBuilder({
|
|
499
|
+
sessionId: 's\\"id\\tail',
|
|
500
|
+
contentId: 'c\\"id\\tail',
|
|
501
|
+
communicationType: "query",
|
|
502
|
+
});
|
|
503
|
+
const payload: any = builder.getCmcdDataForManifest("dash");
|
|
504
|
+
const qs = decodeURIComponent(payload.value[0][1]);
|
|
505
|
+
expect(qs).toContain('cid="c\\\\\\"id\\\\tail"');
|
|
506
|
+
expect(qs).toContain('sid="s\\\\\\"id\\\\tail"');
|
|
507
|
+
});
|
|
463
508
|
});
|
|
464
509
|
});
|
|
@@ -186,7 +186,10 @@ export default class CmcdDataBuilder {
|
|
|
186
186
|
|
|
187
187
|
const lastObservation = this._playbackObserver?.getReference().getValue();
|
|
188
188
|
props.pr =
|
|
189
|
-
lastObservation === undefined ||
|
|
189
|
+
lastObservation === undefined ||
|
|
190
|
+
!isFinite(lastObservation.speed) ||
|
|
191
|
+
lastObservation.speed < 0 ||
|
|
192
|
+
lastObservation.speed === 1
|
|
190
193
|
? undefined
|
|
191
194
|
: lastObservation.speed;
|
|
192
195
|
if (lastObservation !== undefined) {
|
|
@@ -300,7 +303,10 @@ export default class CmcdDataBuilder {
|
|
|
300
303
|
}
|
|
301
304
|
|
|
302
305
|
const precizeDeadlineMs =
|
|
303
|
-
precizeBufferLengthMs === undefined ||
|
|
306
|
+
precizeBufferLengthMs === undefined ||
|
|
307
|
+
lastObservation === undefined ||
|
|
308
|
+
!isFinite(lastObservation.speed) ||
|
|
309
|
+
lastObservation.speed <= 0
|
|
304
310
|
? undefined
|
|
305
311
|
: precizeBufferLengthMs / lastObservation.speed;
|
|
306
312
|
|
|
@@ -396,7 +402,7 @@ export default class CmcdDataBuilder {
|
|
|
396
402
|
): void => {
|
|
397
403
|
const val = props[prop];
|
|
398
404
|
if (val !== undefined) {
|
|
399
|
-
const formatted = `"${val.replace(
|
|
405
|
+
const formatted = `"${val.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
400
406
|
const toAdd = `${prop}=${formatted},`;
|
|
401
407
|
addPayload(toAdd, headerName);
|
|
402
408
|
}
|
package/src/core/entry/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Core
|
|
1
|
+
# Core Entry
|
|
2
2
|
|
|
3
3
|
| Consideration | Status |
|
|
4
4
|
| ----------------------- | ------------------------------------------ |
|
|
@@ -11,7 +11,7 @@ any file may be directly imported.
|
|
|
11
11
|
|
|
12
12
|
## Overview
|
|
13
13
|
|
|
14
|
-
The Core
|
|
14
|
+
The Core Entry serves as the entry point of the `core` code, which may optionally run in a
|
|
15
15
|
WebWorker.
|
|
16
16
|
|
|
17
17
|
When in monothreading mode, files declared here may be imported directly to make the link
|
|
@@ -78,6 +78,16 @@ describe("CoreTextDisplayerInterface", () => {
|
|
|
78
78
|
cdi.onPushedTrackError(error);
|
|
79
79
|
await expect(promise).rejects.toThrow("push failed");
|
|
80
80
|
});
|
|
81
|
+
|
|
82
|
+
it("should resolve even if the sender synchronously responds", async () => {
|
|
83
|
+
const ranges = [{ start: 0, end: 10 }];
|
|
84
|
+
messageSender = vi.fn(() => {
|
|
85
|
+
cdi.onPushedTrackSuccess(ranges);
|
|
86
|
+
});
|
|
87
|
+
cdi = new CoreTextDisplayerInterface(CONTENT_ID, messageSender);
|
|
88
|
+
|
|
89
|
+
await expect(cdi.pushTextData({} as any)).resolves.toEqual(ranges);
|
|
90
|
+
});
|
|
81
91
|
});
|
|
82
92
|
|
|
83
93
|
describe("remove", () => {
|
|
@@ -109,6 +119,16 @@ describe("CoreTextDisplayerInterface", () => {
|
|
|
109
119
|
cdi.onRemoveError(error);
|
|
110
120
|
await expect(promise).rejects.toThrow("remove failed");
|
|
111
121
|
});
|
|
122
|
+
|
|
123
|
+
it("should resolve even if the sender synchronously responds", async () => {
|
|
124
|
+
const ranges = [{ start: 5, end: 20 }];
|
|
125
|
+
messageSender = vi.fn(() => {
|
|
126
|
+
cdi.onRemoveSuccess(ranges);
|
|
127
|
+
});
|
|
128
|
+
cdi = new CoreTextDisplayerInterface(CONTENT_ID, messageSender);
|
|
129
|
+
|
|
130
|
+
await expect(cdi.remove(5, 20)).resolves.toEqual(ranges);
|
|
131
|
+
});
|
|
112
132
|
});
|
|
113
133
|
|
|
114
134
|
describe("reset", () => {
|
|
@@ -257,11 +257,6 @@ export default class ContentPreparer {
|
|
|
257
257
|
manifestFetcher.addEventListener(
|
|
258
258
|
"error",
|
|
259
259
|
(err: unknown) => {
|
|
260
|
-
sendMessage({
|
|
261
|
-
type: CoreMessageType.Error,
|
|
262
|
-
contentId,
|
|
263
|
-
value: formatErrorForSender(err),
|
|
264
|
-
});
|
|
265
260
|
rej(err);
|
|
266
261
|
},
|
|
267
262
|
contentCanceller.signal,
|
|
@@ -336,7 +331,9 @@ export default class ContentPreparer {
|
|
|
336
331
|
return Promise.reject(new Error("CP: No content anymore"));
|
|
337
332
|
}
|
|
338
333
|
this._currentContent.trackChoiceSetter.reset();
|
|
334
|
+
this._currentContent.coreTextSender?.stop("ContentPreparer MediaSource reload");
|
|
339
335
|
this._currentMediaSourceCanceller = new TaskCanceller("ContentPreparer MediaSource");
|
|
336
|
+
this._currentMediaSourceCanceller.linkToSignal(this._contentCanceller.signal);
|
|
340
337
|
|
|
341
338
|
const [mediaSourceInterface, segmentSinksStore, coreTextSender] =
|
|
342
339
|
createMediaSourceInterfaceAndSegmentSinksStore(
|
|
@@ -1221,6 +1221,10 @@ function sendThumbnailData(
|
|
|
1221
1221
|
msg.value.time,
|
|
1222
1222
|
).then(
|
|
1223
1223
|
(result) => {
|
|
1224
|
+
// Multiple thumbnail requests can share the same fetched payload.
|
|
1225
|
+
// Transfer a copy here so replying to one request does not detach the
|
|
1226
|
+
// ArrayBuffer that still has to be sent to another requester.
|
|
1227
|
+
const data = result.data.slice(0);
|
|
1224
1228
|
sendMessage(
|
|
1225
1229
|
{
|
|
1226
1230
|
type: CoreMessageType.ThumbnailDataResponse,
|
|
@@ -1228,10 +1232,10 @@ function sendThumbnailData(
|
|
|
1228
1232
|
value: {
|
|
1229
1233
|
status: "success",
|
|
1230
1234
|
requestId: msg.value.requestId,
|
|
1231
|
-
data: result,
|
|
1235
|
+
data: { ...result, data },
|
|
1232
1236
|
},
|
|
1233
1237
|
},
|
|
1234
|
-
[
|
|
1238
|
+
[data],
|
|
1235
1239
|
);
|
|
1236
1240
|
},
|
|
1237
1241
|
(err) => {
|
|
@@ -49,12 +49,12 @@ export default class CoreTextDisplayerInterface implements ITextDisplayerInterfa
|
|
|
49
49
|
*/
|
|
50
50
|
public pushTextData(infos: ITextDisplayerData): Promise<IRange[]> {
|
|
51
51
|
return new Promise((resolve, reject) => {
|
|
52
|
+
this._queues.pushTextData.push({ resolve, reject });
|
|
52
53
|
this._messageSender({
|
|
53
54
|
type: CoreMessageType.PushTextData,
|
|
54
55
|
contentId: this._contentId,
|
|
55
56
|
value: infos,
|
|
56
57
|
});
|
|
57
|
-
this._queues.pushTextData.push({ resolve, reject });
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -63,12 +63,12 @@ export default class CoreTextDisplayerInterface implements ITextDisplayerInterfa
|
|
|
63
63
|
*/
|
|
64
64
|
public remove(start: number, end: number): Promise<IRange[]> {
|
|
65
65
|
return new Promise((resolve, reject) => {
|
|
66
|
+
this._queues.remove.push({ resolve, reject });
|
|
66
67
|
this._messageSender({
|
|
67
68
|
type: CoreMessageType.RemoveTextData,
|
|
68
69
|
contentId: this._contentId,
|
|
69
70
|
value: { start, end },
|
|
70
71
|
});
|
|
71
|
-
this._queues.remove.push({ resolve, reject });
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -153,7 +153,7 @@ export default class CoreTextDisplayerInterface implements ITextDisplayerInterfa
|
|
|
153
153
|
public onRemoveError(err: Error): void {
|
|
154
154
|
const element = this._queues.remove.shift();
|
|
155
155
|
if (element === undefined) {
|
|
156
|
-
log.error("text", "
|
|
156
|
+
log.error("text", "remove error for inexistant operation");
|
|
157
157
|
return;
|
|
158
158
|
}
|
|
159
159
|
element.reject(err);
|
|
@@ -96,7 +96,7 @@ function makeLoadResponse(overrides: Record<string, unknown> = {}) {
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
describe
|
|
99
|
+
describe("ManifestFetcher", () => {
|
|
100
100
|
beforeEach(() => {
|
|
101
101
|
mockConfigGetCurrent.mockReturnValue(DEFAULT_CONFIG);
|
|
102
102
|
mockFormatError.mockImplementation((e: unknown) => e);
|
|
@@ -488,6 +488,51 @@ describe.skip("ManifestFetcher", () => {
|
|
|
488
488
|
expect.anything(),
|
|
489
489
|
);
|
|
490
490
|
});
|
|
491
|
+
|
|
492
|
+
it("updates the stored manifest URLs", async () => {
|
|
493
|
+
const manifest = new MockManifest();
|
|
494
|
+
const fetcher = new ManifestFetcher(
|
|
495
|
+
["http://example.com/manifest"],
|
|
496
|
+
makePipelines(),
|
|
497
|
+
makeSettings({ initialManifest: manifest }),
|
|
498
|
+
);
|
|
499
|
+
fetcher.start();
|
|
500
|
+
await flushPromises();
|
|
501
|
+
|
|
502
|
+
fetcher.updateContentUrls(["http://new.com/manifest"], false);
|
|
503
|
+
|
|
504
|
+
expect(fetcher).toHaveProperty("_manifestUrls", ["http://new.com/manifest"]);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it("uses the new URL once for the next full refresh even when refreshNow is false", async () => {
|
|
508
|
+
const manifest = new MockManifest();
|
|
509
|
+
mockLoadManifest.mockResolvedValue(makeLoadResponse());
|
|
510
|
+
mockParseManifest.mockReturnValue({ manifest: new MockManifest() });
|
|
511
|
+
|
|
512
|
+
const fetcher = new ManifestFetcher(
|
|
513
|
+
["http://example.com/manifest"],
|
|
514
|
+
makePipelines(),
|
|
515
|
+
makeSettings({ initialManifest: manifest }),
|
|
516
|
+
);
|
|
517
|
+
fetcher.start();
|
|
518
|
+
await flushPromises();
|
|
519
|
+
|
|
520
|
+
fetcher.updateContentUrls(["http://new.com/manifest"], false);
|
|
521
|
+
fetcher.scheduleManualRefresh({
|
|
522
|
+
enablePartialRefresh: false,
|
|
523
|
+
delay: 0,
|
|
524
|
+
canUseUnsafeMode: false,
|
|
525
|
+
});
|
|
526
|
+
await sleep(0);
|
|
527
|
+
await flushPromises();
|
|
528
|
+
|
|
529
|
+
expect(mockLoadManifest).toHaveBeenCalledTimes(1);
|
|
530
|
+
expect(mockLoadManifest).toHaveBeenLastCalledWith(
|
|
531
|
+
"http://new.com/manifest",
|
|
532
|
+
expect.anything(),
|
|
533
|
+
expect.anything(),
|
|
534
|
+
);
|
|
535
|
+
});
|
|
491
536
|
});
|
|
492
537
|
|
|
493
538
|
describe("automatic refresh scheduling", () => {
|
|
@@ -694,7 +739,9 @@ describe.skip("ManifestFetcher", () => {
|
|
|
694
739
|
});
|
|
695
740
|
|
|
696
741
|
const refreshManifest = new MockManifest();
|
|
697
|
-
mockLoadManifest.mockResolvedValue(
|
|
742
|
+
mockLoadManifest.mockResolvedValue(
|
|
743
|
+
makeLoadResponse({ sendingTime: undefined, receivedTime: undefined }),
|
|
744
|
+
);
|
|
698
745
|
mockParseManifest.mockReturnValue({ manifest: refreshManifest });
|
|
699
746
|
|
|
700
747
|
const fetcher = new ManifestFetcher(
|
|
@@ -715,7 +762,9 @@ describe.skip("ManifestFetcher", () => {
|
|
|
715
762
|
await flushPromises();
|
|
716
763
|
|
|
717
764
|
// Fallback full refresh fires after FAILED_PARTIAL_UPDATE_MANIFEST_REFRESH_DELAY
|
|
718
|
-
vi.advanceTimersByTime(
|
|
765
|
+
vi.advanceTimersByTime(
|
|
766
|
+
DEFAULT_CONFIG.FAILED_PARTIAL_UPDATE_MANIFEST_REFRESH_DELAY + 1,
|
|
767
|
+
);
|
|
719
768
|
await vi.waitFor(noop, { timeout: 0 }); // real delay
|
|
720
769
|
await flushPromises();
|
|
721
770
|
|
|
@@ -177,6 +177,12 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
|
|
|
177
177
|
* will be triggered immediately.
|
|
178
178
|
*/
|
|
179
179
|
public updateContentUrls(urls: string[] | undefined, refreshNow: boolean): void {
|
|
180
|
+
// Keep track of the application-provided content URLs.
|
|
181
|
+
this._manifestUrls = urls;
|
|
182
|
+
// Indicate that we want to prioritize for the next refresh.
|
|
183
|
+
// Once a refreshed Manifest has been parsed we still let the Manifest's own
|
|
184
|
+
// URL list drive subsequent refreshes (e.g. if the transport/parser updated
|
|
185
|
+
// it on purpose).
|
|
180
186
|
this._prioritizedContentUrl = urls?.[0] ?? undefined;
|
|
181
187
|
if (refreshNow) {
|
|
182
188
|
this.scheduleManualRefresh({
|
|
@@ -608,10 +614,16 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
|
|
|
608
614
|
let fullRefresh: boolean;
|
|
609
615
|
let refreshURL: string | undefined;
|
|
610
616
|
if (this._prioritizedContentUrl !== null) {
|
|
617
|
+
// `updateContentUrls` explicitly requested that the next refresh uses that
|
|
618
|
+
// URL. This override is one-shot on purpose.
|
|
611
619
|
fullRefresh = true;
|
|
612
620
|
refreshURL = this._prioritizedContentUrl;
|
|
613
621
|
this._prioritizedContentUrl = null;
|
|
614
622
|
} else {
|
|
623
|
+
// Outside of that explicit one-shot override, prefer the URLs currently
|
|
624
|
+
// exposed by the Manifest itself. This allows parser/content-level URL
|
|
625
|
+
// updates (e.g. redirects or manifest-provided alternatives) to stay the
|
|
626
|
+
// source of truth after the refresh completed.
|
|
615
627
|
fullRefresh = !enablePartialRefresh || manifestUpdateUrl === undefined;
|
|
616
628
|
refreshURL = fullRefresh ? manifest.getUrls()[0] : manifestUpdateUrl;
|
|
617
629
|
}
|
|
@@ -277,6 +277,76 @@ describe("createThumbnailFetcher", () => {
|
|
|
277
277
|
// p1 should reject
|
|
278
278
|
await expect(p1).rejects.toBeInstanceOf(CancellationError);
|
|
279
279
|
});
|
|
280
|
+
|
|
281
|
+
it("does not pass the first caller's cancellation signal to the shared loader", async () => {
|
|
282
|
+
let loadSignal: CancellationSignal | undefined;
|
|
283
|
+
let resolveRequest!: (v: IRequestedData<ArrayBuffer>) => void;
|
|
284
|
+
const pipeline = makePipeline(
|
|
285
|
+
(
|
|
286
|
+
_cdnMetadata,
|
|
287
|
+
_segment,
|
|
288
|
+
_options,
|
|
289
|
+
cancellationSignal: CancellationSignal,
|
|
290
|
+
): Promise<IRequestedData<ArrayBuffer>> => {
|
|
291
|
+
loadSignal = cancellationSignal;
|
|
292
|
+
return new Promise((res) => {
|
|
293
|
+
resolveRequest = res;
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
mockScheduleRequestWithCdns.mockImplementation(
|
|
299
|
+
(
|
|
300
|
+
cdns: ICdnMetadata[] | null,
|
|
301
|
+
_cdnPrioritizer: CdnPrioritizer | null,
|
|
302
|
+
performRequest: (
|
|
303
|
+
cdn: ICdnMetadata | null,
|
|
304
|
+
cancellationSignal: CancellationSignal,
|
|
305
|
+
) => Promise<IRequestedData<ArrayBuffer>>,
|
|
306
|
+
_options: IBackoffSettings,
|
|
307
|
+
globalCancellationSignal: CancellationSignal,
|
|
308
|
+
): Promise<IRequestedData<ArrayBuffer>> => {
|
|
309
|
+
return performRequest(cdns?.[0] ?? null, globalCancellationSignal);
|
|
310
|
+
},
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const fetcher = createThumbnailFetcher(pipeline, null);
|
|
314
|
+
const canceller1 = new TaskCanceller("test1");
|
|
315
|
+
const canceller2 = new TaskCanceller("test2");
|
|
316
|
+
const ctx = makeThumbnailContext();
|
|
317
|
+
|
|
318
|
+
const p1 = fetcher(ctx, canceller1.signal);
|
|
319
|
+
const p2 = fetcher(ctx, canceller2.signal);
|
|
320
|
+
|
|
321
|
+
expect(mockScheduleRequestWithCdns).toHaveBeenCalledTimes(1);
|
|
322
|
+
expect(loadSignal).not.toBe(canceller1.signal);
|
|
323
|
+
expect(loadSignal).not.toBe(canceller2.signal);
|
|
324
|
+
|
|
325
|
+
canceller1.cancel("only first cancels");
|
|
326
|
+
expect(loadSignal?.isCancelled()).toBe(false);
|
|
327
|
+
|
|
328
|
+
resolveRequest({
|
|
329
|
+
responseData: new Uint8Array([4, 7, 7]).buffer,
|
|
330
|
+
size: 3,
|
|
331
|
+
requestDuration: 500,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
await expect(p1).rejects.toBeInstanceOf(CancellationError);
|
|
335
|
+
await expect(p2).resolves.toEqual({
|
|
336
|
+
data: new Uint8Array([4, 7, 7]).buffer,
|
|
337
|
+
mimeType: "image/png",
|
|
338
|
+
thumbnails: [
|
|
339
|
+
{
|
|
340
|
+
height: 720,
|
|
341
|
+
width: 1280,
|
|
342
|
+
offsetX: 0,
|
|
343
|
+
offsetY: 0,
|
|
344
|
+
start: 0,
|
|
345
|
+
end: Number.MAX_SAFE_INTEGER,
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
});
|
|
349
|
+
});
|
|
280
350
|
});
|
|
281
351
|
|
|
282
352
|
describe("error handling", () => {
|