vidply 1.0.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.
@@ -0,0 +1,332 @@
1
+ {
2
+ "inputs": {
3
+ "src/utils/EventEmitter.js": {
4
+ "bytes": 949,
5
+ "imports": [],
6
+ "format": "esm"
7
+ },
8
+ "src/utils/DOMUtils.js": {
9
+ "bytes": 3730,
10
+ "imports": [],
11
+ "format": "esm"
12
+ },
13
+ "src/utils/TimeUtils.js": {
14
+ "bytes": 1940,
15
+ "imports": [],
16
+ "format": "esm"
17
+ },
18
+ "src/icons/Icons.js": {
19
+ "bytes": 12581,
20
+ "imports": [],
21
+ "format": "esm"
22
+ },
23
+ "src/i18n/translations.js": {
24
+ "bytes": 15062,
25
+ "imports": [],
26
+ "format": "esm"
27
+ },
28
+ "src/i18n/i18n.js": {
29
+ "bytes": 1573,
30
+ "imports": [
31
+ {
32
+ "path": "src/i18n/translations.js",
33
+ "kind": "import-statement",
34
+ "original": "./translations.js"
35
+ }
36
+ ],
37
+ "format": "esm"
38
+ },
39
+ "src/controls/ControlBar.js": {
40
+ "bytes": 66505,
41
+ "imports": [
42
+ {
43
+ "path": "src/utils/DOMUtils.js",
44
+ "kind": "import-statement",
45
+ "original": "../utils/DOMUtils.js"
46
+ },
47
+ {
48
+ "path": "src/utils/TimeUtils.js",
49
+ "kind": "import-statement",
50
+ "original": "../utils/TimeUtils.js"
51
+ },
52
+ {
53
+ "path": "src/icons/Icons.js",
54
+ "kind": "import-statement",
55
+ "original": "../icons/Icons.js"
56
+ },
57
+ {
58
+ "path": "src/i18n/i18n.js",
59
+ "kind": "import-statement",
60
+ "original": "../i18n/i18n.js"
61
+ }
62
+ ],
63
+ "format": "esm"
64
+ },
65
+ "src/controls/CaptionManager.js": {
66
+ "bytes": 6437,
67
+ "imports": [
68
+ {
69
+ "path": "src/utils/DOMUtils.js",
70
+ "kind": "import-statement",
71
+ "original": "../utils/DOMUtils.js"
72
+ },
73
+ {
74
+ "path": "src/i18n/i18n.js",
75
+ "kind": "import-statement",
76
+ "original": "../i18n/i18n.js"
77
+ }
78
+ ],
79
+ "format": "esm"
80
+ },
81
+ "src/controls/KeyboardManager.js": {
82
+ "bytes": 5131,
83
+ "imports": [],
84
+ "format": "esm"
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
+ "src/controls/TranscriptManager.js": {
108
+ "bytes": 23405,
109
+ "imports": [
110
+ {
111
+ "path": "src/utils/DOMUtils.js",
112
+ "kind": "import-statement",
113
+ "original": "../utils/DOMUtils.js"
114
+ },
115
+ {
116
+ "path": "src/utils/TimeUtils.js",
117
+ "kind": "import-statement",
118
+ "original": "../utils/TimeUtils.js"
119
+ },
120
+ {
121
+ "path": "src/icons/Icons.js",
122
+ "kind": "import-statement",
123
+ "original": "../icons/Icons.js"
124
+ },
125
+ {
126
+ "path": "src/i18n/i18n.js",
127
+ "kind": "import-statement",
128
+ "original": "../i18n/i18n.js"
129
+ }
130
+ ],
131
+ "format": "esm"
132
+ },
133
+ "src/renderers/HTML5Renderer.js": {
134
+ "bytes": 8362,
135
+ "imports": [],
136
+ "format": "esm"
137
+ },
138
+ "src/renderers/YouTubeRenderer.js": {
139
+ "bytes": 6912,
140
+ "imports": [],
141
+ "format": "esm"
142
+ },
143
+ "src/renderers/VimeoRenderer.js": {
144
+ "bytes": 6442,
145
+ "imports": [],
146
+ "format": "esm"
147
+ },
148
+ "src/renderers/HLSRenderer.js": {
149
+ "bytes": 8455,
150
+ "imports": [
151
+ {
152
+ "path": "src/renderers/HTML5Renderer.js",
153
+ "kind": "dynamic-import",
154
+ "original": "./HTML5Renderer.js"
155
+ }
156
+ ],
157
+ "format": "esm"
158
+ },
159
+ "src/core/Player.js": {
160
+ "bytes": 30756,
161
+ "imports": [
162
+ {
163
+ "path": "src/utils/EventEmitter.js",
164
+ "kind": "import-statement",
165
+ "original": "../utils/EventEmitter.js"
166
+ },
167
+ {
168
+ "path": "src/utils/DOMUtils.js",
169
+ "kind": "import-statement",
170
+ "original": "../utils/DOMUtils.js"
171
+ },
172
+ {
173
+ "path": "src/utils/TimeUtils.js",
174
+ "kind": "import-statement",
175
+ "original": "../utils/TimeUtils.js"
176
+ },
177
+ {
178
+ "path": "src/controls/ControlBar.js",
179
+ "kind": "import-statement",
180
+ "original": "../controls/ControlBar.js"
181
+ },
182
+ {
183
+ "path": "src/controls/CaptionManager.js",
184
+ "kind": "import-statement",
185
+ "original": "../controls/CaptionManager.js"
186
+ },
187
+ {
188
+ "path": "src/controls/KeyboardManager.js",
189
+ "kind": "import-statement",
190
+ "original": "../controls/KeyboardManager.js"
191
+ },
192
+ {
193
+ "path": "src/controls/SettingsDialog.js",
194
+ "kind": "import-statement",
195
+ "original": "../controls/SettingsDialog.js"
196
+ },
197
+ {
198
+ "path": "src/controls/TranscriptManager.js",
199
+ "kind": "import-statement",
200
+ "original": "../controls/TranscriptManager.js"
201
+ },
202
+ {
203
+ "path": "src/renderers/HTML5Renderer.js",
204
+ "kind": "import-statement",
205
+ "original": "../renderers/HTML5Renderer.js"
206
+ },
207
+ {
208
+ "path": "src/renderers/YouTubeRenderer.js",
209
+ "kind": "import-statement",
210
+ "original": "../renderers/YouTubeRenderer.js"
211
+ },
212
+ {
213
+ "path": "src/renderers/VimeoRenderer.js",
214
+ "kind": "import-statement",
215
+ "original": "../renderers/VimeoRenderer.js"
216
+ },
217
+ {
218
+ "path": "src/renderers/HLSRenderer.js",
219
+ "kind": "import-statement",
220
+ "original": "../renderers/HLSRenderer.js"
221
+ },
222
+ {
223
+ "path": "src/icons/Icons.js",
224
+ "kind": "import-statement",
225
+ "original": "../icons/Icons.js"
226
+ },
227
+ {
228
+ "path": "src/i18n/i18n.js",
229
+ "kind": "import-statement",
230
+ "original": "../i18n/i18n.js"
231
+ }
232
+ ],
233
+ "format": "esm"
234
+ },
235
+ "src/features/PlaylistManager.js": {
236
+ "bytes": 10068,
237
+ "imports": [
238
+ {
239
+ "path": "src/utils/DOMUtils.js",
240
+ "kind": "import-statement",
241
+ "original": "../utils/DOMUtils.js"
242
+ },
243
+ {
244
+ "path": "src/icons/Icons.js",
245
+ "kind": "import-statement",
246
+ "original": "../icons/Icons.js"
247
+ }
248
+ ],
249
+ "format": "esm"
250
+ },
251
+ "src/index.js": {
252
+ "bytes": 833,
253
+ "imports": [
254
+ {
255
+ "path": "src/core/Player.js",
256
+ "kind": "import-statement",
257
+ "original": "./core/Player.js"
258
+ },
259
+ {
260
+ "path": "src/features/PlaylistManager.js",
261
+ "kind": "import-statement",
262
+ "original": "./features/PlaylistManager.js"
263
+ }
264
+ ],
265
+ "format": "esm"
266
+ }
267
+ },
268
+ "outputs": {
269
+ "dist/vidply.min.js": {
270
+ "imports": [],
271
+ "exports": [],
272
+ "entryPoint": "src/index.js",
273
+ "inputs": {
274
+ "src/renderers/HTML5Renderer.js": {
275
+ "bytesInOutput": 4670
276
+ },
277
+ "src/index.js": {
278
+ "bytesInOutput": 307
279
+ },
280
+ "src/utils/EventEmitter.js": {
281
+ "bytesInOutput": 436
282
+ },
283
+ "src/utils/DOMUtils.js": {
284
+ "bytesInOutput": 1581
285
+ },
286
+ "src/utils/TimeUtils.js": {
287
+ "bytesInOutput": 728
288
+ },
289
+ "src/icons/Icons.js": {
290
+ "bytesInOutput": 10596
291
+ },
292
+ "src/i18n/translations.js": {
293
+ "bytesInOutput": 12646
294
+ },
295
+ "src/i18n/i18n.js": {
296
+ "bytesInOutput": 720
297
+ },
298
+ "src/controls/ControlBar.js": {
299
+ "bytesInOutput": 34275
300
+ },
301
+ "src/controls/CaptionManager.js": {
302
+ "bytesInOutput": 3634
303
+ },
304
+ "src/controls/KeyboardManager.js": {
305
+ "bytesInOutput": 3002
306
+ },
307
+ "src/controls/SettingsDialog.js": {
308
+ "bytesInOutput": 7622
309
+ },
310
+ "src/controls/TranscriptManager.js": {
311
+ "bytesInOutput": 12461
312
+ },
313
+ "src/core/Player.js": {
314
+ "bytesInOutput": 16392
315
+ },
316
+ "src/renderers/YouTubeRenderer.js": {
317
+ "bytesInOutput": 4140
318
+ },
319
+ "src/renderers/VimeoRenderer.js": {
320
+ "bytesInOutput": 4204
321
+ },
322
+ "src/renderers/HLSRenderer.js": {
323
+ "bytesInOutput": 5386
324
+ },
325
+ "src/features/PlaylistManager.js": {
326
+ "bytesInOutput": 5245
327
+ }
328
+ },
329
+ "bytes": 128662
330
+ }
331
+ }
332
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "vidply",
3
+ "version": "1.0.0",
4
+ "description": "Universal, accessible video & audio player with ES6 modules",
5
+ "type": "module",
6
+ "main": "dist/vidply.js",
7
+ "module": "dist/vidply.esm.js",
8
+ "files": [
9
+ "dist",
10
+ "src"
11
+ ],
12
+ "scripts": {
13
+ "dev": "npx serve . -p 3000",
14
+ "start": "npm run build:js && npm run build:css && npm run dev",
15
+ "build": "npm run build:js && npm run build:css",
16
+ "build:js": "node build/build.js",
17
+ "build:css": "node build/build-css.js",
18
+ "watch": "node build/watch.js",
19
+ "clean": "node build/clean.js"
20
+ },
21
+ "keywords": [
22
+ "video",
23
+ "player",
24
+ "audio",
25
+ "accessible",
26
+ "wcag",
27
+ "a11y",
28
+ "html5",
29
+ "youtube",
30
+ "vimeo",
31
+ "hls",
32
+ "captions",
33
+ "subtitles",
34
+ "vanilla",
35
+ "es6",
36
+ "javascript"
37
+ ],
38
+ "author": "Matthias Peltzer",
39
+ "license": "GPL-2.0-or-later",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/MatthiasPeltzer/vidply.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/MatthiasPeltzer/vidply/issues",
46
+ "email": "vidply@mpeltzer.de"
47
+ },
48
+ "homepage": "https://matthiaspeltzer.github.io/vidply/",
49
+ "devDependencies": {
50
+ "esbuild": "^0.25.10",
51
+ "clean-css": "^5.3.3"
52
+ },
53
+ "dependencies": {}
54
+ }
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Caption/Subtitle Manager
3
+ */
4
+
5
+ import { DOMUtils } from '../utils/DOMUtils.js';
6
+ import { i18n } from '../i18n/i18n.js';
7
+
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();
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
+ }
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
61
+ });
62
+
63
+ // Disable all tracks initially
64
+ track.mode = 'hidden';
65
+ }
66
+ }
67
+ }
68
+
69
+ attachEvents() {
70
+ this.player.on('timeupdate', () => {
71
+ this.updateCaptions();
72
+ });
73
+
74
+ this.player.on('captionschange', () => {
75
+ this.updateStyles();
76
+ });
77
+ }
78
+
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.element.style.display = 'block';
111
+
112
+ this.player.emit('captionsenabled', selectedTrack);
113
+ }
114
+ }
115
+
116
+ disable() {
117
+ if (this.currentTrack) {
118
+ this.currentTrack.track.mode = 'hidden';
119
+ this.currentTrack = null;
120
+ }
121
+
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
+ }
128
+
129
+ updateCaptions() {
130
+ if (!this.currentTrack) {
131
+ return;
132
+ }
133
+
134
+ if (!this.currentTrack.track.activeCues) {
135
+ return;
136
+ }
137
+
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;
165
+ }
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})`;
199
+ }
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;
220
+ }
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);
239
+ }
240
+ }
241
+
242
+ destroy() {
243
+ this.disable();
244
+
245
+ if (this.element && this.element.parentNode) {
246
+ this.element.parentNode.removeChild(this.element);
247
+ }
248
+ }
249
+ }
250
+