@scarlett-player/captions 1.15.4 → 1.16.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/README.md CHANGED
@@ -54,7 +54,7 @@ const player = await createPlayer({
54
54
  | `label` | `string` | required | Human readable name shown in the picker |
55
55
  | `src` | `string` | required | WebVTT URL. Cross-origin URLs need CORS |
56
56
  | `kind` | `'subtitles' \| 'captions'` | `'subtitles'` | Track kind |
57
- | `default` | `boolean` | - | Declared on the interface but not read. Every track starts disabled and selection is managed by the plugin; use `autoSelect` and `defaultLanguage` instead |
57
+ | `default` | `boolean` | - | Select this source once the media loads. Beats `defaultLanguage` and applies even with `autoSelect` off, so it is how you name one specific track. A track the browser is already showing still wins, and selection happens once per media. Mark at most one source |
58
58
 
59
59
  ## How tracks reach the player
60
60
 
package/dist/index.cjs CHANGED
@@ -26,7 +26,7 @@ __export(index_exports, {
26
26
  module.exports = __toCommonJS(index_exports);
27
27
 
28
28
  // src/version.ts
29
- var PKG_VERSION = true ? "1.15.4" : "0.0.0-dev";
29
+ var PKG_VERSION = true ? "1.16.0" : "0.0.0-dev";
30
30
 
31
31
  // src/index.ts
32
32
  var HLS_SUBTITLE_TRACKS_UPDATED = "hlsSubtitleTracksUpdated";
@@ -36,6 +36,7 @@ function createCaptionsPlugin(config = {}) {
36
36
  let api = null;
37
37
  let video = null;
38
38
  let addedTrackElements = [];
39
+ let defaultTrackElement = null;
39
40
  let hlsSubtitleHandler = null;
40
41
  let hlsSubtitleSwitchHandler = null;
41
42
  let observedTextTracks = null;
@@ -45,6 +46,7 @@ function createCaptionsPlugin(config = {}) {
45
46
  const extractFromHLS = config.extractFromHLS !== false;
46
47
  const autoSelect = config.autoSelect ?? false;
47
48
  const defaultLanguage = config.defaultLanguage ?? "en";
49
+ const defaultSource = config.sources?.find((source) => source.default) ?? null;
48
50
  const getVideo = () => {
49
51
  if (video) return video;
50
52
  video = api?.container.querySelector("video") ?? null;
@@ -55,6 +57,7 @@ function createCaptionsPlugin(config = {}) {
55
57
  trackEl.parentNode?.removeChild(trackEl);
56
58
  }
57
59
  addedTrackElements = [];
60
+ defaultTrackElement = null;
58
61
  api?.setState("textTracks", []);
59
62
  api?.setState("currentTextTrack", null);
60
63
  };
@@ -113,17 +116,37 @@ function createCaptionsPlugin(config = {}) {
113
116
  }
114
117
  syncTracksToState();
115
118
  };
119
+ const markedTrack = (tracks) => {
120
+ if (!defaultSource) return void 0;
121
+ const videoEl = getVideo();
122
+ const textTrack = defaultTrackElement?.track;
123
+ if (videoEl && textTrack) {
124
+ for (let i = 0; i < videoEl.textTracks.length; i++) {
125
+ if (videoEl.textTracks[i] !== textTrack) continue;
126
+ const own = tracks.find((t) => t.id === `track-${i}`);
127
+ if (own) return own;
128
+ }
129
+ }
130
+ return tracks.find(
131
+ (t) => t.language === defaultSource.language && (!defaultSource.label || t.label === defaultSource.label)
132
+ );
133
+ };
116
134
  const maybeAutoSelect = () => {
117
- if (!autoSelect || hasAutoSelected) return;
135
+ if (!autoSelect && !defaultSource || hasAutoSelected) return;
118
136
  if (api?.getState("currentTextTrack")) {
119
137
  hasAutoSelected = true;
120
138
  return;
121
139
  }
122
140
  const tracks = api?.getState("textTracks") || [];
123
- const match = tracks.find((t) => t.language === defaultLanguage);
141
+ const marked = markedTrack(tracks);
142
+ const match = marked ?? (autoSelect ? tracks.find((t) => t.language === defaultLanguage) : void 0);
124
143
  if (!match) return;
125
144
  selectTrack(match.id);
126
- api?.logger.debug("Auto-selected caption track", { language: defaultLanguage, id: match.id });
145
+ api?.logger.debug("Selected caption track on load", {
146
+ language: match.language,
147
+ id: match.id,
148
+ reason: marked ? "source default" : "defaultLanguage"
149
+ });
127
150
  };
128
151
  const handleTextTracksChanged = () => {
129
152
  syncTracksToState();
@@ -203,7 +226,8 @@ function createCaptionsPlugin(config = {}) {
203
226
  const initSources = () => {
204
227
  if (!config.sources?.length) return;
205
228
  for (const source of config.sources) {
206
- addTrackElement(source);
229
+ const trackEl = addTrackElement(source);
230
+ if (source === defaultSource) defaultTrackElement = trackEl;
207
231
  }
208
232
  };
209
233
  return {
package/dist/index.d.cts CHANGED
@@ -12,7 +12,13 @@ interface CaptionSource {
12
12
  src: string;
13
13
  /** Track kind - default: 'subtitles' */
14
14
  kind?: 'subtitles' | 'captions';
15
- /** Whether this track should be selected by default */
15
+ /**
16
+ * Select this track once the media loads.
17
+ *
18
+ * Beats `defaultLanguage`, and applies even with `autoSelect` off. A
19
+ * viewer's own pick still wins: selection happens once per media, and a
20
+ * track already showing stands. Mark at most one source.
21
+ */
16
22
  default?: boolean;
17
23
  }
18
24
  interface CaptionsPluginConfig {
package/dist/index.d.ts CHANGED
@@ -12,7 +12,13 @@ interface CaptionSource {
12
12
  src: string;
13
13
  /** Track kind - default: 'subtitles' */
14
14
  kind?: 'subtitles' | 'captions';
15
- /** Whether this track should be selected by default */
15
+ /**
16
+ * Select this track once the media loads.
17
+ *
18
+ * Beats `defaultLanguage`, and applies even with `autoSelect` off. A
19
+ * viewer's own pick still wins: selection happens once per media, and a
20
+ * track already showing stands. Mark at most one source.
21
+ */
16
22
  default?: boolean;
17
23
  }
18
24
  interface CaptionsPluginConfig {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var PKG_VERSION = true ? "1.15.4" : "0.0.0-dev";
2
+ var PKG_VERSION = true ? "1.16.0" : "0.0.0-dev";
3
3
 
4
4
  // src/index.ts
5
5
  var HLS_SUBTITLE_TRACKS_UPDATED = "hlsSubtitleTracksUpdated";
@@ -9,6 +9,7 @@ function createCaptionsPlugin(config = {}) {
9
9
  let api = null;
10
10
  let video = null;
11
11
  let addedTrackElements = [];
12
+ let defaultTrackElement = null;
12
13
  let hlsSubtitleHandler = null;
13
14
  let hlsSubtitleSwitchHandler = null;
14
15
  let observedTextTracks = null;
@@ -18,6 +19,7 @@ function createCaptionsPlugin(config = {}) {
18
19
  const extractFromHLS = config.extractFromHLS !== false;
19
20
  const autoSelect = config.autoSelect ?? false;
20
21
  const defaultLanguage = config.defaultLanguage ?? "en";
22
+ const defaultSource = config.sources?.find((source) => source.default) ?? null;
21
23
  const getVideo = () => {
22
24
  if (video) return video;
23
25
  video = api?.container.querySelector("video") ?? null;
@@ -28,6 +30,7 @@ function createCaptionsPlugin(config = {}) {
28
30
  trackEl.parentNode?.removeChild(trackEl);
29
31
  }
30
32
  addedTrackElements = [];
33
+ defaultTrackElement = null;
31
34
  api?.setState("textTracks", []);
32
35
  api?.setState("currentTextTrack", null);
33
36
  };
@@ -86,17 +89,37 @@ function createCaptionsPlugin(config = {}) {
86
89
  }
87
90
  syncTracksToState();
88
91
  };
92
+ const markedTrack = (tracks) => {
93
+ if (!defaultSource) return void 0;
94
+ const videoEl = getVideo();
95
+ const textTrack = defaultTrackElement?.track;
96
+ if (videoEl && textTrack) {
97
+ for (let i = 0; i < videoEl.textTracks.length; i++) {
98
+ if (videoEl.textTracks[i] !== textTrack) continue;
99
+ const own = tracks.find((t) => t.id === `track-${i}`);
100
+ if (own) return own;
101
+ }
102
+ }
103
+ return tracks.find(
104
+ (t) => t.language === defaultSource.language && (!defaultSource.label || t.label === defaultSource.label)
105
+ );
106
+ };
89
107
  const maybeAutoSelect = () => {
90
- if (!autoSelect || hasAutoSelected) return;
108
+ if (!autoSelect && !defaultSource || hasAutoSelected) return;
91
109
  if (api?.getState("currentTextTrack")) {
92
110
  hasAutoSelected = true;
93
111
  return;
94
112
  }
95
113
  const tracks = api?.getState("textTracks") || [];
96
- const match = tracks.find((t) => t.language === defaultLanguage);
114
+ const marked = markedTrack(tracks);
115
+ const match = marked ?? (autoSelect ? tracks.find((t) => t.language === defaultLanguage) : void 0);
97
116
  if (!match) return;
98
117
  selectTrack(match.id);
99
- api?.logger.debug("Auto-selected caption track", { language: defaultLanguage, id: match.id });
118
+ api?.logger.debug("Selected caption track on load", {
119
+ language: match.language,
120
+ id: match.id,
121
+ reason: marked ? "source default" : "defaultLanguage"
122
+ });
100
123
  };
101
124
  const handleTextTracksChanged = () => {
102
125
  syncTracksToState();
@@ -176,7 +199,8 @@ function createCaptionsPlugin(config = {}) {
176
199
  const initSources = () => {
177
200
  if (!config.sources?.length) return;
178
201
  for (const source of config.sources) {
179
- addTrackElement(source);
202
+ const trackEl = addTrackElement(source);
203
+ if (source === defaultSource) defaultTrackElement = trackEl;
180
204
  }
181
205
  };
182
206
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scarlett-player/captions",
3
- "version": "1.15.4",
3
+ "version": "1.16.0",
4
4
  "description": "Captions Plugin for Scarlett Player - WebVTT subtitles and closed captions",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -30,7 +30,7 @@
30
30
  "tsup": "^8.5.1",
31
31
  "typescript": "^5.3.0",
32
32
  "vitest": "^1.6.0",
33
- "@scarlett-player/core": "1.15.4"
33
+ "@scarlett-player/core": "1.16.0"
34
34
  },
35
35
  "keywords": [
36
36
  "video",