@livepeer-frameworks/player-svelte 0.0.3 → 0.1.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 (41) hide show
  1. package/README.md +78 -0
  2. package/dist/DevModePanel.svelte +2 -19
  3. package/dist/IdleScreen.svelte +3 -3
  4. package/dist/LoadingScreen.svelte +2 -2
  5. package/dist/Player.svelte +13 -17
  6. package/dist/Player.svelte.d.ts +2 -1
  7. package/dist/PlayerControls.svelte +24 -33
  8. package/dist/PlayerControls.svelte.d.ts +2 -0
  9. package/dist/SeekBar.svelte +1 -1
  10. package/dist/SpeedIndicator.svelte +5 -6
  11. package/dist/StatsPanel.svelte +37 -20
  12. package/dist/StatsPanel.svelte.d.ts +2 -14
  13. package/dist/SubtitleRenderer.svelte +3 -3
  14. package/dist/components/VolumeIcons.svelte +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +2 -2
  17. package/dist/stores/playbackQuality.js +0 -9
  18. package/dist/stores/playerController.d.ts +1 -1
  19. package/dist/stores/playerController.js +8 -3
  20. package/dist/stores/streamState.d.ts +1 -1
  21. package/dist/stores/streamState.js +1 -1
  22. package/dist/stores/viewerEndpoints.d.ts +5 -5
  23. package/dist/stores/viewerEndpoints.js +5 -5
  24. package/dist/ui/Slider.svelte +1 -1
  25. package/package.json +1 -1
  26. package/src/DevModePanel.svelte +2 -19
  27. package/src/IdleScreen.svelte +3 -3
  28. package/src/LoadingScreen.svelte +2 -2
  29. package/src/Player.svelte +12 -17
  30. package/src/PlayerControls.svelte +3 -25
  31. package/src/SeekBar.svelte +1 -1
  32. package/src/StatsPanel.svelte +37 -20
  33. package/src/SubtitleRenderer.svelte +3 -3
  34. package/src/components/VolumeIcons.svelte +1 -1
  35. package/src/index.ts +2 -2
  36. package/src/stores/playbackQuality.ts +0 -10
  37. package/src/stores/playerContext.ts +1 -1
  38. package/src/stores/playerController.ts +4 -4
  39. package/src/stores/streamState.ts +1 -1
  40. package/src/stores/viewerEndpoints.ts +7 -7
  41. package/src/ui/Slider.svelte +1 -1
@@ -52,7 +52,7 @@ const initialState = {
52
52
  * let containerEl: HTMLElement;
53
53
  *
54
54
  * const playerStore = createPlayerControllerStore({
55
- * contentId: 'my-stream',
55
+ * contentId: 'pk_...',
56
56
  * contentType: 'live',
57
57
  * gatewayUrl: 'https://gateway.example.com/graphql',
58
58
  * });
@@ -122,6 +122,7 @@ export function createPlayerControllerStore(config) {
122
122
  store.update(prev => ({
123
123
  ...prev,
124
124
  streamState,
125
+ metadata: controller.getMetadata(),
125
126
  isEffectivelyLive: controller.isEffectivelyLive(),
126
127
  shouldShowIdleScreen: controller.shouldShowIdleScreen(),
127
128
  }));
@@ -152,7 +153,11 @@ export function createPlayerControllerStore(config) {
152
153
  }));
153
154
  // Add video event listeners for state sync
154
155
  const video = videoElement;
155
- const handleVideoEvent = () => syncState();
156
+ const handleVideoEvent = () => {
157
+ if (controller?.shouldSuppressVideoEvents?.())
158
+ return;
159
+ syncState();
160
+ };
156
161
  video.addEventListener('play', handleVideoEvent);
157
162
  video.addEventListener('pause', handleVideoEvent);
158
163
  video.addEventListener('waiting', handleVideoEvent);
@@ -164,7 +169,7 @@ export function createPlayerControllerStore(config) {
164
169
  video.removeEventListener('playing', handleVideoEvent);
165
170
  });
