@dan-uni/dan-any 1.4.8 → 2.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.
Files changed (200) hide show
  1. package/README.md +87 -43
  2. package/dist/adapters.d.mts +2 -0
  3. package/dist/adapters.mjs +1 -0
  4. package/dist/chunk-0Lt9GpW0.mjs +1 -0
  5. package/dist/core/db/schema.d.mts +2 -0
  6. package/dist/core/db/schema.mjs +2 -0
  7. package/dist/core/db/schema.mjs.map +1 -0
  8. package/dist/core/db/utils.d.mts +1707 -0
  9. package/dist/core/db/utils.d.mts.map +1 -0
  10. package/dist/core/db/utils.mjs +1 -0
  11. package/dist/core-D7LMAB5h.mjs +2 -0
  12. package/dist/core-D7LMAB5h.mjs.map +1 -0
  13. package/dist/core.d.mts +2 -0
  14. package/dist/core.mjs +1 -0
  15. package/dist/db-DWWzkRBb.mjs +41 -0
  16. package/dist/db-DWWzkRBb.mjs.map +1 -0
  17. package/dist/dm-extra-DrAQCrFv.d.mts +135 -0
  18. package/dist/dm-extra-DrAQCrFv.d.mts.map +1 -0
  19. package/dist/dm-tkTdR_W6.mjs +2 -0
  20. package/dist/dm-tkTdR_W6.mjs.map +1 -0
  21. package/dist/index-vAs4qV9A.d.mts +6195 -0
  22. package/dist/index-vAs4qV9A.d.mts.map +1 -0
  23. package/dist/index.d.mts +4 -0
  24. package/dist/index.mjs +1 -0
  25. package/dist/isSame-D4G9eoE4.mjs +2 -0
  26. package/dist/isSame-D4G9eoE4.mjs.map +1 -0
  27. package/dist/plugins.d.mts +105 -0
  28. package/dist/plugins.d.mts.map +1 -0
  29. package/dist/plugins.mjs +2 -0
  30. package/dist/plugins.mjs.map +1 -0
  31. package/dist/schema-BuenbDx9.d.mts +1619 -0
  32. package/dist/schema-BuenbDx9.d.mts.map +1 -0
  33. package/dist/utils.d.mts +25 -0
  34. package/dist/utils.d.mts.map +1 -0
  35. package/dist/utils.mjs +2 -0
  36. package/dist/utils.mjs.map +1 -0
  37. package/package.json +65 -37
  38. package/.babelrc.json +0 -12
  39. package/buf.gen.yaml +0 -10
  40. package/buf.yaml +0 -12
  41. package/dist/browser/1~rslib-runtime.min.js +0 -49
  42. package/dist/browser/index.min.js +0 -13523
  43. package/dist/browser/index.min.js.LICENSE.txt +0 -17
  44. package/dist/browser/plugins/bili.min.js +0 -1
  45. package/dist/browser/plugins/index.min.js +0 -2
  46. package/dist/browser/plugins/stats.min.js +0 -10
  47. package/dist/browser/src/ass-gen/__tests__/canvas.test.d.ts +0 -1
  48. package/dist/browser/src/ass-gen/__tests__/generate.test.d.ts +0 -1
  49. package/dist/browser/src/ass-gen/ass/create.d.ts +0 -4
  50. package/dist/browser/src/ass-gen/ass/dialogue.d.ts +0 -16
  51. package/dist/browser/src/ass-gen/ass/event.d.ts +0 -2
  52. package/dist/browser/src/ass-gen/ass/info.d.ts +0 -8
  53. package/dist/browser/src/ass-gen/ass/raw.d.ts +0 -14
  54. package/dist/browser/src/ass-gen/ass/style.d.ts +0 -2
  55. package/dist/browser/src/ass-gen/config.d.ts +0 -2
  56. package/dist/browser/src/ass-gen/index.d.ts +0 -30
  57. package/dist/browser/src/ass-gen/types.d.ts +0 -71
  58. package/dist/browser/src/ass-gen/util/color.d.ts +0 -18
  59. package/dist/browser/src/ass-gen/util/danconvert.d.ts +0 -5
  60. package/dist/browser/src/ass-gen/util/index.d.ts +0 -4
  61. package/dist/browser/src/ass-gen/util/lang.d.ts +0 -3
  62. package/dist/browser/src/ass-gen/util/layout.d.ts +0 -4
  63. package/dist/browser/src/index.d.ts +0 -283
  64. package/dist/browser/src/index.test.d.ts +0 -1
  65. package/dist/browser/src/plugins/bili/dedupe.d.ts +0 -3
  66. package/dist/browser/src/plugins/bili/history-danmaku-fast-forward.d.ts +0 -18
  67. package/dist/browser/src/plugins/bili/index.d.ts +0 -2
  68. package/dist/browser/src/plugins/bili/index.test.d.ts +0 -1
  69. package/dist/browser/src/plugins/index.d.ts +0 -2
  70. package/dist/browser/src/plugins/stats/getLatestDan.d.ts +0 -6
  71. package/dist/browser/src/plugins/stats/index.d.ts +0 -1
  72. package/dist/browser/src/plugins/stats/index.test.d.ts +0 -1
  73. package/dist/browser/src/proto/gen/bilibili/community/service/dm/v1/dm_pb.d.ts +0 -3426
  74. package/dist/browser/src/proto/gen/danuni/danmaku/v1/danmaku_pb.d.ts +0 -176
  75. package/dist/browser/src/utils/dm-gen.d.ts +0 -309
  76. package/dist/browser/src/utils/dm-gen.test.d.ts +0 -1
  77. package/dist/browser/src/utils/fileParser.d.ts +0 -3
  78. package/dist/browser/src/utils/id-gen.d.ts +0 -50
  79. package/dist/browser/src/utils/platform.d.ts +0 -24
  80. package/dist/node/0~rslib-runtime.js +0 -23
  81. package/dist/node/index.js +0 -2099
  82. package/dist/node/plugins/bili.js +0 -1
  83. package/dist/node/plugins/index.js +0 -2
  84. package/dist/node/plugins/stats.js +0 -10
  85. package/dist/node/src/ass-gen/__tests__/canvas.test.d.ts +0 -1
  86. package/dist/node/src/ass-gen/__tests__/generate.test.d.ts +0 -1
  87. package/dist/node/src/ass-gen/ass/create.d.ts +0 -4
  88. package/dist/node/src/ass-gen/ass/dialogue.d.ts +0 -16
  89. package/dist/node/src/ass-gen/ass/event.d.ts +0 -2
  90. package/dist/node/src/ass-gen/ass/info.d.ts +0 -8
  91. package/dist/node/src/ass-gen/ass/raw.d.ts +0 -14
  92. package/dist/node/src/ass-gen/ass/style.d.ts +0 -2
  93. package/dist/node/src/ass-gen/config.d.ts +0 -2
  94. package/dist/node/src/ass-gen/index.d.ts +0 -30
  95. package/dist/node/src/ass-gen/types.d.ts +0 -71
  96. package/dist/node/src/ass-gen/util/color.d.ts +0 -18
  97. package/dist/node/src/ass-gen/util/danconvert.d.ts +0 -5
  98. package/dist/node/src/ass-gen/util/index.d.ts +0 -4
  99. package/dist/node/src/ass-gen/util/lang.d.ts +0 -3
  100. package/dist/node/src/ass-gen/util/layout.d.ts +0 -4
  101. package/dist/node/src/index.d.ts +0 -283
  102. package/dist/node/src/index.test.d.ts +0 -1
  103. package/dist/node/src/plugins/bili/dedupe.d.ts +0 -3
  104. package/dist/node/src/plugins/bili/history-danmaku-fast-forward.d.ts +0 -18
  105. package/dist/node/src/plugins/bili/index.d.ts +0 -2
  106. package/dist/node/src/plugins/bili/index.test.d.ts +0 -1
  107. package/dist/node/src/plugins/index.d.ts +0 -2
  108. package/dist/node/src/plugins/stats/getLatestDan.d.ts +0 -6
  109. package/dist/node/src/plugins/stats/index.d.ts +0 -1
  110. package/dist/node/src/plugins/stats/index.test.d.ts +0 -1
  111. package/dist/node/src/proto/gen/bilibili/community/service/dm/v1/dm_pb.d.ts +0 -3426
  112. package/dist/node/src/proto/gen/danuni/danmaku/v1/danmaku_pb.d.ts +0 -176
  113. package/dist/node/src/utils/dm-gen.d.ts +0 -309
  114. package/dist/node/src/utils/dm-gen.test.d.ts +0 -1
  115. package/dist/node/src/utils/fileParser.d.ts +0 -3
  116. package/dist/node/src/utils/id-gen.d.ts +0 -50
  117. package/dist/node/src/utils/platform.d.ts +0 -24
  118. package/dist/umd/index.umd.min.js +0 -32093
  119. package/dist/umd/index.umd.min.js.LICENSE.txt +0 -34
  120. package/dist/umd/plugins/bili.umd.min.js +0 -32004
  121. package/dist/umd/plugins/bili.umd.min.js.LICENSE.txt +0 -34
  122. package/dist/umd/plugins/index.umd.min.js +0 -32020
  123. package/dist/umd/plugins/index.umd.min.js.LICENSE.txt +0 -34
  124. package/dist/umd/plugins/stats.umd.min.js +0 -39
  125. package/dist/umd/src/ass-gen/__tests__/canvas.test.d.ts +0 -1
  126. package/dist/umd/src/ass-gen/__tests__/generate.test.d.ts +0 -1
  127. package/dist/umd/src/ass-gen/ass/create.d.ts +0 -4
  128. package/dist/umd/src/ass-gen/ass/dialogue.d.ts +0 -16
  129. package/dist/umd/src/ass-gen/ass/event.d.ts +0 -2
  130. package/dist/umd/src/ass-gen/ass/info.d.ts +0 -8
  131. package/dist/umd/src/ass-gen/ass/raw.d.ts +0 -14
  132. package/dist/umd/src/ass-gen/ass/style.d.ts +0 -2
  133. package/dist/umd/src/ass-gen/config.d.ts +0 -2
  134. package/dist/umd/src/ass-gen/index.d.ts +0 -30
  135. package/dist/umd/src/ass-gen/types.d.ts +0 -71
  136. package/dist/umd/src/ass-gen/util/color.d.ts +0 -18
  137. package/dist/umd/src/ass-gen/util/danconvert.d.ts +0 -5
  138. package/dist/umd/src/ass-gen/util/index.d.ts +0 -4
  139. package/dist/umd/src/ass-gen/util/lang.d.ts +0 -3
  140. package/dist/umd/src/ass-gen/util/layout.d.ts +0 -4
  141. package/dist/umd/src/index.d.ts +0 -283
  142. package/dist/umd/src/index.test.d.ts +0 -1
  143. package/dist/umd/src/plugins/bili/dedupe.d.ts +0 -3
  144. package/dist/umd/src/plugins/bili/history-danmaku-fast-forward.d.ts +0 -18
  145. package/dist/umd/src/plugins/bili/index.d.ts +0 -2
  146. package/dist/umd/src/plugins/bili/index.test.d.ts +0 -1
  147. package/dist/umd/src/plugins/index.d.ts +0 -2
  148. package/dist/umd/src/plugins/stats/getLatestDan.d.ts +0 -6
  149. package/dist/umd/src/plugins/stats/index.d.ts +0 -1
  150. package/dist/umd/src/plugins/stats/index.test.d.ts +0 -1
  151. package/dist/umd/src/proto/gen/bilibili/community/service/dm/v1/dm_pb.d.ts +0 -3426
  152. package/dist/umd/src/proto/gen/danuni/danmaku/v1/danmaku_pb.d.ts +0 -176
  153. package/dist/umd/src/utils/dm-gen.d.ts +0 -309
  154. package/dist/umd/src/utils/dm-gen.test.d.ts +0 -1
  155. package/dist/umd/src/utils/fileParser.d.ts +0 -3
  156. package/dist/umd/src/utils/id-gen.d.ts +0 -50
  157. package/dist/umd/src/utils/platform.d.ts +0 -24
  158. package/plugins/package.json +0 -6
  159. package/rslib.config.ts +0 -101
  160. package/src/ass-gen/__tests__/898651903.xml +0 -1619
  161. package/src/ass-gen/__tests__/898651903.xml.ass +0 -1516
  162. package/src/ass-gen/__tests__/canvas.test.ts +0 -15
  163. package/src/ass-gen/__tests__/generate.test.ts +0 -26
  164. package/src/ass-gen/ass/create.ts +0 -37
  165. package/src/ass-gen/ass/dialogue.ts +0 -90
  166. package/src/ass-gen/ass/event.ts +0 -57
  167. package/src/ass-gen/ass/info.ts +0 -27
  168. package/src/ass-gen/ass/raw.ts +0 -78
  169. package/src/ass-gen/ass/style.ts +0 -66
  170. package/src/ass-gen/config.ts +0 -45
  171. package/src/ass-gen/index.ts +0 -73
  172. package/src/ass-gen/types.ts +0 -76
  173. package/src/ass-gen/util/color.ts +0 -55
  174. package/src/ass-gen/util/danconvert.ts +0 -43
  175. package/src/ass-gen/util/index.ts +0 -10
  176. package/src/ass-gen/util/lang.ts +0 -35
  177. package/src/ass-gen/util/layout.ts +0 -242
  178. package/src/index.test.ts +0 -157
  179. package/src/index.ts +0 -1144
  180. package/src/plugins/bili/README.md +0 -87
  181. package/src/plugins/bili/dedupe.ts +0 -29
  182. package/src/plugins/bili/history-danmaku-fast-forward.ts +0 -86
  183. package/src/plugins/bili/index.test.ts +0 -129
  184. package/src/plugins/bili/index.ts +0 -2
  185. package/src/plugins/index.ts +0 -2
  186. package/src/plugins/stats/README.md +0 -44
  187. package/src/plugins/stats/getLatestDan.ts +0 -13
  188. package/src/plugins/stats/index.test.ts +0 -39
  189. package/src/plugins/stats/index.ts +0 -1
  190. package/src/proto/gen/bilibili/community/service/dm/v1/dm_pb.ts +0 -4072
  191. package/src/proto/gen/danuni/danmaku/v1/danmaku_pb.ts +0 -223
  192. package/src/proto/src/bilibili/community/service/dm/v1/dm.proto +0 -1095
  193. package/src/proto/src/danuni/danmaku/v1/danmaku.proto +0 -52
  194. package/src/utils/dm-gen.test.ts +0 -129
  195. package/src/utils/dm-gen.ts +0 -1082
  196. package/src/utils/fileParser.ts +0 -37
  197. package/src/utils/id-gen.ts +0 -73
  198. package/src/utils/platform.ts +0 -38
  199. package/tsconfig.json +0 -108
  200. package/types/tsconfig.tsbuildinfo +0 -1
