@livepeer-frameworks/player-svelte 0.0.3

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 (169) hide show
  1. package/dist/DevModePanel.svelte +650 -0
  2. package/dist/DevModePanel.svelte.d.ts +31 -0
  3. package/dist/DvdLogo.svelte +213 -0
  4. package/dist/DvdLogo.svelte.d.ts +7 -0
  5. package/dist/Icons.svelte +27 -0
  6. package/dist/Icons.svelte.d.ts +25 -0
  7. package/dist/IdleScreen.svelte +752 -0
  8. package/dist/IdleScreen.svelte.d.ts +11 -0
  9. package/dist/LoadingScreen.svelte +689 -0
  10. package/dist/LoadingScreen.svelte.d.ts +7 -0
  11. package/dist/Player.svelte +482 -0
  12. package/dist/Player.svelte.d.ts +26 -0
  13. package/dist/PlayerControls.svelte +739 -0
  14. package/dist/PlayerControls.svelte.d.ts +20 -0
  15. package/dist/SeekBar.svelte +274 -0
  16. package/dist/SeekBar.svelte.d.ts +25 -0
  17. package/dist/SkipIndicator.svelte +95 -0
  18. package/dist/SkipIndicator.svelte.d.ts +14 -0
  19. package/dist/SpeedIndicator.svelte +38 -0
  20. package/dist/SpeedIndicator.svelte.d.ts +8 -0
  21. package/dist/StatsPanel.svelte +155 -0
  22. package/dist/StatsPanel.svelte.d.ts +27 -0
  23. package/dist/StreamStateOverlay.svelte +266 -0
  24. package/dist/StreamStateOverlay.svelte.d.ts +18 -0
  25. package/dist/SubtitleRenderer.svelte +234 -0
  26. package/dist/SubtitleRenderer.svelte.d.ts +41 -0
  27. package/dist/ThumbnailOverlay.svelte +96 -0
  28. package/dist/ThumbnailOverlay.svelte.d.ts +11 -0
  29. package/dist/TitleOverlay.svelte +47 -0
  30. package/dist/TitleOverlay.svelte.d.ts +9 -0
  31. package/dist/assets/logomark.svg +56 -0
  32. package/dist/components/VolumeIcons.svelte +53 -0
  33. package/dist/components/VolumeIcons.svelte.d.ts +10 -0
  34. package/dist/global.d.ts +15 -0
  35. package/dist/icons/FullscreenExitIcon.svelte +33 -0
  36. package/dist/icons/FullscreenExitIcon.svelte.d.ts +8 -0
  37. package/dist/icons/FullscreenIcon.svelte +33 -0
  38. package/dist/icons/FullscreenIcon.svelte.d.ts +8 -0
  39. package/dist/icons/PauseIcon.svelte +28 -0
  40. package/dist/icons/PauseIcon.svelte.d.ts +8 -0
  41. package/dist/icons/PictureInPictureIcon.svelte +28 -0
  42. package/dist/icons/PictureInPictureIcon.svelte.d.ts +8 -0
  43. package/dist/icons/PlayIcon.svelte +27 -0
  44. package/dist/icons/PlayIcon.svelte.d.ts +8 -0
  45. package/dist/icons/SeekToLiveIcon.svelte +30 -0
  46. package/dist/icons/SeekToLiveIcon.svelte.d.ts +8 -0
  47. package/dist/icons/SettingsIcon.svelte +40 -0
  48. package/dist/icons/SettingsIcon.svelte.d.ts +8 -0
  49. package/dist/icons/SkipBackIcon.svelte +32 -0
  50. package/dist/icons/SkipBackIcon.svelte.d.ts +8 -0
  51. package/dist/icons/SkipForwardIcon.svelte +32 -0
  52. package/dist/icons/SkipForwardIcon.svelte.d.ts +8 -0
  53. package/dist/icons/StatsIcon.svelte +29 -0
  54. package/dist/icons/StatsIcon.svelte.d.ts +8 -0
  55. package/dist/icons/VolumeOffIcon.svelte +29 -0
  56. package/dist/icons/VolumeOffIcon.svelte.d.ts +8 -0
  57. package/dist/icons/VolumeUpIcon.svelte +34 -0
  58. package/dist/icons/VolumeUpIcon.svelte.d.ts +8 -0
  59. package/dist/icons/index.d.ts +17 -0
  60. package/dist/icons/index.js +17 -0
  61. package/dist/index.d.ts +50 -0
  62. package/dist/index.js +54 -0
  63. package/dist/player.css +2 -0
  64. package/dist/stores/index.d.ts +15 -0
  65. package/dist/stores/index.js +21 -0
  66. package/dist/stores/playbackQuality.d.ts +43 -0
  67. package/dist/stores/playbackQuality.js +107 -0
  68. package/dist/stores/playerContext.d.ts +73 -0
  69. package/dist/stores/playerContext.js +166 -0
  70. package/dist/stores/playerController.d.ts +178 -0
  71. package/dist/stores/playerController.js +358 -0
  72. package/dist/stores/playerSelection.d.ts +84 -0
  73. package/dist/stores/playerSelection.js +159 -0
  74. package/dist/stores/streamState.d.ts +44 -0
  75. package/dist/stores/streamState.js +314 -0
  76. package/dist/stores/viewerEndpoints.d.ts +48 -0
  77. package/dist/stores/viewerEndpoints.js +178 -0
  78. package/dist/types.d.ts +4 -0
  79. package/dist/types.js +4 -0
  80. package/dist/ui/Badge.svelte +21 -0
  81. package/dist/ui/Badge.svelte.d.ts +32 -0
  82. package/dist/ui/Button.svelte +42 -0
  83. package/dist/ui/Button.svelte.d.ts +35 -0
  84. package/dist/ui/Slider.svelte +100 -0
  85. package/dist/ui/Slider.svelte.d.ts +17 -0
  86. package/dist/ui/badge.d.ts +6 -0
  87. package/dist/ui/badge.js +10 -0
  88. package/dist/ui/button.d.ts +8 -0
  89. package/dist/ui/button.js +21 -0
  90. package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte +34 -0
  91. package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte.d.ts +31 -0
  92. package/dist/ui/context-menu/ContextMenuContent.svelte +17 -0
  93. package/dist/ui/context-menu/ContextMenuContent.svelte.d.ts +7 -0
  94. package/dist/ui/context-menu/ContextMenuItem.svelte +22 -0
  95. package/dist/ui/context-menu/ContextMenuItem.svelte.d.ts +8 -0
  96. package/dist/ui/context-menu/ContextMenuLabel.svelte +22 -0
  97. package/dist/ui/context-menu/ContextMenuLabel.svelte.d.ts +8 -0
  98. package/dist/ui/context-menu/ContextMenuPortal.svelte +11 -0
  99. package/dist/ui/context-menu/ContextMenuPortal.svelte.d.ts +6 -0
  100. package/dist/ui/context-menu/ContextMenuRadioItem.svelte +21 -0
  101. package/dist/ui/context-menu/ContextMenuRadioItem.svelte.d.ts +31 -0
  102. package/dist/ui/context-menu/ContextMenuSeparator.svelte +14 -0
  103. package/dist/ui/context-menu/ContextMenuSeparator.svelte.d.ts +6 -0
  104. package/dist/ui/context-menu/ContextMenuShortcut.svelte +19 -0
  105. package/dist/ui/context-menu/ContextMenuShortcut.svelte.d.ts +7 -0
  106. package/dist/ui/context-menu/ContextMenuSubContent.svelte +20 -0
  107. package/dist/ui/context-menu/ContextMenuSubContent.svelte.d.ts +7 -0
  108. package/dist/ui/context-menu/ContextMenuSubTrigger.svelte +34 -0
  109. package/dist/ui/context-menu/ContextMenuSubTrigger.svelte.d.ts +8 -0
  110. package/dist/ui/context-menu/index.d.ts +17 -0
  111. package/dist/ui/context-menu/index.js +17 -0
  112. package/package.json +51 -0
  113. package/src/DevModePanel.svelte +650 -0
  114. package/src/DvdLogo.svelte +213 -0
  115. package/src/Icons.svelte +27 -0
  116. package/src/IdleScreen.svelte +739 -0
  117. package/src/LoadingScreen.svelte +674 -0
  118. package/src/Player.svelte +483 -0
  119. package/src/PlayerControls.svelte +752 -0
  120. package/src/SeekBar.svelte +274 -0
  121. package/src/SkipIndicator.svelte +95 -0
  122. package/src/SpeedIndicator.svelte +37 -0
  123. package/src/StatsPanel.svelte +155 -0
  124. package/src/StreamStateOverlay.svelte +266 -0
  125. package/src/SubtitleRenderer.svelte +234 -0
  126. package/src/ThumbnailOverlay.svelte +96 -0
  127. package/src/TitleOverlay.svelte +47 -0
  128. package/src/assets/logomark.svg +56 -0
  129. package/src/components/VolumeIcons.svelte +53 -0
  130. package/src/global.d.ts +15 -0
  131. package/src/icons/FullscreenExitIcon.svelte +33 -0
  132. package/src/icons/FullscreenIcon.svelte +33 -0
  133. package/src/icons/PauseIcon.svelte +28 -0
  134. package/src/icons/PictureInPictureIcon.svelte +28 -0
  135. package/src/icons/PlayIcon.svelte +27 -0
  136. package/src/icons/SeekToLiveIcon.svelte +30 -0
  137. package/src/icons/SettingsIcon.svelte +40 -0
  138. package/src/icons/SkipBackIcon.svelte +32 -0
  139. package/src/icons/SkipForwardIcon.svelte +32 -0
  140. package/src/icons/StatsIcon.svelte +29 -0
  141. package/src/icons/VolumeOffIcon.svelte +29 -0
  142. package/src/icons/VolumeUpIcon.svelte +34 -0
  143. package/src/icons/index.ts +18 -0
  144. package/src/index.ts +84 -0
  145. package/src/player.css +2 -0
  146. package/src/stores/index.ts +88 -0
  147. package/src/stores/playbackQuality.ts +137 -0
  148. package/src/stores/playerContext.ts +221 -0
  149. package/src/stores/playerController.ts +568 -0
  150. package/src/stores/playerSelection.ts +216 -0
  151. package/src/stores/streamState.ts +367 -0
  152. package/src/stores/viewerEndpoints.ts +224 -0
  153. package/src/types.ts +6 -0
  154. package/src/ui/Badge.svelte +21 -0
  155. package/src/ui/Button.svelte +42 -0
  156. package/src/ui/Slider.svelte +100 -0
  157. package/src/ui/badge.ts +20 -0
  158. package/src/ui/button.ts +35 -0
  159. package/src/ui/context-menu/ContextMenuCheckboxItem.svelte +34 -0
  160. package/src/ui/context-menu/ContextMenuContent.svelte +17 -0
  161. package/src/ui/context-menu/ContextMenuItem.svelte +22 -0
  162. package/src/ui/context-menu/ContextMenuLabel.svelte +22 -0
  163. package/src/ui/context-menu/ContextMenuPortal.svelte +11 -0
  164. package/src/ui/context-menu/ContextMenuRadioItem.svelte +21 -0
  165. package/src/ui/context-menu/ContextMenuSeparator.svelte +14 -0
  166. package/src/ui/context-menu/ContextMenuShortcut.svelte +19 -0
  167. package/src/ui/context-menu/ContextMenuSubContent.svelte +20 -0
  168. package/src/ui/context-menu/ContextMenuSubTrigger.svelte +34 -0
  169. package/src/ui/context-menu/index.ts +36 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Svelte stores for player state management.
