@devix-technologies/react-gjirafa-vp-player 1.0.31-beta.18 → 1.0.31-beta.20

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
@@ -53,20 +53,16 @@ import { VPPlayer } from "@devix-technologies/react-gjirafa-vp-player";
53
53
  <VPPlayer
54
54
  scriptId="ptkzurnx"
55
55
  videoId="vjsobqhe"
56
- onPlay={() => analytics.track('play')}
57
- onQuartile25={() => analytics.track('25% watched')}
58
- onProgress20s={() => analytics.track('20s milestone')}
56
+ onPlay={() => analytics.track("play")}
57
+ onQuartile25={() => analytics.track("25% watched")}
58
+ onProgress20s={() => analytics.track("20s milestone")}
59
59
  />
60
60
  ```
61
61
 
62
62
  ### Vertical Player
63
63
 
64
64
  ```tsx
65
- <VPPlayer
66
- scriptId="rbqcdwzlg"
67
- videoId="vjsobqhe"
68
- isVertical={true}
69
- />
65
+ <VPPlayer scriptId="rbqcdwzlg" videoId="vjsobqhe" isVertical={true} />
70
66
  ```
71
67
 
72
68
  ### Reels Mode (TikTok-style)
@@ -109,8 +105,8 @@ For vertical player with your own videos array (not using GTech admin playlists)
109
105
  ],
110
106
  startIndex: 0,
111
107
  }}
112
- onNext={() => console.log('Next video')}
113
- onVideoStarted={(data) => console.log('Playing:', data.title)}
108
+ onNext={() => console.log("Next video")}
109
+ onVideoStarted={(data) => console.log("Playing:", data.title)}
114
110
  />
115
111
  ```
116
112
 
@@ -132,59 +128,111 @@ The player fills its container (100% width/height). Control sizing with a wrappe
132
128
  </div>
