@flowplayer/spins 0.5.1 → 0.7.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.
package/README.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  A TypeScript library for creating Flowplayer playlist components optimized for short-form video content like Reels and Shorts.
4
4
 
5
+ ## Quick Start
6
+
5
7
  ```typescript
6
8
  import { createSpins } from "@flowplayer/spins";
7
9
 
8
- const spinsContainer = createSpins({
10
+ const spinsContainer = createSpins({
9
11
  playlist: "your-playlist-id",
10
12
  token: "your-token",
11
13
  lang: "en",
@@ -13,30 +15,6 @@ const spinsContainer = createSpins({
13
15
  document.body.appendChild(spinsContainer);
14
16
  ```
15
17
 
16
- ### React Usage
17
-
18
- ```tsx
19
- import { createSpins, type SpinsConfig } from "@flowplayer/spins";
20
- import { useEffect, useRef } from "react";
21
-
22
- function SpinsPlayer({ playlistId }: { playlistId: string }) {
23
- const containerRef = useRef<HTMLDivElement>(null);
24
-
25
- useEffect(() => {
26
- if (containerRef.current) {
27
- const spinsContainer = createSpins({
28
- playlist: playlistId,
29
- token: "your-token",
30
- lang: "en",
31
- });
32
- containerRef.current.appendChild(spinsContainer);
33
- }
34
- }, [playlistId]);
35
-
36
- return <div ref={containerRef} className="spins-container" />;
37
- }
38
- ```
39
-
40
18
  ## Features
41
19
 
42
20
  - 🎯 **Type-safe** - Built with TypeScript and Zod schema validation
@@ -56,20 +34,76 @@ bun add @flowplayer/spins
56
34
 
57
35
  ## Usage
58
36
 
37
+ ### Basic Usage
38
+
59
39
  ```typescript
60
- import { spins, type SpinsConfig } from "@flowplayer/spins";
40
+ import { createSpins, type SpinsConfig } from "@flowplayer/spins";
61
41
 
62
- // Create a playlist component for short-form content
63
42
  const config: SpinsConfig = {
64
43
  playlist: "your-playlist-id", // or array of SpinItem objects
65
44
  token: "your-flowplayer-token",
66
45
  lang: "en"
67
46
  };
68
47
 
69
- const spinsContainer = spins(config);
48
+ const spinsContainer = createSpins(config);
70
49
  document.body.appendChild(spinsContainer);
71
50
  ```
72
51
 
52
+ ### React Integration
53
+
54
+ ```tsx
55
+ import { createSpins, type SpinsConfig } from "@flowplayer/spins";
56
+ import { useEffect, useRef } from "react";
57
+
58
+ function SpinsPlayer({ playlistId }: { playlistId: string }) {
59
+ const containerRef = useRef<HTMLDivElement>(null);
60
+
61
+ useEffect(() => {
62
+ if (!containerRef.current) return;
63
+
64
+ const spinsContainer = createSpins({
65
+ playlist: playlistId,
66
+ token: "your-token",
67
+ lang: "en",
68
+ });
69
+
70
+ containerRef.current.appendChild(spinsContainer);
71
+
72
+ return () => {
73
+ spinsContainer.remove();
74
+ };
75
+ }, [playlistId]);
76
+
77
+ return <div ref={containerRef} className="spins-container" />;
78
+ }
79
+ ```
80
+
81
+ ### With Custom Playlist
82
+
83
+ ```typescript
84
+ import { createSpins, type SpinItem } from "@flowplayer/spins";
85
+
86
+ const customPlaylist: SpinItem[] = [
87
+ {
88
+ url: "https://example.com/video1.mp4",
89
+ title: "First Video",
90
+ poster: "https://example.com/poster1.jpg",
91
+ description: "Description of first video"
92
+ },
93
+ {
94
+ url: { src: "https://example.com/video2.mp4", type: "video/mp4" },
95
+ title: "Second Video",
96
+ poster: "https://example.com/poster2.jpg"
97
+ }
98
+ ];
99
+
100
+ const spinsContainer = createSpins({
101
+ playlist: customPlaylist,
102
+ token: "your-token",
103
+ lang: "en"
104
+ });
105
+ ```
106
+
73
107
  ## Short-Form Video Content
74
108
 
75
109
  This library is specifically designed to handle short-form video playlists, making it easy to create engaging vertical video experiences similar to:
@@ -86,106 +120,297 @@ The component automatically handles playlist loading and provides the structure
86
120
 
87
121
  Creates a Flowplayer playlist component container optimized for short-form video content.
88
122
 
