From 4e9ceada94be7c7a8c72df719ed4140c7725dcee Mon Sep 17 00:00:00 2001 From: blissful Date: Fri, 3 Nov 2023 11:37:37 -0400 Subject: [PATCH] cli todos --- conftest.py | 2 +- docs/AVAILABLE_COMMANDS.md | 16 +++++----- rose/__init__.py | 2 +- rose/cache.py | 4 +-- rose/cache_test.py | 12 ++++---- rose/cli.py | 58 ++++++++++++++++------------------- rose/config_test.py | 14 ++++----- rose/releases_test.py | 2 +- rose/rule_parser.py | 2 +- rose/rules.py | 62 +++++++++++++++++++------------------- 10 files changed, 84 insertions(+), 90 deletions(-) diff --git a/conftest.py b/conftest.py index 2101073..6a9028d 100644 --- a/conftest.py +++ b/conftest.py @@ -167,7 +167,7 @@ def seeded_cache(config: Config) -> None: (playlist_name, track_id, position, missing) VALUES ('Lala Lisa' , 't1' , 1 , false) , ('Lala Lisa' , 't3' , 2 , false); - """ # noqa: E501 + """ ) (config.music_source_dir / "!collages").mkdir() diff --git a/docs/AVAILABLE_COMMANDS.md b/docs/AVAILABLE_COMMANDS.md index e401e53..026cfc7 100644 --- a/docs/AVAILABLE_COMMANDS.md +++ b/docs/AVAILABLE_COMMANDS.md @@ -5,21 +5,21 @@ This document enumerates the commands available in Rosé's CLI. First, a quick note on the structure: Rosé primarily organizes commands by the resource they effect. Most commands are of the structure `rose {resource} {action}`. -- `fs/` _(See [Browsing with the Virtual Filesystem](./VIRTUAL_FILESYSTEM.md))_ +- fs/ _(see [Browsing with the Virtual Filesystem](./VIRTUAL_FILESYSTEM.md))_ - `fs mount`: Mount the virtual filesystem onto the configured `$fuse_mount_dir`. - `fs unmount`: Unmount the virtual filesystem by invoking `umount`. -- `cache/` _(See [Maintaining the Cache](./CACHE_MAINTENANCE.md))_ +- cache/ _(see [Maintaining the Cache](./CACHE_MAINTENANCE.md))_ - `cache update`: Scan the source directory and update the read cache with any new metadata changes. - `cache watch`: Start a watcher that will trigger `cache update` for any files and directories that have been modified. - `cache unwatch`: Kill the running cache watcher process. -- `releases/` _(See [Managing Releases](./RELEASES.md))_ +- releases/ _(see [Managing Releases](./RELEASES.md))_ - `releases print`: Print a single release's metadata in JSON. - `releases print-all`: Print all releases' metadata in JSON, with an optional matcher rule to filter out releases. - `releases import`: Import a release directory into the managed source - - directory. + directory. - `releases edit`: Edit a release's metadata as a text file in your `$EDITOR`. - `releases toggle-new`: Toggle the "new"-ness of a release. @@ -40,12 +40,12 @@ resource they effect. Most commands are of the structure `rose {resource} {actio suggest metadata improvements. - `releases create-single`: Create a "phony" single release from a track and copy the track into the new release. -- `tracks/` +- tracks/ - `tracks print`: Print a single track's metadata in JSON. - `tracks print-all`: Print all tracks' metadata in JSON, with an optional matcher rule to filter out tracks. - `tracks run-rule`: Run one or more metadata actions on a track. -- `collages/` _(See [Managing Playlists & Collages](./PLAYLISTS_COLLAGES.md))_ +- collages/ _(see [Managing Playlists & Collages](./PLAYLISTS_COLLAGES.md))_ - `collages print`: Print a single collage's metadata in JSON. - `collages print-all`: Print all collages' metadata in JSON. - `collages create`: Create a new collage. @@ -56,7 +56,7 @@ resource they effect. Most commands are of the structure `rose {resource} {actio - `collages rename`: Rename a collage. - `collages add-release`: Add a release to a collage. - `collages remove-release`: Remove a release from a collage. -- `playlists/` _(See [Managing Playlists & Collages](./PLAYLISTS_COLLAGES.md))_ +- playlists/ _(see [Managing Playlists & Collages](./PLAYLISTS_COLLAGES.md))_ - `playlists print`: Print a single playlist's metadata in JSON. - `playlists print-all`: Print all playlists' metadata in JSON. - `playlists create`: Create a new playlist. @@ -70,7 +70,7 @@ resource they effect. Most commands are of the structure `rose {resource} {actio - `playlists set-cover`: Set the cover art for a playlist. Replaces any existing cover art. - `playlists delete-cover`: Remove the cover art of a playlist. -- `rules/` _(See [Managing Your Music Metadata](./METADATA_MANAGEMENT.md))_ +- rules/ _(see [Managing Your Music Metadata](./METADATA_MANAGEMENT.md))_ - `rules run`: Run an ad hoc rule in the command line interface. You can also easily test rules with the `--dry-run` flag. - `rules run-stored`: Run the rules stored in the configuration file. diff --git a/rose/__init__.py b/rose/__init__.py index 611cca0..8175977 100644 --- a/rose/__init__.py +++ b/rose/__init__.py @@ -25,7 +25,7 @@ # captures logging output on its own, so by default, we do not attach our own. if "pytest" not in sys.modules or LOG_EVEN_THOUGH_WERE_IN_TEST: # pragma: no cover stream_template = "[%(asctime)s] %(levelname)s: %(message)s" - verbose_template = "[ts=%(asctime)s.%(msecs)d] [pid=%(process)d] [src=%(name)s:%(lineno)s] %(levelname)s: %(message)s" # noqa: E501 + verbose_template = "[ts=%(asctime)s.%(msecs)d] [pid=%(process)d] [src=%(name)s:%(lineno)s] %(levelname)s: %(message)s" stream_formatter = logging.Formatter( stream_template if not LOG_EVEN_THOUGH_WERE_IN_TEST else verbose_template, diff --git a/rose/cache.py b/rose/cache.py index 27c2e57..0cd3e39 100644 --- a/rose/cache.py +++ b/rose/cache.py @@ -77,7 +77,7 @@ def connect(c: Config) -> Iterator[sqlite3.Connection]: conn.close() -def migrate_database(c: Config) -> None: +def maybe_invalidate_cache_database(c: Config) -> None: """ "Migrate" the database. If the schema in the database does not match that on disk, then nuke the database and recreate it from scratch. Otherwise, no op. @@ -1318,7 +1318,7 @@ def _update_cache_for_releases_executor( WHERE t.id IN ({",".join(["?"]*len(upd_track_ids))}) OR r.id IN ({",".join(["?"]*len(upd_release_ids))}) GROUP BY t.id - """, # noqa: E501 + """, [*upd_track_ids, *upd_release_ids], ) diff --git a/rose/cache_test.py b/rose/cache_test.py index 3fd7c6d..81927fd 100644 --- a/rose/cache_test.py +++ b/rose/cache_test.py @@ -39,7 +39,7 @@ list_playlists, list_releases, lock, - migrate_database, + maybe_invalidate_cache_database, playlist_exists, release_exists, track_exists, @@ -55,7 +55,7 @@ def test_schema(config: Config) -> None: """Test that the schema successfully bootstraps.""" with CACHE_SCHEMA_PATH.open("rb") as fp: schema_hash = hashlib.sha256(fp.read()).hexdigest() - migrate_database(config) + maybe_invalidate_cache_database(config) with connect(config) as conn: cursor = conn.execute("SELECT schema_hash, config_hash, version FROM _schema_hash") row = cursor.fetchone() @@ -87,7 +87,7 @@ def test_migration(config: Config) -> None: with CACHE_SCHEMA_PATH.open("rb") as fp: latest_schema_hash = hashlib.sha256(fp.read()).hexdigest() - migrate_database(config) + maybe_invalidate_cache_database(config) with connect(config) as conn: cursor = conn.execute("SELECT schema_hash, config_hash, version FROM _schema_hash") row = cursor.fetchone() @@ -134,7 +134,7 @@ def test_update_cache_all(config: Config) -> None: """ INSERT INTO releases (id, source_path, virtual_dirname, added_at, datafile_mtime, title, releasetype, multidisc, formatted_artists) VALUES ('aaaaaa', '/nonexistent', '0000-01-01T00:00:00+00:00', '999', 'nonexistent', 'aa', 'unknown', false, 'aa;aa') - """ # noqa: E501 + """ ) update_cache(config) @@ -283,7 +283,7 @@ def test_update_cache_releases_preserves_track_ids_across_rebuilds(config: Confi # Nuke the database. config.cache_database_path.unlink() - migrate_database(config) + maybe_invalidate_cache_database(config) # Repeat cache population. update_cache_for_releases(config, [release_dir]) @@ -429,7 +429,7 @@ def test_update_cache_releases_delete_nonexistent(config: Config) -> None: """ INSERT INTO releases (id, source_path, virtual_dirname, added_at, datafile_mtime, title, releasetype, multidisc, formatted_artists) VALUES ('aaaaaa', '/nonexistent', '0000-01-01T00:00:00+00:00', '999', 'nonexistent', 'aa', 'unknown', false, 'aa;aa') - """ # noqa: E501 + """ ) update_cache_evict_nonexistent_releases(config) with connect(config) as conn: diff --git a/rose/cli.py b/rose/cli.py index daddf27..d465f51 100644 --- a/rose/cli.py +++ b/rose/cli.py @@ -17,7 +17,7 @@ import click -from rose.cache import migrate_database, update_cache +from rose.cache import maybe_invalidate_cache_database, update_cache from rose.collages import ( add_release_to_collage, create_collage, @@ -73,7 +73,7 @@ class Context: # fmt: off @click.group() @click.option("--verbose", "-v", is_flag=True, help="Emit verbose logging.") -@click.option("--config", "-c", type=click.Path(path_type=Path), help="Override the config file location.") # noqa: E501 +@click.option("--config", "-c", type=click.Path(path_type=Path), help="Override the config file location.") @click.pass_context # fmt: on def cli(cc: click.Context, verbose: bool, config: Path | None = None) -> None: @@ -81,12 +81,9 @@ def cli(cc: click.Context, verbose: bool, config: Path | None = None) -> None: cc.obj = Context( config=Config.parse(config_path_override=config), ) - if verbose: logging.getLogger().setLevel(logging.DEBUG) - - # Migrate the database on every command invocation. - migrate_database(cc.obj.config) + maybe_invalidate_cache_database(cc.obj.config) @cli.command() @@ -110,7 +107,7 @@ def cache() -> None: # fmt: off @cache.command() -@click.option("--force", "-f", is_flag=True, help="Force re-read all data from disk, even for unchanged files.") # noqa: E501 +@click.option("--force", "-f", is_flag=True, help="Force re-read all data from disk, even for unchanged files.") @click.pass_obj # fmt: on def update(ctx: Context, force: bool) -> None: @@ -120,7 +117,7 @@ def update(ctx: Context, force: bool) -> None: # fmt: off @cache.command() -@click.option("--foreground", "-f", is_flag=True, help="Run the filesystem watcher in the foreground (default: daemon).") # noqa: E501 +@click.option("--foreground", "-f", is_flag=True, help="Run the filesystem watcher in the foreground (default: daemon).") @click.pass_obj # fmt: on def watch(ctx: Context, foreground: bool) -> None: @@ -155,7 +152,7 @@ def fs() -> None: # fmt: off @fs.command() -@click.option("--foreground", "-f", is_flag=True, help="Run the FUSE controller in the foreground (default: daemon).") # noqa: E501 +@click.option("--foreground", "-f", is_flag=True, help="Run the FUSE controller in the foreground (default: daemon).") @click.pass_obj # fmt: on def mount(ctx: Context, foreground: bool) -> None: @@ -184,6 +181,7 @@ def unmount(ctx: Context) -> None: @cli.group() def releases() -> None: """Manage releases""" + # TODO: print / run-rule / extract-covers / add-metadata-url / search-metadata-urls / create-single / import @releases.command(name="print-all") @@ -211,6 +209,18 @@ def toggle_new(ctx: Context, release: str) -> None: toggle_release_new(ctx.config, release) +@releases.command(name="delete") +@click.argument("release", type=str, nargs=1) +@click.pass_obj +def delete3(ctx: Context, release: str) -> None: + """ + Delete a release from the library. The release is moved to the trash bin, following the + freedesktop spec. + """ + release = parse_release_from_potential_path(ctx.config, release) + delete_release(ctx.config, release) + + @releases.command() @click.argument("release", type=str, nargs=1) @click.argument("cover", type=click.Path(path_type=Path), nargs=1) @@ -230,18 +240,6 @@ def delete_cover(ctx: Context, release: str) -> None: delete_release_cover_art(ctx.config, release) -@releases.command(name="delete") -@click.argument("release", type=str, nargs=1) -@click.pass_obj -def delete3(ctx: Context, release: str) -> None: - """ - Delete a release from the library. The release is moved to the trash bin, following the - freedesktop spec. - """ - release = parse_release_from_potential_path(ctx.config, release) - delete_release(ctx.config, release) - - @releases.command() @click.argument("track_path", type=click.Path(path_type=Path), nargs=1) @click.pass_obj @@ -253,12 +251,14 @@ def create_single(ctx: Context, track_path: Path) -> None: @cli.group() def tracks() -> None: """Manage tracks""" + # TODO: print / print-all / run-rule raise UnimplementedError("Coming soon!") @cli.group() def collages() -> None: """Manage collages""" + # TODO: print @collages.command() @@ -324,6 +324,7 @@ def print_all1(ctx: Context) -> None: @cli.group() def playlists() -> None: """Manage playlists""" + # TODO: print @playlists.command(name="create") @@ -406,13 +407,6 @@ def delete_cover2(ctx: Context, playlist: str) -> None: delete_playlist_cover_art(ctx.config, playlist) -@playlists.command() -@click.pass_obj -def extract_covers(ctx: Context) -> None: # noqa: ARG001 - """Extract all embedded cover arts to external cover art files""" - raise UnimplementedError("Coming soon!") - - @cli.group() def rules() -> None: """Run metadata update rules on the entire library""" @@ -422,7 +416,7 @@ def rules() -> None: # fmt: off @click.argument("matcher", type=str, nargs=1) @click.argument("actions", type=str, nargs=-1) -@click.option("--dry-run", "-d", is_flag=True, help="Display intended changes without applying them.") # noqa: E501 +@click.option("--dry-run", "-d", is_flag=True, help="Display intended changes without applying them.") @click.option("--yes", "-y", is_flag=True, help="Bypass confirmation prompts.") # fmt: on @click.pass_obj @@ -436,10 +430,10 @@ def run(ctx: Context, matcher: str, actions: list[str], dry_run: bool, yes: bool @rules.command() -@click.option( - "--dry-run", "-d", is_flag=True, help="Display intended changes without applying them." -) # noqa: E501 +# fmt: off +@click.option("--dry-run", "-d", is_flag=True, help="Display intended changes without applying them.") @click.option("--yes", "-y", is_flag=True, help="Bypass confirmation prompts.") +# fmt: on @click.pass_obj def run_stored(ctx: Context, dry_run: bool, yes: bool) -> None: """Run the rules stored in the config""" diff --git a/rose/config_test.py b/rose/config_test.py index 30e5d50..c27b093 100644 --- a/rose/config_test.py +++ b/rose/config_test.py @@ -48,7 +48,7 @@ def test_config_full() -> None: [[stored_metadata_rules]] matcher = "tracktitle:lala" actions = ["replace:hihi"] - """ # noqa: E501 + """ ) c = Config.parse(config_path_override=path) @@ -333,7 +333,7 @@ def write(x: str) -> None: Config.parse(config_path_override=path) assert ( str(excinfo.value) - == f"Cannot specify both fuse_artists_whitelist and fuse_artists_blacklist in configuration file ({path}): must specify only one or the other" # noqa: E501 + == f"Cannot specify both fuse_artists_whitelist and fuse_artists_blacklist in configuration file ({path}): must specify only one or the other" ) # fuse_genres_whitelist + fuse_genres_blacklist @@ -342,7 +342,7 @@ def write(x: str) -> None: Config.parse(config_path_override=path) assert ( str(excinfo.value) - == f"Cannot specify both fuse_genres_whitelist and fuse_genres_blacklist in configuration file ({path}): must specify only one or the other" # noqa: E501 + == f"Cannot specify both fuse_genres_whitelist and fuse_genres_blacklist in configuration file ({path}): must specify only one or the other" ) # fuse_labels_whitelist + fuse_labels_blacklist @@ -351,7 +351,7 @@ def write(x: str) -> None: Config.parse(config_path_override=path) assert ( str(excinfo.value) - == f"Cannot specify both fuse_labels_whitelist and fuse_labels_blacklist in configuration file ({path}): must specify only one or the other" # noqa: E501 + == f"Cannot specify both fuse_labels_whitelist and fuse_labels_blacklist in configuration file ({path}): must specify only one or the other" ) # cover_art_stems @@ -411,12 +411,12 @@ def write(x: str) -> None: Config.parse(config_path_override=path) assert ( str(excinfo.value) - == f"Invalid value in stored_metadata_rules in configuration file ({path}): list values must be a dict: got " # noqa: E501 + == f"Invalid value in stored_metadata_rules in configuration file ({path}): list values must be a dict: got " ) write( config + '\nstored_metadata_rules = [{ matcher = "tracktitle:hi", actions = ["delete:hi"] }]' - ) # noqa: E501 + ) with pytest.raises(InvalidConfigValueError) as excinfo: Config.parse(config_path_override=path) assert ( @@ -427,5 +427,5 @@ def write(x: str) -> None: delete:hi ^ Found another section after the action kind, but the delete action has no parameters. Please remove this section. -""" # noqa: E501 +""" ) diff --git a/rose/releases_test.py b/rose/releases_test.py index d6a526c..89f9d27 100644 --- a/rose/releases_test.py +++ b/rose/releases_test.py @@ -165,7 +165,7 @@ def test_edit_release(monkeypatch: Any, config: Config, source_dir: Path) -> Non cover_image_path=None, added_at=release.added_at, datafile_mtime=release.datafile_mtime, - virtual_dirname="{NEW} BLACKPINK;JISOO - 2222. I Really Love Blackpink - Single [J-Pop;Pop-Rap]", # noqa: E501 + virtual_dirname="{NEW} BLACKPINK;JISOO - 2222. I Really Love Blackpink - Single [J-Pop;Pop-Rap]", title="I Really Love Blackpink", releasetype="single", year=2222, diff --git a/rose/rule_parser.py b/rose/rule_parser.py index 0aaeb47..1cf3b92 100644 --- a/rose/rule_parser.py +++ b/rose/rule_parser.py @@ -337,7 +337,7 @@ def parse_action(raw: str, action_number: int) -> MetadataAction: "split", "add", "delete", - ] # noqa: E501 + ] if action_kind not in valid_actions: feedback = f"Invalid action kind: must be one of {{{', '.join(valid_actions)}}}." if idx == 0 and ":" in raw: diff --git a/rose/rules.py b/rose/rules.py index 903503b..3701563 100644 --- a/rose/rules.py +++ b/rose/rules.py @@ -130,16 +130,16 @@ def execute_metadata_rule( for field in rule.matcher.tags: match = False # fmt: off - match = match or (field == "tracktitle" and matches_pattern(rule.matcher.pattern, tags.title)) # noqa: E501 - match = match or (field == "year" and matches_pattern(rule.matcher.pattern, tags.year)) # noqa: E501 - match = match or (field == "tracknumber" and matches_pattern(rule.matcher.pattern, tags.tracknumber)) # noqa: E501 - match = match or (field == "discnumber" and matches_pattern(rule.matcher.pattern, tags.discnumber)) # noqa: E501 - match = match or (field == "albumtitle" and matches_pattern(rule.matcher.pattern, tags.album)) # noqa: E501 - match = match or (field == "releasetype" and matches_pattern(rule.matcher.pattern, tags.releasetype)) # noqa: E501 - match = match or (field == "genre" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.genre)) # noqa: E501 - match = match or (field == "label" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.label)) # noqa: E501 - match = match or (field == "trackartist" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.trackartists.all)) # noqa: E501 - match = match or (field == "albumartist" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.albumartists.all)) # noqa: E501 + match = match or (field == "tracktitle" and matches_pattern(rule.matcher.pattern, tags.title)) + match = match or (field == "year" and matches_pattern(rule.matcher.pattern, tags.year)) + match = match or (field == "tracknumber" and matches_pattern(rule.matcher.pattern, tags.tracknumber)) + match = match or (field == "discnumber" and matches_pattern(rule.matcher.pattern, tags.discnumber)) + match = match or (field == "albumtitle" and matches_pattern(rule.matcher.pattern, tags.album)) + match = match or (field == "releasetype" and matches_pattern(rule.matcher.pattern, tags.releasetype)) + match = match or (field == "genre" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.genre)) + match = match or (field == "label" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.label)) + match = match or (field == "trackartist" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.trackartists.all)) + match = match or (field == "albumartist" and any(matches_pattern(rule.matcher.pattern, x) for x in tags.albumartists.all)) # fmt: on if match: matcher_audiotags.append(tags) @@ -176,16 +176,16 @@ def execute_metadata_rule( potential_changes.append(("year", origtags.year, tags.year)) elif field == "tracknumber": tags.tracknumber = execute_single_action(act, tags.tracknumber) - potential_changes.append(("tracknumber", origtags.tracknumber, tags.tracknumber)) # noqa: E501 + potential_changes.append(("tracknumber", origtags.tracknumber, tags.tracknumber)) elif field == "discnumber": tags.discnumber = execute_single_action(act, tags.discnumber) - potential_changes.append(("discnumber", origtags.discnumber, tags.discnumber)) # noqa: E501 + potential_changes.append(("discnumber", origtags.discnumber, tags.discnumber)) elif field == "albumtitle": tags.album = execute_single_action(act, tags.album) potential_changes.append(("album", origtags.album, tags.album)) elif field == "releasetype": tags.releasetype = execute_single_action(act, tags.releasetype) or "unknown" - potential_changes.append(("releasetype", origtags.releasetype, tags.releasetype)) # noqa: E501 + potential_changes.append(("releasetype", origtags.releasetype, tags.releasetype)) elif field == "genre": tags.genre = execute_multi_value_action(act, tags.genre) potential_changes.append(("genre", origtags.genre, tags.genre)) @@ -194,30 +194,30 @@ def execute_metadata_rule( potential_changes.append(("label", origtags.label, tags.label)) elif field == "trackartist": tags.trackartists.main = execute_multi_value_action(act, tags.trackartists.main) - potential_changes.append(("trackartist[main]", origtags.trackartists.main, tags.trackartists.main)) # noqa: E501 + potential_changes.append(("trackartist[main]", origtags.trackartists.main, tags.trackartists.main)) tags.trackartists.guest = execute_multi_value_action(act, tags.trackartists.guest) - potential_changes.append(("trackartist[guest]", origtags.trackartists.guest, tags.trackartists.guest)) # noqa: E501 + potential_changes.append(("trackartist[guest]", origtags.trackartists.guest, tags.trackartists.guest)) tags.trackartists.remixer = execute_multi_value_action(act, tags.trackartists.remixer) - potential_changes.append(("trackartist[remixer]", origtags.trackartists.remixer, tags.trackartists.remixer)) # noqa: E501 + potential_changes.append(("trackartist[remixer]", origtags.trackartists.remixer, tags.trackartists.remixer)) tags.trackartists.producer = execute_multi_value_action(act, tags.trackartists.producer) - potential_changes.append(("trackartist[producer]", origtags.trackartists.producer, tags.trackartists.producer)) # noqa: E501 + potential_changes.append(("trackartist[producer]", origtags.trackartists.producer, tags.trackartists.producer)) tags.trackartists.composer = execute_multi_value_action(act, tags.trackartists.composer) - potential_changes.append(("trackartist[composer]", origtags.trackartists.composer, tags.trackartists.composer)) # noqa: E501 + potential_changes.append(("trackartist[composer]", origtags.trackartists.composer, tags.trackartists.composer)) tags.trackartists.djmixer = execute_multi_value_action(act, tags.trackartists.djmixer) - potential_changes.append(("trackartist[djmixer]", origtags.trackartists.djmixer, tags.trackartists.djmixer)) # noqa: E501 + potential_changes.append(("trackartist[djmixer]", origtags.trackartists.djmixer, tags.trackartists.djmixer)) elif field == "albumartist": - tags.albumartists.main = execute_multi_value_action(act, tags.albumartists.main) # noqa: E501 - potential_changes.append(("albumartist[main]", origtags.albumartists.main, tags.albumartists.main)) # noqa: E501 - tags.albumartists.guest = execute_multi_value_action(act, tags.albumartists.guest) # noqa: E501 - potential_changes.append(("albumartist[guest]", origtags.albumartists.guest, tags.albumartists.guest)) # noqa: E501 - tags.albumartists.remixer = execute_multi_value_action(act, tags.albumartists.remixer) # noqa: E501 - potential_changes.append(("albumartist[remixer]", origtags.albumartists.remixer, tags.albumartists.remixer)) # noqa: E501 - tags.albumartists.producer = execute_multi_value_action(act, tags.albumartists.producer) # noqa: E501 - potential_changes.append(("albumartist[producer]", origtags.albumartists.producer, tags.albumartists.producer)) # noqa: E501 - tags.albumartists.composer = execute_multi_value_action(act, tags.albumartists.composer) # noqa: E501 - potential_changes.append(("albumartist[composer]", origtags.albumartists.composer, tags.albumartists.composer)) # noqa: E501 - tags.albumartists.djmixer = execute_multi_value_action(act, tags.albumartists.djmixer) # noqa: E501 - potential_changes.append(("albumartist[djmixer]", origtags.albumartists.djmixer, tags.albumartists.djmixer)) # noqa: E501 + tags.albumartists.main = execute_multi_value_action(act, tags.albumartists.main) + potential_changes.append(("albumartist[main]", origtags.albumartists.main, tags.albumartists.main)) + tags.albumartists.guest = execute_multi_value_action(act, tags.albumartists.guest) + potential_changes.append(("albumartist[guest]", origtags.albumartists.guest, tags.albumartists.guest)) + tags.albumartists.remixer = execute_multi_value_action(act, tags.albumartists.remixer) + potential_changes.append(("albumartist[remixer]", origtags.albumartists.remixer, tags.albumartists.remixer)) + tags.albumartists.producer = execute_multi_value_action(act, tags.albumartists.producer) + potential_changes.append(("albumartist[producer]", origtags.albumartists.producer, tags.albumartists.producer)) + tags.albumartists.composer = execute_multi_value_action(act, tags.albumartists.composer) + potential_changes.append(("albumartist[composer]", origtags.albumartists.composer, tags.albumartists.composer)) + tags.albumartists.djmixer = execute_multi_value_action(act, tags.albumartists.djmixer) + potential_changes.append(("albumartist[djmixer]", origtags.albumartists.djmixer, tags.albumartists.djmixer)) # fmt: on # Compute real changes by diffing the tags, and then store.