lastgenre: Add --pretend option for previewing genre changes (#6008)

Introduce a `--pretend` option to the lastgenre plugin, allowing users
to preview genre changes without making any modifications to their
library. This feature enhances user control by showing potential changes
before they are applied.
This commit is contained in:
J0J0 Todos 2025-09-21 07:21:16 +02:00 committed by GitHub
commit 2c1aa27385
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 84 additions and 15 deletions

View file

@ -461,6 +461,12 @@ class LastGenrePlugin(plugins.BeetsPlugin):
def commands(self):
lastgenre_cmd = ui.Subcommand("lastgenre", help="fetch genres")
lastgenre_cmd.parser.add_option(
"-p",
"--pretend",
action="store_true",
help="show actions but do nothing",
)
lastgenre_cmd.parser.add_option(
"-f",
"--force",
@ -521,45 +527,64 @@ class LastGenrePlugin(plugins.BeetsPlugin):
def lastgenre_func(lib, opts, args):
write = ui.should_write()
pretend = getattr(opts, "pretend", False)
self.config.set_args(opts)
if opts.album:
# Fetch genres for whole albums
for album in lib.albums(args):
album.genre, src = self._get_genre(album)
album_genre, src = self._get_genre(album)
prefix = "Pretend: " if pretend else ""
self._log.info(
'genre for album "{0.album}" ({1}): {0.genre}',
'{}genre for album "{.album}" ({}): {}',
prefix,
album,
src,
album_genre,
)
if "track" in self.sources:
album.store(inherit=False)
else:
album.store()
if not pretend:
album.genre = album_genre
if "track" in self.sources:
album.store(inherit=False)
else:
album.store()
for item in album.items():
# If we're using track-level sources, also look up each
# track on the album.
if "track" in self.sources:
item.genre, src = self._get_genre(item)
item.store()
item_genre, src = self._get_genre(item)
self._log.info(
'genre for track "{0.title}" ({1}): {0.genre}',
'{}genre for track "{.title}" ({}): {}',
prefix,
item,
src,
item_genre,
)
if not pretend:
item.genre = item_genre
item.store()
if write:
if write and not pretend:
item.try_write()
else:
# Just query singletons, i.e. items that are not part of
# an album
for item in lib.items(args):
item.genre, src = self._get_genre(item)
item.store()
item_genre, src = self._get_genre(item)
prefix = "Pretend: " if pretend else ""
self._log.info(
"genre for track {0.title} ({1}): {0.genre}", item, src
'{}genre for track "{0.title}" ({1}): {}',
prefix,
item,
src,
item_genre,
)
if not pretend:
item.genre = item_genre
item.store()
if write and not pretend:
item.try_write()
lastgenre_cmd.func = lastgenre_func
return [lastgenre_cmd]

View file

@ -9,6 +9,9 @@ Unreleased
New features:
- :doc:`plugins/lastgenre`: Add a ``--pretend`` option to preview genre changes
without storing or writing them.
Bug fixes:
- :doc:`plugins/spotify` Fixed an issue where track matching and lookups could

View file

@ -124,7 +124,7 @@ tags** and will only **fetch new genres for empty tags**. When ``force`` is
``yes`` the setting of the ``whitelist`` option (as documented in Usage_)
applies to any existing or newly fetched genres.
The follwing configurations are possible:
The following configurations are possible:
**Setup 1** (default)
@ -213,5 +213,9 @@ fetch genres for albums or items matching a certain query.
By default, ``beet lastgenre`` matches albums. To match individual tracks or
singletons, use the ``-A`` switch: ``beet lastgenre -A [QUERY]``.
To preview the changes that would be made without applying them, use the ``-p``
or ``--pretend`` flag. This shows which genres would be set but does not write
or store any changes.
To disable automatic genre fetching on import, set the ``auto`` config option to
false.

View file

@ -14,7 +14,7 @@
"""Tests for the 'lastgenre' plugin."""
from unittest.mock import Mock
from unittest.mock import Mock, patch
import pytest
@ -131,6 +131,43 @@ class LastGenrePluginTest(BeetsTestCase):
"math rock",
]
def test_pretend_option_skips_library_updates(self):
item = self.create_item(
album="Pretend Album",
albumartist="Pretend Artist",
artist="Pretend Artist",
title="Pretend Track",
genre="Original Genre",
)
album = self.lib.add_album([item])
command = self.plugin.commands()[0]
opts, args = command.parser.parse_args(["--pretend"])
with patch.object(lastgenre.ui, "should_write", return_value=True):
with patch.object(
self.plugin,
"_get_genre",
return_value=("Mock Genre", "mock stage"),
) as mock_get_genre:
with patch.object(self.plugin._log, "info") as log_info:
# Mock try_write to verify it's never called in pretend mode
with patch.object(item, "try_write") as mock_try_write:
command.func(self.lib, opts, args)
mock_get_genre.assert_called_once()
assert any(
call.args[1] == "Pretend: " for call in log_info.call_args_list
)
# Verify that try_write was never called (file operations skipped)
mock_try_write.assert_not_called()
stored_album = self.lib.get_album(album.id)
assert stored_album.genre == "Original Genre"
assert stored_album.items()[0].genre == "Original Genre"
def test_no_duplicate(self):
"""Remove duplicated genres."""
self._setup_config(count=99)