Skip to content

Commit

Permalink
add recently added releases and descriptor views; rename release temp…
Browse files Browse the repository at this point in the history
…lates
  • Loading branch information
azuline committed May 1, 2024
1 parent 6aa8488 commit 0f3e1e4
Show file tree
Hide file tree
Showing 14 changed files with 501 additions and 239 deletions.
2 changes: 2 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ def config(isolated_dir: Path) -> Config:
artist_aliases_parents_map={},
fuse_artists_whitelist=None,
fuse_genres_whitelist=None,
fuse_descriptors_whitelist=None,
fuse_labels_whitelist=None,
fuse_artists_blacklist=None,
fuse_genres_blacklist=None,
fuse_descriptors_blacklist=None,
fuse_labels_blacklist=None,
cover_art_stems=["cover", "folder", "art", "front"],
valid_art_exts=["jpg", "jpeg", "png"],
Expand Down
21 changes: 12 additions & 9 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,27 @@ artist_aliases = [
{ artist = "tripleS", aliases = ["EVOLution", "LOVElution", "+(KR)ystal Eyes", "Acid Angel From Asia", "Acid Eyes"] },
]

# Artists, genres, and labels to show in their respective top-level virtual
# filesystem directories. By # default, all artists, genres, and labels are
# shown. However, if this configuration parameter is specified, the list can be
# restricted to a specific few values. This is useful if you only care about a
# few specific genres and labels.
# Artists, genres, descriptors, and labels to show in their respective
# top-level virtual filesystem directories. By default, all artists, genres,
# and labels are shown. However, if this configuration parameter is specified,
# the list can be restricted to a specific few values. This is useful if you
# only care about a few specific genres and labels.
fuse_artists_whitelist = [ "xxx", "yyy" ]
fuse_genres_whitelist = [ "xxx", "yyy" ]
fuse_descriptors_whitelist = [ "xxx", "yyy" ]
fuse_labels_whitelist = [ "xxx", "yyy" ]
# Artists, genres, and labels to hide from the virtual filesystem navigation.
# These options remove specific entities from their respective top-level
# virtual filesystem directories. This is useful if there are a few values you
# don't find useful, e.g. a random featuring artist or one super niche genre.
# Artists, genres, descriptors, and labels to hide from the virtual filesystem
# navigation. These options remove specific entities from their respective
# top-level virtual filesystem directories. This is useful if there are a few
# values you don't find useful, e.g. a random featuring artist or one super
# niche genre.
#
# These options are mutually exclusive with the fuse_*_whitelist options; if
# both are specified for a given entity type, the configuration will not
# validate.
fuse_artists_blacklist = [ "xxx" ]
fuse_genres_blacklist = [ "xxx" ]
fuse_descriptors_blacklist = [ "xxx" ]
fuse_labels_blacklist = [ "xxx" ]

