@viewlift/player-backup 1.0.1-lighteningjs → 1.0.1

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.
Files changed (120) hide show
  1. package/cjs/autoNextVideo-823821e8.js +1 -0
  2. package/cjs/bundle.css +657 -0
  3. package/cjs/caption-settings-67212c17.js +1 -0
  4. package/cjs/caption-settings-d42c056e.js +1 -0
  5. package/cjs/debugSelection-cbfdabb7.js +1 -0
  6. package/cjs/dvr_skin-f7f770ae.js +1 -0
  7. package/cjs/error-26d5a3c3.js +1 -0
  8. package/cjs/features-164c3cc6.js +1 -0
  9. package/cjs/generic-8b99b151.js +1 -0
  10. package/cjs/icons-7afb9c13.js +1 -0
  11. package/cjs/index-3dfe6459.js +1 -0
  12. package/cjs/index.js +1 -0
  13. package/cjs/keyboardShortcuts-cfd998c2.js +1 -0
  14. package/cjs/loader-393b838a.js +1 -0
  15. package/cjs/multicam-21c9a90b.js +1 -0
  16. package/cjs/multicam-a41fe4a6.js +1 -0
  17. package/cjs/multicamv2-76d8530b.js +1 -0
  18. package/cjs/multicamv2-c148fa24.js +1 -0
  19. package/cjs/mwebPotrait-3c11d7cf.js +1 -0
  20. package/cjs/mwebPotraitTray-b9d5d45a.js +1 -0
  21. package/cjs/nextEpisodeButton-300afadc.js +1 -0
  22. package/cjs/nextEpisodeButton-b6e6586f.js +1 -0
  23. package/cjs/nondvr_skin-ef2cb3b2.js +1 -0
  24. package/cjs/playlist-925f808b.js +1 -0
  25. package/cjs/playlist-df87ca90.js +1 -0
  26. package/cjs/renditions-bc841552.js +1 -0
  27. package/cjs/resolution-6a9adf54.js +1 -0
  28. package/cjs/series-82ec79d3.js +1 -0
  29. package/cjs/series-9c237975.js +1 -0
  30. package/cjs/series-v2-24855152.js +1 -0
  31. package/cjs/series-v2-70137d8c.js +1 -0
  32. package/cjs/settings-icon-a63f46fc.js +1 -0
  33. package/cjs/settings-icon-e6a8feee.js +1 -0
  34. package/cjs/skin-095cc078.js +1 -0
  35. package/cjs/skin-225252ff.js +1 -0
  36. package/cjs/skin-39ab30e2.js +1 -0
  37. package/cjs/skin-84083352.js +1 -0
  38. package/cjs/skin-93a3e5ae.js +1 -0
  39. package/cjs/skin-983a2ea7.js +1 -0
  40. package/cjs/skin-afbec6d7.js +1 -0
  41. package/cjs/skin-ec49b877.js +1 -0
  42. package/cjs/skin-f225a42e.js +1 -0
  43. package/cjs/skin-fb394911.js +1 -0
  44. package/cjs/util-fa21734a.js +1 -0
  45. package/cjs/videoSelector-01a9ef43.js +1 -0
  46. package/cjs/videoSelector-fcdf11eb.js +1 -0
  47. package/esm/autoNextVideo-1672740e.js +1 -0
  48. package/esm/bundle.css +657 -0
  49. package/esm/caption-settings-2e47813b.js +1 -0
  50. package/esm/caption-settings-3eacf3a0.js +1 -0
  51. package/esm/debugSelection-2601b8b3.js +1 -0
  52. package/esm/dvr_skin-9cc1b144.js +1 -0
  53. package/esm/error-2395864b.js +1 -0
  54. package/esm/features-f86404ce.js +1 -0
  55. package/esm/generic-7c43020f.js +1 -0
  56. package/esm/icons-c18c70b7.js +1 -0
  57. package/esm/index-9f022e82.js +1 -0
  58. package/esm/index.js +1 -0
  59. package/esm/keyboardShortcuts-c06f3c7b.js +1 -0
  60. package/esm/loader-580278fe.js +1 -0
  61. package/esm/multicam-94da3759.js +1 -0
  62. package/esm/multicam-de741974.js +1 -0
  63. package/esm/multicamv2-0105c40f.js +1 -0
  64. package/esm/multicamv2-d006cffa.js +1 -0
  65. package/esm/mwebPotrait-3c1805d8.js +1 -0
  66. package/esm/mwebPotraitTray-5ab39989.js +1 -0
  67. package/esm/nextEpisodeButton-2ea69b56.js +1 -0
  68. package/esm/nextEpisodeButton-a1556cdf.js +1 -0
  69. package/esm/nondvr_skin-64be70e5.js +1 -0
  70. package/esm/playlist-032fbbf3.js +1 -0
  71. package/esm/playlist-7ecf432a.js +1 -0
  72. package/esm/renditions-97276745.js +1 -0
  73. package/esm/resolution-38cd4f5c.js +1 -0
  74. package/esm/series-041152aa.js +1 -0
  75. package/esm/series-977edced.js +1 -0
  76. package/esm/series-v2-0727ee8c.js +1 -0
  77. package/esm/series-v2-516c91c2.js +1 -0
  78. package/esm/settings-icon-0125bffd.js +1 -0
  79. package/esm/settings-icon-2b713fbf.js +1 -0
  80. package/esm/skin-2c638dc1.js +1 -0
  81. package/esm/skin-42c2f81d.js +1 -0
  82. package/esm/skin-5e2fa9d4.js +1 -0
  83. package/esm/skin-6093bfb3.js +1 -0
  84. package/esm/skin-60a41d44.js +1 -0
  85. package/esm/skin-665ec17e.js +1 -0
  86. package/esm/skin-93e320fc.js +1 -0
  87. package/esm/skin-b939c35e.js +1 -0
  88. package/esm/skin-cd537931.js +1 -0
  89. package/esm/skin-d5510f70.js +1 -0
  90. package/esm/util-d9f5a7be.js +1 -0
  91. package/esm/videoSelector-8889c58e.js +1 -0
  92. package/esm/videoSelector-8e258d45.js +1 -0
  93. package/package.json +15 -31
  94. package/.babelrc +0 -8
  95. package/.browserslistrc +0 -2
  96. package/README.md +0 -29
  97. package/babel.config.json +0 -8
  98. package/dist/index.cjs.css +0 -2
  99. package/dist/index.cjs.css.map +0 -1
  100. package/dist/index.cjs.js +0 -1
  101. package/dist/index.d.ts +0 -45
  102. package/dist/index.esm.css +0 -2
  103. package/dist/index.esm.css.map +0 -1
  104. package/dist/index.esm.js +0 -1
  105. package/dist/main-player/player-ui.d.ts +0 -19
  106. package/dist/player-components/player-buttons/playerButtons.d.ts +0 -38
  107. package/dist/player-components/player-seekbar/playerSeekbar.d.ts +0 -25
  108. package/dist/player-ui/player-ui.d.ts +0 -18
  109. package/dist/playerInitializer.d.ts +0 -13
  110. package/rollup.config.mjs +0 -37
  111. package/src/index.ts +0 -242
  112. package/src/main-player/player-ui.ts +0 -65
  113. package/src/player-components/player-buttons/playerButtons.css +0 -0
  114. package/src/player-components/player-buttons/playerButtons.ts +0 -127
  115. package/src/player-components/player-seekbar/playerSeekbar.css +0 -75
  116. package/src/player-components/player-seekbar/playerSeekbar.ts +0 -158
  117. package/src/player-ui/player-ui.css +0 -8
  118. package/src/player-ui/player-ui.ts +0 -79
  119. package/src/playerInitializer.ts +0 -54
  120. package/tsconfig.json +0 -14