3
+ *
4
+ * These stores provide reactive state management for:
5
+ * - MistServer stream state polling (WebSocket/HTTP)
6
+ * - Gateway endpoint resolution (GraphQL)
7
+ * - Player instance context sharing
8
+ * - Playback quality monitoring
9
+ */
10
+
11
+ // Stream state (MistServer polling)
12
+ export {
13
+ createStreamStateManager,
14
+ createDerivedStreamStatus,
15
+ createDerivedIsOnline,
16
+ createDerivedStreamInfo,
17
+ type StreamStateOptions,
18
+ type StreamStateStore,
19
+ } from './streamState';
20
+
21
+ // Viewer endpoints (Gateway resolution)
22
+ export {
23
+ createEndpointResolver,
24
+ createDerivedEndpoints,
25
+ createDerivedPrimaryEndpoint,
26
+ createDerivedMetadata,
27
+ createDerivedStatus,
28
+ type ViewerEndpointsOptions,
29
+ type ViewerEndpointsStore,
30
+ type ViewerEndpointsState,
31
+ type EndpointStatus,
32
+ } from './viewerEndpoints';
33
+
34
+ // Player context (instance sharing)
35
+ export {
36
+ createPlayerContext,
37
+ setPlayerContextInComponent,
38
+ getPlayerContextFromComponent,
39
+ getPlayerContextOrFallback,
40
+ createDerivedVideoElement,
41
+ createDerivedIsReady,
42
+ createDerivedPlayerInfo,
43
+ type PlayerContextState,
44
+ type PlayerContextStore,
45
+ } from './playerContext';
46
+
47
+ // Playback quality monitoring
48
+ export {
49
+ createPlaybackQualityMonitor,
50
+ createDerivedQualityScore,
51
+ createDerivedStallCount,
52
+ createDerivedFrameDropRate,
53
+ createDerivedBitrate,
54
+ createDerivedLatency,
55
+ type PlaybackQualityOptions,
56
+ type PlaybackQualityStore,
57
+ } from './playbackQuality';
58
+
59
+ // Player selection (event-driven, cached)
60
+ export {
61
+ createPlayerSelectionStore,
62
+ createDerivedSelection,
63
+ createDerivedCombinations,
64
+ createDerivedReady,
65
+ createDerivedSelectedPlayer,
66
+ createDerivedSelectedSourceType,
67
+ createDerivedCompatibleCombinations,
68
+ createDerivedIncompatibleCombinations,
69
+ type PlayerSelectionOptions,
70
+ type PlayerSelectionState,
71
+ type PlayerSelectionStore,
72
+ } from './playerSelection';
73
+
74
+ // PlayerController store (central orchestrator)
75
+ export {
76
+ createPlayerControllerStore,
77
+ createDerivedState,
78
+ createDerivedIsPlaying,
79
+ createDerivedCurrentTime,
80
+ createDerivedDuration,
81
+ createDerivedError,
82
+ createDerivedVideoElement as createDerivedControllerVideoElement,
83
+ createDerivedShouldShowControls,
84
+ createDerivedShouldShowIdleScreen,
85
+ type PlayerControllerStoreConfig,
86
+ type PlayerControllerState,
87
+ type PlayerControllerStore,
88
+ } from './playerController';
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Svelte store for playback quality monitoring.
3
+ *
4
+ * Wraps QualityMonitor for reactive quality metrics.
5
+ */
6
+
7
+ import { writable, derived, type Readable } from 'svelte/store';
8
+ import { QualityMonitor, type PlaybackQuality } from '@livepeer-frameworks/player-core';
9
+
10
+ export interface PlaybackQualityOptions {
11
+ sampleInterval?: number;
12
+ enabled?: boolean;
13
+ }
14
+
15
+ export interface PlaybackQualityStore extends Readable<PlaybackQuality | null> {
16
+ start: (videoElement: HTMLVideoElement) => void;
17
+ stop: () => void;
18
+ getPlaybackScore: () => number;
19
+ destroy: () => void;
20
+ }
21
+
22
+ const initialQuality: PlaybackQuality = {
23
+ score: 100,
24
+ bitrate: 0,
25
+ bufferedAhead: 0,
26
+ frameDropRate: 0,
27
+ stallCount: 0,
28
+ latency: 0,
29
+ timestamp: Date.now(),
30
+ };
31
+
32
+ /**
33
+ * Create a playback quality monitoring store.
34
+ *
35
+ * @example
36
+ * ```svelte
37
+ * <script>
38
+ * import { createPlaybackQualityMonitor } from './stores/playbackQuality';
39
+ *
40
+ * const quality = createPlaybackQualityMonitor();
41
+ *
42
+ * // Start monitoring when video element is available
43
+ * $: if (videoElement) quality.start(videoElement);
44
+ *
45
+ * // Access quality metrics
46
+ * $: score = $quality?.score ?? 100;
47
+ * $: stallCount = $quality?.stallCount ?? 0;
48
+ * </script>
49
+ * ```
50
+ */
51
+ export function createPlaybackQualityMonitor(options: PlaybackQualityOptions = {}): PlaybackQualityStore {
52
+ const { sampleInterval = 500, enabled = true } = options;
53
+
54
+ const store = writable<PlaybackQuality | null>(null);
55
+ let monitor: QualityMonitor | null = null;
56
+ let currentVideoElement: HTMLVideoElement | null = null;
57
+
58
+ /**
59
+ * Start monitoring a video element
60
+ */
61
+ function start(videoElement: HTMLVideoElement) {
62
+ if (!enabled) return;
63
+
64
+ // Stop existing monitor if different element
65
+ if (currentVideoElement && currentVideoElement !== videoElement) {
66
+ stop();
67
+ }
68
+
69
+ currentVideoElement = videoElement;
70
+
71
+ if (!monitor) {
72
+ monitor = new QualityMonitor({
73
+ sampleInterval,
74
+ onSample: (quality) => {
75
+ store.set(quality);
76
+ },
77
+ });
78
+ }
79
+
80
+ monitor.start(videoElement);
81
+ }
82
+
83
+ /**
84
+ * Stop monitoring
85
+ */
86
+ function stop() {
87
+ monitor?.stop();
88
+ currentVideoElement = null;
89
+ }
90
+
91
+ /**
92
+ * Get current playback score
93
+ */
94
+ function getPlaybackScore(): number {
95
+ return monitor?.getPlaybackScore() ?? 1.0;
96
+ }
97
+
98
+ /**
99
+ * Cleanup
100
+ */
101
+ function destroy() {
102
+ stop();
103
+ monitor = null;
104
+ store.set(null);
105
+ }
106
+
107
+ return {
108
+ subscribe: store.subscribe,
109
+ start,
110
+ stop,
111
+ getPlaybackScore,
112
+ destroy,
113
+ };
114
+ }
115
+
116
+ // Convenience derived stores
117
+ export function createDerivedQualityScore(store: PlaybackQualityStore) {
118
+ return derived(store, $quality => $quality?.score ?? 100);
119
+ }
120
+
121
+ export function createDerivedStallCount(store: PlaybackQualityStore) {
122
+ return derived(store, $quality => $quality?.stallCount ?? 0);
123
+ }
124
+
125
+ export function createDerivedFrameDropRate(store: PlaybackQualityStore) {
126
+ return derived(store, $quality => $quality?.frameDropRate ?? 0);
127
+ }
128
+
129
+ export function createDerivedBitrate(store: PlaybackQualityStore) {
130
+ return derived(store, $quality => $quality?.bitrate ?? 0);
131
+ }
132
+
133
+ export function createDerivedLatency(store: PlaybackQualityStore) {
134
+ return derived(store, $quality => $quality?.latency);
135
+ }
136
+
137
+ export default createPlaybackQualityMonitor;
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Svelte store for player instance context sharing.
3
+ *
4
+ * Port of PlayerContext.tsx React context to Svelte 5 stores.
5
+ */
6
+
7
+ import { writable, derived, type Readable, type Writable } from 'svelte/store';
8
+ import { getContext, setContext } from 'svelte';
9
+ import { globalPlayerManager, type StreamInfo, type IPlayer } from '@livepeer-frameworks/player-core';
10
+
11
+ // Context key
12
+ const PLAYER_CONTEXT_KEY = Symbol('player-context');
13
+
14
+ export interface PlayerContextState {
15
+ /** Current video element (if available) */
16
+ videoElement: HTMLVideoElement | null;
17
+ /** Current player instance */
18
+ player: IPlayer | null;
19
+ /** Player name */
20
+ playerName: string | null;
21
+ /** Player shortname */
22
+ playerShortname: string | null;
23
+ /** Current source type */
24
+ sourceType: string | null;
25
+ /** Current source URL */
26
+ sourceUrl: string | null;
27
+ /** Whether player is ready */
28
+ isReady: boolean;
29
+ /** Current stream info */
30
+ streamInfo: StreamInfo | null;
31
+ }
32
+
33
+ export interface PlayerContextStore extends Readable<PlayerContextState> {
34
+ setVideoElement: (el: HTMLVideoElement | null) => void;
35
+ setPlayer: (player: IPlayer | null) => void;
36
+ setSource: (type: string | null, url: string | null) => void;
37
+ setStreamInfo: (info: StreamInfo | null) => void;
38
+ setReady: (ready: boolean) => void;
39
+ reset: () => void;
40
+ }
41
+
42
+ const initialState: PlayerContextState = {
43
+ videoElement: null,
44
+ player: null,
45
+ playerName: null,
46
+ playerShortname: null,
47
+ sourceType: null,
48
+ sourceUrl: null,
49
+ isReady: false,
50
+ streamInfo: null,
51
+ };
52
+
53
+ /**
54
+ * Create a player context store for sharing player state across components.
55
+ *
56
+ * @example
57
+ * ```svelte
58
+ * <!-- Parent component -->
59
+ * <script>
60
+ * import { createPlayerContext, setPlayerContext } from './stores/playerContext';
61
+ * const playerContext = createPlayerContext();
62
+ * setPlayerContext(playerContext);
63
+ * </script>
64
+ *
65
+ * <!-- Child component -->
66
+ * <script>
67
+ * import { getPlayerContext } from './stores/playerContext';
68
+ * const playerContext = getPlayerContext();
69
+ * $: videoEl = $playerContext.videoElement;
70
+ * </script>
71
+ * ```
72
+ */
73
+ export function createPlayerContext(): PlayerContextStore {
74
+ const store = writable<PlayerContextState>(initialState);
75
+
76
+ function setVideoElement(el: HTMLVideoElement | null) {
77
+ store.update(s => ({ ...s, videoElement: el }));
78
+ }
79
+
80
+ function setPlayer(player: IPlayer | null) {
81
+ store.update(s => ({
82
+ ...s,
83
+ player,
84
+ playerName: player?.capability.name ?? null,
85
+ playerShortname: player?.capability.shortname ?? null,
86
+ }));
87
+ }
88
+
89
+ function setSource(type: string | null, url: string | null) {
90
+ store.update(s => ({ ...s, sourceType: type, sourceUrl: url }));
91
+ }
92
+
93
+ function setStreamInfo(info: StreamInfo | null) {
94
+ store.update(s => ({ ...s, streamInfo: info }));
95
+ }
96
+
97
+ function setReady(ready: boolean) {
98
+ store.update(s => ({ ...s, isReady: ready }));
99
+ }
100
+
101
+ function reset() {
102
+ store.set(initialState);
103
+ }
104
+
105
+ return {
106
+ subscribe: store.subscribe,
107
+ setVideoElement,
108
+ setPlayer,
109
+ setSource,
110
+ setStreamInfo,
111
+ setReady,
112
+ reset,
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Set player context in Svelte context (call in parent component)
118
+ */
119
+ export function setPlayerContextInComponent(context: PlayerContextStore) {
120
+ setContext(PLAYER_CONTEXT_KEY, context);
121
+ }
122
+
123
+ /**
124
+ * Get player context from Svelte context (call in child components)
125
+ */
126
+ export function getPlayerContextFromComponent(): PlayerContextStore | undefined {
127
+ return getContext<PlayerContextStore>(PLAYER_CONTEXT_KEY);
128
+ }
129
+
130
+ /**
131
+ * Get player context or create a fallback that uses globalPlayerManager
132
+ */
133
+ export function getPlayerContextOrFallback(): PlayerContextStore {
134
+ const context = getContext<PlayerContextStore>(PLAYER_CONTEXT_KEY);
135
+ if (context) return context;
136
+
137
+ // Fallback: create a derived context from globalPlayerManager
138
+ return createFallbackContext();
139
+ }
140
+
141
+ /**
142
+ * Create a fallback context that reads from globalPlayerManager
143
+ * Uses event-driven updates instead of polling
144
+ */
145
+ function createFallbackContext(): PlayerContextStore {
146
+ const store = writable<PlayerContextState>(initialState);
147
+
148
+ // Sync state from globalPlayerManager
149
+ function syncState() {
150
+ const player = globalPlayerManager.getCurrentPlayer();
151
+ const videoEl = player?.getVideoElement() ?? null;
152
+
153
+ store.update(s => ({
154
+ ...s,
155
+ videoElement: videoEl,
156
+ player: player ?? null,
157
+ playerName: player?.capability.name ?? null,
158
+ playerShortname: player?.capability.shortname ?? null,
159
+ isReady: !!videoEl,
160
+ }));
161
+ }
162
+
163
+ // Event handlers
164
+ const handleInitialized = () => syncState();
165
+ const handleSelectionChanged = () => syncState();
166
+
167
+ // Start/stop event listeners based on subscriber count
168
+ const originalSubscribe = store.subscribe;
169
+ let subscribers = 0;
170
+
171
+ const subscribe: typeof originalSubscribe = (run, invalidate) => {
172
+ subscribers++;
173
+ if (subscribers === 1) {
174
+ // Initial sync
175
+ syncState();
176
+ // Subscribe to events
177
+ globalPlayerManager.on('playerInitialized', handleInitialized);
178
+ globalPlayerManager.on('selection-changed', handleSelectionChanged);
179
+ }
180
+
181
+ const unsubscribe = originalSubscribe(run, invalidate);
182
+
183
+ return () => {
184
+ unsubscribe();
185
+ subscribers--;
186
+ if (subscribers === 0) {
187
+ // Unsubscribe from events
188
+ globalPlayerManager.off('playerInitialized', handleInitialized);
189
+ globalPlayerManager.off('selection-changed', handleSelectionChanged);
190
+ }
191
+ };
192
+ };
193
+
194
+ return {
195
+ subscribe,
196
+ setVideoElement: () => {},
197
+ setPlayer: () => {},
198
+ setSource: () => {},
199
+ setStreamInfo: () => {},
200
+ setReady: () => {},
201
+ reset: () => {},
202
+ };
203
+ }
204
+
205
+ // Convenience derived stores
206
+ export function createDerivedVideoElement(store: PlayerContextStore) {
207
+ return derived(store, $state => $state.videoElement);
208
+ }
209
+
210
+ export function createDerivedIsReady(store: PlayerContextStore) {
211
+ return derived(store, $state => $state.isReady);
212
+ }
213
+
214
+ export function createDerivedPlayerInfo(store: PlayerContextStore) {
215
+ return derived(store, $state => ({
216
+ name: $state.playerName,
217
+ shortname: $state.playerShortname,
218
+ }));
219
+ }
220
+
221
+ export default createPlayerContext;