feat(plugin): Shelves
This pull request introduces the new "Shelves" plugin for MusicBrainz Picard, providing a comprehensive system for managing and organizing music files by virtual "shelves" (top-level folders). The implementation includes shelf detection, assignment, workflow automation, and integration with Picard’s UI and scripting system. The main changes are grouped by new feature implementation, plugin architecture, and supporting utilities/constants.
Major new features and architecture:
Initial implementation of the Shelves plugin:
- Added
plugins/shelves/__init__.pyto register the plugin with Picard, define plugin metadata, and set up all integration points (file/track processors, context menu actions, options page, and script functions). Introduced a global, thread-safeShelfManagerclass for tracking and resolving shelf assignments per album, including conflict detection and logging.
User interface and actions:
- Implemented
plugins/shelves/actions.pywith context menu actions: "Set shelf name..." (allows manual assignment via dialog with validation and known shelf suggestions) and "Determine shelf" (automatically detects shelf from file paths, updates metadata, and resolves conflicts). Includes UI dialog for shelf selection and validation feedback.
Constants and configuration:
- Added
plugins/shelves/constants.pyto centralize all plugin constants, such as tag keys, default shelf names, validation rules, config keys, and album indicators. This improves maintainability and ensures consistency across the plugin.
Shelf management logic:
- Added
plugins/shelves/manager.pydefining aShelfManagerclass for managing album-to-shelf mappings, voting logic when files suggest different shelves, and conflict logging. This supports robust and maintainable shelf assignment logic.
@phw I can't solve the problem of the failed test. When I run python -m unittest discover locally, it runs without any complaints.
I can't solve the problem of the failed test. When I run
python -m unittest discoverlocally, it runs without any complaints.
Judging from the unittesting output is seems that there may now be a syntax error in the classical_extras plugin:
test_generate_json (test.test_generate.GenerateTestCase.test_generate_json)
Generates the json data from all the plugins ... plugins/classical_extras/__init__.py:7509: SyntaxWarning: invalid escape sequence '\W'
clean_parent = re.sub("(?u)[\W]", ' ', parent)
plugins/classical_extras/__init__.py:7511: SyntaxWarning: invalid escape sequence '\W'
pattern_parent = clean_parent.replace(" ", "\W{0,2}")
plugins/classical_extras/__init__.py:7512: SyntaxWarning: invalid escape sequence '\s'
pattern_parent = "(^|.*?\s)(\W*" + pattern_parent + "\W?)(.*)"
plugins/classical_extras/__init__.py:7512: SyntaxWarning: invalid escape sequence '\W'
pattern_parent = "(^|.*?\s)(\W*" + pattern_parent + "\W?)(.*)"
ok
test_generate_zip (test.test_generate.GenerateTestCase.test_generate_zip)
Generates zip files for all folders and asserts ... ok
test_valid_json (test.test_generate.GenerateTestCase.test_valid_json)
Asserts that the json data contains all the fields ... plugins/classical_extras/__init__.py:7509: SyntaxWarning: invalid escape sequence '\W'
clean_parent = re.sub("(?u)[\W]", ' ', parent)
plugins/classical_extras/__init__.py:7511: SyntaxWarning: invalid escape sequence '\W'
pattern_parent = clean_parent.replace(" ", "\W{0,2}")
plugins/classical_extras/__init__.py:7512: SyntaxWarning: invalid escape sequence '\s'
pattern_parent = "(^|.*?\s)(\W*" + pattern_parent + "\W?)(.*)"
plugins/classical_extras/__init__.py:7512: SyntaxWarning: invalid escape sequence '\W'
pattern_parent = "(^|.*?\s)(\W*" + pattern_parent + "\W?)(.*)"
I would assume that this plugin (which hasn't changed for 3 years) used to pass these tests but (perhaps since we started testing with Python 3.12 & 3.13) it no longer passes. I will raise an issue on Classical Extras to get this fixed.
It is unfortunate that the tests with other versions of Python were cancelled as if they had all run to completion we would have a better idea of whether this is a version issue or not.