@dan-uni/dan-any 0.7.8 → 0.8.5
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/dist/index.js +155 -94
- package/dist/index.umd.min.js +310 -10785
- package/dist/src/ass-gen/index.d.ts +3 -2
- package/dist/src/ass-gen/types.d.ts +24 -0
- package/dist/src/ass-gen/util/layout.d.ts +3 -3
- package/dist/src/index.d.ts +62 -17
- package/dist/src/utils/dm-gen.d.ts +6 -2
- package/package.json +4 -5
- package/rslib.config.ts +0 -1
- package/src/ass-gen/__tests__/canvas.test.ts +8 -4
- package/src/ass-gen/__tests__/generate.test.ts +10 -4
- package/src/ass-gen/index.ts +9 -3
- package/src/ass-gen/types.ts +25 -1
- package/src/ass-gen/util/layout.ts +12 -19
- package/src/index.test.ts +26 -11
- package/src/index.ts +105 -43
- package/src/proto/gen/bili/dm_pb.ts +1 -1
- package/src/proto/gen/danuni_pb.ts +1 -1
- package/src/utils/dm-gen.ts +26 -6
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import 'reflect-metadata/lite'
|
|
|
2
2
|
|
|
3
3
|
import { XMLBuilder, XMLParser } from 'fast-xml-parser'
|
|
4
4
|
import JSONbig from 'json-bigint'
|
|
5
|
-
import type { Options as AssGenOptions } from './ass-gen'
|
|
5
|
+
import type { Options as AssGenOptions, CanvasCtx } from './ass-gen'
|
|
6
6
|
import type { CommandDm as DM_JSON_BiliCommandGrpc } from './proto/gen/bili/dm_pb'
|
|
7
7
|
|
|
8
8
|
import { create, fromBinary, toBinary } from '@bufbuild/protobuf'
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
timestampNow,
|
|
13
13
|
} from '@bufbuild/protobuf/wkt'
|
|
14
14
|
|
|
15
|
+
import pkg from '../package.json'
|
|
15
16
|
import { generateASS, parseAssRawField } from './ass-gen'
|
|
16
17
|
import {
|
|
17
18
|
// DanmakuElem as DM_JSON_BiliGrpc,
|
|
@@ -31,6 +32,17 @@ const JSON = JSONbig({
|
|
|
31
32
|
useNativeBigInt: true,
|
|
32
33
|
})
|
|
33
34
|
|
|
35
|
+
const DanUniConvertTipTemplate: DanUniConvertTip = {
|
|
36
|
+
meassage: 'Converted by DanUni!',
|
|
37
|
+
version: `JS/TS ${pkg.name} (v${pkg.version})`,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface DanUniConvertTip {
|
|
41
|
+
meassage: string
|
|
42
|
+
version: string
|
|
43
|
+
data?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
export interface DM_XML_Bili {
|
|
35
47
|
i: {
|
|
36
48
|
chatserver: string
|
|
@@ -54,12 +66,14 @@ export interface DM_JSON_Dplayer {
|
|
|
54
66
|
data: [number, number, number, string, string][]
|
|
55
67
|
}
|
|
56
68
|
export interface DM_JSON_Artplayer {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
danmuku: {
|
|
70
|
+
text: string // 弹幕文本
|
|
71
|
+
time?: number // 弹幕时间,默认为当前播放器时间
|
|
72
|
+
mode?: number // 弹幕模式:0: 滚动 (默认),1: 顶部,2: 底部
|
|
73
|
+
color?: string // 弹幕颜色,默认为白色
|
|
74
|
+
border?: boolean // 弹幕是否有描边,默认为 false
|
|
75
|
+
style?: {} // 弹幕自定义样式,默认为空对象
|
|
76
|
+
}[]
|
|
63
77
|
}
|
|
64
78
|
export interface DM_JSON_DDPlay {
|
|
65
79
|
count: number | string
|
|
@@ -101,6 +115,12 @@ export class UniPool {
|
|
|
101
115
|
constructor(
|
|
102
116
|
public dans: UniDM[],
|
|
103
117
|
public options: Options = {},
|
|
118
|
+
private info = {
|
|
119
|
+
/**
|
|
120
|
+
* 是否从已被转换过的第三方格式弹幕再次转换而来
|
|
121
|
+
*/
|
|
122
|
+
fromConverted: false,
|
|
123
|
+
},
|
|
104
124
|
) {
|
|
105
125
|
if (options.dedupe !== false) options.dedupe = true
|
|
106
126
|
if (this.options.dedupe) this.dedupe()
|
|
@@ -232,11 +252,15 @@ export class UniPool {
|
|
|
232
252
|
*/
|
|
233
253
|
assign(dans: UniPool | UniDM | UniDM[]) {
|
|
234
254
|
if (dans instanceof UniPool) {
|
|
235
|
-
return new UniPool(
|
|
255
|
+
return new UniPool(
|
|
256
|
+
[...this.dans, ...dans.dans],
|
|
257
|
+
{ ...this.options, ...dans.options },
|
|
258
|
+
{ ...this.info, ...dans.info },
|
|
259
|
+
)
|
|
236
260
|
} else if (dans instanceof UniDM) {
|
|
237
|
-
return new UniPool([...this.dans, dans])
|
|
261
|
+
return new UniPool([...this.dans, dans], this.options, this.info)
|
|
238
262
|
} else if (Array.isArray(dans) && dans.every((d) => d instanceof UniDM)) {
|
|
239
|
-
return new UniPool([...this.dans, ...dans])
|
|
263
|
+
return new UniPool([...this.dans, ...dans], this.options, this.info)
|
|
240
264
|
} else return this
|
|
241
265
|
}
|
|
242
266
|
/**
|
|
@@ -248,7 +272,8 @@ export class UniPool {
|
|
|
248
272
|
return [...set].map((v) => {
|
|
249
273
|
return new UniPool(
|
|
250
274
|
this.dans.filter((d) => d[key] === v),
|
|
251
|
-
{ dedupe: false },
|
|
275
|
+
{ ...this.options, dedupe: false },
|
|
276
|
+
this.info,
|
|
252
277
|
)
|
|
253
278
|
})
|
|
254
279
|
}
|
|
@@ -366,7 +391,7 @@ export class UniPool {
|
|
|
366
391
|
}
|
|
367
392
|
}
|
|
368
393
|
})
|
|
369
|
-
return new UniPool(result)
|
|
394
|
+
return new UniPool(result, this.options, this.info)
|
|
370
395
|
}
|
|
371
396
|
minify() {
|
|
372
397
|
return this.dans.map((d) => d.minify())
|
|
@@ -377,8 +402,8 @@ export class UniPool {
|
|
|
377
402
|
return this.dans
|
|
378
403
|
case 'danuni.bin':
|
|
379
404
|
return this.toPb()
|
|
380
|
-
|
|
381
|
-
|
|
405
|
+
case 'bili.xml':
|
|
406
|
+
return this.toBiliXML()
|
|
382
407
|
// case 'bili.bin':
|
|
383
408
|
// return this.toBiliBin()
|
|
384
409
|
// case 'bili.cmd.bin':
|
|
@@ -389,8 +414,8 @@ export class UniPool {
|
|
|
389
414
|
return this.toArtplayer()
|
|
390
415
|
case 'ddplay.json':
|
|
391
416
|
return this.toDDplay()
|
|
392
|
-
case 'common.ass':
|
|
393
|
-
|
|
417
|
+
// case 'common.ass':
|
|
418
|
+
// return this.toASS()
|
|
394
419
|
default: {
|
|
395
420
|
const message = '(err) Unknown format or unsupported now!'
|
|
396
421
|
if (continue_on_error) return message
|
|
@@ -405,6 +430,7 @@ export class UniPool {
|
|
|
405
430
|
UniDM.create(
|
|
406
431
|
{
|
|
407
432
|
...d,
|
|
433
|
+
progress: d.progress / 1000,
|
|
408
434
|
mode: d.mode as number,
|
|
409
435
|
ctime: timestampDate(d.ctime || timestampNow()),
|
|
410
436
|
pool: d.pool as number,
|
|
@@ -427,7 +453,7 @@ export class UniPool {
|
|
|
427
453
|
return {
|
|
428
454
|
SOID: d.SOID,
|
|
429
455
|
DMID: d.DMID,
|
|
430
|
-
progress: d.progress,
|
|
456
|
+
progress: d.progress * 1000,
|
|
431
457
|
mode: d.mode as number,
|
|
432
458
|
fontsize: d.fontsize,
|
|
433
459
|
color: d.color,
|
|
@@ -446,8 +472,10 @@ export class UniPool {
|
|
|
446
472
|
}
|
|
447
473
|
static fromBiliXML(xml: string, options?: Options) {
|
|
448
474
|
const parser = new XMLParser({ ignoreAttributes: false }),
|
|
449
|
-
oriData: DM_XML_Bili = parser.parse(xml),
|
|
450
|
-
dans = oriData.i.d
|
|
475
|
+
oriData: DM_XML_Bili & { danuni?: DanUniConvertTip } = parser.parse(xml),
|
|
476
|
+
dans = oriData.i.d,
|
|
477
|
+
fromConverted = !!oriData.danuni,
|
|
478
|
+
cid = BigInt(oriData.i.chatid)
|
|
451
479
|
return new UniPool(
|
|
452
480
|
dans
|
|
453
481
|
.map((d) => {
|
|
@@ -466,15 +494,28 @@ export class UniPool {
|
|
|
466
494
|
id: BigInt(p_arr[7]),
|
|
467
495
|
weight: Number.parseInt(p_arr[8]),
|
|
468
496
|
},
|
|
469
|
-
|
|
497
|
+
cid,
|
|
470
498
|
options,
|
|
499
|
+
fromConverted ? oriData.danuni?.data : undefined,
|
|
471
500
|
)
|
|
472
501
|
})
|
|
473
502
|
.filter((d) => d !== null),
|
|
474
503
|
options,
|
|
504
|
+
{ fromConverted },
|
|
475
505
|
)
|
|
476
506
|
}
|
|
477
|
-
toBiliXML(
|
|
507
|
+
toBiliXML(options?: {
|
|
508
|
+
/**
|
|
509
|
+
* 当SOID非来源bili时,若此处指定则使用该值为cid,否则使用SOID
|
|
510
|
+
*/
|
|
511
|
+
cid?: bigint
|
|
512
|
+
/**
|
|
513
|
+
* 当仅含有来自bili的弹幕时,启用将保持发送者标识不含`@`
|
|
514
|
+
* @description
|
|
515
|
+
* bili的弹幕含midHash(crc),不启用该处使用senderID填充,启用则去除`@bili`部分,提高兼容性
|
|
516
|
+
*/
|
|
517
|
+
avoidSenderIDWithAt?: boolean
|
|
518
|
+
}): string {
|
|
478
519
|
const genCID = (id: string) => {
|
|
479
520
|
const UniID = ID.fromString(id)
|
|
480
521
|
if (UniID.domain === platform.PlatformVideoSource.Bilibili) {
|
|
@@ -485,7 +526,13 @@ export class UniPool {
|
|
|
485
526
|
|
|
486
527
|
if (cid) return cid
|
|
487
528
|
}
|
|
488
|
-
return
|
|
529
|
+
return !options?.cid || id
|
|
530
|
+
}
|
|
531
|
+
if (options?.avoidSenderIDWithAt) {
|
|
532
|
+
const ok = this.dans.every((d) =>
|
|
533
|
+
d.senderID.endsWith(`@${platform.PlatformVideoSource.Bilibili}`),
|
|
534
|
+
)
|
|
535
|
+
if (!ok) throw new Error('存在其他来源的senderID,请关闭该功能再试!')
|
|
489
536
|
}
|
|
490
537
|
const builder = new XMLBuilder({ ignoreAttributes: false })
|
|
491
538
|
return builder.build({
|
|
@@ -493,6 +540,7 @@ export class UniPool {
|
|
|
493
540
|
'@_version': '1.0',
|
|
494
541
|
'@_encoding': 'UTF-8',
|
|
495
542
|
},
|
|
543
|
+
danuni: { ...DanUniConvertTipTemplate, data: this.shared.SOID },
|
|
496
544
|
i: {
|
|
497
545
|
chatserver: 'chat.bilibili.com',
|
|
498
546
|
chatid: genCID(this.dans[0].SOID),
|
|
@@ -501,7 +549,7 @@ export class UniPool {
|
|
|
501
549
|
state: 0,
|
|
502
550
|
real_name: 0,
|
|
503
551
|
source: 'k-v',
|
|
504
|
-
d: this.dans.map((dan) => dan.toBiliXML()),
|
|
552
|
+
d: this.dans.map((dan) => dan.toBiliXML(options)),
|
|
505
553
|
},
|
|
506
554
|
})
|
|
507
555
|
}
|
|
@@ -533,7 +581,7 @@ export class UniPool {
|
|
|
533
581
|
)
|
|
534
582
|
}
|
|
535
583
|
static fromDplayer(
|
|
536
|
-
json: DM_JSON_Dplayer,
|
|
584
|
+
json: DM_JSON_Dplayer & { danuni?: DanUniConvertTip },
|
|
537
585
|
playerID: string,
|
|
538
586
|
domain = 'other',
|
|
539
587
|
options?: Options,
|
|
@@ -557,11 +605,13 @@ export class UniPool {
|
|
|
557
605
|
)
|
|
558
606
|
}),
|
|
559
607
|
options,
|
|
608
|
+
{ fromConverted: !!json.danuni },
|
|
560
609
|
)
|
|
561
610
|
}
|
|
562
|
-
toDplayer(): DM_JSON_Dplayer {
|
|
611
|
+
toDplayer(): DM_JSON_Dplayer & { danuni?: DanUniConvertTip } {
|
|
563
612
|
return {
|
|
564
613
|
code: 0,
|
|
614
|
+
danuni: DanUniConvertTipTemplate,
|
|
565
615
|
data: this.dans.map((dan) => {
|
|
566
616
|
const d = dan.toDplayer()
|
|
567
617
|
return [d.progress, d.mode, d.color, d.midHash, d.content]
|
|
@@ -569,13 +619,13 @@ export class UniPool {
|
|
|
569
619
|
}
|
|
570
620
|
}
|
|
571
621
|
static fromArtplayer(
|
|
572
|
-
json: DM_JSON_Artplayer
|
|
622
|
+
json: DM_JSON_Artplayer & { danuni?: DanUniConvertTip },
|
|
573
623
|
playerID: string,
|
|
574
624
|
domain = 'other',
|
|
575
625
|
options?: Options,
|
|
576
626
|
) {
|
|
577
627
|
return new UniPool(
|
|
578
|
-
json.map((d) => {
|
|
628
|
+
json.danmuku.map((d) => {
|
|
579
629
|
// let TYPE = 0
|
|
580
630
|
// if (d.mode === 1) TYPE = 5
|
|
581
631
|
// else if (d.mode === 2) TYPE = 4
|
|
@@ -593,23 +643,27 @@ export class UniPool {
|
|
|
593
643
|
)
|
|
594
644
|
}),
|
|
595
645
|
options,
|
|
646
|
+
{ fromConverted: !!json.danuni },
|
|
596
647
|
)
|
|
597
648
|
}
|
|
598
|
-
toArtplayer(): DM_JSON_Artplayer
|
|
599
|
-
return
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
649
|
+
toArtplayer(): DM_JSON_Artplayer & { danuni?: DanUniConvertTip } {
|
|
650
|
+
return {
|
|
651
|
+
danuni: DanUniConvertTipTemplate,
|
|
652
|
+
danmuku: this.dans.map((dan) => {
|
|
653
|
+
const d = dan.toArtplayer()
|
|
654
|
+
return {
|
|
655
|
+
text: d.content,
|
|
656
|
+
time: d.progress,
|
|
657
|
+
mode: d.mode as 0 | 1 | 2,
|
|
658
|
+
color: `#${d.color.toString(16).toUpperCase() || 'FFFFFF'}`,
|
|
659
|
+
border: d.border,
|
|
660
|
+
style: d.style,
|
|
661
|
+
}
|
|
662
|
+
}),
|
|
663
|
+
}
|
|
610
664
|
}
|
|
611
665
|
static fromDDPlay(
|
|
612
|
-
json: DM_JSON_DDPlay,
|
|
666
|
+
json: DM_JSON_DDPlay & { danuni?: DanUniConvertTip },
|
|
613
667
|
episodeId: string,
|
|
614
668
|
options?: Options,
|
|
615
669
|
) {
|
|
@@ -631,10 +685,12 @@ export class UniPool {
|
|
|
631
685
|
)
|
|
632
686
|
}),
|
|
633
687
|
options,
|
|
688
|
+
{ fromConverted: !!json.danuni },
|
|
634
689
|
)
|
|
635
690
|
}
|
|
636
|
-
toDDplay(): DM_JSON_DDPlay {
|
|
691
|
+
toDDplay(): DM_JSON_DDPlay & { danuni?: DanUniConvertTip } {
|
|
637
692
|
return {
|
|
693
|
+
danuni: DanUniConvertTipTemplate,
|
|
638
694
|
count: this.dans.length,
|
|
639
695
|
comments: this.dans.map((dan) => {
|
|
640
696
|
const d = dan.toDDplay()
|
|
@@ -649,9 +705,15 @@ export class UniPool {
|
|
|
649
705
|
static fromASS(ass: string) {
|
|
650
706
|
return parseAssRawField(ass)
|
|
651
707
|
}
|
|
652
|
-
|
|
708
|
+
/**
|
|
709
|
+
* 转换为ASS字幕格式的弹幕,需播放器支持多行ASS渲染
|
|
710
|
+
*/
|
|
711
|
+
toASS(
|
|
712
|
+
canvasCtx: CanvasCtx,
|
|
713
|
+
options: AssGenOptions = { substyle: {} },
|
|
714
|
+
): string {
|
|
653
715
|
const fn = this.shared.SOID
|
|
654
|
-
return generateASS(this, { filename: fn, title: fn, ...options })
|
|
716
|
+
return generateASS(this, { filename: fn, title: fn, ...options }, canvasCtx)
|
|
655
717
|
}
|
|
656
718
|
}
|
|
657
719
|
|
package/src/utils/dm-gen.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Expose, plainToInstance } from 'class-transformer'
|
|
|
2
2
|
import {
|
|
3
3
|
IsDate,
|
|
4
4
|
IsEmail,
|
|
5
|
+
isEmail,
|
|
5
6
|
IsEnum,
|
|
6
7
|
IsInt,
|
|
7
8
|
IsNotEmpty,
|
|
@@ -733,13 +734,22 @@ export class UniDM {
|
|
|
733
734
|
}
|
|
734
735
|
return mode
|
|
735
736
|
}
|
|
736
|
-
static fromBili(
|
|
737
|
+
static fromBili(
|
|
738
|
+
args: DMBili,
|
|
739
|
+
cid?: bigint,
|
|
740
|
+
options?: Options,
|
|
741
|
+
recSOID?: string,
|
|
742
|
+
) {
|
|
737
743
|
interface TExtra extends Extra {
|
|
738
744
|
bili: ExtraBili
|
|
739
745
|
}
|
|
740
746
|
if (args.oid && !cid) cid = args.oid
|
|
741
|
-
const SOID =
|
|
742
|
-
|
|
747
|
+
const SOID =
|
|
748
|
+
recSOID ||
|
|
749
|
+
`def_${PlatformVideoSource.Bilibili}+${ID.fromBili({ cid })}`,
|
|
750
|
+
senderID = isEmail(args.midHash, { require_tld: false })
|
|
751
|
+
? args.midHash
|
|
752
|
+
: ID.fromBili({ midHash: args.midHash })
|
|
743
753
|
let mode = Modes.Normal
|
|
744
754
|
const pool = args.pool, //暂时不做处理,兼容bili的pool格式
|
|
745
755
|
extra: TExtra = {
|
|
@@ -797,7 +807,13 @@ export class UniDM {
|
|
|
797
807
|
)
|
|
798
808
|
}
|
|
799
809
|
@Expose()
|
|
800
|
-
toBiliXML(options?: {
|
|
810
|
+
toBiliXML(options?: {
|
|
811
|
+
skipBiliCommand?: boolean
|
|
812
|
+
/**
|
|
813
|
+
* 见 ../index.ts UniPool.toBiliXML() 的 options,该option不宜手动调用,判断逻辑未封装
|
|
814
|
+
*/
|
|
815
|
+
avoidSenderIDWithAt?: boolean
|
|
816
|
+
}) {
|
|
801
817
|
if (options?.skipBiliCommand && this.extra.bili?.command) {
|
|
802
818
|
return null
|
|
803
819
|
}
|
|
@@ -846,7 +862,9 @@ export class UniDM {
|
|
|
846
862
|
this.color,
|
|
847
863
|
this.ctime.getTime() / 1000,
|
|
848
864
|
this.extra.bili?.pool || this.pool, // 目前pool与bili兼容
|
|
849
|
-
|
|
865
|
+
options?.avoidSenderIDWithAt
|
|
866
|
+
? this.senderID.replaceAll(`@${PlatformVideoSource.Bilibili}`, '')
|
|
867
|
+
: this.senderID,
|
|
850
868
|
this.extra.bili?.dmid || this.DMID || this.toDMID(),
|
|
851
869
|
this.weight,
|
|
852
870
|
].join(','),
|
|
@@ -1001,7 +1019,9 @@ export class UniDM {
|
|
|
1001
1019
|
color: this.color,
|
|
1002
1020
|
uid: this.senderID,
|
|
1003
1021
|
m: this.content,
|
|
1004
|
-
cid:
|
|
1022
|
+
cid: this.DMID
|
|
1023
|
+
? Number.parseInt(Buffer.from(this.DMID).toString('hex'), 16)
|
|
1024
|
+
: 0,
|
|
1005
1025
|
}
|
|
1006
1026
|
}
|
|
1007
1027
|
}
|