Merge branch 'master' into smartplaylist-attr-url-encoding

This commit is contained in:
J0J0 Todos 2025-02-17 21:16:09 +01:00 committed by GitHub
commit 2286511ebe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 63 additions and 70 deletions

View file

@ -675,7 +675,7 @@ class ImportSessionFixture(ImportSession):
>>> importer.run()
This imports ``/path/to/import`` into `lib`. It skips the first
album and imports thesecond one with metadata from the tags. For the
album and imports the second one with metadata from the tags. For the
remaining albums, the metadata from the autotagger will be applied.
"""

View file

@ -1130,7 +1130,10 @@ def get_temp_filename(
tempdir = get_module_tempdir(module)
tempdir.mkdir(parents=True, exist_ok=True)
_, filename = tempfile.mkstemp(dir=tempdir, prefix=prefix, suffix=suffix)
descriptor, filename = tempfile.mkstemp(
dir=tempdir, prefix=prefix, suffix=suffix
)
os.close(descriptor)
return bytestring_path(filename)

View file

@ -1,20 +0,0 @@
# This file is part of beets.
# Copyright 2016, Adrian Sampson.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
"""A namespace package for beets plugins."""
# Make this a namespace package.
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

View file

@ -22,6 +22,9 @@ New features:
Bug fixes:
* :doc:`plugins/fetchart`: Fix fetchart bug where a tempfile could not be deleted due to never being
properly closed.
:bug:`5521`
* :doc:`plugins/lyrics`: LRCLib will fallback to plain lyrics if synced lyrics
are not found and `synced` flag is set to `yes`.
* Synchronise files included in the source distribution with what we used to
@ -74,6 +77,8 @@ Bug fixes:
For packagers:
* The minimum supported Python version is now 3.9.
* External plugin developers: ``beetsplug/__init__.py`` file can be removed
from your plugin as beets now uses native/implicit namespace package setup.
Other changes:

View file

@ -3,47 +3,57 @@
Writing Plugins
---------------
A beets plugin is just a Python module inside the ``beetsplug`` namespace
package. (Check out this `Stack Overflow question about namespace packages`_ if
you haven't heard of them.) So, to make one, create a directory called
``beetsplug`` and put two files in it: one called ``__init__.py`` and one called
``myawesomeplugin.py`` (but don't actually call it that). Your directory
structure should look like this::
A beets plugin is just a Python module or package inside the ``beetsplug``
namespace package. (Check out `this article`_ and `this Stack Overflow
question`_ if you haven't heard about namespace packages.) So, to make one,
create a directory called ``beetsplug`` and add either your plugin module::
beetsplug/
__init__.py
myawesomeplugin.py
.. _Stack Overflow question about namespace packages:
https://stackoverflow.com/questions/1675734/how-do-i-create-a-namespace-package-in-python/1676069#1676069
or your plugin subpackage::
Then, you'll need to put this stuff in ``__init__.py`` to make ``beetsplug`` a
namespace package::
beetsplug/
myawesomeplugin/
__init__.py
myawesomeplugin.py
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
.. attention::
That's all for ``__init__.py``; you can can leave it alone. The meat of your
plugin goes in ``myawesomeplugin.py``. There, you'll have to import the
``beets.plugins`` module and define a subclass of the ``BeetsPlugin`` class
found therein. Here's a skeleton of a plugin file::
You do not anymore need to add a ``__init__.py`` file to the ``beetsplug``
directory. Python treats your plugin as a namespace package automatically,
thus we do not depend on ``pkgutil``-based setup in the ``__init__.py``
file anymore.
The meat of your plugin goes in ``myawesomeplugin.py``. There, you'll have to
import ``BeetsPlugin`` from ``beets.plugins`` and subclass it, for example
.. code-block:: python
from beets.plugins import BeetsPlugin
class MyPlugin(BeetsPlugin):
class MyAwesomePlugin(BeetsPlugin):
pass
Once you have your ``BeetsPlugin`` subclass, there's a variety of things your
plugin can do. (Read on!)
To use your new plugin, make sure the directory that contains your
``beetsplug`` directory is in the Python
path (using ``PYTHONPATH`` or by installing in a `virtualenv`_, for example).
Then, as described above, edit your ``config.yaml`` to include
``plugins: myawesomeplugin`` (substituting the name of the Python module
containing your plugin).
To use your new plugin, package your plugin (see how to do this with `poetry`_
or `setuptools`_, for example) and install it into your ``beets`` virtual
environment. Then, add your plugin to beets configuration
.. _virtualenv: https://pypi.org/project/virtualenv
.. code-block:: yaml
# config.yaml
plugins:
- myawesomeplugin
and you're good to go!
.. _this article: https://realpython.com/python-namespace-package/#setting-up-some-namespace-packages
.. _this Stack Overflow question: https://stackoverflow.com/a/27586272/9582674
.. _poetry: https://python-poetry.org/docs/pyproject/#packages
.. _setuptools: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#finding-simple-packages
.. _add_subcommands:
@ -249,13 +259,13 @@ The events currently available are:
during a ``beet import`` interactive session. Plugins can use this event for
:ref:`appending choices to the prompt <append_prompt_choices>` by returning a
list of ``PromptChoices``. Parameters: ``task`` and ``session``.
* `mb_track_extract`: called after the metadata is obtained from
MusicBrainz. The parameter is a ``dict`` containing the tags retrieved from
MusicBrainz for a track. Plugins must return a new (potentially empty)
``dict`` with additional ``field: value`` pairs, which the autotagger will
apply to the item, as flexible attributes if ``field`` is not a hardcoded
field. Fields already present on the track are overwritten.
field. Fields already present on the track are overwritten.
Parameter: ``data``
* `mb_album_extract`: Like `mb_track_extract`, but for album tags. Overwrites

View file

@ -174,9 +174,8 @@ pages.
…report a bug in beets?
-----------------------
We use the `issue tracker <https://github.com/beetbox/beets/issues>`__
on GitHub. `Enter a new issue <https://github.com/beetbox/beets/issues/new>`__
there to report a bug. Please follow these guidelines when reporting an issue:
We use the `issue tracker`_ on GitHub where you can `open a new ticket`_.
Please follow these guidelines when reporting an issue:
- Most importantly: if beets is crashing, please `include the
traceback <https://imgur.com/jacoj>`__. Tracebacks can be more
@ -206,6 +205,7 @@ If you've never reported a bug before, Mozilla has some well-written
`general guidelines for good bug
reports`_.
.. _issue tracker: https://github.com/beetbox/beets/issues
.. _general guidelines for good bug reports: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Bug_writing_guidelines
@ -343,11 +343,11 @@ read the file. You can also use specialized programs for checking file
integrity---for example, type ``metaflac --list music.flac`` to check
FLAC files.
If beets still complains about a file that seems to be valid, `file a
bug <https://github.com/beetbox/beets/issues/new>`__ and we'll look into
it. There's always a possibility that there's a bug "upstream" in the
`Mutagen <https://github.com/quodlibet/mutagen>`__ library used by beets,
in which case we'll forward the bug to that project's tracker.
If beets still complains about a file that seems to be valid, `open a new
ticket`_ and we'll look into it. There's always a possibility that there's
a bug "upstream" in the `Mutagen <https://github.com/quodlibet/mutagen>`__
library used by beets, in which case we'll forward the bug to that project's
tracker.
.. _importhang:
@ -398,3 +398,5 @@ try `this Super User answer`_.
.. _this Super User answer: https://superuser.com/a/284361/4569
.. _pip: https://pip.pypa.io/en/stable/
.. _open a new ticket:
https://github.com/beetbox/beets/issues/new?template=bug-report.md

View file

@ -76,7 +76,8 @@ all of these limitations.
Musepack, Windows Media, Opus, and AIFF files are supported. (Do you use
some other format? Please `file a feature request`_!)
.. _file a feature request: https://github.com/beetbox/beets/issues/new
.. _file a feature request:
https://github.com/beetbox/beets/issues/new?template=feature-request.md
Now that that's out of the way, let's tag some music.

View file

@ -37,3 +37,5 @@ allow_any_generics = false
# FIXME: Would be better to actually type the libraries (if under our control),
# or write our own stubs. For now, silence errors
ignore_missing_imports = true
namespace_packages = true
explicit_package_bases = true

View file

@ -456,16 +456,6 @@ lyrics_pages = [
LyricsPage.make(
"https://www.musica.com/letras.asp?letra=59862",
"""
Lady Madonna, children at your feet
Wonder how you manage to make ends meet
Who finds the money when you pay the rent?
Did you think that money was heaven sent?
Friday night arrives without a suitcase
Sunday morning creeping like a nun
Monday's child has learned to tie his bootlace
See how they run
Lady Madonna, baby at your breast
Wonders how you manage to feed the rest

View file

@ -57,7 +57,7 @@ class ImportAddedTest(PluginMixin, ImportTestCase):
os.path.getmtime(mfile.path) for mfile in self.import_media
)
self.matcher = AutotagStub().install()
self.matcher.macthin = AutotagStub.GOOD
self.matcher.matching = AutotagStub.IDENT
self.importer = self.setup_importer()
self.importer.add_choice(importer.action.APPLY)

View file

@ -440,7 +440,7 @@ class ImportTest(ImportTestCase):
self.prepare_album_for_import(1)
self.setup_importer()
self.matcher = AutotagStub().install()
self.matcher.macthin = AutotagStub.GOOD
self.matcher.matching = AutotagStub.IDENT
def tearDown(self):
super().tearDown()