package/src/index.ts DELETED
@@ -1,1144 +0,0 @@
1
- import 'reflect-metadata/lite'
2
-
3
- import { isJSON, isString } from 'class-validator'
4
- import { XMLBuilder, XMLParser } from 'fast-xml-parser'
5
- import JSONbig from 'json-bigint'
6
- import type { Options as AssGenOptions, CanvasCtx } from './ass-gen'
7
- import type { CommandDm as DM_JSON_BiliCommandGrpc } from './proto/gen/bilibili/community/service/dm/v1/dm_pb'
8
- import type { Danmaku } from './proto/gen/danuni/danmaku/v1/danmaku_pb'
9
-
10
- import { create, fromBinary, toBinary } from '@bufbuild/protobuf'
11
- import {
12
- timestampDate,
13
- timestampFromDate,
14
- timestampNow,
15
- } from '@bufbuild/protobuf/wkt'
16
-
17
- import pkg from '../package.json'
18
- import { generateASS, parseAssRawField } from './ass-gen'
19
- import {
20
- // DanmakuElem as DM_JSON_BiliGrpc,
21
- DmSegMobileReplySchema,
22
- DmWebViewReplySchema,
23
- } from './proto/gen/bilibili/community/service/dm/v1/dm_pb'
24
- import { ListDanResponseSchema } from './proto/gen/danuni/danmaku/v1/danmaku_pb'
25
- // import type * as UniIDType from './utils/id-gen'
26
-
27
- import { UniDM } from './utils/dm-gen'
28
- import * as UniDMTools from './utils/dm-gen'
29
- import { fileParser } from './utils/fileParser'
30
- import { UniID as ID } from './utils/id-gen'
31
- import * as UniIDTools from './utils/id-gen'
32
- import * as platform from './utils/platform'
33
-
34
- const JSON = JSONbig({
35
- useNativeBigInt: true,
36
- })
37
-
38
- const DanUniConvertTipTemplate: DanUniConvertTip = {
39
- meassage: 'Converted by DanUni!',
40
- version: `JS/TS ${pkg.name} (v${pkg.version})`,
41
- }
42
-
43
- interface DanUniConvertTip {
44
- meassage: string
45
- version: string
46
- data?: string
47
- }
48
-
49
- export type DM_JSON_DanuniMin = Partial<UniDMTools.UniDMObj>[]
50
-
51
- export interface DM_XML_Bili {
52
- i: {
53
- chatserver: string
54
- chatid: bigint
55
- mission: number
56
- maxlimit: number
57
- state: number
58
- real_name: number
59
- source: string
60
- d: {
61
- '#text': string
62
- '@_p': string
63
- }[]
64
- }
65
- }
66
- export interface DM_JSON_BiliUp {
67
- /** 接口状态码,0 表示成功 */
68
- code: number
69
- /** 文本形式的状态码,约定为字符串 "0" */
70
- message: string
71
- /** TTL(time to live) 标识,本接口常量为 1 */
72
- ttl: number
73
- data: {
74
- /** 分页元信息 */
75
- page: {
76
- /** 当前页序号,从 1 开始 */
77
- num: number
78
- /** 每页返回的弹幕条数 */
79
- size: number
80
- /** 总页数 */
81
- total: number
82
- }
83
- result: {
84
- /** 弹幕 ID,int64 */
85
- id: bigint
86
- /** 弹幕 ID 字符串形式 */
87
- id_str: string
88
- /** 弹幕类型:1 表示视频弹幕(当前接口恒为 1) */
89
- type: number
90
- aid: bigint
91
- bvid: string
92
- oid: bigint
93
- mid: bigint
94
- /** 发送者 mid 的 CRC 哈希(正常接口里用的是这个,保护隐私) */
95
- mid_hash: string
96
- /** 弹幕池 */
97
- pool: number
98
- /** 属性位字符串,逗号分隔的数字列表,对应 attr 二进制位 */
99
- attrs: string
100
- /** 弹幕出现时间,单位毫秒(注意,此处与protobuf接口保持一致,但xml中progress是秒) */
101
- progress: number
102
- mode: number
103
- /** 弹幕内容, content */
104
- msg: string
105
- state: number // ?
106
- fontsize: number
107
- /** 弹幕颜色,需将16进制转化为普通弹幕的10进制,示例:"ffffff" */
108
- color: string
109
- /** 发送时间戳,单位秒 */
110
- ctime: number
111
- /** 发送者昵称 */
112
- uname: string
113
- /** 发送者头像链接 */
114
- uface: string
115
- /** 视频主标题 */
116
- title: string
117
- self_seen: boolean // 尽自己可见?
118
- /** 弹幕点赞数 */
119
- like_count: number
120
- user_like: number // ?
121
- /** 分 P 标题 */
122
- p_title: string
123
- /** 视频封面链接 */
124
- cover: string
125
- is_charge: boolean // 该up是否开通充电计划?
126
- is_charge_plus: boolean // 该up是否开通高级充电计划?
127
- following: boolean // 当前登录用户是否关注该发送者?
128
- extra_cps: null // ?
129
- }[]
130
- }
131
- }
132
- export interface DM_JSON_Dplayer {
133
- code: number
134
- /**
135
- * progress,mode,color,midHash,content
136
- */
137
- data: [number, number, number, string, string][]
138
- }
139
- export interface DM_JSON_Artplayer {
140
- danmuku: {
141
- text: string // 弹幕文本
142
- time?: number // 弹幕时间,默认为当前播放器时间
143
- mode?: number // 弹幕模式:0: 滚动 (默认),1: 顶部,2: 底部
144
- color?: string // 弹幕颜色,默认为白色
145
- border?: boolean // 弹幕是否有描边,默认为 false
146
- style?: {} // 弹幕自定义样式,默认为空对象
147
- }[]
148
- }
149
- export interface DM_JSON_DDPlay {
150
- count: number | string
151
- comments: {
152
- cid: bigint
153
- p: string
154
- m: string
155
- }[]
156
- }
157
-
158
- export enum DM_format {
159
- DanuniJson = 'danuni.json',
160
- DanuniMinJson = 'danuni.min.json',
161
- DanuniPbBin = 'danuni.binpb',
162
- BiliXml = 'bili.xml',
163
- BiliPbBin = 'bili.binpb',
164
- BiliCmdPbBin = 'bili.cmd.binpb',
165
- BiliUpJson = 'bili.up.json',
166
- DplayerJson = 'dplayer.json',
167
- ArtplayerJson = 'artplayer.json',
168
- DDPlayJson = 'ddplay.json',
169
- CommonAss = 'common.ass',
170
- }
171
-
172
- type shareItems = Partial<
173
- Pick<
174
- UniDMTools.UniDMObj,
175
- 'SOID' | 'senderID' | 'platform' | 'SOID' | 'pool' | 'mode' | 'color'
176
- >
177
- >
178
- type statItems = Partial<
179
- Pick<
180
- UniDMTools.UniDMObj,
181
- | 'SOID'
182
- | 'mode'
183
- | 'fontsize'
184
- | 'color'
185
- | 'senderID'
186
- | 'content'
187
- | 'weight'
188
- | 'pool'
189
- | 'platform'
190
- >
191
- >
192
- type Stats<T extends keyof statItems> = Map<statItems[T], number>
193
-
194
- export interface Options {
195
- dedupe?: boolean
196
- /**
197
- * @description
198
- * 当为`false`时,关闭DMID生成; 当为正整数时,表示生成DMID的截取位数; 或可传入一个DMID生成器实例
199
- */
200
- dmid?: boolean | number | UniIDTools.DMIDGenerator
201
- }
202
-
203
- export const convert2Formats = [
204
- DM_format.DanuniJson,
205
- DM_format.DanuniMinJson,
206
- DM_format.DanuniPbBin,
207
- DM_format.BiliXml,
208
- DM_format.DplayerJson,
209
- DM_format.ArtplayerJson,
210
- DM_format.DDPlayJson,
211
- ] as const
212
- export type Convert2Formats =
213
- | DM_format.DanuniJson
214
- | DM_format.DanuniMinJson
215
- | DM_format.DanuniPbBin
216
- | DM_format.BiliXml
217
- | DM_format.DplayerJson
218
- | DM_format.ArtplayerJson
219
- | DM_format.DDPlayJson
220
- type Convert2ResultMap = {
221
- [DM_format.DanuniJson]: UniDM[]
222
- [DM_format.DanuniMinJson]: DM_JSON_DanuniMin
223
- [DM_format.DanuniPbBin]: Uint8Array
224
- [DM_format.BiliXml]: string
225
- [DM_format.DplayerJson]: DM_JSON_Dplayer & { danuni?: DanUniConvertTip }
226
- [DM_format.ArtplayerJson]: DM_JSON_Artplayer & { danuni?: DanUniConvertTip }
227
- [DM_format.DDPlayJson]: DM_JSON_DDPlay & { danuni?: DanUniConvertTip }
228
- }
229
-
230
- export class UniPool {
231
- constructor(
232
- public dans: UniDM[],
233
- public options: Options = {},
234
- public info = {
235
- /**
236
- * 是否从已被转换过的第三方格式弹幕再次转换而来
237
- */
238
- fromConverted: false,
239
- },
240
- ) {
241
- if (options.dedupe !== false) options.dedupe = true
242
- if (this.options.dedupe) this.dedupe()
243
- }
244
- pipe<T extends (...args: any) => any>(fn: T): ReturnType<T> {
245
- return fn(this)
246
- }
247
- /**
248
- * @deprecated 使用 `getShared` 代替
249
- */
250
- get shared(): shareItems {
251
- if (this.dans.length === 0) return {}
252
- const keys: (keyof shareItems)[] = [
253
- 'SOID',
254
- 'senderID',
255
- 'platform',
256
- 'pool',
257
- 'mode',
258
- 'color',
259
- ]
260
- const result: shareItems = {} as shareItems
261
- for (const key of keys) {
262
- const sharedVal = this.getShared(key)
263
- if (sharedVal !== undefined) {
264
- result[key] = sharedVal as any
265
- }
266
- }
267
- return result
268
- }
269
- getShared<K extends keyof shareItems>(key: K): shareItems[K] {
270
- if (this.dans.length === 0) return undefined
271
- const firstVal = this.dans[0][key]
272
- for (let i = 1; i < this.dans.length; i++) {
273
- if (this.dans[i][key] !== firstVal) {
274
- return undefined
275
- }
276
- }
277
- return firstVal
278
- }
279
- getStat<K extends keyof statItems>(key: K): Stats<K> {
280
- const statMap = new Map<statItems[K], number>()
281
- for (const dan of this.dans) {
282
- const val = dan[key]
283
- statMap.set(val, (statMap.get(val) || 0) + 1)
284
- }
285
- return statMap
286
- }
287
- getMost<K extends keyof statItems>(key: K) {
288
- const stats = this.getStat(key)
289
- if (stats.size === 0) return { val: undefined, count: 0 }
290
- let mostVal: statItems[K] | undefined
291
- let maxCount = 0
292
- for (const [val, count] of stats.entries()) {
293
- if (count > maxCount) {
294
- maxCount = count
295
- mostVal = val
296
- }
297
- }
298
- return { val: mostVal, count: maxCount }
299
- }
300
- /**
301
- * @deprecated 使用 `getMost` 代替
302
- */
303
- get most() {
304
- const keys: (keyof statItems)[] = [
305
- 'mode',
306
- 'fontsize',
307
- 'color',
308
- 'senderID',
309
- 'content',
310
- 'weight',
311
- 'pool',
312
- 'platform',
313
- ]
314
- const statMaps = new Map<
315
- keyof statItems,
316
- Map<statItems[keyof statItems], number>
317
- >()
318
- for (const dan of this.dans) {
319
- for (const key of keys) {
320
- if (!statMaps.has(key)) {
321
- statMaps.set(key, new Map())
322
- }
323
- const statMap = statMaps.get(key)!
324
- const val = dan[key]
325
- statMap.set(val, (statMap.get(val) || 0) + 1)
326
- }
327
- }
328
- const result: Record<string, any> = {}
329
- for (const key of keys) {
330
- const statMap = statMaps.get(key)!
331
- let mostVal: statItems[keyof statItems] | undefined
332
- let maxCount = 0
333
- for (const [val, count] of statMap.entries()) {
334
- if (count > maxCount) {
335
- maxCount = count
336
- mostVal = val
337
- }
338
- }
339
- result[key] = mostVal
340
- }
341
- return {
342
- mode: result.mode as UniDMTools.Modes,
343
- fontsize: result.fontsize as number,
344
- color: result.color as number,
345
- senderID: result.senderID as string,
346
- content: result.content as string,
347
- weight: result.weight as number,
348
- pool: result.pool as UniDMTools.Pools,
349
- platform: result.platform as string | undefined,
350
- }
351
- }
352
- static create(options?: Options) {
353
- return new UniPool([], options)
354
- }
355
- /**
356
- * 合并弹幕/弹幕库
357
- */
358
- assign(dans: UniPool | UniDM | UniDM[]) {
359
- if (dans instanceof UniPool) {
360
- return new UniPool(
361
- [...this.dans, ...dans.dans],
362
- { ...this.options, ...dans.options },
363
- { ...this.info, ...dans.info },
364
- )
365
- } else if (dans instanceof UniDM) {
366
- return new UniPool([...this.dans, dans], this.options, this.info)
367
- } else if (Array.isArray(dans) && dans.every((d) => d instanceof UniDM)) {
368
- return new UniPool([...this.dans, ...dans], this.options, this.info)
369
- } else return this
370
- }
371
- /**
372
- * 按共通属性拆分弹幕库
373
- */
374
- split(key: keyof shareItems) {
375
- if (this.getShared(key)) return [this]
376
- const set = new Set(this.dans.map((d) => d[key]))
377
- return [...set].map((v) => {
378
- return new UniPool(
379
- this.dans.filter((d) => d[key] === v),
380
- { ...this.options, dedupe: false },
381
- this.info,
382
- )
383
- })
384
- }
385
- /**
386
- * 基于DMID的基本去重功能,用于解决该class下dans为array而非Set的问题
387
- */
388
- private dedupe() {
389
- // 这里基本上没有性能瓶颈(大文件测试与AI优化下无明显区别)
390
- if (this.options.dmid !== false) {
391
- const map = new Map()
392
- this.dans.forEach((d) => map.set(d.DMID || d.toDMID(), d))
393
- this.dans = [...map.values()]
394
- }
395
- this.options.dedupe = false
396
- }
397
- /**
398
- * 合并一定时间段内的重复弹幕,防止同屏出现过多
399
- * @param lifetime 查重时间区段,单位秒 (默认为 0,表示不查重)
400
- */
401
- merge(lifetime = 0) {
402
- if (!this.getShared('SOID')) {
403
- console.error(
404
- "本功能仅支持同弹幕库内使用,可先 .split('SOID') 在分别使用",
405
- )
406
- return this
407
- }
408
- if (lifetime <= 0) return this
409
- const result: UniDM[] = []
410
- const cache: Record<string, UniDM> = {}
411
- const mergeObj: Record<string, UniDMTools.ExtraDanUniMerge> = {}
412
- // 第一遍:合并弹幕
413
- for (const danmaku of this.dans) {
414
- const key = `${danmaku.content}|${danmaku.mode}|${danmaku.pool}|${danmaku.platform}`
415
- const cached = cache[key]
416
-
417
- if (
418
- cached &&
419
- danmaku.progress - cached.progress <= lifetime &&
420
- danmaku.isSameAs(cached, { skipDanuniMerge: true })
421
- ) {
422
- // 更新已存在的弹幕
423
- mergeObj[key].senders.push(danmaku.senderID)
424
- mergeObj[key].count = mergeObj[key].senders.length
425
- mergeObj[key].taolu_count = mergeObj[key].count
426
- mergeObj[key].taolu_senders = mergeObj[key].senders
427
- mergeObj[key].duration = Number.parseFloat(
428
- (danmaku.progress - cached.progress).toFixed(3),
429
- )
430
- cache[key] = danmaku
431
- } else {
432
- // 新弹幕
433
- mergeObj[key] = {
434
- count: 1,
435
- duration: 0,
436
- senders: [danmaku.senderID],
437
- taolu_count: 1,
438
- taolu_senders: [danmaku.senderID],
439
- }
440
- cache[key] = danmaku
441
- result.push(danmaku)
442
- }
443
- }
444
- // 第二遍:更新 extraStr
445
- for (const danmaku of result) {
446
- const key = `${danmaku.content}|${danmaku.mode}|${danmaku.pool}|${danmaku.platform}`
447
- const mergeData = mergeObj[key]
448
- const extra = danmaku.extra
449
- if (mergeData.count > 1) {
450
- // 多个发送者:设置为机器人并添加保护标记
451
- danmaku.senderID = 'merge[bot]@dan-any'
452
- if (!danmaku.attr) {
453
- danmaku.attr = [UniDMTools.DMAttr.Protect]
454
- } else if (!danmaku.attr.includes(UniDMTools.DMAttr.Protect)) {
455
- danmaku.attr.push(UniDMTools.DMAttr.Protect)
456
- }
457
-
458
- extra.danuni = extra.danuni || {}
459
- extra.danuni.merge = mergeData
460
- danmaku.extraStr = JSON.stringify(extra)
461
- } else {
462
- // 单个发送者:清理 merge 字段
463
- if (extra.danuni?.merge) {
464
- delete extra.danuni.merge
465
- if (Object.keys(extra.danuni).length === 0) {
466
- delete extra.danuni
467
- }
468
- }
469
- danmaku.extraStr =
470
- Object.keys(extra).length > 0 ? JSON.stringify(extra) : undefined
471
- }
472
- }
473
- return new UniPool(result, this.options, this.info)
474
- }
475
- static import(
476
- file: unknown,
477
- options?: Options,
478
- /**
479
- * 加载指定解析模块,为空则全选,为字符串则视为文件名解析加载模块
480
- */
481
- mod?: string | ('json' | 'str' | 'bin')[],
482
- ): { pool: UniPool; fmt: DM_format } {
483
- const handlers = {
484
- [DM_format.DanuniJson]: (json: UniDM[]) => ({
485
- pool: new UniPool(json, options),
486
- fmt: DM_format.DanuniJson,
487
- }),
488
- [DM_format.DanuniMinJson]: (json: DM_JSON_DanuniMin) => ({
489
- pool: this.fromMin(json, options),
490
- fmt: DM_format.DanuniMinJson,
491
- }),
492
- [DM_format.DanuniPbBin]: (
493
- file: ArrayBuffer | Uint8Array<ArrayBufferLike>,
494
- ) => ({
495
- pool: this.fromPb(file),
496
- fmt: DM_format.DanuniPbBin,
497
- }),
498
- [DM_format.BiliXml]: (file: string) => ({
499
- pool: this.fromBiliXML(file, options),
500
- fmt: DM_format.BiliXml,
501
- }),
502
- [DM_format.BiliPbBin]: (
503
- file: ArrayBuffer | Uint8Array<ArrayBufferLike>,
504
- ) => ({
505
- pool: this.fromBiliGrpc(file),
506
- fmt: DM_format.BiliPbBin,
507
- }),
508
- [DM_format.BiliCmdPbBin]: (
509
- file: ArrayBuffer | Uint8Array<ArrayBufferLike>,
510
- ) => ({
511
- pool: this.fromBiliCommandGrpc(file),
512
- fmt: DM_format.BiliCmdPbBin,
513
- }),
514
- [DM_format.BiliUpJson]: (json: DM_JSON_BiliUp) => ({
515
- pool: this.fromBiliUp(json, options),
516
- fmt: DM_format.BiliUpJson,
517
- }),
518
- [DM_format.DplayerJson]: (
519
- json: DM_JSON_Dplayer & {
520
- danuni?: DanUniConvertTip
521
- },
522
- ) => ({
523
- pool: this.fromDplayer(
524
- json,
525
- json.danuni?.data ?? '',
526
- undefined,
527
- options,
528
- ),
529
- fmt: DM_format.DplayerJson,
530
- }),
531
- [DM_format.ArtplayerJson]: (
532
- json: DM_JSON_Artplayer & {
533
- danuni?: DanUniConvertTip
534
- },
535
- ) => ({
536
- pool: this.fromArtplayer(
537
- json,
538
- json.danuni?.data ?? '',
539
- undefined,
540
- options,
541
- ),
542
- fmt: DM_format.ArtplayerJson,
543
- }),
544
- [DM_format.DDPlayJson]: (
545
- json: DM_JSON_DDPlay & {
546
- danuni?: DanUniConvertTip
547
- },
548
- ) => ({
549
- pool: this.fromDDPlay(json, json.danuni?.data ?? '', options),
550
- fmt: DM_format.DDPlayJson,
551
- }),
552
- [DM_format.CommonAss]: (file: string) => ({
553
- pool: this.fromASS(file, options),
554
- fmt: DM_format.CommonAss,
555
- }),
556
- }
557
-
558
- if (typeof mod === 'string') {
559
- const fn = mod
560
- // 直接按照默认模式处理匹配后缀的文件
561
- try {
562
- if (fn.endsWith(DM_format.DanuniJson)) {
563
- return handlers[DM_format.DanuniJson](fileParser(file, 'json'))
564
- } else if (fn.endsWith(DM_format.DanuniMinJson))
565
- return handlers[DM_format.DanuniMinJson](fileParser(file, 'json'))
566
- else if (fn.endsWith(DM_format.DanuniPbBin))
567
- return handlers[DM_format.DanuniPbBin](fileParser(file, 'bin'))
568
- else if (fn.endsWith(DM_format.BiliXml))
569
- return handlers[DM_format.BiliXml](fileParser(file, 'string'))
570
- else if (fn.endsWith(DM_format.BiliPbBin))
571
- return handlers[DM_format.BiliPbBin](fileParser(file, 'bin'))
572
- else if (fn.endsWith(DM_format.BiliCmdPbBin))
573
- return handlers[DM_format.BiliCmdPbBin](fileParser(file, 'bin'))
574
- else if (fn.endsWith(DM_format.BiliUpJson))
575
- return handlers[DM_format.BiliUpJson](fileParser(file, 'json'))
576
- else if (fn.endsWith(DM_format.DplayerJson))
577
- return handlers[DM_format.DplayerJson](fileParser(file, 'json'))
578
- else if (fn.endsWith(DM_format.ArtplayerJson))
579
- return handlers[DM_format.ArtplayerJson](fileParser(file, 'json'))
580
- else if (fn.endsWith(DM_format.DDPlayJson))
581
- return handlers[DM_format.DDPlayJson](fileParser(file, 'json'))
582
- else if (fn.endsWith(DM_format.CommonAss))
583
- return handlers[DM_format.CommonAss](fileParser(file, 'string'))
584
- } catch {}
585
- // 按照后缀设定启用模块
586
- const ext = fn.split('.').pop()?.toLowerCase()
587
- if (ext) {
588
- if (ext === 'json') mod = ['json']
589
- else if (['xml', 'ass'].includes(ext)) mod = ['str']
590
- else if (['binpb', 'bin', 'so'].includes(ext)) mod = ['bin']
591
- }
592
- }
593
- if (!mod) mod = ['json', 'str', 'bin']
594
- const err = '无法识别该文件,请手动指定格式!'
595
- const parseJSON = (
596
- json: DM_JSON_Artplayer &
597
- DM_JSON_DDPlay &
598
- DM_JSON_Dplayer &
599
- DM_JSON_BiliUp & { danuni?: DanUniConvertTip },
600
- ): { pool: UniPool; fmt: DM_format } | undefined => {
601
- try {
602
- if (Array.isArray(json) && json.every((d) => d.SOID)) {
603
- return handlers[DM_format.DanuniMinJson](json) // 使用兼容性参数,danuni.min.json解析器可以解析danuni.json
604
- } else if (json.danmuku && json.danmuku.every((d) => d.text)) {
605
- return handlers[DM_format.ArtplayerJson](json)
606
- } else if (
607
- json.count &&
608
- json.comments &&
609
- Array.isArray(json.comments) &&
610
- json.comments.every((d) => d.m)
611
- ) {
612
- return handlers[DM_format.DDPlayJson](json)
613
- } else if (
614
- json.code == 0 &&
615
- json.data &&
616
- Array.isArray(json.data) &&
617
- json.data.every((d) => Array.isArray(d))
618
- ) {
619
- return handlers[DM_format.DplayerJson](json)
620
- } else if (
621
- json.code == 0 &&
622
- json.message == '0' &&
623
- json.data &&
624
- json.data.page &&
625
- json.data.result &&
626
- Array.isArray(json.data.result) &&
627
- json.data.result.every((d) => d.id && d.oid)
628
- ) {
629
- return handlers[DM_format.BiliUpJson](json)
630
- }
631
- } catch {}
632
- }
633
- const parseStr = (
634
- file: string,
635
- ): { pool: UniPool; fmt: DM_format } | undefined => {
636
- // json-str
637
- if (mod.includes('json'))
638
- try {
639
- if (isJSON(file)) {
640
- const json = JSON.parse(file)
641
- return parseJSON(json)
642
- }
643
- } catch {}
644
- // pure-str (xml/ass)
645
- if (mod.includes('str')) {
646
- try {
647
- const xmlParser = new XMLParser({ ignoreAttributes: false })
648
- const xml = xmlParser.parse(file)
649
- if (xml?.i?.d) return handlers[DM_format.BiliXml](file)
650
- } catch {}
651
- try {
652
- return handlers[DM_format.CommonAss](file)
653
- } catch {}
654
- }
655
- }
656
- let errmesg
657
- if (typeof file === 'object' || Array.isArray(file)) {
658
- if (file instanceof ArrayBuffer || file instanceof Uint8Array) {
659
- // pure-bin (pb)
660
- if (mod.includes('bin')) {
661
- try {
662
- return handlers[DM_format.DanuniPbBin](file)
663
- } catch {}
664
- try {
665
- return handlers[DM_format.BiliPbBin](file)
666
- } catch {}
667
- try {
668
- return handlers[DM_format.BiliCmdPbBin](file)
669
- } catch {}
670
- }
671
- // str-bin (pure-str + json-str)
672
-
673
- try {
674
- const fileStr = new TextDecoder().decode(file)
675
- const prStr = parseStr(fileStr)
676
- if (prStr) {
677
- return prStr
678
- } else {
679
- errmesg = `${err}(定位: bin->string)`
680
- }
681
- } catch {}
682
- } else if (mod.includes('json')) {
683
- // pure-json
684
- const prJSON = parseJSON(file as any)
685
- if (!prJSON) throw new Error(`${err}(定位: json)`)
686
- return prJSON
687
- }
688
- } else if (isString(file)) {
689
- // pure-str + json-str
690
- const prStr = parseStr(file)
691
- if (!prStr) throw new Error(`${err}(定位: string)`)
692
- return prStr
693
- }
694
- throw new Error(errmesg ?? err)
695
- }
696
- convert2<T extends Convert2Formats>(
697
- format: T,
698
- file_wrapper: true,
699
- continue_on_error?: boolean,
700
- ): File
701
- convert2<T extends Convert2Formats>(
702
- format: T,
703
- file_wrapper?: false,
704
- continue_on_error?: boolean,
705
- ): Convert2ResultMap[T]
706
- convert2(
707
- format: DM_format,
708
- file_wrapper = false,
709
- continue_on_error = false,
710
- ): File | Convert2ResultMap[Convert2Formats] {
711
- switch (format) {
712
- case DM_format.DanuniJson:
713
- if (file_wrapper)
714
- return new File([JSON.stringify(this.dans)], DM_format.DanuniJson, {
715
- type: 'application/json',
716
- })
717
- else return this.dans
718
- case DM_format.DanuniMinJson:
719
- if (file_wrapper)
720
- return new File(
721
- [JSON.stringify(this.minify())],
722
- DM_format.DanuniMinJson,
723
- {
724
- type: 'application/json',
725
- },
726
- )
727
- else return this.minify()
728
- case DM_format.DanuniPbBin:
729
- if (file_wrapper)
730
- return new File([this.toPb()], DM_format.DanuniPbBin, {
731
- type: 'application/protobuf',
732
- })
733
- else return this.toPb()
734
- case DM_format.BiliXml:
735
- if (file_wrapper)
736
- return new File([this.toBiliXML()], DM_format.BiliXml, {
737
- type: 'application/xml',
738
- })
739
- else return this.toBiliXML()
740
- // case DM_format.BiliPbBin:
741
- // return this.toBiliBin()
742
- // case DM_format.BiliCmdPbBin:
743
- // return this.toBiliCmdBin()
744
- // case DM_format.BiliUpJson:
745
- // return this.toBiliUp()
746
- case DM_format.DplayerJson:
747
- if (file_wrapper)
748
- return new File(
749
- [JSON.stringify(this.toDplayer())],
750
- DM_format.DplayerJson,
751
- {
752
- type: 'application/json',
753
- },
754
- )
755
- return this.toDplayer()
756
- case DM_format.ArtplayerJson:
757
- if (file_wrapper)
758
- return new File(
759
- [JSON.stringify(this.toArtplayer())],
760
- DM_format.ArtplayerJson,
761
- {
762
- type: 'application/json',
763
- },
764
- )
765
- return this.toArtplayer()
766
- case DM_format.DDPlayJson:
767
- if (file_wrapper)
768
- return new File(
769
- [JSON.stringify(this.toDDPlay())],
770
- DM_format.DDPlayJson,
771
- {
772
- type: 'application/json',
773
- },
774
- )
775
- return this.toDDPlay()
776
- // case DM_format.CommonAss:
777
- // return this.toASS()
778
- default: {
779
- const message = '(err) Unknown format or unsupported now!'
780
- if (continue_on_error) return message
781
- else throw new Error(message)
782
- }
783
- }
784
- }
785
- minify() {
786
- return this.dans.map((d) => d.minify())
787
- }
788
- static fromMin(json: DM_JSON_DanuniMin, options?: Options) {
789
- return new UniPool(json.map((d: any) => UniDM.create(d, options)))
790
- }
791
- static fromPb(bin: Uint8Array | ArrayBuffer, options?: Options) {
792
- const data = fromBinary(ListDanResponseSchema, new Uint8Array(bin))
793
- return new UniPool(
794
- data.danmakus.map((d) =>
795
- UniDM.create(
796
- {
797
- ...d,
798
- SOID: d.soid,
799
- DMID: d.dmid,
800
- progress: d.progress / 1000,
801
- mode: d.mode as number,
802
- senderID: d.senderId,
803
- ctime: timestampDate(d.ctime || timestampNow()),
804
- pool: d.pool as number,
805
- attr: d.attr as UniDMTools.DMAttr[],
806
- extra: undefined,
807
- extraStr: d.extra,
808
- },
809
- options,
810
- ),
811
- ),
812
- options,
813
- )
814
- }
815
- /**
816
- * 转为 protobuf 二进制
817
- */
818
- toPb() {
819
- return toBinary(
820
- ListDanResponseSchema,
821
- create(ListDanResponseSchema, {
822
- danmakus: this.dans.map((d) => {
823
- return {
824
- soid: d.SOID,
825
- dmid: d.DMID ?? '',
826
- progress: Math.round(d.progress * 1000),
827
- mode: d.mode as number,
828
- fontsize: d.fontsize,
829
- color: d.color,
830
- senderId: d.senderID,
831
- content: d.content,
832
- ctime: timestampFromDate(d.ctime),
833
- weight: d.weight,
834
- pool: d.pool as number,
835
- attr: d.attr,
836
- platform: d.platform,
837
- extra: d.extraStr,
838
- } satisfies Omit<Danmaku, '$typeName'>
839
- }),
840
- }),
841
- )
842
- }
843
- static fromBiliXML(xml: string, options?: Options) {
844
- const parser = new XMLParser({
845
- ignoreAttributes: false,
846
- isArray: (_name, jpath, _isLeafNode, _isAttribute) => {
847
- if (jpath === 'i.d') return true
848
- return false
849
- },
850
- })
851
- const oriData: DM_XML_Bili & { i: { danuni?: DanUniConvertTip } } =
852
- parser.parse(xml)
853
- const dans = oriData.i.d
854
- const fromConverted = !!oriData.i.danuni
855
- const cid = BigInt(oriData.i.chatid)
856
- return new UniPool(
857
- dans
858
- .map((d) => {
859
- return UniDM.fromBili(
860
- UniDM.parseBiliSingle(d['@_p'], d['#text']),
861
- cid,
862
- options,
863
- fromConverted ? oriData.i.danuni?.data : undefined,
864
- )
865
- })
866
- .filter((d) => d !== null),
867
- options,
868
- { fromConverted },
869
- )
870
- }
871
- toBiliXML(options?: {
872
- /**
873
- * 当SOID非来源bili时,若此处指定则使用该值为cid,否则使用SOID
874
- */
875
- cid?: bigint
876
- /**
877
- * 跳过command类型的特殊弹幕
878
- */
879
- skipBiliCommand?: boolean
880
- /**
881
- * 当仅含有来自bili的弹幕时,启用将保持发送者标识不含`@`
882
- * @description
883
- * bili的弹幕含midHash(crc),不启用该处使用senderID填充,启用则去除`@bili`部分,提高兼容性
884
- */
885
- avoidSenderIDWithAt?: boolean
886
- }): string {
887
- const genCID = (id?: string) => {
888
- if (id) {
889
- const UniID = ID.fromString(id)
890
- if (UniID.domain === platform.PlatformVideoSource.Bilibili) {
891
- const cid = UniID.id.replaceAll(
892
- `def_${platform.PlatformVideoSource.Bilibili}+`,
893
- '',
894
- )
895
- if (cid) return cid
896
- }
897
- return options?.cid || id
898
- } else return options?.cid || ID.fromNull().toString()
899
- }
900
- if (options?.avoidSenderIDWithAt) {
901
- const ok = this.dans.every((d) =>
902
- d.senderID.endsWith(`@${platform.PlatformVideoSource.Bilibili}`),
903
- )
904
- if (!ok) throw new Error('存在其他来源的senderID,请关闭该功能再试!')
905
- }
906
- let ds = this.dans.map((dan) => dan.toBiliXML(options))
907
- if (options?.skipBiliCommand) ds = ds.filter((d) => d !== null)
908
- const builder = new XMLBuilder({ ignoreAttributes: false })
909
- return builder.build({
910
- '?xml': {
911
- '@_version': '1.0',
912
- // eslint-disable-next-line unicorn/text-encoding-identifier-case
913
- '@_encoding': 'UTF-8',
914
- },
915
- i: {
916
- chatserver: 'chat.bilibili.com',
917
- chatid: genCID(this.dans[0]?.SOID),
918
- mission: 0,
919
- maxlimit: this.dans.length,
920
- state: 0,
921
- real_name: 0,
922
- source: 'k-v',
923
- danuni: { ...DanUniConvertTipTemplate, data: this.getShared('SOID') },
924
- d: ds,
925
- },
926
- })
927
- }
928
- static fromBiliGrpc(bin: Uint8Array | ArrayBuffer, options?: Options) {
929
- const data = fromBinary(DmSegMobileReplySchema, new Uint8Array(bin))
930
- const json = data.elems
931
- return new UniPool(
932
- json.map((d) => {
933
- return UniDM.fromBili(
934
- { ...d, progress: d.progress / 1000 },
935
- d.oid,
936
- options,
937
- )
938
- }),
939
- options,
940
- )
941
- }
942
- /**
943
- * @param bin 符合`DmWebViewReplySchema`(bili视频meta)的protobuf二进制
944
- */
945
- static fromBiliCommandGrpc(bin: Uint8Array | ArrayBuffer, options?: Options) {
946
- const data = fromBinary(DmWebViewReplySchema, new Uint8Array(bin))
947
- const json = data.commandDms
948
- return new UniPool(
949
- json.map((d) => {
950
- return UniDM.fromBiliCommand(d, d.oid, options)
951
- }),
952
- options,
953
- )
954
- }
955
- static fromBiliUp(json: DM_JSON_BiliUp, options?: Options) {
956
- return new UniPool(
957
- json.data.result.map((d) => {
958
- // 处理 attrs 字符串转换为 attr 二进制
959
- // attrs 格式如 "1,13,21",每个数字对应二进制位
960
- const attrBin = d.attrs
961
- ? d.attrs
962
- .split(',')
963
- .map(Number)
964
- .reduce((bin, bitPosition) => bin | (1 << (bitPosition - 1)), 0)
965
- : 0
966
-
967
- return UniDM.fromBili(
968
- {
969
- id: BigInt(d.id_str || d.id),
970
- progress: d.progress / 1000, // 毫秒转秒
971
- mode: d.mode,
972
- fontsize: d.fontsize,
973
- color: Number.parseInt(d.color, 16),
974
- mid: d.mid,
975
- midHash: d.mid_hash,
976
- content: d.msg,
977
- ctime: BigInt(d.ctime),
978
- pool: d.pool,
979
- // idStr: d.id_str,
980
- attr: attrBin,
981
- oid: BigInt(d.oid),
982
- },
983
- BigInt(d.oid),
984
- options,
985
- )
986
- }),
987
- options,
988
- )
989
- }
990
- static fromDplayer(
991
- json: DM_JSON_Dplayer & { danuni?: DanUniConvertTip },
992
- playerID: string,
993
- domain = 'other',
994
- options?: Options,
995
- ) {
996
- return new UniPool(
997
- json.data.map((d) => {
998
- return UniDM.fromDplayer(
999
- {
1000
- content: d[4],
1001
- progress: d[0],
1002
- mode: d[1],
1003
- color: d[2],
1004
- midHash: d[3],
1005
- },
1006
- playerID,
1007
- domain,
1008
- options,
1009
- )
1010
- }),
1011
- options,
1012
- { fromConverted: !!json.danuni },
1013
- )
1014
- }
1015
- toDplayer(): DM_JSON_Dplayer & { danuni?: DanUniConvertTip } {
1016
- return {
1017
- code: 0,
1018
- danuni: {
1019
- ...DanUniConvertTipTemplate,
1020
- data: this.dans[0]?.SOID.split('@')[0],
1021
- },
1022
- data: this.dans.map((dan) => {
1023
- const d = dan.toDplayer()
1024
- return [d.progress, d.mode, d.color, d.midHash, d.content]
1025
- }),
1026
- }
1027
- }
1028
- static fromArtplayer(
1029
- json: DM_JSON_Artplayer & { danuni?: DanUniConvertTip },
1030
- playerID: string,
1031
- domain = 'other',
1032
- options?: Options,
1033
- ) {
1034
- return new UniPool(
1035
- json.danmuku.map((d) => {
1036
- return UniDM.fromArtplayer(
1037
- {
1038
- content: d.text,
1039
- progress: d.time || 0,
1040
- mode: d.mode || 0,
1041
- color: Number((d.color || 'FFFFFF').replace('#', '0x')),
1042
- style: d.style,
1043
- },
1044
- playerID,
1045
- domain,
1046
- options,
1047
- )
1048
- }),
1049
- options,
1050
- { fromConverted: !!json.danuni },
1051
- )
1052
- }
1053
- toArtplayer(): DM_JSON_Artplayer & { danuni?: DanUniConvertTip } {
1054
- return {
1055
- danuni: {
1056
- ...DanUniConvertTipTemplate,
1057
- data: this.dans[0]?.SOID.split('@')[0],
1058
- },
1059
- danmuku: this.dans.map((dan) => {
1060
- const d = dan.toArtplayer()
1061
- return {
1062
- text: d.content,
1063
- time: d.progress,
1064
- mode: d.mode as 0 | 1 | 2,
1065
- color: `#${d.color.toString(16).toUpperCase() || 'FFFFFF'}`,
1066
- border: d.border,
1067
- style: d.style,
1068
- }
1069
- }),
1070
- }
1071
- }
1072
- static fromDDPlay(
1073
- json: DM_JSON_DDPlay & { danuni?: DanUniConvertTip },
1074
- episodeId: string,
1075
- options?: Options,
1076
- ) {
1077
- return new UniPool(
1078
- json.comments.map((d) => {
1079
- const p_arr = d.p.split(',')
1080
- return UniDM.fromDDPlay(
1081
- {
1082
- cid: d.cid,
1083
- color: Number.parseInt(p_arr[2]),
1084
- m: d.m,
1085
- mode: Number.parseInt(p_arr[1]),
1086
- progress: Number.parseFloat(p_arr[0]),
1087
- uid: p_arr[3],
1088
- },
1089
- episodeId,
1090
- undefined, //使用默认
1091
- options,
1092
- )
1093
- }),
1094
- options,
1095
- { fromConverted: !!json.danuni },
1096
- )
1097
- }
1098
- toDDPlay(): DM_JSON_DDPlay & { danuni?: DanUniConvertTip } {
1099
- const episodeId = this.dans[0]?.SOID.split('@')[0].replaceAll(
1100
- `def_${platform.PlatformDanmakuOnlySource.DanDanPlay}+`,
1101
- '',
1102
- )
1103
- return {
1104
- danuni: { ...DanUniConvertTipTemplate, data: episodeId },
1105
- count: this.dans.length,
1106
- comments: this.dans.map((dan) => {
1107
- const d = dan.toDDPlay()
1108
- return {
1109
- cid: d.cid,
1110
- p: `${d.progress},${d.mode},${d.color},${d.uid}`,
1111
- m: d.m,
1112
- }
1113
- }),
1114
- }
1115
- }
1116
- static fromASS(ass: string, options?: Options) {
1117
- return parseAssRawField(ass, options)
1118
- }
1119
- /**
1120
- * 转换为ASS字幕格式的弹幕,需播放器支持多行ASS渲染
1121
- */
1122
- toASS(canvasCtx: CanvasCtx, options?: AssGenOptions): string {
1123
- const defaultOptions: AssGenOptions = { substyle: {} }
1124
- const finalOptions = options ?? defaultOptions
1125
- const fn = this.getShared('SOID')
1126
- return generateASS(
1127
- this,
1128
- { filename: fn, title: fn, ...finalOptions },
1129
- canvasCtx,
1130
- )
1131
- }
1132
- }
1133
-
1134
- export {
1135
- platform,
1136
- // UniPool,
1137
- UniDM,
1138
- UniDMTools,
1139
- UniIDTools,
1140
- type DM_JSON_BiliCommandGrpc,
1141
- // type UniDMType,
1142
- // type UniIDType,
1143
- }
1144
- export * as plugins from './plugins'