89
- #### Parameters
123
+ #### Configuration Options
90
124
 
91
- - `config.playlist: string | Array<SpinConfig>` - The playlist ID or array of spin configurations
92
- - `config.token: string` - Your Flowplayer token
93
- - `config.lang: string` - Language code (e.g., "en", "es")
125
+ ```typescript
126
+ interface SpinsConfig {
127
+ playlist: string | Array<SpinItem>; // Playlist ID or array of SpinItem
128
+ token?: string; // Your Flowplayer token
129
+ lang?: string; // Language code (e.g., "en", "es")
130
+ ui?: number; // UI configuration bitmask
131
+ ima?: PlayerIMAConfig; // IMA ads configuration
132
+ adsFrequency?: number; // Show ads every Nth spin (default: 10)
133
+ share?: ShareConfig; // Share plugin configuration
134
+ consent?: number; // Consent plugin configuration
135
+ plugins?: string[]; // List of plugins to enable
136
+ }
137
+ ```
94
138
 
95
139
  #### Returns
96
140
 
97
- Returns a `SpinsContainer` (extends `HTMLElement`) with event handling capabilities:
98
- - `on(event, handler)` - Subscribe to events
99
- - `off(event, handler)` - Unsubscribe from events
141
+ A `SpinsContainer` instance (extends `HTMLElement`) with the following methods:
100
142
 
101
- #### Events
143
+ ##### Event Handling
102
144
 
103
- - `SPIN_CREATED` - Fired when a new spin is created
104
- - `SPIN_IN_VIEWPORT` - Fired when a spin enters the viewport
145
+ ```typescript
146
+ // Listen to spin lifecycle events
147
+ spinsContainer.on(spinEvents.SPIN_CREATED, (e) => {
148
+ console.log('Spin created:', e.detail.config, e.detail.index);
149
+ });
105
150
 
106
- #### Error Handling
151
+ spinsContainer.on(spinEvents.SPIN_IN_VIEWPORT, (e) => {
152
+ console.log('Spin in viewport:', e.detail.index);
153
+ });
154
+
155
+ // Listen to player events (media events)
156
+ spinsContainer.onPlayerEvent(mediaEvents.PLAY, (e) => {
157
+ console.log('Video playing:', e.detail.spinIndex, e.detail.currentTime);
158
+ });
159
+
160
+ spinsContainer.onPlayerEvent(mediaEvents.ENDED, (e) => {
161
+ console.log('Video ended:', e.detail.spinIndex);
162
+ });
163
+
164
+ // Listen to ad events
165
+ spinsContainer.onPlayerEvent(adEvents.AD_STARTED, (e) => {
166
+ console.log('Ad started:', e.detail.spinIndex);
167
+ });
168
+
169
+ // Remove event listeners
170
+ spinsContainer.off(spinEvents.SPIN_CREATED, handler);
171
+ spinsContainer.offPlayerEvent(mediaEvents.PLAY, handler);
172
+ ```
173
+
174
+ ##### Dynamic Content
175
+
176
+ ```typescript
177
+ // Add more spins dynamically
178
+ spinsContainer.addSpins([
179
+ {
180
+ url: "https://example.com/video3.mp4",
181
+ title: "New Video",
182
+ poster: "https://example.com/poster3.jpg"
183
+ }
184
+ ]);
185
+ ```
107
186
 
