Skip to content

Commit

Permalink
imporved api names
Browse files Browse the repository at this point in the history
  • Loading branch information
Dr-Blank committed Oct 1, 2023
1 parent 22ca17a commit 4d4dc4e
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 57 deletions.
72 changes: 39 additions & 33 deletions lrctoolbox/synced_lyrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,44 @@ class SyncedLyrics(LRCMetadata):

def __init__(self):
super().__init__()
self._lines: list[SyncedLyricLine] = []
self._synced_lines: list[SyncedLyricLine] = []

def __str__(self) -> str:
return "\n".join(self.lyrics)

@property
def lines(self) -> list[SyncedLyricLine]:
def synced_lines(self) -> list[SyncedLyricLine]:
"""returns the lines as a list of SyncedLyricLine objects"""
return self._lines
return self._synced_lines

@lines.setter
def lines(self, lines: list[SyncedLyricLine]):
@synced_lines.setter
def synced_lines(self, lines: list[SyncedLyricLine]):
"""sets the lines from a list of SyncedLyricLine objects"""
self._lines = lines
self._synced_lines = lines

@property
def lyrics(self) -> list[str]:
"""lyrics as a list of strings with timestamp if present"""

if not self.is_synced:
return [line.text for line in self._lines]
return [line.text for line in self._synced_lines]

return [line.formatted_lyric for line in self._lines]
return [line.formatted_lyric for line in self._synced_lines]

@lyrics.setter
def lyrics(self, lyrics: list[str] | list[SyncedLyricLine]):
"""sets the lyrics from a list of strings or SyncedLyricLine objects"""
if not lyrics:
logger.warning("lyrics is empty")
self._lines = []
self._synced_lines = []
return

# only for convenience
if all(isinstance(line, SyncedLyricLine) for line in lyrics):
self._lines = lyrics # type: ignore
self._synced_lines = lyrics # type: ignore
return

self._lines = self.load_from_lines(lyrics).lines # type: ignore
self._synced_lines = self.load_from_lines(lyrics).synced_lines # type: ignore # noqa: E501
# all this is done to preserve any metadata that was present

