Skip to content

Commit

Permalink
New approach
Browse files Browse the repository at this point in the history
  • Loading branch information
kinuax committed May 8, 2024
1 parent 89d6269 commit 6e16800
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 39 deletions.
2 changes: 1 addition & 1 deletion rolabesti/controllers/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __call__(self) -> None:
del self.parameters["list_"]
del self.parameters["reset"]

if all(map(lambda parameter: parameter is None, self.parameters.values())):
if all(parameter is None for parameter in self.parameters.values()):
self.logger.log("[green]no new settings to configure[/green]")
return

Expand Down
70 changes: 39 additions & 31 deletions rolabesti/controllers/parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import re
from pathlib import Path

from mutagen import MutagenError
Expand All @@ -9,56 +8,65 @@
from rolabesti.models import ID3Tags, Track


PATTERNS = {
r"/Places/([^/]+)/Genres/([^/]+)/Albums/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": (
"place", "genre", "album", "side", "title"),
r"/Places/([^/]+)/Genres/([^/]+)/([^/]+)/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": (
"place", "genre", "artist", "album", "side", "title"),
r"/Places/([^/]+)/Genres/([^/]+)/([^/]+)/([^/]+)\.[mM][pP]3$": ("place", "genre", "artist", "title"),
r"/Places/([^/]+)/Genres/([^/]+)/([^/]+)\.[mM][pP]3$": ("place", "genre", "title"),
r"/Places/([^/]+)/([^/]+)\.[mM][pP]3$": ("place", "title"),
r"/Places/([^/]+)/Albums/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": ("place", "album", "side", "title"),
r"/Places/([^/]+)/([^/]+)/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": ("place", "artist", "album", "side", "title"),
r"/Places/([^/]+)/([^/]+)/([^/]+)\.[mM][pP]3$": ("place", "artist", "title"),
r"/Genres/([^/]+)/Albums/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": ("genre", "album", "side", "title"),
r"/Genres/([^/]+)/([^/]+)/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": ("genre", "artist", "album", "side", "title"),
r"/Genres/([^/]+)/([^/]+)/([^/]+)\.[mM][pP]3$": ("genre", "artist", "title"),
r"/Genres/([^/]+)/([^/]+)\.[mM][pP]3$": ("genre", "title"),
r"/Artists/([^/]+)/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": ("artist", "album", "side", "title"),
r"/Artists/([^/]+)/([^/]+)\.[mM][pP]3$": ("artist", "title"),
r"/Albums/([^/]+)/(?:([^/]+)/)?([^/]+)\.[mM][pP]3$": ("album", "side", "title"),
r"/([^/]+)\.[mM][pP]3$": ("title",),
}
patterns = [
[{"Places": -8, "Genres": -6, "Albums": -4}, {"place": -7, "genre": -5, "album": -3}],
[{"Places": -7, "Genres": -5, "Albums": -3}, {"place": -6, "genre": -4, "album": -2}],
[{"Places": -8, "Genres": -6}, {"place": -7, "genre": -5, "artist": -4, "album": -3}],
[{"Places": -7, "Genres": -5}, {"place": -6, "genre": -4, "artist": -3, "album": -2}],
[{"Places": -6, "Genres": -4}, {"place": -5, "genre": -3, "artist": -2}],
[{"Places": -5, "Genres": -3}, {"place": -4, "genre": -2}],
[{"Places": -6, "Albums": -4}, {"place": -5, "album": -3}],
[{"Places": -5, "Albums": -3}, {"place": -4, "album": -2}],
[{"Places": -6}, {"place": -5, "artist": -4, "album": -3}],
[{"Places": -5}, {"place": -4, "artist": -3, "album": -2}],
[{"Places": -4}, {"place": -3, "artist": -2}],
[{"Places": -3}, {"place": -2}],
[{"Genres": -6, "Albums": -4}, {"genre": -5, "album": -3}],
[{"Genres": -5, "Albums": -3}, {"genre": -4, "album": -2}],
[{"Genres": -6}, {"genre": -5, "artist": -4, "album": -3}],
[{"Genres": -5}, {"genre": -4, "artist": -3, "album": -2}],
[{"Genres": -4}, {"genre": -3, "artist": -2}],
[{"Genres": -3}, {"genre": -2}],
[{"Artists": -5}, {"artist": -4, "album": -3}],
[{"Artists": -4}, {"artist": -3, "album": -2}],
[{"Artists": -3}, {"artist": -2}],
[{"Albums": -4}, {"album": -3}],
[{"Albums": -3}, {"album": -2}],
]


class Parser:
"""Parsing related functionality."""

