Skip to content

lastgenre: Genre ignorelist#6449

Open
JOJ0 wants to merge 3 commits intomasterfrom
lastgenre_forbidden
Open

lastgenre: Genre ignorelist#6449
JOJ0 wants to merge 3 commits intomasterfrom
lastgenre_forbidden

Conversation

@JOJ0
Copy link
Member

@JOJ0 JOJ0 commented Mar 19, 2026

Description

Adds a global and artist-specific genre ignorelist to lastgenre. Ignorelist entries can use regex patterns or literal genre names and are configurable per artist or globally (*). Example:

fracture:
    ^(heavy|black|power|death)?\s?(metal|rock)$
*:
    electronic

This avoids incorrect genre assignments for artists with overlapping names (e.g., “Fracture” the DnB producer vs a Metal band)..

  • A mixture of regex and simple genre names is possible since regex patterns are precompiled and if that fails, escaped literal strings are added to the ignorelist.
  • Genre ignore filtering occurs at two stages: immediately after Last.fm fetching to prevent unwanted tags from entering the pipeline, and again during genre resolution to filter unwanted genres from existing file tags.

Why invent a file format?

YAML and INI formats were tested but proved cumbersome for regex patterns. A simple custom format is user-friendly and allows all regex patterns without special escaping or formatting.

Further notes

My personal roadmap for the lastgenre plugin: #5915

Feature idea originated from: #5721 (comment).

To Do

  • Documentation.
  • Changelog.
  • Tests.

- Test file format (valid and error cases)
- Test regex pattern matching (_is_ignored)
- Test _resolve_genres: ignored genres filtered
- Test _resolve_genres: c14n ancestry walk blocked for ignored tags
- Test _resolve_genres: without whitelist, oldest ancestor is kept
- Test _resolve_genres: whithout whitelist, ignored oldest ancestor is
  removed
@JOJ0 JOJ0 requested a review from a team as a code owner March 19, 2026 22:03
Copilot AI review requested due to automatic review settings March 19, 2026 22:03
@github-actions
Copy link

Thank you for the PR! The changelog has not been updated, so here is a friendly reminder to check if you need to add an entry.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

grug see PR add genre ignorelist to lastgenre plugin, so wrong Last.fm tags can be blocked global (*) or per artist. this fit plugin job: pick good genre tags and keep user control.

Changes:

  • add ignorelist file parsing + regex/literal compile, plus shared is_ignored() helper
  • apply ignorelist both right after Last.fm fetch and again during genre resolve/whitelist/c14n
  • add docs + tests for ignorelist matching and file format/errors

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
beetsplug/lastgenre/__init__.py load ignorelist from config file; thread artist through resolve path; filter ignored tags during c14n + final validation
beetsplug/lastgenre/client.py filter ignored tags immediately after Last.fm lookup (while keeping cache)
beetsplug/lastgenre/utils.py new shared is_ignored() helper + ignorelist typing
docs/plugins/lastgenre.rst document ignorelist feature + config option
test/plugins/test_lastgenre.py add ignorelist tests (resolve behavior, matcher behavior, file format + errors)
Comments suppressed due to low confidence (1)

test/plugins/test_lastgenre.py:293

  • grug see this config fixture just return upstream module-scoped config. it not reset state per test, so config['lastgenre']['ignorelist'] (and other keys) can leak between tests and even break LastGenrePlugin() init when prior test set ignorelist path then delete file. grug suggest make this fixture actually reset needed keys (ex: set lastgenre.ignorelist = False here) or use function-scoped fresh config object.
@pytest.fixture
def config(config):
    """Provide a fresh beets configuration for every test/parameterize call

    This is necessary to prevent the following parameterized test to bleed
    config test state in between test cases.
    """
    return config

Comment on lines 544 to 548
elif obj.albumartist != config["va_name"].as_str():
new_genres = self.client.fetch_artist_genre(obj.albumartist)
stage_label = "album artist"
stage_artist = obj.albumartist
if not new_genres:
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grug see multi-valued album artist fallback collect genres from many obj.albumartists, but stage_artist stay set to obj.albumartist. then later resolve step apply artist-specific ignorelist for wrong artist and can drop genre that came from other albumartist. grug think when stage_label become "multi-valued album artist", set stage_artist = None (global only) or handle per-artist filtering another way.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix that

Comment on lines +186 to +188
fracture:
^(heavy|black|power|death)?\s?(metal|rock)$|\w+-metal\d*$
progressive metal
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grug think regex example in code block wrong: it show double backslash (\\s, \\w, \\d). in ignorelist file user must write single backslash (\s, \w, \d) or regex will match literal "\s" text, not whitespace/word/digit. please change example to single backslashes so copy/paste work.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix that

JOJ0 added 2 commits March 19, 2026 23:16
- Prevents wrong last.fm genres based on a per artist (or global) list of regex
  patterns that should be ignored.
  - Genre _ignoring_ happens in two places: Right after fetching the
    last.fm genre and in _resolve_genres.
  - As a fallback literal string matching can be used instead of
    supplying a regex pattern
- Includes docs for the new feature.
@JOJ0 JOJ0 force-pushed the lastgenre_forbidden branch from 1400171 to c17eebb Compare March 19, 2026 22:16
@JOJ0
Copy link
Member Author

JOJ0 commented Mar 19, 2026

@snejus this is now ready for a first (human) review except the copilot comments I will address. Thanks in advance :-)

@codecov
Copy link

codecov bot commented Mar 19, 2026

Codecov Report

❌ Patch coverage is 83.33333% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.82%. Comparing base (1943b14) to head (c17eebb).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
beetsplug/lastgenre/__init__.py 84.81% 7 Missing and 5 partials ⚠️
beetsplug/lastgenre/client.py 37.50% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6449      +/-   ##
==========================================
+ Coverage   69.73%   69.82%   +0.09%     
==========================================
  Files         145      146       +1     
  Lines       18534    18615      +81     
  Branches     3023     3043      +20     
==========================================
+ Hits        12924    12998      +74     
- Misses       4976     4979       +3     
- Partials      634      638       +4     
Files with missing lines Coverage Δ
beetsplug/lastgenre/utils.py 100.00% <100.00%> (ø)
beetsplug/lastgenre/client.py 55.10% <37.50%> (+1.91%) ⬆️
beetsplug/lastgenre/__init__.py 78.15% <84.81%> (+2.61%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants