vidply 1.0.2 → 1.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.
@@ -37,7 +37,7 @@
37
37
  "format": "esm"
38
38
  },
39
39
  "src/controls/ControlBar.js": {
40
- "bytes": 66505,
40
+ "bytes": 70888,
41
41
  "imports": [
42
42
  {
43
43
  "path": "src/utils/DOMUtils.js",
@@ -63,7 +63,7 @@
63
63
  "format": "esm"
64
64
  },
65
65
  "src/controls/CaptionManager.js": {
66
- "bytes": 6437,
66
+ "bytes": 7219,
67
67
  "imports": [
68
68
  {
69
69
  "path": "src/utils/DOMUtils.js",
@@ -79,31 +79,10 @@
79
79
  "format": "esm"
80
80
  },
81
81
  "src/controls/KeyboardManager.js": {
82
- "bytes": 5131,
82
+ "bytes": 6553,
83
83
  "imports": [],
84
84
  "format": "esm"
85
85
  },
86
- "src/controls/SettingsDialog.js": {
87
- "bytes": 12224,
88
- "imports": [
89
- {
90
- "path": "src/utils/DOMUtils.js",
91
- "kind": "import-statement",
92
- "original": "../utils/DOMUtils.js"
93
- },
94
- {
95
- "path": "src/icons/Icons.js",
96
- "kind": "import-statement",
97
- "original": "../icons/Icons.js"
98
- },
99
- {
100
- "path": "src/i18n/i18n.js",
101
- "kind": "import-statement",
102
- "original": "../i18n/i18n.js"
103
- }
104
- ],
105
- "format": "esm"
106
- },
107
86
  "src/controls/TranscriptManager.js": {
108
87
  "bytes": 23405,
109
88
  "imports": [
@@ -157,7 +136,7 @@
157
136
  "format": "esm"
158
137
  },
159
138
  "src/core/Player.js": {
160
- "bytes": 30813,
139
+ "bytes": 34580,
161
140
  "imports": [
162
141
  {
163
142
  "path": "src/utils/EventEmitter.js",
@@ -169,11 +148,6 @@
169
148
  "kind": "import-statement",
170
149
  "original": "../utils/DOMUtils.js"
171
150
  },
172
- {
173
- "path": "src/utils/TimeUtils.js",
174
- "kind": "import-statement",
175
- "original": "../utils/TimeUtils.js"
176
- },
177
151
  {
178
152
  "path": "src/controls/ControlBar.js",
179
153
  "kind": "import-statement",
@@ -189,11 +163,6 @@
189
163
  "kind": "import-statement",
190
164
  "original": "../controls/KeyboardManager.js"
191
165
  },
192
- {
193
- "path": "src/controls/SettingsDialog.js",
194
- "kind": "import-statement",
195
- "original": "../controls/SettingsDialog.js"
196
- },
197
166
  {
198
167
  "path": "src/controls/TranscriptManager.js",
199
168
  "kind": "import-statement",
@@ -296,22 +265,19 @@
296
265
  "bytesInOutput": 720
297
266
  },
298
267
  "src/controls/ControlBar.js": {
299
- "bytesInOutput": 34275
268
+ "bytesInOutput": 35686
300
269
  },
301
270
  "src/controls/CaptionManager.js": {
302
- "bytesInOutput": 3634
271
+ "bytesInOutput": 3599
303
272
  },
304
273
  "src/controls/KeyboardManager.js": {
305
- "bytesInOutput": 3002
306
- },
307
- "src/controls/SettingsDialog.js": {
308
- "bytesInOutput": 7622
274
+ "bytesInOutput": 3738
309
275
  },
310
276
  "src/controls/TranscriptManager.js": {
311
277
  "bytesInOutput": 12461
312
278
  },
313
279
  "src/core/Player.js": {
314
- "bytesInOutput": 16389
280
+ "bytesInOutput": 16383
315
281
  },
316
282
  "src/renderers/YouTubeRenderer.js": {
317
283
  "bytesInOutput": 4140
@@ -326,7 +292,7 @@
326
292
  "bytesInOutput": 5245
327
293
  }
328
294
  },
329
- "bytes": 129227
295
+ "bytes": 123711
330
296
  }
331
297
  }
332
298
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidply",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Universal, accessible video & audio player with ES6 modules",
5
5
  "type": "module",
6
6
  "main": "dist/vidply.js",
@@ -2,249 +2,247 @@
2
2
  * Caption/Subtitle Manager
3
3
  */
4
4
 
5
- import { DOMUtils } from '../utils/DOMUtils.js';
6
- import { i18n } from '../i18n/i18n.js';
5
+ import {DOMUtils} from '../utils/DOMUtils.js';
6
+ import {i18n} from '../i18n/i18n.js';
7
7
 
8
8
  export class CaptionManager {
9
- constructor(player) {
10
- this.player = player;
11
- this.element = null;
12
- this.tracks = [];
13
- this.currentTrack = null;
14
- this.currentCue = null;
15
-
16
- this.init();
17
- }
18
-
19
- init() {
20
- this.createElement();
21
- this.loadTracks();
22
- this.attachEvents();
23
-
24
- if (this.player.options.captionsDefault && this.tracks.length > 0) {
25
- this.enable();
9
+ constructor(player) {
10
+ this.player = player;
11
+ this.element = null;
12
+ this.tracks = [];
13
+ this.currentTrack = null;
14
+ this.currentCue = null;
15
+
16
+ this.init();
26
17
  }
27
- }
28
-
29
- createElement() {
30
- this.element = DOMUtils.createElement('div', {
31
- className: `${this.player.options.classPrefix}-captions`,
32
- attributes: {
33
- 'aria-live': 'polite',
34
- 'aria-atomic': 'true',
35
- 'role': 'region',
36
- 'aria-label': i18n.t('player.captions')
37
- }
38
- });
39
-
40
- // Apply caption styles
41
- this.updateStyles();
42
-
43
- // Append to videoWrapper if it exists, otherwise to container
44
- const target = this.player.videoWrapper || this.player.container;
45
- target.appendChild(this.element);
46
- }
47
-
48
- loadTracks() {
49
- const textTracks = this.player.element.textTracks;
50
-
51
- for (let i = 0; i < textTracks.length; i++) {
52
- const track = textTracks[i];
53
-
54
- if (track.kind === 'subtitles' || track.kind === 'captions') {
55
- this.tracks.push({
56
- track: track,
57
- language: track.language,
58
- label: track.label,
59
- kind: track.kind,
60
- index: i
18
+
19
+ init() {
20
+ this.createElement();
21
+ this.loadTracks();
22
+ this.attachEvents();
23
+
24
+ if (this.player.options.captionsDefault && this.tracks.length > 0) {
25
+ this.enable();
26
+ }
27
+ }
28
+
29
+ createElement() {
30
+ this.element = DOMUtils.createElement('div', {
31
+ className: `${this.player.options.classPrefix}-captions`,
32
+ attributes: {
33
+ 'aria-live': 'polite',
34
+ 'aria-atomic': 'true',
35
+ 'role': 'region',
36
+ 'aria-label': i18n.t('player.captions')
37
+ }
61
38
  });
62
-
63
- // Disable all tracks initially
64
- track.mode = 'hidden';
65
- }
39
+
40
+ // Apply caption styles
41
+ this.updateStyles();
42
+
43
+ // Append to videoWrapper if it exists, otherwise to container
44
+ const target = this.player.videoWrapper || this.player.container;
45
+ target.appendChild(this.element);
66
46
  }
67
- }
68
47
 
69
- attachEvents() {
70
- this.player.on('timeupdate', () => {
71
- this.updateCaptions();
72
- });
48
+ loadTracks() {
49
+ const textTracks = this.player.element.textTracks;
50
+
51
+ for (let i = 0; i < textTracks.length; i++) {
52
+ const track = textTracks[i];
53
+
54
+ if (track.kind === 'subtitles' || track.kind === 'captions') {
55
+ this.tracks.push({
56
+ track: track,
57
+ language: track.language,
58
+ label: track.label,
59
+ kind: track.kind,
60
+ index: i
61
+ });
62
+
63
+ // Disable all tracks initially
64
+ track.mode = 'hidden';
65
+ }
66
+ }
67
+ }
73
68
 
74
- this.player.on('captionschange', () => {
75
- this.updateStyles();
76
- });
77
- }
69
+ attachEvents() {
70
+ this.player.on('timeupdate', () => {
71
+ this.updateCaptions();
72
+ });
78
73
 
79
- enable(trackIndex = 0) {
80
- if (this.tracks.length === 0) {
81
- return;
74
+ this.player.on('captionschange', () => {
75
+ this.updateStyles();
76
+ });
82
77
  }
83
78
 
84
- // Disable current track
85
- if (this.currentTrack) {
86
- this.currentTrack.track.mode = 'hidden';
79
+ enable(trackIndex = 0) {
80
+ if (this.tracks.length === 0) {
81
+ return;
82
+ }
83
+
84
+ // Disable current track
85
+ if (this.currentTrack) {
86
+ this.currentTrack.track.mode = 'hidden';
87
+ }
88
+
89
+ // Enable selected track
90
+ const selectedTrack = this.tracks[trackIndex];
91
+
92
+ if (selectedTrack) {
93
+ // Set to 'hidden' not 'showing' to prevent browser from displaying native captions
94
+ // We'll handle the display ourselves
95
+ selectedTrack.track.mode = 'hidden';
96
+ this.currentTrack = selectedTrack;
97
+ this.player.state.captionsEnabled = true;
98
+
99
+ // Remove any existing cuechange listener
100
+ if (this.cueChangeHandler) {
101
+ selectedTrack.track.removeEventListener('cuechange', this.cueChangeHandler);
102
+ }
103
+
104
+ // Add event listener for cue changes
105
+ this.cueChangeHandler = () => {
106
+ this.updateCaptions();
107
+ };
108
+ selectedTrack.track.addEventListener('cuechange', this.cueChangeHandler);
109
+
110
+ this.player.emit('captionsenabled', selectedTrack);
111
+ }
87
112
  }
88
113
 
89
- // Enable selected track
90
- const selectedTrack = this.tracks[trackIndex];
91
-
92
- if (selectedTrack) {
93
- // Set to 'hidden' not 'showing' to prevent browser from displaying native captions
94
- // We'll handle the display ourselves
95
- selectedTrack.track.mode = 'hidden';
96
- this.currentTrack = selectedTrack;
97
- this.player.state.captionsEnabled = true;
98
-
99
- // Remove any existing cuechange listener
100
- if (this.cueChangeHandler) {
101
- selectedTrack.track.removeEventListener('cuechange', this.cueChangeHandler);
102
- }
103
-
104
- // Add event listener for cue changes
105
- this.cueChangeHandler = () => {
106
- this.updateCaptions();
107
- };
108
- selectedTrack.track.addEventListener('cuechange', this.cueChangeHandler);
109
-
110
- this.element.style.display = 'block';
111
-
112
- this.player.emit('captionsenabled', selectedTrack);
114
+ disable() {
115
+ if (this.currentTrack) {
116
+ this.currentTrack.track.mode = 'hidden';
117
+ this.currentTrack = null;
118
+ }
119
+
120
+ this.element.style.display = 'none';
121
+ this.element.innerHTML = '';
122
+ this.currentCue = null;
123
+ this.player.state.captionsEnabled = false;
124
+ this.player.emit('captionsdisabled');
113
125
  }
114
- }
115
126
 
116
- disable() {
117
- if (this.currentTrack) {
118
- this.currentTrack.track.mode = 'hidden';
119
- this.currentTrack = null;
127
+ updateCaptions() {
128
+ if (!this.currentTrack) {
129
+ return;
130
+ }
131
+
132
+ if (!this.currentTrack.track.activeCues) {
133
+ return;
134
+ }
135
+
136
+ const activeCues = this.currentTrack.track.activeCues;
137
+
138
+ if (activeCues.length > 0) {
139
+ const cue = activeCues[0];
140
+
141
+ // Only update if the cue has changed
142
+ if (this.currentCue !== cue) {
143
+ this.currentCue = cue;
144
+
145
+ // Parse and display cue text
146
+ let text = cue.text;
147
+
148
+ // Handle VTT formatting
149
+ text = this.parseVTTFormatting(text);
150
+
151
+ this.element.innerHTML = DOMUtils.sanitizeHTML(text);
152
+
153
+ // Make sure it's visible when there's content
154
+ this.element.style.display = 'block';
155
+
156
+ this.player.emit('captionchange', cue);
157
+ }
158
+ } else if (this.currentCue) {
159
+ // Clear caption
160
+ this.element.innerHTML = '';
161
+ this.element.style.display = 'none';
162
+ this.currentCue = null;
163
+ }
120
164
  }
121
165
 
122
- this.element.style.display = 'none';
123
- this.element.innerHTML = '';
124
- this.currentCue = null;
125
- this.player.state.captionsEnabled = false;
126
- this.player.emit('captionsdisabled');
127
- }
166
+ parseVTTFormatting(text) {
167
+ // Basic VTT tag support
168
+ text = text.replace(/<c[^>]*>(.*?)<\/c>/g, '<span class="caption-class">$1</span>');
169
+ text = text.replace(/<b>(.*?)<\/b>/g, '<strong>$1</strong>');
170
+ text = text.replace(/<i>(.*?)<\/i>/g, '<em>$1</em>');
171
+ text = text.replace(/<u>(.*?)<\/u>/g, '<u>$1</u>');
172
+
173
+ // Voice tags
174
+ text = text.replace(/<v\s+([^>]+)>(.*?)<\/v>/g, '<span class="caption-voice" data-voice="$1">$2</span>');
128
175
 
129
- updateCaptions() {
130
- if (!this.currentTrack) {
131
- return;
176
+ return text;
132
177
  }
133
-
134
- if (!this.currentTrack.track.activeCues) {
135
- return;
178
+
179
+ updateStyles() {
180
+ if (!this.element) return;
181
+
182
+ const options = this.player.options;
183
+
184
+ this.element.style.fontSize = options.captionsFontSize;
185
+ this.element.style.fontFamily = options.captionsFontFamily;
186
+ this.element.style.color = options.captionsColor;
187
+ this.element.style.backgroundColor = this.hexToRgba(
188
+ options.captionsBackgroundColor,
189
+ options.captionsOpacity
190
+ );
136
191
  }
137
192
 
138
- const activeCues = this.currentTrack.track.activeCues;
139
-
140
- if (activeCues.length > 0) {
141
- const cue = activeCues[0];
142
-
143
- // Only update if the cue has changed
144
- if (this.currentCue !== cue) {
145
- this.currentCue = cue;
146
-
147
- // Parse and display cue text
148
- let text = cue.text;
149
-
150
- // Handle VTT formatting
151
- text = this.parseVTTFormatting(text);
152
-
153
- this.element.innerHTML = DOMUtils.sanitizeHTML(text);
154
-
155
- // Make sure it's visible when there's content
156
- this.element.style.display = 'block';
157
-
158
- this.player.emit('captionchange', cue);
159
- }
160
- } else if (this.currentCue) {
161
- // Clear caption
162
- this.element.innerHTML = '';
163
- this.element.style.display = 'none';
164
- this.currentCue = null;
193
+ hexToRgba(hex, alpha) {
194
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
195
+ if (result) {
196
+ return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${alpha})`;
197
+ }
198
+ return hex;
165
199
  }
166
- }
167
-
168
- parseVTTFormatting(text) {
169
- // Basic VTT tag support
170
- text = text.replace(/<c[^>]*>(.*?)<\/c>/g, '<span class="caption-class">$1</span>');
171
- text = text.replace(/<b>(.*?)<\/b>/g, '<strong>$1</strong>');
172
- text = text.replace(/<i>(.*?)<\/i>/g, '<em>$1</em>');
173
- text = text.replace(/<u>(.*?)<\/u>/g, '<u>$1</u>');
174
-
175
- // Voice tags
176
- text = text.replace(/<v\s+([^>]+)>(.*?)<\/v>/g, '<span class="caption-voice" data-voice="$1">$2</span>');
177
-
178
- return text;
179
- }
180
-
181
- updateStyles() {
182
- if (!this.element) return;
183
-
184
- const options = this.player.options;
185
-
186
- this.element.style.fontSize = options.captionsFontSize;
187
- this.element.style.fontFamily = options.captionsFontFamily;
188
- this.element.style.color = options.captionsColor;
189
- this.element.style.backgroundColor = this.hexToRgba(
190
- options.captionsBackgroundColor,
191
- options.captionsOpacity
192
- );
193
- }
194
-
195
- hexToRgba(hex, alpha) {
196
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
197
- if (result) {
198
- return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${alpha})`;
200
+
201
+ setCaptionStyle(property, value) {
202
+ switch (property) {
203
+ case 'fontSize':
204
+ this.player.options.captionsFontSize = value;
205
+ break;
206
+ case 'fontFamily':
207
+ this.player.options.captionsFontFamily = value;
208
+ break;
209
+ case 'color':
210
+ this.player.options.captionsColor = value;
211
+ break;
212
+ case 'backgroundColor':
213
+ this.player.options.captionsBackgroundColor = value;
214
+ break;
215
+ case 'opacity':
216
+ this.player.options.captionsOpacity = value;
217
+ break;
218
+ }
219
+
220
+ this.updateStyles();
221
+ this.player.emit('captionschange');
199
222
  }
200
- return hex;
201
- }
202
-
203
- setCaptionStyle(property, value) {
204
- switch (property) {
205
- case 'fontSize':
206
- this.player.options.captionsFontSize = value;
207
- break;
208
- case 'fontFamily':
209
- this.player.options.captionsFontFamily = value;
210
- break;
211
- case 'color':
212
- this.player.options.captionsColor = value;
213
- break;
214
- case 'backgroundColor':
215
- this.player.options.captionsBackgroundColor = value;
216
- break;
217
- case 'opacity':
218
- this.player.options.captionsOpacity = value;
219
- break;
223
+
224
+ getAvailableTracks() {
225
+ return this.tracks.map((t, index) => ({
226
+ index,
227
+ language: t.language,
228
+ label: t.label || t.language,
229
+ kind: t.kind
230
+ }));
220
231
  }
221
-
222
- this.updateStyles();
223
- this.player.emit('captionschange');
224
- }
225
-
226
- getAvailableTracks() {
227
- return this.tracks.map((t, index) => ({
228
- index,
229
- language: t.language,
230
- label: t.label || t.language,
231
- kind: t.kind
232
- }));
233
- }
234
-
235
- switchTrack(trackIndex) {
236
- if (trackIndex >= 0 && trackIndex < this.tracks.length) {
237
- this.disable();
238
- this.enable(trackIndex);
232
+
233
+ switchTrack(trackIndex) {
234
+ if (trackIndex >= 0 && trackIndex < this.tracks.length) {
235
+ this.disable();
236
+ this.enable(trackIndex);
237
+ }
239
238
  }
240
- }
241
239
 
242
- destroy() {
243
- this.disable();
244
-
245
- if (this.element && this.element.parentNode) {
246
- this.element.parentNode.removeChild(this.element);
240
+ destroy() {
241
+ this.disable();
242
+
243
+ if (this.element && this.element.parentNode) {
244
+ this.element.parentNode.removeChild(this.element);
245
+ }
247
246
  }
248
- }
249
247
  }
250
248