From 08a72d6b849c5a111ced5383754870c265caad79 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 21 Jul 2024 17:23:37 +0800 Subject: [PATCH] Remove the ruby-tag config because it's not a part of lrc definition(might be .kar only). And note that will throw the exception if contains the ruby tag in the lyric because this parser can not deal with ruby-tag. --- .../Parser/Lrc/Lines/LrcRubyParserTest.cs | 68 -------- LrcParser/Parser/Lrc/Lines/LrcRubyParser.cs | 51 ------ LrcParser/Parser/Lrc/LrcParser.cs | 159 +----------------- LrcParser/Parser/Lrc/Metadata/LrcRuby.cs | 42 ----- 4 files changed, 1 insertion(+), 319 deletions(-) delete mode 100644 LrcParser.Tests/Parser/Lrc/Lines/LrcRubyParserTest.cs delete mode 100644 LrcParser/Parser/Lrc/Lines/LrcRubyParser.cs delete mode 100644 LrcParser/Parser/Lrc/Metadata/LrcRuby.cs 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/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..c458a1f 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,11 @@ 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, - }; - } - } + throw new InvalidOperationException("Does not support converting ruby tags to LRC format."); 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; } -}