@playkit-js/unisphere-service 0.0.17-canary.0-e5f111c

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 (46) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +99 -0
  3. package/dist/index.d.ts +40 -0
  4. package/dist/playkit-unisphere-service.js +3 -0
  5. package/dist/playkit-unisphere-service.js.LICENSE.txt +1 -0
  6. package/dist/playkit-unisphere-service.js.map +1 -0
  7. package/dist/tsdoc-metadata.json +11 -0
  8. package/package.json +100 -0
  9. package/src/components/close-button/close-button.tsx +25 -0
  10. package/src/components/close-button/index.ts +1 -0
  11. package/src/components/container-element/container-element.tsx +13 -0
  12. package/src/components/container-element/index.ts +1 -0
  13. package/src/components/index.ts +4 -0
  14. package/src/components/sidepanel-buttons-container/index.ts +1 -0
  15. package/src/components/sidepanel-buttons-container/sidepanel-buttons-container.tsx +40 -0
  16. package/src/components/upper-bar-icon/index.ts +1 -0
  17. package/src/components/upper-bar-icon/upper-bar-icon.tsx +23 -0
  18. package/src/index.ts +6 -0
  19. package/src/services/container/container-manager.ts +25 -0
  20. package/src/services/container/index.ts +1 -0
  21. package/src/services/container/producers/base-container-producer.ts +37 -0
  22. package/src/services/container/producers/container-producer.ts +8 -0
  23. package/src/services/container/producers/index.ts +3 -0
  24. package/src/services/container/producers/sidepanel-container-producer.scss +59 -0
  25. package/src/services/container/producers/sidepanel-container-producer.tsx +67 -0
  26. package/src/services/container/producers/simple-container-producer.tsx +27 -0
  27. package/src/services/index.ts +3 -0
  28. package/src/services/unisphere/index.ts +4 -0
  29. package/src/services/unisphere/unisphere-base-service.ts +35 -0
  30. package/src/services/unisphere/unisphere-element-service.ts +25 -0
  31. package/src/services/unisphere/unisphere-event-service.ts +36 -0
  32. package/src/services/unisphere/unisphere-runtime-service.ts +17 -0
  33. package/src/services/unisphere/unisphere-storage-service.ts +42 -0
  34. package/src/services/unisphere/unisphere-workspace-service.ts +25 -0
  35. package/src/services/utils/index.ts +1 -0
  36. package/src/services/utils/utils.ts +9 -0
  37. package/src/types/container-types.ts +6 -0
  38. package/src/types/global.d.ts +1 -0
  39. package/src/types/index.ts +5 -0
  40. package/src/types/integrations.ts +38 -0
  41. package/src/types/service-keys.ts +6 -0
  42. package/src/types/unisphere-event.ts +3 -0
  43. package/src/types/unisphere-plugin-config.ts +38 -0
  44. package/src/types/unisphere-storage-keys.ts +9 -0
  45. package/src/unisphere-plugin-manager.tsx +254 -0
  46. package/src/unisphere-plugin.ts +45 -0
