@dan-uni/dan-any 0.9.9 → 1.0.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/src/index.ts CHANGED
@@ -47,7 +47,7 @@ interface DanUniConvertTip {
47
47
  export interface DM_XML_Bili {
48
48
  i: {
49
49
  chatserver: string
50
- chatid: number
50
+ chatid: bigint
51
51
  mission: number
52
52
  maxlimit: number
53
53
  state: number
@@ -78,15 +78,15 @@ export interface DM_JSON_BiliUp {
78
78
  }
79
79
  result: {
80
80
  /** 弹幕 ID,int64 */
81
- id: number
81
+ id: bigint
82
82
  /** 弹幕 ID 字符串形式 */
83
83
  id_str: string
84
84
  /** 弹幕类型:1 表示视频弹幕(当前接口恒为 1) */
85
85
  type: number
86
- aid: number
86
+ aid: bigint
87
87
  bvid: string
88
- oid: number
89
- mid: number
88
+ oid: bigint
89
+ mid: bigint
90
90
  /** 发送者 mid 的 CRC 哈希(正常接口里用的是这个,保护隐私) */
91
91
  mid_hash: string
92
92
  /** 弹幕池 */
@@ -145,7 +145,7 @@ export interface DM_JSON_Artplayer {
145
145
  export interface DM_JSON_DDPlay {
146
146
  count: number | string
147
147
  comments: {
148
- cid: number
148
+ cid: bigint
149
149
  p: string
150
150
  m: string
151
151
  }[]
@@ -183,16 +183,17 @@ type statItems = Partial<
183
183
  | 'platform'
184
184
  >
185
185
  >
186
- interface Stat {
187
- val: statItems[keyof statItems]
188
- count: number
189
- }
186
+ type Stats<T extends keyof statItems> = Map<statItems[T], number>
190
187
 
191
188
  type UniPoolPipe = (that: UniPool) => Promise<UniPool>
192
189
  type UniPoolPipeSync = (that: UniPool) => UniPool
193
190
 
194
191
  export interface Options {
195
192
  dedupe?: boolean
193
+ /**
194
+ * @description
195
+ * 当为`false`时,关闭DMID生成; 当为正整数时,表示生成DMID的截取位数; 或可传入一个DMID生成器实例
196
+ */
196
197
  dmid?: boolean | number | UniIDTools.DMIDGenerator
197
198
  }
198
199
 
@@ -216,51 +217,109 @@ export class UniPool {
216
217
  pipeSync(fn: UniPoolPipeSync): UniPool {
217
218
  return fn(this)
218
219
  }
220
+ /**
221
+ * @deprecated 使用 `getShared` 代替
222
+ */
219
223
  get shared(): shareItems {
220
- const isShared = (key: keyof UniDMTools.UniDMObj) => {
221
- return this.dans.every((d) => d[key])
224
+ if (this.dans.length === 0) return {}
225
+ const keys: (keyof shareItems)[] = [
226
+ 'SOID',
227
+ 'senderID',
228
+ 'platform',
229
+ 'pool',
230
+ 'mode',
231
+ 'color',
232
+ ]
233
+ const result: shareItems = {} as shareItems
234
+ for (const key of keys) {
235
+ const sharedVal = this.getShared(key)
236
+ if (sharedVal !== undefined) {
237
+ result[key] = sharedVal as any
238
+ }
222
239
  }
223
- return {
224
- SOID: isShared('SOID') ? this.dans[0].SOID : undefined,
225
- senderID: isShared('senderID') ? this.dans[0].senderID : undefined,
226
- platform: isShared('platform') ? this.dans[0].platform : undefined,
227
- pool: isShared('pool') ? this.dans[0].pool : undefined,
228
- mode: isShared('mode') ? this.dans[0].mode : undefined,
229
- color: isShared('color') ? this.dans[0].color : undefined,
240
+ return result
241
+ }
242
+ getShared<K extends keyof shareItems>(key: K): shareItems[K] {
243
+ if (this.dans.length === 0) return undefined
244
+ const firstVal = this.dans[0][key]
245
+ for (let i = 1; i < this.dans.length; i++) {
246
+ if (this.dans[i][key] !== firstVal) {
247
+ return undefined
248
+ }
230
249
  }
250
+ return firstVal
231
251
  }
232
- getShared(key: keyof shareItems): shareItems[keyof shareItems] {
233
- const isShared = (key: keyof UniDMTools.UniDMObj) => {
234
- return this.dans.every((d) => d[key])
252
+ getStat<K extends keyof statItems>(key: K): Stats<K> {
253
+ const statMap = new Map<statItems[K], number>()
254
+ for (const dan of this.dans) {
255
+ const val = dan[key]
256
+ statMap.set(val, (statMap.get(val) || 0) + 1)
235
257
  }
236
- return isShared(key) ? this.dans[0][key] : undefined
237
- }
238
- getStat(key: keyof statItems): Stat[] {
239
- const default_stat: Stat[] = []
240
- const stats = this.dans.reduce((stat, dan) => {
241
- const valWithCount = stat.find((i) => i.val === dan[key])
242
- if (valWithCount) {
243
- valWithCount.count++
244
- } else {
245
- stat.push({ val: dan[key], count: 1 })
258
+ return statMap
259
+ }
260
+ getMost<K extends keyof statItems>(key: K) {
261
+ const stats = this.getStat(key)
262
+ if (stats.size === 0) return { val: undefined, count: 0 }
263
+ let mostVal: statItems[K] | undefined
264
+ let maxCount = 0
265
+ for (const [val, count] of stats.entries()) {
266
+ if (count > maxCount) {
267
+ maxCount = count
268
+ mostVal = val
246
269
  }
247
- return stat
248
- }, default_stat)
249
- return stats
250
- }
251
- getMost(key: keyof statItems) {
252
- return this.getStat(key).toSorted((a, b) => b.count - a.count)[0]
270
+ }
271
+ return { val: mostVal, count: maxCount }
253
272
  }
273
+ /**
274
+ * @deprecated 使用 `getMost` 代替
275
+ */
254
276
  get most() {
277
+ const keys: (keyof statItems)[] = [
278
+ 'mode',
279
+ 'fontsize',
280
+ 'color',
281
+ 'senderID',
282
+ 'content',
283
+ 'weight',
284
+ 'pool',
285
+ 'platform',
286
+ ]
287
+ const statMaps = new Map<
288
+ keyof statItems,
289
+ Map<statItems[keyof statItems], number>
290
+ >()
291
+ for (const dan of this.dans) {
292
+ for (const key of keys) {
293
+ if (!statMaps.has(key)) {
294
+ statMaps.set(key, new Map())
295
+ }
296
+ const statMap = statMaps.get(key)!
297
+ const val = dan[key]
298
+ statMap.set(val, (statMap.get(val) || 0) + 1)
299
+ }
300
+ }
301
+ const result: Record<string, any> = {}
302
+ for (const key of keys) {
303
+ const statMap = statMaps.get(key)!
304
+ let mostVal: statItems[keyof statItems] | undefined
305
+ let maxCount = 0
306
+ for (const [val, count] of statMap.entries()) {
307
+ if (count > maxCount) {
308
+ maxCount = count
309
+ mostVal = val
310
+ }
311
+ }
312
+ result[key] = mostVal
313
+ }
255
314
  return {
256
- mode: this.getMost('mode').val as UniDMTools.Modes,
257
- fontsize: this.getMost('fontsize').val as number,
258
- color: this.getMost('color').val as number,
259
- senderID: this.getMost('senderID').val as string,
260
- content: this.getMost('content').val as string,
261
- weight: this.getMost('weight').val as number,
262
- pool: this.getMost('pool').val as UniDMTools.Pools,
263
- platform: this.getMost('platform').val as string | undefined,
315
+ mode: result.mode as UniDMTools.Modes,
316
+ fontsize: result.fontsize as number,
317
+ color: result.color as number,
318
+ senderID: result.senderID as string,
319
+ content: result.content as string,
320
+ weight: result.weight as number,
321
+ pool: result.pool as UniDMTools.Pools,
322
+ platform: result.platform as string | undefined,
264
323
  }
265
324
  }
266
325
  static create(options?: Options) {
@@ -286,7 +345,7 @@ export class UniPool {
286
345
  * 按共通属性拆分弹幕库
287
346
  */
288
347
  split(key: keyof shareItems) {
289
- if (this.shared[key]) return [this]
348
+ if (this.getShared(key)) return [this]
290
349
  const set = new Set(this.dans.map((d) => d[key]))
291
350
  return [...set].map((v) => {
292
351
  return new UniPool(
@@ -300,6 +359,7 @@ export class UniPool {
300
359
  * 基于DMID的基本去重功能,用于解决该class下dans为array而非Set的问题
301
360
  */
302
361
  private dedupe() {
362
+ // 这里基本上没有性能瓶颈(大文件测试与AI优化下无明显区别)
303
363
  if (this.options.dmid !== false) {
304
364
  const map = new Map()
305
365
  this.dans.forEach((d) => map.set(d.DMID || d.toDMID(), d))
@@ -312,104 +372,77 @@ export class UniPool {
312
372
  * @param lifetime 查重时间区段,单位秒 (默认为 0,表示不查重)
313
373
  */
314
374
  merge(lifetime = 0) {
315
- if (!this.shared.SOID) {
375
+ if (!this.getShared('SOID')) {
316
376
  console.error(
317
377
  "本功能仅支持同弹幕库内使用,可先 .split('SOID') 在分别使用",
318
378
  )
319
379
  return this
320
380
  }
321
381
  if (lifetime <= 0) return this
322
- const mergeContext = this.dans.reduce<
323
- [
324
- UniDM[],
325
- Record<string, UniDM>,
326
- Record<string, UniDMTools.ExtraDanUniMerge>,
327
- ]
328
- >(
329
- ([result, cache, mergeObj], danmaku) => {
330
- const key = ['content', 'mode', 'pool', 'platform']
331
- .map((k) => danmaku[k as keyof UniDM])
332
- .join('|')
333
- const cached = cache[key]
334
- const lastAppearTime = cached?.progress || 0
335
- if (
336
- cached &&
337
- danmaku.progress - lastAppearTime <= lifetime &&
338
- danmaku.isSameAs(cached, { skipDanuniMerge: true })
339
- ) {
340
- const senders = mergeObj[key].senders
341
- senders.push(danmaku.senderID)
342
- const extra = danmaku.extra
343
- extra.danuni = extra.danuni || {}
344
- extra.danuni.merge = {
345
- count: senders.length,
346
- duration: Number.parseFloat(
347
- (danmaku.progress - cached.progress).toFixed(3),
348
- ),
349
- senders,
350
- taolu_count: senders.length,
351
- taolu_senders: senders,
352
- }
353
- danmaku.extraStr = JSON.stringify(extra)
354
- cache[key] = danmaku
355
- mergeObj[key] = extra.danuni.merge
356
- return [result, cache, mergeObj]
357
- } else {
358
- mergeObj[key] = {
359
- count: 1,
360
- duration: 0,
361
- senders: [danmaku.senderID],
362
- taolu_count: 1,
363
- taolu_senders: [danmaku.senderID],
364
- }
365
- cache[key] = danmaku
366
- // 初始化merge信息,包含第一个sender
367
- const extra = danmaku.extra
368
- extra.danuni = extra.danuni || {}
369
- extra.danuni.merge = mergeObj[key]
370
- danmaku.extraStr = JSON.stringify(extra)
371
- result.push(danmaku)
372
- return [result, cache, mergeObj]
382
+ const result: UniDM[] = []
383
+ const cache: Record<string, UniDM> = {}
384
+ const mergeObj: Record<string, UniDMTools.ExtraDanUniMerge> = {}
385
+ // 第一遍:合并弹幕
386
+ for (const danmaku of this.dans) {
387
+ const key = `${danmaku.content}|${danmaku.mode}|${danmaku.pool}|${danmaku.platform}`
388
+ const cached = cache[key]
389
+
390
+ if (
391
+ cached &&
392
+ danmaku.progress - cached.progress <= lifetime &&
393
+ danmaku.isSameAs(cached, { skipDanuniMerge: true })
394
+ ) {
395
+ // 更新已存在的弹幕
396
+ mergeObj[key].senders.push(danmaku.senderID)
397
+ mergeObj[key].count = mergeObj[key].senders.length
398
+ mergeObj[key].taolu_count = mergeObj[key].count
399
+ mergeObj[key].taolu_senders = mergeObj[key].senders
400
+ mergeObj[key].duration = Number.parseFloat(
401
+ (danmaku.progress - cached.progress).toFixed(3),
402
+ )
403
+ cache[key] = danmaku
404
+ } else {
405
+ // 新弹幕
406
+ mergeObj[key] = {
407
+ count: 1,
408
+ duration: 0,
409
+ senders: [danmaku.senderID],
410
+ taolu_count: 1,
411
+ taolu_senders: [danmaku.senderID],
373
412
  }
374
- },
375
- [[], {}, {}],
376
- )
377
- // 处理结果,删除senders<=1的merge字段
378
- const [result, _cache, mergeObj] = mergeContext
379
- result.forEach((danmaku, i) => {
380
- const key = ['content', 'mode', 'platform', 'pool']
381
- .map((k) => danmaku[k as keyof UniDM])
382
- .join('|')
383
- const extra = result[i].extra
413
+ cache[key] = danmaku
414
+ result.push(danmaku)
415
+ }
416
+ }
417
+ // 第二遍:更新 extraStr
418
+ for (const danmaku of result) {
419
+ const key = `${danmaku.content}|${danmaku.mode}|${danmaku.pool}|${danmaku.platform}`
384
420
  const mergeData = mergeObj[key]
385
- result[i].extraStr = JSON.stringify({
386
- ...extra,
387
- danuni: {
388
- ...extra.danuni,
389
- merge: mergeData,
390
- },
391
- } satisfies UniDMTools.Extra)
392
- if (mergeData?.count) {
393
- if (mergeData.count <= 1) {
394
- const updatedExtra = { ...extra }
395
- if (updatedExtra.danuni) {
396
- delete updatedExtra.danuni.merge
397
- if (Object.keys(updatedExtra.danuni).length === 0) {
398
- delete updatedExtra.danuni
399
- }
421
+ const extra = danmaku.extra
422
+ if (mergeData.count > 1) {
423
+ // 多个发送者:设置为机器人并添加保护标记
424
+ danmaku.senderID = 'merge[bot]@dan-any'
425
+ if (!danmaku.attr) {
426
+ danmaku.attr = [UniDMTools.DMAttr.Protect]
427
+ } else if (!danmaku.attr.includes(UniDMTools.DMAttr.Protect)) {
428
+ danmaku.attr.push(UniDMTools.DMAttr.Protect)
429
+ }
430
+
431
+ extra.danuni = extra.danuni || {}
432
+ extra.danuni.merge = mergeData
433
+ danmaku.extraStr = JSON.stringify(extra)
434
+ } else {
435
+ // 单个发送者:清理 merge 字段
436
+ if (extra.danuni?.merge) {
437
+ delete extra.danuni.merge
438
+ if (Object.keys(extra.danuni).length === 0) {
439
+ delete extra.danuni
400
440
  }
401
- result[i].extraStr =
402
- Object.keys(updatedExtra).length > 0
403
- ? JSON.stringify(updatedExtra)
404
- : undefined
405
- } else {
406
- result[i].senderID = 'merge[bot]@dan-any'
407
- result[i].attr
408
- ? result[i].attr.push(UniDMTools.DMAttr.Protect)
409
- : (result[i].attr = [UniDMTools.DMAttr.Protect])
410
441
  }
442
+ danmaku.extraStr =
443
+ Object.keys(extra).length > 0 ? JSON.stringify(extra) : undefined
411
444
  }
412
- })
445
+ }
413
446
  return new UniPool(result, this.options, this.info)
414
447
  }
415
448
  minify() {
@@ -447,6 +480,7 @@ export class UniPool {
447
480
  } else if (
448
481
  json.count &&
449
482
  json.comments &&
483
+ Array.isArray(json.comments) &&
450
484
  json.comments.every((d) => d.m)
451
485
  ) {
452
486
  return {
@@ -694,7 +728,7 @@ export class UniPool {
694
728
  state: 0,
695
729
  real_name: 0,
696
730
  source: 'k-v',
697
- danuni: { ...DanUniConvertTipTemplate, data: this.shared.SOID },
731
+ danuni: { ...DanUniConvertTipTemplate, data: this.getShared('SOID') },
698
732
  d: this.dans.map((dan) => dan.toBiliXML(options)),
699
733
  },
700
734
  })
@@ -896,7 +930,7 @@ export class UniPool {
896
930
  toASS(canvasCtx: CanvasCtx, options?: AssGenOptions): string {
897
931
  const defaultOptions: AssGenOptions = { substyle: {} }
898
932
  const finalOptions = options ?? defaultOptions
899
- const fn = this.shared.SOID
933
+ const fn = this.getShared('SOID')
900
934
  return generateASS(
901
935
  this,
902
936
  { filename: fn, title: fn, ...finalOptions },
@@ -1,4 +1,4 @@
1
- // @generated by protoc-gen-es v2.9.0 with parameter "target=ts"
1
+ // @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
2
2
  // @generated from file bili/dm.proto (package bilibili.community.service.dm.v1, syntax proto3)
3
3
  /* eslint-disable */
4
4
 
@@ -1,4 +1,4 @@
1
- // @generated by protoc-gen-es v2.9.0 with parameter "target=ts"
1
+ // @generated by protoc-gen-es v2.10.2 with parameter "target=ts"
2
2
  // @generated from file danuni.proto (package danuni.danmaku.v1, syntax proto3)
3
3
  /* eslint-disable */
4
4
 
@@ -12,7 +12,7 @@ import type { Message } from "@bufbuild/protobuf";
12
12
  * Describes the file danuni.proto.
13
13
  */
14
14
  export const file_danuni: GenFile = /*@__PURE__*/
15
- fileDesc("CgxkYW51bmkucHJvdG8SEWRhbnVuaS5kYW5tYWt1LnYxIjIKCmxpc3REYW5SZXESCgoCSUQYASABKAkSEAoDc2VnGAIgASgFSACIAQFCBgoEX3NlZyLCAgoHRGFubWFrdRIMCgRTT0lEGAEgASgJEgwKBERNSUQYAiABKAkSEAoIcHJvZ3Jlc3MYAyABKAUSJQoEbW9kZRgEIAEoDjIXLmRhbnVuaS5kYW5tYWt1LnYxLk1vZGUSEAoIZm9udHNpemUYBSABKAUSDQoFY29sb3IYBiABKAUSEAoIc2VuZGVySUQYByABKAkSDwoHY29udGVudBgIIAEoCRIpCgVjdGltZRgJIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDgoGd2VpZ2h0GAogASgFEiUKBHBvb2wYCyABKA4yFy5kYW51bmkuZGFubWFrdS52MS5Qb29sEgwKBGF0dHIYDCADKAkSEAoIcGxhdGZvcm0YDSABKAkSEgoFZXh0cmEYDiABKAlIAIgBAUIICgZfZXh0cmEiPAoMRGFubWFrdVJlcGx5EiwKCGRhbm1ha3VzGAEgAygLMhouZGFudW5pLmRhbm1ha3UudjEuRGFubWFrdSo9CgRNb2RlEgoKBk5vcm1hbBAAEgoKBkJvdHRvbRABEgcKA1RvcBACEgsKB1JldmVyc2UQAxIHCgNFeHQQBCopCgRQb29sEgcKA0RlZhAAEgcKA1N1YhABEgcKA0FkdhACEgYKAkl4EAMyXQoORGFubWFrdVNlcnZpY2USSwoHbGlzdERhbhIdLmRhbnVuaS5kYW5tYWt1LnYxLmxpc3REYW5SZXEaHy5kYW51bmkuZGFubWFrdS52MS5EYW5tYWt1UmVwbHkiAFAAYgZwcm90bzM", [file_google_protobuf_timestamp]);
15
+ fileDesc("CgxkYW51bmkucHJvdG8SEWRhbnVuaS5kYW5tYWt1LnYxIjIKCmxpc3REYW5SZXESCgoCSUQYASABKAkSEAoDc2VnGAIgASgFSACIAQFCBgoEX3NlZyLUAgoHRGFubWFrdRIMCgRTT0lEGAEgASgJEgwKBERNSUQYAiABKAkSEAoIcHJvZ3Jlc3MYAyABKAUSJQoEbW9kZRgEIAEoDjIXLmRhbnVuaS5kYW5tYWt1LnYxLk1vZGUSEAoIZm9udHNpemUYBSABKAUSDQoFY29sb3IYBiABKAUSEAoIc2VuZGVySUQYByABKAkSDwoHY29udGVudBgIIAEoCRIpCgVjdGltZRgJIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDgoGd2VpZ2h0GAogASgFEiUKBHBvb2wYCyABKA4yFy5kYW51bmkuZGFubWFrdS52MS5Qb29sEgwKBGF0dHIYDCADKAkSFQoIcGxhdGZvcm0YDSABKAlIAIgBARISCgVleHRyYRgOIAEoCUgBiAEBQgsKCV9wbGF0Zm9ybUIICgZfZXh0cmEiPAoMRGFubWFrdVJlcGx5EiwKCGRhbm1ha3VzGAEgAygLMhouZGFudW5pLmRhbm1ha3UudjEuRGFubWFrdSo9CgRNb2RlEgoKBk5vcm1hbBAAEgoKBkJvdHRvbRABEgcKA1RvcBACEgsKB1JldmVyc2UQAxIHCgNFeHQQBCopCgRQb29sEgcKA0RlZhAAEgcKA1N1YhABEgcKA0FkdhACEgYKAkl4EAMyXQoORGFubWFrdVNlcnZpY2USSwoHbGlzdERhbhIdLmRhbnVuaS5kYW5tYWt1LnYxLmxpc3REYW5SZXEaHy5kYW51bmkuZGFubWFrdS52MS5EYW5tYWt1UmVwbHkiAFAAYgZwcm90bzM", [file_google_protobuf_timestamp]);
16
16
 
17
17
  /**
18
18
  * @generated from message danuni.danmaku.v1.listDanReq
@@ -101,9 +101,9 @@ export type Danmaku = Message<"danuni.danmaku.v1.Danmaku"> & {
101
101
  attr: string[];
102
102
 
103
103
  /**
104
- * @generated from field: string platform = 13;
104
+ * @generated from field: optional string platform = 13;
105
105
  */
106
- platform: string;
106
+ platform?: string;
107
107
 
108
108
  /**
109
109
  * @generated from field: optional string extra = 14;
@@ -41,7 +41,7 @@ message Danmaku {
41
41
  int32 weight = 10;
42
42
  Pool pool = 11;
43
43
  repeated string attr = 12;
44
- string platform = 13;
44
+ optional string platform = 13;
45
45
  optional string extra = 14;
46
46
  }
47
47
 
@@ -88,8 +88,8 @@ describe('其它', () => {
88
88
  const c = pool.dans[1].isSameAs(pool.dans[3])
89
89
  console.info(a, b, c)
90
90
  expect(a).toBe(false)
91
- expect(b).toBe(true)
92
- expect(c).toBe(true)
91
+ expect(b).toBe(false)
92
+ expect(c).toBe(false)
93
93
  })
94
94
  it('比较(extra)', () => {
95
95
  const commonSample = {
@@ -160,7 +160,7 @@ interface DMBili {
160
160
  mode: number // xml 1
161
161
  fontsize: number // xml 2
162
162
  color: number // xml 3
163
- mid?: number // 仅创作中心源
163
+ mid?: bigint // 仅创作中心源
164
164
  midHash: string // xml 6
165
165
  /**
166
166
  * 特殊类型解析:
@@ -204,7 +204,7 @@ interface DMArtplayer {
204
204
  style?: object
205
205
  }
206
206
  interface DMDDplay {
207
- cid: number
207
+ cid: bigint
208
208
  /**
209
209
  * content
210
210
  */
@@ -241,7 +241,7 @@ interface ExtraBili {
241
241
  pool?: number //原弹幕池
242
242
  dmid?: bigint //原弹幕ID
243
243
  attr?: number //原弹幕属性
244
- mid?: number //发送者mid(仅创作中心源)
244
+ mid?: bigint //发送者mid(仅创作中心源)
245
245
  adv?: string
246
246
  code?: string
247
247
  bas?: string
@@ -562,6 +562,8 @@ export class UniDM {
562
562
  }
563
563
  @Expose()
564
564
  isSameAs(dan: UniDM, options?: { skipDanuniMerge?: boolean }): boolean {
565
+ // 引用相同直接返回
566
+ if (this === dan) return true
565
567
  // 不支持比较高级弹幕
566
568
  if (this.mode === Modes.Ext || dan.mode === Modes.Ext) return false
567
569
  // 合并过视为不同,防止存在合并完成弹幕后再次合并造成计数错误
@@ -570,6 +572,31 @@ export class UniDM {
570
572
  (this.extra.danuni?.merge || dan.extra.danuni?.merge)
571
573
  )
572
574
  return false
575
+ // 如果是bili弹幕,则以dmid判断是否相同
576
+ if (this.extra.bili?.dmid && dan.extra.bili?.dmid) {
577
+ // 当来源不同(标准源/创作中心源)时,视为不同弹幕
578
+ if (
579
+ (this.extra.bili.dmid && !dan.extra.bili.dmid) ||
580
+ (!this.extra.bili.dmid && dan.extra.bili.dmid)
581
+ )
582
+ return false
583
+ if (this.extra.bili.dmid === dan.extra.bili.dmid) return true
584
+ else return false
585
+ }
586
+ // 如果是artplayer弹幕,需额外比较extra项目
587
+ if (
588
+ (this.extra.artplayer && !dan.extra.artplayer) ||
589
+ (!this.extra.artplayer && dan.extra.artplayer)
590
+ )
591
+ return false
592
+ else if (
593
+ this.extra.artplayer &&
594
+ dan.extra.artplayer &&
595
+ (this.extra.artplayer.border !== dan.extra.artplayer.border ||
596
+ JSON.stringify(this.extra.artplayer.style) !==
597
+ JSON.stringify(dan.extra.artplayer.style))
598
+ )
599
+ return false
573
600
  const isSame = (k: keyof UniDMObj) => this[k] === dan[k]
574
601
  const checks = (
575
602
  [
@@ -586,25 +613,20 @@ export class UniDM {
586
613
  @Expose()
587
614
  minify() {
588
615
  type UObj = Partial<UniDMObj> & Pick<UniDMObj, 'SOID'>
589
- const def: UObj = UniDM.create()
590
- const dan: UObj = UniDM.create(this)
591
- const shouldKeep = (key: keyof UObj, value: UObj[keyof UObj]) => {
592
- if (key === 'SOID') return true
593
- if (value === undefined || value === null) return false
594
- if (value === def[key]) return false
595
- if (key === 'attr' && Array.isArray(value) && value.length === 0)
596
- return false
597
- if (key === 'extraStr' && value === '{}') return false
598
- return true
599
- }
600
- const result: UObj = { SOID: dan.SOID }
601
- for (const key of Object.keys(dan) as (keyof UObj)[]) {
602
- const value = dan[key]
603
- if (shouldKeep(key, value)) {
604
- if (key === 'SOID') continue
605
- Reflect.set(result, key, value)
606
- }
607
- }
616
+ const def = new UniDM()
617
+ const result: UObj = { SOID: this.SOID }
618
+ if (this.progress !== def.progress) result.progress = this.progress
619
+ if (this.mode !== def.mode) result.mode = this.mode
620
+ if (this.fontsize !== def.fontsize) result.fontsize = this.fontsize
621
+ if (this.color !== def.color) result.color = this.color
622
+ if (this.senderID !== def.senderID) result.senderID = this.senderID
623
+ if (this.content !== def.content) result.content = this.content
624
+ if (this.weight !== def.weight) result.weight = this.weight
625
+ if (this.pool !== def.pool) result.pool = this.pool
626
+ if (this.attr.length > 0) result.attr = this.attr
627
+ if (this.platform !== undefined) result.platform = this.platform
628
+ if (this.extraStr && this.extraStr !== '{}') result.extraStr = this.extraStr
629
+ if (this.DMID !== undefined) result.DMID = this.DMID
608
630
  return result
609
631
  }
610
632
  @Expose()
@@ -1045,8 +1067,8 @@ export class UniDM {
1045
1067
  uid: this.senderID,
1046
1068
  m: this.content,
1047
1069
  cid: this.DMID
1048
- ? Number.parseInt(Buffer.from(this.DMID).toString('hex'), 16)
1049
- : 0,
1070
+ ? BigInt(`0x${Buffer.from(this.DMID).toString('hex')}`)
1071
+ : 0n,
1050
1072
  }
1051
1073
  }
1052
1074
  }