Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite the lrc parser logic. #69

Merged
merged 7 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 51 additions & 17 deletions LrcParser.Tests/Parser/Lrc/Lines/LrcLyricParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ namespace LrcParser.Tests.Parser.Lrc.Lines;
public class LrcLyricParserTest : BaseSingleLineParserTest<LrcLyricParser, LrcLyric>
{
[TestCase("[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", true)]
[TestCase("karaoke", true)]
[TestCase("[00:17:97]<00:00.00>帰<00:00.00>り<00:00.00>道<00:00.00>は<00:00.00>", true)]
[TestCase("karaoke", true)] // depends on the config, might be parsed but no time, or being ignored.
[TestCase("", false)]
[TestCase(null, false)]
public void TestCanDecode(string text, bool expected)
Expand All @@ -33,34 +34,56 @@ public void TestDecode(string lyric, LrcLyric expected)
private static IEnumerable<object[]> testDecodeSource => new object[][]
{
[
"[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]",
"[00:17.00] <00:00.00>帰<00:01.00>り<00:02.00>道<00:03.00>は<00:04.00>",
new LrcLyric
{
Text = "帰り道は",
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]),
StartTimes = [17000],
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:0", "[1,start]:1000", "[2,start]:2000", "[3,start]:3000", "[3,end]:4000"]),
},
],
[
"[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]",
"[00:17.00] 帰<00:01.00>り<00:02.00>道<00:03.00>は<00:04.00>",
new LrcLyric
{
Text = "帰り道は",
TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]),
StartTimes = [17000],
TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:1000", "[2,start]:2000", "[3,start]:3000", "[3,end]:4000"]),
},
],
[
"[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は",
"[00:17.00] <00:00.00>帰<00:01.00>り<00:02.00>道<00:03.00>は",
new LrcLyric
{
Text = "帰り道は",
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940"]),
StartTimes = [17000],
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:0", "[1,start]:1000", "[2,start]:2000", "[3,start]:3000"]),
},
],
[
"[00:17.00] 帰り道は",
new LrcLyric
{
Text = "帰り道は",
StartTimes = [17000],
TimeTags = [],
},
],
[
"[00:17.00][00:18.00] 帰り道は",
new LrcLyric
{
Text = "帰り道は",
StartTimes = [17000, 18000],
TimeTags = [],
},
],
[
"帰り道は",
new LrcLyric
{
Text = "帰り道は",
StartTimes = [],
TimeTags = [],
},
],
Expand All @@ -85,9 +108,16 @@ public void TestDecode(string lyric, LrcLyric expected)
[TestCaseSource(nameof(testEncodeSource))]
public void TestEncode(LrcLyric lyric, string expected)
{
var actual = Encode(lyric);
if (string.IsNullOrEmpty(expected))
{
Assert.That(() => Encode(lyric), Throws.InvalidOperationException);
}
else
{
var actual = Encode(lyric);

Assert.That(actual, Is.EqualTo(expected));
Assert.That(actual, Is.EqualTo(expected));
}
}

