@therealtinhtute/baroiplayer 1.0.0
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/LICENSE +21 -0
- package/README.md +384 -0
- package/dist/context/media.js +2 -0
- package/dist/context/media.js.map +1 -0
- package/dist/context/media.mjs +15 -0
- package/dist/context/media.mjs.map +1 -0
- package/dist/context/store.js +2 -0
- package/dist/context/store.js.map +1 -0
- package/dist/context/store.mjs +5 -0
- package/dist/context/store.mjs.map +1 -0
- package/dist/context-BMteAJDN.js +1140 -0
- package/dist/context-BMteAJDN.js.map +1 -0
- package/dist/context-DCJ_scmO.cjs +23 -0
- package/dist/context-DCJ_scmO.cjs.map +1 -0
- package/dist/core-events-B5W_j8yU.js +480 -0
- package/dist/core-events-B5W_j8yU.js.map +1 -0
- package/dist/core-events-CN0-Vhhq.cjs +2 -0
- package/dist/core-events-CN0-Vhhq.cjs.map +1 -0
- package/dist/core-plugins-Bq9KLany.js +696 -0
- package/dist/core-plugins-Bq9KLany.js.map +1 -0
- package/dist/core-plugins-CPDSHMiQ.cjs +2 -0
- package/dist/core-plugins-CPDSHMiQ.cjs.map +1 -0
- package/dist/core.js +37 -0
- package/dist/core.js.map +1 -0
- package/dist/core.mjs +1217 -0
- package/dist/core.mjs.map +1 -0
- package/dist/enhanced-components-DHv0MFvG.js +1073 -0
- package/dist/enhanced-components-DHv0MFvG.js.map +1 -0
- package/dist/enhanced-components-DTIObgB7.cjs +2 -0
- package/dist/enhanced-components-DTIObgB7.cjs.map +1 -0
- package/dist/hooks-C_r8wexn.cjs +4 -0
- package/dist/hooks-C_r8wexn.cjs.map +1 -0
- package/dist/hooks-Da2wVXlF.js +1630 -0
- package/dist/hooks-Da2wVXlF.js.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6126 -0
- package/dist/index.mjs.map +1 -0
- package/dist/minimal-components-DSq5sQpy.js +2002 -0
- package/dist/minimal-components-DSq5sQpy.js.map +1 -0
- package/dist/minimal-components-DflWiihN.cjs +2 -0
- package/dist/minimal-components-DflWiihN.cjs.map +1 -0
- package/dist/minimal-components.css +1 -0
- package/dist/mona-loading.gif +0 -0
- package/dist/performance-monitor-CUW6f5ll.cjs +2 -0
- package/dist/performance-monitor-CUW6f5ll.cjs.map +1 -0
- package/dist/performance-monitor-rMW1RgV3.js +302 -0
- package/dist/performance-monitor-rMW1RgV3.js.map +1 -0
- package/dist/utils-BnscpYYd.js +2798 -0
- package/dist/utils-BnscpYYd.js.map +1 -0
- package/dist/utils-D_czS_0K.cjs +9 -0
- package/dist/utils-D_czS_0K.cjs.map +1 -0
- package/dist/utils.js +2 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +7 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +114 -0
- package/src/adapters/videojs-adapter.tsx +470 -0
- package/src/assets/mona-loading.gif +0 -0
- package/src/components/AnimateLoading.tsx +115 -0
- package/src/components/LoadingState.tsx +248 -0
- package/src/components/accessibility/keyboard-help-overlay.tsx +213 -0
- package/src/components/audio-player.tsx +85 -0
- package/src/components/basic/index.ts +37 -0
- package/src/components/enhanced/advanced-features.tsx +316 -0
- package/src/components/enhanced/debug-panel.tsx +422 -0
- package/src/components/enhanced/enhanced-controls.tsx +600 -0
- package/src/components/enhanced/enhanced-media-player.tsx +509 -0
- package/src/components/enhanced/error-handling.tsx +345 -0
- package/src/components/enhanced/index.ts +39 -0
- package/src/components/enhanced/media-player-enhanced.tsx +349 -0
- package/src/components/examples/basic-video-player.tsx +55 -0
- package/src/components/examples/custom-controls-example.tsx +63 -0
- package/src/components/examples/enhanced-controls-example.tsx +114 -0
- package/src/components/examples/enhanced-media-player-example.tsx +293 -0
- package/src/components/examples/loading-states-demo.tsx +142 -0
- package/src/components/examples/react-19-demo.tsx +381 -0
- package/src/components/examples/volume-orientation-demo.tsx +114 -0
- package/src/components/icon-demo.tsx +209 -0
- package/src/components/icons/index.tsx +166 -0
- package/src/components/icons/svg-icons/ArrowLeftIcon.tsx +18 -0
- package/src/components/icons/svg-icons/ArrowRightIcon.tsx +18 -0
- package/src/components/icons/svg-icons/AudioIcon.tsx +22 -0
- package/src/components/icons/svg-icons/BackwardIcon.tsx +12 -0
- package/src/components/icons/svg-icons/CameraIcon.tsx +19 -0
- package/src/components/icons/svg-icons/CheckIcon.tsx +19 -0
- package/src/components/icons/svg-icons/EnablePiPIcon.tsx +17 -0
- package/src/components/icons/svg-icons/ForwardIcon.tsx +12 -0
- package/src/components/icons/svg-icons/FullscreenEnterIcon.tsx +23 -0
- package/src/components/icons/svg-icons/FullscreenExitIcon.tsx +23 -0
- package/src/components/icons/svg-icons/LoadingIcon.tsx +32 -0
- package/src/components/icons/svg-icons/PauseIcon.tsx +23 -0
- package/src/components/icons/svg-icons/PlayIcon.tsx +21 -0
- package/src/components/icons/svg-icons/PlaybackSpeedIcon.tsx +23 -0
- package/src/components/icons/svg-icons/QualityIcon.tsx +21 -0
- package/src/components/icons/svg-icons/SettingsIcon.tsx +12 -0
- package/src/components/icons/svg-icons/SliderIcon.tsx +21 -0
- package/src/components/icons/svg-icons/SubtitleIcon.tsx +21 -0
- package/src/components/icons/svg-icons/SubtitleOffIcon.tsx +25 -0
- package/src/components/icons/svg-icons/UnPiPIcon.tsx +17 -0
- package/src/components/icons/svg-icons/VolumeMutedIcon.tsx +16 -0
- package/src/components/icons/svg-icons/VolumeOneIcon.tsx +22 -0
- package/src/components/icons/svg-icons/VolumeThreeIcon.tsx +16 -0
- package/src/components/icons/svg-icons/VolumeTwoIcon.tsx +16 -0
- package/src/components/legacy.tsx +93 -0
- package/src/components/media-metadata-display.tsx +100 -0
- package/src/components/media-player-audio.tsx +39 -0
- package/src/components/media-player-controls.tsx +226 -0
- package/src/components/media-player-error.tsx +163 -0
- package/src/components/media-player-fullscreen.tsx +109 -0
- package/src/components/media-player-hls.tsx +111 -0
- package/src/components/media-player-loading.tsx +76 -0
- package/src/components/media-player-play.tsx +75 -0
- package/src/components/media-player-root.tsx +340 -0
- package/src/components/media-player-seek.tsx +168 -0
- package/src/components/media-player-time.tsx +99 -0
- package/src/components/media-player-tooltip.tsx +58 -0
- package/src/components/media-player-video.tsx +39 -0
- package/src/components/media-player-volume.tsx +155 -0
- package/src/components/media-player.tsx +257 -0
- package/src/components/media-suspense.tsx +362 -0
- package/src/components/player-settings-form.tsx +163 -0
- package/src/components/react-player-wrapper.tsx +651 -0
- package/src/components/simple-video-player.tsx +60 -0
- package/src/components/ui/enhanced-seek-bar.tsx +235 -0
- package/src/components/ui/fullscreen-button.tsx +60 -0
- package/src/components/ui/media-resource-preloader.tsx +395 -0
- package/src/components/ui/play-button.tsx +107 -0
- package/src/components/ui/seek-bar.tsx +161 -0
- package/src/components/ui/time-display.tsx +40 -0
- package/src/components/ui/volume-control.tsx +143 -0
- package/src/components/unified/base-media-player.tsx +246 -0
- package/src/components/unified/media-player.tsx +75 -0
- package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +561 -0
- package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +552 -0
- package/src/components/variants/minimal/MinimalControls.tsx +142 -0
- package/src/components/variants/minimal/MinimalLoading.tsx +32 -0
- package/src/components/variants/minimal/MinimalMediaPlayer.tsx +315 -0
- package/src/components/variants/minimal/MinimalOverlay.tsx +64 -0
- package/src/components/variants/minimal/MinimalPlayButton.tsx +65 -0
- package/src/components/variants/minimal/MinimalSeekBar.tsx +150 -0
- package/src/components/variants/minimal/MinimalVolumeControl.tsx +80 -0
- package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +210 -0
- package/src/components/variants/minimal/components/CenterPlayButton.tsx +22 -0
- package/src/components/variants/minimal/components/ControlBar.tsx +146 -0
- package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +361 -0
- package/src/components/variants/minimal/components/PlaybackControls.tsx +51 -0
- package/src/components/variants/minimal/components/RightControls.tsx +173 -0
- package/src/components/variants/minimal/components/SeekBar.tsx +131 -0
- package/src/components/variants/minimal/components/SettingsDropdown.tsx +70 -0
- package/src/components/variants/minimal/components/SubtitleDisplay.tsx +21 -0
- package/src/components/variants/minimal/components/TimeDisplay.tsx +18 -0
- package/src/components/variants/minimal/components/VideoElement.tsx +40 -0
- package/src/components/variants/minimal/components/VolumeControl.tsx +217 -0
- package/src/components/variants/minimal/index.ts +23 -0
- package/src/components/video-player.tsx +100 -0
- package/src/context/error-handling.ts +394 -0
- package/src/context/media-context.tsx +208 -0
- package/src/context/media-provider.tsx +265 -0
- package/src/context/media-store.ts +379 -0
- package/src/context/persistence.ts +453 -0
- package/src/core/events/event-system.ts +442 -0
- package/src/core/events/hooks.ts +302 -0
- package/src/core/events/index.ts +22 -0
- package/src/core/events/middleware.ts +269 -0
- package/src/core/index.ts +22 -0
- package/src/core/optimization/bundle-optimizer.tsx +308 -0
- package/src/core/performance/cicd.ts +96 -0
- package/src/core/performance/index.ts +38 -0
- package/src/core/performance/media-loading-optimizer.ts +538 -0
- package/src/core/performance/performance-budgets.ts +432 -0
- package/src/core/performance/performance-monitor.tsx +476 -0
- package/src/core/performance/performance-testing.ts +459 -0
- package/src/core/plugins/base-plugin.ts +224 -0
- package/src/core/plugins/enhanced-plugin-system.ts +411 -0
- package/src/core/plugins/examples/analytics-plugin.ts +242 -0
- package/src/core/plugins/index.ts +39 -0
- package/src/core/plugins/registry.ts +327 -0
- package/src/core/plugins/types.ts +159 -0
- package/src/core/plugins/utils.ts +316 -0
- package/src/core/progressive-loading.tsx +322 -0
- package/src/core/providers/enhanced-hls-provider.ts +339 -0
- package/src/core/providers/service-provider.tsx +168 -0
- package/src/core/utils.tsx +38 -0
- package/src/features/media-session/media-session-manager.ts +429 -0
- package/src/features/picture-in-picture/pip-manager.ts +429 -0
- package/src/features/playlist/playlist-manager.ts +606 -0
- package/src/features/subtitles/subtitle-manager.ts +441 -0
- package/src/features/subtitles/subtitle-parser.ts +280 -0
- package/src/features/thumbnails/thumbnail-generator.ts +519 -0
- package/src/hooks/accessibility/index.ts +27 -0
- package/src/hooks/accessibility/use-accessibility-preferences.ts +188 -0
- package/src/hooks/accessibility/use-focus-management.ts +176 -0
- package/src/hooks/accessibility/use-keyboard-shortcuts.ts +144 -0
- package/src/hooks/accessibility/use-screen-reader.ts +182 -0
- package/src/hooks/core/index.ts +47 -0
- package/src/hooks/legacy.ts +27 -0
- package/src/hooks/use-enhanced-media-player.ts +468 -0
- package/src/hooks/use-intelligent-preloading.ts +358 -0
- package/src/hooks/use-keyboard-controls.ts +115 -0
- package/src/hooks/use-media-actions.ts +175 -0
- package/src/hooks/use-media-metadata.ts +89 -0
- package/src/hooks/use-media-optimistic.ts +270 -0
- package/src/hooks/use-media-player.ts +312 -0
- package/src/hooks/use-media-state.ts +190 -0
- package/src/hooks/use-memory-optimization.ts +358 -0
- package/src/hooks/use-optimistic-controls.ts +92 -0
- package/src/hooks/use-optimized-media.ts +263 -0
- package/src/hooks/use-touch-gestures.ts +417 -0
- package/src/hooks/useEnhancedSubtitles.ts +258 -0
- package/src/hooks/useKeyboardShortcuts.ts +74 -0
- package/src/hooks/useMediaControls.ts +52 -0
- package/src/hooks/useScreenshot.ts +57 -0
- package/src/hooks/useSubtitles.ts +91 -0
- package/src/hooks/useVideoPlayer.ts +232 -0
- package/src/icons/components/FullscreenIcon.tsx +37 -0
- package/src/icons/components/SettingsIcon.tsx +38 -0
- package/src/icons/components/animated/LoadingIcon.tsx +43 -0
- package/src/icons/components/animated/PlayPauseIcon.tsx +50 -0
- package/src/icons/components/media/PauseIcon.tsx +39 -0
- package/src/icons/components/media/PlayIcon.tsx +38 -0
- package/src/icons/components/volume/VolumeIcon.tsx +39 -0
- package/src/icons/components/volume/VolumeOffIcon.tsx +39 -0
- package/src/icons/index.ts +56 -0
- package/src/icons/provider.tsx +64 -0
- package/src/icons/types.ts +40 -0
- package/src/index-enhanced.ts +35 -0
- package/src/index-legacy.ts +326 -0
- package/src/index.css +102 -0
- package/src/index.ts +216 -0
- package/src/lib/deprecation.ts +51 -0
- package/src/lib/format-time.ts +37 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/globals.css +462 -0
- package/src/styles/minimal-player.css +589 -0
- package/src/styles/pixel-art.css +46 -0
- package/src/styles/videojs.css +100 -0
- package/src/test/setup.ts +79 -0
- package/src/types/events.ts +47 -0
- package/src/types/formats.ts +95 -0
- package/src/types/index.ts +190 -0
- package/src/types/videojs-plugins.d.ts +14 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Plugin System with Video.js-inspired hooks architecture
|
|
3
|
+
* Provides middleware-based extensibility while maintaining backward compatibility
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BasePlugin } from './base-plugin';
|
|
7
|
+
import { MediaPlayerInstance, MediaPlayerConfig } from '../../types';
|
|
8
|
+
|
|
9
|
+
// Plugin hooks interface inspired by Video.js architecture
|
|
10
|
+
export interface MediaPluginHooks {
|
|
11
|
+
/** Called before player setup, can modify configuration */
|
|
12
|
+
beforeSetup?: (element: HTMLElement, options: MediaPlayerConfig) => MediaPlayerConfig;
|
|
13
|
+
/** Called during player setup */
|
|
14
|
+
setup?: (player: MediaPlayerInstance) => MediaPlayerInstance | void;
|
|
15
|
+
/** Called when player is ready */
|
|
16
|
+
ready?: (player: MediaPlayerInstance) => void;
|
|
17
|
+
/** Called when media source is loaded */
|
|
18
|
+
sourceLoaded?: (player: MediaPlayerInstance, source: string) => void;
|
|
19
|
+
/** Called on playback state changes */
|
|
20
|
+
playbackChange?: (player: MediaPlayerInstance, isPlaying: boolean) => void;
|
|
21
|
+
/** Called on error events */
|
|
22
|
+
error?: (player: MediaPlayerInstance, error: MediaError) => MediaError | void;
|
|
23
|
+
/** Called before player disposal */
|
|
24
|
+
beforeDispose?: (player: MediaPlayerInstance) => void;
|
|
25
|
+
/** Called during quality changes */
|
|
26
|
+
qualityChange?: (player: MediaPlayerInstance, quality: number) => void;
|
|
27
|
+
/** Called during volume changes */
|
|
28
|
+
volumeChange?: (player: MediaPlayerInstance, volume: number) => void;
|
|
29
|
+
/** Called during fullscreen changes */
|
|
30
|
+
fullscreenChange?: (player: MediaPlayerInstance, isFullscreen: boolean) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Enhanced plugin with hooks support
|
|
34
|
+
export abstract class EnhancedMediaPlugin extends BasePlugin {
|
|
35
|
+
protected hooks: Partial<MediaPluginHooks> = {};
|
|
36
|
+
protected priority: number = 0;
|
|
37
|
+
protected middleware?: PluginMiddleware;
|
|
38
|
+
|
|
39
|
+
constructor(name: string, config: any = {}) {
|
|
40
|
+
super(name, config);
|
|
41
|
+
this.priority = config.priority || 0;
|
|
42
|
+
this.setupHooks();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Override to setup plugin-specific hooks
|
|
47
|
+
*/
|
|
48
|
+
protected setupHooks(): void {
|
|
49
|
+
// Override in subclasses
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Register a hook
|
|
54
|
+
*/
|
|
55
|
+
public registerHook<K extends keyof MediaPluginHooks>(
|
|
56
|
+
hookType: K,
|
|
57
|
+
handler: MediaPluginHooks[K]
|
|
58
|
+
): void {
|
|
59
|
+
this.hooks[hookType] = handler;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Execute a hook if it exists
|
|
64
|
+
*/
|
|
65
|
+
public executeHook<K extends keyof MediaPluginHooks>(
|
|
66
|
+
hookType: K,
|
|
67
|
+
...args: Parameters<NonNullable<MediaPluginHooks[K]>>
|
|
68
|
+
): ReturnType<NonNullable<MediaPluginHooks[K]>> | void {
|
|
69
|
+
const hook = this.hooks[hookType];
|
|
70
|
+
if (hook) {
|
|
71
|
+
return (hook as any)(...args);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get plugin priority
|
|
77
|
+
*/
|
|
78
|
+
public getPriority(): number {
|
|
79
|
+
return this.priority;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Set middleware for advanced plugin behavior
|
|
84
|
+
*/
|
|
85
|
+
public setMiddleware(middleware: PluginMiddleware): void {
|
|
86
|
+
this.middleware = middleware;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Middleware interface for plugin system
|
|
91
|
+
export interface PluginMiddleware {
|
|
92
|
+
name: string;
|
|
93
|
+
priority: number;
|
|
94
|
+
execute: (
|
|
95
|
+
context: PluginContext,
|
|
96
|
+
next: () => PluginContext | Promise<PluginContext>
|
|
97
|
+
) => PluginContext | Promise<PluginContext>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Plugin execution context
|
|
101
|
+
export interface PluginContext {
|
|
102
|
+
player: MediaPlayerInstance;
|
|
103
|
+
event: string;
|
|
104
|
+
data: any;
|
|
105
|
+
originalData: any;
|
|
106
|
+
modified: boolean;
|
|
107
|
+
stopped: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Enhanced Plugin Registry with hooks and middleware support
|
|
112
|
+
*/
|
|
113
|
+
export class EnhancedPluginRegistry {
|
|
114
|
+
private plugins: Map<string, EnhancedMediaPlugin> = new Map();
|
|
115
|
+
private middlewares: PluginMiddleware[] = [];
|
|
116
|
+
private hooks: Map<keyof MediaPluginHooks, Array<{ plugin: string; handler: Function }>> = new Map();
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Register a plugin with the system
|
|
120
|
+
*/
|
|
121
|
+
public register(plugin: EnhancedMediaPlugin): void {
|
|
122
|
+
this.plugins.set(plugin.getName(), plugin);
|
|
123
|
+
|
|
124
|
+
// Register plugin hooks
|
|
125
|
+
this.registerPluginHooks(plugin);
|
|
126
|
+
|
|
127
|
+
// Register plugin middleware if available
|
|
128
|
+
if (plugin.middleware) {
|
|
129
|
+
this.addMiddleware(plugin.middleware);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Unregister a plugin
|
|
135
|
+
*/
|
|
136
|
+
public unregister(pluginName: string): void {
|
|
137
|
+
const plugin = this.plugins.get(pluginName);
|
|
138
|
+
if (plugin) {
|
|
139
|
+
this.unregisterPluginHooks(plugin);
|
|
140
|
+
this.plugins.delete(pluginName);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get a registered plugin
|
|
146
|
+
*/
|
|
147
|
+
public getPlugin(name: string): EnhancedMediaPlugin | undefined {
|
|
148
|
+
return this.plugins.get(name);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get all registered plugins
|
|
153
|
+
*/
|
|
154
|
+
public getPlugins(): EnhancedMediaPlugin[] {
|
|
155
|
+
return Array.from(this.plugins.values());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get plugins sorted by priority
|
|
160
|
+
*/
|
|
161
|
+
public getPluginsByPriority(): EnhancedMediaPlugin[] {
|
|
162
|
+
return Array.from(this.plugins.values()).sort((a, b) => a.getPriority() - b.getPriority());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Execute a hook across all registered plugins
|
|
167
|
+
*/
|
|
168
|
+
public async executeHook<K extends keyof MediaPluginHooks>(
|
|
169
|
+
hookType: K,
|
|
170
|
+
...args: Parameters<NonNullable<MediaPluginHooks[K]>>
|
|
171
|
+
): Promise<any[]> {
|
|
172
|
+
const hookHandlers = this.hooks.get(hookType) || [];
|
|
173
|
+
const results: any[] = [];
|
|
174
|
+
|
|
175
|
+
for (const { plugin, handler } of hookHandlers) {
|
|
176
|
+
try {
|
|
177
|
+
const result = await (handler as any)(...args);
|
|
178
|
+
results.push({ plugin, result });
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error(`Error executing hook ${hookType} in plugin ${plugin}:`, error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return results;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Add middleware to the execution chain
|
|
189
|
+
*/
|
|
190
|
+
public addMiddleware(middleware: PluginMiddleware): void {
|
|
191
|
+
this.middlewares.push(middleware);
|
|
192
|
+
// Sort by priority
|
|
193
|
+
this.middlewares.sort((a, b) => a.priority - b.priority);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Remove middleware
|
|
198
|
+
*/
|
|
199
|
+
public removeMiddleware(name: string): void {
|
|
200
|
+
this.middlewares = this.middlewares.filter(m => m.name !== name);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Execute middleware chain
|
|
205
|
+
*/
|
|
206
|
+
public async executeMiddleware(context: PluginContext): Promise<PluginContext> {
|
|
207
|
+
let index = 0;
|
|
208
|
+
|
|
209
|
+
const next = (): Promise<PluginContext> => {
|
|
210
|
+
if (index >= this.middlewares.length || context.stopped) {
|
|
211
|
+
return Promise.resolve(context);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const middleware = this.middlewares[index++];
|
|
215
|
+
return Promise.resolve(middleware.execute(context, next));
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return next();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Register hooks from a plugin
|
|
223
|
+
*/
|
|
224
|
+
private registerPluginHooks(plugin: EnhancedMediaPlugin): void {
|
|
225
|
+
const hooks = (plugin as any).hooks || {};
|
|
226
|
+
|
|
227
|
+
Object.entries(hooks).forEach(([hookType, handler]) => {
|
|
228
|
+
if (handler) {
|
|
229
|
+
if (!this.hooks.has(hookType as keyof MediaPluginHooks)) {
|
|
230
|
+
this.hooks.set(hookType as keyof MediaPluginHooks, []);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.hooks.get(hookType as keyof MediaPluginHooks)!.push({
|
|
234
|
+
plugin: plugin.getName(),
|
|
235
|
+
handler,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Unregister hooks from a plugin
|
|
243
|
+
*/
|
|
244
|
+
private unregisterPluginHooks(plugin: EnhancedMediaPlugin): void {
|
|
245
|
+
const hooks = (plugin as any).hooks || {};
|
|
246
|
+
|
|
247
|
+
Object.keys(hooks).forEach((hookType) => {
|
|
248
|
+
const handlers = this.hooks.get(hookType as keyof MediaPluginHooks);
|
|
249
|
+
if (handlers) {
|
|
250
|
+
this.hooks.set(
|
|
251
|
+
hookType as keyof MediaPluginHooks,
|
|
252
|
+
handlers.filter(h => h.plugin !== plugin.getName())
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Clear all plugins and middleware
|
|
260
|
+
*/
|
|
261
|
+
public clear(): void {
|
|
262
|
+
this.plugins.clear();
|
|
263
|
+
this.middlewares = [];
|
|
264
|
+
this.hooks.clear();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Plugin Manager for enhanced plugin system integration
|
|
270
|
+
*/
|
|
271
|
+
export class EnhancedPluginManager {
|
|
272
|
+
private registry: EnhancedPluginRegistry;
|
|
273
|
+
private player: MediaPlayerInstance;
|
|
274
|
+
|
|
275
|
+
constructor(player: MediaPlayerInstance) {
|
|
276
|
+
this.player = player;
|
|
277
|
+
this.registry = new EnhancedPluginRegistry();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Setup plugin system for player
|
|
282
|
+
*/
|
|
283
|
+
public async setup(options: MediaPlayerConfig = {}): Promise<void> {
|
|
284
|
+
// Execute beforeSetup hooks
|
|
285
|
+
const setupResults = await this.registry.executeHook('beforeSetup', this.player.getElement(), options);
|
|
286
|
+
|
|
287
|
+
// Apply configuration modifications from hooks
|
|
288
|
+
const modifiedOptions = setupResults.reduce((acc, { result }) => {
|
|
289
|
+
return result || acc;
|
|
290
|
+
}, options);
|
|
291
|
+
|
|
292
|
+
// Execute setup hooks
|
|
293
|
+
await this.registry.executeHook('setup', this.player);
|
|
294
|
+
|
|
295
|
+
// Execute ready hooks
|
|
296
|
+
await this.registry.executeHook('ready', this.player);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Register a plugin
|
|
301
|
+
*/
|
|
302
|
+
public register(plugin: EnhancedMediaPlugin): void {
|
|
303
|
+
this.registry.register(plugin);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Unregister a plugin
|
|
308
|
+
*/
|
|
309
|
+
public unregister(pluginName: string): void {
|
|
310
|
+
this.registry.unregister(pluginName);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Execute hooks for specific events
|
|
315
|
+
*/
|
|
316
|
+
public async handleEvent(event: string, data: any): Promise<void> {
|
|
317
|
+
const context: PluginContext = {
|
|
318
|
+
player: this.player,
|
|
319
|
+
event,
|
|
320
|
+
data,
|
|
321
|
+
originalData: data,
|
|
322
|
+
modified: false,
|
|
323
|
+
stopped: false,
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Execute middleware chain
|
|
327
|
+
const processedContext = await this.registry.executeMiddleware(context);
|
|
328
|
+
|
|
329
|
+
// Execute relevant hooks based on event
|
|
330
|
+
switch (event) {
|
|
331
|
+
case 'sourceLoaded':
|
|
332
|
+
await this.registry.executeHook('sourceLoaded', this.player, data.source);
|
|
333
|
+
break;
|
|
334
|
+
case 'playbackChange':
|
|
335
|
+
await this.registry.executeHook('playbackChange', this.player, data.isPlaying);
|
|
336
|
+
break;
|
|
337
|
+
case 'error':
|
|
338
|
+
await this.registry.executeHook('error', this.player, data.error);
|
|
339
|
+
break;
|
|
340
|
+
case 'qualityChange':
|
|
341
|
+
await this.registry.executeHook('qualityChange', this.player, data.quality);
|
|
342
|
+
break;
|
|
343
|
+
case 'volumeChange':
|
|
344
|
+
await this.registry.executeHook('volumeChange', this.player, data.volume);
|
|
345
|
+
break;
|
|
346
|
+
case 'fullscreenChange':
|
|
347
|
+
await this.registry.executeHook('fullscreenChange', this.player, data.isFullscreen);
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Dispose plugin manager
|
|
354
|
+
*/
|
|
355
|
+
public async dispose(): Promise<void> {
|
|
356
|
+
// Execute beforeDispose hooks
|
|
357
|
+
await this.registry.executeHook('beforeDispose', this.player);
|
|
358
|
+
|
|
359
|
+
// Clear registry
|
|
360
|
+
this.registry.clear();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get plugin registry
|
|
365
|
+
*/
|
|
366
|
+
public getRegistry(): EnhancedPluginRegistry {
|
|
367
|
+
return this.registry;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Default middlewares
|
|
372
|
+
export const createPerformanceMiddleware = (): PluginMiddleware => ({
|
|
373
|
+
name: 'performance',
|
|
374
|
+
priority: 0,
|
|
375
|
+
execute: async (context, next) => {
|
|
376
|
+
const start = performance.now();
|
|
377
|
+
const result = await next();
|
|
378
|
+
const duration = performance.now() - start;
|
|
379
|
+
|
|
380
|
+
if (duration > 16) { // Log slow operations
|
|
381
|
+
console.warn(`Slow plugin operation: ${context.event} took ${duration.toFixed(2)}ms`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return result;
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
export const createErrorHandlingMiddleware = (): PluginMiddleware => ({
|
|
389
|
+
name: 'errorHandling',
|
|
390
|
+
priority: -1000, // Run last
|
|
391
|
+
execute: async (context, next) => {
|
|
392
|
+
try {
|
|
393
|
+
return await next();
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.error(`Plugin error in ${context.event}:`, error);
|
|
396
|
+
context.stopped = true;
|
|
397
|
+
return context;
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
export const createLoggingMiddleware = (): PluginMiddleware => ({
|
|
403
|
+
name: 'logging',
|
|
404
|
+
priority: 1000, // Run first
|
|
405
|
+
execute: async (context, next) => {
|
|
406
|
+
console.debug(`Plugin event: ${context.event}`, context.data);
|
|
407
|
+
return next();
|
|
408
|
+
},
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
export default EnhancedPluginManager;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { BasePlugin, type AnalyticsPluginConfig } from '../base-plugin'
|
|
2
|
+
|
|
3
|
+
interface AnalyticsEvent {
|
|
4
|
+
event: string
|
|
5
|
+
timestamp: number
|
|
6
|
+
data?: any
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface AnalyticsConfig extends AnalyticsPluginConfig {
|
|
10
|
+
trackingId: string
|
|
11
|
+
events: string[]
|
|
12
|
+
samplingRate: number
|
|
13
|
+
debug: boolean
|
|
14
|
+
endpoint?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class AnalyticsPlugin extends BasePlugin {
|
|
18
|
+
private events: AnalyticsEvent[] = []
|
|
19
|
+
private analyticsConfig!: AnalyticsConfig
|
|
20
|
+
private cleanupFunctions: Array<() => void> = []
|
|
21
|
+
private reportTimer?: NodeJS.Timeout
|
|
22
|
+
|
|
23
|
+
constructor(config?: AnalyticsPluginConfig) {
|
|
24
|
+
super({
|
|
25
|
+
name: 'analytics',
|
|
26
|
+
description: 'Media player analytics and tracking',
|
|
27
|
+
category: 'analytics',
|
|
28
|
+
priority: 'low',
|
|
29
|
+
author: 'BaroiPlayer Team'
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected onInitialize(): void {
|
|
34
|
+
this.analyticsConfig = this.createConfig<AnalyticsConfig>({
|
|
35
|
+
trackingId: '',
|
|
36
|
+
events: ['play', 'pause', 'seek', 'ended', 'error'],
|
|
37
|
+
samplingRate: 1.0,
|
|
38
|
+
debug: false,
|
|
39
|
+
endpoint: '/api/analytics'
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (!this.analyticsConfig.trackingId) {
|
|
43
|
+
throw new Error('Analytics plugin requires a tracking ID')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.logger?.info('Analytics plugin initialized', {
|
|
47
|
+
trackingId: this.analyticsConfig.trackingId,
|
|
48
|
+
events: this.analyticsConfig.events
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected onActivate(): void {
|
|
53
|
+
// Set up event listeners for media events
|
|
54
|
+
const mediaEventCleanup = this.setupMediaEventListeners()
|
|
55
|
+
this.cleanupFunctions.push(mediaEventCleanup)
|
|
56
|
+
|
|
57
|
+
// Set up periodic reporting
|
|
58
|
+
if (this.analyticsConfig.endpoint) {
|
|
59
|
+
this.reportTimer = setInterval(() => {
|
|
60
|
+
this.reportEvents()
|
|
61
|
+
}, 30000) // Report every 30 seconds
|
|
62
|
+
|
|
63
|
+
this.cleanupFunctions.push(() => {
|
|
64
|
+
if (this.reportTimer) {
|
|
65
|
+
clearInterval(this.reportTimer)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.trackEvent('plugin_activated', { plugin: 'analytics' })
|
|
71
|
+
this.logger?.info('Analytics tracking started')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
protected onDeactivate(): void {
|
|
75
|
+
// Clean up all event listeners and timers
|
|
76
|
+
this.cleanupFunctions.forEach(cleanup => cleanup())
|
|
77
|
+
this.cleanupFunctions = []
|
|
78
|
+
|
|
79
|
+
// Report any remaining events
|
|
80
|
+
if (this.events.length > 0) {
|
|
81
|
+
this.reportEvents()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.trackEvent('plugin_deactivated', { plugin: 'analytics' })
|
|
85
|
+
this.logger?.info('Analytics tracking stopped')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected onConfigure(newConfig: AnalyticsConfig): void {
|
|
89
|
+
// Update tracking configuration
|
|
90
|
+
this.analyticsConfig = { ...this.analyticsConfig, ...newConfig }
|
|
91
|
+
if (newConfig.events) {
|
|
92
|
+
// Re-setup event listeners if events changed
|
|
93
|
+
this.cleanupFunctions.forEach(cleanup => cleanup())
|
|
94
|
+
this.cleanupFunctions = []
|
|
95
|
+
|
|
96
|
+
const mediaEventCleanup = this.setupMediaEventListeners()
|
|
97
|
+
this.cleanupFunctions.push(mediaEventCleanup)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private setupMediaEventListeners(): () => void {
|
|
102
|
+
const cleanupFunctions: Array<() => void> = []
|
|
103
|
+
|
|
104
|
+
// Listen for media state changes
|
|
105
|
+
if (this.analyticsConfig.events.includes('play')) {
|
|
106
|
+
const unsubscribe = this.on('media:play', (data) => {
|
|
107
|
+
this.trackEvent('play', data)
|
|
108
|
+
})
|
|
109
|
+
cleanupFunctions.push(unsubscribe)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (this.analyticsConfig.events.includes('pause')) {
|
|
113
|
+
const unsubscribe = this.on('media:pause', (data) => {
|
|
114
|
+
this.trackEvent('pause', data)
|
|
115
|
+
})
|
|
116
|
+
cleanupFunctions.push(unsubscribe)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (this.analyticsConfig.events.includes('seek')) {
|
|
120
|
+
const unsubscribe = this.on('media:seek', (data) => {
|
|
121
|
+
this.trackEvent('seek', data)
|
|
122
|
+
})
|
|
123
|
+
cleanupFunctions.push(unsubscribe)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.analyticsConfig.events.includes('ended')) {
|
|
127
|
+
const unsubscribe = this.on('media:ended', (data) => {
|
|
128
|
+
this.trackEvent('ended', data)
|
|
129
|
+
})
|
|
130
|
+
cleanupFunctions.push(unsubscribe)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (this.analyticsConfig.events.includes('error')) {
|
|
134
|
+
const unsubscribe = this.on('media:error', (data) => {
|
|
135
|
+
this.trackEvent('error', data)
|
|
136
|
+
})
|
|
137
|
+
cleanupFunctions.push(unsubscribe)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Return cleanup function
|
|
141
|
+
return () => {
|
|
142
|
+
cleanupFunctions.forEach(cleanup => cleanup())
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private trackEvent(event: string, data?: any): void {
|
|
147
|
+
// Apply sampling rate
|
|
148
|
+
if (Math.random() > this.analyticsConfig.samplingRate) {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const analyticsEvent: AnalyticsEvent = {
|
|
153
|
+
event,
|
|
154
|
+
timestamp: Date.now(),
|
|
155
|
+
data: {
|
|
156
|
+
...data,
|
|
157
|
+
trackingId: this.analyticsConfig.trackingId,
|
|
158
|
+
sessionId: this.generateSessionId(),
|
|
159
|
+
mediaState: {
|
|
160
|
+
currentTime: this.state?.currentTime,
|
|
161
|
+
duration: this.state?.duration,
|
|
162
|
+
volume: this.state?.volume,
|
|
163
|
+
isPlaying: this.state?.isPlaying
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.events.push(analyticsEvent)
|
|
169
|
+
|
|
170
|
+
if (this.analyticsConfig.debug) {
|
|
171
|
+
this.logger?.info('Analytics event tracked', analyticsEvent)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Emit event for other plugins or applications to use
|
|
175
|
+
this.emit('analytics:event', analyticsEvent)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private async reportEvents(): Promise<void> {
|
|
179
|
+
if (this.events.length === 0 || !this.analyticsConfig.endpoint) {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const eventsToReport = [...this.events]
|
|
184
|
+
this.events = []
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const response = await fetch(this.analyticsConfig.endpoint, {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
headers: {
|
|
190
|
+
'Content-Type': 'application/json'
|
|
191
|
+
},
|
|
192
|
+
body: JSON.stringify({
|
|
193
|
+
trackingId: this.analyticsConfig.trackingId,
|
|
194
|
+
events: eventsToReport
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
if (!response.ok) {
|
|
199
|
+
throw new Error(`Analytics report failed: ${response.status}`)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (this.analyticsConfig.debug) {
|
|
203
|
+
this.logger?.info(`Reported ${eventsToReport.length} analytics events`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
} catch (error) {
|
|
207
|
+
// Put events back in queue on failure
|
|
208
|
+
this.events.unshift(...eventsToReport)
|
|
209
|
+
this.logger?.error('Failed to report analytics events', error)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private generateSessionId(): string {
|
|
214
|
+
// Simple session ID generation - could be more sophisticated
|
|
215
|
+
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Public API for manual event tracking
|
|
219
|
+
public track(event: string, data?: any): void {
|
|
220
|
+
this.trackEvent(event, data)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Get pending events count
|
|
224
|
+
public getPendingEventsCount(): number {
|
|
225
|
+
return this.events.length
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Flush all pending events
|
|
229
|
+
public async flush(): Promise<void> {
|
|
230
|
+
await this.reportEvents()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
protected onHealthCheck(): boolean {
|
|
234
|
+
// Check if we're not accumulating too many events
|
|
235
|
+
return this.events.length < 1000
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Plugin factory
|
|
240
|
+
export const createAnalyticsPlugin = (config?: AnalyticsPluginConfig) => {
|
|
241
|
+
return new AnalyticsPlugin(config)
|
|
242
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Plugin system core exports
|
|
2
|
+
export type {
|
|
3
|
+
Plugin,
|
|
4
|
+
PluginFactory,
|
|
5
|
+
PluginConfig,
|
|
6
|
+
PluginRegistry as IPluginRegistry,
|
|
7
|
+
PluginContext,
|
|
8
|
+
PluginLogger,
|
|
9
|
+
PluginMetadata,
|
|
10
|
+
PluginCategory,
|
|
11
|
+
PluginPriority,
|
|
12
|
+
PluginPhase,
|
|
13
|
+
PluginEvents
|
|
14
|
+
} from './types'
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
PluginError,
|
|
18
|
+
PluginDependencyError,
|
|
19
|
+
PluginConflictError
|
|
20
|
+
} from './types'
|
|
21
|
+
|
|
22
|
+
export { PluginRegistry } from './registry'
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
BasePlugin,
|
|
26
|
+
createPlugin,
|
|
27
|
+
type UIPluginConfig,
|
|
28
|
+
type AnalyticsPluginConfig,
|
|
29
|
+
type PerformancePluginConfig
|
|
30
|
+
} from './base-plugin'
|
|
31
|
+
|
|
32
|
+
// Example plugins
|
|
33
|
+
export { AnalyticsPlugin, createAnalyticsPlugin } from './examples/analytics-plugin'
|
|
34
|
+
|
|
35
|
+
// Plugin utilities
|
|
36
|
+
export * from './utils'
|
|
37
|
+
|
|
38
|
+
// Version
|
|
39
|
+
export const PLUGIN_SYSTEM_VERSION = '1.0.0'
|