# When Rosé scans a release directory, it looks for cover art that matches:
Expand Down
16 changes: 10 additions & 6 deletions docs/TEMPLATES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@ default.release = "..."
default.track = "..."
source.release = "..."
source.track = "..."
all_releases.release = "..."
all_releases.track = "..."
new_releases.release = "..."
new_releases.track = "..."
recently_added_releases.release = "..."
recently_added_releases.track = "..."
releases.release = "..."
releases.track = "..."
releases_new.release = "..."
releases_new.track = "..."
releases_added_on.release = "..."
releases_added_on.track = "..."
releases_released_on.release = "..."
releases_released_on.track = "..."
artists.release = "..."
artists.track = "..."
genres.release = "..."
genres.track = "..."
descriptors.release = "..."
descriptors.track = "..."
labels.release = "..."
labels.track = "..."
collages.release = "..."
Expand Down
6 changes: 6 additions & 0 deletions rose/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
calculate_release_logtext,
calculate_track_logtext,
collage_exists,
descriptor_exists,
genre_exists,
get_collage,
get_path_of_track_in_playlist,
Expand All @@ -30,6 +31,7 @@
label_exists,
list_artists,
list_collages,
list_descriptors,
list_genres,
list_labels,
list_playlists,
Expand Down Expand Up @@ -82,6 +84,7 @@
from rose.rule_parser import MetadataAction, MetadataMatcher, MetadataRule
from rose.rules import execute_metadata_rule, execute_stored_metadata_rules
from rose.templates import (
PathContext,
PathTemplate,
eval_release_template,
eval_track_template,
Expand Down Expand Up @@ -119,8 +122,10 @@
"delete_playlist_cover_art",
"delete_release",
"delete_release_cover_art",
"descriptor_exists",
"dump_all_collages",
"dump_all_playlists",
"PathContext",
"dump_all_releases",
"dump_all_tracks",
"dump_collage",
Expand All @@ -145,6 +150,7 @@
"label_exists",
"list_artists",
"list_collages",
"list_descriptors",
"list_genres",
"list_labels",
"list_playlists",
Expand Down
24 changes: 24 additions & 0 deletions rose/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,7 @@ def list_releases_delete_this(
c: Config,
artist_filter: str | None = None,
genre_filter: str | None = None,
descriptor_filter: str | None = None,
label_filter: str | None = None,
new: bool | None = None,
) -> list[CachedRelease]:
Expand Down Expand Up @@ -1856,6 +1857,14 @@ def list_releases_delete_this(
"""
args.extend(genres)
args.extend(genres)
if descriptor_filter:
query += """
AND EXISTS (
SELECT * FROM releases_descriptors
WHERE release_id = id AND descriptor = ?
)
"""
args.append(descriptor_filter)
if label_filter:
query += """
AND EXISTS (
Expand Down Expand Up @@ -2288,6 +2297,21 @@ def genre_exists(c: Config, genre: str) -> bool:
return bool(cursor.fetchone()[0])


def list_descriptors(c: Config) -> list[str]:
with connect(c) as conn:
cursor = conn.execute("SELECT DISTINCT descriptor FROM releases_descriptors")
return [row["descriptor"] for row in cursor]


def descriptor_exists(c: Config, descriptor: str) -> bool:
with connect(c) as conn:
cursor = conn.execute(
"SELECT EXISTS(SELECT * FROM releases_descriptors WHERE descriptor = ?)",
(descriptor,),
)
return bool(cursor.fetchone()[0])


def list_labels(c: Config) -> list[str]:
with connect(c) as conn:
cursor = conn.execute("SELECT DISTINCT label FROM releases_labels")
Expand Down
14 changes: 14 additions & 0 deletions rose/cache_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
artist_exists,
collage_exists,
connect,
descriptor_exists,
genre_exists,
get_collage,
get_path_of_track_in_playlist,
Expand All @@ -35,6 +36,7 @@
label_exists,
list_artists,
list_collages,
list_descriptors,
list_genres,
list_labels,
list_playlists,
Expand Down Expand Up @@ -1588,6 +1590,12 @@ def test_list_genres(config: Config) -> None:
}


@pytest.mark.usefixtures("seeded_cache")
def test_list_descriptors(config: Config) -> None:
descriptors = list_descriptors(config)
assert set(descriptors) == {"Warm", "Hot", "Wet"}


@pytest.mark.usefixtures("seeded_cache")
def test_list_labels(config: Config) -> None:
labels = list_labels(config)
Expand Down Expand Up @@ -1856,6 +1864,12 @@ def test_genre_exists(config: Config) -> None:
assert not genre_exists(config, "Lo-Fi House")


@pytest.mark.usefixtures("seeded_cache")
def test_descriptor_exists(config: Config) -> None:
assert descriptor_exists(config, "Warm")
assert not descriptor_exists(config, "Icy")


@pytest.mark.usefixtures("seeded_cache")
def test_label_exists(config: Config) -> None:
assert label_exists(config, "Silk Music")
Expand Down
6 changes: 3 additions & 3 deletions rose/collages.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ def remove_release_from_collage(c: Config, collage_name: str, release_id: str) -
with path.open("rb") as fp:
data = tomllib.load(fp)
old_releases = data.get("releases", [])
new_releases = [r for r in old_releases if r["uuid"] != release_id]
if old_releases == new_releases:
releases_new = [r for r in old_releases if r["uuid"] != release_id]
if old_releases == releases_new:
logger.info(f"No-Op: Release {release_logtext} not in collage {collage_name}")
return
data["releases"] = new_releases
data["releases"] = releases_new
with path.open("wb") as fp:
tomli_w.dump(data, fp)
logger.info(f"Removed release {release_logtext} from collage {collage_name}")
Expand Down
42 changes: 39 additions & 3 deletions rose/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ class Config:

fuse_artists_whitelist: list[str] | None
fuse_genres_whitelist: list[str] | None
fuse_descriptors_whitelist: list[str] | None
fuse_labels_whitelist: list[str] | None
fuse_artists_blacklist: list[str] | None
fuse_genres_blacklist: list[str] | None
fuse_descriptors_blacklist: list[str] | None
fuse_labels_blacklist: list[str] | None

cover_art_stems: list[str]
Expand Down Expand Up @@ -202,6 +204,21 @@ def parse(cls, config_path_override: Path | None = None) -> Config:
f"Invalid value for fuse_genres_whitelist in configuration file ({cfgpath}): {e}"
) from e

try:
fuse_descriptors_whitelist = data["fuse_descriptors_whitelist"]
del data["fuse_descriptors_whitelist"]
if not isinstance(fuse_descriptors_whitelist, list):
raise ValueError(f"Must be a list[str]: got {type(fuse_descriptors_whitelist)}")
for s in fuse_descriptors_whitelist:
if not isinstance(s, str):
raise ValueError(f"Each descriptor must be of type str: got {type(s)}")
except KeyError:
fuse_descriptors_whitelist = None
except ValueError as e:
raise InvalidConfigValueError(
f"Invalid value for fuse_descriptors_whitelist in configuration file ({cfgpath}): {e}"
) from e

try:
fuse_labels_whitelist = data["fuse_labels_whitelist"]
del data["fuse_labels_whitelist"]
Expand Down Expand Up @@ -247,6 +264,21 @@ def parse(cls, config_path_override: Path | None = None) -> Config:
f"Invalid value for fuse_genres_blacklist in configuration file ({cfgpath}): {e}"
) from e

