@mottosports/motto-video-player 1.0.1-rc.2 → 1.0.1-rc.6

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Motto Video Player
2
2
 
3
- React video player component for the Motto platform, powered by Shaka Player with TanStack Query integration.
3
+ A modern, feature-rich video player built on top of Shaka Player with React support.
4
4
 
5
5
  ## Features
6
6
 
@@ -17,319 +17,220 @@ React video player component for the Motto platform, powered by Shaka Player wit
17
17
  ## Installation
18
18
 
19
19
  ```bash
20
- npm install @motto-ui-components/motto-video-player @tanstack/react-query
20
+ npm install @motto/video-player
21
+ # or
22
+ yarn add @motto/video-player
23
+ # or
24
+ pnpm add @motto/video-player
21
25
  ```
22
26
 
23
- ## Next.js SSR Compatibility
24
-
25
- This component is now fully compatible with Next.js Server-Side Rendering (SSR). The component automatically detects when it's running in a server environment and gracefully handles browser-only APIs.
26
-
27
- ### Basic Usage in Next.js
28
-
29
- For SSR compatibility, use the `ClientSideEvent` component which only renders on the client side:
27
+ ## Basic Usage
30
28
 
31
29
  ```tsx
32
- import { ClientSideEvent } from '@mottosports/motto-video-player';
30
+ import { Player } from '@motto/video-player';
33
31
 
34
- export default function MyPage() {
32
+ function MyVideoPlayer() {
35
33
  return (
36
- <ClientSideEvent
37
- publicKey="your-public-key"
38
- eventId="your-event-id"
39
- locale="en"
34
+ <Player
35
+ src="https://example.com/video.m3u8"
36
+ autoPlay={false}
37
+ controls={true}
38
+ width={800}
39
+ height={450}
40
40
  />
41
41
  );
42
42
  }
43
43
  ```
44
44
 
45
- Alternatively, if you prefer to use the regular `Event` component, wrap it with SSR checks:
46
-
47
- ```tsx
48
- import { Event } from '@mottosports/motto-video-player';
49
-
50
- export default function MyPage() {
51
- return (
52
- <Event
53
- publicKey="your-public-key"
54
- eventId="your-event-id"
55
- locale="en"
56
- />
57
- );
58
- }
59
- ```
45
+ ## Next.js Usage (SSR Support)
60
46
 
61
- ### Dynamic Imports (Alternative Approach)
47
+ If you're using Next.js, you need to handle server-side rendering (SSR) since Shaka Player requires browser APIs. Here are three approaches:
62
48
 
63
- You can also use Next.js dynamic imports with the regular Event component:
49
+ ### Option 1: Using next/dynamic (Recommended)
64
50
 
65
51
  ```tsx
66
52
  import dynamic from 'next/dynamic';
67
53
 
68
- const Event = dynamic(
69
- () => import('@mottosports/motto-video-player').then(mod => ({ default: mod.Event })),
54
+ const MottoPlayer = dynamic(
55
+ () => import('@motto/video-player').then((mod) => ({ default: mod.Player })),
70
56
  {
71
57
  ssr: false,
72
- loading: () => <div>Loading video player...</div>
58
+ loading: () => <div>Loading player...</div>
73
59
  }
74
60
  );
75
61
 
76
- export default function MyPage() {
62
+ function MyVideoPage() {
77
63
  return (
78
- <Event
79
- publicKey="your-public-key"
80
- eventId="your-event-id"
81
- locale="en"
82
- />
64
+ <div>
65
+ <h1>My Video</h1>
66
+ <MottoPlayer
67
+ src="https://example.com/video.m3u8"
68
+ autoPlay={false}
69
+ controls={true}
70
+ width={800}
71
+ height={450}
72
+ />
73
+ </div>
83
74
  );
84
75
  }
85
76
  ```
86
77
 
87
- **Recommended Approach**: Use `ClientSideEvent` for simpler SSR handling without dynamic imports.
88
-
89
- ### App Router Usage (Next.js 13+)
90
-
91
- With App Router, you can use `ClientSideEvent` directly without 'use client' directive:
78
+ ### Option 2: Using the built-in ClientOnlyPlayer
92
79
 
