Skip to content

Commit 7940295

Browse files
committed
feat: implement parseLrc function with improved parsing logic and add corresponding tests
1 parent eaa24ec commit 7940295

File tree

6 files changed

+300
-13
lines changed

6 files changed

+300
-13
lines changed

src/string/parseLrc.ts

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,56 @@ import { parseTime } from './parseTime'
33
/**
44
* 解析歌词字符串
55
* 得到一个歌词对象的数组
6-
* 每个歌词对象
7-
* {time: 开始时间, words: 歌词内容}
8-
* @param lrc
6+
* 每个歌词对象包含 {time: 开始时间(秒), words: 歌词内容}
7+
* @param { string } lrc 歌词字符串,格式如 "[00:12.34]歌词内容"
8+
* @returns { ParseLrcResult[] } 解析后的歌词数组
9+
* @example
10+
* const lrc = `[00:12.34]第一句歌词
11+
* [00:25.67]第二句歌词`
12+
* parseLrc(lrc)
13+
* // => [
14+
* // { time: 12.34, words: '第一句歌词' },
15+
* // { time: 25.67, words: '第二句歌词' }
16+
* // ]
917
*/
1018

1119
interface ParseLrcResult {
1220
time: number
1321
words: string
1422
}
15-
export function parseLrc(lrc: string) {
23+
export function parseLrc(lrc: string): ParseLrcResult[] {
1624
const lines = lrc.split('\n')
1725
return lines.reduce((result, str) => {
18-
const parts = str.split(']')
19-
const timeStr = parts[0].slice(1)
20-
result.push({
21-
time: parseTime(timeStr),
22-
words: parts[1],
23-
})
26+
// 过滤空行和无效行
27+
const trimmedStr = str.trim()
28+
if (
29+
!trimmedStr
30+
|| !trimmedStr.startsWith('[')
31+
|| !trimmedStr.includes(']')
32+
) {
33+
return result
34+
}
35+
36+
const parts = trimmedStr.split(']')
37+
if (parts.length < 2) {
38+
return result
39+
}
40+
41+
const timeStr = parts[0].slice(1) // 移除开头的 '['
42+
const words = parts.slice(1).join(']').trim() // 处理歌词中可能包含 ']' 的情况
43+
44+
try {
45+
const time = parseTime(timeStr)
46+
result.push({
47+
time,
48+
words,
49+
})
50+
}
51+
catch (error) {
52+
// 如果时间解析失败,跳过这一行
53+
console.warn(`Failed to parse time from: ${timeStr}`)
54+
}
55+
2456
return result
2557
}, [] as ParseLrcResult[])
2658
}

test/string/parseLrc.lrc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[00:00.000]LRC-toomic.com
2+
[00:00]稻香 (2015中国好声音第四季现场) - 周杰伦/徐林/Will Jay
3+
[00:05]词:周杰伦
4+
[00:06]曲:周杰伦
5+
[00:22]对这个世界如果你有太多的抱怨
6+
[00:25]跌倒了就不敢继续往前走
7+
[00:28]为什么人要这么的脆弱堕落
8+
[00:32]请你打开电视看看
9+
[00:33]多少人为生命在努力勇敢的走下去
10+
[00:37]我们是不是该知足
11+
[00:39]珍惜一切就算没有拥有
12+
[00:44]还记得你说家是唯一的城堡
13+
[00:47]随着稻香河流继续奔跑
14+
[00:50]微微笑小时候的梦我知道
15+
[00:55]不要哭让萤火虫带着你逃跑
16+
[00:59]乡间的歌谣永远的依靠
17+
[01:01]回家吧回到最初的美好
18+
[01:29]不要这么容易就想放弃
19+
[01:31]就像我说的
20+
[01:33]追不到的梦想换个梦不就得了
21+
[01:36]为自己的人生鲜艳上色
22+
[01:38]先把爱涂上喜欢的颜色
23+
[01:40]笑一个吧功成名就不是目的
24+
[01:43]让自己快乐快乐这才叫做意义
25+
[01:46]童年的纸飞机
26+
[01:48]现在终于飞回我手里
27+
[01:52]所谓的那快乐
28+
[01:53]赤脚在田里追蜻蜓追到累了
29+
[01:56]偷摘水果被蜜蜂给叮到怕了
30+
[01:59]谁在偷笑呢
31+
[02:00]我靠着稻草人吹着风唱着歌睡着了
32+
[02:04]哦哦哦哦
33+
[02:10]珍惜一切就算没有拥有
34+
[02:14]还记得你说家是唯一的城堡
35+
[02:18]随着稻香河流继续奔跑
36+
[02:21]微微笑小时候的梦我知道
37+
[02:26]不要哭让萤火虫带着你逃跑
38+
[02:29]乡间的歌谣永远的依靠
39+
[02:32]回家吧回到最初的美好
40+
[02:37]还记得你说家是唯一的城堡
41+
[02:40]随着稻香河流继续奔跑
42+
[02:43]微微笑小时候的梦我知道
43+
[02:48]不要哭让萤火虫带着你逃跑
44+
[02:52]乡间的歌谣永远的依靠
45+
[02:54]回家吧回到最初的美好

test/string/parseLrc.test.ts

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { parseLrc } from '../../src/string/parseLrc'
3+
import lrc from './parseLrc.lrc?raw'
4+
5+
describe('parseLrc test', () => {
6+
it('test', async () => {
7+
expect(parseLrc(lrc)).toMatchInlineSnapshot(`
8+
[
9+
{
10+
"time": 0,
11+
"words": "LRC-toomic.com",
12+
},
13+
{
14+
"time": 0,
15+
"words": "稻香 (2015中国好声音第四季现场) - 周杰伦/徐林/Will Jay",
16+
},
17+
{
18+
"time": 5,
19+
"words": "词:周杰伦",
20+
},
21+
{
22+
"time": 6,
23+
"words": "曲:周杰伦",
24+
},
25+
{
26+
"time": 22,
27+
"words": "对这个世界如果你有太多的抱怨",
28+
},
29+
{
30+
"time": 25,
31+
"words": "跌倒了就不敢继续往前走",
32+
},
33+
{
34+
"time": 28,
35+
"words": "为什么人要这么的脆弱堕落",
36+
},
37+
{
38+
"time": 32,
39+
"words": "请你打开电视看看",
40+
},
41+
{
42+
"time": 33,
43+
"words": "多少人为生命在努力勇敢的走下去",
44+
},
45+
{
46+
"time": 37,
47+
"words": "我们是不是该知足",
48+
},
49+
{
50+
"time": 39,
51+
"words": "珍惜一切就算没有拥有",
52+
},
53+
{
54+
"time": 44,
55+
"words": "还记得你说家是唯一的城堡",
56+
},
57+
{
58+
"time": 47,
59+
"words": "随着稻香河流继续奔跑",
60+
},
61+
{
62+
"time": 50,
63+
"words": "微微笑小时候的梦我知道",
64+
},
65+
{
66+
"time": 55,
67+
"words": "不要哭让萤火虫带着你逃跑",
68+
},
69+
{
70+
"time": 59,
71+
"words": "乡间的歌谣永远的依靠",
72+
},
73+
{
74+
"time": 61,
75+
"words": "回家吧回到最初的美好",
76+
},
77+
{
78+
"time": 89,
79+
"words": "不要这么容易就想放弃",
80+
},
81+
{
82+
"time": 91,
83+
"words": "就像我说的",
84+
},
85+
{
86+
"time": 93,
87+
"words": "追不到的梦想换个梦不就得了",
88+
},
89+
{
90+
"time": 96,
91+
"words": "为自己的人生鲜艳上色",
92+
},
93+
{
94+
"time": 98,
95+
"words": "先把爱涂上喜欢的颜色",
96+
},
97+
{
98+
"time": 100,
99+
"words": "笑一个吧功成名就不是目的",
100+
},
101+
{
102+
"time": 103,
103+
"words": "让自己快乐快乐这才叫做意义",
104+
},
105+
{
106+
"time": 106,
107+
"words": "童年的纸飞机",
108+
},
109+
{
110+
"time": 108,
111+
"words": "现在终于飞回我手里",
112+
},
113+
{
114+
"time": 112,
115+
"words": "所谓的那快乐",
116+
},
117+
{
118+
"time": 113,
119+
"words": "赤脚在田里追蜻蜓追到累了",
120+
},
121+
{
122+
"time": 116,
123+
"words": "偷摘水果被蜜蜂给叮到怕了",
124+
},
125+
{
126+
"time": 119,
127+
"words": "谁在偷笑呢",
128+
},
129+
{
130+
"time": 120,
131+
"words": "我靠着稻草人吹着风唱着歌睡着了",
132+
},
133+
{
134+
"time": 124,
135+
"words": "哦哦哦哦",
136+
},
137+
{
138+
"time": 130,
139+
"words": "珍惜一切就算没有拥有",
140+
},
141+
{
142+
"time": 134,
143+
"words": "还记得你说家是唯一的城堡",
144+
},
145+
{
146+
"time": 138,
147+
"words": "随着稻香河流继续奔跑",
148+
},
149+
{
150+
"time": 141,
151+
"words": "微微笑小时候的梦我知道",
152+
},
153+
{
154+
"time": 146,
155+
"words": "不要哭让萤火虫带着你逃跑",
156+
},
157+
{
158+
"time": 149,
159+
"words": "乡间的歌谣永远的依靠",
160+
},
161+
{
162+
"time": 152,
163+
"words": "回家吧回到最初的美好",
164+
},
165+
{
166+
"time": 157,
167+
"words": "还记得你说家是唯一的城堡",
168+
},
169+
{
170+
"time": 160,
171+
"words": "随着稻香河流继续奔跑",
172+
},
173+
{
174+
"time": 163,
175+
"words": "微微笑小时候的梦我知道",
176+
},
177+
{
178+
"time": 168,
179+
"words": "不要哭让萤火虫带着你逃跑",
180+
},
181+
{
182+
"time": 172,
183+
"words": "乡间的歌谣永远的依靠",
184+
},
185+
{
186+
"time": 174,
187+
"words": "回家吧回到最初的美好",
188+
},
189+
]
190+
`)
191+
})
192+
})

tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
"module": "esnext",
77
"moduleResolution": "node",
88
"resolveJsonModule": true,
9+
"typeRoots": ["./node_modules/@types", "./types.d.ts"],
910
"strict": true,
1011
"strictNullChecks": true,
1112
"esModuleInterop": true,
1213
"skipDefaultLibCheck": true,
1314
"skipLibCheck": true
1415
},
15-
"exclude": ["dist", "playground", "cypress", "test"]
16+
"include": ["src/**/*", "test/**/*", "types.d.ts"],
17+
"exclude": ["dist", "playground", "cypress"]
1618
}

types.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// 声明 .lrc 文件的模块类型
2+
declare module '*.lrc' {
3+
const content: string
4+
export default content
5+
}
6+
7+
// 声明 .lrc?raw 的模块类型
8+
declare module '*.lrc?raw' {
9+
const content: string
10+
export default content
11+
}

vitest.config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
import { defineConfig } from 'vite'
1+
import { defineConfig } from 'vitest/config'
22

3-
export default defineConfig({})
3+
export default defineConfig({
4+
test: {
5+
// 配置测试环境
6+
},
7+
assetsInclude: ['**/*.lrc'], // 让 Vite 将 .lrc 文件视为资源
8+
})

0 commit comments

Comments
 (0)