@dan-uni/dan-any 0.7.8 → 0.8.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.
@@ -1,6 +1,7 @@
1
1
  import type { RawConfig } from './ass/raw';
2
- import type { SubtitleStyle } from './types';
2
+ import type { CanvasCtx, SubtitleStyle } from './types';
3
3
  import { UniPool } from '..';
4
+ export { CanvasCtx };
4
5
  export type Options = {
5
6
  filename?: string;
6
7
  title?: string;
@@ -24,5 +25,5 @@ const assText = generateASS(xmlText, { filename, title: 'Quick Example' })
24
25
  fs.writeFileSync(`${filename}.ass`, assText, 'utf-8')
25
26
  ```
26
27
  */
27
- export declare function generateASS(danmaku: UniPool, options: Options): string;
28
+ export declare function generateASS(danmaku: UniPool, options: Options, canvasCtx: CanvasCtx): string;
28
29
  export declare function parseAssRawField(ass: string): UniPool;
@@ -1,4 +1,28 @@
1
+ import type { CanvasRenderingContext2D as NodeCRCtx2D } from 'canvas';
1
2
  import type { UniDM } from '../utils/dm-gen';
3
+ /**
4
+ * 请根据您的使用环境提供一个 50x50 的 2D Canvas 上下文
5
+ * @example
6
+ * // Node.js + canvas
7
+ * import { createCanvas } from 'canvas'
8
+ * const canvas = createCanvas(50, 50)
9
+ * const ctx = canvas.getContext('2d')
10
+ * @example
11
+ * // Node.js + Fabric.js
12
+ * import { StaticCanvas } from 'fabric/node'
13
+ * const ctx = new StaticCanvas(null, { width: 50, height: 50 }).getContext()
14
+ * @example
15
+ * // Browser + Native Canvas
16
+ * const canvas = document.createElement('canvas')
17
+ * canvas.width = 50
18
+ * canvas.height = 50
19
+ * const ctx = canvas.getContext('2d')
20
+ * @example
21
+ * // Browser + Fabric.js
22
+ * import { Canvas } from 'fabric'
23
+ * const ctx = new Canvas('canvas', { width: 50, height: 50 }).getContext()
24
+ */
25
+ export type CanvasCtx = NodeCRCtx2D | CanvasRenderingContext2D;
2
26
  export interface Context {
3
27
  filename: string;
4
28
  title: string;
@@ -1,4 +1,4 @@
1
1
  import type { UniPool } from '../..';
2
- import type { SubtitleStyle } from '../types';
3
- export declare const measureTextWidth: (fontName: string, fontSize: number, bold: boolean, text: string) => number;
4
- export declare const layoutDanmaku: (inputList: UniPool, config: SubtitleStyle) => UniPool;
2
+ import type { CanvasCtx, SubtitleStyle } from '../types';
3
+ export declare const measureTextWidthConstructor: (canvasContext: CanvasCtx) => (fontName: string, fontSize: number, bold: boolean, text: string) => number;
4
+ export declare const layoutDanmaku: (inputList: UniPool, config: SubtitleStyle, canvasCtx: CanvasCtx) => UniPool;
@@ -1,5 +1,5 @@
1
1
  import 'reflect-metadata/lite';
2
- import type { Options as AssGenOptions } from './ass-gen';
2
+ import type { Options as AssGenOptions, CanvasCtx } from './ass-gen';
3
3
  import type { CommandDm as DM_JSON_BiliCommandGrpc } from './proto/gen/bili/dm_pb';
4
4
  import { UniDM } from './utils/dm-gen';
5
5
  import * as UniDMTools from './utils/dm-gen';
@@ -145,6 +145,9 @@ export declare class UniPool {
145
145
  static fromDDPlay(json: DM_JSON_DDPlay, episodeId: string, options?: Options): UniPool;
146
146
  toDDplay(): DM_JSON_DDPlay;
147
147
  static fromASS(ass: string): UniPool;
148
- toASS(options?: AssGenOptions): string;
148
+ /**
149
+ * 转换为ASS字幕格式的弹幕,需播放器支持多行ASS渲染
150
+ */
151
+ toASS(canvasCtx: CanvasCtx, options?: AssGenOptions): string;
149
152
  }
150
153
  export { platform, UniDM, UniDMTools, UniIDTools, type DM_JSON_BiliCommandGrpc, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dan-uni/dan-any",
3
- "version": "0.7.8",
3
+ "version": "0.8.1",
4
4
  "description": "A danmaku transformer lib, supporting danmaku from different platforms.",
5
5
  "keywords": [
6
6
  "bangumi",
@@ -31,12 +31,10 @@
31
31
  "buf": "buf generate"
32
32
  },
33
33
  "dependencies": {
34
- "@bufbuild/protobuf": "^2.6.2",
34
+ "@bufbuild/protobuf": "^2.6.3",
35
35
  "base16384": "^1.0.0",
36
- "canvas": "^3.1.2",
37
36
  "class-transformer": "^0.5.1",
38
37
  "class-validator": "^0.14.2",
39
- "fabric": "^6.7.1",
40
38
  "fast-xml-parser": "^5.2.5",
41
39
  "fs-extra": "^11.3.0",
42
40
  "hh-mm-ss": "^1.2.0",
@@ -46,10 +44,11 @@
46
44
  },
47
45
  "devDependencies": {
48
46
  "@bufbuild/buf": "^1.56.0",
49
- "@bufbuild/protoc-gen-es": "^2.6.2",
47
+ "@bufbuild/protoc-gen-es": "^2.6.3",
50
48
  "@types/fs-extra": "^11.0.4",
51
49
  "@types/hh-mm-ss": "^1.2.3",
52
50
  "@types/json-bigint": "^1.0.4",
51
+ "canvas": "^3.1.2",
53
52
  "protobufjs": "^7.5.3"
54
53
  }
55
54
  }
package/rslib.config.ts CHANGED
@@ -18,7 +18,6 @@ export default defineConfig({
18
18
  output: {
19
19
  filename: { js: 'index.umd.min.js' },
20
20
  target: 'web',
21
- externals: ['fabric/node'],
22
21
  },
23
22
  dts: true,
24
23
  umdName: pkg.name,
@@ -1,11 +1,15 @@
1
- import { assert, assertType, it } from 'vitest'
1
+ import { createCanvas } from 'canvas'
2
+ import { assertType, it } from 'vitest'
2
3
 
3
- import { measureTextWidth } from '../util/layout'
4
+ import { measureTextWidthConstructor } from '../util/layout'
4
5
 
5
6
  it('canvas measureTextWidth', () => {
6
7
  const text = '一段测试文字'
7
- const width = measureTextWidth('SimHei', 25, false, text)
8
+ const canvas = createCanvas(50, 50)
9
+ const width = measureTextWidthConstructor(
10
+ canvas.getContext('2d') as unknown as CanvasRenderingContext2D,
11
+ )('SimHei', 25, false, text)
8
12
  assertType<number>(width)
9
13
  console.info(width, text.length)
10
- assert(width >= 25 * text.length)
14
+ // assert(width >= 25 * text.length)
11
15
  })
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs'
2
2
  import path, { dirname } from 'node:path'
3
3
  import { fileURLToPath } from 'node:url'
4
+ import { createCanvas } from 'canvas'
4
5
  import { it } from 'vitest'
5
6
 
6
7
  import { generateASS } from '../'
@@ -12,9 +13,14 @@ it('generate ass from xml', () => {
12
13
  const filename = '898651903.xml'
13
14
  const xmlPath = path.join(__dirname, filename)
14
15
  const xmlText = fs.readFileSync(xmlPath, 'utf-8')
15
- const assText = generateASS(UniPool.fromBiliXML(xmlText), {
16
- // filename,
17
- // title: '我的忏悔',
18
- })
16
+ const canvas = createCanvas(50, 50)
17
+ const assText = generateASS(
18
+ UniPool.fromBiliXML(xmlText),
19
+ {
20
+ // filename,
21
+ // title: '我的忏悔',
22
+ },
23
+ canvas.getContext('2d') as unknown as CanvasRenderingContext2D,
24
+ )
19
25
  fs.writeFileSync(path.join(__dirname, `${filename}.ass`), assText, 'utf-8')
20
26
  })
@@ -1,6 +1,6 @@
1
1
  // import parse from './parse/bilibili'
2
2
  import type { RawConfig } from './ass/raw'
3
- import type { SubtitleStyle } from './types'
3
+ import type { CanvasCtx, SubtitleStyle } from './types'
4
4
 
5
5
  import { UniPool } from '..'
6
6
  import ass from './ass/create'
@@ -8,6 +8,8 @@ import { deRaw } from './ass/raw'
8
8
  import getConfig from './config'
9
9
  import { DanmakuList2UniPool, layoutDanmaku } from './util'
10
10
 
11
+ export { CanvasCtx }
12
+
11
13
  export type Options = {
12
14
  filename?: string
13
15
  title?: string
@@ -32,13 +34,17 @@ const assText = generateASS(xmlText, { filename, title: 'Quick Example' })
32
34
  fs.writeFileSync(`${filename}.ass`, assText, 'utf-8')
33
35
  ```
34
36
  */
35
- export function generateASS(danmaku: UniPool, options: Options): string {
37
+ export function generateASS(
38
+ danmaku: UniPool,
39
+ options: Options,
40
+ canvasCtx: CanvasCtx,
41
+ ): string {
36
42
  // const result = parse(text)
37
43
  const config = getConfig(options.substyle)
38
44
  // const filteredList = filterDanmaku(result.list, config.block)
39
45
  // const mergedList = mergeDanmaku(result.list, config.mergeIn)
40
46
  const mergedList = danmaku.merge(config.mergeIn)
41
- const layoutList = layoutDanmaku(mergedList, config)
47
+ const layoutList = layoutDanmaku(mergedList, config, canvasCtx)
42
48
  const content = ass(
43
49
  layoutList,
44
50
  danmaku,
@@ -1,7 +1,31 @@
1
1
  // export type BlockRule = string | RegExp
2
-
2
+ import type { CanvasRenderingContext2D as NodeCRCtx2D } from 'canvas'
3
3
  import type { UniDM } from '../utils/dm-gen'
4
4
 
5
+ /**
6
+ * 请根据您的使用环境提供一个 50x50 的 2D Canvas 上下文
7
+ * @example
8
+ * // Node.js + canvas
9
+ * import { createCanvas } from 'canvas'
10
+ * const canvas = createCanvas(50, 50)
11
+ * const ctx = canvas.getContext('2d')
12
+ * @example
13
+ * // Node.js + Fabric.js
14
+ * import { StaticCanvas } from 'fabric/node'
15
+ * const ctx = new StaticCanvas(null, { width: 50, height: 50 }).getContext()
16
+ * @example
17
+ * // Browser + Native Canvas
18
+ * const canvas = document.createElement('canvas')
19
+ * canvas.width = 50
20
+ * canvas.height = 50
21
+ * const ctx = canvas.getContext('2d')
22
+ * @example
23
+ * // Browser + Fabric.js
24
+ * import { Canvas } from 'fabric'
25
+ * const ctx = new Canvas('canvas', { width: 50, height: 50 }).getContext()
26
+ */
27
+ export type CanvasCtx = NodeCRCtx2D | CanvasRenderingContext2D
28
+
5
29
  export interface Context {
6
30
  filename: string
7
31
  title: string
@@ -1,7 +1,5 @@
1
- import { Canvas as WebCanvas } from 'fabric'
2
- import { StaticCanvas as NodeCanvas } from 'fabric/node'
3
1
  import type { UniPool } from '../..'
4
- import type { Danmaku, SubtitleStyle } from '../types'
2
+ import type { CanvasCtx, Danmaku, SubtitleStyle } from '../types'
5
3
 
6
4
  import { DanmakuType, FontSize } from '../types'
7
5
  import { DanmakuList2UniPool, UniPool2DanmakuLists } from './danconvert'
@@ -78,18 +76,7 @@ const splitGrids = ({
78
76
  }
79
77
  }
80
78
 
81
- export const measureTextWidth = (() => {
82
- let isWeb
83
- try {
84
- isWeb = !!window
85
- } catch {
86
- isWeb = false
87
- }
88
- const Canvas = isWeb ? WebCanvas : NodeCanvas
89
- const canvasContext = new Canvas(undefined, {
90
- width: 50,
91
- height: 50,
92
- }).getContext()
79
+ export const measureTextWidthConstructor = (canvasContext: CanvasCtx) => {
93
80
  const supportTextMeasure = !!canvasContext.measureText('中')
94
81
 
95
82
  if (supportTextMeasure) {
@@ -110,7 +97,7 @@ export const measureTextWidth = (() => {
110
97
  )
111
98
  return (_fontName: string, fontSize: number, _bold: boolean, text: string) =>
112
99
  text.length * fontSize
113
- })()
100
+ }
114
101
 
115
102
  // 找到能用的行
116
103
  const resolveAvailableFixGrid = (grids: FixGrid[], time: number) => {
@@ -149,7 +136,7 @@ const resolveAvailableScrollGrid = (
149
136
  return -1
150
137
  }
151
138
 
152
- const initializeLayout = (config: SubtitleStyle) => {
139
+ const initializeLayout = (config: SubtitleStyle, canvasCtx: CanvasCtx) => {
153
140
  const {
154
141
  playResX,
155
142
  playResY,
@@ -171,7 +158,12 @@ const initializeLayout = (config: SubtitleStyle) => {
171
158
  const targetGrids = grids[danmaku.type as keyof DanmakuGrids]
172
159
  const danmakuFontSize = fontSize[danmaku.fontSizeType]
173
160
  const rectWidth =
174
- measureTextWidth(fontName, danmakuFontSize, bold, danmaku.content) +
161
+ measureTextWidthConstructor(canvasCtx)(
162
+ fontName,
163
+ danmakuFontSize,
164
+ bold,
165
+ danmaku.content,
166
+ ) +
175
167
  paddingLeft +
176
168
  paddingRight
177
169
  const verticalOffset =
@@ -239,11 +231,12 @@ const initializeLayout = (config: SubtitleStyle) => {
239
231
  export const layoutDanmaku = (
240
232
  inputList: UniPool,
241
233
  config: SubtitleStyle,
234
+ canvasCtx: CanvasCtx,
242
235
  ): UniPool => {
243
236
  const list = [...UniPool2DanmakuLists(inputList)].sort(
244
237
  (x, y) => x.time - y.time,
245
238
  )
246
- const layout = initializeLayout(config)
239
+ const layout = initializeLayout(config, canvasCtx)
247
240
 
248
241
  return DanmakuList2UniPool(list.map(layout).filter((danmaku) => !!danmaku))
249
242
  }
package/src/index.test.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  //基于以下注释,根据vitest生成测试用例
2
+ import { createCanvas } from 'canvas'
2
3
  import { describe, it } from 'vitest'
3
4
 
4
5
  import { UniPool } from './index'
@@ -50,8 +51,9 @@ describe('转化自', () => {
50
51
  console.info(pool)
51
52
  })
52
53
  it('ass[双向]', () => {
54
+ const canvas = createCanvas(50, 50)
53
55
  const pool = UniPool.fromBiliXML(xml)
54
- const ass = pool.toASS()
56
+ const ass = pool.toASS(canvas.getContext('2d'))
55
57
  console.info(ass)
56
58
  console.info(UniPool.fromASS(ass))
57
59
  })
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'
@@ -377,8 +377,8 @@ export class UniPool {
377
377
  return this.dans
378
378
  case 'danuni.bin':
379
379
  return this.toPb()
380
- // case 'bili.xml':
381
- // return this.toBiliXML()
380
+ case 'bili.xml':
381
+ return this.toBiliXML()
382
382
  // case 'bili.bin':
383
383
  // return this.toBiliBin()
384
384
  // case 'bili.cmd.bin':
@@ -389,8 +389,8 @@ export class UniPool {
389
389
  return this.toArtplayer()
390
390
  case 'ddplay.json':
391
391
  return this.toDDplay()
392
- case 'common.ass':
393
- return this.toASS()
392
+ // case 'common.ass':
393
+ // return this.toASS()
394
394
  default: {
395
395
  const message = '(err) Unknown format or unsupported now!'
396
396
  if (continue_on_error) return message
@@ -649,9 +649,15 @@ export class UniPool {
649
649
  static fromASS(ass: string) {
650
650
  return parseAssRawField(ass)
651
651
  }
652
- toASS(options: AssGenOptions = { substyle: {} }): string {
652
+ /**
653
+ * 转换为ASS字幕格式的弹幕,需播放器支持多行ASS渲染
654
+ */
655
+ toASS(
656
+ canvasCtx: CanvasCtx,
657
+ options: AssGenOptions = { substyle: {} },
658
+ ): string {
653
659
  const fn = this.shared.SOID
654
- return generateASS(this, { filename: fn, title: fn, ...options })
660
+ return generateASS(this, { filename: fn, title: fn, ...options }, canvasCtx)
655
661
  }
656
662
  }
657
663
 
@@ -1,4 +1,4 @@
1
- // @generated by protoc-gen-es v2.6.2 with parameter "target=ts"
1
+ // @generated by protoc-gen-es v2.6.3 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.6.2 with parameter "target=ts"
1
+ // @generated by protoc-gen-es v2.6.3 with parameter "target=ts"
2
2
  // @generated from file danuni.proto (package danuni.danmaku.v1, syntax proto3)
3
3
  /* eslint-disable */
4
4