@viewlift/player-backup 1.0.0 → 1.0.1-lighteningjs

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/.babelrc +8 -0
  2. package/.browserslistrc +2 -0
  3. package/README.md +29 -0
  4. package/babel.config.json +8 -0
  5. package/dist/index.cjs.css +2 -0
  6. package/dist/index.cjs.css.map +1 -0
  7. package/dist/index.cjs.js +1 -0
  8. package/dist/index.d.ts +45 -0
  9. package/dist/index.esm.css +2 -0
  10. package/dist/index.esm.css.map +1 -0
  11. package/dist/index.esm.js +1 -0
  12. package/dist/main-player/player-ui.d.ts +19 -0
  13. package/dist/player-components/player-buttons/playerButtons.d.ts +38 -0
  14. package/dist/player-components/player-seekbar/playerSeekbar.d.ts +25 -0
  15. package/dist/player-ui/player-ui.d.ts +18 -0
  16. package/dist/playerInitializer.d.ts +13 -0
  17. package/package.json +31 -15
  18. package/rollup.config.mjs +37 -0
  19. package/src/index.ts +242 -0
  20. package/src/main-player/player-ui.ts +65 -0
  21. package/src/player-components/player-buttons/playerButtons.css +0 -0
  22. package/src/player-components/player-buttons/playerButtons.ts +127 -0
  23. package/src/player-components/player-seekbar/playerSeekbar.css +75 -0
  24. package/src/player-components/player-seekbar/playerSeekbar.ts +158 -0
  25. package/src/player-ui/player-ui.css +8 -0
  26. package/src/player-ui/player-ui.ts +79 -0
  27. package/src/playerInitializer.ts +54 -0
  28. package/tsconfig.json +14 -0
  29. package/cjs/autoNextVideo-823821e8.js +0 -1
  30. package/cjs/bundle.css +0 -657
  31. package/cjs/caption-settings-67212c17.js +0 -1
  32. package/cjs/caption-settings-d42c056e.js +0 -1
  33. package/cjs/debugSelection-cbfdabb7.js +0 -1
  34. package/cjs/dvr_skin-f7f770ae.js +0 -1
  35. package/cjs/error-26d5a3c3.js +0 -1
  36. package/cjs/features-164c3cc6.js +0 -1
  37. package/cjs/generic-8b99b151.js +0 -1
  38. package/cjs/icons-7afb9c13.js +0 -1
  39. package/cjs/index-3dfe6459.js +0 -1
  40. package/cjs/index.js +0 -1
  41. package/cjs/keyboardShortcuts-cfd998c2.js +0 -1
  42. package/cjs/loader-393b838a.js +0 -1
  43. package/cjs/multicam-21c9a90b.js +0 -1
  44. package/cjs/multicam-a41fe4a6.js +0 -1
  45. package/cjs/multicamv2-76d8530b.js +0 -1
  46. package/cjs/multicamv2-c148fa24.js +0 -1
  47. package/cjs/mwebPotrait-3c11d7cf.js +0 -1
  48. package/cjs/mwebPotraitTray-b9d5d45a.js +0 -1
  49. package/cjs/nextEpisodeButton-300afadc.js +0 -1
  50. package/cjs/nextEpisodeButton-b6e6586f.js +0 -1
  51. package/cjs/nondvr_skin-ef2cb3b2.js +0 -1
  52. package/cjs/playlist-925f808b.js +0 -1
  53. package/cjs/playlist-df87ca90.js +0 -1
  54. package/cjs/renditions-bc841552.js +0 -1
  55. package/cjs/resolution-6a9adf54.js +0 -1
  56. package/cjs/series-82ec79d3.js +0 -1
  57. package/cjs/series-9c237975.js +0 -1
  58. package/cjs/series-v2-24855152.js +0 -1
  59. package/cjs/series-v2-70137d8c.js +0 -1
  60. package/cjs/settings-icon-a63f46fc.js +0 -1
  61. package/cjs/settings-icon-e6a8feee.js +0 -1
  62. package/cjs/skin-225252ff.js +0 -1
  63. package/cjs/skin-39ab30e2.js +0 -1
  64. package/cjs/skin-83edff36.js +0 -1
  65. package/cjs/skin-84083352.js +0 -1
  66. package/cjs/skin-93a3e5ae.js +0 -1
  67. package/cjs/skin-983a2ea7.js +0 -1
  68. package/cjs/skin-afbec6d7.js +0 -1
  69. package/cjs/skin-ec49b877.js +0 -1
  70. package/cjs/skin-f225a42e.js +0 -1
  71. package/cjs/skin-fb394911.js +0 -1
  72. package/cjs/util-fa21734a.js +0 -1
  73. package/cjs/videoSelector-01a9ef43.js +0 -1
  74. package/cjs/videoSelector-fcdf11eb.js +0 -1
  75. package/esm/autoNextVideo-1672740e.js +0 -1
  76. package/esm/bundle.css +0 -657
  77. package/esm/caption-settings-2e47813b.js +0 -1
  78. package/esm/caption-settings-3eacf3a0.js +0 -1
  79. package/esm/debugSelection-2601b8b3.js +0 -1
  80. package/esm/dvr_skin-9cc1b144.js +0 -1
  81. package/esm/error-2395864b.js +0 -1
  82. package/esm/features-f86404ce.js +0 -1
  83. package/esm/generic-7c43020f.js +0 -1
  84. package/esm/icons-c18c70b7.js +0 -1
  85. package/esm/index-9f022e82.js +0 -1
  86. package/esm/index.js +0 -1
  87. package/esm/keyboardShortcuts-c06f3c7b.js +0 -1
  88. package/esm/loader-580278fe.js +0 -1
  89. package/esm/multicam-94da3759.js +0 -1
  90. package/esm/multicam-de741974.js +0 -1
  91. package/esm/multicamv2-0105c40f.js +0 -1
  92. package/esm/multicamv2-d006cffa.js +0 -1
  93. package/esm/mwebPotrait-3c1805d8.js +0 -1
  94. package/esm/mwebPotraitTray-5ab39989.js +0 -1
  95. package/esm/nextEpisodeButton-2ea69b56.js +0 -1
  96. package/esm/nextEpisodeButton-a1556cdf.js +0 -1
  97. package/esm/nondvr_skin-64be70e5.js +0 -1
  98. package/esm/playlist-032fbbf3.js +0 -1
  99. package/esm/playlist-7ecf432a.js +0 -1
  100. package/esm/renditions-97276745.js +0 -1
  101. package/esm/resolution-38cd4f5c.js +0 -1
  102. package/esm/series-041152aa.js +0 -1
  103. package/esm/series-977edced.js +0 -1
  104. package/esm/series-v2-0727ee8c.js +0 -1
  105. package/esm/series-v2-516c91c2.js +0 -1
  106. package/esm/settings-icon-0125bffd.js +0 -1
  107. package/esm/settings-icon-2b713fbf.js +0 -1
  108. package/esm/skin-2c638dc1.js +0 -1
  109. package/esm/skin-42c2f81d.js +0 -1
  110. package/esm/skin-5e2fa9d4.js +0 -1
  111. package/esm/skin-6093bfb3.js +0 -1
  112. package/esm/skin-665ec17e.js +0 -1
  113. package/esm/skin-93e320fc.js +0 -1
  114. package/esm/skin-b939c35e.js +0 -1
  115. package/esm/skin-cd537931.js +0 -1
  116. package/esm/skin-d5510f70.js +0 -1
  117. package/esm/skin-d6b00653.js +0 -1
  118. package/esm/util-d9f5a7be.js +0 -1
  119. package/esm/videoSelector-8889c58e.js +0 -1
  120. package/esm/videoSelector-8e258d45.js +0 -1
