fix: preserve input order for equal-timestamp lyrics in normalize and to_plain

This commit is contained in:
2026-04-08 12:28:28 +02:00
parent f8db549d8e
commit 9b42cab76b
4 changed files with 46 additions and 4 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "lrx-cli" name = "lrx-cli"
version = "0.6.3" version = "0.6.4"
description = "Fetch line-synced lyrics for your music player." description = "Fetch line-synced lyrics for your music player."
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
+12 -2
View File
@@ -378,7 +378,12 @@ class LRCData:
shifted = max(0, time_ms + offset_ms) shifted = max(0, time_ms + offset_ms)
lyric_entries.append((shifted, lyric_text)) lyric_entries.append((shifted, lyric_text))
lyric_entries.sort(key=lambda item: item[0]) # Sort by timestamp; original index as tiebreaker so equal-time entries
# retain the order they appeared in the input.
lyric_entries = [
e
for _, e in sorted(enumerate(lyric_entries), key=lambda x: (x[1][0], x[0]))
]
out_lyrics: list[LyricLine] = [ out_lyrics: list[LyricLine] = [
LyricLine(line_times_ms=[time_ms], words=[LrcWordSegment(text=text)]) LyricLine(line_times_ms=[time_ms], words=[LrcWordSegment(text=text)])
@@ -413,7 +418,12 @@ class LRCData:
for line in self._lines: for line in self._lines:
tagged_lines.extend(line.timed_plain_entries()) tagged_lines.extend(line.timed_plain_entries())
sorted_lines = [lyric for _, lyric in sorted(tagged_lines, key=lambda x: x[0])] sorted_lines = [
lyric
for _, (_, lyric) in sorted(
enumerate(tagged_lines), key=lambda x: (x[1][0], x[0])
)
]
if deduplicate: if deduplicate:
# Remove consecutive duplicates # Remove consecutive duplicates
+32
View File
@@ -185,6 +185,23 @@ def test_normalize_expands_multi_time_tags_and_sorts_lyrics() -> None:
assert normalized == "\n".join(["[00:01.00]x", "[00:02.00]x", "[00:03.00]c"]) assert normalized == "\n".join(["[00:01.00]x", "[00:02.00]x", "[00:03.00]c"])
def test_normalize_preserves_input_order_for_equal_timestamps() -> None:
text = "\n".join(
[
"[00:00.00]first",
"[00:00.00]second",
"[00:00.00]third",
"[00:01.00]later",
]
)
normalized = LRCData(text).to_normalized_text()
assert normalized == "\n".join(
["[00:00.00]first", "[00:00.00]second", "[00:00.00]third", "[00:01.00]later"]
)
def test_normalize_converts_unsynced_lines_and_removes_word_sync_tags() -> None: def test_normalize_converts_unsynced_lines_and_removes_word_sync_tags() -> None:
text = "\n".join( text = "\n".join(
[ [
@@ -257,6 +274,21 @@ def test_to_plain_sorts_lines_by_timestamp_across_lines() -> None:
assert plain == "\n".join(["early", "middle", "late"]) assert plain == "\n".join(["early", "middle", "late"])
def test_to_plain_preserves_input_order_for_equal_timestamps() -> None:
text = "\n".join(
[
"[00:00.00]first",
"[00:00.00]second",
"[00:00.00]third",
"[00:01.00]later",
]
)
plain = LRCData(text).to_plain()
assert plain == "\n".join(["first", "second", "third", "later"])
def test_to_plain_deduplicate_collapses_only_consecutive_equals() -> None: def test_to_plain_deduplicate_collapses_only_consecutive_equals() -> None:
text = "\n".join( text = "\n".join(
[ [
Generated
+1 -1
View File
@@ -153,7 +153,7 @@ wheels = [
[[package]] [[package]]
name = "lrx-cli" name = "lrx-cli"
version = "0.6.3" version = "0.6.4"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "cyclopts" }, { name = "cyclopts" },