133
129
  ```
134
130
 
131
+ ## Video Locking
132
+
133
+ Video locking lets you lock a portion of each video (e.g. require watching N seconds or N% before skipping). The SDK supports this via a `shouldLockVideo` callback and `removeVideoLock()`. The recommended approach is to set the callback inside `onVideoSwitch` so locking applies after each video switch.
134
+
135
+ ### Ref methods
136
+
137
+ - **`shouldLockVideo(callback)`** – Set the callback that returns lock config. Pass a function that returns `{ isEnabled, type, value }`, or `null` to clear. Matches SDK naming.
138
+ - **`removeVideoLock()`** – Clear any active lock.
139
+
140
+ ### Lock config shape
141
+
142
+ ```ts
143
+ { isEnabled: boolean; type: "seconds" | "percentage"; value: number }
144
+ ```
145
+
146
+ ### Recommended pattern: lock on video switch
147
+
148
+ Use `onVideoSwitch` to set or clear `shouldLockVideo` after each switch. In the callback you can decide whether to lock (e.g. from app state) and for how long.
149
+
150
+ ```tsx
151
+ const playerRef = useRef<VPPlayerRef>(null);
152
+
153
+ <VPPlayer
154
+ ref={playerRef}
155
+ scriptId="ptkzurnx"
156
+ videoId="vjsobqhe"
157
+ config={{ /* ... */ }}
158
+ onVideoSwitch={() => {
159
+ const shouldLock = true; // your logic, e.g. from state
160
+ if (shouldLock) {
161
+ playerRef.current?.shouldLockVideo(() => ({
162
+ isEnabled: true,
163
+ type: "seconds",
164
+ value: 20,
165
+ }));
166
+ } else {
167
+ playerRef.current?.removeVideoLock();
168
+ playerRef.current?.shouldLockVideo(() => ({
169
+ isEnabled: false,
170
+ type: "seconds",
171
+ value: 0,
172
+ }));
173
+ }
174
+ }}
175
+ />
176
+ ```
177
+
178
+ To only lock for a period after each switch, enable locking in `onVideoSwitch` with the desired `value` (e.g. 20 seconds). To disable locking for the next video, call `removeVideoLock()` and set `shouldLockVideo` to return `isEnabled: false`.
179
+
135
180
  ## Props Reference
136
181
 
137
182
  ### Identification (one required)
138
183
 
139
- | Prop | Type | Description |
140
- |------|------|-------------|
141
- | `scriptId` | `string` | GTech script ID (e.g., "ptkzurnx") |
142
- | `videoId` | `string` | GTech video ID (e.g., "vjsobqhe") |
143
- | `scriptUrl` | `string` | Full managed script URL |
184
+ | Prop | Type | Description |
185
+ | ----------- | -------- | ---------------------------------- |
186
+ | `scriptId` | `string` | GTech script ID (e.g., "ptkzurnx") |
187
+ | `videoId` | `string` | GTech video ID (e.g., "vjsobqhe") |
188
+ | `scriptUrl` | `string` | Full managed script URL |
144
189
 
145
190
  ### Optional
146
191
 
147
- | Prop | Type | Description |
148
- |------|------|-------------|
149
- | `projectId` | `string` | GTech project ID (required for manual playlist) |
150
- | `playlist` | `ManualPlaylistConfig` | Custom videos array for manual playlist mode |
151
- | `playerId` | `string` | Custom container ID (auto-generated if not provided) |
152
- | `isVertical` | `boolean` | Force vertical player mode |
153
- | `isReels` | `boolean` | Enable Reels overlay mode |
154
- | `thumbnailUrl` | `string` | Thumbnail for Reels mode |
155
- | `className` | `string` | CSS class for container |
156
- | `hiddenClasses` | `string[]` | SDK element classes to hide |
192
+ | Prop | Type | Description |
193
+ | --------------- | ---------------------- | ---------------------------------------------------- |
194
+ | `projectId` | `string` | GTech project ID (required for manual playlist) |
195
+ | `playlist` | `ManualPlaylistConfig` | Custom videos array for manual playlist mode |
196
+ | `playerId` | `string` | Custom container ID (auto-generated if not provided) |
197
+ | `isVertical` | `boolean` | Force vertical player mode |
198
+ | `isReels` | `boolean` | Enable Reels overlay mode |
199
+ | `thumbnailUrl` | `string` | Thumbnail for Reels mode |
200
+ | `className` | `string` | CSS class for container |
201
+ | `hiddenClasses` | `string[]` | SDK element classes to hide |
157
202
 
158
203
  ### Event Callbacks
159
204
 
160
- | Prop | Horizontal | Vertical | Description |
161
- |------|:----------:|:--------:|-------------|
162
- | `onReady` | | | Player initialized |
163
- | `onPlay` | | | Playback started (initial play) |
164
- | `onPause` | | | Playback paused (user-initiated) |
165
- | `onResume` | | | Playback resumed after pause (vertical only) |
166
- | `onComplete` | | | Video completed (horizontal only) |
167
- | `onError` | | | Error occurred |
168
- | `onVideoStarted` | | | Video metadata available (returns `CurrentVideoData`) |
169
- | `onTimeUpdate` | | | Continuous time updates (returns seconds) |
170
- | `onQuartile25` | | | 25% watched |
171
- | `onQuartile50` | | | 50% watched |
172
- | `onQuartile75` | | | 75% watched |
173
- | `onNext` | | | Next video in playlist |
174
- | `onPrevious` | | | Previous video (vertical only) |
175
- | `onProgress10s` | | | Every ~10 seconds (returns seconds) |
176
- | `onProgress20s` | | | 20 second milestone (returns seconds) |
177
- | `onClose` | - | - | Reels overlay closed |
205
+ | Prop | Horizontal | Vertical | Description |
206
+ | ---------------- | :--------: | :------: | ----------------------------------------------------- |
207
+ | `onReady` | | | Player initialized |
208
+ | `onPlay` | | | Playback started (initial play) |
209
+ | `onPause` | | | Playback paused (user-initiated) |
210
+ | `onResume` | | | Playback resumed after pause (vertical only) |
211
+ | `onComplete` | | | Video completed (horizontal only) |
212
+ | `onError` | | | Error occurred |
213
+ | `onVideoStarted` | | | Video metadata available (returns `CurrentVideoData`) |
214
+ | `onTimeUpdate` | | | Continuous time updates (returns seconds) |
215
+ | `onQuartile25` | | | 25% watched |
216
+ | `onQuartile50` | | | 50% watched |
217
+ | `onQuartile75` | | | 75% watched |
218
+ | `onNext` | | | Next video in playlist |
219
+ | `onPrevious` | | | Previous video (vertical only) |
220
+ | `onProgress10s` | | | Every ~10 seconds (returns seconds) |
221
+ | `onProgress20s` | | | 20 second milestone (returns seconds) |
222
+ | `onVideoSwitch` | ✓ | ✓ | SDK `vp-video-switch` (use for video locking) |
223
+ | `onClose` | - | - | Reels overlay closed |
178
224
 
179
225
  #### Event Implementation Notes
180
226
 
181
227
  **Horizontal Player** uses standard VP Player SDK events via `.on()` listener:
228
+
182
229
  - `ready`, `play`, `pause`, `complete`, `video-started`, `video-state`, `time`, `playlistItem`, `error`
183
230
  - Quartiles: `analytics-25%-completed`, `analytics-50%-completed`, `analytics-75%-completed`
184
231
 
185
232
  **Vertical Player** uses a combination of `.on()` listener and the global `vp-event` handler:
186
233
 
187
234
  Standard events via `.on()`:
235
+
188
236
  - `vp-first-frame`, `vp-video-started`, `vp-video-state`, `vp-time`, `vp-video-switch`, `error`
189
237
  - Quartiles: `analytics-25%-completed`, `analytics-50%-completed`, `analytics-75%-completed`
190
238
 
@@ -192,15 +240,16 @@ Standard events via `.on()`:
192
240
 
193
241
  The following events are intercepted from the global `vp-event` emission because the VP Vertical Player SDK doesn't expose them via the standard `.on()` listener:
194
242
 
195
- | Event | SDK Event | Special Logic |
196
- |-------|-----------|---------------|
197
- | `onPlay` | `vp-state-playing` | Fires on initial play (when not resuming from pause) |
198
- | `onPause` | `vp-state-paused` | Only fires on **user-initiated** pause (not on video loop/end) |
199
- | `onResume` | `vp-state-playing` | Fires when resuming after **user-initiated** pause |
243
+ | Event | SDK Event | Special Logic |
244
+ | ---------- | ------------------ | -------------------------------------------------------------- |
245
+ | `onPlay` | `vp-state-playing` | Fires on initial play (when not resuming from pause) |
246
+ | `onPause` | `vp-state-paused` | Only fires on **user-initiated** pause (not on video loop/end) |
247
+ | `onResume` | `vp-state-playing` | Fires when resuming after **user-initiated** pause |
200
248
 
201
249
  **User Interaction Tracking:**
202
250
 
203
251
  To distinguish between user actions and automatic playback events (like video loops), we track the `vp-user-interaction` event:
252
+
204
253
  - When `vp-user-interaction` fires before `vp-state-paused` → user clicked pause → `onPause` fires
205
254
  - When `vp-user-interaction` fires before `vp-state-playing` (after pause) → user clicked play → `onResume` fires
206
255
  - When `vp-state-paused` fires without prior user interaction → video ended/looped → no `onPause` (silent)
@@ -209,6 +258,7 @@ To distinguish between user actions and automatic playback events (like video lo
209
258
  This ensures analytics events like `player_pause` and `player_resume` only fire for actual user actions, not automatic video looping.
210
259
 
211
260
  **Key Differences:**
261
+
212
262
  - `onComplete` - Only available on horizontal player (vertical auto-advances to next video)
213
263
  - `onResume` - Only available on vertical player (horizontal has no pause/resume distinction)
214
264
  - `onPrevious` - Only available on vertical player (horizontal has no direction detection)
@@ -222,14 +272,14 @@ import {
222
272
  getFullyManagedPlayerScriptUrl,
223
273
  getVerticalPlayerScriptUrl,
224
274
  appendDivIdToUrl,
225
- } from '@devix-technologies/react-gjirafa-vp-player';
275
+ } from "@devix-technologies/react-gjirafa-vp-player";
226
276
 
227
277
  // Build managed script URL
228
- const url = getFullyManagedPlayerScriptUrl('ptkzurnx', 'vjsobqhe');
278
+ const url = getFullyManagedPlayerScriptUrl("ptkzurnx", "vjsobqhe");
229
279
  // => "https://host.vpplayer.tech/player/ptkzurnx/vjsobqhe.js"
230
280
 
231
281
  // Build vertical player URL
232
- const verticalUrl = getVerticalPlayerScriptUrl('rbqcdwzlg');
282
+ const verticalUrl = getVerticalPlayerScriptUrl("rbqcdwzlg");
233
283
  // => "https://host.vpplayer.tech/vertical-player/rbqcdwzlg.js"
234
284
  ```
