@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
|
@@ -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
|
|
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;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
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';
|
|
6
6
|
import * as UniIDTools from './utils/id-gen';
|
|
7
7
|
import * as platform from './utils/platform';
|
|
8
|
+
interface DanUniConvertTip {
|
|
9
|
+
meassage: string;
|
|
10
|
+
version: string;
|
|
11
|
+
data?: string;
|
|
12
|
+
}
|
|
8
13
|
export interface DM_XML_Bili {
|
|
9
14
|
i: {
|
|
10
15
|
chatserver: string;
|
|
@@ -28,12 +33,14 @@ export interface DM_JSON_Dplayer {
|
|
|
28
33
|
data: [number, number, number, string, string][];
|
|
29
34
|
}
|
|
30
35
|
export interface DM_JSON_Artplayer {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
danmuku: {
|
|
37
|
+
text: string;
|
|
38
|
+
time?: number;
|
|
39
|
+
mode?: number;
|
|
40
|
+
color?: string;
|
|
41
|
+
border?: boolean;
|
|
42
|
+
style?: {};
|
|
43
|
+
}[];
|
|
37
44
|
}
|
|
38
45
|
export interface DM_JSON_DDPlay {
|
|
39
46
|
count: number | string;
|
|
@@ -54,7 +61,13 @@ interface Options {
|
|
|
54
61
|
export declare class UniPool {
|
|
55
62
|
dans: UniDM[];
|
|
56
63
|
options: Options;
|
|
57
|
-
|
|
64
|
+
private info;
|
|
65
|
+
constructor(dans: UniDM[], options?: Options, info?: {
|
|
66
|
+
/**
|
|
67
|
+
* 是否从已被转换过的第三方格式弹幕再次转换而来
|
|
68
|
+
*/
|
|
69
|
+
fromConverted: boolean;
|
|
70
|
+
});
|
|
58
71
|
pipe(fn: UniPoolPipe): Promise<UniPool>;
|
|
59
72
|
pipeSync(fn: UniPoolPipeSync): UniPool;
|
|
60
73
|
get shared(): shareItems;
|
|
@@ -125,26 +138,58 @@ export declare class UniPool {
|
|
|
125
138
|
*/
|
|
126
139
|
merge(lifetime?: number): UniPool;
|
|
127
140
|
minify(): (Partial<UniDMTools.UniDMObj> & Pick<UniDMTools.UniDMObj, "SOID">)[];
|
|
128
|
-
convert2(format: DM_format, continue_on_error?: boolean): string | Uint8Array<ArrayBufferLike> | UniDM[] | DM_JSON_Dplayer
|
|
141
|
+
convert2(format: DM_format, continue_on_error?: boolean): string | Uint8Array<ArrayBufferLike> | UniDM[] | (DM_JSON_Dplayer & {
|
|
142
|
+
danuni?: DanUniConvertTip;
|
|
143
|
+
}) | (DM_JSON_Artplayer & {
|
|
144
|
+
danuni?: DanUniConvertTip;
|
|
145
|
+
}) | (DM_JSON_DDPlay & {
|
|
146
|
+
danuni?: DanUniConvertTip;
|
|
147
|
+
});
|
|
129
148
|
static fromPb(bin: Uint8Array | ArrayBuffer, options?: Options): UniPool;
|
|
130
149
|
/**
|
|
131
150
|
* 转为 protobuf 二进制
|
|
132
151
|
*/
|
|
133
152
|
toPb(): Uint8Array<ArrayBufferLike>;
|
|
134
153
|
static fromBiliXML(xml: string, options?: Options): UniPool;
|
|
135
|
-
toBiliXML(
|
|
154
|
+
toBiliXML(options?: {
|
|
155
|
+
/**
|
|
156
|
+
* 当SOID非来源bili时,若此处指定则使用该值为cid,否则使用SOID
|
|
157
|
+
*/
|
|
158
|
+
cid?: bigint;
|
|
159
|
+
/**
|
|
160
|
+
* 当仅含有来自bili的弹幕时,启用将保持发送者标识不含`@`
|
|
161
|
+
* @description
|
|
162
|
+
* bili的弹幕含midHash(crc),不启用该处使用senderID填充,启用则去除`@bili`部分,提高兼容性
|
|
163
|
+
*/
|
|
164
|
+
avoidSenderIDWithAt?: boolean;
|
|
165
|
+
}): string;
|
|
136
166
|
static fromBiliGrpc(bin: Uint8Array | ArrayBuffer, options?: Options): UniPool;
|
|
137
167
|
/**
|
|
138
168
|
* @param bin 符合`DmWebViewReplySchema`(bili视频meta)的protobuf二进制
|
|
139
169
|
*/
|
|
140
170
|
static fromBiliCommandGrpc(bin: Uint8Array | ArrayBuffer, options?: Options): UniPool;
|
|
141
|
-
static fromDplayer(json: DM_JSON_Dplayer
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
171
|
+
static fromDplayer(json: DM_JSON_Dplayer & {
|
|
172
|
+
danuni?: DanUniConvertTip;
|
|
173
|
+
}, playerID: string, domain?: string, options?: Options): UniPool;
|
|
174
|
+
toDplayer(): DM_JSON_Dplayer & {
|
|
175
|
+
danuni?: DanUniConvertTip;
|
|
176
|
+
};
|
|
177
|
+
static fromArtplayer(json: DM_JSON_Artplayer & {
|
|
178
|
+
danuni?: DanUniConvertTip;
|
|
179
|
+
}, playerID: string, domain?: string, options?: Options): UniPool;
|
|
180
|
+
toArtplayer(): DM_JSON_Artplayer & {
|
|
181
|
+
danuni?: DanUniConvertTip;
|
|
182
|
+
};
|
|
183
|
+
static fromDDPlay(json: DM_JSON_DDPlay & {
|
|
184
|
+
danuni?: DanUniConvertTip;
|
|
185
|
+
}, episodeId: string, options?: Options): UniPool;
|
|
186
|
+
toDDplay(): DM_JSON_DDPlay & {
|
|
187
|
+
danuni?: DanUniConvertTip;
|
|
188
|
+
};
|
|
147
189
|
static fromASS(ass: string): UniPool;
|
|
148
|
-
|
|
190
|
+
/**
|
|
191
|
+
* 转换为ASS字幕格式的弹幕,需播放器支持多行ASS渲染
|
|
192
|
+
*/
|
|
193
|
+
toASS(canvasCtx: CanvasCtx, options?: AssGenOptions): string;
|
|
149
194
|
}
|
|
150
195
|
export { platform, UniDM, UniDMTools, UniIDTools, type DM_JSON_BiliCommandGrpc, };
|
|
@@ -272,9 +272,13 @@ export declare class UniDM {
|
|
|
272
272
|
*/
|
|
273
273
|
static transCtime(oriCtime: ctime, tsUnit?: 'ms' | 's'): Date;
|
|
274
274
|
static transMode(oriMode: number, fmt: 'bili' | 'dplayer' | 'artplayer' | 'ddplay'): Modes;
|
|
275
|
-
static fromBili(args: DMBili, cid?: bigint, options?: Options): UniDM;
|
|
275
|
+
static fromBili(args: DMBili, cid?: bigint, options?: Options, recSOID?: string): UniDM;
|
|
276
276
|
toBiliXML(options?: {
|
|
277
|
-
skipBiliCommand
|
|
277
|
+
skipBiliCommand?: boolean;
|
|
278
|
+
/**
|
|
279
|
+
* 见 ../index.ts UniPool.toBiliXML() 的 options,该option不宜手动调用,判断逻辑未封装
|
|
280
|
+
*/
|
|
281
|
+
avoidSenderIDWithAt?: boolean;
|
|
278
282
|
}): {
|
|
279
283
|
'#text': string;
|
|
280
284
|
'@_p': string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dan-uni/dan-any",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.5",
|
|
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.
|
|
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.
|
|
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
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createCanvas } from 'canvas'
|
|
2
|
+
import { assertType, it } from 'vitest'
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { measureTextWidthConstructor } from '../util/layout'
|
|
4
5
|
|
|
5
6
|
it('canvas measureTextWidth', () => {
|
|
6
7
|
const text = '一段测试文字'
|
|
7
|
-
const
|
|
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
|
|
16
|
-
|
|
17
|
-
|
|
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
|
})
|
package/src/ass-gen/index.ts
CHANGED
|
@@ -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(
|
|
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,
|
package/src/ass-gen/types.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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'
|
|
@@ -33,28 +34,42 @@ describe('转化自', () => {
|
|
|
33
34
|
console.info(xml)
|
|
34
35
|
console.info(pool)
|
|
35
36
|
console.info(pool.toBiliXML())
|
|
37
|
+
console.info(pool.toBiliXML({ avoidSenderIDWithAt: true }))
|
|
36
38
|
})
|
|
37
39
|
it('artplayer(json)', () => {
|
|
38
|
-
const json =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
const json = {
|
|
41
|
+
danmuku: [
|
|
42
|
+
{
|
|
43
|
+
text: 'artplayer测试弹幕', // 弹幕文本
|
|
44
|
+
time: 10, // 弹幕时间, 默认为当前播放器时间
|
|
45
|
+
mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部
|
|
46
|
+
color: '#FFFFFF', // 弹幕颜色,默认为白色
|
|
47
|
+
border: false, // 弹幕是否有描边, 默认为 false
|
|
48
|
+
style: { border: '10rem' }, // 弹幕自定义样式, 默认为空对象
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
48
52
|
pool = UniPool.fromArtplayer(json, 'playerid-test', 'acfun')
|
|
49
53
|
console.info(json)
|
|
50
54
|
console.info(pool)
|
|
51
55
|
})
|
|
52
56
|
it('ass[双向]', () => {
|
|
57
|
+
const canvas = createCanvas(50, 50)
|
|
53
58
|
const pool = UniPool.fromBiliXML(xml)
|
|
54
|
-
const ass = pool.toASS()
|
|
59
|
+
const ass = pool.toASS(canvas.getContext('2d'))
|
|
55
60
|
console.info(ass)
|
|
56
61
|
console.info(UniPool.fromASS(ass))
|
|
57
62
|
})
|
|
63
|
+
it('pb[双向]', () => {
|
|
64
|
+
const pool = UniPool.fromBiliXML(xml)
|
|
65
|
+
const pb = pool.toPb()
|
|
66
|
+
console.info(UniPool.fromPb(pb))
|
|
67
|
+
})
|
|
68
|
+
it('DDplay[双向]', () => {
|
|
69
|
+
const pool = UniPool.fromBiliXML(xml)
|
|
70
|
+
const ddplay = pool.toDDplay()
|
|
71
|
+
console.info(UniPool.fromDDPlay(ddplay, '1'))
|
|
72
|
+
})
|
|
58
73
|
})
|
|
59
74
|
|
|
60
75
|
describe('共通值', () => {
|