@mattebox/player-preferences 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +43 -2
- package/dist/cdn/mattebox-player-preferences.min.js +1 -0
- package/dist/component.d.ts +31 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/component.js +49 -0
- package/dist/component.js.map +1 -0
- package/dist/element.d.ts +39 -0
- package/dist/element.d.ts.map +1 -0
- package/dist/element.js +261 -0
- package/dist/element.js.map +1 -0
- package/dist/es2015/_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/defineProperty.js +11 -0
- package/dist/es2015/_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/objectSpread2.js +24 -0
- package/dist/es2015/_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/toPrimitive.js +13 -0
- package/dist/es2015/_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/toPropertyKey.js +8 -0
- package/dist/es2015/_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/typeof.js +10 -0
- package/dist/es2015/component.js +43 -0
- package/dist/es2015/element.js +223 -0
- package/dist/es2015/host.js +18 -0
- package/dist/es2015/index.js +18 -0
- package/dist/es2015/store.js +70 -0
- package/dist/es2015/tracks.js +45 -0
- package/dist/host.d.ts +14 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +19 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/store.d.ts +62 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +89 -0
- package/dist/store.js.map +1 -0
- package/dist/tracks.d.ts +21 -0
- package/dist/tracks.d.ts.map +1 -0
- package/dist/tracks.js +47 -0
- package/dist/tracks.js.map +1 -0
- package/package.json +56 -4
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { Component } from "./component.js";
|
|
2
|
+
import _objectSpread2 from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/objectSpread2.js";
|
|
3
|
+
import { preferences } from "./store.js";
|
|
4
|
+
import { best, engineChoice, nativeChoice } from "./tracks.js";
|
|
5
|
+
// src/element.ts
|
|
6
|
+
const SIZE = "subtitle-size";
|
|
7
|
+
const BACKGROUND = "subtitle-background";
|
|
8
|
+
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
9
|
+
var MbxPreferences = class extends Component {
|
|
10
|
+
static get observedAttributes() {
|
|
11
|
+
return ["storage-key"];
|
|
12
|
+
}
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
this.store = preferences();
|
|
16
|
+
this.engine = null;
|
|
17
|
+
this.put = {};
|
|
18
|
+
this.pinned = null;
|
|
19
|
+
this.loading = true;
|
|
20
|
+
this.offEngine = null;
|
|
21
|
+
this.saved = /* @__PURE__ */ new Set();
|
|
22
|
+
}
|
|
23
|
+
attributeChangedCallback() {
|
|
24
|
+
const key = this.getAttribute("storage-key");
|
|
25
|
+
const next = preferences(key === null ? {} : { key });
|
|
26
|
+
if (next === this.store) return;
|
|
27
|
+
for (const name of this.saved) next.set(name, this.store.get(name));
|
|
28
|
+
this.saved = /* @__PURE__ */ new Set();
|
|
29
|
+
this.store = next;
|
|
30
|
+
}
|
|
31
|
+
/** Whether the element keeps `key`: every key, less the ones `except` names. */
|
|
32
|
+
keeps(key) {
|
|
33
|
+
var _this$getAttribute;
|
|
34
|
+
return !((_this$getAttribute = this.getAttribute("except")) !== null && _this$getAttribute !== void 0 ? _this$getAttribute : "").split(/\s+/).includes(key);
|
|
35
|
+
}
|
|
36
|
+
/** The stored value of `key`, or undefined when the element leaves it alone. */
|
|
37
|
+
stored(key) {
|
|
38
|
+
return this.keeps(key) ? this.store.get(key) : void 0;
|
|
39
|
+
}
|
|
40
|
+
save(key, value) {
|
|
41
|
+
if (!this.keeps(key)) return;
|
|
42
|
+
this.store.set(key, value);
|
|
43
|
+
this.saved.add(key);
|
|
44
|
+
}
|
|
45
|
+
attach(player) {
|
|
46
|
+
const video = player.video;
|
|
47
|
+
const volume = this.stored("volume");
|
|
48
|
+
if (volume !== void 0) video.volume = volume;
|
|
49
|
+
let pageMuted = player.hasAttribute("muted");
|
|
50
|
+
const muted = this.stored("muted");
|
|
51
|
+
if (muted !== void 0 && !pageMuted) video.muted = muted;
|
|
52
|
+
const look = this.stored("subtitle-look");
|
|
53
|
+
if ((look === null || look === void 0 ? void 0 : look.size) !== void 0) player.setAttribute(SIZE, look.size);
|
|
54
|
+
if ((look === null || look === void 0 ? void 0 : look.background) !== void 0) player.setAttribute(BACKGROUND, look.background);
|
|
55
|
+
this.on(video, "volumechange", () => {
|
|
56
|
+
this.save("volume", video.volume);
|
|
57
|
+
pageMuted = pageMuted && video.muted;
|
|
58
|
+
if (!pageMuted) this.save("muted", video.muted);
|
|
59
|
+
});
|
|
60
|
+
const watch = new MutationObserver(() => {
|
|
61
|
+
const size = player.getAttribute(SIZE);
|
|
62
|
+
const background = player.getAttribute(BACKGROUND);
|
|
63
|
+
this.save("subtitle-look", _objectSpread2(_objectSpread2({}, size === null ? {} : { size }), background === null ? {} : { background }));
|
|
64
|
+
});
|
|
65
|
+
watch.observe(player, {
|
|
66
|
+
attributes: true,
|
|
67
|
+
attributeFilter: [SIZE, BACKGROUND]
|
|
68
|
+
});
|
|
69
|
+
this.keep(() => {
|
|
70
|
+
watch.disconnect();
|
|
71
|
+
});
|
|
72
|
+
this.on(video, "loadstart", () => {
|
|
73
|
+
this.loading = true;
|
|
74
|
+
this.put = {};
|
|
75
|
+
});
|
|
76
|
+
this.on(video, "loadedmetadata", () => {
|
|
77
|
+
const rate = this.stored("rate");
|
|
78
|
+
if (rate !== void 0) video.playbackRate = rate;
|
|
79
|
+
this.loading = false;
|
|
80
|
+
if (this.engine === null) this.restoreNative(player);
|
|
81
|
+
});
|
|
82
|
+
this.on(video, "ratechange", () => {
|
|
83
|
+
var _this$engine, _moving$live;
|
|
84
|
+
const moving = (_this$engine = this.engine) !== null && _this$engine !== void 0 ? _this$engine : {};
|
|
85
|
+
const trick = moving.trick;
|
|
86
|
+
const live = ((_moving$live = moving.live) === null || _moving$live === void 0 ? void 0 : _moving$live.edge) != null || video.duration === Number.POSITIVE_INFINITY;
|
|
87
|
+
if (this.loading || live || trick !== void 0 && (trick.rate !== 1 || trick.scrubbing)) return;
|
|
88
|
+
this.save("rate", video.playbackRate);
|
|
89
|
+
});
|
|
90
|
+
this.on(player, "click", () => {
|
|
91
|
+
setTimeout(() => {
|
|
92
|
+
this.notePin();
|
|
93
|
+
}, 0);
|
|
94
|
+
});
|
|
95
|
+
for (const name of ["audioTracks", "textTracks"]) {
|
|
96
|
+
const list = video[name];
|
|
97
|
+
if (list !== void 0) this.on(list, "change", () => this.noteNative(player));
|
|
98
|
+
}
|
|
99
|
+
this.on(player, "sourcechange", () => {
|
|
100
|
+
this.follow(player.engine);
|
|
101
|
+
});
|
|
102
|
+
this.follow(player.engine);
|
|
103
|
+
this.keep(() => {
|
|
104
|
+
this.follow(null);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Puts the stored tracks and pin back on each engine once its manifest lists them, and saves the viewer's changes. */
|
|
108
|
+
follow(engine) {
|
|
109
|
+
var _this$offEngine;
|
|
110
|
+
(_this$offEngine = this.offEngine) === null || _this$offEngine === void 0 || _this$offEngine.call(this);
|
|
111
|
+
this.offEngine = null;
|
|
112
|
+
this.engine = engine;
|
|
113
|
+
if (engine === null) return;
|
|
114
|
+
let offChanged = () => void 0;
|
|
115
|
+
let offSelected = () => void 0;
|
|
116
|
+
const ready = () => {
|
|
117
|
+
offChanged();
|
|
118
|
+
this.restoreEngine(engine);
|
|
119
|
+
offSelected = engine.on("tracks:selected", () => {
|
|
120
|
+
this.noteEngine(engine);
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
if (engine.tracks.available.length > 0) ready();
|
|
124
|
+
else offChanged = engine.on("tracks:changed", ready);
|
|
125
|
+
this.offEngine = () => {
|
|
126
|
+
offChanged();
|
|
127
|
+
offSelected();
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
restoreEngine(engine) {
|
|
131
|
+
const tracks = engine.tracks;
|
|
132
|
+
for (const kind of ["audio", "subtitles"]) {
|
|
133
|
+
const type = kind === "audio" ? "audio" : "text";
|
|
134
|
+
const list = tracks.available.filter((track) => track.contentType === type && tracks.selectable(track.id) && !track.forced);
|
|
135
|
+
const wanted = this.stored(kind);
|
|
136
|
+
this.put[kind] = this.engineActive(engine, kind);
|
|
137
|
+
if (wanted === null) {
|
|
138
|
+
var _tracks$active$forced, _tracks$active;
|
|
139
|
+
if (!((_tracks$active$forced = (_tracks$active = tracks.active("text")) === null || _tracks$active === void 0 ? void 0 : _tracks$active.forced) !== null && _tracks$active$forced !== void 0 ? _tracks$active$forced : false)) tracks.deselect("text");
|
|
140
|
+
this.put[kind] = null;
|
|
141
|
+
} else if (wanted !== void 0) {
|
|
142
|
+
const active = tracks.active(type);
|
|
143
|
+
const pick = list[best(list.map(engineChoice), wanted, active === null ? -1 : list.indexOf(active))];
|
|
144
|
+
if (pick !== void 0) {
|
|
145
|
+
tracks.select(pick.id);
|
|
146
|
+
this.put[kind] = engineChoice(pick);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const height = this.stored("quality");
|
|
151
|
+
const quality = engine.quality;
|
|
152
|
+
if (typeof height === "number") {
|
|
153
|
+
var _fit$find;
|
|
154
|
+
const fit = [...quality.selectable].filter((rendition) => rendition.height !== void 0).sort((a, b) => b.height - a.height);
|
|
155
|
+
const pick = (_fit$find = fit.find((rendition) => rendition.height <= height)) !== null && _fit$find !== void 0 ? _fit$find : fit[fit.length - 1];
|
|
156
|
+
if (pick !== void 0) {
|
|
157
|
+
quality.pin(pick.id);
|
|
158
|
+
this.pinned = pick.id;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
this.pinned = quality.pinned;
|
|
163
|
+
}
|
|
164
|
+
/** The active track of `kind` as a choice: null for subtitles off, or for a forced track. */
|
|
165
|
+
engineActive(engine, kind) {
|
|
166
|
+
const track = engine.tracks.active(kind === "audio" ? "audio" : "text");
|
|
167
|
+
return track === null || track.forced === true ? null : engineChoice(track);
|
|
168
|
+
}
|
|
169
|
+
noteEngine(engine) {
|
|
170
|
+
for (const kind of ["audio", "subtitles"]) {
|
|
171
|
+
const now = this.engineActive(engine, kind);
|
|
172
|
+
if (kind === "subtitles" && now === null && engine.tracks.active("text") !== null) continue;
|
|
173
|
+
if (kind === "audio" && now === null) continue;
|
|
174
|
+
if (same(now, this.put[kind])) continue;
|
|
175
|
+
this.put[kind] = now;
|
|
176
|
+
this.save(kind, now);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
notePin() {
|
|
180
|
+
var _this$engine2, _quality$renditions$f;
|
|
181
|
+
const quality = (_this$engine2 = this.engine) === null || _this$engine2 === void 0 ? void 0 : _this$engine2.quality;
|
|
182
|
+
if (quality === void 0 || quality.pinned === this.pinned) return;
|
|
183
|
+
this.pinned = quality.pinned;
|
|
184
|
+
const height = (_quality$renditions$f = quality.renditions.find((rendition) => rendition.id === this.pinned)) === null || _quality$renditions$f === void 0 ? void 0 : _quality$renditions$f.height;
|
|
185
|
+
this.save("quality", height !== null && height !== void 0 ? height : null);
|
|
186
|
+
}
|
|
187
|
+
/** The native session's audio and subtitle tracks, as Safari lists them. */
|
|
188
|
+
native(player, kind) {
|
|
189
|
+
const name = kind === "audio" ? "audioTracks" : "textTracks";
|
|
190
|
+
const list = player.video[name];
|
|
191
|
+
const tracks = list === void 0 ? [] : Array.from(list);
|
|
192
|
+
return kind === "audio" ? tracks : tracks.filter((track) => track.kind === "subtitles" || track.kind === "captions");
|
|
193
|
+
}
|
|
194
|
+
nativeActive(player, kind) {
|
|
195
|
+
const on = this.native(player, kind).find((track) => kind === "audio" ? track.enabled === true : track.mode === "showing");
|
|
196
|
+
return on === void 0 ? null : nativeChoice(on);
|
|
197
|
+
}
|
|
198
|
+
restoreNative(player) {
|
|
199
|
+
for (const kind of ["audio", "subtitles"]) {
|
|
200
|
+
const tracks = this.native(player, kind);
|
|
201
|
+
const wanted = this.stored(kind);
|
|
202
|
+
if (wanted !== void 0 && tracks.length > 0) {
|
|
203
|
+
const choices = tracks.map(nativeChoice);
|
|
204
|
+
const index = wanted === null ? -1 : best(choices, wanted);
|
|
205
|
+
if (index !== -1 || kind === "subtitles" && wanted === null) tracks.forEach((track, at) => {
|
|
206
|
+
if (kind === "audio") track.enabled = at === index;
|
|
207
|
+
else track.mode = at === index ? "showing" : "disabled";
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
this.put[kind] = this.nativeActive(player, kind);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
noteNative(player) {
|
|
214
|
+
if (this.engine !== null || this.loading) return;
|
|
215
|
+
for (const kind of ["audio", "subtitles"]) {
|
|
216
|
+
const now = this.nativeActive(player, kind);
|
|
217
|
+
if (kind === "audio" && now === null || same(now, this.put[kind])) continue;
|
|
218
|
+
this.put[kind] = now;
|
|
219
|
+
this.save(kind, now);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
export { MbxPreferences };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/host.ts
|
|
2
|
+
const PLAYER = "mattebox-player";
|
|
3
|
+
/** The parent, or the host of the shadow root `node` sits in, or null at the document. */
|
|
4
|
+
function above(node) {
|
|
5
|
+
const parent = node.parentNode;
|
|
6
|
+
if (parent !== null) return parent;
|
|
7
|
+
return node instanceof ShadowRoot ? node.host : null;
|
|
8
|
+
}
|
|
9
|
+
/** The nearest player above `node`, or null outside one. It may not be upgraded yet. */
|
|
10
|
+
function findPlayer(node) {
|
|
11
|
+
let current = node;
|
|
12
|
+
while (current !== null) {
|
|
13
|
+
if (current instanceof HTMLElement && current.localName === "mattebox-player") return current;
|
|
14
|
+
current = above(current);
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
export { PLAYER, above, findPlayer };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { preferences } from "./store.js";
|
|
2
|
+
import { MbxPreferences } from "./element.js";
|
|
3
|
+
// src/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* @mattebox/player-preferences: the viewer's preferences, as a store that
|
|
6
|
+
* every player and every element of the page reads and writes, and
|
|
7
|
+
* <mbx-preferences>, which keeps the player's own choices in it. Importing
|
|
8
|
+
* this module registers the element; that is the package's one side
|
|
9
|
+
* effect. A page without it stores nothing.
|
|
10
|
+
*/
|
|
11
|
+
const PREFERENCES = "mbx-preferences";
|
|
12
|
+
/** The store of the `<mbx-preferences>` inside `player`, or null without one. */
|
|
13
|
+
function preferencesOf(player) {
|
|
14
|
+
var _player$querySelector, _player$querySelector2;
|
|
15
|
+
return (_player$querySelector = (_player$querySelector2 = player.querySelector("mbx-preferences")) === null || _player$querySelector2 === void 0 ? void 0 : _player$querySelector2.store) !== null && _player$querySelector !== void 0 ? _player$querySelector : null;
|
|
16
|
+
}
|
|
17
|
+
if (customElements.get("mbx-preferences") === void 0) customElements.define(PREFERENCES, MbxPreferences);
|
|
18
|
+
export { MbxPreferences, PREFERENCES, preferences, preferencesOf };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import _objectSpread2 from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/objectSpread2.js";
|
|
2
|
+
// src/store.ts
|
|
3
|
+
/** The format of the stored entry. An entry of another version is ignored. */
|
|
4
|
+
const VERSION = 1;
|
|
5
|
+
/** By storage key, and by the empty key for the memory store. */
|
|
6
|
+
const stores = /* @__PURE__ */ new Map();
|
|
7
|
+
function read(storage, key) {
|
|
8
|
+
try {
|
|
9
|
+
var _storage$getItem;
|
|
10
|
+
const entry = JSON.parse((_storage$getItem = storage === null || storage === void 0 ? void 0 : storage.getItem(key)) !== null && _storage$getItem !== void 0 ? _storage$getItem : "null");
|
|
11
|
+
return (entry === null || entry === void 0 ? void 0 : entry.version) === VERSION && typeof entry.values === "object" && entry.values !== null ? entry.values : {};
|
|
12
|
+
} catch (_unused) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function defaultStorage() {
|
|
17
|
+
try {
|
|
18
|
+
return window.localStorage;
|
|
19
|
+
} catch (_unused2) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** The store for `options.key`: the same object for the same key. The first call's `storage` applies. */
|
|
24
|
+
function preferences(options = {}) {
|
|
25
|
+
var _options$key, _options$storage;
|
|
26
|
+
const key = (_options$key = options.key) !== null && _options$key !== void 0 ? _options$key : "";
|
|
27
|
+
const known = stores.get(key);
|
|
28
|
+
if (known !== void 0) return known;
|
|
29
|
+
const storage = key === "" ? null : (_options$storage = options.storage) !== null && _options$storage !== void 0 ? _options$storage : defaultStorage();
|
|
30
|
+
let values = read(storage, key);
|
|
31
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
32
|
+
const tell = (name) => {
|
|
33
|
+
var _listeners$get;
|
|
34
|
+
for (const fn of (_listeners$get = listeners.get(name)) !== null && _listeners$get !== void 0 ? _listeners$get : []) fn(values[name]);
|
|
35
|
+
};
|
|
36
|
+
if (storage !== null && typeof window !== "undefined") window.addEventListener("storage", (event) => {
|
|
37
|
+
if (event.key !== key || event.storageArea !== storage) return;
|
|
38
|
+
const before = values;
|
|
39
|
+
values = read(storage, key);
|
|
40
|
+
for (const name of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(values)])) if (JSON.stringify(before[name]) !== JSON.stringify(values[name])) tell(name);
|
|
41
|
+
});
|
|
42
|
+
const store = {
|
|
43
|
+
get: (name) => values[name],
|
|
44
|
+
set(name, value) {
|
|
45
|
+
if (JSON.stringify(values[name]) === JSON.stringify(value)) return;
|
|
46
|
+
values = _objectSpread2({}, values);
|
|
47
|
+
if (value === void 0) delete values[name];
|
|
48
|
+
else values[name] = value;
|
|
49
|
+
try {
|
|
50
|
+
storage === null || storage === void 0 || storage.setItem(key, JSON.stringify({
|
|
51
|
+
version: VERSION,
|
|
52
|
+
values
|
|
53
|
+
}));
|
|
54
|
+
} catch (_unused3) {}
|
|
55
|
+
tell(name);
|
|
56
|
+
},
|
|
57
|
+
on(name, fn) {
|
|
58
|
+
var _listeners$get2;
|
|
59
|
+
const set = (_listeners$get2 = listeners.get(name)) !== null && _listeners$get2 !== void 0 ? _listeners$get2 : /* @__PURE__ */ new Set();
|
|
60
|
+
listeners.set(name, set);
|
|
61
|
+
set.add(fn);
|
|
62
|
+
return () => {
|
|
63
|
+
set.delete(fn);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
stores.set(key, store);
|
|
68
|
+
return store;
|
|
69
|
+
}
|
|
70
|
+
export { preferences };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isAudioDescription, isEnhancedSpeech, isSdh } from "mattebox";
|
|
2
|
+
// src/tracks.ts
|
|
3
|
+
/** The kinds a viewer keeps across languages. */
|
|
4
|
+
const ACCESS = ["description", "enhanced-speech"];
|
|
5
|
+
/** The primary language subtag, so `de-CH` and `de` are the same language (RFC 5646 §2.2.1). */
|
|
6
|
+
function primary(tag) {
|
|
7
|
+
return tag.toLowerCase().split("-")[0];
|
|
8
|
+
}
|
|
9
|
+
function score(track, wanted) {
|
|
10
|
+
const language = wanted.language !== "" && primary(track.language) === primary(wanted.language);
|
|
11
|
+
const kind = track.kind === wanted.kind;
|
|
12
|
+
if (language) return kind ? 3 : 2;
|
|
13
|
+
return kind && ACCESS.includes(wanted.kind) ? 1 : 0;
|
|
14
|
+
}
|
|
15
|
+
/** The index of the best match for `wanted`, or -1 when none scores. `current` wins a tie. */
|
|
16
|
+
function best(tracks, wanted, current = -1) {
|
|
17
|
+
let found = -1;
|
|
18
|
+
let top = 0;
|
|
19
|
+
tracks.forEach((track, index) => {
|
|
20
|
+
const points = score(track, wanted);
|
|
21
|
+
if (points > top || points === top && points > 0 && index === current) {
|
|
22
|
+
found = index;
|
|
23
|
+
top = points;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
/** An engine track as a choice, its kind from the manifest's standard signals. */
|
|
29
|
+
function engineChoice(track) {
|
|
30
|
+
var _track$lang;
|
|
31
|
+
const kind = track.contentType === "text" ? isSdh(track) ? "captions" : "subtitles" : isAudioDescription(track) ? "description" : isEnhancedSpeech(track) ? "enhanced-speech" : "main";
|
|
32
|
+
return {
|
|
33
|
+
language: (_track$lang = track.lang) !== null && _track$lang !== void 0 ? _track$lang : "",
|
|
34
|
+
kind
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** A native `AudioTrack` or `TextTrack` as a choice, from HTML's kinds. */
|
|
38
|
+
function nativeChoice(track) {
|
|
39
|
+
const kind = track.kind === "main-desc" ? "description" : track.kind || "main";
|
|
40
|
+
return {
|
|
41
|
+
language: track.language,
|
|
42
|
+
kind
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export { best, engineChoice, nativeChoice, score };
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract of a control of the page's own, as guide chapter 03 of the
|
|
3
|
+
* player states it: the nearest <mattebox-player> above, through shadow
|
|
4
|
+
* hosts. This package implements the contract from outside, as the cast
|
|
5
|
+
* package does: the player exports no base class.
|
|
6
|
+
*/
|
|
7
|
+
import type { PlayerHost } from '@mattebox/player';
|
|
8
|
+
export type { PlayerHost };
|
|
9
|
+
export declare const PLAYER = "mattebox-player";
|
|
10
|
+
/** The parent, or the host of the shadow root `node` sits in, or null at the document. */
|
|
11
|
+
export declare function above(node: Node): Node | null;
|
|
12
|
+
/** The nearest player above `node`, or null outside one. It may not be upgraded yet. */
|
|
13
|
+
export declare function findPlayer(node: Node): HTMLElement | null;
|
|
14
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,eAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,0FAA0F;AAC1F,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAI7C;AAED,wFAAwF;AACxF,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,IAAI,CAOzD"}
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const PLAYER = 'mattebox-player';
|
|
2
|
+
/** The parent, or the host of the shadow root `node` sits in, or null at the document. */
|
|
3
|
+
export function above(node) {
|
|
4
|
+
const parent = node.parentNode;
|
|
5
|
+
if (parent !== null)
|
|
6
|
+
return parent;
|
|
7
|
+
return node instanceof ShadowRoot ? node.host : null;
|
|
8
|
+
}
|
|
9
|
+
/** The nearest player above `node`, or null outside one. It may not be upgraded yet. */
|
|
10
|
+
export function findPlayer(node) {
|
|
11
|
+
let current = node;
|
|
12
|
+
while (current !== null) {
|
|
13
|
+
if (current instanceof HTMLElement && current.localName === PLAYER)
|
|
14
|
+
return current;
|
|
15
|
+
current = above(current);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=host.js.map
|
package/dist/host.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,MAAM,GAAG,iBAAiB,CAAC;AAExC,0FAA0F;AAC1F,MAAM,UAAU,KAAK,CAAC,IAAU;IAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IAC/B,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IACnC,OAAO,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,IAAI,OAAO,GAAgB,IAAI,CAAC;IAChC,OAAO,OAAO,KAAK,IAAI,EAAE,CAAC;QACxB,IAAI,OAAO,YAAY,WAAW,IAAI,OAAO,CAAC,SAAS,KAAK,MAAM;YAAE,OAAO,OAAO,CAAC;QACnF,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mattebox/player-preferences: the viewer's preferences, as a store that
|
|
3
|
+
* every player and every element of the page reads and writes, and
|
|
4
|
+
* <mbx-preferences>, which keeps the player's own choices in it. Importing
|
|
5
|
+
* this module registers the element; that is the package's one side
|
|
6
|
+
* effect. A page without it stores nothing.
|
|
7
|
+
*/
|
|
8
|
+
import { MbxPreferences } from './element.js';
|
|
9
|
+
import type { PreferenceStore } from './store.js';
|
|
10
|
+
export { MbxPreferences } from './element.js';
|
|
11
|
+
export type { PreferenceOptions, PreferenceStorage, PreferenceStore, Preferences, SubtitleLook, TrackChoice, } from './store.js';
|
|
12
|
+
export { preferences } from './store.js';
|
|
13
|
+
export declare const PREFERENCES = "mbx-preferences";
|
|
14
|
+
/** The store of the `<mbx-preferences>` inside `player`, or null without one. */
|
|
15
|
+
export declare function preferencesOf(player: Element): PreferenceStore | null;
|
|
16
|
+
declare global {
|
|
17
|
+
interface HTMLElementTagNameMap {
|
|
18
|
+
'mbx-preferences': MbxPreferences;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,YAAY,EACZ,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAE7C,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAErE;AAMD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iBAAiB,EAAE,cAAc,CAAC;KACnC;CACF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mattebox/player-preferences: the viewer's preferences, as a store that
|
|
3
|
+
* every player and every element of the page reads and writes, and
|
|
4
|
+
* <mbx-preferences>, which keeps the player's own choices in it. Importing
|
|
5
|
+
* this module registers the element; that is the package's one side
|
|
6
|
+
* effect. A page without it stores nothing.
|
|
7
|
+
*/
|
|
8
|
+
import { MbxPreferences } from './element.js';
|
|
9
|
+
export { MbxPreferences } from './element.js';
|
|
10
|
+
export { preferences } from './store.js';
|
|
11
|
+
export const PREFERENCES = 'mbx-preferences';
|
|
12
|
+
/** The store of the `<mbx-preferences>` inside `player`, or null without one. */
|
|
13
|
+
export function preferencesOf(player) {
|
|
14
|
+
return player.querySelector(PREFERENCES)?.store ?? null;
|
|
15
|
+
}
|
|
16
|
+
if (customElements.get(PREFERENCES) === undefined) {
|
|
17
|
+
customElements.define(PREFERENCES, MbxPreferences);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAS9C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAE7C,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,MAAe;IAC3C,OAAO,MAAM,CAAC,aAAa,CAAiB,WAAW,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;AAC1E,CAAC;AAED,IAAI,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,SAAS,EAAE,CAAC;IAClD,cAAc,CAAC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;AACrD,CAAC"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The viewer's preferences: one store per storage key, shared by every
|
|
3
|
+
* player on the page and by the page's own elements. A store without a key
|
|
4
|
+
* keeps its values in memory, for as long as the page is open. A store
|
|
5
|
+
* with one keeps them in localStorage, or in the storage the page gives,
|
|
6
|
+
* under that key, and hears the writes of the page's other tabs. Every
|
|
7
|
+
* storage call sits in `try`: a browser that blocks storage keeps the
|
|
8
|
+
* values in memory instead.
|
|
9
|
+
*/
|
|
10
|
+
/** A track the viewer chose: its language, and its kind in HTML's words. */
|
|
11
|
+
export interface TrackChoice {
|
|
12
|
+
readonly language: string;
|
|
13
|
+
/** `main`, `description` or `enhanced-speech` for audio; `subtitles` or `captions` for text. */
|
|
14
|
+
readonly kind: string;
|
|
15
|
+
}
|
|
16
|
+
/** The look of the subtitles: the player's `subtitle-size` and `subtitle-background`. */
|
|
17
|
+
export interface SubtitleLook {
|
|
18
|
+
readonly size?: string;
|
|
19
|
+
readonly background?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Every key a store holds, with its type. A package of your own adds its
|
|
23
|
+
* keys through declaration merging, under a prefix of its name:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* declare module '@mattebox/player-preferences' {
|
|
27
|
+
* interface Preferences {
|
|
28
|
+
* 'my-app.theme': 'dark' | 'light';
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export interface Preferences {
|
|
34
|
+
volume: number;
|
|
35
|
+
muted: boolean;
|
|
36
|
+
rate: number;
|
|
37
|
+
/** The height of the quality the viewer pinned, or null for Auto. */
|
|
38
|
+
quality: number | null;
|
|
39
|
+
audio: TrackChoice;
|
|
40
|
+
/** Null for subtitles off. */
|
|
41
|
+
subtitles: TrackChoice | null;
|
|
42
|
+
'subtitle-look': SubtitleLook;
|
|
43
|
+
}
|
|
44
|
+
/** Where a store keeps its values: localStorage, sessionStorage, or an object of your own with these two methods. */
|
|
45
|
+
export type PreferenceStorage = Pick<Storage, 'getItem' | 'setItem'>;
|
|
46
|
+
export interface PreferenceStore {
|
|
47
|
+
/** The value of `key`, or undefined when the viewer has none. */
|
|
48
|
+
get<K extends keyof Preferences>(key: K): Preferences[K] | undefined;
|
|
49
|
+
/** Sets `key`. Undefined removes it. */
|
|
50
|
+
set<K extends keyof Preferences>(key: K, value: Preferences[K] | undefined): void;
|
|
51
|
+
/** Calls `fn` when `key` changes, here or in another tab, until the returned function runs. */
|
|
52
|
+
on<K extends keyof Preferences>(key: K, fn: (value: Preferences[K] | undefined) => void): () => void;
|
|
53
|
+
}
|
|
54
|
+
export interface PreferenceOptions {
|
|
55
|
+
/** The storage key. Without one, the store keeps its values in memory. */
|
|
56
|
+
readonly key?: string;
|
|
57
|
+
/** The storage under `key`. localStorage by default. */
|
|
58
|
+
readonly storage?: PreferenceStorage;
|
|
59
|
+
}
|
|
60
|
+
/** The store for `options.key`: the same object for the same key. The first call's `storage` applies. */
|
|
61
|
+
export declare function preferences(options?: PreferenceOptions): PreferenceStore;
|
|
62
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gGAAgG;IAChG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,yFAAyF;AACzF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,8BAA8B;IAC9B,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,YAAY,CAAC;CAC/B;AAED,qHAAqH;AACrH,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,GAAG,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,GAAG,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACrE,wCAAwC;IACxC,GAAG,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;IAClF,+FAA+F;IAC/F,EAAE,CAAC,CAAC,SAAS,MAAM,WAAW,EAC5B,GAAG,EAAE,CAAC,EACN,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,KAAK,IAAI,GAC9C,MAAM,IAAI,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAgCD,yGAAyG;AACzG,wBAAgB,WAAW,CAAC,OAAO,GAAE,iBAAsB,GAAG,eAAe,CA8C5E"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The viewer's preferences: one store per storage key, shared by every
|
|
3
|
+
* player on the page and by the page's own elements. A store without a key
|
|
4
|
+
* keeps its values in memory, for as long as the page is open. A store
|
|
5
|
+
* with one keeps them in localStorage, or in the storage the page gives,
|
|
6
|
+
* under that key, and hears the writes of the page's other tabs. Every
|
|
7
|
+
* storage call sits in `try`: a browser that blocks storage keeps the
|
|
8
|
+
* values in memory instead.
|
|
9
|
+
*/
|
|
10
|
+
/** The format of the stored entry. An entry of another version is ignored. */
|
|
11
|
+
const VERSION = 1;
|
|
12
|
+
/** By storage key, and by the empty key for the memory store. */
|
|
13
|
+
const stores = new Map();
|
|
14
|
+
function read(storage, key) {
|
|
15
|
+
try {
|
|
16
|
+
const entry = JSON.parse(storage?.getItem(key) ?? 'null');
|
|
17
|
+
return entry?.version === VERSION && typeof entry.values === 'object' && entry.values !== null
|
|
18
|
+
? entry.values
|
|
19
|
+
: {};
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function defaultStorage() {
|
|
26
|
+
try {
|
|
27
|
+
return window.localStorage;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** The store for `options.key`: the same object for the same key. The first call's `storage` applies. */
|
|
34
|
+
export function preferences(options = {}) {
|
|
35
|
+
const key = options.key ?? '';
|
|
36
|
+
const known = stores.get(key);
|
|
37
|
+
if (known !== undefined)
|
|
38
|
+
return known;
|
|
39
|
+
const storage = key === '' ? null : (options.storage ?? defaultStorage());
|
|
40
|
+
let values = read(storage, key);
|
|
41
|
+
const listeners = new Map();
|
|
42
|
+
const tell = (name) => {
|
|
43
|
+
for (const fn of listeners.get(name) ?? [])
|
|
44
|
+
fn(values[name]);
|
|
45
|
+
};
|
|
46
|
+
// Another tab wrote the entry: take its values, and tell what changed.
|
|
47
|
+
if (storage !== null && typeof window !== 'undefined') {
|
|
48
|
+
window.addEventListener('storage', (event) => {
|
|
49
|
+
if (event.key !== key || event.storageArea !== storage)
|
|
50
|
+
return;
|
|
51
|
+
const before = values;
|
|
52
|
+
values = read(storage, key);
|
|
53
|
+
for (const name of new Set([...Object.keys(before), ...Object.keys(values)])) {
|
|
54
|
+
if (JSON.stringify(before[name]) !== JSON.stringify(values[name]))
|
|
55
|
+
tell(name);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const store = {
|
|
60
|
+
get: (name) => values[name],
|
|
61
|
+
set(name, value) {
|
|
62
|
+
if (JSON.stringify(values[name]) === JSON.stringify(value))
|
|
63
|
+
return;
|
|
64
|
+
values = { ...values };
|
|
65
|
+
if (value === undefined)
|
|
66
|
+
delete values[name];
|
|
67
|
+
else
|
|
68
|
+
values[name] = value;
|
|
69
|
+
try {
|
|
70
|
+
storage?.setItem(key, JSON.stringify({ version: VERSION, values }));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Full or blocked: the value stays in memory for this page.
|
|
74
|
+
}
|
|
75
|
+
tell(name);
|
|
76
|
+
},
|
|
77
|
+
on(name, fn) {
|
|
78
|
+
const set = listeners.get(name) ?? new Set();
|
|
79
|
+
listeners.set(name, set);
|
|
80
|
+
set.add(fn);
|
|
81
|
+
return () => {
|
|
82
|
+
set.delete(fn);
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
stores.set(key, store);
|
|
87
|
+
return store;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/tracks.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which track of a new programme matches the one the viewer chose last:
|
|
3
|
+
* the same language and kind scores 3, the same language 2. The same kind
|
|
4
|
+
* alone scores 1 only for audio made for access, an audio description or
|
|
5
|
+
* enhanced speech, which a viewer who needs it keeps over the language.
|
|
6
|
+
* The first track with the highest score wins, and no score keeps the
|
|
7
|
+
* engine's choice.
|
|
8
|
+
*/
|
|
9
|
+
import type { Track } from 'mattebox';
|
|
10
|
+
import type { TrackChoice } from './store.js';
|
|
11
|
+
export declare function score(track: TrackChoice, wanted: TrackChoice): number;
|
|
12
|
+
/** The index of the best match for `wanted`, or -1 when none scores. `current` wins a tie. */
|
|
13
|
+
export declare function best(tracks: readonly TrackChoice[], wanted: TrackChoice, current?: number): number;
|
|
14
|
+
/** An engine track as a choice, its kind from the manifest's standard signals. */
|
|
15
|
+
export declare function engineChoice(track: Track): TrackChoice;
|
|
16
|
+
/** A native `AudioTrack` or `TextTrack` as a choice, from HTML's kinds. */
|
|
17
|
+
export declare function nativeChoice(track: {
|
|
18
|
+
readonly kind: string;
|
|
19
|
+
readonly language: string;
|
|
20
|
+
}): TrackChoice;
|
|
21
|
+
//# sourceMappingURL=tracks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracks.d.ts","sourceRoot":"","sources":["../src/tracks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAU9C,wBAAgB,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAKrE;AAED,8FAA8F;AAC9F,wBAAgB,IAAI,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,SAAK,GAAG,MAAM,CAW9F;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,CAYtD;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,GAAG,WAAW,CAId"}
|