1- import videojs from 'video.js' ;
1+ import videojs , { type Player as VideoJsPlayer } from 'video.js' ;
22import PluginType from 'video.js/dist/types/plugin' ;
33import './modules/http-source-selector/plugin' ;
44import './modules/context-menu/plugin' ;
5- import type { IKPlayerOptions , RemoteTextTrackOptions } from './interfaces' ;
6- import type Player from 'video.js/dist/types/player' ;
5+ import type { IKPlayerOptions , RemoteTextTrackOptions , Player } from './interfaces' ;
76import type { SourceOptions } from './interfaces' ;
87import type { AugmentedSourceOptions } from './interfaces/AugementedSourceOptions' ;
98
@@ -16,6 +15,7 @@ import { ShoppableManager } from './modules/shoppable/shoppable-manager';
1615import { prepareSource , normalizeInput , waitForVideoReady , preparePosterSrc , validateIKPlayerOptions , prepareChaptersVttSrc , CleanupRegistry } from './utils' ;
1716import { enableFloatingPlayer } from './modules/floating-player' ;
1817import './modules/logo-button' ;
18+
1919const defaults : IKPlayerOptions = {
2020 imagekitId : 'random_id' ,
2121 floatingWhenNotVisible : null ,
@@ -248,6 +248,10 @@ this.player.ready(() => {
248248
249249 private overrideSrc ( ) {
250250 const nativeSrc = this . player . src . bind ( this . player ) ;
251+ const ensurePrepared = ( src : AugmentedSourceOptions ) : NonNullable < AugmentedSourceOptions [ 'prepared' ] > => {
252+ if ( ! src . prepared ) src . prepared = { } ;
253+ return src . prepared ;
254+ } ;
251255
252256 this . player . src = ( raw : string | SourceOptions | Array < string | SourceOptions > | undefined ) => {
253257 // increment the version on each call
@@ -286,18 +290,8 @@ this.player.ready(() => {
286290 const { maxTries, videoTimeoutInMS, delayInMS } = this . ikGlobalSettings_ ;
287291 // set prepared.src
288292 inputs . forEach ( ( src ) => {
289- // @ts -ignore
290- if ( ! this . hasPreparedSrc ( src ) ) {
291- // if src is not prepared, set the src directly
292-
293- // @ts -ignore
294- if ( ! src . prepared )
295- {
296- // @ts -ignore
297- src . prepared = { }
298- }
299- // @ts -ignore
300- src . prepared . src = prepared [ 0 ] . src ;
293+ if ( typeof src === 'object' && src !== null && ! this . hasPreparedSrc ( src ) ) {
294+ ensurePrepared ( src as AugmentedSourceOptions ) . src = prepared [ 0 ] . src ;
301295 }
302296 } ) ;
303297
@@ -323,29 +317,20 @@ this.player.ready(() => {
323317 ? this . currentSource_ [ 0 ]
324318 : this . currentSource_ ;
325319 // if poster is already prepared, use it directly
326- // @ts -ignore
327- if ( currentSource_ ?. prepared ?. poster ) {
328- console . log ( "Using prepared poster src" )
329- // @ts -ignore
330- this . player . poster ( currentSource_ ?. prepared ?. poster ) ;
320+ const preparedPoster = ( currentSource_ as AugmentedSourceOptions | null | undefined ) ?. prepared ?. poster ;
321+ if ( preparedPoster ) {
322+ this . player . poster ( preparedPoster ) ;
331323 }
332324 else {
333325 preparePosterSrc ( currentSource_ , this . ikGlobalSettings_ ) . then (
334326 poster => {
335- // if preparedPosterSrc is not empty, set it
336- // @ts -ignore
337- // currentSource_.preparedPosterSrc = poster;
338327 // set the poster on the player
339328 if ( poster ) {
340329 this . player . poster ( poster ) ;
341330 }
342- // @ts -ignore
343- if ( ! currentSource_ . prepared ) {
344- // @ts -ignore
345- currentSource_ . prepared = { } ;
331+ if ( currentSource_ ) {
332+ ensurePrepared ( currentSource_ as AugmentedSourceOptions ) . poster = poster ?? undefined ;
346333 }
347- // @ts -ignore
348- currentSource_ . prepared . poster = poster ;
349334 }
350335 ) . catch ( err => {
351336 this . player . error ( err . message ) ;
@@ -399,14 +384,6 @@ this.player.ready(() => {
399384 this . player . log . warn ( `Default VTT fetch failed with status: (${ res . status } ); skipping chapters.` ) ;
400385 return ;
401386 }
402- // add chapters track
403- // @todo commented this. You are overriding remoteTextTrack. Verify the correctness of this.
404- // const chaptersTrack = this.player.addRemoteTextTrack({
405- // kind: 'chapters',
406- // label: 'Chapters',
407- // src: 'https://ik.imagekit.io/zuqlyov9d/chapters.vtt',
408- // default: false,
409- // }, false);
410387 const data = await res . text ( ) ;
411388 chapterList = parseChaptersFromVTT ( data ) ;
412389 } catch ( e ) {
@@ -432,7 +409,6 @@ this.player.ready(() => {
432409 if ( ! src . recommendations ) return ;
433410
434411 const overlay = this . player . getChild ( 'RecommendationsOverlay' ) ;
435- console . log ( 'RecommendationsOverlay:' , overlay ) ;
436412 if ( overlay ) overlay . dispose ( ) ;
437413 this . player . addChild ( 'RecommendationsOverlay' , { recommendations : src . recommendations , playerOptions : this . ikGlobalSettings_ } ) ;
438414 }
@@ -493,63 +469,62 @@ export function videoPlayer(
493469 element : string | HTMLElement ,
494470 options : IKPlayerOptions ,
495471 videoJsOptions : any = { }
496- ) {
497- const player = videojs ( element , {
498- ... videoJsOptions ,
499- // playbackRates: [0.5, 1, 1.5, 2],
500- // children: {
501- // controlBar: {
502- // fullscreenToggle: false,
503- // pictureInPictureToggle: false,
504- // volumePanel: false,
505- // playbackRateMenuButton: false,
506- // }
507- // },
508- // controls: false,
509- // autoplay: true,
510- // aspectRatio: '9:16',
472+ ) : Player {
473+ // Keep this for reference
474+ // videoJsOptions = {
475+ // playbackRates: [0.5, 1, 1.5, 2],
476+ // children: {
477+ // controlBar: {
478+ // fullscreenToggle: false,
479+ // pictureInPictureToggle: false,
480+ // volumePanel: false,
481+ // playbackRateMenuButton: false,
482+ // }
483+ // },
484+ // controls: false,
485+ // autoplay: true,
486+ // aspectRatio: '9:16',
511487
512- // responsive: true,
513- // breakpoints: {
514- // // tiny: 300,
515- // // xsmall: 400,
516- // // small: 500,
517- // // medium: 600,
518- // // large: 700,
519- // // xlarge: 800,
520- // huge: 900
521- // },
522- // controlBar: {
523- // skipButtons: {
524- // forward: 10
525- // }
526- // },
527-
488+ // responsive: true,
489+ // breakpoints: {
490+ // // tiny: 300,
491+ // // xsmall: 400,
492+ // // small: 500,
493+ // // medium: 600,
494+ // // large: 700,
495+ // // xlarge: 800,
496+ // huge: 900
497+ // },
498+ // controlBar: {
499+ // skipButtons: {
500+ // forward: 10
501+ // }
502+ // },
503+ // }
504+ const player : VideoJsPlayer = videojs ( element , {
505+ ...videoJsOptions ,
528506 html5 : { nativeTextTracks : false } ,
529507 plugins : {
530508 ...( videoJsOptions . plugins ?? { } ) ,
531509 httpSourceSelector : { default : 'auto' } ,
532510 imagekitVideoPlayer : options ,
533511 } ,
534512 } ) ;
535- // @ts -ignore
536- player . httpSourceSelector ( ) ;
537- // @ts -ignore
538- // Explicitly handle both cases for the context menu
513+
514+ // Handle context menu visibility
539515 if ( options . hideContextMenu === true ) {
540516 // If hiding is requested, add a listener that ONLY prevents the default menu.
541517 // This will disable all right-click menus on the player.
542518 player . on ( 'contextmenu' , ( e : Event ) => {
543519 e . preventDefault ( ) ;
544520 } ) ;
545521 } else {
546- // Otherwise, set up our custom, dynamic context menu.
547-
522+ // Initialize context menu plugin only when not hidden
548523 /**
549524 * Helper function to generate the context menu content
550525 * based on the player's current state.
551526 */
552- const createContextMenuContent = ( ) => {
527+ const createContextMenuContent = ( player : Player ) => {
553528 return [ {
554529 label : player . paused ( ) ? "Play" : "Pause" ,
555530 listener : function ( ) {
@@ -584,23 +559,33 @@ export function videoPlayer(
584559 } ] ;
585560 } ;
586561
587- // Initialize the context menu with the initial content.
588- // The contextmenuUI plugin internally handles preventDefault for this case.
589- // @ts -ignore
590- player . contextmenuUI ( {
591- content : createContextMenuContent ( )
592- } ) ;
562+ // Check if plugin is available
563+ const hasContextMenuUIMethod = typeof ( player as any ) . contextmenuUI === 'function' ;
593564
594- // Add an event listener to update the menu content on every right-click
595- player . on ( 'contextmenu' , ( ) => {
596- // @ts -ignore
597- player . contextmenuUI ( {
598- content : createContextMenuContent ( )
599- } ) ;
600- } ) ;
565+ if ( ! hasContextMenuUIMethod ) {
566+ console . error ( '[ImageKit Video Player] ERROR: contextmenuUI method not found on player!' ) ;
567+ console . error ( '[ImageKit Video Player] Available plugins:' , Object . keys ( videojs . getPlugins ( ) ) ) ;
568+ } else {
569+ try {
570+ ( player as any ) . contextmenuUI ( {
571+ createContextMenuContent : createContextMenuContent
572+ } ) ;
573+ } catch ( error ) {
574+ console . error ( '[ImageKit Video Player] ERROR initializing context menu plugin:' , error ) ;
575+ }
576+ }
577+ }
578+
579+ // Verify that plugin augmentation completed successfully
580+ // The imagekitVideoPlayer plugin adds methods (playlist, src override, etc.) at runtime
581+ if ( ! ( 'playlist' in player ) || typeof ( player as any ) . playlist !== 'function' ) {
582+ throw new Error ( 'ImageKit video player plugin failed to initialize: playlist method not found' ) ;
601583 }
602584
603- return player ;
585+ // Type assertion: player has augmented methods added by the plugin at runtime
586+ // Using double assertion (as unknown as Player) because VideoJsPlayer and Player
587+ // don't have sufficient type overlap (playlist method is added at runtime)
588+ return player as unknown as Player ;
604589}
605590
606591export * from './interfaces' ;
0 commit comments