235
285
 
@@ -238,13 +288,16 @@ const verticalUrl = getVerticalPlayerScriptUrl('rbqcdwzlg');
238
288
  For programmatic control, use the hook directly:
239
289
 
240
290
  ```tsx
241
- import { useVPPlayerLogic, PlayerContainer } from '@devix-technologies/react-gjirafa-vp-player';
291
+ import {
292
+ useVPPlayerLogic,
293
+ PlayerContainer,
294
+ } from "@devix-technologies/react-gjirafa-vp-player";
242
295
 
243
296
  function CustomPlayer() {
244
297
  const { playerRef, playerInstanceRef, isScriptLoaded, generatedPlayerId } =
245
298
  useVPPlayerLogic({
246
- scriptId: 'ptkzurnx',
247
- videoId: 'vjsobqhe',
299
+ scriptId: "ptkzurnx",
300
+ videoId: "vjsobqhe",
248
301
  });
249
302
 
250
303
  return (
@@ -257,7 +310,9 @@ function CustomPlayer() {
257
310
  $hiddenClasses={[]}
258
311
  />
259
312
  <button onClick={() => playerInstanceRef.current?.play?.()}>Play</button>
260
- <button onClick={() => playerInstanceRef.current?.pause?.()}>Pause</button>
313
+ <button onClick={() => playerInstanceRef.current?.pause?.()}>
314
+ Pause
315
+ </button>
261
316
  </div>
262
317
  );
263
318
  }
package/dist/index.d.ts CHANGED
@@ -267,6 +267,8 @@ export declare interface EventCallbackRefs {
267
267
  onPrevious?: (currentTime: number) => void;
268
268
  onProgress10s?: (seconds: number) => void;
269
269
  onProgress20s?: (seconds: number) => void;
270
+ onPlaylistItem?: (data: CurrentVideoData_2) => void;
271
+ onVideoSwitch?: (data: VideoSwitchData) => void;
270
272
  }
271
273
 
272
274
  /**
@@ -726,7 +728,7 @@ export declare interface TimesliderSkin {
726
728
  * 1. **Auto-init mode** (default): Load managed script that auto-initializes with GTech admin config
727
729
  * 2. **Config override mode**: Fetch config, merge user overrides, and call setup()
728
730
  */
729
- export declare const useVPPlayerLogic: ({ scriptId, videoId, scriptUrl, projectId, config, playerId, isVertical, isReels, onReady, onPlay, onPause, onResume, onComplete, onError, onVideoStarted, onTimeUpdate, onQuartile25, onQuartile50, onQuartile75, onNext, onPrevious, onProgress10s, onProgress20s, }: VPPlayerProps_2) => {
731
+ export declare const useVPPlayerLogic: ({ scriptId, videoId, scriptUrl, projectId, config, playerId, isVertical, isReels, onReady, onPlay, onPause, onResume, onComplete, onError, onVideoStarted, onTimeUpdate, onQuartile25, onQuartile50, onQuartile75, onNext, onPrevious, onProgress10s, onProgress20s, onPlaylistItem, onVideoSwitch, }: VPPlayerProps_2) => {
730
732
  playerRef: RefObject<HTMLDivElement | null>;
731
733
  playerInstanceRef: RefObject<VPPlayerInstance_2 | null>;
732
734
  isScriptLoaded: boolean;
@@ -818,6 +820,17 @@ export declare interface VideoFlag {
818
820
  name: string;
819
821
  }
820
822
 
823
+ export declare interface VideoLockingConfig {
824
+ isEnabled: boolean;
825
+ type: "seconds" | "percentage";
826
+ value: number;
827
+ }
828
+
829
+ export declare interface VideoSwitchData {
830
+ direction?: "next" | "prev";
831
+ currentTime?: number;
832
+ }
833
+
821
834
  /**
822
835
  * Volume skin configuration
823
836
  */
@@ -936,7 +949,7 @@ export declare interface VPPlayerConfig {
936
949
  * @interface VPPlayerInstance
937
950
  */
938
951
  export declare interface VPPlayerInstance {
939
- setup: (config: unknown) => void;
952
+ setup: (config: unknown) => Promise<void> | void;
940
953
  destroy?: () => void;
941
954
  play?: () => void;
942
955
  pause?: () => void;
@@ -974,6 +987,9 @@ export declare interface VPPlayerInstance {
974
987
  setCurrentQuality?: (index: number) => void;
975
988
  setLevelToAuto?: () => void;
976
989
  setCuePoint?: (cue: number, callback: () => void) => void;
990
+ setVideoLocking?: (config: VideoLockingConfig) => void;
991
+ removeVideoLock?: () => void;
992
+ shouldLockVideo?: () => VideoLockingConfig;
977
993
  on?: (event: string, callback: (data?: unknown) => void) => void;
978
994
  off?: (event: string, callback?: (data?: unknown) => void) => void;
979
995
  videoIndex?: number;
@@ -1151,6 +1167,17 @@ export declare interface VPPlayerProps {
1151
1167
  * @param seconds - Current playback position (should be ~20)
1152
1168
  */
1153
1169
  onProgress20s?: (seconds: number) => void;
1170
+ /**
1171
+ * Called when the active playlist item changes (horizontal player only)
1172
+ * Fires reliably for all playlist transitions, including auto-advance
1173
+ * @param data - Video metadata for the new playlist item
1174
+ */
1175
+ onPlaylistItem?: (data: CurrentVideoData_2) => void;
1176
+ /**
1177
+ * Called when the SDK emits vp-video-switch (e.g. user switched to next/prev video).
1178
+ * Use with ref.shouldLockVideo() for video locking; see README Video Locking section.
1179
+ */
1180
+ onVideoSwitch?: (data: VideoSwitchData) => void;
1154
1181
  }
1155
1182
 
1156
1183
  /**
@@ -1189,6 +1216,15 @@ export declare interface VPPlayerRef {
1189
1216
  isPlaying: () => boolean;
1190
1217
  isPaused: () => boolean;
1191
1218
  isFullscreen: () => boolean;
1219
+ /**
1220
+ * Set the shouldLockVideo callback on the player (SDK naming).
1221
+ * Pass a function that returns lock config, or null to clear.
1222
+ */
1223
+ shouldLockVideo: (callback: (() => VideoLockingConfig) | null) => void;
1224
+ /**
1225
+ * Remove/unlock the video.
1226
+ */
1227
+ removeVideoLock: () => void;
1192
1228
  }
1193
1229
 
1194
1230
  /**