From fb2225ed9614c6e26962158ba4a2b145a571c54c Mon Sep 17 00:00:00 2001 From: blissful Date: Fri, 26 Apr 2024 16:27:10 -0500 Subject: [PATCH] support dates (#112) --- conftest.py | 20 +++---- docs/CONFIGURATION.md | 2 +- docs/METADATA_TOOLS.md | 12 ++--- docs/TEMPLATES.md | 16 +++--- rose/audiotags.py | 84 +++++++++++++++++------------- rose/audiotags_test.py | 15 +++--- rose/cache.py | 110 +++++++++++++++++++++------------------ rose/cache.sql | 18 +++---- rose/cache_test.py | 98 +++++++++++++++++----------------- rose/collages_test.py | 24 ++++----- rose/playlists_test.py | 24 ++++----- rose/releases.py | 56 ++++++++++---------- rose/releases_test.py | 62 +++++++++++----------- rose/rule_parser.py | 30 +++++------ rose/rule_parser_test.py | 6 +-- rose/rules.py | 64 ++++++++++++----------- rose/rules_test.py | 8 +-- rose/templates.py | 33 ++++++------ rose/templates_test.py | 13 ++--- rose/tracks_test.py | 42 +++++++-------- rose_vfs/virtualfs.py | 6 +-- 21 files changed, 385 insertions(+), 358 deletions(-) diff --git a/conftest.py b/conftest.py index 859ba54..571f5d3 100644 --- a/conftest.py +++ b/conftest.py @@ -113,10 +113,10 @@ def seeded_cache(config: Config) -> None: conn.executescript( f"""\ INSERT INTO releases - (id , source_path , cover_image_path , added_at , datafile_mtime, title , releasetype, releaseyear, originalyear, compositionyear, catalognumber, edition , disctotal, new , metahash) -VALUES ('r1', '{dirpaths[0]}', null , '0000-01-01T00:00:00+00:00', '999' , 'Release 1', 'album' , 2023 , null , null , null , null , 1 , false, '1') - , ('r2', '{dirpaths[1]}', '{imagepaths[0]}', '0000-01-01T00:00:00+00:00', '999' , 'Release 2', 'album' , 2021 , 2019 , null , 'DG-001' , 'Deluxe', 1 , false, '2') - , ('r3', '{dirpaths[2]}', null , '0000-01-01T00:00:00+00:00', '999' , 'Release 3', 'album' , 2021 , null , 1780 , 'DG-002' , null , 1 , true , '3'); + (id , source_path , cover_image_path , added_at , datafile_mtime, title , releasetype, releasedate , originaldate, compositiondate, catalognumber, edition , disctotal, new , metahash) +VALUES ('r1', '{dirpaths[0]}', null , '0000-01-01T00:00:00+00:00', '999' , 'Release 1', 'album' , '2023' , null , null , null , null , 1 , false, '1') + , ('r2', '{dirpaths[1]}', '{imagepaths[0]}', '0000-01-01T00:00:00+00:00', '999' , 'Release 2', 'album' , '2021' , '2019' , null , 'DG-001' , 'Deluxe', 1 , false, '2') + , ('r3', '{dirpaths[2]}', null , '0000-01-01T00:00:00+00:00', '999' , 'Release 3', 'album' , '2021-04-20', null , '1780' , 'DG-002' , null , 1 , true , '3'); INSERT INTO releases_genres (release_id, genre , position) @@ -196,9 +196,9 @@ def seeded_cache(config: Config) -> None: , tracknumber , discnumber , releasetitle - , releaseyear - , originalyear - , compositionyear + , releasedate + , originaldate + , compositiondate , catalognumber , edition , releasetype @@ -215,9 +215,9 @@ def seeded_cache(config: Config) -> None: , process_string_for_fts(t.tracknumber) AS tracknumber , process_string_for_fts(t.discnumber) AS discnumber , process_string_for_fts(r.title) AS releasetitle - , process_string_for_fts(r.releaseyear) AS releaseyear - , process_string_for_fts(r.originalyear) AS originalyear - , process_string_for_fts(r.compositionyear) AS compositionyear + , process_string_for_fts(r.releasedate) AS releasedate + , process_string_for_fts(r.originaldate) AS originaldate + , process_string_for_fts(r.compositiondate) AS compositiondate , process_string_for_fts(r.catalognumber) AS catalognumber , process_string_for_fts(r.edition) AS edition , process_string_for_fts(r.releasetype) AS releasetype diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 875a56f..084fc19 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -131,7 +131,7 @@ max_proc = 4 [path_templates] default.release = """ {{ artists | artistsfmt }} - -{% if releaseyear %}{{ releaseyear }}.{% endif %} +{% if releasedate %}{{ releasedate }}.{% endif %} {{ title }} {% if releasetype == "single" %}- {{ releasetype | releasetypefmt }}{% endif %} """ diff --git a/docs/METADATA_TOOLS.md b/docs/METADATA_TOOLS.md index e8561cd..ee4ac62 100644 --- a/docs/METADATA_TOOLS.md +++ b/docs/METADATA_TOOLS.md @@ -36,9 +36,9 @@ An example of the release's TOML representation: title = "Mix & Match" new = false releasetype = "ep" -releaseyear = 2017 -originalyear = 2017 -compositionyear = -9999 +releasedate = 2017 +originaldate = 2017 +compositiondate = -9999 genres = [ "K-Pop", "Dance-Pop", @@ -316,9 +316,9 @@ The rules engine supports matching and acting on the following tags: - `releaseartist[conductor]` - `releaseartist[djmixer]` - `releasetype` -- `releaseyear` -- `originalyear` -- `compositionyear` +- `releasedate` +- `originaldate` +- `compositiondate` - `genre` - `parentgenre` - `secondarygenre` diff --git a/docs/TEMPLATES.md b/docs/TEMPLATES.md index a818299..5ada246 100644 --- a/docs/TEMPLATES.md +++ b/docs/TEMPLATES.md @@ -41,7 +41,7 @@ If set, the `default.xxx` templates are used as the default values for all other {# "Default Default" Release Template #} {{ releaseartists | artistsfmt }} - -{% if releaseyear %}{{ releaseyear }}.{% endif %} +{% if releasedate %}{{ releasedate }}.{% endif %} {{ releasetitle }} {% if releasetype == "single" %}- {{ releasetype | releasetypefmt }}{% endif %} {% if new %}[NEW]{% endif %} @@ -66,7 +66,7 @@ comments when defining your templates, like so: [path_templates] default.release = """ {{ releaseartists | artistsfmt }} - - {% if releaseyear %}{{ releaseyear }}.{% endif %} {# Hi! This is a comment! #} + {% if releasedate %}{{ releasedate }}.{% endif %} {# Hi! This is a comment! #} {{ releasetitle }} {% if new %}[NEW]{% endif %} """ @@ -78,9 +78,9 @@ Rosé provides the following template variables for releases: added_at: str # ISO8601 timestamp of when the release was added to the library. releasetitle: str releasetype: str # The type of the release (e.g. single, ep, etc). One of the enums as defined in TAGGING_CONVENTIONS.md. -releaseyear: int | None # The year of this edition of the release. -originalyear: int | None # The year of the first edition of the release. -compositionyear: int | None # The year that the release was composed. Mainly of interest in classical music. +releasedate: int | None # The year of this edition of the release. +originaldate: int | None # The year of the first edition of the release. +compositiondate: int | None # The year that the release was composed. Mainly of interest in classical music. new: bool # The "new"-ness of the release. See RELEASES.md for documentation on this feature. disctotal: int # The number of discs in the release. genres: list[str] @@ -126,9 +126,9 @@ trackartists.conductor: list[Artist] trackartists.djmixer: list[Artist] releasetitle: str releasetype: str # The type of the track's release (e.g. single, ep, etc). -releaseyear: int | None -originalyear: int | None # The year of the first edition of the release. -compositionyear: int | None # The year that the release was composed. Mainly of interest in classical music. +releasedate: int | None +originaldate: int | None # The year of the first edition of the release. +compositiondate: int | None # The year that the release was composed. Mainly of interest in classical music. new: bool # The "new"-ness of the track's release. genres: list[str] parent_genres: list[str] # The parent genres of `genres`, excluding `genres`. diff --git a/rose/audiotags.py b/rose/audiotags.py index 7a23a4e..c177d77 100644 --- a/rose/audiotags.py +++ b/rose/audiotags.py @@ -35,7 +35,7 @@ TAG_SPLITTER_REGEX = re.compile(r" \\\\ | / |; ?| vs\. ") YEAR_REGEX = re.compile(r"\d{4}$") -DATE_REGEX = re.compile(r"(\d{4})-\d{2}-\d{2}") +DATE_REGEX = re.compile(r"(\d{4})-(\d{2})-(\d{2})") SUPPORTED_AUDIO_EXTENSIONS = [ ".mp3", @@ -81,6 +81,29 @@ class UnsupportedTagValueTypeError(RoseExpectedError): pass +@dataclass(frozen=True) +class RoseDate: + year: int + month: int | None = None + day: int | None = None + + @classmethod + def parse(cls, value: str | None) -> RoseDate | None: + if not value: + return None + with contextlib.suppress(ValueError): + return RoseDate(year=int(value), month=None, day=None) + # There may be a time value after the date... allow that and other crap. + if m := DATE_REGEX.match(value): + return RoseDate(year=int(m[1]), month=int(m[2]), day=int(m[3])) + return None + + def __str__(self) -> str: + if self.month is None and self.day is None: + return f"{self.year:04}" + return f"{self.year:04}-{self.month or 1:02}-{self.day or 1:02}" + + @dataclass class AudioTags: id: str | None @@ -95,9 +118,9 @@ class AudioTags: releasetitle: str | None releasetype: str - releaseyear: int | None - originalyear: int | None - compositionyear: int | None + releasedate: RoseDate | None + originaldate: RoseDate | None + compositiondate: RoseDate | None genre: list[str] secondarygenre: list[str] descriptor: list[str] @@ -149,9 +172,11 @@ def _get_paired_frame(x: str) -> str | None: id=_get_tag(m.tags, ["TXXX:ROSEID"], first=True), release_id=_get_tag(m.tags, ["TXXX:ROSERELEASEID"], first=True), tracktitle=_get_tag(m.tags, ["TIT2"]), - releaseyear=_parse_year(_get_tag(m.tags, ["TDRC", "TYER", "TDAT"])), - originalyear=_parse_year(_get_tag(m.tags, ["TDOR", "TORY"])), - compositionyear=_parse_year(_get_tag(m.tags, ["TXXX:COMPOSITIONDATE"], first=True)), + releasedate=RoseDate.parse(_get_tag(m.tags, ["TDRC", "TYER", "TDAT"])), + originaldate=RoseDate.parse(_get_tag(m.tags, ["TDOR", "TORY"])), + compositiondate=RoseDate.parse( + _get_tag(m.tags, ["TXXX:COMPOSITIONDATE"], first=True) + ), tracknumber=tracknumber, tracktotal=tracktotal, discnumber=discnumber, @@ -193,8 +218,8 @@ def _get_paired_frame(x: str) -> str | None: id=_get_tag(m.tags, ["----:net.sunsetglow.rose:ID"]), release_id=_get_tag(m.tags, ["----:net.sunsetglow.rose:RELEASEID"]), tracktitle=_get_tag(m.tags, ["\xa9nam"]), - releaseyear=_parse_year(_get_tag(m.tags, ["\xa9day"])), - originalyear=_parse_year( + releasedate=RoseDate.parse(_get_tag(m.tags, ["\xa9day"])), + originaldate=RoseDate.parse( _get_tag( m.tags, [ @@ -204,7 +229,7 @@ def _get_paired_frame(x: str) -> str | None: ], ) ), - compositionyear=_parse_year( + compositiondate=RoseDate.parse( _get_tag(m.tags, ["----:net.sunsetglow.rose:COMPOSITIONDATE"]) ), tracknumber=str(tracknumber), @@ -249,9 +274,9 @@ def _get_paired_frame(x: str) -> str | None: id=_get_tag(m.tags, ["roseid"]), release_id=_get_tag(m.tags, ["rosereleaseid"]), tracktitle=_get_tag(m.tags, ["title"]), - releaseyear=_parse_year(_get_tag(m.tags, ["date", "year"])), - originalyear=_parse_year(_get_tag(m.tags, ["originaldate", "originalyear"])), - compositionyear=_parse_year(_get_tag(m.tags, ["compositiondate"])), + releasedate=RoseDate.parse(_get_tag(m.tags, ["date", "year"])), + originaldate=RoseDate.parse(_get_tag(m.tags, ["originaldate", "originalyear"])), + compositiondate=RoseDate.parse(_get_tag(m.tags, ["compositiondate"])), tracknumber=_get_tag(m.tags, ["tracknumber"], first=True), tracktotal=_parse_int(_get_tag(m.tags, ["tracktotal"], first=True)), discnumber=_get_tag(m.tags, ["discnumber"], first=True), @@ -321,9 +346,9 @@ def _write_tag_with_description(name: str, value: str | None) -> None: _write_tag_with_description("TXXX:ROSEID", self.id) _write_tag_with_description("TXXX:ROSERELEASEID", self.release_id) _write_standard_tag("TIT2", self.tracktitle) - _write_standard_tag("TDRC", str(self.releaseyear).zfill(4)) - _write_standard_tag("TDOR", str(self.originalyear).zfill(4)) - _write_tag_with_description("TXXX:COMPOSITIONDATE", self.compositionyear) + _write_standard_tag("TDRC", str(self.releasedate)) + _write_standard_tag("TDOR", str(self.originaldate)) + _write_tag_with_description("TXXX:COMPOSITIONDATE", str(self.compositiondate)) _write_standard_tag("TRCK", self.tracknumber) _write_standard_tag("TPOS", self.discnumber) _write_standard_tag("TALB", self.releasetitle) @@ -352,13 +377,9 @@ def _write_tag_with_description(name: str, value: str | None) -> None: m.tags["----:net.sunsetglow.rose:ID"] = (self.id or "").encode() m.tags["----:net.sunsetglow.rose:RELEASEID"] = (self.release_id or "").encode() m.tags["\xa9nam"] = self.tracktitle or "" - m.tags["\xa9day"] = str(self.releaseyear).zfill(4) - m.tags["----:net.sunsetglow.rose:ORIGINALDATE"] = ( - str(self.originalyear).zfill(4).encode() - ) - m.tags["----:net.sunsetglow.rose:COMPOSITIONDATE"] = ( - str(self.compositionyear).zfill(4).encode() - ) + m.tags["\xa9day"] = str(self.releasedate) + m.tags["----:net.sunsetglow.rose:ORIGINALDATE"] = str(self.originaldate).encode() + m.tags["----:net.sunsetglow.rose:COMPOSITIONDATE"] = str(self.compositiondate).encode() m.tags["\xa9alb"] = self.releasetitle or "" m.tags["\xa9gen"] = ";".join(self.genre) m.tags["----:net.sunsetglow.rose:SECONDARYGENRE"] = ";".join( @@ -419,9 +440,9 @@ def _write_tag_with_description(name: str, value: str | None) -> None: m.tags["roseid"] = self.id or "" m.tags["rosereleaseid"] = self.release_id or "" m.tags["title"] = self.tracktitle or "" - m.tags["date"] = str(self.releaseyear).zfill(4) - m.tags["originaldate"] = str(self.originalyear).zfill(4) - m.tags["compositiondate"] = str(self.compositionyear).zfill(4) + m.tags["date"] = str(self.releasedate) + m.tags["originaldate"] = str(self.originaldate) + m.tags["compositiondate"] = str(self.compositiondate) m.tags["tracknumber"] = self.tracknumber or "" m.tags["discnumber"] = self.discnumber or "" m.tags["album"] = self.releasetitle or "" @@ -508,17 +529,6 @@ def _parse_int(x: str | None) -> int | None: return None -def _parse_year(value: str | None) -> int | None: - if not value: - return None - if YEAR_REGEX.match(value): - return int(value) - # There may be a time value after the date... allow that and other crap. - if m := DATE_REGEX.match(value): - return int(m[1]) - return None - - TAG_SPLITTER_REGEX = re.compile(r" \\\\ | / |; ?| vs\. ") diff --git a/rose/audiotags_test.py b/rose/audiotags_test.py index 730819c..e5f8dae 100644 --- a/rose/audiotags_test.py +++ b/rose/audiotags_test.py @@ -6,6 +6,7 @@ from conftest import TEST_TAGGER from rose.audiotags import ( AudioTags, + RoseDate, UnsupportedTagValueTypeError, _split_tag, format_artist_string, @@ -28,9 +29,9 @@ def test_getters(filename: str, track_num: str, duration: int) -> None: af = AudioTags.from_file(TEST_TAGGER / filename) assert af.releasetitle == "A Cool Album" assert af.releasetype == "album" - assert af.releaseyear == 1990 - assert af.originalyear == 1990 - assert af.compositionyear == 1984 + assert af.releasedate == RoseDate(1990, 2, 5) + assert af.originaldate == RoseDate(1990) + assert af.compositiondate == RoseDate(1984) assert af.genre == ["Electronic", "House"] assert af.secondarygenre == ["Minimal", "Ambient"] assert af.descriptor == ["Lush", "Warm"] @@ -75,14 +76,16 @@ def test_flush(isolated_dir: Path, filename: str, track_num: str, duration: int) # Inject one special case into here: modify the djmixer artist. This checks that we also clear # the original djmixer tag, so that the next read does not contain Artist EF and Artist FG. af.trackartists.djmixer = [Artist("New")] + # Also test date writing. + af.originaldate = RoseDate(1990, 4, 20) af.flush() af = AudioTags.from_file(fpath) assert af.releasetitle == "A Cool Album" assert af.releasetype == "album" - assert af.releaseyear == 1990 - assert af.originalyear == 1990 - assert af.compositionyear == 1984 + assert af.releasedate == RoseDate(1990, 2, 5) + assert af.originaldate == RoseDate(1990, 4, 20) + assert af.compositiondate == RoseDate(1984) assert af.genre == ["Electronic", "House"] assert af.secondarygenre == ["Minimal", "Ambient"] assert af.descriptor == ["Lush", "Warm"] diff --git a/rose/cache.py b/rose/cache.py index aab9038..0dba38a 100644 --- a/rose/cache.py +++ b/rose/cache.py @@ -50,7 +50,7 @@ import tomllib import uuid6 -from rose.audiotags import SUPPORTED_AUDIO_EXTENSIONS, AudioTags +from rose.audiotags import SUPPORTED_AUDIO_EXTENSIONS, AudioTags, RoseDate from rose.common import ( VERSION, Artist, @@ -209,9 +209,9 @@ class CachedRelease: datafile_mtime: str releasetitle: str releasetype: str - releaseyear: int | None - originalyear: int | None - compositionyear: int | None + releasedate: RoseDate | None + originaldate: RoseDate | None + compositiondate: RoseDate | None edition: str | None catalognumber: str | None new: bool @@ -237,9 +237,9 @@ def from_view(cls, c: Config, row: dict[str, Any], aliases: bool = True) -> Cach datafile_mtime=row["datafile_mtime"], releasetitle=row["releasetitle"], releasetype=row["releasetype"], - releaseyear=row["releaseyear"], - originalyear=row["originalyear"], - compositionyear=row["compositionyear"], + releasedate=RoseDate.parse(row["releasedate"]), + originaldate=RoseDate.parse(row["originaldate"]), + compositiondate=RoseDate.parse(row["compositiondate"]), catalognumber=row["catalognumber"], edition=row["edition"], disctotal=row["disctotal"], @@ -266,9 +266,9 @@ def dump(self) -> dict[str, Any]: "added_at": self.added_at, "releasetitle": self.releasetitle, "releasetype": self.releasetype, - "releaseyear": self.releaseyear, - "originalyear": self.originalyear, - "compositionyear": self.compositionyear, + "releasedate": str(self.releasedate) if self.releasedate else None, + "originaldate": str(self.originaldate) if self.originaldate else None, + "compositiondate": str(self.compositiondate) if self.compositiondate else None, "catalognumber": self.catalognumber, "edition": self.edition, "new": self.new, @@ -344,9 +344,15 @@ def dump(self, with_release_info: bool = True) -> dict[str, Any]: "releasetitle": self.release.releasetitle, "releasetype": self.release.releasetype, "disctotal": self.release.disctotal, - "releaseyear": self.release.releaseyear, - "originalyear": self.release.originalyear, - "compositionyear": self.release.compositionyear, + "releasedate": str(self.release.releasedate) + if self.release.releasedate + else None, + "originaldate": str(self.release.originaldate) + if self.release.originaldate + else None, + "compositiondate": str(self.release.compositiondate) + if self.release.compositiondate + else None, "catalognumber": self.release.catalognumber, "edition": self.release.edition, "new": self.release.new, @@ -649,9 +655,9 @@ def _update_cache_for_releases_executor( added_at="", releasetitle="", releasetype="", - releaseyear=None, - originalyear=None, - compositionyear=None, + releasedate=None, + originaldate=None, + compositiondate=None, catalognumber=None, edition=None, new=True, @@ -822,23 +828,23 @@ def _update_cache_for_releases_executor( release.releasetype = releasetype release_dirty = True - if tags.releaseyear != release.releaseyear: + if tags.releasedate != release.releasedate: logger.debug(f"Release year change detected for {source_path}, updating") - release.releaseyear = tags.releaseyear + release.releasedate = tags.releasedate release_dirty = True - if tags.originalyear != release.originalyear: + if tags.originaldate != release.originaldate: logger.debug( f"Release original year change detected for {source_path}, updating" ) - release.originalyear = tags.originalyear + release.originaldate = tags.originaldate release_dirty = True - if tags.compositionyear != release.compositionyear: + if tags.compositiondate != release.compositiondate: logger.debug( f"Release composition year change detected for {source_path}, updating" ) - release.compositionyear = tags.compositionyear + release.compositiondate = tags.compositiondate release_dirty = True if tags.edition != release.edition: @@ -1034,9 +1040,9 @@ def _update_cache_for_releases_executor( release.datafile_mtime, release.releasetitle, release.releasetype, - release.releaseyear, - release.originalyear, - release.compositionyear, + str(release.releasedate) if release.releasedate else None, + str(release.originaldate) if release.originaldate else None, + str(release.compositiondate) if release.compositiondate else None, release.edition, release.catalognumber, release.disctotal, @@ -1116,9 +1122,9 @@ def _update_cache_for_releases_executor( , datafile_mtime , title , releasetype - , releaseyear - , originalyear - , compositionyear + , releasedate + , originaldate + , compositiondate , edition , catalognumber , disctotal @@ -1132,9 +1138,9 @@ def _update_cache_for_releases_executor( , datafile_mtime = excluded.datafile_mtime , title = excluded.title , releasetype = excluded.releasetype - , releaseyear = excluded.releaseyear - , originalyear = excluded.originalyear - , compositionyear = excluded.compositionyear + , releasedate = excluded.releasedate + , originaldate = excluded.originaldate + , compositiondate = excluded.compositiondate , edition = excluded.edition , catalognumber = excluded.catalognumber , disctotal = excluded.disctotal @@ -1294,9 +1300,9 @@ def _update_cache_for_releases_executor( , discnumber , disctotal , releasetitle - , releaseyear - , originalyear - , compositionyear + , releasedate + , originaldate + , compositiondate , edition , catalognumber , releasetype @@ -1315,9 +1321,9 @@ def _update_cache_for_releases_executor( , process_string_for_fts(t.discnumber) AS discnumber , process_string_for_fts(r.disctotal) AS discnumber , process_string_for_fts(r.title) AS releasetitle - , process_string_for_fts(r.releaseyear) AS releaseyear - , process_string_for_fts(r.originalyear) AS originalyear - , process_string_for_fts(r.compositionyear) AS compositionyear + , process_string_for_fts(r.releasedate) AS releasedate + , process_string_for_fts(r.originaldate) AS originaldate + , process_string_for_fts(r.compositiondate) AS compositiondate , process_string_for_fts(r.edition) AS edition , process_string_for_fts(r.catalognumber) AS catalognumber , process_string_for_fts(r.releasetype) AS releasetype @@ -1499,7 +1505,7 @@ def update_cache_for_collages( desc_map: dict[str, str] = {} cursor = conn.execute( f""" - SELECT id, releasetitle, releaseyear, releaseartist_names, releaseartist_roles FROM releases_view + SELECT id, releasetitle, releasedate, releaseartist_names, releaseartist_roles FROM releases_view WHERE id IN ({','.join(['?']*len(releases))}) """, cached_collage.release_ids, @@ -1507,7 +1513,7 @@ def update_cache_for_collages( for row in cursor: desc_map[row["id"]] = calculate_release_logtext( title=row["releasetitle"], - releaseyear=row["releaseyear"], + releasedate=RoseDate.parse(row["releasedate"]), artists=_unpack_artists( c, row["releaseartist_names"], row["releaseartist_roles"] ), @@ -1722,7 +1728,7 @@ def update_cache_for_playlists( , t.source_path , t.trackartist_names , t.trackartist_roles - , r.releaseyear + , r.releasedate FROM tracks_view t JOIN releases_view r ON r.id = t.release_id WHERE t.id IN ({','.join(['?']*len(tracks))}) @@ -1735,7 +1741,7 @@ def update_cache_for_playlists( artists=_unpack_artists( c, row["trackartist_names"], row["trackartist_roles"] ), - releaseyear=row["releaseyear"], + releasedate=RoseDate.parse(row["releasedate"]), suffix=Path(row["source_path"]).suffix, ) for i, trk in enumerate(tracks): @@ -1895,7 +1901,7 @@ def get_release_logtext(c: Config, release_id: str) -> str | None: """Get a human-readable identifier for a release suitable for logging.""" with connect(c) as conn: cursor = conn.execute( - "SELECT releasetitle, releaseyear, releaseartist_names, releaseartist_roles FROM releases_view WHERE id = ?", + "SELECT releasetitle, releasedate, releaseartist_names, releaseartist_roles FROM releases_view WHERE id = ?", (release_id,), ) row = cursor.fetchone() @@ -1903,19 +1909,19 @@ def get_release_logtext(c: Config, release_id: str) -> str | None: return None return calculate_release_logtext( title=row["releasetitle"], - releaseyear=row["releaseyear"], + releasedate=RoseDate.parse(row["releasedate"]), artists=_unpack_artists(c, row["releaseartist_names"], row["releaseartist_roles"]), ) def calculate_release_logtext( title: str, - releaseyear: int | None, + releasedate: RoseDate | None, artists: ArtistMapping, ) -> str: logtext = f"{artistsfmt(artists)} - " - if releaseyear: - logtext += f"{releaseyear}. " + if releasedate: + logtext += f"{releasedate.year}. " logtext += title return logtext @@ -2067,7 +2073,7 @@ def get_track_logtext(c: Config, track_id: str) -> str | None: , t.source_path , t.trackartist_names , t.trackartist_roles - , r.releaseyear + , r.releasedate FROM tracks_view t JOIN releases_view r ON r.id = t.release_id WHERE t.id = ? @@ -2080,7 +2086,7 @@ def get_track_logtext(c: Config, track_id: str) -> str | None: return calculate_track_logtext( title=row["tracktitle"], artists=_unpack_artists(c, row["trackartist_names"], row["trackartist_roles"]), - releaseyear=row["releaseyear"], + releasedate=RoseDate.parse(row["releasedate"]), suffix=Path(row["source_path"]).suffix, ) @@ -2088,10 +2094,14 @@ def get_track_logtext(c: Config, track_id: str) -> str | None: def calculate_track_logtext( title: str, artists: ArtistMapping, - releaseyear: int | None, + releasedate: RoseDate | None, suffix: str, ) -> str: - return f"{artistsfmt(artists)} - {title or 'Unknown Title'} [{releaseyear}]{suffix}" + rval = f"{artistsfmt(artists)} - {title or 'Unknown Title'}" + if releasedate: + rval += f" [{releasedate.year}]" + rval += suffix + return rval def list_playlists(c: Config) -> list[str]: diff --git a/rose/cache.sql b/rose/cache.sql index d2acf56..b5f8b1a 100644 --- a/rose/cache.sql +++ b/rose/cache.sql @@ -14,9 +14,9 @@ CREATE TABLE releases ( datafile_mtime TEXT NOT NULL, title TEXT NOT NULL, releasetype TEXT NOT NULL, - releaseyear INTEGER, - originalyear INTEGER, - compositionyear INTEGER, + releasedate TEXT, + originaldate TEXT, + compositiondate TEXT, edition TEXT, catalognumber TEXT, disctotal INTEGER NOT NULL, @@ -172,9 +172,9 @@ CREATE VIRTUAL TABLE rules_engine_fts USING fts5 ( , disctotal , releasetitle , releasetype - , releaseyear - , originalyear - , compositionyear + , releasedate + , originaldate + , compositiondate , edition , catalognumber , genre @@ -233,9 +233,9 @@ CREATE VIEW releases_view AS , r.datafile_mtime , r.title AS releasetitle , r.releasetype - , r.releaseyear - , r.originalyear - , r.compositionyear + , r.releasedate + , r.originaldate + , r.compositiondate , r.edition , r.catalognumber , r.disctotal diff --git a/rose/cache_test.py b/rose/cache_test.py index 1084488..c5fa1d0 100644 --- a/rose/cache_test.py +++ b/rose/cache_test.py @@ -8,7 +8,7 @@ import tomllib from conftest import TEST_COLLAGE_1, TEST_PLAYLIST_1, TEST_RELEASE_1, TEST_RELEASE_2, TEST_RELEASE_3 -from rose.audiotags import AudioTags +from rose.audiotags import AudioTags, RoseDate from rose.cache import ( CACHE_SCHEMA_PATH, STORED_DATA_FILE_REGEX, @@ -174,7 +174,7 @@ def test_update_cache_releases(config: Config) -> None: with connect(config) as conn: cursor = conn.execute( """ - SELECT id, source_path, title, releasetype, releaseyear, compositionyear, catalognumber, new + SELECT id, source_path, title, releasetype, releasedate, compositiondate, catalognumber, new FROM releases WHERE id = ? """, (release_id,), @@ -183,8 +183,8 @@ def test_update_cache_releases(config: Config) -> None: assert row["source_path"] == str(release_dir) assert row["title"] == "I Love Blackpink" assert row["releasetype"] == "album" - assert row["releaseyear"] == 1990 - assert row["compositionyear"] is None + assert row["releasedate"] == "1990-02-05" + assert row["compositiondate"] is None assert row["catalognumber"] is None assert row["new"] @@ -311,13 +311,13 @@ def test_update_cache_releases_already_fully_cached(config: Config) -> None: # Assert that the release metadata was read correctly. with connect(config) as conn: cursor = conn.execute( - "SELECT id, source_path, title, releasetype, releaseyear, new FROM releases", + "SELECT id, source_path, title, releasetype, releasedate, new FROM releases", ) row = cursor.fetchone() assert row["source_path"] == str(release_dir) assert row["title"] == "I Love Blackpink" assert row["releasetype"] == "album" - assert row["releaseyear"] == 1990 + assert row["releasedate"] == "1990-02-05" assert row["new"] @@ -336,13 +336,13 @@ def test_update_cache_releases_disk_update_to_previously_cached(config: Config) # Assert that the release metadata was re-read and updated correctly. with connect(config) as conn: cursor = conn.execute( - "SELECT id, source_path, title, releasetype, releaseyear, new FROM releases", + "SELECT id, source_path, title, releasetype, releasedate, new FROM releases", ) row = cursor.fetchone() assert row["source_path"] == str(release_dir) assert row["title"] == "I Love Blackpink" assert row["releasetype"] == "album" - assert row["releaseyear"] == 1990 + assert row["releasedate"] == "1990-02-05" assert row["new"] @@ -396,13 +396,13 @@ def test_update_cache_releases_source_path_renamed(config: Config) -> None: # Assert that the release metadata was re-read and updated correctly. with connect(config) as conn: cursor = conn.execute( - "SELECT id, source_path, title, releasetype, releaseyear, new FROM releases", + "SELECT id, source_path, title, releasetype, releasedate, new FROM releases", ) row = cursor.fetchone() assert row["source_path"] == str(moved_release_dir) assert row["title"] == "I Love Blackpink" assert row["releasetype"] == "album" - assert row["releaseyear"] == 1990 + assert row["releasedate"] == "1990-02-05" assert row["new"] @@ -1089,9 +1089,9 @@ def test_list_releases(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 1", releasetype="album", - compositionyear=None, + compositiondate=None, catalognumber=None, - releaseyear=2023, + releasedate=RoseDate(2023), disctotal=1, new=False, genres=["Techno", "Deep House"], @@ -1101,7 +1101,7 @@ def test_list_releases(config: Config) -> None: "Electronic Dance Music", "House", ], - originalyear=None, + originaldate=None, edition=None, secondary_genres=["Rominimal", "Ambient"], parent_secondary_genres=[ @@ -1124,15 +1124,15 @@ def test_list_releases(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 2", releasetype="album", - releaseyear=2021, - compositionyear=None, + releasedate=RoseDate(2021), + compositiondate=None, catalognumber="DG-001", disctotal=1, new=False, genres=["Classical"], parent_genres=[], labels=["Native State"], - originalyear=2019, + originaldate=RoseDate(2019), edition="Deluxe", secondary_genres=["Orchestral"], parent_secondary_genres=[ @@ -1153,15 +1153,15 @@ def test_list_releases(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 3", releasetype="album", - releaseyear=2021, - compositionyear=1780, + releasedate=RoseDate(2021, 4, 20), + compositiondate=RoseDate(1780), catalognumber="DG-002", disctotal=1, new=True, genres=[], parent_genres=[], labels=[], - originalyear=None, + originaldate=None, edition=None, secondary_genres=[], parent_secondary_genres=[], @@ -1187,8 +1187,8 @@ def test_get_release_and_associated_tracks(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 1", releasetype="album", - releaseyear=2023, - compositionyear=None, + releasedate=RoseDate(2023), + compositiondate=None, catalognumber=None, disctotal=1, new=False, @@ -1200,7 +1200,7 @@ def test_get_release_and_associated_tracks(config: Config) -> None: "House", ], labels=["Silk Music"], - originalyear=None, + originaldate=None, edition=None, secondary_genres=["Rominimal", "Ambient"], parent_secondary_genres=[ @@ -1304,8 +1304,8 @@ def test_list_tracks(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 1", releasetype="album", - releaseyear=2023, - compositionyear=None, + releasedate=RoseDate(2023), + compositiondate=None, catalognumber=None, disctotal=1, new=False, @@ -1317,7 +1317,7 @@ def test_list_tracks(config: Config) -> None: "House", ], labels=["Silk Music"], - originalyear=None, + originaldate=None, edition=None, secondary_genres=["Rominimal", "Ambient"], parent_secondary_genres=[ @@ -1351,8 +1351,8 @@ def test_list_tracks(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 1", releasetype="album", - releaseyear=2023, - compositionyear=None, + releasedate=RoseDate(2023), + compositiondate=None, catalognumber=None, disctotal=1, new=False, @@ -1364,7 +1364,7 @@ def test_list_tracks(config: Config) -> None: "House", ], labels=["Silk Music"], - originalyear=None, + originaldate=None, edition=None, secondary_genres=["Rominimal", "Ambient"], parent_secondary_genres=[ @@ -1400,15 +1400,15 @@ def test_list_tracks(config: Config) -> None: datafile_mtime="999", releasetitle="Release 2", releasetype="album", - releaseyear=2021, - compositionyear=None, + releasedate=RoseDate(2021), + compositiondate=None, catalognumber="DG-001", new=False, disctotal=1, genres=["Classical"], parent_genres=[], labels=["Native State"], - originalyear=2019, + originaldate=RoseDate(2019), edition="Deluxe", secondary_genres=["Orchestral"], parent_secondary_genres=[ @@ -1441,15 +1441,15 @@ def test_list_tracks(config: Config) -> None: datafile_mtime="999", releasetitle="Release 3", releasetype="album", - releaseyear=2021, - compositionyear=1780, + releasedate=RoseDate(2021, 4, 20), + compositiondate=RoseDate(1780), catalognumber="DG-002", new=True, disctotal=1, genres=[], parent_genres=[], labels=[], - originalyear=None, + originaldate=None, edition=None, secondary_genres=[], parent_secondary_genres=[], @@ -1485,8 +1485,8 @@ def test_get_track(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 1", releasetype="album", - releaseyear=2023, - compositionyear=None, + releasedate=RoseDate(2023), + compositiondate=None, catalognumber=None, disctotal=1, new=False, @@ -1498,7 +1498,7 @@ def test_get_track(config: Config) -> None: "House", ], labels=["Silk Music"], - originalyear=None, + originaldate=None, edition=None, secondary_genres=["Rominimal", "Ambient"], parent_secondary_genres=[ @@ -1598,8 +1598,8 @@ def test_get_collage(config: Config) -> None: datafile_mtime="999", releasetitle="Release 1", releasetype="album", - releaseyear=2023, - compositionyear=None, + releasedate=RoseDate(2023), + compositiondate=None, catalognumber=None, new=False, disctotal=1, @@ -1611,7 +1611,7 @@ def test_get_collage(config: Config) -> None: "House", ], labels=["Silk Music"], - originalyear=None, + originaldate=None, edition=None, secondary_genres=["Rominimal", "Ambient"], parent_secondary_genres=[ @@ -1633,15 +1633,15 @@ def test_get_collage(config: Config) -> None: datafile_mtime="999", releasetitle="Release 2", releasetype="album", - releaseyear=2021, - compositionyear=None, + releasedate=RoseDate(2021), + compositiondate=None, catalognumber="DG-001", new=False, disctotal=1, genres=["Classical"], parent_genres=[], labels=["Native State"], - originalyear=2019, + originaldate=RoseDate(2019), edition="Deluxe", secondary_genres=["Orchestral"], parent_secondary_genres=[ @@ -1710,8 +1710,8 @@ def test_get_playlist(config: Config) -> None: added_at="0000-01-01T00:00:00+00:00", releasetitle="Release 1", releasetype="album", - releaseyear=2023, - compositionyear=None, + releasedate=RoseDate(2023), + compositiondate=None, catalognumber=None, disctotal=1, new=False, @@ -1723,7 +1723,7 @@ def test_get_playlist(config: Config) -> None: "House", ], labels=["Silk Music"], - originalyear=None, + originaldate=None, edition=None, secondary_genres=["Rominimal", "Ambient"], parent_secondary_genres=[ @@ -1759,15 +1759,15 @@ def test_get_playlist(config: Config) -> None: datafile_mtime="999", releasetitle="Release 2", releasetype="album", - releaseyear=2021, - compositionyear=None, + releasedate=RoseDate(2021), + compositiondate=None, catalognumber="DG-001", new=False, disctotal=1, genres=["Classical"], parent_genres=[], labels=["Native State"], - originalyear=2019, + originaldate=RoseDate(2019), edition="Deluxe", secondary_genres=["Orchestral"], parent_secondary_genres=[ diff --git a/rose/collages_test.py b/rose/collages_test.py index 606c704..267a290 100644 --- a/rose/collages_test.py +++ b/rose/collages_test.py @@ -132,8 +132,8 @@ def test_dump_collage(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "disctotal": 1, @@ -145,7 +145,7 @@ def test_dump_collage(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -177,15 +177,15 @@ def test_dump_collage(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 2", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": None, + "releasedate": "2021", + "compositiondate": None, "catalognumber": "DG-001", "new": False, "disctotal": 1, "genres": ["Classical"], "parent_genres": [], "labels": ["Native State"], - "originalyear": 2019, + "originaldate": "2019", "edition": "Deluxe", "secondary_genres": ["Orchestral"], "parent_secondary_genres": [ @@ -222,8 +222,8 @@ def test_dump_collages(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "disctotal": 1, @@ -235,7 +235,7 @@ def test_dump_collages(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -267,15 +267,15 @@ def test_dump_collages(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 2", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": None, + "releasedate": "2021", + "compositiondate": None, "catalognumber": "DG-001", "new": False, "disctotal": 1, "genres": ["Classical"], "parent_genres": [], "labels": ["Native State"], - "originalyear": 2019, + "originaldate": "2019", "edition": "Deluxe", "secondary_genres": ["Orchestral"], "parent_secondary_genres": [ diff --git a/rose/playlists_test.py b/rose/playlists_test.py index 3aae320..cd18182 100644 --- a/rose/playlists_test.py +++ b/rose/playlists_test.py @@ -157,8 +157,8 @@ def test_dump_playlist(config: Config) -> None: "release_id": "r1", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "genres": ["Techno", "Deep House"], @@ -169,7 +169,7 @@ def test_dump_playlist(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -216,14 +216,14 @@ def test_dump_playlist(config: Config) -> None: "release_id": "r2", "releasetitle": "Release 2", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": None, + "releasedate": "2021", + "compositiondate": None, "catalognumber": "DG-001", "new": False, "genres": ["Classical"], "parent_genres": [], "labels": ["Native State"], - "originalyear": 2019, + "originaldate": "2019", "edition": "Deluxe", "secondary_genres": ["Orchestral"], "parent_secondary_genres": [ @@ -279,8 +279,8 @@ def test_dump_playlists(config: Config) -> None: "release_id": "r1", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "genres": ["Techno", "Deep House"], @@ -291,7 +291,7 @@ def test_dump_playlists(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -338,14 +338,14 @@ def test_dump_playlists(config: Config) -> None: "release_id": "r2", "releasetitle": "Release 2", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": None, + "releasedate": "2021", + "compositiondate": None, "catalognumber": "DG-001", "new": False, "genres": ["Classical"], "parent_genres": [], "labels": ["Native State"], - "originalyear": 2019, + "originaldate": "2019", "edition": "Deluxe", "secondary_genres": ["Orchestral"], "parent_secondary_genres": [ diff --git a/rose/releases.py b/rose/releases.py index b1892dc..004cc7e 100644 --- a/rose/releases.py +++ b/rose/releases.py @@ -17,7 +17,7 @@ import tomllib from send2trash import send2trash -from rose.audiotags import AudioTags +from rose.audiotags import AudioTags, RoseDate from rose.cache import ( STORED_DATA_FILE_REGEX, CachedRelease, @@ -101,7 +101,7 @@ def delete_release(c: Config, release_id: str) -> None: send2trash(release.source_path) release_logtext = calculate_release_logtext( title=release.releasetitle, - releaseyear=release.releaseyear, + releasedate=release.releasedate, artists=release.releaseartists, ) logger.info(f"Trashed release {release_logtext}") @@ -118,7 +118,7 @@ def toggle_release_new(c: Config, release_id: str) -> None: release_logtext = calculate_release_logtext( title=release.releasetitle, - releaseyear=release.releaseyear, + releasedate=release.releasedate, artists=release.releaseartists, ) @@ -160,7 +160,7 @@ def set_release_cover_art( release_logtext = calculate_release_logtext( title=release.releasetitle, - releaseyear=release.releaseyear, + releasedate=release.releasedate, artists=release.releaseartists, ) @@ -181,7 +181,7 @@ def delete_release_cover_art(c: Config, release_id: str) -> None: release_logtext = calculate_release_logtext( title=release.releasetitle, - releaseyear=release.releaseyear, + releasedate=release.releasedate, artists=release.releaseartists, ) @@ -238,9 +238,9 @@ class MetadataRelease: title: str new: bool releasetype: str - releaseyear: int | None - originalyear: int | None - compositionyear: int | None + releasedate: RoseDate | None + originaldate: RoseDate | None + compositiondate: RoseDate | None artists: list[MetadataArtist] labels: list[str] edition: str | None @@ -256,9 +256,9 @@ def from_cache(cls, release: CachedRelease, tracks: list[CachedTrack]) -> Metada title=release.releasetitle, new=release.new, releasetype=release.releasetype, - releaseyear=release.releaseyear, - originalyear=release.originalyear, - compositionyear=release.compositionyear, + releasedate=release.releasedate, + originaldate=release.originaldate, + compositiondate=release.compositiondate, edition=release.catalognumber, catalognumber=release.edition, labels=release.labels, @@ -281,9 +281,9 @@ def serialize(self) -> str: # LOL TOML DOESN'T HAVE A NULL TYPE. Use -9999 as sentinel. If your music is legitimately # released in -9999, you should probably lay off the shrooms. data = asdict(self) - data["releaseyear"] = self.releaseyear or -9999 - data["originalyear"] = self.originalyear or -9999 - data["compositionyear"] = self.compositionyear or -9999 + data["releasedate"] = str(self.releasedate) if self.releasedate else "" + data["originaldate"] = str(self.originaldate) if self.originaldate else "" + data["compositiondate"] = str(self.compositiondate) if self.compositiondate else "" data["edition"] = self.edition or -9999 data["catalognumber"] = self.catalognumber or "" return tomli_w.dumps(data) @@ -295,9 +295,9 @@ def from_toml(cls, toml: str) -> MetadataRelease: title=d["title"], new=d["new"], releasetype=d["releasetype"], - originalyear=d["originalyear"] if d["originalyear"] != -9999 else None, - releaseyear=d["releaseyear"] if d["releaseyear"] != -9999 else None, - compositionyear=d["compositionyear"] if d["compositionyear"] != -9999 else None, + originaldate=RoseDate.parse(d["originaldate"]), + releasedate=RoseDate.parse(d["releasedate"]), + compositiondate=RoseDate.parse(d["compositiondate"]), genres=d["genres"], secondary_genres=d["secondary_genres"], descriptors=d["descriptors"], @@ -400,18 +400,18 @@ def edit_release( tags.releasetype = release_meta.releasetype.lower() dirty = True logger.debug(f"Modified tag detected for {t.source_path}: releasetype") - if tags.releaseyear != release_meta.releaseyear: - tags.releaseyear = release_meta.releaseyear + if tags.releasedate != release_meta.releasedate: + tags.releasedate = release_meta.releasedate dirty = True - logger.debug(f"Modified tag detected for {t.source_path}: releaseyear") - if tags.originalyear != release_meta.originalyear: - tags.originalyear = release_meta.originalyear + logger.debug(f"Modified tag detected for {t.source_path}: releasedate") + if tags.originaldate != release_meta.originaldate: + tags.originaldate = release_meta.originaldate dirty = True - logger.debug(f"Modified tag detected for {t.source_path}: originalyear") - if tags.compositionyear != release_meta.compositionyear: - tags.compositionyear = release_meta.compositionyear + logger.debug(f"Modified tag detected for {t.source_path}: originaldate") + if tags.compositiondate != release_meta.compositiondate: + tags.compositiondate = release_meta.compositiondate dirty = True - logger.debug(f"Modified tag detected for {t.source_path}: compositionyear") + logger.debug(f"Modified tag detected for {t.source_path}: compositiondate") if tags.edition != release_meta.edition: tags.edition = release_meta.edition dirty = True @@ -501,8 +501,8 @@ def create_single_release(c: Config, track_path: Path) -> None: title = (af.tracktitle or "Unknown Title").strip() dirname = f"{artistsfmt(af.trackartists)} - " - if af.releaseyear: - dirname += f"{af.releaseyear}. " + if af.releasedate: + dirname += f"{af.releasedate.year}. " dirname += title # Handle directory name collisions. collision_no = 2 diff --git a/rose/releases_test.py b/rose/releases_test.py index d640888..b25998d 100644 --- a/rose/releases_test.py +++ b/rose/releases_test.py @@ -8,7 +8,7 @@ import tomllib from conftest import TEST_RELEASE_1 -from rose.audiotags import AudioTags +from rose.audiotags import AudioTags, RoseDate from rose.cache import ( CachedRelease, CachedTrack, @@ -136,9 +136,9 @@ def test_edit_release(monkeypatch: Any, config: Config, source_dir: Path) -> Non title = "I Really Love Blackpink" new = false releasetype = "single" - releaseyear = 2222 - originalyear = 2000 - compositionyear = 1800 + releasedate = "2222" + originaldate = "2000" + compositiondate = "1800" artists = [ {{ name = "BLACKPINK", role = "main" }}, {{ name = "JISOO", role = "main" }}, @@ -189,9 +189,9 @@ def test_edit_release(monkeypatch: Any, config: Config, source_dir: Path) -> Non datafile_mtime=release.datafile_mtime, releasetitle="I Really Love Blackpink", releasetype="single", - releaseyear=2222, - originalyear=2000, - compositionyear=1800, + releasedate=RoseDate(2222), + originaldate=RoseDate(2000), + compositiondate=RoseDate(1800), catalognumber="Lalala", edition="Blabla", new=False, @@ -259,9 +259,9 @@ def test_edit_release_failure_and_resume( title = "I Really Love Blackpink" new = false releasetype = "bullshit" - releaseyear = 2222 - originalyear = -9999 - compositionyear = -9999 + releasedate = "2222" + originaldate = "" + compositiondate = "" artists = [ {{ name = "BLACKPINK", role = "main" }}, {{ name = "JISOO", role = "main" }}, @@ -307,9 +307,9 @@ def test_edit_release_failure_and_resume( title = "I Really Love Blackpink" new = false releasetype = "single" - releaseyear = 2222 - originalyear = -9999 - compositionyear = -9999 + releasedate = "2222" + originaldate = "" + compositiondate = "" artists = [ {{ name = "BLACKPINK", role = "main" }}, {{ name = "JISOO", role = "main" }}, @@ -363,9 +363,9 @@ def editfn(text: str, **_: Any) -> str: datafile_mtime=release.datafile_mtime, releasetitle="I Really Love Blackpink", releasetype="single", - releaseyear=2222, - originalyear=None, - compositionyear=None, + releasedate=RoseDate(2222), + originaldate=None, + compositiondate=None, catalognumber=None, edition=None, new=False, @@ -455,8 +455,8 @@ def test_dump_release(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "disctotal": 1, @@ -468,7 +468,7 @@ def test_dump_release(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -548,8 +548,8 @@ def test_dump_releases(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "disctotal": 1, @@ -561,7 +561,7 @@ def test_dump_releases(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -636,15 +636,15 @@ def test_dump_releases(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 2", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": None, + "releasedate": "2021", + "compositiondate": None, "catalognumber": "DG-001", "new": False, "disctotal": 1, "genres": ["Classical"], "parent_genres": [], "labels": ["Native State"], - "originalyear": 2019, + "originaldate": "2019", "edition": "Deluxe", "secondary_genres": ["Orchestral"], "parent_secondary_genres": [ @@ -689,15 +689,15 @@ def test_dump_releases(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 3", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": 1780, + "releasedate": "2021-04-20", + "compositiondate": "1780", "catalognumber": "DG-002", "new": True, "disctotal": 1, "genres": [], "parent_genres": [], "labels": [], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": [], "parent_secondary_genres": [], @@ -746,15 +746,15 @@ def test_dump_releases_matcher(config: Config) -> None: "added_at": "0000-01-01T00:00:00+00:00", "releasetitle": "Release 2", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": None, + "releasedate": "2021", + "compositiondate": None, "catalognumber": "DG-001", "new": False, "disctotal": 1, "genres": ["Classical"], "parent_genres": [], "labels": ["Native State"], - "originalyear": 2019, + "originaldate": "2019", "edition": "Deluxe", "secondary_genres": ["Orchestral"], "parent_secondary_genres": [ diff --git a/rose/rule_parser.py b/rose/rule_parser.py index f88cbcc..88ccb89 100644 --- a/rose/rule_parser.py +++ b/rose/rule_parser.py @@ -65,9 +65,9 @@ def __str__(self) -> str: "releaseartist[conductor]", "releaseartist[djmixer]", "releasetype", - "releaseyear", - "originalyear", - "compositionyear", + "releasedate", + "originaldate", + "compositiondate", "catalognumber", "edition", "genre", @@ -118,9 +118,9 @@ def __str__(self) -> str: "releaseartist[conductor]": ["releaseartist[conductor]"], "releaseartist[djmixer]": ["releaseartist[djmixer]"], "releasetype": ["releasetype"], - "releaseyear": ["releaseyear"], - "originalyear": ["originalyear"], - "compositionyear": ["compositionyear"], + "releasedate": ["releasedate"], + "originaldate": ["originaldate"], + "compositiondate": ["compositiondate"], "edition": ["edition"], "catalognumber": ["catalognumber"], "genre": ["genre"], @@ -165,9 +165,9 @@ def __str__(self) -> str: "releaseartist[conductor]", "releaseartist[djmixer]", "releasetype", - "releaseyear", - "originalyear", - "compositionyear", + "releasedate", + "originaldate", + "compositiondate", "edition", "catalognumber", "genre", @@ -184,9 +184,9 @@ def __str__(self) -> str: "disctotal", "releasetitle", "releasetype", - "releaseyear", - "originalyear", - "compositionyear", + "releasedate", + "originaldate", + "compositiondate", "edition", "catalognumber", ] @@ -202,9 +202,9 @@ def __str__(self) -> str: "releaseartist[djmixer]", "releasetype", "releasetype", - "releaseyear", - "originalyear", - "compositionyear", + "releasedate", + "originaldate", + "compositiondate", "edition", "catalognumber", "genre", diff --git a/rose/rule_parser_test.py b/rose/rule_parser_test.py index 997c603..1c8a575 100644 --- a/rose/rule_parser_test.py +++ b/rose/rule_parser_test.py @@ -77,7 +77,7 @@ def test_err(rule: str, err: str) -> None: tracknumber^Track$ ^ - Invalid tag: must be one of {tracktitle, trackartist, trackartist[main], trackartist[guest], trackartist[remixer], trackartist[producer], trackartist[composer], trackartist[conductor], trackartist[djmixer], tracknumber, tracktotal, discnumber, disctotal, releasetitle, releaseartist, releaseartist[main], releaseartist[guest], releaseartist[remixer], releaseartist[producer], releaseartist[composer], releaseartist[conductor], releaseartist[djmixer], releasetype, releaseyear, originalyear, compositionyear, edition, catalognumber, genre, secondarygenre, descriptor, label, artist}. The next character after a tag must be ':' or ','. + Invalid tag: must be one of {tracktitle, trackartist, trackartist[main], trackartist[guest], trackartist[remixer], trackartist[producer], trackartist[composer], trackartist[conductor], trackartist[djmixer], tracknumber, tracktotal, discnumber, disctotal, releasetitle, releaseartist, releaseartist[main], releaseartist[guest], releaseartist[remixer], releaseartist[producer], releaseartist[composer], releaseartist[conductor], releaseartist[djmixer], releasetype, releasedate, originaldate, compositiondate, edition, catalognumber, genre, secondarygenre, descriptor, label, artist}. The next character after a tag must be ':' or ','. """, ) @@ -250,7 +250,7 @@ def test_err(rule: str, err: str, matcher: MetadataMatcher | None = None) -> Non haha/delete ^ - Invalid tag: must be one of {tracktitle, trackartist, trackartist[main], trackartist[guest], trackartist[remixer], trackartist[producer], trackartist[composer], trackartist[conductor], trackartist[djmixer], tracknumber, discnumber, releasetitle, releaseartist, releaseartist[main], releaseartist[guest], releaseartist[remixer], releaseartist[producer], releaseartist[composer], releaseartist[conductor], releaseartist[djmixer], releasetype, releaseyear, originalyear, compositionyear, edition, catalognumber, genre, secondarygenre, descriptor, label, artist}. The next character after a tag must be ':' or ','. + Invalid tag: must be one of {tracktitle, trackartist, trackartist[main], trackartist[guest], trackartist[remixer], trackartist[producer], trackartist[composer], trackartist[conductor], trackartist[djmixer], tracknumber, discnumber, releasetitle, releaseartist, releaseartist[main], releaseartist[guest], releaseartist[remixer], releaseartist[producer], releaseartist[composer], releaseartist[conductor], releaseartist[djmixer], releasetype, releasedate, originaldate, compositiondate, edition, catalognumber, genre, secondarygenre, descriptor, label, artist}. The next character after a tag must be ':' or ','. """, ) @@ -261,7 +261,7 @@ def test_err(rule: str, err: str, matcher: MetadataMatcher | None = None) -> Non tracktitler/delete ^ - Invalid tag: must be one of {tracktitle, trackartist, trackartist[main], trackartist[guest], trackartist[remixer], trackartist[producer], trackartist[composer], trackartist[conductor], trackartist[djmixer], tracknumber, discnumber, releasetitle, releaseartist, releaseartist[main], releaseartist[guest], releaseartist[remixer], releaseartist[producer], releaseartist[composer], releaseartist[conductor], releaseartist[djmixer], releasetype, releaseyear, originalyear, compositionyear, edition, catalognumber, genre, secondarygenre, descriptor, label, artist}. The next character after a tag must be ':' or ','. + Invalid tag: must be one of {tracktitle, trackartist, trackartist[main], trackartist[guest], trackartist[remixer], trackartist[producer], trackartist[composer], trackartist[conductor], trackartist[djmixer], tracknumber, discnumber, releasetitle, releaseartist, releaseartist[main], releaseartist[guest], releaseartist[remixer], releaseartist[producer], releaseartist[composer], releaseartist[conductor], releaseartist[djmixer], releasetype, releasedate, originaldate, compositiondate, edition, catalognumber, genre, secondarygenre, descriptor, label, artist}. The next character after a tag must be ':' or ','. """, ) diff --git a/rose/rules.py b/rose/rules.py index f276faf..1fdef67 100644 --- a/rose/rules.py +++ b/rose/rules.py @@ -22,7 +22,7 @@ import click -from rose.audiotags import AudioTags +from rose.audiotags import AudioTags, RoseDate from rose.cache import ( CachedRelease, CachedTrack, @@ -227,9 +227,9 @@ def filter_track_false_positives_using_tags( match = False # fmt: off match = match or (field == "tracktitle" and matches_pattern(matcher.pattern, tags.tracktitle)) - match = match or (field == "releaseyear" and matches_pattern(matcher.pattern, tags.releaseyear)) - match = match or (field == "originalyear" and matches_pattern(matcher.pattern, tags.originalyear)) - match = match or (field == "compositionyear" and matches_pattern(matcher.pattern, tags.compositionyear)) + match = match or (field == "releasedate" and matches_pattern(matcher.pattern, tags.releasedate)) + match = match or (field == "originaldate" and matches_pattern(matcher.pattern, tags.originaldate)) + match = match or (field == "compositiondate" and matches_pattern(matcher.pattern, tags.compositiondate)) match = match or (field == "edition" and matches_pattern(matcher.pattern, tags.edition)) match = match or (field == "catalognumber" and matches_pattern(matcher.pattern, tags.catalognumber)) match = match or (field == "tracknumber" and matches_pattern(matcher.pattern, tags.tracknumber)) @@ -265,9 +265,9 @@ def filter_track_false_positives_using_tags( for i in ignore: # fmt: off skip = skip or (field == "tracktitle" and matches_pattern(i.pattern, tags.tracktitle)) - skip = skip or (field == "releaseyear" and matches_pattern(i.pattern, tags.releaseyear)) - skip = skip or (field == "originalyear" and matches_pattern(i.pattern, tags.originalyear)) - skip = skip or (field == "compositionyear" and matches_pattern(i.pattern, tags.compositionyear)) + skip = skip or (field == "releasedate" and matches_pattern(i.pattern, tags.releasedate)) + skip = skip or (field == "originaldate" and matches_pattern(i.pattern, tags.originaldate)) + skip = skip or (field == "compositiondate" and matches_pattern(i.pattern, tags.compositiondate)) skip = skip or (field == "edition" and matches_pattern(i.pattern, tags.edition)) skip = skip or (field == "catalognumber" and matches_pattern(i.pattern, tags.catalognumber)) skip = skip or (field == "tracknumber" and matches_pattern(i.pattern, tags.tracknumber)) @@ -310,7 +310,9 @@ def filter_track_false_positives_using_tags( return rval -Changes = tuple[str, str | int | None | list[str], str | int | None | list[str]] +Changes = tuple[ + str, str | int | RoseDate | None | list[str], str | int | RoseDate | None | list[str] +] def execute_metadata_actions( @@ -347,33 +349,33 @@ def artists(xs: list[str]) -> list[Artist]: if field == "tracktitle": tags.tracktitle = execute_single_action(act, tags.tracktitle) potential_changes.append(("title", origtags.tracktitle, tags.tracktitle)) - elif field == "releaseyear": - v = execute_single_action(act, tags.releaseyear) + elif field == "releasedate": + v = execute_single_action(act, tags.releasedate) try: - tags.releaseyear = int(v) if v else None + tags.releasedate = RoseDate.parse(v) except ValueError as e: raise InvalidReplacementValueError( - f"Failed to assign new value {v} to releaseyear: value must be integer" + f"Failed to assign new value {v} to releasedate: value must be date string" ) from e - potential_changes.append(("releaseyear", origtags.releaseyear, tags.releaseyear)) - elif field == "originalyear": - v = execute_single_action(act, tags.originalyear) + potential_changes.append(("releasedate", origtags.releasedate, tags.releasedate)) + elif field == "originaldate": + v = execute_single_action(act, tags.originaldate) try: - tags.originalyear = int(v) if v else None + tags.originaldate = RoseDate.parse(v) except ValueError as e: raise InvalidReplacementValueError( - f"Failed to assign new value {v} to originalyear: value must be integer" + f"Failed to assign new value {v} to originaldate: value must be date string" ) from e - potential_changes.append(("originalyear", origtags.originalyear, tags.originalyear)) - elif field == "compositionyear": - v = execute_single_action(act, tags.compositionyear) + potential_changes.append(("originaldate", origtags.originaldate, tags.originaldate)) + elif field == "compositiondate": + v = execute_single_action(act, tags.compositiondate) try: - tags.compositionyear = int(v) if v else None + tags.compositiondate = RoseDate.parse(v) except ValueError as e: raise InvalidReplacementValueError( - f"Failed to assign new value {v} to compositionyear: value must be integer" + f"Failed to assign new value {v} to compositiondate: value must be date string" ) from e - potential_changes.append(("compositionyear", origtags.compositionyear, tags.compositionyear)) + potential_changes.append(("compositiondate", origtags.compositiondate, tags.compositiondate)) elif field == "edition": tags.edition = execute_single_action(act, tags.edition) potential_changes.append(("edition", origtags.edition, tags.edition)) @@ -539,7 +541,7 @@ def artists(xs: list[str]) -> list[Artist]: update_cache_for_releases(c, source_paths) -def matches_pattern(pattern: MatcherPattern, value: str | int | None) -> bool: +def matches_pattern(pattern: MatcherPattern, value: str | int | RoseDate | None) -> bool: value = str(value) if value is not None else "" needle = pattern.pattern @@ -571,7 +573,7 @@ def matches_pattern(pattern: MatcherPattern, value: str | int | None) -> bool: # Factor out the logic for executing an action on a single-value tag and a multi-value tag. -def execute_single_action(action: MetadataAction, value: str | int | None) -> str | None: +def execute_single_action(action: MetadataAction, value: str | int | RoseDate | None) -> str | None: if action.pattern and not matches_pattern(action.pattern, value): return str(value) @@ -684,9 +686,9 @@ def filter_track_false_positives_using_read_cache( match = False # fmt: off match = match or (field == "tracktitle" and matches_pattern(matcher.pattern, t.tracktitle)) - match = match or (field == "releaseyear" and matches_pattern(matcher.pattern, t.release.releaseyear)) - match = match or (field == "originalyear" and matches_pattern(matcher.pattern, t.release.originalyear)) - match = match or (field == "compositionyear" and matches_pattern(matcher.pattern, t.release.compositionyear)) + match = match or (field == "releasedate" and matches_pattern(matcher.pattern, t.release.releasedate)) + match = match or (field == "originaldate" and matches_pattern(matcher.pattern, t.release.originaldate)) + match = match or (field == "compositiondate" and matches_pattern(matcher.pattern, t.release.compositiondate)) match = match or (field == "edition" and matches_pattern(matcher.pattern, t.release.edition)) match = match or (field == "catalognumber" and matches_pattern(matcher.pattern, t.release.catalognumber)) match = match or (field == "tracknumber" and matches_pattern(matcher.pattern, t.tracknumber)) @@ -734,9 +736,9 @@ def filter_release_false_positives_using_read_cache( match = False # Only attempt to match the release tags; ignore track tags. # fmt: off - match = match or (field == "releaseyear" and matches_pattern(matcher.pattern, r.releaseyear)) - match = match or (field == "originalyear" and matches_pattern(matcher.pattern, r.originalyear)) - match = match or (field == "compositionyear" and matches_pattern(matcher.pattern, r.compositionyear)) + match = match or (field == "releasedate" and matches_pattern(matcher.pattern, r.releasedate)) + match = match or (field == "originaldate" and matches_pattern(matcher.pattern, r.originaldate)) + match = match or (field == "compositiondate" and matches_pattern(matcher.pattern, r.compositiondate)) match = match or (field == "edition" and matches_pattern(matcher.pattern, r.edition)) match = match or (field == "catalognumber" and matches_pattern(matcher.pattern, r.catalognumber)) match = match or (field == "releasetitle" and matches_pattern(matcher.pattern, r.releasetitle)) diff --git a/rose/rules_test.py b/rose/rules_test.py index 204a0be..24673b2 100644 --- a/rose/rules_test.py +++ b/rose/rules_test.py @@ -5,7 +5,7 @@ import pytest -from rose.audiotags import AudioTags +from rose.audiotags import AudioTags, RoseDate from rose.cache import ( list_releases, list_tracks, @@ -115,11 +115,11 @@ def test_rules_fields_match_tracktitle(config: Config, source_dir: Path) -> None assert af.tracktitle == "8" -def test_rules_fields_match_releaseyear(config: Config, source_dir: Path) -> None: - rule = MetadataRule.parse("releaseyear:1990", ["replace:8"]) +def test_rules_fields_match_releasedate(config: Config, source_dir: Path) -> None: + rule = MetadataRule.parse("releasedate:1990", ["replace:8"]) execute_metadata_rule(config, rule, confirm_yes=False) af = AudioTags.from_file(source_dir / "Test Release 1" / "01.m4a") - assert af.releaseyear == 8 + assert af.releasedate == RoseDate(8) def test_rules_fields_match_releasetype(config: Config, source_dir: Path) -> None: diff --git a/rose/templates.py b/rose/templates.py index 524a223..3e9131d 100644 --- a/rose/templates.py +++ b/rose/templates.py @@ -17,6 +17,7 @@ import click import jinja2 +from rose.audiotags import RoseDate from rose.common import Artist, ArtistMapping, RoseExpectedError if typing.TYPE_CHECKING: @@ -144,7 +145,7 @@ class PathTemplatePair: DEFAULT_RELEASE_TEMPLATE = PathTemplate( """ {{ releaseartists | artistsfmt }} - -{% if releaseyear %}{{ releaseyear }}.{% endif %} +{% if releasedate %}{{ releasedate.year }}.{% endif %} {{ releasetitle }} {% if releasetype == "single" %}- {{ releasetype | releasetypefmt }}{% endif %} {% if new %}[NEW]{% endif %} @@ -290,9 +291,9 @@ def _calc_release_variables(release: CachedRelease, position: str | None) -> dic "added_at": release.added_at, "releasetitle": release.releasetitle, "releasetype": release.releasetype, - "releaseyear": release.releaseyear, - "originalyear": release.originalyear, - "compositionyear": release.compositionyear, + "releasedate": release.releasedate, + "originaldate": release.originaldate, + "compositiondate": release.compositiondate, "edition": release.edition, "catalognumber": release.catalognumber, "new": release.new, @@ -320,9 +321,9 @@ def _calc_track_variables(track: CachedTrack, position: str | None) -> dict[str, "trackartists": track.trackartists, "releasetitle": track.release.releasetitle, "releasetype": track.release.releasetype, - "releaseyear": track.release.releaseyear, - "originalyear": track.release.originalyear, - "compositionyear": track.release.compositionyear, + "releasedate": track.release.releasedate, + "originaldate": track.release.originaldate, + "compositiondate": track.release.compositiondate, "edition": track.release.edition, "catalognumber": track.release.catalognumber, "new": track.release.new, @@ -386,9 +387,9 @@ def _get_preview_releases(c: Config) -> tuple[CachedRelease, CachedRelease, Cach datafile_mtime="999", releasetitle="Kim Lip", releasetype="single", - releaseyear=2017, - originalyear=2017, - compositionyear=None, + releasedate=RoseDate(2017, 5, 23), + originaldate=RoseDate(2017, 5, 23), + compositiondate=None, edition=None, catalognumber="CMCC11088", new=True, @@ -424,9 +425,9 @@ def _get_preview_releases(c: Config) -> tuple[CachedRelease, CachedRelease, Cach datafile_mtime="999", releasetitle="Young Forever (花樣年華)", releasetype="album", - releaseyear=2016, - originalyear=2016, - compositionyear=None, + releasedate=RoseDate(2016), + originaldate=RoseDate(2016), + compositiondate=None, edition="Deluxe", catalognumber="L200001238", new=False, @@ -466,9 +467,9 @@ def _get_preview_releases(c: Config) -> tuple[CachedRelease, CachedRelease, Cach datafile_mtime="999", releasetitle="Images", releasetype="album", - releaseyear=1992, - originalyear=1991, - compositionyear=1907, + releasedate=RoseDate(1992), + originaldate=RoseDate(1991), + compositiondate=RoseDate(1907), edition=None, catalognumber="435-766 2", new=False, diff --git a/rose/templates_test.py b/rose/templates_test.py index 336b274..2482ade 100644 --- a/rose/templates_test.py +++ b/rose/templates_test.py @@ -4,6 +4,7 @@ import click from click.testing import CliRunner +from rose.audiotags import RoseDate from rose.cache import CachedRelease, CachedTrack from rose.common import Artist, ArtistMapping from rose.config import Config @@ -24,9 +25,9 @@ datafile_mtime="999", releasetitle="", releasetype="unknown", - releaseyear=None, - originalyear=None, - compositionyear=None, + releasedate=None, + originaldate=None, + compositiondate=None, edition=None, catalognumber=None, new=False, @@ -61,7 +62,7 @@ def test_default_templates() -> None: release = deepcopy(EMPTY_CACHED_RELEASE) release.releasetitle = "Title" - release.releaseyear = 2023 + release.releasedate = RoseDate(2023) release.releaseartists = ArtistMapping( main=[Artist("A1"), Artist("A2"), Artist("A3")], guest=[Artist("BB")], @@ -210,10 +211,10 @@ def test_classical(config: Config) -> None: """ {% if new %}{{ '{N}' }}{% endif %} {{ releaseartists.composer | map(attribute='name') | map('sortorder') | arrayfmt }} - - {% if compositionyear %}{{ compositionyear }}.{% endif %} + {% if compositiondate %}{{ compositiondate }}.{% endif %} {{ releasetitle }} performed by {{ releaseartists | artistsfmt(omit=["composer"]) }} - {% if releaseyear %}({{ releaseyear }}){% endif %} + {% if releasedate %}({{ releasedate }}){% endif %} """ ) diff --git a/rose/tracks_test.py b/rose/tracks_test.py index 37c7c55..b96555b 100644 --- a/rose/tracks_test.py +++ b/rose/tracks_test.py @@ -46,8 +46,8 @@ def test_dump_tracks(config: Config) -> None: "release_id": "r1", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "genres": ["Techno", "Deep House"], @@ -58,7 +58,7 @@ def test_dump_tracks(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -107,8 +107,8 @@ def test_dump_tracks(config: Config) -> None: "release_id": "r1", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "genres": ["Techno", "Deep House"], @@ -119,7 +119,7 @@ def test_dump_tracks(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -165,14 +165,14 @@ def test_dump_tracks(config: Config) -> None: "release_id": "r2", "releasetitle": "Release 2", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": None, + "releasedate": "2021", + "compositiondate": None, "catalognumber": "DG-001", "new": False, "genres": ["Classical"], "parent_genres": [], "labels": ["Native State"], - "originalyear": 2019, + "originaldate": "2019", "edition": "Deluxe", "secondary_genres": ["Orchestral"], "parent_secondary_genres": [ @@ -212,14 +212,14 @@ def test_dump_tracks(config: Config) -> None: "release_id": "r3", "releasetitle": "Release 3", "releasetype": "album", - "releaseyear": 2021, - "compositionyear": 1780, + "releasedate": "2021-04-20", + "compositiondate": "1780", "catalognumber": "DG-002", "new": True, "genres": [], "parent_genres": [], "labels": [], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": [], "parent_secondary_genres": [], @@ -266,8 +266,8 @@ def test_dump_tracks_with_matcher(config: Config) -> None: "release_id": "r1", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "genres": ["Techno", "Deep House"], @@ -278,7 +278,7 @@ def test_dump_tracks_with_matcher(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -327,8 +327,8 @@ def test_dump_tracks_with_matcher(config: Config) -> None: "release_id": "r1", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "genres": ["Techno", "Deep House"], @@ -339,7 +339,7 @@ def test_dump_tracks_with_matcher(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ @@ -393,8 +393,8 @@ def test_dump_track(config: Config) -> None: "release_id": "r1", "releasetitle": "Release 1", "releasetype": "album", - "releaseyear": 2023, - "compositionyear": None, + "releasedate": "2023", + "compositiondate": None, "catalognumber": None, "new": False, "genres": ["Techno", "Deep House"], @@ -405,7 +405,7 @@ def test_dump_track(config: Config) -> None: "House", ], "labels": ["Silk Music"], - "originalyear": None, + "originaldate": None, "edition": None, "secondary_genres": ["Rominimal", "Ambient"], "parent_secondary_genres": [ diff --git a/rose_vfs/virtualfs.py b/rose_vfs/virtualfs.py index a16111c..2c8236d 100644 --- a/rose_vfs/virtualfs.py +++ b/rose_vfs/virtualfs.py @@ -417,7 +417,7 @@ def list_release_paths( logtext = calculate_release_logtext( title=release.releasetitle, - releaseyear=release.releaseyear, + releasedate=release.releasedate, artists=release.releaseartists, ) @@ -521,7 +521,7 @@ def list_track_paths( logtext = calculate_track_logtext( title=track.tracktitle, artists=track.trackartists, - releaseyear=track.release.releaseyear, + releasedate=track.release.releasedate, suffix=track.source_path.suffix, ) @@ -1194,7 +1194,7 @@ def open(self, p: VirtualPath, flags: int) -> int: fh = self.fhandler.next() logtext = calculate_release_logtext( title=release.releasetitle, - releaseyear=release.releaseyear, + releasedate=release.releasedate, artists=release.releaseartists, ) logger.debug(