108
- The function gracefully handles:
109
- - Network errors (API unavailable, timeout, etc.)
110
- - Invalid playlist configurations
111
- - HTTP errors (404, 500, etc.)
187
+ ### Events
188
+
189
+ #### Spin Events (`spinEvents`)
190
+
191
+ - `SPIN_CREATED` - Fired when a new spin is created
192
+ - `detail.config`: SpinItem configuration
193
+ - `detail.spin`: The spin container HTMLElement
194
+ - `detail.index`: Spin index
195
+
196
+ - `SPIN_IN_VIEWPORT` - Fired when a spin enters the viewport
197
+ - `detail.config`: SpinItem configuration
198
+ - `detail.spin`: The spin container HTMLElement
199
+ - `detail.index`: Spin index
200
+
201
+ #### Media Events (`mediaEvents`)
202
+
203
+ - `PLAY` - Playback started/resumed
204
+ - `PAUSE` - Playback paused
205
+ - `PLAYING` - Actively playing
206
+ - `WAITING` - Buffering
207
+ - `CAN_PLAY` - Ready to play
208
+ - `TIME_UPDATE` - Playback position changed
209
+ - `SEEKING` - User seeking
210
+ - `ENDED` - Playback ended
211
+
212
+ All media events include:
213
+ - `detail.spinIndex`: Index of the spin
214
+ - `detail.currentTime`: Current playback time in seconds
215
+ - `detail.duration`: Video duration in seconds
216
+ - `detail.mediaId`: Media ID (if available)
217
+ - `detail.url`: Full URL of the media
218
+
219
+ #### Ad Events (`adEvents`)
220
+
221
+ - `AD_STARTED` - Ad playback started
222
+ - `AD_COMPLETED` - Ad finished playing
223
+ - `AD_PROGRESS` - Ad playback progress
224
+ - `AD_PAUSED` - Ad paused
225
+ - `AD_RESUMED` - Ad resumed
226
+ - `AD_SKIPPED` - Ad skipped by user
227
+ - `AD_ERROR` - Ad error occurred
228
+ - `AD_REQUEST_ERROR` - Ad request failed
229
+ - `AD_PLAYBACK_ERROR` - Ad playback error
230
+ - `AD_BREAK_COMPLETED` - Ad break finished
231
+ - `AD_TEARDOWN` - Ad cleanup
232
+ - `AD_MUTED` - Ad muted
233
+ - `AD_VOLUME_CHANGED` - Ad volume changed
234
+ - `AD_PREROLL_FINSIHED` - Preroll ad finished
235
+
236
+ All ad events include:
237
+ - `detail.spinIndex`: Index of the spin where the ad is shown
112
238
 
113
239
  ## TypeScript Support
114
240
 
115
- The library exports TypeScript types for full type safety:
241
+ The library is fully typed with TypeScript. Import types for full type safety:
116
242
 
117
243
  ```typescript
118
- import { type SpinsConfig, type SpinItem, type SpinsContainer } from "@flowplayer/spins";
244
+ import {
245
+ createSpins,
246
+ createAdPlayer,
247
+ spinEvents,
248
+ mediaEvents,
249
+ adEvents,
250
+ type SpinsConfig,
251
+ type SpinItem,
252
+ type SpinsContainer,
253
+ type SpinsAdPlayer,
254
+ type SpinsAdPlayerConfig,
255
+ type SpinEvent,
256
+ type MediaEvent,
257
+ type AdEvent,
258
+ type PlayerEvent,
259
+ type SpinEventData,
260
+ type MediaEventData,
261
+ type AdEventData,
262
+ type PlayerEventsDetailMap,
263
+ type AdEventsDetailMap,
264
+ } from "@flowplayer/spins";
119
265
  ```
120
266
 
121
- ### `SpinsConfig`
267
+ ### Key Type Definitions
268
+
269
+ #### `SpinsConfig`
122
270
 
123
- The main configuration interface:
271
+ Main configuration for creating a spins container:
124
272
 
125
273
  ```typescript
126
274
  interface SpinsConfig {
127
275
  playlist: string | Array<SpinItem>;
128
- token: string;
129
- lang: string;
276
+ token?: string;
277
+ lang?: string;
278
+ ui?: number;
279
+ ima?: PlayerIMAConfig;
280
+ adsFrequency?: number;
281
+ share?: ShareConfig;
282
+ consent?: number;
283
+ plugins?: string[];
130
284
  }
131
285
  ```
132
286
 
133
- ### `SpinItem`
287
+ #### `SpinItem`
134
288
 
135
- Individual spin configuration:
289
+ Individual video configuration:
136
290
 
137
291
  ```typescript
138
292
  interface SpinItem {
139
- src: Config["src"];
293
+ url: string | { src: string; type: string };
140
294
  title?: string;
141
295
  poster?: string;
142
296
  description?: string;
143
- subtitles?: SubtitlesConfig["subtitles"];
297
+ subtitles?: SubtitlesConfig;
144
298
  }
145
299
  ```
146
300
 
147
- ## CSS Styling
301
+ #### `SpinsContainer`
148
302
 
149
- The component includes default CSS styling optimized for short-form video content. Styles are imported from:
150
- You can override styles by targeting the spins container and its children:
303
+ The container element with event handling:
151
304
 
