lyrics-core 0.4.0

Core types, traits, and helpers for lyrics processing
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use super::lyrics_types::LyricsAlignment;
use super::syllable_info::SyllableItem;

/// 歌词行信息,支持四种变体:简单行、音节行、完整行、完整音节行。
///
/// 所有变体均支持可选的子行(`sub_line`)和对齐方式(`alignment`)。
/// Full 系列变体额外支持翻译和拼音。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LineInfo {
    /// 简单歌词行,包含纯文本和可选的时间戳。
    Line {
        /// 行文本内容
        text: String,
        /// 开始时间(毫秒)
        start_time: Option<i32>,
        /// 结束时间(毫秒)
        end_time: Option<i32>,
        /// 文本对齐方式
        alignment: LyricsAlignment,
        /// 子行(如背景和声)
        sub_line: Option<Box<LineInfo>>,
    },
    /// 音节歌词行,由多个音节组成,无直接文本。
    Syllable {
        /// 音节列表
        syllables: Vec<SyllableItem>,
        /// 文本对齐方式
        alignment: LyricsAlignment,
        /// 子行(如背景和声)
        sub_line: Option<Box<LineInfo>>,
    },
    /// 完整歌词行,在简单行基础上增加翻译和拼音信息。
    FullLine {
        /// 行文本内容
        text: String,
        /// 开始时间(毫秒)
        start_time: Option<i32>,
        /// 结束时间(毫秒)
        end_time: Option<i32>,
        /// 文本对齐方式
        alignment: LyricsAlignment,
        /// 子行(如背景和声)
        sub_line: Option<Box<LineInfo>>,
        /// 翻译映射(键为语言代码,如 `"zh"`)
        translations: HashMap<String, String>,
        /// 拼音/注音
        pronunciation: Option<String>,
    },
    /// 完整音节歌词行,在音节行基础上增加翻译和拼音信息。
    FullSyllable {
        /// 音节列表
        syllables: Vec<SyllableItem>,
        /// 文本对齐方式
        alignment: LyricsAlignment,
        /// 子行(如背景和声)
        sub_line: Option<Box<LineInfo>>,
        /// 翻译映射(键为语言代码,如 `"zh"`)
        translations: HashMap<String, String>,
        /// 拼音/注音
        pronunciation: Option<String>,
    },
}

impl LineInfo {
    /// 创建带可选时间戳的简单歌词行。
    pub fn new_line(text: String, start_time: Option<i32>, end_time: Option<i32>) -> Self {
        Self::Line {
            text,
            start_time,
            end_time,
            alignment: LyricsAlignment::Unspecified,
            sub_line: None,
        }
    }

    /// 创建无时间信息的简单歌词行。
    pub fn new_line_simple(text: String) -> Self {
        Self::Line {
            text,
            start_time: None,
            end_time: None,
            alignment: LyricsAlignment::Unspecified,
            sub_line: None,
        }
    }

    /// 创建仅有开始时间的简单歌词行。
    pub fn new_line_with_time(text: String, start_time: i32) -> Self {
        Self::Line {
            text,
            start_time: Some(start_time),
            end_time: None,
            alignment: LyricsAlignment::Unspecified,
            sub_line: None,
        }
    }

    /// 创建音节歌词行。
    pub fn new_syllable(syllables: Vec<SyllableItem>) -> Self {
        Self::Syllable {
            syllables,
            alignment: LyricsAlignment::Unspecified,
            sub_line: None,
        }
    }

    /// 创建带翻译和拼音的完整歌词行。
    pub fn new_full_line(
        text: String,
        start_time: Option<i32>,
        end_time: Option<i32>,
        translations: HashMap<String, String>,
        pronunciation: Option<String>,
    ) -> Self {
        Self::FullLine {
            text,
            start_time,
            end_time,
            alignment: LyricsAlignment::Unspecified,
            sub_line: None,
            translations,
            pronunciation,
        }
    }

    /// 创建带翻译和拼音的完整音节歌词行。
    pub fn new_full_syllable(
        syllables: Vec<SyllableItem>,
        translations: HashMap<String, String>,
        pronunciation: Option<String>,
    ) -> Self {
        Self::FullSyllable {
            syllables,
            alignment: LyricsAlignment::Unspecified,
            sub_line: None,
            translations,
            pronunciation,
        }
    }

    /// 返回行文本内容(音节行返回空字符串)。
    pub fn text(&self) -> &str {
        match self {
            Self::Line { text, .. } | Self::FullLine { text, .. } => text,
            Self::Syllable { .. } | Self::FullSyllable { .. } => "",
        }
    }

    /// 将音节列表拼接为完整文本字符串。
    pub fn text_from_syllables(syllables: &[SyllableItem]) -> String {
        super::syllable_info::get_text_from_syllable_items(syllables)
    }

