@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.
Files changed (244) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +384 -0
  3. package/dist/context/media.js +2 -0
  4. package/dist/context/media.js.map +1 -0
  5. package/dist/context/media.mjs +15 -0
  6. package/dist/context/media.mjs.map +1 -0
  7. package/dist/context/store.js +2 -0
  8. package/dist/context/store.js.map +1 -0
  9. package/dist/context/store.mjs +5 -0
  10. package/dist/context/store.mjs.map +1 -0
  11. package/dist/context-BMteAJDN.js +1140 -0
  12. package/dist/context-BMteAJDN.js.map +1 -0
  13. package/dist/context-DCJ_scmO.cjs +23 -0
  14. package/dist/context-DCJ_scmO.cjs.map +1 -0
  15. package/dist/core-events-B5W_j8yU.js +480 -0
  16. package/dist/core-events-B5W_j8yU.js.map +1 -0
  17. package/dist/core-events-CN0-Vhhq.cjs +2 -0
  18. package/dist/core-events-CN0-Vhhq.cjs.map +1 -0
  19. package/dist/core-plugins-Bq9KLany.js +696 -0
  20. package/dist/core-plugins-Bq9KLany.js.map +1 -0
  21. package/dist/core-plugins-CPDSHMiQ.cjs +2 -0
  22. package/dist/core-plugins-CPDSHMiQ.cjs.map +1 -0
  23. package/dist/core.js +37 -0
  24. package/dist/core.js.map +1 -0
  25. package/dist/core.mjs +1217 -0
  26. package/dist/core.mjs.map +1 -0
  27. package/dist/enhanced-components-DHv0MFvG.js +1073 -0
  28. package/dist/enhanced-components-DHv0MFvG.js.map +1 -0
  29. package/dist/enhanced-components-DTIObgB7.cjs +2 -0
  30. package/dist/enhanced-components-DTIObgB7.cjs.map +1 -0
  31. package/dist/hooks-C_r8wexn.cjs +4 -0
  32. package/dist/hooks-C_r8wexn.cjs.map +1 -0
  33. package/dist/hooks-Da2wVXlF.js +1630 -0
  34. package/dist/hooks-Da2wVXlF.js.map +1 -0
  35. package/dist/index.css +1 -0
  36. package/dist/index.js +112 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/index.mjs +6126 -0
  39. package/dist/index.mjs.map +1 -0
  40. package/dist/minimal-components-DSq5sQpy.js +2002 -0
  41. package/dist/minimal-components-DSq5sQpy.js.map +1 -0
  42. package/dist/minimal-components-DflWiihN.cjs +2 -0
  43. package/dist/minimal-components-DflWiihN.cjs.map +1 -0
  44. package/dist/minimal-components.css +1 -0
  45. package/dist/mona-loading.gif +0 -0
  46. package/dist/performance-monitor-CUW6f5ll.cjs +2 -0
  47. package/dist/performance-monitor-CUW6f5ll.cjs.map +1 -0
  48. package/dist/performance-monitor-rMW1RgV3.js +302 -0
  49. package/dist/performance-monitor-rMW1RgV3.js.map +1 -0
  50. package/dist/utils-BnscpYYd.js +2798 -0
  51. package/dist/utils-BnscpYYd.js.map +1 -0
  52. package/dist/utils-D_czS_0K.cjs +9 -0
  53. package/dist/utils-D_czS_0K.cjs.map +1 -0
  54. package/dist/utils.js +2 -0
  55. package/dist/utils.js.map +1 -0
  56. package/dist/utils.mjs +7 -0
  57. package/dist/utils.mjs.map +1 -0
  58. package/package.json +114 -0
  59. package/src/adapters/videojs-adapter.tsx +470 -0
  60. package/src/assets/mona-loading.gif +0 -0
  61. package/src/components/AnimateLoading.tsx +115 -0
  62. package/src/components/LoadingState.tsx +248 -0
  63. package/src/components/accessibility/keyboard-help-overlay.tsx +213 -0
  64. package/src/components/audio-player.tsx +85 -0
  65. package/src/components/basic/index.ts +37 -0
  66. package/src/components/enhanced/advanced-features.tsx +316 -0
  67. package/src/components/enhanced/debug-panel.tsx +422 -0
  68. package/src/components/enhanced/enhanced-controls.tsx +600 -0
  69. package/src/components/enhanced/enhanced-media-player.tsx +509 -0
  70. package/src/components/enhanced/error-handling.tsx +345 -0
  71. package/src/components/enhanced/index.ts +39 -0
  72. package/src/components/enhanced/media-player-enhanced.tsx +349 -0
  73. package/src/components/examples/basic-video-player.tsx +55 -0
  74. package/src/components/examples/custom-controls-example.tsx +63 -0
  75. package/src/components/examples/enhanced-controls-example.tsx +114 -0
  76. package/src/components/examples/enhanced-media-player-example.tsx +293 -0
  77. package/src/components/examples/loading-states-demo.tsx +142 -0
  78. package/src/components/examples/react-19-demo.tsx +381 -0
  79. package/src/components/examples/volume-orientation-demo.tsx +114 -0
  80. package/src/components/icon-demo.tsx +209 -0
  81. package/src/components/icons/index.tsx +166 -0
  82. package/src/components/icons/svg-icons/ArrowLeftIcon.tsx +18 -0
  83. package/src/components/icons/svg-icons/ArrowRightIcon.tsx +18 -0
  84. package/src/components/icons/svg-icons/AudioIcon.tsx +22 -0
  85. package/src/components/icons/svg-icons/BackwardIcon.tsx +12 -0
  86. package/src/components/icons/svg-icons/CameraIcon.tsx +19 -0
  87. package/src/components/icons/svg-icons/CheckIcon.tsx +19 -0
  88. package/src/components/icons/svg-icons/EnablePiPIcon.tsx +17 -0
  89. package/src/components/icons/svg-icons/ForwardIcon.tsx +12 -0
  90. package/src/components/icons/svg-icons/FullscreenEnterIcon.tsx +23 -0
  91. package/src/components/icons/svg-icons/FullscreenExitIcon.tsx +23 -0
  92. package/src/components/icons/svg-icons/LoadingIcon.tsx +32 -0
  93. package/src/components/icons/svg-icons/PauseIcon.tsx +23 -0
  94. package/src/components/icons/svg-icons/PlayIcon.tsx +21 -0
  95. package/src/components/icons/svg-icons/PlaybackSpeedIcon.tsx +23 -0
  96. package/src/components/icons/svg-icons/QualityIcon.tsx +21 -0
  97. package/src/components/icons/svg-icons/SettingsIcon.tsx +12 -0
  98. package/src/components/icons/svg-icons/SliderIcon.tsx +21 -0
  99. package/src/components/icons/svg-icons/SubtitleIcon.tsx +21 -0
  100. package/src/components/icons/svg-icons/SubtitleOffIcon.tsx +25 -0
  101. package/src/components/icons/svg-icons/UnPiPIcon.tsx +17 -0
  102. package/src/components/icons/svg-icons/VolumeMutedIcon.tsx +16 -0
  103. package/src/components/icons/svg-icons/VolumeOneIcon.tsx +22 -0
  104. package/src/components/icons/svg-icons/VolumeThreeIcon.tsx +16 -0
  105. package/src/components/icons/svg-icons/VolumeTwoIcon.tsx +16 -0
  106. package/src/components/legacy.tsx +93 -0
  107. package/src/components/media-metadata-display.tsx +100 -0
  108. package/src/components/media-player-audio.tsx +39 -0
  109. package/src/components/media-player-controls.tsx +226 -0
  110. package/src/components/media-player-error.tsx +163 -0
  111. package/src/components/media-player-fullscreen.tsx +109 -0
  112. package/src/components/media-player-hls.tsx +111 -0
  113. package/src/components/media-player-loading.tsx +76 -0
  114. package/src/components/media-player-play.tsx +75 -0
  115. package/src/components/media-player-root.tsx +340 -0
  116. package/src/components/media-player-seek.tsx +168 -0
  117. package/src/components/media-player-time.tsx +99 -0
  118. package/src/components/media-player-tooltip.tsx +58 -0
  119. package/src/components/media-player-video.tsx +39 -0
  120. package/src/components/media-player-volume.tsx +155 -0
  121. package/src/components/media-player.tsx +257 -0
  122. package/src/components/media-suspense.tsx +362 -0
  123. package/src/components/player-settings-form.tsx +163 -0
  124. package/src/components/react-player-wrapper.tsx +651 -0
  125. package/src/components/simple-video-player.tsx +60 -0
  126. package/src/components/ui/enhanced-seek-bar.tsx +235 -0
  127. package/src/components/ui/fullscreen-button.tsx +60 -0
  128. package/src/components/ui/media-resource-preloader.tsx +395 -0
  129. package/src/components/ui/play-button.tsx +107 -0
  130. package/src/components/ui/seek-bar.tsx +161 -0
  131. package/src/components/ui/time-display.tsx +40 -0
  132. package/src/components/ui/volume-control.tsx +143 -0
  133. package/src/components/unified/base-media-player.tsx +246 -0
  134. package/src/components/unified/media-player.tsx +75 -0
  135. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +561 -0
  136. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +552 -0
  137. package/src/components/variants/minimal/MinimalControls.tsx +142 -0
  138. package/src/components/variants/minimal/MinimalLoading.tsx +32 -0
  139. package/src/components/variants/minimal/MinimalMediaPlayer.tsx +315 -0
  140. package/src/components/variants/minimal/MinimalOverlay.tsx +64 -0
  141. package/src/components/variants/minimal/MinimalPlayButton.tsx +65 -0
  142. package/src/components/variants/minimal/MinimalSeekBar.tsx +150 -0
  143. package/src/components/variants/minimal/MinimalVolumeControl.tsx +80 -0
  144. package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +210 -0
  145. package/src/components/variants/minimal/components/CenterPlayButton.tsx +22 -0
  146. package/src/components/variants/minimal/components/ControlBar.tsx +146 -0
  147. package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +361 -0
  148. package/src/components/variants/minimal/components/PlaybackControls.tsx +51 -0
  149. package/src/components/variants/minimal/components/RightControls.tsx +173 -0
  150. package/src/components/variants/minimal/components/SeekBar.tsx +131 -0
  151. package/src/components/variants/minimal/components/SettingsDropdown.tsx +70 -0
  152. package/src/components/variants/minimal/components/SubtitleDisplay.tsx +21 -0
  153. package/src/components/variants/minimal/components/TimeDisplay.tsx +18 -0
  154. package/src/components/variants/minimal/components/VideoElement.tsx +40 -0
  155. package/src/components/variants/minimal/components/VolumeControl.tsx +217 -0
  156. package/src/components/variants/minimal/index.ts +23 -0
  157. package/src/components/video-player.tsx +100 -0
  158. package/src/context/error-handling.ts +394 -0
  159. package/src/context/media-context.tsx +208 -0
  160. package/src/context/media-provider.tsx +265 -0
  161. package/src/context/media-store.ts +379 -0
  162. package/src/context/persistence.ts +453 -0
  163. package/src/core/events/event-system.ts +442 -0
  164. package/src/core/events/hooks.ts +302 -0
  165. package/src/core/events/index.ts +22 -0
  166. package/src/core/events/middleware.ts +269 -0
  167. package/src/core/index.ts +22 -0
  168. package/src/core/optimization/bundle-optimizer.tsx +308 -0
  169. package/src/core/performance/cicd.ts +96 -0
  170. package/src/core/performance/index.ts +38 -0
  171. package/src/core/performance/media-loading-optimizer.ts +538 -0
  172. package/src/core/performance/performance-budgets.ts +432 -0
  173. package/src/core/performance/performance-monitor.tsx +476 -0
  174. package/src/core/performance/performance-testing.ts +459 -0
  175. package/src/core/plugins/base-plugin.ts +224 -0
  176. package/src/core/plugins/enhanced-plugin-system.ts +411 -0
  177. package/src/core/plugins/examples/analytics-plugin.ts +242 -0
  178. package/src/core/plugins/index.ts +39 -0
  179. package/src/core/plugins/registry.ts +327 -0
  180. package/src/core/plugins/types.ts +159 -0
  181. package/src/core/plugins/utils.ts +316 -0
  182. package/src/core/progressive-loading.tsx +322 -0
  183. package/src/core/providers/enhanced-hls-provider.ts +339 -0
  184. package/src/core/providers/service-provider.tsx +168 -0
  185. package/src/core/utils.tsx +38 -0
  186. package/src/features/media-session/media-session-manager.ts +429 -0
  187. package/src/features/picture-in-picture/pip-manager.ts +429 -0
  188. package/src/features/playlist/playlist-manager.ts +606 -0
  189. package/src/features/subtitles/subtitle-manager.ts +441 -0
  190. package/src/features/subtitles/subtitle-parser.ts +280 -0
  191. package/src/features/thumbnails/thumbnail-generator.ts +519 -0
  192. package/src/hooks/accessibility/index.ts +27 -0
  193. package/src/hooks/accessibility/use-accessibility-preferences.ts +188 -0
  194. package/src/hooks/accessibility/use-focus-management.ts +176 -0
  195. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +144 -0
  196. package/src/hooks/accessibility/use-screen-reader.ts +182 -0
  197. package/src/hooks/core/index.ts +47 -0
  198. package/src/hooks/legacy.ts +27 -0
  199. package/src/hooks/use-enhanced-media-player.ts +468 -0
  200. package/src/hooks/use-intelligent-preloading.ts +358 -0
  201. package/src/hooks/use-keyboard-controls.ts +115 -0
  202. package/src/hooks/use-media-actions.ts +175 -0
  203. package/src/hooks/use-media-metadata.ts +89 -0
  204. package/src/hooks/use-media-optimistic.ts +270 -0
  205. package/src/hooks/use-media-player.ts +312 -0
  206. package/src/hooks/use-media-state.ts +190 -0
  207. package/src/hooks/use-memory-optimization.ts +358 -0
  208. package/src/hooks/use-optimistic-controls.ts +92 -0
  209. package/src/hooks/use-optimized-media.ts +263 -0
  210. package/src/hooks/use-touch-gestures.ts +417 -0
  211. package/src/hooks/useEnhancedSubtitles.ts +258 -0
  212. package/src/hooks/useKeyboardShortcuts.ts +74 -0
  213. package/src/hooks/useMediaControls.ts +52 -0
  214. package/src/hooks/useScreenshot.ts +57 -0
  215. package/src/hooks/useSubtitles.ts +91 -0
  216. package/src/hooks/useVideoPlayer.ts +232 -0
  217. package/src/icons/components/FullscreenIcon.tsx +37 -0
  218. package/src/icons/components/SettingsIcon.tsx +38 -0
  219. package/src/icons/components/animated/LoadingIcon.tsx +43 -0
  220. package/src/icons/components/animated/PlayPauseIcon.tsx +50 -0
  221. package/src/icons/components/media/PauseIcon.tsx +39 -0
  222. package/src/icons/components/media/PlayIcon.tsx +38 -0
  223. package/src/icons/components/volume/VolumeIcon.tsx +39 -0
  224. package/src/icons/components/volume/VolumeOffIcon.tsx +39 -0
  225. package/src/icons/index.ts +56 -0
  226. package/src/icons/provider.tsx +64 -0
  227. package/src/icons/types.ts +40 -0
  228. package/src/index-enhanced.ts +35 -0
  229. package/src/index-legacy.ts +326 -0
  230. package/src/index.css +102 -0
  231. package/src/index.ts +216 -0
  232. package/src/lib/deprecation.ts +51 -0
  233. package/src/lib/format-time.ts +37 -0
  234. package/src/lib/index.ts +3 -0
  235. package/src/lib/utils.ts +6 -0
  236. package/src/styles/globals.css +462 -0
  237. package/src/styles/minimal-player.css +589 -0
  238. package/src/styles/pixel-art.css +46 -0
  239. package/src/styles/videojs.css +100 -0
  240. package/src/test/setup.ts +79 -0
  241. package/src/types/events.ts +47 -0
  242. package/src/types/formats.ts +95 -0
  243. package/src/types/index.ts +190 -0
  244. package/src/types/videojs-plugins.d.ts +14 -0