def parse(self, trackpath: Path) -> Track | None:
"""
Parse track located at trackpath and return a Track object.
If there is an error, return None.
"""
if (path_fields := self._parse_path_fields(trackpath)) is None:
return
if (id3_tags := self._parse_id3_tags(trackpath)) is None:
return
if (length := self._parse_length(trackpath)) is None:
return
path_fields = self._parse_path_fields(trackpath)
path_fields["title"] = trackpath.stem
if (track := self._build_track(trackpath, path_fields, id3_tags, length)) is None:
return
return track

@staticmethod
def _parse_path_fields(trackpath: Path) -> dict | None:
def _parse_path_fields(trackpath: Path) -> dict:
"""
Match and parse trackpath against PATTERNS.
Return a dictionary with parsed fields if there is a match.
If there is no matching pattern, return None.
Parse trackpath and return a dictionary with the path fields.
If there is no matching pattern, return an empty dictionary.
"""
for regex, fields in PATTERNS.items():
match = re.search(regex, str(trackpath))
if match:
return {field: value for field, value in zip(fields, match.groups()) if value}
for pattern in patterns:
try:
if all(field == trackpath.parts[index] for field, index in pattern[0].items()):
return {field: trackpath.parts[index] for field, index in pattern[1].items()}
except IndexError:
# Discard trackpath unable to match pattern.
pass
return {}

@staticmethod
def _parse_id3_tags(trackpath: Path) -> dict | None:
Expand Down
32 changes: 26 additions & 6 deletions tests/controllers/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,28 @@


@pytest.mark.parametrize("trackpath, length", [
(Path().joinpath("path-to-music-directory", "Places", f"{place}", "Genres", f"{genre}", "Albums", f"{album}", f"{side}", f"{title}.mp3"), 5),
(Path() / "Places" / place / "Genres" / genre / "Albums" / album / f"{title}.mp3", 3),
(Path() / "Places" / place / "Genres" / genre / artist / album / side / f"{title}.mp3", 4),
(Path() / "Places" / place / "Genres" / genre / artist / album / f"{title}.mp3", 4),
(Path() / "Places" / place / "Genres" / genre / artist / f"{title}.mp3", 3),
(Path() / "Places" / place / "Genres" / genre / f"{title}.mp3", 2),
(Path() / "Places" / place / "Albums" / album / side / f"{title}.mp3", 2),
(Path() / "Places" / place / "Albums" / album / f"{title}.mp3", 2),
(Path() / "Places" / place / artist / album / side / f"{title}.mp3", 3),
(Path() / "Places" / place / artist / album / f"{title}.mp3", 3),
(Path() / "Places" / place / artist / f"{title}.mp3", 2),
(Path() / "Places" / place / f"{title}.mp3", 1),
(Path() / "Genres" / genre / "Albums" / album / side / f"{title}.mp3", 2),
(Path() / "Genres" / genre / "Albums" / album / f"{title}.mp3", 2),
(Path() / "Genres" / genre / artist / album / side / f"{title}.mp3", 3),
(Path() / "Genres" / genre / artist / album / f"{title}.mp3", 3),
(Path() / "Genres" / genre / artist / f"{title}.mp3", 2),
(Path() / "Genres" / genre / f"{title}.mp3", 1),
(Path() / "Albums" / album / side / f"{title}.mp3", 1),
(Path() / "Albums" / album / f"{title}.mp3", 1),
(Path() / "Artists" / artist / album / side / f"{title}.mp3", 2),
(Path() / "Artists" / artist / album / f"{title}.mp3", 2),
(Path() / "Artists" / artist / f"{title}.mp3", 1),
])
def test_parse_path_fields_with_supported_trackpaths(
trackpath: Path,
Expand All @@ -31,13 +52,12 @@ def test_parse_path_fields_with_supported_trackpaths(


@pytest.mark.parametrize("trackpath", [
(Path(f"/path/to/music/directory/Places/{place}/Genre/{genre}"), ),
(Path(f"/path/to/music/directory/Places/{place}/Genre/{genre}/{title}"), ),
(Path(f"/path/to/music/directory/Places/{place}/{title}.pdf"), ),
(Path("@#wW#$DS23VTW#@%$wsVWExEW234ER^#^#$%"), ),
Path() / "Places" / place,
Path() / "Albums" / album,
Path() / "some" / "path",
])
def test_parse_path_fields_with_unsupported_trackpaths(
trackpath: Path,
) -> None:
path_fields = parser._parse_path_fields(trackpath)
assert path_fields is None
assert path_fields == {}
1 change: 0 additions & 1 deletion tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@


def get_random_string() -> str:
return "asdf"
seed = string.ascii_letters + string.digits
return "".join(random.sample(seed, len(seed)))

0 comments on commit 6e16800

Please sign in to comment.