@dan-uni/dan-any 1.2.2 → 1.2.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/README.md +1 -0
- package/dist/browser/17.min.js +2 -5
- package/dist/browser/705.min.js +77 -0
- package/dist/browser/plugins/bili.min.js +1 -0
- package/dist/browser/plugins/index.min.js +1 -1
- package/dist/browser/src/index.d.ts +1 -4
- package/dist/browser/src/plugins/bili/history-danmaku-fast-forward.d.ts +18 -0
- package/dist/browser/src/plugins/bili/index.d.ts +2 -0
- package/dist/browser/src/plugins/index.d.ts +1 -1
- package/dist/node/17.js +2 -5
- package/dist/node/705.js +77 -0
- package/dist/node/plugins/bili.js +1 -0
- package/dist/node/plugins/index.js +1 -1
- package/dist/node/src/index.d.ts +1 -4
- package/dist/node/src/plugins/bili/history-danmaku-fast-forward.d.ts +18 -0
- package/dist/node/src/plugins/bili/index.d.ts +2 -0
- package/dist/node/src/plugins/index.d.ts +1 -1
- package/dist/umd/index.umd.min.js +9 -7
- package/dist/umd/plugins/{bili-dedupe.umd.min.js → bili.umd.min.js} +4623 -12
- package/dist/umd/plugins/index.umd.min.js +4625 -14
- package/dist/umd/src/index.d.ts +1 -4
- package/dist/umd/src/plugins/bili/history-danmaku-fast-forward.d.ts +18 -0
- package/dist/umd/src/plugins/bili/index.d.ts +2 -0
- package/dist/umd/src/plugins/index.d.ts +1 -1
- package/package.json +4 -2
- package/rslib.config.ts +1 -1
- package/src/index.ts +1 -11
- package/src/plugins/bili/README.md +87 -0
- package/src/plugins/bili/history-danmaku-fast-forward.ts +86 -0
- package/src/plugins/bili/index.test.ts +129 -0
- package/src/plugins/bili/index.ts +2 -0
- package/src/plugins/index.ts +1 -1
- package/src/plugins/stats/README.md +44 -0
- package/src/plugins/stats/index.test.ts +1 -1
- package/dist/browser/157.min.js +0 -30
- package/dist/browser/plugins/bili-dedupe.min.js +0 -1
- package/dist/node/157.js +0 -30
- package/dist/node/plugins/bili-dedupe.js +0 -1
- package/src/plugins/bili-dedupe/index.test.ts +0 -43
- /package/dist/browser/src/plugins/{bili-dedupe/index.d.ts → bili/dedupe.d.ts} +0 -0
- /package/dist/browser/src/plugins/{bili-dedupe → bili}/index.test.d.ts +0 -0
- /package/dist/node/src/plugins/{bili-dedupe/index.d.ts → bili/dedupe.d.ts} +0 -0
- /package/dist/node/src/plugins/{bili-dedupe → bili}/index.test.d.ts +0 -0
- /package/dist/umd/plugins/{bili-dedupe.umd.min.js.LICENSE.txt → bili.umd.min.js.LICENSE.txt} +0 -0
- /package/dist/umd/src/plugins/{bili-dedupe/index.d.ts → bili/dedupe.d.ts} +0 -0
- /package/dist/umd/src/plugins/{bili-dedupe → bili}/index.test.d.ts +0 -0
- /package/src/plugins/{bili-dedupe/index.ts → bili/dedupe.ts} +0 -0
package/dist/umd/src/index.d.ts
CHANGED
|
@@ -120,8 +120,6 @@ export type DM_format = 'danuni.json' | 'danuni.pb.bin' | 'bili.xml' | 'bili.pb.
|
|
|
120
120
|
type shareItems = Partial<Pick<UniDMTools.UniDMObj, 'SOID' | 'senderID' | 'platform' | 'SOID' | 'pool' | 'mode' | 'color'>>;
|
|
121
121
|
type statItems = Partial<Pick<UniDMTools.UniDMObj, 'SOID' | 'mode' | 'fontsize' | 'color' | 'senderID' | 'content' | 'weight' | 'pool' | 'platform'>>;
|
|
122
122
|
type Stats<T extends keyof statItems> = Map<statItems[T], number>;
|
|
123
|
-
type UniPoolPipe = (that: UniPool) => Promise<any>;
|
|
124
|
-
type UniPoolPipeSync = (that: UniPool) => any;
|
|
125
123
|
export interface Options {
|
|
126
124
|
dedupe?: boolean;
|
|
127
125
|
/**
|
|
@@ -145,8 +143,7 @@ export declare class UniPool {
|
|
|
145
143
|
*/
|
|
146
144
|
fromConverted: boolean;
|
|
147
145
|
});
|
|
148
|
-
pipe<T extends (...args: any) => any
|
|
149
|
-
pipeSync<T extends (...args: any) => any = UniPoolPipeSync>(fn: T): ReturnType<T>;
|
|
146
|
+
pipe<T extends (...args: any) => any>(fn: T): ReturnType<T>;
|
|
150
147
|
/**
|
|
151
148
|
* @deprecated 使用 `getShared` 代替
|
|
152
149
|
*/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 注:该方法受BiliPlus全弹幕下载器启发
|
|
3
|
+
* 详情参考以下链接
|
|
4
|
+
* @see https://github.com/HengXin666/BiLiBiLi_DanMu_Crawling/issues/14
|
|
5
|
+
*/
|
|
6
|
+
import type { UniPool } from '../..';
|
|
7
|
+
interface BiliHistoryDanmakuFastForwardResult {
|
|
8
|
+
earliest: string | null;
|
|
9
|
+
FastForward: string[];
|
|
10
|
+
skip: string[];
|
|
11
|
+
SpecificDate: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function bili_history_fast_forward(
|
|
14
|
+
/**
|
|
15
|
+
* 请求api时使用的日期(UTC+8, yyyy-MM-dd)
|
|
16
|
+
*/
|
|
17
|
+
query_history_date: string): (that: UniPool) => BiliHistoryDanmakuFastForwardResult;
|
|
18
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * as
|
|
1
|
+
export * as bili from './bili';
|
|
2
2
|
export * as stats from './stats';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dan-uni/dan-any",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.5",
|
|
5
5
|
"description": "A danmaku transformer lib, supporting danmaku from different platforms.",
|
|
6
6
|
"author": "rinne",
|
|
7
7
|
"license": "LGPL-3.0-or-later",
|
|
@@ -36,11 +36,12 @@
|
|
|
36
36
|
"base16384": "^1.0.0",
|
|
37
37
|
"class-transformer": "^0.5.1",
|
|
38
38
|
"class-validator": "^0.14.3",
|
|
39
|
-
"fast-xml-parser": "^5.3.
|
|
39
|
+
"fast-xml-parser": "^5.3.4",
|
|
40
40
|
"fs-extra": "^11.3.3",
|
|
41
41
|
"hh-mm-ss": "^1.2.0",
|
|
42
42
|
"json-bigint": "^1.0.0",
|
|
43
43
|
"jssha": "^3.3.1",
|
|
44
|
+
"luxon": "^3.7.2",
|
|
44
45
|
"reflect-metadata": "^0.2.2"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"@types/fs-extra": "^11.0.4",
|
|
50
51
|
"@types/hh-mm-ss": "^1.2.3",
|
|
51
52
|
"@types/json-bigint": "^1.0.4",
|
|
53
|
+
"@types/luxon": "^3.7.1",
|
|
52
54
|
"canvas": "^3.2.1",
|
|
53
55
|
"protobufjs": "^8.0.0"
|
|
54
56
|
}
|
package/rslib.config.ts
CHANGED
|
@@ -8,7 +8,7 @@ export default defineConfig({
|
|
|
8
8
|
entry: {
|
|
9
9
|
index: './src/index.ts',
|
|
10
10
|
'plugins/index': './src/plugins/index.ts',
|
|
11
|
-
'plugins/bili
|
|
11
|
+
'plugins/bili': './src/plugins/bili/index.ts',
|
|
12
12
|
'plugins/stats': './src/plugins/stats/index.ts',
|
|
13
13
|
},
|
|
14
14
|
},
|
package/src/index.ts
CHANGED
|
@@ -185,9 +185,6 @@ type statItems = Partial<
|
|
|
185
185
|
>
|
|
186
186
|
type Stats<T extends keyof statItems> = Map<statItems[T], number>
|
|
187
187
|
|
|
188
|
-
type UniPoolPipe = (that: UniPool) => Promise<any>
|
|
189
|
-
type UniPoolPipeSync = (that: UniPool) => any
|
|
190
|
-
|
|
191
188
|
export interface Options {
|
|
192
189
|
dedupe?: boolean
|
|
193
190
|
/**
|
|
@@ -211,14 +208,7 @@ export class UniPool {
|
|
|
211
208
|
if (options.dedupe !== false) options.dedupe = true
|
|
212
209
|
if (this.options.dedupe) this.dedupe()
|
|
213
210
|
}
|
|
214
|
-
|
|
215
|
-
fn: T,
|
|
216
|
-
): Promise<ReturnType<T>> {
|
|
217
|
-
return fn(this)
|
|
218
|
-
}
|
|
219
|
-
pipeSync<T extends (...args: any) => any = UniPoolPipeSync>(
|
|
220
|
-
fn: T,
|
|
221
|
-
): ReturnType<T> {
|
|
211
|
+
pipe<T extends (...args: any) => any>(fn: T): ReturnType<T> {
|
|
222
212
|
return fn(this)
|
|
223
213
|
}
|
|
224
214
|
/**
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Bili 插件
|
|
2
|
+
|
|
3
|
+
为 B 站(哔哩哔哩)弹幕处理提供的实用工具集合。
|
|
4
|
+
|
|
5
|
+
## 功能模块
|
|
6
|
+
|
|
7
|
+
### 1. 去重 (`dedupe`)
|
|
8
|
+
|
|
9
|
+
针对直接从 B 站 API 获取到的弹幕进行去重处理。
|
|
10
|
+
|
|
11
|
+
#### 导出函数
|
|
12
|
+
|
|
13
|
+
- **`to_bili_deduped(pool: UniPool): UniPool`** - 返回去重后的新 UniPool 实例
|
|
14
|
+
- **`bili_dedupe(pool: UniPool): void`** - 原地修改 UniPool 实例,移除重复弹幕
|
|
15
|
+
|
|
16
|
+
#### 去重示例
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { bili } from '@danuni/dan-any/plugins'
|
|
20
|
+
|
|
21
|
+
const pool = UniPool.import(biliData)
|
|
22
|
+
|
|
23
|
+
// 方式1:获取去重后的新实例
|
|
24
|
+
const _deduped = pool.pipe(bili.bili_dedupe.to_bili_deduped)
|
|
25
|
+
|
|
26
|
+
// 方式2:原地去重
|
|
27
|
+
pool.pipe(bili.bili_dedupe.bili_dedupe)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### 说明
|
|
31
|
+
|
|
32
|
+
- 使用 B 站 API 中的 `dmid`(弹幕 ID)作为去重依据
|
|
33
|
+
- **仅支持** B 站主站直接获取的弹幕(需要包含 `extra.bili.dmid` 字段)
|
|
34
|
+
- 若弹幕缺少 `dmid` 字段会抛出错误
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### 2. 历史弹幕逆向快进算法 (`history-danmaku-fast-forward`)
|
|
39
|
+
|
|
40
|
+
针对 B 站历史弹幕采用时间逆序查询时,快速确定完成获取/下一次获取位置的算法。
|
|
41
|
+
|
|
42
|
+
该方法受 [BiliPlus 全弹幕下载器](https://github.com/HengXin666/BiLiBiLi_DanMu_Crawling/issues/14) 启发。
|
|
43
|
+
|
|
44
|
+
#### 快进算法函数
|
|
45
|
+
|
|
46
|
+
- **`bili_history_fast_forward(query_history_date: string): Function`** - 返回处理函数
|
|
47
|
+
|
|
48
|
+
#### 快进算法示例
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { bili } from '@danuni/dan-any/plugins'
|
|
52
|
+
|
|
53
|
+
const pool = UniPool.import(biliHistoryData)
|
|
54
|
+
|
|
55
|
+
// 查询 2024-01-10 这一天的历史弹幕
|
|
56
|
+
const _result = pool.pipe(bili.bili_history_fast_forward('2024-01-10'))
|
|
57
|
+
|
|
58
|
+
// 返回值示例:
|
|
59
|
+
// {
|
|
60
|
+
// earliest: '2024-01-01', // 最早弹幕的日期
|
|
61
|
+
// FastForward: ['2024-01-01', '2024-01-02', '2024-01-04'], // 有弹幕的日期
|
|
62
|
+
// skip: ['2024-01-03', '2024-01-05', ...], // 无弹幕的日期
|
|
63
|
+
// SpecicDate: '2024-01-10' // 查询的日期
|
|
64
|
+
// }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### 返回值说明
|
|
68
|
+
|
|
69
|
+
| 字段 | 类型 | 说明 |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| `earliest` | `string \| null` | 最早弹幕的日期(`yyyy-MM-dd` 格式),无弹幕时为 `null` |
|
|
72
|
+
| `FastForward` | `string[]` | 早于查询日期、且有弹幕的日期列表 |
|
|
73
|
+
| `skip` | `string[]` | `earliest` 到查询日期之间、无弹幕的日期列表 |
|
|
74
|
+
| `SpecicDate` | `string` | 查询的日期(`yyyy-MM-dd` 格式) |
|
|
75
|
+
|
|
76
|
+
#### 工作原理
|
|
77
|
+
|
|
78
|
+
1. 筛选出早于查询日期的所有弹幕
|
|
79
|
+
2. 找出最早弹幕的日期作为 `earliest`
|
|
80
|
+
3. 收集有弹幕的日期到 `FastForward` 列表
|
|
81
|
+
4. 找出没有弹幕的日期到 `skip` 列表
|
|
82
|
+
|
|
83
|
+
#### 时间处理
|
|
84
|
+
|
|
85
|
+
- 所有日期基于时区 `Asia/Shanghai`(UTC+8)
|
|
86
|
+
- 输入格式:`yyyy-MM-dd`
|
|
87
|
+
- 输出格式:`yyyy-MM-dd`
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 注:该方法受BiliPlus全弹幕下载器启发
|
|
3
|
+
* 详情参考以下链接
|
|
4
|
+
* @see https://github.com/HengXin666/BiLiBiLi_DanMu_Crawling/issues/14
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { DateTime } from 'luxon'
|
|
8
|
+
import type { UniPool } from '../..'
|
|
9
|
+
|
|
10
|
+
interface BiliHistoryDanmakuFastForwardResult {
|
|
11
|
+
earliest: string | null
|
|
12
|
+
FastForward: string[]
|
|
13
|
+
skip: string[]
|
|
14
|
+
SpecificDate: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 针对B站历史弹幕使用时间逆序+快进以确定完成获取/下一次获取位置的算法
|
|
19
|
+
*
|
|
20
|
+
* 传入值是一个包含历史弹幕的UniPool,来自对任意一天(UTC+8, yyyy-MM-dd)的历史弹幕api
|
|
21
|
+
*/
|
|
22
|
+
function main(
|
|
23
|
+
that: UniPool,
|
|
24
|
+
query_history_date: string,
|
|
25
|
+
): BiliHistoryDanmakuFastForwardResult {
|
|
26
|
+
const qhd = DateTime.fromFormat(query_history_date, 'yyyy-MM-dd', {
|
|
27
|
+
zone: 'Asia/Shanghai',
|
|
28
|
+
}).setZone('Asia/Shanghai')
|
|
29
|
+
if (!qhd.isValid) throw new Error('Invalid query_history_date')
|
|
30
|
+
const s = qhd.startOf('day')
|
|
31
|
+
const before = that.dans.filter((d) => d.ctime < s.toJSDate())
|
|
32
|
+
|
|
33
|
+
if (before.length === 0) {
|
|
34
|
+
return {
|
|
35
|
+
earliest: null,
|
|
36
|
+
FastForward: [],
|
|
37
|
+
skip: [],
|
|
38
|
+
SpecificDate: qhd.toFormat('yyyy-MM-dd'),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 获取最早的日期(字符串格式)
|
|
43
|
+
const earliestCtime = before.toSorted(
|
|
44
|
+
(a, b) => a.ctime.getTime() - b.ctime.getTime(),
|
|
45
|
+
)[0].ctime
|
|
46
|
+
const earliestDate = DateTime.fromJSDate(earliestCtime)
|
|
47
|
+
.setZone('Asia/Shanghai')
|
|
48
|
+
.startOf('day')
|
|
49
|
+
|
|
50
|
+
// 提取有弹幕的日期集合
|
|
51
|
+
const datesWithDanmaku = new Set<string>()
|
|
52
|
+
for (const dan of before) {
|
|
53
|
+
const dateStr = DateTime.fromJSDate(dan.ctime)
|
|
54
|
+
.setZone('Asia/Shanghai')
|
|
55
|
+
.toFormat('yyyy-MM-dd')
|
|
56
|
+
datesWithDanmaku.add(dateStr)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 计算earliest到query_history_date之间的所有日期
|
|
60
|
+
let current = earliestDate
|
|
61
|
+
const allDates: string[] = []
|
|
62
|
+
while (current < s) {
|
|
63
|
+
allDates.push(current.toFormat('yyyy-MM-dd'))
|
|
64
|
+
current = current.plus({ days: 1 })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 区分fast-forward和skip
|
|
68
|
+
const FastForward = allDates.filter((d) => datesWithDanmaku.has(d))
|
|
69
|
+
const skip = allDates.filter((d) => !datesWithDanmaku.has(d))
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
earliest: earliestDate.toFormat('yyyy-MM-dd'),
|
|
73
|
+
FastForward,
|
|
74
|
+
skip,
|
|
75
|
+
SpecificDate: qhd.toFormat('yyyy-MM-dd'),
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function bili_history_fast_forward(
|
|
80
|
+
/**
|
|
81
|
+
* 请求api时使用的日期(UTC+8, yyyy-MM-dd)
|
|
82
|
+
*/
|
|
83
|
+
query_history_date: string,
|
|
84
|
+
) {
|
|
85
|
+
return (that: UniPool) => main(that, query_history_date)
|
|
86
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
//基于以下注释,根据vitest生成测试用例
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { UniDM, UniPool } from '../..'
|
|
5
|
+
import { bili_dedupe, to_bili_deduped } from './dedupe'
|
|
6
|
+
import { bili_history_fast_forward } from './history-danmaku-fast-forward'
|
|
7
|
+
|
|
8
|
+
const xml = `<i>
|
|
9
|
+
<chatserver>chat.bilibili.com</chatserver>
|
|
10
|
+
<chatid>1156756312</chatid>
|
|
11
|
+
<mission>0</mission>
|
|
12
|
+
<maxlimit>2947</maxlimit>
|
|
13
|
+
<state>0</state>
|
|
14
|
+
<real_name>0</real_name>
|
|
15
|
+
<source>k-v</source>
|
|
16
|
+
<d p="13.213,1,25,16777215,1686314041,3,ff41173d,1335658005672492032">喜欢</d>
|
|
17
|
+
<d p="13.213,1,25,16777215,1686590010,0,296b35b5,1337972999512832512">来了 哈哈~~</d>
|
|
18
|
+
<d p="13.246,1,25,16777215,1686276875,0,5664cfc4,1335346233459549696">就是</d>
|
|
19
|
+
<d p="13.266,1,25,16777215,1686283375,0,c7e6646f,1335400761013670912">什么鬼?</d>
|
|
20
|
+
<d p="13.284,1,25,16777215,1686291338,0,38662881,1335517923728804864">哇哦</d>
|
|
21
|
+
<d p="13.306,1,25,16777215,1686268410,0,4c01de10,1335275224983600896">试试</d>
|
|
22
|
+
<d p="13.331,1,25,16777215,1686948453,3,56a3c5d5,1340979831550069760">不喜欢</d>
|
|
23
|
+
<d p="13.374,1,25,16777215,1686300770,3,647fe355,1335546672880933888">不喜欢</d>
|
|
24
|
+
<d p="13.376,1,25,16777215,1686297921,0,469d94b8,1335522778300134400">哦豁</d>
|
|
25
|
+
<d p="13.419,1,25,8700107,1686268005,0,be402447,1335271828100244224">太酷啦</d>
|
|
26
|
+
<d p="13.419,1,25,16777215,1686316828,3,7ffb6619,1335681385016736768">喜欢</d>
|
|
27
|
+
<d p="13.459,1,25,16777215,1686299729,0,45834405,1335537942797634048">一般,不好看</d>
|
|
28
|
+
<d p="13.462,1,25,16777215,1686302133,0,3cab672c,1335558106620590080">哈哈哈</d>
|
|
29
|
+
<d p="13.481,1,25,16777215,1686297342,0,ce67fafd,1335517923728804864">?</d>
|
|
30
|
+
<d p="13.499,1,25,16777215,1686301548,3,2848bf1c,1335517923728804864">不喜欢</d>
|
|
31
|
+
</i>`
|
|
32
|
+
|
|
33
|
+
describe('B站弹幕相关', () => {
|
|
34
|
+
const pool = UniPool.fromBiliXML(xml)
|
|
35
|
+
it('B站dmid去重(仅限主站直接获取的弹幕)', () => {
|
|
36
|
+
const ori = pool.dans.length
|
|
37
|
+
expect(ori).toBe(15)
|
|
38
|
+
const n = pool.pipe(to_bili_deduped)
|
|
39
|
+
expect(n.dans.length).toBe(13)
|
|
40
|
+
expect(pool.dans.length).toBe(15)
|
|
41
|
+
pool.pipe(bili_dedupe)
|
|
42
|
+
expect(pool.dans.length).toBe(13)
|
|
43
|
+
})
|
|
44
|
+
describe('B站历史弹幕反向快进算法', () => {
|
|
45
|
+
it('正常测试', () => {
|
|
46
|
+
// Mock一些历史弹幕数据:跨越2024-01-01到2024-01-10
|
|
47
|
+
const mockDans = [
|
|
48
|
+
// 2024-01-01 有弹幕
|
|
49
|
+
UniDM.create({
|
|
50
|
+
SOID: '123@bili',
|
|
51
|
+
content: 'test1',
|
|
52
|
+
progress: 10,
|
|
53
|
+
ctime: new Date('2024-01-01T10:00:00+08:00'),
|
|
54
|
+
}),
|
|
55
|
+
// 2024-01-02 有弹幕
|
|
56
|
+
UniDM.create({
|
|
57
|
+
SOID: '123@bili',
|
|
58
|
+
content: 'test2',
|
|
59
|
+
progress: 20,
|
|
60
|
+
ctime: new Date('2024-01-02T15:30:00+08:00'),
|
|
61
|
+
}),
|
|
62
|
+
// 2024-01-03 无弹幕(跳过)
|
|
63
|
+
// 2024-01-04 有弹幕
|
|
64
|
+
UniDM.create({
|
|
65
|
+
SOID: '123@bili',
|
|
66
|
+
content: 'test3',
|
|
67
|
+
progress: 30,
|
|
68
|
+
ctime: new Date('2024-01-04T08:00:00+08:00'),
|
|
69
|
+
}),
|
|
70
|
+
// 2024-01-05 到 2024-01-09 无弹幕(跳过)
|
|
71
|
+
// 2024-01-10 有弹幕(但这是查询日期,不应该包含在结果中)
|
|
72
|
+
UniDM.create({
|
|
73
|
+
SOID: '123@bili',
|
|
74
|
+
content: 'test4',
|
|
75
|
+
progress: 40,
|
|
76
|
+
ctime: new Date('2024-01-10T12:00:00+08:00'),
|
|
77
|
+
}),
|
|
78
|
+
// 2024-01-11 有弹幕(晚于查询日期,不应该包含)
|
|
79
|
+
UniDM.create({
|
|
80
|
+
SOID: '123@bili',
|
|
81
|
+
content: 'test5',
|
|
82
|
+
progress: 50,
|
|
83
|
+
ctime: new Date('2024-01-11T09:00:00+08:00'),
|
|
84
|
+
}),
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
const pool = new UniPool(mockDans)
|
|
88
|
+
const result = pool.pipe(bili_history_fast_forward('2024-01-10'))
|
|
89
|
+
|
|
90
|
+
// 验证结果
|
|
91
|
+
expect(result.earliest).toBe('2024-01-01')
|
|
92
|
+
expect(result.SpecificDate).toBe('2024-01-10')
|
|
93
|
+
|
|
94
|
+
// fast-forward 应该包含有弹幕的日期(2024-01-01, 02, 04)
|
|
95
|
+
expect(result.FastForward).toEqual([
|
|
96
|
+
'2024-01-01',
|
|
97
|
+
'2024-01-02',
|
|
98
|
+
'2024-01-04',
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
// skip 应该包含没有弹幕的日期(2024-01-03, 05-09)
|
|
102
|
+
expect(result.skip).toEqual([
|
|
103
|
+
'2024-01-03',
|
|
104
|
+
'2024-01-05',
|
|
105
|
+
'2024-01-06',
|
|
106
|
+
'2024-01-07',
|
|
107
|
+
'2024-01-08',
|
|
108
|
+
'2024-01-09',
|
|
109
|
+
])
|
|
110
|
+
})
|
|
111
|
+
it('无历史弹幕', () => {
|
|
112
|
+
const mockDans = [
|
|
113
|
+
UniDM.create({
|
|
114
|
+
SOID: '123@bili',
|
|
115
|
+
content: 'test',
|
|
116
|
+
progress: 10,
|
|
117
|
+
ctime: new Date('2024-01-10T12:00:00+08:00'),
|
|
118
|
+
}),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
const pool = new UniPool(mockDans)
|
|
122
|
+
const result = pool.pipe(bili_history_fast_forward('2024-01-10'))
|
|
123
|
+
|
|
124
|
+
expect(result.earliest).toBe(null)
|
|
125
|
+
expect(result.FastForward).toEqual([])
|
|
126
|
+
expect(result.skip).toEqual([])
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
})
|
package/src/plugins/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * as
|
|
1
|
+
export * as bili from './bili'
|
|
2
2
|
export * as stats from './stats'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 统计插件
|
|
2
|
+
|
|
3
|
+
为弹幕池提供统计和查询功能的工具集合。
|
|
4
|
+
|
|
5
|
+
## 功能模块
|
|
6
|
+
|
|
7
|
+
### 获取最新弹幕 (`getLatestDan`)
|
|
8
|
+
|
|
9
|
+
从弹幕池中查找出最新发送的一条弹幕。
|
|
10
|
+
|
|
11
|
+
#### 导出函数
|
|
12
|
+
|
|
13
|
+
- **`getLatestDan(pool: UniPool): UniDM | null`** - 获取最新的一条弹幕
|
|
14
|
+
|
|
15
|
+
#### 使用示例
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { stats } from '@danuni/dan-any/plugins'
|
|
19
|
+
|
|
20
|
+
const pool = UniPool.import(biliData)
|
|
21
|
+
|
|
22
|
+
// 获取最新弹幕
|
|
23
|
+
const latest = pool.pipe(stats.getLatestDan)
|
|
24
|
+
|
|
25
|
+
if (latest) {
|
|
26
|
+
console.log(`最新弹幕: ${latest.content}`)
|
|
27
|
+
console.log(`发送时间: ${latest.ctime}`)
|
|
28
|
+
} else {
|
|
29
|
+
console.log('弹幕池为空')
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### 返回值
|
|
34
|
+
|
|
35
|
+
- **`UniDM`** - 最新的弹幕对象(包含内容、时间、发送者等信息)
|
|
36
|
+
- **`null`** - 当弹幕池为空时
|
|
37
|
+
|
|
38
|
+
#### 工作原理
|
|
39
|
+
|
|
40
|
+
按照弹幕的创建时间(`ctime`)进行比较,通过遍历整个弹幕池找出时间戳最新的一条弹幕。
|
|
41
|
+
|
|
42
|
+
#### 时间复杂度
|
|
43
|
+
|
|
44
|
+
- 平均/最坏情况:**O(n)** — n 是弹幕数量
|
|
@@ -31,7 +31,7 @@ const xml = `<i>
|
|
|
31
31
|
describe('统计', () => {
|
|
32
32
|
it('获取最新的一条弹幕', () => {
|
|
33
33
|
const pool = UniPool.fromBiliXML(xml)
|
|
34
|
-
const latest = pool.
|
|
34
|
+
const latest = pool.pipe(getLatestDan)
|
|
35
35
|
expect(latest).not.toBeNull()
|
|
36
36
|
expect(latest?.content).toBe('不喜欢')
|
|
37
37
|
expect(latest?.ctime).toStrictEqual(new Date(1686948453 * 1000))
|
package/dist/browser/157.min.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { __webpack_require__ } from "./rslib-runtime.min.js";
|
|
2
|
-
import { UniPool, platform_PlatformVideoSource } from "./17.min.js";
|
|
3
|
-
var bili_dedupe_namespaceObject = {};
|
|
4
|
-
__webpack_require__.r(bili_dedupe_namespaceObject);
|
|
5
|
-
__webpack_require__.d(bili_dedupe_namespaceObject, {
|
|
6
|
-
bili_dedupe: ()=>bili_dedupe,
|
|
7
|
-
to_bili_deduped: ()=>to_bili_deduped
|
|
8
|
-
});
|
|
9
|
-
function main(that) {
|
|
10
|
-
that.dans.forEach((d)=>{
|
|
11
|
-
if (d.platform !== platform_PlatformVideoSource.Bilibili) throw new Error('bili-dedupe: 仅支持B站(主站)的弹幕');
|
|
12
|
-
if (!d.extra.bili?.dmid) throw new Error('bili-dedupe: 弹幕缺少bili extra dmid字段');
|
|
13
|
-
});
|
|
14
|
-
const map = new Map();
|
|
15
|
-
that.dans.forEach((d)=>map.set(d.extra.bili.dmid, d));
|
|
16
|
-
return map;
|
|
17
|
-
}
|
|
18
|
-
function to_bili_deduped(that) {
|
|
19
|
-
const map = main(that);
|
|
20
|
-
return new UniPool([
|
|
21
|
-
...map.values()
|
|
22
|
-
], that.options, that.info);
|
|
23
|
-
}
|
|
24
|
-
function bili_dedupe(that) {
|
|
25
|
-
const map = main(that);
|
|
26
|
-
that.dans = [
|
|
27
|
-
...map.values()
|
|
28
|
-
];
|
|
29
|
-
}
|
|
30
|
-
export { bili_dedupe as bili_dedupe_0, bili_dedupe_namespaceObject as bili_dedupe, to_bili_deduped };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { bili_dedupe_0 as bili_dedupe, to_bili_deduped } from "../157.min.js";
|
package/dist/node/157.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { __webpack_require__ } from "./rslib-runtime.js";
|
|
2
|
-
import { UniPool, platform_PlatformVideoSource } from "./17.js";
|
|
3
|
-
var bili_dedupe_namespaceObject = {};
|
|
4
|
-
__webpack_require__.r(bili_dedupe_namespaceObject);
|
|
5
|
-
__webpack_require__.d(bili_dedupe_namespaceObject, {
|
|
6
|
-
bili_dedupe: ()=>bili_dedupe,
|
|
7
|
-
to_bili_deduped: ()=>to_bili_deduped
|
|
8
|
-
});
|
|
9
|
-
function main(that) {
|
|
10
|
-
that.dans.forEach((d)=>{
|
|
11
|
-
if (d.platform !== platform_PlatformVideoSource.Bilibili) throw new Error('bili-dedupe: 仅支持B站(主站)的弹幕');
|
|
12
|
-
if (!d.extra.bili?.dmid) throw new Error('bili-dedupe: 弹幕缺少bili extra dmid字段');
|
|
13
|
-
});
|
|
14
|
-
const map = new Map();
|
|
15
|
-
that.dans.forEach((d)=>map.set(d.extra.bili.dmid, d));
|
|
16
|
-
return map;
|
|
17
|
-
}
|
|
18
|
-
function to_bili_deduped(that) {
|
|
19
|
-
const map = main(that);
|
|
20
|
-
return new UniPool([
|
|
21
|
-
...map.values()
|
|
22
|
-
], that.options, that.info);
|
|
23
|
-
}
|
|
24
|
-
function bili_dedupe(that) {
|
|
25
|
-
const map = main(that);
|
|
26
|
-
that.dans = [
|
|
27
|
-
...map.values()
|
|
28
|
-
];
|
|
29
|
-
}
|
|
30
|
-
export { bili_dedupe as bili_dedupe_0, bili_dedupe_namespaceObject as bili_dedupe, to_bili_deduped };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { bili_dedupe_0 as bili_dedupe, to_bili_deduped } from "../157.js";
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
//基于以下注释,根据vitest生成测试用例
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { bili_dedupe, to_bili_deduped } from '.'
|
|
5
|
-
import { UniPool } from '../..'
|
|
6
|
-
|
|
7
|
-
const xml = `<i>
|
|
8
|
-
<chatserver>chat.bilibili.com</chatserver>
|
|
9
|
-
<chatid>1156756312</chatid>
|
|
10
|
-
<mission>0</mission>
|
|
11
|
-
<maxlimit>2947</maxlimit>
|
|
12
|
-
<state>0</state>
|
|
13
|
-
<real_name>0</real_name>
|
|
14
|
-
<source>k-v</source>
|
|
15
|
-
<d p="13.213,1,25,16777215,1686314041,3,ff41173d,1335658005672492032">喜欢</d>
|
|
16
|
-
<d p="13.213,1,25,16777215,1686590010,0,296b35b5,1337972999512832512">来了 哈哈~~</d>
|
|
17
|
-
<d p="13.246,1,25,16777215,1686276875,0,5664cfc4,1335346233459549696">就是</d>
|
|
18
|
-
<d p="13.266,1,25,16777215,1686283375,0,c7e6646f,1335400761013670912">什么鬼?</d>
|
|
19
|
-
<d p="13.284,1,25,16777215,1686291338,0,38662881,1335517923728804864">哇哦</d>
|
|
20
|
-
<d p="13.306,1,25,16777215,1686268410,0,4c01de10,1335275224983600896">试试</d>
|
|
21
|
-
<d p="13.331,1,25,16777215,1686948453,3,56a3c5d5,1340979831550069760">不喜欢</d>
|
|
22
|
-
<d p="13.374,1,25,16777215,1686300770,3,647fe355,1335546672880933888">不喜欢</d>
|
|
23
|
-
<d p="13.376,1,25,16777215,1686297921,0,469d94b8,1335522778300134400">哦豁</d>
|
|
24
|
-
<d p="13.419,1,25,8700107,1686268005,0,be402447,1335271828100244224">太酷啦</d>
|
|
25
|
-
<d p="13.419,1,25,16777215,1686316828,3,7ffb6619,1335681385016736768">喜欢</d>
|
|
26
|
-
<d p="13.459,1,25,16777215,1686299729,0,45834405,1335537942797634048">一般,不好看</d>
|
|
27
|
-
<d p="13.462,1,25,16777215,1686302133,0,3cab672c,1335558106620590080">哈哈哈</d>
|
|
28
|
-
<d p="13.481,1,25,16777215,1686297342,0,ce67fafd,1335517923728804864">?</d>
|
|
29
|
-
<d p="13.499,1,25,16777215,1686301548,3,2848bf1c,1335517923728804864">不喜欢</d>
|
|
30
|
-
</i>`
|
|
31
|
-
|
|
32
|
-
describe('其它', () => {
|
|
33
|
-
const pool = UniPool.fromBiliXML(xml)
|
|
34
|
-
it('B站dmid去重(仅限主站直接获取的弹幕)', () => {
|
|
35
|
-
const ori = pool.dans.length
|
|
36
|
-
expect(ori).toBe(15)
|
|
37
|
-
const n = pool.pipeSync(to_bili_deduped)
|
|
38
|
-
expect(n.dans.length).toBe(13)
|
|
39
|
-
expect(pool.dans.length).toBe(15)
|
|
40
|
-
pool.pipeSync(bili_dedupe)
|
|
41
|
-
expect(pool.dans.length).toBe(13)
|
|
42
|
-
})
|
|
43
|
-
})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/dist/umd/plugins/{bili-dedupe.umd.min.js.LICENSE.txt → bili.umd.min.js.LICENSE.txt}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|