@dan-uni/dan-any 0.0.2
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/.babelrc.json +12 -0
- package/LICENSE +165 -0
- package/README.md +30 -0
- package/buf.gen.yaml +8 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +1 -0
- package/dist/index.min.js +1 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.umd.min.js +1 -0
- package/dist/proto/gen/bili/dm_pb.d.ts +3426 -0
- package/dist/proto/gen/danuni_pb.d.ts +145 -0
- package/dist/utils/dm-gen.d.ts +314 -0
- package/dist/utils/id-gen.d.ts +72 -0
- package/package.json +45 -0
- package/rollup.config.mjs +51 -0
- package/src/index.test.ts +51 -0
- package/src/index.ts +282 -0
- package/src/proto/gen/bili/dm_pb.ts +4072 -0
- package/src/proto/gen/danuni_pb.ts +187 -0
- package/src/proto/src/bili/dm.proto +1095 -0
- package/src/proto/src/danuni.proto +42 -0
- package/src/utils/dm-gen.ts +638 -0
- package/src/utils/id-gen.ts +118 -0
- package/tsconfig.json +110 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package danuni.danmaku.v1;
|
|
4
|
+
|
|
5
|
+
import public "google/protobuf/timestamp.proto";
|
|
6
|
+
|
|
7
|
+
enum Mode {
|
|
8
|
+
Normal = 0;
|
|
9
|
+
Bottom = 1;
|
|
10
|
+
Top = 2;
|
|
11
|
+
Reverse = 3;
|
|
12
|
+
Ext = 4;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
enum Pool {
|
|
16
|
+
Def = 0;
|
|
17
|
+
Sub = 1;
|
|
18
|
+
Adv = 2;
|
|
19
|
+
Ix = 3;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
message Danmaku {
|
|
23
|
+
string FCID = 1;
|
|
24
|
+
string DMID = 2;
|
|
25
|
+
int32 progress = 3;
|
|
26
|
+
Mode mode = 4;
|
|
27
|
+
int32 fontsize = 5;
|
|
28
|
+
int32 color = 6;
|
|
29
|
+
string senderID = 7;
|
|
30
|
+
string content = 8;
|
|
31
|
+
google.protobuf.Timestamp ctime = 9;
|
|
32
|
+
int32 weight = 10;
|
|
33
|
+
Pool pool = 11;
|
|
34
|
+
repeated string attr = 12;
|
|
35
|
+
string platform = 13;
|
|
36
|
+
optional string SPMO = 14;
|
|
37
|
+
optional string extra = 15;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
message DanmakuReply {
|
|
41
|
+
repeated Danmaku danmakus = 1;
|
|
42
|
+
}
|
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import type { DM_JSON_BiliCommandGrpc } from '..'
|
|
2
|
+
import type { platfrom } from './id-gen'
|
|
3
|
+
|
|
4
|
+
import { createDMID, domainPreset, UniID as ID, platforms } from './id-gen'
|
|
5
|
+
|
|
6
|
+
const BigIntSerializer = (k: string, v: any) =>
|
|
7
|
+
typeof v === 'bigint' ? v.toString() : v
|
|
8
|
+
|
|
9
|
+
class SetBin {
|
|
10
|
+
constructor(public bin: number) {}
|
|
11
|
+
set1(bit: number) {
|
|
12
|
+
this.bin |= 1 << bit
|
|
13
|
+
}
|
|
14
|
+
set0(bit: number) {
|
|
15
|
+
this.bin &= ~(1 << bit)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const toBits = (number: number) => {
|
|
19
|
+
// 低速方案
|
|
20
|
+
// return [...number.toString(2)].map(Number)
|
|
21
|
+
// 高速方案,位运算允许范围内更快
|
|
22
|
+
const bits: boolean[] = []
|
|
23
|
+
do {
|
|
24
|
+
bits.unshift(!!(number & 1)) // boolean[]
|
|
25
|
+
// bits.unshift(number & 1) // (0|1)[]
|
|
26
|
+
number >>= 1
|
|
27
|
+
} while (number)
|
|
28
|
+
return bits
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type DMAttr =
|
|
32
|
+
| 'Protect'
|
|
33
|
+
| 'FromLive'
|
|
34
|
+
| 'HighLike'
|
|
35
|
+
| 'Compatible'
|
|
36
|
+
| 'Reported'
|
|
37
|
+
const DMAttrUtils = {
|
|
38
|
+
fromBin(bin: number = 0, format?: platfrom) {
|
|
39
|
+
const array = toBits(bin),
|
|
40
|
+
attr: DMAttr[] = []
|
|
41
|
+
if (format === 'bili') {
|
|
42
|
+
if (array[0]) attr.push('Protect')
|
|
43
|
+
if (array[1]) attr.push('FromLive')
|
|
44
|
+
if (array[2]) attr.push('HighLike')
|
|
45
|
+
}
|
|
46
|
+
return attr
|
|
47
|
+
},
|
|
48
|
+
toBin(
|
|
49
|
+
attr: DMAttr[] = [],
|
|
50
|
+
/**
|
|
51
|
+
* 对于二进制格式的读取,应该分别读取各位,
|
|
52
|
+
* 但由于不知道B站及其它使用该参数程序的读取逻辑,
|
|
53
|
+
* 所以单独提供 bili 格式
|
|
54
|
+
*/
|
|
55
|
+
format?: platfrom,
|
|
56
|
+
) {
|
|
57
|
+
const bin = new SetBin(0)
|
|
58
|
+
if (format === 'bili') {
|
|
59
|
+
if (attr.includes('Protect')) bin.set1(0)
|
|
60
|
+
if (attr.includes('FromLive')) bin.set1(1)
|
|
61
|
+
if (attr.includes('HighLike')) bin.set1(2)
|
|
62
|
+
}
|
|
63
|
+
return bin.bin
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
// class DMAttr {
|
|
67
|
+
// constructor(
|
|
68
|
+
// /**
|
|
69
|
+
// * 保护
|
|
70
|
+
// */
|
|
71
|
+
// public protect: boolean = false,
|
|
72
|
+
// /**
|
|
73
|
+
// * 直播
|
|
74
|
+
// */
|
|
75
|
+
// public live: boolean = false,
|
|
76
|
+
// /**
|
|
77
|
+
// * 高赞
|
|
78
|
+
// */
|
|
79
|
+
// public highpraise: boolean = false,
|
|
80
|
+
// ) {}
|
|
81
|
+
// toBin(
|
|
82
|
+
// /**
|
|
83
|
+
// * 对于二进制格式的读取,应该分别读取各位,
|
|
84
|
+
// * 但由于不知道B站及其它使用该参数程序的读取逻辑,
|
|
85
|
+
// * 所以单独提供 bili 格式
|
|
86
|
+
// */
|
|
87
|
+
// format?: platfrom,
|
|
88
|
+
// ) {
|
|
89
|
+
// const bin = new SetBin(0)
|
|
90
|
+
// if (this.protect) bin.set1(0)
|
|
91
|
+
// if (this.live) bin.set1(1)
|
|
92
|
+
// if (this.highpraise) bin.set1(2)
|
|
93
|
+
// if (format === 'bili') return bin.bin
|
|
94
|
+
// return bin.bin
|
|
95
|
+
// }
|
|
96
|
+
// static fromBin(bin: number = 0, format?: platfrom) {
|
|
97
|
+
// const array = toBits(bin)
|
|
98
|
+
// if (format === 'bili') {
|
|
99
|
+
// return new DMAttr(array[0], array[1], array[2])
|
|
100
|
+
// } else {
|
|
101
|
+
// return new DMAttr(array[0], array[1], array[2])
|
|
102
|
+
// }
|
|
103
|
+
// }
|
|
104
|
+
// }
|
|
105
|
+
|
|
106
|
+
interface DMBili {
|
|
107
|
+
id: bigint // xml 7
|
|
108
|
+
progress: number // xml 0
|
|
109
|
+
mode: number // xml 1
|
|
110
|
+
fontsize: number // xml 2
|
|
111
|
+
color: number // xml 3
|
|
112
|
+
midHash: string // xml 6
|
|
113
|
+
/**
|
|
114
|
+
* 特殊类型解析:
|
|
115
|
+
* - [ohh] : /oh{2,}/gi
|
|
116
|
+
* - [前方高能]
|
|
117
|
+
* - [...] (JS数组) : 高级弹幕
|
|
118
|
+
*/
|
|
119
|
+
content: string // xml content
|
|
120
|
+
ctime: bigint // xml 4
|
|
121
|
+
pool: number // xml 5
|
|
122
|
+
weight?: number // xml 8
|
|
123
|
+
action?: string
|
|
124
|
+
idStr?: string
|
|
125
|
+
attr?: number
|
|
126
|
+
animation?: string
|
|
127
|
+
extra?: string
|
|
128
|
+
colorful?: number
|
|
129
|
+
type?: number
|
|
130
|
+
oid?: bigint
|
|
131
|
+
}
|
|
132
|
+
interface DMBiliCommand extends DM_JSON_BiliCommandGrpc {}
|
|
133
|
+
interface DMDplayer {
|
|
134
|
+
/**
|
|
135
|
+
* 进度(秒)
|
|
136
|
+
*/
|
|
137
|
+
progress: number
|
|
138
|
+
mode: number
|
|
139
|
+
color: number
|
|
140
|
+
midHash: string
|
|
141
|
+
content: string
|
|
142
|
+
}
|
|
143
|
+
interface DMArtplayer {
|
|
144
|
+
/**
|
|
145
|
+
* 进度(秒)
|
|
146
|
+
*/
|
|
147
|
+
progress: number
|
|
148
|
+
mode: number
|
|
149
|
+
color: number
|
|
150
|
+
content: string
|
|
151
|
+
border?: boolean
|
|
152
|
+
style?: object
|
|
153
|
+
}
|
|
154
|
+
interface DMDDplay {
|
|
155
|
+
cid: number
|
|
156
|
+
/**
|
|
157
|
+
* content
|
|
158
|
+
*/
|
|
159
|
+
m: string
|
|
160
|
+
/**
|
|
161
|
+
* p[0]
|
|
162
|
+
*/
|
|
163
|
+
progress: number
|
|
164
|
+
/**
|
|
165
|
+
* p[1]
|
|
166
|
+
*/
|
|
167
|
+
mode: number
|
|
168
|
+
/**
|
|
169
|
+
* p[2]
|
|
170
|
+
*/
|
|
171
|
+
color: number
|
|
172
|
+
/**
|
|
173
|
+
* p[3]
|
|
174
|
+
*/
|
|
175
|
+
uid: string
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface Extra {
|
|
179
|
+
artplayer?: ExtraArtplayer
|
|
180
|
+
bili?: ExtraBili
|
|
181
|
+
danuni?: ExtraDanUni
|
|
182
|
+
}
|
|
183
|
+
interface ExtraArtplayer {
|
|
184
|
+
style?: object
|
|
185
|
+
border?: boolean
|
|
186
|
+
}
|
|
187
|
+
interface ExtraBili {
|
|
188
|
+
mode: number //原弹幕类型
|
|
189
|
+
pool: number //原弹幕池
|
|
190
|
+
adv?: string
|
|
191
|
+
code?: string
|
|
192
|
+
bas?: string
|
|
193
|
+
command?: DMBiliCommand
|
|
194
|
+
}
|
|
195
|
+
export interface ExtraDanUni {
|
|
196
|
+
chapter?: {
|
|
197
|
+
// seg: {
|
|
198
|
+
// st: number //开始时刻
|
|
199
|
+
// } //起止时间(ms)
|
|
200
|
+
duration: number //持续时间
|
|
201
|
+
type: ExtraDanUniChapterType
|
|
202
|
+
// action: ExtraDanUniChapterAction
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export enum ExtraDanUniChapterType {
|
|
206
|
+
Chapter = 'ch', //其它片段(用于标记章节)
|
|
207
|
+
Review = 'rev', //回顾
|
|
208
|
+
OP = 'op', //片头
|
|
209
|
+
Intermission = 'int', //中场
|
|
210
|
+
ED = 'ed', //片尾
|
|
211
|
+
Preview = 'prvw', //预告
|
|
212
|
+
Cut = 'cut', //删减(删减版中提供删减说明,提供开始位置、长度)
|
|
213
|
+
Duplicates = 'dup', //补档(完整版中指明其它平台中删减位置)
|
|
214
|
+
AdBiz = 'biz', //商业广告
|
|
215
|
+
AdUnpaid = 'promo', //推广(无偿/公益)广告
|
|
216
|
+
}
|
|
217
|
+
export enum ExtraDanUniChapterAction {
|
|
218
|
+
Disabled = -1,
|
|
219
|
+
ShowOverlay,
|
|
220
|
+
ManualSkip,
|
|
221
|
+
AutoSkip,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export enum Modes {
|
|
225
|
+
Normal,
|
|
226
|
+
Bottom,
|
|
227
|
+
Top,
|
|
228
|
+
Reverse, //逆向弹幕
|
|
229
|
+
Ext, //需要读取extra的弹幕,用于兼容bili等复杂弹幕
|
|
230
|
+
}
|
|
231
|
+
export enum Pools {
|
|
232
|
+
Def, //默认池
|
|
233
|
+
Sub, //重要池,建议强制加载,含字幕、科普、空降等
|
|
234
|
+
Adv, //高级弹幕专用池,均需读取extra
|
|
235
|
+
Ix, //互动池
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export type ctime = string | number | bigint | Date
|
|
239
|
+
// enum ctimeFmt {
|
|
240
|
+
// bigintStr = 'bigintStr',
|
|
241
|
+
// dateObj = 'dateObj',
|
|
242
|
+
// dateStr = 'dateStr',
|
|
243
|
+
// }
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 所有 number/bigint 值设为0自动转换为默认
|
|
247
|
+
*/
|
|
248
|
+
export class UniDM {
|
|
249
|
+
/**
|
|
250
|
+
* 同步时确认位置的参数
|
|
251
|
+
*/
|
|
252
|
+
// syncAnchor = BigInt(Date.now())
|
|
253
|
+
constructor(
|
|
254
|
+
/**
|
|
255
|
+
* FCID
|
|
256
|
+
*/
|
|
257
|
+
public FCID: string,
|
|
258
|
+
/**
|
|
259
|
+
* 弹幕出现位置(单位ms)
|
|
260
|
+
*/
|
|
261
|
+
public progress: number = 0,
|
|
262
|
+
/**
|
|
263
|
+
* 类型 1 2 3:普通弹幕 4:底部弹幕 5:顶部弹幕 6:逆向弹幕 7:高级弹幕 8:代码弹幕 9:BAS弹幕(pool必须为2)
|
|
264
|
+
*/
|
|
265
|
+
public mode: Modes = Modes.Normal,
|
|
266
|
+
/**
|
|
267
|
+
* 字号
|
|
268
|
+
* @default 25
|
|
269
|
+
* - 18:小
|
|
270
|
+
* - 25:标准
|
|
271
|
+
* - 36:大
|
|
272
|
+
*/
|
|
273
|
+
public fontsize: number = 25,
|
|
274
|
+
/**
|
|
275
|
+
* 颜色
|
|
276
|
+
* @description 为DEC值(十进制RGB888值),默认白色
|
|
277
|
+
* @default 16777215
|
|
278
|
+
*/
|
|
279
|
+
public color: number = 16777215,
|
|
280
|
+
/**
|
|
281
|
+
* 发送者 senderID
|
|
282
|
+
*/
|
|
283
|
+
public senderID: string = ID.fromNull().toString(),
|
|
284
|
+
/**
|
|
285
|
+
* 正文
|
|
286
|
+
*/
|
|
287
|
+
public content: string = '',
|
|
288
|
+
/**
|
|
289
|
+
* 发送时间
|
|
290
|
+
*/
|
|
291
|
+
// public ctime: bigint = BigInt(Date.now()),
|
|
292
|
+
public ctime: Date = new Date(),
|
|
293
|
+
/**
|
|
294
|
+
* 权重 用于屏蔽等级 区间:[1,10]
|
|
295
|
+
* @description 参考B站,源弹幕有该参数则直接利用,
|
|
296
|
+
* 本实现默认取5,再经过ruleset匹配加减分数
|
|
297
|
+
* @description 特殊情况下接受值为0,即设置0需转换为默认权重(5)
|
|
298
|
+
*/
|
|
299
|
+
public weight: number = 5,
|
|
300
|
+
/**
|
|
301
|
+
* 弹幕池 0:普通池 1:字幕池 2:特殊池(代码/BAS弹幕) 3:互动池(互动弹幕中选择投票快速发送的弹幕)
|
|
302
|
+
*/
|
|
303
|
+
public pool: Pools = Pools.Def,
|
|
304
|
+
/**
|
|
305
|
+
* 弹幕属性位(bin求AND)
|
|
306
|
+
* bit0:保护 bit1:直播 bit2:高赞
|
|
307
|
+
*/
|
|
308
|
+
public attr: DMAttr[] = [],
|
|
309
|
+
/**
|
|
310
|
+
* 初始来源平台
|
|
311
|
+
* `danuni`与任意空值(可隐式转换为false的值)等价
|
|
312
|
+
*/
|
|
313
|
+
public platform?: platfrom | string,
|
|
314
|
+
/**
|
|
315
|
+
* Same Platform Multiple Origin
|
|
316
|
+
* @description 解决B站等同一番剧存在港澳台站、多语言配音(不同一CID)的问题,同时方便过滤
|
|
317
|
+
* @description 示例:
|
|
318
|
+
* - main: 主站
|
|
319
|
+
* - hm: 仅港澳
|
|
320
|
+
* - t: 仅台
|
|
321
|
+
* - hmt: 仅港澳台
|
|
322
|
+
* - lang:{ISO语言代号}: 多语言
|
|
323
|
+
*/
|
|
324
|
+
public SPMO?: string,
|
|
325
|
+
/**
|
|
326
|
+
* 弹幕原始数据(不推荐使用)
|
|
327
|
+
* @description 适用于无法解析的B站代码弹幕、Artplayer弹幕样式等
|
|
328
|
+
* @description 初步约定:
|
|
329
|
+
* - Artplayer: style不为空时,将其JSON.stringify()存入
|
|
330
|
+
*/
|
|
331
|
+
public extraStr?: string,
|
|
332
|
+
public DMID?: string,
|
|
333
|
+
) {
|
|
334
|
+
//TODO 引入class-validator
|
|
335
|
+
if (progress < 0) this.progress = 0
|
|
336
|
+
if (mode < Modes.Normal || mode > Modes.Ext) this.mode = Modes.Normal
|
|
337
|
+
if (fontsize <= 0) this.fontsize = 25
|
|
338
|
+
if (color <= 0) this.color = 16777215 //虽然不知道为0是否为可用值,但过为少见,利用其作为默认位
|
|
339
|
+
// if (ctime <= 0n) this.ctime = BigInt(Date.now())
|
|
340
|
+
if (weight <= 0 || weight > 10) this.weight = 5
|
|
341
|
+
if (pool < Pools.Def || pool > Pools.Ix) this.pool = Pools.Def
|
|
342
|
+
// if (attr < 0 || attr > 0b111) this.attr = 0
|
|
343
|
+
if (!DMID) DMID = this.toDMID()
|
|
344
|
+
}
|
|
345
|
+
get extra() {
|
|
346
|
+
const extra = JSON.parse(this.extraStr || '{}')
|
|
347
|
+
return (typeof extra === 'object' ? extra : {}) as Extra
|
|
348
|
+
}
|
|
349
|
+
get isFrom3rdPlatform() {
|
|
350
|
+
if (this.platform && platforms.includes(this.platform as platfrom))
|
|
351
|
+
return true
|
|
352
|
+
else return false
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* 弹幕id
|
|
356
|
+
* @description sha3-256(content+senderID+ctime)截取前8位
|
|
357
|
+
* @description 同一FCID下唯一
|
|
358
|
+
*/
|
|
359
|
+
toDMID() {
|
|
360
|
+
return createDMID(this.content, this.senderID, this.ctime)
|
|
361
|
+
}
|
|
362
|
+
downgradeAdvcancedDan() {
|
|
363
|
+
if (!this.extra) return this
|
|
364
|
+
else {
|
|
365
|
+
// TODO 分别对 mode7/8/9 command artplayer等正常播放器无法绘制的弹幕做降级处理
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
static transCtime(oriCtime: ctime) {
|
|
369
|
+
if (typeof oriCtime === 'number') return new Date(oriCtime)
|
|
370
|
+
else if (typeof oriCtime === 'bigint') return new Date(Number(oriCtime))
|
|
371
|
+
else if (typeof oriCtime === 'string') {
|
|
372
|
+
if (/^\d+n$/.test(oriCtime))
|
|
373
|
+
return new Date(Number(oriCtime.slice(0, -1)))
|
|
374
|
+
// else return Date.parse(oriCtime)
|
|
375
|
+
else return new Date(oriCtime)
|
|
376
|
+
}
|
|
377
|
+
// } else if (typeof oriCtime === 'object') return BigInt(oriCtime.getTime())
|
|
378
|
+
else return oriCtime // BigInt(Date.now())
|
|
379
|
+
}
|
|
380
|
+
// static reviveCtime(ctime: bigint, fmt: ctimeFmt = ctimeFmt.bigintStr) {
|
|
381
|
+
// if (fmt === ctimeFmt.dateObj || fmt === ctimeFmt.dateStr) {
|
|
382
|
+
// const date = new Date(Number(ctime))
|
|
383
|
+
// if (fmt === ctimeFmt.dateStr) return date.toString()
|
|
384
|
+
// else return date
|
|
385
|
+
// } else return ctime.toString() + 'n'
|
|
386
|
+
// }
|
|
387
|
+
static transMode(
|
|
388
|
+
oriMode: number,
|
|
389
|
+
fmt: 'bili' | 'dplayer' | 'artplayer' | 'ddplay',
|
|
390
|
+
): Modes {
|
|
391
|
+
let mode = Modes.Normal
|
|
392
|
+
switch (fmt) {
|
|
393
|
+
case 'bili':
|
|
394
|
+
switch (oriMode) {
|
|
395
|
+
case 4:
|
|
396
|
+
mode = Modes.Bottom
|
|
397
|
+
break
|
|
398
|
+
case 5:
|
|
399
|
+
mode = Modes.Top
|
|
400
|
+
break
|
|
401
|
+
case 6:
|
|
402
|
+
mode = Modes.Reverse
|
|
403
|
+
break
|
|
404
|
+
case 7:
|
|
405
|
+
mode = Modes.Ext
|
|
406
|
+
break
|
|
407
|
+
case 8:
|
|
408
|
+
mode = Modes.Ext
|
|
409
|
+
break
|
|
410
|
+
case 9:
|
|
411
|
+
mode = Modes.Ext
|
|
412
|
+
break
|
|
413
|
+
}
|
|
414
|
+
break
|
|
415
|
+
|
|
416
|
+
case 'dplayer':
|
|
417
|
+
if (oriMode === 1) mode = Modes.Top
|
|
418
|
+
else if (oriMode === 2) mode = Modes.Bottom
|
|
419
|
+
break
|
|
420
|
+
|
|
421
|
+
case 'artplayer':
|
|
422
|
+
if (oriMode === 1) mode = Modes.Top
|
|
423
|
+
else if (oriMode === 2) mode = Modes.Bottom
|
|
424
|
+
break
|
|
425
|
+
|
|
426
|
+
case 'ddplay':
|
|
427
|
+
// 弹幕模式:1-普通弹幕,4-底部弹幕,5-顶部弹幕
|
|
428
|
+
// 其适配为bili格式子集
|
|
429
|
+
mode = this.transMode(oriMode, 'bili')
|
|
430
|
+
break
|
|
431
|
+
|
|
432
|
+
default:
|
|
433
|
+
mode = Modes.Normal
|
|
434
|
+
break
|
|
435
|
+
}
|
|
436
|
+
return mode
|
|
437
|
+
}
|
|
438
|
+
static fromBili(args: DMBili, SPMO?: string, cid?: bigint) {
|
|
439
|
+
interface TExtra extends Extra {
|
|
440
|
+
bili: ExtraBili
|
|
441
|
+
}
|
|
442
|
+
if (args.oid && !cid) cid = args.oid
|
|
443
|
+
const FCID = ID.fromBili({ cid }),
|
|
444
|
+
senderID = ID.fromBili({ midHash: args.midHash })
|
|
445
|
+
let mode = Modes.Normal
|
|
446
|
+
const pool = args.pool, //暂时不做处理,兼容bili的pool格式
|
|
447
|
+
extra: TExtra = { bili: { mode: args.mode, pool: args.pool } }
|
|
448
|
+
//重复 transMode ,但此处有关于extra的额外处理
|
|
449
|
+
switch (args.mode) {
|
|
450
|
+
case 4:
|
|
451
|
+
mode = Modes.Bottom
|
|
452
|
+
break
|
|
453
|
+
case 5:
|
|
454
|
+
mode = Modes.Top
|
|
455
|
+
break
|
|
456
|
+
case 6:
|
|
457
|
+
mode = Modes.Reverse
|
|
458
|
+
break
|
|
459
|
+
case 7:
|
|
460
|
+
mode = Modes.Ext
|
|
461
|
+
extra.bili.adv = args.content
|
|
462
|
+
break
|
|
463
|
+
case 8:
|
|
464
|
+
mode = Modes.Ext
|
|
465
|
+
extra.bili.code = args.content
|
|
466
|
+
break
|
|
467
|
+
case 9:
|
|
468
|
+
mode = Modes.Ext
|
|
469
|
+
extra.bili.bas = args.content
|
|
470
|
+
break
|
|
471
|
+
|
|
472
|
+
default:
|
|
473
|
+
mode = Modes.Normal
|
|
474
|
+
break
|
|
475
|
+
}
|
|
476
|
+
// if (args.mode === 7) extra.bili.adv = args.content
|
|
477
|
+
// else if (args.mode === 8) extra.bili.code = args.content
|
|
478
|
+
// else if (args.mode === 9) extra.bili.bas = args.content
|
|
479
|
+
return new UniDM(
|
|
480
|
+
FCID.toString(),
|
|
481
|
+
args.progress,
|
|
482
|
+
mode,
|
|
483
|
+
args.fontsize,
|
|
484
|
+
args.color,
|
|
485
|
+
senderID.toString(),
|
|
486
|
+
args.content,
|
|
487
|
+
this.transCtime(args.ctime),
|
|
488
|
+
args.weight ? args.weight : pool === Pools.Ix ? 1 : 0,
|
|
489
|
+
pool,
|
|
490
|
+
DMAttrUtils.fromBin(args.attr, 'bili'),
|
|
491
|
+
domainPreset.bili,
|
|
492
|
+
SPMO,
|
|
493
|
+
// 需改进,7=>advanced 8=>code 9=>bas 互动=>command
|
|
494
|
+
// 同时塞进无法/无需直接解析的数据
|
|
495
|
+
// 另开一个解析器,为大部分播放器(无法解析该类dm)做文本类型降级处理
|
|
496
|
+
args.mode >= 7 ? JSON.stringify(extra, BigIntSerializer) : undefined,
|
|
497
|
+
)
|
|
498
|
+
}
|
|
499
|
+
static fromBiliCommand(args: DMBiliCommand, SPMO?: string, cid?: bigint) {
|
|
500
|
+
if (args.oid && !cid) cid = args.oid
|
|
501
|
+
const FCID = ID.fromBili({ cid }),
|
|
502
|
+
senderID = ID.fromBili({ mid: args.mid })
|
|
503
|
+
return new UniDM(
|
|
504
|
+
FCID.toString(),
|
|
505
|
+
args.progress,
|
|
506
|
+
Modes.Ext,
|
|
507
|
+
0,
|
|
508
|
+
0,
|
|
509
|
+
senderID.toString(),
|
|
510
|
+
args.content,
|
|
511
|
+
// BigInt(Date.parse(args.ctime + ' GMT+0800')), // 无视本地时区,按照B站的东8区计算时间
|
|
512
|
+
new Date(`${args.ctime} GMT+0800`), // 无视本地时区,按照B站的东8区计算时间
|
|
513
|
+
10,
|
|
514
|
+
Pools.Adv,
|
|
515
|
+
['Protect'],
|
|
516
|
+
domainPreset.bili,
|
|
517
|
+
SPMO,
|
|
518
|
+
JSON.stringify(
|
|
519
|
+
{
|
|
520
|
+
bili: {
|
|
521
|
+
command: args,
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
BigIntSerializer,
|
|
525
|
+
),
|
|
526
|
+
)
|
|
527
|
+
}
|
|
528
|
+
static fromDplayer(args: DMDplayer, playerID: string, domain: string) {
|
|
529
|
+
const FCID = ID.fromUnknown(playerID, domain),
|
|
530
|
+
senderID = ID.fromUnknown(args.midHash, domain)
|
|
531
|
+
return new UniDM(
|
|
532
|
+
FCID.toString(),
|
|
533
|
+
args.progress,
|
|
534
|
+
this.transMode(args.mode, 'dplayer'),
|
|
535
|
+
0,
|
|
536
|
+
args.color,
|
|
537
|
+
senderID.toString(),
|
|
538
|
+
args.content,
|
|
539
|
+
new Date(),
|
|
540
|
+
0,
|
|
541
|
+
0,
|
|
542
|
+
[],
|
|
543
|
+
domain,
|
|
544
|
+
)
|
|
545
|
+
}
|
|
546
|
+
toDplayer(): DMDplayer {
|
|
547
|
+
let mode = 0
|
|
548
|
+
if (this.mode === Modes.Top) mode = 1
|
|
549
|
+
else if (this.mode === Modes.Bottom) mode = 2
|
|
550
|
+
return {
|
|
551
|
+
mode,
|
|
552
|
+
progress: this.progress,
|
|
553
|
+
color: this.color,
|
|
554
|
+
midHash: this.senderID,
|
|
555
|
+
content: this.content,
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
static fromArtplayer(args: DMArtplayer, playerID: string, domain: string) {
|
|
559
|
+
const FCID = ID.fromUnknown(playerID, domain),
|
|
560
|
+
senderID = ID.fromUnknown('', domain)
|
|
561
|
+
let extra = args.border
|
|
562
|
+
? ({ artplayer: { border: args.border, style: {} } } as Extra)
|
|
563
|
+
: undefined
|
|
564
|
+
if (args.style) {
|
|
565
|
+
if (extra)
|
|
566
|
+
extra = {
|
|
567
|
+
...extra,
|
|
568
|
+
artplayer: { ...extra.artplayer, style: args.style },
|
|
569
|
+
}
|
|
570
|
+
else extra = { artplayer: { style: args.style } }
|
|
571
|
+
}
|
|
572
|
+
return new UniDM(
|
|
573
|
+
FCID.toString(),
|
|
574
|
+
args.progress,
|
|
575
|
+
this.transMode(args.mode, 'artplayer'),
|
|
576
|
+
0,
|
|
577
|
+
args.color,
|
|
578
|
+
senderID.toString(),
|
|
579
|
+
args.content,
|
|
580
|
+
new Date(),
|
|
581
|
+
0,
|
|
582
|
+
0,
|
|
583
|
+
[],
|
|
584
|
+
domain,
|
|
585
|
+
undefined,
|
|
586
|
+
JSON.stringify(extra),
|
|
587
|
+
)
|
|
588
|
+
}
|
|
589
|
+
toArtplayer(): DMArtplayer {
|
|
590
|
+
let mode = 0
|
|
591
|
+
if (this.mode === Modes.Top) mode = 1
|
|
592
|
+
else if (this.mode === Modes.Bottom) mode = 2
|
|
593
|
+
return {
|
|
594
|
+
progress: this.progress,
|
|
595
|
+
mode,
|
|
596
|
+
color: this.color,
|
|
597
|
+
content: this.content,
|
|
598
|
+
style: this.extra.artplayer?.style,
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
static fromDDplay(
|
|
602
|
+
args: DMDDplay,
|
|
603
|
+
episodeId: string,
|
|
604
|
+
domain = domainPreset.ddplay,
|
|
605
|
+
) {
|
|
606
|
+
const FCID = ID.fromUnknown(episodeId, domain)
|
|
607
|
+
return new UniDM(
|
|
608
|
+
FCID.toString(),
|
|
609
|
+
args.progress,
|
|
610
|
+
this.transMode(args.mode, 'ddplay'),
|
|
611
|
+
0,
|
|
612
|
+
args.color,
|
|
613
|
+
args.uid,
|
|
614
|
+
args.m,
|
|
615
|
+
new Date(),
|
|
616
|
+
0,
|
|
617
|
+
0,
|
|
618
|
+
[],
|
|
619
|
+
domain,
|
|
620
|
+
undefined,
|
|
621
|
+
undefined,
|
|
622
|
+
args.cid.toString(), //无需 new ID() 获取带suffix的ID
|
|
623
|
+
)
|
|
624
|
+
}
|
|
625
|
+
toDDplay(): DMDDplay {
|
|
626
|
+
let mode = 1
|
|
627
|
+
if (this.mode === Modes.Top) mode = 5
|
|
628
|
+
else if (this.mode === Modes.Bottom) mode = 4
|
|
629
|
+
return {
|
|
630
|
+
progress: this.progress,
|
|
631
|
+
mode,
|
|
632
|
+
color: this.color,
|
|
633
|
+
uid: this.senderID,
|
|
634
|
+
m: this.content,
|
|
635
|
+
cid: Number(this.DMID) || 1,
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|