diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 975c884d6..c6ec4cb5f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -53,3 +53,7 @@ c490ac5810b70f3cf5fd8649669838e8fdb19f4d 1a045c91668c771686f4c871c84f1680af2e944b # Library restructure (split library.py into multiple modules) 0ad4e19d4f870db757373f44d12ff3be2441363a +# Docs: fix linting issues +769dcdc88a1263638ae25944ba6b2be3e8933666 +# Reformat all docs using docstrfmt +ab5acaabb3cd24c482adb7fa4800c89fd6a2f08d diff --git a/.github/problem-matchers/sphinx-build.json b/.github/problem-matchers/sphinx-build.json new file mode 100644 index 000000000..aff752ae9 --- /dev/null +++ b/.github/problem-matchers/sphinx-build.json @@ -0,0 +1,16 @@ +{ + "problemMatcher": [ + { + "owner": "sphinx-build", + "severity": "error", + "pattern": [ + { + "regexp": "^(/[^:]+):((\\d+):)?(\\sWARNING:)?\\s*(.+)$", + "file": 1, + "line": 3, + "message": 5 + } + ] + } + ] +} diff --git a/.github/problem-matchers/sphinx-lint.json b/.github/problem-matchers/sphinx-lint.json new file mode 100644 index 000000000..44e93e886 --- /dev/null +++ b/.github/problem-matchers/sphinx-lint.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "sphinx-lint", + "severity": "error", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):\\s+(.*)\\s\\(([a-z-]+)\\)$", + "file": 1, + "line": 2, + "message": 3, + "code": 4 + } + ] + } + ] +} diff --git a/.github/sphinx-problem-matcher.json b/.github/sphinx-problem-matcher.json deleted file mode 100644 index 0bfcf0ef4..000000000 --- a/.github/sphinx-problem-matcher.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "sphinx", - "pattern": [ - { - "regexp": "^([^:]+):(\\d+): (WARNING: )?(.+)$", - "file": 1, - "line": 2, - "message": 4 - } - ] - } - ] -} diff --git a/.github/workflows/changelog_reminder.yaml b/.github/workflows/changelog_reminder.yaml index da0f670a0..a9c26c1f5 100644 --- a/.github/workflows/changelog_reminder.yaml +++ b/.github/workflows/changelog_reminder.yaml @@ -1,6 +1,6 @@ name: Verify changelog updated -on: +on: pull_request_target: types: - opened @@ -14,20 +14,20 @@ jobs: - name: Get all updated Python files id: changed-python-files - uses: tj-actions/changed-files@v44 + uses: tj-actions/changed-files@v46 with: files: | **.py - name: Check for the changelog update id: changelog-update - uses: tj-actions/changed-files@v44 + uses: tj-actions/changed-files@v46 with: files: docs/changelog.rst - + - name: Comment under the PR with a reminder if: steps.changed-python-files.outputs.any_changed == 'true' && steps.changelog-update.outputs.any_changed == 'false' uses: thollander/actions-comment-pull-request@v2 with: - message: '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.' - GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + message: '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.' + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index baeb52f18..80826f468 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,6 +4,12 @@ on: push: branches: - master + +concurrency: + # Cancel previous workflow run when a new commit is pushed to a feature branch + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + env: PY_COLORS: 1 @@ -37,7 +43,7 @@ jobs: - name: Get changed lyrics files id: lyrics-update - uses: tj-actions/changed-files@v45 + uses: tj-actions/changed-files@v46 with: files: | beetsplug/lyrics.py @@ -52,7 +58,7 @@ jobs: - if: ${{ env.IS_MAIN_PYTHON != 'true' }} name: Test without coverage run: | - poetry install --extras=autobpm --extras=lyrics --extras=embedart + poetry install --without=lint --extras=autobpm --extras=lyrics --extras=replaygain --extras=reflink --extras=fetchart --extras=chroma --extras=sonosupdate poe test - if: ${{ env.IS_MAIN_PYTHON == 'true' }} @@ -60,10 +66,16 @@ jobs: env: LYRICS_UPDATED: ${{ steps.lyrics-update.outputs.any_changed }} run: | - poetry install --extras=autobpm --extras=lyrics --extras=docs --extras=replaygain --extras=reflink --extras=fetchart + poetry install --extras=autobpm --extras=lyrics --extras=docs --extras=replaygain --extras=reflink --extras=fetchart --extras=chroma --extras=sonosupdate poe docs poe test-with-coverage + - if: ${{ !cancelled() }} + name: Upload test results to Codecov + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + - if: ${{ env.IS_MAIN_PYTHON == 'true' }} name: Store the coverage report uses: actions/upload-artifact@v4 @@ -86,7 +98,7 @@ jobs: name: coverage-report - name: Upload code coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: files: ./coverage.xml use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7900d247d..0048a8f6e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,6 +6,11 @@ on: branches: - master +concurrency: + # Cancel previous workflow run when a new commit is pushed to a feature branch + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + env: PYTHON_VERSION: 3.9 @@ -22,13 +27,13 @@ jobs: - uses: actions/checkout@v4 - name: Get changed docs files id: changed-doc-files - uses: tj-actions/changed-files@v44 + uses: tj-actions/changed-files@v46 with: files: | docs/** - name: Get changed python files id: raw-changed-python-files - uses: tj-actions/changed-files@v44 + uses: tj-actions/changed-files@v46 with: files: | **.py @@ -107,7 +112,7 @@ jobs: uses: liskin/gh-problem-matcher-wrap@v3 with: linters: mypy - run: poe check-types --show-column-numbers --no-error-summary ${{ needs.changed-files.outputs.changed_python_files }} + run: poe check-types --show-column-numbers --no-error-summary . docs: if: needs.changed-files.outputs.any_docs_changed == 'true' @@ -126,11 +131,16 @@ jobs: - name: Install dependencies run: poetry install --extras=docs - - name: Add Sphinx problem matcher - run: echo "::add-matcher::.github/sphinx-problem-matcher.json" + - name: Add Sphinx problem matchers + run: | + echo "::add-matcher::.github/problem-matchers/sphinx-build.json" + echo "::add-matcher::.github/problem-matchers/sphinx-lint.json" + + - name: Check docs formatting + run: poe format-docs --check + + - name: Lint docs + run: poe lint-docs - name: Build docs - run: |- - poe docs |& tee /tmp/output - # fail the job if there are issues - grep -q " WARNING:" /tmp/output && exit 1 || exit 0 + run: poe docs -e 'SPHINXOPTS=--fail-on-warning --keep-going' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50ee3ec17..d33ff2955 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,8 +5,14 @@ repos: - repo: local hooks: - id: format - name: Format + name: Format Python files entry: poe format language: system files: '.*.py' pass_filenames: true + - id: format-docs + name: Format docs + entry: poe format-docs + language: system + files: '.*.rst' + pass_filenames: true diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst index 63c3eb537..e4f744bdd 100644 --- a/CODE_OF_CONDUCT.rst +++ b/CODE_OF_CONDUCT.rst @@ -1,9 +1,8 @@ -#################################### Contributor Covenant Code of Conduct -#################################### +==================================== Our Pledge -========== +---------- We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body @@ -16,7 +15,7 @@ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. Our Standards -============= +------------- Examples of behavior that contributes to a positive environment for our community include: @@ -41,7 +40,7 @@ Examples of unacceptable behavior include: professional setting Enforcement Responsibilities -============================ +---------------------------- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in @@ -54,7 +53,7 @@ not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. Scope -===== +----- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. @@ -63,7 +62,7 @@ posting via an official social media account, or acting as an appointed representative at an online or offline event. Enforcement -=========== +----------- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at here on Github. @@ -73,13 +72,13 @@ All community leaders are obligated to respect the privacy and security of the reporter of any incident. Enforcement Guidelines -====================== +---------------------- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 1. Correction -------------- +~~~~~~~~~~~~~ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. @@ -89,7 +88,7 @@ clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 2. Warning ----------- +~~~~~~~~~~ **Community Impact**: A violation through a single incident or series of actions. @@ -102,7 +101,7 @@ like social media. Violating these terms may lead to a temporary or permanent ban. 3. Temporary Ban ----------------- +~~~~~~~~~~~~~~~~ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. @@ -114,7 +113,7 @@ with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 4. Permanent Ban ----------------- +~~~~~~~~~~~~~~~~ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an @@ -124,7 +123,7 @@ individual, or aggression toward or disparagement of classes of individuals. community. Attribution -=========== +----------- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available `here @@ -136,4 +135,3 @@ enforcement ladder. For answers to common questions about this code of conduct, see the `FAQ `_. Translations are available at `translations `_. - diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2a6006b36..92375b465 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,111 +1,106 @@ -############ Contributing -############ +============ .. contents:: :depth: 3 Thank you! -========== +---------- -First off, thank you for considering contributing to beets! It’s people -like you that make beets continue to succeed. +First off, thank you for considering contributing to beets! It’s people like you +that make beets continue to succeed. -These guidelines describe how you can help most effectively. By -following these guidelines, you can make life easier for the development -team as it indicates you respect the maintainers’ time; in return, the -maintainers will reciprocate by helping to address your issue, review -changes, and finalize pull requests. +These guidelines describe how you can help most effectively. By following these +guidelines, you can make life easier for the development team as it indicates +you respect the maintainers’ time; in return, the maintainers will reciprocate +by helping to address your issue, review changes, and finalize pull requests. Types of Contributions -====================== +---------------------- -We love to get contributions from our community—you! There are many ways -to contribute, whether you’re a programmer or not. +We love to get contributions from our community—you! There are many ways to +contribute, whether you’re a programmer or not. The first thing to do, regardless of how you'd like to contribute to the project, is to check out our :doc:`Code of Conduct ` and to keep that in mind while interacting with other contributors and users. Non-Programming ---------------- +~~~~~~~~~~~~~~~ -- Promote beets! Help get the word out by telling your friends, writing - a blog post, or discussing it on a forum you frequent. -- Improve the `documentation`_. It’s - incredibly easy to contribute here: just find a page you want to - modify and hit the “Edit on GitHub” button in the upper-right. You - can automatically send us a pull request for your changes. -- GUI design. For the time being, beets is a command-line-only affair. - But that’s mostly because we don’t have any great ideas for what a - good GUI should look like. If you have those great ideas, please get - in touch. -- Benchmarks. We’d like to have a consistent way of measuring speed - improvements in beets’ tagger and other functionality as well as a - way of comparing beets’ performance to other tools. You can help by - compiling a library of freely-licensed music files (preferably with - incorrect metadata) for testing and measurement. -- Think you have a nice config or cool use-case for beets? We’d love to - hear about it! Submit a post to our `discussion board - `__ - under the “Show and Tell” category for a chance to get featured in `the - docs `__. -- Consider helping out fellow users by by `responding to support requests - `__ . +- Promote beets! Help get the word out by telling your friends, writing a blog + post, or discussing it on a forum you frequent. +- Improve the documentation_. It’s incredibly easy to contribute here: just find + a page you want to modify and hit the “Edit on GitHub” button in the + upper-right. You can automatically send us a pull request for your changes. +- GUI design. For the time being, beets is a command-line-only affair. But + that’s mostly because we don’t have any great ideas for what a good GUI should + look like. If you have those great ideas, please get in touch. +- Benchmarks. We’d like to have a consistent way of measuring speed improvements + in beets’ tagger and other functionality as well as a way of comparing beets’ + performance to other tools. You can help by compiling a library of + freely-licensed music files (preferably with incorrect metadata) for testing + and measurement. +- Think you have a nice config or cool use-case for beets? We’d love to hear + about it! Submit a post to our `discussion board + `__ + under the “Show and Tell” category for a chance to get featured in `the docs + `__. +- Consider helping out fellow users by by `responding to support requests + `__ . Programming ------------ +~~~~~~~~~~~ -- As a programmer (even if you’re just a beginner!), you have a ton of - opportunities to get your feet wet with beets. -- For developing plugins, or hacking away at beets, there’s some good - information in the `“For Developers” section of the - docs `__. +- As a programmer (even if you’re just a beginner!), you have a ton of + opportunities to get your feet wet with beets. +- For developing plugins, or hacking away at beets, there’s some good + information in the `“For Developers” section of the docs + `__. .. _development-tools: Development Tools -^^^^^^^^^^^^^^^^^ ++++++++++++++++++ In order to develop beets, you will need a few tools installed: -- `poetry`_ for packaging, virtual environment and dependency management -- `poethepoet`_ to run tasks, such as linting, formatting, testing +- poetry_ for packaging, virtual environment and dependency management +- poethepoet_ to run tasks, such as linting, formatting, testing -Python community recommends using `pipx`_ to install stand-alone command-line -applications such as above. `pipx`_ installs each application in an isolated +Python community recommends using pipx_ to install stand-alone command-line +applications such as above. pipx_ installs each application in an isolated virtual environment, where its dependencies will not interfere with your system and other CLI tools. -If you do not have `pipx`_ installed in your system, follow `pipx-installation-instructions`_ or +If you do not have pipx_ installed in your system, follow `pipx installation +instructions `__ or .. code-block:: sh $ python3 -m pip install --user pipx -Install `poetry`_ and `poethepoet`_ using `pipx`_:: +Install poetry_ and poethepoet_ using pipx_: + +:: $ pipx install poetry poethepoet -.. admonition:: Check ``tool.pipx-install`` section in ``pyproject.toml`` to - see supported versions +.. admonition:: Check ``tool.pipx-install`` section in ``pyproject.toml`` to see supported versions - :: + .. code-block:: toml - [tool.pipx-install] - poethepoet = ">=0.26" - poetry = "<2" - -.. _pipx: https://pipx.pypa.io/stable -.. _pipx-installation-instructions: https://pipx.pypa.io/stable/installation/ + [tool.pipx-install] + poethepoet = ">=0.26" + poetry = "<2" .. _getting-the-source: Getting the Source -^^^^^^^^^^^^^^^^^^ +++++++++++++++++++ The easiest way to get started with the latest beets source is to clone the -repository and install ``beets`` in a local virtual environment using `poetry`_. +repository and install ``beets`` in a local virtual environment using poetry_. This can be done with: .. code-block:: bash @@ -115,26 +110,32 @@ This can be done with: $ poetry install This will install ``beets`` and all development dependencies into its own -virtual environment in your ``$POETRY_CACHE_DIR``. See ``poetry install ---help`` for installation options, including installing ``extra`` dependencies -for plugins. +virtual environment in your ``$POETRY_CACHE_DIR``. See ``poetry install --help`` +for installation options, including installing ``extra`` dependencies for +plugins. In order to run something within this virtual environment, start the command with ``poetry run`` to them, for example ``poetry run pytest``. On the other hand, it may get tedious to type ``poetry run`` before every -command. Instead, you can activate the virtual environment in your shell with:: +command. Instead, you can activate the virtual environment in your shell with: + +:: $ poetry shell You should see ``(beets-py3.9)`` prefix in your shell prompt. Now you can run -commands directly, for example:: +commands directly, for example: + +:: $ (beets-py3.9) pytest -Additionally, `poethepoet`_ task runner assists us with the most common +Additionally, poethepoet_ task runner assists us with the most common operations. Formatting, linting, testing are defined as ``poe`` tasks in -`pyproject.toml`_. Run:: +pyproject.toml_. Run: + +:: $ poe @@ -149,204 +150,200 @@ to see all available tasks. They can be used like this, for example $ poe test --lf # re-run failing tests (note the additional pytest option) $ poe check-types --pretty # check types with an extra option for mypy - Code Contribution Ideas -^^^^^^^^^^^^^^^^^^^^^^^ ++++++++++++++++++++++++ -- We maintain a set of `issues marked as - “good first issue” `__. - These are issues that would serve as a good introduction to the - codebase. Claim one and start exploring! -- Like testing? Our `test - coverage `__ is somewhat - low. You can help out by finding low-coverage modules or checking out - other `testing-related - issues `__. -- There are several ways to improve the tests in general (see :ref:`testing` and some - places to think about performance optimization (see - `Optimization `__). -- Not all of our code is up to our coding conventions. In particular, - the `library API - documentation `__ - are currently quite sparse. You can help by adding to the docstrings - in the code and to the documentation pages themselves. beets follows - `PEP-257 `__ for - docstrings and in some places, we also sometimes use `ReST autodoc - syntax for - Sphinx `__ - to, for example, refer to a class name. - -.. _poethepoet: https://poethepoet.natn.io/index.html -.. _poetry: https://python-poetry.org/docs/ +- We maintain a set of `issues marked as “good first issue” + `__. These are + issues that would serve as a good introduction to the codebase. Claim one and + start exploring! +- Like testing? Our `test coverage `__ + is somewhat low. You can help out by finding low-coverage modules or checking + out other `testing-related issues + `__. +- There are several ways to improve the tests in general (see :ref:`testing` and + some places to think about performance optimization (see `Optimization + `__). +- Not all of our code is up to our coding conventions. In particular, the + `library API documentation + `__ are currently + quite sparse. You can help by adding to the docstrings in the code and to the + documentation pages themselves. beets follows `PEP-257 + `__ for docstrings and in some + places, we also sometimes use `ReST autodoc syntax for Sphinx + `__ to, + for example, refer to a class name. Your First Contribution -======================= +----------------------- -If this is your first time contributing to an open source project, -welcome! If you are confused at all about how to contribute or what to -contribute, take a look at `this great -tutorial `__, or stop by our -`discussion board `__ -if you have any questions. - -We maintain a list of issues we reserved for those new to open source -labeled `“first timers -only” `__. -Since the goal of these issues is to get users comfortable with -contributing to an open source project, please do not hesitate to ask +If this is your first time contributing to an open source project, welcome! If +you are confused at all about how to contribute or what to contribute, take a +look at `this great tutorial `__, or stop by our +`discussion board `__ if you have any questions. +We maintain a list of issues we reserved for those new to open source labeled +`first timers only`_. Since the goal of these issues is to get users comfortable +with contributing to an open source project, please do not hesitate to ask any +questions. + +.. _first timers only: https://github.com/beetbox/beets/issues?q=is%3Aopen+is%3Aissue+label%3A%22first+timers+only%22 + How to Submit Your Work -======================= +----------------------- -Do you have a great bug fix, new feature, or documentation expansion -you’d like to contribute? Follow these steps to create a GitHub pull -request and your code will ship in no time. +Do you have a great bug fix, new feature, or documentation expansion you’d like +to contribute? Follow these steps to create a GitHub pull request and your code +will ship in no time. -1. Fork the beets repository and clone it (see above) to create a - workspace. +1. Fork the beets repository and clone it (see above) to create a workspace. 2. Install pre-commit, following the instructions `here `_. 3. Make your changes. -4. Add tests. If you’ve fixed a bug, write a test to ensure that you’ve - actually fixed it. If there’s a new feature or plugin, please - contribute tests that show that your code does what it says. -5. Add documentation. If you’ve added a new command flag, for example, - find the appropriate page under ``docs/`` where it needs to be - listed. -6. Add a changelog entry to ``docs/changelog.rst`` near the top of the - document. +4. Add tests. If you’ve fixed a bug, write a test to ensure that you’ve actually + fixed it. If there’s a new feature or plugin, please contribute tests that + show that your code does what it says. +5. Add documentation. If you’ve added a new command flag, for example, find the + appropriate page under ``docs/`` where it needs to be listed. +6. Add a changelog entry to ``docs/changelog.rst`` near the top of the document. 7. Run the tests and style checker, see :ref:`testing`. 8. Push to your fork and open a pull request! We’ll be in touch shortly. -9. If you add commits to a pull request, please add a comment or - re-request a review after you push them since GitHub doesn’t - automatically notify us when commits are added. +9. If you add commits to a pull request, please add a comment or re-request a + review after you push them since GitHub doesn’t automatically notify us when + commits are added. Remember, code contributions have four parts: the code, the tests, the documentation, and the changelog entry. Thank you for contributing! The Code -======== +-------- -The documentation has a section on the -`library API `__ -that serves as an introduction to beets’ design. +The documentation has a section on the `library API +`__ that serves as an +introduction to beets’ design. Coding Conventions -================== +------------------ General -------- +~~~~~~~ + There are a few coding conventions we use in beets: -- Whenever you access the library database, do so through the provided - Library methods or via a Transaction object. Never call - ``lib.conn.*`` directly. For example, do this: +- Whenever you access the library database, do so through the provided Library + methods or via a Transaction object. Never call ``lib.conn.*`` directly. For + example, do this: - .. code-block:: python + .. code-block:: python - with g.lib.transaction() as tx: - rows = tx.query("SELECT DISTINCT '{0}' FROM '{1}' ORDER BY '{2}'" - .format(field, model._table, sort_field)) + with g.lib.transaction() as tx: + rows = tx.query( + "SELECT DISTINCT '{0}' FROM '{1}' ORDER BY '{2}'".format( + field, model._table, sort_field + ) + ) - To fetch Item objects from the database, use lib.items(…) and supply - a query as an argument. Resist the urge to write raw SQL for your - query. If you must use lower-level queries into the database, do - this: + To fetch Item objects from the database, use lib.items(…) and supply a query + as an argument. Resist the urge to write raw SQL for your query. If you must + use lower-level queries into the database, do this: - .. code-block:: python + .. code-block:: python - with lib.transaction() as tx: - rows = tx.query("SELECT …") + with lib.transaction() as tx: + rows = tx.query("SELECT …") - Transaction objects help control concurrent access to the database - and assist in debugging conflicting accesses. -- ``str.format()`` should be used instead of the ``%`` operator -- Never ``print`` informational messages; use the - `logging `__ module - instead. In particular, we have our own logging shim, so you’ll see - ``from beets import logging`` in most files. + Transaction objects help control concurrent access to the database and assist + in debugging conflicting accesses. - - The loggers use - `str.format `__-style - logging instead of ``%``-style, so you can type - ``log.debug("{0}", obj)`` to do your formatting. +- ``str.format()`` should be used instead of the ``%`` operator +- Never ``print`` informational messages; use the `logging + `__ module instead. In + particular, we have our own logging shim, so you’ll see ``from beets import + logging`` in most files. -- Exception handlers must use ``except A as B:`` instead of - ``except A, B:``. + - The loggers use `str.format + `__-style logging + instead of ``%``-style, so you can type ``log.debug("{0}", obj)`` to do your + formatting. + +- Exception handlers must use ``except A as B:`` instead of ``except A, B:``. Style ------ +~~~~~ -We use `ruff`_ to format and lint the codebase. +We use `ruff `__ to format and lint the codebase. Run ``poe check-format`` and ``poe lint`` to check your code for style and linting errors. Running ``poe format`` will automatically format your code according to the specifications required by the project. -.. _ruff: https://docs.astral.sh/ruff/ +Similarly, run ``poe format-docs`` and ``poe lint-docs`` to ensure consistent +documentation formatting and check for any issues. Handling Paths --------------- +~~~~~~~~~~~~~~ -A great deal of convention deals with the handling of **paths**. Paths -are stored internally—in the database, for instance—as byte strings -(i.e., ``bytes`` instead of ``str`` in Python 3). This is because POSIX -operating systems’ path names are only reliably usable as byte -strings—operating systems typically recommend but do not require that -filenames use a given encoding, so violations of any reported encoding -are inevitable. On Windows, the strings are always encoded with UTF-8; -on Unix, the encoding is controlled by the filesystem. Here are some -guidelines to follow: +A great deal of convention deals with the handling of **paths**. Paths are +stored internally—in the database, for instance—as byte strings (i.e., ``bytes`` +instead of ``str`` in Python 3). This is because POSIX operating systems’ path +names are only reliably usable as byte strings—operating systems typically +recommend but do not require that filenames use a given encoding, so violations +of any reported encoding are inevitable. On Windows, the strings are always +encoded with UTF-8; on Unix, the encoding is controlled by the filesystem. Here +are some guidelines to follow: -- If you have a Unicode path or you’re not sure whether something is - Unicode or not, pass it through ``bytestring_path`` function in the - ``beets.util`` module to convert it to bytes. -- Pass every path name through the ``syspath`` function (also in - ``beets.util``) before sending it to any *operating system* file - operation (``open``, for example). This is necessary to use long - filenames (which, maddeningly, must be Unicode) on Windows. This - allows us to consistently store bytes in the database but use the - native encoding rule on both POSIX and Windows. -- Similarly, the ``displayable_path`` utility function converts - bytestring paths to a Unicode string for displaying to the user. - Every time you want to print out a string to the terminal or log it - with the ``logging`` module, feed it through this function. +- If you have a Unicode path or you’re not sure whether something is Unicode or + not, pass it through ``bytestring_path`` function in the ``beets.util`` module + to convert it to bytes. +- Pass every path name through the ``syspath`` function (also in ``beets.util``) + before sending it to any *operating system* file operation (``open``, for + example). This is necessary to use long filenames (which, maddeningly, must be + Unicode) on Windows. This allows us to consistently store bytes in the + database but use the native encoding rule on both POSIX and Windows. +- Similarly, the ``displayable_path`` utility function converts bytestring paths + to a Unicode string for displaying to the user. Every time you want to print + out a string to the terminal or log it with the ``logging`` module, feed it + through this function. Editor Settings ---------------- +~~~~~~~~~~~~~~~ -Personally, I work on beets with `vim`_. Here are -some ``.vimrc`` lines that might help with PEP 8-compliant Python -coding:: +Personally, I work on beets with vim_. Here are some ``.vimrc`` lines that might +help with PEP 8-compliant Python coding: + +:: filetype indent on autocmd FileType python setlocal shiftwidth=4 tabstop=4 softtabstop=4 expandtab shiftround autoindent -Consider installing `this alternative Python indentation -plugin `__. I also -like `neomake `__ with its flake8 -checker. +Consider installing `this alternative Python indentation plugin +`__. I also like `neomake +`__ with its flake8 checker. .. _testing: Testing -======= +------- Running the Tests ------------------ +~~~~~~~~~~~~~~~~~ -Use ``poe`` to run tests:: +Use ``poe`` to run tests: + +:: $ poe test [pytest options] -You can disable a hand-selected set of "slow" tests by setting the -environment variable ``SKIP_SLOW_TESTS``, for example:: +You can disable a hand-selected set of "slow" tests by setting the environment +variable ``SKIP_SLOW_TESTS``, for example: + +:: $ SKIP_SLOW_TESTS=1 poe test Coverage -^^^^^^^^ +++++++++ The ``test`` command does not include coverage as it slows down testing. In order to measure it, use the ``test-with-coverage`` task @@ -359,56 +356,69 @@ You are welcome to explore coverage by opening the HTML report in Note that for each covered line the report shows **which tests cover it** (expand the list on the right-hand side of the affected line). -You can find project coverage status on `Codecov`_. +You can find project coverage status on Codecov_. Red Flags -^^^^^^^^^ ++++++++++ -The `pytest-random`_ plugin makes it easy to randomize the order of -tests. ``poe test --random`` will occasionally turn up failing tests -that reveal ordering dependencies—which are bad news! +The pytest-random_ plugin makes it easy to randomize the order of tests. ``poe +test --random`` will occasionally turn up failing tests that reveal ordering +dependencies—which are bad news! Test Dependencies -^^^^^^^^^^^^^^^^^ ++++++++++++++++++ The tests have a few more dependencies than beets itself. (The additional dependencies consist of testing utilities and dependencies of non-default plugins exercised by the test suite.) The dependencies are listed under the -``tool.poetry.group.test.dependencies`` section in `pyproject.toml`_. +``tool.poetry.group.test.dependencies`` section in pyproject.toml_. Writing Tests -------------- +~~~~~~~~~~~~~ -Writing tests is done by adding or modifying files in folder `test`_. -Take a look at -`https://github.com/beetbox/beets/blob/master/test/test_template.py#L224`_ -to get a basic view on how tests are written. Since we are currently migrating -the tests from `unittest`_ to `pytest`_, new tests should be written using -`pytest`_. Contributions migrating existing tests are welcome! +Writing tests is done by adding or modifying files in folder test_. Take a look +at `https://github.com/beetbox/beets/blob/master/test/test_template.py#L224`_ to +get a basic view on how tests are written. Since we are currently migrating the +tests from unittest_ to pytest_, new tests should be written using pytest_. +Contributions migrating existing tests are welcome! -External API requests under test should be mocked with `requests-mock`_, -However, we still want to know whether external APIs are up and that they -return expected responses, therefore we test them weekly with our `integration -test`_ suite. +External API requests under test should be mocked with requests-mock_, However, +we still want to know whether external APIs are up and that they return expected +responses, therefore we test them weekly with our `integration test`_ suite. In order to add such a test, mark your test with the ``integration_test`` marker .. code-block:: python - @pytest.mark.integration_test - def test_external_api_call(): - ... + @pytest.mark.integration_test + def test_external_api_call(): ... This way, the test will be run only in the integration test suite. -.. _Codecov: https://codecov.io/github/beetbox/beets -.. _pytest-random: https://github.com/klrmn/pytest-random -.. _pytest: https://docs.pytest.org/en/stable/ -.. _pyproject.toml: https://github.com/beetbox/beets/tree/master/pyproject.toml -.. _test: https://github.com/beetbox/beets/tree/master/test -.. _`https://github.com/beetbox/beets/blob/master/test/test_template.py#L224`: https://github.com/beetbox/beets/blob/master/test/test_template.py#L224 -.. _unittest: https://docs.python.org/3/library/unittest.html -.. _integration test: https://github.com/beetbox/beets/actions?query=workflow%3A%22integration+tests%22 -.. _requests-mock: https://requests-mock.readthedocs.io/en/latest/response.html +.. _codecov: https://codecov.io/github/beetbox/beets + .. _documentation: https://beets.readthedocs.io/en/stable/ + +.. _https://github.com/beetbox/beets/blob/master/test/test_template.py#l224: https://github.com/beetbox/beets/blob/master/test/test_template.py#L224 + +.. _integration test: https://github.com/beetbox/beets/actions?query=workflow%3A%22integration+tests%22 + +.. _pipx: https://pipx.pypa.io/stable + +.. _poethepoet: https://poethepoet.natn.io/index.html + +.. _poetry: https://python-poetry.org/docs/ + +.. _pyproject.toml: https://github.com/beetbox/beets/tree/master/pyproject.toml + +.. _pytest: https://docs.pytest.org/en/stable/ + +.. _pytest-random: https://github.com/klrmn/pytest-random + +.. _requests-mock: https://requests-mock.readthedocs.io/en/latest/response.html + +.. _test: https://github.com/beetbox/beets/tree/master/test + +.. _unittest: https://docs.python.org/3/library/unittest.html + .. _vim: https://www.vim.org/ diff --git a/README.rst b/README.rst index c916b65de..e8cec8ce9 100644 --- a/README.rst +++ b/README.rst @@ -10,115 +10,132 @@ .. image:: https://repology.org/badge/tiny-repos/beets.svg :target: https://repology.org/project/beets/versions - beets ===== Beets is the media library management system for obsessive music geeks. -The purpose of beets is to get your music collection right once and for all. -It catalogs your collection, automatically improving its metadata as it goes. -It then provides a bouquet of tools for manipulating and accessing your music. +The purpose of beets is to get your music collection right once and for all. It +catalogs your collection, automatically improving its metadata as it goes. It +then provides a bouquet of tools for manipulating and accessing your music. -Here's an example of beets' brainy tag corrector doing its thing:: +Here's an example of beets' brainy tag corrector doing its thing: - $ beet import ~/music/ladytron - Tagging: - Ladytron - Witching Hour - (Similarity: 98.4%) - * Last One Standing -> The Last One Standing - * Beauty -> Beauty*2 - * White Light Generation -> Whitelightgenerator - * All the Way -> All the Way... +:: + + $ beet import ~/music/ladytron + Tagging: + Ladytron - Witching Hour + (Similarity: 98.4%) + * Last One Standing -> The Last One Standing + * Beauty -> Beauty*2 + * White Light Generation -> Whitelightgenerator + * All the Way -> All the Way... Because beets is designed as a library, it can do almost anything you can -imagine for your music collection. Via `plugins`_, beets becomes a panacea: +imagine for your music collection. Via plugins_, beets becomes a panacea: - Fetch or calculate all the metadata you could possibly need: `album art`_, - `lyrics`_, `genres`_, `tempos`_, `ReplayGain`_ levels, or `acoustic - fingerprints`_. -- Get metadata from `MusicBrainz`_, `Discogs`_, and `Beatport`_. Or guess - metadata using songs' filenames or their acoustic fingerprints. + lyrics_, genres_, tempos_, ReplayGain_ levels, or `acoustic fingerprints`_. +- Get metadata from MusicBrainz_, Discogs_, and Beatport_. Or guess metadata + using songs' filenames or their acoustic fingerprints. - `Transcode audio`_ to any format you like. -- Check your library for `duplicate tracks and albums`_ or for `albums that - are missing tracks`_. +- Check your library for `duplicate tracks and albums`_ or for `albums that are + missing tracks`_. - Clean up crufty tags left behind by other, less-awesome tools. - Embed and extract album art from files' metadata. - Browse your music library graphically through a Web browser and play it in any browser that supports `HTML5 Audio`_. - Analyze music files' metadata from the command line. -- Listen to your library with a music player that speaks the `MPD`_ protocol - and works with a staggering variety of interfaces. +- Listen to your library with a music player that speaks the MPD_ protocol and + works with a staggering variety of interfaces. -If beets doesn't do what you want yet, `writing your own plugin`_ is -shockingly simple if you know a little Python. +If beets doesn't do what you want yet, `writing your own plugin`_ is shockingly +simple if you know a little Python. + +.. _acoustic fingerprints: https://beets.readthedocs.org/page/plugins/chroma.html + +.. _album art: https://beets.readthedocs.org/page/plugins/fetchart.html + +.. _albums that are missing tracks: https://beets.readthedocs.org/page/plugins/missing.html + +.. _beatport: https://www.beatport.com + +.. _discogs: https://www.discogs.com/ + +.. _duplicate tracks and albums: https://beets.readthedocs.org/page/plugins/duplicates.html + +.. _genres: https://beets.readthedocs.org/page/plugins/lastgenre.html + +.. _html5 audio: https://html.spec.whatwg.org/multipage/media.html#the-audio-element + +.. _lyrics: https://beets.readthedocs.org/page/plugins/lyrics.html + +.. _mpd: https://www.musicpd.org/ + +.. _musicbrainz: https://musicbrainz.org/ + +.. _musicbrainz music collection: https://musicbrainz.org/doc/Collections/ .. _plugins: https://beets.readthedocs.org/page/plugins/ -.. _MPD: https://www.musicpd.org/ -.. _MusicBrainz music collection: https://musicbrainz.org/doc/Collections/ -.. _writing your own plugin: - https://beets.readthedocs.org/page/dev/plugins.html -.. _HTML5 Audio: - https://html.spec.whatwg.org/multipage/media.html#the-audio-element -.. _albums that are missing tracks: - https://beets.readthedocs.org/page/plugins/missing.html -.. _duplicate tracks and albums: - https://beets.readthedocs.org/page/plugins/duplicates.html -.. _Transcode audio: - https://beets.readthedocs.org/page/plugins/convert.html -.. _Discogs: https://www.discogs.com/ -.. _acoustic fingerprints: - https://beets.readthedocs.org/page/plugins/chroma.html -.. _ReplayGain: https://beets.readthedocs.org/page/plugins/replaygain.html + +.. _replaygain: https://beets.readthedocs.org/page/plugins/replaygain.html + .. _tempos: https://beets.readthedocs.org/page/plugins/acousticbrainz.html -.. _genres: https://beets.readthedocs.org/page/plugins/lastgenre.html -.. _album art: https://beets.readthedocs.org/page/plugins/fetchart.html -.. _lyrics: https://beets.readthedocs.org/page/plugins/lyrics.html -.. _MusicBrainz: https://musicbrainz.org/ -.. _Beatport: https://www.beatport.com + +.. _transcode audio: https://beets.readthedocs.org/page/plugins/convert.html + +.. _writing your own plugin: https://beets.readthedocs.org/page/dev/plugins.html Install ------- -You can install beets by typing ``pip install beets`` or directly from Github (see details `here`_). -Beets has also been packaged in the `software repositories`_ of several -distributions. Check out the `Getting Started`_ guide for more information. +You can install beets by typing ``pip install beets`` or directly from Github +(see details here_). Beets has also been packaged in the `software +repositories`_ of several distributions. Check out the `Getting Started`_ guide +for more information. + +.. _getting started: https://beets.readthedocs.org/page/guides/main.html .. _here: https://beets.readthedocs.io/en/latest/faq.html#run-the-latest-source-version-of-beets -.. _Getting Started: https://beets.readthedocs.org/page/guides/main.html + .. _software repositories: https://repology.org/project/beets/versions Contribute ---------- -Thank you for considering contributing to ``beets``! Whether you're a -programmer or not, you should be able to find all the info you need at -`CONTRIBUTING.rst`_. +Thank you for considering contributing to ``beets``! Whether you're a programmer +or not, you should be able to find all the info you need at CONTRIBUTING.rst_. -.. _CONTRIBUTING.rst: https://github.com/beetbox/beets/blob/master/CONTRIBUTING.rst +.. _contributing.rst: https://github.com/beetbox/beets/blob/master/CONTRIBUTING.rst Read More --------- -Learn more about beets at `its Web site`_. Follow `@b33ts`_ on Mastodon for -news and updates. +Learn more about beets at `its Web site`_. Follow `@b33ts`_ on Mastodon for news +and updates. -.. _its Web site: https://beets.io/ .. _@b33ts: https://fosstodon.org/@beets +.. _its web site: https://beets.io/ + Contact ------- -* Encountered a bug you'd like to report? Check out our `issue tracker`_! - * If your issue hasn't already been reported, please `open a new ticket`_ - and we'll be in touch with you shortly. - * If you'd like to vote on a feature/bug, simply give a :+1: on issues - you'd like to see prioritized over others. -* Need help/support, would like to start a discussion, have an idea for a new - feature, or would just like to introduce yourself to the team? Check out - `GitHub Discussions`_! -.. _GitHub Discussions: https://github.com/beetbox/beets/discussions +- Encountered a bug you'd like to report? Check out our `issue tracker`_! + + - If your issue hasn't already been reported, please `open a new ticket`_ and + we'll be in touch with you shortly. + - If you'd like to vote on a feature/bug, simply give a :+1: on issues you'd + like to see prioritized over others. + - Need help/support, would like to start a discussion, have an idea for a new + feature, or would just like to introduce yourself to the team? Check out + `GitHub Discussions`_! + +.. _github discussions: https://github.com/beetbox/beets/discussions + .. _issue tracker: https://github.com/beetbox/beets/issues + .. _open a new ticket: https://github.com/beetbox/beets/issues/new/choose Authors @@ -126,4 +143,4 @@ Authors Beets is by `Adrian Sampson`_ with a supporting cast of thousands. -.. _Adrian Sampson: https://www.cs.cornell.edu/~asampson/ +.. _adrian sampson: https://www.cs.cornell.edu/~asampson/ diff --git a/README_kr.rst b/README_kr.rst index c12fc8b71..2233c379d 100644 --- a/README_kr.rst +++ b/README_kr.rst @@ -1,108 +1,119 @@ -.. image:: https://img.shields.io/pypi/v/beets.svg - :target: https://pypi.python.org/pypi/beets - -.. image:: https://img.shields.io/codecov/c/github/beetbox/beets.svg - :target: https://codecov.io/github/beetbox/beets - -.. image:: https://travis-ci.org/beetbox/beets.svg?branch=master - :target: https://travis-ci.org/beetbox/beets - - -beets -===== - -Beets는 강박적인 음악을 듣는 사람들을 위한 미디어 라이브러리 관리 시스템이다. - -Beets의 목적은 음악들을 한번에 다 받는 것이다. -음악들을 카탈로그화 하고, 자동으로 메타 데이터를 개선한다. -그리고 음악에 접근하고 조작할 수 있는 도구들을 제공한다. - -다음은 Beets의 brainy tag corrector가 한 일의 예시이다. - - $ beet import ~/music/ladytron - Tagging: - Ladytron - Witching Hour - (Similarity: 98.4%) - * Last One Standing -> The Last One Standing - * Beauty -> Beauty*2 - * White Light Generation -> Whitelightgenerator - * All the Way -> All the Way... - -Beets는 라이브러리로 디자인 되었기 때문에, 당신이 음악들에 대해 상상하는 모든 것을 할 수 있다. -`plugins`_ 을 통해서 모든 것을 할 수 있는 것이다! - -- 필요하는 메타 데이터를 계산하거나 패치 할 때: `album art`_, - `lyrics`_, `genres`_, `tempos`_, `ReplayGain`_ levels, or `acoustic - fingerprints`_. -- `MusicBrainz`_, `Discogs`_,`Beatport`_로부터 메타데이터를 가져오거나, - 노래 제목이나 음향 특징으로 메타데이터를 추측한다 -- `Transcode audio`_ 당신이 좋아하는 어떤 포맷으로든 변경한다. -- 당신의 라이브러리에서 `duplicate tracks and albums`_ 이나 `albums that are missing tracks`_ 를 검사한다. -- 남이 남기거나, 좋지 않은 도구로 남긴 잡다한 태그들을 지운다. -- 파일의 메타데이터에서 앨범 아트를 삽입이나 추출한다. -- 당신의 음악들을 `HTML5 Audio`_ 를 지원하는 어떤 브라우저든 재생할 수 있고, - 웹 브라우저에 표시 할 수 있다. -- 명령어로부터 음악 파일의 메타데이터를 분석할 수 있다. -- `MPD`_ 프로토콜을 사용하여 음악 플레이어로 음악을 들으면, 엄청나게 다양한 인터페이스로 작동한다. - -만약 Beets에 당신이 원하는게 아직 없다면, -당신이 python을 안다면 `writing your own plugin`_ _은 놀라울정도로 간단하다. - -.. _plugins: https://beets.readthedocs.org/page/plugins/ -.. _MPD: https://www.musicpd.org/ -.. _MusicBrainz music collection: https://musicbrainz.org/doc/Collections/ -.. _writing your own plugin: - https://beets.readthedocs.org/page/dev/plugins.html -.. _HTML5 Audio: - https://html.spec.whatwg.org/multipage/media.html#the-audio-element -.. _albums that are missing tracks: - https://beets.readthedocs.org/page/plugins/missing.html -.. _duplicate tracks and albums: - https://beets.readthedocs.org/page/plugins/duplicates.html -.. _Transcode audio: - https://beets.readthedocs.org/page/plugins/convert.html -.. _Discogs: https://www.discogs.com/ -.. _acoustic fingerprints: - https://beets.readthedocs.org/page/plugins/chroma.html -.. _ReplayGain: https://beets.readthedocs.org/page/plugins/replaygain.html -.. _tempos: https://beets.readthedocs.org/page/plugins/acousticbrainz.html -.. _genres: https://beets.readthedocs.org/page/plugins/lastgenre.html -.. _album art: https://beets.readthedocs.org/page/plugins/fetchart.html -.. _lyrics: https://beets.readthedocs.org/page/plugins/lyrics.html -.. _MusicBrainz: https://musicbrainz.org/ -.. _Beatport: https://www.beatport.com - -설치 -------- - -당신은 ``pip install beets`` 을 사용해서 Beets를 설치할 수 있다. -그리고 `Getting Started`_ 가이드를 확인할 수 있다. - -.. _Getting Started: https://beets.readthedocs.org/page/guides/main.html - -컨트리뷰션 ----------- - -어떻게 도우려는지 알고싶다면 `Hacking`_ 위키페이지를 확인하라. -당신은 docs 안에 `For Developers`_ 에도 관심이 있을수 있다. - -.. _Hacking: https://github.com/beetbox/beets/wiki/Hacking -.. _For Developers: https://beets.readthedocs.io/en/stable/dev/ - -Read More ---------- - -`its Web site`_ 에서 Beets에 대해 조금 더 알아볼 수 있다. -트위터에서 `@b33ts`_ 를 팔로우하면 새 소식을 볼 수 있다. - -.. _its Web site: https://beets.io/ -.. _@b33ts: https://twitter.com/b33ts/ - -저자들 -------- - -`Adrian Sampson`_ 와 많은 사람들의 지지를 받아 Beets를 만들었다. -돕고 싶다면 `forum`_.를 방문하면 된다. - -.. _forum: https://github.com/beetbox/beets/discussions/ -.. _Adrian Sampson: https://www.cs.cornell.edu/~asampson/ +.. image:: https://img.shields.io/pypi/v/beets.svg + :target: https://pypi.python.org/pypi/beets + +.. image:: https://img.shields.io/codecov/c/github/beetbox/beets.svg + :target: https://codecov.io/github/beetbox/beets + +.. image:: https://travis-ci.org/beetbox/beets.svg?branch=master + :target: https://travis-ci.org/beetbox/beets + +beets +===== + +Beets는 강박적인 음악을 듣는 사람들을 위한 미디어 라이브러리 관리 시스템이다. + +Beets의 목적은 음악들을 한번에 다 받는 것이다. 음악들을 카탈로그화 하고, 자동으로 메타 데이터를 개선한다. 그리고 음악에 접근하고 조작할 +수 있는 도구들을 제공한다. + +다음은 Beets의 brainy tag corrector가 한 일의 예시이다. + +:: + + $ beet import ~/music/ladytron + Tagging: + Ladytron - Witching Hour + (Similarity: 98.4%) + * Last One Standing -> The Last One Standing + * Beauty -> Beauty*2 + * White Light Generation -> Whitelightgenerator + * All the Way -> All the Way... + +Beets는 라이브러리로 디자인 되었기 때문에, 당신이 음악들에 대해 상상하는 모든 것을 할 수 있다. plugins_ 을 통해서 모든 것을 할 +수 있는 것이다! + +- 필요하는 메타 데이터를 계산하거나 패치 할 때: `album art`_, lyrics_, genres_, tempos_, + ReplayGain_ levels, or `acoustic fingerprints`_. +- MusicBrainz_, Discogs_,`Beatport`_로부터 메타데이터를 가져오거나, 노래 제목이나 음향 특징으로 메타데이터를 + 추측한다 +- `Transcode audio`_ 당신이 좋아하는 어떤 포맷으로든 변경한다. +- 당신의 라이브러리에서 `duplicate tracks and albums`_ 이나 `albums that are missing + tracks`_ 를 검사한다. +- 남이 남기거나, 좋지 않은 도구로 남긴 잡다한 태그들을 지운다. +- 파일의 메타데이터에서 앨범 아트를 삽입이나 추출한다. +- 당신의 음악들을 `HTML5 Audio`_ 를 지원하는 어떤 브라우저든 재생할 수 있고, 웹 브라우저에 표시 할 수 있다. +- 명령어로부터 음악 파일의 메타데이터를 분석할 수 있다. +- MPD_ 프로토콜을 사용하여 음악 플레이어로 음악을 들으면, 엄청나게 다양한 인터페이스로 작동한다. + +만약 Beets에 당신이 원하는게 아직 없다면, 당신이 python을 안다면 `writing your own plugin`_ _은 놀라울정도로 +간단하다. + +.. _acoustic fingerprints: https://beets.readthedocs.org/page/plugins/chroma.html + +.. _album art: https://beets.readthedocs.org/page/plugins/fetchart.html + +.. _albums that are missing tracks: https://beets.readthedocs.org/page/plugins/missing.html + +.. _beatport: https://www.beatport.com + +.. _discogs: https://www.discogs.com/ + +.. _duplicate tracks and albums: https://beets.readthedocs.org/page/plugins/duplicates.html + +.. _genres: https://beets.readthedocs.org/page/plugins/lastgenre.html + +.. _html5 audio: https://html.spec.whatwg.org/multipage/media.html#the-audio-element + +.. _lyrics: https://beets.readthedocs.org/page/plugins/lyrics.html + +.. _mpd: https://www.musicpd.org/ + +.. _musicbrainz: https://musicbrainz.org/ + +.. _musicbrainz music collection: https://musicbrainz.org/doc/Collections/ + +.. _plugins: https://beets.readthedocs.org/page/plugins/ + +.. _replaygain: https://beets.readthedocs.org/page/plugins/replaygain.html + +.. _tempos: https://beets.readthedocs.org/page/plugins/acousticbrainz.html + +.. _transcode audio: https://beets.readthedocs.org/page/plugins/convert.html + +.. _writing your own plugin: https://beets.readthedocs.org/page/dev/plugins.html + +설치 +------- + +당신은 ``pip install beets`` 을 사용해서 Beets를 설치할 수 있다. 그리고 `Getting Started`_ 가이드를 +확인할 수 있다. + +.. _getting started: https://beets.readthedocs.org/page/guides/main.html + +컨트리뷰션 +---------- + +어떻게 도우려는지 알고싶다면 Hacking_ 위키페이지를 확인하라. 당신은 docs 안에 `For Developers`_ 에도 관심이 있을수 +있다. + +.. _for developers: https://beets.readthedocs.io/en/stable/dev/ + +.. _hacking: https://github.com/beetbox/beets/wiki/Hacking + +Read More +--------- + +`its Web site`_ 에서 Beets에 대해 조금 더 알아볼 수 있다. 트위터에서 `@b33ts`_ 를 팔로우하면 새 소식을 볼 수 +있다. + +.. _@b33ts: https://twitter.com/b33ts/ + +.. _its web site: https://beets.io/ + +저자들 +------- + +`Adrian Sampson`_ 와 많은 사람들의 지지를 받아 Beets를 만들었다. 돕고 싶다면 forum_.를 방문하면 된다. + +.. _adrian sampson: https://www.cs.cornell.edu/~asampson/ + +.. _forum: https://github.com/beetbox/beets/discussions/ diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 8cfe534ab..4d107b3a1 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -16,15 +16,16 @@ from __future__ import annotations +import warnings +from importlib import import_module from typing import TYPE_CHECKING, Union from beets import config, logging -from beets.util import get_most_common_tags as current_metadata # Parts of external interface. from beets.util import unique_list -from .distance import Distance +from ..util import deprecate_imports from .hooks import AlbumInfo, AlbumMatch, TrackInfo, TrackMatch from .match import Proposal, Recommendation, tag_album, tag_item @@ -33,10 +34,27 @@ if TYPE_CHECKING: from beets.library import Album, Item, LibModel + +def __getattr__(name: str): + if name == "current_metadata": + warnings.warn( + ( + f"'beets.autotag.{name}' is deprecated and will be removed in" + " 3.0.0. Use 'beets.util.get_most_common_tags' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return import_module("beets.util").get_most_common_tags + + return deprecate_imports( + __name__, {"Distance": "beets.autotag.distance"}, name, "3.0.0" + ) + + __all__ = [ "AlbumInfo", "AlbumMatch", - "Distance", # for backwards compatibility "Proposal", "Recommendation", "TrackInfo", @@ -44,7 +62,6 @@ __all__ = [ "apply_album_metadata", "apply_item_metadata", "apply_metadata", - "current_metadata", # for backwards compatibility "tag_album", "tag_item", ] diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index a569feba7..81c1be4b9 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -26,8 +26,9 @@ from abc import ABC from collections import defaultdict from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence from sqlite3 import Connection -from typing import TYPE_CHECKING, Any, AnyStr, Callable, Generic, TypeVar +from typing import TYPE_CHECKING, Any, AnyStr, Callable, Generic +from typing_extensions import TypeVar # default value support from unidecode import unidecode import beets @@ -49,10 +50,7 @@ if TYPE_CHECKING: from .query import SQLiteType - D = TypeVar("D", bound="Database", default=Any) -else: - D = TypeVar("D", bound="Database") - +D = TypeVar("D", bound="Database", default=Any) FlexAttrs = dict[str, str] diff --git a/beets/event_types.py b/beets/event_types.py deleted file mode 100644 index d5fc01eec..000000000 --- a/beets/event_types.py +++ /dev/null @@ -1,33 +0,0 @@ -from typing import Literal - -EventType = Literal[ - "pluginload", - "import", - "album_imported", - "album_removed", - "item_copied", - "item_imported", - "before_item_moved", - "item_moved", - "item_linked", - "item_hardlinked", - "item_reflinked", - "item_removed", - "write", - "after_write", - "import_task_created", - "import_task_start", - "import_task_apply", - "import_task_before_choice", - "import_task_choice", - "import_task_files", - "library_opened", - "database_change", - "cli_exit", - "import_begin", - "trackinfo_received", - "albuminfo_received", - "before_choose_candidate", - "mb_track_extract", - "mb_album_extract", -] diff --git a/beets/library/__init__.py b/beets/library/__init__.py index 286b84189..b38381438 100644 --- a/beets/library/__init__.py +++ b/beets/library/__init__.py @@ -1,8 +1,21 @@ +from beets.util import deprecate_imports + from .exceptions import FileOperationError, ReadError, WriteError from .library import Library from .models import Album, Item, LibModel from .queries import parse_query_parts, parse_query_string +NEW_MODULE_BY_NAME = dict.fromkeys( + ("DateType", "DurationType", "MusicalKey", "PathType"), "beets.dbcore.types" +) | dict.fromkeys( + ("BLOB_TYPE", "SingletonQuery", "PathQuery"), "beets.dbcore.query" +) + + +def __getattr__(name: str): + return deprecate_imports(__name__, NEW_MODULE_BY_NAME, name, "3.0.0") + + __all__ = [ "Library", "LibModel", diff --git a/beets/metadata_plugins.py b/beets/metadata_plugins.py index 5b11dc4ec..9d69633d6 100644 --- a/beets/metadata_plugins.py +++ b/beets/metadata_plugins.py @@ -10,20 +10,16 @@ from __future__ import annotations import abc import inspect import re -import sys import warnings from typing import TYPE_CHECKING, Generic, Literal, Sequence, TypedDict, TypeVar +from typing_extensions import NotRequired + from beets.util import cached_classproperty from beets.util.id_extractors import extract_release_id from .plugins import BeetsPlugin, find_plugins, notify_info_yielded, send -if sys.version_info >= (3, 11): - from typing import NotRequired -else: - from typing_extensions import NotRequired - if TYPE_CHECKING: from collections.abc import Iterable diff --git a/beets/plugins.py b/beets/plugins.py index 81f423431..c5c5b2c53 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -20,28 +20,21 @@ import abc import inspect import re import sys -import traceback from collections import defaultdict from functools import wraps +from pathlib import Path from types import GenericAlias -from typing import TYPE_CHECKING, Any, Callable, Sequence, TypeVar +from typing import TYPE_CHECKING, Any, ClassVar, Literal, TypeVar import mediafile +from typing_extensions import ParamSpec import beets from beets import logging +from beets.util import unique_list if TYPE_CHECKING: - from beets.event_types import EventType - -if sys.version_info >= (3, 10): - from typing import ParamSpec -else: - from typing_extensions import ParamSpec - - -if TYPE_CHECKING: - from collections.abc import Iterable + from collections.abc import Callable, Iterable, Sequence from confuse import ConfigView @@ -63,7 +56,7 @@ if TYPE_CHECKING: P = ParamSpec("P") Ret = TypeVar("Ret", bound=Any) - Listener = Callable[..., None] + Listener = Callable[..., Any] IterF = Callable[P, Iterable[Ret]] @@ -72,6 +65,37 @@ PLUGIN_NAMESPACE = "beetsplug" # Plugins using the Last.fm API can share the same API key. LASTFM_KEY = "2dc3914abf35f0d9c92d97d8f8e42b43" +EventType = Literal[ + "after_write", + "album_imported", + "album_removed", + "albuminfo_received", + "before_choose_candidate", + "before_item_moved", + "cli_exit", + "database_change", + "import", + "import_begin", + "import_task_apply", + "import_task_before_choice", + "import_task_choice", + "import_task_created", + "import_task_files", + "import_task_start", + "item_copied", + "item_hardlinked", + "item_imported", + "item_linked", + "item_moved", + "item_reflinked", + "item_removed", + "library_opened", + "mb_album_extract", + "mb_track_extract", + "pluginload", + "trackinfo_received", + "write", +] # Global logger. log = logging.getLogger("beets") @@ -84,6 +108,17 @@ class PluginConflictError(Exception): """ +class PluginImportError(ImportError): + """Indicates that a plugin could not be imported. + + This is a subclass of ImportError so that it can be caught separately + from other errors. + """ + + def __init__(self, name: str): + super().__init__(f"Could not import plugin {name}") + + class PluginLogFilter(logging.Filter): """A logging filter that identifies the plugin that emitted a log message. @@ -110,6 +145,14 @@ class BeetsPlugin(metaclass=abc.ABCMeta): the abstract methods defined here. """ + _raw_listeners: ClassVar[dict[EventType, list[Listener]]] = defaultdict( + list + ) + listeners: ClassVar[dict[EventType, list[Listener]]] = defaultdict(list) + template_funcs: TFuncMap[str] | None = None + template_fields: TFuncMap[Item] | None = None + album_template_fields: TFuncMap[Album] | None = None + name: str config: ConfigView early_import_stages: list[ImportStageFunc] @@ -223,25 +266,13 @@ class BeetsPlugin(metaclass=abc.ABCMeta): mediafile.MediaFile.add_field(name, descriptor) library.Item._media_fields.add(name) - _raw_listeners: dict[str, list[Listener]] | None = None - listeners: dict[str, list[Listener]] | None = None - - def register_listener(self, event: "EventType", func: Listener): + def register_listener(self, event: EventType, func: Listener) -> None: """Add a function as a listener for the specified event.""" - wrapped_func = self._set_log_level_and_params(logging.WARNING, func) - - cls = self.__class__ - - if cls.listeners is None or cls._raw_listeners is None: - cls._raw_listeners = defaultdict(list) - cls.listeners = defaultdict(list) - if func not in cls._raw_listeners[event]: - cls._raw_listeners[event].append(func) - cls.listeners[event].append(wrapped_func) - - template_funcs: TFuncMap[str] | None = None - template_fields: TFuncMap[Item] | None = None - album_template_fields: TFuncMap[Album] | None = None + if func not in self._raw_listeners[event]: + self._raw_listeners[event].append(func) + self.listeners[event].append( + self._set_log_level_and_params(logging.WARNING, func) + ) @classmethod def template_func(cls, name: str) -> Callable[[TFunc[str]], TFunc[str]]: @@ -275,69 +306,92 @@ class BeetsPlugin(metaclass=abc.ABCMeta): return helper -_classes: set[type[BeetsPlugin]] = set() +def get_plugin_names() -> list[str]: + """Discover and return the set of plugin names to be loaded. - -def load_plugins(names: Sequence[str] = ()) -> None: - """Imports the modules for a sequence of plugin names. Each name - must be the name of a Python module under the "beetsplug" namespace - package in sys.path; the module indicated should contain the - BeetsPlugin subclasses desired. + Configures the plugin search paths and resolves the final set of plugins + based on configuration settings, inclusion filters, and exclusion rules. + Automatically includes the musicbrainz plugin when enabled in configuration. """ - for name in names: - modname = f"{PLUGIN_NAMESPACE}.{name}" + paths = [ + str(Path(p).expanduser().absolute()) + for p in beets.config["pluginpath"].as_str_seq(split=False) + ] + log.debug("plugin paths: {}", paths) + + # Extend the `beetsplug` package to include the plugin paths. + import beetsplug + + beetsplug.__path__ = paths + list(beetsplug.__path__) + + # For backwards compatibility, also support plugin paths that + # *contain* a `beetsplug` package. + sys.path += paths + plugins = unique_list(beets.config["plugins"].as_str_seq()) + # TODO: Remove in v3.0.0 + if ( + "musicbrainz" not in plugins + and "musicbrainz" in beets.config + and beets.config["musicbrainz"].get().get("enabled") + ): + plugins.append("musicbrainz") + + beets.config.add({"disabled_plugins": []}) + disabled_plugins = set(beets.config["disabled_plugins"].as_str_seq()) + return [p for p in plugins if p not in disabled_plugins] + + +def _get_plugin(name: str) -> BeetsPlugin | None: + """Dynamically load and instantiate a plugin class by name. + + Attempts to import the plugin module, locate the appropriate plugin class + within it, and return an instance. Handles import failures gracefully and + logs warnings for missing plugins or loading errors. + """ + try: try: - try: - namespace = __import__(modname, None, None) - except ImportError as exc: - # Again, this is hacky: - if exc.args[0].endswith(" " + name): - log.warning("** plugin {0} not found", name) - else: - raise - else: - for obj in getattr(namespace, name).__dict__.values(): - if ( - inspect.isclass(obj) - and not isinstance( - obj, GenericAlias - ) # seems to be needed for python <= 3.9 only - and issubclass(obj, BeetsPlugin) - and obj != BeetsPlugin - and not inspect.isabstract(obj) - and obj not in _classes - ): - _classes.add(obj) + namespace = __import__(f"{PLUGIN_NAMESPACE}.{name}", None, None) + except Exception as exc: + raise PluginImportError(name) from exc - except Exception: - log.warning( - "** error loading plugin {}:\n{}", - name, - traceback.format_exc(), - ) + for obj in getattr(namespace, name).__dict__.values(): + if ( + inspect.isclass(obj) + and not isinstance( + obj, GenericAlias + ) # seems to be needed for python <= 3.9 only + and issubclass(obj, BeetsPlugin) + and obj != BeetsPlugin + and not inspect.isabstract(obj) + ): + return obj() + + except Exception: + log.warning("** error loading plugin {}", name, exc_info=True) + + return None -_instances: dict[type[BeetsPlugin], BeetsPlugin] = {} +_instances: list[BeetsPlugin] = [] -def find_plugins() -> list[BeetsPlugin]: - """Returns a list of BeetsPlugin subclass instances from all - currently loaded beets plugins. Loads the default plugin set - first. +def load_plugins() -> None: + """Initialize the plugin system by loading all configured plugins. + + Performs one-time plugin discovery and instantiation, storing loaded plugin + instances globally. Emits a pluginload event after successful initialization + to notify other components. """ - if _instances: - # After the first call, use cached instances for performance reasons. - # See https://github.com/beetbox/beets/pull/3810 - return list(_instances.values()) + if not _instances: + names = get_plugin_names() + log.info("Loading plugins: {}", ", ".join(sorted(names))) + _instances.extend(filter(None, map(_get_plugin, names))) - load_plugins() - plugins = [] - for cls in _classes: - # Only instantiate each plugin class once. - if cls not in _instances: - _instances[cls] = cls() - plugins.append(_instances[cls]) - return plugins + send("pluginload") + + +def find_plugins() -> Iterable[BeetsPlugin]: + return _instances # Communication with plugins. @@ -388,7 +442,9 @@ def named_queries(model_cls: type[AnyModel]) -> dict[str, FieldQueryType]: } -def notify_info_yielded(event: str) -> Callable[[IterF[P, Ret]], IterF[P, Ret]]: +def notify_info_yielded( + event: EventType, +) -> Callable[[IterF[P, Ret]], IterF[P, Ret]]: """Makes a generator send the event 'event' every time it yields. This decorator is supposed to decorate a generator, but any function returning an iterable should work. @@ -479,19 +535,7 @@ def album_field_getters() -> TFuncMap[Album]: # Event dispatch. -def event_handlers() -> dict[str, list[Listener]]: - """Find all event handlers from plugins as a dictionary mapping - event names to sequences of callables. - """ - all_handlers: dict[str, list[Listener]] = defaultdict(list) - for plugin in find_plugins(): - if plugin.listeners: - for event, handlers in plugin.listeners.items(): - all_handlers[event] += handlers - return all_handlers - - -def send(event: str, **arguments: Any) -> list[Any]: +def send(event: EventType, **arguments: Any) -> list[Any]: """Send an event to all assigned event listeners. `event` is the name of the event to send, all other named arguments @@ -500,12 +544,11 @@ def send(event: str, **arguments: Any) -> list[Any]: Return a list of non-None values returned from the handlers. """ log.debug("Sending event: {0}", event) - results: list[Any] = [] - for handler in event_handlers()[event]: - result = handler(**arguments) - if result is not None: - results.append(result) - return results + return [ + r + for handler in BeetsPlugin.listeners[event] + if (r := handler(**arguments)) is not None + ] def feat_tokens(for_artist: bool = True) -> str: diff --git a/beets/py.typed b/beets/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/beets/test/helper.py b/beets/test/helper.py index eb024a7aa..f1633c110 100644 --- a/beets/test/helper.py +++ b/beets/test/helper.py @@ -481,6 +481,11 @@ class PluginMixin(ConfigMixin): super().teardown_beets() self.unload_plugins() + def register_plugin( + self, plugin_class: type[beets.plugins.BeetsPlugin] + ) -> None: + beets.plugins._instances.append(plugin_class()) + def load_plugins(self, *plugins: str) -> None: """Load and initialize plugins by names. @@ -491,18 +496,15 @@ class PluginMixin(ConfigMixin): plugins = (self.plugin,) if hasattr(self, "plugin") else plugins self.config["plugins"] = plugins cached_classproperty.cache.clear() - beets.plugins.load_plugins(plugins) - beets.plugins.send("pluginload") - beets.plugins.find_plugins() + beets.plugins.load_plugins() def unload_plugins(self) -> None: """Unload all plugins and remove them from the configuration.""" # FIXME this should eventually be handled by a plugin manager - for plugin_class in beets.plugins._instances: - plugin_class.listeners = None + beets.plugins.BeetsPlugin.listeners.clear() + beets.plugins.BeetsPlugin._raw_listeners.clear() self.config["plugins"] = [] - beets.plugins._classes = set() - beets.plugins._instances = {} + beets.plugins._instances.clear() @contextmanager def configure_plugin(self, config: Any): diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index 8b2419a07..01030a977 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -28,8 +28,9 @@ import struct import sys import textwrap import traceback +import warnings from difflib import SequenceMatcher -from typing import TYPE_CHECKING, Any, Callable +from typing import Any, Callable import confuse @@ -39,9 +40,6 @@ from beets.dbcore import query as db_query from beets.util import as_string from beets.util.functemplate import template -if TYPE_CHECKING: - from types import ModuleType - # On Windows platforms, use colorama to support "ANSI" terminal colors. if sys.platform == "win32": try: @@ -104,6 +102,21 @@ def _stream_encoding(stream, default="utf-8"): return stream.encoding or default +def decargs(arglist): + """Given a list of command-line argument bytestrings, attempts to + decode them to Unicode strings when running under Python 2. + + .. deprecated:: 2.4.0 + This function will be removed in 3.0.0. + """ + warnings.warn( + "decargs() is deprecated and will be removed in version 3.0.0.", + DeprecationWarning, + stacklevel=2, + ) + return arglist + + def print_(*strings: str, end: str = "\n") -> None: """Like print, but rather than raising an error when a character is not in the terminal's encoding's character set, just silently @@ -1557,59 +1570,16 @@ optparse.Option.ALWAYS_TYPED_ACTIONS += ("callback",) # The main entry point and bootstrapping. -def _load_plugins( - options: optparse.Values, config: confuse.LazyConfig -) -> ModuleType: - """Load the plugins specified on the command line or in the configuration.""" - paths = config["pluginpath"].as_str_seq(split=False) - paths = [util.normpath(p) for p in paths] - log.debug("plugin paths: {0}", util.displayable_path(paths)) - - # On Python 3, the search paths need to be unicode. - paths = [os.fsdecode(p) for p in paths] - - # Extend the `beetsplug` package to include the plugin paths. - import beetsplug - - beetsplug.__path__ = paths + list(beetsplug.__path__) - - # For backwards compatibility, also support plugin paths that - # *contain* a `beetsplug` package. - sys.path += paths - - # If we were given any plugins on the command line, use those. - if options.plugins is not None: - plugin_list = ( - options.plugins.split(",") if len(options.plugins) > 0 else [] - ) - else: - plugin_list = config["plugins"].as_str_seq() - # TODO: Remove in v2.4 or v3 - if "musicbrainz" in config and config["musicbrainz"].get().get( - "enabled" - ): - plugin_list.append("musicbrainz") - - # Exclude any plugins that were specified on the command line - if options.exclude is not None: - plugin_list = [ - p for p in plugin_list if p not in options.exclude.split(",") - ] - - plugins.load_plugins(plugin_list) - return plugins - - -def _setup(options, lib=None): +def _setup( + options: optparse.Values, lib: library.Library | None +) -> tuple[list[Subcommand], library.Library]: """Prepare and global state and updates it with command line options. Returns a list of subcommands, a list of plugins, and a library instance. """ config = _configure(options) - plugins = _load_plugins(options, config) - - plugins.send("pluginload") + plugins.load_plugins() # Get the default subcommands. from beets.ui.commands import default_commands @@ -1621,7 +1591,7 @@ def _setup(options, lib=None): lib = _open_library(config) plugins.send("library_opened", lib=lib) - return subcommands, plugins, lib + return subcommands, lib def _configure(options): @@ -1675,7 +1645,7 @@ def _ensure_db_directory_exists(path): os.makedirs(newpath) -def _open_library(config): +def _open_library(config: confuse.LazyConfig) -> library.Library: """Create a new library instance from the configuration.""" dbpath = util.bytestring_path(config["library"].as_filename()) _ensure_db_directory_exists(dbpath) @@ -1702,7 +1672,7 @@ def _open_library(config): return lib -def _raw_main(args, lib=None): +def _raw_main(args: list[str], lib=None) -> None: """A helper function for `main` without top-level exception handling. """ @@ -1728,16 +1698,31 @@ def _raw_main(args, lib=None): parser.add_option( "-c", "--config", dest="config", help="path to configuration file" ) + + def parse_csl_callback( + option: optparse.Option, _, value: str, parser: SubcommandsOptionParser + ): + """Parse a comma-separated list of values.""" + setattr( + parser.values, + option.dest, # type: ignore[arg-type] + list(filter(None, value.split(","))), + ) + parser.add_option( "-p", "--plugins", dest="plugins", + action="callback", + callback=parse_csl_callback, help="a comma-separated list of plugins to load", ) parser.add_option( "-P", "--disable-plugins", - dest="exclude", + dest="disabled_plugins", + action="callback", + callback=parse_csl_callback, help="a comma-separated list of plugins to disable", ) parser.add_option( @@ -1769,7 +1754,7 @@ def _raw_main(args, lib=None): return config_edit() test_lib = bool(lib) - subcommands, plugins, lib = _setup(options, lib) + subcommands, lib = _setup(options, lib) parser.add_subcommand(*subcommands) subcommand, suboptions, subargs = parser.parse_subcommand(subargs) diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 58b08c844..e2f7f46bd 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -27,6 +27,7 @@ import subprocess import sys import tempfile import traceback +import warnings from collections import Counter from collections.abc import Sequence from contextlib import suppress @@ -1191,3 +1192,26 @@ def get_temp_filename( def unique_list(elements: Iterable[T]) -> list[T]: """Return a list with unique elements in the original order.""" return list(dict.fromkeys(elements)) + + +def deprecate_imports( + old_module: str, new_module_by_name: dict[str, str], name: str, version: str +) -> Any: + """Handle deprecated module imports by redirecting to new locations. + + Facilitates gradual migration of module structure by intercepting import + attempts for relocated functionality. Issues deprecation warnings while + transparently providing access to the moved implementation, allowing + existing code to continue working during transition periods. + """ + if new_module := new_module_by_name.get(name): + warnings.warn( + ( + f"'{old_module}.{name}' is deprecated and will be removed" + f" in {version}. Use '{new_module}.{name}' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return getattr(import_module(new_module), name) + raise AttributeError(f"module '{old_module}' has no attribute '{name}'") diff --git a/beets/util/pipeline.py b/beets/util/pipeline.py index 140407f04..bd2c49316 100644 --- a/beets/util/pipeline.py +++ b/beets/util/pipeline.py @@ -38,10 +38,7 @@ import sys from threading import Lock, Thread from typing import Callable, Generator, TypeVar -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack +from typing_extensions import TypeVarTuple, Unpack BUBBLE = "__PIPELINE_BUBBLE__" POISON = "__PIPELINE_POISON__" diff --git a/beetsplug/absubmit.py b/beetsplug/absubmit.py index 3d3227ed2..c02a1c923 100644 --- a/beetsplug/absubmit.py +++ b/beetsplug/absubmit.py @@ -18,9 +18,9 @@ import errno import hashlib import json import os +import shutil import subprocess import tempfile -from distutils.spawn import find_executable import requests @@ -84,7 +84,7 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): # Get the executable location on the system, which we need # to calculate the SHA-1 hash. - self.extractor = find_executable(self.extractor) + self.extractor = shutil.which(self.extractor) # Calculate extractor hash. self.extractor_sha = hashlib.sha1() diff --git a/beetsplug/aura.py b/beetsplug/aura.py index e7034c1e9..53458d7ee 100644 --- a/beetsplug/aura.py +++ b/beetsplug/aura.py @@ -16,7 +16,6 @@ import os import re -import sys from collections.abc import Mapping from dataclasses import dataclass from mimetypes import guess_type @@ -30,11 +29,7 @@ from flask import ( request, send_file, ) - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self +from typing_extensions import Self from beets import config from beets.dbcore.query import ( diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 9765f317f..ac7421c5f 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -305,7 +305,14 @@ class DiscogsPlugin(MetadataSourcePlugin): # Explicitly reload the `Release` fields, as they might not be yet # present if the result is from a `discogs_client.search()`. if not result.data.get("artists"): - result.refresh() + try: + result.refresh() + except CONNECTION_ERRORS: + self._log.debug( + "Connection error in release lookup: {0}", + result, + ) + return None # Sanity check for required fields. The list of required fields is # defined at Guideline 1.3.1.a, but in practice some releases might be diff --git a/beetsplug/loadext.py b/beetsplug/loadext.py index cc673dab2..f20580217 100644 --- a/beetsplug/loadext.py +++ b/beetsplug/loadext.py @@ -25,7 +25,7 @@ class LoadExtPlugin(BeetsPlugin): super().__init__() if not Database.supports_extensions: - self._log.warn( + self._log.warning( "loadext is enabled but the current SQLite " "installation does not support extensions" ) diff --git a/beetsplug/web/templates/index.html b/beetsplug/web/templates/index.html index 0fdd46d15..fe88a20ad 100644 --- a/beetsplug/web/templates/index.html +++ b/beetsplug/web/templates/index.html @@ -1,14 +1,17 @@ - + + + + + beets - - + href="{{ url_for('static', filename='beets.css') }}" + type="text/css"> - + @@ -17,18 +20,14 @@

beets

- - - - +
-
@@ -36,15 +35,14 @@
- -
-
- -
-
- +
+
diff --git a/codecov.yml b/codecov.yml index c899db06a..dbfa484f5 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,5 +1,5 @@ comment: - layout: "condensed_header, condensed_files" + layout: "header, diff, files" require_changes: true # Sets non-blocking status checks @@ -13,6 +13,3 @@ coverage: default: informational: true changes: false - -github_checks: - annotations: false diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst index 822f55dc2..6ea19ecd9 100644 --- a/docs/_templates/autosummary/base.rst +++ b/docs/_templates/autosummary/base.rst @@ -1,3 +1,3 @@ {{ fullname | escape | underline}} .. currentmodule:: {{ module }} -.. auto{{ objtype }}:: {{ objname }} \ No newline at end of file +.. auto{{ objtype }}:: {{ objname }} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst index 6927f8360..fdf251b15 100644 --- a/docs/_templates/autosummary/class.rst +++ b/docs/_templates/autosummary/class.rst @@ -3,10 +3,10 @@ .. currentmodule:: {{ module }} .. autoclass:: {{ objname }} - :members: <-- add at least this line + :members: <-- add at least this line :private-members: - :show-inheritance: <-- plus I want to show inheritance... - :inherited-members: <-- ...and inherited members too + :show-inheritance: <-- plus I want to show inheritance... + :inherited-members: <-- ...and inherited members too {% block methods %} .. automethod:: __init__ @@ -25,4 +25,3 @@ {% endblock %} .. rubric:: {{ _('Methods definition') }} - diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst index 9383a2307..923bc55f8 100644 --- a/docs/_templates/autosummary/module.rst +++ b/docs/_templates/autosummary/module.rst @@ -8,4 +8,4 @@ {%- endfor %} {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/docs/api/database.rst b/docs/api/database.rst index 627b5dc39..b8c2235a2 100644 --- a/docs/api/database.rst +++ b/docs/api/database.rst @@ -1,20 +1,18 @@ Database --------- +======== .. currentmodule:: beets.library - Library -''''''' +------- .. autosummary:: :toctree: generated/ Library - Models -'''''' +------ .. autosummary:: :toctree: generated/ @@ -23,9 +21,8 @@ Models Album Item - Transactions -'''''''''''' +------------ .. currentmodule:: beets.dbcore.db @@ -35,7 +32,7 @@ Transactions Transaction Queries -''''''' +------- .. currentmodule:: beets.dbcore.query @@ -44,4 +41,4 @@ Queries Query FieldQuery - AndQuery \ No newline at end of file + AndQuery diff --git a/docs/api/plugins.rst b/docs/api/plugins.rst index 0d6c13718..9320425db 100644 --- a/docs/api/plugins.rst +++ b/docs/api/plugins.rst @@ -1,10 +1,8 @@ Plugins -------- +======= .. currentmodule:: beets.plugins - - .. autosummary:: :toctree: generated/ diff --git a/docs/changelog.rst b/docs/changelog.rst index c2b0c0ca5..ab896a7ff 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,238 +1,250 @@ Changelog ========= -Changelog goes here! Please add your entry to the bottom of one of the lists below! +Changelog goes here! Please add your entry to the bottom of one of the lists +below! Unreleased ---------- New features: -* :doc:`plugins/musicbrainz`: The MusicBrainz autotagger has been moved to - a separate plugin. The default :ref:`plugins-config` includes `musicbrainz`, - but if you've customized your `plugins` list in your configuration, you'll - need to explicitly add `musicbrainz` to continue using this functionality. - Configuration option `musicbrainz.enabled` has thus been deprecated. - :bug:`2686` - :bug:`4605` -* :doc:`plugins/web`: Show notifications when a track plays. This uses the - Media Session API to customize media notifications. -* :doc:`plugins/discogs`: Add configurable ``search_limit`` option to - limit the number of results returned by the Discogs metadata search queries. -* :doc:`plugins/discogs`: Implement ``track_for_id`` method to allow retrieving - singletons by their Discogs ID. - :bug:`4661` -* :doc:`plugins/replace`: Add new plugin. -* :doc:`plugins/duplicates`: Add ``--remove`` option, allowing to remove from - the library without deleting media files. - :bug:`5832` -* :doc:`plugins/playlist`: Support files with the `.m3u8` extension. +- :doc:`plugins/musicbrainz`: The MusicBrainz autotagger has been moved to a + separate plugin. The default :ref:`plugins-config` includes ``musicbrainz``, + but if you've customized your ``plugins`` list in your configuration, you'll + need to explicitly add ``musicbrainz`` to continue using this functionality. + Configuration option ``musicbrainz.enabled`` has thus been deprecated. + :bug:`2686` :bug:`4605` +- :doc:`plugins/web`: Show notifications when a track plays. This uses the Media + Session API to customize media notifications. +- :doc:`plugins/discogs`: Add configurable ``search_limit`` option to limit the + number of results returned by the Discogs metadata search queries. +- :doc:`plugins/discogs`: Implement ``track_for_id`` method to allow retrieving + singletons by their Discogs ID. :bug:`4661` +- :doc:`plugins/replace`: Add new plugin. +- :doc:`plugins/duplicates`: Add ``--remove`` option, allowing to remove from + the library without deleting media files. :bug:`5832` +- :doc:`plugins/playlist`: Support files with the ``.m3u8`` extension. :bug:`5829` -* :doc:`plugins/mbcollection`: When getting the user collections, only consider +- :doc:`plugins/mbcollection`: When getting the user collections, only consider collections of releases, and ignore collections of other entity types. -* :doc:`plugins/mpdstats`: Add new configuration option, +- :doc:`plugins/mpdstats`: Add new configuration option, ``played_ratio_threshold``, to allow configuring the percentage the song must be played for it to be counted as played instead of skipped. +- :doc:`plugins/web`: Display artist and album as part of the search results. Bug fixes: -* :doc:`plugins/musicbrainz`: fix regression where user configured - ``extra_tags`` have been read incorrectly. - :bug:`5788` -* tests: Fix library tests failing on Windows when run from outside ``D:/``. +- :doc:`plugins/musicbrainz`: fix regression where user configured + ``extra_tags`` have been read incorrectly. :bug:`5788` +- tests: Fix library tests failing on Windows when run from outside ``D:/``. :bug:`5802` -* Fix an issue where calling `Library.add` would cause the `database_change` - event to be sent twice, not once. - :bug:`5560` -* Fix ``HiddenFileTest`` by using ``bytestring_path()``. -* tests: Fix tests failing without ``langdetect`` (by making it required). +- Fix an issue where calling ``Library.add`` would cause the ``database_change`` + event to be sent twice, not once. :bug:`5560` +- Fix ``HiddenFileTest`` by using ``bytestring_path()``. +- tests: Fix tests failing without ``langdetect`` (by making it required). :bug:`5797` -* :doc:`plugins/musicbrainz`: Fix the MusicBrainz search not taking into - account the album/recording aliases -* :doc:`/plugins/spotify`: Fix the issue with that every query to spotify was +- :doc:`plugins/musicbrainz`: Fix the MusicBrainz search not taking into account + the album/recording aliases +- :doc:`/plugins/spotify`: Fix the issue with that every query to spotify was ascii encoded. This resulted in bad matches for queries that contained special - e.g. non latin characters as 盗作. If you want to keep the legacy behavior - set the config option ``spotify.search_query_ascii: yes``. - :bug:`5699` - + e.g. non latin characters as 盗作. If you want to keep the legacy behavior set + the config option ``spotify.search_query_ascii: yes``. :bug:`5699` +- :doc:`plugins/discogs`: Beets will no longer crash if a release has been + deleted, and returns a 404. + For packagers: -* Optional ``extra_tags`` parameter has been removed from +- Optional ``extra_tags`` parameter has been removed from ``BeetsPlugin.candidates`` method signature since it is never passed in. If you override this method in your plugin, feel free to remove this parameter. +- Loosened ``typing_extensions`` dependency in pyproject.toml to apply to every + python version. For plugin developers: -* The `fetchart` plugins has seen a few changes to function signatures and - source registration in the process of introducing typings to the code. - Custom art sources might need to be adapted. -* We split the responsibilities of plugins into two base classes - #. :class:`beets.plugins.BeetsPlugin` - is the base class for all plugins, any plugin needs to inherit from this class. - #. :class:`beets.metadata_plugin.MetadataSourcePlugin` - allows plugins to act like metadata sources. E.g. used by the MusicBrainz plugin. All plugins - in the beets repo are opted into this class where applicable. If you are maintaining a plugin - that acts like a metadata source, i.e. you expose any of ``track_for_id``, - ``album_for_id``, ``candidates``, ``item_candidates``, ``album_distance``, ``track_distance`` methods, - please update your plugin to inherit from the new baseclass, as otherwise your plugin will - stop working with the next major release. -* Flexible fields, which can be used by plugins to store additional metadata, +- The ``fetchart`` plugins has seen a few changes to function signatures and + source registration in the process of introducing typings to the code. Custom + art sources might need to be adapted. +- We split the responsibilities of plugins into two base classes + + 1. :class:`beets.plugins.BeetsPlugin` is the base class for all plugins, any + plugin needs to inherit from this class. + 2. :class:`beets.metadata_plugin.MetadataSourcePlugin` allows plugins to act + like metadata sources. E.g. used by the MusicBrainz plugin. All plugins in + the beets repo are opted into this class where applicable. If you are + maintaining a plugin that acts like a metadata source, i.e. you expose any + of ``track_for_id``, ``album_for_id``, ``candidates``, ``item_candidates``, + ``album_distance``, ``track_distance`` methods, please update your plugin + to inherit from the new baseclass, as otherwise your plugin will stop + working with the next major release. + +- Several definitions have been moved: + + - ``BLOB_TYPE`` constant, ``PathQuery`` and ``SingletonQuery`` queries have + moved from ``beets.library`` to ``beets.dbcore.query`` module + - ``DateType``, ``DurationType``, ``PathType`` types and ``MusicalKey`` class + have moved from ``beets.library`` to ``beets.dbcore.types`` module. + - ``Distance`` has moved from ``beets.autotag`` to ``beets.autotag.distance`` + module. + - ``beets.autotag.current_metadata`` has been renamed to + ``beets.util.get_most_common_tags``. + + Old imports are now deprecated and will be removed in version ``3.0.0``. + +- ``beets.ui.decargs`` is deprecated and will be removed in version ``3.0.0``. +- Beets is now PEP 561 compliant, which means that it provides type hints for + all public APIs. This allows IDEs to provide better autocompletion and type + checking for downstream users of the beets API. +- ``plugins.find_plugins`` function does not anymore load plugins. You need to + explicitly call ``plugins.load_plugins()`` to load them. +- ``plugins.load_plugins`` function does not anymore accept the list of plugins + to load. Instead, it loads all plugins that are configured by + :ref:`plugins-config` configuration. +- Flexible fields, which can be used by plugins to store additional metadata, now also support list values. Previously, beets would throw an error while storing the data in the SQL database due to missing type conversion. :bug:`5698` Other changes: -* Refactor: Split responsibilities of Plugins into MetaDataPlugins and general Plugins. -* Documentation structure for auto generated API references changed slightly. - Autogenerated API references are now located in the `docs/api` subdirectory. -* :doc:`/plugins/substitute`: Fix rST formatting for example cases so that each +- Refactor: Split responsibilities of Plugins into MetaDataPlugins and general + Plugins. +- Documentation structure for auto generated API references changed slightly. + Autogenerated API references are now located in the ``docs/api`` subdirectory. +- :doc:`/plugins/substitute`: Fix rST formatting for example cases so that each case is shown on separate lines. -* Refactored library.py file by splitting it into multiple modules within the +- Refactored library.py file by splitting it into multiple modules within the beets/library directory. +- Added a test to check that all plugins can be imported without errors. 2.3.1 (May 14, 2025) -------------------- Bug fixes: -* :doc:`/reference/pathformat`: Fixed a regression where path legalization +- :doc:`/reference/pathformat`: Fixed a regression where path legalization incorrectly removed parts of user-configured path formats that followed a dot - (**.**). - :bug:`5771` + (**.**). :bug:`5771` For packagers: -* Force ``poetry`` version below 2 to avoid it mangling file modification times - in ``sdist`` package. - :bug:`5770` +- Force ``poetry`` version below 2 to avoid it mangling file modification times + in ``sdist`` package. :bug:`5770` 2.3.0 (May 07, 2025) -------------------- -Beets now requires Python 3.9 or later since support for EOL Python 3.8 has -been dropped. +Beets now requires Python 3.9 or later since support for EOL Python 3.8 has been +dropped. New features: -* :doc:`plugins/lastgenre`: The new configuration option, ``keep_existing``, +- :doc:`plugins/lastgenre`: The new configuration option, ``keep_existing``, provides more fine-grained control over how pre-populated genre tags are handled. The ``force`` option now behaves in a more conventional manner. :bug:`4982` -* :doc:`plugins/lyrics`: Add new configuration option ``dist_thresh`` to - control the maximum allowed distance between the lyrics search result and the - tagged item's artist and title. This is useful for preventing false positives - when fetching lyrics. -* :doc:`plugins/lyrics`: Rewrite lyrics translation functionality to use Azure +- :doc:`plugins/lyrics`: Add new configuration option ``dist_thresh`` to control + the maximum allowed distance between the lyrics search result and the tagged + item's artist and title. This is useful for preventing false positives when + fetching lyrics. +- :doc:`plugins/lyrics`: Rewrite lyrics translation functionality to use Azure AI Translator API and add relevant instructions to the documentation. -* :doc:`plugins/missing`: Add support for all metadata sources. -* :doc:`plugins/mbsync`: Add support for all metadata sorces. +- :doc:`plugins/missing`: Add support for all metadata sources. +- :doc:`plugins/mbsync`: Add support for all metadata sorces. Bug fixes: -* :doc:`plugins/thumbnails`: Fix API call to GIO on big endian architectures - (like s390x) in thumbnails plugin. - :bug:`5708` -* :doc:`plugins/listenbrainz`: Fix rST formatting for URLs of Listenbrainz API Key documentation and config.yaml. -* :doc:`plugins/listenbrainz`: Fix ``UnboundLocalError`` in cases where 'mbid' is not defined. -* :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 - have before the introduction of Poetry. - :bug:`5531` - :bug:`5526` -* :ref:`write-cmd`: Fix the issue where for certain files differences in +- :doc:`plugins/thumbnails`: Fix API call to GIO on big endian architectures + (like s390x) in thumbnails plugin. :bug:`5708` +- :doc:`plugins/listenbrainz`: Fix rST formatting for URLs of Listenbrainz API + Key documentation and config.yaml. +- :doc:`plugins/listenbrainz`: Fix ``UnboundLocalError`` in cases where 'mbid' + is not defined. +- :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 + have before the introduction of Poetry. :bug:`5531` :bug:`5526` +- :ref:`write-cmd`: Fix the issue where for certain files differences in ``mb_artistid``, ``mb_albumartistid`` and ``albumtype`` fields are shown on every attempt to write tags. Note: your music needs to be reimported with - ``beet import -LI`` or synchronised with ``beet mbsync`` in order to fix - this! - :bug:`5265` - :bug:`5371` - :bug:`4715` -* :ref:`import-cmd`: Fix ``MemoryError`` and improve performance tagging large - albums by replacing ``munkres`` library with ``lap.lapjv``. - :bug:`5207` -* :ref:`query-sort`: Fix a bug that would raise an exception when sorting on - a non-string field that is not populated in all items. - :bug:`5512` -* :doc:`plugins/lastgenre`: Fix track-level genre handling. Now when an album-level - genre is set already, single tracks don't fall back to the album's genre and - request their own last.fm genre. Also log messages regarding what's been - tagged are now more polished. - :bug:`5582` -* Fix ambiguous column name ``sqlite3.OperationalError`` that occured in album + ``beet import -LI`` or synchronised with ``beet mbsync`` in order to fix this! + :bug:`5265` :bug:`5371` :bug:`4715` +- :ref:`import-cmd`: Fix ``MemoryError`` and improve performance tagging large + albums by replacing ``munkres`` library with ``lap.lapjv``. :bug:`5207` +- :ref:`query-sort`: Fix a bug that would raise an exception when sorting on a + non-string field that is not populated in all items. :bug:`5512` +- :doc:`plugins/lastgenre`: Fix track-level genre handling. Now when an + album-level genre is set already, single tracks don't fall back to the album's + genre and request their own last.fm genre. Also log messages regarding what's + been tagged are now more polished. :bug:`5582` +- Fix ambiguous column name ``sqlite3.OperationalError`` that occured in album queries that filtered album track titles, for example ``beet list -a keyword title:foo``. -* :doc:`plugins/lyrics`: Rewrite lyrics tests using pytest to provide isolated - configuration for each test case. This fixes the issue where some tests - failed because they read developers' local lyrics configuration. - :bug:`5133` -* :doc:`plugins/lyrics`: Do not attempt to search for lyrics if either the +- :doc:`plugins/lyrics`: Rewrite lyrics tests using pytest to provide isolated + configuration for each test case. This fixes the issue where some tests failed + because they read developers' local lyrics configuration. :bug:`5133` +- :doc:`plugins/lyrics`: Do not attempt to search for lyrics if either the artist or title is missing and ignore ``artist_sort`` value if it is empty. :bug:`2635` -* :doc:`plugins/lyrics`: Fix fetching lyrics from ``lrclib`` source. If we - cannot find lyrics for a specific album, artist, title combination, the - plugin now tries to search for the artist and title and picks the most - relevant result. Update the default ``sources`` configuration to prioritize - ``lrclib`` over other sources since it returns reliable results quicker than - others. +- :doc:`plugins/lyrics`: Fix fetching lyrics from ``lrclib`` source. If we + cannot find lyrics for a specific album, artist, title combination, the plugin + now tries to search for the artist and title and picks the most relevant + result. Update the default ``sources`` configuration to prioritize ``lrclib`` + over other sources since it returns reliable results quicker than others. :bug:`5102` -* :doc:`plugins/lyrics`: Fix the issue with ``genius`` backend not being able - to match lyrics when there is a slight variation in the artist name. - :bug:`4791` -* :doc:`plugins/lyrics`: Fix plugin crash when ``genius`` backend returns empty - lyrics. - :bug:`5583` -* ImageMagick 7.1.1-44 is now supported. -* :doc:`plugins/parentwork`: Only output parentwork changes when running in verbose mode. +- :doc:`plugins/lyrics`: Fix the issue with ``genius`` backend not being able to + match lyrics when there is a slight variation in the artist name. :bug:`4791` +- :doc:`plugins/lyrics`: Fix plugin crash when ``genius`` backend returns empty + lyrics. :bug:`5583` +- ImageMagick 7.1.1-44 is now supported. +- :doc:`plugins/parentwork`: Only output parentwork changes when running in + verbose mode. 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. +- 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: -* Release workflow: fix the issue where the new release tag is created for the +- Release workflow: fix the issue where the new release tag is created for the wrong (outdated) commit. Now the tag is created in the same workflow step - right after committing the version update. - :bug:`5539` -* :doc:`/plugins/smartplaylist`: URL-encode additional item `fields` within generated - EXTM3U playlists instead of JSON-encoding them. -* typehints: `./beets/importer.py` file now has improved typehints. -* typehints: `./beets/plugins.py` file now includes typehints. -* :doc:`plugins/ftintitle`: Optimize the plugin by avoiding unnecessary writes + right after committing the version update. :bug:`5539` +- :doc:`/plugins/smartplaylist`: URL-encode additional item ``fields`` within + generated EXTM3U playlists instead of JSON-encoding them. +- typehints: ``./beets/importer.py`` file now has improved typehints. +- typehints: ``./beets/plugins.py`` file now includes typehints. +- :doc:`plugins/ftintitle`: Optimize the plugin by avoiding unnecessary writes to the database. -* Database models are now serializable with pickle. +- Database models are now serializable with pickle. 2.2.0 (December 02, 2024) ------------------------- New features: -* :doc:`/plugins/substitute`: Allow the replacement string to use capture groups +- :doc:`/plugins/substitute`: Allow the replacement string to use capture groups from the match. It is thus possible to create more general rules, applying to many different artists at once. Bug fixes: -* Check if running python from the Microsoft Store and provide feedback to install - from python.org. - :bug:`5467` -* Fix bug where matcher doesn't consider medium number when importing. This makes - it difficult to import hybrid SACDs and other releases with duplicate tracks. - :bug:`5148` -* Bring back test files and the manual to the source distribution tarball. +- Check if running python from the Microsoft Store and provide feedback to + install from python.org. :bug:`5467` +- Fix bug where matcher doesn't consider medium number when importing. This + makes it difficult to import hybrid SACDs and other releases with duplicate + tracks. :bug:`5148` +- Bring back test files and the manual to the source distribution tarball. :bug:`5513` Other changes: -* Changed `bitesize` label to `good first issue`. Our `contribute`_ page is now - automatically populated with these issues. :bug:`4855` +- Changed ``bitesize`` label to ``good first issue``. Our contribute_ page is + now automatically populated with these issues. :bug:`4855` .. _contribute: https://github.com/beetbox/beets/contribute @@ -241,90 +253,90 @@ Other changes: New features: -* New template function added: ``%capitalize``. Converts the first letter of - the text to uppercase and the rest to lowercase. -* Ability to query albums with track db fields and vice-versa, for example +- New template function added: ``%capitalize``. Converts the first letter of the + text to uppercase and the rest to lowercase. +- Ability to query albums with track db fields and vice-versa, for example ``beet list -a title:something`` or ``beet list artpath:cover``. Consequently album queries involving ``path`` field have been sped up, like ``beet list -a path:/path/``. -* :doc:`plugins/ftintitle`: New ``keep_in_artist`` option for the plugin, which +- :doc:`plugins/ftintitle`: New ``keep_in_artist`` option for the plugin, which allows keeping the "feat." part in the artist metadata while still changing the title. -* :doc:`plugins/autobpm`: Add new configuration option ``beat_track_kwargs`` - which enables adjusting keyword arguments supplied to librosa's - ``beat_track`` function call. -* Beets now uses ``platformdirs`` to determine the default music directory. - This location varies between systems -- for example, users can configure it - on Unix systems via ``user-dirs.dirs(5)``. +- :doc:`plugins/autobpm`: Add new configuration option ``beat_track_kwargs`` + which enables adjusting keyword arguments supplied to librosa's ``beat_track`` + function call. +- Beets now uses ``platformdirs`` to determine the default music directory. This + location varies between systems -- for example, users can configure it on Unix + systems via ``user-dirs.dirs(5)``. Bug fixes: -* :doc:`plugins/ftintitle`: The detection of a "feat. X" part in a song title does not produce any false - positives caused by words like "and" or "with" anymore. :bug:`5441` -* :doc:`plugins/ftintitle`: The detection of a "feat. X" part now also matches such parts if they are in - parentheses or brackets. :bug:`5436` -* Improve naming of temporary files by separating the random part with the file extension. -* Fix the ``auto`` value for the :ref:`reflink` config option. -* Fix lyrics plugin only getting part of the lyrics from ``Genius.com`` :bug:`4815` -* Album flexible fields are now correctly saved. For instance MusicBrainz external links - such as `bandcamp_album_id` will be available on albums in addition to tracks. - For albums already in your library, a re-import is required for the fields to be added. - Such a re-import can be done with, in this case, `beet import -L data_source:=MusicBrainz`. -* :doc:`plugins/autobpm`: Fix the ``TypeError`` where tempo was being returned +- :doc:`plugins/ftintitle`: The detection of a "feat. X" part in a song title + does not produce any false positives caused by words like "and" or "with" + anymore. :bug:`5441` +- :doc:`plugins/ftintitle`: The detection of a "feat. X" part now also matches + such parts if they are in parentheses or brackets. :bug:`5436` +- Improve naming of temporary files by separating the random part with the file + extension. +- Fix the ``auto`` value for the :ref:`reflink` config option. +- Fix lyrics plugin only getting part of the lyrics from ``Genius.com`` + :bug:`4815` +- Album flexible fields are now correctly saved. For instance MusicBrainz + external links such as ``bandcamp_album_id`` will be available on albums in + addition to tracks. For albums already in your library, a re-import is + required for the fields to be added. Such a re-import can be done with, in + this case, ``beet import -L data_source:=MusicBrainz``. +- :doc:`plugins/autobpm`: Fix the ``TypeError`` where tempo was being returned as a numpy array. Update ``librosa`` dependency constraint to prevent similar - issues in the future. - :bug:`5289` -* :doc:`plugins/discogs`: Fix the ``TypeError`` when there is no description. -* Use single quotes in all SQL queries - :bug:`4709` -* :doc:`plugins/lyrics`: Update ``tekstowo`` backend to fetch lyrics directly - since recent updates to their website made it unsearchable. - :bug:`5456` -* :doc:`plugins/convert`: Fixed the convert plugin ``no_convert`` option so - that it no longer treats "and" and "or" queries the same. To maintain - previous behaviour add commas between your query keywords. For help see + issues in the future. :bug:`5289` +- :doc:`plugins/discogs`: Fix the ``TypeError`` when there is no description. +- Use single quotes in all SQL queries :bug:`4709` +- :doc:`plugins/lyrics`: Update ``tekstowo`` backend to fetch lyrics directly + since recent updates to their website made it unsearchable. :bug:`5456` +- :doc:`plugins/convert`: Fixed the convert plugin ``no_convert`` option so that + it no longer treats "and" and "or" queries the same. To maintain previous + behaviour add commas between your query keywords. For help see :ref:`combiningqueries`. -* Fix the ``TypeError`` when :ref:`set_fields` is provided non-string values. :bug:`4840` +- Fix the ``TypeError`` when :ref:`set_fields` is provided non-string values. + :bug:`4840` For packagers: -* The minimum supported Python version is now 3.8. -* The ``beet`` script has been removed from the repository. -* The ``typing_extensions`` is required for Python 3.10 and below. +- The minimum supported Python version is now 3.8. +- The ``beet`` script has been removed from the repository. +- The ``typing_extensions`` is required for Python 3.10 and below. Other changes: -* :doc:`contributing`: The project now uses ``poetry`` for packaging and +- :doc:`contributing`: The project now uses ``poetry`` for packaging and dependency management. This change affects project management and mostly affects beets developers. Please see updates in :ref:`getting-the-source` and :ref:`testing` for more information. -* :doc:`contributing`: Since ``poetry`` now manages local virtual environments, - `tox` has been replaced by a task runner ``poethepoet``. This change affects +- :doc:`contributing`: Since ``poetry`` now manages local virtual environments, + ``tox`` has been replaced by a task runner ``poethepoet``. This change affects beets developers and contributors. Please see updates in the - :ref:`development-tools` section for more details. Type ``poe`` while in - the project directory to see the available commands. -* Installation instructions have been made consistent across plugins + :ref:`development-tools` section for more details. Type ``poe`` while in the + project directory to see the available commands. +- Installation instructions have been made consistent across plugins documentation. Users should simply install ``beets`` with an ``extra`` of the corresponding plugin name in order to install extra dependencies for that plugin. -* GitHub workflows have been reorganised for clarity: style, linting, type and +- GitHub workflows have been reorganised for clarity: style, linting, type and docs checks now live in separate jobs and are named accordingly. -* Added caching for dependency installation in all CI jobs which speeds them up +- Added caching for dependency installation in all CI jobs which speeds them up a bit, especially the tests. -* The linting workflow has been made to run only when Python files or +- The linting workflow has been made to run only when Python files or documentation is changed, and they only check the changed files. When dependencies are updated (``poetry.lock``), then the entire code base is checked. -* The long-deprecated ``beets.util.confit`` module has been removed. This may +- The long-deprecated ``beets.util.confit`` module has been removed. This may cause extremely outdated external plugins to fail to load. -* :doc:`plugins/autobpm`: Add plugin dependencies to ``pyproject.toml`` under +- :doc:`plugins/autobpm`: Add plugin dependencies to ``pyproject.toml`` under the ``autobpm`` extra and update the plugin installation instructions in the - docs. - Since importing the bpm calculation functionality from ``librosa`` takes + docs. Since importing the bpm calculation functionality from ``librosa`` takes around 4 seconds, update the plugin to only do so when it actually needs to - calculate the bpm. Previously this import was being done immediately, so - every ``beet`` invocation was being delayed by a couple of seconds. - :bug:`5185` + calculate the bpm. Previously this import was being done immediately, so every + ``beet`` invocation was being delayed by a couple of seconds. :bug:`5185` 2.0.0 (May 30, 2024) -------------------- @@ -334,911 +346,779 @@ for Python 3.6). Major new features: -* The beets importer UI received a major overhaul. Several new configuration +- The beets importer UI received a major overhaul. Several new configuration options are available for customizing layout and colors: :ref:`ui_options`. :bug:`3721` :bug:`5028` New features: -* :doc:`/plugins/edit`: Prefer editor from ``VISUAL`` environment variable over ``EDITOR``. -* :ref:`config-cmd`: Prefer editor from ``VISUAL`` environment variable over ``EDITOR``. -* :doc:`/plugins/listenbrainz`: Add initial support for importing history and playlists from `ListenBrainz` - :bug:`1719` -* :doc:`plugins/mbsubmit`: add new prompt choices helping further to submit unmatched tracks to MusicBrainz faster. -* :doc:`plugins/spotify`: We now fetch track's ISRC, EAN, and UPC identifiers from Spotify when using the ``spotifysync`` command. - :bug:`4992` -* :doc:`plugins/discogs`: supply a value for the `cover_art_url` attribute, for use by `fetchart`. - :bug:`429` -* :ref:`update-cmd`: added ```-e``` flag for excluding fields from being updated. -* :doc:`/plugins/deezer`: Import rank and other attributes from Deezer during import and add a function to update the rank of existing items. - :bug:`4841` -* resolve transl-tracklisting relations for pseudo releases and merge data with the actual release - :bug:`654` -* Fetchart: Use the right field (`spotify_album_id`) to obtain the Spotify album id - :bug:`4803` -* Prevent reimporting album if it is permanently removed from Spotify +- :doc:`/plugins/edit`: Prefer editor from ``VISUAL`` environment variable over + ``EDITOR``. +- :ref:`config-cmd`: Prefer editor from ``VISUAL`` environment variable over + ``EDITOR``. +- :doc:`/plugins/listenbrainz`: Add initial support for importing history and + playlists from ``ListenBrainz`` :bug:`1719` +- :doc:`plugins/mbsubmit`: add new prompt choices helping further to submit + unmatched tracks to MusicBrainz faster. +- :doc:`plugins/spotify`: We now fetch track's ISRC, EAN, and UPC identifiers + from Spotify when using the ``spotifysync`` command. :bug:`4992` +- :doc:`plugins/discogs`: supply a value for the ``cover_art_url`` attribute, + for use by ``fetchart``. :bug:`429` +- :ref:`update-cmd`: added ``-e`` flag for excluding fields from being updated. +- :doc:`/plugins/deezer`: Import rank and other attributes from Deezer during + import and add a function to update the rank of existing items. :bug:`4841` +- resolve transl-tracklisting relations for pseudo releases and merge data with + the actual release :bug:`654` +- Fetchart: Use the right field (``spotify_album_id``) to obtain the Spotify + album id :bug:`4803` +- Prevent reimporting album if it is permanently removed from Spotify :bug:`4800` -* Added option to use `cover_art_url` as an album art source in the `fetchart` plugin. - :bug:`4707` -* :doc:`/plugins/fetchart`: The plugin can now get album art from `spotify`. -* Added option to specify a URL in the `embedart` plugin. - :bug:`83` -* :ref:`list-cmd` `singleton:true` queries have been made faster -* :ref:`list-cmd` `singleton:1` and `singleton:0` can now alternatively be used in queries, same as `comp` -* --from-logfile now parses log files using a UTF-8 encoding in `beets/beets/ui/commands.py`. - :bug:`4693` -* :doc:`/plugins/bareasc` lookups have been made faster -* :ref:`list-cmd` lookups using the pattern operator `::` have been made faster -* Added additional error handling for `spotify` plugin. - :bug:`4686` -* We now import the remixer field from Musicbrainz into the library. - :bug:`4428` -* :doc:`/plugins/mbsubmit`: Added a new `mbsubmit` command to print track information to be submitted to MusicBrainz after initial import. - :bug:`4455` -* Added `spotify_updated` field to track when the information was last updated. -* We now import and tag the `album` information when importing singletons using Spotify source. - :bug:`4398` -* :doc:`/plugins/spotify`: The plugin now provides an additional command - `spotifysync` that allows getting track popularity and audio features - information from Spotify. - :bug:`4094` -* :doc:`/plugins/spotify`: The plugin now records Spotify-specific IDs in the - `spotify_album_id`, `spotify_artist_id`, and `spotify_track_id` fields. +- Added option to use ``cover_art_url`` as an album art source in the + ``fetchart`` plugin. :bug:`4707` +- :doc:`/plugins/fetchart`: The plugin can now get album art from ``spotify``. +- Added option to specify a URL in the ``embedart`` plugin. :bug:`83` +- :ref:`list-cmd` ``singleton:true`` queries have been made faster +- :ref:`list-cmd` ``singleton:1`` and ``singleton:0`` can now alternatively be + used in queries, same as ``comp`` +- --from-logfile now parses log files using a UTF-8 encoding in + ``beets/beets/ui/commands.py``. :bug:`4693` +- :doc:`/plugins/bareasc` lookups have been made faster +- :ref:`list-cmd` lookups using the pattern operator ``::`` have been made + faster +- Added additional error handling for ``spotify`` plugin. :bug:`4686` +- We now import the remixer field from Musicbrainz into the library. :bug:`4428` +- :doc:`/plugins/mbsubmit`: Added a new ``mbsubmit`` command to print track + information to be submitted to MusicBrainz after initial import. :bug:`4455` +- Added ``spotify_updated`` field to track when the information was last + updated. +- We now import and tag the ``album`` information when importing singletons + using Spotify source. :bug:`4398` +- :doc:`/plugins/spotify`: The plugin now provides an additional command + ``spotifysync`` that allows getting track popularity and audio features + information from Spotify. :bug:`4094` +- :doc:`/plugins/spotify`: The plugin now records Spotify-specific IDs in the + ``spotify_album_id``, ``spotify_artist_id``, and ``spotify_track_id`` fields. :bug:`4348` -* Create the parental directories for database if they do not exist. - :bug:`3808` :bug:`4327` -* :ref:`musicbrainz-config`: a new :ref:`musicbrainz.enabled` option allows disabling - the MusicBrainz metadata source during the autotagging process -* :doc:`/plugins/kodiupdate`: Now supports multiple kodi instances - :bug:`4101` -* Add the item fields ``bitrate_mode``, ``encoder_info`` and ``encoder_settings``. -* Add query prefixes ``=`` and ``~``. -* A new configuration option, :ref:`duplicate_keys`, lets you change which - fields the beets importer uses to identify duplicates. - :bug:`1133` :bug:`4199` -* Add :ref:`exact match ` queries, using the prefixes ``=`` and - ``=~``. - :bug:`4251` -* :doc:`/plugins/discogs`: Permit appending style to genre. -* :doc:`plugins/discogs`: Implement item_candidates for matching singletons. -* :doc:`plugins/discogs`: Check for compliant discogs_client module. -* :doc:`/plugins/convert`: Add a new `auto_keep` option that automatically - converts files but keeps the *originals* in the library. - :bug:`1840` :bug:`4302` -* Added a ``-P`` (or ``--disable-plugins``) flag to specify one/multiple plugin(s) to be - disabled at startup. -* :ref:`import-options`: Add support for re-running the importer on paths in - log files that were created with the ``-l`` (or ``--logfile``) argument. +- Create the parental directories for database if they do not exist. :bug:`3808` + :bug:`4327` +- :ref:`musicbrainz-config`: a new :ref:`musicbrainz.enabled` option allows + disabling the MusicBrainz metadata source during the autotagging process +- :doc:`/plugins/kodiupdate`: Now supports multiple kodi instances :bug:`4101` +- Add the item fields ``bitrate_mode``, ``encoder_info`` and + ``encoder_settings``. +- Add query prefixes ``=`` and ``~``. +- A new configuration option, :ref:`duplicate_keys`, lets you change which + fields the beets importer uses to identify duplicates. :bug:`1133` :bug:`4199` +- Add :ref:`exact match ` queries, using the prefixes ``=`` and + ``=~``. :bug:`4251` +- :doc:`/plugins/discogs`: Permit appending style to genre. +- :doc:`plugins/discogs`: Implement item_candidates for matching singletons. +- :doc:`plugins/discogs`: Check for compliant discogs_client module. +- :doc:`/plugins/convert`: Add a new ``auto_keep`` option that automatically + converts files but keeps the *originals* in the library. :bug:`1840` + :bug:`4302` +- Added a ``-P`` (or ``--disable-plugins``) flag to specify one/multiple + plugin(s) to be disabled at startup. +- :ref:`import-options`: Add support for re-running the importer on paths in log + files that were created with the ``-l`` (or ``--logfile``) argument. :bug:`4379` :bug:`4387` -* Preserve mtimes from archives - :bug:`4392` -* Add :ref:`%sunique{} ` template to disambiguate between singletons. +- Preserve mtimes from archives :bug:`4392` +- Add :ref:`%sunique{} ` template to disambiguate between singletons. :bug:`4438` -* Add a new ``import.ignored_alias_types`` config option to allow for - specific alias types to be skipped over when importing items/albums. -* :doc:`/plugins/smartplaylist`: A new ``--pretend`` option lets the user see +- Add a new ``import.ignored_alias_types`` config option to allow for specific + alias types to be skipped over when importing items/albums. +- :doc:`/plugins/smartplaylist`: A new ``--pretend`` option lets the user see what a new or changed smart playlist saved in the config is actually - returning. - :bug:`4573` -* :doc:`/plugins/fromfilename`: Add debug log messages that inform when the + returning. :bug:`4573` +- :doc:`/plugins/fromfilename`: Add debug log messages that inform when the plugin replaced bad (missing) artist, title or tracknumber metadata. :bug:`4561` :bug:`4600` -* :ref:`musicbrainz-config`: MusicBrainz release pages often link to related +- :ref:`musicbrainz-config`: MusicBrainz release pages often link to related metadata sources like Discogs, Bandcamp, Spotify, Deezer and Beatport. When enabled via the :ref:`musicbrainz.external_ids` options, release ID's will be - extracted from those URL's and imported to the library. - :bug:`4220` -* :doc:`/plugins/convert`: Add support for generating m3u8 playlists together - with converted media files. - :bug:`4373` -* Fetch the ``release_group_title`` field from MusicBrainz. - :bug: `4809` -* :doc:`plugins/discogs`: Add support for applying album information on - singleton imports. - :bug: `4716` -* :doc:`/plugins/smartplaylist`: During explicit runs of the ``splupdate`` + extracted from those URL's and imported to the library. :bug:`4220` +- :doc:`/plugins/convert`: Add support for generating m3u8 playlists together + with converted media files. :bug:`4373` +- Fetch the ``release_group_title`` field from MusicBrainz. :bug:`4809` +- :doc:`plugins/discogs`: Add support for applying album information on + singleton imports. :bug:`4716` +- :doc:`/plugins/smartplaylist`: During explicit runs of the ``splupdate`` command, the log message "Creating playlist ..."" is now displayed instead of hidden in the debug log, which states some form of progress through the UI. :bug:`4861` -* :doc:`plugins/subsonicupdate`: Updates are now triggered whenever either the - beets database is changed or a smart playlist is created/updated. - :bug: `4862` -* :doc:`plugins/importfeeds`: Add a new output format allowing to save a - playlist once per import session. - :bug: `4863` -* Make ArtResizer work with :pypi:`PIL`/:pypi:`pillow` 10.0.0 removals. +- :doc:`plugins/subsonicupdate`: Updates are now triggered whenever either the + beets database is changed or a smart playlist is created/updated. :bug:`4862` +- :doc:`plugins/importfeeds`: Add a new output format allowing to save a + playlist once per import session. :bug:`4863` +- Make ArtResizer work with :pypi:`PIL`/:pypi:`pillow` 10.0.0 removals. :bug:`4869` -* A new configuration option, :ref:`duplicate_verbose_prompt`, allows changing - how duplicates are presented during import. - :bug: `4866` -* :doc:`/plugins/embyupdate`: Add handling for private users by adding - ``userid`` config option. - :bug:`4402` -* :doc:`/plugins/substitute`: Add the new plugin `substitute` as an alternative - to the `rewrite` plugin. The main difference between them being that - `rewrite` modifies files' metadata and `substitute` does not. +- A new configuration option, :ref:`duplicate_verbose_prompt`, allows changing + how duplicates are presented during import. :bug:`4866` +- :doc:`/plugins/embyupdate`: Add handling for private users by adding + ``userid`` config option. :bug:`4402` +- :doc:`/plugins/substitute`: Add the new plugin ``substitute`` as an + alternative to the ``rewrite`` plugin. The main difference between them being + that ``rewrite`` modifies files' metadata and ``substitute`` does not. :bug:`2786` -* Add support for ``artists`` and ``albumartists`` multi-valued tags. - :bug:`505` -* :doc:`/plugins/autobpm`: Add the `autobpm` plugin which uses Librosa to - calculate the BPM of the audio. - :bug:`3856` -* :doc:`/plugins/fetchart`: Fix the error with CoverArtArchive where the - `maxwidth` option would not be used to download a pre-sized thumbnail for +- Add support for ``artists`` and ``albumartists`` multi-valued tags. :bug:`505` +- :doc:`/plugins/autobpm`: Add the ``autobpm`` plugin which uses Librosa to + calculate the BPM of the audio. :bug:`3856` +- :doc:`/plugins/fetchart`: Fix the error with CoverArtArchive where the + ``maxwidth`` option would not be used to download a pre-sized thumbnail for release groups, as is already done with releases. -* :doc:`/plugins/fetchart`: Fix the error with CoverArtArchive where no cover - would be found when the `maxwidth` option matches a pre-sized thumbnail size, - but no thumbnail is provided by CAA. We now fallback to the raw image. -* :doc:`/plugins/advancedrewrite`: Add an advanced version of the `rewrite` +- :doc:`/plugins/fetchart`: Fix the error with CoverArtArchive where no cover + would be found when the ``maxwidth`` option matches a pre-sized thumbnail + size, but no thumbnail is provided by CAA. We now fallback to the raw image. +- :doc:`/plugins/advancedrewrite`: Add an advanced version of the ``rewrite`` plugin which allows to replace fields based on a given library query. -* :doc:`/plugins/lyrics`: Add LRCLIB as a new lyrics provider and a new - `synced` option to prefer synced lyrics over plain lyrics. -* :ref:`import-cmd`: Expose import.quiet_fallback as CLI option. -* :ref:`import-cmd`: Expose `import.incremental_skip_later` as CLI option. -* :doc:`/plugins/smartplaylist`: Expose config options as CLI options. -* :doc:`/plugins/smartplaylist`: Add new option `smartplaylist.output`. -* :doc:`/plugins/smartplaylist`: Add new option `smartplaylist.uri_format`. -* Sorted the default configuration file into categories. - :bug:`4987` -* :doc:`/plugins/convert`: Don't treat WAVE (`.wav`) files as lossy anymore - when using the `never_convert_lossy_files` option. They will get transcoded +- :doc:`/plugins/lyrics`: Add LRCLIB as a new lyrics provider and a new + ``synced`` option to prefer synced lyrics over plain lyrics. +- :ref:`import-cmd`: Expose import.quiet_fallback as CLI option. +- :ref:`import-cmd`: Expose ``import.incremental_skip_later`` as CLI option. +- :doc:`/plugins/smartplaylist`: Expose config options as CLI options. +- :doc:`/plugins/smartplaylist`: Add new option ``smartplaylist.output``. +- :doc:`/plugins/smartplaylist`: Add new option ``smartplaylist.uri_format``. +- Sorted the default configuration file into categories. :bug:`4987` +- :doc:`/plugins/convert`: Don't treat WAVE (``.wav``) files as lossy anymore + when using the ``never_convert_lossy_files`` option. They will get transcoded like the other lossless formats. -* Add support for `barcode` field. - :bug:`3172` -* :doc:`/plugins/smartplaylist`: Add new config option `smartplaylist.fields`. -* :doc:`/plugins/fetchart`: Defer source removal config option evaluation to - the point where they are used really, supporting temporary config changes. +- Add support for ``barcode`` field. :bug:`3172` +- :doc:`/plugins/smartplaylist`: Add new config option ``smartplaylist.fields``. +- :doc:`/plugins/fetchart`: Defer source removal config option evaluation to the + point where they are used really, supporting temporary config changes. Bug fixes: -* Improve ListenBrainz error handling. - :bug:`5459` -* :doc:`/plugins/deezer`: Improve requests error handling. -* :doc:`/plugins/lastimport`: Improve error handling in the `process_tracks` function and enable it to be used with other plugins. -* :doc:`/plugins/spotify`: Improve handling of ConnectionError. -* :doc:`/plugins/deezer`: Improve Deezer plugin error handling and set requests timeout to 10 seconds. - :bug:`4983` -* :doc:`/plugins/spotify`: Add bad gateway (502) error handling. -* :doc:`/plugins/spotify`: Add a limit of 3 retries, instead of retrying endlessly when the API is not available. -* Fix a crash when the Spotify API timeouts or does not return a `Retry-After` interval. - :bug:`4942` -* :doc:`/plugins/scrub`: Fixed the import behavior where scrubbed database tags - were restored to newly imported tracks with config settings ``scrub.auto: yes`` - and ``import.write: no``. - :bug:`4326` -* :doc:`/plugins/deezer`: Fixed the error where Deezer plugin would crash if non-Deezer id is passed during import. -* :doc:`/plugins/fetchart`: Fix fetching from Cover Art Archive when the - `maxwidth` option is set to one of the supported Cover Art Archive widths. -* :doc:`/plugins/discogs`: Fix "Discogs plugin replacing Feat. or Ft. with - a comma" by fixing an oversight that removed a functionality from the code - base when the MetadataSourcePlugin abstract class was introduced in PR's - #3335 and #3371. - :bug:`4401` -* :doc:`/plugins/convert`: Set default ``max_bitrate`` value to ``None`` to +- Improve ListenBrainz error handling. :bug:`5459` +- :doc:`/plugins/deezer`: Improve requests error handling. +- :doc:`/plugins/lastimport`: Improve error handling in the ``process_tracks`` + function and enable it to be used with other plugins. +- :doc:`/plugins/spotify`: Improve handling of ConnectionError. +- :doc:`/plugins/deezer`: Improve Deezer plugin error handling and set requests + timeout to 10 seconds. :bug:`4983` +- :doc:`/plugins/spotify`: Add bad gateway (502) error handling. +- :doc:`/plugins/spotify`: Add a limit of 3 retries, instead of retrying + endlessly when the API is not available. +- Fix a crash when the Spotify API timeouts or does not return a ``Retry-After`` + interval. :bug:`4942` +- :doc:`/plugins/scrub`: Fixed the import behavior where scrubbed database tags + were restored to newly imported tracks with config settings ``scrub.auto: + yes`` and ``import.write: no``. :bug:`4326` +- :doc:`/plugins/deezer`: Fixed the error where Deezer plugin would crash if + non-Deezer id is passed during import. +- :doc:`/plugins/fetchart`: Fix fetching from Cover Art Archive when the + ``maxwidth`` option is set to one of the supported Cover Art Archive widths. +- :doc:`/plugins/discogs`: Fix "Discogs plugin replacing Feat. or Ft. with a + comma" by fixing an oversight that removed a functionality from the code base + when the MetadataSourcePlugin abstract class was introduced in PR's #3335 and + #3371. :bug:`4401` +- :doc:`/plugins/convert`: Set default ``max_bitrate`` value to ``None`` to avoid transcoding when this parameter is not set. :bug:`4472` -* :doc:`/plugins/replaygain`: Avoid a crash when errors occur in the analysis - backend. - :bug:`4506` -* We now use Python's defaults for command-line argument encoding, which - should reduce the chance for errors and "file not found" failures when - invoking other command-line tools, especially on Windows. - :bug:`4507` -* We now respect the Spotify API's rate limiting, which avoids crashing when the API reports code 429 (too many requests). - :bug:`4370` -* Fix implicit paths OR queries (e.g. ``beet list /path/ , /other-path/``) - which have previously been returning the entire library. - :bug:`1865` -* The Discogs release ID is now populated correctly to the discogs_albumid - field again (it was no longer working after Discogs changed their release URL - format). - :bug:`4225` -* The autotagger no longer considers all matches without a MusicBrainz ID as - duplicates of each other. - :bug:`4299` -* :doc:`/plugins/convert`: Resize album art when embedding - :bug:`2116` -* :doc:`/plugins/deezer`: Fix auto tagger pagination issues (fetch beyond the +- :doc:`/plugins/replaygain`: Avoid a crash when errors occur in the analysis + backend. :bug:`4506` +- We now use Python's defaults for command-line argument encoding, which should + reduce the chance for errors and "file not found" failures when invoking other + command-line tools, especially on Windows. :bug:`4507` +- We now respect the Spotify API's rate limiting, which avoids crashing when the + API reports code 429 (too many requests). :bug:`4370` +- Fix implicit paths OR queries (e.g. ``beet list /path/ , /other-path/``) which + have previously been returning the entire library. :bug:`1865` +- The Discogs release ID is now populated correctly to the discogs_albumid field + again (it was no longer working after Discogs changed their release URL + format). :bug:`4225` +- The autotagger no longer considers all matches without a MusicBrainz ID as + duplicates of each other. :bug:`4299` +- :doc:`/plugins/convert`: Resize album art when embedding :bug:`2116` +- :doc:`/plugins/deezer`: Fix auto tagger pagination issues (fetch beyond the first 25 tracks of a release). -* :doc:`/plugins/spotify`: Fix auto tagger pagination issues (fetch beyond the +- :doc:`/plugins/spotify`: Fix auto tagger pagination issues (fetch beyond the first 50 tracks of a release). -* :doc:`/plugins/lyrics`: Fix Genius search by using query params instead of body. -* :doc:`/plugins/unimported`: The new ``ignore_subdirectories`` configuration +- :doc:`/plugins/lyrics`: Fix Genius search by using query params instead of + body. +- :doc:`/plugins/unimported`: The new ``ignore_subdirectories`` configuration option added in 1.6.0 now has a default value if it hasn't been set. -* :doc:`/plugins/deezer`: Tolerate missing fields when searching for singleton - tracks. - :bug:`4116` -* :doc:`/plugins/replaygain`: The type of the internal ``r128_track_gain`` and +- :doc:`/plugins/deezer`: Tolerate missing fields when searching for singleton + tracks. :bug:`4116` +- :doc:`/plugins/replaygain`: The type of the internal ``r128_track_gain`` and ``r128_album_gain`` fields was changed from integer to float to fix loss of - precision due to truncation. - :bug:`4169` -* Fix a regression in the previous release that caused a `TypeError` when - moving files across filesystems. - :bug:`4168` -* :doc:`/plugins/convert`: Deleting the original files during conversion no + precision due to truncation. :bug:`4169` +- Fix a regression in the previous release that caused a ``TypeError`` when + moving files across filesystems. :bug:`4168` +- :doc:`/plugins/convert`: Deleting the original files during conversion no longer logs output when the ``quiet`` flag is enabled. -* :doc:`plugins/web`: Fix handling of "query" requests. Previously queries +- :doc:`plugins/web`: Fix handling of "query" requests. Previously queries consisting of more than one token (separated by a slash) always returned an empty result. -* :doc:`/plugins/discogs`: Skip Discogs query on insufficiently tagged files +- :doc:`/plugins/discogs`: Skip Discogs query on insufficiently tagged files (artist and album tags missing) to prevent arbitrary candidate results. :bug:`4227` -* :doc:`plugins/lyrics`: Fixed issues with the Tekstowo.pl and Genius - backends where some non-lyrics content got included in the lyrics -* :doc:`plugins/limit`: Better header formatting to improve index -* :doc:`plugins/replaygain`: Correctly handle the ``overwrite`` config option, - which forces recomputing ReplayGain values on import even for tracks - that already have the tags. -* :doc:`plugins/embedart`: Fix a crash when using recent versions of - ImageMagick and the ``compare_threshold`` option. - :bug:`4272` -* :doc:`plugins/lyrics`: Fixed issue with Genius header being included in lyrics, - added test case of up-to-date Genius html -* :doc:`plugins/importadded`: Fix a bug with recently added reflink import option - that causes a crash when ImportAdded plugin enabled. - :bug:`4389` -* :doc:`plugins/convert`: Fix a bug with the `wma` format alias. -* :doc:`/plugins/web`: Fix get file from item. -* :doc:`/plugins/lastgenre`: Fix a duplicated entry for trip hop in the - default genre list. - :bug:`4510` -* :doc:`plugins/lyrics`: Fixed issue with Tekstowo backend not actually checking - if the found song matches. - :bug:`4406` -* :doc:`plugins/embedart`: Add support for ImageMagick 7.1.1-12 - :bug:`4836` -* :doc:`/plugins/fromfilename`: Fix failed detection of - filename patterns. - :bug:`4561` :bug:`4600` -* Fix issue where deletion of flexible fields on an album doesn't cascade to items - :bug:`4662` -* Fix issue where ``beet write`` continuously retags the ``albumtypes`` metadata +- :doc:`plugins/lyrics`: Fixed issues with the Tekstowo.pl and Genius backends + where some non-lyrics content got included in the lyrics +- :doc:`plugins/limit`: Better header formatting to improve index +- :doc:`plugins/replaygain`: Correctly handle the ``overwrite`` config option, + which forces recomputing ReplayGain values on import even for tracks that + already have the tags. +- :doc:`plugins/embedart`: Fix a crash when using recent versions of ImageMagick + and the ``compare_threshold`` option. :bug:`4272` +- :doc:`plugins/lyrics`: Fixed issue with Genius header being included in + lyrics, added test case of up-to-date Genius html +- :doc:`plugins/importadded`: Fix a bug with recently added reflink import + option that causes a crash when ImportAdded plugin enabled. :bug:`4389` +- :doc:`plugins/convert`: Fix a bug with the ``wma`` format alias. +- :doc:`/plugins/web`: Fix get file from item. +- :doc:`/plugins/lastgenre`: Fix a duplicated entry for trip hop in the default + genre list. :bug:`4510` +- :doc:`plugins/lyrics`: Fixed issue with Tekstowo backend not actually checking + if the found song matches. :bug:`4406` +- :doc:`plugins/embedart`: Add support for ImageMagick 7.1.1-12 :bug:`4836` +- :doc:`/plugins/fromfilename`: Fix failed detection of <track> <title> filename + patterns. :bug:`4561` :bug:`4600` +- Fix issue where deletion of flexible fields on an album doesn't cascade to + items :bug:`4662` +- Fix issue where ``beet write`` continuously retags the ``albumtypes`` metadata field in files. Additionally broken data could have been added to the library when the tag was read from file back into the library using ``beet update``. It is required for all users to **check if such broken data is present in the library**. Following the instructions `described here <https://github.com/beetbox/beets/pull/4582#issuecomment-1445023493>`_, a sanity check and potential fix is easily possible. :bug:`4528` -* Fix updating "data_source" on re-imports and improve logging when flexible - attributes are being re-imported. - :bug:`4726` -* :doc:`/plugins/fetchart`: Correctly select the cover art from fanart.tv with +- Fix updating "data_source" on re-imports and improve logging when flexible + attributes are being re-imported. :bug:`4726` +- :doc:`/plugins/fetchart`: Correctly select the cover art from fanart.tv with the highest number of likes -* :doc:`/plugins/lyrics`: Fix a crash with the Google backend when processing +- :doc:`/plugins/lyrics`: Fix a crash with the Google backend when processing some web pages. :bug:`4875` -* Modifying flexible attributes of albums now cascade to the individual album +- Modifying flexible attributes of albums now cascade to the individual album tracks, similar to how fixed album attributes have been cascading to tracks - already. A new option ``--noinherit/-I`` to :ref:`modify <modify-cmd>` - allows changing this behaviour. - :bug:`4822` -* Fix bug where an interrupted import process poisons the database, causing - a null path that can't be removed. - :bug:`4906` -* :doc:`/plugins/discogs`: Fix bug where empty artist and title fields would - return None instead of an empty list. - :bug:`4973` -* Fix bug regarding displaying tracks that have been changed not being - displayed unless the detail configuration is enabled. -* :doc:`/plugins/web`: Fix range request support, allowing to play large audio/ + already. A new option ``--noinherit/-I`` to :ref:`modify <modify-cmd>` allows + changing this behaviour. :bug:`4822` +- Fix bug where an interrupted import process poisons the database, causing a + null path that can't be removed. :bug:`4906` +- :doc:`/plugins/discogs`: Fix bug where empty artist and title fields would + return None instead of an empty list. :bug:`4973` +- Fix bug regarding displaying tracks that have been changed not being displayed + unless the detail configuration is enabled. +- :doc:`/plugins/web`: Fix range request support, allowing to play large audio/ opus files using e.g. a browser/firefox or gstreamer/mopidy directly. -* Fix bug where `zsh` completion script made assumptions about the specific - variant of `awk` installed and required specific settings for `sqlite3` - and caching in `zsh`. - :bug:`3546` -* Remove unused functions :bug:`5103` -* Fix bug where all media types are reported as the first media type when - importing with MusicBrainz as the data source - :bug:`4947` -* Fix bug where unimported plugin would not ignore children directories of - ignored directories. - :bug:`5130` -* Fix bug where some plugin commands hang indefinitely due to a missing - `requests` timeout. -* Fix cover art resizing logic to support multiple steps of resizing - :bug:`5151` -* :doc:`/plugins/convert`: Fix attempt to convert and perform side-effects if +- Fix bug where ``zsh`` completion script made assumptions about the specific + variant of ``awk`` installed and required specific settings for ``sqlite3`` + and caching in ``zsh``. :bug:`3546` +- Remove unused functions :bug:`5103` +- Fix bug where all media types are reported as the first media type when + importing with MusicBrainz as the data source :bug:`4947` +- Fix bug where unimported plugin would not ignore children directories of + ignored directories. :bug:`5130` +- Fix bug where some plugin commands hang indefinitely due to a missing + ``requests`` timeout. +- Fix cover art resizing logic to support multiple steps of resizing :bug:`5151` +- :doc:`/plugins/convert`: Fix attempt to convert and perform side-effects if library file is not readable. For plugin developers: -* beets now explicitly prevents multiple plugins to define replacement - functions for the same field. When previously defining `template_fields` - for the same field in two plugins, the last loaded plugin would silently - overwrite the function defined by the other plugin. - Now, beets will raise an exception when this happens. - :bug:`5002` -* Allow reuse of some parts of beets' testing components. This may ease the - work for externally developed plugins or related software (e.g. the beets - plugin for Mopidy), if they need to create an in-memory instance of a beets - music library for their tests. +- beets now explicitly prevents multiple plugins to define replacement functions + for the same field. When previously defining ``template_fields`` for the same + field in two plugins, the last loaded plugin would silently overwrite the + function defined by the other plugin. Now, beets will raise an exception when + this happens. :bug:`5002` +- Allow reuse of some parts of beets' testing components. This may ease the work + for externally developed plugins or related software (e.g. the beets plugin + for Mopidy), if they need to create an in-memory instance of a beets music + library for their tests. For packagers: -* As noted above, the minimum Python version is now 3.7. -* We fixed a version for the dependency on the `Confuse`_ library. - :bug:`4167` -* The minimum required version of :pypi:`mediafile` is now 0.9.0. +- As noted above, the minimum Python version is now 3.7. +- We fixed a version for the dependency on the Confuse_ library. :bug:`4167` +- The minimum required version of :pypi:`mediafile` is now 0.9.0. Other changes: -* Add ``sphinx`` and ``sphinx_rtd_theme`` as dependencies for a new ``docs`` extra - :bug:`4643` -* :doc:`/plugins/absubmit`: Deprecate the ``absubmit`` plugin since - AcousticBrainz has stopped accepting new submissions. - :bug:`4627` -* :doc:`/plugins/acousticbrainz`: Deprecate the ``acousticbrainz`` plugin - since the AcousticBrainz project has shut down. - :bug:`4627` -* :doc:`/plugins/limit`: Limit query results to head or tail (``lslimit`` +- Add ``sphinx`` and ``sphinx_rtd_theme`` as dependencies for a new ``docs`` + extra :bug:`4643` +- :doc:`/plugins/absubmit`: Deprecate the ``absubmit`` plugin since + AcousticBrainz has stopped accepting new submissions. :bug:`4627` +- :doc:`/plugins/acousticbrainz`: Deprecate the ``acousticbrainz`` plugin since + the AcousticBrainz project has shut down. :bug:`4627` +- :doc:`/plugins/limit`: Limit query results to head or tail (``lslimit`` command only) -* :doc:`/plugins/fish`: Add ``--output`` option. -* :doc:`/plugins/lyrics`: Remove Musixmatch from default enabled sources as - they are currently blocking requests from the beets user agent. - :bug:`4585` -* :doc:`/faq`: :ref:`multidisc`: Elaborated the multi-disc FAQ :bug:`4806` -* :doc:`/faq`: :ref:`src`: Removed some long lines. -* Refactor the test cases to avoid test smells. +- :doc:`/plugins/fish`: Add ``--output`` option. +- :doc:`/plugins/lyrics`: Remove Musixmatch from default enabled sources as they + are currently blocking requests from the beets user agent. :bug:`4585` +- :doc:`/faq`: :ref:`multidisc`: Elaborated the multi-disc FAQ :bug:`4806` +- :doc:`/faq`: :ref:`src`: Removed some long lines. +- Refactor the test cases to avoid test smells. 1.6.0 (November 27, 2021) ------------------------- -This release is our first experiment with time-based releases! We are aiming -to publish a new release of beets every 3 months. We therefore have a healthy -but not dizzyingly long list of new features and fixes. +This release is our first experiment with time-based releases! We are aiming to +publish a new release of beets every 3 months. We therefore have a healthy but +not dizzyingly long list of new features and fixes. With this release, beets now requires Python 3.6 or later (it removes support for Python 2.7, 3.4, and 3.5). There are also a few other dependency -changes---if you're a maintainer of a beets package for a package manager, -thank you for your ongoing efforts, and please see the list of notes below. +changes---if you're a maintainer of a beets package for a package manager, thank +you for your ongoing efforts, and please see the list of notes below. Major new features: -* When fetching genres from MusicBrainz, we now include genres from the - release group (in addition to the release). We also prioritize genres based - on the number of votes. - Thanks to :user:`aereaux`. -* Primary and secondary release types from MusicBrainz are now stored in a new - ``albumtypes`` field. - Thanks to :user:`edgars-supe`. - :bug:`2200` -* An accompanying new :doc:`/plugins/albumtypes` includes some options for - formatting this new ``albumtypes`` field. - Thanks to :user:`edgars-supe`. -* The :ref:`modify-cmd` and :ref:`import-cmd` can now use - :doc:`/reference/pathformat` formats when setting fields. - For example, you can now do ``beet modify title='$track $title'`` to put - track numbers into songs' titles. - :bug:`488` +- When fetching genres from MusicBrainz, we now include genres from the release + group (in addition to the release). We also prioritize genres based on the + number of votes. Thanks to :user:`aereaux`. +- Primary and secondary release types from MusicBrainz are now stored in a new + ``albumtypes`` field. Thanks to :user:`edgars-supe`. :bug:`2200` +- An accompanying new :doc:`/plugins/albumtypes` includes some options for + formatting this new ``albumtypes`` field. Thanks to :user:`edgars-supe`. +- The :ref:`modify-cmd` and :ref:`import-cmd` can now use + :doc:`/reference/pathformat` formats when setting fields. For example, you can + now do ``beet modify title='$track $title'`` to put track numbers into songs' + titles. :bug:`488` Other new things: -* :doc:`/plugins/permissions`: The plugin now sets cover art permissions to +- :doc:`/plugins/permissions`: The plugin now sets cover art permissions to match the audio file permissions. -* :doc:`/plugins/unimported`: A new configuration option supports excluding +- :doc:`/plugins/unimported`: A new configuration option supports excluding specific subdirectories in library. -* :doc:`/plugins/info`: Add support for an ``--album`` flag. -* :doc:`/plugins/export`: Similarly add support for an ``--album`` flag. -* ``beet move`` now highlights path differences in color (when enabled). -* When moving files and a direct rename of a file is not possible (for - example, when crossing filesystems), beets now copies to a temporary file in - the target folder first and then moves to the destination instead of - directly copying the target path. This gets us closer to always updating - files atomically. - Thanks to :user:`catap`. - :bug:`4060` -* :doc:`/plugins/fetchart`: Add a new option to store cover art as - non-progressive image. This is useful for DAPs that do not support - progressive images. Set ``deinterlace: yes`` in your configuration to enable - this conversion. -* :doc:`/plugins/fetchart`: Add a new option to change the file format of - cover art images. This may also be useful for DAPs that only support some - image formats. -* Support flexible attributes in ``%aunique``. - :bug:`2678` :bug:`3553` -* Make ``%aunique`` faster, especially when using inline fields. - :bug:`4145` +- :doc:`/plugins/info`: Add support for an ``--album`` flag. +- :doc:`/plugins/export`: Similarly add support for an ``--album`` flag. +- ``beet move`` now highlights path differences in color (when enabled). +- When moving files and a direct rename of a file is not possible (for example, + when crossing filesystems), beets now copies to a temporary file in the target + folder first and then moves to the destination instead of directly copying the + target path. This gets us closer to always updating files atomically. Thanks + to :user:`catap`. :bug:`4060` +- :doc:`/plugins/fetchart`: Add a new option to store cover art as + non-progressive image. This is useful for DAPs that do not support progressive + images. Set ``deinterlace: yes`` in your configuration to enable this + conversion. +- :doc:`/plugins/fetchart`: Add a new option to change the file format of cover + art images. This may also be useful for DAPs that only support some image + formats. +- Support flexible attributes in ``%aunique``. :bug:`2678` :bug:`3553` +- Make ``%aunique`` faster, especially when using inline fields. :bug:`4145` Bug fixes: -* :doc:`/plugins/lyrics`: Fix a crash when Beautiful Soup is not installed. +- :doc:`/plugins/lyrics`: Fix a crash when Beautiful Soup is not installed. :bug:`4027` -* :doc:`/plugins/discogs`: Support a new Discogs URL format for IDs. - :bug:`4080` -* :doc:`/plugins/discogs`: Remove built-in rate-limiting because the Discogs - Python library we use now has its own rate-limiting. - :bug:`4108` -* :doc:`/plugins/export`: Fix some duplicated output. -* :doc:`/plugins/aura`: Fix a potential security hole when serving image - files. +- :doc:`/plugins/discogs`: Support a new Discogs URL format for IDs. :bug:`4080` +- :doc:`/plugins/discogs`: Remove built-in rate-limiting because the Discogs + Python library we use now has its own rate-limiting. :bug:`4108` +- :doc:`/plugins/export`: Fix some duplicated output. +- :doc:`/plugins/aura`: Fix a potential security hole when serving image files. :bug:`4160` For plugin developers: -* :py:meth:`beets.library.Item.destination` now accepts a `replacements` +- :py:meth:`beets.library.Item.destination` now accepts a ``replacements`` argument to be used in favor of the default. -* The `pluginload` event is now sent after plugin types and queries are +- The ``pluginload`` event is now sent after plugin types and queries are available, not before. -* A new plugin event, `album_removed`, is called when an album is removed from +- A new plugin event, ``album_removed``, is called when an album is removed from the library (even when its file is not deleted from disk). Here are some notes for packagers: -* As noted above, the minimum Python version is now 3.6. -* We fixed a flaky test, named `test_album_art` in the `test_zero.py` file, - that some distributions had disabled. Disabling this test should no longer - be necessary. - :bug:`4037` :bug:`4038` -* This version of beets no longer depends on the `six`_ library. - :bug:`4030` -* The `gmusic` plugin was removed since Google Play Music has been shut down. - Thus, the optional dependency on `gmusicapi` does not exist anymore. +- As noted above, the minimum Python version is now 3.6. +- We fixed a flaky test, named ``test_album_art`` in the ``test_zero.py`` file, + that some distributions had disabled. Disabling this test should no longer be + necessary. :bug:`4037` :bug:`4038` +- This version of beets no longer depends on the six_ library. :bug:`4030` +- The ``gmusic`` plugin was removed since Google Play Music has been shut down. + Thus, the optional dependency on ``gmusicapi`` does not exist anymore. :bug:`4089` 1.5.0 (August 19, 2021) ----------------------- This long overdue release of beets includes far too many exciting and useful -features than could ever be satisfactorily enumerated. -As a technical detail, it also introduces two new external libraries: -`MediaFile`_ and `Confuse`_ used to be part of beets but are now reusable -dependencies---packagers, please take note. -Finally, this is the last version of beets where we intend to support Python -2.x and 3.5; future releases will soon require Python 3.6. +features than could ever be satisfactorily enumerated. As a technical detail, it +also introduces two new external libraries: MediaFile_ and Confuse_ used to be +part of beets but are now reusable dependencies---packagers, please take note. +Finally, this is the last version of beets where we intend to support Python 2.x +and 3.5; future releases will soon require Python 3.6. -One non-technical change is that we moved our official ``#beets`` home -on IRC from freenode to `Libera.Chat`_. +One non-technical change is that we moved our official ``#beets`` home on IRC +from freenode to Libera.Chat_. -.. _Libera.Chat: https://libera.chat/ +.. _libera.chat: https://libera.chat/ Major new features: -* Fields in queries now fall back to an item's album and check its fields too. +- Fields in queries now fall back to an item's album and check its fields too. Notably, this allows querying items by an album's attribute: in other words, - ``beet list foo:bar`` will not only find tracks with the `foo` attribute; it - will also find tracks *on albums* that have the `foo` attribute. This may be - particularly useful in the :ref:`path-format-config`, which matches - individual items to decide which path to use. - Thanks to :user:`FichteFoll`. - :bug:`2797` :bug:`2988` -* A new :ref:`reflink` config option instructs the importer to create fast, + ``beet list foo:bar`` will not only find tracks with the ``foo`` attribute; it + will also find tracks *on albums* that have the ``foo`` attribute. This may be + particularly useful in the :ref:`path-format-config`, which matches individual + items to decide which path to use. Thanks to :user:`FichteFoll`. :bug:`2797` + :bug:`2988` +- A new :ref:`reflink` config option instructs the importer to create fast, copy-on-write file clones on filesystems that support them. Thanks to :user:`rubdos`. -* A new :doc:`/plugins/unimported` lets you find untracked files in your - library directory. -* The :doc:`/plugins/aura` has arrived! Try out the future of remote music +- A new :doc:`/plugins/unimported` lets you find untracked files in your library + directory. +- The :doc:`/plugins/aura` has arrived! Try out the future of remote music library access today. -* We now fetch information about `works`_ from MusicBrainz. - MusicBrainz matches provide the fields ``work`` (the title), ``mb_workid`` - (the MBID), and ``work_disambig`` (the disambiguation string). - Thanks to :user:`dosoe`. +- We now fetch information about works_ from MusicBrainz. MusicBrainz matches + provide the fields ``work`` (the title), ``mb_workid`` (the MBID), and + ``work_disambig`` (the disambiguation string). Thanks to :user:`dosoe`. :bug:`2580` :bug:`3272` -* A new :doc:`/plugins/parentwork` gets information about the original work, - which is useful for classical music. - Thanks to :user:`dosoe`. - :bug:`2580` :bug:`3279` -* :doc:`/plugins/bpd`: BPD now supports most of the features of version 0.16 - of the MPD protocol. This is enough to get it talking to more complicated - clients like ncmpcpp, but there are still some incompatibilities, largely due - to MPD commands we don't support yet. (Let us know if you find an MPD client - that doesn't get along with BPD!) - :bug:`3214` :bug:`800` -* A new :doc:`/plugins/deezer` can autotag tracks and albums using the - `Deezer`_ database. - Thanks to :user:`rhlahuja`. - :bug:`3355` -* A new :doc:`/plugins/bareasc` provides a new query type: "bare ASCII" - queries that ignore accented characters, treating them as though they - were plain ASCII characters. Use the ``#`` prefix with :ref:`list-cmd` or - other commands. :bug:`3882` -* :doc:`/plugins/fetchart`: The plugin can now get album art from `last.fm`_. +- A new :doc:`/plugins/parentwork` gets information about the original work, + which is useful for classical music. Thanks to :user:`dosoe`. :bug:`2580` + :bug:`3279` +- :doc:`/plugins/bpd`: BPD now supports most of the features of version 0.16 of + the MPD protocol. This is enough to get it talking to more complicated clients + like ncmpcpp, but there are still some incompatibilities, largely due to MPD + commands we don't support yet. (Let us know if you find an MPD client that + doesn't get along with BPD!) :bug:`3214` :bug:`800` +- A new :doc:`/plugins/deezer` can autotag tracks and albums using the Deezer_ + database. Thanks to :user:`rhlahuja`. :bug:`3355` +- A new :doc:`/plugins/bareasc` provides a new query type: "bare ASCII" queries + that ignore accented characters, treating them as though they were plain ASCII + characters. Use the ``#`` prefix with :ref:`list-cmd` or other commands. + :bug:`3882` +- :doc:`/plugins/fetchart`: The plugin can now get album art from last.fm_. :bug:`3530` -* :doc:`/plugins/web`: The API now supports the HTTP `DELETE` and `PATCH` - methods for modifying items. - They are disabled by default; set ``readonly: no`` in your configuration - file to enable modification via the API. +- :doc:`/plugins/web`: The API now supports the HTTP ``DELETE`` and ``PATCH`` + methods for modifying items. They are disabled by default; set ``readonly: + no`` in your configuration file to enable modification via the API. :bug:`3870` Other new things: -* ``beet remove`` now also allows interactive selection of items from the query, +- ``beet remove`` now also allows interactive selection of items from the query, similar to ``beet modify``. -* Enable HTTPS for MusicBrainz by default and add configuration option - `https` for custom servers. See :ref:`musicbrainz-config` for more details. -* :doc:`/plugins/mpdstats`: Add a new `strip_path` option to help build the +- Enable HTTPS for MusicBrainz by default and add configuration option ``https`` + for custom servers. See :ref:`musicbrainz-config` for more details. +- :doc:`/plugins/mpdstats`: Add a new ``strip_path`` option to help build the right local path from MPD information. -* :doc:`/plugins/convert`: Conversion can now parallelize conversion jobs on +- :doc:`/plugins/convert`: Conversion can now parallelize conversion jobs on Python 3. -* :doc:`/plugins/lastgenre`: Add a new `title_case` config option to make +- :doc:`/plugins/lastgenre`: Add a new ``title_case`` config option to make title-case formatting optional. -* There's a new message when running ``beet config`` when there's no available - configuration file. - :bug:`3779` -* When importing a duplicate album, the prompt now says "keep all" instead of +- There's a new message when running ``beet config`` when there's no available + configuration file. :bug:`3779` +- When importing a duplicate album, the prompt now says "keep all" instead of "keep both" to reflect that there may be more than two albums involved. :bug:`3569` -* :doc:`/plugins/chroma`: The plugin now updates file metadata after - generating fingerprints through the `submit` command. -* :doc:`/plugins/lastgenre`: Added more heavy metal genres to the built-in - genre filter lists. -* A new :doc:`/plugins/subsonicplaylist` can import playlists from a Subsonic +- :doc:`/plugins/chroma`: The plugin now updates file metadata after generating + fingerprints through the ``submit`` command. +- :doc:`/plugins/lastgenre`: Added more heavy metal genres to the built-in genre + filter lists. +- A new :doc:`/plugins/subsonicplaylist` can import playlists from a Subsonic server. -* :doc:`/plugins/subsonicupdate`: The plugin now automatically chooses between +- :doc:`/plugins/subsonicupdate`: The plugin now automatically chooses between token- and password-based authentication based on the server version. -* A new :ref:`extra_tags` configuration option lets you use more metadata in +- A new :ref:`extra_tags` configuration option lets you use more metadata in MusicBrainz queries to further narrow the search. -* A new :doc:`/plugins/fish` adds `Fish shell`_ tab autocompletion to beets. -* :doc:`plugins/fetchart` and :doc:`plugins/embedart`: Added a new ``quality`` +- A new :doc:`/plugins/fish` adds `Fish shell`_ tab autocompletion to beets. +- :doc:`plugins/fetchart` and :doc:`plugins/embedart`: Added a new ``quality`` option that controls the quality of the image output when the image is resized. -* :doc:`plugins/keyfinder`: Added support for `keyfinder-cli`_. - Thanks to :user:`BrainDamage`. -* :doc:`plugins/fetchart`: Added a new ``high_resolution`` config option to - allow downloading of higher resolution iTunes artwork (at the expense of - file size). - :bug:`3391` -* :doc:`plugins/discogs`: The plugin applies two new fields: `discogs_labelid` - and `discogs_artistid`. - :bug:`3413` -* :doc:`/plugins/export`: Added a new ``-f`` (``--format``) flag, - which can export your data as JSON, JSON lines, CSV, or XML. - Thanks to :user:`austinmm`. +- :doc:`plugins/keyfinder`: Added support for keyfinder-cli_. Thanks to + :user:`BrainDamage`. +- :doc:`plugins/fetchart`: Added a new ``high_resolution`` config option to + allow downloading of higher resolution iTunes artwork (at the expense of file + size). :bug:`3391` +- :doc:`plugins/discogs`: The plugin applies two new fields: ``discogs_labelid`` + and ``discogs_artistid``. :bug:`3413` +- :doc:`/plugins/export`: Added a new ``-f`` (``--format``) flag, which can + export your data as JSON, JSON lines, CSV, or XML. Thanks to :user:`austinmm`. :bug:`3402` -* :doc:`/plugins/convert`: Added a new ``-l`` (``--link``) flag and ``link`` - option as well as the ``-H`` (``--hardlink``) flag and ``hardlink`` - option, which symlink or hardlink files that do not need to - be converted (instead of copying them). - :bug:`2324` -* :doc:`/plugins/replaygain`: The plugin now supports a ``per_disc`` option - that enables calculation of album ReplayGain on disc level instead of album - level. - Thanks to :user:`samuelnilsson`. - :bug:`293` -* :doc:`/plugins/replaygain`: The new ``ffmpeg`` ReplayGain backend supports - ``R128_`` tags. - :bug:`3056` -* :doc:`plugins/replaygain`: A new ``r128_targetlevel`` configuration option +- :doc:`/plugins/convert`: Added a new ``-l`` (``--link``) flag and ``link`` + option as well as the ``-H`` (``--hardlink``) flag and ``hardlink`` option, + which symlink or hardlink files that do not need to be converted (instead of + copying them). :bug:`2324` +- :doc:`/plugins/replaygain`: The plugin now supports a ``per_disc`` option that + enables calculation of album ReplayGain on disc level instead of album level. + Thanks to :user:`samuelnilsson`. :bug:`293` +- :doc:`/plugins/replaygain`: The new ``ffmpeg`` ReplayGain backend supports + ``R128_`` tags. :bug:`3056` +- :doc:`plugins/replaygain`: A new ``r128_targetlevel`` configuration option defines the reference volume for files using ``R128_`` tags. ``targetlevel`` - only configures the reference volume for ``REPLAYGAIN_`` files. - :bug:`3065` -* :doc:`/plugins/discogs`: The plugin now collects the "style" field. - Thanks to :user:`thedevilisinthedetails`. - :bug:`2579` :bug:`3251` -* :doc:`/plugins/absubmit`: By default, the plugin now avoids re-analyzing - files that already have AcousticBrainz data. - There are new ``force`` and ``pretend`` options to help control this new - behavior. - Thanks to :user:`SusannaMaria`. + only configures the reference volume for ``REPLAYGAIN_`` files. :bug:`3065` +- :doc:`/plugins/discogs`: The plugin now collects the "style" field. Thanks to + :user:`thedevilisinthedetails`. :bug:`2579` :bug:`3251` +- :doc:`/plugins/absubmit`: By default, the plugin now avoids re-analyzing files + that already have AcousticBrainz data. There are new ``force`` and ``pretend`` + options to help control this new behavior. Thanks to :user:`SusannaMaria`. :bug:`3318` -* :doc:`/plugins/discogs`: The plugin now also gets genre information and a - new ``discogs_albumid`` field from the Discogs API. - Thanks to :user:`thedevilisinthedetails`. - :bug:`465` :bug:`3322` -* :doc:`/plugins/acousticbrainz`: The plugin now fetches two more additional - fields: ``moods_mirex`` and ``timbre``. - Thanks to :user:`malcops`. - :bug:`2860` -* :doc:`/plugins/playlist` and :doc:`/plugins/smartplaylist`: A new - ``forward_slash`` config option facilitates compatibility with MPD on - Windows. - Thanks to :user:`MartyLake`. - :bug:`3331` :bug:`3334` -* The `data_source` field, which indicates which metadata source was used +- :doc:`/plugins/discogs`: The plugin now also gets genre information and a new + ``discogs_albumid`` field from the Discogs API. Thanks to + :user:`thedevilisinthedetails`. :bug:`465` :bug:`3322` +- :doc:`/plugins/acousticbrainz`: The plugin now fetches two more additional + fields: ``moods_mirex`` and ``timbre``. Thanks to :user:`malcops`. :bug:`2860` +- :doc:`/plugins/playlist` and :doc:`/plugins/smartplaylist`: A new + ``forward_slash`` config option facilitates compatibility with MPD on Windows. + Thanks to :user:`MartyLake`. :bug:`3331` :bug:`3334` +- The ``data_source`` field, which indicates which metadata source was used during an autotagging import, is now also applied as an album-level flexible - attribute. - :bug:`3350` :bug:`1693` -* :doc:`/plugins/beatport`: The plugin now gets the musical key, BPM, and - genre for each track. - :bug:`2080` -* A new :doc:`/plugins/bpsync` can synchronize metadata changes from the + attribute. :bug:`3350` :bug:`1693` +- :doc:`/plugins/beatport`: The plugin now gets the musical key, BPM, and genre + for each track. :bug:`2080` +- A new :doc:`/plugins/bpsync` can synchronize metadata changes from the Beatport database (like the existing :doc:`/plugins/mbsync` for MusicBrainz). -* :doc:`/plugins/hook`: The plugin now treats non-zero exit codes as errors. +- :doc:`/plugins/hook`: The plugin now treats non-zero exit codes as errors. :bug:`3409` -* :doc:`/plugins/subsonicupdate`: A new ``url`` configuration replaces the - older (and now deprecated) separate ``host``, ``port``, and ``contextpath`` - config options. As a consequence, the plugin can now talk to Subsonic over - HTTPS. - Thanks to :user:`jef`. - :bug:`3449` -* :doc:`/plugins/discogs`: The new ``index_tracks`` option enables - incorporation of work names and intra-work divisions into imported track - titles. - Thanks to :user:`cole-miller`. - :bug:`3459` -* :doc:`/plugins/web`: The query API now interprets backslashes as path - separators to support path queries. - Thanks to :user:`nmeum`. - :bug:`3567` -* ``beet import`` now handles tar archives with bzip2 or gzip compression. +- :doc:`/plugins/subsonicupdate`: A new ``url`` configuration replaces the older + (and now deprecated) separate ``host``, ``port``, and ``contextpath`` config + options. As a consequence, the plugin can now talk to Subsonic over HTTPS. + Thanks to :user:`jef`. :bug:`3449` +- :doc:`/plugins/discogs`: The new ``index_tracks`` option enables incorporation + of work names and intra-work divisions into imported track titles. Thanks to + :user:`cole-miller`. :bug:`3459` +- :doc:`/plugins/web`: The query API now interprets backslashes as path + separators to support path queries. Thanks to :user:`nmeum`. :bug:`3567` +- ``beet import`` now handles tar archives with bzip2 or gzip compression. :bug:`3606` -* ``beet import`` *also* now handles 7z archives, via the `py7zr`_ library. - Thanks to :user:`arogl`. - :bug:`3906` -* :doc:`/plugins/plexupdate`: Added an option to use a secure connection to - Plex server, and to ignore certificate validation errors if necessary. - :bug:`2871` -* :doc:`/plugins/convert`: A new ``delete_originals`` configuration option can - delete the source files after conversion during import. - Thanks to :user:`logan-arens`. - :bug:`2947` -* There is a new ``--plugins`` (or ``-p``) CLI flag to specify a list of - plugins to load. -* A new :ref:`genres` option fetches genre information from MusicBrainz. This +- ``beet import`` *also* now handles 7z archives, via the py7zr_ library. Thanks + to :user:`arogl`. :bug:`3906` +- :doc:`/plugins/plexupdate`: Added an option to use a secure connection to Plex + server, and to ignore certificate validation errors if necessary. :bug:`2871` +- :doc:`/plugins/convert`: A new ``delete_originals`` configuration option can + delete the source files after conversion during import. Thanks to + :user:`logan-arens`. :bug:`2947` +- There is a new ``--plugins`` (or ``-p``) CLI flag to specify a list of plugins + to load. +- A new :ref:`genres` option fetches genre information from MusicBrainz. This functionality depends on functionality that is currently unreleased in the - `python-musicbrainzngs`_ library: see PR `#266 - <https://github.com/alastair/python-musicbrainzngs/pull/266>`_. - Thanks to :user:`aereaux`. -* :doc:`/plugins/replaygain`: Analysis now happens in parallel using the - ``command`` and ``ffmpeg`` backends. - :bug:`3478` -* :doc:`plugins/replaygain`: The bs1770gain backend is removed. - Thanks to :user:`SamuelCook`. -* Added ``trackdisambig`` which stores the recording disambiguation from - MusicBrainz for each track. - :bug:`1904` -* :doc:`plugins/fetchart`: The new ``max_filesize`` configuration sets a - maximum target image file size. -* :doc:`/plugins/badfiles`: Checkers can now run during import with the + python-musicbrainzngs_ library: see PR `#266 + <https://github.com/alastair/python-musicbrainzngs/pull/266>`_. Thanks to + :user:`aereaux`. +- :doc:`/plugins/replaygain`: Analysis now happens in parallel using the + ``command`` and ``ffmpeg`` backends. :bug:`3478` +- :doc:`plugins/replaygain`: The bs1770gain backend is removed. Thanks to + :user:`SamuelCook`. +- Added ``trackdisambig`` which stores the recording disambiguation from + MusicBrainz for each track. :bug:`1904` +- :doc:`plugins/fetchart`: The new ``max_filesize`` configuration sets a maximum + target image file size. +- :doc:`/plugins/badfiles`: Checkers can now run during import with the ``check_on_import`` config option. -* :doc:`/plugins/export`: The plugin is now much faster when using the - `--include-keys` option is used. - Thanks to :user:`ssssam`. -* The importer's :ref:`set_fields` option now saves all updated fields to - on-disk metadata. - :bug:`3925` :bug:`3927` -* We now fetch ISRC identifiers from MusicBrainz. - Thanks to :user:`aereaux`. -* :doc:`/plugins/metasync`: The plugin now also fetches the "Date Added" field - from iTunes databases and stores it in the ``itunes_dateadded`` field. - Thanks to :user:`sandersantema`. -* :doc:`/plugins/lyrics`: Added a new Tekstowo.pl lyrics provider. Thanks to +- :doc:`/plugins/export`: The plugin is now much faster when using the + ``--include-keys`` option is used. Thanks to :user:`ssssam`. +- The importer's :ref:`set_fields` option now saves all updated fields to + on-disk metadata. :bug:`3925` :bug:`3927` +- We now fetch ISRC identifiers from MusicBrainz. Thanks to :user:`aereaux`. +- :doc:`/plugins/metasync`: The plugin now also fetches the "Date Added" field + from iTunes databases and stores it in the ``itunes_dateadded`` field. Thanks + to :user:`sandersantema`. +- :doc:`/plugins/lyrics`: Added a new Tekstowo.pl lyrics provider. Thanks to various people for the implementation and for reporting issues with the - initial version. - :bug:`3344` :bug:`3904` :bug:`3905` :bug:`3994` -* ``beet update`` will now confirm that the user still wants to update if - their library folder cannot be found, preventing the user from accidentally - wiping out their beets database. - Thanks to user: `logan-arens`. - :bug:`1934` + initial version. :bug:`3344` :bug:`3904` :bug:`3905` :bug:`3994` +- ``beet update`` will now confirm that the user still wants to update if their + library folder cannot be found, preventing the user from accidentally wiping + out their beets database. Thanks to user: ``logan-arens``. :bug:`1934` Fixes: -* Adapt to breaking changes in Python's ``ast`` module in Python 3.8. -* :doc:`/plugins/beatport`: Fix the assignment of the `genre` field, and - rename `musical_key` to `initial_key`. - :bug:`3387` -* :doc:`/plugins/lyrics`: Fixed the Musixmatch backend for lyrics pages when - lyrics are divided into multiple elements on the webpage, and when the - lyrics are missing. -* :doc:`/plugins/web`: Allow use of the backslash character in regex queries. +- Adapt to breaking changes in Python's ``ast`` module in Python 3.8. +- :doc:`/plugins/beatport`: Fix the assignment of the ``genre`` field, and + rename ``musical_key`` to ``initial_key``. :bug:`3387` +- :doc:`/plugins/lyrics`: Fixed the Musixmatch backend for lyrics pages when + lyrics are divided into multiple elements on the webpage, and when the lyrics + are missing. +- :doc:`/plugins/web`: Allow use of the backslash character in regex queries. :bug:`3867` -* :doc:`/plugins/web`: Fixed a small bug that caused the album art path to be - redacted even when ``include_paths`` option is set. - :bug:`3866` -* :doc:`/plugins/discogs`: Fixed a bug with the ``index_tracks`` option that +- :doc:`/plugins/web`: Fixed a small bug that caused the album art path to be + redacted even when ``include_paths`` option is set. :bug:`3866` +- :doc:`/plugins/discogs`: Fixed a bug with the ``index_tracks`` option that sometimes caused the index to be discarded. Also, remove the extra semicolon that was added when there is no index track. -* :doc:`/plugins/subsonicupdate`: The API client was using the `POST` method - rather the `GET` method. - Also includes better exception handling, response parsing, and tests. -* :doc:`/plugins/the`: Fixed incorrect regex for "the" that matched any - 3-letter combination of the letters t, h, e. - :bug:`3701` -* :doc:`/plugins/fetchart`: Fixed a bug that caused the plugin to not take +- :doc:`/plugins/subsonicupdate`: The API client was using the ``POST`` method + rather the ``GET`` method. Also includes better exception handling, response + parsing, and tests. +- :doc:`/plugins/the`: Fixed incorrect regex for "the" that matched any 3-letter + combination of the letters t, h, e. :bug:`3701` +- :doc:`/plugins/fetchart`: Fixed a bug that caused the plugin to not take environment variables, such as proxy servers, into account when making - requests. - :bug:`3450` -* :doc:`/plugins/fetchart`: Temporary files for fetched album art that fail + requests. :bug:`3450` +- :doc:`/plugins/fetchart`: Temporary files for fetched album art that fail validation are now removed. -* :doc:`/plugins/inline`: In function-style field definitions that refer to - flexible attributes, values could stick around from one function invocation - to the next. This meant that, when displaying a list of objects, later - objects could seem to reuse values from earlier objects when they were - missing a value for a given field. These values are now properly undefined. - :bug:`2406` -* :doc:`/plugins/bpd`: Seeking by fractions of a second now works as intended, - fixing crashes in MPD clients like mpDris2 on seek. - The ``playlistid`` command now works properly in its zero-argument form. - :bug:`3214` -* :doc:`/plugins/replaygain`: Fix a Python 3 incompatibility in the Python - Audio Tools backend. - :bug:`3305` -* :doc:`/plugins/importadded`: Fixed a crash that occurred when the - ``after_write`` signal was emitted. - :bug:`3301` -* :doc:`plugins/replaygain`: Fix the storage format for R128 gain tags. +- :doc:`/plugins/inline`: In function-style field definitions that refer to + flexible attributes, values could stick around from one function invocation to + the next. This meant that, when displaying a list of objects, later objects + could seem to reuse values from earlier objects when they were missing a value + for a given field. These values are now properly undefined. :bug:`2406` +- :doc:`/plugins/bpd`: Seeking by fractions of a second now works as intended, + fixing crashes in MPD clients like mpDris2 on seek. The ``playlistid`` command + now works properly in its zero-argument form. :bug:`3214` +- :doc:`/plugins/replaygain`: Fix a Python 3 incompatibility in the Python Audio + Tools backend. :bug:`3305` +- :doc:`/plugins/importadded`: Fixed a crash that occurred when the + ``after_write`` signal was emitted. :bug:`3301` +- :doc:`plugins/replaygain`: Fix the storage format for R128 gain tags. :bug:`3311` :bug:`3314` -* :doc:`/plugins/discogs`: Fixed a crash that occurred when the master URI - isn't set in the API response. - :bug:`2965` :bug:`3239` -* :doc:`/plugins/spotify`: Fix handling of year-only release dates - returned by the Spotify albums API. - Thanks to :user:`rhlahuja`. - :bug:`3343` -* Fixed a bug that caused the UI to display incorrect track numbers for tracks - with index 0 when the ``per_disc_numbering`` option was set. - :bug:`3346` -* ``none_rec_action`` does not import automatically when ``timid`` is enabled. - Thanks to :user:`RollingStar`. - :bug:`3242` -* Fix a bug that caused a crash when tagging items with the beatport plugin. +- :doc:`/plugins/discogs`: Fixed a crash that occurred when the master URI isn't + set in the API response. :bug:`2965` :bug:`3239` +- :doc:`/plugins/spotify`: Fix handling of year-only release dates returned by + the Spotify albums API. Thanks to :user:`rhlahuja`. :bug:`3343` +- Fixed a bug that caused the UI to display incorrect track numbers for tracks + with index 0 when the ``per_disc_numbering`` option was set. :bug:`3346` +- ``none_rec_action`` does not import automatically when ``timid`` is enabled. + Thanks to :user:`RollingStar`. :bug:`3242` +- Fix a bug that caused a crash when tagging items with the beatport plugin. :bug:`3374` -* ``beet import`` now logs which files are ignored when in debug mode. +- ``beet import`` now logs which files are ignored when in debug mode. :bug:`3764` -* :doc:`/plugins/bpd`: Fix the transition to next track when in consume mode. - Thanks to :user:`aereaux`. - :bug:`3437` -* :doc:`/plugins/lyrics`: Fix a corner-case with Genius lowercase artist names +- :doc:`/plugins/bpd`: Fix the transition to next track when in consume mode. + Thanks to :user:`aereaux`. :bug:`3437` +- :doc:`/plugins/lyrics`: Fix a corner-case with Genius lowercase artist names :bug:`3446` -* :doc:`/plugins/parentwork`: Don't save tracks when nothing has changed. +- :doc:`/plugins/parentwork`: Don't save tracks when nothing has changed. :bug:`3492` -* Added a warning when configuration files defined in the `include` directive - of the configuration file fail to be imported. - :bug:`3498` -* Added normalization to integer values in the database, which should avoid +- Added a warning when configuration files defined in the ``include`` directive + of the configuration file fail to be imported. :bug:`3498` +- Added normalization to integer values in the database, which should avoid problems where fields like ``bpm`` would sometimes store non-integer values. :bug:`762` :bug:`3507` :bug:`3508` -* Fix a crash when querying for null values. - :bug:`3516` :bug:`3517` -* :doc:`/plugins/lyrics`: Tolerate a missing lyrics div in the Genius scraper. - Thanks to :user:`thejli21`. - :bug:`3535` :bug:`3554` -* :doc:`/plugins/lyrics`: Use the artist sort name to search for lyrics, which - can help find matches when the artist name has special characters. - Thanks to :user:`hashhar`. - :bug:`3340` :bug:`3558` -* :doc:`/plugins/replaygain`: Trying to calculate volume gain for an album - consisting of some formats using ``ReplayGain`` and some using ``R128`` - will no longer crash; instead it is skipped and and a message is logged. - The log message has also been rewritten for to improve clarity. - Thanks to :user:`autrimpo`. - :bug:`3533` -* :doc:`/plugins/lyrics`: Adapt the Genius backend to changes in markup to - reduce the scraping failure rate. - :bug:`3535` :bug:`3594` -* :doc:`/plugins/lyrics`: Fix a crash when writing ReST files for a query - without results or fetched lyrics. - :bug:`2805` -* :doc:`/plugins/fetchart`: Attempt to fetch pre-resized thumbnails from Cover +- Fix a crash when querying for null values. :bug:`3516` :bug:`3517` +- :doc:`/plugins/lyrics`: Tolerate a missing lyrics div in the Genius scraper. + Thanks to :user:`thejli21`. :bug:`3535` :bug:`3554` +- :doc:`/plugins/lyrics`: Use the artist sort name to search for lyrics, which + can help find matches when the artist name has special characters. Thanks to + :user:`hashhar`. :bug:`3340` :bug:`3558` +- :doc:`/plugins/replaygain`: Trying to calculate volume gain for an album + consisting of some formats using ``ReplayGain`` and some using ``R128`` will + no longer crash; instead it is skipped and and a message is logged. The log + message has also been rewritten for to improve clarity. Thanks to + :user:`autrimpo`. :bug:`3533` +- :doc:`/plugins/lyrics`: Adapt the Genius backend to changes in markup to + reduce the scraping failure rate. :bug:`3535` :bug:`3594` +- :doc:`/plugins/lyrics`: Fix a crash when writing ReST files for a query + without results or fetched lyrics. :bug:`2805` +- :doc:`/plugins/fetchart`: Attempt to fetch pre-resized thumbnails from Cover Art Archive if the ``maxwidth`` option matches one of the sizes supported by - the Cover Art Archive API. - Thanks to :user:`trolley`. - :bug:`3637` -* :doc:`/plugins/ipfs`: Fix Python 3 compatibility. - Thanks to :user:`musoke`. + the Cover Art Archive API. Thanks to :user:`trolley`. :bug:`3637` +- :doc:`/plugins/ipfs`: Fix Python 3 compatibility. Thanks to :user:`musoke`. :bug:`2554` -* Fix a bug that caused metadata starting with something resembling a drive +- Fix a bug that caused metadata starting with something resembling a drive letter to be incorrectly split into an extra directory after the colon. :bug:`3685` -* :doc:`/plugins/mpdstats`: Don't record a skip when stopping MPD, as MPD keeps - the current track in the queue. - Thanks to :user:`aereaux`. - :bug:`3722` -* String-typed fields are now normalized to string values, avoiding an +- :doc:`/plugins/mpdstats`: Don't record a skip when stopping MPD, as MPD keeps + the current track in the queue. Thanks to :user:`aereaux`. :bug:`3722` +- String-typed fields are now normalized to string values, avoiding an occasional crash when using both the :doc:`/plugins/fetchart` and the - :doc:`/plugins/discogs` together. - :bug:`3773` :bug:`3774` -* Fix a bug causing PIL to generate poor quality JPEGs when resizing artwork. + :doc:`/plugins/discogs` together. :bug:`3773` :bug:`3774` +- Fix a bug causing PIL to generate poor quality JPEGs when resizing artwork. :bug:`3743` -* :doc:`plugins/keyfinder`: Catch output from ``keyfinder-cli`` that is missing key. - :bug:`2242` -* :doc:`plugins/replaygain`: Disable parallel analysis on import by default. +- :doc:`plugins/keyfinder`: Catch output from ``keyfinder-cli`` that is missing + key. :bug:`2242` +- :doc:`plugins/replaygain`: Disable parallel analysis on import by default. :bug:`3819` -* :doc:`/plugins/mpdstats`: Fix Python 2/3 compatibility - :bug:`3798` -* :doc:`/plugins/discogs`: Replace the deprecated official `discogs-client` - library with the community supported `python3-discogs-client`_ library. +- :doc:`/plugins/mpdstats`: Fix Python 2/3 compatibility :bug:`3798` +- :doc:`/plugins/discogs`: Replace the deprecated official ``discogs-client`` + library with the community supported python3-discogs-client_ library. :bug:`3608` -* :doc:`/plugins/chroma`: Fixed submitting AcoustID information for tracks - that already have a fingerprint. - :bug:`3834` -* Allow equals within the value part of the ``--set`` option to the ``beet - import`` command. - :bug:`2984` -* Duplicates can now generate checksums. Thanks :user:`wisp3rwind` - for the pointer to how to solve. Thanks to :user:`arogl`. - :bug:`2873` -* Templates that use ``%ifdef`` now produce the expected behavior when used in - conjunction with non-string fields from the :doc:`/plugins/types`. - :bug:`3852` -* :doc:`/plugins/lyrics`: Fix crashes when a website could not be retrieved, - affecting at least the Genius source. - :bug:`3970` -* :doc:`/plugins/duplicates`: Fix a crash when running the ``dup`` command with - a query that returns no results. - :bug:`3943` -* :doc:`/plugins/beatport`: Fix the default assignment of the musical key. +- :doc:`/plugins/chroma`: Fixed submitting AcoustID information for tracks that + already have a fingerprint. :bug:`3834` +- Allow equals within the value part of the ``--set`` option to the ``beet + import`` command. :bug:`2984` +- Duplicates can now generate checksums. Thanks :user:`wisp3rwind` for the + pointer to how to solve. Thanks to :user:`arogl`. :bug:`2873` +- Templates that use ``%ifdef`` now produce the expected behavior when used in + conjunction with non-string fields from the :doc:`/plugins/types`. :bug:`3852` +- :doc:`/plugins/lyrics`: Fix crashes when a website could not be retrieved, + affecting at least the Genius source. :bug:`3970` +- :doc:`/plugins/duplicates`: Fix a crash when running the ``dup`` command with + a query that returns no results. :bug:`3943` +- :doc:`/plugins/beatport`: Fix the default assignment of the musical key. :bug:`3377` -* :doc:`/plugins/lyrics`: Improved searching on the Genius backend when the - artist contains special characters. - :bug:`3634` -* :doc:`/plugins/parentwork`: Also get the composition date of the parent work, - instead of just the child work. - Thanks to :user:`aereaux`. - :bug:`3650` -* :doc:`/plugins/lyrics`: Fix a bug in the heuristic for detecting valid - lyrics in the Google source. - :bug:`2969` -* :doc:`/plugins/thumbnails`: Fix a crash due to an incorrect string type on - Python 3. - :bug:`3360` -* :doc:`/plugins/fetchart`: The Cover Art Archive source now iterates over - all front images instead of blindly selecting the first one. -* :doc:`/plugins/lyrics`: Removed the LyricWiki source (the site shut down on +- :doc:`/plugins/lyrics`: Improved searching on the Genius backend when the + artist contains special characters. :bug:`3634` +- :doc:`/plugins/parentwork`: Also get the composition date of the parent work, + instead of just the child work. Thanks to :user:`aereaux`. :bug:`3650` +- :doc:`/plugins/lyrics`: Fix a bug in the heuristic for detecting valid lyrics + in the Google source. :bug:`2969` +- :doc:`/plugins/thumbnails`: Fix a crash due to an incorrect string type on + Python 3. :bug:`3360` +- :doc:`/plugins/fetchart`: The Cover Art Archive source now iterates over all + front images instead of blindly selecting the first one. +- :doc:`/plugins/lyrics`: Removed the LyricWiki source (the site shut down on 21/09/2020). -* :doc:`/plugins/subsonicupdate`: The plugin is now functional again. A new - `auth` configuration option is required in the configuration to specify the - flavor of authentication to use. - :bug:`4002` +- :doc:`/plugins/subsonicupdate`: The plugin is now functional again. A new + ``auth`` configuration option is required in the configuration to specify the + flavor of authentication to use. :bug:`4002` For plugin developers: -* `MediaFile`_ has been split into a standalone project. Where you used to do +- MediaFile_ has been split into a standalone project. Where you used to do ``from beets import mediafile``, now just do ``import mediafile``. Beets re-exports MediaFile at the old location for backwards-compatibility, but a deprecation warning is raised if you do this since we might drop this wrapper in a future release. -* Similarly, we've replaced beets' configuration library (previously called - Confit) with a standalone version called `Confuse`_. Where you used to do - ``from beets.util import confit``, now just do ``import confuse``. The code - is almost identical apart from the name change. Again, we'll re-export at the - old location (with a deprecation warning) for backwards compatibility, but - we might stop doing this in a future release. -* ``beets.util.command_output`` now returns a named tuple containing both the +- Similarly, we've replaced beets' configuration library (previously called + Confit) with a standalone version called Confuse_. Where you used to do ``from + beets.util import confit``, now just do ``import confuse``. The code is almost + identical apart from the name change. Again, we'll re-export at the old + location (with a deprecation warning) for backwards compatibility, but we + might stop doing this in a future release. +- ``beets.util.command_output`` now returns a named tuple containing both the standard output and the standard error data instead of just stdout alone. - Client code will need to access the ``stdout`` attribute on the return - value. - Thanks to :user:`zsinskri`. - :bug:`3329` -* There were sporadic failures in ``test.test_player``. Hopefully these are - fixed. If they resurface, please reopen the relevant issue. - :bug:`3309` :bug:`3330` -* The ``beets.plugins.MetadataSourcePlugin`` base class has been added to - simplify development of plugins which query album, track, and search - APIs to provide metadata matches for the importer. Refer to the + Client code will need to access the ``stdout`` attribute on the return value. + Thanks to :user:`zsinskri`. :bug:`3329` +- There were sporadic failures in ``test.test_player``. Hopefully these are + fixed. If they resurface, please reopen the relevant issue. :bug:`3309` + :bug:`3330` +- The ``beets.plugins.MetadataSourcePlugin`` base class has been added to + simplify development of plugins which query album, track, and search APIs to + provide metadata matches for the importer. Refer to the :doc:`/plugins/spotify` and the :doc:`/plugins/deezer` for examples of using - this template class. - :bug:`3355` -* Accessing fields on an `Item` now falls back to the album's - attributes. So, for example, ``item.foo`` will first look for a field `foo` on - `item` and, if it doesn't exist, next tries looking for a field named `foo` - on the album that contains `item`. If you specifically want to access an - item's attributes, use ``Item.get(key, with_album=False)``. :bug:`2988` -* ``Item.keys`` also has a ``with_album`` argument now, defaulting to ``True``. -* A ``revision`` attribute has been added to ``Database``. It is increased on + this template class. :bug:`3355` +- Accessing fields on an ``Item`` now falls back to the album's attributes. So, + for example, ``item.foo`` will first look for a field ``foo`` on ``item`` and, + if it doesn't exist, next tries looking for a field named ``foo`` on the album + that contains ``item``. If you specifically want to access an item's + attributes, use ``Item.get(key, with_album=False)``. :bug:`2988` +- ``Item.keys`` also has a ``with_album`` argument now, defaulting to ``True``. +- A ``revision`` attribute has been added to ``Database``. It is increased on every transaction that mutates it. :bug:`2988` -* The classes ``AlbumInfo`` and ``TrackInfo`` now convey arbitrary attributes +- The classes ``AlbumInfo`` and ``TrackInfo`` now convey arbitrary attributes instead of a fixed, built-in set of field names (which was important to - address :bug:`1547`). - Thanks to :user:`dosoe`. -* Two new events, ``mb_album_extract`` and ``mb_track_extract``, let plugins - add new fields based on MusicBrainz data. Thanks to :user:`dosoe`. + address :bug:`1547`). Thanks to :user:`dosoe`. +- Two new events, ``mb_album_extract`` and ``mb_track_extract``, let plugins add + new fields based on MusicBrainz data. Thanks to :user:`dosoe`. For packagers: -* Beets' library for manipulating media file metadata has now been split to a - standalone project called `MediaFile`_, released as :pypi:`mediafile`. Beets - now depends on this new package. Beets now depends on Mutagen transitively - through MediaFile rather than directly, except in the case of one of beets' - plugins (in particular, the :doc:`/plugins/scrub`). -* Beets' library for configuration has been split into a standalone project - called `Confuse`_, released as :pypi:`confuse`. Beets now depends on this - package. Confuse has existed separately for some time and is used by - unrelated projects, but until now we've been bundling a copy within beets. -* We attempted to fix an unreliable test, so a patch to `skip <https://sources.debian.org/src/beets/1.4.7-2/debian/patches/skip-broken-test/>`_ - or `repair <https://build.opensuse.org/package/view_file/openSUSE:Factory/beets/fix_test_command_line_option_relative_to_working_dir.diff?expand=1>`_ - the test may no longer be necessary. -* This version drops support for Python 3.4. -* We have removed an optional dependency on bs1770gain. +- Beets' library for manipulating media file metadata has now been split to a + standalone project called MediaFile_, released as :pypi:`mediafile`. Beets now + depends on this new package. Beets now depends on Mutagen transitively through + MediaFile rather than directly, except in the case of one of beets' plugins + (in particular, the :doc:`/plugins/scrub`). +- Beets' library for configuration has been split into a standalone project + called Confuse_, released as :pypi:`confuse`. Beets now depends on this + package. Confuse has existed separately for some time and is used by unrelated + projects, but until now we've been bundling a copy within beets. +- We attempted to fix an unreliable test, so a patch to skip-broken-test_ or + repairing_ may no longer be necessary. +- This version drops support for Python 3.4. +- We have removed an optional dependency on bs1770gain. + +.. _confuse: https://github.com/beetbox/confuse + +.. _deezer: https://www.deezer.com + +.. _fish shell: https://fishshell.com/ -.. _Fish shell: https://fishshell.com/ -.. _MediaFile: https://github.com/beetbox/mediafile -.. _Confuse: https://github.com/beetbox/confuse -.. _works: https://musicbrainz.org/doc/Work -.. _Deezer: https://www.deezer.com .. _keyfinder-cli: https://github.com/EvanPurkhiser/keyfinder-cli + .. _last.fm: https://last.fm -.. _python3-discogs-client: https://github.com/joalla/discogs_client + +.. _mediafile: https://github.com/beetbox/mediafile + .. _py7zr: https://pypi.org/project/py7zr/ +.. _python3-discogs-client: https://github.com/joalla/discogs_client + +.. _repairing: https://build.opensuse.org/package/view_file/openSUSE:Factory/beets/fix_test_command_line_option_relative_to_working_dir.diff?expand=1 + +.. _skip-broken-test: https://sources.debian.org/src/beets/1.4.7-2/debian/patches/skip-broken-test/ + +.. _works: https://musicbrainz.org/doc/Work + 1.4.9 (May 30, 2019) -------------------- @@ -1248,754 +1128,644 @@ beets' dependencies in the next version. The new feature is: -* You can use the `NO_COLOR`_ environment variable to disable terminal colors. +- You can use the NO_COLOR_ environment variable to disable terminal colors. :bug:`3273` There are some fixes in this release: -* Fix a regression in the last release that made the image resizer fail to - detect older versions of ImageMagick. - :bug:`3269` -* :doc:`/plugins/gmusic`: The ``oauth_file`` config option now supports more - flexible path values, including ``~`` for the home directory. - :bug:`3270` -* :doc:`/plugins/gmusic`: Fix a crash when using version 12.0.0 or later of - the ``gmusicapi`` module. - :bug:`3270` -* Fix an incompatibility with Python 3.8's AST changes. - :bug:`3278` +- Fix a regression in the last release that made the image resizer fail to + detect older versions of ImageMagick. :bug:`3269` +- :doc:`/plugins/gmusic`: The ``oauth_file`` config option now supports more + flexible path values, including ``~`` for the home directory. :bug:`3270` +- :doc:`/plugins/gmusic`: Fix a crash when using version 12.0.0 or later of the + ``gmusicapi`` module. :bug:`3270` +- Fix an incompatibility with Python 3.8's AST changes. :bug:`3278` Here's a note for packagers: -* ``pathlib`` is now an optional test dependency on Python 3.4+, removing the - need for `a Debian patch <https://sources.debian.org/src/beets/1.4.7-2/debian/patches/pathlib-is-stdlib/>`_. - :bug:`3275` +- ``pathlib`` is now an optional test dependency on Python 3.4+, removing the + need for `Debian pathlib patch`_ :bug:`3275` -.. _NO_COLOR: https://no-color.org +.. _debian pathlib patch: https://sources.debian.org/src/beets/1.4.7-2/debian/patches/pathlib-is-stdlib/ + +.. _no_color: https://no-color.org 1.4.8 (May 16, 2019) -------------------- -This release is far too long in coming, but it's a good one. There is the -usual torrent of new features and a ridiculously long line of fixes, but there -are also some crucial maintenance changes. -We officially support Python 3.7 and 3.8, and some performance optimizations -can (anecdotally) make listing your library more than three times faster than -in the previous version. +This release is far too long in coming, but it's a good one. There is the usual +torrent of new features and a ridiculously long line of fixes, but there are +also some crucial maintenance changes. We officially support Python 3.7 and 3.8, +and some performance optimizations can (anecdotally) make listing your library +more than three times faster than in the previous version. The new core features are: -* A new :ref:`config-aunique` configuration option allows setting default +- A new :ref:`config-aunique` configuration option allows setting default options for the :ref:`aunique` template function. -* The ``albumdisambig`` field no longer includes the MusicBrainz release group +- The ``albumdisambig`` field no longer includes the MusicBrainz release group disambiguation comment. A new ``releasegroupdisambig`` field has been added. :bug:`3024` -* The :ref:`modify-cmd` command now allows resetting fixed attributes. For +- The :ref:`modify-cmd` command now allows resetting fixed attributes. For example, ``beet modify -a artist:beatles artpath!`` resets ``artpath`` - attribute from matching albums back to the default value. - :bug:`2497` -* A new importer option, :ref:`ignore_data_tracks`, lets you skip audio tracks + attribute from matching albums back to the default value. :bug:`2497` +- A new importer option, :ref:`ignore_data_tracks`, lets you skip audio tracks contained in data files. :bug:`3021` There are some new plugins: -* The :doc:`/plugins/playlist` can query the beets library using M3U playlists. - Thanks to :user:`Holzhaus` and :user:`Xenopathic`. - :bug:`123` :bug:`3145` -* The :doc:`/plugins/loadext` allows loading of SQLite extensions, primarily - for use with the ICU SQLite extension for internationalization. - :bug:`3160` :bug:`3226` -* The :doc:`/plugins/subsonicupdate` can automatically update your Subsonic - library. - Thanks to :user:`maffo999`. - :bug:`3001` +- The :doc:`/plugins/playlist` can query the beets library using M3U playlists. + Thanks to :user:`Holzhaus` and :user:`Xenopathic`. :bug:`123` :bug:`3145` +- The :doc:`/plugins/loadext` allows loading of SQLite extensions, primarily for + use with the ICU SQLite extension for internationalization. :bug:`3160` + :bug:`3226` +- The :doc:`/plugins/subsonicupdate` can automatically update your Subsonic + library. Thanks to :user:`maffo999`. :bug:`3001` And many improvements to existing plugins: -* :doc:`/plugins/lastgenre`: Added option ``-A`` to match individual tracks - and singletons. - :bug:`3220` :bug:`3219` -* :doc:`/plugins/play`: The plugin can now emit a UTF-8 BOM, fixing some - issues with foobar2000 and Winamp. - Thanks to :user:`mz2212`. - :bug:`2944` -* :doc:`/plugins/gmusic`: - * Add a new option to automatically upload to Google Play Music library on - track import. - Thanks to :user:`shuaiscott`. - * Add new options for Google Play Music authentication. - Thanks to :user:`thetarkus`. - :bug:`3002` -* :doc:`/plugins/replaygain`: ``albumpeak`` on large collections is calculated - as the average, not the maximum. - :bug:`3008` :bug:`3009` -* :doc:`/plugins/chroma`: - * Now optionally has a bias toward looking up more relevant releases - according to the :ref:`preferred` configuration options. - Thanks to :user:`archer4499`. - :bug:`3017` - * Fingerprint values are now properly stored as strings, which prevents - strange repeated output when running ``beet write``. - Thanks to :user:`Holzhaus`. - :bug:`3097` :bug:`2942` -* :doc:`/plugins/convert`: The plugin now has an ``id3v23`` option that allows - you to override the global ``id3v23`` option. - Thanks to :user:`Holzhaus`. - :bug:`3104` -* :doc:`/plugins/spotify`: - * The plugin now uses OAuth for authentication to the Spotify API. - Thanks to :user:`rhlahuja`. - :bug:`2694` :bug:`3123` - * The plugin now works as an import metadata - provider: you can match tracks and albums using the Spotify database. - Thanks to :user:`rhlahuja`. - :bug:`3123` -* :doc:`/plugins/ipfs`: The plugin now supports a ``nocopy`` option which - passes that flag to ipfs. - Thanks to :user:`wildthyme`. -* :doc:`/plugins/discogs`: The plugin now has rate limiting for the Discogs API. - :bug:`3081` -* :doc:`/plugins/mpdstats`, :doc:`/plugins/mpdupdate`: These plugins now use - the ``MPD_PORT`` environment variable if no port is specified in the - configuration file. - :bug:`3223` -* :doc:`/plugins/bpd`: - * MPD protocol commands ``consume`` and ``single`` are now supported along - with updated semantics for ``repeat`` and ``previous`` and new fields for - ``status``. The bpd server now understands and ignores some additional - commands. - :bug:`3200` :bug:`800` - * MPD protocol command ``idle`` is now supported, allowing the MPD version - to be bumped to 0.14. - :bug:`3205` :bug:`800` - * MPD protocol command ``decoders`` is now supported. - :bug:`3222` - * The plugin now uses the main beets logging system. - The special-purpose ``--debug`` flag has been removed. - Thanks to :user:`arcresu`. - :bug:`3196` -* :doc:`/plugins/mbsync`: The plugin no longer queries MusicBrainz when either - the ``mb_albumid`` or ``mb_trackid`` field is invalid. - See also the discussion on `Google Groups`_ - Thanks to :user:`arogl`. -* :doc:`/plugins/export`: The plugin now also exports ``path`` field if the user - explicitly specifies it with ``-i`` parameter. This only works when exporting - library fields. - :bug:`3084` -* :doc:`/plugins/acousticbrainz`: The plugin now declares types for all its - fields, which enables easier querying and avoids a problem where very small - numbers would be stored as strings. - Thanks to :user:`rain0r`. - :bug:`2790` :bug:`3238` +- :doc:`/plugins/lastgenre`: Added option ``-A`` to match individual tracks and + singletons. :bug:`3220` :bug:`3219` +- :doc:`/plugins/play`: The plugin can now emit a UTF-8 BOM, fixing some issues + with foobar2000 and Winamp. Thanks to :user:`mz2212`. :bug:`2944` +- :doc:`/plugins/gmusic`: -.. _Google Groups: https://groups.google.com/forum/#!searchin/beets-users/mbsync|sort:date/beets-users/iwCF6bNdh9A/i1xl4Gx8BQAJ + - Add a new option to automatically upload to Google Play Music library on + track import. Thanks to :user:`shuaiscott`. + - Add new options for Google Play Music authentication. Thanks to + :user:`thetarkus`. :bug:`3002` + +- :doc:`/plugins/replaygain`: ``albumpeak`` on large collections is calculated + as the average, not the maximum. :bug:`3008` :bug:`3009` +- :doc:`/plugins/chroma`: + + - Now optionally has a bias toward looking up more relevant releases according + to the :ref:`preferred` configuration options. Thanks to :user:`archer4499`. + :bug:`3017` + - Fingerprint values are now properly stored as strings, which prevents + strange repeated output when running ``beet write``. Thanks to + :user:`Holzhaus`. :bug:`3097` :bug:`2942` + +- :doc:`/plugins/convert`: The plugin now has an ``id3v23`` option that allows + you to override the global ``id3v23`` option. Thanks to :user:`Holzhaus`. + :bug:`3104` +- :doc:`/plugins/spotify`: + + - The plugin now uses OAuth for authentication to the Spotify API. Thanks to + :user:`rhlahuja`. :bug:`2694` :bug:`3123` + - The plugin now works as an import metadata provider: you can match tracks + and albums using the Spotify database. Thanks to :user:`rhlahuja`. + :bug:`3123` + +- :doc:`/plugins/ipfs`: The plugin now supports a ``nocopy`` option which passes + that flag to ipfs. Thanks to :user:`wildthyme`. +- :doc:`/plugins/discogs`: The plugin now has rate limiting for the Discogs API. + :bug:`3081` +- :doc:`/plugins/mpdstats`, :doc:`/plugins/mpdupdate`: These plugins now use the + ``MPD_PORT`` environment variable if no port is specified in the configuration + file. :bug:`3223` +- :doc:`/plugins/bpd`: + + - MPD protocol commands ``consume`` and ``single`` are now supported along + with updated semantics for ``repeat`` and ``previous`` and new fields for + ``status``. The bpd server now understands and ignores some additional + commands. :bug:`3200` :bug:`800` + - MPD protocol command ``idle`` is now supported, allowing the MPD version to + be bumped to 0.14. :bug:`3205` :bug:`800` + - MPD protocol command ``decoders`` is now supported. :bug:`3222` + - The plugin now uses the main beets logging system. The special-purpose + ``--debug`` flag has been removed. Thanks to :user:`arcresu`. :bug:`3196` + +- :doc:`/plugins/mbsync`: The plugin no longer queries MusicBrainz when either + the ``mb_albumid`` or ``mb_trackid`` field is invalid. See also the discussion + on `Google Groups`_ Thanks to :user:`arogl`. +- :doc:`/plugins/export`: The plugin now also exports ``path`` field if the user + explicitly specifies it with ``-i`` parameter. This only works when exporting + library fields. :bug:`3084` +- :doc:`/plugins/acousticbrainz`: The plugin now declares types for all its + fields, which enables easier querying and avoids a problem where very small + numbers would be stored as strings. Thanks to :user:`rain0r`. :bug:`2790` + :bug:`3238` + +.. _google groups: https://groups.google.com/forum/#!searchin/beets-users/mbsync|sort:date/beets-users/iwCF6bNdh9A/i1xl4Gx8BQAJ Some improvements have been focused on improving beets' performance: -* Querying the library is now faster: - * We only convert fields that need to be displayed. - Thanks to :user:`pprkut`. - :bug:`3089` - * We now compile templates once and reuse them instead of recompiling them - to print out each matching object. - Thanks to :user:`SimonPersson`. - :bug:`3258` - * Querying the library for items is now faster, for all queries that do not - need to access album level properties. This was implemented by lazily - fetching the album only when needed. - Thanks to :user:`SimonPersson`. - :bug:`3260` -* :doc:`/plugins/absubmit`, :doc:`/plugins/badfiles`: Analysis now works in - parallel (on Python 3 only). - Thanks to :user:`bemeurer`. - :bug:`2442` :bug:`3003` -* :doc:`/plugins/mpdstats`: Use the ``currentsong`` MPD command instead of +- Querying the library is now faster: + + - We only convert fields that need to be displayed. Thanks to :user:`pprkut`. + :bug:`3089` + - We now compile templates once and reuse them instead of recompiling them to + print out each matching object. Thanks to :user:`SimonPersson`. :bug:`3258` + - Querying the library for items is now faster, for all queries that do not + need to access album level properties. This was implemented by lazily + fetching the album only when needed. Thanks to :user:`SimonPersson`. + :bug:`3260` + +- :doc:`/plugins/absubmit`, :doc:`/plugins/badfiles`: Analysis now works in + parallel (on Python 3 only). Thanks to :user:`bemeurer`. :bug:`2442` + :bug:`3003` +- :doc:`/plugins/mpdstats`: Use the ``currentsong`` MPD command instead of ``playlist`` to get the current song, improving performance when the playlist - is long. - Thanks to :user:`ray66`. - :bug:`3207` :bug:`2752` + is long. Thanks to :user:`ray66`. :bug:`3207` :bug:`2752` Several improvements are related to usability: -* The disambiguation string for identifying albums in the importer now shows - the catalog number. - Thanks to :user:`8h2a`. - :bug:`2951` -* Added whitespace padding to missing tracks dialog to improve readability. - Thanks to :user:`jams2`. - :bug:`2962` -* The :ref:`move-cmd` command now lists the number of items already in-place. - Thanks to :user:`RollingStar`. - :bug:`3117` -* Modify selection can now be applied early without selecting every item. +- The disambiguation string for identifying albums in the importer now shows the + catalog number. Thanks to :user:`8h2a`. :bug:`2951` +- Added whitespace padding to missing tracks dialog to improve readability. + Thanks to :user:`jams2`. :bug:`2962` +- The :ref:`move-cmd` command now lists the number of items already in-place. + Thanks to :user:`RollingStar`. :bug:`3117` +- Modify selection can now be applied early without selecting every item. :bug:`3083` -* Beets now emits more useful messages during startup if SQLite returns an error. The - SQLite error message is now attached to the beets message. +- Beets now emits more useful messages during startup if SQLite returns an + error. The SQLite error message is now attached to the beets message. :bug:`3005` -* Fixed a confusing typo when the :doc:`/plugins/convert` plugin copies the art - covers. - :bug:`3063` +- Fixed a confusing typo when the :doc:`/plugins/convert` plugin copies the art + covers. :bug:`3063` Many fixes have been focused on issues where beets would previously crash: -* Avoid a crash when archive extraction fails during import. - :bug:`3041` -* Missing album art file during an update no longer causes a fatal exception +- Avoid a crash when archive extraction fails during import. :bug:`3041` +- Missing album art file during an update no longer causes a fatal exception (instead, an error is logged and the missing file path is removed from the - library). - :bug:`3030` -* When updating the database, beets no longer tries to move album art twice. + library). :bug:`3030` +- When updating the database, beets no longer tries to move album art twice. :bug:`3189` -* Fix an unhandled exception when pruning empty directories. - :bug:`1996` :bug:`3209` -* :doc:`/plugins/fetchart`: Added network connection error handling to backends - so that beets won't crash if a request fails. - Thanks to :user:`Holzhaus`. +- Fix an unhandled exception when pruning empty directories. :bug:`1996` + :bug:`3209` +- :doc:`/plugins/fetchart`: Added network connection error handling to backends + so that beets won't crash if a request fails. Thanks to :user:`Holzhaus`. :bug:`1579` -* :doc:`/plugins/badfiles`: Avoid a crash when the underlying tool emits - undecodable output. - :bug:`3165` -* :doc:`/plugins/beatport`: Avoid a crash when the server produces an error. +- :doc:`/plugins/badfiles`: Avoid a crash when the underlying tool emits + undecodable output. :bug:`3165` +- :doc:`/plugins/beatport`: Avoid a crash when the server produces an error. :bug:`3184` -* :doc:`/plugins/bpd`: Fix crashes in the bpd server during exception handling. +- :doc:`/plugins/bpd`: Fix crashes in the bpd server during exception handling. :bug:`3200` -* :doc:`/plugins/bpd`: Fix a crash triggered when certain clients tried to list - the albums belonging to a particular artist. - :bug:`3007` :bug:`3215` -* :doc:`/plugins/replaygain`: Avoid a crash when the ``bs1770gain`` tool emits - malformed XML. - :bug:`2983` :bug:`3247` +- :doc:`/plugins/bpd`: Fix a crash triggered when certain clients tried to list + the albums belonging to a particular artist. :bug:`3007` :bug:`3215` +- :doc:`/plugins/replaygain`: Avoid a crash when the ``bs1770gain`` tool emits + malformed XML. :bug:`2983` :bug:`3247` There are many fixes related to compatibility with our dependencies including addressing changes interfaces: -* On Python 2, pin the :pypi:`jellyfish` requirement to version 0.6.0 for +- On Python 2, pin the :pypi:`jellyfish` requirement to version 0.6.0 for compatibility. -* Fix compatibility with Python 3.7 and its change to a name in the - :stdlib:`re` module. - :bug:`2978` -* Fix several uses of deprecated standard-library features on Python 3.7. - Thanks to :user:`arcresu`. - :bug:`3197` -* Fix compatibility with pre-release versions of Python 3.8. - :bug:`3201` :bug:`3202` -* :doc:`/plugins/web`: Fix an error when using more recent versions of Flask - with CORS enabled. - Thanks to :user:`rveachkc`. - :bug:`2979`: :bug:`2980` -* Avoid some deprecation warnings with certain versions of the MusicBrainz - library. - Thanks to :user:`zhelezov`. - :bug:`2826` :bug:`3092` -* Restore iTunes Store album art source, and remove the dependency on +- Fix compatibility with Python 3.7 and its change to a name in the :stdlib:`re` + module. :bug:`2978` +- Fix several uses of deprecated standard-library features on Python 3.7. Thanks + to :user:`arcresu`. :bug:`3197` +- Fix compatibility with pre-release versions of Python 3.8. :bug:`3201` + :bug:`3202` +- :doc:`/plugins/web`: Fix an error when using more recent versions of Flask + with CORS enabled. Thanks to :user:`rveachkc`. :bug:`2979`: :bug:`2980` +- Avoid some deprecation warnings with certain versions of the MusicBrainz + library. Thanks to :user:`zhelezov`. :bug:`2826` :bug:`3092` +- Restore iTunes Store album art source, and remove the dependency on :pypi:`python-itunes`, which had gone unmaintained and was not - Python-3-compatible. - Thanks to :user:`ocelma` for creating :pypi:`python-itunes` in the first place. - Thanks to :user:`nathdwek`. + Python-3-compatible. Thanks to :user:`ocelma` for creating + :pypi:`python-itunes` in the first place. Thanks to :user:`nathdwek`. :bug:`2371` :bug:`2551` :bug:`2718` -* :doc:`/plugins/lastgenre`, :doc:`/plugins/edit`: Avoid a deprecation warnings - from the :pypi:`PyYAML` library by switching to the safe loader. - Thanks to :user:`translit` and :user:`sbraz`. - :bug:`3192` :bug:`3225` -* Fix a problem when resizing images with :pypi:`PIL`/:pypi:`pillow` on Python 3. - Thanks to :user:`architek`. - :bug:`2504` :bug:`3029` +- :doc:`/plugins/lastgenre`, :doc:`/plugins/edit`: Avoid a deprecation warnings + from the :pypi:`PyYAML` library by switching to the safe loader. Thanks to + :user:`translit` and :user:`sbraz`. :bug:`3192` :bug:`3225` +- Fix a problem when resizing images with :pypi:`PIL`/:pypi:`pillow` on Python + 3. Thanks to :user:`architek`. :bug:`2504` :bug:`3029` And there are many other fixes: -* R128 normalization tags are now properly deleted from files when the values - are missing. - Thanks to :user:`autrimpo`. - :bug:`2757` -* Display the artist credit when matching albums if the :ref:`artist_credit` - configuration option is set. - :bug:`2953` -* With the :ref:`from_scratch` configuration option set, only writable fields +- R128 normalization tags are now properly deleted from files when the values + are missing. Thanks to :user:`autrimpo`. :bug:`2757` +- Display the artist credit when matching albums if the :ref:`artist_credit` + configuration option is set. :bug:`2953` +- With the :ref:`from_scratch` configuration option set, only writable fields are cleared. Beets now no longer ignores the format your music is saved in. :bug:`2972` -* The ``%aunique`` template function now works correctly with the - ``-f/--format`` option. - :bug:`3043` -* Fixed the ordering of items when manually selecting changes while updating - tags - Thanks to :user:`TaizoSimpson`. - :bug:`3501` -* The ``%title`` template function now works correctly with apostrophes. - Thanks to :user:`GuilhermeHideki`. - :bug:`3033` -* :doc:`/plugins/lastgenre`: It's now possible to set the ``prefer_specific`` - option without also setting ``canonical``. - :bug:`2973` -* :doc:`/plugins/fetchart`: The plugin now respects the ``ignore`` and - ``ignore_hidden`` settings. - :bug:`1632` -* :doc:`/plugins/hook`: Fix byte string interpolation in hook commands. +- The ``%aunique`` template function now works correctly with the + ``-f/--format`` option. :bug:`3043` +- Fixed the ordering of items when manually selecting changes while updating + tags Thanks to :user:`TaizoSimpson`. :bug:`3501` +- The ``%title`` template function now works correctly with apostrophes. Thanks + to :user:`GuilhermeHideki`. :bug:`3033` +- :doc:`/plugins/lastgenre`: It's now possible to set the ``prefer_specific`` + option without also setting ``canonical``. :bug:`2973` +- :doc:`/plugins/fetchart`: The plugin now respects the ``ignore`` and + ``ignore_hidden`` settings. :bug:`1632` +- :doc:`/plugins/hook`: Fix byte string interpolation in hook commands. :bug:`2967` :bug:`3167` -* :doc:`/plugins/the`: Log a message when something has changed, not when it - hasn't. - Thanks to :user:`arcresu`. - :bug:`3195` -* :doc:`/plugins/lastgenre`: The ``force`` config option now actually works. +- :doc:`/plugins/the`: Log a message when something has changed, not when it + hasn't. Thanks to :user:`arcresu`. :bug:`3195` +- :doc:`/plugins/lastgenre`: The ``force`` config option now actually works. :bug:`2704` :bug:`3054` -* Resizing image files with ImageMagick now avoids problems on systems where +- Resizing image files with ImageMagick now avoids problems on systems where there is a ``convert`` command that is *not* ImageMagick's by using the - ``magick`` executable when it is available. - Thanks to :user:`ababyduck`. + ``magick`` executable when it is available. Thanks to :user:`ababyduck`. :bug:`2093` :bug:`3236` There is one new thing for plugin developers to know about: -* In addition to prefix-based field queries, plugins can now define *named - queries* that are not associated with any specific field. - For example, the new :doc:`/plugins/playlist` supports queries like - ``playlist:name`` although there is no field named ``playlist``. - See :ref:`extend-query` for details. +- In addition to prefix-based field queries, plugins can now define *named + queries* that are not associated with any specific field. For example, the new + :doc:`/plugins/playlist` supports queries like ``playlist:name`` although + there is no field named ``playlist``. See :ref:`extend-query` for details. And some messages for packagers: -* Note the changes to the dependencies on :pypi:`jellyfish` and :pypi:`munkres`. -* The optional :pypi:`python-itunes` dependency has been removed. -* Python versions 3.7 and 3.8 are now supported. +- Note the changes to the dependencies on :pypi:`jellyfish` and :pypi:`munkres`. +- The optional :pypi:`python-itunes` dependency has been removed. +- Python versions 3.7 and 3.8 are now supported. 1.4.7 (May 29, 2018) -------------------- -This new release includes lots of new features in the importer and the -metadata source backends that it uses. -We've changed how the beets importer handles non-audio tracks listed in -metadata sources like MusicBrainz: +This new release includes lots of new features in the importer and the metadata +source backends that it uses. We've changed how the beets importer handles +non-audio tracks listed in metadata sources like MusicBrainz: -* The importer now ignores non-audio tracks (namely, data and video tracks) +- The importer now ignores non-audio tracks (namely, data and video tracks) listed in MusicBrainz. Also, a new option, :ref:`ignore_video_tracks`, lets - you return to the old behavior and include these video tracks. - :bug:`1210` -* A new importer option, :ref:`ignored_media`, can let you skip certain media - formats. - :bug:`2688` + you return to the old behavior and include these video tracks. :bug:`1210` +- A new importer option, :ref:`ignored_media`, can let you skip certain media + formats. :bug:`2688` There are other subtle improvements to metadata handling in the importer: -* In the MusicBrainz backend, beets now imports the - ``musicbrainz_releasetrackid`` field. This is a first step toward - :bug:`406`. +- In the MusicBrainz backend, beets now imports the + ``musicbrainz_releasetrackid`` field. This is a first step toward :bug:`406`. Thanks to :user:`Rawrmonkeys`. -* A new importer configuration option, :ref:`artist_credit`, will tell beets - to prefer the artist credit over the artist when autotagging. - :bug:`1249` +- A new importer configuration option, :ref:`artist_credit`, will tell beets to + prefer the artist credit over the artist when autotagging. :bug:`1249` And there are even more new features: -* :doc:`/plugins/replaygain`: The ``beet replaygain`` command now has +- :doc:`/plugins/replaygain`: The ``beet replaygain`` command now has ``--force``, ``--write`` and ``--nowrite`` options. :bug:`2778` -* A new importer configuration option, :ref:`incremental_skip_later`, lets you - avoid recording skipped directories to the list of "processed" directories - in :ref:`incremental` mode. This way, you can revisit them later with - another import. - Thanks to :user:`sekjun9878`. - :bug:`2773` -* :doc:`/plugins/fetchart`: The configuration options now support - finer-grained control via the ``sources`` option. You can now specify the - search order for different *matching strategies* within different backends. -* :doc:`/plugins/web`: A new ``cors_supports_credentials`` configuration - option lets in-browser clients communicate with the server even when it is - protected by an authorization mechanism (a proxy with HTTP authentication - enabled, for example). -* A new :doc:`/plugins/sonosupdate` plugin automatically notifies Sonos - controllers to update the music library when the beets library changes. - Thanks to :user:`cgtobi`. -* :doc:`/plugins/discogs`: The plugin now stores master release IDs into - ``mb_releasegroupid``. It also "simulates" track IDs using the release ID - and the track list position. - Thanks to :user:`dbogdanov`. - :bug:`2336` -* :doc:`/plugins/discogs`: Fetch the original year from master releases. +- A new importer configuration option, :ref:`incremental_skip_later`, lets you + avoid recording skipped directories to the list of "processed" directories in + :ref:`incremental` mode. This way, you can revisit them later with another + import. Thanks to :user:`sekjun9878`. :bug:`2773` +- :doc:`/plugins/fetchart`: The configuration options now support finer-grained + control via the ``sources`` option. You can now specify the search order for + different *matching strategies* within different backends. +- :doc:`/plugins/web`: A new ``cors_supports_credentials`` configuration option + lets in-browser clients communicate with the server even when it is protected + by an authorization mechanism (a proxy with HTTP authentication enabled, for + example). +- A new :doc:`/plugins/sonosupdate` plugin automatically notifies Sonos + controllers to update the music library when the beets library changes. Thanks + to :user:`cgtobi`. +- :doc:`/plugins/discogs`: The plugin now stores master release IDs into + ``mb_releasegroupid``. It also "simulates" track IDs using the release ID and + the track list position. Thanks to :user:`dbogdanov`. :bug:`2336` +- :doc:`/plugins/discogs`: Fetch the original year from master releases. :bug:`1122` There are lots and lots of fixes: -* :doc:`/plugins/replaygain`: Fix a corner-case with the ``bs1770gain`` backend +- :doc:`/plugins/replaygain`: Fix a corner-case with the ``bs1770gain`` backend where ReplayGain values were assigned to the wrong files. The plugin now - requires version 0.4.6 or later of the ``bs1770gain`` tool. - :bug:`2777` -* :doc:`/plugins/lyrics`: The plugin no longer crashes in the Genius source - when BeautifulSoup is not found. Instead, it just logs a message and - disables the source. - :bug:`2911` -* :doc:`/plugins/lyrics`: Handle network and API errors when communicating - with Genius. :bug:`2771` -* :doc:`/plugins/lyrics`: The ``lyrics`` command previously wrote ReST files - by default, even when you didn't ask for them. This default has been fixed. -* :doc:`/plugins/lyrics`: When writing ReST files, the ``lyrics`` command - now groups lyrics by the ``albumartist`` field, rather than ``artist``. + requires version 0.4.6 or later of the ``bs1770gain`` tool. :bug:`2777` +- :doc:`/plugins/lyrics`: The plugin no longer crashes in the Genius source when + BeautifulSoup is not found. Instead, it just logs a message and disables the + source. :bug:`2911` +- :doc:`/plugins/lyrics`: Handle network and API errors when communicating with + Genius. :bug:`2771` +- :doc:`/plugins/lyrics`: The ``lyrics`` command previously wrote ReST files by + default, even when you didn't ask for them. This default has been fixed. +- :doc:`/plugins/lyrics`: When writing ReST files, the ``lyrics`` command now + groups lyrics by the ``albumartist`` field, rather than ``artist``. :bug:`2924` -* Plugins can now see updated import task state, such as when rejecting the +- Plugins can now see updated import task state, such as when rejecting the initial candidates and finding new ones via a manual search. Notably, this - means that the importer prompt options that the :doc:`/plugins/edit` - provides show up more reliably after doing a secondary import search. - :bug:`2441` :bug:`2731` -* :doc:`/plugins/importadded`: Fix a crash on non-autotagged imports. - Thanks to :user:`m42i`. - :bug:`2601` :bug:`1918` -* :doc:`/plugins/plexupdate`: The Plex token is now redacted in configuration - output. - Thanks to :user:`Kovrinic`. - :bug:`2804` -* Avoid a crash when importing a non-ASCII filename when using an ASCII locale - on Unix under Python 3. - :bug:`2793` :bug:`2803` -* Fix a problem caused by time zone misalignment that could make date queries + means that the importer prompt options that the :doc:`/plugins/edit` provides + show up more reliably after doing a secondary import search. :bug:`2441` + :bug:`2731` +- :doc:`/plugins/importadded`: Fix a crash on non-autotagged imports. Thanks to + :user:`m42i`. :bug:`2601` :bug:`1918` +- :doc:`/plugins/plexupdate`: The Plex token is now redacted in configuration + output. Thanks to :user:`Kovrinic`. :bug:`2804` +- Avoid a crash when importing a non-ASCII filename when using an ASCII locale + on Unix under Python 3. :bug:`2793` :bug:`2803` +- Fix a problem caused by time zone misalignment that could make date queries fail to match certain dates that are near the edges of a range. For example, querying for dates within a certain month would fail to match dates within - hours of the end of that month. - :bug:`2652` -* :doc:`/plugins/convert`: The plugin now runs before other plugin-provided + hours of the end of that month. :bug:`2652` +- :doc:`/plugins/convert`: The plugin now runs before other plugin-provided import stages, which addresses an issue with generating ReplayGain data - incompatible between the source and target file formats. - Thanks to :user:`autrimpo`. - :bug:`2814` -* :doc:`/plugins/ftintitle`: The ``drop`` config option had no effect; it now - does what it says it should do. - :bug:`2817` -* Importing a release with multiple release events now selects the - event based on the order of your :ref:`preferred` countries rather than - the order of release events in MusicBrainz. :bug:`2816` -* :doc:`/plugins/web`: The time display in the web interface would incorrectly jump - at the 30-second mark of every minute. Now, it correctly changes over at zero - seconds. :bug:`2822` -* :doc:`/plugins/web`: Fetching album art now works (instead of throwing an - exception) under Python 3. - Additionally, the server will now return a 404 response when the album ID - is unknown (instead of throwing an exception and producing a 500 response). - :bug:`2823` -* :doc:`/plugins/web`: Fix an exception on Python 3 for filenames with + incompatible between the source and target file formats. Thanks to + :user:`autrimpo`. :bug:`2814` +- :doc:`/plugins/ftintitle`: The ``drop`` config option had no effect; it now + does what it says it should do. :bug:`2817` +- Importing a release with multiple release events now selects the event based + on the order of your :ref:`preferred` countries rather than the order of + release events in MusicBrainz. :bug:`2816` +- :doc:`/plugins/web`: The time display in the web interface would incorrectly + jump at the 30-second mark of every minute. Now, it correctly changes over at + zero seconds. :bug:`2822` +- :doc:`/plugins/web`: Fetching album art now works (instead of throwing an + exception) under Python 3. Additionally, the server will now return a 404 + response when the album ID is unknown (instead of throwing an exception and + producing a 500 response). :bug:`2823` +- :doc:`/plugins/web`: Fix an exception on Python 3 for filenames with non-Latin1 characters. (These characters are now converted to their ASCII - equivalents.) - :bug:`2815` -* Partially fix bash completion for subcommand names that contain hyphens. - Thanks to :user:`jhermann`. - :bug:`2836` :bug:`2837` -* :doc:`/plugins/replaygain`: Really fix album gain calculation using the + equivalents.) :bug:`2815` +- Partially fix bash completion for subcommand names that contain hyphens. + Thanks to :user:`jhermann`. :bug:`2836` :bug:`2837` +- :doc:`/plugins/replaygain`: Really fix album gain calculation using the GStreamer backend. :bug:`2846` -* Avoid an error when doing a "no-op" move on non-existent files (i.e., moving - a file onto itself). :bug:`2863` -* :doc:`/plugins/discogs`: Fix the ``medium`` and ``medium_index`` values, which - were occasionally incorrect for releases with two-sided mediums such as - vinyl. Also fix the ``medium_total`` value, which now contains total number - of tracks on the medium to which a track belongs, not the total number of - different mediums present on the release. - Thanks to :user:`dbogdanov`. - :bug:`2887` -* The importer now supports audio files contained in data tracks when they are +- Avoid an error when doing a "no-op" move on non-existent files (i.e., moving a + file onto itself). :bug:`2863` +- :doc:`/plugins/discogs`: Fix the ``medium`` and ``medium_index`` values, which + were occasionally incorrect for releases with two-sided mediums such as vinyl. + Also fix the ``medium_total`` value, which now contains total number of tracks + on the medium to which a track belongs, not the total number of different + mediums present on the release. Thanks to :user:`dbogdanov`. :bug:`2887` +- The importer now supports audio files contained in data tracks when they are listed in MusicBrainz: the corresponding audio tracks are now merged into the main track list. Thanks to :user:`jdetrey`. :bug:`1638` -* :doc:`/plugins/keyfinder`: Avoid a crash when trying to process unmatched +- :doc:`/plugins/keyfinder`: Avoid a crash when trying to process unmatched tracks. :bug:`2537` -* :doc:`/plugins/mbsync`: Support MusicBrainz recording ID changes, relying - on release track IDs instead. Thanks to :user:`jdetrey`. :bug:`1234` -* :doc:`/plugins/mbsync`: We can now successfully update albums even when the +- :doc:`/plugins/mbsync`: Support MusicBrainz recording ID changes, relying on + release track IDs instead. Thanks to :user:`jdetrey`. :bug:`1234` +- :doc:`/plugins/mbsync`: We can now successfully update albums even when the first track has a missing MusicBrainz recording ID. :bug:`2920` There are a couple of changes for developers: -* Plugins can now run their import stages *early*, before other plugins. Use - the ``early_import_stages`` list instead of plain ``import_stages`` to - request this behavior. - :bug:`2814` -* We again properly send ``albuminfo_received`` and ``trackinfo_received`` in +- Plugins can now run their import stages *early*, before other plugins. Use the + ``early_import_stages`` list instead of plain ``import_stages`` to request + this behavior. :bug:`2814` +- We again properly send ``albuminfo_received`` and ``trackinfo_received`` in all cases, most notably when using the ``mbsync`` plugin. This was a - regression since version 1.4.1. - :bug:`2921` + regression since version 1.4.1. :bug:`2921` 1.4.6 (December 21, 2017) ------------------------- -The highlight of this release is "album merging," an oft-requested option in -the importer to add new tracks to an existing album you already have in your +The highlight of this release is "album merging," an oft-requested option in the +importer to add new tracks to an existing album you already have in your library. This way, you no longer need to resort to removing the partial album from your library, combining the files manually, and importing again. Here are the larger new features in this release: -* When the importer finds duplicate albums, you can now merge all the +- When the importer finds duplicate albums, you can now merge all the tracks---old and new---together and try importing them as a single, combined - album. - Thanks to :user:`udiboy1209`. - :bug:`112` :bug:`2725` -* :doc:`/plugins/lyrics`: The plugin can now produce reStructuredText files - for beautiful, readable books of lyrics. Thanks to :user:`anarcat`. - :bug:`2628` -* A new :ref:`from_scratch` configuration option makes the importer remove old + album. Thanks to :user:`udiboy1209`. :bug:`112` :bug:`2725` +- :doc:`/plugins/lyrics`: The plugin can now produce reStructuredText files for + beautiful, readable books of lyrics. Thanks to :user:`anarcat`. :bug:`2628` +- A new :ref:`from_scratch` configuration option makes the importer remove old metadata before applying new metadata. This new feature complements the :doc:`zero </plugins/zero>` and :doc:`scrub </plugins/scrub>` plugins but is - slightly different: beets clears out all the old tags it knows about and - only keeps the new data it gets from the remote metadata source. - Thanks to :user:`tummychow`. - :bug:`934` :bug:`2755` + slightly different: beets clears out all the old tags it knows about and only + keeps the new data it gets from the remote metadata source. Thanks to + :user:`tummychow`. :bug:`934` :bug:`2755` There are also somewhat littler, but still great, new features: -* :doc:`/plugins/convert`: A new ``no_convert`` option lets you skip - transcoding items matching a query. Instead, the files are just copied - as-is. Thanks to :user:`Stunner`. - :bug:`2732` :bug:`2751` -* :doc:`/plugins/fetchart`: A new quiet switch that only prints out messages - when album art is missing. - Thanks to :user:`euri10`. - :bug:`2683` -* :doc:`/plugins/mbcollection`: You can configure a custom MusicBrainz - collection via the new ``collection`` configuration option. - :bug:`2685` -* :doc:`/plugins/mbcollection`: The collection update command can now remove +- :doc:`/plugins/convert`: A new ``no_convert`` option lets you skip transcoding + items matching a query. Instead, the files are just copied as-is. Thanks to + :user:`Stunner`. :bug:`2732` :bug:`2751` +- :doc:`/plugins/fetchart`: A new quiet switch that only prints out messages + when album art is missing. Thanks to :user:`euri10`. :bug:`2683` +- :doc:`/plugins/mbcollection`: You can configure a custom MusicBrainz + collection via the new ``collection`` configuration option. :bug:`2685` +- :doc:`/plugins/mbcollection`: The collection update command can now remove albums from collections that are longer in the beets library. -* :doc:`/plugins/fetchart`: The ``clearart`` command now asks for confirmation - before touching your files. - Thanks to :user:`konman2`. - :bug:`2708` :bug:`2427` -* :doc:`/plugins/mpdstats`: The plugin now correctly updates song statistics +- :doc:`/plugins/fetchart`: The ``clearart`` command now asks for confirmation + before touching your files. Thanks to :user:`konman2`. :bug:`2708` :bug:`2427` +- :doc:`/plugins/mpdstats`: The plugin now correctly updates song statistics when MPD switches from a song to a stream and when it plays the same song - multiple times consecutively. - :bug:`2707` -* :doc:`/plugins/acousticbrainz`: The plugin can now be configured to write only - a specific list of tags. - Thanks to :user:`woparry`. + multiple times consecutively. :bug:`2707` +- :doc:`/plugins/acousticbrainz`: The plugin can now be configured to write only + a specific list of tags. Thanks to :user:`woparry`. There are lots and lots of bug fixes: -* :doc:`/plugins/hook`: Fixed a problem where accessing non-string properties - of ``item`` or ``album`` (e.g., ``item.track``) would cause a crash. - Thanks to :user:`broddo`. - :bug:`2740` -* :doc:`/plugins/play`: When ``relative_to`` is set, the plugin correctly - emits relative paths even when querying for albums rather than tracks. - Thanks to :user:`j000`. - :bug:`2702` -* We suppress a spurious Python warning about a ``BrokenPipeError`` being - ignored. This was an issue when using beets in simple shell scripts. - Thanks to :user:`Azphreal`. - :bug:`2622` :bug:`2631` -* :doc:`/plugins/replaygain`: Fix a regression in the previous release related +- :doc:`/plugins/hook`: Fixed a problem where accessing non-string properties of + ``item`` or ``album`` (e.g., ``item.track``) would cause a crash. Thanks to + :user:`broddo`. :bug:`2740` +- :doc:`/plugins/play`: When ``relative_to`` is set, the plugin correctly emits + relative paths even when querying for albums rather than tracks. Thanks to + :user:`j000`. :bug:`2702` +- We suppress a spurious Python warning about a ``BrokenPipeError`` being + ignored. This was an issue when using beets in simple shell scripts. Thanks to + :user:`Azphreal`. :bug:`2622` :bug:`2631` +- :doc:`/plugins/replaygain`: Fix a regression in the previous release related to the new R128 tags. :bug:`2615` :bug:`2623` -* :doc:`/plugins/lyrics`: The MusixMatch backend now detects and warns - when the server has blocked the client. - Thanks to :user:`anarcat`. :bug:`2634` :bug:`2632` -* :doc:`/plugins/importfeeds`: Fix an error on Python 3 in certain +- :doc:`/plugins/lyrics`: The MusixMatch backend now detects and warns when the + server has blocked the client. Thanks to :user:`anarcat`. :bug:`2634` + :bug:`2632` +- :doc:`/plugins/importfeeds`: Fix an error on Python 3 in certain configurations. Thanks to :user:`djl`. :bug:`2467` :bug:`2658` -* :doc:`/plugins/edit`: Fix a bug when editing items during a re-import with - the ``-L`` flag. Previously, diffs against against unrelated items could be - shown or beets could crash. :bug:`2659` -* :doc:`/plugins/kodiupdate`: Fix the server URL and add better error - reporting. +- :doc:`/plugins/edit`: Fix a bug when editing items during a re-import with the + ``-L`` flag. Previously, diffs against against unrelated items could be shown + or beets could crash. :bug:`2659` +- :doc:`/plugins/kodiupdate`: Fix the server URL and add better error reporting. :bug:`2662` -* Fixed a problem where "no-op" modifications would reset files' mtimes, +- Fixed a problem where "no-op" modifications would reset files' mtimes, resulting in unnecessary writes. This most prominently affected the :doc:`/plugins/edit` when saving the text file without making changes to some music. :bug:`2667` -* :doc:`/plugins/chroma`: Fix a crash when running the ``submit`` command on +- :doc:`/plugins/chroma`: Fix a crash when running the ``submit`` command on Python 3 on Windows with non-ASCII filenames. :bug:`2671` -* :doc:`/plugins/absubmit`: Fix an occasional crash on Python 3 when the AB +- :doc:`/plugins/absubmit`: Fix an occasional crash on Python 3 when the AB analysis tool produced non-ASCII metadata. :bug:`2673` -* :doc:`/plugins/duplicates`: Use the default tiebreak for items or albums - when the configuration only specifies a tiebreak for the other kind of - entity. - Thanks to :user:`cgevans`. - :bug:`2758` -* :doc:`/plugins/duplicates`: Fix the ``--key`` command line option, which was +- :doc:`/plugins/duplicates`: Use the default tiebreak for items or albums when + the configuration only specifies a tiebreak for the other kind of entity. + Thanks to :user:`cgevans`. :bug:`2758` +- :doc:`/plugins/duplicates`: Fix the ``--key`` command line option, which was ignored. -* :doc:`/plugins/replaygain`: Fix album ReplayGain calculation with the +- :doc:`/plugins/replaygain`: Fix album ReplayGain calculation with the GStreamer backend. :bug:`2636` -* :doc:`/plugins/scrub`: Handle errors when manipulating files using newer +- :doc:`/plugins/scrub`: Handle errors when manipulating files using newer versions of Mutagen. :bug:`2716` -* :doc:`/plugins/fetchart`: The plugin no longer gets skipped during import - when the "Edit Candidates" option is used from the :doc:`/plugins/edit`. +- :doc:`/plugins/fetchart`: The plugin no longer gets skipped during import when + the "Edit Candidates" option is used from the :doc:`/plugins/edit`. :bug:`2734` -* Fix a crash when numeric metadata fields contain just a minus or plus sign +- Fix a crash when numeric metadata fields contain just a minus or plus sign with no following numbers. Thanks to :user:`eigengrau`. :bug:`2741` -* :doc:`/plugins/fromfilename`: Recognize file names that contain *only* a - track number, such as `01.mp3`. Also, the plugin now allows underscores as a - separator between fields. - Thanks to :user:`Vrihub`. - :bug:`2738` :bug:`2759` -* Fixed an issue where images would be resized according to their longest - edge, instead of their width, when using the ``maxwidth`` config option in - the :doc:`/plugins/fetchart` and :doc:`/plugins/embedart`. Thanks to +- :doc:`/plugins/fromfilename`: Recognize file names that contain *only* a track + number, such as ``01.mp3``. Also, the plugin now allows underscores as a + separator between fields. Thanks to :user:`Vrihub`. :bug:`2738` :bug:`2759` +- Fixed an issue where images would be resized according to their longest edge, + instead of their width, when using the ``maxwidth`` config option in the + :doc:`/plugins/fetchart` and :doc:`/plugins/embedart`. Thanks to :user:`sekjun9878`. :bug:`2729` There are some changes for developers: -* "Fixed fields" in Album and Item objects are now more strict about translating - missing values into type-specific null-like values. This should help in - cases where a string field is unexpectedly `None` sometimes instead of just +- "Fixed fields" in Album and Item objects are now more strict about translating + missing values into type-specific null-like values. This should help in cases + where a string field is unexpectedly ``None`` sometimes instead of just showing up as an empty string. :bug:`2605` -* Refactored the move functions the `beets.library` module and the - `manipulate_files` function in `beets.importer` to use a single parameter - describing the file operation instead of multiple Boolean flags. - There is a new numerated type describing how to move, copy, or link files. - :bug:`2682` +- Refactored the move functions the ``beets.library`` module and the + ``manipulate_files`` function in ``beets.importer`` to use a single parameter + describing the file operation instead of multiple Boolean flags. There is a + new numerated type describing how to move, copy, or link files. :bug:`2682` 1.4.5 (June 20, 2017) --------------------- -Version 1.4.5 adds some oft-requested features. When you're importing files, -you can now manually set fields on the new music. Date queries have gotten -much more powerful: you can write precise queries down to the second, and we -now have *relative* queries like ``-1w``, which means *one week ago*. +Version 1.4.5 adds some oft-requested features. When you're importing files, you +can now manually set fields on the new music. Date queries have gotten much more +powerful: you can write precise queries down to the second, and we now have +*relative* queries like ``-1w``, which means *one week ago*. Here are the new features: -* You can now set fields to certain values during :ref:`import-cmd`, using +- You can now set fields to certain values during :ref:`import-cmd`, using either a ``--set field=value`` command-line flag or a new :ref:`set_fields` - configuration option under the `importer` section. - Thanks to :user:`bartkl`. :bug:`1881` :bug:`2581` -* :ref:`Date queries <datequery>` can now include times, so you can filter - your music down to the second. Thanks to :user:`discopatrick`. :bug:`2506` + configuration option under the ``importer`` section. Thanks to :user:`bartkl`. + :bug:`1881` :bug:`2581` +- :ref:`Date queries <datequery>` can now include times, so you can filter your + music down to the second. Thanks to :user:`discopatrick`. :bug:`2506` :bug:`2528` -* :ref:`Date queries <datequery>` can also be *relative*. You can say - ``added:-1w..`` to match music added in the last week, for example. Thanks - to :user:`euri10`. :bug:`2598` -* A new :doc:`/plugins/gmusic` lets you interact with your Google Play Music +- :ref:`Date queries <datequery>` can also be *relative*. You can say + ``added:-1w..`` to match music added in the last week, for example. Thanks to + :user:`euri10`. :bug:`2598` +- A new :doc:`/plugins/gmusic` lets you interact with your Google Play Music library. Thanks to :user:`tigranl`. :bug:`2553` :bug:`2586` -* :doc:`/plugins/replaygain`: We now keep R128 data in separate tags from +- :doc:`/plugins/replaygain`: We now keep R128 data in separate tags from classic ReplayGain data for formats that need it (namely, Ogg Opus). A new - `r128` configuration option enables this behavior for specific formats. + ``r128`` configuration option enables this behavior for specific formats. Thanks to :user:`autrimpo`. :bug:`2557` :bug:`2560` -* The :ref:`move-cmd` command gained a new ``--export`` flag, which copies - files to an external location without changing their paths in the library - database. Thanks to :user:`SpirosChadoulos`. :bug:`435` :bug:`2510` +- The :ref:`move-cmd` command gained a new ``--export`` flag, which copies files + to an external location without changing their paths in the library database. + Thanks to :user:`SpirosChadoulos`. :bug:`435` :bug:`2510` There are also some bug fixes: -* :doc:`/plugins/lastgenre`: Fix a crash when using the `prefer_specific` and - `canonical` options together. Thanks to :user:`yacoob`. :bug:`2459` +- :doc:`/plugins/lastgenre`: Fix a crash when using the ``prefer_specific`` and + ``canonical`` options together. Thanks to :user:`yacoob`. :bug:`2459` :bug:`2583` -* :doc:`/plugins/web`: Fix a crash on Windows under Python 2 when serving +- :doc:`/plugins/web`: Fix a crash on Windows under Python 2 when serving non-ASCII filenames. Thanks to :user:`robot3498712`. :bug:`2592` :bug:`2593` -* :doc:`/plugins/metasync`: Fix a crash in the Amarok backend when filenames +- :doc:`/plugins/metasync`: Fix a crash in the Amarok backend when filenames contain quotes. Thanks to :user:`aranc23`. :bug:`2595` :bug:`2596` -* More informative error messages are displayed when the file format is not +- More informative error messages are displayed when the file format is not recognized. :bug:`2599` 1.4.4 (June 10, 2017) --------------------- This release built up a longer-than-normal list of nifty new features. We now -support DSF audio files and the importer can hard-link your files, for -example. +support DSF audio files and the importer can hard-link your files, for example. Here's a full list of new features: -* Added support for DSF files, once a future version of Mutagen is released - that supports them. Thanks to :user:`docbobo`. :bug:`459` :bug:`2379` -* A new :ref:`hardlink` config option instructs the importer to create hard +- Added support for DSF files, once a future version of Mutagen is released that + supports them. Thanks to :user:`docbobo`. :bug:`459` :bug:`2379` +- A new :ref:`hardlink` config option instructs the importer to create hard links on filesystems that support them. Thanks to :user:`jacobwgillespie`. :bug:`2445` -* A new :doc:`/plugins/kodiupdate` lets you keep your Kodi library in sync - with beets. Thanks to :user:`Pauligrinder`. :bug:`2411` -* A new :ref:`bell` configuration option under the ``import`` section enables - a terminal bell when input is required. Thanks to :user:`SpirosChadoulos`. +- A new :doc:`/plugins/kodiupdate` lets you keep your Kodi library in sync with + beets. Thanks to :user:`Pauligrinder`. :bug:`2411` +- A new :ref:`bell` configuration option under the ``import`` section enables a + terminal bell when input is required. Thanks to :user:`SpirosChadoulos`. :bug:`2366` :bug:`2495` -* A new field, ``composer_sort``, is now supported and fetched from - MusicBrainz. - Thanks to :user:`dosoe`. - :bug:`2519` :bug:`2529` -* The MusicBrainz backend and :doc:`/plugins/discogs` now both provide a new - attribute called ``track_alt`` that stores more nuanced, possibly - non-numeric track index data. For example, some vinyl or tape media will - report the side of the record using a letter instead of a number in that - field. :bug:`1831` :bug:`2363` -* :doc:`/plugins/web`: Added a new endpoint, ``/item/path/foo``, which will +- A new field, ``composer_sort``, is now supported and fetched from MusicBrainz. + Thanks to :user:`dosoe`. :bug:`2519` :bug:`2529` +- The MusicBrainz backend and :doc:`/plugins/discogs` now both provide a new + attribute called ``track_alt`` that stores more nuanced, possibly non-numeric + track index data. For example, some vinyl or tape media will report the side + of the record using a letter instead of a number in that field. :bug:`1831` + :bug:`2363` +- :doc:`/plugins/web`: Added a new endpoint, ``/item/path/foo``, which will return the item info for the file at the given path, or 404. -* :doc:`/plugins/web`: Added a new config option, ``include_paths``, - which will cause paths to be included in item API responses if set to true. -* The ``%aunique`` template function for :ref:`aunique` now takes a third - argument that specifies which brackets to use around the disambiguator - value. The argument can be any two characters that represent the left and - right brackets. It defaults to `[]` and can also be blank to turn off - bracketing. :bug:`2397` :bug:`2399` -* Added a ``--move`` or ``-m`` option to the importer so that the files can be - moved to the library instead of being copied or added "in place." - :bug:`2252` :bug:`2429` -* :doc:`/plugins/badfiles`: Added a ``--verbose`` or ``-v`` option. Results are +- :doc:`/plugins/web`: Added a new config option, ``include_paths``, which will + cause paths to be included in item API responses if set to true. +- The ``%aunique`` template function for :ref:`aunique` now takes a third + argument that specifies which brackets to use around the disambiguator value. + The argument can be any two characters that represent the left and right + brackets. It defaults to ``[]`` and can also be blank to turn off bracketing. + :bug:`2397` :bug:`2399` +- Added a ``--move`` or ``-m`` option to the importer so that the files can be + moved to the library instead of being copied or added "in place." :bug:`2252` + :bug:`2429` +- :doc:`/plugins/badfiles`: Added a ``--verbose`` or ``-v`` option. Results are now displayed only for corrupted files by default and for all the files when the verbose option is set. :bug:`1654` :bug:`2434` -* :doc:`/plugins/embedart`: The explicit ``embedart`` command now asks for - confirmation before embedding art into music files. Thanks to - :user:`Stunner`. :bug:`1999` -* You can now run beets by typing `python -m beets`. :bug:`2453` -* :doc:`/plugins/smartplaylist`: Different playlist specifications that - generate identically-named playlist files no longer conflict; instead, the - resulting lists of tracks are concatenated. :bug:`2468` -* :doc:`/plugins/missing`: A new mode lets you see missing albums from artists +- :doc:`/plugins/embedart`: The explicit ``embedart`` command now asks for + confirmation before embedding art into music files. Thanks to :user:`Stunner`. + :bug:`1999` +- You can now run beets by typing ``python -m beets``. :bug:`2453` +- :doc:`/plugins/smartplaylist`: Different playlist specifications that generate + identically-named playlist files no longer conflict; instead, the resulting + lists of tracks are concatenated. :bug:`2468` +- :doc:`/plugins/missing`: A new mode lets you see missing albums from artists you have in your library. Thanks to :user:`qlyoung`. :bug:`2481` -* :doc:`/plugins/web` : Add new `reverse_proxy` config option to allow serving +- :doc:`/plugins/web` : Add new ``reverse_proxy`` config option to allow serving the web plugins under a reverse proxy. -* Importing a release with multiple release events now selects the - event based on your :ref:`preferred` countries. :bug:`2501` -* :doc:`/plugins/play`: A new ``-y`` or ``--yes`` parameter lets you skip - the warning message if you enqueue more items than the warning threshold - usually allows. -* Fix a bug where commands which forked subprocesses would sometimes prevent - further inputs. This bug mainly affected :doc:`/plugins/convert`. - Thanks to :user:`jansol`. - :bug:`2488` - :bug:`2524` +- Importing a release with multiple release events now selects the event based + on your :ref:`preferred` countries. :bug:`2501` +- :doc:`/plugins/play`: A new ``-y`` or ``--yes`` parameter lets you skip the + warning message if you enqueue more items than the warning threshold usually + allows. +- Fix a bug where commands which forked subprocesses would sometimes prevent + further inputs. This bug mainly affected :doc:`/plugins/convert`. Thanks to + :user:`jansol`. :bug:`2488` :bug:`2524` There are also quite a few fixes: -* In the :ref:`replace` configuration option, we now replace a leading hyphen +- In the :ref:`replace` configuration option, we now replace a leading hyphen (-) with an underscore. :bug:`549` :bug:`2509` -* :doc:`/plugins/absubmit`: We no longer filter audio files for specific +- :doc:`/plugins/absubmit`: We no longer filter audio files for specific formats---we will attempt the submission process for all formats. :bug:`2471` -* :doc:`/plugins/mpdupdate`: Fix Python 3 compatibility. :bug:`2381` -* :doc:`/plugins/replaygain`: Fix Python 3 compatibility in the ``bs1770gain`` +- :doc:`/plugins/mpdupdate`: Fix Python 3 compatibility. :bug:`2381` +- :doc:`/plugins/replaygain`: Fix Python 3 compatibility in the ``bs1770gain`` backend. :bug:`2382` -* :doc:`/plugins/bpd`: Report playback times as integers. :bug:`2394` -* :doc:`/plugins/mpdstats`: Fix Python 3 compatibility. The plugin also now +- :doc:`/plugins/bpd`: Report playback times as integers. :bug:`2394` +- :doc:`/plugins/mpdstats`: Fix Python 3 compatibility. The plugin also now requires version 0.4.2 or later of the ``python-mpd2`` library. :bug:`2405` -* :doc:`/plugins/mpdstats`: Improve handling of MPD status queries. -* :doc:`/plugins/badfiles`: Fix Python 3 compatibility. -* Fix some cases where album-level ReplayGain/SoundCheck metadata would be +- :doc:`/plugins/mpdstats`: Improve handling of MPD status queries. +- :doc:`/plugins/badfiles`: Fix Python 3 compatibility. +- Fix some cases where album-level ReplayGain/SoundCheck metadata would be written to files incorrectly. :bug:`2426` -* :doc:`/plugins/badfiles`: The command no longer bails out if the validator +- :doc:`/plugins/badfiles`: The command no longer bails out if the validator command is not found or exits with an error. :bug:`2430` :bug:`2433` -* :doc:`/plugins/lyrics`: The Google search backend no longer crashes when the +- :doc:`/plugins/lyrics`: The Google search backend no longer crashes when the server responds with an error. :bug:`2437` -* :doc:`/plugins/discogs`: You can now authenticate with Discogs using a +- :doc:`/plugins/discogs`: You can now authenticate with Discogs using a personal access token. :bug:`2447` -* Fix Python 3 compatibility when extracting rar archives in the importer. +- Fix Python 3 compatibility when extracting rar archives in the importer. Thanks to :user:`Lompik`. :bug:`2443` :bug:`2448` -* :doc:`/plugins/duplicates`: Fix Python 3 compatibility when using the - ``copy`` and ``move`` options. :bug:`2444` -* :doc:`/plugins/mbsubmit`: The tracks are now sorted properly. Thanks to +- :doc:`/plugins/duplicates`: Fix Python 3 compatibility when using the ``copy`` + and ``move`` options. :bug:`2444` +- :doc:`/plugins/mbsubmit`: The tracks are now sorted properly. Thanks to :user:`awesomer`. :bug:`2457` -* :doc:`/plugins/thumbnails`: Fix a string-related crash on Python 3. +- :doc:`/plugins/thumbnails`: Fix a string-related crash on Python 3. :bug:`2466` -* :doc:`/plugins/beatport`: More than just 10 songs are now fetched per album. +- :doc:`/plugins/beatport`: More than just 10 songs are now fetched per album. :bug:`2469` -* On Python 3, the :ref:`terminal_encoding` setting is respected again for - output and printing will no longer crash on systems configured with a - limited encoding. -* :doc:`/plugins/convert`: The default configuration uses FFmpeg's built-in - AAC codec instead of faac. Thanks to :user:`jansol`. :bug:`2484` -* Fix the importer's detection of multi-disc albums when other subdirectories +- On Python 3, the :ref:`terminal_encoding` setting is respected again for + output and printing will no longer crash on systems configured with a limited + encoding. +- :doc:`/plugins/convert`: The default configuration uses FFmpeg's built-in AAC + codec instead of faac. Thanks to :user:`jansol`. :bug:`2484` +- Fix the importer's detection of multi-disc albums when other subdirectories are present. :bug:`2493` -* Invalid date queries now print an error message instead of being silently +- Invalid date queries now print an error message instead of being silently ignored. Thanks to :user:`discopatrick`. :bug:`2513` :bug:`2517` -* When the SQLite database stops being accessible, we now print a friendly - error message. Thanks to :user:`Mary011196`. :bug:`1676` :bug:`2508` -* :doc:`/plugins/web`: Avoid a crash when sending binary data, such as +- When the SQLite database stops being accessible, we now print a friendly error + message. Thanks to :user:`Mary011196`. :bug:`1676` :bug:`2508` +- :doc:`/plugins/web`: Avoid a crash when sending binary data, such as Chromaprint fingerprints, in music attributes. :bug:`2542` :bug:`2532` -* Fix a hang when parsing templates that end in newlines. :bug:`2562` -* Fix a crash when reading non-ASCII characters in configuration files on +- Fix a hang when parsing templates that end in newlines. :bug:`2562` +- Fix a crash when reading non-ASCII characters in configuration files on Windows under Python 3. :bug:`2456` :bug:`2565` :bug:`2566` We removed backends from two metadata plugins because of bitrot: -* :doc:`/plugins/lyrics`: The Lyrics.com backend has been removed. (It stopped - working because of changes to the site's URL structure.) - :bug:`2548` :bug:`2549` -* :doc:`/plugins/fetchart`: The documentation no longer recommends iTunes - Store artwork lookup because the unmaintained `python-itunes`_ is broken. - Want to adopt it? :bug:`2371` :bug:`1610` +- :doc:`/plugins/lyrics`: The Lyrics.com backend has been removed. (It stopped + working because of changes to the site's URL structure.) :bug:`2548` + :bug:`2549` +- :doc:`/plugins/fetchart`: The documentation no longer recommends iTunes Store + artwork lookup because the unmaintained python-itunes_ is broken. Want to + adopt it? :bug:`2371` :bug:`1610` .. _python-itunes: https://github.com/ocelma/python-itunes @@ -2006,86 +1776,84 @@ Happy new year! This new version includes a cornucopia of new features from contributors, including new tags related to classical music and a new :doc:`/plugins/absubmit` for performing acoustic analysis on your music. The :doc:`/plugins/random` has a new mode that lets you generate time-limited -music---for example, you might generate a random playlist that lasts the -perfect length for your walk to work. We also access as many Web services as -possible over secure connections now---HTTPS everywhere! +music---for example, you might generate a random playlist that lasts the perfect +length for your walk to work. We also access as many Web services as possible +over secure connections now---HTTPS everywhere! The most visible new features are: -* We now support the composer, lyricist, and arranger tags. The MusicBrainz - data source will fetch data for these fields when the next version of - `python-musicbrainzngs`_ is released. Thanks to :user:`ibmibmibm`. - :bug:`506` :bug:`507` :bug:`1547` :bug:`2333` -* A new :doc:`/plugins/absubmit` lets you run acoustic analysis software and +- We now support the composer, lyricist, and arranger tags. The MusicBrainz data + source will fetch data for these fields when the next version of + python-musicbrainzngs_ is released. Thanks to :user:`ibmibmibm`. :bug:`506` + :bug:`507` :bug:`1547` :bug:`2333` +- A new :doc:`/plugins/absubmit` lets you run acoustic analysis software and upload the results for others to use. Thanks to :user:`inytar`. :bug:`2253` :bug:`2342` -* :doc:`/plugins/play`: The plugin now provides an importer prompt choice to - play the music you're about to import. Thanks to :user:`diomekes`. - :bug:`2008` :bug:`2360` -* We now use SSL to access Web services whenever possible. That includes - MusicBrainz itself, several album art sources, some lyrics sources, and - other servers. Thanks to :user:`tigranl`. :bug:`2307` -* :doc:`/plugins/random`: A new ``--time`` option lets you generate a random +- :doc:`/plugins/play`: The plugin now provides an importer prompt choice to + play the music you're about to import. Thanks to :user:`diomekes`. :bug:`2008` + :bug:`2360` +- We now use SSL to access Web services whenever possible. That includes + MusicBrainz itself, several album art sources, some lyrics sources, and other + servers. Thanks to :user:`tigranl`. :bug:`2307` +- :doc:`/plugins/random`: A new ``--time`` option lets you generate a random playlist that takes a given amount of time. Thanks to :user:`diomekes`. :bug:`2305` :bug:`2322` Some smaller new features: -* :doc:`/plugins/zero`: A new ``zero`` command manually triggers the zero +- :doc:`/plugins/zero`: A new ``zero`` command manually triggers the zero plugin. Thanks to :user:`SJoshBrown`. :bug:`2274` :bug:`2329` -* :doc:`/plugins/acousticbrainz`: The plugin will avoid re-downloading data - for files that already have it by default. You can override this behavior - using a new ``force`` option. Thanks to :user:`SusannaMaria`. :bug:`2347` - :bug:`2349` -* :doc:`/plugins/bpm`: The ``import.write`` configuration option now - decides whether or not to write tracks after updating their BPM. :bug:`1992` +- :doc:`/plugins/acousticbrainz`: The plugin will avoid re-downloading data for + files that already have it by default. You can override this behavior using a + new ``force`` option. Thanks to :user:`SusannaMaria`. :bug:`2347` :bug:`2349` +- :doc:`/plugins/bpm`: The ``import.write`` configuration option now decides + whether or not to write tracks after updating their BPM. :bug:`1992` And the fixes: -* :doc:`/plugins/bpd`: Fix a crash on non-ASCII MPD commands. :bug:`2332` -* :doc:`/plugins/scrub`: Avoid a crash when files cannot be read or written. +- :doc:`/plugins/bpd`: Fix a crash on non-ASCII MPD commands. :bug:`2332` +- :doc:`/plugins/scrub`: Avoid a crash when files cannot be read or written. :bug:`2351` -* :doc:`/plugins/scrub`: The image type values on scrubbed files are preserved +- :doc:`/plugins/scrub`: The image type values on scrubbed files are preserved instead of being reset to "other." :bug:`2339` -* :doc:`/plugins/web`: Fix a crash on Python 3 when serving files from the +- :doc:`/plugins/web`: Fix a crash on Python 3 when serving files from the filesystem. :bug:`2353` -* :doc:`/plugins/discogs`: Improve the handling of releases that contain +- :doc:`/plugins/discogs`: Improve the handling of releases that contain subtracks. :bug:`2318` -* :doc:`/plugins/discogs`: Fix a crash when a release does not contain format +- :doc:`/plugins/discogs`: Fix a crash when a release does not contain format information, and increase robustness when other fields are missing. :bug:`2302` -* :doc:`/plugins/lyrics`: The plugin now reports a beets-specific User-Agent +- :doc:`/plugins/lyrics`: The plugin now reports a beets-specific User-Agent header when requesting lyrics. :bug:`2357` -* :doc:`/plugins/embyupdate`: The plugin now checks whether an API key or a +- :doc:`/plugins/embyupdate`: The plugin now checks whether an API key or a password is provided in the configuration. -* :doc:`/plugins/play`: The misspelled configuration option - ``warning_treshold`` is no longer supported. +- :doc:`/plugins/play`: The misspelled configuration option ``warning_treshold`` + is no longer supported. For plugin developers: when providing new importer prompt choices (see :ref:`append_prompt_choices`), you can now provide new candidates for the user -to consider. For example, you might provide an alternative strategy for -picking between the available alternatives or for looking up a release on -MusicBrainz. +to consider. For example, you might provide an alternative strategy for picking +between the available alternatives or for looking up a release on MusicBrainz. 1.4.2 (December 16, 2016) ------------------------- This is just a little bug fix release. With 1.4.2, we're also confident enough to recommend that anyone who's interested give Python 3 a try: bugs may still -lurk, but we've deemed things safe enough for broad adoption. If you can, -please install beets with ``pip3`` instead of ``pip2`` this time and let us -know how it goes! +lurk, but we've deemed things safe enough for broad adoption. If you can, please +install beets with ``pip3`` instead of ``pip2`` this time and let us know how it +goes! Here are the fixes: -* :doc:`/plugins/badfiles`: Fix a crash on non-ASCII filenames. :bug:`2299` -* The ``%asciify{}`` path formatting function and the :ref:`asciify-paths` +- :doc:`/plugins/badfiles`: Fix a crash on non-ASCII filenames. :bug:`2299` +- The ``%asciify{}`` path formatting function and the :ref:`asciify-paths` setting properly substitute path separators generated by converting some Unicode characters, such as ½ and ¢, into ASCII. -* :doc:`/plugins/convert`: Fix a logging-related crash when filenames contain +- :doc:`/plugins/convert`: Fix a logging-related crash when filenames contain curly braces. Thanks to :user:`kierdavis`. :bug:`2323` -* We've rolled back some changes to the included zsh completion script that - were causing problems for some users. :bug:`2266` +- We've rolled back some changes to the included zsh completion script that were + causing problems for some users. :bug:`2266` Also, we've removed some special handling for logging in the :doc:`/plugins/discogs` that we believe was unnecessary. If spurious log @@ -2095,98 +1863,94 @@ messages appear in this version, please let us know by filing a bug. ------------------------- Version 1.4 has **alpha-level** Python 3 support. Thanks to the heroic efforts -of :user:`jrobeson`, beets should run both under Python 2.7, as before, and -now under Python 3.4 and above. The support is still new: it undoubtedly -contains bugs, so it may replace all your music with Limp Bizkit---but if -you're brave and you have backups, please try installing on Python 3. Let us -know how it goes. +of :user:`jrobeson`, beets should run both under Python 2.7, as before, and now +under Python 3.4 and above. The support is still new: it undoubtedly contains +bugs, so it may replace all your music with Limp Bizkit---but if you're brave +and you have backups, please try installing on Python 3. Let us know how it +goes. If you package beets for distribution, here's what you'll want to know: -* This version of beets now depends on the `six`_ library. -* We also bumped our minimum required version of `Mutagen`_ to 1.33 (from - 1.27). -* Please don't package beets as a Python 3 application *yet*, even though most +- This version of beets now depends on the six_ library. +- We also bumped our minimum required version of Mutagen_ to 1.33 (from 1.27). +- Please don't package beets as a Python 3 application *yet*, even though most things work under Python 3.4 and later. This version also makes a few changes to the command-line interface and configuration that you may need to know about: -* :doc:`/plugins/duplicates`: The ``duplicates`` command no longer accepts +- :doc:`/plugins/duplicates`: The ``duplicates`` command no longer accepts multiple field arguments in the form ``-k title albumartist album``. Each argument must be prefixed with ``-k``, as in ``-k title -k albumartist -k album``. -* The old top-level ``colors`` configuration option has been removed (the +- The old top-level ``colors`` configuration option has been removed (the setting is now under ``ui``). -* The deprecated ``list_format_album`` and ``list_format_item`` - configuration options have been removed (see :ref:`format_album` and - :ref:`format_item`). +- The deprecated ``list_format_album`` and ``list_format_item`` configuration + options have been removed (see :ref:`format_album` and :ref:`format_item`). The are a few new features: -* :doc:`/plugins/mpdupdate`, :doc:`/plugins/mpdstats`: When the ``host`` option +- :doc:`/plugins/mpdupdate`, :doc:`/plugins/mpdstats`: When the ``host`` option is not set, these plugins will now look for the ``$MPD_HOST`` environment variable before falling back to ``localhost``. Thanks to :user:`tarruda`. :bug:`2175` -* :doc:`/plugins/web`: Added an ``expand`` option to show the items of an - album. :bug:`2050` -* :doc:`/plugins/embyupdate`: The plugin can now use an API key instead of a +- :doc:`/plugins/web`: Added an ``expand`` option to show the items of an album. + :bug:`2050` +- :doc:`/plugins/embyupdate`: The plugin can now use an API key instead of a password to authenticate with Emby. :bug:`2045` :bug:`2117` -* :doc:`/plugins/acousticbrainz`: The plugin now adds a ``bpm`` field. -* ``beet --version`` now includes the Python version used to run beets. -* :doc:`/reference/pathformat` can now include unescaped commas (``,``) when +- :doc:`/plugins/acousticbrainz`: The plugin now adds a ``bpm`` field. +- ``beet --version`` now includes the Python version used to run beets. +- :doc:`/reference/pathformat` can now include unescaped commas (``,``) when they are not part of a function call. :bug:`2166` :bug:`2213` -* The :ref:`update-cmd` command takes a new ``-F`` flag to specify the fields - to update. Thanks to :user:`dangmai`. :bug:`2229` :bug:`2231` +- The :ref:`update-cmd` command takes a new ``-F`` flag to specify the fields to + update. Thanks to :user:`dangmai`. :bug:`2229` :bug:`2231` And there are a few bug fixes too: -* :doc:`/plugins/convert`: The plugin no longer asks for confirmation if the +- :doc:`/plugins/convert`: The plugin no longer asks for confirmation if the query did not return anything to convert. :bug:`2260` :bug:`2262` -* :doc:`/plugins/embedart`: The plugin now uses ``jpg`` as an extension rather - than ``jpeg``, to ensure consistency with the :doc:`plugins/fetchart`. - Thanks to :user:`tweitzel`. :bug:`2254` :bug:`2255` -* :doc:`/plugins/embedart`: The plugin now works for all jpeg files, including - those that are only recognizable by their magic bytes. - :bug:`1545` :bug:`2255` -* :doc:`/plugins/web`: The JSON output is no longer pretty-printed (for a - space savings). :bug:`2050` -* :doc:`/plugins/permissions`: Fix a regression in the previous release where +- :doc:`/plugins/embedart`: The plugin now uses ``jpg`` as an extension rather + than ``jpeg``, to ensure consistency with the :doc:`plugins/fetchart`. Thanks + to :user:`tweitzel`. :bug:`2254` :bug:`2255` +- :doc:`/plugins/embedart`: The plugin now works for all jpeg files, including + those that are only recognizable by their magic bytes. :bug:`1545` :bug:`2255` +- :doc:`/plugins/web`: The JSON output is no longer pretty-printed (for a space + savings). :bug:`2050` +- :doc:`/plugins/permissions`: Fix a regression in the previous release where the plugin would always fail to set permissions (and log a warning). :bug:`2089` -* :doc:`/plugins/beatport`: Use track numbers from Beatport (instead of - determining them from the order of tracks) and set the `medium_index` - value. -* With :ref:`per_disc_numbering` enabled, some metadata sources (notably, the +- :doc:`/plugins/beatport`: Use track numbers from Beatport (instead of + determining them from the order of tracks) and set the ``medium_index`` value. +- With :ref:`per_disc_numbering` enabled, some metadata sources (notably, the :doc:`/plugins/beatport`) would not set the track number at all. This is fixed. :bug:`2085` -* :doc:`/plugins/play`: Fix ``$args`` getting passed verbatim to the play +- :doc:`/plugins/play`: Fix ``$args`` getting passed verbatim to the play command if it was set in the configuration but ``-A`` or ``--args`` was omitted. -* With :ref:`ignore_hidden` enabled, non-UTF-8 filenames would cause a crash. +- With :ref:`ignore_hidden` enabled, non-UTF-8 filenames would cause a crash. This is fixed. :bug:`2168` -* :doc:`/plugins/embyupdate`: Fixes authentication header problem that caused - a problem that it was not possible to get tokens from the Emby API. -* :doc:`/plugins/lyrics`: Some titles use a colon to separate the main title - from a subtitle. To find more matches, the plugin now also searches for - lyrics using the part part preceding the colon character. :bug:`2206` -* Fix a crash when a query uses a date field and some items are missing that +- :doc:`/plugins/embyupdate`: Fixes authentication header problem that caused a + problem that it was not possible to get tokens from the Emby API. +- :doc:`/plugins/lyrics`: Some titles use a colon to separate the main title + from a subtitle. To find more matches, the plugin now also searches for lyrics + using the part part preceding the colon character. :bug:`2206` +- Fix a crash when a query uses a date field and some items are missing that field. :bug:`1938` -* :doc:`/plugins/discogs`: Subtracks are now detected and combined into a - single track, two-sided mediums are treated as single discs, and tracks - have ``media``, ``medium_total`` and ``medium`` set correctly. :bug:`2222` +- :doc:`/plugins/discogs`: Subtracks are now detected and combined into a single + track, two-sided mediums are treated as single discs, and tracks have + ``media``, ``medium_total`` and ``medium`` set correctly. :bug:`2222` :bug:`2228`. -* :doc:`/plugins/missing`: ``missing`` is now treated as an integer, allowing +- :doc:`/plugins/missing`: ``missing`` is now treated as an integer, allowing the use of (for example) ranges in queries. -* :doc:`/plugins/smartplaylist`: Playlist names will be sanitized to - ensure valid filenames. :bug:`2258` -* The ID3 APIC tag now uses the Latin-1 encoding when possible instead of a +- :doc:`/plugins/smartplaylist`: Playlist names will be sanitized to ensure + valid filenames. :bug:`2258` +- The ID3 APIC tag now uses the Latin-1 encoding when possible instead of a Unicode encoding. This should increase compatibility with other software, especially with iTunes and when using ID3v2.3. Thanks to :user:`lazka`. :bug:`899` :bug:`2264` :bug:`2270` -The last release, 1.3.19, also erroneously reported its version as "1.3.18" -when you typed ``beet version``. This has been corrected. +The last release, 1.3.19, also erroneously reported its version as "1.3.18" when +you typed ``beet version``. This has been corrected. .. _six: https://pypi.org/project/six/ @@ -2202,42 +1966,42 @@ this herald a new age of cross-platform reliability for beets. New features: -* :doc:`/plugins/beatport`: This metadata source plugin has arisen from the +- :doc:`/plugins/beatport`: This metadata source plugin has arisen from the dead! It now works with Beatport's new OAuth-based API. Thanks to :user:`jbaiter`. :bug:`1989` :bug:`2067` -* :doc:`/plugins/bpd`: The plugin now uses the modern GStreamer 1.0 instead of +- :doc:`/plugins/bpd`: The plugin now uses the modern GStreamer 1.0 instead of the old 0.10. Thanks to :user:`philippbeckmann`. :bug:`2057` :bug:`2062` -* A new ``--force`` option for the :ref:`remove-cmd` command allows removal of +- A new ``--force`` option for the :ref:`remove-cmd` command allows removal of items without prompting beforehand. :bug:`2042` -* A new :ref:`duplicate_action` importer config option controls how duplicate +- A new :ref:`duplicate_action` importer config option controls how duplicate albums or tracks treated in import task. :bug:`185` Some fixes for Windows: -* Queries are now detected as paths when they contain backslashes (in - addition to forward slashes). This only applies on Windows. -* :doc:`/plugins/embedart`: Image similarity comparison with ImageMagick - should now work on Windows. -* :doc:`/plugins/fetchart`: The plugin should work more reliably with - non-ASCII paths. +- Queries are now detected as paths when they contain backslashes (in addition + to forward slashes). This only applies on Windows. +- :doc:`/plugins/embedart`: Image similarity comparison with ImageMagick should + now work on Windows. +- :doc:`/plugins/fetchart`: The plugin should work more reliably with non-ASCII + paths. And other fixes: -* :doc:`/plugins/replaygain`: The ``bs1770gain`` backend now correctly - calculates sample peak instead of true peak. This comes with a major - speed increase. :bug:`2031` -* :doc:`/plugins/lyrics`: Avoid a crash and a spurious warning introduced in - the last version about a Google API key, which appeared even when you hadn't +- :doc:`/plugins/replaygain`: The ``bs1770gain`` backend now correctly + calculates sample peak instead of true peak. This comes with a major speed + increase. :bug:`2031` +- :doc:`/plugins/lyrics`: Avoid a crash and a spurious warning introduced in the + last version about a Google API key, which appeared even when you hadn't enabled the Google lyrics source. -* Fix a hard-coded path to ``bash-completion`` to work better with Homebrew +- Fix a hard-coded path to ``bash-completion`` to work better with Homebrew installations. Thanks to :user:`bismark`. :bug:`2038` -* Fix a crash introduced in the previous version when the standard input was +- Fix a crash introduced in the previous version when the standard input was connected to a Unix pipe. :bug:`2041` -* Fix a crash when specifying non-ASCII format strings on the command line - with the ``-f`` option for many commands. :bug:`2063` -* :doc:`/plugins/fetchart`: Determine the file extension for downloaded images - based on the image's magic bytes. The plugin prints a warning if result is - not consistent with the server-supplied ``Content-Type`` header. In previous +- Fix a crash when specifying non-ASCII format strings on the command line with + the ``-f`` option for many commands. :bug:`2063` +- :doc:`/plugins/fetchart`: Determine the file extension for downloaded images + based on the image's magic bytes. The plugin prints a warning if result is not + consistent with the server-supplied ``Content-Type`` header. In previous versions, the plugin would use a ``.jpg`` extension for all images. :bug:`2053` @@ -2249,273 +2013,270 @@ command-line tools and an :doc:`/plugins/export` that can dump data from the beets database as JSON. You can also automatically translate lyrics using a machine translation service. -The ``echonest`` plugin has been removed in this version because the API it -used is `shutting down`_. You might want to try the -:doc:`/plugins/acousticbrainz` instead. +The ``echonest`` plugin has been removed in this version because the API it used +is `shutting down`_. You might want to try the :doc:`/plugins/acousticbrainz` +instead. .. _shutting down: https://developer.spotify.com/news-stories/2016/03/29/api-improvements-update/ Some of the larger new features: -* The new :doc:`/plugins/hook` lets you execute commands in response to beets +- The new :doc:`/plugins/hook` lets you execute commands in response to beets events. -* The new :doc:`/plugins/export` can export data from beets' database as - JSON. Thanks to :user:`GuilhermeHideki`. -* :doc:`/plugins/lyrics`: The plugin can now translate the fetched lyrics to +- The new :doc:`/plugins/export` can export data from beets' database as JSON. + Thanks to :user:`GuilhermeHideki`. +- :doc:`/plugins/lyrics`: The plugin can now translate the fetched lyrics to your native language using the Bing translation API. Thanks to :user:`Kraymer`. -* :doc:`/plugins/fetchart`: Album art can now be fetched from `fanart.tv`_. +- :doc:`/plugins/fetchart`: Album art can now be fetched from fanart.tv_. Smaller new things: -* There are two new functions available in templates: ``%first`` and ``%ifdef``. +- There are two new functions available in templates: ``%first`` and ``%ifdef``. See :ref:`template-functions`. -* :doc:`/plugins/convert`: A new `album_art_maxwidth` setting lets you resize +- :doc:`/plugins/convert`: A new ``album_art_maxwidth`` setting lets you resize album art while copying it. -* :doc:`/plugins/convert`: The `extension` setting is now optional for +- :doc:`/plugins/convert`: The ``extension`` setting is now optional for conversion formats. By default, the extension is the same as the name of the configured format. -* :doc:`/plugins/importadded`: A new `preserve_write_mtimes` option - lets you preserve mtime of files even when beets updates their metadata. -* :doc:`/plugins/fetchart`: The `enforce_ratio` option now lets you tolerate - images that are *almost* square but differ slightly from an exact 1:1 - aspect ratio. -* :doc:`/plugins/fetchart`: The plugin can now optionally save the artwork's +- :doc:`/plugins/importadded`: A new ``preserve_write_mtimes`` option lets you + preserve mtime of files even when beets updates their metadata. +- :doc:`/plugins/fetchart`: The ``enforce_ratio`` option now lets you tolerate + images that are *almost* square but differ slightly from an exact 1:1 aspect + ratio. +- :doc:`/plugins/fetchart`: The plugin can now optionally save the artwork's source in an attribute in the database. -* The :ref:`terminal_encoding` configuration option can now also override the +- The :ref:`terminal_encoding` configuration option can now also override the *input* encoding. (Previously, it only affected the encoding of the standard *output* stream.) -* A new :ref:`ignore_hidden` configuration option lets you ignore files that +- A new :ref:`ignore_hidden` configuration option lets you ignore files that your OS marks as invisible. -* :doc:`/plugins/web`: A new `values` endpoint lets you get the distinct values - of a field. Thanks to :user:`sumpfralle`. :bug:`2010` +- :doc:`/plugins/web`: A new ``values`` endpoint lets you get the distinct + values of a field. Thanks to :user:`sumpfralle`. :bug:`2010` .. _fanart.tv: https://fanart.tv/ Fixes: -* Fix a problem with the :ref:`stats-cmd` command in exact mode when filenames +- Fix a problem with the :ref:`stats-cmd` command in exact mode when filenames on Windows use non-ASCII characters. :bug:`1891` -* Fix a crash when iTunes Sound Check tags contained invalid data. :bug:`1895` -* :doc:`/plugins/mbcollection`: The plugin now redacts your MusicBrainz - password in the ``beet config`` output. :bug:`1907` -* :doc:`/plugins/scrub`: Fix an occasional problem where scrubbing on import +- Fix a crash when iTunes Sound Check tags contained invalid data. :bug:`1895` +- :doc:`/plugins/mbcollection`: The plugin now redacts your MusicBrainz password + in the ``beet config`` output. :bug:`1907` +- :doc:`/plugins/scrub`: Fix an occasional problem where scrubbing on import could undo the :ref:`id3v23` setting. :bug:`1903` -* :doc:`/plugins/lyrics`: Add compatibility with some changes to the - LyricsWiki page markup. :bug:`1912` :bug:`1909` -* :doc:`/plugins/lyrics`: Fix retrieval from Musixmatch by improving the way - we guess the URL for lyrics on that service. :bug:`1880` -* :doc:`/plugins/edit`: Fail gracefully when the configured text editor - command can't be invoked. :bug:`1927` -* :doc:`/plugins/fetchart`: Fix a crash in the Wikipedia backend on non-ASCII +- :doc:`/plugins/lyrics`: Add compatibility with some changes to the LyricsWiki + page markup. :bug:`1912` :bug:`1909` +- :doc:`/plugins/lyrics`: Fix retrieval from Musixmatch by improving the way we + guess the URL for lyrics on that service. :bug:`1880` +- :doc:`/plugins/edit`: Fail gracefully when the configured text editor command + can't be invoked. :bug:`1927` +- :doc:`/plugins/fetchart`: Fix a crash in the Wikipedia backend on non-ASCII artist and album names. :bug:`1960` -* :doc:`/plugins/convert`: Change the default `ogg` encoding quality from 2 to - 3 (to fit the default from the `oggenc(1)` manpage). :bug:`1982` -* :doc:`/plugins/convert`: The `never_convert_lossy_files` option now +- :doc:`/plugins/convert`: Change the default ``ogg`` encoding quality from 2 to + 3 (to fit the default from the ``oggenc(1)`` manpage). :bug:`1982` +- :doc:`/plugins/convert`: The ``never_convert_lossy_files`` option now considers AIFF a lossless format. :bug:`2005` -* :doc:`/plugins/web`: A proper 404 error, instead of an internal exception, - is returned when missing album art is requested. Thanks to - :user:`sumpfralle`. :bug:`2011` -* Tolerate more malformed floating-point numbers in metadata tags. :bug:`2014` -* The :ref:`ignore` configuration option now includes the ``lost+found`` +- :doc:`/plugins/web`: A proper 404 error, instead of an internal exception, is + returned when missing album art is requested. Thanks to :user:`sumpfralle`. + :bug:`2011` +- Tolerate more malformed floating-point numbers in metadata tags. :bug:`2014` +- The :ref:`ignore` configuration option now includes the ``lost+found`` directory by default. -* :doc:`/plugins/acousticbrainz`: AcousticBrainz lookups are now done over +- :doc:`/plugins/acousticbrainz`: AcousticBrainz lookups are now done over HTTPS. Thanks to :user:`Freso`. :bug:`2007` 1.3.17 (February 7, 2016) ------------------------- This release introduces one new plugin to fetch audio information from the -`AcousticBrainz`_ project and another plugin to make it easier to submit your -handcrafted metadata back to MusicBrainz. -The importer also gained two oft-requested features: a way to skip the initial -search process by specifying an ID ahead of time, and a way to *manually* -provide metadata in the middle of the import process (via the -:doc:`/plugins/edit`). +AcousticBrainz_ project and another plugin to make it easier to submit your +handcrafted metadata back to MusicBrainz. The importer also gained two +oft-requested features: a way to skip the initial search process by specifying +an ID ahead of time, and a way to *manually* provide metadata in the middle of +the import process (via the :doc:`/plugins/edit`). -Also, as of this release, the beets project has some new Internet homes! Our -new domain name is `beets.io`_, and we have a shiny new GitHub organization: -`beetbox`_. +Also, as of this release, the beets project has some new Internet homes! Our new +domain name is beets.io_, and we have a shiny new GitHub organization: beetbox_. Here are the big new features: -* A new :doc:`/plugins/acousticbrainz` fetches acoustic-analysis information - from the `AcousticBrainz`_ project. Thanks to :user:`opatel99`, and thanks - to `Google Code-In`_! :bug:`1784` -* A new :doc:`/plugins/mbsubmit` lets you print music's current metadata in a +- A new :doc:`/plugins/acousticbrainz` fetches acoustic-analysis information + from the AcousticBrainz_ project. Thanks to :user:`opatel99`, and thanks to + `Google Code-In`_! :bug:`1784` +- A new :doc:`/plugins/mbsubmit` lets you print music's current metadata in a format that the MusicBrainz data parser can understand. You can trigger it during an interactive import session. :bug:`1779` -* A new ``--search-id`` importer option lets you manually specify - IDs (i.e., MBIDs or Discogs IDs) for imported music. Doing this skips the - initial candidate search, which can be important for huge albums where this - initial lookup is slow. - Also, the ``enter Id`` prompt choice now accepts several IDs, separated by - spaces. :bug:`1808` -* :doc:`/plugins/edit`: You can now edit metadata *on the fly* during the - import process. The plugin provides two new interactive options: one to edit - *your music's* metadata, and one to edit the *matched metadata* retrieved - from MusicBrainz (or another data source). This feature is still in its - early stages, so please send feedback if you find anything missing. - :bug:`1846` :bug:`396` +- A new ``--search-id`` importer option lets you manually specify IDs (i.e., + MBIDs or Discogs IDs) for imported music. Doing this skips the initial + candidate search, which can be important for huge albums where this initial + lookup is slow. Also, the ``enter Id`` prompt choice now accepts several IDs, + separated by spaces. :bug:`1808` +- :doc:`/plugins/edit`: You can now edit metadata *on the fly* during the import + process. The plugin provides two new interactive options: one to edit *your + music's* metadata, and one to edit the *matched metadata* retrieved from + MusicBrainz (or another data source). This feature is still in its early + stages, so please send feedback if you find anything missing. :bug:`1846` + :bug:`396` There are even more new features: -* :doc:`/plugins/fetchart`: The Google Images backend has been restored. It - now requires an API key from Google. Thanks to :user:`lcharlick`. - :bug:`1778` -* :doc:`/plugins/info`: A new option will print only fields' names and not - their values. Thanks to :user:`GuilhermeHideki`. :bug:`1812` -* The :ref:`fields-cmd` command now displays flexible attributes. - Thanks to :user:`GuilhermeHideki`. :bug:`1818` -* The :ref:`modify-cmd` command lets you interactively select which albums or +- :doc:`/plugins/fetchart`: The Google Images backend has been restored. It now + requires an API key from Google. Thanks to :user:`lcharlick`. :bug:`1778` +- :doc:`/plugins/info`: A new option will print only fields' names and not their + values. Thanks to :user:`GuilhermeHideki`. :bug:`1812` +- The :ref:`fields-cmd` command now displays flexible attributes. Thanks to + :user:`GuilhermeHideki`. :bug:`1818` +- The :ref:`modify-cmd` command lets you interactively select which albums or items you want to change. :bug:`1843` -* The :ref:`move-cmd` command gained a new ``--timid`` flag to print and - confirm which files you want to move. :bug:`1843` -* The :ref:`move-cmd` command no longer prints filenames for files that - don't actually need to be moved. :bug:`1583` +- The :ref:`move-cmd` command gained a new ``--timid`` flag to print and confirm + which files you want to move. :bug:`1843` +- The :ref:`move-cmd` command no longer prints filenames for files that don't + actually need to be moved. :bug:`1583` -.. _Google Code-In: https://codein.withgoogle.com/ -.. _AcousticBrainz: https://acousticbrainz.org/ +.. _acousticbrainz: https://acousticbrainz.org/ + +.. _google code-in: https://codein.withgoogle.com/ Fixes: -* :doc:`/plugins/play`: Fix a regression in the last version where there was - no default command. :bug:`1793` -* :doc:`/plugins/lastimport`: The plugin now works again after being broken by +- :doc:`/plugins/play`: Fix a regression in the last version where there was no + default command. :bug:`1793` +- :doc:`/plugins/lastimport`: The plugin now works again after being broken by some unannounced changes to the Last.fm API. :bug:`1574` -* :doc:`/plugins/play`: Fixed a typo in a configuration option. The option is - now ``warning_threshold`` instead of ``warning_treshold``, but we kept the - old name around for compatibility. Thanks to :user:`JesseWeinstein`. - :bug:`1802` :bug:`1803` -* :doc:`/plugins/edit`: Editing metadata now moves files, when appropriate - (like the :ref:`modify-cmd` command). :bug:`1804` -* The :ref:`stats-cmd` command no longer crashes when files are missing or +- :doc:`/plugins/play`: Fixed a typo in a configuration option. The option is + now ``warning_threshold`` instead of ``warning_treshold``, but we kept the old + name around for compatibility. Thanks to :user:`JesseWeinstein`. :bug:`1802` + :bug:`1803` +- :doc:`/plugins/edit`: Editing metadata now moves files, when appropriate (like + the :ref:`modify-cmd` command). :bug:`1804` +- The :ref:`stats-cmd` command no longer crashes when files are missing or inaccessible. :bug:`1806` -* :doc:`/plugins/fetchart`: Possibly fix a Unicode-related crash when using - some versions of pyOpenSSL. :bug:`1805` -* :doc:`/plugins/replaygain`: Fix an intermittent crash with the GStreamer +- :doc:`/plugins/fetchart`: Possibly fix a Unicode-related crash when using some + versions of pyOpenSSL. :bug:`1805` +- :doc:`/plugins/replaygain`: Fix an intermittent crash with the GStreamer backend. :bug:`1855` -* :doc:`/plugins/lastimport`: The plugin now works with the beets API key by +- :doc:`/plugins/lastimport`: The plugin now works with the beets API key by default. You can still provide a different key the configuration. -* :doc:`/plugins/replaygain`: Fix a crash using the Python Audio Tools - backend. :bug:`1873` +- :doc:`/plugins/replaygain`: Fix a crash using the Python Audio Tools backend. + :bug:`1873` + +.. _beetbox: https://github.com/beetbox .. _beets.io: https://beets.io/ -.. _Beetbox: https://github.com/beetbox 1.3.16 (December 28, 2015) -------------------------- The big news in this release is a new :doc:`interactive editor plugin -</plugins/edit>`. It's really nifty: you can now change your music's metadata -by making changes in a visual text editor, which can sometimes be far more +</plugins/edit>`. It's really nifty: you can now change your music's metadata by +making changes in a visual text editor, which can sometimes be far more efficient than the built-in :ref:`modify-cmd` command. No more carefully retyping the same artist name with slight capitalization changes. -This version also adds an oft-requested "not" operator to beets' queries, so -you can exclude music from any operation. It also brings friendlier formatting -(and querying!) of song durations. +This version also adds an oft-requested "not" operator to beets' queries, so you +can exclude music from any operation. It also brings friendlier formatting (and +querying!) of song durations. The big new stuff: -* A new :doc:`/plugins/edit` lets you manually edit your music's metadata - using your favorite text editor. :bug:`164` :bug:`1706` -* Queries can now use "not" logic. Type a ``^`` before part of a query to - *exclude* matching music from the results. For example, ``beet list -a - beatles ^album:1`` will find all your albums by the Beatles except for their - singles compilation, "1." See :ref:`not_query`. :bug:`819` :bug:`1728` -* A new :doc:`/plugins/embyupdate` can trigger a library refresh on an `Emby`_ +- A new :doc:`/plugins/edit` lets you manually edit your music's metadata using + your favorite text editor. :bug:`164` :bug:`1706` +- Queries can now use "not" logic. Type a ``^`` before part of a query to + *exclude* matching music from the results. For example, ``beet list -a beatles + ^album:1`` will find all your albums by the Beatles except for their singles + compilation, "1." See :ref:`not_query`. :bug:`819` :bug:`1728` +- A new :doc:`/plugins/embyupdate` can trigger a library refresh on an Emby_ server when your beets database changes. -* Track length is now displayed as "M:SS" rather than a raw number of seconds. +- Track length is now displayed as "M:SS" rather than a raw number of seconds. Queries on track length also accept this format: for example, ``beet list - length:5:30..`` will find all your tracks that have a duration over 5 - minutes and 30 seconds. You can turn off this new behavior using the + length:5:30..`` will find all your tracks that have a duration over 5 minutes + and 30 seconds. You can turn off this new behavior using the ``format_raw_length`` configuration option. :bug:`1749` Smaller changes: -* Three commands, ``modify``, ``update``, and ``mbsync``, would previously - move files by default after changing their metadata. Now, these commands - will only move files if you have the :ref:`config-import-copy` or - :ref:`config-import-move` options enabled in your importer configuration. - This way, if you configure the importer not to touch your filenames, other - commands will respect that decision by default too. Each command also - sprouted a ``--move`` command-line option to override this default (in - addition to the ``--nomove`` flag they already had). :bug:`1697` -* A new configuration option, ``va_name``, controls the album artist name for +- Three commands, ``modify``, ``update``, and ``mbsync``, would previously move + files by default after changing their metadata. Now, these commands will only + move files if you have the :ref:`config-import-copy` or + :ref:`config-import-move` options enabled in your importer configuration. This + way, if you configure the importer not to touch your filenames, other commands + will respect that decision by default too. Each command also sprouted a + ``--move`` command-line option to override this default (in addition to the + ``--nomove`` flag they already had). :bug:`1697` +- A new configuration option, ``va_name``, controls the album artist name for various-artists albums. The setting defaults to "Various Artists," the MusicBrainz standard. In order to match MusicBrainz, the :doc:`/plugins/discogs` also adopts the same setting. -* :doc:`/plugins/info`: The ``info`` command now accepts a ``-f/--format`` +- :doc:`/plugins/info`: The ``info`` command now accepts a ``-f/--format`` option for customizing how items are displayed, just like the built-in ``list`` command. :bug:`1737` Some changes for developers: -* Two new :ref:`plugin hooks <plugin_events>`, ``albuminfo_received`` and +- Two new :ref:`plugin hooks <plugin_events>`, ``albuminfo_received`` and ``trackinfo_received``, let plugins intercept metadata as soon as it is received, before it is applied to music in the database. :bug:`872` -* Plugins can now add options to the interactive importer prompts. See +- Plugins can now add options to the interactive importer prompts. See :ref:`append_prompt_choices`. :bug:`1758` Fixes: -* :doc:`/plugins/plexupdate`: Fix a crash when Plex libraries use non-ASCII +- :doc:`/plugins/plexupdate`: Fix a crash when Plex libraries use non-ASCII collection names. :bug:`1649` -* :doc:`/plugins/discogs`: Maybe fix a crash when using some versions of the +- :doc:`/plugins/discogs`: Maybe fix a crash when using some versions of the ``requests`` library. :bug:`1656` -* Fix a race in the importer when importing two albums with the same artist - and name in quick succession. The importer would fail to detect them as - duplicates, claiming that there were "empty albums" in the database even - when there were not. :bug:`1652` -* :doc:`plugins/lastgenre`: Clean up the reggae-related genres somewhat. - Thanks to :user:`Freso`. :bug:`1661` -* The importer now correctly moves album art files when re-importing. - :bug:`314` -* :doc:`/plugins/fetchart`: In auto mode, the plugin now skips albums that +- Fix a race in the importer when importing two albums with the same artist and + name in quick succession. The importer would fail to detect them as + duplicates, claiming that there were "empty albums" in the database even when + there were not. :bug:`1652` +- :doc:`plugins/lastgenre`: Clean up the reggae-related genres somewhat. Thanks + to :user:`Freso`. :bug:`1661` +- The importer now correctly moves album art files when re-importing. :bug:`314` +- :doc:`/plugins/fetchart`: In auto mode, the plugin now skips albums that already have art attached to them so as not to interfere with re-imports. :bug:`314` -* :doc:`plugins/fetchart`: The plugin now only resizes album art if necessary, +- :doc:`plugins/fetchart`: The plugin now only resizes album art if necessary, rather than always by default. :bug:`1264` -* :doc:`plugins/fetchart`: Fix a bug where a database reference to a +- :doc:`plugins/fetchart`: Fix a bug where a database reference to a non-existent album art file would prevent the command from fetching new art. :bug:`1126` -* :doc:`/plugins/thumbnails`: Fix a crash with Unicode paths. :bug:`1686` -* :doc:`/plugins/embedart`: The ``remove_art_file`` option now works on import +- :doc:`/plugins/thumbnails`: Fix a crash with Unicode paths. :bug:`1686` +- :doc:`/plugins/embedart`: The ``remove_art_file`` option now works on import (as well as with the explicit command). :bug:`1662` :bug:`1675` -* :doc:`/plugins/metasync`: Fix a crash when syncing with recent versions of +- :doc:`/plugins/metasync`: Fix a crash when syncing with recent versions of iTunes. :bug:`1700` -* :doc:`/plugins/duplicates`: Fix a crash when merging items. :bug:`1699` -* :doc:`/plugins/smartplaylist`: More gracefully handle malformed queries and +- :doc:`/plugins/duplicates`: Fix a crash when merging items. :bug:`1699` +- :doc:`/plugins/smartplaylist`: More gracefully handle malformed queries and missing configuration. -* Fix a crash with some files with unreadable iTunes SoundCheck metadata. +- Fix a crash with some files with unreadable iTunes SoundCheck metadata. :bug:`1666` -* :doc:`/plugins/thumbnails`: Fix a nasty segmentation fault crash that arose +- :doc:`/plugins/thumbnails`: Fix a nasty segmentation fault crash that arose with some library versions. :bug:`1433` -* :doc:`/plugins/convert`: Fix a crash with Unicode paths in ``--pretend`` - mode. :bug:`1735` -* Fix a crash when sorting by nonexistent fields on queries. :bug:`1734` -* Probably fix some mysterious errors when dealing with images using - ImageMagick on Windows. :bug:`1721` -* Fix a crash when writing some Unicode comment strings to MP3s that used - older encodings. The encoding is now always updated to UTF-8. :bug:`879` -* :doc:`/plugins/fetchart`: The Google Images backend has been removed. It - used an API that has been shut down. :bug:`1760` -* :doc:`/plugins/lyrics`: Fix a crash in the Google backend when searching for +- :doc:`/plugins/convert`: Fix a crash with Unicode paths in ``--pretend`` mode. + :bug:`1735` +- Fix a crash when sorting by nonexistent fields on queries. :bug:`1734` +- Probably fix some mysterious errors when dealing with images using ImageMagick + on Windows. :bug:`1721` +- Fix a crash when writing some Unicode comment strings to MP3s that used older + encodings. The encoding is now always updated to UTF-8. :bug:`879` +- :doc:`/plugins/fetchart`: The Google Images backend has been removed. It used + an API that has been shut down. :bug:`1760` +- :doc:`/plugins/lyrics`: Fix a crash in the Google backend when searching for bands with regular-expression characters in their names, like Sunn O))). :bug:`1673` -* :doc:`/plugins/scrub`: In ``auto`` mode, the plugin now *actually* only - scrubs files on import, as the documentation always claimed it did---not - every time files were written, as it previously did. :bug:`1657` -* :doc:`/plugins/scrub`: Also in ``auto`` mode, album art is now correctly +- :doc:`/plugins/scrub`: In ``auto`` mode, the plugin now *actually* only scrubs + files on import, as the documentation always claimed it did---not every time + files were written, as it previously did. :bug:`1657` +- :doc:`/plugins/scrub`: Also in ``auto`` mode, album art is now correctly restored. :bug:`1657` -* Possibly allow flexible attributes to be used with the ``%aunique`` template +- Possibly allow flexible attributes to be used with the ``%aunique`` template function. :bug:`1775` -* :doc:`/plugins/lyrics`: The Genius backend is now more robust to - communication errors. The backend has also been disabled by default, since - the API it depends on is currently down. :bug:`1770` +- :doc:`/plugins/lyrics`: The Genius backend is now more robust to communication + errors. The backend has also been disabled by default, since the API it + depends on is currently down. :bug:`1770` -.. _Emby: https://emby.media +.. _emby: https://emby.media 1.3.15 (October 17, 2015) ------------------------- @@ -2523,169 +2284,167 @@ Fixes: This release adds a new plugin for checking file quality and a new source for lyrics. The larger features are: -* A new :doc:`/plugins/badfiles` helps you scan for corruption in your music +- A new :doc:`/plugins/badfiles` helps you scan for corruption in your music collection. Thanks to :user:`fxthomas`. :bug:`1568` -* :doc:`/plugins/lyrics`: You can now fetch lyrics from Genius.com. - Thanks to :user:`sadatay`. :bug:`1626` :bug:`1639` -* :doc:`/plugins/zero`: The plugin can now use a "whitelist" policy as an +- :doc:`/plugins/lyrics`: You can now fetch lyrics from Genius.com. Thanks to + :user:`sadatay`. :bug:`1626` :bug:`1639` +- :doc:`/plugins/zero`: The plugin can now use a "whitelist" policy as an alternative to the (default) "blacklist" mode. Thanks to :user:`adkow`. :bug:`1621` :bug:`1641` And there are smaller new features too: -* Add new color aliases for standard terminal color names (e.g., cyan and +- Add new color aliases for standard terminal color names (e.g., cyan and magenta). Thanks to :user:`mathstuf`. :bug:`1548` -* :doc:`/plugins/play`: A new ``--args`` option lets you specify options for - the player command. :bug:`1532` -* :doc:`/plugins/play`: A new ``raw`` configuration option lets the command - work with players (such as VLC) that expect music filenames as arguments, - rather than in a playlist. Thanks to :user:`nathdwek`. :bug:`1578` -* :doc:`/plugins/play`: You can now configure the number of tracks that - trigger a "lots of music" warning. :bug:`1577` -* :doc:`/plugins/embedart`: A new ``remove_art_file`` option lets you clean up +- :doc:`/plugins/play`: A new ``--args`` option lets you specify options for the + player command. :bug:`1532` +- :doc:`/plugins/play`: A new ``raw`` configuration option lets the command work + with players (such as VLC) that expect music filenames as arguments, rather + than in a playlist. Thanks to :user:`nathdwek`. :bug:`1578` +- :doc:`/plugins/play`: You can now configure the number of tracks that trigger + a "lots of music" warning. :bug:`1577` +- :doc:`/plugins/embedart`: A new ``remove_art_file`` option lets you clean up if you prefer *only* embedded album art. Thanks to :user:`jackwilsdon`. :bug:`1591` :bug:`733` -* :doc:`/plugins/plexupdate`: A new ``library_name`` option allows you to select +- :doc:`/plugins/plexupdate`: A new ``library_name`` option allows you to select which Plex library to update. :bug:`1572` :bug:`1595` -* A new ``include`` option lets you import external configuration files. +- A new ``include`` option lets you import external configuration files. This release has plenty of fixes: -* :doc:`/plugins/lastgenre`: Fix a bug that prevented tag popularity from - being considered. Thanks to :user:`svoos`. :bug:`1559` -* Fixed a bug where plugins wouldn't be notified of the deletion of an item's +- :doc:`/plugins/lastgenre`: Fix a bug that prevented tag popularity from being + considered. Thanks to :user:`svoos`. :bug:`1559` +- Fixed a bug where plugins wouldn't be notified of the deletion of an item's art, for example with the ``clearart`` command from the :doc:`/plugins/embedart`. Thanks to :user:`nathdwek`. :bug:`1565` -* :doc:`/plugins/fetchart`: The Google Images source is disabled by default - (as it was before beets 1.3.9), as is the Wikipedia source (which was - causing lots of unnecessary delays due to DBpedia downtime). To re-enable - these sources, add ``wikipedia google`` to your ``sources`` configuration - option. -* The :ref:`list-cmd` command's help output now has a small query and format +- :doc:`/plugins/fetchart`: The Google Images source is disabled by default (as + it was before beets 1.3.9), as is the Wikipedia source (which was causing lots + of unnecessary delays due to DBpedia downtime). To re-enable these sources, + add ``wikipedia google`` to your ``sources`` configuration option. +- The :ref:`list-cmd` command's help output now has a small query and format string example. Thanks to :user:`pkess`. :bug:`1582` -* :doc:`/plugins/fetchart`: The plugin now fetches PNGs but not GIFs. (It - still fetches JPEGs.) This avoids an error when trying to embed images, - since not all formats support GIFs. :bug:`1588` -* Date fields are now written in the correct order (year-month-day), which +- :doc:`/plugins/fetchart`: The plugin now fetches PNGs but not GIFs. (It still + fetches JPEGs.) This avoids an error when trying to embed images, since not + all formats support GIFs. :bug:`1588` +- Date fields are now written in the correct order (year-month-day), which eliminates an intermittent bug where the latter two fields would not get written to files. Thanks to :user:`jdetrey`. :bug:`1303` :bug:`1589` -* :doc:`/plugins/replaygain`: Avoid a crash when the PyAudioTools backend +- :doc:`/plugins/replaygain`: Avoid a crash when the PyAudioTools backend encounters an error. :bug:`1592` -* The case sensitivity of path queries is more useful now: rather than just +- The case sensitivity of path queries is more useful now: rather than just guessing based on the platform, we now check the case sensitivity of your filesystem. :bug:`1586` -* Case-insensitive path queries might have returned nothing because of a - wrong SQL query. -* Fix a crash when a query contains a "+" or "-" alone in a component. +- Case-insensitive path queries might have returned nothing because of a wrong + SQL query. +- Fix a crash when a query contains a "+" or "-" alone in a component. :bug:`1605` -* Fixed unit of file size to powers of two (MiB, GiB, etc.) instead of powers - of ten (MB, GB, etc.). :bug:`1623` +- Fixed unit of file size to powers of two (MiB, GiB, etc.) instead of powers of + ten (MB, GB, etc.). :bug:`1623` 1.3.14 (August 2, 2015) ----------------------- -This is mainly a bugfix release, but we also have a nifty new plugin for -`ipfs`_ and a bunch of new configuration options. +This is mainly a bugfix release, but we also have a nifty new plugin for ipfs_ +and a bunch of new configuration options. The new features: -* A new :doc:`/plugins/ipfs` lets you share music via a new, global, +- A new :doc:`/plugins/ipfs` lets you share music via a new, global, decentralized filesystem. :bug:`1397` -* :doc:`/plugins/duplicates`: You can now merge duplicate - track metadata (when detecting duplicate items), or duplicate album - tracks (when detecting duplicate albums). -* :doc:`/plugins/duplicates`: Duplicate resolution now uses an ordering to +- :doc:`/plugins/duplicates`: You can now merge duplicate track metadata (when + detecting duplicate items), or duplicate album tracks (when detecting + duplicate albums). +- :doc:`/plugins/duplicates`: Duplicate resolution now uses an ordering to prioritize duplicates. By default, it prefers music with more complete metadata, but you can configure it to use any list of attributes. -* :doc:`/plugins/metasync`: Added a new backend to fetch metadata from iTunes. +- :doc:`/plugins/metasync`: Added a new backend to fetch metadata from iTunes. This plugin is still in an experimental phase. :bug:`1450` -* The `move` command has a new ``--pretend`` option, making the command show +- The ``move`` command has a new ``--pretend`` option, making the command show how the items will be moved without actually changing anything. -* The importer now supports matching of "pregap" or HTOA (hidden track-one +- The importer now supports matching of "pregap" or HTOA (hidden track-one audio) tracks when they are listed in MusicBrainz. (This feature depends on a - new version of the `python-musicbrainzngs`_ library that is not yet released, but - will start working when it is available.) Thanks to :user:`ruippeixotog`. + new version of the python-musicbrainzngs_ library that is not yet released, + but will start working when it is available.) Thanks to :user:`ruippeixotog`. :bug:`1104` :bug:`1493` -* :doc:`/plugins/plexupdate`: A new ``token`` configuration option lets you +- :doc:`/plugins/plexupdate`: A new ``token`` configuration option lets you specify a key for Plex Home setups. Thanks to :user:`edcarroll`. :bug:`1494` Fixes: -* :doc:`/plugins/fetchart`: Complain when the `enforce_ratio` - or `min_width` options are enabled but no local imaging backend is available - to carry them out. :bug:`1460` -* :doc:`/plugins/importfeeds`: Avoid generating incorrect m3u filename when - both of the `m3u` and `m3u_multi` options are enabled. :bug:`1490` -* :doc:`/plugins/duplicates`: Avoid a crash when misconfigured. :bug:`1457` -* :doc:`/plugins/mpdstats`: Avoid a crash when the music played is not in the +- :doc:`/plugins/fetchart`: Complain when the ``enforce_ratio`` or ``min_width`` + options are enabled but no local imaging backend is available to carry them + out. :bug:`1460` +- :doc:`/plugins/importfeeds`: Avoid generating incorrect m3u filename when both + of the ``m3u`` and ``m3u_multi`` options are enabled. :bug:`1490` +- :doc:`/plugins/duplicates`: Avoid a crash when misconfigured. :bug:`1457` +- :doc:`/plugins/mpdstats`: Avoid a crash when the music played is not in the beets library. Thanks to :user:`CodyReichert`. :bug:`1443` -* Fix a crash with ArtResizer on Windows systems (affecting - :doc:`/plugins/embedart`, :doc:`/plugins/fetchart`, - and :doc:`/plugins/thumbnails`). :bug:`1448` -* :doc:`/plugins/permissions`: Fix an error with non-ASCII paths. :bug:`1449` -* Fix sorting by paths when the :ref:`sort_case_insensitive` option is - enabled. :bug:`1451` -* :doc:`/plugins/embedart`: Avoid an error when trying to embed invalid images +- Fix a crash with ArtResizer on Windows systems (affecting + :doc:`/plugins/embedart`, :doc:`/plugins/fetchart`, and + :doc:`/plugins/thumbnails`). :bug:`1448` +- :doc:`/plugins/permissions`: Fix an error with non-ASCII paths. :bug:`1449` +- Fix sorting by paths when the :ref:`sort_case_insensitive` option is enabled. + :bug:`1451` +- :doc:`/plugins/embedart`: Avoid an error when trying to embed invalid images into MPEG-4 files. -* :doc:`/plugins/fetchart`: The Wikipedia source can now better deal artists +- :doc:`/plugins/fetchart`: The Wikipedia source can now better deal artists that use non-standard capitalization (e.g., alt-J, dEUS). -* :doc:`/plugins/web`: Fix searching for non-ASCII queries. Thanks to +- :doc:`/plugins/web`: Fix searching for non-ASCII queries. Thanks to :user:`oldtopman`. :bug:`1470` -* :doc:`/plugins/mpdupdate`: We now recommend the newer ``python-mpd2`` - library instead of its unmaintained parent. Thanks to :user:`Somasis`. - :bug:`1472` -* The importer interface and log file now output a useful list of files - (instead of the word "None") when in album-grouping mode. :bug:`1475` - :bug:`825` -* Fix some logging errors when filenames and other user-provided strings - contain curly braces. :bug:`1481` -* Regular expression queries over paths now work more reliably with non-ASCII +- :doc:`/plugins/mpdupdate`: We now recommend the newer ``python-mpd2`` library + instead of its unmaintained parent. Thanks to :user:`Somasis`. :bug:`1472` +- The importer interface and log file now output a useful list of files (instead + of the word "None") when in album-grouping mode. :bug:`1475` :bug:`825` +- Fix some logging errors when filenames and other user-provided strings contain + curly braces. :bug:`1481` +- Regular expression queries over paths now work more reliably with non-ASCII characters in filenames. :bug:`1482` -* Fix a bug where the autotagger's :ref:`ignored` setting was sometimes, well, +- Fix a bug where the autotagger's :ref:`ignored` setting was sometimes, well, ignored. :bug:`1487` -* Fix a bug with Unicode strings when generating image thumbnails. :bug:`1485` -* :doc:`/plugins/keyfinder`: Fix handling of Unicode paths. :bug:`1502` -* :doc:`/plugins/fetchart`: When album art is already present, the message is +- Fix a bug with Unicode strings when generating image thumbnails. :bug:`1485` +- :doc:`/plugins/keyfinder`: Fix handling of Unicode paths. :bug:`1502` +- :doc:`/plugins/fetchart`: When album art is already present, the message is now printed in the ``text_highlight_minor`` color (light gray). Thanks to :user:`Somasis`. :bug:`1512` -* Some messages in the console UI now use plural nouns correctly. Thanks to +- Some messages in the console UI now use plural nouns correctly. Thanks to :user:`JesseWeinstein`. :bug:`1521` -* Sorting numerical fields (such as track) now works again. :bug:`1511` -* :doc:`/plugins/replaygain`: Missing GStreamer plugins now cause a helpful +- Sorting numerical fields (such as track) now works again. :bug:`1511` +- :doc:`/plugins/replaygain`: Missing GStreamer plugins now cause a helpful error message instead of a crash. :bug:`1518` -* Fix an edge case when producing sanitized filenames where the maximum path +- Fix an edge case when producing sanitized filenames where the maximum path length conflicted with the :ref:`replace` rules. Thanks to Ben Ockmore. :bug:`496` :bug:`1361` -* Fix an incompatibility with OS X 10.11 (where ``/usr/sbin`` seems not to be - on the user's path by default). -* Fix an incompatibility with certain JPEG files. Here's a relevant `Python +- Fix an incompatibility with OS X 10.11 (where ``/usr/sbin`` seems not to be on + the user's path by default). +- Fix an incompatibility with certain JPEG files. Here's a relevant `Python bug`_. Thanks to :user:`nathdwek`. :bug:`1545` -* Fix the :ref:`group_albums` importer mode so that it works correctly when +- Fix the :ref:`group_albums` importer mode so that it works correctly when files are not already in order by album. :bug:`1550` -* The ``fields`` command no longer separates built-in fields from +- The ``fields`` command no longer separates built-in fields from plugin-provided ones. This distinction was becoming increasingly unreliable. -* :doc:`/plugins/duplicates`: Fix a Unicode warning when paths contained +- :doc:`/plugins/duplicates`: Fix a Unicode warning when paths contained non-ASCII characters. :bug:`1551` -* :doc:`/plugins/fetchart`: Work around a urllib3 bug that could cause a - crash. :bug:`1555` :bug:`1556` -* When you edit the configuration file with ``beet config -e`` and the file - does not exist, beets creates an empty file before editing it. This fixes an - error on OS X, where the ``open`` command does not work with non-existent - files. :bug:`1480` -* :doc:`/plugins/convert`: Fix a problem with filename encoding on Windows - under Python 3. :bug:`2515` :bug:`2516` +- :doc:`/plugins/fetchart`: Work around a urllib3 bug that could cause a crash. + :bug:`1555` :bug:`1556` +- When you edit the configuration file with ``beet config -e`` and the file does + not exist, beets creates an empty file before editing it. This fixes an error + on OS X, where the ``open`` command does not work with non-existent files. + :bug:`1480` +- :doc:`/plugins/convert`: Fix a problem with filename encoding on Windows under + Python 3. :bug:`2515` :bug:`2516` -.. _Python bug: https://bugs.python.org/issue16512 .. _ipfs: https://ipfs.io +.. _python bug: https://bugs.python.org/issue16512 + 1.3.13 (April 24, 2015) ----------------------- This is a tiny bug-fix release. It copes with a dependency upgrade that broke beets. There are just two fixes: -* Fix compatibility with `Jellyfish`_ version 0.5.0. -* :doc:`/plugins/embedart`: In ``auto`` mode (the import hook), the plugin now +- Fix compatibility with Jellyfish_ version 0.5.0. +- :doc:`/plugins/embedart`: In ``auto`` mode (the import hook), the plugin now respects the ``write`` config option under ``import``. If this is disabled, album art is no longer embedded on import in order to leave files untouched---in effect, ``auto`` is implicitly disabled. :bug:`1427` @@ -2693,62 +2452,61 @@ beets. There are just two fixes: 1.3.12 (April 18, 2015) ----------------------- -This little update makes queries more powerful, sorts music more -intelligently, and removes a performance bottleneck. There's an experimental -new plugin for synchronizing metadata with music players. +This little update makes queries more powerful, sorts music more intelligently, +and removes a performance bottleneck. There's an experimental new plugin for +synchronizing metadata with music players. -Packagers should also note a new dependency in this version: the `Jellyfish`_ +Packagers should also note a new dependency in this version: the Jellyfish_ Python library makes our text comparisons (a big part of the auto-tagging process) go much faster. New features: -* Queries can now use **"or" logic**: if you use a comma to separate parts of a +- Queries can now use **"or" logic**: if you use a comma to separate parts of a query, items and albums will match *either* side of the comma. For example, - ``beet ls foo , bar`` will get all the items matching `foo` or matching - `bar`. See :ref:`combiningqueries`. :bug:`1423` -* The autotagger's **matching algorithm is faster**. We now use the - `Jellyfish`_ library to compute string similarity, which is better optimized - than our hand-rolled edit distance implementation. :bug:`1389` -* Sorting is now **case insensitive** by default. This means that artists will - be sorted lexicographically regardless of case. For example, the artist - alt-J will now properly sort before YACHT. (Previously, it would have ended - up at the end of the list, after all the capital-letter artists.) - You can turn this new behavior off using the :ref:`sort_case_insensitive` - configuration option. See :ref:`query-sort`. :bug:`1429` -* An experimental new :doc:`/plugins/metasync` lets you get metadata from your + ``beet ls foo , bar`` will get all the items matching ``foo`` or matching + ``bar``. See :ref:`combiningqueries`. :bug:`1423` +- The autotagger's **matching algorithm is faster**. We now use the Jellyfish_ + library to compute string similarity, which is better optimized than our + hand-rolled edit distance implementation. :bug:`1389` +- Sorting is now **case insensitive** by default. This means that artists will + be sorted lexicographically regardless of case. For example, the artist alt-J + will now properly sort before YACHT. (Previously, it would have ended up at + the end of the list, after all the capital-letter artists.) You can turn this + new behavior off using the :ref:`sort_case_insensitive` configuration option. + See :ref:`query-sort`. :bug:`1429` +- An experimental new :doc:`/plugins/metasync` lets you get metadata from your favorite music players, starting with Amarok. :bug:`1386` -* :doc:`/plugins/fetchart`: There are new settings to control what constitutes - "acceptable" images. The `minwidth` option constrains the minimum image - width in pixels and the `enforce_ratio` option requires that images be +- :doc:`/plugins/fetchart`: There are new settings to control what constitutes + "acceptable" images. The ``minwidth`` option constrains the minimum image + width in pixels and the ``enforce_ratio`` option requires that images be square. :bug:`1394` Little fixes and improvements: -* :doc:`/plugins/fetchart`: Remove a hard size limit when fetching from the +- :doc:`/plugins/fetchart`: Remove a hard size limit when fetching from the Cover Art Archive. -* The output of the :ref:`fields-cmd` command is now sorted. Thanks to +- The output of the :ref:`fields-cmd` command is now sorted. Thanks to :user:`multikatt`. :bug:`1402` -* :doc:`/plugins/replaygain`: Fix a number of issues with the new - ``bs1770gain`` backend on Windows. Also, fix missing debug output in import - mode. :bug:`1398` -* Beets should now be better at guessing the appropriate output encoding on - Windows. (Specifically, the console output encoding is guessed separately - from the encoding for command-line arguments.) A bug was also fixed where - beets would ignore the locale settings and use UTF-8 by default. :bug:`1419` -* :doc:`/plugins/discogs`: Better error handling when we can't communicate - with Discogs on setup. :bug:`1417` -* :doc:`/plugins/importadded`: Fix a crash when importing singletons in-place. +- :doc:`/plugins/replaygain`: Fix a number of issues with the new ``bs1770gain`` + backend on Windows. Also, fix missing debug output in import mode. :bug:`1398` +- Beets should now be better at guessing the appropriate output encoding on + Windows. (Specifically, the console output encoding is guessed separately from + the encoding for command-line arguments.) A bug was also fixed where beets + would ignore the locale settings and use UTF-8 by default. :bug:`1419` +- :doc:`/plugins/discogs`: Better error handling when we can't communicate with + Discogs on setup. :bug:`1417` +- :doc:`/plugins/importadded`: Fix a crash when importing singletons in-place. :bug:`1416` -* :doc:`/plugins/fuzzy`: Fix a regression causing a crash in the last release. +- :doc:`/plugins/fuzzy`: Fix a regression causing a crash in the last release. :bug:`1422` -* Fix a crash when the importer cannot open its log file. Thanks to +- Fix a crash when the importer cannot open its log file. Thanks to :user:`barsanuphe`. :bug:`1426` -* Fix an error when trying to write tags for items with flexible fields called - `date` and `original_date` (which are not built-in beets fields). +- Fix an error when trying to write tags for items with flexible fields called + ``date`` and ``original_date`` (which are not built-in beets fields). :bug:`1404` -.. _Jellyfish: https://github.com/sunlightlabs/jellyfish +.. _jellyfish: https://github.com/sunlightlabs/jellyfish 1.3.11 (April 5, 2015) ---------------------- @@ -2763,184 +2521,179 @@ evolved plugin for using album art as directory thumbnails in file managers. There's a new source for album art, and the importer now records the source of match data. This is a particularly huge release---there's lots more below. -There's one big change with this release: **Python 2.6 is no longer -supported**. You'll need Python 2.7. Please trust us when we say this let us -remove a surprising number of ugly hacks throughout the code. +There's one big change with this release: **Python 2.6 is no longer supported**. +You'll need Python 2.7. Please trust us when we say this let us remove a +surprising number of ugly hacks throughout the code. Major new features and bigger changes: -* There are now **multiple levels of output verbosity**. On the command line, - you can make beets somewhat verbose with ``-v`` or very verbose with - ``-vv``. For the importer especially, this makes the first verbose mode much - more manageable, while still preserving an option for overwhelmingly verbose - debug output. :bug:`1244` -* A new :doc:`/plugins/filefilter` lets you write regular expressions to +- There are now **multiple levels of output verbosity**. On the command line, + you can make beets somewhat verbose with ``-v`` or very verbose with ``-vv``. + For the importer especially, this makes the first verbose mode much more + manageable, while still preserving an option for overwhelmingly verbose debug + output. :bug:`1244` +- A new :doc:`/plugins/filefilter` lets you write regular expressions to automatically **avoid importing** certain files. Thanks to :user:`mried`. :bug:`1186` -* A new :doc:`/plugins/thumbnails` generates cover-art **thumbnails for - album folders** for Freedesktop.org-compliant file managers. (This replaces - the :doc:`/plugins/freedesktop`, which only worked with the Dolphin file - manager.) -* :doc:`/plugins/replaygain`: There is a new backend that uses the - `bs1770gain`_ analysis tool. Thanks to :user:`jmwatte`. :bug:`1343` -* A new ``filesize`` field on items indicates the number of bytes in the file. +- A new :doc:`/plugins/thumbnails` generates cover-art **thumbnails for album + folders** for Freedesktop.org-compliant file managers. (This replaces the + :doc:`/plugins/freedesktop`, which only worked with the Dolphin file manager.) +- :doc:`/plugins/replaygain`: There is a new backend that uses the bs1770gain_ + analysis tool. Thanks to :user:`jmwatte`. :bug:`1343` +- A new ``filesize`` field on items indicates the number of bytes in the file. :bug:`1291` -* A new :ref:`searchlimit` configuration option allows you to specify how many - search results you wish to see when looking up releases at MusicBrainz - during import. :bug:`1245` -* The importer now records the data source for a match in a new - flexible attribute ``data_source`` on items and albums. :bug:`1311` -* The colors used in the terminal interface are now configurable via the new - config option ``colors``, nested under the option ``ui``. (Also, the `color` +- A new :ref:`searchlimit` configuration option allows you to specify how many + search results you wish to see when looking up releases at MusicBrainz during + import. :bug:`1245` +- The importer now records the data source for a match in a new flexible + attribute ``data_source`` on items and albums. :bug:`1311` +- The colors used in the terminal interface are now configurable via the new + config option ``colors``, nested under the option ``ui``. (Also, the ``color`` config option has been moved from top-level to under ``ui``. Beets will respect the old color setting, but will warn the user with a deprecation message.) :bug:`1238` -* :doc:`/plugins/fetchart`: There's a new Wikipedia image source that uses +- :doc:`/plugins/fetchart`: There's a new Wikipedia image source that uses DBpedia to find albums. Thanks to Tom Jaspers. :bug:`1194` -* In the :ref:`config-cmd` command, the output is now redacted by default. +- In the :ref:`config-cmd` command, the output is now redacted by default. Sensitive information like passwords and API keys is not included. The new ``--clear`` option disables redaction. :bug:`1376` You should probably also know about these core changes to the way beets works: -* As mentioned above, Python 2.6 is no longer supported. -* The ``tracktotal`` attribute is now a *track-level field* instead of an - album-level one. This field stores the total number of tracks on the - album, or if the :ref:`per_disc_numbering` config option is set, the total - number of tracks on a particular medium (i.e., disc). The field was causing - problems with that :ref:`per_disc_numbering` mode: different discs on the - same album needed different track totals. The field can now work correctly - in either mode. -* To replace ``tracktotal`` as an album-level field, there is a new - ``albumtotal`` computed attribute that provides the total number of tracks - on the album. (The :ref:`per_disc_numbering` option has no influence on this +- As mentioned above, Python 2.6 is no longer supported. +- The ``tracktotal`` attribute is now a *track-level field* instead of an + album-level one. This field stores the total number of tracks on the album, or + if the :ref:`per_disc_numbering` config option is set, the total number of + tracks on a particular medium (i.e., disc). The field was causing problems + with that :ref:`per_disc_numbering` mode: different discs on the same album + needed different track totals. The field can now work correctly in either + mode. +- To replace ``tracktotal`` as an album-level field, there is a new + ``albumtotal`` computed attribute that provides the total number of tracks on + the album. (The :ref:`per_disc_numbering` option has no influence on this field.) -* The `list_format_album` and `list_format_item` configuration keys - now affect (almost) every place where objects are printed and logged. - (Previously, they only controlled the :ref:`list-cmd` command and a few - other scattered pieces.) :bug:`1269` -* Relatedly, the ``beet`` program now accept top-level options - ``--format-item`` and ``--format-album`` before any subcommand to control - how items and albums are displayed. :bug:`1271` -* `list_format_album` and `list_format_album` have respectively been - renamed :ref:`format_album` and :ref:`format_item`. The old names still work - but each triggers a warning message. :bug:`1271` -* :ref:`Path queries <pathquery>` are automatically triggered only if the - path targeted by the query exists. Previously, just having a slash somewhere - in the query was enough, so ``beet ls AC/DC`` wouldn't work to refer to the - artist. +- The ``list_format_album`` and ``list_format_item`` configuration keys now + affect (almost) every place where objects are printed and logged. (Previously, + they only controlled the :ref:`list-cmd` command and a few other scattered + pieces.) :bug:`1269` +- Relatedly, the ``beet`` program now accept top-level options ``--format-item`` + and ``--format-album`` before any subcommand to control how items and albums + are displayed. :bug:`1271` +- ``list_format_album`` and ``list_format_album`` have respectively been renamed + :ref:`format_album` and :ref:`format_item`. The old names still work but each + triggers a warning message. :bug:`1271` +- :ref:`Path queries <pathquery>` are automatically triggered only if the path + targeted by the query exists. Previously, just having a slash somewhere in the + query was enough, so ``beet ls AC/DC`` wouldn't work to refer to the artist. There are also lots of medium-sized features in this update: -* :doc:`/plugins/duplicates`: The command has a new ``--strict`` option - that will only report duplicates if all attributes are explicitly set. - :bug:`1000` -* :doc:`/plugins/smartplaylist`: Playlist updating should now be faster: the - plugin detects, for each playlist, whether it needs to be regenerated, - instead of obliviously regenerating all of them. The ``splupdate`` command - can now also take additional parameters that indicate the names of the - playlists to regenerate. -* :doc:`/plugins/play`: The command shows the output of the underlying player +- :doc:`/plugins/duplicates`: The command has a new ``--strict`` option that + will only report duplicates if all attributes are explicitly set. :bug:`1000` +- :doc:`/plugins/smartplaylist`: Playlist updating should now be faster: the + plugin detects, for each playlist, whether it needs to be regenerated, instead + of obliviously regenerating all of them. The ``splupdate`` command can now + also take additional parameters that indicate the names of the playlists to + regenerate. +- :doc:`/plugins/play`: The command shows the output of the underlying player command and lets you interact with it. :bug:`1321` -* The summary shown to compare duplicate albums during import now displays - the old and new filesizes. :bug:`1291` -* :doc:`/plugins/lastgenre`: Add *comedy*, *humor*, and *stand-up* as well as - a longer list of classical music genre tags to the built-in whitelist and +- The summary shown to compare duplicate albums during import now displays the + old and new filesizes. :bug:`1291` +- :doc:`/plugins/lastgenre`: Add *comedy*, *humor*, and *stand-up* as well as a + longer list of classical music genre tags to the built-in whitelist and canonicalization tree. :bug:`1206` :bug:`1239` :bug:`1240` -* :doc:`/plugins/web`: Add support for *cross-origin resource sharing* for - more flexible in-browser clients. Thanks to Andre Miller. :bug:`1236` - :bug:`1237` -* :doc:`plugins/mbsync`: A new ``-f/--format`` option controls the output - format when listing unrecognized items. The output is also now more helpful - by default. :bug:`1246` -* :doc:`/plugins/fetchart`: A new option, ``-n``, extracts the cover art of - all matched albums into their respective directories. Another new flag, - ``-a``, associates the extracted files with the albums in the database. - :bug:`1261` -* :doc:`/plugins/info`: A new option, ``-i``, can display only a specified +- :doc:`/plugins/web`: Add support for *cross-origin resource sharing* for more + flexible in-browser clients. Thanks to Andre Miller. :bug:`1236` :bug:`1237` +- :doc:`plugins/mbsync`: A new ``-f/--format`` option controls the output format + when listing unrecognized items. The output is also now more helpful by + default. :bug:`1246` +- :doc:`/plugins/fetchart`: A new option, ``-n``, extracts the cover art of all + matched albums into their respective directories. Another new flag, ``-a``, + associates the extracted files with the albums in the database. :bug:`1261` +- :doc:`/plugins/info`: A new option, ``-i``, can display only a specified subset of properties. :bug:`1287` -* The number of missing/unmatched tracks is shown during import. :bug:`1088` -* :doc:`/plugins/permissions`: The plugin now also adjusts the permissions of +- The number of missing/unmatched tracks is shown during import. :bug:`1088` +- :doc:`/plugins/permissions`: The plugin now also adjusts the permissions of the directories. (Previously, it only affected files.) :bug:`1308` :bug:`1324` -* :doc:`/plugins/ftintitle`: You can now configure the format that the plugin +- :doc:`/plugins/ftintitle`: You can now configure the format that the plugin uses to add the artist to the title. Thanks to :user:`amishb`. :bug:`1377` And many little fixes and improvements: -* :doc:`/plugins/replaygain`: Stop applying replaygain directly to source files +- :doc:`/plugins/replaygain`: Stop applying replaygain directly to source files when using the mp3gain backend. :bug:`1316` -* Path queries are case-sensitive on non-Windows OSes. :bug:`1165` -* :doc:`/plugins/lyrics`: Silence a warning about insecure requests in the new +- Path queries are case-sensitive on non-Windows OSes. :bug:`1165` +- :doc:`/plugins/lyrics`: Silence a warning about insecure requests in the new MusixMatch backend. :bug:`1204` -* Fix a crash when ``beet`` is invoked without arguments. :bug:`1205` +- Fix a crash when ``beet`` is invoked without arguments. :bug:`1205` :bug:`1207` -* :doc:`/plugins/fetchart`: Do not attempt to import directories as album art. +- :doc:`/plugins/fetchart`: Do not attempt to import directories as album art. :bug:`1177` :bug:`1211` -* :doc:`/plugins/mpdstats`: Avoid double-counting some play events. :bug:`773` +- :doc:`/plugins/mpdstats`: Avoid double-counting some play events. :bug:`773` :bug:`1212` -* Fix a crash when the importer deals with Unicode metadata in ``--pretend`` +- Fix a crash when the importer deals with Unicode metadata in ``--pretend`` mode. :bug:`1214` -* :doc:`/plugins/smartplaylist`: Fix ``album_query`` so that individual files +- :doc:`/plugins/smartplaylist`: Fix ``album_query`` so that individual files are added to the playlist instead of directories. :bug:`1225` -* Remove the ``beatport`` plugin. `Beatport`_ has shut off public access to - their API and denied our request for an account. We have not heard from the - company since 2013, so we are assuming access will not be restored. -* Incremental imports now (once again) show a "skipped N directories" message. -* :doc:`/plugins/embedart`: Handle errors in ImageMagick's output. :bug:`1241` -* :doc:`/plugins/keyfinder`: Parse the underlying tool's output more robustly. +- Remove the ``beatport`` plugin. Beatport_ has shut off public access to their + API and denied our request for an account. We have not heard from the company + since 2013, so we are assuming access will not be restored. +- Incremental imports now (once again) show a "skipped N directories" message. +- :doc:`/plugins/embedart`: Handle errors in ImageMagick's output. :bug:`1241` +- :doc:`/plugins/keyfinder`: Parse the underlying tool's output more robustly. :bug:`1248` -* :doc:`/plugins/embedart`: We now show a comprehensible error message when +- :doc:`/plugins/embedart`: We now show a comprehensible error message when ``beet embedart -f FILE`` is given a non-existent path. :bug:`1252` -* Fix a crash when a file has an unrecognized image type tag. Thanks to - Matthias Kiefer. :bug:`1260` -* :doc:`/plugins/importfeeds` and :doc:`/plugins/smartplaylist`: Automatically +- Fix a crash when a file has an unrecognized image type tag. Thanks to Matthias + Kiefer. :bug:`1260` +- :doc:`/plugins/importfeeds` and :doc:`/plugins/smartplaylist`: Automatically create parent directories for playlist files (instead of crashing when the parent directory does not exist). :bug:`1266` -* The :ref:`write-cmd` command no longer tries to "write" non-writable fields, +- The :ref:`write-cmd` command no longer tries to "write" non-writable fields, such as the bitrate. :bug:`1268` -* The error message when MusicBrainz is not reachable on the network is now - much clearer. Thanks to Tom Jaspers. :bug:`1190` :bug:`1272` -* Improve error messages when parsing query strings with shlex. :bug:`1290` -* :doc:`/plugins/embedart`: Fix a crash that occurred when used together - with the *check* plugin. :bug:`1241` -* :doc:`/plugins/scrub`: Log an error instead of stopping when the ``beet +- The error message when MusicBrainz is not reachable on the network is now much + clearer. Thanks to Tom Jaspers. :bug:`1190` :bug:`1272` +- Improve error messages when parsing query strings with shlex. :bug:`1290` +- :doc:`/plugins/embedart`: Fix a crash that occurred when used together with + the *check* plugin. :bug:`1241` +- :doc:`/plugins/scrub`: Log an error instead of stopping when the ``beet scrub`` command cannot write a file. Also, avoid problems on Windows with Unicode filenames. :bug:`1297` -* :doc:`/plugins/discogs`: Handle and log more kinds of communication - errors. :bug:`1299` :bug:`1305` -* :doc:`/plugins/lastgenre`: Bugs in the `pylast` library can no longer crash +- :doc:`/plugins/discogs`: Handle and log more kinds of communication errors. + :bug:`1299` :bug:`1305` +- :doc:`/plugins/lastgenre`: Bugs in the ``pylast`` library can no longer crash beets. -* :doc:`/plugins/convert`: You can now configure the temporary directory for +- :doc:`/plugins/convert`: You can now configure the temporary directory for conversions. Thanks to :user:`autochthe`. :bug:`1382` :bug:`1383` -* :doc:`/plugins/rewrite`: Fix a regression that prevented the plugin's +- :doc:`/plugins/rewrite`: Fix a regression that prevented the plugin's rewriting from applying to album-level fields like ``$albumartist``. :bug:`1393` -* :doc:`/plugins/play`: The plugin now sorts items according to the +- :doc:`/plugins/play`: The plugin now sorts items according to the configuration in album mode. -* :doc:`/plugins/fetchart`: The name for extracted art files is taken from the +- :doc:`/plugins/fetchart`: The name for extracted art files is taken from the ``art_filename`` configuration option. :bug:`1258` -* When there's a parse error in a query (for example, when you type a - malformed date in a :ref:`date query <datequery>`), beets now stops with an - error instead of silently ignoring the query component. -* :doc:`/plugins/smartplaylist`: Stream-friendly smart playlists. - The ``splupdate`` command can now also add a URL-encodable prefix to every - path in the playlist file. +- When there's a parse error in a query (for example, when you type a malformed + date in a :ref:`date query <datequery>`), beets now stops with an error + instead of silently ignoring the query component. +- :doc:`/plugins/smartplaylist`: Stream-friendly smart playlists. The + ``splupdate`` command can now also add a URL-encodable prefix to every path in + the playlist file. For developers: -* The ``database_change`` event now sends the item or album that is subject to - a change. -* The ``OptionParser`` is now a ``CommonOptionsParser`` that offers facilities +- The ``database_change`` event now sends the item or album that is subject to a + change. +- The ``OptionParser`` is now a ``CommonOptionsParser`` that offers facilities for adding usual options (``--album``, ``--path`` and ``--format``). See :ref:`add_subcommands`. :bug:`1271` -* The logging system in beets has been overhauled. Plugins now each have their +- The logging system in beets has been overhauled. Plugins now each have their own logger, which helps by automatically adjusting the verbosity level in - import mode and by prefixing the plugin's name. Logging levels are - dynamically set when a plugin is called, depending on how it is called - (import stage, event or direct command). Finally, logging calls can (and - should!) use modern ``{}``-style string formatting lazily. See - :ref:`plugin-logging` in the plugin API docs. -* A new ``import_task_created`` event lets you manipulate import tasks + import mode and by prefixing the plugin's name. Logging levels are dynamically + set when a plugin is called, depending on how it is called (import stage, + event or direct command). Finally, logging calls can (and should!) use modern + ``{}``-style string formatting lazily. See :ref:`plugin-logging` in the plugin + API docs. +- A new ``import_task_created`` event lets you manipulate import tasks immediately after they are initialized. It's also possible to replace the originally created tasks by returning new ones using this event. @@ -2951,12 +2704,12 @@ For developers: This version adds a healthy helping of new features and fixes a critical MPEG-4--related bug. There are more lyrics sources, there new plugins for -managing permissions and integrating with `Plex`_, and the importer has a new +managing permissions and integrating with Plex_, and the importer has a new ``--pretend`` flag that shows which music *would* be imported. One backwards-compatibility note: the :doc:`/plugins/lyrics` now requires the -`requests`_ library. If you use this plugin, you will need to install the -library by typing ``pip install requests`` or the equivalent for your OS. +requests_ library. If you use this plugin, you will need to install the library +by typing ``pip install requests`` or the equivalent for your OS. Also, as an advance warning, this will be one of the last releases to support Python 2.6. If you have a system that cannot run Python 2.7, please consider @@ -2964,180 +2717,178 @@ upgrading soon. The new features are: -* A new :doc:`/plugins/permissions` makes it easy to fix permissions on music +- A new :doc:`/plugins/permissions` makes it easy to fix permissions on music files as they are imported. Thanks to :user:`xsteadfastx`. :bug:`1098` -* A new :doc:`/plugins/plexupdate` lets you notify a `Plex`_ server when the +- A new :doc:`/plugins/plexupdate` lets you notify a Plex_ server when the database changes. Thanks again to xsteadfastx. :bug:`1120` -* The :ref:`import-cmd` command now has a ``--pretend`` flag that lists the +- The :ref:`import-cmd` command now has a ``--pretend`` flag that lists the files that will be imported. Thanks to :user:`mried`. :bug:`1162` -* :doc:`/plugins/lyrics`: Add `Musixmatch`_ source and introduce a new - ``sources`` config option that lets you choose exactly where to look for - lyrics and in which order. -* :doc:`/plugins/lyrics`: Add Brazilian and Spanish sources to Google custom +- :doc:`/plugins/lyrics`: Add Musixmatch_ source and introduce a new ``sources`` + config option that lets you choose exactly where to look for lyrics and in + which order. +- :doc:`/plugins/lyrics`: Add Brazilian and Spanish sources to Google custom search engine. -* Add a warning when importing a directory that contains no music. :bug:`1116` +- Add a warning when importing a directory that contains no music. :bug:`1116` :bug:`1127` -* :doc:`/plugins/zero`: Can now remove embedded images. :bug:`1129` :bug:`1100` -* The :ref:`config-cmd` command can now be used to edit the configuration even +- :doc:`/plugins/zero`: Can now remove embedded images. :bug:`1129` :bug:`1100` +- The :ref:`config-cmd` command can now be used to edit the configuration even when it has syntax errors. :bug:`1123` :bug:`1128` -* :doc:`/plugins/lyrics`: Added a new ``force`` config option. :bug:`1150` +- :doc:`/plugins/lyrics`: Added a new ``force`` config option. :bug:`1150` As usual, there are loads of little fixes and improvements: -* Fix a new crash with the latest version of Mutagen (1.26). -* :doc:`/plugins/lyrics`: Avoid fetching truncated lyrics from the Google - backed by merging text blocks separated by empty ``<div>`` tags before - scraping. -* We now print a better error message when the database file is corrupted. -* :doc:`/plugins/discogs`: Only prompt for authentication when running the +- Fix a new crash with the latest version of Mutagen (1.26). +- :doc:`/plugins/lyrics`: Avoid fetching truncated lyrics from the Google backed + by merging text blocks separated by empty ``<div>`` tags before scraping. +- We now print a better error message when the database file is corrupted. +- :doc:`/plugins/discogs`: Only prompt for authentication when running the :ref:`import-cmd` command. :bug:`1123` -* When deleting fields with the :ref:`modify-cmd` command, do not crash when - the field cannot be removed (i.e., when it does not exist, when it is a - built-in field, or when it is a computed field). :bug:`1124` -* The deprecated ``echonest_tempo`` plugin has been removed. Please use the +- When deleting fields with the :ref:`modify-cmd` command, do not crash when the + field cannot be removed (i.e., when it does not exist, when it is a built-in + field, or when it is a computed field). :bug:`1124` +- The deprecated ``echonest_tempo`` plugin has been removed. Please use the ``echonest`` plugin instead. -* ``echonest`` plugin: Fingerprint-based lookup has been removed in - accordance with `API changes`_. :bug:`1121` -* ``echonest`` plugin: Avoid a crash when the song has no duration - information. :bug:`896` -* :doc:`/plugins/lyrics`: Avoid a crash when retrieving non-ASCII lyrics from +- ``echonest`` plugin: Fingerprint-based lookup has been removed in accordance + with `API changes`_. :bug:`1121` +- ``echonest`` plugin: Avoid a crash when the song has no duration information. + :bug:`896` +- :doc:`/plugins/lyrics`: Avoid a crash when retrieving non-ASCII lyrics from the Google backend. :bug:`1135` :bug:`1136` -* :doc:`/plugins/smartplaylist`: Sort specifiers are now respected in queries. +- :doc:`/plugins/smartplaylist`: Sort specifiers are now respected in queries. Thanks to :user:`djl`. :bug:`1138` :bug:`1137` -* :doc:`/plugins/ftintitle` and :doc:`/plugins/lyrics`: Featuring artists can - now be detected when they use the Spanish word *con*. :bug:`1060` - :bug:`1143` -* :doc:`/plugins/mbcollection`: Fix an "HTTP 400" error caused by a change in +- :doc:`/plugins/ftintitle` and :doc:`/plugins/lyrics`: Featuring artists can + now be detected when they use the Spanish word *con*. :bug:`1060` :bug:`1143` +- :doc:`/plugins/mbcollection`: Fix an "HTTP 400" error caused by a change in the MusicBrainz API. :bug:`1152` -* The ``%`` and ``_`` characters in path queries do not invoke their - special SQL meaning anymore. :bug:`1146` -* :doc:`/plugins/convert`: Command-line argument construction now works - on Windows. Thanks to :user:`mluds`. :bug:`1026` :bug:`1157` :bug:`1158` -* :doc:`/plugins/embedart`: Fix an erroneous missing-art error on Windows. +- The ``%`` and ``_`` characters in path queries do not invoke their special SQL + meaning anymore. :bug:`1146` +- :doc:`/plugins/convert`: Command-line argument construction now works on + Windows. Thanks to :user:`mluds`. :bug:`1026` :bug:`1157` :bug:`1158` +- :doc:`/plugins/embedart`: Fix an erroneous missing-art error on Windows. Thanks to :user:`mluds`. :bug:`1163` -* :doc:`/plugins/importadded`: Now works with in-place and symlinked imports. +- :doc:`/plugins/importadded`: Now works with in-place and symlinked imports. :bug:`1170` -* :doc:`/plugins/ftintitle`: The plugin is now quiet when it runs as part of - the import process. Thanks to :user:`Freso`. :bug:`1176` :bug:`1172` -* :doc:`/plugins/ftintitle`: Fix weird behavior when the same artist appears +- :doc:`/plugins/ftintitle`: The plugin is now quiet when it runs as part of the + import process. Thanks to :user:`Freso`. :bug:`1176` :bug:`1172` +- :doc:`/plugins/ftintitle`: Fix weird behavior when the same artist appears twice in the artist string. Thanks to Marc Addeo. :bug:`1179` :bug:`1181` -* :doc:`/plugins/lastgenre`: Match songs more robustly when they contain - dashes. Thanks to :user:`djl`. :bug:`1156` -* The :ref:`config-cmd` command can now use ``$EDITOR`` variables with +- :doc:`/plugins/lastgenre`: Match songs more robustly when they contain dashes. + Thanks to :user:`djl`. :bug:`1156` +- The :ref:`config-cmd` command can now use ``$EDITOR`` variables with arguments. -.. _API changes: https://web.archive.org/web/20160814092627/https://developer.echonest.com/forums/thread/3650 -.. _Plex: https://plex.tv/ +.. _api changes: https://web.archive.org/web/20160814092627/https://developer.echonest.com/forums/thread/3650 + .. _musixmatch: https://www.musixmatch.com/ +.. _plex: https://plex.tv/ + 1.3.9 (November 17, 2014) ------------------------- This release adds two new standard plugins to beets: one for synchronizing -Last.fm listening data and one for integrating with Linux desktops. And at -long last, imports can now create symbolic links to music files instead of -copying or moving them. We also gained the ability to search for album art on -the iTunes Store and a new way to compute ReplayGain levels. +Last.fm listening data and one for integrating with Linux desktops. And at long +last, imports can now create symbolic links to music files instead of copying or +moving them. We also gained the ability to search for album art on the iTunes +Store and a new way to compute ReplayGain levels. The major new features are: -* A new :doc:`/plugins/lastimport` lets you download your play count data from +- A new :doc:`/plugins/lastimport` lets you download your play count data from Last.fm into a flexible attribute. Thanks to Rafael Bodill. -* A new :doc:`/plugins/freedesktop` creates metadata files for +- A new :doc:`/plugins/freedesktop` creates metadata files for Freedesktop.org--compliant file managers. Thanks to :user:`kerobaros`. :bug:`1056`, :bug:`707` -* A new :ref:`link` option in the ``import`` section creates symbolic links +- A new :ref:`link` option in the ``import`` section creates symbolic links during import instead of moving or copying. Thanks to Rovanion Luckey. :bug:`710`, :bug:`114` -* :doc:`/plugins/fetchart`: You can now search for art on the iTunes Store. +- :doc:`/plugins/fetchart`: You can now search for art on the iTunes Store. There's also a new ``sources`` config option that lets you choose exactly where to look for images and in which order. -* :doc:`/plugins/replaygain`: A new Python Audio Tools backend was added. - Thanks to Francesco Rubino. :bug:`1070` -* :doc:`/plugins/embedart`: You can now automatically check that new art looks - similar to existing art---ensuring that you only get a better "version" of - the art you already have. See :ref:`image-similarity-check`. -* :doc:`/plugins/ftintitle`: The plugin now runs automatically on import. To +- :doc:`/plugins/replaygain`: A new Python Audio Tools backend was added. Thanks + to Francesco Rubino. :bug:`1070` +- :doc:`/plugins/embedart`: You can now automatically check that new art looks + similar to existing art---ensuring that you only get a better "version" of the + art you already have. See :ref:`image-similarity-check`. +- :doc:`/plugins/ftintitle`: The plugin now runs automatically on import. To disable this, unset the ``auto`` config flag. There are also core improvements and other substantial additions: -* The ``media`` attribute is now a *track-level field* instead of an - album-level one. This field stores the delivery mechanism for the music, so - in its album-level incarnation, it could not represent heterogeneous - releases---for example, an album consisting of a CD and a DVD. Now, tracks - accurately indicate the media they appear on. Thanks to Heinz Wiesinger. -* Re-imports of your existing music (see :ref:`reimport`) now preserve its - added date and flexible attributes. Thanks to Stig Inge Lea Bjørnsen. -* Slow queries, such as those over flexible attributes, should now be much +- The ``media`` attribute is now a *track-level field* instead of an album-level + one. This field stores the delivery mechanism for the music, so in its + album-level incarnation, it could not represent heterogeneous releases---for + example, an album consisting of a CD and a DVD. Now, tracks accurately + indicate the media they appear on. Thanks to Heinz Wiesinger. +- Re-imports of your existing music (see :ref:`reimport`) now preserve its added + date and flexible attributes. Thanks to Stig Inge Lea Bjørnsen. +- Slow queries, such as those over flexible attributes, should now be much faster when used with certain commands---notably, the :doc:`/plugins/play`. -* :doc:`/plugins/bpd`: Add a new configuration option for setting the default +- :doc:`/plugins/bpd`: Add a new configuration option for setting the default volume. Thanks to IndiGit. -* :doc:`/plugins/embedart`: A new ``ifempty`` config option lets you only - embed album art when no album art is present. Thanks to kerobaros. -* :doc:`/plugins/discogs`: Authenticate with the Discogs server. The plugin - now requires a Discogs account due to new API restrictions. Thanks to +- :doc:`/plugins/embedart`: A new ``ifempty`` config option lets you only embed + album art when no album art is present. Thanks to kerobaros. +- :doc:`/plugins/discogs`: Authenticate with the Discogs server. The plugin now + requires a Discogs account due to new API restrictions. Thanks to :user:`multikatt`. :bug:`1027`, :bug:`1040` And countless little improvements and fixes: -* Standard cover art in APEv2 metadata is now supported. Thanks to Matthias +- Standard cover art in APEv2 metadata is now supported. Thanks to Matthias Kiefer. :bug:`1042` -* :doc:`/plugins/convert`: Avoid a crash when embedding cover art - fails. -* :doc:`/plugins/mpdstats`: Fix an error on start (introduced in the previous +- :doc:`/plugins/convert`: Avoid a crash when embedding cover art fails. +- :doc:`/plugins/mpdstats`: Fix an error on start (introduced in the previous version). Thanks to Zach Denton. -* :doc:`/plugins/convert`: The ``--yes`` command-line flag no longer expects - an argument. -* :doc:`/plugins/play`: Remove the temporary .m3u file after sending it to - the player. -* The importer no longer tries to highlight partial differences in numeric +- :doc:`/plugins/convert`: The ``--yes`` command-line flag no longer expects an + argument. +- :doc:`/plugins/play`: Remove the temporary .m3u file after sending it to the + player. +- The importer no longer tries to highlight partial differences in numeric quantities (track numbers and durations), which was often confusing. -* Date-based queries that are malformed (not parse-able) no longer crash - beets and instead fail silently. -* :doc:`/plugins/duplicates`: Emit an error when the ``checksum`` config - option is set incorrectly. -* The migration from pre-1.1, non-YAML configuration files has been removed. - If you need to upgrade an old config file, use an older version of beets +- Date-based queries that are malformed (not parse-able) no longer crash beets + and instead fail silently. +- :doc:`/plugins/duplicates`: Emit an error when the ``checksum`` config option + is set incorrectly. +- The migration from pre-1.1, non-YAML configuration files has been removed. If + you need to upgrade an old config file, use an older version of beets temporarily. -* :doc:`/plugins/discogs`: Recover from HTTP errors when communicating with - the Discogs servers. Thanks to Dustin Rodriguez. -* :doc:`/plugins/embedart`: Do not log "embedding album art into..." messages +- :doc:`/plugins/discogs`: Recover from HTTP errors when communicating with the + Discogs servers. Thanks to Dustin Rodriguez. +- :doc:`/plugins/embedart`: Do not log "embedding album art into..." messages during the import process. -* Fix a crash in the autotagger when files had only whitespace in their +- Fix a crash in the autotagger when files had only whitespace in their metadata. -* :doc:`/plugins/play`: Fix a potential crash when the command outputs special +- :doc:`/plugins/play`: Fix a potential crash when the command outputs special characters. :bug:`1041` -* :doc:`/plugins/web`: Queries typed into the search field are now treated as +- :doc:`/plugins/web`: Queries typed into the search field are now treated as separate query components. :bug:`1045` -* Date tags that use slashes instead of dashes as separators are now - interpreted correctly. And WMA (ASF) files now map the ``comments`` field to - the "Description" tag (in addition to "WM/Comments"). Thanks to Matthias - Kiefer. :bug:`1043` -* :doc:`/plugins/embedart`: Avoid resizing the image multiple times when - embedding into an album. Thanks to :user:`kerobaros`. :bug:`1028`, - :bug:`1036` -* :doc:`/plugins/discogs`: Avoid a situation where a trailing comma could be +- Date tags that use slashes instead of dashes as separators are now interpreted + correctly. And WMA (ASF) files now map the ``comments`` field to the + "Description" tag (in addition to "WM/Comments"). Thanks to Matthias Kiefer. + :bug:`1043` +- :doc:`/plugins/embedart`: Avoid resizing the image multiple times when + embedding into an album. Thanks to :user:`kerobaros`. :bug:`1028`, :bug:`1036` +- :doc:`/plugins/discogs`: Avoid a situation where a trailing comma could be appended to some artist names. :bug:`1049` -* The output of the :ref:`stats-cmd` command is slightly different: the +- The output of the :ref:`stats-cmd` command is slightly different: the approximate size is now marked as such, and the total number of seconds only appears in exact mode. -* :doc:`/plugins/convert`: A new ``copy_album_art`` option puts images - alongside converted files. Thanks to Ángel Alonso. :bug:`1050`, :bug:`1055` -* There is no longer a "conflict" between two plugins that declare the same +- :doc:`/plugins/convert`: A new ``copy_album_art`` option puts images alongside + converted files. Thanks to Ángel Alonso. :bug:`1050`, :bug:`1055` +- There is no longer a "conflict" between two plugins that declare the same field with the same type. Thanks to Peter Schnebel. :bug:`1059` :bug:`1061` -* :doc:`/plugins/chroma`: Limit the number of releases and recordings fetched - as the result of an Acoustid match to avoid extremely long processing times - for very popular music. :bug:`1068` -* Fix an issue where modifying an album's field without actually changing it +- :doc:`/plugins/chroma`: Limit the number of releases and recordings fetched as + the result of an Acoustid match to avoid extremely long processing times for + very popular music. :bug:`1068` +- Fix an issue where modifying an album's field without actually changing it would not update the corresponding tracks to bring differing tracks back in line with the album. :bug:`856` -* ``echonest`` plugin: When communicating with the Echo Nest servers - fails repeatedly, log an error instead of exiting. :bug:`1096` -* :doc:`/plugins/lyrics`: Avoid an error when the Google source returns a - result without a title. Thanks to Alberto Leal. :bug:`1097` -* Importing an archive will no longer leave temporary files behind in - ``/tmp``. Thanks to :user:`multikatt`. :bug:`1067`, :bug:`1091` +- ``echonest`` plugin: When communicating with the Echo Nest servers fails + repeatedly, log an error instead of exiting. :bug:`1096` +- :doc:`/plugins/lyrics`: Avoid an error when the Google source returns a result + without a title. Thanks to Alberto Leal. :bug:`1097` +- Importing an archive will no longer leave temporary files behind in ``/tmp``. + Thanks to :user:`multikatt`. :bug:`1067`, :bug:`1091` 1.3.8 (September 17, 2014) -------------------------- @@ -3146,221 +2897,218 @@ This release has two big new chunks of functionality. Queries now support **sorting** and user-defined fields can now have **types**. If you want to see all your songs in reverse chronological order, just type -``beet list year-``. It couldn't be easier. For details, see -:ref:`query-sort`. +``beet list year-``. It couldn't be easier. For details, see :ref:`query-sort`. Flexible field types mean that some functionality that has previously only worked for built-in fields, like range queries, can now work with plugin- and user-defined fields too. For starters, the ``echonest`` plugin and -:doc:`/plugins/mpdstats` now mark the types of the fields they provide---so -you can now say, for example, ``beet ls liveness:0.5..1.5`` for the Echo Nest +:doc:`/plugins/mpdstats` now mark the types of the fields they provide---so you +can now say, for example, ``beet ls liveness:0.5..1.5`` for the Echo Nest "liveness" attribute. The :doc:`/plugins/types` makes it easy to specify field types in your config file. One upgrade note: if you use the :doc:`/plugins/discogs`, you will need to -upgrade the Discogs client library to use this version. Just type -``pip install -U discogs-client``. +upgrade the Discogs client library to use this version. Just type ``pip install +-U discogs-client``. Other new features: -* :doc:`/plugins/info`: Target files can now be specified through library +- :doc:`/plugins/info`: Target files can now be specified through library queries (in addition to filenames). The ``--library`` option prints library - fields instead of tags. Multiple files can be summarized together with the - new ``--summarize`` option. -* :doc:`/plugins/mbcollection`: A new option lets you automatically update - your collection on import. Thanks to Olin Gay. -* :doc:`/plugins/convert`: A new ``never_convert_lossy_files`` option can + fields instead of tags. Multiple files can be summarized together with the new + ``--summarize`` option. +- :doc:`/plugins/mbcollection`: A new option lets you automatically update your + collection on import. Thanks to Olin Gay. +- :doc:`/plugins/convert`: A new ``never_convert_lossy_files`` option can prevent lossy transcoding. Thanks to Simon Kohlmeyer. -* :doc:`/plugins/convert`: A new ``--yes`` command-line flag skips the +- :doc:`/plugins/convert`: A new ``--yes`` command-line flag skips the confirmation. Still more fixes and little improvements: -* Invalid state files don't crash the importer. -* :doc:`/plugins/lyrics`: Only strip featured artists and - parenthesized title suffixes if no lyrics for the original artist and - title were found. -* Fix a crash when reading some files with missing tags. -* :doc:`/plugins/discogs`: Compatibility with the new 2.0 version of the - `discogs_client`_ Python library. If you were using the old version, you will +- Invalid state files don't crash the importer. +- :doc:`/plugins/lyrics`: Only strip featured artists and parenthesized title + suffixes if no lyrics for the original artist and title were found. +- Fix a crash when reading some files with missing tags. +- :doc:`/plugins/discogs`: Compatibility with the new 2.0 version of the + discogs_client_ Python library. If you were using the old version, you will need to upgrade to the latest version of the library to use the - correspondingly new version of the plugin (e.g., with - ``pip install -U discogs-client``). Thanks to Andriy Kohut. -* Fix a crash when writing files that can't be read. Thanks to Jocelyn De La + correspondingly new version of the plugin (e.g., with ``pip install -U + discogs-client``). Thanks to Andriy Kohut. +- Fix a crash when writing files that can't be read. Thanks to Jocelyn De La Rosa. -* The :ref:`stats-cmd` command now counts album artists. The album count also +- The :ref:`stats-cmd` command now counts album artists. The album count also more accurately reflects the number of albums in the database. -* :doc:`/plugins/convert`: Avoid crashes when tags cannot be written to newly +- :doc:`/plugins/convert`: Avoid crashes when tags cannot be written to newly converted files. -* Formatting templates with item data no longer confusingly shows album-level +- Formatting templates with item data no longer confusingly shows album-level data when the two are inconsistent. -* Resuming imports and beginning incremental imports should now be much faster +- Resuming imports and beginning incremental imports should now be much faster when there is a lot of previously-imported music to skip. -* :doc:`/plugins/lyrics`: Remove ``<script>`` tags from scraped lyrics. Thanks +- :doc:`/plugins/lyrics`: Remove ``<script>`` tags from scraped lyrics. Thanks to Bombardment. -* :doc:`/plugins/play`: Add a ``relative_to`` config option. Thanks to +- :doc:`/plugins/play`: Add a ``relative_to`` config option. Thanks to BrainDamage. -* Fix a crash when a MusicBrainz release has zero tracks. -* The ``--version`` flag now works as an alias for the ``version`` command. -* :doc:`/plugins/lastgenre`: Remove some unhelpful genres from the default +- Fix a crash when a MusicBrainz release has zero tracks. +- The ``--version`` flag now works as an alias for the ``version`` command. +- :doc:`/plugins/lastgenre`: Remove some unhelpful genres from the default whitelist. Thanks to gwern. -* :doc:`/plugins/importfeeds`: A new ``echo`` output mode prints files' paths - to standard error. Thanks to robotanarchy. -* :doc:`/plugins/replaygain`: Restore some error handling when ``mp3gain`` +- :doc:`/plugins/importfeeds`: A new ``echo`` output mode prints files' paths to + standard error. Thanks to robotanarchy. +- :doc:`/plugins/replaygain`: Restore some error handling when ``mp3gain`` output cannot be parsed. The verbose log now contains the bad tool output in this case. -* :doc:`/plugins/convert`: Fix filename extensions when converting +- :doc:`/plugins/convert`: Fix filename extensions when converting automatically. -* The ``write`` plugin event allows plugins to change the tags that are - written to a media file. -* :doc:`/plugins/zero`: Do not delete database values; only media file - tags are affected. +- The ``write`` plugin event allows plugins to change the tags that are written + to a media file. +- :doc:`/plugins/zero`: Do not delete database values; only media file tags are + affected. .. _discogs_client: https://github.com/discogs/discogs_client 1.3.7 (August 22, 2014) ----------------------- -This release of beets fixes all the bugs, and you can be confident that you -will never again find any bugs in beets, ever. -It also adds support for plain old AIFF files and adds three more plugins, -including a nifty one that lets you measure a song's tempo by tapping out the -beat on your keyboard. -The importer deals more elegantly with duplicates and you can broaden your -cover art search to the entire web with Google Image Search. +This release of beets fixes all the bugs, and you can be confident that you will +never again find any bugs in beets, ever. It also adds support for plain old +AIFF files and adds three more plugins, including a nifty one that lets you +measure a song's tempo by tapping out the beat on your keyboard. The importer +deals more elegantly with duplicates and you can broaden your cover art search +to the entire web with Google Image Search. The big new features are: -* Support for AIFF files. Tags are stored as ID3 frames in one of the file's - IFF chunks. Thanks to Evan Purkhiser for contributing support to `Mutagen`_. -* The new :doc:`/plugins/importadded` reads files' modification times to set +- Support for AIFF files. Tags are stored as ID3 frames in one of the file's IFF + chunks. Thanks to Evan Purkhiser for contributing support to Mutagen_. +- The new :doc:`/plugins/importadded` reads files' modification times to set their "added" date. Thanks to Stig Inge Lea Bjørnsen. -* The new :doc:`/plugins/bpm` lets you manually measure the tempo of a playing +- The new :doc:`/plugins/bpm` lets you manually measure the tempo of a playing song. Thanks to aroquen. -* The new :doc:`/plugins/spotify` generates playlists for your `Spotify`_ - account. Thanks to Olin Gay. -* A new :ref:`required` configuration option for the importer skips matches - that are missing certain data. Thanks to oprietop. -* When the importer detects duplicates, it now shows you some details about - the potentially-replaced music so you can make an informed decision. Thanks - to Howard Jones. -* :doc:`/plugins/fetchart`: You can now optionally search for cover art on +- The new :doc:`/plugins/spotify` generates playlists for your Spotify_ account. + Thanks to Olin Gay. +- A new :ref:`required` configuration option for the importer skips matches that + are missing certain data. Thanks to oprietop. +- When the importer detects duplicates, it now shows you some details about the + potentially-replaced music so you can make an informed decision. Thanks to + Howard Jones. +- :doc:`/plugins/fetchart`: You can now optionally search for cover art on Google Image Search. Thanks to Lemutar. -* A new :ref:`asciify-paths` configuration option replaces all non-ASCII +- A new :ref:`asciify-paths` configuration option replaces all non-ASCII characters in paths. -.. _Mutagen: https://github.com/quodlibet/mutagen -.. _Spotify: https://www.spotify.com/ +.. _mutagen: https://github.com/quodlibet/mutagen + +.. _spotify: https://www.spotify.com/ And the multitude of little improvements and fixes: -* Compatibility with the latest version of `Mutagen`_, 1.23. -* :doc:`/plugins/web`: Lyrics now display readably with correct line breaks. +- Compatibility with the latest version of Mutagen_, 1.23. +- :doc:`/plugins/web`: Lyrics now display readably with correct line breaks. Also, the detail view scrolls to reveal all of the lyrics. Thanks to Meet Udeshi. -* :doc:`/plugins/play`: The ``command`` config option can now contain - arguments (rather than just an executable). Thanks to Alessandro Ghedini. -* Fix an error when using the :ref:`modify-cmd` command to remove a flexible +- :doc:`/plugins/play`: The ``command`` config option can now contain arguments + (rather than just an executable). Thanks to Alessandro Ghedini. +- Fix an error when using the :ref:`modify-cmd` command to remove a flexible attribute. Thanks to Pierre Rust. -* :doc:`/plugins/info`: The command now shows audio properties (e.g., bitrate) +- :doc:`/plugins/info`: The command now shows audio properties (e.g., bitrate) in addition to metadata. Thanks Alessandro Ghedini. -* Avoid a crash on Windows when writing to files with special characters in +- Avoid a crash on Windows when writing to files with special characters in their names. -* :doc:`/plugins/play`: Playing albums now generates filenames by default (as +- :doc:`/plugins/play`: Playing albums now generates filenames by default (as opposed to directories) for better compatibility. The ``use_folders`` option restores the old behavior. Thanks to Lucas Duailibe. -* Fix an error when importing an empty directory with the ``--flat`` option. -* :doc:`/plugins/mpdstats`: The last song in a playlist is now correctly - counted as played. Thanks to Johann Klähn. -* :doc:`/plugins/zero`: Prevent accidental nulling of dangerous fields (IDs - and paths). Thanks to brunal. -* The :ref:`remove-cmd` command now shows the paths of files that will be +- Fix an error when importing an empty directory with the ``--flat`` option. +- :doc:`/plugins/mpdstats`: The last song in a playlist is now correctly counted + as played. Thanks to Johann Klähn. +- :doc:`/plugins/zero`: Prevent accidental nulling of dangerous fields (IDs and + paths). Thanks to brunal. +- The :ref:`remove-cmd` command now shows the paths of files that will be deleted. Thanks again to brunal. -* Don't display changes for fields that are not in the restricted field set. - This fixes :ref:`write-cmd` showing changes for fields that are not written - to the file. -* The :ref:`write-cmd` command avoids displaying the item name if there are - no changes for it. -* When using both the :doc:`/plugins/convert` and the :doc:`/plugins/scrub`, +- Don't display changes for fields that are not in the restricted field set. + This fixes :ref:`write-cmd` showing changes for fields that are not written to + the file. +- The :ref:`write-cmd` command avoids displaying the item name if there are no + changes for it. +- When using both the :doc:`/plugins/convert` and the :doc:`/plugins/scrub`, avoid scrubbing the source file of conversions. (Fix a regression introduced in the previous release.) -* :doc:`/plugins/replaygain`: Logging is now quieter during import. Thanks to +- :doc:`/plugins/replaygain`: Logging is now quieter during import. Thanks to Yevgeny Bezman. -* :doc:`/plugins/fetchart`: When loading art from the filesystem, we now +- :doc:`/plugins/fetchart`: When loading art from the filesystem, we now prioritize covers with more keywords in them. This means that ``cover-front.jpg`` will now be taken before ``cover-back.jpg`` because it contains two keywords rather than one. Thanks to Fabrice Laporte. -* :doc:`/plugins/lastgenre`: Remove duplicates from canonicalized genre lists. +- :doc:`/plugins/lastgenre`: Remove duplicates from canonicalized genre lists. Thanks again to Fabrice Laporte. -* The importer now records its progress when skipping albums. This means that +- The importer now records its progress when skipping albums. This means that incremental imports will no longer try to import albums again after you've - chosen to skip them, and erroneous invitations to resume "interrupted" - imports should be reduced. Thanks to jcassette. -* :doc:`/plugins/bucket`: You can now customize the definition of alphanumeric - "ranges" using regular expressions. And the heuristic for detecting years - has been improved. Thanks to sotho. -* Already-imported singleton tracks are skipped when resuming an - import. -* :doc:`/plugins/chroma`: A new ``auto`` configuration option disables + chosen to skip them, and erroneous invitations to resume "interrupted" imports + should be reduced. Thanks to jcassette. +- :doc:`/plugins/bucket`: You can now customize the definition of alphanumeric + "ranges" using regular expressions. And the heuristic for detecting years has + been improved. Thanks to sotho. +- Already-imported singleton tracks are skipped when resuming an import. +- :doc:`/plugins/chroma`: A new ``auto`` configuration option disables fingerprinting on import. Thanks to ddettrittus. -* :doc:`/plugins/convert`: A new ``--format`` option to can select the +- :doc:`/plugins/convert`: A new ``--format`` option to can select the transcoding preset from the command-line. -* :doc:`/plugins/convert`: Transcoding presets can now omit their filename +- :doc:`/plugins/convert`: Transcoding presets can now omit their filename extensions (extensions default to the name of the preset). -* :doc:`/plugins/convert`: A new ``--pretend`` option lets you preview the - commands the plugin will execute without actually taking any action. Thanks - to Dietrich Daroch. -* Fix a crash when a float-valued tag field only contained a ``+`` or ``-`` +- :doc:`/plugins/convert`: A new ``--pretend`` option lets you preview the + commands the plugin will execute without actually taking any action. Thanks to + Dietrich Daroch. +- Fix a crash when a float-valued tag field only contained a ``+`` or ``-`` character. -* Fixed a regression in the core that caused the :doc:`/plugins/scrub` not to +- Fixed a regression in the core that caused the :doc:`/plugins/scrub` not to work in ``auto`` mode. Thanks to Harry Khanna. -* The :ref:`write-cmd` command now has a ``--force`` flag. Thanks again to - Harry Khanna. -* :doc:`/plugins/mbsync`: Track alignment now works with albums that have +- The :ref:`write-cmd` command now has a ``--force`` flag. Thanks again to Harry + Khanna. +- :doc:`/plugins/mbsync`: Track alignment now works with albums that have multiple copies of the same recording. Thanks to Rui Gonçalves. 1.3.6 (May 10, 2014) -------------------- -This is primarily a bugfix release, but it also brings two new plugins: one -for playing music in desktop players and another for organizing your -directories into "buckets." It also brings huge performance optimizations to -queries---your ``beet ls`` commands will now go much faster. +This is primarily a bugfix release, but it also brings two new plugins: one for +playing music in desktop players and another for organizing your directories +into "buckets." It also brings huge performance optimizations to queries---your +``beet ls`` commands will now go much faster. New features: -* The new :doc:`/plugins/play` lets you start your desktop music player with - the songs that match a query. Thanks to David Hamp-Gonsalves. -* The new :doc:`/plugins/bucket` provides a ``%bucket{}`` function for path +- The new :doc:`/plugins/play` lets you start your desktop music player with the + songs that match a query. Thanks to David Hamp-Gonsalves. +- The new :doc:`/plugins/bucket` provides a ``%bucket{}`` function for path formatting to generate folder names representing ranges of years or initial letter. Thanks to Fabrice Laporte. -* Item and album queries are much faster. -* :doc:`/plugins/ftintitle`: A new option lets you remove featured artists +- Item and album queries are much faster. +- :doc:`/plugins/ftintitle`: A new option lets you remove featured artists entirely instead of moving them to the title. Thanks to SUTJael. And those all-important bug fixes: -* :doc:`/plugins/mbsync`: Fix a regression in 1.3.5 that broke the plugin +- :doc:`/plugins/mbsync`: Fix a regression in 1.3.5 that broke the plugin entirely. -* :ref:`Shell completion <completion>` now searches more common paths for its +- :ref:`Shell completion <completion>` now searches more common paths for its ``bash_completion`` dependency. -* Fix encoding-related logging errors in :doc:`/plugins/convert` and +- Fix encoding-related logging errors in :doc:`/plugins/convert` and :doc:`/plugins/replaygain`. -* :doc:`/plugins/replaygain`: Suppress a deprecation warning emitted by later +- :doc:`/plugins/replaygain`: Suppress a deprecation warning emitted by later versions of PyGI. -* Fix a crash when reading files whose iTunes SoundCheck tags contain - non-ASCII characters. -* The ``%if{}`` template function now appropriately interprets the condition - as false when it contains the string "false". Thanks to Ayberk Yilmaz. -* :doc:`/plugins/convert`: Fix conversion for files that include a video - stream by ignoring it. Thanks to brunal. -* :doc:`/plugins/fetchart`: Log an error instead of crashing when tag +- Fix a crash when reading files whose iTunes SoundCheck tags contain non-ASCII + characters. +- The ``%if{}`` template function now appropriately interprets the condition as + false when it contains the string "false". Thanks to Ayberk Yilmaz. +- :doc:`/plugins/convert`: Fix conversion for files that include a video stream + by ignoring it. Thanks to brunal. +- :doc:`/plugins/fetchart`: Log an error instead of crashing when tag manipulation fails. -* :doc:`/plugins/convert`: Log an error instead of crashing when - embedding album art fails. -* :doc:`/plugins/convert`: Embed cover art into converted files. - Previously they were embedded into the source files. -* New plugin event: `before_item_moved`. Thanks to Robert Speicher. +- :doc:`/plugins/convert`: Log an error instead of crashing when embedding album + art fails. +- :doc:`/plugins/convert`: Embed cover art into converted files. Previously they + were embedded into the source files. +- New plugin event: ``before_item_moved``. Thanks to Robert Speicher. 1.3.5 (April 15, 2014) ---------------------- @@ -3370,59 +3118,59 @@ support for tracking and calculating musical keys, the ReplayGain plugin was expanded to work with more music formats via GStreamer, we can now import directly from compressed archives, and the lyrics plugin is more robust. -One note for upgraders and packagers: this version of beets has a new -dependency in `enum34`_, which is a backport of the new `enum`_ standard -library module. +One note for upgraders and packagers: this version of beets has a new dependency +in enum34_, which is a backport of the new enum_ standard library module. The major new features are: -* Beets can now import `zip`, `tar`, and `rar` archives. Just type ``beet +- Beets can now import ``zip``, ``tar``, and ``rar`` archives. Just type ``beet import music.zip`` to have beets transparently extract the files to import. -* :doc:`/plugins/replaygain`: Added support for calculating ReplayGain values +- :doc:`/plugins/replaygain`: Added support for calculating ReplayGain values with GStreamer as well the mp3gain program. This enables ReplayGain calculation for any audio format. Thanks to Yevgeny Bezman. -* :doc:`/plugins/lyrics`: Lyrics should now be found for more songs. Searching - is now sensitive to featured artists and parenthesized title suffixes. - When a song has multiple titles, lyrics from all the named songs are now +- :doc:`/plugins/lyrics`: Lyrics should now be found for more songs. Searching + is now sensitive to featured artists and parenthesized title suffixes. When a + song has multiple titles, lyrics from all the named songs are now concatenated. Thanks to Fabrice Laporte and Paul Phillips. -In particular, a full complement of features for supporting musical keys are -new in this release: +In particular, a full complement of features for supporting musical keys are new +in this release: -* A new `initial_key` field is available in the database and files' tags. You +- A new ``initial_key`` field is available in the database and files' tags. You can set the field manually using a command like ``beet modify initial_key=Am``. -* The ``echonest`` plugin sets the `initial_key` field if the data is +- The ``echonest`` plugin sets the ``initial_key`` field if the data is available. -* A new :doc:`/plugins/keyfinder` runs a command-line tool to get the key from - audio data and store it in the `initial_key` field. +- A new :doc:`/plugins/keyfinder` runs a command-line tool to get the key from + audio data and store it in the ``initial_key`` field. There are also many bug fixes and little enhancements: -* ``echonest`` plugin: Truncate files larger than 50MB before uploading for +- ``echonest`` plugin: Truncate files larger than 50MB before uploading for analysis. -* :doc:`/plugins/fetchart`: Fix a crash when the server does not specify a +- :doc:`/plugins/fetchart`: Fix a crash when the server does not specify a content type. Thanks to Lee Reinhardt. -* :doc:`/plugins/convert`: The ``--keep-new`` flag now works correctly - and the library includes the converted item. -* The importer now logs a message instead of crashing when errors occur while +- :doc:`/plugins/convert`: The ``--keep-new`` flag now works correctly and the + library includes the converted item. +- The importer now logs a message instead of crashing when errors occur while opening the files to be imported. -* :doc:`/plugins/embedart`: Better error messages in exceptional conditions. -* Silenced some confusing error messages when searching for a non-MusicBrainz +- :doc:`/plugins/embedart`: Better error messages in exceptional conditions. +- Silenced some confusing error messages when searching for a non-MusicBrainz ID. Using an invalid ID (of any kind---Discogs IDs can be used there too) at the "Enter ID:" importer prompt now just silently returns no results. More info is in the verbose logs. -* :doc:`/plugins/mbsync`: Fix application of album-level metadata. Due to a +- :doc:`/plugins/mbsync`: Fix application of album-level metadata. Due to a regression a few releases ago, only track-level metadata was being updated. -* On Windows, paths on network shares (UNC paths) no longer cause "invalid +- On Windows, paths on network shares (UNC paths) no longer cause "invalid filename" errors. -* :doc:`/plugins/replaygain`: Fix crashes when attempting to log errors. -* The :ref:`modify-cmd` command can now accept query arguments that contain = - signs. An argument is considered a query part when a : appears before any - =s. Thanks to mook. +- :doc:`/plugins/replaygain`: Fix crashes when attempting to log errors. +- The :ref:`modify-cmd` command can now accept query arguments that contain = + signs. An argument is considered a query part when a : appears before any =s. + Thanks to mook. + +.. _enum: https://docs.python.org/3.4/library/enum.html .. _enum34: https://pypi.python.org/pypi/enum34 -.. _enum: https://docs.python.org/3.4/library/enum.html 1.3.4 (April 5, 2014) --------------------- @@ -3434,71 +3182,70 @@ attributes. There are also some significant performance optimizations to the autotagger's matching logic. One note for upgraders: if you use the :doc:`/plugins/fetchart`, it has a new -dependency, the `requests`_ module. +dependency, the requests_ module. New stuff: -* Added a :ref:`config-cmd` command to manage your configuration. It can show +- Added a :ref:`config-cmd` command to manage your configuration. It can show you what you currently have in your config file, point you at where the file should be, or launch your text editor to let you modify the file. Thanks to geigerzaehler. -* Beets now ships with a shell command completion script! See - :ref:`completion`. Thanks to geigerzaehler. -* The :ref:`modify-cmd` command now allows removing flexible attributes. For +- Beets now ships with a shell command completion script! See :ref:`completion`. + Thanks to geigerzaehler. +- The :ref:`modify-cmd` command now allows removing flexible attributes. For example, ``beet modify artist:beatles oldies!`` deletes the ``oldies`` attribute from matching items. Thanks to brilnius. -* Internally, beets has laid the groundwork for supporting multi-valued - fields. Thanks to geigerzaehler. -* The importer interface now shows the URL for MusicBrainz matches. Thanks to +- Internally, beets has laid the groundwork for supporting multi-valued fields. + Thanks to geigerzaehler. +- The importer interface now shows the URL for MusicBrainz matches. Thanks to johtso. -* :doc:`/plugins/smartplaylist`: Playlists can now be generated from multiple - queries (combined with "or" logic). Album-level queries are also now - possible and automatic playlist regeneration can now be disabled. Thanks to - brilnius. -* ``echonest`` plugin: Echo Nest similarity now weights the tempo in - better proportion to other metrics. Also, options were added to specify - custom thresholds and output formats. Thanks to Adam M. -* Added the :ref:`after_write <plugin_events>` plugin event. -* :doc:`/plugins/lastgenre`: Separator in genre lists can now be - configured. Thanks to brilnius. -* We now only use "primary" aliases for artist names from MusicBrainz. This - eliminates some strange naming that could occur when the `languages` config +- :doc:`/plugins/smartplaylist`: Playlists can now be generated from multiple + queries (combined with "or" logic). Album-level queries are also now possible + and automatic playlist regeneration can now be disabled. Thanks to brilnius. +- ``echonest`` plugin: Echo Nest similarity now weights the tempo in better + proportion to other metrics. Also, options were added to specify custom + thresholds and output formats. Thanks to Adam M. +- Added the :ref:`after_write <plugin_events>` plugin event. +- :doc:`/plugins/lastgenre`: Separator in genre lists can now be configured. + Thanks to brilnius. +- We now only use "primary" aliases for artist names from MusicBrainz. This + eliminates some strange naming that could occur when the ``languages`` config option was set. Thanks to Filipe Fortes. -* The performance of the autotagger's matching mechanism is vastly improved. +- The performance of the autotagger's matching mechanism is vastly improved. This should be noticeable when matching against very large releases such as box sets. -* The :ref:`import-cmd` command can now accept individual files as arguments +- The :ref:`import-cmd` command can now accept individual files as arguments even in non-singleton mode. Files are imported as one-track albums. Fixes: -* Error messages involving paths no longer escape non-ASCII characters (for +- Error messages involving paths no longer escape non-ASCII characters (for legibility). -* Fixed a regression that made it impossible to use the :ref:`modify-cmd` +- Fixed a regression that made it impossible to use the :ref:`modify-cmd` command to add new flexible fields. Thanks to brilnius. -* ``echonest`` plugin: Avoid crashing when the audio analysis fails. - Thanks to Pedro Silva. -* :doc:`/plugins/duplicates`: Fix checksumming command execution for files - with quotation marks in their names. Thanks again to Pedro Silva. -* Fix a crash when importing with both of the :ref:`group_albums` and +- ``echonest`` plugin: Avoid crashing when the audio analysis fails. Thanks to + Pedro Silva. +- :doc:`/plugins/duplicates`: Fix checksumming command execution for files with + quotation marks in their names. Thanks again to Pedro Silva. +- Fix a crash when importing with both of the :ref:`group_albums` and :ref:`incremental` options enabled. Thanks to geigerzaehler. -* Give a sensible error message when ``BEETSDIR`` points to a file. Thanks - again to geigerzaehler. -* Fix a crash when reading WMA files whose boolean-valued fields contain +- Give a sensible error message when ``BEETSDIR`` points to a file. Thanks again + to geigerzaehler. +- Fix a crash when reading WMA files whose boolean-valued fields contain strings. Thanks to johtso. -* :doc:`/plugins/fetchart`: The plugin now sends "beets" as the User-Agent - when making scraping requests. This helps resolve some blocked requests. The - plugin now also depends on the `requests`_ Python library. -* The :ref:`write-cmd` command now only shows the changes to fields that will +- :doc:`/plugins/fetchart`: The plugin now sends "beets" as the User-Agent when + making scraping requests. This helps resolve some blocked requests. The plugin + now also depends on the requests_ Python library. +- The :ref:`write-cmd` command now only shows the changes to fields that will actually be written to a file. -* :doc:`/plugins/duplicates`: Spurious reports are now avoided for tracks with +- :doc:`/plugins/duplicates`: Spurious reports are now avoided for tracks with missing values (e.g., no MBIDs). Thanks to Pedro Silva. -* The default :ref:`replace` sanitation options now remove leading whitespace - by default. Thanks to brilnius. -* :doc:`/plugins/importfeeds`: Fix crash when importing albums - containing ``/`` with the ``m3u_multi`` format. -* Avoid crashing on Mutagen bugs while writing files' tags. -* :doc:`/plugins/convert`: Display a useful error message when the FFmpeg +- The default :ref:`replace` sanitation options now remove leading whitespace by + default. Thanks to brilnius. +- :doc:`/plugins/importfeeds`: Fix crash when importing albums containing ``/`` + with the ``m3u_multi`` format. +- Avoid crashing on Mutagen bugs while writing files' tags. +- :doc:`/plugins/convert`: Display a useful error message when the FFmpeg executable can't be found. .. _requests: https://requests.readthedocs.io/en/master/ @@ -3511,96 +3258,93 @@ internally. Along with laying the groundwork for some great things in the future, this brings a number of improvements to how you interact with beets. Here's what's new with fields in particular: -* Plugin-provided fields can now be used in queries. For example, if you use - the :doc:`/plugins/inline` to define a field called ``era``, you can now - filter your library based on that field by typing something like - ``beet list era:goldenage``. -* Album-level flexible attributes and plugin-provided attributes can now be - used in path formats (and other item-level templates). -* :ref:`Date-based queries <datequery>` are now possible. Try getting every +- Plugin-provided fields can now be used in queries. For example, if you use the + :doc:`/plugins/inline` to define a field called ``era``, you can now filter + your library based on that field by typing something like ``beet list + era:goldenage``. +- Album-level flexible attributes and plugin-provided attributes can now be used + in path formats (and other item-level templates). +- :ref:`Date-based queries <datequery>` are now possible. Try getting every track you added in February 2014 with ``beet ls added:2014-02`` or in the whole decade with ``added:2010..``. Thanks to Stig Inge Lea Bjørnsen. -* The :ref:`modify-cmd` command is now better at parsing and formatting - fields. You can assign to boolean fields like ``comp``, for example, using - either the words "true" or "false" or the numerals 1 and 0. Any - boolean-esque value is normalized to a real boolean. The :ref:`update-cmd` - and :ref:`write-cmd` commands also got smarter at formatting and colorizing - changes. +- The :ref:`modify-cmd` command is now better at parsing and formatting fields. + You can assign to boolean fields like ``comp``, for example, using either the + words "true" or "false" or the numerals 1 and 0. Any boolean-esque value is + normalized to a real boolean. The :ref:`update-cmd` and :ref:`write-cmd` + commands also got smarter at formatting and colorizing changes. For developers, the short version of the story is that Item and Album objects provide *uniform access* across fixed, flexible, and computed attributes. You -can write ``item.foo`` to access the ``foo`` field without worrying about -where the data comes from. +can write ``item.foo`` to access the ``foo`` field without worrying about where +the data comes from. Unrelated new stuff: -* The importer has a new interactive option (*G* for "Group albums"), +- The importer has a new interactive option (*G* for "Group albums"), command-line flag (``--group-albums``), and config option - (:ref:`group_albums`) that lets you split apart albums that are mixed - together in a single directory. Thanks to geigerzaehler. -* A new ``--config`` command-line option lets you specify an additional + (:ref:`group_albums`) that lets you split apart albums that are mixed together + in a single directory. Thanks to geigerzaehler. +- A new ``--config`` command-line option lets you specify an additional configuration file. This option *combines* config settings with your default - config file. (As part of this change, the ``BEETSDIR`` environment variable - no longer combines---it *replaces* your default config file.) Thanks again - to geigerzaehler. -* :doc:`/plugins/ihate`: The plugin's configuration interface was overhauled. + config file. (As part of this change, the ``BEETSDIR`` environment variable no + longer combines---it *replaces* your default config file.) Thanks again to + geigerzaehler. +- :doc:`/plugins/ihate`: The plugin's configuration interface was overhauled. Its configuration is now much simpler---it uses beets queries instead of an - ad-hoc per-field configuration. This is *backwards-incompatible*---if you - use this plugin, you will need to update your configuration. Thanks to + ad-hoc per-field configuration. This is *backwards-incompatible*---if you use + this plugin, you will need to update your configuration. Thanks to BrainDamage. Other little fixes: -* ``echonest`` plugin: Tempo (BPM) is now always stored as an integer. - Thanks to Heinz Wiesinger. -* Fix Python 2.6 compatibility in some logging statements in +- ``echonest`` plugin: Tempo (BPM) is now always stored as an integer. Thanks to + Heinz Wiesinger. +- Fix Python 2.6 compatibility in some logging statements in :doc:`/plugins/chroma` and :doc:`/plugins/lastgenre`. -* Prevent some crashes when things go really wrong when writing file metadata - at the end of the import process. -* New plugin events: ``item_removed`` (thanks to Romuald Conty) and +- Prevent some crashes when things go really wrong when writing file metadata at + the end of the import process. +- New plugin events: ``item_removed`` (thanks to Romuald Conty) and ``item_copied`` (thanks to Stig Inge Lea Bjørnsen). -* The ``pluginpath`` config option can now point to the directory containing +- The ``pluginpath`` config option can now point to the directory containing plugin code. (Previously, it awkwardly needed to point at a directory - containing a ``beetsplug`` directory, which would then contain your code. - This is preserved as an option for backwards compatibility.) This change - should also work around a long-standing issue when using ``pluginpath`` when - beets is installed using pip. Many thanks to geigerzaehler. -* :doc:`/plugins/web`: The ``/item/`` and ``/album/`` API endpoints now - produce full details about albums and items, not just lists of IDs. Thanks - to geigerzaehler. -* Fix a potential crash when using image resizing with the + containing a ``beetsplug`` directory, which would then contain your code. This + is preserved as an option for backwards compatibility.) This change should + also work around a long-standing issue when using ``pluginpath`` when beets is + installed using pip. Many thanks to geigerzaehler. +- :doc:`/plugins/web`: The ``/item/`` and ``/album/`` API endpoints now produce + full details about albums and items, not just lists of IDs. Thanks to + geigerzaehler. +- Fix a potential crash when using image resizing with the :doc:`/plugins/fetchart` or :doc:`/plugins/embedart` without ImageMagick installed. -* Also, when invoking ``convert`` for image resizing fails, we now log an - error instead of crashing. -* :doc:`/plugins/fetchart`: The ``beet fetchart`` command can now associate - local images with albums (unless ``--force`` is provided). Thanks to +- Also, when invoking ``convert`` for image resizing fails, we now log an error + instead of crashing. +- :doc:`/plugins/fetchart`: The ``beet fetchart`` command can now associate + local images with albums (unless ``--force`` is provided). Thanks to brilnius. +- :doc:`/plugins/fetchart`: Command output is now colorized. Thanks again to brilnius. -* :doc:`/plugins/fetchart`: Command output is now colorized. Thanks again to - brilnius. -* The :ref:`modify-cmd` command avoids writing files and committing to the +- The :ref:`modify-cmd` command avoids writing files and committing to the database when nothing has changed. Thanks once more to brilnius. -* The importer now uses the album artist field when guessing existing - metadata for albums (rather than just the track artist field). Thanks to - geigerzaehler. -* :doc:`/plugins/fromfilename`: Fix a crash when a filename contained only a +- The importer now uses the album artist field when guessing existing metadata + for albums (rather than just the track artist field). Thanks to geigerzaehler. +- :doc:`/plugins/fromfilename`: Fix a crash when a filename contained only a track number (e.g., ``02.mp3``). -* :doc:`/plugins/convert`: Transcoding should now work on Windows. -* :doc:`/plugins/duplicates`: The ``move`` and ``copy`` destination arguments +- :doc:`/plugins/convert`: Transcoding should now work on Windows. +- :doc:`/plugins/duplicates`: The ``move`` and ``copy`` destination arguments are now treated as directories. Thanks to Pedro Silva. -* The :ref:`modify-cmd` command now skips confirmation and prints a message if +- The :ref:`modify-cmd` command now skips confirmation and prints a message if no changes are necessary. Thanks to brilnius. -* :doc:`/plugins/fetchart`: When using the ``remote_priority`` config option, +- :doc:`/plugins/fetchart`: When using the ``remote_priority`` config option, local image files are no longer completely ignored. -* ``echonest`` plugin: Fix an issue causing the plugin to appear twice in - the output of the ``beet version`` command. -* :doc:`/plugins/lastgenre`: Fix an occasional crash when no tag weight was +- ``echonest`` plugin: Fix an issue causing the plugin to appear twice in the + output of the ``beet version`` command. +- :doc:`/plugins/lastgenre`: Fix an occasional crash when no tag weight was returned by Last.fm. -* :doc:`/plugins/mpdstats`: Restore the ``last_played`` field. Thanks to - Johann Klähn. -* The :ref:`modify-cmd` command's output now clearly shows when a file has - been deleted. -* Album art in files with Vorbis Comments is now marked with the "front cover" +- :doc:`/plugins/mpdstats`: Restore the ``last_played`` field. Thanks to Johann + Klähn. +- The :ref:`modify-cmd` command's output now clearly shows when a file has been + deleted. +- Album art in files with Vorbis Comments is now marked with the "front cover" type. Thanks to Jason Lefley. 1.3.2 (December 22, 2013) @@ -3610,144 +3354,139 @@ This update brings new plugins for fetching acoustic metrics and listening statistics, many more options for the duplicate detection plugin, and flexible options for fetching multiple genres. -The "core" of beets gained a new built-in command: :ref:`beet write -<write-cmd>` updates the metadata tags for files, bringing them back -into sync with your database. Thanks to Heinz Wiesinger. +The "core" of beets gained a new built-in command: :ref:`beet write <write-cmd>` +updates the metadata tags for files, bringing them back into sync with your +database. Thanks to Heinz Wiesinger. We added some plugins and overhauled some existing ones: -* The new ``echonest`` plugin plugin can fetch a wide range of `acoustic - attributes`_ from `The Echo Nest`_, including the "speechiness" and - "liveness" of each track. The new plugin supersedes an older version - (``echonest_tempo``) that only fetched the BPM field. Thanks to Pedro Silva - and Peter Schnebel. - -* The :doc:`/plugins/duplicates` got a number of new features, thanks to Pedro +- The new ``echonest`` plugin plugin can fetch a wide range of `acoustic + attributes`_ from `The Echo Nest`_, including the "speechiness" and "liveness" + of each track. The new plugin supersedes an older version (``echonest_tempo``) + that only fetched the BPM field. Thanks to Pedro Silva and Peter Schnebel. +- The :doc:`/plugins/duplicates` got a number of new features, thanks to Pedro Silva: - * The ``keys`` option lets you specify the fields used detect duplicates. - * You can now use checksumming (via an external command) to find - duplicates instead of metadata via the ``checksum`` option. - * The plugin can perform actions on the duplicates it find. The new - ``copy``, ``move``, ``delete``, ``delete_file``, and ``tag`` options - perform those actions. + - The ``keys`` option lets you specify the fields used detect duplicates. + - You can now use checksumming (via an external command) to find duplicates + instead of metadata via the ``checksum`` option. + - The plugin can perform actions on the duplicates it find. The new ``copy``, + ``move``, ``delete``, ``delete_file``, and ``tag`` options perform those + actions. -* The new :doc:`/plugins/mpdstats` collects statistics about your - listening habits from `MPD`_. Thanks to Peter Schnebel and Johann Klähn. - -* :doc:`/plugins/lastgenre`: The new ``multiple`` option has been replaced - with the ``count`` option, which lets you limit the number of genres added - to your music. (No more thousand-character genre fields!) Also, the - ``min_weight`` field filters out nonsense tags to make your genres more - relevant. Thanks to Peter Schnebel and rashley60. - -* :doc:`/plugins/lyrics`: A new ``--force`` option optionally re-downloads +- The new :doc:`/plugins/mpdstats` collects statistics about your listening + habits from MPD_. Thanks to Peter Schnebel and Johann Klähn. +- :doc:`/plugins/lastgenre`: The new ``multiple`` option has been replaced with + the ``count`` option, which lets you limit the number of genres added to your + music. (No more thousand-character genre fields!) Also, the ``min_weight`` + field filters out nonsense tags to make your genres more relevant. Thanks to + Peter Schnebel and rashley60. +- :doc:`/plugins/lyrics`: A new ``--force`` option optionally re-downloads lyrics even when files already have them. Thanks to Bitdemon. As usual, there are also innumerable little fixes and improvements: -* When writing ID3 tags for ReplayGain normalization, tags are written with - both upper-case and lower-case TXXX frame descriptions. Previous versions of - beets used only the upper-case style, which seems to be more standard, but - some players (namely, Quod Libet and foobar2000) seem to only use lower-case - names. -* :doc:`/plugins/missing`: Avoid a possible error when an album's - ``tracktotal`` field is missing. -* :doc:`/plugins/ftintitle`: Fix an error when the sort artist is missing. -* ``echonest_tempo``: The plugin should now match songs more - reliably (i.e., fewer "no tempo found" messages). Thanks to Peter Schnebel. -* :doc:`/plugins/convert`: Fix an "Item has no library" error when using the +- When writing ID3 tags for ReplayGain normalization, tags are written with both + upper-case and lower-case TXXX frame descriptions. Previous versions of beets + used only the upper-case style, which seems to be more standard, but some + players (namely, Quod Libet and foobar2000) seem to only use lower-case names. +- :doc:`/plugins/missing`: Avoid a possible error when an album's ``tracktotal`` + field is missing. +- :doc:`/plugins/ftintitle`: Fix an error when the sort artist is missing. +- ``echonest_tempo``: The plugin should now match songs more reliably (i.e., + fewer "no tempo found" messages). Thanks to Peter Schnebel. +- :doc:`/plugins/convert`: Fix an "Item has no library" error when using the ``auto`` config option. -* :doc:`/plugins/convert`: Fix an issue where files of the wrong format would +- :doc:`/plugins/convert`: Fix an issue where files of the wrong format would have their transcoding skipped (and files with the right format would be needlessly transcoded). Thanks to Jakob Schnitzer. -* Fix an issue that caused the :ref:`id3v23` option to work only occasionally. -* Also fix using :ref:`id3v23` in conjunction with the ``scrub`` and +- Fix an issue that caused the :ref:`id3v23` option to work only occasionally. +- Also fix using :ref:`id3v23` in conjunction with the ``scrub`` and ``embedart`` plugins. Thanks to Chris Cogburn. -* :doc:`/plugins/ihate`: Fix an error when importing singletons. Thanks to +- :doc:`/plugins/ihate`: Fix an error when importing singletons. Thanks to Mathijs de Bruin. -* The :ref:`clutter` option can now be a whitespace-separated list in addition +- The :ref:`clutter` option can now be a whitespace-separated list in addition to a YAML list. -* Values for the :ref:`replace` option can now be empty (i.e., null is +- Values for the :ref:`replace` option can now be empty (i.e., null is equivalent to the empty string). -* :doc:`/plugins/lastgenre`: Fix a conflict between canonicalization and +- :doc:`/plugins/lastgenre`: Fix a conflict between canonicalization and multiple genres. -* When a match has a year but not a month or day, the autotagger now "zeros - out" the month and day fields after applying the year. -* For plugin developers: added an ``optparse`` callback utility function for +- When a match has a year but not a month or day, the autotagger now "zeros out" + the month and day fields after applying the year. +- For plugin developers: added an ``optparse`` callback utility function for performing actions based on arguments. Thanks to Pedro Silva. -* :doc:`/plugins/scrub`: Fix scrubbing of MPEG-4 files. Thanks to Yevgeny +- :doc:`/plugins/scrub`: Fix scrubbing of MPEG-4 files. Thanks to Yevgeny Bezman. -.. _Acoustic Attributes: https://web.archive.org/web/20160701063109/http://developer.echonest.com/acoustic-attributes.html -.. _MPD: https://www.musicpd.org/ +.. _acoustic attributes: https://web.archive.org/web/20160701063109/http://developer.echonest.com/acoustic-attributes.html + +.. _mpd: https://www.musicpd.org/ 1.3.1 (October 12, 2013) ------------------------ This release boasts a host of new little features, many of them contributed by -beets' amazing and prolific community. It adds support for `Opus`_ files, +beets' amazing and prolific community. It adds support for Opus_ files, transcoding to any format, and two new plugins: one that guesses metadata for -"blank" files based on their filenames and one that moves featured artists -into the title field. +"blank" files based on their filenames and one that moves featured artists into +the title field. Here's the new stuff: -* Add `Opus`_ audio support. Thanks to Rowan Lewis. -* :doc:`/plugins/convert`: You can now transcode files to any audio format, +- Add Opus_ audio support. Thanks to Rowan Lewis. +- :doc:`/plugins/convert`: You can now transcode files to any audio format, rather than just MP3. Thanks again to Rowan Lewis. -* The new :doc:`/plugins/fromfilename` guesses tags from the filenames during +- The new :doc:`/plugins/fromfilename` guesses tags from the filenames during import when metadata tags themselves are missing. Thanks to Jan-Erik Dahlin. -* The :doc:`/plugins/ftintitle`, by `@Verrus`_, is now distributed with beets. - It helps you rewrite tags to move "featured" artists from the artist field - to the title field. -* The MusicBrainz data source now uses track artists over recording - artists. This leads to better metadata when tagging classical music. Thanks - to Henrique Ferreiro. -* :doc:`/plugins/lastgenre`: You can now get multiple genres per album or - track using the ``multiple`` config option. Thanks to rashley60 on GitHub. -* A new :ref:`id3v23` config option makes beets write MP3 files' tags using - the older ID3v2.3 metadata standard. Use this if you want your tags to be - visible to Windows and some older players. +- The :doc:`/plugins/ftintitle`, by `@Verrus`_, is now distributed with beets. + It helps you rewrite tags to move "featured" artists from the artist field to + the title field. +- The MusicBrainz data source now uses track artists over recording artists. + This leads to better metadata when tagging classical music. Thanks to Henrique + Ferreiro. +- :doc:`/plugins/lastgenre`: You can now get multiple genres per album or track + using the ``multiple`` config option. Thanks to rashley60 on GitHub. +- A new :ref:`id3v23` config option makes beets write MP3 files' tags using the + older ID3v2.3 metadata standard. Use this if you want your tags to be visible + to Windows and some older players. And some fixes: -* :doc:`/plugins/fetchart`: Better error message when the image file has an +- :doc:`/plugins/fetchart`: Better error message when the image file has an unrecognized type. -* :doc:`/plugins/mbcollection`: Detect, log, and skip invalid MusicBrainz IDs +- :doc:`/plugins/mbcollection`: Detect, log, and skip invalid MusicBrainz IDs (instead of failing with an API error). -* :doc:`/plugins/info`: Fail gracefully when used erroneously with a - directory. -* ``echonest_tempo``: Fix an issue where the plugin could use the - tempo from the wrong song when the API did not contain the requested song. -* Fix a crash when a file's metadata included a very large number (one wider +- :doc:`/plugins/info`: Fail gracefully when used erroneously with a directory. +- ``echonest_tempo``: Fix an issue where the plugin could use the tempo from the + wrong song when the API did not contain the requested song. +- Fix a crash when a file's metadata included a very large number (one wider than 64 bits). These huge numbers are now replaced with zeroes in the database. -* When a track on a MusicBrainz release has a different length from the +- When a track on a MusicBrainz release has a different length from the underlying recording's length, the track length is now used instead. -* With :ref:`per_disc_numbering` enabled, the ``tracktotal`` field is now set +- With :ref:`per_disc_numbering` enabled, the ``tracktotal`` field is now set correctly (i.e., to the number of tracks on the disc). -* :doc:`/plugins/scrub`: The ``scrub`` command now restores album art in +- :doc:`/plugins/scrub`: The ``scrub`` command now restores album art in addition to other (database-backed) tags. -* :doc:`/plugins/mpdupdate`: Domain sockets can now begin with a tilde (which - is correctly expanded to ``$HOME``) as well as a slash. Thanks to Johann - Klähn. -* :doc:`/plugins/lastgenre`: Fix a regression that could cause new genres - found during import not to be persisted. -* Fixed a crash when imported album art was also marked as "clutter" where the +- :doc:`/plugins/mpdupdate`: Domain sockets can now begin with a tilde (which is + correctly expanded to ``$HOME``) as well as a slash. Thanks to Johann Klähn. +- :doc:`/plugins/lastgenre`: Fix a regression that could cause new genres found + during import not to be persisted. +- Fixed a crash when imported album art was also marked as "clutter" where the art would be deleted before it could be moved into place. This led to a "image.jpg not found during copy" error. Now clutter is removed (and - directories pruned) much later in the process, after the - ``import_task_files`` hook. -* :doc:`/plugins/missing`: Fix an error when printing missing track names. + directories pruned) much later in the process, after the ``import_task_files`` + hook. +- :doc:`/plugins/missing`: Fix an error when printing missing track names. Thanks to Pedro Silva. -* Fix an occasional KeyError in the :ref:`update-cmd` command introduced in +- Fix an occasional KeyError in the :ref:`update-cmd` command introduced in 1.3.0. -* :doc:`/plugins/scrub`: Avoid preserving certain non-standard ID3 tags such - as NCON. +- :doc:`/plugins/scrub`: Avoid preserving certain non-standard ID3 tags such as + NCON. -.. _Opus: https://www.opus-codec.org/ -.. _@Verrus: https://github.com/Verrus +.. _@verrus: https://github.com/Verrus + +.. _opus: https://www.opus-codec.org/ 1.3.0 (September 11, 2013) -------------------------- @@ -3759,15 +3498,21 @@ artist, track, etc.). Instead, you can use any field name you can think of and treat it just like the built-in fields. For example, you can use the :ref:`modify-cmd` command to set a new field on a -track:: +track: + +:: $ beet modify mood=sexy artist:miguel -and then query your music based on that field:: +and then query your music based on that field: + +:: $ beet ls mood:sunny -or use templates to see the value of the field:: +or use templates to see the value of the field: + +:: $ beet ls -f '$title: $mood' @@ -3778,8 +3523,8 @@ infrastructure. One side effect of this change: queries that include unknown fields will now match *nothing* instead of *everything*. So if you type ``beet ls -fieldThatDoesNotExist:foo``, beets will now return no results, whereas -previous versions would spit out a warning and then list your entire library. +fieldThatDoesNotExist:foo``, beets will now return no results, whereas previous +versions would spit out a warning and then list your entire library. There's more detail than you could ever need `on the beets blog`_. @@ -3788,27 +3533,26 @@ There's more detail than you could ever need `on the beets blog`_. 1.2.2 (August 27, 2013) ----------------------- -This is a bugfix release. We're in the midst of preparing for a large change -in beets 1.3, so 1.2.2 resolves some issues that came up over the last few -weeks. Stay tuned! +This is a bugfix release. We're in the midst of preparing for a large change in +beets 1.3, so 1.2.2 resolves some issues that came up over the last few weeks. +Stay tuned! The improvements in this release are: -* A new plugin event, ``item_moved``, is sent when files are moved on disk. +- A new plugin event, ``item_moved``, is sent when files are moved on disk. Thanks to dsedivec. -* :doc:`/plugins/lyrics`: More improvements to the Google backend by Fabrice +- :doc:`/plugins/lyrics`: More improvements to the Google backend by Fabrice Laporte. -* :doc:`/plugins/bpd`: Fix for a crash when searching, thanks to Simon Chopin. -* Regular expression queries (and other query types) over paths now work. +- :doc:`/plugins/bpd`: Fix for a crash when searching, thanks to Simon Chopin. +- Regular expression queries (and other query types) over paths now work. (Previously, special query types were ignored for the ``path`` field.) -* :doc:`/plugins/fetchart`: Look for images in the Cover Art Archive for - the release group in addition to the specific release. Thanks to Filipe - Fortes. -* Fix a race in the importer that could cause files to be deleted before they +- :doc:`/plugins/fetchart`: Look for images in the Cover Art Archive for the + release group in addition to the specific release. Thanks to Filipe Fortes. +- Fix a race in the importer that could cause files to be deleted before they were imported. This happened when importing one album, importing a duplicate - album, and then asking for the first album to be replaced with the second. - The situation could only arise when importing music from the library - directory and when the two albums are imported close in time. + album, and then asking for the first album to be replaced with the second. The + situation could only arise when importing music from the library directory and + when the two albums are imported close in time. 1.2.1 (June 22, 2013) --------------------- @@ -3816,285 +3560,278 @@ The improvements in this release are: This release introduces a major internal change in the way that similarity scores are handled. It means that the importer interface can now show you exactly why a match is assigned its score and that the autotagger gained a few -new options that let you customize how matches are prioritized and -recommended. +new options that let you customize how matches are prioritized and recommended. -The refactoring work is due to the continued efforts of Tai Lee. The -changes you'll notice while using the autotagger are: +The refactoring work is due to the continued efforts of Tai Lee. The changes +you'll notice while using the autotagger are: -* The top 3 distance penalties are now displayed on the release listing, - and all album and track penalties are now displayed on the track changes - list. This should make it clear exactly which metadata is contributing to a - low similarity score. -* When displaying differences, the colorization has been made more consistent - and helpful: red for an actual difference, yellow to indicate that a - distance penalty is being applied, and light gray for no penalty (e.g., case - changes) or disambiguation data. +- The top 3 distance penalties are now displayed on the release listing, and all + album and track penalties are now displayed on the track changes list. This + should make it clear exactly which metadata is contributing to a low + similarity score. +- When displaying differences, the colorization has been made more consistent + and helpful: red for an actual difference, yellow to indicate that a distance + penalty is being applied, and light gray for no penalty (e.g., case changes) + or disambiguation data. There are also three new (or overhauled) configuration options that let you customize the way that matches are selected: -* The :ref:`ignored` setting lets you instruct the importer not to show you +- The :ref:`ignored` setting lets you instruct the importer not to show you matches that have a certain penalty applied. -* The :ref:`preferred` collection of settings specifies a sorted list of +- The :ref:`preferred` collection of settings specifies a sorted list of preferred countries and media types, or prioritizes releases closest to the original year for an album. -* The :ref:`max_rec` settings can now be used for any distance penalty +- The :ref:`max_rec` settings can now be used for any distance penalty component. The recommendation will be downgraded if a non-zero penalty is being applied to the specified field. And some little enhancements and bug fixes: -* Multi-disc directory names can now contain "disk" (in addition to "disc"). +- Multi-disc directory names can now contain "disk" (in addition to "disc"). Thanks to John Hawthorn. -* :doc:`/plugins/web`: Item and album counts are now exposed through the API - for use with the Tomahawk resolver. Thanks to Uwe L. Korn. -* Python 2.6 compatibility for ``beatport``, - :doc:`/plugins/missing`, and :doc:`/plugins/duplicates`. Thanks to Wesley - Bitter and Pedro Silva. -* Don't move the config file during a null migration. Thanks to Theofilos +- :doc:`/plugins/web`: Item and album counts are now exposed through the API for + use with the Tomahawk resolver. Thanks to Uwe L. Korn. +- Python 2.6 compatibility for ``beatport``, :doc:`/plugins/missing`, and + :doc:`/plugins/duplicates`. Thanks to Wesley Bitter and Pedro Silva. +- Don't move the config file during a null migration. Thanks to Theofilos Intzoglou. -* Fix an occasional crash in the ``beatport`` when a length - field was missing from the API response. Thanks to Timothy Appnel. -* :doc:`/plugins/scrub`: Handle and log I/O errors. -* :doc:`/plugins/lyrics`: The Google backend should now turn up more results. +- Fix an occasional crash in the ``beatport`` when a length field was missing + from the API response. Thanks to Timothy Appnel. +- :doc:`/plugins/scrub`: Handle and log I/O errors. +- :doc:`/plugins/lyrics`: The Google backend should now turn up more results. Thanks to Fabrice Laporte. -* :doc:`/plugins/random`: Fix compatibility with Python 2.6. Thanks to - Matthias Drochner. +- :doc:`/plugins/random`: Fix compatibility with Python 2.6. Thanks to Matthias + Drochner. 1.2.0 (June 5, 2013) -------------------- There's a *lot* of new stuff in this release: new data sources for the -autotagger, new plugins to look for problems in your library, tracking the -date that you acquired new music, an awesome new syntax for doing queries over -numeric fields, support for ALAC files, and major enhancements to the -importer's UI and distance calculations. A special thanks goes out to all the -contributors who helped make this release awesome. +autotagger, new plugins to look for problems in your library, tracking the date +that you acquired new music, an awesome new syntax for doing queries over +numeric fields, support for ALAC files, and major enhancements to the importer's +UI and distance calculations. A special thanks goes out to all the contributors +who helped make this release awesome. For the first time, beets can now tag your music using additional **data sources** to augment the matches from MusicBrainz. When you enable either of these plugins, the importer will start showing you new kinds of matches: -* New :doc:`/plugins/discogs`: Get matches from the `Discogs`_ database. - Thanks to Artem Ponomarenko and Tai Lee. -* New ``beatport`` plugin: Get matches from the `Beatport`_ database. - Thanks to Johannes Baiter. +- New :doc:`/plugins/discogs`: Get matches from the Discogs_ database. Thanks to + Artem Ponomarenko and Tai Lee. +- New ``beatport`` plugin: Get matches from the Beatport_ database. Thanks to + Johannes Baiter. We also have two other new plugins that can scan your library to check for common problems, both by Pedro Silva: -* New :doc:`/plugins/duplicates`: Find tracks or albums in your - library that are **duplicated**. -* New :doc:`/plugins/missing`: Find albums in your library that are **missing +- New :doc:`/plugins/duplicates`: Find tracks or albums in your library that are + **duplicated**. +- New :doc:`/plugins/missing`: Find albums in your library that are **missing tracks**. There are also three more big features added to beets core: -* Your library now keeps track of **when music was added** to it. The new +- Your library now keeps track of **when music was added** to it. The new ``added`` field is a timestamp reflecting when each item and album was imported and the new ``%time{}`` template function lets you format this timestamp for humans. Thanks to Lucas Duailibe. -* When using queries to match on quantitative fields, you can now use - **numeric ranges**. For example, you can get a list of albums from the '90s - by typing ``beet ls year:1990..1999`` or find high-bitrate music with +- When using queries to match on quantitative fields, you can now use **numeric + ranges**. For example, you can get a list of albums from the '90s by typing + ``beet ls year:1990..1999`` or find high-bitrate music with ``bitrate:128000..``. See :ref:`numericquery`. Thanks to Michael Schuerig. -* **ALAC files** are now marked as ALAC instead of being conflated with AAC +- **ALAC files** are now marked as ALAC instead of being conflated with AAC audio. Thanks to Simon Luijk. In addition, the importer saw various UI enhancements, thanks to Tai Lee: -* More consistent format and colorization of album and track metadata. -* Display data source URL for matches from the new data source plugins. This +- More consistent format and colorization of album and track metadata. +- Display data source URL for matches from the new data source plugins. This should make it easier to migrate data from Discogs or Beatport into MusicBrainz. -* Display album disambiguation and disc titles in the track listing, when +- Display album disambiguation and disc titles in the track listing, when available. -* Track changes are highlighted in yellow when they indicate a change in - format to or from the style of :ref:`per_disc_numbering`. (As before, no - penalty is applied because the track number is still "correct", just in a - different format.) -* Sort missing and unmatched tracks by index and title and group them - together for better readability. -* Indicate MusicBrainz ID mismatches. +- Track changes are highlighted in yellow when they indicate a change in format + to or from the style of :ref:`per_disc_numbering`. (As before, no penalty is + applied because the track number is still "correct", just in a different + format.) +- Sort missing and unmatched tracks by index and title and group them together + for better readability. +- Indicate MusicBrainz ID mismatches. The calculation of the similarity score for autotagger matches was also -improved, again thanks to Tai Lee. These changes, in general, help deal with -the new metadata sources and help disambiguate between similar releases in the -same MusicBrainz release group: +improved, again thanks to Tai Lee. These changes, in general, help deal with the +new metadata sources and help disambiguate between similar releases in the same +MusicBrainz release group: -* Strongly prefer releases with a matching MusicBrainz album ID. This helps +- Strongly prefer releases with a matching MusicBrainz album ID. This helps beets re-identify the same release when re-importing existing files. -* Prefer releases that are closest to the tagged ``year``. Tolerate files - tagged with release or original year. -* The new ``preferred_media`` config option lets you prefer a certain media - type when the ``media`` field is unset on an album. -* Apply minor penalties across a range of fields to differentiate between - nearly identical releases: ``disctotal``, ``label``, ``catalognum``, - ``country`` and ``albumdisambig``. +- Prefer releases that are closest to the tagged ``year``. Tolerate files tagged + with release or original year. +- The new ``preferred_media`` config option lets you prefer a certain media type + when the ``media`` field is unset on an album. +- Apply minor penalties across a range of fields to differentiate between nearly + identical releases: ``disctotal``, ``label``, ``catalognum``, ``country`` and + ``albumdisambig``. As usual, there were also lots of other great littler enhancements: -* :doc:`/plugins/random`: A new ``-e`` option gives an equal chance to each - artist in your collection to avoid biasing random samples to prolific - artists. Thanks to Georges Dubus. -* The :ref:`modify-cmd` now correctly converts types when modifying non-string +- :doc:`/plugins/random`: A new ``-e`` option gives an equal chance to each + artist in your collection to avoid biasing random samples to prolific artists. + Thanks to Georges Dubus. +- The :ref:`modify-cmd` now correctly converts types when modifying non-string fields. You can now safely modify the "comp" flag and the "year" field, for example. Thanks to Lucas Duailibe. -* :doc:`/plugins/convert`: You can now configure the path formats for - converted files separately from your main library. Thanks again to Lucas - Duailibe. -* The importer output now shows the number of audio files in each album. - Thanks to jayme on GitHub. -* Plugins can now provide fields for both Album and Item templates, thanks - to Pedro Silva. Accordingly, the :doc:`/plugins/inline` can also now define - album fields. For consistency, the ``pathfields`` configuration section has - been renamed ``item_fields`` (although the old name will still work for +- :doc:`/plugins/convert`: You can now configure the path formats for converted + files separately from your main library. Thanks again to Lucas Duailibe. +- The importer output now shows the number of audio files in each album. Thanks + to jayme on GitHub. +- Plugins can now provide fields for both Album and Item templates, thanks to + Pedro Silva. Accordingly, the :doc:`/plugins/inline` can also now define album + fields. For consistency, the ``pathfields`` configuration section has been + renamed ``item_fields`` (although the old name will still work for compatibility). -* Plugins can also provide metadata matches for ID searches. For example, the +- Plugins can also provide metadata matches for ID searches. For example, the new Discogs plugin lets you search for an album by its Discogs ID from the - same prompt that previously just accepted MusicBrainz IDs. Thanks to - Johannes Baiter. -* The :ref:`fields-cmd` command shows template fields provided by plugins. + same prompt that previously just accepted MusicBrainz IDs. Thanks to Johannes + Baiter. +- The :ref:`fields-cmd` command shows template fields provided by plugins. Thanks again to Pedro Silva. -* :doc:`/plugins/mpdupdate`: You can now communicate with MPD over a Unix - domain socket. Thanks to John Hawthorn. +- :doc:`/plugins/mpdupdate`: You can now communicate with MPD over a Unix domain + socket. Thanks to John Hawthorn. And a batch of fixes: -* Album art filenames now respect the :ref:`replace` configuration. -* Friendly error messages are now printed when trying to read or write files +- Album art filenames now respect the :ref:`replace` configuration. +- Friendly error messages are now printed when trying to read or write files that go missing. -* The :ref:`modify-cmd` command can now change albums' album art paths (i.e., +- The :ref:`modify-cmd` command can now change albums' album art paths (i.e., ``beet modify artpath=...`` works). Thanks to Lucas Duailibe. -* :doc:`/plugins/zero`: Fix a crash when nulling out a field that contains - None. -* Templates can now refer to non-tag item fields (e.g., ``$id`` and +- :doc:`/plugins/zero`: Fix a crash when nulling out a field that contains None. +- Templates can now refer to non-tag item fields (e.g., ``$id`` and ``$album_id``). -* :doc:`/plugins/lyrics`: Lyrics searches should now turn up more results due - to some fixes in dealing with special characters. +- :doc:`/plugins/lyrics`: Lyrics searches should now turn up more results due to + some fixes in dealing with special characters. -.. _Discogs: https://discogs.com/ -.. _Beatport: https://www.beatport.com/ +.. _beatport: https://www.beatport.com/ + +.. _discogs: https://discogs.com/ 1.1.0 (April 29, 2013) ---------------------- This final release of 1.1 brings a little polish to the betas that introduced -the new configuration system. The album art and lyrics plugins also got a -little love. +the new configuration system. The album art and lyrics plugins also got a little +love. If you're upgrading from 1.0.0 or earlier, this release (like the 1.1 betas) will automatically migrate your configuration to the new system. -* :doc:`/plugins/embedart`: The ``embedart`` command now embeds each album's - associated art by default. The ``--file`` option invokes the old behavior, - in which a specific image file is used. -* :doc:`/plugins/lyrics`: A new (optional) Google Custom Search backend was - added for finding lyrics on a wide array of sites. Thanks to Fabrice - Laporte. -* When automatically detecting the filesystem's maximum filename length, never +- :doc:`/plugins/embedart`: The ``embedart`` command now embeds each album's + associated art by default. The ``--file`` option invokes the old behavior, in + which a specific image file is used. +- :doc:`/plugins/lyrics`: A new (optional) Google Custom Search backend was + added for finding lyrics on a wide array of sites. Thanks to Fabrice Laporte. +- When automatically detecting the filesystem's maximum filename length, never guess more than 200 characters. This prevents errors on systems where the - maximum length was misreported. You can, of course, override this default - with the :ref:`max_filename_length` option. -* :doc:`/plugins/fetchart`: Two new configuration options were added: + maximum length was misreported. You can, of course, override this default with + the :ref:`max_filename_length` option. +- :doc:`/plugins/fetchart`: Two new configuration options were added: ``cover_names``, the list of keywords used to identify preferred images, and ``cautious``, which lets you avoid falling back to images that don't contain those keywords. Thanks to Fabrice Laporte. -* Avoid some error cases in the ``update`` command and the ``embedart`` and +- Avoid some error cases in the ``update`` command and the ``embedart`` and ``mbsync`` plugins. Invalid or missing files now cause error logs instead of crashing beets. Thanks to Lucas Duailibe. -* :doc:`/plugins/lyrics`: Searches now strip "featuring" artists when - searching for lyrics, which should increase the hit rate for these tracks. - Thanks to Fabrice Laporte. -* When listing the items in an album, the items are now always in track-number +- :doc:`/plugins/lyrics`: Searches now strip "featuring" artists when searching + for lyrics, which should increase the hit rate for these tracks. Thanks to + Fabrice Laporte. +- When listing the items in an album, the items are now always in track-number order. This should lead to more predictable listings from the :doc:`/plugins/importfeeds`. -* :doc:`/plugins/smartplaylist`: Queries are now split using shell-like syntax +- :doc:`/plugins/smartplaylist`: Queries are now split using shell-like syntax instead of just whitespace, so you can now construct terms that contain spaces. -* :doc:`/plugins/lastgenre`: The ``force`` config option now defaults to true +- :doc:`/plugins/lastgenre`: The ``force`` config option now defaults to true and controls the behavior of the import hook. (Previously, new genres were always forced during import.) -* :doc:`/plugins/web`: Fix an error when specifying the hostname on the - command line. -* :doc:`/plugins/web`: The underlying API was expanded slightly to support - `Tomahawk`_ collections. And file transfers now have a "Content-Length" - header. Thanks to Uwe L. Korn. -* :doc:`/plugins/lastgenre`: Fix an error when using genre canonicalization. +- :doc:`/plugins/web`: Fix an error when specifying the hostname on the command + line. +- :doc:`/plugins/web`: The underlying API was expanded slightly to support + Tomahawk_ collections. And file transfers now have a "Content-Length" header. + Thanks to Uwe L. Korn. +- :doc:`/plugins/lastgenre`: Fix an error when using genre canonicalization. -.. _Tomahawk: https://github.com/tomahawk-player/tomahawk +.. _tomahawk: https://github.com/tomahawk-player/tomahawk 1.1b3 (March 16, 2013) ---------------------- This third beta of beets 1.1 brings a hodgepodge of little new features (and -internal overhauls that will make improvements easier in the future). There -are new options for getting metadata in a particular language and seeing more -detail during the import process. There's also a new plugin for synchronizing -your metadata with MusicBrainz. Under the hood, plugins can now extend the -query syntax. +internal overhauls that will make improvements easier in the future). There are +new options for getting metadata in a particular language and seeing more detail +during the import process. There's also a new plugin for synchronizing your +metadata with MusicBrainz. Under the hood, plugins can now extend the query +syntax. New configuration options: -* :ref:`languages` controls the preferred languages when selecting an alias - from MusicBrainz. This feature requires `python-musicbrainzngs`_ 0.3 or - later. Thanks to Sam Doshi. -* :ref:`detail` enables a mode where all tracks are listed in the importer UI, +- :ref:`languages` controls the preferred languages when selecting an alias from + MusicBrainz. This feature requires python-musicbrainzngs_ 0.3 or later. Thanks + to Sam Doshi. +- :ref:`detail` enables a mode where all tracks are listed in the importer UI, as opposed to only changed tracks. -* The ``--flat`` option to the ``beet import`` command treats an entire +- The ``--flat`` option to the ``beet import`` command treats an entire directory tree of music files as a single album. This can help in situations where a multi-disc album is split across multiple directories. -* :doc:`/plugins/importfeeds`: An option was added to use absolute, rather - than relative, paths. Thanks to Lucas Duailibe. +- :doc:`/plugins/importfeeds`: An option was added to use absolute, rather than + relative, paths. Thanks to Lucas Duailibe. Other stuff: -* A new :doc:`/plugins/mbsync` provides a command that looks up each item and - track in MusicBrainz and updates your library to reflect it. This can help - you easily correct errors that have been fixed in the MB database. Thanks to - Jakob Schnitzer. -* :doc:`/plugins/fuzzy`: The ``fuzzy`` command was removed and replaced with a +- A new :doc:`/plugins/mbsync` provides a command that looks up each item and + track in MusicBrainz and updates your library to reflect it. This can help you + easily correct errors that have been fixed in the MB database. Thanks to Jakob + Schnitzer. +- :doc:`/plugins/fuzzy`: The ``fuzzy`` command was removed and replaced with a new query type. To perform fuzzy searches, use the ``~`` prefix with :ref:`list-cmd` or other commands. Thanks to Philippe Mongeau. -* As part of the above, plugins can now extend the query syntax and new kinds - of matching capabilities to beets. See :ref:`extend-query`. Thanks again to +- As part of the above, plugins can now extend the query syntax and new kinds of + matching capabilities to beets. See :ref:`extend-query`. Thanks again to Philippe Mongeau. -* :doc:`/plugins/convert`: A new ``--keep-new`` option lets you store - transcoded files in your library while backing up the originals (instead of - vice-versa). Thanks to Lucas Duailibe. -* :doc:`/plugins/convert`: Also, a new ``auto`` config option will transcode +- :doc:`/plugins/convert`: A new ``--keep-new`` option lets you store transcoded + files in your library while backing up the originals (instead of vice-versa). + Thanks to Lucas Duailibe. +- :doc:`/plugins/convert`: Also, a new ``auto`` config option will transcode audio files automatically during import. Thanks again to Lucas Duailibe. -* :doc:`/plugins/chroma`: A new ``fingerprint`` command lets you generate and +- :doc:`/plugins/chroma`: A new ``fingerprint`` command lets you generate and store fingerprints for items that don't yet have them. One more round of applause for Lucas Duailibe. -* ``echonest_tempo``: API errors now issue a warning instead of - exiting with an exception. We also avoid an error when track metadata - contains newlines. -* When the importer encounters an error (insufficient permissions, for - example) when walking a directory tree, it now logs an error instead of - crashing. -* In path formats, null database values now expand to the empty string instead +- ``echonest_tempo``: API errors now issue a warning instead of exiting with an + exception. We also avoid an error when track metadata contains newlines. +- When the importer encounters an error (insufficient permissions, for example) + when walking a directory tree, it now logs an error instead of crashing. +- In path formats, null database values now expand to the empty string instead of the string "None". -* Add "System Volume Information" (an internal directory found on some - Windows filesystems) to the default ignore list. -* Fix a crash when ReplayGain values were set to null. -* Fix a crash when iTunes Sound Check tags contained invalid data. -* Fix an error when the configuration file (``config.yaml``) is completely +- Add "System Volume Information" (an internal directory found on some Windows + filesystems) to the default ignore list. +- Fix a crash when ReplayGain values were set to null. +- Fix a crash when iTunes Sound Check tags contained invalid data. +- Fix an error when the configuration file (``config.yaml``) is completely empty. -* Fix an error introduced in 1.1b1 when importing using timid mode. Thanks to +- Fix an error introduced in 1.1b1 when importing using timid mode. Thanks to Sam Doshi. -* :doc:`/plugins/convert`: Fix a bug when creating files with Unicode - pathnames. -* Fix a spurious warning from the Unidecode module when matching albums that - are missing all metadata. -* Fix Unicode errors when a directory or file doesn't exist when invoking the +- :doc:`/plugins/convert`: Fix a bug when creating files with Unicode pathnames. +- Fix a spurious warning from the Unidecode module when matching albums that are + missing all metadata. +- Fix Unicode errors when a directory or file doesn't exist when invoking the import command. Thanks to Lucas Duailibe. -* :doc:`/plugins/mbcollection`: Show friendly, human-readable errors when +- :doc:`/plugins/mbcollection`: Show friendly, human-readable errors when MusicBrainz exceptions occur. -* ``echonest_tempo``: Catch socket errors that are not handled by - the Echo Nest library. -* :doc:`/plugins/chroma`: Catch Acoustid Web service errors when submitting +- ``echonest_tempo``: Catch socket errors that are not handled by the Echo Nest + library. +- :doc:`/plugins/chroma`: Catch Acoustid Web service errors when submitting fingerprints. 1.1b2 (February 16, 2013) @@ -4102,8 +3839,8 @@ Other stuff: The second beta of beets 1.1 uses the fancy new configuration infrastructure to add many, many new config options. The import process is more flexible; -filenames can be customized in more detail; and more. This release also -supports Windows Media (ASF) files and iTunes Sound Check volume normalization. +filenames can be customized in more detail; and more. This release also supports +Windows Media (ASF) files and iTunes Sound Check volume normalization. This version introduces one **change to the default behavior** that you should be aware of. Previously, when importing new albums matched in MusicBrainz, the @@ -4115,123 +3852,123 @@ behavior, just set :ref:`original_date` to true in your config file. New configuration options: -* :ref:`default_action` lets you determine the default (just-hit-return) option +- :ref:`default_action` lets you determine the default (just-hit-return) option is when considering a candidate. -* :ref:`none_rec_action` lets you skip the prompt, and automatically choose an +- :ref:`none_rec_action` lets you skip the prompt, and automatically choose an action, when there is no good candidate. Thanks to Tai Lee. -* :ref:`max_rec` lets you define a maximum recommendation for albums with +- :ref:`max_rec` lets you define a maximum recommendation for albums with missing/extra tracks or differing track lengths/numbers. Thanks again to Tai Lee. -* :ref:`original_date` determines whether, when importing new albums, the +- :ref:`original_date` determines whether, when importing new albums, the ``year``, ``month``, and ``day`` fields should reflect the specific (e.g., reissue) release date or the original release date. Note that the original release date is always available as ``original_year``, etc. -* :ref:`clutter` controls which files should be ignored when cleaning up empty +- :ref:`clutter` controls which files should be ignored when cleaning up empty directories. Thanks to Steinþór Pálsson. -* :doc:`/plugins/lastgenre`: A new configuration option lets you choose to +- :doc:`/plugins/lastgenre`: A new configuration option lets you choose to retrieve artist-level tags as genres instead of album- or track-level tags. Thanks to Peter Fern and Peter Schnebel. -* :ref:`max_filename_length` controls truncation of long filenames. Also, beets +- :ref:`max_filename_length` controls truncation of long filenames. Also, beets now tries to determine the filesystem's maximum length automatically if you leave this option unset. -* :doc:`/plugins/fetchart`: The ``remote_priority`` option searches remote - (Web) art sources even when local art is present. -* You can now customize the character substituted for path separators (e.g., /) +- :doc:`/plugins/fetchart`: The ``remote_priority`` option searches remote (Web) + art sources even when local art is present. +- You can now customize the character substituted for path separators (e.g., /) in filenames via ``path_sep_replace``. The default is an underscore. Use this setting with caution. Other new stuff: -* Support for Windows Media/ASF audio files. Thanks to Dave Hayes. -* New :doc:`/plugins/smartplaylist`: generate and maintain m3u playlist files +- Support for Windows Media/ASF audio files. Thanks to Dave Hayes. +- New :doc:`/plugins/smartplaylist`: generate and maintain m3u playlist files based on beets queries. Thanks to Dang Mai Hai. -* ReplayGain tags on MPEG-4/AAC files are now supported. And, even more +- ReplayGain tags on MPEG-4/AAC files are now supported. And, even more astonishingly, ReplayGain values in MP3 and AAC files are now compatible with `iTunes Sound Check`_. Thanks to Dave Hayes. -* Track titles in the importer UI's difference display are now either aligned +- Track titles in the importer UI's difference display are now either aligned vertically or broken across two lines for readability. Thanks to Tai Lee. -* Albums and items have new fields reflecting the *original* release date +- Albums and items have new fields reflecting the *original* release date (``original_year``, ``original_month``, and ``original_day``). Previously, when tagging from MusicBrainz, *only* the original date was stored; now, the old fields refer to the *specific* release date (e.g., when the album was reissued). -* Some changes to the way candidates are recommended for selection, thanks to +- Some changes to the way candidates are recommended for selection, thanks to Tai Lee: - * According to the new :ref:`max_rec` configuration option, partial album + - According to the new :ref:`max_rec` configuration option, partial album matches are downgraded to a "low" recommendation by default. - * When a match isn't great but is either better than all the others or the + - When a match isn't great but is either better than all the others or the only match, it is given a "low" (rather than "medium") recommendation. - * There is no prompt default (i.e., input is required) when matches are - bad: "low" or "none" recommendations or when choosing a candidate - other than the first. + - There is no prompt default (i.e., input is required) when matches are bad: + "low" or "none" recommendations or when choosing a candidate other than the + first. -* The importer's heuristic for coalescing the directories in a multi-disc album - has been improved. It can now detect when two directories alongside each - other share a similar prefix but a different number (e.g., "Album Disc 1" and - "Album Disc 2") even when they are not alone in a common parent directory. - Thanks once again to Tai Lee. -* Album listings in the importer UI now show the release medium (CD, Vinyl, +- The importer's heuristic for coalescing the directories in a multi-disc album + has been improved. It can now detect when two directories alongside each other + share a similar prefix but a different number (e.g., "Album Disc 1" and "Album + Disc 2") even when they are not alone in a common parent directory. Thanks + once again to Tai Lee. +- Album listings in the importer UI now show the release medium (CD, Vinyl, 3xCD, etc.) as well as the disambiguation string. Thanks to Peter Schnebel. -* :doc:`/plugins/lastgenre`: The plugin can now get different genres for +- :doc:`/plugins/lastgenre`: The plugin can now get different genres for individual tracks on an album. Thanks to Peter Schnebel. -* When getting data from MusicBrainz, the album disambiguation string +- When getting data from MusicBrainz, the album disambiguation string (``albumdisambig``) now reflects both the release and the release group. -* :doc:`/plugins/mpdupdate`: Sends an update message whenever *anything* in the +- :doc:`/plugins/mpdupdate`: Sends an update message whenever *anything* in the database changes---not just when importing. Thanks to Dang Mai Hai. -* When the importer UI shows a difference in track numbers or durations, they +- When the importer UI shows a difference in track numbers or durations, they are now colorized based on the *suffixes* that differ. For example, when showing the difference between 2:01 and 2:09, only the last digit will be highlighted. -* The importer UI no longer shows a change when the track length difference is +- The importer UI no longer shows a change when the track length difference is less than 10 seconds. (This threshold was previously 2 seconds.) -* Two new plugin events were added: *database_change* and *cli_exit*. Thanks +- Two new plugin events were added: *database_change* and *cli_exit*. Thanks again to Dang Mai Hai. -* Plugins are now loaded in the order they appear in the config file. Thanks to +- Plugins are now loaded in the order they appear in the config file. Thanks to Dang Mai Hai. -* :doc:`/plugins/bpd`: Browse by album artist and album artist sort name. - Thanks to Steinþór Pálsson. -* ``echonest_tempo``: Don't attempt a lookup when the artist or - track title is missing. -* Fix an error when migrating the ``.beetsstate`` file on Windows. -* A nicer error message is now given when the configuration file contains tabs. +- :doc:`/plugins/bpd`: Browse by album artist and album artist sort name. Thanks + to Steinþór Pálsson. +- ``echonest_tempo``: Don't attempt a lookup when the artist or track title is + missing. +- Fix an error when migrating the ``.beetsstate`` file on Windows. +- A nicer error message is now given when the configuration file contains tabs. (YAML doesn't like tabs.) -* Fix the ``-l`` (log path) command-line option for the ``import`` command. +- Fix the ``-l`` (log path) command-line option for the ``import`` command. -.. _iTunes Sound Check: https://support.apple.com/kb/HT2425 +.. _itunes sound check: https://support.apple.com/kb/HT2425 1.1b1 (January 29, 2013) ------------------------ This release entirely revamps beets' configuration system. The configuration -file is now a `YAML`_ document and is located, along with other support files, -in a common directory (e.g., ``~/.config/beets`` on Unix-like systems). +file is now a YAML_ document and is located, along with other support files, in +a common directory (e.g., ``~/.config/beets`` on Unix-like systems). -.. _YAML: https://en.wikipedia.org/wiki/YAML +.. _yaml: https://en.wikipedia.org/wiki/YAML -* Renamed plugins: The ``rdm`` plugin has been renamed to ``random`` and +- Renamed plugins: The ``rdm`` plugin has been renamed to ``random`` and ``fuzzy_search`` has been renamed to ``fuzzy``. -* Renamed config options: Many plugins have a flag dictating whether their +- Renamed config options: Many plugins have a flag dictating whether their action runs at import time. This option had many names (``autofetch``, ``autoembed``, etc.) but is now consistently called ``auto``. -* Reorganized import config options: The various ``import_*`` options are now +- Reorganized import config options: The various ``import_*`` options are now organized under an ``import:`` heading and their prefixes have been removed. -* New default file locations: The default filename of the library database is +- New default file locations: The default filename of the library database is now ``library.db`` in the same directory as the config file, as opposed to ``~/.beetsmusic.blb`` previously. Similarly, the runtime state file is now called ``state.pickle`` in the same directory instead of ``~/.beetsstate``. It also adds some new features: -* :doc:`/plugins/inline`: Inline definitions can now contain statements or +- :doc:`/plugins/inline`: Inline definitions can now contain statements or blocks in addition to just expressions. Thanks to Florent Thoumie. -* Add a configuration option, :ref:`terminal_encoding`, controlling the text +- Add a configuration option, :ref:`terminal_encoding`, controlling the text encoding used to print messages to standard output. -* The MusicBrainz hostname (and rate limiting) are now configurable. See +- The MusicBrainz hostname (and rate limiting) are now configurable. See :ref:`musicbrainz-config`. -* You can now configure the similarity thresholds used to determine when the +- You can now configure the similarity thresholds used to determine when the autotagger automatically accepts a metadata match. See :ref:`match-config`. -* :doc:`/plugins/importfeeds`: Added a new configuration option that controls +- :doc:`/plugins/importfeeds`: Added a new configuration option that controls the base for relative paths used in m3u files. Thanks to Philippe Mongeau. 1.0.0 (January 29, 2013) @@ -4242,12 +3979,12 @@ one-point-oh. Congratulations to everybody involved. This version of beets will remain stable and receive only bug fixes from here on out. New development is ongoing in the betas of version 1.1. -* :doc:`/plugins/scrub`: Fix an incompatibility with Python 2.6. -* :doc:`/plugins/lyrics`: Fix an issue that failed to find lyrics when metadata +- :doc:`/plugins/scrub`: Fix an incompatibility with Python 2.6. +- :doc:`/plugins/lyrics`: Fix an issue that failed to find lyrics when metadata contained "real" apostrophes. -* :doc:`/plugins/replaygain`: On Windows, emit a warning instead of - crashing when analyzing non-ASCII filenames. -* Silence a spurious warning from version 0.04.12 of the Unidecode module. +- :doc:`/plugins/replaygain`: On Windows, emit a warning instead of crashing + when analyzing non-ASCII filenames. +- Silence a spurious warning from version 0.04.12 of the Unidecode module. 1.0rc2 (December 31, 2012) -------------------------- @@ -4256,12 +3993,12 @@ This second release candidate follows quickly after rc1 and fixes a few small bugs found since that release. There were a couple of regressions and some bugs in a newly added plugin. -* ``echonest_tempo``: If the Echo Nest API limit is exceeded or a - communication error occurs, the plugin now waits and tries again instead of - crashing. Thanks to Zach Denton. -* :doc:`/plugins/fetchart`: Fix a regression that caused crashes when art was +- ``echonest_tempo``: If the Echo Nest API limit is exceeded or a communication + error occurs, the plugin now waits and tries again instead of crashing. Thanks + to Zach Denton. +- :doc:`/plugins/fetchart`: Fix a regression that caused crashes when art was not available from some sources. -* Fix a regression on Windows that caused all relative paths to be "not found". +- Fix a regression on Windows that caused all relative paths to be "not found". 1.0rc1 (December 17, 2012) -------------------------- @@ -4272,105 +4009,107 @@ goes to the growing and vibrant beets community. A million thanks to everybody who contributed to this release. There are new plugins for transcoding music, fuzzy searches, tempo collection, -and fiddling with metadata. The ReplayGain plugin has been rebuilt from -scratch. Album art images can now be resized automatically. Many other smaller +and fiddling with metadata. The ReplayGain plugin has been rebuilt from scratch. +Album art images can now be resized automatically. Many other smaller refinements make things "just work" as smoothly as possible. -With this release candidate, beets 1.0 is feature-complete. We'll be fixing -bugs on the road to 1.0 but no new features will be added. Concurrently, work -begins today on features for version 1.1. +With this release candidate, beets 1.0 is feature-complete. We'll be fixing bugs +on the road to 1.0 but no new features will be added. Concurrently, work begins +today on features for version 1.1. -* New plugin: :doc:`/plugins/convert` **transcodes** music and embeds album art +- New plugin: :doc:`/plugins/convert` **transcodes** music and embeds album art while copying to a separate directory. Thanks to Jakob Schnitzer and Andrew G. Dunn. -* New plugin: :doc:`/plugins/fuzzy` lets you find albums and tracks - using **fuzzy string matching** so you don't have to type (or even remember) - their exact names. Thanks to Philippe Mongeau. -* New plugin: ``echonest_tempo`` fetches **tempo** (BPM) information - from `The Echo Nest`_. Thanks to David Brenner. -* New plugin: :doc:`/plugins/the` adds a template function that helps format +- New plugin: :doc:`/plugins/fuzzy` lets you find albums and tracks using + **fuzzy string matching** so you don't have to type (or even remember) their + exact names. Thanks to Philippe Mongeau. +- New plugin: ``echonest_tempo`` fetches **tempo** (BPM) information from `The + Echo Nest`_. Thanks to David Brenner. +- New plugin: :doc:`/plugins/the` adds a template function that helps format text for nicely-sorted directory listings. Thanks to Blemjhoo Tezoulbr. -* New plugin: :doc:`/plugins/zero` **filters out undesirable fields** before +- New plugin: :doc:`/plugins/zero` **filters out undesirable fields** before they are written to your tags. Thanks again to Blemjhoo Tezoulbr. -* New plugin: :doc:`/plugins/ihate` automatically skips (or warns you about) +- New plugin: :doc:`/plugins/ihate` automatically skips (or warns you about) importing albums that match certain criteria. Thanks once again to Blemjhoo Tezoulbr. -* :doc:`/plugins/replaygain`: This plugin has been completely overhauled to use - the `mp3gain`_ or `aacgain`_ command-line tools instead of the failure-prone +- :doc:`/plugins/replaygain`: This plugin has been completely overhauled to use + the mp3gain_ or aacgain_ command-line tools instead of the failure-prone Gstreamer ReplayGain implementation. Thanks to Fabrice Laporte. -* :doc:`/plugins/fetchart` and :doc:`/plugins/embedart`: Both plugins can now +- :doc:`/plugins/fetchart` and :doc:`/plugins/embedart`: Both plugins can now **resize album art** to avoid excessively large images. Use the ``maxwidth`` config option with either plugin. Thanks to Fabrice Laporte. -* :doc:`/plugins/scrub`: Scrubbing now removes *all* types of tags from a file +- :doc:`/plugins/scrub`: Scrubbing now removes *all* types of tags from a file rather than just one. For example, if your FLAC file has both ordinary FLAC tags and ID3 tags, the ID3 tags are now also removed. -* :ref:`stats-cmd` command: New ``--exact`` switch to make the file size +- :ref:`stats-cmd` command: New ``--exact`` switch to make the file size calculation more accurate (thanks to Jakob Schnitzer). -* :ref:`list-cmd` command: Templates given with ``-f`` can now show items' and +- :ref:`list-cmd` command: Templates given with ``-f`` can now show items' and albums' paths (using ``$path``). -* The output of the :ref:`update-cmd`, :ref:`remove-cmd`, and :ref:`modify-cmd` - commands now respects the :ref:`list_format_album` and - :ref:`list_format_item` config options. Thanks to Mike Kazantsev. -* The :ref:`art-filename` option can now be a template rather than a simple +- The output of the :ref:`update-cmd`, :ref:`remove-cmd`, and :ref:`modify-cmd` + commands now respects the :ref:`list_format_album` and :ref:`list_format_item` + config options. Thanks to Mike Kazantsev. +- The :ref:`art-filename` option can now be a template rather than a simple string. Thanks to Jarrod Beardwood. -* Fix album queries for ``artpath`` and other non-item fields. -* Null values in the database can now be matched with the empty-string regular +- Fix album queries for ``artpath`` and other non-item fields. +- Null values in the database can now be matched with the empty-string regular expression, ``^$``. -* Queries now correctly match non-string values in path format predicates. -* When autotagging a various-artists album, the album artist field is now - used instead of the majority track artist. -* :doc:`/plugins/lastgenre`: Use the albums' existing genre tags if they pass +- Queries now correctly match non-string values in path format predicates. +- When autotagging a various-artists album, the album artist field is now used + instead of the majority track artist. +- :doc:`/plugins/lastgenre`: Use the albums' existing genre tags if they pass the whitelist (thanks to Fabrice Laporte). -* :doc:`/plugins/lastgenre`: Add a ``lastgenre`` command for fetching genres +- :doc:`/plugins/lastgenre`: Add a ``lastgenre`` command for fetching genres post facto (thanks to Jakob Schnitzer). -* :doc:`/plugins/fetchart`: Local image filenames are now used in alphabetical +- :doc:`/plugins/fetchart`: Local image filenames are now used in alphabetical order. -* :doc:`/plugins/fetchart`: Fix a bug where cover art filenames could lack - a ``.jpg`` extension. -* :doc:`/plugins/lyrics`: Fix an exception with non-ASCII lyrics. -* :doc:`/plugins/web`: The API now reports file sizes (for use with the +- :doc:`/plugins/fetchart`: Fix a bug where cover art filenames could lack a + ``.jpg`` extension. +- :doc:`/plugins/lyrics`: Fix an exception with non-ASCII lyrics. +- :doc:`/plugins/web`: The API now reports file sizes (for use with the `Tomahawk resolver`_). -* :doc:`/plugins/web`: Files now download with a reasonable filename rather - than just being called "file" (thanks to Zach Denton). -* :doc:`/plugins/importfeeds`: Fix error in symlink mode with non-ASCII +- :doc:`/plugins/web`: Files now download with a reasonable filename rather than + just being called "file" (thanks to Zach Denton). +- :doc:`/plugins/importfeeds`: Fix error in symlink mode with non-ASCII filenames. -* :doc:`/plugins/mbcollection`: Fix an error when submitting a large number of - releases (we now submit only 200 releases at a time instead of 350). Thanks - to Jonathan Towne. -* :doc:`/plugins/embedart`: Made the method for embedding art into FLAC files +- :doc:`/plugins/mbcollection`: Fix an error when submitting a large number of + releases (we now submit only 200 releases at a time instead of 350). Thanks to + Jonathan Towne. +- :doc:`/plugins/embedart`: Made the method for embedding art into FLAC files `standard <https://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE>`_-compliant. Thanks to Daniele Sluijters. -* Add the track mapping dictionary to the ``album_distance`` plugin function. -* When an exception is raised while reading a file, the path of the file in +- Add the track mapping dictionary to the ``album_distance`` plugin function. +- When an exception is raised while reading a file, the path of the file in question is now logged (thanks to Mike Kazantsev). -* Truncate long filenames based on their *bytes* rather than their Unicode +- Truncate long filenames based on their *bytes* rather than their Unicode *characters*, fixing situations where encoded names could be too long. -* Filename truncation now incorporates the length of the extension. -* Fix an assertion failure when the MusicBrainz main database and search server +- Filename truncation now incorporates the length of the extension. +- Fix an assertion failure when the MusicBrainz main database and search server disagree. -* Fix a bug that caused the :doc:`/plugins/lastgenre` and other plugins not to +- Fix a bug that caused the :doc:`/plugins/lastgenre` and other plugins not to modify files' tags even when they successfully change the database. -* Fix a VFS bug leading to a crash in the :doc:`/plugins/bpd` when files had +- Fix a VFS bug leading to a crash in the :doc:`/plugins/bpd` when files had non-ASCII extensions. -* Fix for changing date fields (like "year") with the :ref:`modify-cmd` - command. -* Fix a crash when input is read from a pipe without a specified encoding. -* Fix some problem with identifying files on Windows with Unicode directory +- Fix for changing date fields (like "year") with the :ref:`modify-cmd` command. +- Fix a crash when input is read from a pipe without a specified encoding. +- Fix some problem with identifying files on Windows with Unicode directory names in their path. -* Fix a crash when Unicode queries were used with ``import -L`` re-imports. -* Fix an error when fingerprinting files with Unicode filenames on Windows. -* Warn instead of crashing when importing a specific file in singleton mode. -* Add human-readable error messages when writing files' tags fails or when a +- Fix a crash when Unicode queries were used with ``import -L`` re-imports. +- Fix an error when fingerprinting files with Unicode filenames on Windows. +- Warn instead of crashing when importing a specific file in singleton mode. +- Add human-readable error messages when writing files' tags fails or when a directory can't be created. -* Changed plugin loading so that modules can be imported without - unintentionally loading the plugins they contain. +- Changed plugin loading so that modules can be imported without unintentionally + loading the plugins they contain. -.. _The Echo Nest: https://web.archive.org/web/20180329103558/http://the.echonest.com/ -.. _Tomahawk resolver: https://beets.io/blog/tomahawk-resolver.html -.. _mp3gain: http://mp3gain.sourceforge.net/download.php .. _aacgain: https://aacgain.altosdesign.com +.. _mp3gain: http://mp3gain.sourceforge.net/download.php + +.. _the echo nest: https://web.archive.org/web/20180329103558/http://the.echonest.com/ + +.. _tomahawk resolver: https://beets.io/blog/tomahawk-resolver.html + 1.0b15 (July 26, 2012) ---------------------- @@ -4387,96 +4126,96 @@ encapsulated in a plugin (the :doc:`/plugins/fetchart`). If you want to continue fetching cover art for your music, enable this plugin after upgrading to beets 1.0b15. -* The autotagger can now find matches for albums when you have **extra tracks** +- The autotagger can now find matches for albums when you have **extra tracks** on your filesystem that aren't present in the MusicBrainz catalog. Previously, if you tried to match album with 15 audio files but the MusicBrainz entry had only 14 tracks, beets would ignore this match. Now, beets will show you matches even when they are "too short" and indicate which tracks from your disk are unmatched. -* Tracks on multi-disc albums can now be **numbered per-disc** instead of +- Tracks on multi-disc albums can now be **numbered per-disc** instead of per-album via the :ref:`per_disc_numbering` config option. -* The default output format for the ``beet list`` command is now configurable +- The default output format for the ``beet list`` command is now configurable via the :ref:`list_format_item` and :ref:`list_format_album` config options. Thanks to Fabrice Laporte. -* Album **cover art fetching** is now encapsulated in the +- Album **cover art fetching** is now encapsulated in the :doc:`/plugins/fetchart`. Be sure to enable this plugin if you're using this functionality. As a result of this new organization, the new plugin has gained a few new features: - * "As-is" and non-autotagged imports can now have album art imported from - the local filesystem (although Web repositories are still not searched in - these cases). - * A new command, ``beet fetchart``, allows you to download album art + - "As-is" and non-autotagged imports can now have album art imported from the + local filesystem (although Web repositories are still not searched in these + cases). + - A new command, ``beet fetchart``, allows you to download album art post-import. If you only want to fetch art manually, not automatically during import, set the new plugin's ``autofetch`` option to ``no``. - * New album art sources have been added. + - New album art sources have been added. -* Errors when communicating with MusicBrainz now log an error message instead of +- Errors when communicating with MusicBrainz now log an error message instead of halting the importer. -* Similarly, filesystem manipulation errors now print helpful error messages +- Similarly, filesystem manipulation errors now print helpful error messages instead of a messy traceback. They still interrupt beets, but they should now be easier for users to understand. Tracebacks are still available in verbose mode. -* New metadata fields for `artist credits`_: ``artist_credit`` and +- New metadata fields for `artist credits`_: ``artist_credit`` and ``albumartist_credit`` can now contain release- and recording-specific variations of the artist's name. See :ref:`itemfields`. -* Revamped the way beets handles concurrent database access to avoid +- Revamped the way beets handles concurrent database access to avoid nondeterministic SQLite-related crashes when using the multithreaded importer. On systems where SQLite was compiled without ``usleep(3)`` support, multithreaded database access could cause an internal error (with the message "database is locked"). This release synchronizes access to the database to avoid internal SQLite contention, which should avoid this error. -* Plugins can now add parallel stages to the import pipeline. See +- Plugins can now add parallel stages to the import pipeline. See :ref:`writing-plugins`. -* Beets now prints out an error when you use an unrecognized field name in a +- Beets now prints out an error when you use an unrecognized field name in a query: for example, when running ``beet ls -a artist:foo`` (because ``artist`` is an item-level field). -* New plugin events: +- New plugin events: - * ``import_task_choice`` is called after an import task has an action + - ``import_task_choice`` is called after an import task has an action assigned. - * ``import_task_files`` is called after a task's file manipulation has + - ``import_task_files`` is called after a task's file manipulation has finished (copying or moving files, writing metadata tags). - * ``library_opened`` is called when beets starts up and opens the library + - ``library_opened`` is called when beets starts up and opens the library database. -* :doc:`/plugins/lastgenre`: Fixed a problem where path formats containing +- :doc:`/plugins/lastgenre`: Fixed a problem where path formats containing ``$genre`` would use the old genre instead of the newly discovered one. -* Fix a crash when moving files to a Samba share. -* :doc:`/plugins/mpdupdate`: Fix TypeError crash (thanks to Philippe Mongeau). -* When re-importing files with ``import_copy`` enabled, only files inside the +- Fix a crash when moving files to a Samba share. +- :doc:`/plugins/mpdupdate`: Fix TypeError crash (thanks to Philippe Mongeau). +- When re-importing files with ``import_copy`` enabled, only files inside the library directory are moved. Files outside the library directory are still copied. This solves a problem (introduced in 1.0b14) where beets could crash after adding files to the library but before finishing copying them; during the next import, the (external) files would be moved instead of copied. -* Artist sort names are now populated correctly for multi-artist tracks and +- Artist sort names are now populated correctly for multi-artist tracks and releases. (Previously, they only reflected the first artist.) -* When previewing changes during import, differences in track duration are now +- When previewing changes during import, differences in track duration are now shown as "2:50 vs. 3:10" rather than separated with ``->`` like track numbers. This should clarify that beets isn't doing anything to modify lengths. -* Fix a problem with query-based path format matching where a field-qualified +- Fix a problem with query-based path format matching where a field-qualified pattern, like ``albumtype_soundtrack``, would match everything. -* :doc:`/plugins/chroma`: Fix matching with ambiguous Acoustids. Some Acoustids +- :doc:`/plugins/chroma`: Fix matching with ambiguous Acoustids. Some Acoustids are identified with multiple recordings; beets now considers any associated recording a valid match. This should reduce some cases of errant track reordering when using chroma. -* Fix the ID3 tag name for the catalog number field. -* :doc:`/plugins/chroma`: Fix occasional crash at end of fingerprint submission +- Fix the ID3 tag name for the catalog number field. +- :doc:`/plugins/chroma`: Fix occasional crash at end of fingerprint submission and give more context to "failed fingerprint generation" errors. -* Interactive prompts are sent to stdout instead of stderr. -* :doc:`/plugins/embedart`: Fix crash when audio files are unreadable. -* :doc:`/plugins/bpd`: Fix crash when sockets disconnect (thanks to Matteo +- Interactive prompts are sent to stdout instead of stderr. +- :doc:`/plugins/embedart`: Fix crash when audio files are unreadable. +- :doc:`/plugins/bpd`: Fix crash when sockets disconnect (thanks to Matteo Mecucci). -* Fix an assertion failure while importing with moving enabled when the file was +- Fix an assertion failure while importing with moving enabled when the file was already at its destination. -* Fix Unicode values in the ``replace`` config option (thanks to Jakob Borg). -* Use a nicer error message when input is requested but stdin is closed. -* Fix errors on Windows for certain Unicode characters that can't be represented +- Fix Unicode values in the ``replace`` config option (thanks to Jakob Borg). +- Use a nicer error message when input is requested but stdin is closed. +- Fix errors on Windows for certain Unicode characters that can't be represented in the MBCS encoding. This required a change to the way that paths are represented in the database on Windows; if you find that beets' paths are out of sync with your filesystem with this release, delete and recreate your database with ``beet import -AWC /path/to/music``. -* Fix ``import`` with relative path arguments on Windows. +- Fix ``import`` with relative path arguments on Windows. .. _artist credits: https://wiki.musicbrainz.org/Artist_Credit @@ -4494,74 +4233,74 @@ and a plugin for interoperability with other music library systems. A million thanks to the (growing) beets community for making this a huge release. -* The importer now gives you **choices when duplicates are detected**. +- The importer now gives you **choices when duplicates are detected**. Previously, when beets found an existing album or item in your library matching the metadata on a newly-imported one, it would just skip the new music to avoid introducing duplicates into your library. Now, you have three choices: skip the new music (the previous behavior), keep both, or remove the old music. See the :ref:`guide-duplicates` section in the autotagging guide for details. -* Beets can now avoid storing identically-named albums in the same directory. +- Beets can now avoid storing identically-named albums in the same directory. The new ``%aunique{}`` template function, which is included in the default path formats, ensures that Crystal Castles' albums will be placed into different directories. See :ref:`aunique` for details. -* Beets queries can now use **regular expressions**. Use an additional ``:`` in +- Beets queries can now use **regular expressions**. Use an additional ``:`` in your query to enable regex matching. See :ref:`regex` for the full details. Thanks to Matteo Mecucci. -* Artist **sort names** are now fetched from MusicBrainz. There are two new data +- Artist **sort names** are now fetched from MusicBrainz. There are two new data fields, ``artist_sort`` and ``albumartist_sort``, that contain sortable artist names like "Beatles, The". These fields are also used to sort albums and items when using the ``list`` command. Thanks to Paul Provost. -* Many other **new metadata fields** were added, including ASIN, label catalog +- Many other **new metadata fields** were added, including ASIN, label catalog number, disc title, encoder, and MusicBrainz release group ID. For a full list of fields, see :ref:`itemfields`. -* :doc:`/plugins/chroma`: A new command, ``beet submit``, will **submit +- :doc:`/plugins/chroma`: A new command, ``beet submit``, will **submit fingerprints** to the Acoustid database. Submitting your library helps increase the coverage and accuracy of Acoustid fingerprinting. The Chromaprint fingerprint and Acoustid ID are also now stored for all fingerprinted tracks. - This version of beets *requires* at least version 0.6 of `pyacoustid`_ for + This version of beets *requires* at least version 0.6 of pyacoustid_ for fingerprinting to work. -* The importer can now **move files**. Previously, beets could only copy files +- The importer can now **move files**. Previously, beets could only copy files and delete the originals, which is inefficient if the source and destination are on the same filesystem. Use the ``import_move`` configuration option and see :doc:`/reference/config` for more details. Thanks to Domen Kožar. -* New :doc:`/plugins/random`: Randomly select albums and tracks from your library. - Thanks to Philippe Mongeau. -* The :doc:`/plugins/mbcollection` by Jeffrey Aylesworth was added to the core +- New :doc:`/plugins/random`: Randomly select albums and tracks from your + library. Thanks to Philippe Mongeau. +- The :doc:`/plugins/mbcollection` by Jeffrey Aylesworth was added to the core beets distribution. -* New :doc:`/plugins/importfeeds`: Catalog imported files in ``m3u`` playlist +- New :doc:`/plugins/importfeeds`: Catalog imported files in ``m3u`` playlist files or as symlinks for easy importing to other systems. Thanks to Fabrice Laporte. -* The ``-f`` (output format) option to the ``beet list`` command can now contain +- The ``-f`` (output format) option to the ``beet list`` command can now contain template functions as well as field references. Thanks to Steve Dougherty. -* A new command ``beet fields`` displays the available metadata fields (thanks +- A new command ``beet fields`` displays the available metadata fields (thanks to Matteo Mecucci). -* The ``import`` command now has a ``--noincremental`` or ``-I`` flag to disable +- The ``import`` command now has a ``--noincremental`` or ``-I`` flag to disable incremental imports (thanks to Matteo Mecucci). -* When the autotagger fails to find a match, it now displays the number of +- When the autotagger fails to find a match, it now displays the number of tracks on the album (to help you guess what might be going wrong) and a link to the FAQ. -* The default filename character substitutions were changed to be more +- The default filename character substitutions were changed to be more conservative. The Windows "reserved characters" are substituted by default even on Unix platforms (this causes less surprise when using Samba shares to store music). To customize your character substitutions, see :ref:`the replace config option <replace>`. -* :doc:`/plugins/lastgenre`: Added a "fallback" option when no suitable genre +- :doc:`/plugins/lastgenre`: Added a "fallback" option when no suitable genre can be found (thanks to Fabrice Laporte). -* :doc:`/plugins/rewrite`: Unicode rewriting rules are now allowed (thanks to +- :doc:`/plugins/rewrite`: Unicode rewriting rules are now allowed (thanks to Nicolas Dietrich). -* Filename collisions are now avoided when moving album art. -* :doc:`/plugins/bpd`: Print messages to show when directory tree is being +- Filename collisions are now avoided when moving album art. +- :doc:`/plugins/bpd`: Print messages to show when directory tree is being constructed. -* :doc:`/plugins/bpd`: Use Gstreamer's ``playbin2`` element instead of the +- :doc:`/plugins/bpd`: Use Gstreamer's ``playbin2`` element instead of the deprecated ``playbin``. -* :doc:`/plugins/bpd`: Random and repeat modes are now supported (thanks to +- :doc:`/plugins/bpd`: Random and repeat modes are now supported (thanks to Matteo Mecucci). -* :doc:`/plugins/bpd`: Listings are now sorted (thanks once again to Matteo +- :doc:`/plugins/bpd`: Listings are now sorted (thanks once again to Matteo Mecucci). -* Filenames are normalized with Unicode Normal Form D (NFD) on Mac OS X and NFC +- Filenames are normalized with Unicode Normal Form D (NFD) on Mac OS X and NFC on all other platforms. -* Significant internal restructuring to avoid SQLite locking errors. As part of +- Significant internal restructuring to avoid SQLite locking errors. As part of these changes, the not-very-useful "save" plugin event has been removed. .. _pyacoustid: https://github.com/beetbox/pyacoustid @@ -4577,65 +4316,65 @@ deletion now cleans up after itself more thoroughly. Many, many bugs—including several crashers—were fixed. This release lays the foundation for more features to come in the next couple of releases. -* The :doc:`/plugins/lyrics`, originally by `Peter Brunner`_, is revamped and +- The :doc:`/plugins/lyrics`, originally by `Peter Brunner`_, is revamped and included with beets, making it easy to fetch **song lyrics**. -* Items now expose their audio **sample rate**, number of **channels**, and +- Items now expose their audio **sample rate**, number of **channels**, and **bits per sample** (bitdepth). See :doc:`/reference/pathformat` for a list of all available audio properties. Thanks to Andrew Dunn. -* The ``beet list`` command now accepts a "format" argument that lets you **show +- The ``beet list`` command now accepts a "format" argument that lets you **show specific information about each album or track**. For example, run ``beet ls -af '$album: $tracktotal' beatles`` to see how long each Beatles album is. Thanks to Philippe Mongeau. -* The autotagger now tolerates tracks on multi-disc albums that are numbered +- The autotagger now tolerates tracks on multi-disc albums that are numbered per-disc. For example, if track 24 on a release is the first track on the second disc, then it is not penalized for having its track number set to 1 instead of 24. -* The autotagger sets the disc number and disc total fields on autotagged +- The autotagger sets the disc number and disc total fields on autotagged albums. -* The autotagger now also tolerates tracks whose track artists tags are set - to "Various Artists". -* Terminal colors are now supported on Windows via `Colorama`_ (thanks to Karl). -* When previewing metadata differences, the importer now shows discrepancies in +- The autotagger now also tolerates tracks whose track artists tags are set to + "Various Artists". +- Terminal colors are now supported on Windows via Colorama_ (thanks to Karl). +- When previewing metadata differences, the importer now shows discrepancies in track length. -* Importing with ``import_delete`` enabled now cleans up empty directories that +- Importing with ``import_delete`` enabled now cleans up empty directories that contained deleting imported music files. -* Similarly, ``import_delete`` now causes original album art imported from the +- Similarly, ``import_delete`` now causes original album art imported from the disk to be deleted. -* Plugin-supplied template values, such as those created by ``rewrite``, are now +- Plugin-supplied template values, such as those created by ``rewrite``, are now properly sanitized (for example, ``AC/DC`` properly becomes ``AC_DC``). -* Filename extensions are now always lower-cased when copying and moving files. -* The ``inline`` plugin now prints a more comprehensible error when exceptions +- Filename extensions are now always lower-cased when copying and moving files. +- The ``inline`` plugin now prints a more comprehensible error when exceptions occur in Python snippets. -* The ``replace`` configuration option can now remove characters entirely (in +- The ``replace`` configuration option can now remove characters entirely (in addition to replacing them) if the special string ``<strip>`` is specified as the replacement. -* New plugin API: plugins can now add fields to the MediaFile tag abstraction +- New plugin API: plugins can now add fields to the MediaFile tag abstraction layer. See :ref:`writing-plugins`. -* A reasonable error message is now shown when the import log file cannot be +- A reasonable error message is now shown when the import log file cannot be opened. -* The import log file is now flushed and closed properly so that it can be used +- The import log file is now flushed and closed properly so that it can be used to monitor import progress, even when the import crashes. -* Duplicate track matches are no longer shown when autotagging singletons. -* The ``chroma`` plugin now logs errors when fingerprinting fails. -* The ``lastgenre`` plugin suppresses more errors when dealing with the Last.fm +- Duplicate track matches are no longer shown when autotagging singletons. +- The ``chroma`` plugin now logs errors when fingerprinting fails. +- The ``lastgenre`` plugin suppresses more errors when dealing with the Last.fm API. -* Fix a bug in the ``rewrite`` plugin that broke the use of multiple rules for - a single field. -* Fix a crash with non-ASCII characters in bytestring metadata fields (e.g., +- Fix a bug in the ``rewrite`` plugin that broke the use of multiple rules for a + single field. +- Fix a crash with non-ASCII characters in bytestring metadata fields (e.g., MusicBrainz IDs). -* Fix another crash with non-ASCII characters in the configuration paths. -* Fix a divide-by-zero crash on zero-length audio files. -* Fix a crash in the ``chroma`` plugin when the Acoustid database had no +- Fix another crash with non-ASCII characters in the configuration paths. +- Fix a divide-by-zero crash on zero-length audio files. +- Fix a crash in the ``chroma`` plugin when the Acoustid database had no recording associated with a fingerprint. -* Fix a crash when an autotagging with an artist or album containing "AND" or +- Fix a crash when an autotagging with an artist or album containing "AND" or "OR" (upper case). -* Fix an error in the ``rewrite`` and ``inline`` plugins when the corresponding +- Fix an error in the ``rewrite`` and ``inline`` plugins when the corresponding config sections did not exist. -* Fix bitrate estimation for AAC files whose headers are missing the relevant +- Fix bitrate estimation for AAC files whose headers are missing the relevant data. -* Fix the ``list`` command in BPD (thanks to Simon Chopin). +- Fix the ``list`` command in BPD (thanks to Simon Chopin). -.. _Colorama: https://pypi.python.org/pypi/colorama +.. _colorama: https://pypi.python.org/pypi/colorama 1.0b12 (January 16, 2012) ------------------------- @@ -4650,44 +4389,44 @@ In addition, beets avoids problematic filename conflicts by appending numbers to filenames that would otherwise conflict. Three new plugins (``inline``, ``scrub``, and ``rewrite``) are included in this release. -* **Functions in path formats** provide a simple way to write complex file +- **Functions in path formats** provide a simple way to write complex file naming rules: for example, ``%upper{%left{$artist,1}}`` will insert the capitalized first letter of the track's artist. For more details, see :doc:`/reference/pathformat`. If you're interested in adding your own template functions via a plugin, see :ref:`writing-plugins`. -* Plugins can also now define new path *fields* in addition to functions. -* The new :doc:`/plugins/inline` lets you **use Python expressions to customize +- Plugins can also now define new path *fields* in addition to functions. +- The new :doc:`/plugins/inline` lets you **use Python expressions to customize path formats** by defining new fields in the config file. -* The configuration can **condition path formats based on queries**. That is, +- The configuration can **condition path formats based on queries**. That is, you can write a path format that is only used if an item matches a given query. (This supersedes the earlier functionality that only allowed conditioning on album type; if you used this feature in a previous version, you will need to replace, for example, ``soundtrack:`` with ``albumtype_soundtrack:``.) See :ref:`path-format-config`. -* **Filename substitutions are now configurable** via the ``replace`` config +- **Filename substitutions are now configurable** via the ``replace`` config value. You can choose which characters you think should be allowed in your - directory and music file names. See :doc:`/reference/config`. -* Beets now ensures that files have **unique filenames** by appending a number + directory and music file names. See :doc:`/reference/config`. +- Beets now ensures that files have **unique filenames** by appending a number to any filename that would otherwise conflict with an existing file. -* The new :doc:`/plugins/scrub` can remove extraneous metadata either manually +- The new :doc:`/plugins/scrub` can remove extraneous metadata either manually or automatically. -* The new :doc:`/plugins/rewrite` can canonicalize names for path formats. -* The autotagging heuristics have been tweaked in situations where the +- The new :doc:`/plugins/rewrite` can canonicalize names for path formats. +- The autotagging heuristics have been tweaked in situations where the MusicBrainz database did not contain track lengths. Previously, beets penalized matches where this was the case, leading to situations where seemingly good matches would have poor similarity. This penalty has been removed. -* Fix an incompatibility in BPD with libmpc (the library that powers mpc and +- Fix an incompatibility in BPD with libmpc (the library that powers mpc and ncmpc). -* Fix a crash when importing a partial match whose first track was missing. -* The ``lastgenre`` plugin now correctly writes discovered genres to imported +- Fix a crash when importing a partial match whose first track was missing. +- The ``lastgenre`` plugin now correctly writes discovered genres to imported files (when tag-writing is enabled). -* Add a message when skipping directories during an incremental import. -* The default ignore settings now ignore all files beginning with a dot. -* Date values in path formats (``$year``, ``$month``, and ``$day``) are now +- Add a message when skipping directories during an incremental import. +- The default ignore settings now ignore all files beginning with a dot. +- Date values in path formats (``$year``, ``$month``, and ``$day``) are now appropriately zero-padded. -* Removed the ``--path-format`` global flag for ``beet``. -* Removed the ``lastid`` plugin, which was deprecated in the previous version. +- Removed the ``--path-format`` global flag for ``beet``. +- Removed the ``lastid`` plugin, which was deprecated in the previous version. 1.0b11 (December 12, 2011) -------------------------- @@ -4695,66 +4434,71 @@ filenames that would otherwise conflict. Three new plugins (``inline``, This version of beets focuses on transitioning the autotagger to the new version of the MusicBrainz database (called NGS). This transition brings with it a number of long-overdue improvements: most notably, predictable behavior when -tagging multi-disc albums and integration with the new `Acoustid`_ acoustic +tagging multi-disc albums and integration with the new Acoustid_ acoustic fingerprinting technology. The importer can also now tag *incomplete* albums when you're missing a few tracks from a given release. Two other new plugins are also included with this release: one for assigning genres and another for ReplayGain analysis. -* Beets now communicates with MusicBrainz via the new `Next Generation Schema`_ - (NGS) service via `python-musicbrainzngs`_. The bindings are included with - this version of beets, but a future version will make them an external - dependency. -* The importer now detects **multi-disc albums** and tags them together. Using a +- Beets now communicates with MusicBrainz via the new `Next Generation Schema`_ + (NGS) service via python-musicbrainzngs_. The bindings are included with this + version of beets, but a future version will make them an external dependency. +- The importer now detects **multi-disc albums** and tags them together. Using a heuristic based on the names of directories, certain structures are classified as multi-disc albums: for example, if a directory contains subdirectories labeled "disc 1" and "disc 2", these subdirectories will be coalesced into a single album for tagging. -* The new :doc:`/plugins/chroma` uses the `Acoustid`_ **open-source acoustic +- The new :doc:`/plugins/chroma` uses the Acoustid_ **open-source acoustic fingerprinting** service. This replaces the old ``lastid`` plugin, which used Last.fm fingerprinting and is now deprecated. Fingerprinting with this library should be faster and more reliable. -* The importer can now perform **partial matches**. This means that, if you're +- The importer can now perform **partial matches**. This means that, if you're missing a few tracks from an album, beets can still tag the remaining tracks as a single album. (Thanks to `Simon Chopin`_.) -* The new :doc:`/plugins/lastgenre` automatically **assigns genres to imported +- The new :doc:`/plugins/lastgenre` automatically **assigns genres to imported albums** and items based on Last.fm tags and an internal whitelist. (Thanks to - `KraYmer`_.) -* The :doc:`/plugins/replaygain`, written by `Peter Brunner`_, has been merged + KraYmer_.) +- The :doc:`/plugins/replaygain`, written by `Peter Brunner`_, has been merged into the core beets distribution. Use it to analyze audio and **adjust playback levels** in ReplayGain-aware music players. -* Albums are now tagged with their *original* release date rather than the date +- Albums are now tagged with their *original* release date rather than the date of any reissue, remaster, "special edition", or the like. -* The config file and library databases are now given better names and locations +- The config file and library databases are now given better names and locations on Windows. Namely, both files now reside in ``%APPDATA%``; the config file is named ``beetsconfig.ini`` and the database is called ``beetslibrary.blb`` (neither has a leading dot as on Unix). For backwards compatibility, beets will check the old locations first. -* When entering an ID manually during tagging, beets now searches for anything +- When entering an ID manually during tagging, beets now searches for anything that looks like an MBID in the entered string. This means that full MusicBrainz URLs now work as IDs at the prompt. (Thanks to derwin.) -* The importer now ignores certain "clutter" files like ``.AppleDouble`` +- The importer now ignores certain "clutter" files like ``.AppleDouble`` directories and ``._*`` files. The list of ignored patterns is configurable via the ``ignore`` setting; see :doc:`/reference/config`. -* The database now keeps track of files' modification times so that, during - an ``update``, unmodified files can be skipped. (Thanks to Jos van der Til.) -* The album art fetcher now uses `albumart.org`_ as a fallback when the Amazon - art downloader fails. -* A new ``timeout`` config value avoids database locking errors on slow systems. -* Fix a crash after using the "as Tracks" option during import. -* Fix a Unicode error when tagging items with missing titles. -* Fix a crash when the state file (``~/.beetsstate``) became emptied or +- The database now keeps track of files' modification times so that, during an + ``update``, unmodified files can be skipped. (Thanks to Jos van der Til.) +- The album art fetcher now uses albumart.org_ as a fallback when the Amazon art + downloader fails. +- A new ``timeout`` config value avoids database locking errors on slow systems. +- Fix a crash after using the "as Tracks" option during import. +- Fix a Unicode error when tagging items with missing titles. +- Fix a crash when the state file (``~/.beetsstate``) became emptied or corrupted. -.. _KraYmer: https://github.com/KraYmer -.. _Next Generation Schema: https://musicbrainz.org/doc/XML_Web_Service/Version_2 -.. _python-musicbrainzngs: https://github.com/alastair/python-musicbrainzngs .. _acoustid: https://acoustid.org/ -.. _Peter Brunner: https://github.com/Lugoues -.. _Simon Chopin: https://github.com/laarmen + .. _albumart.org: https://www.albumart.org/ +.. _kraymer: https://github.com/KraYmer + +.. _next generation schema: https://musicbrainz.org/doc/XML_Web_Service/Version_2 + +.. _peter brunner: https://github.com/Lugoues + +.. _python-musicbrainzngs: https://github.com/alastair/python-musicbrainzngs + +.. _simon chopin: https://github.com/laarmen + 1.0b10 (September 22, 2011) --------------------------- @@ -4771,54 +4515,40 @@ previously-imported directories (with the ``-i`` flag) and there's an :doc:`experimental Web interface </plugins/web>` to beets in a new standard plugin. -* A new ``beet modify`` command enables **manual, command-line-based +- A new ``beet modify`` command enables **manual, command-line-based modification** of music metadata. Pass it a query along with ``field=value`` pairs that specify the changes you want to make. - -* A new ``beet update`` command updates the database to reflect **changes in the +- A new ``beet update`` command updates the database to reflect **changes in the on-disk metadata**. You can now use an external program to edit tags on files, remove files and directories, etc., and then run ``beet update`` to make sure your beets library is in sync. This will also rename files to reflect their new metadata. - -* A new ``beet move`` command can **copy or move files** into your library +- A new ``beet move`` command can **copy or move files** into your library directory or to another specified directory. - -* When importing files that are already in the library database, the items are +- When importing files that are already in the library database, the items are no longer duplicated---instead, the library is updated to reflect the new metadata. This way, the import command can be transparently used as a **re-import**. - -* Relatedly, the ``-L`` flag to the "import" command makes it take a query as +- Relatedly, the ``-L`` flag to the "import" command makes it take a query as its argument instead of a list of directories. The matched albums (or items, depending on the ``-s`` flag) are then re-imported. - -* A new flag ``-i`` to the import command runs **incremental imports**, keeping +- A new flag ``-i`` to the import command runs **incremental imports**, keeping track of and skipping previously-imported directories. This has the effect of making repeated import commands pick up only newly-added directories. The ``import_incremental`` config option makes this the default. - -* When pruning directories, "clutter" files such as ``.DS_Store`` and +- When pruning directories, "clutter" files such as ``.DS_Store`` and ``Thumbs.db`` are ignored (and removed with otherwise-empty directories). - -* The :doc:`/plugins/web` encapsulates a simple **Web-based GUI for beets**. The +- The :doc:`/plugins/web` encapsulates a simple **Web-based GUI for beets**. The current iteration can browse the library and play music in browsers that support HTML5 Audio. - -* When moving items that are part of an album, the album art implicitly moves +- When moving items that are part of an album, the album art implicitly moves too. - -* Files are no longer silently overwritten when moving and copying files. - -* Handle exceptions thrown when running Mutagen. - -* Fix a missing ``__future__`` import in ``embed art`` on Python 2.5. - -* Fix ID3 and MPEG-4 tag names for the album-artist field. - -* Fix Unicode encoding of album artist, album type, and label. - -* Fix crash when "copying" an art file that's already in place. +- Files are no longer silently overwritten when moving and copying files. +- Handle exceptions thrown when running Mutagen. +- Fix a missing ``__future__`` import in ``embed art`` on Python 2.5. +- Fix ID3 and MPEG-4 tag names for the album-artist field. +- Fix Unicode encoding of album artist, album type, and label. +- Fix crash when "copying" an art file that's already in place. 1.0b9 (July 9, 2011) -------------------- @@ -4827,101 +4557,74 @@ This release focuses on a large number of small fixes and improvements that turn beets into a well-oiled, music-devouring machine. See the full release notes, below, for a plethora of new features. -* **Queries can now contain whitespace.** Spaces passed as shell arguments are +- **Queries can now contain whitespace.** Spaces passed as shell arguments are now preserved, so you can use your shell's escaping syntax (quotes or - backslashes, for instance) to include spaces in queries. For example, - typing``beet ls "the knife"`` or ``beet ls the\ knife``. Read more in + backslashes, for instance) to include spaces in queries. For example, ``beet + ls "the knife"`` or ``beet ls theknife``. Read more in :doc:`/reference/query`. - -* Queries can **match items from the library by directory**. A ``path:`` prefix +- Queries can **match items from the library by directory**. A ``path:`` prefix is optional; any query containing a path separator (/ on POSIX systems) is assumed to be a path query. Running ``beet ls path/to/music`` will show all the music in your library under the specified directory. The :doc:`/reference/query` reference again has more details. - -* **Local album art** is now automatically discovered and copied from the +- **Local album art** is now automatically discovered and copied from the imported directories when available. - -* When choosing the "as-is" import album (or doing a non-autotagged import), +- When choosing the "as-is" import album (or doing a non-autotagged import), **every album either has an "album artist" set or is marked as a compilation (Various Artists)**. The choice is made based on the homogeneity of the tracks' artists. This prevents compilations that are imported as-is from being scattered across many directories after they are imported. - -* The release **label** for albums and tracks is now fetched from !MusicBrainz, +- The release **label** for albums and tracks is now fetched from !MusicBrainz, written to files, and stored in the database. - -* The "list" command now accepts a ``-p`` switch that causes it to **show +- The "list" command now accepts a ``-p`` switch that causes it to **show paths** instead of titles. This makes the output of ``beet ls -p`` suitable - for piping into another command such as `xargs`_. - -* Release year and label are now shown in the candidate selection list to help + for piping into another command such as xargs_. +- Release year and label are now shown in the candidate selection list to help disambiguate different releases of the same album. - -* Prompts in the importer interface are now colorized for easy reading. The +- Prompts in the importer interface are now colorized for easy reading. The default option is always highlighted. - -* The importer now provides the option to specify a MusicBrainz ID manually if +- The importer now provides the option to specify a MusicBrainz ID manually if the built-in searching isn't working for a particular album or track. - -* ``$bitrate`` in path formats is now formatted as a human-readable kbps value +- ``$bitrate`` in path formats is now formatted as a human-readable kbps value instead of as a raw integer. - -* The import logger has been improved for "always-on" use. First, it is now +- The import logger has been improved for "always-on" use. First, it is now possible to specify a log file in .beetsconfig. Also, logs are now appended rather than overwritten and contain timestamps. - -* Album art fetching and plugin events are each now run in separate pipeline +- Album art fetching and plugin events are each now run in separate pipeline stages during imports. This should bring additional performance when using album art plugins like embedart or beets-lyrics. - -* Accents and other Unicode decorators on characters are now treated more fairly +- Accents and other Unicode decorators on characters are now treated more fairly by the autotagger. For example, if you're missing the acute accent on the "e" - in "café", that change won't be penalized. This introduces a new dependency - on the `unidecode`_ Python module. - -* When tagging a track with no title set, the track's filename is now shown + in "café", that change won't be penalized. This introduces a new dependency on + the unidecode_ Python module. +- When tagging a track with no title set, the track's filename is now shown (instead of nothing at all). - -* The bitrate of lossless files is now calculated from their file size (rather +- The bitrate of lossless files is now calculated from their file size (rather than being fixed at 0 or reflecting the uncompressed audio bitrate). - -* Fixed a problem where duplicate albums or items imported at the same time +- Fixed a problem where duplicate albums or items imported at the same time would fail to be detected. - -* BPD now uses a persistent "virtual filesystem" in order to fake a directory +- BPD now uses a persistent "virtual filesystem" in order to fake a directory structure. This means that your path format settings are respected in BPD's browsing hierarchy. This may come at a performance cost, however. The virtual filesystem used by BPD is available for reuse by plugins (e.g., the FUSE plugin). - -* Singleton imports (``beet import -s``) can now take individual files as +- Singleton imports (``beet import -s``) can now take individual files as arguments as well as directories. +- Fix Unicode queries given on the command line. +- Fix crasher in quiet singleton imports (``import -qs``). +- Fix crash when autotagging files with no metadata. +- Fix a rare deadlock when finishing the import pipeline. +- Fix an issue that was causing mpdupdate to run twice for every album. +- Fix a bug that caused release dates/years not to be fetched. +- Fix a crasher when setting MBIDs on MP3s file metadata. +- Fix a "broken pipe" error when piping beets' standard output. +- A better error message is given when the database file is unopenable. +- Suppress errors due to timeouts and bad responses from MusicBrainz. +- Fix a crash on album queries with item-only field names. -* Fix Unicode queries given on the command line. - -* Fix crasher in quiet singleton imports (``import -qs``). - -* Fix crash when autotagging files with no metadata. - -* Fix a rare deadlock when finishing the import pipeline. - -* Fix an issue that was causing mpdupdate to run twice for every album. - -* Fix a bug that caused release dates/years not to be fetched. - -* Fix a crasher when setting MBIDs on MP3s file metadata. - -* Fix a "broken pipe" error when piping beets' standard output. - -* A better error message is given when the database file is unopenable. - -* Suppress errors due to timeouts and bad responses from MusicBrainz. - -* Fix a crash on album queries with item-only field names. +.. _unidecode: https://pypi.python.org/pypi/Unidecode/0.04.1 .. _xargs: https://en.wikipedia.org/wiki/xargs -.. _unidecode: https://pypi.python.org/pypi/Unidecode/0.04.1 1.0b8 (April 28, 2011) ---------------------- @@ -4935,57 +4638,44 @@ catalog, and manipulate your individual tracks. Second, beets can now storing it in a "file on the side." Check out the :doc:`/plugins/embedart` for that functionality. -* Better support for **singleton (non-album) tracks**. Whereas beets previously +- Better support for **singleton (non-album) tracks**. Whereas beets previously only really supported full albums, now it can also keep track of individual, off-album songs. The "singleton" path format can be used to customize where these tracks are stored. To import singleton tracks, provide the -s switch to the import command or, while doing a normal full-album import, choose the "as - Tracks" (T) option to add singletons to your library. To list only singleton + Tracks" (T) option to add singletons to your library. To list only singleton or only album tracks, use the new ``singleton:`` query term: the query ``singleton:true`` matches only singleton tracks; ``singleton:false`` matches - only album tracks. The ``lastid`` plugin has been extended to support - matching individual items as well. - -* The importer/autotagger system has been heavily refactored in this release. - If anything breaks as a result, please get in touch or just file a bug. - -* Support for **album art embedded in files**. A new :doc:`/plugins/embedart` + only album tracks. The ``lastid`` plugin has been extended to support matching + individual items as well. +- The importer/autotagger system has been heavily refactored in this release. If + anything breaks as a result, please get in touch or just file a bug. +- Support for **album art embedded in files**. A new :doc:`/plugins/embedart` implements this functionality. Enable the plugin to automatically embed downloaded album art into your music files' metadata. The plugin also provides the "embedart" and "extractart" commands for moving image files in and out of metadata. See the wiki for more details. (Thanks, daenney!) - -* The "distance" number, which quantifies how different an album's current and +- The "distance" number, which quantifies how different an album's current and proposed metadata are, is now displayed as "similarity" instead. This should be less noisy and confusing; you'll now see 99.5% instead of 0.00489323. - -* A new "timid mode" in the importer asks the user every time, even when it +- A new "timid mode" in the importer asks the user every time, even when it makes a match with very high confidence. The ``-t`` flag on the command line and the ``import_timid`` config option control this mode. (Thanks to mdecker on GitHub!) - -* The multithreaded importer should now abort (either by selecting aBort or by +- The multithreaded importer should now abort (either by selecting aBort or by typing ^C) much more quickly. Previously, it would try to get a lot of work done before quitting; now it gives up as soon as it can. - -* Added a new plugin event, ``album_imported``, which is called every time an +- Added a new plugin event, ``album_imported``, which is called every time an album is added to the library. (Thanks, Lugoues!) - -* A new plugin method, ``register_listener``, is an imperative alternative to +- A new plugin method, ``register_listener``, is an imperative alternative to the ``@listen`` decorator (Thanks again, Lugoues!) - -* In path formats, ``$albumartist`` now falls back to ``$artist`` (as well as +- In path formats, ``$albumartist`` now falls back to ``$artist`` (as well as the other way around). - -* The importer now prints "(unknown album)" when no tags are present. - -* When autotagging, "and" is considered equal to "&". - -* Fix some crashes when deleting files that don't exist. - -* Fix adding individual tracks in BPD. - -* Fix crash when ``~/.beetsconfig`` does not exist. +- The importer now prints "(unknown album)" when no tags are present. +- When autotagging, "and" is considered equal to "&". +- Fix some crashes when deleting files that don't exist. +- Fix adding individual tracks in BPD. +- Fix crash when ``~/.beetsconfig`` does not exist. 1.0b7 (April 5, 2011) --------------------- @@ -4996,74 +4686,55 @@ autotagger is better at handling them. It also includes a number of oft-requested improvements to the ``beet`` command-line tool, including several new configuration options and the ability to clean up empty directory subtrees. -* **"Various artists" releases** are handled much more gracefully. The +- **"Various artists" releases** are handled much more gracefully. The autotagger now sets the ``comp`` flag on albums whenever the album is identified as a "various artists" release by !MusicBrainz. Also, there is now a distinction between the "album artist" and the "track artist", the latter of which is never "Various Artists" or other such bogus stand-in. *(Thanks to Jonathan for the bulk of the implementation work on this feature!)* - -* The directory hierarchy can now be **customized based on release type**. In +- The directory hierarchy can now be **customized based on release type**. In particular, the ``path_format`` setting in .beetsconfig has been replaced with a new ``[paths]`` section, which allows you to specify different path formats for normal and "compilation" (various artists) releases as well as for each album type (see below). The default path formats have been changed to use ``$albumartist`` instead of ``$artist``. - -* A **new ``albumtype`` field** reflects the release type `as specified by +- A **new** ``albumtype`` **field** reflects the release type `as specified by MusicBrainz`_. - -* When deleting files, beets now appropriately "prunes" the directory - tree---empty directories are automatically cleaned up. *(Thanks to - wlof on GitHub for this!)* - -* The tagger's output now always shows the album directory that is currently +- When deleting files, beets now appropriately "prunes" the directory + tree---empty directories are automatically cleaned up. *(Thanks to wlof on + GitHub for this!)* +- The tagger's output now always shows the album directory that is currently being tagged. This should help in situations where files' current tags are missing or useless. - -* The logging option (``-l``) to the ``import`` command now logs duplicate +- The logging option (``-l``) to the ``import`` command now logs duplicate albums. - -* A new ``import_resume`` configuration option can be used to disable the +- A new ``import_resume`` configuration option can be used to disable the importer's resuming feature or force it to resume without asking. This option may be either ``yes``, ``no``, or ``ask``, with the obvious meanings. The ``-p`` and ``-P`` command-line flags override this setting and correspond to the "yes" and "no" settings. - -* Resuming is automatically disabled when the importer is in quiet (``-q``) +- Resuming is automatically disabled when the importer is in quiet (``-q``) mode. Progress is still saved, however, and the ``-p`` flag (above) can be used to force resuming. - -* The ``BEETSCONFIG`` environment variable can now be used to specify the +- The ``BEETSCONFIG`` environment variable can now be used to specify the location of the config file that is at ~/.beetsconfig by default. - -* A new ``import_quiet_fallback`` config option specifies what should - happen in quiet mode when there is no strong recommendation. The options are - ``skip`` (the default) and "asis". - -* When importing with the "delete" option and importing files that are already +- A new ``import_quiet_fallback`` config option specifies what should happen in + quiet mode when there is no strong recommendation. The options are ``skip`` + (the default) and "asis". +- When importing with the "delete" option and importing files that are already at their destination, files could be deleted (leaving zero copies afterward). This is fixed. +- The ``version`` command now lists all the loaded plugins. +- A new plugin, called ``info``, just prints out audio file metadata. +- Fix a bug where some files would be erroneously interpreted as MPEG-4 audio. +- Fix permission bits applied to album art files. +- Fix malformed !MusicBrainz queries caused by null characters. +- Fix a bug with old versions of the Monkey's Audio format. +- Fix a crash on broken symbolic links. +- Retry in more cases when !MusicBrainz servers are slow/overloaded. +- The old "albumify" plugin for upgrading databases was removed. -* The ``version`` command now lists all the loaded plugins. - -* A new plugin, called ``info``, just prints out audio file metadata. - -* Fix a bug where some files would be erroneously interpreted as MPEG-4 audio. - -* Fix permission bits applied to album art files. - -* Fix malformed !MusicBrainz queries caused by null characters. - -* Fix a bug with old versions of the Monkey's Audio format. - -* Fix a crash on broken symbolic links. - -* Retry in more cases when !MusicBrainz servers are slow/overloaded. - -* The old "albumify" plugin for upgrading databases was removed. - -.. _as specified by MusicBrainz: https://wiki.musicbrainz.org/ReleaseType +.. _as specified by musicbrainz: https://wiki.musicbrainz.org/ReleaseType 1.0b6 (January 20, 2011) ------------------------ @@ -5072,51 +4743,40 @@ This version consists primarily of bug fixes and other small improvements. It's in preparation for a more feature-ful release in beta 7. The most important issue involves correct ordering of autotagged albums. -* **Quiet import:** a new "-q" command line switch for the import command +- **Quiet import:** a new "-q" command line switch for the import command suppresses all prompts for input; it pessimistically skips all albums that the importer is not completely confident about. - -* Added support for the **WavPack** and **Musepack** formats. Unfortunately, due +- Added support for the **WavPack** and **Musepack** formats. Unfortunately, due to a limitation in the Mutagen library (used by beets for metadata manipulation), Musepack SV8 is not yet supported. Here's the `upstream bug`_ in question. - -* BPD now uses a pure-Python socket library and no longer requires +- BPD now uses a pure-Python socket library and no longer requires eventlet/greenlet (the latter of which is a C extension). For the curious, the - socket library in question is called `Bluelet`_. - -* Non-autotagged imports are now resumable (just like autotagged imports). - -* Fix a terrible and long-standing bug where track orderings were never applied. + socket library in question is called Bluelet_. +- Non-autotagged imports are now resumable (just like autotagged imports). +- Fix a terrible and long-standing bug where track orderings were never applied. This manifested when the tagger appeared to be applying a reasonable ordering to the tracks but, later, the database reflects a completely wrong association of track names to files. The order applied was always just alphabetical by filename, which is frequently but not always what you want. - -* We now use Windows' "long filename" support. This API is fairly tricky, +- We now use Windows' "long filename" support. This API is fairly tricky, though, so some instability may still be present---please file a bug if you run into pathname weirdness on Windows. Also, filenames on Windows now never end in spaces. - -* Fix crash in lastid when the artist name is not available. - -* Fixed a spurious crash when ``LANG`` or a related environment variable is set +- Fix crash in lastid when the artist name is not available. +- Fixed a spurious crash when ``LANG`` or a related environment variable is set to an invalid value (such as ``'UTF-8'`` on some installations of Mac OS X). - -* Fixed an error when trying to copy a file that is already at its destination. - -* When copying read-only files, the importer now tries to make the copy +- Fixed an error when trying to copy a file that is already at its destination. +- When copying read-only files, the importer now tries to make the copy writable. (Previously, this would just crash the import.) - -* Fixed an ``UnboundLocalError`` when no matches are found during autotag. - -* Fixed a Unicode encoding error when entering special characters into the +- Fixed an ``UnboundLocalError`` when no matches are found during autotag. +- Fixed a Unicode encoding error when entering special characters into the "manual search" prompt. +- Added ``beet version`` command that just shows the current release version. -* Added `` beet version`` command that just shows the current release version. +.. _bluelet: https://github.com/sampsyo/bluelet .. _upstream bug: https://github.com/quodlibet/mutagen/issues/7 -.. _Bluelet: https://github.com/sampsyo/bluelet 1.0b5 (September 28, 2010) -------------------------- @@ -5129,55 +4789,41 @@ it more reliable. This release also greatly expands the capabilities of beets' :doc:`plugin API </plugins/index>`. A host of other little features and fixes are also rolled into this release. -* The ``lastid`` plugin adds Last.fm **acoustic fingerprinting - support** to the autotagger. Similar to the PUIDs used by !MusicBrainz Picard, - this system allows beets to recognize files that don't have any metadata at - all. You'll need to install some dependencies for this plugin to work. - -* To support the above, there's also a new system for **extending the autotagger +- The ``lastid`` plugin adds Last.fm **acoustic fingerprinting support** to the + autotagger. Similar to the PUIDs used by !MusicBrainz Picard, this system + allows beets to recognize files that don't have any metadata at all. You'll + need to install some dependencies for this plugin to work. +- To support the above, there's also a new system for **extending the autotagger via plugins**. Plugins can currently add components to the track and album distance functions as well as augment the MusicBrainz search. The new API is documented at :doc:`/plugins/index`. - -* **String comparisons** in the autotagger have been augmented to act more +- **String comparisons** in the autotagger have been augmented to act more intuitively. Previously, if your album had the title "Something (EP)" and it was officially called "Something", then beets would think this was a fairly significant change. It now checks for and appropriately reweights certain parts of each string. As another example, the title "The Great Album" is considered equal to "Great Album, The". - -* New **event system for plugins** (thanks, Jeff!). Plugins can now get +- New **event system for plugins** (thanks, Jeff!). Plugins can now get callbacks from beets when certain events occur in the core. Again, the API is documented in :doc:`/plugins/index`. - -* The BPD plugin is now disabled by default. This greatly simplifies +- The BPD plugin is now disabled by default. This greatly simplifies installation of the beets core, which is now 100% pure Python. To use BPD, though, you'll need to set ``plugins: bpd`` in your .beetsconfig. - -* The ``import`` command can now remove original files when it copies items into +- The ``import`` command can now remove original files when it copies items into your library. (This might be useful if you're low on disk space.) Set the ``import_delete`` option in your .beetsconfig to ``yes``. - -* Importing without autotagging (``beet import -A``) now prints out album names +- Importing without autotagging (``beet import -A``) now prints out album names as it imports them to indicate progress. - -* The new :doc:`/plugins/mpdupdate` will automatically update your MPD server's +- The new :doc:`/plugins/mpdupdate` will automatically update your MPD server's index whenever your beets library changes. - -* Efficiency tweak should reduce the number of !MusicBrainz queries per +- Efficiency tweak should reduce the number of !MusicBrainz queries per autotagged album. - -* A new ``-v`` command line switch enables debugging output. - -* Fixed bug that completely broke non-autotagged imports (``import -A``). - -* Fixed bug that logged the wrong paths when using ``import -l``. - -* Fixed autotagging for the creatively-named band `!!!`_. - -* Fixed normalization of relative paths. - -* Fixed escaping of ``/`` characters in paths on Windows. +- A new ``-v`` command line switch enables debugging output. +- Fixed bug that completely broke non-autotagged imports (``import -A``). +- Fixed bug that logged the wrong paths when using ``import -l``. +- Fixed autotagging for the creatively-named band `!!!`_. +- Fixed normalization of relative paths. +- Fixed escaping of ``/`` characters in paths on Windows. .. _!!!: https://musicbrainz.org/artist/f26c72d3-e52c-467b-b651-679c73d8e1a7.html @@ -5209,24 +4855,22 @@ for Windows users. This should make running beets much easier: just type Here's the detailed list of changes: -* **Parallel tagger.** The autotagger has been reimplemented to use multiple +- **Parallel tagger.** The autotagger has been reimplemented to use multiple threads. This means that it can concurrently read files from disk, talk to the user, communicate with MusicBrainz, and write data back to disk. Not only does this make the tagger much faster because independent work may be performed in parallel, but it makes the tagging process much more pleasant for large imports. The user can let albums queue up in the background while making a - decision rather than waiting for beets between each question it asks. The + decision rather than waiting for beets between each question it asks. The parallel tagger is on by default but a sequential (single- threaded) version is still available by setting the ``threaded`` config value to ``no`` (because the parallel version is still quite experimental). - -* **Colorized tagger output.** The autotagger interface now makes it a little +- **Colorized tagger output.** The autotagger interface now makes it a little easier to see what's going on at a glance by highlighting changes with terminal colors. This feature is on by default, but you can turn it off by setting ``color`` to ``no`` in your ``.beetsconfig`` (if, for example, your terminal doesn't understand colors and garbles the output). - -* **Pause and resume imports.** The ``import`` command now keeps track of its +- **Pause and resume imports.** The ``import`` command now keeps track of its progress, so if you're interrupted (beets crashes, you abort the process, an alien devours your motherboard, etc.), beets will try to resume from the point where you left off. The next time you run ``import`` on the same directory, it @@ -5234,34 +4878,26 @@ Here's the detailed list of changes: through the albums in the directory until it encounters the last one it saw. (This means it might fail if that album can't be found.) Also, you can now abort the tagging process by entering ``b`` (for aBort) at any of the prompts. - -* Overhauled methods for handling filesystem paths to allow filenames that have +- Overhauled methods for handling filesystem paths to allow filenames that have badly encoded special characters. These changes are pretty fragile, so please report any bugs involving ``UnicodeError`` or SQLite ``ProgrammingError`` messages in this version. - -* The destination paths (the library directory structure) now respect +- The destination paths (the library directory structure) now respect album-level metadata. This means that if you have an album in which two tracks have different album-level attributes (like year, for instance), they will - still wind up in the same directory together. (There's currently not a very + still wind up in the same directory together. (There's currently not a very smart method for picking the "correct" album-level metadata, but we'll fix that later.) - -* Fixed a bug where the CLI would fail completely if the ``LANG`` environment +- Fixed a bug where the CLI would fail completely if the ``LANG`` environment variable was not set. - -* Fixed removal of albums (``beet remove -a``): previously, the album record +- Fixed removal of albums (``beet remove -a``): previously, the album record would stay around although the items were deleted. - -* The setup script now makes a ``beet.exe`` startup stub on Windows; Windows +- The setup script now makes a ``beet.exe`` startup stub on Windows; Windows users can now just type ``beet`` at the prompt to run beets. - -* Fixed an occasional bug where Mutagen would complain that a tag was already +- Fixed an occasional bug where Mutagen would complain that a tag was already present. - -* Fixed a bug with reading invalid integers from ID3 tags. - -* The tagger should now be a little more reluctant to reorder tracks that +- Fixed a bug with reading invalid integers from ID3 tags. +- The tagger should now be a little more reluctant to reorder tracks that already have indices. 1.0b3 (July 22, 2010) @@ -5269,8 +4905,8 @@ Here's the detailed list of changes: This release features two major additions to the autotagger's functionality: album art fetching and MusicBrainz ID tags. It also contains some important -under-the-hood improvements: a new plugin architecture is introduced -and the database schema is extended with explicit support for albums. +under-the-hood improvements: a new plugin architecture is introduced and the +database schema is extended with explicit support for albums. This release has one major backwards-incompatibility. Because of the new way beets handles albums in the library, databases created with an old version of @@ -5279,7 +4915,7 @@ switch to ``beet list`` and ``beet remove``, as well as the file browser for BPD). To "upgrade" an old database, you can use the included ``albumify`` plugin (see the fourth bullet point below). -* **Album art.** The tagger now, by default, downloads album art from Amazon +- **Album art.** The tagger now, by default, downloads album art from Amazon that is referenced in the MusicBrainz database. It places the album art alongside the audio files in a file called (for example) ``cover.jpg``. The ``import_art`` config option controls this behavior, as do the ``-r`` and @@ -5287,8 +4923,7 @@ BPD). To "upgrade" an old database, you can use the included ``albumify`` plugin of the album art file with the ``art_filename`` config option. (See :doc:`/reference/config` for more information about how to configure the album art downloader.) - -* **Support for MusicBrainz ID tags.** The autotagger now keeps track of the +- **Support for MusicBrainz ID tags.** The autotagger now keeps track of the MusicBrainz track, album, and artist IDs it matched for each file. It also looks for album IDs in new files it's importing and uses those to look up data in MusicBrainz. Furthermore, track IDs are used as a component of the tagger's @@ -5298,8 +4933,7 @@ BPD). To "upgrade" an old database, you can use the included ``albumify`` plugin form of migrations so that new columns could be added to old databases--this is a delicate feature, so it would be very wise to make a backup of your database before upgrading to this version. - -* **Plugin architecture.** Add-on modules can now add new commands to the beets +- **Plugin architecture.** Add-on modules can now add new commands to the beets command-line interface. The ``bpd`` and ``dadd`` commands were removed from the beets core and turned into plugins; BPD is loaded by default. To load the non-default plugins, use the config options ``plugins`` (a space-separated @@ -5308,8 +4942,7 @@ BPD). To "upgrade" an old database, you can use the included ``albumify`` plugin under the ``beetsplug`` namespace package containing subclasses of ``beets.plugins.BeetsPlugin``. See `the beetsplug directory`_ for examples or :doc:`/plugins/index` for instructions. - -* As a consequence of adding album art, the database was significantly +- As a consequence of adding album art, the database was significantly refactored to keep track of some information at an album (rather than item) granularity. Databases created with earlier versions of beets should work fine, but they won't have any "albums" in them--they'll just be a bag of @@ -5318,19 +4951,17 @@ BPD). To "upgrade" an old database, you can use the included ``albumify`` plugin ``albumify`` plugin. Running ``beets albumify`` with the plugin activated (set ``plugins=albumify`` in your config file) will group all your items into albums, making beets behave more or less as it did before. - -* Fixed some bugs with encoding paths on Windows. Also, ``:`` is now replaced +- Fixed some bugs with encoding paths on Windows. Also, ``:`` is now replaced with ``-`` in path names (instead of ``_``) for readability. - -* ``MediaFile``s now have a ``format`` attribute, so you can use ``$format`` in +- ``MediaFile`` now has a ``format`` attribute, so you can use ``$format`` in your library path format strings like ``$artist - $album ($format)`` to get directories with names like ``Paul Simon - Graceland (FLAC)``. .. _for the future: https://github.com/google-code-export/beets/issues/69 -.. _the beetsplug directory: - https://github.com/beetbox/beets/tree/master/beetsplug -Beets also now has its first third-party plugin: `beetfs`_, by Martin Eve! It +.. _the beetsplug directory: https://github.com/beetbox/beets/tree/master/beetsplug + +Beets also now has its first third-party plugin: beetfs_, by Martin Eve! It exposes your music in a FUSE filesystem using a custom directory structure. Even cooler: it lets you keep your files intact on-disk while correcting their tags when accessed through FUSE. Check it out! @@ -5344,27 +4975,22 @@ This release focuses on high-priority fixes and conspicuously missing features. Highlights include support for two new audio formats (Monkey's Audio and Ogg Vorbis) and an option to log untaggable albums during import. -* **Support for Ogg Vorbis and Monkey's Audio** files and their tags. (This +- **Support for Ogg Vorbis and Monkey's Audio** files and their tags. (This support should be considered preliminary: I haven't tested it heavily because I don't use either of these formats regularly.) - -* An option to the ``beet import`` command for **logging albums that are +- An option to the ``beet import`` command for **logging albums that are untaggable** (i.e., are skipped or taken "as-is"). Use ``beet import -l LOGFILE PATHS``. The log format is very simple: it's just a status (either "skip" or "asis") followed by the path to the album in question. The idea is that you can tag a large collection and automatically keep track of the albums that weren't found in MusicBrainz so you can come back and look at them later. - -* Fixed a ``UnicodeEncodeError`` on terminals that don't (or don't claim to) +- Fixed a ``UnicodeEncodeError`` on terminals that don't (or don't claim to) support UTF-8. - -* Importing without autotagging (``beet import -A``) is now faster and doesn't +- Importing without autotagging (``beet import -A``) is now faster and doesn't print out a bunch of whitespace. It also lets you specify single files on the command line (rather than just directories). - -* Fixed importer crash when attempting to read a corrupt file. - -* Reorganized code for CLI in preparation for adding pluggable subcommands. Also +- Fixed importer crash when attempting to read a corrupt file. +- Reorganized code for CLI in preparation for adding pluggable subcommands. Also removed dependency on the aging ``cmdln`` module in favor of `a hand-rolled solution`_. diff --git a/docs/code_of_conduct.rst b/docs/code_of_conduct.rst index 76e57d0e6..772800d44 100644 --- a/docs/code_of_conduct.rst +++ b/docs/code_of_conduct.rst @@ -1,3 +1,4 @@ -.. code_of_conduct: +.. + code_of_conduct: .. include:: ../CODE_OF_CONDUCT.rst diff --git a/docs/contributing.rst b/docs/contributing.rst index 6af7deaef..6c71b2ce0 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -1,3 +1,4 @@ -.. contributing: +.. + contributing: .. include:: ../CONTRIBUTING.rst diff --git a/docs/dev/cli.rst b/docs/dev/cli.rst index 77d3af5a5..aab78d536 100644 --- a/docs/dev/cli.rst +++ b/docs/dev/cli.rst @@ -2,8 +2,7 @@ Providing a CLI =============== The ``beets.ui`` module houses interactions with the user via a terminal, the -:doc:`/reference/cli`. -The main function is called when the user types beet on the command line. -The CLI functionality is organized into commands, some of which are built-in -and some of which are provided by plugins. The built-in commands are all -implemented in the ``beets.ui.commands`` submodule. +:doc:`/reference/cli`. The main function is called when the user types beet on +the command line. The CLI functionality is organized into commands, some of +which are built-in and some of which are provided by plugins. The built-in +commands are all implemented in the ``beets.ui.commands`` submodule. diff --git a/docs/dev/importer.rst b/docs/dev/importer.rst index 5182c7134..2aca3b5fd 100644 --- a/docs/dev/importer.rst +++ b/docs/dev/importer.rst @@ -3,17 +3,17 @@ Music Importer The importer component is responsible for the user-centric workflow that adds music to a library. This is one of the first aspects that a user experiences -when using beets: it finds music in the filesystem, groups it into albums, -finds corresponding metadata in MusicBrainz, asks the user for intervention, -applies changes, and moves/copies files. A description of its user interface is -given in :doc:`/guides/tagger`. +when using beets: it finds music in the filesystem, groups it into albums, finds +corresponding metadata in MusicBrainz, asks the user for intervention, applies +changes, and moves/copies files. A description of its user interface is given in +:doc:`/guides/tagger`. -The workflow is implemented in the ``beets.importer`` module and is -distinct from the core logic for matching MusicBrainz metadata (in the -``beets.autotag`` module). The workflow is also decoupled from the command-line -interface with the hope that, eventually, other (graphical) interfaces can be -bolted onto the same importer implementation. +The workflow is implemented in the ``beets.importer`` module and is distinct +from the core logic for matching MusicBrainz metadata (in the ``beets.autotag`` +module). The workflow is also decoupled from the command-line interface with the +hope that, eventually, other (graphical) interfaces can be bolted onto the same +importer implementation. The importer is multithreaded and follows the pipeline pattern. Each pipeline -stage is a Python coroutine. The ``beets.util.pipeline`` module houses -a generic, reusable implementation of a multithreaded pipeline. +stage is a Python coroutine. The ``beets.util.pipeline`` module houses a +generic, reusable implementation of a multithreaded pipeline. diff --git a/docs/dev/index.rst b/docs/dev/index.rst index 10b3566c2..7f8af5276 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -4,10 +4,10 @@ For Developers This section contains information for developers. Read on if you're interested in hacking beets itself or creating plugins for it. -See also the documentation for `MediaFile`_, the library used by beets to read -and write metadata tags in media files. +See also the documentation for MediaFile_, the library used by beets to read and +write metadata tags in media files. -.. _MediaFile: https://mediafile.readthedocs.io/en/latest/ +.. _mediafile: https://mediafile.readthedocs.io/en/latest/ .. toctree:: :maxdepth: 1 @@ -17,11 +17,9 @@ and write metadata tags in media files. importer cli - .. toctree:: :maxdepth: 1 :caption: API Reference ../api/plugins ../api/database - diff --git a/docs/dev/library.rst b/docs/dev/library.rst index 8c47e4dc3..0f7554aac 100644 --- a/docs/dev/library.rst +++ b/docs/dev/library.rst @@ -4,8 +4,8 @@ Library Database API .. currentmodule:: beets.library This page describes the internal API of beets' core database features. It -doesn't exhaustively document the API, but is aimed at giving an overview of -the architecture to orient anyone who wants to dive into the code. +doesn't exhaustively document the API, but is aimed at giving an overview of the +architecture to orient anyone who wants to dive into the code. The :class:`Library` object is the central repository for data in beets. It represents a database containing songs, which are :class:`Item` instances, and @@ -16,26 +16,26 @@ The Library Class The :class:`Library` is typically instantiated as a singleton. A single invocation of beets usually has only one :class:`Library`. It's powered by -:class:`dbcore.Database` under the hood, which handles the `SQLite`_ -abstraction, something like a very minimal `ORM`_. The library is also -responsible for handling queries to retrieve stored objects. +:class:`dbcore.Database` under the hood, which handles the SQLite_ abstraction, +something like a very minimal ORM_. The library is also responsible for handling +queries to retrieve stored objects. -Overview -'''''''' +Overview +~~~~~~~~ -You can add new items or albums to the library via the -:py:meth:`Library.add` and :py:meth:`Library.add_album` methods. +You can add new items or albums to the library via the :py:meth:`Library.add` +and :py:meth:`Library.add_album` methods. You may also query the library for items and albums using the -:py:meth:`Library.items`, :py:meth:`Library.albums`, :py:meth:`Library.get_item` and :py:meth:`Library.get_album` methods. +:py:meth:`Library.items`, :py:meth:`Library.albums`, :py:meth:`Library.get_item` +and :py:meth:`Library.get_album` methods. -Any modifications to the library must go through a -:class:`Transaction` object, which you can get using the -:py:meth:`Library.transaction` context manager. +Any modifications to the library must go through a :class:`Transaction` object, +which you can get using the :py:meth:`Library.transaction` context manager. -.. _SQLite: https://sqlite.org/index.html -.. _ORM: https://en.wikipedia.org/wiki/Object-relational_mapping +.. _orm: https://en.wikipedia.org/wiki/Object-relational_mapping +.. _sqlite: https://sqlite.org/index.html Model Classes ------------- @@ -49,108 +49,103 @@ To get or change the metadata of a model (an item or album), either access its attributes (e.g., ``print(album.year)`` or ``album.year = 2012``) or use the ``dict``-like interface (e.g. ``item['artist']``). - Model base -'''''''''' +~~~~~~~~~~ -Models use dirty-flags to track when the object's metadata goes out of -sync with the database. The dirty dictionary maps field names to booleans -indicating whether the field has been written since the object was last -synchronized (via load or store) with the database. This logic is implemented -in the model base class :class:`LibModel` and is inherited by both -:class:`Item` and :class:`Album`. +Models use dirty-flags to track when the object's metadata goes out of sync with +the database. The dirty dictionary maps field names to booleans indicating +whether the field has been written since the object was last synchronized (via +load or store) with the database. This logic is implemented in the model base +class :class:`LibModel` and is inherited by both :class:`Item` and +:class:`Album`. We provide CRUD-like methods for interacting with the database: -* :py:meth:`LibModel.store` -* :py:meth:`LibModel.load` -* :py:meth:`LibModel.remove` -* :py:meth:`LibModel.add` +- :py:meth:`LibModel.store` +- :py:meth:`LibModel.load` +- :py:meth:`LibModel.remove` +- :py:meth:`LibModel.add` The base class :class:`beets.dbcore.Model` has a ``dict``-like interface, so normal the normal mapping API is supported: -* :py:meth:`LibModel.keys` -* :py:meth:`LibModel.update` -* :py:meth:`LibModel.items` -* :py:meth:`LibModel.get` - +- :py:meth:`LibModel.keys` +- :py:meth:`LibModel.update` +- :py:meth:`LibModel.items` +- :py:meth:`LibModel.get` Item -'''' +~~~~ Each :class:`Item` object represents a song or track. (We use the more generic term item because, one day, beets might support non-music media.) An item can -either be purely abstract, in which case it's just a bag of metadata fields, -or it can have an associated file (indicated by ``item.path``). +either be purely abstract, in which case it's just a bag of metadata fields, or +it can have an associated file (indicated by ``item.path``). In terms of the underlying SQLite database, items are backed by a single table called items with one column per metadata fields. The metadata fields currently in use are listed in ``library.py`` in ``Item._fields``. -To read and write a file's tags, we use the `MediaFile`_ library. -To make changes to either the database or the tags on a file, you -update an item's fields (e.g., ``item.title = "Let It Be"``) and then call -``item.write()``. +To read and write a file's tags, we use the MediaFile_ library. To make changes +to either the database or the tags on a file, you update an item's fields (e.g., +``item.title = "Let It Be"``) and then call ``item.write()``. -.. _MediaFile: https://mediafile.readthedocs.io/en/latest/ +.. _mediafile: https://mediafile.readthedocs.io/en/latest/ Items also track their modification times (mtimes) to help detect when they become out of sync with on-disk metadata, mainly to speed up the -:ref:`update-cmd` (which needs to check whether the database is in sync with -the filesystem). This feature turns out to be sort of complicated. +:ref:`update-cmd` (which needs to check whether the database is in sync with the +filesystem). This feature turns out to be sort of complicated. For any :class:`Item`, there are two mtimes: the on-disk mtime (maintained by the OS) and the database mtime (maintained by beets). Correspondingly, there is on-disk metadata (ID3 tags, for example) and DB metadata. The goal with the mtime is to ensure that the on-disk and DB mtimes match when the on-disk and DB -metadata are in sync; this lets beets do a quick mtime check and avoid -rereading files in some circumstances. +metadata are in sync; this lets beets do a quick mtime check and avoid rereading +files in some circumstances. Specifically, beets attempts to maintain the following invariant: - If the on-disk metadata differs from the DB metadata, then the on-disk - mtime must be greater than the DB mtime. + If the on-disk metadata differs from the DB metadata, then the on-disk mtime + must be greater than the DB mtime. As a result, it is always valid for the DB mtime to be zero (assuming that real -disk mtimes are always positive). However, whenever possible, beets tries to -set ``db_mtime = disk_mtime`` at points where it knows the metadata is -synchronized. When it is possible that the metadata is out of sync, beets can -then just set ``db_mtime = 0`` to return to a consistent state. +disk mtimes are always positive). However, whenever possible, beets tries to set +``db_mtime = disk_mtime`` at points where it knows the metadata is synchronized. +When it is possible that the metadata is out of sync, beets can then just set +``db_mtime = 0`` to return to a consistent state. This leads to the following implementation policy: - * On every write of disk metadata (``Item.write()``), the DB mtime is updated - to match the post-write disk mtime. - * Same for metadata reads (``Item.read()``). - * On every modification to DB metadata (``item.field = ...``), the DB mtime - is reset to zero. + - On every write of disk metadata (``Item.write()``), the DB mtime is + updated to match the post-write disk mtime. + - Same for metadata reads (``Item.read()``). + - On every modification to DB metadata (``item.field = ...``), the DB mtime + is reset to zero. Album -''''' +~~~~~ An :class:`Album` is a collection of Items in the database. Every item in the database has either zero or one associated albums (accessible via -``item.album_id``). An item that has no associated album is called a -singleton. +``item.album_id``). An item that has no associated album is called a singleton. Changing fields on an album (e.g. ``album.year = 2012``) updates the album itself and also changes the same field in all associated items. An :class:`Album` object keeps track of album-level metadata, which is (mostly) -a subset of the track-level metadata. The album-level metadata fields are -listed in ``Album._fields``. -For those fields that are both item-level and album-level (e.g., ``year`` or -``albumartist``), every item in an album should share the same value. Albums -use an SQLite table called ``albums``, in which each column is an album -metadata field. - +a subset of the track-level metadata. The album-level metadata fields are listed +in ``Album._fields``. For those fields that are both item-level and album-level +(e.g., ``year`` or ``albumartist``), every item in an album should share the +same value. Albums use an SQLite table called ``albums``, in which each column +is an album metadata field. .. note:: + The :py:meth:`Album.items` method is not inherited from :py:meth:`LibModel.items` for historical reasons. Transactions -'''''''''''' +~~~~~~~~~~~~ The :class:`Library` class provides the basic methods necessary to access and manipulate its contents. To perform more complicated operations atomically, or @@ -167,10 +162,9 @@ to interact directly with the underlying SQLite database, you must use a .. currentmodule:: beets.dbcore.db The :class:`Transaction` class is a context manager that provides a -transactional interface to the underlying SQLite database. It is -responsible for managing the transaction's lifecycle, including -beginning, committing, and rolling back the transaction if -an error occurs. +transactional interface to the underlying SQLite database. It is responsible for +managing the transaction's lifecycle, including beginning, committing, and +rolling back the transaction if an error occurs. .. _blog post: https://beets.io/blog/sqlite-nightmare.html @@ -179,11 +173,11 @@ Queries .. currentmodule:: beets.dbcore.query -To access albums and items in a library, we use :doc:`/reference/query`. -In beets, the :class:`Query` abstract base class represents a criterion that -matches items or albums in the database. -Every subclass of :class:`Query` must implement two methods, which implement -two different ways of identifying matching items/albums. +To access albums and items in a library, we use :doc:`/reference/query`. In +beets, the :class:`Query` abstract base class represents a criterion that +matches items or albums in the database. Every subclass of :class:`Query` must +implement two methods, which implement two different ways of identifying +matching items/albums. The ``clause()`` method should return an SQLite ``WHERE`` clause that matches appropriate albums/items. This allows for efficient batch queries. @@ -194,12 +188,10 @@ items that have already been fetched from the database match the query. There are many different types of queries. Just as an example, :class:`FieldQuery` determines whether a certain field matches a certain value -(an equality query). -:class:`AndQuery` (like its abstract superclass, :class:`CollectionQuery`) -takes a set of other query objects and bundles them together, matching only -albums/items that match all constituent queries. +(an equality query). :class:`AndQuery` (like its abstract superclass, +:class:`CollectionQuery`) takes a set of other query objects and bundles them +together, matching only albums/items that match all constituent queries. Beets has a human-writable plain-text query syntax that can be parsed into -:class:`Query` objects. Calling ``AndQuery.from_strings`` parses a list of -query parts into a query object that can then be used with :class:`Library` -objects. +:class:`Query` objects. Calling ``AndQuery.from_strings`` parses a list of query +parts into a query object that can then be used with :class:`Library` objects. diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst index c24a94093..620e1caec 100644 --- a/docs/dev/plugins.rst +++ b/docs/dev/plugins.rst @@ -5,7 +5,6 @@ Beets plugins are Python modules or packages that extend the core functionality of beets. The plugin system is designed to be flexible, allowing developers to add virtually any type of features. - .. _writing-plugins: Writing Plugins @@ -14,12 +13,16 @@ Writing Plugins 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:: +create a directory called ``beetsplug`` and add either your plugin module: + +:: beetsplug/ myawesomeplugin.py -or your plugin subpackage:: +or your plugin subpackage: + +:: beetsplug/ myawesomeplugin/ @@ -30,8 +33,12 @@ or your plugin subpackage:: 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. + thus we do not depend on ``pkgutil``-based setup in the ``__init__.py`` file + anymore. + +.. _this article: https://realpython.com/python-namespace-package/#setting-up-some-namespace-packages + +.. _this stack overflow question: https://stackoverflow.com/a/27586272/9582674 The meat of your plugin goes in ``myawesomeplugin.py``. There, you'll have to import ``BeetsPlugin`` from ``beets.plugins`` and subclass it, for example @@ -40,16 +47,21 @@ import ``BeetsPlugin`` from ``beets.plugins`` and subclass it, for example from beets.plugins import 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, package your plugin (see how to do this with `poetry`_ -or `setuptools`_, for example) and install it into your ``beets`` virtual +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 +.. _poetry: https://python-poetry.org/docs/pyproject/#packages + +.. _setuptools: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#finding-simple-packages + .. code-block:: yaml # config.yaml @@ -58,27 +70,24 @@ environment. Then, add your plugin to beets configuration 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: Add Commands to the CLI -^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~ Plugins can add new subcommands to the ``beet`` command-line interface. Define the plugin class' ``commands()`` method to return a list of ``Subcommand`` objects. (The ``Subcommand`` class is defined in the ``beets.ui`` module.) -Here's an example plugin that adds a simple command:: +Here's an example plugin that adds a simple command: + +:: from beets.plugins import BeetsPlugin from beets.ui import Subcommand my_super_command = Subcommand('super', help='do something super') def say_hi(lib, opts, args): - print "Hello everybody! I'm a plugin!" + print("Hello everybody! I'm a plugin!") my_super_command.func = say_hi class SuperPlug(BeetsPlugin): @@ -92,15 +101,14 @@ but it defaults to an empty parser (you can extend it later). ``help`` is a description of your command, and ``aliases`` is a list of shorthand versions of your command name. -.. _OptionParser instance: https://docs.python.org/library/optparse.html +.. _optionparser instance: https://docs.python.org/library/optparse.html You'll need to add a function to your command by saying ``mycommand.func = myfunction``. This function should take the following parameters: ``lib`` (a beets ``Library`` object) and ``opts`` and ``args`` (command-line options and -arguments as returned by `OptionParser.parse_args`_). +arguments as returned by OptionParser.parse_args_). -.. _OptionParser.parse_args: - https://docs.python.org/library/optparse.html#parsing-arguments +.. _optionparser.parse_args: https://docs.python.org/library/optparse.html#parsing-arguments The function should use any of the utility functions defined in ``beets.ui``. Try running ``pydoc beets.ui`` to see what's available. @@ -115,15 +123,17 @@ and ``--format``. This feature is versatile and extensively documented, try .. _plugin_events: Listen for Events -^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~ Event handlers allow plugins to run code whenever something happens in beets' operation. For instance, a plugin could write a log message every time an album is successfully autotagged or update MPD's index whenever the database is changed. -You can "listen" for events using ``BeetsPlugin.register_listener``. Here's -an example:: +You can "listen" for events using ``BeetsPlugin.register_listener``. Here's an +example: + +:: from beets.plugins import BeetsPlugin @@ -137,7 +147,9 @@ an example:: Note that if you want to access an attribute of your plugin (e.g. ``config`` or ``log``) you'll have to define a method and not a function. Here is the usual -registration process in this case:: +registration process in this case: + +:: from beets.plugins import BeetsPlugin @@ -151,139 +163,108 @@ registration process in this case:: The events currently available are: -* `pluginload`: called after all the plugins have been loaded after the ``beet`` - command starts - -* `import`: called after a ``beet import`` command finishes (the ``lib`` keyword - argument is a Library object; ``paths`` is a list of paths (strings) that were - imported) - -* `album_imported`: called with an ``Album`` object every time the ``import`` +- ``pluginload``: called after all the plugins have been loaded after the + ``beet`` command starts +- ``import``: called after a ``beet import`` command finishes (the ``lib`` + keyword argument is a Library object; ``paths`` is a list of paths (strings) + that were imported) +- ``album_imported``: called with an ``Album`` object every time the ``import`` command finishes adding an album to the library. Parameters: ``lib``, ``album`` - -* `album_removed`: called with an ``Album`` object every time an album is +- ``album_removed``: called with an ``Album`` object every time an album is removed from the library (even when its file is not deleted from disk). - -* `item_copied`: called with an ``Item`` object whenever its file is copied. +- ``item_copied``: called with an ``Item`` object whenever its file is copied. Parameters: ``item``, ``source`` path, ``destination`` path - -* `item_imported`: called with an ``Item`` object every time the importer adds a - singleton to the library (not called for full-album imports). Parameters: +- ``item_imported``: called with an ``Item`` object every time the importer adds + a singleton to the library (not called for full-album imports). Parameters: ``lib``, ``item`` - -* `before_item_moved`: called with an ``Item`` object immediately before its +- ``before_item_moved``: called with an ``Item`` object immediately before its file is moved. Parameters: ``item``, ``source`` path, ``destination`` path - -* `item_moved`: called with an ``Item`` object whenever its file is moved. +- ``item_moved``: called with an ``Item`` object whenever its file is moved. Parameters: ``item``, ``source`` path, ``destination`` path - -* `item_linked`: called with an ``Item`` object whenever a symlink is created - for a file. - Parameters: ``item``, ``source`` path, ``destination`` path - -* `item_hardlinked`: called with an ``Item`` object whenever a hardlink is - created for a file. - Parameters: ``item``, ``source`` path, ``destination`` path - -* `item_reflinked`: called with an ``Item`` object whenever a reflink is - created for a file. - Parameters: ``item``, ``source`` path, ``destination`` path - -* `item_removed`: called with an ``Item`` object every time an item (singleton +- ``item_linked``: called with an ``Item`` object whenever a symlink is created + for a file. Parameters: ``item``, ``source`` path, ``destination`` path +- ``item_hardlinked``: called with an ``Item`` object whenever a hardlink is + created for a file. Parameters: ``item``, ``source`` path, ``destination`` + path +- ``item_reflinked``: called with an ``Item`` object whenever a reflink is + created for a file. Parameters: ``item``, ``source`` path, ``destination`` + path +- ``item_removed``: called with an ``Item`` object every time an item (singleton or album's part) is removed from the library (even when its file is not deleted from disk). - -* `write`: called with an ``Item`` object, a ``path``, and a ``tags`` - dictionary just before a file's metadata is written to disk (i.e., - just before the file on disk is opened). Event handlers may change - the ``tags`` dictionary to customize the tags that are written to the - media file. Event handlers may also raise a - ``library.FileOperationError`` exception to abort the write - operation. Beets will catch that exception, print an error message - and continue. - -* `after_write`: called with an ``Item`` object after a file's metadata is +- ``write``: called with an ``Item`` object, a ``path``, and a ``tags`` + dictionary just before a file's metadata is written to disk (i.e., just before + the file on disk is opened). Event handlers may change the ``tags`` dictionary + to customize the tags that are written to the media file. Event handlers may + also raise a ``library.FileOperationError`` exception to abort the write + operation. Beets will catch that exception, print an error message and + continue. +- ``after_write``: called with an ``Item`` object after a file's metadata is written to disk (i.e., just after the file on disk is closed). - -* `import_task_created`: called immediately after an import task is +- ``import_task_created``: called immediately after an import task is initialized. Plugins can use this to, for example, change imported files of a - task before anything else happens. It's also possible to replace the task - with another task by returning a list of tasks. This list can contain zero - or more `ImportTask`s. Returning an empty list will stop the task. - Parameters: ``task`` (an `ImportTask`) and ``session`` (an `ImportSession`). - -* `import_task_start`: called when before an import task begins processing. + task before anything else happens. It's also possible to replace the task with + another task by returning a list of tasks. This list can contain zero or more + ``ImportTask``. Returning an empty list will stop the task. Parameters: + ``task`` (an ``ImportTask``) and ``session`` (an ``ImportSession``). +- ``import_task_start``: called when before an import task begins processing. Parameters: ``task`` and ``session``. - -* `import_task_apply`: called after metadata changes have been applied in an +- ``import_task_apply``: called after metadata changes have been applied in an import task. This is called on the same thread as the UI, so use this sparingly and only for tasks that can be done quickly. For most plugins, an import pipeline stage is a better choice (see :ref:`plugin-stage`). Parameters: ``task`` and ``session``. - -* `import_task_before_choice`: called after candidate search for an import task - before any decision is made about how/if to import or tag. Can be used to +- ``import_task_before_choice``: called after candidate search for an import + task before any decision is made about how/if to import or tag. Can be used to present information about the task or initiate interaction with the user before importing occurs. Return an importer action to take a specific action. - Only one handler may return a non-None result. - Parameters: ``task`` and ``session`` - -* `import_task_choice`: called after a decision has been made about an import + Only one handler may return a non-None result. Parameters: ``task`` and + ``session`` +- ``import_task_choice``: called after a decision has been made about an import task. This event can be used to initiate further interaction with the user. - Use ``task.choice_flag`` to determine or change the action to be - taken. Parameters: ``task`` and ``session``. - -* `import_task_files`: called after an import task finishes manipulating the + Use ``task.choice_flag`` to determine or change the action to be taken. + Parameters: ``task`` and ``session``. +- ``import_task_files``: called after an import task finishes manipulating the filesystem (copying and moving files, writing metadata tags). Parameters: ``task`` and ``session``. - -* `library_opened`: called after beets starts up and initializes the main +- ``library_opened``: called after beets starts up and initializes the main Library object. Parameter: ``lib``. - -* `database_change`: a modification has been made to the library database. The +- ``database_change``: a modification has been made to the library database. The change might not be committed yet. Parameters: ``lib`` and ``model``. - -* `cli_exit`: called just before the ``beet`` command-line program exits. +- ``cli_exit``: called just before the ``beet`` command-line program exits. Parameter: ``lib``. - -* `import_begin`: called just before a ``beet import`` session starts up. +- ``import_begin``: called just before a ``beet import`` session starts up. Parameter: ``session``. - -* `trackinfo_received`: called after metadata for a track item has been - fetched from a data source, such as MusicBrainz. You can modify the tags - that the rest of the pipeline sees on a ``beet import`` operation or during - later adjustments, such as ``mbsync``. Slow handlers of the event can impact - the operation, since the event is fired for any fetched possible match - `before` the user (or the autotagger machinery) gets to see the match. - Parameter: ``info``. - -* `albuminfo_received`: like `trackinfo_received`, the event indicates new - metadata for album items. The parameter is an ``AlbumInfo`` object instead - of a ``TrackInfo``. - Parameter: ``info``. - -* `before_choose_candidate`: called before the user is prompted for a decision +- ``trackinfo_received``: called after metadata for a track item has been + fetched from a data source, such as MusicBrainz. You can modify the tags that + the rest of the pipeline sees on a ``beet import`` operation or during later + adjustments, such as ``mbsync``. Slow handlers of the event can impact the + operation, since the event is fired for any fetched possible match ``before`` + the user (or the autotagger machinery) gets to see the match. Parameter: + ``info``. +- ``albuminfo_received``: like ``trackinfo_received``, the event indicates new + metadata for album items. The parameter is an ``AlbumInfo`` object instead of + a ``TrackInfo``. Parameter: ``info``. +- ``before_choose_candidate``: called before the user is prompted for a decision 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. +- ``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. Parameter: ``data`` +- ``mb_album_extract``: Like ``mb_track_extract``, but for album tags. + Overwrites tags set at the track level, if they have the same ``field``. Parameter: ``data`` -* `mb_album_extract`: Like `mb_track_extract`, but for album tags. Overwrites - tags set at the track level, if they have the same ``field``. - Parameter: ``data`` - -The included ``mpdupdate`` plugin provides an example use case for event listeners. +The included ``mpdupdate`` plugin provides an example use case for event +listeners. Extend the Autotagger -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ Plugins can also enhance the functionality of the autotagger. For a comprehensive example, try looking at the ``chroma`` plugin, which is included @@ -296,79 +277,79 @@ levels; the initial search controls which candidates are presented to the matching algorithm. Plugins implement these extensions by implementing four methods on the plugin class: -* ``track_distance(self, item, info)``: adds a component to the distance +- ``track_distance(self, item, info)``: adds a component to the distance function (i.e., the similarity metric) for individual tracks. ``item`` is the - track to be matched (an Item object) and ``info`` is the TrackInfo object - that is proposed as a match. Should return a ``(dist, dist_max)`` pair - of floats indicating the distance. - -* ``album_distance(self, items, album_info, mapping)``: like the above, but + track to be matched (an Item object) and ``info`` is the TrackInfo object that + is proposed as a match. Should return a ``(dist, dist_max)`` pair of floats + indicating the distance. +- ``album_distance(self, items, album_info, mapping)``: like the above, but compares a list of items (representing an album) to an album-level MusicBrainz entry. ``items`` is a list of Item objects; ``album_info`` is an AlbumInfo object; and ``mapping`` is a dictionary that maps Items to their corresponding TrackInfo objects. - -* ``candidates(self, items, artist, album, va_likely)``: given a list of items +- ``candidates(self, items, artist, album, va_likely)``: given a list of items comprised by an album to be matched, return a list of ``AlbumInfo`` objects for candidate albums to be compared and matched. - -* ``item_candidates(self, item, artist, album)``: given a *singleton* item, +- ``item_candidates(self, item, artist, album)``: given a *singleton* item, return a list of ``TrackInfo`` objects for candidate tracks to be compared and matched. - -* ``album_for_id(self, album_id)``: given an ID from user input or an album's +- ``album_for_id(self, album_id)``: given an ID from user input or an album's tags, return a candidate AlbumInfo object (or None). - -* ``track_for_id(self, track_id)``: given an ID from user input or a file's +- ``track_for_id(self, track_id)``: given an ID from user input or a file's tags, return a candidate TrackInfo object (or None). When implementing these functions, you may want to use the functions from the -``beets.autotag`` and ``beets.autotag.mb`` modules, both of which have -somewhat helpful docstrings. +``beets.autotag`` and ``beets.autotag.mb`` modules, both of which have somewhat +helpful docstrings. Read Configuration Options -^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~ Plugins can configure themselves using the ``config.yaml`` file. You can read -configuration values in two ways. The first is to use `self.config` within +configuration values in two ways. The first is to use ``self.config`` within your plugin class. This gives you a view onto the configuration values in a section with the same name as your plugin's module. For example, if your plugin -is in ``greatplugin.py``, then `self.config` will refer to options under the +is in ``greatplugin.py``, then ``self.config`` will refer to options under the ``greatplugin:`` section of the config file. For example, if you have a configuration value called "foo", then users can put -this in their ``config.yaml``:: +this in their ``config.yaml``: + +:: greatplugin: foo: bar To access this value, say ``self.config['foo'].get()`` at any point in your -plugin's code. The `self.config` object is a *view* as defined by the `Confuse`_ +plugin's code. The ``self.config`` object is a *view* as defined by the Confuse_ library. -.. _Confuse: https://confuse.readthedocs.io/en/latest/ +.. _confuse: https://confuse.readthedocs.io/en/latest/ If you want to access configuration values *outside* of your plugin's section, -import the `config` object from the `beets` module. That is, just put ``from +import the ``config`` object from the ``beets`` module. That is, just put ``from beets import config`` at the top of your plugin and access values from there. If your plugin provides configuration values for sensitive data (e.g., passwords, API keys, ...), you should add these to the config so they can be -redacted automatically when users dump their config. This can be done by -setting each value's `redact` flag, like so:: +redacted automatically when users dump their config. This can be done by setting +each value's ``redact`` flag, like so: + +:: self.config['password'].redact = True - Add Path Format Functions and Fields -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Beets supports *function calls* in its path format syntax (see :doc:`/reference/pathformat`). Beets includes a few built-in functions, but plugins can register new functions by adding them to the ``template_funcs`` dictionary. -Here's an example:: +Here's an example: + +:: class MyPlugin(BeetsPlugin): def __init__(self): @@ -385,10 +366,12 @@ This plugin provides a function ``%initial`` to path templates where ``%initial{$artist}`` expands to the artist's initial (its capitalized first character). -Plugins can also add template *fields*, which are computed values referenced -as ``$name`` in templates. To add a new field, add a function that takes an +Plugins can also add template *fields*, which are computed values referenced as +``$name`` in templates. To add a new field, add a function that takes an ``Item`` object to the ``template_fields`` dictionary on the plugin object. -Here's an example that adds a ``$disc_and_track`` field:: +Here's an example that adds a ``$disc_and_track`` field: + +:: class MyPlugin(BeetsPlugin): def __init__(self): @@ -413,20 +396,21 @@ template fields by adding a function accepting an ``Album`` argument to the ``album_template_fields`` dict. Extend MediaFile -^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~ -`MediaFile`_ is the file tag abstraction layer that beets uses to make +MediaFile_ is the file tag abstraction layer that beets uses to make cross-format metadata manipulation simple. Plugins can add fields to MediaFile to extend the kinds of metadata that they can easily manage. -The ``MediaFile`` class uses ``MediaField`` descriptors to provide -access to file tags. If you have created a descriptor you can add it through -your plugins :py:meth:`beets.plugins.BeetsPlugin.add_media_field()`` method. +The ``MediaFile`` class uses ``MediaField`` descriptors to provide access to +file tags. If you have created a descriptor you can add it through your plugins +:py:meth:`beets.plugins.BeetsPlugin.add_media_field()` method. -.. _MediaFile: https://mediafile.readthedocs.io/en/latest/ +.. _mediafile: https://mediafile.readthedocs.io/en/latest/ +Here's an example plugin that provides a meaningless new field "foo": -Here's an example plugin that provides a meaningless new field "foo":: +:: class FooPlugin(BeetsPlugin): def __init__(self): @@ -444,11 +428,10 @@ Here's an example plugin that provides a meaningless new field "foo":: item.write() # The "foo" tag of the file is now "ham" - .. _plugin-stage: Add Import Pipeline Stages -^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~ Many plugins need to add high-latency operations to the import workflow. For example, a plugin that fetches lyrics from the Web would, ideally, not block the @@ -462,9 +445,11 @@ Multiple stages run in parallel but each stage processes only one task at a time and each task is processed by only one stage at a time. Plugins provide stages as functions that take two arguments: ``config`` and -``task``, which are ``ImportSession`` and ``ImportTask`` objects (both defined in -``beets.importer``). Add such a function to the plugin's ``import_stages`` field -to register it:: +``task``, which are ``ImportSession`` and ``ImportTask`` objects (both defined +in ``beets.importer``). Add such a function to the plugin's ``import_stages`` +field to register it: + +:: from beets.plugins import BeetsPlugin class ExamplePlugin(BeetsPlugin): @@ -475,14 +460,16 @@ to register it:: print('Importing something!') It is also possible to request your function to run early in the pipeline by -adding the function to the plugin's ``early_import_stages`` field instead:: +adding the function to the plugin's ``early_import_stages`` field instead: + +:: self.early_import_stages = [self.stage] .. _extend-query: Extend the Query Syntax -^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~ You can add new kinds of queries to beets' :doc:`query syntax </reference/query>`. There are two ways to add custom queries: using a prefix @@ -491,24 +478,25 @@ named queries are not associated with any field. For example, beets already supports regular expression queries, which are indicated by a colon prefix---plugins can do the same. -For either kind of query extension, define a subclass of the ``Query`` type -from the ``beets.dbcore.query`` module. Then: +For either kind of query extension, define a subclass of the ``Query`` type from +the ``beets.dbcore.query`` module. Then: - To define a prefix-based query, define a ``queries`` method in your plugin class. Return from this method a dictionary mapping prefix strings to query classes. -- To define a named query, defined dictionaries named either ``item_queries`` - or ``album_queries``. These should map names to query types. So if you - use ``{ "foo": FooQuery }``, then the query ``foo:bar`` will construct a - query like ``FooQuery("bar")``. +- To define a named query, defined dictionaries named either ``item_queries`` or + ``album_queries``. These should map names to query types. So if you use ``{ + "foo": FooQuery }``, then the query ``foo:bar`` will construct a query like + ``FooQuery("bar")``. For prefix-based queries, you will want to extend ``FieldQuery``, which -implements string comparisons on fields. To use it, create a subclass -inheriting from that class and override the ``value_match`` class method. -(Remember the ``@classmethod`` decorator!) The following example plugin -declares a query using the ``@`` prefix to delimit exact string matches. The -plugin will be used if we issue a command like ``beet ls @something`` or -``beet ls artist:@something``:: +implements string comparisons on fields. To use it, create a subclass inheriting +from that class and override the ``value_match`` class method. (Remember the +``@classmethod`` decorator!) The following example plugin declares a query using +the ``@`` prefix to delimit exact string matches. The plugin will be used if we +issue a command like ``beet ls @something`` or ``beet ls artist:@something``: + +:: from beets.plugins import BeetsPlugin from beets.dbcore import FieldQuery @@ -524,14 +512,14 @@ plugin will be used if we issue a command like ``beet ls @something`` or '@': ExactMatchQuery } - Flexible Field Types -^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~ -If your plugin uses flexible fields to store numbers or other -non-string values, you can specify the types of those fields. A rating -plugin, for example, might want to declare that the ``rating`` field -should have an integer type:: +If your plugin uses flexible fields to store numbers or other non-string values, +you can specify the types of those fields. A rating plugin, for example, might +want to declare that the ``rating`` field should have an integer type: + +:: from beets.plugins import BeetsPlugin from beets.dbcore import types @@ -543,73 +531,74 @@ should have an integer type:: def album_types(self): return {'rating': types.INTEGER} -A plugin may define two attributes: `item_types` and `album_types`. -Each of those attributes is a dictionary mapping a flexible field name -to a type instance. You can find the built-in types in the -`beets.dbcore.types` and `beets.library` modules or implement your own -type by inheriting from the `Type` class. +A plugin may define two attributes: ``item_types`` and ``album_types``. Each of +those attributes is a dictionary mapping a flexible field name to a type +instance. You can find the built-in types in the ``beets.dbcore.types`` and +``beets.library`` modules or implement your own type by inheriting from the +``Type`` class. Specifying types has several advantages: -* Code that accesses the field like ``item['my_field']`` gets the right - type (instead of just a string). - -* You can use advanced queries (like :ref:`ranges <numericquery>`) - from the command line. - -* User input for flexible fields may be validated and converted. - -* Items missing the given field can use an appropriate null value for - querying and sorting purposes. - +- Code that accesses the field like ``item['my_field']`` gets the right type + (instead of just a string). +- You can use advanced queries (like :ref:`ranges <numericquery>`) from the + command line. +- User input for flexible fields may be validated and converted. +- Items missing the given field can use an appropriate null value for querying + and sorting purposes. .. _plugin-logging: Logging -^^^^^^^ +~~~~~~~ Each plugin object has a ``_log`` attribute, which is a ``Logger`` from the `standard Python logging module`_. The logger is set up to `PEP 3101`_, -str.format-style string formatting. So you can write logging calls like this:: +str.format-style string formatting. So you can write logging calls like this: + +:: self._log.debug(u'Processing {0.title} by {0.artist}', item) -.. _PEP 3101: https://www.python.org/dev/peps/pep-3101/ -.. _standard Python logging module: https://docs.python.org/2/library/logging.html +.. _pep 3101: https://www.python.org/dev/peps/pep-3101/ -When beets is in verbose mode, plugin messages are prefixed with the plugin -name to make them easier to see. +.. _standard python logging module: https://docs.python.org/2/library/logging.html + +When beets is in verbose mode, plugin messages are prefixed with the plugin name +to make them easier to see. Which messages will be logged depends on the logging level and the action performed: -* Inside import stages and event handlers, the default is ``WARNING`` messages +- Inside import stages and event handlers, the default is ``WARNING`` messages and above. -* Everywhere else, the default is ``INFO`` or above. +- Everywhere else, the default is ``INFO`` or above. The verbosity can be increased with ``--verbose`` (``-v``) flags: each flags lowers the level by a notch. That means that, with a single ``-v`` flag, event handlers won't have their ``DEBUG`` messages displayed, but command functions -(for example) will. With ``-vv`` on the command line, ``DEBUG`` messages will -be displayed everywhere. +(for example) will. With ``-vv`` on the command line, ``DEBUG`` messages will be +displayed everywhere. This addresses a common pattern where plugins need to use the same code for a command and an import stage, but the command needs to print more messages than the import stage. (For example, you'll want to log "found lyrics for this song" -when you're run explicitly as a command, but you don't want to noisily -interrupt the importer interface when running automatically.) +when you're run explicitly as a command, but you don't want to noisily interrupt +the importer interface when running automatically.) .. _append_prompt_choices: Append Prompt Choices -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ -Plugins can also append choices to the prompt presented to the user during -an import session. +Plugins can also append choices to the prompt presented to the user during an +import session. -To do so, add a listener for the ``before_choose_candidate`` event, and return -a list of ``PromptChoices`` that represent the additional choices that your -plugin shall expose to the user:: +To do so, add a listener for the ``before_choose_candidate`` event, and return a +list of ``PromptChoices`` that represent the additional choices that your plugin +shall expose to the user: + +:: from beets.plugins import BeetsPlugin from beets.ui.commands import PromptChoice @@ -630,31 +619,35 @@ plugin shall expose to the user:: def bar(self, session, task): print('User has chosen "Do bar"!') -The previous example modifies the standard prompt:: +The previous example modifies the standard prompt: + +:: # selection (default 1), Skip, Use as-is, as Tracks, Group albums, Enter search, enter Id, aBort? -by appending two additional options (``Print foo`` and ``Do bar``):: +by appending two additional options (``Print foo`` and ``Do bar``): + +:: # selection (default 1), Skip, Use as-is, as Tracks, Group albums, Enter search, enter Id, aBort, Print foo, Do bar? If the user selects a choice, the ``callback`` attribute of the corresponding -``PromptChoice`` will be called. It is the responsibility of the plugin to -check for the status of the import session and decide the choices to be -appended: for example, if a particular choice should only be presented if the -album has no candidates, the relevant checks against ``task.candidates`` should -be performed inside the plugin's ``before_choose_candidate_event`` accordingly. +``PromptChoice`` will be called. It is the responsibility of the plugin to check +for the status of the import session and decide the choices to be appended: for +example, if a particular choice should only be presented if the album has no +candidates, the relevant checks against ``task.candidates`` should be performed +inside the plugin's ``before_choose_candidate_event`` accordingly. Please make sure that the short letter for each of the choices provided by the -plugin is not already in use: the importer will emit a warning and discard -all but one of the choices using the same letter, giving priority to the -core importer prompt choices. As a reference, the following characters are used -by the choices on the core importer prompt, and hence should not be used: -``a``, ``s``, ``u``, ``t``, ``g``, ``e``, ``i``, ``b``. +plugin is not already in use: the importer will emit a warning and discard all +but one of the choices using the same letter, giving priority to the core +importer prompt choices. As a reference, the following characters are used by +the choices on the core importer prompt, and hence should not be used: ``a``, +``s``, ``u``, ``t``, ``g``, ``e``, ``i``, ``b``. -Additionally, the callback function can optionally specify the next action to -be performed by returning a ``importer.Action`` value. It may also return a +Additionally, the callback function can optionally specify the next action to be +performed by returning a ``importer.Action`` value. It may also return a ``autotag.Proposal`` value to update the set of current proposals to be considered. diff --git a/docs/faq.rst b/docs/faq.rst index ac7818ab2..718356e42 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -1,57 +1,56 @@ FAQ -### +=== -Here are some answers to frequently-asked questions from IRC and elsewhere. -Got a question that isn't answered here? Try the `discussion board`_, or +Here are some answers to frequently-asked questions from IRC and elsewhere. Got +a question that isn't answered here? Try the `discussion board`_, or :ref:`filing an issue <bugs>` in the bug tracker. -.. _mailing list: https://groups.google.com/group/beets-users .. _discussion board: https://github.com/beetbox/beets/discussions/ +.. _mailing list: https://groups.google.com/group/beets-users + .. contents:: :local: :depth: 2 - How do I… -========= - +--------- .. _move: …rename my files according to a new path format configuration? --------------------------------------------------------------- - -Just run the :ref:`move-cmd` command. Use a :doc:`query </reference/query>` -to rename a subset of your music or leave the query off to rename -everything. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Just run the :ref:`move-cmd` command. Use a :doc:`query </reference/query>` to +rename a subset of your music or leave the query off to rename everything. .. _asispostfacto: …find all the albums I imported "as-is"? ----------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Enable the :ref:`import log <import_log>` -to automatically record whenever you skip an album or accept one -"as-is". +Enable the :ref:`import log <import_log>` to automatically record whenever you +skip an album or accept one "as-is". -Alternatively, you can find all the albums in your library that are -missing MBIDs using a command like this:: +Alternatively, you can find all the albums in your library that are missing +MBIDs using a command like this: + +:: beet ls -a mb_albumid::^$ -Assuming your files didn't have MBIDs already, then this will roughly -correspond to those albums that didn't get autotagged. - +Assuming your files didn't have MBIDs already, then this will roughly correspond +to those albums that didn't get autotagged. .. _discdir: …create "Disc N" directories for multi-disc albums? ---------------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use the :doc:`/plugins/inline` along -with the ``%if{}`` function to accomplish this:: +Use the :doc:`/plugins/inline` along with the ``%if{}`` function to accomplish +this: + +:: plugins: inline paths: @@ -59,344 +58,313 @@ with the ``%if{}`` function to accomplish this:: item_fields: multidisc: 1 if disctotal > 1 else 0 -This ``paths`` configuration only contains the -``default`` key: it leaves the ``comp`` and ``singleton`` keys as their -default values, as documented in :ref:`path-format-config`. -To create "Disc N" directories for compilations and singletons, you will need -to specify similar templates for those keys as well. - +This ``paths`` configuration only contains the ``default`` key: it leaves the +``comp`` and ``singleton`` keys as their default values, as documented in +:ref:`path-format-config`. To create "Disc N" directories for compilations and +singletons, you will need to specify similar templates for those keys as well. .. _multidisc: …import a multi-disc album? ---------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -As of 1.0b11, beets tags multi-disc albums as a *single unit*. To get a -good match, it needs to treat all of the album's parts together as a -single release. +As of 1.0b11, beets tags multi-disc albums as a *single unit*. To get a good +match, it needs to treat all of the album's parts together as a single release. To help with this, the importer uses a simple heuristic to guess when a -directory represents a multi-disc album that's been divided into -multiple subdirectories. When it finds a situation like this, it -collapses all of the items in the subdirectories into a single release -for tagging. +directory represents a multi-disc album that's been divided into multiple +subdirectories. When it finds a situation like this, it collapses all of the +items in the subdirectories into a single release for tagging. The heuristic works by looking at the names of directories. If multiple -subdirectories of a common parent directory follow the pattern "(title) -disc (number) (...)" and the *prefix* (everything up to the number) is -the same, the directories are collapsed together. One of the key words -"disc" or "CD" must be present to make this work. - -If you have trouble tagging a multi-disc album, consider the ``--flat`` -flag (which treats a whole tree as a single album) or just putting all -the tracks into a single directory to force them to be tagged together. +subdirectories of a common parent directory follow the pattern "(title) disc +(number) (...)" and the *prefix* (everything up to the number) is the same, the +directories are collapsed together. One of the key words "disc" or "CD" must be +present to make this work. +If you have trouble tagging a multi-disc album, consider the ``--flat`` flag +(which treats a whole tree as a single album) or just putting all the tracks +into a single directory to force them to be tagged together. .. _mbid: …enter a MusicBrainz ID? ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ An MBID looks like one of these: -- ``https://musicbrainz.org/release/ded77dcf-7279-457e-955d-625bd3801b87`` -- ``d569deba-8c6b-4d08-8c43-d0e5a1b8c7f3`` +- ``https://musicbrainz.org/release/ded77dcf-7279-457e-955d-625bd3801b87`` +- ``d569deba-8c6b-4d08-8c43-d0e5a1b8c7f3`` -Beets can recognize either the hex-with-dashes UUID-style string or the -full URL that contains it (as of 1.0b11). +Beets can recognize either the hex-with-dashes UUID-style string or the full URL +that contains it (as of 1.0b11). -You can get these IDs by `searching on the MusicBrainz web -site <https://musicbrainz.org/>`__ and going to a *release* page (when -tagging full albums) or a *recording* page (when tagging singletons). -Then, copy the URL of the page and paste it into beets. - -Note that MusicBrainz has both "releases" and "release groups," which -link together different versions of the same album. Use *release* IDs -here. +You can get these IDs by `searching on the MusicBrainz web site +<https://musicbrainz.org/>`__ and going to a *release* page (when tagging full +albums) or a *recording* page (when tagging singletons). Then, copy the URL of +the page and paste it into beets. +Note that MusicBrainz has both "releases" and "release groups," which link +together different versions of the same album. Use *release* IDs here. .. _upgrade: …upgrade to the latest version of beets? ----------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Run a command like this:: +Run a command like this: + +:: pip install -U beets -The ``-U`` flag tells `pip`_ to upgrade -beets to the latest version. If you want a specific version, you can -specify with using ``==`` like so:: +The ``-U`` flag tells pip_ to upgrade beets to the latest version. If you want a +specific version, you can specify with using ``==`` like so: + +:: pip install beets==1.0rc2 - .. _src: …run the latest source version of beets? ----------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Beets sees regular releases (about every six weeks or so), but sometimes -it's helpful to run on the "bleeding edge". To run the latest source: +Beets sees regular releases (about every six weeks or so), but sometimes it's +helpful to run on the "bleeding edge". To run the latest source: -1. Uninstall beets. If you installed using ``pip``, you can just run - ``pip uninstall beets``. +1. Uninstall beets. If you installed using ``pip``, you can just run ``pip + uninstall beets``. 2. Install from source. Choose one of these methods: - - Directly from GitHub using - ``python -m pip install git+https://github.com/beetbox/beets.git`` - command. Depending on your system, you may need to use ``pip3`` - and ``python3`` instead of ``pip`` and ``python`` respectively. - - Use ``pip`` to install the latest snapshot tarball. Type: - ``pip install https://github.com/beetbox/beets/tarball/master`` - - Use ``pip`` to install an "editable" version of beets based on an - automatic source checkout. For example, run - ``pip install -e git+https://github.com/beetbox/beets#egg=beets`` - to clone beets and install it, allowing you to modify the source - in-place to try out changes. - - Clone source code and install it in editable mode + - Directly from GitHub using ``python -m pip install + git+https://github.com/beetbox/beets.git`` command. Depending on your + system, you may need to use ``pip3`` and ``python3`` instead of ``pip`` and + ``python`` respectively. + - Use ``pip`` to install the latest snapshot tarball. Type: ``pip install + https://github.com/beetbox/beets/tarball/master`` + - Use ``pip`` to install an "editable" version of beets based on an automatic + source checkout. For example, run ``pip install -e + git+https://github.com/beetbox/beets#egg=beets`` to clone beets and install + it, allowing you to modify the source in-place to try out changes. + - Clone source code and install it in editable mode - .. code-block:: shell + .. code-block:: shell git clone https://github.com/beetbox/beets.git poetry install - This approach lets you decide where the - source is stored, with any changes immediately reflected in your - environment. - -More details about the beets source are available on the :doc:`developer documentation </dev/index>` -pages. + This approach lets you decide where the source is stored, with any changes + immediately reflected in your environment. +More details about the beets source are available on the :doc:`developer +documentation </dev/index>` pages. .. _bugs: …report a bug in beets? ------------------------ +~~~~~~~~~~~~~~~~~~~~~~~ -We use the `issue tracker`_ on GitHub where you can `open a new ticket`_. -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 - readable if you put them in a pastebin (e.g., - `Gist <https://gist.github.com/>`__ or - `Hastebin <https://hastebin.com/>`__), especially when communicating - over IRC or email. -- Turn on beets' debug output (using the -v option: for example, - ``beet -v import ...``) and include that with your bug report. Look - through this verbose output for any red flags that might point to the - problem. -- If you can, try installing the latest beets source code to see if the - bug is fixed in an unreleased version. You can also look at the - :doc:`latest changelog entries </changelog>` - for descriptions of the problem you're seeing. -- Try to narrow your problem down to something specific. Is a - particular plugin causing the problem? (You can disable plugins to - see whether the problem goes away.) Is a some music file or a single - album leading to the crash? (Try importing individual albums to - determine which one is causing the problem.) Is some entry in your - configuration file causing it? Et cetera. -- If you do narrow the problem down to a particular audio file or - album, include it with your bug report so the developers can run - tests. - -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 +- Most importantly: if beets is crashing, please `include the traceback + <https://imgur.com/jacoj>`__. Tracebacks can be more readable if you put them + in a pastebin (e.g., `Gist <https://gist.github.com/>`__ or `Hastebin + <https://hastebin.com/>`__), especially when communicating over IRC or email. +- Turn on beets' debug output (using the -v option: for example, ``beet -v + import ...``) and include that with your bug report. Look through this verbose + output for any red flags that might point to the problem. +- If you can, try installing the latest beets source code to see if the bug is + fixed in an unreleased version. You can also look at the :doc:`latest + changelog entries </changelog>` for descriptions of the problem you're seeing. +- Try to narrow your problem down to something specific. Is a particular plugin + causing the problem? (You can disable plugins to see whether the problem goes + away.) Is a some music file or a single album leading to the crash? (Try + importing individual albums to determine which one is causing the problem.) Is + some entry in your configuration file causing it? Et cetera. +- If you do narrow the problem down to a particular audio file or album, include + it with your bug report so the developers can run tests. +If you've never reported a bug before, Mozilla has some well-written `general +guidelines for good bug reports`_. .. _find-config: +.. _general guidelines for good bug reports: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Bug_writing_guidelines + +.. _issue tracker: https://github.com/beetbox/beets/issues + …find the configuration file (config.yaml)? -------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You create this file yourself; beets just reads it. See :doc:`/reference/config`. - .. _special-chars: …avoid using special characters in my filenames? ------------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the ``%asciify{}`` function in your path formats. See :ref:`template-functions`. - .. _move-dir: …point beets at a new music directory? --------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you want to move your music from one directory to another, the best way is -to let beets do it for you. First, edit your configuration and set the +If you want to move your music from one directory to another, the best way is to +let beets do it for you. First, edit your configuration and set the ``directory`` setting to the new place. Then, type ``beet move`` to have beets move all your files. If you've already moved your music *outside* of beets, you have a few options: - Move the music back (with an ordinary ``mv``) and then use the above steps. -- Delete your database and re-create it from the new paths using ``beet import -AWC``. +- Delete your database and re-create it from the new paths using ``beet import + -AWC``. - Resort to manually modifying the SQLite database (not recommended). - Why does beets… -=============== +--------------- .. _nomatch: …complain that it can't find a match? -------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are a number of possibilities: -- First, make sure the album is in `the MusicBrainz - database <https://musicbrainz.org/>`__. You - can search on their site to make sure it's cataloged there. (If not, - anyone can edit MusicBrainz---so consider adding the data yourself.) -- If the album in question is a multi-disc release, see the relevant - FAQ answer above. -- The music files' metadata might be insufficient. Try using the "enter - search" or "enter ID" options to help the matching process find the - right MusicBrainz entry. -- If you have a lot of files that are missing metadata, consider using - :doc:`acoustic fingerprinting </plugins/chroma>` or - :doc:`filename-based guesses </plugins/fromfilename>` - for that music. - -If none of these situations apply and you're still having trouble -tagging something, please :ref:`file a bug report <bugs>`. +- First, make sure the album is in `the MusicBrainz database + <https://musicbrainz.org/>`__. You can search on their site to make sure it's + cataloged there. (If not, anyone can edit MusicBrainz---so consider adding the + data yourself.) +- If the album in question is a multi-disc release, see the relevant FAQ answer + above. +- The music files' metadata might be insufficient. Try using the "enter search" + or "enter ID" options to help the matching process find the right MusicBrainz + entry. +- If you have a lot of files that are missing metadata, consider using + :doc:`acoustic fingerprinting </plugins/chroma>` or :doc:`filename-based + guesses </plugins/fromfilename>` for that music. +If none of these situations apply and you're still having trouble tagging +something, please :ref:`file a bug report <bugs>`. .. _plugins: …appear to be missing some plugins? ------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Please make sure you're using the latest version of beets---you might -be using a version earlier than the one that introduced the plugin. In -many cases, the plugin may be introduced in beets "trunk" (the latest -source version) and might not be released yet. Take a look at :doc:`the -changelog </changelog>` -to see which version added the plugin. (You can type ``beet version`` to -check which version of beets you have installed.) +Please make sure you're using the latest version of beets---you might be using a +version earlier than the one that introduced the plugin. In many cases, the +plugin may be introduced in beets "trunk" (the latest source version) and might +not be released yet. Take a look at :doc:`the changelog </changelog>` to see +which version added the plugin. (You can type ``beet version`` to check which +version of beets you have installed.) -If you want to live on the bleeding edge and use the latest source -version of beets, you can check out the source (see :ref:`the relevant -question <src>`). - -To see the beets documentation for your version (and avoid confusion -with new features in trunk), select your version from the menu in the sidebar. +If you want to live on the bleeding edge and use the latest source version of +beets, you can check out the source (see :ref:`the relevant question <src>`). +To see the beets documentation for your version (and avoid confusion with new +features in trunk), select your version from the menu in the sidebar. .. _kill: …ignore control-C during an import? ------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Typing a ^C (control-C) control sequence will not halt beets' -multithreaded importer while it is waiting at a prompt for user input. -Instead, hit "return" (dismissing the prompt) after typing ^C. -Alternatively, just type a "b" for "aBort" at most prompts. Typing ^C -*will* work if the importer interface is between prompts. +Typing a ^C (control-C) control sequence will not halt beets' multithreaded +importer while it is waiting at a prompt for user input. Instead, hit "return" +(dismissing the prompt) after typing ^C. Alternatively, just type a "b" for +"aBort" at most prompts. Typing ^C *will* work if the importer interface is +between prompts. -Also note that beets may take some time to quit after ^C is typed; it -tries to clean up after itself briefly even when canceled. - -(For developers: this is because the UI thread is blocking on -``input`` and cannot be interrupted by the main thread, which is -trying to close all pipeline stages in the exception handler by setting -a flag. There is no simple way to remedy this.) +Also note that beets may take some time to quit after ^C is typed; it tries to +clean up after itself briefly even when canceled. +(For developers: this is because the UI thread is blocking on ``input`` and +cannot be interrupted by the main thread, which is trying to close all pipeline +stages in the exception handler by setting a flag. There is no simple way to +remedy this.) .. _id3v24: …not change my ID3 tags? ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ -Beets writes `ID3v2.4`_ tags by default. -Some software, including Windows (i.e., Windows Explorer and Windows -Media Player) and `id3lib/id3v2 <http://id3v2.sourceforge.net/>`__, -don't support v2.4 tags. When using 2.4-unaware software, it might look -like the tags are unmodified or missing completely. +Beets writes ID3v2.4_ tags by default. Some software, including Windows (i.e., +Windows Explorer and Windows Media Player) and `id3lib/id3v2 +<http://id3v2.sourceforge.net/>`__, don't support v2.4 tags. When using +2.4-unaware software, it might look like the tags are unmodified or missing +completely. To enable ID3v2.3 tags, enable the :ref:`id3v23` config option. +.. _id3v2.4: https://id3.org/id3v2.4.0-structure .. _invalid: -.. _ID3v2.4: https://id3.org/id3v2.4.0-structure …complain that a file is "unreadable"? --------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Beets will log a message like "unreadable file: /path/to/music.mp3" when -it encounters files that *look* like music files (according to their -extension) but seem to be broken. Most of the time, this is because the -file is corrupted. To check whether the file is intact, try opening it -in another media player (e.g., -`VLC <https://www.videolan.org/vlc/index.html>`__) to see whether it can -read the file. You can also use specialized programs for checking file -integrity---for example, type ``metaflac --list music.flac`` to check -FLAC files. +Beets will log a message like "unreadable file: /path/to/music.mp3" when it +encounters files that *look* like music files (according to their extension) but +seem to be broken. Most of the time, this is because the file is corrupted. To +check whether the file is intact, try opening it in another media player (e.g., +`VLC <https://www.videolan.org/vlc/index.html>`__) to see whether it can 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, `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. - +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: …seem to "hang" after an import finishes? ------------------------------------------ - -Probably not. Beets uses a *multithreaded importer* that overlaps many -different activities: it can prompt you for decisions while, in the -background, it talks to MusicBrainz and copies files. This means that, -even after you make your last decision, there may be a backlog of files -to be copied into place and tags to be written. (Plugin tasks, like -looking up lyrics and genres, also run at this time.) If beets pauses -after you see all the albums go by, have patience. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Probably not. Beets uses a *multithreaded importer* that overlaps many different +activities: it can prompt you for decisions while, in the background, it talks +to MusicBrainz and copies files. This means that, even after you make your last +decision, there may be a backlog of files to be copied into place and tags to be +written. (Plugin tasks, like looking up lyrics and genres, also run at this +time.) If beets pauses after you see all the albums go by, have patience. .. _replaceq: …put a bunch of underscores in my filenames? --------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When naming files, beets replaces certain characters to avoid causing -problems on the filesystem. For example, leading dots can confusingly -hide files on Unix and several non-alphanumeric characters are forbidden -on Windows. +When naming files, beets replaces certain characters to avoid causing problems +on the filesystem. For example, leading dots can confusingly hide files on Unix +and several non-alphanumeric characters are forbidden on Windows. -The :ref:`replace` config option -controls which replacements are made. By default, beets makes filenames -safe for all known platforms by replacing several patterns with -underscores. This means that, even on Unix, filenames are made -Windows-safe so that network filesystems (such as SMB) can be used -safely. - -Most notably, Windows forbids trailing dots, so a folder called "M.I.A." -will be rewritten to "M.I.A\_" by default. Change the ``replace`` config -if you don't want this behavior and don't need Windows-safe names. +The :ref:`replace` config option controls which replacements are made. By +default, beets makes filenames safe for all known platforms by replacing several +patterns with underscores. This means that, even on Unix, filenames are made +Windows-safe so that network filesystems (such as SMB) can be used safely. +Most notably, Windows forbids trailing dots, so a folder called "M.I.A." will be +rewritten to "M.I.A\_" by default. Change the ``replace`` config if you don't +want this behavior and don't need Windows-safe names. .. _pathq: …say "command not found"? -------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~ You need to put the ``beet`` program on your system's search path. If you installed using pip, the command ``pip show -f beets`` can show you where ``beet`` was placed on your system. If you need help extending your ``$PATH``, try `this Super User answer`_. -.. _this Super User answer: https://superuser.com/a/284361/4569 +.. _open a new ticket: https://github.com/beetbox/beets/issues/new?template=bug-report.md + .. _pip: https://pip.pypa.io/en/stable/ -.. _open a new ticket: - https://github.com/beetbox/beets/issues/new?template=bug-report.md + +.. _this super user answer: https://superuser.com/a/284361/4569 diff --git a/docs/guides/advanced.rst b/docs/guides/advanced.rst index 3802c91b1..b3a5aff20 100644 --- a/docs/guides/advanced.rst +++ b/docs/guides/advanced.rst @@ -1,40 +1,40 @@ Advanced Awesomeness ==================== -So you have beets up and running and you've started :doc:`importing your -music </guides/tagger>`. There's a lot more that beets can do now that it has +So you have beets up and running and you've started :doc:`importing your music +</guides/tagger>`. There's a lot more that beets can do now that it has cataloged your collection. Here's a few features to get you started. Most of these tips involve :doc:`plugins </plugins/index>` and fiddling with beets' :doc:`configuration </reference/config>`. So use your favorite text editor to create a config file before you continue. - Fetch album art, genres, and lyrics ----------------------------------- -Beets can help you fill in more than just the basic taxonomy metadata that -comes from MusicBrainz. Plugins can provide :doc:`album art -</plugins/fetchart>`, :doc:`lyrics </plugins/lyrics>`, and -:doc:`genres </plugins/lastgenre>` from databases around the Web. +Beets can help you fill in more than just the basic taxonomy metadata that comes +from MusicBrainz. Plugins can provide :doc:`album art </plugins/fetchart>`, +:doc:`lyrics </plugins/lyrics>`, and :doc:`genres </plugins/lastgenre>` from +databases around the Web. If you want beets to get any of this data automatically during the import process, just enable any of the three relevant plugins (see :doc:`/plugins/index`). For example, put this line in your :doc:`config file -</reference/config>` to enable all three:: +</reference/config>` to enable all three: + +:: plugins: fetchart lyrics lastgenre -Each plugin also has a command you can run to fetch data manually. For -example, if you want to get lyrics for all the Beatles tracks in your -collection, just type ``beet lyrics beatles`` after enabling the plugin. +Each plugin also has a command you can run to fetch data manually. For example, +if you want to get lyrics for all the Beatles tracks in your collection, just +type ``beet lyrics beatles`` after enabling the plugin. Read more about using each of these plugins: -* :doc:`/plugins/fetchart` (and its accompanying :doc:`/plugins/embedart`) -* :doc:`/plugins/lyrics` -* :doc:`/plugins/lastgenre` - +- :doc:`/plugins/fetchart` (and its accompanying :doc:`/plugins/embedart`) +- :doc:`/plugins/lyrics` +- :doc:`/plugins/lastgenre` Customize your file and folder names ------------------------------------ @@ -42,22 +42,21 @@ Customize your file and folder names Beets uses an extremely flexible template system to name the folders and files that organize your music in your filesystem. Take a look at :ref:`path-format-config` for the basics: use fields like ``$year`` and -``$title`` to build up a naming scheme. But if you need more flexibility, -there are two features you need to know about: +``$title`` to build up a naming scheme. But if you need more flexibility, there +are two features you need to know about: -* :ref:`Template functions <template-functions>` are simple expressions you - can use in your path formats to add logic to your names. For example, you - can get an artist's first initial using ``%upper{%left{$albumartist,1}}``. -* If you need more flexibility, the :doc:`/plugins/inline` lets you write - snippets of Python code that generate parts of your filenames. The - equivalent code for getting an artist initial with the *inline* plugin looks - like ``initial: albumartist[0].upper()``. +- :ref:`Template functions <template-functions>` are simple expressions you can + use in your path formats to add logic to your names. For example, you can get + an artist's first initial using ``%upper{%left{$albumartist,1}}``. +- If you need more flexibility, the :doc:`/plugins/inline` lets you write + snippets of Python code that generate parts of your filenames. The equivalent + code for getting an artist initial with the *inline* plugin looks like + ``initial: albumartist[0].upper()``. If you already have music in your library and want to update their names according to a new scheme, just run the :ref:`move-cmd` command to rename everything. - Stream your music to another computer ------------------------------------- @@ -69,8 +68,8 @@ your own personal Spotify. First, enable the ``web`` plugin (see :doc:`/plugins/index`). Run the server by typing ``beet web`` and head to http://localhost:8337 in a browser. You can -browse your collection with queries and, if your browser supports it, play -music using HTML5 audio. +browse your collection with queries and, if your browser supports it, play music +using HTML5 audio. Transcode music files for media players --------------------------------------- @@ -78,9 +77,11 @@ Transcode music files for media players Do you ever find yourself transcoding high-quality rips to a lower-bitrate, lossy format for your phone or music player? Beets can help with that. -You'll first need to install `ffmpeg`_. Then, enable beets' -:doc:`/plugins/convert`. Set a destination directory in your -:doc:`config file </reference/config>` like so:: +You'll first need to install ffmpeg_. Then, enable beets' +:doc:`/plugins/convert`. Set a destination directory in your :doc:`config file +</reference/config>` like so: + +:: convert: dest: ~/converted_music @@ -95,41 +96,44 @@ you like them. Check out :doc:`its documentation </plugins/convert>`. .. _ffmpeg: https://www.ffmpeg.org - Store any data you like ----------------------- The beets database keeps track of a long list of :ref:`built-in fields -<itemfields>`, but you're not limited to just that list. Say, for example, -that you like to categorize your music by the setting where it should be -played. You can invent a new ``context`` attribute to store this. Set the field -using the :ref:`modify-cmd` command:: +<itemfields>`, but you're not limited to just that list. Say, for example, that +you like to categorize your music by the setting where it should be played. You +can invent a new ``context`` attribute to store this. Set the field using the +:ref:`modify-cmd` command: + +:: beet modify context=party artist:'beastie boys' By default, beets will show you the changes that are about to be applied and ask if you really want to apply them to all, some or none of the items or albums. -You can type y for "yes", n for "no", or s for "select". If you choose the latter, -the command will prompt you for each individual matching item or album. +You can type y for "yes", n for "no", or s for "select". If you choose the +latter, the command will prompt you for each individual matching item or album. -Then :doc:`query </reference/query>` your music just as you would with any -other field:: +Then :doc:`query </reference/query>` your music just as you would with any other +field: + +:: beet ls context:mope -You can even use these fields in your filenames (see -:ref:`path-format-config`). +You can even use these fields in your filenames (see :ref:`path-format-config`). -And, unlike :ref:`built-in fields <itemfields>`, such fields can be removed:: +And, unlike :ref:`built-in fields <itemfields>`, such fields can be removed: + +:: beet modify context! artist:'beastie boys' -Read more than you ever wanted to know about the *flexible attributes* -feature `on the beets blog`_. +Read more than you ever wanted to know about the *flexible attributes* feature +`on the beets blog`_. .. _on the beets blog: https://beets.io/blog/flexattr.html - Choose a path style manually for some music ------------------------------------------- @@ -139,19 +143,22 @@ like, but keep around to play for friends and family. This is, of course, impossible to determine automatically using metadata from MusicBrainz. Instead, use a flexible attribute (see above) to store a flag on the music you -want to categorize, like so:: +want to categorize, like so: + +:: beet modify bad=1 christmas Then, you can query on this field in your path formats to sort this music -differently. Put something like this in your configuration file:: +differently. Put something like this in your configuration file: + +:: paths: bad:1: Bad/$artist/$title -Used together, flexible attributes and path format conditions let you sort -your music by any criteria you can imagine. - +Used together, flexible attributes and path format conditions let you sort your +music by any criteria you can imagine. Automatically add new music to your library ------------------------------------------- @@ -167,19 +174,16 @@ or the like. To use it this way, you might want to use these options in your quiet: yes log: /path/to/log.txt -The :ref:`incremental` option will skip importing any directories that have -been imported in the past. -:ref:`quiet` avoids asking you any questions (since this will be run -automatically, no input is possible). -You might also want to use the :ref:`quiet_fallback` options to configure -what should happen when no near-perfect match is found -- this option depends -on your level of paranoia. +The :ref:`incremental` option will skip importing any directories that have been +imported in the past. :ref:`quiet` avoids asking you any questions (since this +will be run automatically, no input is possible). You might also want to use the +:ref:`quiet_fallback` options to configure what should happen when no +near-perfect match is found -- this option depends on your level of paranoia. Finally, :ref:`import_log` will make beets record its decisions so you can come back later and see what you need to handle manually. -The last step is to set up cron or some other automation system to run -``beet import /path/to/incoming/music``. - +The last step is to set up cron or some other automation system to run ``beet +import /path/to/incoming/music``. Useful reports -------------- @@ -187,19 +191,25 @@ Useful reports Since beets has a quite powerful query tool, this list contains some useful and powerful queries to run on your library. -* See a list of all albums which have files which are 128 bit rate:: +- See a list of all albums which have files which are 128 bit rate: + + :: beet list bitrate:128000 -* See a list of all albums with the tracks listed in order of bit rate:: +- See a list of all albums with the tracks listed in order of bit rate: + + :: beet ls -f '$bitrate $artist - $title' bitrate+ -* See a list of albums and their formats:: +- See a list of albums and their formats: + + :: beet ls -f '$albumartist $album $format' | sort | uniq Note that ``beet ls --album -f '... $format'`` doesn't do what you want, - because ``format`` is an item-level field, not an album-level one. - If an album's tracks exist in multiple formats, the album will appear in the - list once for each format. + because ``format`` is an item-level field, not an album-level one. If an + album's tracks exist in multiple formats, the album will appear in the list + once for each format. diff --git a/docs/guides/index.rst b/docs/guides/index.rst index ff538eec6..08685abba 100644 --- a/docs/guides/index.rst +++ b/docs/guides/index.rst @@ -6,8 +6,8 @@ with beets. If you're new to beets, you'll want to begin with the :doc:`main` guide. .. toctree:: - :maxdepth: 1 + :maxdepth: 1 - main - tagger - advanced + main + tagger + advanced diff --git a/docs/guides/main.rst b/docs/guides/main.rst index 255484dde..3e9c880ff 100644 --- a/docs/guides/main.rst +++ b/docs/guides/main.rst @@ -1,7 +1,7 @@ Getting Started =============== -Welcome to `beets`_! This guide will help you begin using it to make your music +Welcome to beets_! This guide will help you begin using it to make your music collection better. .. _beets: https://beets.io/ @@ -9,65 +9,66 @@ collection better. Installing ---------- -You will need Python. -Beets works on Python 3.8 or later. +You will need Python. Beets works on Python 3.8 or later. -* **macOS** 11 (Big Sur) includes Python 3.8 out of the box. - You can opt for a more recent Python installing it via `Homebrew`_ - (``brew install python3``). - There's also a `MacPorts`_ port. Run ``port install beets`` or - ``port install beets-full`` to include many third-party plugins. - -* On **Debian or Ubuntu**, depending on the version, beets is available as an +- **macOS** 11 (Big Sur) includes Python 3.8 out of the box. You can opt for a + more recent Python installing it via Homebrew_ (``brew install python3``). + There's also a MacPorts_ port. Run ``port install beets`` or ``port install + beets-full`` to include many third-party plugins. +- On **Debian or Ubuntu**, depending on the version, beets is available as an official package (`Debian details`_, `Ubuntu details`_), so try typing: ``apt-get install beets``. But the version in the repositories might lag - behind, so make sure you read the right version of these docs. If you want - the latest version, you can get everything you need to install with pip - as described below by running: - ``apt-get install python-dev python-pip`` - -* On **Arch Linux**, `beets is in [extra] <Arch extra_>`_, so just run ``pacman -S - beets``. (There's also a bleeding-edge `dev package <AUR_>`_ in the AUR, which will - probably set your computer on fire.) - -* On **Alpine Linux**, `beets is in the community repository <Alpine package_>`_ + behind, so make sure you read the right version of these docs. If you want the + latest version, you can get everything you need to install with pip as + described below by running: ``apt-get install python-dev python-pip`` +- On **Arch Linux**, `beets is in [extra] <arch extra_>`_, so just run ``pacman + -S beets``. (There's also a bleeding-edge `dev package <aur_>`_ in the AUR, + which will probably set your computer on fire.) +- On **Alpine Linux**, `beets is in the community repository <alpine package_>`_ and can be installed with ``apk add beets``. - -* For **Gentoo Linux**, beets is in Portage as ``media-sound/beets``. Just run +- For **Gentoo Linux**, beets is in Portage as ``media-sound/beets``. Just run ``emerge beets`` to install. There are several USE flags available for optional plugin dependencies. +- On **FreeBSD**, there's a `beets port <freebsd_>`_ at ``audio/beets``. +- On **OpenBSD**, there's a `beets port <openbsd_>`_ can be installed with + ``pkg_add beets``. +- For **Slackware**, there's a SlackBuild_ available. +- On **Fedora** 22 or later, there's a `DNF package`_ you can install with + ``sudo dnf install beets beets-plugins beets-doc``. +- On **Solus**, run ``eopkg install beets``. +- On **NixOS**, there's a `package <nixos_>`_ you can install with ``nix-env -i + beets``. -* On **FreeBSD**, there's a `beets port <FreeBSD_>`_ at ``audio/beets``. +.. _alpine package: https://pkgs.alpinelinux.org/package/edge/community/x86_64/beets -* On **OpenBSD**, there's a `beets port <OpenBSD_>`_ can be installed with ``pkg_add beets``. +.. _arch extra: https://archlinux.org/packages/extra/any/beets/ -* For **Slackware**, there's a `SlackBuild`_ available. +.. _aur: https://aur.archlinux.org/packages/beets-git/ -* On **Fedora** 22 or later, there's a `DNF package`_ you can install with ``sudo dnf install beets beets-plugins beets-doc``. +.. _debian details: https://tracker.debian.org/pkg/beets -* On **Solus**, run ``eopkg install beets``. +.. _dnf package: https://packages.fedoraproject.org/pkgs/beets/ -* On **NixOS**, there's a `package <NixOS_>`_ you can install with ``nix-env -i beets``. +.. _freebsd: http://portsmon.freebsd.org/portoverview.py?category=audio&portname=beets -.. _DNF package: https://packages.fedoraproject.org/pkgs/beets/ -.. _SlackBuild: https://slackbuilds.org/repository/14.2/multimedia/beets/ -.. _FreeBSD: http://portsmon.freebsd.org/portoverview.py?category=audio&portname=beets -.. _AUR: https://aur.archlinux.org/packages/beets-git/ -.. _Debian details: https://tracker.debian.org/pkg/beets -.. _Ubuntu details: https://launchpad.net/ubuntu/+source/beets -.. _OpenBSD: http://openports.se/audio/beets -.. _Arch extra: https://archlinux.org/packages/extra/any/beets/ -.. _Alpine package: https://pkgs.alpinelinux.org/package/edge/community/x86_64/beets -.. _NixOS: https://github.com/NixOS/nixpkgs/tree/master/pkgs/tools/audio/beets -.. _MacPorts: https://www.macports.org +.. _macports: https://www.macports.org -If you have `pip`_, just say ``pip install beets`` (or ``pip install --user +.. _nixos: https://github.com/NixOS/nixpkgs/tree/master/pkgs/tools/audio/beets + +.. _openbsd: http://openports.se/audio/beets + +.. _slackbuild: https://slackbuilds.org/repository/14.2/multimedia/beets/ + +.. _ubuntu details: https://launchpad.net/ubuntu/+source/beets + +If you have pip_, just say ``pip install beets`` (or ``pip install --user beets`` if you run into permissions problems). To install without pip, download beets from `its PyPI page`_ and run ``python setup.py install`` in the directory therein. -.. _its PyPI page: https://pypi.org/project/beets/#files +.. _its pypi page: https://pypi.org/project/beets/#files + .. _pip: https://pip.pypa.io The best way to upgrade beets to a new version is by running ``pip install -U @@ -77,119 +78,124 @@ new versions. .. _@b33ts: https://twitter.com/b33ts Installing by Hand on macOS 10.11 and Higher -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Starting with version 10.11 (El Capitan), macOS has a new security feature called `System Integrity Protection`_ (SIP) that prevents you from modifying some parts of the system. This means that some ``pip`` commands may fail with a permissions error. (You probably *won't* run into this if you've installed -Python yourself with `Homebrew`_ or otherwise. You can also try `MacPorts`_.) +Python yourself with Homebrew_ or otherwise. You can also try MacPorts_.) -If this happens, you can install beets for the current user only by typing -``pip install --user beets``. If you do that, you might want to add +If this happens, you can install beets for the current user only by typing ``pip +install --user beets``. If you do that, you might want to add ``~/Library/Python/3.6/bin`` to your ``$PATH``. -.. _System Integrity Protection: https://support.apple.com/en-us/HT204899 -.. _Homebrew: https://brew.sh +.. _homebrew: https://brew.sh + +.. _system integrity protection: https://support.apple.com/en-us/HT204899 Installing on Windows -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ Installing beets on Windows can be tricky. Following these steps might help you get it right: 1. If you don't have it, `install Python`_ (you want at least Python 3.8). The - installer should give you the option to "add Python to PATH." Check this - box. If you do that, you can skip the next step. - + installer should give you the option to "add Python to PATH." Check this box. + If you do that, you can skip the next step. 2. If you haven't done so already, set your ``PATH`` environment variable to - include Python and its scripts. To do so, open the "Settings" application, - then access the "System" screen, then access the "About" tab, and then hit - "Advanced system settings" located on the right side of the screen. This - should open the "System Properties" screen, then select the "Advanced" tab, - then hit the "Environmental Variables..." button, and then look for the PATH - variable in the table. Add the following to the end of the variable's value: - ``;C:\Python38;C:\Python38\Scripts``. You may need to adjust these paths to + include Python and its scripts. To do so, open the "Settings" application, + then access the "System" screen, then access the "About" tab, and then hit + "Advanced system settings" located on the right side of the screen. This + should open the "System Properties" screen, then select the "Advanced" tab, + then hit the "Environmental Variables..." button, and then look for the PATH + variable in the table. Add the following to the end of the variable's value: + ``;C:\Python38;C:\Python38\Scripts``. You may need to adjust these paths to point to your Python installation. - 3. Now install beets by running: ``pip install beets`` - 4. You're all set! Type ``beet`` at the command prompt to make sure everything's in order. Windows users may also want to install a context menu item for importing files -into beets. Download the `beets.reg`_ file and open it in a text file to make -sure the paths to Python match your system. Then double-click the file add the -necessary keys to your registry. You can then right-click a directory and -choose "Import with beets". +into beets. Download the beets.reg_ file and open it in a text file to make sure +the paths to Python match your system. Then double-click the file add the +necessary keys to your registry. You can then right-click a directory and choose +"Import with beets". Because I don't use Windows myself, I may have missed something. If you have -trouble or you have more detail to contribute here, please direct it to -`the mailing list`_. +trouble or you have more detail to contribute here, please direct it to `the +mailing list`_. -.. _install Python: https://python.org/download/ .. _beets.reg: https://github.com/beetbox/beets/blob/master/extra/beets.reg -.. _install pip: https://pip.pypa.io/en/stable/installing/ + .. _get-pip.py: https://bootstrap.pypa.io/get-pip.py +.. _install pip: https://pip.pypa.io/en/stable/installing/ + +.. _install python: https://python.org/download/ + Installing on ARM (Raspberry Pi and similar) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Beets on ARM devices is not recommended for Linux novices. If you are -comfortable with light troubleshooting in tools like ``pip``, ``make``, -and beets' command-line binary dependencies (e.g. ``ffmpeg`` and -``ImageMagick``), you will probably be okay on ARM devices like the -Raspberry Pi. We have `notes for ARM`_ and an `older ARM reference`_. -Beets is generally developed on x86-64 based devices, and most plugins -target that platform as well. +comfortable with light troubleshooting in tools like ``pip``, ``make``, and +beets' command-line binary dependencies (e.g. ``ffmpeg`` and ``ImageMagick``), +you will probably be okay on ARM devices like the Raspberry Pi. We have `notes +for ARM`_ and an `older ARM reference`_. Beets is generally developed on x86-64 +based devices, and most plugins target that platform as well. -.. _notes for ARM: https://github.com/beetbox/beets/discussions/4910 -.. _older ARM reference: https://discourse.beets.io/t/diary-of-beets-on-arm-odroid-hc4-armbian/1993 +.. _notes for arm: https://github.com/beetbox/beets/discussions/4910 + +.. _older arm reference: https://discourse.beets.io/t/diary-of-beets-on-arm-odroid-hc4-armbian/1993 Configuring ----------- You'll want to set a few basic options before you start using beets. The -:doc:`configuration </reference/config>` is stored in a text file. You -can show its location by running ``beet config -p``, though it may not -exist yet. Run ``beet config -e`` to edit the configuration in your -favorite text editor. The file will start out empty, but here's good -place to start:: +:doc:`configuration </reference/config>` is stored in a text file. You can show +its location by running ``beet config -p``, though it may not exist yet. Run +``beet config -e`` to edit the configuration in your favorite text editor. The +file will start out empty, but here's good place to start: + +:: directory: ~/music library: ~/data/musiclibrary.db Change that first path to a directory where you'd like to keep your music. Then, for ``library``, choose a good place to keep a database file that keeps an index -of your music. (The config's format is `YAML`_. You'll want to configure your -text editor to use spaces, not real tabs, for indentation. Also, ``~`` means -your home directory in these paths, even on Windows.) +of your music. (The config's format is YAML_. You'll want to configure your text +editor to use spaces, not real tabs, for indentation. Also, ``~`` means your +home directory in these paths, even on Windows.) The default configuration assumes you want to start a new organized music folder (that ``directory`` above) and that you'll *copy* cleaned-up music into that empty folder using beets' ``import`` command (see below). But you can configure beets to behave many other ways: -* Start with a new empty directory, but *move* new music in instead of copying - it (saving disk space). Put this in your config file:: +- Start with a new empty directory, but *move* new music in instead of copying + it (saving disk space). Put this in your config file: - import: - move: yes + :: -* Keep your current directory structure; importing should never move or copy + import: + move: yes + +- Keep your current directory structure; importing should never move or copy files but instead just correct the tags on music. Put the line ``copy: no`` under the ``import:`` heading in your config file to disable any copying or renaming. Make sure to point ``directory`` at the place where your music is currently stored. - -* Keep your current directory structure and *do not* correct files' tags: leave +- Keep your current directory structure and *do not* correct files' tags: leave files completely unmodified on your disk. (Corrected tags will still be stored in beets' database, and you can use them to do renaming or tag changes later.) - Put this in your config file:: + Put this in your config file: - import: - copy: no - write: no + :: + + import: + copy: no + write: no to disable renaming and tag-writing. @@ -197,20 +203,19 @@ There are approximately six million other configuration options you can set here, including the directory and file naming scheme. See :doc:`/reference/config` for a full reference. -.. _YAML: https://yaml.org/ +.. _yaml: https://yaml.org/ To check that you've set up your configuration how you want it, you can type ``beet version`` to see a list of enabled plugins or ``beet config`` to get a complete listing of your current configuration. - Importing Your Library ---------------------- The next step is to import your music files into the beets library database. Because this can involve modifying files and moving them around, data loss is -always a possibility, so now would be a good time to make sure you have a -recent backup of all your music. We'll wait. +always a possibility, so now would be a good time to make sure you have a recent +backup of all your music. We'll wait. There are two good ways to bring your existing library into beets. You can either: (a) quickly bring all your files with all their current metadata into @@ -219,18 +224,21 @@ metadata for every album you import. Option (a) is really fast, but option (b) makes sure all your songs' tags are exactly right from the get-go. The point about speed bears repeating: using the autotagger on a large library can take a very long time, and it's an interactive process. So set aside a good chunk of -time if you're going to go that route. For more on the interactive -tagging process, see :doc:`tagger`. +time if you're going to go that route. For more on the interactive tagging +process, see :doc:`tagger`. If you've got time and want to tag all your music right once and for all, do -this:: +this: + +:: $ beet import /path/to/my/music (Note that by default, this command will *copy music into the directory you specified above*. If you want to use your current directory structure, set the -``import.copy`` config option.) To take the fast, -un-autotagged path, just say:: +``import.copy`` config option.) To take the fast, un-autotagged path, just say: + +:: $ beet import -A /my/huge/mp3/library @@ -240,7 +248,9 @@ Adding More Music ----------------- If you've ripped or... otherwise obtained some new music, you can add it with -the ``beet import`` command, the same way you imported your library. Like so:: +the ``beet import`` command, the same way you imported your library. Like so: + +:: $ beet import ~/some_great_album @@ -254,7 +264,9 @@ Seeing Your Music If you want to query your music library, the ``beet list`` (shortened to ``beet ls``) command is for you. You give it a :doc:`query string </reference/query>`, which is formatted something like a Google search, and it gives you a list of -songs. Thus:: +songs. Thus: + +:: $ beet ls the magnetic fields The Magnetic Fields - Distortion - Three-Way @@ -268,33 +280,40 @@ songs. Thus:: $ beet ls album:bird The Mae Shi - Terrorbird - Revelation Six -By default, a search term will match any of a handful of :ref:`common -attributes <keywordquery>` of songs. -(They're -also implicitly joined by ANDs: a track must match *all* criteria in order to -match the query.) To narrow a search term to a particular metadata field, just -put the field before the term, separated by a : character. So ``album:bird`` -only looks for ``bird`` in the "album" field of your songs. (Need to know more? -:doc:`/reference/query/` will answer all your questions.) +By default, a search term will match any of a handful of :ref:`common attributes +<keywordquery>` of songs. (They're also implicitly joined by ANDs: a track must +match *all* criteria in order to match the query.) To narrow a search term to a +particular metadata field, just put the field before the term, separated by a : +character. So ``album:bird`` only looks for ``bird`` in the "album" field of +your songs. (Need to know more? :doc:`/reference/query/` will answer all your +questions.) -The ``beet list`` command also has an ``-a`` option, which searches for albums instead of songs:: +The ``beet list`` command also has an ``-a`` option, which searches for albums +instead of songs: + +:: $ beet ls -a forever Bon Iver - For Emma, Forever Ago Freezepop - Freezepop Forever -There's also an ``-f`` option (for *format*) that lets you specify what gets displayed in the results of a search:: +There's also an ``-f`` option (for *format*) that lets you specify what gets +displayed in the results of a search: + +:: $ beet ls -a forever -f "[$format] $album ($year) - $artist - $title" [MP3] For Emma, Forever Ago (2009) - Bon Iver - Flume [AAC] Freezepop Forever (2011) - Freezepop - Harebrained Scheme -In the format option, field references like `$format` and `$year` are filled +In the format option, field references like ``$format`` and ``$year`` are filled in with data from each result. You can see a full list of available fields by running ``beet fields``. Beets also has a ``stats`` command, just in case you want to see how much music -you have:: +you have: + +:: $ beet stats Tracks: 13019 @@ -307,25 +326,27 @@ Keep Playing ------------ This is only the beginning of your long and prosperous journey with beets. To -keep learning, take a look at :doc:`advanced` for a sampling of what else -is possible. You'll also want to glance over the :doc:`/reference/cli` page -for a more detailed description of all of beets' functionality. (Like -deleting music! That's important.) +keep learning, take a look at :doc:`advanced` for a sampling of what else is +possible. You'll also want to glance over the :doc:`/reference/cli` page for a +more detailed description of all of beets' functionality. (Like deleting music! +That's important.) -Also, check out :doc:`beets' plugins </plugins/index>`. The -real power of beets is in its extensibility---with plugins, beets can do almost -anything for your music collection. +Also, check out :doc:`beets' plugins </plugins/index>`. The real power of beets +is in its extensibility---with plugins, beets can do almost anything for your +music collection. You can always get help using the ``beet help`` command. The plain ``beet help`` command lists all the available commands; then, for example, ``beet help import`` gives more specific help about the ``import`` command. -If you need more of a walkthrough, you can read an illustrated one `on the -beets blog <https://beets.io/blog/walkthrough.html>`_. +If you need more of a walkthrough, you can read an illustrated one `on the beets +blog <https://beets.io/blog/walkthrough.html>`_. Please let us know what you think of beets via `the discussion board`_ or -`Mastodon`_. +Mastodon_. + +.. _mastodon: https://fosstodon.org/@beets + +.. _the discussion board: https://github.com/beetbox/beets/discussions .. _the mailing list: https://groups.google.com/group/beets-users -.. _the discussion board: https://github.com/beetbox/beets/discussions -.. _mastodon: https://fosstodon.org/@beets diff --git a/docs/guides/tagger.rst b/docs/guides/tagger.rst index bf1ecbd8a..dea1713f3 100644 --- a/docs/guides/tagger.rst +++ b/docs/guides/tagger.rst @@ -39,7 +39,7 @@ directory and it imports the files into your library, tagging them as it goes beets currently makes about the music you import. In time, we'd like to remove all of these limitations. -* Your music should be organized by album into directories. That is, the tagger +- Your music should be organized by album into directories. That is, the tagger assumes that each album is in a single directory. These directories can be arbitrarily deep (like ``music/2010/hiphop/seattle/freshespresso/glamour``), but any directory with music files in it is interpreted as a separate album. @@ -48,36 +48,35 @@ all of these limitations. First, directories that look like separate parts of a *multi-disc album* are tagged together as a single release. If two adjacent albums have a common - prefix, followed by "disc," "disk," or "CD" and then a number, they are - tagged together. + prefix, followed by "disc," "disk," or "CD" and then a number, they are tagged + together. Second, if you have jumbled directories containing more than one album, you - can ask beets to split them apart for you based on their metadata. Use - either the ``--group-albums`` command-line flag or the *G* interactive - option described below. + can ask beets to split them apart for you based on their metadata. Use either + the ``--group-albums`` command-line flag or the *G* interactive option + described below. -* The music may have bad tags, but it's not completely untagged. This is - because beets by default infers tags based on existing metadata. But this is - not a hard and fast rule---there are a few ways to tag metadata-poor music: +- The music may have bad tags, but it's not completely untagged. This is because + beets by default infers tags based on existing metadata. But this is not a + hard and fast rule---there are a few ways to tag metadata-poor music: - * You can use the *E* or *I* options described below to search in - MusicBrainz for a specific album or song. - * The :doc:`Acoustid plugin </plugins/chroma>` extends the autotagger to - use acoustic fingerprinting to find information for arbitrary audio. - Install that plugin if you're willing to spend a little more CPU power - to get tags for unidentified albums. (But be aware that it does slow - down the process.) - * The :doc:`FromFilename plugin </plugins/fromfilename>` adds the ability - to guess tags from the filenames. Use this plugin if your tracks have - useful names (like "03 Call Me Maybe.mp3") but their tags don't reflect - that. + - You can use the *E* or *I* options described below to search in + MusicBrainz for a specific album or song. + - The :doc:`Acoustid plugin </plugins/chroma>` extends the autotagger to + use acoustic fingerprinting to find information for arbitrary audio. + Install that plugin if you're willing to spend a little more CPU power + to get tags for unidentified albums. (But be aware that it does slow + down the process.) + - The :doc:`FromFilename plugin </plugins/fromfilename>` adds the ability + to guess tags from the filenames. Use this plugin if your tracks have + useful names (like "03 Call Me Maybe.mp3") but their tags don't reflect + that. -* Currently, MP3, AAC, FLAC, ALAC, Ogg Vorbis, Monkey's Audio, WavPack, - Musepack, Windows Media, Opus, and AIFF files are supported. (Do you use - some other format? Please `file a feature request`_!) +- Currently, MP3, AAC, FLAC, ALAC, Ogg Vorbis, Monkey's Audio, WavPack, + 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?template=feature-request.md +.. _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. @@ -89,44 +88,35 @@ Options To import music, just say ``beet import MUSICDIR``. There are, of course, a few command-line options you should know: -* ``beet import -A``: don't try to autotag anything; just import files (this +- ``beet import -A``: don't try to autotag anything; just import files (this goes much faster than with autotagging enabled) - -* ``beet import -W``: when autotagging, don't write new tags to the files +- ``beet import -W``: when autotagging, don't write new tags to the files themselves (just keep the new metadata in beets' database) - -* ``beet import -C``: don't copy imported files to your music directory; leave +- ``beet import -C``: don't copy imported files to your music directory; leave them where they are - -* ``beet import -m``: move imported files to your music directory (overrides - the ``-c`` option) - -* ``beet import -l LOGFILE``: write a message to ``LOGFILE`` every time you skip +- ``beet import -m``: move imported files to your music directory (overrides the + ``-c`` option) +- ``beet import -l LOGFILE``: write a message to ``LOGFILE`` every time you skip an album or choose to take its tags "as-is" (see below) or the album is skipped as a duplicate; this lets you come back later and reexamine albums that weren't tagged successfully. Run ``beet import --from-logfile=LOGFILE`` rerun the importer on such paths from the logfile. - -* ``beet import -q``: quiet mode. Never prompt for input and, instead, +- ``beet import -q``: quiet mode. Never prompt for input and, instead, conservatively skip any albums that need your opinion. The ``-ql`` combination is recommended. - -* ``beet import -t``: timid mode, which is sort of the opposite of "quiet." The +- ``beet import -t``: timid mode, which is sort of the opposite of "quiet." The importer will ask your permission for everything it does, confirming even very good matches with a prompt. - -* ``beet import -p``: automatically resume an interrupted import. The importer +- ``beet import -p``: automatically resume an interrupted import. The importer keeps track of imports that don't finish completely (either due to a crash or because you stop them halfway through) and, by default, prompts you to decide whether to resume them. The ``-p`` flag automatically says "yes" to this question. Relatedly, ``-P`` flag automatically says "no." - -* ``beet import -s``: run in *singleton* mode, tagging individual tracks instead - of whole albums at a time. See the "as Tracks" choice below. This means you +- ``beet import -s``: run in *singleton* mode, tagging individual tracks instead + of whole albums at a time. See the "as Tracks" choice below. This means you can use ``beet import -AC`` to quickly add a bunch of files to your library without doing anything to them. - -* ``beet import -g``: assume there are multiple albums contained in each +- ``beet import -g``: assume there are multiple albums contained in each directory. The tracks contained a directory are grouped by album artist and album name and you will be asked to import each of these groups separately. See the "Group albums" choice below. @@ -134,7 +124,9 @@ command-line options you should know: Similarity ---------- -So you import an album into your beets library. It goes like this:: +So you import an album into your beets library. It goes like this: + +:: $ beet imp witchinghour Tagging: @@ -161,7 +153,9 @@ better at making the call than a computer. So it occasionally asks for help. Choices ------- -When beets needs your input about a match, it says something like this:: +When beets needs your input about a match, it says something like this: + +:: Tagging: Beirut - Lon Gisland @@ -173,36 +167,28 @@ When beets asks you this question, it wants you to enter one of the capital letters: A, M, S, U, T, G, E, I or B. That is, you can choose one of the following: -* *A*: Apply the suggested changes shown and move on. - -* *M*: Show more options. (See the Candidates section, below.) - -* *S*: Skip this album entirely and move on to the next one. - -* *U*: Import the album without changing any tags. This is a good option for +- *A*: Apply the suggested changes shown and move on. +- *M*: Show more options. (See the Candidates section, below.) +- *S*: Skip this album entirely and move on to the next one. +- *U*: Import the album without changing any tags. This is a good option for albums that aren't in the MusicBrainz database, like your friend's operatic faux-goth solo record that's only on two CD-Rs in the universe. - -* *T*: Import the directory as *singleton* tracks, not as an album. Choose this +- *T*: Import the directory as *singleton* tracks, not as an album. Choose this if the tracks don't form a real release---you just have one or more loner tracks that aren't a full album. This will temporarily flip the tagger into *singleton* mode, which attempts to match each track individually. - -* *G*: Group tracks in this directory by *album artist* and *album* and import +- *G*: Group tracks in this directory by *album artist* and *album* and import groups as albums. If the album artist for a track is not set then the artist is used to group that track. For each group importing proceeds as for directories. This is helpful if a directory contains multiple albums. - -* *E*: Enter an artist and album to use as a search in the database. Use this +- *E*: Enter an artist and album to use as a search in the database. Use this option if beets hasn't found any good options because the album is mistagged or untagged. - -* *I*: Enter a metadata backend ID to use as search in the database. Use this +- *I*: Enter a metadata backend ID to use as search in the database. Use this option to specify a backend entity (for example, a MusicBrainz release or recording) directly, by pasting its ID or the full URL. You can also specify several IDs by separating them by a space. - -* *B*: Cancel this import task altogether. No further albums will be tagged; +- *B*: Cancel this import task altogether. No further albums will be tagged; beets shuts down immediately. The next time you attempt to import the same directory, though, beets will ask you if you want to resume tagging where you left off. @@ -215,7 +201,9 @@ Candidates If you choose the M option, or if beets isn't very confident about any of the choices it found, it will present you with a list of choices (called -candidates), like so:: +candidates), like so: + +:: Finding tags for "Panther - Panther". Candidates: @@ -225,9 +213,9 @@ candidates), like so:: Here, you have many of the same options as before, but you can also enter a number to choose one of the options that beets has found. Don't worry about -guessing---beets will show you the proposed changes and ask you to confirm -them, just like the earlier example. As the prompt suggests, you can just hit -return to select the first candidate. +guessing---beets will show you the proposed changes and ask you to confirm them, +just like the earlier example. As the prompt suggests, you can just hit return +to select the first candidate. .. _guide-duplicates: @@ -235,7 +223,9 @@ Duplicates ---------- If beets finds an album or item in your library that seems to be the same as the -one you're importing, you may see a prompt like this:: +one you're importing, you may see a prompt like this: + +:: This album is already in the library! [S]kip new, Keep all, Remove old, Merge all? @@ -243,19 +233,17 @@ one you're importing, you may see a prompt like this:: Beets wants to keep you safe from duplicates, which can be a real pain, so you have four choices in this situation. You can skip importing the new music, choosing to keep the stuff you already have in your library; you can keep both -the old and the new music; you can remove the existing music and choose the -new stuff; or you can merge all the new and old tracks into a single album. -If you choose that "remove" option, any duplicates will be -removed from your library database---and, if the corresponding files are located -inside of your beets library directory, the files themselves will be deleted as -well. +the old and the new music; you can remove the existing music and choose the new +stuff; or you can merge all the new and old tracks into a single album. If you +choose that "remove" option, any duplicates will be removed from your library +database---and, if the corresponding files are located inside of your beets +library directory, the files themselves will be deleted as well. If you choose "merge", beets will try re-importing the existing and new tracks -as one bundle together. -This is particularly helpful when you have an album that's missing some tracks -and then want to import the remaining songs. -The importer will ask you the same questions as it would if you were importing -all tracks at once. +as one bundle together. This is particularly helpful when you have an album +that's missing some tracks and then want to import the remaining songs. The +importer will ask you the same questions as it would if you were importing all +tracks at once. If you choose to keep two identically-named albums, beets can avoid storing both in the same directory. See :ref:`aunique` for details. @@ -268,15 +256,16 @@ files, but can get confused when files don't have any metadata (or have wildly incorrect metadata). In this case, you need *acoustic fingerprinting*, a technology that identifies songs from the audio itself. With fingerprinting, beets can autotag files that have very bad or missing tags. The :doc:`"chroma" -plugin </plugins/chroma>`, distributed with beets, uses the `Chromaprint`_ open-source fingerprinting technology, but it's disabled by default. That's because -it's sort of tricky to install. See the :doc:`/plugins/chroma` page for a guide -to getting it set up. +plugin </plugins/chroma>`, distributed with beets, uses the Chromaprint_ +open-source fingerprinting technology, but it's disabled by default. That's +because it's sort of tricky to install. See the :doc:`/plugins/chroma` page for +a guide to getting it set up. Before you jump into acoustic fingerprinting with both feet, though, give beets a try without it. You may be surprised at how well metadata-based matching works. -.. _Chromaprint: https://acoustid.org/chromaprint +.. _chromaprint: https://acoustid.org/chromaprint Album Art, Lyrics, Genres and Such ---------------------------------- @@ -292,11 +281,11 @@ Missing Albums? --------------- If you're having trouble tagging a particular album with beets, check to make -sure the album is present in `the MusicBrainz database`_. You can search on +sure the album is present in `the MusicBrainz database`_. You can search on their site to make sure it's cataloged there. If not, anyone can edit MusicBrainz---so consider adding the data yourself. -.. _the MusicBrainz database: https://musicbrainz.org/ +.. _the musicbrainz database: https://musicbrainz.org/ If you think beets is ignoring an album that's listed in MusicBrainz, please `file a bug report`_. @@ -306,8 +295,9 @@ If you think beets is ignoring an album that's listed in MusicBrainz, please I Hope That Makes Sense ----------------------- -If we haven't made the process clear, please post on `the discussion -board`_ and we'll try to improve this guide. +If we haven't made the process clear, please post on `the discussion board`_ and +we'll try to improve this guide. + +.. _the discussion board: https://github.com/beetbox/beets/discussions/ .. _the mailing list: https://groups.google.com/group/beets-users -.. _the discussion board: https://github.com/beetbox/beets/discussions/ diff --git a/docs/index.rst b/docs/index.rst index 3ec408fd2..2b2c2e723 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,8 +1,8 @@ beets: the music geek's media organizer ======================================= -Welcome to the documentation for `beets`_, the media library management system -for obsessive music geeks. +Welcome to the documentation for beets_, the media library management system for +obsessive music geeks. If you're new to beets, begin with the :doc:`guides/main` guide. That guide walks you through installing beets, setting it up how you like it, and starting @@ -13,31 +13,34 @@ Then you can get a more detailed look at beets' features in the be interested in exploring the :doc:`plugins </plugins/index>`. If you still need help, you can drop by the ``#beets`` IRC channel on -Libera.Chat, drop by `the discussion board`_, send email to -`the mailing list`_, or `file a bug`_ in the issue tracker. Please let us know -where you think this documentation can be improved. +Libera.Chat, drop by `the discussion board`_, send email to `the mailing list`_, +or `file a bug`_ in the issue tracker. Please let us know where you think this +documentation can be improved. .. _beets: https://beets.io/ -.. _the mailing list: https://groups.google.com/group/beets-users + .. _file a bug: https://github.com/beetbox/beets/issues + .. _the discussion board: https://github.com/beetbox/beets/discussions/ +.. _the mailing list: https://groups.google.com/group/beets-users + Contents -------- .. toctree:: - :maxdepth: 2 + :maxdepth: 2 - guides/index - reference/index - plugins/index - faq - team - contributing - code_of_conduct - dev/index + guides/index + reference/index + plugins/index + faq + team + contributing + code_of_conduct + dev/index .. toctree:: - :maxdepth: 1 + :maxdepth: 1 - changelog + changelog diff --git a/docs/plugins/absubmit.rst b/docs/plugins/absubmit.rst index 25c176e51..262977c4f 100644 --- a/docs/plugins/absubmit.rst +++ b/docs/plugins/absubmit.rst @@ -2,10 +2,10 @@ AcousticBrainz Submit Plugin ============================ The ``absubmit`` plugin lets you submit acoustic analysis results to an -`AcousticBrainz`_ server. This plugin is now deprecated since the -AcousicBrainz project has been shut down. +AcousticBrainz_ server. This plugin is now deprecated since the AcousicBrainz +project has been shut down. -As an alternative the `beets-xtractor`_ plugin can be used. +As an alternative the beets-xtractor_ plugin can be used. Warning ------- @@ -16,37 +16,37 @@ The AcousticBrainz project has shut down. To use this plugin you must set the Installation ------------ -The ``absubmit`` plugin requires the `streaming_extractor_music`_ program -to run. Its source can be found on `GitHub`_, and while it is possible to -compile the extractor from source, AcousticBrainz would prefer if you used -their binary (see the AcousticBrainz `FAQ`_). +The ``absubmit`` plugin requires the streaming_extractor_music_ program to run. +Its source can be found on GitHub_, and while it is possible to compile the +extractor from source, AcousticBrainz would prefer if you used their binary (see +the AcousticBrainz FAQ_). Then, install ``beets`` with ``absubmit`` extra pip install "beets[absubmit]" -Lastly, enable the plugin in your configuration (see :ref:`using-plugins`). - +Lastly, enable the plugin in your configuration (see :ref:`using-plugins`). Submitting Data --------------- -To run the analysis program and upload its results, type:: +To run the analysis program and upload its results, type: + +:: beet absubmit [-f] [-d] [QUERY] By default, the command will only look for AcousticBrainz data when the tracks -don't already have it; the ``-f`` or ``--force`` switch makes it refetch -data even when it already exists. You can use the ``-d`` or ``--dry`` switch -to check which files will be analyzed, before you start a longer period -of processing. +don't already have it; the ``-f`` or ``--force`` switch makes it refetch data +even when it already exists. You can use the ``-d`` or ``--dry`` switch to check +which files will be analyzed, before you start a longer period of processing. -The plugin works on music with a MusicBrainz track ID attached. The plugin -will also skip music that the analysis tool doesn't support. -`streaming_extractor_music`_ currently supports files with the extensions -``mp3``, ``ogg``, ``oga``, ``flac``, ``mp4``, ``m4a``, ``m4r``, ``m4b``, -``m4p``, ``aac``, ``wma``, ``asf``, ``mpc``, ``wv``, ``spx``, ``tta``, -``3g2``, ``aif``, ``aiff`` and ``ape``. +The plugin works on music with a MusicBrainz track ID attached. The plugin will +also skip music that the analysis tool doesn't support. +streaming_extractor_music_ currently supports files with the extensions ``mp3``, +``ogg``, ``oga``, ``flac``, ``mp4``, ``m4a``, ``m4r``, ``m4b``, ``m4p``, +``aac``, ``wma``, ``asf``, ``mpc``, ``wv``, ``spx``, ``tta``, ``3g2``, ``aif``, +``aiff`` and ``ape``. Configuration ------------- @@ -54,25 +54,27 @@ Configuration To configure the plugin, make a ``absubmit:`` section in your configuration file. The available options are: -- **auto**: Analyze every file on import. Otherwise, you need to use the - ``beet absubmit`` command explicitly. - Default: ``no`` -- **extractor**: The absolute path to the `streaming_extractor_music`_ binary. +- **auto**: Analyze every file on import. Otherwise, you need to use the ``beet + absubmit`` command explicitly. Default: ``no`` +- **extractor**: The absolute path to the streaming_extractor_music_ binary. Default: search for the program in your ``$PATH`` - **force**: Analyze items and submit of AcousticBrainz data even for tracks - that already have it. - Default: ``no``. + that already have it. Default: ``no``. - **pretend**: Do not analyze and submit of AcousticBrainz data but print out - the items which would be processed. - Default: ``no``. + the items which would be processed. Default: ``no``. - **base_url**: The base URL of the AcousticBrainz server. The plugin has no - function if this option is not set. - Default: None + function if this option is not set. Default: None + +.. _acousticbrainz: https://acousticbrainz.org + +.. _beets-xtractor: https://github.com/adamjakab/BeetsPluginXtractor + +.. _faq: https://acousticbrainz.org/faq + +.. _github: https://github.com/MTG/essentia + +.. _pip: https://pip.pypa.io + +.. _requests: https://requests.readthedocs.io/en/master/ .. _streaming_extractor_music: https://essentia.upf.edu/ -.. _FAQ: https://acousticbrainz.org/faq -.. _pip: https://pip.pypa.io -.. _requests: https://requests.readthedocs.io/en/master/ -.. _github: https://github.com/MTG/essentia -.. _AcousticBrainz: https://acousticbrainz.org -.. _beets-xtractor: https://github.com/adamjakab/BeetsPluginXtractor diff --git a/docs/plugins/acousticbrainz.rst b/docs/plugins/acousticbrainz.rst index 3a053e123..7f2f9a534 100644 --- a/docs/plugins/acousticbrainz.rst +++ b/docs/plugins/acousticbrainz.rst @@ -2,51 +2,54 @@ AcousticBrainz Plugin ===================== The ``acousticbrainz`` plugin gets acoustic-analysis information from the -`AcousticBrainz`_ project. This plugin is now deprecated since the -AcousicBrainz project has been shut down. +AcousticBrainz_ project. This plugin is now deprecated since the AcousicBrainz +project has been shut down. -As an alternative the `beets-xtractor`_ plugin can be used. +As an alternative the beets-xtractor_ plugin can be used. + +.. _acousticbrainz: https://acousticbrainz.org/ -.. _AcousticBrainz: https://acousticbrainz.org/ .. _beets-xtractor: https://github.com/adamjakab/BeetsPluginXtractor -Enable the ``acousticbrainz`` plugin in your configuration (see :ref:`using-plugins`) and run it by typing:: +Enable the ``acousticbrainz`` plugin in your configuration (see +:ref:`using-plugins`) and run it by typing: + +:: $ beet acousticbrainz [-f] [QUERY] By default, the command will only look for AcousticBrainz data when the tracks doesn't already have it; the ``-f`` or ``--force`` switch makes it re-download data even when it already exists. If you specify a query, only matching tracks -will be processed; otherwise, the command processes every track in your -library. +will be processed; otherwise, the command processes every track in your library. -For all tracks with a MusicBrainz recording ID, the plugin currently sets -these fields: +For all tracks with a MusicBrainz recording ID, the plugin currently sets these +fields: -* ``average_loudness`` -* ``bpm`` -* ``chords_changes_rate`` -* ``chords_key`` -* ``chords_number_rate`` -* ``chords_scale`` -* ``danceable`` -* ``gender`` -* ``genre_rosamerica`` -* ``initial_key`` (This is a built-in beets field, which can also be provided - by :doc:`/plugins/keyfinder`.) -* ``key_strength`` -* ``mood_acoustic`` -* ``mood_aggressive`` -* ``mood_electronic`` -* ``mood_happy`` -* ``mood_party`` -* ``mood_relaxed`` -* ``mood_sad`` -* ``moods_mirex`` -* ``rhythm`` -* ``timbre`` -* ``tonal`` -* ``voice_instrumental`` +- ``average_loudness`` +- ``bpm`` +- ``chords_changes_rate`` +- ``chords_key`` +- ``chords_number_rate`` +- ``chords_scale`` +- ``danceable`` +- ``gender`` +- ``genre_rosamerica`` +- ``initial_key`` (This is a built-in beets field, which can also be provided by + :doc:`/plugins/keyfinder`.) +- ``key_strength`` +- ``mood_acoustic`` +- ``mood_aggressive`` +- ``mood_electronic`` +- ``mood_happy`` +- ``mood_party`` +- ``mood_relaxed`` +- ``mood_sad`` +- ``moods_mirex`` +- ``rhythm`` +- ``timbre`` +- ``tonal`` +- ``voice_instrumental`` Warning ------- @@ -57,10 +60,10 @@ The AcousticBrainz project has shut down. To use this plugin you must set the Automatic Tagging ----------------- -To automatically tag files using AcousticBrainz data during import, just -enable the ``acousticbrainz`` plugin (see :ref:`using-plugins`). When importing -new files, beets will query the AcousticBrainz API using MBID and -set the appropriate metadata. +To automatically tag files using AcousticBrainz data during import, just enable +the ``acousticbrainz`` plugin (see :ref:`using-plugins`). When importing new +files, beets will query the AcousticBrainz API using MBID and set the +appropriate metadata. Configuration ------------- @@ -68,13 +71,10 @@ Configuration To configure the plugin, make a ``acousticbrainz:`` section in your configuration file. The available options are: -- **auto**: Enable AcousticBrainz during ``beet import``. - Default: ``yes``. -- **force**: Download AcousticBrainz data even for tracks that already have - it. +- **auto**: Enable AcousticBrainz during ``beet import``. Default: ``yes``. +- **force**: Download AcousticBrainz data even for tracks that already have it. Default: ``no``. -- **tags**: Which tags from the list above to set on your files. - Default: [] (all). +- **tags**: Which tags from the list above to set on your files. Default: [] + (all). - **base_url**: The base URL of the AcousticBrainz server. The plugin has no - function if this option is not set. - Default: None + function if this option is not set. Default: None diff --git a/docs/plugins/advancedrewrite.rst b/docs/plugins/advancedrewrite.rst index e244be44b..d796d7ee9 100644 --- a/docs/plugins/advancedrewrite.rst +++ b/docs/plugins/advancedrewrite.rst @@ -1,37 +1,38 @@ Advanced Rewrite Plugin ======================= -The ``advancedrewrite`` plugin lets you easily substitute values -in your templates and path formats, similarly to the :doc:`/plugins/rewrite`. -It's recommended to read the documentation of that plugin first. +The ``advancedrewrite`` plugin lets you easily substitute values in your +templates and path formats, similarly to the :doc:`/plugins/rewrite`. It's +recommended to read the documentation of that plugin first. -The *advanced* rewrite plugin does not only support the simple rule format -of the ``rewrite`` plugin, but also an advanced format: -there, the plugin doesn't consider the value of the rewritten field, -but instead checks if the given item matches a :doc:`query </reference/query>`. -Only then, the field is replaced with the given value. -It's also possible to replace multiple fields at once, -and even supports multi-valued fields. +The *advanced* rewrite plugin does not only support the simple rule format of +the ``rewrite`` plugin, but also an advanced format: there, the plugin doesn't +consider the value of the rewritten field, but instead checks if the given item +matches a :doc:`query </reference/query>`. Only then, the field is replaced with +the given value. It's also possible to replace multiple fields at once, and even +supports multi-valued fields. To use advanced field rewriting, first enable the ``advancedrewrite`` plugin -(see :ref:`using-plugins`). -Then, make a ``advancedrewrite:`` section in your config file to contain -your rewrite rules. +(see :ref:`using-plugins`). Then, make a ``advancedrewrite:`` section in your +config file to contain your rewrite rules. In contrast to the normal ``rewrite`` plugin, you need to provide a list of -replacement rule objects, which can have a different syntax depending on -the rule complexity. +replacement rule objects, which can have a different syntax depending on the +rule complexity. -The simple syntax is the same as the one of the rewrite plugin and allows -to replace a single field:: +The simple syntax is the same as the one of the rewrite plugin and allows to +replace a single field: + +:: advancedrewrite: - artist ODD EYE CIRCLE: 이달의 소녀 오드아이써클 -The advanced syntax consists of a query to match against, as well as a map -of replacements to apply. -For example, to credit all songs of ODD EYE CIRCLE before 2023 -to their original group name, you can use the following rule:: +The advanced syntax consists of a query to match against, as well as a map of +replacements to apply. For example, to credit all songs of ODD EYE CIRCLE before +2023 to their original group name, you can use the following rule: + +:: advancedrewrite: - match: "mb_artistid:dec0f331-cb08-4c8e-9c9f-aeb1f0f6d88c year:..2022" @@ -39,10 +40,12 @@ to their original group name, you can use the following rule:: artist: 이달의 소녀 오드아이써클 artist_sort: LOONA / ODD EYE CIRCLE -Note how the sort name is also rewritten within the same rule. -You can specify as many fields as you'd like in the replacements map. +Note how the sort name is also rewritten within the same rule. You can specify +as many fields as you'd like in the replacements map. -If you need to work with multi-valued fields, you can use the following syntax:: +If you need to work with multi-valued fields, you can use the following syntax: + +:: advancedrewrite: - match: "artist:배유빈 feat. 김미현" @@ -55,10 +58,12 @@ As a convenience, the plugin applies patterns for the ``artist`` field to the ``albumartist`` field as well. (Otherwise, you would probably want to duplicate every rule for ``artist`` and ``albumartist``.) -Make sure to properly quote your query strings if they contain spaces, -otherwise they might not do what you expect, or even cause beets to crash. +Make sure to properly quote your query strings if they contain spaces, otherwise +they might not do what you expect, or even cause beets to crash. -Take the following example:: +Take the following example: + +:: advancedrewrite: # BAD, DON'T DO THIS! @@ -69,10 +74,12 @@ Take the following example:: On the first sight, this might look sane, and replace the artist of the album *THE ALBUM* with *New artist*. However, due to the space and missing quotes, this query will evaluate to ``album:THE`` and match ``ALBUM`` on any field, -including ``artist``. As ``artist`` is the field being replaced, -this query will result in infinite recursion and ultimately crash beets. +including ``artist``. As ``artist`` is the field being replaced, this query will +result in infinite recursion and ultimately crash beets. -Instead, you should use the following rule:: +Instead, you should use the following rule: + +:: advancedrewrite: # Note the quotes around the query string! @@ -81,11 +88,11 @@ Instead, you should use the following rule:: artist: New artist A word of warning: This plugin theoretically only applies to templates and path -formats; it initially does not modify files' metadata tags or the values -tracked by beets' library database, but since it *rewrites all field lookups*, -it modifies the file's metadata anyway. See comments in issue :bug:`2786`. +formats; it initially does not modify files' metadata tags or the values tracked +by beets' library database, but since it *rewrites all field lookups*, it +modifies the file's metadata anyway. See comments in issue :bug:`2786`. As an alternative to this plugin the simpler but less powerful -:doc:`/plugins/rewrite` can be used. -If you don't want to modify the item's metadata and only replace values -in file paths, you can check out the :doc:`/plugins/substitute`. +:doc:`/plugins/rewrite` can be used. If you don't want to modify the item's +metadata and only replace values in file paths, you can check out the +:doc:`/plugins/substitute`. diff --git a/docs/plugins/albumtypes.rst b/docs/plugins/albumtypes.rst index bf736abca..f3b1b7587 100644 --- a/docs/plugins/albumtypes.rst +++ b/docs/plugins/albumtypes.rst @@ -2,19 +2,19 @@ AlbumTypes Plugin ================= The ``albumtypes`` plugin adds the ability to format and output album types, -such as "Album", "EP", "Single", etc. For the list of available album types, -see the `MusicBrainz documentation`_. +such as "Album", "EP", "Single", etc. For the list of available album types, see +the `MusicBrainz documentation`_. -To use the ``albumtypes`` plugin, enable it in your configuration -(see :ref:`using-plugins`). The plugin defines a new field ``$atypes``, which -you can use in your path formats or elsewhere. +To use the ``albumtypes`` plugin, enable it in your configuration (see +:ref:`using-plugins`). The plugin defines a new field ``$atypes``, which you can +use in your path formats or elsewhere. -.. _MusicBrainz documentation: https://musicbrainz.org/doc/Release_Group/Type +.. _musicbrainz documentation: https://musicbrainz.org/doc/Release_Group/Type A bug introduced in beets 1.6.0 could have possibly imported broken data into -the ``albumtypes`` library field. Please follow the instructions `described -here <https://github.com/beetbox/beets/pull/4582#issuecomment-1445023493>`_ for -a sanity check and potential fix. :bug:`4528` +the ``albumtypes`` library field. Please follow the instructions `described here +<https://github.com/beetbox/beets/pull/4582#issuecomment-1445023493>`_ for a +sanity check and potential fix. :bug:`4528` Configuration ------------- @@ -30,7 +30,9 @@ file. The available options are: are often compilations. - **bracket**: Defines the brackets to enclose each album type in the output. -The default configuration looks like this:: +The default configuration looks like this: + +:: albumtypes: types: @@ -45,18 +47,22 @@ The default configuration looks like this:: Examples -------- -With path formats configured like:: + +With path formats configured like: + +:: paths: default: $albumartist/[$year]$atypes $album/... albumtype:soundtrack: Various Artists/$album [$year]$atypes/... comp: Various Artists/$album [$year]$atypes/... +The default plugin configuration generates paths that look like this, for +example: -The default plugin configuration generates paths that look like this, for example:: +:: Aphex Twin/[1993][EP][Remix] On Remixes Pink Floyd/[1995][Live] p·u·l·s·e Various Artists/20th Century Lullabies [1999] Various Artists/Ocean's Eleven [2001][OST] - diff --git a/docs/plugins/aura.rst b/docs/plugins/aura.rst index 49e2649b6..a29fa1952 100644 --- a/docs/plugins/aura.rst +++ b/docs/plugins/aura.rst @@ -1,13 +1,14 @@ AURA Plugin =========== -This plugin is a server implementation of the `AURA`_ specification using the -`Flask`_ framework. AURA is still a work in progress and doesn't yet have a -stable version, but this server should be kept up to date. You are advised to -read the :ref:`aura-issues` section. +This plugin is a server implementation of the AURA_ specification using the +Flask_ framework. AURA is still a work in progress and doesn't yet have a stable +version, but this server should be kept up to date. You are advised to read the +:ref:`aura-issues` section. -.. _AURA: https://auraspec.readthedocs.io -.. _Flask: https://palletsprojects.com/p/flask/ +.. _aura: https://auraspec.readthedocs.io + +.. _flask: https://palletsprojects.com/p/flask/ Install ------- @@ -17,18 +18,16 @@ To use the ``aura`` plugin, first enable it in your configuration (see pip install "beets[aura]" - Usage ----- -Use ``beet aura`` to start the AURA server. -By default Flask's built-in server is used, which will give a warning about -using it in a production environment. It is safe to ignore this warning if the -server will have only a few users. +Use ``beet aura`` to start the AURA server. By default Flask's built-in server +is used, which will give a warning about using it in a production environment. +It is safe to ignore this warning if the server will have only a few users. -Alternatively, you can use ``beet aura -d`` to start the server in -`development mode`_, which will reload the server every time the AURA plugin -file is changed. +Alternatively, you can use ``beet aura -d`` to start the server in `development +mode <https://flask.palletsprojects.com/en/1.1.x/server>`__, which will reload +the server every time the AURA plugin file is changed. You can specify the hostname and port number used by the server in your :doc:`configuration file </reference/config>`. For more detail see the @@ -39,50 +38,48 @@ then see :ref:`aura-external-server`. AURA is designed to separate the client and server functionality. This plugin provides the server but not the client, so unless you like looking at JSON you -will need a separate client. Currently the only client is `AURA Web Client`_. -In order to use a local browser client with ``file:///`` see :ref:`aura-cors`. +will need a separate client. Currently the only client is `AURA Web Client`_. In +order to use a local browser client with ``file:///`` see :ref:`aura-cors`. By default the API is served under http://127.0.0.1:8337/aura/. For example information about the track with an id of 3 can be obtained at http://127.0.0.1:8337/aura/tracks/3. -**Note the absence of a trailing slash**: -http://127.0.0.1:8337/aura/tracks/3/ returns a ``404 Not Found`` error. - -.. _development mode: https://flask.palletsprojects.com/en/1.1.x/server -.. _AURA Web Client: https://sr.ht/~callum/aura-web-client/ +**Note the absence of a trailing slash**: http://127.0.0.1:8337/aura/tracks/3/ +returns a ``404 Not Found`` error. +.. _aura web client: https://sr.ht/~callum/aura-web-client/ .. _configuration: Configuration ------------- -To configure the plugin, make an ``aura:`` section in your -configuration file. The available options are: +To configure the plugin, make an ``aura:`` section in your configuration file. +The available options are: - **host**: The server hostname. Set this to ``0.0.0.0`` to bind to all interfaces. Default: ``127.0.0.1``. -- **port**: The server port. - Default: ``8337``. +- **port**: The server port. Default: ``8337``. - **cors**: A YAML list of origins to allow CORS requests from (see - :ref:`aura-cors`, below). - Default: disabled. + :ref:`aura-cors`, below). Default: disabled. - **cors_supports_credentials**: Allow authenticated requests when using CORS. Default: disabled. - **page_limit**: The number of items responses should be truncated to if the client does not specify. Default ``500``. - .. _aura-cors: Cross-Origin Resource Sharing (CORS) ------------------------------------ -`CORS`_ allows browser clients to make requests to the AURA server. You should -set the ``cors`` configuration option to a YAML list of allowed origins. +`CORS <https://en.wikipedia.org/wiki/Cross-origin_resource_sharing>`__ allows +browser clients to make requests to the AURA server. You should set the ``cors`` +configuration option to a YAML list of allowed origins. -For example:: +For example: + +:: aura: cors: @@ -91,24 +88,21 @@ For example:: In order to use the plugin with a local browser client accessed using ``file:///`` you must include ``'null'`` in the list of allowed origins -(including quote marks):: +(including quote marks): + +:: aura: cors: - 'null' -Alternatively you use ``'*'`` to enable access from all origins. -Note that there are security implications if you set the origin to ``'*'``, -so please research this before using it. Note the use of quote marks when -allowing all origins. - -If the server is behind a proxy that uses credentials, you might want to set -the ``cors_supports_credentials`` configuration option to true to let -in-browser clients log in. Note that this option has not been tested, so it -may not work. - -.. _CORS: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing +Alternatively you use ``'*'`` to enable access from all origins. Note that there +are security implications if you set the origin to ``'*'``, so please research +this before using it. Note the use of quote marks when allowing all origins. +If the server is behind a proxy that uses credentials, you might want to set the +``cors_supports_credentials`` configuration option to true to let in-browser +clients log in. Note that this option has not been tested, so it may not work. .. _aura-external-server: @@ -119,16 +113,16 @@ If you would like to use a different WSGI server (not Flask's built-in one), then you can! The ``beetsplug.aura`` module provides a WSGI callable called ``create_app()`` which can be used by many WSGI servers. -For example to run the AURA server using `gunicorn`_ use -``gunicorn 'beetsplug.aura:create_app()'``, or for `uWSGI`_ use -``uwsgi --http :8337 --module 'beetsplug.aura:create_app()'``. -Note that these commands just show how to use the AURA app and you would -probably use something a bit different in a production environment. Read the -relevant server's documentation to figure out what you need. +For example to run the AURA server using gunicorn_ use ``gunicorn +'beetsplug.aura:create_app()'``, or for uWSGI_ use ``uwsgi --http :8337 --module +'beetsplug.aura:create_app()'``. Note that these commands just show how to use +the AURA app and you would probably use something a bit different in a +production environment. Read the relevant server's documentation to figure out +what you need. .. _gunicorn: https://gunicorn.org -.. _uWSGI: https://uwsgi-docs.readthedocs.io +.. _uwsgi: https://uwsgi-docs.readthedocs.io Reverse Proxy Support --------------------- @@ -137,6 +131,8 @@ The plugin should work behind a reverse proxy without further configuration, however this has not been tested extensively. For details of what headers must be rewritten and a sample NGINX configuration see `Flask proxy setups`_. +.. _flask proxy setups: https://flask.palletsprojects.com/en/1.1.x/deploying/wsgi-standalone/#proxy-setups + It is (reportedly) possible to run the application under a URL prefix (for example so you could have ``/foo/aura/server`` rather than ``/aura/server``), but you'll have to work it out for yourself :-) @@ -145,9 +141,6 @@ If using NGINX, do **not** add a trailing slash (``/``) to the URL where the application is running, otherwise you will get a 404. However if you are using Apache then you **should** add a trailing slash. -.. _Flask proxy setups: https://flask.palletsprojects.com/en/1.1.x/deploying/wsgi-standalone/#proxy-setups - - .. _aura-issues: Issues @@ -160,26 +153,26 @@ implementation: multiple ``filter`` parameters as AND. See `issue #19`_ for discussion. - The ``bitrate`` parameter used for content negotiation is not supported. Adding support for this is doable, but the way Flask handles acceptable MIME - types means it's a lot easier not to bother with it. This means an error - could be returned even if no transcoding was required. + types means it's a lot easier not to bother with it. This means an error could + be returned even if no transcoding was required. It is possible that some attributes required by AURA could be absent from the server's response if beets does not have a saved value for them. However, this has not happened so far. -Beets fields (including flexible fields) that do not have an AURA equivalent -are not provided in any resource's attributes section, however these fields may -be used for filtering. +Beets fields (including flexible fields) that do not have an AURA equivalent are +not provided in any resource's attributes section, however these fields may be +used for filtering. The ``mimetype`` and ``framecount`` attributes for track resources are not -supported. The first is due to beets storing the file type (e.g. ``MP3``), so -it is hard to filter by MIME type. The second is because there is no -corresponding beets field. +supported. The first is due to beets storing the file type (e.g. ``MP3``), so it +is hard to filter by MIME type. The second is because there is no corresponding +beets field. Artists are defined by the ``artist`` field on beets Items, which means some -albums have no ``artists`` relationship. Albums only have related artists -when their beets ``albumartist`` field is the same as the ``artist`` field on -at least one of it's constituent tracks. +albums have no ``artists`` relationship. Albums only have related artists when +their beets ``albumartist`` field is the same as the ``artist`` field on at +least one of it's constituent tracks. The only art tracked by beets is a single cover image, so only albums have related images at the moment. This could be expanded to looking in the same diff --git a/docs/plugins/autobpm.rst b/docs/plugins/autobpm.rst index 53908c517..00f02d6a8 100644 --- a/docs/plugins/autobpm.rst +++ b/docs/plugins/autobpm.rst @@ -1,10 +1,10 @@ AutoBPM Plugin ============== -The `autobpm` plugin uses the `Librosa`_ library to calculate the BPM -of a track from its audio data and store it in the `bpm` field of your -database. It does so automatically when importing music or through -the ``beet autobpm [QUERY]`` command. +The ``autobpm`` plugin uses the Librosa_ library to calculate the BPM of a track +from its audio data and store it in the ``bpm`` field of your database. It does +so automatically when importing music or through the ``beet autobpm [QUERY]`` +command. Install ------- @@ -19,17 +19,15 @@ To use the ``autobpm`` plugin, first enable it in your configuration (see Configuration ------------- -To configure the plugin, make a ``autobpm:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``autobpm:`` section in your configuration file. +The available options are: -- **auto**: Analyze every file on import. - Otherwise, you need to use the ``beet autobpm`` command explicitly. - Default: ``yes`` -- **overwrite**: Calculate a BPM even for files that already have a - `bpm` value. - Default: ``no``. +- **auto**: Analyze every file on import. Otherwise, you need to use the ``beet + autobpm`` command explicitly. Default: ``yes`` +- **overwrite**: Calculate a BPM even for files that already have a ``bpm`` + value. Default: ``no``. - **beat_track_kwargs**: Any extra keyword arguments that you would like to - provide to librosa's `beat_track`_ function call, for example: + provide to librosa's beat_track_ function call, for example: .. code-block:: yaml @@ -37,5 +35,6 @@ configuration file. The available options are: beat_track_kwargs: start_bpm: 160 -.. _Librosa: https://github.com/librosa/librosa/ .. _beat_track: https://librosa.org/doc/latest/generated/librosa.beat.beat_track.html + +.. _librosa: https://github.com/librosa/librosa/ diff --git a/docs/plugins/badfiles.rst b/docs/plugins/badfiles.rst index 796f991e1..8f496cfce 100644 --- a/docs/plugins/badfiles.rst +++ b/docs/plugins/badfiles.rst @@ -11,10 +11,12 @@ First, enable the ``badfiles`` plugin (see :ref:`using-plugins`). The default configuration defines the following default checkers, which you may need to install yourself: -* `mp3val`_ for MP3 files -* `FLAC`_ command-line tools for FLAC files +- mp3val_ for MP3 files +- FLAC_ command-line tools for FLAC files -You can also add custom commands for a specific extension, like this:: +You can also add custom commands for a specific extension, like this: + +:: badfiles: check_on_import: yes @@ -26,26 +28,33 @@ Custom commands will be run once for each file of the specified type, with the path to the file as the last argument. Commands must return a status code greater than zero for a file to be considered corrupt. -You can run the checkers when importing files by using the `check_on_import` +You can run the checkers when importing files by using the ``check_on_import`` option. When on, checkers will be run against every imported file and warnings and errors will be presented when selecting a tagging option. -.. _mp3val: http://mp3val.sourceforge.net/ .. _flac: https://xiph.org/flac/ +.. _mp3val: http://mp3val.sourceforge.net/ + Using ----- Type ``beet bad`` with a query according to beets' usual query syntax. For -instance, this will run a check on all songs containing the word "wolf":: +instance, this will run a check on all songs containing the word "wolf": + +:: beet bad wolf -This one will run checks on a specific album:: +This one will run checks on a specific album: + +:: beet bad album_id:1234 -Here is an example where the FLAC decoder signals a corrupt file:: +Here is an example where the FLAC decoder signals a corrupt file: + +:: beet bad title::^$ /tank/Music/__/00.flac: command exited with status 1 @@ -54,10 +63,10 @@ Here is an example where the FLAC decoder signals a corrupt file:: state = FLAC__STREAM_DECODER_READ_FRAME Note that the default ``mp3val`` checker is a bit verbose and can output a lot -of "stream error" messages, even for files that play perfectly well. -Generally, if more than one stream error happens, or if a stream error happens -in the middle of a file, this is a bad sign. +of "stream error" messages, even for files that play perfectly well. Generally, +if more than one stream error happens, or if a stream error happens in the +middle of a file, this is a bad sign. By default, only errors for the bad files will be shown. In order for the -results for all of the checked files to be seen, including the uncorrupted -ones, use the ``-v`` or ``--verbose`` option. +results for all of the checked files to be seen, including the uncorrupted ones, +use the ``-v`` or ``--verbose`` option. diff --git a/docs/plugins/bareasc.rst b/docs/plugins/bareasc.rst index 0c8d6636c..0a9c75fad 100644 --- a/docs/plugins/bareasc.rst +++ b/docs/plugins/bareasc.rst @@ -1,15 +1,17 @@ Bare-ASCII Search Plugin ======================== -The ``bareasc`` plugin provides a prefixed query that searches your library using -simple ASCII character matching, with accented characters folded to their base -ASCII character. This can be useful if you want to find a track with accented -characters in the title or artist, particularly if you are not confident -you have the accents correct. It is also not unknown for the accents +The ``bareasc`` plugin provides a prefixed query that searches your library +using simple ASCII character matching, with accented characters folded to their +base ASCII character. This can be useful if you want to find a track with +accented characters in the title or artist, particularly if you are not +confident you have the accents correct. It is also not unknown for the accents to not be correct in the database entry or wrong in the CD information. -First, enable the plugin named ``bareasc`` (see :ref:`using-plugins`). -You'll then be able to use the ``#`` prefix to use bare-ASCII matching:: +First, enable the plugin named ``bareasc`` (see :ref:`using-plugins`). You'll +then be able to use the ``#`` prefix to use bare-ASCII matching: + +:: $ beet ls '#dvorak' István Kertész - REQUIEM - Dvořàk: Requiem, op.89 - Confutatis maledictis @@ -17,13 +19,16 @@ You'll then be able to use the ``#`` prefix to use bare-ASCII matching:: Command ------- -In addition to the query prefix, the plugin provides a utility ``bareasc`` command. -This command is **exactly** the same as the ``beet list`` command except that -the output is passed through the bare-ASCII transformation before being printed. -This allows you to easily check what the library data looks like in bare ASCII, -which can be useful if you are trying to work out why a query is not matching. +In addition to the query prefix, the plugin provides a utility ``bareasc`` +command. This command is **exactly** the same as the ``beet list`` command +except that the output is passed through the bare-ASCII transformation before +being printed. This allows you to easily check what the library data looks like +in bare ASCII, which can be useful if you are trying to work out why a query is +not matching. -Using the same example track as above:: +Using the same example track as above: + +:: $ beet bareasc 'Dvořàk' Istvan Kertesz - REQUIEM - Dvorak: Requiem, op.89 - Confutatis maledictis @@ -37,33 +42,34 @@ Notes If the query string is all in lower case, the comparison ignores case as well as accents. -The default ``bareasc`` prefix (``#``) is used as a comment character in some shells -so may need to be protected (for example in quotes) when typed into the command line. +The default ``bareasc`` prefix (``#``) is used as a comment character in some +shells so may need to be protected (for example in quotes) when typed into the +command line. -The bare ASCII transliteration is quite simple. It may not give the expected output -for all languages. For example, German u-umlaut ``ü`` is transformed into ASCII ``u``, -not into ``ue``. +The bare ASCII transliteration is quite simple. It may not give the expected +output for all languages. For example, German u-umlaut ``ü`` is transformed into +ASCII ``u``, not into ``ue``. -The bare ASCII transformation also changes Unicode punctuation like double quotes, -apostrophes and even some hyphens. It is often best to leave out punctuation -in the queries. Note that the punctuation changes are often not even visible -with normal terminal fonts. You can always use the ``bareasc`` command to print the -transformed entries and use a command like ``diff`` to compare with the output -from the ``list`` command. +The bare ASCII transformation also changes Unicode punctuation like double +quotes, apostrophes and even some hyphens. It is often best to leave out +punctuation in the queries. Note that the punctuation changes are often not even +visible with normal terminal fonts. You can always use the ``bareasc`` command +to print the transformed entries and use a command like ``diff`` to compare with +the output from the ``list`` command. Configuration ------------- -To configure the plugin, make a ``bareasc:`` section in your configuration -file. The only available option is: +To configure the plugin, make a ``bareasc:`` section in your configuration file. +The only available option is: -- **prefix**: The character used to designate bare-ASCII queries. - Default: ``#``, which may need to be escaped in some shells. +- **prefix**: The character used to designate bare-ASCII queries. Default: + ``#``, which may need to be escaped in some shells. Credits ------- -The hard work in this plugin is done in Sean Burke's -`Unidecode <https://pypi.org/project/Unidecode/>`__ library. -Thanks are due to Sean and to all the people who created the Python -version and the beets extensible query architecture. +The hard work in this plugin is done in Sean Burke's `Unidecode +<https://pypi.org/project/Unidecode/>`__ library. Thanks are due to Sean and to +all the people who created the Python version and the beets extensible query +architecture. diff --git a/docs/plugins/beatport.rst b/docs/plugins/beatport.rst index bc10f02a4..4e7569a1b 100644 --- a/docs/plugins/beatport.rst +++ b/docs/plugins/beatport.rst @@ -1,15 +1,16 @@ Beatport Plugin =============== -The ``beatport`` plugin adds support for querying the `Beatport`_ catalogue -during the autotagging process. This can potentially be helpful for users -whose collection includes a lot of diverse electronic music releases, for which -both MusicBrainz and (to a lesser degree) `Discogs`_ show no matches. +The ``beatport`` plugin adds support for querying the Beatport_ catalogue during +the autotagging process. This can potentially be helpful for users whose +collection includes a lot of diverse electronic music releases, for which both +MusicBrainz and (to a lesser degree) Discogs_ show no matches. -.. _Discogs: https://discogs.com +.. _discogs: https://discogs.com Installation ------------ + To use the ``beatport`` plugin, first enable it in your configuration (see :ref:`using-plugins`). Then, install ``beets`` with ``beatport`` extra @@ -17,27 +18,29 @@ To use the ``beatport`` plugin, first enable it in your configuration (see pip install "beets[beatport]" -You will also need to register for a `Beatport`_ account. The first time you -run the :ref:`import-cmd` command after enabling the plugin, it will ask you -to authorize with Beatport by visiting the site in a browser. On the site -you will be asked to enter your username and password to authorize beets -to query the Beatport API. You will then be displayed with a single line of -text that you should paste as a whole into your terminal. This will store the -authentication data for subsequent runs and you will not be required to repeat -the above steps. +You will also need to register for a Beatport_ account. The first time you run +the :ref:`import-cmd` command after enabling the plugin, it will ask you to +authorize with Beatport by visiting the site in a browser. On the site you will +be asked to enter your username and password to authorize beets to query the +Beatport API. You will then be displayed with a single line of text that you +should paste as a whole into your terminal. This will store the authentication +data for subsequent runs and you will not be required to repeat the above steps. -Matches from Beatport should now show up alongside matches -from MusicBrainz and other sources. +Matches from Beatport should now show up alongside matches from MusicBrainz and +other sources. If you have a Beatport ID or a URL for a release or track you want to tag, you can just enter one of the two at the "enter Id" prompt in the importer. You can -also search for an id like so:: +also search for an id like so: + +:: beet import path/to/music/library --search-id id Configuration ------------- -This plugin can be configured like other metadata source plugins as described in :ref:`metadata-source-plugin-configuration`. +This plugin can be configured like other metadata source plugins as described in +:ref:`metadata-source-plugin-configuration`. -.. _Beatport: https://www.beatport.com/ +.. _beatport: https://www.beatport.com/ diff --git a/docs/plugins/bpd.rst b/docs/plugins/bpd.rst index c7ac350df..dc0bd92b2 100644 --- a/docs/plugins/bpd.rst +++ b/docs/plugins/bpd.rst @@ -3,23 +3,25 @@ BPD Plugin BPD is a music player using music from a beets library. It runs as a daemon and implements the MPD protocol, so it's compatible with all the great MPD clients -out there. I'm using `Theremin`_, `gmpc`_, `Sonata`_, and `Ario`_ successfully. +out there. I'm using Theremin_, gmpc_, Sonata_, and Ario_ successfully. + +.. _ario: http://ario-player.sourceforge.net/ -.. _Theremin: https://github.com/TheStalwart/Theremin .. _gmpc: https://gmpc.wikia.com/wiki/Gnome_Music_Player_Client -.. _Sonata: http://sonata.berlios.de/ -.. _Ario: http://ario-player.sourceforge.net/ + +.. _sonata: http://sonata.berlios.de/ + +.. _theremin: https://github.com/TheStalwart/Theremin Dependencies ------------ -Before you can use BPD, you'll need the media library called `GStreamer`_ (along +Before you can use BPD, you'll need the media library called GStreamer_ (along with its Python bindings) on your system. -* On Mac OS X, you can use `Homebrew`_. Run ``brew install gstreamer +- On Mac OS X, you can use Homebrew_. Run ``brew install gstreamer gst-plugins-base pygobject3``. - -* On Linux, you need to install GStreamer 1.0 and the GObject bindings for +- On Linux, you need to install GStreamer 1.0 and the GObject bindings for python. Under Ubuntu, they are called ``python-gi`` and ``gstreamer1.0``. You will also need the various GStreamer plugin packages to make everything @@ -33,15 +35,17 @@ extra which installs Python bindings for ``GStreamer``: pip install "beets[bpd]" -.. _GStreamer: https://gstreamer.freedesktop.org/download -.. _Homebrew: https://brew.sh +.. _gstreamer: https://gstreamer.freedesktop.org/download + +.. _homebrew: https://brew.sh Usage ----- To use the ``bpd`` plugin, first enable it in your configuration (see -:ref:`using-plugins`). -Then, you can run BPD by invoking:: +:ref:`using-plugins`). Then, you can run BPD by invoking: + +:: $ beet bpd @@ -50,15 +54,12 @@ long list of available clients`_. Here are my favorites: .. _a long list of available clients: https://mpd.wikia.com/wiki/Clients -* Linux: `gmpc`_, `Sonata`_ +- Linux: gmpc_, Sonata_ +- Mac: Theremin_ +- Windows: I don't know. Get in touch if you have a recommendation. +- iPhone/iPod touch: Rigelian_ -* Mac: `Theremin`_ - -* Windows: I don't know. Get in touch if you have a recommendation. - -* iPhone/iPod touch: `Rigelian`_ - -.. _Rigelian: https://www.rigelian.net/ +.. _rigelian: https://www.rigelian.net/ One nice thing about MPD's (and thus BPD's) client-server architecture is that the client can just as easily on a different computer from the server as it can @@ -68,21 +69,18 @@ on your headless server box. Rad! Configuration ------------- -To configure the plugin, make a ``bpd:`` section in your configuration file. -The available options are: +To configure the plugin, make a ``bpd:`` section in your configuration file. The +available options are: -- **host**: - Default: Bind to all interfaces. -- **port**: - Default: 6600 -- **password**: - Default: No password. -- **volume**: Initial volume, as a percentage. - Default: 100 -- **control_port**: Port for the internal control socket. - Default: 6601 +- **host**: Default: Bind to all interfaces. +- **port**: Default: 6600 +- **password**: Default: No password. +- **volume**: Initial volume, as a percentage. Default: 100 +- **control_port**: Port for the internal control socket. Default: 6601 -Here's an example:: +Here's an example: + +:: bpd: host: 127.0.0.1 @@ -93,49 +91,49 @@ Here's an example:: Implementation Notes -------------------- -In the real MPD, the user can browse a music directory as it appears on disk. -In beets, we like to abstract away from the directory structure. Therefore, BPD +In the real MPD, the user can browse a music directory as it appears on disk. In +beets, we like to abstract away from the directory structure. Therefore, BPD creates a "virtual" directory structure (artist/album/track) to present to -clients. This is static for now and cannot be reconfigured like the real -on-disk directory structure can. (Note that an obvious solution to this is just -string matching on items' destination, but this requires examining the entire -library Python-side for every query.) +clients. This is static for now and cannot be reconfigured like the real on-disk +directory structure can. (Note that an obvious solution to this is just string +matching on items' destination, but this requires examining the entire library +Python-side for every query.) -BPD plays music using GStreamer's ``playbin`` player, which has a simple API -but doesn't support many advanced playback features. +BPD plays music using GStreamer's ``playbin`` player, which has a simple API but +doesn't support many advanced playback features. Differences from the real MPD ----------------------------- BPD currently supports version 0.16 of `the MPD protocol`_, but several of the commands and features are "pretend" implementations or have slightly different -behaviour to their MPD equivalents. BPD aims to look enough like MPD that it -can interact with the ecosystem of clients, but doesn't try to be -a fully-fledged MPD replacement in terms of its playback capabilities. +behaviour to their MPD equivalents. BPD aims to look enough like MPD that it can +interact with the ecosystem of clients, but doesn't try to be a fully-fledged +MPD replacement in terms of its playback capabilities. -.. _the MPD protocol: https://www.musicpd.org/doc/protocol/ +.. _the mpd protocol: https://www.musicpd.org/doc/protocol/ These are some of the known differences between BPD and MPD: -* BPD doesn't currently support versioned playlists. Many clients, however, use +- BPD doesn't currently support versioned playlists. Many clients, however, use plchanges instead of playlistinfo to get the current playlist, so plchanges contains a dummy implementation that just calls playlistinfo. -* Stored playlists aren't supported (BPD understands the commands though). -* The ``stats`` command always send zero for ``playtime``, which is supposed to +- Stored playlists aren't supported (BPD understands the commands though). +- The ``stats`` command always send zero for ``playtime``, which is supposed to indicate the amount of time the server has spent playing music. BPD doesn't currently keep track of this. -* The ``update`` command regenerates the directory tree from the beets database +- The ``update`` command regenerates the directory tree from the beets database synchronously, whereas MPD does this in the background. -* Advanced playback features like cross-fade, ReplayGain and MixRamp are not +- Advanced playback features like cross-fade, ReplayGain and MixRamp are not supported due to BPD's simple audio player backend. -* Advanced query syntax is not currently supported. -* Clients can't use the ``tagtypes`` mask to hide fields. -* BPD's ``random`` mode is not deterministic and doesn't support priorities. -* Mounts and streams are not supported. BPD can only play files from disk. -* Stickers are not supported (although this is basically a flexattr in beets +- Advanced query syntax is not currently supported. +- Clients can't use the ``tagtypes`` mask to hide fields. +- BPD's ``random`` mode is not deterministic and doesn't support priorities. +- Mounts and streams are not supported. BPD can only play files from disk. +- Stickers are not supported (although this is basically a flexattr in beets nomenclature so this is feasible to add). -* There is only a single password, and is enabled it grants access to all +- There is only a single password, and is enabled it grants access to all features rather than having permissions-based granularity. -* Partitions and alternative outputs are not supported; BPD can only play one +- Partitions and alternative outputs are not supported; BPD can only play one song at a time. -* Client channels are not implemented. +- Client channels are not implemented. diff --git a/docs/plugins/bpm.rst b/docs/plugins/bpm.rst index 012c3903c..249f8f767 100644 --- a/docs/plugins/bpm.rst +++ b/docs/plugins/bpm.rst @@ -10,17 +10,21 @@ Usage To use the ``bpm`` plugin, first enable it in your configuration (see :ref:`using-plugins`). -Then, play a song you want to measure in your favorite media player and type:: +Then, play a song you want to measure in your favorite media player and type: - beet bpm <song> +:: + + beet bpm <song> You'll be prompted to press Enter three times to the rhythm. This typically allows to determine the BPM within 5% accuracy. -The plugin works best if you wrap it in a script that gets the playing song. -for instance, with ``mpc`` you can do something like:: +The plugin works best if you wrap it in a script that gets the playing song. for +instance, with ``mpc`` you can do something like: - beet bpm $(mpc |head -1|tr -d "-") +:: + + beet bpm $(mpc |head -1|tr -d "-") If :ref:`import.write <config-import-write>` is ``yes``, the song's tags are written to disk. @@ -28,14 +32,12 @@ written to disk. Configuration ------------- -To configure the plugin, make a ``bpm:`` section in your configuration file. -The available options are: +To configure the plugin, make a ``bpm:`` section in your configuration file. The +available options are: - **max_strokes**: The maximum number of strokes to accept when tapping out the - BPM. - Default: 3. -- **overwrite**: Overwrite the track's existing BPM. - Default: ``yes``. + BPM. Default: 3. +- **overwrite**: Overwrite the track's existing BPM. Default: ``yes``. Credit ------ diff --git a/docs/plugins/bpsync.rst b/docs/plugins/bpsync.rst index 29cbd08e3..6c420b61e 100644 --- a/docs/plugins/bpsync.rst +++ b/docs/plugins/bpsync.rst @@ -1,15 +1,13 @@ BPSync Plugin ============= -This plugin provides the ``bpsync`` command, which lets you fetch metadata -from Beatport for albums and tracks that already have Beatport IDs. -This plugin works similarly to :doc:`/plugins/mbsync`. - -If you have downloaded music from Beatport, this can speed -up the initial import if you just import "as-is" and then use ``bpsync`` to -get up-to-date tags that are written to the files according to your beets -configuration. +This plugin provides the ``bpsync`` command, which lets you fetch metadata from +Beatport for albums and tracks that already have Beatport IDs. This plugin works +similarly to :doc:`/plugins/mbsync`. +If you have downloaded music from Beatport, this can speed up the initial import +if you just import "as-is" and then use ``bpsync`` to get up-to-date tags that +are written to the files according to your beets configuration. Usage ----- @@ -18,17 +16,17 @@ Enable the ``bpsync`` plugin in your configuration (see :ref:`using-plugins`) and then run ``beet bpsync QUERY`` to fetch updated metadata for a part of your collection (or omit the query to run over your whole library). -This plugin treats albums and singletons (non-album tracks) separately. It -first processes all matching singletons and then proceeds on to full albums. -The same query is used to search for both kinds of entities. +This plugin treats albums and singletons (non-album tracks) separately. It first +processes all matching singletons and then proceeds on to full albums. The same +query is used to search for both kinds of entities. The command has a few command-line options: -* To preview the changes that would be made without applying them, use the +- To preview the changes that would be made without applying them, use the ``-p`` (``--pretend``) flag. -* By default, files will be moved (renamed) according to their metadata if - they are inside your beets library directory. To disable this, use the - ``-M`` (``--nomove``) command-line option. -* If you have the ``import.write`` configuration option enabled, then this - plugin will write new metadata to files' tags. To disable this, use the - ``-W`` (``--nowrite``) option. +- By default, files will be moved (renamed) according to their metadata if they + are inside your beets library directory. To disable this, use the ``-M`` + (``--nomove``) command-line option. +- If you have the ``import.write`` configuration option enabled, then this + plugin will write new metadata to files' tags. To disable this, use the ``-W`` + (``--nowrite``) option. diff --git a/docs/plugins/bucket.rst b/docs/plugins/bucket.rst index ee1857777..7d2061991 100644 --- a/docs/plugins/bucket.rst +++ b/docs/plugins/bucket.rst @@ -8,14 +8,17 @@ smaller subfolders by grouping albums or artists alphabetically (e.g. *A-F*, *G-M*, *N-Z*). To use the ``bucket`` plugin, first enable it in your configuration (see -:ref:`using-plugins`). -The plugin provides a :ref:`template function -<template-functions>` called ``%bucket`` for use in path format expressions:: +:ref:`using-plugins`). The plugin provides a :ref:`template function +<template-functions>` called ``%bucket`` for use in path format expressions: + +:: paths: default: /%bucket{$year}/%bucket{$artist}/$albumartist-$album-$year -Then, define your ranges in the ``bucket:`` section of the config file:: +Then, define your ranges in the ``bucket:`` section of the config file: + +:: bucket: bucket_alpha: ['A-F', 'G-M', 'N-Z'] @@ -30,17 +33,16 @@ The definition of a range is somewhat loose, and multiple formats are allowed: alphanumeric characters in the string you provide. For example, ``ABCD``, ``A-D``, ``A->D``, and ``[AD]`` are all equivalent. - For year ranges: digits characters are extracted and the two extreme years - define the range. For example, ``1975-77``, ``1975,76,77`` and ``1975-1977`` are - equivalent. If no upper bound is given, the range is extended to current year - (unless a later range is defined). For example, ``1975`` encompasses all years - from 1975 until now. + define the range. For example, ``1975-77``, ``1975,76,77`` and ``1975-1977`` + are equivalent. If no upper bound is given, the range is extended to current + year (unless a later range is defined). For example, ``1975`` encompasses all + years from 1975 until now. The ``%bucket`` template function guesses whether to use alpha- or year-style buckets depending on the text it receives. It can guess wrong if, for example, an artist or album happens to begin with four digits. Provide ``alpha`` as the -second argument to the template to avoid this automatic detection: for -example, use ``%bucket{$artist,alpha}``. - +second argument to the template to avoid this automatic detection: for example, +use ``%bucket{$artist,alpha}``. Configuration ------------- @@ -49,29 +51,28 @@ To configure the plugin, make a ``bucket:`` section in your configuration file. The available options are: - **bucket_alpha**: Ranges to use for all substitutions occurring on textual - fields. - Default: none. + fields. Default: none. - **bucket_alpha_regex**: A ``range: regex`` mapping (one per line) where - ``range`` is one of the `bucket_alpha` ranges and ``value`` is a regex that - overrides original range definition. - Default: none. + ``range`` is one of the ``bucket_alpha`` ranges and ``value`` is a regex that + overrides original range definition. Default: none. - **bucket_year**: Ranges to use for all substitutions occurring on the - ``$year`` field. - Default: none. + ``$year`` field. Default: none. - **extrapolate**: Enable this if you want to group your files into multiple year ranges without enumerating them all. This option will generate year - bucket names by reproducing characteristics of declared buckets. - Default: ``no`` + bucket names by reproducing characteristics of declared buckets. Default: + ``no`` -Here's an example:: +Here's an example: - bucket: - bucket_year: ['2000-05'] - extrapolate: true - bucket_alpha: ['A - D', 'E - L', 'M - R', 'S - Z'] - bucket_alpha_regex: - 'A - D': ^[0-9a-dA-D…äÄ] +:: -This configuration creates five-year ranges for any input year. -The `A - D` bucket now matches also all artists starting with ä or Ä and 0 to 9 -and … (ellipsis). The other alpha buckets work as ranges. + bucket: + bucket_year: ['2000-05'] + extrapolate: true + bucket_alpha: ['A - D', 'E - L', 'M - R', 'S - Z'] + bucket_alpha_regex: + 'A - D': ^[0-9a-dA-D…äÄ] + +This configuration creates five-year ranges for any input year. The ``A - D`` +bucket now matches also all artists starting with ä or Ä and 0 to 9 and … +(ellipsis). The other alpha buckets work as ranges. diff --git a/docs/plugins/chroma.rst b/docs/plugins/chroma.rst index 30d57939f..4e333ab99 100644 --- a/docs/plugins/chroma.rst +++ b/docs/plugins/chroma.rst @@ -4,18 +4,19 @@ Chromaprint/Acoustid Plugin Acoustic fingerprinting is a technique for identifying songs from the way they "sound" rather from their existing metadata. That means that beets' autotagger can theoretically use fingerprinting to tag files that don't have any ID3 -information at all (or have completely incorrect data). This plugin uses an -open-source fingerprinting technology called `Chromaprint`_ and its associated -Web service, called `Acoustid`_. +information at all (or have completely incorrect data). This plugin uses an +open-source fingerprinting technology called Chromaprint_ and its associated Web +service, called Acoustid_. -.. _Chromaprint: https://acoustid.org/chromaprint .. _acoustid: https://acoustid.org/ +.. _chromaprint: https://acoustid.org/chromaprint + Turning on fingerprinting can increase the accuracy of the autotagger---especially on files with very poor metadata---but it comes at a cost. First, it can be trickier to set up than beets itself (you need to set up the native fingerprinting library, whereas all of the beets core is written in -pure Python). Also, fingerprinting takes significantly more CPU and memory than +pure Python). Also, fingerprinting takes significantly more CPU and memory than ordinary tagging---which means that imports will go substantially slower. If you're willing to pay the performance cost for fingerprinting, read on! @@ -23,80 +24,83 @@ If you're willing to pay the performance cost for fingerprinting, read on! Installing Dependencies ----------------------- -To get fingerprinting working, you'll need to install three things: +To get fingerprinting working, you'll need to install three things: -1. `pyacoustid`_ Python library (version 0.6 or later). You can install it by +1. pyacoustid_ Python library (version 0.6 or later). You can install it by installing ``beets`` with ``chroma`` extra .. code-block:: bash pip install "beets[chroma]" -2. the `Chromaprint`_ library_ or |command-line-tool|_ +2. the Chromaprint_ library_ or |command-line-tool|_ 3. an |audio-decoder|_ .. |command-line-tool| replace:: command line tool -.. |audio-decoder| replace:: audio decoder +.. |audio-decoder| replace:: audio decoder .. _command-line-tool: Installing the Binary Command-Line Tool -''''''''''''''''''''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The simplest way to get up and running, especially on Windows, is to -`download`_ the appropriate Chromaprint binary package and place the -``fpcalc`` (or ``fpcalc.exe``) on your shell search path. On Windows, this -means something like ``C:\\Program Files``. On OS X or Linux, put the -executable somewhere like ``/usr/local/bin``. +The simplest way to get up and running, especially on Windows, is to download_ +the appropriate Chromaprint binary package and place the ``fpcalc`` (or +``fpcalc.exe``) on your shell search path. On Windows, this means something like +``C:\\Program Files``. On OS X or Linux, put the executable somewhere like +``/usr/local/bin``. .. _download: https://acoustid.org/chromaprint .. _library: Installing the Library -'''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~ -On OS X and Linux, you can also use a library installed by your package -manager, which has some advantages (automatic upgrades, etc.). The Chromaprint -site has links to packages for major Linux distributions. If you use -`Homebrew`_ on Mac OS X, you can install the library with ``brew install -chromaprint``. - -.. _Homebrew: https://brew.sh/ +On OS X and Linux, you can also use a library installed by your package manager, +which has some advantages (automatic upgrades, etc.). The Chromaprint site has +links to packages for major Linux distributions. If you use Homebrew_ on Mac OS +X, you can install the library with ``brew install chromaprint``. .. _audio-decoder: +.. _homebrew: https://brew.sh/ + Audio Decoder -''''''''''''' +~~~~~~~~~~~~~ You will also need a mechanism for decoding audio files supported by the -`audioread`_ library: +audioread_ library: -* OS X has a number of decoders already built into Core Audio, so there's no +- OS X has a number of decoders already built into Core Audio, so there's no need to install anything. - -* On Linux, you can install `GStreamer`_ with `PyGObject`_, `FFmpeg`_, or - `MAD`_ with `pymad`_. How you install these will depend on your - distribution. - For example, on Ubuntu, run ``apt-get install gstreamer1.0 python-gi``. On - Arch Linux, you want ``pacman -S gstreamer python2-gobject``. If you use - GStreamer, be sure to install its codec plugins also (``gst-plugins-good``, - etc.). +- On Linux, you can install GStreamer_ with PyGObject_, FFmpeg_, or MAD_ with + pymad_. How you install these will depend on your distribution. For example, + on Ubuntu, run ``apt-get install gstreamer1.0 python-gi``. On Arch Linux, you + want ``pacman -S gstreamer python2-gobject``. If you use GStreamer, be sure to + install its codec plugins also (``gst-plugins-good``, etc.). Note that if you install beets in a virtualenv, you'll need it to have ``--system-site-packages`` enabled for Python to see the GStreamer bindings. -* On Windows, builds are provided by `GStreamer`_ +- On Windows, builds are provided by GStreamer_ .. _audioread: https://github.com/beetbox/audioread + +.. _core audio: https://developer.apple.com/technologies/mac/audio-and-video.html + +.. _ffmpeg: https://ffmpeg.org/ + +.. _gstreamer: https://gstreamer.freedesktop.org/ + +.. _mad: https://www.underbit.com/products/mad/ + .. _pyacoustid: https://github.com/beetbox/pyacoustid -.. _FFmpeg: https://ffmpeg.org/ + +.. _pygobject: https://wiki.gnome.org/Projects/PyGObject + .. _pymad: https://spacepants.org/src/pymad/ -.. _MAD: https://www.underbit.com/products/mad/ -.. _Core Audio: https://developer.apple.com/technologies/mac/audio-and-video.html -.. _Gstreamer: https://gstreamer.freedesktop.org/ -.. _PyGObject: https://wiki.gnome.org/Projects/PyGObject To decode audio formats (MP3, FLAC, etc.) with GStreamer, you'll need the standard set of Gstreamer plugins. For example, on Ubuntu, install the packages @@ -107,16 +111,16 @@ Usage ----- Once you have all the dependencies sorted out, enable the ``chroma`` plugin in -your configuration (see :ref:`using-plugins`) to benefit from fingerprinting -the next time you run ``beet import``. (The plugin doesn't produce any obvious -output by default. If you want to confirm that it's enabled, you can try -running in verbose mode once with ``beet -v import``.) +your configuration (see :ref:`using-plugins`) to benefit from fingerprinting the +next time you run ``beet import``. (The plugin doesn't produce any obvious +output by default. If you want to confirm that it's enabled, you can try running +in verbose mode once with ``beet -v import``.) You can also use the ``beet fingerprint`` command to generate fingerprints for items already in your library. (Provide a query to fingerprint a subset of your -library.) The generated fingerprints will be stored in the library database. -If you have the ``import.write`` config option enabled, they will also be -written to files' metadata. +library.) The generated fingerprints will be stored in the library database. If +you have the ``import.write`` config option enabled, they will also be written +to files' metadata. .. _submitfp: @@ -125,7 +129,9 @@ Configuration There is one configuration option in the ``chroma:`` section, ``auto``, which controls whether to fingerprint files during the import process. To disable -fingerprint-based autotagging, set it to ``no``, like so:: +fingerprint-based autotagging, set it to ``no``, like so: + +:: chroma: auto: no @@ -133,11 +139,13 @@ fingerprint-based autotagging, set it to ``no``, like so:: Submitting Fingerprints ----------------------- -You can help expand the `Acoustid`_ database by submitting fingerprints for the +You can help expand the Acoustid_ database by submitting fingerprints for the music in your collection. To do this, first `get an API key`_ from the Acoustid service. Just use an OpenID or MusicBrainz account to log in and you'll get a -short token string. Then, add the key to your ``config.yaml`` as the -value ``apikey`` in a section called ``acoustid`` like so:: +short token string. Then, add the key to your ``config.yaml`` as the value +``apikey`` in a section called ``acoustid`` like so: + +:: acoustid: apikey: AbCd1234 @@ -146,4 +154,4 @@ Then, run ``beet submit``. (You can also provide a query to submit a subset of your library.) The command will use stored fingerprints if they're available; otherwise it will fingerprint each file before submitting it. -.. _get an API key: https://acoustid.org/api-key +.. _get an api key: https://acoustid.org/api-key diff --git a/docs/plugins/convert.rst b/docs/plugins/convert.rst index a41e6c529..8917422c5 100644 --- a/docs/plugins/convert.rst +++ b/docs/plugins/convert.rst @@ -1,44 +1,40 @@ Convert Plugin ============== -The ``convert`` plugin lets you convert parts of your collection to a -directory of your choice, transcoding audio and embedding album art along the -way. It can transcode to and from any format using a configurable command -line. Optionally an m3u playlist file containing all the converted files can be -saved to the destination path. - +The ``convert`` plugin lets you convert parts of your collection to a directory +of your choice, transcoding audio and embedding album art along the way. It can +transcode to and from any format using a configurable command line. Optionally +an m3u playlist file containing all the converted files can be saved to the +destination path. Installation ------------ To use the ``convert`` plugin, first enable it in your configuration (see -:ref:`using-plugins`). By default, the plugin depends on `FFmpeg`_ to -transcode the audio, so you might want to install it. - -.. _FFmpeg: https://ffmpeg.org +:ref:`using-plugins`). By default, the plugin depends on FFmpeg_ to transcode +the audio, so you might want to install it. +.. _ffmpeg: https://ffmpeg.org Usage ----- -To convert a part of your collection, run ``beet convert QUERY``. The -command will transcode all the files matching the query to the -destination directory given by the ``-d`` (``--dest``) option or the -``dest`` configuration. The path layout mirrors that of your library, -but it may be customized through the ``paths`` configuration. Files -that have been previously converted---and thus already exist in the -destination directory---will be skipped. +To convert a part of your collection, run ``beet convert QUERY``. The command +will transcode all the files matching the query to the destination directory +given by the ``-d`` (``--dest``) option or the ``dest`` configuration. The path +layout mirrors that of your library, but it may be customized through the +``paths`` configuration. Files that have been previously converted---and thus +already exist in the destination directory---will be skipped. -The plugin uses a command-line program to transcode the audio. With the -``-f`` (``--format``) option you can choose the transcoding command -and customize the available commands -:ref:`through the configuration <convert-format-config>`. +The plugin uses a command-line program to transcode the audio. With the ``-f`` +(``--format``) option you can choose the transcoding command and customize the +available commands :ref:`through the configuration <convert-format-config>`. -Unless the ``-y`` (``--yes``) flag is set, the command will list all -the items to be converted and ask for your confirmation. +Unless the ``-y`` (``--yes``) flag is set, the command will list all the items +to be converted and ask for your confirmation. -The ``-a`` (or ``--album``) option causes the command -to match albums instead of tracks. +The ``-a`` (or ``--album``) option causes the command to match albums instead of +tracks. By default, the command places converted files into the destination directory and leaves your library pristine. To instead back up your original files into @@ -51,18 +47,18 @@ them. By default, files that do not need to be transcoded will be copied to their destination. Passing the ``-l`` (``--link``) flag creates symbolic links -instead, passing ``-H`` (``--hardlink``) creates hard links. -Note that album art embedding is disabled for files that are linked. -Refer to the ``link`` and ``hardlink`` options below. +instead, passing ``-H`` (``--hardlink``) creates hard links. Note that album art +embedding is disabled for files that are linked. Refer to the ``link`` and +``hardlink`` options below. The ``-m`` (or ``--playlist``) option enables the plugin to create an m3u8 playlist file in the destination folder given by the ``-d`` (``--dest``) option or the ``dest`` configuration. The path to the playlist file can either be absolute or relative to the ``dest`` directory. The contents will always be relative paths to media files, which tries to ensure compatibility when read -from external drives or on computers other than the one used for the -conversion. There is one caveat though: A list generated on Unix/macOS can't be -read on Windows and vice versa. +from external drives or on computers other than the one used for the conversion. +There is one caveat though: A list generated on Unix/macOS can't be read on +Windows and vice versa. Depending on the beets user's settings a generated playlist potentially could contain unicode characters. This is supported, playlists are written in `M3U8 @@ -71,31 +67,31 @@ format`_. Configuration ------------- -To configure the plugin, make a ``convert:`` section in your configuration -file. The available options are: +To configure the plugin, make a ``convert:`` section in your configuration file. +The available options are: - **auto**: Import transcoded versions of your files automatically during imports. With this option enabled, the importer will transcode all (in the default configuration) non-MP3 files over the maximum bitrate before adding - them to your library. - Default: ``no``. + them to your library. Default: ``no``. - **auto_keep**: Convert your files automatically on import to **dest** but - import the non transcoded version. It uses the default format you have - defined in your config file. - Default: ``no``. + import the non transcoded version. It uses the default format you have defined + in your config file. Default: ``no``. - .. note:: You probably want to use only one of the `auto` and `auto_keep` - options, not both. Enabling both will convert your files twice on import, - which you probably don't want. + .. note:: + + You probably want to use only one of the ``auto`` and ``auto_keep`` + options, not both. Enabling both will convert your files twice on import, + which you probably don't want. - **tmpdir**: The directory where temporary files will be stored during import. Default: none (system default), - **copy_album_art**: Copy album art when copying or transcoding albums matched using the ``-a`` option. Default: ``no``. - **album_art_maxwidth**: Downscale album art if it's too big. The resize - operation reduces image width to at most ``maxwidth`` pixels while - preserving the aspect ratio. The specified image size will apply to both - embedded album art and external image files. + operation reduces image width to at most ``maxwidth`` pixels while preserving + the aspect ratio. The specified image size will apply to both embedded album + art and external image files. - **dest**: The directory where the files will be converted (or copied) to. Default: none. - **embed**: Embed album art in converted items. Default: ``yes``. @@ -103,64 +99,61 @@ file. The available options are: ``inherit``. - **max_bitrate**: By default, the plugin does not transcode files that are already in the destination format. This option instead also transcodes files - with high bitrates, even if they are already in the same format as the - output. Note that this does not guarantee that all converted files will have - a lower bitrate---that depends on the encoder and its configuration. - Default: none. + with high bitrates, even if they are already in the same format as the output. + Note that this does not guarantee that all converted files will have a lower + bitrate---that depends on the encoder and its configuration. Default: none. - **no_convert**: Does not transcode items matching the query string provided - (see :doc:`/reference/query`). For example, to not convert AAC or WMA formats, you can use ``format:AAC, format:WMA`` or - ``path::\.(m4a|wma)$``. If you only want to transcode WMA format, you can use a negative query, e.g., ``^path::\.(wma)$``, to not convert any other format except WMA. + (see :doc:`/reference/query`). For example, to not convert AAC or WMA formats, + you can use ``format:AAC, format:WMA`` or ``path::\.(m4a|wma)$``. If you only + want to transcode WMA format, you can use a negative query, e.g., + ``^path::\.(wma)$``, to not convert any other format except WMA. - **never_convert_lossy_files**: Cross-conversions between lossy codecs---such as mp3, ogg vorbis, etc.---makes little sense as they will decrease quality - even further. If set to ``yes``, lossy files are always copied. - Default: ``no``. -- **paths**: The directory structure and naming scheme for the converted - files. Uses the same format as the top-level ``paths`` section (see - :ref:`path-format-config`). - Default: Reuse your top-level path format settings. + even further. If set to ``yes``, lossy files are always copied. Default: + ``no``. +- **paths**: The directory structure and naming scheme for the converted files. + Uses the same format as the top-level ``paths`` section (see + :ref:`path-format-config`). Default: Reuse your top-level path format + settings. - **quiet**: Prevent the plugin from announcing every file it processes. Default: ``false``. -- **threads**: The number of threads to use for parallel encoding. - By default, the plugin will detect the number of processors available and use - them all. +- **threads**: The number of threads to use for parallel encoding. By default, + the plugin will detect the number of processors available and use them all. - **link**: By default, files that do not need to be transcoded will be copied to their destination. This option creates symbolic links instead. Note that options such as ``embed`` that modify the output files after the transcoding step will cause the original files to be modified as well if ``link`` is - enabled. For this reason, album-art embedding is disabled - for files that are linked. - Default: ``false``. -- **hardlink**: This options works similar to ``link``, but it creates - hard links instead of symlinks. - This option overrides ``link``. Only works when converting to a directory - on the same filesystem as the library. - Default: ``false``. -- **delete_originals**: Transcoded files will be copied or moved to their destination, depending on the import configuration. By default, the original files are not modified by the plugin. This option deletes the original files after the transcoding step has completed. - Default: ``false``. + enabled. For this reason, album-art embedding is disabled for files that are + linked. Default: ``false``. +- **hardlink**: This options works similar to ``link``, but it creates hard + links instead of symlinks. This option overrides ``link``. Only works when + converting to a directory on the same filesystem as the library. Default: + ``false``. +- **delete_originals**: Transcoded files will be copied or moved to their + destination, depending on the import configuration. By default, the original + files are not modified by the plugin. This option deletes the original files + after the transcoding step has completed. Default: ``false``. - **playlist**: The name of a playlist file that should be written on each run - of the plugin. A relative file path (e.g `playlists/mylist.m3u8`) is allowed - as well. The final destination of the playlist file will always be relative - to the destination path (``dest``, ``--dest``, ``-d``). This configuration is - overridden by the ``-m`` (``--playlist``) command line option. - Default: none. + of the plugin. A relative file path (e.g ``playlists/mylist.m3u8``) is allowed + as well. The final destination of the playlist file will always be relative to + the destination path (``dest``, ``--dest``, ``-d``). This configuration is + overridden by the ``-m`` (``--playlist``) command line option. Default: none. -You can also configure the format to use for transcoding (see the next -section): +You can also configure the format to use for transcoding (see the next section): - **format**: The name of the format to transcode to when none is specified on - the command line. - Default: ``mp3``. + the command line. Default: ``mp3``. - **formats**: A set of formats and associated command lines for transcoding each. .. _convert-format-config: Configuring the transcoding command -``````````````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can customize the transcoding command through the ``formats`` map -and select a command with the ``--format`` command-line option or the -``format`` configuration. +You can customize the transcoding command through the ``formats`` map and select +a command with the ``--format`` command-line option or the ``format`` +configuration. :: @@ -172,25 +165,25 @@ and select a command with the ``--format`` command-line option or the extension: spx wav: ffmpeg -i $source -y -acodec pcm_s16le $dest -In this example ``beet convert`` will use the *speex* command by -default. To convert the audio to `wav`, run ``beet convert -f wav``. -This will also use the format key (``wav``) as the file extension. +In this example ``beet convert`` will use the *speex* command by default. To +convert the audio to ``wav``, run ``beet convert -f wav``. This will also use +the format key (``wav``) as the file extension. -Each entry in the ``formats`` map consists of a key (the name of the -format) as well as the command and optionally the file extension. -``extension`` is the filename extension to be used for newly transcoded -files. If only the command is given as a string or the extension is not -provided, the file extension defaults to the format's name. ``command`` is the -command to use to transcode audio. The tokens ``$source`` and ``$dest`` in the -command are replaced with the paths to the existing and new file. +Each entry in the ``formats`` map consists of a key (the name of the format) as +well as the command and optionally the file extension. ``extension`` is the +filename extension to be used for newly transcoded files. If only the command is +given as a string or the extension is not provided, the file extension defaults +to the format's name. ``command`` is the command to use to transcode audio. The +tokens ``$source`` and ``$dest`` in the command are replaced with the paths to +the existing and new file. -The plugin in comes with default commands for the most common audio -formats: `mp3`, `alac`, `flac`, `aac`, `opus`, `ogg`, `wma`. For -details have a look at the output of ``beet config -d``. +The plugin in comes with default commands for the most common audio formats: +``mp3``, ``alac``, ``flac``, ``aac``, ``opus``, ``ogg``, ``wma``. For details +have a look at the output of ``beet config -d``. For a one-command-fits-all solution use the ``convert.command`` and -``convert.extension`` options. If these are set, the formats are ignored -and the given command is used for all conversions. +``convert.extension`` options. If these are set, the formats are ignored and the +given command is used for all conversions. :: @@ -198,31 +191,38 @@ and the given command is used for all conversions. command: ffmpeg -i $source -y -vn -aq 2 $dest extension: mp3 - Gapless MP3 encoding -```````````````````` +~~~~~~~~~~~~~~~~~~~~ -While FFmpeg cannot produce "`gapless`_" MP3s by itself, you can create them -by using `LAME`_ directly. Use a shell script like this to pipe the output of -FFmpeg into the LAME tool:: +While FFmpeg cannot produce "gapless_" MP3s by itself, you can create them by +using LAME_ directly. Use a shell script like this to pipe the output of FFmpeg +into the LAME tool: + +:: #!/bin/sh ffmpeg -i "$1" -f wav - | lame -V 2 --noreplaygain - "$2" -Then configure the ``convert`` plugin to use the script:: +Then configure the ``convert`` plugin to use the script: + +:: convert: command: /path/to/script.sh $source $dest extension: mp3 This strategy configures FFmpeg to produce a WAV file with an accurate length -header for LAME to use. Using ``--noreplaygain`` disables gain analysis; you -can use the :doc:`/plugins/replaygain` to do this analysis. See the LAME -`documentation`_ and the `HydrogenAudio wiki`_ for other LAME configuration +header for LAME to use. Using ``--noreplaygain`` disables gain analysis; you can +use the :doc:`/plugins/replaygain` to do this analysis. See the LAME +documentation_ and the `HydrogenAudio wiki`_ for other LAME configuration options and a thorough discussion of MP3 encoding. .. _documentation: https://lame.sourceforge.io/index.php -.. _HydrogenAudio wiki: https://wiki.hydrogenaud.io/index.php?title=LAME + .. _gapless: https://wiki.hydrogenaud.io/index.php?title=Gapless_playback -.. _LAME: https://lame.sourceforge.io/index.php -.. _M3U8 format: https://en.wikipedia.org/wiki/M3U#M3U8 + +.. _hydrogenaudio wiki: https://wiki.hydrogenaud.io/index.php?title=LAME + +.. _lame: https://lame.sourceforge.io/index.php + +.. _m3u8 format: https://en.wikipedia.org/wiki/M3U#M3U8 diff --git a/docs/plugins/deezer.rst b/docs/plugins/deezer.rst index 9f8da41fd..e58252e84 100644 --- a/docs/plugins/deezer.rst +++ b/docs/plugins/deezer.rst @@ -1,20 +1,24 @@ Deezer Plugin -============== +============= The ``deezer`` plugin provides metadata matches for the importer using the -`Deezer`_ `Album`_ and `Track`_ APIs. +Deezer_ Album_ and Track_ APIs. -.. _Deezer: https://www.deezer.com -.. _Album: https://developers.deezer.com/api/album -.. _Track: https://developers.deezer.com/api/track +.. _album: https://developers.deezer.com/api/album + +.. _deezer: https://www.deezer.com + +.. _track: https://developers.deezer.com/api/track Basic Usage ----------- First, enable the ``deezer`` plugin (see :ref:`using-plugins`). -You can enter the URL for an album or song on Deezer at the ``enter Id`` -prompt during import:: +You can enter the URL for an album or song on Deezer at the ``enter Id`` prompt +during import: + +:: Enter search, enter Id, aBort, eDit, edit Candidates, plaY? i Enter release ID: https://www.deezer.com/en/album/572261 @@ -22,6 +26,10 @@ prompt during import:: Configuration ------------- -This plugin can be configured like other metadata source plugins as described in :ref:`metadata-source-plugin-configuration`. +This plugin can be configured like other metadata source plugins as described in +:ref:`metadata-source-plugin-configuration`. -The ``deezer`` plugin provides an additional command ``deezerupdate`` to update the ``rank`` information from Deezer. The ``rank`` (ranges from 0 to 1M) is a global indicator of a song's popularity on Deezer that is updated daily based on streams. The higher the ``rank``, the more popular the track is. +The ``deezer`` plugin provides an additional command ``deezerupdate`` to update +the ``rank`` information from Deezer. The ``rank`` (ranges from 0 to 1M) is a +global indicator of a song's popularity on Deezer that is updated daily based on +streams. The higher the ``rank``, the more popular the track is. diff --git a/docs/plugins/discogs.rst b/docs/plugins/discogs.rst index c8df12a41..e6b93961a 100644 --- a/docs/plugins/discogs.rst +++ b/docs/plugins/discogs.rst @@ -1,15 +1,15 @@ Discogs Plugin ============== -The ``discogs`` plugin extends the autotagger's search capabilities to -include matches from the `Discogs`_ database. +The ``discogs`` plugin extends the autotagger's search capabilities to include +matches from the Discogs_ database. -Files can be imported as albums or as singletons. Since `Discogs`_ matches are -always based on `Discogs`_ releases, the album tag is written even to -singletons. This enhances the importers results when reimporting as (full or -partial) albums later on. +Files can be imported as albums or as singletons. Since Discogs_ matches are +always based on Discogs_ releases, the album tag is written even to singletons. +This enhances the importers results when reimporting as (full or partial) albums +later on. -.. _Discogs: https://discogs.com +.. _discogs: https://discogs.com Installation ------------ @@ -21,7 +21,7 @@ To use the ``discogs`` plugin, first enable it in your configuration (see pip install "beets[discogs]" -You will also need to register for a `Discogs`_ account, and provide +You will also need to register for a Discogs_ account, and provide authentication credentials via a personal access token or an OAuth2 authorization. @@ -29,59 +29,56 @@ Matches from Discogs will now show up during import alongside matches from MusicBrainz. The search terms sent to the Discogs API are based on the artist and album tags of your tracks. If those are empty no query will be issued. -If you have a Discogs ID for an album you want to tag, you can also enter it -at the "enter Id" prompt in the importer. +If you have a Discogs ID for an album you want to tag, you can also enter it at +the "enter Id" prompt in the importer. OAuth Authorization -``````````````````` +~~~~~~~~~~~~~~~~~~~ The first time you run the :ref:`import-cmd` command after enabling the plugin, it will ask you to authorize with Discogs by visiting the site in a browser. Subsequent runs will not require re-authorization. Authentication via Personal Access Token -```````````````````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -As an alternative to OAuth, you can get a token from Discogs and add it to -your configuration. -To get a personal access token (called a "user token" in the `python3-discogs-client`_ -documentation): +As an alternative to OAuth, you can get a token from Discogs and add it to your +configuration. To get a personal access token (called a "user token" in the +python3-discogs-client_ documentation): -#. login to `Discogs`_; -#. visit the `Developer settings page <https://www.discogs.com/settings/developers>`_; -#. press the *Generate new token* button; -#. copy the generated token; -#. place it in your configuration in the ``discogs`` section as the ``user_token`` option: +1. login to Discogs_; +2. visit the `Developer settings page + <https://www.discogs.com/settings/developers>`_; +3. press the *Generate new token* button; +4. copy the generated token; +5. place it in your configuration in the ``discogs`` section as the + ``user_token`` option: .. code-block:: yaml - discogs: - user_token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - + discogs: + user_token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" Configuration ------------- -This plugin can be configured like other metadata source plugins as described in :ref:`metadata-source-plugin-configuration`. +This plugin can be configured like other metadata source plugins as described in +:ref:`metadata-source-plugin-configuration`. There is one additional option in the ``discogs:`` section, ``index_tracks``. -Index tracks (see the `Discogs guidelines -<https://support.discogs.com/hc/en-us/articles/360005055373-Database-Guidelines-12-Tracklisting#Index_Tracks_And_Headings>`_), -along with headers, mark divisions between distinct works on the same release -or within works. When ``index_tracks`` is enabled: +Index tracks (see the `Discogs guidelines`_) along with headers, mark divisions +between distinct works on the same release or within works. When +``index_tracks`` is enabled: .. code-block:: yaml discogs: index_tracks: yes -beets will incorporate the names of the divisions containing each track into -the imported track's title. +beets will incorporate the names of the divisions containing each track into the +imported track's title. -For example, importing -`this album -<https://www.discogs.com/Handel-Sutherland-Kirkby-Kwella-Nelson-Watkinson-Bowman-Rolfe-Johnson-Elliott-Partridge-Thomas-The-A/release/2026070>`_ -would result in track names like: +For example, importing `divisions album`_ would result in track names like: .. code-block:: text @@ -101,21 +98,21 @@ This option is useful when importing classical music. Other configurations available under ``discogs:`` are: -- **append_style_genre**: Appends the Discogs style (if found) to the genre - tag. This can be useful if you want more granular genres to categorize your - music. For example, a release in Discogs might have a genre of "Electronic" - and a style of "Techno": enabling this setting would set the genre to be +- **append_style_genre**: Appends the Discogs style (if found) to the genre tag. + This can be useful if you want more granular genres to categorize your music. + For example, a release in Discogs might have a genre of "Electronic" and a + style of "Techno": enabling this setting would set the genre to be "Electronic, Techno" (assuming default separator of ``", "``) instead of just - "Electronic". - Default: ``False`` -- **separator**: How to join multiple genre and style values from Discogs into - a string. - Default: ``", "`` -- **search_limit**: The maximum number of results to return from Discogs. This is - useful if you want to limit the number of results returned to speed up - searches. - Default: ``5`` + "Electronic". Default: ``False`` +- **separator**: How to join multiple genre and style values from Discogs into a + string. Default: ``", "`` +- **search_limit**: The maximum number of results to return from Discogs. This + is useful if you want to limit the number of results returned to speed up + searches. Default: ``5`` +.. _discogs guidelines: https://support.discogs.com/hc/en-us/articles/360005055373-Database-Guidelines-12-Tracklisting#Index_Tracks_And_Headings + +.. _divisions album: https://www.discogs.com/Handel-Sutherland-Kirkby-Kwella-Nelson-Watkinson-Bowman-Rolfe-Johnson-Elliott-Partridge-Thomas-The-A/release/2026070 Troubleshooting --------------- @@ -126,12 +123,13 @@ please start by searching for `a similar issue on the repo Here are two things you can try: -* Try deleting the token file (``~/.config/beets/discogs_token.json`` by +- Try deleting the token file (``~/.config/beets/discogs_token.json`` by default) to force re-authorization. -* Make sure that your system clock is accurate. The Discogs servers can reject +- Make sure that your system clock is accurate. The Discogs servers can reject your request if your clock is too out of sync. Matching tracks by Discogs ID is not yet supported. The ``--group-albums`` -option in album import mode provides an alternative to singleton mode for autotagging tracks that are not in album-related folders. +option in album import mode provides an alternative to singleton mode for +autotagging tracks that are not in album-related folders. .. _python3-discogs-client: https://github.com/joalla/discogs_client diff --git a/docs/plugins/duplicates.rst b/docs/plugins/duplicates.rst index 8ce0e4578..4580343de 100644 --- a/docs/plugins/duplicates.rst +++ b/docs/plugins/duplicates.rst @@ -1,8 +1,8 @@ Duplicates Plugin ================= -This plugin adds a new command, ``duplicates`` or ``dup``, which finds -and lists duplicate tracks or albums in your collection. +This plugin adds a new command, ``duplicates`` or ``dup``, which finds and lists +duplicate tracks or albums in your collection. Usage ----- @@ -10,31 +10,31 @@ Usage To use the ``duplicates`` plugin, first enable it in your configuration (see :ref:`using-plugins`). -By default, the ``beet duplicates`` command lists the names of tracks -in your library that are duplicates. It assumes that Musicbrainz track -and album ids are unique to each track or album. That is, it lists -every track or album with an ID that has been seen before in the -library. -You can customize the output format, count the number of duplicate -tracks or albums, and list all tracks that have duplicates or just the -duplicates themselves via command-line switches :: +By default, the ``beet duplicates`` command lists the names of tracks in your +library that are duplicates. It assumes that Musicbrainz track and album ids are +unique to each track or album. That is, it lists every track or album with an ID +that has been seen before in the library. You can customize the output format, +count the number of duplicate tracks or albums, and list all tracks that have +duplicates or just the duplicates themselves via command-line switches - -h, --help show this help message and exit - -f FMT, --format=FMT print with custom format - -a, --album show duplicate albums instead of tracks - -c, --count count duplicate tracks or albums - -C PROG, --checksum=PROG - report duplicates based on arbitrary command - -d, --delete delete items from library and disk - -F, --full show all versions of duplicate tracks or albums - -s, --strict report duplicates only if all attributes are set - -k, --key report duplicates based on keys (can be used multiple times) - -M, --merge merge duplicate items - -m DEST, --move=DEST move items to dest - -o DEST, --copy=DEST copy items to dest - -p, --path print paths for matched items or albums - -t TAG, --tag=TAG tag matched items with 'k=v' attribute - -r, --remove remove items from library +:: + + -h, --help show this help message and exit + -f FMT, --format=FMT print with custom format + -a, --album show duplicate albums instead of tracks + -c, --count count duplicate tracks or albums + -C PROG, --checksum=PROG + report duplicates based on arbitrary command + -d, --delete delete items from library and disk + -F, --full show all versions of duplicate tracks or albums + -s, --strict report duplicates only if all attributes are set + -k, --key report duplicates based on keys (can be used multiple times) + -M, --merge merge duplicate items + -m DEST, --move=DEST move items to dest + -o DEST, --copy=DEST copy items to dest + -p, --path print paths for matched items or albums + -t TAG, --tag=TAG tag matched items with 'k=v' attribute + -r, --remove remove items from library Configuration ------------- @@ -42,117 +42,132 @@ Configuration To configure the plugin, make a ``duplicates:`` section in your configuration file. The available options mirror the command-line options: -- **album**: List duplicate albums instead of tracks. - Default: ``no``. -- **checksum**: Use an arbitrary command to compute a checksum - of items. This overrides the ``keys`` option the first time it is run; - however, because it caches the resulting checksum as ``flexattrs`` in the - database, you can use ``--key=name_of_the_checksumming_program - --key=any_other_keys`` (or set the ``keys`` configuration option) the second - time around. - Default: ``ffmpeg -i {file} -f crc -``. -- **copy**: A destination base directory into which to copy matched - items. +- **album**: List duplicate albums instead of tracks. Default: ``no``. +- **checksum**: Use an arbitrary command to compute a checksum of items. This + overrides the ``keys`` option the first time it is run; however, because it + caches the resulting checksum as ``flexattrs`` in the database, you can use + ``--key=name_of_the_checksumming_program --key=any_other_keys`` (or set the + ``keys`` configuration option) the second time around. Default: ``ffmpeg -i + {file} -f crc -``. +- **copy**: A destination base directory into which to copy matched items. Default: none (disabled). - **count**: Print a count of duplicate tracks or albums in the format ``$albumartist - $album - $title: $count`` (for tracks) or ``$albumartist - - $album: $count`` (for albums). - Default: ``no``. -- **delete**: Remove matched items from the library and from the disk. - Default: ``no`` -- **format**: A specific format with which to print every track - or album. This uses the same template syntax as beets' - :doc:`path formats</reference/pathformat>`. The usage is inspired by, and - therefore similar to, the :ref:`list <list-cmd>` command. - Default: :ref:`format_item` + $album: $count`` (for albums). Default: ``no``. +- **delete**: Remove matched items from the library and from the disk. Default: + ``no`` +- **format**: A specific format with which to print every track or album. This + uses the same template syntax as beets' :doc:`path + formats</reference/pathformat>`. The usage is inspired by, and therefore + similar to, the :ref:`list <list-cmd>` command. Default: :ref:`format_item` - **full**: List every track or album that has duplicates, not just the - duplicates themselves. - Default: ``no`` -- **keys**: Define in which track or album fields duplicates are to be - searched. By default, the plugin uses the musicbrainz track and album IDs for - this purpose. Using the ``keys`` option (as a YAML list in the configuration - file, or as space-delimited strings in the command-line), you can extend this - behavior to consider other attributes. - Default: ``[mb_trackid, mb_albumid]`` -- **merge**: Merge duplicate items by consolidating tracks and-or - metadata where possible. -- **move**: A destination base directory into which it will move matched - items. + duplicates themselves. Default: ``no`` +- **keys**: Define in which track or album fields duplicates are to be searched. + By default, the plugin uses the musicbrainz track and album IDs for this + purpose. Using the ``keys`` option (as a YAML list in the configuration file, + or as space-delimited strings in the command-line), you can extend this + behavior to consider other attributes. Default: ``[mb_trackid, mb_albumid]`` +- **merge**: Merge duplicate items by consolidating tracks and-or metadata where + possible. +- **move**: A destination base directory into which it will move matched items. Default: none (disabled). - **path**: Output the path instead of metadata when listing duplicates. Default: ``no``. -- **strict**: Do not report duplicate matches if some of the - attributes are not defined (ie. null or empty). - Default: ``no`` +- **strict**: Do not report duplicate matches if some of the attributes are not + defined (ie. null or empty). Default: ``no`` - **tag**: A ``key=value`` pair. The plugin will add a new ``key`` attribute with ``value`` value as a flexattr to the database for duplicate items. Default: ``no``. -- **tiebreak**: Dictionary of lists of attributes keyed by ``items`` - or ``albums`` to use when choosing duplicates. By default, the - tie-breaking procedure favors the most complete metadata attribute - set. If you would like to consider the lower bitrates as duplicates, - for example, set ``tiebreak: items: [bitrate]``. - Default: ``{}``. +- **tiebreak**: Dictionary of lists of attributes keyed by ``items`` or + ``albums`` to use when choosing duplicates. By default, the tie-breaking + procedure favors the most complete metadata attribute set. If you would like + to consider the lower bitrates as duplicates, for example, set ``tiebreak: + items: [bitrate]``. Default: ``{}``. - **remove**: Remove matched items from the library, but not from the disk. Default: ``no``. Examples -------- -List all duplicate tracks in your collection:: +List all duplicate tracks in your collection: - beet duplicates +:: -List all duplicate tracks from 2008:: + beet duplicates - beet duplicates year:2008 +List all duplicate tracks from 2008: -Print out a unicode histogram of duplicate track years using `spark`_:: +:: - beet duplicates -f '$year' | spark - ▆▁▆█▄▇▇▄▇▇▁█▇▆▇▂▄█▁██▂█▁▁██▁█▂▇▆▂▇█▇▇█▆▆▇█▇█▇▆██▂▇ + beet duplicates year:2008 -Print out a listing of all albums with duplicate tracks, and respective -counts:: +Print out a unicode histogram of duplicate track years using spark_: - beet duplicates -ac +:: -The same as the above but include the original album, and show the path:: + beet duplicates -f '$year' | spark + ▆▁▆█▄▇▇▄▇▇▁█▇▆▇▂▄█▁██▂█▁▁██▁█▂▇▆▂▇█▇▇█▆▆▇█▇█▇▆██▂▇ - beet duplicates -acf '$path' +Print out a listing of all albums with duplicate tracks, and respective counts: -Get tracks with the same title, artist, and album:: +:: - beet duplicates -k title -k albumartist -k album + beet duplicates -ac -Compute Adler CRC32 or MD5 checksums, storing them as flexattrs, and report -back duplicates based on those values:: +The same as the above but include the original album, and show the path: - beet dup -C 'ffmpeg -i {file} -f crc -' - beet dup -C 'md5sum {file}' +:: -Copy highly danceable items to ``party`` directory:: + beet duplicates -acf '$path' - beet dup --copy /tmp/party +Get tracks with the same title, artist, and album: -Move likely duplicates to ``trash`` directory:: +:: - beet dup --move ${HOME}/.Trash + beet duplicates -k title -k albumartist -k album -Delete items (careful!), if they're Nickelback:: +Compute Adler CRC32 or MD5 checksums, storing them as flexattrs, and report back +duplicates based on those values: - beet duplicates --delete -k albumartist -k albumartist:nickelback +:: -Tag duplicate items with some flag:: + beet dup -C 'ffmpeg -i {file} -f crc -' + beet dup -C 'md5sum {file}' - beet duplicates --tag dup=1 +Copy highly danceable items to ``party`` directory: -Ignore items with undefined keys:: +:: - beet duplicates --strict + beet dup --copy /tmp/party -Merge and delete duplicate albums with different missing tracks:: +Move likely duplicates to ``trash`` directory: - beet duplicates --album --merge --delete +:: + + beet dup --move ${HOME}/.Trash + +Delete items (careful!), if they're Nickelback: + +:: + + beet duplicates --delete -k albumartist -k albumartist:nickelback + +Tag duplicate items with some flag: + +:: + + beet duplicates --tag dup=1 + +Ignore items with undefined keys: + +:: + + beet duplicates --strict + +Merge and delete duplicate albums with different missing tracks: + +:: + + beet duplicates --album --merge --delete .. _spark: https://github.com/holman/spark diff --git a/docs/plugins/edit.rst b/docs/plugins/edit.rst index fe5e348d6..ab38dd169 100644 --- a/docs/plugins/edit.rst +++ b/docs/plugins/edit.rst @@ -5,9 +5,11 @@ The ``edit`` plugin lets you modify music metadata using your favorite text editor. Enable the ``edit`` plugin in your configuration (see :ref:`using-plugins`) and -then type:: +then type: - beet edit QUERY +:: + + beet edit QUERY Your text editor (i.e., the command in your ``$VISUAL`` or ``$EDITOR`` environment variable) will open with a list of tracks to edit. Make your changes @@ -19,15 +21,17 @@ Command-Line Options The ``edit`` command has these command-line options: - ``-a`` or ``--album``: Edit albums instead of individual items. -- ``-f FIELD`` or ``--field FIELD``: Specify an additional field to edit - (in addition to the defaults set in the configuration). +- ``-f FIELD`` or ``--field FIELD``: Specify an additional field to edit (in + addition to the defaults set in the configuration). - ``--all``: Edit *all* available fields. Interactive Usage ----------------- The ``edit`` plugin can also be invoked during an import session. If enabled, it -adds two new options to the user prompt:: +adds two new options to the user prompt: + +:: [A]pply, More candidates, Skip, Use as-is, as Tracks, Group albums, Enter search, enter Id, aBort, eDit, edit Candidates? @@ -38,8 +42,8 @@ adds two new options to the user prompt:: Please note that currently the interactive usage of the plugin will only allow you to change the item-level fields. In case you need to edit the album-level -fields, the recommended approach is to invoke the plugin via the command line -in album mode (``beet edit -a QUERY``) after the import. +fields, the recommended approach is to invoke the plugin via the command line in +album mode (``beet edit -a QUERY``) after the import. Also, please be aware that the ``edit Candidates`` choice can only be used with the matches found during the initial search (and currently not supporting the @@ -50,11 +54,10 @@ cases where you already have a specific candidate ID that you want to edit. Configuration ------------- -To configure the plugin, make an ``edit:`` section in your configuration -file. The available options are: +To configure the plugin, make an ``edit:`` section in your configuration file. +The available options are: -- **itemfields**: A space-separated list of item fields to include in the - editor by default. - Default: ``track title artist album`` +- **itemfields**: A space-separated list of item fields to include in the editor + by default. Default: ``track title artist album`` - **albumfields**: The same when editing albums (with the ``-a`` option). Default: ``album albumartist`` diff --git a/docs/plugins/embedart.rst b/docs/plugins/embedart.rst index 9a91055f4..abbe2460d 100644 --- a/docs/plugins/embedart.rst +++ b/docs/plugins/embedart.rst @@ -25,15 +25,14 @@ This behavior can be disabled with the ``auto`` config option (see below). .. _image-similarity-check: Image Similarity -'''''''''''''''' +~~~~~~~~~~~~~~~~ When importing a lot of files with the ``auto`` option, one may be reluctant to overwrite existing embedded art for all of them. You can tell beets to avoid embedding images that are too different from the -existing ones. -This works by computing the perceptual hashes (`PHASH`_) of the two images and -checking that the difference between the two does not exceed a +existing ones. This works by computing the perceptual hashes (PHASH_) of the two +images and checking that the difference between the two does not exceed a threshold. You can set the threshold with the ``compare_threshold`` option. A threshold of 0 (the default) disables similarity checking and always embeds @@ -41,7 +40,7 @@ new images. Set the threshold to another number---we recommend between 10 and 100---to adjust the sensitivity of the comparison. The smaller the threshold number, the more similar the images must be. -This feature requires `ImageMagick`_. +This feature requires ImageMagick_. Configuration ------------- @@ -49,40 +48,37 @@ Configuration To configure the plugin, make an ``embedart:`` section in your configuration file. The available options are: -- **auto**: Enable automatic album art embedding. - Default: ``yes``. -- **compare_threshold**: How similar candidate art must be to - existing art to be written to the file (see :ref:`image-similarity-check`). - Default: 0 (disabled). +- **auto**: Enable automatic album art embedding. Default: ``yes``. +- **compare_threshold**: How similar candidate art must be to existing art to be + written to the file (see :ref:`image-similarity-check`). Default: 0 + (disabled). - **ifempty**: Avoid embedding album art for files that already have art - embedded. - Default: ``no``. -- **maxwidth**: A maximum width to downscale images before embedding - them (the original image file is not altered). The resize operation reduces - image width to at most ``maxwidth`` pixels. The height is recomputed so that - the aspect ratio is preserved. See also :ref:`image-resizing` for further - caveats about image resizing. - Default: 0 (disabled). + embedded. Default: ``no``. +- **maxwidth**: A maximum width to downscale images before embedding them (the + original image file is not altered). The resize operation reduces image width + to at most ``maxwidth`` pixels. The height is recomputed so that the aspect + ratio is preserved. See also :ref:`image-resizing` for further caveats about + image resizing. Default: 0 (disabled). - **quality**: The JPEG quality level to use when compressing images (when - ``maxwidth`` is set). This should be either a number from 1 to 100 or 0 to - use the default quality. 65–75 is usually a good starting point. The default + ``maxwidth`` is set). This should be either a number from 1 to 100 or 0 to use + the default quality. 65–75 is usually a good starting point. The default behavior depends on the imaging tool used for scaling: ImageMagick tries to estimate the input image quality and uses 92 if it cannot be determined, and - PIL defaults to 75. - Default: 0 (disabled) + PIL defaults to 75. Default: 0 (disabled) - **remove_art_file**: Automatically remove the album art file for the album after it has been embedded. This option is best used alongside the :doc:`FetchArt </plugins/fetchart>` plugin to download art with the purpose of - directly embedding it into the file's metadata without an "intermediate" - album art file. - Default: ``no``. + directly embedding it into the file's metadata without an "intermediate" album + art file. Default: ``no``. -Note: ``compare_threshold`` option requires `ImageMagick`_, and ``maxwidth`` -requires either `ImageMagick`_ or `Pillow`_. +Note: ``compare_threshold`` option requires ImageMagick_, and ``maxwidth`` +requires either ImageMagick_ or Pillow_. -.. _Pillow: https://github.com/python-pillow/Pillow -.. _ImageMagick: https://www.imagemagick.org/ -.. _PHASH: http://www.fmwconcepts.com/misc_tests/perceptual_hash_test_results_510/ +.. _imagemagick: https://www.imagemagick.org/ + +.. _phash: http://www.fmwconcepts.com/misc_tests/perceptual_hash_test_results_510/ + +.. _pillow: https://github.com/python-pillow/Pillow Manually Embedding and Extracting Art ------------------------------------- @@ -90,29 +86,28 @@ Manually Embedding and Extracting Art The ``embedart`` plugin provides a couple of commands for manually managing embedded album art: -* ``beet embedart [-f IMAGE] QUERY``: embed images in every track of the - albums matching the query. If the ``-f`` (``--file``) option is given, then - use a specific image file from the filesystem; otherwise, each album embeds - its own currently associated album art. The command prompts for confirmation - before making the change unless you specify the ``-y`` (``--yes``) option. - -* ``beet embedart [-u IMAGE_URL] QUERY``: embed image specified in the URL - into every track of the albums matching the query. The ``-u`` (``--url``) option can be used to specify the URL of the image to be used. The command prompts for confirmation before making the change unless you specify the ``-y`` (``--yes``) option. - -* ``beet extractart [-a] [-n FILE] QUERY``: extracts the images for all albums +- ``beet embedart [-f IMAGE] QUERY``: embed images in every track of the albums + matching the query. If the ``-f`` (``--file``) option is given, then use a + specific image file from the filesystem; otherwise, each album embeds its own + currently associated album art. The command prompts for confirmation before + making the change unless you specify the ``-y`` (``--yes``) option. +- ``beet embedart [-u IMAGE_URL] QUERY``: embed image specified in the URL into + every track of the albums matching the query. The ``-u`` (``--url``) option + can be used to specify the URL of the image to be used. The command prompts + for confirmation before making the change unless you specify the ``-y`` + (``--yes``) option. +- ``beet extractart [-a] [-n FILE] QUERY``: extracts the images for all albums matching the query. The images are placed inside the album folder. You can specify the destination file name using the ``-n`` option, but leave off the extension: it will be chosen automatically. The destination filename is specified using the ``art_filename`` configuration option. It defaults to - ``cover`` if it's not specified via ``-o`` nor the config. - Using ``-a``, the extracted image files are automatically associated with the - corresponding album. - -* ``beet extractart -o FILE QUERY``: extracts the image from an item matching + ``cover`` if it's not specified via ``-o`` nor the config. Using ``-a``, the + extracted image files are automatically associated with the corresponding + album. +- ``beet extractart -o FILE QUERY``: extracts the image from an item matching the query and stores it in a file. You have to specify the destination file using the ``-o`` option, but leave off the extension: it will be chosen automatically. - -* ``beet clearart QUERY``: removes all embedded images from all items matching +- ``beet clearart QUERY``: removes all embedded images from all items matching the query. The command prompts for confirmation before making the change unless you specify the ``-y`` (``--yes``) option. diff --git a/docs/plugins/embyupdate.rst b/docs/plugins/embyupdate.rst index 1c6fc61e4..5bb69cca0 100644 --- a/docs/plugins/embyupdate.rst +++ b/docs/plugins/embyupdate.rst @@ -1,11 +1,11 @@ EmbyUpdate Plugin ================= -``embyupdate`` is a plugin that lets you automatically update `Emby`_'s library +``embyupdate`` is a plugin that lets you automatically update Emby_'s library whenever you change your beets library. -To use it, first enable the your configuration (see :ref:`using-plugins`). -Then, install ``beets`` with ``embyupdate`` extra +To use it, first enable the your configuration (see :ref:`using-plugins`). Then, +install ``beets`` with ``embyupdate`` extra .. code-block:: bash @@ -22,26 +22,27 @@ that using an ``emby`` section in your ``config.yaml`` username: user apikey: apikey -With that all in place, you'll see beets send the "update" command to your Emby server every time you change your beets library. +With that all in place, you'll see beets send the "update" command to your Emby +server every time you change your beets library. -.. _Emby: https://emby.media/ +.. _emby: https://emby.media/ Configuration ------------- The available options under the ``emby:`` section are: -- **host**: The Emby server host. You also can include ``http://`` or ``https://``. - Default: ``localhost`` -- **port**: The Emby server port. - Default: 8096 -- **username**: A username of an Emby user that is allowed to refresh the library. +- **host**: The Emby server host. You also can include ``http://`` or + ``https://``. Default: ``localhost`` +- **port**: The Emby server port. Default: 8096 +- **username**: A username of an Emby user that is allowed to refresh the + library. - **userid**: A user ID of an Emby user that is allowed to refresh the library. (This is only necessary for private users i.e. when the user is hidden from login screens) - **apikey**: An Emby API key for the user. -- **password**: The password for the user. (This is only necessary if no API - key is provided.) +- **password**: The password for the user. (This is only necessary if no API key + is provided.) You can choose to authenticate either with ``apikey`` or ``password``, but only one of those two is required. diff --git a/docs/plugins/export.rst b/docs/plugins/export.rst index bca9d1e5a..a5fa78617 100644 --- a/docs/plugins/export.rst +++ b/docs/plugins/export.rst @@ -1,54 +1,56 @@ Export Plugin ============= -The ``export`` plugin lets you get data from the items and export the content -as `JSON`_, `CSV`_, or `XML`_. +The ``export`` plugin lets you get data from the items and export the content as +JSON_, CSV_, or XML_. -.. _JSON: https://www.json.org -.. _CSV: https://fileinfo.com/extension/csv -.. _XML: https://fileinfo.com/extension/xml +.. _csv: https://fileinfo.com/extension/csv -Enable the ``export`` plugin (see :ref:`using-plugins` for help). Then, type ``beet export`` followed by a :doc:`query </reference/query>` to get the data from -your library. For example, run this:: +.. _json: https://www.json.org + +.. _xml: https://fileinfo.com/extension/xml + +Enable the ``export`` plugin (see :ref:`using-plugins` for help). Then, type +``beet export`` followed by a :doc:`query </reference/query>` to get the data +from your library. For example, run this: + +:: $ beet export beatles to print a JSON file containing information about your Beatles tracks. - Command-Line Options -------------------- The ``export`` command has these command-line options: -* ``--include-keys`` or ``-i``: Choose the properties to include in the output +- ``--include-keys`` or ``-i``: Choose the properties to include in the output data. The argument is a comma-separated list of simple glob patterns where - ``*`` matches any string. For example:: + ``*`` matches any string. For example: + + :: $ beet export -i 'title,mb*' beatles - will include the ``title`` property and all properties starting with - ``mb``. You can add the ``-i`` option multiple times to the command - line. + will include the ``title`` property and all properties starting with ``mb``. + You can add the ``-i`` option multiple times to the command line. -* ``--library`` or ``-l``: Show data from the library database instead of the +- ``--library`` or ``-l``: Show data from the library database instead of the files' tags. - -* ``--album`` or ``-a``: Show data from albums instead of tracks (implies +- ``--album`` or ``-a``: Show data from albums instead of tracks (implies ``--library``). - -* ``--output`` or ``-o``: Path for an output file. If not informed, will print +- ``--output`` or ``-o``: Path for an output file. If not informed, will print the data in the console. - -* ``--append``: Appends the data to the file instead of writing. - -* ``--format`` or ``-f``: Specifies the format the data will be exported as. If not informed, JSON will be used by default. The format options include csv, json, `jsonlines <https://jsonlines.org/>`_ and xml. +- ``--append``: Appends the data to the file instead of writing. +- ``--format`` or ``-f``: Specifies the format the data will be exported as. If + not informed, JSON will be used by default. The format options include csv, + json, `jsonlines <https://jsonlines.org/>`_ and xml. Configuration ------------- -To configure the plugin, make a ``export:`` section in your configuration -file. +To configure the plugin, make a ``export:`` section in your configuration file. For JSON export, these options are available under the ``json`` and ``jsonlines`` keys: @@ -57,19 +59,22 @@ For JSON export, these options are available under the ``json`` and - **separators**: A ``[item_separator, dict_separator]`` tuple. - **sort_keys**: Sorts the keys in JSON dictionaries. -Those options match the options from the `Python json module`_. -Similarly, these options are available for the CSV format under the ``csv`` -key: +Those options match the options from the `Python json module`_. Similarly, these +options are available for the CSV format under the ``csv`` key: -- **delimiter**: Used as the separating character between fields. The default value is a comma (,). -- **dialect**: The kind of CSV file to produce. The default is `excel`. +- **delimiter**: Used as the separating character between fields. The default + value is a comma (,). +- **dialect**: The kind of CSV file to produce. The default is ``excel``. These options match the options from the `Python csv module`_. -.. _Python json module: https://docs.python.org/2/library/json.html#basic-usage -.. _Python csv module: https://docs.python.org/3/library/csv.html#csv-fmt-params +.. _python csv module: https://docs.python.org/3/library/csv.html#csv-fmt-params -The default options look like this:: +.. _python json module: https://docs.python.org/2/library/json.html#basic-usage + +The default options look like this: + +:: export: json: diff --git a/docs/plugins/fetchart.rst b/docs/plugins/fetchart.rst index 1da92a493..1d64f4b2e 100644 --- a/docs/plugins/fetchart.rst +++ b/docs/plugins/fetchart.rst @@ -28,57 +28,51 @@ Configuration To configure the plugin, make a ``fetchart:`` section in your configuration file. The available options are: -- **auto**: Enable automatic album art fetching during import. - Default: ``yes``. +- **auto**: Enable automatic album art fetching during import. Default: ``yes``. - **cautious**: Pick only trusted album art by ignoring filenames that do not - contain one of the keywords in ``cover_names``. - Default: ``no``. -- **cover_names**: Prioritize images containing words in this list. - Default: ``cover front art album folder``. + contain one of the keywords in ``cover_names``. Default: ``no``. +- **cover_names**: Prioritize images containing words in this list. Default: + ``cover front art album folder``. - **minwidth**: Only images with a width bigger or equal to ``minwidth`` are considered as valid album art candidates. Default: 0. - **maxwidth**: A maximum image width to downscale fetched images if they are too big. The resize operation reduces image width to at most ``maxwidth`` pixels. The height is recomputed so that the aspect ratio is preserved. See the section on :ref:`cover-art-archive-maxwidth` below for additional - information regarding the Cover Art Archive source. - Default: 0 (no maximum is enforced). + information regarding the Cover Art Archive source. Default: 0 (no maximum is + enforced). - **quality**: The JPEG quality level to use when compressing images (when - ``maxwidth`` is set). This should be either a number from 1 to 100 or 0 to - use the default quality. 65–75 is usually a good starting point. The default + ``maxwidth`` is set). This should be either a number from 1 to 100 or 0 to use + the default quality. 65–75 is usually a good starting point. The default behavior depends on the imaging tool used for scaling: ImageMagick tries to estimate the input image quality and uses 92 if it cannot be determined, and - PIL defaults to 75. - Default: 0 (disabled) + PIL defaults to 75. Default: 0 (disabled) - **max_filesize**: The maximum size of a target piece of cover art in bytes. - When using an ImageMagick backend this sets - ``-define jpeg:extent=max_filesize``. Using PIL this will reduce JPG quality - by up to 50% to attempt to reach the target filesize. Neither method is - *guaranteed* to reach the target size, however in most cases it should - succeed. - Default: 0 (disabled) -- **enforce_ratio**: Only images with a width:height ratio of 1:1 are - considered as valid album art candidates if set to ``yes``. - It is also possible to specify a certain deviation to the exact ratio to - still be considered valid. This can be done either in pixels - (``enforce_ratio: 10px``) or as a percentage of the longer edge - (``enforce_ratio: 0.5%``). Default: ``no``. -- **sources**: List of sources to search for images. An asterisk `*` expands - to all available sources. - Default: ``filesystem coverart itunes amazon albumart``, i.e., everything but - ``wikipedia``, ``google``, ``fanarttv`` and ``lastfm``. Enable those sources - for more matches at the cost of some speed. They are searched in the given - order, thus in the default config, no remote (Web) art source are queried if - local art is found in the filesystem. To use a local image as fallback, - move it to the end of the list. For even more fine-grained control over - the search order, see the section on :ref:`album-art-sources` below. + When using an ImageMagick backend this sets ``-define + jpeg:extent=max_filesize``. Using PIL this will reduce JPG quality by up to + 50% to attempt to reach the target filesize. Neither method is *guaranteed* to + reach the target size, however in most cases it should succeed. Default: 0 + (disabled) +- **enforce_ratio**: Only images with a width:height ratio of 1:1 are considered + as valid album art candidates if set to ``yes``. It is also possible to + specify a certain deviation to the exact ratio to still be considered valid. + This can be done either in pixels (``enforce_ratio: 10px``) or as a percentage + of the longer edge (``enforce_ratio: 0.5%``). Default: ``no``. +- **sources**: List of sources to search for images. An asterisk ``*`` expands + to all available sources. Default: ``filesystem coverart itunes amazon + albumart``, i.e., everything but ``wikipedia``, ``google``, ``fanarttv`` and + ``lastfm``. Enable those sources for more matches at the cost of some speed. + They are searched in the given order, thus in the default config, no remote + (Web) art source are queried if local art is found in the filesystem. To use a + local image as fallback, move it to the end of the list. For even more + fine-grained control over the search order, see the section on + :ref:`album-art-sources` below. - **google_key**: Your Google API key (to enable the Google Custom Search - backend). - Default: None. -- **google_engine**: The custom search engine to use. - Default: The `beets custom search engine`_, which searches the entire web. -- **fanarttv_key**: The personal API key for requesting art from - fanart.tv. See below. + backend). Default: None. +- **google_engine**: The custom search engine to use. Default: The `beets custom + search engine`_, which searches the entire web. +- **fanarttv_key**: The personal API key for requesting art from fanart.tv. See + below. - **lastfm_key**: The personal API key for requesting art from Last.fm. See below. - **store_source**: If enabled, fetchart stores the artwork's source in a @@ -87,64 +81,70 @@ file. The available options are: - **high_resolution**: If enabled, fetchart retrieves artwork in the highest resolution it can find (warning: image files can sometimes reach >20MB). Default: ``no``. -- **deinterlace**: If enabled, `Pillow`_ or `ImageMagick`_ backends are - instructed to store cover art as non-progressive JPEG. You might need this if - you use DAPs that don't support progressive images. - Default: ``no``. +- **deinterlace**: If enabled, Pillow_ or ImageMagick_ backends are instructed + to store cover art as non-progressive JPEG. You might need this if you use + DAPs that don't support progressive images. Default: ``no``. - **cover_format**: If enabled, forced the cover image into the specified - format. Most often, this will be either ``JPEG`` or ``PNG`` [#imgformats]_. - Also respects ``deinterlace``. - Default: None (leave unchanged). + format. Most often, this will be either ``JPEG`` or ``PNG`` (see + image-formats_). Also respects ``deinterlace``. Default: None (leave + unchanged). -Note: ``maxwidth`` and ``enforce_ratio`` options require either `ImageMagick`_ -or `Pillow`_. +Note: ``maxwidth`` and ``enforce_ratio`` options require either ImageMagick_ or +Pillow_. .. note:: - Previously, there was a ``remote_priority`` option to specify when to - look for art on the filesystem. This is - still respected, but a deprecation message will be shown until you - replace this configuration with the new ``filesystem`` value in the - ``sources`` array. + Previously, there was a ``remote_priority`` option to specify when to look + for art on the filesystem. This is still respected, but a deprecation + message will be shown until you replace this configuration with the new + ``filesystem`` value in the ``sources`` array. + +.. _image-formats: + +.. admonition:: Image formats + + Other image formats are available, though the full list depends on your + system and what backend you are using. If you're using the ImageMagick + backend, you can use ``magick identify -list format`` to get a full list of + all supported formats, and you can use the Python function + PIL.features.pilinfo() to print a list of all supported formats in Pillow + (``python3 -c 'import PIL.features as f; f.pilinfo()'``). .. _beets custom search engine: https://cse.google.com.au:443/cse/publicurl?cx=001442825323518660753:hrh5ch1gjzm -.. _Pillow: https://github.com/python-pillow/Pillow -.. _ImageMagick: https://www.imagemagick.org/ -.. [#imgformats] Other image formats are available, though the full list - depends on your system and what backend you are using. If you're using the - ImageMagick backend, you can use ``magick identify -list format`` to get a - full list of all supported formats, and you can use the Python function - PIL.features.pilinfo() to print a list of all supported formats in Pillow - (``python3 -c 'import PIL.features as f; f.pilinfo()'``). Here's an example that makes plugin select only images that contain ``front`` or ``back`` keywords in their filenames and prioritizes the iTunes source over -others:: +others: + +:: fetchart: cautious: true cover_names: front back sources: itunes * - Manually Fetching Album Art --------------------------- Use the ``fetchart`` command to download album art after albums have already -been imported:: +been imported: + +:: $ beet fetchart [-f] [query] By default, the command will only look for album art when the album doesn't -already have it; the ``-f`` or ``--force`` switch makes it search for art -in Web databases regardless. If you specify a query, only matching albums will -be processed; otherwise, the command processes every album in your library. +already have it; the ``-f`` or ``--force`` switch makes it search for art in Web +databases regardless. If you specify a query, only matching albums will be +processed; otherwise, the command processes every album in your library. Display Only Missing Album Art ------------------------------ -Use the ``fetchart`` command with the ``-q`` switch in order to display only missing -art:: +Use the ``fetchart`` command with the ``-q`` switch in order to display only +missing art: + +:: $ beet fetchart [-q] [query] @@ -157,7 +157,7 @@ fetched, or for which artwork could not be found will be printed. Image Resizing -------------- -Beets can resize images using `Pillow`_, `ImageMagick`_, or a server-side resizing +Beets can resize images using Pillow_, ImageMagick_, or a server-side resizing proxy. If either Pillow or ImageMagick is installed, beets will use those; otherwise, it falls back to the resizing proxy. If the resizing proxy is used, no resizing is performed for album art found on the filesystem---only downloaded @@ -169,9 +169,6 @@ On some versions of Windows, the program can be shadowed by a system-provided ``convert.exe``. On these systems, you may need to modify your ``%PATH%`` environment variable so that ImageMagick comes first or use Pillow instead. -.. _Pillow: https://github.com/python-pillow/Pillow -.. _ImageMagick: https://www.imagemagick.org/ - .. _album-art-sources: Album Art Sources @@ -179,9 +176,8 @@ Album Art Sources By default, this plugin searches for art in the local filesystem as well as on the Cover Art Archive, the iTunes Store, Amazon, and AlbumArt.org, in that -order. -You can reorder the sources or remove -some to speed up the process using the ``sources`` configuration option. +order. You can reorder the sources or remove some to speed up the process using +the ``sources`` configuration option. When looking for local album art, beets checks for image files located in the same folder as the music files you're importing. Beets prefers to use an image @@ -190,8 +186,10 @@ the absence of well-known names, it will use any image file in the same folder as your music files. For some of the art sources, the backend service can match artwork by various -criteria. If you want finer control over the search order in such cases, you -can use this alternative syntax for the ``sources`` option:: +criteria. If you want finer control over the search order in such cases, you can +use this alternative syntax for the ``sources`` option: + +:: fetchart: sources: @@ -203,73 +201,72 @@ can use this alternative syntax for the ``sources`` option:: where listing a source without matching criteria will default to trying all available strategies. Entries of the forms ``coverart: release releasegroup`` -and ``coverart: *`` are also valid. -Currently, only the ``coverart`` source supports multiple criteria: -namely, ``release`` and ``releasegroup``, which refer to the -respective MusicBrainz IDs. +and ``coverart: *`` are also valid. Currently, only the ``coverart`` source +supports multiple criteria: namely, ``release`` and ``releasegroup``, which +refer to the respective MusicBrainz IDs. When you choose to apply changes during an import, beets will search for art as -described above. For "as-is" imports (and non-autotagged imports using the +described above. For "as-is" imports (and non-autotagged imports using the ``-A`` flag), beets only looks for art on the local filesystem. Google custom search -'''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~ -To use the google image search backend you need to -`register for a Google API key`_. Set the ``google_key`` configuration -option to your key, then add ``google`` to the list of sources in your -configuration. +To use the google image search backend you need to `register for a Google API +key`_. Set the ``google_key`` configuration option to your key, then add +``google`` to the list of sources in your configuration. -.. _register for a Google API key: https://console.developers.google.com. +.. _register for a google api key: https://console.developers.google.com. Optionally, you can `define a custom search engine`_. Get your search engine's -token and use it for your ``google_engine`` configuration option. The -default engine searches the entire web for cover art. +token and use it for your ``google_engine`` configuration option. The default +engine searches the entire web for cover art. .. _define a custom search engine: https://www.google.com/cse/all -Note that the Google custom search API is limited to 100 queries per day. -After that, the fetchart plugin will fall back on other declared data sources. +Note that the Google custom search API is limited to 100 queries per day. After +that, the fetchart plugin will fall back on other declared data sources. Fanart.tv -''''''''' +~~~~~~~~~ -Although not strictly necessary right now, you might think about -`registering a personal fanart.tv API key`_. Set the ``fanarttv_key`` -configuration option to your key, then add ``fanarttv`` to the list of sources -in your configuration. +Although not strictly necessary right now, you might think about `registering a +personal fanart.tv API key`_. Set the ``fanarttv_key`` configuration option to +your key, then add ``fanarttv`` to the list of sources in your configuration. -.. _registering a personal fanart.tv API key: https://fanart.tv/get-an-api-key/ +.. _registering a personal fanart.tv api key: https://fanart.tv/get-an-api-key/ More detailed information can be found `on their Wiki`_. Specifically, the personal key will give you earlier access to new art. -.. _on their Wiki: https://wiki.fanart.tv/General/personal%20api/ +.. _on their wiki: https://wiki.fanart.tv/General/personal%20api/ Last.fm -''''''' +~~~~~~~ To use the Last.fm backend, you need to `register for a Last.fm API key`_. Set the ``lastfm_key`` configuration option to your API key, then add ``lastfm`` to the list of sources in your configuration. -.. _register for a Last.fm API key: https://www.last.fm/api/account/create +.. _register for a last.fm api key: https://www.last.fm/api/account/create Spotify -''''''' +~~~~~~~ -Spotify backend is enabled by default and will update album art if a valid Spotify album id is found. +Spotify backend is enabled by default and will update album art if a valid +Spotify album id is found. + +.. _beautifulsoup: https://www.crummy.com/software/BeautifulSoup/bs4/doc/ .. _pip: https://pip.pypa.io -.. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/bs4/doc/ Cover Art URL -''''''''''''' +~~~~~~~~~~~~~ -The `fetchart` plugin can also use a flexible attribute field ``cover_art_url`` -where you can manually specify the image URL to be used as cover art. Any custom -plugin can use this field to provide the cover art and ``fetchart`` will use it -as a source. +The ``fetchart`` plugin can also use a flexible attribute field +``cover_art_url`` where you can manually specify the image URL to be used as +cover art. Any custom plugin can use this field to provide the cover art and +``fetchart`` will use it as a source. .. _cover-art-archive-maxwidth: @@ -277,20 +274,24 @@ Cover Art Archive Pre-sized Thumbnails -------------------------------------- The CAA provides pre-sized thumbnails of width 250, 500, and 1200 pixels. If you -set the `maxwidth` option to one of these values, the corresponding image will -be downloaded, saving `beets` the need to scale down the image. It can also +set the ``maxwidth`` option to one of these values, the corresponding image will +be downloaded, saving ``beets`` the need to scale down the image. It can also speed up the downloading process, as some cover arts can sometimes be very large. Storing the Artwork's Source ---------------------------- -Storing the current artwork's source might be used to narrow down -``fetchart`` commands. For example, if some albums have artwork placed -manually in their directories that should not be replaced by a forced -album art fetch, you could do +Storing the current artwork's source might be used to narrow down ``fetchart`` +commands. For example, if some albums have artwork placed manually in their +directories that should not be replaced by a forced album art fetch, you could +do ``beet fetchart -f ^art_source:filesystem`` The values written to ``art_source`` are the same names used in the ``sources`` configuration value. + +.. _imagemagick: https://www.imagemagick.org/ + +.. _pillow: https://github.com/python-pillow/Pillow diff --git a/docs/plugins/filefilter.rst b/docs/plugins/filefilter.rst index 21600aca7..f56d14553 100644 --- a/docs/plugins/filefilter.rst +++ b/docs/plugins/filefilter.rst @@ -1,8 +1,8 @@ FileFilter Plugin ================= -The ``filefilter`` plugin allows you to skip files during import using -regular expressions. +The ``filefilter`` plugin allows you to skip files during import using regular +expressions. To use the ``filefilter`` plugin, enable it in your configuration (see :ref:`using-plugins`). @@ -10,18 +10,20 @@ To use the ``filefilter`` plugin, enable it in your configuration (see Configuration ------------- -To configure the plugin, make a ``filefilter:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``filefilter:`` section in your configuration +file. The available options are: - **path**: A regular expression to filter files based on their path and name. Default: ``.*`` (import everything) - **album_path** and **singleton_path**: You may specify different regular expressions used for imports of albums and singletons. This way, you can automatically skip singletons when importing albums if the names (and paths) - of the files are distinguishable via a regex. The regexes defined here - take precedence over the global ``path`` option. + of the files are distinguishable via a regex. The regexes defined here take + precedence over the global ``path`` option. -Here's an example:: +Here's an example: + +:: filefilter: path: .*\d\d[^/]+$ diff --git a/docs/plugins/fish.rst b/docs/plugins/fish.rst index 0c89576c5..c1ae4f990 100644 --- a/docs/plugins/fish.rst +++ b/docs/plugins/fish.rst @@ -2,10 +2,10 @@ Fish Plugin =========== The ``fish`` plugin adds a ``beet fish`` command that creates a `Fish shell`_ -tab-completion file named ``beet.fish`` in ``~/.config/fish/completions``. -This enables tab-completion of ``beet`` commands for the `Fish shell`_. +tab-completion file named ``beet.fish`` in ``~/.config/fish/completions``. This +enables tab-completion of ``beet`` commands for the `Fish shell`_. -.. _Fish shell: https://fishshell.com/ +.. _fish shell: https://fishshell.com/ Configuration ------------- @@ -24,11 +24,11 @@ For users not accustomed to tab completion… After you type ``beet`` followed b a space in your shell prompt and then the ``TAB`` key, you should see a list of the beets commands (and their abbreviated versions) that can be invoked in your current environment. Similarly, typing ``beet -<TAB>`` will show you all the -option flags available to you, which also applies to subcommands such as -``beet import -<TAB>``. If you type ``beet ls`` followed by a space and then the -and the ``TAB`` key, you will see a list of all the album/track fields that can -be used in beets queries. For example, typing ``beet ls ge<TAB>`` will complete -to ``genre:`` and leave you ready to type the rest of your query. +option flags available to you, which also applies to subcommands such as ``beet +import -<TAB>``. If you type ``beet ls`` followed by a space and then the and +the ``TAB`` key, you will see a list of all the album/track fields that can be +used in beets queries. For example, typing ``beet ls ge<TAB>`` will complete to +``genre:`` and leave you ready to type the rest of your query. Options ------- @@ -41,17 +41,16 @@ commands and option flags. If you want generated completions to also contain album/track field *values* for the items in your library, you can use the ``-e`` or ``--extravalues`` option. -For example: ``beet fish -e genre`` or ``beet fish -e genre -e albumartist`` -In the latter case, subsequently typing ``beet list genre: <TAB>`` will display -a list of all the genres in your library and ``beet list albumartist: <TAB>`` -will show a list of the album artists in your library. Keep in mind that all of -these values will be put into the generated completions file, so use this option -with care when specified fields contain a large number of values. Libraries with, -for example, very large numbers of genres/artists may result in higher memory +For example: ``beet fish -e genre`` or ``beet fish -e genre -e albumartist`` In +the latter case, subsequently typing ``beet list genre: <TAB>`` will display a +list of all the genres in your library and ``beet list albumartist: <TAB>`` will +show a list of the album artists in your library. Keep in mind that all of these +values will be put into the generated completions file, so use this option with +care when specified fields contain a large number of values. Libraries with, for +example, very large numbers of genres/artists may result in higher memory utilization, completion latency, et cetera. This option is not meant to replace database queries altogether. By default, the completion file will be generated at -``~/.config/fish/completions/``. -If you want to save it somewhere else, you can use the ``-o`` or ``--output`` -option. +``~/.config/fish/completions/``. If you want to save it somewhere else, you can +use the ``-o`` or ``--output`` option. diff --git a/docs/plugins/freedesktop.rst b/docs/plugins/freedesktop.rst index 0368cc5da..c584fd08e 100644 --- a/docs/plugins/freedesktop.rst +++ b/docs/plugins/freedesktop.rst @@ -1,6 +1,6 @@ Freedesktop Plugin ================== -The ``freedesktop`` plugin created .directory files in your album folders. -This plugin is now deprecated and replaced by the :doc:`/plugins/thumbnails` -with the ``dolphin`` option enabled. +The ``freedesktop`` plugin created .directory files in your album folders. This +plugin is now deprecated and replaced by the :doc:`/plugins/thumbnails` with the +``dolphin`` option enabled. diff --git a/docs/plugins/fromfilename.rst b/docs/plugins/fromfilename.rst index 5cb6ccb76..e78677b86 100644 --- a/docs/plugins/fromfilename.rst +++ b/docs/plugins/fromfilename.rst @@ -1,13 +1,12 @@ FromFilename Plugin =================== -The ``fromfilename`` plugin helps to tag albums that are missing tags -altogether but where the filenames contain useful information like the artist -and title. +The ``fromfilename`` plugin helps to tag albums that are missing tags altogether +but where the filenames contain useful information like the artist and title. -When you attempt to import a track that's missing a title, this plugin will -look at the track's filename and guess its track number, title, and artist. -These will be used to search in MusicBrainz and match track ordering. +When you attempt to import a track that's missing a title, this plugin will look +at the track's filename and guess its track number, title, and artist. These +will be used to search in MusicBrainz and match track ordering. -To use the ``fromfilename`` plugin, enable it in your configuration -(see :ref:`using-plugins`). +To use the ``fromfilename`` plugin, enable it in your configuration (see +:ref:`using-plugins`). diff --git a/docs/plugins/ftintitle.rst b/docs/plugins/ftintitle.rst index 63d023dc9..90b89ae89 100644 --- a/docs/plugins/ftintitle.rst +++ b/docs/plugins/ftintitle.rst @@ -10,8 +10,8 @@ tracks in your library like "Tellin' Me Things" by the artist "Blakroc feat. RZA". If you prefer to tag this as "Tellin' Me Things feat. RZA" by "Blakroc", then this plugin is for you. -To use the ``ftintitle`` plugin, enable it in your configuration -(see :ref:`using-plugins`). +To use the ``ftintitle`` plugin, enable it in your configuration (see +:ref:`using-plugins`). Configuration ------------- @@ -19,23 +19,22 @@ Configuration To configure the plugin, make a ``ftintitle:`` section in your configuration file. The available options are: -- **auto**: Enable metadata rewriting during import. - Default: ``yes``. -- **drop**: Remove featured artists entirely instead of adding them to the - title field. - Default: ``no``. -- **format**: Defines the format for the featuring X part of the new title field. - In this format the ``{0}`` is used to define where the featured artists are placed. - Default: ``feat. {0}`` -- **keep_in_artist**: Keep the featuring X part in the artist field. This can - be useful if you still want to be able to search for features in the artist - field. - Default: ``no``. +- **auto**: Enable metadata rewriting during import. Default: ``yes``. +- **drop**: Remove featured artists entirely instead of adding them to the title + field. Default: ``no``. +- **format**: Defines the format for the featuring X part of the new title + field. In this format the ``{0}`` is used to define where the featured artists + are placed. Default: ``feat. {0}`` +- **keep_in_artist**: Keep the featuring X part in the artist field. This can be + useful if you still want to be able to search for features in the artist + field. Default: ``no``. Running Manually ---------------- -From the command line, type:: +From the command line, type: + +:: $ beet ftintitle [QUERY] @@ -45,4 +44,4 @@ your entire collection. Use the ``-d`` flag to remove featured artists (equivalent of the ``drop`` config option). -.. _MusicBrainz style: https://musicbrainz.org/doc/Style +.. _musicbrainz style: https://musicbrainz.org/doc/Style diff --git a/docs/plugins/fuzzy.rst b/docs/plugins/fuzzy.rst index 6b013b9f5..4a65a8827 100644 --- a/docs/plugins/fuzzy.rst +++ b/docs/plugins/fuzzy.rst @@ -5,8 +5,10 @@ The ``fuzzy`` plugin provides a prefixed query that searches your library using fuzzy pattern matching. This can be useful if you want to find a track with complicated characters in the title. -First, enable the plugin named ``fuzzy`` (see :ref:`using-plugins`). -You'll then be able to use the ``~`` prefix to use fuzzy matching:: +First, enable the plugin named ``fuzzy`` (see :ref:`using-plugins`). You'll then +be able to use the ``~`` prefix to use fuzzy matching: + +:: $ beet ls '~Vareoldur' Sigur Rós - Valtari - Varðeldur @@ -14,11 +16,10 @@ You'll then be able to use the ``~`` prefix to use fuzzy matching:: Configuration ------------- -To configure the plugin, make a ``fuzzy:`` section in your configuration -file. The available options are: +To configure the plugin, make a ``fuzzy:`` section in your configuration file. +The available options are: -- **threshold**: The "sensitivity" of the fuzzy match. A value of 1.0 will - show only perfect matches and a value of 0.0 will match everything. - Default: 0.7. -- **prefix**: The character used to designate fuzzy queries. - Default: ``~``, which may need to be escaped in some shells. +- **threshold**: The "sensitivity" of the fuzzy match. A value of 1.0 will show + only perfect matches and a value of 0.0 will match everything. Default: 0.7. +- **prefix**: The character used to designate fuzzy queries. Default: ``~``, + which may need to be escaped in some shells. diff --git a/docs/plugins/gmusic.rst b/docs/plugins/gmusic.rst index 412978bd6..76697ea31 100644 --- a/docs/plugins/gmusic.rst +++ b/docs/plugins/gmusic.rst @@ -1,5 +1,5 @@ Gmusic Plugin ============= -The ``gmusic`` plugin interfaced beets to Google Play Music. It has been -removed after the shutdown of this service. +The ``gmusic`` plugin interfaced beets to Google Play Music. It has been removed +after the shutdown of this service. diff --git a/docs/plugins/hook.rst b/docs/plugins/hook.rst index 2c1dfec25..f3e847aa3 100644 --- a/docs/plugins/hook.rst +++ b/docs/plugins/hook.rst @@ -3,43 +3,42 @@ Hook Plugin Internally, beets uses *events* to tell plugins when something happens. For example, one event fires when the importer finishes processes a song, and -another triggers just before the ``beet`` command exits. -The ``hook`` plugin lets you run commands in response to these events. +another triggers just before the ``beet`` command exits. The ``hook`` plugin +lets you run commands in response to these events. .. _hook-configuration: Configuration ------------- -To configure the plugin, make a ``hook`` section in your configuration -file. The available options are: +To configure the plugin, make a ``hook`` section in your configuration file. The +available options are: -- **hooks**: A list of events and the commands to run - (see :ref:`individual-hook-configuration`). Default: Empty. +- **hooks**: A list of events and the commands to run (see + :ref:`individual-hook-configuration`). Default: Empty. .. _individual-hook-configuration: Configuring Each Hook -''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~ Each element under ``hooks`` should have these keys: -- **event**: The name of the event that will trigger this hook. - See the :ref:`plugin events <plugin_events>` documentation for a list - of possible values. +- **event**: The name of the event that will trigger this hook. See the + :ref:`plugin events <plugin_events>` documentation for a list of possible + values. - **command**: The command to run when this hook executes. .. _command-substitution: Command Substitution -'''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~ -Commands can access the parameters of events using `Python string -formatting`_. Use ``{name}`` in your command and the plugin will substitute it -with the named value. The name can also refer to a field, as in -``{album.path}``. +Commands can access the parameters of events using `Python string formatting`_. +Use ``{name}`` in your command and the plugin will substitute it with the named +value. The name can also refer to a field, as in ``{album.path}``. -.. _Python string formatting: https://www.python.org/dev/peps/pep-3101/ +.. _python string formatting: https://www.python.org/dev/peps/pep-3101/ You can find a list of all available events and their arguments in the :ref:`plugin events <plugin_events>` documentation. diff --git a/docs/plugins/ihate.rst b/docs/plugins/ihate.rst index f9cde39eb..47e679dbd 100644 --- a/docs/plugins/ihate.rst +++ b/docs/plugins/ihate.rst @@ -2,9 +2,8 @@ IHate Plugin ============ The ``ihate`` plugin allows you to automatically skip things you hate during -import or warn you about them. You specify queries (see -:doc:`/reference/query`) and the plugin skips (or warns about) albums or items -that match any query. +import or warn you about them. You specify queries (see :doc:`/reference/query`) +and the plugin skips (or warns about) albums or items that match any query. To use the ``ihate`` plugin, enable it in your configuration (see :ref:`using-plugins`). @@ -12,15 +11,17 @@ To use the ``ihate`` plugin, enable it in your configuration (see Configuration ------------- -To configure the plugin, make an ``ihate:`` section in your configuration -file. The available options are: +To configure the plugin, make an ``ihate:`` section in your configuration file. +The available options are: - **skip**: Never import items and albums that match a query in this list. Default: ``[]`` (empty list). - **warn**: Print a warning message for matches in this list of queries. Default: ``[]``. -Here's an example:: +Here's an example: + +:: ihate: warn: diff --git a/docs/plugins/importadded.rst b/docs/plugins/importadded.rst index 2a2e8ea29..8a6f92277 100644 --- a/docs/plugins/importadded.rst +++ b/docs/plugins/importadded.rst @@ -1,8 +1,8 @@ ImportAdded Plugin ================== -The ``importadded`` plugin is useful when an existing collection is imported -and the time when albums and items were added should be preserved. +The ``importadded`` plugin is useful when an existing collection is imported and +the time when albums and items were added should be preserved. To use the ``importadded`` plugin, enable it in your configuration (see :ref:`using-plugins`). @@ -11,30 +11,29 @@ Usage ----- The :abbr:`mtime (modification time)` of files that are imported into the -library are assumed to represent the time when the items were originally -added. +library are assumed to represent the time when the items were originally added. The ``item.added`` field is populated as follows: -* For singleton items with no album, ``item.added`` is set to the item's file +- For singleton items with no album, ``item.added`` is set to the item's file mtime before it was imported. -* For items that are part of an album, ``album.added`` and ``item.added`` are +- For items that are part of an album, ``album.added`` and ``item.added`` are set to the oldest mtime of the files in the album before they were imported. The mtime of album directories is ignored. -This plugin can optionally be configured to also preserve mtimes at -import using the ``preserve_mtimes`` option. +This plugin can optionally be configured to also preserve mtimes at import using +the ``preserve_mtimes`` option. -When ``preserve_write_mtimes`` option is set, this plugin preserves -mtimes after each write to files using the ``item.added`` attribute. +When ``preserve_write_mtimes`` option is set, this plugin preserves mtimes after +each write to files using the ``item.added`` attribute. File modification times are preserved as follows: -* For all items: +- For all items: - * ``item.mtime`` is set to the mtime of the file - from which the item is imported from. - * The mtime of the file ``item.path`` is set to ``item.mtime``. + - ``item.mtime`` is set to the mtime of the file from which the item is + imported from. + - The mtime of the file ``item.path`` is set to ``item.mtime``. Note that there is no ``album.mtime`` field in the database and that the mtime of album directories on disk aren't preserved. @@ -42,16 +41,13 @@ of album directories on disk aren't preserved. Configuration ------------- -To configure the plugin, make an ``importadded:`` section in your -configuration file. There are two options available: +To configure the plugin, make an ``importadded:`` section in your configuration +file. There are two options available: - **preserve_mtimes**: After importing files, re-set their mtimes to their - original value. - Default: ``no``. - + original value. Default: ``no``. - **preserve_write_mtimes**: After writing files, re-set their mtimes to their - original value. - Default: ``no``. + original value. Default: ``no``. Reimport -------- diff --git a/docs/plugins/importfeeds.rst b/docs/plugins/importfeeds.rst index 5f108db86..5246f2bc7 100644 --- a/docs/plugins/importfeeds.rst +++ b/docs/plugins/importfeeds.rst @@ -3,45 +3,43 @@ ImportFeeds Plugin This plugin helps you keep track of newly imported music in your library. -To use the ``importfeeds`` plugin, enable it in your configuration -(see :ref:`using-plugins`). +To use the ``importfeeds`` plugin, enable it in your configuration (see +:ref:`using-plugins`). Configuration ------------- -To configure the plugin, make an ``importfeeds:`` section in your -configuration file. The available options are: +To configure the plugin, make an ``importfeeds:`` section in your configuration +file. The available options are: - **absolute_path**: Use absolute paths instead of relative paths. Some - applications may need this to work properly. - Default: ``no``. -- **dir**: The output directory. - Default: Your beets library directory. + applications may need this to work properly. Default: ``no``. +- **dir**: The output directory. Default: Your beets library directory. - **formats**: Select the kind of output. Use one or more of: - - **m3u**: Catalog the imports in a centralized playlist. - - **m3u_multi**: Create a new playlist for each import (uniquely named by - appending the date and track/album name). - - **m3u_session**: Create a new playlist for each import session. The file - is named as ``m3u_name`` appending the date and time the import session - was started. - - **link**: Create a symlink for each imported item. This is the - recommended setting to propagate beets imports to your iTunes library: - just drag and drop the ``dir`` folder on the iTunes dock icon. - - **echo**: Do not write a playlist file at all, but echo a list of new - file paths to the terminal. + - **m3u**: Catalog the imports in a centralized playlist. + - **m3u_multi**: Create a new playlist for each import (uniquely named by + appending the date and track/album name). + - **m3u_session**: Create a new playlist for each import session. The file + is named as ``m3u_name`` appending the date and time the import session + was started. + - **link**: Create a symlink for each imported item. This is the + recommended setting to propagate beets imports to your iTunes library: + just drag and drop the ``dir`` folder on the iTunes dock icon. + - **echo**: Do not write a playlist file at all, but echo a list of new + file paths to the terminal. Default: None. -- **m3u_name**: Playlist name used by the ``m3u`` format and as a prefix used - by the ``m3u_session`` format. - Default: ``imported.m3u``. -- **relative_to**: Make the m3u paths relative to another - folder than where the playlist is being written. If you're using importfeeds - to generate a playlist for MPD, you should set this to the root of your music - library. - Default: None. -Here's an example configuration for this plugin:: +- **m3u_name**: Playlist name used by the ``m3u`` format and as a prefix used by + the ``m3u_session`` format. Default: ``imported.m3u``. +- **relative_to**: Make the m3u paths relative to another folder than where the + playlist is being written. If you're using importfeeds to generate a playlist + for MPD, you should set this to the root of your music library. Default: None. + +Here's an example configuration for this plugin: + +:: importfeeds: formats: m3u link diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 5fbe42d9f..1dfa3aae2 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -13,7 +13,8 @@ Using Plugins ------------- To use one of the plugins included with beets (see the rest of this page for a -list), just use the ``plugins`` option in your :doc:`config.yaml </reference/config>` file: +list), just use the ``plugins`` option in your :doc:`config.yaml +</reference/config>` file: .. code-block:: sh @@ -23,7 +24,8 @@ The value for ``plugins`` can be a space-separated list of plugin names or a YAML list like ``[foo, bar]``. You can see which plugins are currently enabled by typing ``beet version``. -Each plugin has its own set of options that can be defined in a section bearing its name: +Each plugin has its own set of options that can be defined in a section bearing +its name: .. code-block:: yaml @@ -33,8 +35,8 @@ Each plugin has its own set of options that can be defined in a section bearing auto: true Some plugins have special dependencies that you'll need to install. The -documentation page for each plugin will list them in the setup instructions. -For some, you can use ``pip``'s "extras" feature to install the dependencies: +documentation page for each plugin will list them in the setup instructions. For +some, you can use ``pip``'s "extras" feature to install the dependencies: .. code-block:: sh @@ -49,8 +51,7 @@ Some plugins provide sources for metadata in addition to MusicBrainz. These plugins share the following configuration option: - **source_weight**: Penalty applied to matches during import. Set to 0.0 to - disable. - Default: ``0.5``. + disable. Default: ``0.5``. For example, to equally consider matches from Discogs and MusicBrainz add the following to your configuration: @@ -62,85 +63,84 @@ following to your configuration: discogs: source_weight: 0.0 - .. toctree:: - :hidden: + :hidden: - absubmit - acousticbrainz - advancedrewrite - albumtypes - aura - autobpm - badfiles - bareasc - beatport - bpd - bpm - bpsync - bucket - chroma - convert - deezer - discogs - duplicates - edit - embedart - embyupdate - export - fetchart - filefilter - fish - freedesktop - fromfilename - ftintitle - fuzzy - gmusic - hook - ihate - importadded - importfeeds - info - inline - ipfs - keyfinder - kodiupdate - lastgenre - lastimport - limit - listenbrainz - loadext - lyrics - mbcollection - mbsubmit - mbsync - metasync - missing - mpdstats - mpdupdate - musicbrainz - parentwork - permissions - play - playlist - plexupdate - random - replace - replaygain - rewrite - scrub - smartplaylist - sonosupdate - spotify - subsonicplaylist - subsonicupdate - substitute - the - thumbnails - types - unimported - web - zero + absubmit + acousticbrainz + advancedrewrite + albumtypes + aura + autobpm + badfiles + bareasc + beatport + bpd + bpm + bpsync + bucket + chroma + convert + deezer + discogs + duplicates + edit + embedart + embyupdate + export + fetchart + filefilter + fish + freedesktop + fromfilename + ftintitle + fuzzy + gmusic + hook + ihate + importadded + importfeeds + info + inline + ipfs + keyfinder + kodiupdate + lastgenre + lastimport + limit + listenbrainz + loadext + lyrics + mbcollection + mbsubmit + mbsync + metasync + missing + mpdstats + mpdupdate + musicbrainz + parentwork + permissions + play + playlist + plexupdate + random + replace + replaygain + rewrite + scrub + smartplaylist + sonosupdate + spotify + subsonicplaylist + subsonicupdate + substitute + the + thumbnails + types + unimported + web + zero .. _autotagger_extensions: @@ -148,259 +148,263 @@ Autotagger Extensions --------------------- :doc:`chroma <chroma>` - Use acoustic fingerprinting to identify audio files with - missing or incorrect metadata. + Use acoustic fingerprinting to identify audio files with missing or + incorrect metadata. :doc:`deezer <deezer>` - Search for releases in the `Deezer`_ database. + Search for releases in the Deezer_ database. :doc:`discogs <discogs>` - Search for releases in the `Discogs`_ database. + Search for releases in the Discogs_ database. :doc:`fromfilename <fromfilename>` - Guess metadata for untagged tracks from their filenames. + Guess metadata for untagged tracks from their filenames. :doc:`musicbrainz <musicbrainz>` - Search for releases in the `MusicBrainz`_ database. + Search for releases in the MusicBrainz_ database. :doc:`spotify <spotify>` - Search for releases in the `Spotify`_ database. + Search for releases in the Spotify_ database. +.. _deezer: https://www.deezer.com -.. _Deezer: https://www.deezer.com -.. _Discogs: https://www.discogs.com -.. _MusicBrainz: https://www.musicbrainz.com -.. _Spotify: https://www.spotify.com +.. _discogs: https://www.discogs.com + +.. _musicbrainz: https://www.musicbrainz.com + +.. _spotify: https://www.spotify.com Metadata -------- :doc:`absubmit <absubmit>` - Analyse audio with the `streaming_extractor_music`_ program and submit the metadata to an AcousticBrainz server + Analyse audio with the streaming_extractor_music_ program and submit the + metadata to an AcousticBrainz server :doc:`acousticbrainz <acousticbrainz>` - Fetch various AcousticBrainz metadata + Fetch various AcousticBrainz metadata :doc:`autobpm <autobpm>` - Use `Librosa`_ to calculate the BPM from the audio. + Use Librosa_ to calculate the BPM from the audio. :doc:`bpm <bpm>` - Measure tempo using keystrokes. + Measure tempo using keystrokes. :doc:`bpsync <bpsync>` - Fetch updated metadata from Beatport. + Fetch updated metadata from Beatport. :doc:`edit <edit>` - Edit metadata from a text editor. + Edit metadata from a text editor. :doc:`embedart <embedart>` - Embed album art images into files' metadata. + Embed album art images into files' metadata. :doc:`fetchart <fetchart>` - Fetch album cover art from various sources. + Fetch album cover art from various sources. :doc:`ftintitle <ftintitle>` - Move "featured" artists from the artist field to the title - field. + Move "featured" artists from the artist field to the title field. :doc:`keyfinder <keyfinder>` - Use the `KeyFinder`_ program to detect the musical - key from the audio. + Use the KeyFinder_ program to detect the musical key from the audio. :doc:`importadded <importadded>` - Use file modification times for guessing the value for - the `added` field in the database. + Use file modification times for guessing the value for the ``added`` field + in the database. :doc:`lastgenre <lastgenre>` - Fetch genres based on Last.fm tags. + Fetch genres based on Last.fm tags. :doc:`lastimport <lastimport>` - Collect play counts from Last.fm. + Collect play counts from Last.fm. :doc:`lyrics <lyrics>` - Automatically fetch song lyrics. + Automatically fetch song lyrics. :doc:`mbsync <mbsync>` - Fetch updated metadata from MusicBrainz. + Fetch updated metadata from MusicBrainz. :doc:`metasync <metasync>` - Fetch metadata from local or remote sources + Fetch metadata from local or remote sources :doc:`mpdstats <mpdstats>` - Connect to `MPD`_ and update the beets library with play - statistics (last_played, play_count, skip_count, rating). + Connect to MPD_ and update the beets library with play statistics + (last_played, play_count, skip_count, rating). :doc:`parentwork <parentwork>` - Fetch work titles and works they are part of. + Fetch work titles and works they are part of. :doc:`replaygain <replaygain>` - Calculate volume normalization for players that support it. + Calculate volume normalization for players that support it. :doc:`scrub <scrub>` - Clean extraneous metadata from music files. + Clean extraneous metadata from music files. :doc:`zero <zero>` - Nullify fields by pattern or unconditionally. + Nullify fields by pattern or unconditionally. + +.. _keyfinder: http://www.ibrahimshaath.co.uk/keyfinder/ + +.. _librosa: https://github.com/librosa/librosa/ -.. _Librosa: https://github.com/librosa/librosa/ -.. _KeyFinder: http://www.ibrahimshaath.co.uk/keyfinder/ .. _streaming_extractor_music: https://acousticbrainz.org/download Path Formats ------------ :doc:`albumtypes <albumtypes>` - Format album type in path formats. + Format album type in path formats. :doc:`bucket <bucket>` - Group your files into bucket directories that cover different - field values ranges. + Group your files into bucket directories that cover different field values + ranges. :doc:`inline <inline>` - Use Python snippets to customize path format strings. + Use Python snippets to customize path format strings. :doc:`rewrite <rewrite>` - Substitute values in path formats. + Substitute values in path formats. :doc:`advancedrewrite <advancedrewrite>` - Substitute field values for items matching a query. + Substitute field values for items matching a query. :doc:`substitute <substitute>` - As an alternative to :doc:`rewrite <rewrite>`, use this plugin. The main - difference between them is that this plugin never modifies the files - metadata. + As an alternative to :doc:`rewrite <rewrite>`, use this plugin. The main + difference between them is that this plugin never modifies the files + metadata. :doc:`the <the>` - Move patterns in path formats (i.e., move "a" and "the" to the - end). + Move patterns in path formats (i.e., move "a" and "the" to the end). Interoperability ---------------- :doc:`aura <aura>` - A server implementation of the `AURA`_ specification. + A server implementation of the AURA_ specification. :doc:`badfiles <badfiles>` - Check audio file integrity. + Check audio file integrity. :doc:`embyupdate <embyupdate>` - Automatically notifies `Emby`_ whenever the beets library changes. + Automatically notifies Emby_ whenever the beets library changes. :doc:`fish <fish>` - Adds `Fish shell`_ tab autocompletion to ``beet`` commands. + Adds `Fish shell`_ tab autocompletion to ``beet`` commands. :doc:`importfeeds <importfeeds>` - Keep track of imported files via ``.m3u`` playlist file(s) or symlinks. + Keep track of imported files via ``.m3u`` playlist file(s) or symlinks. :doc:`ipfs <ipfs>` - Import libraries from friends and get albums from them via ipfs. + Import libraries from friends and get albums from them via ipfs. :doc:`kodiupdate <kodiupdate>` - Automatically notifies `Kodi`_ whenever the beets library - changes. + Automatically notifies Kodi_ whenever the beets library changes. :doc:`mpdupdate <mpdupdate>` - Automatically notifies `MPD`_ whenever the beets library - changes. + Automatically notifies MPD_ whenever the beets library changes. :doc:`play <play>` - Play beets queries in your music player. + Play beets queries in your music player. :doc:`playlist <playlist>` - Use M3U playlists to query the beets library. + Use M3U playlists to query the beets library. :doc:`plexupdate <plexupdate>` - Automatically notifies `Plex`_ whenever the beets library - changes. + Automatically notifies Plex_ whenever the beets library changes. :doc:`smartplaylist <smartplaylist>` - Generate smart playlists based on beets queries. + Generate smart playlists based on beets queries. :doc:`sonosupdate <sonosupdate>` - Automatically notifies `Sonos`_ whenever the beets library - changes. + Automatically notifies Sonos_ whenever the beets library changes. :doc:`thumbnails <thumbnails>` - Get thumbnails with the cover art on your album folders. + Get thumbnails with the cover art on your album folders. :doc:`subsonicupdate <subsonicupdate>` - Automatically notifies `Subsonic`_ whenever the beets - library changes. + Automatically notifies Subsonic_ whenever the beets library changes. +.. _aura: https://auraspec.readthedocs.io -.. _AURA: https://auraspec.readthedocs.io -.. _Emby: https://emby.media -.. _Fish shell: https://fishshell.com/ -.. _Plex: https://plex.tv -.. _Kodi: https://kodi.tv -.. _Sonos: https://sonos.com -.. _Subsonic: http://www.subsonic.org/ +.. _emby: https://emby.media + +.. _fish shell: https://fishshell.com/ + +.. _kodi: https://kodi.tv + +.. _plex: https://plex.tv + +.. _sonos: https://sonos.com + +.. _subsonic: http://www.subsonic.org/ Miscellaneous ------------- :doc:`bareasc <bareasc>` - Search albums and tracks with bare ASCII string matching. + Search albums and tracks with bare ASCII string matching. :doc:`bpd <bpd>` - A music player for your beets library that emulates `MPD`_ and is - compatible with `MPD clients`_. + A music player for your beets library that emulates MPD_ and is compatible + with `MPD clients`_. :doc:`convert <convert>` - Transcode music and embed album art while exporting to - a different directory. + Transcode music and embed album art while exporting to a different + directory. :doc:`duplicates <duplicates>` - List duplicate tracks or albums. + List duplicate tracks or albums. :doc:`export <export>` - Export data from queries to a format. + Export data from queries to a format. :doc:`filefilter <filefilter>` - Automatically skip files during the import process based - on regular expressions. + Automatically skip files during the import process based on regular + expressions. :doc:`fuzzy <fuzzy>` - Search albums and tracks with fuzzy string matching. + Search albums and tracks with fuzzy string matching. :doc:`hook <hook>` - Run a command when an event is emitted by beets. + Run a command when an event is emitted by beets. :doc:`ihate <ihate>` - Automatically skip albums and tracks during the import process. + Automatically skip albums and tracks during the import process. :doc:`info <info>` - Print music files' tags to the console. + Print music files' tags to the console. :doc:`loadext <loadext>` - Load SQLite extensions. + Load SQLite extensions. :doc:`mbcollection <mbcollection>` - Maintain your MusicBrainz collection list. + Maintain your MusicBrainz collection list. :doc:`mbsubmit <mbsubmit>` - Print an album's tracks in a MusicBrainz-friendly format. + Print an album's tracks in a MusicBrainz-friendly format. :doc:`missing <missing>` - List missing tracks. + List missing tracks. -`mstream`_ - A music streaming server + webapp that can be used alongside beets. +mstream_ + A music streaming server + webapp that can be used alongside beets. :doc:`random <random>` - Randomly choose albums and tracks from your library. + Randomly choose albums and tracks from your library. :doc:`spotify <spotify>` - Create Spotify playlists from the Beets library. + Create Spotify playlists from the Beets library. :doc:`types <types>` - Declare types for flexible attributes. + Declare types for flexible attributes. :doc:`web <web>` - An experimental Web-based GUI for beets. + An experimental Web-based GUI for beets. + +.. _mpd: https://www.musicpd.org/ + +.. _mpd clients: https://mpd.wikia.com/wiki/Clients -.. _MPD: https://www.musicpd.org/ -.. _MPD clients: https://mpd.wikia.com/wiki/Clients .. _mstream: https://github.com/IrosTheBeggar/mStream .. _other-plugins: @@ -408,204 +412,248 @@ Miscellaneous Other Plugins ------------- -In addition to the plugins that come with beets, there are several plugins -that are maintained by the beets community. To use an external plugin, there -are two options for installation: +In addition to the plugins that come with beets, there are several plugins that +are maintained by the beets community. To use an external plugin, there are two +options for installation: -* Make sure it's in the Python path (known as ``sys.path`` to developers). This +- Make sure it's in the Python path (known as ``sys.path`` to developers). This just means the plugin has to be installed on your system (e.g., with a ``setup.py`` script or a command like ``pip`` or ``easy_install``). - -* Set the ``pluginpath`` config variable to point to the directory containing the - plugin. (See :doc:`/reference/config`.) +- Set the ``pluginpath`` config variable to point to the directory containing + the plugin. (See :doc:`/reference/config`.) Once the plugin is installed, enable it by placing its name on the ``plugins`` line in your config file. Here are a few of the plugins written by the beets community: -`beets-alternatives`_ - Manages external files. +beets-alternatives_ + Manages external files. -`beet-amazon`_ - Adds Amazon.com as a tagger data source. +beet-amazon_ + Adds Amazon.com as a tagger data source. -`beets-artistcountry`_ - Fetches the artist's country of origin from MusicBrainz. +beets-artistcountry_ + Fetches the artist's country of origin from MusicBrainz. -`beets-autofix`_ - Automates repetitive tasks to keep your library in order. +beets-autofix_ + Automates repetitive tasks to keep your library in order. -`beets-autogenre`_ - Assigns genres to your library items using the :doc:`lastgenre <lastgenre>` - and `beets-xtractor`_ plugins as well as additional rules. +beets-autogenre_ + Assigns genres to your library items using the :doc:`lastgenre <lastgenre>` + and beets-xtractor_ plugins as well as additional rules. -`beets-audible`_ - Adds Audible as a tagger data source and provides - other features for managing audiobook collections. +beets-audible_ + Adds Audible as a tagger data source and provides other features for + managing audiobook collections. -`beets-barcode`_ - Lets you scan or enter barcodes for physical media to - search for their metadata. +beets-barcode_ + Lets you scan or enter barcodes for physical media to search for their + metadata. -`beetcamp`_ - Enables **bandcamp.com** autotagger with a fairly extensive amount of metadata. +beetcamp_ + Enables **bandcamp.com** autotagger with a fairly extensive amount of + metadata. -`beetstream`_ - Server implementation of the `Subsonic API`_ specification, serving the - beets library and (:doc:`smartplaylist <smartplaylist>` plugin generated) - M3U playlists, allowing you to stream your music on a multitude of clients. +beetstream_ + Server implementation of the `Subsonic API`_ specification, serving the + beets library and (:doc:`smartplaylist <smartplaylist>` plugin generated) + M3U playlists, allowing you to stream your music on a multitude of clients. -`beets-bpmanalyser`_ - Analyses songs and calculates their tempo (BPM). +beets-bpmanalyser_ + Analyses songs and calculates their tempo (BPM). -`beets-check`_ - Automatically checksums your files to detect corruption. +beets-check_ + Automatically checksums your files to detect corruption. `A cmus plugin`_ - Integrates with the `cmus`_ console music player. + Integrates with the cmus_ console music player. -`beets-copyartifacts`_ - Helps bring non-music files along during import. +beets-copyartifacts_ + Helps bring non-music files along during import. -`beets-describe`_ - Gives you the full picture of a single attribute of your library items. +beets-describe_ + Gives you the full picture of a single attribute of your library items. -`drop2beets`_ - Automatically imports singles as soon as they are dropped in a - folder (using Linux's ``inotify``). You can also set a sub-folders - hierarchy to set flexible attributes by the way. +drop2beets_ + Automatically imports singles as soon as they are dropped in a folder (using + Linux's ``inotify``). You can also set a sub-folders hierarchy to set + flexible attributes by the way. -`dsedivec`_ - Has two plugins: ``edit`` and ``moveall``. +dsedivec_ + Has two plugins: ``edit`` and ``moveall``. -`beets-filetote`_ - Helps bring non-music extra files, attachments, and artifacts during - imports and CLI file manipulation actions (`beet move`, etc.). +beets-filetote_ + Helps bring non-music extra files, attachments, and artifacts during imports + and CLI file manipulation actions (``beet move``, etc.). -`beets-follow`_ - Lets you check for new albums from artists you like. +beets-follow_ + Lets you check for new albums from artists you like. -`beetFs`_ - Is a FUSE filesystem for browsing the music in your beets library. - (Might be out of date.) +beetFs_ + Is a FUSE filesystem for browsing the music in your beets library. (Might be + out of date.) -`beets-goingrunning`_ - Generates playlists to go with your running sessions. +beets-goingrunning_ + Generates playlists to go with your running sessions. -`beets-ibroadcast`_ - Uploads tracks to the `iBroadcast`_ cloud service. +beets-ibroadcast_ + Uploads tracks to the iBroadcast_ cloud service. -`beets-id3extract`_ - Maps arbitrary ID3 tags to beets custom fields. +beets-id3extract_ + Maps arbitrary ID3 tags to beets custom fields. -`beets-importreplace`_ - Lets you perform regex replacements on incoming - metadata. +beets-importreplace_ + Lets you perform regex replacements on incoming metadata. -`beets-jiosaavn`_ - Adds JioSaavn.com as a tagger data source. +beets-jiosaavn_ + Adds JioSaavn.com as a tagger data source. -`beets-more`_ - Finds versions of indexed releases with more tracks, like deluxe and anniversary editions. +beets-more_ + Finds versions of indexed releases with more tracks, like deluxe and + anniversary editions. -`beets-mosaic`_ - Generates a montage of a mosaic from cover art. +beets-mosaic_ + Generates a montage of a mosaic from cover art. -`beets-mpd-utils`_ - Plugins to interface with `MPD`_. Comes with ``mpd_tracker`` (track play/skip counts from MPD) and ``mpd_dj`` (auto-add songs to your queue.) +beets-mpd-utils_ + Plugins to interface with MPD_. Comes with ``mpd_tracker`` (track play/skip + counts from MPD) and ``mpd_dj`` (auto-add songs to your queue.) -`beets-noimport`_ - Adds and removes directories from the incremental import skip list. +beets-noimport_ + Adds and removes directories from the incremental import skip list. -`beets-originquery`_ - Augments MusicBrainz queries with locally-sourced data - to improve autotagger results. +beets-originquery_ + Augments MusicBrainz queries with locally-sourced data to improve autotagger + results. -`beets-plexsync`_ - Allows you to sync your Plex library with your beets library, create smart playlists in Plex, and import online playlists (from services like Spotify) into Plex. +beets-plexsync_ + Allows you to sync your Plex library with your beets library, create smart + playlists in Plex, and import online playlists (from services like Spotify) + into Plex. -`beets-setlister`_ - Generate playlists from the setlists of a given artist. +beets-setlister_ + Generate playlists from the setlists of a given artist. -`beet-summarize`_ - Can compute lots of counts and statistics about your music - library. +beet-summarize_ + Can compute lots of counts and statistics about your music library. -`beets-usertag`_ - Lets you use keywords to tag and organize your music. +beets-usertag_ + Lets you use keywords to tag and organize your music. -`beets-webm3u`_ - Serves the (:doc:`smartplaylist <smartplaylist>` plugin generated) M3U - playlists via HTTP. +beets-webm3u_ + Serves the (:doc:`smartplaylist <smartplaylist>` plugin generated) M3U + playlists via HTTP. -`beets-webrouter`_ - Serves multiple beets webapps (e.g. :doc:`web <web>`, `beets-webm3u`_, - `beetstream`_, :doc:`aura <aura>`) using a single command/process/host/port, - each under a different path. +beets-webrouter_ + Serves multiple beets webapps (e.g. :doc:`web <web>`, beets-webm3u_, + beetstream_, :doc:`aura <aura>`) using a single command/process/host/port, + each under a different path. -`whatlastgenre`_ - Fetches genres from various music sites. +whatlastgenre_ + Fetches genres from various music sites. -`beets-xtractor`_ - Extracts low- and high-level musical information from your songs. +beets-xtractor_ + Extracts low- and high-level musical information from your songs. -`beets-ydl`_ - Downloads audio from youtube-dl sources and import into beets. +beets-ydl_ + Downloads audio from youtube-dl sources and import into beets. -`beets-ytimport`_ - Download and import your liked songs from YouTube into beets. +beets-ytimport_ + Download and import your liked songs from YouTube into beets. -`beets-yearfixer`_ - Attempts to fix all missing ``original_year`` and ``year`` fields. +beets-yearfixer_ + Attempts to fix all missing ``original_year`` and ``year`` fields. -`beets-youtube`_ - Adds YouTube Music as a tagger data source. +beets-youtube_ + Adds YouTube Music as a tagger data source. + +.. _a cmus plugin: https://github.com/coolkehon/beets/blob/master/beetsplug/cmus.py + +.. _beet-amazon: https://github.com/jmwatte/beet-amazon + +.. _beet-musicbrainz-collection: https://github.com/jeffayle/Beet-MusicBrainz-Collection/ + +.. _beet-summarize: https://github.com/steven-murray/beet-summarize + +.. _beetcamp: https://github.com/snejus/beetcamp + +.. _beetfs: https://github.com/jbaiter/beetfs + +.. _beets-alternatives: https://github.com/geigerzaehler/beets-alternatives + +.. _beets-artistcountry: https://github.com/agrausem/beets-artistcountry + +.. _beets-audible: https://github.com/Neurrone/beets-audible + +.. _beets-autofix: https://github.com/adamjakab/BeetsPluginAutofix + +.. _beets-autogenre: https://github.com/mgoltzsche/beets-autogenre .. _beets-barcode: https://github.com/8h2a/beets-barcode -.. _beetcamp: https://github.com/snejus/beetcamp -.. _beetstream: https://github.com/BinaryBrain/Beetstream -.. _Subsonic API: http://www.subsonic.org/pages/api.jsp -.. _beets-check: https://github.com/geigerzaehler/beets-check -.. _beets-copyartifacts: https://github.com/adammillerio/beets-copyartifacts -.. _dsedivec: https://github.com/dsedivec/beets-plugins -.. _beets-artistcountry: https://github.com/agrausem/beets-artistcountry -.. _beetFs: https://github.com/jbaiter/beetfs -.. _Beet-MusicBrainz-Collection: - https://github.com/jeffayle/Beet-MusicBrainz-Collection/ -.. _A cmus plugin: - https://github.com/coolkehon/beets/blob/master/beetsplug/cmus.py -.. _cmus: http://cmus.sourceforge.net/ -.. _beet-amazon: https://github.com/jmwatte/beet-amazon -.. _beets-alternatives: https://github.com/geigerzaehler/beets-alternatives -.. _beets-filetote: https://github.com/gtronset/beets-filetote -.. _beets-follow: https://github.com/nolsto/beets-follow -.. _beets-ibroadcast: https://github.com/ctrueden/beets-ibroadcast -.. _iBroadcast: https://ibroadcast.com/ -.. _beets-id3extract: https://github.com/bcotton/beets-id3extract -.. _beets-importreplace: https://github.com/edgars-supe/beets-importreplace -.. _beets-setlister: https://github.com/tomjaspers/beets-setlister -.. _beets-noimport: https://gitlab.com/tiago.dias/beets-noimport -.. _whatlastgenre: https://github.com/YetAnotherNerd/whatlastgenre/tree/master/plugin/beets -.. _beets-usertag: https://github.com/igordertigor/beets-usertag -.. _beets-plexsync: https://github.com/arsaboo/beets-plexsync -.. _beets-jiosaavn: https://github.com/arsaboo/beets-jiosaavn -.. _beets-youtube: https://github.com/arsaboo/beets-youtube -.. _beets-ydl: https://github.com/vmassuchetto/beets-ydl -.. _beets-ytimport: https://github.com/mgoltzsche/beets-ytimport -.. _beet-summarize: https://github.com/steven-murray/beet-summarize -.. _beets-mosaic: https://github.com/SusannaMaria/beets-mosaic -.. _beets-goingrunning: https://pypi.org/project/beets-goingrunning -.. _beets-xtractor: https://github.com/adamjakab/BeetsPluginXtractor -.. _beets-yearfixer: https://github.com/adamjakab/BeetsPluginYearFixer -.. _beets-autofix: https://github.com/adamjakab/BeetsPluginAutofix -.. _beets-describe: https://github.com/adamjakab/BeetsPluginDescribe + .. _beets-bpmanalyser: https://github.com/adamjakab/BeetsPluginBpmAnalyser -.. _beets-originquery: https://github.com/x1ppy/beets-originquery -.. _drop2beets: https://github.com/martinkirch/drop2beets -.. _beets-audible: https://github.com/Neurrone/beets-audible + +.. _beets-check: https://github.com/geigerzaehler/beets-check + +.. _beets-copyartifacts: https://github.com/adammillerio/beets-copyartifacts + +.. _beets-describe: https://github.com/adamjakab/BeetsPluginDescribe + +.. _beets-filetote: https://github.com/gtronset/beets-filetote + +.. _beets-follow: https://github.com/nolsto/beets-follow + +.. _beets-goingrunning: https://pypi.org/project/beets-goingrunning + +.. _beets-ibroadcast: https://github.com/ctrueden/beets-ibroadcast + +.. _beets-id3extract: https://github.com/bcotton/beets-id3extract + +.. _beets-importreplace: https://github.com/edgars-supe/beets-importreplace + +.. _beets-jiosaavn: https://github.com/arsaboo/beets-jiosaavn + .. _beets-more: https://forgejo.sny.sh/sun/beetsplug/src/branch/main/more + +.. _beets-mosaic: https://github.com/SusannaMaria/beets-mosaic + .. _beets-mpd-utils: https://github.com/thekakkun/beets-mpd-utils + +.. _beets-noimport: https://gitlab.com/tiago.dias/beets-noimport + +.. _beets-originquery: https://github.com/x1ppy/beets-originquery + +.. _beets-plexsync: https://github.com/arsaboo/beets-plexsync + +.. _beets-setlister: https://github.com/tomjaspers/beets-setlister + +.. _beets-usertag: https://github.com/igordertigor/beets-usertag + .. _beets-webm3u: https://github.com/mgoltzsche/beets-webm3u + .. _beets-webrouter: https://github.com/mgoltzsche/beets-webrouter -.. _beets-autogenre: https://github.com/mgoltzsche/beets-autogenre + +.. _beets-xtractor: https://github.com/adamjakab/BeetsPluginXtractor + +.. _beets-ydl: https://github.com/vmassuchetto/beets-ydl + +.. _beets-yearfixer: https://github.com/adamjakab/BeetsPluginYearFixer + +.. _beets-youtube: https://github.com/arsaboo/beets-youtube + +.. _beets-ytimport: https://github.com/mgoltzsche/beets-ytimport + +.. _beetstream: https://github.com/BinaryBrain/Beetstream + +.. _cmus: http://cmus.sourceforge.net/ + +.. _drop2beets: https://github.com/martinkirch/drop2beets + +.. _dsedivec: https://github.com/dsedivec/beets-plugins + +.. _ibroadcast: https://ibroadcast.com/ + +.. _subsonic api: http://www.subsonic.org/pages/api.jsp + +.. _whatlastgenre: https://github.com/YetAnotherNerd/whatlastgenre/tree/master/plugin/beets diff --git a/docs/plugins/info.rst b/docs/plugins/info.rst index 1ed7582af..051b081ef 100644 --- a/docs/plugins/info.rst +++ b/docs/plugins/info.rst @@ -1,45 +1,52 @@ Info Plugin =========== -The ``info`` plugin provides a command that dumps the current tag values for -any file format supported by beets. It works like a supercharged version of -`mp3info`_ or `id3v2`_. +The ``info`` plugin provides a command that dumps the current tag values for any +file format supported by beets. It works like a supercharged version of mp3info_ +or id3v2_. Enable the ``info`` plugin in your configuration (see :ref:`using-plugins`) and -then type:: +then type: + +:: $ beet info /path/to/music.flac and the plugin will enumerate all the tags in the specified file. It also accepts multiple filenames in a single command-line. -You can also enter a :doc:`query </reference/query>` to inspect music from -your library:: +You can also enter a :doc:`query </reference/query>` to inspect music from your +library: + +:: $ beet info beatles -If you just want to see specific properties you can use the -``--include-keys`` option to filter them. The argument is a -comma-separated list of field names. For example:: +If you just want to see specific properties you can use the ``--include-keys`` +option to filter them. The argument is a comma-separated list of field names. +For example: + +:: $ beet info -i 'title,mb_artistid' beatles -Will only show the ``title`` and ``mb_artistid`` properties. You can add the +Will only show the ``title`` and ``mb_artistid`` properties. You can add the ``-i`` option multiple times to the command line. Additional command-line options include: -* ``--library`` or ``-l``: Show data from the library database instead of the +- ``--library`` or ``-l``: Show data from the library database instead of the files' tags. -* ``--album`` or ``-a``: Show data from albums instead of tracks (implies +- ``--album`` or ``-a``: Show data from albums instead of tracks (implies ``--library``). -* ``--summarize`` or ``-s``: Merge all the information from multiple files - into a single list of values. If the tags differ across the files, print +- ``--summarize`` or ``-s``: Merge all the information from multiple files into + a single list of values. If the tags differ across the files, print ``[various]``. -* ``--format`` or ``-f``: Specify a specific format with which to print every +- ``--format`` or ``-f``: Specify a specific format with which to print every item. This uses the same template syntax as beets’ :doc:`path formats </reference/pathformat>`. -* ``--keys-only`` or ``-k``: Show the name of the tags without the values. +- ``--keys-only`` or ``-k``: Show the name of the tags without the values. .. _id3v2: http://id3v2.sourceforge.net + .. _mp3info: https://www.ibiblio.org/mp3info/ diff --git a/docs/plugins/inline.rst b/docs/plugins/inline.rst index 4dfca261d..46ee3d634 100644 --- a/docs/plugins/inline.rst +++ b/docs/plugins/inline.rst @@ -2,20 +2,21 @@ Inline Plugin ============= The ``inline`` plugin lets you use Python to customize your path formats. Using -it, you can define template fields in your beets configuration file and refer -to them from your template strings in the ``paths:`` section (see +it, you can define template fields in your beets configuration file and refer to +them from your template strings in the ``paths:`` section (see :doc:`/reference/config/`). -To use the ``inline`` plugin, enable it in your configuration -(see :ref:`using-plugins`). -Then, make a ``item_fields:`` block in your config file. Under this key, every line defines a -new template field; the key is the name of the field (you'll use the name to -refer to the field in your templates) and the value is a Python expression or -function body. The Python code has all of a track's fields in scope, so you can -refer to any normal attributes (such as ``artist`` or ``title``) as Python -variables. +To use the ``inline`` plugin, enable it in your configuration (see +:ref:`using-plugins`). Then, make a ``item_fields:`` block in your config file. +Under this key, every line defines a new template field; the key is the name of +the field (you'll use the name to refer to the field in your templates) and the +value is a Python expression or function body. The Python code has all of a +track's fields in scope, so you can refer to any normal attributes (such as +``artist`` or ``title``) as Python variables. -Here are a couple of examples of expressions:: +Here are a couple of examples of expressions: + +:: item_fields: initial: albumartist[0].upper() + u'.' @@ -26,18 +27,21 @@ Note that YAML syntax allows newlines in values if the subsequent lines are indented. These examples define ``$initial`` and ``$disc_and_track`` fields that can be -referenced in path templates like so:: +referenced in path templates like so: + +:: paths: default: $initial/$artist/$album%aunique{}/$disc_and_track $title - Block Definitions ----------------- If you need to use statements like ``import``, you can write a Python function -body instead of a single expression. In this case, you'll need to ``return`` -a result for the value of the path field, like so:: +body instead of a single expression. In this case, you'll need to ``return`` a +result for the value of the path field, like so: + +:: item_fields: filename: | @@ -48,17 +52,18 @@ a result for the value of the path field, like so:: You might want to use the YAML syntax for "block literals," in which a leading ``|`` character indicates a multi-line block of text. - Album Fields ------------ The above examples define fields for *item* templates, but you can also define -fields for *album* templates. Use the ``album_fields`` configuration section. -In this context, all existing album fields are available as variables along -with ``items``, which is a list of items in the album. +fields for *album* templates. Use the ``album_fields`` configuration section. In +this context, all existing album fields are available as variables along with +``items``, which is a list of items in the album. This example defines a ``$bitrate`` field for albums as the average of the -tracks' fields:: +tracks' fields: + +:: album_fields: bitrate: | diff --git a/docs/plugins/ipfs.rst b/docs/plugins/ipfs.rst index 5bf8ca906..6f8144087 100644 --- a/docs/plugins/ipfs.rst +++ b/docs/plugins/ipfs.rst @@ -2,15 +2,15 @@ IPFS Plugin =========== The ``ipfs`` plugin makes it easy to share your library and music with friends. -The plugin uses `ipfs`_ for storing the library and file content. +The plugin uses ipfs_ for storing the library and file content. .. _ipfs: https://ipfs.io/ Installation ------------ -This plugin requires `go-ipfs`_ to be running as a daemon and that the -associated ``ipfs`` command is on the user's ``$PATH``. +This plugin requires go-ipfs_ to be running as a daemon and that the associated +``ipfs`` command is on the user's ``$PATH``. .. _go-ipfs: https://github.com/ipfs/go-ipfs @@ -24,51 +24,54 @@ This plugin can store and retrieve music individually, or it can share entire library databases. Adding -'''''' +~~~~~~ -To add albums to ipfs, making them shareable, use the ``-a`` or ``--add`` -flag. If used without arguments it will add all albums in the local library. -When added, all items and albums will get an "ipfs" field in the database -containing the hash of that specific file/folder. Newly imported albums will -be added automatically to ipfs by default (see below). +To add albums to ipfs, making them shareable, use the ``-a`` or ``--add`` flag. +If used without arguments it will add all albums in the local library. When +added, all items and albums will get an "ipfs" field in the database containing +the hash of that specific file/folder. Newly imported albums will be added +automatically to ipfs by default (see below). Retrieving -'''''''''' +~~~~~~~~~~ You can give the ipfs hash for some music to a friend. They can get that album -from ipfs, and import it into beets, using the ``-g`` or ``--get`` flag. If -the argument passed to the ``-g`` flag isn't an ipfs hash, it will be used as -a query instead, getting all albums matching the query. +from ipfs, and import it into beets, using the ``-g`` or ``--get`` flag. If the +argument passed to the ``-g`` flag isn't an ipfs hash, it will be used as a +query instead, getting all albums matching the query. Sharing Libraries -''''''''''''''''' +~~~~~~~~~~~~~~~~~ Using the ``-p`` or ``--publish`` flag, a copy of the local library will be published to ipfs. Only albums/items with ipfs records in the database will published, and local paths will be stripped from the library. A hash of the library will be returned to the user. -A friend can then import this remote library by using the ``-i`` or -``--import`` flag. To tag an imported library with a specific name by passing -a name as the second argument to ``-i,`` after the hash. The content of all -remote libraries will be combined into an additional library as long as the -content doesn't already exist in the joined library. +A friend can then import this remote library by using the ``-i`` or ``--import`` +flag. To tag an imported library with a specific name by passing a name as the +second argument to ``-i,`` after the hash. The content of all remote libraries +will be combined into an additional library as long as the content doesn't +already exist in the joined library. -When remote libraries has been imported you can search them by using the -``-l`` or ``--list`` flag. The hash of albums matching the query will be -returned, and this can then be used with ``-g`` to fetch and import the album -to the local library. +When remote libraries has been imported you can search them by using the ``-l`` +or ``--list`` flag. The hash of albums matching the query will be returned, and +this can then be used with ``-g`` to fetch and import the album to the local +library. Ipfs can be mounted as a FUSE file system. This means that music in a remote library can be streamed directly, without importing them to the local library -first. If the ``/ipfs`` folder is mounted then matching queries will be sent -to the :doc:`/plugins/play` using the ``-m`` or ``--play`` flag. +first. If the ``/ipfs`` folder is mounted then matching queries will be sent to +the :doc:`/plugins/play` using the ``-m`` or ``--play`` flag. Configuration ------------- The ipfs plugin will automatically add imported albums to ipfs and add those -hashes to the database. This can be turned off by setting the ``auto`` option -in the ``ipfs:`` section of the config to ``no``. +hashes to the database. This can be turned off by setting the ``auto`` option in +the ``ipfs:`` section of the config to ``no``. -If the setting ``nocopy`` is true (defaults false) then the plugin will pass the ``--nocopy`` option when adding things to ipfs. If the filestore option of ipfs is enabled this will mean files are neither removed from beets nor copied somewhere else. +If the setting ``nocopy`` is true (defaults false) then the plugin will pass the +``--nocopy`` option when adding things to ipfs. If the filestore option of ipfs +is enabled this will mean files are neither removed from beets nor copied +somewhere else. diff --git a/docs/plugins/keyfinder.rst b/docs/plugins/keyfinder.rst index a5c64d39c..c692c5407 100644 --- a/docs/plugins/keyfinder.rst +++ b/docs/plugins/keyfinder.rst @@ -1,11 +1,10 @@ Key Finder Plugin ================= -The `keyfinder` plugin uses either the `KeyFinder`_ or `keyfinder-cli`_ -program to detect the musical key of a track from its audio data and store -it in the `initial_key` field of your database. It does so -automatically when importing music or through the ``beet keyfinder -[QUERY]`` command. +The ``keyfinder`` plugin uses either the KeyFinder_ or keyfinder-cli_ program to +detect the musical key of a track from its audio data and store it in the +``initial_key`` field of your database. It does so automatically when importing +music or through the ``beet keyfinder [QUERY]`` command. To use the ``keyfinder`` plugin, enable it in your configuration (see :ref:`using-plugins`). @@ -13,23 +12,20 @@ To use the ``keyfinder`` plugin, enable it in your configuration (see Configuration ------------- -To configure the plugin, make a ``keyfinder:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``keyfinder:`` section in your configuration +file. The available options are: -- **auto**: Analyze every file on - import. Otherwise, you need to use the ``beet keyfinder`` command - explicitly. - Default: ``yes`` +- **auto**: Analyze every file on import. Otherwise, you need to use the ``beet + keyfinder`` command explicitly. Default: ``yes`` - **bin**: The name of the program use for key analysis. You can use either - `KeyFinder`_ or `keyfinder-cli`_. - If you installed the KeyFinder GUI on a Mac, for example, you want - something like - ``/Applications/KeyFinder.app/Contents/MacOS/KeyFinder``. - If using `keyfinder-cli`_, the binary must be named ``keyfinder-cli``. - Default: ``KeyFinder`` (i.e., search for the program in your ``$PATH``).. + KeyFinder_ or keyfinder-cli_. If you installed the KeyFinder GUI on a Mac, for + example, you want something like + ``/Applications/KeyFinder.app/Contents/MacOS/KeyFinder``. If using + keyfinder-cli_, the binary must be named ``keyfinder-cli``. Default: + ``KeyFinder`` (i.e., search for the program in your ``$PATH``).. - **overwrite**: Calculate a key even for files that already have an - `initial_key` value. - Default: ``no``. + ``initial_key`` value. Default: ``no``. + +.. _keyfinder: http://www.ibrahimshaath.co.uk/keyfinder/ -.. _KeyFinder: http://www.ibrahimshaath.co.uk/keyfinder/ .. _keyfinder-cli: https://github.com/EvanPurkhiser/keyfinder-cli/ diff --git a/docs/plugins/kodiupdate.rst b/docs/plugins/kodiupdate.rst index 90b33d9c1..20bc38b0f 100644 --- a/docs/plugins/kodiupdate.rst +++ b/docs/plugins/kodiupdate.rst @@ -1,14 +1,15 @@ KodiUpdate Plugin ================= -The ``kodiupdate`` plugin lets you automatically update `Kodi`_'s music -library whenever you change your beets library. +The ``kodiupdate`` plugin lets you automatically update Kodi_'s music library +whenever you change your beets library. -To use ``kodiupdate`` plugin, enable it in your configuration -(see :ref:`using-plugins`). -Then, you'll want to configure the specifics of your Kodi host. -You can do that using a ``kodi:`` section in your ``config.yaml``, -which looks like this:: +To use ``kodiupdate`` plugin, enable it in your configuration (see +:ref:`using-plugins`). Then, you'll want to configure the specifics of your Kodi +host. You can do that using a ``kodi:`` section in your ``config.yaml``, which +looks like this: + +:: kodi: host: localhost @@ -16,7 +17,9 @@ which looks like this:: user: kodi pwd: kodi -To update multiple Kodi instances, specify them as an array:: +To update multiple Kodi instances, specify them as an array: + +:: kodi: - host: x.x.x.x @@ -28,7 +31,6 @@ To update multiple Kodi instances, specify them as an array:: user: kodi2 pwd: kodi2 - To use the ``kodiupdate`` plugin, first enable it in your configuration (see :ref:`using-plugins`). Then, install ``beets`` with ``kodiupdate`` extra @@ -38,23 +40,20 @@ To use the ``kodiupdate`` plugin, first enable it in your configuration (see You'll also need to enable JSON-RPC in Kodi. -In Kodi's interface, navigate to System/Settings/Network/Services and choose "Allow control of Kodi via HTTP." +In Kodi's interface, navigate to System/Settings/Network/Services and choose +"Allow control of Kodi via HTTP." With that all in place, you'll see beets send the "update" command to your Kodi host every time you change your beets library. -.. _Kodi: https://kodi.tv/ +.. _kodi: https://kodi.tv/ Configuration ------------- The available options under the ``kodi:`` section are: -- **host**: The Kodi host name. - Default: ``localhost`` -- **port**: The Kodi host port. - Default: 8080 -- **user**: The Kodi host user. - Default: ``kodi`` -- **pwd**: The Kodi host password. - Default: ``kodi`` +- **host**: The Kodi host name. Default: ``localhost`` +- **port**: The Kodi host port. Default: 8080 +- **user**: The Kodi host user. Default: ``kodi`` +- **pwd**: The Kodi host password. Default: ``kodi`` diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index a48cd3074..68d4a60a7 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -1,11 +1,10 @@ LastGenre Plugin ================ - -The ``lastgenre`` plugin fetches *tags* from `Last.fm`_ and assigns them as genres +The ``lastgenre`` plugin fetches *tags* from Last.fm_ and assigns them as genres to your albums and items. -.. _Last.fm: https://last.fm/ +.. _last.fm: https://last.fm/ Installation ------------ @@ -20,30 +19,33 @@ To use the ``lastgenre`` plugin, first enable it in your configuration (see Usage ----- -The plugin chooses genres based on a *whitelist*, meaning that only certain -tags can be considered genres. This way, tags like "my favorite music" or "seen -live" won't be considered genres. The plugin ships with a fairly extensive -`internal whitelist`_, but you can set your own in the config file using the -``whitelist`` configuration value or forgo a whitelist altogether by setting -the option to ``no``. +The plugin chooses genres based on a *whitelist*, meaning that only certain tags +can be considered genres. This way, tags like "my favorite music" or "seen live" +won't be considered genres. The plugin ships with a fairly extensive `internal +whitelist`_, but you can set your own in the config file using the ``whitelist`` +configuration value or forgo a whitelist altogether by setting the option to +``no``. The genre list file should contain one genre per line. Blank lines are ignored. For the curious, the default genre list is generated by a `script that scrapes Wikipedia`_. -.. _script that scrapes Wikipedia: https://gist.github.com/1241307 .. _internal whitelist: https://raw.githubusercontent.com/beetbox/beets/master/beetsplug/lastgenre/genres.txt +.. _script that scrapes wikipedia: https://gist.github.com/1241307 + Canonicalization -^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~ The plugin can also *canonicalize* genres, meaning that more obscure genres can be turned into coarser-grained ones that are present in the whitelist. This -works using a `tree of nested genre names`_, represented using `YAML`_, where the +works using a `tree of nested genre names`_, represented using YAML_, where the leaves of the tree represent the most specific genres. The most common way to use this would be with a custom whitelist containing only -a desired subset of genres. Consider for a example this minimal whitelist:: +a desired subset of genres. Consider for a example this minimal whitelist: + +:: rock heavy metal @@ -54,7 +56,9 @@ as *viking metal* would actually be tagged as *heavy metal* because neither *viking metal* nor its parent *black metal* are in the whitelist. It always tries to use the most specific genre that's available in the whitelist. -The relevant subtree path in the default tree looks like this:: +The relevant subtree path in the default tree looks like this: + +:: - rock: - heavy metal: @@ -66,60 +70,58 @@ contains about any genre contained in the tree) with canonicalization because nothing would ever be matched to a more generic node since all the specific subgenres are in the whitelist to begin with. - -.. _YAML: https://yaml.org/ .. _tree of nested genre names: https://raw.githubusercontent.com/beetbox/beets/master/beetsplug/lastgenre/genres-tree.yaml +.. _yaml: https://yaml.org/ Genre Source -^^^^^^^^^^^^ +~~~~~~~~~~~~ When looking up genres for albums or individual tracks, you can choose whether to use Last.fm tags on the album, the artist, or the track. For example, you -might want all the albums for a certain artist to carry the same genre. -The default is "album". When set to "track", the plugin will fetch *both* +might want all the albums for a certain artist to carry the same genre. The +default is "album". When set to "track", the plugin will fetch *both* album-level and track-level genres for your music when importing albums. - Multiple Genres -^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~ By default, the plugin chooses the most popular tag on Last.fm as a genre. If -you prefer to use a *list* of popular genre tags, you can increase the number -of the ``count`` config option. +you prefer to use a *list* of popular genre tags, you can increase the number of +the ``count`` config option. Lists of up to *count* genres will then be used instead of single genres. The genres are separated by commas by default, but you can change this with the ``separator`` config option. -`Last.fm`_ provides a popularity factor, a.k.a. *weight*, for each tag ranging -from 100 for the most popular tag down to 0 for the least popular. -The plugin uses this weight to discard unpopular tags. The default is to -ignore tags with a weight less then 10. You can change this by setting -the ``min_weight`` config option. +Last.fm_ provides a popularity factor, a.k.a. *weight*, for each tag ranging +from 100 for the most popular tag down to 0 for the least popular. The plugin +uses this weight to discard unpopular tags. The default is to ignore tags with a +weight less then 10. You can change this by setting the ``min_weight`` config +option. Specific vs. Popular Genres -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, the plugin sorts genres by popularity. However, you can use the -``prefer_specific`` option to override this behavior and instead sort genres -by specificity, as determined by your whitelist and canonicalization tree. +``prefer_specific`` option to override this behavior and instead sort genres by +specificity, as determined by your whitelist and canonicalization tree. -For instance, say you have both ``folk`` and ``americana`` in your whitelist -and canonicalization tree and ``americana`` is a leaf within ``folk``. If -Last.fm returns both of those tags, lastgenre is going to use the most -popular, which is often the most generic (in this case ``folk``). By setting -``prefer_specific`` to true, lastgenre would use ``americana`` instead. +For instance, say you have both ``folk`` and ``americana`` in your whitelist and +canonicalization tree and ``americana`` is a leaf within ``folk``. If Last.fm +returns both of those tags, lastgenre is going to use the most popular, which is +often the most generic (in this case ``folk``). By setting ``prefer_specific`` +to true, lastgenre would use ``americana`` instead. Handling pre-populated tags -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``force``, ``keep_existing`` and ``whitelist`` options control how pre-existing genres are handled. As you would assume, setting ``force: no`` **won't touch pre-existing genre tags** and will only **fetch new genres for empty tags**. When ``force`` is -``yes`` the setting of the ``whitelist`` option (as documented in `Usage`_) +``yes`` the setting of the ``whitelist`` option (as documented in Usage_) applies to any existing or newly fetched genres. The follwing configurations are possible: @@ -154,73 +156,61 @@ make sure any existing genres remain, set ``whitelist: no``). keep_existing: yes .. attention:: - If ``force`` is disabled the ``keep_existing`` option is simply ignored (since ``force: - no`` means `not touching` existing tags anyway). - + If ``force`` is disabled the ``keep_existing`` option is simply ignored + (since ``force: no`` means ``not touching`` existing tags anyway). Configuration ------------- -To configure the plugin, make a ``lastgenre:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``lastgenre:`` section in your configuration +file. The available options are: -- **auto**: Fetch genres automatically during import. - Default: ``yes``. -- **canonical**: Use a canonicalization tree. Setting this to ``yes`` will use - a built-in tree. You can also set it to a path, like the ``whitelist`` - config value, to use your own tree. - Default: ``no`` (disabled). -- **count**: Number of genres to fetch. - Default: 1 -- **fallback**: A string to use as a fallback genre when no genre is found `or` - the original genre is not desired to be kept (``keep_existing: no``). You can - use the empty string ``''`` to reset the genre. - Default: None. +- **auto**: Fetch genres automatically during import. Default: ``yes``. +- **canonical**: Use a canonicalization tree. Setting this to ``yes`` will use a + built-in tree. You can also set it to a path, like the ``whitelist`` config + value, to use your own tree. Default: ``no`` (disabled). +- **count**: Number of genres to fetch. Default: 1 +- **fallback**: A string to use as a fallback genre when no genre is found + ``or`` the original genre is not desired to be kept (``keep_existing: no``). + You can use the empty string ``''`` to reset the genre. Default: None. - **force**: By default, lastgenre will fetch new genres for empty tags only, enable this option to always try to fetch new last.fm genres. Enable the ``keep_existing`` option to combine existing and new genres. (see `Handling - pre-populated tags`_). - Default: ``no``. -- **keep_existing**: This option alters the ``force`` behavior. - If both ``force`` and ``keep_existing`` are enabled, existing genres are - combined with new ones. Depending on the ``whitelist`` setting, existing and - new genres are filtered accordingly. To ensure only fresh last.fm genres, - disable this option. (see `Handling pre-populated tags`_) - Default: ``no``. + pre-populated tags`_). Default: ``no``. +- **keep_existing**: This option alters the ``force`` behavior. If both + ``force`` and ``keep_existing`` are enabled, existing genres are combined with + new ones. Depending on the ``whitelist`` setting, existing and new genres are + filtered accordingly. To ensure only fresh last.fm genres, disable this + option. (see `Handling pre-populated tags`_) Default: ``no``. - **min_weight**: Minimum popularity factor below which genres are discarded. Default: 10. - **prefer_specific**: Sort genres by the most to least specific, rather than most to least popular. Note that this option requires a ``canonical`` tree, and if not configured it will automatically enable and use the built-in tree. Default: ``no``. -- **source**: Which entity to look up in Last.fm. Can be - either ``artist``, ``album`` or ``track``. - Default: ``album``. -- **separator**: A separator for multiple genres. - Default: ``', '``. -- **whitelist**: The filename of a custom genre list, ``yes`` to use - the internal whitelist, or ``no`` to consider all genres valid. - Default: ``yes``. -- **title_case**: Convert the new tags to TitleCase before saving. - Default: ``yes``. +- **source**: Which entity to look up in Last.fm. Can be either ``artist``, + ``album`` or ``track``. Default: ``album``. +- **separator**: A separator for multiple genres. Default: ``', '``. +- **whitelist**: The filename of a custom genre list, ``yes`` to use the + internal whitelist, or ``no`` to consider all genres valid. Default: ``yes``. +- **title_case**: Convert the new tags to TitleCase before saving. Default: + ``yes``. - **extended_debug**: Add additional debug logging messages that show what last.fm tags were fetched for tracks, albums and artists. This is done before any canonicalization and whitelist filtering is applied. It's useful for tuning the plugin's settings and understanding how it works, but it can be - quite verbose. - Default: ``no``. + quite verbose. Default: ``no``. Running Manually ---------------- -In addition to running automatically on import, the plugin can also be run manually -from the command line. Use the command ``beet lastgenre [QUERY]`` to fetch -genres for albums or items matching a certain query. +In addition to running automatically on import, the plugin can also be run +manually from the command line. Use the command ``beet lastgenre [QUERY]`` to +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]``. +By default, ``beet lastgenre`` matches albums. To match individual tracks or +singletons, use the ``-A`` switch: ``beet lastgenre -A [QUERY]``. -To disable automatic genre fetching on import, set the ``auto`` config option -to false. +To disable automatic genre fetching on import, set the ``auto`` config option to +false. diff --git a/docs/plugins/lastimport.rst b/docs/plugins/lastimport.rst index 5fc7e4b4c..61fadc506 100644 --- a/docs/plugins/lastimport.rst +++ b/docs/plugins/lastimport.rst @@ -1,12 +1,12 @@ LastImport Plugin ================= -The ``lastimport`` plugin downloads play-count data from your `Last.fm`_ -library into beets' database. You can later create :doc:`smart playlists -</plugins/smartplaylist>` by querying ``play_count`` and do other fun stuff -with this field. +The ``lastimport`` plugin downloads play-count data from your Last.fm_ library +into beets' database. You can later create :doc:`smart playlists +</plugins/smartplaylist>` by querying ``play_count`` and do other fun stuff with +this field. -.. _Last.fm: https://last.fm +.. _last.fm: https://last.fm Installation ------------ @@ -18,7 +18,9 @@ To use the ``lastimport`` plugin, first enable it in your configuration (see pip install "beets[lastimport]" -Next, add your Last.fm username to your beets configuration file:: +Next, add your Last.fm username to your beets configuration file: + +:: lastfm: user: beetsfanatic @@ -27,11 +29,13 @@ Importing Play Counts --------------------- Simply run ``beet lastimport`` and wait for the plugin to request tracks from -Last.fm and match them to your beets library. (You will be notified of tracks -in your Last.fm profile that do not match any songs in your library.) +Last.fm and match them to your beets library. (You will be notified of tracks in +your Last.fm profile that do not match any songs in your library.) -Then, your matched tracks will be populated with the ``play_count`` field, -which you can use in any query or template. For example:: +Then, your matched tracks will be populated with the ``play_count`` field, which +you can use in any query or template. For example: + +:: $ beet ls -f '$title: $play_count' play_count:5.. Eple (Melody A.M.): 60 @@ -45,14 +49,15 @@ Configuration Aside from the required ``lastfm.user`` field, this plugin has some specific options under the ``lastimport:`` section: -* **per_page**: The number of tracks to request from the API at once. - Default: 500. -* **retry_limit**: How many times should we re-send requests to Last.fm on - failure? - Default: 3. +- **per_page**: The number of tracks to request from the API at once. Default: + 500. +- **retry_limit**: How many times should we re-send requests to Last.fm on + failure? Default: 3. By default, the plugin will use beets's own Last.fm API key. You can also -override it with your own API key:: +override it with your own API key: + +:: lastfm: api_key: your_api_key diff --git a/docs/plugins/limit.rst b/docs/plugins/limit.rst index cd89a5579..64ed89ed2 100644 --- a/docs/plugins/limit.rst +++ b/docs/plugins/limit.rst @@ -1,58 +1,63 @@ Limit Query Plugin ================== -``limit`` is a plugin to limit a query to the first or last set of -results. We also provide a query prefix ``'<n'`` to inline the same -behavior in the ``list`` command. They are analogous to piping results: +``limit`` is a plugin to limit a query to the first or last set of results. We +also provide a query prefix ``'<n'`` to inline the same behavior in the ``list`` +command. They are analogous to piping results: $ beet [list|ls] [QUERY] | [head|tail] -n n There are two provided interfaces: -1. ``beet lslimit [--head n | --tail n] [QUERY]`` returns the head or -tail of a query +1. ``beet lslimit [--head n | --tail n] [QUERY]`` returns the head or tail of a +query 2. ``beet [list|ls] [QUERY] '<n'`` returns the head of a query -There are two differences in behavior: +There are two differences in behavior: 1. The query prefix does not support tail. -2. The query prefix could appear anywhere in the query but will only -have the same behavior as the ``lslimit`` command and piping to ``head`` -when it appears last. +2. The query prefix could appear anywhere in the query but will only have the +same behavior as the ``lslimit`` command and piping to ``head`` when it appears +last. -Performance for the query previx is much worse due to the current -singleton-based implementation. +Performance for the query previx is much worse due to the current +singleton-based implementation. -So why does the query prefix exist? Because it composes with any other -query-based API or plugin (see :doc:`/reference/query`). For example, -you can use the query prefix in ``smartplaylist`` -(see :doc:`/plugins/smartplaylist`) to limit the number of tracks in a smart -playlist for applications like most played and recently added. +So why does the query prefix exist? Because it composes with any other +query-based API or plugin (see :doc:`/reference/query`). For example, you can +use the query prefix in ``smartplaylist`` (see :doc:`/plugins/smartplaylist`) to +limit the number of tracks in a smart playlist for applications like most played +and recently added. Configuration ------------- -Enable the ``limit`` plugin in your configuration (see -:ref:`using-plugins`). +Enable the ``limit`` plugin in your configuration (see :ref:`using-plugins`). Examples -------- First 10 tracks +.. code-block:: sh + $ beet ls | head -n 10 $ beet lslimit --head 10 $ beet ls '<10' Last 10 tracks +.. code-block:: sh + $ beet ls | tail -n 10 $ beet lslimit --tail 10 100 mostly recently released tracks +.. code-block:: sh + $ beet lslimit --head 100 year- month- day- $ beet ls year- month- day- '<100' $ beet lslimit --tail 100 year+ month+ day+ diff --git a/docs/plugins/listenbrainz.rst b/docs/plugins/listenbrainz.rst index 037ccd685..21629ab54 100644 --- a/docs/plugins/listenbrainz.rst +++ b/docs/plugins/listenbrainz.rst @@ -3,30 +3,35 @@ ListenBrainz Plugin =================== -The ListenBrainz plugin for beets allows you to interact with the ListenBrainz service. +The ListenBrainz plugin for beets allows you to interact with the ListenBrainz +service. Installation ------------ -To enable the ListenBrainz plugin, add the following to your beets configuration file (`config.yaml`_): +To enable the ListenBrainz plugin, add the following to your beets configuration +file (config.yaml_): .. code-block:: yaml - plugins: - - listenbrainz + plugins: + - listenbrainz -You can then configure the plugin by providing your Listenbrainz token (see intructions `here`_) and username:: +You can then configure the plugin by providing your Listenbrainz token (see +intructions here_) and username: + +:: listenbrainz: token: TOKEN username: LISTENBRAINZ_USERNAME - Usage ----- -Once the plugin is enabled, you can import the listening history using the `lbimport` command in beets. +Once the plugin is enabled, you can import the listening history using the +``lbimport`` command in beets. +.. _config.yaml: ../reference/config.rst .. _here: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#get-the-user-token -.. _config.yaml: ../reference/config.rst diff --git a/docs/plugins/loadext.rst b/docs/plugins/loadext.rst index 5acd10ec7..f0012b9b7 100644 --- a/docs/plugins/loadext.rst +++ b/docs/plugins/loadext.rst @@ -1,21 +1,21 @@ Load Extension Plugin ===================== -Beets uses an SQLite database to store and query library information, which -has support for extensions to extend its functionality. The ``loadext`` plugin -lets you enable these SQLite extensions within beets. +Beets uses an SQLite database to store and query library information, which has +support for extensions to extend its functionality. The ``loadext`` plugin lets +you enable these SQLite extensions within beets. One of the primary uses of this within beets is with the `"ICU" extension`_, which adds support for case insensitive querying of non-ASCII characters. -.. _"ICU" extension: https://www.sqlite.org/src/dir?ci=7461d2e120f21493&name=ext/icu +.. _"icu" extension: https://www.sqlite.org/src/dir?ci=7461d2e120f21493&name=ext/icu Configuration ------------- -To configure the plugin, make a ``loadext`` section in your configuration -file. The section must consist of a list of paths to extensions to load, which -looks like this: +To configure the plugin, make a ``loadext`` section in your configuration file. +The section must consist of a list of paths to extensions to load, which looks +like this: .. code-block:: yaml @@ -25,21 +25,22 @@ looks like this: If a relative path is specified, it is resolved relative to the beets configuration directory. -If no file extension is specified, the default dynamic library extension for -the current platform will be used. +If no file extension is specified, the default dynamic library extension for the +current platform will be used. Building the ICU extension -------------------------- + This section is for **advanced** users only, and is not an in-depth guide on building the extension. To compile the ICU extension, you will need a few dependencies: - - gcc - - icu-devtools - - libicu - - libicu-dev - - libsqlite3-dev + - gcc + - icu-devtools + - libicu + - libicu-dev + - libsqlite3-dev Here's roughly how to download, build and install the extension (although the specifics may vary from system to system): @@ -49,5 +50,5 @@ specifics may vary from system to system): $ wget https://sqlite.org/2019/sqlite-src-3280000.zip $ unzip sqlite-src-3280000.zip $ cd sqlite-src-3280000/ext/icu - $ gcc -shared -fPIC icu.c `icu-config --ldflags` -o libicu.so + $ gcc -shared -fPIC icu.c $(icu-config --ldflags) -o libicu.so $ cp libicu.so ~/.config/beets diff --git a/docs/plugins/lyrics.rst b/docs/plugins/lyrics.rst index a20f97faf..7984fcb6c 100644 --- a/docs/plugins/lyrics.rst +++ b/docs/plugins/lyrics.rst @@ -2,13 +2,14 @@ Lyrics Plugin ============= The ``lyrics`` plugin fetches and stores song lyrics from databases on the Web. -Namely, the current version of the plugin uses `Genius.com`_, `Tekstowo.pl`_, -`LRCLIB`_ and, optionally, the Google Custom Search API. +Namely, the current version of the plugin uses Genius.com_, Tekstowo.pl_, +LRCLIB_ and, optionally, the Google Custom Search API. -.. _Genius.com: https://genius.com/ -.. _Tekstowo.pl: https://www.tekstowo.pl/ -.. _LRCLIB: https://lrclib.net/ +.. _genius.com: https://genius.com/ +.. _lrclib: https://lrclib.net/ + +.. _tekstowo.pl: https://www.tekstowo.pl/ Install ------- @@ -62,20 +63,20 @@ The available options are: ``translate_to`` are translated. Use a list of language codes to restrict them. - **to_language**: Language code to translate lyrics to. -- **dist_thresh**: The maximum distance between the artist and title - combination of the music file and lyrics candidate to consider them a match. - Lower values will make the plugin more strict, higher values will make it - more lenient. This does not apply to the ``lrclib`` backend as it matches - durations. + +- **dist_thresh**: The maximum distance between the artist and title combination + of the music file and lyrics candidate to consider them a match. Lower values + will make the plugin more strict, higher values will make it more lenient. + This does not apply to the ``lrclib`` backend as it matches durations. - **fallback**: By default, the file will be left unchanged when no lyrics are found. Use the empty string ``''`` to reset the lyrics in such a case. - **force**: By default, beets won't fetch lyrics if the files already have ones. To instead always fetch lyrics, set the ``force`` option to ``yes``. - **google_API_key**: Your Google API key (to enable the Google Custom Search backend). -- **google_engine_ID**: The custom search engine to use. - Default: The `beets custom search engine`_, which gathers an updated list of - sources known to be scrapeable. +- **google_engine_ID**: The custom search engine to use. Default: The `beets + custom search engine`_, which gathers an updated list of sources known to be + scrapeable. - **print**: Print lyrics to the console. - **sources**: List of sources to search for lyrics. An asterisk ``*`` expands to all available sources. The ``google`` source will be automatically @@ -109,61 +110,60 @@ Rendering Lyrics into Other Formats ----------------------------------- The ``-r directory, --write-rest directory`` option renders all lyrics as -`reStructuredText`_ (ReST) documents in ``directory``. That directory, in turn, -can be parsed by tools like `Sphinx`_ to generate HTML, ePUB, or PDF documents. +reStructuredText_ (ReST) documents in ``directory``. That directory, in turn, +can be parsed by tools like Sphinx_ to generate HTML, ePUB, or PDF documents. Minimal ``conf.py`` and ``index.rst`` files are created the first time the command is run. They are not overwritten on subsequent runs, so you can safely modify these files to customize the output. -Sphinx supports various `builders`_, see a few suggestions: - +Sphinx supports various builders_, see a few suggestions: .. admonition:: Build an HTML version - :: + :: - sphinx-build -b html <dir> <dir>/html + sphinx-build -b html <dir> <dir>/html .. admonition:: Build an ePUB3 formatted file, usable on ebook readers - :: + :: - sphinx-build -b epub3 <dir> <dir>/epub + sphinx-build -b epub3 <dir> <dir>/epub .. admonition:: Build a PDF file, which incidentally also builds a LaTeX file - :: + :: - sphinx-build -b latex <dir> <dir>/latex && make -C <dir>/latex all-pdf + sphinx-build -b latex <dir> <dir>/latex && make -C <dir>/latex all-pdf - -.. _Sphinx: https://www.sphinx-doc.org/ -.. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _builders: https://www.sphinx-doc.org/en/stable/builders.html -Activate Google Custom Search ------------------------------- +.. _restructuredtext: http://docutils.sourceforge.net/rst.html -You need to `register for a Google API key`_. Set the ``google_API_key`` +.. _sphinx: https://www.sphinx-doc.org/ + +Activate Google Custom Search +----------------------------- + +You need to `register for a Google API key +<https://console.developers.google.com/>`__. Set the ``google_API_key`` configuration option to your key. -Then add ``google`` to the list of sources in your configuration (or use -default list, which includes it as long as you have an API key). -If you use default ``google_engine_ID``, we recommend limiting the sources to -``google`` as the other sources are already included in the Google results. +Then add ``google`` to the list of sources in your configuration (or use default +list, which includes it as long as you have an API key). If you use default +``google_engine_ID``, we recommend limiting the sources to ``google`` as the +other sources are already included in the Google results. Optionally, you can `define a custom search engine`_. Get your search engine's -token and use it for your ``google_engine_ID`` configuration option. By -default, beets use a list of sources known to be scrapeable. +token and use it for your ``google_engine_ID`` configuration option. By default, +beets use a list of sources known to be scrapeable. -Note that the Google custom search API is limited to 100 queries per day. -After that, the lyrics plugin will fall back on other declared data sources. +Note that the Google custom search API is limited to 100 queries per day. After +that, the lyrics plugin will fall back on other declared data sources. -.. _register for a Google API key: https://console.developers.google.com/ .. _define a custom search engine: https://www.google.com/cse/all - .. _lyrics-translation: Activate On-the-Fly Translation @@ -177,20 +177,22 @@ follow these steps: 3. Add the API key to your configuration as ``translate.api_key``. 4. Configure your target language using the ``translate.to_language`` option. - For example, with the following configuration .. code-block:: yaml - lyrics: - translate: - api_key: YOUR_TRANSLATOR_API_KEY - to_language: de + lyrics: + translate: + api_key: YOUR_TRANSLATOR_API_KEY + to_language: de -You should expect lyrics like this:: +You should expect lyrics like this: - Original verse / Ursprünglicher Vers - Some other verse / Ein anderer Vers +:: -.. _create a Translator resource: https://learn.microsoft.com/en-us/azure/ai-services/translator/create-translator-resource -.. _obtain its API key: https://learn.microsoft.com/en-us/python/api/overview/azure/ai-translation-text-readme?view=azure-python&preserve-view=true#get-an-api-key + Original verse / Ursprünglicher Vers + Some other verse / Ein anderer Vers + +.. _create a translator resource: https://learn.microsoft.com/en-us/azure/ai-services/translator/create-translator-resource + +.. _obtain its api key: https://learn.microsoft.com/en-us/python/api/overview/azure/ai-translation-text-readme?view=azure-python&preserve-view=true#get-an-api-key diff --git a/docs/plugins/mbcollection.rst b/docs/plugins/mbcollection.rst index 00acd4604..87efcd6d5 100644 --- a/docs/plugins/mbcollection.rst +++ b/docs/plugins/mbcollection.rst @@ -6,10 +6,11 @@ maintain your `music collection`_ list there. .. _music collection: https://musicbrainz.org/doc/Collections -To begin, just enable the ``mbcollection`` plugin in your -configuration (see :ref:`using-plugins`). -Then, add your MusicBrainz username and password to your -:doc:`configuration file </reference/config>` under a ``musicbrainz`` section:: +To begin, just enable the ``mbcollection`` plugin in your configuration (see +:ref:`using-plugins`). Then, add your MusicBrainz username and password to your +:doc:`configuration file </reference/config>` under a ``musicbrainz`` section: + +:: musicbrainz: user: you @@ -22,21 +23,18 @@ profile first. The command has one command-line option: -* To remove albums from the collection which are no longer present in - the beets database, use the ``-r`` (``--remove``) flag. - +- To remove albums from the collection which are no longer present in the beets + database, use the ``-r`` (``--remove``) flag. Configuration ------------- -To configure the plugin, make a ``mbcollection:`` section in your -configuration file. There is one option available: +To configure the plugin, make a ``mbcollection:`` section in your configuration +file. There is one option available: -- **auto**: Automatically amend your MusicBrainz collection whenever you - import a new album. - Default: ``no``. -- **collection**: The MBID of which MusicBrainz collection to update. - Default: ``None``. -- **remove**: Remove albums from collections which are no longer - present in the beets database. - Default: ``no``. +- **auto**: Automatically amend your MusicBrainz collection whenever you import + a new album. Default: ``no``. +- **collection**: The MBID of which MusicBrainz collection to update. Default: + ``None``. +- **remove**: Remove albums from collections which are no longer present in the + beets database. Default: ``no``. diff --git a/docs/plugins/mbsubmit.rst b/docs/plugins/mbsubmit.rst index 0e86ddc69..12e9cd208 100644 --- a/docs/plugins/mbsubmit.rst +++ b/docs/plugins/mbsubmit.rst @@ -8,28 +8,32 @@ that is parseable by MusicBrainz's `track parser`_. The prompt choices are: - Print the tracks to stdout in a format suitable for MusicBrainz's `track parser`_. +- Open the program Picard_ with the unmatched folder as an input, allowing you + to start submitting the unmatched release to MusicBrainz with many input + fields already filled in, thanks to Picard reading the preexisting tags of the + files. -- Open the program `Picard`_ with the unmatched folder as an input, allowing - you to start submitting the unmatched release to MusicBrainz with many input - fields already filled in, thanks to Picard reading the preexisting tags of - the files. - -For the last option, `Picard`_ is assumed to be installed and available on the +For the last option, Picard_ is assumed to be installed and available on the machine including a ``picard`` executable. Picard developers list `download options`_. `other GNU/Linux distributions`_ may distribute Picard via their package manager as well. -.. _track parser: https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings -.. _Picard: https://picard.musicbrainz.org/ .. _download options: https://picard.musicbrainz.org/downloads/ -.. _other GNU/Linux distributions: https://repology.org/project/picard-tagger/versions + +.. _other gnu/linux distributions: https://repology.org/project/picard-tagger/versions + +.. _picard: https://picard.musicbrainz.org/ + +.. _track parser: https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings Usage ----- Enable the ``mbsubmit`` plugin in your configuration (see :ref:`using-plugins`) and select one of the options mentioned above. Here the option ``Print tracks`` -choice is demonstrated:: +choice is demonstrated: + +:: No matching release found for 3 tracks. For help, see: https://beets.readthedocs.org/en/latest/faq.html#nomatch @@ -44,7 +48,10 @@ choice is demonstrated:: [U]se as-is, as Tracks, Group albums, Skip, Enter search, enter Id, aBort, Print tracks? -You can also run ``beet mbsubmit QUERY`` to print the track information for any album:: +You can also run ``beet mbsubmit QUERY`` to print the track information for any +album: + +:: $ beet mbsubmit album:"An Obscure Album" 01. An Obscure Track - An Obscure Artist (3:37) @@ -53,8 +60,8 @@ You can also run ``beet mbsubmit QUERY`` to print the track information for any As MusicBrainz currently does not support submitting albums programmatically, the recommended workflow is to copy the output of the ``Print tracks`` choice -and paste it into the parser that can be found by clicking on the -"Track Parser" button on MusicBrainz "Tracklist" tab. +and paste it into the parser that can be found by clicking on the "Track Parser" +button on MusicBrainz "Tracklist" tab. Configuration ------------- @@ -62,22 +69,22 @@ Configuration To configure the plugin, make a ``mbsubmit:`` section in your configuration file. The following options are available: -- **format**: The format used for printing the tracks, defined using the - same template syntax as beets’ :doc:`path formats </reference/pathformat>`. +- **format**: The format used for printing the tracks, defined using the same + template syntax as beets’ :doc:`path formats </reference/pathformat>`. Default: ``$track. $title - $artist ($length)``. -- **threshold**: The minimum strength of the autotagger recommendation that - will cause the ``Print tracks`` choice to be displayed on the prompt. - Default: ``medium`` (causing the choice to be displayed for all albums that - have a recommendation of medium strength or lower). Valid values: ``none``, - ``low``, ``medium``, ``strong``. +- **threshold**: The minimum strength of the autotagger recommendation that will + cause the ``Print tracks`` choice to be displayed on the prompt. Default: + ``medium`` (causing the choice to be displayed for all albums that have a + recommendation of medium strength or lower). Valid values: ``none``, ``low``, + ``medium``, ``strong``. - **picard_path**: The path to the ``picard`` executable. Could be an absolute path, and if not, ``$PATH`` is consulted. The default value is simply ``picard``. Windows users will have to find and specify the absolute path to - their ``picard.exe``. That would probably be: - ``C:\Program Files\MusicBrainz Picard\picard.exe``. + their ``picard.exe``. That would probably be: ``C:\Program Files\MusicBrainz + Picard\picard.exe``. Please note that some values of the ``threshold`` configuration option might require other ``beets`` command line switches to be enabled in order to work as -intended. In particular, setting a threshold of ``strong`` will only display -the prompt if ``timid`` mode is enabled. You can find more information about -how the recommendation system works at :ref:`match-config`. +intended. In particular, setting a threshold of ``strong`` will only display the +prompt if ``timid`` mode is enabled. You can find more information about how the +recommendation system works at :ref:`match-config`. diff --git a/docs/plugins/mbsync.rst b/docs/plugins/mbsync.rst index 647ff4df8..d2f80d1f8 100644 --- a/docs/plugins/mbsync.rst +++ b/docs/plugins/mbsync.rst @@ -1,8 +1,8 @@ MBSync Plugin ============= -This plugin provides the ``mbsync`` command, which lets you synchronize -metadata for albums and tracks that have external data source IDs. +This plugin provides the ``mbsync`` command, which lets you synchronize metadata +for albums and tracks that have external data source IDs. This is useful for syncing your library with online data or when changing configuration options that affect tag writing. If your music library already @@ -10,7 +10,6 @@ contains correct tags, you can speed up the initial import by importing files "as-is" and then using ``mbsync`` to write tags according to your beets configuration. - Usage ----- @@ -18,20 +17,20 @@ Enable the ``mbsync`` plugin in your configuration (see :ref:`using-plugins`) and then run ``beet mbsync QUERY`` to fetch updated metadata for a part of your collection (or omit the query to run over your whole library). -This plugin treats albums and singletons (non-album tracks) separately. It -first processes all matching singletons and then proceeds on to full albums. -The same query is used to search for both kinds of entities. +This plugin treats albums and singletons (non-album tracks) separately. It first +processes all matching singletons and then proceeds on to full albums. The same +query is used to search for both kinds of entities. The command has a few command-line options: -* To preview the changes that would be made without applying them, use the +- To preview the changes that would be made without applying them, use the ``-p`` (``--pretend``) flag. -* By default, files will be moved (renamed) according to their metadata if - they are inside your beets library directory. To disable this, use the - ``-M`` (``--nomove``) command-line option. -* If you have the ``import.write`` configuration option enabled, then this - plugin will write new metadata to files' tags. To disable this, use the - ``-W`` (``--nowrite``) option. -* To customize the output of unrecognized items, use the ``-f`` - (``--format``) option. The default output is ``format_item`` or - ``format_album`` for items and albums, respectively. +- By default, files will be moved (renamed) according to their metadata if they + are inside your beets library directory. To disable this, use the ``-M`` + (``--nomove``) command-line option. +- If you have the ``import.write`` configuration option enabled, then this + plugin will write new metadata to files' tags. To disable this, use the ``-W`` + (``--nowrite``) option. +- To customize the output of unrecognized items, use the ``-f`` (``--format``) + option. The default output is ``format_item`` or ``format_album`` for items + and albums, respectively. diff --git a/docs/plugins/metasync.rst b/docs/plugins/metasync.rst index 8d9dac5a3..617281c6b 100644 --- a/docs/plugins/metasync.rst +++ b/docs/plugins/metasync.rst @@ -4,23 +4,21 @@ MetaSync Plugin This plugin provides the ``metasync`` command, which lets you fetch certain metadata from other sources: for example, your favorite audio player. -Currently, the plugin supports synchronizing with the `Amarok`_ music player, -and with `iTunes`_. -It can fetch the rating, score, first-played date, last-played date, play -count, and track uid from Amarok. +Currently, the plugin supports synchronizing with the Amarok_ music player, and +with iTunes_. It can fetch the rating, score, first-played date, last-played +date, play count, and track uid from Amarok. -.. _Amarok: https://amarok.kde.org/ -.. _iTunes: https://www.apple.com/itunes/ +.. _amarok: https://amarok.kde.org/ +.. _itunes: https://www.apple.com/itunes/ Installation ------------ -Enable the ``metasync`` plugin in your configuration (see -:ref:`using-plugins`). +Enable the ``metasync`` plugin in your configuration (see :ref:`using-plugins`). -To synchronize with Amarok, you'll need the `dbus-python`_ library. In such -case, install ``beets`` with ``metasync`` extra +To synchronize with Amarok, you'll need the dbus-python_ library. In such case, +install ``beets`` with ``metasync`` extra .. code-block:: bash @@ -28,23 +26,24 @@ case, install ``beets`` with ``metasync`` extra .. _dbus-python: https://dbus.freedesktop.org/releases/dbus-python/ - Configuration ------------- To configure the plugin, make a ``metasync:`` section in your configuration file. The available options are: -- **source**: A list of comma-separated sources to fetch metadata from. - Set this to "amarok" or "itunes" to enable synchronization with that player. - Default: empty +- **source**: A list of comma-separated sources to fetch metadata from. Set this + to "amarok" or "itunes" to enable synchronization with that player. Default: + empty The follow subsections describe additional configure required for some players. itunes -'''''' +~~~~~~ -The path to your iTunes library **xml** file has to be configured, e.g.:: +The path to your iTunes library **xml** file has to be configured, e.g.: + +:: metasync: source: itunes @@ -61,7 +60,7 @@ sources. The command has a few command-line options: -* To preview the changes that would be made without applying them, use the +- To preview the changes that would be made without applying them, use the ``-p`` (``--pretend``) flag. -* To specify temporary sources to fetch metadata from, use the ``-s`` +- To specify temporary sources to fetch metadata from, use the ``-s`` (``--source``) flag with a comma-separated list of a sources. diff --git a/docs/plugins/missing.rst b/docs/plugins/missing.rst index 9cd3fde71..7764f5fe1 100644 --- a/docs/plugins/missing.rst +++ b/docs/plugins/missing.rst @@ -8,22 +8,24 @@ call to album data source. Usage ----- -Add the ``missing`` plugin to your configuration (see :ref:`using-plugins`). -The ``beet missing`` command fetches album information from the origin data -source and lists names of the **tracks** that are missing from your library. +Add the ``missing`` plugin to your configuration (see :ref:`using-plugins`). The +``beet missing`` command fetches album information from the origin data source +and lists names of the **tracks** that are missing from your library. It can also list the names of missing **albums** for each artist, although this is limited to albums from the MusicBrainz data source only. You can customize the output format, show missing counts instead of track titles, or display the total number of missing entities across your entire -library:: +library: - -f FORMAT, --format=FORMAT - print with custom FORMAT - -c, --count count missing tracks per album - -t, --total count totals across the entire library - -a, --album show missing albums for artist instead of tracks for album +:: + + -f FORMAT, --format=FORMAT + print with custom FORMAT + -c, --count count missing tracks per album + -t, --total count totals across the entire library + -a, --album show missing albums for artist instead of tracks for album …or by editing the corresponding configuration options. @@ -34,21 +36,21 @@ library:: Configuration ------------- -To configure the plugin, make a ``missing:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``missing:`` section in your configuration file. +The available options are: - **count**: Print a count of missing tracks per album, with ``format`` - defaulting to ``$albumartist - $album: $missing``. - Default: ``no``. -- **format**: A specific format with which to print every - track. This uses the same template syntax as beets' - :doc:`path formats </reference/pathformat>`. The usage is inspired by, and - therefore similar to, the :ref:`list <list-cmd>` command. - Default: :ref:`format_item`. -- **total**: Print a single count of missing tracks in all albums. - Default: ``no``. + defaulting to ``$albumartist - $album: $missing``. Default: ``no``. +- **format**: A specific format with which to print every track. This uses the + same template syntax as beets' :doc:`path formats </reference/pathformat>`. + The usage is inspired by, and therefore similar to, the :ref:`list <list-cmd>` + command. Default: :ref:`format_item`. +- **total**: Print a single count of missing tracks in all albums. Default: + ``no``. -Here's an example :: +Here's an example + +:: missing: format: $albumartist - $album - $title @@ -58,39 +60,53 @@ Here's an example :: Template Fields --------------- -With this plugin enabled, the ``$missing`` template field expands to the -number of tracks missing from each album. +With this plugin enabled, the ``$missing`` template field expands to the number +of tracks missing from each album. Examples -------- -List all missing tracks in your collection:: +List all missing tracks in your collection: - beet missing +:: -List all missing albums in your collection:: + beet missing - beet missing -a +List all missing albums in your collection: -List all missing tracks from 2008:: +:: - beet missing year:2008 + beet missing -a -Print out a unicode histogram of the missing track years using `spark`_:: +List all missing tracks from 2008: - beet missing -f '$year' | spark - ▆▁▆█▄▇▇▄▇▇▁█▇▆▇▂▄█▁██▂█▁▁██▁█▂▇▆▂▇█▇▇█▆▆▇█▇█▇▆██▂▇ +:: -Print out a listing of all albums with missing tracks, and respective counts:: + beet missing year:2008 - beet missing -c +Print out a unicode histogram of the missing track years using spark_: -Print out a count of the total number of missing tracks:: +:: - beet missing -t + beet missing -f '$year' | spark + ▆▁▆█▄▇▇▄▇▇▁█▇▆▇▂▄█▁██▂█▁▁██▁█▂▇▆▂▇█▇▇█▆▆▇█▇█▇▆██▂▇ -Call this plugin from other beet commands:: +Print out a listing of all albums with missing tracks, and respective counts: - beet ls -a -f '$albumartist - $album: $missing' +:: + + beet missing -c + +Print out a count of the total number of missing tracks: + +:: + + beet missing -t + +Call this plugin from other beet commands: + +:: + + beet ls -a -f '$albumartist - $album: $missing' .. _spark: https://github.com/holman/spark diff --git a/docs/plugins/mpdstats.rst b/docs/plugins/mpdstats.rst index 865b615a7..276b069e3 100644 --- a/docs/plugins/mpdstats.rst +++ b/docs/plugins/mpdstats.rst @@ -1,25 +1,24 @@ MPDStats Plugin -================ +=============== ``mpdstats`` is a plugin for beets that collects statistics about your listening -habits from `MPD`_. It collects the following information about tracks: +habits from MPD_. It collects the following information about tracks: -* ``play_count``: The number of times you *fully* listened to this track. -* ``skip_count``: The number of times you *skipped* this track. -* ``last_played``: UNIX timestamp when you last played this track. -* ``rating``: A rating based on ``play_count`` and ``skip_count``. +- ``play_count``: The number of times you *fully* listened to this track. +- ``skip_count``: The number of times you *skipped* this track. +- ``last_played``: UNIX timestamp when you last played this track. +- ``rating``: A rating based on ``play_count`` and ``skip_count``. -To gather these statistics it runs as an MPD client and watches the current state -of MPD. This means that ``mpdstats`` needs to be running continuously for it to -work. +To gather these statistics it runs as an MPD client and watches the current +state of MPD. This means that ``mpdstats`` needs to be running continuously for +it to work. -.. _MPD: https://www.musicpd.org/ +.. _mpd: https://www.musicpd.org/ Installing Dependencies ----------------------- -This plugin requires the python-mpd2 library in order to talk to the MPD -server. +This plugin requires the python-mpd2 library in order to talk to the MPD server. To use the ``mpdstats`` plugin, first enable it in your configuration (see :ref:`using-plugins`). Then, install ``beets`` with ``mpdstats`` extra @@ -29,79 +28,77 @@ To use the ``mpdstats`` plugin, first enable it in your configuration (see Usage ----- -Use the ``mpdstats`` command to fire it up:: +Use the ``mpdstats`` command to fire it up: + +:: $ beet mpdstats Configuration ------------- -To configure the plugin, make an ``mpd:`` section in your -configuration file. The available options are: +To configure the plugin, make an ``mpd:`` section in your configuration file. +The available options are: -- **host**: The MPD server hostname. - Default: The ``$MPD_HOST`` environment variable if set, - falling back to ``localhost`` otherwise. -- **port**: The MPD server port. - Default: The ``$MPD_PORT`` environment variable if set, - falling back to 6600 otherwise. -- **password**: The MPD server password. - Default: None. +- **host**: The MPD server hostname. Default: The ``$MPD_HOST`` environment + variable if set, falling back to ``localhost`` otherwise. +- **port**: The MPD server port. Default: The ``$MPD_PORT`` environment variable + if set, falling back to 6600 otherwise. +- **password**: The MPD server password. Default: None. - **music_directory**: If your MPD library is at a different location from the beets library (e.g., because one is mounted on a NFS share), specify the path here. -- **strip_path**: If your MPD library contains local path, specify the part to remove - here. Combining this with **music_directory** you can mangle MPD path to match the - beets library one. - Default: The beets library directory. -- **rating**: Enable rating updates. - Default: ``yes``. -- **rating_mix**: Tune the way rating is calculated (see below). - Default: 0.75. +- **strip_path**: If your MPD library contains local path, specify the part to + remove here. Combining this with **music_directory** you can mangle MPD path + to match the beets library one. Default: The beets library directory. +- **rating**: Enable rating updates. Default: ``yes``. +- **rating_mix**: Tune the way rating is calculated (see below). Default: 0.75. - **played_ratio_threshold**: If a song was played for less than this percentage - of its duration it will be considered a skip. - Default: 0.85 + of its duration it will be considered a skip. Default: 0.85 A Word on Ratings ----------------- Ratings are calculated based on the *play_count*, *skip_count* and the last -*action* (play or skip). It consists in one part of a *stable_rating* and in -another part on a *rolling_rating*. The *stable_rating* is calculated like -this:: +*action* (play or skip). It consists in one part of a *stable_rating* and in +another part on a *rolling_rating*. The *stable_rating* is calculated like this: + +:: stable_rating = (play_count + 1.0) / (play_count + skip_count + 2.0) So if the *play_count* equals the *skip_count*, the *stable_rating* is always -0.5. More *play_counts* adjust the rating up to 1.0. More *skip_counts* -adjust it down to 0.0. One of the disadvantages of this rating system, is -that it doesn't really cover *recent developments*. e.g. a song that you -loved last year and played over 50 times will keep a high rating even if you -skipped it the last 10 times. That's were the *rolling_rating* comes in. +0.5. More *play_counts* adjust the rating up to 1.0. More *skip_counts* adjust +it down to 0.0. One of the disadvantages of this rating system, is that it +doesn't really cover *recent developments*. e.g. a song that you loved last year +and played over 50 times will keep a high rating even if you skipped it the last +10 times. That's were the *rolling_rating* comes in. -If a song has been fully played, the *rolling_rating* is calculated like -this:: +If a song has been fully played, the *rolling_rating* is calculated like this: + +:: rolling_rating = old_rating + (1.0 - old_rating) / 2.0 -If a song has been skipped, like this:: +If a song has been skipped, like this: + +:: rolling_rating = old_rating - old_rating / 2.0 -So *rolling_rating* adapts pretty fast to *recent developments*. But it's too -fast. Taking the example from above, your old favorite with 50 plays will get -a negative rating (<0.5) the first time you skip it. Also not good. +So *rolling_rating* adapts pretty fast to *recent developments*. But it's too +fast. Taking the example from above, your old favorite with 50 plays will get a +negative rating (<0.5) the first time you skip it. Also not good. To take the best of both worlds, we mix the ratings together with the -``rating_mix`` factor. A ``rating_mix`` of 0.0 means all -*rolling* and 1.0 means all *stable*. We found 0.75 to be a good compromise, -but fell free to play with that. - +``rating_mix`` factor. A ``rating_mix`` of 0.0 means all *rolling* and 1.0 means +all *stable*. We found 0.75 to be a good compromise, but fell free to play with +that. Warning ------- -This has only been tested with MPD versions >= 0.16. It may not work -on older versions. If that is the case, please report an `issue`_. +This has only been tested with MPD versions >= 0.16. It may not work on older +versions. If that is the case, please report an issue_. .. _issue: https://github.com/beetbox/beets/issues diff --git a/docs/plugins/mpdupdate.rst b/docs/plugins/mpdupdate.rst index 01a6a9fe7..9ac011ff5 100644 --- a/docs/plugins/mpdupdate.rst +++ b/docs/plugins/mpdupdate.rst @@ -2,15 +2,16 @@ MPDUpdate Plugin ================ ``mpdupdate`` is a very simple plugin for beets that lets you automatically -update `MPD`_'s index whenever you change your beets library. +update MPD_'s index whenever you change your beets library. -.. _MPD: https://www.musicpd.org/ +.. _mpd: https://www.musicpd.org/ -To use ``mpdupdate`` plugin, enable it in your configuration -(see :ref:`using-plugins`). -Then, you'll probably want to configure the specifics of your MPD server. -You can do that using an ``mpd:`` section in your ``config.yaml``, -which looks like this:: +To use ``mpdupdate`` plugin, enable it in your configuration (see +:ref:`using-plugins`). Then, you'll probably want to configure the specifics of +your MPD server. You can do that using an ``mpd:`` section in your +``config.yaml``, which looks like this: + +:: mpd: host: localhost @@ -20,9 +21,9 @@ which looks like this:: With that all in place, you'll see beets send the "update" command to your MPD server every time you change your beets library. -If you want to communicate with MPD over a Unix domain socket instead over -TCP, just give the path to the socket in the filesystem for the ``host`` -setting. (Any ``host`` value starting with a slash or a tilde is interpreted as a domain +If you want to communicate with MPD over a Unix domain socket instead over TCP, +just give the path to the socket in the filesystem for the ``host`` setting. +(Any ``host`` value starting with a slash or a tilde is interpreted as a domain socket.) Configuration @@ -30,10 +31,8 @@ Configuration The available options under the ``mpd:`` section are: -- **host**: The MPD server name. - Default: The ``$MPD_HOST`` environment variable if set, falling back to ``localhost`` otherwise. -- **port**: The MPD server port. - Default: The ``$MPD_PORT`` environment variable if set, falling back to 6600 - otherwise. -- **password**: The MPD server password. - Default: None. +- **host**: The MPD server name. Default: The ``$MPD_HOST`` environment variable + if set, falling back to ``localhost`` otherwise. +- **port**: The MPD server port. Default: The ``$MPD_PORT`` environment variable + if set, falling back to 6600 otherwise. +- **password**: The MPD server password. Default: None. diff --git a/docs/plugins/musicbrainz.rst b/docs/plugins/musicbrainz.rst index 9068ec45d..fe22335b0 100644 --- a/docs/plugins/musicbrainz.rst +++ b/docs/plugins/musicbrainz.rst @@ -2,9 +2,9 @@ MusicBrainz Plugin ================== The ``musicbrainz`` plugin extends the autotagger's search capabilities to -include matches from the `MusicBrainz`_ database. +include matches from the MusicBrainz_ database. -.. _MusicBrainz: https://musicbrainz.org/ +.. _musicbrainz: https://musicbrainz.org/ Installation ------------ @@ -18,7 +18,7 @@ Configuration ------------- Default -^^^^^^^ +~~~~~~~ .. code-block:: yaml @@ -38,10 +38,11 @@ Default beatport: no tidal: no +You can instruct beets to use `your own MusicBrainz database +<https://musicbrainz.org/doc/MusicBrainz_Server/Setup>`__ instead of the -You can instruct beets to use `your own MusicBrainz database`_ instead of -the `main server`_. Use the ``host``, ``https`` and ``ratelimit`` options -under a ``musicbrainz:`` header, like so +`main server`_. Use the ``host``, ``https`` and ``ratelimit`` options under a +``musicbrainz:`` header, like so .. code-block:: yaml @@ -51,49 +52,50 @@ under a ``musicbrainz:`` header, like so ratelimit: 100 The ``host`` key, of course, controls the Web server hostname (and port, -optionally) that will be contacted by beets (default: musicbrainz.org). -The ``https`` key makes the client use HTTPS instead of HTTP. This setting applies -only to custom servers. The official MusicBrainz server always uses HTTPS. (Default: no.) -The server must have search indices enabled (see `Building search indexes`_). +optionally) that will be contacted by beets (default: musicbrainz.org). The +``https`` key makes the client use HTTPS instead of HTTP. This setting applies +only to custom servers. The official MusicBrainz server always uses HTTPS. +(Default: no.) The server must have search indices enabled (see `Building search +indexes`_). -The ``ratelimit`` option, an integer, controls the number of Web service requests -per second (default: 1). **Do not change the rate limit setting** if you're -using the main MusicBrainz server---on this public server, you're `limited`_ -to one request per second. +The ``ratelimit`` option, an integer, controls the number of Web service +requests per second (default: 1). **Do not change the rate limit setting** if +you're using the main MusicBrainz server---on this public server, you're +limited_ to one request per second. + +.. _building search indexes: https://musicbrainz.org/doc/Development/Search_server_setup -.. _your own MusicBrainz database: https://musicbrainz.org/doc/MusicBrainz_Server/Setup -.. _main server: https://musicbrainz.org/ .. _limited: https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting -.. _Building search indexes: https://musicbrainz.org/doc/Development/Search_server_setup + +.. _main server: https://musicbrainz.org/ .. _musicbrainz.enabled: enabled -~~~~~~~ ++++++++ -.. deprecated:: 2.3 - Add `musicbrainz` to the `plugins` list instead. +.. deprecated:: 2.3 Add ``musicbrainz`` to the ``plugins`` list instead. -This option allows you to disable using MusicBrainz as a metadata source. This applies -if you use plugins that fetch data from alternative sources and should make the import -process quicker. +This option allows you to disable using MusicBrainz as a metadata source. This +applies if you use plugins that fetch data from alternative sources and should +make the import process quicker. Default: ``yes``. .. _searchlimit: searchlimit -~~~~~~~~~~~ ++++++++++++ -The number of matches returned when sending search queries to the -MusicBrainz server. +The number of matches returned when sending search queries to the MusicBrainz +server. Default: ``5``. .. _extra_tags: extra_tags -~~~~~~~~~~ +++++++++++ By default, beets will use only the artist, album, and track count to query MusicBrainz. Additional tags to be queried can be supplied with the @@ -115,23 +117,22 @@ Default: ``[]`` .. _genres: genres -~~~~~~ +++++++ Use MusicBrainz genre tags to populate (and replace if it's already set) the -``genre`` tag. This will make it a list of all the genres tagged for the -release and the release-group on MusicBrainz, separated by "; " and sorted by -the total number of votes. -Default: ``no`` +``genre`` tag. This will make it a list of all the genres tagged for the release +and the release-group on MusicBrainz, separated by "; " and sorted by the total +number of votes. Default: ``no`` .. _musicbrainz.external_ids: external_ids -~~~~~~~~~~~~ +++++++++++++ Set any of the ``external_ids`` options to ``yes`` to enable the MusicBrainz importer to look for links to related metadata sources. If such a link is -available the release ID will be extracted from the URL provided and imported -to the beets library +available the release ID will be extracted from the URL provided and imported to +the beets library .. code-block:: yaml @@ -144,10 +145,9 @@ to the beets library deezer: yes tidal: yes - -The library fields of the corresponding :ref:`autotagger_extensions` are used -to save the data (``discogs_albumid``, ``bandcamp_album_id``, -``spotify_album_id``, ``beatport_album_id``, ``deezer_album_id``, -``tidal_album_id``). On re-imports existing data will be overwritten. +The library fields of the corresponding :ref:`autotagger_extensions` are used to +save the data (``discogs_albumid``, ``bandcamp_album_id``, ``spotify_album_id``, +``beatport_album_id``, ``deezer_album_id``, ``tidal_album_id``). On re-imports +existing data will be overwritten. The default of all options is ``no``. diff --git a/docs/plugins/parentwork.rst b/docs/plugins/parentwork.rst index fb15af9f1..50c2c1ff0 100644 --- a/docs/plugins/parentwork.rst +++ b/docs/plugins/parentwork.rst @@ -1,30 +1,28 @@ ParentWork Plugin ================= -The ``parentwork`` plugin fetches the work title, parent work title and -parent work composer from MusicBrainz. +The ``parentwork`` plugin fetches the work title, parent work title and parent +work composer from MusicBrainz. -In the MusicBrainz database, a recording can be associated with a work. A -work can itself be associated with another work, for example one being part -of the other (what we call the *direct parent*). This plugin looks the work id -from the library and then looks up the direct parent, then the direct parent -of the direct parent and so on until it reaches the top. The work at the top -is what we call the *parent work*. +In the MusicBrainz database, a recording can be associated with a work. A work +can itself be associated with another work, for example one being part of the +other (what we call the *direct parent*). This plugin looks the work id from the +library and then looks up the direct parent, then the direct parent of the +direct parent and so on until it reaches the top. The work at the top is what we +call the *parent work*. -This plugin is especially designed for -classical music. For classical music, just fetching the work title as in -MusicBrainz is not satisfying, because MusicBrainz has separate works for, for -example, all the movements of a symphony. This plugin aims to solve this -problem by also fetching the parent work, which would be the whole symphony in -this example. +This plugin is especially designed for classical music. For classical music, +just fetching the work title as in MusicBrainz is not satisfying, because +MusicBrainz has separate works for, for example, all the movements of a +symphony. This plugin aims to solve this problem by also fetching the parent +work, which would be the whole symphony in this example. The plugin can detect changes in ``mb_workid`` so it knows when to re-fetch other metadata, such as ``parentwork``. To do this, when it runs, it stores a -copy of ``mb_workid`` in the bookkeeping field ``parentwork_workid_current``. -At any later run of ``beet parentwork`` it will check if the tags -``mb_workid`` and ``parentwork_workid_current`` are still identical. If it is -not the case, it means the work has changed and all the tags need to be -fetched again. +copy of ``mb_workid`` in the bookkeeping field ``parentwork_workid_current``. At +any later run of ``beet parentwork`` it will check if the tags ``mb_workid`` and +``parentwork_workid_current`` are still identical. If it is not the case, it +means the work has changed and all the tags need to be fetched again. This plugin adds seven tags: @@ -33,11 +31,11 @@ This plugin adds seven tags: - **parentwork_disambig**: The disambiguation of the parent work title. - **parent_composer**: The composer of the parent work. - **parent_composer_sort**: The sort name of the parent work composer. -- **work_date**: The composition date of the work, or the first parent work - that has a composition date. Format: yyyy-mm-dd. +- **work_date**: The composition date of the work, or the first parent work that + has a composition date. Format: yyyy-mm-dd. - **parentwork_workid_current**: The MusicBrainz id of the work as it was when the parentwork was retrieved. This tag exists only for internal bookkeeping, - to keep track of recordings whose works have changed. + to keep track of recordings whose works have changed. - **parentwork_date**: The composition date of the parent work. To use the ``parentwork`` plugin, enable it in your configuration (see @@ -46,15 +44,13 @@ To use the ``parentwork`` plugin, enable it in your configuration (see Configuration ------------- -To configure the plugin, make a ``parentwork:`` section in your -configuration file. The available options are: - -- **force**: As a default, ``parentwork`` only fetches work info for - recordings that do not already have a ``parentwork`` tag or where - ``mb_workid`` differs from ``parentwork_workid_current``. If ``force`` - is enabled, it fetches it for all recordings. - Default: ``no`` +To configure the plugin, make a ``parentwork:`` section in your configuration +file. The available options are: +- **force**: As a default, ``parentwork`` only fetches work info for recordings + that do not already have a ``parentwork`` tag or where ``mb_workid`` differs + from ``parentwork_workid_current``. If ``force`` is enabled, it fetches it for + all recordings. Default: ``no`` - **auto**: If enabled, automatically fetches works at import. It takes quite some time, because beets is restricted to one MusicBrainz query per second. Default: ``no`` diff --git a/docs/plugins/permissions.rst b/docs/plugins/permissions.rst index 9c4cdc0aa..33841d8d9 100644 --- a/docs/plugins/permissions.rst +++ b/docs/plugins/permissions.rst @@ -1,8 +1,8 @@ Permissions Plugin ================== -The ``permissions`` plugin allows you to set file permissions for imported -music files and its directories. +The ``permissions`` plugin allows you to set file permissions for imported music +files and its directories. To use the ``permissions`` plugin, enable it in your configuration (see :ref:`using-plugins`). Permissions will be adjusted automatically on import. @@ -12,9 +12,12 @@ Configuration To configure the plugin, make an ``permissions:`` section in your configuration file. The ``file`` config value therein uses **octal modes** to specify the -desired permissions. The default flags for files are octal 644 and 755 for directories. +desired permissions. The default flags for files are octal 644 and 755 for +directories. -Here's an example:: +Here's an example: + +:: permissions: file: 644 diff --git a/docs/plugins/play.rst b/docs/plugins/play.rst index d72ec4e0d..2bc825773 100644 --- a/docs/plugins/play.rst +++ b/docs/plugins/play.rst @@ -1,70 +1,72 @@ Play Plugin =========== -The ``play`` plugin allows you to pass the results of a query to a music -player in the form of an m3u playlist or paths on the command line. +The ``play`` plugin allows you to pass the results of a query to a music player +in the form of an m3u playlist or paths on the command line. Command Line Usage ------------------ To use the ``play`` plugin, enable it in your configuration (see -:ref:`using-plugins`). Then use it by invoking the ``beet play`` command with -a query. The command will create a temporary m3u file and open it using an -appropriate application. You can query albums instead of tracks using the -``-a`` option. +:ref:`using-plugins`). Then use it by invoking the ``beet play`` command with a +query. The command will create a temporary m3u file and open it using an +appropriate application. You can query albums instead of tracks using the ``-a`` +option. By default, the playlist is opened using the ``open`` command on OS X, ``xdg-open`` on other Unixes, and ``start`` on Windows. To configure the -command, you can use a ``play:`` section in your configuration file:: +command, you can use a ``play:`` section in your configuration file: + +:: play: command: /Applications/VLC.app/Contents/MacOS/VLC You can also specify additional space-separated options to command (like you -would on the command-line):: +would on the command-line): + +:: play: command: /usr/bin/command --option1 --option2 some_other_option -While playing you'll be able to interact with the player if it is a -command-line oriented, and you'll get its output in real time. +While playing you'll be able to interact with the player if it is a command-line +oriented, and you'll get its output in real time. Interactive Usage ----------------- The ``play`` plugin can also be invoked during an import. If enabled, the plugin -adds a ``plaY`` option to the prompt, so pressing ``y`` will execute the configured -command and play the items currently being imported. +adds a ``plaY`` option to the prompt, so pressing ``y`` will execute the +configured command and play the items currently being imported. -Once the configured command exits, you will be returned to the import -decision prompt. If your player is configured to run in the background (in a +Once the configured command exits, you will be returned to the import decision +prompt. If your player is configured to run in the background (in a client/server setup), the music will play until you choose to stop it, and the import operation continues immediately. Configuration ------------- -To configure the plugin, make a ``play:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``play:`` section in your configuration file. +The available options are: -- **command**: The command used to open the playlist. - Default: ``open`` on OS X, ``xdg-open`` on other Unixes and ``start`` on - Windows. Insert ``$args`` to use the ``--args`` feature. -- **relative_to**: If set, emit paths relative to this directory. - Default: None. -- **use_folders**: When using the ``-a`` option, the m3u will contain the - paths to each track on the matched albums. Enable this option to - store paths to folders instead. - Default: ``no``. +- **command**: The command used to open the playlist. Default: ``open`` on OS X, + ``xdg-open`` on other Unixes and ``start`` on Windows. Insert ``$args`` to use + the ``--args`` feature. +- **relative_to**: If set, emit paths relative to this directory. Default: None. +- **use_folders**: When using the ``-a`` option, the m3u will contain the paths + to each track on the matched albums. Enable this option to store paths to + folders instead. Default: ``no``. - **raw**: Instead of creating a temporary m3u playlist and then opening it, simply call the command with the paths returned by the query as arguments. Default: ``no``. - **warning_threshold**: Set the minimum number of files to play which will trigger a warning to be emitted. If set to ``no``, warning are never issued. Default: 100. -- **bom**: Set whether or not a UTF-8 Byte Order Mark should be emitted into - the m3u file. If you're using foobar2000 or Winamp, this is needed. - Default: ``no``. +- **bom**: Set whether or not a UTF-8 Byte Order Mark should be emitted into the + m3u file. If you're using foobar2000 or Winamp, this is needed. Default: + ``no``. Optional Arguments ------------------ @@ -73,24 +75,31 @@ The ``--args`` (or ``-A``) flag to the ``play`` command lets you specify additional arguments for your player command. Options are inserted after the configured ``command`` string and before the playlist filename. -For example, if you have the plugin configured like this:: +For example, if you have the plugin configured like this: + +:: play: command: mplayer -quiet -and you occasionally want to shuffle the songs you play, you can type:: +and you occasionally want to shuffle the songs you play, you can type: + +:: $ beet play --args -shuffle -to get beets to execute this command:: +to get beets to execute this command: + +:: mplayer -quiet -shuffle /path/to/playlist.m3u instead of the default. -If you need to insert arguments somewhere other than the end of the -``command`` string, use ``$args`` to indicate where to insert them. For -example:: +If you need to insert arguments somewhere other than the end of the ``command`` +string, use ``$args`` to indicate where to insert them. For example: + +:: play: command: mpv $args --playlist @@ -99,8 +108,8 @@ indicates that you need to insert extra arguments before specifying the playlist. The ``--yes`` (or ``-y``) flag to the ``play`` command will skip the warning -message if you choose to play more items than the **warning_threshold** -value usually allows. +message if you choose to play more items than the **warning_threshold** value +usually allows. Note on the Leakage of the Generated Playlists ---------------------------------------------- @@ -109,9 +118,9 @@ Because the command that will open the generated ``.m3u`` files can be arbitrarily configured by the user, beets won't try to delete those files. For this reason, using this plugin will leave one or several playlist(s) in the directory selected to create temporary files (Most likely ``/tmp/`` on Unix-like -systems. See `tempfile.tempdir`_ in the Python docs.). Leaking those playlists until -they are externally wiped could be an issue for privacy or storage reasons. If -this is the case for you, you might want to use the ``raw`` config option -described above. +systems. See tempfile.tempdir_ in the Python docs.). Leaking those playlists +until they are externally wiped could be an issue for privacy or storage +reasons. If this is the case for you, you might want to use the ``raw`` config +option described above. .. _tempfile.tempdir: https://docs.python.org/2/library/tempfile.html#tempfile.tempdir diff --git a/docs/plugins/playlist.rst b/docs/plugins/playlist.rst index 9737874b0..e89c880ad 100644 --- a/docs/plugins/playlist.rst +++ b/docs/plugins/playlist.rst @@ -3,9 +3,10 @@ Playlist Plugin ``playlist`` is a plugin to use playlists in m3u format. -To use it, enable the ``playlist`` plugin in your configuration -(see :ref:`using-plugins`). -Then configure your playlists like this:: +To use it, enable the ``playlist`` plugin in your configuration (see +:ref:`using-plugins`). Then configure your playlists like this: + +:: playlist: auto: no @@ -14,21 +15,26 @@ Then configure your playlists like this:: forward_slash: no It is possible to query the library based on a playlist by specifying its -absolute path:: +absolute path: + +:: $ beet ls playlist:/path/to/someplaylist.m3u The plugin also supports referencing playlists by name. The playlist is then -searched in the playlist_dir and the ".m3u" extension is appended to the -name:: +searched in the playlist_dir and the ".m3u" extension is appended to the name: + +:: $ beet ls playlist:anotherplaylist -A playlist query will use the paths found in the playlist file to match items -in the beets library. ``playlist:`` submits a regular beets -:ref:`query<queries>` similar to a :ref:`specific fields query<fieldsquery>`. -If you want the list in any particular order, you can use the standard beets -query syntax for :ref:`sorting<query-sort>`:: +A playlist query will use the paths found in the playlist file to match items in +the beets library. ``playlist:`` submits a regular beets :ref:`query<queries>` +similar to a :ref:`specific fields query<fieldsquery>`. If you want the list in +any particular order, you can use the standard beets query syntax for +:ref:`sorting<query-sort>`: + +:: $ beet ls playlist:/path/to/someplaylist.m3u artist+ year+ @@ -41,22 +47,19 @@ configuration option. Configuration ------------- -To configure the plugin, make a ``playlist:`` section in your -configuration file. In addition to the ``playlists`` described above, the -other configuration options are: +To configure the plugin, make a ``playlist:`` section in your configuration +file. In addition to the ``playlists`` described above, the other configuration +options are: - **auto**: If this is set to ``yes``, then anytime an item in the library is - moved or removed, the plugin will update all playlists in the - ``playlist_dir`` directory that contain that item to reflect the change. - Default: ``no`` -- **playlist_dir**: Where to read playlist files from. - Default: The current working directory (i.e., ``'.'``). + moved or removed, the plugin will update all playlists in the ``playlist_dir`` + directory that contain that item to reflect the change. Default: ``no`` +- **playlist_dir**: Where to read playlist files from. Default: The current + working directory (i.e., ``'.'``). - **relative_to**: Interpret paths in the playlist files relative to a base - directory. Instead of setting it to a fixed path, it is also possible to - set it to ``playlist`` to use the playlist's parent directory or to - ``library`` to use the library directory. - Default: ``library`` -- **forward_slash**: Forces forward slashes in the generated playlist files. - If you intend to use this plugin to generate playlists for MPD on - Windows, set this to yes. - Default: Use system separator. + directory. Instead of setting it to a fixed path, it is also possible to set + it to ``playlist`` to use the playlist's parent directory or to ``library`` to + use the library directory. Default: ``library`` +- **forward_slash**: Forces forward slashes in the generated playlist files. If + you intend to use this plugin to generate playlists for MPD on Windows, set + this to yes. Default: Use system separator. diff --git a/docs/plugins/plexupdate.rst b/docs/plugins/plexupdate.rst index 3ca9cbfab..a3aecae11 100644 --- a/docs/plugins/plexupdate.rst +++ b/docs/plugins/plexupdate.rst @@ -2,7 +2,7 @@ PlexUpdate Plugin ================= ``plexupdate`` is a very simple plugin for beets that lets you automatically -update `Plex`_'s music library whenever you change your beets library. +update Plex_'s music library whenever you change your beets library. Firstly, install ``beets`` with ``plexupdate`` extra @@ -10,39 +10,36 @@ Firstly, install ``beets`` with ``plexupdate`` extra pip install "beets[plexupdate]" -Then, enable ``plexupdate`` plugin it in your configuration (see :ref:`using-plugins`). -Optionally, configure the specifics of your Plex server. You can do this using -a ``plex:`` section in your ``config.yaml``: +Then, enable ``plexupdate`` plugin it in your configuration (see +:ref:`using-plugins`). Optionally, configure the specifics of your Plex server. +You can do this using a ``plex:`` section in your ``config.yaml``: .. code-block:: yaml - plex: - host: "localhost" - port: 32400 - token: "TOKEN" + plex: + host: "localhost" + port: 32400 + token: "TOKEN" -The ``token`` key is optional: you'll need to use it when in a Plex Home (see Plex's own `documentation about tokens`_). +The ``token`` key is optional: you'll need to use it when in a Plex Home (see +Plex's own `documentation about tokens`_). With that all in place, you'll see beets send the "update" command to your Plex server every time you change your beets library. -.. _Plex: https://plex.tv/ .. _documentation about tokens: https://support.plex.tv/hc/en-us/articles/204059436-Finding-your-account-token-X-Plex-Token +.. _plex: https://plex.tv/ + Configuration ------------- The available options under the ``plex:`` section are: -- **host**: The Plex server name. - Default: ``localhost``. -- **port**: The Plex server port. - Default: 32400. -- **token**: The Plex Home token. - Default: Empty. -- **library_name**: The name of the Plex library to update. - Default: ``Music`` -- **secure**: Use secure connections to the Plex server. - Default: ``False`` -- **ignore_cert_errors**: Ignore TLS certificate errors when using secure connections. - Default: ``False`` +- **host**: The Plex server name. Default: ``localhost``. +- **port**: The Plex server port. Default: 32400. +- **token**: The Plex Home token. Default: Empty. +- **library_name**: The name of the Plex library to update. Default: ``Music`` +- **secure**: Use secure connections to the Plex server. Default: ``False`` +- **ignore_cert_errors**: Ignore TLS certificate errors when using secure + connections. Default: ``False`` diff --git a/docs/plugins/random.rst b/docs/plugins/random.rst index b0c437819..ca227c4b8 100644 --- a/docs/plugins/random.rst +++ b/docs/plugins/random.rst @@ -6,7 +6,9 @@ from your library. This can be helpful if you need some help deciding what to listen to. First, enable the plugin named ``random`` (see :ref:`using-plugins`). You'll -then be able to use the ``beet random`` command:: +then be able to use the ``beet random`` command: + +:: $ beet random Aesop Rock - None Shall Pass - The Harbor Is Yours @@ -16,14 +18,14 @@ command (see :doc:`/reference/cli`). To choose an album instead of a single track, use ``-a``; to print paths to items instead of metadata, use ``-p``; and to use a custom format for printing, use ``-f FORMAT``. -If the ``-e`` option is passed, the random choice will be even among -artists (the albumartist field). This makes sure that your anthology -of Bob Dylan won't make you listen to Bob Dylan 50% of the time. +If the ``-e`` option is passed, the random choice will be even among artists +(the albumartist field). This makes sure that your anthology of Bob Dylan won't +make you listen to Bob Dylan 50% of the time. The ``-n NUMBER`` option controls the number of objects that are selected and printed (default 1). To select 5 tracks from your library, type ``beet random -n5``. As an alternative, you can use ``-t MINUTES`` to choose a set of music with a -given play time. To select tracks that total one hour, for example, type -``beet random -t60``. +given play time. To select tracks that total one hour, for example, type ``beet +random -t60``. diff --git a/docs/plugins/replace.rst b/docs/plugins/replace.rst index 8695d492c..7216f8399 100644 --- a/docs/plugins/replace.rst +++ b/docs/plugins/replace.rst @@ -1,17 +1,19 @@ Replace Plugin ============== -The ``replace`` plugin provides a command that replaces the audio file -of a track, while keeping the name and tags intact. It should save -some time when you get the wrong version of a song. +The ``replace`` plugin provides a command that replaces the audio file of a +track, while keeping the name and tags intact. It should save some time when you +get the wrong version of a song. Enable the ``replace`` plugin in your configuration (see :ref:`using-plugins`) -and then type:: +and then type: + +:: $ beet replace <query> <path> -The plugin will show you a list of files for you to pick from, and then -ask for confirmation. +The plugin will show you a list of files for you to pick from, and then ask for +confirmation. -Consider using the `replaygain` command from the -:doc:`/plugins/replaygain` plugin, if you usually use it during imports. +Consider using the ``replaygain`` command from the :doc:`/plugins/replaygain` +plugin, if you usually use it during imports. diff --git a/docs/plugins/replaygain.rst b/docs/plugins/replaygain.rst index 900f6f8c4..c7e51d25d 100644 --- a/docs/plugins/replaygain.rst +++ b/docs/plugins/replaygain.rst @@ -1,11 +1,10 @@ ReplayGain Plugin ================= -This plugin adds support for `ReplayGain`_, a technique for normalizing audio +This plugin adds support for ReplayGain_, a technique for normalizing audio playback levels. -.. _ReplayGain: https://wiki.hydrogenaudio.org/index.php?title=ReplayGain - +.. _replaygain: https://wiki.hydrogenaudio.org/index.php?title=ReplayGain Installation ------------ @@ -20,21 +19,22 @@ can be a slow process; to instead analyze after the fact, disable automatic analysis and use the ``beet replaygain`` command (see below). To speed up analysis with some of the available backends, this plugin processes -tracks or albums (when using the ``-a`` option) in parallel. By default, -a single thread is used per logical core of your CPU. +tracks or albums (when using the ``-a`` option) in parallel. By default, a +single thread is used per logical core of your CPU. GStreamer -````````` +~~~~~~~~~ -To use `GStreamer`_ for ReplayGain analysis, you will of course need to -install GStreamer and plugins for compatibility with your audio files. -You will need at least GStreamer 1.0 and `PyGObject 3.x`_ (a.k.a. ``python-gi``). +To use GStreamer_ for ReplayGain analysis, you will of course need to install +GStreamer and plugins for compatibility with your audio files. You will need at +least GStreamer 1.0 and `PyGObject 3.x`_ (a.k.a. ``python-gi``). -.. _PyGObject 3.x: https://pygobject.readthedocs.io/en/latest/ -.. _GStreamer: https://gstreamer.freedesktop.org/ +.. _gstreamer: https://gstreamer.freedesktop.org/ -Then, install ``beets`` with ``replaygain`` extra which installs -``GStreamer`` bindings for Python +.. _pygobject 3.x: https://pygobject.readthedocs.io/en/latest/ + +Then, install ``beets`` with ``replaygain`` extra which installs ``GStreamer`` +bindings for Python .. code-block:: bash @@ -42,7 +42,9 @@ Then, install ``beets`` with ``replaygain`` extra which installs Lastly, enable the ``replaygain`` plugin in your configuration (see :ref:`using-plugins`) and specify the GStreamer backend by adding this to your -configuration file:: +configuration file: + +:: replaygain: backend: gstreamer @@ -50,116 +52,118 @@ configuration file:: The GStreamer backend does not support parallel analysis. mp3gain and aacgain -``````````````````` +~~~~~~~~~~~~~~~~~~~ -In order to use this backend, you will need to install the `mp3gain`_ -command-line tool or the `aacgain`_ fork thereof. Here are some hints: +In order to use this backend, you will need to install the mp3gain_ command-line +tool or the aacgain_ fork thereof. Here are some hints: -* On Mac OS X, you can use `Homebrew`_. Type ``brew install aacgain``. -* On Linux, `mp3gain`_ is probably in your repositories. On Debian or Ubuntu, - for example, you can run ``apt-get install mp3gain``. -* On Windows, download and install the original `mp3gain`_. +- On Mac OS X, you can use Homebrew_. Type ``brew install aacgain``. +- On Linux, mp3gain_ is probably in your repositories. On Debian or Ubuntu, for + example, you can run ``apt-get install mp3gain``. +- On Windows, download and install the original mp3gain_. + +.. _aacgain: https://aacgain.altosdesign.com + +.. _homebrew: https://brew.sh .. _mp3gain: http://mp3gain.sourceforge.net/download.php -.. _aacgain: https://aacgain.altosdesign.com -.. _Homebrew: https://brew.sh Then, enable the plugin (see :ref:`using-plugins`) and specify the "command" -backend in your configuration file:: +backend in your configuration file: + +:: replaygain: backend: command If beets doesn't automatically find the ``mp3gain`` or ``aacgain`` executable, -you can configure the path explicitly like so:: +you can configure the path explicitly like so: + +:: replaygain: command: /Applications/MacMP3Gain.app/Contents/Resources/aacgain Python Audio Tools -`````````````````` +~~~~~~~~~~~~~~~~~~ -This backend uses the `Python Audio Tools`_ package to compute ReplayGain for -a range of different file formats. The package is not available via PyPI; it -must be installed manually (only versions preceding 3.x are compatible). +This backend uses the `Python Audio Tools`_ package to compute ReplayGain for a +range of different file formats. The package is not available via PyPI; it must +be installed manually (only versions preceding 3.x are compatible). -On OS X, most of the dependencies can be installed with `Homebrew`_:: +On OS X, most of the dependencies can be installed with Homebrew_: + +:: brew install mpg123 mp3gain vorbisgain faad2 libvorbis The Python Audio Tools backend does not support parallel analysis. -.. _Python Audio Tools: http://audiotools.sourceforge.net +.. _python audio tools: http://audiotools.sourceforge.net ffmpeg -`````` +~~~~~~ -This backend uses ffmpeg to calculate EBU R128 gain values. -To use it, install the `ffmpeg`_ command-line tool and select the -``ffmpeg`` backend in your config file. +This backend uses ffmpeg to calculate EBU R128 gain values. To use it, install +the ffmpeg_ command-line tool and select the ``ffmpeg`` backend in your config +file. .. _ffmpeg: https://ffmpeg.org Configuration ------------- -To configure the plugin, make a ``replaygain:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``replaygain:`` section in your configuration +file. The available options are: -- **auto**: Enable ReplayGain analysis during import. - Default: ``yes``. +- **auto**: Enable ReplayGain analysis during import. Default: ``yes``. - **threads**: The number of parallel threads to run the analysis in. Overridden - by ``--threads`` at the command line. - Default: # of logical CPU cores -- **parallel_on_import**: Whether to enable parallel analysis during import. - As of now this ReplayGain data is not written to files properly, so this option - is disabled by default. - If you wish to enable it, remember to run ``beet write`` after importing to - actually write to the imported files. - Default: ``no`` -- **backend**: The analysis backend; either ``gstreamer``, ``command``, ``audiotools`` - or ``ffmpeg``. - Default: ``command``. + by ``--threads`` at the command line. Default: # of logical CPU cores +- **parallel_on_import**: Whether to enable parallel analysis during import. As + of now this ReplayGain data is not written to files properly, so this option + is disabled by default. If you wish to enable it, remember to run ``beet + write`` after importing to actually write to the imported files. Default: + ``no`` +- **backend**: The analysis backend; either ``gstreamer``, ``command``, + ``audiotools`` or ``ffmpeg``. Default: ``command``. - **overwrite**: On import, re-analyze files that already have ReplayGain tags. Note that, for historical reasons, the name of this option is somewhat unfortunate: It does not decide whether tags are written to the files (which is controlled by the :ref:`import.write <config-import-write>` option). Default: ``no``. - **targetlevel**: A number of decibels for the target loudness level for files - using ``REPLAYGAIN_`` tags. - Default: ``89``. -- **r128_targetlevel**: The target loudness level in decibels (i.e. - ``<loudness in LUFS> + 107``) for files using ``R128_`` tags. - Default: 84 (Use ``83`` for ATSC A/85, ``84`` for EBU R128 or ``89`` for - ReplayGain 2.0.) + using ``REPLAYGAIN_`` tags. Default: ``89``. +- **r128_targetlevel**: The target loudness level in decibels (i.e. ``<loudness + in LUFS> + 107``) for files using ``R128_`` tags. Default: 84 (Use ``83`` for + ATSC A/85, ``84`` for EBU R128 or ``89`` for ReplayGain 2.0.) - **r128**: A space separated list of formats that will use ``R128_`` tags with integer values instead of the common ``REPLAYGAIN_`` tags with floating point - values. Requires the "ffmpeg" backend. - Default: ``Opus``. + values. Requires the "ffmpeg" backend. Default: ``Opus``. - **per_disc**: Calculate album ReplayGain on disc level instead of album level. Default: ``no`` These options only work with the "command" backend: - **command**: The path to the ``mp3gain`` or ``aacgain`` executable (if beets - cannot find it by itself). - For example: ``/Applications/MacMP3Gain.app/Contents/Resources/aacgain``. - Default: Search in your ``$PATH``. + cannot find it by itself). For example: + ``/Applications/MacMP3Gain.app/Contents/Resources/aacgain``. Default: Search + in your ``$PATH``. - **noclip**: Reduce the amount of ReplayGain adjustment to whatever amount - would keep clipping from occurring. - Default: ``yes``. + would keep clipping from occurring. Default: ``yes``. This option only works with the "ffmpeg" backend: -- **peak**: Either ``true`` (the default) or ``sample``. ``true`` is - more accurate but slower. +- **peak**: Either ``true`` (the default) or ``sample``. ``true`` is more + accurate but slower. Manual Analysis --------------- By default, the plugin will analyze all items an albums as they are implemented. However, you can also manually analyze files that are already in your library. -Use the ``beet replaygain`` command:: +Use the ``beet replaygain`` command: + +:: $ beet replaygain [-Waf] [QUERY] @@ -167,19 +171,23 @@ The ``-a`` flag analyzes whole albums instead of individual tracks. Provide a query (see :doc:`/reference/query`) to indicate which items or albums to analyze. Files that already have ReplayGain values are skipped unless ``-f`` is supplied. Use ``-w`` (write tags) or ``-W`` (don't write tags) to control -whether ReplayGain tags are written into the music files, or stored in the -beets database only (the default is to use :ref:`the importer's configuration +whether ReplayGain tags are written into the music files, or stored in the beets +database only (the default is to use :ref:`the importer's configuration <config-import-write>`). -To execute with a different number of threads, call ``beet replaygain --threads N``:: +To execute with a different number of threads, call ``beet replaygain --threads +N``: + +:: $ beet replaygain --threads N [-Waf] [QUERY] with N any integer. To disable parallelism, use ``--threads 0``. ReplayGain analysis is not fast, so you may want to disable it during import. -Use the ``auto`` config option to control this:: +Use the ``auto`` config option to control this: + +:: replaygain: auto: no - diff --git a/docs/plugins/rewrite.rst b/docs/plugins/rewrite.rst index 41cd454bf..87a124cc6 100644 --- a/docs/plugins/rewrite.rst +++ b/docs/plugins/rewrite.rst @@ -2,16 +2,18 @@ Rewrite Plugin ============== The ``rewrite`` plugin lets you easily substitute values in your templates and -path formats. Specifically, it is intended to let you *canonicalize* names -such as artists: for example, perhaps you want albums from The Jimi Hendrix +path formats. Specifically, it is intended to let you *canonicalize* names such +as artists: for example, perhaps you want albums from The Jimi Hendrix Experience to be sorted into the same folder as solo Hendrix albums. -To use field rewriting, first enable the ``rewrite`` plugin -(see :ref:`using-plugins`). -Then, make a ``rewrite:`` section in your config file to contain your rewrite -rules. Each rule consists of a field name, a regular expression pattern, and a -replacement value. Rules are written ``fieldname regex: replacement``. -For example, this line implements the Jimi Hendrix example above:: +To use field rewriting, first enable the ``rewrite`` plugin (see +:ref:`using-plugins`). Then, make a ``rewrite:`` section in your config file to +contain your rewrite rules. Each rule consists of a field name, a regular +expression pattern, and a replacement value. Rules are written ``fieldname +regex: replacement``. For example, this line implements the Jimi Hendrix example +above: + +:: rewrite: artist The Jimi Hendrix Experience: Jimi Hendrix @@ -21,7 +23,9 @@ would otherwise be "The Jimi Hendrix Experience". The pattern is a case-insensitive regular expression. This means you can use ordinary regular expression syntax to match multiple artists. For example, you -might use:: +might use: + +:: rewrite: artist .*jimi hendrix.*: Jimi Hendrix @@ -31,8 +35,8 @@ As a convenience, the plugin applies patterns for the ``artist`` field to the every rule for ``artist`` and ``albumartist``.) A word of warning: This plugin theoretically only applies to templates and path -formats; it initially does not modify files' metadata tags or the values -tracked by beets' library database, but since it *rewrites all field lookups*, -it modifies the file's metadata anyway. See comments in issue :bug:`2786`. +formats; it initially does not modify files' metadata tags or the values tracked +by beets' library database, but since it *rewrites all field lookups*, it +modifies the file's metadata anyway. See comments in issue :bug:`2786`. As an alternative to this plugin the :doc:`/plugins/substitute` could be used. diff --git a/docs/plugins/scrub.rst b/docs/plugins/scrub.rst index 73ee01645..77e3dc696 100644 --- a/docs/plugins/scrub.rst +++ b/docs/plugins/scrub.rst @@ -1,5 +1,5 @@ Scrub Plugin -============= +============ The ``scrub`` plugin lets you remove extraneous metadata from files' tags. If you'd prefer never to see crufty tags that come from other tools, the plugin can @@ -40,8 +40,7 @@ whatsoever. Configuration ------------- -To configure the plugin, make a ``scrub:`` section in your -configuration file. There is one option: +To configure the plugin, make a ``scrub:`` section in your configuration file. +There is one option: -- **auto**: Enable metadata stripping during import. - Default: ``yes``. +- **auto**: Enable metadata stripping during import. Default: ``yes``. diff --git a/docs/plugins/smartplaylist.rst b/docs/plugins/smartplaylist.rst index cb697f762..f227559a8 100644 --- a/docs/plugins/smartplaylist.rst +++ b/docs/plugins/smartplaylist.rst @@ -5,11 +5,13 @@ Smart Playlist Plugin beets queries every time your library changes. This plugin is specifically created to work well with `MPD's`_ playlist functionality. -.. _MPD's: https://www.musicpd.org/ +.. _mpd's: https://www.musicpd.org/ -To use it, enable the ``smartplaylist`` plugin in your configuration -(see :ref:`using-plugins`). -Then configure your smart playlists like the following example:: +To use it, enable the ``smartplaylist`` plugin in your configuration (see +:ref:`using-plugins`). Then configure your smart playlists like the following +example: + +:: smartplaylist: relative_to: ~/Music @@ -23,15 +25,17 @@ Then configure your smart playlists like the following example:: query: 'artist:Beatles' You can generate as many playlists as you want by adding them to the -``playlists`` section, using beets query syntax (see -:doc:`/reference/query`) for ``query`` and the file name to be generated for -``name``. The query will be split using shell-like syntax, so if you need to -use spaces in the query, be sure to quote them (e.g., ``artist:"The Beatles"``). -If you have existing files with the same names, you should back them up---they -will be overwritten when the plugin runs. +``playlists`` section, using beets query syntax (see :doc:`/reference/query`) +for ``query`` and the file name to be generated for ``name``. The query will be +split using shell-like syntax, so if you need to use spaces in the query, be +sure to quote them (e.g., ``artist:"The Beatles"``). If you have existing files +with the same names, you should back them up---they will be overwritten when the +plugin runs. For more advanced usage, you can use template syntax (see -:doc:`/reference/pathformat/`) in the ``name`` field. For example:: +:doc:`/reference/pathformat/`) in the ``name`` field. For example: + +:: - name: 'ReleasedIn$year.m3u' query: 'year::201(0|1)' @@ -40,13 +44,17 @@ This will query all the songs in 2010 and 2011 and generate the two playlist files ``ReleasedIn2010.m3u`` and ``ReleasedIn2011.m3u`` using those songs. You can also gather the results of several queries by putting them in a list. -(Items that match both queries are not included twice.) For example:: +(Items that match both queries are not included twice.) For example: + +:: - name: 'BeatlesUniverse.m3u' query: ['artist:beatles', 'genre:"beatles cover"'] Note that since beets query syntax is in effect, you can also use sorting -directives:: +directives: + +:: - name: 'Chronological Beatles' query: 'artist:Beatles year+' @@ -57,47 +65,55 @@ The former case behaves as expected, however please note that in the latter the sorts will be merged: ``year+ bitrate+`` will apply to both the Beatles and Led Zeppelin. If that bothers you, please get in touch. -For querying albums instead of items (mainly useful with extensible fields), -use the ``album_query`` field. ``query`` and ``album_query`` can be used at the -same time. The following example gathers single items but also items belonging -to albums that have a ``for_travel`` extensible field set to 1:: +For querying albums instead of items (mainly useful with extensible fields), use +the ``album_query`` field. ``query`` and ``album_query`` can be used at the same +time. The following example gathers single items but also items belonging to +albums that have a ``for_travel`` extensible field set to 1: + +:: - name: 'MyTravelPlaylist.m3u' album_query: 'for_travel:1' query: 'for_travel:1' -By default, each playlist is automatically regenerated at the end of the -session if an item or album it matches changed in the library database. To -force regeneration, you can invoke it manually from the command line:: +By default, each playlist is automatically regenerated at the end of the session +if an item or album it matches changed in the library database. To force +regeneration, you can invoke it manually from the command line: + +:: $ beet splupdate This will regenerate all smart playlists. You can also specify which ones you -want to regenerate:: +want to regenerate: + +:: $ beet splupdate BeatlesUniverse.m3u MyTravelPlaylist You can also use this plugin together with the :doc:`mpdupdate`, in order to -automatically notify MPD of the playlist change, by adding ``mpdupdate`` to -the ``plugins`` line in your config file *after* the ``smartplaylist`` -plugin. +automatically notify MPD of the playlist change, by adding ``mpdupdate`` to the +``plugins`` line in your config file *after* the ``smartplaylist`` plugin. While changing existing playlists in the beets configuration it can help to use the ``--pretend`` option to find out if the edits work as expected. The results of the queries will be printed out instead of being written to the playlist file. +:: + $ beet splupdate --pretend BeatlesUniverse.m3u The ``pretend_paths`` configuration option sets whether the items should be -displayed as per the user's ``format_item`` setting or what the file -paths as they would be written to the m3u file look like. +displayed as per the user's ``format_item`` setting or what the file paths as +they would be written to the m3u file look like. In case you want to export additional fields from the beets database into the generated playlists, you can do so by specifying them within the ``fields`` -configuration option and setting the ``output`` option to ``extm3u``. -For instance the following configuration exports the ``id`` and ``genre`` -fields:: +configuration option and setting the ``output`` option to ``extm3u``. For +instance the following configuration exports the ``id`` and ``genre`` fields: + +:: smartplaylist: playlist_dir: /data/playlists @@ -110,58 +126,56 @@ fields:: - name: all.m3u query: '' -Values of additional fields are URL-encoded. -A resulting ``all.m3u`` file could look as follows:: +Values of additional fields are URL-encoded. A resulting ``all.m3u`` file could +look as follows: + +:: #EXTM3U #EXTINF:805 id="1931" genre="Progressive%20Rock",Led Zeppelin - Stairway to Heaven ../music/singles/Led Zeppelin/Stairway to Heaven.mp3 -To give a usage example, the `webm3u`_ and `Beetstream`_ plugins read the -exported ``id`` field, allowing you to serve your local m3u playlists via HTTP. +To give a usage example, the webm3u_ and Beetstream_ plugins read the exported +``id`` field, allowing you to serve your local m3u playlists via HTTP. + +.. _beetstream: https://github.com/BinaryBrain/Beetstream -.. _Beetstream: https://github.com/BinaryBrain/Beetstream .. _webm3u: https://github.com/mgoltzsche/beets-webm3u Configuration ------------- -To configure the plugin, make a ``smartplaylist:`` section in your -configuration file. In addition to the ``playlists`` described above, the -other configuration options are: +To configure the plugin, make a ``smartplaylist:`` section in your configuration +file. In addition to the ``playlists`` described above, the other configuration +options are: -- **auto**: Regenerate the playlist after every database change. - Default: ``yes``. -- **playlist_dir**: Where to put the generated playlist files. - Default: The current working directory (i.e., ``'.'``). +- **auto**: Regenerate the playlist after every database change. Default: + ``yes``. +- **playlist_dir**: Where to put the generated playlist files. Default: The + current working directory (i.e., ``'.'``). - **relative_to**: Generate paths in the playlist files relative to a base directory. If you intend to use this plugin to generate playlists for MPD, - point this to your MPD music directory. - Default: Use absolute paths. -- **forward_slash**: Forces forward slashes in the generated playlist files. - If you intend to use this plugin to generate playlists for MPD on - Windows, set this to yes. - Default: Use system separator. + point this to your MPD music directory. Default: Use absolute paths. +- **forward_slash**: Forces forward slashes in the generated playlist files. If + you intend to use this plugin to generate playlists for MPD on Windows, set + this to yes. Default: Use system separator. - **prefix**: Prepend this string to every path in the playlist file. For example, you could use the URL for a server where the music is stored. Default: empty string. - **urlencode**: URL-encode all paths. Default: ``no``. -- **pretend_paths**: When running with ``--pretend``, show the actual file - paths that will be written to the m3u file. Default: ``false``. -- **uri_format**: Template with an ``$id`` placeholder used generate a - playlist item URI, e.g. ``http://beets:8337/item/$id/file``. - When this option is specified, the local path-related options ``prefix``, - ``relative_to``, ``forward_slash`` and ``urlencode`` are ignored. +- **pretend_paths**: When running with ``--pretend``, show the actual file paths + that will be written to the m3u file. Default: ``false``. +- **uri_format**: Template with an ``$id`` placeholder used generate a playlist + item URI, e.g. ``http://beets:8337/item/$id/file``. When this option is + specified, the local path-related options ``prefix``, ``relative_to``, + ``forward_slash`` and ``urlencode`` are ignored. - **output**: Specify the playlist format: m3u|extm3u. Default ``m3u``. -- **fields**: Specify the names of the additional item fields to export into - the playlist. This allows using e.g. the ``id`` field within other tools such - as the `webm3u`_ and `Beetstream`_ plugins. - To use this option, you must set the ``output`` option to ``extm3u``. - -.. _Beetstream: https://github.com/BinaryBrain/Beetstream -.. _webm3u: https://github.com/mgoltzsche/beets-webm3u +- **fields**: Specify the names of the additional item fields to export into the + playlist. This allows using e.g. the ``id`` field within other tools such as + the webm3u_ and Beetstream_ plugins. To use this option, you must set the + ``output`` option to ``extm3u``. For many configuration options, there is a corresponding CLI option, e.g. ``--playlist-dir``, ``--relative-to``, ``--prefix``, ``--forward-slash``, -``--urlencode``, ``--uri-format``, ``--output``, ``--pretend-paths``. -CLI options take precedence over those specified within the configuration file. +``--urlencode``, ``--uri-format``, ``--output``, ``--pretend-paths``. CLI +options take precedence over those specified within the configuration file. diff --git a/docs/plugins/sonosupdate.rst b/docs/plugins/sonosupdate.rst index 6076590e3..956a26a2a 100644 --- a/docs/plugins/sonosupdate.rst +++ b/docs/plugins/sonosupdate.rst @@ -1,11 +1,11 @@ SonosUpdate Plugin ================== -The ``sonosupdate`` plugin lets you automatically update `Sonos`_'s music -library whenever you change your beets library. +The ``sonosupdate`` plugin lets you automatically update Sonos_'s music library +whenever you change your beets library. -To use ``sonosupdate`` plugin, enable it in your configuration -(see :ref:`using-plugins`). +To use ``sonosupdate`` plugin, enable it in your configuration (see +:ref:`using-plugins`). To use the ``sonosupdate`` plugin, first enable it in your configuration (see :ref:`using-plugins`). Then, install ``beets`` with ``sonosupdate`` extra @@ -15,4 +15,4 @@ To use the ``sonosupdate`` plugin, first enable it in your configuration (see With that all in place, you'll see beets send the "update" command to your Sonos controller every time you change your beets library. -.. _Sonos: https://sonos.com/ +.. _sonos: https://sonos.com/ diff --git a/docs/plugins/spotify.rst b/docs/plugins/spotify.rst index c5aff8ef3..be929adf7 100644 --- a/docs/plugins/spotify.rst +++ b/docs/plugins/spotify.rst @@ -1,34 +1,43 @@ Spotify Plugin ============== -The ``spotify`` plugin generates `Spotify`_ playlists from tracks in your -library with the ``beet spotify`` command using the `Spotify Search API`_. +The ``spotify`` plugin generates Spotify_ playlists from tracks in your library +with the ``beet spotify`` command using the `Spotify Search API`_. -Also, the plugin can use the Spotify `Album`_ and `Track`_ APIs to provide -metadata matches for the importer. +Also, the plugin can use the Spotify Album_ and Track_ APIs to provide metadata +matches for the importer. -.. _Spotify: https://www.spotify.com/ -.. _Spotify Search API: https://developer.spotify.com/documentation/web-api/reference/#/operations/search -.. _Album: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-an-album -.. _Track: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-track +.. _album: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-an-album + +.. _spotify: https://www.spotify.com/ + +.. _spotify search api: https://developer.spotify.com/documentation/web-api/reference/#/operations/search + +.. _track: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-track Why Use This Plugin? -------------------- -* You're a Beets user and Spotify user already. -* You have playlists or albums you'd like to make available in Spotify from Beets without having to search for each artist/album/track. -* You want to check which tracks in your library are available on Spotify. -* You want to autotag music with metadata from the Spotify API. -* You want to obtain track popularity and audio features (e.g., danceability) +- You're a Beets user and Spotify user already. +- You have playlists or albums you'd like to make available in Spotify from + Beets without having to search for each artist/album/track. +- You want to check which tracks in your library are available on Spotify. +- You want to autotag music with metadata from the Spotify API. +- You want to obtain track popularity and audio features (e.g., danceability) Basic Usage ----------- -First, enable the ``spotify`` plugin (see :ref:`using-plugins`). -Then, use the ``spotify`` command with a beets query:: + +First, enable the ``spotify`` plugin (see :ref:`using-plugins`). Then, use the +``spotify`` command with a beets query: + +:: beet spotify [OPTIONS...] QUERY -Here's an example:: +Here's an example: + +:: $ beet spotify "In The Lonely Hour" Processing 14 tracks... @@ -38,14 +47,16 @@ Here's an example:: Command-line options include: -* ``-m MODE`` or ``--mode=MODE`` where ``MODE`` is either "list" or "open" - controls whether to print out the playlist (for copying and pasting) or - open it in the Spotify app. (See below.) -* ``--show-failures`` or ``-f``: List the tracks that did not match a Spotify +- ``-m MODE`` or ``--mode=MODE`` where ``MODE`` is either "list" or "open" + controls whether to print out the playlist (for copying and pasting) or open + it in the Spotify app. (See below.) +- ``--show-failures`` or ``-f``: List the tracks that did not match a Spotify ID. -You can enter the URL for an album or song on Spotify at the ``enter Id`` -prompt during import:: +You can enter the URL for an album or song on Spotify at the ``enter Id`` prompt +during import: + +:: Enter search, enter Id, aBort, eDit, edit Candidates, plaY? i Enter release ID: https://open.spotify.com/album/2rFYTHFBLQN3AYlrymBPPA @@ -53,45 +64,44 @@ prompt during import:: Configuration ------------- -This plugin can be configured like other metadata source plugins as described in :ref:`metadata-source-plugin-configuration`. In addition, the following +This plugin can be configured like other metadata source plugins as described in +:ref:`metadata-source-plugin-configuration`. In addition, the following configuration options are provided. -The default options should work as-is, but there are some options you can put -in config.yaml under the ``spotify:`` section: +The default options should work as-is, but there are some options you can put in +config.yaml under the ``spotify:`` section: - **mode**: One of the following: - - ``list``: Print out the playlist as a list of links. This list can then - be pasted in to a new or existing Spotify playlist. - - ``open``: This mode actually sends a link to your default browser with - instructions to open Spotify with the playlist you created. Until this - has been tested on all platforms, it will remain optional. + - ``list``: Print out the playlist as a list of links. This list can then + be pasted in to a new or existing Spotify playlist. + - ``open``: This mode actually sends a link to your default browser with + instructions to open Spotify with the playlist you created. Until this + has been tested on all platforms, it will remain optional. Default: ``list``. -- **region_filter**: A two-character country abbreviation, to limit results - to that market. - Default: None. + +- **region_filter**: A two-character country abbreviation, to limit results to + that market. Default: None. - **show_failures**: List each lookup that does not return a Spotify ID (and - therefore cannot be added to a playlist). - Default: ``no``. + therefore cannot be added to a playlist). Default: ``no``. - **tiebreak**: How to choose the track if there is more than one identical - result. For example, there might be multiple releases of the same album. - The options are ``popularity`` and ``first`` (to just choose the first match - returned). - Default: ``popularity``. + result. For example, there might be multiple releases of the same album. The + options are ``popularity`` and ``first`` (to just choose the first match + returned). Default: ``popularity``. - **regex**: An array of regex transformations to perform on the - track/album/artist fields before sending them to Spotify. Can be useful for - changing certain abbreviations, like ft. -> feat. See the examples below. + track/album/artist fields before sending them to Spotify. Can be useful for + changing certain abbreviations, like ft. -> feat. See the examples below. Default: None. -- **search_query_ascii**: If set to ``yes``, the search query will be converted to - ASCII before being sent to Spotify. Converting searches to ASCII can - enhance search results in some cases, but in general, it is not recommended. - For instance `artist:deadmau5 album:4×4` will be converted to - `artist:deadmau5 album:4x4` (notice `×!=x`). - Default: ``no``. +- **search_query_ascii**: If set to ``yes``, the search query will be converted + to ASCII before being sent to Spotify. Converting searches to ASCII can + enhance search results in some cases, but in general, it is not recommended. + For instance ``artist:deadmau5 album:4×4`` will be converted to + ``artist:deadmau5 album:4x4`` (notice ``×!=x``). Default: ``no``. +Here's an example: -Here's an example:: +:: spotify: source_weight: 0.7 @@ -117,17 +127,17 @@ Here's an example:: Obtaining Track Popularity and Audio Features from Spotify ---------------------------------------------------------- -Spotify provides information on track `popularity`_ and audio `features`_ that -can be used for music discovery. - -.. _popularity: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-track +Spotify provides information on track popularity_ and audio features_ that can +be used for music discovery. .. _features: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-audio-features +.. _popularity: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-track + The ``spotify`` plugin provides an additional command ``spotifysync`` to obtain these track attributes from Spotify: -* ``beet spotifysync [-f]``: obtain popularity and audio features information +- ``beet spotifysync [-f]``: obtain popularity and audio features information for every track in the library. By default, ``spotifysync`` will skip tracks that already have this information populated. Using the ``-f`` or ``-force`` option will download the data even for tracks that already have it. Please @@ -139,15 +149,15 @@ these track attributes from Spotify: In addition to ``popularity``, the command currently sets these audio features for all tracks with a Spotify track ID: - * ``acousticness`` - * ``danceability`` - * ``energy`` - * ``instrumentalness`` - * ``key`` - * ``liveness`` - * ``loudness`` - * ``mode`` - * ``speechiness`` - * ``tempo`` - * ``time_signature`` - * ``valence`` + - ``acousticness`` + - ``danceability`` + - ``energy`` + - ``instrumentalness`` + - ``key`` + - ``liveness`` + - ``loudness`` + - ``mode`` + - ``speechiness`` + - ``tempo`` + - ``time_signature`` + - ``valence`` diff --git a/docs/plugins/subsonicplaylist.rst b/docs/plugins/subsonicplaylist.rst index 98c83ebe1..484a9ca8a 100644 --- a/docs/plugins/subsonicplaylist.rst +++ b/docs/plugins/subsonicplaylist.rst @@ -1,36 +1,44 @@ Subsonic Playlist Plugin ======================== -The ``subsonicplaylist`` plugin allows to import playlists from a subsonic server. -This is done by retrieving the track info from the subsonic server, searching -for them in the beets library, and adding the playlist names to the -`subsonic_playlist` tag of the found items. The content of the tag has the format: +The ``subsonicplaylist`` plugin allows to import playlists from a subsonic +server. This is done by retrieving the track info from the subsonic server, +searching for them in the beets library, and adding the playlist names to the +``subsonic_playlist`` tag of the found items. The content of the tag has the +format: subsonic_playlist: ";first playlist;second playlist;" -To get all items in a playlist use the query `;playlist name;`. +To get all items in a playlist use the query ``;playlist name;``. Command Line Usage ------------------ To use the ``subsonicplaylist`` plugin, enable it in your configuration (see :ref:`using-plugins`). Then use it by invoking the ``subsonicplaylist`` command. -Next, configure the plugin to connect to your Subsonic server, like this:: +Next, configure the plugin to connect to your Subsonic server, like this: + +:: subsonicplaylist: base_url: http://subsonic.example.com username: someUser password: somePassword -After this you can import your playlists by invoking the `subsonicplaylist` command. +After this you can import your playlists by invoking the ``subsonicplaylist`` +command. -By default only the tags of the items found for playlists will be updated. -This means that, if one imported a playlist, then delete one song from it and +By default only the tags of the items found for playlists will be updated. This +means that, if one imported a playlist, then delete one song from it and imported the playlist again, the deleted song will still have the playlist set -in its `subsonic_playlist` tag. To solve this problem one can use the `-d/--delete` -flag. This resets all `subsonic_playlist` tag before importing playlists. +in its ``subsonic_playlist`` tag. To solve this problem one can use the +``-d/--delete`` flag. This resets all ``subsonic_playlist`` tag before importing +playlists. -Here's an example configuration with all the available options and their default values:: +Here's an example configuration with all the available options and their default +values: + +:: subsonicplaylist: base_url: "https://your.subsonic.server" @@ -40,4 +48,4 @@ Here's an example configuration with all the available options and their default username: '' password: '' -The `base_url`, `username`, and `password` options are required. +The ``base_url``, ``username``, and ``password`` options are required. diff --git a/docs/plugins/subsonicupdate.rst b/docs/plugins/subsonicupdate.rst index fc7e0019e..27ee925dc 100644 --- a/docs/plugins/subsonicupdate.rst +++ b/docs/plugins/subsonicupdate.rst @@ -2,15 +2,16 @@ SubsonicUpdate Plugin ===================== ``subsonicupdate`` is a very simple plugin for beets that lets you automatically -update `Subsonic`_'s index whenever you change your beets library. +update Subsonic_'s index whenever you change your beets library. -.. _Subsonic: http://www.subsonic.org/pages/index.jsp +.. _subsonic: http://www.subsonic.org/pages/index.jsp -To use ``subsonicupdate`` plugin, enable it in your configuration -(see :ref:`using-plugins`). -Then, you'll probably want to configure the specifics of your Subsonic server. -You can do that using a ``subsonic:`` section in your ``config.yaml``, -which looks like this:: +To use ``subsonicupdate`` plugin, enable it in your configuration (see +:ref:`using-plugins`). Then, you'll probably want to configure the specifics of +your Subsonic server. You can do that using a ``subsonic:`` section in your +``config.yaml``, which looks like this: + +:: subsonic: url: https://example.com:443/subsonic @@ -19,17 +20,17 @@ which looks like this:: auth: token With that all in place, this plugin will send a REST API call to your Subsonic -server every time you change your beets library. Due to a current limitation -of the API, all libraries visible to that user will be scanned. +server every time you change your beets library. Due to a current limitation of +the API, all libraries visible to that user will be scanned. -If the :doc:`/plugins/smartplaylist` is used, creating or changing any -playlist will trigger a Subsonic update as well. +If the :doc:`/plugins/smartplaylist` is used, creating or changing any playlist +will trigger a Subsonic update as well. This plugin requires Subsonic with an active Premium license (or active trial) or any other `Subsonic API compatible`_ server implementing the ``startScan`` endpoint. -.. _Subsonic API compatible: http://www.subsonic.org/pages/api.jsp +.. _subsonic api compatible: http://www.subsonic.org/pages/api.jsp Configuration ------------- @@ -41,5 +42,5 @@ The available options under the ``subsonic:`` section are: - **pass**: The Subsonic user password. (This may either be a clear-text password or hex-encoded with the prefix ``enc:``.) Default: ``admin`` - **auth**: The authentication method. Possible choices are ``token`` or - ``password``. ``token`` authentication is preferred to avoid sending - cleartext password. + ``password``. ``token`` authentication is preferred to avoid sending cleartext + password. diff --git a/docs/plugins/substitute.rst b/docs/plugins/substitute.rst index c6fec8054..292314101 100644 --- a/docs/plugins/substitute.rst +++ b/docs/plugins/substitute.rst @@ -1,28 +1,29 @@ Substitute Plugin ================= -The ``substitute`` plugin lets you easily substitute values in your templates and -path formats. Specifically, it is intended to let you *canonicalize* names +The ``substitute`` plugin lets you easily substitute values in your templates +and path formats. Specifically, it is intended to let you *canonicalize* names such as artists: For example, perhaps you want albums from The Jimi Hendrix Experience to be sorted into the same folder as solo Hendrix albums. -This plugin is intended as a replacement for the ``rewrite`` plugin. While -the ``rewrite`` plugin modifies the metadata, this plugin does not. +This plugin is intended as a replacement for the ``rewrite`` plugin. While the +``rewrite`` plugin modifies the metadata, this plugin does not. -Enable the ``substitute`` plugin (see :ref:`using-plugins`), then make a ``substitute:`` section in your config file to contain your rules. -Each rule consists of a case-insensitive regular expression pattern, and a -replacement string. For example, you might use: +Enable the ``substitute`` plugin (see :ref:`using-plugins`), then make a +``substitute:`` section in your config file to contain your rules. Each rule +consists of a case-insensitive regular expression pattern, and a replacement +string. For example, you might use: .. code-block:: yaml substitute: .*jimi hendrix.*: Jimi Hendrix -The replacement can be an expression utilising the matched regex, allowing us -to create more general rules. Say for example, we want to sort all albums by +The replacement can be an expression utilising the matched regex, allowing us to +create more general rules. Say for example, we want to sort all albums by multiple artists into the directory of the first artist. We can thus capture -everything before the first ``,``, `` &`` or `` and``, and use this capture -group in the output, discarding the rest of the string. +everything before the first ``,``, ``&`` or ``and``, and use this capture group +in the output, discarding the rest of the string. .. code-block:: yaml @@ -31,12 +32,13 @@ group in the output, discarding the rest of the string. This would handle all the below cases in a single rule: - | Bob Dylan and The Band -> Bob Dylan - | Neil Young & Crazy Horse -> Neil Young - | James Yorkston, Nina Persson & The Second Hand Orchestra -> James Yorkston + | Bob Dylan and The Band -> Bob Dylan + | Neil Young & Crazy Horse -> Neil Young + | James Yorkston, Nina Persson & The Second Hand Orchestra -> James + Yorkston - -To apply the substitution, you have to call the function ``%substitute{}`` in the paths section. For example: +To apply the substitution, you have to call the function ``%substitute{}`` in +the paths section. For example: .. code-block:: yaml diff --git a/docs/plugins/the.rst b/docs/plugins/the.rst index 5de0f5e54..b7a880b53 100644 --- a/docs/plugins/the.rst +++ b/docs/plugins/the.rst @@ -3,16 +3,20 @@ The Plugin The ``the`` plugin allows you to move patterns in path formats. It's suitable, for example, for moving articles from string start to the end. This is useful -for quick search on filesystems and generally looks good. Plugin does not -change tags. By default plugin supports English "the, a, an", but custom -regexp patterns can be added by user. How it works:: +for quick search on filesystems and generally looks good. Plugin does not change +tags. By default plugin supports English "the, a, an", but custom regexp +patterns can be added by user. How it works: + +:: The Something -> Something, The A Band -> Band, A An Orchestra -> Orchestra, An -To use the ``the`` plugin, enable it (see :doc:`/plugins/index`) and then use -a template function called ``%the`` in path format expressions:: +To use the ``the`` plugin, enable it (see :doc:`/plugins/index`) and then use a +template function called ``%the`` in path format expressions: + +:: paths: default: %the{$albumartist}/($year) $album/$track $title @@ -23,21 +27,17 @@ but you can override these defaults to make more complex changes. Configuration ------------- -To configure the plugin, make a ``the:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``the:`` section in your configuration file. The +available options are: -- **a**: Handle "A/An" moves. - Default: ``yes``. -- **the**: handle "The" moves. - Default: ``yes``. +- **a**: Handle "A/An" moves. Default: ``yes``. +- **the**: handle "The" moves. Default: ``yes``. - **patterns**: Custom regexp patterns, space-separated. Custom patterns are case-insensitive regular expressions. Patterns can be matched anywhere in the string (not just the beginning), so use ``^`` if you intend to match leading - words. - Default: ``[]``. + words. Default: ``[]``. - **strip**: Remove the article altogether instead of moving it to the end. Default: ``no``. -- **format**: A Python format string for the output. Use ``{0}`` to indicate - the part without the article and ``{1}`` for the article. - Spaces are already trimmed from ends of both parts. - Default: ``'{0}, {1}'``. +- **format**: A Python format string for the output. Use ``{0}`` to indicate the + part without the article and ``{1}`` for the article. Spaces are already + trimmed from ends of both parts. Default: ``'{0}, {1}'``. diff --git a/docs/plugins/thumbnails.rst b/docs/plugins/thumbnails.rst index c5cc3f5e5..4eeeb74be 100644 --- a/docs/plugins/thumbnails.rst +++ b/docs/plugins/thumbnails.rst @@ -1,5 +1,5 @@ Thumbnails Plugin -================== +================= The ``thumbnails`` plugin creates thumbnails for your album folders with the album cover. This works on freedesktop.org-compliant file managers such as @@ -14,10 +14,11 @@ install ``beets`` with ``thumbnails`` and ``fetchart`` extras pip install "beets[fetchart,thumbnails]" ``thumbnails`` need to resize the covers, and therefore requires either -`ImageMagick`_ or `Pillow`_. +ImageMagick_ or Pillow_. -.. _Pillow: https://github.com/python-pillow/Pillow -.. _ImageMagick: https://www.imagemagick.org/ +.. _imagemagick: https://www.imagemagick.org/ + +.. _pillow: https://github.com/python-pillow/Pillow Configuration ------------- @@ -28,12 +29,10 @@ file. The available options are - **auto**: Whether the thumbnail should be automatically set on import. Default: ``yes``. - **force**: Generate the thumbnail even when there's one that seems fine (more - recent than the cover art). - Default: ``no``. + recent than the cover art). Default: ``no``. - **dolphin**: Generate dolphin-compatible thumbnails. Dolphin (KDE file explorer) does not respect freedesktop.org's standard on thumbnails. This - functionality replaces the :doc:`/plugins/freedesktop` - Default: ``no`` + functionality replaces the :doc:`/plugins/freedesktop` Default: ``no`` Usage ----- diff --git a/docs/plugins/types.rst b/docs/plugins/types.rst index 9847fec44..713664ff5 100644 --- a/docs/plugins/types.rst +++ b/docs/plugins/types.rst @@ -2,22 +2,26 @@ Types Plugin ============ The ``types`` plugin lets you declare types for attributes you use in your -library. For example, you can declare that a ``rating`` field is numeric so -that you can query it with ranges---which isn't possible when the field is -considered a string (the default). +library. For example, you can declare that a ``rating`` field is numeric so that +you can query it with ranges---which isn't possible when the field is considered +a string (the default). -Enable the ``types`` plugin as described in :doc:`/plugins/index` and then add -a ``types`` section to your :doc:`configuration file </reference/config>`. The +Enable the ``types`` plugin as described in :doc:`/plugins/index` and then add a +``types`` section to your :doc:`configuration file </reference/config>`. The configuration section should map field name to one of ``int``, ``float``, ``bool``, or ``date``. -Here's an example:: +Here's an example: + +:: types: rating: int Now you can assign numeric ratings to tracks and albums and use :ref:`range -queries <numericquery>` to filter them.:: +queries <numericquery>` to filter them.: + +:: beet modify "My favorite track" rating=5 beet ls rating:4..5 diff --git a/docs/plugins/unimported.rst b/docs/plugins/unimported.rst index 80ee8004b..1673c9d54 100644 --- a/docs/plugins/unimported.rst +++ b/docs/plugins/unimported.rst @@ -1,15 +1,19 @@ Unimported Plugin ================= -The ``unimported`` plugin allows one to list all files in the library folder which are not listed in the beets library database, including art files. +The ``unimported`` plugin allows one to list all files in the library folder +which are not listed in the beets library database, including art files. Command Line Usage ------------------ To use the ``unimported`` plugin, enable it in your configuration (see :ref:`using-plugins`). Then use it by invoking the ``beet unimported`` command. -The command will list all files in the library folder which are not imported. You can -exclude file extensions or entire subdirectories using the configuration file:: +The command will list all files in the library folder which are not imported. +You can exclude file extensions or entire subdirectories using the configuration +file: + +:: unimported: ignore_extensions: jpg png diff --git a/docs/plugins/web.rst b/docs/plugins/web.rst index 15719e119..74e2cf03e 100644 --- a/docs/plugins/web.rst +++ b/docs/plugins/web.rst @@ -46,45 +46,43 @@ HTML5 Audio. Configuration ------------- -To configure the plugin, make a ``web:`` section in your -configuration file. The available options are: +To configure the plugin, make a ``web:`` section in your configuration file. The +available options are: - **host**: The server hostname. Set this to 0.0.0.0 to bind to all interfaces. Default: Bind to 127.0.0.1. -- **port**: The server port. - Default: 8337. -- **cors**: The CORS allowed origin (see :ref:`web-cors`, below). - Default: CORS is disabled. -- **cors_supports_credentials**: Support credentials when using CORS (see :ref:`web-cors`, below). - Default: CORS_SUPPORTS_CREDENTIALS is disabled. +- **port**: The server port. Default: 8337. +- **cors**: The CORS allowed origin (see :ref:`web-cors`, below). Default: CORS + is disabled. +- **cors_supports_credentials**: Support credentials when using CORS (see + :ref:`web-cors`, below). Default: CORS_SUPPORTS_CREDENTIALS is disabled. - **reverse_proxy**: If true, enable reverse proxy support (see - :ref:`reverse-proxy`, below). - Default: false. -- **include_paths**: If true, includes paths in item objects. - Default: false. -- **readonly**: If true, DELETE and PATCH operations are not allowed. Only GET is permitted. - Default: true. + :ref:`reverse-proxy`, below). Default: false. +- **include_paths**: If true, includes paths in item objects. Default: false. +- **readonly**: If true, DELETE and PATCH operations are not allowed. Only GET + is permitted. Default: true. Implementation -------------- -The Web backend is built using a simple REST+JSON API with the excellent -`Flask`_ library. The frontend is a single-page application written with -`Backbone.js`_. This allows future non-Web clients to use the same backend API. +The Web backend is built using a simple REST+JSON API with the excellent Flask_ +library. The frontend is a single-page application written with Backbone.js_. +This allows future non-Web clients to use the same backend API. - -.. _Backbone.js: https://backbonejs.org +.. _backbone.js: https://backbonejs.org Eventually, to make the Web player really viable, we should use a Flash fallback for unsupported formats/browsers. There are a number of options for this: -* `audio.js`_ -* `html5media`_ -* `MediaElement.js`_ +- audio.js_ +- html5media_ +- MediaElement.js_ .. _audio.js: https://kolber.github.io/audiojs/ + .. _html5media: https://html5media.info/ -.. _MediaElement.js: https://www.mediaelementjs.com/ + +.. _mediaelement.js: https://www.mediaelementjs.com/ .. _web-cors: @@ -92,45 +90,50 @@ Cross-Origin Resource Sharing (CORS) ------------------------------------ The ``web`` plugin's API can be used as a backend for an in-browser client. By -default, browsers will only allow access from clients running on the same -server as the API. (You will get an arcane error about ``XMLHttpRequest`` -otherwise.) A technology called `CORS`_ lets you relax this restriction. +default, browsers will only allow access from clients running on the same server +as the API. (You will get an arcane error about ``XMLHttpRequest`` otherwise.) A +technology called CORS_ lets you relax this restriction. If you want to use an in-browser client hosted elsewhere (or running from a -different server on your machine), set the ``cors`` configuration option to -the "origin" (protocol, host, and optional port number) where the client is -served. Or set it to ``'*'`` to enable access from all origins. Note that there -are security implications if you set the origin to ``'*'``, so please research -this before using it. +different server on your machine), set the ``cors`` configuration option to the +"origin" (protocol, host, and optional port number) where the client is served. +Or set it to ``'*'`` to enable access from all origins. Note that there are +security implications if you set the origin to ``'*'``, so please research this +before using it. -If the ``web`` server is behind a proxy that uses credentials, you might want -to set the ``cors_supports_credentials`` configuration option to true to let +If the ``web`` server is behind a proxy that uses credentials, you might want to +set the ``cors_supports_credentials`` configuration option to true to let in-browser clients log in. -For example:: +For example: + +:: web: host: 0.0.0.0 cors: 'http://example.com' -.. _CORS: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing +.. _cors: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing + .. _reverse-proxy: Reverse Proxy Support --------------------- When the server is running behind a reverse proxy, you can tell the plugin to -respect forwarded headers. Specifically, this can help when you host the -plugin at a base URL other than the root ``/`` or when you use the proxy to -handle secure connections. Enable the ``reverse_proxy`` configuration option -if you do this. +respect forwarded headers. Specifically, this can help when you host the plugin +at a base URL other than the root ``/`` or when you use the proxy to handle +secure connections. Enable the ``reverse_proxy`` configuration option if you do +this. Technically, this option lets the proxy provide ``X-Script-Name`` and ``X-Scheme`` HTTP headers to control the plugin's the ``SCRIPT_NAME`` and its ``wsgi.url_scheme`` parameter. -Here's a sample `Nginx`_ configuration that serves the web plugin under the -/beets directory:: +Here's a sample Nginx_ configuration that serves the web plugin under the /beets +directory: + +:: location /beets { proxy_pass http://127.0.0.1:8080; @@ -140,15 +143,17 @@ Here's a sample `Nginx`_ configuration that serves the web plugin under the proxy_set_header X-Script-Name /beets; } -.. _Nginx: https://www.nginx.com +.. _nginx: https://www.nginx.com JSON API -------- ``GET /item/`` -++++++++++++++ +~~~~~~~~~~~~~~ -Responds with a list of all tracks in the beets library. :: +Responds with a list of all tracks in the beets library. + +:: { "items": [ @@ -165,12 +170,13 @@ Responds with a list of all tracks in the beets library. :: ] } - ``GET /item/6`` -+++++++++++++++ +~~~~~~~~~~~~~~~ Looks for an item with id *6* in the beets library and responds with its JSON -representation. :: +representation. + +:: { "id": 6, @@ -178,24 +184,25 @@ representation. :: ... } -If there is no item with that id responds with a *404* status -code. +If there is no item with that id responds with a *404* status code. ``DELETE /item/6`` -++++++++++++++++++ +~~~~~~~~~~~~~~~~~~ -Removes the item with id *6* from the beets library. If the *?delete* query string is included, -the matching file will be deleted from disk. +Removes the item with id *6* from the beets library. If the *?delete* query +string is included, the matching file will be deleted from disk. Only allowed if ``readonly`` configuration option is set to ``no``. ``PATCH /item/6`` -++++++++++++++++++ +~~~~~~~~~~~~~~~~~ -Updates the item with id *6* and write the changes to the music file. The body should be a JSON object -containing the changes to the object. +Updates the item with id *6* and write the changes to the music file. The body +should be a JSON object containing the changes to the object. -Returns the updated JSON representation. :: +Returns the updated JSON representation. + +:: { "id": 6, @@ -206,18 +213,18 @@ Returns the updated JSON representation. :: Only allowed if ``readonly`` configuration option is set to ``no``. ``GET /item/6,12,13`` -+++++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~~~ -Response with a list of tracks with the ids *6*, *12* and *13*. The format of +Response with a list of tracks with the ids *6*, *12* and *13*. The format of the response is the same as for `GET /item/`_. It is *not guaranteed* that the -response includes all the items requested. If a track is not found it is silently -dropped from the response. +response includes all the items requested. If a track is not found it is +silently dropped from the response. -This endpoint also supports *DELETE* and *PATCH* methods as above, to operate on all -items of the list. +This endpoint also supports *DELETE* and *PATCH* methods as above, to operate on +all items of the list. ``GET /item/path/...`` -++++++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~~~~ Look for an item at the given absolute path on the server. If it corresponds to a track, return the track in the same format as ``/item/*``. @@ -225,12 +232,13 @@ a track, return the track in the same format as ``/item/*``. If the server runs UNIX, you'll need to include an extra leading slash: ``http://localhost:8337/item/path//Users/beets/Music/Foo/Bar/Baz.mp3`` - ``GET /item/query/querystring`` -+++++++++++++++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Returns a list of tracks matching the query. The *querystring* must be a -valid query as described in :doc:`/reference/query`. :: +Returns a list of tracks matching the query. The *querystring* must be a valid +query as described in :doc:`/reference/query`. + +:: { "results": [ @@ -240,57 +248,51 @@ valid query as described in :doc:`/reference/query`. :: } Path elements are joined as parts of a query. For example, -``/item/query/foo/bar`` will be converted to the query ``foo,bar``. -To specify literal path separators in a query, use a backslash instead of a -slash. +``/item/query/foo/bar`` will be converted to the query ``foo,bar``. To specify +literal path separators in a query, use a backslash instead of a slash. -This endpoint also supports *DELETE* and *PATCH* methods as above, to operate on all -items returned by the query. +This endpoint also supports *DELETE* and *PATCH* methods as above, to operate on +all items returned by the query. ``GET /item/6/file`` -++++++++++++++++++++ - -Sends the media file for the track. If the item or its corresponding file do -not exist a *404* status code is returned. +~~~~~~~~~~~~~~~~~~~~ +Sends the media file for the track. If the item or its corresponding file do not +exist a *404* status code is returned. Albums -++++++ +~~~~~~ For albums, the following endpoints are provided: -* ``GET /album/`` - -* ``GET /album/5`` - -* ``GET /album/5/art`` - -* ``DELETE /album/5`` - -* ``GET /album/5,7`` - -* ``DELETE /album/5,7`` - -* ``GET /album/query/querystring`` - -* ``DELETE /album/query/querystring`` +- ``GET /album/`` +- ``GET /album/5`` +- ``GET /album/5/art`` +- ``DELETE /album/5`` +- ``GET /album/5,7`` +- ``DELETE /album/5,7`` +- ``GET /album/query/querystring`` +- ``DELETE /album/query/querystring`` The interface and response format is similar to the item API, except replacing the encapsulation key ``"items"`` with ``"albums"`` when requesting ``/album/`` or ``/album/5,7``. In addition we can request the cover art of an album with -``GET /album/5/art``. -You can also add the '?expand' flag to get the individual items of an album. +``GET /album/5/art``. You can also add the '?expand' flag to get the individual +items of an album. -``DELETE`` is only allowed if ``readonly`` configuration option is set to ``no``. +``DELETE`` is only allowed if ``readonly`` configuration option is set to +``no``. ``GET /stats`` -++++++++++++++ +~~~~~~~~~~~~~~ -Responds with the number of tracks and albums in the database. :: +Responds with the number of tracks and albums in the database. + +:: { "items": 5, "albums": 3 } -.. _Flask: https://flask.palletsprojects.com/en/1.1.x/ +.. _flask: https://flask.palletsprojects.com/en/1.1.x/ diff --git a/docs/plugins/zero.rst b/docs/plugins/zero.rst index e3d717dfd..6ed9427d9 100644 --- a/docs/plugins/zero.rst +++ b/docs/plugins/zero.rst @@ -7,33 +7,37 @@ the plugin can strip useless comments like "ripped by MyGreatRipper." The plugin can work in one of two modes: -* ``fields``: A blacklist, where you choose the tags you want to remove (used by default). -* ``keep_fields``: A whitelist, where you instead specify the tags you want to keep. +- ``fields``: A blacklist, where you choose the tags you want to remove (used by + default). +- ``keep_fields``: A whitelist, where you instead specify the tags you want to + keep. -To use the ``zero`` plugin, enable the plugin in your configuration -(see :ref:`using-plugins`). +To use the ``zero`` plugin, enable the plugin in your configuration (see +:ref:`using-plugins`). Configuration ------------- -Make a ``zero:`` section in your configuration file. You can specify the -fields to nullify and the conditions for nullifying them: +Make a ``zero:`` section in your configuration file. You can specify the fields +to nullify and the conditions for nullifying them: -* Set ``auto`` to ``yes`` to null fields automatically on import. - Default: ``yes``. -* Set ``fields`` to a whitespace-separated list of fields to remove. You can - get the list of all available fields by running ``beet fields``. In - addition, the ``images`` field allows you to remove any images - embedded in the media file. -* Set ``keep_fields`` to *invert* the logic of the plugin. Only these fields - will be kept; other fields will be removed. Remember to set only - ``fields`` or ``keep_fields``---not both! -* To conditionally filter a field, use ``field: [regexp, regexp]`` to specify +- Set ``auto`` to ``yes`` to null fields automatically on import. Default: + ``yes``. +- Set ``fields`` to a whitespace-separated list of fields to remove. You can get + the list of all available fields by running ``beet fields``. In addition, the + ``images`` field allows you to remove any images embedded in the media file. +- Set ``keep_fields`` to *invert* the logic of the plugin. Only these fields + will be kept; other fields will be removed. Remember to set only ``fields`` or + ``keep_fields``---not both! +- To conditionally filter a field, use ``field: [regexp, regexp]`` to specify regular expressions. -* By default this plugin only affects files' tags; the beets database is left - unchanged. To update the tags in the database, set the ``update_database`` option to true. +- By default this plugin only affects files' tags; the beets database is left + unchanged. To update the tags in the database, set the ``update_database`` + option to true. -For example:: +For example: + +:: zero: fields: month day genre genres comments @@ -56,9 +60,11 @@ in your library. Preserving Album Art -------------------- -If you use the ``keep_fields`` option, the plugin will remove embedded album -art from files' tags unless you tell it not to. To keep the album art, include -the special field ``images`` in the list. For example:: +If you use the ``keep_fields`` option, the plugin will remove embedded album art +from files' tags unless you tell it not to. To keep the album art, include the +special field ``images`` in the list. For example: + +:: zero: keep_fields: title artist album year track genre genres images diff --git a/docs/reference/cli.rst b/docs/reference/cli.rst index 456059c6c..f1ef041b6 100644 --- a/docs/reference/cli.rst +++ b/docs/reference/cli.rst @@ -26,9 +26,6 @@ Command-Line Interface command; for zsh, see the accompanying `completion script`_ for the ``beet`` command. - - - Commands -------- @@ -45,7 +42,8 @@ Commands .. _import-cmd: import -`````` +~~~~~~ + :: beet import [-CWAPRqst] [-l LOGPATH] PATH... @@ -54,120 +52,104 @@ import Add music to your library, attempting to get correct tags for it from MusicBrainz. -Point the command at some music: directories, single files, or -compressed archives. The music will be copied to a configurable -directory structure and added to a library database. The command is -interactive and will try to get you to verify MusicBrainz tags that it -thinks are suspect. See the :doc:`autotagging guide </guides/tagger>` -for detail on how to use the interactive tag-correction flow. +Point the command at some music: directories, single files, or compressed +archives. The music will be copied to a configurable directory structure and +added to a library database. The command is interactive and will try to get you +to verify MusicBrainz tags that it thinks are suspect. See the :doc:`autotagging +guide </guides/tagger>` for detail on how to use the interactive tag-correction +flow. -Directories passed to the import command can contain either a single -album or many, in which case the leaf directories will be considered -albums (the latter case is true of typical Artist/Album organizations -and many people's "downloads" folders). The path can also be a single -song or an archive. Beets supports `zip` and `tar` archives out of the -box. To extract `rar` files, install the `rarfile`_ package and the -`unrar` command. To extract `7z` files, install the `py7zr`_ package. +Directories passed to the import command can contain either a single album or +many, in which case the leaf directories will be considered albums (the latter +case is true of typical Artist/Album organizations and many people's "downloads" +folders). The path can also be a single song or an archive. Beets supports +``zip`` and ``tar`` archives out of the box. To extract ``rar`` files, install +the rarfile_ package and the ``unrar`` command. To extract ``7z`` files, install +the py7zr_ package. Optional command flags: -* By default, the command copies files to your library directory and - updates the ID3 tags on your music. In order to move the files, instead of - copying, use the ``-m`` (move) option. If you'd like to leave your music - files untouched, try the ``-C`` (don't copy) and ``-W`` (don't write tags) - options. You can also disable this behavior by default in the - configuration file (below). - -* Also, you can disable the autotagging behavior entirely using ``-A`` - (don't autotag)---then your music will be imported with its existing - metadata. - -* During a long tagging import, it can be useful to keep track of albums - that weren't tagged successfully---either because they're not in the - MusicBrainz database or because something's wrong with the files. Use the - ``-l`` option to specify a filename to log every time you skip an album - or import it "as-is" or an album gets skipped as a duplicate. You can later - review the file manually or import skipped paths from the logfile - automatically by using the ``--from-logfile LOGFILE`` argument. - -* Relatedly, the ``-q`` (quiet) option can help with large imports by - autotagging without ever bothering to ask for user input. Whenever the - normal autotagger mode would ask for confirmation, the quiet mode - performs a fallback action that can be configured using the - ``quiet_fallback`` configuration or ``--quiet-fallback`` CLI option. - By default it pessimistically ``skip``s the file. +- By default, the command copies files to your library directory and updates the + ID3 tags on your music. In order to move the files, instead of copying, use + the ``-m`` (move) option. If you'd like to leave your music files untouched, + try the ``-C`` (don't copy) and ``-W`` (don't write tags) options. You can + also disable this behavior by default in the configuration file (below). +- Also, you can disable the autotagging behavior entirely using ``-A`` (don't + autotag)---then your music will be imported with its existing metadata. +- During a long tagging import, it can be useful to keep track of albums that + weren't tagged successfully---either because they're not in the MusicBrainz + database or because something's wrong with the files. Use the ``-l`` option to + specify a filename to log every time you skip an album or import it "as-is" or + an album gets skipped as a duplicate. You can later review the file manually + or import skipped paths from the logfile automatically by using the + ``--from-logfile LOGFILE`` argument. +- Relatedly, the ``-q`` (quiet) option can help with large imports by + autotagging without ever bothering to ask for user input. Whenever the normal + autotagger mode would ask for confirmation, the quiet mode performs a fallback + action that can be configured using the ``quiet_fallback`` configuration or + ``--quiet-fallback`` CLI option. By default it pessimistically skips the file. Alternatively, it can be used as is, by configuring ``asis``. - -* Speaking of resuming interrupted imports, the tagger will prompt you if it - seems like the last import of the directory was interrupted (by you or by - a crash). If you want to skip this prompt, you can say "yes" automatically - by providing ``-p`` or "no" using ``-P``. The resuming feature can be - disabled by default using a configuration option (see below). - -* If you want to import only the *new* stuff from a directory, use the - ``-i`` - option to run an *incremental* import. With this flag, beets will keep - track of every directory it ever imports and avoid importing them again. - This is useful if you have an "incoming" directory that you periodically - add things to. - To get this to work correctly, you'll need to use an incremental import *every - time* you run an import on the directory in question---including the first - time, when no subdirectories will be skipped. So consider enabling the +- Speaking of resuming interrupted imports, the tagger will prompt you if it + seems like the last import of the directory was interrupted (by you or by a + crash). If you want to skip this prompt, you can say "yes" automatically by + providing ``-p`` or "no" using ``-P``. The resuming feature can be disabled by + default using a configuration option (see below). +- If you want to import only the *new* stuff from a directory, use the ``-i`` + option to run an *incremental* import. With this flag, beets will keep track + of every directory it ever imports and avoid importing them again. This is + useful if you have an "incoming" directory that you periodically add things + to. To get this to work correctly, you'll need to use an incremental import + *every time* you run an import on the directory in question---including the + first time, when no subdirectories will be skipped. So consider enabling the ``incremental`` configuration option. - -* If you don't want to record skipped files during an *incremental* import, use - the ``--incremental-skip-later`` flag which corresponds to the - ``incremental_skip_later`` configuration option. - Setting the flag prevents beets from persisting skip decisions during a - non-interactive import so that a user can make a decision regarding - previously skipped files during a subsequent interactive import run. - To record skipped files during incremental import explicitly, use the - ``--noincremental-skip-later`` option. - -* When beets applies metadata to your music, it will retain the value of any +- If you don't want to record skipped files during an *incremental* import, use + the ``--incremental-skip-later`` flag which corresponds to the + ``incremental_skip_later`` configuration option. Setting the flag prevents + beets from persisting skip decisions during a non-interactive import so that a + user can make a decision regarding previously skipped files during a + subsequent interactive import run. To record skipped files during incremental + import explicitly, use the ``--noincremental-skip-later`` option. +- When beets applies metadata to your music, it will retain the value of any existing tags that weren't overwritten, and import them into the database. You may prefer to only use existing metadata for finding matches, and to erase it completely when new metadata is applied. You can enforce this behavior with the ``--from-scratch`` option, or the ``from_scratch`` configuration option. - -* By default, beets will proceed without asking if it finds a very close - metadata match. To disable this and have the importer ask you every time, - use the ``-t`` (for *timid*) option. - -* The importer typically works in a whole-album-at-a-time mode. If you - instead want to import individual, non-album tracks, use the *singleton* - mode by supplying the ``-s`` option. - -* If you have an album that's split across several directories under a common - top directory, use the ``--flat`` option. This takes all the music files - under the directory (recursively) and treats them as a single large album - instead of as one album per directory. This can help with your more stubborn - multi-disc albums. - -* Similarly, if you have one directory that contains multiple albums, use the +- By default, beets will proceed without asking if it finds a very close + metadata match. To disable this and have the importer ask you every time, use + the ``-t`` (for *timid*) option. +- The importer typically works in a whole-album-at-a-time mode. If you instead + want to import individual, non-album tracks, use the *singleton* mode by + supplying the ``-s`` option. +- If you have an album that's split across several directories under a common + top directory, use the ``--flat`` option. This takes all the music files under + the directory (recursively) and treats them as a single large album instead of + as one album per directory. This can help with your more stubborn multi-disc + albums. +- Similarly, if you have one directory that contains multiple albums, use the ``--group-albums`` option to split the files based on their metadata before matching them as separate albums. - -* If you want to preview which files would be imported, use the ``--pretend`` - option. If set, beets will just print a list of files that it would - otherwise import. - -* If you already have a metadata backend ID that matches the items to be +- If you want to preview which files would be imported, use the ``--pretend`` + option. If set, beets will just print a list of files that it would otherwise + import. +- If you already have a metadata backend ID that matches the items to be imported, you can instruct beets to restrict the search to that ID instead of searching for other candidates by using the ``--search-id SEARCH_ID`` option. Multiple IDs can be specified by simply repeating the option several times. +- You can supply ``--set field=value`` to assign ``field`` to ``value`` on + import. Values support the same template syntax as beets' :doc:`path formats + <pathformat>`. -* You can supply ``--set field=value`` to assign `field` to `value` on import. - Values support the same template syntax as beets' - :doc:`path formats <pathformat>`. These assignments will merge with (and possibly override) the :ref:`set_fields` configuration dictionary. You can use the option multiple - times on the command line, like so:: + times on the command line, like so: - beet import --set genre="Alternative Rock" --set mood="emotional" + :: + + beet import --set genre="Alternative Rock" --set mood="emotional" + +.. _py7zr: https://pypi.org/project/py7zr/ .. _rarfile: https://pypi.python.org/pypi/rarfile/ -.. _py7zr: https://pypi.org/project/py7zr/ .. only:: html @@ -206,7 +188,8 @@ Optional command flags: .. _list-cmd: list -```` +~~~~ + :: beet list [-apf] QUERY @@ -214,9 +197,9 @@ list :doc:`Queries <query>` the database for music. Want to search for "Gronlandic Edit" by of Montreal? Try ``beet list -gronlandic``. Maybe you want to see everything released in 2009 with -"vegetables" in the title? Try ``beet list year:2009 title:vegetables``. You -can also specify the sort order. (Read more in :doc:`query`.) +gronlandic``. Maybe you want to see everything released in 2009 with +"vegetables" in the title? Try ``beet list year:2009 title:vegetables``. You can +also specify the sort order. (Read more in :doc:`query`.) You can use the ``-a`` switch to search for albums instead of individual items. In this case, the queries you use are restricted to album-level fields: for @@ -225,20 +208,19 @@ like ``title:foo`` will be ignored. Remember that ``artist`` is an item-level field; ``albumartist`` is the corresponding album field. The ``-p`` option makes beets print out filenames of matched items, which might -be useful for piping into other Unix commands (such as `xargs`_). Similarly, the -``-f`` option lets you specify a specific format with which to print every album -or track. This uses the same template syntax as beets' :doc:`path formats -<pathformat>`. For example, the command ``beet ls -af '$album: $albumtotal' -beatles`` prints out the number of tracks on each Beatles album. In Unix shells, -remember to enclose the template argument in single quotes to avoid environment -variable expansion. - -.. _xargs: https://en.wikipedia.org/wiki/Xargs +be useful for piping into other Unix commands (such as `xargs +<https://en.wikipedia.org/wiki/Xargs>`__). Similarly, the ``-f`` option lets you +specify a specific format with which to print every album or track. This uses +the same template syntax as beets' :doc:`path formats <pathformat>`. For +example, the command ``beet ls -af '$album: $albumtotal' beatles`` prints out +the number of tracks on each Beatles album. In Unix shells, remember to enclose +the template argument in single quotes to avoid environment variable expansion. .. _remove-cmd: remove -`````` +~~~~~~ + :: beet remove [-adf] QUERY @@ -246,26 +228,28 @@ remove Remove music from your library. This command uses the same :doc:`query <query>` syntax as the ``list`` command. -By default, it just removes entries from the library database; it doesn't -touch the files on disk. To actually delete the files, use the ``-d`` flag. -When the ``-a`` flag is given, the command operates on albums instead of -individual tracks. +By default, it just removes entries from the library database; it doesn't touch +the files on disk. To actually delete the files, use the ``-d`` flag. When the +``-a`` flag is given, the command operates on albums instead of individual +tracks. + +When you run the ``remove`` command, it prints a list of all affected items in +the library and asks for your permission before removing them. You can then +choose to abort (type ``n``), confirm (``y``), or interactively choose some of +the items (``s``). In the latter case, the command will prompt you for every +matching item or album and invite you to type ``y`` to remove the item/album, +``n`` to keep it or ``q`` to exit and only remove the items/albums selected up +to this point. -When you run the ``remove`` command, it prints a list of all -affected items in the library and asks for your permission before removing -them. You can then choose to abort (type `n`), confirm (`y`), or interactively -choose some of the items (`s`). In the latter case, the command will prompt you -for every matching item or album and invite you to type `y` to remove the -item/album, `n` to keep it or `q` to exit and only remove the items/albums -selected up to this point. This option lets you choose precisely which tracks/albums to remove without -spending too much time to carefully craft a query. -If you do not want to be prompted at all, use the ``-f`` option. +spending too much time to carefully craft a query. If you do not want to be +prompted at all, use the ``-f`` option. .. _modify-cmd: modify -`````` +~~~~~~ + :: beet modify [-IMWay] [-f FORMAT] QUERY [FIELD=VALUE...] [FIELD!...] @@ -278,44 +262,42 @@ artist="Tom Tom Club"`` will change the artist for the track "Genius of Love." To remove fields (which is only possible for flexible attributes), follow a field name with an exclamation point: ``field!``. -Values can also be *templates*, using the same syntax as -:doc:`path formats <pathformat>`. -For example, ``beet modify artist='$artist_sort'`` will copy the artist sort -name into the artist field for all your tracks, -and ``beet modify title='$track $title'`` will add track numbers to their -title metadata. +Values can also be *templates*, using the same syntax as :doc:`path formats +<pathformat>`. For example, ``beet modify artist='$artist_sort'`` will copy the +artist sort name into the artist field for all your tracks, and ``beet modify +title='$track $title'`` will add track numbers to their title metadata. The ``-a`` option changes to querying album fields instead of track fields and -also enables to operate on albums in addition to the individual tracks. -Without this flag, the command will only change *track-level* data, even if all -the tracks belong to the same album. If you want to change an *album-level* -field, such as ``year`` or ``albumartist``, you'll want to use the ``-a`` flag -to avoid a confusing situation where the data for individual tracks conflicts -with the data for the whole album. +also enables to operate on albums in addition to the individual tracks. Without +this flag, the command will only change *track-level* data, even if all the +tracks belong to the same album. If you want to change an *album-level* field, +such as ``year`` or ``albumartist``, you'll want to use the ``-a`` flag to avoid +a confusing situation where the data for individual tracks conflicts with the +data for the whole album. Modifications issued using ``-a`` by default cascade to individual tracks. To prevent this behavior, use ``-I``/``--noinherit``. Items will automatically be moved around when necessary if they're in your -library directory, but you can disable that with ``-M``. Tags will be written -to the files according to the settings you have for imports, but these can be -overridden with ``-w`` (write tags, the default) and ``-W`` (don't write -tags). +library directory, but you can disable that with ``-M``. Tags will be written to +the files according to the settings you have for imports, but these can be +overridden with ``-w`` (write tags, the default) and ``-W`` (don't write tags). -When you run the ``modify`` command, it prints a list of all -affected items in the library and asks for your permission before making any -changes. You can then choose to abort the change (type `n`), confirm -(`y`), or interactively choose some of the items (`s`). In the latter case, -the command will prompt you for every matching item or album and invite you to -type `y` to apply the changes, `n` to discard them or `q` to exit and apply -the selected changes. This option lets you choose precisely which data to -change without spending too much time to carefully craft a query. To skip the -prompts entirely, use the ``-y`` option. +When you run the ``modify`` command, it prints a list of all affected items in +the library and asks for your permission before making any changes. You can then +choose to abort the change (type ``n``), confirm (``y``), or interactively +choose some of the items (``s``). In the latter case, the command will prompt +you for every matching item or album and invite you to type ``y`` to apply the +changes, ``n`` to discard them or ``q`` to exit and apply the selected changes. +This option lets you choose precisely which data to change without spending too +much time to carefully craft a query. To skip the prompts entirely, use the +``-y`` option. .. _move-cmd: move -```` +~~~~ + :: beet move [-capt] [-d DIR] QUERY @@ -329,15 +311,16 @@ anywhere in your filesystem. The ``-c`` option copies files instead of moving them. As with other commands, the ``-a`` option matches albums instead of items. The ``-e`` flag (for "export") copies files without changing the database. -To perform a "dry run", just use the ``-p`` (for "pretend") flag. This will -show you a list of files that would be moved but won't actually change anything -on disk. The ``-t`` option sets the timid mode which will ask again -before really moving or copying the files. +To perform a "dry run", just use the ``-p`` (for "pretend") flag. This will show +you a list of files that would be moved but won't actually change anything on +disk. The ``-t`` option sets the timid mode which will ask again before really +moving or copying the files. .. _update-cmd: update -`````` +~~~~~~ + :: beet update [-F] FIELD [-e] EXCLUDE_FIELD [-aM] QUERY @@ -356,78 +339,80 @@ To perform a "dry run" of an update, just use the ``-p`` (for "pretend") flag. This will show you all the proposed changes but won't actually change anything on disk. -By default, all the changed metadata will be populated back to the database. -If you only want certain fields to be written, specify them with the ```-F``` -flags (which can be used multiple times). Alternatively, specify fields to *not* -write with ```-e``` flags (which can be used multiple times). For the list of -supported fields, please see ```beet fields```. +By default, all the changed metadata will be populated back to the database. If +you only want certain fields to be written, specify them with the ``-F`` flags +(which can be used multiple times). Alternatively, specify fields to *not* write +with ``-e`` flags (which can be used multiple times). For the list of supported +fields, please see ``beet fields``. When an updated track is part of an album, the album-level fields of *all* tracks from the album are also updated. (Specifically, the command copies -album-level data from the first track on the album and applies it to the -rest of the tracks.) This means that, if album-level fields aren't identical -within an album, some changes shown by the ``update`` command may be -overridden by data from other tracks on the same album. This means that -running the ``update`` command multiple times may show the same changes being -applied. - +album-level data from the first track on the album and applies it to the rest of +the tracks.) This means that, if album-level fields aren't identical within an +album, some changes shown by the ``update`` command may be overridden by data +from other tracks on the same album. This means that running the ``update`` +command multiple times may show the same changes being applied. .. _write-cmd: write -````` +~~~~~ + :: beet write [-pf] [QUERY] Write metadata from the database into files' tags. -When you make changes to the metadata stored in beets' library database -(during import or with the :ref:`modify-cmd` command, for example), you often -have the option of storing changes only in the database, leaving your files -untouched. The ``write`` command lets you later change your mind and write the -contents of the database into the files. By default, this writes the changes only if there is a difference between the database and the tags in the file. +When you make changes to the metadata stored in beets' library database (during +import or with the :ref:`modify-cmd` command, for example), you often have the +option of storing changes only in the database, leaving your files untouched. +The ``write`` command lets you later change your mind and write the contents of +the database into the files. By default, this writes the changes only if there +is a difference between the database and the tags in the file. You can think of this command as the opposite of :ref:`update-cmd`. The ``-p`` option previews metadata changes without actually applying them. -The ``-f`` option forces a write to the file, even if the file tags match the database. This is useful for making sure that enabled plugins that run on write (e.g., the Scrub and Zero plugins) are run on the file. - - +The ``-f`` option forces a write to the file, even if the file tags match the +database. This is useful for making sure that enabled plugins that run on write +(e.g., the Scrub and Zero plugins) are run on the file. .. _stats-cmd: stats -````` +~~~~~ + :: beet stats [-e] [QUERY] -Show some statistics on your entire library (if you don't provide a -:doc:`query <query>`) or the matched items (if you do). +Show some statistics on your entire library (if you don't provide a :doc:`query +<query>`) or the matched items (if you do). -By default, the command calculates file sizes using their bitrate and -duration. The ``-e`` (``--exact``) option reads the exact sizes of each file -(but is slower). The exact mode also outputs the exact duration in seconds. +By default, the command calculates file sizes using their bitrate and duration. +The ``-e`` (``--exact``) option reads the exact sizes of each file (but is +slower). The exact mode also outputs the exact duration in seconds. .. _fields-cmd: fields -`````` +~~~~~~ + :: beet fields Show the item and album metadata fields available for use in :doc:`query` and -:doc:`pathformat`. The listing includes any template fields provided by -plugins and any flexible attributes you've manually assigned to your items and -albums. +:doc:`pathformat`. The listing includes any template fields provided by plugins +and any flexible attributes you've manually assigned to your items and albums. .. _config-cmd: config -`````` +~~~~~~ + :: beet config [-pdc] @@ -435,20 +420,19 @@ config Show or edit the user configuration. This command does one of three things: -* With no options, print a YAML representation of the current user - configuration. With the ``--default`` option, beets' default options are - also included in the dump. -* The ``--path`` option instead shows the path to your configuration file. - This can be combined with the ``--default`` flag to show where beets keeps - its internal defaults. -* By default, sensitive information like passwords is removed when dumping the +- With no options, print a YAML representation of the current user + configuration. With the ``--default`` option, beets' default options are also + included in the dump. +- The ``--path`` option instead shows the path to your configuration file. This + can be combined with the ``--default`` flag to show where beets keeps its + internal defaults. +- By default, sensitive information like passwords is removed when dumping the configuration. The ``--clear`` option includes this sensitive data. -* With the ``--edit`` option, beets attempts to open your config file for +- With the ``--edit`` option, beets attempts to open your config file for editing. It first tries the ``$EDITOR`` environment variable, followed by ``$EDITOR`` and then a fallback option depending on your platform: ``open`` on OS X, ``xdg-open`` on Unix, and direct invocation on Windows. - .. _global-flags: Global Flags @@ -458,27 +442,26 @@ Beets has a few "global" flags that affect all commands. These must appear between the executable name (``beet``) and the command---for example, ``beet -v import ...``. -* ``-l LIBPATH``: specify the library database file to use. -* ``-d DIRECTORY``: specify the library root directory. -* ``-v``: verbose mode; prints out a deluge of debugging information. Please use +- ``-l LIBPATH``: specify the library database file to use. +- ``-d DIRECTORY``: specify the library root directory. +- ``-v``: verbose mode; prints out a deluge of debugging information. Please use this flag when reporting bugs. You can use it twice, as in ``-vv``, to make beets even more verbose. -* ``-c FILE``: read a specified YAML :doc:`configuration file <config>`. This +- ``-c FILE``: read a specified YAML :doc:`configuration file <config>`. This configuration works as an overlay: rather than replacing your normal configuration options entirely, the two are merged. Any individual options set in this config file will override the corresponding settings in your base configuration. -* ``-p plugins``: specify a comma-separated list of plugins to enable. If - specified, the plugin list in your configuration is ignored. The long form - of this argument also allows specifying no plugins, effectively disabling - all plugins: ``--plugins=``. -* ``-P plugins``: specify a comma-separated list of plugins to disable in a - specific beets run. This will overwrite ``-p`` if used with it. To disable all plugins, use - ``--plugins=`` instead. - -Beets also uses the ``BEETSDIR`` environment variable to look for -configuration and data. +- ``-p plugins``: specify a comma-separated list of plugins to enable. If + specified, the plugin list in your configuration is ignored. The long form of + this argument also allows specifying no plugins, effectively disabling all + plugins: ``--plugins=``. +- ``-P plugins``: specify a comma-separated list of plugins to disable in a + specific beets run. This will overwrite ``-p`` if used with it. To disable all + plugins, use ``--plugins=`` instead. +Beets also uses the ``BEETSDIR`` environment variable to look for configuration +and data. .. _completion: @@ -486,48 +469,55 @@ Shell Completion ---------------- Beets includes support for shell command completion. The command ``beet -completion`` prints out a `bash`_ 3.2 script; to enable completion put a line -like this into your ``.bashrc`` or similar file:: +completion`` prints out a bash_ 3.2 script; to enable completion put a line like +this into your ``.bashrc`` or similar file: + +:: eval "$(beet completion)" Or, to avoid slowing down your shell startup time, you can pipe the ``beet completion`` output to a file and source that instead. -You will also need to source the `bash-completion`_ script, which is probably +You will also need to source the bash-completion_ script, which is probably available via your package manager. On OS X, you can install it via Homebrew with ``brew install bash-completion``; Homebrew will give you instructions for sourcing the script. -.. _bash-completion: https://github.com/scop/bash-completion .. _bash: https://www.gnu.org/software/bash/ -The completion script suggests names of subcommands and (after typing -``-``) options of the given command. If you are using a command that -accepts a query, the script will also complete field names. :: +.. _bash-completion: https://github.com/scop/bash-completion + +The completion script suggests names of subcommands and (after typing ``-``) +options of the given command. If you are using a command that accepts a query, +the script will also complete field names. + +:: beet list ar[TAB] # artist: artist_credit: artist_sort: artpath: beet list artp[TAB] beet list artpath\: -(Don't worry about the slash in front of the colon: this is a escape -sequence for the shell and won't be seen by beets.) +(Don't worry about the slash in front of the colon: this is a escape sequence +for the shell and won't be seen by beets.) -Completion of plugin commands only works for those plugins -that were enabled when running ``beet completion``. If you add a plugin -later on you will want to re-generate the script. +Completion of plugin commands only works for those plugins that were enabled +when running ``beet completion``. If you add a plugin later on you will want to +re-generate the script. zsh -``` +~~~ If you use zsh, take a look at the included `completion script`_. The script -should be placed in a directory that is part of your ``fpath``, and `not` +should be placed in a directory that is part of your ``fpath``, and ``not`` sourced in your ``.zshrc``. Running ``echo $fpath`` will give you a list of valid directories. Another approach is to use zsh's bash completion compatibility. This snippet -defines some bash-specific functions to make this work without errors:: +defines some bash-specific functions to make this work without errors: + +:: autoload bashcompinit bashcompinit @@ -538,7 +528,6 @@ defines some bash-specific functions to make this work without errors:: .. _completion script: https://github.com/beetbox/beets/blob/master/extra/_beet - .. only:: man See Also diff --git a/docs/reference/config.rst b/docs/reference/config.rst index 7e93b00ff..d4f5b3674 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -1,25 +1,27 @@ Configuration ============= -Beets has an extensive configuration system that lets you customize nearly -every aspect of its operation. To configure beets, you create a file called +Beets has an extensive configuration system that lets you customize nearly every +aspect of its operation. To configure beets, you create a file called ``config.yaml``. The location of the file depends on your platform (type ``beet config -p`` to see the path on your system): -* On Unix-like OSes, write ``~/.config/beets/config.yaml``. -* On Windows, use ``%APPDATA%\beets\config.yaml``. This is usually in a +- On Unix-like OSes, write ``~/.config/beets/config.yaml``. +- On Windows, use ``%APPDATA%\beets\config.yaml``. This is usually in a directory like ``C:\Users\You\AppData\Roaming``. -* On OS X, you can use either the Unix location or ``~/Library/Application +- On OS X, you can use either the Unix location or ``~/Library/Application Support/beets/config.yaml``. -You can launch your text editor to create or update your configuration by -typing ``beet config -e``. (See the :ref:`config-cmd` command for details.) It -is also possible to customize the location of the configuration file and even -use multiple layers of configuration. See `Configuration Location`_, below. +You can launch your text editor to create or update your configuration by typing +``beet config -e``. (See the :ref:`config-cmd` command for details.) It is also +possible to customize the location of the configuration file and even use +multiple layers of configuration. See `Configuration Location`_, below. -The config file uses `YAML`_ syntax. You can use the full power of YAML, but -most configuration options are simple key/value pairs. This means your config -file will look like this:: +The config file uses YAML_ syntax. You can use the full power of YAML, but most +configuration options are simple key/value pairs. This means your config file +will look like this: + +:: option: value another_option: foo @@ -28,14 +30,14 @@ file will look like this:: foo: bar In YAML, you will need to use spaces (not tabs!) to indent some lines. If you -have questions about more sophisticated syntax, take a look at the `YAML`_ +have questions about more sophisticated syntax, take a look at the YAML_ documentation. -.. _YAML: https://yaml.org/ +.. _yaml: https://yaml.org/ The rest of this page enumerates the dizzying litany of configuration options -available in beets. You might also want to see an -:ref:`example <config-example>`. +available in beets. You might also want to see an :ref:`example +<config-example>`. .. contents:: :local: @@ -63,23 +65,24 @@ library. Defaults to a folder called ``Music`` in your home directory. plugins ~~~~~~~ -A space-separated list of plugin module names to load. See -:ref:`using-plugins`. +A space-separated list of plugin module names to load. See :ref:`using-plugins`. include ~~~~~~~ -A space-separated list of extra configuration files to include. -Filenames are relative to the directory containing ``config.yaml``. +A space-separated list of extra configuration files to include. Filenames are +relative to the directory containing ``config.yaml``. pluginpath ~~~~~~~~~~ -Directories to search for plugins. Each Python file or directory in a plugin -path represents a plugin and should define a subclass of :class:`BeetsPlugin`. -A plugin can then be loaded by adding the filename to the `plugins` configuration. -The plugin path can either be a single string or a list of strings---so, if you -have multiple paths, format them as a YAML list like so:: +Directories to search for plugins. Each Python file or directory in a plugin +path represents a plugin and should define a subclass of :class:`BeetsPlugin`. A +plugin can then be loaded by adding the filename to the ``plugins`` +configuration. The plugin path can either be a single string or a list of +strings---so, if you have multiple paths, format them as a YAML list like so: + +:: pluginpath: - /path/one @@ -91,7 +94,7 @@ ignore ~~~~~~ A list of glob patterns specifying file and directory names to be ignored when -importing. By default, this consists of ``.*``, ``*~``, ``System Volume +importing. By default, this consists of ``.*``, ``*~``, ``System Volume Information``, ``lost+found`` (i.e., beets ignores Unix-style hidden files, backup files, and directories that appears at the root of some Linux and Windows filesystems). @@ -121,9 +124,11 @@ replacement strings. For example, ``[xy]: z`` will make beets replace all instances of the characters ``x`` or ``y`` with the character ``z``. If you do change this value, be certain that you include at least enough -substitutions to avoid causing errors on your operating system. Here are -the default substitutions used by beets, which are sufficient to avoid -unexpected behavior on all popular platforms:: +substitutions to avoid causing errors on your operating system. Here are the +default substitutions used by beets, which are sufficient to avoid unexpected +behavior on all popular platforms: + +:: replace: '[\\/]': _ @@ -135,40 +140,39 @@ unexpected behavior on all popular platforms:: '^\s+': '' '^-': _ -These substitutions remove forward and back slashes, leading dots, and -control characters—all of which is a good idea on any OS. The fourth line -removes the Windows "reserved characters" (useful even on Unix for -compatibility with Windows-influenced network filesystems like Samba). -Trailing dots and trailing whitespace, which can cause problems on Windows -clients, are also removed. +These substitutions remove forward and back slashes, leading dots, and control +characters—all of which is a good idea on any OS. The fourth line removes the +Windows "reserved characters" (useful even on Unix for compatibility with +Windows-influenced network filesystems like Samba). Trailing dots and trailing +whitespace, which can cause problems on Windows clients, are also removed. When replacements other than the defaults are used, it is possible that they will increase the length of the path. In the scenario where this leads to a -conflict with the maximum filename length, the default replacements will be -used to resolve the conflict and beets will display a warning. +conflict with the maximum filename length, the default replacements will be used +to resolve the conflict and beets will display a warning. -Note that paths might contain special characters such as typographical -quotes (``“”``). With the configuration above, those will not be -replaced as they don't match the typewriter quote (``"``). To also strip these -special characters, you can either add them to the replacement list or use the -:ref:`asciify-paths` configuration option below. +Note that paths might contain special characters such as typographical quotes +(``“”``). With the configuration above, those will not be replaced as they don't +match the typewriter quote (``"``). To also strip these special characters, you +can either add them to the replacement list or use the :ref:`asciify-paths` +configuration option below. .. _path-sep-replace: path_sep_replace ~~~~~~~~~~~~~~~~ -A string that replaces the path separator (for example, the forward slash -``/`` on Linux and MacOS, and the backward slash ``\\`` on Windows) when -generating filenames with beets. -This option is related to :ref:`replace`, but is distinct from it for -technical reasons. +A string that replaces the path separator (for example, the forward slash ``/`` +on Linux and MacOS, and the backward slash ``\\`` on Windows) when generating +filenames with beets. This option is related to :ref:`replace`, but is distinct +from it for technical reasons. .. warning:: - Changing this option is potentially dangerous. For example, setting - it to the actual path separator could create directories in unexpected - locations. Use caution when changing it and always try it out on a small - number of files before applying it to your whole library. + + Changing this option is potentially dangerous. For example, setting it to + the actual path separator could create directories in unexpected locations. + Use caution when changing it and always try it out on a small number of + files before applying it to your whole library. Default: ``_``. @@ -179,22 +183,20 @@ asciify_paths Convert all non-ASCII characters in paths to ASCII equivalents. -For example, if your path template for -singletons is ``singletons/$title`` and the title of a track is "Café", -then the track will be saved as ``singletons/Cafe.mp3``. The changes -take place before applying the :ref:`replace` configuration and are roughly -equivalent to wrapping all your path templates in the ``%asciify{}`` -:ref:`template function <template-functions>`. +For example, if your path template for singletons is ``singletons/$title`` and +the title of a track is "Café", then the track will be saved as +``singletons/Cafe.mp3``. The changes take place before applying the +:ref:`replace` configuration and are roughly equivalent to wrapping all your +path templates in the ``%asciify{}`` :ref:`template function +<template-functions>`. -This uses the `unidecode module`_ which is language agnostic, so some -characters may be transliterated from a different language than expected. -For example, Japanese kanji will usually use their Chinese readings. +This uses the `unidecode module <https://pypi.org/project/Unidecode>`__ which is +language agnostic, so some characters may be transliterated from a different +language than expected. For example, Japanese kanji will usually use their +Chinese readings. Default: ``no``. -.. _unidecode module: https://pypi.org/project/Unidecode - - .. _art-filename: art_filename @@ -209,38 +211,37 @@ album's directory). threaded ~~~~~~~~ -Either ``yes`` or ``no``, indicating whether the autotagger should use -multiple threads. This makes things substantially faster by overlapping work: -for example, it can copy files for one album in parallel with looking up data -in MusicBrainz for a different album. You may want to disable this when -debugging problems with the autotagger. -Defaults to ``yes``. +Either ``yes`` or ``no``, indicating whether the autotagger should use multiple +threads. This makes things substantially faster by overlapping work: for +example, it can copy files for one album in parallel with looking up data in +MusicBrainz for a different album. You may want to disable this when debugging +problems with the autotagger. Defaults to ``yes``. +.. _format_item: .. _list_format_item: -.. _format_item: format_item ~~~~~~~~~~~ -Format to use when listing *individual items* with the :ref:`list-cmd` -command and other commands that need to print out items. Defaults to -``$artist - $album - $title``. The ``-f`` command-line option overrides -this setting. +Format to use when listing *individual items* with the :ref:`list-cmd` command +and other commands that need to print out items. Defaults to ``$artist - $album +- $title``. The ``-f`` command-line option overrides this setting. -It used to be named `list_format_item`. +It used to be named ``list_format_item``. + +.. _format_album: .. _list_format_album: -.. _format_album: format_album ~~~~~~~~~~~~ -Format to use when listing *albums* with :ref:`list-cmd` and other -commands. Defaults to ``$albumartist - $album``. The ``-f`` command-line -option overrides this setting. +Format to use when listing *albums* with :ref:`list-cmd` and other commands. +Defaults to ``$albumartist - $album``. The ``-f`` command-line option overrides +this setting. -It used to be named `list_format_album`. +It used to be named ``list_format_album``. .. _sort_item: @@ -262,10 +263,11 @@ Default sort order to use when fetching albums from the database. Defaults to sort_case_insensitive ~~~~~~~~~~~~~~~~~~~~~ + Either ``yes`` or ``no``, indicating whether the case should be ignored when sorting lexicographic fields. When set to ``no``, lower-case values will be -placed after upper-case values (e.g., *Bar Qux foo*), while ``yes`` would -result in the more expected *Bar foo Qux*. Default: ``yes``. +placed after upper-case values (e.g., *Bar Qux foo*), while ``yes`` would result +in the more expected *Bar foo Qux*. Default: ``yes``. .. _original_date: @@ -283,11 +285,13 @@ That is, if this option is turned on, then ``year`` will always equal overwrite_null ~~~~~~~~~~~~~~ -This confusingly-named option indicates which fields have meaningful `null` values. If -an album or track field is in the corresponding list, then an existing value for this -field in an item in the database can be overwritten with `null`. By default, however, -`null` is interpreted as information about the field being unavailable, so it would not -overwrite existing values. For example:: +This confusingly-named option indicates which fields have meaningful ``null`` +values. If an album or track field is in the corresponding list, then an +existing value for this field in an item in the database can be overwritten with +``null``. By default, however, ``null`` is interpreted as information about the +field being unavailable, so it would not overwrite existing values. For example: + +:: overwrite_null: album: ["albumid"] @@ -316,15 +320,16 @@ first (non-pregap) track on each disc always has track number 1. If you enable ``per_disc_numbering``, you will likely want to change your :ref:`path-format-config` also to include ``$disc`` before ``$track`` to make filenames sort correctly in album directories. For example, you might want to -use a path format like this:: +use a path format like this: + +:: paths: default: $albumartist/$album%aunique{}/$disc-$track $title -When this option is off (the default), even "pregap" hidden tracks are -numbered from one, not zero, so other track numbers may appear to be bumped up -by one. When it is on, the pregap track for each disc can be numbered zero. - +When this option is off (the default), even "pregap" hidden tracks are numbered +from one, not zero, so other track numbers may appear to be bumped up by one. +When it is on, the pregap track for each disc can be numbered zero. .. _config-aunique: @@ -334,7 +339,9 @@ aunique These options are used to generate a string that is guaranteed to be unique among all albums in the library who share the same set of keys. -The defaults look like this:: +The defaults look like this: + +:: aunique: keys: albumartist album @@ -343,7 +350,6 @@ The defaults look like this:: See :ref:`aunique` for more details. - .. _config-sunique: sunique @@ -353,7 +359,9 @@ Like :ref:`config-aunique` above for albums, these options control the generation of a unique string to disambiguate *singletons* that share similar metadata. -The defaults look like this:: +The defaults look like this: + +:: sunique: keys: artist title @@ -362,18 +370,16 @@ The defaults look like this:: See :ref:`sunique` for more details. - .. _terminal_encoding: terminal_encoding ~~~~~~~~~~~~~~~~~ -The text encoding, as `known to Python`_, to use for messages printed to the -standard output. It's also used to read messages from the standard input. -By default, this is determined automatically from the locale -environment variables. - -.. _known to python: https://docs.python.org/2/library/codecs.html#standard-encodings +The text encoding, as `known to Python +<https://docs.python.org/2/library/codecs.html#standard-encodings>`__, to use +for messages printed to the standard output. It's also used to read messages +from the standard input. By default, this is determined automatically from the +locale environment variables. .. _clutter: @@ -382,7 +388,7 @@ clutter When beets imports all the files in a directory, it tries to remove the directory if it's empty. A directory is considered empty if it only contains -files whose names match the glob patterns in `clutter`, which should be a list +files whose names match the glob patterns in ``clutter``, which should be a list of strings. The default list consists of "Thumbs.DB" and ".DS_Store". The importer only removes recursively searched subdirectories---the top-level @@ -402,9 +408,9 @@ maximum. id3v23 ~~~~~~ -By default, beets writes MP3 tags using the ID3v2.4 standard, the latest -version of ID3. Enable this option to instead use the older ID3v2.3 standard, -which is preferred by certain older software such as Windows Media Player. +By default, beets writes MP3 tags using the ID3v2.4 standard, the latest version +of ID3. Enable this option to instead use the older ID3v2.3 standard, which is +preferred by certain older software such as Windows Media Player. .. _va_name: @@ -420,23 +426,23 @@ Artists'`` (the MusicBrainz standard). Affects other sources, such as UI Options ---------- -The options that allow for customization of the visual appearance -of the console interface. +The options that allow for customization of the visual appearance of the console +interface. These options are available in this section: color ~~~~~ -Either ``yes`` or ``no``; whether to use color in console output (currently -only in the ``import`` command). Turn this off if your terminal doesn't -support ANSI colors. +Either ``yes`` or ``no``; whether to use color in console output (currently only +in the ``import`` command). Turn this off if your terminal doesn't support ANSI +colors. .. note:: - The `color` option was previously a top-level configuration. This is - still respected, but a deprecation message will be shown until your - top-level `color` configuration has been nested under `ui`. + The ``color`` option was previously a top-level configuration. This is still + respected, but a deprecation message will be shown until your top-level + ``color`` configuration has been nested under ``ui``. .. _colors: @@ -444,8 +450,10 @@ colors ~~~~~~ The colors that are used throughout the user interface. These are only used if -the ``color`` option is set to ``yes``. For example, you might have a section -in your configuration file that looks like this:: +the ``color`` option is set to ``yes``. For example, you might have a section in +your configuration file that looks like this: + +:: ui: colors: @@ -473,8 +481,8 @@ in your configuration file that looks like this:: action_description: ['white'] Available colors: black, darkred, darkgreen, brown (darkyellow), darkblue, -purple (darkmagenta), teal (darkcyan), lightgray, darkgray, red, green, -yellow, blue, fuchsia (magenta), turquoise (cyan), white +purple (darkmagenta), teal (darkcyan), lightgray, darkgray, red, green, yellow, +blue, fuchsia (magenta), turquoise (cyan), white Legacy UI colors config directive used strings. If any colors value is still a string instead of a list, it will be translated to list automatically. For @@ -485,7 +493,9 @@ terminal_width Controls line wrapping on non-Unix systems. On Unix systems, the width of the terminal is detected automatically. If this fails, or on non-Unix systems, the -specified value is used as a fallback. Defaults to ``80`` characters:: +specified value is used as a fallback. Defaults to ``80`` characters: + +:: ui: terminal_width: 80 @@ -497,9 +507,11 @@ Beets compares the length of the imported track with the length the metadata source provides. If any tracks differ by at least ``length_diff_thresh`` seconds, they will be colored with ``text_highlight``. Below this threshold, different track lengths are colored with ``text_highlight_minor``. -``length_diff_thresh`` does not impact which releases are selected in -autotagger matching or distance score calculation (see :ref:`match-config`, -``distance_weights`` and :ref:`colors`):: +``length_diff_thresh`` does not impact which releases are selected in autotagger +matching or distance score calculation (see :ref:`match-config`, +``distance_weights`` and :ref:`colors`): + +:: ui: length_diff_thresh: 10.0 @@ -508,11 +520,13 @@ import ~~~~~~ When importing, beets will read several options to configure the visuals of the -import dialogue. There are two layouts controlling how horizontal space and -line wrapping is dealt with: ``column`` and ``newline``. The indentation of the -respective elements of the import UI can also be configured. For example -setting ``4`` for ``match_header`` will indent the very first block of a -proposed match by five characters in the terminal:: +import dialogue. There are two layouts controlling how horizontal space and line +wrapping is dealt with: ``column`` and ``newline``. The indentation of the +respective elements of the import UI can also be configured. For example setting +``4`` for ``match_header`` will indent the very first block of a proposed match +by five characters in the terminal: + +:: ui: import: @@ -527,7 +541,9 @@ Importer Options The options that control the :ref:`import-cmd` command are indented under the ``import:`` key. For example, you might have a section in your configuration -file that looks like this:: +file that looks like this: + +:: import: write: yes @@ -542,39 +558,38 @@ write ~~~~~ Either ``yes`` or ``no``, controlling whether metadata (e.g., ID3) tags are -written to files when using ``beet import``. Defaults to ``yes``. The ``-w`` -and ``-W`` command-line options override this setting. +written to files when using ``beet import``. Defaults to ``yes``. The ``-w`` and +``-W`` command-line options override this setting. .. _config-import-copy: copy ~~~~ -Either ``yes`` or ``no``, indicating whether to **copy** files into the -library directory when using ``beet import``. Defaults to ``yes``. Can be -overridden with the ``-c`` and ``-C`` command-line options. +Either ``yes`` or ``no``, indicating whether to **copy** files into the library +directory when using ``beet import``. Defaults to ``yes``. Can be overridden +with the ``-c`` and ``-C`` command-line options. -The option is ignored if ``move`` is enabled (i.e., beets can move or -copy files but it doesn't make sense to do both). +The option is ignored if ``move`` is enabled (i.e., beets can move or copy files +but it doesn't make sense to do both). .. _config-import-move: move ~~~~ -Either ``yes`` or ``no``, indicating whether to **move** files into the -library directory when using ``beet import``. -Defaults to ``no``. +Either ``yes`` or ``no``, indicating whether to **move** files into the library +directory when using ``beet import``. Defaults to ``no``. -The effect is similar to the ``copy`` option but you end up with only -one copy of the imported file. ("Moving" works even across filesystems; if -necessary, beets will copy and then delete when a simple rename is -impossible.) Moving files can be risky—it's a good idea to keep a backup in -case beets doesn't do what you expect with your files. +The effect is similar to the ``copy`` option but you end up with only one copy +of the imported file. ("Moving" works even across filesystems; if necessary, +beets will copy and then delete when a simple rename is impossible.) Moving +files can be risky—it's a good idea to keep a backup in case beets doesn't do +what you expect with your files. -This option *overrides* ``copy``, so enabling it will always move -(and not copy) files. The ``-c`` switch to the ``beet import`` command, -however, still takes precedence. +This option *overrides* ``copy``, so enabling it will always move (and not copy) +files. The ``-c`` switch to the ``beet import`` command, however, still takes +precedence. .. _link: @@ -597,11 +612,11 @@ hardlink ~~~~~~~~ Either ``yes`` or ``no``, indicating whether to use hard links instead of -moving, copying, or symlinking files. (It conflicts with the ``move``, -``copy``, and ``link`` options.) Defaults to ``no``. +moving, copying, or symlinking files. (It conflicts with the ``move``, ``copy``, +and ``link`` options.) Defaults to ``no``. As with symbolic links (see :ref:`link`, above), this will not work on Windows -and you will want to set ``write`` to ``no``. Otherwise, metadata on the +and you will want to set ``write`` to ``no``. Otherwise, metadata on the original file will be modified. .. _reflink: @@ -610,51 +625,49 @@ reflink ~~~~~~~ Either ``yes``, ``no``, or ``auto``, indicating whether to use copy-on-write -`file clones`_ (a.k.a. "reflinks") instead of copying or moving files. -The ``auto`` option uses reflinks when possible and falls back to plain -copying when necessary. -Defaults to ``no``. +`file clones`_ (a.k.a. "reflinks") instead of copying or moving files. The +``auto`` option uses reflinks when possible and falls back to plain copying when +necessary. Defaults to ``no``. -This kind of clone is only available on certain filesystems: for example, -btrfs and APFS. For more details on filesystem support, see the `pyreflink`_ +This kind of clone is only available on certain filesystems: for example, btrfs +and APFS. For more details on filesystem support, see the pyreflink_ documentation. Note that you need to install ``pyreflink``, either through ``python -m pip install beets[reflink]`` or ``python -m pip install reflink``. -The option is ignored if ``move`` is enabled (i.e., beets can move or -copy files but it doesn't make sense to do both). +The option is ignored if ``move`` is enabled (i.e., beets can move or copy files +but it doesn't make sense to do both). .. _file clones: https://en.wikipedia.org/wiki/Copy-on-write + .. _pyreflink: https://reflink.readthedocs.io/en/latest/ resume ~~~~~~ -Either ``yes``, ``no``, or ``ask``. Controls whether interrupted imports -should be resumed. "Yes" means that imports are always resumed when -possible; "no" means resuming is disabled entirely; "ask" (the default) -means that the user should be prompted when resuming is possible. The ``-p`` -and ``-P`` flags correspond to the "yes" and "no" settings and override this -option. +Either ``yes``, ``no``, or ``ask``. Controls whether interrupted imports should +be resumed. "Yes" means that imports are always resumed when possible; "no" +means resuming is disabled entirely; "ask" (the default) means that the user +should be prompted when resuming is possible. The ``-p`` and ``-P`` flags +correspond to the "yes" and "no" settings and override this option. .. _incremental: incremental ~~~~~~~~~~~ -Either ``yes`` or ``no``, controlling whether imported directories are -recorded and whether these recorded directories are skipped. This -corresponds to the ``-i`` flag to ``beet import``. +Either ``yes`` or ``no``, controlling whether imported directories are recorded +and whether these recorded directories are skipped. This corresponds to the +``-i`` flag to ``beet import``. .. _incremental_skip_later: incremental_skip_later ~~~~~~~~~~~~~~~~~~~~~~ -Either ``yes`` or ``no``, controlling whether skipped directories are -recorded in the incremental list. When set to ``yes``, skipped -directories won't be recorded, and beets will try to import them again -later. When set to ``no``, skipped directories will be recorded, and -skipped later. Defaults to ``no``. +Either ``yes`` or ``no``, controlling whether skipped directories are recorded +in the incremental list. When set to ``yes``, skipped directories won't be +recorded, and beets will try to import them again later. When set to ``no``, +skipped directories will be recorded, and skipped later. Defaults to ``no``. .. _from_scratch: @@ -679,9 +692,9 @@ corresponds to the ``--quiet`` flag to ``beet import``. quiet_fallback ~~~~~~~~~~~~~~ -Either ``skip`` (default) or ``asis``, specifying what should happen in -quiet mode (see the ``-q`` flag to ``import``, above) when there is no -strong recommendation. +Either ``skip`` (default) or ``asis``, specifying what should happen in quiet +mode (see the ``-q`` flag to ``import``, above) when there is no strong +recommendation. .. _none_rec_action: @@ -696,19 +709,18 @@ interactively. timid ~~~~~ -Either ``yes`` or ``no``, controlling whether the importer runs in *timid* -mode, in which it asks for confirmation on every autotagging match, even the -ones that seem very close. Defaults to ``no``. The ``-t`` command-line flag -controls the same setting. +Either ``yes`` or ``no``, controlling whether the importer runs in *timid* mode, +in which it asks for confirmation on every autotagging match, even the ones that +seem very close. Defaults to ``no``. The ``-t`` command-line flag controls the +same setting. .. _import_log: log ~~~ -Specifies a filename where the importer's log should be kept. By default, -no log is written. This can be overridden with the ``-l`` flag to -``import``. +Specifies a filename where the importer's log should be kept. By default, no log +is written. This can be overridden with the ``-l`` flag to ``import``. .. _default_action: @@ -728,8 +740,8 @@ languages A list of locale names to search for preferred aliases. For example, setting this to ``en`` uses the transliterated artist name "Pyotr Ilyich Tchaikovsky" instead of the Cyrillic script for the composer's name when tagging from -MusicBrainz. You can use a space-separated list of language abbreviations, like -``en jp es``, to specify a preference order. Defaults to an empty list, meaning +MusicBrainz. You can use a space-separated list of language abbreviations, like +``en jp es``, to specify a preference order. Defaults to an empty list, meaning that no language is preferred. .. _ignored_alias_types: @@ -739,7 +751,7 @@ ignored_alias_types A list of alias types to be ignored when importing new items. -See the `MusicBrainz Documentation` for more information on aliases. +See the ``MusicBrainz Documentation`` for more information on aliases. .._MusicBrainz Documentation: https://musicbrainz.org/doc/Aliases @@ -760,8 +772,8 @@ group_albums By default, the beets importer groups tracks into albums based on the directories they reside in. This option instead uses files' metadata to -partition albums. Enable this option if you have directories that contain -tracks from many albums mixed together. +partition albums. Enable this option if you have directories that contain tracks +from many albums mixed together. The ``--group-albums`` or ``-g`` option to the :ref:`import-cmd` command is equivalent, and the *G* interactive option invokes the same workflow. @@ -773,10 +785,10 @@ Default: ``no``. autotag ~~~~~~~ -By default, the beets importer always attempts to autotag new music. If -most of your collection consists of obscure music, you may be interested in -disabling autotagging by setting this option to ``no``. (You can re-enable it -with the ``-a`` flag to the :ref:`import-cmd` command.) +By default, the beets importer always attempts to autotag new music. If most of +your collection consists of obscure music, you may be interested in disabling +autotagging by setting this option to ``no``. (You can re-enable it with the +``-a`` flag to the :ref:`import-cmd` command.) Default: ``yes``. @@ -785,13 +797,14 @@ Default: ``yes``. duplicate_keys ~~~~~~~~~~~~~~ -The fields used to find duplicates when importing. -There are two sub-values here: ``album`` and ``item``. -Each one is a list of field names; if an existing object (album or item) in -the library matches the new object on all of these fields, the importer will -consider it a duplicate. +The fields used to find duplicates when importing. There are two sub-values +here: ``album`` and ``item``. Each one is a list of field names; if an existing +object (album or item) in the library matches the new object on all of these +fields, the importer will consider it a duplicate. -Default:: +Default: + +:: album: albumartist album item: artist title @@ -801,12 +814,11 @@ Default:: duplicate_action ~~~~~~~~~~~~~~~~ -Either ``skip``, ``keep``, ``remove``, ``merge`` or ``ask``. -Controls how duplicates are treated in import task. -"skip" means that new item(album or track) will be skipped; -"keep" means keep both old and new items; "remove" means remove old -item; "merge" means merge into one album; "ask" means the user -should be prompted for the action each time. The default is ``ask``. +Either ``skip``, ``keep``, ``remove``, ``merge`` or ``ask``. Controls how +duplicates are treated in import task. "skip" means that new item(album or +track) will be skipped; "keep" means keep both old and new items; "remove" means +remove old item; "merge" means merge into one album; "ask" means the user should +be prompted for the action each time. The default is ``ask``. .. _duplicate_verbose_prompt: @@ -815,9 +827,8 @@ duplicate_verbose_prompt Usually when duplicates are detected during import, information about the existing and the newly imported album is summarized. Enabling this option also -lists details on individual tracks. The :ref:`format_item setting -<format_item>` is applied, which would, considering the default, look like -this: +lists details on individual tracks. The :ref:`format_item setting <format_item>` +is applied, which would, considering the default, look like this: .. code-block:: console @@ -845,8 +856,10 @@ Default: ``no``. set_fields ~~~~~~~~~~ -A dictionary indicating fields to set to values for newly imported music. -Here's an example:: +A dictionary indicating fields to set to values for newly imported music. Here's +an example: + +:: set_fields: genre: 'To Listen' @@ -855,11 +868,11 @@ Here's an example:: Other field/value pairs supplied via the ``--set`` option on the command-line override any settings here for fields with the same name. -Values support the same template syntax as beets' -:doc:`path formats <pathformat>`. +Values support the same template syntax as beets' :doc:`path formats +<pathformat>`. -Fields are set on both the album and each individual track of the album. -Fields are persisted to the media files of each track. +Fields are set on both the album and each individual track of the album. Fields +are persisted to the media files of each track. Default: ``{}`` (empty). @@ -868,10 +881,10 @@ Default: ``{}`` (empty). singleton_album_disambig ~~~~~~~~~~~~~~~~~~~~~~~~ -During singleton imports and if the metadata source provides it, album names -are appended to the disambiguation string of matching track candidates. For -example: ``The Artist - The Title (Discogs, Index 3, Track B1, [The Album]``. -This feature is currently supported by the :doc:`/plugins/discogs` and the +During singleton imports and if the metadata source provides it, album names are +appended to the disambiguation string of matching track candidates. For example: +``The Artist - The Title (Discogs, Index 3, Track B1, [The Album]``. This +feature is currently supported by the :doc:`/plugins/discogs` and the :doc:`/plugins/spotify`. Default: ``yes``. @@ -885,13 +898,15 @@ You can configure some aspects of the logic beets uses when automatically matching MusicBrainz results under the ``match:`` section. To control how *tolerant* the autotagger is of differences, use the ``strong_rec_thresh`` option, which reflects the distance threshold below which beets will make a -"strong recommendation" that the metadata be used. Strong recommendations -are accepted automatically (except in "timid" mode), so you can use this to -make beets ask your opinion more or less often. +"strong recommendation" that the metadata be used. Strong recommendations are +accepted automatically (except in "timid" mode), so you can use this to make +beets ask your opinion more or less often. The threshold is a *distance* value between 0.0 and 1.0, so you can think of it as the opposite of a *similarity* value. For example, if you want to -automatically accept any matches above 90% similarity, use:: +automatically accept any matches above 90% similarity, use: + +:: match: strong_rec_thresh: 0.10 @@ -911,13 +926,15 @@ max_rec As mentioned above, autotagger matches have *recommendations* that control how the UI behaves for a certain quality of match. The recommendation for a certain -match is based on the overall distance calculation. But you can also control -the recommendation when a specific distance penalty is applied by defining -*maximum* recommendations for each field: +match is based on the overall distance calculation. But you can also control the +recommendation when a specific distance penalty is applied by defining *maximum* +recommendations for each field: To define maxima, use keys under ``max_rec:`` in the ``match`` section. The defaults are "medium" for missing and unmatched tracks and "strong" (i.e., no -maximum) for everything else:: +maximum) for everything else: + +:: match: max_rec: @@ -925,30 +942,30 @@ maximum) for everything else:: unmatched_tracks: medium If a recommendation is higher than the configured maximum and the indicated -penalty is applied, the recommendation is downgraded. The setting for -each field can be one of ``none``, ``low``, ``medium`` or ``strong``. When the -maximum recommendation is ``strong``, no "downgrading" occurs. The available -penalty names here are: +penalty is applied, the recommendation is downgraded. The setting for each field +can be one of ``none``, ``low``, ``medium`` or ``strong``. When the maximum +recommendation is ``strong``, no "downgrading" occurs. The available penalty +names here are: -* source -* artist -* album -* media -* mediums -* year -* country -* label -* catalognum -* albumdisambig -* album_id -* tracks -* missing_tracks -* unmatched_tracks -* track_title -* track_artist -* track_index -* track_length -* track_id +- source +- artist +- album +- media +- mediums +- year +- country +- label +- catalognum +- albumdisambig +- album_id +- tracks +- missing_tracks +- unmatched_tracks +- track_title +- track_artist +- track_index +- track_length +- track_id .. _preferred: @@ -962,13 +979,15 @@ media types. A distance penalty will be applied if the country or media type from the match metadata doesn't match. The specified values are preferred in descending order (i.e., the first item will be most preferred). Each item may be a regular -expression, and will be matched case insensitively. The number of media will -be stripped when matching preferred media (e.g. "2x" in "2xCD"). +expression, and will be matched case insensitively. The number of media will be +stripped when matching preferred media (e.g. "2x" in "2xCD"). You can also tell the autotagger to prefer matches that have a release year closest to the original year for an album. -Here's an example:: +Here's an example: + +:: match: preferred: @@ -984,14 +1003,20 @@ ignored ~~~~~~~ You can completely avoid matches that have certain penalties applied by adding -the penalty name to the ``ignored`` setting:: +the penalty name to the ``ignored`` setting: + +:: match: ignored: missing_tracks unmatched_tracks The available penalties are the same as those for the :ref:`max_rec` setting. -For example, setting ``ignored: missing_tracks`` will skip any album matches where your audio files are missing some of the tracks. The importer will not attempt to display these matches. It does not ignore the fact that the album is missing tracks, which would allow these matches to apply more easily. To do that, you'll want to adjust the penalty for missing tracks. +For example, setting ``ignored: missing_tracks`` will skip any album matches +where your audio files are missing some of the tracks. The importer will not +attempt to display these matches. It does not ignore the fact that the album is +missing tracks, which would allow these matches to apply more easily. To do +that, you'll want to adjust the penalty for missing tracks. .. _required: @@ -999,7 +1024,9 @@ required ~~~~~~~~ You can avoid matches that lack certain required information. Add the tags you -want to enforce to the ``required`` setting:: +want to enforce to the ``required`` setting: + +:: match: required: year label catalognum country @@ -1013,7 +1040,9 @@ ignored_media A list of media (i.e., formats) in metadata databases to ignore when matching music. You can use this to ignore all media that usually contain video instead -of audio, for example:: +of audio, for example: + +:: match: ignored_media: ['Data CD', 'DVD', 'DVD-Video', 'Blu-ray', 'HD-DVD', @@ -1021,11 +1050,10 @@ of audio, for example:: No formats are ignored by default. - .. _ignore_data_tracks: ignore_data_tracks -~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ By default, audio files contained in data tracks within a release are included in the album's tracklist. If you want them to be included, set it ``no``. @@ -1048,27 +1076,31 @@ Default: ``yes``. Path Format Configuration ------------------------- -You can also configure the directory hierarchy beets uses to store music. -These settings appear under the ``paths:`` key. Each string is a template -string that can refer to metadata fields like ``$artist`` or ``$title``. The -filename extension is added automatically. At the moment, you can specify three -special paths: ``default`` for most releases, ``comp`` for "various artist" -releases with no dominant artist, and ``singleton`` for non-album tracks. The -defaults look like this:: +You can also configure the directory hierarchy beets uses to store music. These +settings appear under the ``paths:`` key. Each string is a template string that +can refer to metadata fields like ``$artist`` or ``$title``. The filename +extension is added automatically. At the moment, you can specify three special +paths: ``default`` for most releases, ``comp`` for "various artist" releases +with no dominant artist, and ``singleton`` for non-album tracks. The defaults +look like this: + +:: paths: default: $albumartist/$album%aunique{}/$track $title singleton: Non-Album/$artist/$title comp: Compilations/$album%aunique{}/$track $title -Note the use of ``$albumartist`` instead of ``$artist``; this ensures that albums -will be well-organized. For more about these format strings, see +Note the use of ``$albumartist`` instead of ``$artist``; this ensures that +albums will be well-organized. For more about these format strings, see :doc:`pathformat`. The ``aunique{}`` function ensures that identically-named albums are placed in different directories; see :ref:`aunique` for details. In addition to ``default``, ``comp``, and ``singleton``, you can condition path queries based on beets queries (see :doc:`/reference/query`). This means that a -config file like this:: +config file like this: + +:: paths: albumtype:soundtrack: Soundtracks/$album/$track $title @@ -1082,7 +1114,6 @@ fact, just shorthand for the explicit queries ``singleton:true`` and ``comp:true``. In contrast, ``default`` is special and has no query equivalent: the ``default`` format is only used if no queries match. - Configuration Location ---------------------- @@ -1104,35 +1135,36 @@ Command-Line Option ~~~~~~~~~~~~~~~~~~~ Alternatively, you can use the ``--config`` command-line option to indicate a -YAML file containing options that will then be merged with your existing -options (from ``BEETSDIR`` or the default locations). This is useful if you -want to keep your configuration mostly the same but modify a few options as a -batch. For example, you might have different strategies for importing files, -each with a different set of importer options. +YAML file containing options that will then be merged with your existing options +(from ``BEETSDIR`` or the default locations). This is useful if you want to keep +your configuration mostly the same but modify a few options as a batch. For +example, you might have different strategies for importing files, each with a +different set of importer options. Default Location ~~~~~~~~~~~~~~~~ -In the absence of a ``BEETSDIR`` variable, beets searches a few places for -your configuration, depending on the platform: +In the absence of a ``BEETSDIR`` variable, beets searches a few places for your +configuration, depending on the platform: - On Unix platforms, including OS X:``~/.config/beets`` and then ``$XDG_CONFIG_DIR/beets``, if the environment variable is set. - On OS X, we also search ``~/Library/Application Support/beets`` before the Unixy locations. -- On Windows: ``~\AppData\Roaming\beets``, and then ``%APPDATA%\beets``, if - the environment variable is set. +- On Windows: ``~\AppData\Roaming\beets``, and then ``%APPDATA%\beets``, if the + environment variable is set. Beets uses the first directory in your platform's list that contains ``config.yaml``. If no config file exists, the last path in the list is used. - .. _config-example: Example ------- -Here's an example file:: +Here's an example file: + +:: directory: /var/mp3 import: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 42600dc93..1f0436659 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -6,9 +6,9 @@ started with beets as a new user, though, you may want to read the :doc:`/guides/main` guide first. .. toctree:: - :maxdepth: 2 + :maxdepth: 2 - cli - config - pathformat - query + cli + config + pathformat + query diff --git a/docs/reference/pathformat.rst b/docs/reference/pathformat.rst index d89eb6767..1fc204b62 100644 --- a/docs/reference/pathformat.rst +++ b/docs/reference/pathformat.rst @@ -1,30 +1,27 @@ Path Formats ============ -The ``paths:`` section of the config file (see :doc:`config`) lets -you specify the directory and file naming scheme for your music library. -Templates substitute symbols like ``$title`` (any field value prefixed by ``$``) -with the appropriate value from the track's metadata. Beets adds the filename -extension automatically. +The ``paths:`` section of the config file (see :doc:`config`) lets you specify +the directory and file naming scheme for your music library. Templates +substitute symbols like ``$title`` (any field value prefixed by ``$``) with the +appropriate value from the track's metadata. Beets adds the filename extension +automatically. -For example, consider this path format string: -``$albumartist/$album/$track $title`` +For example, consider this path format string: ``$albumartist/$album/$track +$title`` Here are some paths this format will generate: -* ``Yeah Yeah Yeahs/It's Blitz!/01 Zero.mp3`` - -* ``Spank Rock/YoYoYoYoYo/11 Competition.mp3`` - -* ``The Magnetic Fields/Realism/01 You Must Be Out of Your Mind.mp3`` +- ``Yeah Yeah Yeahs/It's Blitz!/01 Zero.mp3`` +- ``Spank Rock/YoYoYoYoYo/11 Competition.mp3`` +- ``The Magnetic Fields/Realism/01 You Must Be Out of Your Mind.mp3`` Because ``$`` is used to delineate a field reference, you can use ``$$`` to emit a dollars sign. As with `Python template strings`_, ``${title}`` is equivalent to ``$title``; you can use this if you need to separate a field name from the text that follows it. -.. _Python template strings: https://docs.python.org/library/string.html#template-strings - +.. _python template strings: https://docs.python.org/library/string.html#template-strings A Note About Artists -------------------- @@ -37,11 +34,10 @@ Continuing with the Stop Making Sense example, you'll end up with most of the tracks in a "Talking Heads" directory and one in a "Tom Tom Club" directory. You probably don't want that! So use ``$albumartist``. -.. _Stop Making Sense: - https://musicbrainz.org/release/798dcaab-0f1a-4f02-a9cb-61d5b0ddfd36.html - -As a convenience, however, beets allows ``$albumartist`` to fall back to the value for ``$artist`` and vice-versa if one tag is present but the other is not. +.. _stop making sense: https://musicbrainz.org/release/798dcaab-0f1a-4f02-a9cb-61d5b0ddfd36.html +As a convenience, however, beets allows ``$albumartist`` to fall back to the +value for ``$artist`` and vice-versa if one tag is present but the other is not. .. _template-functions: @@ -58,47 +54,46 @@ track's artists. These functions are built in to beets: -* ``%lower{text}``: Convert ``text`` to lowercase. -* ``%upper{text}``: Convert ``text`` to UPPERCASE. -* ``%capitalize{text}``: Make the first letter of ``text`` UPPERCASE and the rest lowercase. -* ``%title{text}``: Convert ``text`` to Title Case. -* ``%left{text,n}``: Return the first ``n`` characters of ``text``. -* ``%right{text,n}``: Return the last ``n`` characters of ``text``. -* ``%if{condition,text}`` or ``%if{condition,truetext,falsetext}``: If - ``condition`` is nonempty (or nonzero, if it's a number), then returns - the second argument. Otherwise, returns the third argument if specified (or +- ``%lower{text}``: Convert ``text`` to lowercase. +- ``%upper{text}``: Convert ``text`` to UPPERCASE. +- ``%capitalize{text}``: Make the first letter of ``text`` UPPERCASE and the + rest lowercase. +- ``%title{text}``: Convert ``text`` to Title Case. +- ``%left{text,n}``: Return the first ``n`` characters of ``text``. +- ``%right{text,n}``: Return the last ``n`` characters of ``text``. +- ``%if{condition,text}`` or ``%if{condition,truetext,falsetext}``: If + ``condition`` is nonempty (or nonzero, if it's a number), then returns the + second argument. Otherwise, returns the third argument if specified (or nothing if ``falsetext`` is left off). -* ``%asciify{text}``: Convert non-ASCII characters to their ASCII equivalents. +- ``%asciify{text}``: Convert non-ASCII characters to their ASCII equivalents. For example, "café" becomes "cafe". Uses the mapping provided by the - `unidecode module`_. See the :ref:`asciify-paths` configuration - option. -* ``%aunique{identifiers,disambiguators,brackets}``: Provides a unique string - to disambiguate similar albums in the database. See :ref:`aunique`, below. -* ``%sunique{identifiers,disambiguators,brackets}``: Similarly, a unique string + `unidecode module`_. See the :ref:`asciify-paths` configuration option. +- ``%aunique{identifiers,disambiguators,brackets}``: Provides a unique string to + disambiguate similar albums in the database. See :ref:`aunique`, below. +- ``%sunique{identifiers,disambiguators,brackets}``: Similarly, a unique string to disambiguate similar singletons in the database. See :ref:`sunique`, below. -* ``%time{date_time,format}``: Return the date and time in any format accepted - by `strftime`_. For example, to get the year some music was added to your +- ``%time{date_time,format}``: Return the date and time in any format accepted + by strftime_. For example, to get the year some music was added to your library, use ``%time{$added,%Y}``. -* ``%first{text}``: Returns the first item, separated by ``;`` (a semicolon - followed by a space). - You can use ``%first{text,count,skip}``, where ``count`` is the number of - items (default 1) and ``skip`` is number to skip (default 0). You can also use - ``%first{text,count,skip,sep,join}`` where ``sep`` is the separator, like - ``;`` or ``/`` and join is the text to concatenate the items. -* ``%ifdef{field}``, ``%ifdef{field,truetext}`` or +- ``%first{text}``: Returns the first item, separated by ``;`` (a semicolon + followed by a space). You can use ``%first{text,count,skip}``, where ``count`` + is the number of items (default 1) and ``skip`` is number to skip (default 0). + You can also use ``%first{text,count,skip,sep,join}`` where ``sep`` is the + separator, like ``;`` or ``/`` and join is the text to concatenate the items. +- ``%ifdef{field}``, ``%ifdef{field,truetext}`` or ``%ifdef{field,truetext,falsetext}``: Checks if an flexible attribute ``field`` is defined. If it exists, then return ``truetext`` or ``field`` (default). Otherwise, returns ``falsetext``. The ``field`` should be entered without ``$``. Note that this doesn't work with built-in :ref:`itemfields`, as they are always defined. -.. _unidecode module: https://pypi.org/project/Unidecode .. _strftime: https://docs.python.org/3/library/time.html#time.strftime +.. _unidecode module: https://pypi.org/project/Unidecode + Plugins can extend beets with more template functions (see :ref:`templ_plugins`). - .. _aunique: Album Disambiguation @@ -113,32 +108,32 @@ disk. The ``aunique`` function detects situations where two albums have some identical fields and emits text from additional fields to disambiguate the albums. For example, if you have both Crystal Castles albums in your library, ``%aunique{}`` -will expand to "[2008]" for one album and "[2010]" for the other. The -function detects that you have two albums with the same artist and title but -that they have different release years. +will expand to "[2008]" for one album and "[2010]" for the other. The function +detects that you have two albums with the same artist and title but that they +have different release years. -For full flexibility, the ``%aunique`` function takes three arguments. The -first two are whitespace-separated lists of album field names: a set of -*identifiers* and a set of *disambiguators*. The third argument is a pair of -characters used to surround the disambiguator. +For full flexibility, the ``%aunique`` function takes three arguments. The first +two are whitespace-separated lists of album field names: a set of *identifiers* +and a set of *disambiguators*. The third argument is a pair of characters used +to surround the disambiguator. Any group of albums with identical values for all the identifiers will be considered "duplicates". Then, the function tries each disambiguator field, -looking for one that distinguishes each of the duplicate albums from each -other. The first such field is used as the result for ``%aunique``. If no field +looking for one that distinguishes each of the duplicate albums from each other. +The first such field is used as the result for ``%aunique``. If no field suffices, an arbitrary number is used to distinguish the two albums. -The default identifiers are ``albumartist album`` and the default -disambiguators are ``albumtype year label catalognum albumdisambig -releasegroupdisambig``. So you can get reasonable disambiguation -behavior if you just use ``%aunique{}`` with no parameters in your -path forms (as in the default path formats), but you can customize the -disambiguation if, for example, you include the year by default in -path formats. +The default identifiers are ``albumartist album`` and the default disambiguators +are ``albumtype year label catalognum albumdisambig releasegroupdisambig``. So +you can get reasonable disambiguation behavior if you just use ``%aunique{}`` +with no parameters in your path forms (as in the default path formats), but you +can customize the disambiguation if, for example, you include the year by +default in path formats. The default characters used as brackets are ``[]``. To change this, provide a -third argument to the ``%aunique`` function consisting of two characters: the left -and right brackets. Or, to turn off bracketing entirely, leave argument blank. +third argument to the ``%aunique`` function consisting of two characters: the +left and right brackets. Or, to turn off bracketing entirely, leave argument +blank. One caveat: When you import an album that is named identically to one already in your library, the *first* album—the one already in your library— will not @@ -154,12 +149,12 @@ Singleton Disambiguation ------------------------ It is also possible to have singleton tracks with the same name and the same -artist. Beets provides the ``%sunique{}`` template to avoid giving these -tracks the same file path. +artist. Beets provides the ``%sunique{}`` template to avoid giving these tracks +the same file path. -It has the same arguments as the :ref:`%aunique <aunique>` template, but the default -values are different. The default identifiers are ``artist title`` and the -default disambiguators are ``year trackdisambig``. +It has the same arguments as the :ref:`%aunique <aunique>` template, but the +default values are different. The default identifiers are ``artist title`` and +the default disambiguators are ``year trackdisambig``. Syntax Details -------------- @@ -168,16 +163,16 @@ The characters ``$``, ``%``, ``{``, ``}``, and ``,`` are "special" in the path template syntax. This means that, for example, if you want a ``%`` character to appear in your paths, you'll need to be careful that you don't accidentally write a function call. To escape any of these characters (except ``{``, and -``,`` outside a function argument), prefix it with a ``$``. For example, -``$$`` becomes ``$``; ``$%`` becomes ``%``, etc. The only exceptions are: +``,`` outside a function argument), prefix it with a ``$``. For example, ``$$`` +becomes ``$``; ``$%`` becomes ``%``, etc. The only exceptions are: -* ``${``, which is ambiguous with the variable reference syntax (like +- ``${``, which is ambiguous with the variable reference syntax (like ``${title}``). To insert a ``{`` alone, it's always sufficient to just type ``{``. You do, however need to use ``$`` to escape a closing brace ``$}``. -* commas are used as argument separators in function calls. Inside of a +- commas are used as argument separators in function calls. Inside of a function's argument, use ``$,`` to get a literal ``,`` character. Outside of - any function argument, escaping is not necessary: ``,`` by itself will - produce ``,`` in the output. + any function argument, escaping is not necessary: ``,`` by itself will produce + ``,`` in the output. If a value or function is undefined, the syntax is simply left unreplaced. For example, if you write ``$foo`` in a path template, this will yield ``$foo`` in @@ -191,7 +186,6 @@ your template. For example, the second parameter to ``%left`` must be an integer; if you write ``%left{foo,bar}``, this will be expanded to something like ``<ValueError: invalid literal for int()>``. - .. _itemfields: Available Values @@ -204,95 +198,95 @@ plugins can add new (or replace existing) template values (see Ordinary metadata: -* title -* artist -* artist_sort: The "sort name" of the track artist (e.g., "Beatles, The" or +- title +- artist +- artist_sort: The "sort name" of the track artist (e.g., "Beatles, The" or "White, Jack"). -* artist_credit: The track-specific `artist credit`_ name, which may be a +- artist_credit: The track-specific `artist credit`_ name, which may be a variation of the artist's "canonical" name. -* album -* albumartist: The artist for the entire album, which may be different from the +- album +- albumartist: The artist for the entire album, which may be different from the artists for the individual tracks. -* albumartist_sort -* albumartist_credit -* genre -* composer -* grouping -* year, month, day: The release date of the specific release. -* original_year, original_month, original_day: The release date of the original +- albumartist_sort +- albumartist_credit +- genre +- composer +- grouping +- year, month, day: The release date of the specific release. +- original_year, original_month, original_day: The release date of the original version of the album. -* track -* tracktotal -* disc -* disctotal -* lyrics -* comments -* bpm -* comp: Compilation flag. -* albumtype: The MusicBrainz album type; the MusicBrainz wiki has a `list of +- track +- tracktotal +- disc +- disctotal +- lyrics +- comments +- bpm +- comp: Compilation flag. +- albumtype: The MusicBrainz album type; the MusicBrainz wiki has a `list of type names`_. -* label -* asin -* catalognum -* script -* language -* country -* albumstatus -* media -* albumdisambig -* disctitle -* encoder +- label +- asin +- catalognum +- script +- language +- country +- albumstatus +- media +- albumdisambig +- disctitle +- encoder .. _artist credit: https://wiki.musicbrainz.org/Artist_Credit + .. _list of type names: https://musicbrainz.org/doc/Release_Group/Type Audio information: -* length (in seconds) -* bitrate (in kilobits per second, with units: e.g., "192kbps") -* bitrate_mode (e.g., "CBR", "VBR" or "ABR", only available for the MP3 format) -* encoder_info (e.g., "LAME 3.97.0", only available for some formats) -* encoder_settings (e.g., "-V2", only available for the MP3 format) -* format (e.g., "MP3" or "FLAC") -* channels -* bitdepth (only available for some formats) -* samplerate (in kilohertz, with units: e.g., "48kHz") +- length (in seconds) +- bitrate (in kilobits per second, with units: e.g., "192kbps") +- bitrate_mode (e.g., "CBR", "VBR" or "ABR", only available for the MP3 format) +- encoder_info (e.g., "LAME 3.97.0", only available for some formats) +- encoder_settings (e.g., "-V2", only available for the MP3 format) +- format (e.g., "MP3" or "FLAC") +- channels +- bitdepth (only available for some formats) +- samplerate (in kilohertz, with units: e.g., "48kHz") MusicBrainz and fingerprint information: -* mb_trackid -* mb_releasetrackid -* mb_albumid -* mb_artistid -* mb_albumartistid -* mb_releasegroupid -* acoustid_fingerprint -* acoustid_id +- mb_trackid +- mb_releasetrackid +- mb_albumid +- mb_artistid +- mb_albumartistid +- mb_releasegroupid +- acoustid_fingerprint +- acoustid_id Library metadata: -* mtime: The modification time of the audio file. -* added: The date and time that the music was added to your library. -* path: The item's filename. - +- mtime: The modification time of the audio file. +- added: The date and time that the music was added to your library. +- path: The item's filename. .. _templ_plugins: Template functions and values provided by plugins ------------------------------------------------- -Beets plugins can provide additional fields and functions to templates. See -the :doc:`/plugins/index` page for a full list of plugins. Some plugin-provided +Beets plugins can provide additional fields and functions to templates. See the +:doc:`/plugins/index` page for a full list of plugins. Some plugin-provided constructs include: -* ``$missing`` by :doc:`/plugins/missing`: The number of missing tracks per +- ``$missing`` by :doc:`/plugins/missing`: The number of missing tracks per album. -* ``%bucket{text}`` by :doc:`/plugins/bucket`: Substitute a string by the - range it belongs to. -* ``%the{text}`` by :doc:`/plugins/the`: Moves English articles to ends of +- ``%bucket{text}`` by :doc:`/plugins/bucket`: Substitute a string by the range + it belongs to. +- ``%the{text}`` by :doc:`/plugins/the`: Moves English articles to ends of strings. The :doc:`/plugins/inline` lets you define template fields in your beets -configuration file using Python snippets. And for more advanced processing, -you can go all-in and write a dedicated plugin to register your own fields and +configuration file using Python snippets. And for more advanced processing, you +can go all-in and write a dedicated plugin to register your own fields and functions (see :ref:`writing-plugins`). diff --git a/docs/reference/query.rst b/docs/reference/query.rst index eaa2d6701..a8d2c4487 100644 --- a/docs/reference/query.rst +++ b/docs/reference/query.rst @@ -13,7 +13,9 @@ search engines. Keyword ------- -This command:: +This command: + +:: $ beet list love @@ -21,7 +23,9 @@ will show all tracks matching the query string ``love``. By default any unadorned word like this matches in a track's title, artist, album name, album artist, genre and comments. See below on how to search other fields. -For example, this is what I might see when I run the command above:: +For example, this is what I might see when I run the command above: + +:: Against Me! - Reinventing Axl Rose - I Still Love You Julie Air - Love 2 - Do the Joy @@ -36,7 +40,9 @@ Combining Keywords Multiple keywords are implicitly joined with a Boolean "and." That is, if a query has two keywords, it only matches tracks that contain *both* keywords. For -example, this command:: +example, this command: + +:: $ beet ls magnetic tomorrow @@ -45,8 +51,10 @@ my library. It *doesn't* match other songs by the Magnetic Fields, nor does it match "Tomorrowland" by Walter Meego---those songs only have *one* of the two keywords I specified. -Keywords can also be joined with a Boolean "or" using a comma. For example, -the command:: +Keywords can also be joined with a Boolean "or" using a comma. For example, the +command: + +:: $ beet ls magnetic tomorrow , beatles yesterday @@ -65,16 +73,22 @@ Just say ``field:value``, where ``field`` is the name of the thing you're trying to match (such as ``artist``, ``album``, or ``title``) and ``value`` is the keyword you're searching for. -For example, while this query:: +For example, while this query: + +:: $ beet list dream -matches a lot of songs in my library, this more-specific query:: +matches a lot of songs in my library, this more-specific query: + +:: $ beet list artist:dream only matches songs by the artist The-Dream. One query I especially appreciate is -one that matches albums by year:: +one that matches albums by year: + +:: $ beet list -a year:2012 @@ -85,11 +99,15 @@ For multi-valued tags (such as ``artists`` or ``albumartists``), a regular expression search must be used to search for a single value within the multi-valued tag. -Note that you can filter albums by querying tracks fields and vice versa:: +Note that you can filter albums by querying tracks fields and vice versa: + +:: $ beet list -a title:love -and vice versa:: +and vice versa: + +:: $ beet list art_path::love @@ -97,11 +115,15 @@ Phrases ------- You can query for strings with spaces in them by quoting or escaping them using -your shell's argument syntax. For example, this command:: +your shell's argument syntax. For example, this command: + +:: $ beet list the rebel -shows several tracks in my library, but these (equivalent) commands:: +shows several tracks in my library, but these (equivalent) commands: + +:: $ beet list "the rebel" $ beet list the\ rebel @@ -118,7 +140,9 @@ Exact Matches While ordinary queries perform *substring* matches, beets can also match whole strings by adding either ``=`` (case-sensitive) or ``=~`` (ignore case) after -the field name's colon and before the expression:: +the field name's colon and before the expression: + +:: $ beet list artist:air $ beet list artist:=~air @@ -130,7 +154,9 @@ case-insensitive match for the entire expression, but does not return anything by Air Supply. The third query, which requires a case-sensitive exact match, returns tracks by AIR only. -Exact matches may be performed on phrases as well:: +Exact matches may be performed on phrases as well: + +:: $ beet list artist:=~"dave matthews" $ beet list artist:="Dave Matthews" @@ -139,7 +165,9 @@ Both of these queries return tracks by Dave Matthews, but not by Dave Matthews Band. To search for exact matches across *all* fields, just prefix the expression with -a single ``=`` or ``=~``:: +a single ``=`` or ``=~``: + +:: $ beet list =~crash $ beet list ="American Football" @@ -151,58 +179,65 @@ Regular Expressions In addition to simple substring and exact matches, beets also supports regular expression matching for more advanced queries. To run a regex query, use an -additional ``:`` between the field name and the expression:: +additional ``:`` between the field name and the expression: + +:: $ beet list "artist::Ann(a|ie)" That query finds songs by Anna Calvi and Annie but not Annuals. Similarly, this -query prints the path to any file in my library that's missing a track title:: +query prints the path to any file in my library that's missing a track title: + +:: $ beet list -p title::^$ To search *all* fields using a regular expression, just prefix the expression -with a single ``:``, like so:: +with a single ``:``, like so: + +:: $ beet list ":Ho[pm]eless" Regular expressions are case-sensitive and build on `Python's built-in -implementation`_. See Python's documentation for specifics on regex syntax. +implementation <https://docs.python.org/library/re.html>`__. See Python's +documentation for specifics on regex syntax. Most command-line shells will try to interpret common characters in regular -expressions, such as ``()[]|``. To type those characters, you'll need to -escape them (e.g., with backslashes or quotation marks, depending on your -shell). - -.. _Python's built-in implementation: https://docs.python.org/library/re.html - +expressions, such as ``()[]|``. To type those characters, you'll need to escape +them (e.g., with backslashes or quotation marks, depending on your shell). .. _numericquery: Numeric Range Queries --------------------- -For numeric fields, such as year, bitrate, and track, you can query using one- -or two-sided intervals. That is, you can find music that falls within a -*range* of values. To use ranges, write a query that has two dots (``..``) at -the beginning, middle, or end of a string of numbers. Dots in the beginning -let you specify a maximum (e.g., ``..7``); dots at the end mean a minimum -(``4..``); dots in the middle mean a range (``4..7``). +For numeric fields, such as year, bitrate, and track, you can query using one-or +two-sided intervals. That is, you can find music that falls within a *range* of +values. To use ranges, write a query that has two dots (``..``) at the +beginning, middle, or end of a string of numbers. Dots in the beginning let you +specify a maximum (e.g., ``..7``); dots at the end mean a minimum (``4..``); +dots in the middle mean a range (``4..7``). -For example, this command finds all your albums that were released in the -'90s:: +For example, this command finds all your albums that were released in the '90s: + +:: $ beet list -a year:1990..1999 -and this command finds MP3 files with bitrates of 128k or lower:: +and this command finds MP3 files with bitrates of 128k or lower: + +:: $ beet list format:MP3 bitrate:..128000 -The ``length`` field also lets you use a "M:SS" format. For example, this -query finds tracks that are less than four and a half minutes in length:: +The ``length`` field also lets you use a "M:SS" format. For example, this query +finds tracks that are less than four and a half minutes in length: + +:: $ beet list length:..4:30 - .. _datequery: Date and Date Range Queries @@ -218,51 +253,66 @@ matches for the whole month. Date *intervals*, like the numeric intervals described above, are separated by two dots (``..``). You can specify a start, an end, or both. -Here is an example that finds all the albums added in 2008:: +Here is an example that finds all the albums added in 2008: + +:: $ beet ls -a 'added:2008' -Find all items added in the years 2008, 2009 and 2010:: +Find all items added in the years 2008, 2009 and 2010: + +:: $ beet ls 'added:2008..2010' -Find all items added before the year 2010:: +Find all items added before the year 2010: + +:: $ beet ls 'added:..2009' -Find all items added on or after 2008-12-01 but before 2009-10-12:: +Find all items added on or after 2008-12-01 but before 2009-10-12: + +:: $ beet ls 'added:2008-12..2009-10-11' -Find all items with a file modification time between 2008-12-01 and -2008-12-03:: +Find all items with a file modification time between 2008-12-01 and 2008-12-03: + +:: $ beet ls 'mtime:2008-12-01..2008-12-02' You can also add an optional time value to date queries, specifying hours, minutes, and seconds. -Times are separated from dates by a space, an uppercase 'T' or a lowercase -'t', for example: ``2008-12-01T23:59:59``. If you specify a time, then the -date must contain a year, month, and day. The minutes and seconds are -optional. +Times are separated from dates by a space, an uppercase 'T' or a lowercase 't', +for example: ``2008-12-01T23:59:59``. If you specify a time, then the date must +contain a year, month, and day. The minutes and seconds are optional. Here is an example that finds all items added on 2008-12-01 at or after 22:00 -but before 23:00:: +but before 23:00: + +:: $ beet ls 'added:2008-12-01T22' -To find all items added on or after 2008-12-01 at 22:45:: +To find all items added on or after 2008-12-01 at 22:45: + +:: $ beet ls 'added:2008-12-01T22:45..' -To find all items added on 2008-12-01, at or after 22:45:20 but before -22:45:41:: +To find all items added on 2008-12-01, at or after 22:45:20 but before 22:45:41: + +:: $ beet ls 'added:2008-12-01T22:45:20..2008-12-01T22:45:40' Here are example of the three ways to separate dates from times. All of these -queries do the same thing:: +queries do the same thing: + +:: $ beet ls 'added:2008-12-01T22:45:20' $ beet ls 'added:2008-12-01t22:45:20' @@ -278,12 +328,16 @@ and ``+4d`` means four days in the future. A relative date has three parts: weeks, months or years. (A "month" is always 30 days and a "year" is always 365 days.) -Here's an example that finds all the albums added since last week:: +Here's an example that finds all the albums added since last week: + +:: $ beet ls -a 'added:-1w..' -And here's an example that lists items added in a two-week period starting -four weeks ago:: +And here's an example that lists items added in a two-week period starting four +weeks ago: + +:: $ beet ls 'added:-6w..-4w' @@ -292,9 +346,11 @@ four weeks ago:: Query Term Negation ------------------- -Query terms can also be negated, acting like a Boolean "not," by prefixing -them with ``-`` or ``^``. This has the effect of returning all the items that -do **not** match the query term. For example, this command:: +Query terms can also be negated, acting like a Boolean "not," by prefixing them +with ``-`` or ``^``. This has the effect of returning all the items that do +**not** match the query term. For example, this command: + +:: $ beet list ^love @@ -302,7 +358,9 @@ matches all the songs in the library that do not have "love" in any of their fields. Negation can be combined with the rest of the query mechanisms, so you can -negate specific fields, regular expressions, etc. For example, this command:: +negate specific fields, regular expressions, etc. For example, this command: + +:: $ beet list -a artist:dylan ^year:1980..1989 "^album::the(y)?" @@ -311,7 +369,9 @@ released in the eighties and those that have "the" or "they" on the title. The syntax supports both ``^`` and ``-`` as synonyms because the latter indicates flags on the command line. To use a minus sign in a command-line -query, use a double dash ``--`` to separate the options from the query:: +query, use a double dash ``--`` to separate the options from the query: + +:: $ beet list -a -- artist:dylan -year:1980..1990 "-album::the(y)?" @@ -321,13 +381,17 @@ Path Queries ------------ Sometimes it's useful to find all the items in your library that are -(recursively) inside a certain directory. Use the ``path:`` field to do this:: +(recursively) inside a certain directory. Use the ``path:`` field to do this: + +:: $ beet list path:/my/music/directory In fact, beets automatically recognizes any query term containing a path separator (``/`` on POSIX systems) as a path query if that path exists, so this -command is equivalent as long as ``/my/music/directory`` exist:: +command is equivalent as long as ``/my/music/directory`` exist: + +:: $ beet list /my/music/directory @@ -343,14 +407,18 @@ filesystem. Sort Order ---------- -Queries can specify a sort order. Use the name of the `field` you want to sort -on, followed by a ``+`` or ``-`` sign to indicate ascending or descending -sort. For example, this command:: +Queries can specify a sort order. Use the name of the ``field`` you want to sort +on, followed by a ``+`` or ``-`` sign to indicate ascending or descending sort. +For example, this command: + +:: $ beet list -a year+ will list all albums in chronological order. You can also specify several sort -orders, which will be used in the same order as they appear in your query:: +orders, which will be used in the same order as they appear in your query: + +:: $ beet list -a genre+ year+ @@ -364,8 +432,8 @@ transparently (but fall back to the ordinary fields when those are empty). Lexicographic sorts are case insensitive by default, resulting in the following sort order: ``Bar foo Qux``. This behavior can be changed with the :ref:`sort_case_insensitive` configuration option. Case sensitive sort will -result in lower-case values being placed after upper-case values, e.g., -``Bar Qux foo``. +result in lower-case values being placed after upper-case values, e.g., ``Bar +Qux foo``. Note that when sorting by fields that are not present on all items (such as flexible fields, or those defined by plugins) in *ascending* order, the items diff --git a/docs/team.rst b/docs/team.rst index eae3ef532..b23b125ce 100644 --- a/docs/team.rst +++ b/docs/team.rst @@ -1,89 +1,90 @@ Team -#### +==== This is an introduction of beets' core-team members, collaborators and frequent -contributors. Refer to this list to find out who to ask about your -collaboration idea, discuss a usage-question, request a review of your open PR. -Beets is a huge project and not everyone involved, knows everything. We hope -this helps to point you in the right direction in the first place and should -give you an idea of what you can expect from these *knowledge owners*. +contributors. Refer to this list to find out who to ask about your collaboration +idea, discuss a usage-question, request a review of your open PR. Beets is a +huge project and not everyone involved, knows everything. We hope this helps to +point you in the right direction in the first place and should give you an idea +of what you can expect from these *knowledge owners*. @arsaboo -======== +-------- -* The master of the Spotify plugin -* Testing out new contributions -* beets as a music discovery tool +- The master of the Spotify plugin +- Testing out new contributions +- beets as a music discovery tool @bal-e -====== +------ -* Documentation -* The Fish plugin -* Type annotations +- Documentation +- The Fish plugin +- Type annotations @govynnus -========= +--------- -* The AURA plugin -* The AURA specification -* The web plugin -* The plugin ecosystem -* The library database API and its documentation +- The AURA plugin +- The AURA specification +- The web plugin +- The plugin ecosystem +- The library database API and its documentation @jackwilsdon -============ -* Broad knowledge around beets' configuration and plugins -* Assists in discussion forums frequently -* Knows internals of beets and puts new contributors into the right direction +------------ + +- Broad knowledge around beets' configuration and plugins +- Assists in discussion forums frequently +- Knows internals of beets and puts new contributors into the right direction @joj0 -===== +----- -* The Discogs plugin -* Other metadata source plugins -* Generalization of source plugin logic (The MetaDataSourcePlugin abstract +- The Discogs plugin +- Other metadata source plugins +- Generalization of source plugin logic (The MetaDataSourcePlugin abstract class) -* Good documentation throughout the project -* The smartplaylist plugin -* Things around m3u and other playlist formats +- Good documentation throughout the project +- The smartplaylist plugin +- Things around m3u and other playlist formats @RollingStar -============ +------------ -* Data visualization -* ListenBrainz / Last.fm -* Smart playlists -* Library reports -* MusicBrainz fields and searching -* Project organization and roadmap +- Data visualization +- ListenBrainz / Last.fm +- Smart playlists +- Library reports +- MusicBrainz fields and searching +- Project organization and roadmap @sampsyo -======== +-------- -* The founder -* Knows almost everything ;-) +- The founder +- Knows almost everything ;-) @serene-arc -=========== +----------- -* Good documentation throughout the project -* Experienced Python developer -* Experienced in test-driven-development -* Code quality -* Typing +- Good documentation throughout the project +- Experienced Python developer +- Experienced in test-driven-development +- Code quality +- Typing @wisp3rwind -=========== +----------- -* Mr. Tidy - Keeping the code in shape -* Focus on improving core things rather than implementing new features +- Mr. Tidy - Keeping the code in shape +- Focus on improving core things rather than implementing new features @ybnd -===== +----- -* The replaygain plugin -* Improving the general parallelism of plugins -* Experienced with web scrapers -* Experienced with Flask and JavaScript integration -* The web plugin +- The replaygain plugin +- Improving the general parallelism of plugins +- Experienced with web scrapers +- Experienced with Flask and JavaScript integration +- The web plugin diff --git a/extra/release.py b/extra/release.py index 16e2e860e..647cc49c9 100755 --- a/extra/release.py +++ b/extra/release.py @@ -105,10 +105,6 @@ def create_rst_replacements() -> list[Replacement]: r.split("/")[-1] for r in refs if r.startswith("plugins/") ) return [ - # Fix nested bullet points indent: use 2 spaces consistently - (r"(?<=\n) {3,4}(?=\*)", " "), - # Fix nested text indent: use 4 spaces consistently - (r"(?<=\n) {5,6}(?=[\w:`])", " "), # Replace Sphinx :ref: and :doc: directives by documentation URLs # :ref:`/plugins/autobpm` -> [AutoBPM Plugin](DOCS/plugins/autobpm.html) ( @@ -124,9 +120,6 @@ def create_rst_replacements() -> list[Replacement]: # Convert plugin references to documentation URLs # `fetchart` plugin -> [fetchart](DOCS/plugins/fetchart.html) (rf"`+({plugins})`+", lambda m: make_ref_link(f"plugins/{m[1]}")), - # Add additional backticks around existing backticked text to ensure it - # is rendered as inline code in Markdown - (r"(?<=[\s])(`[^`]+`)(?!_)", r"`\1`"), # Convert bug references to GitHub issue links (r":bug:`(\d+)`", r":bug: (#\1)"), # Convert user references to GitHub @mentions @@ -135,13 +128,12 @@ def create_rst_replacements() -> list[Replacement]: MD_REPLACEMENTS: list[Replacement] = [ - (r"<span[^>]+>([^<]+)</span>", r"_\1"), # remove a couple of wild span refs (r"^(\w[^\n]{,80}):(?=\n\n[^ ])", r"### \1"), # format section headers (r"^(\w[^\n]{81,}):(?=\n\n[^ ])", r"**\1**"), # and bolden too long ones (r"### [^\n]+\n+(?=### )", ""), # remove empty sections ] order_bullet_points = partial( - re.compile("(\n- .*?(?=\n(?! *- )|$))", flags=re.DOTALL).sub, + re.compile(r"(\n- .*?(?=\n(?! *(-|\d\.) )|$))", flags=re.DOTALL).sub, lambda m: "\n- ".join(sorted(m.group().split("\n- "))), ) diff --git a/poetry.lock b/poetry.lock index 752953e1d..25d9448ba 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,7 +22,7 @@ tests = ["hypothesis", "pytest"] name = "alabaster" version = "0.7.16" description = "A light, configurable Sphinx theme" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, @@ -80,7 +80,7 @@ test = ["tox"] name = "babel" version = "2.17.0" description = "Internationalization utilities" -optional = true +optional = false python-versions = ">=3.8" files = [ {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, @@ -112,6 +112,52 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "blinker" version = "1.9.0" @@ -298,13 +344,13 @@ cffi = ">=1.0.0" [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.7.14" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, - {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, + {file = "certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2"}, + {file = "certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995"}, ] [[package]] @@ -543,74 +589,78 @@ pyyaml = "*" [[package]] name = "coverage" -version = "7.8.0" +version = "7.9.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe"}, - {file = "coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28"}, - {file = "coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3"}, - {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676"}, - {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d"}, - {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a"}, - {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c"}, - {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f"}, - {file = "coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f"}, - {file = "coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23"}, - {file = "coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27"}, - {file = "coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea"}, - {file = "coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7"}, - {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040"}, - {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543"}, - {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2"}, - {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318"}, - {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9"}, - {file = "coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c"}, - {file = "coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78"}, - {file = "coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc"}, - {file = "coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6"}, - {file = "coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d"}, - {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05"}, - {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a"}, - {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6"}, - {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47"}, - {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe"}, - {file = "coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545"}, - {file = "coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b"}, - {file = "coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd"}, - {file = "coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00"}, - {file = "coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64"}, - {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067"}, - {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008"}, - {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733"}, - {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323"}, - {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3"}, - {file = "coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d"}, - {file = "coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487"}, - {file = "coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25"}, - {file = "coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42"}, - {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502"}, - {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1"}, - {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4"}, - {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73"}, - {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a"}, - {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883"}, - {file = "coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada"}, - {file = "coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257"}, - {file = "coverage-7.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f"}, - {file = "coverage-7.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a"}, - {file = "coverage-7.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82"}, - {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814"}, - {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c"}, - {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd"}, - {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4"}, - {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899"}, - {file = "coverage-7.8.0-cp39-cp39-win32.whl", hash = "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f"}, - {file = "coverage-7.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3"}, - {file = "coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd"}, - {file = "coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7"}, - {file = "coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501"}, + {file = "coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912"}, + {file = "coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f"}, + {file = "coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f"}, + {file = "coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf"}, + {file = "coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547"}, + {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45"}, + {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2"}, + {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e"}, + {file = "coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e"}, + {file = "coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c"}, + {file = "coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba"}, + {file = "coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa"}, + {file = "coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a"}, + {file = "coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc"}, + {file = "coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2"}, + {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c"}, + {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd"}, + {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74"}, + {file = "coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6"}, + {file = "coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7"}, + {file = "coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62"}, + {file = "coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0"}, + {file = "coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3"}, + {file = "coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1"}, + {file = "coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615"}, + {file = "coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b"}, + {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9"}, + {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f"}, + {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d"}, + {file = "coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355"}, + {file = "coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0"}, + {file = "coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b"}, + {file = "coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038"}, + {file = "coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d"}, + {file = "coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3"}, + {file = "coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14"}, + {file = "coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6"}, + {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b"}, + {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d"}, + {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868"}, + {file = "coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a"}, + {file = "coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b"}, + {file = "coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694"}, + {file = "coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5"}, + {file = "coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b"}, + {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3"}, + {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8"}, + {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46"}, + {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584"}, + {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e"}, + {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac"}, + {file = "coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926"}, + {file = "coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd"}, + {file = "coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb"}, + {file = "coverage-7.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddc39510ac922a5c4c27849b739f875d3e1d9e590d1e7b64c98dadf037a16cce"}, + {file = "coverage-7.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a535c0c7364acd55229749c2b3e5eebf141865de3a8f697076a3291985f02d30"}, + {file = "coverage-7.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df0f9ef28e0f20c767ccdccfc5ae5f83a6f4a2fbdfbcbcc8487a8a78771168c8"}, + {file = "coverage-7.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f3da12e0ccbcb348969221d29441ac714bbddc4d74e13923d3d5a7a0bebef7a"}, + {file = "coverage-7.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a17eaf46f56ae0f870f14a3cbc2e4632fe3771eab7f687eda1ee59b73d09fe4"}, + {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:669135a9d25df55d1ed56a11bf555f37c922cf08d80799d4f65d77d7d6123fcf"}, + {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9d3a700304d01a627df9db4322dc082a0ce1e8fc74ac238e2af39ced4c083193"}, + {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:71ae8b53855644a0b1579d4041304ddc9995c7b21c8a1f16753c4d8903b4dfed"}, + {file = "coverage-7.9.2-cp39-cp39-win32.whl", hash = "sha256:dd7a57b33b5cf27acb491e890720af45db05589a80c1ffc798462a765be6d4d7"}, + {file = "coverage-7.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f65bb452e579d5540c8b37ec105dd54d8b9307b07bcaa186818c104ffda22441"}, + {file = "coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050"}, + {file = "coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4"}, + {file = "coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b"}, ] [package.dependencies] @@ -645,14 +695,42 @@ files = [ ] [[package]] -name = "docutils" -version = "0.21.2" -description = "Docutils -- Python Documentation Utilities" -optional = true -python-versions = ">=3.9" +name = "docstrfmt" +version = "1.10.0" +description = "docstrfmt: A formatter for Sphinx flavored reStructuredText." +optional = false +python-versions = "<4,>=3.8" files = [ - {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, - {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, + {file = "docstrfmt-1.10.0-py3-none-any.whl", hash = "sha256:a34ef6f3d8ab3233a7d0b3d1c2f3c66f8acbb3917df5ed2f3e34c1629ac29cef"}, + {file = "docstrfmt-1.10.0.tar.gz", hash = "sha256:9da96e71552937f4b49ae2d6ab1c118ffa8ad6968082e6b8fd978b01d1bc0066"}, +] + +[package.dependencies] +black = "==24.*" +click = "==8.*" +docutils = "==0.20.*" +libcst = "==1.*" +platformdirs = "==4.*" +sphinx = ">=7,<9" +tabulate = "==0.9.*" +toml = "==0.10.*" + +[package.extras] +ci = ["coveralls"] +d = ["aiohttp (==3.*)"] +dev = ["docstrfmt[lint]", "docstrfmt[test]", "packaging"] +lint = ["pre-commit", "ruff (>=0.0.292)"] +test = ["pytest", "pytest-aiohttp"] + +[[package]] +name = "docutils" +version = "0.20.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] @@ -709,13 +787,13 @@ dotenv = ["python-dotenv"] [[package]] name = "flask-cors" -version = "5.0.1" +version = "6.0.1" description = "A Flask extension simplifying CORS support" optional = true python-versions = "<4.0,>=3.9" files = [ - {file = "flask_cors-5.0.1-py3-none-any.whl", hash = "sha256:fa5cb364ead54bbf401a26dbf03030c6b18fb2fcaf70408096a572b409586b0c"}, - {file = "flask_cors-5.0.1.tar.gz", hash = "sha256:6ccb38d16d6b72bbc156c1c3f192bc435bfcc3c2bc864b2df1eb9b2d97b2403c"}, + {file = "flask_cors-6.0.1-py3-none-any.whl", hash = "sha256:c7b2cbfb1a31aa0d2e5341eea03a6805349f7a61647daee1a15c46bbe981494c"}, + {file = "flask_cors-6.0.1.tar.gz", hash = "sha256:d81bcb31f07b0985be7f48406247e9243aced229b7747219160a0559edd678db"}, ] [package.dependencies] @@ -807,7 +885,7 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" -optional = true +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, @@ -1017,13 +1095,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "joblib" -version = "1.5.0" +version = "1.5.1" description = "Lightweight pipelining with Python functions" optional = true python-versions = ">=3.9" files = [ - {file = "joblib-1.5.0-py3-none-any.whl", hash = "sha256:206144b320246485b712fc8cc51f017de58225fa8b414a1fe1764a7231aca491"}, - {file = "joblib-1.5.0.tar.gz", hash = "sha256:d8757f955389a3dd7a23152e43bc297c2e0c2d3060056dad0feefc88a06939b5"}, + {file = "joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a"}, + {file = "joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444"}, ] [[package]] @@ -1125,6 +1203,81 @@ dev = ["changelist (==0.5)"] lint = ["pre-commit (==3.7.0)"] test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"] +[[package]] +name = "libcst" +version = "1.8.2" +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." +optional = false +python-versions = ">=3.9" +files = [ + {file = "libcst-1.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:67d9720d91f507c87b3e5f070627ad640a00bc6cfdf5635f8c6ee9f2964cf71c"}, + {file = "libcst-1.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:94b7c032b72566077614a02baab1929739fd0af0cc1d46deaba4408b870faef2"}, + {file = "libcst-1.8.2-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:11ea148902e3e1688afa392087c728ac3a843e54a87d334d1464d2097d3debb7"}, + {file = "libcst-1.8.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:22c9473a2cc53faabcc95a0ac6ca4e52d127017bf34ba9bc0f8e472e44f7b38e"}, + {file = "libcst-1.8.2-cp310-cp310-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b5269b96367e65793a7714608f6d906418eb056d59eaac9bba980486aabddbed"}, + {file = "libcst-1.8.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d20e932ddd9a389da57b060c26e84a24118c96ff6fc5dcc7b784da24e823b694"}, + {file = "libcst-1.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a553d452004e44b841788f6faa7231a02157527ddecc89dbbe5b689b74822226"}, + {file = "libcst-1.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe762c4c390039b79b818cbc725d8663586b25351dc18a2704b0e357d69b924"}, + {file = "libcst-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:5c513e64eff0f7bf2a908e2d987a98653eb33e1062ce2afd3a84af58159a24f9"}, + {file = "libcst-1.8.2-cp310-cp310-win_arm64.whl", hash = "sha256:41613fe08e647213546c7c59a5a1fc5484666e7d4cab6e80260c612acbb20e8c"}, + {file = "libcst-1.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:688a03bac4dfb9afc5078ec01d53c21556381282bdf1a804dd0dbafb5056de2a"}, + {file = "libcst-1.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c34060ff2991707c710250463ae9f415ebb21653f2f5b013c61c9c376ff9b715"}, + {file = "libcst-1.8.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f54f5c4176d60e7cd6b0880e18fb3fa8501ae046069151721cab457c7c538a3d"}, + {file = "libcst-1.8.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d11992561de0ad29ec2800230fbdcbef9efaa02805d5c633a73ab3cf2ba51bf1"}, + {file = "libcst-1.8.2-cp311-cp311-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fa3b807c2d2b34397c135d19ad6abb20c47a2ddb7bf65d90455f2040f7797e1e"}, + {file = "libcst-1.8.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:b0110140738be1287e3724080a101e7cec6ae708008b7650c9d8a1c1788ec03a"}, + {file = "libcst-1.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a50618f4819a97ef897e055ac7aaf1cad5df84c206f33be35b0759d671574197"}, + {file = "libcst-1.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e9bb599c175dc34a4511f0e26d5b5374fbcc91ea338871701a519e95d52f3c28"}, + {file = "libcst-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:96e2363e1f6e44bd7256bbbf3a53140743f821b5133046e6185491e0d9183447"}, + {file = "libcst-1.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:f5391d71bd7e9e6c73dcb3ee8d8c63b09efc14ce6e4dad31568d4838afc9aae0"}, + {file = "libcst-1.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2e8c1dfa854e700fcf6cd79b2796aa37d55697a74646daf5ea47c7c764bac31c"}, + {file = "libcst-1.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b5c57a3c1976c365678eb0730bcb140d40510990cb77df9a91bb5c41d587ba6"}, + {file = "libcst-1.8.2-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:0f23409add2aaebbb6d8e881babab43c2d979f051b8bd8aed5fe779ea180a4e8"}, + {file = "libcst-1.8.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b88e9104c456590ad0ef0e82851d4fc03e9aa9d621fa8fdd4cd0907152a825ae"}, + {file = "libcst-1.8.2-cp312-cp312-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5ba3ea570c8fb6fc44f71aa329edc7c668e2909311913123d0d7ab8c65fc357"}, + {file = "libcst-1.8.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:460fcf3562f078781e1504983cb11909eb27a1d46eaa99e65c4b0fafdc298298"}, + {file = "libcst-1.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1381ddbd1066d543e05d580c15beacf671e1469a0b2adb6dba58fec311f4eed"}, + {file = "libcst-1.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a70e40ce7600e1b32e293bb9157e9de3b69170e2318ccb219102f1abb826c94a"}, + {file = "libcst-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:3ece08ba778b6eeea74d9c705e9af2d1b4e915e9bc6de67ad173b962e575fcc0"}, + {file = "libcst-1.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:5efd1bf6ee5840d1b0b82ec8e0b9c64f182fa5a7c8aad680fbd918c4fa3826e0"}, + {file = "libcst-1.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08e9dca4ab6f8551794ce7ec146f86def6a82da41750cbed2c07551345fa10d3"}, + {file = "libcst-1.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8310521f2ccb79b5c4345750d475b88afa37bad930ab5554735f85ad5e3add30"}, + {file = "libcst-1.8.2-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:da2d8b008aff72acd5a4a588491abdda1b446f17508e700f26df9be80d8442ae"}, + {file = "libcst-1.8.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:be821d874ce8b26cbadd7277fa251a9b37f6d2326f8b5682b6fc8966b50a3a59"}, + {file = "libcst-1.8.2-cp313-cp313-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f74b0bc7378ad5afcf25ac9d0367b4dbba50f6f6468faa41f5dfddcf8bf9c0f8"}, + {file = "libcst-1.8.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b68ea4a6018abfea1f68d50f74de7d399172684c264eb09809023e2c8696fc23"}, + {file = "libcst-1.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e264307ec49b2c72480422abafe80457f90b4e6e693b7ddf8a23d24b5c24001"}, + {file = "libcst-1.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5d5519962ce7c72d81888fb0c09e58e308ba4c376e76bcd853b48151063d6a8"}, + {file = "libcst-1.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:b62aa11d6b74ed5545e58ac613d3f63095e5fd0254b3e0d1168fda991b9a6b41"}, + {file = "libcst-1.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9c2bd4ac288a9cdb7ffc3229a9ce8027a66a3fd3f2ab9e13da60f5fbfe91f3b2"}, + {file = "libcst-1.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:08a8c7d9922ca6eed24e2c13a3c552b3c186af8fc78e5d4820b58487d780ec19"}, + {file = "libcst-1.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bba7c2b5063e8ada5a5477f9fa0c01710645426b5a8628ec50d558542a0a292e"}, + {file = "libcst-1.8.2-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d97c9fe13aacfbefded6861f5200dcb8e837da7391a9bdeb44ccb133705990af"}, + {file = "libcst-1.8.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d2194ae959630aae4176a4b75bd320b3274c20bef2a5ca6b8d6fc96d3c608edf"}, + {file = "libcst-1.8.2-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0be639f5b2e1999a4b4a82a0f4633969f97336f052d0c131627983589af52f56"}, + {file = "libcst-1.8.2-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6753e50904e05c27915933da41518ecd7a8ca4dd3602112ba44920c6e353a455"}, + {file = "libcst-1.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:706d07106af91c343150be86caeae1ea3851b74aa0730fcbbf8cd089e817f818"}, + {file = "libcst-1.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd4310ea8ddc49cc8872e083737cf806299b17f93159a1f354d59aa08993e876"}, + {file = "libcst-1.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:51bbafdd847529e8a16d1965814ed17831af61452ee31943c414cb23451de926"}, + {file = "libcst-1.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:4f14f5045766646ed9e8826b959c6d07194788babed1e0ba08c94ea4f39517e3"}, + {file = "libcst-1.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f69582e24667715e3860d80d663f1caeb2398110077e23cc0a1e0066a851f5ab"}, + {file = "libcst-1.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ba85f9e6a7f37ef998168aa3fd28d263d7f83016bd306a4508a2394e5e793b4"}, + {file = "libcst-1.8.2-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:43ccaa6c54daa1749cec53710c70d47150965574d4c6d4c4f2e3f87b9bf9f591"}, + {file = "libcst-1.8.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8a81d816c2088d2055112af5ecd82fdfbe8ff277600e94255e2639b07de10234"}, + {file = "libcst-1.8.2-cp39-cp39-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:449f9ff8a5025dcd5c8d4ad28f6c291de5de89e4c044b0bda96b45bef8999b75"}, + {file = "libcst-1.8.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:36d5ab95f39f855521585b0e819dc2d4d1b2a4080bad04c2f3de1e387a5d2233"}, + {file = "libcst-1.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:207575dec2dae722acf6ab39b4b361151c65f8f895fd37edf9d384f5541562e1"}, + {file = "libcst-1.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:52a1067cf31d9e9e4be514b253bea6276f1531dd7de6ab0917df8ce5b468a820"}, + {file = "libcst-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:59e8f611c977206eba294c296c2d29a1c1b1b88206cb97cd0d4847c1a3d923e7"}, + {file = "libcst-1.8.2-cp39-cp39-win_arm64.whl", hash = "sha256:ae22376633cfa3db21c4eed2870d1c36b5419289975a41a45f34a085b2d9e6ea"}, + {file = "libcst-1.8.2.tar.gz", hash = "sha256:66e82cedba95a6176194a817be4232c720312f8be6d2c8f3847f3317d95a0c7f"}, +] + +[package.dependencies] +pyyaml = {version = ">=5.2", markers = "python_version < \"3.13\""} +pyyaml-ft = {version = ">=8.0.0", markers = "python_version >= \"3.13\""} +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + [[package]] name = "librosa" version = "0.10.2.post1" @@ -1188,143 +1341,105 @@ files = [ [[package]] name = "lxml" -version = "5.4.0" +version = "6.0.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = true -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "lxml-5.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e7bc6df34d42322c5289e37e9971d6ed114e3776b45fa879f734bded9d1fea9c"}, - {file = "lxml-5.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6854f8bd8a1536f8a1d9a3655e6354faa6406621cf857dc27b681b69860645c7"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:696ea9e87442467819ac22394ca36cb3d01848dad1be6fac3fb612d3bd5a12cf"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ef80aeac414f33c24b3815ecd560cee272786c3adfa5f31316d8b349bfade28"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b9c2754cef6963f3408ab381ea55f47dabc6f78f4b8ebb0f0b25cf1ac1f7609"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a62cc23d754bb449d63ff35334acc9f5c02e6dae830d78dab4dd12b78a524f4"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f82125bc7203c5ae8633a7d5d20bcfdff0ba33e436e4ab0abc026a53a8960b7"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b67319b4aef1a6c56576ff544b67a2a6fbd7eaee485b241cabf53115e8908b8f"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:a8ef956fce64c8551221f395ba21d0724fed6b9b6242ca4f2f7beb4ce2f41997"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:0a01ce7d8479dce84fc03324e3b0c9c90b1ece9a9bb6a1b6c9025e7e4520e78c"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:91505d3ddebf268bb1588eb0f63821f738d20e1e7f05d3c647a5ca900288760b"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a3bcdde35d82ff385f4ede021df801b5c4a5bcdfb61ea87caabcebfc4945dc1b"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aea7c06667b987787c7d1f5e1dfcd70419b711cdb47d6b4bb4ad4b76777a0563"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a7fb111eef4d05909b82152721a59c1b14d0f365e2be4c742a473c5d7372f4f5"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43d549b876ce64aa18b2328faff70f5877f8c6dede415f80a2f799d31644d776"}, - {file = "lxml-5.4.0-cp310-cp310-win32.whl", hash = "sha256:75133890e40d229d6c5837b0312abbe5bac1c342452cf0e12523477cd3aa21e7"}, - {file = "lxml-5.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:de5b4e1088523e2b6f730d0509a9a813355b7f5659d70eb4f319c76beea2e250"}, - {file = "lxml-5.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98a3912194c079ef37e716ed228ae0dcb960992100461b704aea4e93af6b0bb9"}, - {file = "lxml-5.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ea0252b51d296a75f6118ed0d8696888e7403408ad42345d7dfd0d1e93309a7"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92b69441d1bd39f4940f9eadfa417a25862242ca2c396b406f9272ef09cdcaa"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20e16c08254b9b6466526bc1828d9370ee6c0d60a4b64836bc3ac2917d1e16df"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7605c1c32c3d6e8c990dd28a0970a3cbbf1429d5b92279e37fda05fb0c92190e"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecf4c4b83f1ab3d5a7ace10bafcb6f11df6156857a3c418244cef41ca9fa3e44"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cef4feae82709eed352cd7e97ae062ef6ae9c7b5dbe3663f104cd2c0e8d94ba"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:df53330a3bff250f10472ce96a9af28628ff1f4efc51ccba351a8820bca2a8ba"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:aefe1a7cb852fa61150fcb21a8c8fcea7b58c4cb11fbe59c97a0a4b31cae3c8c"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ef5a7178fcc73b7d8c07229e89f8eb45b2908a9238eb90dcfc46571ccf0383b8"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d2ed1b3cb9ff1c10e6e8b00941bb2e5bb568b307bfc6b17dffbbe8be5eecba86"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:72ac9762a9f8ce74c9eed4a4e74306f2f18613a6b71fa065495a67ac227b3056"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f5cb182f6396706dc6cc1896dd02b1c889d644c081b0cdec38747573db88a7d7"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3a3178b4873df8ef9457a4875703488eb1622632a9cee6d76464b60e90adbfcd"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e094ec83694b59d263802ed03a8384594fcce477ce484b0cbcd0008a211ca751"}, - {file = "lxml-5.4.0-cp311-cp311-win32.whl", hash = "sha256:4329422de653cdb2b72afa39b0aa04252fca9071550044904b2e7036d9d97fe4"}, - {file = "lxml-5.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd3be6481ef54b8cfd0e1e953323b7aa9d9789b94842d0e5b142ef4bb7999539"}, - {file = "lxml-5.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5aff6f3e818e6bdbbb38e5967520f174b18f539c2b9de867b1e7fde6f8d95a4"}, - {file = "lxml-5.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942a5d73f739ad7c452bf739a62a0f83e2578afd6b8e5406308731f4ce78b16d"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:460508a4b07364d6abf53acaa0a90b6d370fafde5693ef37602566613a9b0779"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529024ab3a505fed78fe3cc5ddc079464e709f6c892733e3f5842007cec8ac6e"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ca56ebc2c474e8f3d5761debfd9283b8b18c76c4fc0967b74aeafba1f5647f9"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a81e1196f0a5b4167a8dafe3a66aa67c4addac1b22dc47947abd5d5c7a3f24b5"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b8686694423ddae324cf614e1b9659c2edb754de617703c3d29ff568448df5"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c5681160758d3f6ac5b4fea370495c48aac0989d6a0f01bb9a72ad8ef5ab75c4"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:2dc191e60425ad70e75a68c9fd90ab284df64d9cd410ba8d2b641c0c45bc006e"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:67f779374c6b9753ae0a0195a892a1c234ce8416e4448fe1e9f34746482070a7"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:79d5bfa9c1b455336f52343130b2067164040604e41f6dc4d8313867ed540079"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d3c30ba1c9b48c68489dc1829a6eede9873f52edca1dda900066542528d6b20"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1af80c6316ae68aded77e91cd9d80648f7dd40406cef73df841aa3c36f6907c8"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4d885698f5019abe0de3d352caf9466d5de2baded00a06ef3f1216c1a58ae78f"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea53d51859b6c64e7c51d522c03cc2c48b9b5d6172126854cc7f01aa11f52bc"}, - {file = "lxml-5.4.0-cp312-cp312-win32.whl", hash = "sha256:d90b729fd2732df28130c064aac9bb8aff14ba20baa4aee7bd0795ff1187545f"}, - {file = "lxml-5.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1dc4ca99e89c335a7ed47d38964abcb36c5910790f9bd106f2a8fa2ee0b909d2"}, - {file = "lxml-5.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:773e27b62920199c6197130632c18fb7ead3257fce1ffb7d286912e56ddb79e0"}, - {file = "lxml-5.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ce9c671845de9699904b1e9df95acfe8dfc183f2310f163cdaa91a3535af95de"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9454b8d8200ec99a224df8854786262b1bd6461f4280064c807303c642c05e76"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cccd007d5c95279e529c146d095f1d39ac05139de26c098166c4beb9374b0f4d"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fce1294a0497edb034cb416ad3e77ecc89b313cff7adbee5334e4dc0d11f422"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24974f774f3a78ac12b95e3a20ef0931795ff04dbb16db81a90c37f589819551"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:497cab4d8254c2a90bf988f162ace2ddbfdd806fce3bda3f581b9d24c852e03c"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:e794f698ae4c5084414efea0f5cc9f4ac562ec02d66e1484ff822ef97c2cadff"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:2c62891b1ea3094bb12097822b3d44b93fc6c325f2043c4d2736a8ff09e65f60"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:142accb3e4d1edae4b392bd165a9abdee8a3c432a2cca193df995bc3886249c8"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1a42b3a19346e5601d1b8296ff6ef3d76038058f311902edd574461e9c036982"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4291d3c409a17febf817259cb37bc62cb7eb398bcc95c1356947e2871911ae61"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4f5322cf38fe0e21c2d73901abf68e6329dc02a4994e483adbcf92b568a09a54"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0be91891bdb06ebe65122aa6bf3fc94489960cf7e03033c6f83a90863b23c58b"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:15a665ad90054a3d4f397bc40f73948d48e36e4c09f9bcffc7d90c87410e478a"}, - {file = "lxml-5.4.0-cp313-cp313-win32.whl", hash = "sha256:d5663bc1b471c79f5c833cffbc9b87d7bf13f87e055a5c86c363ccd2348d7e82"}, - {file = "lxml-5.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:bcb7a1096b4b6b24ce1ac24d4942ad98f983cd3810f9711bcd0293f43a9d8b9f"}, - {file = "lxml-5.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7be701c24e7f843e6788353c055d806e8bd8466b52907bafe5d13ec6a6dbaecd"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb54f7c6bafaa808f27166569b1511fc42701a7713858dddc08afdde9746849e"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97dac543661e84a284502e0cf8a67b5c711b0ad5fb661d1bd505c02f8cf716d7"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:c70e93fba207106cb16bf852e421c37bbded92acd5964390aad07cb50d60f5cf"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9c886b481aefdf818ad44846145f6eaf373a20d200b5ce1a5c8e1bc2d8745410"}, - {file = "lxml-5.4.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:fa0e294046de09acd6146be0ed6727d1f42ded4ce3ea1e9a19c11b6774eea27c"}, - {file = "lxml-5.4.0-cp36-cp36m-win32.whl", hash = "sha256:61c7bbf432f09ee44b1ccaa24896d21075e533cd01477966a5ff5a71d88b2f56"}, - {file = "lxml-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ce1a171ec325192c6a636b64c94418e71a1964f56d002cc28122fceff0b6121"}, - {file = "lxml-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:795f61bcaf8770e1b37eec24edf9771b307df3af74d1d6f27d812e15a9ff3872"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29f451a4b614a7b5b6c2e043d7b64a15bd8304d7e767055e8ab68387a8cacf4e"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f7f991a68d20c75cb13c5c9142b2a3f9eb161f1f12a9489c82172d1f133c0"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4aa412a82e460571fad592d0f93ce9935a20090029ba08eca05c614f99b0cc92"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:ac7ba71f9561cd7d7b55e1ea5511543c0282e2b6450f122672a2694621d63b7e"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:c5d32f5284012deaccd37da1e2cd42f081feaa76981f0eaa474351b68df813c5"}, - {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:ce31158630a6ac85bddd6b830cffd46085ff90498b397bd0a259f59d27a12188"}, - {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:31e63621e073e04697c1b2d23fcb89991790eef370ec37ce4d5d469f40924ed6"}, - {file = "lxml-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:be2ba4c3c5b7900246a8f866580700ef0d538f2ca32535e991027bdaba944063"}, - {file = "lxml-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:09846782b1ef650b321484ad429217f5154da4d6e786636c38e434fa32e94e49"}, - {file = "lxml-5.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eaf24066ad0b30917186420d51e2e3edf4b0e2ea68d8cd885b14dc8afdcf6556"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b31a3a77501d86d8ade128abb01082724c0dfd9524f542f2f07d693c9f1175f"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e108352e203c7afd0eb91d782582f00a0b16a948d204d4dec8565024fafeea5"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11a96c3b3f7551c8a8109aa65e8594e551d5a84c76bf950da33d0fb6dfafab7"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:ca755eebf0d9e62d6cb013f1261e510317a41bf4650f22963474a663fdfe02aa"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:4cd915c0fb1bed47b5e6d6edd424ac25856252f09120e3e8ba5154b6b921860e"}, - {file = "lxml-5.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:226046e386556a45ebc787871d6d2467b32c37ce76c2680f5c608e25823ffc84"}, - {file = "lxml-5.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b108134b9667bcd71236c5a02aad5ddd073e372fb5d48ea74853e009fe38acb6"}, - {file = "lxml-5.4.0-cp38-cp38-win32.whl", hash = "sha256:1320091caa89805df7dcb9e908add28166113dcd062590668514dbd510798c88"}, - {file = "lxml-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:073eb6dcdf1f587d9b88c8c93528b57eccda40209cf9be549d469b942b41d70b"}, - {file = "lxml-5.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bda3ea44c39eb74e2488297bb39d47186ed01342f0022c8ff407c250ac3f498e"}, - {file = "lxml-5.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9ceaf423b50ecfc23ca00b7f50b64baba85fb3fb91c53e2c9d00bc86150c7e40"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:664cdc733bc87449fe781dbb1f309090966c11cc0c0cd7b84af956a02a8a4729"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67ed8a40665b84d161bae3181aa2763beea3747f748bca5874b4af4d75998f87"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4a3bd174cc9cdaa1afbc4620c049038b441d6ba07629d89a83b408e54c35cd"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:b0989737a3ba6cf2a16efb857fb0dfa20bc5c542737fddb6d893fde48be45433"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:dc0af80267edc68adf85f2a5d9be1cdf062f973db6790c1d065e45025fa26140"}, - {file = "lxml-5.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:639978bccb04c42677db43c79bdaa23785dc7f9b83bfd87570da8207872f1ce5"}, - {file = "lxml-5.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a99d86351f9c15e4a901fc56404b485b1462039db59288b203f8c629260a142"}, - {file = "lxml-5.4.0-cp39-cp39-win32.whl", hash = "sha256:3e6d5557989cdc3ebb5302bbdc42b439733a841891762ded9514e74f60319ad6"}, - {file = "lxml-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:a8c9b7f16b63e65bbba889acb436a1034a82d34fa09752d754f88d708eca80e1"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1b717b00a71b901b4667226bba282dd462c42ccf618ade12f9ba3674e1fabc55"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27a9ded0f0b52098ff89dd4c418325b987feed2ea5cc86e8860b0f844285d740"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7ce10634113651d6f383aa712a194179dcd496bd8c41e191cec2099fa09de5"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53370c26500d22b45182f98847243efb518d268374a9570409d2e2276232fd37"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c6364038c519dffdbe07e3cf42e6a7f8b90c275d4d1617a69bb59734c1a2d571"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b12cb6527599808ada9eb2cd6e0e7d3d8f13fe7bbb01c6311255a15ded4c7ab4"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5f11a1526ebd0dee85e7b1e39e39a0cc0d9d03fb527f56d8457f6df48a10dc0c"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b4afaf38bf79109bb060d9016fad014a9a48fb244e11b94f74ae366a64d252"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de6f6bb8a7840c7bf216fb83eec4e2f79f7325eca8858167b68708b929ab2172"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5cca36a194a4eb4e2ed6be36923d3cffd03dcdf477515dea687185506583d4c9"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b7c86884ad23d61b025989d99bfdd92a7351de956e01c61307cb87035960bcb1"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:53d9469ab5460402c19553b56c3648746774ecd0681b1b27ea74d5d8a3ef5590"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:56dbdbab0551532bb26c19c914848d7251d73edb507c3079d6805fa8bba5b706"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14479c2ad1cb08b62bb941ba8e0e05938524ee3c3114644df905d2331c76cd57"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32697d2ea994e0db19c1df9e40275ffe84973e4232b5c274f47e7c1ec9763cdd"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:24f6df5f24fc3385f622c0c9d63fe34604893bc1a5bdbb2dbf5870f85f9a404a"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:151d6c40bc9db11e960619d2bf2ec5829f0aaffb10b41dcf6ad2ce0f3c0b2325"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4025bf2884ac4370a3243c5aa8d66d3cb9e15d3ddd0af2d796eccc5f0244390e"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9459e6892f59ecea2e2584ee1058f5d8f629446eab52ba2305ae13a32a059530"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47fb24cc0f052f0576ea382872b3fc7e1f7e3028e53299ea751839418ade92a6"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50441c9de951a153c698b9b99992e806b71c1f36d14b154592580ff4a9d0d877"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ab339536aa798b1e17750733663d272038bf28069761d5be57cb4a9b0137b4f8"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9776af1aad5a4b4a1317242ee2bea51da54b2a7b7b48674be736d463c999f37d"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:63e7968ff83da2eb6fdda967483a7a023aa497d85ad8f05c3ad9b1f2e8c84987"}, - {file = "lxml-5.4.0.tar.gz", hash = "sha256:d12832e1dbea4be280b22fd0ea7c9b87f0d8fc51ba06e92dc62d52f804f78ebd"}, + {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35bc626eec405f745199200ccb5c6b36f202675d204aa29bb52e27ba2b71dea8"}, + {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:246b40f8a4aec341cbbf52617cad8ab7c888d944bfe12a6abd2b1f6cfb6f6082"}, + {file = "lxml-6.0.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2793a627e95d119e9f1e19720730472f5543a6d84c50ea33313ce328d870f2dd"}, + {file = "lxml-6.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:46b9ed911f36bfeb6338e0b482e7fe7c27d362c52fde29f221fddbc9ee2227e7"}, + {file = "lxml-6.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b4790b558bee331a933e08883c423f65bbcd07e278f91b2272489e31ab1e2b4"}, + {file = "lxml-6.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2030956cf4886b10be9a0285c6802e078ec2391e1dd7ff3eb509c2c95a69b76"}, + {file = "lxml-6.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d23854ecf381ab1facc8f353dcd9adeddef3652268ee75297c1164c987c11dc"}, + {file = "lxml-6.0.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:43fe5af2d590bf4691531b1d9a2495d7aab2090547eaacd224a3afec95706d76"}, + {file = "lxml-6.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74e748012f8c19b47f7d6321ac929a9a94ee92ef12bc4298c47e8b7219b26541"}, + {file = "lxml-6.0.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:43cfbb7db02b30ad3926e8fceaef260ba2fb7df787e38fa2df890c1ca7966c3b"}, + {file = "lxml-6.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34190a1ec4f1e84af256495436b2d196529c3f2094f0af80202947567fdbf2e7"}, + {file = "lxml-6.0.0-cp310-cp310-win32.whl", hash = "sha256:5967fe415b1920a3877a4195e9a2b779249630ee49ece22021c690320ff07452"}, + {file = "lxml-6.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:f3389924581d9a770c6caa4df4e74b606180869043b9073e2cec324bad6e306e"}, + {file = "lxml-6.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:522fe7abb41309e9543b0d9b8b434f2b630c5fdaf6482bee642b34c8c70079c8"}, + {file = "lxml-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ee56288d0df919e4aac43b539dd0e34bb55d6a12a6562038e8d6f3ed07f9e36"}, + {file = "lxml-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8dd6dd0e9c1992613ccda2bcb74fc9d49159dbe0f0ca4753f37527749885c25"}, + {file = "lxml-6.0.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d7ae472f74afcc47320238b5dbfd363aba111a525943c8a34a1b657c6be934c3"}, + {file = "lxml-6.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5592401cdf3dc682194727c1ddaa8aa0f3ddc57ca64fd03226a430b955eab6f6"}, + {file = "lxml-6.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58ffd35bd5425c3c3b9692d078bf7ab851441434531a7e517c4984d5634cd65b"}, + {file = "lxml-6.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f720a14aa102a38907c6d5030e3d66b3b680c3e6f6bc95473931ea3c00c59967"}, + {file = "lxml-6.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a5e8d207311a0170aca0eb6b160af91adc29ec121832e4ac151a57743a1e1e"}, + {file = "lxml-6.0.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:2dd1cc3ea7e60bfb31ff32cafe07e24839df573a5e7c2d33304082a5019bcd58"}, + {file = "lxml-6.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cfcf84f1defed7e5798ef4f88aa25fcc52d279be731ce904789aa7ccfb7e8d2"}, + {file = "lxml-6.0.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a52a4704811e2623b0324a18d41ad4b9fabf43ce5ff99b14e40a520e2190c851"}, + {file = "lxml-6.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c16304bba98f48a28ae10e32a8e75c349dd742c45156f297e16eeb1ba9287a1f"}, + {file = "lxml-6.0.0-cp311-cp311-win32.whl", hash = "sha256:f8d19565ae3eb956d84da3ef367aa7def14a2735d05bd275cd54c0301f0d0d6c"}, + {file = "lxml-6.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b2d71cdefda9424adff9a3607ba5bbfc60ee972d73c21c7e3c19e71037574816"}, + {file = "lxml-6.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:8a2e76efbf8772add72d002d67a4c3d0958638696f541734304c7f28217a9cab"}, + {file = "lxml-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78718d8454a6e928470d511bf8ac93f469283a45c354995f7d19e77292f26108"}, + {file = "lxml-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:84ef591495ffd3f9dcabffd6391db7bb70d7230b5c35ef5148354a134f56f2be"}, + {file = "lxml-6.0.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2930aa001a3776c3e2601cb8e0a15d21b8270528d89cc308be4843ade546b9ab"}, + {file = "lxml-6.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:219e0431ea8006e15005767f0351e3f7f9143e793e58519dc97fe9e07fae5563"}, + {file = "lxml-6.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bd5913b4972681ffc9718bc2d4c53cde39ef81415e1671ff93e9aa30b46595e7"}, + {file = "lxml-6.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:390240baeb9f415a82eefc2e13285016f9c8b5ad71ec80574ae8fa9605093cd7"}, + {file = "lxml-6.0.0-cp312-cp312-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d6e200909a119626744dd81bae409fc44134389e03fbf1d68ed2a55a2fb10991"}, + {file = "lxml-6.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca50bd612438258a91b5b3788c6621c1f05c8c478e7951899f492be42defc0da"}, + {file = "lxml-6.0.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:c24b8efd9c0f62bad0439283c2c795ef916c5a6b75f03c17799775c7ae3c0c9e"}, + {file = "lxml-6.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afd27d8629ae94c5d863e32ab0e1d5590371d296b87dae0a751fb22bf3685741"}, + {file = "lxml-6.0.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:54c4855eabd9fc29707d30141be99e5cd1102e7d2258d2892314cf4c110726c3"}, + {file = "lxml-6.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c907516d49f77f6cd8ead1322198bdfd902003c3c330c77a1c5f3cc32a0e4d16"}, + {file = "lxml-6.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36531f81c8214e293097cd2b7873f178997dae33d3667caaae8bdfb9666b76c0"}, + {file = "lxml-6.0.0-cp312-cp312-win32.whl", hash = "sha256:690b20e3388a7ec98e899fd54c924e50ba6693874aa65ef9cb53de7f7de9d64a"}, + {file = "lxml-6.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:310b719b695b3dd442cdfbbe64936b2f2e231bb91d998e99e6f0daf991a3eba3"}, + {file = "lxml-6.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:8cb26f51c82d77483cdcd2b4a53cda55bbee29b3c2f3ddeb47182a2a9064e4eb"}, + {file = "lxml-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6da7cd4f405fd7db56e51e96bff0865b9853ae70df0e6720624049da76bde2da"}, + {file = "lxml-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b34339898bb556a2351a1830f88f751679f343eabf9cf05841c95b165152c9e7"}, + {file = "lxml-6.0.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:51a5e4c61a4541bd1cd3ba74766d0c9b6c12d6a1a4964ef60026832aac8e79b3"}, + {file = "lxml-6.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d18a25b19ca7307045581b18b3ec9ead2b1db5ccd8719c291f0cd0a5cec6cb81"}, + {file = "lxml-6.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4f0c66df4386b75d2ab1e20a489f30dc7fd9a06a896d64980541506086be1f1"}, + {file = "lxml-6.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f4b481b6cc3a897adb4279216695150bbe7a44c03daba3c894f49d2037e0a24"}, + {file = "lxml-6.0.0-cp313-cp313-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a78d6c9168f5bcb20971bf3329c2b83078611fbe1f807baadc64afc70523b3a"}, + {file = "lxml-6.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae06fbab4f1bb7db4f7c8ca9897dc8db4447d1a2b9bee78474ad403437bcc29"}, + {file = "lxml-6.0.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:1fa377b827ca2023244a06554c6e7dc6828a10aaf74ca41965c5d8a4925aebb4"}, + {file = "lxml-6.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1676b56d48048a62ef77a250428d1f31f610763636e0784ba67a9740823988ca"}, + {file = "lxml-6.0.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0e32698462aacc5c1cf6bdfebc9c781821b7e74c79f13e5ffc8bfe27c42b1abf"}, + {file = "lxml-6.0.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4d6036c3a296707357efb375cfc24bb64cd955b9ec731abf11ebb1e40063949f"}, + {file = "lxml-6.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7488a43033c958637b1a08cddc9188eb06d3ad36582cebc7d4815980b47e27ef"}, + {file = "lxml-6.0.0-cp313-cp313-win32.whl", hash = "sha256:5fcd7d3b1d8ecb91445bd71b9c88bdbeae528fefee4f379895becfc72298d181"}, + {file = "lxml-6.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:2f34687222b78fff795feeb799a7d44eca2477c3d9d3a46ce17d51a4f383e32e"}, + {file = "lxml-6.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:21db1ec5525780fd07251636eb5f7acb84003e9382c72c18c542a87c416ade03"}, + {file = "lxml-6.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4eb114a0754fd00075c12648d991ec7a4357f9cb873042cc9a77bf3a7e30c9db"}, + {file = "lxml-6.0.0-cp38-cp38-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:7da298e1659e45d151b4028ad5c7974917e108afb48731f4ed785d02b6818994"}, + {file = "lxml-6.0.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7bf61bc4345c1895221357af8f3e89f8c103d93156ef326532d35c707e2fb19d"}, + {file = "lxml-6.0.0-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63b634facdfbad421d4b61c90735688465d4ab3a8853ac22c76ccac2baf98d97"}, + {file = "lxml-6.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e380e85b93f148ad28ac15f8117e2fd8e5437aa7732d65e260134f83ce67911b"}, + {file = "lxml-6.0.0-cp38-cp38-win32.whl", hash = "sha256:185efc2fed89cdd97552585c624d3c908f0464090f4b91f7d92f8ed2f3b18f54"}, + {file = "lxml-6.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:f97487996a39cb18278ca33f7be98198f278d0bc3c5d0fd4d7b3d63646ca3c8a"}, + {file = "lxml-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85b14a4689d5cff426c12eefe750738648706ea2753b20c2f973b2a000d3d261"}, + {file = "lxml-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f64ccf593916e93b8d36ed55401bb7fe9c7d5de3180ce2e10b08f82a8f397316"}, + {file = "lxml-6.0.0-cp39-cp39-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:b372d10d17a701b0945f67be58fae4664fd056b85e0ff0fbc1e6c951cdbc0512"}, + {file = "lxml-6.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a674c0948789e9136d69065cc28009c1b1874c6ea340253db58be7622ce6398f"}, + {file = "lxml-6.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:edf6e4c8fe14dfe316939711e3ece3f9a20760aabf686051b537a7562f4da91a"}, + {file = "lxml-6.0.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:048a930eb4572829604982e39a0c7289ab5dc8abc7fc9f5aabd6fbc08c154e93"}, + {file = "lxml-6.0.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0b5fa5eda84057a4f1bbb4bb77a8c28ff20ae7ce211588d698ae453e13c6281"}, + {file = "lxml-6.0.0-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:c352fc8f36f7e9727db17adbf93f82499457b3d7e5511368569b4c5bd155a922"}, + {file = "lxml-6.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8db5dc617cb937ae17ff3403c3a70a7de9df4852a046f93e71edaec678f721d0"}, + {file = "lxml-6.0.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:2181e4b1d07dde53986023482673c0f1fba5178ef800f9ab95ad791e8bdded6a"}, + {file = "lxml-6.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b3c98d5b24c6095e89e03d65d5c574705be3d49c0d8ca10c17a8a4b5201b72f5"}, + {file = "lxml-6.0.0-cp39-cp39-win32.whl", hash = "sha256:04d67ceee6db4bcb92987ccb16e53bef6b42ced872509f333c04fb58a3315256"}, + {file = "lxml-6.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:e0b1520ef900e9ef62e392dd3d7ae4f5fa224d1dd62897a792cf353eb20b6cae"}, + {file = "lxml-6.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:e35e8aaaf3981489f42884b59726693de32dabfc438ac10ef4eb3409961fd402"}, + {file = "lxml-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:dbdd7679a6f4f08152818043dbb39491d1af3332128b3752c3ec5cebc0011a72"}, + {file = "lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40442e2a4456e9910875ac12951476d36c0870dcb38a68719f8c4686609897c4"}, + {file = "lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db0efd6bae1c4730b9c863fc4f5f3c0fa3e8f05cae2c44ae141cb9dfc7d091dc"}, + {file = "lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ab542c91f5a47aaa58abdd8ea84b498e8e49fe4b883d67800017757a3eb78e8"}, + {file = "lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:013090383863b72c62a702d07678b658fa2567aa58d373d963cca245b017e065"}, + {file = "lxml-6.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c86df1c9af35d903d2b52d22ea3e66db8058d21dc0f59842ca5deb0595921141"}, + {file = "lxml-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4337e4aec93b7c011f7ee2e357b0d30562edd1955620fdd4aeab6aacd90d43c5"}, + {file = "lxml-6.0.0-pp39-pypy39_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ae74f7c762270196d2dda56f8dd7309411f08a4084ff2dfcc0b095a218df2e06"}, + {file = "lxml-6.0.0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:059c4cbf3973a621b62ea3132934ae737da2c132a788e6cfb9b08d63a0ef73f9"}, + {file = "lxml-6.0.0-pp39-pypy39_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f090a9bc0ce8da51a5632092f98a7e7f84bca26f33d161a98b57f7fb0004ca"}, + {file = "lxml-6.0.0-pp39-pypy39_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9da022c14baeec36edfcc8daf0e281e2f55b950249a455776f0d1adeeada4734"}, + {file = "lxml-6.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a55da151d0b0c6ab176b4e761670ac0e2667817a1e0dadd04a01d0561a219349"}, + {file = "lxml-6.0.0.tar.gz", hash = "sha256:032e65120339d44cdc3efc326c9f660f5f7205f3a535c1fdbf898b29ea01fb72"}, ] [package.extras] @@ -1332,7 +1447,6 @@ cssselect = ["cssselect (>=0.7)"] html-clean = ["lxml_html_clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.11,<3.1.0)"] [[package]] name = "markupsafe" @@ -1440,75 +1554,70 @@ test = ["pytest", "pytest-cov"] [[package]] name = "msgpack" -version = "1.1.0" +version = "1.1.1" description = "MessagePack serializer" optional = true python-versions = ">=3.8" files = [ - {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, - {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, - {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, - {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, - {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, - {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, - {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, - {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, - {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, - {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, - {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, - {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, - {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, - {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, - {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, - {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, + {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, + {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78426096939c2c7482bf31ef15ca219a9e24460289c00dd0b94411040bb73ad2"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b17ba27727a36cb73aabacaa44b13090feb88a01d012c0f4be70c00f75048b4"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a17ac1ea6ec3c7687d70201cfda3b1e8061466f28f686c24f627cae4ea8efd0"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:88d1e966c9235c1d4e2afac21ca83933ba59537e2e2727a999bf3f515ca2af26"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f6d58656842e1b2ddbe07f43f56b10a60f2ba5826164910968f5933e5178af75"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96decdfc4adcbc087f5ea7ebdcfd3dee9a13358cae6e81d54be962efc38f6338"}, + {file = "msgpack-1.1.1-cp310-cp310-win32.whl", hash = "sha256:6640fd979ca9a212e4bcdf6eb74051ade2c690b862b679bfcb60ae46e6dc4bfd"}, + {file = "msgpack-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:8b65b53204fe1bd037c40c4148d00ef918eb2108d24c9aaa20bc31f9810ce0a8"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752"}, + {file = "msgpack-1.1.1-cp311-cp311-win32.whl", hash = "sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295"}, + {file = "msgpack-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a"}, + {file = "msgpack-1.1.1-cp312-cp312-win32.whl", hash = "sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c"}, + {file = "msgpack-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5"}, + {file = "msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323"}, + {file = "msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bba1be28247e68994355e028dcd668316db30c1f758d3241a7b903ac78dcd285"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8f93dcddb243159c9e4109c9750ba5b335ab8d48d9522c5308cd05d7e3ce600"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fbbc0b906a24038c9958a1ba7ae0918ad35b06cb449d398b76a7d08470b0ed9"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:61e35a55a546a1690d9d09effaa436c25ae6130573b6ee9829c37ef0f18d5e78"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1abfc6e949b352dadf4bce0eb78023212ec5ac42f6abfd469ce91d783c149c2a"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:996f2609ddf0142daba4cefd767d6db26958aac8439ee41db9cc0db9f4c4c3a6"}, + {file = "msgpack-1.1.1-cp38-cp38-win32.whl", hash = "sha256:4d3237b224b930d58e9d83c81c0dba7aacc20fcc2f89c1e5423aa0529a4cd142"}, + {file = "msgpack-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:da8f41e602574ece93dbbda1fab24650d6bf2a24089f9e9dbb4f5730ec1e58ad"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5be6b6bc52fad84d010cb45433720327ce886009d862f46b26d4d154001994b"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a89cd8c087ea67e64844287ea52888239cbd2940884eafd2dcd25754fb72232"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d75f3807a9900a7d575d8d6674a3a47e9f227e8716256f35bc6f03fc597ffbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d182dac0221eb8faef2e6f44701812b467c02674a322c739355c39e94730cdbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b13fe0fb4aac1aa5320cd693b297fe6fdef0e7bea5518cbc2dd5299f873ae90"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:435807eeb1bc791ceb3247d13c79868deb22184e1fc4224808750f0d7d1affc1"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4835d17af722609a45e16037bb1d4d78b7bdf19d6c0128116d178956618c4e88"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8ef6e342c137888ebbfb233e02b8fbd689bb5b5fcc59b34711ac47ebd504478"}, + {file = "msgpack-1.1.1-cp39-cp39-win32.whl", hash = "sha256:61abccf9de335d9efd149e2fff97ed5974f2481b3353772e8e2dd3402ba2bd57"}, + {file = "msgpack-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:40eae974c873b2992fd36424a5d9407f93e97656d999f43fca9d29f820899084"}, + {file = "msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd"}, ] [[package]] @@ -1551,47 +1660,48 @@ files = [ [[package]] name = "mypy" -version = "1.15.0" +version = "1.17.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, + {file = "mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6"}, + {file = "mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d"}, + {file = "mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b"}, + {file = "mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a"}, + {file = "mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f"}, + {file = "mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937"}, + {file = "mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be"}, + {file = "mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61"}, + {file = "mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f"}, + {file = "mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d"}, + {file = "mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3"}, + {file = "mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70"}, + {file = "mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb"}, + {file = "mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d"}, + {file = "mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8"}, + {file = "mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e"}, + {file = "mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8"}, + {file = "mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d"}, + {file = "mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06"}, + {file = "mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a"}, + {file = "mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889"}, + {file = "mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba"}, + {file = "mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658"}, + {file = "mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c"}, + {file = "mypy-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:63e751f1b5ab51d6f3d219fe3a2fe4523eaa387d854ad06906c63883fde5b1ab"}, + {file = "mypy-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7fb09d05e0f1c329a36dcd30e27564a3555717cde87301fae4fb542402ddfad"}, + {file = "mypy-1.17.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b72c34ce05ac3a1361ae2ebb50757fb6e3624032d91488d93544e9f82db0ed6c"}, + {file = "mypy-1.17.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434ad499ad8dde8b2f6391ddfa982f41cb07ccda8e3c67781b1bfd4e5f9450a8"}, + {file = "mypy-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f105f61a5eff52e137fd73bee32958b2add9d9f0a856f17314018646af838e97"}, + {file = "mypy-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:ba06254a5a22729853209550d80f94e28690d5530c661f9416a68ac097b13fc4"}, + {file = "mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496"}, + {file = "mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03"}, ] [package.dependencies] mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing_extensions = ">=4.6.0" @@ -1703,13 +1813,13 @@ files = [ [[package]] name = "oauthlib" -version = "3.2.2" +version = "3.3.1" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, + {file = "oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1"}, + {file = "oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9"}, ] [package.extras] @@ -1728,102 +1838,138 @@ files = [ {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + [[package]] name = "pillow" -version = "11.2.1" +version = "11.3.0" description = "Python Imaging Library (Fork)" optional = true python-versions = ">=3.9" files = [ - {file = "pillow-11.2.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047"}, - {file = "pillow-11.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95"}, - {file = "pillow-11.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4ba4be812c7a40280629e55ae0b14a0aafa150dd6451297562e1764808bbe61"}, - {file = "pillow-11.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8bd62331e5032bc396a93609982a9ab6b411c05078a52f5fe3cc59234a3abd1"}, - {file = "pillow-11.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:562d11134c97a62fe3af29581f083033179f7ff435f78392565a1ad2d1c2c45c"}, - {file = "pillow-11.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c97209e85b5be259994eb5b69ff50c5d20cca0f458ef9abd835e262d9d88b39d"}, - {file = "pillow-11.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0c3e6d0f59171dfa2e25d7116217543310908dfa2770aa64b8f87605f8cacc97"}, - {file = "pillow-11.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc1c3bc53befb6096b84165956e886b1729634a799e9d6329a0c512ab651e579"}, - {file = "pillow-11.2.1-cp310-cp310-win32.whl", hash = "sha256:312c77b7f07ab2139924d2639860e084ec2a13e72af54d4f08ac843a5fc9c79d"}, - {file = "pillow-11.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9bc7ae48b8057a611e5fe9f853baa88093b9a76303937449397899385da06fad"}, - {file = "pillow-11.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:2728567e249cdd939f6cc3d1f049595c66e4187f3c34078cbc0a7d21c47482d2"}, - {file = "pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70"}, - {file = "pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf"}, - {file = "pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7"}, - {file = "pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8"}, - {file = "pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600"}, - {file = "pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788"}, - {file = "pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e"}, - {file = "pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e"}, - {file = "pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6"}, - {file = "pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193"}, - {file = "pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7"}, - {file = "pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f"}, - {file = "pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b"}, - {file = "pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d"}, - {file = "pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4"}, - {file = "pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d"}, - {file = "pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4"}, - {file = "pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443"}, - {file = "pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c"}, - {file = "pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3"}, - {file = "pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941"}, - {file = "pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb"}, - {file = "pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28"}, - {file = "pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830"}, - {file = "pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0"}, - {file = "pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1"}, - {file = "pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f"}, - {file = "pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155"}, - {file = "pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14"}, - {file = "pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b"}, - {file = "pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2"}, - {file = "pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691"}, - {file = "pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c"}, - {file = "pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22"}, - {file = "pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7"}, - {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16"}, - {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b"}, - {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406"}, - {file = "pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91"}, - {file = "pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751"}, - {file = "pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9"}, - {file = "pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd"}, - {file = "pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e"}, - {file = "pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681"}, - {file = "pillow-11.2.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:7491cf8a79b8eb867d419648fff2f83cb0b3891c8b36da92cc7f1931d46108c8"}, - {file = "pillow-11.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b02d8f9cb83c52578a0b4beadba92e37d83a4ef11570a8688bbf43f4ca50909"}, - {file = "pillow-11.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:014ca0050c85003620526b0ac1ac53f56fc93af128f7546623cc8e31875ab928"}, - {file = "pillow-11.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3692b68c87096ac6308296d96354eddd25f98740c9d2ab54e1549d6c8aea9d79"}, - {file = "pillow-11.2.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:f781dcb0bc9929adc77bad571b8621ecb1e4cdef86e940fe2e5b5ee24fd33b35"}, - {file = "pillow-11.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2b490402c96f907a166615e9a5afacf2519e28295f157ec3a2bb9bd57de638cb"}, - {file = "pillow-11.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd6b20b93b3ccc9c1b597999209e4bc5cf2853f9ee66e3fc9a400a78733ffc9a"}, - {file = "pillow-11.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b835d89c08a6c2ee7781b8dd0a30209a8012b5f09c0a665b65b0eb3560b6f36"}, - {file = "pillow-11.2.1-cp39-cp39-win32.whl", hash = "sha256:b10428b3416d4f9c61f94b494681280be7686bda15898a3a9e08eb66a6d92d67"}, - {file = "pillow-11.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:6ebce70c3f486acf7591a3d73431fa504a4e18a9b97ff27f5f47b7368e4b9dd1"}, - {file = "pillow-11.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:c27476257b2fdcd7872d54cfd119b3a9ce4610fb85c8e32b70b42e3680a29a1e"}, - {file = "pillow-11.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156"}, - {file = "pillow-11.2.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772"}, - {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363"}, - {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85d27ea4c889342f7e35f6d56e7e1cb345632ad592e8c51b693d7b7556043ce0"}, - {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bf2c33d6791c598142f00c9c4c7d47f6476731c31081331664eb26d6ab583e01"}, - {file = "pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e616e7154c37669fc1dfc14584f11e284e05d1c650e1c0f972f281c4ccc53193"}, - {file = "pillow-11.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39ad2e0f424394e3aebc40168845fee52df1394a4673a6ee512d840d14ab3013"}, - {file = "pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed"}, - {file = "pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c"}, - {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd"}, - {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076"}, - {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b"}, - {file = "pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f"}, - {file = "pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044"}, - {file = "pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6"}, + {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"}, + {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae"}, + {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9"}, + {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e"}, + {file = "pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6"}, + {file = "pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f"}, + {file = "pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f"}, + {file = "pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722"}, + {file = "pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f"}, + {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e"}, + {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94"}, + {file = "pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0"}, + {file = "pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac"}, + {file = "pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd"}, + {file = "pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4"}, + {file = "pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024"}, + {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809"}, + {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d"}, + {file = "pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149"}, + {file = "pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d"}, + {file = "pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f"}, + {file = "pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c"}, + {file = "pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8"}, + {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2"}, + {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b"}, + {file = "pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3"}, + {file = "pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51"}, + {file = "pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580"}, + {file = "pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e"}, + {file = "pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59"}, + {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe"}, + {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c"}, + {file = "pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788"}, + {file = "pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31"}, + {file = "pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e"}, + {file = "pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12"}, + {file = "pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77"}, + {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874"}, + {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a"}, + {file = "pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214"}, + {file = "pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635"}, + {file = "pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6"}, + {file = "pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae"}, + {file = "pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477"}, + {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50"}, + {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b"}, + {file = "pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12"}, + {file = "pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db"}, + {file = "pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa"}, + {file = "pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f"}, + {file = "pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a"}, + {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978"}, + {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d"}, + {file = "pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71"}, + {file = "pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada"}, + {file = "pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8"}, + {file = "pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] test-arrow = ["pyarrow"] -tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] typing = ["typing-extensions"] xmp = ["defusedxml"] @@ -1845,18 +1991,29 @@ type = ["mypy (>=1.14.1)"] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "polib" +version = "1.2.0" +description = "A library to manipulate gettext files (po and mo files)." +optional = false +python-versions = "*" +files = [ + {file = "polib-1.2.0-py2.py3-none-any.whl", hash = "sha256:1c77ee1b81feb31df9bca258cbc58db1bbb32d10214b173882452c73af06d62d"}, + {file = "polib-1.2.0.tar.gz", hash = "sha256:f3ef94aefed6e183e342a8a269ae1fc4742ba193186ad76f175938621dbfc26b"}, +] [[package]] name = "pooch" @@ -1904,13 +2061,13 @@ test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "py7zr" -version = "0.22.0" +version = "1.0.0" description = "Pure python 7-zip library" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "py7zr-0.22.0-py3-none-any.whl", hash = "sha256:993b951b313500697d71113da2681386589b7b74f12e48ba13cc12beca79d078"}, - {file = "py7zr-0.22.0.tar.gz", hash = "sha256:c6c7aea5913535184003b73938490f9a4d8418598e533f9ca991d3b8e45a139e"}, + {file = "py7zr-1.0.0-py3-none-any.whl", hash = "sha256:6f42d2ff34c808e9026ad11b721c13b41b0673cf2b4e8f8fb34f9d65ae143dd1"}, + {file = "py7zr-1.0.0.tar.gz", hash = "sha256:f6bfee81637c9032f6a9f0eb045a4bfc7a7ff4138becfc42d7cb89b54ffbfef1"}, ] [package.dependencies] @@ -1920,16 +2077,16 @@ inflate64 = ">=1.0.0,<1.1.0" multivolumefile = ">=0.2.3" psutil = {version = "*", markers = "sys_platform != \"cygwin\""} pybcj = ">=1.0.0,<1.1.0" -pycryptodomex = ">=3.16.0" -pyppmd = ">=1.1.0,<1.2.0" -pyzstd = ">=0.15.9" +pycryptodomex = ">=3.20.0" +pyppmd = ">=1.1.0,<1.3.0" +pyzstd = ">=0.16.1" texttable = "*" [package.extras] -check = ["black (>=23.1.0)", "check-manifest", "flake8 (<8)", "flake8-black (>=0.3.6)", "flake8-deprecated", "flake8-isort", "isort (>=5.0.3)", "lxml", "mypy (>=0.940)", "mypy-extensions (>=0.4.1)", "pygments", "readme-renderer", "twine", "types-psutil"] +check = ["black (>=24.8.0)", "check-manifest", "flake8 (<8)", "flake8-black (>=0.3.6)", "flake8-deprecated", "flake8-isort", "isort (>=5.13.2)", "lxml", "mypy (>=1.10.0)", "mypy_extensions (>=1.0.0)", "pygments", "pylint", "readme-renderer", "twine", "types-psutil"] debug = ["pytest", "pytest-leaks", "pytest-profiling"] -docs = ["docutils", "sphinx (>=5.0)", "sphinx-a4doc", "sphinx-py3doc-enhanced-theme"] -test = ["coverage[toml] (>=5.2)", "coveralls (>=2.1.1)", "py-cpuinfo", "pytest", "pytest-benchmark", "pytest-cov", "pytest-remotedata", "pytest-timeout"] +docs = ["docutils", "sphinx (>=7.0.0)", "sphinx-a4doc", "sphinx-py3doc-enhanced-theme"] +test = ["coverage[toml] (>=5.2)", "coveralls (>=2.1.1)", "py-cpuinfo", "pytest", "pytest-benchmark", "pytest-cov", "pytest-httpserver", "pytest-remotedata", "pytest-timeout", "requests"] test-compat = ["libarchive-c"] [[package]] @@ -2043,40 +2200,52 @@ files = [ [[package]] name = "pycryptodomex" -version = "3.22.0" +version = "3.23.0" description = "Cryptographic library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "pycryptodomex-3.22.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:41673e5cc39a8524557a0472077635d981172182c9fe39ce0b5f5c19381ffaff"}, - {file = "pycryptodomex-3.22.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:276be1ed006e8fd01bba00d9bd9b60a0151e478033e86ea1cb37447bbc057edc"}, - {file = "pycryptodomex-3.22.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:813e57da5ceb4b549bab96fa548781d9a63f49f1d68fdb148eeac846238056b7"}, - {file = "pycryptodomex-3.22.0-cp27-cp27m-win32.whl", hash = "sha256:d7beeacb5394765aa8dabed135389a11ee322d3ee16160d178adc7f8ee3e1f65"}, - {file = "pycryptodomex-3.22.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:b3746dedf74787da43e4a2f85bd78f5ec14d2469eb299ddce22518b3891f16ea"}, - {file = "pycryptodomex-3.22.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5ebc09b7d8964654aaf8a4f5ac325f2b0cc038af9bea12efff0cd4a5bb19aa42"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:aef4590263b9f2f6283469e998574d0bd45c14fb262241c27055b82727426157"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5ac608a6dce9418d4f300fab7ba2f7d499a96b462f2b9b5c90d8d994cd36dcad"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a24f681365ec9757ccd69b85868bbd7216ba451d0f86f6ea0eed75eeb6975db"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:259664c4803a1fa260d5afb322972813c5fe30ea8b43e54b03b7e3a27b30856b"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7127d9de3c7ce20339e06bcd4f16f1a1a77f1471bcf04e3b704306dde101b719"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ee75067b35c93cc18b38af47b7c0664998d8815174cfc66dd00ea1e244eb27e6"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:1a8b0c5ba061ace4bcd03496d42702c3927003db805b8ec619ea6506080b381d"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:bfe4fe3233ef3e58028a3ad8f28473653b78c6d56e088ea04fe7550c63d4d16b"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-win32.whl", hash = "sha256:2cac9ed5c343bb3d0075db6e797e6112514764d08d667c74cb89b931aac9dddd"}, - {file = "pycryptodomex-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:ff46212fda7ee86ec2f4a64016c994e8ad80f11ef748131753adb67e9b722ebd"}, - {file = "pycryptodomex-3.22.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:5bf3ce9211d2a9877b00b8e524593e2209e370a287b3d5e61a8c45f5198487e2"}, - {file = "pycryptodomex-3.22.0-pp27-pypy_73-win32.whl", hash = "sha256:684cb57812cd243217c3d1e01a720c5844b30f0b7b64bb1a49679f7e1e8a54ac"}, - {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c8cffb03f5dee1026e3f892f7cffd79926a538c67c34f8b07c90c0bd5c834e27"}, - {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:140b27caa68a36d0501b05eb247bd33afa5f854c1ee04140e38af63c750d4e39"}, - {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:644834b1836bb8e1d304afaf794d5ae98a1d637bd6e140c9be7dd192b5374811"}, - {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c506aba3318505dbeecf821ed7b9a9f86f422ed085e2d79c4fba0ae669920a"}, - {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7cd39f7a110c1ab97ce9ee3459b8bc615920344dc00e56d1b709628965fba3f2"}, - {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e4eaaf6163ff13788c1f8f615ad60cdc69efac6d3bf7b310b21e8cfe5f46c801"}, - {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eac39e237d65981554c2d4c6668192dc7051ad61ab5fc383ed0ba049e4007ca2"}, - {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ab0d89d1761959b608952c7b347b0e76a32d1a5bb278afbaa10a7f3eaef9a0a"}, - {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e64164f816f5e43fd69f8ed98eb28f98157faf68208cd19c44ed9d8e72d33e8"}, - {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f005de31efad6f9acefc417296c641f13b720be7dbfec90edeaca601c0fab048"}, - {file = "pycryptodomex-3.22.0.tar.gz", hash = "sha256:a1da61bacc22f93a91cbe690e3eb2022a03ab4123690ab16c46abb693a9df63d"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245"}, + {file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"}, ] [[package]] @@ -2108,13 +2277,13 @@ test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." -optional = true +optional = false python-versions = ">=3.8" files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] @@ -2152,66 +2321,68 @@ tests = ["flaky", "pytest", "pytest-cov", "pytest-random-order", "pyyaml"] [[package]] name = "pyppmd" -version = "1.1.1" +version = "1.2.0" description = "PPMd compression/decompression library" optional = false python-versions = ">=3.9" files = [ - {file = "pyppmd-1.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:406b184132c69e3f60ea9621b69eaa0c5494e83f82c307b3acce7b86a4f8f888"}, - {file = "pyppmd-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2cf003bb184adf306e1ac1828107307927737dde63474715ba16462e266cbef"}, - {file = "pyppmd-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71c8fd0ecc8d4760e852dd6df19d1a827427cb9e6c9e568cbf5edba7d860c514"}, - {file = "pyppmd-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6b5edee08b66ad6c39fd4d34a7ef4cfeb4b69fd6d68957e59cd2db674611a9e"}, - {file = "pyppmd-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e95bd23eb1543ab3149f24fe02f6dd2695023326027a4b989fb2c6dba256e75e"}, - {file = "pyppmd-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e633ee4cc19d0c71b3898092c3c4cc20a10bd5e6197229fffac29d68ad5d83b8"}, - {file = "pyppmd-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ecaafe2807ef557f0c49b8476a4fa04091b43866072fbcf31b3ceb01a96c9168"}, - {file = "pyppmd-1.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c182fccff60ae8f24f28f5145c36a60708b5b041a25d36b67f23c44923552fa4"}, - {file = "pyppmd-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:70c93d19efe67cdac3e7fa2d4e171650a2c4f90127a9781b25e496a43f12fbbc"}, - {file = "pyppmd-1.1.1-cp310-cp310-win32.whl", hash = "sha256:57c75856920a210ed72b553885af7bc06eddfd30ff26b62a3a63cb8f86f3d217"}, - {file = "pyppmd-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d5293f10dc8c1d571b780e0d54426d3d858c19bbd8cb0fe972dcea3906acd05c"}, - {file = "pyppmd-1.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:753c5297c91c059443caef33bccbffb10764221739d218046981638aeb9bc5f2"}, - {file = "pyppmd-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b5a73da09de480a94793c9064876af14a01be117de872737935ac447b7cde3c"}, - {file = "pyppmd-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89c6febb7114dea02a061143d78d04751a945dfcadff77560e9a3d3c7583c24b"}, - {file = "pyppmd-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0001e467c35e35e6076a8c32ed9074aa45833615ee16115de9282d5c0985a1d8"}, - {file = "pyppmd-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c76820db25596afc859336ba06c01c9be0ff326480beec9c699fd378a546a77f"}, - {file = "pyppmd-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b67f0a228f8c58750a21ba667c170ae957283e08fd580857f13cb686334e5b3e"}, - {file = "pyppmd-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b18f24c14f0b0f1757a42c458ae7b6fd7aa0bce8147ac1016a9c134068c1ccc2"}, - {file = "pyppmd-1.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c9e43729161cc3b6ad5b04b16bae7665d3c0cc803de047d8a979aa9232a4f94a"}, - {file = "pyppmd-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fe057d254528b4eeebe2800baefde47d6af679bae184d3793c13a06f794df442"}, - {file = "pyppmd-1.1.1-cp311-cp311-win32.whl", hash = "sha256:faa51240493a5c53c9b544c99722f70303eea702742bf90f3c3064144342da4a"}, - {file = "pyppmd-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:62486f544d6957e1381147e3961eee647b7f4421795be4fb4f1e29d52aee6cb5"}, - {file = "pyppmd-1.1.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9877ef273e2c0efdec740855e28004a708ada9012e0db6673df4bb6eba3b05e0"}, - {file = "pyppmd-1.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f816a5cbccceced80e15335389eeeaf1b56a605fb7eebe135b1c85bd161e288c"}, - {file = "pyppmd-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6bddabf8f2c6b991d15d6785e603d9d414ae4a791f131b1a729bb8a5d31133d1"}, - {file = "pyppmd-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855bc2b0d19c3fead5815d72dbe350b4f765334336cbf8bcb504d46edc9e9dd2"}, - {file = "pyppmd-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a95b11b3717c083b912f0879678ba72f301bbdb9b69efed46dbc5df682aa3ce7"}, - {file = "pyppmd-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38b645347b6ea217b0c58e8edac27473802868f152db520344ac8c7490981849"}, - {file = "pyppmd-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f8f94b6222262def5b532f2b9716554ef249ad8411fd4da303596cc8c2e8eda1"}, - {file = "pyppmd-1.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1c0306f69ceddf385ef689ebd0218325b7e523c48333d87157b37393466cfa1e"}, - {file = "pyppmd-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4ba510457a56535522a660098399e3fa8722e4de55808d089c9d13435d87069"}, - {file = "pyppmd-1.1.1-cp312-cp312-win32.whl", hash = "sha256:032f040a89fd8348109e8638f94311bd4c3c693fb4cad213ad06a37c203690b1"}, - {file = "pyppmd-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:2be8cbd13dd59fad1a0ad38062809e28596f3673b77a799dfe82b287986265ed"}, - {file = "pyppmd-1.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9458f972f090f3846fc5bea0a6f7363da773d3c4b2d4654f1d4ca3c11f6ecbfa"}, - {file = "pyppmd-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:44811a9d958873d857ca81cebf7ba646a0952f8a7bbf8a60cf6ec5d002faa040"}, - {file = "pyppmd-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a1b12460958885ca44e433986644009d0599b87a444f668ce3724a46ce588924"}, - {file = "pyppmd-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:200c74f05b97b00f047cf60607914a0b50f80991f1fb3677f624a85aa79d9458"}, - {file = "pyppmd-1.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ebe0d98a341b32f164e860059243e125398865cc0363b32ffc31f953460fe87"}, - {file = "pyppmd-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf93e1e047a82f1e7e194fcf49da166d2b9d8dc98d7c0b5cd844dc4360d9c1f5"}, - {file = "pyppmd-1.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f5b0b8c746bde378ae3b4df42a11fd8599ba3e5808dfea36e16d722b74bd0506"}, - {file = "pyppmd-1.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bcdd5207b6c79887f25639632ca2623a399d8c54f567973e9ba474b5ebae2b1c"}, - {file = "pyppmd-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7bfcca94e5452b6d54ac24a11c2402f6a193c331e5dc221c1f1df71773624374"}, - {file = "pyppmd-1.1.1-cp39-cp39-win32.whl", hash = "sha256:18e99c074664f996f511bc6e87aab46bc4c75f5bd0157d3210292919be35e22c"}, - {file = "pyppmd-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b29788d5a0f8f39ea46a1255cd886daddf9c64ba9d4cb64677bc93bd3859ac0e"}, - {file = "pyppmd-1.1.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28648ef56793bf1ed0ff24728642f56fa39cb96ea161dec6ee2d26f97c0cdd28"}, - {file = "pyppmd-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:427d6f9b9c011e032db9529b2a15773f2e2944ca490b67d5757f4af33bbda406"}, - {file = "pyppmd-1.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34c7a07197a03656c1920fd88e05049c155a955c4de4b8b8a8e5fec19a97b45b"}, - {file = "pyppmd-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1fea2eee28beca61165c4714dcd032de76af318553791107d308b4b08575ecc"}, - {file = "pyppmd-1.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:04391e4f82c8c2c316ba60e480300ad1af37ec12bdb5c20f06b502030ff35975"}, - {file = "pyppmd-1.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cf08a354864c352a94e6e53733009baeab1e7c570010c4f5be226923ecfa09d1"}, - {file = "pyppmd-1.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:334e5fe5d75764b87c591a16d2b2df6f9939e2ad114dacf98bb4b0e7c90911e9"}, - {file = "pyppmd-1.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15d5928b25f04f5431585d17c835cd509a34e1c9f1416653db8d2815e97d4e20"}, - {file = "pyppmd-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af06329796a4965788910ac40f1b012d2e173ede08456ceea0ec7fc4d2e69d62"}, - {file = "pyppmd-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4ccdd3751e432e71e02de96f16fc8824e4f4bfc47a8b470f0c7aae88dae4c666"}, - {file = "pyppmd-1.1.1.tar.gz", hash = "sha256:f1a812f1e7628f4c26d05de340b91b72165d7b62778c27d322b82ce2e8ff00cb"}, + {file = "pyppmd-1.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4a25d8b2a71e0cc6f34475c36450e905586b13d0c88fb28471655c215f370908"}, + {file = "pyppmd-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9dd8a6261152591a352d91e5e52c16b81fa760f64c361a7afb24a1f3b5e048"}, + {file = "pyppmd-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2cd2694f43720fa1304c1fa31b8a1e7d80162f045e91569fb93473277c2747b8"}, + {file = "pyppmd-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0354919ab0f4065d76c64ad0dc21f14116651a2124cf4915b96c4e76d9caf470"}, + {file = "pyppmd-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:416c15576924ff9d2852fbe53d162c946e0466ce79d8a03a058e6f09a54934f0"}, + {file = "pyppmd-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dcdd5bf53f562af2a9be76739be69c9de080dfa59a4c4a8bcc4a163f9c5cb53e"}, + {file = "pyppmd-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c67196af6cfcc68e72a8fffbc332d743327bb9323cb7f3709e27185e743c7272"}, + {file = "pyppmd-1.2.0-cp310-cp310-win32.whl", hash = "sha256:d529c78382a2315db22c93e1c831231ee3fd2ad5a352f61496d72474558c6b00"}, + {file = "pyppmd-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f19285ae4dd20bb917c4fd177f0911847feb3abada91cec5fd5d9d5f1b9f3e0"}, + {file = "pyppmd-1.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:30068ed6da301f6ba25219f96d828f3c3a80ca227647571d21c7704301e095e6"}, + {file = "pyppmd-1.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a5f0b78d68620ffb51c46c34c9e0ec02c40bb68e903e6c3ce02870c528164af"}, + {file = "pyppmd-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5f1ee49b88fd2e58a144b1ae0da9c2fe0dabc1962531da9475badbed6fba61fc"}, + {file = "pyppmd-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c98697fea3f3baf5ffc759fd41c766d708ff3fba7379776031077527873ce4ac"}, + {file = "pyppmd-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a3087d7ee6fc35db0bfecabd1df4615f2a9d58a56af61f5fc18b9ce2b379cbf"}, + {file = "pyppmd-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69fe10feb24a92e673b68aca5d945564232d09e25a4e185899e0c657096ae695"}, + {file = "pyppmd-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aa40c982d1df515cd4cb366d3e1ae95ce22f3c20e6b8b2d31aa492679f7ad78c"}, + {file = "pyppmd-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a5c03dd85da64a237c601dd690d8eb95951b7c2eef91b89e110eb208010c6035"}, + {file = "pyppmd-1.2.0-cp311-cp311-win32.whl", hash = "sha256:c577f3dadd514979255e9df6eb89a63409d0e91855bb8c0969ffcd67d5d4f124"}, + {file = "pyppmd-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:f29dfb7aaf4b49ebc09d726fcdeabbce1cb21e9cf3a055244bb1384b8b51dd3b"}, + {file = "pyppmd-1.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:bf26c2def22322135fbaaa3de3c0963062c1835bd43d595478e3a2a674466a1a"}, + {file = "pyppmd-1.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d28cc9febcf37f2ff08b9e25d472de529e8973119c0a3279603b1915c809dd45"}, + {file = "pyppmd-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0f07d5376e1f699d09fbb9139562e5b72a347100aecaa73b688fa08461b3c118"}, + {file = "pyppmd-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:874f52eae03647b653aa34476f4e23c4c30458245c0eb7aa7fb290940abbd5b9"}, + {file = "pyppmd-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abafffb3d5b292924eafd8214ad80487400cf358c4e9dc2ac6c21d2c651c5ee2"}, + {file = "pyppmd-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e955de43991346d4ccb28a74fb4c80cadecf72a6724705301fe1adb569689fe"}, + {file = "pyppmd-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14ed0846c3bcee506555cd943db950d5787a6ffa1089e05deced010759ef1fe5"}, + {file = "pyppmd-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3caef2fb93a63d696b21e5ff72cb2955687b5dfcbed1938936334f9f7737fcd3"}, + {file = "pyppmd-1.2.0-cp312-cp312-win32.whl", hash = "sha256:011c813389476e84387599ad4aa834100e888c6608a6e7d6f07ea7cbac8a8e65"}, + {file = "pyppmd-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:42c7c9050b44b713676d255f0c212b8ff5c0463821053960c89292cf6b6221cc"}, + {file = "pyppmd-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:5768bff11936047613bcb91ee466f21779efc24360bd7953bd338b32da88577a"}, + {file = "pyppmd-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4aa8ffca1727872923d2673045975bca571eb810cf14a21d048648da5237369b"}, + {file = "pyppmd-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6dc00f0ce9f79e1c1c87d9998220a714ab8216873b6c996776b88ab23efe05ac"}, + {file = "pyppmd-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d437881763ffd0d19079402f50e7f4aad5895e3cd5312d095edef0b32dac3aef"}, + {file = "pyppmd-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c763f2e3a011d5e96dfa0195f38accce9a14d489725a3d3641a74addbb5b72"}, + {file = "pyppmd-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38e3835a1951d18dd273000c870a4eb2804c20c400aa3c72231449f300cedf19"}, + {file = "pyppmd-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c76b8881fc087e70338b1cccd452bd12566206587a0d0d8266ba2833be902194"}, + {file = "pyppmd-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8b43e299310e27af5a4bc505bcc87d10cfc38ae28e5ed1f6a779d811705e5ad6"}, + {file = "pyppmd-1.2.0-cp313-cp313-win32.whl", hash = "sha256:4b3249256f8a7ecdd36477f277b232a46ee2c8ca280b23faaeacb7f50cab949a"}, + {file = "pyppmd-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:625896f5da7038fe7145907b68b0b58f7c13b88ad6bbfdc1c20c05654c17fa6c"}, + {file = "pyppmd-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:bec8abbf1edb0300c0a2e4f1bbad6a96154de3e507a2b054a0b1187f1c2e4982"}, + {file = "pyppmd-1.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b5c3284be4dccebb87d81c14b148c81e035356cd01a29889736c75672f6187d"}, + {file = "pyppmd-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:40bfa26fdb3332a6a8d90fe1f6e0d9f489505a014911b470d66f2f79caea6d61"}, + {file = "pyppmd-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75b173bbc9164cdc6fb257d3480269cc26b1eb102ad72281a98cf90e0f7dc860"}, + {file = "pyppmd-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91534eb8c9c0bff9d6c6ec5eb5119a583d31bb9f8cf208d5a925b4e2293c9a7b"}, + {file = "pyppmd-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edc4fcd928bf6219bcddb8230a5830e33a35b684b16ca3e8d1357b17029a9ef7"}, + {file = "pyppmd-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5ff515c2c3544096fe524f341c244787d6449b36692d27131bf74d5075e5c83b"}, + {file = "pyppmd-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:af9be87228cba6b543531260f44675a23b4a1527158a44162dce186157cb13d9"}, + {file = "pyppmd-1.2.0-cp39-cp39-win32.whl", hash = "sha256:3674b5eba0e312b9af987ec7e6af59248f54db9a7f5ca63add5365d6c6639e9e"}, + {file = "pyppmd-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:cff27496fd164b587f150abba9524cae81629adbd2e9472f09e7b2b24b2d4939"}, + {file = "pyppmd-1.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:c9d0f5a903045ee6b399f5fb308e192e39f8f1f551b61441a595676d95dc76ad"}, + {file = "pyppmd-1.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86e252979fc5ae2492ebb46ed0eed0625a46a2cce519c4616b870eab58d77fb7"}, + {file = "pyppmd-1.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9095d8b098ce8cb5c1e404843a16e5167fb5bdebb4d6aed259d43dd2d73cfca3"}, + {file = "pyppmd-1.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:064307c7fec7bdf3da63f5e28c0f1c5cb5c9bf888c1b268c6df3c131391ab345"}, + {file = "pyppmd-1.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c012c17a53b6d9744e0514b17b0c4169c5f21fb54b4db7a0119bc2d7b3fcc609"}, + {file = "pyppmd-1.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0877758ffa73b2e9d2f93b698e17336a4d8acab8d9a3d17cd7960aec08347387"}, + {file = "pyppmd-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ac0960d2d0a1738af3ca3f27c6ed6eead38518d77875a47b2b4aae90ae933f4"}, + {file = "pyppmd-1.2.0.tar.gz", hash = "sha256:cc04af92f1d26831ec96963439dfb27c96467b5452b94436a6af696649a121fd"}, ] [package.extras] @@ -2222,40 +2393,42 @@ test = ["coverage[toml] (>=5.2)", "hypothesis", "pytest (>=6.0)", "pytest-benchm [[package]] name = "pytest" -version = "8.3.5" +version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" +pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" -version = "6.1.1" +version = "6.2.1" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" files = [ - {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"}, - {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"}, + {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, + {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, ] [package.dependencies] coverage = {version = ">=7.5", extras = ["toml"]} -pytest = ">=4.6" +pluggy = ">=1.2" +pytest = ">=6.2.5" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] @@ -2399,6 +2572,32 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "pyyaml-ft" +version = "8.0.0" +description = "YAML parser and emitter for Python with support for free-threading" +optional = false +python-versions = ">=3.13" +files = [ + {file = "pyyaml_ft-8.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c1306282bc958bfda31237f900eb52c9bedf9b93a11f82e1aab004c9a5657a6"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30c5f1751625786c19de751e3130fc345ebcba6a86f6bddd6e1285342f4bbb69"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fa992481155ddda2e303fcc74c79c05eddcdbc907b888d3d9ce3ff3e2adcfb0"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cec6c92b4207004b62dfad1f0be321c9f04725e0f271c16247d8b39c3bf3ea42"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06237267dbcab70d4c0e9436d8f719f04a51123f0ca2694c00dd4b68c338e40b"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8a7f332bc565817644cdb38ffe4739e44c3e18c55793f75dddb87630f03fc254"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d10175a746be65f6feb86224df5d6bc5c049ebf52b89a88cf1cd78af5a367a8"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:58e1015098cf8d8aec82f360789c16283b88ca670fe4275ef6c48c5e30b22a96"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5f3e2ceb790d50602b2fd4ec37abbd760a8c778e46354df647e7c5a4ebb"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8d445bf6ea16bb93c37b42fdacfb2f94c8e92a79ba9e12768c96ecde867046d1"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c56bb46b4fda34cbb92a9446a841da3982cdde6ea13de3fbd80db7eeeab8b49"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dab0abb46eb1780da486f022dce034b952c8ae40753627b27a626d803926483b"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd48d639cab5ca50ad957b6dd632c7dd3ac02a1abe0e8196a3c24a52f5db3f7a"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:052561b89d5b2a8e1289f326d060e794c21fa068aa11255fe71d65baf18a632e"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3bb4b927929b0cb162fb1605392a321e3333e48ce616cdcfa04a839271373255"}, + {file = "pyyaml_ft-8.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:de04cfe9439565e32f178106c51dd6ca61afaa2907d143835d501d84703d3793"}, + {file = "pyyaml_ft-8.0.0.tar.gz", hash = "sha256:0c947dce03954c7b5d38869ed4878b2e6ff1d44b08a0d84dc83fdad205ae39ab"}, +] + [[package]] name = "pyzstd" version = "0.17.0" @@ -2531,20 +2730,116 @@ files = [ [package.dependencies] cffi = "*" +[[package]] +name = "regex" +version = "2025.7.34" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.9" +files = [ + {file = "regex-2025.7.34-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d856164d25e2b3b07b779bfed813eb4b6b6ce73c2fd818d46f47c1eb5cd79bd6"}, + {file = "regex-2025.7.34-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d15a9da5fad793e35fb7be74eec450d968e05d2e294f3e0e77ab03fa7234a83"}, + {file = "regex-2025.7.34-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:95b4639c77d414efa93c8de14ce3f7965a94d007e068a94f9d4997bb9bd9c81f"}, + {file = "regex-2025.7.34-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7de1ceed5a5f84f342ba4a9f4ae589524adf9744b2ee61b5da884b5b659834"}, + {file = "regex-2025.7.34-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02e5860a250cd350c4933cf376c3bc9cb28948e2c96a8bc042aee7b985cfa26f"}, + {file = "regex-2025.7.34-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a5966220b9a1a88691282b7e4350e9599cf65780ca60d914a798cb791aa1177"}, + {file = "regex-2025.7.34-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48fb045bbd4aab2418dc1ba2088a5e32de4bfe64e1457b948bb328a8dc2f1c2e"}, + {file = "regex-2025.7.34-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:20ff8433fa45e131f7316594efe24d4679c5449c0ca69d91c2f9d21846fdf064"}, + {file = "regex-2025.7.34-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c436fd1e95c04c19039668cfb548450a37c13f051e8659f40aed426e36b3765f"}, + {file = "regex-2025.7.34-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0b85241d3cfb9f8a13cefdfbd58a2843f208f2ed2c88181bf84e22e0c7fc066d"}, + {file = "regex-2025.7.34-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:075641c94126b064c65ab86e7e71fc3d63e7ff1bea1fb794f0773c97cdad3a03"}, + {file = "regex-2025.7.34-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:70645cad3407d103d1dbcb4841839d2946f7d36cf38acbd40120fee1682151e5"}, + {file = "regex-2025.7.34-cp310-cp310-win32.whl", hash = "sha256:3b836eb4a95526b263c2a3359308600bd95ce7848ebd3c29af0c37c4f9627cd3"}, + {file = "regex-2025.7.34-cp310-cp310-win_amd64.whl", hash = "sha256:cbfaa401d77334613cf434f723c7e8ba585df162be76474bccc53ae4e5520b3a"}, + {file = "regex-2025.7.34-cp310-cp310-win_arm64.whl", hash = "sha256:bca11d3c38a47c621769433c47f364b44e8043e0de8e482c5968b20ab90a3986"}, + {file = "regex-2025.7.34-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da304313761b8500b8e175eb2040c4394a875837d5635f6256d6fa0377ad32c8"}, + {file = "regex-2025.7.34-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:35e43ebf5b18cd751ea81455b19acfdec402e82fe0dc6143edfae4c5c4b3909a"}, + {file = "regex-2025.7.34-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96bbae4c616726f4661fe7bcad5952e10d25d3c51ddc388189d8864fbc1b3c68"}, + {file = "regex-2025.7.34-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9feab78a1ffa4f2b1e27b1bcdaad36f48c2fed4870264ce32f52a393db093c78"}, + {file = "regex-2025.7.34-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f14b36e6d4d07f1a5060f28ef3b3561c5d95eb0651741474ce4c0a4c56ba8719"}, + {file = "regex-2025.7.34-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85c3a958ef8b3d5079c763477e1f09e89d13ad22198a37e9d7b26b4b17438b33"}, + {file = "regex-2025.7.34-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37555e4ae0b93358fa7c2d240a4291d4a4227cc7c607d8f85596cdb08ec0a083"}, + {file = "regex-2025.7.34-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ee38926f31f1aa61b0232a3a11b83461f7807661c062df9eb88769d86e6195c3"}, + {file = "regex-2025.7.34-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a664291c31cae9c4a30589bd8bc2ebb56ef880c9c6264cb7643633831e606a4d"}, + {file = "regex-2025.7.34-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f3e5c1e0925e77ec46ddc736b756a6da50d4df4ee3f69536ffb2373460e2dafd"}, + {file = "regex-2025.7.34-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d428fc7731dcbb4e2ffe43aeb8f90775ad155e7db4347a639768bc6cd2df881a"}, + {file = "regex-2025.7.34-cp311-cp311-win32.whl", hash = "sha256:e154a7ee7fa18333ad90b20e16ef84daaeac61877c8ef942ec8dfa50dc38b7a1"}, + {file = "regex-2025.7.34-cp311-cp311-win_amd64.whl", hash = "sha256:24257953d5c1d6d3c129ab03414c07fc1a47833c9165d49b954190b2b7f21a1a"}, + {file = "regex-2025.7.34-cp311-cp311-win_arm64.whl", hash = "sha256:3157aa512b9e606586900888cd469a444f9b898ecb7f8931996cb715f77477f0"}, + {file = "regex-2025.7.34-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7f7211a746aced993bef487de69307a38c5ddd79257d7be83f7b202cb59ddb50"}, + {file = "regex-2025.7.34-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fb31080f2bd0681484b275461b202b5ad182f52c9ec606052020fe13eb13a72f"}, + {file = "regex-2025.7.34-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0200a5150c4cf61e407038f4b4d5cdad13e86345dac29ff9dab3d75d905cf130"}, + {file = "regex-2025.7.34-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:739a74970e736df0773788377969c9fea3876c2fc13d0563f98e5503e5185f46"}, + {file = "regex-2025.7.34-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4fef81b2f7ea6a2029161ed6dea9ae13834c28eb5a95b8771828194a026621e4"}, + {file = "regex-2025.7.34-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea74cf81fe61a7e9d77989050d0089a927ab758c29dac4e8e1b6c06fccf3ebf0"}, + {file = "regex-2025.7.34-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4636a7f3b65a5f340ed9ddf53585c42e3ff37101d383ed321bfe5660481744b"}, + {file = "regex-2025.7.34-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cef962d7834437fe8d3da6f9bfc6f93f20f218266dcefec0560ed7765f5fe01"}, + {file = "regex-2025.7.34-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:cbe1698e5b80298dbce8df4d8d1182279fbdaf1044e864cbc9d53c20e4a2be77"}, + {file = "regex-2025.7.34-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:32b9f9bcf0f605eb094b08e8da72e44badabb63dde6b83bd530580b488d1c6da"}, + {file = "regex-2025.7.34-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:524c868ba527eab4e8744a9287809579f54ae8c62fbf07d62aacd89f6026b282"}, + {file = "regex-2025.7.34-cp312-cp312-win32.whl", hash = "sha256:d600e58ee6d036081c89696d2bdd55d507498a7180df2e19945c6642fac59588"}, + {file = "regex-2025.7.34-cp312-cp312-win_amd64.whl", hash = "sha256:9a9ab52a466a9b4b91564437b36417b76033e8778e5af8f36be835d8cb370d62"}, + {file = "regex-2025.7.34-cp312-cp312-win_arm64.whl", hash = "sha256:c83aec91af9c6fbf7c743274fd952272403ad9a9db05fe9bfc9df8d12b45f176"}, + {file = "regex-2025.7.34-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c3c9740a77aeef3f5e3aaab92403946a8d34437db930a0280e7e81ddcada61f5"}, + {file = "regex-2025.7.34-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:69ed3bc611540f2ea70a4080f853741ec698be556b1df404599f8724690edbcd"}, + {file = "regex-2025.7.34-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d03c6f9dcd562c56527c42b8530aad93193e0b3254a588be1f2ed378cdfdea1b"}, + {file = "regex-2025.7.34-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6164b1d99dee1dfad33f301f174d8139d4368a9fb50bf0a3603b2eaf579963ad"}, + {file = "regex-2025.7.34-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1e4f4f62599b8142362f164ce776f19d79bdd21273e86920a7b604a4275b4f59"}, + {file = "regex-2025.7.34-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:72a26dcc6a59c057b292f39d41465d8233a10fd69121fa24f8f43ec6294e5415"}, + {file = "regex-2025.7.34-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5273fddf7a3e602695c92716c420c377599ed3c853ea669c1fe26218867002f"}, + {file = "regex-2025.7.34-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c1844be23cd40135b3a5a4dd298e1e0c0cb36757364dd6cdc6025770363e06c1"}, + {file = "regex-2025.7.34-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dde35e2afbbe2272f8abee3b9fe6772d9b5a07d82607b5788e8508974059925c"}, + {file = "regex-2025.7.34-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f6e8e7af516a7549412ce57613e859c3be27d55341a894aacaa11703a4c31a"}, + {file = "regex-2025.7.34-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:469142fb94a869beb25b5f18ea87646d21def10fbacb0bcb749224f3509476f0"}, + {file = "regex-2025.7.34-cp313-cp313-win32.whl", hash = "sha256:da7507d083ee33ccea1310447410c27ca11fb9ef18c95899ca57ff60a7e4d8f1"}, + {file = "regex-2025.7.34-cp313-cp313-win_amd64.whl", hash = "sha256:9d644de5520441e5f7e2db63aec2748948cc39ed4d7a87fd5db578ea4043d997"}, + {file = "regex-2025.7.34-cp313-cp313-win_arm64.whl", hash = "sha256:7bf1c5503a9f2cbd2f52d7e260acb3131b07b6273c470abb78568174fe6bde3f"}, + {file = "regex-2025.7.34-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:8283afe7042d8270cecf27cca558873168e771183d4d593e3c5fe5f12402212a"}, + {file = "regex-2025.7.34-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6c053f9647e3421dd2f5dff8172eb7b4eec129df9d1d2f7133a4386319b47435"}, + {file = "regex-2025.7.34-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a16dd56bbcb7d10e62861c3cd000290ddff28ea142ffb5eb3470f183628011ac"}, + {file = "regex-2025.7.34-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69c593ff5a24c0d5c1112b0df9b09eae42b33c014bdca7022d6523b210b69f72"}, + {file = "regex-2025.7.34-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98d0ce170fcde1a03b5df19c5650db22ab58af375aaa6ff07978a85c9f250f0e"}, + {file = "regex-2025.7.34-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d72765a4bff8c43711d5b0f5b452991a9947853dfa471972169b3cc0ba1d0751"}, + {file = "regex-2025.7.34-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4494f8fd95a77eb434039ad8460e64d57baa0434f1395b7da44015bef650d0e4"}, + {file = "regex-2025.7.34-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4f42b522259c66e918a0121a12429b2abcf696c6f967fa37bdc7b72e61469f98"}, + {file = "regex-2025.7.34-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:aaef1f056d96a0a5d53ad47d019d5b4c66fe4be2da87016e0d43b7242599ffc7"}, + {file = "regex-2025.7.34-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:656433e5b7dccc9bc0da6312da8eb897b81f5e560321ec413500e5367fcd5d47"}, + {file = "regex-2025.7.34-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e91eb2c62c39705e17b4d42d4b86c4e86c884c0d15d9c5a47d0835f8387add8e"}, + {file = "regex-2025.7.34-cp314-cp314-win32.whl", hash = "sha256:f978ddfb6216028c8f1d6b0f7ef779949498b64117fc35a939022f67f810bdcb"}, + {file = "regex-2025.7.34-cp314-cp314-win_amd64.whl", hash = "sha256:4b7dc33b9b48fb37ead12ffc7bdb846ac72f99a80373c4da48f64b373a7abeae"}, + {file = "regex-2025.7.34-cp314-cp314-win_arm64.whl", hash = "sha256:4b8c4d39f451e64809912c82392933d80fe2e4a87eeef8859fcc5380d0173c64"}, + {file = "regex-2025.7.34-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fd5edc3f453de727af267c7909d083e19f6426fc9dd149e332b6034f2a5611e6"}, + {file = "regex-2025.7.34-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa1cdfb8db96ef20137de5587954c812821966c3e8b48ffc871e22d7ec0a4938"}, + {file = "regex-2025.7.34-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89c9504fc96268e8e74b0283e548f53a80c421182a2007e3365805b74ceef936"}, + {file = "regex-2025.7.34-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33be70d75fa05a904ee0dc43b650844e067d14c849df7e82ad673541cd465b5f"}, + {file = "regex-2025.7.34-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57d25b6732ea93eeb1d090e8399b6235ca84a651b52d52d272ed37d3d2efa0f1"}, + {file = "regex-2025.7.34-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:baf2fe122a3db1c0b9f161aa44463d8f7e33eeeda47bb0309923deb743a18276"}, + {file = "regex-2025.7.34-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a764a83128af9c1a54be81485b34dca488cbcacefe1e1d543ef11fbace191e1"}, + {file = "regex-2025.7.34-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7f663ccc4093877f55b51477522abd7299a14c5bb7626c5238599db6a0cb95d"}, + {file = "regex-2025.7.34-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4913f52fbc7a744aaebf53acd8d3dc1b519e46ba481d4d7596de3c862e011ada"}, + {file = "regex-2025.7.34-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:efac4db9e044d47fd3b6b0d40b6708f4dfa2d8131a5ac1d604064147c0f552fd"}, + {file = "regex-2025.7.34-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7373afae7cfb716e3b8e15d0184510d518f9d21471f2d62918dbece85f2c588f"}, + {file = "regex-2025.7.34-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9960d162f3fecf6af252534a1ae337e9c2e20d74469fed782903b24e2cc9d3d7"}, + {file = "regex-2025.7.34-cp39-cp39-win32.whl", hash = "sha256:95d538b10eb4621350a54bf14600cc80b514211d91a019dc74b8e23d2159ace5"}, + {file = "regex-2025.7.34-cp39-cp39-win_amd64.whl", hash = "sha256:f7f3071b5faa605b0ea51ec4bb3ea7257277446b053f4fd3ad02b1dcb4e64353"}, + {file = "regex-2025.7.34-cp39-cp39-win_arm64.whl", hash = "sha256:716a47515ba1d03f8e8a61c5013041c8c90f2e21f055203498105d7571b44531"}, + {file = "regex-2025.7.34.tar.gz", hash = "sha256:9ead9765217afd04a86822dfcd4ed2747dfe426e887da413b15ff0ac2457e21a"}, +] + [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, + {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -2628,29 +2923,29 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "ruff" -version = "0.11.9" +version = "0.12.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.11.9-py3-none-linux_armv6l.whl", hash = "sha256:a31a1d143a5e6f499d1fb480f8e1e780b4dfdd580f86e05e87b835d22c5c6f8c"}, - {file = "ruff-0.11.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:66bc18ca783b97186a1f3100e91e492615767ae0a3be584e1266aa9051990722"}, - {file = "ruff-0.11.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bd576cd06962825de8aece49f28707662ada6a1ff2db848d1348e12c580acbf1"}, - {file = "ruff-0.11.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b1d18b4be8182cc6fddf859ce432cc9631556e9f371ada52f3eaefc10d878de"}, - {file = "ruff-0.11.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f3f46f759ac623e94824b1e5a687a0df5cd7f5b00718ff9c24f0a894a683be7"}, - {file = "ruff-0.11.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f34847eea11932d97b521450cf3e1d17863cfa5a94f21a056b93fb86f3f3dba2"}, - {file = "ruff-0.11.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f33b15e00435773df97cddcd263578aa83af996b913721d86f47f4e0ee0ff271"}, - {file = "ruff-0.11.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b27613a683b086f2aca8996f63cb3dd7bc49e6eccf590563221f7b43ded3f65"}, - {file = "ruff-0.11.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e0d88756e63e8302e630cee3ce2ffb77859797cc84a830a24473939e6da3ca6"}, - {file = "ruff-0.11.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:537c82c9829d7811e3aa680205f94c81a2958a122ac391c0eb60336ace741a70"}, - {file = "ruff-0.11.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:440ac6a7029f3dee7d46ab7de6f54b19e34c2b090bb4f2480d0a2d635228f381"}, - {file = "ruff-0.11.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:71c539bac63d0788a30227ed4d43b81353c89437d355fdc52e0cda4ce5651787"}, - {file = "ruff-0.11.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c67117bc82457e4501473c5f5217d49d9222a360794bfb63968e09e70f340abd"}, - {file = "ruff-0.11.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e4b78454f97aa454586e8a5557facb40d683e74246c97372af3c2d76901d697b"}, - {file = "ruff-0.11.9-py3-none-win32.whl", hash = "sha256:7fe1bc950e7d7b42caaee2a8a3bc27410547cc032c9558ee2e0f6d3b209e845a"}, - {file = "ruff-0.11.9-py3-none-win_amd64.whl", hash = "sha256:52edaa4a6d70f8180343a5b7f030c7edd36ad180c9f4d224959c2d689962d964"}, - {file = "ruff-0.11.9-py3-none-win_arm64.whl", hash = "sha256:bcf42689c22f2e240f496d0c183ef2c6f7b35e809f12c1db58f75d9aa8d630ca"}, - {file = "ruff-0.11.9.tar.gz", hash = "sha256:ebd58d4f67a00afb3a30bf7d383e52d0e036e6195143c6db7019604a05335517"}, + {file = "ruff-0.12.3-py3-none-linux_armv6l.whl", hash = "sha256:47552138f7206454eaf0c4fe827e546e9ddac62c2a3d2585ca54d29a890137a2"}, + {file = "ruff-0.12.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0a9153b000c6fe169bb307f5bd1b691221c4286c133407b8827c406a55282041"}, + {file = "ruff-0.12.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fa6b24600cf3b750e48ddb6057e901dd5b9aa426e316addb2a1af185a7509882"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2506961bf6ead54887ba3562604d69cb430f59b42133d36976421bc8bd45901"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4faaff1f90cea9d3033cbbcdf1acf5d7fb11d8180758feb31337391691f3df0"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40dced4a79d7c264389de1c59467d5d5cefd79e7e06d1dfa2c75497b5269a5a6"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0262d50ba2767ed0fe212aa7e62112a1dcbfd46b858c5bf7bbd11f326998bafc"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12371aec33e1a3758597c5c631bae9a5286f3c963bdfb4d17acdd2d395406687"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:560f13b6baa49785665276c963edc363f8ad4b4fc910a883e2625bdb14a83a9e"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:023040a3499f6f974ae9091bcdd0385dd9e9eb4942f231c23c57708147b06311"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:883d844967bffff5ab28bba1a4d246c1a1b2933f48cb9840f3fdc5111c603b07"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2120d3aa855ff385e0e562fdee14d564c9675edbe41625c87eeab744a7830d12"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6b16647cbb470eaf4750d27dddc6ebf7758b918887b56d39e9c22cce2049082b"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e1417051edb436230023575b149e8ff843a324557fe0a265863b7602df86722f"}, + {file = "ruff-0.12.3-py3-none-win32.whl", hash = "sha256:dfd45e6e926deb6409d0616078a666ebce93e55e07f0fb0228d4b2608b2c248d"}, + {file = "ruff-0.12.3-py3-none-win_amd64.whl", hash = "sha256:a946cf1e7ba3209bdef039eb97647f1c77f6f540e5845ec9c114d3af8df873e7"}, + {file = "ruff-0.12.3-py3-none-win_arm64.whl", hash = "sha256:5f9c7c9c8f84c2d7f27e93674d27136fbf489720251544c4da7fb3d742e011b1"}, + {file = "ruff-0.12.3.tar.gz", hash = "sha256:f1b5a4b6668fd7b7ea3697d8d98857390b40c1320a63a178eee6be0899ea2d77"}, ] [[package]] @@ -2775,7 +3070,7 @@ files = [ name = "snowballstemmer" version = "3.0.1" description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." -optional = true +optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" files = [ {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, @@ -2877,7 +3172,7 @@ test = ["pytest"] name = "sphinx" version = "7.4.7" description = "Python documentation generator" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, @@ -2909,11 +3204,29 @@ docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] +[[package]] +name = "sphinx-lint" +version = "1.0.0" +description = "Check for stylistic and formal issues in .rst and .py files included in the documentation." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_lint-1.0.0-py3-none-any.whl", hash = "sha256:6117a0f340b2dc73eadfc57db7531d4477e0929f92a0c1a2f61e6edbc272f0bc"}, + {file = "sphinx_lint-1.0.0.tar.gz", hash = "sha256:6eafdb44172ce526f405bf36c713eb246f1340ec2d667e7298e2487ed76decd2"}, +] + +[package.dependencies] +polib = "*" +regex = "*" + +[package.extras] +tests = ["pytest", "pytest-cov"] + [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, @@ -2929,7 +3242,7 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, @@ -2945,7 +3258,7 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, @@ -2961,7 +3274,7 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" -optional = true +optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, @@ -2975,7 +3288,7 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-qthelp" version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, @@ -2991,7 +3304,7 @@ test = ["defusedxml (>=0.7.1)", "pytest"] name = "sphinxcontrib-serializinghtml" version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, @@ -3003,6 +3316,20 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + [[package]] name = "texttable" version = "1.7.0" @@ -3025,6 +3352,17 @@ files = [ {file = "threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e"}, ] +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + [[package]] name = "tomli" version = "2.2.1" @@ -3068,13 +3406,13 @@ files = [ [[package]] name = "types-beautifulsoup4" -version = "4.12.0.20250204" +version = "4.12.0.20250516" description = "Typing stubs for beautifulsoup4" optional = false python-versions = ">=3.9" files = [ - {file = "types_beautifulsoup4-4.12.0.20250204-py3-none-any.whl", hash = "sha256:57ce9e75717b63c390fd789c787d267a67eb01fa6d800a03b9bdde2e877ed1eb"}, - {file = "types_beautifulsoup4-4.12.0.20250204.tar.gz", hash = "sha256:f083d8edcbd01279f8c3995b56cfff2d01f1bb894c3b502ba118d36fbbc495bf"}, + {file = "types_beautifulsoup4-4.12.0.20250516-py3-none-any.whl", hash = "sha256:5923399d4a1ba9cc8f0096fe334cc732e130269541d66261bb42ab039c0376ee"}, + {file = "types_beautifulsoup4-4.12.0.20250516.tar.gz", hash = "sha256:aa19dd73b33b70d6296adf92da8ab8a0c945c507e6fb7d5db553415cc77b417e"}, ] [package.dependencies] @@ -3082,13 +3420,13 @@ types-html5lib = "*" [[package]] name = "types-flask-cors" -version = "5.0.0.20250413" +version = "6.0.0.20250520" description = "Typing stubs for Flask-Cors" optional = false python-versions = ">=3.9" files = [ - {file = "types_flask_cors-5.0.0.20250413-py3-none-any.whl", hash = "sha256:8183fdba764d45a5b40214468a1d5daa0e86c4ee6042d13f38cc428308f27a64"}, - {file = "types_flask_cors-5.0.0.20250413.tar.gz", hash = "sha256:b346d052f4ef3b606b73faf13e868e458f1efdbfedcbe1aba739eb2f54a6cf5f"}, + {file = "types_flask_cors-6.0.0.20250520-py3-none-any.whl", hash = "sha256:8898ed43a6b68d0b3b499e1d2f7aa696a99a001610de44e09fc6f404d16eb704"}, + {file = "types_flask_cors-6.0.0.20250520.tar.gz", hash = "sha256:9357c21be733f65e568ff27e816426832f3e3fd906eedbb896bcc6b1cfa026e6"}, ] [package.dependencies] @@ -3096,24 +3434,24 @@ Flask = ">=2.0.0" [[package]] name = "types-html5lib" -version = "1.1.11.20241018" +version = "1.1.11.20250708" description = "Typing stubs for html5lib" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "types-html5lib-1.1.11.20241018.tar.gz", hash = "sha256:98042555ff78d9e3a51c77c918b1041acbb7eb6c405408d8a9e150ff5beccafa"}, - {file = "types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403"}, + {file = "types_html5lib-1.1.11.20250708-py3-none-any.whl", hash = "sha256:bb898066b155de7081cb182179e2ded31b9e0e234605e2cb46536894e68a6954"}, + {file = "types_html5lib-1.1.11.20250708.tar.gz", hash = "sha256:24321720fdbac71cee50d5a4bec9b7448495b7217974cffe3fcf1ede4eef7afe"}, ] [[package]] name = "types-mock" -version = "5.2.0.20250306" +version = "5.2.0.20250516" description = "Typing stubs for mock" optional = false python-versions = ">=3.9" files = [ - {file = "types_mock-5.2.0.20250306-py3-none-any.whl", hash = "sha256:eb69fec98b8de26be1d7121623d05a2f117d1ea2e01dd30c123d07d204a15c95"}, - {file = "types_mock-5.2.0.20250306.tar.gz", hash = "sha256:15882cb5cf9980587a7607e31890801223801d7997f559686805ce09b6536087"}, + {file = "types_mock-5.2.0.20250516-py3-none-any.whl", hash = "sha256:e50fbd0c3be8bcea25c30a47fac0b7a6ca22f630ef2f53416a73b319b39dfde1"}, + {file = "types_mock-5.2.0.20250516.tar.gz", hash = "sha256:aab7d3d9ad3814f2f8da12cc8e42d9be7d38200c5f214e3c0278c38fa01299d7"}, ] [[package]] @@ -3129,24 +3467,24 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20250402" +version = "6.0.12.20250516" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.9" files = [ - {file = "types_pyyaml-6.0.12.20250402-py3-none-any.whl", hash = "sha256:652348fa9e7a203d4b0d21066dfb00760d3cbd5a15ebb7cf8d33c88a49546681"}, - {file = "types_pyyaml-6.0.12.20250402.tar.gz", hash = "sha256:d7c13c3e6d335b6af4b0122a01ff1d270aba84ab96d1a1a1063ecba3e13ec075"}, + {file = "types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530"}, + {file = "types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba"}, ] [[package]] name = "types-requests" -version = "2.32.0.20250328" +version = "2.32.4.20250611" description = "Typing stubs for requests" optional = false python-versions = ">=3.9" files = [ - {file = "types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2"}, - {file = "types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32"}, + {file = "types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072"}, + {file = "types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826"}, ] [package.dependencies] @@ -3165,13 +3503,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.14.1" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, + {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, + {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, ] [[package]] @@ -3187,13 +3525,13 @@ files = [ [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" files = [ - {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, - {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -3232,13 +3570,13 @@ files = [ [[package]] name = "zipp" -version = "3.21.0" +version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] @@ -3246,7 +3584,7 @@ check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [extras] @@ -3279,4 +3617,4 @@ web = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "b3f2746a43227fe639d17eb22d7924e30c9d83eef53dce2c10388c602f0c6665" +content-hash = "daa6c3c2b5bee3180f74f4186bb29ee1ad825870b5b9f6c2b743fcaa61b34c8c" diff --git a/pyproject.toml b/pyproject.toml index 39c543307..dbe8e568a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ musicbrainzngs = ">=0.4" numpy = ">=1.24.4" platformdirs = ">=3.5.0" pyyaml = "*" -typing_extensions = { version = "*", python = "<=3.10" } +typing_extensions = "*" unidecode = ">=1.3.6" beautifulsoup4 = { version = "*", optional = true } @@ -100,7 +100,9 @@ requests_oauthlib = "*" responses = ">=0.3.0" [tool.poetry.group.lint.dependencies] +docstrfmt = ">=1.10.0" ruff = ">=0.6.4" +sphinx-lint = ">=1.0.0" [tool.poetry.group.typing.dependencies] mypy = "*" @@ -127,7 +129,7 @@ beatport = ["requests-oauthlib"] bpd = ["PyGObject"] # gobject-introspection, gstreamer1.0-plugins-base, python3-gst-1.0 chroma = ["pyacoustid"] # chromaprint or fpcalc # convert # ffmpeg -docs = ["pydata-sphinx-theme", "sphinx"] +docs = ["pydata-sphinx-theme", "sphinx", "sphinx-lint"] discogs = ["python3-discogs-client"] embedart = ["Pillow"] # ImageMagick embyupdate = ["requests"] @@ -200,10 +202,18 @@ cmd = "make -C docs html" help = "Format the codebase" cmd = "ruff format" +[tool.poe.tasks.format-docs] +help = "Format the documentation" +cmd = "docstrfmt" + [tool.poe.tasks.lint] help = "Check the code for linting issues. Accepts ruff options." cmd = "ruff check" +[tool.poe.tasks.lint-docs] +help = "Lint the documentation" +shell = "sphinx-lint --enable all $(git ls-files '*.rst')" + [tool.poe.tasks.update-dependencies] help = "Update dependencies to their latest versions." cmd = "poetry update -vv" @@ -245,6 +255,15 @@ done """ interpreter = "zsh" +[tool.docstrfmt] +line-length = 80 +extend-exclude = [ + "docs/_templates/**/*", + "docs/api/**/*", + "README_kr.rst", +] +files = ["docs", "*.rst"] + [tool.ruff] target-version = "py39" line-length = 80 @@ -265,7 +284,7 @@ select = [ "W", # pycodestyle ] ignore = [ - "TC006" # no need to quote 'cast's since we use 'from __future__ import annotations' + "TC006", # no need to quote 'cast's since we use 'from __future__ import annotations' ] [tool.ruff.lint.per-file-ignores] diff --git a/setup.cfg b/setup.cfg index 0b50485ea..000c4a77e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,10 +3,13 @@ cache_dir = /tmp/pytest_cache # slightly more verbose output console_output_style = count +# pretty-print test names in the Codecov U +junit_family = legacy addopts = # show all skipped/failed/xfailed tests in the summary except passed -ra --strict-config + --junitxml=.reports/pytest.xml markers = on_lyrics_update: mark a test to run only after lyrics source code is updated integration_test: mark a test as an integration test diff --git a/test/plugins/test_hook.py b/test/plugins/test_hook.py index d15de1cec..3392d6881 100644 --- a/test/plugins/test_hook.py +++ b/test/plugins/test_hook.py @@ -37,12 +37,14 @@ class HookTestCase(PluginTestCase): class HookLogsTest(HookTestCase): + HOOK: plugins.EventType = "write" + @contextmanager def _configure_logs(self, command: str) -> Iterator[list[str]]: - config = {"hooks": [self._get_hook("test_event", command)]} + config = {"hooks": [self._get_hook(self.HOOK, command)]} with self.configure_plugin(config), capture_log("beets.hook") as logs: - plugins.send("test_event") + plugins.send(self.HOOK) yield logs def test_hook_empty_command(self): @@ -53,13 +55,13 @@ class HookLogsTest(HookTestCase): @unittest.skipIf(sys.platform == "win32", "win32") def test_hook_non_zero_exit(self): with self._configure_logs('sh -c "exit 1"') as logs: - assert "hook: hook for test_event exited with status 1" in logs + assert f"hook: hook for {self.HOOK} exited with status 1" in logs def test_hook_non_existent_command(self): with self._configure_logs("non-existent-command") as logs: logs = "\n".join(logs) - assert "hook: hook for test_event failed: " in logs + assert f"hook: hook for {self.HOOK} failed: " in logs # The error message is different for each OS. Unfortunately the text is # different in each case, where the only shared text is the string # 'file' and substring 'Err' @@ -68,13 +70,11 @@ class HookLogsTest(HookTestCase): class HookCommandTest(HookTestCase): - TEST_HOOK_COUNT = 2 - - events = [f"test_event_{i}" for i in range(TEST_HOOK_COUNT)] + EVENTS: list[plugins.EventType] = ["write", "after_write"] def setUp(self): super().setUp() - self.paths = [str(self.temp_dir_path / e) for e in self.events] + self.paths = [str(self.temp_dir_path / e) for e in self.EVENTS] def _test_command( self, @@ -93,13 +93,14 @@ class HookCommandTest(HookTestCase): 2. Assert that a file has been created under the original path, which proves that the configured hook command has been executed. """ + events_with_paths = list(zip(self.EVENTS, self.paths)) hooks = [ self._get_hook(e, f"touch {make_test_path(e, p)}") - for e, p in zip(self.events, self.paths) + for e, p in events_with_paths ] with self.configure_plugin({"hooks": hooks}): - for event, path in zip(self.events, self.paths): + for event, path in events_with_paths: if send_path_kwarg: plugins.send(event, path=path) else: diff --git a/test/test_dbcore.py b/test/test_dbcore.py index a4bae97c9..3f9a9d45e 100644 --- a/test/test_dbcore.py +++ b/test/test_dbcore.py @@ -25,6 +25,7 @@ import pytest from beets import dbcore from beets.library import LibModel from beets.test import _common +from beets.util import cached_classproperty # Fixture: concrete database and model classes. For migration tests, we # have multiple models with different numbers of fields. @@ -53,15 +54,22 @@ class ModelFixture1(LibModel): "field_one": dbcore.types.INTEGER, "field_two": dbcore.types.STRING, } - _types = { - "some_float_field": dbcore.types.FLOAT, - } + _sorts = { "some_sort": SortFixture, } - _queries = { - "some_query": QueryFixture, - } + + @cached_classproperty + def _types(cls): + return { + "some_float_field": dbcore.types.FLOAT, + } + + @cached_classproperty + def _queries(cls): + return { + "some_query": QueryFixture, + } @classmethod def _getters(cls): diff --git a/test/test_plugins.py b/test/test_plugins.py index d9da17e61..df338f924 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -13,8 +13,12 @@ # included in all copies or substantial portions of the Software. +import importlib import itertools +import logging import os +import pkgutil +import sys from unittest.mock import ANY, Mock, patch import pytest @@ -30,76 +34,59 @@ from beets.importer import ( ) from beets.library import Item from beets.test import helper -from beets.test.helper import AutotagStub, ImportHelper, TerminalImportMixin -from beets.test.helper import PluginTestCase as BasePluginTestCase +from beets.test.helper import ( + AutotagStub, + ImportHelper, + PluginMixin, + PluginTestCase, + TerminalImportMixin, +) from beets.util import displayable_path, syspath -class PluginLoaderTestCase(BasePluginTestCase): - def setup_plugin_loader(self): - # FIXME the mocking code is horrific, but this is the lowest and - # earliest level of the plugin mechanism we can hook into. - self._plugin_loader_patch = patch("beets.plugins.load_plugins") - self._plugin_classes = set() - load_plugins = self._plugin_loader_patch.start() +class TestPluginRegistration(PluginTestCase): + class RatingPlugin(plugins.BeetsPlugin): + item_types = { + "rating": types.Float(), + "multi_value": types.MULTI_VALUE_DSV, + } - def myload(names=()): - plugins._classes.update(self._plugin_classes) + def __init__(self): + super().__init__() + self.register_listener("write", self.on_write) - load_plugins.side_effect = myload - - def teardown_plugin_loader(self): - self._plugin_loader_patch.stop() - - def register_plugin(self, plugin_class): - self._plugin_classes.add(plugin_class) + @staticmethod + def on_write(item=None, path=None, tags=None): + if tags["artist"] == "XXX": + tags["artist"] = "YYY" def setUp(self): - self.setup_plugin_loader() super().setUp() - def tearDown(self): - self.teardown_plugin_loader() - super().tearDown() + self.register_plugin(self.RatingPlugin) + def test_field_type_registered(self): + assert isinstance(Item._types.get("rating"), types.Float) -class PluginImportTestCase(ImportHelper, PluginLoaderTestCase): - def setUp(self): - super().setUp() - self.prepare_album_for_import(2) + def test_duplicate_type(self): + class DuplicateTypePlugin(plugins.BeetsPlugin): + item_types = {"rating": types.INTEGER} + self.register_plugin(DuplicateTypePlugin) + with pytest.raises( + plugins.PluginConflictError, match="already been defined" + ): + Item._types -class ItemTypesTest(PluginLoaderTestCase): - def test_flex_field_type(self): - class RatingPlugin(plugins.BeetsPlugin): - item_types = {"rating": types.Float()} + def test_listener_registered(self): + self.RatingPlugin() + item = self.add_item_fixture(artist="XXX") - self.register_plugin(RatingPlugin) - self.config["plugins"] = "rating" + item.write() - item = Item(path="apath", artist="aaa") - item.add(self.lib) - - # Do not match unset values - out = self.run_with_output("ls", "rating:1..3") - assert "aaa" not in out - - self.run_command("modify", "rating=2", "--yes") - - # Match in range - out = self.run_with_output("ls", "rating:1..3") - assert "aaa" in out - - # Don't match out of range - out = self.run_with_output("ls", "rating:3..5") - assert "aaa" not in out + assert MediaFile(syspath(item.path)).artist == "YYY" def test_multi_value_flex_field_type(self): - class MultiValuePlugin(plugins.BeetsPlugin): - item_types = {"multi_value": types.MULTI_VALUE_DSV} - - self.register_plugin(MultiValuePlugin) - item = Item(path="apath", artist="aaa") item.multi_value = ["one", "two", "three"] item.add(self.lib) @@ -109,66 +96,13 @@ class ItemTypesTest(PluginLoaderTestCase): assert out == f"one{delimiter}two{delimiter}three\n" -class ItemWriteTest(PluginLoaderTestCase): +class PluginImportTestCase(ImportHelper, PluginTestCase): def setUp(self): super().setUp() - - class EventListenerPlugin(plugins.BeetsPlugin): - pass - - self.event_listener_plugin = EventListenerPlugin() - self.register_plugin(EventListenerPlugin) - - def test_change_tags(self): - def on_write(item=None, path=None, tags=None): - if tags["artist"] == "XXX": - tags["artist"] = "YYY" - - self.register_listener("write", on_write) - - item = self.add_item_fixture(artist="XXX") - item.write() - - mediafile = MediaFile(syspath(item.path)) - assert mediafile.artist == "YYY" - - def register_listener(self, event, func): - self.event_listener_plugin.register_listener(event, func) - - -class ItemTypeConflictTest(PluginLoaderTestCase): - def test_mismatch(self): - class EventListenerPlugin(plugins.BeetsPlugin): - item_types = {"duplicate": types.INTEGER} - - class AdventListenerPlugin(plugins.BeetsPlugin): - item_types = {"duplicate": types.FLOAT} - - self.event_listener_plugin = EventListenerPlugin - self.advent_listener_plugin = AdventListenerPlugin - self.register_plugin(EventListenerPlugin) - self.register_plugin(AdventListenerPlugin) - with pytest.raises(plugins.PluginConflictError): - plugins.types(Item) - - def test_match(self): - class EventListenerPlugin(plugins.BeetsPlugin): - item_types = {"duplicate": types.INTEGER} - - class AdventListenerPlugin(plugins.BeetsPlugin): - item_types = {"duplicate": types.INTEGER} - - self.event_listener_plugin = EventListenerPlugin - self.advent_listener_plugin = AdventListenerPlugin - self.register_plugin(EventListenerPlugin) - self.register_plugin(AdventListenerPlugin) - assert plugins.types(Item) is not None + self.prepare_album_for_import(2) class EventsTest(PluginImportTestCase): - def setUp(self): - super().setUp() - def test_import_task_created(self): self.importer = self.setup_importer(pretend=True) @@ -228,7 +162,7 @@ class EventsTest(PluginImportTestCase): ] -class ListenersTest(PluginLoaderTestCase): +class ListenersTest(PluginTestCase): def test_register(self): class DummyPlugin(plugins.BeetsPlugin): def __init__(self): @@ -248,15 +182,7 @@ class ListenersTest(PluginLoaderTestCase): d.register_listener("cli_exit", d2.dummy) assert DummyPlugin._raw_listeners["cli_exit"] == [d.dummy, d2.dummy] - @patch("beets.plugins.find_plugins") - @patch("inspect.getfullargspec") - def test_events_called(self, mock_gfa, mock_find_plugins): - mock_gfa.return_value = Mock( - args=(), - varargs="args", - varkw="kwargs", - ) - + def test_events_called(self): class DummyPlugin(plugins.BeetsPlugin): def __init__(self): super().__init__() @@ -266,7 +192,6 @@ class ListenersTest(PluginLoaderTestCase): self.register_listener("event_bar", self.bar) d = DummyPlugin() - mock_find_plugins.return_value = (d,) plugins.send("event") d.foo.assert_has_calls([]) @@ -276,8 +201,7 @@ class ListenersTest(PluginLoaderTestCase): d.foo.assert_called_once_with(var="tagada") d.bar.assert_has_calls([]) - @patch("beets.plugins.find_plugins") - def test_listener_params(self, mock_find_plugins): + def test_listener_params(self): class DummyPlugin(plugins.BeetsPlugin): def __init__(self): super().__init__() @@ -321,8 +245,7 @@ class ListenersTest(PluginLoaderTestCase): def dummy9(self, **kwargs): assert kwargs == {"foo": 5} - d = DummyPlugin() - mock_find_plugins.return_value = (d,) + DummyPlugin() plugins.send("event1", foo=5) plugins.send("event2", foo=5) @@ -545,3 +468,58 @@ class PromptChoicesTest(TerminalImportMixin, PluginImportTestCase): self.mock_input_options.assert_called_once_with( opts, default="a", require=ANY ) + + +def get_available_plugins(): + """Get all available plugins in the beetsplug namespace.""" + namespace_pkg = importlib.import_module("beetsplug") + + return [ + m.name + for m in pkgutil.iter_modules(namespace_pkg.__path__) + if not m.name.startswith("_") + ] + + +class TestImportPlugin(PluginMixin): + @pytest.fixture(params=get_available_plugins()) + def plugin_name(self, request): + """Fixture to provide the name of each available plugin.""" + name = request.param + + # skip gstreamer plugins on windows + gstreamer_plugins = {"bpd", "replaygain"} + if sys.platform == "win32" and name in gstreamer_plugins: + pytest.skip(f"GStreamer is not available on Windows: {name}") + + return name + + def unload_plugins(self): + """Unimport plugins before each test to avoid conflicts.""" + super().unload_plugins() + for mod in list(sys.modules): + if mod.startswith("beetsplug."): + del sys.modules[mod] + + @pytest.fixture(autouse=True) + def cleanup(self): + """Ensure plugins are unimported before and after each test.""" + self.unload_plugins() + yield + self.unload_plugins() + + @pytest.mark.skipif( + os.environ.get("GITHUB_ACTIONS") != "true", + reason=( + "Requires all dependencies to be installed, which we can't" + " guarantee in the local environment." + ), + ) + def test_import_plugin(self, caplog, plugin_name): + """Test that a plugin is importable without an error.""" + caplog.set_level(logging.WARNING) + self.load_plugins(plugin_name) + + assert "PluginImportError" not in caplog.text, ( + f"Plugin '{plugin_name}' has issues during import." + ) diff --git a/test/test_release.py b/test/test_release.py index 62986fa95..62b32a714 100644 --- a/test/test_release.py +++ b/test/test_release.py @@ -22,29 +22,36 @@ pytestmark = pytest.mark.skipif( def rst_changelog(): return """New features: -* :doc:`/plugins/substitute`: Some substitute +- :doc:`/plugins/substitute`: Some substitute multi-line change. :bug:`5467` -* :ref:`list-cmd` Update. +- :ref:`list-cmd` Update. -You can do something with this command:: +You can do something with this command: + +:: $ do-something Bug fixes: -* Some fix that refers to an issue. +- Some fix that refers to an issue. :bug:`5467` -* Some fix that mentions user :user:`username`. -* Some fix thanks to +- Some fix that mentions user :user:`username`. +- Some fix thanks to :user:`username`. :bug:`5467` -* Some fix with its own bullet points using incorrect indentation: - * First nested bullet point - with some text that wraps to the next line - * Second nested bullet point -* Another fix with its own bullet points using correct indentation: - * First - * Second +- Some fix with its own bullet points using incorrect indentation: + + - First nested bullet point + with some text that wraps to the next line + - Second nested bullet point + +- Another fix with an enumerated list + + 1. First + and some details + 2. Second + and some details Section naaaaaaaaaaaaaaaaaaaaaaaammmmmmmmmmmmmmmmeeeeeeeeeeeeeee with over 80 characters: @@ -53,7 +60,7 @@ Empty section: Other changes: -* Changed `bitesize` label to `good first issue`. Our `contribute`_ page is now +- Changed ``bitesize`` label to ``good first issue``. Our `contribute`_ page is now automatically populated with these issues. :bug:`4855` .. _contribute: https://github.com/beetbox/beets/contribute @@ -63,7 +70,7 @@ Other changes: Bug fixes: -* Fixed something.""" +- Fixed something.""" @pytest.fixture @@ -79,9 +86,9 @@ You can do something with this command: ### Bug fixes -- Another fix with its own bullet points using correct indentation: - - First - - Second +- Another fix with an enumerated list + 1. First and some details + 2. Second and some details - Some fix thanks to @username. :bug: (#5467) - Some fix that mentions user @username. - Some fix that refers to an issue. :bug: (#5467)