@gcorevideo/player 2.29.0 → 2.30.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/lib/Player.d.ts CHANGED
@@ -132,6 +132,15 @@ export declare class Player {
132
132
  * @beta
133
133
  */
134
134
  load(mediaSources: PlayerMediaSource[]): void;
135
+ /**
136
+ * Returns a registered core plugin instance by name, or `null` if not found.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * const tokenRefresh = player.getPlugin('token_refresh') as TokenRefreshPlugin | null
141
+ * ```
142
+ */
143
+ getPlugin(name: string): any;
135
144
  /**
136
145
  * Mutes the sound of the video.
137
146
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Player.d.ts","sourceRoot":"","sources":["../src/Player.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Player.d.ts","sourceRoot":"","sources":["../src/Player.ts"],"names":[],"mappings":"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"}
package/lib/Player.js CHANGED
@@ -199,6 +199,17 @@ export class Player {
199
199
  }
200
200
  this.player?.load(ms, ms[0].mimeType ?? '');
201
201
  }
202
+ /**
203
+ * Returns a registered core plugin instance by name, or `null` if not found.
204
+ *
205
+ * @example
206
+ * ```ts
207
+ * const tokenRefresh = player.getPlugin('token_refresh') as TokenRefreshPlugin | null
208
+ * ```
209
+ */
210
+ getPlugin(name) {
211
+ return this.player?.core.getPlugin(name) ?? null;
212
+ }
202
213
  /**
203
214
  * Mutes the sound of the video.
204
215
  */
@@ -32,5 +32,6 @@ export * from './plugins/subtitles/ClosedCaptions.js';
32
32
  export { ClosedCaptions as Subtitles } from './plugins/subtitles/ClosedCaptions.js';
33
33
  export * from './plugins/telemetry/Telemetry.js';
34
34
  export * from './plugins/thumbnails/Thumbnails.js';
35
+ export * from './plugins/token-refresh/index.js';
35
36
  export * from './plugins/volume-fade/VolumeFade.js';
36
37
  //# sourceMappingURL=index.plugins.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.plugins.d.ts","sourceRoot":"","sources":["../src/index.plugins.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAA;AAElC,cAAc,yCAAyC,CAAA;AACvD,OAAO,EAAE,WAAW,IAAI,aAAa,EAAE,MAAM,yCAAyC,CAAA;AACtF,cAAc,4CAA4C,CAAA;AAC1D,cAAc,qCAAqC,CAAA;AACnD,cAAc,uCAAuC,CAAA;AACrD,cAAc,qCAAqC,CAAA;AACnD,cAAc,0CAA0C,CAAA;AACxD,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,0CAA0C,CAAA;AACvF,cAAc,0CAA0C,CAAA;AACxD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uCAAuC,CAAA;AACrD,cAAc,uCAAuC,CAAA;AACrD,cAAc,8BAA8B,CAAA;AAE5C,cAAc,+CAA+C,CAAA;AAC7D,cAAc,wBAAwB,CAAA;AACtC,cAAc,yCAAyC,CAAA;AACvD,cAAc,uCAAuC,CAAA;AACrD,cAAc,kDAAkD,CAAA;AAChE,cAAc,yCAAyC,CAAA;AACvD,cAAc,4BAA4B,CAAA;AAC1C,cAAc,2CAA2C,CAAA;AACzD,OAAO,EAAE,aAAa,IAAI,aAAa,EAAE,MAAM,2CAA2C,CAAA;AAC1F,cAAc,iCAAiC,CAAA;AAC/C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,sDAAsD,CAAA;AACpE,OAAO,EAAE,kBAAkB,IAAI,OAAO,EAAE,MAAM,sDAAsD,CAAA;AACpG,cAAc,iDAAiD,CAAA;AAC/D,cAAc,uCAAuC,CAAA;AACrD,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,MAAM,uCAAuC,CAAA;AACnF,cAAc,kCAAkC,CAAA;AAChD,cAAc,oCAAoC,CAAA;AAGlD,cAAc,qCAAqC,CAAA"}
1
+ {"version":3,"file":"index.plugins.d.ts","sourceRoot":"","sources":["../src/index.plugins.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAA;AAElC,cAAc,yCAAyC,CAAA;AACvD,OAAO,EAAE,WAAW,IAAI,aAAa,EAAE,MAAM,yCAAyC,CAAA;AACtF,cAAc,4CAA4C,CAAA;AAC1D,cAAc,qCAAqC,CAAA;AACnD,cAAc,uCAAuC,CAAA;AACrD,cAAc,qCAAqC,CAAA;AACnD,cAAc,0CAA0C,CAAA;AACxD,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,0CAA0C,CAAA;AACvF,cAAc,0CAA0C,CAAA;AACxD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uCAAuC,CAAA;AACrD,cAAc,uCAAuC,CAAA;AACrD,cAAc,8BAA8B,CAAA;AAE5C,cAAc,+CAA+C,CAAA;AAC7D,cAAc,wBAAwB,CAAA;AACtC,cAAc,yCAAyC,CAAA;AACvD,cAAc,uCAAuC,CAAA;AACrD,cAAc,kDAAkD,CAAA;AAChE,cAAc,yCAAyC,CAAA;AACvD,cAAc,4BAA4B,CAAA;AAC1C,cAAc,2CAA2C,CAAA;AACzD,OAAO,EAAE,aAAa,IAAI,aAAa,EAAE,MAAM,2CAA2C,CAAA;AAC1F,cAAc,iCAAiC,CAAA;AAC/C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,sDAAsD,CAAA;AACpE,OAAO,EAAE,kBAAkB,IAAI,OAAO,EAAE,MAAM,sDAAsD,CAAA;AACpG,cAAc,iDAAiD,CAAA;AAC/D,cAAc,uCAAuC,CAAA;AACrD,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,MAAM,uCAAuC,CAAA;AACnF,cAAc,kCAAkC,CAAA;AAChD,cAAc,oCAAoC,CAAA;AAGlD,cAAc,kCAAkC,CAAA;AAChD,cAAc,qCAAqC,CAAA"}
@@ -35,4 +35,5 @@ export * from './plugins/telemetry/Telemetry.js';
35
35
  export * from './plugins/thumbnails/Thumbnails.js';
36
36
  // _ vast-ads
37
37
  // _ video360
38
+ export * from './plugins/token-refresh/index.js';
38
39
  export * from './plugins/volume-fade/VolumeFade.js';
@@ -1 +1 @@
1
- {"version":3,"file":"DashPlayback.d.ts","sourceRoot":"","sources":["../../../src/playback/dash-playback/DashPlayback.ts"],"names":[],"mappings":"AAKA,OAAO,EAAe,QAAQ,EAAyB,MAAM,cAAc,CAAA;AAG3E,OAAO,EAGL,gBAAgB,EAKhB,aAAa,EAQd,MAAM,QAAQ,CAAA;AAEf,OAAO,EAGL,YAAY,EACZ,YAAY,EACZ,SAAS,EACV,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAkB,UAAU,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAA;AAMzE,KAAK,YAAY,GACb,OAAO,QAAQ,CAAC,GAAG,GACnB,OAAO,QAAQ,CAAC,IAAI,GACpB,OAAO,QAAQ,CAAC,GAAG,GACnB,OAAO,QAAQ,CAAC,KAAK,CAAA;AAEzB,KAAK,YAAY,GAAG,MAAM,CAAA;AAE1B,KAAK,oBAAoB,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAID,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,YAAY;IACpD,OAAO,EAAE,YAAY,EAAE,CAAK;IAE5B,aAAa,EAAE,MAAM,CAAO;IAE5B,mBAAmB,EAAE,MAAM,CAAK;IAKhC,mCAAmC,EAAE,OAAO,CAAQ;IAEpD,aAAa,EAAE,OAAO,CAAQ;IAI9B,uBAAuB,EAAE,MAAM,CAAI;IASnC,wBAAwB,EAAE,MAAM,CAAI;IAEpC,aAAa,EAAE,YAAY,CAAe;IAG1C,aAAa,EAAE,YAAY,GAAG,IAAI,CAAO;IAEzC,gBAAgB,EAAE,SAAS,CAAI;IAE/B,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAO;IAErC,2BAA2B,EAAE,MAAM,CAAI;IAEvC,aAAa,EAAE,SAAS,GAAG,IAAI,CAAO;IAEtC,eAAe,EAAE,YAAY,CAA2B;IAIxD,0BAA0B,EAAE,oBAAoB,GAAG,IAAI,CAAO;IAI9D,wBAAwB,EAAE,oBAAoB,GAAG,IAAI,CAAO;IAE5D,kBAAkB,UAAQ;IAE1B,YAAY,EAAE,aAAa,GAAG,IAAI,CAAO;IAEzC,gBAAgB,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,GAAG,IAAI,CAAO;IAE9D,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,IAAI,CAAO;IAEnD,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAO;IAEtD,IAAI,IAAI,WAEP;IAED,IAAI,MAAM,IAAI,YAAY,EAAE,CAE3B;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,OAAO,YAEV;IAED,IAAI,YAAY,CAAC,EAAE,EAAE,MAAM,EAoC1B;IAED,IAAI,UAAU,WAUb;IAED,IAAI,IAAI,WAEP;IAID,IAAI,sBAAsB,WAczB;IAID,IAAI,oBAAoB,WAgBvB;IAED,IAAI,SAAS,WAKZ;gBAEW,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,GAAG;IAOzD,MAAM;IAyFN,MAAM;IAMN,MAAM;YAKW,SAAS;IAI1B,qBAAqB;IAQrB,oBAAoB;IAapB,WAAW,IAAI,SAAS;IAQxB,cAAc,IAAI,SAAS;IAO3B,kBAAkB,IAAI,SAAS;IAItB,cAAc,CAAC,UAAU,EAAE,MAAM;IAejC,IAAI,CAAC,IAAI,EAAE,SAAS;IAgB7B,eAAe;IAIf,UAAU,CAAC,MAAM,EAAE,OAAO;IAMjB,eAAe;IAgBxB,OAAO,CAAC,gBAAgB,CAEvB;IAED,OAAO,CAAC,eAAe,CAkCtB;IAED,OAAO,CAAC,YAAY;IAiBX,aAAa;IAqBb,iBAAiB;IAW1B,IAAI,UAAU,YAQb;IAEQ,WAAW;IAgBX,IAAI;IAMJ,KAAK;IAUL,IAAI;IAQb,OAAO,CAAC,eAAe;IAuBd,OAAO;IAMhB,mBAAmB;IASnB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAQxB,eAAe;IAIf,aAAa;IAIb,OAAO,CAAC,QAAQ;IAMhB,eAAe,CAAC,IAAI,EAAE,MAAM;IAI5B,IAAI,WAAW,IAAI,UAAU,EAAE,CAI9B;IAGD,IAAI,iBAAiB,IAAI,UAAU,GAAG,IAAI,CAOzC;IAEQ,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQlC,IAAI,CAAC,MAAM,EAAE,MAAM;IAQ5B,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IACH,sBAAsB;IAKtB,YAAY,CAAC,EAAE,EAAE,MAAM;IAKvB;;OAEG;IACH,IAAI,oBAAoB;;;;;;;;;QAEvB;IAED,OAAO,CAAC,aAAa;CAYtB"}
1
+ {"version":3,"file":"DashPlayback.d.ts","sourceRoot":"","sources":["../../../src/playback/dash-playback/DashPlayback.ts"],"names":[],"mappings":"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"}
@@ -145,6 +145,7 @@ export default class DashPlayback extends BasePlayback {
145
145
  this._dash = dash;
146
146
  this._dash.initialize();
147
147
  if (this.options.dash) {
148
+ const { requestInterceptor, ...dashSettings } = this.options.dash;
148
149
  const settings = $.extend(true, {
149
150
  streaming: {
150
151
  text: {
@@ -156,8 +157,11 @@ export default class DashPlayback extends BasePlayback {
156
157
  // dispatchForManualRendering: true, // TODO only when useNativeSubtitles is not true?
157
158
  },
158
159
  },
159
- }, this.options.dash);
160
+ }, dashSettings);
160
161
  this._dash.updateSettings(settings);
162
+ if (typeof requestInterceptor === 'function') {
163
+ this._dash.addRequestInterceptor(requestInterceptor);
164
+ }
161
165
  }
162
166
  this._dash.attachView(this.el);
163
167
  this._dash.setAutoPlay(false);
@@ -0,0 +1,119 @@
1
+ import { CorePlugin } from '@clappr/core';
2
+ /**
3
+ * Response shape expected from your token-refresh API endpoint.
4
+ * @public
5
+ */
6
+ export interface TokenResponse {
7
+ /** Plain (non-IP-bound) secure token */
8
+ token: string;
9
+ /** IP-bound secure token */
10
+ token_ip: string;
11
+ /** Client IP address (informational) */
12
+ client_ip: string;
13
+ /** Unix timestamp (seconds) when both tokens expire */
14
+ expires: number;
15
+ /** Ready-to-use HLS master playlist URL with plain token embedded */
16
+ url: string;
17
+ /** Ready-to-use HLS master playlist URL with IP-bound token embedded */
18
+ url_ip: string;
19
+ }
20
+ /**
21
+ * Configuration options for {@link TokenRefreshPlugin}.
22
+ * @public
23
+ */
24
+ export interface TokenRefreshOptions {
25
+ /**
26
+ * Async function called each time a fresh token is needed.
27
+ * Must return a {@link TokenResponse}.
28
+ */
29
+ getToken: () => Promise<TokenResponse>;
30
+ /**
31
+ * When `true`, the IP-bound variant (`token_ip` / `url_ip`) is used.
32
+ * Defaults to `false`.
33
+ */
34
+ ipBound?: boolean;
35
+ /**
36
+ * Seconds before the token expiry timestamp to request a fresh token.
37
+ * Defaults to `5`.
38
+ */
39
+ refreshLeadSeconds?: number;
40
+ /**
41
+ * Optional callback invoked after every successful token refresh.
42
+ */
43
+ onTokenRefreshed?: (data: TokenResponse) => void;
44
+ }
45
+ /**
46
+ * `PLUGIN` — automatic token refresh for Gcore protected-content streams.
47
+ *
48
+ * Supports all three playback engines:
49
+ *
50
+ * | Engine | Mechanism | Interruption |
51
+ * |--------|-----------|--------------|
52
+ * | **hls.js** | Custom loader rewrites every request URL | None |
53
+ * | **dash.js** | `addRequestInterceptor` rewrites every request URL | None |
54
+ * | **Native `<video>`** (Safari ≤ iOS 14.4) | Source reload + seek restore | Brief |
55
+ *
56
+ * @public
57
+ * @remarks
58
+ * Register the plugin once before creating any player instance:
59
+ * ```ts
60
+ * import { Player, TokenRefreshPlugin } from '@gcorevideo/player'
61
+ * Player.registerPlugin(TokenRefreshPlugin)
62
+ * ```
63
+ *
64
+ * Then pass `tokenRefresh` in `PlayerConfig`:
65
+ * ```ts
66
+ * const player = new Player({
67
+ * sources: [{ source: initialUrl, mimeType: 'application/x-mpegURL' }],
68
+ * tokenRefresh: {
69
+ * getToken: () => fetch('https://…/token').then(r => r.json()),
70
+ * ipBound: false,
71
+ * refreshLeadSeconds: 5,
72
+ * onTokenRefreshed: (data) => console.log('new token expires', data.expires),
73
+ * },
74
+ * })
75
+ * ```
76
+ *
77
+ * @example
78
+ * Safari native — opt-in Service Worker for fully seamless refresh:
79
+ * ```js
80
+ * // Register token-refresh-sw.js (see example/ directory)
81
+ * // and omit tokenRefresh config — the SW handles rewriting.
82
+ * ```
83
+ */
84
+ export declare class TokenRefreshPlugin extends CorePlugin {
85
+ /** @internal */
86
+ static get type(): 'core';
87
+ /** @internal */
88
+ get name(): string;
89
+ /** @internal */
90
+ get supportedVersion(): {
91
+ min: string;
92
+ };
93
+ /** Token state extracted from the currently-managed source URL */
94
+ private originalState;
95
+ /** Latest token state (updated after each refresh) */
96
+ private currentState;
97
+ /** Scheduled refresh timer handle */
98
+ private refreshTimer;
99
+ /** Playback time (seconds) to restore after a native-video source reload */
100
+ private savedPosition;
101
+ /** True when using native HTML5 Video playback (no request interception) */
102
+ private isNativePlayback;
103
+ /** Set in destroy(); short-circuits late timer callbacks and getToken() resolutions */
104
+ private destroyed;
105
+ /** @internal */
106
+ bindEvents(): void;
107
+ /** @internal */
108
+ destroy(): void;
109
+ private onContainersCreated;
110
+ private injectHlsLoader;
111
+ private injectDashInterceptor;
112
+ private reloadNativeSource;
113
+ private onActiveContainerChangedForNative;
114
+ private scheduleRefresh;
115
+ private performRefresh;
116
+ private get opts();
117
+ private clearTimer;
118
+ }
119
+ //# sourceMappingURL=TokenRefreshPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenRefreshPlugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/token-refresh/TokenRefreshPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,UAAU,EAAU,MAAM,cAAc,CAAA;AAQrE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAA;IACX,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAA;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;OAEG;IACH,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAA;CACjD;AAgED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,gBAAgB;IAChB,MAAM,KAAK,IAAI,IAAI,MAAM,CAExB;IAED,gBAAgB;IAChB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,gBAAgB;IAChB,IAAI,gBAAgB;;MAEnB;IAED,kEAAkE;IAClE,OAAO,CAAC,aAAa,CAA0B;IAC/C,sDAAsD;IACtD,OAAO,CAAC,YAAY,CAA0B;IAC9C,qCAAqC;IACrC,OAAO,CAAC,YAAY,CAA6C;IACjE,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAAsB;IAC3C,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB,CAAQ;IAChC,uFAAuF;IACvF,OAAO,CAAC,SAAS,CAAQ;IAEzB,gBAAgB;IACP,UAAU,IAAI,IAAI;IAQ3B,gBAAgB;IACP,OAAO,IAAI,IAAI;IAMxB,OAAO,CAAC,mBAAmB;IA8D3B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,qBAAqB;YAmBf,kBAAkB;IA+ChC,OAAO,CAAC,iCAAiC;IAiBzC,OAAO,CAAC,eAAe;YAmBT,cAAc;IAmC5B,OAAO,KAAK,IAAI,GAEf;IAED,OAAO,CAAC,UAAU;CAMnB"}
@@ -0,0 +1,318 @@
1
+ import { $, CorePlugin, Events } from '@clappr/core';
2
+ import HLSJS from 'hls.js';
3
+ import { trace } from '@gcorevideo/utils';
4
+ import { CLAPPR_VERSION } from '../../build.js';
5
+ const T = 'plugins.token_refresh';
6
+ /**
7
+ * Matches the `/{token}/{expires}/` segment in a Gcore protected-content URL.
8
+ * Token is base64url (letters, digits, `-`, `_`); expires is a ≥10-digit Unix timestamp.
9
+ */
10
+ const TOKEN_SEGMENT_RE = /\/([A-Za-z0-9_-]{6,})\/(1\d{9,})\//;
11
+ function extractTokenState(url) {
12
+ const m = url.match(TOKEN_SEGMENT_RE);
13
+ if (!m)
14
+ return null;
15
+ return { token: m[1], expires: parseInt(m[2], 10) };
16
+ }
17
+ /** Replaces the exact `/{oldToken}/{oldExpires}/` segment in a URL. */
18
+ function rewriteUrl(url, from, to) {
19
+ const oldPart = `/${from.token}/${from.expires}/`;
20
+ const newPart = `/${to.token}/${to.expires}/`;
21
+ return url.includes(oldPart) ? url.replace(oldPart, newPart) : url;
22
+ }
23
+ /**
24
+ * Normalises a URL by removing the `/{token}/{expires}/` segment so two URLs
25
+ * for the same stream with different token pairs compare equal.
26
+ * Returns `null` for unparseable input.
27
+ */
28
+ function streamKey(url) {
29
+ try {
30
+ const u = new URL(url);
31
+ return u.origin + u.pathname.replace(TOKEN_SEGMENT_RE, '/');
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ /**
38
+ * Returns a custom hls.js loader class that transparently rewrites the
39
+ * token/expires path segments in every request URL.
40
+ *
41
+ * The returned class extends the default hls.js XhrLoader so all native
42
+ * hls.js behaviour (retry, timeout, range requests …) is preserved.
43
+ */
44
+ function createTokenRewritingLoader(getOriginal, getCurrent) {
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ const DefaultLoader = HLSJS.DefaultConfig.loader;
47
+ return class TokenRewritingLoader extends DefaultLoader {
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ load(context, config, callbacks) {
50
+ const original = getOriginal();
51
+ const current = getCurrent();
52
+ if (original && current && context.url) {
53
+ context.url = rewriteUrl(context.url, original, current);
54
+ }
55
+ super.load(context, config, callbacks);
56
+ }
57
+ };
58
+ }
59
+ /**
60
+ * `PLUGIN` — automatic token refresh for Gcore protected-content streams.
61
+ *
62
+ * Supports all three playback engines:
63
+ *
64
+ * | Engine | Mechanism | Interruption |
65
+ * |--------|-----------|--------------|
66
+ * | **hls.js** | Custom loader rewrites every request URL | None |
67
+ * | **dash.js** | `addRequestInterceptor` rewrites every request URL | None |
68
+ * | **Native `<video>`** (Safari ≤ iOS 14.4) | Source reload + seek restore | Brief |
69
+ *
70
+ * @public
71
+ * @remarks
72
+ * Register the plugin once before creating any player instance:
73
+ * ```ts
74
+ * import { Player, TokenRefreshPlugin } from '@gcorevideo/player'
75
+ * Player.registerPlugin(TokenRefreshPlugin)
76
+ * ```
77
+ *
78
+ * Then pass `tokenRefresh` in `PlayerConfig`:
79
+ * ```ts
80
+ * const player = new Player({
81
+ * sources: [{ source: initialUrl, mimeType: 'application/x-mpegURL' }],
82
+ * tokenRefresh: {
83
+ * getToken: () => fetch('https://…/token').then(r => r.json()),
84
+ * ipBound: false,
85
+ * refreshLeadSeconds: 5,
86
+ * onTokenRefreshed: (data) => console.log('new token expires', data.expires),
87
+ * },
88
+ * })
89
+ * ```
90
+ *
91
+ * @example
92
+ * Safari native — opt-in Service Worker for fully seamless refresh:
93
+ * ```js
94
+ * // Register token-refresh-sw.js (see example/ directory)
95
+ * // and omit tokenRefresh config — the SW handles rewriting.
96
+ * ```
97
+ */
98
+ export class TokenRefreshPlugin extends CorePlugin {
99
+ /** @internal */
100
+ static get type() {
101
+ return 'core';
102
+ }
103
+ /** @internal */
104
+ get name() {
105
+ return 'token_refresh';
106
+ }
107
+ /** @internal */
108
+ get supportedVersion() {
109
+ return { min: CLAPPR_VERSION };
110
+ }
111
+ /** Token state extracted from the currently-managed source URL */
112
+ originalState = null;
113
+ /** Latest token state (updated after each refresh) */
114
+ currentState = null;
115
+ /** Scheduled refresh timer handle */
116
+ refreshTimer = null;
117
+ /** Playback time (seconds) to restore after a native-video source reload */
118
+ savedPosition = null;
119
+ /** True when using native HTML5 Video playback (no request interception) */
120
+ isNativePlayback = false;
121
+ /** Set in destroy(); short-circuits late timer callbacks and getToken() resolutions */
122
+ destroyed = false;
123
+ /** @internal */
124
+ bindEvents() {
125
+ this.listenTo(this.core, Events.CORE_CONTAINERS_CREATED, this.onContainersCreated);
126
+ }
127
+ /** @internal */
128
+ destroy() {
129
+ this.destroyed = true;
130
+ this.clearTimer();
131
+ super.destroy();
132
+ }
133
+ onContainersCreated() {
134
+ const container = this.core.containers[0];
135
+ if (!container)
136
+ return;
137
+ const playbackName = container.playback.name;
138
+ const src = container.playback.options?.src ?? '';
139
+ trace(`${T} onContainersCreated`, { playbackName, src: src.slice(0, 80) });
140
+ this.isNativePlayback = playbackName !== 'hls' && playbackName !== 'dash';
141
+ const state = extractTokenState(src);
142
+ if (!state) {
143
+ // Active source has no token pattern — drop any refresh state we were
144
+ // holding for a previous stream (e.g. SourceController rotated away).
145
+ if (this.originalState) {
146
+ trace(`${T} active source has no token pattern — clearing refresh state`);
147
+ this.clearTimer();
148
+ this.originalState = null;
149
+ this.currentState = null;
150
+ }
151
+ return;
152
+ }
153
+ // Adopt the new token state if this is the first source we see, or if
154
+ // the stream has changed (SourceController rotated, consumer called
155
+ // player.load() with a different stream, or the plugin itself reloaded
156
+ // with a refreshed URL in the native path).
157
+ const isNewStream = !this.originalState ||
158
+ state.token !== this.originalState.token ||
159
+ state.expires !== this.originalState.expires;
160
+ if (isNewStream) {
161
+ trace(`${T} adopting source token state`, {
162
+ token: state.token.slice(0, 8) + '…',
163
+ expires: new Date(state.expires * 1000).toISOString(),
164
+ });
165
+ this.originalState = { ...state };
166
+ this.currentState = { ...state };
167
+ this.scheduleRefresh();
168
+ }
169
+ // Inject the appropriate interception mechanism for this playback engine.
170
+ switch (playbackName) {
171
+ case 'hls':
172
+ this.injectHlsLoader(container);
173
+ break;
174
+ case 'dash':
175
+ this.injectDashInterceptor(container);
176
+ break;
177
+ default:
178
+ // Native HTML5 Video — no request hooks available.
179
+ // Seek restore after a token-triggered reload.
180
+ this.listenToOnce(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChangedForNative);
181
+ break;
182
+ }
183
+ }
184
+ injectHlsLoader(container) {
185
+ const getOriginal = () => this.originalState;
186
+ const getCurrent = () => this.currentState;
187
+ const TokenLoader = createTokenRewritingLoader(getOriginal, getCurrent);
188
+ $.extend(true, container.playback.options, {
189
+ playback: {
190
+ hlsjsConfig: {
191
+ loader: TokenLoader,
192
+ },
193
+ },
194
+ });
195
+ trace(`${T} HLS custom loader injected`);
196
+ }
197
+ injectDashInterceptor(container) {
198
+ $.extend(true, container.playback.options, {
199
+ dash: {
200
+ requestInterceptor: (request) => {
201
+ if (this.originalState && this.currentState) {
202
+ request.url = rewriteUrl(request.url, this.originalState, this.currentState);
203
+ }
204
+ return Promise.resolve(request);
205
+ },
206
+ },
207
+ });
208
+ trace(`${T} DASH request interceptor injected`);
209
+ }
210
+ async reloadNativeSource(data) {
211
+ const container = this.core.activeContainer;
212
+ const playback = container?.playback;
213
+ if (!playback)
214
+ return;
215
+ // SourceController (or any other actor) may have switched the active
216
+ // playback to hls/dash while getToken() was in flight. Those engines
217
+ // have their own request-interception path; a native reload would
218
+ // undo the switch.
219
+ if (playback.name === 'hls' || playback.name === 'dash') {
220
+ trace(`${T} skipping native reload — active playback is ${playback.name}`);
221
+ return;
222
+ }
223
+ if (!this.isNativePlayback) {
224
+ trace(`${T} skipping native reload — no longer in native playback mode`);
225
+ return;
226
+ }
227
+ // Verify the URL we're about to load belongs to the same stream that's
228
+ // currently active. If SourceController rotated to a different stream
229
+ // during the getToken() await, the refreshed URL would silently swap
230
+ // us back, undoing SourceController's decision.
231
+ const currentSrc = playback.options?.src ?? '';
232
+ const newUrl = this.opts.ipBound ? data.url_ip : data.url;
233
+ const activeKey = streamKey(currentSrc);
234
+ const nextKey = streamKey(newUrl);
235
+ if (!activeKey || !nextKey || activeKey !== nextKey) {
236
+ trace(`${T} skipping native reload — active source differs from refresh URL`, {
237
+ activeKey,
238
+ nextKey,
239
+ });
240
+ return;
241
+ }
242
+ // Capture current playback position before tearing down the container.
243
+ const mediaEl = playback.el;
244
+ const currentTime = mediaEl?.currentTime ?? 0;
245
+ this.savedPosition = currentTime > 0 ? currentTime : null;
246
+ trace(`${T} native reload`, { newUrl: newUrl.slice(0, 80), savedPosition: this.savedPosition });
247
+ // core.load() destroys and recreates all containers.
248
+ this.core.load([{ source: newUrl, mimeType: this.core.options.mimeType ?? 'application/x-mpegURL' }]);
249
+ }
250
+ onActiveContainerChangedForNative() {
251
+ if (this.savedPosition === null)
252
+ return;
253
+ const pos = this.savedPosition;
254
+ this.savedPosition = null;
255
+ // Wait for the new container to be fully ready before seeking.
256
+ const container = this.core.activeContainer;
257
+ if (!container)
258
+ return;
259
+ this.listenToOnce(container, Events.CONTAINER_READY, () => {
260
+ trace(`${T} native: restoring position`, { pos });
261
+ container.seek(pos);
262
+ container.play();
263
+ });
264
+ }
265
+ scheduleRefresh() {
266
+ this.clearTimer();
267
+ if (this.destroyed || !this.currentState)
268
+ return;
269
+ const leadMs = (this.opts.refreshLeadSeconds ?? 5) * 1000;
270
+ const msUntilRefresh = this.currentState.expires * 1000 - Date.now() - leadMs;
271
+ trace(`${T} next refresh in`, {
272
+ seconds: Math.round(msUntilRefresh / 1000),
273
+ expires: new Date(this.currentState.expires * 1000).toISOString(),
274
+ });
275
+ this.refreshTimer = setTimeout(() => this.performRefresh(), Math.max(msUntilRefresh, 1000));
276
+ }
277
+ async performRefresh() {
278
+ trace(`${T} fetching new token`);
279
+ try {
280
+ const data = await this.opts.getToken();
281
+ // Plugin may have been destroyed while getToken() was in flight; drop the result.
282
+ if (this.destroyed)
283
+ return;
284
+ const newToken = this.opts.ipBound ? data.token_ip : data.token;
285
+ const newState = { token: newToken, expires: data.expires };
286
+ if (this.isNativePlayback) {
287
+ // Must reload source because the <video> element has no request hook.
288
+ await this.reloadNativeSource(data);
289
+ }
290
+ // originalState is never changed after init — it holds the token that was
291
+ // baked into every URL in the initial manifest. hls.js/dash.js always
292
+ // produces request URLs based on that manifest, so every segment URL
293
+ // still contains the original token regardless of how many refreshes
294
+ // have already happened. The loader replaces original→current on each
295
+ // request, so updating only currentState is sufficient.
296
+ this.currentState = newState;
297
+ this.opts.onTokenRefreshed?.(data);
298
+ trace(`${T} token refreshed`, {
299
+ token: newToken.slice(0, 8) + '…',
300
+ expires: new Date(data.expires * 1000).toISOString(),
301
+ });
302
+ }
303
+ catch (err) {
304
+ trace(`${T} token refresh failed`, { err });
305
+ }
306
+ // Always reschedule, even after an error (will retry near next expiry).
307
+ this.scheduleRefresh();
308
+ }
309
+ get opts() {
310
+ return this.options.tokenRefresh;
311
+ }
312
+ clearTimer() {
313
+ if (this.refreshTimer !== null) {
314
+ clearTimeout(this.refreshTimer);
315
+ this.refreshTimer = null;
316
+ }
317
+ }
318
+ }
@@ -0,0 +1,2 @@
1
+ export { TokenRefreshPlugin, type TokenRefreshOptions, type TokenResponse, } from './TokenRefreshPlugin.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/token-refresh/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,yBAAyB,CAAA"}
@@ -0,0 +1 @@
1
+ export { TokenRefreshPlugin, } from './TokenRefreshPlugin.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.29.0",
3
+ "version": "2.30.0",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
package/src/Player.ts CHANGED
@@ -278,6 +278,18 @@ export class Player {
278
278
  this.player?.load(ms, ms[0].mimeType ?? '')
279
279
  }
280
280
 
281
+ /**
282
+ * Returns a registered core plugin instance by name, or `null` if not found.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * const tokenRefresh = player.getPlugin('token_refresh') as TokenRefreshPlugin | null
287
+ * ```
288
+ */
289
+ getPlugin(name: string) {
290
+ return this.player?.core.getPlugin(name) ?? null
291
+ }
292
+
281
293
  /**
282
294
  * Mutes the sound of the video.
283
295
  */
@@ -36,4 +36,5 @@ export * from './plugins/telemetry/Telemetry.js'
36
36
  export * from './plugins/thumbnails/Thumbnails.js'
37
37
  // _ vast-ads
38
38
  // _ video360
39
+ export * from './plugins/token-refresh/index.js'
39
40
  export * from './plugins/volume-fade/VolumeFade.js'
@@ -243,6 +243,7 @@ export default class DashPlayback extends BasePlayback {
243
243
  this._dash.initialize()
244
244
 
245
245
  if (this.options.dash) {
246
+ const { requestInterceptor, ...dashSettings } = this.options.dash as Record<string, unknown>
246
247
  const settings = $.extend(
247
248
  true,
248
249
  {
@@ -257,9 +258,13 @@ export default class DashPlayback extends BasePlayback {
257
258
  },
258
259
  },
259
260
  },
260
- this.options.dash,
261
+ dashSettings,
261
262
  )
262
263
  this._dash.updateSettings(settings)
264
+
265
+ if (typeof requestInterceptor === 'function') {
266
+ this._dash.addRequestInterceptor(requestInterceptor as Parameters<typeof this._dash.addRequestInterceptor>[0])
267
+ }
263
268
  }
264
269
 
265
270
  this._dash.attachView(this.el as HTMLMediaElement)