@netless/fastboard-core 0.2.11 → 0.3.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/index.d.ts +291 -3
- package/dist/index.js +257 -216
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +579 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -12
- package/src/behaviors/{register-apps.ts → index.ts} +12 -7
- package/src/impl/{app.ts → FastboardApp.ts} +177 -50
- package/src/impl/FastboardPlayer.ts +201 -0
- package/src/impl/index.ts +4 -0
- package/src/index.ts +2 -3
- package/src/internal.ts +14 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/misc.ts +49 -0
- package/src/utils/store.ts +87 -0
- package/src/utils/uid.ts +12 -0
- package/src/utils/warn.ts +11 -0
- package/dist/chunk-IHDHV4XE.mjs +0 -538
- package/dist/chunk-IHDHV4XE.mjs.map +0 -1
- package/dist/minimal.d.ts +0 -286
- package/dist/minimal.js +0 -559
- package/dist/minimal.js.map +0 -1
- package/dist/minimal.mjs +0 -13
- package/dist/minimal.mjs.map +0 -1
- package/minimal.mjs +0 -1
- package/src/behaviors/register-slide.ts +0 -11
- package/src/helpers/emitter.ts +0 -21
- package/src/helpers/utils.ts +0 -78
- package/src/helpers/value.ts +0 -84
- package/src/impl/player.ts +0 -122
- package/src/minimal.ts +0 -163
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type { MountParams, PublicEvent } from "@netless/window-manager";
|
|
2
|
+
import type {
|
|
3
|
+
Player,
|
|
4
|
+
PlayerPhase as PlayerPhaseEnum,
|
|
5
|
+
PlayerCallbacks,
|
|
6
|
+
PlayerState,
|
|
7
|
+
PlayerSeekingResult,
|
|
8
|
+
ReplayRoomParams,
|
|
9
|
+
ViewCallbacks,
|
|
10
|
+
WhiteWebSdkConfiguration,
|
|
11
|
+
} from "white-web-sdk";
|
|
12
|
+
|
|
13
|
+
import { WhiteWebSdk } from "white-web-sdk";
|
|
14
|
+
import { WindowManager } from "@netless/window-manager";
|
|
15
|
+
import { readable, writable } from "../utils";
|
|
16
|
+
import { ensure_window_manager } from "../internal";
|
|
17
|
+
|
|
18
|
+
class FastboardPlayerBase {
|
|
19
|
+
public constructor(readonly sdk: WhiteWebSdk, readonly player: Player, readonly manager: WindowManager) {}
|
|
20
|
+
|
|
21
|
+
protected _destroyed = false;
|
|
22
|
+
protected _assertNotDestroyed() {
|
|
23
|
+
if (this._destroyed) {
|
|
24
|
+
throw new Error("FastboardApp has been destroyed");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected _addPlayerListener<K extends keyof PlayerCallbacks>(name: K, listener: PlayerCallbacks[K]) {
|
|
29
|
+
this._assertNotDestroyed();
|
|
30
|
+
this.player.callbacks.on(name, listener);
|
|
31
|
+
return () => this.player.callbacks.off(name, listener);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected _addManagerListener<K extends keyof PublicEvent>(
|
|
35
|
+
name: K,
|
|
36
|
+
listener: (value: PublicEvent[K]) => void
|
|
37
|
+
) {
|
|
38
|
+
this._assertNotDestroyed();
|
|
39
|
+
this.manager.emitter.on(name, listener);
|
|
40
|
+
return () => this.manager.emitter.off(name, listener);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected _addMainViewListener<K extends keyof ViewCallbacks>(name: K, listener: ViewCallbacks[K]) {
|
|
44
|
+
this._assertNotDestroyed();
|
|
45
|
+
this.manager.mainView.callbacks.on(name, listener);
|
|
46
|
+
return () => this.manager.mainView.callbacks.off(name, listener);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public destroy() {
|
|
50
|
+
this._destroyed = true;
|
|
51
|
+
this.manager.destroy();
|
|
52
|
+
return this.player.callbacks.off();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type PlayerPhase = `${PlayerPhaseEnum}`;
|
|
57
|
+
|
|
58
|
+
export type { PlayerPhase, PlayerSeekingResult };
|
|
59
|
+
|
|
60
|
+
export class FastboardPlayer extends FastboardPlayerBase {
|
|
61
|
+
/**
|
|
62
|
+
* Render this player to some DOM.
|
|
63
|
+
*/
|
|
64
|
+
bindContainer(container: HTMLElement) {
|
|
65
|
+
this._assertNotDestroyed();
|
|
66
|
+
this.manager.bindContainer(container);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Move window-manager's collector to some place.
|
|
71
|
+
*/
|
|
72
|
+
bindCollector(container: HTMLElement) {
|
|
73
|
+
this._assertNotDestroyed();
|
|
74
|
+
this.manager.bindCollectorContainer(container);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
readonly currentTime = writable(
|
|
78
|
+
this.player.progressTime,
|
|
79
|
+
set => this._addPlayerListener("onProgressTimeChanged", set),
|
|
80
|
+
this.player.seekToProgressTime.bind(this.player)
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
readonly phase = readable<PlayerPhase>(this.player.phase, set =>
|
|
84
|
+
this._addPlayerListener("onPhaseChanged", set)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
readonly canplay = readable(this.player.isPlayable, set =>
|
|
88
|
+
this._addPlayerListener("onIsPlayableChanged", set)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
private _setSpeed!: (value: number) => void;
|
|
92
|
+
readonly speed = writable(
|
|
93
|
+
this.player.playbackSpeed,
|
|
94
|
+
set => {
|
|
95
|
+
this._setSpeed = set;
|
|
96
|
+
},
|
|
97
|
+
value => {
|
|
98
|
+
this.player.playbackSpeed = value;
|
|
99
|
+
this._setSpeed(value);
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
private _setReady!: (value: boolean) => void;
|
|
104
|
+
readonly ready = readable(false, set => {
|
|
105
|
+
this._setReady = set;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
private _setDuration!: (value: number) => void;
|
|
109
|
+
readonly duration = readable(0, set => {
|
|
110
|
+
this._setDuration = set;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
readonly state = readable<PlayerState | null>(null, set => {
|
|
114
|
+
const update = () => set(this.player.state);
|
|
115
|
+
this.player.callbacks.once("onLoadFirstFrame", () => {
|
|
116
|
+
this._setDuration(this.player.timeDuration);
|
|
117
|
+
this._setReady(true);
|
|
118
|
+
update();
|
|
119
|
+
});
|
|
120
|
+
return this._addPlayerListener("onPlayerStateChanged", update);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
seek(timestamp: number) {
|
|
124
|
+
this._assertNotDestroyed();
|
|
125
|
+
return this.player.seekToProgressTime(timestamp);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
play() {
|
|
129
|
+
this._assertNotDestroyed();
|
|
130
|
+
this.player.play();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
pause() {
|
|
134
|
+
this._assertNotDestroyed();
|
|
135
|
+
this.player.pause();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
stop() {
|
|
139
|
+
this._assertNotDestroyed();
|
|
140
|
+
this.player.stop();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
setSpeed(value: number) {
|
|
144
|
+
this._assertNotDestroyed();
|
|
145
|
+
this.speed.set(value);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface FastboardReplayOptions {
|
|
150
|
+
sdkConfig: Omit<WhiteWebSdkConfiguration, "useMobXState">;
|
|
151
|
+
replayRoom: Omit<ReplayRoomParams, "useMultiViews"> & {
|
|
152
|
+
callbacks?: Partial<PlayerCallbacks>;
|
|
153
|
+
};
|
|
154
|
+
managerConfig?: Omit<MountParams, "room">;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Create a FastboardApp instance.
|
|
159
|
+
* @example
|
|
160
|
+
* let player = await replayFastboard({
|
|
161
|
+
* sdkConfig: {
|
|
162
|
+
* appIdentifier: import.meta.env.VITE_APPID,
|
|
163
|
+
* region: 'cn-hz',
|
|
164
|
+
* },
|
|
165
|
+
* replayRoom: {
|
|
166
|
+
* uid: unique_id,
|
|
167
|
+
* uuid: import.meta.env.VITE_ROOM_UUID,
|
|
168
|
+
* roomToken: import.meta.env.VITE_ROOM_TOKEN,
|
|
169
|
+
* },
|
|
170
|
+
* })
|
|
171
|
+
*/
|
|
172
|
+
export async function replayFastboard({
|
|
173
|
+
sdkConfig,
|
|
174
|
+
replayRoom: { callbacks, ...replayRoomParams },
|
|
175
|
+
managerConfig,
|
|
176
|
+
}: FastboardReplayOptions) {
|
|
177
|
+
const sdk = new WhiteWebSdk({
|
|
178
|
+
...sdkConfig,
|
|
179
|
+
useMobXState: true,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const player = await sdk.replayRoom(
|
|
183
|
+
{
|
|
184
|
+
...ensure_window_manager(replayRoomParams),
|
|
185
|
+
useMultiViews: true,
|
|
186
|
+
},
|
|
187
|
+
callbacks
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const managerPromise = WindowManager.mount({
|
|
191
|
+
cursor: true,
|
|
192
|
+
...managerConfig,
|
|
193
|
+
room: player,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
player.play();
|
|
197
|
+
const manager = await managerPromise;
|
|
198
|
+
player.pause();
|
|
199
|
+
|
|
200
|
+
return new FastboardPlayer(sdk, player, manager);
|
|
201
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export * from "./minimal";
|
|
1
|
+
export * from "./utils";
|
|
2
|
+
export * from "./impl";
|
package/src/internal.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { JoinRoomParams, ReplayRoomParams } from "white-web-sdk";
|
|
2
|
+
import type { PublicEvent } from "@netless/window-manager";
|
|
3
|
+
import { WindowManager } from "@netless/window-manager";
|
|
4
|
+
|
|
5
|
+
export function ensure_window_manager<T extends JoinRoomParams | ReplayRoomParams>(joinRoom: T): T {
|
|
6
|
+
if (!joinRoom.invisiblePlugins || !joinRoom.invisiblePlugins.includes(WindowManager)) {
|
|
7
|
+
joinRoom.invisiblePlugins = [...(joinRoom.invisiblePlugins || []), WindowManager];
|
|
8
|
+
}
|
|
9
|
+
return joinRoom;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function transform_app_status(status: PublicEvent["loadApp"]["status"]) {
|
|
13
|
+
return status === "start" ? "loading" : status === "failed" ? "failed" : "idle";
|
|
14
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ConvertedFile, SceneDefinition, Size } from "white-web-sdk";
|
|
2
|
+
|
|
3
|
+
export function getImageSize(url: string, fallback: Size) {
|
|
4
|
+
return new Promise<Size>(resolve => {
|
|
5
|
+
const img = new Image();
|
|
6
|
+
img.onload = () => resolve(img);
|
|
7
|
+
img.onerror = () => resolve(fallback);
|
|
8
|
+
img.src = url;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function makeSlideParams(scenes: SceneDefinition[]) {
|
|
13
|
+
const emptyScenes: SceneDefinition[] = [];
|
|
14
|
+
let taskId = "";
|
|
15
|
+
let url = "";
|
|
16
|
+
|
|
17
|
+
// e.g. "ppt(x)://cdn/prefix/dynamicConvert/{taskId}/1.slide"
|
|
18
|
+
const pptSrcRE = /^pptx?(?<prefix>:\/\/\S+?dynamicConvert)\/(?<taskId>\w+)\//;
|
|
19
|
+
|
|
20
|
+
for (const { name, ppt } of scenes) {
|
|
21
|
+
// make sure scenesWithoutPPT.length === scenes.length
|
|
22
|
+
emptyScenes.push({ name });
|
|
23
|
+
|
|
24
|
+
if (!ppt || !ppt.src.startsWith("ppt")) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const match = pptSrcRE.exec(ppt.src);
|
|
28
|
+
if (!match || !match.groups) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
taskId = match.groups.taskId;
|
|
32
|
+
url = "https" + match.groups.prefix;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { scenes: emptyScenes, taskId, url };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function convertedFileToScene(f: ConvertedFile, i: number): SceneDefinition {
|
|
40
|
+
return {
|
|
41
|
+
name: String(i + 1),
|
|
42
|
+
ppt: {
|
|
43
|
+
src: f.conversionFileUrl,
|
|
44
|
+
width: f.width,
|
|
45
|
+
height: f.height,
|
|
46
|
+
previewURL: f.preview,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// This is a simple mimic of svelte/store.
|
|
2
|
+
export type Subscriber<T> = (value: T) => void;
|
|
3
|
+
export type Unsubscriber = () => void;
|
|
4
|
+
export type Updater<T> = (value: T) => T;
|
|
5
|
+
export type StartStopNotifier<T> = (set: Subscriber<T>) => Unsubscriber | void;
|
|
6
|
+
|
|
7
|
+
export interface Readable<T> {
|
|
8
|
+
readonly value: T;
|
|
9
|
+
subscribe(this: void, run: Subscriber<T>): Unsubscriber;
|
|
10
|
+
reaction(this: void, run: Subscriber<T>): Unsubscriber;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Writable<T> extends Readable<T> {
|
|
14
|
+
set(this: void, value: T): void;
|
|
15
|
+
update(this: void, updater: Updater<T>): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
19
|
+
function noop() {}
|
|
20
|
+
|
|
21
|
+
function safe_not_equal(a: unknown, b: unknown) {
|
|
22
|
+
return a != a ? b == b : a !== b || (a && typeof a === "object") || typeof a === "function";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function readable<T>(value: T, start: StartStopNotifier<T> = noop): Readable<T> {
|
|
26
|
+
let stop: Unsubscriber | undefined;
|
|
27
|
+
const subscribers = new Set<Subscriber<T>>();
|
|
28
|
+
function set(new_value: T) {
|
|
29
|
+
if (safe_not_equal(value, new_value)) {
|
|
30
|
+
value = new_value;
|
|
31
|
+
if (stop) {
|
|
32
|
+
for (const run of subscribers) {
|
|
33
|
+
run(value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function subscribe(run: Subscriber<T>) {
|
|
39
|
+
subscribers.add(run);
|
|
40
|
+
if (subscribers.size === 1) {
|
|
41
|
+
stop = start(set) || noop;
|
|
42
|
+
}
|
|
43
|
+
run(value);
|
|
44
|
+
return () => {
|
|
45
|
+
subscribers.delete(run);
|
|
46
|
+
if (subscribers.size === 0) {
|
|
47
|
+
stop && stop();
|
|
48
|
+
stop = undefined;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function reaction(run: Subscriber<T>) {
|
|
53
|
+
subscribers.add(run);
|
|
54
|
+
if (subscribers.size === 1) {
|
|
55
|
+
stop = start(set) || noop;
|
|
56
|
+
}
|
|
57
|
+
return () => {
|
|
58
|
+
subscribers.delete(run);
|
|
59
|
+
if (subscribers.size === 0) {
|
|
60
|
+
stop && stop();
|
|
61
|
+
stop = undefined;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
get value() {
|
|
67
|
+
return value;
|
|
68
|
+
},
|
|
69
|
+
subscribe,
|
|
70
|
+
reaction,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function writable<T>(value: T, start: StartStopNotifier<T> = noop, set: Subscriber<T>): Writable<T> {
|
|
75
|
+
const internal = readable(value, start);
|
|
76
|
+
return {
|
|
77
|
+
get value() {
|
|
78
|
+
return internal.value;
|
|
79
|
+
},
|
|
80
|
+
subscribe: internal.subscribe,
|
|
81
|
+
reaction: internal.reaction,
|
|
82
|
+
set,
|
|
83
|
+
update(fn: Updater<T>) {
|
|
84
|
+
set(fn(value));
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
package/src/utils/uid.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copy from https://github.com/crimx/side-effect-manager/blob/main/src/gen-uid.ts
|
|
2
|
+
const SOUP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
3
|
+
const SOUP_LEN = 62; // SOUP.length
|
|
4
|
+
const ID_LEN = 20;
|
|
5
|
+
const reusedIdCarrier = /* @__PURE__ */ Array(ID_LEN);
|
|
6
|
+
|
|
7
|
+
export function genUID() {
|
|
8
|
+
for (let i = 0; i < ID_LEN; i++) {
|
|
9
|
+
reusedIdCarrier[i] = SOUP.charAt(Math.random() * SOUP_LEN);
|
|
10
|
+
}
|
|
11
|
+
return reusedIdCarrier.join("");
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const warnings = {
|
|
2
|
+
"no-ppt-in-scenes":
|
|
3
|
+
"You're probably inserting the slide app in a wrong way, there shouldn't exist `scenes[0].ppt`.",
|
|
4
|
+
} as const;
|
|
5
|
+
const warned = new Set<string>();
|
|
6
|
+
|
|
7
|
+
export function warn(id: keyof typeof warnings) {
|
|
8
|
+
if (warned.has(id)) return;
|
|
9
|
+
warned.add(id);
|
|
10
|
+
console.warn(warnings[id]);
|
|
11
|
+
}
|