diff --git a/LrcParser.Tests/Parser/Lrc/Lines/LrcRubyParserTest.cs b/LrcParser.Tests/Parser/Lrc/Lines/LrcRubyParserTest.cs deleted file mode 100644 index 18fab03..0000000 --- a/LrcParser.Tests/Parser/Lrc/Lines/LrcRubyParserTest.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) karaoke.dev . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using LrcParser.Parser.Lrc.Lines; -using LrcParser.Parser.Lrc.Metadata; -using LrcParser.Tests.Helper; -using LrcParser.Tests.Parser.Lines; -using NUnit.Framework; - -namespace LrcParser.Tests.Parser.Lrc.Lines; - -public class LrcRubyParserTest : BaseSingleLineParserTest -{ - [TestCase("@Ruby1=帰,かえ", true)] - [TestCase("", false)] - [TestCase(null, false)] - [TestCase("[00:17:97]帰[00:18:37]り[00:18:55]道[00:18:94]は[00:19:22]", false)] - [TestCase("karaoke", false)] - public void TestCanDecode(string text, bool expected) - { - var actual = CanDecode(text); - Assert.That(actual, Is.EqualTo(expected)); - } - - [TestCase("@Ruby1=帰,かえ,[00:53:19],[01:24:77]", "帰", "かえ", new string[] { }, 53190, 84770)] - [TestCase("@Ruby1=帰,かえ,[01:24:77]", "帰", "かえ", new string[] { }, 84770, null)] - [TestCase("@Ruby1=帰,かえ,,[01:24:77]", "帰", "かえ", new string[] { }, null, 84770)] - [TestCase("@Ruby1=帰,かえ", "帰", "かえ", new string[] { }, null, null)] - [TestCase("@Ruby1=帰,か[00:00:50]え", "帰", "かえ", new[] { "[1,start]:500" }, null, null)] - public void TestDecode(string rubyTag, string parent, string ruby, string[] timeTags, int? startTime, int? endTime) - { - var expected = new LrcRuby - { - Parent = parent, - Ruby = ruby, - TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), - StartTime = startTime, - EndTime = endTime, - }; - var actual = Decode(rubyTag); - - Assert.That(actual.Ruby, Is.EqualTo(expected.Ruby)); - Assert.That(actual.Parent, Is.EqualTo(expected.Parent)); - Assert.That(actual.TimeTags, Is.EqualTo(expected.TimeTags)); - Assert.That(actual.StartTime, Is.EqualTo(expected.StartTime)); - Assert.That(actual.EndTime, Is.EqualTo(expected.EndTime)); - } - - [TestCase("帰", "かえ", new string[] { }, 53190, 84770, "@Ruby1=帰,かえ,[00:53.19],[01:24.77]")] - [TestCase("帰", "かえ", new string[] { }, 84770, null, "@Ruby1=帰,かえ,[01:24.77]")] - [TestCase("帰", "かえ", new string[] { }, null, 84770, "@Ruby1=帰,かえ,,[01:24.77]")] - [TestCase("帰", "かえ", new string[] { }, null, null, "@Ruby1=帰,かえ")] - [TestCase("帰", "かえ", new[] { "[1,start]:500" }, null, null, "@Ruby1=帰,か[00:00.50]え")] - public void TestEncode(string parent, string ruby, string[] timeTags, int? startTime, int? endTime, string expected) - { - var rubyTag = new LrcRuby - { - Parent = parent, - Ruby = ruby, - TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), - StartTime = startTime, - EndTime = endTime, - }; - var actual = Encode(rubyTag); - - Assert.That(actual, Is.EqualTo(expected)); - } -} diff --git a/LrcParser.Tests/Parser/Lrc/LrcParserTest.cs b/LrcParser.Tests/Parser/Lrc/LrcParserTest.cs index 27f42cd..d0c5f22 100644 --- a/LrcParser.Tests/Parser/Lrc/LrcParserTest.cs +++ b/LrcParser.Tests/Parser/Lrc/LrcParserTest.cs @@ -39,352 +39,6 @@ public void TestDecode() checkDecode(lrcText, song); } - [Test] - public void TestDecodeWithRuby() - { - var lrcText = new[] - { - "[00:01:00]島[00:02:00]島[00:03:00]島[00:04:00]", - "@Ruby1=島,しま,,[00:02:00]", - "@Ruby2=島,じま,[00:02:00],[00:03:00]", - "@Ruby3=島,とう,[00:03:00]", - }; - - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島島島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(1), 2000 }, - { new TextIndex(2), 3000 }, - { new TextIndex(2, IndexState.End), 4000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - StartCharIndex = 0, - EndCharIndex = 0, - }, - new RubyTag - { - Text = "じま", - StartCharIndex = 1, - EndCharIndex = 1, - }, - new RubyTag - { - Text = "とう", - StartCharIndex = 2, - EndCharIndex = 2, - }, - ], - }, - ], - }; - - checkDecode(lrcText, song); - } - - [Test] - public void TestDecodeWithRubyAndRubyTimeTag() - { - var lrcText = new[] - { - "[00:01:00]島[00:02:00]島[00:03:00]島[00:04:00]", - "@Ruby1=島,し[00:00:50]ま,,[00:02:00]", - "@Ruby2=島,じ[00:00:50]ま,[00:02:00],[00:03:00]", - "@Ruby3=島,と[00:00:50]う,[00:03:00]", - }; - - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島島島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(1), 2000 }, - { new TextIndex(2), 3000 }, - { new TextIndex(2, IndexState.End), 4000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 1500 }, - }, - StartCharIndex = 0, - EndCharIndex = 0, - }, - - new RubyTag - { - Text = "じま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 2500 }, - }, - StartCharIndex = 1, - EndCharIndex = 1, - }, - - new RubyTag - { - Text = "とう", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 3500 }, - }, - StartCharIndex = 2, - EndCharIndex = 2, - }, - ], - }, - ], - }; - - checkDecode(lrcText, song); - } - - [Test] - public void TestDecodeWithSameRubyWithDifferentRubyTimeTag() - { - var lrcText = new[] - { - "[00:01:00]島[00:02:00]島[00:03:00]島[00:04:00]", - "@Ruby1=島,し[00:00:40]ま,,[00:02:00]", - "@Ruby2=島,し[00:00:50]ま,[00:02:00],[00:03:00]", - "@Ruby3=島,し[00:00:60]ま,[00:03:00]", - }; - - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島島島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(1), 2000 }, - { new TextIndex(2), 3000 }, - { new TextIndex(2, IndexState.End), 4000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 1400 }, - }, - StartCharIndex = 0, - EndCharIndex = 0, - }, - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 2500 }, - }, - StartCharIndex = 1, - EndCharIndex = 1, - }, - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 3600 }, - }, - StartCharIndex = 2, - EndCharIndex = 2, - }, - ], - }, - ], - }; - - checkDecode(lrcText, song); - } - - [Test] - public void TestDecodeWithNoTimeRangeRuby() - { - var lrcText = new[] - { - "カラオケ", - "@Ruby1=カ,か", - "@Ruby2=ラ,ら", - "@Ruby3=オ,お", - "@Ruby4=ケ,け", - }; - - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "カラオケ", - RubyTags = - [ - new RubyTag - { - Text = "か", - StartCharIndex = 0, - EndCharIndex = 0, - }, - new RubyTag - { - Text = "ら", - StartCharIndex = 1, - EndCharIndex = 1, - }, - new RubyTag - { - Text = "お", - StartCharIndex = 2, - EndCharIndex = 2, - }, - new RubyTag - { - Text = "け", - StartCharIndex = 3, - EndCharIndex = 3, - }, - ], - }, - ], - }; - - checkDecode(lrcText, song); - } - - [Test] - public void TestDecodeWithRubyInDifferentLine() - { - var lrcText = new[] - { - "[00:01:00]島[00:02:00]", - "[00:03:00]島[00:04:00]", - "[00:05:00]島[00:06:00]", - "@Ruby1=島,しま,,[00:02:00]", - "@Ruby2=島,じま,[00:03:00],[00:04:00]", - "@Ruby3=島,とう,[00:05:00]", - }; - - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(0, IndexState.End), 2000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - StartCharIndex = 0, - EndCharIndex = 0, - }, - ], - }, - - new Lyric - { - Text = "島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 3000 }, - { new TextIndex(0, IndexState.End), 4000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "じま", - StartCharIndex = 0, - EndCharIndex = 0, - }, - ], - }, - - new Lyric - { - Text = "島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 5000 }, - { new TextIndex(0, IndexState.End), 6000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "とう", - StartCharIndex = 0, - EndCharIndex = 0, - }, - ], - }, - ], - }; - - checkDecode(lrcText, song); - } - - [Test] - public void TestDecodeWithInvalid() - { - // should not generate the ruby if ruby text is same as parent text. - var lrcText = new[] - { - "[00:01:00]島[00:02:00]", - "@Ruby1=島,島", - }; - - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(0, IndexState.End), 2000 }, - }, - }, - ], - }; - - checkDecode(lrcText, song); - } - [Test] public void TestEncode() { @@ -415,337 +69,6 @@ public void TestEncode() checkEncode(song, lrcText); } - [Test] - public void TestEncodeWithRuby() - { - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島島島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(1), 2000 }, - { new TextIndex(2), 3000 }, - { new TextIndex(2, IndexState.End), 4000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - StartCharIndex = 0, - EndCharIndex = 0, - }, - new RubyTag - { - Text = "じま", - StartCharIndex = 1, - EndCharIndex = 1, - }, - new RubyTag - { - Text = "とう", - StartCharIndex = 2, - EndCharIndex = 2, - }, - ], - }, - ], - }; - - var lrcText = new[] - { - "[00:01.00]島[00:02.00]島[00:03.00]島[00:04.00]", - "", - "@Ruby1=島,しま,,[00:02.00]", - "@Ruby2=島,じま,[00:02.00],[00:03.00]", - "@Ruby3=島,とう,[00:03.00]", - }; - - checkEncode(song, lrcText); - } - - [Test] - public void TestEncodeWithRubyAndRubyTimeTag() - { - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島島島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(1), 2000 }, - { new TextIndex(2), 3000 }, - { new TextIndex(2, IndexState.End), 4000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 1500 }, - }, - StartCharIndex = 0, - EndCharIndex = 0, - }, - new RubyTag - { - Text = "じま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 2500 }, - }, - StartCharIndex = 1, - EndCharIndex = 1, - }, - new RubyTag - { - Text = "とう", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 3500 }, - }, - StartCharIndex = 2, - EndCharIndex = 2, - }, - ], - }, - ], - }; - - var lrcText = new[] - { - "[00:01.00]島[00:02.00]島[00:03.00]島[00:04.00]", - "", - "@Ruby1=島,し[00:00.50]ま,,[00:02.00]", - "@Ruby2=島,じ[00:00.50]ま,[00:02.00],[00:03.00]", - "@Ruby3=島,と[00:00.50]う,[00:03.00]", - }; - - checkEncode(song, lrcText); - } - - [Test] - public void TestEncodeWithSameRubyWithDifferentRubyTimeTag() - { - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島島島島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(1), 2000 }, - { new TextIndex(2), 3000 }, - { new TextIndex(3), 4000 }, - { new TextIndex(3, IndexState.End), 5000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 1400 }, - }, - StartCharIndex = 0, - EndCharIndex = 0, - }, - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - { new TextIndex(1), 2500 }, - }, - StartCharIndex = 1, - EndCharIndex = 1, - }, - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - // will merge with second time-tag - { new TextIndex(1), 3500 }, - }, - StartCharIndex = 2, - EndCharIndex = 2, - }, - new RubyTag - { - Text = "しま", - TimeTags = new SortedDictionary - { - // although the relative time is same as the first time-tag, but might not be able to merge. - { new TextIndex(1), 4400 }, - }, - StartCharIndex = 3, - EndCharIndex = 3, - }, - ], - }, - ], - }; - - var lrcText = new[] - { - "[00:01.00]島[00:02.00]島[00:03.00]島[00:04.00]島[00:05.00]", - "", - "@Ruby1=島,し[00:00.40]ま,,[00:02.00]", - "@Ruby2=島,し[00:00.50]ま,[00:02.00],[00:04.00]", - "@Ruby3=島,し[00:00.40]ま,[00:04.00]", - }; - - checkEncode(song, lrcText); - } - - [Test] - public void TestEncodeWithNoTimeRangeRuby() - { - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "カラオケ", - RubyTags = - [ - new RubyTag - { - Text = "か", - StartCharIndex = 0, - EndCharIndex = 0, - }, - new RubyTag - { - Text = "ら", - StartCharIndex = 1, - EndCharIndex = 1, - }, - new RubyTag - { - Text = "お", - StartCharIndex = 2, - EndCharIndex = 2, - }, - new RubyTag - { - Text = "け", - StartCharIndex = 3, - EndCharIndex = 3, - }, - ], - }, - ], - }; - - var lrcText = new[] - { - "カラオケ", - "", - "@Ruby1=カ,か", - "@Ruby2=ラ,ら", - "@Ruby3=オ,お", - "@Ruby4=ケ,け", - }; - - checkEncode(song, lrcText); - } - - [Test] - public void TestEncodeWithRubyInDifferentLine() - { - var song = new Song - { - Lyrics = - [ - new Lyric - { - Text = "島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 1000 }, - { new TextIndex(0, IndexState.End), 2000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "しま", - StartCharIndex = 0, - EndCharIndex = 0, - }, - ], - }, - - new Lyric - { - Text = "島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 3000 }, - { new TextIndex(0, IndexState.End), 4000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "じま", - StartCharIndex = 0, - EndCharIndex = 0, - }, - ], - }, - - new Lyric - { - Text = "島", - TimeTags = new SortedDictionary - { - { new TextIndex(0), 5000 }, - { new TextIndex(0, IndexState.End), 6000 }, - }, - RubyTags = - [ - new RubyTag - { - Text = "とう", - StartCharIndex = 0, - EndCharIndex = 0, - }, - ], - }, - ], - }; - - var lrcText = new[] - { - "[00:01.00]島[00:02.00]\n[00:03.00]島[00:04.00]\n[00:05.00]島[00:06.00]", - "", - "@Ruby1=島,しま,,[00:02.00]", - "@Ruby2=島,じま,[00:03.00],[00:04.00]", - "@Ruby3=島,とう,[00:05.00]", - }; - - checkEncode(song, lrcText); - } - [Test] public void TestEncodeWithEmptyFile() { diff --git a/LrcParser/Parser/Lrc/Lines/LrcRubyParser.cs b/LrcParser/Parser/Lrc/Lines/LrcRubyParser.cs deleted file mode 100644 index 11a0616..0000000 --- a/LrcParser/Parser/Lrc/Lines/LrcRubyParser.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) karaoke.dev . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Text.RegularExpressions; -using LrcParser.Extension; -using LrcParser.Parser.Lines; -using LrcParser.Parser.Lrc.Metadata; -using LrcParser.Parser.Lrc.Utils; - -namespace LrcParser.Parser.Lrc.Lines; - -public class LrcRubyParser : SingleLineParser -{ - public override bool CanDecode(string text) - => !string.IsNullOrEmpty(text) && text.ToLower().StartsWith("@ruby", StringComparison.Ordinal); - - public override LrcRuby Decode(string text) - { - var rubyTextRegex = new Regex("@(Ruby|ruby)(?[0-9]+)=(?.*$)"); - - var rubyTagResult = text.Split(','); - var rubyTextResult = rubyTextRegex.Match(rubyTagResult[0]); - - var parent = rubyTextResult.GetGroupValue("text")!; - var (ruby, timeTags) = LrcTimedTextUtils.TimedTextToObject(rubyTagResult[1]); - var startTime = string.IsNullOrEmpty(rubyTagResult.ElementAtOrDefault(2)) ? default(int?) : TimeTagUtils.TimeTagToMillionSecond(rubyTagResult[2]); - var endTime = string.IsNullOrEmpty(rubyTagResult.ElementAtOrDefault(3)) ? default(int?) : TimeTagUtils.TimeTagToMillionSecond(rubyTagResult[3]); - - return new LrcRuby - { - Parent = parent, - Ruby = ruby, - TimeTags = timeTags, - StartTime = startTime, - EndTime = endTime, - }; - } - - public override string Encode(LrcRuby component, int index) - { - var parent = component.Parent; - var ruby = LrcTimedTextUtils.ToTimedText(component.Ruby, component.TimeTags); - var startTime = component.StartTime == null ? "" : TimeTagUtils.MillionSecondToTimeTag(component.StartTime.Value); - var endTime = component.EndTime == null ? "" : TimeTagUtils.MillionSecondToTimeTag(component.EndTime.Value); - - var input = $"@Ruby{index + 1}={parent},{ruby},{startTime},{endTime}"; - - const string remove_last_comma_pattern = "([,]*)$"; - return Regex.Replace(input, remove_last_comma_pattern, ""); - } -} diff --git a/LrcParser/Parser/Lrc/LrcParser.cs b/LrcParser/Parser/Lrc/LrcParser.cs index 5d384a6..09c5d0a 100644 --- a/LrcParser/Parser/Lrc/LrcParser.cs +++ b/LrcParser/Parser/Lrc/LrcParser.cs @@ -1,8 +1,6 @@ // Copyright (c) karaoke.dev . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Text.RegularExpressions; -using LrcParser.Extension; using LrcParser.Model; using LrcParser.Parser.Lrc.Lines; using LrcParser.Parser.Lrc.Metadata; @@ -16,14 +14,12 @@ public class LrcParser : LyricParser { public LrcParser() { - Register(); Register(); } protected override Song PostProcess(List values) { var lyrics = values.OfType(); - var rubies = values.OfType(); return new Song { @@ -31,79 +27,11 @@ protected override Song PostProcess(List values) { Text = l.Text, TimeTags = getTimeTags(l.TimeTags), - RubyTags = getRubyTags(rubies, l).ToList(), }).ToList(), }; - static IEnumerable getRubyTags(IEnumerable rubyTags, LrcLyric lyric) - { - var text = lyric.Text; - var timeTags = lyric.TimeTags; - - foreach (var rubyTag in rubyTags) - { - if (string.IsNullOrEmpty(rubyTag.Ruby) || string.IsNullOrEmpty(rubyTag.Parent)) - continue; - - if (rubyTag.Ruby == rubyTag.Parent) - continue; - - var hasStartTime = rubyTag.StartTime.HasValue; - var hasEndTime = rubyTag.EndTime.HasValue; - - var matches = new Regex(rubyTag.Parent).Matches(text); - - foreach (var match in matches.ToArray()) - { - var startLyricCharIndex = match.Index; - var endLyricCharIndex = startLyricCharIndex + match.Length - 1; - - var startTimeTag = timeTags.Reverse().LastOrDefault(x => x.Key >= new TextIndex(startLyricCharIndex)); - var endTimeTag = timeTags.FirstOrDefault(x => x.Key >= new TextIndex(endLyricCharIndex, IndexState.End)); - - if (!hasStartTime && !hasEndTime) - { - yield return new RubyTag - { - Text = rubyTag.Ruby, - TimeTags = getTimeTags(rubyTag.TimeTags, startTimeTag.Value), - StartCharIndex = startLyricCharIndex, - EndCharIndex = endLyricCharIndex, - }; - } - else - { - // should not add the ruby if is not in the time-range. - if (hasStartTime && rubyTag.StartTime > startTimeTag.Value) - continue; - - if (hasEndTime && rubyTag.EndTime < endTimeTag.Value) - continue; - - yield return new RubyTag - { - Text = rubyTag.Ruby, - TimeTags = getTimeTags(rubyTag.TimeTags, startTimeTag.Value), - StartCharIndex = convertStartTextIndexToCharIndex(startTimeTag.Key), - EndCharIndex = convertEndTextIndexToCharIndex(endTimeTag.Key), - }; - } - } - } - } - static SortedDictionary getTimeTags(SortedDictionary timeTags, int offsetTime = 0) => new(timeTags.ToDictionary(k => k.Key, v => v.Value + offsetTime as int?)); - - static int convertStartTextIndexToCharIndex(TextIndex textIndex) => textIndex.Index; - - static int convertEndTextIndexToCharIndex(TextIndex textIndex) => - textIndex.State switch - { - IndexState.Start => textIndex.Index - 1, - IndexState.End => textIndex.Index, - _ => throw new ArgumentOutOfRangeException(), - }; } protected override IEnumerable PreProcess(Song song) @@ -122,96 +50,15 @@ protected override IEnumerable PreProcess(Song song) // give it a line if contains ruby. if (lyrics.Any(l => l.RubyTags.Any())) - yield return new object(); - - // then, export the ruby. - // should group by parent first because merge the ruby should not be affect by those rubies with different ruby. - var rubiesWithSameParent = lyrics.Select(getRubyTags).SelectMany(x => x).GroupBy(x => x.Parent); - - foreach (var groupWithSameParent in rubiesWithSameParent) { - // should group with continuous ruby. - var rubiesWithSameRuby = groupWithSameParent - .OrderBy(x => x.StartTime).ThenBy(x => x.EndTime) - .GroupByContinuous(x => new RubyGroup - { - Ruby = x.Ruby, - TimeTags = x.TimeTags, - }).ToList(); - - // then, combine those continuous ruby. - foreach (var groupWithSameRuby in rubiesWithSameRuby) - { - var ruby = groupWithSameRuby.Key.Ruby; - var parent = groupWithSameParent.Key; - var timeTags = groupWithSameRuby.Key.TimeTags; - - // should process the value with same parent text and ruby text. - var isFirst = rubiesWithSameRuby.IndexOf(groupWithSameRuby) == 0; - var isLast = rubiesWithSameRuby.IndexOf(groupWithSameRuby) == rubiesWithSameRuby.Count - 1; - - var minStartTime = isFirst ? null : groupWithSameRuby.Min(x => x.StartTime); - var maxEndTime = isLast ? null : groupWithSameRuby.Max(x => x.EndTime); - - yield return new LrcRuby - { - Ruby = ruby, - Parent = parent, - TimeTags = timeTags, - StartTime = minStartTime, - EndTime = maxEndTime, - }; - } + // todo: throw the exception by config. + // throw new InvalidOperationException("Does not support converting ruby tags to LRC format."); + yield break; } yield break; - static IEnumerable getRubyTags(Lyric lyric) - { - var timeTags = lyric.TimeTags; - - foreach (var rubyTag in lyric.RubyTags) - { - var startLyricCharIndex = rubyTag.StartCharIndex; - var endLyricCharIndex = rubyTag.EndCharIndex; - - var startTimeTag = timeTags.Reverse().LastOrDefault(x => x.Key >= new TextIndex(startLyricCharIndex) && x.Value.HasValue); - var endTimeTag = timeTags.FirstOrDefault(x => x.Key >= new TextIndex(endLyricCharIndex, IndexState.End) && x.Value.HasValue); - - yield return new LrcRuby - { - Ruby = rubyTag.Text, - Parent = lyric.Text[startLyricCharIndex..(endLyricCharIndex + 1)], - TimeTags = getTimeTags(rubyTag.TimeTags, -startTimeTag.Value ?? 0), - StartTime = startTimeTag.Value, - EndTime = endTimeTag.Value, - }; - } - } - static SortedDictionary getTimeTags(SortedDictionary timeTags, int offsetTime = 0) => new(timeTags.Where(x => x.Value.HasValue).ToDictionary(k => k.Key, v => v.Value!.Value + offsetTime)); } - - private struct RubyGroup : IEquatable - { - public string Ruby { get; set; } - - public SortedDictionary TimeTags { get; set; } - - public bool Equals(RubyGroup other) - { - return Ruby == other.Ruby && TimeTags.SequenceEqual(other.TimeTags); - } - - public override bool Equals(object? obj) - { - return obj is RubyGroup other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Ruby); - } - } } diff --git a/LrcParser/Parser/Lrc/Metadata/LrcRuby.cs b/LrcParser/Parser/Lrc/Metadata/LrcRuby.cs deleted file mode 100644 index 13adedd..0000000 --- a/LrcParser/Parser/Lrc/Metadata/LrcRuby.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) karaoke.dev . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using LrcParser.Model; - -namespace LrcParser.Parser.Lrc.Metadata; - -/// -/// Ruby tag -/// -/// -/// @Ruby1=帰,かえ -/// @Ruby25=時,じか,,[00:38:45] -/// @Ruby49=時,とき,[00:38:45],[01:04:49] -/// -public class LrcRuby -{ - /// - /// Parent kanji - /// - public string Parent { get; set; } = ""; - - /// - /// Ruby - /// - public string Ruby { get; set; } = ""; - - /// - /// Time tags - /// - public SortedDictionary TimeTags { get; set; } = new(); - - /// - /// Start position - /// - public int? StartTime { get; set; } - - /// - /// End position - /// - public int? EndTime { get; set; } -}