@@ -0,0 +1,25 @@
1
+ import { UnisphereWorkspaceType } from '@unisphere/runtime';
2
+
3
+ class UnisphereWorkspaceService {
4
+ private static workspaceInstance: UnisphereWorkspaceType | null = null;
5
+
6
+ public static set(workspaceInstance: UnisphereWorkspaceType): void {
7
+ UnisphereWorkspaceService.workspaceInstance = workspaceInstance;
8
+ }
9
+
10
+ public static get(): UnisphereWorkspaceType | null {
11
+ return UnisphereWorkspaceService.workspaceInstance;
12
+ }
13
+
14
+ public static destroy(): void {
15
+ try {
16
+ (this.workspaceInstance as any).kill();
17
+ } catch (e) {
18
+ // TODO add logs
19
+ }
20
+
21
+ UnisphereWorkspaceService.workspaceInstance = null;
22
+ }
23
+ }
24
+
25
+ export { UnisphereWorkspaceService };
@@ -0,0 +1 @@
1
+ export * from './utils';
@@ -0,0 +1,9 @@
1
+ import { Utils } from '@playkit-js/playkit-js';
2
+
3
+ function getUniqueId(prefix = ''): string {
4
+ let result = prefix;
5
+ result += Utils.Generator.uniqueId(10);
6
+ return result;
7
+ }
8
+
9
+ export { getUniqueId };
@@ -0,0 +1,6 @@
1
+ enum ContainerTypes {
2
+ SIDEPANEL = 'sidepanel',
3
+ SIMPLE = 'simple'
4
+ }
5
+
6
+ export { ContainerTypes };
@@ -0,0 +1 @@
1
+ declare module '*.scss';
@@ -0,0 +1,5 @@
1
+ export { UnisphereEvent } from './unisphere-event';
2
+ export { UnisphereStorageKeys } from './unisphere-storage-keys';
3
+ export { ServiceKeys } from './service-keys';
4
+ export { ContainerTypes } from './container-types';
5
+ export * from './unisphere-plugin-config';
@@ -0,0 +1,38 @@
1
+ export interface UnisphereIntegrationButton {
2
+ id: string;
3
+ icon: string;
4
+ tooltip: string;
5
+ visible?: boolean;
6
+ onClick?: () => void;
7
+ }
8
+
9
+ export interface UnisphereIntegrationHeader {
10
+ type: string;
11
+ title: string;
12
+ showCloseButton: boolean;
13
+ buttons: UnisphereIntegrationButton[];
14
+ }
15
+
16
+ export interface UnisphereIntegrationSidepanel {
17
+ header: UnisphereIntegrationHeader;
18
+ visualType: string;
19
+ visualSettings: Record<string, any>;
20
+ }
21
+
22
+ export interface UnisphereIntegrationLayoutHeader {
23
+ icon: string;
24
+ tooltip: string;
25
+ }
26
+
27
+ export interface UnisphereIntegrationLayout {
28
+ presets: string[];
29
+ type: string;
30
+ header: UnisphereIntegrationLayoutHeader;
31
+ sidepanel: UnisphereIntegrationSidepanel;
32
+ mountWhileHidden: boolean;
33
+ }
34
+
35
+ export interface UnisphereIntegrations {
36
+ schema: number;
37
+ layouts: UnisphereIntegrationLayout[];
38
+ }
@@ -0,0 +1,6 @@
1
+ enum ServiceKeys {
2
+ SIDE_PANELS_MANAGER = 'sidePanelsManager',
3
+ UPPER_BAR_MANAGER = 'upperBarManager'
4
+ }
5
+
6
+ export { ServiceKeys };
@@ -0,0 +1,3 @@
1
+ export enum UnisphereEvent {
2
+ MEDIA_SEEK_TO_TIME = 'kaltura-player.events.media.seek-to-time'
3
+ }
@@ -0,0 +1,38 @@
1
+ import { ReservedPresetAreas, ReservedPresetNames } from 'services/preset-manager/models/preset-item-data';
2
+ import { UnisphereIntegrationLayout } from './integrations';
3
+
4
+ export const isReservedPresetNames = (value: any): value is ReservedPresetNames => {
5
+ return ['Playback'].includes(value);
6
+ }
7
+
8
+ export interface ContainerConfig {
9
+ containerType: string;
10
+ presets: ReservedPresetNames[];
11
+ unisphereArea: string;
12
+ position?: string,
13
+ expandMode?: string
14
+ expandOnFirstPlay?: boolean,
15
+ layout?: UnisphereIntegrationLayout
16
+ }
17
+
18
+ export interface SidePanelContainerConfig extends ContainerConfig {
19
+ position?: string;
20
+ expandMode?: string;
21
+ expandOnFirstPlay?: boolean
22
+ }
23
+
24
+ export interface SimpleContainerConfig extends ContainerConfig {
25
+ playerArea: ReservedPresetAreas;
26
+ }
27
+
28
+ export interface WidgetConfig {
29
+ widgetId: string;
30
+ context: string;
31
+ settings: unknown;
32
+ containers: ContainerConfig[];
33
+ }
34
+
35
+ export interface UnispherePluginConfig {
36
+ workspaceName?: string;
37
+ widgets: WidgetConfig[];
38
+ }
@@ -0,0 +1,9 @@
1
+ enum UnisphereStorageKeys {
2
+ SELECTED_CHAPTER = 'selectedChapter',
3
+ CURRENT_TIME = 'currentTime',
4
+ IS_PAUSED = 'isPaused',
5
+ PLAYER_SESSION = 'player-session',
6
+ IS_SMALL_SIZE = 'isSmallSize'
7
+ }
8
+
9
+ export { UnisphereStorageKeys };
@@ -0,0 +1,254 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
+ import { h } from 'preact';
3
+
4
+ import { core, KalturaPlayer } from '@playkit-js/kaltura-player-js';
5
+ import { SidePanelsManager, UpperBarManager } from '@playkit-js/ui-managers';
6
+
7
+ import {
8
+ ContainerConfig,
9
+ isReservedPresetNames,
10
+ SidePanelContainerConfig,
11
+ ContainerTypes,
12
+ UnisphereEvent,
13
+ ServiceKeys,
14
+ UnisphereStorageKeys
15
+ } from './types';
16
+ import { UpperBarIcon } from './components';
17
+ import { ContainerManager, getUniqueId, UnisphereEventService, UnisphereStorageService, UnisphereWorkspaceService } from './services';
18
+ import { UnisphereRuntimeBaseType, UnisphereWorkspaceType } from '@unisphere/runtime';
19
+ import { fetchAndLoadUnisphereWorkspace, getUnisphereInstance } from '@unisphere/runtime-js';
20
+ import { UnisphereIntegrations } from './types/integrations';
21
+
22
+ class UnispherePluginManager {
23
+ private iconIds = new Map<string, number>();
24
+ private panelIds = new Set<number>();
25
+ private persistentIconIds = new Map<string, number>();
26
+ private containerManager: ContainerManager;
27
+ private unsubscribeStore?: () => void;
28
+
29
+ private workspaceName = '';
30
+ private shouldCreateWorkspace = false;
31
+ private expandOnFirstPlayPanels = new Set<number>();
32
+
33
+ constructor(private player: KalturaPlayer, private eventManager: core.EventManager, private logger: any, private config: any) {
34
+ this.containerManager = new ContainerManager(player);
35
+ // TODO remove once this is added to providers.env
36
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
37
+ // @ts-ignore
38
+ const { unisphereLoaderUrl } = this.player.config.provider;
39
+ if (unisphereLoaderUrl && typeof this.config.workspaceName === 'string') {
40
+ this.workspaceName = this.config.workspaceName;
41
+ } else {
42
+ this.workspaceName = getUniqueId('kaltura_player_workspace_');
43
+ this.shouldCreateWorkspace = true;
44
+ }
45
+ }
46
+
47
+ private get upperBarManager(): UpperBarManager | undefined {
48
+ return this.player.getService(ServiceKeys.UPPER_BAR_MANAGER) as UpperBarManager;
49
+ }
50
+
51
+ private get sidePanelsManager(): SidePanelsManager | undefined {
52
+ return this.player.getService(ServiceKeys.SIDE_PANELS_MANAGER) as SidePanelsManager;
53
+ }
54
+
55
+ private addEventHandlers(): void {
56
+ // listen to events from runtime side
57
+ UnisphereEventService.subscribe(UnisphereEvent.MEDIA_SEEK_TO_TIME, (data) => {
58
+ const { seekToInSeconds, entryId, play } = data.payload as {
59
+ seekToInSeconds: string;
60
+ entryId?: string;
61
+ play?: boolean;
62
+ };
63
+ this.handleSeekToTime({ seekToInSeconds, entryId, play });
64
+ });
65
+
66
+ // listen to player events
67
+ this.eventManager.listenOnce(this.player, core.EventType.FIRST_PLAYING, () => {
68
+ this.handleFirstPlay();
69
+ });
70
+ }
71
+
72
+ private handleFirstPlay(): void {
73
+ // activate all panels that should expand on first play
74
+ for (const panelId of this.expandOnFirstPlayPanels) {
75
+ this.sidePanelsManager?.activateItem(panelId);
76
+ }
77
+ }
78
+
79
+ private handleSeekToTime(data: { seekToInSeconds: string; entryId?: string; play?: boolean }): void {
80
+ const { seekToInSeconds, entryId, play } = data;
81
+ const seekTime = parseFloat(seekToInSeconds);
82
+
83
+ if (isNaN(seekTime)) {
84
+ this.logger.warn('Invalid seekToInSeconds value:', seekToInSeconds);
85
+ return;
86
+ }
87
+
88
+ if (entryId && this.player.sources.id !== entryId) {
89
+ this.logger.warn('Entry ID mismatch:', { current: this.player.sources.id, requested: entryId });
90
+ return;
91
+ }
92
+
93
+ this.player.currentTime = seekTime;
94
+
95
+ if (play === true) {
96
+ this.player.play();
97
+ } else if (play === false) {
98
+ this.player.pause();
99
+ }
100
+ }
101
+
102
+ public async getWorkspace(): Promise<UnisphereWorkspaceType> {
103
+ let workspace: UnisphereWorkspaceType;
104
+
105
+ if (this.shouldCreateWorkspace) {
106
+ workspace = (await fetchAndLoadUnisphereWorkspace({
107
+ // TODO remove once this is added to providers.env
108
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
109
+ // @ts-ignore
110
+ serverUrl: this.player.config.provider.unisphereLoaderUrl,
111
+ workspaceName: this.workspaceName,
112
+ appId: 'kaltura-player',
113
+ appVersion: '',
114
+ ui: {
115
+ theme: 'light',
116
+ language: 'en'
117
+ }
118
+ })) as unknown as UnisphereWorkspaceType; // TODO remove once there the type is updated
119
+ } else {
120
+ workspace = await getUnisphereInstance(this.config.workspaceName);
121
+ }
122
+
123
+ UnisphereWorkspaceService.set(workspace);
124
+ return workspace;
125
+ }
126
+
127
+ private addUpperBarIcon(id: string, label: string, order: number, iconPath: string, panelId: number, isPersistent: boolean): void {
128
+ if (this.iconIds.has(id) || this.persistentIconIds.has(id)) return;
129
+
130
+ this.panelIds.add(panelId);
131
+ const iconId = this.upperBarManager?.add({
132
+ displayName: label,
133
+ ariaLabel: label,
134
+ order,
135
+ component: () => (<UpperBarIcon id={id} label={label} iconPath={iconPath} />) as preact.JSX.Element,
136
+ svgIcon: { path: iconPath },
137
+ onClick: () => {
138
+ if (this.sidePanelsManager?.isItemActive(panelId)) {
139
+ this.sidePanelsManager?.deactivateItem(panelId);
140
+ } else {
141
+ this.sidePanelsManager?.activateItem(panelId);
142
+ }
143
+ }
144
+ }) as number;
145
+
146
+ if (isPersistent) {
147
+ this.persistentIconIds.set(id, iconId);
148
+ } else {
149
+ this.iconIds.set(id, iconId);
150
+ }
151
+ }
152
+
153
+ public initRuntime(runtime: UnisphereRuntimeBaseType<any>, integrations: UnisphereIntegrations, sidePanelConfig?: SidePanelContainerConfig): void {
154
+ if (!runtime?.widgetName || !runtime?.runtimeName || !integrations?.layouts) return;
155
+
156
+ this.addEventHandlers();
157
+
158
+ const layouts = integrations.layouts;
159
+
160
+ if (layouts && layouts.length > 0) {
161
+ const currentPreset = this.player.ui.store.getState().shell.activePresetName;
162
+
163
+ for (const layout of layouts) {
164
+ if (layout?.type === ContainerTypes.SIDEPANEL && layout?.presets?.includes(currentPreset)) {
165
+ const containerConfig: ContainerConfig = {
166
+ containerType: ContainerTypes.SIDEPANEL,
167
+ presets: layout.presets.filter(isReservedPresetNames),
168
+ unisphereArea: layout.sidepanel.visualType,
169
+ position: sidePanelConfig?.position,
170
+ expandMode: sidePanelConfig?.expandMode,
171
+ expandOnFirstPlay: sidePanelConfig?.expandOnFirstPlay,
172
+ layout
173
+ };
174
+
175
+ const containerId = this.containerManager.createContainer(containerConfig);
176
+ if (containerId) {
177
+ const storageKey = layout.sidepanel.visualType;
178
+ const panelData = UnisphereStorageService.get(storageKey) as any;
179
+ const panelId = panelData?.upperBarIcon?.panelId;
180
+
181
+ if (panelId && layout.header) {
182
+ this.addUpperBarIcon(getUniqueId(runtime.widgetName.split('.')[2]), layout.header.tooltip, 0, layout.header.icon, panelId, false);
183
+ }
184
+ this.mountRuntimeToContainer(runtime, layout, containerId);
185
+
186
+ // track panels that should expand on first play
187
+ if (sidePanelConfig?.expandOnFirstPlay && panelId) {
188
+ this.expandOnFirstPlayPanels.add(panelId);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+
196
+ private mountRuntimeToContainer(runtimeInstance: any, layout: any, containerId: string): void {
197
+ setTimeout(() => {
198
+ const targetElement = document.getElementById(containerId);
199
+ if (targetElement) {
200
+ runtimeInstance?.mountVisual({
201
+ type: layout.sidepanel.visualType,
202
+ settings: layout.sidepanel.visualSettings,
203
+ target: targetElement
204
+ });
205
+ }
206
+ }, 100);
207
+ }
208
+
209
+ public updateSessionData(): void {
210
+ UnisphereStorageService.update(UnisphereStorageKeys.PLAYER_SESSION, {
211
+ entryId: this.player.sources.id,
212
+ ks: this.player.config.session?.ks,
213
+ entryName: this.player.sources.metadata?.name
214
+ });
215
+ }
216
+
217
+ public reset(): void {
218
+ for (const panelId of this.panelIds) {
219
+ if (this.sidePanelsManager?.isItemActive(panelId)) {
220
+ this.sidePanelsManager.deactivateItem(panelId);
221
+ }
222
+ this.sidePanelsManager?.remove(panelId);
223
+ }
224
+ this.panelIds.clear();
225
+
226
+ for (const v of this.iconIds.values()) {
227
+ this.upperBarManager?.remove(v);
228
+ }
229
+ this.iconIds.clear();
230
+ this.containerManager.destroy();
231
+
232
+ // reset expandOnFirstPlay state
233
+ this.expandOnFirstPlayPanels.clear();
234
+ }
235
+
236
+ public destroy(): void {
237
+ this.reset();
238
+ this.containerManager.destroy();
239
+
240
+ for (const v of this.persistentIconIds.values()) {
241
+ this.upperBarManager?.remove(v);
242
+ }
243
+
244
+ UnisphereEventService.destroy();
245
+ UnisphereStorageService.destroy();
246
+ UnisphereWorkspaceService.destroy();
247
+
248
+ if (this.unsubscribeStore) {
249
+ this.unsubscribeStore();
250
+ }
251
+ }
252
+ }
253
+
254
+ export { UnispherePluginManager };
@@ -0,0 +1,45 @@
1
+ import { BasePlugin, KalturaPlayer } from '@playkit-js/kaltura-player-js';
2
+ import { UnispherePluginConfig } from './types';
3
+ import { UnispherePluginManager } from './unisphere-plugin-manager';
4
+ import { UnisphereEventService } from './services';
5
+
6
+ export const PLUGIN_NAME = 'unisphereService';
7
+
8
+ export class UnispherePlugin extends BasePlugin {
9
+ private _unispherePluginManager: UnispherePluginManager;
10
+ /**
11
+ * The default configuration of the plugin.
12
+ * @static
13
+ */
14
+ public static defaultConfig: UnispherePluginConfig = {
15
+ widgets: []
16
+ };
17
+
18
+ constructor(name: string, player: KalturaPlayer, config: UnispherePluginConfig) {
19
+ super(name, player, config);
20
+ this._unispherePluginManager = new UnispherePluginManager(player, this.eventManager, this.logger, this.config);
21
+ player.registerService(PLUGIN_NAME, {
22
+ pluginManager: this._unispherePluginManager,
23
+ eventService: UnisphereEventService
24
+ });
25
+ }
26
+
27
+ public reset(): void {
28
+ this._unispherePluginManager.reset();
29
+ }
30
+
31
+ public destroy(): void {
32
+ this.eventManager.destroy();
33
+ this._unispherePluginManager.reset();
34
+ UnisphereEventService.destroy();
35
+ }
36
+
37
+ /**
38
+ * @static
39
+ * @public
40
+ * @returns {boolean} - Whether the plugin is valid.
41
+ */
42
+ public static isValid(): boolean {
43
+ return true;
44
+ }
45
+ }