    /// 返回开始时间(毫秒)。音节行取第一个音节的开始时间。
    pub fn start_time(&self) -> Option<i32> {
        match self {
            Self::Line { start_time, .. } | Self::FullLine { start_time, .. } => *start_time,
            Self::Syllable { syllables, .. } | Self::FullSyllable { syllables, .. } => {
                syllables.first().map(|s| s.start_time())
            }
        }
    }

    /// 返回结束时间(毫秒)。音节行取最后一个音节的结束时间。
    pub fn end_time(&self) -> Option<i32> {
        match self {
            Self::Line { end_time, .. } | Self::FullLine { end_time, .. } => *end_time,
            Self::Syllable { syllables, .. } | Self::FullSyllable { syllables, .. } => {
                syllables.last().map(|s| s.end_time())
            }
        }
    }

    /// 返回持续时长(毫秒),缺少开始或结束时间时返回 `None`。
    pub fn duration(&self) -> Option<i32> {
        match (self.start_time(), self.end_time()) {
            (Some(s), Some(e)) => Some(e - s),
            _ => None,
        }
    }

    /// 返回文本对齐方式。
    pub fn alignment(&self) -> LyricsAlignment {
        match self {
            Self::Line { alignment, .. }
            | Self::Syllable { alignment, .. }
            | Self::FullLine { alignment, .. }
            | Self::FullSyllable { alignment, .. } => *alignment,
        }
    }

    /// 设置文本对齐方式。
    pub fn set_alignment(&mut self, new_alignment: LyricsAlignment) {
        match self {
            Self::Line { alignment, .. }
            | Self::Syllable { alignment, .. }
            | Self::FullLine { alignment, .. }
            | Self::FullSyllable { alignment, .. } => *alignment = new_alignment,
        }
    }

    /// 返回子行的只读引用(如背景和声)。
    pub fn sub_line(&self) -> Option<&LineInfo> {
        match self {
            Self::Line { sub_line, .. }
            | Self::Syllable { sub_line, .. }
            | Self::FullLine { sub_line, .. }
            | Self::FullSyllable { sub_line, .. } => sub_line.as_deref(),
        }
    }

    /// 返回子行的可变引用(如背景和声)。
    pub fn sub_line_mut(&mut self) -> Option<&mut LineInfo> {
        match self {
            Self::Line { sub_line, .. }
            | Self::Syllable { sub_line, .. }
            | Self::FullLine { sub_line, .. }
            | Self::FullSyllable { sub_line, .. } => sub_line.as_deref_mut(),
        }
    }

    /// 设置子行。
    pub fn set_sub_line(&mut self, new_sub_line: Option<Box<LineInfo>>) {
        match self {
            Self::Line { sub_line, .. }
            | Self::Syllable { sub_line, .. }
            | Self::FullLine { sub_line, .. }
            | Self::FullSyllable { sub_line, .. } => *sub_line = new_sub_line,
        }
    }

    /// 取出子行,原位置留下 `None`。
    pub fn take_sub_line(&mut self) -> Option<Box<LineInfo>> {
        match self {
            Self::Line { sub_line, .. }
            | Self::Syllable { sub_line, .. }
            | Self::FullLine { sub_line, .. }
            | Self::FullSyllable { sub_line, .. } => sub_line.take(),
        }
    }

    /// 返回拼音/注音(仅 Full 系列变体支持)。
    pub fn pronunciation(&self) -> Option<&str> {
        match self {
            Self::FullLine { pronunciation, .. } | Self::FullSyllable { pronunciation, .. } => {
                pronunciation.as_deref()
            }
            _ => None,
        }
    }

    /// 返回音节列表的只读切片(非音节行返回 `None`)。
    pub fn syllables(&self) -> Option<&[SyllableItem]> {
        match self {
            Self::Syllable { syllables, .. } | Self::FullSyllable { syllables, .. } => {
                Some(syllables)
            }
            _ => None,
        }
    }

    /// 返回音节列表的可变引用(非音节行返回 `None`)。
    pub fn syllables_mut(&mut self) -> Option<&mut Vec<SyllableItem>> {
        match self {
            Self::Syllable { syllables, .. } | Self::FullSyllable { syllables, .. } => {
                Some(syllables)
            }
            _ => None,
        }
    }

    /// 判断是否为音节行(`Syllable` 或 `FullSyllable`)。
    pub fn is_syllable(&self) -> bool {
        matches!(self, Self::Syllable { .. } | Self::FullSyllable { .. })
    }

    /// 判断是否为完整行(`FullLine` 或 `FullSyllable`),即包含翻译和拼音。
    pub fn is_full(&self) -> bool {
        matches!(self, Self::FullLine { .. } | Self::FullSyllable { .. })
    }

    /// 返回主行与子行中较早的开始时间。
    pub fn start_time_with_sub_line(&self) -> Option<i32> {
        let main = self.start_time();
        let sub = self.sub_line().and_then(|s| s.start_time());
        crate::helpers::math_helper::min_opt(main, sub)
    }