@@ -0,0 +1,429 @@
1
+ export interface PiPCapabilities {
2
+ supported: boolean
3
+ documentPiP?: boolean
4
+ videoPiP?: boolean
5
+ }
6
+
7
+ export interface PiPOptions {
8
+ width?: number
9
+ height?: number
10
+ disallowReturnToOpener?: boolean
11
+ }
12
+
13
+ export interface PiPManager {
14
+ isSupported: () => boolean
15
+ isActive: () => boolean
16
+ enter: (element?: HTMLVideoElement, options?: PiPOptions) => Promise<void>
17
+ exit: () => Promise<void>
18
+ toggle: (element?: HTMLVideoElement, options?: PiPOptions) => Promise<void>
19
+ addEventListener: (event: string, callback: Function) => () => void
20
+ getCapabilities: () => PiPCapabilities
21
+ }
22
+
23
+ export class EnhancedPiPManager implements PiPManager {
24
+ private videoElement: HTMLVideoElement | null = null
25
+ private pipWindow: Window | null = null
26
+ private eventListeners: Map<string, Set<Function>> = new Map()
27
+ private isCurrentlyActive: boolean = false
28
+
29
+ constructor() {
30
+ this.setupEventListeners()
31
+ }
32
+
33
+ /**
34
+ * Check if Picture-in-Picture is supported
35
+ */
36
+ isSupported(): boolean {
37
+ const capabilities = this.getCapabilities()
38
+ return capabilities.supported
39
+ }
40
+
41
+ /**
42
+ * Get detailed PiP capabilities
43
+ */
44
+ getCapabilities(): PiPCapabilities {
45
+ const capabilities: PiPCapabilities = {
46
+ supported: false,
47
+ documentPiP: false,
48
+ videoPiP: false
49
+ }
50
+
51
+ // Check for video Picture-in-Picture support
52
+ if ('pictureInPictureEnabled' in document && document.pictureInPictureEnabled) {
53
+ capabilities.videoPiP = true
54
+ capabilities.supported = true
55
+ }
56
+
57
+ // Check for Document Picture-in-Picture support (Chrome 111+)
58
+ if ('documentPictureInPicture' in window) {
59
+ capabilities.documentPiP = true
60
+ capabilities.supported = true
61
+ }
62
+
63
+ return capabilities
64
+ }
65
+
66
+ /**
67
+ * Check if Picture-in-Picture is currently active
68
+ */
69
+ isActive(): boolean {
70
+ return this.isCurrentlyActive
71
+ }
72
+
73
+ /**
74
+ * Enter Picture-in-Picture mode
75
+ */
76
+ async enter(element?: HTMLVideoElement, options: PiPOptions = {}): Promise<void> {
77
+ if (!this.isSupported()) {
78
+ throw new Error('Picture-in-Picture is not supported')
79
+ }
80
+
81
+ if (this.isCurrentlyActive) {
82
+ return
83
+ }
84
+
85
+ const capabilities = this.getCapabilities()
86
+
87
+ // Try video PiP first if we have a video element
88
+ if (element && capabilities.videoPiP) {
89
+ await this.enterVideoPiP(element, options)
90
+ }
91
+ // Fallback to document PiP if available
92
+ else if (capabilities.documentPiP) {
93
+ await this.enterDocumentPiP(options)
94
+ }
95
+ else {
96
+ throw new Error('No suitable Picture-in-Picture method available')
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Exit Picture-in-Picture mode
102
+ */
103
+ async exit(): Promise<void> {
104
+ if (!this.isCurrentlyActive) {
105
+ return
106
+ }
107
+
108
+ try {
109
+ if (document.pictureInPictureElement) {
110
+ await document.exitPictureInPicture()
111
+ }
112
+
113
+ if (this.pipWindow) {
114
+ this.pipWindow.close()
115
+ this.pipWindow = null
116
+ }
117
+ } catch (error) {
118
+ console.error('Failed to exit Picture-in-Picture:', error)
119
+ throw error
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Toggle Picture-in-Picture mode
125
+ */
126
+ async toggle(element?: HTMLVideoElement, options: PiPOptions = {}): Promise<void> {
127
+ if (this.isCurrentlyActive) {
128
+ await this.exit()
129
+ } else {
130
+ await this.enter(element, options)
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Add event listener
136
+ */
137
+ addEventListener(event: string, callback: Function): () => void {
138
+ if (!this.eventListeners.has(event)) {
139
+ this.eventListeners.set(event, new Set())
140
+ }
141
+
142
+ this.eventListeners.get(event)!.add(callback)
143
+
144
+ return () => {
145
+ const listeners = this.eventListeners.get(event)
146
+ if (listeners) {
147
+ listeners.delete(callback)
148
+ }
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Enter video Picture-in-Picture mode
154
+ */
155
+ private async enterVideoPiP(element: HTMLVideoElement, options: PiPOptions): Promise<void> {
156
+ try {
157
+ this.videoElement = element
158
+
159
+ // Request video Picture-in-Picture
160
+ await element.requestPictureInPicture()
161
+
162
+ this.isCurrentlyActive = true
163
+ this.emit('enter', { type: 'video' })
164
+
165
+ } catch (error) {
166
+ console.error('Failed to enter video Picture-in-Picture:', error)
167
+ throw error
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Enter document Picture-in-Picture mode
173
+ */
174
+ private async enterDocumentPiP(options: PiPOptions): Promise<void> {
175
+ if (!('documentPictureInPicture' in window)) {
176
+ throw new Error('Document Picture-in-Picture is not supported')
177
+ }
178
+
179
+ try {
180
+ const { width = 640, height = 360, disallowReturnToOpener = false } = options
181
+
182
+ // @ts-ignore - documentPictureInPicture is experimental
183
+ this.pipWindow = await window.documentPictureInPicture.requestWindow({
184
+ width,
185
+ height,
186
+ disallowReturnToOpener
187
+ })
188
+
189
+ this.isCurrentlyActive = true
190
+ this.emit('enter', { type: 'document', window: this.pipWindow })
191
+
192
+ // Set up window event listeners
193
+ this.pipWindow.addEventListener('unload', () => {
194
+ this.handleDocumentPiPClose()
195
+ })
196
+
197
+ this.pipWindow.addEventListener('pagehide', () => {
198
+ this.handleDocumentPiPClose()
199
+ })
200
+
201
+ } catch (error) {
202
+ console.error('Failed to enter document Picture-in-Picture:', error)
203
+ throw error
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Setup global event listeners
209
+ */
210
+ private setupEventListeners(): void {
211
+ // Video PiP events
212
+ document.addEventListener('enterpictureinpicture', (event) => {
213
+ this.isCurrentlyActive = true
214
+ this.emit('enter', { type: 'video', element: event.target })
215
+ })
216
+
217
+ document.addEventListener('leavepictureinpicture', (event) => {
218
+ this.isCurrentlyActive = false
219
+ this.videoElement = null
220
+ this.emit('exit', { type: 'video', element: event.target })
221
+ })
222
+
223
+ // Document PiP events (if supported)
224
+ if ('documentPictureInPicture' in window) {
225
+ // @ts-ignore - documentPictureInPicture is experimental
226
+ window.documentPictureInPicture.addEventListener('enter', (event: any) => {
227
+ this.isCurrentlyActive = true
228
+ this.pipWindow = event.window
229
+ this.emit('enter', { type: 'document', window: event.window })
230
+ })
231
+ }
232
+
233
+ // Handle visibility change (when user switches tabs)
234
+ document.addEventListener('visibilitychange', () => {
235
+ if (document.hidden && this.isCurrentlyActive) {
236
+ this.emit('backgrounded')
237
+ } else if (!document.hidden && this.isCurrentlyActive) {
238
+ this.emit('foregrounded')
239
+ }
240
+ })
241
+ }
242
+
243
+ /**
244
+ * Handle document PiP window close
245
+ */
246
+ private handleDocumentPiPClose(): void {
247
+ this.isCurrentlyActive = false
248
+ this.pipWindow = null
249
+ this.emit('exit', { type: 'document' })
250
+ }
251
+
252
+ /**
253
+ * Emit events to listeners
254
+ */
255
+ private emit(event: string, data?: any): void {
256
+ const listeners = this.eventListeners.get(event)
257
+ if (listeners) {
258
+ listeners.forEach(callback => callback(data))
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Create custom controls for PiP window
264
+ */
265
+ createCustomControls(options: {
266
+ onPlay?: () => void
267
+ onPause?: () => void
268
+ onSeek?: (time: number) => void
269
+ onVolumeChange?: (volume: number) => void
270
+ onClose?: () => void
271
+ } = {}): HTMLElement {
272
+ const controlsContainer = document.createElement('div')
273
+ controlsContainer.className = 'pip-controls'
274
+ controlsContainer.style.cssText = `
275
+ position: fixed;
276
+ bottom: 10px;
277
+ left: 10px;
278
+ right: 10px;
279
+ background: rgba(0, 0, 0, 0.8);
280
+ border-radius: 8px;
281
+ padding: 10px;
282
+ display: flex;
283
+ align-items: center;
284
+ gap: 10px;
285
+ z-index: 1000;
286
+ `
287
+
288
+ // Play/Pause button
289
+ const playPauseBtn = document.createElement('button')
290
+ playPauseBtn.innerHTML = '▶️'
291
+ playPauseBtn.style.cssText = `
292
+ background: none;
293
+ border: none;
294
+ color: white;
295
+ font-size: 16px;
296
+ cursor: pointer;
297
+ padding: 5px;
298
+ `
299
+ playPauseBtn.onclick = () => {
300
+ const isPlaying = playPauseBtn.innerHTML === '⏸️'
301
+ if (isPlaying) {
302
+ options.onPause?.()
303
+ playPauseBtn.innerHTML = '▶️'
304
+ } else {
305
+ options.onPlay?.()
306
+ playPauseBtn.innerHTML = '⏸️'
307
+ }
308
+ }
309
+
310
+ // Progress bar
311
+ const progressContainer = document.createElement('div')
312
+ progressContainer.style.cssText = `
313
+ flex: 1;
314
+ height: 4px;
315
+ background: rgba(255, 255, 255, 0.3);
316
+ border-radius: 2px;
317
+ cursor: pointer;
318
+ `
319
+
320
+ const progressBar = document.createElement('div')
321
+ progressBar.style.cssText = `
322
+ height: 100%;
323
+ background: #3b82f6;
324
+ border-radius: 2px;
325
+ width: 0%;
326
+ `
327
+ progressContainer.appendChild(progressBar)
328
+
329
+ // Volume control
330
+ const volumeContainer = document.createElement('div')
331
+ volumeContainer.style.cssText = `
332
+ display: flex;
333
+ align-items: center;
334
+ gap: 5px;
335
+ `
336
+
337
+ const volumeIcon = document.createElement('span')
338
+ volumeIcon.innerHTML = '🔊'
339
+ volumeIcon.style.cssText = `
340
+ font-size: 14px;
341
+ cursor: pointer;
342
+ `
343
+
344
+ const volumeSlider = document.createElement('input')
345
+ volumeSlider.type = 'range'
346
+ volumeSlider.min = '0'
347
+ volumeSlider.max = '100'
348
+ volumeSlider.value = '100'
349
+ volumeSlider.style.cssText = `
350
+ width: 60px;
351
+ height: 4px;
352
+ `
353
+ volumeSlider.oninput = () => {
354
+ const volume = parseInt(volumeSlider.value) / 100
355
+ options.onVolumeChange?.(volume)
356
+ }
357
+
358
+ volumeContainer.appendChild(volumeIcon)
359
+ volumeContainer.appendChild(volumeSlider)
360
+
361
+ // Close button
362
+ const closeBtn = document.createElement('button')
363
+ closeBtn.innerHTML = '✕'
364
+ closeBtn.style.cssText = `
365
+ background: none;
366
+ border: none;
367
+ color: white;
368
+ font-size: 16px;
369
+ cursor: pointer;
370
+ padding: 5px;
371
+ `
372
+ closeBtn.onclick = () => {
373
+ options.onClose?.()
374
+ this.exit()
375
+ }
376
+
377
+ // Assemble controls
378
+ controlsContainer.appendChild(playPauseBtn)
379
+ controlsContainer.appendChild(progressContainer)
380
+ controlsContainer.appendChild(volumeContainer)
381
+ controlsContainer.appendChild(closeBtn)
382
+
383
+ return controlsContainer
384
+ }
385
+
386
+ /**
387
+ * Setup responsive sizing for PiP window
388
+ */
389
+ setupResponsiveSizing(window: Window, aspectRatio: number = 16/9): void {
390
+ if (!window) return
391
+
392
+ const resizeWindow = () => {
393
+ const maxWidth = screen.width * 0.3 // 30% of screen width
394
+ const maxHeight = screen.height * 0.3 // 30% of screen height
395
+
396
+ let width = maxWidth
397
+ let height = width / aspectRatio
398
+
399
+ if (height > maxHeight) {
400
+ height = maxHeight
401
+ width = height * aspectRatio
402
+ }
403
+
404
+ window.resizeTo(Math.round(width), Math.round(height))
405
+ }
406
+
407
+ // Initial resize
408
+ resizeWindow()
409
+
410
+ // Handle screen orientation changes
411
+ if (screen.orientation) {
412
+ screen.orientation.addEventListener('change', resizeWindow)
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Cleanup resources
418
+ */
419
+ destroy(): void {
420
+ this.exit().catch(() => {})
421
+ this.eventListeners.clear()
422
+ this.videoElement = null
423
+ this.pipWindow = null
424
+ this.isCurrentlyActive = false
425
+ }
426
+ }
427
+
428
+ // Singleton instance
429
+ export const pipManager = new EnhancedPiPManager()