floating-live 0.15.5 → 0.15.6

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.
@@ -0,0 +1,77 @@
1
+ import { EventEmitter } from "events";
2
+ import { ConnectStatus, RoomStatus } from "../enums";
3
+ export class LiveRoom extends EventEmitter {
4
+ constructor() {
5
+ super();
6
+ /** 直播状态 */
7
+ this.status = RoomStatus.off;
8
+ /** 时间戳 */
9
+ this.timestamp = 0;
10
+ /** 房间是否打开 */
11
+ this.opened = false;
12
+ /** 是否连接上房间 */
13
+ this.connection = ConnectStatus.off;
14
+ }
15
+ /** 销毁 */
16
+ destroy() {
17
+ this.close();
18
+ this.removeAllListeners();
19
+ this.onDestroy?.();
20
+ }
21
+ // 发送消息
22
+ emitMessage(msg) {
23
+ // 其他消息 => 先更改状态再发送消息
24
+ // 直播结束消息 => 先发送消息再更改状态
25
+ switch (msg.type) {
26
+ case "live_start":
27
+ this.status = RoomStatus.live;
28
+ this.timestamp = msg.timestamp;
29
+ this.emit("status", RoomStatus.live, {
30
+ timestamp: msg.timestamp,
31
+ id: msg.info.id,
32
+ });
33
+ break;
34
+ case "live_detail":
35
+ this.detail = Object.assign(this.detail, msg.info);
36
+ this.emit("detail", msg.info);
37
+ break;
38
+ case "live_stats":
39
+ this.stats = Object.assign(this.stats, msg.info);
40
+ this.emit("stats", msg.info);
41
+ break;
42
+ case "live_end":
43
+ case "live_cut":
44
+ this.status = msg.status || RoomStatus.off;
45
+ this.timestamp = msg.timestamp;
46
+ this.emit("message", msg);
47
+ this.emit("status", msg.status || RoomStatus.off, {
48
+ timestamp: msg.timestamp,
49
+ });
50
+ return;
51
+ }
52
+ this.emit("message", msg);
53
+ }
54
+ // 发送源消息
55
+ emitRaw(msg) {
56
+ this.emit("raw", msg, { platform: this.platform, room: this.id });
57
+ }
58
+ get info() {
59
+ return {
60
+ platform: this.platform,
61
+ id: this.id,
62
+ detail: this.detail,
63
+ stats: this.stats,
64
+ anchor: this.anchor,
65
+ timestamp: this.timestamp,
66
+ status: this.status,
67
+ liveId: this.liveId,
68
+ available: this.available,
69
+ opened: this.opened,
70
+ connection: this.connection,
71
+ key: this.key,
72
+ };
73
+ }
74
+ get key() {
75
+ return `${this.platform}:${this.id}`;
76
+ }
77
+ }
@@ -0,0 +1 @@
1
+ export * from "./LiveRoom";
@@ -0,0 +1,24 @@
1
+ export class BasePlugin {
2
+ constructor(main) {
3
+ this.registerList = [];
4
+ this.main = main;
5
+ }
6
+ /** 插件监听事件 */
7
+ on(eventName, listener) {
8
+ this.main.on(eventName, listener);
9
+ this.registerList.push(["event", eventName, listener]);
10
+ }
11
+ /** 插件一次性监听事件 */
12
+ once(eventName, listener) {
13
+ this.main.once(eventName, listener);
14
+ this.registerList.push(["event", eventName, listener]);
15
+ }
16
+ /** 插件移除监听事件 */
17
+ off(eventName, listener) {
18
+ this.main.off(eventName, listener);
19
+ }
20
+ /** 插件发送事件 */
21
+ emit(eventName, ...args) {
22
+ this.main.emit(eventName, ...args);
23
+ }
24
+ }
package/esm/enums.js ADDED
@@ -0,0 +1,56 @@
1
+ /** 直播间状态 */
2
+ export var RoomStatus;
3
+ (function (RoomStatus) {
4
+ /** 未开播 */
5
+ RoomStatus[RoomStatus["off"] = 0] = "off";
6
+ /** 正在直播 */
7
+ RoomStatus[RoomStatus["live"] = 1] = "live";
8
+ /** 正在轮播 */
9
+ RoomStatus[RoomStatus["round"] = 2] = "round";
10
+ /** 被封禁 */
11
+ RoomStatus[RoomStatus["banned"] = -1] = "banned";
12
+ /** 已上锁 */
13
+ RoomStatus[RoomStatus["locked"] = -2] = "locked";
14
+ })(RoomStatus || (RoomStatus = {}));
15
+ /** 直播间状态 */
16
+ export var ConnectStatus;
17
+ (function (ConnectStatus) {
18
+ /** 未连接 */
19
+ ConnectStatus[ConnectStatus["off"] = 0] = "off";
20
+ /** 连接中 */
21
+ ConnectStatus[ConnectStatus["connecting"] = 1] = "connecting";
22
+ /** 连接到服务器 */
23
+ ConnectStatus[ConnectStatus["connected"] = 2] = "connected";
24
+ /** 连接到房间 */
25
+ ConnectStatus[ConnectStatus["entered"] = 3] = "entered";
26
+ /** 连接失败或断开 */
27
+ ConnectStatus[ConnectStatus["disconnected"] = -1] = "disconnected";
28
+ })(ConnectStatus || (ConnectStatus = {}));
29
+ /** 图片大小 */
30
+ export var ImageSize;
31
+ (function (ImageSize) {
32
+ /** 自定义大小 */
33
+ ImageSize[ImageSize["custom"] = 0] = "custom";
34
+ /** 小表情(文字行高) */
35
+ ImageSize[ImageSize["small"] = 1] = "small";
36
+ /** 大表情 */
37
+ ImageSize[ImageSize["large"] = 2] = "large";
38
+ })(ImageSize || (ImageSize = {}));
39
+ /** 弹幕模式 */
40
+ export var DanmakuMode;
41
+ (function (DanmakuMode) {
42
+ DanmakuMode[DanmakuMode["left"] = 1] = "left";
43
+ DanmakuMode[DanmakuMode["bottom"] = 4] = "bottom";
44
+ DanmakuMode[DanmakuMode["top"] = 5] = "top";
45
+ DanmakuMode[DanmakuMode["right"] = 6] = "right";
46
+ })(DanmakuMode || (DanmakuMode = {}));
47
+ /** 用户类型 */
48
+ export var UserType;
49
+ (function (UserType) {
50
+ /** 普通观众 */
51
+ UserType[UserType["normal"] = 0] = "normal";
52
+ /** 房管 */
53
+ UserType[UserType["admin"] = 1] = "admin";
54
+ /** 主播 */
55
+ UserType[UserType["anchor"] = 2] = "anchor";
56
+ })(UserType || (UserType = {}));
package/esm/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { FloatingLive } from "./live";
2
+ export * from "./types";
3
+ export * from "./enums";
4
+ export * from "./core/LiveRoom";
5
+ export * from "./utils";
package/esm/live.js ADDED
@@ -0,0 +1,46 @@
1
+ import { EventEmitter } from "events";
2
+ import { ModPlugin } from "./module/plugin";
3
+ import { ModRoom } from "./module/room";
4
+ import { ModManifest } from "./module/manifest";
5
+ import { ModCommand } from "./module/command";
6
+ import { ModValue } from "./module/value";
7
+ import { ModHook } from "./module/hook";
8
+ export class FloatingLive extends EventEmitter {
9
+ constructor() {
10
+ super();
11
+ this.command = new ModCommand(this);
12
+ this.manifest = new ModManifest(this);
13
+ this.plugin = new ModPlugin(this);
14
+ this.room = new ModRoom(this);
15
+ this.value = new ModValue(this);
16
+ this.hook = new ModHook(this);
17
+ }
18
+ call(name, ...args) {
19
+ return this.command.call(name, ...args);
20
+ }
21
+ on(eventName, listener) {
22
+ return super.on(eventName, listener);
23
+ }
24
+ emit(eventName, ...args) {
25
+ const emit = super.emit(eventName, ...args);
26
+ super.emit("event", eventName, ...args);
27
+ return emit;
28
+ }
29
+ throw(err, options) {
30
+ if (err instanceof Error) {
31
+ throw Object.assign(err, options);
32
+ }
33
+ else {
34
+ throw Object.assign(new Error(), err);
35
+ }
36
+ }
37
+ getSnapshot() {
38
+ return {
39
+ room: this.room.getSnapshot(),
40
+ value: this.value.getSnapshot(),
41
+ manifest: this.manifest.getSnapshot(),
42
+ command: this.command.getSnapshot(),
43
+ hook: this.value.getSnapshot(),
44
+ };
45
+ }
46
+ }
@@ -0,0 +1,38 @@
1
+ export class ModCommand {
2
+ constructor(main) {
3
+ /** 功能列表 */
4
+ this.list = new Map();
5
+ this.main = main;
6
+ }
7
+ /** 注册命令 */
8
+ register(name, func) {
9
+ this.list.set(name, func);
10
+ this.main.emit("command:add", name);
11
+ }
12
+ /** 取消注册命令 */
13
+ unregister(name) {
14
+ this.list.delete(name);
15
+ this.main.emit("command:remove", name);
16
+ }
17
+ /** 调用命令 */
18
+ call(name, ...args) {
19
+ const command = this.list.get(name);
20
+ if (!command) {
21
+ this.main.throw({
22
+ message: "命令不存在",
23
+ id: "command:call_unexist",
24
+ });
25
+ }
26
+ else {
27
+ return command(...args);
28
+ }
29
+ }
30
+ /** 检测命令是否存在 */
31
+ has(name) {
32
+ return this.list.has(name);
33
+ }
34
+ /** 获取快照 */
35
+ getSnapshot() {
36
+ return [...this.list].map(([key]) => key);
37
+ }
38
+ }
@@ -0,0 +1,40 @@
1
+ export class ModHook {
2
+ constructor(main) {
3
+ this.list = new Map();
4
+ this.main = main;
5
+ }
6
+ /** 注册钩子函数 */
7
+ register(name, func) {
8
+ let hooks = this.list.get(name);
9
+ if (!hooks) {
10
+ hooks = [];
11
+ this.list.set(name, hooks);
12
+ }
13
+ hooks.push(func);
14
+ }
15
+ /** 取消注册钩子函数 */
16
+ unregister(name, func) {
17
+ let hooks = this.list.get(name);
18
+ if (hooks) {
19
+ const index = hooks.indexOf(func);
20
+ index > -1 && hooks.splice(index, 1);
21
+ }
22
+ }
23
+ /** 调用钩子函数 */
24
+ async call(name, ctx) {
25
+ const hooks = this.list.get(name);
26
+ if (!hooks) {
27
+ return true;
28
+ }
29
+ for (const func of hooks) {
30
+ const result = await func(ctx);
31
+ if (result == true) {
32
+ return true;
33
+ }
34
+ else if (result == false) {
35
+ return false;
36
+ }
37
+ }
38
+ return true;
39
+ }
40
+ }
@@ -0,0 +1,40 @@
1
+ export class ModManifest {
2
+ constructor(main) {
3
+ this.list = new Map();
4
+ this.main = main;
5
+ }
6
+ /** 注册manifest */
7
+ register(name, id, value) {
8
+ let store = this.list.get(name);
9
+ if (!store) {
10
+ store = new Map();
11
+ this.list.set(name, store);
12
+ }
13
+ store.set(id, value);
14
+ this.main.emit("manifest:add", name, id, value);
15
+ }
16
+ /** 取消注册manifest */
17
+ unregister(name, id) {
18
+ let store = this.list.get(name);
19
+ if (store) {
20
+ store.delete(id);
21
+ this.main.emit("manifest:remove", name, id);
22
+ }
23
+ }
24
+ /** 获取值 */
25
+ get(name) {
26
+ return this.list.get(name);
27
+ }
28
+ /** 获取快照 */
29
+ getSnapshot() {
30
+ const map = {};
31
+ this.list.forEach((store, name) => {
32
+ const part = {};
33
+ map[name] = part;
34
+ store.forEach((value, id) => {
35
+ part[id] = value;
36
+ });
37
+ });
38
+ return map;
39
+ }
40
+ }
@@ -0,0 +1,59 @@
1
+ export class ModPlugin {
2
+ constructor(main) {
3
+ /** 插件列表 */
4
+ this.list = new Map();
5
+ this.main = main;
6
+ }
7
+ /** 注册插件 */
8
+ async register(pluginFunc, options = {}) {
9
+ const name = pluginFunc.pluginName;
10
+ if (!name) {
11
+ this.main.throw({
12
+ message: "缺少插件id",
13
+ id: "plugin:register_id_missing",
14
+ });
15
+ }
16
+ if (this.list.has(name)) {
17
+ this.main.throw({
18
+ message: "已存在相同id的插件",
19
+ id: "plugin:register_id_duplicate",
20
+ });
21
+ }
22
+ return await this.main.hook
23
+ .call("plugin.register", { name, options })
24
+ .then(async (res) => {
25
+ if (!res)
26
+ return;
27
+ // 执行插件函数
28
+ const plugin = new pluginFunc(this.main, options);
29
+ this.list.set(name, plugin);
30
+ // 调用插件的register钩子
31
+ await plugin.register?.(this.main, options);
32
+ this.main.emit("plugin:add", name);
33
+ return plugin;
34
+ });
35
+ }
36
+ /** 移除插件 */
37
+ async unregister(name) {
38
+ const plugin = this.list.get(name);
39
+ // 检测插件是否存在
40
+ if (!plugin) {
41
+ this.main.throw({
42
+ message: "插件不存在",
43
+ id: "plugin:unregister_unexist",
44
+ });
45
+ }
46
+ else {
47
+ // 调用该插件的destroy钩子
48
+ await plugin.destroy?.();
49
+ // 从列表中移除插件
50
+ this.list.delete(name);
51
+ // 移除插件注册
52
+ this.main.emit("plugin:remove", name);
53
+ }
54
+ }
55
+ /** 获取插件实例 */
56
+ get(name) {
57
+ return this.list.get(name);
58
+ }
59
+ }
@@ -0,0 +1,151 @@
1
+ /** 直播间监听实例控制器 */
2
+ export class ModRoom {
3
+ constructor(main) {
4
+ this.list = new Map();
5
+ this.main = main;
6
+ const { command } = main;
7
+ command.register("add", this.add.bind(this));
8
+ command.register("remove", this.remove.bind(this));
9
+ command.register("open", this.open.bind(this));
10
+ command.register("close", this.close.bind(this));
11
+ command.register("update", this.update.bind(this));
12
+ }
13
+ /** 添加房间监听实例 */
14
+ addRoom(room, open) {
15
+ let key = room.key;
16
+ if (this.list.has(key)) {
17
+ this.main.throw({
18
+ message: "房间已存在",
19
+ id: "room:add_exist",
20
+ });
21
+ return;
22
+ }
23
+ this.list.set(key, room);
24
+ // 添加监听事件
25
+ // 直播消息
26
+ room.on("message", (data) => {
27
+ this.main.hook.call("message", { message: data }).then((res) => {
28
+ res && this.main.emit("live:message", data);
29
+ });
30
+ });
31
+ // 直播消息源数据
32
+ room.on("raw", (data, { platform, room }) => {
33
+ this.main.emit("live:raw", data, { platform, room });
34
+ });
35
+ // 正在连接服务器
36
+ room.on("connecting", () => {
37
+ this.main.emit("room:connecting", key);
38
+ });
39
+ // 连接到服务器
40
+ room.on("connected", () => {
41
+ this.main.emit("room:connected", key);
42
+ });
43
+ // 与服务器的连接已断开
44
+ room.on("disconnect", () => {
45
+ this.main.emit("room:disconnect", key);
46
+ });
47
+ // 连接到直播间
48
+ room.on("enter", () => {
49
+ this.main.emit("room:enter", key);
50
+ });
51
+ // 获取房间信息
52
+ room.on("info", () => {
53
+ this.main.emit("room:info", key, room.info);
54
+ });
55
+ // 直播展示信息更改
56
+ room.on("detail", (data) => {
57
+ this.main.emit("room:detail", key, data);
58
+ });
59
+ // 直播状态更改
60
+ room.on("status", (status, { id, timestamp }) => {
61
+ this.main.emit("room:status", key, { status, id, timestamp });
62
+ });
63
+ // 统计数据更新
64
+ room.on("stats", (data) => {
65
+ this.main.emit("room:stats", key, data);
66
+ });
67
+ // 房间被打开
68
+ room.on("open", () => {
69
+ this.main.emit("room:open", key, room.info);
70
+ });
71
+ // 房间被关闭
72
+ room.on("close", () => {
73
+ this.main.emit("room:close", key, room.info);
74
+ });
75
+ if (open) {
76
+ room.open();
77
+ }
78
+ this.main.emit("room:add", key, room.info);
79
+ }
80
+ /** 添加房间 */
81
+ async add(platform, id, options) {
82
+ const opt = typeof options == "boolean" ? { open: options } : options || {};
83
+ const ctx = { platform, id, options: opt };
84
+ const res = await this.main.hook.call("room.add", ctx);
85
+ if (!res) {
86
+ this.main.throw({ message: "无法添加房间", id: "room:add_fail" });
87
+ }
88
+ const room = await this.main.call(`${platform}.room.create`, id, opt);
89
+ if (!room) {
90
+ this.main.throw({ message: "无法创建房间", id: "room:create_fail" });
91
+ }
92
+ else {
93
+ this.addRoom(room);
94
+ }
95
+ }
96
+ /** 移除房间 */
97
+ remove(roomKey) {
98
+ let room = this.list.get(roomKey);
99
+ if (!room) {
100
+ this.main.throw({
101
+ message: "房间不存在",
102
+ id: "room:remove_unexist",
103
+ });
104
+ }
105
+ this.list.delete(roomKey); // 从表中删除房间
106
+ room?.removeAllListeners(); // 移除房间监听实例
107
+ this.main.emit("room:remove", roomKey);
108
+ }
109
+ /** 获取房间 */
110
+ get(roomKey) {
111
+ return this.list.get(roomKey);
112
+ }
113
+ /** 获取房间信息 */
114
+ info(roomKey) {
115
+ let room = this.list.get(roomKey);
116
+ return room ? room.info : undefined;
117
+ }
118
+ /** 更新房间信息 */
119
+ update(roomKey) {
120
+ let room = this.list.get(roomKey);
121
+ room && room.getInfo();
122
+ }
123
+ /** 获取roomKey列表 */
124
+ get keys() {
125
+ return [...this.list.keys()];
126
+ }
127
+ /** 打开房间连接 */
128
+ open(roomKey) {
129
+ let room = this.list.get(roomKey);
130
+ room?.available && !room.opened && room.open();
131
+ }
132
+ /** 关闭房间连接 */
133
+ close(roomKey) {
134
+ let room = this.list.get(roomKey);
135
+ room?.opened && room.close();
136
+ }
137
+ /** 更改某个房间的顺序 */
138
+ move(roomKey, position) {
139
+ const arr = [...this.list];
140
+ const index = arr.findIndex(([key]) => key == roomKey);
141
+ if (index == -1)
142
+ return;
143
+ const [item] = arr.splice(index, 1);
144
+ arr.splice(position, 0, item);
145
+ this.list = new Map(arr);
146
+ }
147
+ /** 获取快照 */
148
+ getSnapshot() {
149
+ return [...this.list].map(([key, room]) => room.info);
150
+ }
151
+ }
@@ -0,0 +1,49 @@
1
+ export class ModValue {
2
+ constructor(main) {
3
+ this.list = new Map();
4
+ this.main = main;
5
+ }
6
+ /** 注册值 */
7
+ register(name, config) {
8
+ this.list.set(name, config);
9
+ this.main.emit("value:add", name);
10
+ }
11
+ /** 取消注册 */
12
+ unregister(name) {
13
+ this.list.delete(name);
14
+ this.main.emit("value:remove", name);
15
+ }
16
+ /** 获取值 */
17
+ get(name) {
18
+ return this.list.get(name)?.get();
19
+ }
20
+ /** 设置值 */
21
+ set(name, value) {
22
+ const valueConfig = this.list.get(name);
23
+ if (valueConfig?.set) {
24
+ valueConfig?.set(value);
25
+ return true;
26
+ }
27
+ else {
28
+ return false;
29
+ }
30
+ }
31
+ /** 获取注册的所有值 */
32
+ getSnapshot() {
33
+ const map = {};
34
+ this.list.forEach((config, name) => {
35
+ map[name] = config.get();
36
+ });
37
+ return map;
38
+ }
39
+ emit(name, value) {
40
+ this.main.emit(`change:${name}`, value);
41
+ this.main.emit("value:change", name, value);
42
+ }
43
+ watch(name, listener) {
44
+ this.main.on(`change:${name}`, listener);
45
+ }
46
+ unwatch(name, listener) {
47
+ this.main.off(`change:${name}`, listener);
48
+ }
49
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export * from "./message";
2
+ export * from "./plugin";
3
+ export * from "./room";
4
+ export * from "./command";
5
+ export * from "./events";
6
+ export * from "./manifest";
7
+ export * from "./hook";
8
+ export * from "./value";
9
+ export * from "./error";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/esm/utils.js ADDED
@@ -0,0 +1,17 @@
1
+ export function generateId(msg) {
2
+ switch (msg.type) {
3
+ case "gift":
4
+ return `gift:${msg.info.user.id}-${msg.timestamp}`;
5
+ case "superchat":
6
+ return `superchat:${msg.info.id}`;
7
+ case "membership":
8
+ return `membership:${msg.info.user.id}-${msg.info.level || 0}-${msg.timestamp}`;
9
+ default:
10
+ if (/^live_/.test(msg.type)) {
11
+ return `${msg.type}:${msg.platform}:${msg.room}-${msg.timestamp}`;
12
+ }
13
+ else {
14
+ return `${msg.type}:${msg.info.user?.id || 0}-${msg.timestamp}`;
15
+ }
16
+ }
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floating-live",
3
- "version": "0.15.5",
3
+ "version": "0.15.6",
4
4
  "type": "commonjs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,31 +9,36 @@
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
- "require": "./dist/index.js"
12
+ "require": "./dist/index.js",
13
+ "import": "./esm/index.js"
13
14
  },
14
15
  "./core": {
15
16
  "types": "./dist/core/index.d.ts",
16
- "require": "./dist/core/index.js"
17
+ "require": "./dist/core/index.js",
18
+ "import": "./esm/core/index.js"
17
19
  },
18
20
  "./types": {
19
21
  "types": "./dist/types/index.d.ts",
20
- "require": "./dist/types/index.js"
22
+ "require": "./dist/types/index.js",
23
+ "import": "./esm/types/index.js"
21
24
  },
22
25
  "./enums": {
23
26
  "types": "./dist/enums.d.ts",
24
- "require": "./dist/enums.js"
27
+ "require": "./dist/enums.js",
28
+ "import": "./esm/enums.js"
25
29
  },
26
30
  "./utils": {
27
31
  "types": "./dist/utils.d.ts",
28
- "require": "./dist/utils.js"
32
+ "require": "./dist/utils.js",
33
+ "import": "./esm/utils.js"
29
34
  }
30
35
  },
31
36
  "scripts": {
32
37
  "tsc": "tsc -b",
33
- "clear": "rimraf dist",
38
+ "clear": "rimraf dist esm",
34
39
  "test": "npm run clear && ts-node demo/demo.ts",
35
40
  "build-test": "npm run build && node demo/demo.js",
36
- "build": "npm run clear && npm run tsc",
41
+ "build": "npm run clear && tsc -b && tsc --project tsconfig.esm.json",
37
42
  "prepack": "npm run build",
38
43
  "postpack": "npm run clear"
39
44
  },