@hopecloud/jetstream-player 1.1.4 → 1.3.0-rc.1
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 +76 -1
- package/dist/embed-frame.d.ts +7 -0
- package/dist/{@types/iframe-pip-plugin.d.ts → iframe-pip-plugin.d.ts} +4 -0
- package/dist/{@types/index.d.ts → index.d.ts} +0 -1
- package/dist/index.js +300 -2
- package/dist/player-bridge.d.ts +13 -0
- package/dist/player-transport.d.ts +5 -0
- package/dist/{@types/player.d.ts → player.d.ts} +7 -7
- package/dist/{@types/player.type.d.ts → player.type.d.ts} +12 -14
- package/dist/protocol.d.ts +3 -0
- package/package.json +36 -41
- package/dist/@types/iframe-builder-src.d.ts +0 -10
- package/dist/@types/iframe-builder.d.ts +0 -8
- package/dist/@types/utils.d.ts +0 -3
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1 +1,76 @@
|
|
|
1
|
-
|
|
1
|
+
# @hopecloud/jetstream-player
|
|
2
|
+
|
|
3
|
+
Control an embedded [Jetstream](https://jetstream-player.docs.hope.cloud/) player
|
|
4
|
+
from your page — play, pause, seek, mute, read playback state, and subscribe to
|
|
5
|
+
events — across the iframe boundary, with no server glue.
|
|
6
|
+
|
|
7
|
+
**[Full documentation →](https://jetstream-player.docs.hope.cloud/)**
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i @hopecloud/jetstream-player
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { JetstreamPlayer } from '@hopecloud/jetstream-player';
|
|
19
|
+
|
|
20
|
+
const player = new JetstreamPlayer('#player', {
|
|
21
|
+
mediaId: 'jsv:xxxxxxxxxx',
|
|
22
|
+
events: {
|
|
23
|
+
ready: () => console.log('ready'),
|
|
24
|
+
timeupdate: (time) => console.log('time', time),
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
player.play();
|
|
29
|
+
const muted = await player.isMuted();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`mediaId` is the only required option; the library creates and mounts the iframe
|
|
33
|
+
at the given selector.
|
|
34
|
+
|
|
35
|
+
## What you can do
|
|
36
|
+
|
|
37
|
+
- **Commands** — `play()`, `pause()`, `mute()`, `unmute()`, `seekTo(seconds)`,
|
|
38
|
+
`playNext()`, `playPrev()`.
|
|
39
|
+
- **Getters** (Promise-based) — `isMuted()`, `isPaused()`,
|
|
40
|
+
`getVideoCurrentTime()`, `getDuration()`. They reject after 5s if the player
|
|
41
|
+
doesn't reply.
|
|
42
|
+
- **Events** — subscribe via `options.events`: `ready`, `timeupdate`, `play`,
|
|
43
|
+
`pause`, `loadedmetadata`, `ended`, `volumechange`, `error`.
|
|
44
|
+
- **Teardown** — `dispose()` removes listeners and tears down Picture-in-Picture.
|
|
45
|
+
|
|
46
|
+
See the [options](https://jetstream-player.docs.hope.cloud/api/player-options)
|
|
47
|
+
and [methods](https://jetstream-player.docs.hope.cloud/api/player-methods)
|
|
48
|
+
reference for the full list.
|
|
49
|
+
|
|
50
|
+
## Targeting a non-prod embed
|
|
51
|
+
|
|
52
|
+
By default the player loads the production embed. Point it at another host
|
|
53
|
+
(staging/dev) with `origin`:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
new JetstreamPlayer('#player', {
|
|
57
|
+
mediaId: 'jsv:xxxxxxxxxx',
|
|
58
|
+
origin: 'https://embed-stage.jetstream.studio',
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Communication is cross-origin `postMessage`, and the SDK learns the iframe's
|
|
63
|
+
real origin at runtime — so it keeps working even when the embed redirects.
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm install
|
|
69
|
+
pnpm build # rolldown + .d.ts
|
|
70
|
+
pnpm test # vitest
|
|
71
|
+
pnpm lint
|
|
72
|
+
pnpm typecheck
|
|
73
|
+
pnpm docs:dev # vitepress docs
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Architecture decisions are recorded in [`adr/`](./adr).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EmbedFrameOptions } from './player.type';
|
|
2
|
+
export declare const buildEmbedSrc: (options: EmbedFrameOptions) => string;
|
|
3
|
+
export interface MountedFrame {
|
|
4
|
+
iframe: HTMLIFrameElement;
|
|
5
|
+
src: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const mountEmbedFrame: (selector: string, options: EmbedFrameOptions) => MountedFrame;
|
|
@@ -2,7 +2,11 @@ export declare class IFramePiPPlugin {
|
|
|
2
2
|
containerSelector: string;
|
|
3
3
|
iframe: HTMLIFrameElement;
|
|
4
4
|
private observer;
|
|
5
|
+
private originalParentHeight;
|
|
6
|
+
private originalIframeStyle;
|
|
7
|
+
private parent;
|
|
5
8
|
constructor(containerSelector: string, iframe: HTMLIFrameElement);
|
|
6
9
|
init(): void;
|
|
10
|
+
destroy(): void;
|
|
7
11
|
destory(): void;
|
|
8
12
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,300 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/embed-frame.ts
|
|
2
|
+
const DEFAULT_ORIGIN = "https://jstre.am";
|
|
3
|
+
const regionOrigin = (origin, region) => {
|
|
4
|
+
const url = new URL(origin);
|
|
5
|
+
if (region === "eu") {
|
|
6
|
+
const parts = url.hostname.split(".");
|
|
7
|
+
parts.splice(parts.length > 2 ? 1 : 0, 0, "eu");
|
|
8
|
+
url.hostname = parts.join(".");
|
|
9
|
+
}
|
|
10
|
+
return url.origin;
|
|
11
|
+
};
|
|
12
|
+
const buildEmbedSrc = (options) => {
|
|
13
|
+
if (options.mediaId === "") throw new Error("mediaId not provided!");
|
|
14
|
+
const origin = regionOrigin(options.origin ?? DEFAULT_ORIGIN, options.region);
|
|
15
|
+
const path = options.playerId ? `/embed/${options.mediaId}/${options.playerId}` : `/embed/${options.mediaId}`;
|
|
16
|
+
const url = new URL(`${origin}${path}`);
|
|
17
|
+
if (options.region === "eu") url.searchParams.append("region", "eu");
|
|
18
|
+
if (options.audioLang) url.searchParams.append("audioLang", options.audioLang.toLowerCase());
|
|
19
|
+
if (options.subtitleLang) url.searchParams.append("subtitleLang", options.subtitleLang.toLowerCase());
|
|
20
|
+
return url.href;
|
|
21
|
+
};
|
|
22
|
+
const mountEmbedFrame = (selector, options) => {
|
|
23
|
+
const src = buildEmbedSrc(options);
|
|
24
|
+
const iframe = document.createElement("iframe");
|
|
25
|
+
iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
|
|
26
|
+
iframe.allowFullscreen = true;
|
|
27
|
+
iframe.frameBorder = "0";
|
|
28
|
+
iframe.title = "Jetstream player";
|
|
29
|
+
iframe.src = src;
|
|
30
|
+
iframe.width = options.width || "480px";
|
|
31
|
+
iframe.height = options.height || "270px";
|
|
32
|
+
iframe.classList.add("jet-stream-iframe-player");
|
|
33
|
+
const parent = document.querySelector(selector);
|
|
34
|
+
if (parent) parent.appendChild(iframe);
|
|
35
|
+
else console.error(`Element matching selector "${selector}" not found.`);
|
|
36
|
+
return {
|
|
37
|
+
iframe,
|
|
38
|
+
src
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/iframe-pip-plugin.ts
|
|
43
|
+
const PiP_STYLES = {
|
|
44
|
+
position: "fixed",
|
|
45
|
+
bottom: "20px",
|
|
46
|
+
right: "20px",
|
|
47
|
+
width: "330px",
|
|
48
|
+
height: "180px"
|
|
49
|
+
};
|
|
50
|
+
var IFramePiPPlugin = class {
|
|
51
|
+
containerSelector;
|
|
52
|
+
iframe;
|
|
53
|
+
observer = null;
|
|
54
|
+
originalParentHeight = null;
|
|
55
|
+
originalIframeStyle = null;
|
|
56
|
+
parent = null;
|
|
57
|
+
constructor(containerSelector, iframe) {
|
|
58
|
+
this.containerSelector = containerSelector;
|
|
59
|
+
this.iframe = iframe;
|
|
60
|
+
this.containerSelector = containerSelector;
|
|
61
|
+
this.iframe = iframe;
|
|
62
|
+
this.init();
|
|
63
|
+
}
|
|
64
|
+
init() {
|
|
65
|
+
if (!this.iframe || !this.containerSelector) return;
|
|
66
|
+
const parent = document.querySelector(this.containerSelector);
|
|
67
|
+
if (!parent) throw new Error("Selector of iframe not found");
|
|
68
|
+
this.parent = parent;
|
|
69
|
+
this.originalParentHeight = parent.style.height || null;
|
|
70
|
+
this.originalIframeStyle = this.iframe.getAttribute("style");
|
|
71
|
+
this.observer = new IntersectionObserver(([{ isIntersecting }]) => {
|
|
72
|
+
if (!isIntersecting) {
|
|
73
|
+
const height = parent.getBoundingClientRect().height;
|
|
74
|
+
parent.style.height = `${height}px`;
|
|
75
|
+
this.iframe.style.position = PiP_STYLES.position;
|
|
76
|
+
this.iframe.style.bottom = PiP_STYLES.bottom;
|
|
77
|
+
this.iframe.style.right = PiP_STYLES.right;
|
|
78
|
+
this.iframe.style.width = PiP_STYLES.width;
|
|
79
|
+
this.iframe.style.height = PiP_STYLES.height;
|
|
80
|
+
} else {
|
|
81
|
+
parent.style.height = this.originalParentHeight ?? "";
|
|
82
|
+
if (this.originalIframeStyle) this.iframe.setAttribute("style", this.originalIframeStyle);
|
|
83
|
+
else this.iframe.removeAttribute("style");
|
|
84
|
+
}
|
|
85
|
+
}, {
|
|
86
|
+
root: null,
|
|
87
|
+
rootMargin: "0px",
|
|
88
|
+
threshold: .1
|
|
89
|
+
});
|
|
90
|
+
this.observer.observe(parent);
|
|
91
|
+
}
|
|
92
|
+
destroy() {
|
|
93
|
+
if (this.observer) {
|
|
94
|
+
this.observer.disconnect();
|
|
95
|
+
this.observer = null;
|
|
96
|
+
}
|
|
97
|
+
if (this.parent) this.parent.style.height = this.originalParentHeight ?? "";
|
|
98
|
+
if (this.iframe) if (this.originalIframeStyle) this.iframe.setAttribute("style", this.originalIframeStyle);
|
|
99
|
+
else this.iframe.removeAttribute("style");
|
|
100
|
+
}
|
|
101
|
+
destory() {
|
|
102
|
+
this.destroy();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/protocol.ts
|
|
107
|
+
const encode = (command) => JSON.stringify(command);
|
|
108
|
+
const decode = (raw) => {
|
|
109
|
+
if (typeof raw !== "string") return null;
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(raw);
|
|
112
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.type === "string") return parsed;
|
|
113
|
+
return null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/player-bridge.ts
|
|
120
|
+
const REQUEST_TIMEOUT_MS = 5e3;
|
|
121
|
+
var PlayerBridge = class {
|
|
122
|
+
transport;
|
|
123
|
+
targetOrigin;
|
|
124
|
+
listeners = /* @__PURE__ */ new Set();
|
|
125
|
+
detach;
|
|
126
|
+
constructor(transport, initialTargetOrigin) {
|
|
127
|
+
this.transport = transport;
|
|
128
|
+
this.targetOrigin = initialTargetOrigin;
|
|
129
|
+
this.detach = this.transport.subscribe((message, origin) => {
|
|
130
|
+
const event = decode(message);
|
|
131
|
+
if (!event) return;
|
|
132
|
+
this.targetOrigin = origin;
|
|
133
|
+
this.listeners.forEach((listener) => listener(event));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
send(command) {
|
|
137
|
+
this.transport.send(encode(command), this.targetOrigin);
|
|
138
|
+
}
|
|
139
|
+
subscribe(listener) {
|
|
140
|
+
this.listeners.add(listener);
|
|
141
|
+
return () => {
|
|
142
|
+
this.listeners.delete(listener);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
request(command, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
let off = () => void 0;
|
|
148
|
+
const timer = setTimeout(() => {
|
|
149
|
+
off();
|
|
150
|
+
reject(/* @__PURE__ */ new Error(`Timed out waiting for "${command.name}" response`));
|
|
151
|
+
}, timeoutMs);
|
|
152
|
+
off = this.subscribe((event) => {
|
|
153
|
+
if (event.type === command.name) {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
off();
|
|
156
|
+
resolve(event.value);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
this.send(command);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
dispose() {
|
|
163
|
+
this.detach();
|
|
164
|
+
this.listeners.clear();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/player-transport.ts
|
|
169
|
+
const windowTransport = (iframe) => ({
|
|
170
|
+
send: (message, targetOrigin) => {
|
|
171
|
+
iframe.contentWindow?.postMessage(message, targetOrigin);
|
|
172
|
+
},
|
|
173
|
+
subscribe: (handler) => {
|
|
174
|
+
const listener = (event) => {
|
|
175
|
+
if (event.source !== iframe.contentWindow) return;
|
|
176
|
+
handler(event.data, event.origin);
|
|
177
|
+
};
|
|
178
|
+
window.addEventListener("message", listener);
|
|
179
|
+
return () => {
|
|
180
|
+
window.removeEventListener("message", listener);
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/player.ts
|
|
186
|
+
var JetstreamPlayer = class {
|
|
187
|
+
options;
|
|
188
|
+
listeners = /* @__PURE__ */ new Map();
|
|
189
|
+
iFramePiP = null;
|
|
190
|
+
bridge = null;
|
|
191
|
+
constructor(el, options) {
|
|
192
|
+
this.options = options;
|
|
193
|
+
const { iframe, src } = mountEmbedFrame(el, options);
|
|
194
|
+
let initialTargetOrigin;
|
|
195
|
+
try {
|
|
196
|
+
initialTargetOrigin = new URL(src).origin;
|
|
197
|
+
} catch {
|
|
198
|
+
initialTargetOrigin = "*";
|
|
199
|
+
}
|
|
200
|
+
this.bridge = new PlayerBridge(windowTransport(iframe), initialTargetOrigin);
|
|
201
|
+
iframe.addEventListener("load", () => {
|
|
202
|
+
this.registerEvents();
|
|
203
|
+
this.bridge?.subscribe((event) => {
|
|
204
|
+
this.emit(event.type, event.value);
|
|
205
|
+
});
|
|
206
|
+
}, { once: true });
|
|
207
|
+
if (typeof this.options.sticky !== "undefined" && this.options.sticky) this.iFramePiP = new IFramePiPPlugin(el, iframe);
|
|
208
|
+
}
|
|
209
|
+
registerEvents() {
|
|
210
|
+
if (!this.options.events) return;
|
|
211
|
+
Object.entries(this.options.events).forEach(([eventName, handler]) => {
|
|
212
|
+
if (handler) this.on(eventName, handler);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
emit(type, value) {
|
|
216
|
+
const handlers = this.listeners.get(type);
|
|
217
|
+
if (!handlers) return;
|
|
218
|
+
[...handlers].forEach((handler) => handler(value));
|
|
219
|
+
}
|
|
220
|
+
on(event, handler) {
|
|
221
|
+
let handlers = this.listeners.get(event);
|
|
222
|
+
if (!handlers) {
|
|
223
|
+
handlers = /* @__PURE__ */ new Set();
|
|
224
|
+
this.listeners.set(event, handlers);
|
|
225
|
+
}
|
|
226
|
+
handlers.add(handler);
|
|
227
|
+
return () => {
|
|
228
|
+
this.off(event, handler);
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
off(event, handler) {
|
|
232
|
+
if (!handler) {
|
|
233
|
+
this.listeners.delete(event);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const handlers = this.listeners.get(event);
|
|
237
|
+
if (!handlers) return;
|
|
238
|
+
handlers.delete(handler);
|
|
239
|
+
if (handlers.size === 0) this.listeners.delete(event);
|
|
240
|
+
}
|
|
241
|
+
request(command) {
|
|
242
|
+
if (!this.bridge) return Promise.reject(/* @__PURE__ */ new Error("iframe is not provided or parent window not found!"));
|
|
243
|
+
return this.bridge.request(command);
|
|
244
|
+
}
|
|
245
|
+
play() {
|
|
246
|
+
this.bridge?.send({ name: "play" });
|
|
247
|
+
}
|
|
248
|
+
pause() {
|
|
249
|
+
this.bridge?.send({ name: "pause" });
|
|
250
|
+
}
|
|
251
|
+
mute() {
|
|
252
|
+
this.bridge?.send({ name: "mute" });
|
|
253
|
+
}
|
|
254
|
+
unmute() {
|
|
255
|
+
this.bridge?.send({ name: "unmute" });
|
|
256
|
+
}
|
|
257
|
+
seekTo(seconds) {
|
|
258
|
+
this.bridge?.send({
|
|
259
|
+
name: "seekTo",
|
|
260
|
+
params: seconds
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
playNext() {
|
|
264
|
+
this.bridge?.send({ name: "play-next" });
|
|
265
|
+
}
|
|
266
|
+
playPrev() {
|
|
267
|
+
this.bridge?.send({ name: "play-prev" });
|
|
268
|
+
}
|
|
269
|
+
isMuted() {
|
|
270
|
+
return this.request({ name: "is-muted" });
|
|
271
|
+
}
|
|
272
|
+
isPaused() {
|
|
273
|
+
return this.request({ name: "is-paused" });
|
|
274
|
+
}
|
|
275
|
+
getVideoCurrentTime() {
|
|
276
|
+
return this.request({ name: "get-video-current-time" });
|
|
277
|
+
}
|
|
278
|
+
getDuration() {
|
|
279
|
+
return this.request({ name: "get-duration" });
|
|
280
|
+
}
|
|
281
|
+
dispose() {
|
|
282
|
+
this.bridge?.dispose();
|
|
283
|
+
this.listeners.clear();
|
|
284
|
+
this.iFramePiP?.destroy();
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
//#endregion
|
|
288
|
+
//#region src/player.type.ts
|
|
289
|
+
const playerListenEvents = [
|
|
290
|
+
"ready",
|
|
291
|
+
"timeupdate",
|
|
292
|
+
"play",
|
|
293
|
+
"pause",
|
|
294
|
+
"loadedmetadata",
|
|
295
|
+
"ended",
|
|
296
|
+
"volumechange",
|
|
297
|
+
"error"
|
|
298
|
+
];
|
|
299
|
+
//#endregion
|
|
300
|
+
export { JetstreamPlayer, playerListenEvents };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PlayerCommand, PlayerEvent } from './player.type';
|
|
2
|
+
import type { PlayerTransport } from './player-transport';
|
|
3
|
+
export declare class PlayerBridge {
|
|
4
|
+
private readonly transport;
|
|
5
|
+
private targetOrigin;
|
|
6
|
+
private readonly listeners;
|
|
7
|
+
private readonly detach;
|
|
8
|
+
constructor(transport: PlayerTransport, initialTargetOrigin: string);
|
|
9
|
+
send(command: PlayerCommand): void;
|
|
10
|
+
subscribe(listener: (event: PlayerEvent) => void): () => void;
|
|
11
|
+
request<T>(command: PlayerCommand, timeoutMs?: number): Promise<T>;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { JetstreamPlayerOptions } from './player.type';
|
|
1
|
+
import type { PlayerEvents, JetstreamPlayerOptions, PlayerEventHandler } from './player.type';
|
|
2
2
|
export declare class JetstreamPlayer {
|
|
3
|
-
private iframe;
|
|
4
3
|
private options;
|
|
5
|
-
private
|
|
4
|
+
private readonly listeners;
|
|
6
5
|
private iFramePiP;
|
|
6
|
+
private bridge;
|
|
7
7
|
constructor(el: string, options: JetstreamPlayerOptions);
|
|
8
8
|
private registerEvents;
|
|
9
|
-
private
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
private
|
|
9
|
+
private emit;
|
|
10
|
+
on(event: PlayerEvents, handler: PlayerEventHandler): () => void;
|
|
11
|
+
off(event: PlayerEvents, handler?: PlayerEventHandler): void;
|
|
12
|
+
private request;
|
|
13
13
|
play(): void;
|
|
14
14
|
pause(): void;
|
|
15
15
|
mute(): void;
|
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
export declare const playerListenEvents: readonly ["ready", "timeupdate", "play", "pause", "loadedmetadata", "ended", "volumechange", "error"];
|
|
2
2
|
export type PlayerEvents = 'ready' | 'timeupdate' | 'play' | 'pause' | 'loadedmetadata' | 'ended' | 'volumechange' | 'error';
|
|
3
3
|
export type PlayerGetterEvents = 'is-muted' | 'get-video-current-time' | 'get-duration' | 'is-paused';
|
|
4
|
-
export
|
|
4
|
+
export interface PlayerEvent {
|
|
5
5
|
type: PlayerEvents | PlayerGetterEvents;
|
|
6
6
|
value?: unknown;
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
|
+
export type PlayerEventHandler = (value?: unknown) => void;
|
|
8
9
|
export type PlayerCommandEvent = 'play' | 'pause' | 'mute' | 'unmute' | 'seekTo' | 'play-next' | 'play-prev';
|
|
9
|
-
export
|
|
10
|
+
export interface PlayerCommand {
|
|
10
11
|
name: PlayerCommandEvent | PlayerGetterEvents;
|
|
11
12
|
params?: unknown;
|
|
12
|
-
}
|
|
13
|
-
export
|
|
14
|
-
selector: string;
|
|
15
|
-
src: string;
|
|
16
|
-
width?: string;
|
|
17
|
-
height?: string;
|
|
18
|
-
};
|
|
19
|
-
export type IFrameBuilderSrcOpt = {
|
|
13
|
+
}
|
|
14
|
+
export interface EmbedFrameOptions {
|
|
20
15
|
mediaId: string;
|
|
21
16
|
playerId?: string;
|
|
22
17
|
region?: 'eu';
|
|
23
18
|
audioLang?: string;
|
|
24
19
|
subtitleLang?: string;
|
|
25
|
-
|
|
20
|
+
origin?: string;
|
|
21
|
+
width?: string;
|
|
22
|
+
height?: string;
|
|
23
|
+
}
|
|
26
24
|
export type JetstreamPlayerOptions = {
|
|
27
25
|
sticky?: boolean;
|
|
28
|
-
events?: Partial<Record<PlayerEvents,
|
|
29
|
-
} &
|
|
26
|
+
events?: Partial<Record<PlayerEvents, PlayerEventHandler>>;
|
|
27
|
+
} & EmbedFrameOptions;
|
package/package.json
CHANGED
|
@@ -1,59 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hopecloud/jetstream-player",
|
|
3
|
-
"version": "1.1
|
|
4
|
-
"description": "Jetstream Embed Player
|
|
3
|
+
"version": "1.3.0-rc.1",
|
|
4
|
+
"description": "Jetstream Embed Player SDK",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"email": "code@deepvision.team",
|
|
8
|
-
"name": "DeepVision Code"
|
|
9
|
-
},
|
|
6
|
+
"sideEffects": false,
|
|
10
7
|
"repository": {
|
|
11
8
|
"type": "git",
|
|
12
|
-
"url": "https://github.com/
|
|
9
|
+
"url": "git+https://github.com/hopechannel/hopecloud.git",
|
|
10
|
+
"directory": "libs/jetstream-player"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://jetstream-player.docs.hope.cloud/",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": "24"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
13
18
|
},
|
|
14
19
|
"main": "dist/index.js",
|
|
15
|
-
"
|
|
16
|
-
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
17
23
|
"exports": {
|
|
18
24
|
".": {
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"default": "./dist/index.js"
|
|
22
|
-
}
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
29
|
+
"author": {
|
|
30
|
+
"email": "code@deepvision.software",
|
|
31
|
+
"name": "DV Software"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@deepvision/eslint-plugin": "2.0.3",
|
|
35
|
+
"@types/node": "24.12.0",
|
|
36
|
+
"eslint": "10.1.0",
|
|
37
|
+
"happy-dom": "^20.4.0",
|
|
38
|
+
"rolldown": "^1.1.4",
|
|
39
|
+
"typescript": "6.0.3",
|
|
40
|
+
"vitepress": "^1.6.4",
|
|
41
|
+
"vitest": "^4.0.18"
|
|
42
|
+
},
|
|
28
43
|
"scripts": {
|
|
29
|
-
"build": "
|
|
30
|
-
"pack": "npm run build && npm pack",
|
|
31
|
-
"publish:patch": "npm version patch -m \"release: %s\"",
|
|
32
|
-
"publish:minor": "npm version minor -m \"release: %s\"",
|
|
33
|
-
"postpublish:patch": "git push origin && git push origin --tags",
|
|
34
|
-
"postpublish:minor": "git push origin && git push origin --tags",
|
|
44
|
+
"build": "rolldown --config && tsc --emitDeclarationOnly",
|
|
35
45
|
"docs:dev": "vitepress dev docs",
|
|
36
46
|
"docs:build": "vitepress build docs",
|
|
37
47
|
"docs:preview": "vitepress preview docs",
|
|
38
48
|
"lint": "eslint",
|
|
39
49
|
"lint:fix": "eslint --fix",
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"@eslint/eslintrc": "^3.2.0",
|
|
44
|
-
"@eslint/js": "^9.17.0",
|
|
45
|
-
"@rollup/plugin-terser": "^0.4.4",
|
|
46
|
-
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
47
|
-
"@typescript-eslint/parser": "^8.19.1",
|
|
48
|
-
"eslint": "^9.17.0",
|
|
49
|
-
"eslint-config-prettier": "^9.1.0",
|
|
50
|
-
"globals": "^15.14.0",
|
|
51
|
-
"prettier": "3.4.2",
|
|
52
|
-
"rimraf": "^6.0.1",
|
|
53
|
-
"rollup": "^4.30.1",
|
|
54
|
-
"rollup-plugin-delete": "^2.1.0",
|
|
55
|
-
"rollup-plugin-typescript2": "^0.36.0",
|
|
56
|
-
"typescript": "^5.7.3",
|
|
57
|
-
"vitepress": "1.5.0"
|
|
50
|
+
"type-check": "tsc --noEmit",
|
|
51
|
+
"test": "vitest",
|
|
52
|
+
"test:run": "vitest run"
|
|
58
53
|
}
|
|
59
|
-
}
|
|
54
|
+
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type IFrameBuilderSrcOpt } from './player.type';
|
|
2
|
-
export declare class IFrameBuilderSrc {
|
|
3
|
-
private options;
|
|
4
|
-
origin: string;
|
|
5
|
-
baseURL: URL;
|
|
6
|
-
constructor(options: IFrameBuilderSrcOpt);
|
|
7
|
-
getRegionBasedOrigin(): string;
|
|
8
|
-
appendPath(path: string, builtUrl: string): string;
|
|
9
|
-
buildSrc(): string;
|
|
10
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type IFrameBuilderOpt } from './player.type';
|
|
2
|
-
export declare class IFrameBuilder {
|
|
3
|
-
options: IFrameBuilderOpt;
|
|
4
|
-
iframe: HTMLIFrameElement | null;
|
|
5
|
-
constructor(options: IFrameBuilderOpt);
|
|
6
|
-
private generateIframe;
|
|
7
|
-
insertIframe(iframe: HTMLIFrameElement): void;
|
|
8
|
-
}
|
package/dist/@types/utils.d.ts
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|