@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
package/.babelrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "presets": [
3
+ ["@babel/preset-env", {
4
+ "targets": { "ie": "11" },
5
+ "modules": false
6
+ }]
7
+ ]
8
+ }
@@ -0,0 +1,2 @@
1
+ IE 11
2
+ last 2 versions
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @viewlift/ligheningjs-video-player
2
+
3
+ A LightningJS video player component for Smart TV app development.
4
+
5
+ ## Features
6
+ - Video player component for LightningJS
7
+ - TypeScript support
8
+ - Easy to use in LightningJS-based projects
9
+
10
+ ## Installation
11
+ ```bash
12
+ npm install @viewlift/ligheningjs-video-player
13
+ ```
14
+
15
+ ## Usage
16
+ ```ts
17
+ import { LightningVideoPlayer } from '@viewlift/ligheningjs-video-player';
18
+
19
+ const player = new LightningVideoPlayer();
20
+ player.play();
21
+ ```
22
+
23
+ ## Build
24
+ ```bash
25
+ npm run build
26
+ ```
27
+
28
+ ## License
29
+ ISC
@@ -0,0 +1,8 @@
1
+ {
2
+ "presets": [
3
+ ["@babel/preset-env", {
4
+ "targets": { "ie": "11" },
5
+ "modules": false
6
+ }]
7
+ ]
8
+ }
@@ -0,0 +1,2 @@
1
+ .seekbar-container{background:#444;border-radius:6px;bottom:50px;cursor:pointer;height:12px;left:50%;margin:0;position:absolute;transform:translateX(-50%);user-select:none;width:90%;z-index:20}.seekbar-buffer{background:#888;z-index:1}.seekbar-buffer,.seekbar-progress{border-radius:6px;height:100%;position:absolute;width:0}.seekbar-progress{background:#4169e1;transition:width .1s linear;z-index:2}.seekbar-thumb{background:#fff;border:2px solid #4169e1;border-radius:50%;box-shadow:0 2px 8px rgba(0,0,0,.2);cursor:pointer;height:16px;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s;width:16px;z-index:3}.seekbar-thumb:hover{background:#ffeaea}.seekbar-tooltip{background:#222;border-radius:4px;bottom:24px;color:#fff;font-size:12px;left:0;padding:2px 8px;pointer-events:none;position:absolute;transform:translateX(-50%);white-space:nowrap;z-index:10}.seekbar-duration-label,.seekbar-time-label{color:#eee;font-size:13px;margin-right:4px;position:relative;top:18px;z-index:5}.play-pause-btn{left:50%;position:absolute!important;top:50%;transform:translate(-50%,-50%);z-index:30}
2
+ /*# sourceMappingURL=index.cjs.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["playerSeekbar.css","player-ui.css"],"names":[],"mappings":"AAAA,mBAME,eAAgB,CAGhB,iBAAkB,CALlB,WAAY,CAIZ,cAAe,CAHf,WAAY,CAHZ,QAAS,CAQT,QAAS,CATT,iBAAkB,CAElB,0BAA2B,CAQ3B,gBAAiB,CAJjB,SAAU,CAKV,UACF,CAEA,gBAGE,eAAgB,CAGhB,SACF,CAEA,kCAJE,iBAAkB,CAHlB,WAAY,CADZ,iBAAkB,CAGlB,OAaF,CARA,kBAGE,kBAAmB,CAInB,2BAA6B,CAD7B,SAEF,CAEA,eAME,eAAgB,CAEhB,wBAAyB,CADzB,iBAAkB,CAGlB,mCAAqC,CACrC,cAAe,CANf,WAAY,CAJZ,iBAAkB,CAClB,OAAQ,CACR,8BAAgC,CAShC,yBAA2B,CAR3B,UAAW,CAKX,SAIF,CACA,qBACE,kBACF,CAEA,iBAKE,eAAgB,CAGhB,iBAAkB,CANlB,WAAY,CAIZ,UAAW,CAGX,cAAe,CANf,MAAO,CAIP,eAAgB,CAIhB,mBAAoB,CAVpB,iBAAkB,CAGlB,0BAA2B,CAM3B,kBAAmB,CAEnB,UACF,CAEA,4CAIE,UAAW,CADX,cAAe,CAEf,gBAAiB,CAJjB,iBAAkB,CAClB,QAAS,CAIT,SACF,CCzEA,gBAEE,QAAS,CADT,2BAA6B,CAE7B,OAAQ,CACR,8BAAgC,CAChC,UACF","file":"index.cjs.css","sourcesContent":[".seekbar-container {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n bottom: 50px;\n height: 12px;\n background: #444;\n width: 90%;\n cursor: pointer;\n border-radius: 6px;\n margin: 0;\n user-select: none;\n z-index: 20;\n}\n\n.seekbar-buffer {\n position: absolute;\n height: 100%;\n background: #888;\n width: 0;\n border-radius: 6px;\n z-index: 1;\n}\n\n.seekbar-progress {\n position: absolute;\n height: 100%;\n background: #4169E1;\n width: 0;\n border-radius: 6px;\n z-index: 2;\n transition: width 0.1s linear;\n}\n\n.seekbar-thumb {\n position: absolute;\n top: 50%;\n transform: translate(-50%, -50%);\n width: 16px;\n height: 16px;\n background: #fff;\n border-radius: 50%;\n border: 2px solid #4169E1;\n z-index: 3;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n cursor: pointer;\n transition: background 0.2s;\n}\n.seekbar-thumb:hover {\n background: #ffeaea;\n}\n\n.seekbar-tooltip {\n position: absolute;\n bottom: 24px;\n left: 0;\n transform: translateX(-50%);\n background: #222;\n color: #fff;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 12px;\n white-space: nowrap;\n pointer-events: none;\n z-index: 10;\n}\n\n.seekbar-time-label, .seekbar-duration-label {\n position: relative;\n top: 18px;\n font-size: 13px;\n color: #eee;\n margin-right: 4px;\n z-index: 5;\n}\n","/* Center play/pause button absolutely in the middle of the player */\n.play-pause-btn {\n position: absolute !important;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n z-index: 30;\n} "]}
@@ -0,0 +1 @@
1
+ "use strict";var t=function(){return t=Object.assign||function(t){for(var e,n=1,i=arguments.length;n<i;n++)for(var s in e=arguments[n])Object.prototype.hasOwnProperty.call(e,s)&&(t[s]=e[s]);return t},t.apply(this,arguments)};"function"==typeof SuppressedError&&SuppressedError;var e=function(){function t(t){if(this.imgSrc=t.imgSrc||"",this.imgAltText=t.imgAltText||"",this.buttonText=t.buttonText||"",this.klassname=t.klassname||"default-button",this.width=t.width||100,this.height=t.height||50,this.focused=t.focused||!1,this.disabled=t.disabled||!1,this.handleClick=t.handleClick||function(){},"function"!=typeof t.handleClick)throw new Error("Button Class: handleClick must be a function");this.disabled&&(this.klassname+=" disabled"),this.focused&&(this.klassname+=" focused"),this.handleClick=this.handleClick.bind(this)}return t.prototype.mount=function(t){var e=this,n=document.createElement("button");if(n.className=this.klassname,n.style.width="".concat(this.width,"px"),n.style.height="".concat(this.height,"px"),n.disabled=this.disabled,n.tabIndex=0,this.imgSrc){var i=document.createElement("img");i.src=this.imgSrc,i.alt=this.imgAltText,n.appendChild(i)}else{var s=document.createElement("span");s.textContent=this.buttonText,n.appendChild(s)}n.addEventListener("click",this.handleClick),n.addEventListener("focus",function(){e.focused=!0,n.classList.add("focused")}),n.addEventListener("blur",function(){e.focused=!1,n.classList.remove("focused")}),this.buttonElement=n,t.appendChild(n)},t.prototype.unmount=function(){this.buttonElement&&(this.buttonElement.removeEventListener("click",this.handleClick),this.buttonElement.removeEventListener("focus",this.handleClick),this.buttonElement.removeEventListener("blur",this.handleClick),this.buttonElement=void 0)},t.prototype.focus=function(){this.buttonElement&&(this.buttonElement.focus(),this.buttonElement.classList.add("focused"),this.focused=!0)},t.prototype.blur=function(){this.buttonElement&&(this.buttonElement.blur(),this.buttonElement.classList.remove("focused"),this.focused=!1)},t}(),n=function(){function t(t){var e=this;this.isDragging=!1,this.focused=!1,this.video=t,this.container=document.createElement("div"),this.container.className="seekbar-container",this.container.tabIndex=0,this.buffer=document.createElement("div"),this.buffer.className="seekbar-buffer",this.progress=document.createElement("div"),this.progress.className="seekbar-progress",this.thumb=document.createElement("div"),this.thumb.className="seekbar-thumb",this.tooltip=document.createElement("div"),this.tooltip.className="seekbar-tooltip",this.tooltip.style.display="none",this.timeLabel=document.createElement("span"),this.timeLabel.className="seekbar-time-label",this.durationLabel=document.createElement("span"),this.durationLabel.className="seekbar-duration-label",this.container.appendChild(this.buffer),this.container.appendChild(this.progress),this.container.appendChild(this.thumb),this.container.appendChild(this.tooltip),this.container.appendChild(this.timeLabel),this.container.appendChild(this.durationLabel),this.video.addEventListener("timeupdate",function(){return e.update()}),this.video.addEventListener("progress",function(){return e.updateBuffer()}),this.video.addEventListener("durationchange",function(){return e.update()}),this.container.addEventListener("click",function(t){return e.seek(t)}),this.thumb.addEventListener("mousedown",function(t){return e.startDrag(t)}),this.container.addEventListener("mousemove",function(t){return e.showTooltip(t)}),this.container.addEventListener("mouseleave",function(){return e.hideTooltip()}),this.container.addEventListener("focus",function(){e.focused=!0,e.container.classList.add("focused")}),this.container.addEventListener("blur",function(){e.focused=!1,e.container.classList.remove("focused")})}return t.prototype.focus=function(){this.container.focus(),this.container.classList.add("focused"),this.focused=!0},t.prototype.blur=function(){this.container.blur(),this.container.classList.remove("focused"),this.focused=!1},t.prototype.formatTime=function(t){if(isNaN(t))return"0:00";var e=Math.floor(t/60),n=Math.floor(t%60);return"".concat(e,":").concat(String(n).padStart(2,"0"))},t.prototype.update=function(){var t=this.video.currentTime/this.video.duration*100;this.progress.style.width=t+"%",this.thumb.style.left=t+"%",this.timeLabel.textContent=this.formatTime(this.video.currentTime),this.durationLabel.textContent=" / "+this.formatTime(this.video.duration)},t.prototype.updateBuffer=function(){if(this.video.buffered.length){var t=this.video.buffered.end(this.video.buffered.length-1)/this.video.duration*100;this.buffer.style.width=t+"%"}},t.prototype.seek=function(t){if(!this.isDragging){var e=this.container.getBoundingClientRect(),n=(t.clientX-e.left)/e.width;this.video.currentTime=n*this.video.duration}},t.prototype.startDrag=function(t){var e=this;t.preventDefault(),this.isDragging=!0;var n=function(t){var n=e.container.getBoundingClientRect(),i=(t.clientX-n.left)/n.width;i=Math.max(0,Math.min(1,i)),e.progress.style.width=100*i+"%",e.thumb.style.left=100*i+"%",e.timeLabel.textContent=e.formatTime(i*e.video.duration)},i=function(t){var s=e.container.getBoundingClientRect(),o=(t.clientX-s.left)/s.width;o=Math.max(0,Math.min(1,o)),e.video.currentTime=o*e.video.duration,e.isDragging=!1,document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",i)};document.addEventListener("mousemove",n),document.addEventListener("mouseup",i)},t.prototype.showTooltip=function(t){var e=this.container.getBoundingClientRect(),n=(t.clientX-e.left)/e.width,i=n*this.video.duration;this.tooltip.textContent=this.formatTime(i),this.tooltip.style.display="block",this.tooltip.style.left="".concat(100*n,"%")},t.prototype.hideTooltip=function(){this.tooltip.style.display="none"},t.prototype.mount=function(t){t.appendChild(this.container),this.update(),this.updateBuffer()},t.prototype.getElement=function(){return this.container},t}(),i=function(){function t(t){var e=t.player;this.player=e,this.container=document.createElement("div"),this.container.className="video-player-ui",this.createControls()}return t.prototype.createControls=function(){var t,i,s,o=this;this.playPauseBtn=new e({imgSrc:"",imgAltText:"Play/Pause",buttonText:"Play",width:80,height:40,klassname:"play-pause-btn",focused:!1,disabled:!1,handleClick:function(){o.player.video.paused?(o.player.play(),o.playPauseBtn&&o.playPauseBtn.buttonElement&&(o.playPauseBtn.buttonElement.textContent="Pause")):(o.player.pause(),o.playPauseBtn&&o.playPauseBtn.buttonElement&&(o.playPauseBtn.buttonElement.textContent="Play"))}}),this.playPauseBtn.mount(this.container),this.container.appendChild(this.player.video),this.seekbar=new n(this.player.video),this.seekbar.mount(this.container),(null===(t=this.playPauseBtn)||void 0===t?void 0:t.buttonElement)&&this.seekbar&&(this.playPauseBtn.buttonElement.addEventListener("keydown",function(t){var e;"ArrowDown"===t.key&&(t.preventDefault(),null===(e=o.seekbar)||void 0===e||e.focus())}),null===(s=null===(i=this.seekbar)||void 0===i?void 0:i.getElement())||void 0===s||s.addEventListener("keydown",function(t){var e;"ArrowUp"===t.key&&(t.preventDefault(),null===(e=o.playPauseBtn)||void 0===e||e.focus())}))},t.prototype.attachTo=function(t){t.appendChild(this.container)},t.prototype.getElement=function(){return this.container},t}();exports.createHlsPlayer=function(e,n){var s=document.createElement("video");s.controls=!1,s.style.width="100%",s.style.height="100%";var o=null;if(window.Hls&&window.Hls.isSupported())(o=new window.Hls).loadSource(n),o.attachMedia(s),o.on(window.Hls.Events.MANIFEST_PARSED,function(){s.play()});else{if(!s.canPlayType("application/vnd.apple.mpegurl"))throw new Error("HLS is not supported in this browser.");s.src=n,s.addEventListener("loadedmetadata",function(){s.play()})}var a={video:s,play:function(){return s.play()},pause:function(){return s.pause()}},r=new i({player:a});return r.attachTo(e),t(t({},a),{ui:r})};
@@ -0,0 +1,45 @@
1
+ export interface VideoPlayerOptions {
2
+ onPlaybackSuccess?: () => void;
3
+ onPlaybackEnded?: () => void;
4
+ onPlaybackError?: (error?: any) => void;
5
+ src: string;
6
+ autoplay?: boolean;
7
+ controls?: boolean;
8
+ width?: number;
9
+ height?: number;
10
+ }
11
+ type EventHandler = (...args: any[]) => void;
12
+ type VideoPlayerEvent = 'play' | 'pause' | 'ended' | 'timeupdate' | 'error' | 'loadedmetadata' | 'seeking' | 'seeked' | 'volumechange';
13
+ export declare class LightningVideoPlayer {
14
+ /**
15
+ * Reinitialize the current videoElement with a new set of options and handle autoplay.
16
+ */
17
+ reinitialize(options: VideoPlayerOptions): void;
18
+ private videoElement;
19
+ private options;
20
+ private listeners;
21
+ private boundEmitters;
22
+ private boundHandlerMap;
23
+ constructor(options: VideoPlayerOptions);
24
+ attachTo(container: HTMLElement): void;
25
+ play(): void;
26
+ pause(): void;
27
+ stop(): void;
28
+ destroy(): void;
29
+ get element(): HTMLVideoElement;
30
+ onPlay(e: Event): void;
31
+ onPause(e: Event): void;
32
+ onEnded(e: Event): void;
33
+ onTimeupdate(e: Event): void;
34
+ onError(e: Event): void;
35
+ onLoadedmetadata(e: Event): void;
36
+ onSeeking(e: Event): void;
37
+ onSeeked(e: Event): void;
38
+ onVolumechange(e: Event): void;
39
+ on(event: VideoPlayerEvent, handler: EventHandler): void;
40
+ off(event: VideoPlayerEvent, handler: EventHandler): void;
41
+ once(event: VideoPlayerEvent, handler: EventHandler): void;
42
+ private emitEvent;
43
+ private removeAllListeners;
44
+ }
45
+ export {};
@@ -0,0 +1,2 @@
1
+ .seekbar-container{background:#444;border-radius:6px;bottom:50px;cursor:pointer;height:12px;left:50%;margin:0;position:absolute;transform:translateX(-50%);user-select:none;width:90%;z-index:20}.seekbar-buffer{background:#888;z-index:1}.seekbar-buffer,.seekbar-progress{border-radius:6px;height:100%;position:absolute;width:0}.seekbar-progress{background:#4169e1;transition:width .1s linear;z-index:2}.seekbar-thumb{background:#fff;border:2px solid #4169e1;border-radius:50%;box-shadow:0 2px 8px rgba(0,0,0,.2);cursor:pointer;height:16px;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s;width:16px;z-index:3}.seekbar-thumb:hover{background:#ffeaea}.seekbar-tooltip{background:#222;border-radius:4px;bottom:24px;color:#fff;font-size:12px;left:0;padding:2px 8px;pointer-events:none;position:absolute;transform:translateX(-50%);white-space:nowrap;z-index:10}.seekbar-duration-label,.seekbar-time-label{color:#eee;font-size:13px;margin-right:4px;position:relative;top:18px;z-index:5}.play-pause-btn{left:50%;position:absolute!important;top:50%;transform:translate(-50%,-50%);z-index:30}
2
+ /*# sourceMappingURL=index.esm.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["playerSeekbar.css","player-ui.css"],"names":[],"mappings":"AAAA,mBAME,eAAgB,CAGhB,iBAAkB,CALlB,WAAY,CAIZ,cAAe,CAHf,WAAY,CAHZ,QAAS,CAQT,QAAS,CATT,iBAAkB,CAElB,0BAA2B,CAQ3B,gBAAiB,CAJjB,SAAU,CAKV,UACF,CAEA,gBAGE,eAAgB,CAGhB,SACF,CAEA,kCAJE,iBAAkB,CAHlB,WAAY,CADZ,iBAAkB,CAGlB,OAaF,CARA,kBAGE,kBAAmB,CAInB,2BAA6B,CAD7B,SAEF,CAEA,eAME,eAAgB,CAEhB,wBAAyB,CADzB,iBAAkB,CAGlB,mCAAqC,CACrC,cAAe,CANf,WAAY,CAJZ,iBAAkB,CAClB,OAAQ,CACR,8BAAgC,CAShC,yBAA2B,CAR3B,UAAW,CAKX,SAIF,CACA,qBACE,kBACF,CAEA,iBAKE,eAAgB,CAGhB,iBAAkB,CANlB,WAAY,CAIZ,UAAW,CAGX,cAAe,CANf,MAAO,CAIP,eAAgB,CAIhB,mBAAoB,CAVpB,iBAAkB,CAGlB,0BAA2B,CAM3B,kBAAmB,CAEnB,UACF,CAEA,4CAIE,UAAW,CADX,cAAe,CAEf,gBAAiB,CAJjB,iBAAkB,CAClB,QAAS,CAIT,SACF,CCzEA,gBAEE,QAAS,CADT,2BAA6B,CAE7B,OAAQ,CACR,8BAAgC,CAChC,UACF","file":"index.esm.css","sourcesContent":[".seekbar-container {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n bottom: 50px;\n height: 12px;\n background: #444;\n width: 90%;\n cursor: pointer;\n border-radius: 6px;\n margin: 0;\n user-select: none;\n z-index: 20;\n}\n\n.seekbar-buffer {\n position: absolute;\n height: 100%;\n background: #888;\n width: 0;\n border-radius: 6px;\n z-index: 1;\n}\n\n.seekbar-progress {\n position: absolute;\n height: 100%;\n background: #4169E1;\n width: 0;\n border-radius: 6px;\n z-index: 2;\n transition: width 0.1s linear;\n}\n\n.seekbar-thumb {\n position: absolute;\n top: 50%;\n transform: translate(-50%, -50%);\n width: 16px;\n height: 16px;\n background: #fff;\n border-radius: 50%;\n border: 2px solid #4169E1;\n z-index: 3;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n cursor: pointer;\n transition: background 0.2s;\n}\n.seekbar-thumb:hover {\n background: #ffeaea;\n}\n\n.seekbar-tooltip {\n position: absolute;\n bottom: 24px;\n left: 0;\n transform: translateX(-50%);\n background: #222;\n color: #fff;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 12px;\n white-space: nowrap;\n pointer-events: none;\n z-index: 10;\n}\n\n.seekbar-time-label, .seekbar-duration-label {\n position: relative;\n top: 18px;\n font-size: 13px;\n color: #eee;\n margin-right: 4px;\n z-index: 5;\n}\n","/* Center play/pause button absolutely in the middle of the player */\n.play-pause-btn {\n position: absolute !important;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n z-index: 30;\n} "]}
@@ -0,0 +1 @@
1
+ var t=function(){return t=Object.assign||function(t){for(var e,n=1,i=arguments.length;n<i;n++)for(var o in e=arguments[n])Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t},t.apply(this,arguments)};"function"==typeof SuppressedError&&SuppressedError;var e=function(){function t(t){if(this.imgSrc=t.imgSrc||"",this.imgAltText=t.imgAltText||"",this.buttonText=t.buttonText||"",this.klassname=t.klassname||"default-button",this.width=t.width||100,this.height=t.height||50,this.focused=t.focused||!1,this.disabled=t.disabled||!1,this.handleClick=t.handleClick||function(){},"function"!=typeof t.handleClick)throw new Error("Button Class: handleClick must be a function");this.disabled&&(this.klassname+=" disabled"),this.focused&&(this.klassname+=" focused"),this.handleClick=this.handleClick.bind(this)}return t.prototype.mount=function(t){var e=this,n=document.createElement("button");if(n.className=this.klassname,n.style.width="".concat(this.width,"px"),n.style.height="".concat(this.height,"px"),n.disabled=this.disabled,n.tabIndex=0,this.imgSrc){var i=document.createElement("img");i.src=this.imgSrc,i.alt=this.imgAltText,n.appendChild(i)}else{var o=document.createElement("span");o.textContent=this.buttonText,n.appendChild(o)}n.addEventListener("click",this.handleClick),n.addEventListener("focus",function(){e.focused=!0,n.classList.add("focused")}),n.addEventListener("blur",function(){e.focused=!1,n.classList.remove("focused")}),this.buttonElement=n,t.appendChild(n)},t.prototype.unmount=function(){this.buttonElement&&(this.buttonElement.removeEventListener("click",this.handleClick),this.buttonElement.removeEventListener("focus",this.handleClick),this.buttonElement.removeEventListener("blur",this.handleClick),this.buttonElement=void 0)},t.prototype.focus=function(){this.buttonElement&&(this.buttonElement.focus(),this.buttonElement.classList.add("focused"),this.focused=!0)},t.prototype.blur=function(){this.buttonElement&&(this.buttonElement.blur(),this.buttonElement.classList.remove("focused"),this.focused=!1)},t}(),n=function(){function t(t){var e=this;this.isDragging=!1,this.focused=!1,this.video=t,this.container=document.createElement("div"),this.container.className="seekbar-container",this.container.tabIndex=0,this.buffer=document.createElement("div"),this.buffer.className="seekbar-buffer",this.progress=document.createElement("div"),this.progress.className="seekbar-progress",this.thumb=document.createElement("div"),this.thumb.className="seekbar-thumb",this.tooltip=document.createElement("div"),this.tooltip.className="seekbar-tooltip",this.tooltip.style.display="none",this.timeLabel=document.createElement("span"),this.timeLabel.className="seekbar-time-label",this.durationLabel=document.createElement("span"),this.durationLabel.className="seekbar-duration-label",this.container.appendChild(this.buffer),this.container.appendChild(this.progress),this.container.appendChild(this.thumb),this.container.appendChild(this.tooltip),this.container.appendChild(this.timeLabel),this.container.appendChild(this.durationLabel),this.video.addEventListener("timeupdate",function(){return e.update()}),this.video.addEventListener("progress",function(){return e.updateBuffer()}),this.video.addEventListener("durationchange",function(){return e.update()}),this.container.addEventListener("click",function(t){return e.seek(t)}),this.thumb.addEventListener("mousedown",function(t){return e.startDrag(t)}),this.container.addEventListener("mousemove",function(t){return e.showTooltip(t)}),this.container.addEventListener("mouseleave",function(){return e.hideTooltip()}),this.container.addEventListener("focus",function(){e.focused=!0,e.container.classList.add("focused")}),this.container.addEventListener("blur",function(){e.focused=!1,e.container.classList.remove("focused")})}return t.prototype.focus=function(){this.container.focus(),this.container.classList.add("focused"),this.focused=!0},t.prototype.blur=function(){this.container.blur(),this.container.classList.remove("focused"),this.focused=!1},t.prototype.formatTime=function(t){if(isNaN(t))return"0:00";var e=Math.floor(t/60),n=Math.floor(t%60);return"".concat(e,":").concat(String(n).padStart(2,"0"))},t.prototype.update=function(){var t=this.video.currentTime/this.video.duration*100;this.progress.style.width=t+"%",this.thumb.style.left=t+"%",this.timeLabel.textContent=this.formatTime(this.video.currentTime),this.durationLabel.textContent=" / "+this.formatTime(this.video.duration)},t.prototype.updateBuffer=function(){if(this.video.buffered.length){var t=this.video.buffered.end(this.video.buffered.length-1)/this.video.duration*100;this.buffer.style.width=t+"%"}},t.prototype.seek=function(t){if(!this.isDragging){var e=this.container.getBoundingClientRect(),n=(t.clientX-e.left)/e.width;this.video.currentTime=n*this.video.duration}},t.prototype.startDrag=function(t){var e=this;t.preventDefault(),this.isDragging=!0;var n=function(t){var n=e.container.getBoundingClientRect(),i=(t.clientX-n.left)/n.width;i=Math.max(0,Math.min(1,i)),e.progress.style.width=100*i+"%",e.thumb.style.left=100*i+"%",e.timeLabel.textContent=e.formatTime(i*e.video.duration)},i=function(t){var o=e.container.getBoundingClientRect(),s=(t.clientX-o.left)/o.width;s=Math.max(0,Math.min(1,s)),e.video.currentTime=s*e.video.duration,e.isDragging=!1,document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",i)};document.addEventListener("mousemove",n),document.addEventListener("mouseup",i)},t.prototype.showTooltip=function(t){var e=this.container.getBoundingClientRect(),n=(t.clientX-e.left)/e.width,i=n*this.video.duration;this.tooltip.textContent=this.formatTime(i),this.tooltip.style.display="block",this.tooltip.style.left="".concat(100*n,"%")},t.prototype.hideTooltip=function(){this.tooltip.style.display="none"},t.prototype.mount=function(t){t.appendChild(this.container),this.update(),this.updateBuffer()},t.prototype.getElement=function(){return this.container},t}(),i=function(){function t(t){var e=t.player;this.player=e,this.container=document.createElement("div"),this.container.className="video-player-ui",this.createControls()}return t.prototype.createControls=function(){var t,i,o,s=this;this.playPauseBtn=new e({imgSrc:"",imgAltText:"Play/Pause",buttonText:"Play",width:80,height:40,klassname:"play-pause-btn",focused:!1,disabled:!1,handleClick:function(){s.player.video.paused?(s.player.play(),s.playPauseBtn&&s.playPauseBtn.buttonElement&&(s.playPauseBtn.buttonElement.textContent="Pause")):(s.player.pause(),s.playPauseBtn&&s.playPauseBtn.buttonElement&&(s.playPauseBtn.buttonElement.textContent="Play"))}}),this.playPauseBtn.mount(this.container),this.container.appendChild(this.player.video),this.seekbar=new n(this.player.video),this.seekbar.mount(this.container),(null===(t=this.playPauseBtn)||void 0===t?void 0:t.buttonElement)&&this.seekbar&&(this.playPauseBtn.buttonElement.addEventListener("keydown",function(t){var e;"ArrowDown"===t.key&&(t.preventDefault(),null===(e=s.seekbar)||void 0===e||e.focus())}),null===(o=null===(i=this.seekbar)||void 0===i?void 0:i.getElement())||void 0===o||o.addEventListener("keydown",function(t){var e;"ArrowUp"===t.key&&(t.preventDefault(),null===(e=s.playPauseBtn)||void 0===e||e.focus())}))},t.prototype.attachTo=function(t){t.appendChild(this.container)},t.prototype.getElement=function(){return this.container},t}();function o(e,n){var o=document.createElement("video");o.controls=!1,o.style.width="100%",o.style.height="100%";var s=null;if(window.Hls&&window.Hls.isSupported())(s=new window.Hls).loadSource(n),s.attachMedia(o),s.on(window.Hls.Events.MANIFEST_PARSED,function(){o.play()});else{if(!o.canPlayType("application/vnd.apple.mpegurl"))throw new Error("HLS is not supported in this browser.");o.src=n,o.addEventListener("loadedmetadata",function(){o.play()})}var a={video:o,play:function(){return o.play()},pause:function(){return o.pause()}},r=new i({player:a});return r.attachTo(e),t(t({},a),{ui:r})}export{o as createHlsPlayer};
@@ -0,0 +1,19 @@
1
+ interface VideoPlayerUIOptions {
2
+ player: {
3
+ video: HTMLVideoElement;
4
+ play: () => void;
5
+ pause: () => void;
6
+ };
7
+ width?: string;
8
+ height?: string;
9
+ }
10
+ declare class VideoPlayerUI {
11
+ private player;
12
+ private container;
13
+ private playPauseBtn?;
14
+ private seekbar?;
15
+ constructor({ player, width, height }: VideoPlayerUIOptions);
16
+ private createControls;
17
+ attachTo(parent: HTMLElement): void;
18
+ }
19
+ export default VideoPlayerUI;
@@ -0,0 +1,38 @@
1
+ import './playerButtons.css';
2
+ type EventHandler = (event?: any) => void;
3
+ type ButtonProps = {
4
+ imgSrc: string;
5
+ imgAltText: string;
6
+ width: number;
7
+ height: number;
8
+ klassname: string;
9
+ buttonText: string;
10
+ focused: boolean;
11
+ disabled: boolean;
12
+ handleClick: EventHandler;
13
+ };
14
+ export declare class Button {
15
+ private imgSrc;
16
+ private imgAltText;
17
+ private klassname;
18
+ private width;
19
+ private height;
20
+ private focused;
21
+ private disabled;
22
+ private handleClick;
23
+ private buttonText;
24
+ buttonElement?: HTMLButtonElement;
25
+ constructor(props: ButtonProps);
26
+ /**
27
+ * Mounts the button to the specified mount point
28
+ * @param mountPoint The HTML element to which the button will be appended
29
+ */
30
+ mount(mountPoint: HTMLElement): void;
31
+ /**
32
+ * Removes the click event listener from the button
33
+ */
34
+ unmount(): void;
35
+ focus(): void;
36
+ blur(): void;
37
+ }
38
+ export {};
@@ -0,0 +1,25 @@
1
+ import './playerSeekbar.css';
2
+ export declare class PlayerSeekbar {
3
+ private container;
4
+ private progress;
5
+ private buffer;
6
+ private thumb;
7
+ private tooltip;
8
+ private video;
9
+ private timeLabel;
10
+ private durationLabel;
11
+ private isDragging;
12
+ private focused;
13
+ constructor(video: HTMLVideoElement);
14
+ focus(): void;
15
+ blur(): void;
16
+ private formatTime;
17
+ private update;
18
+ private updateBuffer;
19
+ private seek;
20
+ private startDrag;
21
+ private showTooltip;
22
+ private hideTooltip;
23
+ mount(parent: HTMLElement): void;
24
+ getElement(): HTMLDivElement;
25
+ }
@@ -0,0 +1,18 @@
1
+ import './player-ui.css';
2
+ declare class VideoPlayerUI {
3
+ private player;
4
+ private container;
5
+ private playPauseBtn?;
6
+ private seekbar?;
7
+ constructor({ player }: {
8
+ player: {
9
+ video: HTMLVideoElement;
10
+ play: () => void;
11
+ pause: () => void;
12
+ };
13
+ });
14
+ private createControls;
15
+ attachTo(parent: HTMLElement): void;
16
+ getElement(): HTMLDivElement;
17
+ }
18
+ export default VideoPlayerUI;
@@ -0,0 +1,13 @@
1
+ import VideoPlayerUI from './player-ui/player-ui';
2
+ declare global {
3
+ interface Window {
4
+ Hls: any;
5
+ }
6
+ }
7
+ export interface PlayerInstance {
8
+ video: HTMLVideoElement;
9
+ play: () => void;
10
+ pause: () => void;
11
+ ui: VideoPlayerUI;
12
+ }
13
+ export declare function createHlsPlayer(mountPoint: HTMLElement, streamUrl: string): PlayerInstance;
package/package.json CHANGED
@@ -1,17 +1,33 @@
1
1
  {
2
- "name": "@viewlift/player-backup",
3
- "version": "1.0.0",
4
- "main": "dist/bundle.cjs.js",
5
- "amdName": "_playerCore",
6
- "dependencies": {
7
- "@silvermine/videojs-airplay": "^1.2.0",
8
- "@silvermine/videojs-chromecast": "^1.4.1",
9
- "axios": "^1.2.3",
10
- "js-cookie": "^3.0.5",
11
- "video.js": "^7.20.3",
12
- "videojs-contrib-ads": "^6.9.0",
13
- "videojs-contrib-dash": "^5.1.1",
14
- "videojs-contrib-eme": "^3.11.0",
15
- "videojs-ima": "^2.1.0"
16
- }
2
+ "name": "@viewlift/player-backup",
3
+ "version": "1.0.1-lighteningjs",
4
+ "description": "A LightningJS video player component for Smart TV apps.",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "rollup -c",
10
+ "dev": "rollup -c -w",
11
+ "test": "echo \"No tests specified\" && exit 0"
12
+ },
13
+ "author": "ViewLift",
14
+ "license": "ISC",
15
+ "keywords": [
16
+ "LightningJS",
17
+ "SmartTV",
18
+ "video-player"
19
+ ],
20
+ "devDependencies": {
21
+ "@babel/core": "^7.24.4",
22
+ "@babel/preset-env": "^7.24.4",
23
+ "@rollup/plugin-babel": "^6.0.4",
24
+ "@rollup/plugin-commonjs": "^25.0.7",
25
+ "@rollup/plugin-node-resolve": "^15.0.2",
26
+ "@rollup/plugin-terser": "^0.4.4",
27
+ "@rollup/plugin-typescript": "^11.1.6",
28
+ "rollup": "^4.16.4",
29
+ "tslib": "^2.6.2",
30
+ "typescript": "^5.4.5",
31
+ "rollup-plugin-postcss": "^4.0.2"
32
+ }
17
33
  }
@@ -0,0 +1,37 @@
1
+ import resolve from '@rollup/plugin-node-resolve';
2
+ import commonjs from '@rollup/plugin-commonjs';
3
+ import terser from '@rollup/plugin-terser';
4
+ import typescript from '@rollup/plugin-typescript';
5
+ import babel from '@rollup/plugin-babel';
6
+ import postcss from 'rollup-plugin-postcss';
7
+
8
+ export default {
9
+ input: 'src/playerInitializer.ts',
10
+ output: [
11
+ {
12
+ file: 'dist/index.cjs.js',
13
+ format: 'cjs',
14
+ exports: 'auto',
15
+ },
16
+ {
17
+ file: 'dist/index.esm.js',
18
+ format: 'esm',
19
+ },
20
+ ],
21
+ plugins: [
22
+ resolve(),
23
+ commonjs(),
24
+ typescript({ tsconfig: './tsconfig.json' }),
25
+ babel({
26
+ babelHelpers: 'bundled',
27
+ extensions: ['.js', '.ts'],
28
+ exclude: 'node_modules/**',
29
+ }),
30
+ postcss({
31
+ extract: true, // creates a separate CSS file
32
+ minimize: true,
33
+ sourceMap: true,
34
+ }),
35
+ terser(),
36
+ ],
37
+ };
package/src/index.ts ADDED
@@ -0,0 +1,242 @@
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
+ }