@therealtinhtute/baroiplayer 1.2.1 → 2.0.0-beta.1

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 (302) hide show
  1. package/README.md +90 -71
  2. package/dist/components/icons/index.d.ts +36 -0
  3. package/dist/components/icons/index.d.ts.map +1 -0
  4. package/dist/components/icons/svg-icons/ArrowLeftIcon.d.ts +4 -0
  5. package/dist/components/icons/svg-icons/ArrowLeftIcon.d.ts.map +1 -0
  6. package/dist/components/icons/svg-icons/ArrowRightIcon.d.ts +5 -0
  7. package/dist/components/icons/svg-icons/ArrowRightIcon.d.ts.map +1 -0
  8. package/dist/components/icons/svg-icons/AudioIcon.d.ts +5 -0
  9. package/dist/components/icons/svg-icons/AudioIcon.d.ts.map +1 -0
  10. package/dist/components/icons/svg-icons/BackwardIcon.d.ts +5 -0
  11. package/dist/components/icons/svg-icons/BackwardIcon.d.ts.map +1 -0
  12. package/dist/components/icons/svg-icons/CameraIcon.d.ts +5 -0
  13. package/dist/components/icons/svg-icons/CameraIcon.d.ts.map +1 -0
  14. package/dist/components/icons/svg-icons/CheckIcon.d.ts +5 -0
  15. package/dist/components/icons/svg-icons/CheckIcon.d.ts.map +1 -0
  16. package/dist/components/icons/svg-icons/EnablePiPIcon.d.ts +4 -0
  17. package/dist/components/icons/svg-icons/EnablePiPIcon.d.ts.map +1 -0
  18. package/dist/components/icons/svg-icons/ForwardIcon.d.ts +5 -0
  19. package/dist/components/icons/svg-icons/ForwardIcon.d.ts.map +1 -0
  20. package/dist/components/icons/svg-icons/FullscreenEnterIcon.d.ts +5 -0
  21. package/dist/components/icons/svg-icons/FullscreenEnterIcon.d.ts.map +1 -0
  22. package/dist/components/icons/svg-icons/FullscreenExitIcon.d.ts +5 -0
  23. package/dist/components/icons/svg-icons/FullscreenExitIcon.d.ts.map +1 -0
  24. package/dist/components/icons/svg-icons/LoadingIcon.d.ts +5 -0
  25. package/dist/components/icons/svg-icons/LoadingIcon.d.ts.map +1 -0
  26. package/dist/components/icons/svg-icons/PauseIcon.d.ts +5 -0
  27. package/dist/components/icons/svg-icons/PauseIcon.d.ts.map +1 -0
  28. package/dist/components/icons/svg-icons/PlayIcon.d.ts +5 -0
  29. package/dist/components/icons/svg-icons/PlayIcon.d.ts.map +1 -0
  30. package/dist/components/icons/svg-icons/PlaybackSpeedIcon.d.ts +5 -0
  31. package/dist/components/icons/svg-icons/PlaybackSpeedIcon.d.ts.map +1 -0
  32. package/dist/components/icons/svg-icons/QualityIcon.d.ts +5 -0
  33. package/dist/components/icons/svg-icons/QualityIcon.d.ts.map +1 -0
  34. package/dist/components/icons/svg-icons/SettingsIcon.d.ts +5 -0
  35. package/dist/components/icons/svg-icons/SettingsIcon.d.ts.map +1 -0
  36. package/dist/components/icons/svg-icons/SliderIcon.d.ts +5 -0
  37. package/dist/components/icons/svg-icons/SliderIcon.d.ts.map +1 -0
  38. package/dist/components/icons/svg-icons/SubtitleIcon.d.ts +5 -0
  39. package/dist/components/icons/svg-icons/SubtitleIcon.d.ts.map +1 -0
  40. package/dist/components/icons/svg-icons/SubtitleOffIcon.d.ts +5 -0
  41. package/dist/components/icons/svg-icons/SubtitleOffIcon.d.ts.map +1 -0
  42. package/dist/components/icons/svg-icons/UnPiPIcon.d.ts +4 -0
  43. package/dist/components/icons/svg-icons/UnPiPIcon.d.ts.map +1 -0
  44. package/dist/components/icons/svg-icons/VolumeMutedIcon.d.ts +5 -0
  45. package/dist/components/icons/svg-icons/VolumeMutedIcon.d.ts.map +1 -0
  46. package/dist/components/icons/svg-icons/VolumeOneIcon.d.ts +5 -0
  47. package/dist/components/icons/svg-icons/VolumeOneIcon.d.ts.map +1 -0
  48. package/dist/components/icons/svg-icons/VolumeThreeIcon.d.ts +5 -0
  49. package/dist/components/icons/svg-icons/VolumeThreeIcon.d.ts.map +1 -0
  50. package/dist/components/icons/svg-icons/VolumeTwoIcon.d.ts +5 -0
  51. package/dist/components/icons/svg-icons/VolumeTwoIcon.d.ts.map +1 -0
  52. package/dist/components/variants/gradient/gradient-audio.d.ts +13 -0
  53. package/dist/components/variants/gradient/gradient-audio.d.ts.map +1 -0
  54. package/dist/components/variants/gradient/gradient-controls.d.ts +4 -0
  55. package/dist/components/variants/gradient/gradient-controls.d.ts.map +1 -0
  56. package/dist/components/variants/gradient/gradient-glyphs.d.ts +30 -0
  57. package/dist/components/variants/gradient/gradient-glyphs.d.ts.map +1 -0
  58. package/dist/components/variants/gradient/gradient-video.d.ts +16 -0
  59. package/dist/components/variants/gradient/gradient-video.d.ts.map +1 -0
  60. package/dist/index.css +1 -1
  61. package/dist/index.d.ts +5 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +1 -41
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.mjs +750 -5640
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/package.json +3 -0
  68. package/package.json +110 -114
  69. package/src/components/variants/gradient/gradient-audio.tsx +47 -0
  70. package/src/components/variants/gradient/gradient-controls.tsx +254 -0
  71. package/src/components/variants/gradient/gradient-glyphs.test.tsx +69 -0
  72. package/src/components/variants/gradient/gradient-glyphs.tsx +129 -0
  73. package/src/components/variants/gradient/gradient-skin.css +211 -0
  74. package/src/components/variants/gradient/gradient-video.tsx +70 -0
  75. package/src/index.ts +5 -198
  76. package/src/test/accessibility-utils.ts +0 -59
  77. package/dist/context/media.js +0 -1
  78. package/dist/context/media.mjs +0 -3
  79. package/dist/context/store.js +0 -1
  80. package/dist/context/store.mjs +0 -3
  81. package/dist/context-Bzd5QCKM.cjs +0 -7
  82. package/dist/context-Bzd5QCKM.cjs.map +0 -1
  83. package/dist/context-DD_SH4tT.js +0 -1053
  84. package/dist/context-DD_SH4tT.js.map +0 -1
  85. package/dist/core-events-BRnLMB7s.cjs +0 -2
  86. package/dist/core-events-BRnLMB7s.cjs.map +0 -1
  87. package/dist/core-events-v_mKdj5p.js +0 -417
  88. package/dist/core-events-v_mKdj5p.js.map +0 -1
  89. package/dist/core-plugins-CMBv-yVh.cjs +0 -2
  90. package/dist/core-plugins-CMBv-yVh.cjs.map +0 -1
  91. package/dist/core-plugins-CqCxLkbd.js +0 -754
  92. package/dist/core-plugins-CqCxLkbd.js.map +0 -1
  93. package/dist/core.js +0 -15
  94. package/dist/core.js.map +0 -1
  95. package/dist/core.mjs +0 -960
  96. package/dist/core.mjs.map +0 -1
  97. package/dist/enhanced-components-BwjBqSA1.js +0 -2858
  98. package/dist/enhanced-components-BwjBqSA1.js.map +0 -1
  99. package/dist/enhanced-components-C-ka-XLp.cjs +0 -2
  100. package/dist/enhanced-components-C-ka-XLp.cjs.map +0 -1
  101. package/dist/hooks-B-IIWPJr.js +0 -1842
  102. package/dist/hooks-B-IIWPJr.js.map +0 -1
  103. package/dist/hooks-DcHzmBwA.cjs +0 -4
  104. package/dist/hooks-DcHzmBwA.cjs.map +0 -1
  105. package/dist/minimal-components-BYsMQde5.js +0 -1610
  106. package/dist/minimal-components-BYsMQde5.js.map +0 -1
  107. package/dist/minimal-components-CsxQbDp7.cjs +0 -2
  108. package/dist/minimal-components-CsxQbDp7.cjs.map +0 -1
  109. package/dist/minimal-components.css +0 -1
  110. package/dist/mona-loading.gif +0 -0
  111. package/dist/performance-monitor-DCSijLJg.js +0 -312
  112. package/dist/performance-monitor-DCSijLJg.js.map +0 -1
  113. package/dist/performance-monitor-wNFKmqMr.cjs +0 -2
  114. package/dist/performance-monitor-wNFKmqMr.cjs.map +0 -1
  115. package/dist/rolldown-runtime-BmowowmI.js +0 -14
  116. package/dist/rolldown-runtime-mLOUI7ql.cjs +0 -1
  117. package/dist/utils-D2gQ7c2R.js +0 -64
  118. package/dist/utils-D2gQ7c2R.js.map +0 -1
  119. package/dist/utils-DJaAwiZ9.cjs +0 -2
  120. package/dist/utils-DJaAwiZ9.cjs.map +0 -1
  121. package/dist/utils.js +0 -1
  122. package/dist/utils.mjs +0 -3
  123. package/src/adapters/videojs-adapter.tsx +0 -470
  124. package/src/assets/mona-loading.gif +0 -0
  125. package/src/components/AnimateLoading.tsx +0 -111
  126. package/src/components/LoadingState.tsx +0 -239
  127. package/src/components/__tests__/media-player-accessibility.test.tsx +0 -510
  128. package/src/components/__tests__/media-player-react19.test.tsx +0 -457
  129. package/src/components/accessibility/keyboard-help-overlay.tsx +0 -207
  130. package/src/components/audio-player.tsx +0 -85
  131. package/src/components/basic/index.ts +0 -37
  132. package/src/components/enhanced/advanced-features.tsx +0 -311
  133. package/src/components/enhanced/debug-panel.tsx +0 -448
  134. package/src/components/enhanced/enhanced-controls.tsx +0 -597
  135. package/src/components/enhanced/enhanced-media-player.tsx +0 -566
  136. package/src/components/enhanced/error-handling.tsx +0 -338
  137. package/src/components/enhanced/index.ts +0 -39
  138. package/src/components/enhanced/media-player-enhanced.tsx +0 -383
  139. package/src/components/examples/basic-video-player.tsx +0 -50
  140. package/src/components/examples/custom-controls-example.tsx +0 -63
  141. package/src/components/examples/enhanced-controls-example.tsx +0 -100
  142. package/src/components/examples/enhanced-media-player-example.tsx +0 -302
  143. package/src/components/examples/loading-states-demo.tsx +0 -141
  144. package/src/components/examples/react-19-demo.tsx +0 -349
  145. package/src/components/examples/volume-orientation-demo.tsx +0 -105
  146. package/src/components/icon-demo.tsx +0 -196
  147. package/src/components/legacy.tsx +0 -87
  148. package/src/components/media-metadata-display.tsx +0 -99
  149. package/src/components/media-player-audio.tsx +0 -39
  150. package/src/components/media-player-controls.tsx +0 -223
  151. package/src/components/media-player-error.tsx +0 -163
  152. package/src/components/media-player-fullscreen.tsx +0 -109
  153. package/src/components/media-player-hls.tsx +0 -111
  154. package/src/components/media-player-loading.tsx +0 -76
  155. package/src/components/media-player-play.tsx +0 -75
  156. package/src/components/media-player-root.tsx +0 -338
  157. package/src/components/media-player-seek.tsx +0 -168
  158. package/src/components/media-player-time.tsx +0 -99
  159. package/src/components/media-player-tooltip.tsx +0 -58
  160. package/src/components/media-player-video.tsx +0 -54
  161. package/src/components/media-player-volume.tsx +0 -155
  162. package/src/components/media-player.tsx +0 -292
  163. package/src/components/media-suspense.tsx +0 -344
  164. package/src/components/player-settings-form.tsx +0 -158
  165. package/src/components/react-player-wrapper.tsx +0 -651
  166. package/src/components/simple-video-player.tsx +0 -62
  167. package/src/components/ui/blur-poster.tsx +0 -96
  168. package/src/components/ui/empty-state.tsx +0 -91
  169. package/src/components/ui/enhanced-seek-bar.tsx +0 -254
  170. package/src/components/ui/error-state.tsx +0 -124
  171. package/src/components/ui/fullscreen-button.tsx +0 -81
  172. package/src/components/ui/loading-skeleton.tsx +0 -107
  173. package/src/components/ui/media-resource-preloader.tsx +0 -458
  174. package/src/components/ui/offline-message.tsx +0 -98
  175. package/src/components/ui/play-button.tsx +0 -99
  176. package/src/components/ui/seek-bar.tsx +0 -191
  177. package/src/components/ui/time-display.tsx +0 -61
  178. package/src/components/ui/volume-control.tsx +0 -190
  179. package/src/components/unified/base-media-player.tsx +0 -252
  180. package/src/components/unified/media-player.tsx +0 -75
  181. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +0 -587
  182. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +0 -573
  183. package/src/components/variants/minimal/MinimalControls.tsx +0 -124
  184. package/src/components/variants/minimal/MinimalLoading.tsx +0 -28
  185. package/src/components/variants/minimal/MinimalMediaPlayer.tsx +0 -318
  186. package/src/components/variants/minimal/MinimalOverlay.tsx +0 -58
  187. package/src/components/variants/minimal/MinimalPlayButton.tsx +0 -56
  188. package/src/components/variants/minimal/MinimalSeekBar.tsx +0 -140
  189. package/src/components/variants/minimal/MinimalVolumeControl.tsx +0 -75
  190. package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +0 -206
  191. package/src/components/variants/minimal/components/CenterPlayButton.tsx +0 -22
  192. package/src/components/variants/minimal/components/ControlBar.tsx +0 -142
  193. package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +0 -412
  194. package/src/components/variants/minimal/components/PlaybackControls.tsx +0 -46
  195. package/src/components/variants/minimal/components/RightControls.tsx +0 -164
  196. package/src/components/variants/minimal/components/SeekBar.tsx +0 -127
  197. package/src/components/variants/minimal/components/SettingsDropdown.tsx +0 -68
  198. package/src/components/variants/minimal/components/SubtitleDisplay.tsx +0 -21
  199. package/src/components/variants/minimal/components/TimeDisplay.tsx +0 -15
  200. package/src/components/variants/minimal/components/VideoElement.tsx +0 -35
  201. package/src/components/variants/minimal/components/VolumeControl.tsx +0 -220
  202. package/src/components/variants/minimal/index.ts +0 -23
  203. package/src/components/video-player.tsx +0 -100
  204. package/src/context/__tests__/media-store.test.ts +0 -509
  205. package/src/context/error-handling.ts +0 -398
  206. package/src/context/media-context.tsx +0 -254
  207. package/src/context/media-provider.tsx +0 -264
  208. package/src/context/media-store.ts +0 -387
  209. package/src/context/persistence.ts +0 -462
  210. package/src/core/events/event-system.ts +0 -448
  211. package/src/core/events/hooks.ts +0 -325
  212. package/src/core/events/index.ts +0 -22
  213. package/src/core/events/middleware.ts +0 -286
  214. package/src/core/index.ts +0 -22
  215. package/src/core/optimization/bundle-optimizer.tsx +0 -312
  216. package/src/core/performance/cicd.ts +0 -98
  217. package/src/core/performance/index.ts +0 -38
  218. package/src/core/performance/media-loading-optimizer.ts +0 -545
  219. package/src/core/performance/performance-budgets.ts +0 -437
  220. package/src/core/performance/performance-monitor.tsx +0 -488
  221. package/src/core/performance/performance-testing.ts +0 -461
  222. package/src/core/plugins/__tests__/registry.test.ts +0 -595
  223. package/src/core/plugins/base-plugin.ts +0 -225
  224. package/src/core/plugins/enhanced-plugin-system.ts +0 -435
  225. package/src/core/plugins/examples/analytics-plugin.ts +0 -241
  226. package/src/core/plugins/index.ts +0 -39
  227. package/src/core/plugins/registry.ts +0 -546
  228. package/src/core/plugins/types.ts +0 -169
  229. package/src/core/plugins/utils.ts +0 -317
  230. package/src/core/progressive-loading.tsx +0 -331
  231. package/src/core/providers/enhanced-hls-provider.ts +0 -307
  232. package/src/core/providers/lazy-provider.tsx +0 -14
  233. package/src/core/providers/service-provider.tsx +0 -164
  234. package/src/core/utils.tsx +0 -34
  235. package/src/features/media-session/media-session-manager.ts +0 -411
  236. package/src/features/picture-in-picture/pip-manager.ts +0 -428
  237. package/src/features/playlist/playlist-manager.ts +0 -626
  238. package/src/features/subtitles/subtitle-manager.ts +0 -435
  239. package/src/features/subtitles/subtitle-parser.ts +0 -284
  240. package/src/features/thumbnails/thumbnail-generator.ts +0 -528
  241. package/src/hooks/__tests__/currentTime-fix.test.ts +0 -149
  242. package/src/hooks/__tests__/use-media-player.test.ts +0 -712
  243. package/src/hooks/__tests__/use-media-state.test.tsx +0 -316
  244. package/src/hooks/accessibility/index.ts +0 -27
  245. package/src/hooks/accessibility/use-accessibility-preferences.ts +0 -190
  246. package/src/hooks/accessibility/use-focus-management.ts +0 -183
  247. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +0 -152
  248. package/src/hooks/accessibility/use-screen-reader.ts +0 -212
  249. package/src/hooks/core/index.ts +0 -47
  250. package/src/hooks/legacy.ts +0 -21
  251. package/src/hooks/use-buffer-health.ts +0 -218
  252. package/src/hooks/use-enhanced-media-player.ts +0 -575
  253. package/src/hooks/use-focus-trap.tsx +0 -162
  254. package/src/hooks/use-intelligent-preloading.ts +0 -387
  255. package/src/hooks/use-keyboard-controls.ts +0 -115
  256. package/src/hooks/use-keyboard-shortcuts.ts +0 -255
  257. package/src/hooks/use-media-actions.ts +0 -184
  258. package/src/hooks/use-media-metadata.ts +0 -91
  259. package/src/hooks/use-media-optimistic.ts +0 -264
  260. package/src/hooks/use-media-player.ts +0 -343
  261. package/src/hooks/use-media-state.ts +0 -194
  262. package/src/hooks/use-memory-optimization.ts +0 -360
  263. package/src/hooks/use-network-status.ts +0 -124
  264. package/src/hooks/use-optimistic-controls.ts +0 -95
  265. package/src/hooks/use-optimized-media.ts +0 -283
  266. package/src/hooks/use-smart-preload.ts +0 -229
  267. package/src/hooks/use-touch-gestures.ts +0 -461
  268. package/src/hooks/useEnhancedSubtitles.ts +0 -258
  269. package/src/hooks/useKeyboardShortcuts.ts +0 -106
  270. package/src/hooks/useMediaControls.ts +0 -52
  271. package/src/hooks/useScreenshot.ts +0 -77
  272. package/src/hooks/useSubtitles.ts +0 -91
  273. package/src/hooks/useVideoPlayer.ts +0 -278
  274. package/src/icons/components/FullscreenIcon.tsx +0 -36
  275. package/src/icons/components/SettingsIcon.tsx +0 -37
  276. package/src/icons/components/animated/LoadingIcon.tsx +0 -27
  277. package/src/icons/components/animated/PlayPauseIcon.tsx +0 -42
  278. package/src/icons/components/media/PauseIcon.tsx +0 -38
  279. package/src/icons/components/media/PlayIcon.tsx +0 -37
  280. package/src/icons/components/volume/VolumeIcon.tsx +0 -38
  281. package/src/icons/components/volume/VolumeOffIcon.tsx +0 -38
  282. package/src/icons/index.ts +0 -56
  283. package/src/icons/provider.tsx +0 -58
  284. package/src/icons/types.ts +0 -60
  285. package/src/index-enhanced.ts +0 -35
  286. package/src/index-legacy.ts +0 -324
  287. package/src/index.css +0 -102
  288. package/src/lib/__tests__/async-utils.test.ts +0 -291
  289. package/src/lib/async-utils.ts +0 -194
  290. package/src/lib/deprecation.ts +0 -51
  291. package/src/lib/format-time.ts +0 -37
  292. package/src/lib/index.ts +0 -4
  293. package/src/lib/utils.ts +0 -6
  294. package/src/styles/globals.css +0 -462
  295. package/src/styles/minimal-player.css +0 -621
  296. package/src/styles/pixel-art.css +0 -41
  297. package/src/styles/videojs.css +0 -100
  298. package/src/types/assets.d.ts +0 -29
  299. package/src/types/events.ts +0 -47
  300. package/src/types/formats.ts +0 -79
  301. package/src/types/index.ts +0 -203
  302. package/src/types/videojs-plugins.d.ts +0 -14