private static IEnumerable<object[]> testEncodeSource => new object[][]
Expand All @@ -96,41 +126,45 @@ public void TestEncode(LrcLyric lyric, string expected)
new LrcLyric
{
Text = "帰り道は",
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]),
StartTimes = [17000],
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:0", "[1,start]:1000", "[2,start]:2000", "[3,start]:3000", "[3,end]:4000"]),
},
"[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]",
"[00:17.00] <00:00.00>帰<00:01.00>り<00:02.00>道<00:03.00>は<00:04.00>",
],
[
new LrcLyric
{
Text = "帰り道は",
TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:18370", "[2,start]:18550", "[3,start]:18940", "[3,end]:19220"]),
StartTimes = [17000],
TimeTags = TestCaseTagHelper.ParseTimeTags(["[1,start]:1000", "[2,start]:2000", "[3,start]:3000", "[3,end]:4000"]),
},
"[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]",
"[00:17.00] 帰<00:01.00>り<00:02.00>道<00:03.00>は<00:04.00>",
],
[
new LrcLyric
{
Text = "帰り道は",
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:17970", "[1,start]:18370", "[2,start]:18550", "[3,start]:18940"]),
StartTimes = [17000],
TimeTags = TestCaseTagHelper.ParseTimeTags(["[0,start]:0", "[1,start]:1000", "[2,start]:2000", "[3,start]:3000"]),
},
"[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は",
"[00:17.00] <00:00.00>帰<00:01.00>り<00:02.00>道<00:03.00>は",
],
[
new LrcLyric
{
Text = "帰り道は",
StartTimes = [17000],
TimeTags = [],
},
"帰り道は",
"[00:17.00] 帰り道は",
],
[
new LrcLyric
{
Text = "",
TimeTags = [],
},
"",
null!,
],
};
}
26 changes: 14 additions & 12 deletions LrcParser.Tests/Parser/Lrc/LrcParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public void TestDecode()
{
var lrcText = new[]
{
"[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]",
"[00:17.00] <00:00.00>帰<00:01.00>り<00:02.00>道<00:03.00>は<00:04.00>",
};

var song = new Song
Expand All @@ -24,13 +24,14 @@ public void TestDecode()
new Lyric
{
Text = "帰り道は",
StartTime = 17000,
TimeTags = new SortedDictionary<TextIndex, int?>
{
{ new TextIndex(0), 17970 },
{ new TextIndex(1), 18370 },
{ new TextIndex(2), 18550 },
{ new TextIndex(3), 18940 },
{ new TextIndex(3, IndexState.End), 19220 },
{ new TextIndex(0), 17000 },
{ new TextIndex(1), 18000 },
{ new TextIndex(2), 19000 },
{ new TextIndex(3), 20000 },
{ new TextIndex(3, IndexState.End), 21000 },
},
},
],
Expand All @@ -49,21 +50,22 @@ public void TestEncode()
new Lyric
{
Text = "帰り道は",
StartTime = 17000,
TimeTags = new SortedDictionary<TextIndex, int?>
{
{ new TextIndex(0), 17970 },
{ new TextIndex(1), 18370 },
{ new TextIndex(2), 18550 },
{ new TextIndex(3), 18940 },
{ new TextIndex(3, IndexState.End), 19220 },
{ new TextIndex(0), 17000 },
{ new TextIndex(1), 18000 },
{ new TextIndex(2), 19000 },
{ new TextIndex(3), 20000 },
{ new TextIndex(3, IndexState.End), 21000 },
},
},
],
};

var lrcText = new[]
{
"[00:17.97]帰[00:18.37]り[00:18.55]道[00:18.94]は[00:19.22]",
"[00:17.00] <00:00.00>帰<00:01.00>り<00:02.00>道<00:03.00>は<00:04.00>",
};

checkEncode(song, lrcText);
Expand Down
105 changes: 105 additions & 0 deletions LrcParser.Tests/Parser/Lrc/Utils/LrcStartTimeUtilsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) karaoke.dev <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using LrcParser.Parser.Lrc.Utils;
using NUnit.Framework;

namespace LrcParser.Tests.Parser.Lrc.Utils;

