Merge branch 'master' into discogs-disambiguation-fix

This commit is contained in:
henry 2025-09-21 09:34:12 -07:00 committed by GitHub
commit e577df0f25
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)