@dan-uni/dan-any 0.9.9 → 1.0.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.
@@ -119,14 +119,15 @@ export interface DM_JSON_DDPlay {
119
119
  export type DM_format = 'danuni.json' | 'danuni.pb.bin' | 'bili.xml' | 'bili.pb.bin' | 'bili.cmd.pb.bin' | 'bili.up.json' | 'dplayer.json' | 'artplayer.json' | 'ddplay.json' | 'common.ass';
120
120
  type shareItems = Partial<Pick<UniDMTools.UniDMObj, 'SOID' | 'senderID' | 'platform' | 'SOID' | 'pool' | 'mode' | 'color'>>;
121
121
  type statItems = Partial<Pick<UniDMTools.UniDMObj, 'SOID' | 'mode' | 'fontsize' | 'color' | 'senderID' | 'content' | 'weight' | 'pool' | 'platform'>>;
122
- interface Stat {
123
- val: statItems[keyof statItems];
124
- count: number;
125
- }
122
+ type Stats<T extends keyof statItems> = Map<statItems[T], number>;
126
123
  type UniPoolPipe = (that: UniPool) => Promise<UniPool>;
127
124
  type UniPoolPipeSync = (that: UniPool) => UniPool;
128
125
  export interface Options {
129
126
  dedupe?: boolean;
127
+ /**
128
+ * @description
129
+ * 当为`false`时,关闭DMID生成; 当为正整数时,表示生成DMID的截取位数; 或可传入一个DMID生成器实例
130
+ */
130
131
  dmid?: boolean | number | UniIDTools.DMIDGenerator;
131
132
  }
132
133
  export declare class UniPool {
@@ -146,10 +147,19 @@ export declare class UniPool {
146
147
  });
147
148
  pipe(fn: UniPoolPipe): Promise<UniPool>;
148
149
  pipeSync(fn: UniPoolPipeSync): UniPool;
150
+ /**
151
+ * @deprecated 使用 `getShared` 代替
152
+ */
149
153
  get shared(): shareItems;
150
- getShared(key: keyof shareItems): shareItems[keyof shareItems];
151
- getStat(key: keyof statItems): Stat[];
152
- getMost(key: keyof statItems): Stat;
154
+ getShared<K extends keyof shareItems>(key: K): shareItems[K];
155
+ getStat<K extends keyof statItems>(key: K): Stats<K>;
156
+ getMost<K extends keyof statItems>(key: K): {
157
+ val: Partial<Pick<UniDMTools.UniDMObj, "SOID" | "mode" | "fontsize" | "color" | "senderID" | "content" | "weight" | "pool" | "platform">>[K] | undefined;
158
+ count: number;
159
+ };
160
+ /**
161
+ * @deprecated 使用 `getMost` 代替
162
+ */
153
163
  get most(): {
154
164
  mode: UniDMTools.Modes;
155
165
  fontsize: number;
@@ -76,9 +76,9 @@ export type Danmaku = Message<"danuni.danmaku.v1.Danmaku"> & {
76
76
  */
77
77
  attr: string[];
78
78
  /**
79
- * @generated from field: string platform = 13;
79
+ * @generated from field: optional string platform = 13;
80
80
  */
81
- platform: string;
81
+ platform?: string;
82
82
  /**
83
83
  * @generated from field: optional string extra = 14;
84
84
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dan-uni/dan-any",
3
- "version": "0.9.9",
3
+ "version": "1.0.0",
4
4
  "description": "A danmaku transformer lib, supporting danmaku from different platforms.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -32,11 +32,11 @@
32
32
  "buf": "buf generate"
33
33
  },
34
34
  "dependencies": {
35
- "@bufbuild/protobuf": "^2.9.0",
35
+ "@bufbuild/protobuf": "^2.10.1",
36
36
  "base16384": "^1.0.0",
37
37
  "class-transformer": "^0.5.1",
38
38
  "class-validator": "^0.14.2",
39
- "fast-xml-parser": "^5.3.0",
39
+ "fast-xml-parser": "^5.3.2",
40
40
  "fs-extra": "^11.3.2",
41
41
  "hh-mm-ss": "^1.2.0",
42
42
  "json-bigint": "^1.0.0",
@@ -44,8 +44,8 @@
44
44
  "reflect-metadata": "^0.2.2"
45
45
  },
46
46
  "devDependencies": {
47
- "@bufbuild/buf": "^1.58.0",
48
- "@bufbuild/protoc-gen-es": "^2.9.0",
47
+ "@bufbuild/buf": "^1.60.0",
48
+ "@bufbuild/protoc-gen-es": "^2.10.1",
49
49
  "@types/fs-extra": "^11.0.4",
50
50
  "@types/hh-mm-ss": "^1.2.3",
51
51
  "@types/json-bigint": "^1.0.4",
@@ -34,11 +34,11 @@ const scrollCommand = ({
34
34
  start: number
35
35
  end: number
36
36
  top: number
37
- }) => `\\move(${start},${top},${end},${top})`
37
+ }) => String.raw`\move(${start},${top},${end},${top})`
38
38
  const fixCommand = ({ top, left }: { top: number; left: number }) =>
39
- `\\an8\\pos(${left},${top})`
40
- const colorCommand = (color: RGB) => `\\c${formatColor(color)}`
41
- const borderColorCommand = (color: RGB) => `\\3c${formatColor(color)}`
39
+ String.raw`\an8\pos(${left},${top})`
40
+ const colorCommand = (color: RGB) => String.raw`\c${formatColor(color)}`
41
+ const borderColorCommand = (color: RGB) => String.raw`\3c${formatColor(color)}`
42
42
 
43
43
  export const dialogue = (
44
44
  danmaku: {
package/src/index.ts CHANGED
@@ -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.1 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.1 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
 
@@ -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
  // 合并过视为不同,防止存在合并完成弹幕后再次合并造成计数错误
@@ -586,25 +588,20 @@ export class UniDM {
586
588
  @Expose()
587
589
  minify() {
588
590
  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
- }
591
+ const def = new UniDM()
592
+ const result: UObj = { SOID: this.SOID }
593
+ if (this.progress !== def.progress) result.progress = this.progress
594
+ if (this.mode !== def.mode) result.mode = this.mode
595
+ if (this.fontsize !== def.fontsize) result.fontsize = this.fontsize
596
+ if (this.color !== def.color) result.color = this.color
597
+ if (this.senderID !== def.senderID) result.senderID = this.senderID
598
+ if (this.content !== def.content) result.content = this.content
599
+ if (this.weight !== def.weight) result.weight = this.weight
600
+ if (this.pool !== def.pool) result.pool = this.pool
601
+ if (this.attr.length > 0) result.attr = this.attr
602
+ if (this.platform !== undefined) result.platform = this.platform
603
+ if (this.extraStr && this.extraStr !== '{}') result.extraStr = this.extraStr
604
+ if (this.DMID !== undefined) result.DMID = this.DMID
608
605
  return result
609
606
  }
610
607
  @Expose()