@@ -0,0 +1,65 @@
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;
@@ -0,0 +1,127 @@
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
+ }
@@ -0,0 +1,75 @@
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
+ }
@@ -0,0 +1,158 @@
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
+ }
@@ -0,0 +1,8 @@
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
+ }
@@ -0,0 +1,79 @@
1
+ import { Button } from '../player-components/player-buttons/playerButtons';
2
+ import { PlayerSeekbar } from '../player-components/player-seekbar/playerSeekbar';
3
+ import './player-ui.css';
4
+
5
+
6
+ class VideoPlayerUI {
7
+ private player: { video: HTMLVideoElement; play: () => void; pause: () => void };
8
+ private container: HTMLDivElement;
9
+ private playPauseBtn?: Button;
10
+ private seekbar?: PlayerSeekbar;
11
+
12
+ constructor({ player }: { player: { video: HTMLVideoElement; play: () => void; pause: () => void } }) {
13
+ this.player = player;
14
+ this.container = document.createElement('div');
15
+ this.container.className = 'video-player-ui';
16
+ this.createControls();
17
+ }
18
+
19
+ private createControls() {
20
+
21
+ this.playPauseBtn = new Button({
22
+ imgSrc: '',
23
+ imgAltText: 'Play/Pause',
24
+ buttonText: 'Play',
25
+ width: 80,
26
+ height: 40,
27
+ klassname: 'play-pause-btn',
28
+ focused: false,
29
+ disabled: false,
30
+ handleClick: () => {
31
+ if (this.player.video.paused) {
32
+ this.player.play();
33
+ if (this.playPauseBtn && this.playPauseBtn.buttonElement) {
34
+ this.playPauseBtn.buttonElement.textContent = 'Pause';
35
+ }
36
+ } else {
37
+ this.player.pause();
38
+ if (this.playPauseBtn && this.playPauseBtn.buttonElement) {
39
+ this.playPauseBtn.buttonElement.textContent = 'Play';
40
+ }
41
+ }
42
+ }
43
+ });
44
+ this.playPauseBtn.mount(this.container);
45
+ // Attach the video element
46
+ this.container.appendChild(this.player.video);
47
+ // Add the seekbar below the video
48
+ this.seekbar = new PlayerSeekbar(this.player.video);
49
+ this.seekbar.mount(this.container);
50
+
51
+ if (this.playPauseBtn?.buttonElement && this.seekbar) {
52
+ // Down arrow from play button focuses seekbar
53
+ this.playPauseBtn.buttonElement.addEventListener('keydown', (e: KeyboardEvent) => {
54
+ if (e.key === 'ArrowDown') {
55
+ e.preventDefault();
56
+ this.seekbar?.focus();
57
+ }
58
+ });
59
+
60
+ // Up arrow from seekbar focuses play button
61
+ this.seekbar?.getElement()?.addEventListener('keydown', (e: KeyboardEvent) => {
62
+ if (e.key === 'ArrowUp') {
63
+ e.preventDefault();
64
+ this.playPauseBtn?.focus();
65
+ }
66
+ });
67
+ }
68
+ }
69
+
70
+ attachTo(parent: HTMLElement) {
71
+ parent.appendChild(this.container);
72
+ }
73
+
74
+ public getElement() {
75
+ return this.container;
76
+ }
77
+ }
78
+
79
+ export default VideoPlayerUI;
@@ -0,0 +1,54 @@
1
+ import VideoPlayerUI from './player-ui/player-ui';
2
+
3
+ // Declare Hls type for CDN usage
4
+ declare global {
5
+ interface Window {
6
+ Hls: any;
7
+ }
8
+ }
9
+
10
+ export interface PlayerInstance {
11
+ video: HTMLVideoElement;
12
+ play: () => void;
13
+ pause: () => void;
14
+ ui: VideoPlayerUI;
15
+ }
16
+
17
+ export function createHlsPlayer(
18
+ mountPoint: HTMLElement,
19
+ streamUrl: string
20
+ ): PlayerInstance {
21
+ const video = document.createElement('video');
22
+ video.controls = false;
23
+ video.style.width = '100%';
24
+ video.style.height = '100%';
25
+
26
+ let hls: any = null;
27
+
28
+ if (window.Hls && window.Hls.isSupported()) {
29
+ hls = new window.Hls();
30
+ hls.loadSource(streamUrl);
31
+ hls.attachMedia(video);
32
+ hls.on(window.Hls.Events.MANIFEST_PARSED, function () {
33
+ video.play();
34
+ });
35
+ } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
36
+ video.src = streamUrl;
37
+ video.addEventListener('loadedmetadata', function () {
38
+ video.play();
39
+ });
40
+ } else {
41
+ throw new Error('HLS is not supported in this browser.');
42
+ }
43
+
44
+ const player = {
45
+ video,
46
+ play: () => video.play(),
47
+ pause: () => video.pause(),
48
+ };
49
+
50
+ const ui = new VideoPlayerUI({ player });
51
+ ui.attachTo(mountPoint);
52
+
53
+ return { ...player, ui };
54
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES5",
4
+ "module": "ESNext",
5
+ "declaration": true,
6
+ "outDir": "dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "moduleResolution": "node",
10
+ "skipLibCheck": true,
11
+ "lib": ["dom", "es2017"]
12
+ },
13
+ "include": ["src/**/*"]
14
+ }
@@ -1 +0,0 @@
1
- "use strict";var e,t=require("./generic-8b99b151.js"),i=require("./util-fa21734a.js"),o=require("./skin-84083352.js"),n=0,d=0,u=function e(i,n,u,s,c){d=i,i>u.length-1||t.load(n,u[i].id,s).then((function(e){o.highlightVideo(d),d++,t.setVideoIdx(d),r(e)})).catch((function(i){r(i),setTimeout((function(){o.highlightVideo(d),d++,t.setVideoIdx(d),e(d,n,u,s)}),5e3)}))},r=function(t){e&&e(t)};exports.autoPlayNext=function(s){clearTimeout(n);var c=null==s?void 0:s.content;e=null==s?void 0:s.cb;var h=s.playerId,l=sessionStorage.getItem("token");i.onVideoEnded(h,(function(){var e=t.getPlayer(h);0!==(null==e?void 0:e.currentTime())&&((d=t.getVideoIdx())>c.length-1||t.load(h,c[d].id,l).then((function(e){o.highlightVideo(d),t.setCurrentVideo(e.response),d++,t.setVideoIdx(d),r(e)})).catch((function(e){r(e),o.highlightVideo(d),d++,t.setVideoIdx(d),n=setTimeout((function(){t.isPaused(h)&&u(d,h,c,l)}),5e3)})))}))},exports.switchToNext=u;