@@ -1,106 +0,0 @@
1
- import { useCallback, useEffect } from "react"
2
- import type { VideoControls } from "./useVideoPlayer"
3
-
4
- interface UseKeyboardShortcutsProps {
5
- videoControls: VideoControls
6
- toggleSubtitles: () => void
7
- takeScreenshot: (ctrlKey?: boolean) => void
8
- duration: number
9
- }
10
-
11
- /**
12
- * Safely sets currentTime on a video element, ensuring the value is finite
13
- */
14
- function safeSetCurrentTime(video: HTMLVideoElement, time: number): boolean {
15
- if (!Number.isFinite(time)) {
16
- console.warn(`Attempted to set non-finite currentTime: ${time}`)
17
- return false
18
- }
19
-
20
- if (time < 0) {
21
- console.warn(`Attempted to set negative currentTime: ${time}`)
22
- return false
23
- }
24
-
25
- try {
26
- video.currentTime = time
27
- return true
28
- } catch (error) {
29
- console.error(`Failed to set currentTime to ${time}:`, error)
30
- return false
31
- }
32
- }
33
-
34
- export function useKeyboardShortcuts({
35
- videoControls,
36
- toggleSubtitles,
37
- takeScreenshot,
38
- duration
39
- }: UseKeyboardShortcutsProps) {
40
- const handleKeyDown = (e: KeyboardEvent) => {
41
- const video = document.querySelector("video")
42
- if (!video) return
43
-
44
- switch (e.key.toLowerCase()) {
45
- case " ":
46
- case "k":
47
- e.preventDefault()
48
- videoControls.togglePlay()
49
- break
50
- case "j":
51
- e.preventDefault()
52
- videoControls.seekBackward()
53
- break
54
- case "l":
55
- e.preventDefault()
56
- videoControls.seekForward()
57
- break
58
- case "arrowleft":
59
- e.preventDefault()
60
- const currentTime = Number.isFinite(video.currentTime) ? video.currentTime : 0
61
- const newTimeLeft = Math.max(0, currentTime - 5)
62
- safeSetCurrentTime(video, newTimeLeft)
63
- break
64
- case "arrowright":
65
- e.preventDefault()
66
- const validDuration = Number.isFinite(duration) && duration > 0 ? duration : Number.isFinite(video.duration) ? video.duration : 0
67
- if (validDuration === 0) {
68
- console.warn("Cannot seek: duration is not available")
69
- break
70
- }
71
- const currentTimeRight = Number.isFinite(video.currentTime) ? video.currentTime : 0
72
- const newTimeRight = Math.min(validDuration, currentTimeRight + 5)
73
- safeSetCurrentTime(video, newTimeRight)
74
- break
75
- case "arrowup":
76
- e.preventDefault()
77
- video.volume = Math.min(1, video.volume + 0.1)
78
- break
79
- case "arrowdown":
80
- e.preventDefault()
81
- video.volume = Math.max(0, video.volume - 0.1)
82
- break
83
- case "m":
84
- e.preventDefault()
85
- videoControls.toggleMute()
86
- break
87
- case "f":
88
- e.preventDefault()
89
- videoControls.toggleFullscreen()
90
- break
91
- case "c":
92
- e.preventDefault()
93
- toggleSubtitles()
94
- break
95
- case "s":
96
- e.preventDefault()
97
- takeScreenshot(e.ctrlKey || e.metaKey)
98
- break
99
- }
100
- }
101
-
102
- useEffect(() => {
103
- document.addEventListener("keydown", handleKeyDown)
104
- return () => document.removeEventListener("keydown", handleKeyDown)
105
- }, [handleKeyDown])
106
- }
@@ -1,52 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from "react"
2
-
3
- export function useMediaControls(isPlaying: boolean, showSettings: boolean) {
4
- const [controlsVisible, setControlsVisible] = useState(true)
5
- const [showVolumeSlider, setShowVolumeSlider] = useState(false)
6
- const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null)
7
-
8
- const showControls = () => {
9
- setControlsVisible(true)
10
- if (hideTimeoutRef.current) {
11
- clearTimeout(hideTimeoutRef.current)
12
- }
13
-
14
- if (isPlaying && !showSettings) {
15
- hideTimeoutRef.current = setTimeout(() => {
16
- setControlsVisible(false)
17
- }, 3000)
18
- }
19
- }
20
-
21
- const handleMouseMove = () => {
22
- showControls()
23
- }
24
-
25
- const handleMouseEnter = () => {
26
- setControlsVisible(true)
27
- }
28
-
29
- const handleMouseLeave = () => {
30
- if (isPlaying) {
31
- setControlsVisible(false)
32
- }
33
- }
34
-
35
- useEffect(() => {
36
- showControls()
37
- return () => {
38
- if (hideTimeoutRef.current) {
39
- clearTimeout(hideTimeoutRef.current)
40
- }
41
- }
42
- }, [showControls])
43
-
44
- return {
45
- controlsVisible,
46
- showVolumeSlider,
47
- setShowVolumeSlider,
48
- handleMouseMove,
49
- handleMouseEnter,
50
- handleMouseLeave
51
- }
52
- }
@@ -1,77 +0,0 @@
1
- import { useCallback } from "react"
2
-
3
- function isCanvasTaintError(error: unknown): boolean {
4
- if (error instanceof DOMException && error.name === "SecurityError") return true
5
- if (error instanceof Error && /tainted/i.test(error.message)) return true
6
- return false
7
- }
8
-
9
- /**
10
- * Capture the current video frame.
11
- * Requires the video to be loaded with CORS-friendly headers and
12
- * `crossOrigin="anonymous"` (or `use-credentials`) on the element.
13
- */
14
- export function useScreenshot(videoRef: React.RefObject<HTMLVideoElement | null>) {
15
- const takeScreenshot = useCallback(
16
- (ctrlKey: boolean = false) => {
17
- const video = videoRef.current
18
- if (!video) return
19
-
20
- if (!video.videoWidth || !video.videoHeight) {
21
- console.warn("Screenshot skipped: video frame not ready yet.")
22
- return
23
- }
24
-
25
- const canvas = document.createElement("canvas")
26
- canvas.width = video.videoWidth
27
- canvas.height = video.videoHeight
28
- const ctx = canvas.getContext("2d")
29
- if (!ctx) return
30
-
31
- try {
32
- ctx.drawImage(video, 0, 0)
33
-
34
- // SecurityError throws here when the canvas is tainted by a
35
- // cross-origin video without usable CORS + crossOrigin on <video>.
36
- canvas.toBlob((blob) => {
37
- if (!blob) {
38
- console.error("Screenshot failed: could not create image blob.")
39
- return
40
- }
41
-
42
- if (ctrlKey) {
43
- if (navigator.clipboard && window.ClipboardItem) {
44
- const item = new ClipboardItem({ "image/png": blob })
45
- navigator.clipboard.write([item]).catch((err) => {
46
- console.error("Failed to copy screenshot to clipboard:", err)
47
- })
48
- } else {
49
- console.warn("Clipboard API not supported for screenshot copy.")
50
- }
51
- return
52
- }
53
-
54
- const url = URL.createObjectURL(blob)
55
- const a = document.createElement("a")
56
- a.href = url
57
- a.download = `screenshot-${Date.now()}.png`
58
- a.click()
59
- URL.revokeObjectURL(url)
60
- }, "image/png")
61
- } catch (error) {
62
- if (isCanvasTaintError(error)) {
63
- console.error(
64
- "Screenshot failed: canvas tainted. The video is cross-origin without CORS allowing canvas use. " +
65
- "Serve the media with Access-Control-Allow-Origin and set crossOrigin=\"anonymous\" on the player " +
66
- "(must be set before the media loads)."
67
- )
68
- return
69
- }
70
- console.error("Screenshot failed:", error)
71
- }
72
- },
73
- [videoRef]
74
- )
75
-
76
- return { takeScreenshot }
77
- }
@@ -1,91 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react"
2
-
3
- interface SubtitleCue {
4
- start: number
5
- end: number
6
- text: string
7
- }
8
-
9
- export function useSubtitles(subtitleSrc?: string) {
10
- const [subtitlesEnabled, setSubtitlesEnabled] = useState(false)
11
- const [subtitleCues, setSubtitleCues] = useState<SubtitleCue[]>([])
12
- const [currentSubtitle, setCurrentSubtitle] = useState("")
13
-
14
- const parseVTTTime = (timeStr: string): number => {
15
- const parts = timeStr.split(":")
16
- const seconds = parts[2] ? parseFloat(parts[2]) : 0
17
- const minutes = parts[1] ? parseInt(parts[1], 10) * 60 : 0
18
- const hours = parts[0] ? parseInt(parts[0], 10) * 3600 : 0
19
- return hours + minutes + seconds
20
- }
21
-
22
- const loadSubtitles = async (url: string) => {
23
- try {
24
- const response = await fetch(url)
25
- const text = await response.text()
26
-
27
- const cues: SubtitleCue[] = []
28
- const lines = text.split("\n")
29
- let i = 0
30
-
31
- while (i < lines.length) {
32
- if (lines[i]?.includes("-->")) {
33
- const [startStr, endStr] = (lines[i] || "").split("-->").map((s) => s.trim())
34
- const start = parseVTTTime(startStr || "")
35
- const end = parseVTTTime(endStr || "")
36
-
37
- i++
38
- let subtitleText = ""
39
- while (i < lines.length && lines[i]?.trim() !== "") {
40
- subtitleText += (subtitleText ? " " : "") + lines[i]?.trim()
41
- i++
42
- }
43
-
44
- if (subtitleText) {
45
- cues.push({ start, end, text: subtitleText })
46
- }
47
- }
48
- i++
49
- }
50
-
51
- setSubtitleCues(cues)
52
- } catch (error) {
53
- console.error("Failed to load subtitles:", error)
54
- }
55
- }
56
-
57
- const getCurrentSubtitle = (currentTime: number) => {
58
- if (!subtitlesEnabled || subtitleCues.length === 0) return ""
59
-
60
- const currentCue = subtitleCues.find(
61
- (cue) => currentTime >= cue.start && currentTime <= cue.end
62
- )
63
- return currentCue?.text || ""
64
- }
65
-
66
- const toggleSubtitles = () => {
67
- setSubtitlesEnabled((prev) => {
68
- const newEnabled = !prev
69
- if (newEnabled && subtitleSrc && subtitleCues.length === 0) {
70
- loadSubtitles(subtitleSrc)
71
- } else if (!newEnabled) {
72
- setCurrentSubtitle("")
73
- }
74
- return newEnabled
75
- })
76
- }
77
-
78
- useEffect(() => {
79
- if (!subtitlesEnabled) {
80
- setCurrentSubtitle("")
81
- }
82
- }, [subtitlesEnabled])
83
-
84
- return {
85
- subtitlesEnabled,
86
- currentSubtitle,
87
- toggleSubtitles,
88
- getCurrentSubtitle,
89
- setCurrentSubtitle
90
- }
91
- }
@@ -1,278 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from "react"
2
-
3
- export interface BufferedRange {
4
- start: number
5
- end: number
6
- percentage: number
7
- }
8
-
9
- export interface VideoState {
10
- isPlaying: boolean
11
- currentTime: number
12
- duration: number
13
- volume: number
14
- isMuted: boolean
15
- isFullscreen: boolean
16
- isLoading: boolean
17
- isSeeking: boolean
18
- isBuffering: boolean
19
- bufferedRanges: BufferedRange[]
20
- }
21
-
22
- export interface VideoControls {
23
- togglePlay: () => void
24
- seekForward: (seconds?: number) => void
25
- seekBackward: (seconds?: number) => void
26
- toggleMute: () => void
27
- setVolume: (volume: number) => void
28
- toggleFullscreen: () => void
29
- seekTo: (time: number) => void
30
- }
31
-
32
- /**
33
- * Safely sets currentTime on a video element, ensuring the value is finite
34
- * @param video - The video element
35
- * @param time - The desired time value
36
- * @returns true if successfully set, false otherwise
37
- */
38
- function safeSetCurrentTime(video: HTMLVideoElement, time: number): boolean {
39
- if (!Number.isFinite(time)) {
40
- console.warn(`Attempted to set non-finite currentTime: ${time}`)
41
- return false
42
- }
43
-
44
- if (time < 0) {
45
- console.warn(`Attempted to set negative currentTime: ${time}`)
46
- return false
47
- }
48
-
49
- try {
50
- video.currentTime = time
51
- return true
52
- } catch (error) {
53
- console.error(`Failed to set currentTime to ${time}:`, error)
54
- return false
55
- }
56
- }
57
-
58
- export function useVideoPlayer(containerRef: React.RefObject<HTMLDivElement | null>) {
59
- const videoRef = useRef<HTMLVideoElement>(null)
60
- const [state, setState] = useState<VideoState>({
61
- isPlaying: false,
62
- currentTime: 0,
63
- duration: 0,
64
- volume: 1,
65
- isMuted: false,
66
- isFullscreen: false,
67
- isLoading: true,
68
- isSeeking: false,
69
- isBuffering: false,
70
- bufferedRanges: []
71
- })
72
-
73
- useEffect(() => {
74
- const video = videoRef.current
75
- if (!video) return
76
-
77
- const handleTimeUpdate = () => {
78
- setState((prev) => ({ ...prev, currentTime: video.currentTime }))
79
- }
80
-
81
- const handleLoadedMetadata = () => {
82
- setState((prev) => ({ ...prev, duration: video.duration }))
83
- }
84
-
85
- const handlePlay = () => {
86
- setState((prev) => ({ ...prev, isPlaying: true, isBuffering: false }))
87
- }
88
-
89
- const handlePause = () => {
90
- setState((prev) => ({ ...prev, isPlaying: false }))
91
- }
92
-
93
- const handleVolumeChange = () => {
94
- setState((prev) => ({
95
- ...prev,
96
- volume: video.volume,
97
- isMuted: video.muted
98
- }))
99
- }
100
-
101
- // Loading state handlers
102
- const handleLoadStart = () => {
103
- setState((prev) => ({ ...prev, isLoading: true }))
104
- }
105
-
106
- const handleLoadedData = () => {
107
- setState((prev) => ({ ...prev, isLoading: false }))
108
- }
109
-
110
- // Seeking state handlers
111
- const handleSeeking = () => {
112
- setState((prev) => ({ ...prev, isSeeking: true }))
113
- }
114
-
115
- const handleSeeked = () => {
116
- setState((prev) => ({ ...prev, isSeeking: false }))
117
- }
118
-
119
- // Buffering state handlers
120
- const handleWaiting = () => {
121
- setState((prev) => ({ ...prev, isBuffering: true }))
122
- }
123
-
124
- const handlePlaying = () => {
125
- setState((prev) => ({ ...prev, isBuffering: false, isLoading: false }))
126
- }
127
-
128
- const handleCanPlay = () => {
129
- setState((prev) => ({ ...prev, isLoading: false, isBuffering: false }))
130
- }
131
-
132
- // Progress handler for buffered ranges
133
- const handleProgress = () => {
134
- if (!video.buffered || video.buffered.length === 0 || video.duration === 0) {
135
- setState((prev) => ({ ...prev, bufferedRanges: [] }))
136
- return
137
- }
138
-
139
- const ranges: BufferedRange[] = []
140
- for (let i = 0; i < video.buffered.length; i++) {
141
- const start = video.buffered.start(i)
142
- const end = video.buffered.end(i)
143
- ranges.push({
144
- start,
145
- end,
146
- percentage: ((end - start) / video.duration) * 100
147
- })
148
- }
149
- setState((prev) => ({ ...prev, bufferedRanges: ranges }))
150
- }
151
-
152
- video.addEventListener("timeupdate", handleTimeUpdate)
153
- video.addEventListener("loadedmetadata", handleLoadedMetadata)
154
- video.addEventListener("play", handlePlay)
155
- video.addEventListener("pause", handlePause)
156
- video.addEventListener("volumechange", handleVolumeChange)
157
- video.addEventListener("loadstart", handleLoadStart)
158
- video.addEventListener("loadeddata", handleLoadedData)
159
- video.addEventListener("seeking", handleSeeking)
160
- video.addEventListener("seeked", handleSeeked)
161
- video.addEventListener("waiting", handleWaiting)
162
- video.addEventListener("playing", handlePlaying)
163
- video.addEventListener("canplay", handleCanPlay)
164
- video.addEventListener("progress", handleProgress)
165
-
166
- return () => {
167
- video.removeEventListener("timeupdate", handleTimeUpdate)
168
- video.removeEventListener("loadedmetadata", handleLoadedMetadata)
169
- video.removeEventListener("play", handlePlay)
170
- video.removeEventListener("pause", handlePause)
171
- video.removeEventListener("volumechange", handleVolumeChange)
172
- video.removeEventListener("loadstart", handleLoadStart)
173
- video.removeEventListener("loadeddata", handleLoadedData)
174
- video.removeEventListener("seeking", handleSeeking)
175
- video.removeEventListener("seeked", handleSeeked)
176
- video.removeEventListener("waiting", handleWaiting)
177
- video.removeEventListener("playing", handlePlaying)
178
- video.removeEventListener("canplay", handleCanPlay)
179
- video.removeEventListener("progress", handleProgress)
180
- }
181
- }, [])
182
-
183
- const togglePlay = () => {
184
- const video = videoRef.current
185
- if (!video) return
186
-
187
- if (state.isPlaying) {
188
- video.pause()
189
- } else {
190
- video.play()
191
- }
192
- }
193
-
194
- const seekForward = (seconds = 10) => {
195
- const video = videoRef.current
196
- if (!video) return
197
-
198
- const duration = Number.isFinite(video.duration) ? video.duration : Number.isFinite(state.duration) ? state.duration : 0
199
- if (duration === 0) {
200
- console.warn("Cannot seek forward: duration is not available")
201
- return
202
- }
203
-
204
- const currentTime = Number.isFinite(video.currentTime) ? video.currentTime : 0
205
- const newTime = Math.min(duration, currentTime + seconds)
206
- safeSetCurrentTime(video, newTime)
207
- }
208
-
209
- const seekBackward = (seconds = 10) => {
210
- const video = videoRef.current
211
- if (!video) return
212
-
213
- const currentTime = Number.isFinite(video.currentTime) ? video.currentTime : 0
214
- const newTime = Math.max(0, currentTime - seconds)
215
- safeSetCurrentTime(video, newTime)
216
- }
217
-
218
- const toggleMute = () => {
219
- const video = videoRef.current
220
- if (!video) return
221
- video.muted = !state.isMuted
222
- }
223
-
224
- const setVolume = (volume: number) => {
225
- const video = videoRef.current
226
- if (!video) return
227
- video.volume = volume
228
- if (volume > 0 && state.isMuted) {
229
- video.muted = false
230
- }
231
- }
232
-
233
- const toggleFullscreen = () => {
234
- const container = containerRef.current
235
- if (!container) return
236
-
237
- if (!state.isFullscreen) {
238
- if (container.requestFullscreen) {
239
- container.requestFullscreen()
240
- }
241
- } else {
242
- if (document.exitFullscreen) {
243
- document.exitFullscreen()
244
- }
245
- }
246
- setState((prev) => ({ ...prev, isFullscreen: !prev.isFullscreen }))
247
- }
248
-
249
- const seekTo = (time: number) => {
250
- const video = videoRef.current
251
- if (!video) return
252
-
253
- const duration = Number.isFinite(video.duration) ? video.duration : Number.isFinite(state.duration) ? state.duration : 0
254
- if (duration === 0) {
255
- console.warn("Cannot seek: duration is not available")
256
- return
257
- }
258
-
259
- const clampedTime = Math.max(0, Math.min(duration, time))
260
- safeSetCurrentTime(video, clampedTime)
261
- }
262
-
263
- const controls: VideoControls = {
264
- togglePlay,
265
- seekForward,
266
- seekBackward,
267
- toggleMute,
268
- setVolume,
269
- toggleFullscreen,
270
- seekTo
271
- }
272
-
273
- return {
274
- videoRef,
275
- state,
276
- controls
277
- }
278
- }
@@ -1,36 +0,0 @@
1
- import { useCustomIcon, useIconTheme } from "../provider"
2
- import type { IconProps } from "../types"
3
-
4
- export function FullscreenIcon(props: IconProps) {
5
- const theme = useIconTheme()
6
- const customIcon = useCustomIcon("fullscreen")
7
-
8
- if (customIcon) {
9
- return customIcon(props)
10
- }
11
-
12
- const {
13
- size = theme.size,
14
- color = theme.color,
15
- strokeWidth = theme.strokeWidth,
16
- className = theme.className,
17
- ...rest
18
- } = props
19
-
20
- return (
21
- <svg
22
- width={size}
23
- height={size}
24
- viewBox="0 0 24 24"
25
- fill="none"
26
- stroke={color}
27
- strokeWidth={strokeWidth}
28
- strokeLinecap="round"
29
- strokeLinejoin="round"
30
- className={className}
31
- aria-hidden="true"
32
- {...rest}>
33
- <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
34
- </svg>
35
- )
36
- }
@@ -1,37 +0,0 @@
1
- import { useCustomIcon, useIconTheme } from "../provider"
2
- import type { IconProps } from "../types"
3
-
4
- export function SettingsIcon(props: IconProps) {
5
- const theme = useIconTheme()
6
- const customIcon = useCustomIcon("settings")
7
-
8
- if (customIcon) {
9
- return customIcon(props)
10
- }
11
-
12
- const {
13
- size = theme.size,
14
- color = theme.color,
15
- strokeWidth = theme.strokeWidth,
16
- className = theme.className,
17
- ...rest
18
- } = props
19
-
20
- return (
21
- <svg
22
- width={size}
23
- height={size}
24
- viewBox="0 0 24 24"
25
- fill="none"
26
- stroke={color}
27
- strokeWidth={strokeWidth}
28
- strokeLinecap="round"
29
- strokeLinejoin="round"
30
- className={className}
31
- aria-hidden="true"
32
- {...rest}>
33
- <circle cx="12" cy="12" r="3" />
34
- <path d="M12 1v6m0 6v6m11-7h-6m-6 0H1m10.5-4A1.5 1.5 0 0 1 15 12a1.5 1.5 0 0 1-3.5 0 1.5 1.5 0 0 1 3.5 0Z" />
35
- </svg>
36
- )
37
- }
@@ -1,27 +0,0 @@
1
- import { useCustomIcon, useIconTheme } from "../../provider"
2
- import type { IconProps } from "../../types"
3
-
4
- export function LoadingIcon(props: IconProps) {
5
- const theme = useIconTheme()
6
- const customIcon = useCustomIcon("loading")
7
-
8
- if (customIcon) {
9
- return customIcon(props)
10
- }
11
-
12
- const { size = theme.size, color = theme.color, className = theme.className, ...rest } = props
13
-
14
- return (
15
- <svg
16
- width={size}
17
- height={size}
18
- viewBox="0 0 24 24"
19
- fill="none"
20
- className={`animate-spin ${className || ""}`}
21
- aria-hidden="true"
22
- {...rest}>
23
- <circle cx="12" cy="12" r="10" stroke={color} strokeWidth="4" strokeOpacity="0.25" />
24
- <path fill={color} d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
25
- </svg>
26
- )
27
- }