public class LrcStartTimeUtilsTest
{
#region Decode

[TestCase("[1:00.00] ", new[] { 60000 }, "")]
[TestCase("[1:00.00][1:02.00] Lyric", new[] { 60000, 62000 }, "Lyric")]
[TestCase("[1:00.00]Lyric", new[] { 60000 }, "Lyric")] // With no spacing.
[TestCase("[1:00.00] Lyric", new[] { 60000 }, "Lyric")] // With lots of spacing.
[TestCase("[1:00.00] <00:00.04> Lyric <00:00.16>", new[] { 60000 }, "<00:00.04> Lyric <00:00.16>")] // With time-tag.
[TestCase("[1:00.00] <00:00.04> Lyric", new[] { 60000 }, "<00:00.04> Lyric")] // With time-tag.
[TestCase("[1:00.00] <00:00.04> Lyric ", new[] { 60000 }, "<00:00.04> Lyric")] // Remove the end spacing.
public void TestDecodeWithValidLine(string line, int[] expectedStartTimes, string lyric)
{
var actual = LrcStartTimeUtils.SplitLyricAndTimeTag(line);

Assert.That(actual.Item1, Is.EqualTo(expectedStartTimes));
Assert.That(actual.Item2, Is.EqualTo(lyric));
}

[TestCase("Lyric", new int[] { }, "Lyric")] // With no start time.
[TestCase(" Lyric", new int[] { }, "Lyric")] // With no start time.
[TestCase("<00:00.04> Lyric <00:00.16>", new int[] { }, "<00:00.04> Lyric <00:00.16>")] // With no start time but with time-tag.
public void TestDecodeWithInvalidLine(string line, int[] expectedStartTimes, string lyric)
{
var actual = LrcStartTimeUtils.SplitLyricAndTimeTag(line);

// still return the value, but let outside handle the invalid value.
Assert.That(actual.Item1, Is.EqualTo(expectedStartTimes));
Assert.That(actual.Item2, Is.EqualTo(lyric));
}

[TestCase("[00:00.00]", 0)]
[TestCase("[00:06.00]", 6000)]
[TestCase("[01:00.00]", 60000)]
[TestCase("[10:00.00]", 600000)]
[TestCase("[100:00.00]", 6000000)]
[TestCase("[0:00.00]", 0)] // prevent throw error in some invalid format.
[TestCase("[0:0.0]", 0)] // prevent throw error in some invalid format.
[TestCase("[1:00.00][1:02.00]", 60000)] // rarely to get this case, so return the first one.
public void TestConvertTimeTagToMillionSecond(string timeTag, int expectedMillionSecond)
{
var actual = LrcStartTimeUtils.ConvertTimeTagToMillionSecond(timeTag);

Assert.That(actual, Is.EqualTo(expectedMillionSecond));
}

[TestCase("[--:--.--]")]
[TestCase("[]")]
[TestCase("<1:00.00>")] // should not contains embedded time-tag.
public void TestConvertTimeTagToMillionSecondWithInvalidValue(string timeTag)
{
Assert.Throws<InvalidOperationException>(() => LrcStartTimeUtils.ConvertTimeTagToMillionSecond(timeTag));
}

#endregion

#region Encode

[TestCase(new[] { 60000 }, "Lyric", "[01:00.00] Lyric")]
[TestCase(new[] { 60000, 62000 }, "Lyric", "[01:00.00][01:02.00] Lyric")]
[TestCase(new[] { 60000 }, "<00:00.04> Lyric <00:00.16>", "[01:00.00] <00:00.04> Lyric <00:00.16>")] // With time-tag.
[TestCase(new[] { 60000 }, " Lyric", "[01:00.00] Lyric")] // Start spacing will be removed automatically.
public void TestEncodeWithValidValue(int[] startTimes, string lyric, string expectedLine)
{
var actual = LrcStartTimeUtils.JoinLyricAndTimeTag(startTimes, lyric);

Assert.That(actual, Is.EqualTo(expectedLine));
}

[TestCase(new int[] { }, "Lyric")] // With no start time.
[TestCase(new int[] { }, "[00:00.00] Lyric")] // Lyric should not contains any start time-tag info.
public void TestEncodeWithInvalidValue(int[] startTimes, string expectedLine)
{
Assert.Throws<InvalidOperationException>(() => LrcStartTimeUtils.JoinLyricAndTimeTag(startTimes, expectedLine));
}

[TestCase(0, "[00:00.00]")]
[TestCase(6000, "[00:06.00]")]
[TestCase(60000, "[01:00.00]")]
[TestCase(600000, "[10:00.00]")]
[TestCase(6000000, "[100:00.00]")]
public void TestConvertMillionSecondToTimeTag(int millionSecond, string expectedTimeTag)
{
var actual = LrcStartTimeUtils.ConvertMillionSecondToTimeTag(millionSecond);

Assert.That(actual, Is.EqualTo(expectedTimeTag));
}

[TestCase(-1)]
public void TestConvertMillionSecondToTimeTagWithInvalidValue(int millionSecond)
{
Assert.Throws<InvalidOperationException>(() => LrcStartTimeUtils.ConvertMillionSecondToTimeTag(millionSecond));
}

#endregion
}
Loading
Loading