@property
Expand All @@ -72,41 +75,42 @@ def is_synced(self) -> bool:
"""
return bool(
self._lines
and self._is_timestamp_in_ascending_order
and not self._is_timestamp_all_same
self._synced_lines
and self.has_timestamps_in_ascending_order
and not self.has_timestamps_all_equal
)

@property
def _is_timestamp_in_ascending_order(self) -> bool:
def has_timestamps_in_ascending_order(self) -> bool:
"""checks if the timestamp is in ascending order"""
if not self._lines:
if not self._synced_lines:
return False

# if any of the timestamp is None return False
if any(line.timestamp is None for line in self._lines):
if any(line.timestamp is None for line in self._synced_lines):
return False

return all(
(self._lines[i].timestamp <= self._lines[i + 1].timestamp) # type: ignore # noqa: E501
for i in range(len(self._lines) - 1)
(self._synced_lines[i].timestamp <= self._synced_lines[i + 1].timestamp) # type: ignore # noqa: E501
for i in range(len(self._synced_lines) - 1)
)

@property
def _is_timestamp_all_same(self) -> bool:
def has_timestamps_all_equal(self) -> bool:
"""checks if the timestamp is all same"""
if not self._lines:
if not self._synced_lines:
return False

return all(
self._lines[i].timestamp == self._lines[i + 1].timestamp
for i in range(len(self._lines) - 1)
self._synced_lines[i].timestamp
== self._synced_lines[i + 1].timestamp
for i in range(len(self._synced_lines) - 1)
)

@property
def _is_missing_any_timestamp(self) -> bool:
def is_missing_any_timestamp(self) -> bool:
"""Check if any timestamp is None"""
return any(line.timestamp is None for line in self._lines)
return any(line.timestamp is None for line in self._synced_lines)

@classmethod
def parse_str(cls, line: str) -> SyncedLyricLine | dict[str, str]:
Expand Down Expand Up @@ -170,8 +174,10 @@ def load_from_lines(cls, lines: list[str]) -> SyncedLyrics:
(line for line in lines if not isinstance(line, str)), None
)
exc = TypeError(
"lines must be a list of str, got"
f" {type(line_which_is_not_str)}",
(
"lines must be a list of str, got"
f" {type(line_which_is_not_str)}"
),
line_which_is_not_str,
)
logger.exception(exc)
Expand All @@ -183,20 +189,20 @@ def load_from_lines(cls, lines: list[str]) -> SyncedLyrics:
for line in lines:
parsed_line = cls.parse_str(line)
if isinstance(parsed_line, SyncedLyricLine):
synced_lyrics._lines.append(parsed_line)
synced_lyrics._synced_lines.append(parsed_line)
continue
synced_lyrics.update_metadata(parsed_line)

if synced_lyrics._is_timestamp_all_same:
if synced_lyrics.has_timestamps_all_equal:
# set all timestamp to None
for _line in synced_lyrics._lines:
for _line in synced_lyrics._synced_lines:
_line.timestamp = None

if (
not synced_lyrics._is_timestamp_in_ascending_order
and not synced_lyrics._is_timestamp_all_same
not synced_lyrics.has_timestamps_in_ascending_order
and not synced_lyrics.has_timestamps_all_equal
):
synced_lyrics._lines.sort(key=lambda x: x.timestamp or 0)
synced_lyrics._synced_lines.sort(key=lambda x: x.timestamp or 0)
return synced_lyrics

@classmethod
Expand Down
59 changes: 35 additions & 24 deletions tests/test_synced_lyrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ def test_lines_setter_getter():
SyncedLyricLine(text="Baz qux", timestamp=5000),
SyncedLyricLine(text="Quux quuz", timestamp=10000),
]
synced_lyrics.lines = lines
synced_lyrics.synced_lines = lines
assert synced_lyrics.lyrics == [
"[00:00.00]Foo bar",
"[00:05.00]Baz qux",
"[00:10.00]Quux quuz",
]
assert synced_lyrics.lines == lines
assert synced_lyrics.synced_lines == lines


def test_lyrics_setter_getter():
Expand All @@ -95,52 +95,63 @@ def test_lyrics_setter_getter():
]
synced_lyrics.lyrics = lyrics
assert synced_lyrics.lyrics == lyrics
assert synced_lyrics.lines == synced_lines
assert synced_lyrics.synced_lines == synced_lines
synced_lyrics.lyrics = []
assert not synced_lyrics.lyrics
assert not synced_lyrics.lines
assert not synced_lyrics.synced_lines

synced_lyrics.lyrics = synced_lines
assert synced_lyrics.lyrics == lyrics
assert synced_lyrics.lines == synced_lines
assert synced_lyrics.synced_lines == synced_lines


def test_timestamp_properties():
synced_lyrics = SyncedLyrics()
assert synced_lyrics._is_timestamp_in_ascending_order is False
assert synced_lyrics._is_timestamp_all_same is False
assert synced_lyrics._is_missing_any_timestamp is False
assert synced_lyrics.has_timestamps_in_ascending_order is False
assert synced_lyrics.has_timestamps_all_equal is False
assert synced_lyrics.is_missing_any_timestamp is False
lyrics = [
"[00:00.00]Foo bar",
"[00:05.00]Baz qux",
"[00:10.00]Quux quuz",
]
synced_lyrics.lyrics = lyrics
assert synced_lyrics._is_missing_any_timestamp is False
synced_lyrics.lines[0].timestamp = None
assert synced_lyrics._is_missing_any_timestamp is True
synced_lyrics.lines[0].timestamp = 0
assert synced_lyrics._is_timestamp_in_ascending_order is True
synced_lyrics.lines[2].timestamp = 2500
assert synced_lyrics._is_timestamp_in_ascending_order is False
assert synced_lyrics._is_timestamp_all_same is False
synced_lyrics.lines[1].timestamp = 0
synced_lyrics.lines[2].timestamp = 0
assert synced_lyrics._is_timestamp_all_same is True
assert synced_lyrics.is_missing_any_timestamp is False
synced_lyrics.synced_lines[0].timestamp = None
assert synced_lyrics.is_missing_any_timestamp is True
synced_lyrics.synced_lines[0].timestamp = 0
assert synced_lyrics.has_timestamps_in_ascending_order is True
synced_lyrics.synced_lines[2].timestamp = 2500
assert synced_lyrics.has_timestamps_in_ascending_order is False
assert synced_lyrics.has_timestamps_all_equal is False
synced_lyrics.synced_lines[1].timestamp = 0
synced_lyrics.synced_lines[2].timestamp = 0
assert synced_lyrics.has_timestamps_all_equal is True


@pytest.mark.parametrize(
"line, expected",
[
("[au: DrB]", "DrB"),
("[00:00.00]Lyricist: DrB ", "DrB"),
("[ar: Artist]", {"artist": "Artist"}),
("[by: DrB]", {"author": "DrB"}),
("[al: Album]", {"album": "Album"}),
("[ti: Title]", {"title": "Title"}),
("[re: LRCMaker]", {"re_name": "LRCMaker"}),
("[ve: 1.0.0]", {"version": "1.0.0"}),
(
"[uri: spotify:track:foobarbazqux]",
{"uri": "spotify:track:foobarbazqux"},
),
("[length: 200000]", {"length": "200000"}),
("[00:00.00]Lyricist: DrB ", {"lyricist": "DrB"}),
("[random: 200000]", {"random": "200000"}),
],
)
def test_string_parsing_lyricist(line, expected):
def test_metadata_parsing(line, expected):
synced_lyrics = SyncedLyrics()
res = synced_lyrics.parse_str(line)
assert isinstance(res, dict)
assert res.get("lyricist") == expected
assert res == expected


@pytest.mark.parametrize(
Expand All @@ -150,7 +161,7 @@ def test_string_parsing_lyricist(line, expected):
("[00:05.00]Baz qux", SyncedLyricLine(text="Baz qux", timestamp=5000)),
(
"[14:25.565]Quux quuz",
SyncedLyricLine(text="Quux quuz", timestamp=865565),
SyncedLyricLine(text="Quux quuz", timestamp=(14 * 60 + 25) * 1000 + 565),
),
("Quux quuz", SyncedLyricLine(text="Quux quuz")),
],
Expand Down

0 comments on commit 4d4dc4e

Please sign in to comment.