@hopecloud/jetstream-player 1.3.0-rc.3 → 1.4.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 +52 -32
- package/dist/index.d.ts +78 -2
- package/dist/index.js +101 -98
- package/package.json +18 -33
- package/dist/embed-frame.d.ts +0 -7
- package/dist/iframe-pip-plugin.d.ts +0 -12
- package/dist/player-bridge.d.ts +0 -13
- package/dist/player-transport.d.ts +0 -5
- package/dist/player.d.ts +0 -25
- package/dist/player.type.d.ts +0 -27
- package/dist/protocol.d.ts +0 -3
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Jetstream Player SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Put the [Jetstream](https://docs.hope.cloud/libs/jetstream-player/) Player on
|
|
4
|
+
your website and drive it from your own code. The player itself lives in an
|
|
5
|
+
iframe served by Jetstream; this SDK is your remote control for it — play,
|
|
6
|
+
pause, seek, mute, ask what's happening, and react to events, without ever
|
|
7
|
+
thinking about the iframe boundary.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
It works the same way for videos, audio, and whole playlists.
|
|
10
|
+
|
|
11
|
+
**[Full documentation →](https://docs.hope.cloud/libs/jetstream-player/)**
|
|
8
12
|
|
|
9
13
|
## Install
|
|
10
14
|
|
|
@@ -14,6 +18,14 @@ npm i @hopecloud/jetstream-player
|
|
|
14
18
|
|
|
15
19
|
## Quick start
|
|
16
20
|
|
|
21
|
+
Give the player a place on your page — any element with an id:
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<div id="player"></div>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then create the player and tell it what to play:
|
|
28
|
+
|
|
17
29
|
```ts
|
|
18
30
|
import { JetstreamPlayer } from '@hopecloud/jetstream-player';
|
|
19
31
|
|
|
@@ -21,7 +33,7 @@ const player = new JetstreamPlayer('#player', {
|
|
|
21
33
|
mediaId: 'jsv:xxxxxxxxxx',
|
|
22
34
|
events: {
|
|
23
35
|
ready: () => console.log('ready'),
|
|
24
|
-
timeupdate: (
|
|
36
|
+
timeupdate: (seconds) => console.log('now at', seconds),
|
|
25
37
|
},
|
|
26
38
|
});
|
|
27
39
|
|
|
@@ -29,48 +41,56 @@ player.play();
|
|
|
29
41
|
const muted = await player.isMuted();
|
|
30
42
|
```
|
|
31
43
|
|
|
32
|
-
`mediaId`
|
|
33
|
-
|
|
44
|
+
`mediaId` — a video, an audio, or a playlist id from Jetstream — is the only
|
|
45
|
+
required option. The SDK creates the iframe and mounts it inside your element;
|
|
46
|
+
no other markup needed.
|
|
34
47
|
|
|
35
48
|
## What you can do
|
|
36
49
|
|
|
37
|
-
- **
|
|
38
|
-
`
|
|
39
|
-
- **
|
|
40
|
-
`
|
|
41
|
-
doesn't reply.
|
|
42
|
-
- **
|
|
43
|
-
`
|
|
44
|
-
|
|
50
|
+
- **Control playback** — `play()`, `pause()`, `seekTo(seconds)`, `mute()`,
|
|
51
|
+
`unmute()`, and `playNext()` / `playPrev()` for playlists.
|
|
52
|
+
- **Ask the player questions** — `isMuted()`, `isPaused()`, `getCurrentTime()`,
|
|
53
|
+
`getDuration()`. The answers come from the player itself, so these return
|
|
54
|
+
Promises (and reject after 5 seconds if the player doesn't reply).
|
|
55
|
+
- **React to events** — pass handlers in `options.events`, or subscribe later
|
|
56
|
+
with `on()` (it returns an unsubscribe function): `ready`, `play`, `pause`,
|
|
57
|
+
`timeupdate`, `loadedmetadata`, `ended`, `volumechange`, `error`.
|
|
58
|
+
- **Keep it on screen** — `sticky: true` pins the player to a corner of the
|
|
59
|
+
screen when the visitor scrolls past it.
|
|
60
|
+
- **Clean up** — `dispose()` when your page removes the player.
|
|
45
61
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
Start with [Getting Started](https://docs.hope.cloud/libs/jetstream-player/getting-started),
|
|
63
|
+
then see [Player Options](https://docs.hope.cloud/libs/jetstream-player/player-options)
|
|
64
|
+
and [Player Methods](https://docs.hope.cloud/libs/jetstream-player/player-methods)
|
|
65
|
+
for everything you can configure and call.
|
|
49
66
|
|
|
50
|
-
##
|
|
67
|
+
## Pointing at a staging environment
|
|
51
68
|
|
|
52
|
-
By default the player loads the production embed.
|
|
53
|
-
|
|
69
|
+
By default the player loads the production embed. For special setups — a
|
|
70
|
+
staging or dev environment — point it elsewhere with `embedOrigin`:
|
|
54
71
|
|
|
55
72
|
```ts
|
|
56
|
-
new JetstreamPlayer('#player', {
|
|
73
|
+
const player = new JetstreamPlayer('#player', {
|
|
57
74
|
mediaId: 'jsv:xxxxxxxxxx',
|
|
58
|
-
|
|
75
|
+
embedOrigin: 'https://embed-stage.jetstream.studio',
|
|
59
76
|
});
|
|
60
77
|
```
|
|
61
78
|
|
|
62
|
-
|
|
63
|
-
real origin at runtime — so it keeps working even when the embed
|
|
79
|
+
Under the hood everything is cross-origin `postMessage`, and the SDK learns
|
|
80
|
+
the iframe's real origin at runtime — so it keeps working even when the embed
|
|
81
|
+
host redirects.
|
|
64
82
|
|
|
65
83
|
## Development
|
|
66
84
|
|
|
67
85
|
```bash
|
|
68
86
|
pnpm install
|
|
69
|
-
pnpm build #
|
|
70
|
-
pnpm test # vitest
|
|
71
|
-
pnpm lint
|
|
72
|
-
pnpm typecheck
|
|
73
|
-
pnpm docs:dev # vitepress docs
|
|
87
|
+
pnpm build # vp pack — bundle + .d.ts
|
|
88
|
+
pnpm test # vp test (vitest)
|
|
89
|
+
pnpm check # format check + type-aware lint + type errors (the CI gate)
|
|
74
90
|
```
|
|
75
91
|
|
|
76
|
-
|
|
92
|
+
Documentation lives in the monorepo's public docs site at
|
|
93
|
+
`docs/cloud/libs/jetstream-player/`, published to docs.hope.cloud.
|
|
94
|
+
|
|
95
|
+
Architecture decisions are recorded in the monorepo at
|
|
96
|
+
`docs/adr/jetstream-player/`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/bridge/protocol.types.d.ts
|
|
2
|
+
interface PlayerEventMap {
|
|
3
|
+
ready: unknown;
|
|
4
|
+
timeupdate: number;
|
|
5
|
+
play: unknown;
|
|
6
|
+
pause: unknown;
|
|
7
|
+
loadedmetadata: undefined;
|
|
8
|
+
ended: boolean;
|
|
9
|
+
volumechange: number;
|
|
10
|
+
error: unknown;
|
|
11
|
+
}
|
|
12
|
+
type PlayerEvents = keyof PlayerEventMap;
|
|
13
|
+
declare const playerListenEvents: readonly PlayerEvents[];
|
|
14
|
+
type PlayerGetterEvents = "is-muted" | "get-current-time" | "get-video-current-time" | "get-duration" | "is-paused";
|
|
15
|
+
interface PlayerEvent {
|
|
16
|
+
type: PlayerEvents | PlayerGetterEvents;
|
|
17
|
+
value?: unknown;
|
|
18
|
+
}
|
|
19
|
+
type PlayerCommandEvent = "play" | "pause" | "mute" | "unmute" | "seek-to" | "seekTo" | "play-next" | "play-prev";
|
|
20
|
+
interface PlayerCommand {
|
|
21
|
+
name: PlayerCommandEvent | PlayerGetterEvents;
|
|
22
|
+
params?: unknown;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/embed/embed-frame.types.d.ts
|
|
26
|
+
interface EmbedFrameOptions {
|
|
27
|
+
mediaId: string;
|
|
28
|
+
playerId?: string;
|
|
29
|
+
region?: "eu";
|
|
30
|
+
audioLang?: string;
|
|
31
|
+
subtitleLang?: string;
|
|
32
|
+
embedOrigin?: string;
|
|
33
|
+
/** @deprecated Use {@link embedOrigin} instead. */
|
|
34
|
+
origin?: string;
|
|
35
|
+
width?: string;
|
|
36
|
+
height?: string;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/player.types.d.ts
|
|
40
|
+
type PlayerEventHandler<E extends PlayerEvents = PlayerEvents> = (value: PlayerEventMap[E]) => void;
|
|
41
|
+
type PlayerEventHandlers = { [E in PlayerEvents]?: PlayerEventHandler<E> };
|
|
42
|
+
type JetstreamPlayerOptions = {
|
|
43
|
+
sticky?: boolean;
|
|
44
|
+
events?: PlayerEventHandlers;
|
|
45
|
+
} & EmbedFrameOptions;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/player.d.ts
|
|
48
|
+
declare class JetstreamPlayer {
|
|
49
|
+
private options;
|
|
50
|
+
private readonly listeners;
|
|
51
|
+
private iFramePiP;
|
|
52
|
+
private bridge;
|
|
53
|
+
constructor(el: string, options: JetstreamPlayerOptions);
|
|
54
|
+
private registerEvents;
|
|
55
|
+
private emit;
|
|
56
|
+
on<E extends PlayerEvents>(event: E, handler: PlayerEventHandler<E>): () => void;
|
|
57
|
+
off<E extends PlayerEvents>(event: E, handler?: PlayerEventHandler<E>): void;
|
|
58
|
+
private request;
|
|
59
|
+
play(): void;
|
|
60
|
+
pause(): void;
|
|
61
|
+
mute(): void;
|
|
62
|
+
unmute(): void;
|
|
63
|
+
seekTo(seconds: number): void;
|
|
64
|
+
playNext(): void;
|
|
65
|
+
playPrev(): void;
|
|
66
|
+
isMuted(): Promise<boolean>;
|
|
67
|
+
isPaused(): Promise<boolean>;
|
|
68
|
+
getCurrentTime(): Promise<number>;
|
|
69
|
+
/** @deprecated Use {@link getCurrentTime} instead. */
|
|
70
|
+
getVideoCurrentTime(): Promise<number>;
|
|
71
|
+
getDuration(): Promise<number>;
|
|
72
|
+
dispose(): void;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/bridge/protocol.d.ts
|
|
76
|
+
declare const decodeCommand: (raw: string) => PlayerCommand | null;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { type EmbedFrameOptions, JetstreamPlayer, type JetstreamPlayerOptions, type PlayerCommand, type PlayerCommandEvent, type PlayerEvent, type PlayerEventHandler, type PlayerEventHandlers, type PlayerEventMap, type PlayerEvents, type PlayerGetterEvents, decodeCommand, playerListenEvents };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
|
-
//#region src/
|
|
2
|
-
const
|
|
1
|
+
//#region src/bridge/protocol.ts
|
|
2
|
+
const encode = (command) => JSON.stringify(command);
|
|
3
|
+
const parseObjectWithStringKey = (raw, key) => {
|
|
4
|
+
if (typeof raw !== "string") return null;
|
|
5
|
+
try {
|
|
6
|
+
const parsed = JSON.parse(raw);
|
|
7
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed[key] === "string") return parsed;
|
|
8
|
+
return null;
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const decode = (raw) => parseObjectWithStringKey(raw, "type");
|
|
14
|
+
const decodeCommand = (raw) => parseObjectWithStringKey(raw, "name");
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/bridge/player-bridge.ts
|
|
17
|
+
const REQUEST_TIMEOUT_MS = 5e3;
|
|
18
|
+
var PlayerBridge = class {
|
|
19
|
+
transport;
|
|
20
|
+
targetOrigin;
|
|
21
|
+
listeners = /* @__PURE__ */ new Set();
|
|
22
|
+
detach;
|
|
23
|
+
constructor(transport, initialTargetOrigin) {
|
|
24
|
+
this.transport = transport;
|
|
25
|
+
this.targetOrigin = initialTargetOrigin;
|
|
26
|
+
this.detach = this.transport.subscribe((message, origin) => {
|
|
27
|
+
const event = decode(message);
|
|
28
|
+
if (!event) return;
|
|
29
|
+
this.targetOrigin = origin;
|
|
30
|
+
this.listeners.forEach((listener) => listener(event));
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
send(command) {
|
|
34
|
+
this.transport.send(encode(command), this.targetOrigin);
|
|
35
|
+
}
|
|
36
|
+
subscribe(listener) {
|
|
37
|
+
this.listeners.add(listener);
|
|
38
|
+
return () => {
|
|
39
|
+
this.listeners.delete(listener);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
request(command, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
let off = () => void 0;
|
|
45
|
+
const timer = setTimeout(() => {
|
|
46
|
+
off();
|
|
47
|
+
reject(/* @__PURE__ */ new Error(`Timed out waiting for "${command.name}" response`));
|
|
48
|
+
}, timeoutMs);
|
|
49
|
+
off = this.subscribe((event) => {
|
|
50
|
+
if (event.type === command.name) {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
off();
|
|
53
|
+
resolve(event.value);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.send(command);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
dispose() {
|
|
60
|
+
this.detach();
|
|
61
|
+
this.listeners.clear();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/bridge/player-transport.ts
|
|
66
|
+
const windowTransport = (iframe) => ({
|
|
67
|
+
send: (message, targetOrigin) => {
|
|
68
|
+
iframe.contentWindow?.postMessage(message, targetOrigin);
|
|
69
|
+
},
|
|
70
|
+
subscribe: (handler) => {
|
|
71
|
+
const listener = (event) => {
|
|
72
|
+
if (event.source !== iframe.contentWindow) return;
|
|
73
|
+
handler(event.data, event.origin);
|
|
74
|
+
};
|
|
75
|
+
window.addEventListener("message", listener);
|
|
76
|
+
return () => {
|
|
77
|
+
window.removeEventListener("message", listener);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/embed/embed-frame.ts
|
|
83
|
+
const DEFAULT_EMBED_ORIGIN = "https://jstre.am";
|
|
3
84
|
const regionOrigin = (origin, region) => {
|
|
4
85
|
const url = new URL(origin);
|
|
5
86
|
if (region === "eu") {
|
|
@@ -11,9 +92,9 @@ const regionOrigin = (origin, region) => {
|
|
|
11
92
|
};
|
|
12
93
|
const buildEmbedSrc = (options) => {
|
|
13
94
|
if (options.mediaId === "") throw new Error("mediaId not provided!");
|
|
14
|
-
const
|
|
95
|
+
const embedOrigin = regionOrigin(options.embedOrigin ?? options.origin ?? DEFAULT_EMBED_ORIGIN, options.region);
|
|
15
96
|
const path = options.playerId ? `/embed/${options.mediaId}/${options.playerId}` : `/embed/${options.mediaId}`;
|
|
16
|
-
const url = new URL(`${
|
|
97
|
+
const url = new URL(`${embedOrigin}${path}`);
|
|
17
98
|
if (options.region === "eu") url.searchParams.append("region", "eu");
|
|
18
99
|
if (options.audioLang) url.searchParams.append("audioLang", options.audioLang.toLowerCase());
|
|
19
100
|
if (options.subtitleLang) url.searchParams.append("subtitleLang", options.subtitleLang.toLowerCase());
|
|
@@ -24,12 +105,12 @@ const mountEmbedFrame = (selector, options) => {
|
|
|
24
105
|
const iframe = document.createElement("iframe");
|
|
25
106
|
iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
|
|
26
107
|
iframe.allowFullscreen = true;
|
|
27
|
-
iframe.
|
|
28
|
-
iframe.title = "Jetstream
|
|
108
|
+
iframe.style.border = "0";
|
|
109
|
+
iframe.title = "Jetstream Player";
|
|
29
110
|
iframe.src = src;
|
|
30
111
|
iframe.width = options.width || "480px";
|
|
31
112
|
iframe.height = options.height || "270px";
|
|
32
|
-
iframe.classList.add("
|
|
113
|
+
iframe.classList.add("jetstream-player-iframe");
|
|
33
114
|
const parent = document.querySelector(selector);
|
|
34
115
|
if (parent) parent.appendChild(iframe);
|
|
35
116
|
else console.error(`Element matching selector "${selector}" not found.`);
|
|
@@ -39,7 +120,7 @@ const mountEmbedFrame = (selector, options) => {
|
|
|
39
120
|
};
|
|
40
121
|
};
|
|
41
122
|
//#endregion
|
|
42
|
-
//#region src/iframe-pip-plugin.ts
|
|
123
|
+
//#region src/pip/iframe-pip-plugin.ts
|
|
43
124
|
const PiP_STYLES = {
|
|
44
125
|
position: "fixed",
|
|
45
126
|
bottom: "20px",
|
|
@@ -55,8 +136,6 @@ var IFramePiPPlugin = class {
|
|
|
55
136
|
originalIframeStyle = null;
|
|
56
137
|
parent = null;
|
|
57
138
|
constructor(containerSelector, iframe) {
|
|
58
|
-
this.containerSelector = containerSelector;
|
|
59
|
-
this.iframe = iframe;
|
|
60
139
|
this.containerSelector = containerSelector;
|
|
61
140
|
this.iframe = iframe;
|
|
62
141
|
this.init();
|
|
@@ -68,8 +147,10 @@ var IFramePiPPlugin = class {
|
|
|
68
147
|
this.parent = parent;
|
|
69
148
|
this.originalParentHeight = parent.style.height || null;
|
|
70
149
|
this.originalIframeStyle = this.iframe.getAttribute("style");
|
|
71
|
-
this.observer = new IntersectionObserver((
|
|
72
|
-
|
|
150
|
+
this.observer = new IntersectionObserver((entries) => {
|
|
151
|
+
const entry = entries[0];
|
|
152
|
+
if (!entry) return;
|
|
153
|
+
if (!entry.isIntersecting) {
|
|
73
154
|
const height = parent.getBoundingClientRect().height;
|
|
74
155
|
parent.style.height = `${height}px`;
|
|
75
156
|
this.iframe.style.position = PiP_STYLES.position;
|
|
@@ -98,90 +179,8 @@ var IFramePiPPlugin = class {
|
|
|
98
179
|
if (this.iframe) if (this.originalIframeStyle) this.iframe.setAttribute("style", this.originalIframeStyle);
|
|
99
180
|
else this.iframe.removeAttribute("style");
|
|
100
181
|
}
|
|
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
182
|
};
|
|
167
183
|
//#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
184
|
//#region src/player.ts
|
|
186
185
|
var JetstreamPlayer = class {
|
|
187
186
|
options;
|
|
@@ -256,7 +255,7 @@ var JetstreamPlayer = class {
|
|
|
256
255
|
}
|
|
257
256
|
seekTo(seconds) {
|
|
258
257
|
this.bridge?.send({
|
|
259
|
-
name: "
|
|
258
|
+
name: "seek-to",
|
|
260
259
|
params: seconds
|
|
261
260
|
});
|
|
262
261
|
}
|
|
@@ -272,8 +271,12 @@ var JetstreamPlayer = class {
|
|
|
272
271
|
isPaused() {
|
|
273
272
|
return this.request({ name: "is-paused" });
|
|
274
273
|
}
|
|
274
|
+
getCurrentTime() {
|
|
275
|
+
return this.request({ name: "get-current-time" });
|
|
276
|
+
}
|
|
277
|
+
/** @deprecated Use {@link getCurrentTime} instead. */
|
|
275
278
|
getVideoCurrentTime() {
|
|
276
|
-
return this.
|
|
279
|
+
return this.getCurrentTime();
|
|
277
280
|
}
|
|
278
281
|
getDuration() {
|
|
279
282
|
return this.request({ name: "get-duration" });
|
|
@@ -285,7 +288,7 @@ var JetstreamPlayer = class {
|
|
|
285
288
|
}
|
|
286
289
|
};
|
|
287
290
|
//#endregion
|
|
288
|
-
//#region src/
|
|
291
|
+
//#region src/bridge/protocol.types.ts
|
|
289
292
|
const playerListenEvents = [
|
|
290
293
|
"ready",
|
|
291
294
|
"timeupdate",
|
|
@@ -297,4 +300,4 @@ const playerListenEvents = [
|
|
|
297
300
|
"error"
|
|
298
301
|
];
|
|
299
302
|
//#endregion
|
|
300
|
-
export { JetstreamPlayer, playerListenEvents };
|
|
303
|
+
export { JetstreamPlayer, decodeCommand, playerListenEvents };
|
package/package.json
CHANGED
|
@@ -1,54 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hopecloud/jetstream-player",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0-rc.1",
|
|
4
4
|
"description": "Jetstream Embed Player SDK",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
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"
|
|
5
|
+
"homepage": "https://docs.hope.cloud/libs/jetstream-player/",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "DV Software",
|
|
8
|
+
"email": "code@deepvision.software"
|
|
18
9
|
},
|
|
19
|
-
"main": "dist/index.js",
|
|
20
10
|
"files": [
|
|
21
11
|
"dist"
|
|
22
12
|
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"main": "dist/index.js",
|
|
23
16
|
"exports": {
|
|
24
17
|
".": {
|
|
25
18
|
"types": "./dist/index.d.ts",
|
|
26
19
|
"import": "./dist/index.js"
|
|
27
20
|
}
|
|
28
21
|
},
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"name": "DV Software"
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
32
24
|
},
|
|
33
25
|
"devDependencies": {
|
|
34
|
-
"@deepvision/eslint-plugin": "2.0.3",
|
|
35
26
|
"@types/node": "24.12.0",
|
|
36
|
-
"eslint": "10.1.0",
|
|
37
27
|
"happy-dom": "^20.4.0",
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
28
|
+
"vite-plus": "^0.2.2"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": "24"
|
|
42
32
|
},
|
|
43
33
|
"scripts": {
|
|
44
|
-
"build": "
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"lint": "eslint",
|
|
49
|
-
"lint:fix": "eslint --fix",
|
|
50
|
-
"type-check": "tsc --noEmit",
|
|
51
|
-
"test": "vitest",
|
|
52
|
-
"test:run": "vitest run"
|
|
34
|
+
"build": "vp pack",
|
|
35
|
+
"check": "vp check",
|
|
36
|
+
"check:fix": "vp check --fix",
|
|
37
|
+
"test": "vp test"
|
|
53
38
|
}
|
|
54
39
|
}
|
package/dist/embed-frame.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
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;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare class IFramePiPPlugin {
|
|
2
|
-
containerSelector: string;
|
|
3
|
-
iframe: HTMLIFrameElement;
|
|
4
|
-
private observer;
|
|
5
|
-
private originalParentHeight;
|
|
6
|
-
private originalIframeStyle;
|
|
7
|
-
private parent;
|
|
8
|
-
constructor(containerSelector: string, iframe: HTMLIFrameElement);
|
|
9
|
-
init(): void;
|
|
10
|
-
destroy(): void;
|
|
11
|
-
destory(): void;
|
|
12
|
-
}
|
package/dist/player-bridge.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/player.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { PlayerEvents, JetstreamPlayerOptions, PlayerEventHandler } from './player.type';
|
|
2
|
-
export declare class JetstreamPlayer {
|
|
3
|
-
private options;
|
|
4
|
-
private readonly listeners;
|
|
5
|
-
private iFramePiP;
|
|
6
|
-
private bridge;
|
|
7
|
-
constructor(el: string, options: JetstreamPlayerOptions);
|
|
8
|
-
private registerEvents;
|
|
9
|
-
private emit;
|
|
10
|
-
on(event: PlayerEvents, handler: PlayerEventHandler): () => void;
|
|
11
|
-
off(event: PlayerEvents, handler?: PlayerEventHandler): void;
|
|
12
|
-
private request;
|
|
13
|
-
play(): void;
|
|
14
|
-
pause(): void;
|
|
15
|
-
mute(): void;
|
|
16
|
-
unmute(): void;
|
|
17
|
-
seekTo(seconds: number): void;
|
|
18
|
-
playNext(): void;
|
|
19
|
-
playPrev(): void;
|
|
20
|
-
isMuted(): Promise<boolean>;
|
|
21
|
-
isPaused(): Promise<boolean>;
|
|
22
|
-
getVideoCurrentTime(): Promise<number>;
|
|
23
|
-
getDuration(): Promise<number>;
|
|
24
|
-
dispose(): void;
|
|
25
|
-
}
|
package/dist/player.type.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export declare const playerListenEvents: readonly ["ready", "timeupdate", "play", "pause", "loadedmetadata", "ended", "volumechange", "error"];
|
|
2
|
-
export type PlayerEvents = 'ready' | 'timeupdate' | 'play' | 'pause' | 'loadedmetadata' | 'ended' | 'volumechange' | 'error';
|
|
3
|
-
export type PlayerGetterEvents = 'is-muted' | 'get-video-current-time' | 'get-duration' | 'is-paused';
|
|
4
|
-
export interface PlayerEvent {
|
|
5
|
-
type: PlayerEvents | PlayerGetterEvents;
|
|
6
|
-
value?: unknown;
|
|
7
|
-
}
|
|
8
|
-
export type PlayerEventHandler = (value?: unknown) => void;
|
|
9
|
-
export type PlayerCommandEvent = 'play' | 'pause' | 'mute' | 'unmute' | 'seekTo' | 'play-next' | 'play-prev';
|
|
10
|
-
export interface PlayerCommand {
|
|
11
|
-
name: PlayerCommandEvent | PlayerGetterEvents;
|
|
12
|
-
params?: unknown;
|
|
13
|
-
}
|
|
14
|
-
export interface EmbedFrameOptions {
|
|
15
|
-
mediaId: string;
|
|
16
|
-
playerId?: string;
|
|
17
|
-
region?: 'eu';
|
|
18
|
-
audioLang?: string;
|
|
19
|
-
subtitleLang?: string;
|
|
20
|
-
origin?: string;
|
|
21
|
-
width?: string;
|
|
22
|
-
height?: string;
|
|
23
|
-
}
|
|
24
|
-
export type JetstreamPlayerOptions = {
|
|
25
|
-
sticky?: boolean;
|
|
26
|
-
events?: Partial<Record<PlayerEvents, PlayerEventHandler>>;
|
|
27
|
-
} & EmbedFrameOptions;
|
package/dist/protocol.d.ts
DELETED