166
171
  }));
167
- unsubscribers.push(controller.on('playerSelected', ({ player, source }) => {
172
+ unsubscribers.push(controller.on('playerSelected', ({ player: _player, source }) => {
168
173
  store.update(prev => ({
169
174
  ...prev,
170
175
  currentPlayerInfo: controller.getCurrentPlayerInfo(),
@@ -28,7 +28,7 @@ export interface StreamStateStore extends Readable<StreamState> {
28
28
  *
29
29
  * const streamState = createStreamStateManager({
30
30
  * mistBaseUrl: 'https://mist.example.com',
31
- * streamName: 'my-stream',
31
+ * streamName: 'pk_...', // playbackId (view key)
32
32
  * });
33
33
  *
34
34
  * // Access values
@@ -65,7 +65,7 @@ const initialState = {
65
65
  *
66
66
  * const streamState = createStreamStateManager({
67
67
  * mistBaseUrl: 'https://mist.example.com',
68
- * streamName: 'my-stream',
68
+ * streamName: 'pk_...', // playbackId (view key)
69
69
  * });
70
70
  *
71
71
  * // Access values
@@ -4,11 +4,11 @@
4
4
  * Port of useViewerEndpoints.ts React hook to Svelte 5 stores.
5
5
  */
6
6
  import { type Readable } from 'svelte/store';
7
- import type { ContentEndpoints, EndpointInfo, ContentMetadata, ContentType } from '@livepeer-frameworks/player-core';
7
+ import type { ContentEndpoints, ContentType } from '@livepeer-frameworks/player-core';
8
8
  export interface ViewerEndpointsOptions {
9
9
  gatewayUrl: string;
10
- contentType: ContentType;
11
10
  contentId: string;
11
+ contentType?: ContentType;
12
12
  authToken?: string;
13
13
  }
14
14
  export type EndpointStatus = 'idle' | 'loading' | 'ready' | 'error';
@@ -32,7 +32,7 @@ export interface ViewerEndpointsStore extends Readable<ViewerEndpointsState> {
32
32
  * const resolver = createEndpointResolver({
33
33
  * gatewayUrl: 'https://gateway.example.com/graphql',
34
34
  * contentType: 'live',
35
- * contentId: 'my-stream',
35
+ * contentId: 'pk_...',
36
36
  * });
37
37
  *
38
38
  * $: endpoints = $resolver.endpoints;
@@ -42,7 +42,7 @@ export interface ViewerEndpointsStore extends Readable<ViewerEndpointsState> {
42
42
  */
43
43
  export declare function createEndpointResolver(options: ViewerEndpointsOptions): ViewerEndpointsStore;
44
44
  export declare function createDerivedEndpoints(store: ViewerEndpointsStore): Readable<ContentEndpoints | null>;
45
- export declare function createDerivedPrimaryEndpoint(store: ViewerEndpointsStore): Readable<EndpointInfo | null>;
46
- export declare function createDerivedMetadata(store: ViewerEndpointsStore): Readable<ContentMetadata | null>;
45
+ export declare function createDerivedPrimaryEndpoint(store: ViewerEndpointsStore): Readable<import("@livepeer-frameworks/player-core").EndpointInfo | null>;
46
+ export declare function createDerivedMetadata(store: ViewerEndpointsStore): Readable<import("@livepeer-frameworks/player-core").ContentMetadata | null>;
47
47
  export declare function createDerivedStatus(store: ViewerEndpointsStore): Readable<EndpointStatus>;
48
48
  export default createEndpointResolver;
@@ -48,7 +48,7 @@ const initialState = {
48
48
  * const resolver = createEndpointResolver({
49
49
  * gatewayUrl: 'https://gateway.example.com/graphql',
50
50
  * contentType: 'live',
51
- * contentId: 'my-stream',
51
+ * contentId: 'pk_...',
52
52
  * });
53
53
  *
54
54
  * $: endpoints = $resolver.endpoints;
@@ -65,7 +65,7 @@ export function createEndpointResolver(options) {
65
65
  * Fetch endpoints from Gateway
66
66
  */
67
67
  async function fetchEndpoints() {
68
- if (!gatewayUrl || !contentType || !contentId || !mounted)
68
+ if (!gatewayUrl || !contentId || !mounted)
69
69
  return;
70
70
  // Abort previous request
71
71
  abortController?.abort();
@@ -74,8 +74,8 @@ export function createEndpointResolver(options) {
74
74
  try {
75
75
  const graphqlEndpoint = gatewayUrl.replace(/\/$/, '');
76
76
  const query = `
77
- query ResolveViewer($contentType: String!, $contentId: String!) {
78
- resolveViewerEndpoint(contentType: $contentType, contentId: $contentId) {
77
+ query ResolveViewer($contentId: String!) {
78
+ resolveViewerEndpoint(contentId: $contentId) {
79
79
  primary { nodeId baseUrl protocol url geoDistance loadScore outputs }
80
80
  fallbacks { nodeId baseUrl protocol url geoDistance loadScore outputs }
81
81
  metadata { contentType contentId title description durationSeconds status isLive viewers recordingSizeBytes clipSource createdAt }
@@ -88,7 +88,7 @@ export function createEndpointResolver(options) {
88
88
  'Content-Type': 'application/json',
89
89
  ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
90
90
  },
91
- body: JSON.stringify({ query, variables: { contentType, contentId } }),
91
+ body: JSON.stringify({ query, variables: { contentId } }),
92
92
  signal: abortController.signal,
93
93
  });
94
94
  if (!res.ok)
@@ -27,7 +27,7 @@
27
27
  trackClassName = "",
28
28
  thumbClassName = "",
29
29
  showTrack = true,
30
- hoverThumb = false,
30
+ hoverThumb: _hoverThumb = false,
31
31
  accentColor = false,
32
32
  oninput = undefined,
33
33
  ...rest
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livepeer-frameworks/player-svelte",
3
- "version": "0.0.3",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "description": "Svelte 5 components for FrameWorks streaming player",
6
6
  "svelte": "./dist/index.js",
@@ -11,8 +11,6 @@
11
11
  type MistStreamInfo,
12
12
  type PlaybackMode,
13
13
  } from '@livepeer-frameworks/player-core';
14
- import Button from './ui/Button.svelte';
15
- import Badge from './ui/Badge.svelte';
16
14
 
17
15
  /** Short labels for source types */
18
16
  const SOURCE_TYPE_LABELS: Record<string, string> = {
@@ -56,7 +54,7 @@
56
54
  videoElement = null,
57
55
  protocol = undefined,
58
56
  nodeId = undefined,
59
- isVisible = true,
57
+ isVisible: _isVisible = true,
60
58
  isOpen: controlledIsOpen = undefined,
61
59
  onOpenChange = undefined,
62
60
  }: Props = $props();
@@ -258,22 +256,7 @@
258
256
  });
259
257
  </script>
260
258
 
261
- {#if !isOpen}
262
- <button
263
- type="button"
264
- onclick={() => setIsOpen(true)}
265
- class={cn(
266
- 'fw-dev-toggle',
267
- isVisible ? '' : 'fw-dev-toggle--hidden'
268
- )}
269
- title="Advanced Settings"
270
- aria-label="Open advanced settings panel"
271
- >
272
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
273
- <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
274
- </svg>
275
- </button>
276
- {:else}
259
+ {#if isOpen}
277
260
  <div class="fw-dev-panel">
278
261
  <!-- Header with tabs -->
279
262
  <div class="fw-dev-header">
@@ -299,7 +299,7 @@
299
299
  }
300
300
  }
301
301
 
302
- let statusLabel = $derived(getStatusLabel(status));
302
+ let _statusLabel = $derived(getStatusLabel(status));
303
303
  let showRetry = $derived((status === 'ERROR' || status === 'INVALID') && onRetry);
304
304
  let showProgress = $derived(status === 'INITIALIZING' && percentage !== undefined);
305
305
  let displayMessage = $derived(error || message);
@@ -633,7 +633,7 @@
633
633
  {/each}
634
634
 
635
635
  <!-- Floating particles -->
636
- {#each particles as particle, i}
636
+ {#each particles as particle, _i}
637
637
  <div
638
638
  class="particle"
639
639
  style="
@@ -648,7 +648,7 @@
648
648
  {/each}
649
649
 
650
650
  <!-- Animated bubbles -->
651
- {#each bubbles as bubble, i}
651
+ {#each bubbles as bubble, _i}
652
652
  <div
653
653
  class="bubble"
654
654
  style="
@@ -608,7 +608,7 @@
608
608
  {/each}
609
609
 
610
610
  <!-- Floating particles -->
611
- {#each particles as particle, i}
611
+ {#each particles as particle, _i}
612
612
  <div
613
613
  class="particle"
614
614
  style="
@@ -623,7 +623,7 @@
623
623
  {/each}
624
624
 
625
625
  <!-- Animated bubbles -->
626
- {#each bubbles as bubble, i}
626
+ {#each bubbles as bubble, _i}
627
627
  <div
628
628
  class="bubble"
629
629
  style="
package/src/Player.svelte CHANGED
@@ -3,9 +3,8 @@
3
3
  Thin wrapper over PlayerController from @livepeer-frameworks/player-core
4
4
  -->
5
5
  <script lang="ts">
6
- import { onMount, onDestroy } from 'svelte';
6
+ import { onMount } from 'svelte';
7
7
  import IdleScreen from './IdleScreen.svelte';
8
- import LoadingScreen from './LoadingScreen.svelte';
9
8
  import SubtitleRenderer from './SubtitleRenderer.svelte';
10
9
  import PlayerControls from './PlayerControls.svelte';
11
10
  import SpeedIndicator from './SpeedIndicator.svelte';
@@ -22,7 +21,7 @@
22
21
  ContextMenuSeparator,
23
22
  } from './ui/context-menu';
24
23
  import { StatsIcon, SettingsIcon, PictureInPictureIcon } from './icons';
25
- import { cn, type PlaybackMode, type ContentEndpoints, type ContentMetadata, type PlayerState, type PlayerStateContext, type ContentType, type EndpointInfo } from '@livepeer-frameworks/player-core';
24
+ import { cn, type PlaybackMode, type ContentEndpoints, type PlayerState, type PlayerStateContext, type ContentType, type EndpointInfo, type PlayerMetadata } from '@livepeer-frameworks/player-core';
26
25
  import { createPlayerControllerStore, type PlayerControllerStore } from './stores/playerController';
27
26
  import type { SkipDirection } from './SkipIndicator.svelte';
28
27
 
@@ -48,15 +47,17 @@
48
47
  playbackMode?: PlaybackMode;
49
48
  };
50
49
  onStateChange?: (state: PlayerState, context?: PlayerStateContext) => void;
50
+ onMetadata?: (metadata: PlayerMetadata) => void;
51
51
  }
52
52
 
53
53
  let {
54
54
  contentId,
55
- contentType = 'live',
55
+ contentType,
56
56
  thumbnailUrl = null,
57
57
  endpoints = undefined,
58
58
  options = {},
59
59
  onStateChange = undefined,
60
+ onMetadata = undefined,
60
61
  }: Props = $props();
61
62
 
62
63
  // ============================================================================
@@ -141,12 +142,18 @@
141
142
  debug('playerStore created');
142
143
 
143
144
  // Subscribe to store state
145
+ let prevMetadata: PlayerMetadata | null = null;
144
146
  const unsubscribe = playerStore.subscribe(state => {
145
147
  storeState = state;
146
148
  // Forward state changes to prop callback
147
149
  if (onStateChange && state.state) {
148
150
  onStateChange(state.state);
149
151
  }
152
+ // Forward metadata changes to prop callback
153
+ if (onMetadata && state.metadata && state.metadata !== prevMetadata) {
154
+ prevMetadata = state.metadata;
155
+ onMetadata(state.metadata);
156
+ }
150
157
  });
151
158
 
152
159
  return () => {
@@ -277,19 +284,7 @@
277
284
  isOpen={isStatsOpen}
278
285
  onClose={() => isStatsOpen = false}
279
286
  {metadata}
280
- streamState={storeState.streamState?.isOnline ? {
281
- status: storeState.streamState.status,
282
- viewers: metadata?.viewers,
283
- tracks: storeState.streamState.streamInfo?.meta?.tracks
284
- ? Object.values(storeState.streamState.streamInfo.meta.tracks).map((t: any) => ({
285
- type: t.type,
286
- codec: t.codec,
287
- width: t.width,
288
- height: t.height,
289
- bps: t.bps,
290
- }))
291
- : [],
292
- } : null}
287
+ streamState={storeState.streamState}
293
288
  quality={storeState.playbackQuality}
294
289
  videoElement={storeState.videoElement}
295
290
  protocol={primaryEndpoint?.protocol}
@@ -6,7 +6,6 @@
6
6
  type PlaybackMode,
7
7
  // Seeking utilities from core
8
8
  SPEED_PRESETS,
9
- getLatencyTier,
10
9
  isMediaStreamSource,
11
10
  supportsPlaybackRate as coreSupportsPlaybackRate,
12
11
  calculateSeekableRange,
@@ -15,7 +14,6 @@
15
14
  calculateIsNearLive,
16
15
  isLiveContent,
17
16
  // Time formatting from core
18
- formatTime,
19
17
  formatTimeDisplay,
20
18
  } from '@livepeer-frameworks/player-core';
21
19
  import SeekBar from './SeekBar.svelte';
@@ -119,7 +117,7 @@
119
117
  let showSettingsMenu = $state(false);
120
118
  let isNearLiveState = $state(true);
121
119
  let buffered: TimeRanges | undefined = $state(undefined);
122
- let hasSeekToLive = false; // Track if we've auto-seeked to live
120
+ let _hasSeekToLive = false; // Track if we've auto-seeked to live
123
121
  let qualityValue = $state('auto');
124
122
  let captionValue = $state('none');
125
123
 
@@ -287,7 +285,7 @@
287
285
  // Reset seek-to-live flag when video element changes
288
286
  $effect(() => {
289
287
  if (video) {
290
- hasSeekToLive = false;
288
+ _hasSeekToLive = false;
291
289
  }
292
290
  });
293
291
 
@@ -311,7 +309,7 @@
311
309
  }));
312
310
 
313
311
  // Seek value for slider
314
- let seekValue = $derived.by(() => {
312
+ let _seekValue = $derived.by(() => {
315
313
  if (isLive) {
316
314
  const window = liveEdge - seekableStart;
317
315
  if (window <= 0) return 1000;
@@ -387,26 +385,6 @@
387
385
  isMuted = next === 0;
388
386
  }
389
387
 
390
- function handleSeekChange(val: number) {
391
- if (disabled || !video) return;
392
- if (isLive) {
393
- const window = liveEdge - seekableStart;
394
- const newTime = seekableStart + (val / 1000) * window;
395
- if (onseek) {
396
- onseek(newTime);
397
- } else {
398
- video.currentTime = newTime;
399
- }
400
- } else if (Number.isFinite(duration)) {
401
- const newTime = (val / 1000) * duration;
402
- if (onseek) {
403
- onseek(newTime);
404
- } else {
405
- video.currentTime = newTime;
406
- }
407
- }
408
- }
409
-
410
388
  function handleFullscreen() {
411
389
  if (disabled) return;
412
390
  const container = document.querySelector('[data-player-container="true"]') as HTMLElement | null;
@@ -240,7 +240,7 @@
240
240
  isDragging && 'fw-seek-track--active'
241
241
  )}>
242
242
  <!-- Buffered segments -->
243
- {#each bufferedSegments as segment, index}
243
+ {#each bufferedSegments as segment, _index}
244
244
  <div
245
245
  class="fw-seek-buffered"
246
246
  style="left: {segment.startPercent}%; width: {segment.endPercent - segment.startPercent}%;"
@@ -3,27 +3,14 @@
3
3
  Port of src/components/StatsPanel.tsx
4
4
  -->
5
5
  <script lang="ts">
6
- import { cn, type ContentMetadata, type PlaybackQuality } from '@livepeer-frameworks/player-core';
6
+ import { cn, type ContentMetadata, type PlaybackQuality, type StreamState } from '@livepeer-frameworks/player-core';
7
7
  import Button from './ui/Button.svelte';
8
8
 
9
- interface StreamStateInfo {
10
- status?: string;
11
- viewers?: number;
12
- tracks?: Array<{
13
- type: string;
14
- codec: string;
15
- width?: number;
16
- height?: number;
17
- bps?: number;
18
- channels?: number;
19
- }>;
20
- }
21
-
22
9
  interface Props {
23
10
  isOpen: boolean;
24
11
  onClose: () => void;
25
12
  metadata?: ContentMetadata | null;
26
- streamState?: StreamStateInfo | null;
13
+ streamState?: StreamState | null;
27
14
  quality?: PlaybackQuality | null;
28
15
  videoElement?: HTMLVideoElement | null;
29
16
  protocol?: string;
@@ -59,17 +46,38 @@
59
46
  let latency = $derived(quality?.latency ? `${Math.round(quality.latency)} ms` : '—');
60
47
 
61
48
  // Stream state stats
62
- let viewers = $derived(streamState?.viewers ?? metadata?.viewers ?? '—');
49
+ let viewers = $derived(metadata?.viewers ?? '—');
63
50
  let streamStatus = $derived(streamState?.status ?? metadata?.status ?? '—');
64
51
 
52
+ const mistInfo = $derived(metadata?.mist ?? streamState?.streamInfo);
53
+
54
+ function deriveTracksFromMist() {
55
+ const mistTracks = mistInfo?.meta?.tracks;
56
+ if (!mistTracks) return undefined;
57
+ return Object.values(mistTracks).map((t: any) => ({
58
+ type: t.type,
59
+ codec: t.codec,
60
+ width: t.width,
61
+ height: t.height,
62
+ bitrate: typeof t.bps === 'number' ? Math.round(t.bps) : undefined,
63
+ fps: typeof t.fpks === 'number' ? t.fpks / 1000 : undefined,
64
+ channels: t.channels,
65
+ sampleRate: t.rate,
66
+ }));
67
+ }
68
+
65
69
  // Format track info
66
70
  function formatTracks(): string {
67
- if (!streamState?.tracks?.length) return '—';
68
- return streamState.tracks.map(t => {
71
+ const tracks = metadata?.tracks ?? deriveTracksFromMist();
72
+ if (!tracks?.length) return '—';
73
+ return tracks.map(t => {
69
74
  if (t.type === 'video') {
70
- return `${t.codec} ${t.width}x${t.height}@${t.bps ? Math.round(t.bps / 1000) + 'kbps' : '?'}`;
75
+ const resolution = t.width && t.height ? `${t.width}x${t.height}` : '?';
76
+ const bitrate = t.bitrate ? `${Math.round(t.bitrate / 1000)}kbps` : '?';
77
+ return `${t.codec ?? '?'} ${resolution}@${bitrate}`;
71
78
  }
72
- return `${t.codec} ${t.channels}ch`;
79
+ const channels = t.channels ? `${t.channels}ch` : '?';
80
+ return `${t.codec ?? '?'} ${channels}`;
73
81
  }).join(', ');
74
82
  }
75
83
 
@@ -96,6 +104,15 @@
96
104
  { label: 'Viewers', value: String(viewers) },
97
105
  { label: 'Status', value: streamStatus },
98
106
  { label: 'Tracks', value: formatTracks() },
107
+ { label: 'Mist Type', value: mistInfo?.type ?? '—' },
108
+ {
109
+ label: 'Mist Buffer Window',
110
+ value: mistInfo?.meta?.buffer_window != null
111
+ ? String(mistInfo.meta.buffer_window)
112
+ : '—',
113
+ },
114
+ { label: 'Mist Lastms', value: mistInfo?.lastms != null ? String(mistInfo.lastms) : '—' },
115
+ { label: 'Mist Unixoffset', value: mistInfo?.unixoffset != null ? String(mistInfo.unixoffset) : '—' },
99
116
  );
100
117
 
101
118
  if (metadata?.durationSeconds) {
@@ -78,7 +78,7 @@
78
78
  // State
79
79
  let liveCues = $state<SubtitleCue[]>([]);
80
80
  let displayedText = $state<string>('');
81
- let lastCueId: string | null = null;
81
+ let _lastCueId: string | null = null;
82
82
  let unsubscribe: (() => void) | null = null;
83
83
 
84
84
  // Merged style
@@ -166,10 +166,10 @@
166
166
 
167
167
  if (activeCue) {
168
168
  displayedText = activeCue.text;
169
- lastCueId = activeCue.id;
169
+ _lastCueId = activeCue.id;
170
170
  } else {
171
171
  displayedText = '';
172
- lastCueId = null;
172
+ _lastCueId = null;
173
173
  }
174
174
  });
175
175
 
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  let {
3
3
  size = 16,
4
- color = 'currentColor',
4
+ color: _color = 'currentColor',
5
5
  className = '',
6
6
  isMuted = false,
7
7
  volume = 1 // 0-1 range
package/src/index.ts CHANGED
@@ -12,11 +12,11 @@
12
12
  * @example
13
13
  * ```svelte
14
14
  * <script>
15
- * import { Player } from '@livepeer-frameworks/player/svelte';
15
+ * import { Player } from '@livepeer-frameworks/player-svelte';
16
16
  * </script>
17
17
  *
18
18
  * <Player
19
- * contentId="my-stream"
19
+ * contentId="pk_..."
20
20
  * contentType="live"
21
21
  * options={{ gatewayUrl: "https://gateway.example.com/graphql", devMode: true }}
22
22
  * autoplay
@@ -19,16 +19,6 @@ export interface PlaybackQualityStore extends Readable<PlaybackQuality | null> {
19
19
  destroy: () => void;
20
20
  }
21
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
22
  /**
33
23
  * Create a playback quality monitoring store.
34
24
  *
@@ -4,7 +4,7 @@
4
4
  * Port of PlayerContext.tsx React context to Svelte 5 stores.
5
5
  */
6
6
 
7
- import { writable, derived, type Readable, type Writable } from 'svelte/store';
7
+ import { writable, derived, type Readable } from 'svelte/store';
8
8
  import { getContext, setContext } from 'svelte';
9
9
  import { globalPlayerManager, type StreamInfo, type IPlayer } from '@livepeer-frameworks/player-core';
10
10
 
@@ -3,13 +3,12 @@
3
3
  * for declarative usage in Svelte 5 components.
4
4
  */
5
5
 
6
- import { writable, derived, type Readable, type Writable } from 'svelte/store';
6
+ import { writable, derived, type Readable } from 'svelte/store';
7
7
  import {
8
8
  PlayerController,
9
9
  type PlayerControllerConfig,
10
10
  type PlayerState,
11
11
  type StreamState,
12
- type StreamSource,
13
12
  type PlaybackQuality,
14
13
  type ContentEndpoints,
15
14
  type ContentMetadata,
@@ -192,7 +191,7 @@ const initialState: PlayerControllerState = {
192
191
  * let containerEl: HTMLElement;
193
192
  *
194
193
  * const playerStore = createPlayerControllerStore({
195
- * contentId: 'my-stream',
194
+ * contentId: 'pk_...',
196
195
  * contentType: 'live',
197
196
  * gatewayUrl: 'https://gateway.example.com/graphql',
198
197
  * });
@@ -270,6 +269,7 @@ export function createPlayerControllerStore(
270
269
  store.update(prev => ({
271
270
  ...prev,
272
271
  streamState,
272
+ metadata: controller!.getMetadata(),
273
273
  isEffectivelyLive: controller!.isEffectivelyLive(),
274
274
  shouldShowIdleScreen: controller!.shouldShowIdleScreen(),
275
275
  }));
@@ -321,7 +321,7 @@ export function createPlayerControllerStore(
321
321
  });
322
322
  }));
323
323
 
324
- unsubscribers.push(controller.on('playerSelected', ({ player, source }) => {
324
+ unsubscribers.push(controller.on('playerSelected', ({ player: _player, source }) => {
325
325
  store.update(prev => ({
326
326
  ...prev,
327
327
  currentPlayerInfo: controller!.getCurrentPlayerInfo(),
@@ -82,7 +82,7 @@ const initialState: StreamState = {
82
82
  *
83
83
  * const streamState = createStreamStateManager({
84
84
  * mistBaseUrl: 'https://mist.example.com',
85
- * streamName: 'my-stream',
85
+ * streamName: 'pk_...', // playbackId (view key)
86
86
  * });
87
87
  *
88
88
  * // Access values
@@ -5,12 +5,12 @@
5
5
  */
6
6
 
7
7
  import { writable, derived, type Readable } from 'svelte/store';
8
- import type { ContentEndpoints, EndpointInfo, ContentMetadata, ContentType } from '@livepeer-frameworks/player-core';
8
+ import type { ContentEndpoints, ContentType } from '@livepeer-frameworks/player-core';
9
9
 
10
10
  export interface ViewerEndpointsOptions {
11
11
  gatewayUrl: string;
12
- contentType: ContentType;
13
12
  contentId: string;
13
+ contentType?: ContentType;
14
14
  authToken?: string;
15
15
  }
16
16
 
@@ -82,7 +82,7 @@ const initialState: ViewerEndpointsState = {
82
82
  * const resolver = createEndpointResolver({
83
83
  * gatewayUrl: 'https://gateway.example.com/graphql',
84
84
  * contentType: 'live',
85
- * contentId: 'my-stream',
85
+ * contentId: 'pk_...',
86
86
  * });
87
87
  *
88
88
  * $: endpoints = $resolver.endpoints;
@@ -101,7 +101,7 @@ export function createEndpointResolver(options: ViewerEndpointsOptions): ViewerE
101
101
  * Fetch endpoints from Gateway
102
102
  */
103
103
  async function fetchEndpoints() {
104
- if (!gatewayUrl || !contentType || !contentId || !mounted) return;
104
+ if (!gatewayUrl || !contentId || !mounted) return;
105
105
 
106
106
  // Abort previous request
107
107
  abortController?.abort();
@@ -112,8 +112,8 @@ export function createEndpointResolver(options: ViewerEndpointsOptions): ViewerE
112
112
  try {
113
113
  const graphqlEndpoint = gatewayUrl.replace(/\/$/, '');
114
114
  const query = `
115
- query ResolveViewer($contentType: String!, $contentId: String!) {
116
- resolveViewerEndpoint(contentType: $contentType, contentId: $contentId) {
115
+ query ResolveViewer($contentId: String!) {
116
+ resolveViewerEndpoint(contentId: $contentId) {
117
117
  primary { nodeId baseUrl protocol url geoDistance loadScore outputs }
118
118
  fallbacks { nodeId baseUrl protocol url geoDistance loadScore outputs }
119
119
  metadata { contentType contentId title description durationSeconds status isLive viewers recordingSizeBytes clipSource createdAt }
@@ -127,7 +127,7 @@ export function createEndpointResolver(options: ViewerEndpointsOptions): ViewerE
127
127
  'Content-Type': 'application/json',
128
128
  ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
129
129
  },
130
- body: JSON.stringify({ query, variables: { contentType, contentId } }),
130
+ body: JSON.stringify({ query, variables: { contentId } }),
131
131
  signal: abortController.signal,
132
132
  });
133
133