93
80
  ```tsx
94
- import { ClientSideEvent } from '@mottosports/motto-video-player';
81
+ import { ClientOnlyPlayer } from '@motto/video-player';
95
82
 
96
- export default function VideoPlayer() {
83
+ function MyVideoPage() {
97
84
  return (
98
- <ClientSideEvent
99
- publicKey="your-public-key"
100
- eventId="your-event-id"
101
- locale="en"
102
- />
85
+ <div>
86
+ <h1>My Video</h1>
87
+ <ClientOnlyPlayer
88
+ src="https://example.com/video.m3u8"
89
+ autoPlay={false}
90
+ controls={true}
91
+ width={800}
92
+ height={450}
93
+ />
94
+ </div>
103
95
  );
104
96
  }
105
97
  ```
106
98
 
107
- Or use the regular Event component with 'use client':
99
+ ### Option 3: Using useEffect for client-side only rendering
108
100
 
109
101
  ```tsx
110
- 'use client';
111
-
112
- import { Event } from '@mottosports/motto-video-player';
113
-
114
- export default function VideoPlayer() {
115
- return (
116
- <Event
117
- publicKey="your-public-key"
118
- eventId="your-event-id"
119
- locale="en"
120
- />
121
- );
122
- }
123
- ```
124
-
125
- ## Quick Start
126
-
127
- ### Setup QueryClient (Required for Video wrapper)
128
-
129
- ```jsx
130
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
131
-
132
- const queryClient = new QueryClient();
133
-
134
- function App() {
135
- return (
136
- <QueryClientProvider client={queryClient}>
137
- <YourComponents />
138
- </QueryClientProvider>
139
- );
140
- }
141
- ```
102
+ import { Player } from '@motto/video-player';
103
+ import { useEffect, useState } from 'react';
142
104
 
