@nomercy-entertainment/nomercy-player-core 2.0.2 → 2.0.3
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 +10 -0
- package/dist/adapters/quality/display-range.d.ts +33 -0
- package/dist/adapters/quality/display-range.d.ts.map +1 -0
- package/dist/adapters/quality/display-range.js +46 -0
- package/dist/adapters/quality/display-range.js.map +1 -0
- package/dist/adapters/quality/hdr-policy.d.ts +80 -0
- package/dist/adapters/quality/hdr-policy.d.ts.map +1 -0
- package/dist/adapters/quality/hdr-policy.js +75 -0
- package/dist/adapters/quality/hdr-policy.js.map +1 -0
- package/dist/adapters/quality/size-policy.d.ts +57 -0
- package/dist/adapters/quality/size-policy.d.ts.map +1 -0
- package/dist/adapters/quality/size-policy.js +114 -0
- package/dist/adapters/quality/size-policy.js.map +1 -0
- package/dist/adapters/retry-policy/IRetryPolicy.d.ts +5 -2
- package/dist/adapters/retry-policy/IRetryPolicy.d.ts.map +1 -1
- package/dist/core/kit-version.js +1 -1
- package/dist/core/mixins/device.d.ts +2 -0
- package/dist/core/mixins/device.d.ts.map +1 -1
- package/dist/core/mixins/device.js +4 -0
- package/dist/core/mixins/device.js.map +1 -1
- package/dist/core/mixins/lifecycle.d.ts.map +1 -1
- package/dist/core/mixins/lifecycle.js +3 -0
- package/dist/core/mixins/lifecycle.js.map +1 -1
- package/dist/core/mixins/queue.d.ts +14 -0
- package/dist/core/mixins/queue.d.ts.map +1 -1
- package/dist/core/mixins/queue.js +21 -3
- package/dist/core/mixins/queue.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/testing/stub-player.d.ts.map +1 -1
- package/dist/testing/stub-player.js +1 -0
- package/dist/testing/stub-player.js.map +1 -1
- package/dist/types/config.d.ts +11 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/device.d.ts +7 -0
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/player.d.ts +7 -1
- package/dist/types/player.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.0.3] — 2026-07-31
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `item(target)` called between `setup()` and `ready()` addressed a queue the configured `playlist` had not seeded yet, so the cursor never moved and the choice was silently lost — a deep link naming a start item opened on the first item instead. The choice is now parked and applied the moment the pipeline seeds the queue.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Screen-aware quality decisions: `detectDisplayHdr()`, `HdrOnSdrFallback` and the `hdrOnSdr` config field, plus `sizeAbrCeiling()` for capping the ladder to the space the picture is drawn in.
|
|
14
|
+
|
|
5
15
|
## [2.0.1] — 2026-07-18
|
|
6
16
|
|
|
7
17
|
First published stable of the v2 line (2.0.0 was withdrawn before release, so 2.0.1 is the first stable on npm). The core now publishes under the `latest` dist-tag, so a plain `npm install @nomercy-entertainment/nomercy-player-core` resolves `2.0.1` instead of the stale `2.0.0-rc.2` that predated `publishConfig.tag`. This promotes the `2.0.0-rc.33` surface to stable with no runtime or signature changes; consumers already installing `@rc` are running the same code.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this screen can actually show HDR.
|
|
3
|
+
*
|
|
4
|
+
* `types/tracks.ts` already named the query — `matchMedia('(dynamic-range: high)')`
|
|
5
|
+
* — as how this would be answered, and nothing ever asked it. So `hdrDecision` had
|
|
6
|
+
* no input and an HDR ladder played on an SDR panel unchallenged.
|
|
7
|
+
*
|
|
8
|
+
* `video-dynamic-range` is asked first because it is the one that describes the
|
|
9
|
+
* VIDEO plane. `dynamic-range` answers for the page, and a browser can composite
|
|
10
|
+
* HDR video on a screen whose page rendering it reports as standard.
|
|
11
|
+
*/
|
|
12
|
+
export interface DisplayRangeProbe {
|
|
13
|
+
/** Answers a CSS media query. Injected so the decision is testable. */
|
|
14
|
+
matches: (query: string) => boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A probe backed by the real browser, or null when there is no browser.
|
|
18
|
+
*
|
|
19
|
+
* Null rather than a probe that always says no: "there is no screen to ask" and
|
|
20
|
+
* "the screen said no" are different answers, and only the first one should leave
|
|
21
|
+
* a caller free to skip the whole question.
|
|
22
|
+
*/
|
|
23
|
+
export declare function browserDisplayRangeProbe(): DisplayRangeProbe | null;
|
|
24
|
+
/**
|
|
25
|
+
* True only when the screen says so.
|
|
26
|
+
*
|
|
27
|
+
* Defaults to SDR when nothing can be asked, because that is the assumption whose
|
|
28
|
+
* failure is visible and recoverable: an SDR decision on an HDR panel shows a
|
|
29
|
+
* correct-looking picture at a lower rung, and an HDR decision on an SDR panel shows
|
|
30
|
+
* the washed-out one a viewer reported.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectDisplayHdr(probe?: DisplayRangeProbe | null): boolean;
|
|
33
|
+
//# sourceMappingURL=display-range.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-range.d.ts","sourceRoot":"","sources":["../../../src/adapters/quality/display-range.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IACjC,uEAAuE;IACvE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACpC;AAKD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,iBAAiB,GAAG,IAAI,CAgBnE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,iBAAiB,GAAG,IAAiC,GAAG,OAAO,CAKtG"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// -----------------------------------------------------------------------------
|
|
2
|
+
// Copyright (c) NoMercy Entertainment
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0. See LICENSE for details.
|
|
5
|
+
//
|
|
6
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
// -----------------------------------------------------------------------------
|
|
8
|
+
const VIDEO_RANGE_QUERY = '(video-dynamic-range: high)';
|
|
9
|
+
const PAGE_RANGE_QUERY = '(dynamic-range: high)';
|
|
10
|
+
/**
|
|
11
|
+
* A probe backed by the real browser, or null when there is no browser.
|
|
12
|
+
*
|
|
13
|
+
* Null rather than a probe that always says no: "there is no screen to ask" and
|
|
14
|
+
* "the screen said no" are different answers, and only the first one should leave
|
|
15
|
+
* a caller free to skip the whole question.
|
|
16
|
+
*/
|
|
17
|
+
export function browserDisplayRangeProbe() {
|
|
18
|
+
if (typeof globalThis.matchMedia !== 'function')
|
|
19
|
+
return null;
|
|
20
|
+
return {
|
|
21
|
+
matches: (query) => {
|
|
22
|
+
try {
|
|
23
|
+
return globalThis.matchMedia(query).matches;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// A browser that does not understand the query throws rather than
|
|
27
|
+
// returning false, and an unknown query is not a claim about the screen.
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* True only when the screen says so.
|
|
35
|
+
*
|
|
36
|
+
* Defaults to SDR when nothing can be asked, because that is the assumption whose
|
|
37
|
+
* failure is visible and recoverable: an SDR decision on an HDR panel shows a
|
|
38
|
+
* correct-looking picture at a lower rung, and an HDR decision on an SDR panel shows
|
|
39
|
+
* the washed-out one a viewer reported.
|
|
40
|
+
*/
|
|
41
|
+
export function detectDisplayHdr(probe = browserDisplayRangeProbe()) {
|
|
42
|
+
if (probe === null)
|
|
43
|
+
return false;
|
|
44
|
+
return probe.matches(VIDEO_RANGE_QUERY) || probe.matches(PAGE_RANGE_QUERY);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=display-range.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-range.js","sourceRoot":"","sources":["../../../src/adapters/quality/display-range.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,uCAAuC;AACvC,EAAE;AACF,4EAA4E;AAC5E,EAAE;AACF,uCAAuC;AACvC,gFAAgF;AAkBhF,MAAM,iBAAiB,GAAG,6BAA6B,CAAC;AACxD,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB;IACvC,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU;QAC9C,OAAO,IAAI,CAAC;IAEb,OAAO;QACN,OAAO,EAAE,CAAC,KAAa,EAAW,EAAE;YACnC,IAAI,CAAC;gBACJ,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;YAC7C,CAAC;YACD,MAAM,CAAC;gBACN,kEAAkE;gBAClE,yEAAyE;gBACzE,OAAO,KAAK,CAAC;YACd,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAkC,wBAAwB,EAAE;IAC5F,IAAI,KAAK,KAAK,IAAI;QACjB,OAAO,KAAK,CAAC;IAEd,OAAO,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { QualityLevel } from '../../types/tracks.js';
|
|
2
|
+
/**
|
|
3
|
+
* What to do when HDR content meets an SDR screen.
|
|
4
|
+
*
|
|
5
|
+
* The web had `dynamicRange` on QualityLevel and nothing that read it, so an HDR
|
|
6
|
+
* ladder played on an SDR display with nothing consulted and nothing converted —
|
|
7
|
+
* a washed-out picture, and a viewer with good bandwidth gets the WORST one,
|
|
8
|
+
* because adaptation climbs into the HDR rungs on its own.
|
|
9
|
+
*
|
|
10
|
+
* This is the oracle for the native port rather than the other way round: the
|
|
11
|
+
* native trio must match the web, so new behaviour lands here first and the Kotlin
|
|
12
|
+
* and Swift sides conform. Written to mirror `HdrPolicy.kt` decision for decision
|
|
13
|
+
* so the two cannot drift.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* What a consumer wants when the backend cannot convert and there is no SDR rung.
|
|
17
|
+
*
|
|
18
|
+
* A choice rather than a rule, because both answers are defensible and the library
|
|
19
|
+
* is not the one who has to explain either to a viewer: `play` shows a washed-out
|
|
20
|
+
* picture, `refuse` shows none. Tone-mapping is preferred over both wherever the
|
|
21
|
+
* backend can do it, so this only decides the case where it cannot.
|
|
22
|
+
*/
|
|
23
|
+
export type HdrOnSdrFallback = 'play' | 'refuse';
|
|
24
|
+
export type HdrDecision = {
|
|
25
|
+
kind: 'as-is';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Ask the backend to convert HDR to SDR as it decodes.
|
|
29
|
+
*
|
|
30
|
+
* Preferred wherever the backend can do it, on every platform that can: it needs
|
|
31
|
+
* no second rendition on the server and no bandwidth decision, and the picture is
|
|
32
|
+
* right rather than merely watchable. It costs work per frame, deliberately.
|
|
33
|
+
*/
|
|
34
|
+
| {
|
|
35
|
+
kind: 'tone-map';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Pin adaptation to the best SDR rung the ladder has.
|
|
39
|
+
*
|
|
40
|
+
* Ahead of tone-mapping when a rung exists, because a natively-SDR stream is
|
|
41
|
+
* correct without spending anything per frame.
|
|
42
|
+
*/
|
|
43
|
+
| {
|
|
44
|
+
kind: 'cap-to';
|
|
45
|
+
level: QualityLevel;
|
|
46
|
+
}
|
|
47
|
+
/** No SDR rung, no converter, and the consumer chose to show it anyway. */
|
|
48
|
+
| {
|
|
49
|
+
kind: 'play-unconverted';
|
|
50
|
+
}
|
|
51
|
+
/** No SDR rung, no converter, and the consumer chose not to. */
|
|
52
|
+
| {
|
|
53
|
+
kind: 'refuse';
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The best SDR rung, or null when there is nothing to cap to.
|
|
57
|
+
*
|
|
58
|
+
* Null on an HDR display, and null on a ladder with no HDR in it — a cap that
|
|
59
|
+
* changes nothing still narrows what adaptation may pick if anything about the
|
|
60
|
+
* comparison is subtly wrong, and the safest constraint is the one not applied.
|
|
61
|
+
*
|
|
62
|
+
* Ordered height first then bitrate, the same ordering a quality menu uses.
|
|
63
|
+
* Falling back to the lowest rung would be a worse picture than the display can
|
|
64
|
+
* show, which is the opposite mistake.
|
|
65
|
+
*/
|
|
66
|
+
export declare function hdrAbrCeiling(levels: ReadonlyArray<QualityLevel>, displayHdr: boolean): QualityLevel | null;
|
|
67
|
+
/** Every rung is HDR and the display is not: there is nothing to cap to. */
|
|
68
|
+
export declare function isHdrUnplayable(levels: ReadonlyArray<QualityLevel>, displayHdr: boolean): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* The decision, cheapest correct answer first.
|
|
71
|
+
*
|
|
72
|
+
* Pure and taking booleans rather than a Screen or a backend, so every branch is
|
|
73
|
+
* provable without a device. The device answers two questions and this answers the
|
|
74
|
+
* rest.
|
|
75
|
+
*
|
|
76
|
+
* `backendCanToneMap` is not a platform constant: the same browser answers
|
|
77
|
+
* differently by codec, by decoder, and by whether hardware decoding is in use.
|
|
78
|
+
*/
|
|
79
|
+
export declare function hdrDecision(levels: ReadonlyArray<QualityLevel>, displayHdr: boolean, backendCanToneMap: boolean, fallback?: HdrOnSdrFallback): HdrDecision;
|
|
80
|
+
//# sourceMappingURL=hdr-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hdr-policy.d.ts","sourceRoot":"","sources":["../../../src/adapters/quality/hdr-policy.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjD,MAAM,MAAM,WAAW,GAElB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE;AACrB;;;;;;GAMG;GACA;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE;AACvB;;;;;GAKG;GACA;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE;AAC1C,2EAA2E;GACxE;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE;AAC/B,gEAAgE;GAC7D;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAMvB;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,EAAE,UAAU,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAuB3G;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAEjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,EACnC,UAAU,EAAE,OAAO,EACnB,iBAAiB,EAAE,OAAO,EAC1B,QAAQ,GAAE,gBAAyB,GACjC,WAAW,CAsBb"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// -----------------------------------------------------------------------------
|
|
2
|
+
// Copyright (c) NoMercy Entertainment
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0. See LICENSE for details.
|
|
5
|
+
//
|
|
6
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
// -----------------------------------------------------------------------------
|
|
8
|
+
function isHdr(level) {
|
|
9
|
+
return level.dynamicRange === 'hdr';
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The best SDR rung, or null when there is nothing to cap to.
|
|
13
|
+
*
|
|
14
|
+
* Null on an HDR display, and null on a ladder with no HDR in it — a cap that
|
|
15
|
+
* changes nothing still narrows what adaptation may pick if anything about the
|
|
16
|
+
* comparison is subtly wrong, and the safest constraint is the one not applied.
|
|
17
|
+
*
|
|
18
|
+
* Ordered height first then bitrate, the same ordering a quality menu uses.
|
|
19
|
+
* Falling back to the lowest rung would be a worse picture than the display can
|
|
20
|
+
* show, which is the opposite mistake.
|
|
21
|
+
*/
|
|
22
|
+
export function hdrAbrCeiling(levels, displayHdr) {
|
|
23
|
+
if (displayHdr)
|
|
24
|
+
return null;
|
|
25
|
+
if (!levels.some(isHdr))
|
|
26
|
+
return null;
|
|
27
|
+
const sdr = levels.filter(level => !isHdr(level));
|
|
28
|
+
if (sdr.length === 0)
|
|
29
|
+
return null;
|
|
30
|
+
// `height` is optional on the web's QualityLevel — an audio-only rendition has
|
|
31
|
+
// none, and so does a ladder a backend has only half described. Nought sorts it
|
|
32
|
+
// below every real rung rather than above them, which is what an unknown height
|
|
33
|
+
// deserves when the question is "the best picture this screen can show".
|
|
34
|
+
return sdr.reduce((best, level) => {
|
|
35
|
+
const height = level.height ?? 0;
|
|
36
|
+
const bestHeight = best.height ?? 0;
|
|
37
|
+
if (height !== bestHeight)
|
|
38
|
+
return height > bestHeight ? level : best;
|
|
39
|
+
return (level.bitrate ?? 0) > (best.bitrate ?? 0) ? level : best;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Every rung is HDR and the display is not: there is nothing to cap to. */
|
|
43
|
+
export function isHdrUnplayable(levels, displayHdr) {
|
|
44
|
+
return !displayHdr && levels.length > 0 && levels.every(isHdr);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The decision, cheapest correct answer first.
|
|
48
|
+
*
|
|
49
|
+
* Pure and taking booleans rather than a Screen or a backend, so every branch is
|
|
50
|
+
* provable without a device. The device answers two questions and this answers the
|
|
51
|
+
* rest.
|
|
52
|
+
*
|
|
53
|
+
* `backendCanToneMap` is not a platform constant: the same browser answers
|
|
54
|
+
* differently by codec, by decoder, and by whether hardware decoding is in use.
|
|
55
|
+
*/
|
|
56
|
+
export function hdrDecision(levels, displayHdr, backendCanToneMap, fallback = 'play') {
|
|
57
|
+
if (displayHdr)
|
|
58
|
+
return { kind: 'as-is' };
|
|
59
|
+
const ceiling = hdrAbrCeiling(levels, displayHdr);
|
|
60
|
+
if (ceiling !== null) {
|
|
61
|
+
return {
|
|
62
|
+
kind: 'cap-to',
|
|
63
|
+
level: ceiling,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// An ordinary SDR item on an SDR screen. An empty ladder lands here too, which
|
|
67
|
+
// is right — a backend that has not enumerated its rungs yet, or a progressive
|
|
68
|
+
// file with no ladder, must not be refused.
|
|
69
|
+
if (!isHdrUnplayable(levels, displayHdr))
|
|
70
|
+
return { kind: 'as-is' };
|
|
71
|
+
if (backendCanToneMap)
|
|
72
|
+
return { kind: 'tone-map' };
|
|
73
|
+
return fallback === 'play' ? { kind: 'play-unconverted' } : { kind: 'refuse' };
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=hdr-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hdr-policy.js","sourceRoot":"","sources":["../../../src/adapters/quality/hdr-policy.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,uCAAuC;AACvC,EAAE;AACF,4EAA4E;AAC5E,EAAE;AACF,uCAAuC;AACvC,gFAAgF;AAmDhF,SAAS,KAAK,CAAC,KAAmB;IACjC,OAAO,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC;AACrC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,MAAmC,EAAE,UAAmB;IACrF,IAAI,UAAU;QACb,OAAO,IAAI,CAAC;IACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QACtB,OAAO,IAAI,CAAC;IAEb,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAClD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QACnB,OAAO,IAAI,CAAC;IAEb,+EAA+E;IAC/E,gFAAgF;IAChF,gFAAgF;IAChF,yEAAyE;IACzE,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QACjC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QAEpC,IAAI,MAAM,KAAK,UAAU;YACxB,OAAO,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAE3C,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAClE,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,MAAmC,EAAE,UAAmB;IACvF,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAC1B,MAAmC,EACnC,UAAmB,EACnB,iBAA0B,EAC1B,WAA6B,MAAM;IAEnC,IAAI,UAAU;QACb,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAE1B,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO;YACN,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,OAAO;SACd,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,+EAA+E;IAC/E,4CAA4C;IAC5C,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAE1B,IAAI,iBAAiB;QACpB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAE7B,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAChF,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { QualityLevel } from '../../types/tracks.js';
|
|
2
|
+
/**
|
|
3
|
+
* What to do when a 4K rung meets a 740px-wide player.
|
|
4
|
+
*
|
|
5
|
+
* Adaptation climbed the ladder on bandwidth alone, so a pane a fifth of the
|
|
6
|
+
* screen wide pulled a 3840-wide rendition and threw most of it away in the
|
|
7
|
+
* scaler — paid for three times over, in bytes, in decode and in battery.
|
|
8
|
+
*
|
|
9
|
+
* `hls.js` ships `capLevelToPlayerSize` for this and it stays off deliberately:
|
|
10
|
+
* `autoLevelCapping` is already written from `hdr-policy`'s ceiling, and two
|
|
11
|
+
* writers to one property is a bug waiting for a resize. The rule lives here so
|
|
12
|
+
* the Kotlin and Swift ports mirror one decision rather than one decision and
|
|
13
|
+
* one library's private heuristic.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The smallest rung that still covers the pane, or null when nothing should cap.
|
|
17
|
+
*
|
|
18
|
+
* Smallest rather than largest: a rung above the pane is bytes the scaler
|
|
19
|
+
* discards, and a rung below it upscales into a soft picture, which is the
|
|
20
|
+
* mistake a viewer actually notices.
|
|
21
|
+
*
|
|
22
|
+
* Null on an unmeasured pane, on an empty ladder, and when the covering rung is
|
|
23
|
+
* already the tallest thing on offer — a cap that changes nothing still narrows
|
|
24
|
+
* what adaptation may pick if anything about the comparison is subtly wrong, and
|
|
25
|
+
* the safest constraint is the one not applied. The same reason `hdrAbrCeiling`
|
|
26
|
+
* returns null on a ladder with no HDR in it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sizeAbrCeiling(levels: ReadonlyArray<QualityLevel>, paneWidthPx: number, paneHeightPx: number): QualityLevel | null;
|
|
29
|
+
/**
|
|
30
|
+
* The pane in device pixels, which is the unit a rendition is measured in.
|
|
31
|
+
*
|
|
32
|
+
* CSS pixels understate a phone or a HiDPI laptop by two or three times, so a
|
|
33
|
+
* ceiling computed from them caps a retina viewer to a picture visibly softer
|
|
34
|
+
* than the panel can draw. Mirrors `hls.js`'s own `contentScaleFactor`.
|
|
35
|
+
*
|
|
36
|
+
* The port deliberately does NOT do this: a Compose or UIKit surface is already
|
|
37
|
+
* handed its size in real pixels, so a second multiplication there would cap a
|
|
38
|
+
* rung too high.
|
|
39
|
+
*/
|
|
40
|
+
export declare function panePixels(element: {
|
|
41
|
+
getBoundingClientRect(): {
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
};
|
|
45
|
+
} | null | undefined): {
|
|
46
|
+
widthPx: number;
|
|
47
|
+
heightPx: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The more restrictive of the two ceilings.
|
|
51
|
+
*
|
|
52
|
+
* Two independent reasons to cap answer different questions, so neither may
|
|
53
|
+
* overwrite the other: the lower rung satisfies both, the higher one breaks
|
|
54
|
+
* whichever constraint it clears.
|
|
55
|
+
*/
|
|
56
|
+
export declare function abrCeiling(hdr: QualityLevel | null, size: QualityLevel | null): QualityLevel | null;
|
|
57
|
+
//# sourceMappingURL=size-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"size-policy.d.ts","sourceRoot":"","sources":["../../../src/adapters/quality/size-policy.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,EACnC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GAClB,YAAY,GAAG,IAAI,CAyCrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE;IAAE,qBAAqB,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACxF;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAgBvC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,CAanG"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// -----------------------------------------------------------------------------
|
|
2
|
+
// Copyright (c) NoMercy Entertainment
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0. See LICENSE for details.
|
|
5
|
+
//
|
|
6
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
// -----------------------------------------------------------------------------
|
|
8
|
+
/**
|
|
9
|
+
* What to do when a 4K rung meets a 740px-wide player.
|
|
10
|
+
*
|
|
11
|
+
* Adaptation climbed the ladder on bandwidth alone, so a pane a fifth of the
|
|
12
|
+
* screen wide pulled a 3840-wide rendition and threw most of it away in the
|
|
13
|
+
* scaler — paid for three times over, in bytes, in decode and in battery.
|
|
14
|
+
*
|
|
15
|
+
* `hls.js` ships `capLevelToPlayerSize` for this and it stays off deliberately:
|
|
16
|
+
* `autoLevelCapping` is already written from `hdr-policy`'s ceiling, and two
|
|
17
|
+
* writers to one property is a bug waiting for a resize. The rule lives here so
|
|
18
|
+
* the Kotlin and Swift ports mirror one decision rather than one decision and
|
|
19
|
+
* one library's private heuristic.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The smallest rung that still covers the pane, or null when nothing should cap.
|
|
23
|
+
*
|
|
24
|
+
* Smallest rather than largest: a rung above the pane is bytes the scaler
|
|
25
|
+
* discards, and a rung below it upscales into a soft picture, which is the
|
|
26
|
+
* mistake a viewer actually notices.
|
|
27
|
+
*
|
|
28
|
+
* Null on an unmeasured pane, on an empty ladder, and when the covering rung is
|
|
29
|
+
* already the tallest thing on offer — a cap that changes nothing still narrows
|
|
30
|
+
* what adaptation may pick if anything about the comparison is subtly wrong, and
|
|
31
|
+
* the safest constraint is the one not applied. The same reason `hdrAbrCeiling`
|
|
32
|
+
* returns null on a ladder with no HDR in it.
|
|
33
|
+
*/
|
|
34
|
+
export function sizeAbrCeiling(levels, paneWidthPx, paneHeightPx) {
|
|
35
|
+
// A pane measured before layout reports zero. Capping on that answer pins the
|
|
36
|
+
// stream to its lowest rung for the whole session, so an unmeasured pane must
|
|
37
|
+
// never cap at all.
|
|
38
|
+
if (paneWidthPx <= 0 || paneHeightPx <= 0)
|
|
39
|
+
return null;
|
|
40
|
+
if (levels.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
// An undeclared width is not a narrow width. The native ladder carries no
|
|
43
|
+
// widths whatsoever, so testing a missing one against the pane would drop
|
|
44
|
+
// every rung there and land the port on a taller rendition than the web —
|
|
45
|
+
// the divergence the port exists to remove. Height is the mandatory signal;
|
|
46
|
+
// width only narrows the field when a rung actually states it.
|
|
47
|
+
const covering = levels.filter((level) => {
|
|
48
|
+
const heightCovers = (level.height ?? 0) >= paneHeightPx;
|
|
49
|
+
const widthCovers = level.width == null || level.width >= paneWidthPx;
|
|
50
|
+
return heightCovers && widthCovers;
|
|
51
|
+
});
|
|
52
|
+
if (covering.length === 0)
|
|
53
|
+
return null;
|
|
54
|
+
// `height` is optional on the web's QualityLevel — an audio-only rendition has
|
|
55
|
+
// none, and so does a ladder a backend has only half described. Nought sorts it
|
|
56
|
+
// below every real rung, which is what an unknown height deserves when the
|
|
57
|
+
// question is "the least this pane can be fed without going soft".
|
|
58
|
+
const ceiling = covering.reduce((best, level) => {
|
|
59
|
+
const height = level.height ?? 0;
|
|
60
|
+
const bestHeight = best.height ?? 0;
|
|
61
|
+
if (height !== bestHeight)
|
|
62
|
+
return height < bestHeight ? level : best;
|
|
63
|
+
return (level.bitrate ?? 0) < (best.bitrate ?? 0) ? level : best;
|
|
64
|
+
});
|
|
65
|
+
const tallest = levels.reduce((max, level) => Math.max(max, level.height ?? 0), 0);
|
|
66
|
+
if ((ceiling.height ?? 0) >= tallest)
|
|
67
|
+
return null;
|
|
68
|
+
return ceiling;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The pane in device pixels, which is the unit a rendition is measured in.
|
|
72
|
+
*
|
|
73
|
+
* CSS pixels understate a phone or a HiDPI laptop by two or three times, so a
|
|
74
|
+
* ceiling computed from them caps a retina viewer to a picture visibly softer
|
|
75
|
+
* than the panel can draw. Mirrors `hls.js`'s own `contentScaleFactor`.
|
|
76
|
+
*
|
|
77
|
+
* The port deliberately does NOT do this: a Compose or UIKit surface is already
|
|
78
|
+
* handed its size in real pixels, so a second multiplication there would cap a
|
|
79
|
+
* rung too high.
|
|
80
|
+
*/
|
|
81
|
+
export function panePixels(element) {
|
|
82
|
+
if (!element) {
|
|
83
|
+
return {
|
|
84
|
+
widthPx: 0,
|
|
85
|
+
heightPx: 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const rect = element.getBoundingClientRect();
|
|
89
|
+
const reported = typeof window === 'undefined' ? undefined : window.devicePixelRatio;
|
|
90
|
+
const scale = typeof reported === 'number' && Number.isFinite(reported) && reported > 0 ? reported : 1;
|
|
91
|
+
return {
|
|
92
|
+
widthPx: Math.round(rect.width * scale),
|
|
93
|
+
heightPx: Math.round(rect.height * scale),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The more restrictive of the two ceilings.
|
|
98
|
+
*
|
|
99
|
+
* Two independent reasons to cap answer different questions, so neither may
|
|
100
|
+
* overwrite the other: the lower rung satisfies both, the higher one breaks
|
|
101
|
+
* whichever constraint it clears.
|
|
102
|
+
*/
|
|
103
|
+
export function abrCeiling(hdr, size) {
|
|
104
|
+
if (hdr === null)
|
|
105
|
+
return size;
|
|
106
|
+
if (size === null)
|
|
107
|
+
return hdr;
|
|
108
|
+
const hdrHeight = hdr.height ?? 0;
|
|
109
|
+
const sizeHeight = size.height ?? 0;
|
|
110
|
+
if (hdrHeight !== sizeHeight)
|
|
111
|
+
return hdrHeight < sizeHeight ? hdr : size;
|
|
112
|
+
return (hdr.bitrate ?? 0) <= (size.bitrate ?? 0) ? hdr : size;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=size-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"size-policy.js","sourceRoot":"","sources":["../../../src/adapters/quality/size-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-code retry behaviour. All fields except `attempts` are optional; omitting
|
|
3
|
-
* `backoff` means
|
|
4
|
-
*
|
|
3
|
+
* `backoff` means linear, and `computeBackoff` fills in `baseMs: 500` and
|
|
4
|
+
* `maxMs: 30_000` — so an unset backoff still waits 500, 1000, 1500, …
|
|
5
|
+
* Set `baseMs: 0` for immediate retries. `refreshFirst: true` runs the auth
|
|
6
|
+
* refresh flow before the first retry attempt — used for 401s, which retry with
|
|
7
|
+
* no delay because that call site passes 0 rather than reading this config.
|
|
5
8
|
*
|
|
6
9
|
* `attempts: 0` is the canonical "do not retry" value. The kit checks this before
|
|
7
10
|
* scheduling any delay, so `baseMs` / `maxMs` are irrelevant when `attempts` is 0.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IRetryPolicy.d.ts","sourceRoot":"","sources":["../../../src/adapters/retry-policy/IRetryPolicy.ts"],"names":[],"mappings":"AAQA
|
|
1
|
+
{"version":3,"file":"IRetryPolicy.d.ts","sourceRoot":"","sources":["../../../src/adapters/retry-policy/IRetryPolicy.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC"}
|
package/dist/core/kit-version.js
CHANGED
|
@@ -29,6 +29,8 @@ export declare const deviceMethods: {
|
|
|
29
29
|
* autoplay and handles a NotAllowedError from the backend.
|
|
30
30
|
* - `preferred` — `'powerEfficient'` on TV and mobile, `'smooth'` on
|
|
31
31
|
* desktop; used by quality selection heuristics.
|
|
32
|
+
* - `hdrDisplay` — from `detectDisplayHdr()`, which asks the video plane
|
|
33
|
+
* before the page and defaults to SDR when nothing can be asked.
|
|
32
34
|
*/
|
|
33
35
|
readonly device: (this: Internals) => DeviceCapabilities;
|
|
34
36
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/core/mixins/device.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/core/mixins/device.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAyE1C,eAAO,MAAM,aAAa;IACzB;;;;OAIG;0BACQ,SAAS,KAAG,OAAO;IAI9B;;;;OAIG;8BACY,SAAS,KAAG,OAAO;IAIlC;;;OAGG;+BACa,SAAS,KAAG,OAAO;IAInC;;;;;;;;;;;;;OAaG;4BACU,SAAS,KAAG,kBAAkB;CAkBlC,CAAC"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// SPDX-License-Identifier: Apache-2.0
|
|
7
7
|
// -----------------------------------------------------------------------------
|
|
8
8
|
import { browserPlatform } from '../../adapters/platform/browser.js';
|
|
9
|
+
import { detectDisplayHdr } from '../../adapters/quality/display-range.js';
|
|
9
10
|
let _cachedDevice;
|
|
10
11
|
/**
|
|
11
12
|
* UA-based device classification. Order matters: TV detection runs FIRST so
|
|
@@ -91,6 +92,8 @@ export const deviceMethods = {
|
|
|
91
92
|
* autoplay and handles a NotAllowedError from the backend.
|
|
92
93
|
* - `preferred` — `'powerEfficient'` on TV and mobile, `'smooth'` on
|
|
93
94
|
* desktop; used by quality selection heuristics.
|
|
95
|
+
* - `hdrDisplay` — from `detectDisplayHdr()`, which asks the video plane
|
|
96
|
+
* before the page and defaults to SDR when nothing can be asked.
|
|
94
97
|
*/
|
|
95
98
|
device() {
|
|
96
99
|
const detected = _detectDevice();
|
|
@@ -107,6 +110,7 @@ export const deviceMethods = {
|
|
|
107
110
|
webLocksSupported,
|
|
108
111
|
autoplayAllowed: 'unknown',
|
|
109
112
|
preferred: detected.isTv || detected.isMobile ? 'powerEfficient' : 'smooth',
|
|
113
|
+
hdrDisplay: detectDisplayHdr(),
|
|
110
114
|
};
|
|
111
115
|
},
|
|
112
116
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../../src/core/mixins/device.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,uCAAuC;AACvC,EAAE;AACF,4EAA4E;AAC5E,EAAE;AACF,uCAAuC;AACvC,gFAAgF;AAKhF,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../../src/core/mixins/device.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/core/mixins/lifecycle.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,KAAK,EACX,gBAAgB,EAEhB,WAAW,EAGX,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAgB1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/core/mixins/lifecycle.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,KAAK,EACX,gBAAgB,EAEhB,WAAW,EAGX,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAgB1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA6DzC,eAAO,MAAM,gBAAgB;IAC5B;;;;;;;;;;;;;;;;;;OAkBG;2BACS,SAAS,UAAU,gBAAgB,KAAG,OAAO;IA+BzD;;;;;;;;;;;;;;OAcG;2BACS,SAAS,KAAG,OAAO,CAAC,IAAI,CAAC;IAuBrC;;;;;;;;;;;;;;;;;;;;;;OAsBG;6BACiB,SAAS,KAAG,OAAO,CAAC,IAAI,CAAC;IAoD7C;;;;;OAKG;gCACc,SAAS,KAAG,UAAU;IAcvC,mGAAmG;2BACvF,SAAS,KAAG,WAAW;IAInC;;;;;OAKG;iCACe,SAAS,KAAG,aAAa,CAAC,MAAM,CAAC;IAInD;;;;OAIG;kCACgB,SAAS,QAAQ,MAAM,KAAG,IAAI;IAIjD,0FAA0F;iCACxE,SAAS,KAAG,MAAM,GAAG,SAAS;IAIhD;;;;;OAKG;8BACY,SAAS,KAAG,SAAS;CAG3B,CAAC"}
|
|
@@ -21,6 +21,7 @@ import { kitTranslations } from '../../kit-translations.js';
|
|
|
21
21
|
import { SetupState } from '../../types/index.js';
|
|
22
22
|
import { authFetch } from '../auth-fetch/index.js';
|
|
23
23
|
import { wireActivityTracking } from './activity.js';
|
|
24
|
+
import { applyPendingSelection } from './queue.js';
|
|
24
25
|
// ──────────────────────────────────────────────────────────────────────────
|
|
25
26
|
// Playlist URL resolver
|
|
26
27
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -855,6 +856,7 @@ function _runSetupPipeline(self) {
|
|
|
855
856
|
const playlist = self.options.playlist;
|
|
856
857
|
if (typeof playlist === 'string') {
|
|
857
858
|
await _resolvePlaylistUrl(self, playlist);
|
|
859
|
+
applyPendingSelection(self);
|
|
858
860
|
}
|
|
859
861
|
else if (Array.isArray(playlist)) {
|
|
860
862
|
// Only seed the queue from the config array when the consumer
|
|
@@ -867,6 +869,7 @@ function _runSetupPipeline(self) {
|
|
|
867
869
|
if (!alreadySeeded) {
|
|
868
870
|
self.queue(playlist);
|
|
869
871
|
}
|
|
872
|
+
applyPendingSelection(self);
|
|
870
873
|
self.emit('playlistReady', { length: playlist.length });
|
|
871
874
|
}
|
|
872
875
|
else {
|