152
- ```css
153
- .spins-container {
154
- /* Base spins container styles */
305
+ ```typescript
306
+ interface SpinsContainer extends HTMLElement {
307
+ on(event: SpinEvent, handler: (e: CustomEvent<SpinEventData>) => void): void;
308
+ off(event: SpinEvent, handler: (e: CustomEvent<SpinEventData>) => void): void;
309
+ onPlayerEvent<K extends PlayerEvent>(
310
+ event: K,
311
+ handler: (e: CustomEvent<PlayerEventsDetailMap[K]>) => void
312
+ ): void;
313
+ offPlayerEvent<K extends PlayerEvent>(
314
+ event: K,
315
+ handler: (e: CustomEvent<PlayerEventsDetailMap[K]>) => void
316
+ ): void;
317
+ addSpins(spins: SpinItem[]): void;
318
+ }
319
+ ```
320
+
321
+ #### Event Data Types
322
+
323
+ ```typescript
324
+ interface SpinEventData {
325
+ config: SpinItem;
326
+ spin: HTMLElement;
327
+ index: number;
155
328
  }
156
329
 
157
- flowplayer-spin {
158
- /* Individual spin container styles */
330
+ interface MediaEventData {
331
+ spinIndex: number;
332
+ duration?: number;
333
+ currentTime: number;
334
+ mediaId?: string;
335
+ url: string;
159
336
  }
337
+
338
+ type AdEventData<K extends AdEvent> = {
339
+ spinIndex: number;
340
+ } & AdEventsDetailMap[K];
160
341
  ```
161
342
 
162
- ## Development
343
+ ## Advanced Usage
163
344
 
164
- ### Prerequisites
345
+ ### Listening to All Events
165
346
 
166
- - [Bun](https://bun.sh/) runtime
167
- - TypeScript 5+
347
+ ```typescript
348
+ import {
349
+ createSpins,
350
+ spinEvents,
351
+ mediaEvents,
352
+ adEvents,
353
+ type SpinEventData,
354
+ type MediaEventData
355
+ } from "@flowplayer/spins";
356
+
357
+ const container = createSpins({
358
+ playlist: "your-playlist-id",
359
+ token: "your-token",
360
+ lang: "en",
361
+ adsFrequency: 5 // Show ads every 5th spin
362
+ });
168
363
 
169
- ### Scripts
364
+ // Spin lifecycle events
365
+ container.on(spinEvents.SPIN_CREATED, (e) => {
366
+ console.log(`Spin ${e.detail.index} created:`, e.detail.config);
367
+ });
170
368
 
171
- ```bash
172
- # Development with Storybook
173
- bun run dev
369
+ container.on(spinEvents.SPIN_IN_VIEWPORT, (e) => {
370
+ console.log(`Spin ${e.detail.index} is now visible`);
371
+ });
174
372
 
175
- # Build the library
176
- bun run build
373
+ // Media playback events
374
+ container.onPlayerEvent(mediaEvents.PLAY, (e) => {
375
+ console.log(`Playing spin ${e.detail.spinIndex}`);
376
+ });
177
377
 
178
- # Build TypeScript declarations
179
- bun run build:dts
378
+ container.onPlayerEvent(mediaEvents.TIME_UPDATE, (e) => {
379
+ console.log(`Progress: ${e.detail.currentTime}/${e.detail.duration}`);
380
+ });
180
381
 
181
- # Run tests
182
- bun run test
382
+ container.onPlayerEvent(mediaEvents.ENDED, (e) => {
383
+ console.log(`Spin ${e.detail.spinIndex} finished`);
384
+ });
183
385
 
184
- # Lint and format code
185
- bun run lint
386
+ // Ad events
387
+ container.onPlayerEvent(adEvents.AD_STARTED, (e) => {
388
+ console.log(`Ad started at spin ${e.detail.spinIndex}`);
389
+ });
186
390
 
187
- # Build Storybook for production
188
- bun run build-storybook
391
+ container.onPlayerEvent(adEvents.AD_COMPLETED, (e) => {
392
+ console.log(`Ad completed at spin ${e.detail.spinIndex}`);
393
+ });
394
+
395
+ document.body.appendChild(container);
396
+ ```
397
+
398
+ ### Advertising Configuration
399
+
400
+ ```typescript
401
+ import { createSpins } from "@flowplayer/spins";
402
+
403
+ const container = createSpins({
404
+ playlist: "your-playlist-id",
405
+ token: "your-token",
406
+ lang: "en",
407
+ adsFrequency: 3, // Show ads every 3rd spin
408
+ ima: {
409
+ ads: [{
410
+ adTag: "https://pubads.g.doubleclick.net/gampad/ads?...",
411
+ time: 0
412
+ }]}
413
+ });
189
414
  ```
190
415
 
191
416
  ## Contributing
@@ -196,6 +421,7 @@ This project uses:
196
421
  - **Vitest** with **Playwright** for browser testing
197
422
  - **Storybook** for component development and documentation
198
423
  - **Lit** for custom web components
424
+ - **Zod** for runtime schema validation
199
425
 
200
426
  ## License
201
427