    /// 返回主行与子行中较晚的结束时间。
    pub fn end_time_with_sub_line(&self) -> Option<i32> {
        let main = self.end_time();
        let sub = self.sub_line().and_then(|s| s.end_time());
        crate::helpers::math_helper::max_opt(main, sub)
    }

    /// 返回包含子行在内的总持续时长(毫秒)。
    pub fn duration_with_sub_line(&self) -> Option<i32> {
        match (
            self.start_time_with_sub_line(),
            self.end_time_with_sub_line(),
        ) {
            (Some(s), Some(e)) => Some(e - s),
            _ => None,
        }
    }

    /// 返回包含子行文本的完整显示字符串。
    ///
    /// 子行文本以括号附加,若子行开始时间早于主行则前置显示。
    pub fn full_text(&self) -> String {
        let text = self.text_from_any();
        let Some(sub) = self.sub_line() else {
            return text;
        };

        let sub_text =
            crate::helpers::string_helper::remove_front_back_brackets(&sub.text_from_any());
        match (sub.start_time(), self.start_time()) {
            (Some(sub_start), Some(start)) if sub_start < start => {
                format!("({}) {}", sub_text, text.trim())
            }
            _ => format!("{} ({})", text.trim(), sub_text),
        }
    }

    /// 从任意变体获取文本内容(音节行从音节列表拼接)。
    pub fn text_from_any(&self) -> String {
        match self {
            Self::Line { text, .. } | Self::FullLine { text, .. } => text.clone(),
            Self::Syllable { syllables, .. } | Self::FullSyllable { syllables, .. } => {
                Self::text_from_syllables(syllables)
            }
        }
    }

    /// 返回翻译映射的只读引用(仅 Full 系列变体支持)。
    pub fn translations(&self) -> Option<&HashMap<String, String>> {
        match self {
            Self::FullLine { translations, .. } | Self::FullSyllable { translations, .. } => {
                Some(translations)
            }
            _ => None,
        }
    }

    /// 返回翻译映射的可变引用(仅 Full 系列变体支持)。
    pub fn translations_mut(&mut self) -> Option<&mut HashMap<String, String>> {
        match self {
            Self::FullLine { translations, .. } | Self::FullSyllable { translations, .. } => {
                Some(translations)
            }
            _ => None,
        }
    }

    /// 返回中文翻译内容(键为 `"zh"`)。
    pub fn chinese_translation(&self) -> Option<&str> {
        self.translations()?.get("zh").map(|s| s.as_str())
    }

    /// 设置中文翻译。传入 `None` 或空字符串时移除该翻译。
    pub fn set_chinese_translation(&mut self, value: Option<String>) {
        if let Some(translations) = self.translations_mut() {
            match value {
                Some(v) if !v.is_empty() => {
                    translations.insert("zh".to_string(), v);
                }
                _ => {
                    translations.remove("zh");
                }
            }
        }
    }

    /// 将 `Line` 转换为 `FullLine`,或 `Syllable` 转换为 `FullSyllable`。
    ///
    /// 已是 Full 系列或其他变体则原样返回。
    pub fn to_full_line(
        self,
        translations: HashMap<String, String>,
        pronunciation: Option<String>,
    ) -> Self {
        match self {
            Self::Line {
                text,
                start_time,
                end_time,
                alignment,
                sub_line,
            } => Self::FullLine {
                text,
                start_time,
                end_time,
                alignment,
                sub_line,
                translations,
                pronunciation,
            },
            Self::Syllable {
                syllables,
                alignment,
                sub_line,
            } => Self::FullSyllable {
                syllables,
                alignment,
                sub_line,
                translations,
                pronunciation,
            },
            other => other,
        }
    }

    /// 将 `Syllable` 转换为 `FullSyllable`,其他变体原样返回。
    pub fn to_full_syllable(
        self,
        translations: HashMap<String, String>,
        pronunciation: Option<String>,
    ) -> Self {
        match self {
            Self::Syllable {
                syllables,
                alignment,
                sub_line,
            } => Self::FullSyllable {
                syllables,
                alignment,
                sub_line,
                translations,
                pronunciation,
            },
            other => other,
        }
    }

    /// 返回开始时间,无时间信息时返回 0。
    pub fn start_time_or_zero(&self) -> i32 {
        self.start_time().unwrap_or(0)
    }
}

impl PartialEq for LineInfo {
    fn eq(&self, other: &Self) -> bool {
        self.start_time() == other.start_time()
    }
}

impl Eq for LineInfo {}

impl PartialOrd for LineInfo {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for LineInfo {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        match (self.start_time(), other.start_time()) {
            (Some(a), Some(b)) => a.cmp(&b),
            _ => std::cmp::Ordering::Equal,
        }
    }
}