@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,58 @@
1
+ "use client"
2
+
3
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@rp/ui/components/tooltip"
4
+ import React from "react"
5
+ import { useMediaContext } from "../context/media-context"
6
+
7
+ export interface MediaPlayerTooltipProps {
8
+ children: React.ReactNode
9
+ tooltip?: string
10
+ shortcut?: string | string[]
11
+ delayDuration?: number
12
+ sideOffset?: number
13
+ }
14
+
15
+ export function MediaPlayerTooltip({
16
+ children,
17
+ tooltip,
18
+ shortcut,
19
+ delayDuration,
20
+ sideOffset
21
+ }: MediaPlayerTooltipProps) {
22
+ const context = useMediaContext()
23
+
24
+ if (context.withoutTooltip || !tooltip) {
25
+ return <>{children}</>
26
+ }
27
+
28
+ const tooltipDelayDuration = delayDuration ?? context.tooltipDelayDuration
29
+ const tooltipSideOffset = sideOffset ?? context.tooltipSideOffset
30
+
31
+ return (
32
+ <Tooltip delayDuration={tooltipDelayDuration}>
33
+ <TooltipTrigger asChild>{children}</TooltipTrigger>
34
+ <TooltipContent sideOffset={tooltipSideOffset} className="flex items-center gap-2 text-sm">
35
+ <span>{tooltip}</span>
36
+ {shortcut && (
37
+ <div className="flex items-center gap-1">
38
+ {Array.isArray(shortcut) ? (
39
+ shortcut.map((key, index) => (
40
+ <kbd
41
+ key={index}
42
+ className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-medium font-mono text-[10px] text-muted-foreground opacity-100">
43
+ {key}
44
+ </kbd>
45
+ ))
46
+ ) : (
47
+ <kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-medium font-mono text-[10px] text-muted-foreground opacity-100">
48
+ {shortcut}
49
+ </kbd>
50
+ )}
51
+ </div>
52
+ )}
53
+ </TooltipContent>
54
+ </Tooltip>
55
+ )
56
+ }
57
+
58
+ MediaPlayerTooltip.displayName = "MediaPlayerTooltip"
@@ -0,0 +1,39 @@
1
+ "use client"
2
+
3
+ import React from "react"
4
+ import { useMediaContext } from "../context/media-context"
5
+
6
+ export interface MediaPlayerVideoProps extends React.VideoHTMLAttributes<HTMLVideoElement> {
7
+ asChild?: boolean
8
+ }
9
+
10
+ export function MediaPlayerVideo({
11
+ asChild = false,
12
+ className,
13
+ children,
14
+ ...props
15
+ }: MediaPlayerVideoProps) {
16
+ const context = useMediaContext()
17
+
18
+ if (asChild) {
19
+ // When asChild is true, clone the child element and add our props
20
+ const child = React.Children.only(children) as React.ReactElement
21
+ return React.cloneElement(child, {
22
+ ...props,
23
+ ref: context.mediaRef,
24
+ "data-slot": "media-player-video"
25
+ } as any)
26
+ }
27
+
28
+ return (
29
+ <video
30
+ ref={context.mediaRef as React.RefObject<HTMLVideoElement>}
31
+ data-slot="media-player-video"
32
+ className={className}
33
+ {...props}>
34
+ {children}
35
+ </video>
36
+ )
37
+ }
38
+
39
+ MediaPlayerVideo.displayName = "MediaPlayerVideo"
@@ -0,0 +1,155 @@
1
+ "use client"
2
+
3
+ import { Button } from "@rp/ui/components/button"
4
+ import { Slider } from "@rp/ui/components/slider"
5
+ import { Volume1Icon, Volume2Icon, VolumeXIcon } from "lucide-react"
6
+ import React from "react"
7
+ import {
8
+ MediaActionTypes,
9
+ useMediaContext,
10
+ useMediaDispatch,
11
+ useMediaSelector
12
+ } from "../context/media-context"
13
+ import { cn } from "../lib/utils"
14
+ import { MediaPlayerTooltip } from "./media-player-tooltip"
15
+
16
+ export interface MediaPlayerVolumeProps {
17
+ className?: string
18
+ disabled?: boolean
19
+ asChild?: boolean
20
+ expandable?: boolean
21
+ children?: React.ReactNode
22
+ }
23
+
24
+ export function MediaPlayerVolume({
25
+ className,
26
+ disabled = false,
27
+ asChild = false,
28
+ expandable = true,
29
+ children,
30
+ ...props
31
+ }: MediaPlayerVolumeProps) {
32
+ const context = useMediaContext()
33
+ const dispatch = useMediaDispatch()
34
+
35
+ const volume = useMediaSelector((state) => state.volume)
36
+ const isMuted = useMediaSelector((state) => state.isMuted)
37
+ const isDisabled = disabled || useMediaSelector((state) => state.error !== null)
38
+
39
+ // Determine volume level for icon
40
+ const volumeLevel = React.useMemo(() => {
41
+ if (isMuted || volume === 0) return 0
42
+ if (volume < 0.5) return 1
43
+ return 2
44
+ }, [volume, isMuted])
45
+
46
+ const handleMuteToggle = React.useCallback(() => {
47
+ dispatch({
48
+ type: MediaActionTypes.TOGGLE_MUTE
49
+ })
50
+ }, [dispatch])
51
+
52
+ const handleVolumeChange = React.useCallback(
53
+ (values: number[]) => {
54
+ const newVolume = values[0]
55
+ dispatch({
56
+ type: MediaActionTypes.SET_VOLUME,
57
+ detail: newVolume
58
+ })
59
+ },
60
+ [dispatch]
61
+ )
62
+
63
+ const handleVolumeCommit = React.useCallback(
64
+ (values: number[]) => {
65
+ const newVolume = values[0]
66
+ dispatch({
67
+ type: MediaActionTypes.SET_VOLUME,
68
+ detail: newVolume
69
+ })
70
+ },
71
+ [dispatch]
72
+ )
73
+
74
+ const VolumeIcon = volumeLevel === 0 ? VolumeXIcon : volumeLevel === 1 ? Volume1Icon : Volume2Icon
75
+
76
+ const containerProps = {
77
+ "data-disabled": isDisabled ? "" : undefined,
78
+ "data-slot": "media-player-volume",
79
+ className: cn(
80
+ "group flex items-center",
81
+ expandable ? "gap-2 transition-all duration-200 hover:gap-3" : "gap-2",
82
+ className
83
+ ),
84
+ ...props
85
+ }
86
+
87
+ const buttonProps = {
88
+ type: "button" as const,
89
+ "aria-controls": context.mediaId,
90
+ "aria-label": isMuted ? "Unmute" : "Mute",
91
+ "aria-pressed": isMuted,
92
+ "data-slot": "media-player-volume-button",
93
+ "data-state": isMuted ? "muted" : "unmuted",
94
+ variant: "ghost" as const,
95
+ size: "icon" as const,
96
+ className: "size-8 shrink-0",
97
+ disabled: isDisabled,
98
+ onClick: handleMuteToggle
99
+ }
100
+
101
+ const sliderProps = {
102
+ "aria-controls": context.mediaId,
103
+ "aria-valuetext": `Volume ${Math.round(volume * 100)}%`,
104
+ "data-slot": "media-player-volume-slider",
105
+ min: 0,
106
+ max: 1,
107
+ step: 0.01,
108
+ value: [isMuted ? 0 : volume],
109
+ onValueChange: handleVolumeChange,
110
+ onValueCommit: handleVolumeCommit,
111
+ disabled: isDisabled,
112
+ className: cn(
113
+ "relative flex touch-none select-none items-center",
114
+ expandable
115
+ ? "w-0 opacity-0 transition-all duration-200 group-hover:w-20 group-hover:opacity-100"
116
+ : "w-20",
117
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
118
+ )
119
+ }
120
+
121
+ const content = (
122
+ <div {...containerProps}>
123
+ <MediaPlayerTooltip tooltip={isMuted ? "Unmute" : "Mute"} shortcut="M">
124
+ <Button {...buttonProps}>
125
+ <VolumeIcon className="size-4" />
126
+ </Button>
127
+ </MediaPlayerTooltip>
128
+
129
+ <Slider {...sliderProps}>
130
+ <div className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
131
+ <div
132
+ className="h-full bg-primary transition-all"
133
+ style={{ width: `${(isMuted ? 0 : volume) * 100}%` }}
134
+ />
135
+ </div>
136
+ <div
137
+ className="block h-4 w-4 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
138
+ style={{
139
+ transform: `translateX(-50%)`,
140
+ left: `${(isMuted ? 0 : volume) * 100}%`
141
+ }}
142
+ />
143
+ </Slider>
144
+ </div>
145
+ )
146
+
147
+ if (asChild) {
148
+ const child = React.Children.only(children) as React.ReactElement
149
+ return React.cloneElement(child, containerProps as any, content)
150
+ }
151
+
152
+ return content
153
+ }
154
+
155
+ MediaPlayerVolume.displayName = "MediaPlayerVolume"
@@ -0,0 +1,257 @@
1
+ "use client"
2
+
3
+ import { forwardRef, useMemo, useTransition, startTransition } from "react"
4
+ import { useKeyboardControls } from "../hooks/use-keyboard-controls"
5
+ import { useEnhancedMediaPlayer } from "../hooks/use-enhanced-media-player"
6
+ import { useMemoryOptimization } from "../hooks/use-memory-optimization"
7
+ import { EnhancedMediaWrapper } from "./media-suspense"
8
+ import { cn } from "../lib/utils"
9
+ import type { MediaPlayerProps } from "../types/index"
10
+
11
+ export const MediaPlayer = forwardRef<HTMLDivElement, MediaPlayerProps>(
12
+ (
13
+ {
14
+ src,
15
+ className,
16
+ children,
17
+ keyboardControls = true,
18
+ showControls = true,
19
+ customControls = false,
20
+ crossOrigin,
21
+ enablePerformanceMonitoring = true,
22
+ bufferingStrategy = "adaptive",
23
+ preloadStrategy = "progressive",
24
+ enableOptimizations = true,
25
+ ...options
26
+ },
27
+ ref
28
+ ) => {
29
+ // React 19 concurrent features
30
+ const [isPending] = useTransition()
31
+
32
+ // Enhanced media player with optimizations
33
+ const { state, controls, mediaRef, containerRef, performanceMetrics, isEnhanced } = useEnhancedMediaPlayer({
34
+ src,
35
+ crossOrigin,
36
+ enablePerformanceMonitoring,
37
+ bufferingStrategy,
38
+ preloadStrategy,
39
+ enableTimeUpdateThrottling: true,
40
+ timeUpdateThrottleMs: 100,
41
+ enableSeekPreview: true,
42
+ ...options
43
+ })
44
+
45
+ // Memory optimization
46
+ const { memoryStats, addEventListener } = useMemoryOptimization({
47
+ maxMemoryUsage: 50, // 50MB for media player
48
+ cleanupInterval: 15000, // 15 seconds
49
+ enableGC: true,
50
+ monitorMemory: enableOptimizations
51
+ })
52
+
53
+ // Keyboard controls with performance optimization
54
+ useKeyboardControls({
55
+ enabled: keyboardControls,
56
+ state,
57
+ controls
58
+ })
59
+
60
+ // Memoize media type detection
61
+ const isVideo = useMemo(() => {
62
+ return Array.isArray(src)
63
+ ? src.some((s) => /\.(mp4|webm|ogv|mov|avi)$/i.test(s))
64
+ : /\.(mp4|webm|ogv|mov|avi)$/i.test(src)
65
+ }, [src])
66
+
67
+ // Memoize source rendering
68
+ const renderSources = useMemo(() => {
69
+ if (!Array.isArray(src)) return null
70
+
71
+ return src.map((source, index) => (
72
+ <source key={`${source}-${index}`} src={source} />
73
+ ))
74
+ }, [src])
75
+
76
+ // Memoize common props
77
+ const commonProps = useMemo(() => ({
78
+ controls: showControls && !customControls,
79
+ className: cn(
80
+ "w-full transition-all duration-300",
81
+ isVideo && "aspect-video",
82
+ "bg-[color:var(--media-bg)] rounded-lg",
83
+ state.isTransitioning && "opacity-90"
84
+ ),
85
+ preload: preloadStrategy === "eager" ? "auto" : "metadata",
86
+ crossOrigin,
87
+ ...(Array.isArray(src) ? {} : { src })
88
+ }), [showControls, customControls, isVideo, state.isTransitioning, preloadStrategy, crossOrigin, src])
89
+
90
+ return (
91
+ <EnhancedMediaWrapper
92
+ priority="high"
93
+ errorFallback={({ error, resetError }) => (
94
+ <div className={cn(
95
+ "absolute inset-0 flex items-center justify-center",
96
+ "glass-strong animate-enter rounded-lg",
97
+ "bg-[color:var(--color-error)]/10 backdrop-blur-md"
98
+ )}>
99
+ <div className="space-y-2 text-center text-[color:var(--media-text)]">
100
+ <div className="mx-auto w-fit rounded-full bg-[color:var(--color-error)]/20 p-3">
101
+ <svg className="size-6 text-[color:var(--color-error)]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
102
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
103
+ </svg>
104
+ </div>
105
+ <p className="font-semibold text-lg">Playback Error</p>
106
+ <p className="max-w-xs text-[color:var(--media-text-secondary)] text-sm">
107
+ {error.message || state.error}
108
+ </p>
109
+ <button
110
+ onClick={resetError}
111
+ className="mt-2 rounded bg-[color:var(--color-brand-primary)] px-4 py-2 text-sm text-white transition-colors hover:bg-[color:var(--color-brand-accent)]">
112
+ Try Again
113
+ </button>
114
+ </div>
115
+ </div>
116
+ )}
117
+ loadingFallback={
118
+ <div className={cn(
119
+ "absolute inset-0 flex items-center justify-center",
120
+ "glass animate-enter rounded-lg",
121
+ "bg-[color:var(--media-bg)]/20 backdrop-blur-sm"
122
+ )}>
123
+ <div className="space-y-4 text-center text-[color:var(--media-text)]">
124
+ <div className="relative">
125
+ <div className={cn(
126
+ "size-12 animate-spin rounded-full border-2",
127
+ "border-[color:var(--color-brand-accent)]/30",
128
+ "border-t-[color:var(--color-brand-accent)]"
129
+ )}></div>
130
+ <div className={cn(
131
+ "absolute inset-0 size-12 rounded-full",
132
+ "bg-gradient-to-r from-[color:var(--color-brand-primary)] to-[color:var(--color-brand-accent)]",
133
+ "animate-pulse opacity-20 blur-sm"
134
+ )}></div>
135
+ </div>
136
+ <p className="font-medium text-[color:var(--media-text-secondary)] text-sm">
137
+ Loading media...
138
+ </p>
139
+ </div>
140
+ </div>
141
+ }>
142
+ <div
143
+ ref={ref || containerRef}
144
+ className={cn(
145
+ "media-container relative overflow-hidden contain-strict",
146
+ "bg-[color:var(--media-bg)] focus-within:outline-none",
147
+ "rounded-xl border border-[color:var(--media-border)]/30",
148
+ "shadow-2xl shadow-[color:var(--media-bg)]/50",
149
+ "transition-all duration-300 ease-out",
150
+ "hover:border-[color:var(--media-border)]/50",
151
+ "hover:shadow-3xl hover:shadow-[color:var(--media-bg)]/60",
152
+ // Performance optimizations
153
+ "will-change-transform",
154
+ state.isTransitioning && "cursor-wait",
155
+ className
156
+ )}
157
+ tabIndex={0}
158
+ // Performance attributes
159
+ role="application"
160
+ aria-label={isVideo ? "Video player" : "Audio player"}
161
+ aria-busy={state.isLoading || state.isBuffering}>
162
+
163
+ {isVideo ? (
164
+ <video
165
+ ref={mediaRef as React.RefObject<HTMLVideoElement>}
166
+ {...commonProps}
167
+ // Performance optimizations
168
+ disablePictureInPicture={false}
169
+ playsInline
170
+ x-webkit-airplay="allow"
171
+ style={{ willChange: 'transform' }}
172
+ >
173
+ {renderSources()}
174
+ Your browser does not support the video element.
175
+ </video>
176
+ ) : (
177
+ <audio
178
+ ref={mediaRef as React.RefObject<HTMLAudioElement>}
179
+ {...commonProps}
180
+ // Performance optimizations
181
+ preload={preloadStrategy === "eager" ? "auto" : "metadata"}
182
+ >
183
+ {renderSources()}
184
+ Your browser does not support the audio element.
185
+ </audio>
186
+ )}
187
+
188
+ {state.error && (
189
+ <div className={cn(
190
+ "absolute inset-0 flex items-center justify-center",
191
+ "glass-strong animate-enter rounded-lg",
192
+ "bg-[color:var(--color-error)]/10 backdrop-blur-md"
193
+ )}>
194
+ <div className="space-y-2 text-center text-[color:var(--media-text)]">
195
+ <div className="mx-auto w-fit rounded-full bg-[color:var(--color-error)]/20 p-3">
196
+ <svg className="size-6 text-[color:var(--color-error)]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
197
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
198
+ </svg>
199
+ </div>
200
+ <p className="font-semibold text-lg">Playback Error</p>
201
+ <p className="max-w-xs text-[color:var(--media-text-secondary)] text-sm">
202
+ {state.error}
203
+ </p>
204
+ </div>
205
+ </div>
206
+ )}
207
+
208
+ {state.isLoading && (
209
+ <div className={cn(
210
+ "absolute inset-0 flex items-center justify-center",
211
+ "glass animate-enter rounded-lg",
212
+ "bg-[color:var(--media-bg)]/20 backdrop-blur-sm"
213
+ )}>
214
+ <div className="space-y-4 text-center text-[color:var(--media-text)]">
215
+ <div className="relative">
216
+ <div className={cn(
217
+ "size-12 animate-spin rounded-full border-2",
218
+ "border-[color:var(--color-brand-accent)]/30",
219
+ "border-t-[color:var(--color-brand-accent)]"
220
+ )}></div>
221
+ <div className={cn(
222
+ "absolute inset-0 size-12 rounded-full",
223
+ "bg-gradient-to-r from-[color:var(--color-brand-primary)] to-[color:var(--color-brand-accent)]",
224
+ "animate-pulse opacity-20 blur-sm"
225
+ )}></div>
226
+ </div>
227
+ <p className="font-medium text-[color:var(--media-text-secondary)] text-sm">
228
+ {state.isBuffering ? "Buffering..." : "Loading media..."}
229
+ </p>
230
+ </div>
231
+ </div>
232
+ )}
233
+
234
+ {customControls && typeof children === "function" && (
235
+ <div className="pointer-events-auto absolute inset-0 z-50">
236
+ {children({ state, controls })}
237
+ </div>
238
+ )}
239
+
240
+ {customControls && typeof children !== "function" && (
241
+ <div className="pointer-events-auto absolute inset-0 z-50">{children}</div>
242
+ )}
243
+
244
+ {/* Performance indicator for development */}
245
+ {process.env.NODE_ENV === 'development' && enablePerformanceMonitoring && (
246
+ <div className="absolute bottom-2 left-2 rounded bg-black/50 px-2 py-1 text-white text-xs">
247
+ <div>FPS: {performanceMetrics.frameRate || 0}</div>
248
+ <div>Memory: {memoryStats.usage.toFixed(1)}MB</div>
249
+ {isEnhanced && <div>Enhanced ✓</div>}
250
+ </div>
251
+ )}
252
+ </div>
253
+ </EnhancedMediaWrapper>
254
+ )
255
+ })
256
+
257
+ MediaPlayer.displayName = "MediaPlayer"