unified-video-framework 1.4.217 → 1.4.218
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.
- package/CHANGELOG.md +0 -102
- package/package.json +1 -1
- package/packages/core/dist/interfaces.d.ts +2 -34
- package/packages/core/dist/interfaces.d.ts.map +1 -1
- package/packages/core/src/interfaces.ts +3 -51
- package/packages/web/dist/ads/GoogleAdsManager.d.ts +39 -0
- package/packages/web/dist/ads/GoogleAdsManager.d.ts.map +1 -0
- package/packages/web/dist/ads/GoogleAdsManager.js +180 -0
- package/packages/web/dist/ads/GoogleAdsManager.js.map +1 -0
- package/packages/web/dist/index.d.ts +0 -1
- package/packages/web/dist/index.d.ts.map +1 -1
- package/packages/web/dist/index.js +0 -1
- package/packages/web/dist/index.js.map +1 -1
- package/packages/web/dist/react/WebPlayerView.d.ts +90 -0
- package/packages/web/dist/react/WebPlayerView.d.ts.map +1 -1
- package/packages/web/dist/react/WebPlayerView.js +162 -1
- package/packages/web/dist/react/WebPlayerView.js.map +1 -1
- package/packages/web/src/ads/GoogleAdsManager.ts +358 -0
- package/packages/web/src/index.ts +0 -3
- package/packages/web/src/react/WebPlayerView.tsx +304 -2
- package/packages/web/src/ads/AdsManager.ts +0 -691
- package/packages/web/src/ads/README.md +0 -403
- package/packages/web/src/ads/index.ts +0 -17
- package/packages/web/src/ads/types.ts +0 -442
|
@@ -20,6 +20,76 @@ const loadEPGComponents = async () => {
|
|
|
20
20
|
return EPGOverlay;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
// Chapter API interface exposed to parent component
|
|
24
|
+
export interface ChapterAPI {
|
|
25
|
+
loadChapters: (chapters: any) => Promise<void>;
|
|
26
|
+
loadChaptersFromUrl: (url: string) => Promise<void>;
|
|
27
|
+
getCurrentSegment: () => any | null;
|
|
28
|
+
skipToSegment: (segmentId: string) => void;
|
|
29
|
+
getSegments: () => any[];
|
|
30
|
+
updateChapterConfig: (config: any) => void;
|
|
31
|
+
hasChapters: () => boolean;
|
|
32
|
+
getChapters: () => any | null;
|
|
33
|
+
getCoreChapters: () => any[];
|
|
34
|
+
getCoreSegments: () => any[];
|
|
35
|
+
getCurrentChapterInfo: () => any | null;
|
|
36
|
+
seekToChapter: (chapterId: string) => void;
|
|
37
|
+
getNextChapter: () => any | null;
|
|
38
|
+
getPreviousChapter: () => any | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Quality control API
|
|
42
|
+
export interface QualityAPI {
|
|
43
|
+
getQualities: () => any[];
|
|
44
|
+
getCurrentQuality: () => any | null;
|
|
45
|
+
setQuality: (index: number) => void;
|
|
46
|
+
setAutoQuality: (enabled: boolean) => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// EPG API interface
|
|
50
|
+
export interface EPGControlAPI {
|
|
51
|
+
setEPGData: (data: any) => void;
|
|
52
|
+
showEPGButton: () => void;
|
|
53
|
+
hideEPGButton: () => void;
|
|
54
|
+
isEPGButtonVisible: () => boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// UI Helper API
|
|
58
|
+
export interface UIHelperAPI {
|
|
59
|
+
focusPlayer: () => void;
|
|
60
|
+
showFullscreenTip: () => void;
|
|
61
|
+
triggerFullscreenButton: () => void;
|
|
62
|
+
showTemporaryMessage: (message: string) => void;
|
|
63
|
+
showFullscreenInstructions: () => void;
|
|
64
|
+
enterFullscreenSynchronously: () => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Fullscreen API
|
|
68
|
+
export interface FullscreenAPI {
|
|
69
|
+
enterFullscreen: () => Promise<void>;
|
|
70
|
+
exitFullscreen: () => Promise<void>;
|
|
71
|
+
toggleFullscreen: () => Promise<void>;
|
|
72
|
+
enterPictureInPicture: () => Promise<void>;
|
|
73
|
+
exitPictureInPicture: () => Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Playback control API
|
|
77
|
+
export interface PlaybackAPI {
|
|
78
|
+
play: () => Promise<void>;
|
|
79
|
+
pause: () => void;
|
|
80
|
+
requestPause: () => void;
|
|
81
|
+
seek: (time: number) => void;
|
|
82
|
+
setVolume: (level: number) => void;
|
|
83
|
+
mute: () => void;
|
|
84
|
+
unmute: () => void;
|
|
85
|
+
toggleMute: () => void;
|
|
86
|
+
setPlaybackRate: (rate: number) => void;
|
|
87
|
+
getPlaybackRate: () => number;
|
|
88
|
+
getCurrentTime: () => number;
|
|
89
|
+
getDuration: () => number;
|
|
90
|
+
getState: () => any;
|
|
91
|
+
}
|
|
92
|
+
|
|
23
93
|
export type WebPlayerViewProps = {
|
|
24
94
|
// Player config
|
|
25
95
|
autoPlay?: boolean;
|
|
@@ -144,9 +214,45 @@ export type WebPlayerViewProps = {
|
|
|
144
214
|
};
|
|
145
215
|
};
|
|
146
216
|
|
|
217
|
+
// Settings customization
|
|
218
|
+
settingsScrollbar?: {
|
|
219
|
+
style?: 'default' | 'compact' | 'overlay';
|
|
220
|
+
widthPx?: number;
|
|
221
|
+
intensity?: number;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Auto-focus player on mount
|
|
225
|
+
autoFocusPlayer?: boolean;
|
|
226
|
+
|
|
227
|
+
// Show fullscreen tip on mount
|
|
228
|
+
showFullscreenTipOnMount?: boolean;
|
|
229
|
+
|
|
230
|
+
// Player instance ref (for imperative control)
|
|
231
|
+
playerRef?: React.RefObject<WebPlayer>;
|
|
232
|
+
|
|
233
|
+
// API callbacks - expose imperative APIs to parent
|
|
234
|
+
onChapterAPI?: (api: ChapterAPI) => void;
|
|
235
|
+
onQualityAPI?: (api: QualityAPI) => void;
|
|
236
|
+
onEPGAPI?: (api: EPGControlAPI) => void;
|
|
237
|
+
onUIHelperAPI?: (api: UIHelperAPI) => void;
|
|
238
|
+
onFullscreenAPI?: (api: FullscreenAPI) => void;
|
|
239
|
+
onPlaybackAPI?: (api: PlaybackAPI) => void;
|
|
240
|
+
|
|
147
241
|
// Callbacks
|
|
148
242
|
onReady?: (player: WebPlayer) => void;
|
|
149
243
|
onError?: (error: unknown) => void;
|
|
244
|
+
|
|
245
|
+
// Additional player event callbacks
|
|
246
|
+
onPlay?: () => void;
|
|
247
|
+
onPause?: () => void;
|
|
248
|
+
onEnded?: () => void;
|
|
249
|
+
onTimeUpdate?: (data: { currentTime: number; duration: number }) => void;
|
|
250
|
+
onProgress?: (data: { buffered: number }) => void;
|
|
251
|
+
onVolumeChange?: (data: { volume: number; muted: boolean }) => void;
|
|
252
|
+
onQualityChange?: (quality: any) => void;
|
|
253
|
+
onBuffering?: (isBuffering: boolean) => void;
|
|
254
|
+
onFullscreenChange?: (isFullscreen: boolean) => void;
|
|
255
|
+
onPictureInPictureChange?: (isPiP: boolean) => void;
|
|
150
256
|
|
|
151
257
|
// EPG (Electronic Program Guide) support
|
|
152
258
|
epg?: EPGData;
|
|
@@ -258,7 +364,9 @@ export type WebPlayerViewProps = {
|
|
|
258
364
|
|
|
259
365
|
export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
260
366
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
261
|
-
const
|
|
367
|
+
const internalPlayerRef = useRef<WebPlayer | null>(null);
|
|
368
|
+
// Use external ref if provided, otherwise use internal
|
|
369
|
+
const playerRef = props.playerRef || internalPlayerRef;
|
|
262
370
|
const [dimensions, setDimensions] = useState({
|
|
263
371
|
width: typeof window !== 'undefined' ? window.innerWidth : 1920,
|
|
264
372
|
height: typeof window !== 'undefined' ? window.innerHeight : 1080,
|
|
@@ -479,6 +587,9 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
479
587
|
|
|
480
588
|
const player = new WebPlayer();
|
|
481
589
|
playerRef.current = player;
|
|
590
|
+
|
|
591
|
+
// Expose all APIs to parent component via callbacks
|
|
592
|
+
exposeAPIsToParent(player);
|
|
482
593
|
|
|
483
594
|
// Optionally load Google Cast sender SDK
|
|
484
595
|
if (props.cast) {
|
|
@@ -626,6 +737,21 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
626
737
|
if (!cancelled) {
|
|
627
738
|
setPlayerReady(true);
|
|
628
739
|
|
|
740
|
+
// Apply settings scrollbar configuration if provided
|
|
741
|
+
if (props.settingsScrollbar) {
|
|
742
|
+
applySettingsScrollbar(player, props.settingsScrollbar);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Auto-focus player if requested
|
|
746
|
+
if (props.autoFocusPlayer && typeof (player as any).focusPlayer === 'function') {
|
|
747
|
+
(player as any).focusPlayer();
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Show fullscreen tip if requested
|
|
751
|
+
if (props.showFullscreenTipOnMount && typeof (player as any).showFullscreenTip === 'function') {
|
|
752
|
+
(player as any).showFullscreenTip();
|
|
753
|
+
}
|
|
754
|
+
|
|
629
755
|
// Set up EPG integration
|
|
630
756
|
if (props.epg && typeof (player as any).setEPGData === 'function') {
|
|
631
757
|
(player as any).setEPGData(props.epg);
|
|
@@ -678,6 +804,48 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
678
804
|
(player as any).on('navigationCloseClicked', props.onNavigationCloseClicked);
|
|
679
805
|
}
|
|
680
806
|
|
|
807
|
+
// Additional event listeners
|
|
808
|
+
if (props.onPlay && typeof (player as any).on === 'function') {
|
|
809
|
+
(player as any).on('onPlay', props.onPlay);
|
|
810
|
+
}
|
|
811
|
+
if (props.onPause && typeof (player as any).on === 'function') {
|
|
812
|
+
(player as any).on('onPause', props.onPause);
|
|
813
|
+
}
|
|
814
|
+
if (props.onEnded && typeof (player as any).on === 'function') {
|
|
815
|
+
(player as any).on('onEnded', props.onEnded);
|
|
816
|
+
}
|
|
817
|
+
if (props.onTimeUpdate && typeof (player as any).on === 'function') {
|
|
818
|
+
(player as any).on('onTimeUpdate', (currentTime: number) => {
|
|
819
|
+
props.onTimeUpdate?.({
|
|
820
|
+
currentTime,
|
|
821
|
+
duration: (player as any).getDuration ? (player as any).getDuration() : 0
|
|
822
|
+
});
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
if (props.onProgress && typeof (player as any).on === 'function') {
|
|
826
|
+
(player as any).on('onProgress', (buffered: number) => {
|
|
827
|
+
props.onProgress?.({ buffered });
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
if (props.onVolumeChange && typeof (player as any).on === 'function') {
|
|
831
|
+
(player as any).on('onVolumeChanged', (volume: number) => {
|
|
832
|
+
const state = (player as any).getState ? (player as any).getState() : {};
|
|
833
|
+
props.onVolumeChange?.({ volume, muted: state.isMuted || false });
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
if (props.onQualityChange && typeof (player as any).on === 'function') {
|
|
837
|
+
(player as any).on('onQualityChanged', props.onQualityChange);
|
|
838
|
+
}
|
|
839
|
+
if (props.onBuffering && typeof (player as any).on === 'function') {
|
|
840
|
+
(player as any).on('onBuffering', props.onBuffering);
|
|
841
|
+
}
|
|
842
|
+
if (props.onFullscreenChange && typeof (player as any).on === 'function') {
|
|
843
|
+
(player as any).on('onFullscreenChanged', props.onFullscreenChange);
|
|
844
|
+
}
|
|
845
|
+
if (props.onPictureInPictureChange && typeof (player as any).on === 'function') {
|
|
846
|
+
(player as any).on('onPictureInPicturechange', props.onPictureInPictureChange);
|
|
847
|
+
}
|
|
848
|
+
|
|
681
849
|
props.onReady?.(player);
|
|
682
850
|
}
|
|
683
851
|
} catch (err) {
|
|
@@ -721,7 +889,141 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
721
889
|
props.showFrameworkBranding,
|
|
722
890
|
JSON.stringify(props.watermark),
|
|
723
891
|
JSON.stringify(props.navigation),
|
|
724
|
-
])
|
|
892
|
+
]);
|
|
893
|
+
|
|
894
|
+
// Helper function to expose all APIs to parent
|
|
895
|
+
const exposeAPIsToParent = useCallback((player: WebPlayer) => {
|
|
896
|
+
const p = player as any;
|
|
897
|
+
|
|
898
|
+
// Chapter API
|
|
899
|
+
if (props.onChapterAPI) {
|
|
900
|
+
const chapterAPI: ChapterAPI = {
|
|
901
|
+
loadChapters: (chapters: any) => p.loadChapters ? p.loadChapters(chapters) : Promise.resolve(),
|
|
902
|
+
loadChaptersFromUrl: (url: string) => p.loadChaptersFromUrl ? p.loadChaptersFromUrl(url) : Promise.resolve(),
|
|
903
|
+
getCurrentSegment: () => p.getCurrentSegment ? p.getCurrentSegment() : null,
|
|
904
|
+
skipToSegment: (segmentId: string) => p.skipToSegment && p.skipToSegment(segmentId),
|
|
905
|
+
getSegments: () => p.getSegments ? p.getSegments() : [],
|
|
906
|
+
updateChapterConfig: (config: any) => p.updateChapterConfig && p.updateChapterConfig(config),
|
|
907
|
+
hasChapters: () => p.hasChapters ? p.hasChapters() : false,
|
|
908
|
+
getChapters: () => p.getChapters ? p.getChapters() : null,
|
|
909
|
+
getCoreChapters: () => p.getCoreChapters ? p.getCoreChapters() : [],
|
|
910
|
+
getCoreSegments: () => p.getCoreSegments ? p.getCoreSegments() : [],
|
|
911
|
+
getCurrentChapterInfo: () => p.getCurrentChapterInfo ? p.getCurrentChapterInfo() : null,
|
|
912
|
+
seekToChapter: (chapterId: string) => p.seekToChapter && p.seekToChapter(chapterId),
|
|
913
|
+
getNextChapter: () => p.getNextChapter ? p.getNextChapter() : null,
|
|
914
|
+
getPreviousChapter: () => p.getPreviousChapter ? p.getPreviousChapter() : null,
|
|
915
|
+
};
|
|
916
|
+
props.onChapterAPI(chapterAPI);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Quality API
|
|
920
|
+
if (props.onQualityAPI) {
|
|
921
|
+
const qualityAPI: QualityAPI = {
|
|
922
|
+
getQualities: () => p.getQualities ? p.getQualities() : [],
|
|
923
|
+
getCurrentQuality: () => p.getCurrentQuality ? p.getCurrentQuality() : null,
|
|
924
|
+
setQuality: (index: number) => p.setQuality && p.setQuality(index),
|
|
925
|
+
setAutoQuality: (enabled: boolean) => p.setAutoQuality && p.setAutoQuality(enabled),
|
|
926
|
+
};
|
|
927
|
+
props.onQualityAPI(qualityAPI);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// EPG API
|
|
931
|
+
if (props.onEPGAPI) {
|
|
932
|
+
const epgAPI: EPGControlAPI = {
|
|
933
|
+
setEPGData: (data: any) => p.setEPGData && p.setEPGData(data),
|
|
934
|
+
showEPGButton: () => p.showEPGButton && p.showEPGButton(),
|
|
935
|
+
hideEPGButton: () => p.hideEPGButton && p.hideEPGButton(),
|
|
936
|
+
isEPGButtonVisible: () => p.isEPGButtonVisible ? p.isEPGButtonVisible() : false,
|
|
937
|
+
};
|
|
938
|
+
props.onEPGAPI(epgAPI);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// UI Helper API
|
|
942
|
+
if (props.onUIHelperAPI) {
|
|
943
|
+
const uiAPI: UIHelperAPI = {
|
|
944
|
+
focusPlayer: () => p.focusPlayer && p.focusPlayer(),
|
|
945
|
+
showFullscreenTip: () => p.showFullscreenTip && p.showFullscreenTip(),
|
|
946
|
+
triggerFullscreenButton: () => p.triggerFullscreenButton && p.triggerFullscreenButton(),
|
|
947
|
+
showTemporaryMessage: (message: string) => p.showTemporaryMessage && p.showTemporaryMessage(message),
|
|
948
|
+
showFullscreenInstructions: () => p.showFullscreenInstructions && p.showFullscreenInstructions(),
|
|
949
|
+
enterFullscreenSynchronously: () => p.enterFullscreenSynchronously && p.enterFullscreenSynchronously(),
|
|
950
|
+
};
|
|
951
|
+
props.onUIHelperAPI(uiAPI);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// Fullscreen API
|
|
955
|
+
if (props.onFullscreenAPI) {
|
|
956
|
+
const fullscreenAPI: FullscreenAPI = {
|
|
957
|
+
enterFullscreen: () => p.enterFullscreen ? p.enterFullscreen() : Promise.resolve(),
|
|
958
|
+
exitFullscreen: () => p.exitFullscreen ? p.exitFullscreen() : Promise.resolve(),
|
|
959
|
+
toggleFullscreen: () => p.toggleFullscreen ? p.toggleFullscreen() : Promise.resolve(),
|
|
960
|
+
enterPictureInPicture: () => p.enterPictureInPicture ? p.enterPictureInPicture() : Promise.resolve(),
|
|
961
|
+
exitPictureInPicture: () => p.exitPictureInPicture ? p.exitPictureInPicture() : Promise.resolve(),
|
|
962
|
+
};
|
|
963
|
+
props.onFullscreenAPI(fullscreenAPI);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Playback API
|
|
967
|
+
if (props.onPlaybackAPI) {
|
|
968
|
+
const playbackAPI: PlaybackAPI = {
|
|
969
|
+
play: () => p.play ? p.play() : Promise.resolve(),
|
|
970
|
+
pause: () => p.pause && p.pause(),
|
|
971
|
+
requestPause: () => p.requestPause && p.requestPause(),
|
|
972
|
+
seek: (time: number) => p.seek && p.seek(time),
|
|
973
|
+
setVolume: (level: number) => p.setVolume && p.setVolume(level),
|
|
974
|
+
mute: () => p.mute && p.mute(),
|
|
975
|
+
unmute: () => p.unmute && p.unmute(),
|
|
976
|
+
toggleMute: () => p.toggleMute && p.toggleMute(),
|
|
977
|
+
setPlaybackRate: (rate: number) => p.setPlaybackRate && p.setPlaybackRate(rate),
|
|
978
|
+
getPlaybackRate: () => p.getPlaybackRate ? p.getPlaybackRate() : 1,
|
|
979
|
+
getCurrentTime: () => p.getCurrentTime ? p.getCurrentTime() : 0,
|
|
980
|
+
getDuration: () => p.getDuration ? p.getDuration() : 0,
|
|
981
|
+
getState: () => p.getState ? p.getState() : {},
|
|
982
|
+
};
|
|
983
|
+
props.onPlaybackAPI(playbackAPI);
|
|
984
|
+
}
|
|
985
|
+
}, [
|
|
986
|
+
props.onChapterAPI,
|
|
987
|
+
props.onQualityAPI,
|
|
988
|
+
props.onEPGAPI,
|
|
989
|
+
props.onUIHelperAPI,
|
|
990
|
+
props.onFullscreenAPI,
|
|
991
|
+
props.onPlaybackAPI,
|
|
992
|
+
]);
|
|
993
|
+
|
|
994
|
+
// Helper function to apply settings scrollbar configuration
|
|
995
|
+
const applySettingsScrollbar = useCallback((player: WebPlayer, config: NonNullable<typeof props.settingsScrollbar>) => {
|
|
996
|
+
const p = player as any;
|
|
997
|
+
|
|
998
|
+
// Apply scrollbar style
|
|
999
|
+
if (config.style && typeof p.setSettingsScrollbarStyle === 'function') {
|
|
1000
|
+
p.setSettingsScrollbarStyle(config.style);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// Apply scrollbar config (width and intensity)
|
|
1004
|
+
if ((config.widthPx !== undefined || config.intensity !== undefined) &&
|
|
1005
|
+
typeof p.setSettingsScrollbarConfig === 'function') {
|
|
1006
|
+
p.setSettingsScrollbarConfig({
|
|
1007
|
+
widthPx: config.widthPx,
|
|
1008
|
+
intensity: config.intensity,
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
}, []);
|
|
1012
|
+
|
|
1013
|
+
// Apply settings scrollbar changes at runtime
|
|
1014
|
+
useEffect(() => {
|
|
1015
|
+
const p = playerRef.current as any;
|
|
1016
|
+
if (p && props.settingsScrollbar && playerReady) {
|
|
1017
|
+
applySettingsScrollbar(p, props.settingsScrollbar);
|
|
1018
|
+
}
|
|
1019
|
+
}, [JSON.stringify(props.settingsScrollbar), playerReady, applySettingsScrollbar]);
|
|
1020
|
+
|
|
1021
|
+
// Re-expose APIs when player is ready (handles hot reloading)
|
|
1022
|
+
useEffect(() => {
|
|
1023
|
+
if (playerRef.current && playerReady) {
|
|
1024
|
+
exposeAPIsToParent(playerRef.current);
|
|
1025
|
+
}
|
|
1026
|
+
}, [playerReady, exposeAPIsToParent]);
|
|
725
1027
|
|
|
726
1028
|
// Update free preview duration at runtime without full re-init
|
|
727
1029
|
useEffect(() => {
|