package/src/index.ts DELETED
@@ -1,242 +0,0 @@
1
- // LightningJS-compatible Video Player Component (ES5 compatible)
2
-
3
- export interface VideoPlayerOptions {
4
- // Optional playback callbacks
5
- onPlaybackSuccess?: () => void;
6
- onPlaybackEnded?: () => void;
7
- onPlaybackError?: (error?: any) => void;
8
- src: string;
9
- autoplay?: boolean;
10
- controls?: boolean;
11
- width?: number;
12
- height?: number;
13
- // Event callbacks
14
- }
15
-
16
- type EventHandler = (...args: any[]) => void;
17
-
18
- type VideoPlayerEvent =
19
- | 'play'
20
- | 'pause'
21
- | 'ended'
22
- | 'timeupdate'
23
- | 'error'
24
- | 'loadedmetadata'
25
- | 'seeking'
26
- | 'seeked'
27
- | 'volumechange';
28
-
29
- export class LightningVideoPlayer {
30
- /**
31
- * Reinitialize the current videoElement with a new set of options and handle autoplay.
32
- */
33
- reinitialize(options: VideoPlayerOptions) {
34
- this.options = options;
35
- this.videoElement.src = options.src;
36
- this.videoElement.autoplay = !!options.autoplay;
37
- this.videoElement.controls = !!options.controls;
38
- if (options.width) {
39
- this.videoElement.width = options.width;
40
- } else {
41
- this.videoElement.removeAttribute('width');
42
- }
43
- if (options.height) {
44
- this.videoElement.height = options.height;
45
- } else {
46
- this.videoElement.removeAttribute('height');
47
- }
48
- // Reset playback state
49
- this.videoElement.currentTime = 0;
50
- this.videoElement.pause();
51
- // If autoplay is set, start playback
52
- if (options.autoplay) {
53
- // Wait for metadata to be loaded before playing (for some browsers)
54
- const playOnLoaded = () => {
55
- this.videoElement.play();
56
- this.videoElement.removeEventListener('loadedmetadata', playOnLoaded);
57
- };
58
- this.videoElement.addEventListener('loadedmetadata', playOnLoaded);
59
- // If already loaded, play immediately
60
- if (this.videoElement.readyState >= 1) {
61
- this.videoElement.play();
62
- this.videoElement.removeEventListener('loadedmetadata', playOnLoaded);
63
- }
64
- }
65
- }
66
- private videoElement: HTMLVideoElement;
67
- private options: VideoPlayerOptions;
68
- private listeners: { [event: string]: EventHandler[] } = {};
69
- private boundEmitters: { [event: string]: EventListener } = {};
70
- private boundHandlerMap: { [K in VideoPlayerEvent]?: (e: Event) => void } = {};
71
-
72
- constructor(options: VideoPlayerOptions) {
73
- this.options = options;
74
- this.videoElement = document.createElement('video');
75
- this.videoElement.src = options.src;
76
- this.videoElement.autoplay = !!options.autoplay;
77
- this.videoElement.controls = !!options.controls;
78
- if (options.width) this.videoElement.width = options.width;
79
- if (options.height) this.videoElement.height = options.height;
80
- this.videoElement.style.display = 'block';
81
- this.videoElement.style.background = 'black';
82
-
83
- // Attach class event handlers for all player events
84
- const handlerMap: { [K in VideoPlayerEvent]: (e: Event) => void } = {
85
- play: this.onPlay,
86
- pause: this.onPause,
87
- ended: this.onEnded,
88
- timeupdate: this.onTimeupdate,
89
- error: this.onError,
90
- loadedmetadata: this.onLoadedmetadata,
91
- seeking: this.onSeeking,
92
- seeked: this.onSeeked,
93
- volumechange: this.onVolumechange,
94
- };
95
- (Object.keys(handlerMap) as VideoPlayerEvent[]).forEach(event => {
96
- const boundHandler = handlerMap[event].bind(this);
97
- this.boundHandlerMap[event] = boundHandler;
98
- this.videoElement.addEventListener(event, boundHandler);
99
- });
100
- }
101
-
102
- attachTo(container: HTMLElement) {
103
- container.appendChild(this.videoElement);
104
- }
105
-
106
- play() {
107
- this.videoElement.play();
108
- }
109
-
110
- pause() {
111
- this.videoElement.pause();
112
- }
113
-
114
- stop() {
115
- this.videoElement.pause();
116
- this.videoElement.currentTime = 0;
117
- }
118
-
119
- destroy() {
120
- if (this.videoElement.parentNode) {
121
- this.videoElement.parentNode.removeChild(this.videoElement);
122
- }
123
- this.removeAllListeners();
124
- // Remove class event handlers
125
- (Object.keys(this.boundHandlerMap) as VideoPlayerEvent[]).forEach(event => {
126
- if (this.boundHandlerMap[event]) {
127
- this.videoElement.removeEventListener(event, this.boundHandlerMap[event]!);
128
- delete this.boundHandlerMap[event];
129
- }
130
- });
131
- }
132
-
133
- get element(): HTMLVideoElement {
134
- return this.videoElement;
135
- }
136
-
137
- // Event callback function definitions
138
- onPlay(e: Event) {
139
- // Called when playback starts
140
- if (typeof this.options.onPlaybackSuccess === 'function') {
141
- this.options.onPlaybackSuccess();
142
- }
143
- }
144
-
145
- onPause(e: Event) {
146
- // Called when playback is paused
147
- // Override or pass as option
148
- }
149
-
150
- onEnded(e: Event) {
151
- // Called when playback ends
152
- if (typeof this.options.onPlaybackEnded === 'function') {
153
- this.options.onPlaybackEnded();
154
- }
155
- }
156
-
157
- onTimeupdate(e: Event) {
158
- // Called when playback position changes
159
- // Override or pass as option
160
- }
161
-
162
- onError(e: Event) {
163
- // Called on playback error
164
- if (typeof this.options.onPlaybackError === 'function') {
165
- this.options.onPlaybackError(e);
166
- }
167
- }
168
-
169
- onLoadedmetadata(e: Event) {
170
- // Called when metadata is loaded
171
- // Override or pass as option
172
- }
173
-
174
- onSeeking(e: Event) {
175
- // Called when seeking starts
176
- // Override or pass as option
177
- }
178
-
179
- onSeeked(e: Event) {
180
- // Called when seeking ends
181
- // Override or pass as option
182
- }
183
-
184
- onVolumechange(e: Event) {
185
- // Called when volume changes
186
- // Override or pass as option
187
- }
188
-
189
- // Event system
190
- on(event: VideoPlayerEvent, handler: EventHandler) {
191
- if (!this.listeners[event]) {
192
- this.listeners[event] = [];
193
- if (!this.boundEmitters[event]) {
194
- this.boundEmitters[event] = this.emitEvent(event);
195
- this.videoElement.addEventListener(event, this.boundEmitters[event]);
196
- }
197
- }
198
- this.listeners[event].push(handler);
199
- }
200
-
201
- off(event: VideoPlayerEvent, handler: EventHandler) {
202
- if (!this.listeners[event]) return;
203
- this.listeners[event] = this.listeners[event].filter(h => h !== handler);
204
- if (this.listeners[event].length === 0) {
205
- if (this.boundEmitters[event]) {
206
- this.videoElement.removeEventListener(event, this.boundEmitters[event]);
207
- delete this.boundEmitters[event];
208
- }
209
- delete this.listeners[event];
210
- }
211
- }
212
-
213
- once(event: VideoPlayerEvent, handler: EventHandler) {
214
- const onceHandler: EventHandler = (...args) => {
215
- handler(...args);
216
- this.off(event, onceHandler);
217
- };
218
- this.on(event, onceHandler);
219
- }
220
-
221
- private emitEvent(event: VideoPlayerEvent) {
222
- return (e: Event) => {
223
- if (this.listeners[event]) {
224
- this.listeners[event].forEach(handler => handler(e));
225
- }
226
- };
227
- }
228
-
229
- private removeAllListeners() {
230
- (Object.keys(this.listeners) as VideoPlayerEvent[]).forEach(event => {
231
- if (this.boundEmitters[event]) {
232
- this.videoElement.removeEventListener(event, this.boundEmitters[event]);
233
- delete this.boundEmitters[event];
234
- }
235
- });
236
- this.listeners = {};
237
- }
238
- }
239
-
240
- function getHandlerMethodName(event: string): string {
241
- return 'on' + event.charAt(0).toUpperCase() + event.slice(1);
242
- }
@@ -1,65 +0,0 @@
1
- import { Button } from '../player-components/player-buttons/playerButtons';
2
- import { PlayerSeekbar } from '../player-components/player-seekbar/playerSeekbar';
3
-
4
- interface VideoPlayerUIOptions {
5
- player: { video: HTMLVideoElement; play: () => void; pause: () => void };
6
- width?: string;
7
- height?: string;
8
- }
9
-
10
- class VideoPlayerUI {
11
- private player: { video: HTMLVideoElement; play: () => void; pause: () => void };
12
- private container: HTMLDivElement;
13
- private playPauseBtn?: Button;
14
- private seekbar?: PlayerSeekbar;
15
-
16
- constructor({ player, width = '1280px', height = '720px' }: VideoPlayerUIOptions) {
17
- this.player = player;
18
- this.container = document.createElement('div');
19
- this.container.className = 'video-player-ui';
20
- if (width) this.container.style.width = width;
21
- if (height) this.container.style.height = height;
22
- this.createControls(width, height);
23
- }
24
-
25
- private createControls(width?: string, height?: string) {
26
- // Play/Pause button using Button class
27
- this.playPauseBtn = new Button({
28
- imgSrc: '', // Optionally provide an image
29
- imgAltText: 'Play/Pause',
30
- buttonText: 'Play',
31
- width: 80,
32
- height: 40,
33
- klassname: 'play-pause-btn',
34
- focused: false,
35
- disabled: false,
36
- handleClick: () => {
37
- if (this.player.video.paused) {
38
- this.player.play();
39
- if (this.playPauseBtn && this.playPauseBtn.buttonElement) {
40
- this.playPauseBtn.buttonElement.textContent = 'Pause';
41
- }
42
- } else {
43
- this.player.pause();
44
- if (this.playPauseBtn && this.playPauseBtn.buttonElement) {
45
- this.playPauseBtn.buttonElement.textContent = 'Play';
46
- }
47
- }
48
- }
49
- });
50
- this.playPauseBtn.mount(this.container);
51
- // Attach the video element
52
- if (width) this.player.video.style.width = width;
53
- if (height) this.player.video.style.height = height;
54
- this.container.appendChild(this.player.video);
55
- // Add the seekbar below the video
56
- this.seekbar = new PlayerSeekbar(this.player.video);
57
- this.seekbar.mount(this.container);
58
- }
59
-
60
- attachTo(parent: HTMLElement) {
61
- parent.appendChild(this.container);
62
- }
63
- }
64
-
65
- export default VideoPlayerUI;
@@ -1,127 +0,0 @@
1
- import './playerButtons.css';
2
-
3
- type EventHandler = (event?: any) => void;
4
-
5
- type ButtonProps = {
6
- imgSrc: string;
7
- imgAltText: string;
8
- width: number;
9
- height: number;
10
- klassname: string;
11
- buttonText: string;
12
- focused: boolean;
13
- disabled: boolean;
14
- handleClick: EventHandler
15
- };
16
-
17
- export class Button {
18
-
19
- private imgSrc: string;
20
- private imgAltText: string;
21
- private klassname: string;
22
- private width: number;
23
- private height: number;
24
- private focused: boolean;
25
- private disabled: boolean;
26
- private handleClick: EventHandler;
27
- private buttonText: string;
28
- public buttonElement?: HTMLButtonElement;
29
-
30
-
31
-
32
- constructor(props: ButtonProps) {
33
- this.imgSrc = props.imgSrc || '';
34
- this.imgAltText = props.imgAltText || '';
35
- this.buttonText = props.buttonText || '';
36
- this.klassname = props.klassname || 'default-button';
37
- this.width = props.width || 100;
38
- this.height = props.height || 50;
39
- this.focused = props.focused || false;
40
- this.disabled = props.disabled || false;
41
- this.handleClick = props.handleClick || (() => {});
42
-
43
- if (typeof props.handleClick !== 'function') {
44
- throw new Error('Button Class: handleClick must be a function');
45
- }
46
-
47
- if (this.disabled) {
48
- this.klassname += ' disabled';
49
- }
50
-
51
- if (this.focused) {
52
- this.klassname += ' focused';
53
- }
54
-
55
- this.handleClick = this.handleClick.bind(this);
56
- }
57
-
58
- /**
59
- * Mounts the button to the specified mount point
60
- * @param mountPoint The HTML element to which the button will be appended
61
- */
62
- mount(mountPoint: HTMLElement) {
63
- // Create the button element
64
- const button = document.createElement('button');
65
- button.className = this.klassname;
66
- button.style.width = `${this.width}px`;
67
- button.style.height = `${this.height}px`;
68
- button.disabled = this.disabled;
69
- button.tabIndex = 0;
70
-
71
- // Add image or text
72
- if (this.imgSrc) {
73
- const img = document.createElement('img');
74
- img.src = this.imgSrc;
75
- img.alt = this.imgAltText;
76
- button.appendChild(img);
77
- } else {
78
- const span = document.createElement('span');
79
- span.textContent = this.buttonText;
80
- button.appendChild(span);
81
- }
82
-
83
- // Attach event listeners
84
- button.addEventListener('click', this.handleClick);
85
- button.addEventListener('focus', () => {
86
- this.focused = true;
87
- button.classList.add('focused');
88
- });
89
- button.addEventListener('blur', () => {
90
- this.focused = false;
91
- button.classList.remove('focused');
92
- });
93
-
94
- // Store reference and append
95
- this.buttonElement = button;
96
- mountPoint.appendChild(button);
97
- }
98
-
99
- /**
100
- * Removes the click event listener from the button
101
- */
102
- unmount() {
103
- if (this.buttonElement) {
104
- this.buttonElement.removeEventListener('click', this.handleClick);
105
- this.buttonElement.removeEventListener('focus', this.handleClick);
106
- this.buttonElement.removeEventListener('blur', this.handleClick);
107
- this.buttonElement = undefined;
108
- }
109
- }
110
-
111
- focus() {
112
- if (this.buttonElement) {
113
- this.buttonElement.focus();
114
- this.buttonElement.classList.add('focused');
115
- this.focused = true;
116
- }
117
- }
118
-
119
- blur() {
120
- if (this.buttonElement) {
121
- this.buttonElement.blur();
122
- this.buttonElement.classList.remove('focused');
123
- this.focused = false;
124
- }
125
- }
126
-
127
- }
@@ -1,75 +0,0 @@
1
- .seekbar-container {
2
- position: absolute;
3
- left: 50%;
4
- transform: translateX(-50%);
5
- bottom: 50px;
6
- height: 12px;
7
- background: #444;
8
- width: 90%;
9
- cursor: pointer;
10
- border-radius: 6px;
11
- margin: 0;
12
- user-select: none;
13
- z-index: 20;
14
- }
15
-
16
- .seekbar-buffer {
17
- position: absolute;
18
- height: 100%;
19
- background: #888;
20
- width: 0;
21
- border-radius: 6px;
22
- z-index: 1;
23
- }
24
-
25
- .seekbar-progress {
26
- position: absolute;
27
- height: 100%;
28
- background: #4169E1;
29
- width: 0;
30
- border-radius: 6px;
31
- z-index: 2;
32
- transition: width 0.1s linear;
33
- }
34
-
35
- .seekbar-thumb {
36
- position: absolute;
37
- top: 50%;
38
- transform: translate(-50%, -50%);
39
- width: 16px;
40
- height: 16px;
41
- background: #fff;
42
- border-radius: 50%;
43
- border: 2px solid #4169E1;
44
- z-index: 3;
45
- box-shadow: 0 2px 8px rgba(0,0,0,0.2);
46
- cursor: pointer;
47
- transition: background 0.2s;
48
- }
49
- .seekbar-thumb:hover {
50
- background: #ffeaea;
51
- }
52
-
53
- .seekbar-tooltip {
54
- position: absolute;
55
- bottom: 24px;
56
- left: 0;
57
- transform: translateX(-50%);
58
- background: #222;
59
- color: #fff;
60
- padding: 2px 8px;
61
- border-radius: 4px;
62
- font-size: 12px;
63
- white-space: nowrap;
64
- pointer-events: none;
65
- z-index: 10;
66
- }
67
-
68
- .seekbar-time-label, .seekbar-duration-label {
69
- position: relative;
70
- top: 18px;
71
- font-size: 13px;
72
- color: #eee;
73
- margin-right: 4px;
74
- z-index: 5;
75
- }
@@ -1,158 +0,0 @@
1
- import './playerSeekbar.css';
2
-
3
- export class PlayerSeekbar {
4
- private container: HTMLDivElement;
5
- private progress: HTMLDivElement;
6
- private buffer: HTMLDivElement;
7
- private thumb: HTMLDivElement;
8
- private tooltip: HTMLDivElement;
9
- private video: HTMLVideoElement;
10
- private timeLabel: HTMLSpanElement;
11
- private durationLabel: HTMLSpanElement;
12
- private isDragging: boolean = false;
13
- private focused: boolean = false;
14
-
15
- constructor(video: HTMLVideoElement) {
16
- this.video = video;
17
- this.container = document.createElement('div');
18
- this.container.className = 'seekbar-container';
19
- this.container.tabIndex = 0; // Make focusable
20
-
21
- this.buffer = document.createElement('div');
22
- this.buffer.className = 'seekbar-buffer';
23
-
24
- this.progress = document.createElement('div');
25
- this.progress.className = 'seekbar-progress';
26
-
27
- this.thumb = document.createElement('div');
28
- this.thumb.className = 'seekbar-thumb';
29
-
30
- this.tooltip = document.createElement('div');
31
- this.tooltip.className = 'seekbar-tooltip';
32
- this.tooltip.style.display = 'none';
33
-
34
- this.timeLabel = document.createElement('span');
35
- this.timeLabel.className = 'seekbar-time-label';
36
- this.durationLabel = document.createElement('span');
37
- this.durationLabel.className = 'seekbar-duration-label';
38
-
39
- this.container.appendChild(this.buffer);
40
- this.container.appendChild(this.progress);
41
- this.container.appendChild(this.thumb);
42
- this.container.appendChild(this.tooltip);
43
- this.container.appendChild(this.timeLabel);
44
- this.container.appendChild(this.durationLabel);
45
-
46
- // Update UI as video plays
47
- this.video.addEventListener('timeupdate', () => this.update());
48
- this.video.addEventListener('progress', () => this.updateBuffer());
49
- this.video.addEventListener('durationchange', () => this.update());
50
-
51
- // Seek on click
52
- this.container.addEventListener('click', (e) => this.seek(e));
53
- // Dragging
54
- this.thumb.addEventListener('mousedown', (e) => this.startDrag(e));
55
- // Tooltip on hover
56
- this.container.addEventListener('mousemove', (e) => this.showTooltip(e));
57
- this.container.addEventListener('mouseleave', () => this.hideTooltip());
58
-
59
- // Focus/blur events for accessibility and visual feedback
60
- this.container.addEventListener('focus', () => {
61
- this.focused = true;
62
- this.container.classList.add('focused');
63
- });
64
- this.container.addEventListener('blur', () => {
65
- this.focused = false;
66
- this.container.classList.remove('focused');
67
- });
68
- }
69
-
70
- public focus() {
71
- this.container.focus();
72
- this.container.classList.add('focused');
73
- this.focused = true;
74
- }
75
-
76
- public blur() {
77
- this.container.blur();
78
- this.container.classList.remove('focused');
79
- this.focused = false;
80
- }
81
-
82
- private formatTime(seconds: number): string {
83
- if (isNaN(seconds)) return '0:00';
84
- const m = Math.floor(seconds / 60);
85
- const s = Math.floor(seconds % 60);
86
- return `${m}:${String(s).padStart(2, '0')}`;
87
- }
88
-
89
- private update() {
90
- const percent = (this.video.currentTime / this.video.duration) * 100;
91
- this.progress.style.width = percent + '%';
92
- this.thumb.style.left = percent + '%';
93
- this.timeLabel.textContent = this.formatTime(this.video.currentTime);
94
- this.durationLabel.textContent = ' / ' + this.formatTime(this.video.duration);
95
- }
96
-
97
- private updateBuffer() {
98
- if (this.video.buffered.length) {
99
- const end = this.video.buffered.end(this.video.buffered.length - 1);
100
- const percent = (end / this.video.duration) * 100;
101
- this.buffer.style.width = percent + '%';
102
- }
103
- }
104
-
105
- private seek(e: MouseEvent) {
106
- if (this.isDragging) return;
107
- const rect = this.container.getBoundingClientRect();
108
- const percent = (e.clientX - rect.left) / rect.width;
109
- this.video.currentTime = percent * this.video.duration;
110
- }
111
-
112
- private startDrag(e: MouseEvent) {
113
- e.preventDefault();
114
- this.isDragging = true;
115
- const onMove = (moveEvent: MouseEvent) => {
116
- const rect = this.container.getBoundingClientRect();
117
- let percent = (moveEvent.clientX - rect.left) / rect.width;
118
- percent = Math.max(0, Math.min(1, percent));
119
- this.progress.style.width = percent * 100 + '%';
120
- this.thumb.style.left = percent * 100 + '%';
121
- this.timeLabel.textContent = this.formatTime(percent * this.video.duration);
122
- };
123
- const onUp = (upEvent: MouseEvent) => {
124
- const rect = this.container.getBoundingClientRect();
125
- let percent = (upEvent.clientX - rect.left) / rect.width;
126
- percent = Math.max(0, Math.min(1, percent));
127
- this.video.currentTime = percent * this.video.duration;
128
- this.isDragging = false;
129
- document.removeEventListener('mousemove', onMove);
130
- document.removeEventListener('mouseup', onUp);
131
- };
132
- document.addEventListener('mousemove', onMove);
133
- document.addEventListener('mouseup', onUp);
134
- }
135
-
136
- private showTooltip(e: MouseEvent) {
137
- const rect = this.container.getBoundingClientRect();
138
- const percent = (e.clientX - rect.left) / rect.width;
139
- const time = percent * this.video.duration;
140
- this.tooltip.textContent = this.formatTime(time);
141
- this.tooltip.style.display = 'block';
142
- this.tooltip.style.left = `${percent * 100}%`;
143
- }
144
-
145
- private hideTooltip() {
146
- this.tooltip.style.display = 'none';
147
- }
148
-
149
- public mount(parent: HTMLElement) {
150
- parent.appendChild(this.container);
151
- this.update();
152
- this.updateBuffer();
153
- }
154
-
155
- public getElement() {
156
- return this.container;
157
- }
158
- }
@@ -1,8 +0,0 @@
1
- /* Center play/pause button absolutely in the middle of the player */
2
- .play-pause-btn {
3
- position: absolute !important;
4
- left: 50%;
5
- top: 50%;
6
- transform: translate(-50%, -50%);
7
- z-index: 30;
8
- }