143
- ### Basic Usage
105
+ function MyVideoPage() {
106
+ const [isClient, setIsClient] = useState(false);
144
107
 
145
- #### VideoPlayer (Bare Component)
146
- For direct stream URL playback:
108
+ useEffect(() => {
109
+ setIsClient(true);
110
+ }, []);
147
111
 
148
- ```jsx
149
- import { VideoPlayer } from '@motto-ui-components/motto-video-player';
112
+ if (!isClient) {
113
+ return <div>Loading player...</div>;
114
+ }
150
115
 
151
- function MyPlayer() {
152
116
  return (
153
- <VideoPlayer
154
- src="https://example.com/video.m3u8"
155
- controls
156
- aspectRatio={16/9}
157
- onPlay={() => console.log('Playing')}
158
- onError={(error) => console.error(error)}
159
- />
117
+ <div>
118
+ <h1>My Video</h1>
119
+ <Player
120
+ src="https://example.com/video.m3u8"
121
+ autoPlay={false}
122
+ controls={true}
123
+ width={800}
124
+ height={450}
125
+ />
126
+ </div>
160
127
  );
161
128
  }
162
129
  ```
163
130
 
164
- #### Video (Wrapper Component)
165
- For data fetching with TanStack Query:
166
-
167
- ```jsx
168
- import { Video } from '@motto-ui-components/motto-video-player';
169
-
170
- function MyPlayer() {
171
- return (
172
- <Video
173
- videoId="your-video-id"
174
- publicKey="your-public-key"
175
- controls
176
- refetchInterval={30000}
177
- events={{
178
- onVideoData: (video) => console.log('Video loaded:', video),
179
- onError: (error) => console.error('Error:', error),
180
- onPlay: () => console.log('Playing')
181
- }}
182
- />
183
- );
184
- }
185
- ```
131
+ ## Player Props
186
132
 
187
- ## Component Comparison
133
+ The player accepts the following props:
188
134
 
189
- | Feature | VideoPlayer | Video |
190
- |---------|-------------|-------|
191
- | **Use Case** | Direct stream URL | Data fetching with videoId |
192
- | **Data Fetching** | Manual | ✅ Automatic with TanStack Query |
193
- | **Caching** | None | ✅ Smart caching & background updates |
194
- | **Loading States** | Manual | Built-in loading indicators |
195
- | **Error Handling** | Manual | Automatic retry with exponential backoff |
196
- | **Performance** | Minimal overhead | Optimized with query deduplication |
135
+ - `src` (string, required): URL of the video manifest (HLS, DASH, etc.)
136
+ - `autoPlay` (boolean, default: false): Whether to start playing automatically
137
+ - `controls` (boolean, default: true): Whether to show player controls
138
+ - `width` (number): Fixed width in pixels
139
+ - `height` (number): Fixed height in pixels
140
+ - `aspectRatio` (number, default: 16/9): Aspect ratio for responsive sizing
141
+ - `poster` (string): URL of poster image to show before video loads
142
+ - `muted` (boolean, default: false): Whether to start muted
143
+ - `loop` (boolean, default: false): Whether to loop the video
197
144
 
198
145
  ## Advanced Configuration
199
146
 
200
- ### TanStack Query Options
201
-
202
- ```jsx
203
- <Video
204
- videoId="123"
205
- publicKey="key"
206
- refetchInterval={30000}
207
- queryOptions={{
208
- staleTime: 5 * 60 * 1000, // 5 minutes
209
- retry: 3,
210
- retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000)
147
+ ### Quality Control
148
+
149
+ ```tsx
150
+ <Player
151
+ src="https://example.com/video.m3u8"
152
+ qualityConfig={{
153
+ enableAutoQuality: true,
154
+ preferredVideoHeight: 720,
155
+ preferredAudioLanguage: 'en'
211
156
  }}
212
157
  />
213
158
  ```
214
159
 
215
- ### Skip Controls
160
+ ### DRM Configuration
216
161
 
217
- ```jsx
218
- <VideoPlayer
219
- src="..."
220
- skipConfig={{
221
- showSkipBack: true,
222
- showSkipForward: true,
223
- skipDuration: 15,
224
- position: 'controls' // or 'overlay'
162
+ ```tsx
163
+ <Player
164
+ src="https://example.com/protected-video.mpd"
165
+ drmConfig={{
166
+ servers: {
167
+ 'com.widevine.alpha': 'https://license-server.com/widevine',
168
+ 'com.microsoft.playready': 'https://license-server.com/playready'
169
+ }
225
170
  }}
226
- onSkipBack={(newTime) => console.log('Skipped to:', newTime)}
227
- onSkipForward={(newTime) => console.log('Skipped to:', newTime)}
228
171
  />
229
172
  ```
230
173
 
231
- ### Responsive Sizing
232
-
233
- ```jsx
234
- // Default responsive 16:9
235
- <VideoPlayer src="..." />
236
-
237
- // Custom aspect ratio
238
- <VideoPlayer src="..." aspectRatio={4/3} />
239
-
240
- // Fixed dimensions
241
- <VideoPlayer src="..." width={800} height={450} />
242
- ```
243
-
244
- ### Analytics Integration
174
+ ### Mux Analytics
245
175
 
246
- ```jsx
247
- <VideoPlayer
248
- src="..."
176
+ ```tsx
177
+ <Player
178
+ src="https://example.com/video.m3u8"
249
179
  muxConfig={{
250
- debug: false,
251
- data: {
252
- env_key: 'your-mux-env-key',
253
- video_title: 'Video Title',
254
- video_id: 'video-123',
255
- player_name: 'Web Player',
256
- viewer_user_id: 'user-456'
180
+ envKey: 'your-mux-env-key',
181
+ metadata: {
182
+ video_title: 'My Video',
183
+ viewer_user_id: 'user123'
257
184
  }
258
185
  }}
259
- onMuxReady={(monitor) => console.log('Mux ready')}
260
186
  />
261
187
  ```
262
188
 
263
- ## API Reference
264
-
265
- ### VideoPlayer Props
266
-
267
- ```typescript
268
- interface VideoPlayerProps {
269
- src: string; // Video source URL
270
- autoPlay?: boolean; // Auto-play video
271
- controls?: boolean; // Show player controls
272
- aspectRatio?: number; // Video aspect ratio (default: 16/9)
273
- width?: number; // Fixed width
274
- height?: number; // Fixed height
275
- skipConfig?: SkipConfig; // Skip controls configuration
276
- muxConfig?: MuxConfig; // Mux analytics configuration
277
- onPlay?: () => void; // Play event callback
278
- onPause?: () => void; // Pause event callback
279
- onError?: (error: any) => void; // Error event callback
280
- // ... more props
281
- }
282
- ```
189
+ ## Event Handling
283
190
 
284
- ### Video Props
285
-
286
- ```typescript
287
- interface VideoProps extends Omit<VideoPlayerProps, 'src'> {
288
- videoId?: string; // Video ID for data fetching
289
- publicKey?: string; // Public key for API authentication
290
- videoData?: VideoData; // Pre-loaded video data
291
- refetchInterval?: number; // Background refetch interval (ms)
292
- queryOptions?: QueryOptions; // TanStack Query configuration
293
- events?: {
294
- onVideoData?: (video: VideoData) => void;
295
- onEmptyPlaylists?: () => void;
296
- onError?: (error: Error) => void;
297
- // ... player events
298
- };
299
- }
191
+ ```tsx
192
+ <Player
193
+ src="https://example.com/video.m3u8"
194
+ events={{
195
+ onPlay: () => console.log('Video started playing'),
196
+ onPause: () => console.log('Video paused'),
197
+ onEnded: () => console.log('Video ended'),
198
+ onError: (error) => console.error('Playback error:', error),
199
+ onQualityChange: (quality) => console.log('Quality changed:', quality)
200
+ }}
201
+ />
300
202
  ```
301
203
 
302
- ## TanStack Query Benefits
204
+ ## Responsive Design
303
205
 
304
- ### Caching & Performance
305
- - **Automatic Caching**: Videos are cached with configurable stale time
306
- - **Background Refetching**: Data stays fresh with background updates
307
- - **Request Deduplication**: Identical requests are automatically deduplicated
308
- - **Garbage Collection**: Memory-efficient cleanup of unused cache entries
206
+ For responsive video players, omit the `width` and `height` props and optionally set an `aspectRatio`:
309
207
 
310
- ### Resilience & UX
311
- - **Smart Retries**: Exponential backoff retry logic for failed requests
312
- - **Loading States**: Built-in loading indicators for better UX
313
- - **Error Recovery**: Automatic error handling and recovery mechanisms
314
- - **Optimistic Updates**: Support for optimistic UI updates
208
+ ```tsx
209
+ <Player
210
+ src="https://example.com/video.m3u8"
211
+ aspectRatio={16/9}
212
+ containerClassName="w-full max-w-4xl"
213
+ />
214
+ ```
315
215
 
316
- ## Examples
216
+ ## Live Streaming
317
217
 
318
- Check out the [example app](../../apps/example) for comprehensive usage examples including:
218
+ The player automatically detects live streams and shows appropriate UI:
319
219
 
320
- - Basic VideoPlayer usage
321
- - Video wrapper with TanStack Query
322
- - Pre-loaded data scenarios
323
- - Skip controls integration
324
- - Responsive design patterns
220
+ ```tsx
221
+ <Player
222
+ src="https://example.com/live-stream.m3u8"
223
+ streamStartDate={new Date('2024-01-01T12:00:00Z')} // Optional: for absolute time display
224
+ />
225
+ ```
325
226
 
326
- ## Contributing
227
+ ## Browser Support
327
228
 
328
- 1. Clone the repository
329
- 2. Install dependencies: `pnpm install`
330
- 3. Start development: `pnpm dev`
331
- 4. Build: `pnpm build`
229
+ - Chrome 80+
230
+ - Firefox 75+
231
+ - Safari 13+
232
+ - Edge 80+
332
233
 
333
234
  ## License
334
235
 
335
- ISC
236
+ MIT
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import React, { HTMLAttributes } from 'react';
2
- import { QueryClient } from '@tanstack/react-query';
1
+ import * as React from 'react';
2
+ import React__default, { HTMLAttributes } from 'react';
3
3
 
4
4
  /**
5
5
  * Unified events interface for both Player and Video wrapper
@@ -330,7 +330,7 @@ interface MuxAnalyticsConfig {
330
330
  metadata?: MuxMetadata;
331
331
  }
332
332
 
333
- declare const Player: React.ForwardRefExoticComponent<PlayerProps & React.RefAttributes<HTMLVideoElement>>;
333
+ declare const Player: React__default.ForwardRefExoticComponent<PlayerProps & React__default.RefAttributes<HTMLVideoElement>>;
334
334
 
335
335
  interface VideoData {
336
336
  id: string;
@@ -352,6 +352,22 @@ interface VideoListItem {
352
352
  }>;
353
353
  error?: string;
354
354
  }
355
+ /**
356
+ * Fetches video data from the Motto Streaming API
357
+ * @param videoId - The ID of the video to fetch
358
+ * @param publicKey - The public key for authentication
359
+ * @param mottoToken - Optional motto token for authenticated requests
360
+ * @returns Promise<VideoData> - The video data
361
+ */
362
+ declare const fetchVideoData: (videoId: string, publicKey: string, mottoToken?: string) => Promise<VideoData>;
363
+ /**
364
+ * Fetches multiple videos data from the Motto Streaming API
365
+ * @param publicKey - The public key for authentication
366
+ * @param videoIds - Array of video IDs to fetch
367
+ * @param mottoToken - Optional motto token for authenticated requests
368
+ * @returns Promise<VideoListItem[]> - Array of video data
369
+ */
370
+ declare function fetchVideosList(publicKey: string, videoIds: string[], mottoToken?: string, skip?: number, limit?: number): Promise<VideoListItem[]>;
355
371
 
356
372
  interface EventData {
357
373
  id: string;
@@ -367,6 +383,7 @@ declare enum EventsSortDirection {
367
383
  ASC = "asc",
368
384
  DESC = "desc"
369
385
  }
386
+ declare function fetchEventData(publicKey: string, eventId: string, unused?: any, filter?: string, order?: EventsSortDirection, locale?: string): Promise<EventData>;
370
387
 
371
388
  interface CreativeWorkData {
372
389
  id: string;
@@ -382,6 +399,7 @@ declare enum CreativeWorksSortDirection {
382
399
  ASC = "asc",
383
400
  DESC = "desc"
384
401
  }
402
+ declare function fetchCreativeWorkData(publicKey: string, creativeWorkId: string, unused?: any, filter?: string, order?: CreativeWorksSortDirection, locale?: string): Promise<CreativeWorkData>;
385
403
 
386
404
  interface VideoProps extends Omit<PlayerProps, 'src'> {
387
405
  videoId?: string;
@@ -402,7 +420,7 @@ interface VideoProps extends Omit<PlayerProps, 'src'> {
402
420
  onCanPlay?: () => void;
403
421
  onPlayerReady?: () => void;
404
422
  };
405
- children?: React.ReactNode;
423
+ children?: React__default.ReactNode;
406
424
  className?: string;
407
425
  queryOptions?: {
408
426
  enabled?: boolean;
@@ -412,7 +430,7 @@ interface VideoProps extends Omit<PlayerProps, 'src'> {
412
430
  retryDelay?: number;
413
431
  };
414
432
  }
415
- declare const Video: React.FC<VideoProps>;
433
+ declare const Video: React__default.FC<VideoProps>;
416
434
 
417
435
  interface EventProps extends Omit<PlayerProps, 'src'> {
418
436
  publicKey: string;
@@ -450,14 +468,7 @@ interface EventProps extends Omit<PlayerProps, 'src'> {
450
468
  retryDelay?: number;
451
469
  };
452
470
  }
453
- declare const Event: React.FC<EventProps>;
454
-
455
- /**
456
- * Client-side wrapper for the Event component that prevents SSR issues.
457
- * This component only renders the Event component after the client has mounted,
458
- * preventing any browser API calls during server-side rendering.
459
- */
460
- declare const ClientSideEvent: React.FC<EventProps>;
471
+ declare const Event: React__default.FC<EventProps>;
461
472
 
462
473
  interface CreativeWorkProps extends Omit<PlayerProps, 'src'> {
463
474
  publicKey: string;
@@ -495,12 +506,15 @@ interface CreativeWorkProps extends Omit<PlayerProps, 'src'> {
495
506
  retryDelay?: number;
496
507
  };
497
508
  }
498
- declare const CreativeWork: React.FC<CreativeWorkProps>;
509
+ declare const CreativeWork: React__default.FC<CreativeWorkProps>;
499
510
 
500
- declare const queryClient: QueryClient;
501
511
  interface QueryProviderProps {
502
- children: React.ReactNode;
512
+ children: React__default.ReactNode;
503
513
  }
504
- declare const QueryProvider: React.FC<QueryProviderProps>;
514
+ declare const QueryProvider: React__default.FC<QueryProviderProps>;
515
+
516
+ declare const _default: {
517
+ Player: React.ForwardRefExoticComponent<PlayerProps & React.RefAttributes<HTMLVideoElement>>;
518
+ };
505
519
 
506
- export { ClientSideEvent, CreativeWork, type CreativeWorkData, type CreativeWorkProps, CreativeWorksSortDirection, Event, type EventData, type EventProps, EventsSortDirection, Player, type PlayerEvents, type PlayerProps, QueryProvider, Video, type VideoData, type VideoListItem, type VideoProps, queryClient };
520
+ export { CreativeWork, type CreativeWorkData, type CreativeWorkProps, CreativeWorksSortDirection, Event, type EventData, type EventProps, EventsSortDirection, type MuxAnalyticsConfig, type MuxDataUpdatePayload, type MuxErrorTranslator, type MuxMetadata, Player, type PlayerEvents, type PlayerProps, QueryProvider, type SeekbarConfig, Video, type VideoData, type VideoListItem, type VideoProps, _default as default, fetchCreativeWorkData, fetchEventData, fetchVideoData, fetchVideosList };