try:
fuse_descriptors_blacklist = data["fuse_descriptors_blacklist"]
del data["fuse_descriptors_blacklist"]
if not isinstance(fuse_descriptors_blacklist, list):
raise ValueError(f"Must be a list[str]: got {type(fuse_descriptors_blacklist)}")
for s in fuse_descriptors_blacklist:
if not isinstance(s, str):
raise ValueError(f"Each descriptor must be of type str: got {type(s)}")
except KeyError:
fuse_descriptors_blacklist = None
except ValueError as e:
raise InvalidConfigValueError(
f"Invalid value for fuse_descriptors_blacklist in configuration file ({cfgpath}): {e}"
) from e

try:
fuse_labels_blacklist = data["fuse_labels_blacklist"]
del data["fuse_labels_blacklist"]
Expand Down Expand Up @@ -417,11 +449,13 @@ def parse(cls, config_path_override: Path | None = None) -> Config:
if tmpl_config := data.get("path_templates", None):
for key in [
"source",
"all_releases",
"new_releases",
"recently_added_releases",
"releases",
"releases_new",
"releases_added_on",
"releases_released_on",
"artists",
"genres",
"descriptors",
"labels",
"collages",
]:
Expand Down Expand Up @@ -475,9 +509,11 @@ def parse(cls, config_path_override: Path | None = None) -> Config:
artist_aliases_parents_map=artist_aliases_parents_map,
fuse_artists_whitelist=fuse_artists_whitelist,
fuse_genres_whitelist=fuse_genres_whitelist,
fuse_descriptors_whitelist=fuse_descriptors_whitelist,
fuse_labels_whitelist=fuse_labels_whitelist,
fuse_artists_blacklist=fuse_artists_blacklist,
fuse_genres_blacklist=fuse_genres_blacklist,
fuse_descriptors_blacklist=fuse_descriptors_blacklist,
fuse_labels_blacklist=fuse_labels_blacklist,
cover_art_stems=cover_art_stems,
valid_art_exts=valid_art_exts,
Expand Down
Loading

0 comments on commit 0f3e1e4

Please sign in to comment.