@dan-uni/dan-any 0.8.7 → 0.9.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.
@@ -1,3 +1,4 @@
1
+ import type { Options as UniPoolOptions } from '..';
1
2
  import type { RawConfig } from './ass/raw';
2
3
  import type { CanvasCtx, SubtitleStyle } from './types';
3
4
  import { UniPool } from '..';
@@ -26,4 +27,4 @@ fs.writeFileSync(`${filename}.ass`, assText, 'utf-8')
26
27
  ```
27
28
  */
28
29
  export declare function generateASS(danmaku: UniPool, options: Options, canvasCtx: CanvasCtx): string;
29
- export declare function parseAssRawField(ass: string): UniPool;
30
+ export declare function parseAssRawField(ass: string, options?: UniPoolOptions): UniPool;
@@ -1,4 +1,5 @@
1
+ import type { Options as UniPoolOptions } from '../..';
1
2
  import type { Danmaku } from '../types';
2
3
  import { UniPool } from '../..';
3
4
  export declare function UniPool2DanmakuLists(UP: UniPool): Danmaku[];
4
- export declare function DanmakuList2UniPool(d: Danmaku[]): UniPool;
5
+ export declare function DanmakuList2UniPool(d: Danmaku[], options?: UniPoolOptions): UniPool;
@@ -50,11 +50,11 @@ export interface DM_JSON_DDPlay {
50
50
  m: string;
51
51
  }[];
52
52
  }
53
- export type DM_format = 'danuni.json' | 'danuni.bin' | 'danuni.pb.zst' | 'bili.xml' | 'bili.bin' | 'bili.cmd.bin' | 'dplayer.json' | 'artplayer.json' | 'ddplay.json' | 'common.ass';
53
+ export type DM_format = 'danuni.json' | 'danuni.pb.bin' | 'bili.xml' | 'bili.pb.bin' | 'bili.cmd.pb.bin' | 'dplayer.json' | 'artplayer.json' | 'ddplay.json' | 'common.ass';
54
54
  type shareItems = Partial<Pick<UniDMTools.UniDMObj, 'SOID' | 'senderID' | 'platform' | 'SOID' | 'pool' | 'mode' | 'color'>>;
55
55
  type UniPoolPipe = (that: UniPool) => Promise<UniPool>;
56
56
  type UniPoolPipeSync = (that: UniPool) => UniPool;
57
- interface Options {
57
+ export interface Options {
58
58
  dedupe?: boolean;
59
59
  dmid?: boolean | number | UniIDTools.DMIDGenerator;
60
60
  }
@@ -143,6 +143,10 @@ export declare class UniPool {
143
143
  */
144
144
  merge(lifetime?: number): UniPool;
145
145
  minify(): (Partial<UniDMTools.UniDMObj> & Pick<UniDMTools.UniDMObj, "SOID">)[];
146
+ static import(file: unknown, options?: Options): {
147
+ pool: UniPool;
148
+ fmt: DM_format;
149
+ };
146
150
  convert2(format: DM_format, continue_on_error?: boolean): string | Uint8Array<ArrayBufferLike> | UniDM[] | (DM_JSON_Dplayer & {
147
151
  danuni?: DanUniConvertTip;
148
152
  }) | (DM_JSON_Artplayer & {
@@ -191,7 +195,7 @@ export declare class UniPool {
191
195
  toDDplay(): DM_JSON_DDPlay & {
192
196
  danuni?: DanUniConvertTip;
193
197
  };
194
- static fromASS(ass: string): UniPool;
198
+ static fromASS(ass: string, options?: Options): UniPool;
195
199
  /**
196
200
  * 转换为ASS字幕格式的弹幕,需播放器支持多行ASS渲染
197
201
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dan-uni/dan-any",
3
- "version": "0.8.7",
3
+ "version": "0.9.0",
4
4
  "description": "A danmaku transformer lib, supporting danmaku from different platforms.",
5
5
  "keywords": [
6
6
  "bangumi",
@@ -1,4 +1,5 @@
1
1
  // import parse from './parse/bilibili'
2
+ import type { Options as UniPoolOptions } from '..'
2
3
  import type { RawConfig } from './ass/raw'
3
4
  import type { CanvasCtx, SubtitleStyle } from './types'
4
5
 
@@ -59,8 +60,11 @@ export function generateASS(
59
60
  return content
60
61
  }
61
62
 
62
- export function parseAssRawField(ass: string): UniPool {
63
+ export function parseAssRawField(
64
+ ass: string,
65
+ options?: UniPoolOptions,
66
+ ): UniPool {
63
67
  const raw = deRaw(ass)
64
68
  if (!raw) return UniPool.create()
65
- else return DanmakuList2UniPool(raw.list)
69
+ else return DanmakuList2UniPool(raw.list, options)
66
70
  }
@@ -1,3 +1,4 @@
1
+ import type { Options as UniPoolOptions } from '../..'
1
2
  import type { Danmaku, RGB } from '../types'
2
3
 
3
4
  import { UniPool } from '../..'
@@ -31,6 +32,12 @@ export function UniPool2DanmakuLists(UP: UniPool): Danmaku[] {
31
32
  } satisfies Danmaku
32
33
  })
33
34
  }
34
- export function DanmakuList2UniPool(d: Danmaku[]): UniPool {
35
- return new UniPool(d.map((d) => UniDM.create(d.extra)))
35
+ export function DanmakuList2UniPool(
36
+ d: Danmaku[],
37
+ options?: UniPoolOptions,
38
+ ): UniPool {
39
+ return new UniPool(
40
+ d.map((d) => UniDM.create(d.extra)),
41
+ options,
42
+ )
36
43
  }
package/src/index.test.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  //基于以下注释,根据vitest生成测试用例
2
2
  import { createCanvas } from 'canvas'
3
- import { describe, it } from 'vitest'
3
+ import { describe, expect, it } from 'vitest'
4
4
 
5
5
  import { UniPool } from './index'
6
6
 
@@ -35,6 +35,9 @@ describe('转化自', () => {
35
35
  console.info(pool)
36
36
  console.info(pool.toBiliXML())
37
37
  console.info(pool.toBiliXML({ avoidSenderIDWithAt: true }))
38
+ const imp = UniPool.import(xml)
39
+ expect(imp.fmt).toBe('bili.xml')
40
+ expect(imp.pool).toEqual(pool)
38
41
  })
39
42
  it('artplayer(json)', () => {
40
43
  const json = {
@@ -52,6 +55,9 @@ describe('转化自', () => {
52
55
  pool = UniPool.fromArtplayer(json, 'playerid-test', 'acfun')
53
56
  console.info(json)
54
57
  console.info(pool)
58
+ const imp = UniPool.import(json)
59
+ expect(imp.fmt).toBe('artplayer.json')
60
+ // expect(imp.pool).toEqual(pool)
55
61
  })
56
62
  it('ass[双向]', () => {
57
63
  const canvas = createCanvas(50, 50)
@@ -59,16 +65,25 @@ describe('转化自', () => {
59
65
  const ass = pool.toASS(canvas.getContext('2d'))
60
66
  console.info(ass)
61
67
  console.info(UniPool.fromASS(ass))
68
+ const imp = UniPool.import(ass)
69
+ expect(imp.fmt).toBe('common.ass')
70
+ expect(imp.pool).toEqual(pool)
62
71
  })
63
72
  it('pb[双向]', () => {
64
73
  const pool = UniPool.fromBiliXML(xml)
65
74
  const pb = pool.toPb()
66
75
  console.info(UniPool.fromPb(pb))
76
+ const imp = UniPool.import(pb)
77
+ expect(imp.fmt).toBe('danuni.pb.bin')
78
+ expect(imp.pool).toEqual(pool)
67
79
  })
68
80
  it('DDplay[双向]', () => {
69
81
  const pool = UniPool.fromBiliXML(xml)
70
82
  const ddplay = pool.toDDplay()
71
83
  console.info(UniPool.fromDDPlay(ddplay, '1'))
84
+ const imp = UniPool.import(ddplay)
85
+ expect(imp.fmt).toBe('ddplay.json')
86
+ // expect(imp.pool).toEqual(pool)
72
87
  })
73
88
  })
74
89
 
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import 'reflect-metadata/lite'
2
2
 
3
+ import { isJSON, isObject, isString } from 'class-validator'
3
4
  import { XMLBuilder, XMLParser } from 'fast-xml-parser'
4
5
  import JSONbig from 'json-bigint'
5
6
  import type { Options as AssGenOptions, CanvasCtx } from './ass-gen'
@@ -86,11 +87,10 @@ export interface DM_JSON_DDPlay {
86
87
 
87
88
  export type DM_format =
88
89
  | 'danuni.json'
89
- | 'danuni.bin'
90
- | 'danuni.pb.zst'
90
+ | 'danuni.pb.bin'
91
91
  | 'bili.xml'
92
- | 'bili.bin'
93
- | 'bili.cmd.bin'
92
+ | 'bili.pb.bin'
93
+ | 'bili.cmd.pb.bin'
94
94
  | 'dplayer.json'
95
95
  | 'artplayer.json'
96
96
  | 'ddplay.json'
@@ -106,7 +106,7 @@ type shareItems = Partial<
106
106
  type UniPoolPipe = (that: UniPool) => Promise<UniPool>
107
107
  type UniPoolPipeSync = (that: UniPool) => UniPool
108
108
 
109
- interface Options {
109
+ export interface Options {
110
110
  dedupe?: boolean
111
111
  dmid?: boolean | number | UniIDTools.DMIDGenerator
112
112
  }
@@ -396,11 +396,111 @@ export class UniPool {
396
396
  minify() {
397
397
  return this.dans.map((d) => d.minify())
398
398
  }
399
+ static import(
400
+ file: unknown,
401
+ options?: Options,
402
+ ): { pool: UniPool; fmt: DM_format } {
403
+ const err = '无法识别该文件,请手动指定格式!'
404
+ const parseJSON = (
405
+ json: DM_JSON_Artplayer &
406
+ DM_JSON_DDPlay &
407
+ DM_JSON_Dplayer & { danuni?: DanUniConvertTip },
408
+ ): { pool: UniPool; fmt: DM_format } | undefined => {
409
+ try {
410
+ if (Array.isArray(json) && json.every((d) => d.SOID)) {
411
+ return { pool: new UniPool(json, options), fmt: 'danuni.json' }
412
+ } else if (json.danmuku && json.danmuku.every((d) => d.text)) {
413
+ return {
414
+ pool: this.fromArtplayer(
415
+ json,
416
+ json.danuni?.data ?? '',
417
+ undefined,
418
+ options,
419
+ ),
420
+ fmt: 'artplayer.json',
421
+ }
422
+ } else if (
423
+ json.count &&
424
+ json.comments &&
425
+ json.comments.every((d) => d.m)
426
+ ) {
427
+ return {
428
+ pool: this.fromDDPlay(json, json.danuni?.data ?? '', options),
429
+ fmt: 'ddplay.json',
430
+ }
431
+ } else if (
432
+ json.code &&
433
+ json.code == 0 &&
434
+ json.data &&
435
+ json.data.every((d) => Array.isArray(d))
436
+ ) {
437
+ return {
438
+ pool: this.fromDplayer(
439
+ json,
440
+ json.danuni?.data ?? '',
441
+ undefined,
442
+ options,
443
+ ),
444
+ fmt: 'dplayer.json',
445
+ }
446
+ }
447
+ } catch {}
448
+ }
449
+ const parseStr = (
450
+ file: string,
451
+ ): { pool: UniPool; fmt: DM_format } | undefined => {
452
+ try {
453
+ if (isJSON(file)) {
454
+ const json = JSON.parse(file)
455
+ return parseJSON(json)
456
+ }
457
+ } catch {}
458
+ try {
459
+ const xmlParser = new XMLParser({ ignoreAttributes: false })
460
+ const xml = xmlParser.parse(file)
461
+ if (xml?.i?.d)
462
+ return { pool: this.fromBiliXML(file, options), fmt: 'bili.xml' }
463
+ } catch {}
464
+ try {
465
+ return { pool: this.fromASS(file, options), fmt: 'common.ass' }
466
+ } catch {}
467
+ }
468
+ if (isObject(file)) {
469
+ if (file instanceof ArrayBuffer || file instanceof Uint8Array) {
470
+ try {
471
+ const fileStr = new TextDecoder().decode(file)
472
+ const prStr = parseStr(fileStr)
473
+ if (!prStr) throw new Error(`${err}(定位: bin->string)`)
474
+ } catch {}
475
+ try {
476
+ return { pool: this.fromPb(file), fmt: 'danuni.pb.bin' }
477
+ } catch {}
478
+ try {
479
+ return { pool: this.fromBiliGrpc(file), fmt: 'bili.pb.bin' }
480
+ } catch {}
481
+ try {
482
+ return {
483
+ pool: this.fromBiliCommandGrpc(file),
484
+ fmt: 'bili.cmd.pb.bin',
485
+ }
486
+ } catch {}
487
+ } else {
488
+ const prJSON = parseJSON(file as any)
489
+ if (!prJSON) throw new Error(`${err}(定位: json)`)
490
+ return prJSON
491
+ }
492
+ } else if (isString(file)) {
493
+ const prStr = parseStr(file)
494
+ if (!prStr) throw new Error(`${err}(定位: string)`)
495
+ return prStr
496
+ }
497
+ throw new Error(err)
498
+ }
399
499
  convert2(format: DM_format, continue_on_error = false) {
400
500
  switch (format) {
401
501
  case 'danuni.json':
402
502
  return this.dans
403
- case 'danuni.bin':
503
+ case 'danuni.pb.bin':
404
504
  return this.toPb()
405
505
  case 'bili.xml':
406
506
  return this.toBiliXML()
@@ -435,6 +535,8 @@ export class UniPool {
435
535
  ctime: timestampDate(d.ctime || timestampNow()),
436
536
  pool: d.pool as number,
437
537
  attr: d.attr as UniDMTools.DMAttr[],
538
+ extra: undefined,
539
+ extraStr: d.extra,
438
540
  },
439
541
  options,
440
542
  ),
@@ -588,9 +690,6 @@ export class UniPool {
588
690
  ) {
589
691
  return new UniPool(
590
692
  json.data.map((d) => {
591
- // let TYPE = 0
592
- // if (d[1] === 1) TYPE = 5
593
- // else if (d[1] === 2) TYPE = 4
594
693
  return UniDM.fromDplayer(
595
694
  {
596
695
  content: d[4],
@@ -611,7 +710,10 @@ export class UniPool {
611
710
  toDplayer(): DM_JSON_Dplayer & { danuni?: DanUniConvertTip } {
612
711
  return {
613
712
  code: 0,
614
- danuni: DanUniConvertTipTemplate,
713
+ danuni: {
714
+ ...DanUniConvertTipTemplate,
715
+ data: this.dans[0].SOID.split('@')[0],
716
+ },
615
717
  data: this.dans.map((dan) => {
616
718
  const d = dan.toDplayer()
617
719
  return [d.progress, d.mode, d.color, d.midHash, d.content]
@@ -626,9 +728,6 @@ export class UniPool {
626
728
  ) {
627
729
  return new UniPool(
628
730
  json.danmuku.map((d) => {
629
- // let TYPE = 0
630
- // if (d.mode === 1) TYPE = 5
631
- // else if (d.mode === 2) TYPE = 4
632
731
  return UniDM.fromArtplayer(
633
732
  {
634
733
  content: d.text,
@@ -648,7 +747,10 @@ export class UniPool {
648
747
  }
649
748
  toArtplayer(): DM_JSON_Artplayer & { danuni?: DanUniConvertTip } {
650
749
  return {
651
- danuni: DanUniConvertTipTemplate,
750
+ danuni: {
751
+ ...DanUniConvertTipTemplate,
752
+ data: this.dans[0].SOID.split('@')[0],
753
+ },
652
754
  danmuku: this.dans.map((dan) => {
653
755
  const d = dan.toArtplayer()
654
756
  return {
@@ -689,8 +791,12 @@ export class UniPool {
689
791
  )
690
792
  }
691
793
  toDDplay(): DM_JSON_DDPlay & { danuni?: DanUniConvertTip } {
794
+ const episodeId = this.dans[0].SOID.split('@')[0].replaceAll(
795
+ `def_${platform.PlatformDanmakuOnlySource.DanDanPlay}+`,
796
+ '',
797
+ )
692
798
  return {
693
- danuni: DanUniConvertTipTemplate,
799
+ danuni: { ...DanUniConvertTipTemplate, data: episodeId },
694
800
  count: this.dans.length,
695
801
  comments: this.dans.map((dan) => {
696
802
  const d = dan.toDDplay()
@@ -702,8 +808,8 @@ export class UniPool {
702
808
  }),
703
809
  }
704
810
  }
705
- static fromASS(ass: string) {
706
- return parseAssRawField(ass)
811
+ static fromASS(ass: string, options?: Options) {
812
+ return parseAssRawField(ass, options)
707
813
  }
708
814
  /**
709
815
  * 转换为ASS字幕格式的弹幕,需播放器支持多行ASS渲染
@@ -471,6 +471,7 @@ export class UniDM {
471
471
  this.options.dmid = createDMID
472
472
 
473
473
  this.content = String(this.content)
474
+ this.ctime = UniDM.transCtime(this.ctime)
474
475
 
475
476
  if (!this.SOID) this.SOID = def.SOID
476
477
  if (!this.progress) this.progress = def.progress